/***(VdtView.m)****************************************************************
*H* Virtual Desk Top View subclass implementation   	    	    	      *
******************************************************************************/

#import <stdlib.h>

#import <appkit/publicWraps.h>
#import <appkit/Control.h>
#import <appkit/Listener.h>
#import <appkit/NXImage.h>
#import <appkit/Speaker.h>

#import <dpsclient/dpsclient.h>
#import <dpsclient/dpswraps.h>
#import <dpsclient/wraps.h>

#import "VdtView.h"
#import "VdtApplication.h"
#import "VdtWindow.h"
#import "PswWindowOps.h"


/* Private Class Wide Definitions */
static CTwinInfo *FindWinfoByScaledPoint (NXPoint*, int, CTwinInfo*, int);
#ifdef PAN_GALACTIC_GARGLE_BLASTER
static CTwinInfo *FindWinfoByWindowNum (int, CTwinInfo*, int);
#endif

#define MKpswAbove  1
#define MKpswBelow  -1

@implementation VdtView

/******************************************************************************
* INSTANCE METHOD:- initFrame:	    	    	    	    	    	      *
*   This method  overrides  the  View's  superclass method.  The - initFrame: *
* method gets called  automatically when the object is instanciated.  It used *
* to setup initial data structures and instance variables.  	    	      *
******************************************************************************/
- initFrame

    /* Arguments */
    :(const NXRect*)frameRect	/* The size and position of the view */

{   /* Local Variables */
    int     defOriginX;
    int     defOriginY;
    int     defSizeH;
    int     defSizeW;
    int	    panGrid;
    NXRect  vdtFrame;
    NXRect  vdtRect;

/* BEGIN initFrame: */
/* Get Virtual Desktop parameters, via defaults */
[NXApp getDefaultDockHandling:&IVdockHandling];
[NXApp getDefaultOrigin:&defOriginX :&defOriginY];
[NXApp getDefaultPanJump:&IVvdtPanJump];
[NXApp getDefaultPanGrid:&panGrid];
[NXApp getDefaultScale:&IVvdtScale];
[NXApp getDefaultSize:&defSizeW :&defSizeH];
[NXApp getDefaultWindowMotion:&IVwindowMotion];

/* Initialize VDT working parameters */
[NXApp getScreenSize:&IVscreenSize];
NXSetRect(&vdtRect, 0.0, 0.0,
    	  IVscreenSize.width*defSizeW, IVscreenSize.height*defSizeH);

/* Rework the View's frame rectangle to fit defaults settings */
vdtFrame.origin = frameRect->origin;
vdtFrame.size.width  = vdtRect.size.width / IVvdtScale;
vdtFrame.size.height = vdtRect.size.height / IVvdtScale;

/* Perform superclass inits */
[super initFrame:(const NXRect*)&vdtFrame];

/* Initialize the off screen Stationary VDT image */
IVvdtStillImage = [[NXImage alloc] initSize:&vdtFrame.size];
[IVvdtStillImage setFlipped:NO];

IVvdtActualSize  = vdtFrame.size;
IVvdtVirtualSize = vdtRect.size;
IVvdtOrigin.x = defOriginX * IVscreenSize.width / IVvdtScale;
IVvdtOrigin.y = defOriginY * IVscreenSize.height / IVvdtScale;
NXSetRect(&IVviewPort, IVvdtOrigin.x, IVvdtOrigin.y,
    	  IVscreenSize.width/IVvdtScale, IVscreenSize.height/IVvdtScale);

/* Initialize instance variables */
IVwinInfoCount  = 0;
IVwinInfoList   = (CTwinInfo*)0;
IVvdtWinNum     = -1;
IVvdtRootWinNum = -1;
IVstickyCount   = 0;
IVstickySize    = 0;
IVstickyTable	= NULL;
[self setVdtPanGrid:panGrid];

/* Initialize the on screen window information list */
[self updateWindowList];

return(self);
}/* END initFrame: */



/******************************************************************************
* INSTANCE METHOD:- alignIcons:        	    	    	    	       	      *
*   This method finds  all  the Non-Docked icons within the current ViewPort, *
* and arranges them sequentially row upon row.  The icons are bunched towards *
* given  corner  of  the  screen  determined  by  the  current  Icon  Gravity *
* preference.  The right hand corners are set 67 pixels away from the edge of *
* the screen to leave room for the Dock.    	    	    	    	      *
******************************************************************************/
- alignIcons:sender

{   /* Local Variables */
    float   	 curX;      /* What column the next icon will be moved to */
    int   	 curY;      /* What row    the next icon will be moved to */
    int   	 deltaX;    /* Direction to in Icon row */
    int   	 deltaY;    /* Direction to in Icon column */
    int	    	 gravity;   /* 0:TopLeft 1:TopRight 2:BottomLeft 3:BottomRight */
    int	    	 index;	    /* Loop Variable */
    int	    	 iconsX;    /* Number of icons that fit in the X dir */
    int	    	 iconsY;    /* Number of icons that fit in the Y dir */
    float   	 posX;	    /* New Y position of Icon to move */
    float   	 posY;	    /* New Y position of Icon to move */
    float   	 originX;   /* Base position of Icon Row */
    float   	 originY;   /* Base position of Icon Column */
    NXRect  	 viewPort;  /* Actual size rectangle of VDT View Port */
    CTwinInfo	*wiPtr;	    /* Pointer to node in IVwinInfoList */

/* BEGIN-alignIcons: */

/* Make sure the Window Information List is accurate */
[self updateWindowList];

/* Get actual size of the rectangle for the current View Port */
viewPort.origin.x = 0.0;
viewPort.origin.y = 0.0;
viewPort.size     = IVscreenSize;

/* Calculate the number of icons that can fit across the screen */
iconsX = (IVscreenSize.width - 64.0) / 64.0;
iconsY = IVscreenSize.height / 64.0;

/* Determine the positioning parameters according to the Icon Gravity */
[NXApp getDefaultIconGravity:&gravity];
if ( gravity == 0 )
    {/* Set Top Left icon gravity parameters */
    originX = 0.0;
    originY = IVscreenSize.height - 64.0;
    deltaX  = 1;
    deltaY  = -1;
    }
else if ( gravity == 1 )
    {/* Set Top Right icon gravity parameters */
    originX = IVscreenSize.width  - 131.0;
    originY = IVscreenSize.height - 64.0;
    deltaX  = -1;
    deltaY  = -1;
    }
else if ( gravity == 3 )
    {/* Set Bottom Right icon gravity parameters */
    originX = IVscreenSize.width - 131.0;
    originY = 0.0;
    deltaX  = -1;
    deltaY  = 1;
    }
else
    {/* Assume gravity of Bottom Left */
    originX = 0.0;
    originY = 0.0;
    deltaX  = 1;
    deltaY  = 1;
    }

/* Loop through the window list, move any 64x64 window in the View Port */
curX = curY = 0;
for (index = 0; index < IVwinInfoCount; index++)
    {
    wiPtr = IVwinInfoList+index;

    if ( wiPtr->level == NX_NORMALLEVEL &&
    	 wiPtr->actualRect.size.width == 64.0 &&
         wiPtr->actualRect.size.height == 64.0 &&
         NXIntersectsRect(&viewPort, &wiPtr->actualRect) )
    	{/* This icon is within the current View Port Rect, so align it */

    	posX = originX + curX * deltaX * 64.0;
    	posY = originY + curY * deltaY * 64.0;
        PswMoveWindow(wiPtr->number, posX, posY);

    	/* Calculate the position for the next Icon */
    	curX++;
    	if ( curX == iconsX )
    	    {/* Start a new row of icons */
    	    curX = 0;
       	    curY++;
    	    if ( curY == iconsY )
    	    	/* An awful lot of icons! Start piling them up */
    	    	curY = 0;
    	    }
    	}/*if*/
    }/*for*/

/* Update the VDT window representation */
[self updateVdtView:self];

return(self);
}/* END-alignIcons: */



