/* -*- 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.
 */
/* 
   ABDirListView.cpp -- class definition for XFE_ABDirListView
   Created: Tao Cheng <tao@netscape.com>, 14-oct-97
 */

#include "ABDirPropertyDlg.h"
#include "ABDirListView.h"

#if defined(USE_MOTIF_DND)

#include "OutlinerDrop.h"

#endif /* USE_MOTIF_DND */

#include "xpgetstr.h"

const char *XFE_ABDirListView::dirCollapse = 
                              "XFE_ABDirListView::dirCollapse";
const char *XFE_ABDirListView::dirSelect = 
                              "XFE_ABDirListView::dirSelect";

// icons
fe_icon XFE_ABDirListView::m_openParentIcon = { 0 };
fe_icon XFE_ABDirListView::m_closedParentIcon = { 0 };
fe_icon XFE_ABDirListView::m_pabIcon = { 0 };
fe_icon XFE_ABDirListView::m_ldapDirIcon = { 0 };
fe_icon XFE_ABDirListView::m_mListIcon = { 0 };

extern int XFE_AB_HEADER_NAME;
extern int XFE_AB2PANE_DIR_HEADER;

#if defined(DEBUG_tao)
#define D(x) printf x
#else
#define D(x)
#endif


#define AB_DIR_OUTLINER_GEOMETRY_PREF "ab_dir.outliner_geometry"

XFE_ABDirListView::XFE_ABDirListView(XFE_Component *toplevel_component,
									 Widget         parent, 
									 XFE_View      *parent_view, 
									 MWContext     *context,
									 XP_List       *directories):
	XFE_MNListView(toplevel_component, 
				   parent_view,
				   context, 
				   (MSG_Pane *)NULL),
#if !defined(USE_ABCOM)
	m_directories(directories)
#else
	m_containerLine(NULL),
	m_activeContainer(NULL),
	m_dataIndex(MSG_VIEWINDEXNONE)
#endif /* USE_ABCOM */
{
	/* initialize 
	 */
	m_dir = 0;
	m_dirLine = 0;
	m_ancestorInfo = 0;
	m_deleted_directories = NULL;

	/* For outliner
	 */
	int num_columns = OUTLINER_COLUMN_LAST;
	static int column_widths[] = {23};
	m_outliner = new XFE_Outliner("dirList",
								  this,
								  toplevel_component,
								  parent,
								  False, // constantSize
								  True,  // hasHeadings
								  num_columns,
								  num_columns,// num_visible 
								  column_widths,
								  AB_DIR_OUTLINER_GEOMETRY_PREF);
	m_outliner->setHideColumnsAllowed(False);
	m_outliner->setPipeColumn(OUTLINER_COLUMN_NAME);

	/* BEGIN_3P: XmLGrid
	 */
	XtVaSetValues(m_outliner->getBaseWidget(),
				  XtVaTypedArg, XmNblankBackground, XmRString, "white", 6,
				  // XmNselectionPolicy, XmSELECT_MULTIPLE_ROW,
				  XmNvisibleRows, 15,
				  NULL);
	XtVaSetValues(m_outliner->getBaseWidget(),
				  XmNcellDefaults, True,
				  XtVaTypedArg, XmNcellBackground, XmRString, "white", 6,
				  NULL);
	/* END_3P: XmLGrid
	 */

#if defined(USE_ABCOM)
	// todo: check return val
	int error = AB_CreateContainerPane(&m_pane,
									   context,
									   fe_getMNMaster());


	error = 
		AB_SetShowPropertySheetForDirFunc(m_pane, 
		   &XFE_ABDirListView::ShowPropertySheetForDirFunc);

	error = AB_InitializeContainerPane(m_pane);
	m_nDirs = MSG_GetNumLines(m_pane);
#if defined(DEBUG_tao)
	printf("\n MSG_GetNumLines, m_nDirs=%d\n", m_nDirs);
#endif

#else
	/* XFE_Outliner constructor does not allocate any content row
	 * XFE_Outliner::change(int first, int length, int newnumrows)
	 */
	if (directories) {
		m_nDirs = XP_ListCount(directories);
	}/* if */
#endif /* USE_ABCOM */

	setPane(m_pane);

	m_outliner->change(0, m_nDirs, m_nDirs);
	m_outliner->show();
	if (m_nDirs)
		m_outliner->selectItemExclusive(0);
	setBaseWidget(m_outliner->getBaseWidget());
    XtVaSetValues(getBaseWidget(),
                  XmNpaneMinimum, 1,
                  XmNpaneMaximum, 10000,
                  NULL);
	/* initialize the icons if they haven't already been
	 */
	Pixel bg_pixel;
	XtVaGetValues(m_outliner->getBaseWidget(), XmNbackground, &bg_pixel, 0);

	if (!m_openParentIcon.pixmap)
		fe_NewMakeIcon(getToplevel()->getBaseWidget(),
					   /* umm. fix me
						*/
					   BlackPixelOfScreen(XtScreen(m_outliner->getBaseWidget())),
					   bg_pixel,
					   &m_openParentIcon,
					   NULL, 
					   oparent.width, 
					   oparent.height,
					   oparent.mono_bits, 
					   oparent.color_bits, 
					   oparent.mask_bits, 
					   FALSE);
  
	if (!m_closedParentIcon.pixmap)
		fe_NewMakeIcon(getToplevel()->getBaseWidget(),
					   /* umm. fix me
						*/
					   BlackPixelOfScreen(XtScreen(m_outliner->getBaseWidget())),
					   bg_pixel,
					   &m_closedParentIcon,
					   NULL, 
					   cparent.width, 
					   cparent.height,
					   cparent.mono_bits, 
					   cparent.color_bits, 
					   cparent.mask_bits, 
					   FALSE);
  

	if (!m_pabIcon.pixmap)
		fe_NewMakeIcon(getToplevel()->getBaseWidget(),
					   /* umm. fix me
						*/
					   BlackPixelOfScreen(XtScreen(m_outliner->getBaseWidget())),
					   bg_pixel,
					   &m_pabIcon,
					   NULL, 
					   MNC_AddressSmall.width, 
					   MNC_AddressSmall.height,
					   MNC_AddressSmall.mono_bits, 
					   MNC_AddressSmall.color_bits, 
					   MNC_AddressSmall.mask_bits, 
					   FALSE);
  

	if (!m_ldapDirIcon.pixmap)
		fe_NewMakeIcon(getToplevel()->getBaseWidget(),
					   /* umm. fix me
						*/
					   BlackPixelOfScreen(XtScreen(m_outliner->getBaseWidget())),
					   bg_pixel,
					   &m_ldapDirIcon,
					   NULL, 
					   MN_FolderServer.width, 
					   MN_FolderServer.height,
					   MN_FolderServer.mono_bits, 
					   MN_FolderServer.color_bits, 
					   MN_FolderServer.mask_bits, 
					   FALSE);
  

	if (!m_mListIcon.pixmap)
		fe_NewMakeIcon(getToplevel()->getBaseWidget(),
					   /* umm. fix me
						*/
					   BlackPixelOfScreen(XtScreen(m_outliner->getBaseWidget())),
					   bg_pixel,
					   &m_mListIcon,
					   NULL, 
					   MN_People.width, 
					   MN_People.height,
					   MN_People.mono_bits, 
					   MN_People.color_bits, 
					   MN_People.mask_bits, 
					   FALSE);
  

}

