[comp.sys.next] dragging an image from a button

pegasus@quiche (PROVOST philippe) (07/02/90)

greetings,

does anyone know how to "drag an image from a button" , ie, when you select
the button with a mouse, how to move the "picture" representing the object (being
created) from the button to the document. For instance, IB do this operation
when you select a button, window....in the Palettes Window and drag that tool to 
the document you define. 

Help greatly appreciated.

duggie@jessica.stanford.edu (Doug Felt) (07/03/90)

In article <3645@calvin.cs.mcgill.ca> pegasus@quiche.cs.mcgill.ca (PROVOST philippe) writes:
>
>greetings,
>
>does anyone know how to "drag an image from a button" , ie, when you select
>the button with a mouse, how to move the "picture" representing the object (being
>created) from the button to the document. For instance, IB do this operation
>when you select a button, window....in the Palettes Window and drag that tool to 
>the document you define. 
>
>Help greatly appreciated.

What you do is create a window where the object was, make it look like
the object, and drag that.  When you release the window, you create a
new object where the window was released.

Many months ago, playing around with this, I wrote a View subclass
which does this for its subviews.  That is, when you click in this
view, it hit tests against its subviews to see whether one was hit.
If one was, it creates a window of the same size and in the same
(screen) position as the view, clones the view and installs it in the
window, then calls the window method dragFrom::eventNum: to let the
user drag the window.  After the window is released, it checks to see
what window and view it was on top of, and whether the view
understands what to do with a new view.  If it does then it sends the
receiving view a message with the cloned view as a parameter.  The
receiving view cleans up.

The nice thing about this technique is that the views that get dragged
don't have to be subclasses of anything in particular, and the
receiving view just has to understand the message used to pass it a
new view.

There are some caveats.  I don't use a window delegate to receive
notification when the dragFrom::eventNum: method returns, because,
unfortunately, a windowDidMove: message never gets sent if the user
never actually moves the window.  Fortunately, dragFrom::eventNum: is
modal and won't return until the window is dropped.  If the window
gets 'dropped' over the deskTop, PSfindwindow will return the majik
number 14, which you need to check for because NXConvertGlobalToWinNum
will gag on this.  So this may not be compatible with future releases.

Have fun.

Doug Felt


Following is the (slightly messy) relevant code:

--------------------------- ( view that receives ) -----------------------

- acceptNewView: aView
{
	[self addSubview: aView];
	[aView display];
	[window flushWindow];
	NXPing();
	return self;
}

------------- ( view that allows its subviews to be dragged ) ------------

/*
 * we want the hitTest: message
 */
- (BOOL)acceptsFirstMouse 
{
	return YES;
}

/*
 * override default so subviews don't get a chance to respond
 */
- hitTest:(NXPoint *)pt
{
	if (NXPointInRect(pt,&frame))
		return self;
	return nil;
}

/*
 * point is in window coordinates.  return the view to clone, or nil.
 */
- getSelectedView:(const NXPoint *)pt
{
int i;
NXPoint loc;
NXRect r;
View *v;

	loc = *pt;
	[self convertPoint: &loc fromView: nil];
	for (i=[subviews count]; i--;) {
		v = [subviews objectAt: i];
		[v getFrame: &r];
		if (NXPointInRect(&loc,&r))
			return v;
	}
	return nil;
}

/*
 * clone this view and return the clone
 */
- cloneView: aView
{
char * buf;
int length;
View *newView;

	buf = NXWriteRootObjectToBuffer(aView,&length);
	newView = NXReadObjectFromBuffer(buf,length);
	NXFreeObjectBuffer(buf,length);
	return newView;
}