/******************************************************************************
* INSTANCE METHOD:- dragVdtViewPort::	    	    	    	    	      *
*   This method is called from  the  mouse  down method when the user presses *
* MB1 without any modifier keys and the cursor is inside the current VDT View *
* Port.  This  method waits  until  it  actually detects the first drag event *
* before setting up an off screen image that will be composited into the view *
* as each  drag  event  is  processed.   With  each drag event the VdtView is *
* cleared to the background color and the View Port rectangle is drawn in its *
* new position.  Then  the  still  image  containing  all the window icons is *
* composited on top of the current VdtView.  When a  mouse up is detected the *
* change in the View Port's  position  is   calculated  and a moveViewPortAbs *
* message is sent to effect translation in the view port position.  	      *
******************************************************************************/
- dragVdtViewPort

    /* Arguments */
    :(NXPoint*) startPos    /* Point in the VdtView where the mouseDown occurred */
    :(int)  	checkMask   /* Event mask needed to get drag events */

{   /* Local Variables */
    NXRect     dragViewPort;	/* VDT view port rectangle used in dragging */
    NXRect     dragBounds;  	/* Limiting rectangle to process drag events */
    NXPoint    deltaPoint;  	/* Delta X and Delta Y */
    int	       firstDrag=1; 	/* Flag to indicate first drag event detected */
    int	       keepDragging=1;	/* Flag to be cleared when dragging over */
    NXPoint    newPos;	        /* Latest point of mouse drag */
    NXEvent   *nextEvent;   	/* Latest event from the event queue */
    int	       panGridAdj;  	/* Integer for adjusting to PanGrid point */
    
/* BEGIN- dragVdtViewPort:: */

while ( keepDragging )
    {
    nextEvent = [NXApp getNextEvent:checkMask];

    if ( nextEvent->type == NX_MOUSEUP )
    	keepDragging = 0;

    else if ( nextEvent->type == NX_LMOUSEDRAGGED )
    	{
    	if ( firstDrag )
    	    {/* First detected drag, do setup for subsequent drags */ 
	    /* Calculate the drag restraint rectangle */
	    deltaPoint.x = startPos->x - IVviewPort.origin.x;
	    deltaPoint.y = startPos->y - IVviewPort.origin.y;
	    NXSetRect(&dragBounds, bounds.origin.x, bounds.origin.y,
	      	      bounds.size.width  - IVviewPort.size.width,
	      	      bounds.size.height - IVviewPort.size.height);

	    /* Create image of all stationary VDT objects durring ViewPort drag */
	    [IVvdtStillImage lockFocus];
	    [self renderVdt:0];
	    [IVvdtStillImage unlockFocus];

    	    /* Set up view port rectangle to be dragged */
    	    dragViewPort = IVviewPort;
    	    deltaPoint.x = deltaPoint.y = 0.0;

    	    [self lockFocus]; 	    /* Lock focus on VdtView */
    	    firstDrag = 0;
    	    }/*if*/

    	/* Translate the current event to the VdtView coordinate space */
    	newPos = nextEvent->location;
    	[self convertPoint:&newPos fromView:nil];

	/* Draw the VDT background and ViewPort */
	PSsetgray(NX_DKGRAY);
	NXRectFill(&bounds);

	/* Calculate where to draw the VDT View Port from drag position */
	deltaPoint.x = newPos.x - startPos->x;
	deltaPoint.y = newPos.y - startPos->y;
	dragViewPort.origin.x = IVviewPort.origin.x + deltaPoint.x;
	dragViewPort.origin.y = IVviewPort.origin.y + deltaPoint.y;

    	/* Adjust new position to nearest PanGrid increment if needed */
    	if ( IVvdtPanGrid.x != 1.0 && IVvdtPanGrid.y != 1.0 )
    	    {/* Adjust newPos to nearest grid point */
    	    panGridAdj = dragViewPort.origin.x / IVvdtPanGrid.x + 0.5;
    	    panGridAdj = panGridAdj * IVvdtPanGrid.x + 0.5;
    	    dragViewPort.origin.x = panGridAdj;
    	    panGridAdj = dragViewPort.origin.y / IVvdtPanGrid.y + 0.5;
    	    panGridAdj = panGridAdj * IVvdtPanGrid.y + 0.5;
    	    dragViewPort.origin.y = panGridAdj;
    	    }

    	/* Limit the new origin to the dragging bounds */
	if ( dragViewPort.origin.x < dragBounds.origin.x )
	    dragViewPort.origin.x = dragBounds.origin.x;
	if ( dragViewPort.origin.x > dragBounds.size.width )
	    dragViewPort.origin.x = dragBounds.size.width;
	if ( dragViewPort.origin.y < dragBounds.origin.y )
	    dragViewPort.origin.y = dragBounds.origin.y;
	if ( dragViewPort.origin.y > dragBounds.size.height )
	    dragViewPort.origin.y = dragBounds.size.height;

    	/* Draw the View Port rectangle */
	PSsetgray(NX_LTGRAY);
	NXRectFill(&dragViewPort);

	/* Then composite still image over background */
	[IVvdtStillImage composite:NX_SOVER toPoint:&bounds.origin];
	[window flushWindow];   // Needed else drawing is not shown 
    	}/*if*/

    }/*while*/

if ( !firstDrag )
    {/* The view port was dragged unlockFocus and move the View Port */
    [self unlockFocus];
    [self moveViewPortAbs:dragViewPort.origin.x * IVvdtScale
    	    	    	 :dragViewPort.origin.y * IVvdtScale];
    }

return(self);
}/* END- dragVdtViewPort:: */



/******************************************************************************
* INSTANCE METHOD:- dragVdtWindow::	    	    	    	    	      *
*   This method is called from the mouse down method when the user presses *
* MB1 with the CONTROL key down.  This method determines which window *
* represented in the VDT window that was clicked on.  If the click located a *
* valid window this method process the events needed to drag the window *
* representation about the VDT window.
******************************************************************************/
- dragVdtWindow

    /* Arguments */
    :(NXPoint*) startPos    /* Point in the VdtView where the mouseDown occurred */
    :(int)  	checkMask   /* Event mask needed to get drag events */

