/**
 *
 * $Id: Gadget.c,v 1.16 1999/02/02 18:23:10 gritton Exp $
 *
 * Copyright (C) 1995 Free Software Foundation, Inc.
 *
 * This file is part of the GNU LessTif Library.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 **/

static const char rcsid[] = "$Id: Gadget.c,v 1.16 1999/02/02 18:23:10 gritton Exp $";

#include <LTconfig.h>
#include <XmI/XmI.h>

#include <Xm/XmP.h>
#include <Xm/GadgetP.h>
#include <Xm/ExtObjectP.h>
#include <Xm/RepType.h>
#include <string.h>

#include <XmI/DebugUtil.h>

static void class_initialize(void);

static void class_part_initialize(WidgetClass w_class);

static void initialize(Widget request, Widget new_w,
		       ArgList args, Cardinal *num_args);

static void destroy(Widget w);

static Boolean set_values(Widget current, Widget request, Widget new_w,
			  ArgList args, Cardinal *num_args);


static void input_dispatch(Widget w, XEvent *event, Mask event_mask);

#if 0
static Boolean visual_change(Widget w, Widget parent, Widget n);
#endif

static void focus_change(Widget w, XmFocusChange fevent);

static void secondary_object_create(Widget req, Widget new_w,
				    ArgList args, Cardinal *num_args);

static XmNavigability widget_navigable(Widget w);

static void gadget_border_unhighlight(Widget w);

static void gadget_border_highlight(Widget w);

static void _XmGetParentHighlightColor(Widget w,
				       int offset, XtArgVal *value);

static void _XmGetParentTopShadowColor(Widget w,
				       int offset, XtArgVal *value);

static void _XmGetParentBottomShadowColor(Widget w,
					  int offset, XtArgVal *value);

/*
 * Resources for the Gadget class
 */
#define Offset(field) XtOffsetOf(XmGadgetRec, gadget.field)
static XtResource resources[] =
{
    {
	XmNx, XmCPosition, XmRHorizontalPosition,
	sizeof(Dimension), XtOffsetOf(XmGadgetRec, rectangle.x),
	XmRImmediate, (XtPointer)0
    },
    {
	XmNy, XmCPosition, XmRVerticalPosition,
	sizeof(Dimension), XtOffsetOf(XmGadgetRec, rectangle.y),
	XmRImmediate, (XtPointer)0
    },
    {
	XmNwidth, XmCDimension, XmRHorizontalDimension,
	sizeof(Dimension), XtOffsetOf(XmGadgetRec, rectangle.width),
	XmRImmediate, (XtPointer)0
    },
    {
	XmNheight, XmCDimension, XmRVerticalDimension,
	sizeof(Dimension), XtOffsetOf(XmGadgetRec, rectangle.height),
	XmRImmediate, (XtPointer)0
    },
    {
	XmNborderWidth, XmCBorderWidth, XmRHorizontalDimension,
	sizeof(Dimension), XtOffsetOf(XmGadgetRec, rectangle.border_width),
	XmRImmediate, (XtPointer)0
    },
    {
	XmNtraversalOn, XmCTraversalOn, XmRBoolean,
	sizeof(Boolean), Offset(traversal_on),
	XmRImmediate, (XtPointer)True
    },
    {
	XmNhighlightOnEnter, XmCHighlightOnEnter, XmRBoolean,
	sizeof(Boolean), Offset(highlight_on_enter),
	XmRImmediate, (XtPointer)False
    },
    {
	XmNhighlightThickness, XmCHighlightThickness, XmRHorizontalDimension,
	sizeof(Dimension), Offset(highlight_thickness),
	XmRImmediate, (XtPointer)2
    },
    {
	XmNshadowThickness, XmCShadowThickness, XmRHorizontalDimension,
	sizeof(Dimension), Offset(shadow_thickness),
	XmRImmediate, (XtPointer)2
    },
    {
	XmNunitType, XmCUnitType, XmRUnitType,
	sizeof(unsigned char), Offset(unit_type),
	XmRCallProc, (XtPointer)_XmUnitTypeDefault
    },
    {
	XmNnavigationType, XmCNavigationType, XmRNavigationType,
	sizeof(unsigned char), Offset(navigation_type),
	XmRImmediate, (XtPointer)XmNONE
    },
    {
	XmNhelpCallback, XmCCallback, XmRCallback,
	sizeof(XtCallbackList), Offset(help_callback),
	XmRPointer, (XtPointer)NULL
    },
    {
	XmNuserData, XmCUserData, XmRPointer,
	sizeof(XtPointer), Offset(user_data),
	XmRPointer, NULL
    }
};

