/*	XGPopWindow.cpp
 *
 *		This is a very simplified class which handles a short-term
 *	popup window. Generally this is used for tracking popup menus
 */

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

  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++
#include <XDraw.h>
#include <XView.h>
#include <XWindow.h>
#include <XApplication.h>

#if OPT_MACOS
#include "MacFloater.h"
#endif

/************************************************************************/
/*																		*/
/*	Globals																*/
/*																		*/
/************************************************************************/

long XGPopWindow::gPopCount = 0;
XGPopWindow *XGPopWindow::gList = NULL;

/************************************************************************/
/*																		*/
/*	Construction/Destruction											*/
/*																		*/
/************************************************************************/

/*$	XGPopWindow::XGPopWindow
 *
 *		Create a popup window
 */

XGPopWindow::XGPopWindow(XGView *base, XRect r, XGView *view)
{
	/*
	 *	Add to list
	 */
	
	fNext = gList;
	gList = this;
	
	/*
	 *	Initialize
	 */
	
	fBase = base;
	fLocation = r;
	fView = view;
	gPopCount++;
	
	/*
	 *	Now figure the global location (in the OS, where to build
	 *	the window)
	 */
	
	base->ViewToScreen(&r);

	/*
	 *	Now that I know where my popup window goes, create it.
	 */
	
	CreateOSWindow(r);
	
	/*
	 *	And set the window parameter of this view
	 */
	
#if OPT_MACOS == 1
	fView->SetRootWindow(&fWindow.port);
#endif

#if OPT_WINOS == 1
	fView->SetRootWindow(fWindow);
#endif

#if OPT_XWIN == 1
	fView->SetRootWindow(fWindow);
#endif
	
	::OffsetRect(&r,-r.left,-r.top);
	fView->SetLocation(r);
	fView->ProcessDraw(r);
}

/*$	XGPopWindow::~XGPopWindow
 *
 *		Delete me
 */

XGPopWindow::~XGPopWindow()
{
	XGPopWindow *foo;
	
	gPopCount--;
	fView->SetRootWindow(0);
	delete fView;
	DestroyOSWindow();
	
	/*
	 *	Remove from list
	 */
	
	if (gList == this) {
		gList = fNext;
	} else {
		for (foo = gList; foo != NULL; foo = foo->fNext) {
			if (foo->fNext == this) {
				foo->fNext = fNext;
				break;
			}
		}
	}
}

/*$	XGPopWindow::FixToScreen
 *
 *		Fix the location of the rectangle to the screen boundaries
 */

void XGPopWindow::FixToScreen(XRect *r)
{
	XRect s;
	short tmp;
	
#if OPT_WINOS
	/* Windows: GetScreenSize doesn't quite return the right thing. */
	s.left   = 0;
	s.top    = 0;
	s.right  = ::GetSystemMetrics(SM_CXFULLSCREEN);
	s.bottom = ::GetSystemMetrics(SM_CYFULLSCREEN);
#else
	XGWindow::GetScreenSize(&s);
#endif
	if (r->left < s.left + 1) {
		tmp = s.left + 1 - r->left;
		::OffsetRect(r,tmp,0);
		::OffsetRect(&fLocation,tmp,0);
	}
	if (r->right > s.right - 1) {
		tmp = r->right - s.right + 1;
		::OffsetRect(r,-tmp,0);
		::OffsetRect(&fLocation,-tmp,0);
	}
	if (r->top < s.top + 1) {
		tmp = s.top + 1 - r->top;
		::OffsetRect(r,0,tmp);
		::OffsetRect(&fLocation,0,tmp);
	}
	if (r->bottom > s.bottom - 1) {
		tmp = r->bottom - s.bottom + 1;
		::OffsetRect(r,0,-tmp);
		::OffsetRect(&fLocation,0,-tmp);
	}
}

/************************************************************************/
/*																		*/
/*	OS Window Construction (Macintosh)									*/
/*																		*/
/************************************************************************/

#if OPT_MACOS == 1

/*	XGPopWindow::CreateOSWindow
 *
 *		Actually build the OS window that will contain this popup. In
 *	order to prevent window activation, this is kludged to create the
 *	window and then show it. The 'ShowWindow()' call bypasses the
 *	activation stuff...
 */

void XGPopWindow::CreateOSWindow(XRect r)
{
	FixToScreen(&r);
	Rect mr = r.MacRect();
	::NewCWindow((Ptr)&fWindow,
			     &mr,
			     "\p",
			     false,
			     plainDBox,
			     (WindowPtr)-1L,
			     false,
			     0L);
	fWindow.windowKind = KTooltipWindow;		// not an XGWindow object...

	::ShowHide(&fWindow.port,true);
}

/*	XGPopWindow::DestroyOSWindow
 *
 *		Destroy the OS window that will contain this popup
 */