{   /* Local Variables */
    NXPoint    deltaPoint;  	/* Delta X and Delta Y */
    NXRect     dragBounds;  	/* Limiting rectangle to process drag events */
    NXRect     dragRect;	/* VDT view port rectangle used in dragging */
    int	       firstDrag=1; 	/* Flag to indicate first drag event detected */
    int	       keepDragging=1;	/* Flag to be cleared when dragging over */
    NXPoint    newPos;	        /* Latest point of mouse drag */
    NXEvent   *nextEvent;   	/* Latest event from the event queue */
    CTwinInfo *wiPtr=0;	    	/* Pointer to window info node of found window */
    
/* BEGIN- dragVdtWindow:: */

/* Locate the window by its scaled coordinates */
wiPtr = FindWinfoByScaledPoint(startPos, IVvdtWinNum,
    	    	    	       IVwinInfoList, IVwinInfoCount);

/* Loop until a mouseUp event is received */
while ( keepDragging )
    {
    nextEvent = [NXApp getNextEvent:checkMask];

    if ( nextEvent->type == NX_MOUSEUP )
    	keepDragging = 0;

    else if ( nextEvent->type == NX_LMOUSEDRAGGED && wiPtr )
    	{
    	if ( firstDrag )
    	    {/* First detected drag, do setup for subsequent drags */ 
	    /* Calculate the drag restraint rectangle */
	    deltaPoint.x = startPos->x - wiPtr->scaledRect.origin.x;
	    deltaPoint.y = startPos->y - wiPtr->scaledRect.origin.y;
	    NXSetRect(&dragBounds, bounds.origin.x, bounds.origin.y,
	      	      bounds.size.width  - wiPtr->scaledRect.size.width,
	      	      bounds.size.height - wiPtr->scaledRect.size.height);

	    /* Create image of all stationary VDT objects durring window drag */
	    [IVvdtStillImage lockFocus];
	    [self renderVdt:wiPtr->number];
	    [IVvdtStillImage unlockFocus];

    	    /* Set up view port rectangle to be dragged */
    	    dragRect = wiPtr->scaledRect;
    	    deltaPoint.x = deltaPoint.y = 0.0;

    	    [self lockFocus];
    	    firstDrag = 0;
    	    }/*if*/

    	newPos = nextEvent->location;
    	[self convertPoint:&newPos fromView:nil];

	/* Composite still image as background */
	[IVvdtStillImage composite:NX_COPY toPoint:&bounds.origin];

	/* Translate the drag window rectangle to the new drag position */
	deltaPoint.x = newPos.x - startPos->x;
	deltaPoint.y = newPos.y - startPos->y;
	dragRect.origin.x = wiPtr->scaledRect.origin.x + deltaPoint.x;
	if ( dragRect.origin.x < dragBounds.origin.x )
	    dragRect.origin.x = dragBounds.origin.x;
	if ( dragRect.origin.x > dragBounds.size.width )
	    dragRect.origin.x = dragBounds.size.width;
	dragRect.origin.y = wiPtr->scaledRect.origin.y + deltaPoint.y;
	if ( dragRect.origin.y < dragBounds.origin.y )
	    dragRect.origin.y = dragBounds.origin.y;
	if ( dragRect.origin.y > dragBounds.size.height )
	    dragRect.origin.y = dragBounds.size.height;

	/* Next draw the scaled window in the VDT window */
	PSsetgray(NX_WHITE);
	NXRectFill(&dragRect);
	PSsetgray(NX_BLACK);
	PSrectstroke(dragRect.origin.x, dragRect.origin.y,
		     dragRect.size.width, dragRect.size.height);

    	if ( IVwindowMotion == 1 )
    	    {/* Update the actual window position immediately */
    	    wiPtr->actualRect.origin.x = (dragRect.origin.x - IVviewPort.origin.x) * IVvdtScale;
    	    wiPtr->actualRect.origin.y = (dragRect.origin.y - IVviewPort.origin.y) * IVvdtScale;
    	    PswMoveWindow(wiPtr->number,
        	    	  wiPtr->actualRect.origin.x,
        	    	  wiPtr->actualRect.origin.y);
    	    }

	[window flushWindow];   // Needed else drawing is not shown 
    	}/*if*/

    }/*while*/

if ( !firstDrag )
    {/* The window was dragged unlockFocus and move the window */
    [self unlockFocus];
    wiPtr->scaledRect = dragRect;
    wiPtr->actualRect.origin.x = (dragRect.origin.x - IVviewPort.origin.x) * IVvdtScale;
    wiPtr->actualRect.origin.y = (dragRect.origin.y - IVviewPort.origin.y) * IVvdtScale;
    PswMoveWindow(wiPtr->number,
        	  wiPtr->actualRect.origin.x,
        	  wiPtr->actualRect.origin.y);
    [self updateVdtView:self];
    }

return(self);
}/* END- dragVdtWindow:: */



/******************************************************************************
* INSTANCE METHOD: -drawSelf::	    	    	    	    	    	      *
*   The drawSelf:: method  is  invoked  when  Vdt  is  launched.   It is also *
* invoked whenever a "display" message is received by this object.  	      *
******************************************************************************/
- drawSelf

    /* Arguments */
    :(NXRect*)rects 	/* List of rectangular regions needing redrawing */
    :(int)rectCount 	/* Number of rectangular regions in rects list */

{   /* Local Variables */

/* BEGIN drawSelf:: */
#ifdef DEBUG
printf("Executing 'drawSelf'\n");
#endif

[self renderVdt:-1];

return(self);
}/* END drawSelf:: */



/******************************************************************************
* INSTANCE METHOD:- mouseDown:	                                          *
*   Overrides the Responder super-super class method.  This method is invoked *
* each time a mouse button is pressed down inside VdtView.
******************************************************************************/
- mouseDown:(NXEvent*)theEvent

