/*
 * File:        wx_txt.cc
 * Purpose:     Panel items implementation (X version)
 * Author:      Julian Smart
 * Created:     1993
 * Updated:	April 1995
 * Copyright:   (c) 1995, AIAI, University of Edinburgh
 */

#ifdef __GNUG__
#pragma implementation
#pragma implementation "wx_txt.h"
#endif

#include <stdlib.h>
#include "common.h"
#include "wx_utils.h"
#include "wx_privt.h"
#include "wx_txt.h"

#include <X11/keysym.h>

#ifdef wx_motif
#include <Xm/Label.h>
#include <Xm/LabelG.h>
#include <Xm/Text.h>
#include <Xm/Form.h>

void 
wxTextModifyProc (Widget w, XtPointer clientData, XmTextVerifyCallbackStruct *cbs);
#endif

static Bool checkFunctionKey(wxPanel *panelPtr, wxItem *itemPtr,
		XEvent *event)
{
	if (event->type == KeyPress) {

		KeySym keySym;
		XComposeStatus compose;
		XLookupString((XKeyEvent *) event, wxBuffer, 20,
				&keySym, &compose);

		int id = CharCodeXToWX(keySym);

		wxKeyEvent wxevent(wxEVENT_TYPE_CHAR);
      
		if (event->xkey.state & ShiftMask)
			wxevent.shiftDown = TRUE;
		if (event->xkey.state & ControlMask)
			wxevent.controlDown = TRUE;
		wxevent.eventObject = panelPtr;
		wxevent.keyCode = id;
        
		if (IsCursorKey(keySym) || IsPFKey(keySym) ||
				IsFunctionKey(keySym) || IsMiscFunctionKey(keySym) ||
				IsKeypadKey(keySym)) {
			if (panelPtr->GetEventHandler()->OnFunctionKey(wxevent))
				return TRUE;
		}
		itemPtr->GetEventHandler()->OnChar(wxevent);
		return TRUE;
	}
	return FALSE;
}

#ifdef wx_xview
Panel_setting 
wxTextProc (Panel_item item, Event * x_event)
{
  Panel_setting ret_code = panel_text_notify (item, x_event);
  if (ret_code == PANEL_NONE)
    return (PANEL_NONE);
  // So, ret_code is PANEL_INSERT,PANEL_NEXT or PANEL_PREVIOUS
  wxText *text = (wxText *) xv_get (item, PANEL_CLIENT_DATA);
  int event_type;
  if (ret_code != PANEL_INSERT)
    event_type = wxEVENT_TYPE_TEXT_ENTER_COMMAND;
  else
    event_type = wxEVENT_TYPE_TEXT_COMMAND;

  wxCommandEvent event (event_type);
  event.commandString = (char *) xv_get (item, PANEL_VALUE);
  event.eventHandle = (char *) x_event;
  event.eventObject = text;
  text->ProcessCommand (event);
  return (Panel_setting) ret_code;
}

void (*old_event_proc)(Panel_item, Event *) = NULL;

#if 0
    Text_info	   *dp = TEXT_PRIVATE(item_public);
    int		    format;
    Frame	    frame;
    Item_info      *ip = ITEM_PRIVATE(item_public);
    long	    length;
    Xv_Notice	    notice;
    int		    own_primary_seln;
    Panel_info	   *panel = ip->panel;
    struct pr_size  size;
    int		    take_down_caret;
    int		    retstatus;
#endif

