/*	XGDropper.cpp
 *
 *		Implements drag and drop. The XGDropperWindow object is
 *	created and associated with an XGWindow when one of it's
 *	contained views is an XGDropper view. This XGDropper view is
 *	created and associated with a view which implements
 *	drag and drop.
 */


/**********************************************************************

  XGDropper.cpp original code Copyright 1998-1999 Sassafras Software, Inc.
  Modifications Copyright 1999 William Woody
  
  YAAF is Copyright 1997-1999 by William Woody.

  Redistribution and use in source and binary forms, with or without 
  modification, are permitted provided that the following conditions 
  are met:

  Redistributions of source code must retain the above copyright notice, 
  this list of conditions and the following disclaimer.

  Neither the name of the copyright holder or other contributors may
  be used to promote products derived from this software without
  specific written prior permission.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 
  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 
  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
  SOFTWARE.
  
  Contact:   woody@alumni.caltech.edu
  Web page:  http://www.pandawave.com/yaaf/

**********************************************************************/

#include <XConfig.h>			// must be first to optimize compile on MSVC++

#if OPT_MACOS
#include <XApplication.h>		// I only need _GHasDragDrop
#include <drag.h>
#endif

#if OPT_WINOS
#include <ole2.h>
#include <shellapi.h>
#endif

#include <XView.h>
#include <XWindow.h>
#include <XDraw.h>
#include <XError.h>
#include <XFile.h>
#include <XDragDrop.h>

/************************************************************************/
/*																		*/
/*	Construction														*/
/*																		*/
/************************************************************************/

#if 0
/*$	XGDropper::XGDropper
 *
 *		The default; this permits me to mix this in easily to a view.
 */

XGDropper::XGDropper (void)
{
	fView = dynamic_cast<XGView *>(this);
	Assert(fView != NULL);
	Constrain();
	IInitDropper();
}
#endif

/*$	XGDropper::XGDropper
 *
 *		A second version of this constructor allows me to indicate
 *	another view and/or a subset of that view
 */

XGDropper::XGDropper (XGView *v, XRect *r)
{
	fView = v;
	Constrain(r);
	IInitDropper();
}

/*$	XGDropper::~XGDropper
 *
 *		Free memory associated with this thing
 */

XGDropper::~XGDropper (void)
{
	if (fDrop) fDrop->RemoveDropper(this);
}

/************************************************************************/
/*																		*/
/*	Construction Support												*/
/*																		*/
/************************************************************************/

/*$	XGDropper::Constrain
 *
 *		Set the current constraints for accepting dragging. If set to NULL
 *	(the default), this clears the constraint rectangle, permitting the
 *	entire view to accept drag and drop.
 */

void XGDropper::Constrain (XRect *r)
{
	if (r) {
		fBounds = *r;
		if (EmptyRect(r)) fBounds = fView->GetContentRect();
		fConstrained = true;
	} else {
		fConstrained = false;
	}
}

/*$	XGDropper::GetBounds
 *
 *		Get the current boundary within this view which is accepting
 *	drag and drop
 */

XRect XGDropper::GetBounds()
{
	if (!fConstrained) return fView->GetContentRect();
	else return fBounds;
}

/*	XGDropper::IInitDropper
 *
 *		Internal initialization; this builds the appropriate internal
 *	structures, depending on if this is Winodws or the Mac.
 */

void XGDropper::IInitDropper (void)
{
	fDrop = XGDropperWindow::InstallDropper(this);
	fHiliteFlag = false;
}

/************************************************************************/
/*																		*/
/*	Drag/Drop Events													*/
/*																		*/
/************************************************************************/

/*$	XGDropper::Accept
 *
 *		Does this accept the target? This returns 'true' if this does.
 */

bool XGDropper::Accept(long /* modifiers */, long /* itemRef */, long /* flavor */)
{
	return false;
}

/*$	XGDropper::Track
 *
 *		This is called when the mouse is dragging inside of me. The
 *	hilite has already been drawn when I'm called; this is only so
 *	I can show a specific insertion point, if needed.
 */

void XGDropper::Track(XPoint /* where */, long /* modifiers */)
{
}


void XGDropper::BeginDrop( XPoint /* where */, long /* mods */ )
{
}

/*$	XGDropper::Drop
 *
 *		This is called when the data is actually dropped. Return true if
 *	this worked.
 */