{   /* Local Variables */
    int	       checkMask;   	/* Mask of events to be checked */
    NXSize     jumpDelta;   	/* Amount to Jump View Port in X and Y */
    int	       mouseDown=1; 	/* Flag to clear when mousUp event received */
    NXEvent   *nextEvent;   	/* Next DPS event from the event queue */
    int	       oldMask;	    	/* Window event mask to restore when done */
    NXPoint    oldPos;	    	/* Position of original mouse down */
    CTwinInfo *wiPtr=NULL;    	/* Pointer to window info node of found window */

/* BEGIN- mouseDown: */

/* Make sure the window will deliver subsequent MouseUp&MouseDragged events */
oldMask   = [window eventMask];
checkMask = oldMask | NX_MOUSEUPMASK | NX_MOUSEDRAGGEDMASK;
[window setEventMask:checkMask];

/* Convert event location to VdtView coordinates */
oldPos = theEvent->location;
[self convertPoint:&oldPos fromView:nil];

/* Detetmine what action to perform according to modifier keys */
if ( (theEvent->flags & NX_ALTERNATEMASK) )
    {/* Performing a stick/unstick operation */
#ifdef DEBUG
    printf("VdtView- mouseDown NX_ALTERNATEMASK\n");
#endif
    if ( NXPointInRect(&oldPos, &IVviewPort) )
    	{/* Only allowed to stick windows within ViewPort */
        wiPtr = FindWinfoByScaledPoint(&oldPos, IVvdtWinNum,
                               	       IVwinInfoList, IVwinInfoCount);
        if ( wiPtr )
    	    {/* The click was within a window icon */
    	    if ( wiPtr->sticky )
    	    	/* The window is marked sticky so unstick it */
    	    	[self stickyListDelete:wiPtr->number];
    	    else
    	    	/* Window isn't currently sticky so mark it as sticky */
    	    	[self stickyListAdd:wiPtr->number];
    	    }/*if*/
    	}/*if*/
    }/*if*/

else if ( (theEvent->flags & NX_COMMANDMASK) )
    {/* Performing a order window down operation */
    wiPtr = FindWinfoByScaledPoint(&oldPos, IVvdtWinNum,
                               	   IVwinInfoList, IVwinInfoCount);
    if ( wiPtr )
    	PswOrderWindow(MKpswBelow, 0, wiPtr->number);
    }

else if ( (theEvent->flags & NX_CONTROLMASK) )
    {/* Performing a move window operation */
    [self dragVdtWindow:&oldPos :checkMask];
    mouseDown = 0;    
    }

else if ( (theEvent->flags & NX_SHIFTMASK) )
    {/* Performing a order up operation */
    wiPtr = FindWinfoByScaledPoint(&oldPos, IVvdtWinNum,
                               	   IVwinInfoList, IVwinInfoCount);
    if ( wiPtr )
    	PswOrderWindow(MKpswAbove, 0, wiPtr->number);
    }

else if ( IVvdtPanJump )
    {/* Then attempt to center View Port about mouse position */
    jumpDelta.width  = oldPos.x -
    	    	       (IVviewPort.origin.x + IVviewPort.size.width/2.0);
    jumpDelta.width  *= IVvdtScale;
    jumpDelta.height = oldPos.y -
    	    	       (IVviewPort.origin.y + IVviewPort.size.height/2.0);
    jumpDelta.height *= IVvdtScale;
    [self moveViewPortRel:jumpDelta.width :jumpDelta.height];

    /* Now begin normal View Port dragging from this point */
    [self dragVdtViewPort:&oldPos :checkMask];
    mouseDown = 0;    
    }

else if ( NXPointInRect(&oldPos, &IVviewPort) )
    {/* Performing a move ViewPort operation */
    [self dragVdtViewPort:&oldPos :checkMask];
    mouseDown = 0;    
    }


/* Now take over event loop until a mouseUp event is detected */
while ( mouseDown )
    {
    nextEvent = [NXApp getNextEvent:checkMask];
    if ( nextEvent->type == NX_MOUSEUP )
    	/* A mouseUp event was received, terminate loop */
    	mouseDown = 0;
    }/*while*/

/* Restore the previous event mask */
[window setEventMask:oldMask];

/* Update the VDT window to reflect the current changes */
[self updateVdtView:self];

return(self);
}/* END mouseDown: */



/******************************************************************************
* INSTANCE METHOD:- moveViewPortAbs:	    	    	    	    	      *
*   This method simulates moving the VDT View Port to a new absolute position.
******************************************************************************/
- moveViewPortAbs

    /* Arguments */
    :(float)newX
    :(float)newY

{   /* Local Variables */
    int	    	 index;
    float   	 oldX;
    float   	 oldY;
    float   	 deltaX;
    float   	 deltaY;
    CTwinInfo	*wiPtr;	    	/* Pointer to node in IVwinInfoList */

/* BEGIN moveViewPortAbs */
[self updateWindowList];

/* Calculate the current ViewPort origin in VDT coordinates */
oldX = IVviewPort.origin.x * IVvdtScale;
oldY = IVviewPort.origin.y * IVvdtScale;

/* Make sure the desired origin is within the VDT limits */
if ( newX < 0.0 )
    newX = 0.0;
if ( newX > IVvdtVirtualSize.width - IVscreenSize.width )
    newX = IVvdtVirtualSize.width - IVscreenSize.width;
if ( newY < 0.0 )
    newY = 0.0;
if ( newY > IVvdtVirtualSize.height - IVscreenSize.height )
    newY = IVvdtVirtualSize.height - IVscreenSize.height;

/* Calculate the X,Y delta to be applied to each window to simulate **
** the ViewPort movement.   	    	    	    	    	    */
deltaX = oldX - newX;
deltaY = oldY - newY;

/* Translate the VDT ViewPort origin to the VDT window scale */
IVviewPort.origin.x = newX / IVvdtScale;
IVviewPort.origin.y = newY / IVvdtScale;

/* Now run through the window list moving each window by deltaX,deltaY */
for (index = 0; index < IVwinInfoCount; index++)
    {
    wiPtr = (IVwinInfoList+index);
    if ( wiPtr->number != IVvdtWinNum &&
    	 wiPtr->sticky == 0 && wiPtr->level >= 0 )
    	{/* This is not the VDT window so we move it */
    	wiPtr->actualRect.origin.x += deltaX;
    	wiPtr->actualRect.origin.y += deltaY;

        PswMoveWindow(wiPtr->number,
        	      wiPtr->actualRect.origin.x,
        	      wiPtr->actualRect.origin.y);
    	}
    }/*for*/    

/* Redraw the VDT Window to reflect the window movement */
[self updateVdtView:self];

return(self);
}/* END moveViewPortAbs */



/******************************************************************************
* INSTANCE METHOD:- moveViewPortRel:	    	    	    	    	      *
*   This method simulates moving the VDT View Port relative to its current *
* position.
******************************************************************************/
- moveViewPortRel

    :(float)deltaX
    :(float)deltaY

