AeIntFieldUI.c++ 7.15 KB
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <limits.h>
#include <X11/Xlib.h>
#include <Vk/VkApp.h>

#include "AeTypes.h"
#include "AeIntFieldUI.h"
#include "AeAsset.h"
#include "AeAssetUI.h"
#include "AeBinEditor.h"
#include "AeAssetBase.h"
#include "AeUtils.h"


AeIntFieldUI::AeIntFieldUI (const char * name,
			    Boolean isEditable,
			    Boolean isSelectable,
			    Boolean isHierarchical,
			    TJustify justify,
			    TIntGetProc getProc,
			    TIntSetProc setProc) :
    AeFieldUI (name, isEditable, isSelectable, isHierarchical, False, justify, kIntMaxLen)
{
    fGetProc = getProc;
    fSetProc = setProc;

    fMin = INT_MIN;
    fMax = INT_MAX;
    fDefault = 0;
}

AeIntFieldUI::AeIntFieldUI (const char * name,
			    Boolean isEditable,
			    Boolean isSelectable,
			    Boolean isHierarchical,
			    TJustify justify,
			    TIntGetProc getProc,
			    TIntSetProc setProc,
			    int min, int max, int def) : 
    AeFieldUI (name, isEditable, isSelectable, isHierarchical, False, justify, kIntMaxLen)
{
    fGetProc = getProc;
    fSetProc = setProc;

    fMin = min;
    fMax = max;
    fDefault = def;
}

AeIntFieldUI::~AeIntFieldUI (void)
{
}


void
AeIntFieldUI::GetValueString (AeAsset * asset, String string)
{
    sprintf (string, "%d", fGetProc (asset));
}


Boolean
AeIntFieldUI::SetValueString (AeAsset * asset, String string)
{
    assert (fSetProc);

    int value = atoi (string);

    int max = GetMax (asset);
    int min = GetMin (asset);

    if (value > max)
	value = max;
    else if (value < min)
	value = min;

    if (value == fGetProc (asset))
	return False;

    fSetProc (asset, value);
    return True;
}


Boolean
AeIntFieldUI::VerifyValueString (String string)
{
    return True;
}


Boolean
AeIntFieldUI::VerifyInput (XmTextVerifyCallbackStruct * verify, const char * prevStr)
{
    Boolean	okay = True;
    String	string = verify->text->ptr;

    if (string)
    {
	int pos = (int)verify->startPos;

	// if the first char is a '+' or '-', then continue checking for digits.
	if (issign (*string, pos))
	    string++;

	// for integers allow only digits for the rest of the entire string.
	while (*string)
	{
	    if (!isdigit (*string++))
	    {
		okay = False;
		break;
	    }
	}
    }

    return okay;
}



void
AeIntFieldUI::HandleDoubleClick (AeBinEditor * editor,
				 AeAssetUI * assetUI,
				 XEvent * event,
				 TEditModifier modifier)
{
    assert (assetUI);
    AeAsset * asset = assetUI->GetAsset ();

    if (fGetProc (asset) != fDefault)
    {
	fSetProc (asset, fDefault);
	(editor->GetAssetBase ())->NotifyUpdate (asset);
    }
}



Boolean
AeIntFieldUI::HandleButtonPress (AeBinEditor * editor,
				 AeAssetUI * assetUI,
				 XEvent * event,
				 TEditModifier modifier)
{
    #define kMaxSamplePeriod	250	// in msec

    if (!IsEditable (assetUI->GetAsset ()))
	return False;

    if (modifier == kModDown)
    {
	Boolean first = True;
	int root_x, curY, startY;
	Window child, root;
	int win_x, win_y;
	unsigned int mask;
	long time, lastTime = 0;
	long samplePeriod = kMaxSamplePeriod / 5;
	int deltaValue, periodDivisor;
	Boolean isDefValue;
	int linearDelta, min, max;

	getBallisticsConsts (assetUI, &linearDelta, &min, &max);

	while (1)
	{
	    time = GetClockMSecs ();
	    
	    if (time - lastTime > samplePeriod)
	    {
		XQueryPointer (theApplication->display(), ((XButtonEvent *)event)->root,
			       &root, &child, &root_x, &curY, &win_x, &win_y, &mask);

		// if the button was released then exit
		if (!(mask & Button1MotionMask))
		    break;
		
		lastTime = time;

		if (first)
		{
		    startY = curY;
		    first = False;
		}
		else
		{
		    getBallistics (startY - curY, linearDelta,
				   &deltaValue, &periodDivisor);
		    
		    samplePeriod = kMaxSamplePeriod / periodDivisor;

		    if (deltaValue)
		    {
			isDefValue = incrementValue (editor, assetUI,
						     deltaValue, min, max);

			// if we reached the default value, stall the updating
			// so user has a chance to stop editing at this value
			if (isDefValue)
			    samplePeriod = 500;

			XQueryPointer (theApplication->display(), ((XButtonEvent *)event)->root,
				       &root, &child, &root_x, &curY, &win_x, &win_y, &mask);

			// if the button was released then exit
			if (!(mask & Button1MotionMask))
			    break;
		    }
		}
	    }
	}
    }

    return True;
}


