#include "apricot.h"
#include "guts.h"
#include "Widget.h"
#include "private/Widget.h"
#ifdef __cplusplus
extern
"C"
{
#endif
#ifdef MAX
# undef MAX
#endif
#define MAX(x,y) ((x) > (y) ? (x) : (y))
#ifdef MIN
# undef MIN
#endif
#define MIN(x,y) ((x) > (y) ? (y) : (x))
#define COLUMN (1) /* working on column offsets */
#define ROW (2) /* working on row offsets */
#define CHECK_ONLY (1) /* check max slot constraint */
#define CHECK_SPACE (2) /* alloc more space, don't change max */
#define TYPICAL_SIZE 25 /* (arbitrary guess) */
#define PREALLOC 10 /* extra slots to allocate */
#define UNIFORM_PREALLOC 10
#define MAX_ELEMENT 10000
#define REL_SKIP 'x' /* Skip this column. */
#define REL_HORIZ '-' /* Extend previous widget horizontally. */
#define REL_VERT '^' /* Extend widget from row above. */
typedef
struct
SlotInfo {
int
minSize;
int
weight;
int
pad;
int
uniform;
int
offset;
int
temp;
} SlotInfo;
typedef
struct
GridLayout {
struct
Gridder *binNextPtr;
int
minSize;
int
pad;
int
weight;
unsigned
int
uniform;
int
minOffset;
int
maxOffset;
} GridLayout;
typedef
struct
{
SlotInfo *columnPtr;
SlotInfo *rowPtr;
int
columnEnd;
int
columnMax;
int
columnSpace;
int
rowEnd;
int
rowMax;
int
rowSpace;
int
startX;
int
startY;
} GridMaster;
typedef
struct
Gridder {
Handle window;
struct
Gridder *masterPtr;
struct
Gridder *nextPtr;
struct
Gridder *slavePtr;
GridMaster *masterDataPtr;
int
column, row;
int
numCols, numRows;
int
padX, padY;
int
padLeft, padBottom;
int
iPadX, iPadY;
int
sticky;
int
*abortPtr;
int
flags;
Handle saved_in;
int
lock;
struct
Gridder *binNextPtr;
int
size;
} Gridder;
#define STICK_NORTH 1
#define STICK_EAST 2
#define STICK_SOUTH 4
#define STICK_WEST 8
typedef
struct
UniformGroup {
unsigned
int
group;
int
minSize;
} UniformGroup;
#define DONT_PROPAGATE 2
static
void
AdjustForSticky(Gridder *slavePtr,
int
*xPtr,
int
*yPtr,
int
*widthPtr,
int
*heightPtr);
static
int
AdjustOffsets(
int
width,
int
elements,
register
SlotInfo *slotPtr);
static
void
ArrangeGrid(Gridder *masterPtr);
static
Bool CheckSlotData(Gridder *masterPtr,
int
slot,
int
slotType,
int
checkOnly);
static
void
ConfigureSlaves(Handle self, SV * window, HV * profile);
static
void
DestroyGrid(Handle self);
static
Gridder* GetGrid(Handle self);
static
Bool GridBboxCommand(Handle self, PList in, PList out);
static
Bool GridInfoCommand(Handle self, PList in, PList out);
static
Bool GridForgetRemoveCommand(Handle self, Bool forget, PList in, PList out);
static
Bool GridLocationCommand(Handle self, PList in, PList out);
static
Bool GridPropagateCommand(Handle self, PList in, PList out);
static
Bool GridRowColumnConfigureCommand(Handle self,
int
slotType, PList in, PList out);
static
Bool GridSizeCommand(Handle self, PList in, PList out);
static
void
InitMasterData(Gridder *masterPtr);
static
int
ResolveConstraints(Gridder *gridPtr,
int
rowOrColumn,
int
maxOffset);
static
void
SetGridSize(Gridder *gridPtr);
static
void
StickyToString(
int
flags,
char
*result);
static
int
StringToSticky(
char
*string);
static
void
Unlink(Gridder *gridPtr);
#define ARGsv(x) ((SV*)(in->items[x]))
#define ARGi(x) SvIV(ARGsv(x))
#define ARGs(x) SvPV_nolen(ARGsv(x))
#define RETsv(x) list_add(out, (Handle)(x))
#define RETi(x) list_add(out, (Handle) newSViv(x))
#define RETs(x) list_add(out, (Handle) newSVpv(x,0))
static
Bool
GridBboxCommand(Handle self, PList in, PList out)
{
Gridder *masterPtr;
GridMaster *gridPtr;
int
row, column;
int
row2, column2;
int
endX, endY;
Box r = {0,0,0,0};
switch
(in->count) {
case
0:
row = column = row2 = column2 = 0;
break
;
case
2:
column = column2 = ARGi(0);
row = row2 = ARGi(1);
break
;
case
4:
column = ARGi(0);
row = ARGi(1);
column2 = ARGi(2);
row2 = ARGi(3);
break
;
default
:
return
false
;
}
masterPtr = GetGrid(self);
if
( !( gridPtr = masterPtr->masterDataPtr))
return
false
;
SetGridSize(masterPtr);
endX = MAX(gridPtr->columnEnd, gridPtr->columnMax);
endY = MAX(gridPtr->rowEnd, gridPtr->rowMax);
if
((endX == 0) || (endY == 0))
goto
EXIT;
if
( in-> count == 0 ) {
row = column = 0;
row2 = endY;
column2 = endX;
}
if
(column > column2) {
int
temp = column;
column = column2, column2 = temp;
}
if
(row > row2) {
int
temp = row;
row = row2, row2 = temp;
}
if
(column > 0 && column < endX) {
r.x = gridPtr->columnPtr[column-1].offset;
}
else
if
(column > 0) {
r.x = gridPtr->columnPtr[endX-1].offset;
}
if
(row > 0 && row < endY) {
r.y = gridPtr->rowPtr[row-1].offset;
}
else
if
(row > 0) {
r.y = gridPtr->rowPtr[endY-1].offset;
}
if
(column2 < 0) {
r.width = 0;
}
else
if
(column2 >= endX) {
r.width = gridPtr->columnPtr[endX-1].offset - r.x;
}
else
{
r.width = gridPtr->columnPtr[column2].offset - r.x;
}
if
(row2 < 0) {
r.height = 0;
}
else
if
(row2 >= endY) {
r.height = gridPtr->rowPtr[endY-1].offset - r.y;
}
else
{
r.height = gridPtr->rowPtr[row2].offset - r.y;
}
r.x += gridPtr->startX;
r.y += gridPtr->startY;
EXIT:
RETi(r.x);
RETi(r.y);
RETi(r.width);
RETi(r.height);
return
true
;
}
static
Handle
get_slave( Handle self, SV * window)
{
if
( !window )
return
self;
if
( !SvOK(window))
return
NULL_HANDLE;
return
SvROK(window) ?
gimme_the_mate(window) :
my->bring(self, SvPV_nolen(window), is_opt(optDeepLookup) ? 1000 : 0);
}
static
Bool
GridForgetRemoveCommand(Handle self, Bool forget, PList in, PList out)
{
int
i;
for
(i = 0; i < in->count; i++) {
Handle slave = get_slave(self, ARGsv(i));
Gridder *slavePtr = GetGrid(slave), *masterPtr = slavePtr->masterPtr;
if
(!masterPtr)
continue
;
if
(forget) {
slavePtr->column = slavePtr->row = -1;
slavePtr->numCols = 1;
slavePtr->numRows = 1;
slavePtr->padX = slavePtr->padY = 0;
slavePtr->padLeft = slavePtr->padBottom = 0;
slavePtr->iPadX = slavePtr->iPadY = 0;
slavePtr->flags = 0;
slavePtr->sticky = 0;
}
Unlink(slavePtr);
CWidget(slavePtr->window)->hide(slavePtr->window);
CWidget(slave)->set_geometry(self, gtGrowMode);
SetGridSize(masterPtr);
ArrangeGrid(masterPtr);
}
return
true
;
}
static
SV*
two_ints(
int
a,
int
b)
{
AV * av = newAV();
av_push(av, newSViv(a));
av_push(av, newSViv(b-a));
return
newRV_noinc((SV*) av);
}
static
Bool
GridInfoCommand(Handle self, PList in, PList out)
{
HV * profile = newHV();
register
Gridder *p;
char
buffer[4];
if
( in->count != 0 )
return
false
;
p = GetGrid(self);
pset_H( in , p-> saved_in ? p-> saved_in : var->owner );
pset_i( column , p-> column );
pset_i( row , p-> row );
pset_i( colspan , p-> numCols );
pset_i( rowspan , p-> numRows );
pset_i( ipadx , p-> iPadX );
pset_i( ipady , p-> iPadY );
if
( p->padX * 2 != p->padLeft )
pset_sv( padx, two_ints(p->padLeft,p->padX) );
else
pset_i( padx, p-> padX );
if
( p->padY * 2 != p->padBottom )
pset_sv( pady, two_ints(p->padBottom,p->padY) );
else
pset_i( pady, p-> padY );
StickyToString(p->sticky,buffer);
pset_c( sticky , buffer);
RETsv(newRV_noinc(( SV *) profile));
return
true
;
}
static
Bool
GridLocationCommand(Handle self, PList in, PList out)
{
Gridder *masterPtr;
GridMaster *gridPtr;
register
SlotInfo *slotPtr;
int
x, y;
int
endX, endY;
Point r = {-1,-1};
if
(in->count != 2)
return
false
;
masterPtr = GetGrid(self);
if
(masterPtr->masterDataPtr == NULL)
return
false
;
x = ARGi(0);
y = ARGi(1);
gridPtr = masterPtr->masterDataPtr;
SetGridSize(masterPtr);
endX = MAX(gridPtr->columnEnd, gridPtr->columnMax);
endY = MAX(gridPtr->rowEnd, gridPtr->rowMax);
slotPtr = masterPtr->masterDataPtr->columnPtr;
if
(x >= masterPtr->masterDataPtr->startX) {
x -= masterPtr->masterDataPtr->startX;
for
(r.x = 0; slotPtr[r.x].offset < x && r.x < endX; r.x++) {
}
}
slotPtr = masterPtr->masterDataPtr->rowPtr;
if
(y >= masterPtr->masterDataPtr->startY) {
y -= masterPtr->masterDataPtr->startY;
for
(r.y = 0; slotPtr[r.y].offset < y && r.y < endY; r.y++) {
}
}
RETi(r.x);
RETi(r.y);
return
true
;
}
static
Bool
GridPropagateCommand(Handle self, PList in, PList out)
{
Gridder *masterPtr;
int
propagate, old;
if
( in->count > 1 )
return
false
;
masterPtr = GetGrid(self);
if
( in->count == 0 ) {
RETi( !(masterPtr->flags & DONT_PROPAGATE)) ;
return
true
;
}
propagate = SvBOOL(ARGsv(1));
old = !(masterPtr->flags & DONT_PROPAGATE);
if
(propagate != old) {
if
(propagate) {
masterPtr->flags &= ~DONT_PROPAGATE;
}
else
{
masterPtr->flags |= DONT_PROPAGATE;
}
if
(masterPtr->abortPtr != NULL) {
*masterPtr->abortPtr = 1;
}
ArrangeGrid (masterPtr);
}
return
true
;
}
static
Bool
GridRowColumnConfigureCommand(Handle self,
int
slotType, PList in, PList out)
{
Gridder *masterPtr;
SlotInfo *slotPtr = NULL;
int
slot;
int
checkOnly;
int
j, *indexes, n_indexes;
SV *index;
#define METHOD (slotType == COLUMN) ? "column" : "row"
if
((((in->count % 2) == 0) && (in->count > 3)) || (in->count < 1))
return
false
;
index = ARGsv(0);
if
(!SvOK(index) || (SvROK(index) && SvTYPE(SvRV(index)) != SVt_PVAV))
croak(
"Widget::grid(%sconfigure): index is neither a number nor an array of numbers"
, METHOD);
if
(SvROK(index)) {
if
( !( indexes = (
int
*) prima_read_array(index,
"Widget::gridInfo"
,
'i'
, 1, 0, 0, &n_indexes, NULL)))
croak(
"Widget::grid(%sconfigure): cannot read array of indexes, or the array is empty"
, METHOD);
}
else
{
indexes =
malloc
(
sizeof
(
int
));
*indexes = SvIV(index);
n_indexes = 1;
}
masterPtr = GetGrid(self);
checkOnly = ((in->count == 1) || (in->count == 2));
if
(checkOnly && n_indexes > 1)
croak(
"Widget::grid(%sconfigure): row must be a single element"
, METHOD);
for
( slot = 0; slot < n_indexes; slot++) {
Bool ok = CheckSlotData(masterPtr, slot, slotType, checkOnly);
if
(!ok && !checkOnly)
croak(
"Widget::grid(%sconfigure %d): is out of range"
, METHOD, slot);
slotPtr = (slotType == COLUMN) ?
masterPtr->masterDataPtr->columnPtr :
masterPtr->masterDataPtr->rowPtr;
if
(in->count == 1) {
int
minsize = 0, pad = 0, weight = 0, uniform = 0;
if
(ok) {
minsize = slotPtr[slot].minSize;
pad = slotPtr[slot].pad;
weight = slotPtr[slot].weight;
uniform = slotPtr[slot].uniform;
}
RETs(
"minsize"
);
RETi(minsize);
RETs(
"pad"
);
RETi(pad);
RETs(
"uniform"
);
RETi(uniform);
RETs(
"weight"
);
RETi(weight);
return
true
;
}
for
( j = 1; j < in->count; j += 2) {
int
i, *p;
char
*f = ARGs(j);
if
(
strcmp
(f,
"minsize"
) == 0) p = &slotPtr[slot].minSize;
else
if
(
strcmp
(f,
"weight"
) == 0) p = &slotPtr[slot].weight ;
else
if
(
strcmp
(f,
"uniform"
) == 0) p = &slotPtr[slot].uniform;
else
if
(
strcmp
(f,
"pad"
) == 0) p = &slotPtr[slot].pad ;
else
croak(
"Widget::grid(%sconfigure): bad option `%s'"
, METHOD, f);
if
( in->count == 2 ) {
i = ok ? *p : 0;
RETi(i);
}
else
{
i = ARGi(j+1);
if
( i < 0 )
croak(
"Widget::grid(%sconfigure): bad option `%s': should be non-negative"
, METHOD, f);
*p = i;
}
}
}
if
( in->count != 2 ) {
if
(slotType == ROW) {
int
last = masterPtr->masterDataPtr->rowMax - 1;
while
((last >= 0) && (slotPtr[last].weight == 0)
&& (slotPtr[last].pad == 0)
&& (slotPtr[last].minSize == 0)
&& (slotPtr[last].uniform == 0)) {
last--;
}
masterPtr->masterDataPtr->rowMax = last+1;
}
else
{
int
last = masterPtr->masterDataPtr->columnMax - 1;
while
((last >= 0) && (slotPtr[last].weight == 0)
&& (slotPtr[last].pad == 0)
&& (slotPtr[last].minSize == 0)
&& (slotPtr[last].uniform == 0)) {
last--;
}
masterPtr->masterDataPtr->columnMax = last + 1;
}
}
if
(masterPtr->abortPtr != NULL) {
*masterPtr->abortPtr = 1;
}
ArrangeGrid(masterPtr);
#undef METHOD
return
true
;
}
static
Bool
GridSizeCommand(Handle self, PList in, PList out)
{
Gridder *masterPtr;
GridMaster *gridPtr;
Point r = {0,0};
if
( in->count != 0 )
return
false
;
masterPtr = GetGrid(self);
if
(masterPtr->masterDataPtr != NULL) {
SetGridSize(masterPtr);
gridPtr = masterPtr->masterDataPtr;
r.x = MAX(gridPtr->columnEnd, gridPtr->columnMax);
r.y = MAX(gridPtr->rowEnd, gridPtr->rowEnd);
}
RETi(r.x);
RETi(r.y);
return
true
;
}
Bool
GridSlavesCommand( Handle self, PList in, PList out)
{
int
slot, slotType;
char
*cmd;
Gridder *masterPtr;
Gridder *slavePtr;
if
(in->count != 2)
return
false
;
cmd = ARGs(0);
if
(
strcmp
(cmd,
"row"
) == 0)
slotType = ROW;
else
if
(
strcmp
(cmd,
"column"
) == 0)
slotType = COLUMN;
else
return
false
;
slot = ARGi(1);
if
( slot < 0 )
croak(
"Widget::grid(slaves): is an invalid value: should NOT be < 0"
);
masterPtr = GetGrid(self);
for
(slavePtr = masterPtr->slavePtr; slavePtr != NULL; slavePtr = slavePtr->nextPtr) {
if
(slotType == COLUMN && (slavePtr->column > slot
|| slavePtr->column+slavePtr->numCols-1 < slot)) {
continue
;
}
if
(slotType == ROW && (slavePtr->row > slot ||
slavePtr->row+slavePtr->numRows-1 < slot)) {
continue
;
}
if
(slavePtr->window == NULL_HANDLE)
continue
;
RETsv( newSVsv((( PAnyObject) slavePtr->window)-> mate));
}
return
true
;
}
static
int
AdjustOffsets(
int
size,
int
slots,
register
SlotInfo *slotPtr
) {
register
int
slot;
int
diff;
int
totalWeight = 0;
int
weight = 0;
int
minSize = 0;
int
newDiff;
diff = size - slotPtr[slots-1].offset;
if
(diff == 0) {
return
(0);
}
for
(slot=0; slot < slots; slot++) {
totalWeight += slotPtr[slot].weight;
}
if
(totalWeight == 0 ) {
return
(diff > 0 ? diff/2 : 0);
}
if
(diff > 0) {
for
(weight=slot=0; slot < slots; slot++) {
weight += slotPtr[slot].weight;
slotPtr[slot].offset += diff * weight / totalWeight;
}
return
(0);
}
for
(slot=0; slot < slots; slot++) {
if
(slotPtr[slot].weight > 0) {
minSize += slotPtr[slot].minSize;
}
else
if
(slot > 0) {
minSize += slotPtr[slot].offset - slotPtr[slot-1].offset;
}
else
{
minSize += slotPtr[slot].offset;
}
}
if
(size <= minSize) {
int
offset = 0;
for
(slot=0; slot < slots; slot++) {
if
(slotPtr[slot].weight > 0) {
offset += slotPtr[slot].minSize;
}
else
if
(slot > 0) {
offset += slotPtr[slot].offset - slotPtr[slot-1].offset;
}
else
{
offset += slotPtr[slot].offset;
}
slotPtr[slot].offset = offset;
}
return
(0);
}
while
(diff < 0) {
for
(totalWeight=slot=0; slot < slots; slot++) {
int
current = (slot == 0) ? slotPtr[slot].offset :
slotPtr[slot].offset - slotPtr[slot-1].offset;
if
(current > slotPtr[slot].minSize) {
totalWeight += slotPtr[slot].weight;
slotPtr[slot].temp = slotPtr[slot].weight;
}
else
{
slotPtr[slot].temp = 0;
}
}
if
(totalWeight == 0) {
break
;
}
newDiff = diff;
for
(slot = 0; slot < slots; slot++) {
int
current;
int
maxDiff;
if
(slotPtr[slot].temp == 0) {
continue
;
}
current = (slot == 0) ? slotPtr[slot].offset :
slotPtr[slot].offset - slotPtr[slot-1].offset;
maxDiff = totalWeight * (slotPtr[slot].minSize - current)
/ slotPtr[slot].temp;
if
(maxDiff > newDiff) {
newDiff = maxDiff;
}
}
for
(weight=slot=0; slot < slots; slot++) {
weight += slotPtr[slot].temp;
slotPtr[slot].offset += newDiff * weight / totalWeight;
}
diff -= newDiff;
}
return
(0);
}
static
void
AdjustForSticky(
Gridder *slavePtr,
int
*xPtr,
int
*yPtr,
int
*widthPtr,
int
*heightPtr
) {
int
diffx=0;
int
diffy=0;
int
sticky = slavePtr->sticky;
Point sz;
*xPtr += slavePtr->padLeft;
*widthPtr -= slavePtr->padX;
*yPtr += slavePtr->padBottom;
*heightPtr -= slavePtr->padY;
sz = PWidget(slavePtr->window)->geomSize;
if
(*widthPtr > sz.x + slavePtr->iPadX) {
diffx = *widthPtr - (sz.x + slavePtr->iPadX);
*widthPtr = sz.x + slavePtr->iPadX;
}
if
(*heightPtr > (sz.y + slavePtr->iPadY)) {
diffy = *heightPtr - (sz.y + slavePtr->iPadY);
*heightPtr = sz.y + slavePtr->iPadY;
}
if
(sticky&STICK_EAST && sticky&STICK_WEST) {
*widthPtr += diffx;
}
if
(sticky&STICK_NORTH && sticky&STICK_SOUTH) {
*heightPtr += diffy;
}
if
(!(sticky&STICK_WEST)) {
*xPtr += (sticky&STICK_EAST) ? diffx : diffx/2;
}
if
(!(sticky&STICK_SOUTH)) {
*yPtr += (sticky&STICK_NORTH) ? diffy : diffy/2;
}
}
static
void
ArrangeGrid(Gridder *masterPtr)
{
register
Gridder *slavePtr;
GridMaster *slotPtr = masterPtr->masterDataPtr;
int
abort
;
int
width, height;
int
realWidth, realHeight;
Point sz;
if
(masterPtr->lock > 0)
return
;
if
(masterPtr->slavePtr == NULL) {
return
;
}
if
(masterPtr->masterDataPtr == NULL) {
return
;
}
if
(masterPtr->abortPtr != NULL) {
*masterPtr->abortPtr = 1;
}
masterPtr->abortPtr = &
abort
;
abort
= 0;
SetGridSize(masterPtr);
width = ResolveConstraints(masterPtr, COLUMN, 0);
height = ResolveConstraints(masterPtr, ROW, 0);
if
(width < PWidget(masterPtr->window)->sizeMin.x) {
width = PWidget(masterPtr->window)->sizeMin.x;
}
if
(height < PWidget(masterPtr->window)->sizeMin.y) {
height = PWidget(masterPtr->window)->sizeMin.y;
}
sz = PWidget(masterPtr->window)->geomSize;
if
(
(width != sz.x || height != sz.y ) &&
!(masterPtr->flags & DONT_PROPAGATE)
) {
Point p = {width,height};
CWidget(masterPtr->window)->set_geomSize(masterPtr->window,p);
masterPtr->abortPtr = NULL;
return
;
}
sz = CWidget(masterPtr->window)->get_size(masterPtr->window);
realWidth = sz.x;
realHeight = sz.y;
slotPtr->startX = AdjustOffsets(realWidth,
MAX(slotPtr->columnEnd,slotPtr->columnMax), slotPtr->columnPtr);
slotPtr->startY = AdjustOffsets(realHeight,
MAX(slotPtr->rowEnd,slotPtr->rowMax), slotPtr->rowPtr);
for
(slavePtr = masterPtr->slavePtr; slavePtr != NULL && !
abort
;
slavePtr = slavePtr->nextPtr) {
int
x, y;
int
width, height;
int
col = slavePtr->column;
int
row = slavePtr->row;
x = (col>0) ? slotPtr->columnPtr[col-1].offset : 0;
y = (row>0) ? slotPtr->rowPtr[row-1].offset : 0;
width = slotPtr->columnPtr[slavePtr->numCols+col-1].offset - x;
height = slotPtr->rowPtr[slavePtr->numRows+row-1].offset - y;
x += slotPtr->startX;
y += slotPtr->startY;
AdjustForSticky(slavePtr, &x, &y, &width, &height);
if
(masterPtr->window == PWidget(slavePtr-> window)-> owner) {
Rect r;
r. left = x;
r. bottom = y;
r. right = x + width;
r. top = y + height;
CWidget(slavePtr-> window)-> set_rect(slavePtr->window, r);
}
}
masterPtr->abortPtr = NULL;
}
static
int
ResolveConstraints(
Gridder *masterPtr,
int
slotType,
int
maxOffset
) {
register
SlotInfo *slotPtr;
register
Gridder *slavePtr;
int
constraintCount;
int
slotCount;
int
gridCount;
GridLayout *layoutPtr;
int
requiredSize;
int
offset;
int
slot;
int
start;
int
end;
UniformGroup uniformPre[UNIFORM_PREALLOC];
UniformGroup *uniformGroupPtr;
int
uniformGroups;
int
uniformGroupsAlloced;
int
weight, minSize;
GridLayout layoutData[TYPICAL_SIZE + 2];
if
(slotType == COLUMN) {
constraintCount = masterPtr->masterDataPtr->columnMax;
slotCount = masterPtr->masterDataPtr->columnEnd;
slotPtr = masterPtr->masterDataPtr->columnPtr;
}
else
{
constraintCount = masterPtr->masterDataPtr->rowMax;
slotCount = masterPtr->masterDataPtr->rowEnd;
slotPtr = masterPtr->masterDataPtr->rowPtr;
}
gridCount = MAX(constraintCount,slotCount);
if
(gridCount >= TYPICAL_SIZE) {
if
( !( layoutPtr = (GridLayout *)
malloc
(
sizeof
(GridLayout) * (1+gridCount)))) {
warn(
"Not enough memory"
);
return
1;
}
}
else
{
layoutPtr = layoutData;
}
layoutPtr->minOffset = 0;
layoutPtr->maxOffset = 0;
layoutPtr++;
for
(slot=0; slot < constraintCount; slot++) {
layoutPtr[slot].minSize = slotPtr[slot].minSize;
layoutPtr[slot].weight = slotPtr[slot].weight;
layoutPtr[slot].uniform = slotPtr[slot].uniform;
layoutPtr[slot].pad = slotPtr[slot].pad;
layoutPtr[slot].binNextPtr = NULL;
}
for
(;slot<gridCount;slot++) {
layoutPtr[slot].minSize = 0;
layoutPtr[slot].weight = 0;
layoutPtr[slot].uniform = 0;
layoutPtr[slot].pad = 0;
layoutPtr[slot].binNextPtr = NULL;
}
switch
(slotType) {
case
COLUMN:
for
(slavePtr = masterPtr->slavePtr; slavePtr != NULL; slavePtr = slavePtr->nextPtr) {
int
rightEdge = slavePtr->column + slavePtr->numCols - 1;
slavePtr->size = PWidget(slavePtr->window)->geomSize.x + slavePtr->padX + slavePtr->iPadX;
if
(slavePtr->numCols > 1) {
slavePtr->binNextPtr = layoutPtr[rightEdge].binNextPtr;
layoutPtr[rightEdge].binNextPtr = slavePtr;
}
else
{
int
size = slavePtr->size + layoutPtr[rightEdge].pad;
if
(size > layoutPtr[rightEdge].minSize) {
layoutPtr[rightEdge].minSize = size;
}
}
}
break
;
case
ROW:
for
(slavePtr = masterPtr->slavePtr; slavePtr != NULL; slavePtr = slavePtr->nextPtr) {
int
rightEdge = slavePtr->row + slavePtr->numRows - 1;
slavePtr->size = PWidget(slavePtr->window)->geomSize.y + slavePtr->padY + slavePtr->iPadY;
if
(slavePtr->numRows > 1) {
slavePtr->binNextPtr = layoutPtr[rightEdge].binNextPtr;
layoutPtr[rightEdge].binNextPtr = slavePtr;
}
else
{
int
size = slavePtr->size + layoutPtr[rightEdge].pad;
if
(size > layoutPtr[rightEdge].minSize) {
layoutPtr[rightEdge].minSize = size;
}
}
}
break
;
}
uniformGroupPtr = uniformPre;
uniformGroupsAlloced = UNIFORM_PREALLOC;
uniformGroups = 0;
for
(slot = 0; slot < gridCount; slot++) {
if
(layoutPtr[slot].uniform != 0) {
for
(start = 0; start < uniformGroups; start++) {
if
(uniformGroupPtr[start].group == layoutPtr[slot].uniform) {
break
;
}
}
if
(start >= uniformGroups) {
if
(uniformGroups >= uniformGroupsAlloced) {
size_t
newSize = (uniformGroupsAlloced + UNIFORM_PREALLOC)
*
sizeof
(UniformGroup);
UniformGroup *n = (UniformGroup *)
realloc
( uniformGroupPtr, newSize);
if
(!n) {
warn(
"Not enough memory"
);
return
1;
}
uniformGroupPtr = n;
uniformGroupsAlloced += UNIFORM_PREALLOC;
}
uniformGroups++;
uniformGroupPtr[start].group = layoutPtr[slot].uniform;
uniformGroupPtr[start].minSize = 0;
}
weight = layoutPtr[slot].weight;
weight = weight > 0 ? weight : 1;
minSize = (layoutPtr[slot].minSize + weight - 1) / weight;
if
(minSize > uniformGroupPtr[start].minSize) {
uniformGroupPtr[start].minSize = minSize;
}
}
}
if
(uniformGroups > 0) {
for
(slot = 0; slot < gridCount; slot++) {
if
(layoutPtr[slot].uniform != 0) {
for
(start = 0; start < uniformGroups; start++) {
if
(uniformGroupPtr[start].group ==
layoutPtr[slot].uniform) {
weight = layoutPtr[slot].weight;
weight = weight > 0 ? weight : 1;
layoutPtr[slot].minSize =
uniformGroupPtr[start].minSize * weight;
break
;
}
}
}
}
}
if
(uniformGroupPtr != uniformPre) {
free
(uniformGroupPtr);
}
for
(offset=slot=0; slot < gridCount; slot++) {
layoutPtr[slot].minOffset = layoutPtr[slot].minSize + offset;
for
(slavePtr = layoutPtr[slot].binNextPtr; slavePtr != NULL;
slavePtr = slavePtr->binNextPtr) {
int
span = (slotType == COLUMN) ? slavePtr->numCols : slavePtr->numRows;
int
required = slavePtr->size + layoutPtr[slot - span].minOffset;
if
(required > layoutPtr[slot].minOffset) {
layoutPtr[slot].minOffset = required;
}
}
offset = layoutPtr[slot].minOffset;
}
requiredSize = offset;
if
(maxOffset > offset) {
offset=maxOffset;
}
for
(slot=0; slot < gridCount; slot++) {
layoutPtr[slot].maxOffset = offset;
}
for
(slot=gridCount-1; slot > 0;) {
for
(slavePtr = layoutPtr[slot].binNextPtr; slavePtr != NULL;
slavePtr = slavePtr->binNextPtr) {
int
span = (slotType == COLUMN) ? slavePtr->numCols : slavePtr->numRows;
int
require = offset - slavePtr->size;
int
startSlot = slot - span;
if
(startSlot >=0 && require < layoutPtr[startSlot].maxOffset) {
layoutPtr[startSlot].maxOffset = require;
}
}
offset -= layoutPtr[slot].minSize;
slot--;
if
(layoutPtr[slot].maxOffset < offset) {
offset = layoutPtr[slot].maxOffset;
}
else
{
layoutPtr[slot].maxOffset = offset;
}
}
for
(start=0; start < gridCount;) {
int
totalWeight = 0;
int
need = 0;
int
have;
int
weight;
int
noWeights = 0;
if
(layoutPtr[start].minOffset == layoutPtr[start].maxOffset) {
start++;
continue
;
}
for
(end=start+1; end<gridCount; end++) {
if
(layoutPtr[end].minOffset == layoutPtr[end].maxOffset) {
break
;
}
}
for
(slot=start; slot<=end; slot++) {
totalWeight += layoutPtr[slot].weight;
need += layoutPtr[slot].minSize;
}
have = layoutPtr[end].maxOffset - layoutPtr[start-1].minOffset;
if
(totalWeight == 0) {
noWeights++;
totalWeight = end - start + 1;
}
for
(weight=0,slot=start; slot<end; slot++) {
int
diff = layoutPtr[slot].maxOffset - layoutPtr[slot].minOffset;
weight += noWeights ? 1 : layoutPtr[slot].weight;
if
((noWeights || layoutPtr[slot].weight>0) &&
(diff*totalWeight/weight) < (have-need)) {
have = diff * totalWeight / weight + need;
}
}
for
(weight=0,slot=start; slot<end; slot++) {
weight += noWeights ? 1 : layoutPtr[slot].weight;
layoutPtr[slot].minOffset +=
(
int
)((
double
) (have-need) * weight/totalWeight + 0.5);
layoutPtr[slot].minSize = layoutPtr[slot].minOffset
- layoutPtr[slot-1].minOffset;
}
layoutPtr[slot].minSize = layoutPtr[slot].minOffset
- layoutPtr[slot-1].minOffset;
for
(slot=end; slot > start; slot--) {
layoutPtr[slot-1].maxOffset =
layoutPtr[slot].maxOffset-layoutPtr[slot].minSize;
}
}
for
(slot=0; slot < gridCount; slot++) {
slotPtr[slot].offset = layoutPtr[slot].minOffset;
}
--layoutPtr;
if
(layoutPtr != layoutData) {
free
(layoutPtr);
}
return
requiredSize;
}
static
Gridder *
GetGrid(
Handle self
) {
register
Gridder *gridPtr;
if
( var-> gridder )
return
(Gridder*) var->gridder;
if
( !( gridPtr = (Gridder *)
malloc
(
sizeof
(Gridder))))
croak(
"Not enough memory"
);
gridPtr->window = self;
gridPtr->masterPtr = NULL;
gridPtr->masterDataPtr = NULL;
gridPtr->nextPtr = NULL;
gridPtr->slavePtr = NULL;
gridPtr->binNextPtr = NULL;
gridPtr->column = gridPtr->row = -1;
gridPtr->numCols = 1;
gridPtr->numRows = 1;
gridPtr->padX = gridPtr->padY = 0;
gridPtr->padLeft = gridPtr->padBottom = 0;
gridPtr->iPadX = gridPtr->iPadY = 0;
gridPtr->abortPtr = NULL;
gridPtr->flags = 0;
gridPtr->sticky = 0;
gridPtr->size = 0;
gridPtr->saved_in = NULL_HANDLE;
gridPtr->lock = 0;
var->gridder = gridPtr;
return
gridPtr;
}
static
void
SetGridSize(
Gridder *masterPtr
) {
register
Gridder *slavePtr;
int
maxX = 0, maxY = 0;
for
(slavePtr = masterPtr->slavePtr; slavePtr != NULL;
slavePtr = slavePtr->nextPtr) {
maxX = MAX(maxX,slavePtr->numCols + slavePtr->column);
maxY = MAX(maxY,slavePtr->numRows + slavePtr->row);
}
masterPtr->masterDataPtr->columnEnd = maxX;
masterPtr->masterDataPtr->rowEnd = maxY;
CheckSlotData(masterPtr, maxX, COLUMN, CHECK_SPACE);
CheckSlotData(masterPtr, maxY, ROW, CHECK_SPACE);
}
static
Bool
CheckSlotData(
Gridder *masterPtr,
int
slot,
int
slotType,
int
checkOnly
) {
int
numSlot;
int
end;
if
(slot < 0 || slot >= MAX_ELEMENT) {
warn(
"grid row or column out of range"
);
return
false
;
}
if
((checkOnly == CHECK_ONLY) && (masterPtr->masterDataPtr == NULL)) {
return
false
;
}
InitMasterData(masterPtr);
end = (slotType == ROW) ? masterPtr->masterDataPtr->rowMax :
masterPtr->masterDataPtr->columnMax;
if
(checkOnly == CHECK_ONLY)
return
end >= slot;
numSlot = (slotType == ROW) ?
masterPtr->masterDataPtr->rowSpace :
masterPtr->masterDataPtr->columnSpace;
if
(slot >= numSlot) {
int
newNumSlot = slot + PREALLOC ;
size_t
oldSize = numSlot *
sizeof
(SlotInfo) ;
size_t
newSize = newNumSlot *
sizeof
(SlotInfo) ;
SlotInfo *old = (slotType == ROW) ?
masterPtr->masterDataPtr->rowPtr :
masterPtr->masterDataPtr->columnPtr;
SlotInfo *newx = (SlotInfo *)
realloc
(old, newSize);
if
( !newx ) {
warn(
"not enough memory"
);
return
false
;
}
bzero(newx + numSlot, newSize - oldSize );
if
(slotType == ROW) {
masterPtr->masterDataPtr->rowPtr = newx;
masterPtr->masterDataPtr->rowSpace = newNumSlot ;
}
else
{
masterPtr->masterDataPtr->columnPtr = newx;
masterPtr->masterDataPtr->columnSpace = newNumSlot ;
}
}
if
(slot >= end && checkOnly != CHECK_SPACE) {
if
(slotType == ROW) {
masterPtr->masterDataPtr->rowMax = slot+1;
}
else
{
masterPtr->masterDataPtr->columnMax = slot+1;
}
}
return
true
;
}
static
void
InitMasterData( Gridder *masterPtr)
{
size_t
size;
if
(masterPtr->masterDataPtr == NULL) {
GridMaster *gridPtr = masterPtr->masterDataPtr =
(GridMaster *)
malloc
(
sizeof
(GridMaster));
size =
sizeof
(SlotInfo) * TYPICAL_SIZE;
gridPtr->columnEnd = 0;
gridPtr->columnMax = 0;
gridPtr->columnPtr = (SlotInfo *)
malloc
(size);
gridPtr->columnSpace = TYPICAL_SIZE;
gridPtr->rowEnd = 0;
gridPtr->rowMax = 0;
gridPtr->rowPtr = (SlotInfo *)
malloc
(size);
gridPtr->rowSpace = TYPICAL_SIZE;
gridPtr->startX = 0;
gridPtr->startY = 0;
bzero(gridPtr->columnPtr, size);
bzero(gridPtr->rowPtr, size);
}
}
static
void
Unlink( Gridder *slavePtr)
{
register
Gridder *masterPtr, *slavePtr2;
masterPtr = slavePtr->masterPtr;
if
(masterPtr == NULL) {
return
;
}
if
(masterPtr->slavePtr == slavePtr) {
masterPtr->slavePtr = slavePtr->nextPtr;
}
else
{
for
(slavePtr2 = masterPtr->slavePtr; ; slavePtr2 = slavePtr2->nextPtr) {
if
(slavePtr2 == NULL) {
croak(
"Widget.gridUnlink: couldn't find previous window"
);
}
if
(slavePtr2->nextPtr == slavePtr) {
slavePtr2->nextPtr = slavePtr->nextPtr;
break
;
}
}
}
if
(masterPtr->abortPtr != NULL) {
*masterPtr->abortPtr = 1;
}
SetGridSize(slavePtr->masterPtr);
slavePtr->masterPtr = NULL;
}
static
void
DestroyGrid(Handle self)
{
Gridder *gridPtr = (Gridder*) var-> gridder, *gridPtr2, *nextPtr;
if
( !gridPtr )
return
;
if
(gridPtr->masterPtr != NULL) {
Unlink(gridPtr);
}
for
(gridPtr2 = gridPtr->slavePtr; gridPtr2 != NULL; gridPtr2 = nextPtr) {
gridPtr2->masterPtr = NULL;
nextPtr = gridPtr2->nextPtr;
gridPtr2->nextPtr = NULL;
}
if
(gridPtr->masterDataPtr != NULL) {
if
(gridPtr->masterDataPtr->rowPtr != NULL) {
free
(gridPtr->masterDataPtr -> rowPtr);
}
if
(gridPtr->masterDataPtr->columnPtr != NULL) {
free
(gridPtr->masterDataPtr -> columnPtr);
}
free
(gridPtr->masterDataPtr);
}
free
(gridPtr);
var-> gridder = NULL;
}
#define METHOD "Widget::grid_configure_slaves"
static
void
parse_pad_amount(SV *src,
int
*i1,
int
*i2)
{
if
(SvOK(src) && SvROK(src) && SvTYPE(SvRV(src)) == SVt_PVAV) {
int
*p = prima_read_array(src, METHOD,
'i'
, 1, 2, 2, NULL, NULL);
*i1 = p[0];
*i2 = p[0] + p[1];
free
(p);
}
else
{
*i1 = SvIV(src);
*i2 = *i1 * 2;
}
}
static
Gridder*
parse_info( Gridder *slavePtr, HV *profile)
{
dPROFILE;
Gridder *masterPtr = NULL;
if
( pexist(column)) {
int
c = pget_i(column);
if
( c < 0 ) croak(
"%s: column value must be a non-negative integer"
, METHOD);
slavePtr->column = c;
}
if
( pexist(colspan)) {
int
c = pget_i(colspan);
if
( c < 0 ) croak(
"%s: colspan value must be a non-negative integer"
, METHOD);
slavePtr->numCols = c;
}
if
( pexist(in)) {
Handle in, h;
if
( ( h = in = pget_H(in)) != NULL_HANDLE ) {
if
(in == slavePtr->window)
croak(
"%s: widget can't be managed in itself"
, METHOD);
while
( h) {
h = PWidget(h)-> owner;
if
(in == slavePtr->window)
croak(
"%s: widget can't be managed by its child"
, METHOD);
}
masterPtr = GetGrid(in);
InitMasterData(masterPtr);
}
slavePtr->saved_in = in;
}
if
(pexist(ipadx)) slavePtr->iPadX = 2 * pget_i(ipadx);
if
(pexist(ipady)) slavePtr->iPadY = 2 * pget_i(ipady);
if
(pexist(padx)) parse_pad_amount( pget_sv(padx), &slavePtr->padLeft, &slavePtr->padX);
if
(pexist(pady)) parse_pad_amount( pget_sv(pady), &slavePtr->padBottom, &slavePtr->padY);
if
( pexist(row)) {
int
c = pget_i(row);
if
( c < 0 ) croak(
"%s: row value must be a non-negative integer"
, METHOD);
slavePtr->row = c;
}
if
( pexist(rowspan)) {
int
c = pget_i(rowspan);
if
( c < 0 ) croak(
"%s: rowspan value must be a non-negative integer"
, METHOD);
slavePtr->numRows = c;
}
if
(pexist(sticky)) {
char
*s = pget_c(sticky);
int
sticky = StringToSticky(s);
if
(sticky == -1)
croak(
"%s: bad stickyness value \"%s\": must be a string containing n, e, s, and/or w"
,
METHOD, s);
slavePtr->sticky = sticky;
}
return
masterPtr;
}
static
void
ConfigureSlaves(Handle self, SV* window, HV *profile)
{
SV **slaves;
int
n_slaves;
Gridder *masterPtr;
Gridder *slavePtr;
Handle slave;
int
i;
int
width;
int
defaultColumn = 0;
int
defaultColumnSpan = 1;
Handle lastWindow;
int
numSkip;
STRLEN len;
char
*firstStr, *prevStr;
if
(window && !(
SvOK(window) && (
!SvROK(window) || (
(SvTYPE(SvRV(window)) == SVt_PVAV) ||
(SvTYPE(SvRV(window)) == SVt_PVHV)
)
)
))
croak(
"%s: window is neither an object nor an array of objects"
, METHOD);
if
(window && SvROK(window) && (SvTYPE(SvRV(window)) == SVt_PVAV)) {
if
( !( slaves = (SV**) prima_read_array(window, METHOD,
'H'
, 1, 0, 0, &n_slaves, NULL)))
croak(
"%s: cannot read array of windows, or the array is empty"
, METHOD);
}
else
{
slaves =
malloc
(
sizeof
(SV*));
n_slaves = 1;
*slaves = window;
}
firstStr =
""
;
for
(i = 0; i < n_slaves; i++) {
if
( slaves[i] && SvROK( slaves[i]))
continue
;
prevStr = firstStr;
firstStr = slaves[i] ? SvPV( slaves[i], len) :
""
;
if
(*firstStr == REL_HORIZ) {
if
(
i==0 ||
(prevStr[0] == REL_SKIP && prevStr[1] == 0) ||
(
strcmp
(prevStr,
"^"
) == 0)
)
croak(
"%s: Must specify window before shortcut '-'."
, METHOD);
continue
;
}
}
masterPtr = NULL;
for
(i = 0; i < n_slaves; i++) {
if
( slaves[i] && !SvROK(slaves[i])) {
firstStr = SvPV( slaves[i], len);
if
(len == 1 && (*firstStr == REL_VERT || *firstStr == REL_SKIP)) {
defaultColumn++;
continue
;
}
if
(*firstStr == REL_HORIZ) {
continue
;
}
for
(defaultColumnSpan = 1; i + defaultColumnSpan < n_slaves; defaultColumnSpan++) {
STRLEN l = 1;
char
*string = SvROK( slaves[i+defaultColumnSpan] ) ?
""
: SvPV(slaves[i + defaultColumnSpan], l);
if
( l != 1 || *string != REL_HORIZ) {
break
;
}
}
}
if
(( slave = get_slave(self, slaves[i])) == NULL_HANDLE )
croak(
"%s: cannot locate child widget %s"
, METHOD, firstStr);
slavePtr = GetGrid(slave);
masterPtr = parse_info( slavePtr, profile );
if
( slavePtr->saved_in || slave != self )
Widget_check_in(slave, self, gtGrid);
if
(masterPtr == NULL) {
masterPtr = slavePtr->masterPtr;
}
if
(masterPtr == NULL) {
masterPtr = GetGrid(PWidget(slave)->owner);
InitMasterData(masterPtr);
}
if
(slavePtr->masterPtr != NULL && slavePtr->masterPtr != masterPtr) {
Unlink(slavePtr);
slavePtr->masterPtr = NULL;
}
if
(slavePtr->masterPtr == NULL) {
Gridder *tempPtr = masterPtr->slavePtr;
slavePtr->masterPtr = masterPtr;
masterPtr->slavePtr = slavePtr;
slavePtr->nextPtr = tempPtr;
}
if
(masterPtr->masterPtr == slavePtr) {
Unlink(slavePtr);
croak(
"%s: can't put that slave here"
, METHOD);
}
if
(slavePtr->column == -1) {
slavePtr->column = defaultColumn;
}
slavePtr->numCols += defaultColumnSpan - 1;
if
(slavePtr->row == -1) {
if
(masterPtr->masterDataPtr == NULL) {
slavePtr->row = 0;
}
else
{
slavePtr->row = masterPtr->masterDataPtr->rowEnd;
}
}
defaultColumn += slavePtr->numCols;
defaultColumnSpan = 1;
if
(masterPtr->abortPtr != NULL) {
*masterPtr->abortPtr = 1;
}
if
( PWidget(slave)->geometry == gtGrid) {
SetGridSize(masterPtr);
ArrangeGrid(masterPtr);
}
else
{
CWidget(slave)->set_geometry(slave, gtGrid);
}
}
lastWindow = -1;
numSkip = 0;
for
(i = 0; i < n_slaves; i++) {
struct
Gridder *otherPtr;
int
match;
int
lastRow, lastColumn;
len = 0;
firstStr = (!slaves[i] || SvROK(slaves[i])) ?
""
:
SvPV( slaves[i], len);
if
( len == 1 && *firstStr == REL_SKIP ) {
numSkip++;
}
else
if
( len == 1 && *firstStr == REL_VERT) {
}
else
{
numSkip = 0;
lastWindow = get_slave(self, slaves[i]);
continue
;
}
if
(masterPtr == NULL) {
croak(
"%s: can't use '^', can't find master"
, METHOD);
}
for
(width = 1; width + i < n_slaves; width++) {
len = 0;
firstStr = SvROK(slaves[i+width]) ?
""
: SvPV( slaves[i+width], len);
if
(len != 1 || *firstStr != REL_VERT )
break
;
}
if
(lastWindow < 0) {
if
(masterPtr->masterDataPtr != NULL) {
SetGridSize(masterPtr);
lastRow = masterPtr->masterDataPtr->rowEnd - 2;
}
else
{
lastRow = 0;
}
lastColumn = 0;
}
else
{
otherPtr = GetGrid(lastWindow);
lastRow = otherPtr->row + otherPtr->numRows - 2;
lastColumn = otherPtr->column + otherPtr->numCols;
}
lastColumn += numSkip;
for
(match=0, slavePtr = masterPtr->slavePtr; slavePtr != NULL;
slavePtr = slavePtr->nextPtr) {
if
(slavePtr->column == lastColumn
&& slavePtr->row + slavePtr->numRows - 1 == lastRow) {
if
(slavePtr->numCols <= width) {
slavePtr->numRows++;
match++;
i += slavePtr->numCols - 1;
lastWindow = slavePtr->window;
numSkip = 0;
break
;
}
}
}
if
(!match) {
croak(
"%s: can't find slave to extend with \"^\"."
, METHOD);
}
}
if
(masterPtr == NULL) {
croak(
"%s: can't determine master window"
, METHOD);
}
SetGridSize(masterPtr);
}
#undef METHOD
static
void
StickyToString(
int
flags,
char
*result
) {
int
count = 0;
if
(flags&STICK_NORTH) {
result[count++] =
'n'
;
}
if
(flags&STICK_EAST) {
result[count++] =
'e'
;
}
if
(flags&STICK_SOUTH) {
result[count++] =
's'
;
}
if
(flags&STICK_WEST) {
result[count++] =
'w'
;
}
if
(count) {
result[count] =
'\0'
;
}
else
{
sprintf
(result,
"{}"
);
}
}
static
int
StringToSticky(
char
*string)
{
int
sticky = 0;
char
c;
while
((c = *string++) !=
'\0'
) {
switch
(c) {
case
'n'
:
case
'N'
: sticky |= STICK_NORTH;
break
;
case
'e'
:
case
'E'
: sticky |= STICK_EAST;
break
;
case
's'
:
case
'S'
: sticky |= STICK_SOUTH;
break
;
case
'w'
:
case
'W'
: sticky |= STICK_WEST;
break
;
case
' '
:
case
','
:
case
'\t'
:
case
'\r'
:
case
'\n'
:
break
;
default
:
return
-1;
}
}
return
sticky;
}
void
Widget_grid_slaves( Handle self)
{
if
( !var-> gridder )
return
;
ArrangeGrid((Gridder*) var->gridder);
}
void
Widget_grid_leave( Handle self)
{
Gridder *g = (Gridder*) var-> gridder;
if
( !g)
return
;
Unlink(g);
g->masterPtr = NULL;
}
static
void
link_slave( Gridder *slavePtr, Gridder* masterPtr)
{
slavePtr->masterPtr = masterPtr;
InitMasterData(slavePtr->masterPtr);
slavePtr->nextPtr = masterPtr->slavePtr;
masterPtr->slavePtr = slavePtr;
if
(masterPtr->abortPtr != NULL) {
*masterPtr->abortPtr = 1;
}
SetGridSize(slavePtr->masterPtr);
}
void
Widget_grid_enter( Handle self)
{
Gridder *slavePtr = (Gridder*) var-> gridder;
if
(slavePtr->masterPtr != NULL) {
Unlink(slavePtr);
slavePtr->masterPtr = NULL;
}
if
(
slavePtr->saved_in &&
!hash_fetch( prima_guts.objects, &slavePtr->saved_in,
sizeof
(Handle))
)
slavePtr->saved_in = NULL_HANDLE;
link_slave(slavePtr, GetGrid(slavePtr->saved_in ? slavePtr->saved_in : var->owner));
}
void
Widget_grid_destroy( Handle self)
{
DestroyGrid(self);
}
static
Bool
grid_lock( Handle self, PList in, PList out)
{
Gridder *gridPtr = (Gridder*) var-> gridder;
if
( in-> count == 0 ) {
RETi( gridPtr ? gridPtr->lock : 0);
}
else
if
( in-> count == 1 ) {
int
d = ARGi(0);
if
( d > 0 )
gridPtr->lock++;
else
if
(d < 0 && gridPtr->lock > 0 ) {
gridPtr->lock--;
if
( gridPtr->lock == 0 )
ArrangeGrid(gridPtr);
}
}
else
return
false
;
return
true
;
}
static
HV*
opt2hv( PList in,
int
start )
{
HV *profile = prima_hash_create();
for
( ; start < in->count; start += 2 ) {
STRLEN l;
char
*key = SvPV((SV*) in->items[start], l);
SV *val = (SV*) in->items[start+1];
(
void
) hv_store( profile, key, (I32) l, val, 0);
}
return
profile;
}
XS( Widget_grid_action_FROMPERL)
{
dXSARGS;
Handle self;
char
*selector =
""
;
int
i, ok =
false
;
List in, out;
list_create(&in, items, 1);
list_create(&out, 32, 32);
if
(items < 2)
goto
FAIL;
if
( !( self = gimme_the_mate( ST( 0))))
goto
FAIL;
selector = SvPV_nolen(ST(1));
for
( i = 2; i < items; i++)
list_add( &in, (Handle)(ST(i)));
SPAGAIN;
SP -= items;
if
(
strcmp
(selector,
"bbox"
) == 0) {
ok = GridBboxCommand(self, &in, &out);
}
else
if
(
(
strcmp
(selector,
"colconfigure"
) == 0) ||
(
strcmp
(selector,
"rowconfigure"
) == 0)
) {
ok = GridRowColumnConfigureCommand(self, (*selector ==
'c'
) ? COLUMN : ROW, &in, &out);
}
else
if
(
strcmp
(selector,
"configure"
) == 0) {
if
(( ok = ( in.count > 0 ))) {
HV * profile = opt2hv(&in, in.count % 2);
ConfigureSlaves(self,
(in.count % 2) ? (SV*)in.items[0] : NULL,
profile
);
hash_destroy(profile,
false
);
}
}
else
if
(
(
strcmp
(selector,
"forget"
) == 0) ||
(
strcmp
(selector,
"remove"
) == 0)
) {
ok = GridForgetRemoveCommand(self, *selector ==
'f'
, &in, &out);
}
else
if
(
strcmp
(selector,
"info"
) == 0) {
ok = GridInfoCommand(self, &in, &out);
}
else
if
(
strcmp
(selector,
"location"
) == 0) {
ok = GridLocationCommand(self, &in, &out);
}
else
if
(
strcmp
(selector,
"propagate"
) == 0) {
ok = GridPropagateCommand(self, &in, &out);
}
else
if
(
strcmp
(selector,
"size"
) == 0) {
ok = GridSizeCommand(self, &in, &out);
}
else
if
(
strcmp
(selector,
"slaves"
) == 0) {
ok = GridSlavesCommand(self, &in, &out);
}
else
if
(
strcmp
(selector,
"lock"
) == 0) {
ok = grid_lock(self, &in, &out);
}
else
{
croak(
"Widget.grid_action: not such subcommand"
);
}
if
( out.count > 0 ) {
EXTEND(sp, out.count);
for
( i = 0; i < out.count; i++)
PUSHs(sv_2mortal((SV*) out.items[i]));
}
list_destroy(&in);
list_destroy(&out);
PUTBACK;
FAIL:
if
( !ok ) croak (
"Invalid usage of Widget.grid_action(%s)"
, selector);
return
;
}
void
Widget_grid_action ( Handle self) { warn(
"Invalid call of Widget::grid_action"
); }
void
Widget_grid_action_REDEFINED( Handle self) { warn(
"Invalid call of Widget::grid_action"
); }
Bool
Widget_is_grid_slave( Handle self, Handle slave)
{
Gridder *slavePtr = (Gridder*) var-> gridder;
if
( !slavePtr )
return
false
;
for
(slavePtr = slavePtr->slavePtr; slavePtr != NULL; slavePtr = slavePtr->nextPtr) {
if
( slavePtr-> window == slave )
return
true
;
}
return
false
;
}
Bool
Widget_has_grid_slaves(Handle self)
{
Gridder *slavePtr = (Gridder*) var-> gridder;
if
( !slavePtr )
return
false
;
return
slavePtr->slavePtr != NULL;
}
#ifdef __cplusplus
}
#endif