/* -*- 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.
 */
/* 
   ThreadView.cpp -- presents view of mail threads.
   Created: Chris Toshok <toshok@netscape.com>, 7-Aug-96.
   */



#include "MozillaApp.h"
#include "ThreadView.h"
#include "MsgFrame.h"
#include "FolderFrame.h"
#include "FolderMenu.h"
#include "ViewGlue.h"

#if defined(USE_MOTIF_DND)
#include "OutlinerDrop.h"
#endif /* USE_MOTIF_DND */

#include "xpassert.h"
#include "xfe.h"
#include "icondata.h"
#include "msgcom.h"
//#include "allxpstr.h"
#include "felocale.h" /* for fe_ConvertToLocalEncoding */
#include "xp_mem.h"
#include "Xfe/Xfe.h"

#include <Xm/ArrowB.h>
#include <Xm/Form.h>
#include <Xm/Label.h>
#include <Xm/PanedW.h>

#include "MNSearchFrame.h"
#include "MailFilterDlg.h"
#include "LdapSearchFrame.h"
#include "prefs.h"

#include "prefapi.h"

#ifdef DEBUG_toshok
#define D(x) x
#else
#define D(x)
#endif

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

#define OUTLINER_GEOMETRY_PREF "mail.threadpane.outliner_geometry"
#define MESSAGEPANE_HEIGHT_PREF "mail.threadpane.messagepane_height"

#include "xpgetstr.h"

extern int XFE_RE;
extern int XFE_WINDOW_TITLE_NEWSGROUP;
extern int XFE_WINDOW_TITLE_FOLDER;
extern int XFE_SIZE_IN_LINES;
extern int XFE_SIZE_IN_BYTES;
extern int XFE_THREAD_OUTLINER_COLUMN_SUBJECT;
extern int XFE_THREAD_OUTLINER_COLUMN_DATE;
extern int XFE_THREAD_OUTLINER_COLUMN_PRIORITY;
extern int XFE_THREAD_OUTLINER_COLUMN_STATUS;
extern int XFE_THREAD_OUTLINER_COLUMN_SENDER;
extern int XFE_THREAD_OUTLINER_COLUMN_RECIPIENT;

extern int XP_STATUS_NEW;
extern int XP_STATUS_REPLIED_AND_FORWARDED;
extern int XP_STATUS_FORWARDED;
extern int XP_STATUS_REPLIED;

extern int MK_MSG_CANCEL_MESSAGE;
extern int MK_MSG_DELETE_SEL_MSGS;

extern int XFE_DND_MESSAGE_ERROR;

const int XFE_ThreadView::OUTLINER_COLUMN_SUBJECT = 0;
const int XFE_ThreadView::OUTLINER_COLUMN_SENDERRECIPIENT = 1;
const int XFE_ThreadView::OUTLINER_COLUMN_UNREADMSG = 2;
const int XFE_ThreadView::OUTLINER_COLUMN_DATE = 3;
const int XFE_ThreadView::OUTLINER_COLUMN_PRIORITY = 4;
const int XFE_ThreadView::OUTLINER_COLUMN_FLAG = 5;
const int XFE_ThreadView::OUTLINER_COLUMN_STATUS = 6;
const int XFE_ThreadView::OUTLINER_COLUMN_SIZE = 7;

// Minimum and maximum sizes for the paned window panes:
#define PANE_MIN 50
#define PANE_MAX 6000

fe_icon XFE_ThreadView::threadonIcon = { 0 };
fe_icon XFE_ThreadView::threadoffIcon = { 0 };

MenuSpec XFE_ThreadView::priority_popup_submenu[] = {
  { xfeCmdSetPriorityHighest,	PUSHBUTTON },
  { xfeCmdSetPriorityHigh,	PUSHBUTTON },
  { xfeCmdSetPriorityNormal,	PUSHBUTTON },
  { xfeCmdSetPriorityLow,	PUSHBUTTON },
  { xfeCmdSetPriorityLowest,	PUSHBUTTON },
  { NULL }
};

MenuSpec XFE_ThreadView::news_popup_spec[] = {
  { xfeCmdOpenSelected,     PUSHBUTTON },
// open in new window
  MENU_SEPARATOR,
  // Setting the call data to non-null is a signal to commandToString
  // not to reset the menu string
  { xfeCmdReplyToSender,	PUSHBUTTON, NULL, NULL, NULL, "reply-to-sender" },
  { xfeCmdReplyToAll,	PUSHBUTTON, NULL, NULL, NULL, "reply-to-all" },
  { xfeCmdReplyToNewsgroup,	PUSHBUTTON, NULL, NULL, NULL, "reply-to-newsgroup" },
  { xfeCmdReplyToSenderAndNewsgroup,    PUSHBUTTON, NULL, NULL, NULL, "reply-to-sender-and-newsgroup" },
  { xfeCmdForwardMessage,		PUSHBUTTON },
  { xfeCmdForwardMessageQuoted,	PUSHBUTTON },
  MENU_SEPARATOR,
  { "addToABSubmenu",   CASCADEBUTTON,
    (MenuSpec *) &addrbk_submenu_spec },
  MENU_SEPARATOR,
  { xfeCmdIgnoreThread,		PUSHBUTTON },
  { xfeCmdWatchThread,		PUSHBUTTON },
  MENU_SEPARATOR,
  { "fileSubmenu",              DYNA_CASCADEBUTTON, NULL, NULL, False, (void*)xfeCmdMoveMessage, XFE_FolderMenu::generate },
  { xfeCmdDeleteMessage,	PUSHBUTTON },
  { xfeCmdSaveMessagesAs,	PUSHBUTTON },
  { xfeCmdPrint,		PUSHBUTTON },
  { NULL }

};

MenuSpec XFE_ThreadView::mail_popup_spec[] = {
  { xfeCmdOpenSelected,     PUSHBUTTON },
  MENU_SEPARATOR,
  // Setting the call data to non-null is a signal to commandToString
  // not to reset the menu string
  { xfeCmdReplyToSender,	PUSHBUTTON, NULL, NULL, NULL, "reply-to-sender" },
  { xfeCmdReplyToAll,		PUSHBUTTON, NULL, NULL, NULL, "reply-to-all" },
  { xfeCmdForwardMessage,	PUSHBUTTON },
  { xfeCmdForwardMessageQuoted,	PUSHBUTTON },
  MENU_SEPARATOR,
  { "addToABSubmenu",   CASCADEBUTTON,
    (MenuSpec *) &addrbk_submenu_spec },
  MENU_SEPARATOR,
  { xfeCmdIgnoreThread,		PUSHBUTTON },
  { xfeCmdWatchThread,		PUSHBUTTON },
  MENU_SEPARATOR,
  { "changePriority",		CASCADEBUTTON, priority_popup_submenu },
  MENU_SEPARATOR,
  { "fileSubmenu",              DYNA_CASCADEBUTTON, NULL, NULL, False, (void*)xfeCmdMoveMessage, XFE_FolderMenu::generate },
  { xfeCmdDeleteMessage,	PUSHBUTTON },
  { xfeCmdSaveMessagesAs,	PUSHBUTTON },
  { xfeCmdPrint,		PUSHBUTTON },
  { NULL }
};

MenuSpec XFE_ThreadView::addrbk_submenu_spec[] = {
	{ xfeCmdAddSenderToAddressBook,	PUSHBUTTON },
	{ xfeCmdAddAllToAddressBook,		PUSHBUTTON },
	//  { xfeCmdAddCardToAddressBook,		PUSHBUTTON } ?
	{ NULL }
};

XFE_ThreadView::XFE_ThreadView(XFE_Component *toplevel_component,
			       Widget parent,
			       XFE_View *parent_view, MWContext *context,
			       MSG_Pane *p) 
  : XFE_MNListView(toplevel_component, parent_view, context, p)
{
  /* initialize
   */
  m_nLoadingFolders = 0;

  m_frameDeleted = False;

#if HANDLE_CMD_QUEUE

  m_lastLoadedInd = MSG_VIEWINDEXNONE;
  m_lastLoadedKey = MSG_MESSAGEKEYNONE;
  m_lineChanged = MSG_VIEWINDEXNONE;

  m_selected = NULL;
  m_selectedCount = 0;

  m_deleted = NULL;
  m_deletedCount = 0;

  m_collapsed = NULL;
  m_collapsedCount = 0;

#endif /* HANDLE_CMD_QUEUE */

  Widget panedw;
  int num_columns = 8;
  XtWidgetGeometry arrow_geo;
  static int default_column_widths[] = {30, 20, 3, 20, 9, 3, 15, 6};

  panedw = XtVaCreateWidget("panedw",
							xmPanedWindowWidgetClass,
							parent,
							NULL);


  // create the outliner message list.
  m_outliner = new XFE_Outliner("messageList",
								this,
								getToplevel(),
								panedw,
								FALSE,  // constantSize
								TRUE,   // hasHeadings
								num_columns, 
								5,
								default_column_widths,
								OUTLINER_GEOMETRY_PREF);

  // initialize the icons if they haven't already been
  {
    Pixel bg_pixel;
    
    XtVaGetValues(m_outliner->getBaseWidget(), XmNbackground, &bg_pixel, 0);

	initMessageIcons(getToplevel()->getBaseWidget(),
					 getToplevel()->getFGPixel(),
					 bg_pixel);
  }

  // now that we've created the icons, we can size the two icon-only columns to be
  // the widths required of the pixmaps.

  m_outliner->setColumnWidth ( OUTLINER_COLUMN_UNREADMSG, msgUnreadIcon.width + 2 /* for the outliner's shadow */);
  m_outliner->setColumnWidth ( OUTLINER_COLUMN_FLAG, msgFlagIcon.width + 2 /* for the outliner's shadow */);

  m_outliner->setPipeColumn( OUTLINER_COLUMN_SUBJECT );
  m_outliner->setMultiSelectAllowed( True );

  m_outliner->setColumnResizable( OUTLINER_COLUMN_UNREADMSG, False );
  m_outliner->setColumnResizable( OUTLINER_COLUMN_FLAG, False );

  m_outliner->setHideColumnsAllowed( True );

#if !defined(USE_MOTIF_DND)
  fe_dnd_CreateDrop(m_outliner->getBaseWidget(), drop_func, this);
#endif /* USE_MOTIF_DND */

  XtVaSetValues(m_outliner->getBaseWidget(),
				XmNallowResize, TRUE,
				XmNpaneMinimum, PANE_MIN, // should this be a resource?
				XmNpaneMaximum, PANE_MAX, // why is the limit in HPaned 1000???
				NULL);

  m_outliner->show();

  // create the arrow button stuff

  m_arrowform = XtVaCreateManagedWidget("arrowform",
										xmFormWidgetClass,
										panedw,
										XmNallowResize, FALSE,
										XmNskipAdjust, TRUE,
										NULL);

  m_arrowb = XtVaCreateManagedWidget("arrowb",
				     xmArrowButtonWidgetClass,
				     m_arrowform,
				     XmNleftAttachment, XmATTACH_FORM,
				     XmNtopAttachment, XmATTACH_FORM,
				     XmNbottomAttachment, XmATTACH_FORM,
				     XmNrightAttachment, XmATTACH_NONE,
				     XmNarrowDirection, XmARROW_DOWN,
				     XmNshadowThickness, 0,
				     NULL);

  XtAddCallback(m_arrowb, XmNactivateCallback, toggleMsgExpansionCallback, this);

  // Arrow label should be blank initially.
  XmString tempString = XmStringCreateSimple(" ");
  m_arrowlabel = XtVaCreateManagedWidget("arrowlabel",
					 xmLabelWidgetClass,
					 m_arrowform,
					 XmNleftAttachment, XmATTACH_WIDGET,
					 XmNleftWidget, m_arrowb,
					 XmNtopAttachment, XmATTACH_FORM,
					 XmNbottomAttachment, XmATTACH_FORM,
					 XmNrightAttachment, XmATTACH_FORM,
					 XmNalignment, XmALIGNMENT_BEGINNING,
					 XmNlabelString, tempString,
					 NULL);
  XmStringFree(tempString);

  arrow_geo.request_mode = CWHeight;
  XtQueryGeometry(m_arrowform, NULL, &arrow_geo);

  XtVaSetValues(m_arrowform,
		XmNpaneMaximum, arrow_geo.height,
		XmNpaneMinimum, arrow_geo.height,
		NULL);

  // create the message viewing area.
  m_msgview = new XFE_MsgView(toplevel_component, panedw, this, m_contextData);
  addView(m_msgview);

  XtVaSetValues(m_msgview->getBaseWidget(),
				XmNallowResize, TRUE,
				XmNpaneMinimum, PANE_MIN, // should this be a resource?
				XmNpaneMaximum, PANE_MAX, // why is the limit in PanedW 1000???
				NULL);

  m_msgview->registerInterest(XFE_MsgView::spacebarAtMsgBottom,
			      this,
			      (XFE_FunctionNotification)spaceAtMsgEnd_cb);

  m_msgview->registerInterest(XFE_MsgView::messageHasChanged,
							  this,
							  (XFE_FunctionNotification)newMessageLoading_cb);

  getToplevel()->registerInterest(XFE_Frame::allConnectionsCompleteCallback,
								  this,
								  (XFE_FunctionNotification)allConnectionsComplete_cb);

  if (!p)
    setPane(MSG_CreateThreadPane(m_contextData,
				 XFE_MNView::m_master));


  m_msgExpanded = TRUE;

  {
	  Widget scrolled = XtNameToWidget (m_msgview->getBaseWidget(), "*scroller");
	  int32 message_pane_desired_height;
	  PREF_GetIntPref(MESSAGEPANE_HEIGHT_PREF, &message_pane_desired_height);

	  XtVaSetValues(scrolled,
					XmNheight, message_pane_desired_height,
					NULL);
	  
	  m_msgview->show();
  }

  /* safe default, for now.  It'll be overriden (if need be), when
	 a folder is loaded. */

  m_displayingDraft = FALSE;

  m_commandPending = noPendingCommand;
  m_getNewMsg = False;

  m_selectionAfterDeleting = MSG_VIEWINDEXNONE;
  m_popup = NULL;
  m_folderInfo = NULL;

  setBaseWidget(panedw);
}

XFE_ThreadView::~XFE_ThreadView()
{
D(printf ("In XFE_ThreadView::~XFE_ThreadView()\n");)


	/* save off the height of the message pane. */
	{
		Dimension message_pane_height;
		Widget scrolled = XtNameToWidget (m_msgview->getBaseWidget(), "*scroller");
		
		XtVaGetValues(scrolled,
					  XmNheight, &message_pane_height,
					  NULL);
		
		PREF_SetIntPref(MESSAGEPANE_HEIGHT_PREF, (int32)message_pane_height);
	}		

  delete m_outliner;

  destroyPane();

  if (m_popup)
    delete m_popup;

  // XFE_View destroys m_msgview for us.
}

void XFE_ThreadView::setGetNewMsg(XP_Bool b)
{
	m_getNewMsg = b;
}

fe_icon*
XFE_ThreadView::flagToIcon(int folder_flags, int message_flags)
{
	if (folder_flags & MSG_FOLDER_FLAG_NEWSGROUP)
		return (message_flags & MSG_FLAG_READ ? &newsPostIcon : &newsNewIcon);
	else if (folder_flags & MSG_FOLDER_FLAG_DRAFTS)
		return &draftIcon;
    else if (message_flags & MSG_FLAG_IMAP_DELETED
             || message_flags & MSG_FLAG_EXPUNGED)
        return &deletedIcon;
	else
		return (message_flags & MSG_FLAG_READ ? &mailMessageReadIcon : &mailMessageUnreadIcon);
}

