/*	XGToolbarView.cpp
 *
 *		This is the declaration of the toolbar. The toolbar is a
 *	collection of buttons which are enabled or disabled according
 *	to their corrisponding menu status
 */

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

  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>
#include <string.h>
#include <XToolbar.h>
#include <XError.h>
#include <XAutoPtr.h>
#include <XDataUtil.h>
#include <XDraw.h>
#include <XEvent.h>
#include <XToolTip.h>
#include <XMessages.h>

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

#if OPT_WINOS
extern "C" {
	extern HINSTANCE _GInstance;
}
#endif

/************************************************************************/
/*																		*/
/*	Toolbar View														*/
/*																		*/
/************************************************************************/

/*$	XGToolbarView::XGToolbarView
 *
 *		Try to construct me from my specification.
 */

XGToolbarView::XGToolbarView(XGView *parent, XGArgStream &stream) : XGView(parent,stream)
{
	fResID = -1;
	fOverTool = -1;
	ChangeToolbar((short)stream.ReadInteger());
	
	/*
	 *	Hook me into periodic events
	 */
	
	XGBroadcast::AddBroadcast(this);
}

/*	XGToolbarView::XGToolbarView
 *
 *		Create from TMenu resource for construction purposes for
 *	the Windows version of this.
 */

#if OPT_WINOS

static XGSViewInitRecord ToolInitRecord = {
	'****', 1, 0,
	0, 0, 0, 0,
	true, true, true, true,
	true, true, false
};

XGToolbarView::XGToolbarView(short resID) : XGView(NULL,ToolInitRecord)
{
	fResID = -1;
	fOverTool = -1;
	ChangeToolMenu(resID);
	
	/*
	 *	Hook me into periodic events
	 */
	
	XGBroadcast::AddBroadcast(this);
}

#endif

/*$	XGToolbarView::~XGToolbarView
 *
 *		Go away
 */

XGToolbarView::~XGToolbarView()
{
}

/************************************************************************/
/*																		*/
/*	Change Current Toolbar												*/
/*																		*/
/************************************************************************/

/*$	XGToolbarView::ChangeToolbar
 *
 *		Change the current toolbar.
 */

void XGToolbarView::ChangeToolbar(short resID)
{
	unsigned short i,len;
	
	if (fResID == resID) return;
	if (fOverTool != 0xFFFF) XGToolTip::CloseTip();
	fResID = resID;

#if OPT_MACOS == 1
	Handle h;
	
	h = ::GetResource('Tool',fResID);
	Assert(h != NULL);
	
	::HLock(h);
	fToolbar.SetSize((unsigned long)::GetHandleSize(h));
	memcpy(*fToolbar,*h,fToolbar.GetSize());
	::HUnlock(h);
	::ReleaseResource(h);
#endif

#if OPT_WINOS == 1
	HRSRC hsrc;
	HGLOBAL h;
	void *data;
	
	hsrc = ::FindResource(_GInstance,MAKEINTRESOURCE(fResID),"Tool");
	Assert(hsrc != NULL);
	h = LoadResource(_GInstance,hsrc);
	Assert(h != NULL);
	data = LockResource(h);
	
	/*
	 *	Now create the object
	 */
	
	fToolbar.SetSize(::SizeofResource(_GInstance,hsrc));
	memcpy(*fToolbar,data,fToolbar.GetSize());
	
	/*
	 *	Release the resource
	 */
	
	UnlockResource(h);
	FreeResource(h);
#endif

#if OPT_XWIN == 1
	void *data;
	
	data = ::GetResourceData('Tool',fResID);
	Assert(data != NULL);
	fToolbar.SetSize(::GetResourceSize('Tool',fResID));
	memcpy(*fToolbar,data,fToolbar.GetSize());
#endif

	fStatus.SetSize(len = GetToolLength());
	for (i = 0; i < len; i++) fStatus.SetChar(i,0);
	fOverTool = 0xFFFF;
	
	/*
	 *	Redraw me
	 */
	
	InvalView();
}

/*	XGToolbarView::ChangeToolMenu
 *
 *		Create from TMenu resource for construction purposes for
 *	the Windows version of this.
 */

#if OPT_WINOS