{   /* Local Variables */
    int	    	 index;
    float   	 oldX;
    float   	 oldY;
    float   	 newX;
    float   	 newY;
    CTwinInfo	*wiPtr;	    	/* Pointer to node in IVwinInfoList */

/* BEGIN moveViewPortRel */
[self updateWindowList];

oldX = IVviewPort.origin.x * IVvdtScale;
oldY = IVviewPort.origin.y * IVvdtScale;
newX = oldX + deltaX;
newY = oldY + deltaY;
if ( newX < 0.0 )
    newX = 0.0;
if ( newX > IVvdtVirtualSize.width - IVscreenSize.width )
    newX = IVvdtVirtualSize.width - IVscreenSize.width;
if ( newY < 0.0 )
    newY = 0.0;
if ( newY > IVvdtVirtualSize.height - IVscreenSize.height )
    newY = IVvdtVirtualSize.height - IVscreenSize.height;

deltaX = oldX - newX;
deltaY = oldY - newY;
IVviewPort.origin.x = newX / IVvdtScale;
IVviewPort.origin.y = newY / IVvdtScale;

/* Now run through the window list moving each window by deltaX,deltaY */
for (index = 0; index < IVwinInfoCount; index++)
    {
    wiPtr = IVwinInfoList+index;
    if ( wiPtr->number != IVvdtWinNum &&
         wiPtr->sticky == 0 && wiPtr->level >= 0 )
    	{
    	wiPtr->actualRect.origin.x += deltaX;
    	wiPtr->actualRect.origin.y += deltaY;

        PswMoveWindow(wiPtr->number,
        	      wiPtr->actualRect.origin.x,
        	      wiPtr->actualRect.origin.y);
    	}
    }/*for*/    

/* Redraw the VDT Window to reflect the window movement */
[self updateVdtView:self];

return(self);
}/* END moveViewPortRel */



/******************************************************************************
* INSTANCE METHOD:- renderVdt:		    	    	    	    	      *
*   This method is used to perform all the drawing of the VDT window.
******************************************************************************/
- renderVdt

    /* Arguments */
    :(int) outWin    	/* Window number of window not to be drawn */

{   /* Local Variables */
    float   	 borderColor;	/* Color to draw window icon border */
    float   	 fillColor;	/* Color to draw window icon interior */
    int	    	 index;  	/* Loop Variable */
    CTwinInfo	*wiPtr;	    	/* Pointer to node in IVwinInfoList */

/* BEGIN- renderVdt: */

if ( outWin == 0 )
    {/* We are rendering the still image for a dragVdtViewPort */
    /* Clear the still image for SOVER composite */
    PScompositerect (bounds.origin.x, bounds.origin.y,
    	    	     bounds.size.width, bounds.size.height, NX_CLEAR);
    }

else
    {/* We are rendering for a still image for a dragVdtWindow, or we **
     ** are rendering for a normal drawSelf.   	    	    	      */
    /* First fill the entire Vdt View to Dark Grey */
    PSsetgray(NX_DKGRAY);
    NXRectFill(&bounds);

    /* Next draw the the current view port as a light gray rectangle */
    PSsetgray(NX_LTGRAY);
    NXRectFill(&IVviewPort);
    }

/* Now run through the window list bottom to top drawing a scaled rectangle */
/* of each window in the list.	    	    	    	    	    	    */
PSsetlinewidth(0.0);
for (index = IVwinInfoCount - 1; index > 0; index--)
    {
    wiPtr = IVwinInfoList+index;

    if ( wiPtr->number != outWin &&
  	 wiPtr->number != IVvdtWinNum && wiPtr->level  >= 0 )
    	{/* Window NOT to be skipped, NOT VDT window, NOT background window */
    	if ( wiPtr->sticky )
    	    {/* Set colors for sticky window */
    	    borderColor = NX_WHITE;
    	    fillColor   = NX_BLACK;
    	    }
    	else
    	    {/* Set colors for normal window */
    	    borderColor = NX_BLACK;
    	    fillColor   = NX_WHITE;
    	    }

	PSsetgray(fillColor);
	NXRectFill(&wiPtr->scaledRect);
	PSsetgray(borderColor);
	PSrectstroke(wiPtr->scaledRect.origin.x, wiPtr->scaledRect.origin.y,
		     wiPtr->scaledRect.size.width, wiPtr->scaledRect.size.height);
    	}
    }/*for*/    

return(self);
}/* END- renderVdt: */



/******************************************************************************
* INSTANCE METHOD:- retrieveDock:        	    	    	    	      *
*   This method moves all the  Dock  Icons back within the current View Port. *
* The first step is to  find  the  Dock  Icon  with the largest Y coordinate. *
* This is used to define  the  deltaY  needed  to reposition each of the Dock *
* Icons.  The top Dock  Icon  is  repositioned to the top right corner of the *
* screen and all the other Dock  Icons  are  moved  to the same relative Dock *
* Positions.	    	    	    	    	    	    	    	      *
******************************************************************************/
- retrieveDock:sender

{   /* Local Variables */
    int	    	 index;	    /* Loop Variable */
    float   	 deltaY;    /* Amount to change Dock Icons Y Origin */
    float   	 newX;	    /* New X to move Dock Icon to */
    float   	 newY;	    /* New Y to move Dock Icon to */
    float   	 topY;	    /* Y coordinate of the highest Dock Icon */
    CTwinInfo	*topWiPtr;  /* Pointer to top Dock Icon IVwinInfoList node */
    CTwinInfo	*wiPtr;	    /* Pointer to node in IVwinInfoList */

/* BEGIN-retrieveDock: */

/* Make sure the Window Information List is accurate */
[self updateWindowList];

/* Locate the Dock Icon with the largest Y origin */
topY     = -1000000.0;
topWiPtr = NULL;
for (index = 0; index < IVwinInfoCount; index++)
    {
    wiPtr = IVwinInfoList+index;

    if ( wiPtr->level == NX_DOCKLEVEL && wiPtr->actualRect.origin.y > topY )
    	{/* This is a Dock Icon and it is higher than any so far */
    	topY     = wiPtr->actualRect.origin.y;
    	topWiPtr = wiPtr;
    	}
    }/*for*/

/* Calculate Y delta to move all dock icons by */
if ( topWiPtr )
    {/* We have found a top Dock Icon so calculate Y delta to apply */
    newY   = IVscreenSize.height - 64.0;
    deltaY = newY - topWiPtr->actualRect.origin.y;
    }
else
    deltaY = 0.0;

/* Now go through the Window List and move each Dock Icon */
newX = IVscreenSize.width - 67.0;
for (index = 0; index < IVwinInfoCount; index++)
    {
    wiPtr = IVwinInfoList+index;

    if ( wiPtr->level == NX_DOCKLEVEL )
    	{/* This is a Dock Icon so calculate new position and move it */
    	newY = wiPtr->actualRect.origin.y + deltaY;
        PswMoveWindow(wiPtr->number, newX, newY);
    	}
    }/*for*/

[self updateVdtView:self];
return(self);
}/* END-retrieveDock: */



/******************************************************************************
* INSTANCE METHOD:- returnDock:	        	    	    	    	      *
*   This method moves all  the  Dock  Icons back to VDT Origin position.  The *
* first step is to  find  the  Dock Icon with the largest Y coordinate.  This *
* is used to define the deltaY  needed  to reposition each of the Dock Icons. *
* The top Dock Icon is repositioned to the top right corner of the VDT Origin *
* relative to the current screen  and  all  the other Dock Icons are moved to *
* the same relative Dock Positions.	    	    	    	      	      *
******************************************************************************/
- returnDock:sender

