/* $XConsortium: list.c /main/5 1995/07/15 20:46:14 drk $ */
/* 
 *  @OPENGROUP_COPYRIGHT@
 *  COPYRIGHT NOTICE
 *  Copyright (c) 1990, 1991, 1992, 1993 Open Software Foundation, Inc.
 *  Copyright (c) 1996, 1997, 1998, 1999, 2000 The Open Group
 *  ALL RIGHTS RESERVED (MOTIF). See the file named COPYRIGHT.MOTIF for
 *  the full copyright text.
 *  
 *  This software is subject to an open license. It may only be
 *  used on, with or for operating systems which are themselves open
 *  source systems. You must contact The Open Group for a license
 *  allowing distribution and sublicensing of this software on, with,
 *  or for operating systems which are not Open Source programs.
 *  
 *  See http://www.opengroup.org/openmotif/license for full
 *  details of the license agreement. Any use, reproduction, or
 *  distribution of the program constitutes recipient's acceptance of
 *  this agreement.
 *  
 *  EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS
 *  PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
 *  WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY
 *  OR FITNESS FOR A PARTICULAR PURPOSE
 *  
 *  EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT
 *  NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 *  DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED
 *  AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 *  ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE
 *  EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGES.
 */ 
/*
 * HISTORY
 */

#include <Xm/XmP.h>
#include <Xm/CSText.h>
#include <Xm/TransferP.h>
#include <errno.h>
#include "transfer.h"

#define MAXT 100
#define DEFSTR "<Deferred>"

extern TransferDataRec datums[];
extern unsigned int num_datums;

static XmString GetItemValue(Widget);
static char GetDisplayFormatFromType(Widget, Atom);
static void setFormatAndMenu(char);
static void SetDisplayTypeFromType(Widget, Atom);

static Boolean global_set_defer = False;
static Atom global_set_type_spec = None;
static char global_read_format_spec = 'S';
static XtPointer global_value = NULL;
static Atom global_type = None;
static unsigned long global_length = 0;
static int global_format = 8;
static Boolean global_deferred = False;
static int current_selected_item = -1;

static Atom type = None;

void 
SetType(Widget w, char *in, XtPointer ignore)
{
  if (strlen(in) == 0)
    global_set_type_spec = XmeGetEncodingAtom(w);
  else if (strcmp(in, "NONE") == 0)
    global_set_type_spec = None;
  else
    global_set_type_spec = XInternAtom(XtDisplay(w), in, False);
}

/*******************************************************************
 * FindRightButton
 * 
 * picks the correct button for the type option menu to display
 * note that the names are ATOMNAME_button,  so it prepends a 
 * '*' in front and '_button' after the name before looking up
 * the button.
 ******************************************************************/

static Widget 
FindRightButton(char* atom_string)
{
  char buffer[80];

  strcpy(buffer, "*b_");
  strcat(buffer, atom_string);

  return(XtNameToWidget(toplevel, buffer));
}

/*********************************************************************
 * ListSelectItem
 * 
 * this is called when the user selects an item in the list.  It takes
 * the type of the item and decides how to display it in the CSText
 * widget
 *********************************************************************/

void 
ListSelectItem(Widget w, XtPointer ignore, XmListCallbackStruct *listcb)
{
  int index = listcb -> item_position;
  Atom XA_COMPOUND_TEXT = XInternAtom(XtDisplay(w), XmSCOMPOUND_TEXT, False),
       XA_PIXEL = XInternAtom(XtDisplay(w), "PIXEL", False),
       XA_LOCALE_TEXT = XmeGetEncodingAtom(w),
       XA_MOTIF_CS = XInternAtom(XtDisplay(w), 
				 XmS_MOTIF_COMPOUND_STRING, False),
       XA_MOTIF_RT = XInternAtom(XtDisplay(w), 
				 XmS_MOTIF_RENDER_TABLE, False);
  Widget mem = (Widget) NULL;
  char *targetname, *typename;
  XmString output, tmp;

  if (index < 1 || index > num_datums) return;

  index --;

  current_selected_item = index;
  global_type = type = datums[index].type;
  global_value = datums[index].value;
  global_length = datums[index].length;
  global_format = datums[index].format;
  global_deferred = datums[index].deferred;

  /* We create a big string and then place the string into
     the display field */

  /* Target */
  /* output = XmStringGenerate("Target: \t", NULL, NULL, NULL); */
  output = XmStringCreateLocalized("Target: ");
  targetname = GetSafeAtom(XtDisplay(w), datums[index].target);
  if (targetname == NULL) targetname = XtNewString("Unknown");
  tmp = XmStringCreateLocalized(targetname);
  XtFree(targetname);
  output = XmStringConcatAndFree(output, tmp);
  output = XmStringConcatAndFree(output, XmStringSeparatorCreate());

  /* Type */
  /* tmp = XmStringGenerate("Type: \t", NULL, NULL, NULL); */
  tmp = XmStringCreateLocalized("Type: ");
  output = XmStringConcatAndFree(output, tmp);
  typename = GetSafeAtom(XtDisplay(w), datums[index].type);
  if (typename == NULL) typename = XtNewString("Unknown");
  tmp = XmStringCreateLocalized(typename);
  XtFree(typename);
  output = XmStringConcatAndFree(output, tmp);
  output = XmStringConcatAndFree(output, XmStringSeparatorCreate());

  /* Value display */
  /* tmp = XmStringGenerate("Value: \t", NULL, NULL, NULL); */
  tmp = XmStringCreateLocalized("Value: ");
  output = XmStringConcatAndFree(output, tmp);
  tmp = GetItemValue(text_display);
  output = XmStringConcatAndFree(output, tmp);

  /* Put value into display */
  XmCSTextSetString(text_display, output);
  XmStringFree(output);
}

