/*
 * Motif
 *
 * Copyright (c) 1987-2012, The Open Group. All rights reserved.
 *
 * These libraries and programs are free software; you can
 * redistribute them and/or modify them under the terms of the GNU
 * Lesser General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * These libraries and programs are distributed in the hope that
 * they will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 * PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with these librararies and programs; if not, write
 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA 02110-1301 USA
 * 
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include "XmI.h"
#include <Xm/XmP.h>
#include <Xm/DrawP.h>
#include <Xm/MultiListP.h>
#ifdef VMS
#include <bitmaps/gray.xbm>
#else
#include <X11/bitmaps/gray>
#endif

#include <Xm/ScrollBar.h>
#include <Xm/TraitP.h>
#include <Xm/TransferT.h>
#include "TransferI.h"		/* for _XmConvertComplete() */
#include <Xm/ExtP.h>
#include <Xm/XmP.h>
#include "RepTypeI.h"

/************************************************************
*	TYPEDEFS AND DEFINES
*************************************************************/

#define SUPERCLASS ((WidgetClass) &xmPrimitiveClassRec)

#define NO_SELECTION -1		/* There is currently no anchor. */
#define IN_COLUMN_HEADER -2	/* The location is in the header. */
#define ENTIRE_ROW -3		/* perform operation on entire row. */

#define EXTEND	 	(1 << 0)
#define TOGGLE	 	(1 << 1)
#define OFF_TOP         (1 << 2)
#define OFF_BOTTOM      (1 << 3)
#define OFF_LEFT        (1 << 4) /* UNUSED BUT RESERVED */
#define OFF_RIGHT       (1 << 5) /* UNUSED BUT RESERVED */
#define OUTSIDE_WIDGET (OFF_TOP | OFF_BOTTOM | OFF_LEFT | OFF_RIGHT)

#define DELAY_TIME(w)  (XtGetMultiClickTime(XtDisplay(w))/3)

#define LINE_HEIGHT 2		/* Height of the separator line */
#define HORIZONTAL_SPACE 8	/* horizontal Space between Text and Text. */
#define VERTICAL_SPACE   2	/* Vertical Space between Text and Text. */

#define NoTimeout ((XtIntervalId) 0)

/*
 * DANGER:  XmeRenderTableGetDefaultFont is an undocumented 
 *          internal MOTIF(tm) function.
 */
    
#define GetDefaultFont XmeRenderTableGetDefaultFont

/************************************************************
*	MACROS
*************************************************************/

#define streq(a, b) (((a) != NULL) && ((b) != NULL) && (strcmp((a), (b)) == 0))
#define IsValidPixmap(p) (((p) != None) && ((p) != XmUNSPECIFIED_PIXMAP))

/************************************************************
*	GLOBAL DECLARATIONS
*************************************************************/

extern Boolean XmeRenderTableGetDefaultFont (XmFontList, XFontStruct **);

static Widget global_current_widget;		/* static global to hold
						   widget id for qsort. */

/************************************************************
*	STATIC FUNCTION DECLARATIONS
*************************************************************/

/******************************
 * Exported Widget Functions
 ******************************/

Boolean XmI18ListDoSearch(Widget w, String str, Boolean reset);
XmMultiListRowInfo *XmI18ListFindRow(Widget w, String str, int *found_column,
			      Boolean reset, Boolean do_visual);

static void ClassInitialize();
static void ClassPartInitialize(WidgetClass w_class);
static void Initialize(Widget, Widget, ArgList, Cardinal *);
static void Realize(Widget, Mask *, XSetWindowAttributes *);
static void Resize(Widget), Redisplay(Widget, XEvent *, Region);
static void Destroy(Widget);
static Boolean SetValues(Widget, Widget, Widget, ArgList, Cardinal *);

/***********************************
 * Internal routines.
 ***********************************/
static void AdjustFirstRowAndCol( XmI18ListWidget );
static void AdjustFirstCol(Widget);
static void CalcLocations(Widget);
static void CalcColumnInfo(Widget, Boolean);
static void SetVisibleSize(Widget, Boolean);
static void DisplayList(Widget, short, short, Boolean);
static short GetListWidth(Widget);
static void HScroll(Widget, short), VScroll(Widget, short);
static short CvtPixelValToColNum(Widget, short);
static short CvtColNumToPixelVal(Widget, short);
static void CvtRowColumnToPosition(Widget, short, short, short *, short *);
static void CvtPositionToRowColumn(Widget, short, short, short *, short *);
static void ToggleRow(Widget, short), InvertArea(Widget, short, short);
static void SelectHeader(Widget, short), DrawSeparator(Widget);
static void CreateGCs(Widget), RedrawList(Widget), GetPixmapInfo(Widget);
static void DestroyGCs(Widget), Notify(Widget, Boolean);
static void ResizeSliders(Widget), SortList(Widget, Boolean);
static void SingleClick(XmI18ListWidget);
static void MoveListTimeout(XtPointer, XtIntervalId *);
static Boolean MakePositionVisible(Widget, short, short, short, int);
static void ExtendedSelect(Widget, short);

static int IsRowVisible(Widget, short);
static int QSortTest(const void *, const void *);

static void UnselectRows(Widget, short);

static Boolean Search(XmI18ListWidget ilist, XmString xms,
       int start_row, int start_column, int *found_row, int *found_column);
static int FirstSelectedRow(Widget w);
static void MakeCellVisible(Widget w, int row, int col);
static void CopyColumnTitles(XmI18ListWidget);
static void FreeColumnTitles(XmI18ListWidget);

static void SelectRow(XmI18ListWidget, int, Boolean, Boolean);
static void SelectItems(XmI18ListWidget, XmString,
			int, Boolean, Boolean);
static void CheckSetRenderTable(Widget wid, int offset, XrmValue *value); 
static void ListConvert(Widget, XtPointer, XmConvertCallbackStruct*);
static void ListPreDestProc(Widget, XtPointer, XmDestinationCallbackStruct*);
static void CopyToClipboard(Widget, XEvent*, String*, Cardinal*);
static void DragDropFinished(Widget, XtPointer, XtPointer);
static XmString GetConcatenatedRow(Widget, int);
static void ProcessDrag(Widget, XEvent*, String*, Cardinal*);

/***********************************
 * Actions and Callback function defs.
 ***********************************/

static void HScrollCallback(Widget, XtPointer, XtPointer);
static void VScrollCallback(Widget, XtPointer, XtPointer);
static void HSlideLeftArrowCallback(Widget, XtPointer, XtPointer);
static void HSlideRightArrowCallback(Widget, XtPointer, XtPointer);

static void ButtonDownAction(Widget, XEvent *, String *, Cardinal *);
static void ButtonUpOrLeaveAction(Widget, XEvent *, String *, Cardinal *);
static void MotionAction(Widget, XEvent *, String *, Cardinal *);

/************************************************************
*	STATIC DECLARATIONS
*************************************************************/

/*
 * The Translation table routines are getting in the way here.
 * Since the implicit removal of event disallows using both:
 *
 * <Btn1Up> (2)  and   button1 <Motion> 
 *
 * in the same translation table I have to do all the work myself.
 */

static char defaultTranslations[] =
    "~Ctrl ~Shift <Btn1Down>:		ButtonDown()\n\
     Ctrl ~Shift  <Btn1Down>:		ButtonDown(Toggle)\n\
     ~Ctrl Shift  <Btn1Down>:		ButtonDown(Extend)\n\
     Button1 <Motion>:		Motion()\n\
     <Btn1Up>:			ButtonUpOrLeave()\n\
     <Btn2Down>:		ProcessDrag()\n\
     :c <Key>osfInsert:		CopyToClipboard()\n\
     :<Key>osfCopy:		CopyToClipboard()";

static XtActionsRec actionsList[] =
{
  {"ButtonDown",	ButtonDownAction},
  {"ButtonUpOrLeave",	ButtonUpOrLeaveAction},
  {"Motion",      	MotionAction},
  {"ProcessDrag",       ProcessDrag},
  {"CopyToClipboard",   CopyToClipboard},
};

/* ARGSUSED */
static void SetEntryBackground(Widget w, int offset, XrmValue *value)
{
    XmI18ListWidget ilist = (XmI18ListWidget) w;
    /* not really used */
    value->addr = (XtPointer) &ilist->core.background_pixel;

    /*
     * If this function is called, then the value of XmNentryBackground wasn't
     * set through a resource file specification, so override the current
     * value of True. If it was set, the value of True stays set in the widget
     * and the value of the pixel is set through the usual XtRPixel converter.
     */
    XmI18List_entry_background_use(ilist) = False;
}

static XtResource resources[] =
{
  {
    XmNnumColumns, XmCNumColumns, XmRShort,
    sizeof(short), XtOffsetOf(XmI18ListRec, ilist.num_columns),
    XmRImmediate, (XtPointer) 0
  },

  {
    XmNnumRows, XmCNumRows, XmRShort,
    sizeof(short), XtOffsetOf(XmI18ListRec, ilist.num_rows),
    XmRImmediate, (XtPointer) 0
  },

  {
    XmNselectedColumn, XmCSelectedColumn, XmRShort,
    sizeof(short), XtOffsetOf(XmI18ListRec, ilist.selected_header),
    XmRImmediate, (XtPointer) 0
  },

  {
    XmNcolumnTitles, XmCColumnTitles, XmRXmStringTable,
    sizeof(XmString *), XtOffsetOf(XmI18ListRec, ilist.column_titles),
    XmRImmediate, (XtPointer) NULL
  },

  {
    XmNentryData, XmCEntryData, XmRPointer,
    sizeof(XtPointer), XtOffsetOf(XmI18ListRec, ilist.row_data),
    XmRImmediate, (XtPointer) NULL
  },

  {
    XmNfirstColumnPixmaps, XmCFirstColumnPixmaps, XmRBoolean,
    sizeof(Boolean), XtOffsetOf(XmI18ListRec, ilist.first_col_pixmaps),
    XmRImmediate, (XtPointer) False
  },

  {
    "pri.vate", "Pri.vate", XmRBoolean,
    sizeof(Boolean), XtOffsetOf(XmI18ListRec, ilist.check_set_render_table),
    XmRImmediate, (XtPointer) False
  },

  {
    XmNfontList, XmCFontList, XmRFontList,
    sizeof(XmFontList), XtOffsetOf(XmI18ListRec, ilist.font_list),
    XmRCallProc, (XtPointer)CheckSetRenderTable
  },

  {
    XmNrenderTable, XmCRenderTable, XmRRenderTable,
    sizeof(XmRenderTable), XtOffsetOf(XmI18ListRec, ilist.font_list),
    XmRCallProc, (XtPointer)CheckSetRenderTable
  },

  {
    XmNverticalScrollBar, XmCScrollBar, XmRWidget,
    sizeof(Widget), XtOffsetOf(XmI18ListRec, ilist.v_bar),
    XmRImmediate, (XtPointer) NULL
  },

  {
    XmNhorizontalScrollBar, XmCScrollBar, XmRWidget,
    sizeof(Widget), XtOffsetOf(XmI18ListRec, ilist.h_bar),
    XmRImmediate, (XtPointer) NULL
  },

  {
    XmNfirstRow, XmCFirstLocation, XmRShort,
    sizeof(short), XtOffsetOf(XmI18ListRec, ilist.first_row),
    XmRImmediate, (XtPointer) 0
  },

  {
    XmNfirstColumn, XmCFirstLocation, XmRShort,
    sizeof(short), XtOffsetOf(XmI18ListRec, ilist.first_col),
    XmRImmediate, (XtPointer) 0
  },

  {
    XmNdoubleClickCallback, XmCCallback, XmRCallback,
    sizeof(XtCallbackList), XtOffsetOf(XmI18ListRec, ilist.double_click),
    XmRImmediate, (XtPointer) NULL
  },

  {
    XmNsortFunctions, XmCFunction, XmRFunction,
    sizeof(Xm18SortFunction **), XtOffsetOf(XmI18ListRec, ilist.sort_functions),
    XmRImmediate, (XtPointer) NULL
  },

  {
    XmNselectionPolicy, XmCSelectionPolicy, XmRSelectionPolicy,
    sizeof(unsigned char), XtOffsetOf(XmI18ListRec, ilist.selection_policy), 
    XmRImmediate, (XtPointer) XmEXTENDED_SELECT
  },
  
  {
    XmNsingleSelectionCallback, XmCCallback, XmRCallback,
    sizeof(XtCallbackList), XtOffsetOf(XmI18ListRec, ilist.single_select),
    XmRImmediate, (XtPointer) NULL
  },
  
  {
      XmNalignment, XmCAlignment, XmRAlignment,
      sizeof(unsigned char), XtOffsetOf(XmI18ListRec, ilist.alignment),
      XmRImmediate, (XtPointer) XmALIGNMENT_BEGINNING
  },

  {
    XmNstringDirection, XmCStringDirection, XmRStringDirection,
    sizeof(unsigned char), XtOffsetOf(XmI18ListRec, ilist.string_direction),
    XmRImmediate, (XtPointer) XmDEFAULT_DIRECTION
  },

  {
    XmNvisibleItemCount, XmCVisibleItemCount, XmRInt,
    sizeof(int), XtOffsetOf(XmI18ListRec, ilist.visible_rows),
    XmRImmediate, (XtPointer) XmMultiList_DEFAULT_VISIBLE_COUNT
  },

  {
    XmNnewVisualStyle, XmCNewVisualStyle, XmRBoolean,
    sizeof(Boolean), XtOffsetOf(XmI18ListRec, ilist.new_visual_style),
    XmRImmediate, (XtPointer) True
  },

  /* process this first; set the value to True */
  {
    "is.entry.background.set", "I.Really.Wonder", XtRBoolean,
    sizeof(Boolean), XtOffsetOf(XmI18ListRec, ilist.entry_background_use),
    XmRImmediate, (XtPointer) True
  },
  /* now see if this resource is set in the defaults file */
  /* XmNentryBackground currently UNDOCUMENTED */
  {
    XmNentryBackground, XmCBackground, XtRPixel,
    sizeof(Pixel), XtOffsetOf(XmI18ListRec, ilist.entry_background_pixel),
    XmRCallProc, (XtPointer) SetEntryBackground
  }
};

XmI18ListClassRec xmI18ListClassRec = {
  { /* core fields */
    /* superclass		*/	SUPERCLASS,
    /* class_name		*/	XM_I18LIST_CLASS_NAME,
    /* widget_size		*/	sizeof(XmI18ListRec),
    /* class_initialize		*/	ClassInitialize,
    /* class_part_initialize	*/	ClassPartInitialize,
    /* class_inited		*/	FALSE,
    /* initialize		*/	Initialize,
    /* initialize_hook		*/	NULL,
    /* realize			*/	Realize,
    /* actions			*/	actionsList,
    /* num_actions		*/	XtNumber(actionsList),
    /* resources		*/	(XtResource*)resources,
    /* num_resources		*/	XtNumber(resources),
    /* xrm_class		*/	NULLQUARK,
    /* compress_motion		*/	TRUE,
    /* compress_exposure	*/	XtExposeCompressMultiple,
    /* compress_enterleave	*/	TRUE,
    /* visible_interest		*/	FALSE,
    /* destroy			*/	Destroy,
    /* resize			*/	Resize,
    /* expose			*/	Redisplay,
    /* set_values		*/	SetValues,
    /* set_values_hook		*/	NULL,
    /* set_values_almost	*/	XtInheritSetValuesAlmost,
    /* get_values_hook		*/	NULL,
    /* accept_focus		*/	NULL,
    /* version			*/	XtVersion,
    /* callback_private		*/	NULL,
    /* tm_table			*/	defaultTranslations,
    /* query_geometry		*/	XtInheritQueryGeometry,
    /* display_accelerator	*/	XtInheritDisplayAccelerator,
    /* extension		*/	NULL
  },
  { /* Xmprimitive fields */
      NULL,			/* border_highlight   */
      NULL,			/* border_unhighlight */
      XtInheritTranslations,    /* translations       */
      NULL,                     /* arm_and_activate   */
      NULL,   	    		/* syn resources      */
      (Cardinal) 0,		/* num syn_resources  */
      NULL                      /* extension          */
  },
  { /* ICS List fields */
      NULL                      /* extension          */
  }
};

WidgetClass xmI18ListWidgetClass = (WidgetClass)&xmI18ListClassRec;

/* Transfer trait record */

static XmConst XmTransferTraitRec ListTransfer = {
  0, 						/* version */
  (XmConvertCallbackProc) ListConvert,		/* convertProc */
  NULL,						/* destinationProc */
  (XmDestinationCallbackProc) ListPreDestProc,	/* destinationPreHookProc */
};