#define GOffset(field)	XtOffset(XmGadget, gadget.field)
#define ROffset(field)	XtOffset(XmGadget, rectangle.field)
#define OOffset(field)	XtOffset(XmGadget, object.field)
static XmSyntheticResource syn_resources[] =
{
    /* rectangle part */
    {
	XmNx,
	sizeof(Position), ROffset(x),
	_XmFromHorizontalPixels, _XmToHorizontalPixels
    },
    {
	XmNy,
	sizeof(Position), ROffset(y),
	_XmFromVerticalPixels, _XmToVerticalPixels
    },
    {
	XmNwidth,
	sizeof(Dimension), ROffset(width),
	_XmFromHorizontalPixels, _XmToHorizontalPixels
    },
    {
	XmNheight,
	sizeof(Dimension), ROffset(height),
	_XmFromVerticalPixels, _XmToVerticalPixels
    },
    /* gadget */
    {
	XmNhighlightThickness,
	sizeof(Dimension), GOffset(highlight_thickness),
	_XmFromHorizontalPixels, _XmToHorizontalPixels
    },
    {
	XmNshadowThickness,
	sizeof(Dimension), GOffset(shadow_thickness),
	_XmFromHorizontalPixels, _XmToHorizontalPixels
    },
    /* object */
    {
	XmNhighlightColor,
	sizeof(Pixel), OOffset(parent),
	_XmGetParentHighlightColor, NULL
    },
    {
	XmNtopShadowColor,
	sizeof(Pixel), OOffset(parent),
	_XmGetParentTopShadowColor, NULL
    },
    {
	XmNbottomShadowColor,
	sizeof(Pixel), OOffset(parent),
	_XmGetParentBottomShadowColor, NULL
    }
};

/* *INDENT-OFF* */
static XmBaseClassExtRec _XmGadgetRectClassExtRec = {
    /* next_extension            */ NULL,
    /* record_type               */ NULLQUARK,                             
    /* version                   */ XmBaseClassExtVersion,
    /* size                      */ sizeof(XmBaseClassExtRec),
    /* initialize_prehook        */ NULL,
    /* set_values_prehook        */ NULL,
    /* initialize_posthook       */ NULL,
    /* set_values_posthook       */ NULL,
    /* secondary_object_class    */ NULL,
    /* secondary_object_create   */ secondary_object_create,
    /* get_secondary_resources   */ NULL,
    /* fast_subclass             */ { 0 },
    /* get_values_prehook        */ NULL,
    /* get_values_posthook       */ NULL,
    /* class_part_init_prehook   */ NULL,
    /* class_part_init_posthook  */ NULL,
    /* ext_resources             */ NULL,
    /* compiled_ext_resources    */ NULL,
    /* num_ext_resources         */ 0,
    /* use_sub_resources         */ False,
    /* widget_navigable          */ widget_navigable,
    /* focus_change              */ focus_change,
    /* wrapper_data              */ NULL
};

static XmGadgetClassExtRec _XmGadgetClassExtRec = {
    /* next_extension            */ NULL,
    /* record_type               */ NULLQUARK,
    /* version                   */ XmGadgetClassExtVersion,
    /* size                      */ sizeof(XmGadgetClassExtRec),
    /* widget_baseline_proc      */ NULL,
    /* display_rect_proc         */ NULL,
};

