/* $Id: tixForm.c,v 1.1.1.1 2000/05/17 11:08:41 idiscovery Exp $ */
/*
* tixForm.c --
*
* Implements the tixForm geometry manager, which has similar
* capability as the Motif Form geometry manager. Please
* refer to the documentation for the use of tixForm.
*
* This file implements the basic algorithm of tixForm.
*
* Copyright (c) 1996, Expert Interface Technologies
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
*/
/*
*
* ToDo:
*
* (1) Delete the master structure when there is no more client to manage
*
* Possible bugs:
* (1) a client is deleted but the master doesn't know
* (clientPtr->tkwin == NULL)
* (2) Whan a client S is deleted or detached from the master, all other
* clients attached to S must delete their reference to S
*/
#define DEBUG 0
#if DEBUG
#define NEED_REAL_STDIO
#endif
#include <tkInt.h>
#include "tixPort.h"
#include "tix.h"
#include "tixForm.h"
typedef struct SpringLink {
struct SpringLink * next;
FormInfo *clientPtr;
} SpringLink;
typedef struct SpringList {
SpringLink * head, * tail;
int num;
} SpringList;
/*
* SubCommands of the tixForm command.
*/
static TIX_DECLARE_SUBCMD(TixFm_SetGrid);
static TIX_DECLARE_SUBCMD(TixFm_SetClient);
static TIX_DECLARE_SUBCMD(TixFm_Check);
static TIX_DECLARE_SUBCMD(TixFm_Forget);
EXTERN TIX_DECLARE_SUBCMD(TixFm_Info);
static TIX_DECLARE_SUBCMD(TixFm_Slaves);
static TIX_DECLARE_SUBCMD(TixFm_Spring);
static void ArrangeGeometry _ANSI_ARGS_((ClientData clientData));
static void ArrangeWhenIdle _ANSI_ARGS_((MasterInfo * masterPtr));
static void CancelArrangeWhenIdle _ANSI_ARGS_((
MasterInfo * masterPtr));
static void CalculateMasterSize _ANSI_ARGS_((MasterInfo *master));
static void CheckIntergrity _ANSI_ARGS_((FormInfo * clientPtr));
static MasterInfo * GetMasterInfo _ANSI_ARGS_((Tk_Window tkwin,
int create));
static void MasterStructureProc _ANSI_ARGS_((ClientData clientData,
XEvent * eventPtr));
static int PinnClient _ANSI_ARGS_((FormInfo *clientPtr));
static int PinnClientSide _ANSI_ARGS_((FormInfo *clientPtr,
int axis, int which, int isSelf));
static int PlaceClientSide _ANSI_ARGS_((FormInfo *clientPtr,
int axis, int which, int isSelf));
static int TestAndArrange _ANSI_ARGS_((MasterInfo *masterPtr));
static int TixFm_CheckArgv _ANSI_ARGS_((ClientData clientData,
Tcl_Interp *interp, int argc, char ** argv));
static void TixFm_LostSlaveProc _ANSI_ARGS_((
ClientData clientData, Tk_Window tkwin));
static void TixFm_ReqProc _ANSI_ARGS_((ClientData clientData,
Tk_Window tkwin));
static int PlaceAllClients _ANSI_ARGS_((MasterInfo *masterPtr));
static int PlaceClient _ANSI_ARGS_((FormInfo *clientPtr));
static int PlaceSide_AttOpposite _ANSI_ARGS_((
FormInfo *clientPtr, int axis, int which));
static int PlaceSide_AttAbsolute _ANSI_ARGS_((
FormInfo *clientPtr, int axis, int which));
static int PlaceSide_AttNone _ANSI_ARGS_((
FormInfo *clientPtr, int axis, int which));
static int PlaceSide_AttParallel _ANSI_ARGS_((FormInfo *clientPtr,
int axis, int which));
static int PlaceSimpleCase _ANSI_ARGS_((
FormInfo *clientPtr, int axis, int which));
static int PlaceWithSpring _ANSI_ARGS_((
FormInfo *clientPtr, int axis, int which));
static int ReqSize _ANSI_ARGS_((Tk_Window tkwin,
int axis));
static void UnmapClient _ANSI_ARGS_((FormInfo *clientPtr));
static void MapClient _ANSI_ARGS_((FormInfo *clientPtr,
int x, int y, int width, int height));
static int PinnSide_AttNone _ANSI_ARGS_((FormInfo *clientPtr,
int axis, int which));
static int PinnSide_AttPercent _ANSI_ARGS_((FormInfo *clientPtr,
int axis, int which));
static int PinnSide_AttOpposite _ANSI_ARGS_((FormInfo *clientPtr,
int axis, int which));
static int PinnSide_AttParallel _ANSI_ARGS_((FormInfo *clientPtr,
int axis, int which));
static SpringLink * AllocSpringLink _ANSI_ARGS_((void));
static void FreeSpringLink _ANSI_ARGS_((SpringLink * link));
static void FreeSpringList _ANSI_ARGS_((SpringList * listPtr));
static void AddRightSprings _ANSI_ARGS_((SpringList * listPtr,
FormInfo *clientPtr));
static void AddLeftSprings _ANSI_ARGS_((SpringList * listPtr,
FormInfo *clientPtr));
/*
* A macro used to simplify the "pinn client" code
*/
#define PINN_CLIENT_SIDE(client, axis, which, isSelf) \
if (PinnClientSide(client, axis, which, isSelf) == TCL_ERROR) { \
return TCL_ERROR; \
}
/*
* A macro used to simplify the "place client" code
*/
#define PLACE_CLIENT_SIDE(client, axis, which, isSelf) \
if (PlaceClientSide(client, axis, which, isSelf) == TCL_ERROR) { \
return TCL_ERROR; \
}
/*
* Information about the Form geometry manager.
*/
static Tk_GeomMgr formType = {
"tixForm", /* name */
TixFm_ReqProc, /* requestProc */
TixFm_LostSlaveProc, /* lostSlaveProc */
};
/*
* Hash table used to map from Tk_Window tokens to corresponding
* FormInfo structures:
*/
static Tcl_HashTable formInfoHashTable;
static Tcl_HashTable masterInfoHashTable;
/*
* Have static variables in this module been initialized?
*/
static int initialized = 0;
static int ReqSize(tkwin, axis)
Tk_Window tkwin;
int axis;
{
if (axis == AXIS_X) {
return Tk_ReqWidth(tkwin);
} else {
return Tk_ReqHeight(tkwin);
}
}
/*
*--------------------------------------------------------------
*
* Tix_FormCmd --
*
* This procedure is invoked to process the "tixForm" Tcl command.
* See the user documentation for details on what it does.
*
* Results:
* A standard Tcl result.
*
* Side effects:
* See the user documentation.
*
*--------------------------------------------------------------
*/
int
Tix_FormCmd(clientData, interp, argc, argv)
ClientData clientData; /* Main window associated with
* interpreter. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
char **argv; /* Argument strings. */
{
static Tix_SubCmdInfo subCmdInfo[] = {
{TIX_DEFAULT_LEN, "check", 1, 1, TixFm_Check,
"master",},
{TIX_DEFAULT_LEN, "configure", 1, TIX_VAR_ARGS, TixFm_SetClient,
"slave ?-flag value ...?",},
{TIX_DEFAULT_LEN, "forget", 1, TIX_VAR_ARGS, TixFm_Forget,
"slave ?slave ...?",},
{TIX_DEFAULT_LEN, "grid", 1, TIX_VAR_ARGS, TixFm_SetGrid,
"master ?x_grids y_grids?"},
{TIX_DEFAULT_LEN, "info", 1, 2, TixFm_Info,
"slave ?-flag?",},
{TIX_DEFAULT_LEN, "slaves", 1, 1, TixFm_Slaves,
"master",},
{TIX_DEFAULT_LEN, "spring", 3, 3, TixFm_Spring,
"slave side strength",},
{TIX_DEFAULT_LEN, TIX_DEFAULT_SUBCMD, 0, 0, TixFm_SetClient, 0,
TixFm_CheckArgv,}
};
static Tix_CmdInfo cmdInfo = {
Tix_ArraySize(subCmdInfo), 1, TIX_VAR_ARGS, "?option? arg ?arg ...?",
};
return Tix_HandleSubCmds(&cmdInfo, subCmdInfo, clientData,
interp, argc, argv);
}
/*----------------------------------------------------------------------
*
* TixFm_SetGrid --
*
* Sets some defaults for the master window
*
*----------------------------------------------------------------------
*/
static int TixFm_SetGrid(clientData, interp, argc, argv)
ClientData clientData; /* Main window associated with
* interpreter. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
char **argv; /* Argument strings. */
{
char buff[100];
Tk_Window topLevel = (Tk_Window) clientData;
Tk_Window master;
MasterInfo* masterPtr;
master = Tk_NameToWindow(interp, argv[0], topLevel);
if (master == NULL) {
return TCL_ERROR;
} else {
masterPtr = GetMasterInfo(master, 1);
}
if (argc != 1 && argc != 3) {
Tcl_AppendResult(interp, "Wrong # of arguments, should be ",
"tixForm grid master ?x_grids y_grids?", NULL);
return TCL_ERROR;
}
if (argc == 1) {
Tcl_IntResults(interp, 2, 0, masterPtr->grids[0], masterPtr->grids[1]);
}
else {
int x, y;
if (Tcl_GetIntFromObj(interp, argv[1], &x) != TCL_OK) {
return TCL_ERROR;
}
if (Tcl_GetIntFromObj(interp, argv[2], &y) != TCL_OK) {
return TCL_ERROR;
}
if (x <=0 || y <=0) {
Tcl_AppendResult(interp, "Grid sizes must be positive integers",
NULL);
return TCL_ERROR;
}
masterPtr->grids[0] = x;
masterPtr->grids[1] = y;
ArrangeWhenIdle(masterPtr);
}
return TCL_OK;
}
/*----------------------------------------------------------------------
*
* TixFm_Forget --
*
* Sets some defaults for the master window
*
*----------------------------------------------------------------------
*/
static int TixFm_Forget(clientData, interp, argc, argv)
ClientData clientData; /* Main window associated with
* interpreter. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
char **argv; /* Argument strings. */
{
FormInfo * clientPtr;
Tk_Window topLevel = (Tk_Window) clientData;
int i;
for (i=0; i<argc; i++) {
clientPtr = TixFm_FindClientPtrByName(interp, argv[i], topLevel);
if (clientPtr == NULL) {
return TCL_ERROR;
}
else {
TixFm_ForgetOneClient(clientPtr);
}
}
return TCL_OK;
}
void TixFm_ForgetOneClient(clientPtr)
FormInfo * clientPtr;
{
if (clientPtr != NULL) {
Tk_DeleteEventHandler(clientPtr->tkwin, StructureNotifyMask,
TixFm_StructureProc, (ClientData) clientPtr);
Tk_ManageGeometry(clientPtr->tkwin, (Tk_GeomMgr *) NULL,
(ClientData) NULL);
if (clientPtr->master->tkwin != Tk_Parent(clientPtr->tkwin)) {
Tk_UnmaintainGeometry(clientPtr->tkwin,
clientPtr->master->tkwin);
}
Tk_UnmapWindow(clientPtr->tkwin);
TixFm_Unlink(clientPtr);
}
}
/*----------------------------------------------------------------------
*
* TixFm_Slaves --
*
* retuen the pathnames of the clients of a master window
*
*----------------------------------------------------------------------
*/
static int TixFm_Slaves(clientData, interp, argc, argv)
ClientData clientData; /* Main window associated with
* interpreter. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
char **argv; /* Argument strings. */
{
Tk_Window topLevel = (Tk_Window) clientData;
Tk_Window master;
MasterInfo* masterPtr;
FormInfo * clientPtr;
master = Tk_NameToWindow(interp, argv[0], topLevel);
if (master == NULL) {
return TCL_ERROR;
} else {
masterPtr = GetMasterInfo(master, 0);
}
if (masterPtr == 0) {
Tcl_AppendResult(interp, "Window \"", argv[0],
"\" is not a tixForm master window", NULL);
return TCL_ERROR;
}
for (clientPtr = masterPtr->client; clientPtr; clientPtr=clientPtr->next) {
/*Tcl_AppendElement(interp, Tk_PathName(clientPtr->tkwin));*/
Tcl_ListObjAppendElement(interp,Tcl_GetObjResult(interp),
LangWidgetObj(interp,clientPtr->tkwin));
}
return TCL_OK;
}
/*----------------------------------------------------------------------
*
* TixFm_Spring --
*
* Sets the spring strength of a slave's attachment sides
*
*----------------------------------------------------------------------
*/
static int TixFm_Spring(clientData, interp, argc, argv)
ClientData clientData; /* Main window associated with
* interpreter. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
char **argv; /* Argument strings. */
{
Tk_Window topLevel = (Tk_Window) clientData;
Tk_Window tkwin;
FormInfo * clientPtr;
int strength;
int i, j;
size_t len;
if ((tkwin = Tk_NameToWindow(interp, argv[0], topLevel)) == NULL) {
return TCL_ERROR;
}
if ((clientPtr = TixFm_GetFormInfo(tkwin, 0)) == NULL) {
Tcl_AppendResult(interp, "Window \"", argv[0],
"\" is not managed by the tixForm manager", NULL);
return TCL_ERROR;
}
if (Tcl_GetIntFromObj(interp, argv[2], &strength) != TCL_OK) {
return TCL_ERROR;
}
len = strlen(argv[1]);
if (strncmp(argv[1], "-top", len) == 0) {
i = 1; j = 0;
}
else if (strncmp(argv[1], "-bottom", len) == 0) {
i = 1; j = 1;
}
else if (strncmp(argv[1], "-left", len) == 0) {
i = 0; j = 0;
}
else if (strncmp(argv[1], "-right", len) == 0) {
i = 0; j = 1;
}
else {
Tcl_AppendResult(interp, "Unknown option \"", argv[1],
"\"", NULL);
return TCL_ERROR;
}
clientPtr->spring[i][j] = strength;
if (clientPtr->attType[i][j] == ATT_OPPOSITE) {
FormInfo * oppo;
oppo = clientPtr->att[i][j].widget;
oppo->spring[i][!j] = strength;
if (strength != 0 && clientPtr->strWidget[i][j] == NULL) {
clientPtr->strWidget[i][j] = oppo;
if (oppo->strWidget[i][!j] != clientPtr) {
if (oppo->strWidget[i][!j] != NULL) {
oppo->strWidget[i][!j]->strWidget[i][j] = NULL;
oppo->strWidget[i][!j]->spring[i][j] = 0;
}
}
oppo->strWidget[i][!j] = clientPtr;
}
}
ArrangeWhenIdle(clientPtr->master);
return TCL_OK;
}
/*----------------------------------------------------------------------
*
* TixFm_Check --
*
* Tests whether the master has circular reference.
*
*----------------------------------------------------------------------
*/
static int TixFm_Check(clientData, interp, argc, argv)
ClientData clientData; /* Main window associated with
* interpreter. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
char **argv; /* Argument strings. */
{
MasterInfo * masterPtr;
Tk_Window topLevel = (Tk_Window) clientData;
Tk_Window master;
master = Tk_NameToWindow(interp, argv[0], topLevel);
if (master == NULL) {
return TCL_ERROR;
}
masterPtr = GetMasterInfo(master, 1);
if (TestAndArrange(masterPtr) == TCL_OK) {
/* OK: no circular dependency */
Tcl_AppendResult(interp, "0", NULL);
} else {
/* Bad: circular dependency */
Tcl_AppendResult(interp, "1", NULL);
}
return TCL_OK;
}
/* Check the arguments to the default subcommand: TixFm_SetClient()
*/
static int TixFm_CheckArgv(clientData, interp, argc, argv)
ClientData clientData; /* Main window associated with
* interpreter. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
char **argv; /* Argument strings. */
{
if ((argc >=1) && (argv[0][0] != '.')) {
return 0; /* sorry, we expect a window name */
} else {
return 1;
}
}
static int TixFm_SetClient(clientData, interp, argc, argv)
ClientData clientData; /* Main window associated with
* interpreter. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
char **argv; /* Argument strings. */
{
Tk_Window topLevel = (Tk_Window) clientData;
Tk_Window client, master;
FormInfo * clientPtr;
MasterInfo * masterPtr;
char *pathName; /* path name of the client window */
if (argc < 1 || (((argc-1) %2) != 0)) {
Tcl_AppendResult(interp, "Wrong # of arguments, should be ",
"tixForm configure slave ?-flag value ...?", NULL);
return TCL_ERROR;
}
pathName = argv[0];
argc -=1;
argv +=1;
client = Tk_NameToWindow(interp, pathName, topLevel);
if (client == NULL) {
return TCL_ERROR;
} else if (Tk_IsTopLevel(client)) {
Tcl_AppendResult(interp, "can't put \"", pathName,
"\"in a form: it's a top-level window", (char *) NULL);
return TCL_ERROR;
} else {
clientPtr = TixFm_GetFormInfo(client, 1);
}
/* Check if the first argument is "-in". If so,
* reset the master of this client
*/
if (argc >= 2 && strcmp(argv[0], "-in")==0) {
if ((master=Tk_NameToWindow(interp, argv[1], topLevel)) == NULL) {
return TCL_ERROR;
}
argc -= 2;
argv += 2;
masterPtr = GetMasterInfo(master, 1);
}
else if (clientPtr->master == NULL) {
if ((master = Tk_Parent(client))==NULL) {
return TCL_ERROR;
}
masterPtr = GetMasterInfo(master, 1);
}
else {
masterPtr =clientPtr->master;
}
if (clientPtr->master != masterPtr) {
if (clientPtr->master != NULL) {
/* Take clientPtr from old master */
Tk_ManageGeometry(clientPtr->tkwin, (Tk_GeomMgr *) NULL,
(ClientData) NULL);
if (clientPtr->master->tkwin != Tk_Parent(clientPtr->tkwin)) {
Tk_UnmaintainGeometry(clientPtr->tkwin,
clientPtr->master->tkwin);
}
TixFm_UnlinkFromMaster(clientPtr);
}
/* attach the client to the master */
TixFm_AddToMaster(masterPtr, clientPtr);
}
if (argc > 0) {
if (TixFm_Configure(clientPtr, topLevel, interp, argc,
argv)==TCL_ERROR){
return TCL_ERROR;
}
}
ArrangeWhenIdle(clientPtr->master);
return TCL_OK;
}
/* The caller of this function needs to find out a pointer to a client
* that is already managed by tixForm.
*/
FormInfo * TixFm_FindClientPtrByName(interp, name, topLevel)
Tcl_Interp * interp;
char * name;
Tk_Window topLevel;
{
Tk_Window tkwin;
FormInfo * clientPtr;
if ((tkwin = Tk_NameToWindow(interp, name, topLevel)) == NULL) {
return NULL;
}
if ((clientPtr = TixFm_GetFormInfo(tkwin, 0)) == NULL) {
Tcl_AppendResult(interp, "Window \"", name,
"\" is not managed by the tixForm manager", NULL);
return NULL;
}
return clientPtr;
}
static int TestAndArrange(masterPtr)
MasterInfo *masterPtr;
{
FormInfo *clientPtr;
int i,j;
/*
* First mark all clients as unpinned, and clean the opposite flags,
* Check the attachment intergrity
*/
for (clientPtr = masterPtr->client; clientPtr; clientPtr=clientPtr->next) {
if (clientPtr->tkwin != NULL) {
for (i=0; i<2; i++) {
for (j=0; j<2; j++) {
clientPtr->side[i][j].pcnt = 0;
clientPtr->side[i][j].disp = 0;
}
/* clear all flags */
clientPtr->sideFlags[i] = 0;
}
clientPtr->depend = 0;
CheckIntergrity(clientPtr);
}
}
/*
* Try to determine all the client's geometry
*/
for (clientPtr = masterPtr->client; clientPtr; clientPtr=clientPtr->next) {
if (clientPtr->tkwin == NULL) { /* it was deleted */
continue;
}
for (i=0; i<2; i++) {
if ((clientPtr->sideFlags[i] & PINNED_ALL) != PINNED_ALL) {
if (PinnClient(clientPtr) == TCL_ERROR) {
/*
* Detected circular dependency
*/
return TCL_ERROR;
}
break;
}
}
}
return TCL_OK;
}
/*----------------------------------------------------------------------
* UnmapClient
*
* Unmap the client from the screen, using different methods according to
* the relationship between the client and slave.
*/
static void UnmapClient(clientPtr)
FormInfo *clientPtr;
{
if (clientPtr->master->tkwin == Tk_Parent(clientPtr->tkwin)) {
Tk_UnmapWindow(clientPtr->tkwin);
}
else {
Tk_UnmaintainGeometry(clientPtr->tkwin, clientPtr->master->tkwin);
Tk_UnmapWindow(clientPtr->tkwin);
}
}
/*----------------------------------------------------------------------
* MapClient
*
* Map the client to the screen, using different methods according to
* the relationship between the client and slave.
*/
static void MapClient(clientPtr, x, y, width, height)
FormInfo *clientPtr;
int x;
int y;
int width;
int height;
{
if (clientPtr->master->tkwin == Tk_Parent(clientPtr->tkwin)) {
Tk_MoveResizeWindow(clientPtr->tkwin, x, y, width, height);
Tk_MapWindow(clientPtr->tkwin);
}
else {
Tk_MaintainGeometry(clientPtr->tkwin, clientPtr->master->tkwin,
x, y, width, height);
Tk_MapWindow(clientPtr->tkwin);
}
}
static void ArrangeWhenIdle(masterPtr)
MasterInfo * masterPtr;
{
if (!(masterPtr->flags.repackPending || masterPtr->flags.isDeleted)) {
masterPtr->flags.repackPending = 1;
Tcl_DoWhenIdle(ArrangeGeometry, (ClientData) masterPtr);
}
}
static void
CancelArrangeWhenIdle(masterPtr)
MasterInfo * masterPtr;
{
if (masterPtr->flags.repackPending) {
Tcl_CancelIdleCall(ArrangeGeometry, (ClientData) masterPtr);
masterPtr->flags.repackPending = 0;
}
}
/*----------------------------------------------------------------------
* ArrangeGeometry --
*
* The heart of the Form geometry manager: calculates the sizes of
* the clients and the master, then arrange the clients inside the
* master according to their attachments.
*/
static void ArrangeGeometry(clientData)
ClientData clientData; /* Structure describing parent whose clients
* are to be re-layed out. */
{
MasterInfo *masterPtr;
FormInfo *clientPtr;
int i, j, coord[2][2];
int mSize[2]; /* Size of master */
int cSize[2]; /* Size of client */
int intBWidth; /* internal borderWidth of master */
TkWindow* winPtr;
masterPtr = (MasterInfo *) clientData;
winPtr = (TkWindow*)masterPtr->tkwin;
if (winPtr->flags & TK_ALREADY_DEAD) {
masterPtr->flags.repackPending = 0;
return;
}
if (masterPtr->flags.isDeleted) {
return;
}
if (masterPtr->numClients == 0) {
masterPtr->flags.repackPending = 0;
return;
}
if (TestAndArrange(masterPtr)) { /* Detected circular dependency */
#if DEBUG
fprintf(stderr, "circular dependency.\n");
#endif
masterPtr->flags.repackPending = 0;
return;
}
/*
* Try to determine the required size of the master
*/
CalculateMasterSize(masterPtr);
/*
* If the requested size is not equal to the actual size of the master,
* we might have to ask TK to change the master's geometry
*/
if ((masterPtr->reqSize[0] != Tk_ReqWidth(masterPtr->tkwin))
|| (masterPtr->reqSize[1] != Tk_ReqHeight(masterPtr->tkwin))) {
if (masterPtr->numRequests++ > 50) {
#if DEBUG
fprintf(stderr,
"(TixForm) Error:Trying to use more than one geometry\n\
manager for the same master window.\n\
Giving up after 50 iterations.\n");
#endif
} else {
masterPtr->flags.repackPending = 0;
Tk_GeometryRequest(masterPtr->tkwin,
masterPtr->reqSize[0], masterPtr->reqSize[1]);
ArrangeWhenIdle(masterPtr);
return;
}
}
masterPtr->numRequests = 0;
if (!Tk_IsMapped(masterPtr->tkwin)) {
goto done;
}
intBWidth = Tk_InternalBorderWidth(masterPtr->tkwin);
mSize[0] = Tk_Width(masterPtr->tkwin) - 2*intBWidth;
mSize[1] = Tk_Height(masterPtr->tkwin) - 2*intBWidth;
if (mSize[0] < 1 || mSize[1] <1) {
/* Master is not visible. Don't bother to place the clients
*/
masterPtr->flags.repackPending = 0;
return;
}
/*
* Now set all the client's geometry
*/
if (PlaceAllClients(masterPtr) != TCL_OK) {
panic("circular dependency");
}
for (clientPtr = masterPtr->client; clientPtr; clientPtr=clientPtr->next) {
if (clientPtr->tkwin == NULL) {
continue;
}
for (i=0; i<2; i++) {
for (j=0; j<2; j++) {
coord[i][j] = clientPtr->posn[i][j];
if (j == 1) {
coord[i][j] -= 1;
}
}
cSize[i] = coord[i][1] - coord[i][0]
- clientPtr->pad[i][0] - clientPtr->pad[i][1] + 1;
}
if ((cSize[0] <= 0) || (cSize[1] <= 0)) {
/*
* Window is too small, don't even bother to map
*/
UnmapClient(clientPtr);
} else if ((coord[0][1] < 0) || (coord[1][1] < 0)) {
/*
* Window is outside of the master (left or top)
*/
UnmapClient(clientPtr);
} else if ((coord[0][0] > mSize[0]) || (coord[1][0] > mSize[1])) {
/*
* Window is outside of the master (bottom or right)
*/
UnmapClient(clientPtr);
} else {
/*
* Window is visible, then map it
*/
MapClient(clientPtr,
coord[0][0] + clientPtr->pad[0][0] + intBWidth,
coord[1][0] + clientPtr->pad[1][0] + intBWidth,
cSize[0], cSize[1]);
}
}
done:
masterPtr->flags.repackPending = 0;
}
static int
PinnSide_AttNone(clientPtr, axis, which)
FormInfo *clientPtr; /* The client to pinn down */
int axis; /* 0 = x axis, 1 = yaxis */
int which; /* 0 = min side, 1= max side */
{
int reqSize;
if (clientPtr->attType[axis][NEXT_SIDE(which)] == ATT_NONE) {
if (which == SIDE0) {
clientPtr->side[axis][which].pcnt = 0;
clientPtr->side[axis][which].disp = 0;
return TCL_OK;
}
}
reqSize = ReqSize(clientPtr->tkwin, axis) +
clientPtr->pad[axis][0] + clientPtr->pad[axis][1];
PINN_CLIENT_SIDE(clientPtr, axis, NEXT_SIDE(which), 1);
clientPtr->side[axis][which].pcnt =
clientPtr->side[axis][NEXT_SIDE(which)].pcnt;
switch (which) {
case SIDE0:
clientPtr->side[axis][which].disp =
clientPtr->side[axis][NEXT_SIDE(which)].disp - reqSize;
break;
case SIDE1:
clientPtr->side[axis][which].disp =
clientPtr->side[axis][NEXT_SIDE(which)].disp + reqSize;
break;
}
return TCL_OK;
}
static int
PinnSide_AttPercent(clientPtr, axis, which)
FormInfo *clientPtr; /* The client to pinn down */
int axis; /* 0 = x axis, 1 = yaxis */
int which; /* 0 = min side, 1= max side */
{
clientPtr->side[axis][which].pcnt = clientPtr->att[axis][which].grid;
clientPtr->side[axis][which].disp = clientPtr->off[axis][which];
return TCL_OK;
}
static int
PinnSide_AttOpposite(clientPtr, axis, which)
FormInfo *clientPtr; /* The client to pinn down */
int axis; /* 0 = x axis, 1 = yaxis */
int which; /* 0 = min side, 1= max side */
{
FormInfo * attachPtr;
attachPtr = clientPtr->att[axis][which].widget;
PINN_CLIENT_SIDE(attachPtr, axis, NEXT_SIDE(which), 0);
clientPtr->side[axis][which].pcnt =
attachPtr->side[axis][NEXT_SIDE(which)].pcnt;
clientPtr->side[axis][which].disp =
attachPtr->side[axis][NEXT_SIDE(which)].disp +
clientPtr->off[axis][which];
return TCL_OK;
}
static int
PinnSide_AttParallel(clientPtr, axis, which)
FormInfo *clientPtr; /* The client to pinn down */
int axis; /* 0 = x axis, 1 = yaxis */
int which; /* 0 = min side, 1= max side */
{
FormInfo * attachPtr;
attachPtr = clientPtr->att[axis][which].widget;
PINN_CLIENT_SIDE(attachPtr, axis, which, 0);
clientPtr->side[axis][which].pcnt =
attachPtr->side[axis][which].pcnt;
clientPtr->side[axis][which].disp =
attachPtr->side[axis][which].disp +
clientPtr->off[axis][which];
return TCL_OK;
}
static int PinnClientSide(clientPtr, axis, which, isSelf)
FormInfo *clientPtr; /* The client to pinn down */
int axis; /* 0 = x axis, 1 = yaxis */
int which; /* 0 = min side, 1= max side */
int isSelf;
{
if ((which == SIDE0) && (clientPtr->sideFlags[axis] & PINNED_SIDE0)) {
/* already pinned */
return TCL_OK;
}
if ((which == SIDE1) && (clientPtr->sideFlags[axis] & PINNED_SIDE1)) {
/* already pinned */
return TCL_OK;
}
if ((clientPtr->depend > 0) && !isSelf) {
/*
* circular dependency detected
*/
return TCL_ERROR;
}
clientPtr->depend ++;
switch (clientPtr->attType[axis][which]) {
case ATT_NONE:
if (PinnSide_AttNone(clientPtr, axis, which) == TCL_ERROR) {
return TCL_ERROR;
}
break;
case ATT_OPPOSITE:
if (PinnSide_AttOpposite(clientPtr, axis, which) == TCL_ERROR) {
return TCL_ERROR;
}
break;
case ATT_PARALLEL:
if (PinnSide_AttParallel(clientPtr, axis, which) == TCL_ERROR) {
return TCL_ERROR;
}
break;
case ATT_GRID:
if (PinnSide_AttPercent(clientPtr, axis, which) == TCL_ERROR) {
return TCL_ERROR;
}
break;
}
if (which == SIDE0) {
clientPtr->sideFlags[axis] |= PINNED_SIDE0;
} else {
clientPtr->sideFlags[axis] |= PINNED_SIDE1;
}
clientPtr->depend --;
return TCL_OK;
}
static int PinnClient(clientPtr)
FormInfo *clientPtr;
{
int i;
for (i=0; i<2; i++) {
if (!(clientPtr->sideFlags[i] & PINNED_SIDE0)) {
PINN_CLIENT_SIDE(clientPtr, i, SIDE0, 0);
}
if (!(clientPtr->sideFlags[i] & PINNED_SIDE1)) {
PINN_CLIENT_SIDE(clientPtr, i, SIDE1, 0);
}
}
return TCL_OK;
}
/*
*--------------------------------------------------------------
*
* CalculateMasterSize --
*
* This internal procedure is used to find out the required
* size of a master window.
*
* Results:
* The return value is a pointer to the FormInfo structure
* corresponding to tkwin.
*
* Side effects:
* the reqSize[2] values in masterPtr is updated.
*
*--------------------------------------------------------------
*/
static void CalculateMasterSize(masterPtr)
MasterInfo *masterPtr;
{
FormInfo *clientPtr;
int i, cSize[2];
int req[2];
int intBWidth;
/* Information about the master window */
intBWidth = Tk_InternalBorderWidth(masterPtr->tkwin);
req[0] = req[1] = 2*intBWidth;
for (clientPtr = masterPtr->client; clientPtr; clientPtr=clientPtr->next) {
if (clientPtr->tkwin == NULL) {
continue;
}
cSize[0] = Tk_ReqWidth(clientPtr->tkwin);
cSize[1] = Tk_ReqHeight(clientPtr->tkwin);
cSize[0] += clientPtr->pad[0][0]+clientPtr->pad[0][1];
cSize[1] += clientPtr->pad[1][0]+clientPtr->pad[1][1];
for (i=0; i<2; i++) {
/* The required size of the master depends on
* (1) natural sizes of the clients
* (2) perc anchor points of the clients
* Ideally, the master must include as much visible parts
* of the clients as possible. It should also have a size
* big enough so that all the clients' requested (natural)
* sizes are satisfied. The algorithm is fairly simple, but
* it took me quite a while to figure out and it quite difficult
* to explain here. Please look at the following in-line
* examples.
*/
int p0 = clientPtr->side[i][0].pcnt;
int p1 = clientPtr->side[i][1].pcnt;
int d0 = clientPtr->side[i][0].disp;
int d1 = clientPtr->side[i][1].disp;
int req0 = 0;
int req1 = 0;
int reqx = 0;
if (d0 < 0 && p0 != 0) {
req0 = -d0 * masterPtr->grids[i] / p0;
}
if (d1 > 0 && p1 != masterPtr->grids[i]) {
req1 = d1 * masterPtr->grids[i] / (masterPtr->grids[i] - p1);
}
if (p0 == p1) {
/* case 1 */
/* Example: p0 = p1 = 10%; d0 = -10, d1 = 10
* then mSize should at least be 100 pixels so that
* side 0 can be visible. They are calculated in the
* previous two if statements
* result:
* size = 100
* side0 = 0;
* side1 = 20;
*/
/* Two sides are attached to the same perc anchor point */
if (d0 >= d1) {
/* widget invisible */
req0 = req1 = 0;
}
}
else if (p0 < p1) {
/* case 2 */
/* Example: p0 10%, p2 = 20%; cSize = 35, d0 = -5, d1 = 0
* then mSize should at least be 300 pixels so that
* cSize can be satisfied.
* result:
* size = 300
* side0 = 25;
* side1 = 60;
*/
int x = cSize[i];
if (p0 != 0 || d0 > 0) {
x += d0;
}
if (p1 != masterPtr->grids[i] || d1 < 0) {
x += -d1;
}
if (x > 0) {
reqx = x * masterPtr->grids[i] / (p1 - p0);
}
}
else {
/* case 2 */
/* This is very similar to case 1, except there are more cases
* in which the widget becomes invisible
*/
if (d0 >=0 || d1 <=0) {
/* widget invisible */
req0 = req1 = 0;
}
}
if (req[i] < req0) {
req[i] = req0;
}
if (req[i] < req1) {
req[i] = req1;
}
if (req[i] < reqx) {
req[i] = reqx;
}
}
}
req[0] += 2*intBWidth;
req[1] += 2*intBWidth;
masterPtr->reqSize[0] = (req[0] > 0) ? req[0] : 1;
masterPtr->reqSize[1] = (req[1] > 0) ? req[1] : 1;
}
/*
*----------------------------------------------------------------------
*
* TixFm_StructureProc --
*
* This procedure is invoked by the Tk event dispatcher in response
* to StructureNotify events.
*
* Results:
* None.
*
* Side effects:
* If a window was just deleted, clean up all its packer-related
* information. If it was just resized, repack its clients, if
* any.
*
*----------------------------------------------------------------------
*/
void
TixFm_StructureProc(clientData, eventPtr)
ClientData clientData; /* Our information about window
* referred to by eventPtr. */
XEvent *eventPtr; /* Describes what just happened. */
{
FormInfo *clientPtr = (FormInfo *) clientData;
switch (eventPtr->type) {
case ConfigureNotify:
ArrangeWhenIdle(clientPtr->master);
break;
case DestroyNotify:
if (clientPtr->master) {
TixFm_Unlink(clientPtr);
}
break;
case MapNotify:
break;
case UnmapNotify:
break;
}
}
static void
TixFm_ReqProc(clientData, tkwin)
ClientData clientData; /* TixForm's information about
* window that got new preferred
* geometry. */
Tk_Window tkwin; /* Other Tk-related information
* about the window. */
{
FormInfo *clientPtr = (FormInfo *) clientData;
if (clientPtr) {
ArrangeWhenIdle(clientPtr->master);
}
}
static void
MasterStructureProc(clientData, eventPtr)
ClientData clientData; /* Our information about window
* referred to by eventPtr. */
XEvent *eventPtr; /* Describes what just happened. */
{
MasterInfo *masterPtr = (MasterInfo *) clientData;
switch (eventPtr->type) {
case ConfigureNotify:
if (masterPtr->numClients > 0) {
ArrangeWhenIdle(masterPtr);
}
break;
case DestroyNotify:
TixFm_DeleteMaster(masterPtr);
break;
case MapNotify:
break;
case UnmapNotify:
break;
}
}
/*
*--------------------------------------------------------------
*
* TixFm_LostSlaveProc --
*
* This procedure is invoked by Tk whenever some other geometry
* claims control over a slave that used to be managed by us.
*
* Results:
* None.
*
* Side effects:
* Forgets all packer-related information about the slave.
*
*--------------------------------------------------------------
*/
static void
TixFm_LostSlaveProc(clientData, tkwin)
ClientData clientData; /* Form structure for slave window that
* was stolen away. */
Tk_Window tkwin; /* Tk's handle for the slave window. */
{
FormInfo *clientPtr = (FormInfo *) clientData;
Tk_DeleteEventHandler(clientPtr->tkwin, StructureNotifyMask,
TixFm_StructureProc, (ClientData) clientPtr);
if (clientPtr->master->tkwin != Tk_Parent(clientPtr->tkwin)) {
Tk_UnmaintainGeometry(clientPtr->tkwin, clientPtr->master->tkwin);
}
Tk_UnmapWindow(clientPtr->tkwin);
TixFm_Unlink(clientPtr);
}
/*
* Do some basic integrity checking
* --> right, left cannot both attach to none
* --> top, bottom cannot both attach to none.
* Otherwise, top or left is always set to attach at {pixel 0}
*/
static void CheckIntergrity(clientPtr)
FormInfo * clientPtr;
{
#if 0
/* Check the X axis */
if ((clientPtr->attType[0][0] == ATT_NONE)
&&(clientPtr->attType[0][1] == ATT_NONE)) {
clientPtr->attType[0][0] = ATT_DEFAULT_PIXEL;
clientPtr->att[0][0].grid = 0;
}
/* Check the Y axis */
if ((clientPtr->attType[1][0] == ATT_NONE)
&&(clientPtr->attType[1][1] == ATT_NONE)) {
clientPtr->attType[1][0] = ATT_DEFAULT_PIXEL;
clientPtr->att[1][0].grid = 0;
}
#endif
}
/*----------------------------------------------------------------------
* Memory management routines
*
*----------------------------------------------------------------------
*/
void TixFm_AddToMaster(masterPtr, clientPtr)
MasterInfo *masterPtr;
FormInfo *clientPtr;
{
if (clientPtr->master == masterPtr) {
/* already in master */
return;
}
if (clientPtr->master != NULL) {
/* We have to migrate the widget to a different parent*/
}
clientPtr->master = masterPtr;
if (masterPtr->client == NULL) {
masterPtr->client = clientPtr;
masterPtr->client_tail = clientPtr;
} else {
masterPtr->client_tail->next = clientPtr;
}
clientPtr->next = NULL;
masterPtr->client_tail = clientPtr;
++ masterPtr->numClients;
/* Manage its geometry using my proc */
Tk_ManageGeometry(clientPtr->tkwin, &formType, (ClientData)clientPtr);
}
void TixFm_UnlinkFromMaster(clientPtr)
FormInfo *clientPtr;
{
MasterInfo *masterPtr;
FormInfo *ptr, *prev;
#if DEBUG
fprintf(stderr, "unlinking %s\n", Tk_PathName(clientPtr->tkwin));
#endif
masterPtr = clientPtr->master;
/* First: get rid of the reference of this widget from other clients */
for (ptr=masterPtr->client; ptr; ptr=ptr->next) {
if (ptr != clientPtr) {
int i, j;
for (i=0; i<2; i++) {
for (j=0; j<2; j++) {
switch (ptr->attType[i][j]) {
case ATT_OPPOSITE:
case ATT_PARALLEL:
if (ptr->att[i][j].widget == clientPtr) {
ptr->attType[i][j] = ATT_GRID;
ptr->att[i][j].grid = 0;
ptr->off[i][j] = ptr->posn[i][j];
}
break;
}
}
if (ptr->strWidget[i][j] == clientPtr) {
ptr->strWidget[i][j] = 0;
}
}
}
}
/* Second: delete this client from the list */
for (prev=ptr=masterPtr->client; ptr; prev=ptr,ptr=ptr->next) {
if (ptr == clientPtr) {
if (prev==ptr) {
if (masterPtr->numClients == 1) {
masterPtr->client_tail = NULL;
}
masterPtr->client = ptr->next;
}
else {
if (ptr->next == NULL) {
masterPtr->client_tail = prev;
}
prev->next = ptr->next;
}
break;
}
}
-- masterPtr->numClients;
}
void
TixFm_FreeMasterInfo(clientData)
char *clientData;
{
MasterInfo *masterPtr = (MasterInfo *)clientData;
ckfree((char*)masterPtr);
}
void TixFm_DeleteMaster(masterPtr)
MasterInfo *masterPtr;
{
Tcl_HashEntry *hPtr;
FormInfo *clientPtr, * toFree;
if (masterPtr->flags.isDeleted) {
return;
}
Tk_DeleteEventHandler(masterPtr->tkwin, StructureNotifyMask,
MasterStructureProc, (ClientData) masterPtr);
clientPtr=masterPtr->client;
while(clientPtr) {
toFree = clientPtr;
clientPtr = clientPtr->next;
TixFm_ForgetOneClient(toFree);
}
hPtr = Tcl_FindHashEntry(&masterInfoHashTable,(char*)masterPtr->tkwin);
if (hPtr) {
Tcl_DeleteHashEntry(hPtr);
}
CancelArrangeWhenIdle(masterPtr);
masterPtr->flags.isDeleted = 1;
Tcl_EventuallyFree((ClientData)masterPtr, TixFm_FreeMasterInfo);
}
void TixFm_Unlink(clientPtr)
FormInfo *clientPtr;
{
Tcl_HashEntry *hPtr;
MasterInfo *masterPtr;
/* Delete this clientPtr from the master's list */
TixFm_UnlinkFromMaster(clientPtr);
/* Eventually free this clientPtr structure */
hPtr = Tcl_FindHashEntry(&formInfoHashTable,(char*)clientPtr->tkwin);
if (hPtr != NULL) {
Tcl_DeleteHashEntry(hPtr);
}
clientPtr->tkwin = NULL;
masterPtr = clientPtr->master;
ckfree((char*)clientPtr);
ArrangeWhenIdle(masterPtr);
}
/*
*--------------------------------------------------------------
*
* TixFm_GetFormInfo --
*
* This internal procedure is used to locate a FormInfo
* structure for a given window, creating one if one
* doesn't exist already.
*
* Results:
* The return value is a pointer to the FormInfo structure
* corresponding to tkwin.
*
* Side effects:
* A new FormInfo structure may be created. If so, then
* a callback is set up to clean things up when the
* window is deleted.
*
*--------------------------------------------------------------
*/
FormInfo *
TixFm_GetFormInfo(tkwin, create)
Tk_Window tkwin; /* Token for window for which
* FormInfo structure is desired. */
int create;
{
FormInfo *clientPtr;
Tcl_HashEntry *hPtr;
int isNew;
int i,j;
if (!initialized) {
initialized = 1;
Tcl_InitHashTable(&formInfoHashTable, TCL_ONE_WORD_KEYS);
Tcl_InitHashTable(&masterInfoHashTable, TCL_ONE_WORD_KEYS);
}
/*
* See if there's already FormInfo for this window. If not,
* then create a new one.
*/
if (!create) {
hPtr = Tcl_FindHashEntry(&formInfoHashTable, (char *)tkwin);
if (!hPtr) {
return NULL;
} else {
return (FormInfo *) Tcl_GetHashValue(hPtr);
}
} else {
hPtr = Tcl_CreateHashEntry(&formInfoHashTable, (char *) tkwin, &isNew);
if (!isNew) {
return (FormInfo *) Tcl_GetHashValue(hPtr);
} else {
clientPtr = (FormInfo *) ckalloc(sizeof(FormInfo));
clientPtr->tkwin = tkwin;
clientPtr->master = NULL;
clientPtr->next = NULL;
for (i=0; i< 2; i++) {
for (j=0; j< 2; j++) {
clientPtr->attType[i][j] = ATT_NONE;
clientPtr->att[i][j].grid = 0;
clientPtr->att[i][j].widget = NULL;
clientPtr->off[i][j] = 0;
clientPtr->pad[i][j] = 0;
clientPtr->side[i][j].pcnt = 0;
clientPtr->side[i][j].disp = 0;
clientPtr->spring[i][j] = -1;
clientPtr->strWidget[i][j] = 0;
}
clientPtr->springFail[i] = 0;
clientPtr->fill[i] = 0;
}
Tcl_SetHashValue(hPtr, clientPtr);
Tk_CreateEventHandler(tkwin, StructureNotifyMask,
TixFm_StructureProc, (ClientData) clientPtr);
return clientPtr;
}
}
}
static MasterInfo *
GetMasterInfo(tkwin, create)
Tk_Window tkwin; /* Token for window for which
* FormInfo structure is desired. */
int create; /* Should I create the MasterInfo if it
* does not exist? */
{
MasterInfo *masterPtr;
Tcl_HashEntry *hPtr;
int isNew;
if (!initialized) {
initialized = 1;
Tcl_InitHashTable(&formInfoHashTable, TCL_ONE_WORD_KEYS);
Tcl_InitHashTable(&masterInfoHashTable, TCL_ONE_WORD_KEYS);
}
/*
* See if there's already FormInfo for this window. If not,
* then create a new one.
*/
if (!create) {
hPtr = Tcl_FindHashEntry(&masterInfoHashTable, (char *)tkwin);
if (!hPtr) {
return NULL;
} else {
return (MasterInfo *) Tcl_GetHashValue(hPtr);
}
} else {
hPtr = Tcl_CreateHashEntry(&masterInfoHashTable, (char *)tkwin,
&isNew);
if (!isNew) {
masterPtr = (MasterInfo *) Tcl_GetHashValue(hPtr);
}
else {
masterPtr = (MasterInfo *) ckalloc(sizeof(MasterInfo));
masterPtr->tkwin = tkwin;
masterPtr->client = NULL;
masterPtr->client_tail = NULL;
masterPtr->flags.repackPending = 0;
masterPtr->flags.isDeleted = 0;
masterPtr->numClients = 0;
masterPtr->numRequests = 0;
masterPtr->grids[0] = 100;
masterPtr->grids[1] = 100;
Tcl_SetHashValue(hPtr, masterPtr);
}
}
/* TK BUG:
*
* It seems like if you destroy some slaves TK will delete the event
* handler. So for sure we just create it every time a slave is created.
*
* Note: calling Tk_CreateEventHandler with same arguments twice won't
* create two instances of the same event handler: Thus safe to call
* blindly.
*/
Tk_CreateEventHandler(tkwin, StructureNotifyMask,
MasterStructureProc, (ClientData) masterPtr);
#if 0
Tk_ManageGeometry(tkwin, (Tk_GeomMgr *)&masterType,
(ClientData) masterPtr);
#endif
return masterPtr;
}
/*----------------------------------------------------------------------
* PLace the clients
*----------------------------------------------------------------------
*/
static int PlaceSide_AttNone(clientPtr, axis, which)
FormInfo *clientPtr; /* The client to Place down */
int axis; /* 0 = x axis, 1 = yaxis */
int which; /* 0 = min side, 1= max side */
{
int reqSize;
if (clientPtr->attType[axis][NEXT_SIDE(which)] == ATT_NONE) {
if (which == SIDE0) {
clientPtr->posn[axis][which] = 0;
return TCL_OK;
}
}
reqSize = ReqSize(clientPtr->tkwin, axis) +
clientPtr->pad[axis][0] + clientPtr->pad[axis][1];
PLACE_CLIENT_SIDE(clientPtr, axis, NEXT_SIDE(which), 1);
switch (which) {
case SIDE0:
clientPtr->posn[axis][which] =
clientPtr->posn[axis][NEXT_SIDE(which)] - reqSize;
break;
case SIDE1:
clientPtr->posn[axis][which] =
clientPtr->posn[axis][NEXT_SIDE(which)] + reqSize;
break;
}
return TCL_OK;
}
static int PlaceSide_AttAbsolute(clientPtr, axis, which)
FormInfo *clientPtr; /* The client to Place down */
int axis; /* 0 = x axis, 1 = yaxis */
int which; /* 0 = min side, 1= max side */
{
int mSize[2];
MasterInfo * masterPtr = clientPtr->master;
int intBWidth = Tk_InternalBorderWidth(masterPtr->tkwin);
mSize[0] = Tk_Width(masterPtr->tkwin) - 2*intBWidth;
mSize[1] = Tk_Height(masterPtr->tkwin) - 2*intBWidth;
clientPtr->posn[axis][which] =
mSize[axis] * clientPtr->side[axis][which].pcnt/masterPtr->grids[axis] +
clientPtr->side[axis][which].disp;
return TCL_OK;
}
static int PlaceSide_AttOpposite(clientPtr, axis, which)
FormInfo *clientPtr; /* The client to Place down */
int axis; /* 0 = x axis, 1 = yaxis */
int which; /* 0 = min side, 1= max side */
{
FormInfo * attachPtr;
attachPtr = clientPtr->att[axis][which].widget;
PLACE_CLIENT_SIDE(attachPtr, axis, NEXT_SIDE(which), 0);
clientPtr->posn[axis][which] = attachPtr->posn[axis][NEXT_SIDE(which)];
clientPtr->posn[axis][which] += clientPtr->off[axis][which];
return TCL_OK;
}
static int PlaceSide_AttParallel(clientPtr, axis, which)
FormInfo *clientPtr; /* The client to Place down */
int axis; /* 0 = x axis, 1 = yaxis */
int which; /* 0 = min side, 1= max side */
{
FormInfo * attachPtr;
attachPtr = clientPtr->att[axis][which].widget;
PLACE_CLIENT_SIDE(attachPtr, axis, NEXT_SIDE(which), 0);
clientPtr->posn[axis][which] = attachPtr->posn[axis][which];
clientPtr->posn[axis][which] += clientPtr->off[axis][which];
return TCL_OK;
}
static int PlaceSimpleCase(clientPtr, axis, which)
FormInfo *clientPtr; /* The client to Place down */
int axis; /* 0 = x axis, 1 = yaxis */
int which; /* 0 = min side, 1= max side */
{
clientPtr->depend ++;
switch (clientPtr->attType[axis][which]) {
case ATT_NONE:
if (PlaceSide_AttNone(clientPtr, axis, which) == TCL_ERROR) {
return TCL_ERROR;
}
break;
case ATT_GRID:
if (PlaceSide_AttAbsolute(clientPtr, axis, which) == TCL_ERROR) {
return TCL_ERROR;
}
break;
case ATT_OPPOSITE:
if (PlaceSide_AttOpposite(clientPtr, axis, which) == TCL_ERROR) {
return TCL_ERROR;
}
break;
case ATT_PARALLEL:
if (PlaceSide_AttParallel(clientPtr, axis, which) == TCL_ERROR) {
return TCL_ERROR;
}
break;
}
if (which == SIDE0) {
clientPtr->sideFlags[axis] |= PINNED_SIDE0;
} else {
clientPtr->sideFlags[axis] |= PINNED_SIDE1;
}
clientPtr->depend --;
return TCL_OK;
}
/* ToDo: I'll make this more efficient by pre-allocating some links */
static SpringLink *
AllocSpringLink()
{
return (SpringLink *) ckalloc(sizeof(SpringLink));
}
static void
FreeSpringLink(link)
SpringLink * link;
{
ckfree((char*)link);
}
static void FreeSpringList(listPtr)
SpringList * listPtr;
{
SpringLink * link, * toFree;
for (link=listPtr->head; link; ) {
toFree = link;
link=link->next;
FreeSpringLink(toFree);
}
}
static void
AddRightSprings(listPtr, clientPtr)
SpringList * listPtr;
FormInfo *clientPtr;
{
SpringLink * link = AllocSpringLink();
link->next = NULL;
link->clientPtr = clientPtr;
if (listPtr->head == NULL) {
listPtr->head = listPtr->tail = link;
} else {
listPtr->tail->next = link;
listPtr->tail = link;
}
++ listPtr->num;
}
static void
AddLeftSprings(listPtr,clientPtr)
SpringList * listPtr;
FormInfo *clientPtr;
{
SpringLink * link = AllocSpringLink();
link->next = listPtr->head;
link->clientPtr = clientPtr;
listPtr->head = link;
++ listPtr->num;
}
static int
PlaceWithSpring(clientPtr, axis, which)
FormInfo *clientPtr; /* The client to Place down */
int axis; /* 0 = x axis, 1 = yaxis */
int which; /* 0 = min side, 1= max side */
{
SpringList springs;
SpringLink * link;
FormInfo *ptr;
float boundary[2];
float totalSize, totalStrength;
int mSize[2];
float gap, disp;
MasterInfo * masterPtr = clientPtr->master;
int intBWidth = Tk_InternalBorderWidth(masterPtr->tkwin);
springs.head = (SpringLink *)0;
springs.tail = (SpringLink *)0;
springs.num = 0;
mSize[0] = Tk_Width(masterPtr->tkwin) - 2*intBWidth;
mSize[1] = Tk_Height(masterPtr->tkwin) - 2*intBWidth;
/* Expand the right side of the spring list */
ptr = clientPtr;
while (1) {
switch (ptr->attType[axis][1]) {
case ATT_OPPOSITE:
case ATT_NONE:
/* Some attachments */
AddRightSprings(&springs, ptr);
if ((ptr = ptr->strWidget[axis][1]) == 0) {
goto done1;
}
switch (ptr->attType[axis][0]) {
case ATT_GRID:
case ATT_PARALLEL:
goto done1;
}
break;
case ATT_GRID:
case ATT_PARALLEL:
AddRightSprings(&springs, ptr);
goto done1;
}
}
done1:
/* Expand the left side of the spring list */
ptr = clientPtr;
while (2) {
switch (ptr->attType[axis][0]) {
case ATT_OPPOSITE:
case ATT_NONE:
/* Some attachments */
if (ptr != clientPtr) {
AddLeftSprings(&springs, ptr);
}
if ((ptr = ptr->strWidget[axis][0]) == 0) {
goto done2;
}
switch (ptr->attType[axis][1]) {
case ATT_PARALLEL:
goto done2;
}
break;
case ATT_GRID:
case ATT_PARALLEL:
if (ptr != clientPtr) {
AddLeftSprings(&springs, ptr);
}
goto done2;
}
}
done2:
/* Make sure this is a good list (neither ends are none) */
if (springs.head == NULL) {
/* this should never happen, just to make sure */
goto fail;
}
if (springs.head->clientPtr->attType[axis][0] == ATT_NONE) {
goto fail;
}
if (springs.tail->clientPtr->attType[axis][1] == ATT_NONE) {
goto fail;
}
/*
* Now calculate the total requested sizes of the spring group
*/
totalSize = (float)(0.0);
totalStrength = (float)(0.0);
for (link=springs.head; link; link=link->next) {
int size = ReqSize(link->clientPtr->tkwin, axis);
totalSize += size + link->clientPtr->pad[axis][0] +
link->clientPtr->pad[axis][1];
if (link->clientPtr->spring[axis][0] > 0) {
totalStrength += link->clientPtr->spring[axis][0];
}
}
if (springs.tail->clientPtr->spring[axis][1] > 0) {
totalStrength += springs.tail->clientPtr->spring[axis][1];
}
boundary[0] = (float) mSize[axis] *
(float) springs.head->clientPtr->side[axis][0].pcnt /
(float) masterPtr->grids[axis] +
(float) springs.head->clientPtr->side[axis][0].disp;
boundary[1] = (float) mSize[axis] *
(float) springs.tail->clientPtr->side[axis][1].pcnt /
(float) masterPtr->grids[axis] +
(float) springs.tail->clientPtr->side[axis][1].disp;
/* (4) Now spread the sizes to the members of this list */
gap = (float)(boundary[1] - boundary[0]) - totalSize;
if (gap < 0) {
goto fail;
}
disp = boundary[0];
if (totalStrength <= 0.0) {
totalStrength = (float)(1.0);
}
for (link=springs.head; link; link=link->next) {
float spring0, spring1;
int gap0, gap1;
int adjust; /* to overcome round-off errors */
spring0 = (float)link->clientPtr->spring[axis][0];
spring1 = (float)link->clientPtr->spring[axis][1];
if (spring0 < (float)(0.0)) {
spring0 = (float)(0.0);
}
if (spring1 < (float)(0.0)) {
spring1 = (float)(0.0);
}
/* Divide by two: because two consecutive clients share the same
* spring; so each of them get a half.
*/
adjust = 0;
if (link !=springs.head) {
if (spring0 > 0 && link->clientPtr->spring[axis][0] % 2 == 1) {
adjust = 1;
}
spring0 /= (float)(2.0);
}
if (link !=springs.tail) {
spring1 /= (float)(2.0);
}
gap0 = (int)(gap * spring0 / totalStrength) + adjust;
gap1 = (int)(gap * spring1 / totalStrength);
if (link->clientPtr->fill[axis]) {
link->clientPtr->posn[axis][0] = (int)disp;
disp += gap0;
disp += gap1;
disp += ReqSize(link->clientPtr->tkwin, axis);
/* Somehow there may be a round-off right at the end of the
* list --> kludge*/
if (link->next == NULL) {
disp = boundary[1];
}
link->clientPtr->posn[axis][1] = (int)disp;
} else {
disp += gap0;
link->clientPtr->posn[axis][0] = (int)disp;
disp += ReqSize(link->clientPtr->tkwin, axis);
link->clientPtr->posn[axis][1] = (int)disp;
disp += gap1;
/*
* Somehow there may be a round-off right at the end of the
* list --> kludge
*/
if (link->next == NULL && gap1 < 0.001) {
link->clientPtr->posn[axis][1] = (int)boundary[1];
}
}
link->clientPtr->sideFlags[axis] |= PINNED_SIDE0;
link->clientPtr->sideFlags[axis] |= PINNED_SIDE1;
}
FreeSpringList(&springs);
return TCL_OK;
fail:
for (link=springs.head; link; link=link->next) {
link->clientPtr->springFail[axis] = 1;
}
FreeSpringList(&springs);
return TCL_ERROR;
}
static int PlaceClientSide(clientPtr, axis, which, isSelf)
FormInfo *clientPtr; /* The client to Place down */
int axis; /* 0 = x axis, 1 = yaxis */
int which; /* 0 = min side, 1= max side */
int isSelf;
{
if ((which == SIDE0) && (clientPtr->sideFlags[axis] & PINNED_SIDE0)) {
/* already Placeed */
return TCL_OK;
}
if ((which == SIDE1) && (clientPtr->sideFlags[axis] & PINNED_SIDE1)) {
/* already Placeed */
return TCL_OK;
}
if ((clientPtr->depend > 0) && !isSelf) {
/*
* circular dependency detected
*/
return TCL_ERROR;
}
/* No spring : we just do a "simple case"
* The condition is ( (x || x) && (x || x) )
*/
if ((clientPtr->spring[axis][0] < 0 ||
(clientPtr->sideFlags[axis] & PINNED_SIDE0)) &&
(clientPtr->spring[axis][1] < 0 ||
(clientPtr->sideFlags[axis] & PINNED_SIDE1))) {
return PlaceSimpleCase(clientPtr, axis, which);
}
if (clientPtr->springFail[axis]) {
return PlaceSimpleCase(clientPtr, axis, which);
}
if (PlaceWithSpring(clientPtr, axis, which) != TCL_OK) {
/* if comes to here : (1) Not enough space for the spring expansion
* (2) Not both end-sides are spring-attached */
return PlaceSimpleCase(clientPtr, axis, which);
} else {
return TCL_OK;
}
}
static int PlaceClient(clientPtr)
FormInfo *clientPtr;
{
int i;
for (i=0; i<2; i++) {
if (!(clientPtr->sideFlags[i] & PINNED_SIDE0)) {
PLACE_CLIENT_SIDE(clientPtr, i, SIDE0, 0);
}
if (!(clientPtr->sideFlags[i] & PINNED_SIDE1)) {
PLACE_CLIENT_SIDE(clientPtr, i, SIDE1, 0);
}
}
return TCL_OK;
}
static int PlaceAllClients(masterPtr)
MasterInfo * masterPtr;
{
FormInfo *clientPtr;
int i;
/*
* First mark all clients as unpinned, and clean the opposite flags,
*/
for (clientPtr = masterPtr->client; clientPtr; clientPtr=clientPtr->next) {
if (clientPtr->tkwin != NULL) {
for (i=0; i<2; i++) {
/* clear all flags */
clientPtr->sideFlags[i] = 0;
clientPtr->springFail[i] = 0;
}
clientPtr->depend = 0;
}
}
/*
* Now calculate their actual positions on the master
*/
for (clientPtr = masterPtr->client; clientPtr; clientPtr=clientPtr->next) {
if (clientPtr->tkwin == NULL) { /* it was deleted */
continue;
}
for (i=0; i<2; i++) {
if ((clientPtr->sideFlags[i] & PINNED_ALL) != PINNED_ALL) {
if (PlaceClient(clientPtr) == TCL_ERROR) {
/*
* Detected circular dependency
*/
return TCL_ERROR;
}
break;
}
}
}
return TCL_OK;
}