{   /* Local Variables */
    int	    	 index;	    /* Loop Variable */
    float   	 deltaY;    /* Amount to change Dock Icons Y Origin */
    float   	 newX;	    /* New X to move Dock Icon to */
    float   	 newY;	    /* New Y to move Dock Icon to */
    float   	 topY;	    /* Y coordinate of the highest Dock Icon */
    CTwinInfo	*topWiPtr;  /* Pointer to top Dock Icon IVwinInfoList node */
    CTwinInfo	*wiPtr;	    /* Pointer to node in IVwinInfoList */

/* BEGIN-returnDock: */

if ( IVdockHandling == 0 )
    /* The Dock handling is sticky disable this function */
    /* NOTE: Should disable menu cell as well */
    return(self);

/* Make sure the Window Information List is accurate */
[self updateWindowList];

/* Locate the Dock Icon with the largest Y origin */
topY     = -1000000.0;
topWiPtr = NULL;
for (index = 0; index < IVwinInfoCount; index++)
    {
    wiPtr = IVwinInfoList+index;

    if ( wiPtr->level == NX_DOCKLEVEL && wiPtr->actualRect.origin.y > topY )
    	{/* This is a Dock Icon and it is higher than any so far */
    	topY     = wiPtr->actualRect.origin.y;
    	topWiPtr = wiPtr;
    	}
    }/*for*/

/* Calculate Y delta to move all dock icons by */
if ( topWiPtr )
    {/* We have found a top Dock Icon so calculate Y delta to apply */
    newX = IVscreenSize.width - 67.0 +
	   IVvdtScale * (IVvdtOrigin.x - IVviewPort.origin.x);
    newY = IVscreenSize.height - 64.0 +
	   IVvdtScale * (IVvdtOrigin.y - IVviewPort.origin.y);
    deltaY = newY - topY;
    }
else
    return(self);

/* Now go through the Window List and move each Dock Icon */
for (index = 0; index < IVwinInfoCount; index++)
    {
    wiPtr = IVwinInfoList+index;

    if ( wiPtr->level == NX_DOCKLEVEL+1 )
    	{/* This is a Dock Icon so calculate new position and move it */
    	newY = wiPtr->actualRect.origin.y + deltaY;
        PswMoveWindow(wiPtr->number, newX, newY);
    	}
    }/*for*/

[self updateVdtView:self];
return(self);
}/* END-returnDock: */



/******************************************************************************
* INSTANCE METHOD:- setVdtPanGrid:       	    	    	    	      *
*   This method sets the  mouse  panning  grid  increment in both the X and Y *
* directions.  The X and Y  inrecrements  are calculated based on the panGrid *
* value.  The panGrid value  expresses what percentage of the physical screen *
* width and height to use as  the  X  increment and Y increment respectively. *
* If either value is less than 1.0 it is reset to 1.0 since the smallest grid *
* is limited to a single pixel in the VDT window.   	    	    	      *
******************************************************************************/
- setVdtPanGrid

    /* Arguments */
    :(int)panGrid   	/*I* Percentage of screen to use as grid increment */

{   /* Local Variables */
    NXSize  screenSize;

/* BEGIN-setVdtPanGrid: */
[NXApp getScreenSize:&screenSize];

if ( panGrid > 0 )
    {
    IVvdtPanGrid.x   = (screenSize.width  * (panGrid / 100.0)) / IVvdtScale;
    if ( IVvdtPanGrid.x < 1.0 )
    	IVvdtPanGrid.x = 1.0;
    IVvdtPanGrid.y   = (screenSize.height * (panGrid / 100.0)) / IVvdtScale;
    if ( IVvdtPanGrid.y < 1.0 )
    	IVvdtPanGrid.y = 1.0;
    }
else
    IVvdtPanGrid.x = IVvdtPanGrid.y = 1.0;

return(self);
}/* END-setVdtPanGrid: */



/******************************************************************************
* INSTANCE METHOD:- setVdtPanJump:       	    	    	    	      *
*   This method changes sets  the  IVvdtPanJump  flag which determines if Pan *
* Jumps should occur on an unmodified mouse click.  	    	    	      *
******************************************************************************/
- setVdtPanJump

    /* Arguments */
    :(int)panJump   	/*I* Flag to indicate if Pan Jump is on or off  */

{   /* Local Variables */

/* BEGIN setVdtPanJump: */
IVvdtPanJump = panJump;
return(self);
}/* END setVdtPanJump: */



/******************************************************************************
* INSTANCE METHOD:- setVdtWindowNumber:       	    	    	    	      *
******************************************************************************/
- setVdtWindowNumber:(int)windowNum

{/* BEGIN setVdtWindowNumber: */
IVvdtWinNum = windowNum;
return(self);
}/* END setVdtWindowNumber: */



/******************************************************************************
* INSTANCE METHOD:- setViewDockHandling:       	    	    	    	      *
*   This sets the  dock  handling  according  to  the  Preference  panel Dock *
* Handling selection.   Setting the  Dock  handling to Sticky causes the Dock *
* Icons windows to be marked  as sticky by the updateWindowList method.  This *
* will cause all the dock icons to be treated as Sticky windows.    	      *
******************************************************************************/
- setViewDockHandling

    /* Arguments */
    :(int)tagValue  	/* Tag value of the PrefPanel DockHandling selection */

{/* BEGIN-setViewDockHandling: */
IVdockHandling = tagValue;
if ( IVdockHandling  == 0 )
    [self retrieveDock:self];
else
    [self updateVdtView:self];
return(self);
}/* END-setViewDockHandling: */



/******************************************************************************
* INSTANCE METHOD:- setViewWindowMotion:       	    	    	    	      *
*   This sets the window motion  option  according  to  the  Preference panel *
* Window Motion selection.  Setting the Window Motion to Immediate means that *
* the window associated  with  VDT  window  icon  being  dragged  is moved in *
* tandem.  If set to  Deferred  the actual window is not moved until the drag *
* of the VDT window icon is finished.  	    	    	    	    	      *
******************************************************************************/
- setViewWindowMotion

    /* Arguments */
    :(int)tagValue  	/* Tag value of the PrefPanel WindowMotion selection */

{/* BEGIN-setViewWindowMotion: */
IVwindowMotion = tagValue;
return(self);
}/* END-setViewDockHandling: */



/******************************************************************************
* INSTANCE METHOD:- sizeTo::	        	    	    	    	      *
*   This method overrides the Views method to detect to what size the VDT *
* view being changed to. *
******************************************************************************/
- sizeTo

    /* Arguments */
    :(NXCoord)width
    :(NXCoord)height

{/* BEGIN- sizeTo:: */
[super sizeTo:width :height];

/* Update the virtual size of the VDT */
IVvdtVirtualSize.width  = bounds.size.width  * IVvdtScale;
IVvdtVirtualSize.height = bounds.size.height * IVvdtScale;

/* Resize the dragging Still image to the same size as the VdtView */
[IVvdtStillImage setSize:&bounds.size];

return(self);
}/* END- sizeTo:: */