XFE_ABDirListView::~XFE_ABDirListView()
{
}

#if defined(USE_ABCOM)
int
XFE_ABDirListView::ShowPropertySheetForDirFunc(DIR_Server *server, 
											   MWContext  *context, 
											   MSG_Pane   *pane,
											   XP_Bool     newDirectory)
{
#if defined(DEBUG_tao)
	printf("\n XFE_ABDirListView::ShowPropertySheetForDirFunc, newDirectory=%d\n",
		   newDirectory);
#endif
	XFE_ABDirPropertyDlg* Dlg = 
		new XFE_ABDirPropertyDlg(CONTEXT_WIDGET(context),
								 "abDirProperties", 
								 True, 
								 context);
	Dlg->setDlgValues(server);
	Dlg->setPane(pane);
	Dlg->show();
	return 1;
}
#endif /* USE_ABCOM */

void 
XFE_ABDirListView::paneChanged(XP_Bool asynchronous,
							   MSG_PANE_CHANGED_NOTIFY_CODE notify_code,
							   int32 value)
{
#if defined(DEBUG_tao)
	printf("\nXFE_ABDirListView::paneChanged, asynchronous=%d, notify_code=%d, value=0x%x", asynchronous, notify_code, value);
#endif
}

Boolean 
XFE_ABDirListView::isCommandEnabled(CommandType command, 
									void * /* calldata */,
									XFE_CommandInfo* /* i */)
{
#if defined(DEBUG_tao_)
	printf("\nXFE_ABDirListView::isCommandEnabled:%s\n", Command::getString(command));
#endif
	AB_CommandType abCmd = (AB_CommandType)~0;
	if (command == xfeCmdABNewPAB)
		abCmd = AB_NewAddressBook;
	else if (command == xfeCmdABNewLDAPDirectory)
		abCmd = AB_NewLDAPDirectory;
	else if (command == xfeCmdABDelete)
		abCmd = AB_DeleteCmd;
	else if ((command == xfeCmdABProperties) ||
			 (command == xfeCmdABEditEntry))
		abCmd = AB_PropertiesCmd;
	else
		return FALSE;

	int count = 0;
	const int *indices = 0;
	m_outliner->getSelection(&indices, &count);

	XP_Bool selectable_p = False;
#if defined(USE_ABCOM)
	int error = 
		AB_CommandStatusAB2(m_pane, 
							abCmd,
							(MSG_ViewIndex *) indices,
							(int32) count,
							&selectable_p,
							NULL,
							NULL,
							NULL);
#endif /* USE_ABCOM */

#if defined(DEBUG_tao_)
	printf("\nXFE_ABDirListView::isCommandEnabled,cmd=%s, selectable_p=%d\n", 
		   Command::getString(command), selectable_p);
#endif
	return selectable_p;
}

Boolean 
XFE_ABDirListView::isCommandSelected(CommandType /* command */, 
									 void * /* calldata */,
									 XFE_CommandInfo* /* i */)
{
	return FALSE;
}

Boolean 
XFE_ABDirListView::handlesCommand(CommandType command, 
								  void * /* calldata */,
								  XFE_CommandInfo* /* i */)
{
#if defined(DEBUG_tao_)
	printf("\nXFE_ABDirListView::handlesCommand:%s\n", Command::getString(command));
#endif
	if (IS_CONTAINER_PANE_CMD(command) ||
		IS_2_PANE_CMD(command))

		return True;
	else
		return FALSE;
}

