libcm/cmGrPage.c
2012-11-20 22:51:13 -08:00

1472 rindas
36 KiB
C

#include "cmGlobal.h"
#include "cmFloatTypes.h"
#include "cmRpt.h"
#include "cmErr.h"
#include "cmCtx.h"
#include "cmErr.h"
#include "cmMem.h"
#include "cmMallocDebug.h"
#include "cmGr.h"
#include "cmGrDevCtx.h"
#include "cmGrPlot.h"
#include "cmGrPage.h"
#include "cmVectOpsTemplateMain.h"
enum
{
kHashLength = 6,
kHashCharCnt = 10,
kMaxHashCnt = 10
};
enum
{
kNotValidFl = 0x01,
kDirtyFl = 0x02, // set if the layout is dirty
kFocusFl = 0x04
};
typedef struct
{
cmGrPPt_t xy0; //
cmGrPPt_t xy1; // xy1 is on iext
} cmGrPgHash_t;
struct cmGrPgVw_str;
struct cmGrPgAxis_str;
struct cmGrPg_str;
typedef struct cmGrPgLabelFunc_str
{
unsigned id;
cmGrLabelFunc_t func;
void* arg;
cmChar_t* label;
struct cmGrPgLabelFunc_str* link;
} cmGrPgLabelFunc_t;
typedef struct cmGrPgAxis_str
{
struct cmGrPgVw_str* vp;
unsigned flags; // kHashMarkGrFl | kHashLabelGrFl
unsigned hashCnt;
unsigned hashLength;
cmChar_t* title;
cmGrPgHash_t* hash;
unsigned maxHashCnt;
cmGrPPt_t titlePt; // position of the axis title
unsigned titleFontId; // title font id
unsigned titleFontStyle; // title font style
unsigned titleFontSize; // title font size
cmGrPPt_t labelPt; // x = l/r label pos. and y=t/b label pos.
unsigned labelFontId; // label font id
unsigned labelFontStyle; // label font style
unsigned labelFontSize; // label font size
cmGrPgLabelFunc_t* func;
} cmGrPgAxis_t;
typedef struct cmGrPgVw_str
{
struct cmGrPg_str* p;
cmGrH_t grH;
unsigned ri; // row index
unsigned ci; // column index
cmGrPExt_t pext; // page around outside of view
cmGrPExt_t iext; // view physical extents
cmGrPgAxis_t axis[ kAxisGrCnt ]; //
cmGrPPt_t titlePt; // lower/center position of the view title
cmChar_t* title; // view title (upper center)
unsigned fontId; // title font id
unsigned fontStyle; // title font style
unsigned fontSize; // title font size
cmGrPgLabelFunc_t* xfunc;
cmGrPgLabelFunc_t* yfunc;
} cmGrPgVw_t;
typedef struct cmGrPg_str
{
cmCtx_t ctx;
cmErr_t err;
cmGrCbFunc_t cbFunc;
void* cbArg;
unsigned flags; // kNotValidFl
cmGrPExt_t pext; // outer border around all views
unsigned rn; // count of rows
unsigned cn; // count of columns
unsigned vn; // view count (rn*cn)
double* rpropV; // sum(rprop[rn]) == 1.0 pgaction of r.w() assigned to each row
double* cpropV; // sum(cprop[cn]) == 1.0 pgaction of r.h() assigned to each row
cmGrPgVw_t* viewV; // viewV[vn]
unsigned focusIdx; // focused view index
cmGrPPt_t titlePt; //
cmChar_t* title; // page title
unsigned fontId; // title font id
unsigned fontStyle; // title font style
unsigned fontSize; // title font size
unsigned labelFuncId; // next page label function id
cmGrPgLabelFunc_t* funcs; // linked list of value to string translation functions
} cmGrPg_t;
cmGrPgH_t cmGrPgNullHandle = cmSTATIC_NULL_HANDLE;
cmGrVwH_t cmGrVwNullHandle = cmSTATIC_NULL_HANDLE;
cmGrAxH_t cmGrAxNullHandle = cmSTATIC_NULL_HANDLE;
cmGrPg_t* _cmGrPgHandleToPtr( cmGrPgH_t h )
{
cmGrPg_t* p = (cmGrPg_t*)h.h;
assert( p!=NULL);
return p;
}
cmGrPgVw_t* _cmGrPgVwHandleToPtr( cmGrVwH_t h )
{
cmGrPgVw_t* p = (cmGrPgVw_t*)h.h;
assert( p!=NULL);
return p;
}
cmGrPgAxis_t* _cmGrPgAxisHandleToPtr( cmGrAxH_t h )
{
cmGrPgAxis_t* p = (cmGrPgAxis_t*)h.h;
assert( p!=NULL);
return p;
}
void _cmGrPgVwAxisDestroy( cmGrPgAxis_t* ap )
{
ap->func = NULL;
cmMemFree(ap->hash);
cmMemFree(ap->title);
}
void _cmGrPgVwDestroy( cmGrPgVw_t* vp )
{
unsigned i;
cmGrDestroy( &vp->grH );
cmMemFree(vp->title);
for(i=0; i<kAxisGrCnt; ++i)
_cmGrPgVwAxisDestroy(vp->axis + i );
}
void _cmGrPgFinal( cmGrPg_t* p )
{
unsigned i;
for(i=0; i<p->vn; ++i)
_cmGrPgVwDestroy( p->viewV + i );
cmGrPgLabelFunc_t* lfp = p->funcs;
while( lfp != NULL )
{
cmGrPgLabelFunc_t* np = lfp->link;
cmMemFree(lfp->label);
cmMemFree(lfp);
lfp = np;
}
p->funcs = NULL;
cmMemFree(p->viewV);
cmMemFree(p->title);
cmMemFree(p->rpropV);
cmMemFree(p->cpropV);
}
cmGrRC_t _cmGrPgDestroy( cmGrPg_t* p )
{
cmGrRC_t rc = kOkGrRC;
_cmGrPgFinal(p);
cmMemFree(p);
return rc;
}
unsigned _cmGrPgGrHandleToVwIndex( cmGrPg_t* p, cmGrH_t grH )
{
unsigned i;
for(i=0; i<p->vn; ++i)
if( cmHandlesAreEqual(p->viewV[i].grH,grH) )
return i;
return cmInvalidIdx;
}
void _cmGrPageCallback( void* arg, cmGrH_t grH, cmGrCbId_t id, unsigned eventFlags, cmGrKeyCodeId_t keycode )
{
cmGrPg_t* p = (cmGrPg_t*)arg;
switch(id)
{
case kCreateCbGrId:
case kDestroyCbGrId:
case kLocalPtCbGrId:
case kGlobalPtCbGrId:
case kPhysExtCbGrId:
case kViewExtCbGrId:
case kSelectExtCbGrId:
if( p->cbFunc != NULL )
p->cbFunc(p->cbArg,grH,id,eventFlags,keycode);
break;
case kKeyUpCbGrId:
case kKeyDnCbGrId:
{
cmGrVwH_t vwH;
cmGrPgH_t pgH;
pgH.h = p;
if(cmGrViewIsValid(vwH =cmGrPageFocusedView(pgH)))
if( p->cbFunc != NULL )
p->cbFunc(p->cbArg,cmGrViewGrHandle(vwH),id,eventFlags,keycode);
}
break;
case kFocusCbGrId:
{
unsigned i;
if((i = _cmGrPgGrHandleToVwIndex(p,grH)) != cmInvalidIdx )
{
cmGrPgH_t h;
h.h = p;
// if the focus is changing
if( i != p->focusIdx )
{
// inform the prev view that it is losing focus
if( p->focusIdx != cmInvalidIdx )
cmGrPageViewFocus(h,p->focusIdx,false);
// inform the new view that it is gaining focus
cmGrPageViewFocus(h,i,true);
if( p->cbFunc != NULL )
p->cbFunc(p->cbArg,grH,id,eventFlags,keycode);
}
}
}
break;
default:
{ assert(0); }
}
}
cmGrRC_t cmGrPageCreate( cmCtx_t* ctx, cmGrPgH_t* hp, cmGrCbFunc_t cbFunc, void* cbArg )
{
cmGrRC_t rc;
if((rc = cmGrPageDestroy(hp)) != kOkGrRC )
return rc;
cmGrPg_t* p = cmMemAllocZ(cmGrPg_t,1);
cmErrSetup(&p->err,&ctx->rpt,"cmGrPage");
p->cbFunc = cbFunc;
p->cbArg = cbArg;
p->ctx = *ctx;
hp->h = p;
if(rc != kOkGrRC )
_cmGrPgDestroy(p);
return rc;
}
cmGrRC_t cmGrPageDestroy( cmGrPgH_t* hp )
{
cmGrRC_t rc;
if(hp==NULL || cmGrPageIsValid(*hp) == false )
return kOkGrRC;
cmGrPg_t* p = _cmGrPgHandleToPtr(*hp);
if((rc = _cmGrPgDestroy(p)) != kOkGrRC )
return rc;
hp->h = NULL;
return rc;
}
bool cmGrPageIsValid( cmGrPgH_t h )
{ return h.h != NULL; }
cmGrRC_t cmGrPageClear( cmGrPgH_t h )
{
cmGrRC_t rc = kOkGrRC;
unsigned i;
for(i=0; i<cmGrPageViewCount(h); ++i)
{
cmGrVwH_t vwH = cmGrPageViewHandle(h,i);
assert( cmGrViewIsValid(vwH) );
if((rc = cmGrViewClear(vwH)) != kOkGrRC )
break;
}
return rc;
}
// Calcuate the position of each view using the p->pext, and the row/col proportion vectors
// This function calculates the outer page around each view.
// It must be called whenever the size of the window holding the page changes.
bool _cmGrPgLayoutVwPosition( cmGrPg_t* p )
{
enum { kVBord=4, kHBord=4 };
// verify that the page state is sane
if( p->rn==0 || p->cn==0 || p->pext.sz.h==0 || p->pext.sz.w==0)
goto errLabel;
else
{
unsigned i,j;
int x=p->pext.loc.x;
int y=p->pext.loc.y;
// calculate the total page width/height w/o internal borders
int h = p->pext.sz.h - ((p->rn - 1) * kVBord);
int w = p->pext.sz.w - ((p->cn - 1) * kHBord);
// verify that the page area exists
if( h<=0 || w<=0 )
goto errLabel;
// force the row/col proportion vectors to sum to 1.0
cmVOD_NormalizeProbability(p->rpropV,p->rn);
cmVOD_NormalizeProbability(p->cpropV,p->cn);
// determine the row height
for(i=0; i<p->rn; ++i)
{
unsigned hh = (unsigned)floor(p->rpropV[i] * h);
if( hh == 0 )
goto errLabel;
for(j=0; j<p->vn; ++j)
if( p->viewV[j].ri == i )
{
p->viewV[j].pext.loc.y = y;
p->viewV[j].pext.sz.h = hh;
}
y += hh + kVBord;
}
// determine the column width
for(i=0; i<p->cn; ++i)
{
unsigned ww = (unsigned)floor(p->cpropV[i] * w);
if( ww == 0 )
goto errLabel;
for(j=0; j<p->vn; ++j)
if( p->viewV[j].ci == i )
{
p->viewV[j].pext.loc.x = x;
p->viewV[j].pext.sz.w = ww;
}
x += ww + kHBord;
}
p->flags = cmClrFlag(p->flags,kNotValidFl);
return true;
}
errLabel:
p->flags = cmSetFlag(p->flags,kNotValidFl);
return false;
}
// Calculate the layout for a given view.
// txW = max hash label width on x-axis
// txH = hash label height on x-axis
void _cmGrPgLayoutView( cmGrPg_t* p, cmGrPgVw_t* vp, cmGrDcH_t dcH )
{
enum { kVBord=2, kHBord=2 };
int x0 = vp->pext.loc.x + kHBord;
int y0 = vp->pext.loc.y + kVBord;
int w = vp->pext.sz.w - 2*kHBord;
int h = vp->pext.sz.h - 2*kVBord;
int y = y0;
int x = x0;
cmGrPSz_t sz;
// Create a negative string with a repeating decimal to simulate an arbitrary hash label
// Use the measurements from this string to compute the geometry of the view layouts.
char label[ kHashCharCnt + 1];
snprintf(label,kHashCharCnt,"%f",-4.0/3.0);
label[kHashCharCnt] = 0;
int mlx,mrx,mty,mby;
int i;
// add vertical space for the view title
if( vp->title != NULL )
{
cmGrDcFontSetAndMeasure(dcH, vp->fontId, vp->fontSize, vp->fontStyle, vp->title, &sz );
y += sz.h;
vp->titlePt.y = y;
vp->titlePt.x = vp->pext.loc.x + vp->pext.sz.w/2;
y += 2;
}
cmGrPgAxis_t* ap = vp->axis + kTopGrIdx;
// add vertical space for the top axis title
if( ap->title != NULL )
{
cmGrDcFontSetAndMeasure(dcH, ap->titleFontId, ap->titleFontSize, ap->titleFontStyle, ap->title, &sz );
y += sz.h;
ap->titlePt.y = y;
y += 4;
}
// add vertical space for top axis hash labels
if( cmIsFlag(ap->flags,kHashLabelGrFl ) )
{
cmGrDcFontSetAndMeasure(dcH, ap->labelFontId, ap->labelFontSize, ap->labelFontStyle, label, &sz );
y += sz.h;
ap->labelPt.y = y;
y += 1;
}
// calc vertical space for top axis hash marks
if( cmIsFlag(ap->flags,kHashMarkGrFl ) )
{
mty = y;
y += ap->hashLength + 1;
}
// set the internal pext vertical location
vp->iext.loc.y = y;
ap = vp->axis + kBottomGrIdx;
y = y0 + h - 1 - kVBord;
// subtract vertical space for bottom axis title
if( ap->title != NULL )
{
cmGrDcFontSetAndMeasure(dcH, ap->titleFontId, ap->titleFontSize, ap->titleFontStyle, ap->title, &sz );
ap->titlePt.y = y;
y -= sz.h + 4;
}
// calc vertical space for bottom axis hash label
if( cmIsFlag(ap->flags,kHashLabelGrFl) )
{
cmGrDcFontSetAndMeasure(dcH, ap->labelFontId, ap->labelFontSize, ap->labelFontStyle, label, &sz );
ap->labelPt.y = y;
y -= sz.h + 2;
}
// calc vertical space for bottom axis hash mark
if( cmIsFlag(ap->flags,kHashMarkGrFl) )
{
mby = y;
y -= ap->hashLength + 1;
}
// set the internal pext height
vp->iext.sz.h = y - vp->iext.loc.y + 1;
ap = vp->axis + kLeftGrIdx;
// add horizontal space for the left axis title
if( ap->title != NULL )
{
cmGrDcFontSetAndMeasure(dcH, ap->titleFontId, ap->titleFontSize, ap->titleFontStyle, ap->title, &sz );
x += sz.h; // use txH as approx of char. width for rotated title
ap->titlePt.x = x;
x += 4;
}
// add horizontal space for left axis hash labels
if( cmIsFlag(ap->flags,kHashLabelGrFl ) )
{
cmGrDcFontSetAndMeasure(dcH, ap->labelFontId, ap->labelFontSize, ap->labelFontStyle, label, &sz );
ap->labelPt.x = x;
x += sz.w + 3;
}
// calc horizontal space for left axis hash marks
if( cmIsFlag(ap->flags,kHashMarkGrFl ) )
{
mlx = x;
x += ap->hashLength + 1;
}
// set the internal pext horz location
vp->iext.loc.x = x;
ap = vp->axis + kRightGrIdx;
x = x0 + w - 1 - kVBord;
// subtract horizontal space for right axis title
if( ap->title != NULL )
{
cmGrDcFontSetAndMeasure(dcH, ap->titleFontId, ap->titleFontSize, ap->titleFontStyle, ap->title, &sz );
ap->titlePt.x = x;
x -= sz.h + 2; // use txH as approx of char. width for rotated title
}
// calc horizontal space for right axis hash label
if( cmIsFlag(ap->flags,kHashLabelGrFl) )
{
cmGrDcFontSetAndMeasure(dcH, ap->labelFontId, ap->labelFontSize, ap->labelFontStyle, label, &sz );
x -= sz.w + 1;
ap->labelPt.x = x;
x -= 3;
}
// calc horizontal space for right axis hash mark
if( cmIsFlag(ap->flags,kHashMarkGrFl) )
{
mrx = x;
x -= ap->hashLength + 1;
}
// set the internal pext width
vp->iext.sz.w = x - vp->iext.loc.x + 1;
// calc the top hash count and alloc hash array
ap = vp->axis + kTopGrIdx;
cmGrDcFontSetAndMeasure(dcH, ap->labelFontId, ap->labelFontSize, ap->labelFontStyle, label, &sz );
ap->hashCnt = cmMin(ap->maxHashCnt,(vp->iext.sz.w + sz.w) / sz.w);
ap->hash = cmMemResizeZ( cmGrPgHash_t, ap->hash, vp->axis[ kTopGrIdx ].hashCnt );
// calc the bottom hash count and alloc hash array
ap = vp->axis + kBottomGrIdx;
cmGrDcFontSetAndMeasure(dcH, ap->labelFontId, ap->labelFontSize, ap->labelFontStyle, label, &sz );
ap->hashCnt = cmMin(ap->maxHashCnt,(vp->iext.sz.w + sz.w) / sz.w);
ap->hash = cmMemResizeZ( cmGrPgHash_t, ap->hash, vp->axis[ kBottomGrIdx ].hashCnt );
// calc the top hash mark line beg/end
for(i=0; i< vp->axis[ kTopGrIdx ].hashCnt; ++i)
{
int x0 = round(vp->iext.loc.x - 1 + ((i * (vp->iext.sz.w + 1.0))/(vp->axis[ kTopGrIdx ].hashCnt-1)));
vp->axis[kTopGrIdx].hash[i].xy0.y = mty;
vp->axis[kTopGrIdx].hash[i].xy0.x = x0;
vp->axis[kTopGrIdx].hash[i].xy1.y = vp->iext.loc.y - 1;
vp->axis[kTopGrIdx].hash[i].xy1.x = x0;
}
// calc the bottom hash mark line beg/end
for(i=0; i< vp->axis[ kBottomGrIdx ].hashCnt; ++i)
{
int x0 = round(vp->iext.loc.x - 1 + ((i * (vp->iext.sz.w + 1.0))/(vp->axis[ kBottomGrIdx ].hashCnt-1)));
vp->axis[kBottomGrIdx].hash[i].xy0.y = mby;
vp->axis[kBottomGrIdx].hash[i].xy0.x = x0;
vp->axis[kBottomGrIdx].hash[i].xy1.y = cmGrPExtB(&vp->iext) + 1;
vp->axis[kBottomGrIdx].hash[i].xy1.x = x0;
}
// calc the left hash count and alloc hash array
ap = vp->axis + kLeftGrIdx;
cmGrDcFontSetAndMeasure(dcH, ap->labelFontId, ap->labelFontSize, ap->labelFontStyle, label, &sz );
ap->hashCnt = cmMin(ap->maxHashCnt,(vp->iext.sz.h + sz.h) / sz.h);
ap->hash = cmMemResizeZ( cmGrPgHash_t, ap->hash, vp->axis[ kLeftGrIdx ].hashCnt );
// calc right hash count and alloc hash array
ap = vp->axis + kRightGrIdx;
cmGrDcFontSetAndMeasure(dcH, ap->labelFontId, ap->labelFontSize, ap->labelFontStyle, label, &sz );
ap->hashCnt = cmMin(ap->maxHashCnt,(vp->iext.sz.h + sz.h) / sz.h);
ap->hash = cmMemResizeZ( cmGrPgHash_t, ap->hash, vp->axis[ kRightGrIdx ].hashCnt );
// calc the left hash mark beg/end
for(i=0; i< vp->axis[ kLeftGrIdx ].hashCnt; ++i)
{
int y0 = round(vp->iext.loc.y - 1 + ((i * (vp->iext.sz.h + 1.0))/(vp->axis[ kLeftGrIdx ].hashCnt-1)));
vp->axis[kLeftGrIdx].hash[i].xy0.x = mlx;
vp->axis[kLeftGrIdx].hash[i].xy0.y = y0;
vp->axis[kLeftGrIdx].hash[i].xy1.x = vp->iext.loc.x - 1;
vp->axis[kLeftGrIdx].hash[i].xy1.y = y0;
}
// calc the right hash mark beg/end
for(i=0; i< vp->axis[ kRightGrIdx ].hashCnt; ++i)
{
int y0 = round(vp->iext.loc.y - 1 + ((i * (vp->iext.sz.h + 1.0))/(vp->axis[ kRightGrIdx ].hashCnt-1)));
vp->axis[kRightGrIdx].hash[i].xy0.x = mrx;
vp->axis[kRightGrIdx].hash[i].xy0.y = y0;
vp->axis[kRightGrIdx].hash[i].xy1.x = cmGrPExtR(&vp->iext) + 1;
vp->axis[kRightGrIdx].hash[i].xy1.y = y0;
}
// change the location of the view to match vp->iext
if( cmGrIsValid( vp->grH ) )
cmGrSetPhysExtentsE( vp->grH, &vp->iext );
}
bool _cmGrPageLayout( cmGrPg_t* p, cmGrDcH_t dcH )
{
unsigned i;
if( cmIsNotFlag(p->flags,kDirtyFl) )
return false;
cmGrDcSetFontFamily(dcH,kHelveticaFfGrId);
cmGrDcSetFontSize(dcH,10);
// Create a negative string with a repeating decimal to simulate an arbitrary hash label
// Use the measurements pgom this string to compute the geometry of the view layouts.
/*
char label[ kHashCharCnt + 1];
cmGrPSz_t sz;
snprintf(label,kHashCharCnt,"%f",-4.0/3.0);
label[kHashCharCnt] = 0;
cmGrDcMeasure(dcH,label,&sz);
*/
_cmGrPgLayoutVwPosition(p);
for(i=0; i<p->vn; ++i)
{
cmGrPgVw_t* vp = p->viewV + i;
_cmGrPgLayoutView(p, vp, dcH );
}
p->flags = cmClrFlag(p->flags,kDirtyFl);
return true;
}
cmGrRC_t cmGrPageInit( cmGrPgH_t h, const cmGrPExt_t* r, unsigned rn, unsigned cn, cmGrDcH_t dcH )
{
cmGrRC_t rc = kOkGrRC;
cmGrPg_t* p = _cmGrPgHandleToPtr(h);
unsigned i;
_cmGrPgFinal(p);
if( rn*cn > 0 )
{
p->pext = *r;
p->rn = rn;
p->cn = cn;
p->vn = rn*cn;
p->rpropV = cmMemAllocZ(double, rn);
p->cpropV = cmMemAllocZ(double, cn);
p->viewV = cmMemAllocZ(cmGrPgVw_t, p->vn);
p->flags = kDirtyFl;
p->focusIdx = cmInvalidIdx;
p->fontId = kHelveticaFfGrId;
p->fontStyle = kNormalFsGrFl;
p->fontSize = 16;
// setup the view defaults
for(i=0; i<p->vn; ++i)
{
cmGrPgVw_t* vp = p->viewV + i;
vp->p = p;
vp->ri = i % p->rn;
vp->ci = i / p->rn;
vp->fontId = kHelveticaFfGrId;
vp->fontStyle = kNormalFsGrFl;
vp->fontSize = 14;
unsigned j;
for(j=0; j<kAxisGrCnt; ++j)
{
cmGrPgAxis_t* ap = vp->axis + j;
ap->vp = p->viewV + i;
ap->maxHashCnt = kMaxHashCnt;
ap->hashLength = kHashLength;
ap->flags = kHashMarkGrFl | kHashLabelGrFl;
ap->titleFontId = kHelveticaFfGrId;
ap->titleFontStyle = kNormalFsGrFl;
ap->titleFontSize = 10;
ap->labelFontId = kHelveticaFfGrId;
ap->labelFontStyle = kNormalFsGrFl;
ap->labelFontSize = 10;
}
}
// setup the default row proportions
for(i=0; i<p->rn; ++i)
p->rpropV[i] = 1.0/p->rn;
// setup the default col proportions
for(i=0; i<p->cn; ++i)
p->cpropV[i] = 1.0/p->cn;
// _cmGrPgLayoutVw() needs to be called.
p->flags = cmSetFlag(p->flags,kDirtyFl);
// layout the page
// kpl _cmGrPageLayout(p, dcH );
// notify the application that views have been created
for(i=0; i<rn*cn; ++i)
{
cmGrPgVw_t* vp = p->viewV + i;
// Set the 'id' assoc'd with this views cmGrH_t handle to 'i'.
// This will allow the grH to return the index of the plot.
if((rc = cmGrCreate(&p->ctx,&vp->grH,i,kExpandViewGrFl,_cmGrPageCallback,p,NULL)) != kOkGrRC )
break;
}
}
return rc;
}
cmGrRC_t cmGrPageResize( cmGrPgH_t h, const cmGrPExt_t* r, cmGrDcH_t dcH )
{
cmGrPg_t* p = _cmGrPgHandleToPtr(h);
p->pext = *r;
// _cmGrPgLayoutVw() needs to be called.
p->flags = cmSetFlag(p->flags,kDirtyFl);
// layout the page
// kpl _cmGrPageLayout(p, dcH );
return kOkGrRC;
}
void cmGrPageRect( cmGrPgH_t h, cmGrPExt_t* r )
{
cmGrPg_t* p = _cmGrPgHandleToPtr(h);
*r = p->pext;
}
unsigned cmGrPageViewCount( cmGrPgH_t h )
{
cmGrPg_t* p = _cmGrPgHandleToPtr(h);
return p->vn;
}
void _cmGrViewSetTitle(cmGrPgVw_t* vp, const cmChar_t* title )
{
if( title == vp->title || (title != NULL && vp->title!=NULL && strcmp(title,vp->title)==0 ))
return;
cmMemPtrFree(&vp->title);
if( title != NULL )
{
assert( vp->title == NULL );
vp->title = cmMemAllocStr(title);
}
vp->p->flags = cmSetFlag(vp->p->flags, kDirtyFl);
}
void _cmGrAxisSetTitle( cmGrPgAxis_t* ap, const cmChar_t* title )
{
if( title == ap->title || (title != NULL && ap->title!=NULL && strcmp(title,ap->title)==0 ))
return;
cmMemPtrFree(&ap->title);
if( title != NULL )
{
assert( ap->title == NULL );
ap->title = cmMemAllocStr(title);
}
ap->vp->p->flags = cmSetFlag(ap->vp->p->flags, kDirtyFl);
}
void _cmGrPgDrawHashMarks( cmGrPg_t* p, cmGrPgVw_t* vp, cmGrDcH_t dcH, unsigned lineColor )
{
int i,j;
cmGrDcSetColor(dcH, lineColor );
cmGrDcSetPenWidth( dcH, 1 );
for(j=0; j<kAxisGrCnt; ++j )
{
cmGrPgAxis_t* ap = vp->axis + j;
if( cmIsFlag(ap->flags, kHashMarkGrFl) )
for(i=0; i<ap->hashCnt; ++i)
{
cmGrPgHash_t* hp = ap->hash + i;
cmGrDcDrawLine(dcH, hp->xy0.x, hp->xy0.y, hp->xy1.x, hp->xy1.y);
}
}
// draw border 1 pixel outside of iext
cmGrDcDrawRect(dcH,vp->iext.loc.x-1,vp->iext.loc.y-1,vp->iext.sz.w+2,vp->iext.sz.h+2);
//printf("pgm: x:%i y:%i\n", vp->iext.loc.x, vp->iext.loc.y);
// draw the view border
//cmGrDcDrawRect(dcH,vp->pext.loc.x,vp->pext.loc.y,vp->pext.sz.w,vp->pext.sz.h);
}
void _cmGrPgHashValueToLabel( cmGrPgAxis_t* ap, cmChar_t* label, unsigned labelCharCnt, cmGrV_t value )
{
if( ap->func == NULL )
snprintf(label,labelCharCnt,"%f",value);
else
{
ap->func->func( ap->func->arg, label, labelCharCnt, value );
}
}
void _cmGrPgDrawHashLabels( cmGrPg_t* p, cmGrPgVw_t* vp, cmGrDcH_t dcH, unsigned textColor )
{
int i;
cmGrVExt_t vext;
char s[ kHashCharCnt+1 ];
s[kHashCharCnt]=0;
cmGrViewExtents( vp->grH, &vext );
//cmGrVExtPrint("hash:",&vext);
cmGrDcSetColor(dcH, textColor );
cmGrPgAxis_t* ap = vp->axis + kLeftGrIdx;
cmGrDcSetFont( dcH, ap->labelFontId, ap->labelFontSize, ap->labelFontStyle );
// draw the left axis hash labels
if( cmIsFlag(ap->flags, kHashLabelGrFl ) )
{
for(i=0; i<vp->axis[ kLeftGrIdx ].hashCnt; ++i)
{
cmGrPSz_t sz;
double v = vext.loc.y + vext.sz.h - (i * vext.sz.h / (ap->hashCnt-1) );
_cmGrPgHashValueToLabel(ap,s,kHashCharCnt,v);
cmGrDcMeasure(dcH,s,&sz);
int y = ap->hash[i].xy0.y;
cmGrDcDrawText(dcH, s, ap->labelPt.x, y + (sz.h/2) );
}
}
ap = vp->axis + kRightGrIdx;
cmGrDcSetFont( dcH, ap->labelFontId, ap->labelFontSize, ap->labelFontStyle );
// draw the right axis hash labels
if( cmIsFlag(ap->flags, kHashLabelGrFl ))
{
for(i=0; i<ap->hashCnt; ++i)
{
cmGrPSz_t sz;
double v = vext.loc.y + vext.sz.h - (i * vext.sz.h / (ap->hashCnt-1) );
_cmGrPgHashValueToLabel(ap,s,kHashCharCnt,v);
cmGrDcMeasure(dcH,s,&sz);
int y = ap->hash[i].xy0.y;
cmGrDcDrawText(dcH, s, ap->labelPt.x, y + (sz.h/2));
}
}
// draw the top axis hash labels
ap = vp->axis + kTopGrIdx;
cmGrDcSetFont( dcH, ap->labelFontId, ap->labelFontSize, ap->labelFontStyle );
if( cmIsFlag(ap->flags, kHashLabelGrFl ) )
{
for(i=0; i<ap->hashCnt; ++i)
{
cmGrPSz_t sz;
double v = vext.loc.x + (i * vext.sz.w / (ap->hashCnt-1));
_cmGrPgHashValueToLabel(ap,s,kHashCharCnt,v);
cmGrDcMeasure(dcH,s,&sz);
cmGrDcDrawText(dcH, s, ap->hash[i].xy0.x - sz.w/2, ap->labelPt.y );
}
}
// draw the bottom axis hash labels
ap = vp->axis + kBottomGrIdx;
cmGrDcSetFont( dcH, ap->labelFontId, ap->labelFontSize, ap->labelFontStyle );
if( cmIsFlag(ap->flags, kHashLabelGrFl ) )
{
for(i=0; i<ap->hashCnt; ++i)
{
cmGrPSz_t sz;
double v = vext.loc.x + (i * vext.sz.w / (ap->hashCnt-1));
_cmGrPgHashValueToLabel(ap,s,kHashCharCnt,v);
cmGrDcMeasure(dcH,s,&sz);
cmGrDcDrawText(dcH, s, ap->hash[i].xy0.x - sz.w/2, ap->labelPt.y );
}
}
}
void _cmGrPgDrawAxisTitles( cmGrPg_t* p, cmGrPgVw_t* vp, cmGrDcH_t dcH )
{
unsigned i;
for(i=0; i<kAxisGrCnt; ++i)
{
cmGrPgAxis_t* ap = vp->axis + i;
if( ap->title != NULL )
{
cmGrPSz_t sz;
cmGrDcFontSetAndMeasure( dcH, ap->titleFontId, ap->titleFontSize, ap->titleFontStyle, ap->title, &sz );
if( i==kBottomGrIdx || i==kTopGrIdx )
{
int x = vp->iext.loc.x + (vp->iext.sz.w/2) - (sz.w/2);
cmGrDcDrawText( dcH, ap->title, x, ap->titlePt.y );
}
if( i==kLeftGrIdx || i==kRightGrIdx )
{
int y = vp->iext.loc.y + (vp->iext.sz.h/2) + (sz.w/2);
cmGrDcDrawTextRot( dcH, ap->title, ap->titlePt.x, y, 90 );
}
}
}
}
void cmGrPageViewFocus( cmGrPgH_t h, unsigned vwIdx, bool enableFl )
{
cmGrPg_t* p = _cmGrPgHandleToPtr(h);
assert( vwIdx < p->vn );
if( enableFl )
p->focusIdx = vwIdx;
else
{
if( p->focusIdx == vwIdx )
p->focusIdx = cmInvalidId;
}
}
cmGrVwH_t cmGrPageFocusedView( cmGrPgH_t h )
{
cmGrPg_t* p = _cmGrPgHandleToPtr(h);
cmGrVwH_t vwH = cmGrVwNullHandle;
if( p->focusIdx != cmInvalidIdx )
vwH.h = p->viewV + p->focusIdx;
return vwH;
}
bool cmGrPageLayout( cmGrPgH_t h, cmGrDcH_t dcH )
{
cmGrPg_t* p = _cmGrPgHandleToPtr(h);
cmGrDcPushCtx(dcH);
bool fl = _cmGrPageLayout(p,dcH);
cmGrDcPopCtx(dcH);
return fl;
}
void cmGrPageDraw( cmGrPgH_t h, cmGrDcH_t dcH )
{
unsigned i;
cmGrPg_t* p = _cmGrPgHandleToPtr(h);
cmGrDcPushCtx(dcH);
_cmGrPageLayout(p,dcH);
cmGrDcSetColor(dcH,kBlackGrId);
// for each view
for(i=0; i<p->vn; ++i)
{
cmGrPgVw_t* vp = p->viewV + i;
unsigned lineColor = p->focusIdx==i ? kRedGrId : kBlackGrId;
_cmGrPgDrawHashMarks( p, vp, dcH, lineColor );
_cmGrPgDrawHashLabels( p, vp, dcH, kBlackGrId);
_cmGrPgDrawAxisTitles( p, vp, dcH );
if( vp->title != NULL )
{
cmGrPSz_t sz;
cmGrDcFontSetAndMeasure(dcH,vp->fontId,vp->fontSize,vp->fontStyle,vp->title,&sz);
cmGrDcDrawText(dcH,vp->title, vp->titlePt.x - sz.w/2, vp->titlePt.y );
}
}
cmGrDcPopCtx(dcH);
}
cmGrPgLabelFunc_t* _cmGrPageLabelIndexToRecd( cmGrPg_t* p, unsigned idx )
{
cmGrPgLabelFunc_t* lfp = p->funcs;
unsigned i;
for(i=0; i<idx && lfp!=NULL; ++i)
lfp=lfp->link;
return lfp;
}
cmGrPgLabelFunc_t* _cmGrPageLabelIdToRecd( cmGrPg_t* p, unsigned id )
{
cmGrPgLabelFunc_t* lfp = p->funcs;
for(; lfp!=NULL; lfp=lfp->link)
if( lfp->id == id )
return lfp;
return lfp;
}
unsigned cmGrPageLabelFuncRegister( cmGrPgH_t h, cmGrLabelFunc_t func, void* arg, const cmChar_t* label )
{
cmGrPg_t* p = _cmGrPgHandleToPtr(h);
cmGrPgLabelFunc_t* lfp = cmMemAllocZ(cmGrPgLabelFunc_t,1);
lfp->id = p->labelFuncId++;
lfp->func = func;
lfp->arg = arg;
lfp->label = cmMemAllocStr(label);
lfp->link = p->funcs;
p->funcs = lfp;
return lfp->id;
}
unsigned cmGrPageLabelFuncCount( cmGrPgH_t h )
{
cmGrPg_t* p = _cmGrPgHandleToPtr(h);
cmGrPgLabelFunc_t* lfp = p->funcs;
unsigned n = 0;
for(; lfp != NULL; lfp=lfp->link )
++n;
return n;
}
unsigned cmGrPageLabelFuncIndexToId( cmGrPgH_t h, unsigned index )
{
cmGrPgLabelFunc_t* lfp;
cmGrPg_t* p = _cmGrPgHandleToPtr(h);
if((lfp = _cmGrPageLabelIndexToRecd(p,index)) == NULL )
return cmInvalidId;
return lfp->id;
}
unsigned cmGrPageLabelFuncLabelToId( cmGrPgH_t h, const cmChar_t* label )
{
cmGrPg_t* p = _cmGrPgHandleToPtr(h);
cmGrPgLabelFunc_t* lfp = p->funcs;
unsigned i;
for(i=0; lfp!=NULL; lfp=lfp->link,++i)
if( lfp->label!=NULL && strcmp(label,lfp->label)==0 )
return lfp->id;
return cmInvalidId;
}
cmGrLabelFunc_t cmGrPageLabelFunc( cmGrPgH_t h, unsigned id )
{
cmGrPg_t* p = _cmGrPgHandleToPtr(h);
cmGrPgLabelFunc_t* lfp = _cmGrPageLabelIdToRecd(p,id);
assert( lfp != NULL );
return lfp->func;
}
const cmChar_t* cmGrPageLabelFuncLabel( cmGrPgH_t h, unsigned id )
{
cmGrPg_t* p = _cmGrPgHandleToPtr(h);
cmGrPgLabelFunc_t* lfp = _cmGrPageLabelIdToRecd(p,id);
assert( lfp != NULL );
return lfp->label;
}
void* cmGrPageLabelFuncArg( cmGrPgH_t h, unsigned id )
{
cmGrPg_t* p = _cmGrPgHandleToPtr(h);
cmGrPgLabelFunc_t* lfp = _cmGrPageLabelIdToRecd(p,id);
assert( lfp != NULL );
return lfp->arg;
}
cmGrVwH_t cmGrPageViewHandle( cmGrPgH_t h, unsigned vwIdx )
{
cmGrPg_t* p = _cmGrPgHandleToPtr(h);
cmGrVwH_t vwH;
assert( vwIdx < p->vn );
vwH.h = p->viewV + vwIdx;
return vwH;
}
cmGrVwH_t cmGrPageGrHandleToView( cmGrPgH_t pgH, cmGrH_t grH )
{
cmGrPg_t* p = _cmGrPgHandleToPtr(pgH);
cmGrVwH_t vwH = cmGrVwNullHandle;
int i;
for(i=0; i<p->vn; ++i)
if( cmHandlesAreEqual(grH,p->viewV[i].grH) )
{
vwH.h = p->viewV + i;
break;
}
return vwH;
}
bool cmGrViewIsValid( cmGrVwH_t h )
{ return h.h != NULL; }
cmGrRC_t cmGrViewInit( cmGrVwH_t h, cmGrH_t grH, const cmChar_t* vwTitle, const cmChar_t* xLabel, const cmChar_t* yLabel )
{
cmGrPgVw_t* vp = _cmGrPgVwHandleToPtr(h);
vp->grH = grH;
_cmGrViewSetTitle( vp, vwTitle );
_cmGrAxisSetTitle( vp->axis + kBottomGrIdx, xLabel );
_cmGrAxisSetTitle( vp->axis + kLeftGrIdx, yLabel );
vp->p->flags = cmSetFlag(vp->p->flags, kDirtyFl);
return kOkGrRC;
}
cmGrRC_t cmGrViewClear( cmGrVwH_t h )
{
cmGrPgVw_t* vp = _cmGrPgVwHandleToPtr(h);
cmGrH_t grH = cmGrViewGrHandle(h);
assert( cmGrIsValid(grH) );
cmGrRC_t rc = cmGrClear( grH );
vp->p->flags = cmSetFlag(vp->p->flags, kDirtyFl);
return rc;
}
cmGrRC_t cmGrViewPExt( cmGrVwH_t h, cmGrPExt_t* pext )
{
cmGrPgVw_t* vp = _cmGrPgVwHandleToPtr(h);
*pext = vp->iext;
return kOkGrRC;
}
bool cmGrViewHasFocus( cmGrVwH_t h )
{
if( cmGrViewIsValid(h) == false )
return false;
cmGrPgVw_t* vp = _cmGrPgVwHandleToPtr(h);
if( vp->p->focusIdx == cmInvalidIdx )
return false;
return cmGrId(vp->grH) == vp->p->focusIdx;
}
cmGrH_t cmGrViewGrHandle( cmGrVwH_t h )
{
cmGrPgVw_t* vp = _cmGrPgVwHandleToPtr(h);
return vp->grH;
}
void cmGrViewSetCfg( cmGrVwH_t h, unsigned cfgFlags )
{
cmGrPgVw_t* vp = _cmGrPgVwHandleToPtr(h);
cmGrSetCfgFlags( vp->grH, cfgFlags );
vp->p->flags = cmSetFlag(vp->p->flags, kDirtyFl);
}
unsigned cmGrViewCfg( cmGrVwH_t h )
{
cmGrPgVw_t* vp = _cmGrPgVwHandleToPtr(h);
return cmGrCfgFlags( vp->grH );
}
void cmGrViewSetTitle( cmGrVwH_t h, const cmChar_t* title )
{
cmGrPgVw_t* vp = _cmGrPgVwHandleToPtr(h);
_cmGrViewSetTitle( vp, title );
}
const cmChar_t* cmGrViewTitle( cmGrVwH_t h )
{
cmGrPgVw_t* vp = _cmGrPgVwHandleToPtr(h);
return vp->title;
}
void cmGrViewSetFontFamily( cmGrVwH_t h, unsigned id )
{
cmGrPgVw_t* vp = _cmGrPgVwHandleToPtr(h);
if( vp->fontId != id )
{
vp->fontId = id;
vp->p->flags = cmSetFlag(vp->p->flags, kDirtyFl);
}
}
unsigned cmGrViewFontFamily( cmGrVwH_t h )
{
cmGrPgVw_t* vp = _cmGrPgVwHandleToPtr(h);
return vp->fontId;
}
void cmGrViewSetFontStyle( cmGrVwH_t h, unsigned flags )
{
cmGrPgVw_t* vp = _cmGrPgVwHandleToPtr(h);
if( vp->fontStyle != flags )
{
vp->fontStyle = flags;
vp->p->flags = cmSetFlag(vp->p->flags, kDirtyFl);
}
}
unsigned cmGrViewFontStyle( cmGrVwH_t h )
{
cmGrPgVw_t* vp = _cmGrPgVwHandleToPtr(h);
return vp->fontStyle;
}
void cmGrViewSetFontSize( cmGrVwH_t h, unsigned size )
{
cmGrPgVw_t* vp = _cmGrPgVwHandleToPtr(h);
if( vp->fontSize != size )
{
vp->fontSize = size;
vp->p->flags = cmSetFlag(vp->p->flags, kDirtyFl);
}
}
unsigned cmGrViewFontSize( cmGrVwH_t h )
{
cmGrPgVw_t* vp = _cmGrPgVwHandleToPtr(h);
return vp->fontSize;
}
void cmGrViewSetLabelFunc( cmGrVwH_t h, cmGrAxisIdx_t axisId, unsigned pgLabelFuncId )
{
cmGrPgVw_t* vp = _cmGrPgVwHandleToPtr(h);
cmGrPgLabelFunc_t** lfpp = NULL;
switch( axisId )
{
case kLeftGrIdx:
case kRightGrIdx:
lfpp = &vp->yfunc;
break;
case kTopGrIdx:
case kBottomGrIdx:
lfpp = &vp->xfunc;
break;
default:
{ assert(0); }
}
assert( lfpp != NULL );
*lfpp = _cmGrPageLabelIdToRecd(vp->p, pgLabelFuncId );
}
const cmChar_t* cmGrViewValue( cmGrVwH_t h, cmGrViewValueId_t id, cmChar_t* buf, unsigned bufCharCnt )
{
cmGrPgVw_t* vp = _cmGrPgVwHandleToPtr(h);
cmGrPgLabelFunc_t* lfp = NULL;
cmGrH_t grH = vp->grH;
cmGrV_t v;
cmGrVPt_t pt0,pt1;
switch( id )
{
case kLocalX_VwId:
v = cmGrLocalPt(grH)->x;
lfp = vp->xfunc;
break;
case kLocalY_VwId:
v = cmGrLocalPt(grH)->y;
lfp = vp->yfunc;
break;
case kGlobalX_VwId:
v = cmGrGlobalPt(grH)->x;
lfp = vp->xfunc;
break;
case kGlobalY_VwId:
v = cmGrGlobalPt(grH)->y;
lfp = vp->yfunc;
break;
case kSelX0_VwId:
cmGrSelectPoints(grH,&pt0,NULL);
v = pt0.x;
lfp = vp->xfunc;
break;
case kSelY0_VwId:
cmGrSelectPoints(grH,&pt0,NULL);
v = pt0.y;
lfp = vp->yfunc;
break;
case kSelX1_VwId:
cmGrSelectPoints(grH,NULL,&pt1);
v = pt1.x;
lfp = vp->xfunc;
break;
case kSelY1_VwId:
cmGrSelectPoints(grH,NULL,&pt1);
v = pt1.y;
lfp = vp->yfunc;
break;
case kSelW_VwId:
cmGrSelectPoints(grH,&pt0,&pt1);
v = fabs(pt1.x - pt0.x);
lfp = vp->xfunc;
break;
case kSelH_VwId:
cmGrSelectPoints(grH,&pt0,&pt1);
v = fabs(pt1.y - pt0.y);
lfp = vp->yfunc;
break;
default:
{ assert(0); }
}
if( bufCharCnt > 0 )
{
buf[0]=0;
if( lfp != NULL )
lfp->func( lfp->arg, buf, bufCharCnt, v );
else
snprintf(buf,bufCharCnt,"%f",v);
}
return buf;
}
cmGrAxH_t cmGrViewAxisHandle( cmGrVwH_t h, cmGrAxisIdx_t axisIdx )
{
cmGrPgVw_t* vp = _cmGrPgVwHandleToPtr(h);
cmGrAxH_t axH;
assert( axisIdx < kAxisGrCnt );
axH.h = vp->axis + axisIdx;
return axH;
}
bool cmGrAxisIsValid( cmGrAxH_t h )
{ return h.h != NULL; }
void cmGrAxisSetCfg( cmGrAxH_t h, unsigned flags )
{
cmGrPgAxis_t* ap = _cmGrPgAxisHandleToPtr(h);
if( ap->flags != flags )
{
ap->flags = flags;
ap->vp->p->flags = cmSetFlag(ap->vp->p->flags, kDirtyFl);
}
}
unsigned cmGrAxisCfg( cmGrAxH_t h )
{
cmGrPgAxis_t* ap = _cmGrPgAxisHandleToPtr(h);
return ap->flags;
}
void cmGrAxisSetTitle( cmGrAxH_t h, const cmChar_t* title )
{
cmGrPgAxis_t* ap = _cmGrPgAxisHandleToPtr(h);
_cmGrAxisSetTitle(ap,title);
}
const cmChar_t* cmGrAxisTitle( cmGrAxH_t h )
{
cmGrPgAxis_t* ap = _cmGrPgAxisHandleToPtr(h);
return ap->title;
}
void cmGrAxisSetTitleFontFamily( cmGrAxH_t h, unsigned id )
{
cmGrPgAxis_t* ap = _cmGrPgAxisHandleToPtr(h);
if( ap->titleFontId != id )
{
ap->titleFontId = id;
ap->vp->p->flags = cmSetFlag(ap->vp->p->flags, kDirtyFl);
}
}
unsigned cmGrAxisTitleFontFamily( cmGrAxH_t h )
{
cmGrPgAxis_t* ap = _cmGrPgAxisHandleToPtr(h);
return ap->titleFontId;
}
void cmGrAxisTitleSetFontStyle( cmGrAxH_t h, unsigned flags )
{
cmGrPgAxis_t* ap = _cmGrPgAxisHandleToPtr(h);
if( ap->titleFontStyle != flags )
{
ap->titleFontStyle = flags;
ap->vp->p->flags = cmSetFlag(ap->vp->p->flags, kDirtyFl);
}
}
unsigned cmGrAxisTitleFontStyle( cmGrAxH_t h )
{
cmGrPgAxis_t* ap = _cmGrPgAxisHandleToPtr(h);
return ap->titleFontStyle;
}
void cmGrAxisTitleSetFontSize( cmGrAxH_t h, unsigned size )
{
cmGrPgAxis_t* ap = _cmGrPgAxisHandleToPtr(h);
if( ap->titleFontSize != size )
{
ap->titleFontSize = size;
ap->vp->p->flags = cmSetFlag(ap->vp->p->flags, kDirtyFl);
}
}
unsigned cmGrAxisTitleFontSize( cmGrAxH_t h )
{
cmGrPgAxis_t* ap = _cmGrPgAxisHandleToPtr(h);
return ap->titleFontSize;
}
void cmGrAxisSetLabelFunc( cmGrAxH_t h, unsigned id )
{
cmGrPgAxis_t* ap = _cmGrPgAxisHandleToPtr(h);
ap->func = _cmGrPageLabelIdToRecd( ap->vp->p, id );
}