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

#include <z_stdlib.hpp>
#include <z_string.hpp>
#include "m_button.hpp"
#include "m_win.hpp"

// ----- MtfButton widget definition ----------------------------------------

XmOffsetPtr MtfButton::offsets;

XtActionsRec MtfButton::action[] =
{
	{ "ZafMouse",					(XtActionProc)MtfWindowObject::mouse },
	{ "Activate", 					(XtActionProc)activate },
	{ "Arm", 						(XtActionProc)arm },
	{ "ArmAndActivate",				(XtActionProc)arm_and_activate },
	{ "Disarm", 					(XtActionProc)disarm },
	{ "Enter", 						(XtActionProc)enter },
	{ "Leave",						(XtActionProc)leave },
	{ "MultiActivate",				(XtActionProc)multi_activate },
	{ "MultiArm", 					(XtActionProc)multi_arm },
	{ "ZafCallDefault", 			(XtActionProc)call_default },
	{ "ZafDragDrop",				(XtActionProc)drag_drop },
	{ "ZafEnter",					(XtActionProc)enter },
	{ "ZafFocusIn",					(XtActionProc)focus_in },
	{ "ZafHelp", 					(XtActionProc)help },
	{ "ZafLeave",					(XtActionProc)leave },
	{ "ZafMotion",					(XtActionProc)motion }
};

char MtfButton::translations[] =
	"<Key>osfActivate:		ZafCallDefault()\n\
	 <Key>osfSelect:		ZafCallDefault()\n\
	 ~s ~m ~a <Key>Return:	ZafCallDefault()\n\
	 <Btn2Down>:			ZafDragDrop()\n\
	 <Btn3Down>:			ZafMouse()\n\
	 <Btn3Up>:				ZafMouse()\n\
	 <Btn3Motion>:			ZafMouse()\n\
	 <Enter>:				ZafEnter()\n\
	 <FocusIn>:				ZafFocusIn()\n\
	 <Key>F1:				ZafHelp()\n\
	 <Key>osfHelp:			ZafHelp()\n\
	 <Leave>:				ZafLeave()";

MtfButtonClassRec MtfButton::classRecord =
{
	// --- CoreClassPart ---
	{
		(WidgetClass)&xmPushButtonClassRec,	// superclass
		"ZafButton", 						// class_name
		sizeof(MtfButtonPart), 					// mtfObject_size (data part)
		MtfButton::classInitialize,		// class_initialize
		0,									// class_part_initialize
		false,								// class inited
		0,									// initialize
		0,									// initialize_hook
		XtInheritRealize,					// realize
		action, 							// actions
		XtNumber(action), 					// num_actions
		0, 									// resources
		0, 									// num_resources
		NULLQUARK,							// xrm_class
		false,								// compress_motion
		XtExposeCompressMultiple,			// compress_exposure
		true,								// compress_enterleave
		false,								// visible_interest
		0, 									// destroy
		XtInheritResize,					// resize
		(XtExposeProc)expose,				// expose
		0,									// set_values
		0, 									// set_values_hook
		XtInheritSetValuesAlmost,			// set_values_almost
		0, 									// get_values_hook
		XtInheritAcceptFocus,				// accept_focus
		XtVersionDontCheck,					// version
		0, 									// callback_private
		XtInheritTranslations,				// tm_table
		XtInheritQueryGeometry, 			// query_geometry
		XtInheritDisplayAccelerator,		// display_accelerator
		0,									// extension
	},
	// --- XmPrimitiveClassPart ---
	{
		(XtWidgetProc)highlight,			// border_highlight
		(XtWidgetProc)unhighlight,			// border_unhighlight
		XtInheritTranslations,				// translations
		(XtActionProc)_XtInherit,			// arm_and_activate
		0,									// syn_resources
		0,									// num_syn_resources
		0									// extension
	},
	// --- XmLabelClassPart ---
	{
		XmInheritWidgetProc,				// setOverrideCallback
		0,									// menuProcs
		0,									// translations
		0									// extension
	},
	// --- XmPushButtonClassPart ---
	{
		0									// extension
	},
	// --- MtfButtonClassPart ---
	{
		0									// extension
	}
};

void MtfButton::activate(MtfButton *, XEvent *, String *, Cardinal *)
{
	// This action occurs on the first "up-click" portion of a mouse click.
	// Zinc performs operations on the "Disarm" action.  No other action
	// is performed.
}

