Source/NewAppletDialog.cp

/*
 * NewAppletDialog.cp
 */
 
#include "NewAppletDialog.h"
#include "StringListResource.h"
 
const cAppletDialog = 1129;
const cAddEditDialog = 1130;
 
enum AppletItems {
    eAddItem = 3,
    eChooseLabel,
    eListArea,
    eURLArea,
    eEditButton,
    eDeleteButton,
    eRandomCheckbox
};
 
enum AddEditItems {
    eNameLabel = 3,
    eURLLabel,
    eNameText,
    eURLText
};
 
struct FontSaver {
    short saveFace, saveSize, saveFont;
};
 
static void setupDialogFont(DialogPtr dialog, short fontSize, FontSaver* saver)
{
    SetPort(dialog);
    TextFont(geneva);
    TextFace(normal);
    TextSize(fontSize);
    saver->saveFont = dialog->txFont;
    saver->saveSize = dialog->txSize;
    saver->saveFace = dialog->txFace;
}
 
static void restoreDialogFont(FontSaver* saver)
{
    TextFont(saver->saveFont);
    TextFace(saver->saveFace);
    TextSize(saver->saveSize);
}
 
/*
 * doAddDialog
 */
 
static void setEditTextItem(DialogPtr dialog, int ixItem, Str255 text)
{
    short itemType;
    Rect itemRect;
    Handle itemHandle;
    GetDItem(dialog, ixItem, &itemType, &itemHandle, &itemRect);
    SetIText(itemHandle, text);
}
 
static void getEditTextItem(DialogPtr dialog, int ixItem, Str255 text)
{
    short itemType;
    Rect itemRect;
    Handle itemHandle;
    GetDItem(dialog, ixItem, &itemType, &itemHandle, &itemRect);
    GetIText(itemHandle, text);
}
 
static Boolean doAddDialog(Str255 itemLabel, Str255 itemURL)
{
    Boolean result = false;
    DialogPtr dialog = GetNewDialog(cAddEditDialog, nil, WindowPtr(-1));
    if (dialog) {
        SetDialogDefaultItem(dialog, ok);
        SetDialogCancelItem(dialog, cancel);
        SetDialogTracksCursor(dialog, true);
        
        setEditTextItem(dialog, eNameText, itemLabel);
        setEditTextItem(dialog, eURLText, itemURL); 
        SelIText(dialog, eNameText, 0, 32767);
        
        short itemHit;
        Boolean continueLoop = true;
        
        ShowWindow(dialog);
        
        do {
            ModalDialog(nil, &itemHit);
            
            switch (itemHit) {
                case ok:
                    continueLoop = false;
                    getEditTextItem(dialog, eNameText, itemLabel);
                    getEditTextItem(dialog, eURLText, itemURL);
                    result = true;
                    break;
                
                case cancel:
                    continueLoop = false;
                    break;
                
                case eNameLabel:
                    SelIText(dialog, eNameText, 0, 32767);
                    break;
                
                case eURLLabel:
                    SelIText(dialog, eURLText, 0, 32767);
                    break;
            }
        } while (continueLoop);     
        
        DisposeDialog(dialog);
    }
    return result && itemLabel[0] > 0 && itemURL[0] > 0;
}
 
/*
 * newAppletDialog
 */
 
static ListHandle theList;
static UserItemUPP theListUPP = nil;
static UserItemUPP theURLUPP = nil;
 
static Handle theLabelTexts;
static Handle theURLTexts;
static long theCurrentItem = 0;
static Boolean theRandomSet;
 
static void selectItem(int ixCell)
{
    Cell cell = { 0, 0 };
    while (LGetSelect(true, &cell, theList)) {  
        LSetSelect(false, cell, theList);
    }
    cell.v = ixCell;
    LSetSelect(true, cell, theList);
    LAutoScroll(theList);
}
 
static void addToList(Str255 itemLabel, Str255 itemURL)
{
    Cell cell;
    cell.h = 0;
    cell.v = LAddRow(1, -1, theList);
    LSetCell(&itemLabel[1], itemLabel[0], cell, theList);
    PtrAndHand(itemLabel, theLabelTexts, 256);
    PtrAndHand(itemURL, theURLTexts, 256);
    selectItem(cell.v);
}
 
static void getCurrentItem(StringPtr itemLabel, StringPtr itemURL)
{
    if (theCurrentItem == -1) {
        if (itemLabel != nil)
            itemLabel[0] = 0;
        if (itemURL != nil)
            itemURL[0] = 0;
    } else {
        if (itemLabel)
            BlockMoveData(*theLabelTexts + (256 * theCurrentItem), itemLabel, 256);
        if (itemURL)
            BlockMoveData(*theURLTexts + (256 * theCurrentItem), itemURL, 256);
    }
}
 