XmGadgetClassRec xmGadgetClassRec = {
    /* RectObj class part */
    {
	/* superclass            */ (WidgetClass) &rectObjClassRec,
        /* class_name            */ "XmGadget",
	/* widget_size           */ sizeof(XmGadgetRec),
	/* class_initialize      */ class_initialize,
	/* class_part_initialize */ class_part_initialize,
	/* class_inited          */ False,
	/* initialize            */ initialize,
	/* initialize_hook       */ NULL,
	/* realize               */ NULL,
	/* actions               */ NULL,
	/* num_actions           */ 0,
	/* resources             */ resources,
	/* num_resources         */ XtNumber(resources),
	/* xrm_class             */ NULLQUARK,
	/* compress_motion       */ True /*False*/,
	/* compress_exposure     */ XtExposeCompressSeries /*XtExposeNoCompress*/,
	/* compress_enterleave   */ True /*False*/,
	/* visible_interest      */ False,
	/* destroy               */ destroy,
	/* resize                */ NULL,
	/* expose                */ NULL,
	/* set_values            */ set_values,
	/* set_values_hook       */ NULL,
	/* set_values_almost     */ XtInheritSetValuesAlmost,
	/* get_values_hook       */ _XmGadgetGetValuesHook,
	/* accept_focus          */ NULL,
	/* version               */ XtVersion,
	/* callback offsets      */ NULL,
	/* tm_table              */ NULL,
	/* query_geometry        */ NULL,
        /* display_accelerator   */ NULL,
	/* extension             */ (XtPointer)&_XmGadgetRectClassExtRec
    },
    /* XmGadget part */
    {
	/* border_highlight   */ gadget_border_highlight,
        /* border_unhighlight */ gadget_border_unhighlight,
        /* arm_and_activate   */ NULL,
        /* input_dispatch     */ input_dispatch, 
        /* visual_change      */ NULL /*visual_change*/,
        /* syn_resources      */ syn_resources,
        /* num_syn_resources  */ XtNumber(syn_resources),
	/* cache_part         */ NULL,
        /* extension          */ (XtPointer)&_XmGadgetClassExtRec
    }

};
/* *INDENT-ON* */


WidgetClass xmGadgetClass = (WidgetClass)&xmGadgetClassRec;

static void
class_initialize(void)
{
    _XmRegisterConverters();
    _XmRegisterPixmapConverters();
    _XmInitializeExtensions();
    _XmGadgetRectClassExtRec.record_type = XmQmotif;
}

static void
class_part_initialize(WidgetClass widget_class)
{
    XmGadgetClass gc, sc;

    gc = (XmGadgetClass)widget_class;
    sc = (XmGadgetClass)((XmGadgetClass)widget_class)->rect_class.superclass;

    if (gc->gadget_class.border_highlight == XmInheritBorderHighlight)
    {
	gc->gadget_class.border_highlight =
	    sc->gadget_class.border_highlight;
    }
    if (gc->gadget_class.border_unhighlight == XmInheritBorderUnhighlight)
    {
	gc->gadget_class.border_unhighlight =
	    sc->gadget_class.border_unhighlight;
    }
    if (gc->gadget_class.visual_change == XmInheritVisualChange)
    {
	gc->gadget_class.visual_change =
	    sc->gadget_class.visual_change;
    }
    if (gc->gadget_class.input_dispatch == XmInheritInputDispatch)
    {
	gc->gadget_class.input_dispatch =
	    sc->gadget_class.input_dispatch;
    }
    if (gc->gadget_class.arm_and_activate == XmInheritArmAndActivate)
    {
	gc->gadget_class.arm_and_activate =
	    sc->gadget_class.arm_and_activate;
    }

    if (widget_class != xmGadgetClass)
    {
	XmGadgetClassExt *gce = NULL, *sce = NULL;

	gce = _XmGetGadgetClassExtPtr(gc, NULLQUARK);
	sce = _XmGetGadgetClassExtPtr(sc, NULLQUARK);

	if (gce && sce && *gce && *sce)
	{
	    if ((*gce)->widget_baseline == XmInheritBaselineProc)
	    {
		(*gce)->widget_baseline = (*sce)->widget_baseline;
	    }
	    if ((*gce)->widget_display_rect == XmInheritDisplayRectProc)
	    {
		(*gce)->widget_display_rect = (*sce)->widget_display_rect;
	    }
	}
    }

    _XmBaseClassPartInitialize(widget_class);
    _XmFastSubclassInit(widget_class, XmGADGET_BIT);

    /* compile the resources */
    if (widget_class == xmGadgetClass)
    {
	_XmSortResourceList((XrmResource **)gc->rect_class.resources,
			    gc->rect_class.num_resources);
    }

    _XmBuildGadgetResources(widget_class);
}