bool XGDropper::Drop(XPoint /* where */,
					 long /* mods */,
					 long /* itemRef */,
					 long /* flavor */,
					 void * /* dataptr */,
					 long /* datalen */)
{
	return false;
}

void XGDropper::EndDrop( XPoint /* where */, long /* mods */ )
{
}

/*$	XGDropper::Scroll
 *
 *		Handle autoscrolling inside of this view. This is called with the
 *	direction we should scroll.
 */

void XGDropper::Scroll(long /* direction */)
{
}

/*$	XGDropper::Enter
 *
 *		This is called when the mouse first enters the drag region.
 *	By default this draws a hilite border around the view; this can
 *	be overridden to provide different behavior
 */

void XGDropper::Enter(XPoint /* where */,
					  long /* mods */,
					  bool hilite)
{
	XRect r;
	
	if (hilite) {
		XGDraw draw(fView);
		
		r = GetBounds();
		ShowDragFeedback(draw,&r);
	}
}

/*	XGDropper::Leave
 *
 *		This is called when the mouse leaves the view. By default this
 *	hides the dropper area; override to provide different behavior
 */

void XGDropper::Leave()
{
	XGDraw draw(fView);
	XRect r;
	
	r = GetBounds();
	HideDragFeedback(draw,&r);
}

/*	XGDropper::ShowDragFeedback
 *
 *		Show the drag/drop hilite
 */

void XGDropper::ShowDragFeedback(XGDraw &draw, XRect *r)
{
	if (fHiliteFlag) return;
	fHiliteFlag = true;

#if OPT_MACOS
	RgnHandle rgn;
	
	if (_GHasDragDrop) {
		if (NULL != (rgn = NewRgn())) {
			Rect mr = r->MacRect();
			RectRgn(rgn, &mr);
			ShowDragHilite(fDragReference, rgn, true);
			DisposeRgn(rgn);
		}
		return;
	}
#endif

#if OPT_WINOS
	DrawDragFeedback(draw,r);
#endif

#if OPT_MACOS || OPT_XWIN
	draw.InvertFrameRect(*r);
#endif
}

/*	XGDropper::HideDragFeedback
 *
 *		Hide the drag/drop hilite
 */

void XGDropper::HideDragFeedback(XGDraw &draw, XRect *r)
{
	if (!fHiliteFlag) return;
	fHiliteFlag = false;

#if OPT_MACOS
	if (_GHasDragDrop) {
		HideDragHilite(fDragReference);
		return;
	}
#endif

#if OPT_WINOS
	DrawDragFeedback(draw,r);
#endif

#if OPT_MACOS || OPT_XWIN
	draw.InvertFrameRect(*r);
#endif
}

#if OPT_WINOS

/*	XGDropper::DrawDragFeedback
 *
 *		Draw the drag and drop feedback around my view. This is sorta
 *	like the InvertFrameRect routine, but does it in the active border
 *	color.
 */

void XGDropper::DrawDragFeedback(XGDraw &draw, XRect *r)
{
	XRect lr;

	if (r) {
		lr = *r;
	} else {
		lr = GetBounds();
		if (::IsRectEmpty(&lr))
			lr = fView->GetContentRect();
	}

	HDC hDC = draw.GetDC();
	int old = SetROP2(hDC, R2_NOT);
	HGDIOBJ hold = SelectObject(hDC, GetStockObject(HOLLOW_BRUSH));
	HPEN hpen = (HPEN)SelectObject(hDC, CreatePen(PS_SOLID, 2,
								GetSysColor(COLOR_ACTIVEBORDER)));

	Rectangle(hDC, lr.left + 1, lr.top + 1, lr.right, lr.bottom);

	DeleteObject(SelectObject(hDC, hpen));
	SelectObject(hDC, hold);
	SetROP2(hDC, old);
}

#endif




#if OPT_MACOS
void XGDropper::MakeFileSpecFromDropData( XGFileSpecifier &fs, const void *data, int datalen )
{
	fs = XGFileSpecifier( ((HFSFlavor *)data)->fileSpec );
}
#endif

#if OPT_WINOS || OPT_XWIN
void XGDropper::MakeFileSpecFromDropData( XGFileSpecifier &fs, const void *data, int datalen )
{
	fs = XGFileSpecifier( (const char *)data );
}
#endif


