/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */
/* 
AddressOutliner.cpp -- Object wrapper around outline widget hack.
   Created: Dora Hsu <dora@netscape.com>, Sept-30-96.
*/



#include "AddressOutliner.h"
#include "addrbook.h"

#include "xp_core.h"
#include "ComposeView.h"
#include "ComposeFolderView.h"
#include "AddressFolderView.h"
#include "PopupMenu.h"

#include "xfe.h"

#include <Xfe/Xfe.h>

#include <Xm/TextF.h>
#include "felocale.h"
extern XtAppContext fe_XtAppContext;

#ifdef DEBUG_dora
#define XDEBUG(x) x
#else
#define XDEBUG(x)
#endif

extern int MSG_TotalHeaderCount();
extern MSG_HEADER_SET MSG_HeaderValue( int pos );

extern "C"
{

#include "addrbk.h"

XP_List* FE_GetDirServers();
ABook*   fe_GetABook(MWContext *context);

static void KeyIn(
	Widget w,
         XEvent *event,
         String *params,
         Cardinal *nparam);

static void TableTraverse(
	Widget w,
         XEvent *event,
         String *params,
         Cardinal *nparam);

static void FieldTraverse(
	Widget w,
         XEvent *event,
         String *params,
         Cardinal *nparam);
}

extern "C" char * xfe_ExpandForNameCompletion(char * pString);
extern "C" char * xfe_ExpandName(char *pString);

const char *XFE_AddressOutliner::textFocusIn = "XFE_AddressOutliner::textFocusIn";
const char *XFE_AddressOutliner::typeFocusIn = "XFE_AddressOutliner::typeFocusIn";
const char *XFE_AddressOutliner::tabPrev = "XFE_AddressOutliner::tabPrev";
const char *XFE_AddressOutliner::tabNext = "XFE_AddressOutliner::tabNext";

// Customized Traversal Actions

static XtActionsRec actions[] =
{ {"TableTraverse",     TableTraverse     },
  {"FieldTraverse",     FieldTraverse     },
  {"KeyIn",     	KeyIn     }};

// XFE_AddressOutliner Constructor -----------------------------------------

XFE_AddressOutliner::XFE_AddressOutliner(
		const char *name,
		XFE_MNListView *parent_view,
		MAddressable *addressable_view,
		Widget widget_parent,
		Boolean constantSize,
		int numcolumns,
		int *column_widths,
		char *pos_prefname) :
 	XFE_Outliner(name, parent_view, parent_view->getToplevel(), 
		widget_parent,
        	constantSize, False,
        	numcolumns, numcolumns, column_widths, pos_prefname )
{
  Widget baseWidget;
  XmFontList fontList;
  Pixel fg, bg;
  EventMask event_mask =  FocusChangeMask|ButtonPressMask | ButtonReleaseMask;

  m_displayrows = 4; /* same default number of rows as windows */
  m_parentView = parent_view;
  m_addressable = addressable_view;
  m_popup = NULL;
  m_focusRow = 0;
  m_focusCol = XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_RECEIPIENT;
  m_cursorPos = 0;
  m_inPopup = False;
  m_lastFocus = NULL; /* Obselete ?! - dh */
  m_textTimer = 0;

  // Add actions
  XtAppAddActions( fe_XtAppContext, actions,  3);

  XtVaGetValues(m_widget, XmNfontList, &fontList,  
		XmNforeground, &fg,
		XmNbackground, &bg, 0 );

  m_typeWidget = XtVaCreateWidget("addressText", 
			xmTextFieldWidgetClass, m_widget,
			XmNfontList, fontList,
			XmNforeground, fg,	
			XmNbackground, bg,	
			XmNuserData, this,
			XmNcursorPositionVisible, False,
			XmNeditable, False,
			XmNmarginHeight, 0,
                        XmNmarginWidth, 3,
                        XmNshadowThickness, 0,
                        XmNhighlightThickness, 2,
                        XmNx, 0,
                        XmNy, 0,
                        XmNwidth, 40,
                        XmNheight, 40,
			0 );
  XmDropSiteUnregister(m_typeWidget); // prevent conflict with attachment dropsite
  
  m_textWidget = XtVaCreateWidget("addressText", 
			xmTextFieldWidgetClass, m_widget,
			XmNblinkRate, 200,
			XmNcursorPositionVisible, True,
			XmNtraversalOn, True,
			XmNeditable, True,
			XmNforeground, fg,	
			XmNbackground, bg,	
			XmNfontList, fontList,
			XmNuserData, this,
			XmNmarginHeight, 0,
                        XmNmarginWidth, 3,
                        XmNshadowThickness, 0,
                        XmNhighlightThickness, 2,
                        XmNx, 0,
                        XmNy, 0,
                        XmNwidth, 40,
                        XmNheight, 40,
			0);
  XtVaSetValues(m_widget,
		XmNuserData, this,
		XmNhighlightRowMode, False,
		XmNselectionPolicy, XmSELECT_NONE,
                XmNvisibleRows, m_displayrows,
		XmNcellMarginLeft, 1,
		XmNcellMarginRight, 1,
                XmNcellRightBorderType, XmBORDER_DASH,
                XmNcellBottomBorderType, XmBORDER_DASH,
                XtVaTypedArg, XmNcellBackground, XmRString, "white", 6,
                XtVaTypedArg, XmNcellRightBorderColor, XmRString, "Gray60", 7,
                XtVaTypedArg, XmNcellBottomBorderColor, XmRString, "Gray60", 7,
                XmNcellDefaults, True,
                XmNcellAlignment, XmALIGNMENT_LEFT,
                0);
  XmDropSiteUnregister(m_textWidget); // prevent conflict with attachment dropsite

  XmProcessTraversal(m_widget, XmTRAVERSE_CURRENT);
  int total = 0;

  if (m_addressable) 
	total =  ((XFE_AddressFolderView*)m_addressable)->getTotalData();

  change(0,0 ,total+1 );

  //MapText(0);

  getToplevel()->registerInterest(XFE_Component::afterRealizeCallback,
             this,
             (XFE_FunctionNotification)afterRealizeWidget_cb); 
  // Translations will be installed in the afterRealize routine,
  // so that they will override XFE_Frame::hackTranslations.

  baseWidget = m_parentView->getParent()->getBaseWidget();

  XmProcessTraversal(m_textWidget, XmTRAVERSE_CURRENT);

  XtAddCallback(m_widget, XmNscrollCallback, scrollCallback, this);
  XtAddCallback(m_textWidget, XmNactivateCallback, textActivateCallback, this);

  XtAddCallback(m_typeWidget, XmNfocusCallback, typeFocusCallback, this);
  XtAddCallback(m_textWidget, XmNfocusCallback, textFocusCallback, this);

  XtAddCallback(m_typeWidget, XmNlosingFocusCallback, textLosingFocusCallback, this);
  XtAddCallback(m_textWidget, XmNlosingFocusCallback, textLosingFocusCallback, this);
  XtAddCallback(m_textWidget, XmNmodifyVerifyCallback, textModifyCallback, this);

  // Insert our own event handler to the head of the list
  // XFE_Component::m_widget is the grid widget
  XtInsertEventHandler(m_widget,
		       		event_mask,
                    False,
                    eventHandler, this, XtListHead);
  // Add Interests

  m_parentView->getToplevel()->registerInterest(XFE_AddressFolderView::tabNext, this,
	(XFE_FunctionNotification)tabToGrid_cb);

  m_parentView->getParent()->getToplevel()->registerInterest(XFE_ComposeView::tabBeforeSubject, this,
	(XFE_FunctionNotification)tabToGrid_cb);

  setMultiSelectAllowed(True);
  m_firstSelected = -1;
  m_lastSelected = -1;
}