/*
 * Comment on code structure.
 *
 * This code is separated into two separate coordinate spaces.
 *
 * The first is the default space defined by pixels and the X Server.
 *
 * The second is "input" space that contains all the information 
 * to display in the actual list.  This space runs from 0 -> (num_rows - 1)
 * in the y direction and 0 -> (num_cols - 1) in the x direction.  The
 * input from the application is sent in this space.
 *
 * Chris D. Peterson - ICS, Inc.
 * 10/16/90
 */

/************************************************************
*	STATIC (EXPORTED) CODE
*************************************************************/

/*      Function Name: ClassInitialize
 *      Description:   Inits class-specific data (offsets)
 *      Arguments:     none
 *      Returns:       nothing
 */
static void
ClassInitialize()
{
  /* do nothing */
}

/*
 * ClassPartInitialize sets up the fast subclassing for the widget.
 */
static void 
#ifdef _NO_PROTO
ClassPartInitialize(w_class)
        WidgetClass w_class ;
#else
ClassPartInitialize(WidgetClass w_class)
#endif /* _NO_PROTO */
{
    _XmFastSubclassInit (w_class, XmI18LIST_BIT);

    /* Install transfer trait */
    XmeTraitSet((XtPointer)w_class, XmQTtransfer, (XtPointer) &ListTransfer);
}




/*	Function Name: Initialize
 *	Description:   Called to initialize information specific
 *                     to this widget.
 *	Arguments:     req - what was originally requested.
 *                     set - what will be created (our superclassed have
 *                           already mucked with this)
 *                     args, num_args - The arguments passed to 
 *                                      the creation call.
 *	Returns:       none.
 */

/*ARGSUSED*/
static void Initialize(Widget req, Widget set, 
		       ArgList args, Cardinal * num_args)
{
    XmI18ListWidget ilist = (XmI18ListWidget) set;

    XmI18List_column_widths(ilist) = NULL;
    XmI18List_left_loc(ilist) = 0;    
    XmI18List_state(ilist) = 0;
    XmI18List_timeout(ilist) = NoTimeout;
    XmI18List_working_row(ilist) = -1;
    XmI18List_end(ilist) = XmI18List_anchor(ilist) = NO_SELECTION;
    XmI18List_time(ilist) = 0L;

    XmI18List_search_column(ilist) = -1;

    if (XmI18List_font_list(ilist) == NULL)
        XmI18List_font_list(ilist) = XmeGetDefaultRenderTable(set, XmTEXT_FONTLIST);

    CopyColumnTitles(ilist);

    if (XmI18List_v_bar(ilist) != NULL) {
	XtAddCallback(XmI18List_v_bar(ilist), XmNvalueChangedCallback,
		      VScrollCallback, (XtPointer) set);
 	XtAddCallback(XmI18List_v_bar(ilist), XmNdragCallback,
 		      VScrollCallback, (XtPointer) set);
    }

    if (XmI18List_h_bar(ilist) != NULL) {
	XtAddCallback(XmI18List_h_bar(ilist), XmNvalueChangedCallback,
		      HScrollCallback, (XtPointer) set);
 	XtAddCallback(XmI18List_h_bar(ilist), XmNdragCallback,
 		      HScrollCallback, (XtPointer) set);
    }

    /*
     * Add the callbacks to handle left and right arrow selection
     */
    if (XmI18List_h_bar(ilist) != NULL)
	XtAddCallback(XmI18List_h_bar(ilist), XmNdecrementCallback,
		      HSlideLeftArrowCallback, (XtPointer) set);
    if (XmI18List_h_bar(ilist) != NULL)
	XtAddCallback(XmI18List_h_bar(ilist), XmNincrementCallback,
		      HSlideRightArrowCallback, (XtPointer) set);

    if (XtHeight(req) == 0)
    {
	SetVisibleSize(set, (XtWidth(req) == 0));
    }

  /* If layout_direction is set, it overrides string_direction.
   * If string_direction is set, but not layout_direction, use
   *	string_direction value.
   * If neither is set, get from parent 
   */
    if (XmPrim_layout_direction(ilist) != XmDEFAULT_DIRECTION) {
	XmI18List_string_direction(ilist) = 
	    XmDirectionToStringDirection(XmPrim_layout_direction(ilist));
    } else if (XmI18List_string_direction(ilist) != XmDEFAULT_DIRECTION) {
	XmPrim_layout_direction(ilist) = 
	    XmStringDirectionToDirection(XmI18List_string_direction(ilist));
    } else {
	XmPrim_layout_direction(ilist) = _XmGetLayoutDirection(XtParent(set));
	XmI18List_string_direction(ilist) = 
	    XmDirectionToStringDirection(XmPrim_layout_direction(ilist));
    }
  
    if (!XmRepTypeValidValue(XmRID_STRING_DIRECTION,
			   XmI18List_string_direction(ilist), set))
    {
	XmI18List_string_direction(ilist) = XmSTRING_DIRECTION_L_TO_R;
    }
}

/*	Function Name: Realize
 *	Description:   Called to realize this widget.
 *	Arguments:     w - Extended List Widget to realize.
 *                     valueMask, attributes - attributes to use when creating
 *                     this widget's window.
 *	Returns:       none.
 *
 *      This overrides the Manager's frobbing with various values.
 */

static void 
Realize(Widget w, Mask *valueMask, XSetWindowAttributes * attributes)
{
    XmI18ListWidget ilist = (XmI18ListWidget)w;

    attributes->bit_gravity = ForgetGravity;
    *valueMask |= CWBitGravity;

    CreateGCs(w);
    SortList(w, False);
    CalcColumnInfo(w, False);

    /*  get initial left location based on first column resource */
    /* have to calculate it because the columns are different widths */

    AdjustFirstRowAndCol( ilist );

    {
        XtRealizeProc realize;
        _XmProcessLock();
        realize = xmI18ListWidgetClass->core_class.superclass->core_class.realize;
        _XmProcessUnlock();
        (*realize) (w, valueMask, attributes);
    }

    {
        XtWidgetProc resize;
        _XmProcessLock();
        resize = xmI18ListWidgetClass->core_class.resize;
        _XmProcessUnlock();
        (*resize)(w);
    }
}

/*	Function Name: SetValues
 *	Description:   Called when some widget data needs to be modified on-
 *                     the-fly.
 *	Arguments:     current - the current (old) widget values.
 *                     request - before superclassed have changed things.
 *                     set - what will acutally be the new values. 
 *                     args, num_args - the arguments in the list.
 *	Returns:       none
 *
 * NOTE:  No provision is made for SetValues on the mini-icons.
 * 
 *      Modified:      03/17/92 beth - converted to pixels
 */

/*ARGSUSED*/
static Boolean SetValues(Widget current, Widget request, Widget set,
			 ArgList args, Cardinal * num_args)
{
    XmI18ListWidget 	i_old = (XmI18ListWidget)current;
    XmI18ListWidget	i_set = (XmI18ListWidget)set;
    register int 	i;
    Boolean 		redisplay = False;
    Boolean		recalculate = False;
    Boolean		resort = False;
    Boolean		refreshGCs = False;
    Boolean		readjust = False;
    Boolean		check_pos = False;
    Boolean		copyTitles = False;
  
    for (i = 0; i < *num_args; i++)
    {
	String name = args[i].name;
    
	if (streq(XmNcolumnTitles, name))
	{
	    copyTitles = True;
	    redisplay = recalculate = XtIsRealized(set);
	}
    
	if (streq(XmNentryData, name)) 
	{
	    redisplay = recalculate = resort = XtIsRealized(set);
	}
    
	if (streq(XmNsortFunctions, name))
	{
	    redisplay = resort = XtIsRealized(set);
	}
    
	if (streq(XmNfirstRow, name) || streq(XmNfirstColumn,name))
	{
	    check_pos = True;
	}

	if (streq(XmNnumRows, name))
	{
	    check_pos = True;
	    /*
	    int num_rows = (XmI18List_num_rows(i_set) - 
                           XmI18List_visible_rows(i_set));
	
	    check_pos = (XmI18List_first_row(i_set) > ((num_rows < 0)
                         ? 0 
	                 : num_rows));
	    */
	}
    }
  
    if ((XmI18List_v_bar(i_set) != XmI18List_v_bar(i_old)) ||
	(XmI18List_h_bar(i_set) != XmI18List_h_bar(i_old)))
    {
	XmI18List_v_bar(i_set) = XmI18List_v_bar(i_old);
	XmI18List_h_bar(i_set) = XmI18List_h_bar(i_old);
      
	XmeWarning(current, XmNstaticScrollbarsMsg);
    }
  
    if( XtIsSensitive(current) != XtIsSensitive(set) )
    {
	redisplay = XtIsRealized(set);
    }
  
    if (XmI18List_font_list(i_set) != XmI18List_font_list(i_old))
    {
	refreshGCs = recalculate = redisplay = XtIsRealized(set);
    }
  
    if ((i_set->primitive.foreground != i_old->primitive.foreground) ||
	(i_set->core.background_pixel != i_old->core.background_pixel))
    {
	refreshGCs = redisplay = XtIsRealized(set);
    }
  
    if (XmI18List_selected_header(i_set) != XmI18List_selected_header(i_old))
    {
	resort = redisplay = XtIsRealized(set);
    }

    /* XmNvisibleItemCount has been changed... */
    if (XmI18List_visible_rows(i_set) != XmI18List_visible_rows(i_old))
    {
	if (XmI18List_visible_rows(i_set) < 1)
	{
	    XmI18List_visible_rows(i_set) = 1;	/* reset value to 1 */

	    XmeWarning(current, XmNforceGreaterThanZeroMsg);
	}

	/* Change height only... */
	SetVisibleSize(set, False);
    }
  
    /*
     * Convert the user desired column to view to pixels
     * if and only if the entry Data has been specified
     */
    if ( XmI18List_row_data(i_set) != NULL || 
	 XmI18List_row_data(i_old) != NULL ) {
#ifdef UNUSED_CODE    
	short	column;
	if (XmI18List_first_col(i_set) > XmI18List_num_columns(i_set))
	{
	    column = XmI18List_num_columns(i_set) - 1;
	}
	else if (XmI18List_first_col(i_set) <= 0)
	{
	    column = 0;
	}
	else
	{
	    column = XmI18List_first_col(i_set) - 1; 
	}
#endif
	if ((XmI18List_num_rows(i_old) != XmI18List_num_rows(i_set)) ||
	    (XmI18List_num_columns(i_old) != XmI18List_num_columns(i_set)) ||
	    (XmI18List_row_data(i_set) != XmI18List_row_data(i_old))) 
	{
	    redisplay = recalculate = XtIsRealized(set);
	}
    
    
	if ((XmI18List_first_col(i_set) != XmI18List_first_col(i_old)) ||
	    (XmI18List_first_row(i_set) != XmI18List_first_row(i_old)) || 
	    check_pos)
	{
	    redisplay = readjust = XtIsRealized(set);		
	}	
    }

    if (XmI18List_entry_background_pixel(i_set) !=
	                    XmI18List_entry_background_pixel(i_old))
    {
	/* it was explicitly set via XtSetValues, so the user knows about it */
	XmI18List_entry_background_use(i_set) = True;
	redisplay = True;
	refreshGCs = True;
    }
  
    if (refreshGCs)
    {
	DestroyGCs(current);
	CreateGCs(set);
    }
    if (XmI18List_num_columns(i_old) != XmI18List_num_columns(i_set)) 
    {
	/* CR03506 */
	recalculate = redisplay = XtIsRealized(set);
    }
    if (copyTitles)
    {
	FreeColumnTitles(i_old);
	CopyColumnTitles(i_set);
    }
  
    if (readjust && !recalculate)
    {
	AdjustFirstRowAndCol(i_set);
    }
    else if (recalculate)
    {
	CalcColumnInfo(set, True);
	CalcLocations(set);
      
	if (!readjust) AdjustFirstCol(set);
	else AdjustFirstRowAndCol( i_set );
    }
  
    if (resort) SortList(set, False);
  
    return(redisplay);   
}
  
/*	Function Name: Redisplay
 *	Description:   This function redraws the list contents.
 *	Arguments:     w - the Frame Widget widget.
 *                     event - event that caused the exposure.
 *                     region - the region containing all the exposures.
 *	Returns:       none
 * 
 *      Modified:      03/17/92 beth - converted to pixels
 */

/*ARGSUSED*/
static void
Redisplay(Widget w, XEvent * event, Region region)
{
    XmI18ListWidget ilist = (XmI18ListWidget) w;
    short num_rows;
#ifdef notdef
    XExposeEvent * exp_event = (XExposeEvent *) &(event->xexpose);
#endif

    num_rows = XmI18List_num_rows(ilist) - XmI18List_first_row(ilist);

    /*
     * Just refresh the entire window each time.
     */
      
    DisplayList(w, XmI18List_first_row(ilist), num_rows, TRUE);
    ResizeSliders( w );
    DrawSeparator(w);
}

/*	Function Name: Resize
 *	Description:   Called when this widget has been resized.
 *	Arguments:     w - Extended List Widget to realize.
 *	Returns:       none.
 */

static void 
Resize(Widget w)
{
    if (!XtIsRealized(w))
	return;

    CalcLocations(w);
    AdjustFirstCol(w);
    ResizeSliders(w);
}

/*	Function Name: Destroy
 *	Description:   Cleans up after the widget.
 *	Arguments:     w - the widget.
 *	Returns:       none.
 */

static void
Destroy(Widget w)
{
    XmI18ListWidget ilist = (XmI18ListWidget) w;
    DestroyGCs(w);

    FreeColumnTitles(ilist);
    XtFree((XtPointer) XmI18List_column_widths(ilist));
}


/************************************************************
 *
 * Callbacks and Action Routines.
 *
 ************************************************************/

/************************************************************
 *
 *  This Selection code is pretty confusing, but here's the skinny.
 *
 *  When the first button click comes in we call the single click
 *  call back and remember when it happened.
 *
 *  If a second click comes we see if multi click time has passed and if
 *  not then we have a double click and should call the proper callback.
 *
 *  If we move out of the current row then reset the stored time to zero since
 *  we should not call the double click callback.
 *
 *  The single click timeout takes care of initializing the extended 
 *  select code.  We know it will be executed before the motion callback
 *  ever does anything interesting because of the action defined in the
 *  previous paragraph.
 *
 ************************************************************/

/*	Function Name: ButtonDownAction
 *	Description:   Called when the user sends a button up to the ilist.
 *	Arguments:     w - the ilist widget.
 *                     event - the event that caused this action.
 *                     params, num_params - action routine parameters.
 *	Returns:       none.
 */

/*ARGSUSED*/
static void
ButtonDownAction(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    short row, col;
    XmI18ListWidget ilist = (XmI18ListWidget) w;
    XButtonEvent * bevent = (XButtonEvent *) event;

    if (*num_params > 1) {
        XmeWarning(w, XmNbadMotionParamsMsg);
        return;
    }

    if (XmI18List_selection_policy(ilist) != XmSINGLE_SELECT) {
	XmI18List_state(ilist) &= ~(EXTEND | TOGGLE);	/* remove these bits.*/

	if (*num_params == 1) {
	    switch (params[0][0]) {
	    case 'e':
	    case 'E':
		XmI18List_state(ilist) |= EXTEND;	/* set extended mode. */
		break;
	    case 't':
	    case 'T':
		XmI18List_state(ilist) |= TOGGLE;	/* set extended mode. */
		break;
	    default:
	        {
		    _XmWarningMsg(w,
				XmNbadActionParameters,
				XmNbadActionParametersMsg,
				params, 1);
                }
	    }
	}
    }
	    
    if (event->type != ButtonPress) {
        static String params[] = { "BtnDown" };

        _XmWarningMsg(w, XmNunexpectedEvent,
                XmNunexpectedEventMsg, params, 1);

	return;
    }

    CvtPositionToRowColumn(w, bevent->x, bevent->y, &row, &col);

    if (XmI18List_working_row(ilist) != row) {
	/*
	 * This click not in the same row as the previous one, reset time to
	 * zero so that double click will not fire.
	 */
	XmI18List_time(ilist) = 0;	
    }

    XmI18List_working_row(ilist) = row;
    XmI18List_working_col(ilist) = col;

    /*
     * Invalid row.
     */

    if ((col >= XmI18List_num_columns(ilist)) || (row >= XmI18List_num_rows(ilist))) {
	XBell(XtDisplay(w), 0);
	return;
    }

    /*
     * Click in the column headers.  No need to deal with double clicks
     * or extended selections.
     */

    if (XmI18List_working_row(ilist) == IN_COLUMN_HEADER) {
	SelectHeader((Widget) ilist, XmI18List_working_col(ilist));
	return;
    }
	else /* reset search column since user selected a new row */
		XmI18List_search_column(ilist) = -1;

    if ((event->xbutton.time - XmI18List_time(ilist)) >
	XtGetMultiClickTime(XtDisplay(w)))
    {
	SingleClick(ilist);
    }
}