#if defined(USE_MOTIF_DND)
fe_icon_data*
XFE_ThreadView::flagToIconData(int folder_flags, int message_flags)
{
  if (folder_flags & MSG_FOLDER_FLAG_NEWSGROUP)
	return (message_flags & MSG_FLAG_READ ? &MN_Newspost : &MN_NewsNew);
  else if (folder_flags & MSG_FOLDER_FLAG_DRAFTS)
	return &MN_Draftfile;
  else if (message_flags & MSG_FLAG_IMAP_DELETED
		   || message_flags & MSG_FLAG_EXPUNGED)
	return &MN_Delete;
  else
	return (message_flags & MSG_FLAG_READ ? &MN_MailRead : &MN_MailUnread);
}
#endif /* USE_MOTIF_DND */

void
XFE_ThreadView::initMessageIcons(Widget widget,
								 Pixel bg_pixel,
								 Pixel fg_pixel)
{
	static Boolean init_icons_done = False;

	if (init_icons_done)
		return;

	init_icons_done = True;

    if (!mailMessageReadIcon.pixmap)
		fe_NewMakeIcon(widget,
					   fg_pixel,
					   bg_pixel,
					   &mailMessageReadIcon,
					   NULL, 
					   MN_MailRead.width, MN_MailRead.height,
					   MN_MailRead.mono_bits, MN_MailRead.color_bits, MN_MailRead.mask_bits, FALSE); 

    if (!mailMessageUnreadIcon.pixmap)
		fe_NewMakeIcon(widget,
					   fg_pixel,
					   bg_pixel,
					   &mailMessageUnreadIcon,
					   NULL, 
					   MN_MailUnread.width, MN_MailUnread.height,
					   MN_MailUnread.mono_bits, MN_MailUnread.color_bits, MN_MailUnread.mask_bits, FALSE);

    if (!draftIcon.pixmap)
		fe_NewMakeIcon(widget,
					   fg_pixel,
					   bg_pixel,
					   &draftIcon,
					   NULL, 
					   MN_Draftfile.width, MN_Draftfile.height,
					   MN_Draftfile.mono_bits, MN_Draftfile.color_bits, MN_Draftfile.mask_bits, FALSE);
	
    if (!newsPostIcon.pixmap)
		fe_NewMakeIcon(widget,
					   fg_pixel,
					   bg_pixel,
					   &newsPostIcon,
					   NULL, 
					   MN_Newspost.width, MN_Newspost.height,
					   MN_Newspost.mono_bits, MN_Newspost.color_bits, MN_Newspost.mask_bits, FALSE);

    if (!newsNewIcon.pixmap)
		fe_NewMakeIcon(widget,
					   fg_pixel,
					   bg_pixel,
					   &newsNewIcon,
					   NULL, 
					   MN_NewsNew.width, MN_NewsNew.height,
					   MN_NewsNew.mono_bits, MN_NewsNew.color_bits, MN_NewsNew.mask_bits, FALSE);
	
    if (!msgReadIcon.pixmap)
		fe_NewMakeIcon(widget,
					   fg_pixel,
					   bg_pixel,
					   &msgReadIcon,
					   NULL, 
					   MN_DotRead.width, MN_DotRead.height,
					   MN_DotRead.mono_bits, MN_DotRead.color_bits, MN_DotRead.mask_bits, FALSE);
	
    if (!msgUnreadIcon.pixmap)
		fe_NewMakeIcon(widget,
					   fg_pixel,
					   bg_pixel,
					   &msgUnreadIcon,
					   NULL, 
					   MN_Unread.width, MN_Unread.height,
					   MN_Unread.mono_bits, MN_Unread.color_bits, MN_Unread.mask_bits, FALSE);

    if (!msgFlagIcon.pixmap)
		fe_NewMakeIcon(widget,
					   fg_pixel,
					   bg_pixel,
					   &msgFlagIcon,
					   NULL, 
					   MN_Flag.width, MN_Flag.height,
					   MN_Flag.mono_bits, MN_Flag.color_bits, MN_Flag.mask_bits, FALSE);
    
	if (!threadonIcon.pixmap)
		fe_NewMakeIcon(widget,
					   fg_pixel,
					   bg_pixel,
					   &threadonIcon,
					   NULL, 
					   threadon.width, threadon.height,
					   threadon.mono_bits, threadon.color_bits, 
					   threadon.mask_bits, FALSE);
    
	if (!threadoffIcon.pixmap)
		fe_NewMakeIcon(widget,
					   fg_pixel,
					   bg_pixel,
					   &threadoffIcon,
					   NULL, 
					   threadoff.width, threadoff.height,
					   threadoff.mono_bits, threadoff.color_bits, 
					   threadoff.mask_bits, FALSE);

	if (!openSpoolIgnoredIcon.pixmap)
		fe_NewMakeIcon(widget,
					   fg_pixel,
					   bg_pixel,
					   &openSpoolIgnoredIcon,
					   NULL,
					   MN_ThreadIgnoreO.width, MN_ThreadIgnoreO.height,
					   MN_ThreadIgnoreO.mono_bits, MN_ThreadIgnoreO.color_bits,
					   MN_ThreadIgnoreO.mask_bits, FALSE);

	if (!closedSpoolIgnoredIcon.pixmap)
		fe_NewMakeIcon(widget,
					   fg_pixel,
					   bg_pixel,
					   &closedSpoolIgnoredIcon,
					   NULL,
					   MN_ThreadIgnore.width, MN_ThreadIgnore.height,
					   MN_ThreadIgnore.mono_bits, MN_ThreadIgnore.color_bits,
					   MN_ThreadIgnore.mask_bits, FALSE);

	if (!openSpoolWatchedIcon.pixmap)
		fe_NewMakeIcon(widget,
					   fg_pixel,
					   bg_pixel,
					   &openSpoolWatchedIcon,
					   NULL,
					   MN_ThreadWatchO.width, MN_ThreadWatchO.height,
					   MN_ThreadWatchO.mono_bits, MN_ThreadWatchO.color_bits,
					   MN_ThreadWatchO.mask_bits, FALSE);

	if (!closedSpoolWatchedIcon.pixmap)
		fe_NewMakeIcon(widget,
					   fg_pixel,
					   bg_pixel,
					   &closedSpoolWatchedIcon,
					   NULL,
					   MN_ThreadWatch.width, MN_ThreadWatch.height,
					   MN_ThreadWatch.mono_bits, MN_ThreadWatch.color_bits,
					   MN_ThreadWatch.mask_bits, FALSE);

	if (!openSpoolNewIcon.pixmap)
		fe_NewMakeIcon(widget,
					   fg_pixel,
					   bg_pixel,
					   &openSpoolNewIcon,
					   NULL,
					   MN_ThreadNew/*O*/.width, MN_ThreadNew/*O*/.height,
					   MN_ThreadNew/*O*/.mono_bits, MN_ThreadNew/*O*/.color_bits,
					   MN_ThreadNew/*O*/.mask_bits, FALSE);

	if (!closedSpoolNewIcon.pixmap)
		fe_NewMakeIcon(widget,
					   fg_pixel,
					   bg_pixel,
					   &closedSpoolNewIcon,
					   NULL,
					   MN_ThreadNew.width, MN_ThreadNew.height,
					   MN_ThreadNew.mono_bits, MN_ThreadNew.color_bits,
					   MN_ThreadNew.mask_bits, FALSE);

	if (!openSpoolIcon.pixmap)
		fe_NewMakeIcon(widget,
					   fg_pixel,
					   bg_pixel,
					   &openSpoolIcon,
					   NULL,
					   MN_ThreadO.width, MN_ThreadO.height,
					   MN_ThreadO.mono_bits, MN_ThreadO.color_bits,
					   MN_ThreadO.mask_bits, FALSE);

	if (!closedSpoolIcon.pixmap)
		fe_NewMakeIcon(widget,
					   fg_pixel,
					   bg_pixel,
					   &closedSpoolIcon,
					   NULL,
					   MN_Thread.width, MN_Thread.height,
					   MN_Thread.mono_bits, MN_Thread.color_bits,
					   MN_Thread.mask_bits, FALSE);

	if (!deletedIcon.pixmap)
		fe_NewMakeIcon(widget,
					   fg_pixel,
					   bg_pixel,
					   &deletedIcon,
					   NULL,
					   MN_Delete.width, MN_Delete.height,
					   MN_Delete.mono_bits, MN_Delete.color_bits,
					   MN_Delete.mask_bits, FALSE);
}

#if HANDLE_CMD_QUEUE

/* It looks like a selectInvalid here
 */
void XFE_ThreadView::processCmdQueue()
{
	int count =0;
	int *Arr = NULL;

	if (m_deletedCount && m_deleted) {
		count = m_deletedCount;
		Arr = m_deleted;
	}/* else if */

#if defined(DEBUG_tao)
	printf("\n->XFE_ThreadView::processCmdQueue, count=%d", count);
	if (count > 1)
		XP_ASSERT(0);
#endif

	int lastInd = count?Arr[count-1]:-1;
	for (int i=count-2; i >= 0; i--)
		if (Arr[i] < lastInd)
			lastInd--;

	int nLines = MSG_GetNumLines(m_pane);
	if (lastInd >= nLines)
		/* the worst case is -1 ;
		 * which we do deal with it in paneChanged already!
		 */
		lastInd = nLines-1;

	if (lastInd >= 0 && 
		m_lastLoadedInd != MSG_VIEWINDEXNONE)
		showMessage(lastInd);
#if defined(DEBUG_tao)
	else
		printf("\n->XFE_ThreadView::processCmdQueue,m_lastLoadedInd=%d,",
			   m_lastLoadedInd);	
#endif

	/* final step
	 */

	/* reset 
	 */
	XP_FREEIF(m_selected);
	m_selectedCount = 0;
	XP_FREEIF(m_deleted);
	m_deletedCount = 0;
	XP_FREEIF(m_collapsed);
	m_collapsedCount = 0;
}

#endif /* HANDLE_CMD_QUEUE */

#if defined(DEL_5_0)
void
XFE_ThreadView::listChangeStarting(XP_Bool /* asynchronous */, 
								   MSG_NOTIFY_CODE notify, 
								   MSG_ViewIndex /* where */, 
								   int32 /* num */)
{
	int listChangeDepth = m_outliner->getListChangeDepth()-1;
#if defined(DEBUG_tao)
	printf("\n>>XFE_ThreadView::listChangeStarting, depth=%d, m_lastLoadedInd=%d", 
		   listChangeDepth, m_lastLoadedInd);
#endif

	if (notify == MSG_NotifyScramble ||
		notify == MSG_NotifyAll) {
		m_lastLoadedInd = MSG_VIEWINDEXNONE;
	}/* if */
}
#endif /* DEL_5_0 */

void
XFE_ThreadView::listChangeFinished(XP_Bool /* asynchronous */, 
								   MSG_NOTIFY_CODE notify, 
								   MSG_ViewIndex where, 
								   int32 num)
{
	int listChangeDepth = m_outliner->getListChangeDepth()-1;
#if defined(DEBUG_tao)
	printf("\n>>XFE_ThreadView::listChangeFinished, depth=%d, m_lastLoadedInd=%d", 
		   listChangeDepth, m_lastLoadedInd);
#endif

#if defined(DEL_5_0)
	if (notify == MSG_NotifyScramble ||
		notify == MSG_NotifyAll) {
		m_lastLoadedInd = MSG_GetMessageIndexForKey(m_pane, 
													m_lastLoadedKey,
													False);
	}/* if */
	else if (notify == MSG_NotifyChanged) {
		m_lineChanged = where;
		return;
	}/* if */
	else if (notify == MSG_NotifyInsertOrDelete &&
			 m_lastLoadedInd != MSG_VIEWINDEXNONE) {
		/* there was a msg loaded
		 */
		if (num < 0) {
			/* 1. collapsing ??
			 */
			if ((m_lineChanged != MSG_VIEWINDEXNONE) &&
				(m_lineChanged == (where -1)) && /* essential */
				(listChangeDepth == 0)) { /* must be collapsing */
				/* collapsed
				 */
				if ((m_lastLoadedInd >= where) && 
					(m_lastLoadedInd < (where-num))) { 
					/* one of the "reply" is being hidden
					 * selection would go to thread leader; 
					 * thus needs reload right away
					 */
					/* load the thread leader: where -1*/
					if (!m_deletedCount && !m_deleted) {
						m_deleted = (int *) XP_CALLOC(1, sizeof(int));
						m_deleted[m_deletedCount++] = where-1;		
						processCmdQueue();
					}/* if */
					else
						XP_ASSERT(0);
				}/* if collapsed */
				else {
#if defined(DEBUG_tao)
					printf("\n***XFE_ThreadView::listChangeFinished, collapsing not selected\n");
#endif /* DEBUG_tao */
					m_lastLoadedInd += num; /* re-adjust */
				}/* else */
			}/* collapsed */
			else if (where == m_lastLoadedInd) {
				/* the selected one is gone
				 */
				if (!m_deletedCount && !m_deleted) {
					m_deleted = (int *) XP_CALLOC(1, sizeof(int));
					m_deleted[m_deletedCount++] = where;		
					processCmdQueue();
				}/* if */
				else
					XP_ASSERT(0);
			}/* where == m_lastLoadedInd */
			else if (where <= m_lastLoadedInd)
				m_lastLoadedInd += num; /* re-adjust */
		}/* num < 0 */
		else if (num == 0) {
			/* SPECIAL case: the header got deleted
			 */

			/* we do not care deletion on non-focusing line
			 */
			if (m_lastLoadedInd == where) {
				/* deleted
				 */
				if (!m_deletedCount && !m_deleted) {
					m_deleted = (int *) XP_CALLOC(1, sizeof(int));
					m_deleted[m_deletedCount++] = where;		
					processCmdQueue();
				}/* if */
				else
					XP_ASSERT(0);

#if defined(DEBUG_tao)
				printf("\n**>SPECIAL case:listChangeFinished=%d, m_deletedCount=%d\n", 
					   where, m_deletedCount);
#endif		
			}/* m_lastLoadedInd == where */
		}/* if num == 0 */
		else if (num > 0 && where <= m_lastLoadedInd)
			m_lastLoadedInd += num;

	}/* notify == MSG_NotifyInsertOrDelete */
#if defined(DEBUG_tao)
	printf("\n<<XFE_ThreadView::listChangeFinished, m_lastLoadedInd=%d", 
		   m_lastLoadedInd);
#endif


#else
	if (notify == MSG_NotifyChanged) {
		m_lineChanged = where;
		return;
	}/* if */
	else if (notify == MSG_NotifyInsertOrDelete && 
			 num <= 0) {

		if (num == 0) {
			/* SPECIAL case: the header got deleted
			 */

			/* we do not care deletion on non-focusing line
			 */
			if (m_lastLoadedInd != where)
				return;

			/* deleted
			 */
			if (m_deletedCount && m_deleted)
				m_deleted = (int *) XP_REALLOC(m_deleted, 
											   (m_deletedCount+1)*sizeof(int));
			else
				m_deleted = (int *) XP_CALLOC(1, sizeof(int));
			m_deleted[m_deletedCount++] = where;		
#if defined(DEBUG_tao)
			printf("\n**>SPECIAL case:listChangeFinished=%d, m_deletedCount=%d\n", 
				   where, m_deletedCount);
#endif		
			
		}/* if */
		else if ((m_lineChanged != MSG_VIEWINDEXNONE) &&
			(m_lineChanged == (where -1)) && /* essential */
			(listChangeDepth == 0)) { /* must be collapsing */
			/* collapsed
			 */
			if ((m_lastLoadedInd >= where) && 
				(m_lastLoadedInd < (where-num))) { 
				/* one of the "reply" is being hidden
				 * selection would go to thread leader; 
				 * thus needs reload right away
				 */
				if (!m_deletedCount && !m_deleted) {
					m_deleted = (int *) XP_CALLOC(1, sizeof(int));
					m_deleted[m_deletedCount++] = where-1;		
					processCmdQueue();
				}/* if */
			}/* if */
		}/* if */
		else if (num == -1){
			/* we do not care deletion on non-focusing line
			 */
			if (m_lastLoadedInd != where)
				return;

			/* deleted
			 */
			if (m_deletedCount && m_deleted)
				m_deleted = (int *) XP_REALLOC(m_deleted, 
											   (m_deletedCount+1)*sizeof(int));
			else
				m_deleted = (int *) XP_CALLOC(1, sizeof(int));
			m_deleted[m_deletedCount++] = where;		
#if defined(DEBUG_tao)
			printf("\n-->listChangeFinished=%d, m_deletedCount=%d\n", 
				   where, m_deletedCount);
#endif		
		}/* else if num == -1 */
	}/* else if notify == MSG_NotifyInsertOrDelete && num < 0*/
#endif /* DEL_5_0 */

	m_lineChanged = MSG_VIEWINDEXNONE;
}/* XFE_ThreadView::listChangeFinished() */