/******************************************************************************
* INSTANCE METHOD:- stickyListAdd:	    	    	    	    	      *
*   This function adds a new  window  number  to the sticky window table.  If *
* not used the table is created.  If insufficient space is available a larger *
* table is created.   This  method returns self for a succesfull add, and nil *
* otherwise.	 	    	    	    	    	    	    	      *
******************************************************************************/
- stickyListAdd:(int)windowNum

{   /* Local Variables */
    int	    index;  	    /* A loop variable */
    int	   *newTable;	    /* Pointer to newly enlarged sticky table */

/* BEGIN-stickyListAdd: */
if ( IVstickyTable == NULL )
    {/* The sticky table is unused, Initially set up the sticky table */
    IVstickyTable = (int*)malloc(sizeof(int)*32);
    if ( IVstickyTable )
    	{
    	IVstickySize     = 32;
    	IVstickyCount    = 0;
    	}
    else
    	return(nil);
    }

else if ( IVstickyCount >= IVstickySize )
    {/* No room left in sticky table, try compressing then expanding */

    /* Delete any long dead windows from the table */
    [self stickyListPurge];

    if ( IVstickyCount >= IVstickySize )
    	{/* Still no room, so increase the table size */
    	newTable = (int*)malloc(sizeof(int)*(IVstickySize+32));
    	if ( newTable )
    	    {/* Copy old table and add new entry */
    	    for (index = 0; index < IVstickySize; index++)
    	    	newTable[index] = IVstickyTable[index];
    	    IVstickySize += 32;
    	    free((char*)IVstickyTable);
    	    IVstickyTable = newTable;
    	    }
    	else
    	    return(nil);
    	}/*if*/
    }/*elseif*/

/* Add the new entry */
IVstickyTable[IVstickyCount++] = windowNum;

return(self);
}/* END-stickyListAdd: */



/******************************************************************************
* INSTANCE METHOD:- stickyListDelete:	    	    	    	    	      *
*   This method removes a window  number  from the current sticky table.  The *
* list elements that follow  the  deleted  entry are moved up in the table so *
* that new entries can always  be  added  to the end of the list.  The method *
* returns self for a successful deletion, and nil otherwise.    	      *
******************************************************************************/
- stickyListDelete

    /* Arguments */
    :(int)windowNum 	/*I* Window number to be removed from table */

{   /* Local Variables */
    int	    findIndex;	    /* Sticky Table position of entry to delete */
    int	    index;  	    /* A loop variable */

/* BEGIN-stickyListDelete: */
if ( IVstickyTable != NULL )
    {/* There is a table to search */

    /* First search the table to find the supplied window number */
    findIndex = -1;
    index     = 0;
    while ( index < IVstickyCount && findIndex == -1)
    	{
    	if ( IVstickyTable[index] == windowNum )
    	    findIndex = index;
    	else
    	    index++;
    	}/*while*/

    if ( findIndex != -1 )
    	{/* The entry was located, move all subsequent entries up one */
    	for ( index = findIndex; index < IVstickyCount; index++)
    	    IVstickyTable[index] = IVstickyTable[index+1];
    	IVstickyCount--;
    	}
    else
    	/* The entry was not found in the table, return failure status */
    	return(nil);
    }/*if*/

else
    /* The table is non-existant, return failure status */
    return(nil);

return(self);
}/* END-stickyListDelete: */



/******************************************************************************
* INSTANCE METHOD:- stickyListPurge	    	    	    	    	      *
*   This method removes from  the  list any window numbers that are no longer *
* DPS servers global window list.   This  method first gets a copy of the DPS *
* servers global  window  list.   Then  each  entry  in  the  sticky table is *
* searched for in the global  window  list.  If the window is not found it is *
* marked for deletion.   After  the  entire  sitcky  list  is  verified it is *
* scanned to remove any marked entries.	    	    	    	    	      *
******************************************************************************/
- stickyListPurge

{   /* Local Variables */
    int	    	 found;	    	/* Indicates if sticky entry in globalList */
    int	    	 globalCount;   /* Number of current DPS server windows */
    int	    	 globalIndex;	/* Loop Variable to scan globalList */
    int	    	*globalList;	/* Top to bottom list of windows in server */
    int	    	 stickyIndex;  	/* Loop Variable to scan sticky table */

/* BEGIN stickyListPurge */

/* Get the number of windows in the DPS servers global list */
PScountwindowlist(0, &globalCount);

/* Now retrieve the list of global window numbers */
globalList = (int*) malloc(globalCount*sizeof(int));
PSwindowlist(0, globalCount, globalList);

/* Validate each entry in sticky table is in the global window list */
for (stickyIndex = 0; stickyIndex < IVstickyCount; stickyIndex++)
    {
    /* Search for sticky table entry in global window list */
    globalIndex = found = 0;
    while ( !found && globalIndex < globalCount )
    	{
    	if ( globalList[globalIndex] == IVstickyTable[stickyIndex] )
    	    found = 1;
    	}

    if ( !found )
    	/* If the entry wasn't found mark it for deletion */
    	IVstickyTable[stickyIndex] = -1;
    }/*for*/

/* Were done with the global list, so free it */
free((char*)globalList);

/* Now rescan the sticky table and remove any marked entries (== -1) */
globalIndex = 0;    	/* Reuse globalXxx vars as purgeXxx var */
for (stickyIndex = 0; stickyIndex < IVstickyCount; stickyIndex++)
    {
    if ( IVstickyTable[stickyIndex] != -1 )
    	/* This entry wasn't marked for deletion */
    	IVstickyTable[globalIndex++] = IVstickyTable[stickyIndex];
    }
IVstickyCount = globalIndex;

return(self);
}/* END-stickyListPurge */



/******************************************************************************
* INSTANCE METHOD:- updateVdtView:	    	    	    	    	      *
*   This method updates the  window  information  list and then sends display *
* message to execute the drawSelf method to update the VdtView.	    	      *
******************************************************************************/
- updateVdtView:sender

{/* BEGIN updateVdtView: */
[self updateWindowList];
[self display];
return(self);
}/* END updateVdtView: */



/******************************************************************************
* INSTANCE METHOD:- updateWindowList	    	    	    	    	      *
******************************************************************************/
- updateWindowList