void 
XFE_ABDirListView::doCommand(CommandType command, 
							 void * /* calldata */,
							 XFE_CommandInfo* /* i */)
{
	AB_CommandType abCmd = (AB_CommandType)~0;
	if (command == xfeCmdABNewPAB)
		abCmd = AB_NewAddressBook;
	else if (command == xfeCmdABNewLDAPDirectory)
		abCmd = AB_NewLDAPDirectory;
	else if (command == xfeCmdABDelete)
		abCmd = AB_DeleteCmd;
	else if (command == xfeCmdABProperties ||
			 command == xfeCmdABEditEntry)
		abCmd = AB_PropertiesCmd;
	else
		return;

	int count = 0;
	const int *indices = 0;
	m_outliner->getSelection(&indices, &count);

#if defined(USE_ABCOM)	
	int error = 
		AB_CommandAB2(m_pane, 
					  abCmd,
					  (MSG_ViewIndex *) indices,
					  (int32) count);
#endif /* USE_ABCOM */
	return;
}

void 
XFE_ABDirListView::setDirServers(XP_List *dirs)
{
	m_directories = dirs;
	if (m_directories) {
		m_nDirs = XP_ListCount(m_directories);		
	}/* if */
	else {
		m_nDirs = 0;
		m_dir = 0;
		m_dirLine = 0;
	}/* else */

	/* update UI
	 */
	m_outliner->change(0, m_nDirs, m_nDirs);
}

// The Outlinable interface
void* XFE_ABDirListView::ConvFromIndex(int /* index */)
{
	return 0;
}

int XFE_ABDirListView::ConvToIndex(void */* item */)
{
	return -1;
}

//
char *XFE_ABDirListView::getColumnName(int /* column */)
{
	return XP_GetString(XFE_AB_HEADER_NAME);
}

char *XFE_ABDirListView::getColumnHeaderText(int column)
{
  char *tmp = 0;
  switch (column) {
  case OUTLINER_COLUMN_NAME:
    tmp = XP_GetString(XFE_AB2PANE_DIR_HEADER);
    break;
  }/* switch () */

  return tmp;
}


fe_icon *XFE_ABDirListView::getColumnHeaderIcon(int /* column */)
{
	fe_icon *myIcon = 0;

#if 0
	switch (column) {
	case OUTLINER_COLUMN_FLPPY:
		myIcon = &m_openParentIcon;
		break;

	case OUTLINER_COLUMN_TYPE:
		myIcon = &m_pabIcon;
		break;
  case OUTLINER_COLUMN_NAME:
		myIcon = &m_pabIcon;
		break;
		
	}/* switch () */
#endif
	return myIcon;
}

EOutlinerTextStyle 
XFE_ABDirListView::getColumnHeaderStyle(int /* column */)
{
	return OUTLINER_Default;
}


EOutlinerTextStyle 
XFE_ABDirListView::getColumnStyle(int /* column */)
{
	return OUTLINER_Default;
}

//
fe_icon*
XFE_ABDirListView::treeInfoToIcon(int /* depth */, int /* flags */, 
								  XP_Bool /* expanded */)
{
#if 0
	if (depth < 1) {
		if (flags & MSG_FOLDER_FLAG_NEWS_HOST)
			return secure ? &newsServerSecureIcon : &newsServerIcon;
		else if (flags & MSG_FOLDER_FLAG_IMAPBOX)
			return &mailServerIcon;
		else
			return &mailLocalIcon;
	}/* if */

	if (flags & MSG_FOLDER_FLAG_NEWSGROUP)
		return &newsgroupIcon;
	else if (flags & MSG_FOLDER_FLAG_INBOX)
		return expanded ? &inboxOpenIcon : &inboxIcon;
	else if (flags & MSG_FOLDER_FLAG_DRAFTS)
		return expanded ? &draftsOpenIcon : &draftsIcon;
	else if (flags & MSG_FOLDER_FLAG_TRASH)
		return &trashIcon;
	else if (flags & MSG_FOLDER_FLAG_QUEUE)
		return expanded ? &outboxOpenIcon : &outboxIcon;
	else if (flags & MSG_FOLDER_FLAG_IMAPBOX)
		return expanded ? &folderServerOpenIcon : &folderServerIcon;
	else
		return expanded ? &folderOpenIcon : &folderIcon;
#else
	return NULL;
#endif
}