void wxTextEventProc(Panel_item item, Event *event)
{
    if (event_is_up(event)) {
    	(old_event_proc)(item, event);
	} else {
		Panel panel = (Panel) xv_get(item, XV_OWNER);
		wxPanel *panelPtr = (wxPanel *) xv_get(panel, WIN_CLIENT_DATA);

		Panel_item oldCurr = (Panel_item) xv_get(panel, PANEL_CARET_ITEM);
		wxText *oldTxt = NULL;
                if (oldCurr)
                  oldTxt = (wxText *) xv_get(oldCurr, PANEL_CLIENT_DATA);

                if (oldTxt)
                  checkFunctionKey(panelPtr, oldTxt, event->ie_xevent);

		// Try to detect keyboard focus changes between items
		(old_event_proc)(item, event);
		Panel_item newCurr = (Panel_item) xv_get(panel, PANEL_CARET_ITEM);

		if (oldCurr != newCurr) {

			// Detected a change in keyboard focus, notify the items
			// and their panel.
			wxText *newTxt = (wxText *) xv_get(newCurr, PANEL_CLIENT_DATA);
                        if (oldTxt)
                          oldTxt->GetEventHandler()->OnKillFocus();

			// current item may have changed again!
			newCurr = (Panel_item) xv_get(panel, PANEL_CARET_ITEM);
			if (oldCurr != newCurr) {
				newTxt = (wxText *) xv_get(newCurr, PANEL_CLIENT_DATA);
				newTxt->GetEventHandler()->OnSetFocus();
				panelPtr->GetEventHandler()->OnChangeFocus(oldTxt, newTxt);
			}
		}
	}
}
#endif


#ifdef wx_motif
void clientMsgHandler(Widget w, XtPointer cdata, XEvent *event)
{
	if (event->type != ClientMessage)
		return;

	// toDo: use memcpy to retrieve the pointers (using the '.b' part)
	wxItem *item1 = (wxItem *) event->xclient.data.l[0];
	wxItem *item2 = (wxItem *) event->xclient.data.l[1];
	wxPanel *panel = (wxPanel *) item1->GetParent();
	panel->GetEventHandler()->OnChangeFocus(item1, item2);
}

void keyPressHandler(Widget w, XtPointer cData, XEvent *event)
{
	wxText *item = (wxText *) cData;
	wxPanel *panel = (wxPanel *) item->GetParent();
	checkFunctionKey(panel, item, event);
}

void wxTextCallback(Widget w, XtPointer clientData,
                      XmAnyCallbackStruct *ptr)
{
  wxText *item = (wxText *)clientData;
  int type_event ;
  // sorry... but it's the only way we can get to the panel!
  wxPanel *panel = (wxPanel *) item->GetParent();
  if (panel->manualChange)
	return;

  switch (ptr->reason) {
  case XmCR_LOSING_FOCUS:
	panel->previousFocus = item;
	return;
  case XmCR_FOCUS:
/* THIS CODE CAUSES A CRASH in clientMsgHandler for more than 1
   text control. So for now, we're commenting it out. JACS 17/5/95
   
  	if (panel->previousFocus != NULL && panel->previousFocus != item) {
  		wxItem *item1 = panel->previousFocus;
  		wxItem *item2 = item;

	  	XClientMessageEvent ev;
	  	ev.display = XtDisplay(Widget(panel->handle));
	  	ev.window = XtWindow(Widget(panel->handle));
	  	ev.type = ClientMessage;
	  	ev.format = 32;
#if XmVersion > 1100
	  	ev.message_type = XA_INTEGER;
#endif
	  	ev.data.l[0] = (long) item1;
	  	ev.data.l[1] = (long) item2;
                // Surely this is static for all text items and will
                // only get added to the first created??? - JACS
	  	static Bool addedClientMsgHandler = FALSE;
	  	if (!addedClientMsgHandler) {
		  	XtAddEventHandler(Widget(panel->handle), NoEventMask, TRUE,
		  			(XtEventHandler) clientMsgHandler, NULL);
		  	addedClientMsgHandler = TRUE;
		}
	  	XSendEvent(XtDisplay((Widget) panel->handle),
	  			XtWindow((Widget) panel->handle), TRUE, 0, (XEvent *) &ev);
	}
*/
	return;
  case XmCR_ACTIVATE:
    type_event = wxEVENT_TYPE_TEXT_ENTER_COMMAND ;
    break;
  default:
    type_event = wxEVENT_TYPE_TEXT_COMMAND ;
    break;
  }
  wxCommandEvent event(type_event);
  event.commandString = item->GetValue();
  event.eventObject = item;
  item->ProcessCommand(event);
}
#endif