/*	Function Name: ButtonUpOrLeaveAction
 *	Description:   Called when the user sends a button up to the ilist.
 *	Arguments:     w - the ilist widget.
 *                     event - the event that caused this action.
 *                     params, num_params - action routine parameters.
 *	Returns:       none.
 */

/*ARGSUSED*/
static void
ButtonUpOrLeaveAction(Widget w, XEvent *event, 
		      String *params, Cardinal *num_params)
{
    Boolean notify_type;
    XmI18ListWidget ilist = (XmI18ListWidget) w;

    if (event->type != ButtonRelease) {
	static String params[] = { "BtnUp or BtnLeave" };
	
	_XmWarningMsg(w, XmNunexpectedEvent,
		XmNunexpectedEventMsg, params, 1);
	
	return;
    }

    /*
     * Invalid row started selection.
     */

    if ((XmI18List_working_col(ilist) >= XmI18List_num_columns(ilist)) || 
	(XmI18List_working_row(ilist) >= XmI18List_num_rows(ilist))) 
    {
	return;
    }

    /*
     * Click in a row, need to handle double click.
     * notify_type == True specifies double click, False is a single click.
     */

    notify_type = ((event->xbutton.time - XmI18List_time(ilist)) <=
		   XtGetMultiClickTime(XtDisplay(w)));

    Notify(w, notify_type);

    XmI18List_time(ilist) = event->xbutton.time;

    /*
     * Reset all these flags. 
     */

    XmI18List_state(ilist) &= ~OUTSIDE_WIDGET;
}

/*	Function Name: MotionAction
 *	Description:   Called when the user sends a button up to the ilist.
 *	Arguments:     w - the ilist widget.
 *                     event - the event that caused this action.
 *                     params, num_params - action routine parameters.
 *	Returns:       none.
 */

/*ARGSUSED*/
static void
MotionAction(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    XmI18ListWidget ilist = (XmI18ListWidget) w;
    XButtonEvent * bevent = (XButtonEvent *) event;
    short row, col;
    short y = bevent->y;

    /*
     * Invalid row to start selection.
     */

    if ((XmI18List_working_col(ilist) >= XmI18List_num_columns(ilist)) || 
	(XmI18List_working_row(ilist) >= XmI18List_num_rows(ilist))) 
    {
	return;
    }

    if (XmI18List_selection_policy(ilist) == XmSINGLE_SELECT) 
	return;			/* Do nothing here... */

    CvtPositionToRowColumn(w, bevent->x, y,  &row, &col); 
    
    /*
     * We have not moved to a new row, or we started in the headers then
     * do nothing.
     */
    
    if ((row == XmI18List_working_row(ilist)) || 
	(XmI18List_working_row(ilist) == IN_COLUMN_HEADER)) 
    {
	return;
    }

    /*
     * Begin reporting motion events.
     */
    
    if (y < 0) {
	if (XmI18List_state(ilist) & OFF_TOP)
	    return;		/* We are already off the top. */
	else
	    XmI18List_state(ilist) |= OFF_TOP;
    }
    else
	XmI18List_state(ilist) &= ~OFF_TOP;	/* remove this flag. */
    
    if (y > ((short) w->core.height)) {
	if (XmI18List_state(ilist) & OFF_BOTTOM)
	    return;		/* We are already off the bottom. */
	else
	    XmI18List_state(ilist) |= OFF_BOTTOM;
    }
    else
	XmI18List_state(ilist) &= ~OFF_BOTTOM;	/* remove this flag. */
    
    if (!(XmI18List_state(ilist) & OUTSIDE_WIDGET)) { /* Not outside widget.*/
	if (XmI18List_timeout(ilist) != NoTimeout) {
	    (void) XtRemoveTimeOut(XmI18List_timeout(ilist));
	    XmI18List_timeout(ilist) = NoTimeout; /* CR03695 */
        }
	ExtendedSelect(w, row);
	XmI18List_working_row(ilist) = NO_SELECTION; /* set to nothing... */
	return;
    }

    XmI18List_timeout(ilist) = XtAppAddTimeOut(XtWidgetToApplicationContext(w),
					   DELAY_TIME(w),
					   MoveListTimeout, (XtPointer) w);
}

/*	Function Name: SingleClick
 *	Description:   Called to process the single click.
 *	Arguments:     ilist - the ilist widget.
 *	Returns:       none.
 */

/*ARGSUSED*/
static void
SingleClick(XmI18ListWidget ilist)
{
    if (XmI18List_selection_policy(ilist) == XmSINGLE_SELECT)  {
	/*
	 * Unselect all rows except the working row.
	 */

	UnselectRows((Widget) ilist, XmI18List_working_row(ilist));
    }
    else {
	register short i, last;
	XmMultiListRowInfo * ptr;

	last = XmI18List_num_rows(ilist);
	if (!(XmI18List_state(ilist) & EXTEND)) {
	    XmI18List_end(ilist) = XmI18List_anchor(ilist) = XmI18List_working_row(ilist);

	    if (XmI18List_state(ilist) & TOGGLE)
		ToggleRow((Widget) ilist, XmI18List_working_row(ilist));
	    else		/* !EXTEND and !TOGGLE */
		UnselectRows((Widget) ilist, XmI18List_working_row(ilist));
	 
	    for (ptr = XmI18List_row_data(ilist), i = 0; i < last; i++, ptr++) 
		ptr->old_sel_state = ptr->selected;
	}
	else {
	    short l_first, l_last;

	    if (XmI18List_anchor(ilist) == NO_SELECTION) 
		return;

	    if (XmI18List_end(ilist) < XmI18List_anchor(ilist)) {
		l_first = XmI18List_end(ilist);
		l_last = XmI18List_anchor(ilist);
	    }
	    else {
		l_first = XmI18List_anchor(ilist);
		l_last = XmI18List_end(ilist);
	    }
	    
	    for (ptr = XmI18List_row_data(ilist), i = 0; i < last; i++, ptr++) {
		if ((i >= l_first) && (i <= l_last))
		    ptr->old_sel_state = FALSE;
		else
		    ptr->old_sel_state = ptr->selected;
	    }

	    if (XmI18List_state(ilist) & EXTEND)
		ExtendedSelect((Widget) ilist, XmI18List_working_row(ilist));
	}
    }
}

/*	Function Name: MoveListTimeout
 *	Description:   Called when enough time passes so that we know it 
 *                     is time to move the list.
 *	Arguments:     w_ptr - the ilist widget.
 *                     id - the interval id.
 *	Returns:       none.
 */

/*ARGSUSED*/
static void
MoveListTimeout(XtPointer w_ptr, XtIntervalId *id)
{
    short row;
    int v_inc = 0;
    Widget w = (Widget) w_ptr;
    XmI18ListWidget ilist = (XmI18ListWidget) w;

    if (XmI18List_state(ilist) & OFF_TOP) {
	/*
	 * Scroll up if top row is not on screen.
	 */
	if ((IsRowVisible(w, 0)) != 0)
	    v_inc = -1;
    }

    if (XmI18List_state(ilist) & OFF_BOTTOM) {
	/*
	 * Scroll up if last row is not on screen.
	 */
	if ((IsRowVisible(w, XmI18List_num_rows(ilist) - 1)) != 0)
	    v_inc = 1;
    }

    if (v_inc == 0) {
	XmI18List_timeout(ilist) = NoTimeout; /* CR03695 */
	return;
    }
    VScroll(w, v_inc);
    ResizeSliders(w);	/* Reset the sliders on my scrollbars. */
    
    /*
     * Get new end point (first or last row on screen).
     */
    
    if (v_inc < 0) 
	row = XmI18List_first_row(ilist);
    else {
	register int i;
	register int row_height = XmI18List_row_height(ilist);
	register Dimension h, height = w->core.height;
	
	i = XmI18List_first_row(ilist);
	h = (XmI18List_sep_y(ilist) + VERTICAL_SPACE/2);
	
	if(XmI18List_new_visual_style(ilist))
	    h += ilist->primitive.shadow_thickness;
	else
	    h += LINE_HEIGHT;
	
	/*
	 * This used to be:
	 * for (; h < height; h += row_height, i++) {}
	 *
	 * ... sigh ...
	 */
	while(h < height) {
	    h += row_height;
	    i++;
	}

	i--;
	
	if (i > (XmI18List_num_rows(ilist) - 1))
	    row = XmI18List_num_rows(ilist) - 1;
	else
	    row = i;
    }
    
    ExtendedSelect(w, row);

    /*
     * And we can do it again...
     */

    XmI18List_timeout(ilist) = XtAppAddTimeOut(XtWidgetToApplicationContext(w),
					   DELAY_TIME(w),
					   MoveListTimeout, (XtPointer) w);    
}

/*	Function Name: Notify
 *	Description:   Calls the callbacks when a notify should happen.
 *	Arguments:     w - the ilist widget.
 *                     dclick - is this a double click?
 *	Returns:       none.
 */

static void
Notify(Widget w, Boolean dclick)
{
    XmI18ListWidget ilist = (XmI18ListWidget) w;
    Boolean first = True;
    static XrmQuark elist_q;
    int row;

    if (first) {
	elist_q = XrmStringToQuark(XM_EXT_18_LIST_CLASS_NAME);
	first = False;
    }

    row = (int) XmI18List_working_row(ilist);

    /*
     * If we didn't click on a row, don't call the callbacks
     */
    if(row == IN_COLUMN_HEADER)
	return;

    if( (row < XmI18List_num_rows(ilist)) &&
	(XmI18List_working_col(ilist) < XmI18List_num_columns(ilist)) )
    {
	Widget    elist = XtParent(XtParent(w));

	XtPointer cbdata;
	if(row >= 0) 
	    cbdata = (XtPointer) &(XmI18List_row_data(ilist)[row]);
	else
	    cbdata = NULL;

	/*
	 * If my parent is an extended list, then call all callbacks
	 * on its callback list. 
	 *
	 * This is a hack to get around a toolkit specification bug.
	 *
	 * Chris D. Peterson (11/25/90)
	 */

	if( dclick )
	{
	    if( _XmUtilIsSubclassByNameQ(elist, elist_q) ) 
	    {
		XtCallCallbacks(elist, XmNdoubleClickCallback, cbdata);
	    }
	    XtCallCallbackList(w, XmI18List_double_click(ilist), cbdata);
	}
	else /* Is Single notify */
	{
	    if( _XmUtilIsSubclassByNameQ(elist, elist_q) ) 
	    {
		XtCallCallbacks(elist, XmNsingleSelectionCallback, cbdata);
	    }
	    XtCallCallbackList( w, XmI18List_single_select(ilist), cbdata);
	}
    }
}

/************************************************************
 *
 * Internal routines.
 *
 ************************************************************/

/*	Function Name: GetListWidth
 *	Description:   determines total width of data in list
 *	Arguments:     w - Extended List Widget 
 *	Returns:       total width of data in list
 */

static short
GetListWidth(Widget w)
{
    XmI18ListWidget ilist = (XmI18ListWidget) w;
    register int i, width;

    /*
     * Get maximum width of data to display
     */
    width = HORIZONTAL_SPACE;
    for (i=0; i < XmI18List_num_columns(ilist); i++)
        width += XmI18List_column_widths(ilist)[i] + HORIZONTAL_SPACE;
 
    return((short) width);
}

/*	Function Name: AdjustFirstCol
 *	Description:   Called when this widget has been resized.
 *	Arguments:     w - Extended List Widget to realize.
 *	Returns:       none.
 */

static void 
AdjustFirstCol(Widget w)
{
    XmI18ListWidget ilist = (XmI18ListWidget) w;
    register int i, extra, width;

    /*   
     * Determine if the first pixel position is negative or positive
     */
    if ((0 - XmI18List_left_loc(ilist)) >= 0)
        i = 0 - XmI18List_left_loc(ilist);
    else
        i = XmI18List_left_loc(ilist);

    /*
     * If the current pixel position (signed positive) plus the core
     * width of the list is greater than the whole width of the data
     * being displayed, then we need to adjust the first pixel position
     */
    width = GetListWidth((Widget) ilist);
    extra = width - (ilist->core.width + i);
    if (extra < 0)
        XmI18List_left_loc(ilist) = 0 - (i + extra);
}

/*	Function Name: ExtendedSelect
 *	Description:   Handle the extended selection.
 *	Arguments:     w - the ilist widget.
 *                     row - current row.
 *                     extend - extend the selection.
 *	Returns:       none
 */

static void
ExtendedSelect(Widget w, short row)
{
    XmI18ListWidget ilist = (XmI18ListWidget) w;
    register short i, first, last, end, anchor, out_first, out_last;
    Boolean state;
    XmMultiListRowInfo * ptr;

    if (row >= XmI18List_num_rows(ilist))
	row = XmI18List_num_rows(ilist) - 1;
    else if (row < 0)
	row = 0;

    end = XmI18List_end(ilist);	/* The old non-anchor end point. */
    anchor = XmI18List_anchor(ilist);
    XmI18List_end(ilist) = row;	/* New non-anchor end point. */
    state = (XmI18List_row_data(ilist) + anchor)->selected;

    if (XmI18List_anchor(ilist) < end) {
	out_first = anchor;
	out_last = end;
    }
    else {
	out_first = end;
	out_last = anchor;
    }

    if (row < out_first) {
	first = row;
	last = out_last;
	out_first = out_last + 1;	/* nothing removed from list. */
    }
    else if (row > out_last) {
	first = out_first;
	last = row;
	out_first = out_last + 1;	/* nothing removed from list. */
    }
    else {			/* between first and last. */
	if (out_first == anchor) {
	    first = out_first;
	    last = row;
	    out_first = row + 1;
	}
	else {
	    out_last = row - 1;
	    first = row;
	    last = out_last;
	}
    }

    /*
     * Handle the case of a jump from one side of the anchor to the other.
     */

    if (((end > anchor) && (row < anchor)) ||
	((end < anchor) && (row > anchor)))
    {
	register short t_first, t_last;

	if ((end > anchor) && (row < anchor)) {
	    t_first = anchor + 1;
	    t_last = end;
	    last = anchor;
	}
	else {  /* (end < anchor) && (row > anchor) */
	    first = anchor;
	    t_first = end;
	    t_last = anchor - 1;
	}

	/*
	 * Reset all these nodes to their previous state.
	 */

	ptr = XmI18List_row_data(ilist) + t_first;
	for (i = t_first; i <= t_last; i++, ptr++) 
	    if (ptr->old_sel_state != ptr->selected)
		ToggleRow(w, i);
    }

    /*
     * Set all these nodes to the same state as the anchor.
     */

    for (ptr = XmI18List_row_data(ilist) + first,i = first; i <= last;i++, ptr++) 
	if (ptr->selected != state)
	    ToggleRow(w, i);

    /*
     * Reset all these nodes to their previous state.
     */

    ptr = XmI18List_row_data(ilist) + out_first;
    for (i = out_first; i <= out_last;i++, ptr++) {
	if (ptr->old_sel_state != ptr->selected)
	    ToggleRow(w, i);
    }
}

/*	Function Name: VScrollCallback
 *	Description:   Called by the vert. scrollbar when it has been selected
 *                     by the user.
 *	Arguments:     w - the scrollbar widget.
 *                     client_data - the ilist widget.
 *                     call_data - information about where to scroll.
 *	Returns:       none
 */

/*ARGSUSED*/ 
static void
VScrollCallback(Widget w, XtPointer client_data, XtPointer call_data) 
{
    XmI18ListWidget ilist = (XmI18ListWidget) client_data;
    XmScrollBarCallbackStruct * scroll_info;

    scroll_info = (XmScrollBarCallbackStruct *) call_data;

    VScroll((Widget) ilist, (short) (scroll_info->value -
				     XmI18List_first_row(ilist)));
}

/*	Function Name: HScrollCallback
 *	Description:   Called by the horiz. scrollbar when it 
 *                     has been selected.
 *                     by the user.
 *	Arguments:     w - the scrollbar widget.
 *                     client_data - the ilist widget.
 *                     call_data - information about where to scroll.
 *	Returns:       none
 * 
 *      Modified:      03/17/92 beth - converted to pixels
 */
 
/*ARGSUSED*/ 
static void
HScrollCallback(Widget w, XtPointer client_data, XtPointer call_data) 
{
    XmI18ListWidget ilist = (XmI18ListWidget) client_data;
    XmScrollBarCallbackStruct *scroll_info;

    scroll_info = (XmScrollBarCallbackStruct *) call_data;

    HScroll((Widget)ilist,(short) scroll_info->value);

}
    