void 
XFE_ABDirListView::getTreeInfo(XP_Bool *expandable, 
							   XP_Bool *is_expanded, 
							   int *depth, 
							   OutlinerAncestorInfo **ancestor)
{
	XP_Bool is_line_expandable = False;
	XP_Bool is_line_expanded = False;
	
#if defined(USE_ABCOM)
	AB_ContainerAttribValue *value = NULL;
	int error = AB_GetContainerAttribute(m_containerLine,
										 attribNumChildren,
										 &value);
	XP_ASSERT(value && value->attrib == attribNumChildren);

	/* is_line_expandable 
	 */
	if (value->u.number)
		is_line_expandable = value->u.number?True:False;
	AB_FreeContainerAttribValue(value);

	/* depth
	 */
	if (depth) {
		error = AB_GetContainerAttribute(m_containerLine,
										 attribDepth,
										 &value);
	
		XP_ASSERT(value && value->attrib == attribDepth);
		*depth = value->u.number;
		AB_FreeContainerAttribValue(value);
	}/* if */

	/* is_line_expanded ?
	 */
	if (is_line_expandable) {
		int32 delta = MSG_ExpansionDelta(m_pane, m_dataIndex);
		if (delta < 0)
			is_line_expanded = True;
	}/* if */

#else
	is_line_expandable = 
		(m_dirLine->dirType == PABDirectory)?
		True:False;
	if (is_line_expandable) {
		is_line_expanded = True; // hardwired
    }
	else {
		is_line_expanded = False;
    }

	if (depth)
		*depth = (m_dirLine->dirType == PABDirectory)?0:0;
#endif
	if (ancestor)
		*ancestor = m_ancestorInfo;

	if (expandable)
		*expandable =  is_line_expandable;

	if (is_expanded)
		*is_expanded = is_line_expanded;
}

char*
XFE_ABDirListView::getColumnText(int column)
{
  char *tmp = 0;
  switch (column) {
  case OUTLINER_COLUMN_NAME:
#if defined(USE_ABCOM)
	  if (m_containerLine) {
		  AB_ContainerAttribValue *value = NULL;
		  int error = AB_GetContainerAttribute(m_containerLine,
											   attribName,
											   &value);
		  XP_ASSERT(value && value->attrib == attribName);
		  if (value->u.string)
			  tmp = XP_STRDUP(value->u.string); //todo need to free it
		  AB_FreeContainerAttribValue(value);
	  }/* if */
#else
	  if (m_dirLine &&
		  m_dirLine->description) {
		  tmp = XP_STRDUP(m_dirLine->description);
	  }/* if */
#endif /* USE_ABCOM */
	  break;
  }/* switch () */

  return tmp;

}

fe_icon*
XFE_ABDirListView::getColumnIcon(int column)
{
	fe_icon *myIcon = NULL;
#if defined(USE_ABCOM)	
	if (!m_containerLine)
		return myIcon;

	switch (column) {
	case OUTLINER_COLUMN_NAME:
		AB_ContainerAttribValue *value = NULL;
		int error = AB_GetContainerAttribute(m_containerLine,
											 attribContainerType,
											 &value);
		XP_ASSERT(value && value->attrib == attribContainerType);
		AB_ContainerType dirType = value->u.containerType; // attribContainerType
		AB_FreeContainerAttribValue(value);

		if (dirType == AB_PABContainer)
			myIcon = &m_pabIcon;
		else if (dirType == AB_LDAPContainer)
			myIcon = &m_ldapDirIcon;
		else if (dirType == AB_MListContainer)
			myIcon = &m_mListIcon;
		break;
	}/* switch */
#else
	if (!m_dirLine)
		return myIcon;

	switch (column) {
	case OUTLINER_COLUMN_NAME:
		if (m_dirLine->dirType == PABDirectory)
			myIcon = &m_pabIcon;
		else if (m_dirLine->dirType == LDAPDirectory)
			myIcon = &m_ldapDirIcon;
		else
			myIcon = &m_mListIcon;
		break;
	}/* switch */
#endif /* USE_ABCOM */
	return myIcon;	
}

//
void XFE_ABDirListView::clickHeader(const OutlineButtonFuncData *data)
{

  int column = data->column; 
  switch (column) {
  case OUTLINER_COLUMN_NAME:  
  default:
	  break;
  }/* switch() */
}

void
XFE_ABDirListView::propertiesCB()
{
  int count = 0;
  const int *indices = 0;
  m_outliner->getSelection(&indices, &count);
#if defined(USE_ABCOM)
  int error = 
	  AB_CommandAB2(m_pane, 
					AB_PropertiesCmd,
					(MSG_ViewIndex *) indices,
					(int32) count);
#else
  if (count > 0 && indices) {
	  DIR_Server *dir = 
		  (DIR_Server *) XP_ListGetObjectNum(m_directories, 
											 indices[0]+1);
	  fe_showABDirPropertyDlg(getToplevel()->getBaseWidget(),
							  m_contextData,
							  dir,
							  &(XFE_ABDirListView::propertyCallback),
							  this);
  }/* if */
#endif /* USE_ABCOM */
}

void XFE_ABDirListView::propertyCallback(DIR_Server *dir, void *callData)
{
	XFE_ABDirListView *obj = (XFE_ABDirListView *) callData;
	obj->propertyCB(dir);
}