void XGPopWindow::DestroyOSWindow()
{
	::CloseWindow(&fWindow.port);
}

/*$	XGPopWindow::GetPopWindow
 *
 *		Get the XGPopWindow object
 */

XGPopWindow *XGPopWindow::GetPopWindow(WindowPtr w)
{
	XGPopWindow *foo;
	
	for (foo = gList; foo != NULL; foo = foo->fNext) {
		if ((GrafPtr)&(foo->fWindow) == w) return foo;
	}
	return NULL;
}

#endif


/************************************************************************/
/*																		*/
/*	OS Window Construction (Microsoft Windows)							*/
/*																		*/
/************************************************************************/

#if OPT_WINOS == 1

/*	XGPopWindow::PopWindowProc
 *
 *		This is a very brain dead callback routine. This routes messages
 *	to my popup window proc
 */

LRESULT CALLBACK XGPopWindow::PopWindowProc(HWND w, UINT msg, WPARAM wp, LPARAM lp)
{
	PAINTSTRUCT ps;
	XGPopWindow *pw;
	static short gMods;
	XPoint pt;
	
	switch (msg) {
		case WM_NCACTIVATE:
			if (!wp) {
				return TRUE;
			}
			break;

		case WM_PAINT:
			::BeginPaint(w,&ps);
			::EndPaint(w,&ps);
			break;

		/*	WM_LBUTTONDOWN et al
		 *
		 *		Mouse click processing
		 */
		
		case WM_LBUTTONDBLCLK:
		case WM_RBUTTONDBLCLK:
		case WM_MBUTTONDBLCLK:
		case WM_LBUTTONDOWN:
		case WM_RBUTTONDOWN:
		case WM_MBUTTONDOWN:
			pw = XGPopWindow::GetPopWindow(w);
			if (w == GetCapture()) return 0;
			
			gMods = XGAppCore::GetStateFlags();				
			if ((msg == WM_LBUTTONDBLCLK) ||
					(msg == WM_RBUTTONDBLCLK) ||
					(msg == WM_MBUTTONDBLCLK))
				gMods |= KDoubleClick;
			
			pt.h = LOWORD(lp);
			pt.v = HIWORD(lp);
			if (pw) {
				if (NULL != (XGAppCore::gCaptureView = pw->ProcessMouseDown(pt,gMods))) {
					::SetCapture(w);
				}
			}
			return 0;
			
		/*	WM_MOUSEMOVE:
		 *
		 *		Handle mouse move events
		 */
		 
		case WM_MOUSEMOVE:
			pw = XGPopWindow::GetPopWindow(w);
			if (w == ::GetCapture()) {
				gMods |= KCapturedMouse;
			} else {
				gMods = XGAppCore::GetStateFlags();
				XGAppCore::gCaptureView = NULL;
			}
			
			if (pw) {
				pt.h = LOWORD(lp);
				pt.v = HIWORD(lp);
				pw->ProcessMouseMove(pt,gMods,XGAppCore::gCaptureView);
			}
			return 0;
		
		/*	WM_LBUTTONUP et al
		 *
		 *		The mouse up event
		 */
		
		case WM_LBUTTONUP:
		case WM_MBUTTONUP:
		case WM_RBUTTONUP:
			pw = XGPopWindow::GetPopWindow(w);
			if (w == ::GetCapture()) {
				gMods |= KCapturedMouse;
				pt.h = LOWORD(lp);
				pt.v = HIWORD(lp);
				::ReleaseCapture();
				if (pw->ProcessMouseUp(pt,gMods,XGAppCore::gCaptureView)) {
					// get capture back
					::SetCapture(w);
				}
			}
			return 0;
			
		default:
			return ::DefWindowProc(w,msg,wp,lp);
	}
	return 0;
}

/*	XGPopWindow::RegisterPopWindow
 *
 *		Register the popup window if it isn't already
 */

void XGPopWindow::RegisterPopWindow(void)
{
	WNDCLASS wc;
	static bool gRegFlag;
	
	if (gRegFlag) return;
	gRegFlag = true;
	
	wc.style = 0;
	wc.lpfnWndProc = XGPopWindow::PopWindowProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = _GInstance;
	wc.hIcon = NULL;
	wc.hCursor = NULL;
	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
	wc.lpszMenuName = NULL;
	wc.lpszClassName = _GPopClass;
	RegisterClass(&wc);
}

/*	XGPopWindow::CreateOSWindow
 *
 *		Actually build the OS window that will contain this popup
 */

void XGPopWindow::CreateOSWindow(XRect r)
{
	RegisterPopWindow();
	
	FixToScreen(&r);
	fWindow = ::CreateWindowEx(0,
						       _GPopClass,
						       "Copyright",
						       WS_POPUP,
						       r.left,r.top,
						       r.right-r.left,r.bottom-r.top,
						       fBase->GetRootWindow(),
						       NULL,
						       _GInstance,
						       NULL);
	if (fWindow) {
		::ShowWindow(fWindow,SW_SHOWNOACTIVATE);
	}
}