/*	Function Name: HSlideLeftArrowCallback
 *	Description:   handles repositioning of the window data according
 *                     to how long the user pressed on the left arrow
 *	Arguments:     w - the ilist widget.
 *                     client_data - the ilist widget.
 *                     junk - ***UNUSED***
 *	Returns:       none
 * 
 *      Added:         03/19/92 beth - for conversion to pixels
 */

/*ARGSUSED*/
static void
HSlideLeftArrowCallback(Widget w, XtPointer client_data, XtPointer junk) 
{
    XmI18ListWidget ilist = (XmI18ListWidget) client_data;

    /*
     * We are either going to left adjust the first visible column
     * so that the user is able to see the column beginning with the
     * first pixel or - if the first visible pixel is currently between
     * columns, then we will shift all columns once to the left,
     * thereby changing the first visible column.
     */

    /*
     * Don't bother if we're already as far left as we can go
     */
    if (XmI18List_left_loc(ilist) <= 0) {
        register int i, begin, end, before = 0;

        for (i=0; i < XmI18List_num_columns(ilist); i++) {
            if (i == 0) 
                begin = before = XmI18List_left_loc(ilist);
            else
                begin = before + HORIZONTAL_SPACE;
            end = begin + XmI18List_column_widths(ilist)[i];
            if (begin < 0 && end >= 0) {
                /*
                 * Leftmost column column is partially visible
                 * left adjust it including horizontal spacing
                 */
                XmI18List_left_loc(ilist) += 0 - before;
                break;
            } else if (begin >= 0 && i > 0) {
                /*
                 * Shift Leftmost column to the right once
                 */
                XmI18List_left_loc(ilist) += 0 - before + HORIZONTAL_SPACE;
                XmI18List_left_loc(ilist) += XmI18List_column_widths(ilist)[i-1];
                break;
            }
            before = end;
        }
    } else {
        XmI18List_left_loc(ilist) = 0;
    }

    XClearWindow(XtDisplay(ilist), XtWindow(ilist));
    DisplayList((Widget) ilist, XmI18List_first_row(ilist), 
                (XmI18List_num_rows(ilist) - XmI18List_first_row(ilist)), TRUE);
    DrawSeparator((Widget) ilist);
    ResizeSliders((Widget) ilist);
}

/*	Function Name: HSlideRightArrowCallback
 *	Description:   handles repositioning of the window data according
 *                     to how long the user pressed on the right arrow
 *	Arguments:     w - the ilist widget.
 *                     client_data - the ilist widget.
 *                     junk - ***UNUSED***
 *	Returns:       none
 * 
 *      Added:         03/19/92 beth - for conversion to pixels
 */

/*ARGSUSED*/
static void
HSlideRightArrowCallback(Widget w, XtPointer client_data, XtPointer junk) 
{
    XmI18ListWidget ilist = (XmI18ListWidget) client_data;
    register int i, width;

    /*
     * We are either going to left adjust the last visible column
     * so that the user is able to see the column beginning with the
     * first pixel or the entire column - if the last visible pixel
     * is currently between columns, then we will shift all columns
     * once to the right, thereby changing the last visible column.
     */
     
    if ((0 - XmI18List_left_loc(ilist)) >= 0)
        i = XmI18List_left_loc(ilist);
    else
        i = 0 - XmI18List_left_loc(ilist);

    /*
     * Don't bother if we're already as far right as we can go
     */
    width = GetListWidth((Widget) ilist);
    if ((int)width > (int)(i + ilist->core.width)) {
        register int begin, end, before = 0;

        for (i=0; i < XmI18List_num_columns(ilist); i++) {
            if (i == 0) 
                begin = before = XmI18List_left_loc(ilist);
            else
                begin = before + HORIZONTAL_SPACE;
            end = begin + XmI18List_column_widths(ilist)[i];
            if ((begin < 0 && end > 0) ||
                (begin >= 0 && i < XmI18List_num_columns(ilist))) {
                /*
                 * Leftmost column is partially visible
                 * make as much of it visible as possible
                 */
                if ((i + 1) == XmI18List_num_columns(ilist))
                    XmI18List_left_loc(ilist) = 0 - (width - ilist->core.width);
                else
                    XmI18List_left_loc(ilist) -= end;
                break;
            }
            before = end;
        }

    } else {
        XmI18List_left_loc(ilist) = 0 - (width - ilist->core.width);
    }

    XClearWindow(XtDisplay(ilist), XtWindow(ilist));
    DisplayList((Widget) ilist, XmI18List_first_row(ilist), 
                (XmI18List_num_rows(ilist) - XmI18List_first_row(ilist)), TRUE);
    DrawSeparator((Widget) ilist);
    ResizeSliders((Widget) ilist);
}

/************************************************************
 *
 * Local Code
 *
 ************************************************************/

/*	Function Name: CalcLocations
 *	Description:   Calculates the locations of the text positions.
 *	Arguments:     w - the ext list widget.
 *	Returns:       none
 */

static void CalcLocations(Widget w)
{
    XmI18ListWidget ilist = (XmI18ListWidget) w;
    if (XmI18List_num_columns(ilist) && XmI18List_column_titles(ilist)) /* CR03506 */
    XmI18List_sep_y(ilist) = 2 * VERTICAL_SPACE
	+ XmI18List_title_row_height(ilist);
    else XmI18List_sep_y(ilist) = 0; /* CR03506 */
    if(XmI18List_new_visual_style(ilist))
	XmI18List_sep_y(ilist) += ilist->primitive.shadow_thickness;
}

/*	Function Name: DisplayList
 *	Description:   Displays the contents of the list.
 *	Arguments:     w - the extended list widget.
 *                     start_row - the first row to display
 *                     num_rows - the number of rows to display
 *                     redraw_headers - boolean flag for redrawing
 *	Returns:       none
 * 
 *      Modified:      03/17/92 beth - converted to pixels
 */

static void
DisplayList(Widget w, short start_row, short num_rows, Boolean redraw_headers)
{
    XmI18ListWidget ilist = (XmI18ListWidget) w;
    register int i, j, row_height, title_row_height, y_loc, x_loc;
    register Position cur_x=0, cur_y, start_y;
    register short *col_widths, tot_width;
    XmString ptr;
    GC gc;
    Dimension width_unused, text_height;

    Dimension height = ilist->core.height;
    Dimension width = ilist->core.width;

    register short end_row = start_row + num_rows - 1;

    /*
     * The start position is always first_col, which
     * should have been set to the correct value before
     * calling this routine
     */

    if (XmI18List_left_loc(ilist) >= 0)
        XmI18List_left_loc(ilist) = HORIZONTAL_SPACE;
    if (LayoutIsRtoLP(w))
	cur_x = width - XmI18List_left_loc(ilist);
    else
	cur_x = XmI18List_left_loc(ilist);

    col_widths = XmI18List_column_widths(ilist);

    tot_width = GetListWidth((Widget) ilist);

    /* 
     * Find the row height.
     */
    row_height = XmI18List_row_height(ilist) + VERTICAL_SPACE;
    title_row_height = XmI18List_title_row_height(ilist) + VERTICAL_SPACE;

    /*
     * Find the selected row, and draw in the select bar in the
     * background color.
     *
     * NOTE: this must draw exactly the same rectangle as InvertArea.
     */
    
    cur_y = (XmI18List_sep_y(ilist) + VERTICAL_SPACE/2 +
	     (start_row - XmI18List_first_row(ilist)) * row_height);
    
    if(XmI18List_new_visual_style(ilist)) {
        cur_y += ilist->primitive.shadow_thickness;
    }
    else {
	cur_y += LINE_HEIGHT;
    }

    if (XmI18List_entry_background_use(ilist))
    {
	Dimension height = ilist->core.height;
    	if(XmI18List_new_visual_style(ilist))
		height -= ilist->primitive.shadow_thickness;
	    else
		height -= LINE_HEIGHT;
	    XFillRectangle(XtDisplay(w), XtWindow(w), XmI18List_entry_background_fill_gc(ilist),
	                   LayoutIsRtoLP(w)
			   ? -tot_width + XtWidth(w) - XmI18List_left_loc(ilist) + HORIZONTAL_SPACE/2
			   : XmI18List_left_loc(ilist) - HORIZONTAL_SPACE/2,
			   cur_y,
#define MaX(a,b) (a>b?a:b)
			   (unsigned int) MaX(w->core.width,tot_width), 
#undef MaX
			   (unsigned int) height);
    }

    for (i = start_row ; i <= end_row ; i++) {
	if (XmI18List_row_data(ilist)[i].selected) {
	    XFillRectangle(XtDisplay(w), XtWindow(w), XmI18List_gc(ilist),
	                   LayoutIsRtoLP(w)
			   ? -tot_width + XtWidth(w) - XmI18List_left_loc(ilist) + HORIZONTAL_SPACE/2
			   : XmI18List_left_loc(ilist) - HORIZONTAL_SPACE/2,
			   cur_y,
			   (unsigned int) tot_width, 
			   (unsigned int) row_height);
	}
	
	cur_y += row_height;
    }

    start_y = ((XmI18List_sep_y(ilist) + VERTICAL_SPACE) +
	       (start_row - XmI18List_first_row(ilist)) * row_height);

    if(XmI18List_new_visual_style(ilist)) {
	start_y += ilist->primitive.shadow_thickness;
    }
    else {
	start_y += LINE_HEIGHT;
    }

    /*
     * Draw in the Text and icons.
     */

    for (i = 0; i < XmI18List_num_columns(ilist); i++) {
	GC entry_gc;

	if (LayoutIsRtoLP(w))
	{
	    if (cur_x < 0)
		return;
	    else
	      cur_x -= col_widths[i];
	}
	else
	{
	    if (cur_x > (int) width) 
		return;
	}
	
	if (redraw_headers) {

	    /*
	     * Paint the Column Header 
	     */
	    
	    if ((XmI18List_selected_header(ilist) == i) &&
		(XmI18List_sort_functions(ilist) != NULL))
	    {
		if (XtIsSensitive(w))
		    gc = XmI18List_rev_gc(ilist);
		else
		    gc = XmI18List_stippled_rev_gc(ilist);
		
		y_loc = VERTICAL_SPACE/2;
		x_loc = cur_x - HORIZONTAL_SPACE/2;
		
		XFillRectangle(XtDisplay(w), XtWindow(w), XmI18List_gc(ilist),
			       x_loc, y_loc, 
			       (unsigned int) col_widths[i] + HORIZONTAL_SPACE,
			       (unsigned int) title_row_height);
	    }
	    else {
		if (XtIsSensitive(w))
		    gc = XmI18List_gc(ilist);
		else
		    gc = XmI18List_stippled_gc(ilist);
	    }
	    
	    if (XmI18List_column_titles(ilist) != NULL)
	    {
		ptr = XmI18List_column_titles(ilist)[i];

		cur_y = VERTICAL_SPACE;

		if(XmI18List_new_visual_style(ilist))
		    cur_y += ilist->primitive.shadow_thickness;

		XmStringDraw(XtDisplay(w), XtWindow(w),
			     XmI18List_font_list(ilist), ptr, gc, cur_x, cur_y,
			     col_widths[i], XmI18List_alignment(ilist),
			     XmPrim_layout_direction(ilist), NULL);
	    }
	}

	/*
	 * Put in the mini icons if this is column 0 and 
	 * first_col_pixmaps is true.
	 */
	
	if ( (XmI18List_first_col_pixmaps(ilist)) && (i == 0)) {
	    int pix_y_offset;

	    cur_y = XmI18List_sep_y(ilist) + VERTICAL_SPACE;
	    
	    if(XmI18List_new_visual_style(ilist))
		cur_y += ilist->primitive.shadow_thickness;
	    else
		cur_y += LINE_HEIGHT;

	    for (j = start_row; j <= end_row; j++) {
		if (IsValidPixmap(XmI18List_row_data(ilist)[j].pixmap)) {
		    
		    if (XmI18List_row_data(ilist)[j].selected) 
			entry_gc = XmI18List_entry_background_rev_gc(ilist);
		    else
			entry_gc = XmI18List_entry_background_gc(ilist);
		    
		    /*
		     * Copy it in.
		     */
		    pix_y_offset = 
		      (row_height - XmI18List_row_data(ilist)[j].pix_height)/2;
		    
		    if (XmI18List_row_data(ilist)[j].pix_depth == 1 ){
		      XCopyPlane(XtDisplay(w), XmI18List_row_data(ilist)[j].pixmap,
				 XtWindow(w), entry_gc, 0, 0,
				 XmI18List_row_data(ilist)[j].pix_width,
				 XmI18List_row_data(ilist)[j].pix_height,
				 LayoutIsRtoLP(w)
				 ? width - XmI18List_left_loc(ilist) - XmI18List_row_data(ilist)[j].pix_width
				 : cur_x,
				 cur_y+pix_y_offset, (unsigned long) 1);
		    }
		    else {
		      XCopyArea(XtDisplay(w), XmI18List_row_data(ilist)[j].pixmap,
				 XtWindow(w), entry_gc, 0, 0,
				 XmI18List_row_data(ilist)[j].pix_width,
				 XmI18List_row_data(ilist)[j].pix_height,
				 LayoutIsRtoLP(w)
				 ? width - XmI18List_left_loc(ilist) - XmI18List_row_data(ilist)[j].pix_width
				 : cur_x,
				 cur_y+pix_y_offset );
		    }
		}
		
		cur_y += row_height;
		if (cur_y > (int) height)
		    break;
	    }
	}
	else {
	    /* 
	     * Draw in the row data.
	     */
	    
	    cur_y = start_y;
	    for (j = start_row; j <= end_row; j++) {
		
		if (cur_y > (int) height)
		    break;
		
		if (XtIsSensitive(w)) {
		    if (XmI18List_row_data(ilist)[j].selected) 
			entry_gc = XmI18List_entry_background_rev_gc(ilist);
		    else
			entry_gc = XmI18List_entry_background_gc(ilist);
		}
		else {
		    if (XmI18List_row_data(ilist)[j].selected) 
			entry_gc = XmI18List_entry_background_stippled_rev_gc(ilist);
		    else
			entry_gc = XmI18List_entry_background_stippled_gc(ilist);
		}
		
		ptr = XmI18List_row_data(ilist)[j].values[i];

		XmStringExtent(XmI18List_font_list(ilist), ptr,
			       &width_unused, &text_height);

		/* to center each string within its row */
		cur_y += (int)(XmI18List_row_height(ilist) - VERTICAL_SPACE - 
			       text_height)/2;
		XmStringDraw(XtDisplay(w), XtWindow(w), 
			     XmI18List_font_list(ilist), ptr, entry_gc, cur_x, cur_y,
			     col_widths[i], XmI18List_alignment(ilist),
			     XmPrim_layout_direction(ilist), NULL);
		
		cur_y -= (int)(XmI18List_row_height(ilist) - VERTICAL_SPACE -
			       text_height)/2;
		cur_y += XmI18List_row_height(ilist) + VERTICAL_SPACE;
	    }
	}
	
	if (LayoutIsRtoLP(w))    
	    cur_x -= HORIZONTAL_SPACE;
	else
	    cur_x += HORIZONTAL_SPACE + col_widths[i];
    }
}

/*	Function Name: DrawSeparator
 *	Description:   Draws the line that separates the titles form the 
 *                     list elements.
 *	Arguments:     w - the ilist widget.
 *	Returns:       none.
 */

static void
DrawSeparator(Widget w)
{
    XmI18ListWidget ilist = (XmI18ListWidget) w;
    unsigned int width = ilist->core.width;
    unsigned int height = XmI18List_sep_y(ilist);
    GC gc;
    if (!XmI18List_num_columns(ilist) || !XmI18List_column_titles(ilist)) return; /* CR03506 */
    if(XmI18List_new_visual_style(ilist)) {
	XmeDrawShadows(XtDisplay(w), XtWindow(w),
		       ilist->primitive.top_shadow_GC,
		       ilist->primitive.bottom_shadow_GC,
		       0, 0, width,
		       height + ilist->primitive.shadow_thickness,
		       ilist->primitive.shadow_thickness,
		       XmSHADOW_OUT);
		       
		       
    }
    else
    {
	if (XtIsSensitive(w))
	    gc = XmI18List_gc(ilist);
	else
	    gc = XmI18List_stippled_gc(ilist);
	
	XFillRectangle(XtDisplay(w), XtWindow(w), gc,
		       0,
		       XmI18List_sep_y(ilist),
		       width,
		       LINE_HEIGHT);
    }
}

/*	Function Name: CreateGCs
 *	Description:   Creates the Graphics contexts.
 *	Arguments:     w - the ext list widget.
 *	Returns:       none.
 */

 /* Always create the entry_background GCs and always use them, but their 
 ** values may be different depending on whether or not to use the pixel
 ** (and may be identical to the regular GCs). We don't need new GCs for
 ** operations that don't use patterning or that don't use the background
 ** e.g. text/string draw operations.
 */