static void
secondary_object_create(Widget req, Widget new_w,
			ArgList args, Cardinal *num_args)
{
    XmBaseClassExt *bce;
    Arg argl[1];
    ArgList merged;

    XtSetArg(argl[0], XmNlogicalParent, new_w);

    if (*num_args)
    {
	merged = XtMergeArgLists(args, *num_args, argl, XtNumber(argl));

	bce = _XmGetBaseClassExtPtr(XtClass(new_w), XmQmotif);

	XtCreateWidget(XtName(new_w), (*bce)->secondaryObjectClass,
		       XtParent(new_w)
		       ? XtParent(new_w)
		       : new_w,
		       merged, *num_args + 1);

	XtFree((char *)merged);
    }
    else
    {
	bce = _XmGetBaseClassExtPtr(XtClass(new_w), XmQmotif);

	XtCreateWidget(XtName(new_w), (*bce)->secondaryObjectClass,
		       XtParent(new_w)
		       ? XtParent(new_w)
		       : new_w,
		       argl, 1);
    }
}

static void
initialize(Widget request,
	   Widget new_w,
	   ArgList args,
	   Cardinal *num_args)
{
    XmBaseClassExt bce;

    /* Force the borderWidth to 0 */
    new_w->core.border_width = 0;

    if (!XmRepTypeValidValue(XmRepTypeGetId(XmRNavigationType),
			     G_NavigationType(new_w),
			     new_w))
    {
	G_NavigationType(new_w) = XmNONE;
    }

    if (!XmRepTypeValidValue(XmRepTypeGetId(XmRUnitType),
			     G_UnitType(new_w),
			     new_w))
    {
	G_UnitType(new_w) = XmPIXELS;
    }

    if (XtWidth(request) == (Dimension)0)
    {
	XtWidth(new_w) = (G_HighlightThickness(new_w) * 2
			  + G_ShadowThickness(new_w) * 2);
    }
    if (XtHeight(request) == (Dimension)0)
    {
	XtHeight(new_w) = (G_HighlightThickness(new_w) * 2
			   + G_ShadowThickness(new_w) * 2);
    }

    _XmGadgetImportArgs(new_w, args, num_args);

    /* BaseClass stuff provides this */
    _XmGadgetImportSecondaryArgs(new_w, args, num_args);

    bce = *(XmBaseClassExt *)_XmGetBaseClassExtPtr(XtClass(new_w), XmQmotif);

    if (bce && bce->secondaryObjectClass)
    {
	if (bce->secondaryObjectCreate)
	{
	    (bce->secondaryObjectCreate) (request, new_w, args, num_args);
	}
    }

    _XmNavigInitialize(request, new_w, args, num_args);

    G_EventMask(new_w) = 0;
    G_HaveTraversal(new_w) = False;
    G_Highlighted(new_w) = False;
    G_HighlightDrawn(new_w) = False;
}

static void
destroy(Widget w)
{
    _XmNavigDestroy(w);
}

static Boolean
set_values(Widget current,
	   Widget request,
	   Widget new_w,
	   ArgList args,
	   Cardinal *num_args)
{
    Boolean need_refresh = False;

    /* Force the borderWidth to 0 */
    new_w->core.border_width = 0;

    if (!XmRepTypeValidValue(XmRepTypeGetId(XmRUnitType),
			     G_UnitType(new_w),
			     new_w))
    {
	G_UnitType(new_w) = G_UnitType(current);
    }

    if (!XmRepTypeValidValue(XmRepTypeGetId(XmRNavigationType),
			     G_NavigationType(new_w),
			     new_w))
    {
	G_NavigationType(new_w) = G_NavigationType(current);
    }

    _XmGadgetImportArgs(new_w, args, num_args);

    need_refresh = _XmNavigSetValues(current, request, new_w, args, num_args);

    return need_refresh;
}

