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

	ScrolledList.c

	This file contains the C code for the ScrolledList widget, which
	acts as a vertically scrolling list of text, from which, items can
	be selected.

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

/*
 * Author:
 * 	Brian Totty
 * 	Department of Computer Science
 * 	University Of Illinois at Urbana-Champaign
 *	1304 West Springfield Avenue
 * 	Urbana, IL 61801
 *
 * 	totty@cs.uiuc.edu
 *
 * Small fix by Ettore Perazzoli <ettore@comm2000.it>, search for [EP].
 */

#include "vice.h"

#include <stdio.h>
#include <X11/Xos.h>
#include <X11/Xlib.h>
#include <X11/StringDefs.h>
#include <X11/IntrinsicP.h>
#include <X11/Intrinsic.h>
#include <X11/CompositeP.h>
#include <X11/Composite.h>
#include <X11/Xaw/ViewportP.h>
#include <X11/Xaw/Viewport.h>
#include "MultiListP.h"
#include "MultiList.h"
#include "ScrListP.h"
#include "ScrList.h"

/*---------------------------------------------------------------------------*

                       D E C L A R A T I O N S

 *---------------------------------------------------------------------------*/

#define SUPERCLASS (&compositeClassRec)

#define max(a, b)   ((a) > (b) ? (a) : (b))
#define min(a, b)   ((a) < (b) ? (a) : (b))
#define abs(a)      ((a) < 0 ? -(a) : (a))
#define XtStrlen(s) ((s) ? strlen(s) : 0)

#define offset(field)  XtOffset(XfwfScrolledListWidget, scrolledList.field)
#define coffset(field) XtOffset(Widget, core.field)

#define MyData(w)   ((w)->scrolledList)
#define CoreData(w) ((w)->core)

#if (!NeedFunctionPrototypes)

static void MultiListCallbackHandler();
static void ClassInitialize();
static void Initialize();
static void Realize();
static void Destroy();
static void Resize();
static Boolean SetValues();
static XtGeometryResult GeometryManager();
static XtGeometryResult PreferredGeometry();
static void ReCalcChildren();

#else

static void MultiListCallbackHandler(Widget w, Widget self, XfwfMultiListReturnStruct *call_data);
static void ClassInitialize(void);
static void Initialize(Widget request, Widget new);
static void Realize(Widget gw, XtValueMask *valueMask, XSetWindowAttributes *attrs);
static void Destroy(Widget gw);
static void Resize(Widget gw);
static Boolean SetValues(Widget gcurrent, Widget grequest, Widget gnew);
static XtGeometryResult GeometryManager(Widget w, XtWidgetGeometry *request, XtWidgetGeometry *reply);
static XtGeometryResult PreferredGeometry(XfwfScrolledListWidget slw, XtWidgetGeometry *parent_idea, XtWidgetGeometry *our_idea);
static void ReCalcChildren(XfwfScrolledListWidget w);

#endif

/*---------------------------------------------------------------------------*

              R E S O U R C E    I N I T I A L I Z A T I O N

 *---------------------------------------------------------------------------*/

#ifdef MINIX_SUPPORT
static XtResource resources[] =
{
    { XtNwidth, XtCWidth, XtRDimension, sizeof(Dimension), 32, XtRString, "200" },
    { XtNheight, XtCHeight, XtRDimension, sizeof(Dimension), 34, XtRString, "500" },
    { XtNbackground, XtCBackground, XtRPixel, sizeof(Pixel), 104, XtRString, "white" },
    { XtNlist, XtCList, XtRPointer, sizeof(char **), 140, XtRPointer, NULL },
    { XtNnumberStrings, XtCNumberStrings, XtRInt, sizeof(int), 144, XtRInt, 0 },
    { XtNsensitiveArray, XtCList, XtRPointer, sizeof(Boolean *), 148, XtRPointer, NULL },
    { XtNcallback, XtCCallback, XtRCallback, sizeof(caddr_t), 152, XtRCallback, NULL }
};
#else
static XtResource resources[] =
{
    { XtNwidth, XtCWidth, XtRDimension, sizeof(Dimension), coffset(width), XtRString, "200" },
    { XtNheight, XtCHeight, XtRDimension, sizeof(Dimension), coffset(height), XtRString, "500" },
    { XtNbackground, XtCBackground, XtRPixel, sizeof(Pixel), coffset(background_pixel), XtRString, "white" },
    { XtNlist, XtCList, XtRPointer, sizeof(char **), offset(item_array), XtRPointer, NULL },
    { XtNnumberStrings, XtCNumberStrings, XtRInt, sizeof(int), offset(item_count), XtRInt, 0 },
    { XtNsensitiveArray, XtCList, XtRPointer, sizeof(Boolean *), offset(sensitive_array), XtRPointer, NULL },
    { XtNcallback, XtCCallback, XtRCallback, sizeof(caddr_t), offset(callback), XtRCallback, NULL }
};
#endif