static void
CreateGCs(Widget w)
{
    XmI18ListWidget ilist = (XmI18ListWidget) w;    
    XtGCMask mask, smask;
    XGCValues values;
    Arg args[2];
    Cardinal num_args = 0;                
    Pixel temp;
    Pixmap stipple;
    XFontStruct *font;

    stipple = XCreateBitmapFromData(XtDisplay(w), 
				    RootWindowOfScreen(XtScreen(w)), gray_bits,
				    gray_width, gray_height);

    XtSetArg(args[num_args], XmNforeground, &(values.foreground)); num_args++;
    XtSetArg(args[num_args], XmNbackground, &(values.background)); num_args++;
    XtGetValues(w, args, num_args);

    XmeRenderTableGetDefaultFont (XmI18List_font_list(ilist), &font);

    values.font = font->fid;
    values.stipple = stipple;
    values.fill_style = FillStippled;
    values.graphics_exposures = False;

    mask = GCForeground | GCBackground | GCFont | GCGraphicsExposures;

#ifdef FIX_1381
	smask = mask | GCFillStyle;
#else
    smask = mask | GCStipple | GCFillStyle;
#endif
    
    XmI18List_gc(ilist) = XtGetGC(w, mask, &values);

    if (XmI18List_entry_background_use(ilist))
    {
	Pixel cache_foreground = values.foreground;
	values.foreground = XmI18List_entry_background_pixel(ilist);
    	XmI18List_entry_background_fill_gc(ilist) = XtGetGC(w, mask, &values);
	values.foreground = cache_foreground;
    }
    else
    {
	/* same as XmI18List_gc */
	/* not really used in this case */
    	XmI18List_entry_background_fill_gc(ilist) = XtGetGC(w, mask, &values);
    }

    if (XmI18List_entry_background_use(ilist))
    {
	Pixel cache_background = values.background;
	values.background = XmI18List_entry_background_pixel(ilist);
    	XmI18List_entry_background_gc(ilist) = XtGetGC(w, mask, &values);
	values.background = cache_background;
    }
    else
    {
	/* same as XmI18List_gc */
    	XmI18List_entry_background_gc(ilist) = XtGetGC(w, mask, &values);
    }

#ifdef FIX_1381
	/*added for gray insensitive foreground (instead stipple)*/
	temp = values.foreground;
    values.foreground=_XmAssignInsensitiveColor(w);
#endif

    XmI18List_stippled_gc(ilist) = XtGetGC(w, smask, &values);

    if (XmI18List_entry_background_use(ilist))
    {
	Pixel cache_background = values.background;
	values.background = XmI18List_entry_background_pixel(ilist);
    	XmI18List_entry_background_stippled_gc(ilist) = XtGetGC(w, smask, &values);
	values.background = cache_background;
    }
    else
    {
	/* same as XmI18List_stippled_gc */
    	XmI18List_entry_background_stippled_gc(ilist) = XtGetGC(w, smask, &values);
    }

#ifndef FIX_1381
    temp = values.foreground;
#endif
    values.foreground = values.background;
    values.background = temp;
    XmI18List_rev_gc(ilist) = XtGetGC(w, mask, &values);

    if (XmI18List_entry_background_use(ilist))
    {
	Pixel cache_foreground = values.foreground;
	values.foreground = XmI18List_entry_background_pixel(ilist);
    	XmI18List_entry_background_rev_gc(ilist) = XtGetGC(w, mask, &values);
	values.foreground = cache_foreground;
    }
    else
    {
	/* same as XmI18List_rev_gc */
    	XmI18List_entry_background_rev_gc(ilist) = XtGetGC(w, mask, &values);
    }

    XmI18List_stippled_rev_gc(ilist) = XtGetGC(w, smask, &values);

    if (XmI18List_entry_background_use(ilist))
    {
	Pixel cache_background = values.background;
	values.background = XmI18List_entry_background_pixel(ilist);
    	XmI18List_entry_background_stippled_rev_gc(ilist) = XtGetGC(w, smask, &values);
	values.background = cache_background;
    }
    else
    {
	/* same as XmI18List_stippled_rev_gc */
    	XmI18List_entry_background_stippled_rev_gc(ilist) = XtGetGC(w, smask, &values);
    }

    values.foreground ^= values.background;
    values.function = GXxor;
    mask = GCForeground | GCFunction;
    
    XmI18List_inv_gc(ilist) = XtGetGC(w, mask, &values);

    if (XmI18List_entry_background_use(ilist))
    {
	/* last one; we can skip caching */
	/* Pixel cache_foreground = values.foreground; */
	/* Pixel cache_background = values.background; */
	values.foreground = XmI18List_entry_background_pixel(ilist);
	values.foreground ^= values.background;
    	XmI18List_entry_background_inv_gc(ilist) = XtGetGC(w, mask, &values);
	/* values.background = cache_background; */
	/* values.foreground = cache_foreground; */
    }
    else
    {
	/* same as XmI18List_inv_gc */
    	XmI18List_entry_background_inv_gc(ilist) = XtGetGC(w, mask, &values);
    }


    XFreePixmap(XtDisplay(w), stipple);
}

/*	Function Name: DestroyGCs
 *	Description:   Destroys all GC's needed by the list.
 *	Arguments:     w - the ilist widget.
 *	Returns:       none.
 */

static void
DestroyGCs(Widget w)
{
    XmI18ListWidget ilist = (XmI18ListWidget) w;

    XtReleaseGC(w, XmI18List_gc(ilist));
    XtReleaseGC(w, XmI18List_rev_gc(ilist));

    XtReleaseGC(w, XmI18List_stippled_gc(ilist));
    XtReleaseGC(w, XmI18List_stippled_rev_gc(ilist));

    XtReleaseGC(w, XmI18List_inv_gc(ilist));

    XtReleaseGC(w, XmI18List_entry_background_gc(ilist));
    XtReleaseGC(w, XmI18List_entry_background_fill_gc(ilist));
    XtReleaseGC(w, XmI18List_entry_background_stippled_gc(ilist));
    XtReleaseGC(w, XmI18List_entry_background_stippled_rev_gc(ilist));
    XtReleaseGC(w, XmI18List_entry_background_inv_gc(ilist));
    XtReleaseGC(w, XmI18List_entry_background_rev_gc(ilist));
}

/*	Function Name: GetPixmapInfo
 *	Description:   Gets the information about each pixmap.
 *	Arguments:     w - the ilist widget.
 *	Returns:       none
 */

static void
GetPixmapInfo(Widget w)
{
    XmI18ListWidget ilist = (XmI18ListWidget) w;
    register int i, num_rows = XmI18List_num_rows(ilist);

    for (i = 0; i < num_rows; i++) {
	int x, y;
	unsigned int width, height, bw, depth;
	Window root;

	width = height = depth = 0;

	if (IsValidPixmap(XmI18List_row_data(ilist)[i].pixmap)) {
	    XGetGeometry(XtDisplay(w), XmI18List_row_data(ilist)[i].pixmap,
			 &root, &x, &y, &width, &height, &bw, &depth);
	  }

	XmI18List_row_data(ilist)[i].pix_width = width;
	XmI18List_row_data(ilist)[i].pix_height = height;
	XmI18List_row_data(ilist)[i].pix_depth = depth;
    }
}

/*	Function Name: SetVisibleSize
 *	Description:   Calculates and sets list height (& optionally width)
 *	Arguments:     w - the ilist widget.
 *		       set_width - if true, set width
 *	Returns:       none
 */
static void
SetVisibleSize(Widget w, Boolean set_width)
{
    XmI18ListWidget	ilist = (XmI18ListWidget) w;
    Dimension		title_height;
    int			height;

    CalcColumnInfo(w, True);

    title_height = 2 * VERTICAL_SPACE +
	XmI18List_title_row_height(ilist);

    if(XmI18List_new_visual_style(ilist))
	title_height += ilist->primitive.shadow_thickness;
    else
	title_height += LINE_HEIGHT;
       
    /* If we have no rows, guess at row size with font struct info... */
    if (XmI18List_num_rows(ilist) == 0)
    {
#if USE_XFT
        XmRenderTableGetDefaultFontExtents(XmI18List_font_list(ilist),
	                                   &height, NULL, NULL);
	
	if (height == 0)
	    height = VERTICAL_SPACE * XmI18List_visible_rows(ilist);
#else
	XFontStruct	*font = (XFontStruct *) NULL;

	XmeRenderTableGetDefaultFont(XmI18List_font_list(ilist), &font);

	if (font)
	    height = (font->ascent + font->descent + VERTICAL_SPACE) * 
		XmI18List_visible_rows(ilist);
	else
	    height = VERTICAL_SPACE * XmI18List_visible_rows(ilist);
#endif
    }
    else
    {
	height = (XmI18List_row_height(ilist) + VERTICAL_SPACE) * 
	    XmI18List_visible_rows(ilist);
    }

    /* Go ahead and set the widget's height resource... */
    ilist->core.height = title_height + height + VERTICAL_SPACE;

    if (set_width)
	ilist->core.width = GetListWidth(w);
}

/*	Function Name: CalcColumnInfo
 *	Description:   Calculates the layout info for the columns.
 *	Arguments:     w - the ilist widget.
 *	Returns:       none
 */
static void
CalcColumnInfo(Widget w, Boolean force)
{
    XmI18ListWidget ilist = (XmI18ListWidget) w;
    register int i, j;
    int max_width = 0, max_title_height = 0, max_height = 0;
    Dimension width, height;
    XmString ptr;

    register int num_cols = XmI18List_num_columns(ilist);
    register int num_rows = XmI18List_num_rows(ilist);

    if (XmI18List_first_col_pixmaps(ilist)) 
	GetPixmapInfo(w);

    if (((XmI18List_column_widths(ilist) == NULL) || force) && (num_cols != 0))
    {
	if (force)
	    XtFree((XtPointer) XmI18List_column_widths(ilist));
	XmI18List_column_widths(ilist) = (short *) XtMalloc(sizeof(short) *
							num_cols);
    }

    for (i = 0 ; i < num_cols; i++) {
	if (XmI18List_column_titles(ilist) != NULL)
	{
	    ptr = XmI18List_column_titles(ilist)[i];
	    XmStringExtent(XmI18List_font_list(ilist), ptr, &width, &height);

	    /* the max height for the title row */
	    if ((int) height > max_title_height) max_title_height = height;

	    /* find the max width for just this column */
	    max_width = width;
	}

	if (XmI18List_first_col_pixmaps(ilist) && (i == 0)) {
	    for (j = 0 ; j < num_rows; j++) {
		if( (int)(height =XmI18List_row_data(ilist)[j].pix_height) >
		    (int)max_height )
		    max_height = height;
		
		if ( (int)(width = XmI18List_row_data(ilist)[j].pix_width) >
		     (int)max_width )
		    max_width = width;
	    }
	}
	else {
	    for (j = 0 ; j < num_rows; j++) {
		ptr = XmI18List_row_data(ilist)[j].values[i];
		XmStringExtent(XmI18List_font_list(ilist), ptr, &width, &height);
		if ((int)width > (int)max_width)
		    max_width = width;
		if ((int)height > (int)max_height)
		    max_height = height;
	    }
	}

	XmI18List_column_widths(ilist)[i] = max_width;
    }
    XmI18List_row_height(ilist) = max_height;
    if (XmI18List_num_columns(ilist) && XmI18List_column_titles(ilist)) /* CR03506 */
    XmI18List_title_row_height(ilist) = max_title_height;
    else XmI18List_title_row_height(ilist) = 0; /* CR03506 */
}

/*	Function Name: ResizeSliders
 *	Description:   Resizes the thumbs of the v and h scrollbars.
 *	Arguments:     w - the ilist widget.
 *	Returns:       none
 * 
 *      Modified:      03/17/92 beth - converted to pixels
 */

static void
ResizeSliders(Widget w)
{
    XmI18ListWidget ilist = (XmI18ListWidget) w;    
    Arg args[5];
    Cardinal num_args=0;
    register int i, height, max_width, rows_per_screen;
    register int slide_pos, slide_size, col_width=0, min=0;

    /*
     * Get maximum width of data to display
     */
    max_width = HORIZONTAL_SPACE;
    for (i=0; i < XmI18List_num_columns(ilist); i++) {
        if (XmI18List_column_widths(ilist)[i] > col_width)
            col_width = XmI18List_column_widths(ilist)[i];
        max_width += XmI18List_column_widths(ilist)[i] + HORIZONTAL_SPACE;
    }

    if (((col_width + HORIZONTAL_SPACE) != 0) &&
	(XmI18List_h_bar(ilist) != NULL)) 
    {
        /*
         * Adjust the slider size and page increment values
         * The size and position (returned and set) are relative to
         * the number of columns of data that we have - BUT, we
         * have to convert this information to pixels for correct
         * processing.
         */
        slide_size = ilist->core.width;
        if (slide_size < 1)
            slide_size = 1;
        if (slide_size > max_width)
            slide_size = max_width;

        /*
         * Adjust slider position according to new size
         * and location of first pixel of display data
         */
        if (XmI18List_left_loc(ilist) >= 0)
            slide_pos = 1;
        else {
            i = (0 - XmI18List_left_loc(ilist)) + ilist->core.width;
            if (i >= max_width)
                slide_pos = max_width - slide_size;
            else if (i < max_width)
                slide_pos = i - slide_size;
        }

        if (slide_pos < 1)
            slide_pos = 1;
        if (slide_pos > (max_width - slide_size))
            slide_pos = max_width - slide_size;

	XtSetArg(args[num_args], XmNvalue, slide_pos);           num_args++;
	XtSetArg(args[num_args], XmNmaximum, max_width);         num_args++;
	XtSetArg(args[num_args], XmNsliderSize, slide_size);     num_args++;
	XtSetArg(args[num_args], XmNpageIncrement, slide_size);  num_args++;
	XtSetValues(XmI18List_h_bar(ilist), args, num_args);
    }

    height = ilist->core.height - 
	     (2 * VERTICAL_SPACE + XmI18List_title_row_height(ilist));

    if(XmI18List_new_visual_style(ilist))
	height -= ilist->primitive.shadow_thickness;
    else
	height -= LINE_HEIGHT;

    if ((height > 0) && (XmI18List_v_bar(ilist) != NULL)) {
	int max_first;

	rows_per_screen = height / (XmI18List_row_height(ilist) + VERTICAL_SPACE);
	max_first = XmI18List_num_rows(ilist) - rows_per_screen;

	if (max_first < XmI18List_first_row(ilist)) {
	    if (max_first > 0)
		XmI18List_first_row(ilist) = max_first;
	    else
		XmI18List_first_row(ilist) = 0;
	}

        /*
         * Set up the scroll bars resources
         * MUST BE very thorough, otherwise warnings are issued.
         */
        slide_size = rows_per_screen;
        max_width = XmI18List_num_rows(ilist);
	slide_pos = XmI18List_first_row(ilist);
        if (max_width <= 1)
            max_width = 1;

        if (slide_size > max_width)
            slide_size = max_width;

        if (slide_pos > (max_width - slide_size))
            slide_pos = (max_width - slide_size);

        if (slide_pos < min)
            slide_pos = min;

	num_args = 0;
	XtSetArg(args[num_args], XmNminimum, min);              num_args++;
	XtSetArg(args[num_args], XmNvalue, slide_pos);          num_args++;
	XtSetArg(args[num_args], XmNmaximum, max_width);        num_args++;
	XtSetArg(args[num_args], XmNsliderSize, slide_size);    num_args++;
	XtSetArg(args[num_args], XmNpageIncrement, slide_size); num_args++;
	XtSetValues(XmI18List_v_bar(ilist), args, num_args);
    }
}

/*	Function Name: VScroll
 *	Description:   Scrolls the window a given number of
 *                     rows up or down (up is negative)
 *	Arguments:     w - the ICS list widget.
 *                     amount - the number of rows to scroll, negative
 *                              values scroll backward.
 *	Returns:       none
 */

static void
VScroll(Widget w, short amount)
{
    XmI18ListWidget ilist = (XmI18ListWidget) w;
    int y_start;
    short num_rows;

    XmI18List_first_row(ilist) += amount;
    if (XmI18List_first_row(ilist) < 0)
	XmI18List_first_row(ilist) = 0;

    y_start = XmI18List_sep_y(ilist);

    if(XmI18List_new_visual_style(ilist))
	y_start += ilist->primitive.shadow_thickness;
    else
	y_start += LINE_HEIGHT;

    XClearArea(XtDisplay(w), XtWindow(w), 0, y_start,
	       (unsigned int) 0, (unsigned int) 0, FALSE);

    num_rows = XmI18List_num_rows(ilist) - XmI18List_first_row(ilist);
    DisplayList(w, XmI18List_first_row(ilist), num_rows, TRUE);
}