static void updateURLArea(DialogPtr dialog, Boolean justDoIt)
{
    Cell cell = { 0, 0 };
    short newIndex;
 
    if (! LGetSelect(true, &cell, theList))
        newIndex = -1;
    else
        newIndex = cell.v;
        
    if (newIndex != theCurrentItem || justDoIt) {
        short itemType;
        Rect itemRect;
        Handle itemHandle;
        GetDItem(dialog, eURLArea, &itemType, &itemHandle, &itemRect);
        InsetRect(&itemRect, 1, 1);
        InvalRect(&itemRect);
    }
    theCurrentItem = newIndex;
}
 
static void deleteCurrentItem(DialogPtr dialog)
{
    if (theCurrentItem != -1) {
        Munger(theLabelTexts, (256 * theCurrentItem), nil, 256, Ptr(-1), 0);
        Munger(theURLTexts, (256 * theCurrentItem), nil, 256, Ptr(-1), 0);
 
        Cell cell;
        cell.h = 0;
        cell.v = theCurrentItem;
        LDelRow(1, theCurrentItem, theList);
        theCurrentItem--;
        if (theCurrentItem < 0)
            theCurrentItem = 0;
        selectItem(theCurrentItem);
        updateURLArea(dialog, true);
    }
}
 
static void setCurrentItem(DialogPtr dialog, StringPtr itemLabel, StringPtr itemURL)
{
    Cell cell;
    
    cell.h = 0;
    cell.v = theCurrentItem;
 
    LSetCell(&itemLabel[1], itemLabel[0], cell, theList);
 
    BlockMoveData(itemLabel, *theLabelTexts + (256 * theCurrentItem), 256);
    BlockMoveData(itemURL, *theURLTexts + (256 * theCurrentItem), 256);
 
    updateURLArea(dialog, true);
}
 
static pascal void _ListProc(DialogPtr dialog, short itemNumber)
{
    short itemType;
    Rect itemRect;
    Handle itemHandle;
    GetDItem(dialog, itemNumber, &itemType, &itemHandle, &itemRect);
 
    FontSaver saver;
    setupDialogFont(dialog, 12, &saver);
 
    MoveTo(itemRect.right, itemRect.top - 1);
    LineTo(itemRect.left, itemRect.top - 1);
    LineTo(itemRect.left, itemRect.bottom);
    LineTo(itemRect.right, itemRect.bottom);
    LUpdate(dialog->visRgn, theList);
 
    restoreDialogFont(&saver);
}
 
static pascal void _URLProc(DialogPtr dialog, short itemNumber)
{
    short itemType;
    Rect itemRect;
    Handle itemHandle;
    GetDItem(dialog, itemNumber, &itemType, &itemHandle, &itemRect);
    FontSaver saver;
    setupDialogFont(dialog, 9, &saver);
    FrameRect(&itemRect);
    InsetRect(&itemRect, 1, 1);
    EraseRect(&itemRect);
    MoveTo(itemRect.left + 2, itemRect.bottom - 3);
    Str255 url;
    getCurrentItem(nil, url);
    DrawString(url);
    restoreDialogFont(&saver);
}
 
static void setupUserItems(DialogPtr dialog)
{
    /*
     * Others...
     */
     
    short itemType;
    Rect itemRect;
    Handle itemHandle;
    
    FontSaver saver;
    setupDialogFont(dialog, 12, &saver);
 
    /*
     * List
     */
    GetDItem(dialog, eListArea, &itemType, &itemHandle, &itemRect);
    
    Rect rBounds = { 0, 0, 0, 1 };
    Point cellBounds = { 0, 0 };
    Rect listRect;
    listRect = itemRect;
    listRect.left += 1;
    listRect.right -= 16;
    theList = LNew(&listRect, &rBounds, cellBounds, 0, dialog, false, false, false, true);
    (**theList).selFlags = lOnlyOne;
    
    if (theListUPP == nil)
        theListUPP = NewUserItemProc(_ListProc);
 
    SetDItem(dialog, eListArea, itemType, Handle(theListUPP), &itemRect);
    LDoDraw(true, theList);
    
    /*
     * URL label
     */
    GetDItem(dialog, eURLArea, &itemType, &itemHandle, &itemRect);
    if (theURLUPP == nil)
        theURLUPP = NewUserItemProc(_URLProc);
    SetDItem(dialog, eURLArea, itemType, Handle(theURLUPP), &itemRect);
 
    restoreDialogFont(&saver);
}
 