static void
input_dispatch(Widget w,
	       XEvent *event,
	       Mask event_mask)
{
    DEBUGOUT(XdbDebug(__FILE__, w,
		      "Inside base Gadget input_dispatch() routine "
		      "(you really should write one for this gadget... :)\n"));

    if (event_mask & XmENTER_EVENT)
    {
	DEBUGOUT(XdbDebug(__FILE__, w,
			  "Enter event sent to gadget %d\n", w));
    }
    else if (event_mask & XmLEAVE_EVENT)
    {
	DEBUGOUT(XdbDebug(__FILE__, w,
			  "Leave event sent to gadget %d\n", w));
    }
    else if (event_mask & XmFOCUS_IN_EVENT)
    {
	DEBUGOUT(XdbDebug(__FILE__, w,
			  "Focus in event sent to gadget %d\n", w));
    }
    else if (event_mask & XmFOCUS_OUT_EVENT)
    {
	DEBUGOUT(XdbDebug(__FILE__, w,
			  "Focus out event sent to gadget %d\n", w));
    }
    else if (event_mask & XmMOTION_EVENT)
    {
	DEBUGOUT(XdbDebug(__FILE__, w,
			  "Motion event sent to gadget %d\n", w));
    }
    else if (event_mask & XmARM_EVENT)
    {
	DEBUGOUT(XdbDebug(__FILE__, w,
			  "Arm event sent to gadget %d\n", w));
    }
    else if (event_mask & XmACTIVATE_EVENT)
    {
	DEBUGOUT(XdbDebug(__FILE__, w,
			  "Activate event sent to gadget %d\n", w));
    }
    else if (event_mask & XmHELP_EVENT)
    {
	DEBUGOUT(XdbDebug(__FILE__, w,
			  "Help event sent to gadget %d\n", w));
    }
    else if (event_mask & XmKEY_EVENT)
    {
	DEBUGOUT(XdbDebug(__FILE__, w,
			  "Key event sent to gadget %d\n", w));
    }
    else if (event_mask & XmMULTI_ARM_EVENT)
    {
	DEBUGOUT(XdbDebug(__FILE__, w,
			  "Multi arm event sent to gadget %d\n", w));
    }
    else if (event_mask & XmMULTI_ACTIVATE_EVENT)
    {
	DEBUGOUT(XdbDebug(__FILE__, w,
			  "Multi activate event sent to gadget %d\n", w));
    }
    else if (event_mask & XmBDRAG_EVENT)
    {
	DEBUGOUT(XdbDebug(__FILE__, w,
			  "BDrag event sent to gadget %d\n", w));
    }
}

#if 0
static Boolean
visual_change(Widget w,
	      Widget parent,
	      Widget n)
{
    return False;
}
#endif

/*
 * ENTER/LEAVE should only happen when mwm is in pointer-follows-mouse mode.
 * FOCUS_IN/FOCUS_OUT otherwise.
 */
static void
focus_change(Widget w, XmFocusChange change)
{
    XmGadgetClass gc = (XmGadgetClass)XtClass(w);

    switch (change)
    {
    case XmENTER:
	if (!G_HighlightOnEnter(w))
	{
	    break;
	}

	if (gc->gadget_class.border_highlight)
	{
	    (gc->gadget_class.border_highlight) (w);
	}

	break;

    case XmFOCUS_IN:
	G_HaveTraversal(w) = True;

	if (gc->gadget_class.border_highlight)
	{
	    (gc->gadget_class.border_highlight) (w);
	}

	break;

    case XmLEAVE:
	if (!G_HighlightOnEnter(w))
	{
	    break;
	}

	if (gc->gadget_class.border_unhighlight)
	{
	    (gc->gadget_class.border_unhighlight) (w);
	}

	break;

    case XmFOCUS_OUT:
	G_HaveTraversal(w) = False;

	if (gc->gadget_class.border_unhighlight)
	{
	    (gc->gadget_class.border_unhighlight) (w);
	}

	break;
    }
}

static XmNavigability
widget_navigable(Widget w)
{
    if (XtSensitive(w) && G_TraversalOn(w))
    {
	if (G_NavigationType(w) == XmSTICKY_TAB_GROUP ||
	    G_NavigationType(w) == XmEXCLUSIVE_TAB_GROUP ||
	    (G_NavigationType(w) == XmTAB_GROUP && !_XmShellIsExclusive(w)))
	{
	    return XmTAB_NAVIGABLE;
	}

	return XmCONTROL_NAVIGABLE;
    }

    return XmNOT_NAVIGABLE;
}

