//	Zinc Application Framework - M_MOUSE.CPP
//	COPYRIGHT (C) 1990-1999.  All Rights Reserved.
//	Zinc Software Incorporated.  Pleasant Grove, Utah  USA

#include <z_evtmgr.hpp>
#include <z_win.hpp>
#include <z_i18n.hpp>
#include <z_mouse2.hpp>
#include <z_scrdsp.hpp>
#include <z_string.hpp> // For strcmp().
#include <z_sysevt.hpp>
#define ZAF_MOUSE_INFO
#include "img_def.cpp"
#include "gbl_def.cpp"

// Information about X cursor information is contained in Appendix I, The
// Cursor Font, in the Xlib Reference Manual and in the <X11/cursorfont.h>
// information file.  In addition, see the XCreateFontCursor(), 
// XDefineCursor() and XWarpPointer() functions defined in the Xlib 
// Reference Manual.
extern "C"
{
#include <X11/cursorfont.h>
}

// ----- ZafMouse ----------------------------------------------------------

ZafMouseData **ZafMouse::imageTable = ZAF_NULLP(ZafMouseData *);

ZafMouse::ZafMouse(ZafDeviceState _state, ZafDeviceImage _imageType) :
	ZafDevice(E_MOUSE, _state), imageType(_imageType)
{
	// Check the image information.
	ImageAllocate();
	SetStringID(className);
	SetNumberID((ZafNumberID)classID);

	// Initialize the mouse information.
	position.column = position.line = 0;
	offset.column = offset.line = 0;
	installed = true;
}

ZafMouse::~ZafMouse(void)
{
	// Check the image information.
	ImageFree();
	installed = false;
}

ZafEventType ZafMouse::Event(const ZafEventStruct &event)
{
	// Switch on the event type.
	ZafEventType ccode = event.type;
	switch (ccode)
	{
	case D_OFF:
	case D_ON:
		deviceState = ccode;
		break;

	case D_STATE:
		return (deviceState);

	case D_POSITION:
		// Move the mouse pointer.
		{
		Screen *xScreen = XtScreen(((ZafScreenDisplay *)display)->topLevelShell);
		XWarpPointer(((ZafScreenDisplay *)display)->xDisplay, None, RootWindowOfScreen(xScreen),
			0, 0, 0, 0, event.position.column, event.position.line);
		}
		break;

	default:
		// See if it is a change cursor request.
		if (ccode >= DM_MOUSE_FIRST && ccode <= DM_MOUSE_LAST)
		{
			// Reset the image type.
			imageType = (ZafDeviceImage)ccode;
			if (!imageTable)
				break;

			// Change the OS image.
			for (int i = 0; imageTable[i]; i++)
				if (imageTable[i]->NumberID() == imageType)
				{
					// Find the proper window.
					Window window = 0;
					if (event.screenID)
						window = XtWindow(event.screenID);
					else if (ZafWindowObject::windowManager->First())
						window = XtWindow(ZafWindowObject::windowManager->First()->screenID);
					Cursor cursor = imageTable[i]->handle;

					// Reset the cursor.
					if (!window)
						break;
					else if (cursor)
						XDefineCursor(((ZafScreenDisplay *)display)->xDisplay, window, cursor);
					else
						XUndefineCursor(((ZafScreenDisplay *)display)->xDisplay, window);
					XFlush(((ZafScreenDisplay *)display)->xDisplay);
					break;
				}
		}
		break;
	}

	// Return the ccode.
	return (ccode);
}

void ZafMouse::ImageAllocate(void)
{
	// Check for an allocated table.
	if (imageTable)
		return;
	ZafI18nData::AddStaticModule(ImageFree);

	// Define mouse system resources.
	static struct SYS_MOUSE
	{
		ZafDeviceImage imageID;
		unsigned int shape;
		ZafImageStruct *image;
	} _mouse[] = 
	{
		{ DM_VIEW, 0, &_view_ZafMouse },
		{ DM_SELECT, XC_arrow, &_view_ZafMouse },
		{ DM_EDIT, XC_target, &_view_ZafMouse },
		{ DM_WAIT, XC_watch, &_view_ZafMouse },
		{ DM_CROSS_HAIRS, XC_crosshair, &_view_ZafMouse },
		{ DM_MOVE, XC_fleur, &_view_ZafMouse },
		{ DM_LEFT_SIDE, XC_left_side, &_view_ZafMouse },
		{ DM_RIGHT_SIDE, XC_right_side, &_view_ZafMouse },
		{ DM_TOP_SIDE, XC_top_side, &_view_ZafMouse },
		{ DM_BOTTOM_SIDE, XC_bottom_side, &_view_ZafMouse },
		{ DM_TOP_LEFT_CORNER, XC_top_left_corner, &_view_ZafMouse },
		{ DM_TOP_RIGHT_CORNER, XC_top_right_corner, &_view_ZafMouse },
		{ DM_BOTTOM_LEFT_CORNER, XC_bottom_left_corner, &_view_ZafMouse },
		{ DM_BOTTOM_RIGHT_CORNER, XC_bottom_right_corner, &_view_ZafMouse },
		{ DM_CANCEL, XC_cross, &_view_ZafMouse },
		{ DM_VERTICAL_SPLIT, XC_sb_h_double_arrow, &_vertical_split_ZafMouse },
		{ DM_HORIZONTAL_SPLIT, XC_sb_v_double_arrow, &_horizontal_split_ZafMouse },
		{ 0, 0, 0 }
	};

	// Initialize the mouse images.
	imageTable = new ZafMouseData *[sizeof(_mouse) / sizeof(SYS_MOUSE)];
	int i;
	for (i = 0; _mouse[i].imageID; i++)
	{
		imageTable[i] = new ZafMouseData(*_mouse[i].image);
		imageTable[i]->handle = _mouse[i].shape ? XCreateFontCursor(((ZafScreenDisplay *)display)->xDisplay, _mouse[i].shape) : 0;
		imageTable[i]->hotSpotX = imageTable[i]->hotSpotY = 0;
		imageTable[i]->staticHandle = true;
		imageTable[i]->SetNumberID((ZafNumberID)_mouse[i].imageID);
	}
	imageTable[i] = ZAF_NULLP(ZafMouseData);
}

void ZafMouse::ImageFree(bool globalRequest)
{
	// Destroy the static images.
	if (globalRequest && imageTable)
	{
		for (int i = 0; imageTable[i]; i++)
			delete imageTable[i];
		delete []imageTable;
		imageTable = ZAF_NULLP(ZafMouseData *);
	}
}

bool ZafMouse::IsA(ZafClassID compareID) const
{
	return ((compareID == classID) ? true : ZafDevice::IsA(compareID));
}

bool ZafMouse::IsA(ZafClassName compareName) const
{
	return (!strcmp(compareName, className) ? true : ZafDevice::IsA(compareName));
}

void ZafMouse::Poll(void)
{
	// Mouse polling is done by XtAppNextEvent().

	// This code takes events off the queue if mouse is turned off.
	ZafEventStruct event;
	while (DeviceState() == D_OFF &&
	 eventManager->Get(event, Q_BEGIN | Q_NO_DESTROY | Q_NO_POLL | Q_NO_BLOCK) == 0 &&
	 (event.type == E_MOUSE || (event.type == E_MOTIF && event.InputType() == E_MOUSE)))
		eventManager->Get(event, Q_BEGIN | Q_NO_POLL | Q_NO_BLOCK);
}

ZafDeviceImage ZafMouse::SetImageType(ZafDeviceImage newImageType)
{
	imageType = newImageType;
	return (imageType);
}
