[comp.windows.x.motif] setting Dialog position

nazgul@alfalfa.com (Kee Hinckley) (03/18/91)

> 	What I actually want to do is set the position of the dialog
> 	(from XmCreatePromptDialog()), but I can't set the position
> 	(Xt ignores my request)
> 	until after the Dialog has been managed, which means
> 	it appears and then moves.
> 	I have XmNdefaultPosition set to False.
> 	So I was trying to map it after positioning it.
> 	So how do you set the position of a dialog before it appears,
> 	or if you can't, how can you do what the original poster wanted?
> 	(R3/1.x)

Here's what I do at R4/1.1.  It should work in older versions, but you never
know.  I've posted this code before, but it was buggy as all get out then.
This does a bit more than you asked for - it also places stuff at a specified
location or relative to the parent window (I usually try for top-right of parent
and top-left of child).

This is in C++, but it should be easily convertable to C.  If you find any
bugs or have any questions about it let me know.


typedef long OmLocation;
#define OmChildCenter	0x0000
#define OmChildRight	0x0001
#define OmChildLeft	0x0002
#define OmChildTop	0x0004
#define OmChildBottom	0x0008

#define OmParentCenter	0x0000
#define OmParentRight	0x0100
#define OmParentLeft	0x0200
#define OmParentTop	0x0400
#define OmParentBottom	0x0800

#define OmCenter	0x0000
#define OmScreenCenter	0x1000
#define OmDontMove	0x2000


//
// Structure to pass placement information to the callback if we need to wait
// until we are mapped.
//
typedef struct {
    Position	x, y;
    Dimension	width, height;
    OmLocation	loc;
} PlaceInfo;

//
// Internal placement call.
//
static void placeWindow(Widget widget, OmLocation loc=OmScreenCenter, Position px=0,
			Position py=0, Dimension pwidth=0, Dimension pheight=0);

//
// Callback called on map
//
static void placeWindowCB(Widget widget, XtPointer p, XtPointer) {
			  PlaceInfo	*pi = (PlaceInfo *) p;
			    
    placeWindow(widget, pi->loc, pi->x, pi->y, pi->width, pi->height);
    XtRemoveCallback(widget, XmNmapCallback, placeWindowCB, pi);
    delete pi;
}

//
// A couple handy macros
//
#define PLRCentered(loc) (! ((loc & OmParentLeft) || (nloc & OmParentRight)) )
#define PTBCentered(loc) (! ((loc & OmParentTop) || (nloc & OmParentBottom)) )

