ValueControls ƒ/ValueControls.c

#include <Printing.h>
#include <Windows.h>
#include <Fonts.h>
#include <Devices.h>
#include <ToolUtils.h>
#include <TextUtils.h>
#include <LowMem.h>
#include <SegLoad.h>
#include <fp.h>
 
#include "ValueControls.h"
 
#define kVCHeight   12
#define kVCWidth    192
#define kValuePos   142 // right edge (right flush)
#define kDecHitPos  156
#define kIncHitPos  172
#define kResetWidth  36
 
enum { 
    kSettingsDlog = 1280
};
 
enum {
    iVCtitle = 3,
    iMinValue,
    iMaxValue,
    iStepSize,
    iCurrentV
};
 
// private structures
 
struct ValueCtlRec {
    Str255      title;
    float       initV;
    float       currV;
    float       mini;
    float       maxi;
    float       delta;
    Point       titlePos;   // absolute window coordinates; initialized from ValueCtlCluster
    Point       valuePos;
    Rect        wholeRect;
    Rect        incrHit;
    Rect        decrHit;
    long        lastClick;
    Boolean     isCheckBox;
};
typedef struct ValueCtlRec ValueCtlRec, *VcPtr;
 
struct ValueCtlCluster {
    OSType      idTag;
    Str255      vTitle;
    long        howMany;            // 1-based
    Rect        frame;              // tries to position controls by itself; 
                                    // may add scrollbars if required, later.
    Rect        resetHit;
    long        vcHeight;           // height of one ValueCtlRec in pixels
    long        maxEntries;         // 1-based
    VcPtr       vCtls[1];           // 0-based
};
typedef struct ValueCtlCluster ValueCtlCluster;
 
static VcPtr    NewValueCtl(Str255 title, float initV, float mini, float maxi, float delta);
static void     DrawValueCtl(VcPtr v);
static void     IncrementVC(VcPtr v);
static void     DecrementVC(VcPtr v);
static Boolean  DoSettingsDialog(VcPtr v);
 
// from TextUtils.h:
extern pascal StringPtr C2PStr(Ptr cString);
extern pascal Ptr P2CStr(StringPtr pString);
 
// from my dialog utilities:
void    PutFloat(DialogPtr dlg, short item, float value);
float   GetFloat(DialogPtr dlg, short item);
 
//----------------------------------------------------------------------
VcPtr   NewValueCtl(Str255 title, float initV, float mini, float maxi, float delta)
{
    VcPtr   v;
    Rect        r;
    
    v = (VcPtr)NewPtr(sizeof(ValueCtlRec));
    if (v != nil) {
        BlockMove((Ptr)title, (Ptr)v->title, (long)title[0]+1);
        v->initV = initV;
        v->currV = initV;
        v->mini  = mini;
        v->maxi  = maxi;
        v->delta = delta;
        v->titlePos.h = 4;
        v->titlePos.v = kVCHeight - 2;
        v->valuePos.h = kValuePos;
        v->valuePos.v = kVCHeight - 2;
        SetRect(&v->wholeRect, 0, 0, kVCWidth, kVCHeight);
        SetRect(&r, 0, 0, kVCHeight - 2, kVCHeight - 2);
        OffsetRect(&r, kDecHitPos, 1);
        v->decrHit  = r;
        OffsetRect(&r, kIncHitPos - kDecHitPos, 0);
        v->incrHit  = r;
        v->lastClick = 0;
        v->isCheckBox = ((mini == 0.0) && (maxi == 1.0) && (delta == 1.0));
    }
    return v;
}
 
//----------------------------------------------------------------------
VccPtr  NewVCluster(long tag, Str255 title, long numEntries, Rect *bounds)
{
    Rect    r = *bounds; // avoid side effects on bounds parameter!
    long    size;
    VccPtr  vcc;
    
    InsetRect(&r, 4, 4);
    r.top += 4;
    if ( (numEntries + 1) * kVCHeight > (r.bottom - r.top) )
        return nil; // don't have enough space
        
    r.bottom = r.top + numEntries * kVCHeight + 10;
    size = sizeof(ValueCtlCluster) + (numEntries - 1) * sizeof(VcPtr);
    vcc = (VccPtr)NewPtr(size);
    if (vcc != nil) {
        vcc->idTag = tag;
        BlockMove((Ptr)title, (Ptr)vcc->vTitle, (long)title[0] + 1);
        vcc->howMany = 0;
        vcc->frame = r;
        vcc->vcHeight = kVCHeight;
        vcc->maxEntries = numEntries;
        SetRect(&r, 0, 0, kResetWidth, kVCHeight - 1);
        OffsetRect(&r, vcc->frame.right - kResetWidth - 6, vcc->frame.top - 6);
        vcc->resetHit = r;
    }
    return vcc;
}
 
 
//----------------------------------------------------------------------
void AddValueCtl(VccPtr vcc, Str255 title, float initV, float mini, float maxi, float delta)
 