void XFE_ABDirListView::propertyCB(DIR_Server *dir)
{	
	int which = XP_ListGetNumFromObject(m_directories, dir);
#if defined(DEBUG_tao)
	printf("\nXFE_ABDirListView::propertyCB=%d\n", which);
#endif
	if (!which) {
		/* new
		 */
		const int *selected;
		int count;		
		m_outliner->getSelection(&selected, &count);
		int pos = 0;
		if (m_nDirs > 0 && count && selected) {
			// Insert dir at position
			pos = selected[0];
			DIR_Server *prev_dir;
			prev_dir = (DIR_Server*)XP_ListGetObjectNum(m_directories,
														pos+1);
			XP_ListInsertObjectAfter(m_directories, prev_dir, dir);
			pos = pos+1;
		}
		else {
			XP_ListAddObjectToEnd(m_directories, dir);
			pos = m_nDirs+1;
		}
			
		// Repaint 
		m_nDirs = XP_ListCount(m_directories);
		m_outliner->change(0, m_nDirs, m_nDirs);
		// Set selection
		m_outliner->selectItemExclusive(pos);
		notifyInterested(XFE_ABDirListView::dirSelect, (void *) dir);
	}/* if */
	else
		m_outliner->invalidateLine(which);
		
	DIR_SaveServerPreferences(m_directories);
	if (m_deleted_directories) {
		DIR_CleanUpServerPreferences(m_deleted_directories);
		m_deleted_directories = NULL;
	}/* if */

	getToplevel()->notifyInterested(XFE_View::chromeNeedsUpdating);
}

void XFE_ABDirListView::doubleClickBody(const OutlineButtonFuncData *data)
{
#if defined(USE_ABCOM)
	int count = 0;
	const int *indices = 0;
	m_outliner->getSelection(&indices, &count);

	int error = 
		AB_CommandAB2(m_pane, 
					  AB_PropertiesCmd,
					  (MSG_ViewIndex *) indices,
					  (int32) count);
#else
	/* get dir
	 */
	DIR_Server *dir = 
		(DIR_Server *) XP_ListGetObjectNum(m_directories, 
										   data->row+1);

	fe_showABDirPropertyDlg(getToplevel()->getBaseWidget(),
							m_contextData,
							dir,
							&(XFE_ABDirListView::propertyCallback),
							this);
#endif /* USE_ABCOM */
}


void XFE_ABDirListView::Buttonfunc(const OutlineButtonFuncData *data)
{
  int row = data->row, 
      clicks = data->clicks;

  // focus
  notifyInterested(XFE_MNListView::changeFocus, this);

  if (row < 0) {
	  clickHeader(data);
	  return;
  } 
  else {
	  /* content row 
	   */
	  if (clicks == 2) {
		  m_outliner->selectItemExclusive(data->row);
		  doubleClickBody(data);
	  }/* clicks == 2 */
	  else if (clicks == 1) {
		  if (data->ctrl) {
				  m_outliner->toggleSelected(data->row);
		  }
		  else if (data->shift) {
			  // select the range.
			  const int *selected;
			  int count;
  
			  m_outliner->getSelection(&selected, &count);
			  
			  if (count == 0) { /* there wasn't anything selected yet. */
				  m_outliner->selectItemExclusive(data->row);
			  }/* if count == 0 */
			  else if (count == 1) {
				  /* there was only one, so we select the range from
					 that item to the new one. */
				  m_outliner->selectRangeByIndices(selected[0], data->row);
			  }/* count == 1 */
			  else {
				  /* we had a range of items selected, 
				   * so let's do something really
				   * nice with them. */
				  m_outliner->trimOrExpandSelection(data->row);
			  }/* else */
		  }/* if */
		  else {
			  m_outliner->selectItemExclusive(data->row);
			  selectLine(data->row);
		  }/* else */

		  getToplevel()->notifyInterested(XFE_View::chromeNeedsUpdating);
	  }/* clicks == 1 */
  }/* else */

}

void XFE_ABDirListView::Flippyfunc(const OutlineFlippyFuncData *data)
{
#if defined(DEBUG_tao)
	printf("\n XFE_ABDirListView::Flippyfunc \n");
#endif /* DEBUG_tao */
	int row = data->row;
#if defined(USE_ABCOM)
	XP_Bool selection_needs_bubbling = False;

		int delta = MSG_ExpansionDelta(m_pane, row);

	if (delta == 0)
		return;

	/* we're not selected and we're being collapsed.
	   check if any of our children are selected, and if
	   so we select this row. */
	if (!m_outliner->isSelected(row) && delta < 0) {
		int num_children = -1 * delta;
		int i;
		
		for (i = row + 1; i <= row + num_children; i ++)
			if (m_outliner->isSelected(i)) {
					selection_needs_bubbling = True;
			}
	}
	MSG_ToggleExpansion(m_pane, row, NULL);

	if (selection_needs_bubbling)
		m_outliner->selectItem(row);

	getToplevel()->notifyInterested(XFE_View::chromeNeedsUpdating);
#endif /*USE_ABCOM */
}
	
//
void XFE_ABDirListView::releaseLineData()
{
	delete [] m_ancestorInfo;
	m_ancestorInfo = NULL;	
}