// Text item
IMPLEMENT_DYNAMIC_CLASS(wxText, wxItem)

wxText::wxText (void)
{
#ifdef wx_motif
  internalTextValue = NULL;
#endif
}

wxText::wxText (wxPanel * panel, wxFunction Function, char *label, char *value,
	int x, int y, int width, int height, long style, char *name):
wxbText (panel, Function, label, value, x, y, width, height, style, name)
{
  Create (panel, Function, label, value, x, y, width, height, style, name);
}

Bool wxText::
Create (wxPanel * panel, wxFunction Function, char *label, char *value,
	int x, int y, int width, int height, long style, char *name)
{
  SetName(name);
  if (panel)
    panel->AddChild (this);
  buttonFont = panel->buttonFont;
  labelFont = panel->labelFont;
  backColour = panel->backColour;
  labelColour = panel->labelColour;
  buttonColour = panel->buttonColour;
  window_parent = panel;
  labelPosition = panel->label_position;
  windowStyle = style;
#ifdef wx_motif
  canAddEventHandler = TRUE;
  windowName = copystring (name);
  internalTextValue = NULL;
  Widget panelForm = panel->panelWidget;
  formWidget = XtVaCreateManagedWidget (windowName,
					xmFormWidgetClass, panelForm,
					XmNmarginHeight, 0,
					XmNmarginWidth, 0,
					NULL);

  Widget textWidget;

  if (buttonFont)
    textWidget = XtVaCreateManagedWidget ("text",
					  xmTextWidgetClass, formWidget,
				XmNfontList, buttonFont->GetInternalFont (),
					  NULL);
  else
    textWidget = XtVaCreateManagedWidget ("text",
					  xmTextWidgetClass, formWidget,
					  NULL);

  int noCols = 10;
  if (value && (strlen (value) > noCols))
    noCols = strlen (value);

  XtVaSetValues (textWidget,
		 XmNcolumns, noCols,
		 NULL);

  if (label)
    {
      char *the_label = (style & wxFIXED_LENGTH) ? fillCopy (label) : copystring (label);
      XmString text = XmStringCreateSimple (the_label);
      labelWidget = XtVaCreateManagedWidget (label,
#if USE_GADGETS
					     style & wxCOLOURED ?
				    xmLabelWidgetClass : xmLabelGadgetClass,
					     formWidget,
#else
					     xmLabelWidgetClass, formWidget,
#endif
					     XmNlabelString, text,
					     NULL);
      if (labelFont)
	XtVaSetValues (labelWidget,
		       XmNfontList, labelFont->GetInternalFont (),
		       NULL);

      XmStringFree (text);
      delete[]the_label;
    }

  if (panel->label_position == wxHORIZONTAL)
    {
      if (labelWidget)
	XtVaSetValues (labelWidget,
		       XmNtopAttachment, XmATTACH_FORM,
		       XmNleftAttachment, XmATTACH_FORM,
		       XmNbottomAttachment, XmATTACH_FORM,
		       XmNalignment, XmALIGNMENT_BEGINNING,
		       NULL);

      XtVaSetValues (textWidget,
		     XmNleftOffset, 4,
		     XmNtopAttachment, XmATTACH_FORM,
		     XmNbottomAttachment, XmATTACH_FORM,
	   XmNleftAttachment, labelWidget ? XmATTACH_WIDGET : XmATTACH_FORM,
		     XmNleftWidget, labelWidget ? labelWidget : formWidget,
		     XmNrightAttachment, XmATTACH_FORM,
		     NULL);
    }
  else
    {
      if (labelWidget)
	XtVaSetValues (labelWidget,
		       XmNtopAttachment, XmATTACH_FORM,
		       XmNleftAttachment, XmATTACH_FORM,
		       XmNalignment, XmALIGNMENT_BEGINNING,
		       NULL);

      XtVaSetValues (textWidget,
	    XmNtopAttachment, labelWidget ? XmATTACH_WIDGET : XmATTACH_FORM,
		     XmNtopWidget, labelWidget ? labelWidget : formWidget,
		     XmNbottomAttachment, XmATTACH_FORM,
		     XmNleftAttachment, XmATTACH_FORM,
		     XmNrightAttachment, XmATTACH_FORM,
		     XmNalignment, XmALIGNMENT_BEGINNING,
		     NULL);
    }

  handle = (char *) textWidget;

  if (windowStyle & wxREADONLY)
    XmTextSetEditable(textWidget, False);

  XtAddCallback(textWidget, XmNfocusCallback, (XtCallbackProc)wxTextCallback,
                  (XtPointer)this);
  XtAddCallback(textWidget, XmNlosingFocusCallback, (XtCallbackProc)wxTextCallback,
                  (XtPointer)this);
  XtAddCallback (textWidget, XmNactivateCallback, (XtCallbackProc) wxTextCallback,
		 (XtPointer) this);
  XtAddCallback (textWidget, XmNvalueChangedCallback, (XtCallbackProc) wxTextCallback,
		 (XtPointer) this);
  XtAddCallback(textWidget, XmNmodifyVerifyCallback, (XtCallbackProc)wxTextModifyProc, (XtPointer)this);


  panel->AttachWidget (this, formWidget, x, y, width, height);
  ChangeColour ();

  if (value)
    XmTextSetString (textWidget, value);

  /* After creating widgets, no more resizes. */
  if (style & wxFIXED_LENGTH)
    {
      if (labelWidget)
	XtVaSetValues (labelWidget,
		       XmNtopAttachment, XmATTACH_SELF,
		       XmNleftAttachment, XmATTACH_SELF,
		       NULL);

      XtVaSetValues (textWidget,
		     XmNtopAttachment, XmATTACH_SELF,
		     XmNbottomAttachment, XmATTACH_SELF,
		     XmNleftAttachment, XmATTACH_SELF,
		     XmNrightAttachment, XmATTACH_SELF,
		     NULL);
      if (labelWidget)
	{
	  XmString text = XmStringCreateSimple (label);
	  XtVaSetValues (labelWidget,
			 XmNlabelString, text,
			 NULL);
	  XmStringFree (text);
	}
    }
  XtVaSetValues(formWidget, XmNresizePolicy, XmRESIZE_NONE, NULL);
#endif
#ifdef wx_xview
  Panel x_panel = (Panel) (panel->GetHandle ());
  Panel_item x_text;

  int label_position;
  if (panel->label_position == wxVERTICAL)
    label_position = PANEL_VERTICAL;
  else
    label_position = PANEL_HORIZONTAL;

  if (panel->new_line)
    {
      x_text = (Panel_item) xv_create (x_panel, PANEL_TEXT,
				       PANEL_VALUE_DISPLAY_WIDTH, 30,
		    PANEL_LAYOUT, label_position, PANEL_NEXT_ROW, -1, NULL);
      panel->new_line = FALSE;
    }
  else
    x_text = (Panel_item) xv_create (x_panel, PANEL_TEXT,
				     PANEL_VALUE_DISPLAY_WIDTH, 30,
				     PANEL_LAYOUT, label_position, NULL);

  old_event_proc = (void (*)(Panel_item, Event *)) xv_get(x_text, PANEL_EVENT_PROC);
  xv_set(x_text, WIN_X_EVENT_MASK, FocusChangeMask, NULL);

  xv_set (x_text,
	  PANEL_NOTIFY_PROC, wxTextProc,
          PANEL_EVENT_PROC, wxTextEventProc,
	  PANEL_NOTIFY_LEVEL, PANEL_ALL,
	  PANEL_CLIENT_DATA, (char *) this,
	  NULL);

  if ((windowStyle & wxPASSWORD) == wxPASSWORD)
  {
    xv_set(x_text, PANEL_MASK_CHAR, '*', NULL);
  }

  if (x > -1 && y > -1)
    (void) xv_set (x_text, XV_X, x, XV_Y, y, NULL);

  if (value && strlen (value) && (width == -1))
    {
      width = strlen (value);
      if (width < 4)
	width = 4;
      xv_set (x_text, PANEL_VALUE_DISPLAY_LENGTH, (int) width, NULL);
    }
  else
    {
      if (width <= 0)
	width = 80;
      xv_set (x_text, PANEL_VALUE_DISPLAY_WIDTH, (int) width, NULL);
    }

/*
   if (buttonFont)
   xv_set(x_text, XV_FONT, buttonFont->GetInternalFont(), NULL) ;
 */
  if (label)
    {
      actualLabel = wxStripMenuCodes(label);

      if (style & wxFIXED_LENGTH)
	{
	  char *the_label = fillCopy (actualLabel);
	  xv_set (x_text, PANEL_LABEL_STRING, the_label, NULL);

	  int label_x = (int) xv_get (x_text, PANEL_LABEL_X);
	  int item_x = (int) xv_get (x_text, PANEL_VALUE_X);
	  xv_set (x_text, PANEL_LABEL_STRING, actualLabel,
		  PANEL_VALUE_X, item_x,
		  PANEL_LABEL_X, label_x,
		  NULL);
	  delete[] the_label;
	}
      else
	xv_set (x_text, PANEL_LABEL_STRING, actualLabel, NULL);
    }

  if (value)
    {
      xv_set (x_text, PANEL_VALUE, value, NULL);
    };

  handle = (char *) x_text;
#endif
  Callback (Function);

  return TRUE;
}