void MtfButton::arm(MtfButton *mtfObject, XEvent *xEvent, String *, Cardinal *)
{
	// This action occurs on the first "down-click" portion of a mouse click.
	ZafButton *zafObject = GetZincObject(mtfObject);

	// Perform the motif arm.
	XmProcessTraversal((Widget)mtfObject, XmTRAVERSE_CURRENT);
	mtfObject->button.armed = true;
	mtfObject->button.armTimeStamp = xEvent->xbutton.time;

	// Match up zinc.
	ZafEventStruct event(E_MOTIF, xEvent);
	zafObject->ConvertToZafEvent(event);
	event.type = L_BEGIN_SELECT;
	zafObject->Event(event);
}

void MtfButton::arm_and_activate(MtfButton *mtfObject, XEvent *, String *, Cardinal *)
{
	// This action occurs when the user "selects" the button with the 
	// keyboard (typically by pressing the space bar).

	// Match up zinc.
	GetZincObject(mtfObject)->Event(L_SELECT);
}

void MtfButton::classInitialize()
{
	XmResolvePartOffsets((WidgetClass) &(MtfButton::classRecord), 
	 &(MtfButton::offsets));
}

void MtfButton::disarm(MtfButton *mtfObject, XEvent *xEvent, String *, Cardinal *)
{
	// This action occurs when the user "releases" the mouse button after
	// receiving and "Arm" message.
	ZafButton *zafObject = GetZincObject(mtfObject);

	// Perform the motif disarm.
	XmProcessTraversal((Widget)mtfObject, XmTRAVERSE_CURRENT);
	mtfObject->button.armed = false;

	// Match up zinc.
	ZafEventStruct event(E_MOTIF, xEvent);
	zafObject->ConvertToZafEvent(event);
	event.type = L_END_SELECT;
	zafObject->Event(event);
}

void MtfButton::drag_drop(MtfButton *mtfObject, XEvent *xEvent, String *, Cardinal *)
{
	// Begin the drag/drop operation.
	ZafEventStruct event(S_BEGIN_DRAG, xEvent);
	GetZincObject(mtfObject)->Event(event);
}

void MtfButton::enter(MtfButton *mtfObject, XEvent *xEvent, String *param, Cardinal *nparams)
{
	// Perform the motif enter and match with zinc.
	XtCallActionProc((Widget)mtfObject, "PrimitiveEnter", xEvent, param, *nparams);

	ZafButton *zafObject = GetZincObject(mtfObject);
	if ((xEvent->xcrossing.state & Button1Mask) && !zafObject->EditMode())
	{
		mtfObject->button.armed = true;
		zafObject->lastMousePosition.coordinateType = ZAF_PIXEL;
		zafObject->lastMousePosition.column = xEvent->xcrossing.x;
		zafObject->lastMousePosition.line = xEvent->xcrossing.y;
		zafObject->SetDepressed(true);

		// Check the auto-repeat process.
		if (zafObject->AutoRepeatSelection())
		{
			if (zafObject->intervalID)
				XtRemoveTimeOut(zafObject->intervalID);
			zafObject->intervalID = XtAppAddTimeOut(((ZafScreenDisplay *)zafDisplay)->xAppContext, zafObject->RepeatDelay(),
				(XtTimerCallbackProc)zafObject->Repeat, (XtPointer)zafObject);
		}
	}

	// Send the message to the object.
	ZafEventStruct event(N_MOUSE_ENTER, xEvent);
	zafObject->Event(event);
}

void MtfButton::expose(MtfButton *mtfObject, XEvent *xEvent, Region xRegion)
{
	// Draw the object.
	ZafEventStruct event(E_MOTIF, xEvent);
	XRectangle xRect;
	XClipBox(xRegion, &xRect);
  	event.region.Assign(xRect.x, xRect.y, xRect.width, xRect.height, ZAF_PIXEL);
	GetZincObject(mtfObject)->Draw(event, E_MOTIF);
}

void MtfButton::focus_in(MtfButton *mtfObject, XEvent *xEvent, String *param, Cardinal *nparams)
{
	// Defer to the motif widget.
	XtCallActionProc((Widget)mtfObject, "PrimitiveFocusIn", xEvent, param, *nparams);
	if (!mtfObject->primitive.have_traversal)
		return; // Object is not really getting the focus.

	// Reflect changes to Zinc.
	ZafButton *zafObject = GetZincObject(mtfObject);
	zafObject->NotifyFocus(zafObject, true);
}

void MtfButton::highlight(MtfButton *mtfObject)
{
	// Draw the button highlight.
	ZafButton *zafObject = GetZincObject(mtfObject);
	ZafRegionStruct drawRegion = zafObject->BeginDraw();
	zafObject->DrawFocus(drawRegion, S_CURRENT);
	zafObject->EndDraw();
}