void
XFE_ThreadView::paneChanged(XP_Bool asynchronous,
							MSG_PANE_CHANGED_NOTIFY_CODE code,
							int32 value)
{
#if defined(DEBUG_tao)
	printf("\n  XFE_ThreadView::paneChanged, pane=0x%x, asynchronous=%d, code=%d, value=%d",
		   m_pane, asynchronous, code, value);
#endif

  switch (code)
    {
	case MSG_PaneNotifyFolderDeleted:
		{
			/* bug 95564: check folderInfo before closing
			 */
			MSG_FolderInfo *deleted = (MSG_FolderInfo *) value;
			if (m_folderInfo == deleted) {
				/* test frame type
				 */
				XFE_Frame *frame = (XFE_Frame*)m_toplevel;
				if (frame &&
					FRAME_MAILNEWS_THREAD == frame->getType()) {
					/* standalone frame
					 */	
					if (!m_frameDeleted) {
						frame->delete_response();
						m_frameDeleted = True;
					}/* if */
				}/* if */
			}/* if m_folderInfo == deleted */
#if defined(DEBUG_tao)
			else
				printf("\n---Wrong folder IGNORing MSG_PaneNotifyFolderDeleted\n");
#endif
		}
		/* shall we update banner or simply return?
		 */
		return;

	case MSG_PaneNotifyCopyFinished:
#if defined(DEBUG_tao)
		printf("\n++XFE_ThreadView, MSG_PaneNotifyCopyFinished++\n");
		/* It happens in loading mails.
		 * XP_ASSERT(0);
		 */ 
#endif
		break;

	case MSG_PaneNotifyMessageLoaded:
#if defined(DEBUG_tao)
		printf("\n++XFE_ThreadView, MSG_PaneNotifyMessageLoaded++\n");
		XP_ASSERT(0);
#endif
		break;

	case MSG_PaneNotifyLastMessageDeleted:
		/* clear msgPane
		 */
		showMessage(-1);
		break;

	case MSG_PaneNotifyMessageDeleted:
		{
		// If we delete a message from the threadview, and
		// there's currently a message view showing that message,
		// we need to tell the msg view so it can pop down:
		XFE_MozillaApp::theApp()->notifyInterested(msgWasDeleted, (void*)value);

#if HANDLE_CMD_QUEUE

		MessageKey id = (MessageKey) value;
		MSG_ViewIndex index = MSG_GetMessageIndexForKey(m_pane, 
														id,
														False);
#if defined(DEBUG_tao)
		printf("\n MSG_PaneNotifyMessageDeleted,index=%d", index);
#endif
#endif /* HANDLE_CMD_QUEUE */

		}
		break;

	case MSG_PaneNotifyFolderLoadedSync:
	case MSG_PaneNotifyFolderLoaded:
	{
		D(printf ("Inside pane changed -- folder loaded\n");)
#if defined(DEBUG_tao)
		if (code == MSG_PaneNotifyFolderLoadedSync)
			printf("\n**XFE_ThreadView::paneChanged: code == MSG_PaneNotifyFolderLoadedSync \n");
#endif
		if (m_nLoadingFolders > 0) {
			/* release the block
			 */
			m_outliner->setBlockSel(False);

			m_nLoadingFolders--;
		}/* if */
		
		/* Call this after folder is loaded
		 */
		if ((code == MSG_PaneNotifyFolderLoaded) &&
			m_getNewMsg) {

			/* Do it for movemail/pop3 only
			 */ 
			if (fe_globalPrefs.mail_server_type != MAIL_SERVER_IMAP)
				getNewMail();

			/* reset
			 */
			m_getNewMsg = False;
		}/* if */

		/* select first msg
		 */
		if (m_commandPending == noPendingCommand
			&& m_outliner->getTotalLines() > 0)
			{
				m_commandPending = selectByIndex;
				m_pendingSelectionIndex = 0;
			}

		/* we only actually handle the command here 
		 * if the folder was loaded synchronously. 
		 */
		if (code == MSG_PaneNotifyFolderLoadedSync)
			handlePendingCommand();

		XFE_MozillaApp::theApp()->notifyInterested(XFE_MNView::MNChromeNeedsUpdating);

		notifyInterested(XFE_MNView::bannerNeedsUpdating, (void*)code);

		XP_Bool invalidate_needed = False;
		if (MSG_GetToggleStatus(m_pane, MSG_SortByThread, NULL, 0) == MSG_Checked)
			{
				invalidate_needed = m_outliner->getDescendantSelectAllowed() == False;
				m_outliner->setDescendantSelectAllowed( True );
				m_outliner->setSortColumn(-1, OUTLINER_SortAscending);
			}
		else
			{
				invalidate_needed = m_outliner->getDescendantSelectAllowed() == True;
				m_outliner->setDescendantSelectAllowed( False );
			}

		if (invalidate_needed)
			m_outliner->invalidate();
	}
		break;

	default:
		break;
    }

    // This can result in flickering of the banner, but if we don't call it,
    // the number of unread and total messages isn't updated correctly
    XFE_MNListView::paneChanged(asynchronous, code, value);
}

void
XFE_ThreadView::loadFolder(MSG_FolderInfo *folderInfo)
{
  MSG_FolderLine folderLine;
  char *newsgroup_format = XP_GetString(XFE_WINDOW_TITLE_NEWSGROUP);
  char *folder_format = XP_GetString(XFE_WINDOW_TITLE_FOLDER);
  char *window_title;
  int window_title_length;

  // only need to do everything below here if the folder being
  // loaded is different than what's currently displayed.
  if (m_folderInfo != folderInfo) {
	  
	  // must be valid.
	  XP_ASSERT(folderInfo);
	  
	  // clear the currently display message, since we're in a different folder now.
	  showMessage(-1);
	  updateExpandoFlippyText(-1);

	  m_outliner->deselectAllItems();
	  
	  MSG_GetFolderLineById(XFE_MNView::getMaster(), folderInfo, &folderLine);

#if defined(USE_MOTIF_DND)
	  m_outliner->enableDragDrop(this,
								 NULL, /* no dropping -- of anything but outliner columns. */
								 XFE_ThreadView::getDragTargets,
								 XFE_ThreadView::getDragIconData,
								 XFE_ThreadView::dragConvert);
#endif /* USE_MOTIF_DND */
	  
	  if (folderLine.flags & MSG_FOLDER_FLAG_NEWSGROUP)
		  {
			  // it's a newsgroup
			  window_title_length = strlen(newsgroup_format) - 2 /* %s */ + strlen(folderLine.name) + 1;
			  
			  window_title = (char*)XP_CALLOC(sizeof(char), window_title_length);
			  
			  sprintf (window_title, newsgroup_format, folderLine.name);
			  
			  m_displayingNewsgroup = TRUE;
			  m_displayingDraft = FALSE;
			  
#if !defined(USE_MOTIF_DND)
			  m_outliner->setDragType( FE_DND_NEWS_MESSAGE, &newsPostIcon,
									   this, (fe_dnd_SourceDropFunc)&XFE_ThreadView::source_drop_func);
#endif /* USE_MOTIF_DND */
			  
		  }
	  else 
		  {
			  window_title_length = strlen(folder_format) - 2 /* %s */ + strlen(folderLine.name) + 1;
			  
			  window_title = (char*)XP_CALLOC(sizeof(char), window_title_length);
			  
			  sprintf (window_title, folder_format, folderLine.name);
					  

			  if (folderLine.flags & MSG_FOLDER_FLAG_DRAFTS)
				  {
					  // it's the draft folder
					  m_displayingDraft = TRUE;
					  m_displayingNewsgroup = FALSE;
#if !defined(USE_MOTIF_DND)

					  m_outliner->setDragType(FE_DND_MAIL_MESSAGE, &mailMessageUnreadIcon,
											  this, 
											  (fe_dnd_SourceDropFunc)&XFE_ThreadView::source_drop_func);
#endif /* USE_MOTIF_DND */
				  }
			  else
				  {
					  // it's a normal mail folder
					  m_displayingNewsgroup = FALSE;
					  m_displayingDraft = FALSE;
					  
#if !defined(USE_MOTIF_DND)
					  m_outliner->setDragType(FE_DND_MAIL_MESSAGE, &mailMessageUnreadIcon, 
											  this,
											  (fe_dnd_SourceDropFunc)&XFE_ThreadView::source_drop_func);
#endif /* USE_MOTIF_DND */
				  }
		  }
	  
	  //  getContainer()->setTitle(window_title);
	  
	  XP_FREE(window_title);
	  
	  D(printf("Loading folder into ThreadView\n");)
		  
	  /* if it doesn't have categories, we can load it directly
		 into the threadview's pane. */
	  m_folderInfo = folderInfo;
			  
	  /* keep track of on loading folders
	   */
	  if (m_nLoadingFolders == 0) {
		  m_nLoadingFolders++; // FYI

		  /* block selection
		   */
		  m_outliner->setBlockSel(True);
	  }/* if */ 
	  else
		  XP_ASSERT(0);

	  MSG_LoadFolder(m_pane, m_folderInfo);


	  XFE_MozillaApp::theApp()->notifyInterested(XFE_MNView::MNChromeNeedsUpdating);
	  /* either open or close the message view, depending on the folder prefs */
	  {
		  int32 new_folder_prefs = MSG_GetFolderPrefFlags(m_folderInfo);
		  
		  if (new_folder_prefs & MSG_FOLDER_PREF_FEVALID)
			  {
				  if (new_folder_prefs & MSG_FOLDER_PREF_ONEPANE)
					  {
						  if (m_msgview->isShown())
							  {
								  toggleMsgExpansion();
							  }
					  }
				  else
					  {
						  if (!m_msgview->isShown())
							  {
								  toggleMsgExpansion();
							  }
					  }
			  }
	  }
  }/* if  diff folder */
  else  /* if  the same loaded folder, we need to invoke handlePendingCommand */
	handlePendingCommand();
}

MSG_FolderInfo *
XFE_ThreadView::getFolderInfo()
{
  return m_folderInfo;
}

void
XFE_ThreadView::updateExpandoFlippyText(int row)
{
	char *subjectStr;
	XmString subject;
	XmString oldsubject = 0;
	char *oldsubjectStr = NULL;
	XP_Bool needsChange = TRUE;

#if defined(DEBUG_tao)
	printf("\nXFE_ThreadView::updateExpandoFlippyText(row=%d)\n",row);
#endif
	if (row == -1)
		{
			subjectStr = "";
		}
	else
		{
			acquireLineData(row);
			
			subjectStr = getColumnText(OUTLINER_COLUMN_SUBJECT);
		}      
	
	XtVaGetValues (m_arrowlabel, 
				   XmNlabelString, &oldsubject,
				   NULL);
	
	if (XmStringGetLtoR(oldsubject, XmFONTLIST_DEFAULT_TAG, &oldsubjectStr))
		if (oldsubjectStr && !strcmp(oldsubjectStr, subjectStr)) 
			{
				needsChange = FALSE;
				if (oldsubjectStr) XtFree(oldsubjectStr);
			}
	
	if (needsChange) 
		{
			subject = XmStringCreate (subjectStr, XmFONTLIST_DEFAULT_TAG);
			
			XtVaSetValues (m_arrowlabel, 
						   XmNlabelString, subject, 
						   NULL);
			
			XmStringFree (subject);
		}
	
	XmStringFree (oldsubject);
	XFlush(XtDisplay(m_arrowlabel));
}

//
// show a message at a particular row in the thread's outliner.
// sending -1 to this function will clear the label and the message
// area.
//
void
XFE_ThreadView::showMessage(int row)
{
	/* sometimes this can get called before we've loaded a folder. */
	if (!m_folderInfo)
		return;

	/*
	** if the message area is expanded we just load the message, since
	** we will turn around and call the makeVisible/selectItemExclusive/etc
	** in our newMessageLoading method.
	** If the message area isn't expanded, we handle it here.
	*/
	if (row > -1) {
		m_lastLoadedInd = (MSG_ViewIndex) row;
		m_lastLoadedKey = MSG_GetMessageKey(m_pane, row);
#if defined(DEBUG_tao)
		printf("\nXFE_ThreadView::showMessage m_lastLoadedInd=%d\n", 
			   m_lastLoadedInd);
#endif		
	}/* if */

	if (m_msgExpanded)
		{
			m_msgview->loadMessage(m_folderInfo,
								   (row == -1 ? MSG_MESSAGEKEYNONE : MSG_GetMessageKey(m_pane, row)));
		}
	else
		{
			if (row != -1)
				{
					m_outliner->makeVisible(row);
					m_outliner->selectItemExclusive(row);
				}
		}
	updateExpandoFlippyText(row);
}

char *
XFE_ThreadView::commandToString(CommandType cmd, void * calldata, XFE_CommandInfo* info)
{
#define IS_CMD(command) cmd == (command)  
	if (IS_CMD(xfeCmdToggleMessageExpansion))
		{
			if (m_msgview->isShown())
				return stringFromResource("hideMsgAreaCmdString");
			else
				return stringFromResource("showMsgAreaCmdString");
		}
    else if (IS_CMD(xfeCmdDeleteMessage) || IS_CMD(xfeCmdCancelMessages)) {
		if (isDisplayingNews())
			return XP_GetString(MK_MSG_CANCEL_MESSAGE);
		else
			return XP_GetString(MK_MSG_DELETE_SEL_MSGS);			
	}
    else if ( calldata &&
              (IS_CMD(xfeCmdReplyToSender)
               || IS_CMD(xfeCmdReplyToAll)
               || IS_CMD(xfeCmdReplyToNewsgroup)
               || IS_CMD(xfeCmdReplyToSenderAndNewsgroup) ) )
    {
        // if calldata is non-null for a Reply button,
        // that's a signal not to change the
        // widget name in the resources file:
        return 0;
    }

    else
		return XFE_MNListView::commandToString(cmd, calldata, info);
  
#undef IS_CMD
}

