/**
 * 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"

struct LongFlag devFlags[] = {
    { LIBF_SUMMING, "LIBF_SUMMING", "%00000001" },
    { LIBF_CHANGED, "LIBF_CHANGED", "%00000010" },
    { LIBF_SUMUSED, "LIBF_SUMUSED", "%00000100" },
    { LIBF_DELEXP,  "LIBF_DELEXP",  "%00001000" },
    { 0,            NULL,           NULL }
};

struct NSDCommandEntry {
    UWORD nce_Number;
    UBYTE nce_DecNumber[16];
    UBYTE nce_HexNumber[16];
    UBYTE nce_CmdName[64];
};

static __asm __saveds LONG nsdlist_con2func(register __a2 Object *obj, register __a1 struct NList_ConstructMessage *msg, register __a0 struct Hook *hook)
{
    return AllocListEntry(msg->pool, msg->entry, sizeof(struct NSDCommandEntry));
}

MakeHook(nsdlist_con2hook, nsdlist_con2func);

static __asm __saveds LONG nsdlist_des2func(register __a2 Object *obj, register __a1 struct NList_DestructMessage *msg, register __a0 struct Hook *hook)
{
    FreeListEntry(msg->pool, &msg->entry);

    return 0;
}

MakeHook(nsdlist_des2hook, nsdlist_des2func);

static __asm __saveds LONG nsdlist_dsp2func(register __a2 Object *obj, register __a1 struct NList_DisplayMessage *msg, register __a0 struct Hook *hook)
{
    struct NSDCommandEntry *nce = (struct NSDCommandEntry *)msg->entry;

    if (nce) {
        msg->strings[0] = nce->nce_DecNumber;
        msg->strings[1] = nce->nce_HexNumber;
        msg->strings[2] = nce->nce_CmdName;
    } else {
        msg->strings[0] = MUIX_B "Number (dec)";
        msg->strings[1] = MUIX_B "Number (hex)";
        msg->strings[2] = MUIX_B "Name";
    }

    return 0;
}

MakeHook(nsdlist_dsp2hook, nsdlist_dsp2func);

static LONG nsdlist_cmp2colfunc( struct NSDCommandEntry *nce1,
                                 struct NSDCommandEntry *nce2,
                                 ULONG column )
{
    switch (column) {
        case 0:
        case 1: return nce1->nce_Number - nce2->nce_Number;
        case 2: return stricmp(nce1->nce_CmdName, nce2->nce_CmdName);
    }
}

static __asm __saveds LONG nsdlist_cmp2func(register __a2 Object *obj, register __a1 struct NList_CompareMessage *msg, register __a0 struct Hook *hook)
{
    LONG cmp;
    struct NSDCommandEntry *nce1, *nce2;
    ULONG col1, col2;

    nce1 = (struct NSDCommandEntry *)msg->entry1;
    nce2 = (struct NSDCommandEntry *)msg->entry2;
    col1 = msg->sort_type & MUIV_NList_TitleMark_ColMask;
    col2 = msg->sort_type2 & MUIV_NList_TitleMark2_ColMask;

    if (msg->sort_type == MUIV_NList_SortType_None) return 0;

    if (msg->sort_type & MUIV_NList_TitleMark_TypeMask) {
        cmp = nsdlist_cmp2colfunc(nce2, nce1, col1);
    } else {
        cmp = nsdlist_cmp2colfunc(nce1, nce2, col1);
    }

    if (cmp != 0 || col1 == col2) return cmp;

    if (msg->sort_type2 & MUIV_NList_TitleMark2_TypeMask) {
        cmp = nsdlist_cmp2colfunc(nce2, nce1, col2);
    } else {
        cmp = nsdlist_cmp2colfunc(nce1, nce2, col2);
    }

    return cmp;
}

MakeHook(nsdlist_cmp2hook, nsdlist_cmp2func);

static LONG NSDQuery( UBYTE *dev,
                      ULONG unit,
                      struct NSDeviceQueryResult *nqr )
{
    LONG result = IOERR_OPENFAIL;
    struct MsgPort *mp;

    if (mp = CreateMsgPort()) {
        struct IOStdReq *io;

        if (io = CreateIORequest(mp, sizeof(struct IOStdReq) + 128)) {
            if (strcmp(AHINAME, dev) == 0) {
                ((struct AHIRequest *)io)->ahir_Version = 4;
            }

            if (OpenDevice(dev, unit, io, 0) == 0) {
                nqr->DevQueryFormat = 0;
                nqr->SizeAvailable = 0;

                io->io_Command = NSCMD_DEVICEQUERY;
                io->io_Data = nqr;
                io->io_Length = sizeof(struct NSDeviceQueryResult);
                result = DoIO(io);

                CloseDevice(io);
            }

            DeleteIORequest(io);
        }

        DeleteMsgPort(mp);
    }

    return result;
}

static UBYTE *NSDTypeToCmdName( ULONG nsdType,
                                UWORD cmd )
{
    UBYTE *result;

    switch (cmd) {
        case CMD_INVALID      : result = "CMD_INVALID";       break;
        case CMD_RESET        : result = "CMD_RESET";         break;
        case CMD_READ         : result = "CMD_READ";          break;
        case CMD_WRITE        : result = "CMD_WRITE";         break;
        case CMD_UPDATE       : result = "CMD_UPDATE";        break;
        case CMD_CLEAR        : result = "CMD_CLEAR";         break;
        case CMD_STOP         : result = "CMD_STOP";          break;
        case CMD_START        : result = "CMD_START";         break;
        case CMD_FLUSH        : result = "CMD_FLUSH";         break;
        case NSCMD_DEVICEQUERY: result = "NSCMD_DEVICEQUERY"; break;
        default               : result = NULL;                break;
    }

    if (result) return result;

    switch (nsdType) {
        case NSDEVTYPE_GAMEPORT:
            switch (cmd) {
                case GPD_READEVENT : result = "GPD_READEVENT";              break;
                case GPD_ASKCTYPE  : result = "GPD_ASKCTYPE";               break;
                case GPD_SETCTYPE  : result = "GPD_SETCTYPE";               break;
                case GPD_ASKTRIGGER: result = "GPD_ASKTRIGGER";             break;
                case GPD_SETTRIGGER: result = "GPD_SETTRIGGER";             break;
                default            : result = "<unknown gameport command>"; break;
            }
            break;

        case NSDEVTYPE_TIMER:
            switch (cmd) {
                case TR_ADDREQUEST: result = "TR_ADDREQUEST";           break;
                case TR_GETSYSTIME: result = "TR_GETSYSTIME";           break;
                case TR_SETSYSTIME: result = "TR_SETSYSTIME";           break;
                default           : result = "<unknown timer command>"; break;
            }
            break;

        case NSDEVTYPE_KEYBOARD:
            switch (cmd) {
                case KBD_READEVENT       : result = "KBD_READEVENT";              break;
                case KBD_READMATRIX      : result = "KBD_READMATRIX";             break;
                case KBD_ADDRESETHANDLER : result = "KBD_ADDRESETHANDLER";        break;
                case KBD_REMRESETHANDLER : result = "KBD_REMRESETHANDLER";        break;
                case KBD_RESETHANDLERDONE: result = "KBD_RESETHANDLERDONE";       break;
                default                  : result = "<unknown keyboard command>"; break;
            }
            break;

        case NSDEVTYPE_INPUT:
            switch (cmd) {
                case IND_ADDHANDLER: result = "IND_ADDHANDLER";          break;
                case IND_REMHANDLER: result = "IND_REMHANDLER";          break;
                case IND_WRITEEVENT: result = "IND_WRITEEVENT";          break;
                case IND_SETTHRESH : result = "IND_SETTHRESH";           break;
                case IND_SETPERIOD : result = "IND_SETPERIOD";           break;
                case IND_SETMPORT  : result = "IND_SETMPORT";            break;
                case IND_SETMTYPE  : result = "IND_SETMTYPE";            break;
                case IND_SETMTRIG  : result = "IND_SETMTRIG";            break;
                default            : result = "<unknown input command>"; break;
            }
            break;

        case NSDEVTYPE_TRACKDISK:
            switch (cmd) {
                case TD_MOTOR          : result = "TD_MOTOR";                    break;
                case TD_SEEK           : result = "TD_SEEK";                     break;
                case TD_FORMAT         : result = "TD_FORMAT";                   break;
                case TD_REMOVE         : result = "TD_REMOVE";                   break;
                case TD_CHANGENUM      : result = "TD_CHANGENUM";                break;
                case TD_CHANGESTATE    : result = "TD_CHANGESTATE";              break;
                case TD_PROTSTATUS     : result = "TD_PROTSTATUS";               break;
                case TD_RAWREAD        : result = "TD_RAWREAD";                  break;
                case TD_RAWWRITE       : result = "TD_RAWWRITE";                 break;
                case TD_GETDRIVETYPE   : result = "TD_GETDRIVETYPE";             break;
                case TD_GETNUMTRACKS   : result = "TD_GETNUMTRACKS";             break;
                case TD_ADDCHANGEINT   : result = "TD_ADDCHANGEINT";             break;
                case TD_REMCHANGEINT   : result = "TD_REMCHANGEINT";             break;
                case TD_GETGEOMETRY    : result = "TD_GETGEOMETRY";              break;
                case TD_EJECT          : result = "TD_EJECT";                    break;
                case HD_SCSICMD        : result = "HD_SCSICMD";                  break;
                case ETD_WRITE         : result = "ETD_WRITE";                   break;
                case ETD_READ          : result = "ETD_READ";                    break;
                case ETD_MOTOR         : result = "ETD_MOTOR";                   break;
                case ETD_SEEK          : result = "ETD_SEEK";                    break;
                case ETD_FORMAT        : result = "ETD_FORMAT";                  break;
                case ETD_UPDATE        : result = "ETD_UPDATE";                  break;
                case ETD_CLEAR         : result = "ETD_CLEAR";                   break;
                case ETD_RAWREAD       : result = "ETD_RAWREAD";                 break;
                case ETD_RAWWRITE      : result = "ETD_RAWWRITE";                break;
                case NSCMD_TD_READ64   : result = "NSCMD_TD_READ64";             break;
                case NSCMD_TD_WRITE64  : result = "NSCMD_TD_WRITE64";            break;
                case NSCMD_TD_SEEK64   : result = "NSCMD_TD_SEEK64";             break;
                case NSCMD_TD_FORMAT64 : result = "NSCMD_TD_FORMAT64";           break;
                case NSCMD_ETD_READ64  : result = "NSCMD_ETD_READ64";            break;
                case NSCMD_ETD_WRITE64 : result = "NSCMD_ETD_WRITE64";           break;
                case NSCMD_ETD_SEEK64  : result = "NSCMD_ETD_SEEK64";            break;
                case NSCMD_ETD_FORMAT64: result = "NSCMD_ETD_FORMAT64";          break;
                case CD_INFO           : result = "CD_INFO";                     break;
                case CD_CONFIG         : result = "CD_CONFIG";                   break;
                case CD_TOCMSF         : result = "CD_TOCMSF";                   break;
                case CD_TOCLSN         : result = "CD_TOCLSN";                   break;
                case CD_READXL         : result = "CD_READXL";                   break;
                case CD_PLAYTRACK      : result = "CD_PLAYTRACK";                break;
                case CD_PLAYMSF        : result = "CD_PLAYMSF";                  break;
                case CD_PLAYLSN        : result = "CD_PLAYLSN";                  break;
                case CD_PAUSE          : result = "CD_PAUSE";                    break;
                case CD_SEARCH         : result = "CD_SEARCH";                   break;
                case CD_QCODEMSF       : result = "CD_QCODEMSF";                 break;
                case CD_QCODELSN       : result = "CD_QCODELSN";                 break;
                case CD_ATTENUATE      : result = "CD_ATTENUATE";                break;
                case CD_ADDFRAMEINT    : result = "CD_ADDFRAMEINT";              break;
                case CD_REMFRAMEINT    : result = "CD_REMFRAMEINT";              break;
                default                : result = "<unknown trackdisk command>"; break;
            }
            break;

        case NSDEVTYPE_CONSOLE:
            switch (cmd) {
                case CD_ASKKEYMAP       : result = "CD_ASKKEYMAP";              break;
                case CD_SETKEYMAP       : result = "CD_SETKEYMAP";              break;
                case CD_ASKDEFAULTKEYMAP: result = "CD_ASKDEFAULTKEYMAP";       break;
                case CD_SETDEFAULTKEYMAP: result = "CD_SETDEFAULTKEYMAP";       break;
                default                 : result = "<unknown console command>"; break;
            }
            break;

        case NSDEVTYPE_SANA2:
            switch (cmd) {
                case S2_DEVICEQUERY           : result = "S2_DEVICEQUERY";            break;
                case S2_GETSTATIONADDRESS     : result = "S2_GETSTATIONADDRESS";      break;
                case S2_CONFIGINTERFACE       : result = "S2_CONFIGINTERFACE";        break;
                case S2_ADDMULTICASTADDRESS   : result = "S2_ADDMULTICASTADDRESS";    break;
                case S2_DELMULTICASTADDRESS   : result = "S2_DELMULTICASTADDRESS";    break;
                case S2_MULTICAST             : result = "S2_MULTICAST";              break;
                case S2_BROADCAST             : result = "S2_BROADCAST";              break;
                case S2_TRACKTYPE             : result = "S2_TRACKTYPE";              break;
                case S2_UNTRACKTYPE           : result = "S2_UNTRACKTYPE";            break;
                case S2_GETTYPESTATS          : result = "S2_GETTYPESTATS";           break;
                case S2_GETSPECIALSTATS       : result = "S2_GETSPECIALSTATS";        break;
                case S2_GETGLOBALSTATS        : result = "S2_GETGLOBALSTATS";         break;
                case S2_ONEVENT               : result = "S2_ONEVENT";                break;
                case S2_READORPHAN            : result = "S2_READORPHAN";             break;
                case S2_ONLINE                : result = "S2_ONLINE";                 break;
                case S2_OFFLINE               : result = "S2_OFFLINE";                break;
                case S2_ADDMULTICASTADDRESSES : result = "S2_ADDMULTICASTADDRESSES";  break;
                case S2_DELMULTICASTADDRESSES : result = "S2_DELMULTICASTADDRESSES";  break;
                case S2_GETPEERADDRESS        : result = "S2_GETPEERADDRESS";         break;
                case S2_GETDNSADDRESS         : result = "S2_GETDNSADDRESS";          break;
                case S2_GETEXTENDEDGLOBALSTATS: result = "S2_GETEXTENDEDGLOBALSTATS"; break;
                case S2_CONNECT               : result = "S2_CONNECT";                break;
                case S2_DISCONNECT            : result = "S2_DISCONNECT";             break;
                case S2_SAMPLE_THROUGHPUT     : result = "S2_SAMPLE_THROUGHPUT";      break;
                default                       : result = "<unknown sana2 command>";   break;
            }
            break;

        case NSDEVTYPE_AUDIO:
            switch (cmd) {
                case ADCMD_FREE     : result = "ADCMD_FREE";              break;
                case ADCMD_SETPREC  : result = "ADCMD_SETPREC";           break;
                case ADCMD_FINISH   : result = "ADCMD_FINISH";            break;
                case ADCMD_PERVOL   : result = "ADCMD_PERVOL";            break;
                case ADCMD_LOCK     : result = "ADCMD_LOCK";              break;
                case ADCMD_WAITCYCLE: result = "ADCMD_WAITCYCLE";         break;
                case ADCMD_ALLOCATE : result = "ADCMD_ALLOCATE";          break;
                default             : result = "<unknown audio command>"; break;
            }
            break;

        case NSDEVTYPE_CLIPBOARD:
            switch (cmd) {
                case CBD_POST          : result = "CBD_POST";                    break;
                case CBD_CURRENTREADID : result = "CBD_CURRENTREADID";           break;
                case CBD_CURRENTWRITEID: result = "CBD_CURRENTWRITEID";          break;
                case CBD_CHANGEHOOK    : result = "CBD_CHANGEHOOK";              break;
                default                : result = "<unknown clipboard command>"; break;
            }
            break;

        case NSDEVTYPE_PRINTER:
            switch (cmd) {
                case PRD_RAWWRITE     : result = "PRD_RAWWRITE";              break;
                case PRD_PRTCOMMAND   : result = "PRD_PRTCOMMAND";            break;
                case PRD_DUMPRPORT    : result = "PRD_DUMPRPORT";             break;
                case PRD_QUERY        : result = "PRD_QUERY";                 break;
                case PRD_RESETPREFS   : result = "PRD_RESETPREFS";            break;
                case PRD_LOADPREFS    : result = "PRD_LOADPREFS";             break;
                case PRD_USEPREFS     : result = "PRD_USEPREFS";              break;
                case PRD_SAVEPREFS    : result = "PRD_SAVEPREFS";             break;
                case PRD_READPREFS    : result = "PRD_READPREFS";             break;
                case PRD_WRITEPREFS   : result = "PRD_WRITEPREFS";            break;
                case PRD_EDITPREFS    : result = "PRD_EDITPREFS";             break;
                case PRD_SETERRHOOK   : result = "PRD_SETERRHOOK";            break;
                case PRD_DUMPRPORTTAGS: result = "PRD_DUMPRPORTTAGS";         break;
                default               : result = "<unknown printer command>"; break;
            }
            break;

        case NSDEVTYPE_SERIAL:
            switch (cmd) {
                case SDCMD_QUERY    : result = "SDCMD_QUERY";              break;
                case SDCMD_BREAK    : result = "SDCMD_BREAK";              break;
                case SDCMD_SETPARAMS: result = "SDCMD_SETPARAMS";          break;
                case CMD_NONSTD + 7 : result = "SIOCMD_SETCTRLLINES";      break;
                default             : result = "<unknown serial command>"; break;
            }
            break;

        case NSDEVTYPE_PARALLEL:
            switch (cmd) {
                case PDCMD_QUERY    : result = "PDCMD_QUERY";                break;
                case PDCMD_SETPARAMS: result = "PDCMD_SETPARAMS";            break;
                default             : result = "<unknown parallel command>"; break;
            }
            break;
    }

    if (result == NULL) result = "<unknown command>";

    return result;
}

static void SetDetails( struct IClass *cl,
                        Object *obj,
                        struct DeviceEntry *de )
{
    struct DevicesDetailWinData *ddwd = INST_DATA(cl, obj);
    struct Library *lib = &de->de_Addr->dd_Library;
    UBYTE flags[16];
    ULONG unit;

    Flags2BinStr(lib->lib_Flags, flags, sizeof(flags));

    MySetContentsHealed(ddwd->ddwd_DeviceMoreText[0], "%s", lib->lib_Node.ln_Name);
    MySetContents(ddwd->ddwd_DeviceMoreText[1], MUIX_C "$%08lx\n%ld\n%s\n%ld.%ld\n%lD", lib, lib->lib_Node.ln_Pri, GetNodeType(lib->lib_Node.ln_Type), lib->lib_Version, lib->lib_Revision, lib->lib_OpenCnt);
    MySetContents(ddwd->ddwd_DeviceMoreText[2], MUIX_B "%s", flags);
    MySetContents(ddwd->ddwd_DeviceMoreText[3], MUIX_C "%lD\n%lD\n$%08lx\n%s", lib->lib_NegSize, lib->lib_PosSize, lib->lib_Sum, de->de_RAMPtrCount);
    MySetContentsHealed(ddwd->ddwd_DeviceMoreText[4], "%s", lib->lib_IdString);

    ApplicationSleep(TRUE);

    for (unit = 0; unit < 16; unit++) {
        struct NSDeviceQueryResult nqr;
        LONG ioErr;
        UBYTE *nsdType;

        if ((ioErr = NSDQuery(lib->lib_Node.ln_Name, unit, &nqr)) == 0) {
            UWORD *cmds;

            set(ddwd->ddwd_DeviceMoreList, MUIA_NList_Quiet, TRUE);
            DoMethod(ddwd->ddwd_DeviceMoreList, MUIM_NList_Clear);

            cmds = nqr.SupportedCommands;
            while (*cmds) {
                struct NSDCommandEntry nce;

                nce.nce_Number = *cmds;
                _snprintf(nce.nce_DecNumber, sizeof(nce.nce_DecNumber), "%6lD", *cmds);
                _snprintf(nce.nce_HexNumber, sizeof(nce.nce_HexNumber), "$%04lx", *cmds);
                stccpy(nce.nce_CmdName, NSDTypeToCmdName(nqr.DeviceType, *cmds), sizeof(nce.nce_CmdName));

                InsertSortedEntry(ddwd->ddwd_DeviceMoreList, &nce);

                cmds++;
            }

            set(ddwd->ddwd_DeviceMoreList, MUIA_NList_Quiet, FALSE);

            switch (nqr.DeviceType) {
               case NSDEVTYPE_GAMEPORT:  nsdType = "gameport";  break;
               case NSDEVTYPE_TIMER:     nsdType = "timer";     break;
               case NSDEVTYPE_KEYBOARD:  nsdType = "keyboard";  break;
               case NSDEVTYPE_INPUT:     nsdType = "input";     break;
               case NSDEVTYPE_TRACKDISK: nsdType = "trackdisk"; break;
               case NSDEVTYPE_CONSOLE:   nsdType = "console";   break;
               case NSDEVTYPE_SANA2:     nsdType = "sana2";     break;
               case NSDEVTYPE_AUDIO:     nsdType = "audio";     break;
               case NSDEVTYPE_CLIPBOARD: nsdType = "clipboard"; break;
               case NSDEVTYPE_PRINTER:   nsdType = "printer";   break;
               case NSDEVTYPE_SERIAL:    nsdType = "serial";    break;
               case NSDEVTYPE_PARALLEL:  nsdType = "parallel";  break;
               default:                  nsdType = "unknown";   break;
            }

            MySetContents(ddwd->ddwd_DeviceMoreText[5], "%s (subtype %ld)", nsdType, nqr.DeviceSubType);

            break;
        } else {
            switch (ioErr) {
               default:
               case IOERR_OPENFAIL: nsdType = "unknown (device could not be openend)"; break;
               case IOERR_NOCMD:    nsdType = "unknown (device does not support NSD)"; break;
            }

            MySetContents(ddwd->ddwd_DeviceMoreText[5], "%s", nsdType);

            if (ioErr == IOERR_NOCMD) break;
        }
    }

    set(obj, MUIA_Window_Title, MyGetChildWindowTitle("DEVICE", de->de_Name, ddwd->ddwd_Title, sizeof(ddwd->ddwd_Title)));

    ApplicationSleep(FALSE);
}

static ULONG __saveds mNew( struct IClass *cl,
                            Object *obj,
                            struct opSet *msg )
{
    APTR devmoretext[6], nsdCmdList;

    if (obj = (Object *)DoSuperNew(cl, obj,
        MUIA_HelpNode, DevicesText,
        MUIA_Window_ID, MakeID('.','D','E','V'),
        WindowContents, VGroup,

            Child, HGroup,
                Child, VGroup, MUIA_Group_SameWidth, TRUE,
                    Child, MyLabel2("Name:"),
                    Child, MyVSpace(0),
                    Child, MyLabel2("Address:\nPri:\nType:\nVersion:\nOpenCnt:"),
                    Child, MyVSpace(0),
                    Child, MyLabel2("IdString:"),
                    Child, MyVSpace(0),
                    Child, MyLabel2("NSDType:"),
                End,
                Child, VGroup,
                    Child, devmoretext[0] = MyTextObject(),
                    Child, HGroup,
                        Child, devmoretext[1] = MyTextObject(),
                        Child, VGroup,
                            Child, MyLabel ("Flags:"),
                            Child, MyLabel ("NegSize:\nPosSize:\nSum:\nRamPtrCnt:"),
                        End,
                        Child, VGroup,
                            Child, devmoretext[2] = MakeButton(""),
                            Child, devmoretext[3] = MyTextObject(),
                        End,
                    End,
                    Child, devmoretext[4] = MyTextObject(),
                    Child, devmoretext[5] = MyTextObject(),
                End,
            End,
            Child, VGroup,
                Child, HGroup,
                    Child, MyHSpace(0),
                    Child, MyLabel2(txtSupportedCommands),
                    Child, MyHSpace(0),
                End,
                Child, nsdCmdList = MyNListviewObject(MakeID('.','D','L','V'), "BAR P=" MUIX_R ",BAR P=" MUIX_R ",BAR", &nsdlist_con2hook, &nsdlist_des2hook, &nsdlist_dsp2hook, &nsdlist_cmp2hook, FALSE),
            End,
        End,
        TAG_MORE, msg->ops_AttrList))
    {
        struct DevicesDetailWinData *ddwd = INST_DATA(cl, obj);
        APTR parent;

        CopyMem(devmoretext, ddwd->ddwd_DeviceMoreText, sizeof(ddwd->ddwd_DeviceMoreText));
        ddwd->ddwd_DeviceMoreList = nsdCmdList;

        parent = (APTR)GetTagData(MUIA_Window_ParentWindow, (ULONG)NULL, msg->ops_AttrList);

        set(obj, MUIA_Window_ActiveObject, nsdCmdList);

        DoMethod(parent,         MUIM_Window_AddChildWindow, obj);
        DoMethod(obj,            MUIM_Notify,              MUIA_Window_CloseRequest, TRUE,  MUIV_Notify_Application, 5, MUIM_Application_PushMethod, parent, 2, MUIM_Window_RemChildWindow, obj);
        DoMethod(devmoretext[2], MUIM_Notify,              MUIA_Pressed,             FALSE, obj,                     1, MUIM_DevicesDetailWin_DeviceFlagsMore);
        DoMethod(nsdCmdList, MUIM_NList_Sort3, MUIV_NList_Sort3_SortType_1, MUIV_NList_SortTypeAdd_None, MUIV_NList_Sort3_SortType_Both);
    }

    return (ULONG)obj;
}

static ULONG __saveds mDispose( struct IClass *cl,
                                Object *obj,
                                struct opSet *msg )
{
    set(obj, MUIA_Window_Open, FALSE);

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

static ULONG __saveds mSet( struct IClass *cl,
                            Object *obj,
                            Msg msg )
{
    struct DevicesDetailWinData *ddwd = INST_DATA(cl, obj);
    struct TagItem *tags, *tag;

    for (tags = ((struct opSet *)msg)->ops_AttrList; tag = NextTagItem(&tags); ) {
        switch (tag->ti_Tag) {
            case MUIA_Window_ParentWindow:
                DoMethod(obj, MUIM_Notify, MUIA_Window_CloseRequest, TRUE, MUIV_Notify_Application, 5, MUIM_Application_PushMethod, (APTR)tag->ti_Data, 2, MUIM_Window_RemChildWindow, obj);
                break;

            case MUIA_DevicesDetailWin_Device:
                ddwd->ddwd_Device = (struct DeviceEntry *)tag->ti_Data;
                SetDetails(cl, obj, (struct DeviceEntry *)tag->ti_Data);
                break;
        }
    }

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

static ULONG __saveds mDeviceFlagsMore( struct IClass *cl,
                                        Object *obj,
                                        Msg msg )
{
    struct DevicesDetailWinData *ddwd = INST_DATA(cl, obj);
    APTR flagsWin;

    if (flagsWin = FlagsWindowObject,
            MUIA_Window_Title, "Device Flags",
            MUIA_Window_ParentWindow, obj,
        End) {
        DoMethod(flagsWin, MUIM_FlagsWin_ShowFlags, ddwd->ddwd_Device->de_Addr->dd_Library.lib_Flags, MUIV_FlagsWin_FlagsType_Byte, devFlags, NULL, "lib_Flags");
    }

    return 0;
}

ULONG __asm __saveds DevicesDetailWinDispatcher( register __a0 struct IClass *cl,
                                                 register __a2 Object *obj,
                                                 register __a1 Msg msg )
{
    switch (msg->MethodID) {
        case OM_NEW:                                return (mNew(cl, obj, (APTR)msg));
        case OM_DISPOSE:                            return (mDispose(cl, obj, (APTR)msg));
        case OM_SET:                                return (mSet(cl, obj, (APTR)msg));
        case MUIM_DevicesDetailWin_DeviceFlagsMore: return (mDeviceFlagsMore(cl, obj, (APTR)msg));
    }

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