wxText::~wxText (void)
{
#ifdef wx_motif
  if (internalTextValue)
    delete[] internalTextValue;
#endif
}

void wxText::ChangeColour (void)
{
#ifdef wx_motif
  int change;

  wxPanel *panel = (wxPanel *) window_parent;
  change = wxComputeColors (panel->backColour,
			    panel->buttonColour);
  if (change == wxBACK_COLORS)
    XtVaSetValues (formWidget,
		   XmNbackground, itemColors[wxBACK_INDEX].pixel,
		   XmNtopShadowColor, itemColors[wxTOPS_INDEX].pixel,
		   XmNbottomShadowColor, itemColors[wxBOTS_INDEX].pixel,
		   XmNforeground, itemColors[wxFORE_INDEX].pixel,
		   NULL);
  else if (change == wxFORE_COLORS)
    XtVaSetValues (formWidget,
		   XmNforeground, itemColors[wxFORE_INDEX].pixel,
		   NULL);

  change = wxComputeColors (backColour, buttonColour);
  if (change == wxBACK_COLORS)
    XtVaSetValues ((Widget) handle,
		   XmNbackground, itemColors[wxBACK_INDEX].pixel,
		   XmNtopShadowColor, itemColors[wxTOPS_INDEX].pixel,
		   XmNbottomShadowColor, itemColors[wxBOTS_INDEX].pixel,
		   XmNarmColor, itemColors[wxSELE_INDEX].pixel,
		   XmNforeground, itemColors[wxFORE_INDEX].pixel,
		   NULL);
  else if (change == wxFORE_COLORS)
    XtVaSetValues ((Widget) handle,
		   XmNforeground, itemColors[wxFORE_INDEX].pixel,
		   NULL);

  if (labelWidget)
    {
      change = wxComputeColors (panel->backColour, labelColour);

      if (change == wxBACK_COLORS)
	XtVaSetValues (labelWidget,
		       XmNbackground, itemColors[wxBACK_INDEX].pixel,
		       XmNtopShadowColor, itemColors[wxTOPS_INDEX].pixel,
		       XmNbottomShadowColor, itemColors[wxBOTS_INDEX].pixel,
		       XmNforeground, itemColors[wxFORE_INDEX].pixel,
		       NULL);
      else if (change == wxFORE_COLORS)
	XtVaSetValues (labelWidget,
		       XmNforeground, itemColors[wxFORE_INDEX].pixel,
		       NULL);
    }
#endif
}