{   /* Local Variables */
    int	    	 index;  	/* Loop Variable */
    int	    	 screenCount;   /* Number of current on screen windows */
    int	    	*screenList;	/* Top to bottom list of windows on screen */
    int	    	 stickyIndex;	/* Loop variable for sticky table */
    CTwinInfo	*wiPtr;	    	/* Pointer to node in IVwinInfoList */

/* BEGIN updateWindowList */

/* Get the number of on screen windows.  This doesn't mean that the window */
/* is visible within the  physical  screen  viewport,  but  rather that is */
/* the root window. 	    	    	    	    	    	    	   */
PScountscreenlist(0, &screenCount);

/* Now retrieve the list of global window numbers ordered from top to bottom */
screenList = (int*) malloc(screenCount*sizeof(int));
PSscreenlist(0, screenCount, screenList);
IVvdtRootWinNum = screenList[screenCount-1];

/* Ignore the bottom most window as it is not kosher */
screenCount--;

/* Allocate sufficient space to hold needed Info for each on screen window */
if ( IVwinInfoList == NULL )
    {/* This is the first update, allocate an initial list */
    IVwinInfoList = (CTwinInfo*) malloc(screenCount*sizeof(CTwinInfo));
    }
    
else if ( screenCount > IVwinInfoCount )
    {/* Need more space for the increased number of windows */
    free((void*)IVwinInfoList);
    IVwinInfoList = (CTwinInfo*) malloc(screenCount*sizeof(CTwinInfo));
    }

IVwinInfoCount = screenCount;

/* Now get the position and size of each window in the list storing the data */
/* the IVwinInfoList.   A pswrap function is used because the window numbers */
/* in the screenList are only valid in the global DPS context.	    	     */
for (index = 0; index < IVwinInfoCount; index++)
    {
    wiPtr = IVwinInfoList + index;
    wiPtr->number = screenList[index];
    wiPtr->sticky = 0;
    PswCurrentOwner(wiPtr->number, &wiPtr->context);
    PswCurrentWindowLevel(wiPtr->number, &wiPtr->level);
    PswCurrentWindowBounds(screenList[index],
    	    	    	   &wiPtr->actualRect.origin.x,
    	    	    	   &wiPtr->actualRect.origin.y,
    	    	    	   &wiPtr->actualRect.size.width,
    	    	    	   &wiPtr->actualRect.size.height);

    wiPtr->scaledRect.origin.x = wiPtr->actualRect.origin.x / IVvdtScale +
    	    	    	    	 IVviewPort.origin.x;
    wiPtr->scaledRect.origin.y = wiPtr->actualRect.origin.y / IVvdtScale +
    	    	    	    	 IVviewPort.origin.y;
    wiPtr->scaledRect.size.width  = wiPtr->actualRect.size.width / IVvdtScale;
    wiPtr->scaledRect.size.height = wiPtr->actualRect.size.height / IVvdtScale;

    /* Determine if the window is sticky */
    if ( IVdockHandling == 0 && wiPtr->level == NX_DOCKLEVEL )
    	/* This is a dock icon and DockHandling is set to sticky */
    	wiPtr->sticky = 1;
    else
    	{/* Check if the window is in the sticky table */
    	stickyIndex = 0;
    	while ( !wiPtr->sticky && stickyIndex < IVstickyCount )
    	    {
    	    if ( wiPtr->number == IVstickyTable[stickyIndex++] )
    	    	wiPtr->sticky = 1;
    	    }
    	}
    }/*for*/

free((void*)screenList);

return(self);
}/* END updateWindowList */



- Test_2:sender
{
    int	above;
    int below;
PswWhatsAbove(&above);
PswWhatsBelow(&below);
printf("VdtView-Test_2 Above=%d Below=%d\n", above, below);
return(self);
}

- Test_1:sender

{   /* Local Variables */
    int	    	 level;
    int	    	 index;
    float   	 moveToX;
    float   	 moveToY;
    int	    	 winToMove;
    int     	 status;
    CTwinInfo	*wiPtr;	    	/* Pointer to node in IVwinInfoList */
    static id	 speaker=nil;

/* BEGIN Test_1: */
[self updateWindowList];

IVvdtWinNum = [window getGlobalWindowNum];
printf("IVvdtWinNum local=%d global=%d\n",
       [window windowNum], IVvdtWinNum);

printf("Screen List\n");
for (index = 0; index < IVwinInfoCount; index++)
    {
    wiPtr = IVwinInfoList + index;
    PswCurrentWindowLevel(wiPtr->number, &level);
    printf("screenList[%3d]=%4d (%2.1f,%2.1f) [%2.1f,%2.1f] L:%d\n",
    	   index, wiPtr->number,
    	   wiPtr->actualRect.origin.x, wiPtr->actualRect.origin.y,
    	   wiPtr->actualRect.size.width, wiPtr->actualRect.size.height,
    	   level);
    }/*for*/

printf("Window to move: ");
scanf("%d", &winToMove);
if ( winToMove != -1 )
    {
    printf("Position: ");
    scanf("%f %f", &moveToX, &moveToY);
    printf("Moving Window %d to (%2.1f,%2.1f)\n", winToMove, moveToX, moveToY);
    PswMoveWindow(winToMove, moveToX, moveToY);

    if ( speaker == nil )
    	{
    	speaker = [NXApp appSpeaker];
        [speaker setSendPort:NXPortFromName(NX_WORKSPACEREQUEST, NULL)];
    	}
    status = [speaker performRemoteMethod:"updateViewers"];    
    printf("[speaker performRemoteMethod:'updateViewers'] = %d\n", status);
    status = [speaker performRemoteMethod:"update"];    
    printf("[speaker performRemoteMethod:'update'] = %d\n", status);
    }

return(self);
}/* END Test_1: */

@end /* VdtView */



/******************************************************************************
* FUNCTION: FindWinfoByScaledPoint	    	    	    	    	      *
*   This function locates a Window Information list entry by returning a *
* pointer to the first entry that has a scaledRect that contains the supplied *
* point.    	    	    	    	    	    	    	    	      *
******************************************************************************/
static CTwinInfo *FindWinfoByScaledPoint

    (/* Arguments */
    NXPoint   *scaledPoint,
    int	       ignoreWindow,
    CTwinInfo *wiPtr,
    int	       wiCount)

{   /* Local Variables */
    int	    index;

/* BEGIN FindWinfoByScaledPoint */
for (index = 0; index < wiCount; index++)
    {
    if ( wiPtr->number != ignoreWindow && wiPtr->level >= 0 &&
    	 NXPointInRect(scaledPoint, &wiPtr->scaledRect) )
    	return(wiPtr);
    wiPtr++;
    }

return((CTwinInfo*)0);
}/* END FindWinfoByScaledPoint */



#ifdef PAN_GALACTIC_GARGLE_BLASTER
/******************************************************************************
* FUNCTION: FindWinfoByWindowNum	    	    	    	    	      *
*   This function locates a Window Information list entry by returning a *
* pointer to the first entry that has a number equal to the supplied window *
* number.
******************************************************************************/
static CTwinInfo *FindWinfoByWindowNum

    (/* Arguments */
    int	       windowNum,
    CTwinInfo *wiPtr,
    int	       wiCount)

{   /* Local Variables */
    int	    index;

/* BEGIN FindWinfoByWindowNum */
for (index = 0; index < wiCount; index++)
    {
    if ( wiPtr->number == windowNum )
    	return(wiPtr);
    wiPtr++;
    }

fprintf(stderr,"ERROR: VdtView:FindWinfoByWindowNum failed for %d\n", windowNum);
return((CTwinInfo*)0);
}/* END FindWinfoByWindowNum */
#endif