Boolean
XFE_ThreadView::isCommandSelected(CommandType cmd, void *calldata, XFE_CommandInfo*)
{
#define IS_CMD(command) cmd == (command)
  if (IS_CMD(xfeCmdShowAllHeaders)
	   || IS_CMD(xfeCmdShowNormalHeaders)
	   || IS_CMD(xfeCmdShowBriefHeaders)
	   || IS_CMD(xfeCmdWrapLongLines)
	   || IS_CMD(xfeCmdViewAttachmentsInline)
       || IS_CMD(xfeCmdEditMessage)
       || IS_CMD(xfeCmdOpenSelected)
       || IS_CMD(xfeCmdUpdateMessageCount)
	   || IS_CMD(xfeCmdViewAttachmentsAsLinks))
    {
      return m_msgExpanded && m_msgview->isCommandSelected(cmd);
    }
  else if (IS_CMD(xfeCmdPrintSetup)
           || IS_CMD(xfeCmdPrintPreview))
      return True;
  else if (IS_CMD(xfeCmdSortAscending))
  {
      return ( MSG_GetToggleStatus( m_pane, MSG_SortBackward, NULL, 0 )
               != MSG_Checked );
  }
  else if (IS_CMD(xfeCmdSortDescending))
  {
      return ( MSG_GetToggleStatus( m_pane, MSG_SortBackward, NULL, 0 )
               == MSG_Checked );
  }
  else
    return XFE_MNListView::isCommandSelected(cmd, calldata);
#undef IS_CMD
}

Boolean
XFE_ThreadView::isCommandEnabled(CommandType cmd, void *calldata, XFE_CommandInfo* info)
{
#define IS_CMD(command) cmd == (command)

	if (IS_CMD(xfeCmdDeleteMessage)) {
		/* Given a flag or set of flags, returns the number of folders that have
		 *  that flag set.  If the result pointer is not NULL, fills it in with the
		 *  list of folders (providing up to resultsize entries).  
		 */
		int32 resultsize = MSG_GetFoldersWithFlag(XFE_MNView::getMaster(),
												  MSG_FOLDER_FLAG_TRASH,
												  NULL,
												  0);
		if (resultsize == 0)
			return False;
	}/* IS_CMD(xfeCmdDeleteMessage */

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

    // DeleteMessage stands for CancelMessage in the ThreadView,
    // so intercept it early:
    if ( (IS_CMD(xfeCmdDeleteMessage) || IS_CMD(xfeCmdCancelMessages))
              && isDisplayingNews() )
    {
      XP_Bool selectable = False;
      MSG_CommandStatus(m_pane, MSG_CancelMessage,
                        /* just need some integer pointer here...*/
                        (MSG_ViewIndex*)selected, count,
                        &selectable, NULL, NULL, NULL);
      return selectable;
    }

    // Check if selectThread should be enabled or not...
    if ( IS_CMD(xfeCmdSelectThread))
    {
        Boolean status =
                MSG_GetToggleStatus(m_pane, MSG_SortByThread, NULL, 0)==MSG_Checked;
        return ((count > 0) && status);
    }
    if ( !m_displayingNewsgroup && IS_CMD(xfeCmdEditConfiguration) )
    {
        // Manage Mail Account
        return True;
    }
    if ( m_displayingNewsgroup && IS_CMD(xfeCmdModerateDiscussion) )
    {
        // Moderate Newsgroup Discussion
        return True;
    }
 	if (IS_CMD(xfeCmdUndo)
 		|| IS_CMD(xfeCmdRedo)) {
 		XP_Bool selectable = False;
 		MSG_CommandType msg_cmd = commandToMsgCmd(cmd);
 		MSG_CommandStatus(m_pane, msg_cmd,
 						  /* just need some integer pointer here...*/
 						  (MSG_ViewIndex*)selected, count,
 						  &selectable, NULL, NULL, NULL);

#if defined(DEBUG_tao)
 		if (IS_CMD(xfeCmdUndo))
 			printf("\n XFE_ThreadView, MSG_CommandStatus:: m_pane=%x, xfeCmdUndo=%d", m_pane, selectable);
 		else if (IS_CMD(xfeCmdRedo))
 			printf("\n XFE_ThreadView, MSG_CommandStatus:: m_pane=%x, xfeCmdRedo=%d", m_pane, selectable);
#endif
       return selectable;
     }
 

  MSG_MotionType nav_cmd;

  nav_cmd = commandToMsgNav(cmd);

  if ((nav_cmd != (MSG_MotionType)~0)&&
	  (!(IS_CMD(xfeCmdBack)||IS_CMD(xfeCmdForward))))
    {
      XP_Bool selectable = FALSE;
      if (count == 1)
		  MSG_NavigateStatus(m_pane, nav_cmd, selected[0],
							 &selectable, NULL);

      return selectable;
    }

  else if (IS_CMD(xfeCmdToggleMessageExpansion))
	  {
		  return True;
	  }
  else if (IS_CMD(xfeCmdGetNextNNewMsgs))
	  {
		  return m_displayingNewsgroup;
	  }
  else if (IS_CMD(xfeCmdEditPreferences))
    {
      return True;
    }
  //  Always available even if nothing is selected:
  else if (IS_CMD(xfeCmdComposeArticle)
           || IS_CMD(xfeCmdComposeArticleHTML)
           || IS_CMD(xfeCmdComposeArticlePlain)
           || IS_CMD(xfeCmdPrintSetup)
           || IS_CMD(xfeCmdPrintPreview)
           || IS_CMD(xfeCmdUpdateMessageCount))
	{
	  return True;
	}

  // the move and copy commands are available if there are messages selected.
  else if (IS_CMD(xfeCmdMoveMessage)
	   || IS_CMD(xfeCmdCopyMessage)
	   || IS_CMD(xfeCmdSetPriorityHighest)
	   || IS_CMD(xfeCmdSetPriorityHigh)
	   || IS_CMD(xfeCmdSetPriorityNormal)
	   || IS_CMD(xfeCmdSetPriorityLow)
	   || IS_CMD(xfeCmdSetPriorityLowest)
	   || IS_CMD(xfeCmdSetPriorityNone)
       || IS_CMD(xfeCmdEditMessage)
       || IS_CMD(xfeCmdOpenSelected))
    {
      return (count > 0);
    }
  // the sort commands are always available, if there are messages
  else if (IS_CMD(xfeCmdSortBySender)
		   || IS_CMD(xfeCmdSortByDate)
		   || IS_CMD(xfeCmdSortBySubject)
		   || IS_CMD(xfeCmdSortByPriority)
		   || IS_CMD(xfeCmdSortByThread)
		   || IS_CMD(xfeCmdSortByMessageNumber)
		   || IS_CMD(xfeCmdSortBySize)
		   || IS_CMD(xfeCmdSortByFlag)
		   || IS_CMD(xfeCmdSortByStatus)
		   || IS_CMD(xfeCmdSortByUnread)
		   || IS_CMD(xfeCmdSortAscending)
		   || IS_CMD(xfeCmdSortDescending)
		   || IS_CMD(xfeCmdMarkMessageByDate))
    {
      return MSG_GetNumLines(m_pane) > 0;
    }
  // commands that work on the msgview.
  else if (IS_CMD(xfeCmdShowAllHeaders)
		   || IS_CMD(xfeCmdPrint)
		   || IS_CMD(xfeCmdShowNormalHeaders)
		   || IS_CMD(xfeCmdShowBriefHeaders)
		   || IS_CMD(xfeCmdWrapLongLines)
		   || IS_CMD(xfeCmdViewAttachmentsInline)
		   || IS_CMD(xfeCmdViewAttachmentsAsLinks)
		   || IS_CMD(xfeCmdViewPageSource)
		   || IS_CMD(xfeCmdFindInObject)
		   || IS_CMD(xfeCmdFindAgain))
    {
      return m_msgExpanded && m_msgview->isCommandEnabled(cmd);
    }

  else if (IS_CMD(xfeCmdSearch))
	  {
		  return !XP_IsContextBusy(m_contextData);
	  }
  else if (IS_CMD(xfeCmdExpand)
           || IS_CMD(xfeCmdExpandAll)
           || IS_CMD(xfeCmdCollapse)
           || IS_CMD(xfeCmdCollapseAll))
      return True;
  else
    {
      return XFE_MNListView::isCommandEnabled(cmd, calldata, info);
    }
#undef IS_CMD
}

Boolean
XFE_ThreadView::handlesCommand(CommandType cmd, void *calldata, XFE_CommandInfo*)
{
#define IS_CMD(command) cmd == (command)
  if (IS_CMD(xfeCmdToggleMessageExpansion)
	  || IS_CMD(xfeCmdGetNewMessages)
	  || IS_CMD(xfeCmdGetNextNNewMsgs)
      || IS_CMD(xfeCmdAddSenderToAddressBook)
      || IS_CMD(xfeCmdAddAllToAddressBook)
      || IS_CMD(xfeCmdEditMessage)
	  || IS_CMD(xfeCmdSendMessagesInOutbox)
      || IS_CMD(xfeCmdPrint)
	  || IS_CMD(xfeCmdSaveAs)
      || IS_CMD(xfeCmdReplyToSender)
	  || IS_CMD(xfeCmdReplyToSenderAndNewsgroup)
      || IS_CMD(xfeCmdReplyToAll)
      || IS_CMD(xfeCmdForwardMessage)
      || IS_CMD(xfeCmdForwardMessageQuoted)
      || IS_CMD(xfeCmdNextMessage)
      || IS_CMD(xfeCmdNextUnreadMessage)
      || IS_CMD(xfeCmdPreviousMessage)
      || IS_CMD(xfeCmdPreviousUnreadMessage)
      || IS_CMD(xfeCmdDeleteMessage)
      || IS_CMD(xfeCmdSortBySender)
      || IS_CMD(xfeCmdSortByDate)
      || IS_CMD(xfeCmdSortBySubject)
      || IS_CMD(xfeCmdSortByPriority)
      || IS_CMD(xfeCmdSortByThread)
      || IS_CMD(xfeCmdSortByMessageNumber)
      || IS_CMD(xfeCmdSortBySize)
      || IS_CMD(xfeCmdSortByFlag)
      || IS_CMD(xfeCmdSortByStatus)
      || IS_CMD(xfeCmdSortByUnread)
      || IS_CMD(xfeCmdSortAscending)
      || IS_CMD(xfeCmdSortDescending)
	  || IS_CMD(xfeCmdViewNew)
      || IS_CMD(xfeCmdViewAllThreads)
      || IS_CMD(xfeCmdViewThreadsWithNew)
      || IS_CMD(xfeCmdViewWatchedThreadsWithNew)
      || IS_CMD(xfeCmdShowAllHeaders)
      || IS_CMD(xfeCmdShowNormalHeaders)
      || IS_CMD(xfeCmdShowBriefHeaders)
      || IS_CMD(xfeCmdViewAttachmentsInline)
      || IS_CMD(xfeCmdViewAttachmentsAsLinks)
      || IS_CMD(xfeCmdRot13Message)
      || IS_CMD(xfeCmdMarkMessageRead)
      || IS_CMD(xfeCmdMarkMessageUnread)
      || IS_CMD(xfeCmdMarkMessageByDate)
      || IS_CMD(xfeCmdMarkThreadRead)
      || IS_CMD(xfeCmdMarkAllMessagesRead)
      || IS_CMD(xfeCmdMarkMessage)
      || IS_CMD(xfeCmdUnmarkMessage)
      || IS_CMD(xfeCmdToggleThreadKilled)
      || IS_CMD(xfeCmdToggleThreadWatched)
      || IS_CMD(xfeCmdRenameFolder)
      || IS_CMD(xfeCmdSearch)
      || IS_CMD(xfeCmdSearchAddress)
      || IS_CMD(xfeCmdEditPreferences)
      || IS_CMD(xfeCmdWrapLongLines)

      /* priorities. */
      || IS_CMD(xfeCmdSetPriorityHighest)
      || IS_CMD(xfeCmdSetPriorityHigh)
      || IS_CMD(xfeCmdSetPriorityNormal)
      || IS_CMD(xfeCmdSetPriorityLow)
      || IS_CMD(xfeCmdSetPriorityLowest)
      || IS_CMD(xfeCmdSetPriorityNone)

      /* we handle these command to place more constraints on it being sensitive. */
      || IS_CMD(xfeCmdViewPageSource)
	  || IS_CMD(xfeCmdFindInObject)
	  || IS_CMD(xfeCmdFindAgain)

      || IS_CMD(xfeCmdShowPopup)
      || IS_CMD(xfeCmdModerateDiscussion)

      || IS_CMD(xfeCmdEditMessage)
      || IS_CMD(xfeCmdOpenSelected)
      || IS_CMD(xfeCmdUpdateMessageCount)
      || IS_CMD(xfeCmdNewFolder)
      || IS_CMD(xfeCmdCompressAllFolders)
      || IS_CMD(xfeCmdCompressFolders)
      || IS_CMD(xfeCmdAddNewsgroup)
      || IS_CMD(xfeCmdPrintSetup)
      || IS_CMD(xfeCmdPrintPreview)
      || IS_CMD(xfeCmdExpand)
      || IS_CMD(xfeCmdExpandAll)
      || IS_CMD(xfeCmdCollapse)
      || IS_CMD(xfeCmdCollapseAll)
      || IS_CMD(xfeCmdToggleKilledThreads)
      || IS_CMD(xfeCmdSelectThread)
      || IS_CMD(xfeCmdEditConfiguration)
      || IS_CMD(xfeCmdModerateDiscussion)
  	|| IS_CMD(xfeCmdMommy))
    {
      return TRUE;
    }
  else
    {
      return XFE_MNListView::handlesCommand(cmd, calldata);
    }
#undef IS_CMD
}

void
XFE_ThreadView::addCollapsed(int where)
{

	if (m_collapsedCount && m_collapsed)
		m_collapsed = (int *) XP_REALLOC(m_collapsed, 
									   (m_collapsedCount+1)*sizeof(int));
	else
		m_collapsed = (int *) XP_CALLOC(1, sizeof(int));
	m_collapsed[m_collapsedCount++] = where+1;
#if defined(DEBUG_tao)
	printf("\n-->addCollapsed=%d, m_collapsedCount=%d\n", 
		   where+1, m_collapsedCount);
#endif		
}

XP_Bool
XFE_ThreadView::isCollapsed(int where)
{
	if (!m_collapsedCount || !m_collapsed)
		return False;

	for (int i=0; i < m_collapsedCount; i++)
		if (m_collapsed[i] == where) {
			/* clear it
			 * thread children's pos > 0
			 */
			m_collapsed[i] = 0-where;
			return True;
		}/* for i */
	return False;
}