// Default Constructor
XFE_AddressOutliner::XFE_AddressOutliner()
{
}

// Destructor
XFE_AddressOutliner::~XFE_AddressOutliner()
{
}

// Install local translations -- this needs to be after realize time,
// otherwise uparrow and downarrow will be overridden by XFE_Frame.
XFE_CALLBACK_DEFN(XFE_AddressOutliner,
				  afterRealizeWidget)(XFE_NotificationCenter *, void *, void*)
{
  XtOverrideTranslations(m_textWidget, fe_globalData.editing_translations);
  XtOverrideTranslations(m_textWidget, fe_globalData.single_line_editing_translations);
  XtOverrideTranslations(m_textWidget, fe_globalData.address_outliner_traverse_translations);

  XtOverrideTranslations(m_typeWidget, fe_globalData.address_outliner_key_translations);

  // shift focus to the first text field in the outliner if we
  // don't already have a recipient:
  Boolean has_recip = False;

  // Only "To" or "Newsgroup" counts as recipient (not Bcc, Reply-To, etc.)
  int total = getTotalLines();
  int i;
  for (i=0; i<total; ++i)
  {
      char* type = GetCellValue(i,
                         XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_TYPE);
      if (type && (!XP_STRCMP(type, "To:") || !XP_STRCMP(type, "Newsgroup:")))
      {
          has_recip = True;
          delete [] type;
          break;
      }
      if (type)
          delete [] type;
  }
  m_focusRow = total-1;

  if (!has_recip)
  {
      m_focusCol = XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_RECEIPIENT;
      PlaceText(m_focusRow, m_focusCol);
      // PlaceText() is supposed to set the focus into the current field,
      // but it doesn't always.  We need to be sure:
      XmLGridSetFocus(m_widget, m_focusRow, m_focusCol);
  }
}

// Popup callback (for To: Cc: Followup: etc)
void
XFE_AddressOutliner::popupCallback(Widget w, XtPointer clientData, XtPointer)
{
	XFE_AddressOutliner *	obj = (XFE_AddressOutliner*) clientData;
	XmString				xmstr;

	XtVaGetValues(w,XmNlabelString,&xmstr,NULL);

	if (xmstr)
	{
		String str = NULL;

		XmStringGetLtoR(xmstr,XmFONTLIST_DEFAULT_TAG,&str);

		if (str)
		{
			obj->selectedMenu(str);

			XtFree(str);
		}
		
		XmStringFree(xmstr);
	}
}

// Static Methods for traverse actions
void 
XFE_AddressOutliner::fieldTraverse(
	Widget    /*w*/,
        XEvent*   /*event*/,
        String*   /*params*/,
        Cardinal* /*nparam*/)
{
   //XmLGridGetFocus(m_widget, &row, &col, &focus);
   PlaceText(m_focusRow,m_focusCol);
   if ( XtIsManaged(m_textWidget) )
   	XmProcessTraversal(m_textWidget, XmTRAVERSE_CURRENT);
   else if (XtIsManaged(m_typeWidget) )
   	XmProcessTraversal(m_typeWidget, XmTRAVERSE_CURRENT);
}


void 
XFE_AddressOutliner::keyIn(
		Widget /*w*/,
        XEvent * /*event*/,
        String *params,
        Cardinal *nparam)
{
   XrmQuark q;
   static int keyIn_quarksValid = 0;
   static XrmQuark qTo, qReply, qNewsgroup, qFollowup, qCc, qBcc;

   int row, col;
   Widget grid;
   grid = m_widget;

   if (*nparam != 1)
                return;
    if (!keyIn_quarksValid)
    {
        qTo = XrmStringToQuark(XFE_AddressFolderView::TO);
        qReply = XrmStringToQuark(XFE_AddressFolderView::REPLY);
        qFollowup = XrmStringToQuark(XFE_AddressFolderView::FOLLOWUP);
        qNewsgroup = XrmStringToQuark(XFE_AddressFolderView::NEWSGROUP);
        qCc = XrmStringToQuark(XFE_AddressFolderView::CC);
        qBcc = XrmStringToQuark(XFE_AddressFolderView::BCC);
        keyIn_quarksValid = 1;

    }

    q = XrmStringToQuark(params[0] );

    col = m_focusCol;
    row = m_focusRow;

    if (col== XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_RECEIPIENT ) 
	     col = XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_TYPE;

	if ( col == XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_TYPE )
    {
		if ( q == qTo )
		{
	   		XDEBUG(printf("*KEY IN TO * invalidate Line caused by %d\n", row);)
	   		setTypeHeader(row, MSG_TO_HEADER_MASK);
		}
		else if ( q == qReply)
		{
	   		XDEBUG(printf("*KEY IN Reply * invalidate Line caused by %d\n", row);)
	   		setTypeHeader(row, MSG_REPLY_TO_HEADER_MASK);
		}
		else if ( q == qNewsgroup)
		{
	   		XDEBUG(printf("*KEY IN Newsgroup * invalidate Line caused by %d\n", row);)
	   		setTypeHeader(row, MSG_NEWSGROUPS_HEADER_MASK);
		}
		else if ( q == qFollowup)
		{
	   		XDEBUG(printf("*KEY IN Followup * invalidate Line caused by %d\n", row);)
	   		setTypeHeader(row, MSG_FOLLOWUP_TO_HEADER_MASK);
		}
		else if (q == qCc)
    	{
	   		XDEBUG(printf("*KEY IN CC * invalidate Line caused by %d\n", row);)
	   		setTypeHeader(row, MSG_CC_HEADER_MASK);
		}
		else if ( q == qBcc)
    	{
	   		XDEBUG(printf("*KEY IN BCC * invalidate Line caused by %d\n", row);)
	   		setTypeHeader(row, MSG_BCC_HEADER_MASK);
		}
		else
		{
	   		XDEBUG(printf("*Invalide Key In value at %d\n", row);)
			XP_ASSERT(0);
		}
	} /* COLUMN_TYPE */
}

void 
XFE_AddressOutliner::tableTraverse(
	Widget /*w*/,
        XEvent *  event,
        String *  params,
        Cardinal* nparam)

{
   XrmQuark q;
   static int quarksValid = 0;
   static XrmQuark qLEFT, qRIGHT;
   static XrmQuark qINSERT, qDELETE, qBACKSPACE;
   static XrmQuark qNEXT, qPREVIOUS;
   static XrmQuark qHOME, qEND, qUP, qDOWN;

   Widget grid;
   int row, col;
   grid = m_widget;

   if (*nparam != 1)
                return;
    if (!quarksValid)
    {
        qINSERT = XrmStringToQuark("INSERT");
        qDELETE = XrmStringToQuark("DELETE");
        qBACKSPACE = XrmStringToQuark("BACKSPACE");
        qEND = XrmStringToQuark("END");
        qHOME = XrmStringToQuark("HOME");
        qUP = XrmStringToQuark("UP");
        qDOWN = XrmStringToQuark("DOWN");
        qLEFT = XrmStringToQuark("LEFT");
        qRIGHT = XrmStringToQuark("RIGHT");
        qNEXT = XrmStringToQuark("NEXT");
        qPREVIOUS = XrmStringToQuark("PREVIOUS");
	quarksValid = 1;

    }
    q = XrmStringToQuark(params[0] );

   row = m_focusRow;
   col = m_focusCol;


	if ( q == qINSERT)
			doInsert( row,col);
	else if ( q == qDELETE )
      		doDelete(row, col, event);
	else if ( q == qBACKSPACE )
      		doBackSpace(row, col, event);
    else if ( q == qEND)
			doEnd(row, col);
	else if ( q == qHOME)
			doHome(row, col);
    else if ( q == qUP )
			doUp(row, col);
    else if ( q == qDOWN )
			doDown(row, col);
	else if ( q == qLEFT )
			doLeft(row, col);
    else if ( q == qRIGHT)
			doRight(row, col);
    else if ( q == qNEXT)
			doNext(row, col);
	else if ( q == qPREVIOUS)
			doPrevious(row, col);
	else
			XP_ASSERT(0);
}