- mouseDown:(NXEvent *)event
{
DragView *dv;
int found;
int gwnum;
NXPoint loc;
NXRect r;
NXSize siz;
SwallowView *sv;
View *tv;
View *v;
Window *w;
Window *wf;
unsigned int wnum;

	if (event->data.mouse.click > 1)
		return self;

	v = [self getSelectedView: &event->location];
	if (v == nil)
		return self;

	[v getBounds: &r];
	[v convertRect: &r toView:nil];
	[window convertBaseToScreen: (NXPoint *)&r];
	
	w = [Window newContent: &r
		style: NX_PLAINSTYLE
		backing: NX_RETAINED
		buttonMask: 0
		defer: NO];
	
	dv = [self cloneView: v];
	
	[[w setContentView: dv] free];
	[w display];
	[w orderFront: self];
	loc = event->location;
	[window convertBaseToScreen: &loc];
	[w convertScreenToBase: &loc];
	[w dragFrom: loc.x : loc.y eventNum: event->data.mouse.eventNum];
	
	PScurrentwindowbounds([w windowNum],&loc.x,&loc.y,&siz.width,&siz.height);
	PSfindwindow(loc.x,loc.y,NX_BELOW,event->window,&loc.x,&loc.y,&gwnum,&found);
	if (found && (gwnum != 14)) { /* ugly */
		NXConvertGlobalToWinNum(gwnum,&wnum);
		wf = [NXApp findWindow: wnum];
		if (wf != nil) {
			sv = [[wf contentView] hitTest: &loc];
			if ([sv respondsTo: @selector(acceptNewView:)]) {
				[sv convertPoint: &loc fromView: nil];
				[w setContentView: nil];
				[dv moveTo: loc.x : loc.y];
				[sv acceptNewView: dv];
			}
		}
	}
	
	[w free];

	return self;
}

bruce@atncpc.UUCP (Bruce Henderson) (07/03/90)

In article <3645@calvin.cs.mcgill.ca>, pegasus@quiche (PROVOST philippe) writes:
> 
> greetings,
> 
> does anyone know how to "drag an image from a button" , ie, when you select
> the button with a mouse, how to move the "picture" representing the object
 (being
> created) from the button to the document. For instance, IB do this operation
> when you select a button, window....in the Palettes Window and drag that tool to 
> the document you define. 
> 
> Help greatly appreciated.
 
This may not be the best way, but here it goes.

First of all.

The palette that you willbe dragging from should have some rather special
buttons on it.  The way to make them is use Icon to draw a picture of the 
thing that you will be dragging.  In IB set the button flags to be Mom Push
and set the button's Icon and Alt Icon to be the picture you just created.

Of course you tehn connect the button to some target method that I will
describe in a minute.  What this button set up does is notify you (via
the message) whenever the user clicks on that object, the button doesn't
highlight and it doesn't appear to change state. (I am pretty sure this is
how the IB palette works)

Now, when you get the button message that you set up to be the target of
your button you need to check to see if the user is dragging the mouse.
(a great example of this is in Draw App's sample code) If the user is
dragging, create a new window of type NX_PLAINSTYLE (this means not title
bar, no border, just an empty window) the exact same size as the picture
of the item (the icon you made for the button). Make sure you make the
window to be NX_RETAINED, it makes it much easier.


Now for the real hack.  You need to track the mouse as long as it is 
dragged, even (I think you meant this) outside the palette window.  Every
time the mouse moves, you send a moveTo:: message to your little window
that we just made.  As if by magic you are now dragging things around the
screen!  As far as finding where it gets dropped, well I think you guys
can figure that out (more references to Draw App source code..) but 
here's a hint.  The target window will most likely be under where the 
mouse up event occurs.....

I am being a bit sketchy on purpose, because If all you gizmo freaks on
the net can easily reproduce what it took me a while to figure out...
Well, then every peice of software will be as cool as the one we're 
writing!


Bruce Henderson
Interface KGB
Aston Tate NeXTeam

bruce@atncpc.UUCP (Bruce Henderson) (07/03/90)

In article <1990Jul2.233503.7154@portia.Stanford.EDU>, duggie@jessica.stanford.edu (Doug Felt) writes:
> In article <3645@calvin.cs.mcgill.ca> pegasus@quiche.cs.mcgill.ca (PROVOST philippe) writes:
> >
> >
> >Help greatly appreciated.
> 
> What you do is create a window where the object was, make it look like
> the object, and drag that.  When you release the window, you create a
> new object where the window was released.
>
Sorry about being so secretive,  I feel kind of dumb now that some one else
has published code.  I think there are some magic tools one should try to
keep private.  Many kudos to Doug Felt.  His code is 75% the same as what
I came up with!\





Bruce Henderson
Interface KGB
Ashton Tate NeXTeam