{
    VcPtr   v;
    Point   offset;
    
    if (vcc->howMany >= vcc->maxEntries)
        return;
    v = NewValueCtl(title, initV, mini, maxi, delta);
    if (!v)
        return;
    offset.v = vcc->frame.top + (vcc->howMany + 1) * (vcc->vcHeight) - 6;
    offset.h = vcc->frame.left + 2;
    AddPt(offset, &v->titlePos);
    AddPt(offset, &v->valuePos);
    OffsetRect(&v->wholeRect, offset.h, offset.v);
    SectRect(&vcc->frame, &v->wholeRect, &v->wholeRect);
    v->wholeRect.right -= 2;
    OffsetRect(&v->incrHit, offset.h, offset.v);
    OffsetRect(&v->decrHit, offset.h, offset.v);
    vcc->vCtls[vcc->howMany] = v;
    vcc->howMany++;
}
 
//----------------------------------------------------------------------
void    AddSeparator(VccPtr vcc)
{
    AddValueCtl(vcc, "\p------------", 0.0,  0.0,  0.0, 0.0);
}
 
 
//----------------------------------------------------------------------
Boolean TakeHit(Point clickPt, VccPtr vcc)
{
    long        i, ticks;
    Rect        r;
    Boolean     isDblClick = false;
    
    i = 0;
    r = vcc->resetHit;
    if ( PtInRect(clickPt, &r) ) { // an afterthought ...
        InvertRect(&r);
         do {
            vcc->vCtls[i]->currV = vcc->vCtls[i]->initV;
            DrawValueCtl(vcc->vCtls[i]);
        } while (++i < vcc->howMany);
        InvertRect(&r);
        return true;
    }
 
    // i = 0;
    
    do {
        if ((vcc->vCtls)[i]->mini < (vcc->vCtls)[i]->maxi) { 
        // take hits only in real control lines
            r = (vcc->vCtls)[i]->incrHit;
            if ( PtInRect(clickPt, &r) ) {
                InvertRect(&r);
                IncrementVC((vcc->vCtls)[i]);
                goto newValue;
            }
            r = (vcc->vCtls)[i]->decrHit;
            if ( PtInRect(clickPt, &r) ) {
                InvertRect(&r);
                DecrementVC((vcc->vCtls)[i]);
                goto newValue;
            }
            r = (vcc->vCtls)[i]->wholeRect;
            if ( PtInRect(clickPt, &r) ) {
                ticks = ((vcc->vCtls)[i])->lastClick;
                isDblClick = (TickCount() - ticks < LMGetDoubleTime());
                ((vcc->vCtls)[i])->lastClick = TickCount();
            }   
            if ((isDblClick) && DoSettingsDialog((vcc->vCtls)[i])) {
                DrawValueCtl((vcc->vCtls)[i]);
                return true;
            }
            else
                isDblClick = false;
        }
    }  while ( (++i < vcc->howMany) );
 
    return false;
    
newValue:
        Delay(6, &ticks);
        InvertRect(&r);
        ((vcc->vCtls)[i])->lastClick = TickCount();
        DrawValueCtl((vcc->vCtls)[i]);
        return true;
}
 
 
//----------------------------------------------------------------------
static void DrawValueCtl(VcPtr v)
{
    Str255      s;
    Str255      st = "\ptrue";
    Str255      sf = "\pfalse";
    Rect        r;
    long        w;
    decimal     d;
    decform     df;
    
    EraseRect(&v->wholeRect);
    TextFont(geneva);
    TextSize(9);
    TextFace(0);
    MoveTo(v->titlePos.h, v->titlePos.v);
    DrawString(v->title);
    if (v->mini == v->maxi) // separator
        return;
        
    if (v->isCheckBox) {
        if (v->currV == 1.0)
            BlockMove(st, s, st[0]+1);
        else
            BlockMove(sf, s, sf[0]+1);
    }
    else {
        df.style = FIXEDDECIMAL;
        df.digits = 2;
        num2dec(&df, v->currV, &d);
        dec2str(&df, &d, (char *)s);
        C2PStr((Ptr)s);
    }
    
    TextFace(bold);
    w = StringWidth(s);
    MoveTo(v->valuePos.h - w, v->valuePos.v);
    DrawString(s);
 
    r = v->incrHit;
    FrameRect(&r);
    if (v->isCheckBox) {
        if (v->currV == 1.0) {
            MoveTo(r.left, r.top);
            LineTo(r.right - 1, r.bottom - 1);
            MoveTo(r.left, r.bottom - 1);
            LineTo(r.right - 1, r.top);
        }
    }
    else {
        MoveTo( r.left + 2, r.bottom - 2);
        DrawChar('+');
 
        r = v->decrHit;
        FrameRect(&r);
        MoveTo(r.left + 2, r.bottom - 2);
        DrawChar('-');
    }
}
 