void
XFE_AddressOutliner::selectedMenu(char *name)
{
   static Cardinal num = 1;
   char *keyInStr = NULL;

   m_inPopup= False;
   if ( name[0] == 'T')
   {
	keyInStr = XP_STRDUP(XFE_AddressFolderView::TO);
   }
   else if ( name[0] == 'C')
   {
	keyInStr = XP_STRDUP(XFE_AddressFolderView::CC);
   }
   else if ( name[0] == 'B')
   {
	keyInStr = XP_STRDUP(XFE_AddressFolderView::BCC);
   }
   else if ( name[0] == 'R' )
   {
	keyInStr = XP_STRDUP(XFE_AddressFolderView::REPLY);
   }
   else if ( name[0] == 'N' )
   {
	keyInStr = XP_STRDUP(XFE_AddressFolderView::NEWSGROUP);
   }
   else if ( name[0] == 'F' )
   {
	keyInStr = XP_STRDUP(XFE_AddressFolderView::FOLLOWUP);
   }
   else XP_ASSERT(0);

   keyIn(m_typeWidget, NULL, &keyInStr, &num);
   XP_FREE(keyInStr);

   // Make sure the popup menu is not posted
   if (XfeIsAlive(m_popup))
   {
	   XtUnmanageChild(m_popup);
   }

   XtAddCallback(m_typeWidget, 
		XmNlosingFocusCallback, textLosingFocusCallback, this);
}

// XFE_AddressOutliner ---------- Protected ---------------------

// NONE 

// XFE_AddressOutliner ---------- Private ---------------------


void
XFE_AddressOutliner::MapText(int row)
{
  int total;

  total = getTotalLines();

  if ( row >= total )
    change(row, total,total+1 );

  PlaceText(row, XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_RECEIPIENT, False);
}

void 
XFE_AddressOutliner::PlaceText(int row, int col, Boolean doUpdate)
{
  XRectangle rect;
  Boolean removed = False;


  //XtVaGetValues(m_textWidget, XmNcursorPosition, &cursorPos, 0);

  if( XtIsManaged(m_textWidget) &&  doUpdate ) {
    (void) updateAddresses();

  XtRemoveCallback(m_textWidget, 
		XmNlosingFocusCallback, textLosingFocusCallback, this);
  removed = True;
  }
  if ( XtIsManaged(m_textWidget) ) XtUnmanageChild(m_textWidget);

  if ( XtIsManaged(m_typeWidget) ) XtUnmanageChild(m_typeWidget);

  // UPDATE FOCUS CELL CURSOR POSITION & ROW & COL 
  m_focusRow= row;
  m_focusCol= col;

  makeVisible(row);

  //XtVaSetValues(m_widget, XmNscrollRow, row, NULL);

  XmLGridRowColumnToXY( m_widget, XmCONTENT, row,  XmCONTENT, col, False, &rect);

  //fe_SetTextFieldAndCallBack(m_textWidget, NULL);

  if ( col == XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_RECEIPIENT )
  {
     char *str;
	        XtVaSetValues( m_textWidget, XmNx, rect.x,
						   XmNy, rect.y, XmNwidth, rect.width,
						   XmNheight, rect.height, 0);

     str = GetCellValue(row, col);

	 // Force an update may not be necessary
     XmUpdateDisplay(m_widget);

     fe_SetTextFieldAndCallBack(m_textWidget, str);

     if ( str ) 
		 /* allocated by new
		  */
		 delete str;

     XmUpdateDisplay(m_textWidget);
     XmTextFieldShowPosition( m_textWidget, m_cursorPos);
	 // Set insertion pos before managing child, otherwise
	 // cruft sometimes appears at beginning of line (SGI bug?):
	 XmTextFieldSetInsertionPosition( m_textWidget, m_cursorPos);
     XtManageChild(m_textWidget);
     XmProcessTraversal(m_textWidget, XmTRAVERSE_CURRENT);
  }
  else if ( col == XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_TYPE)
  {
     char *str;	
     XtVaSetValues( m_typeWidget, XmNx, rect.x,
					XmNy, rect.y, XmNwidth, rect.width,
					XmNheight, rect.height, 0);
     str = GetCellValue(row, col);

     fe_SetTextFieldAndCallBack(m_typeWidget, str);
     if ( str ) XtFree(str);

     XtManageChild(m_typeWidget);
     XmProcessTraversal(m_typeWidget, XmTRAVERSE_CURRENT);
  }
  XmLGridSetFocus(m_widget, m_focusRow, m_focusCol);
  if ( removed )
    XtAddCallback(m_textWidget, 
		XmNlosingFocusCallback, textLosingFocusCallback, this);
}

void
XFE_AddressOutliner::textFocus()
{
	m_parentView->getToplevel()->notifyInterested(XFE_AddressOutliner::textFocusIn, m_textWidget);
}

void
XFE_AddressOutliner::typeFocus()
{
	m_parentView->getToplevel()->notifyInterested(XFE_AddressOutliner::typeFocusIn, m_typeWidget);
}

void
XFE_AddressOutliner::textLosingFocus(XtPointer /* callData*/ )
{
#if 0
    int oldrow = m_focusRow;
    int oldcol = m_focusCol;
    
    XmTextPosition oldcurs = XmTextFieldGetInsertionPosition(m_textWidget);

    updateAddresses();
    invalidate();               // redraw all grid elements

    XmTextPosition lastpos = XmTextFieldGetLastPosition(m_textWidget);
    m_cursorPos = (oldcurs > lastpos ? lastpos : oldcurs);
    m_focusRow = oldrow;
    m_focusCol = oldcol;
    XmLGridSetFocus(m_widget, m_focusRow, m_focusCol);
    XmTextFieldSetInsertionPosition(m_textWidget, m_cursorPos);
#endif
    // To do: When we lose focus, we also lose the black highlight
    // around the active text widget.  This is the place to fix that.
}

void
XFE_AddressOutliner::textactivate(XtPointer /* callData */)
{
    int oldrow = m_focusRow;
    int count = getTotalLines();

    // Don't do anything if there's nothing in the text field:
    if (XmTextFieldGetInsertionPosition(m_textWidget) <= 0)
        return;

    //textLosingFocus(0);
    updateAddresses();

    count = getTotalLines() - count;
    int curline = m_focusRow + count;

    Boolean insertNewLine = !m_addressable->hasDataAt(curline+1);
    if ( insertNewLine )
    {
        selectLine(-1);
        m_addressable->insertNewDataAfter(curline);
    }

    MapText(m_focusRow + count + 1);
    m_focusRow = oldrow + count + 1;

	// As per the spec (don't blame me for the design!):
	// increment the Type field of the next line to the next
	// addressing mode, e.g. if this is To, make it Cc --
	// but only if there were multiple comma-separated names typed in.
    if ( insertNewLine && count > 0 )
    {
	    // Make it stop at Bcc:
	    int headerPos = 0;
		MSG_HEADER_SET mask;
		if ( headerPos == MSG_TotalHeaderCount()-1 ) 
		    mask = MSG_HeaderValue(0);
		else mask = MSG_HeaderValue(++headerPos);
		const char* header = MSG_HeaderMaskToString((MSG_HEADER_SET)mask);
		if (!XP_STRNCMP(header, "To", 2) || !XP_STRNCMP(header, "Cc", 2))
		    doDown(m_focusRow,
				   XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_TYPE);
    }
}