static void
gadget_border_unhighlight(Widget w)
{

    DEBUGOUT(XdbDebug(__FILE__, w, "gadget_border_unhighlight\n"));

    /* with zero width, we don't need this... */
    if (G_HighlightThickness(w) == 0)
    {
	return;
    }

    if (XmIsManager(XtParent(w)))
    {
	_XmDrawHighlight(XtDisplayOfObject(w), XtWindowOfObject(w),
			 XmParentBackgroundGC(w),
			 XtX(w), XtY(w), XtWidth(w), XtHeight(w),
			 G_HighlightThickness(w), LineSolid);
    }
    else
    {
	_XmClearBorder(XtDisplay(w),
		       XtWindow(w),
		       XtX(w), XtY(w),
		       XtWidth(w), XtHeight(w),
		       G_HighlightThickness(w));
    }

    G_Highlighted(w) = False;
    G_HighlightDrawn(w) = False;
}

static void
gadget_border_highlight(Widget w)
{

    DEBUGOUT(XdbDebug(__FILE__, w, "gadget_border_highlight\n"));

    /* with zero width, we don't need this... */
    if (G_HighlightThickness(w) == 0)
    {
	return;
    }

    _XmDrawHighlight(XtDisplayOfObject(w), XtWindowOfObject(w),
		     XmParentHighlightGC(w),
		     XtX(w), XtY(w), XtWidth(w), XtHeight(w),
		     G_HighlightThickness(w), LineSolid);

    G_Highlighted(w) = True;
    G_HighlightDrawn(w) = True;
}

static void
_XmGetParentHighlightColor(Widget w, int offset, XtArgVal *value)
{
    *value = XmParentHighlightColor(w);
}

static void
_XmGetParentTopShadowColor(Widget w, int offset, XtArgVal *value)
{
    *value = XmParentTopShadowColor(w);
}

static void
_XmGetParentBottomShadowColor(Widget w, int offset, XtArgVal *value)
{
    *value = XmParentBottomShadowColor(w);
}

/*
 * initialize the synthetic resource crap.  The only way I know to get the
 * CachePart stuff for Gadget subclasses is via the baseclass extension
 * (secondary stuff); unfortunately, I don't know how that works yet.
 */
void
_XmBuildGadgetResources(WidgetClass c)
{
    XmGadgetClass super;
    XmGadgetClass gc = (XmGadgetClass)c;
    XmBaseClassExt *bce = NULL;
    XmExtObjectClass subpclass, esuper;


    _XmInitializeSyntheticResources(gc->gadget_class.syn_resources,
				    gc->gadget_class.num_syn_resources);

    /*
     * if we're the Gadget, our super doesn't have synthetic resources
     * nor do we have an extension, so we can bail here.
     */
    if (c == xmGadgetClass)
    {
	return;
    }

    super = ((XmGadgetClass)c->core_class.superclass);
    _XmBuildResources(&gc->gadget_class.syn_resources,
		      &gc->gadget_class.num_syn_resources,
		      super->gadget_class.syn_resources,
		      super->gadget_class.num_syn_resources);

    /*
     * we can get the subpart stuff from the baseclass extension, now that
     * it's there.
     */
    bce = (XmBaseClassExt *)_XmGetBaseClassExtPtr(c, XmQmotif);

    if (!*bce || !(subpclass = (XmExtClassRec *)(*bce)->secondaryObjectClass))
    {
	return;
    }

    _XmInitializeSyntheticResources(subpclass->ext_class.syn_resources,
				    subpclass->ext_class.num_syn_resources);

    /* core dump avoidance overkill */
    esuper = (XmExtObjectClass)subpclass->object_class.superclass;
    if (subpclass == &xmExtClassRec || esuper == &xmExtClassRec)
    {
	return;
    }

    _XmBuildResources(&subpclass->ext_class.syn_resources,
		      &subpclass->ext_class.num_syn_resources,
		      esuper->ext_class.syn_resources,
		      esuper->ext_class.num_syn_resources);
}