void MtfButton::leave(MtfButton *mtfObject, XEvent *xEvent, String *param, Cardinal *nparams)
{
	// Perform the motif enter and match with zinc.
	XtCallActionProc((Widget)mtfObject, "PrimitiveLeave", xEvent, param, *nparams);

	ZafButton *zafObject = GetZincObject(mtfObject);
	if ((xEvent->xcrossing.state & Button1Mask) && !zafObject->EditMode())
	{
		mtfObject->button.armed = false;
		zafObject->lastMousePosition.column = xEvent->xcrossing.x;
		zafObject->lastMousePosition.line = xEvent->xcrossing.y;
		zafObject->SetDepressed(false);

		// Check the auto-repeat process.
		if (zafObject->AutoRepeatSelection() && zafObject->intervalID)
		{
			XtRemoveTimeOut(zafObject->intervalID);
			zafObject->intervalID = 0;
		}
	}

	ZafEventStruct event(N_MOUSE_LEAVE, xEvent);
	zafObject->Event(event);
}

void MtfButton::motion(MtfButton *mtfObject, XEvent *xEvent, String *, Cardinal *)
{
	// This action only occurs when motion is turned on.
	ZafButton *zafObject = GetZincObject(mtfObject);

	// Match up zinc.
	ZafEventStruct event(E_MOTIF, xEvent);
	zafObject->ConvertToZafEvent(event);
	event.type = L_CONTINUE_SELECT;
	zafObject->Event(event);
}

void MtfButton::multi_activate(MtfButton *mtfObject, XEvent *xEvent, String *, Cardinal *)
{
	// This action occurs on the "up-click" portion of a mouse double-click.
	ZafButton *zafObject = GetZincObject(mtfObject);

	// Perform the motif multi-activate and zinc double-click.
	ZafEventStruct event(E_MOTIF, xEvent);
	zafObject->ConvertToZafEvent(event);

 	mtfObject->button.click_count = 1;
 	event.type = L_END_SELECT;
 	zafObject->Event(event);
}

void MtfButton::multi_arm(MtfButton *mtfObject, XEvent *xEvent, String *, Cardinal *)
{
	// This action occurs on the "down-click" portion of a mouse double-click.
	ZafButton *zafObject = GetZincObject(mtfObject);

	// Perform the motif multi-activate and zinc double-click.
	ZafEventStruct event(E_MOTIF, xEvent);
	zafObject->ConvertToZafEvent(event);

	if (zafObject->SelectOnDoubleClick())
	{
		mtfObject->button.click_count++;
		event.type = L_DOUBLE_CLICK;
		zafObject->Event(event);
	}
	else
	{
		mtfObject->button.click_count = 1;
		event.type = L_BEGIN_SELECT;
		zafObject->Event(event);
	}
}

void MtfButton::unhighlight(MtfButton *mtfObject)
{
	// Draw the button highlight.
	ZafButton *zafObject = GetZincObject(mtfObject);
	ZafRegionStruct drawRegion = zafObject->BeginDraw();
	zafObject->DrawFocus(drawRegion, (DynamicPtrCast(zafObject->parent, ZafWindow)->DefaultButton(true) == zafObject) ? S_REDISPLAY_DEFAULT : S_NON_CURRENT);
	zafObject->EndDraw();
}

// ----- ZafButton ----------------------------------------------------------

