/****************************************************************************
*
* $RCSfile: ZoomerWindow.c $
* $Revision: 1.1 $
* $Date: 1997/03/22 08:41:31 $
* $Author: ssolie $
*
*****************************************************************************
*
* Copyright (c) 1997 Software Evolution.  All Rights Reserved.
*
*****************************************************************************
*
* ZoomerWindow.c -- ZoomerWindow object source code
*
* This file contains the code required to use ZoomerWindow objects.
*/
#include <classact.h>
#include <classes/window.h>
#include <exec/memory.h>
#include <intuition/intuition.h>
#include <intuition/screens.h>
#include <utility/hooks.h>

#include <clib/alib_protos.h>
#include <proto/exec.h>
#include <proto/graphics.h>
#include <proto/intuition.h>
#include <proto/layers.h>
#include <proto/window.h>

#include "ZoomerWindow.h"
#include "Drawing.h"
#include "Debug.h"


/*** Local constants ***/
const STRPTR ZOOMER_WINDOW_TITLE = "Zoomer V1.0";
const UWORD ZOOMER_INNER_WIDTH = 300;
const UWORD ZOOMER_INNER_HEIGHT = 200;
const STRPTR FILE_NAME = "drawing.dr2d";


/*** Local data types ***/
struct ZoomerWindowClass {
	struct Screen *screen;			/* pointer to parent screen */
	struct DrawInfo *draw_info;		/* drawing info. for screen */
	struct Window *window;			/* window pointer */
	struct RastPort *rp;			/* window RastPort pointer */
	struct Layer *layer;			/* window Layer pointer */
	APTR zoomer_window;				/* window object */
	BOOL done;						/* TRUE if user is done */
	struct Hook idcmp_hook;			/* IDMCP hook */
	BOOL move;						/* move mode (TRUE=active) */
	BOOL zoom;						/* zoom mode (TRUE=active) */
	Drawing drawing;				/* Drawing object */
};


/*** Local function prototypes ***/
STATIC VOID __asm __saveds zoomerIDCMPHook(register __a0 struct Hook *hook,
	register __a2 Object *zoomer_window,
	register __a1 struct IntuiMessage *message);


/*
 * newZoomerWindow -- Create new ZoomerWindow object
 *
 * Creates a new ZoomerWindow object ready for use.  Returns a pointer to
 * the new object if successful or NULL on error.
 */
ZoomerWindow newZoomerWindow(struct Screen *screen)
{
	ZoomerWindow this;

	if ( screen == NULL )
		return(NULL);

	this = AllocVec(sizeof(struct ZoomerWindowClass), MEMF_ANY);
	if ( this == NULL )
		return(NULL);

	this->screen						= screen;
	this->draw_info						= GetScreenDrawInfo(screen);
	this->window						= NULL;
	this->rp							= NULL;
	this->layer							= NULL;
	this->done							= FALSE;
	this->idcmp_hook.h_MinNode.mln_Succ	= NULL;
	this->idcmp_hook.h_MinNode.mln_Pred	= NULL;
	this->idcmp_hook.h_Entry			= (ULONG (*)())zoomerIDCMPHook;
	this->idcmp_hook.h_SubEntry			= NULL;
	this->idcmp_hook.h_Data				= NULL;
	this->move							= FALSE;
	this->zoom							= FALSE;
	this->drawing						= NULL;

	this->drawing = newDrawing(screen);
	this->zoomer_window = NewObject((struct IClass*)WINDOW_GetClass(), NULL,
		WA_Title, ZOOMER_WINDOW_TITLE,
		WA_Flags, WFLG_SMART_REFRESH | WFLG_CLOSEGADGET | WFLG_DEPTHGADGET |
			WFLG_DRAGBAR | WFLG_ACTIVATE | WFLG_SIZEGADGET | WFLG_REPORTMOUSE |
			WFLG_RMBTRAP,
		WA_PubScreen, screen,
		WA_InnerWidth, ZOOMER_INNER_WIDTH,
		WA_InnerHeight, ZOOMER_INNER_HEIGHT,
		WA_IDCMP, IDCMP_MOUSEMOVE | IDCMP_MOUSEBUTTONS | IDCMP_DELTAMOVE,
		WINDOW_Position, WPOS_CENTERSCREEN,
		WINDOW_IDCMPHook, &this->idcmp_hook,
		WINDOW_IDCMPHookBits, IDCMP_IDCMPUPDATE | IDCMP_REFRESHWINDOW |
			IDCMP_MOUSEMOVE,
		WINDOW_UserData, this,
		CLASSACT_BackFill, NULL,
		TAG_END);

	if ( this->drawing == NULL || this->zoomer_window == NULL )  {
		deleteZoomerWindow(this);
		return(NULL);
	}

	return(this);
}


/*
 * deleteZoomerWindow -- Delete ZoomerWindow object
 *
 * This function is used to delete a ZoomerWindow object.
 */
VOID deleteZoomerWindow(ZoomerWindow this)
{
	if ( this != NULL )  {
		DisposeObject(this->zoomer_window);
		deleteDrawing(this->drawing);
		FreeScreenDrawInfo(this->screen, this->draw_info);
		FreeVec(this);
	}
}


/*
 * handleZoomerWindow -- Handle a ZoomerWindow object
 *
 * This function opens the zoomer window and handles any user input.
 * Returns TRUE if successful or FALSE on error.
 */