void *XFE_ABDirListView::acquireLineData(int line)
{
#if defined(USE_ABCOM)
	if (line < 0 ||
		!m_outliner ||
		line >= m_outliner->getTotalLines())
		return 0;
	m_dataIndex = line;
	AB_ContainerAttribValue *value = NULL;
	int error = AB_GetContainerAttributeForPane(m_pane,
										 line,
										 attribContainerInfo,
										 &value);
	XP_ASSERT(value && value->attrib == attribContainerInfo && value->u.container);
	m_containerLine = value->u.container;
	AB_FreeContainerAttribValue(value);

	/* ancestor
	 */
	/* depth
	 */
	error = AB_GetContainerAttribute(m_containerLine,
									 attribDepth,
									 &value);
	
	XP_ASSERT(value && value->attrib == attribDepth);
	int32 depth = value->u.number;
#if defined(DEBUG_tao_)
	printf("\nXFE_ABDirListView::acquireLineData, depth=%d\n", depth);
#endif
	AB_FreeContainerAttribValue(value);
	if (depth) 
		m_ancestorInfo = new OutlinerAncestorInfo[depth+1];
	else
		m_ancestorInfo = new OutlinerAncestorInfo[1];
      
	// ripped straight from the winfe
	int i = depth - 1;
	int idx = line + 1;
	int total_lines = m_outliner->getTotalLines();
	while (i >  0) {
		if ( idx < total_lines ) {
			/* depth
			 */
			error = AB_GetContainerAttribute(m_containerLine,
											 attribDepth,
											 &value);
			
			XP_ASSERT(value && value->attrib == attribDepth);
			int32 level = value->u.number;
			AB_FreeContainerAttribValue(value);

			if ( (level - 1) == i ) {
				m_ancestorInfo[i].has_prev = TRUE;
				m_ancestorInfo[i].has_next = TRUE;
				i--;
				idx++;
			}/* if */ 
			else if ( (level - 1) < i ) {
				m_ancestorInfo[i].has_prev = FALSE;
				m_ancestorInfo[i].has_next = FALSE;
				i--;
			}/* else if */
			else {
				idx++;
			}/* else */
		}/* if */ 
		else {
			m_ancestorInfo[i].has_prev = FALSE;
			m_ancestorInfo[i].has_next = FALSE;
			i--;
		}/* else */
	}/* while */

	m_ancestorInfo[0].has_prev = FALSE;
	m_ancestorInfo[0].has_next = FALSE;
	return m_containerLine;
#else
	if (line < 0 ||
		line >= m_nDirs ||
		m_directories == 0) {
		m_dirLine = 0;
		return 0;
	}/* if */
	else {
		m_dirLine = (DIR_Server *) XP_ListGetObjectNum(m_directories, 
													   line+1);
	}/* else */

	/* ancestor
	 */
	m_ancestorInfo = NULL;

	int level = (m_dirLine->dirType == PABDirectory)?1:0;

	if (level > 0) {
		m_ancestorInfo = new OutlinerAncestorInfo[level];
    }
	else {
		m_ancestorInfo = new OutlinerAncestorInfo[ 1 ];
    }
      
	// ripped straight from the winfe
	int i = level - 1;
	int idx = line + 1;
	int total_lines = m_outliner->getTotalLines();
	while ( i >  0 ) {
		if ( idx < total_lines ) {
			int level = (m_dirLine->dirType == PABDirectory)?1:2;
			//MSG_GetFolderLevelByIndex(m_pane, idx);
			if ( (level - 1) == i ) {
				m_ancestorInfo[i].has_prev = TRUE;
				m_ancestorInfo[i].has_next = TRUE;
				i--;
				idx++;
			} else if ( (level - 1) < i ) {
				m_ancestorInfo[i].has_prev = FALSE;
				m_ancestorInfo[i].has_next = FALSE;
				i--;
			} else {
				idx++;
			}
		} else {
			m_ancestorInfo[i].has_prev = FALSE;
			m_ancestorInfo[i].has_next = FALSE;
			i--;
		}
	}
	
	m_ancestorInfo[0].has_prev = FALSE;
	m_ancestorInfo[0].has_next = FALSE;

	return m_dirLine;
#endif /* !USE_ABCOM */
}

#define COLLAPSE_IS_HIDE 1
//
void XFE_ABDirListView::expandCollapse(XP_Bool expand)
{
	if (expand)
		show();
	else
		hide();
}

#if defined(USE_ABCOM)
//
void XFE_ABDirListView::selectContainer(AB_ContainerInfo *containerInfo)
{
	if (containerInfo == m_activeContainer)
		return;

	MSG_ViewIndex index = AB_GetIndexForContainer(m_pane, containerInfo);
	if (index != MSG_VIEWINDEXNONE) {
		m_outliner->scroll2Item((int) index);

		selectLine((int) index);
	}/* if */
}
#endif /* USE_ABCOM */

void XFE_ABDirListView::selectLine(int line)
{
#if defined(USE_ABCOM)
	AB_ContainerAttribValue *value = NULL;
	int error = AB_GetContainerAttributeForPane(m_pane,
												line,
												attribContainerInfo,
												&value);
	XP_ASSERT(value && value->attrib == attribContainerInfo && value->u.container);

	// set active one
	m_activeContainer = value->u.container;

	notifyInterested(XFE_ABDirListView::dirSelect, 
					 (void *) value->u.container);
	AB_FreeContainerAttribValue(value);
#else
	DIR_Server *dir = 
		(DIR_Server *) XP_ListGetObjectNum(m_directories, 
										   line+1);
	selectDir(dir);
#endif /* USE_ABCOM */
}

void XFE_ABDirListView::selectDir(DIR_Server* dir)
{
	notifyInterested(XFE_ABDirListView::dirSelect, (void *) dir);
}

#if defined(USE_MOTIF_DND)