//
// Place the window at a particular location relative to its parent's location.
//
// Note that the position values here are for the parent, not the widget.
//
static void placeWindow(Widget widget, OmLocation loc, Position px,
			Position py, Dimension pwidth, Dimension pheight) {
    Dimension	cwidth, cheight, swidth, sheight;
    Position	x, y, origx, origy;
    Arg		args[10];
    int		i;
    OmLocation	nloc;
    Boolean	moveIt = False;

    if (loc == OmDontMove) return;
  
    // If we aren't realized then we can't place ourselves quite yet, so we set
    // up a callback for when we are.
    if (!XtIsRealized(widget)) {
	PlaceInfo	*pi = new PlaceInfo;
	//printf("No realized, setting a mapCallback\n");
	pi->x = px; pi->y = py; pi->width = pwidth; pi->height = pheight; pi->loc = loc;
	XtAddCallback(widget, XmNmapCallback, placeWindowCB, pi);
	return;
    }

    //printf("PlaceWindow %X: %d x %d +%d +%d\n", loc, px, py, pwidth, pheight);
    
    swidth = WidthOfScreen(XtScreen(widget));
    sheight = HeightOfScreen(XtScreen(widget));

    //printf("Screen = +%d +%d\n", swidth, sheight);

    //!! We've got to get the kids managed so we can get the right width/height
    //!! Unfortunately, both of these die in _XmBulletinBoardFOcusMoved - line 2350
    /*
    if (!XtIsRealized(widget)) {
	XtSetArg(args[0], XmNmappedWhenManaged, False);
	XtSetValues(widget, args, 1);
	XtManageChild(widget);
	//XtRealizeWidget(widget);
    }
    */
	
    i = 0;
    XtSetArg(args[i], XmNwidth, &cwidth); ++i;
    XtSetArg(args[i], XmNheight, &cheight); ++i;
    XtGetValues(widget, args, i);

    //!! Hack to deal with case where dialog hasn't been sized yet (see commented out
    //!! stuff above)
    if (cwidth < 50) cwidth = 100;
    if (cheight < 50) cheight = 100;

    //printf("Child = +%d +%d\n", cwidth, cheight);

    //
    // We try and be smart here.  We want to stay on the screen, but the placement
    // might put us somewhere else.  So if we are centered in the axis that doesn't
    // fit we'll just slide over.  But if we were supposed to be on one side, and we
    // don't fit there, we'll try the other side on the assumption that that's better
    // than obscuring the application.  Note that none of this stuff has been tested
    // yet, so who knows if it works.  But that's the concept.  Anyway, if we don't
    // fit on the other side *either* then we just give up and put ourselves on the
    // original side, but slide over until we are on screen.
    //
    nloc = loc;
tryagain:
    x = px;
    y = py;
    if (nloc & OmScreenCenter) {
	x = swidth / 2;
	y = sheight / 2;
    } else {
	if (nloc & OmParentLeft);
	else if (nloc & OmParentRight) x = px + pwidth;
	else x = px + pwidth/2;
	if (nloc & OmParentTop);
	else if (nloc & OmParentBottom) y = py + pheight;
	else y = py + pheight/2;
    }
    if (nloc & OmChildLeft);
    else if (nloc & OmChildRight) x -= cwidth;
    else x -= cwidth/2;
    if (nloc & OmChildTop);
    else if (nloc & OmChildBottom) y -= cheight;
    else y -= cheight/2;

    //printf("nloc = %X, x = %d, y = %d\n", nloc, x, y);

    if (!moveIt) {	// Remember these the first time
	origx = x;
	origy = y;
	//printf("origx = %d, origy = %d\n", origx, origy);
    }
    

    // Sanity checks (we try once as is, once modified, and then we go back do the
    // normal settings and just shift things onto the screen.)

    if (x < 0 || x+cwidth >= swidth || y < 0 || y+cheight >= sheight) {
	//printf("Sanity failed: x < 0 || x+cwidth >= swidth || y < 0 || y+cheight >= sheight\n");
giveup:
	//printf("giveup: ");
	if (moveIt) {
	    //printf("moveit (reset x(%d) to origx and y(%d) to origy): ", x, y);
	    x = origx;			// Tried to change, but it didn't help
	    y = origy;
	    if (x+cwidth >= swidth) x = swidth - cwidth;
	    if (x < 0) x = 0;
	    if (y+cheight >= sheight) y = sheight - cheight;
	    if (y < 0) y = 0;
	    //printf("final x=%d, y=%d\n", x, y);
	} else {
	    //printf("setting moveIt: ");
	    moveIt = True;
	    // Note that we don't try to move around based on child pos
	    if (x < 0) {				// Too far to left
		//printf("x < 0\n");
		if (nloc & OmParentLeft) {		//   If on parent left
		    nloc &= ~OmParentLeft;		//     Try righthand side
		    nloc |= OmParentRight;
		    if (nloc & OmChildRight) {
			nloc &= ~OmChildRight;
			nloc |= OmChildLeft;
		    } else if (nloc & OmChildLeft) {
			nloc &= ~OmChildLeft;
			nloc |= OmChildRight;
		    }			
		    //printf("try the right hand siee\n");
		}
	    } else if (x+cwidth >= swidth) {		// Too far to right
		//printf("x+cwidth >= swidth\n");
		if (nloc & OmParentRight) {		//   If on parent right
		    nloc &= ~OmParentRight;		//     Try lefthand side
		    nloc |= OmParentLeft;
		    //printf("try the left hand side\n");
		    if (nloc & OmChildRight) {
			nloc &= ~OmChildRight;
			nloc |= OmChildLeft;
		    } else if (nloc & OmChildLeft) {
			nloc &= ~OmChildLeft;
			nloc |= OmChildRight;
		    }			
		}
	    }
	    if (y < 0) {				// Too far to top
		//printf("y < 0\n");
		if (nloc & OmParentTop) {		//   If on parent top
		    nloc &= ~OmParentTop;		//     Try bottom side
		    nloc |= OmParentBottom;
		    //printf("Try the bottom\n");
		    if (nloc & OmChildTop) {
			nloc &= ~OmChildTop;
			nloc |= OmChildBottom;
		    } else if (nloc & OmChildBottom) {
			nloc &= ~OmChildBottom;
			nloc |= OmChildTop;
		    }			
		}
	    } else if (y+cheight >= sheight) {		// Too far to bottom
		//printf("y+cheight >= sheight\n");
		if (nloc & OmParentBottom) {		//   If on parent bottom
		    nloc &= ~OmParentBottom;		//     Try top side
		    nloc |= OmParentTop;
		    if (nloc & OmChildTop) {
			nloc &= ~OmChildTop;
			nloc |= OmChildBottom;
		    } else if (nloc & OmChildBottom) {
			nloc &= ~OmChildBottom;
			nloc |= OmChildTop;
		    }			
		    //printf("try the top\n");
		}
	    }
	    //printf("nloc = %X, loc = %X (!= tryagain, else giveup)\n", nloc, loc);
	    if (nloc != loc) goto tryagain;		// Try something different
	    else goto giveup;				// Nothing we can do
	}
    }    
    //printf("Settled on x = %d and y = %d\n", x, y);
    
    XtSetArg(args[0], XmNx, x);
    XtSetArg(args[1], XmNy, y);
    XtSetValues(widget, args, 2);
}


void OmXWindow::placeManage(OmLocation loc, Widget parent) {
    Widget	shell;
    Position	x, y;
    Dimension	width, height;

    for (shell = widget; shell && !XtIsShell(shell); shell = XtParent(shell));
    if (shell && XtIsRealized(parent)) {
	arglist.add(XmNdefaultPosition, (XtArgVal)False, NULL);
	XtSetValues(widget, arglist.args, arglist.clear());
	arglist.add(XmNwidth, &width, XmNheight, &height, NULL);
	XtGetValues(parent, arglist.args, arglist.clear());
	XtTranslateCoords(parent, 0, 0, &x, &y);
	placeWindow(widget, loc, x, y, width, height);
    }
    manage();
}
void OmXWindow::placeManage(OmLocation loc, Position x, Position y) {
    /*Widget	shell;

    for (shell = widget; shell && !XtIsShell(shell); shell = XtParent(shell));
    if (shell) */placeWindow(widget, loc, x, y, 0, 0);
    manage();
}

  

Alfalfa Software, Inc.          |       Poste:  The EMail for Unix
nazgul@alfalfa.com              |       Send Anything... Anywhere
617/646-7703 (voice/fax)        |       info@alfalfa.com

I'm not sure which upsets me more: that people are so unwilling to accept
responsibility for their own actions, or that they are so eager to regulate
everyone else's.