/*********************************************************************
 * EditItem
 * 
 * This is called from the Properties or New menu item.  If param
 * is 0 then this is a new item.
 *********************************************************************/

void 
EditItem(Widget w, int param, XtPointer ignore)
{
  int index = current_selected_item;
  Atom XA_COMPOUND_TEXT = XInternAtom(XtDisplay(w), XmSCOMPOUND_TEXT, False),
       XA_PIXEL = XInternAtom(XtDisplay(w), "PIXEL", False),
       XA_LOCALE_TEXT = XmeGetEncodingAtom(w),
       XA_MOTIF_CS = XInternAtom(XtDisplay(w), 
				 XmS_MOTIF_COMPOUND_STRING, False),
       XA_MOTIF_RT = XInternAtom(XtDisplay(w), 
				 XmS_MOTIF_RENDER_TABLE, False);
  Widget mem = (Widget) NULL;
  char *targetname;
  XmString tmp;

  if (index >= num_datums) return;

  global_type = type = datums[index].type;
  global_value = datums[index].value;
  global_length = datums[index].length;
  global_format = datums[index].format;
  global_deferred = datums[index].deferred;
  global_set_defer = global_deferred;
  global_set_type_spec = global_type;
  global_read_format_spec = 'S';

  if (param != 0) {
    /* Put name of target into display field */
    targetname = GetSafeAtom(XtDisplay(w), datums[index].target);
    
    if (targetname == NULL) targetname = XtNewString("Unknown");

    tmp = XmStringCreateLocalized(targetname);
    XmCSTextSetString(text2, tmp);
    XmStringFree(tmp);
    tmp = GetItemValue(text1);
    XmCSTextSetString(text1, tmp);
    XmStringFree(tmp);
    XtFree(targetname);
    SetDisplayTypeFromType(text1, global_type);
    global_read_format_spec = GetDisplayFormatFromType(text1, global_type);
    setFormatAndMenu(global_read_format_spec);
    XmToggleButtonGadgetSetState(defer, global_deferred, False);
  } else {
    tmp = XmStringCreateLocalized("");
    XmCSTextSetString(text1, tmp);
    XmCSTextSetString(text2, tmp);
    SetDisplayTypeFromType(text1, XA_STRING);
    setFormatAndMenu('S');
    XmToggleButtonGadgetSetState(defer, False, False);
  }

}

static char 
GetDisplayFormatFromType(Widget w, Atom type)
{
  Atom XA_COMPOUND_TEXT = XInternAtom(XtDisplay(w), XmSCOMPOUND_TEXT, False),
       XA_PIXEL = XInternAtom(XtDisplay(w), "PIXEL", False),
       XA_LOCALE_TEXT = XmeGetEncodingAtom(w),
       XA_MOTIF_CS = XInternAtom(XtDisplay(w), 
				 XmS_MOTIF_COMPOUND_STRING, False),
       XA_MOTIF_RT = XInternAtom(XtDisplay(w), 
				 XmS_MOTIF_RENDER_TABLE, False);

  if (type == XA_ATOM ||
      type == XInternAtom(XtDisplay(w), "ATOM_PAIR", False) ||
      type == XA_INTEGER || type == XA_PIXEL ||
      type == XInternAtom(XtDisplay(w), "SPAN", False)) {
    return('D');
  } else if (type == XA_STRING) {
    return('S');
  } else if (type == XA_COMPOUND_TEXT) {
    return('T');
  } else if (type == XA_LOCALE_TEXT) {
    return('L');
  } else if (type == XA_DRAWABLE || type == XA_WINDOW ||
	     type == XA_COLORMAP) {
    return('H');
  } else if (type == XA_MOTIF_CS) {
    return('C');
  } else if (type == XmeGetEncodingAtom(w)) {
    return('L');
  } else {
    return('H');
  }
}