#if defined(ZAF_DEFAULT_DRAW)
ZafEventType ZafButton::Draw(const ZafEventStruct &, ZafEventType ccode)
{
	// Check for draw errors.
	if (!screenID)
		return (S_ERROR);

	// Compute the actual draw region.
	ZafPaletteState state = PaletteState();
	ZafRegionStruct drawRegion = BeginDraw();
	
	// Declaire the box section separately, to get a color change.
	ZafPaletteStruct box;

	ZafPaletteStruct topShadow = LogicalPalette(ZAF_PM_LIGHT_SHADOW, state);
	ZafPaletteStruct bottomShadow = LogicalPalette(ZAF_PM_DARK_SHADOW, state);
	// Redo the state to get the native background color.
	if (buttonType == ZAF_RADIO_BUTTON || buttonType == ZAF_CHECK_BOX ||
	    !AllowToggling())
		state &= ~ZAF_PM_SELECTED;

	// Draw the focus.
	DrawFocus(drawRegion, Focus() ? S_CURRENT : ccode);

	// Draw the border for flat buttons.
	if (Bordered() && buttonType != ZAF_3D_BUTTON)
	{
		display->SetPalette(LogicalPalette(ZAF_PM_OUTLINE, state));
		display->Rectangle(drawRegion, 1, false);
		drawRegion--;
	}

	// Draw the shadow and fill the region.
	if (buttonType != ZAF_RADIO_BUTTON && buttonType != ZAF_CHECK_BOX)
		DrawShadow(drawRegion, (Depressed() || (Selected() && AllowToggling())) ? -depth : depth, ccode);
	if (buttonType == ZAF_RADIO_BUTTON || buttonType == ZAF_CHECK_BOX)
	{
		ZafPaletteStruct background = parent->LogicalPalette(ZAF_PM_BACKGROUND, state);
		display->SetPalette(background);
		if (!Selected())
			box = parent->LogicalPalette(ZAF_PM_BACKGROUND, 
			  state & ~ZAF_PM_CURRENT);
		else
		{
	  		// Change the state so we get the right bitmap color.
	  		state &= ~ZAF_PM_SELECTED;
			box = parent->LogicalPalette(ZAF_PM_TEXT, state & ~ZAF_PM_CURRENT);
	  		state &= ~ZAF_PM_SELECTED;
		}
	}
	else
		display->SetPalette(LogicalPalette(ZAF_PM_BACKGROUND, state));
	display->Rectangle(drawRegion, 0, true);

	// Clip the region.
	if (buttonType == ZAF_RADIO_BUTTON || buttonType == ZAF_CHECK_BOX)
	{
		drawRegion.left += ZAF_MARGIN_SIZE;
		drawRegion.right -= ZAF_MARGIN_SIZE;
	}
	else if (AutoSize() && (Bordered() || SystemObject()))
		drawRegion -= ZAF_MARGIN_SIZE;
	display->SetClipRegion(drawRegion);

	// Compute the draw regions.
	int checkBoxSize = display->cellHeight / 2;
	ZafRegionStruct imageRegion, textRegion;
	display->SetPalette(LogicalPalette(ZAF_PM_TEXT, state));
	if (buttonType == ZAF_RADIO_BUTTON || buttonType == ZAF_CHECK_BOX)
		imageRegion.Assign(1, 1, checkBoxSize, checkBoxSize, ZAF_PIXEL);
	else if (bitmapData)
		imageRegion.Assign(1, 1, bitmapData->Width(), bitmapData->Height(), ZAF_PIXEL);
	else
		imageRegion.Assign(1, 1, 0, 0, ZAF_PIXEL);
	if (stringData)
		textRegion = display->TextSize(*stringData);
	else
		textRegion.Assign(1, 1, 0, 0, ZAF_PIXEL);
	ComputeDrawRegions(drawRegion, imageRegion, textRegion, ZAF_MARGIN_SIZE);

	// Draw the bitmap.
	if (ButtonType() == ZAF_RADIO_BUTTON || ButtonType() == ZAF_CHECK_BOX)
	{
		if ((display->DisplayMode() == ZAF_DISPLAY_MONO && box.monoBackground < ZAF_MAXCOLORS) ||
			(display->DisplayMode() != ZAF_DISPLAY_MONO && box.colorBackground < ZAF_MAXCOLORS))
		{
			XGCValues values;
			XtGCMask mask = GCForeground | GCBackground;
			if (display->DisplayMode() == ZAF_DISPLAY_MONO)
				values.background = ((ZafScreenDisplay *)display)->monoTable[box.monoBackground].pixel;
			else
				values.background = ((ZafScreenDisplay *)display)->colorTable[box.colorBackground].pixel;
			values.foreground = values.background;
			box.osPalette = XtGetGC(screenID, mask, &values);
		}
		if (ButtonType() == ZAF_RADIO_BUTTON)
		{
			_XmDrawDiamond(XtDisplay(screenID), XtWindow(screenID), 
				(Depressed() || Selected()) ? bottomShadow.osPalette : topShadow.osPalette,
				(Depressed() || Selected()) ? topShadow.osPalette : bottomShadow.osPalette,
				box.osPalette, imageRegion.left, imageRegion.top, checkBoxSize,
				checkBoxSize, ZAF_SHADOW_SIZE, true);
		}
		else 
		{
			_XmDrawShadows(XtDisplay(screenID), XtWindow(screenID),
				topShadow.osPalette, bottomShadow.osPalette, imageRegion.left,
				imageRegion.top, checkBoxSize, checkBoxSize,
				ZAF_SHADOW_SIZE, (Depressed() || Selected()) ? XmSHADOW_IN : XmSHADOW_OUT);
			XFillRectangle(XtDisplay(screenID), XtWindow(screenID),
				box.osPalette, imageRegion.left + ZAF_SHADOW_SIZE,
				imageRegion.top + ZAF_SHADOW_SIZE,
				checkBoxSize - 2 * ZAF_SHADOW_SIZE,
				checkBoxSize - 2 * ZAF_SHADOW_SIZE);
		}
	}
	else if (bitmapData)
	{
		display->SetPalette(LogicalPalette(ZAF_PM_OUTLINE, state));
		display->Bitmap(imageRegion.left, imageRegion.top, *bitmapData);
	}

	// Draw the text.
	if (stringData)
	{
		display->SetPalette(LogicalPalette(ZAF_PM_TEXT, state));
		display->Text(textRegion, stringData->Text(), -1, hzJustify, ZAF_VT_CENTER, hotKeyIndex);
	}

	// Return the control code.
	EndDraw();
	return (ccode);
}