void XGToolbarView::ChangeToolMenu(short resID)
{
	unsigned short i,len;
	HRSRC hsrc;
	HGLOBAL h;
	void *data;
	unsigned long offset;
	
	if (fResID == resID) return;
	if (fOverTool != 0xFFFF) XGToolTip::CloseTip();
	fResID = resID;
	
	hsrc = ::FindResource(_GInstance,MAKEINTRESOURCE(resID),"TMenu");
	Assert(hsrc != NULL);
	h = LoadResource(_GInstance,hsrc);
	Assert(h != NULL);
	data = LockResource(h);
	
	/*
	 *	Now create the object
	 */
	
	fToolbar.SetSize(::SizeofResource(_GInstance,hsrc));
	memcpy(*fToolbar,data,fToolbar.GetSize());
	
	/*
	 *	Release the resource
	 */
	
	UnlockResource(h);
	FreeResource(h);

	fToolbar.SetShort(0,SwapWord(fToolbar.GetShort(0)));
	len = GetToolLength();
	for (i = 0; i < len; i++) {
		offset = GetItemOffset(i);
		
		fToolbar.SetShort(offset,SwapWord(fToolbar.GetShort(offset)));
		fToolbar.SetShort(offset+2,SwapWord(fToolbar.GetShort(offset+2)));
	}
	fStatus.SetSize(len);
	for (i = 0; i < len; i++) fStatus.SetChar(i,0);
	fOverTool = -1;
	
	/*
	 *	Redraw me
	 */
	
	InvalView();
}

#endif

/************************************************************************/
/*																		*/
/*	Toolbar View														*/
/*																		*/
/************************************************************************/

/*$	XGToolbarView::DoDrawView
 *
 *		Draw this view
 */

void XGToolbarView::DoDrawView(XRect)
{
	XGDraw draw(this);
	XRect r = GetContentRect();
	long i,len;

	/*
	 *	Draw the background of this thing
	 */

	draw.SetForeColor(KXGColorBtnShadow);
	draw.MoveTo(r.left,r.top);
	draw.LineTo(r.right,r.top);
	r.top++;
	draw.SetForeColor(KXGColorBtnHilite);
	draw.MoveTo(r.left,r.top);
	draw.LineTo(r.right,r.top);
	r.top++;
	
	draw.SetForeColor(KXGColorBtnShadow);
	draw.MoveTo(r.left,r.bottom-1);
	draw.LineTo(r.right,r.bottom-1);
	r.bottom--;
	
	draw.Draw3DRect(r,KXGEBackground);
	
	/*
	 *	Draw the buttons
	 */
	
	len = fStatus.GetSize();
	for (i = 0; i < len; i++) {
		unsigned char c;
		
		c = fStatus.GetChar(i);
		if (0 == (c & kSkip)) {
			DrawButton(draw,
					   CalcButton(i),
					   i,
					   c);
		} else {
			r = CalcButton(i);
			r.left += 5;
			r.bottom = r.top + 22;
			draw.SetForeColor(KXGColorBtnShadow);
			draw.MoveTo(r.left,r.top);
			draw.LineTo(r.left,r.bottom);
			r.left++;
			draw.SetForeColor(KXGColorBtnHilite);
			draw.MoveTo(r.left,r.top);
			draw.LineTo(r.left,r.bottom);
		}
	}
}

/*$	XGToolbarView::DoMouseDown
 *
 *		Handle mouse down event
 */

bool XGToolbarView::DoMouseDown(XPoint where, short)
{
	long i,len;
	XRect r;
	unsigned char c;
	
	XGToolTip::CloseTip();
	len = fStatus.GetSize();
	for (i = 0; i < len; i++) {
		r = CalcButton(i);
		if (PtInRect(where,&r)) {
			c = fStatus.GetChar(i);
			if (c & kSkip) return false;			
			if (0 == (c & kEnable)) return false;		// not enabled.
			fHitStatus = true;
			fHitWhat = i;
			fHitWhere = r;
			fHitMode = c;
			
			XGDraw draw(this);
			DrawButton(draw,r,fHitWhat,c | kDown);
			return true;
		}
	}
	return false;
}

/*$	XGToolbarView::DoMouseMove
 *
 *		Handle mouse move event
 */

void XGToolbarView::DoMouseMove(XPoint where, short flags)
{
	long i,len;
	XRect r;
	
	XGView::DoMouseMove(where,flags);
	if (0 == (flags & KCapturedMouse)) {
		/*
		 *	Find what I've dragged the mouse over, and outline
		 *	that control
		 */
		
		len = fStatus.GetSize();
		for (i = 0; i < len; i++) {
			r = CalcButton(i);
			if (PtInRect(where,&r)) {
				OutlineButton(i);
				break;
			}
		}
		if (i >= len) {
			OutlineButton(-1);
		}
		return;
	}
	
	if (PtInRect(where,&fHitWhere)) {
		if (!fHitStatus) {
			fHitStatus = true;
			XGDraw draw(this);
			DrawButton(draw,fHitWhere,fHitWhat,fHitMode | kDown);
		}
	} else {
		if (fHitStatus) {
			fHitStatus = false;
			XGDraw draw(this);
			DrawButton(draw,fHitWhere,fHitWhat,fHitMode);
		}
	}
}