fe_icon_data*
XFE_ABDirListView::GetDragIconData(int row, int column)
{
	D(("XFE_ABDirListView::GetDragIconData()\n"));
	/* TODO: get line data
	 * determine entry type
	 * return MNC_AddressSmall, or MN_FolderServer
	 */
	fe_icon_data *icon_data = 0;
	if (row < 0) {
#if defined(DEBUG_tao)
		printf("\n XFE_ABDirListView::GetDragIconData (row,col)=(%d,%d)\n",
			   row, column);
#endif
	}/* if */
	else {
		DIR_Server *dirLine = (DIR_Server *) XP_ListGetObjectNum(m_directories, 
																 row+1);
		if (!dirLine)
			return icon_data;

		if (dirLine->dirType == PABDirectory)
			icon_data = &MNC_AddressSmall; /* shall call make/initialize icons */
		else
			icon_data = &MN_FolderServer;
	}/* else */
	return icon_data;
}

void
XFE_ABDirListView::GetDragTargets(int    row, int column,
								  Atom **targets,
								  int   *num_targets)
{
	D(("XFE_ABDirListView::GetDragTargets(row=%d, col=%d)\n", row, column));
	
	XP_ASSERT(row > -1);
	if (row == -1) {
		*targets = NULL;
		*num_targets = 0;
	}/* if */
	else {
		if (!m_outliner->isSelected(row))
			m_outliner->selectItemExclusive(row);
		
		*num_targets = 2;
		
		*targets = new Atom[ *num_targets ];

		(*targets)[0] = XFE_OutlinerDrop::_XA_NETSCAPE_DIRSERV;
		(*targets)[1] = XFE_OutlinerDrop::_XA_NETSCAPE_PAB;

	}/* else */
}

void 
XFE_ABDirListView::getDropTargets(void */*this_ptr*/,
								  Atom **targets,
								  int *num_targets)
{
	D(("XFE_ABDirListView::getDropTargets()\n"));
	*num_targets = 2;
	*targets = new Atom[ *num_targets ];

	(*targets)[0] = XFE_OutlinerDrop::_XA_NETSCAPE_DIRSERV;
	(*targets)[1] = XFE_OutlinerDrop::_XA_NETSCAPE_PAB;
}

char *
XFE_ABDirListView::DragConvert(Atom atom)
{
	/* pack data
	 */
	if (atom == XFE_OutlinerDrop::_XA_NETSCAPE_DIRSERV)	{
#if defined(DEBUG_tao)
		printf("\nXFE_ABDirListView::DragConvert:_XA_NETSCAPE_DIRSERV\n");
#endif		
		
		uint32 count = 0;
		const int *indices = 0;

		m_outliner->getSelection(&indices, (int *) &count);

		char tmp[32];
		sprintf(tmp, "%d", count);

		int len = XP_STRLEN(tmp);
		char *buf = (char *) XtCalloc(len, sizeof(char));
		buf = XP_STRCAT(buf, tmp);		
		for (int i=0; i < count; i++) {
			sprintf(tmp, "%d", indices[i]);
			len += XP_STRLEN(tmp)+1;
			buf = XtRealloc(buf, len);
			buf = XP_STRCAT(buf, " ");		
			buf = XP_STRCAT(buf, tmp);					
		}/* for i */
#if defined(DEBUG_tao)
		printf("\nXFE_ABDirListView::DragConvert:_XA_NETSCAPE_DIRSERV=%x\n", buf);
#endif
		return buf;
	}/* if */
	else if (atom == XFE_OutlinerDrop::_XA_NETSCAPE_PAB)	{
#if defined(DEBUG_tao)
		printf("\nXFE_ABDirListView::DragConvert:_XA_NETSCAPE_PAB\n");
#endif		
		uint32 count = 0;
		const int *indices = 0;

		m_outliner->getSelection(&indices, (int *) &count);

		char tmp[32];
		sprintf(tmp, "%d", count);

		int len = XP_STRLEN(tmp);
		char *buf = (char *) XtCalloc(len, sizeof(char));
		buf = XP_STRCAT(buf, tmp);		
		for (int i=0; i < count; i++) {
			sprintf(tmp, "%d", indices[i]);
			len += XP_STRLEN(tmp)+1;
			buf = XtRealloc(buf, len);
			buf = XP_STRCAT(buf, " ");		
			buf = XP_STRCAT(buf, tmp);					
		}/* for i */
#if defined(DEBUG_tao)
		printf("\nXFE_ABDirListView::DragConvert:_XA_NETSCAPE_PAB=%x\n", buf);
#endif
		return buf;
	}/* else if */
	return (char *) NULL;
}

int
XFE_ABDirListView::ProcessTargets(int row, int col,
								  Atom *targets,
								  const char **data,
								  int numItems)
{
	int i;

	D(("XFE_ABDirListView::ProcessTargets(row=%d, col=%d, numItems=%d)\n", row, col, numItems));
	
	for (i=0; i < numItems; i++) {
		if (targets[i]==None || data[i]==NULL || strlen(data[i])==0)
			continue;
		
		D(("  [%d] %s: \"%s\"\n",i,XmGetAtomName(XtDisplay(m_widget),targets[i]),data[i]));
		if (targets[i] == XFE_OutlinerDrop::_XA_NETSCAPE_DIRSERV) {
#if defined(DEBUG_tao)
			printf("\nXFE_ABDirListView::ProcessTargets:_XA_NETSCAPE_DIRSERV\n");
#endif		
			/* decode
			 */			
			char *pStr = (char *) XP_STRDUP(data[i]);
			int   len = XP_STRLEN(pStr);

			uint32 pCount = 0;
			char tmp[32];
			sscanf(data[i], "%d", &pCount);
			int *indices = (int *) XP_CALLOC(pCount, sizeof(int));
			
			char *tok = 0,
			     *last = 0;
			int   count = 0;
			char *sep = " ";

			while (((tok=XP_STRTOK_R(count?nil:pStr, sep, &last)) != NULL)&&
				   XP_STRLEN(tok) &&
				   count < len) {
				int index = atoi(tok);
				if (!count)
					XP_ASSERT(index == pCount);
				else
					indices[count-1] = 	index;
				count++;
			}/* while */
			return TRUE;
		}/* if */
		else if (targets[i] == XFE_OutlinerDrop::_XA_NETSCAPE_PAB) {
			D(("  [%d] %s: \"%s\"\n",i,XmGetAtomName(XtDisplay(m_widget),targets[i]),data[i]));
#if defined(DEBUG_tao)
			printf("\nXFE_ABDirListView::ProcessTargets:_XA_NETSCAPE_PAB\n");
#endif
			return TRUE;
		}/* elss if */	
	}/* for i */
	return FALSE;
}