char*
XFE_AddressOutliner::getLastEnterredName(char* str, int* comma_pos, int* len, Boolean *moreRecipients)
{
   char *pSubstring = NULL;
   char *string = NULL;
   char *substring = NULL;
   *len = 0;
   *comma_pos = 0;

   *moreRecipients = False;

   if ( str && strlen(str) > 0 )
   {
     string = strdup(str);

     // Returns the pointer to the first occurances of ','
     substring = strrchr(string, ',');
     if (substring && strlen(substring) > 1)
     {
	pSubstring = (char*)XP_CALLOC(strlen(substring)+1, sizeof(char));
	strcpy( pSubstring, substring+1);
	*len = strlen(pSubstring) ;
        *comma_pos  = strlen(str) - *len;
	*moreRecipients = True;
     }
     else if (!substring) 
     {
	pSubstring = (char*)XP_CALLOC(strlen(string)+1, sizeof(char));
	strcpy( pSubstring, string);
	*len = strlen(pSubstring) ;
     }
     else {
       *comma_pos = strlen(string);
       *moreRecipients = True;
     }
     free(string);
   }
   else
   {
	*len = 0;
        *comma_pos = 0;
   }
   return pSubstring;
}

static void
text_timer_func(XtPointer closure, XtIntervalId *)
{
    XFE_AddressOutliner* ao = (XFE_AddressOutliner*)closure;
    if (ao)
        ao->processInput();
}

// This is the routine that looks for name completion.
void
XFE_AddressOutliner::processInput()
{
    m_textTimer = 0;        // Make sure the timer is clear

    char* textStr = fe_GetTextField(m_textWidget);

    XmTextPosition lastPos = XmTextFieldGetLastPosition(m_textWidget);
    XmTextPosition insertPos = XmTextFieldGetInsertionPosition(m_textWidget);

    // if we're not at the end of the string, then we're done;
    // no need for name completion:
    if (insertPos < lastPos || textStr == 0)
        return;

    char* curName = XP_STRRCHR(textStr, ',');
    if (curName != 0)
        ++curName;
        // also need to skip past whitespace
    else
        curName = textStr;
    char* completeName = nameCompletion(curName);
    if (completeName)
    {
        char* buf = (char*)XP_CALLOC((size_t)lastPos
                                     + XP_STRLEN(completeName) + 1,
                                     sizeof (char));
        if (curName != textStr)
        {
            strncpy(buf, textStr, curName - textStr);
            XP_STRCAT(buf, completeName);
        }
        else
            XP_STRCPY(buf, completeName);

        // Probably would be better to set all these
        // with a single XtSetValues command and avoid
        // having to worry about the callback from SetString.
        fe_SetTextFieldAndCallBack(m_textWidget, buf);
        XmTextFieldSetSelection(m_textWidget, lastPos,
                                XmTextFieldGetLastPosition(m_textWidget),
                            XtLastTimestampProcessed(XtDisplay(m_textWidget)));
        XmTextFieldSetInsertionPosition(m_textWidget, lastPos);
        XP_FREE(completeName);
        XP_FREE(buf);
    }
    XtFree(textStr);
}

void
XFE_AddressOutliner::textmodify(Widget, XtPointer callData)
{
    char *pCompleteName = NULL;
    XmTextVerifyCallbackStruct *cbs;
    char *pName = NULL;
    char *pCurrString = NULL;
    int len;
    int comma_pos, length;
    char *pNewName = NULL;
    Boolean moreRecipients;
    XmTextPosition lastpos = 0;

    cbs = (XmTextVerifyCallbackStruct *)callData;

    if (m_lastSelected > -1)     // there's a multiple selection
    {
        // Do nothing unless this is backspace or delete
        if (cbs->text->ptr != NULL)
            return;

        deleteRows(m_firstSelected, m_lastSelected);
        return;
    }

    // If one line is selected, deselect once we start typing:
    else if (m_firstSelected > -1)
        selectLine(-1);

    if ( cbs->text->length == 1 )
    {
        pCurrString = fe_GetTextField(m_textWidget);
        lastpos = XmTextFieldGetLastPosition(m_textWidget);
        XmTextPosition left, right;
        if (XmTextFieldGetSelectionPosition(m_textWidget, &left, &right)
            && lastpos <= right)
            lastpos = left;

        if ( cbs->currInsert < lastpos ) 
        {
            XtFree(pCurrString);
            pCurrString = NULL;
            cbs->doit = True;
        }
        if ( pCurrString )
        {
            XmTextPosition insertPos;

            // We only want to match the part of pCurrString the user
            // actually entered, not the part matched from the addrbook:
            char* enteredString = (char*)XP_CALLOC(lastpos+1, sizeof(char));
            strncpy(enteredString, pCurrString, lastpos);
            pNewName = getLastEnterredName(enteredString, &comma_pos, &length,
                                           &moreRecipients);
            XP_FREE(enteredString);
            len = length + cbs->text->length + 1;
            pName = (char*)XP_CALLOC(len, sizeof(char));

            // If the field was empty before, it's easy:
            if ( cbs->currInsert == 0 || (moreRecipients && length == 0))
            {
                strcpy(pName, cbs->text->ptr);
                insertPos = 0;
            }
            else if ( length > 0 )
            {
                XmTextPosition left, right;
                if (!XmTextFieldGetSelectionPosition(m_textWidget,
                                                     &left, &right))
                    left = cbs->currInsert;
                // "left", not cbs->currInsert, is really the insert position.

                if (!moreRecipients)
                    strncpy(pName, pCurrString, (size_t)left);
                else
                    strncpy(pName, pNewName,
                            (unsigned int)(cbs->currInsert - comma_pos));

                strcat(pName, cbs->text->ptr);

                // Now reset the timer -- when it runs out we'll try
                // name completion, rather than doing it on every char:
                if (m_textTimer)
                    XtRemoveTimeOut(m_textTimer);
                m_textTimer = XtAppAddTimeOut(fe_XtAppContext, 650,
                                              text_timer_func, this);
                pCompleteName = 0;
               
                insertPos = left;
            }

            cbs->doit = True;
            XP_FREE (pName);
        }
        else
        {
            cbs->doit = True;
        }
        XtFree(pCurrString);
    }
}

void
XFE_AddressOutliner::textmotion(XtPointer callData)
{
   XmTextVerifyCallbackStruct *cbs;


  cbs = (XmTextVerifyCallbackStruct *)callData;

}

void
XFE_AddressOutliner::scroll(XtPointer)
{
    selectLine(-1);

    // If we keep the child managed, then the Outliner will try to
    // scroll back to keep the last focused row visible (makeVisible).
    // We can either unmanage the text widget (so nothing has focus)
    // or guess where to shift the focus upon scrolling.
    XtUnmanageChild(m_textWidget);

#if 0
    int focus_r, focus_c;
    Boolean focusIn;
    XRectangle rect;
     XtUnmanageChild(m_textWidget);
     XmLGridGetFocus(m_widget, &focus_r, &focus_c, &focusIn);
     if ( XmLGridRowIsVisible(m_widget, focus_r) )
     {
     	XmLGridRowColumnToXY( m_widget, XmCONTENT,
		focus_r,  XmCONTENT, 2, False, &rect);
     	XtVaSetValues( m_textWidget, XmNx, rect.x,
			XmNy, rect.y, XmNwidth, rect.width,
			XmNheight, rect.height, 0);
     	XtManageChild(m_textWidget);
     	XmProcessTraversal(m_textWidget, XmTRAVERSE_CURRENT);
     }
#endif
}