#undef offset
#undef coffset

/*---------------------------------------------------------------------------*

                    C L A S S    A L L O C A T I O N

 *---------------------------------------------------------------------------*/

XfwfScrolledListClassRec xfwfScrolledListClassRec = {
{
/* superclass            */    (WidgetClass)SUPERCLASS,
/* class_name            */    "XfwfScrolledList",
/* widget_size           */    sizeof(XfwfScrolledListRec),
/* class_initialize      */    (XtProc)ClassInitialize,
/* class_part_initialize */    NULL,
/* class_inited          */    FALSE,
/* initialize            */    (XtInitProc)Initialize,
/* initialize_hook       */    NULL,
/* realize               */    (XtRealizeProc)Realize,
/* actions               */    NULL,
/* num_actions           */    0,
/* resources             */    resources,
/* resource_count        */    XtNumber(resources),
/* xrm_class             */    NULLQUARK,
/* compress_motion       */    TRUE,
/* compress_exposure     */    TRUE,
/* compress_enterleave   */    TRUE,
/* visible_interest      */    FALSE,
/* destroy               */    (XtWidgetProc)Destroy,
/* resize                */    (XtWidgetProc)Resize,
/* expose                */    NULL,
/* set_values            */    (XtSetValuesFunc)SetValues,
/* set_values_hook       */    NULL,
/* set_values_almost     */    XtInheritSetValuesAlmost,
/* get_values_hook       */    NULL,
/* accept_focus          */    NULL,
/* version               */    XtVersion,
/* callback_private      */    NULL,
/* tm_table              */    NULL,
/* query_geometry        */    (XtGeometryHandler)PreferredGeometry,
/* display_accelerator   */    XtInheritDisplayAccelerator,
/* extension             */    NULL
}, /* Core Part */
{
/* geometry_manager      */    GeometryManager,
/* change_managed        */    XtInheritChangeManaged,
/* insert_child          */    XtInheritInsertChild,
/* delete_child          */    XtInheritDeleteChild,
/* extension             */    NULL
}, /* Composite Part */
{
/* no extra class data  */    0
}  /* XfwfScrolledList Part */
};

WidgetClass xfwfScrolledListWidgetClass = (WidgetClass)&xfwfScrolledListClassRec;

/*---------------------------------------------------------------------------*

                    E X P O R T E D    M E T H O D S

 *---------------------------------------------------------------------------*/

/*---------------------------------------------------------------*

	MultiListCallbackHandler(w,client_data,call_data)

	This routine gets called when the user clicks on a
	MultiList item.  It in turn calls any callbacks in the
	ScrolledList widget.

 *---------------------------------------------------------------*/

static void MultiListCallbackHandler(Widget w, Widget self, XfwfMultiListReturnStruct *call_data)
{
    Boolean highlighted,sensitive;
    XfwfScrolledListReturnStruct ret_value;

    if (call_data->num_selected) {
        ret_value.index = call_data->selected_items[0];
        XfwfMultiListGetItemInfo((XfwfMultiListWidget)w, ret_value.index, &(ret_value.string), &highlighted, &sensitive);
    } else {
        ret_value.index = -1;
        ret_value.string = NULL;
    }
    XtCallCallbacks(self, XtNcallback, (caddr_t)(&ret_value));
}