/*	Function Name: CvtColNumToPixelVal
 *	Description:   determines which is the first visible pixel
 *                     in the passed column number
 *	Arguments:     w - the ilist widget data to retrieve info from
 *                     col - the column number to search in
 *	Returns:       the pixel value
 * 
 *      Added:         03/17/92 beth
 */

static short
CvtColNumToPixelVal(Widget w, short col)
{
    XmI18ListWidget ilist = (XmI18ListWidget) w;
    register int i;
    short value=0;

    /*
     * Sum up the widest pixel values in each column
     * until we reach the passed in column
     */
    for (i = 0; i < col && i < XmI18List_num_columns(ilist); i++)
        value += XmI18List_column_widths(ilist)[i] + HORIZONTAL_SPACE;

    return(value);
}

/*	Function Name: CvtPixelValToColNum
 *	Description:   determines which is the visible column
 *                     corresponding to the passed pixel value
 *	Arguments:     w - the ilist widget data to retrieve info from
 *                     x - the x pixel value to look for
 *	Returns:       the column number
 * 
 *      Added:         03/17/92 beth
 */

static short
CvtPixelValToColNum(Widget w, short x)
{
    XmI18ListWidget ilist = (XmI18ListWidget) w;
    register int i=0;
    short cur_col;

    /*
     * Sum up the widest pixel values in each column
     * and see where the passed in x pixel value falls
     */
    if (LayoutIsRtoLP(w))
	cur_col = XtWidth(w) - XmI18List_left_loc(ilist);
    else
	cur_col = XmI18List_left_loc(ilist);
    for (; i < XmI18List_num_columns(ilist); i++) {
	if (LayoutIsRtoLP(w))
	{
            cur_col -= XmI18List_column_widths(ilist)[i] + HORIZONTAL_SPACE;
            if (cur_col < x)
                break;
	}
	else
	{
            cur_col += XmI18List_column_widths(ilist)[i] + HORIZONTAL_SPACE;
            if (cur_col > x)
                break;
	}
    }

    return((short) i);
}

/*	Function Name: HScroll
 *	Description:   Scrolls the window a given number of pixels/
 *                     columns left or right (left is negative)
 *	Arguments:     w - the ICS list widget.
 *                     amount - the pixel amount to scroll.
 *	Returns:       none
 * 
 *      Modified:      03/17/92 beth - converted to pixels
 */

static void
HScroll(Widget w, short amount)
{
    XmI18ListWidget ilist = (XmI18ListWidget) w;
    int y_start;
    unsigned int title_height;
    short num_rows;

    if ((0 - amount) >= 0)
        XmI18List_left_loc(ilist) = amount;
    else
        XmI18List_left_loc(ilist) = 0 - amount;

    title_height = XmI18List_sep_y(ilist);
    y_start = XmI18List_sep_y(ilist);

    if(XmI18List_new_visual_style(ilist))
	y_start += ilist->primitive.shadow_thickness;
    else
	y_start += LINE_HEIGHT;

    XClearArea(XtDisplay(w), XtWindow(w), 0, 0, 
	       (unsigned int) 0, title_height, FALSE);
    XClearArea(XtDisplay(w), XtWindow(w), 0, y_start, 
	       (unsigned int) 0, (unsigned int) 0, FALSE);

    num_rows = XmI18List_num_rows(ilist) - XmI18List_first_row(ilist);
    DisplayList(w, XmI18List_first_row(ilist), num_rows, TRUE);
}

/*	Function Name: CvtPositionToRowColumn
 *	Description:   Converts an X location to a row and column.
 *	Arguments:     w - the ilist widget.
 *                     x, y - the location in the window in pixel space.
 *  RETURNED           row, column - the location of this point in list space.
 *	Returns:       none.
 *
 * NOTE:  This functions can return cells the are off the end of
 *        the current space.  It is up to the caller to check
 *        to be sure he does not run off then end of an array.
 * 
 *      Modified:      03/17/92 beth - converted to pixels
 */

static void
CvtPositionToRowColumn(Widget w, short x, short y, short * row, short * column)
{
    XmI18ListWidget ilist = (XmI18ListWidget) w;
    int title_extra;

    if(XmI18List_new_visual_style(ilist))
	title_extra = ilist->primitive.shadow_thickness;
    else
	title_extra = LINE_HEIGHT;

    /*
     * See if the y pixel value passed is located in the header
     * or a row visible within the clip window or not visible at all
     */
    if (y < 0) {
        *row = IN_COLUMN_HEADER + y/(XmI18List_row_height(ilist) + VERTICAL_SPACE);
    } else if (y < (XmI18List_sep_y(ilist) + title_extra)) {
        *row = IN_COLUMN_HEADER;
    } else {
        y -= XmI18List_sep_y(ilist) + title_extra + VERTICAL_SPACE/2;

        *row = XmI18List_first_row(ilist) + y/(XmI18List_row_height(ilist) +
                                           VERTICAL_SPACE);
    }

    *column = CvtPixelValToColNum((Widget) w, x);
}

/*	Function Name: CvtRowColumnToPosition
 *	Description:   Converts a row and column to an X Window Position.
 *                     the coord's are of the upper left corner of the cell.
 *	Arguments:     w - the ilist widget.
 *                     row, column - the location of this point in list space.
 *    RETURNED         x, y - the location in the window in pixel space.
 *	Returns:       none.
 * 
 *      Modified:      03/17/92 beth - converted to pixels
 */

static void
CvtRowColumnToPosition(Widget w, short row, short column, short * x, short * y)
{
    XmI18ListWidget ilist = (XmI18ListWidget) w;
    int title_extra;

    if(XmI18List_new_visual_style(ilist))
	title_extra = ilist->primitive.shadow_thickness;
    else
	title_extra = LINE_HEIGHT;

    if (row == IN_COLUMN_HEADER) 
	*y = VERTICAL_SPACE/2;
    else {
	*y = (XmI18List_sep_y(ilist) + title_extra + 
	      (row - XmI18List_first_row(ilist)) * 
	      (XmI18List_row_height(ilist) + VERTICAL_SPACE) + VERTICAL_SPACE/2);
    }

    *x = CvtColNumToPixelVal((Widget) ilist, column);
}

/*	Function Name: ToggleRow
 *	Description:   Toggles the state of the given row.
 *	Arguments:     w - the ilist widget.
 *                     row - the row to select.
 *	Returns:       none
 */

static void
ToggleRow(Widget w, short row)
{
    XmI18ListWidget ilist = (XmI18ListWidget) w;

    XmI18List_row_data(ilist)[row].selected = !XmI18List_row_data(ilist)[row].selected;

    if (IsRowVisible(w, row) >= 0)
	InvertArea(w, row, ENTIRE_ROW);
}

/*	Function Name: UnselectRows
 *	Description:   Unselects some rows (see below) and returns the
 *                     row that was left selected.
 *	Arguments:     w - the ilist widget.
 *                     row - row to leave selected.
 *	Returns:       none.
 */

static void
UnselectRows(Widget w, short sel_row)
{
    XmI18ListWidget ilist = (XmI18ListWidget) w;
    register short row;
    XmMultiListRowInfo *ptr = XmI18List_row_data(ilist);

    XmI18List_end(ilist) = XmI18List_anchor(ilist) = sel_row;

    for (row = 0; row < XmI18List_num_rows(ilist); row++, ptr++) {
	/*
	 * if the row is correct and it is unselected 
	 * or if the row incorrect and it is selected then toggle the row.
	 */
	if (ptr->selected != (row == sel_row))
	    ToggleRow(w, row);
    }
}

/*	Function Name: SelectHeader
 *	Description:   Sets the header corrosponding to the column passed in.
 *	Arguments:     w - the ilist widget.
 *                     column - the column header to select.
 *	Returns:       none
 */

static void
SelectHeader(Widget w, short column)
{
    XmI18ListWidget ilist = (XmI18ListWidget) w;

    if ((XmI18List_selected_header(ilist) == column) ||
	(XmI18List_sort_functions(ilist) == NULL))
    {
	return;
    }

    XmI18List_selected_header(ilist) = column;

    SortList(w, True);		/* Causes a rediplay. */
}

/*	Function Name: SortList
 *	Description:   Sorts the elements in the list.
 *	Arguments:     w - the ilist widget.
 *                     redisplay - redraw the ilist widget.
 *	Returns:       none.
 */

static void
SortList(Widget w, Boolean redisplay)
{
    XmI18ListWidget ilist = (XmI18ListWidget) w;

    if (XmI18List_sort_functions(ilist) != NULL) {
	global_current_widget = w;
	qsort(XmI18List_row_data(ilist),
	        XmI18List_num_rows(ilist),
		sizeof(XmMultiListRowInfo),
	        QSortTest);
	if (redisplay)
	    RedrawList(w);
    }
         /* this is kind of unnecessary, instead should just update headers */
    else RedrawList(w);    
}

/*	Function Name: QSortTest
 *	Description:   Takes the internal data, and the calls the 
 *                     appropriate sort routine supplied by the client.
 *	Arguments:     str1, str2 - the two values to compare.
 *	Returns:       
 */

static int 
QSortTest(const void * row1, const void * row2)
{
    XmI18ListWidget ilist = (XmI18ListWidget) global_current_widget;
    short col = XmI18List_selected_header(ilist);
    Xm18SortFunction sort_f = XmI18List_sort_functions(ilist)[(int)col];

    return ((*sort_f) (col, (XmMultiListRowInfo*)row1,
            (XmMultiListRowInfo*)row2));
}    


/*	Function Name: InvertArea
 *	Description:   Inverts the the area specified.
 *	Arguments:     w - the ilist widget.
 *                     row, column - row/column to invert.
 *	Returns:       none.
 *
 * NOTE:  The value IN_COLUMN_HEADER for the row means the area
 *        to be inverted is in the column header.
 *
 *        The value ENTIRE_ROW for the column causes the entire row to
 *        be inverted.
 * 
 *      Modified:      03/17/92 beth - converted to pixels
 */

static void
InvertArea(Widget w, short row, short column)
{
  XmI18ListWidget ilist = (XmI18ListWidget) w;
  unsigned int width, height;
  short x, y, pix_y_offset;
  GC gc_to_use;
  
  if (!XtIsRealized(w)) return; 

  if ((row == NO_SELECTION) || (column == NO_SELECTION))
    return;
  
  if (column == ENTIRE_ROW) {
    CvtRowColumnToPosition(w, row, 0, &x, &y);
    
    width = GetListWidth((Widget) ilist);
    if (LayoutIsRtoLP(w))
	x = -width + XtWidth(w) - XmI18List_left_loc(ilist) + HORIZONTAL_SPACE/2;
    else
	x = XmI18List_left_loc(ilist) - HORIZONTAL_SPACE/2;
  }
  else {
    CvtRowColumnToPosition(w, row, column, &x, &y);
    width = HORIZONTAL_SPACE + XmI18List_column_widths(ilist)[column];
  }
  
  if (row == IN_COLUMN_HEADER)
  {
    height = XmI18List_title_row_height(ilist) + VERTICAL_SPACE;
    gc_to_use = XmI18List_inv_gc(ilist);
  }
  else
  {
    height = XmI18List_row_height(ilist) + VERTICAL_SPACE;
    gc_to_use = XmI18List_entry_background_inv_gc(ilist);
  }
  
  XFillRectangle(XtDisplay(w), XtWindow(w), gc_to_use,
		 (int) x, (int) y, width, height);
  
  if ( (XmI18List_first_col_pixmaps(ilist)) && 
       (XmI18List_row_data(ilist)[row].pix_depth != 1 ) &&
       IsValidPixmap(XmI18List_row_data(ilist)[row].pixmap) )
  {
      pix_y_offset = (height - XmI18List_row_data(ilist)[row].pix_height)/2;
      XCopyArea(XtDisplay(w), XmI18List_row_data(ilist)[row].pixmap,
		XtWindow(w), XmI18List_entry_background_gc(ilist), 0, 0,
		XmI18List_row_data(ilist)[row].pix_width,
		XmI18List_row_data(ilist)[row].pix_height,
		LayoutIsRtoLP(w)
		  ? XtWidth(ilist) - XmI18List_left_loc(ilist) - XmI18List_row_data(ilist)[row].pix_height
		  : XmI18List_left_loc(ilist),
		y+pix_y_offset );
  }
}


/*	Function Name: RedrawList
 *	Description:   Redraws the current list contents.
 *	Arguments:     w - the ilist widget.
 *	Returns:       none.
 * 
 *      Modified:      03/17/92 beth - converted to pixels
 */

static void
RedrawList(Widget w)
{
    XmI18ListWidget ilist = (XmI18ListWidget) w;
    short num_rows;

    if (XtIsRealized(w)) {
        XClearWindow(XtDisplay(w), XtWindow(w));

        num_rows = XmI18List_num_rows(ilist) - XmI18List_first_row(ilist);
        DisplayList(w, XmI18List_first_row(ilist), num_rows, TRUE);
        DrawSeparator(w);
    }
}

/*	Function Name: IsRowVisible
 *	Description:   returns 0 if the row is visable. Otherwise returns
 * 		the number of rows down we have to scroll (- values for up) 
 *		to make the row visible.
 *	Arguments:     w - the ilist widget.
 *                     row - the row to check.
 *	Returns:       none.
 */
 
static int
IsRowVisible(Widget w, short row)
{
    XmI18ListWidget ilist = (XmI18ListWidget) w;
    short r_row, r_col;

    if (row < XmI18List_first_row(ilist))
	return(row - XmI18List_first_row(ilist));
   
    CvtPositionToRowColumn((Widget) w, 0, (short) (ilist->core.height + 1),
			   &r_row, &r_col);
 
    if (row >= r_row)
	return(row - r_row + 1);
   
    return(0);
}

/*	Function Name: AdjustVisiblePosition
 *	Description:   makes the string found by find, visible
 *	Arguments:     w - the ilist widget.
 *                     position - the new position for lef_loc
 *                     check - whether we should check the new position
 *                             to see if it's valid
 *                     value - the amount to check the position against
 *	Returns:       none
 *
 *      Added:         03/17/92 beth - for conversion to pixels
 */

static void
AdjustVisiblePosition(Widget w, short position, Boolean check, short value)
{
    XmI18ListWidget ilist = (XmI18ListWidget) w;

    /*
     * Check to make sure that we can move the amount requested
     */
    if (check && position > value)
        position = 0 - value;
    else
        position = 0 - position;

    XmI18List_left_loc(ilist) += position;
    HScroll(w, XmI18List_left_loc(ilist));
    AdjustFirstCol(w);
    ResizeSliders(w);
}


static void
AdjustFirstRowAndCol( XmI18ListWidget ilist )
{
  Dimension total_width, height;
  int rows_per_screen, new_left_loc;
  register int j;
  int title_extra;
  
  if(XmI18List_new_visual_style(ilist))
      title_extra = ilist->primitive.shadow_thickness;
  else
      title_extra = LINE_HEIGHT;
  
  new_left_loc = 0;	
  for (j=0;  j < XmI18List_first_col(ilist); j++){
    new_left_loc -=  (XmI18List_column_widths(ilist)[j] + 
		      HORIZONTAL_SPACE);
  }

  total_width = GetListWidth((Widget)ilist);

  if (ilist->core.width > total_width ){
    XmI18List_left_loc(ilist) = 0;
  }
  else if (new_left_loc < (int)((int)ilist->core.width-
				(int)total_width)){
    XmI18List_left_loc(ilist) = (int)((int)ilist->core.width-
				  (int)total_width);
  }
  else XmI18List_left_loc(ilist) = new_left_loc;
  
  
  height = ilist->core.height - 
    (2 * VERTICAL_SPACE + XmI18List_title_row_height(ilist) +  title_extra);
  
  rows_per_screen = (int)height/(int)(XmI18List_row_height(ilist)+ VERTICAL_SPACE);
  
  if (XmI18List_num_rows(ilist) < rows_per_screen)
  {
      XmI18List_first_row(ilist) = 0;
  }
  else if (XmI18List_first_row(ilist) > (XmI18List_num_rows(ilist) - 
				     rows_per_screen))
  {
      XmI18List_first_row(ilist) = (XmI18List_num_rows(ilist) - rows_per_screen);
  }

}


/*	Function Name: MakePositionVisible
 *	Description:   makes sure that the string found by the find
 *                     procedure, is as visible (length) as possible
 *	Arguments:     w - the ilist widget.
 *                     row - the row to make visible.
 *                     start - the start pixel of the string
 *                     last - the last pixel of the string
 *                     width - the width of the display data
 *	Returns:       status (True if search and move worked).
 *
 *      Modified:      03/17/92 beth - converted to pixels
 */