static short openPrefsFile()
{
    FSSpec spec;
    short vRefNum;
    long parID;
    FindFolder(kOnSystemDisk, kPreferencesFolderType, true, &vRefNum, &parID);
    if (FSMakeFSSpec(vRefNum, parID, "\pApplet Screen Saver Prefs", &spec) == fnfErr) {
        FSpCreateResFile(&spec, 'WARZ', 'PREF', 0);
    }
    return FSpOpenResFile(&spec, fsRdWrPerm);
}
 
static void addToGlobals(Str255 itemLabel, Str255 itemURL)
{
    PtrAndHand(itemLabel, theLabelTexts, 256);
    PtrAndHand(itemURL, theURLTexts, 256);
}
 
static void getGlobals()
{
    short fRefNum = openPrefsFile();
    if (fRefNum != -1) {
        theLabelTexts = GetResource('STNG', 128);
        theURLTexts = GetResource('STNG', 129);
 
        if (theLabelTexts != nil)
            DetachResource(theLabelTexts);
        if (theURLTexts != nil)
            DetachResource(theURLTexts);
    
        Handle hRandom = GetResource('RAND', 128);
        if (hRandom == nil)
            theRandomSet = false;
        else {
            theRandomSet = **hRandom != 0;
            ReleaseResource(hRandom);
        }
        
        Handle hItem = GetResource('SELE', 128);
        if (hItem == nil)
            theCurrentItem = 0;
        else {
            theCurrentItem = *(long*) *hItem;
            ReleaseResource(hItem);
        }
        
        CloseResFile(fRefNum);
    }
 
    if (theLabelTexts == nil || theURLTexts == nil) {
        theLabelTexts = NewHandle(0);
        theURLTexts = NewHandle(0);
 
#if 0
    #define XX(a,b) \
        addToGlobals("\p" a, "\p" b)
        
        XX("Earthweb's Netris", "http://www.earthweb.com/java/Netris/index.html");
        XX("Rusotto's ZCode Interpreter", "http://www.pond.com/~russotto/zplet/minizork.html");
        XX("HypnoVista Splash", "http://www.hypno.com/javabeta/fromandy/splash/splash.html");
        XX("HypnoVista Bongo Dude", "http://www.hypno.com/javabeta/bongo/bongo.html");
        XX("HypnoVista Running Man", "http://www.hypno.com/javabeta/runner/file.html");
        XX("HypnoVista Hacker", "http://www.hypno.com/javabeta/hacker/file.html");
        XX("HypnoVista Fish", "http://www.hypno.com/javabeta/fish/file.html");
        XX("Circus Animations", "http://circus.compuware.com/");
        XX("At a Distance (Game)", "http://www.cs.cmu.edu/afs/andrew/usr/scier/www/AtADistance.html");
        XX("CERN Accelerator Watch", "http://hpslweb.cern.ch/teletext/java/view110-java.html");
        XX("Sun Golf", "http://www.sun.nl/SunOpenGame/Course1.html");
        XX("Sieve Benchmark", "http://rsb.info.nih.gov/nih-image/Java/Benchmarks/Sieve.html");
        XX("FrogPond", "http://sepwww.stanford.edu/sep/krl/FrogPond/FrogPond.html");
        XX("Bounce Simulator", "http://www.chem.uci.edu/instruction/applets/bounce.html");
        XX("Atmosphere Simulator", "http://www.chem.uci.edu/instruction/applets/canonical.html");
#endif
 
        StringListResource defaultNames(0);
        StringListResource defaultURLs(1);
        
        StringPtr names = defaultNames.First();
        StringPtr urls = defaultURLs.First();
        while (names && urls) {
            addToGlobals(names, urls);
            names = defaultNames.Next();
            urls = defaultURLs.Next();
        }
 
    #undef XX
    }
}
 
static void disposeGlobals()
{
    DisposeHandle(theLabelTexts);
    theLabelTexts = nil;
    DisposeHandle(theURLTexts);
    theURLTexts = nil;
    theList = nil;
}
 
static void setRandomCheckbox(DialogPtr dialog)
{
    short itemType;
    Rect itemRect;
    Handle itemHandle;
    GetDItem(dialog, eRandomCheckbox, &itemType, &itemHandle, &itemRect);
    SetControlValue(ControlHandle(itemHandle), theRandomSet? 1 : 0);
}
 