void
XFE_ThreadView::doCommand(CommandType cmd,
						  void *calldata, XFE_CommandInfo* info)
{
#define IS_CMD(command) cmd == (command)
  const int *selected;
  int count;
  
  m_outliner->getSelection(&selected, &count);

  if (IS_CMD(xfeCmdToggleMessageExpansion))
	  {
		  toggleMsgExpansion();
		  getToplevel()->notifyInterested(XFE_View::chromeNeedsUpdating);
	  }
  else if (IS_CMD(xfeCmdMommy))
	  {
		  fe_showFoldersWithSelected(XtParent(getToplevel()->getBaseWidget()),
									 ViewGlue_getFrame(m_contextData),
									 NULL,
									 m_folderInfo);
	  }
  else if (IS_CMD(xfeCmdShowPopup))
    {
		XEvent *event = info->event;
		int x, y, clickrow;
		Widget w = XtWindowToWidget(event->xany.display, event->xany.window);
		
		if (m_popup)
			delete m_popup;

		if (w == NULL) w = m_widget;
		
		m_popup = new XFE_PopupMenu("popup",(XFE_Frame*)m_toplevel, // XXXXXXX
						XfeAncestorFindApplicationShell(w));
		
		if (m_displayingNewsgroup)
			m_popup->addMenuSpec(news_popup_spec);
		else
			m_popup->addMenuSpec(mail_popup_spec);

		m_outliner->translateFromRootCoords(event->xbutton.x_root,
											event->xbutton.y_root,
											&x, &y);
      
		clickrow = m_outliner->XYToRow(x, y);
		
		if (clickrow != -1) /* if it was actually in the outliner's content rows. */
			{
				if (! m_outliner->isSelected(clickrow))
				{
					m_outliner->selectItemExclusive(clickrow);
					showMessage(clickrow);				
				}
			}

		m_popup->position(event);
		
		m_popup->show();
    }
  else if (IS_CMD(xfeCmdGetNewMessages))
	  {
		  getNewMail();
	  }
  else if (IS_CMD(xfeCmdGetNextNNewMsgs))
	  {
		  getNewNews();
	  }
  else if (IS_CMD(xfeCmdMarkMessageByDate))
	  {
		  markReadByDate();
	  }
  else if (IS_CMD(xfeCmdEditPreferences))
	  {
		  fe_showMailNewsPreferences(getToplevel(), m_contextData);
	  }
  else if (IS_CMD(xfeCmdShowAllHeaders)
		   || IS_CMD(xfeCmdPrint)
		   || IS_CMD(xfeCmdShowNormalHeaders)
		   || IS_CMD(xfeCmdShowBriefHeaders)
		   || IS_CMD(xfeCmdWrapLongLines)
		   || IS_CMD(xfeCmdViewAttachmentsInline)
		   || IS_CMD(xfeCmdViewAttachmentsAsLinks)
		   /* pass to msgview
			*/
		   || IS_CMD(xfeCmdBack)
		   || IS_CMD(xfeCmdForward))
	  {
		  m_msgview->doCommand(cmd);
	  }
  else if (IS_CMD(xfeCmdSetPriorityHighest)
		   || IS_CMD(xfeCmdSetPriorityHigh)
		   || IS_CMD(xfeCmdSetPriorityNormal)
		   || IS_CMD(xfeCmdSetPriorityLow)
		   || IS_CMD(xfeCmdSetPriorityLowest)
		   || IS_CMD(xfeCmdSetPriorityNone))
	  {
		  MSG_PRIORITY priority = commandToPriority(cmd);
		  MessageKey key;
		  int i;
		  
		  for (i = 0; i < count; i ++)
			  {
				  key = MSG_GetMessageKey(m_pane, (MSG_ViewIndex)selected[i]);
				  
				  MSG_SetPriority(m_pane, key, priority);
			  }
	  }
  else if (IS_CMD(xfeCmdMoveMessage))
	  {
		  MSG_FolderInfo *info = (MSG_FolderInfo*)calldata;
		  
		  if (info)
		  {
			MSG_FolderLine folderLine;
		  	MSG_GetFolderLineById(XFE_MNView::getMaster(), info, &folderLine);

			if ( folderLine.flags & MSG_FOLDER_FLAG_NEWSGROUP)
			{
			   MSG_CopyMessagesIntoFolder(m_pane, (MSG_ViewIndex*)selected, count, info);
			}
			else
			{
		 	   MSG_MoveMessagesIntoFolder(m_pane, (MSG_ViewIndex*)selected, count, info);
			}
		  }
	  }
  else if (IS_CMD(xfeCmdCopyMessage))
    {
      MSG_FolderInfo *info = (MSG_FolderInfo*)calldata;

      if (info)
	MSG_CopyMessagesIntoFolder(m_pane, (MSG_ViewIndex*)selected, count, info);
    }
  else if (isDisplayingNews()
      && (IS_CMD(xfeCmdDeleteMessage) || IS_CMD(xfeCmdCancelMessages)))
    {
        // If this is a news article, then Delete Message
        // is really Cancel Message:
        MSG_Command(m_pane, MSG_CancelMessage,
                    (MSG_ViewIndex*)selected, count);
    }
  else if (IS_CMD(xfeCmdExpand))
    { 
        MSG_MessageLine mline;
        MSG_GetThreadLineByIndex(m_pane, (MSG_ViewIndex)selected[0], 1,
                                 &mline);
        if ((mline.numChildren > (uint16)0)
            && ((mline.flags & MSG_FLAG_ELIDED) != (uint32)0))
            MSG_ToggleExpansion(m_pane, (MSG_ViewIndex)selected[0], NULL);
    }
  else if (IS_CMD(xfeCmdCollapse))
    {
        MSG_MessageLine mline;
        MSG_GetThreadLineByIndex(m_pane, (MSG_ViewIndex)selected[0], 1,
                                 &mline);
        if ((mline.numChildren > (uint16)0)
            && ((mline.flags & MSG_FLAG_ELIDED) == (uint32)0)) {
			if (mline.numChildren == 1)
				addCollapsed(selected[0]);
            MSG_ToggleExpansion(m_pane, (MSG_ViewIndex)selected[0], NULL);
		}/* if */
    }
  else if (IS_CMD(xfeCmdExpandAll))
    {
        // Note that in the following loop, getTotalLines()
        // may grow each time we expand, but that's okay.
        int i;
        for (i=0; i < getOutliner()->getTotalLines(); ++i)
        {
            MSG_MessageLine mline;
            MSG_GetThreadLineByIndex(m_pane, i, 1, &mline);
            if ((mline.numChildren != (uint16)0)
                && ((mline.flags & MSG_FLAG_ELIDED) != (uint32)0))
                MSG_ToggleExpansion(m_pane, (MSG_ViewIndex)i, NULL);
        }
    }
  else if (IS_CMD(xfeCmdCollapseAll))
    {
        // Note that in the following loop, getTotalLines()
        // may shrink each time we collapse, but that's okay.
        int i;
        for (i=0; i < getOutliner()->getTotalLines(); ++i)
        {
            MSG_MessageLine mline;
            MSG_GetThreadLineByIndex(m_pane, i, 1, &mline);
            if (mline.numChildren != (uint16)0)
            {
                // Now collapse the parent:
                if ((mline.flags & MSG_FLAG_ELIDED) == (uint32)0) {
					if (mline.numChildren == 1)
						addCollapsed(i);
                    MSG_ToggleExpansion(m_pane, (MSG_ViewIndex)i, NULL);
				}/* if */
            }
        }
    }
  else if (IS_CMD(xfeCmdDeleteMessage))
	  {
		  if (count == 1)
			  /* stop loading since we are deleting this message:
			   * shall we check if it is stopable?
			   */
			  XP_InterruptContext(m_contextData);

			  {
				  MSG_Command(m_pane, MSG_DeleteMessage, (MSG_ViewIndex*)selected, count);
			  }

		  XFE_MozillaApp::theApp()->
			  notifyInterested(XFE_MNView::folderChromeNeedsUpdating, 
							   getFolderInfo());
	  }
  else if (IS_CMD(xfeCmdEditMailFilterRules))
	  {
		  fe_showMailFilterDlg(getToplevel()->getBaseWidget(), 
							   m_contextData);
	  }
  else if (IS_CMD(xfeCmdSearchAddress))
	  {
		  fe_showLdapSearch(XfeAncestorFindApplicationShell(getToplevel()->getBaseWidget()),
							ViewGlue_getFrame(m_contextData),
							(Chrome*)NULL);
	
	  }
  else if (IS_CMD(xfeCmdSelectThread))
  {
        // Call Select Thread
        selectThread();
  }
  else if (IS_CMD(xfeCmdViewNew)
		   || IS_CMD(xfeCmdViewThreadsWithNew)
		   || IS_CMD(xfeCmdViewWatchedThreadsWithNew)
		   || IS_CMD(xfeCmdViewAllThreads)
		   || IS_CMD(xfeCmdToggleKilledThreads)
		   || IS_CMD(xfeCmdSortBySender)
		   || IS_CMD(xfeCmdSortByDate)
		   || IS_CMD(xfeCmdSortBySubject)
		   || IS_CMD(xfeCmdSortByPriority)
		   || IS_CMD(xfeCmdSortByThread)
		   || IS_CMD(xfeCmdSortByMessageNumber)
		   || IS_CMD(xfeCmdSortBySize)
		   || IS_CMD(xfeCmdSortByFlag)
		   || IS_CMD(xfeCmdSortByStatus)
		   || IS_CMD(xfeCmdSortByUnread))
	  {
		  D(printf ("Changing view.\n");)
		  MSG_CommandType msg_cmd = commandToMsgCmd(cmd);

		  if (msg_cmd != (MSG_CommandType)~0)
			  {
				  MSG_Command(m_pane, msg_cmd, (MSG_ViewIndex*)selected, count);
				  
				  /* make sure one of the selected items is still visible. */
				  m_outliner->getSelection(&selected, &count);
				  if (count > 0)
					  m_outliner->makeVisible(selected[0]);
                  if (IS_CMD(xfeCmdSortByMessageNumber))
                      m_outliner->setSortColumn(-1, m_outliner->getSortDirection());
			  }
	  }
  else if (IS_CMD(xfeCmdOpenSelected))
	  {
		  int i;
		  MessageKey key;

		  for (i = 0; i < count; i ++)
			  {
				  key = MSG_GetMessageKey(m_pane, (MSG_ViewIndex)selected[i]);
				  
				  fe_showMsg(XtParent(getToplevel()->getBaseWidget()),
							 ViewGlue_getFrame(m_contextData),
							 NULL, m_folderInfo, key, FALSE);
			  }
	  }
  else if (IS_CMD(xfeCmdSearch))
	  {
		  // We don't need to call XtParent on the base widget because
		  // the base widget is the real toplevel widget already...dora 12/31/96
		  fe_showMNSearch(XfeAncestorFindApplicationShell(getToplevel()->getBaseWidget()),
						  ViewGlue_getFrame(m_contextData),
						  NULL, this, m_folderInfo);
	  }
  else if (IS_CMD(xfeCmdSortDescending))
	  {
		  if ( MSG_GetToggleStatus( m_pane, MSG_SortBackward, NULL, 0  )
			   != MSG_Checked )
			  {
				  MSG_Command(m_pane, MSG_SortBackward, NULL, 0);

				  /* make sure one of the selected items is still visible. */
				  m_outliner->getSelection(&selected, &count);
				  if (count > 0)
					  m_outliner->makeVisible(selected[0]);
			  }
	  }
  else if (IS_CMD(xfeCmdSortAscending))
	  {
		  if ( MSG_GetToggleStatus( m_pane, MSG_SortBackward, NULL, 0 )
			   == MSG_Checked )
			  {
				  MSG_Command(m_pane, MSG_SortBackward, NULL, 0);
				  
				  /* make sure one of the selected items is still visible. */
				  m_outliner->getSelection(&selected, &count);
				  if (count > 0)
					  m_outliner->makeVisible(selected[0]);
			  }
	  }
  else 
	  {
		  MSG_CommandType msg_cmd = commandToMsgCmd(cmd);
		  MSG_MotionType nav_cmd = commandToMsgNav(cmd);
		  
		  if (info) {
			  CONTEXT_DATA(m_contextData)->stealth_cmd =
				  ((info->event->type == ButtonRelease) &&
				   (info->event->xkey.state & ShiftMask));
		  }

		  if (nav_cmd == (MSG_MotionType)~0)
			  {
				  if ((msg_cmd == (MSG_CommandType)~0) ||
					  (msg_cmd == MSG_MailNew) ||
					  (msg_cmd == MSG_PostNew) )
					  {
						  XFE_MNListView::doCommand(cmd, calldata, info);
					  }
				  else {
					  MSG_Command(m_pane, msg_cmd, (MSG_ViewIndex*)selected, count);

					  /* single pane; count ==1
					   */
					  if (IS_CMD(xfeCmdUndo) 
						  || IS_CMD(xfeCmdRedo)) {
						  MessageKey key = MSG_MESSAGEKEYNONE;
						  MSG_FolderInfo *folder = NULL;
 						  
						  if (!m_pane || !m_outliner) 
							  return;
 							  
						  UndoStatus undoStatus = 
							  MSG_GetUndoStatus(m_pane); 
						  
						  if ( UndoComplete == undoStatus ) {
							  if (MSG_GetUndoMessageKey(m_pane, 
														&folder, &key) 
								  && folder) {
								  if (m_folderInfo == folder) {
 									  /* same folder
 									   * select the message right away!
 									   */
 									  MSG_ViewIndex index = 
 										  MSG_GetMessageIndexForKey(m_pane, 
 																	key, 
 																	TRUE);
 									  if (index != MSG_VIEWINDEXNONE) {
 										  showMessage(index);
									  }
									  
 								  }/* if */
 								  else {
#if HANDLE_CMD_QUEUE
 									  /* need to load new folder 
 									   */
									  /* XFE: let handlePendingCmd() deal with 
									   * this part
									   */
									  m_commandPending = selectByKey;
									  m_pendingSelectionKey = key;
									  loadFolder(folder);
#endif /* HANDLE_CMD_QUEUE */
 								  }/* if */
 							  }/* if */
 						  }/* if UndoComplete == undoStatus */
 					  }/* if */
 				  }/* else */
				  
			  }
		  else
			  {
				  MSG_ViewIndex index, threadIndex;
				  MessageKey resultId;
				  MSG_FolderInfo *new_finfo;

				  if (count == 1)
					  {
						  m_outliner->setBlockSel(True);
						  MSG_ViewNavigate(m_pane, nav_cmd, selected[0], 
										   &resultId, &index, &threadIndex, 
										   &new_finfo);
						  m_outliner->setBlockSel(False);
						  
						  // ViewNavigate gives a NULL folderinfo if the folder
						  // info won't be changing.
						  if (new_finfo &&
							  new_finfo != m_folderInfo)
							  {
								  /* insert here any navigational commands that can span folders, and
									 add corresponding entry in pendingCommand */
								  switch (nav_cmd)
									  {
									  case MSG_NextUnreadMessage:
										  m_commandPending = selectFirstUnread;
										  break;
									  case MSG_PreviousUnreadMessage:
										  m_commandPending = selectLastUnread;
										  break;
									  default:
										  XP_ASSERT(0);
										  break;
									  }
								  loadFolder(new_finfo);
							  }
						  else
							  {
								  
								  int numLines = MSG_GetNumLines(m_pane),
									  idx = (int) index;
								  if (idx >=0 && idx < numLines)
									  showMessage(idx);
							  }
					  }
			  }

		  XFE_MozillaApp::theApp()->notifyInterested(XFE_MNView::folderChromeNeedsUpdating, 
													 getFolderInfo());
	  }
#undef IS_CMD
}