/* external entries
 */
fe_icon_data*
XFE_ABDirListView::getDragIconData(void *this_ptr,
								   int   row,
								   int   column)
{
	XFE_ABDirListView *view = (XFE_ABDirListView*)this_ptr;	
	return view->GetDragIconData(row, column);
}

void
XFE_ABDirListView::getDragTargets(void  *this_ptr,
								  int    row, int column,
								  Atom **targets, int *num_targets)
{
	XFE_ABDirListView *view = (XFE_ABDirListView*)this_ptr;	
	view->GetDragTargets(row, column, targets, num_targets);
}

char *
XFE_ABDirListView::dragConvert(void *this_ptr,
							   Atom atom)
{
  XFE_ABDirListView *view = (XFE_ABDirListView*) this_ptr;
  
  return view->DragConvert(atom);
}

int
XFE_ABDirListView::processTargets(void *this_ptr,
								  int row, int col,
								  Atom *targets,
								  const char **data,
								  int numItems)
{
	XFE_ABDirListView *view = (XFE_ABDirListView*)this_ptr;
	
	return view->ProcessTargets(row, col, targets, data, numItems);
}

#else /* USE_MOTIF_DND */

// Address field drop site handler
void
XFE_ABDirListView::dirListDropCallback(Widget, 
									   void          *cd,
									   fe_dnd_Event   type,
									   fe_dnd_Source *source,
#if defined(USE_ABCOM)
									   XEvent        *event) 
#else
									   XEvent        */* event */) 
#endif /* USE_ABCOM */
{
    XFE_ABDirListView *ad = (XFE_ABDirListView *)cd;
    
    if (type == FE_DND_DROP && 
		ad && 
		source)
#if defined(USE_ABCOM)
        ad->dirListDropCB(source, event);
#else
        ad->dirListDropCB(source);
#endif
}

#if defined(USE_ABCOM)
void
XFE_ABDirListView::dirListDropCB(fe_dnd_Source *source, XEvent *event)
{
#if defined(DEBUG_tao)
	printf("\nXFE_ABDirListView::dirListDropCB:srcType=%d\n",
		   source->type);
#endif
	XP_ASSERT(source && event && m_outliner);

	unsigned int state = event->xbutton.state;
    XP_Bool shift = ((state & ShiftMask) != 0),
		    ctrl = ((state & ControlMask) != 0);


	/* onto which 
	 */
	int row = -1;
	int x, y;

	m_outliner->translateFromRootCoords(event->xbutton.x_root, 
										event->xbutton.y_root, 
										&x, &y);
	
	row = m_outliner->XYToRow(x, y);
#if defined(DEBUG_tao)
	printf("\nXFE_ABDirListView::entryListDropCallback,shift=%d, ctrl=%d, row=%d",
		   shift, ctrl, row);
#endif
	if (row < 0 ||
		row >= m_outliner->getTotalLines())
		XP_ASSERT(0);

	AB_ContainerInfo *containerInfo = 
		AB_GetContainerForIndex(m_pane, (MSG_ViewIndex) row);

    switch (source->type) {
    case FE_DND_ADDRESSBOOK:
	case FE_DND_BOOKS_DIRECTORIES: {
		XFE_MNListView* listView = (XFE_MNListView *) source->closure;
		XFE_Outliner *outliner = listView->getOutliner();
		const int *indices = NULL;
		int32 numIndices = 0;
		outliner->getSelection(&indices, (int *) &numIndices);
		MSG_Pane *srcPane = listView->getPane();
		AB_DragEffect effect = 
			AB_DragEntriesIntoContainerStatus(srcPane,
											  (const MSG_ViewIndex *) indices,
											  (int32) numIndices,
											  containerInfo,
											  AB_Default_Drag); 
		int error = 0;
		if (effect == AB_Drag_Not_Allowed)
			return;
		else
			error = AB_DragEntriesIntoContainer(srcPane,
												(const MSG_ViewIndex *) indices,
												(int32) numIndices,
												containerInfo,
												effect);
	}
	
	break;
		
    default:
        break;
    }/* switch */
}

#else
void
XFE_ABDirListView::dirListDropCB(fe_dnd_Source *)
{
}
#endif 
#endif /* USE_MOTIF_DND */

#if defined(USE_ABCOM)

#endif /* USE_ABCOM */