/*---------------------------------------------------------------*

	ClassInitialize()

	This procedure is called by the X toolkit to initialize
	the class.  The hook to this routine is in the
	class_initialize part of the core part of the class.

 *---------------------------------------------------------------*/

static void ClassInitialize(void)
{
} /* End ClassInitialize */


/*---------------------------------------------------------------*

	Initialize()

	This procedure is called by the X toolkit to initialize
	the widget instance.  The hook to this routine is in the
	initialize part of the core part of the class.

 *---------------------------------------------------------------*/

/* ARGSUSED */
static void Initialize(Widget request, Widget new)
{
    XfwfScrolledListWidget w;
    Arg args[10];

    w = (XfwfScrolledListWidget)new;

    if (CoreData(new).width <= 0) {
        CoreData(new).width = 200;
    }
    if (CoreData(new).height <= 0) {
        CoreData(new).height = 300;
    }

    XtSetArg(args[0], XtNuseRight, True);
    XtSetArg(args[1], XtNwidth, CoreData(new).width);
    XtSetArg(args[2], XtNheight, CoreData(new).height);
    XtSetArg(args[3], XtNallowVert, True);
    XtSetArg(args[4], XtNforceBars, True);
    MyData(w).viewport = XtCreateManagedWidget("viewport", viewportWidgetClass, (Widget)w, args, 5);
    XtSetArg(args[0], XtNlist, MyData(w).item_array);
    XtSetArg(args[1], XtNnumberStrings, MyData(w).item_count);
    XtSetArg(args[2], XtNsensitiveArray, MyData(w).sensitive_array);
    XtSetArg(args[3], XtNdefaultColumns, 1);
    XtSetArg(args[4], XtNborderWidth, 0);
    XtSetArg(args[5], XtNx, 1);
    XtSetArg(args[6], XtNy, 1);
    XtSetArg(args[7], XtNmaxSelectable, 1);
    MyData(w).list = XtCreateManagedWidget("multilist", xfwfMultiListWidgetClass, MyData(w).viewport, args, 8);

    XtAddCallback(MyData(w).list, XtNcallback, (XtCallbackProc)MultiListCallbackHandler, (caddr_t)w);
} /* End Initialize */

/*---------------------------------------------------------------*

	Realize()

	This function is called to realize a ScrolledList widget.

 *---------------------------------------------------------------*/

static void Realize(Widget gw, XtValueMask *valueMask, XSetWindowAttributes *attrs)
{
    XfwfScrolledListWidget w;

    w = (XfwfScrolledListWidget)gw;
    XtCreateWindow(gw, InputOutput, (Visual *)CopyFromParent, *valueMask, attrs);
    XtRealizeWidget(MyData(w).viewport);

    Resize(gw);
} /* End Realize */

/*---------------------------------------------------------------*

	Destroy()

	This function is called to destroy a scrolledList widget.

 *---------------------------------------------------------------*/

static void Destroy(Widget gw)
{
    /* Removed by [EP] on 98-06-20.  */
#if 0
    XfwfScrolledListWidget w;

    w = (XfwfScrolledListWidget)gw;
    XtDestroyWidget(MyData(w).viewport);
    XtDestroyWidget(MyData(w).list);
#endif
} /* End Destroy */

/*---------------------------------------------------------------*

	Resize()

	This function is called to resize a scrolledList widget.

 *---------------------------------------------------------------*/

static void Resize(Widget gw)
{
    XfwfScrolledListWidget w;

    w = (XfwfScrolledListWidget)gw;
    ReCalcChildren(w);
} /* End Resize */

/*---------------------------------------------------------------*

	Boolean SetValues(wcurrent,wrequest,wnew)

	This isn't inplemented yet, meaning you can't change
	resources dynamically.

 *---------------------------------------------------------------*/