static void addItems(DialogPtr dialog)
{
    int ctCells = GetHandleSize(theLabelTexts) / sizeof(Str255);
    Cell cell;
    cell.h = 0;
    
    LAddRow(ctCells, -1, theList);
    
    HLock(theLabelTexts);
    char* p = *theLabelTexts;
    for (cell.v = 0;  cell.v < ctCells;  cell.v++) {
        LSetCell(&p[1], p[0], cell, theList);
        p += 256;
    }
 
    setRandomCheckbox(dialog);
}
 
static void updateResource(OSType type, short id, Handle newResource)
{
    Handle hToss = GetResource(type, id);
    if (hToss != nil) {
        RmveResource(hToss);
        DisposeHandle(hToss);
    }
    AddResource(newResource, type, id, "\p");
    WriteResource(newResource);
    DetachResource(newResource);
}
 
static void writeResources()
{
    short fRefNum = openPrefsFile();
    if (fRefNum == -1)
        return;
    updateResource('STNG', 128, theLabelTexts);
    updateResource('STNG', 129, theURLTexts);
    Handle hRandom = NewHandle(1);
    if (hRandom) {
        **hRandom = theRandomSet;
        updateResource('RAND', 128, hRandom);
        DisposeHandle(hRandom);
    }
    Handle hItem = NewHandle(sizeof(long));
    if (hItem) {
        *(long*) *hItem = theCurrentItem;
        updateResource('SELE', 128, hItem);
        DisposeHandle(hItem);
    }
    CloseResFile(fRefNum);
}
 
/*
 * FilterProc
 */
 
static ModalFilterUPP theStdFilterProc;
static ModalFilterUPP theFilterProc = nil;
 
static pascal Boolean _ModalFilter(DialogPtr theDialog, EventRecord *eve, short *itemHit)
{
    Boolean result = false;
    
    *itemHit = -1;
    
    switch (eve->what) {
        case mouseDown: {
            Point localPos = eve->where;
 
            short itemType;
            Rect itemRect;
            Handle itemHandle;
            GetDItem(theDialog, eListArea, &itemType, &itemHandle, &itemRect);
 
            FontSaver saver;
            setupDialogFont(theDialog, 12, &saver);
            
            GlobalToLocal(&localPos);
            if (PtInRect(localPos, &itemRect)) {
                if (LClick(localPos, eve->modifiers, theList)) {
                    *itemHit = eListArea;
                    result = true;
                }
                updateURLArea(theDialog, false);
            }
            
            restoreDialogFont(&saver);
        }   break;
        
        case keyDown: {
            char ch = eve->message & charCodeMask;
            if ((ch == 'c' || ch == 'C' || ch == '' || ch == '‚') && (eve->modifiers & cmdKey) != 0) {
                Str255 itemURL;
                
                // if cmd-key, just copy the selected one.  If option, copy all
                if ((eve->modifiers & optionKey) != 0) {
                    Handle hText = NewHandle(0);
                    int ctItems = GetHandleSize(theURLTexts) / 256;
                    HLock(theURLTexts);
                    char* p = *theURLTexts;
                    for (int i = 0;  i < ctItems;  i++) {
                        PtrAndHand(&p[1], hText, p[0]);
                        PtrAndHand("\r", hText, 1);
                        p += 256;
                    }
                    HUnlock(theURLTexts);
                    HLock(hText);
                    ZeroScrap();
                    PutScrap(GetHandleSize(hText), 'TEXT', *hText);
                    DisposeHandle(hText);
                } else {
                    getCurrentItem(nil, itemURL);
                    if (itemURL[0] > 0) {
                        ZeroScrap();
                        PutScrap(itemURL[0], 'TEXT', &itemURL[1]);
                    }
                }
            } else {
                int newSel = theCurrentItem;
                
                result = true;
                
                switch (ch) {
                    case 30:    // up
                        newSel--;
                        break;
                        
                    case 31:    // down
                        newSel++;
                        break;
                        
                    case 1:     // home
                        newSel = 0;
                        break;
                        
                    case 4:     // end
                        newSel = (**theList).dataBounds.bottom;             
                        break;
                        
                    case 11: {  // pup
                        Rect* rView = &(**theList).rView;
                        Point pp = (**theList).cellSize;
                        newSel -= ((rView->bottom - rView->top) / pp.v) - 1;
                    }   break;
 
                    case 12: {  // pdown
                        Rect* rView = &(**theList).rView;
                        Point pp = (**theList).cellSize;
                        newSel += ((rView->bottom - rView->top) / pp.v) - 1;
                    }   break;
                
                    default:
                        result = false;
                }
 
                if (result == true) {
                    if (newSel < 0)
                        newSel = 0;
                    if (newSel >= (**theList).dataBounds.bottom)
                        newSel = (**theList).dataBounds.bottom - 1; 
                    if (newSel != theCurrentItem) {
                        FontSaver saver;
                        setupDialogFont(theDialog, 12, &saver);
                
                        selectItem(newSel);
                        *itemHit = -1;
                        updateURLArea(theDialog, false);
 
                        restoreDialogFont(&saver);
                    }
                }
            }               
        }   break;
    }
    
    if (result == false && theStdFilterProc != nil)
        result = CallModalFilterProc(theStdFilterProc, theDialog, eve, itemHit);
 
    return result;
}
 