//-----------------------------------
static void IncrementVC(VcPtr v)
{
    if (v->isCheckBox) {
        if (v->currV == 0.0)
            v->currV = 1.0;
        else
            v->currV = 0.0; 
    }
    else {
        v->currV += v->delta;
        if (v->currV > v->maxi)
            v->currV = v->maxi;
    }
}
 
 
//-----------------------------------
static void DecrementVC(VcPtr v)
{
    v->currV -= v->delta;
    if (v->currV < v->mini)
        v->currV = v->mini;
}
 
 
//----------------------------------------------------------------------
//--------------------- ValueControlCluster routines -------------------
//----------------------------------------------------------------------
void    DrawVCluster(VccPtr vcc)
{
    long    i;
    Rect    r;
    
    FrameRect(&vcc->frame);
    MoveTo(vcc->frame.left + 12, vcc->frame.top + 3);
    TextFont(geneva);
    TextSize(9);
    TextFace(bold);
    TextMode(srcCopy);
    DrawChar(' '); DrawString(vcc->vTitle); DrawChar(' ');
    r = vcc->resetHit;
    EraseRect(&r);
    FrameRect(&r);
    MoveTo(r.left + 3, r.bottom - 2);
    TextMode(srcOr);
    DrawString("\pReset");
    for (i = 0; i < vcc->howMany; i++)
        DrawValueCtl(vcc->vCtls[i]);
}
 
//----------------------------------------------------------------------
void    DisposeVCluster(VccPtr  vcc)
{
    long i;
    
    for (i = 0; i < vcc->howMany; i++)
        DisposePtr((Ptr)(&vcc->vCtls[i]));
    DisposePtr((Ptr)vcc);
}
 
 
//----------------------------------------------------------------------
OSType  GetIdTag(VccPtr vcc)
{
    return vcc->idTag;
}
 
 
//----------------------------------------------------------------------
float   GetCurrentValue(VccPtr vcc, long index)
{
    if ((index >= 0) && (index < vcc->howMany))
        return  (vcc->vCtls[index])->currV;
    else {
        SysBeep(10);
        return 0;
    }
}
 
//----------------------------------------------------------------------
void    SetResetValue(VccPtr vcc, long index, float value)
{
    if ((index >= 0) && (index < vcc->howMany))
        (vcc->vCtls[index])->initV = value;
    else {
        SysBeep(10);
    }
}
 
 
//----------------------------------------------------------------------
//---------------------   SettingsDialog routines   -------------------
//----------------------------------------------------------------------
static Boolean DoSettingsDialog(VcPtr v)
{
    DialogPtr       dlg;
    GrafPtr         savePort;
    ModalFilterUPP  filterProc;
    OSErr           err;
    short           item, kind;
    Handle          h;
    Rect            r;
    
    GetPort(&savePort);
    dlg = GetNewDialog(kSettingsDlog, nil, (WindowPtr)(-1));
    if (!dlg)
        return false;
    SetPort(dlg);   
    err = GetStdFilterProc(&filterProc);
    err = SetDialogDefaultItem(dlg, ok);
    err = SetDialogCancelItem(dlg, cancel);
    err = SetDialogTracksCursor(dlg, true);
 
    GetDialogItem(dlg, iVCtitle, &kind, &h, &r);
    SetDialogItemText(h, v->title);
    PutFloat(dlg, iMinValue, v->mini);
    PutFloat(dlg, iMaxValue, v->maxi);
    PutFloat(dlg, iStepSize, v->delta);
    PutFloat(dlg, iCurrentV, v->currV);
    
    do {
        ModalDialog(filterProc, &item);
        switch (item) {
        case ok: 
        // range checks
            break;
        case iMinValue: 
        // range check
            break;
        case iMaxValue:
        // range check
            break;
        case iStepSize:
        // range check
            break;
        case iCurrentV:
        // range check
            break;
        }
        if (item == ok) {
            v->mini  = GetFloat(dlg, iMinValue);
            v->maxi  = GetFloat(dlg, iMaxValue);
            v->delta = GetFloat(dlg, iStepSize);
            v->currV = GetFloat(dlg, iCurrentV);
        }
    } while ((item != cancel) && (item != ok));
    
    DisposeDialog(dlg);
    SetPort(savePort);
    return (item == ok);
}
 
 
//----------------------------------------------------------------------
void    PutFloat(DialogPtr dlg, short item, float value)
{
    Str255      s;
    Handle      h;
    Rect        r;
    decimal     d;
    decform     df;
    short       kind;
    
    df.style = FIXEDDECIMAL;
    df.digits = 2;
    num2dec(&df, value, &d);
    dec2str(&df, &d, (char *)s);
    C2PStr((Ptr)s);
    GetDialogItem(dlg, item, &kind, &h, &r);
    SetDialogItemText(h, s);
}
 
 
//----------------------------------------------------------------------
float   GetFloat(DialogPtr dlg, short item)
{
    Str255      s;
    Handle      h;
    Rect        r;
    decimal     d;
    decform     df;
    float       value = 0.0;
    short       kind;
    short       ix, vp;
    
    GetDialogItem(dlg, item, &kind, &h, &r);
    if (h) {
        GetDialogItemText(h, s);
        P2CStr(s);
        ix = 0;
        str2dec((char *)s, &ix, &d, &vp);
        if (vp) 
            value = dec2f(&d);
    }
    return value;
}