void
XFE_AddressOutliner::handleEvent(XEvent *event, Boolean *c)
{
  int x = event->xbutton.x;
  int y = event->xbutton.y;
  unsigned char rowtype;
  unsigned char coltype;
  int row, column;
  static int press_row=0, press_col = 0;
  // only handles button clicks for now.  See outline.c:ButtonEvent for details. 
  *c = True;
  switch (event->type)
    {
    case KeyPress:
      	XDEBUG(printf("KeyPressMask...\n");)
        selectLine(-1);
	break;
    case FocusOut:
      	XDEBUG(printf("Focus out Mask...\n");)
	break;
    case FocusIn:
      	XDEBUG(printf("Focus In Mask...\n");)
		*c = False;
		//XmLGridGetFocus(m_widget, &row, &column, &focus);
		if ( m_focusCol == XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_RECEIPIENT ) {
			XmProcessTraversal(m_textWidget, XmTRAVERSE_CURRENT);
		}
	break;
    case EnterNotify: // Enter Grid Widget and Leave the text widget
      XDEBUG(printf("Enter Notify-Detail-%d\n", ((XCrossingEvent*)event)->detail);)
      /***************************************************************/
      // This is a HACK, we rely on details to give us more info
      //  about the crossing events.

      // Here are some traces. But, details might be skipped if
      // cursor pointer moves too fast. Aack.. it's X... dh

      // Leave from Grid to Outside Grid, details = NotifyAncestor(0)
      // From Outsidget Gri enter to Grid, details = NotifyAncestor

      // Leave from Grid to Text Widget, details = NotifyInferior(2)
      // Enter to Grid from Text Widget, details = NotifyInferior
      /***************************************************************/

      // Only when one actually enter to the grid widget
      // from outside the grid widget. 
      if ( ((XCrossingEvent*)event)->detail == NotifyAncestor ) onEnter();
      break;
    case LeaveNotify:
      // Don't do anthing now
      break;
    case MotionNotify:
      XDEBUG(printf("Motion Notify..........\n");)
      break;
    case ButtonPress:
      // Always ignore btn3. Btn3 is for popups. - dp 
      *c = False;
      if (event->xbutton.button == 3) break;
      XDEBUG(printf("Button Press....\n");)

      if (XmLGridXYToRowColumn(m_widget, x, y,
                               &rowtype, &row, &coltype, &column) >= 0)
        {
	  	m_inPopup = False;
          if (rowtype == XmHEADING)
            {
              // let microline handle the resizing of rows.
              if (XmLGridPosIsResize(m_widget, x, y))
                {
  		*c = True;
                  return;
                }
            }
	  else if (  column == XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_ICON )
	{

      // If it's a shift-click, we need to do multi selection,
      // otherwise select it exclusively:
      if ((event->xbutton.state & ShiftMask) != 0 && m_firstSelected >= 0)
          extendSelection(row);
      else
          selectLine(row);

      *c = True;
      return;
	}
	  else if (column == XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_TYPE
               || column == XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_BTN)
          m_inPopup = True;
          else if ( column > 0 )
	  {
      		press_row = row;
      		press_col = column;
          }

          // We had a click somewhere other than the icon, so clear selection:
          selectLine(-1);

	  if ( m_inPopup )
	  {
		*c = False;

  		XtRemoveCallback(m_typeWidget, 
		XmNlosingFocusCallback, textLosingFocusCallback, this);
        // This call to PlaceText sets the focus into the type field,
        // which allows changing the type via keyboard but has the
        // unfortunate side effect that we can't pull down a menu
        // from this widget a second time.  But that's arguably better
        // than never allowing keyboard-based changes ...
        // ToDo: place a similar event handler on the m_typeWidget.
        PlaceText(row, XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_TYPE);
		if ( m_popup == NULL )
		{
			int i;

			m_popup = XFE_PopupMenu::CreatePopupMenu(FE_GetToplevelWidget(),
													 "AddressOutlinerPopup",
													 NULL,0);
			
			XtVaSetValues(m_popup,XmNmenuPost,"<Btn1Down>",NULL);
			
			int count = MSG_TotalHeaderCount();

			for ( i = 0; i < count; i++ )
			{
				char *maskStr = XP_STRDUP(MSG_HeaderMaskToString(MSG_HeaderValue(i)));
				
				XmString xmstr = XmStringCreate(maskStr,
												XmFONTLIST_DEFAULT_TAG);
				
				Widget item = XtVaCreateManagedWidget("PopupItem",
													  xmPushButtonWidgetClass,
													  m_popup,
													  XmNlabelString,	xmstr,
													  NULL);
			
				XmStringFree(xmstr);
				
				XtAddCallback(item,XmNactivateCallback,popupCallback,this);
				
				XP_FREE(maskStr);
			}
		}

		Position x_root, y_root;
		XRectangle rect;
		XButtonPressedEvent press_event;
                XmLGridRowColumnToXY(m_widget,
			XmCONTENT, row,
			XmCONTENT, 0,
			True,
			&rect);
		XtTranslateCoords(m_widget, 0,0, &x_root, &y_root);

		press_event.x_root = x_root+rect.x;
		press_event.y_root = y_root+rect.y + rect.height;

		// Position and post the popup menu
		XmMenuPosition(m_popup,(XButtonPressedEvent *) &press_event);
		XtManageChild(m_popup);
	  }
        }
      break;
    case ButtonRelease:
      *c = False;

      if (event->xbutton.button == 3) break;
      if (XmLGridXYToRowColumn(m_widget, x, y,
                               &rowtype, &row, &coltype, &column) >= 0)
        {
          if (rowtype == XmHEADING)
            {
              // let microline handle the resizing of rows.
              if (XmLGridPosIsResize(m_widget, x, y))
                {
  		*c = True;
                  return;
                }
            }
          else 
            {
		if (row == press_row && column == press_col ) 
		{
		  if ( column == XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_ICON )
		    *c = True;
		  else if ( column > 0 )
		  {
			  PlaceText(row, column);

			  // Translate mouse event coordinate to the text field char pos:
			  if (column == XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_RECEIPIENT)
			  {
				  Position x_grid, y_grid;	// root pos of grid origin
				  Position x_text, y_text;	// root pos of textf origin
				  XtTranslateCoords(m_widget, 0,0, &x_grid, &y_grid);
				  XtTranslateCoords(m_textWidget, 0,0, &x_text, &y_text);

				  XmTextFieldSetCursorPosition(m_textWidget,
						XmTextFieldXYToPos(m_textWidget,
										   x + x_grid - x_text,
										   y + y_grid - y_text));
				  XmProcessTraversal(m_textWidget, XmTRAVERSE_CURRENT);
			  }
		  }
		  else *c = False;
		}
            }
        }
      break;
 }
}