//
// These values only need to be calculated once before
// the while loop.  Note that the delta value corresponds
// to the increment delta value used during the linear or
// fast range of the mouse tracking.
//
void
AeIntFieldUI::getBallisticsConsts (AeAssetUI * assetUI,
				   int * pDelta, int * pMin, int * pMax)
{
    #define kValueResolution	8000
    #define kMaxDelta		1000

    int delta;
    int min = GetMin (assetUI->GetAsset());
    int max = GetMax (assetUI->GetAsset());
    
    if (max == INT_MAX || min == INT_MIN)
	delta = kMaxDelta;
    else
	delta = ((long)(max - min))/kValueResolution;	    

    if (delta < 1)
	delta = 1;

    *pDelta = delta;
    *pMin = min;
    *pMax = max;
}


//
// This routine converts the mouse "speed" in pixels
// into ballistic values used to determine how fast
// and by how much the value should be changing.
//
// How fast the value is changed is determined by the 
// periodDivisor.  The bigger the divisor the more
// frequently the value is updated.
//
// How much the value is changed is determined by the
// deltaValue.
//
// Basically at "low" speeds, we want to change the
// integer value by 1 at varying rates to give 
// predictable control over editing the value.  At "high"
// speeds, we want to change the value by >1 amounts
// so here we use the linearDelta amount.
//
void
AeIntFieldUI::getBallistics (int speed, int linearDelta,
			     int * pDeltaValue,
			     int * pPeriodDivisor)
{
    #define kZeroSpeedPix	3	// in pixels
    #define kLinearSpeedPix	40
    #define kPeriodResolution	1

    int		divisor = 5;
    int		delta = 0;
    Boolean	isNeg = False;

    if (speed < 0)
    {
	isNeg = True;
	speed = -speed;
    }

    if (speed > kZeroSpeedPix)
    {
	if ((divisor = (speed - kZeroSpeedPix)/kPeriodResolution) < 1)
	    divisor = 1;
	
	delta = 1;

	if (speed > kLinearSpeedPix)
	    delta = linearDelta * (speed - kLinearSpeedPix);

	if (isNeg)
	    delta = -delta;
    }

    *pPeriodDivisor = divisor;
    *pDeltaValue = delta;
}


Boolean
AeIntFieldUI::incrementValue (AeBinEditor * editor, AeAssetUI * assetUI,
			      int deltaValue, int min, int max)
{
    long value;
    Boolean isDefValue	= False;
    int absDeltaValue	= deltaValue;
    long oldValue	= fGetProc (assetUI->GetAsset());

    if (absDeltaValue < 0)
	absDeltaValue = -absDeltaValue;

    value = oldValue + deltaValue;

    // limit the value to its maximum
    if (value > max)
	value = max;

    // limit the value to its minimum
    else if (value < min)
	value = min;

    // snap to the default value when we're near it
    else if (value < fDefault + absDeltaValue
	     && value > fDefault - absDeltaValue
	     && oldValue != fDefault)
    {
	value = fDefault;
	isDefValue = True;
    }

    if (value != oldValue)
    {
	fSetProc (assetUI->GetAsset (), value);
	(editor->GetAssetBase())->NotifyUpdate (assetUI->GetAsset());
    }
    
    return isDefValue;
}