/*- * Copyright (c) 1997-2002 The Protein Laboratory, University of Copenhagen * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: Widget_geometry.c,v 1.5 2005/04/30 21:50:07 dk Exp $ */ #include "apricot.h" #include "Widget.h" #ifdef __cplusplus extern "C" { #endif #undef my #define inherited CDrawable #define my ((( PWidget) self)-> self) #define var (( PWidget) self) #define his (( PWidget) child) void Widget_pack_slaves( Handle self); void Widget_place_slaves( Handle self); Bool Widget_size_notify( Handle self, Handle child, const Rect* metrix); Bool Widget_move_notify( Handle self, Handle child, Point * moveTo); static void Widget_pack_enter( Handle self); static void Widget_pack_leave( Handle self); static void Widget_place_enter( Handle self); static void Widget_place_leave( Handle self); /* geometry managers. growMode - native Prima model, borrowed from TurboVision. Does not live with geomSize request size, uses virtualSize instead. pack and place - copy-pasted from Perl-Tk. Warning - all Tk positioning code is in Tk coordinates, meaning that Y axis descends */ /* pack Handle fields: next - available only when geometry == gtPack order, in - available always, but is guaranteedly valid when geometry == gtPack only in and order cause croaks when submitted via packInfo(), but are silently converted to nil when geometry changes and the references are not valid anymore */ #define MASTER ((var->geometry != gtGrowMode && var->geomInfo.in)?var->geomInfo.in:var->owner) #define geometry_reset_all() geometry_reset(MASTER,-1) /* resets particular ( or all, if geometry < 0 ) geometry widgets */ static void geometry_reset( Handle self, int geometry) { if ( !self) return; if ( (var-> geometry == gtGrowMode) && (var-> growMode & gmCenter) && ( geometry == gtGrowMode || geometry < 0) ) { my-> set_centered( self, var-> growMode & gmXCenter, var-> growMode & gmYCenter); } if ( geometry == gtPack || geometry < 0) Widget_pack_slaves( self); if ( geometry == gtPlace || geometry < 0) Widget_place_slaves( self); } int Widget_geometry( Handle self, Bool set, int geometry) { if ( !set) return var-> geometry; if ( geometry == var-> geometry) { /* because called within set_owner() */ if ((var-> geometry == gtGrowMode) && (var-> growMode & gmCenter)) my-> set_centered( self, var-> growMode & gmXCenter, var-> growMode & gmYCenter); return geometry; } if ( geometry < gtDefault || geometry > gtMax) croak("Prima::Widget::geometry: invalid value passed"); switch ( var-> geometry) { case gtPlace: Widget_place_leave( self); break; case gtPack: Widget_pack_leave( self); break; } var-> geometry = geometry; switch ( var-> geometry) { case gtGrowMode: if ( var-> growMode & gmCenter) my-> set_centered( self, var-> growMode & gmXCenter, var-> growMode & gmYCenter); break; case gtPlace: Widget_place_enter( self); break; case gtPack: Widget_pack_enter( self); break; } geometry_reset_all(); return var-> geometry; } Point Widget_geomSize( Handle self, Bool set, Point geomSize) { if ( !set) return var-> geomSize; /* return ( var-> geometry == gtDefault) ? my-> get_size(self) : var-> geomSize; */ var-> geomSize = geomSize; if ( var-> geometry == gtDefault) my-> set_size( self, var-> geomSize); else geometry_reset_all(); return var-> geomSize; } int Widget_geomHeight( Handle self, Bool set, int geomHeight) { if ( set) { Point p = { var-> geomSize. x, geomHeight}; my-> set_geomSize( self, p); } return var-> geomSize. y; } int Widget_geomWidth( Handle self, Bool set, int geomWidth) { if ( set) { Point p = { geomWidth, var-> geomSize. y}; my-> set_geomSize( self, p); } return var-> geomSize. x; } Bool Widget_packPropagate( Handle self, Bool set, Bool propagate) { Bool repack; if ( !set) return is_opt( optPackPropagate); repack = !(is_opt( optPackPropagate)) && propagate; opt_assign( optPackPropagate, propagate); if ( repack) geometry_reset(self,-1); return is_opt( optPackPropagate); } void Widget_reset_children_geometry( Handle self) { Widget_pack_slaves( self); Widget_place_slaves( self); } /* checks if Handle in is allowed to be a master for self - used for gt::Pack */ static Handle Widget_check_in( Handle self, Handle in, Bool barf) { Handle h = in; /* check overall validity */ if ( !in || !kind_of( in, CWidget)) { if ( barf) croak("%s: invalid 'in': not a widget", "RTC008F: Prima::Widget::pack"); else return nilHandle; } /* check direct inheritance */ while ( h) { if ( h == self) { if ( barf) croak("%s: invalid 'in': is already a child", "RTC008F: Prima::Widget::pack"); else return nilHandle; } h = PWidget( h)-> owner; } /* check slaves chain */ h = PWidget( in)-> packSlaves; while ( h) { if ( h == in) { if ( barf) croak("%s: invalid 'in': already a pack slave", "RTC008F: Prima::Widget::pack"); else return nilHandle; } h = PWidget( h)-> geomInfo. next; } h = PWidget( in)-> placeSlaves; while ( h) { if ( h == in) { if ( barf) croak("%s: invalid 'in': already a place slave", "RTC008F: Prima::Widget::pack"); else return nilHandle; } h = PWidget( h)-> geomInfo. next; } /* place to check other chains if needed */ return in; } Point Widget_sizeMin( Handle self, Bool set, Point min) { if ( !set) return var-> sizeMin; var-> sizeMin = min; if ( var-> stage <= csFrozen) { Point sizeActual = my-> get_size( self); Point newSize = sizeActual; if ( sizeActual. x < min. x) newSize. x = min. x; if ( sizeActual. y < min. y) newSize. y = min. y; if (( newSize. x != sizeActual. x) || ( newSize. y != sizeActual. y)) my-> set_size( self, newSize); if ( var-> geometry != gtDefault) geometry_reset_all(); } apc_widget_set_size_bounds( self, var-> sizeMin, var-> sizeMax); return min; } Point Widget_sizeMax( Handle self, Bool set, Point max) { if ( !set) return var-> sizeMax; var-> sizeMax = max; if ( var-> stage <= csFrozen) { Point sizeActual = my-> get_size( self); Point newSize = sizeActual; if ( sizeActual. x > max. x) newSize. x = max. x; if ( sizeActual. y > max. y) newSize. y = max. y; if (( newSize. x != sizeActual. x) || ( newSize. y != sizeActual. y)) my-> set_size( self, newSize); if ( var-> geometry != gtDefault) geometry_reset_all(); } apc_widget_set_size_bounds( self, var-> sizeMin, var-> sizeMax); return max; } /* geometry managers */ /* gtGrowMode */ Bool Widget_size_notify( Handle self, Handle child, const Rect* metrix) { if ( his-> growMode) { Point size = his-> self-> get_virtual_size( child); Point pos = his-> self-> get_origin( child); Point osize = size, opos = pos; int dx = ((Rect *) metrix)-> right - ((Rect *) metrix)-> left; int dy = ((Rect *) metrix)-> top - ((Rect *) metrix)-> bottom; if ( his-> growMode & gmGrowLoX) pos. x += dx; if ( his-> growMode & gmGrowHiX) size. x += dx; if ( his-> growMode & gmGrowLoY) pos. y += dy; if ( his-> growMode & gmGrowHiY) size. y += dy; if ( his-> growMode & gmXCenter) pos. x = (((Rect *) metrix)-> right - size. x) / 2; if ( his-> growMode & gmYCenter) pos. y = (((Rect *) metrix)-> top - size. y) / 2; if ( pos.x != opos.x || pos.y != opos.y || size.x != osize.x || size.y != osize.y) { if ( pos.x == opos.x && pos.y == opos.y) { his-> self-> set_size( child, size); } else if ( size.x == osize.x && size.y == osize.y) { his-> self-> set_origin( child, pos); } else { Rect r; r. left = pos. x; r. bottom = pos. y; r. right = pos. x + size. x; r. top = pos. y + size. y; his-> self-> set_rect( child, r); } } } return false; } Bool Widget_move_notify( Handle self, Handle child, Point * moveTo) { Bool clp = his-> self-> get_clipOwner( child); int dx = moveTo-> x - var-> pos. x; int dy = moveTo-> y - var-> pos. y; Point p; if ( his-> growMode & gmDontCare) { if ( !clp) return false; p = his-> self-> get_origin( child); p. x -= dx; p. y -= dy; his-> self-> set_origin( child, p); } else { if ( clp) return false; p = his-> self-> get_origin( child); p. x += dx; p. y += dy; his-> self-> set_origin( child, p); } return false; } /* PACK */ #define LEFT 0 #define BOTTOM 1 #define RIGHT 2 #define TOP 3 #define SOUTH 0 #define NORTH 2 #define WEST 0 #define EAST 2 #define CENTER 1 /* pack() internal mechanism - stolen from Tk v800.24, tkPack.c Note that the original algorithm is taught to respect sizeMin and sizeMax, not present in Tk */ /* *---------------------------------------------------------------------- * * XExpansion -- * * Given a list of packed slaves, the first of which is packed * on the left or right and is expandable, compute how much to * expand the child. * * Results: * The return value is the number of additional pixels to give to * the child. * *---------------------------------------------------------------------- */ static int slave_width( register PWidget slavePtr, register int plus) { register int width = slavePtr-> geomSize. x + slavePtr-> geomInfo. pad.x + slavePtr-> geomInfo. ipad.x + plus; if ( width < slavePtr-> sizeMin.x) width = slavePtr-> sizeMin.x; if ( width > slavePtr-> sizeMax.x) width = slavePtr-> sizeMax.x; return width; } static int slave_height( register PWidget slavePtr, register int plus) { register int height = slavePtr-> geomSize.y + slavePtr-> geomInfo. pad.y + slavePtr-> geomInfo. ipad.y + plus; if ( height < slavePtr-> sizeMin.y) height = slavePtr-> sizeMin.y; if ( height > slavePtr-> sizeMax.y) height = slavePtr-> sizeMax.y; return height; } static int XExpansion(slavePtr, cavityWidth) register PWidget slavePtr; /* First in list of remaining slaves. */ int cavityWidth; /* Horizontal space left for all * remaining slaves. */ { int numExpand, minExpand, curExpand; int childWidth; /* * This procedure is tricky because windows packed top or bottom can * be interspersed among expandable windows packed left or right. * Scan through the list, keeping a running sum of the widths of * all left and right windows (actually, count the cavity space not * allocated) and a running count of all expandable left and right * windows. At each top or bottom window, and at the end of the * list, compute the expansion factor that seems reasonable at that * point. Return the smallest factor seen at any of these points. */ minExpand = cavityWidth; numExpand = 0; for (; slavePtr != NULL; slavePtr = ( PWidget) slavePtr-> geomInfo. next) { childWidth = slave_width(slavePtr, 0); if ((slavePtr-> geomInfo. side == TOP) || (slavePtr-> geomInfo. side == BOTTOM)) { curExpand = (cavityWidth - childWidth)/numExpand; if (curExpand < minExpand) { minExpand = curExpand; } } else { cavityWidth -= childWidth; if (slavePtr->geomInfo. expand) { numExpand++; } } } curExpand = cavityWidth/numExpand; if (curExpand < minExpand) { minExpand = curExpand; } return (minExpand < 0) ? 0 : minExpand; } /* *---------------------------------------------------------------------- * * YExpansion -- * * Given a list of packed slaves, the first of which is packed * on the top or bottom and is expandable, compute how much to * expand the child. * * Results: * The return value is the number of additional pixels to give to * the child. * * Side effects: * None. * *---------------------------------------------------------------------- */ static int YExpansion(slavePtr, cavityHeight) register PWidget slavePtr; /* First in list of remaining * slaves. */ int cavityHeight; /* Vertical space left for all * remaining slaves. */ { int numExpand, minExpand, curExpand; int childHeight; /* * See comments for XExpansion. */ minExpand = cavityHeight; numExpand = 0; for (; slavePtr != NULL; slavePtr = (PWidget) slavePtr->geomInfo. next) { childHeight = slave_height(slavePtr, 0); if ((slavePtr-> geomInfo. side == LEFT) || (slavePtr-> geomInfo. side == RIGHT)) { curExpand = (cavityHeight - childHeight)/numExpand; if (curExpand < minExpand) { minExpand = curExpand; } } else { cavityHeight -= childHeight; if (slavePtr-> geomInfo. expand) { numExpand++; } } } curExpand = cavityHeight/numExpand; if (curExpand < minExpand) { minExpand = curExpand; } return (minExpand < 0) ? 0 : minExpand; } void Widget_pack_slaves( Handle self) { PWidget masterPtr, slavePtr; int cavityX, cavityY, cavityWidth, cavityHeight; /* These variables keep track of the * as-yet-unallocated space remaining in * the middle of the parent window. */ int frameX, frameY, frameWidth, frameHeight; /* These variables keep track of the frame * allocated to the current window. */ int x, y, width, height; /* These variables are used to hold the * actual geometry of the current window. */ int maxWidth, maxHeight, tmp; int borderX, borderY; Point size; if ( var-> stage > csNormal) return; /* * If the parent has no slaves anymore, then don't do anything * at all: just leave the parent's size as-is. */ if (!( masterPtr = ( PWidget) var-> packSlaves)) return; /* * Pass #1: scan all the slaves to figure out the total amount * of space needed. Two separate width and height values are * computed: * * width - Holds the sum of the widths (plus padding) of * all the slaves seen so far that were packed LEFT * or RIGHT. * height - Holds the sum of the heights (plus padding) of * all the slaves seen so far that were packed TOP * or BOTTOM. * * maxWidth - Gradually builds up the width needed by the master * to just barely satisfy all the slave's needs. For * each slave, the code computes the width needed for * all the slaves so far and updates maxWidth if the * new value is greater. * maxHeight - Same as maxWidth, except keeps height info. */ width = height = maxWidth = maxHeight = 0; for (slavePtr=masterPtr; slavePtr != NULL; slavePtr = ( PWidget) slavePtr-> geomInfo. next) { if ((slavePtr-> geomInfo. side == TOP) || (slavePtr-> geomInfo. side == BOTTOM)) { tmp = slave_width( slavePtr, width); if (tmp > maxWidth) maxWidth = tmp; height += slave_height(slavePtr,0); } else { tmp = slave_height(slavePtr, height); if (tmp > maxHeight) maxHeight = tmp; width += slave_width(slavePtr,0); } } if (width > maxWidth) { maxWidth = width; } if (height > maxHeight) { maxHeight = height; } /* * If the total amount of space needed in the parent window has * changed, and if we're propagating geometry information, then * notify the next geometry manager up and requeue ourselves to * start again after the parent has had a chance to * resize us. */ if ((((maxWidth != my-> get_geomWidth(self))) || (maxHeight != my-> get_geomHeight(self))) && is_opt( optPackPropagate)) { Point p, oldsize; p. x = maxWidth; p. y = maxHeight; oldsize = my-> get_size( self); my-> set_geomSize( self, p); size = my-> get_size( self); /* if size didn't change, that means, that no cmSize came, and thus the actual repacking of slaves never took place */ if ( oldsize. x != size. x || oldsize. y != size. y) return; } else { size = my-> get_size( self); } /* * Pass #2: scan the slaves a second time assigning * new sizes. The "cavity" variables keep track of the * unclaimed space in the cavity of the window; this * shrinks inward as we allocate windows around the * edges. The "frame" variables keep track of the space * allocated to the current window and its frame. The * current window is then placed somewhere inside the * frame, depending on anchor. */ cavityX = cavityY = x = y = 0; cavityWidth = size. x; cavityHeight = size. y; for ( slavePtr=masterPtr; slavePtr != NULL; slavePtr = ( PWidget) slavePtr-> geomInfo. next) { if ((slavePtr-> geomInfo. side == TOP) || (slavePtr-> geomInfo. side == BOTTOM)) { frameWidth = cavityWidth; frameHeight = slave_height(slavePtr,0); if (slavePtr-> geomInfo. expand) frameHeight += YExpansion(slavePtr, cavityHeight); cavityHeight -= frameHeight; if (cavityHeight < 0) { frameHeight += cavityHeight; cavityHeight = 0; } frameX = cavityX; if (slavePtr-> geomInfo. side == TOP) { frameY = cavityY; cavityY += frameHeight; } else { frameY = cavityY + cavityHeight; } } else { frameHeight = cavityHeight; frameWidth = slave_width(slavePtr,0); if (slavePtr-> geomInfo. expand) frameWidth += XExpansion(slavePtr, cavityWidth); cavityWidth -= frameWidth; if (cavityWidth < 0) { frameWidth += cavityWidth; cavityWidth = 0; } frameY = cavityY; if (slavePtr-> geomInfo. side == LEFT) { frameX = cavityX; cavityX += frameWidth; } else { frameX = cavityX + cavityWidth; } } /* * Now that we've got the size of the frame for the window, * compute the window's actual size and location using the * fill, padding, and frame factors. */ borderX = slavePtr-> geomInfo. pad.x; borderY = slavePtr-> geomInfo. pad.y; width = slavePtr-> geomSize. x + slavePtr-> geomInfo. ipad.x; if (slavePtr-> geomInfo. fillx || (width > (frameWidth - borderX))) width = frameWidth - borderX; height = slavePtr-> geomSize. y + slavePtr-> geomInfo. ipad.y; if (slavePtr-> geomInfo. filly || (height > (frameHeight - borderY))) height = frameHeight - borderY; borderX /= 2; borderY /= 2; if ( width < slavePtr-> sizeMin.x) width = slavePtr-> sizeMin.x; if ( height < slavePtr-> sizeMin.y) height = slavePtr-> sizeMin.y; if ( width > slavePtr-> sizeMax.x) width = slavePtr-> sizeMax.x; if ( height > slavePtr-> sizeMax.y) height = slavePtr-> sizeMax.y; switch (slavePtr-> geomInfo. anchorx) { case WEST: x = frameX + borderX; break; case CENTER: x = frameX + (frameWidth - width)/2; break; case EAST: x = frameX + frameWidth - width - borderX; break; } switch (slavePtr-> geomInfo. anchory) { case NORTH: y = frameY + borderY; break; case CENTER: y = frameY + (frameHeight - height)/2; break; case SOUTH: y = frameY + frameHeight - height - borderY; break; } { Rect r; r. left = x; r. bottom = size. y - y - height; r. right = x + width; r. top = size. y - y; /* printf("%s: %d %d %d %d\n", slavePtr-> name, x, r.bottom, width, r.top); */ slavePtr-> self-> set_rect(( Handle) slavePtr, r); } } } /* applies pack parameters and enters pack slaves chain */ void Widget_pack_enter( Handle self) { Handle master, ptr; /* see if leftover object references are alive */ if ( var-> geomInfo. order && !hash_fetch( primaObjects, &var-> geomInfo. order, sizeof(Handle))) { var-> geomInfo. order = nilHandle; var-> geomInfo. after = 0; } if ( var-> geomInfo. in) { if ( hash_fetch( primaObjects, &var-> geomInfo. in, sizeof(Handle))) var-> geomInfo. in = Widget_check_in( self, var-> geomInfo. in, false); else var-> geomInfo. in = nilHandle; } /* store into slaves list */ master = (( var-> geomInfo. in) ? var-> geomInfo. in : var-> owner); if ( PWidget( master)-> packSlaves) { /* insert into list using 'order' marker */ ptr = PWidget( master)-> packSlaves; if ( ptr != var-> geomInfo. order) { Handle optr = ptr; Bool inserted = false; while ( ptr) { if ( ptr == var-> geomInfo. order) { if ( var-> geomInfo. after) { var-> geomInfo. next = PWidget( ptr)-> geomInfo. next; PWidget( ptr)-> geomInfo. next = self; } else { var-> geomInfo. next = ptr; PWidget( optr)-> geomInfo. next = self; } inserted = true; break; } optr = ptr; ptr = PWidget( ptr)-> geomInfo. next; } if ( !inserted) PWidget( optr)-> geomInfo. next = self; } else { /* order is first in list */ if ( var-> geomInfo. after) { var-> geomInfo. next = PWidget( ptr)-> geomInfo. next; PWidget( ptr)-> geomInfo. next = self; } else { var-> geomInfo. next = ptr; PWidget( master)-> packSlaves = self; } } } else { /* master has no slaves, we're first */ PWidget( master)-> packSlaves = self; } } /* removes widget from list of pack slaves */ void Widget_pack_leave( Handle self) { Handle ptr, master; master = (( var-> geomInfo. in) ? var-> geomInfo. in : var-> owner); if ( master) { if (( ptr = PWidget( master)-> packSlaves) != self) { if ( ptr) { while ( PWidget(ptr)-> geomInfo. next) { if ( PWidget(ptr)-> geomInfo. next == self) { PWidget(ptr)-> geomInfo. next = var-> geomInfo. next; break; } ptr = PWidget(ptr)-> geomInfo. next; } } } else { PWidget( master)-> packSlaves = var-> geomInfo. next; } } var-> geomInfo. next = nilHandle; } SV * Widget_packInfo( Handle self, Bool set, SV * packInfo) { if ( !set) { HV * profile = newHV(); GeomInfo *p = &var-> geomInfo; switch ( p-> side) { case LEFT : pset_c( side, "top"); break; case BOTTOM : pset_c( side, "bottom"); break; case RIGHT : pset_c( side, "right"); break; case TOP : pset_c( side, "top"); break; } if ( p-> fillx) { pset_c( fill, p-> filly ? "both" : "x"); } else { pset_c( fill, p-> filly ? "y" : "none"); } pset_i( expand, p-> expand); switch ( p-> anchorx) { case WEST: pset_c( anchor, (( p-> anchory == NORTH) ? "nw" : (( p-> anchory == CENTER) ? "w" : "sw")) ); break; case CENTER: pset_c( anchor, (( p-> anchory == NORTH) ? "n" : (( p-> anchory == CENTER) ? "center" : "s")) ); break; case EAST: pset_c( anchor, (( p-> anchory == NORTH) ? "ne" : (( p-> anchory == CENTER) ? "e" : "se")) ); break; } pset_H( after, ( p-> order && p-> after) ? p-> order : nilHandle); pset_H( before, ( p-> order && !p-> after) ? p-> order : nilHandle); pset_H( in, var-> geomInfo. in); pset_i( ipadx, p-> ipad. x); pset_i( ipady, p-> ipad. y); pset_i( padx, p-> pad. x); pset_i( pady, p-> pad. y); return newRV_noinc(( SV *) profile); } else { HV * profile; Bool reset_zorder = false, set_in = false; Handle in = nilHandle; if ( SvTYPE(packInfo) == SVt_NULL) return nilSV; if ( !SvOK(packInfo) || !SvROK(packInfo) || SvTYPE(SvRV(packInfo)) != SVt_PVHV) croak("Widget::packInfo: parameter is not a hash"); profile = ( HV*) SvRV( packInfo); if ( pexist( side)) { char * c = pget_c( side); if ( *c == 'l' && (strcmp( c, "left")==0)) var-> geomInfo. side = LEFT; else if ( *c == 'b' && (strcmp( c, "bottom")==0)) var-> geomInfo. side = BOTTOM; else if ( *c == 'r' && (strcmp( c, "right")==0)) var-> geomInfo. side = RIGHT; else if ( *c == 't' && (strcmp( c, "top")==0)) var-> geomInfo. side = TOP; else croak("%s: invalid 'side'", "RTC008F: Prima::Widget::pack"); } if ( pexist( fill)) { char * c = pget_c( fill); if (( strcmp( c, "x") == 0)) { var-> geomInfo. fillx = 1; var-> geomInfo. filly = 0; } else if (( strcmp( c, "y") == 0)) { var-> geomInfo. fillx = 0; var-> geomInfo. filly = 1; } else if ( *c == 'n' && ( strcmp( c, "none") == 0)) { var-> geomInfo. fillx = var-> geomInfo. filly = 0; } else if ( *c == 'b' && ( strcmp( c, "both") == 0)) { var-> geomInfo. fillx = var-> geomInfo. filly = 1; } else croak("%s: invalid 'fill'", "RTC008F: Prima::Widget::pack"); } if ( pexist( expand)) { var-> geomInfo. expand = pget_B( expand); } if ( pexist( anchor)) { char * c = pget_c( anchor); if (( strcmp( c, "n") == 0)) { var-> geomInfo. anchorx = CENTER; var-> geomInfo. anchory = NORTH; } else if (( strcmp( c, "ne") == 0)) { var-> geomInfo. anchorx = EAST; var-> geomInfo. anchory = NORTH; } else if (( strcmp( c, "e") == 0)) { var-> geomInfo. anchorx = EAST; var-> geomInfo. anchory = CENTER; } else if (( strcmp( c, "se") == 0)) { var-> geomInfo. anchorx = EAST; var-> geomInfo. anchory = SOUTH; } else if (( strcmp( c, "s") == 0)) { var-> geomInfo. anchorx = CENTER; var-> geomInfo. anchory = SOUTH; } else if (( strcmp( c, "sw") == 0)) { var-> geomInfo. anchorx = WEST; var-> geomInfo. anchory = SOUTH; } else if (( strcmp( c, "w") == 0)) { var-> geomInfo. anchorx = WEST; var-> geomInfo. anchory = CENTER; } else if (( strcmp( c, "nw") == 0)) { var-> geomInfo. anchorx = WEST; var-> geomInfo. anchory = NORTH; } else if ( *c == 'c' && ( strcmp( c, "center") == 0)) { var-> geomInfo. anchorx = CENTER; var-> geomInfo. anchory = CENTER; } else croak("%s: invalid 'anchor'", "RTC008F: Prima::Widget::pack"); } if ( pexist( ipadx)) var-> geomInfo. ipad. x = pget_i( ipadx); if ( pexist( ipady)) var-> geomInfo. ipad. y = pget_i( ipady); if ( pexist( padx)) var-> geomInfo. pad. x = pget_i( padx); if ( pexist( pady)) var-> geomInfo. pad. y = pget_i( pady); if ( pexist( after)) { SV * sv = pget_sv( after); if ( SvTYPE(sv) != SVt_NULL) { if ( !( var-> geomInfo. order = gimme_the_mate( sv))) croak("%s: invalid 'after'", "RTC008F: Prima::Widget::pack"); var-> geomInfo. after = 1; if ( pexist( before)) { sv = pget_sv( before); if ( SvTYPE(sv) != SVt_NULL) croak("%s: 'after' and 'before' cannot be present simultaneously", "RTC008F: Prima::Widget::pack"); } } else { var-> geomInfo. order = nilHandle; var-> geomInfo. after = 0; } reset_zorder = true; } else if ( pexist( before)) { SV * sv = pget_sv( before); if ( SvTYPE(sv) != SVt_NULL) { if ( !( var-> geomInfo. order = gimme_the_mate( sv))) croak("%s: invalid 'before'", "RTC008F: Prima::Widget::pack"); } else var-> geomInfo. order = nilHandle; var-> geomInfo. after = 0; reset_zorder = true; } if ( pexist( in)) { SV * sv = pget_sv( in); in = nilHandle; if ( SvTYPE( sv) != SVt_NULL) in = Widget_check_in( self, gimme_the_mate( sv), true); set_in = reset_zorder = true; } if ( var-> geometry == gtPack) { if ( reset_zorder) Widget_pack_leave( self); } if ( set_in) var-> geomInfo. in = in; if ( var-> geometry == gtPack) { if ( reset_zorder) Widget_pack_enter( self); geometry_reset( MASTER, gtPack); } } return nilSV; } XS( Widget_get_pack_slaves_FROMPERL) { dXSARGS; Handle self; if ( items != 1) croak ("Invalid usage of Widget.get_pack_slaves"); SP -= items; self = gimme_the_mate( ST( 0)); if ( self == nilHandle) croak( "Illegal object reference passed to Widget.get_pack_slaves"); self = var-> packSlaves; while ( self) { XPUSHs( sv_2mortal( newSVsv((( PAnyObject) self)-> mate))); self = var-> geomInfo. next; } PUTBACK; return; } void Widget_get_pack_slaves ( Handle self) { warn("Invalid call of Widget::get_pack_slaves"); } void Widget_get_pack_slaves_REDEFINED( Handle self) { warn("Invalid call of Widget::get_pack_slaves"); } /* PLACE */ /* place internal mechanism - stolen from Tk v800.24, tkPlace.c */ void Widget_place_enter( Handle self) { Handle master, ptr; /* see if leftover object references are alive */ if ( var-> geomInfo. in) { if ( hash_fetch( primaObjects, &var-> geomInfo. in, sizeof(Handle))) var-> geomInfo. in = Widget_check_in( self, var-> geomInfo. in, false); else var-> geomInfo. in = nilHandle; } /* store into slaves list */ master = (( var-> geomInfo. in) ? var-> geomInfo. in : var-> owner); if ( PWidget( master)-> placeSlaves) { /* append to the end of list */ if (( ptr = PWidget( master)-> placeSlaves)) { while ( PWidget( ptr)-> geomInfo. next) ptr = PWidget( ptr)-> geomInfo. next; PWidget( ptr)-> geomInfo. next = self; } else { /* first in list */ var-> geomInfo. next = ptr; PWidget( master)-> placeSlaves = self; } } else { /* master has no slaves, we're first */ PWidget( master)-> placeSlaves = self; } } /* removes widget from list of place slaves */ void Widget_place_leave( Handle self) { Handle ptr, master; master = (( var-> geomInfo. in) ? var-> geomInfo. in : var-> owner); if ( master) { if (( ptr = PWidget( master)-> placeSlaves) != self) { if ( ptr) { while ( PWidget(ptr)-> geomInfo. next) { if ( PWidget(ptr)-> geomInfo. next == self) { PWidget(ptr)-> geomInfo. next = var-> geomInfo. next; break; } ptr = PWidget(ptr)-> geomInfo. next; } } } else { PWidget( master)-> placeSlaves = var-> geomInfo. next; } } var-> geomInfo. next = nilHandle; } void Widget_place_slaves( Handle self) { PWidget slave, master; int x, y, width, height, tmp; int masterWidth, masterHeight; double x1, y1, x2, y2; Point size; /* * Iterate over all the slaves for the master. Each slave's * geometry can be computed independently of the other slaves. */ if (!( master = ( PWidget) var-> placeSlaves)) return; size = my-> get_size( self); masterWidth = size. x; masterHeight = size. y; for (slave=master; slave != NULL; slave = ( PWidget) slave-> geomInfo. next) { Point sz; register GeomInfo* slavePtr = &slave-> geomInfo; sz = slave-> self-> get_size(( Handle) slave); /* * Step 2: compute size of slave (outside dimensions including * border) and location of anchor point within master. */ x1 = slavePtr->x + (slavePtr->relX*masterWidth); x = (int) (x1 + ((x1 > 0) ? 0.5 : -0.5)); y1 = slavePtr->y + (slavePtr->relY*masterHeight); y = (int) (y1 + ((y1 > 0) ? 0.5 : -0.5)); if (slavePtr-> use_w || slavePtr-> use_rw) { width = 0; if (slavePtr-> use_w) { width += slave->geomSize.x; } if (slavePtr-> use_rw) { /* * The code below is a bit tricky. In order to round * correctly when both relX and relWidth are specified, * compute the location of the right edge and round that, * then compute width. If we compute the width and round * it, rounding errors in relX and relWidth accumulate. */ x2 = x1 + (slavePtr->relWidth*masterWidth); tmp = (int) (x2 + ((x2 > 0) ? 0.5 : -0.5)); width += tmp - x; } } else { width = sz. x; } if (slavePtr-> use_h || slavePtr-> use_rh) { height = 0; if (slavePtr->use_h) { height += slave->geomSize. y; } if (slavePtr->use_rh) { /* * See note above for rounding errors in width computation. */ y2 = y1 + (slavePtr->relHeight*masterHeight); tmp = (int) (y2 + ((y2 > 0) ? 0.5 : -0.5)); height += tmp - y; } } else { height = sz. y; } /* * Step 3: adjust the x and y positions so that the desired * anchor point on the slave appears at that position. Also * adjust for the border mode and master's border. */ switch (slavePtr-> anchorx) { case WEST: break; case CENTER: x -= width/2; break; case EAST: x -= width; break; } switch (slavePtr-> anchory) { case NORTH: break; case CENTER: y -= height/2; break; case SOUTH: y -= height; break; } { Rect r; r. left = x; r. bottom = size. y - y - height; r. right = x + width; r. top = size. y - y; /* printf("%s: %d %d %d %d\n", slave-> name, x, y, width, height); */ slave-> self-> set_rect(( Handle) slave, r); } } } SV * Widget_placeInfo( Handle self, Bool set, SV * placeInfo) { if ( !set) { HV * profile = newHV(); GeomInfo *p = &var-> geomInfo; switch ( p-> anchorx) { case WEST: pset_c( anchor, (( p-> anchory == NORTH) ? "nw" : (( p-> anchory == CENTER) ? "w" : "sw")) ); break; case CENTER: pset_c( anchor, (( p-> anchory == NORTH) ? "n" : (( p-> anchory == CENTER) ? "center" : "s")) ); break; case EAST: pset_c( anchor, (( p-> anchory == NORTH) ? "ne" : (( p-> anchory == CENTER) ? "e" : "se")) ); break; } pset_H( in, var-> geomInfo. in); if ( p-> use_x) pset_i( x, p-> x); if ( p-> use_y) pset_i( y, p-> y); if ( p-> use_w) pset_i( width, var-> geomSize. x); if ( p-> use_h) pset_i( height, var-> geomSize. y); if ( p-> use_rx) pset_f( relx, p-> relX); if ( p-> use_ry) pset_f( rely, p-> relY); if ( p-> use_rw) pset_f( relwidth, p-> relWidth); if ( p-> use_rh) pset_f( relheight, p-> relHeight); return newRV_noinc(( SV *) profile); } else { HV * profile; Handle in = nilHandle; Bool set_in = false; if ( SvTYPE(placeInfo) == SVt_NULL) return nilSV; if ( !SvOK(placeInfo) || !SvROK(placeInfo) || SvTYPE(SvRV(placeInfo)) != SVt_PVHV) croak("Widget::placeInfo: parameter is not a hash"); profile = ( HV*) SvRV( placeInfo); if ( pexist( anchor)) { char * c = pget_c( anchor); if (( strcmp( c, "n") == 0)) { var-> geomInfo. anchorx = CENTER; var-> geomInfo. anchory = NORTH; } else if (( strcmp( c, "ne") == 0)) { var-> geomInfo. anchorx = EAST; var-> geomInfo. anchory = NORTH; } else if (( strcmp( c, "e") == 0)) { var-> geomInfo. anchorx = EAST; var-> geomInfo. anchory = CENTER; } else if (( strcmp( c, "se") == 0)) { var-> geomInfo. anchorx = EAST; var-> geomInfo. anchory = SOUTH; } else if (( strcmp( c, "s") == 0)) { var-> geomInfo. anchorx = CENTER; var-> geomInfo. anchory = SOUTH; } else if (( strcmp( c, "sw") == 0)) { var-> geomInfo. anchorx = WEST; var-> geomInfo. anchory = SOUTH; } else if (( strcmp( c, "w") == 0)) { var-> geomInfo. anchorx = WEST; var-> geomInfo. anchory = CENTER; } else if (( strcmp( c, "nw") == 0)) { var-> geomInfo. anchorx = WEST; var-> geomInfo. anchory = NORTH; } else if ( *c == 'c' && ( strcmp( c, "center") == 0)) { var-> geomInfo. anchorx = CENTER; var-> geomInfo. anchory = CENTER; } else croak("%s: invalid 'anchor'", "RTC008F: Prima::Widget::place"); } if ( pexist( x)) { SV * sv = pget_sv( x); if (( var-> geomInfo. use_x = (SvTYPE( sv) != SVt_NULL))) var-> geomInfo. x = SvIV( sv); } if ( pexist( y)) { SV * sv = pget_sv( y); if (( var-> geomInfo. use_y = (SvTYPE( sv) != SVt_NULL))) var-> geomInfo. y = SvIV( sv); } if ( pexist( width)) { SV * sv = pget_sv( width); if (( var-> geomInfo. use_w = (SvTYPE( sv) != SVt_NULL))) var-> geomSize. x = SvIV( sv); } if ( pexist( height)) { SV * sv = pget_sv( height); if (( var-> geomInfo. use_h = (SvTYPE( sv) != SVt_NULL))) var-> geomSize. y = SvIV( sv); } if ( pexist( relx)) { SV * sv = pget_sv( relx); if (( var-> geomInfo. use_rx = (SvTYPE( sv) != SVt_NULL))) var-> geomInfo. relX = SvNV( sv); } if ( pexist( rely)) { SV * sv = pget_sv( rely); if (( var-> geomInfo. use_ry = (SvTYPE( sv) != SVt_NULL))) var-> geomInfo. relY = SvNV( sv); } if ( pexist( relwidth)) { SV * sv = pget_sv( relwidth); if (( var-> geomInfo. use_rw = (SvTYPE( sv) != SVt_NULL))) var-> geomInfo. relWidth = SvNV( sv); } if ( pexist( relheight)) { SV * sv = pget_sv( relheight); if (( var-> geomInfo. use_rh = (SvTYPE( sv) != SVt_NULL))) var-> geomInfo. relHeight = SvNV( sv); } if ( pexist( in)) { SV * sv = pget_sv( in); in = nilHandle; if ( SvTYPE( sv) != SVt_NULL) in = Widget_check_in( self, gimme_the_mate( sv), true); set_in = true; } if ( var-> geometry == gtPlace) { if ( set_in) Widget_place_leave( self); } if ( set_in) var-> geomInfo. in = in; if ( var-> geometry == gtPlace) { if ( set_in) Widget_place_enter( self); geometry_reset( MASTER, gtPlace); } } return nilSV; } XS( Widget_get_place_slaves_FROMPERL) { dXSARGS; int i; Handle self; if ( items != 1) croak ("Invalid usage of Widget.get_pack_slaves"); SP -= items; self = gimme_the_mate( ST( 0)); if ( self == nilHandle) croak( "Illegal object reference passed to Widget.get_pack_slaves"); for ( i = 0; i < var-> widgets. count; i++) { if ( PWidget( var-> widgets. items[i])-> geometry == gtPlace) XPUSHs( sv_2mortal( newSVsv((( PAnyObject)(var-> widgets. items[i]))-> mate))); } PUTBACK; return; } void Widget_get_place_slaves ( Handle self) { warn("Invalid call of Widget::get_place_slaves"); } void Widget_get_place_slaves_REDEFINED( Handle self) { warn("Invalid call of Widget::get_place_slaves"); } /* */ #ifdef __cplusplus } #endif