void
XFE_AddressOutliner::selectLine(int row)
{
    Pixel bg;

    Time time = XtLastTimestampProcessed(XtDisplay(m_textWidget));
    XmTextFieldClearSelection(m_textWidget, time);

    XmLGridDeselectAllRows(getBaseWidget(), False);

    if (row < 0)        // clear selection
    {
        XmLGridRow rowp  = XmLGridGetRow(m_widget, XmCONTENT, 0);
        XmLGridColumn colp = XmLGridGetColumn(m_widget, XmCONTENT, 0);
        XtVaGetValues(m_widget,
                      XmNrowPtr, rowp,
                      XmNcolumnPtr, colp,
                      XmNcellBackground,&bg,
                      NULL);
        XmTextFieldClearSelection(m_textWidget, time);
        XtVaSetValues(m_textWidget, XmNbackground, bg, 0 );
        m_firstSelected = m_lastSelected = -1;
        return;
    }

    // highlight everything in the text field
    PlaceText(row, XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_RECEIPIENT);
    XmTextPosition last = XmTextFieldGetLastPosition(m_textWidget);
    XmTextFieldSetSelection(m_textWidget, 0, last, time);
    XmTextFieldSetCursorPosition(m_textWidget, 0);

    XmLGridSelectRow(getBaseWidget(), row, False);

    XtVaGetValues(m_widget,
                  XmNselectBackground, &bg,
                  NULL);
    XtVaSetValues(m_textWidget, XmNbackground, bg, 0 );

    m_firstSelected = row;
    m_lastSelected = -1;
}

void
XFE_AddressOutliner::extendSelection(int row)
{
    Time time = XtLastTimestampProcessed(XtDisplay(m_textWidget));

    // XmLGridSelectRow() may overwrite the selected text,
    // so make sure nothing is selected:
    XmTextFieldClearSelection(m_textWidget, time);

    XmLGridDeselectAllRows(getBaseWidget(), False);

    if (m_lastSelected < 0)     // only one selected item previously
    {
        if (row < m_firstSelected)
        {
            m_lastSelected = m_firstSelected;
            m_firstSelected = row;
        }
        else
            m_lastSelected = row;
    }
    else if (row < m_firstSelected)
        m_firstSelected = row;
    else /* if (row > m_lastSelected) */
        m_lastSelected = row;

    // Select everything in the text field -- not because we
    // really want to, but because it will force the text field
    // to pass both backspace and delete through to textmodify.
    // What a total hack!
    // Worse, if the text field is blank, we can't select it at all! :-(
    XmTextPosition last = XmTextFieldGetLastPosition(m_textWidget);
#ifdef notdef
    // This PlaceText causes lines to be deleted from the outliner
    // if the last line is blank!
    // It's not clear why it was needed, anyway.
    PlaceText(row, XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_RECEIPIENT);
#endif
    XmTextFieldSetSelection(m_textWidget, 0, last, time);
    XmTextFieldSetCursorPosition(m_textWidget, 0);

    int i;
    for (i = m_firstSelected; i <= m_lastSelected; ++i)
        XmLGridSelectRow(getBaseWidget(), i, False);

    Pixel bg;
    XtVaGetValues(m_widget,
                  XmNselectBackground, &bg,
                  NULL);
    XtVaSetValues(m_textWidget, XmNbackground, bg, 0 );
}


/* Caller has to free this return value */
char *
XFE_AddressOutliner::GetCellValue(int row, int col)
{
   XmString xmStr = 0;
   XmLGridRow rowp;
   XmLGridColumn colp;
   char *str;
   Pixel bg;


   m_addressable->getDataAt(row, col, &str);
   rowp = XmLGridGetRow(m_widget, XmCONTENT, row);
   colp = XmLGridGetColumn(m_widget, XmCONTENT, col);

   XtVaGetValues(m_widget,
                XmNrowPtr, rowp,
                XmNcolumnPtr, colp,
                XmNcellString, &xmStr,
		XmNcellBackground,&bg,
                NULL);
   XtVaSetValues(m_textWidget, XmNbackground, bg, 0 );
   XtVaSetValues(m_typeWidget, XmNbackground, bg, 0 );
   return str;
}

//
// This routine returns malloced memory which should be freed with XtFree()
//
static char*
getUnhighlightedText(Widget textW)
{
    XmTextPosition left, right;
    char* textStr = fe_GetTextField(textW);
    if (XmTextFieldGetSelectionPosition(textW, &left,&right))
        textStr[left] = 0;
    return textStr;
}

int
XFE_AddressOutliner::updateAddresses()
{
  int totalAddresses = 1;

  int focus_r, focus_c;

  char *textStr;
  int total = getTotalLines();

  //XmLGridGetFocus(m_widget, &focus_r, &focus_c, &focusIn);

  focus_r = m_focusRow;
  focus_c = m_focusCol;

  if ( focus_c == XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_RECEIPIENT )
  {
    textStr = getUnhighlightedText(m_textWidget);

    setAddress(focus_r, (char*)textStr, True);
    XtFree(textStr);
    totalAddresses = getTotalLines();
    totalAddresses = totalAddresses - total + 1; // Include current line

    // Reset the value of the text field in case it has changed
    // from plural to singular:
    char* str = GetCellValue(m_focusRow,
                    XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_RECEIPIENT);
    if (str)
    {
        // Only update the string if it changed
        // (so we don't change the X selection unnecessesarily)
        char* curstr = fe_GetTextField(m_textWidget);
        if (!curstr || XP_STRCMP(curstr, str))
            fe_SetTextFieldAndCallBack(m_textWidget, str);
        delete [] str;
        if (curstr)
            XtFree(curstr);
    }

    XtVaGetValues(m_textWidget, XmNcursorPosition, &m_cursorPos, 0);
  }
  return totalAddresses;
}


// XFE_AddressOutliner -------- Private ------ static functions -----------

XFE_CALLBACK_DEFN(XFE_AddressOutliner, tabToGrid)(XFE_NotificationCenter*, void*, void*)
{
  if ( m_textWidget && XtIsManaged(m_textWidget))
	XmProcessTraversal(m_textWidget, XmTRAVERSE_CURRENT);
  else if ( m_typeWidget && XtIsManaged(m_typeWidget))
	XmProcessTraversal(m_typeWidget, XmTRAVERSE_CURRENT);
}


void
XFE_AddressOutliner::textFocusCallback(Widget, XtPointer clientData, XtPointer)
{
  XFE_AddressOutliner *obj = (XFE_AddressOutliner*)clientData;
 

  obj->textFocus();
}

void
XFE_AddressOutliner::typeFocusCallback(Widget, XtPointer clientData, XtPointer)
{
  XFE_AddressOutliner *obj = (XFE_AddressOutliner*)clientData;
 

  obj->typeFocus();
}

void
XFE_AddressOutliner::textLosingFocusCallback(Widget, XtPointer clientData, XtPointer callData)
{
  XFE_AddressOutliner *obj = (XFE_AddressOutliner*)clientData;
  obj->textLosingFocus(callData);
}
void
XFE_AddressOutliner::textActivateCallback(Widget, XtPointer clientData, XtPointer callData)
{
  XFE_AddressOutliner *obj = (XFE_AddressOutliner*)clientData;
  obj->textactivate(callData);
}

void
XFE_AddressOutliner::textMotionCallback(Widget, XtPointer clientData, XtPointer callData)
{
  XFE_AddressOutliner *obj = (XFE_AddressOutliner*)clientData;
  obj->textmotion(callData);
}

void
XFE_AddressOutliner::textModifyCallback(Widget w, XtPointer clientData, XtPointer callData)
{
  XFE_AddressOutliner *obj = (XFE_AddressOutliner*)clientData;
  obj->textmodify(w, callData);
}

void
XFE_AddressOutliner::scrollCallback(Widget, XtPointer clientData, XtPointer callData)
{
  XFE_AddressOutliner *obj = (XFE_AddressOutliner*)clientData;
   obj->scroll(callData);
}

void
XFE_AddressOutliner::eventHandler(Widget, XtPointer clientData, XEvent *event, Boolean *cont)
{
  XFE_AddressOutliner *obj = (XFE_AddressOutliner*)clientData;
  obj->handleEvent(event, cont);
}

char *
XFE_AddressOutliner::nameCompletion(char *pString)
{
   return xfe_ExpandForNameCompletion(pString);
}