static void
SetDisplayTypeFromType(Widget w, Atom type)
{
  Atom XA_COMPOUND_TEXT = XInternAtom(XtDisplay(w), XmSCOMPOUND_TEXT, False),
       XA_PIXEL = XInternAtom(XtDisplay(w), "PIXEL", False),
       XA_LOCALE_TEXT = XmeGetEncodingAtom(w),
       XA_MOTIF_CS = XInternAtom(XtDisplay(w), 
				 XmS_MOTIF_COMPOUND_STRING, False),
       XA_MOTIF_RT = XInternAtom(XtDisplay(w), 
				 XmS_MOTIF_RENDER_TABLE, False);
  Widget mem;

  if (type == XA_ATOM ||
      type == XInternAtom(XtDisplay(w), "ATOM_PAIR", False)) {
    char str[80];
    mem = FindRightButton("ATOM");
  } else if (type == XA_INTEGER || type == XA_PIXEL) {
    if (type == XA_INTEGER)
      mem = FindRightButton("INTEGER");
    else
      mem = FindRightButton("PIXEL");
  } else if (type == XInternAtom(XtDisplay(w), "SPAN", False)) {
    mem = FindRightButton("SPAN");
  } else if (type == XA_STRING) {
    mem = FindRightButton("STRING");
  } else if (type == XA_COMPOUND_TEXT) {
    char *str;
    mem = FindRightButton("COMPOUND_TEXT");
  } else if (type == XA_LOCALE_TEXT) {
    mem = FindRightButton("LOCALE_TEXT");
  } else if (type == XA_DRAWABLE || type == XA_WINDOW ||
	     type == XA_COLORMAP) {
    if (type == XA_DRAWABLE)
      mem = FindRightButton("DRAWABLE");
    else if (type == XA_COLORMAP)
      mem = FindRightButton("COLORMAP");
    else
      mem = FindRightButton("WINDOW");
  } else if (type == XA_MOTIF_CS) {
    mem = FindRightButton("_MOTIF_COMPOUND_STRING");
  } else if (type == XmeGetEncodingAtom(w)) {
    mem = FindRightButton("LOCALE");
  } else {
    char *typestring;
    char buffer[80];

    mem = FindRightButton("unknown");

    typestring = GetSafeAtom(XtDisplay(w), type);
    sprintf(buffer, "Unknown type %s (%d) found", typestring, type);
    XFree(typestring);
  }

  if (mem != (Widget) NULL)
    XtVaSetValues(typeOM, XmNmenuHistory, mem, NULL);
}

static void
setFormatAndMenu(char in)
{
  Widget mem = (Widget) NULL;

  if (in == 'S') {
    /* C String display */
    mem = format_s;
  } else if (in == 'L') {
    /* Locale Text display */
    mem = format_l;
  } else if (in == 'T') {
    /* Compound Text display */
    mem = format_t;
  } else if (in == 'C') {
    /* Compound String display */
    mem = format_cs;
  } else if (in == 'D') {
    /* Decimal display */
    mem = format_id;
  } else {
    /* Hex display */
    mem = format_ih;
  }

  SetFormat(NULL, in, NULL);

  if (mem != (Widget) NULL)
    XtVaSetValues(format_om, XmNmenuHistory, mem, NULL);
}

char *output_buffer = NULL;
int output_buffer_max = 0;
int output_buffer_current = 0;

void ClearOB() 
{
  output_buffer_current = 0;
  if (output_buffer_max > 0)
    output_buffer[0] = 0;
}

void 
AppendOB(char *str)
{
  int len;
  int i;

  len = strlen(str);
  while(len + output_buffer_current > output_buffer_max) {
    if (output_buffer_max == 0) {
      output_buffer_max = 256;
      output_buffer = XtMalloc(output_buffer_max);
    } else {
      output_buffer_max *= 2;
      output_buffer = XtRealloc(output_buffer, output_buffer_max);
    }
  }

  /* Copy data */
  for(i = 0; i <= len; i++)
    output_buffer[output_buffer_current + i] = str[i];

  /* Update end pointer */
  output_buffer_current += len;
}

void 
SetFormat(Widget w, char in, XtPointer ignore)
{
  int byte_length;
  XmString strtmp;

  global_read_format_spec = in;
}