char *XFE_ThreadView::getColumnTextByMsgLine(MSG_MessageLine *msgLine, int column)
{
  static char buf[1024];	 /* ## Sigh... */
  static char from_buf[1024];	 /* ## Sigh... */
  static char size_buf[100]; /* ## Sigh... */
  char *tmp = NULL;
  
  switch (column)
    {
    case OUTLINER_COLUMN_SUBJECT:
      {
        tmp = IntlDecodeMimePartIIStr(msgLine->subject, fe_LocaleCharSetID, 
                                      FALSE);

        if (msgLine->flags & MSG_FLAG_HAS_RE) 
          {
            XP_SAFE_SPRINTF(buf, sizeof(buf), 
                            "%s", XP_GetString( XFE_RE ) ); 
            XP_STRNCAT_SAFE(buf,
                            (tmp ? tmp : msgLine->subject),
                            sizeof(buf) - XP_STRLEN(buf));
            
            if (tmp) XP_FREE(tmp);
            
            return buf;
          } 
        else 
          {
            if (tmp) 
              {
                XP_STRNCPY_SAFE(buf, tmp, sizeof(buf));
                XP_FREE(tmp);
                return buf;
              } 
            else 
              {
                XP_STRNCPY_SAFE(buf, msgLine->subject, sizeof(buf));
                return buf;
              }
          }
      }
    case OUTLINER_COLUMN_SENDERRECIPIENT:
      {
	tmp = IntlDecodeMimePartIIStr(msgLine->author, fe_LocaleCharSetID, FALSE);
	if (tmp) 
	  {
	    XP_STRNCPY_SAFE(from_buf, tmp, sizeof(from_buf));
	    XP_FREE(tmp);
	    return from_buf;
	  } 
	else 
	  {
	    XP_STRNCPY_SAFE(from_buf, msgLine->author, sizeof(from_buf));
	    return from_buf;
	  }
      }
    case OUTLINER_COLUMN_DATE:
      {
	if (msgLine->date == 0) 
	  {
	    return "";
	  } 
	else 
	  {
	    return (char*)MSG_FormatDate(m_pane, msgLine->date);
	  }
      }
    case OUTLINER_COLUMN_PRIORITY:
      {
	return priorityToString(msgLine->priority);
      }
    case OUTLINER_COLUMN_SIZE:
      {
		  XP_SAFE_SPRINTF(size_buf, sizeof(size_buf),
						  "%ld", msgLine->messageLines);
	return size_buf;
      }
    case OUTLINER_COLUMN_STATUS:
      {
		  if (msgLine->flags & MSG_FLAG_NEW)
			  return XP_GetString(XP_STATUS_NEW);
		  else if ((msgLine->flags & MSG_FLAG_REPLIED)&&
				   (msgLine->flags & MSG_FLAG_FORWARDED))
			  return XP_GetString(XP_STATUS_REPLIED_AND_FORWARDED);
		  else if (msgLine->flags & MSG_FLAG_FORWARDED)
			  return XP_GetString(XP_STATUS_FORWARDED);
		  else if (msgLine->flags & MSG_FLAG_REPLIED)
			  return XP_GetString(XP_STATUS_REPLIED);

		  return NULL; /* ### */
      }
    case OUTLINER_COLUMN_UNREADMSG:
      {
	return NULL; // we don't have a string in this column -- only an icon.
      }
    case OUTLINER_COLUMN_FLAG:
      {
	return NULL; // we don't have a string in this column -- only an icon.
      }
    default:
      XP_ASSERT(0);
      return NULL;
    }

}
/* Outlinable interface methods */
char *XFE_ThreadView::getCellTipString(int row, int column)
{
	/* returned string will be duplicated by outliner
	 */
	char *tmp = 0;
	if (row < 0) {
		/* header
		 */
		tmp = getColumnHeaderText(column);
	}/* if */
	else {
		/* content 
		 */
		MSG_MessageLine* msgLine = 
			(MSG_MessageLine *) XP_CALLOC(1, 
										  sizeof(MSG_MessageLine));
		if (!MSG_GetThreadLineByIndex(m_pane, row, 1, msgLine))
			return NULL;

		/* static array; do not free
		 */
		tmp = getColumnTextByMsgLine(msgLine, column);
		XP_FREEIF(msgLine);
	}/* else */
	if (tmp && 
		(!m_outliner->isColTextFit(tmp, row, column)))
		return tmp;

	return NULL;
}

char *XFE_ThreadView::getCellDocString(int /* row */, int /* column */)
{
	return NULL;
}

void *
XFE_ThreadView::ConvFromIndex(int index)
{
  MessageKey id = MSG_GetMessageKey(m_pane, index);

  return (void*)id;
}

int
XFE_ThreadView::ConvToIndex(void *item)
{
  MessageKey id = (MessageKey)item;
  MSG_ViewIndex index;

  index = MSG_GetMessageIndexForKey(m_pane, id, False);

  if (index == MSG_VIEWINDEXNONE)
	  {
		  MSG_MessageLine line;

		  if (!MSG_GetThreadLineById(m_pane, id, &line))
			  {
				  /* error while looking up line for this message.
					 punt */
				  return -1;
			  }
		  else
			  {
				  MSG_ViewIndex thread_index =
					  MSG_GetMessageIndexForKey(m_pane, line.threadId, False);
				  
				  if (MSG_ExpansionDelta(m_pane, thread_index) > 0)
					  {
						  MSG_ToggleExpansion(m_pane, thread_index, NULL);
						  index = MSG_GetMessageIndexForKey(m_pane, 
															id, False);
					  }
				  
				  return index;
			  }
	  }
  else
	  {
		  return index;
	  }
}