/*	XGPopWindow::DestroyOSWindow
 *
 *		Destroy the OS window that will contain this popup
 */

void XGPopWindow::DestroyOSWindow()
{
	::DestroyWindow(fWindow);
	fWindow = NULL;
}

/*$	XGPopWindow::GetPopWindow
 *
 *		Get the XGPopWindow object
 */

XGPopWindow *XGPopWindow::GetPopWindow(HWND w)
{
	XGPopWindow *foo;
	
	for (foo = gList; foo != NULL; foo = foo->fNext) {
		if (foo->fWindow == w) return foo;
	}
	return NULL;
}

#endif


/************************************************************************/
/*																		*/
/*	OS Window Construction (X)											*/
/*																		*/
/************************************************************************/

#if OPT_XWIN == 1

/*	XGPopWindow::CreateOSWindow
 *
 *		Actually build the OS window that will contain this popup
 */

void XGPopWindow::CreateOSWindow(XRect r)
{
	XSetWindowAttributes attrib;

	FixToScreen(&r);
	attrib.event_mask = KeyPressMask |
						ButtonPressMask |
						ButtonReleaseMask |
						EnterWindowMask |
						LeaveWindowMask |
						PointerMotionHintMask |
						PointerMotionMask |
						ExposureMask |
						StructureNotifyMask |
						ColormapChangeMask;
	attrib.override_redirect = true;
	fWindow = ::XCreateWindow(_GDisplay,
							  DefaultRootWindow(_GDisplay),
							  r.left,
							  r.top,
							  r.right-r.left,
							  r.bottom-r.top,
							  0,
							  CopyFromParent,
							  InputOutput,
							  CopyFromParent,
							  CWEventMask | CWOverrideRedirect,
							  &attrib);
	::XMapRaised(_GDisplay,fWindow);
}

/*	XGPopWindow::DestroyOSWindow
 *
 *		Destroy the OS window that will contain this popup
 */

void XGPopWindow::DestroyOSWindow()
{
	::XDestroyWindow(_GDisplay,fWindow);
}


/*$	XGPopWindow::GetPopWindow
 *
 *		Get the XGPopWindow object
 */

XGPopWindow *XGPopWindow::GetPopWindow(Window w)
{
	XGPopWindow *foo;
	
	for (foo = gList; foo != NULL; foo = foo->fNext) {
		if (foo->fWindow == w) return foo;
	}
	return NULL;
}

#endif

/************************************************************************/
/*																		*/
/*	Mouse Event Rerouting												*/
/*																		*/
/************************************************************************/

/*	XGPopWindow::ConvertToBaseView
 *
 *		Convert from the coordinate system inside of this window to
 *	the coordinate system of the base view
 */

XPoint XGPopWindow::ConvertToBaseView(XPoint where)
{
	where.h += fLocation.left;
	where.v += fLocation.top;
	return where;
}

/*$	XGPopWindow::ProcessMouseDown
 *
 *		Process the mouse down. This is an internal routine that is
 *	called by YAAF.
 */

XGView *XGPopWindow::ProcessMouseDown(XPoint where, short flags)
{
	XPoint global;
	
	where = ConvertToBaseView(where);
	global = where;
	fBase->ViewToGlobal(&global);
	XGWindow::ProcessMouseEnter(global,flags,fBase);
	
	if (fBase->DoMouseDown(where,flags)) {
		return fBase;
	} else {
		return NULL;
	}
}

/*$	XGPopWindow::ProcessMouseMove
 *
 *		Process the mouse move event. This is an internal routine that is
 *	called by YAAF.
 */

void XGPopWindow::ProcessMouseMove(XPoint where, short flags, XGView *v)
{
	XPoint global;
	
	if (v == NULL) v = fBase;
	where = ConvertToBaseView(where);
	global = where;
	fBase->ViewToGlobal(&global);
	XGWindow::ProcessMouseEnter(global,flags,v);
	
	if (v != fBase) {
		// Why would this happen???
		fBase->ViewToGlobal(&where);
		v->GlobalToView(&where);
	}
	v->DoMouseMove(where,flags);
}

/*$	XGPopWindow::ProcessMouseUp
 *
 *		Process the mouse up event. This is an internal routine that is
 *	called by YAAF.
 */

bool XGPopWindow::ProcessMouseUp(XPoint where, short flags, XGView *v)
{
	if (v == NULL) v = fBase;

	XGWindow::gHoldCapture = false;
	where = ConvertToBaseView(where);
	if (v != fBase) {
		// Why would this happen???
		fBase->ViewToGlobal(&where);
		v->GlobalToView(&where);
	}
	v->DoMouseUp(where,flags);
	return XGWindow::gHoldCapture;
}