ZafEventType ZafButton::DrawBorder(ZafRegionStruct &region, ZafEventType ccode)
{
	// Defer to the base class.
	return (ZafWindowObject::DrawBorder(region, ccode));
}

ZafEventType ZafButton::DrawFocus(ZafRegionStruct &region, ZafEventType ccode)
{
	// Check for a valid system object.
	if (!SystemObject() || (ParentDrawFocus() && parent))
		return (parent->DrawFocus(region, ccode));

	// Draw the focus rectangle.
	MtfButton *mtfObject = MtfButton::GetMotifWidget(screenID);
	if (!mtfObject->primitive.highlight_thickness)
		return (ccode);
	else if (ccode == S_CURRENT)
		(*xmPushButtonClassRec.primitive_class.border_highlight)(screenID);
	else
		(*xmPushButtonClassRec.primitive_class.border_unhighlight)(screenID);
	region -= mtfObject->primitive.highlight_thickness;

	// Draw the default button.
	if (!AllowDefault() || buttonType == ZAF_RADIO_BUTTON || buttonType == ZAF_CHECK_BOX)
		return (ccode);
	ZafButton *button = DynamicPtrCast(parent, ZafWindow)->DefaultButton(false);

	ZafPaletteStruct background = parent->LogicalPalette(ZAF_PM_BACKGROUND, ZAF_PM_ANY_STATE);
	display->SetPalette(background);
	if (ccode == S_CURRENT || (ccode != S_NON_CURRENT && button == this))
	{
		display->Rectangle(region, 1);
		region--;
		DrawShadow(region, -1, ccode);
	}
	else
	{
		display->Rectangle(region, 2);
		region -= 2;
	}
	display->SetPalette(background);
	display->Rectangle(region, 3);
	region -= 3;

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

ZafEventType ZafButton::DrawShadow(ZafRegionStruct &region, int tDepth, ZafEventType ccode)
{
	// Defer to the base class.
	return (ZafWindowObject::DrawShadow(region, tDepth, ccode));
}
#endif

ZafEventType ZafButton::Event(const ZafEventStruct &event)
{
	// Check for edit mode.
	ZafEventType ccode = event.type;
	if (editMode && event.InputType() == L_LOGICAL_EVENT)
		return (ZafWindowObject::Event(event));
	else if (event.InputType() == E_KEY)
		ccode = LogicalEvent(event);

	// Check for zinc events.
	switch (ccode)
	{
	// ----- Create messages -----
	case S_INITIALIZE:
		ccode = ZafWindowObject::Event(event);

		// If AllowDefault button we must add some region for the larger border.
		if (AllowDefault() && !EditMode())
			SetRegion(saveRegion += ZAF_DEFBTN_MARGIN);

		// Reset the intervalID.
		intervalID = 0;
		lastMousePosition.Assign(-1, -1, ZAF_PIXEL);
		break;

	case S_DEINITIALIZE:
		ccode = ZafWindowObject::Event(event);

		// If AllowDefault button we must reset the region to back to original size.
		if (AllowDefault() && !EditMode())
			SetRegion(saveRegion -= ZAF_DEFBTN_MARGIN);
		break;

	case S_COMPUTE_SIZE:
		// Defer to the base class.
		zafRegion = saveRegion;
		ccode = ZafWindowObject::Event(event);

		// Check for default button and adjust region back so positioning is correct.
		if (AllowDefault() && !EditMode())
			zafRegion -= ZAF_DEFBTN_MARGIN;

		// Check for an auto-size button.
		if (AutoSize())
			zafRegion.top = zafRegion.bottom - display->cellHeight + display->preSpace + display->postSpace - 1;
		if (AutoSize() &&
			(bitmapData || buttonType == ZAF_RADIO_BUTTON || buttonType == ZAF_CHECK_BOX))
		{
			// Determine the minimum size for the bitmap. (1/2 cellHeight for 
			// check box, radio button)
			int minHeight = bitmapData ? bitmapData->Height() : display->cellHeight / 2;

			// Add the minimum size for text.
			if (stringData && stringData->Char(0))
			{
				display->SetPalette(LogicalPalette(ZAF_PM_TEXT, PaletteState()));
				ZafRegionStruct region = display->TextSize(stringData->Text());
				if (HzJustify() == ZAF_HZ_CENTER)
					minHeight += region.Height() + ZAF_MARGIN_SIZE; // space between text and bitmap.
				else if (zafRegion.Height() < region.Height())
					minHeight = region.Height();
			}

			// Adjust for general spacing.
			if (buttonType != ZAF_RADIO_BUTTON && buttonType != ZAF_CHECK_BOX)
				minHeight += 2 * ZAF_MARGIN_SIZE;
			// Adjust for shadowing.
			if (buttonType != ZAF_FLAT_BUTTON && buttonType != ZAF_RADIO_BUTTON && buttonType != ZAF_CHECK_BOX)
				minHeight += 2 * ZAF_SHADOW_SIZE;
			// Adjust for border.
			if (Bordered())
				minHeight += 2;
			// Adjust for focus highlight.
			minHeight += 2 * ZAF_BORDER_SIZE;
			if (minHeight > zafRegion.Height())
				zafRegion.top = zafRegion.bottom - minHeight + 1;
		}

		// Check for default button and adjust region so border displays correctly.
		if (AllowDefault() && !EditMode())
			zafRegion += ZAF_DEFBTN_MARGIN;
		break;

	// ----- Actions -----
	case L_SELECT:
		// Toggle the button.
		if (Disabled())
			break;
		else if (AllowToggling())
			ToggleSelected();

		// Call the associated user function.
		ccode = (this->*memberUserFunction)(event, ccode);
		break;

	case L_BEGIN_SELECT:
		// Depress the button.
		if (Disabled())
			break;
		SetDepressed(zafRegion.Overlap(event.position, zafRegion.left, zafRegion.top) ? true : false);

		// Check the button's selection status.
		if (SelectOnDownClick() && Depressed())
		{
			if (AllowToggling() && !SelectOnDoubleClick())
				ToggleSelected();
			ccode = (this->*memberUserFunction)(event, L_SELECT);
		}

		// Set the repeat process.
		if (AutoRepeatSelection() && SystemObject())
		{
			lastMousePosition = event.position;
			if (intervalID)
				XtRemoveTimeOut(intervalID);
			intervalID = XtAppAddTimeOut(((ZafScreenDisplay *)display)->xAppContext, InitialDelay(),
				(XtTimerCallbackProc)Repeat, (XtPointer)this);
		}
		break;

	case L_CONTINUE_SELECT:
		// Depress the button.
		if (Disabled())
			break;
		SetDepressed(zafRegion.Overlap(event.position, zafRegion.left, zafRegion.top) ? true : false);

		// Update the button information.
		if (AutoRepeatSelection() && SystemObject() && Depressed())
		{
			// Send the select message.
			if (Depressed())
				ccode = (this->*memberUserFunction)(event, L_SELECT);

			// Coninue the repeat process.
			lastMousePosition = event.position;
			if (intervalID)
				XtRemoveTimeOut(intervalID);
			intervalID = XtAppAddTimeOut(((ZafScreenDisplay *)display)->xAppContext, RepeatDelay(),
				(XtTimerCallbackProc)Repeat, (XtPointer)this);
		}
		break;

	case L_END_SELECT:
		// End the repeat process.
		if (Disabled())
			break;
		else if (intervalID)
			 XtRemoveTimeOut(intervalID);
		lastMousePosition.Assign(-1, -1, ZAF_PIXEL);
		intervalID = 0;

		// Un-depress the button.
		if (!Depressed())
			break; // user is positioned off the button.
		SetDepressed(false);

		// Check the button's selection status.
		if (!SelectOnDownClick())
		{
			// Toggle the selection.
			if (AllowToggling() && !SelectOnDoubleClick())
				ToggleSelected();

			// Adjust the coordinate for self-comparison.
			if (zafRegion.Overlap(event.position, zafRegion.left, zafRegion.top))
				ccode = (this->*memberUserFunction)(event, L_SELECT);
		}
		break;

	case L_DOUBLE_CLICK:
		// Make sure the button double-clicks.
		if (Disabled())
			break;
		else if (SelectOnDoubleClick())
			ccode = (this->*memberUserFunction)(event, ccode);
		break;

	// ----- Data transfers -----
	case S_COPY_DATA:
		{
		ZafWindowObject *object = event.windowObject;
		ZafButton *button = DynamicPtrCast(object, ZafButton);
		if (button && ButtonType() != ZAF_RADIO_BUTTON && ButtonType() != ZAF_CHECK_BOX)
		{
			if (button->BitmapData())
				bitmapData->SetBitmap(*button->BitmapData());
			else
				SetBitmapData(ZAF_NULLP(ZafBitmapData));
		}
		}
		break;

	case S_SET_DATA:
		{
		ZafWindowObject *object = event.windowObject;
		if (object)
		{
			ZafButton *button = DynamicPtrCast(object, ZafButton);
			if (button && ButtonType() != ZAF_RADIO_BUTTON && ButtonType() != ZAF_CHECK_BOX)
				SetBitmapData(button->BitmapData());
		}
		else
			SetBitmapData(new ZafBitmapData(*bitmapData));
		}
		break;

	case S_HOT_KEY:
		if (!Disabled() && event.key.value == hotKeyChar)
		{
			// See if the object has focus.
			if (!Focus())
				SetFocus(true);

			// Make sure the object is selected.
			if (!Selected() && AllowToggling())
				SetSelected(true);

			// Call the associated user function.
			ccode = (this->*memberUserFunction)(event, L_SELECT);
		}
		else
			ccode = S_UNKNOWN;
		break;

	case S_REDISPLAY_DEFAULT:
		if (SystemObject() && screenID)
		{
			ZafRegionStruct drawRegion = BeginDraw();
			DrawFocus(drawRegion, ccode);
			EndDraw();
		}
		break;

	// ----- Motif or unknown messages -----
	case E_MOTIF:
		ccode = ZafWindowObject::Event(event);
		break;

	default:
		ccode = ZafWindowObject::Event(event);
		break;
	}

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

// ----- Attributes ---------------------------------------------------------

bool ZafButton::SetDepressed(bool tDepressed)
{
	// Make sure the attribute has changed.
	if (depressed != tDepressed)
	{
		// Change the attribute.
		depressed = tDepressed;

		// Update the object, if appropriate.
		if (screenID && !selected)
			Redisplay();
	}

	// Return the current attribute.
	return depressed;
}

bool ZafButton::SetDisabled(bool setDisabled)
{
	return (ZafWindowObject::SetDisabled(setDisabled));
}

bool ZafButton::SetSelected(bool setSelected)
{
	// Defer to the base class.
	bool oldSelected = selected;
	ZafWindowObject::SetSelected(setSelected);

	// Check for needed update.
	if (oldSelected != selected && systemObject && screenID)
		Redisplay();

	// Return the current attribute.
	return (selected);
}

bool ZafButton::SetNoncurrent(bool setNoncurrent)
{
	if (noncurrent != setNoncurrent)
	{
		// Defer the first part to the base class.
		ZafWindowObject::SetNoncurrent(setNoncurrent);

		if (screenID && SystemObject())
 		{	
			if (noncurrent && !AllowDefault())
				XtVaSetValues(screenID, XmNhighlightThickness, 0, 0);
			else
				XtVaSetValues(screenID, XmNhighlightThickness, 2, 0);
			Event(N_SIZE);
		}
	}
	return (noncurrent);				
}

bool ZafButton::SetVisible(bool setVisible)
{
	// Defer to the base class.
	return (ZafWindowObject::SetVisible(setVisible));
}

// ----- OS Specific Functions ----------------------------------------------

ZafEventType ZafButton::OSEvent(const ZafEventStruct &)
{
	return (S_ERROR);
}

void ZafButton::OSMapPalette(ZafPaletteStruct &palette, ZafPaletteType type, ZafPaletteState state)
{
	// Check for a non-system object.
	if (!screenID)
		return;
	else if (!SystemObject())
	{
		parent->OSMapPalette(palette, type, state);
		return;
	}

	// Determine the proper palette.
	MtfButton *mtfObject = MtfButton::GetMotifWidget(screenID);
	if (type == ZAF_PM_TEXT || type == ZAF_PM_HOT_KEY)
#if defined(__linux__) || defined(_IBM_RS6000)
		if (state & ZAF_PM_DISABLED)
		{
			XGCValues values;
			XtGCMask mask = GCForeground | GCBackground | GCFillStyle | GCTile;
			values.background = mtfObject->core.background_pixel;
			values.foreground = mtfObject->primitive.foreground;
			values.fill_style = FillTiled;
			values.tile = XmGetPixmapByDepth(XtScreen(screenID), "50_foreground",
				values.foreground, values.background, mtfObject->core.depth);
			palette.osPalette = mtfObject->label.insensitive_GC;
			XChangeGC(XtDisplay(screenID), palette.osPalette, mask, &values);
		}
		else
			palette.osPalette = mtfObject->label.normal_GC; 
#else
		palette.osPalette = (state & ZAF_PM_DISABLED) ? mtfObject->label.insensitive_GC : mtfObject->label.normal_GC;
#endif
	else if (type == ZAF_PM_BACKGROUND)
		palette.osPalette = (state & ZAF_PM_SELECTED) ? mtfObject->button.fill_gc : mtfObject->button.background_gc;
	else
		ZafWindowObject::OSMapPalette(palette, type, state);
}

void ZafButton::OSRegisterObject(void)
{
	// Create the Motif widget.
	char *name = zafCodeSet->ConvertToOSString(stringID, 0, false);
	screenID = parent->OSScreenID(SupportObject() ? ZAF_FRAMEID : ZAF_CLIENTID);
	screenID = XtVaCreateWidget(name,
		(WidgetClass)&MtfButton::classRecord, screenID,
		// user information
		XmNuserData, this,
		// miscellaneous
		XmNnavigationType, XmTAB_GROUP,
		XmNsensitive, Disabled(false) ? false : true,
		XmNtraversalOn, Noncurrent() ? false : true,
		XmNhighlightThickness, (Noncurrent() && !AllowDefault()) ? 0 : 2, 	
		0);
	if (HzJustify() == ZAF_HZ_LEFT)
		XtVaSetValues(screenID, XmNalignment, XmALIGNMENT_BEGINNING, 0);
	else if (HzJustify() == ZAF_HZ_RIGHT)
		XtVaSetValues(screenID, XmNalignment, XmALIGNMENT_END, 0);
	else
		XtVaSetValues(screenID, XmNalignment, XmALIGNMENT_CENTER, 0);

	// Check the button's background color.
	if (ButtonType() == ZAF_RADIO_BUTTON || ButtonType() == ZAF_CHECK_BOX)
	{
		unsigned long valueMask = GCForeground;
		XGCValues values;
		ZafPaletteState state = 0;
		ZafPaletteStruct background = parent->LogicalPalette(ZAF_PM_BACKGROUND, state);
		XGetGCValues(XtDisplay(parent->OSScreenID(ZAF_CLIENTID)),
			background.osPalette, valueMask, &values);
		screenID->core.background_pixel = values.foreground;
	}

	// Override translations.
	XtTranslations translationTable = XtParseTranslationTable(MtfButton::translations);
	XtOverrideTranslations(screenID, translationTable);

	// Add edit mode overrides.
	ZafEditModeRegister(this);

	// Add drag/drop capabilities.
	ZafDropSiteRegister(this);
}

void ZafButton::Repeat(ZafButton *zafObject, XtIntervalId *)
{
	// Simulate repeat selections by sending the L_CONTINUE_SELECT message
	// to the button.
	zafObject->intervalID = 0;
	if (zafObject->Focus())
	{
		ZafEventStruct event(L_CONTINUE_SELECT, 0, zafObject->lastMousePosition);
		zafObject->Event(event);
	}
}

ZafError ZafButton::OSSetImage(void)
{
	// The ZafButton object draws it's own information, so no APIs need
	// to be called.
	if ((bitmapData || buttonType == ZAF_RADIO_BUTTON || buttonType == ZAF_CHECK_BOX) &&
		screenID && AutomaticUpdate())
		RedisplayData();
	return (ZAF_ERROR_NONE);
}

ZafError ZafButton::OSSetText(void)
{
	// The ZafButton object draws it's own information, so the object
	// just needs to be updated.
	if (stringData && screenID && AutomaticUpdate())
		RedisplayData();
	return (ZAF_ERROR_NONE);
}

void ZafButton::OSSize(void)
{
	// Defer to the base class.
	ZafWindowObject::OSSize();
}

ZafError ZafButton::OSUpdatePalettes(ZafPaletteType , ZafPaletteType )
{
	// Make sure the widget can be updated.
	if (!screenID || !AutomaticUpdate())
		return (ZAF_ERROR_INVALID);
	if (!SystemObject())
	{
		Redisplay();
		return (ZAF_ERROR_NONE);
	}

	// Bypass the ZafWindowObject layer because we do all the drawing
	// ourselves in Draw().  Thus, no system changes are needed.
	ZafRegionStruct tRegion = zafRegion;
	Event(S_COMPUTE_SIZE);
	if (tRegion != zafRegion)
		OSSize();
	else
		Redisplay();
	return (ZAF_ERROR_NONE);
}