void *
XFE_ThreadView::acquireLineData(int line)
{
  m_ancestorInfo = NULL;

  if (!MSG_GetThreadLineByIndex(m_pane, line, 1, &m_messageLine))
    return NULL;

  m_ancestorInfo = new OutlinerAncestorInfo[ m_messageLine.level + 1 ];

  int i = m_messageLine.level;
  int idx = line + 1;
  while ( i > 0 ) {
    if ( idx < m_outliner->getTotalLines()) {
      int level = MSG_GetThreadLevelByIndex( m_pane, idx );
      if ( level == i ) {
	m_ancestorInfo[i].has_prev = TRUE;
	m_ancestorInfo[i].has_next = TRUE;
	i--;
	idx++;
      } else if ( level < 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_messageLine;
}

void
XFE_ThreadView::getTreeInfo(Boolean *expandable,
			      Boolean *is_expanded,
			      int *depth,
			      OutlinerAncestorInfo **ancestor)
{
  XP_Bool is_line_expandable;
  XP_Bool is_line_expanded;

  is_line_expandable = (m_messageLine.numChildren > 0);

  if (is_line_expandable)
    {
      is_line_expanded = (m_messageLine.flags & MSG_FLAG_ELIDED) == 0;
    }
  else
    {
      is_line_expanded = FALSE;
    }

  if ( ancestor )
    *ancestor = m_ancestorInfo;

  if (depth)
    *depth = m_messageLine.level;

  if (expandable)
      *expandable = is_line_expandable;

  if (is_expanded)
    *is_expanded = is_line_expanded;
}

EOutlinerTextStyle
XFE_ThreadView::getColumnStyle(int /*column*/)
{
  if (m_messageLine.flags & MSG_FLAG_EXPIRED)
    return OUTLINER_Default ; /* Don't boldface dummy messages. */
  else if ((m_messageLine.flags & MSG_FLAG_ELIDED) && m_messageLine.numNewChildren > 0)
    return OUTLINER_Bold; /* boldface toplevel thread messages with unread children. */
  else
    return (m_messageLine.flags & MSG_FLAG_READ) ? OUTLINER_Default : OUTLINER_Bold;
}

char *
XFE_ThreadView::getColumnText(int column)
{ 
  static char buf_a[1024];	 /* ## Sigh... */
  static char from_buf_a[1024];	 /* ## Sigh... */
  static char size_buf_a[100]; /* ## Sigh... */
  char *tmp = NULL;
  
  switch (column)
    {
    case OUTLINER_COLUMN_SUBJECT:
      {
	tmp = IntlDecodeMimePartIIStr(m_messageLine.subject, fe_LocaleCharSetID, FALSE);

	if (m_messageLine.flags & MSG_FLAG_HAS_RE) 
	  {
	    XP_STRNCPY_SAFE (buf_a, XP_GetString( XFE_RE ), sizeof(buf_a) ); 
	    XP_STRNCAT_SAFE (buf_a, 
                         (tmp ? tmp : m_messageLine.subject), 
                         sizeof(buf_a) - XP_STRLEN(buf_a));

	    if (tmp) XP_FREE(tmp);

	    return buf_a;
	  } 
	else 
	  {
	    if (tmp) 
	      {
            XP_STRNCPY_SAFE(buf_a, tmp, sizeof(buf_a));
            
            XP_FREE(tmp);
            return buf_a;
	      } 
	    else 
	      {
		return m_messageLine.subject;
	      }
	  }
      }
    case OUTLINER_COLUMN_SENDERRECIPIENT:
      {
	tmp = IntlDecodeMimePartIIStr(m_messageLine.author, fe_LocaleCharSetID, FALSE);
	if (tmp) 
	  {
	    XP_STRNCPY_SAFE(from_buf_a, tmp, sizeof(from_buf_a));
	    XP_FREE(tmp);
	    return from_buf_a;
	  } 
	else 
	  {
	    return m_messageLine.author;
	  }
      }
    case OUTLINER_COLUMN_DATE:
      {
	if (m_messageLine.date == 0) 
	  {
	    return "";
	  } 
	else 
	  {
	    return (char*)MSG_FormatDate(m_pane, m_messageLine.date);
	  }
      }
    case OUTLINER_COLUMN_PRIORITY:
      {
	return priorityToString(m_messageLine.priority);
      }
    case OUTLINER_COLUMN_SIZE:
      {
        XP_SAFE_SPRINTF(size_buf_a, sizeof(size_buf_a),
                        "%ld", m_messageLine.messageLines);
	return size_buf_a;
      }
    case OUTLINER_COLUMN_STATUS:
      {
		  if (m_messageLine.flags & MSG_FLAG_NEW)
			  return XP_GetString(XP_STATUS_NEW);
		  else if ((m_messageLine.flags & MSG_FLAG_REPLIED)&&
				   (m_messageLine.flags & MSG_FLAG_FORWARDED))
			  return XP_GetString(XP_STATUS_REPLIED_AND_FORWARDED);
		  else if (m_messageLine.flags & MSG_FLAG_FORWARDED)
			  return XP_GetString(XP_STATUS_FORWARDED);
		  else if (m_messageLine.flags & MSG_FLAG_REPLIED)
			  return XP_GetString(XP_STATUS_REPLIED);

		  return NULL; /* ### */
      }
    case OUTLINER_COLUMN_UNREADMSG:
      {
	return NULL; // we don't have a string in this column -- only an icon.
      }
    case OUTLINER_COLUMN_FLAG:
      {
	return NULL; // we don't have a string in this column -- only an icon.
      }
    default:
      XP_ASSERT(0);
      return NULL;
    }
}

fe_icon *
XFE_ThreadView::getColumnIcon(int column)
{
  switch (column)
    {
    case OUTLINER_COLUMN_SUBJECT:
		{
			MSG_FolderLine folderLine;
			
			MSG_GetFolderLineById(XFE_MNView::getMaster(), m_folderInfo, &folderLine);
			
			return flagToIcon(folderLine.flags, m_messageLine.flags);
		}
    case OUTLINER_COLUMN_UNREADMSG:
		return (m_messageLine.flags & MSG_FLAG_READ ? &msgReadIcon : &msgUnreadIcon);
    case OUTLINER_COLUMN_FLAG:
		return (m_messageLine.flags & MSG_FLAG_MARKED ? &msgFlagIcon : &msgReadIcon);
	case -1: /* OUTLINER_DESCENDANT_SELECT_COLUMN */
		{
			if (m_messageLine.flags & MSG_FLAG_IGNORED)
				{
					return (m_messageLine.flags & MSG_FLAG_ELIDED ? &closedSpoolIgnoredIcon : &openSpoolIgnoredIcon);
				}
			else if (m_messageLine.flags & MSG_FLAG_WATCHED)
				{
					return (m_messageLine.flags & MSG_FLAG_ELIDED ? &closedSpoolWatchedIcon : &openSpoolWatchedIcon);
				}
			else if (m_messageLine.numChildren > 0)
				{
					if (m_messageLine.flags & MSG_FLAG_NEW
						|| m_messageLine.numNewChildren > 0)
						return (m_messageLine.flags & MSG_FLAG_ELIDED ? &closedSpoolNewIcon : &openSpoolNewIcon);
					else
						return (m_messageLine.flags & MSG_FLAG_ELIDED ? &closedSpoolIcon : &openSpoolIcon);
				}
			else
				{
					return NULL;
				}
		}
    default:
		return 0;
    }
}

char *
XFE_ThreadView::getColumnName(int column)
{
  switch (column) 
    {
    case OUTLINER_COLUMN_SUBJECT:
      return "Subject";
    case OUTLINER_COLUMN_UNREADMSG:
      return "Unread";
    case OUTLINER_COLUMN_FLAG:
      return "Flag";
    case OUTLINER_COLUMN_DATE:
      return "Date";
    case OUTLINER_COLUMN_PRIORITY:
      return "Priority";
    case OUTLINER_COLUMN_SIZE:
      return "Size";
    case OUTLINER_COLUMN_STATUS:
      return "Status";
    case OUTLINER_COLUMN_SENDERRECIPIENT:
      return "Sender";
    default:
      XP_ASSERT(0);
      return 0;
    }
}

char *
XFE_ThreadView::getColumnHeaderText(int column)
{
	switch (column) 
		{
		case OUTLINER_COLUMN_SUBJECT:
			return XP_GetString(XFE_THREAD_OUTLINER_COLUMN_SUBJECT);
		case OUTLINER_COLUMN_UNREADMSG:
			return 0;
		case OUTLINER_COLUMN_FLAG:
			return 0;
		case OUTLINER_COLUMN_DATE:
			return XP_GetString(XFE_THREAD_OUTLINER_COLUMN_DATE);
		case OUTLINER_COLUMN_PRIORITY:
			return XP_GetString(XFE_THREAD_OUTLINER_COLUMN_PRIORITY);
		case OUTLINER_COLUMN_SIZE:
			{
				if (m_displayingNewsgroup)
					return XP_GetString(XFE_SIZE_IN_LINES);
				else
					return XP_GetString(XFE_SIZE_IN_BYTES);
			}
		case OUTLINER_COLUMN_STATUS:
			return XP_GetString(XFE_THREAD_OUTLINER_COLUMN_STATUS);
		case OUTLINER_COLUMN_SENDERRECIPIENT:
			if (MSG_DisplayingRecipients(m_pane))
				return XP_GetString(XFE_THREAD_OUTLINER_COLUMN_RECIPIENT);
			else
				return XP_GetString(XFE_THREAD_OUTLINER_COLUMN_SENDER);
		default:
			// XP_ASSERT(0);
			return 0;
		}
}

fe_icon *
XFE_ThreadView::getColumnHeaderIcon(int column)
{
  switch (column)
    {
    case OUTLINER_COLUMN_UNREADMSG:
      return &msgUnreadIcon;
    case OUTLINER_COLUMN_FLAG:
      return &msgFlagIcon;
    case OUTLINER_COLUMN_SUBJECT:
      if (MSG_GetToggleStatus(m_pane, MSG_SortByThread, NULL, 0) == MSG_Checked)
		  return &threadonIcon;
      else
		  return &threadoffIcon;
    default:
      return 0;
    }
}

EOutlinerTextStyle
XFE_ThreadView::getColumnHeaderStyle(int column)
{
  MSG_CommandType sort_type = (MSG_CommandType)~0;

  switch (column)
    {
    case OUTLINER_COLUMN_SENDERRECIPIENT:
      sort_type = MSG_SortBySender;
      break;
    case OUTLINER_COLUMN_DATE:
      sort_type = MSG_SortByDate;
      break;
    case OUTLINER_COLUMN_SUBJECT:
      sort_type = MSG_SortBySubject;
      break;
    case OUTLINER_COLUMN_PRIORITY:
      sort_type = MSG_SortByPriority;
      break;
	case OUTLINER_COLUMN_SIZE:
		sort_type = MSG_SortBySize;
		break;
	case OUTLINER_COLUMN_STATUS:
		sort_type = MSG_SortByStatus;
		break;
	case OUTLINER_COLUMN_UNREADMSG:
		sort_type = MSG_SortByUnread;
		break;
	case OUTLINER_COLUMN_FLAG:
		sort_type = MSG_SortByFlagged;
		break;
    }
  
  if (sort_type == (MSG_CommandType)~0)
    {
      return OUTLINER_Default;
    }
  else
    {
		XP_Bool checked = MSG_GetToggleStatus(m_pane, sort_type, NULL, 0) == MSG_Checked;
		XP_Bool backward = MSG_GetToggleStatus(m_pane, MSG_SortBackward, NULL, 0) == MSG_Checked;

		if (checked)
			{
				m_outliner->setSortColumn(column,
										  backward ? OUTLINER_SortDescending : OUTLINER_SortAscending);
				return OUTLINER_Bold;
			}
		else
			{
				return OUTLINER_Default;
			}
    }
}

void
XFE_ThreadView::releaseLineData()
{
  delete [] m_ancestorInfo;
  m_ancestorInfo = NULL;
}

XFE_CALLBACK_DEFN(XFE_ThreadView, allConnectionsComplete)(XFE_NotificationCenter *,
														  void *,
														  void *)
{
	D(printf ("in all connections complete.\n");)

#if HANDLE_CMD_QUEUE

#if defined(DEBUG_tao)
	printf("\n XFE_ThreadView::allConnectionsComplete\n");
#endif

	processCmdQueue();
#endif /* HANDLE_CMD_QUEUE */

	handlePendingCommand();

}

void
XFE_ThreadView::setPendingCmdSelByKey(PendingCommand cmd, MessageKey key)
{
	m_commandPending = cmd;
	m_pendingSelectionKey = key;
}

void
XFE_ThreadView::handlePendingCommand()
{
	switch (m_commandPending)
		{
		case invalidateThreadAndSelection:
			/* for this case, we loop backward to the message with level 1 -- the toplevel
			   message in this thread, find it's num_children, and invalidate all those
			   lines.  This fixes pipe updates and lots of other things. */
			{
				D(printf("handlePendingCommand(invalidateThreadAndSelection)\n");)
				/* tao: Obselete deletion code; assert!
				 */
				XP_ASSERT(0);
				break;
			}
		case getNewMessages:
			{
				D(printf("handlePendingCommand(getNewMessages)\n");)
				m_commandPending = noPendingCommand;
				doCommand(xfeCmdGetNewMessages);
				break;
			}
		case selectByIndex:
			{
				D(printf("handlePendingCommand(selectByIndex)\n");)
				D(printf("  pending selection index is %d\n", m_pendingSelectionIndex);)
				m_commandPending = noPendingCommand;
				if (m_pendingSelectionIndex != MSG_VIEWINDEXNONE)
					showMessage(m_pendingSelectionIndex);
				break;
			}
		case selectByKey:
			{
				D(printf("handlePendingCommand(selectByKey,%d)\n", m_pendingSelectionKey);)

				m_commandPending = noPendingCommand;

				if (m_pendingSelectionKey != MSG_MESSAGEKEYNONE)
					{
						/* 87274: Caldera: "Go to Message Folder" fails when 
						 * messages are threaded.
						 *
						 * Fix: call MSG_xxxForKey() to expand the thread 
						 * when needed.
						 */
						MSG_ViewIndex index = 
							MSG_GetMessageIndexForKey(m_pane, 
													  m_pendingSelectionKey,
													  True);
						D(printf ("  pending selection index is %d\n", index);)
						if (index != MSG_VIEWINDEXNONE)
							showMessage(index);
					}
				break;
			}
		case selectFirstUnread:
			{
				D(printf("handlePendingCommand(selectFirstUnread)\n");)
				MSG_ViewIndex index, threadIndex;
				MessageKey resultId;

				m_commandPending = noPendingCommand;

				m_outliner->setBlockSel(True);
				MSG_ViewNavigate(m_pane, MSG_FirstUnreadMessage, MSG_VIEWINDEXNONE/* ??? */,
								 &resultId, &index, &threadIndex, NULL);
				m_outliner->setBlockSel(False);

				showMessage(index);
				break;
			}
		case selectLastUnread:
			{
				D(printf("handlePendingCommand(selectLastUnread)\n");)
				MSG_ViewIndex index, threadIndex;
				MessageKey resultId;

				m_commandPending = noPendingCommand;

				MSG_ViewNavigate(m_pane, MSG_LastUnreadMessage, MSG_VIEWINDEXNONE/* ??? */,
								 &resultId, &index, &threadIndex, NULL);

				showMessage(index);
				break;
			}
		case scrollToFirstNew:
			{
				D(printf("handlePendingCommand(scrollToFirstNew)\n");)
				MessageKey resultId, resultingThread;
				MSG_ViewIndex resultIndex = 0;

				m_commandPending = noPendingCommand;

				/* note:  We don't expand threads here -- we scroll to the message that
				   starts the thread. */

				/* after we do a get new mail, we end up here.  We try to scroll to the
				   first new one. */
				MSG_DataNavigate(m_pane, MSG_FirstNew, MSG_MESSAGEKEYNONE/*???*/,
								 &resultId, &resultingThread);
				
				/* if there wasn't a new message, we scroll to the first unread message */
				if (resultId == MSG_MESSAGEKEYNONE
					|| resultingThread == MSG_MESSAGEKEYNONE)
					MSG_DataNavigate(m_pane, MSG_FirstUnreadMessage, MSG_MESSAGEKEYNONE/*???*/,
									 &resultId, &resultingThread);
				
				/* if there wasn't an unread message, we scroll to the last item. */
				if (m_outliner->getTotalLines() > 0)
					{
						if (resultId == MSG_MESSAGEKEYNONE
							|| resultingThread == MSG_MESSAGEKEYNONE)
							{
								resultIndex == m_outliner->getTotalLines() - 1;
							}
						else
							{
								resultIndex = 
									MSG_GetMessageIndexForKey(m_pane, 
															  resultingThread,
															  False);
								if (resultIndex == MSG_VIEWINDEXNONE)
									resultIndex = m_outliner->getTotalLines() - 1;
							}
						
						m_outliner->makeVisible((int)resultIndex);
					}
				break;
			}
		case noPendingCommand: // we should be so lucky
			D(printf ("no pending command to execute\n");)
			break;
		default:
			XP_ASSERT(0);
			break;
		}
}

void 
XFE_ThreadView::Buttonfunc(const OutlineButtonFuncData *data)
{
        /* 
	** Broadcast the view is currently in focus because 
	** of the button clicking
	*/
        getToplevel()->notifyInterested(XFE_MNListView::changeFocus, (void*) this);

	if (data->row == -1) // heading row.
		{
			const int *selected;
			int count;
			
			m_outliner->setDescendantSelectAllowed(False);
			
			switch (data->column)
				{
				case OUTLINER_COLUMN_SUBJECT:
					if (data->x < (threadonIcon.width + /* hack -- shadowthickness */ 4))
						{
							if (isCommandEnabled(xfeCmdSortByThread))
								{
									m_outliner->setSortColumn(-1,
															  OUTLINER_SortAscending);
									m_outliner->setDescendantSelectAllowed(True);
									doCommand(xfeCmdSortByThread);
								}
						}
					else
						{
							if (isCommandEnabled(xfeCmdSortBySubject))
								{
									if (m_outliner->getSortColumn() == data->column)
										m_outliner->toggleSortDirection();
									doCommand(xfeCmdSortBySubject);
								}
						}
					break;
				case OUTLINER_COLUMN_SENDERRECIPIENT:
					if (isCommandEnabled(xfeCmdSortBySender))
						{
							if (m_outliner->getSortColumn() == data->column)
								m_outliner->toggleSortDirection();
							doCommand(xfeCmdSortBySender);
						}
					break;
				case OUTLINER_COLUMN_DATE:
					if (isCommandEnabled(xfeCmdSortByDate))
						{
							if (m_outliner->getSortColumn() == data->column)
								m_outliner->toggleSortDirection();
							doCommand(xfeCmdSortByDate);
						}
					break;
				case OUTLINER_COLUMN_PRIORITY:
					if (isCommandEnabled(xfeCmdSortByPriority))
						{
							if (m_outliner->getSortColumn() == data->column)
								m_outliner->toggleSortDirection();
							doCommand(xfeCmdSortByPriority);
						}
					break;
				case OUTLINER_COLUMN_STATUS:
					if (isCommandEnabled(xfeCmdSortByStatus))
						{
							if (m_outliner->getSortColumn() == data->column)
								m_outliner->toggleSortDirection();
							doCommand(xfeCmdSortByStatus);
						}
					break;
				case OUTLINER_COLUMN_SIZE:
					if (isCommandEnabled(xfeCmdSortBySize))
						{
							if (m_outliner->getSortColumn() == data->column)
								m_outliner->toggleSortDirection();
							doCommand(xfeCmdSortBySize);
						}
					break;
				case OUTLINER_COLUMN_UNREADMSG:
					if (isCommandEnabled(xfeCmdSortByUnread))
						{
							if (m_outliner->getSortColumn() == data->column)
								m_outliner->toggleSortDirection();
							doCommand(xfeCmdSortByUnread);
						}
					break;
				case OUTLINER_COLUMN_FLAG:
					if (isCommandEnabled(xfeCmdSortByFlag))
						{
							if (m_outliner->getSortColumn() == data->column)
								m_outliner->toggleSortDirection();
							doCommand(xfeCmdSortByFlag);
						}
					break;
				}

			m_outliner->getSelection(&selected, &count);

			if (count >= 1)
				{
					D(printf ("Making %d visible\n", selected[0]);)
					m_outliner->makeVisible(selected[0]);
				}
		}
	else  // content row.
		{
			if (data->clicks == 2)
				{
					m_outliner->selectItemExclusive(data->row);
					
					if ( m_displayingDraft )
						{
							MSG_OpenDraft(m_pane, m_folderInfo,
										  MSG_GetMessageKey(m_pane, data->row));
						}
					else
						{
							// ok.  we're going to display a message in a message frame.
							// what is left to determine is whether or not we will be
							// popping up another window or reusing an existing one (if
							// there is one.)  This behavior is governed by two things:
							// 1.  fe_globalPrefs.reuse_msg_window and,
							// 2.  data->alt (whether the alt key was down or not...)
							
							fe_showMsg(XtParent(getToplevel()->getBaseWidget()),
									   ViewGlue_getFrame(m_contextData),
									   (Chrome*)NULL,
									   m_folderInfo,
									   MSG_GetMessageKey(m_pane, data->row),
									   
									   (( fe_globalPrefs.reuse_msg_window
										  && !data->shift)
										|| (!fe_globalPrefs.reuse_msg_window
											&& data->shift))
									   );
						}
				}
			else if (data->clicks == 1)
				{
					const int *selected;
					int count;
							
					m_outliner->getSelection(&selected, &count);
#if defined(DEBUG_tao)
					printf("\n+++ data->clicks count=%d\n", count);
#endif
					if (data->ctrl)
						{
							m_outliner->toggleSelected(data->row);

							m_outliner->getSelection(&selected, &count);
#if defined(DEBUG_tao)
							printf("\n+++ data->clicks count=%d\n", count);
#endif
							if (count == 1)
								showMessage(selected[0]);
							else if (count == 0 ||
									 count > 1)
								showMessage(-1);
								
						}
					else if (data->shift)
						{
							// select the range.
							
							if (count == 0) /* there wasn't anything selected yet. */
								{
									m_outliner->selectItemExclusive(data->row);
									
									// clear the message area/label
									showMessage(-1);
								}
							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);
									
									// clear the message area/label
									showMessage(-1);
								}
							else /* we had a range of items selected, so let's do something really
									nice with them. */
								{
									m_outliner->trimOrExpandSelection(data->row);
									
									// clear the message area/label
									showMessage(-1);
								}
						}
					else
						{	  
							// handle the columns that don't actually move the selection here
							if (data->column == OUTLINER_COLUMN_UNREADMSG)
								{
									MSG_Command(m_pane, MSG_ToggleMessageRead, (MSG_ViewIndex*)&data->row, 1);
								}
							else if (data->column == OUTLINER_COLUMN_FLAG)
								{
									MSG_MessageLine line;
									
									if (!MSG_GetThreadLineByIndex(m_pane, data->row, 1, &line))
										return;
									
									if (line.flags & MSG_FLAG_MARKED)
										MSG_Command(m_pane, MSG_UnmarkMessages, (MSG_ViewIndex*)&data->row, 1);
									else
										MSG_Command(m_pane, MSG_MarkMessages, (MSG_ViewIndex*)&data->row, 1);
								}
							else
								{
									// we've selected a message, update the label and load it into
									// our message view.
									
									showMessage(data->row);
								}
						}
					
					XFE_MozillaApp::theApp()->notifyInterested(XFE_MNView::folderChromeNeedsUpdating,
															   getFolderInfo());
				}
		}
}

void
XFE_ThreadView::Flippyfunc(const OutlineFlippyFuncData *data)
{
	int delta = MSG_ExpansionDelta(m_pane, data->row);
	XP_Bool need_to_select_top = FALSE;

	if (data->do_selection) // we do descendant selection
		{
			MSG_MessageLine line;

			if (delta > 0)
				MSG_ToggleExpansion(m_pane, data->row, NULL);
			
			MSG_GetThreadLineByIndex(m_pane, data->row, 1, &line);
			
			m_outliner->deselectAllItems();
			m_outliner->selectRangeByIndices(data->row, data->row + line.numChildren);
			showMessage(-1);
		}
	else if (delta != 0) // actually to flippy stuff.
		{
			if (delta < 0)
				{
					if (delta == -1)
						addCollapsed(data->row);

					int i;
					delta = -delta;

					for (i = data->row + 1; i < data->row + 1 + delta; i ++)
						{
							if (m_outliner->isSelected(i))
								{
									need_to_select_top = TRUE;
									m_outliner->deselectItem(i);
								}
						}
				}

			MSG_ToggleExpansion(m_pane, data->row, NULL);

			if (need_to_select_top)
				m_outliner->selectItem(data->row);
		}
}

XFE_CALLBACK_DEFN(XFE_ThreadView, spaceAtMsgEnd)(XFE_NotificationCenter*,
						 void *, void *)
{
	if (isCommandEnabled(xfeCmdNextUnreadMessage))
		doCommand(xfeCmdNextUnreadMessage);
	else if (isCommandEnabled(xfeCmdNextUnreadCollection))
		doCommand(xfeCmdNextUnreadCollection);
}

XFE_CALLBACK_DEFN(XFE_ThreadView, newMessageLoading)(XFE_NotificationCenter*,
													 void *, void*)
{
	if (m_commandPending == noPendingCommand)
		{
			MSG_FolderInfo *info = m_msgview->getFolderInfo();
			MessageKey key = m_msgview->getMessageKey();
			
			if (info != m_folderInfo)
				{
					m_commandPending = selectByKey;
					m_pendingSelectionKey = key;
					loadFolder(info);
				}
			else
				{
					MSG_ViewIndex index = MSG_GetMessageIndexForKey(m_pane, 
																	key, 
																	False);
					
					int numLines = MSG_GetNumLines(m_pane);
					if (m_outliner->getTotalLines() != numLines)
						m_outliner->change(0, numLines, numLines);

					m_outliner->makeVisible(index);
					m_outliner->selectItemExclusive(index);
					updateExpandoFlippyText(index);
				}
      			notifyInterested(XFE_View::chromeNeedsUpdating);
		}
}

void
XFE_ThreadView::toggleMsgExpansion()
{
	int32 folder_prefs = 0;

	if (m_folderInfo)
		{
			folder_prefs = MSG_GetFolderPrefFlags(m_folderInfo);
			folder_prefs |= MSG_FOLDER_PREF_FEVALID;
		}

	if (m_msgExpanded)
		{
			// we are currently expanded
			Dimension message_pane_height;
			
			XtVaGetValues(m_msgview->getBaseWidget(),
						  XmNheight, &message_pane_height,
						  NULL);
			
			D(printf ("Saving message pane height as %d\n", message_pane_height);)
			PREF_SetIntPref(MESSAGEPANE_HEIGHT_PREF, (int32)message_pane_height);

			
			m_msgview->hide();
			
			m_msgExpanded = FALSE;
			// Set maximum heights so that the sash will go away:
			Dimension height;
			XtVaGetValues(m_arrowform,
						  XmNheight, &height,
						  NULL);
			XtVaSetValues(m_arrowform,
						  XmNpaneMinimum, height,
						  XmNpaneMaximum, height,
						  NULL);
			
			XtVaSetValues(m_arrowb, 
						  XmNarrowDirection, XmARROW_UP,
						  NULL);
			
			folder_prefs |= MSG_FOLDER_PREF_ONEPANE;

			notifyInterested(XFE_View::chromeNeedsUpdating);
		}
	else
		{
			const int *selected;
			int count;
			int32 message_pane_desired_height;
			Dimension message_pane_minimum_height;
			Dimension message_pane_resulting_height;
			Dimension panedw_spacing;
			Dimension panedw_marginheight;
			Dimension panedw_height;
			Dimension arrow_form_height;
			Dimension minimum_height_of_outliner;
			int resulting_height_of_outliner;
			XP_Bool need_to_resize_frame = False;
			
			// we are currently unexpanded
			XtVaSetValues(m_arrowb, 
						  XmNarrowDirection, XmARROW_DOWN,
						  NULL);
			
			XtVaGetValues(m_widget,
						  XmNspacing, &panedw_spacing,
						  XmNmarginHeight, &panedw_marginheight,
						  XmNheight, &panedw_height,
						  NULL);
			
			XtVaGetValues(m_arrowform,
						  XmNheight, &arrow_form_height,
						  NULL);
			
			XtVaGetValues(m_outliner->getBaseWidget(),
						  XmNpaneMinimum, &minimum_height_of_outliner,
						  NULL);
			
			XtVaGetValues(m_msgview->getBaseWidget(),
						  XmNpaneMinimum, &message_pane_minimum_height,
						  NULL);
			
			PREF_GetIntPref(MESSAGEPANE_HEIGHT_PREF, &message_pane_desired_height);
			
			D(printf ("Message pane wants to be %d high.\n", message_pane_desired_height);)
			
			resulting_height_of_outliner = (panedw_height
											- (2 * panedw_spacing) /* space between the outliner and arrow
																	  and between the arrow and message */
											- (2 * panedw_marginheight)
											- arrow_form_height
											- message_pane_desired_height);
			
			D(printf ("This will make the outliner %d high.\n", resulting_height_of_outliner);)
			
			if (resulting_height_of_outliner < (int)minimum_height_of_outliner)
				{
					D(printf ("  This is not ok.\n");)
					resulting_height_of_outliner = minimum_height_of_outliner;
					
					message_pane_resulting_height = (message_pane_desired_height
													 - (minimum_height_of_outliner
														- resulting_height_of_outliner));
					
					if (message_pane_resulting_height < message_pane_minimum_height)
						{
							message_pane_resulting_height = message_pane_minimum_height;
							need_to_resize_frame = True;
						}
					
					D(printf ("  The message pane will instead be sized to %d, and the window will%s be resized", message_pane_resulting_height, need_to_resize_frame ? "" : " not");)
				}
			
			if (need_to_resize_frame)
				XtVaSetValues(getToplevel()->getBaseWidget(),
							  XmNallowShellResize, True,
							  NULL);

			XtVaSetValues(m_msgview->getBaseWidget(),
						  XmNskipAdjust, TRUE,
						  NULL);
			
			m_msgview->show();

			XtVaSetValues(m_msgview->getBaseWidget(),
						  XmNskipAdjust, FALSE,
                          XmNpaneMinimum, PANE_MIN,
                          XmNpaneMaximum, PANE_MAX,
						  NULL);

			if (need_to_resize_frame)
				XtVaSetValues(getToplevel()->getBaseWidget(),
							  XmNallowShellResize, False,
							  NULL);
			
			XtVaSetValues(m_outliner->getBaseWidget(),
                          XmNpaneMinimum, PANE_MIN,
                          XmNpaneMaximum, PANE_MAX,
                          NULL);

			m_msgExpanded = TRUE;
			
			folder_prefs &= ~MSG_FOLDER_PREF_ONEPANE;

			m_outliner->getSelection(&selected, &count);
			
			if (count == 1)
				showMessage(selected[0]);
			else
				showMessage(-1);
	
			XFE_MozillaApp::theApp()->notifyInterested(XFE_MNView::folderChromeNeedsUpdating, getFolderInfo());
		}

	if (m_folderInfo)
		MSG_SetFolderPrefFlags(m_folderInfo, folder_prefs);
}

void
XFE_ThreadView::toggleMsgExpansionCallback(Widget, XtPointer clientData, XtPointer)
{
  XFE_ThreadView *obj = (XFE_ThreadView*)clientData;

  obj->toggleMsgExpansion();
}

#if !defined(USE_MOTIF_DND)
void
XFE_ThreadView::sourcedropfunc(fe_dnd_Source */*source*/, fe_dnd_Message msg, void *closure)
{
      MSG_FolderInfo *info = (MSG_FolderInfo*)closure;

      // messages can be copied or moved.  The closure to this function will always
      // be a MSG_FolderInfo*.
      
      // XXX assume that nothing could have changed the selected between the time
      // we started dragging and now...  don't know how smart this is...
      
      const char *name = MSG_GetFolderNameFromID(info);
      const int *indices;
      int count;
      
      m_outliner->getSelection(&indices, &count);
      
      /* New Code to take on the new Drag and Drop APIs with effect for both mail/news. However,
  	 the effect checking on news articles are not completed in BE yet.  Coredump will
	 be encountered when filing news articles. */

      if (msg == FE_DND_MESSAGE_DELETE)
      	{
			  // delete the messages.
      	}
      else 
	{

      		MSG_DragEffect requireEffect = MSG_Default_Drag;

      		MSG_DragEffect effect =
                	MSG_DragMessagesIntoFolderStatus(m_pane, 
                	(MSG_ViewIndex*)indices, count,
                	info, requireEffect);

      		if (effect == MSG_Require_Move ) 
		{
			if (msg == FE_DND_MESSAGE_COPY )
			{
	  			DD(printf("### ThreadView::sourcedropfunc(req MOVE): FE_DND_MESSAGE_COPY To \n");)
			  	MSG_CopyMessagesInto(m_pane, (MSG_ViewIndex*)indices, count, name);
			}
			else
      			{
	  			DD(printf("### ThreadView::sourcedropfunc(req MOVE): FE_DND_MESSAGE_MOVE To \n");)
	  			MSG_MoveMessagesInto(m_pane, (MSG_ViewIndex*)indices, count, name);
      			}
		}
		else if ( effect == MSG_Require_Copy )
		{
	  		DD(printf("### ThreadView::sourcedropfunc(req COPY): FE_DND_MESSAGE_COPY To \n");)
			MSG_CopyMessagesInto(m_pane, (MSG_ViewIndex*)indices, count, name);
		}
		else if ( effect == MSG_Drag_Not_Allowed )
		{
	  		DD(printf("### ThreadView::sourcedropfunc: Not Allowed \n");)
			char tmp[128];
                        XP_SAFE_SPRINTF(tmp, sizeof(tmp),
                                        "%s",
					XP_GetString(XFE_DND_MESSAGE_ERROR));
			XFE_Progress (m_contextData, tmp);

		}
		else /* should not reach here. the effect status should be with in
			the defined range. Please double check if you try to refer
			to any enum outside the effect enum scope */
			XP_ASSERT(0);
	}

  /* now we need to modify the chrome of windows viewing either folder */
  XFE_MozillaApp::theApp()->notifyInterested(XFE_MNView::folderChromeNeedsUpdating, m_folderInfo);
  XFE_MozillaApp::theApp()->notifyInterested(XFE_MNView::folderChromeNeedsUpdating, info);
}

void
XFE_ThreadView::source_drop_func(fe_dnd_Source *src, fe_dnd_Message msg, void *closure)
{
  XFE_ThreadView *obj = (XFE_ThreadView*)src->closure;

  obj->sourcedropfunc(src, msg, closure);
}

void
XFE_ThreadView::dropfunc(Widget /*dropw*/, fe_dnd_Event type,
			 fe_dnd_Source *source, XEvent *event)
{
  m_outliner->handleDragEvent(event, type, source);
}

void
XFE_ThreadView::drop_func(Widget dropw, void *closure, fe_dnd_Event type,
			  fe_dnd_Source *source, XEvent* event)
{
  XFE_ThreadView *obj = (XFE_ThreadView*)closure;

  obj->dropfunc(dropw, type, source, event);
}
#endif /* USE_MOTIF_DND */

void
XFE_ThreadView::openWithKey(MessageKey key )
{
  m_commandPending = selectByKey;
  m_pendingSelectionKey = key;
}

#if defined(USE_MOTIF_DND)
fe_icon_data *
XFE_ThreadView::GetDragIconData(int row, int /*column*/)
{
  D(printf("XFE_ThreadView::GetDragIconData()\n");)
  
  if (row == -1) return &MN_MailRead;
  else
	{
	  MSG_FolderLine folderLine;
	  int flags = m_messageLine.flags;

	  MSG_GetFolderLineById(XFE_MNView::getMaster(), m_folderInfo, &folderLine);

	  acquireLineData(row);
	  flags = m_messageLine.flags;
	  releaseLineData();

	  return flagToIconData(folderLine.flags, flags);
	}
}

fe_icon_data *
XFE_ThreadView::getDragIconData(void *this_ptr,
								int row,
								int column)
{
  D(printf("XFE_ThreadView::getDragIconData()\n");)
  XFE_ThreadView *thread_view = (XFE_ThreadView*)this_ptr;

  return thread_view->GetDragIconData(row, column);
}

void
XFE_ThreadView::GetDragTargets(int row, int column,
							   Atom **targets,
							   int *num_targets)
{
  D(printf("XFE_ThreadView::GetDragTargets()\n");)

  int flags;

  XP_ASSERT(row > -1);
  if (row == -1)
	{
	  *targets = NULL;
	  *num_targets = 0;
	}
  else
	{
	  acquireLineData(row);
	  flags = m_messageLine.flags;
	  releaseLineData();
	  
	  *num_targets = 5;
	  *targets = new Atom[ *num_targets ];

	  (*targets)[1] = XFE_DragBase::_XA_NETSCAPE_URL;
	  (*targets)[2] = XA_STRING;

	  if (isDisplayingNews())
		(*targets)[0] = XFE_OutlinerDrop::_XA_NETSCAPE_NEWS_MESSAGE;
	  else
		(*targets)[0] = XFE_OutlinerDrop::_XA_NETSCAPE_MAIL_MESSAGE;
	}
}

void
XFE_ThreadView::getDragTargets(void *this_ptr,
							   int row,
							   int column,
							   Atom **targets,
							   int *num_targets)
{
  D(printf("XFE_ThreadView::getDragTargets()\n");)
  XFE_ThreadView *thread_view = (XFE_ThreadView*)this_ptr;
 
 thread_view->GetDragTargets(row, column, targets, num_targets);
}

char *
XFE_ThreadView::DragConvert(Atom atom)
{
  if (atom == XFE_DragBase::_XA_NETSCAPE_URL)
	{
	  // translate drag data to NetscapeURL format
	  XFE_URLDesktopType urlData;
	  char *result;
	  const int *selection;
	  int count;
	  int i;

	  m_outliner->getSelection(&selection, &count);

	  urlData.createItemList(count);

	  for (i = 0; i < count; i ++)
		{
		  MessageKey key = MSG_GetMessageKey(m_pane, selection[i]);
		  URL_Struct *url = MSG_ConstructUrlForMessage(m_pane,
													   key);
		  urlData.url(i,url->address);

		  NET_FreeURLStruct(url);
		}

	  result = XtNewString(urlData.getString());
	  
	  return result;
	}
  else if (atom == XFE_OutlinerDrop::_XA_NETSCAPE_MAIL_MESSAGE
		   || atom == XFE_OutlinerDrop::_XA_NETSCAPE_NEWS_MESSAGE)
	{
	  char result[100];
	  const int *selection;
	  int count;

	  m_outliner->getSelection(&selection, &count);

	  if (count < 1) return XtNewString("0"); /* XXX ? */

	  sprintf (result, "%d", selection[0]);
	  return (char*)XtNewString(result); /* XXX fixme */
	}
  else if (atom == XA_STRING)
	{
#if notyet
	  char *result;
	  URL_Struct *url = MSG_ConstructUrlForMessage(m_pane,
												   m_dragmessage);


	  result = XtNewString(url->address);

	  NET_FreeURLStruct(url);

	  return result;
#endif
	}
}

char *
XFE_ThreadView::dragConvert(void *this_ptr,
							Atom atom)
{
  XFE_ThreadView *thread_view = (XFE_ThreadView*)this_ptr;
  
  return thread_view->DragConvert(atom);
}
#endif /* USE_MOTIF_DND */

void
XFE_ThreadView::selectThread()
{
  const int *selected;
  int i,count;
  MessageKey *keys;

  m_outliner->getSelection(&selected, &count);

  if ( count <= 0 ) return;
  keys = new MessageKey[count];

  for ( i = 0; i < count ; i++ )
  {
        keys[i] = MSG_GetMessageKey(m_pane, selected[i]);

  }
  m_outliner->deselectAllItems();

  for ( i = 0; i < count; i++ )
  {
        MSG_ViewIndex index = MSG_ThreadIndexOfMsg( m_pane, keys[ i ] );
        int delta = MSG_ExpansionDelta(m_pane, index);
        if ( delta > 0 )
                MSG_ToggleExpansion(m_pane, index, NULL);
        else
                delta = -delta;
        m_outliner->selectRangeByIndices(index, index+delta);
  }
  delete[] keys;

  showMessage(-1);
}