/*$	XGToolbarView::DoMouseUp
 *
 *		Handle menu command
 */

void XGToolbarView::DoMouseUp(XPoint, short flags)
{
	if (0 == (flags & KCapturedMouse)) return;

	if (fHitStatus) {
		{
			XGDraw draw(this);
			DrawButton(draw,fHitWhere,fHitWhat,fHitMode);
		}
		SendMenuCommand(GetItemCommand(fHitWhat));
	}
}

/*$	XGToolbarView::DoMouseLeave
 *
 *		The mouse has left the control
 */

void XGToolbarView::DoMouseLeave()
{
	OutlineButton(-1);
	XGToolTip::CloseTip();
}

/************************************************************************/
/*																		*/
/*	Toolbar View														*/
/*																		*/
/************************************************************************/

BEGINDISPATCH(XGToolbarView,XGView)
	DISPATCHTABLE(KXGAppPostEvent,XGToolbarView::DoPeriodic)
ENDDISPATCH

/*	XGToolbarView::DoPeriodic
 *
 *		Handle periodic events
 */

long XGToolbarView::DoPeriodic(long,void *)
{
	long i,len;
	autoptr<XGDraw> draw;
	char c;
	
	/*
	 *	Scan all the commands and figure out their status
	 */
	
	len = fStatus.GetSize();
	for (i = 0; i < len; i++) {
		c = GetMenuStatus(GetItemCommand(i));
		if (c != fStatus.GetChar(i)) {
			if (NULL == draw.Ptr()) draw = new XGDraw(this);
			fStatus.SetChar(i,c);
			DrawButton(*draw,
					   CalcButton(i),
					   i,
					   c);
		}
	}
	return 0;
}

/************************************************************************/
/*																		*/
/*	Resource Access														*/
/*																		*/
/************************************************************************/

/*$	XGToolbarView::GetToolLength
 *
 *		# items in this
 */

short XGToolbarView::GetToolLength()
{
	return fToolbar.GetShort(0);
}

/*$	XGToolbarView::GetItemIcon
 *
 *		Item icon
 */

short XGToolbarView::GetItemIcon(long item)
{
	return fToolbar.GetShort(GetItemOffset(item));
}

/*$	XGToolbarView::GetItemCommand
 *
 *		Item icon
 */

short XGToolbarView::GetItemCommand(long item)
{
	return fToolbar.GetShort(2+GetItemOffset(item));
}

/*$	XGToolbarView::GetItemIcon
 *
 *		Item icon
 */

void XGToolbarView::GetItemHelp(long item, char *ptr)
{
	strcpy(ptr,(char *)(*fToolbar) + 4 + GetItemOffset(item));
}

/*$	XGToolbarView::GetItemOffset
 *
 *		Get the offset of this particular item
 */

long XGToolbarView::GetItemOffset(long item)
{
	long offset = 2;
	
	while (item-- > 0) {
		offset += 4;
		offset += 1 + strlen(offset + (char *)*fToolbar);
	}
	return offset;
}

/************************************************************************/
/*																		*/
/*	Toolbar View														*/
/*																		*/
/************************************************************************/

/*$	XGToolbarView::SendMenuCommand
 *
 *		Send the menu command
 */

void XGToolbarView::SendMenuCommand(long arg)
{
	XGDispatch::SendDispatch(KXGDoMenuCommand,arg,NULL);
}

/*$	XGToolbarView::GetMenuStatus
 *
 *		Get the menu status
 */

char XGToolbarView::GetMenuStatus(long arg)
{
	XGSMenuStatus status;
	
	if (arg == 0) return kSkip;
	
	status.enable = false;
	status.check = false;
	status.mark = KXGCommand;
	XGDispatch::SendDispatch(KXGGetMenuStatus,arg,(void *)&status);

	if (!status.enable) return 0;
	if (status.check) return kCheck | kEnable;
	else return kEnable;
}

/*$	XGToolbarView::CalcButton
 *
 *		Calculate where this button should go. This is based on
 *	several different parameters, including any 'skip' menu commands
 */