static void hiliteDialogControl(DialogPtr dialog, short ixItem)
{
    short itemType;
    Rect itemRect;
    Handle itemHandle;
    GetDItem(dialog, ixItem, &itemType, &itemHandle, &itemRect);
    HiliteControl(ControlHandle(itemHandle), inButton);
    long actual; Delay(GetDblTime() / 3, &actual);
    HiliteControl(ControlHandle(itemHandle), 0);
}
 
void newAppletDialog()
{
    Str255 itemLabel;
    Str255 itemURL;
    Boolean doRunApplet = nil;
    DialogPtr dialog = GetNewDialog(cAppletDialog, nil, WindowPtr(-1));
    if (dialog) {
        getGlobals();
 
        GetStdFilterProc(&theStdFilterProc);
        if (theFilterProc == nil)
            theFilterProc = NewModalFilterProc(_ModalFilter);
        
        setupUserItems(dialog);
        
        addItems(dialog);
        
        SetDialogDefaultItem(dialog, ok);
        SetDialogCancelItem(dialog, cancel);
        
        short itemHit;
        Boolean continueLoop = true;
        
        selectItem(theCurrentItem);
 
        ShowWindow(dialog);
 
        do {
            ModalDialog(theFilterProc, &itemHit);
            
            switch (itemHit) {
                case eListArea:
                    hiliteDialogControl(dialog, ok);
                    // fall through
 
                case ok:
                    if (theCurrentItem != -1) {
                        getCurrentItem(nil, itemURL);
                        doRunApplet = true;
                    }
                    HideWindow(dialog);
                    writeResources();
                    // fall through
                                
                case cancel:
                    continueLoop = false;
                    break;
 
                case eRandomCheckbox:
                    theRandomSet = ! theRandomSet;
                    setRandomCheckbox(dialog);
                    break;
                                    
                case eAddItem: {
                    itemLabel[0] = 0;
                    itemURL[0] = 0;
                    if (doAddDialog(itemLabel, itemURL)) {
                        addToList(itemLabel, itemURL);
                        updateURLArea(dialog, true);
                    }
                }   break;
                    
                case eEditButton: {
                    getCurrentItem(itemLabel, itemURL);
                    if (doAddDialog(itemLabel, itemURL)) {
                        setCurrentItem(dialog, itemLabel, itemURL);
                    }
                }   break;
                
                case eDeleteButton: {
                    deleteCurrentItem(dialog);
                }   break;
            }
        } while (continueLoop);     
 
        // dispose other user items?        
        LDispose(theList);
 
        DisposeDialog(dialog);
 
        disposeGlobals();
    }
}
 
void getSelectedURL(Str255 urlString, Str255 urlName)
{
    urlString[0] = 0;
    getGlobals();
 
    if (theURLTexts != nil) {
        int ctItems = GetHandleSize(theURLTexts) / 256;
        if (ctItems > 0) {
            int ixSelected;
 
            if (theRandomSet)
                ixSelected = (Random() & 0x7fff) % ctItems;
            else
                ixSelected = theCurrentItem;
 
            if (ixSelected < 0)
                ixSelected = 0;
            if (ixSelected >= ctItems)
                ixSelected = ctItems - 1;
 
            BlockMoveData(*theLabelTexts + ixSelected * 256, urlName, 256);
            BlockMoveData(*theURLTexts + ixSelected * 256, urlString, 256);
        }
    }
    
    if (urlString[0] == 0) {
        static const unsigned char cDefault[] = "\phttp://www.hypno.com/javabeta/fromandy/splash/splash.html";
        static const unsigned char cDefaultName[] = "\pEmergency default module.";
        BlockMoveData(cDefault, urlString, cDefault[0] + 1);
        BlockMoveData(cDefaultName, urlName, cDefaultName[0] + 1);
    }
 
    disposeGlobals();
}