BOOL handleZoomerWindow(ZoomerWindow this)
{
	struct Region *region;
	struct Rectangle rectangle;
	ULONG sigmask, window_sigmask;
	ULONG result, class;
	WORD code;

	/*** Open window and set state variables ***/
	if ( DoMethod(this->zoomer_window, WM_OPEN) == NULL )
		return(FALSE);

	GetAttr(WINDOW_Window, this->zoomer_window, (ULONG*)&this->window);
	this->rp = this->window->RPort;
	this->layer = this->window->WLayer;

	/*** Install region to perform clipping ***/
	rectangle.MinX = this->window->BorderLeft;
	rectangle.MinY = this->window->BorderTop;
	rectangle.MaxX = this->window->Width - this->window->BorderRight - 1;
	rectangle.MaxY = this->window->Height - this->window->BorderBottom - 1;

	/*** Load the drawing ***/
	if ( loadDrawing(this->drawing, FILE_NAME, &rectangle) == FALSE )  {
		DoMethod(this->zoomer_window, WM_CLOSE);
		return(FALSE);
	}

	region = NewRegion();
	if ( region != NULL )  {
		if ( OrRectRegion(region, &rectangle) == FALSE ) {
			DisposeRegion(region);
			region = NULL;
		}
	}

	if ( region == NULL )  {
		DoMethod(this->zoomer_window, WM_CLOSE);
		return(FALSE);
	}

	(VOID)InstallClipRegion(this->layer, region);

	/*** Render initial drawing ***/
	renderDrawing(this->drawing, this->rp, 0, 0, 0);

	/*** Act on user input ***/
	GetAttr(WINDOW_SigMask, this->zoomer_window, &window_sigmask);
	this->done = FALSE;
	while ( !this->done )  {
		sigmask = Wait(window_sigmask);
		if ( sigmask & window_sigmask )  {
			result = DoMethod(this->zoomer_window, WM_HANDLEINPUT, &code);
			while ( result != WMHI_LASTMSG )  {
				class = result & WMHI_CLASSMASK;
				switch ( class )  {
					case WMHI_CLOSEWINDOW:
						this->done = TRUE;
						break;
					case WMHI_MOUSEBUTTONS:
						switch ( code )  {
							case MIDDLEDOWN:
								this->move = TRUE;
								break;
							case MIDDLEUP:
								this->move = FALSE;
								break;
							case MENUDOWN:
								this->zoom = TRUE;
								break;
							case MENUUP:
								this->zoom = FALSE;
								break;
						}
						break;
					case WMHI_MOUSEMOVE:
					case WMHI_ACTIVE:
					case WMHI_INACTIVE:
						break;
					default:
						D2(bug("handleZoomerWindow(): class=%08lx code=%08lx\n",
							class, code));
				}

				result = DoMethod(this->zoomer_window, WM_HANDLEINPUT, &code);
			}
		}
	}

	region = InstallClipRegion(this->layer, NULL);
	if ( region != NULL )
		DisposeRegion(region);

	DoMethod(this->zoomer_window, WM_CLOSE);

	return(TRUE);
}


/*
 * zoomerIDCMPHook -- ZoomerWindow object IDCMP hook function
 *
 * This function is called when an IDCMP event occurs which requires
 * processing.  The IDCMP events which trigger this hook are defined
 * using the WINDOW_IDCMPHookBits tag.
 */
STATIC VOID __asm __saveds zoomerIDCMPHook(register __a0 struct Hook *hook,
	register __a2 Object *zoomer_window,
	register __a1 struct IntuiMessage *message)
{
	struct Window *window;
	struct Region *new_region, *old_region;
	struct Rectangle rectangle;
	ZoomerWindow this;
	WORD dx, dy, dz;

	GetAttr(WINDOW_Window, zoomer_window, (ULONG*)&window);
	GetAttr(WINDOW_UserData, zoomer_window, (ULONG*)&this);

	switch ( message->Class )  {
		case IDCMP_IDCMPUPDATE:
			D2(bug("IDCMPUPDATE event\n"));
			break;
		case IDCMP_REFRESHWINDOW:
			rectangle.MinX = this->window->BorderLeft;
			rectangle.MinY = this->window->BorderTop;
			rectangle.MaxX =
				this->window->Width - this->window->BorderRight - 1;
			rectangle.MaxY =
				this->window->Height - this->window->BorderBottom - 1;

			new_region = NewRegion();
			if ( new_region != NULL )  {
				if ( OrRectRegion(new_region, &rectangle) == FALSE ) {
					DisposeRegion(new_region);
					new_region = NULL;
				}
			}

			if ( new_region != NULL )  {
				old_region = InstallClipRegion(this->layer, new_region);
				if ( old_region != NULL )
					DisposeRegion(old_region);
			}

			BeginRefresh(this->window);
			renderDrawing(this->drawing, this->rp, 0, 0, 0);
			EndRefresh(this->window, TRUE);
			break;
		case IDCMP_MOUSEMOVE:
			if ( this->move == TRUE && this->zoom == TRUE )  {
				dx = message->MouseX;
				dy = message->MouseY;
				dz = message->MouseY;
				renderDrawing(this->drawing, this->rp, dx, dy, dz);
			}
			else if ( this->move == TRUE )  {
				dx = message->MouseX;
				dy = message->MouseY;
				dz = 0;
				renderDrawing(this->drawing, this->rp, dx, dy, dz);
			}
			else if ( this->zoom == TRUE )  {
				dx = 0;
				dy = 0;
				dz = message->MouseY;
				renderDrawing(this->drawing, this->rp, dx, dy, dz);
			}
			break;
		default:
			D2(bug("Unknown event class=%08lx\n", message->Class));
	}
}