//----------------------------  Static Functions for action table
static void FieldTraverse(
	Widget w,
         XEvent *event,
         String *params,
         Cardinal *nparam)

{
  
   XtPointer client;
   XFE_AddressOutliner *obj ;

   XtVaGetValues( w, XmNuserData, &client, 0);
   obj = (XFE_AddressOutliner *) client;
   obj->fieldTraverse(w, event, params, nparam);

}

static void KeyIn(
	Widget w,
         XEvent *event,
         String *params,
         Cardinal *nparam)
{
   XtPointer client;
   XFE_AddressOutliner *obj ;

   XtVaGetValues( w, XmNuserData, &client, 0);

   obj = (XFE_AddressOutliner *) client;
   obj->keyIn(w, event, params, nparam);
}

static void TableTraverse(
	Widget w,
         XEvent *event,
         String *params,
         Cardinal *nparam)
{
   XtPointer client;
   XFE_AddressOutliner *obj ;

   XtVaGetValues( w, XmNuserData, &client, 0);

   obj = (XFE_AddressOutliner *) client;
   obj->tableTraverse(w, event, params, nparam);
}

//----------- Address Book stuff here ------------

extern "C" char * xfe_ExpandForNameCompletion(char * pString)
{
    ABID entryID;
    ABID field;
    ABook *pAddrBook = fe_GetABook(0);
    XP_List *pDirectories= FE_GetDirServers();
    char *pName = XP_STRDUP(pString);

#if defined(DEBUG_tao)
	printf("\n  xfe_ExpandForNameCompletion");
#endif

	DIR_Server* pDirServer = NULL;
	pAddrBook = fe_GetABook(0);
	DIR_GetComposeNameCompletionAddressBook(pDirectories, &pDirServer);

    AB_GetIDForNameCompletion(
        pAddrBook,
     	pDirServer,
        &entryID,&field,(const char*)pName);

    XP_FREE(pName);

    if (entryID != (unsigned long)-1)
    {
	if ( field == ABNickname )
	{
		/* abdefn.h:const int kMaxFullNameLength = 256;
		 */
		char szNickname[kMaxFullNameLength+1];
	    szNickname[0] = '\0';
            AB_GetNickname(
	    pDirServer,
            pAddrBook, entryID, szNickname);
                if (strlen(szNickname))
                            return strdup(szNickname);
        }
        else if ( field == ABFullName )
	{
		char *szFullname;
		szFullname = (char*)XP_CALLOC(kMaxFullNameLength+1, sizeof(char));
		szFullname[0] = '\0';
		AB_GetFullName(pDirServer, 
					   pAddrBook,
					   entryID,
					   szFullname);
		if (szFullname && strlen(szFullname))
			{
				return szFullname;
			}
		
	}
    }

        return NULL;
}

// New Communication methods with parent view

void 
XFE_AddressOutliner::setTypeHeader(int row, MSG_HEADER_SET header, Boolean redisplay)
{
   const char *type = MSG_HeaderMaskToString(header);
   m_focusRow = row;

   // Set Data
   m_addressable->setHeader(row, header);

   // Update the Type Field Display 

   if ( redisplay ) invalidateLine(row); // Should be invalidateCell
   if ( type )
   {
     fe_SetTextFieldAndCallBack(m_typeWidget, (char*)type );
     XmTextFieldSetHighlight(m_typeWidget, 0, 
					strlen(type), XmHIGHLIGHT_SELECTED); 
   }
   else XP_ASSERT(0);
}



void
XFE_AddressOutliner::setAddress(int row, 
				char *pAddressStr, Boolean redisplay)
{
   int oldCount = getTotalLines();
   int newReceipients = 1;
	
   m_focusRow = row;

   newReceipients = m_addressable->setReceipient(row, pAddressStr);
   if ( newReceipients > 1 )
   {
       change(m_focusRow, oldCount, oldCount+newReceipients-1);
   }
   else if (redisplay)
  {
    invalidate();
  }
}

void
XFE_AddressOutliner::doInsert(int row, int /* col */)
{

  (void)updateAddresses();
  m_addressable->insertNewDataAfter(row);
  m_cursorPos = 0;
  MapText(row+1);
  invalidate();
}

void
XFE_AddressOutliner::doDelete(int row, int col, XEvent *event)
{
  XmTextPosition cpos;
  XmTextPosition pos, newPos;
  char *textStr;
  Boolean deleterow = False;

  if ( col ==  XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_RECEIPIENT)
  {
	   XtVaGetValues(m_textWidget, XmNcursorPosition, &cpos, 0);
	   pos = XmTextFieldGetLastPosition(m_textWidget);

	   if (pos ==0 && cpos== 0 && row+1 == getTotalLines() ) deleterow = False;
	   else if (pos ==0 && cpos== 0) deleterow = True;
	   else if ( pos == cpos ) deleterow = False;
	   else if (pos > 0) 
	   { 
		// Hack text widget action
	    XtCallActionProc(m_textWidget, "delete-next-character", event,
							NULL, 0 );
	    newPos = XmTextFieldGetLastPosition(m_textWidget);
        textStr = fe_GetTextField(m_textWidget);

	    if (newPos == pos && newPos == 0 ) deleterow = True;
	    else{
	       setAddress(row, textStr, False);
               invalidateLine(row);
            }
	    XtFree(textStr);
	   } /* pos > 0 */
   } /* col = */

  if (deleterow
	  || col == XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_TYPE)
	  deleteRow(row, col);
}

void
XFE_AddressOutliner::doBackSpace(int row, int col, XEvent *event)
{
  XmTextPosition cpos;
  XmTextPosition pos;
  Boolean deleterow = False;

  if ( col ==  XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_RECEIPIENT)
  {
	   XtVaGetValues(m_textWidget, XmNcursorPosition, &cpos, 0);
	   pos = XmTextFieldGetLastPosition(m_textWidget);

	   if (pos == 0 && cpos == 0 )
		   deleterow = True;
	   else if (pos > 0) 
	   { 
		   // Hack text widget action
		   XtCallActionProc(m_textWidget, "delete-previous-character",
							event, NULL, 0 );
	   } /* pos > 0 */
   } /* col = */

  if (deleterow
	  || col == XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_TYPE)
  {
	  deleteRow(row, col);

	  // shift focus to end of previous row
	  if ( row > 0 ) {
		  PlaceText(row-1,
					XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_RECEIPIENT);
		  m_cursorPos = XmTextFieldGetLastPosition(m_textWidget);
		  XmTextFieldSetInsertionPosition(m_textWidget, m_cursorPos);
	  }
  }
}

void
XFE_AddressOutliner::deleteRow(int row, int col)
{
   selectLine(-1);

   if ( getTotalLines() == 1)
   {
	  m_cursorPos= 0;
	  setAddress(row, NULL, False);
	  setTypeHeader(row, MSG_TO_HEADER_MASK, False);
	  invalidateLine(row);

      PlaceText(0,XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_RECEIPIENT, False );
   }
   else
   {
	  //m_cursorPos = 0;
	  if (row >= 0 && m_addressable->removeDataAt(row) )
      {
		XtRemoveCallback(m_textWidget, XmNlosingFocusCallback,
						 textLosingFocusCallback, this);
		int total = getTotalLines();
		if ( total > row+1)
		{
		   change(row, total, total-1);
    		   PlaceText(row, col, False);
		}
		else
		{
		   change(row, total, total-1);
    		   PlaceText(row-1, col, False);
		}
		XtAddCallback(m_textWidget, XmNlosingFocusCallback,
					  textLosingFocusCallback, this);
		m_cursorPos = 0;
		XmTextFieldSetCursorPosition(m_textWidget, 0); 
      }
	   col = XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_RECEIPIENT;
	}
}