static 
XmString GetItemValue(Widget w)
{
  int byte_length;
  XmString strtmp;
  char in;

  byte_length = global_length * global_format / 8;

  in = GetDisplayFormatFromType(w, global_type);

  if (in == 'S') {
    /* C String display */
    strtmp = XmStringCreateLocalized(global_value);
  } else if (in == 'L') {
    /* Locale Text display */
    strtmp = XmStringCreateLocalized(global_value);
  } else if (in == 'T') {
    /* Compound Text display */
    strtmp = XmCvtCTToXmString(global_value);
  } else if (in == 'C') {
    /* Compound String display */
    strtmp = XmCvtByteStreamToXmString(global_value);
  } else if (in == 'D') {
    /* Decimal display */
    int count, i;

    count = byte_length / 4; 
    ClearOB();
    for(i = 0; i < count; i++) {
      char buf[32];

      sprintf(buf, "%d ", ((int *) global_value)[i]);
      AppendOB(buf);
    }
    strtmp = XmStringCreateLocalized(output_buffer);
  } else {
    /* Decimal display */
    int count, i;

    count = byte_length / 4; 
    ClearOB();
    for(i = 0; i < count; i++) {
      char buf[32];

      sprintf(buf, "0x%x ", ((int *) global_value)[i]);
      AppendOB(buf);
    }
    strtmp = XmStringCreateLocalized(output_buffer);
  }

  return(strtmp);
}

/*******************************************************************
 * SetValue
 *
 * No,  it's not a widget method.  This SetValue implements an action
 * which takes the current value in the interface and stores it away
 * for that particular target.  This allows arbitrary data to be stored
 * into the data array for later transfer.
 *******************************************************************/
void
SetValue(Widget widget, char *ignore, XmAnyCallbackStruct *callback_data)
{
  XmString value;
  XmString target_name;
  char *type_widget_name;
  char *display_format_widget_name;
  char *target_string;
  Widget mem_history;
  Arg args[1];
  int index;
  int base;
  Atom target;
  
  target_name = XmCSTextGetString(text2);
  value = XmCSTextGetString(text1);
  
  if (global_set_type_spec == None) return;

  /* Get the target string from the XmString */
  target_string = ConvertXmStringToString(target_name);
  target = XInternAtom(XtDisplay(widget), target_string, False);
  XtFree(target_string);
  
  /* Find the index of this target,  or add it to the end */
  for(index = 0; index < num_datums; index++)
    if (datums[index].target == target) break;

  /* Add to the end,  if this is a new target */
  if (index == num_datums) {
    XmListAddItemUnselected(list, target_name, 0);
    datums[index].target = target;
    num_datums++;
  }

  /* Handle deferred */
  if (datums[index].deferred != global_set_defer) {
    XmString new_item[1];
    datums[index].deferred = global_set_defer;

    if (datums[index].deferred) {
      char *buffer;

      buffer = XtMalloc(strlen(target_string) + 2 + strlen(DEFSTR));
      strcpy(buffer, target_string);
      strcat(buffer, "\t");
      strcat(buffer, DEFSTR);
      new_item[0] = XmStringGenerate(buffer, NULL, XmCHARSET_TEXT, NULL);
      XtFree(buffer);
    } else
      new_item[0] = XmStringCopy(target_name);

    XmListReplaceItemsPos(list, new_item, 1, index + 1);
    XmListSelectPos(list, index + 1, False);
    XmStringFree(new_item[0]);
  }

  datums[index].type = global_set_type_spec;
  
  /* Convert the value */
  if (global_read_format_spec == 'S') {
    datums[index].value = ConvertXmStringToString(value);
    if (datums[index].value != NULL) {
      datums[index].length = strlen(datums[index].value);
    } else {
      datums[index].length = 0;
    }
    datums[index].format = 8;
  } else if (global_read_format_spec == 'L') {
    char *ctext;
    char *ltext = NULL;
    Atom COMPOUND_TEXT = XInternAtom(XtDisplay(widget), 
				     XmSCOMPOUND_TEXT, False);
    XTextProperty text_prop;
    int status;
    char ** values;
    int num_values = 0;
    int malloc_size = 0;
    int i;

    ctext = XmCvtXmStringToCT(value);

    text_prop.value = (unsigned char *) ctext;
    text_prop.encoding = COMPOUND_TEXT;
    text_prop.format = 8;
    text_prop.nitems = strlen(ctext);

    status = XmbTextPropertyToTextList(XtDisplay(widget), &text_prop, &values,
				       &num_values);

    XtFree(ctext);

    if (num_values) { 
      for (i = 0; i < num_values ; i++)
	malloc_size += strlen(values[i]);

      ltext = XtMalloc ((unsigned) malloc_size + 1);
      ltext[0] = '\0';
      for (i = 0; i < num_values ; i++)
	strcat(ltext, values[i]);
      XFreeStringList(values);
    } else {
      ltext = XtNewString("");
    }

    datums[index].value = ltext;
    datums[index].length = malloc_size;
    datums[index].format = 8;
  } else if (global_read_format_spec == 'T') {
    datums[index].value = XmCvtXmStringToCT(value);
    if (datums[index].value != NULL) {
      datums[index].length = strlen(datums[index].value);
    } else {
      datums[index].length = 0;
    }
    datums[index].format = 8;
  } else if (global_read_format_spec == 'C') {
    datums[index].length = 
      XmCvtXmStringToByteStream(value, 
				(unsigned char **) &datums[index].value);
    datums[index].format = 8;
  } else if (global_read_format_spec == 'D' ||
	     global_read_format_spec == 'H') {
    /* Doesn't matter,  the numbers contain the base information.
       We assume a string with whitespace separating the values. */
    int max_values = 5;
    long *array = (long *) XtMalloc(sizeof(long) * max_values);
    int count = 0;
    char *convert;
    char *current;
    char *end;
    Boolean done = False;
    
    convert = ConvertXmStringToString(value);
    current = convert;

    if (global_read_format_spec == 'H')
      base = 16;
    else
      base = 0;

    while(! done) {
      /* Realloc array if getting too big */
      if (count >= max_values) {
	max_values += 5;
	array = (long *) XtRealloc((char*) array, sizeof(long) * max_values);
      }
      if (current == 0) break;
      /* Now grab the next number */
      array[count++] = strtol(current, &end, base);
      if (current == end || errno == ERANGE) {
	done = True;
	count--;
      } else 
	current = end;
    }

    datums[index].value = (XtPointer) array;
    datums[index].length = count;
    datums[index].format = 32;
  }

  XmStringFree(target_name);
  XmStringFree(value);
  XmListSelectPos(list, index + 1, True);
}