/* ARGSUSED */
static Boolean SetValues(Widget wcurrent, Widget wrequest, Widget wnew)
{
    Boolean redraw,recalc;
    XfwfScrolledListWidget csl, rsl, nsl;

    csl = (XfwfScrolledListWidget)wcurrent;
    rsl = (XfwfScrolledListWidget)wrequest;
    nsl = (XfwfScrolledListWidget)wnew;

    redraw = False;
    recalc = False;

    if ((MyData(csl).item_array != MyData(nsl).item_array) || (MyData(csl).item_count != MyData(nsl).item_count) || (MyData(csl).sensitive_array != MyData(nsl).sensitive_array)) {
        XfwfScrolledListSetList((Widget)csl, MyData(nsl).item_array, MyData(nsl).item_count, True, MyData(nsl).sensitive_array);
        redraw = recalc = True;
    }

    return redraw;
} /* End SetValues */

/*---------------------------------------------------------------*

	GeometryManager(w,request,reply)

	This routine acts as the geometry_manager method for the
	ScrolledList widget.  It is called when a child wants to
	resize/reposition itself.

	Currently, we allow all requests.

 *---------------------------------------------------------------*/

/* ARGSUSED */
static XtGeometryResult GeometryManager(Widget w, XtWidgetGeometry *request, XtWidgetGeometry *reply)
{
    return XtGeometryYes;
} /* End GeometryManager */

/*---------------------------------------------------------------------------*

	PreferredGeometry(slw,parent_idea,our_idea)

	This routine is called by the parent to tell us about the
	parent's idea of our width and/or height.  We then suggest
	our preference through <our_idea> and return the information
	to the parent.

 *---------------------------------------------------------------------------*/

static XtGeometryResult PreferredGeometry(XfwfScrolledListWidget slw, XtWidgetGeometry *parent_idea, XtWidgetGeometry *our_idea)
{
    Arg multilist_args[2], slist_args[2];
    Dimension col_width, row_height, num_rows, current_width, current_height;

    if (!XtIsRealized((Widget)slw)) {
        return XtGeometryYes;
    }

    XtSetArg(multilist_args[0], XtNrowHeight, &row_height);
    XtSetArg(multilist_args[1], XtNcolumnWidth, &col_width);
    XtSetArg(slist_args[0], XtNwidth, &current_width);
    XtSetArg(slist_args[1], XtNheight, &current_height);
    XtGetValues(MyData(slw).list, multilist_args, 2);
    XtGetValues((Widget)slw, slist_args, 2);

    row_height = max(1, row_height);
    num_rows = max(1, (parent_idea->height / row_height));

    our_idea->request_mode = (CWWidth | CWHeight);
    our_idea->width = col_width;
    our_idea->height = num_rows * row_height;

    if ((our_idea->width == current_width) && (our_idea->height == current_height)) {
        return XtGeometryNo;		/* Prefer To Stay As Is */
    }

    if ((our_idea->width == parent_idea->width) && (our_idea->height == parent_idea->height)) {
        return XtGeometryYes;		/* Proposed Change Is Fine */
    }

    return XtGeometryAlmost;
} /* End PreferredGeometry */

/*---------------------------------------------------------------------------*

                     L O C A L    R O U T I N E S

 *---------------------------------------------------------------------------*/

/*---------------------------------------------------------------*

	ReCalcChildren(w)

	This routine takes a ScrolledList widget <w> and updates
	the widths and positions of the child widgets.

 *---------------------------------------------------------------*/

static void ReCalcChildren(XfwfScrolledListWidget w)
{
    int wid_h, wid_w;

    wid_w = CoreData(w).width;
    wid_h = CoreData(w).height;

    XtResizeWidget(MyData(w).viewport, wid_w, wid_h, 1);
} /* End ReCalcChildren */

/*===========================================================================*

                    E X P O R T E D    F U N C T I O N S

 *===========================================================================*/

/*---------------------------------------------------------------------------*

	XfwfScrolledListSetList(w,newlist,items,resize,sensitive_array)

	This routine tries to set the string array to the new array
	<newlist>.  <items> is the count of strings.  If <items> is 0
	and the array is NULL terminated, the number of strings will be
	automatically counted.	The <resize> flag indicates if we want
	the widget to try to resize itself to hold the new array.  The
	<sensitive_array> parameter (if non-NULL) contains an array of
	Booleans representing the sensitivity of the list items.

 *---------------------------------------------------------------------------*/