void
XFE_AddressOutliner::deleteRows(int startrow, int endrow)
{
   selectLine(-1);

   XtUnmanageChild(m_textWidget);

   if (endrow >= getTotalLines()-1)
       endrow = getTotalLines()-1;

   for (int i = endrow; i >= startrow; --i)
       m_addressable->removeDataAt(i);

   // Make sure we have at least one row:
   Boolean deletedAll = False;
   if (startrow == 0 && endrow == getTotalLines()-1)
   {
       startrow = 1;
       deletedAll = True;
   }
   change(startrow, endrow-startrow+1, getTotalLines()-endrow+startrow-1);

   // Should get the text widget managed again ...

   if (deletedAll)
   {
       PlaceText(0, XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_RECEIPIENT);
       fe_SetTextFieldAndCallBack(m_textWidget, "");
   }
}

void
XFE_AddressOutliner::doEnd(int row, int col)
{
	if (col == XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_RECEIPIENT)
	{
		m_cursorPos = XmTextFieldGetLastPosition(m_textWidget);
		XmTextFieldSetCursorPosition(m_textWidget, m_cursorPos);
	}
	else
	{
		row = getTotalLines(); 
		col = XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_RECEIPIENT;
		PlaceText(row, col);
	}
}

void
XFE_AddressOutliner::doHome(int row, int col)
{
	if (col == XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_RECEIPIENT)
	{
		XmTextFieldSetCursorPosition(m_textWidget, (XmTextPosition)0);
	}
	else
	{
		row = 0; col = 1;
		PlaceText(row, col);
	}
}

void
XFE_AddressOutliner::doUp(int row, int col)
{
	const char *header;
	int headerPos = 0;
    MSG_HEADER_SET mask;

	if ( col == XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_RECEIPIENT )
	{
	    if ( row > 0 ) row--;
	    PlaceText(row,XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_RECEIPIENT);
	}
	else
	{
 	   static Cardinal doUp_num = 1;
	   char *headerStr;

	   headerPos = m_addressable->getHeader(row);
	   
	   if ( headerPos == 0 ) 
			mask = MSG_HeaderValue(MSG_TotalHeaderCount()-1);
	   else mask = MSG_HeaderValue(--headerPos);

           header = MSG_HeaderMaskToString((MSG_HEADER_SET)mask);

	   headerStr = XP_STRDUP(header);
   	   keyIn( m_typeWidget, NULL, &headerStr, &doUp_num );
	   XP_FREE(headerStr);
	}
}

void
XFE_AddressOutliner::doDown(int row, int col)
{	
  const char *header;
  MSG_HEADER_SET mask;
  int headerPos;

  if ( col == XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_RECEIPIENT )
  {
	updateAddresses();

	if ( row+1 < getTotalLines() ) 
        {
      	  row++;
    	}
	PlaceText(row,
	    XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_RECEIPIENT, False);
  }
  else 
  {
 	   static Cardinal doDown_num = 1;
	   char *headerStr;
	   headerPos = m_addressable->getHeader(row);
	   
	   if ( headerPos == MSG_TotalHeaderCount()-1 ) 
			mask = MSG_HeaderValue(0);
	   else mask = MSG_HeaderValue(++headerPos);

           header = MSG_HeaderMaskToString((MSG_HEADER_SET)mask);

	   headerStr = XP_STRDUP(header);
       keyIn( m_typeWidget, NULL, &headerStr, &doDown_num );
	   XP_FREE(headerStr);
  }
}

void 
XFE_AddressOutliner::doLeft(int row, int col)
{
	XmTextPosition pos;

	if ( col == XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_RECEIPIENT )
    	{
 	   pos = XmTextFieldGetInsertionPosition(m_textWidget);
	   if ( pos == 0 )
	     PlaceText(row, XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_TYPE);
	   else 
	   {
	     m_cursorPos = pos-1;
	     XmTextFieldSetInsertionPosition(m_textWidget, m_cursorPos);
	   }
	}
}


void
XFE_AddressOutliner::doRight(int row, int col)
{
	XmTextPosition pos;
	XmTextPosition lastpos;

	if ( col == XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_TYPE )
	{
	   PlaceText(row, XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_RECEIPIENT);
	   XmTextFieldSetInsertionPosition(m_textWidget, 0 );
	}
	else if ( col == XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_RECEIPIENT )
	{
 	   lastpos = XmTextFieldGetLastPosition(m_textWidget);
 	   pos = XmTextFieldGetInsertionPosition(m_textWidget);
	   if ( pos < lastpos )
	   {
	      m_cursorPos = pos+1;
	      XmTextFieldSetInsertionPosition(m_textWidget, m_cursorPos);
	   }
	   else if ( row+1 < getTotalLines() )
	      PlaceText(row+1, XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_TYPE);
	   else
	      PlaceText(row, XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_RECEIPIENT);
	
	}
}

void
XFE_AddressOutliner::doNext(int row, int col)
{
  updateAddresses();

  if ( col == XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_RECEIPIENT )
  {
	  if ( row+1 == getTotalLines() )
	  {
    	// Register Action

  		XtUnmanageChild(m_textWidget);
  		XtUnmanageChild(m_typeWidget);
		m_parentView->getToplevel()->notifyInterested(XFE_AddressOutliner::tabNext, this);
		invalidate();
		return;
	  }
	  else if ( row < getTotalLines() ) row++;
	  col = XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_TYPE;
  }
  else 
	col= XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_RECEIPIENT;
  PlaceText(row, col, False);
}


void
XFE_AddressOutliner::doPrevious(int row, int col)
{
  updateAddresses();

  if ( col == XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_TYPE )
  {
	 if ( row == 0 )
	 {
		m_parentView->getToplevel()->notifyInterested(XFE_AddressOutliner::tabPrev, this);
		return;
	 }
	 else 
	    row --;
     col = XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_RECEIPIENT;
  }
  else col = XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_TYPE;
  PlaceText(row, col, False);
}

void
XFE_AddressOutliner::onLeave()
{
  XtVaGetValues(m_textWidget, XmNcursorPosition, &m_cursorPos, 0);
  XtUnmanageChild(m_textWidget);
  XtUnmanageChild(m_typeWidget);

  updateAddresses();
  invalidate();

  XmLGridSetFocus(m_widget, m_focusRow, m_focusCol);
  XmUpdateDisplay(m_widget);
  if ( m_lastFocus )
	XmProcessTraversal(m_lastFocus, XmTRAVERSE_CURRENT);
  else 
  m_parentView->getToplevel()->notifyInterested(XFE_AddressOutliner::tabNext, this);
}

void
XFE_AddressOutliner::onEnter()
{
  int row, column;
  Widget base;

  base =  m_parentView->getParent()->getParent()->getBaseWidget();
  row = m_focusRow;
  column = m_focusCol;

  if ( XtIsManaged(m_textWidget) || XtIsManaged(m_typeWidget) )
  {
	return;
  }

  if ( column == XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_RECEIPIENT ) 
  {
	// Value has been updated on Leave. No need to update it again
	PlaceText(row, column, False );
  }
  else if ( column == XFE_AddressFolderView::ADDRESS_OUTLINER_COLUMN_TYPE)
  { 
       XtManageChild(m_typeWidget);
       XmProcessTraversal(m_typeWidget, XmTRAVERSE_CURRENT);
  }
//  m_parentView->getToplevel()->notifyInterested(XFE_AddressFolderView::tabNext, this);
}

void
XFE_AddressOutliner::fillText(char *text)
{
	if (m_textWidget && text)
		fe_SetTextFieldAndCallBack(m_textWidget, text);
}