static Boolean
MakePositionVisible(Widget w, short row, short start, short last, int width)
{
    XmI18ListWidget ilist = (XmI18ListWidget) w;
    short amount, before = 0;

    if ((amount = IsRowVisible(w, row)) != 0)
        VScroll(w, amount);

    /*
     * Adjust the start and last values for better viewing
     */
    if (start >= HORIZONTAL_SPACE)
        start -= HORIZONTAL_SPACE;
    last += HORIZONTAL_SPACE;

    /*
     * Convert the start pixel we want to see to visible pixels.
     * This involves comparing the start pixel location within
     * the visible window and seeing if it's possible to move
     * the visible window to contain the pixels we wish to see.
     */
    if (XmI18List_left_loc(ilist) < 0)
        before -= XmI18List_left_loc(ilist);

    if (XmI18List_left_loc(ilist) < 0 && before >= start) {
        /*
         * The start pixel is to the left of the visible window
         * pass a negative value - it will be converted to positive later
         */
        AdjustVisiblePosition((Widget) ilist, (0 - (before - start)), False, 0);
        return(TRUE);

    } else if (start >= before && start <= width) {
        short move, begin, end;

        if ((int)start <= (int)(before + ilist->core.width)) {
            /*
             * The start pixel is inside the visible window
             */

            begin = (before + ilist->core.width) - start;
            end = last - start;
            if (begin < end)
                move = end - begin;
            else
                move = 0;
        } else {
            /*
             * The start pixel is to the right of the visible window
             */
            move = last - (before + ilist->core.width);
        }

        if (move != 0)
            AdjustVisiblePosition((Widget) ilist, move, True, 
                                  (width - (before + ilist->core.width)));
        return(TRUE);
    }

    return(FALSE);
}

/************************************************************
 *
 * Exported Functions.
 *
 ************************************************************/

/* -kat 1/18/91
 * Function Name: XmI18ListGetSelectedRows
 * Description:   Takes an IList and returns a NULL terminated array 
 *                of pointers to selected rows
 * Arguments:     w - the ilist widget
 * Returns:       A NULL terminated array of the row info structures.
 *                NULL is returned if nothing is selected.
 */

XmMultiListRowInfo **
XmI18ListGetSelectedRows(Widget w)
{
    register int i, j;
    register XmMultiListRowInfo *row_data, **ptr;
    XmMultiListRowInfo **ret_rows = NULL;
    XmI18ListWidget ilist = (XmI18ListWidget) w;

    row_data = XmI18List_row_data(ilist);
    for (i = j = 0; j < XmI18List_num_rows(ilist); j++, row_data++) { 
	if (row_data->selected) 
	    i++;
    }
    
    if (i != 0) {
	ptr = ret_rows = (XmMultiListRowInfo **) XtMalloc(
                sizeof(XmMultiListRowInfo *) * (i + 1));
	ret_rows[i] = NULL;
	
	row_data = XmI18List_row_data(ilist);
	for (j = 0; j < XmI18List_num_rows(ilist); j++, row_data++) { 
	    if (row_data->selected) 
		*ptr++ = row_data;
	}
    }

    return(ret_rows);
}

/*  Function Name: XmI18ListDoSearch.
 *  Description:   Performs a search for the specified string.
 *  Arguments:     w - the ilist widget.
 *                     str - the string to search for.
 *                     reset - indicates if we are searching for
 *                             something new
 *  Returns:       status (True if search worked).
 *
 *      Rewritten:      11/17/93 dave - works for I18List now
 */

Boolean
XmI18ListDoSearch(Widget w, String str, Boolean reset)
{
    XmI18ListWidget ilist = (XmI18ListWidget) w;
    Boolean foundit;
    XmString xms = XmStringCreateLocalized(str);
    int found_row, found_column, search_column;

    int first_selected_row = FirstSelectedRow(w);

    if ( reset ) XmI18List_search_column(ilist) = -1;

	/*
	 * If we've searched last, be sure to start at NEXT column.
	 * Otherwise start at first (0th) column
	 */
    search_column = XmI18List_search_column(ilist) + 1;

    if ( first_selected_row == -1 ) first_selected_row = 0;

    foundit = Search(ilist, xms, 
		     first_selected_row, search_column,
		     &found_row, &found_column);

    if ( foundit )
    {
	XmI18List_search_column(ilist) = found_column;
	UnselectRows(w, found_row);
	MakeCellVisible(w, found_row, found_column);
    }
    else
    {
	XmI18List_search_column(ilist) = -1;
    }
    return (foundit);
}

/*  Function Name: XmI18ListFindRow
 *  Description:   Performs a search for the specified string.
 *  Arguments:     w - the ilist widget.
 *                 str - the string to search for.
 *                 reset - indicates if we are searching for
 *                         something new
 *		   do_visual - whether or not to toggle selected rows...
 *  Returns:       XmMultiListRowInfo of matching row (or NULL if no match found)
 *
 */
XmMultiListRowInfo *XmI18ListFindRow(Widget w, String str, int *found_column,
			      Boolean reset, Boolean do_visual)
{
    XmI18ListWidget ilist = (XmI18ListWidget) w;
    XmMultiListRowInfo *ptr = XmI18List_row_data(ilist);
    Boolean foundit;
    XmString xms = XmStringCreateLocalized(str);
    int found_row, search_column;
    int first_selected_row = FirstSelectedRow(w);

    if (reset)
	XmI18List_search_column(ilist) = -1;

    /*
     * If we've searched last, be sure to start at NEXT column.
     * Otherwise start at first (0th) column
     */
    search_column = XmI18List_search_column(ilist) + 1;

    if (first_selected_row == -1)
	first_selected_row = 0;

    foundit = Search(ilist, xms, first_selected_row,
		     search_column, &found_row, found_column);

    if (do_visual)
    {
	if (foundit)
	{
	    XmI18List_search_column(ilist) = *found_column;
	    UnselectRows(w, found_row);
	    MakeCellVisible(w, found_row, *found_column);
	}
	else
	    XmI18List_search_column(ilist) = -1;
    }

    return (XmMultiListRowInfo *)(foundit ? &ptr[found_row] : NULL);
}

static int
FirstSelectedRow(Widget w)
{
    XmI18ListWidget ilist = (XmI18ListWidget) w;
    XmMultiListRowInfo *ptr = XmI18List_row_data(ilist);
	int i;

	for (i=0;i<XmI18List_num_rows(ilist);i++)
	{
		if (ptr[i].selected)
		return (i);
	}

	/* fell thru. Nothing selected */
	return (-1);
}

static Boolean
Search(XmI18ListWidget ilist, XmString xms,
       int start_row, int start_column,
       int *found_row, int *found_column)
{
	int row, column;
	XmString value;

	/*
	 * First a quick check.  If we have no columns or rows then
	 * we cannot find anything.
	 */
	if( XmI18List_num_columns(ilist) == 0 || XmI18List_num_rows(ilist) == 0 )
	{
	    return( False );
	}

	for (row=start_row ; row < XmI18List_num_rows(ilist); row++)
	{
		for (column=start_column; column < XmI18List_num_columns(ilist);
			 column++)
		  {
		  if ((column != 0) || (!XmI18List_first_col_pixmaps(ilist))){
		    value = XmI18List_row_data(ilist)[row].values[column];
			if (XmStringHasSubstring(value, xms))
			  {
			    /* found it! */
			    *found_row = row;
			    *found_column = column;
			    return (True);
			  }
		  }
		}
		start_column = 0; /* all but the first start at zero */
	}

	/* didn't find it. Wrap search around */
	for (row=0; row <= start_row; row++)
        for (column=0; column < XmI18List_num_columns(ilist); column++)
	  if ((column != 0) || (!XmI18List_first_col_pixmaps(ilist))){
	    value = XmI18List_row_data(ilist)[row].values[column];
            if (XmStringHasSubstring(value, xms))
	      {
                /* found it! */
                *found_row = row;
                *found_column = column;
                return (True);
	      }
	  }
	/* fell thru. It's not there */
	return (False);
}

static void
MakeCellVisible(Widget w, int row, int col)
{
    XmI18ListWidget ilist = (XmI18ListWidget) w;
    short width = GetListWidth(w);     /* total width of list data */
    short start = CvtColNumToPixelVal(w, (short) col); /* LHS of col */
    short end;                                         /* RHS of col */

    if (!XtIsRealized(w))
	return; 

    if (col >= XmI18List_num_columns(ilist))
    	end = width;
    else 
    	end = XmI18List_column_widths(ilist)[col] + start;

    (void) MakePositionVisible(w, row, start, end, width);

    ResizeSliders(w);
}



/*
 *
 * Public Convenience Routines
 *
 */



/*  Function Name: Xm18IListUnselectAllItems
 *  Description:   Unselects all rows
 *  Arguments:     w - the ilist widget.
 *  Returns:       none
 */
void
Xm18IListUnselectAllItems( Widget w )
{
  register int row;
  XmI18ListWidget ilist = (XmI18ListWidget) w;
  XmMultiListRowInfo *ptr = XmI18List_row_data(ilist);

  _XmWidgetToAppContext(w);
  _XmAppLock(app);

  for (row = 0; row < XmI18List_num_rows(ilist); row++, ptr++) {
    if (ptr->selected)
      ToggleRow(w, row);
  }
  _XmAppUnlock(app);
}


/*  Function Name: Xm18IListUnselectItem
 *  Description:   Unselects the row passed in
 *  Arguments:     w - the ilist widget.
 *                 row_info - ptr to the row passed in
 *  Returns:       none
 */
void
Xm18IListUnselectItem( Widget w, XmMultiListRowInfo *row_info )
{
  register int row=0;
  XmI18ListWidget ilist = (XmI18ListWidget) w;
  XmMultiListRowInfo *ptr = XmI18List_row_data(ilist);
  Boolean done=False;

  while (row < XmI18List_num_rows(ilist) && !done){
    if (ptr == row_info){
      if (ptr->selected)
	ToggleRow( w, row);
      done = True;
    }
    else {
      ptr++;
      row++;
    }
  }
}


/*  Function Name: XmI18ListToggleRow
 *  Description:   Toggles the selection state of a specified row
 *  Arguments:     w - the ilist widget.
 *  Returns:       none
 */
void
XmI18ListToggleRow(Widget w, short row)
{
      ToggleRow(w, row);
}

/*
 * Function:
 *	CopyColumnTitles(ilist)
 * Description:
 *	This function copies the XmStringTable that is the column titles
 *	into local (widget controled) memory.
 * Input:
 *	ilist : XmI18ListWidget - the widget to copy titles
 * Output:
 *	None.
 */
static void
CopyColumnTitles(XmI18ListWidget ilist)
{
    int           i;
    XmStringTable copy;

    if( XmI18List_num_columns(ilist) == 0 ||
        XmI18List_column_titles(ilist) == NULL )
    {
	copy = (XmStringTable) NULL;
    }
    else
    {
	copy = (XmStringTable)
	    XtMalloc(sizeof(XmString) * XmI18List_num_columns(ilist));

	for( i = 0; i < XmI18List_num_columns(ilist); ++i )
	{
	    copy[i] = XmStringCopy(XmI18List_column_titles(ilist)[i]);
	}
    }
    XmI18List_column_titles(ilist) = copy;
}

/*
 * Function:
 *	FreeColumnTitles(ilist)
 * Description:
 *	This function frees the memory associated with the column titles
 *	for the list.
 * Input:
 *	ilist : XmI18ListWidget - the widget to copy titles
 * Output:
 *	None.
 */
static void
FreeColumnTitles(XmI18ListWidget ilist)
{
    int           i;

    if( XmI18List_num_columns(ilist) == 0 || XmI18List_column_titles(ilist) == NULL )
    {
	return;
    }

    for( i = 0; i < XmI18List_num_columns(ilist); ++i )
    {
	XmStringFree(XmI18List_column_titles(ilist)[i]);
    }
    XtFree((XtPointer) XmI18List_column_titles(ilist));
    XmI18List_column_titles(ilist) = (XmStringTable) NULL;
}

/*  Function Name: GetSelectedRows
 *  Description:   Creates an array of integers which are selected row #'s
 *  Arguments:     i18list - the extended list widget
 *		   rows - pointer to the array of selected row #'s
 *		   num_rows - pointer to the number of selected rows
 *  Returns:       rows
 */
static int *
GetSelectedRows(XmI18ListWidget i18list, int *num_rows)
{
    XmMultiListRowInfo	*row_info = XmI18List_row_data(i18list);
    int		*rows, i;

    rows = NULL;

    for (*num_rows = 0, i = 0; i < XmI18List_num_rows(i18list); i++)
	if (row_info[i].selected)
	    (*num_rows)++;

    if (*num_rows > 0)
    {
	int	j;

	rows = (int *) XtMalloc(*num_rows * sizeof(int));

	for (i = 0, j = 0; i < XmI18List_num_rows(i18list); i++)
	    if (row_info[i].selected)
		rows[j++] = i;
    }

    return rows;
}

/*  Function Name: SelectRow
 *  Description:   Set selection state of row
 *  Arguments:     i18list - the extended list widget
 *		   row - the row to select/unselect
 *		   select - True/False
 *		   notify - if True, call XmNsingleSelectionCallback
 *  Returns:       none
 */
static void
SelectRow(XmI18ListWidget i18list, int row,
	  Boolean select, Boolean notify)
{
    XmMultiListRowInfo	*rows = XmI18List_row_data(i18list);

    if ((row >= 0) && (rows[row].selected != select))
    {
	ToggleRow((Widget) i18list, row);
    }

    /* Call the appropriate notification callbacks...
     */
    if (notify)
	Notify((Widget) i18list, False);
}

/*  Function Name: SelectItems
 *  Description:   Set selection state by matching column entries to XmString
 *  Arguments:     i18list - the extended list widget
 *		   item - XmString to use as selection key (NULL matches all)
 *		   column - column number (0 - N) to match (or XmANY_COLUMN)
 *		   select - True/False whether or not to select matching rows
 *		   notify - if True, call XmNsingleSelectionCallback
 *  Returns:       none
 */
static void
SelectItems(XmI18ListWidget i18list, XmString item,
	    int column, Boolean select, Boolean notify)
{
    XmMultiListRowInfo	*rows = XmI18List_row_data(i18list);
    int	i, j;
    int	rowcount, colcount, colstart;

    _XmWidgetToAppContext((Widget)i18list);
    _XmAppLock(app);

    colstart = (XmI18List_first_col_pixmaps(i18list) ? 1 : 0);

    rowcount = XmI18List_num_rows(i18list);
    colcount = XmI18List_num_columns(i18list);

    for (i = 0; i < rowcount; i++)
	for (j = colstart; j < colcount; j++)
	    /* Check indiviual column entries against XmString, item...
	     * If XmString item matches column etnry (or item is NULL), then
	     * set the selection state of the row
	     */
	    if ((((column == XmANY_COLUMN) || (column == j)) &&
		 XmStringCompare(item, rows[i].values[j])) || !item)
	    {
		if (rows[i].selected != select)
		{
		    ToggleRow((Widget) i18list, i);
		}

		/* Call the appropriate notification callbacks...
		 */
		if (notify)
		    Notify((Widget) i18list, False);

		break;
	    }
    _XmAppUnlock(app);
}

/*
 * XmRCallProc routine for checking font_list before setting it to NULL
 * If "check_set_render_table" is True, then function has 
 * been called twice on same widget, thus resource needs to be set NULL, 
 * otherwise leave it alone.
 */

/*ARGSUSED*/
static void 
CheckSetRenderTable(Widget wid, int offset, XrmValue *value)
{
  XmI18ListWidget il = (XmI18ListWidget)wid;

  /* Check if been here before */
  if (il->ilist.check_set_render_table)
      value->addr = NULL;
  else {
      il->ilist.check_set_render_table = True;
      value->addr = (char*)&(il->ilist.font_list);
  }
}

/***************************************************************************
 *									   *
 * ListConvert - Convert routine for dragNDrop.				   *
 *									   *
 ***************************************************************************/