/*******************************************************************
 * RemoveValue
 *
 * This removes an element from the data array.
 *******************************************************************/
void 
RemoveValue(Widget widget, char *ignore, XmAnyCallbackStruct *callback_data)
{
  if (current_selected_item < 0 ||
      current_selected_item >= num_datums) {
    XBell(XtDisplay(widget), 100);
  } else {
    int i;

    /* Free current value pointed at */
    XtFree(datums[current_selected_item].value);

    num_datums--;
    /* Remove item from internal list */
    for(i = current_selected_item; i < num_datums; i++)
      datums[i] = datums[i + 1];

    /* Remove from screen display */
    XmListDeletePos(list, current_selected_item + 1);
    /* Now update the display with the new value */
    if (current_selected_item < num_datums)
      XmListSelectPos(list, current_selected_item + 1, True);
    else if (num_datums > 0)
      XmListSelectPos(list, current_selected_item, True);
  }
}

void 
deferCB(Widget widget, char *ignore,
	XmToggleButtonCallbackStruct *callback_data)
{
  char *targetname;
  XmString new_item[1];

  global_set_defer = callback_data -> set;
}

XmParseMapping table[2];
int table_size = -1;

char*
ConvertXmStringToString(XmString xmstr)
{
  char *rvalue;

  if (table_size < 0) {
    XmString tmp;
    int nargs;
    Arg args[10];

    table_size = 0;
    tmp = XmStringComponentCreate(XmSTRING_COMPONENT_TAB, 0, NULL);
    nargs = 0;
    XtSetArg(args[nargs], XmNincludeStatus, XmINSERT), nargs++;
    XtSetArg(args[nargs], XmNsubstitute, tmp), 	 nargs++;
    XtSetArg(args[nargs], XmNpattern, "\t"), 		 nargs++;
    table[table_size++] = XmParseMappingCreate(args, nargs);
    XmStringFree(tmp);

    tmp = XmStringSeparatorCreate();
    nargs = 0;
    XtSetArg(args[nargs], XmNincludeStatus, XmINSERT), nargs++;
    XtSetArg(args[nargs], XmNsubstitute, tmp),	 nargs++;
    XtSetArg(args[nargs], XmNpattern, "\n"),		 nargs++;
    table[table_size++] = XmParseMappingCreate(args, nargs);

    XmStringFree(tmp);
  }

  rvalue = (char *) XmStringUnparse(xmstr, NULL, 
				    XmMULTIBYTE_TEXT, XmMULTIBYTE_TEXT,
				    table, table_size, XmOUTPUT_ALL);

  return(rvalue);
}