char *wxText::GetValue (void)
{
#ifdef wx_motif
  // If a password widget, return the REAL
  // string value, not a useless string of asterisks.
  if (internalTextValue)
    return internalTextValue;

  char *s = XmTextGetString ((Widget) handle);
  if (s)
    {
      strcpy (wxBuffer, s);
      XtFree (s);
      return wxBuffer;
    }
  else
    return NULL;
#endif
#ifdef wx_xview
  Panel_item item = (Panel_item) handle;
  return (char *) xv_get (item, PANEL_VALUE);
#endif
}

void wxText::SetValue (char *value)
{
#ifdef wx_motif
  if (value)
    XmTextSetString ((Widget) handle, value);
#endif
#ifdef wx_xview
  Panel_item item = (Panel_item) handle;
  xv_set (item, PANEL_VALUE, value, NULL);
#endif
}

void wxText::SetSize (int x, int y, int width, int height, int sizeFlags)
{
#ifdef wx_motif
  XtVaSetValues(formWidget, XmNresizePolicy, XmRESIZE_ANY, NULL);
  XtUnmanageChild (formWidget);

  if (x > -1)
    XtVaSetValues (formWidget, XmNleftAttachment, XmATTACH_SELF,
		   XmNx, x,
		   NULL);
  if (y > -1)
    XtVaSetValues (formWidget, XmNtopAttachment, XmATTACH_SELF,
		   XmNy, y,
		   NULL);

  Dimension labelWidth = 0, labelHeight = 0;
  int actualWidth = width, actualHeight = height;

  if (labelWidget)
    XtVaGetValues (labelWidget, XmNwidth, &labelWidth, XmNheight, &labelHeight, NULL);

  if (itemOrientation == wxHORIZONTAL)
    {
      actualWidth = width - labelWidth;
      actualHeight = height;
    }
  else
    {
      actualWidth = width;
      actualHeight = height - labelHeight;
    }

  if (width > -1)
    XtVaSetValues ((Widget) handle, XmNwidth, max(actualWidth, 10),
		   NULL);
  if (height > -1)
    XtVaSetValues ((Widget) handle, XmNheight, max(actualHeight, 10),
		   NULL);

  XtManageChild (formWidget);
  XtVaSetValues(formWidget, XmNresizePolicy, XmRESIZE_NONE, NULL);

  GetEventHandler()->OnSize (width, height);
#endif
#ifdef wx_xview
  Xv_opaque x_win = (Xv_opaque) handle;
  if (x > -1)
    (void) xv_set (x_win, XV_X, x, NULL);

  if (y > -1)
    (void) xv_set (x_win, XV_Y, y, NULL);

  // If we're not prepared to use the existing width, then...
  if (!(width == -1 && ((sizeFlags & wxSIZE_AUTO_WIDTH) != wxSIZE_AUTO_WIDTH)))
  {
    int labelWidth = 0;
    int panelLayout = (int)xv_get(x_win, PANEL_LAYOUT);
    if (panelLayout == PANEL_HORIZONTAL)
      labelWidth = (int)xv_get(x_win, PANEL_LABEL_WIDTH);

    if (width == -1)
    {
      int ww, hh;
      GetSize (&ww, &hh);
      width = ww;
    }

    if (width <= 0)
      width = 80;

    (void) xv_set (x_win, PANEL_VALUE_DISPLAY_WIDTH, max(width-labelWidth, 10), NULL);
  }
  GetEventHandler()->OnSize (width, height);
#endif
}