/*ARGSUSED*/
static void
ListConvert(Widget w, XtPointer client_data,
	    XmConvertCallbackStruct *cs)
{
    enum { XmA_MOTIF_COMPOUND_STRING, XmACOMPOUND_TEXT, XmATEXT,
	   XmATARGETS, XmA_MOTIF_DROP, XmA_MOTIF_LOSE_SELECTION,
	   XmA_MOTIF_EXPORT_TARGETS, XmA_MOTIF_CLIPBOARD_TARGETS,
	   XmAUTF8_STRING,
	   NUM_ATOMS };
    static char *atom_names[] = { 
	   XmS_MOTIF_COMPOUND_STRING, XmSCOMPOUND_TEXT, XmSTEXT,
	   XmSTARGETS, XmS_MOTIF_DROP, XmS_MOTIF_LOSE_SELECTION,
	   XmS_MOTIF_EXPORT_TARGETS, XmS_MOTIF_CLIPBOARD_TARGETS,
	   XmSUTF8_STRING };

    Atom atoms[XtNumber(atom_names)];
    Atom C_ENCODING = XmeGetEncodingAtom(w);
    int target_count = 0;
    int i;
    Atom type = None;
    XtPointer value = NULL;
    unsigned long size = 0;
    int format = 8;
    XmI18ListWidget lw = (XmI18ListWidget) w;
    XmI18ListDragConvertStruct *ListDragConv = lw->ilist.drag_conv;

    assert(XtNumber(atom_names) == NUM_ATOMS);
    XInternAtoms(XtDisplay(w), atom_names, XtNumber(atom_names), False, atoms);

    if (cs->target == atoms[XmATARGETS])
    {
	Atom *targs = XmeStandardTargets(w, 5, &target_count);

	value = (XtPointer) targs;
	targs[target_count++] = atoms[XmA_MOTIF_COMPOUND_STRING];
	targs[target_count++] = atoms[XmACOMPOUND_TEXT];
	targs[target_count++] = atoms[XmATEXT];
	targs[target_count++] = atoms[XmAUTF8_STRING];
	targs[target_count++] = C_ENCODING;
	if (XA_STRING != C_ENCODING)
	    targs[target_count++] = XA_STRING;
	if (ListDragConv->pixmap != None)
	    targs[target_count++] = XA_PIXMAP;
	type = XA_ATOM;
	size = target_count;
	format = 32;
    }
    else if ((cs->target == atoms[XmA_MOTIF_EXPORT_TARGETS]) ||
	     (cs->target == atoms[XmA_MOTIF_CLIPBOARD_TARGETS]))
    {
	Atom *targs = (Atom *) XtMalloc(sizeof(Atom) * 5);
	int n = 0;

	value = (XtPointer) targs;
	targs[n++] = atoms[XmA_MOTIF_COMPOUND_STRING];
	targs[n++] = atoms[XmACOMPOUND_TEXT];
	targs[n++] = atoms[XmATEXT];
	targs[n++] = C_ENCODING;
	if (XA_STRING != C_ENCODING)
	    targs[n++] = XA_STRING;
	if (ListDragConv->pixmap != None)
	    targs[n++] = XA_PIXMAP;
	format = 32;
	size = n;
	type = XA_ATOM;
	cs->status = XmCONVERT_DONE;
    }
    else if (cs->target == atoms[XmACOMPOUND_TEXT] ||
	   cs->target == atoms[XmA_MOTIF_COMPOUND_STRING] ||
	   cs->target == XA_STRING ||
	   cs->target == C_ENCODING ||
	   cs->target == atoms[XmATEXT] ||
	   cs->target == atoms[XmAUTF8_STRING])
    {
	XmString concat;
	XmString sep = XmStringSeparatorCreate();

	format = 8;

	if (cs->selection == atoms[XmA_MOTIF_DROP])
	{
	    int itemcount = ListDragConv->num_items;
	    XmString *items = ListDragConv->strings;

	    concat = (itemcount ? XmStringCopy(items[0]) : NULL);
	    for (i = 1; i < itemcount; i++) {
		concat = XmStringConcatAndFree(concat, XmStringCopy(sep));
		concat = XmStringConcatAndFree(concat, XmStringCopy(items[i]));
	    }
	}
	else
	{
	    Cardinal row;
	    int rowcount;
	    int * rows = GetSelectedRows(lw, &rowcount);
	    concat = NULL;
	    for (row = 0; row < rowcount; row++) {
		if (concat) {
		    concat = XmStringConcatAndFree(concat,
			    XmStringCopy(GetConcatenatedRow(w, rows[row])));
		} else {
		    concat = GetConcatenatedRow(w, rows[row]);
		}
		if (row < rowcount-1) {
		    concat = XmStringConcatAndFree(concat,
			XmStringCopy(sep));
		}
	    }
	    XtFree((XtPointer)rows);
	}

	if (cs->target == atoms[XmACOMPOUND_TEXT] ||
		cs->target == C_ENCODING ||
		cs->target == XA_STRING ||
		cs->target == atoms[XmATEXT])
	{
	    if (concat != NULL)
		value = XmCvtXmStringToCT(concat);
	    else
		value = NULL;

	    type = atoms[XmACOMPOUND_TEXT];

	    if (value != NULL)
		size = strlen((char*) value);
	    else
		size = 0;

	    if (cs->target == XA_STRING)
	    {
		XTextProperty tmp_prop;
		int ret_status;

		/* convert value to 8859.1 */
		ret_status = XmbTextListToTextProperty(XtDisplay(w),
						     (char**) &value, 1,
						     (XICCEncodingStyle)
						     XStringStyle,
						     &tmp_prop);
		XtFree((char*) value);
		if (ret_status == Success || ret_status > 0)
		{
		    value = (XtPointer) tmp_prop.value;
		    type = XA_STRING;
		    size = tmp_prop.nitems;
		}
		else
		{
		    value = NULL;
		    size = 0;
		}

	      /* If the target was TEXT then try and convert it.  If
	       * fully converted then we'll pass it back in locale
	       * text.  For locale text requests,  always pass
	       * back the converted text */
	    }
	    else if ((cs->target == atoms[XmATEXT] || cs->target == C_ENCODING)
		    && (value != NULL))
	    {
		char *cvt;
		Boolean success;
		cvt = _XmTextToLocaleText(w, value, type, format, size,
			&success);
		if ((cvt != NULL && success) || cs->target == C_ENCODING)
		{
		    if (! success && cvt != NULL)
			    cs->flags |= XmCONVERTING_PARTIAL;
		    XtFree((char*) value);
		    value = cvt;
		    type = C_ENCODING;
		}
	    }
	}
#ifdef UTF8_SUPPORTED
	else if (cs->target == atoms[XmAUTF8_STRING])
	{
	    type = atoms[XmAUTF8_STRING];
	    value = XmCvtXmStringToUTF8String(concat);
	    if (value != NULL)
	        size = strlen((char*) value);
	    else
	        size = 0;
	}
#endif
	else
	{
	    size = XmCvtXmStringToByteStream(concat, (unsigned char**) &value);
	    type = atoms[XmA_MOTIF_COMPOUND_STRING];
	}

	XmStringFree(concat);
	XmStringFree(sep);
    }
    else if (cs->target == atoms[XmA_MOTIF_LOSE_SELECTION])
    {
	/* Deselect everything in the list since we lost
	   the primary selection. */
	Xm18IListUnselectAllItems(w);
    }
    else if (cs->target == XA_PIXMAP)
    {
	/* Get row's pixmap */
	Pixmap *pix;
      
	pix = (Pixmap *) XtMalloc(sizeof(Pixmap));
	*pix = ListDragConv->pixmap;
	/* value, type, size, and format must be set */
	value = (XtPointer) pix;
	type = XA_DRAWABLE;
	size = 1;
	format = 32;
    }

  _XmConvertComplete(w, value, size, format, type, cs);
}

/*ARGSUSED*/
static void
ListPreDestProc(Widget w,
		XtPointer ignore, /* unused */
		XmDestinationCallbackStruct *cs)
{
  XmDropProcCallbackStruct *ds;
  Atom XA_MOTIF_DROP = XInternAtom(XtDisplay(w), XmS_MOTIF_DROP, False);
  int index;
  short row, col;

  if (cs->selection != XA_MOTIF_DROP) return;

  /* If this is the result of a drop,  we can fill in location_data with
   * the apparent site */
  ds = (XmDropProcCallbackStruct *) cs->destination_data;

  CvtPositionToRowColumn(w, ds->x, ds->y, &row, &col);

  cs->location_data = (XtPointer) (long) row;
}

/*
 * Function:
 *	GetConcatenatedRow(ilist)
 * Description:
 *	Helper fucntion for UTM operations. Returns joined collumns of a row,
 *      with tabs between column values.
 * Input:
 *	w : Widget - the widget to copy titles
 *    row : XmMultiListRowInfo - the row to concatenate
 * Output:
 *	XmString - the concatenated string.
 */

static XmString
GetConcatenatedRow(Widget w, int row)
{
    XmString result = NULL;
    XmString temp_string;
    XmString tab = XmStringComponentCreate(XmSTRING_COMPONENT_TAB, 0, NULL);
    XmI18ListWidget lw = (XmI18ListWidget)w;
    short i;
    
    for (i = 0; i < lw->ilist.num_columns; i++) {
	if (lw->ilist.row_data[row].values[i]) {
	    if (result) {
		temp_string = XmStringConcat(tab,
			lw->ilist.row_data[row].values[i]);
		result = XmStringConcatAndFree(result, temp_string);
	    } else {
		result = XmStringCopy(lw->ilist.row_data[row].values[i]);
	    }
	}
    }
    XmStringFree(tab);
    return result;
}

/***************************************************************************
 *									   *
 * ProcessDrag - drag the selected items				   *
 *									   *
 ***************************************************************************/

/*ARGSUSED*/
static void
ProcessDrag(Widget wid,
	    XEvent *event,
	    String *params,		/* unused */
	    Cardinal *num_params)	/* unused */
{
    XmI18ListWidget lw = (XmI18ListWidget) wid;
    register int i;
    short row, col;
    Widget drag_icon, dc;
    Arg args[10];
    int n, location_data;
    XmI18ListDragConvertStruct *ListDragConv; 
    XmString temp_string = NULL;
    XmString temp_string2 = NULL;
    XmString tab;

    /* Don't allow multi-button drags. */
    if (event->xbutton.state &
	    ~((Button1Mask >> 1) << event->xbutton.button) &
	    (Button1Mask | Button2Mask | Button3Mask | Button4Mask |
	    Button5Mask))
	return;

    CvtPositionToRowColumn(wid, event->xbutton.x, event->xbutton.y, &row, &col);
    
    if (col < 0 || row >= lw->ilist.num_rows || col >= lw->ilist.num_columns)
	return;

    location_data = row;
    
    lw->ilist.drag_conv = ListDragConv = (XmI18ListDragConvertStruct *)
	    XtMalloc(sizeof(XmI18ListDragConvertStruct));

    ListDragConv->w = wid;
    ListDragConv->strings = NULL;
    ListDragConv->pixmap = None;
    ListDragConv->num_items = 0;
    
    if (col == 0 && lw->ilist.first_col_pixmaps && row >= 0) {
    	ListDragConv->num_items = 1;
	ListDragConv->pixmap = lw->ilist.row_data[row].pixmap;
    }
    
    if (row >= 0) {
	if (lw->ilist.row_data[row].selected)
	{
	    int rowcount;
	    int * rows = GetSelectedRows(lw, &rowcount);
	    ListDragConv->num_items = rowcount;
	    ListDragConv->strings =	(XmString *)
		    XtMalloc(sizeof(XmString) * ListDragConv->num_items);
	    for (i = 0; i < rowcount; i++)
		ListDragConv->strings[i] = GetConcatenatedRow(wid, rows[i]);
	    XtFree((XtPointer)rows);
	}
	else
	{
	    ListDragConv->strings = (XmString *) XtMalloc(sizeof(XmString));
	    ListDragConv->num_items = 1;
	    ListDragConv->strings[0] = GetConcatenatedRow(wid, row);
	}
    } else if (row == -2 && lw->ilist.column_titles) { /* drag column header */
	ListDragConv->strings = (XmString *) XtMalloc(sizeof(XmString));
	ListDragConv->num_items = 1;
	ListDragConv->strings[0] = lw->ilist.column_titles[col];
    } else {
	return;
    }

    /* OK, now start the drag... */
    drag_icon = XmeGetTextualDragIcon(wid);

    n = 0;
    XtSetArg(args[n], XmNcursorForeground, lw->primitive.foreground), n++;
    XtSetArg(args[n], XmNcursorBackground, lw->core.background_pixel), n++;
    XtSetArg(args[n], XmNsourceCursorIcon, drag_icon), n++;
    XtSetArg(args[n], XmNdragOperations, XmDROP_COPY), n++;
    dc = XmeDragSource(wid, (XtPointer) (long) location_data, event, args, n);

    if (dc)
	XtAddCallback(dc, XmNdragDropFinishCallback, DragDropFinished, lw);
    else
	DragDropFinished(dc, lw, NULL);
}

/***************************************************************************
 *									   *
 * CopyToClipboard - copy the current selected items to the clipboard.     *
 *									   *
 *	This is a *sloow* process...					   *
 *									   *
 ***************************************************************************/

/*ARGSUSED*/
static void
CopyToClipboard(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    XmI18ListWidget lw = (XmI18ListWidget) w;
    int rowcount;
    
    /* text to the clipboard */
    (void)GetSelectedRows(lw, &rowcount);
    if (rowcount > 0)
	(void) XmeClipboardSource(w, XmCOPY, 0);
}

/*ARGSUSED*/
static void
DragDropFinished(Widget w,		/* unused */
		 XtPointer closure,
		 XtPointer call_data)	/* unused */
{
    int i;
    XmI18ListWidget lw = (XmI18ListWidget)closure;
    XmI18ListDragConvertStruct *ListDragConv = lw->ilist.drag_conv;

    for (i = 0; i < ListDragConv->num_items; i++)
	XmStringFree(ListDragConv->strings[i]);

    XtFree((char *) ListDragConv->strings);
    XtFree((char *) ListDragConv);
}

/*  Function Name: XmI18ListSelectItems
 *  Description:   Set selection state by matching column entries to XmString
 *  Arguments:     i18list - the extended list widget
 *		   item - XmString to use as selection key
 *		   column - column number (0 - N) to match (or XmANY_COLUMN)
 *		   notify - if True, call XmNsingleSelectionCallback
 *  Returns:       none
 */
void
XmI18ListSelectItems(XmI18ListWidget i18list,
		     XmString item, int column, Boolean notify)
{
    SelectItems(i18list, item, column, True, notify);
}

/*  Function Name: XmI18ListDeselectItems
 *  Description:   Set selection state by matching column entries to XmString
 *  Arguments:     i18list - the extended list widget
 *		   item - XmString to use as selection key
 *		   column - column number (0 - N) to match (or XmANY_COLUMN)
 *  Returns:       none
 */
void
XmI18ListDeselectItems(XmI18ListWidget i18list,
		      XmString item, int column)
{
    SelectItems(i18list, item, column, False, False);
}

/*  Function Name: XmI18ListSelectAllItems
 *  Description:   Set selection state by matching column entries to XmString
 *  Arguments:     i18list - the extended list widget
 *		   item - XmString to use as selection key
 *		   column - column number (0 - N) to match (or XmANY_COLUMN)
 *		   notify - if True, call XmNsingleSelectionCallback
 *  Returns:       none
 */
void
XmI18ListSelectAllItems(XmI18ListWidget i18list, Boolean notify)
{
    SelectItems(i18list, NULL, XmANY_COLUMN, True, notify);
}

/*  Function Name: XmI18SelectRow
 *  Description:   Set selection state of row
 *  Arguments:     i18list - the extended list widget
 *		   row - the row to select
 *		   notify - if True, call XmNsingleSelectionCallback
 *  Returns:       none
 */
void
XmI18ListSelectRow(XmI18ListWidget i18list, int row, Boolean notify)
{
    SelectRow(i18list, row, True, notify);
}

/*  Function Name: XmI18DeselectRow
 *  Description:   Set selection state of row
 *  Arguments:     i18list - the extended list widget
 *		   row - the row to deselect
 *  Returns:       none
 */
void
XmI18ListDeselectRow(XmI18ListWidget i18list, int row)
{
    SelectRow(i18list, row, False, False);
}

/*  Function Name: XmI18ListGetSelectedRowArray
 *  Description:   Creates an array of integers which are selected row #'s
 *  Arguments:     i18list - the extended list widget
 *		   rows - pointer to the array of selected row #'s
 *		   num_rows - pointer to the number of selected rows
 *  Returns:       selected rows array
 */
int *
XmI18ListGetSelectedRowArray(XmI18ListWidget i18list,
			     int *num_rows)
{
    return GetSelectedRows(i18list, num_rows);
}

/*  Function Name: XmI18ListMakeRowVisible
 *  Description:   Shifts the visible extended list rows as desired
 *  Arguments:     i18list - the extended list widget
 *		   row - the row number wished to be made visible
 *  Returns:       none
 */
void
XmI18ListMakeRowVisible(XmI18ListWidget i18list, int row)
{
    /* Use column zero as a rule when making a row visible... */
    MakeCellVisible((Widget) i18list, row, 0);
}