void XfwfScrolledListSetList(Widget w, char **newlist, int items, int resize, Boolean *sensitive_array)
{
    XfwfScrolledListWidget sw;

    sw = (XfwfScrolledListWidget)w;
    XfwfMultiListSetNewData((XfwfMultiListWidget)(MyData(sw).list), newlist, items, 0, resize, sensitive_array);
    MyData(sw).item_array = newlist;
    MyData(sw).item_count = items;
    MyData(sw).sensitive_array = sensitive_array;
} /* End XfwfScrolledListSetList */

/*---------------------------------------------------------------------------*

	XfwfScrolledListUnhighlightAll(w)

	This routine unhighlights all highlighted items in the
	ScrolledList widget <w>.

 *---------------------------------------------------------------------------*/

void XfwfScrolledListUnhighlightAll(Widget w)
{
    XfwfScrolledListWidget sw;

    sw = (XfwfScrolledListWidget)w;
    XfwfMultiListUnhighlightAll((XfwfMultiListWidget)(MyData(sw).list));
} /* End XfwfScrolledListUnhighlightAll */

/*---------------------------------------------------------------------------*

	XfwfScrolledListHighlightItem(w,item_index)

	This routine highlights the item with index number <item_index>.

 *---------------------------------------------------------------------------*/

void XfwfScrolledListHighlightItem(Widget w, int item_index)
{
    XfwfScrolledListWidget sw;

    sw = (XfwfScrolledListWidget)w;
    XfwfMultiListHighlightItem((XfwfMultiListWidget)(MyData(sw).list), item_index);
} /* End XfwfScrolledListHighlightItem */

/*---------------------------------------------------------------------------*

	XfwfScrolledListGetHighlighted(w)

	This routine returns the current highlighted item.

 *---------------------------------------------------------------------------*/

XfwfScrolledListReturnStruct *XfwfScrolledListGetHighlighted(XfwfScrolledListWidget sw)
{
    XfwfMultiListReturnStruct *pl_ret;
    static XfwfScrolledListReturnStruct sl_ret; /* Added static [BB] */

    sl_ret.multilist = MyData(sw).list;

    pl_ret = XfwfMultiListGetHighlighted((XfwfMultiListWidget)(MyData(sw).list));
    if (pl_ret->num_selected != 0) {
        sl_ret.string = pl_ret->string;
        sl_ret.index = pl_ret->item;
        sl_ret.highlighted = True;
    } else {
        sl_ret.string = NULL;
        sl_ret.index = -1;
        sl_ret.highlighted = False;
    }
    return &sl_ret;
} /* End XfwfScrolledListGetHighlighted */

/*---------------------------------------------------------------------------*

	XfwfScrolledListIsHighlighted(w,item_index)

	This routine returns a boolean that indicates if the item
	with index <item_index> was highlighted or not.

 *---------------------------------------------------------------------------*/

Boolean XfwfScrolledListIsHighlighted(Widget w, int item_index)
{
    XfwfScrolledListWidget sw;

    sw = (XfwfScrolledListWidget)w;
    return XfwfMultiListIsHighlighted((XfwfMultiListWidget)(MyData(sw).list), item_index);
} /* End XfwfScrolledListIsHighlighted */

/*---------------------------------------------------------------------------*

	XfwfScrolledListGetItem(w,item_index,str_ptr,high_ptr,sens_ptr)

	This routine returns information about a numbered item including
	the name of the item, whether it is highlighted, and its
	sensitivity.  If the item index number does not correspond to an
	item, False is returned, else True is returned.

 *---------------------------------------------------------------------------*/

Boolean XfwfScrolledListGetItem(Widget w, int item_index, String *str_ptr, Boolean *high_ptr, Boolean *sens_ptr)
{
    XfwfScrolledListWidget sw;

    sw = (XfwfScrolledListWidget)w;
    return XfwfMultiListGetItemInfo((XfwfMultiListWidget)(MyData(sw).list), item_index, str_ptr, high_ptr, sens_ptr);
} /* End XfwfScrolledListGetItem */