XRect XGToolbarView::CalcButton(long pos)
{
	XRect r;
	short x,y;
	long i;
	
	r = GetContentRect();
	if (r.bottom - r.top < 44) {
		/*
		 *	Horizontal only
		 */
		
		x = 2;
		y = (r.bottom + r.top - 22)/2+1;
		i = 0;
		while (i < pos) {
			if (GetItemCommand(i) == 0) {
				x += 11;
			} else {
				x += 22;
			}
			i++;
		}
	} else if (r.right - r.left < 44) {
		/*
		 *	Vertical only
		 */
		
		y = 2;
		x = (r.right + r.left - 22)/2;
		i = 0;
		while (i < pos) {
			if (GetItemCommand(i) == 0) {
				y += 11;
			} else {
				y += 22;
			}
			i++;
		}
	} else {
		/*
		 *	Wrap
		 */

		x = 2 + r.left;
		y = 1 + r.top;
		i = 0;
		while (i < pos) {
			if (GetItemCommand(i) == 0) {
				x += 11;
			} else {
				x += 22;
			}
			i++;
			
			if (x + 22 > r.right - 3) {
				x = 2 + r.left;
				y += 22;
			}
		}
	}
	r.left = x;
	r.top = y;
	
	if (GetItemCommand(i) == 0) {
		r.right = r.left;
		r.bottom = r.top;
	} else {
		r.right = r.left + 22;
		r.bottom = r.top + 22;
	}
	return r;
}

/*$	XGToolbarView::OutlineButton
 *
 *		outline the button specified, unoutlining the old button
 */

void XGToolbarView::OutlineButton(long index)
{
	XRect r;
	char c;
	XPoint where;
	char buffer[256];
	
	if (index == fOverTool) return;
	
	XGDraw draw(this);
	if (fOverTool != -1) {
		/*
		 *	Unoutline always
		 */
		
		r = CalcButton(fOverTool);
		draw.Draw3DRect(r,KXGEBackFrame);
		XGToolTip::CloseTip();
	}
	
	/*
	 *	Determine if I should outline here
	 */
	
	if (index != -1) {
		c = fStatus.GetChar(index);
		r = CalcButton(index);
		if (c == kEnable) {
			draw.Draw3DRect(r,KXGEOutset);
		}
		where.h = r.right;
		where.v = r.bottom;
		GetItemHelp(index,buffer);
		XGToolTip::OpenTip(this,where,buffer);
	}
		
	fOverTool = index;
}

/*$	XGToolbarView::DrawButton
 *
 *		Draw the button icon specified at the location specified in the mode
 *	provided
 */

void XGToolbarView::DrawButton(XGDraw &draw, XRect where, long index, unsigned char mode)
{
	bool down;
	short icon;
	
	icon = GetItemIcon(index);
	
	/*
	 *	Draw button frame
	 */
	
	if (0 == (mode & kEnable)) {
		draw.Draw3DRect(where,KXGEBackground);
		::InsetRect(&where,3,3);
		down = false;
	} else {
		if (mode & (kCheck | kDown)) {
			/*
			 *	Draw the outer frame down
			 */
			
			draw.Draw3DRect(where,KXGEInset);
			::InsetRect(&where,1,1);
			draw.Draw3DRect(where,KXGEBackFrame);
			::InsetRect(&where,1,1);
			down = true;
		} else {
			if (index == fOverTool) {
				draw.Draw3DRect(where,KXGEOutset);
			} else {
				draw.Draw3DRect(where,KXGEBackFrame);
			}
			::InsetRect(&where,1,1);
			draw.Draw3DRect(where,KXGEBackFrame);
			::InsetRect(&where,1,1);
			down = false;
		}
		
		/*
		 *	Draw the button outline
		 */
		
		if ((mode & kCheck) && (0 == (mode & kDown))) {
			/*
			 *	Draw the light background
			 */
			
			short r,g,b;
			Color back,light;
			
			back = draw.ColorFromPalette(KXGColorBtnFace);
			light= draw.ColorFromPalette(KXGColorBtnHilite);
			
			r = GetRValue(back);
			r = (r + GetRValue(light))/2;
			g = GetRValue(back);
			g = (g + GetRValue(light))/2;
			b = GetRValue(back);
			b = (b + GetRValue(light))/2;
			
			draw.FillRect(where,RGB(r,g,b));
		} else {
			draw.Draw3DRect(where,KXGEBackground);
		}
		::InsetRect(&where,1,1);
	}

	if (mode & kEnable) {
		if (down) {
			draw.DrawIcon(icon,where.left+1,where.top+1);
		} else {
			draw.DrawIcon(icon,where.left,where.top);
		}
	} else {
		draw.DrawIcon(icon,where.left,where.top,false);
	}
}