void wxText::SetFocus (void)
{
#ifdef wx_motif
  wxPanel *panel = (wxPanel *) GetParent();
  panel->manualChange = TRUE;
  wxItem::SetFocus();
  panel->manualChange = FALSE;
#endif
#ifdef wx_xview
  wxPanel *panel = (wxPanel *) GetParent ();
  if (panel)
    {
      Panel p = (Panel) panel->handle;
      xv_set (p, PANEL_CARET_ITEM, (Panel_item) handle, 0);
    }
#endif
}

// Clipboard operations
void wxText::Copy(void)
{
#ifdef wx_motif
  XmTextCopy((Widget)handle, CurrentTime);
#endif
}

void wxText::Cut(void)
{
#ifdef wx_motif
  XmTextCut((Widget)handle, CurrentTime);
#endif
}

void wxText::Paste(void)
{
#ifdef wx_motif
  XmTextPaste((Widget)handle);
#endif
}

void wxText::SetEditable(Bool editable)
{
#ifdef wx_motif
  Widget textWidget = (Widget)handle;
  XmTextSetEditable(textWidget, editable);
#endif
#ifdef wx_xview
  // What should it be for XView?
#endif
}

#ifdef wx_motif
void 
wxTextModifyProc (Widget w, XtPointer clientData, XmTextVerifyCallbackStruct *cbs)
{
  wxText *tw = (wxText *) clientData;

  if ((tw->GetWindowStyleFlag() & wxPASSWORD) != wxPASSWORD)
    return;

#if 0  // _sm_ -- Broken original.
  if (cbs->text->ptr == NULL)
  {
    // Backspace
    if (tw->internalTextValue)
    {
      cbs->endPos = strlen(tw->internalTextValue);
      tw->internalTextValue[cbs->startPos] = 0;
      return;
    }
    else return;
  }

  char *newS = new char[cbs->endPos + 2]; // new char + NULL
  if (tw->internalTextValue)
  {
    strcpy(newS, tw->internalTextValue);
    delete[] tw->internalTextValue;
  }
  else newS[0] = 0;

  tw->internalTextValue = newS;
  strncat(tw->internalTextValue, cbs->text->ptr, cbs->text->length);
  tw->internalTextValue[cbs->endPos + cbs->text->length] = 0;

  for (int i = 0; i < cbs->text->length; i ++)
    cbs->text->ptr[i] = '*';
#else  // _sm_ My stab at it.
  /*
   * The preceding code has a number of serious bugs, inherited from the
   * original code in The Motif Programming Manual vol 6 (by Dan Heller).
   *
   * At least on my system (SunOS 4.1.3 + Motif 1.2), you need to think of
   * every event as a replace event.  cbs->text->ptr gives the replacement
   * text, cbs->startPos gives the index of the first char affected by the
   * replace, and cbs->endPos gives the index one more than the last char
   * affected by the replace (startPos == endPos implies an empty range).
   * Hence, a deletion is represented by replacing all input text with a
   * blank string ("", *not* NULL!).  A simple insertion that does not
   * overwrite any text has startPos == endPos.
   */

  char * & passwd = tw->internalTextValue;  // Set up a more convenient alias.

  if (!passwd)
    passwd = copystring(cbs->text->ptr);
  else
  {
    int len = passwd ? strlen(passwd) : 0; // Enough room for old text
    len += strlen(cbs->text->ptr) + 1;     // + new text (if any) + NUL
    len -= cbs->endPos - cbs->startPos;    // - text from affected region.

    char * newS = new char [len];
    char * p = passwd, * dest = newS, * insert = cbs->text->ptr;

    // Copy (old) text from passwd, up to the start posn of the change.
    for (int i = 0; i < cbs->startPos; ++i)
      *dest++ = *p++;

    // Copy the text to be inserted).
    while (*insert)
      *dest++ = *insert++;

    // Finally, copy into newS any remaining text from passwd[endPos] on.
    for (p = passwd + cbs->endPos; *p; )
      *dest++ = *p++;
    *dest = 0;

    delete [] passwd;  passwd = newS;
  }

  // Overwrite text with asterisks.  By the way, since we need "i" outside the
  // for-loop, ISO C++ now demands that it be declared outside the for-loop.
  // To check your code for these cases, you can
  //   #define for  if (0); else for
  // This makes your code run the same under both the old and new scoping rules.
  int i;
  for (i = 0; i < cbs->text->length; ++i)
    cbs->text->ptr[i] = '*';
  cbs->text->ptr[i] = 0;
#endif
}
#endif
