/******************************************************************************
 GNBTreeWidgetBase.cc

	<Description>

	BASE CLASS = public JXNamedTreeListWidget

	Copyright © 1997 by Glenn W. Bach.  All rights reserved.
	
	Base code generated by Codemill v0.1.0

 *****************************************************************************/

#include <GNBTreeWidgetBase.h>
#include <GNBTreeDir.h>
#include <GNBRootNode.h>
#include <GNBCategoryNode.h>
#include <GNBNoteNode.h>
#include <GNBToDoNode.h>
#include <GNBGlobals.h>
#include "GNBNoteDir.h"
#include "GNBApp.h"

#include "selectedfolderIcon.xpm"
#include "GNBFolderIcon.xpm"
#include <tododone.xpm>
#include <todonotdone.xpm>
#include <todonotedone.xpm>
#include <todonotenotdone.xpm>

#include <JTree.h>
#include <JNamedTreeList.h>
#include <JNamedTreeNode.h>

#include <JXMenuBar.h>
#include <JXTextMenu.h>
#include <JString.h>
#include <JXWindow.h>
#include <JXWindowPainter.h>
#include <JXDirector.h>
#include <JXSelectionManager.h>
#include <JXUserNotification.h>
#include <JXApplication.h>
#include <JXDNDManager.h>
#include <JXChooseSaveFile.h>
#include <JXImage.h>
#include <JXDisplay.h>
#include <JXColormap.h>
#include <JXInputField.h>

#include <JFontManager.h>
#include <JTableSelection.h>
#include <JTableSelectionIterator.h>
#include <JOrderedSetIterator.h>
#include <JUNIXDirInfo.h>
#include <JUNIXDirEntry.h>
#include <JError.h>
#include <JStaticBuffer.h>

#include <jXCSFIcons.h>

#include <jDirUtil.h>
#include <jFileUtil.h>
#include <jProcessUtil.h>
#include <jASCIIConstants.h>
#include <jStrStreamUtil.h>
#include <jStreamUtil.h>
#include <jErrno.h>
#include <jMath.h>

#include <X11/keysym.h>
#include <strstream.h>
#include <iostream.h>
#include <fstream.h>
#include <jAssert.h>

const JCoordinate kDragBeginBuffer 		= 5;
const JCoordinate kCurrentPrefsVersion	= 2;
const JSize kDragLineWidth				= 2;

static const JCharacter* kDragMessagesXAtomName     = "GMailMessages";
static const JCharacter* kDragNBsXAtomName 			= "GNBBooks";
static const JCharacter* kDNDActionCopyDescrip  	= "copy the items";
static const JCharacter* kDNDActionMoveDescrip  	= "move the items";

const JCharacter* GNBTreeWidgetBase::kDataChanged = "DataChanged::GNBTreeWidgetBase";

/******************************************************************************
 Constructor

 *****************************************************************************/

GNBTreeWidgetBase::GNBTreeWidgetBase
	(
	const JIndex 		prefsIndex,
	JTree*				tree,
	JNamedTreeList* 	treeList, 
	JXScrollbarSet* 	scrollbarSet, 
	JXContainer* 		enclosure,
	const HSizingOption	hSizing, 
	const VSizingOption vSizing,
	const JCoordinate 	x, 
	const JCoordinate 	y,
	const JCoordinate 	w, 
	const JCoordinate 	h
	)
	:
   JXNamedTreeListWidget(treeList, scrollbarSet,
   							enclosure, hSizing, vSizing, x, y, w, h),
   	itsStringInputField(NULL),
   	itsPrefsIndex(prefsIndex),
   	itsIgnoreSelChangesFlag(kTrue)
{
	itsTree 	= tree;

	ListenTo(GetNamedTreeList());
	
	itsCurrentDndHereIndex 	= 0;
	itsOutsideRoot			= kFalse;
	itsWaitingToEdit		= kFalse;
	itsCurrentSelectionType	= kNoneSelected;
	itsDNDIsValid			= kFalse;

	const Atom dndName 	= (GetDNDManager())->GetDNDSelectionName();	
	itsMessageXAtom		= GetDisplay()->RegisterXAtom(kDragMessagesXAtomName);
	itsNoteXAtom	 	= AddSelectionTarget(dndName, kDragNBsXAtomName);

	itsCategoryIcon = new JXImage(GetDisplay(), GetColormap(), JXPM(GNBFolderIcon));
	assert( itsCategoryIcon != NULL );
	itsCategoryIcon->ConvertToRemoteStorage();

	itsSelectedCategoryIcon = new JXImage(GetDisplay(), GetColormap(), JXPM(selectedfolderIcon));
	assert( itsSelectedCategoryIcon != NULL );
	itsSelectedCategoryIcon->ConvertToRemoteStorage();

	itsToDoDoneIcon = new JXImage(GetDisplay(), GetColormap(), JXPM(tododone));
	assert( itsToDoDoneIcon != NULL );
	itsToDoDoneIcon->ConvertToRemoteStorage();

	itsToDoNotDoneIcon = new JXImage(GetDisplay(), GetColormap(), JXPM(todonotdone));
	assert( itsToDoNotDoneIcon != NULL );
	itsToDoNotDoneIcon->ConvertToRemoteStorage();

	itsToDoNoteDoneIcon = new JXImage(GetDisplay(), GetColormap(), JXPM(todonotedone));
	assert( itsToDoNoteDoneIcon != NULL );
	itsToDoNoteDoneIcon->ConvertToRemoteStorage();

	itsToDoNoteNotDoneIcon = new JXImage(GetDisplay(), GetColormap(), JXPM(todonotenotdone));
	assert( itsToDoNoteNotDoneIcon != NULL );
	itsToDoNoteNotDoneIcon->ConvertToRemoteStorage();

	itsNoteIcon = new JXImage(GetDisplay(), GetColormap(), JXGetPlainFileIcon());
	assert( itsNoteIcon != NULL );
	itsNoteIcon->ConvertToRemoteStorage();

	SetDrawOrder(JTable::kDrawByRow);

	ListenTo(GNBGetApplication());
	ListenTo(&(GetTableSelection()));

	SetIndentWidth(20);
	SetSelectionBehavior(kTrue, kTrue);
}

/******************************************************************************
 Destructor

 *****************************************************************************/

GNBTreeWidgetBase::~GNBTreeWidgetBase()
{
	delete itsSelectedCategoryIcon;
	delete itsCategoryIcon;
	delete itsToDoNotDoneIcon;
	delete itsToDoDoneIcon;
	delete itsToDoNoteDoneIcon;
	delete itsToDoNoteNotDoneIcon;
	delete itsNoteIcon;
}

/******************************************************************************
 Draw (virtual protected)


 ******************************************************************************/

void
GNBTreeWidgetBase::Draw
	(
	JXWindowPainter& p, 
	const JRect& rect
	)
{
	JXNamedTreeListWidget::Draw(p, rect);
	if (itsOutsideRoot && itsDNDIsValid)
		{
		p.ResetClipRect();
		if (itsCurrentDndHereIndex == 0)
			{
			JRect crect = GetCellRect(JPoint(2, GetRowCount()));
			crect.bottom -= 2;
			p.SetLineWidth(kDragLineWidth);
			p.Line(0, crect.bottom, crect.right, crect.bottom);
			}
		else 
			{
			JRect crect = GetCellRect(JPoint(2, itsCurrentDndHereIndex));
			crect.top++;
			p.SetLineWidth(kDragLineWidth);
			p.Line(0, crect.top, crect.right, crect.top);
			}
		}
}

/******************************************************************************
 Receive (virtual protected)


 ******************************************************************************/

void
GNBTreeWidgetBase::Receive
	(
	JBroadcaster*	sender,
	const Message&	message
	)
{
	if (sender == &(GetTableSelection()) && !itsIgnoreSelChangesFlag)
		{
		CleanSelection();
		}
	JXNamedTreeListWidget::Receive(sender, message);

	if (sender == GetNamedTreeList() && message.Is(JTreeList::kNodeInserted))
		{
		BroadcastDataChanged();
		const JTreeList::NodeInserted* info = 
			dynamic_cast(JTreeList::NodeInserted*, &message);
		assert(info != NULL);
		const JTreeNode* jnode = info->GetNode();
		GNBBaseNode* bnode = dynamic_cast(GNBBaseNode*, jnode);
		assert(bnode != NULL);
		if (bnode->GetType() == GNBBaseNode::kToDoNode)
			{
			GNBToDoNode* tnode = dynamic_cast(GNBToDoNode*, jnode);
			assert(tnode != NULL);
			SetCellStyle(JPoint(GetNodeColIndex(), info->GetIndex()), tnode->GetFontStyle());
			}
		}
	else if (sender == GetNamedTreeList() && message.Is(JTreeList::kNodeChanged))
		{
		BroadcastDataChanged();
		const JTreeList::NodeChanged* info = 
			dynamic_cast(JTreeList::NodeChanged*, &message);
		assert(info != NULL);
		const JTreeNode* jnode = info->GetNode();
		GNBBaseNode* bnode = dynamic_cast(GNBBaseNode*, jnode);
		assert(bnode != NULL);
		if (bnode->GetType() == GNBBaseNode::kToDoNode)
			{
			GNBToDoNode* tnode = dynamic_cast(GNBToDoNode*, jnode);
			assert(tnode != NULL);
			SetCellStyle(JPoint(GetNodeColIndex(), info->GetIndex()), tnode->GetFontStyle());
			}
		}
	else if (sender == GNBGetApplication() && message.Is(GNBApp::kDataChanged))
		{
		const GNBApp::DataChanged* info = 
			dynamic_cast(GNBApp::DataChanged*, &message);
		assert(info != NULL);
		JTreeNode* base = itsTree->GetRoot();
		GNBBaseNode* node = dynamic_cast(GNBBaseNode*, base);
		assert(node != NULL);
		JTreeNode* fnode;
		if (node->FindChildNode(info->GetID(), &fnode))
			{
			GNBNoteNode* nnode = dynamic_cast(GNBNoteNode*, fnode);
			assert(nnode != NULL);
			nnode->AdjustName(info->GetTitle());
			}
		}
}

/******************************************************************************
 HandleKeyPress

 ******************************************************************************/

void  
GNBTreeWidgetBase::HandleKeyPress
	(
	const int key,
	const JXKeyModifiers&   modifiers
	)
{
	if (key == kJReturnKey)
		{
		if (IsEditing())
			{
			JPoint cell;
			JBoolean ok = GetEditedCell(&cell);
			assert(ok);
			EndEditing();
			GetTableSelection().SelectCell(cell);
			GetTableSelection().SetBoat(cell);
			GetTableSelection().SetAnchor(cell);
			AdjustSelectionType();
			}
		else
			{
			HandleReturn();
			}
		}
	else if ((key == kJEscapeKey) && IsEditing())
		{
		JPoint cell;
		JBoolean ok = GetEditedCell(&cell);
		assert(ok);
		CancelEditing();
		GetTableSelection().SelectCell(cell);
		GetTableSelection().SetBoat(cell);
		GetTableSelection().SetAnchor(cell);
		AdjustSelectionType();
		}
	else if ((key == kJUpArrow || key == kJDownArrow) && !IsEditing())
		{
		const JBoolean hasSelection = (GetTableSelection()).HasSelection();
		if (!hasSelection && key == kJUpArrow && GetRowCount() > 0)
			{
			SelectedFlushed();
			SelectSingleCell(JPoint(GetNodeColIndex(), GetRowCount()));
			AdjustSelectionType();
			}
		else if (!hasSelection && key == kJDownArrow && GetRowCount() > 0)
			{
			SelectedFlushed();
			SelectSingleCell(JPoint(GetNodeColIndex(), 1));
			AdjustSelectionType();
			}
		else
			{
			itsIgnoreSelChangesFlag = kFalse;
			if (!modifiers.shift())
				{
				SelectedFlushed();
				}
			HandleSelectionKeyPress(key, modifiers);
			if (!modifiers.shift())
				{
				AdjustSelectionType();
				}
			itsIgnoreSelChangesFlag = kTrue;
			}
		}
	else if (key == ' ')
		{
		SelectedFlushed();
		itsIgnoreSelChangesFlag = kFalse;
		JXNamedTreeListWidget::HandleKeyPress(key, modifiers);
		itsIgnoreSelChangesFlag = kTrue;
		}
	else
		{
		itsIgnoreSelChangesFlag = kFalse;
		JXNamedTreeListWidget::HandleKeyPress(key, modifiers);
		itsIgnoreSelChangesFlag = kTrue;
		}
}


/******************************************************************************
 HandleMouseDown

 ******************************************************************************/

void
GNBTreeWidgetBase::HandleMouseDown
	(
	const JPoint&			pt,
	const JXMouseButton 	button,
	const JSize 			clickCount,
	const JXButtonStates& 	buttonStates,
	const JXKeyModifiers& 	modifiers
	)
{
	itsDownInCell 		= kFalse;
	itsWaitingToEdit 	= kFalse;
	itsDownPt 			= pt;
	JTableSelection& s  = GetTableSelection();

	JPoint cell;
	JXNamedTreeListWidget::NodePart nodePart;
	if (GetNode(pt, &cell, &nodePart))
		{
		JIndex index 			= cell.y;
		const JPoint newBoat 	= JPoint(2, index);
		const JPoint newAnchor 	= JPoint(2, index);
		if ((nodePart >= JXNamedTreeListWidget::kInText) && 
			(!modifiers.shift()) &&
			(!modifiers.meta()) && (!modifiers.control()) &&
			(button == kJXLeftButton))
			{
			itsWaitingToEdit 	= kTrue;
			}

		if (nodePart == JXNamedTreeListWidget::kInImage)
			{
			itsDownInCell = kTrue;
			if (clickCount == 2)
				{
				DoubleClickIcon(cell.y, modifiers);
				}
			}
		if (((nodePart == JXNamedTreeListWidget::kInImage) ||
			 (nodePart == JXNamedTreeListWidget::kInText)) && 
			 (!itsWaitingToEdit))
			{
			JTreeNode* node 		= GetTreeList()->GetNode(index);
			GNBBaseNode* bnode		= dynamic_cast(GNBBaseNode*, node);
			assert(bnode != NULL);
			GNBBaseNode::Type type 	= bnode->GetType();
			JSize depth 			= node->GetDepth();
			JSize isBranch			= node->IsOpenable();
			JBoolean ok = kTrue;
			if (((itsCurrentSelectionType == kToDosSelected) ||
				 (itsCurrentSelectionType == kNotesSelected) ||
				 (itsCurrentSelectionType == kItemsSelected)) &&
				(modifiers.shift() || modifiers.control()))
				{
				if (isBranch)
					{
					ok 				= kFalse;
					itsDownInCell	= kFalse;
					}
				}
			else if ((itsCurrentSelectionType == kCategoriesSelected) &&
					 (modifiers.shift() || modifiers.control()))
				{
				if ((type != GNBBaseNode::kCategoryNode) || (depth != itsSelectionDepth))
					{
					ok 				= kFalse;
					itsDownInCell	= kFalse;
					}
				}
			if (ok)
				{
				if (type == GNBBaseNode::kCategoryNode)
					{
					itsCurrentSelectionType = kCategoriesSelected;
					itsSelectionDepth		= depth;
					}
				else if (type == GNBBaseNode::kToDoNode)
					{
					if (itsCurrentSelectionType == kNoneSelected)
						{
						itsCurrentSelectionType = kToDosSelected;
						}
					else if (itsCurrentSelectionType == kNotesSelected)
						{
						itsCurrentSelectionType = kItemsSelected;
						}
					}
				else if (type == GNBBaseNode::kNoteNode)
					{
					if (itsCurrentSelectionType == kNoneSelected)
						{
						itsCurrentSelectionType = kNotesSelected;
						}
					else if (itsCurrentSelectionType == kToDosSelected)
						{
						itsCurrentSelectionType = kItemsSelected;
						}
					}
				}
			if ((button == kJXLeftButton && modifiers.shift()) ||
				(button == kJXRightButton))
				{
				if (s.OKToExtendSelection() && ok)
					{
					s.ExtendSelection(newBoat);
					CleanSelection();
					TableRefresh();
					}
				}
			else if (button == kJXLeftButton)
				{
				if (modifiers.control())
					{
					if (ok)
						{
						if (s.IsSelected(index, 2))
							{
							s.SelectCell(index, 2, kFalse);
							s.ClearBoat();
							s.ClearAnchor();
							TableRefreshRow(index);
							}
						else
							{
							s.SelectCell(index, 2, kTrue);
							s.SetBoat(newBoat);
							s.SetAnchor(newAnchor);
							TableRefreshRow(index);
							}
						}
					}
				else if (!s.IsSelected(index, 2) && !modifiers.meta())
					{
					s.ClearSelection();
					s.SelectCell(index, 2);
					s.SetBoat(newBoat);
					s.SetAnchor(newAnchor);
					TableRefresh();
					if (type == GNBBaseNode::kCategoryNode)
						{
						itsCurrentSelectionType = kCategoriesSelected;
						itsSelectionDepth		= depth;
						}
					else if (type == GNBBaseNode::kToDoNode)
						{
						itsCurrentSelectionType = kToDosSelected;
						}
					else if (type == GNBBaseNode::kNoteNode)
						{
						itsCurrentSelectionType = kNotesSelected;
						}
					}
				}
			}
		else
			{
			s.ClearSelection();
			itsCurrentSelectionType = kNoneSelected;
			}
		}
	else
		{
		itsDownInCell = kFalse;
		s.ClearSelection();
		itsCurrentSelectionType = kNoneSelected;
		}
	if (itsDownInCell &&
		(s.GetSelectedCellCount() > 1) &&
		(itsCurrentSelectionType == kCategoriesSelected))
		{
		itsDownInCell = kFalse;
		}
	JXNamedTreeListWidget::HandleMouseDown(pt, button, 
		clickCount, buttonStates, modifiers);
}

/******************************************************************************
 HandleMouseDrag (virtual protected)

 ******************************************************************************/

void
GNBTreeWidgetBase::HandleMouseDrag
	(
	const JPoint&			pt,
	const JXButtonStates&	buttonStates,
	const JXKeyModifiers&	modifiers
	)
{
	JPoint cell;
	JXNamedTreeListWidget::NodePart part;
	if (GetNode(pt, &cell, &part))
		{
		JPoint p = itsDownPt - pt;
		if ((JLAbs(p.x) > kDragBeginBuffer) || (JLAbs(p.y) > kDragBeginBuffer))
			{
			itsWaitingToEdit = kFalse;
			if (itsDownInCell)
				{
				BeginDND(pt, buttonStates, modifiers);
				}
			}
		}
	else
		{
		itsWaitingToEdit = kFalse;
		}
		
	JXNamedTreeListWidget::HandleMouseDrag(pt,buttonStates,modifiers);
}

/******************************************************************************
 HandleMouseUp (virtual protected)

 ******************************************************************************/

void
GNBTreeWidgetBase::HandleMouseUp
	(
	const JPoint& pt, 
	const JXMouseButton button,
	const JXButtonStates& buttonStates,
	const JXKeyModifiers& modifiers
	)
{
	JPoint cell;
	if (GetCell(pt, &cell))
		{
		JTreeNode* jnode = GetNamedTreeList()->GetNode(cell.y);
		GNBBaseNode* bnode = dynamic_cast(GNBBaseNode*, jnode);
		assert(bnode != NULL);
		GNBBaseNode::Type type = bnode->GetType();
		if (itsWaitingToEdit)
			{
			JXNamedTreeListWidget::HandleMouseUp(pt, button, buttonStates, modifiers);
			JNamedTreeNode* node = dynamic_cast(JNamedTreeNode*, jnode);
			assert(node != NULL);
			JTableSelection& s  = GetTableSelection();
			s.ClearSelection();
			BeginEditing(cell);
			JXInputField* input;
			if (GetXInputField(&input))
				{
				input->SetIsRequired(kFalse);
				}
			//TableScrollToCell(JPoint(1, cell.y));
			}
		else if (itsDownInCell && (type == GNBBaseNode::kToDoNode) && modifiers.meta())
			{
			GNBToDoNode* tdnode = dynamic_cast(GNBToDoNode*, jnode);
			assert(tdnode != NULL);
			tdnode->ToggleDone();
			itsTree->BroadcastChange(tdnode);
			}
		else
			{
			JXNamedTreeListWidget::HandleMouseUp(pt, button, buttonStates, modifiers);
			}
		}
	else
		{
		JXNamedTreeListWidget::HandleMouseUp(pt, button, buttonStates, modifiers);
		}
}

/******************************************************************************
 HandleDNDResponse (virtual protected)

 ******************************************************************************/

void
GNBTreeWidgetBase::HandleDNDResponse
	(
	const JXContainer*  target,
	const JBoolean 		dropAccepted, 
	const Atom 			action
	)
{
//	DisplayCursor(itsDNDCursor);
	JXNamedTreeListWidget::HandleDNDResponse(target, dropAccepted, action);
}

/******************************************************************************
 HandleDNDHere 

 ******************************************************************************/

void
GNBTreeWidgetBase::HandleDNDHere
	(
	const JPoint& 	pt,
	const JXWidget* source
	)
{
	ScrollForDrag(pt);
	JIndex oldIndex = itsCurrentDndHereIndex;
	if (oldIndex != 0)
		{
		TableRefreshRow(oldIndex);
		}
	else if (itsOutsideRoot)
		{
		JSize count = GetRowCount();
		if (count > 0)
			{
			TableRefreshRow(count);
			}
		}

	JPoint cell;
	JXNamedTreeListWidget::NodePart nodePart;
	if (GetNode(pt, &cell, &nodePart))
		{
		itsCurrentDndHereIndex = ClosestSelection(pt, cell.y, source);
		TableRefreshRow(itsCurrentDndHereIndex);
		}
	else
		{
		itsCurrentDndHereIndex = ClosestSelection(pt, 0, source);
		if (itsCurrentDndHereIndex == 0)
			{
			JSize count = GetRowCount();
			if (count > 0)
				{
				TableRefreshRow(count);
				}
			}
		}
}

/******************************************************************************
 HandleDNDLeave 

 ******************************************************************************/

void
GNBTreeWidgetBase::HandleDNDLeave()
{
	JIndex oldIndex 		= itsCurrentDndHereIndex;
	itsCurrentDndHereIndex 	= 0;
	if (oldIndex != 0)
		{
		TableRefreshRow(oldIndex);
		}
	else if (itsOutsideRoot)
		{
		JSize count = GetRowCount();
		if (count > 0)
			{
			TableRefreshRow(count);
			}
		}
	itsOutsideRoot		= kFalse;
}

/******************************************************************************
 HandleDNDDrop 

 ******************************************************************************/

void
GNBTreeWidgetBase::HandleDNDDrop
	(
	const JPoint& 		pt,
	const JArray<Atom>& typeList,
	const Atom 			action,
	const Time			time,
	const JXWidget* 	source
	)
{
	JBoolean dropOnSelf = kTrue;

	Atom realAction = action;
	JXDNDManager* dndMgr  = GetDNDManager();
	if (action == dndMgr->GetDNDActionAskXAtom())
		{
		JArray<Atom> actionList;
		JPtrArray<JString> descriptionList;
		descriptionList.SetCleanUpAction(JPtrArrayT::kDeleteAllAsObjects);
		if (!dndMgr->GetAskActions(&actionList, &descriptionList))
			{
			realAction = dndMgr->GetDNDActionMoveXAtom();
			}
		else if (!dndMgr->ChooseDropAction(actionList, descriptionList,
										   &realAction))
			{
			itsOutsideRoot	= kFalse;
			return;
			}
		}
	JPoint cell;
	JIndex oldIndex = itsCurrentDndHereIndex;
	itsCurrentDndHereIndex = 0;
	if (oldIndex != 0)
		{
		TableRefreshRow(oldIndex);
		}
	if (GetNamedTreeList()->GetElementCount() < oldIndex)
		{
		itsOutsideRoot	= kFalse;
		return;
		}
	if (source != this)
		{
		dropOnSelf = kFalse;
		}
	
	if (dropOnSelf)
		{
		JTableSelection& s = GetTableSelection();
		JTableSelectionIterator iter(&s);
		if (itsDNDIsValid)
			{
			JPtrArray<JTreeNode> nodes;
			GetSelectedNodes(&nodes);
			ArrangeDroppedNodes(nodes, oldIndex);
			/*
			if (itsOutsideRoot && oldIndex == 0)
				{
				JTreeNode* lastnode = GetTreeList()->GetNode(GetTreeList()->GetElementCount());
				JTreeNode* lastparent = lastnode->GetParent();
				JPoint cell;
				while (iter.Next(&cell))
					{
					JTreeNode* snode = GetTreeList()->GetNode(cell.y);
					lastparent->Append(snode);
					}
				}
			else if (itsOutsideRoot)
				{
				JTreeNode* inode = GetTreeList()->GetNode(oldIndex);
				JTreeNode* iparent = inode->GetParent();
				JPoint cell;
				while (iter.Next(&cell))
					{
					JTreeNode* snode = GetTreeList()->GetNode(cell.y);
					iparent->InsertBefore(inode,snode);
					}
				}
			else
				{
				JTreeNode* cnode = GetTreeList()->GetNode(oldIndex);
				JPoint cell;
				while (iter.Next(&cell))
					{
					JTreeNode* snode = GetTreeList()->GetNode(cell.y);
					cnode->Append(snode);
					}
				}
				*/
			}			
			
		}
	else if ((itsDragType == kDraggingNotes) &&
			 (source != NULL))
		{
		JPtrArray<JTreeNode> nodes;
		GNBTreeWidgetBase* other = dynamic_cast(GNBTreeWidgetBase*, source);
		assert(other != NULL);
		other->GetSelectedNodes(&nodes);
		ArrangeDroppedNodes(nodes, oldIndex);
		}
	else
		{/*
		JXSelectionManager* selManager = GetSelectionManager();
		Atom textAtom = selManager->GetMimePlainTextXAtom();
		unsigned char* data = NULL;
		JSize dataLength;
		Atom returnType;
		JXSelectionManager::DeleteMethod delMethod;
		const Atom dndName = (GetDNDManager())->GetDNDSelectionName();
		if (selManager->GetSelectionData(dndName, time, GetWindow(), textAtom,
										 &returnType, &data, &dataLength, &delMethod))
			{
			JString textData(reinterpret_cast<char*>(data), dataLength);
			JPtrArray<JString> list;
			GParseNameList(textData, list);
			JSize count = list.GetElementCount();
			if (itsOutsideRoot)
				{
				JString book;
				if (NewBook(&book))
					{
					JIndex findex = GetNamedTreeList()->GetElementCount();
					JTreeNode* jbook = GetNamedTreeList()->GetNode(findex);
					GetNamedTreeList()->OpenBranch(findex);
					JNamedTreeNode* nbook =
						dynamic_cast(JNamedTreeNode*, jbook);
					assert(nbook != NULL);
					NewNickName(nbook, list);
					}
				}
			else
				{
				assert(oldIndex <= GetNamedTreeList()->GetElementCount());
				JTreeNode* jnode = GetNamedTreeList()->GetNode(oldIndex);
				JNamedTreeNode* nnode =
					dynamic_cast(JNamedTreeNode*, jnode);
				assert(nnode != NULL);				
				JSize depth = jnode->GetDepth();
				if (depth == 1)
					{
					NewNickName(nnode, list);
					}
				else if (depth == 2)
					{
					for (JSize i = count; i >= 1; i--)
						{
						AddAddressToEntry(nnode, *(list.NthElement(i)));
						}
					}
				}
			selManager->DeleteSelectionData(&data, delMethod);
			list.DeleteAll();
			if (realAction == dndMgr->GetDNDActionMoveXAtom())
				{
				selManager->SendDeleteSelectionRequest(dndName, time, GetWindow());
				}
			}
		*/}
	itsOutsideRoot	= kFalse;
}

/******************************************************************************
 WillAcceptDrop 

 ******************************************************************************/

JBoolean
GNBTreeWidgetBase::WillAcceptDrop
	(
	const JArray<Atom>& typeList, 
	Atom*				action,
	const Time			time,
	const JXWidget*		source
	)
{
	JBoolean found 				= kFalse;
	const JSize typeCount 		= typeList.GetElementCount();
	for (JSize i=1; i<=typeCount; i++)
		{
		Atom type 				= typeList.GetElement(i);
		if (type == itsNoteXAtom)
			{
			found			= kTrue;
			itsDragType		= kDraggingNotes;
			}
		}
	return found;
}

/******************************************************************************
 ConvertSelection 

 ******************************************************************************/

JBoolean 
GNBTreeWidgetBase::ConvertSelection
	(
	const Atom 		name, 
	const Atom 		requestType,
	Atom* 			returnType,
	unsigned char**	data,
	JSize* 			dataLength, 
	JSize* 			bitsPerBlock
	)
{
/*	*bitsPerBlock = 8;
	ostrstream os;
	
	JTableSelection& selection = GetTableSelection();
	JTableSelectionIterator* iter = 
		new JTableSelectionIterator(&selection);

	JPoint cell;
	if (itsDraggingBranch)
		{
		JSize count = 0;
		while(iter->Next(&cell))
			{
			JTreeNode* jnode = GetNamedTreeList()->GetNode(cell.y);
			count += jnode->GetChildCount();
			}
		os << count;
		iter->MoveTo(kIteratorStartAtBeginning, cell);
		while(iter->Next(&cell))
			{
			JTreeNode* jnode = GetNamedTreeList()->GetNode(cell.y);
			JSize count = jnode->GetChildCount();
			for (JSize j = 1; j <= count; j++)
				{
				JTreeNode* child = jnode->GetChild(j);
				GSimpleFileTreeNode* node = dynamic_cast(GSimpleFileTreeNode*, child);
				assert(node != NULL);
				node->WriteFileName(os);
				}
			
			}
		}
	else
		{
		os << selection.GetSelectedCellCount();
		while(iter->Next(&cell))
			{
			JTreeNode* jnode = GetNamedTreeList()->GetNode(cell.y);
			GMailboxTreeNode* node = dynamic_cast(GMailboxTreeNode*, jnode);
			assert(node != NULL);
			node->WriteFileName(os);
			}
		}
		
	os << ends;
	delete iter;
	*dataLength = strlen(os.str());
	*data = new unsigned char[ *dataLength ];
	assert (*data != NULL);
	memcpy(*data, os.str(), *dataLength);
	return kTrue;
*/
	return kFalse;
}

/******************************************************************************
 GetIcon

******************************************************************************/

JBoolean
GNBTreeWidgetBase::GetImage
	(
	const JIndex 	index,
	const JXImage** image
	)
	const
{
	const JTreeNode* jnode = GetNamedTreeList()->GetNode(index);
	GNBBaseNode* bnode = dynamic_cast(GNBBaseNode*, jnode);
	assert(bnode != NULL);
	GNBBaseNode::Type type = bnode->GetType();
	if (type == GNBBaseNode::kCategoryNode)
		{
		if((index == itsCurrentDndHereIndex) &&
	   		!itsOutsideRoot &&
	   		itsDNDIsValid)
			{
			*image = itsSelectedCategoryIcon;
			return kTrue;
			}
		else
			{
			*image = itsCategoryIcon;
			return kTrue;
			}
		}
	else if (type == GNBBaseNode::kToDoNode)
		{
		GNBToDoNode* tdnode = dynamic_cast(GNBToDoNode*, jnode);
		assert(tdnode != NULL);
		if (tdnode->IsDone())
			{
			if (tdnode->HasNote())
				{
				*image = itsToDoNoteDoneIcon;
				return kTrue;
				}
			*image = itsToDoDoneIcon;
			return kTrue;
			}
		if (tdnode->HasNote())
			{
			*image = itsToDoNoteNotDoneIcon;
			return kTrue;
			}
		*image = itsToDoNotDoneIcon;
		return kTrue;
		}
	else if (type == GNBBaseNode::kNoteNode)
		{
		*image = itsNoteIcon;
		return kTrue;
		}
	return JXNamedTreeListWidget::GetImage(index, image);
}

/******************************************************************************
ClosestSelection (private)

 ******************************************************************************/

JIndex
GNBTreeWidgetBase::ClosestSelection
	(
	const JPoint& 	pt,
	const JIndex  	index,
	const JXWidget* source
	)
{
	itsDNDIsValid		= kTrue;
	itsOutsideRoot		= kTrue;
	JTreeNode* jnode;
	JTableSelection& s  = GetTableSelection();
	if (index == 0)
		{
		JSize count = GetTreeList()->GetElementCount();
		if (count == 0)
			{
			return index;
			}
		jnode = GetTreeList()->GetNode(GetTreeList()->GetElementCount());
		}
	else
		{
		JPoint cell;
		JXNamedTreeListWidget::NodePart nodePart;
		GetNode(pt, &cell, &nodePart);
		jnode = GetTreeList()->GetNode(index);
		if (s.IsSelected(index, 2))
			{
			itsDNDIsValid = kFalse;
			return index;
			}
		if (jnode->IsOpenable() && 
			((nodePart == JXNamedTreeListWidget::kInImage) ||
			 (nodePart == JXNamedTreeListWidget::kInText)))
			{
			itsOutsideRoot = kFalse;
			}
		}
	JSize depth 	 = jnode->GetDepth();

	if ((source == this) && 
		(itsCurrentSelectionType == kCategoriesSelected))
		{
		JPoint cell;
		JBoolean ok = s.GetFirstSelectedCell(&cell);
		assert(ok);
		JTreeNode* dnode = GetTreeList()->GetNode(cell.y);
		
		// We can't drop on ourselves
		if (dnode == jnode)
			{
			itsDNDIsValid = kFalse;
			return index;
			}
		ok 			 		= kTrue;
		JSize ddepth 		= dnode->GetDepth();
		JTreeNode* jparent 	= jnode->GetParent();
		depth 				= jparent->GetDepth();
		while (ok && (depth >= ddepth))
			{
			// we can't drop into one of our children
			if (jparent == dnode)
				{
				ok = kFalse;
				}
			jnode		= jparent;
			jparent 	= jnode->GetParent();
			depth 		= jparent->GetDepth();
			}
		if (!ok)
			{
			itsDNDIsValid = kFalse;
			}
		return index;
		}
	
	return index;
}

/******************************************************************************
 NewNameOK (virtual protected)

 ******************************************************************************/

JBoolean
GNBTreeWidgetBase::ExtractInputData
	(
	const JPoint& cell
	)
{
	JBoolean ok = JXNamedTreeListWidget::ExtractInputData(cell);
	if (ok)
		{
		JTreeNode* jnode = GetTreeList()->GetNode(cell.y);
		GNBBaseNode* bnode = dynamic_cast(GNBBaseNode*, jnode);
		assert(bnode != NULL);
		GNBBaseNode::Type type = bnode->GetType();
		if (type == GNBBaseNode::kNoteNode)
			{
			GNBNoteNode* nnode = dynamic_cast(GNBNoteNode*, jnode);
			assert(nnode != NULL);
			nnode->SetHasName(kTrue);
			}
		}
	return ok;
}

/******************************************************************************
 DoubleClickIcon (virtual public)

 ******************************************************************************/

void
GNBTreeWidgetBase::DoubleClickIcon
	(
	const JIndex 			index, 
	const JXKeyModifiers& 	modifiers
	)
{
	JTreeNode* jnode = GetTreeList()->GetNode(index);
	GNBBaseNode* bnode = dynamic_cast(GNBBaseNode*, jnode);
	assert(bnode != NULL);
	GNBBaseNode::Type type = bnode->GetType();
	if (type == GNBBaseNode::kNoteNode)
		{
		GNBNoteNode* node = dynamic_cast(GNBNoteNode*, jnode);
		assert(node != NULL);
		GNBGetApplication()->OpenNote(node->GetID());
		}
	else if (type == GNBBaseNode::kToDoNode)
		{
		GNBToDoNode* tdnode = dynamic_cast(GNBToDoNode*, jnode);
		assert(tdnode != NULL);
		JFAID_t id;
		if (tdnode->GetID(&id))
			{
			GNBGetApplication()->OpenNote(id);
			}
		}
}

/******************************************************************************
 GetDNDAction (virtual protected)

	This is called repeatedly during the drag so the drop action can be
	changed based on the current target, buttons, and modifier keys.

 ******************************************************************************/

Atom
GNBTreeWidgetBase::GetDNDAction
	(
	const JXContainer*		target,
	const JXButtonStates&	buttonStates,
	const JXKeyModifiers&	modifiers
	)
{
	if (modifiers.control())
		{
		return (GetDNDManager())->GetDNDActionAskXAtom();
		}
	else if (modifiers.meta())
		{
		return (GetDNDManager())->GetDNDActionCopyXAtom();
		}
	else
		{
		return (GetDNDManager())->GetDNDActionMoveXAtom();
		}
}

/******************************************************************************
 GetDNDAskActions (virtual protected)

	This is called when the value returned by GetDropAction() changes to
	XdndActionAsk.  If GetDropAction() repeatedly returns XdndActionAsk,
	this function is not called again because it is assumed that the
	actions are the same within a single DND session.

	This function must place at least 2 elements in askActionList and
	askDescriptionList.

	The first element should be the default action.

 ******************************************************************************/

void
GNBTreeWidgetBase::GetDNDAskActions
	(
	const JXButtonStates&	buttonStates,
	const JXKeyModifiers&	modifiers,
	JArray<Atom>*			askActionList,
	JPtrArray<JString>*		askDescriptionList
	)
{
	JXDNDManager* dndMgr = GetDNDManager();
	askActionList->AppendElement(dndMgr->GetDNDActionCopyXAtom());
	askActionList->AppendElement(dndMgr->GetDNDActionMoveXAtom());

	JString* s = new JString(kDNDActionCopyDescrip);
	assert( s != NULL );
	askDescriptionList->Append(s);

	s = new JString(kDNDActionMoveDescrip);
	assert( s != NULL );
	askDescriptionList->Append(s);
}

/******************************************************************************
 Save (public)

 ******************************************************************************/

void
GNBTreeWidgetBase::Save()
{
	// save the nodes
	JTreeNode* base = itsTree->GetRoot();
	GNBRootNode* node = dynamic_cast(GNBRootNode*, base);
	assert(node != NULL);
	ostrstream os;
	os << kCurrentPrefsVersion << ' ';
	node->SaveBranch(os);

	// save the node's open status
	JSize count = GetTreeList()->GetElementCount();
	for (JSize i = 1; i <= count; i++)
		{
		if (GetTreeList()->IsOpen(i))
			{
			os << i << ' ';
			}
		}
	os << 0 << ' ';
	GetWindow()->WriteGeometry(os);
	assert(GNBGetApplication()->GetFileArray()->IndexValid(JFAIndex(itsPrefsIndex)));
	GNBGetApplication()->GetFileArray()->SetElement(JFAIndex(itsPrefsIndex), os);
}

/******************************************************************************
 ReadSetup (private)

 ******************************************************************************/

void
GNBTreeWidgetBase::ReadSetup()
{
	JStaticBuffer data;
	GNBGetApplication()->GetFileArray()->GetElement(JFAIndex(itsPrefsIndex), &data);
	if (!data.GetLength() == 0)
		{
		jistrstream(is, data.GetData(), strlen(data));
		JFileVersion version;
		is >> version;
		JTreeNode* base = itsTree->GetRoot();
		GNBRootNode* node = dynamic_cast(GNBRootNode*, base);
		assert(node != NULL);
		node->ReadBranchSetup(is, version);

		JIndex index;
		is >> index;
		while (index != 0)
			{
			if (index <= GetTreeList()->GetElementCount())
				{
				GetTreeList()->Open(index);
				}
			is >> index;
			}
		GetWindow()->ReadGeometry(is);
		}
}

/******************************************************************************
 CleanSelection (private)

 ******************************************************************************/

void
GNBTreeWidgetBase::CleanSelection()
{
	JTableSelection& s = GetTableSelection();
	JTableSelectionIterator iter(&s);
	
	JPoint cell;
	if (s.GetSingleSelectedCell(&cell))
		{
		AdjustSelectionType();
		return;
		}
	while (iter.Next(&cell) && cell.x == 2)
		{
		JTreeNode* node			= GetTreeList()->GetNode(cell.y);
		GNBBaseNode* bnode		= dynamic_cast(GNBBaseNode*, node);
		assert(bnode != NULL);
		GNBBaseNode::Type type 	= bnode->GetType();
		JSize depth				= node->GetDepth();
		JBoolean isBranch		= node->IsOpenable();
		JBoolean ok = kTrue;
		if (itsCurrentSelectionType != kCategoriesSelected)
			{
			if (isBranch)
				{
				s.SelectCell(cell.y, 2, kFalse);
				}
			}
		else if (itsCurrentSelectionType == kCategoriesSelected)
			{
			if ((type != GNBBaseNode::kCategoryNode) || 
				(depth != itsSelectionDepth))
				{
				s.SelectCell(cell.y, 2, kFalse);
				}
			}
		}

}

/******************************************************************************
 SelectedFlushed (public)

 ******************************************************************************/

void
GNBTreeWidgetBase::SelectedFlushed()
{
	itsCurrentSelectionType = kNoneSelected;
}

/******************************************************************************
 GetSelectedNodes (private)

 ******************************************************************************/

void
GNBTreeWidgetBase::GetSelectedNodes
	(
	JPtrArray<JTreeNode>* nodes
	)
{
	JTableSelection& s = GetTableSelection();
	JTableSelectionIterator iter(&s);
	JPoint cell;
	while (iter.Next(&cell))
		{
		JTreeNode* node = GetTreeList()->GetNode(cell.y);
		nodes->Append(node);
		}
	s.ClearSelection();
	itsCurrentSelectionType = kNoneSelected;
}

/******************************************************************************
 ArrangeDroppedNodes (private)

 ******************************************************************************/

void
GNBTreeWidgetBase::ArrangeDroppedNodes
	(
	JPtrArray<JTreeNode>&	nodes,
	const JIndex 			index
	)
{
	JSize nodeCount = nodes.GetElementCount();
	if (itsOutsideRoot && index == 0)
		{
		JTreeNode* lastparent;
		JSize count = GetTreeList()->GetElementCount();
		if (count > 0)
			{
			JTreeNode* lastnode = GetTreeList()->GetNode(count);
			lastparent = lastnode->GetParent();
			}
		else
			{
			lastparent = GetTreeList()->GetTree()->GetRoot();
			}
		for (JSize i = 1; i <= nodeCount; i++)
			{
			lastparent->Append(nodes.NthElement(i));
			}
		}
	else if (itsOutsideRoot)
		{
		JTreeNode* inode = GetTreeList()->GetNode(index);
		JTreeNode* iparent = inode->GetParent();
		for (JSize i = 1; i <= nodeCount; i++)
			{
			iparent->InsertBefore(inode, nodes.NthElement(i));
			}
		}
	else
		{
		JTreeNode* cnode = GetTreeList()->GetNode(index);
		for (JSize i = 1; i <= nodeCount; i++)
			{
			cnode->Append(nodes.NthElement(i));
			}
		}
}

/******************************************************************************
 BroadcastDataChanged (protected)

 ******************************************************************************/

void
GNBTreeWidgetBase::BroadcastDataChanged()
{
	Broadcast(DataChanged());
}

/******************************************************************************
 WantsInputFieldKey (virtual)

	Returns kTrue if it wants to preempt the input field from getting the key.

 ******************************************************************************/

JBoolean
GNBTreeWidgetBase::WantsInputFieldKey
	(
	const int				key,
	const JXKeyModifiers&	modifiers
	)
	const
{
	return JConvertToBoolean(
		key == kJReturnKey || key == kJEscapeKey ||
		(modifiers.meta() &&
		 (key == kJUpArrow || key == '8' ||
		  key == kJDownArrow || key == '2')));
}

/******************************************************************************
 AdjustSelectionType (private)

 ******************************************************************************/

void
GNBTreeWidgetBase::AdjustSelectionType()
{
	JPoint cell;
	if (!(GetTableSelection()).GetSingleSelectedCell(&cell))
		{
		return;
		}
	JTreeNode* node 		= GetTreeList()->GetNode(cell.y);
	GNBBaseNode* bnode		= dynamic_cast(GNBBaseNode*, node);
	assert(bnode != NULL);
	GNBBaseNode::Type type 	= bnode->GetType();
	JSize depth 			= node->GetDepth();
	if (type == GNBBaseNode::kCategoryNode)
		{
		itsCurrentSelectionType = kCategoriesSelected;
		itsSelectionDepth		= depth;
		}
	else if (type == GNBBaseNode::kToDoNode)
		{
		itsCurrentSelectionType = kToDosSelected;
		}
	else if (type == GNBBaseNode::kNoteNode)
		{
		itsCurrentSelectionType = kNotesSelected;
		}
}

/******************************************************************************
 IsSelectable (virtual protected)

 ******************************************************************************/

JBoolean
GNBTreeWidgetBase::IsSelectable
	(
	const JPoint& cell,
	const JBoolean forExtend
	)
	const
{
	if (!JXNamedTreeListWidget::IsSelectable(cell, forExtend))
		{
		return kFalse;
		}
	GNBTreeWidgetBase* th 	= const_cast<GNBTreeWidgetBase*>(this);
	
	const JTreeNode* node	= GetTreeList()->GetNode(cell.y);
	GNBBaseNode* bnode		= dynamic_cast(GNBBaseNode*, node);
	assert(bnode != NULL);
	GNBBaseNode::Type type 	= bnode->GetType();
	JSize depth 			= node->GetDepth();
	if (itsCurrentSelectionType == kCategoriesSelected)
		{
		if ((type == GNBBaseNode::kCategoryNode) &&
			(depth == itsSelectionDepth))
			{
			return kTrue;
			}
		else
			{
			return kFalse;
			}
		}
	else if (type == GNBBaseNode::kCategoryNode)
		{
		if (itsCurrentSelectionType == kNoneSelected)
			{
			th->itsCurrentSelectionType = kCategoriesSelected;
			th->itsSelectionDepth		= depth;
			return kTrue;	
			}
		return kFalse;
		}
	else if (type == GNBBaseNode::kToDoNode)
		{
		if (itsCurrentSelectionType == kNoneSelected)
			{
			th->itsCurrentSelectionType = kToDosSelected;
			}
		else if (itsCurrentSelectionType == kNotesSelected)
			{
			th->itsCurrentSelectionType = kItemsSelected;
			}
		}
	else if (type == GNBBaseNode::kNoteNode)
		{
		if (itsCurrentSelectionType == kNoneSelected)
			{
			th->itsCurrentSelectionType = kNotesSelected;
			}
		else if (itsCurrentSelectionType == kToDosSelected)
			{
			th->itsCurrentSelectionType = kItemsSelected;
			}
		}
	return kTrue;
}

/******************************************************************************
 HandleReturn (protected)

 ******************************************************************************/

void
GNBTreeWidgetBase::HandleReturn()
{
	JTableSelection& s = GetTableSelection();
	
	JPoint cell;
	if (s.GetSingleSelectedCell(&cell) && 
		((itsCurrentSelectionType == kToDosSelected) ||
		 (itsCurrentSelectionType == kCategoriesSelected))) 
		{
		s.ClearSelection();
		BeginEditing(cell);
		JXInputField* input;
		if (GetXInputField(&input))
			{
			input->SetIsRequired(kFalse);
			}
		}
	else if (itsCurrentSelectionType == kNotesSelected)
		{
		JTableSelectionIterator iter(&s);
		while (iter.Next(&cell))
			{
			JTreeNode* jnode 	= GetTreeList()->GetNode(cell.y);
			GNBBaseNode* bnode 	= dynamic_cast(GNBBaseNode*, jnode);
			assert(bnode != NULL);
			GNBBaseNode::Type type = bnode->GetType();
			if (type == GNBBaseNode::kNoteNode)
				{
				GNBNoteNode* node = dynamic_cast(GNBNoteNode*, jnode);
				assert(node != NULL);
				GNBGetApplication()->OpenNote(node->GetID());
				}
			}
		}
}

/******************************************************************************
 PlaceNewNode (private)

 ******************************************************************************/

void
GNBTreeWidgetBase::PlaceNewNode
	(
	JNamedTreeNode* parent,
	GNBBaseNode* 	child,
	JTreeNode* 		jsibling,
	const JBoolean	after,
	const JBoolean	startEditing
	)
{
	if (after)
		{
		parent->InsertAfter(jsibling, child);
		}
	else
		{
		if (GetTreeList()->IsOpen(parent))
			{
			parent->Prepend(child);
			}
		else
			{
			GetTreeList()->Open(parent);
			parent->Append(child);
			}
		}
	JIndex findex;
	if (GetTreeList()->FindNode(child, &findex))
		{
		if (startEditing && IsEditing())
			{
			EndEditing();
			}
		JPoint cell(GetNodeColIndex(), findex);
		GetTableSelection().ClearSelection();
		if (startEditing)
			{
			BeginEditing(cell);
			JXInputField* input;
			if (GetXInputField(&input))
				{
				input->SetIsRequired(kFalse);
				}
			}
		else
			{
			GetTableSelection().SelectCell(cell);
			}
		SelectedFlushed();
		AdjustSelectionType();
		}
}

/******************************************************************************
 ExportSelected (public)

 ******************************************************************************/

void
GNBTreeWidgetBase::ExportSelected()
{
	GNBNoteDir* dir = MergeAppended();
	dir->Export();
	dir->Close();
}

/******************************************************************************
 PrintSelected (public)

 ******************************************************************************/

void
GNBTreeWidgetBase::PrintSelected()
{
	GNBNoteDir* dir = MergeAppended();
	dir->MakeTempForPrinting();
	dir->Print();
}

/******************************************************************************
 MergeAppended (private)

 ******************************************************************************/

GNBNoteDir*
GNBTreeWidgetBase::MergeAppended()
{
	JTableSelection& s 			= GetTableSelection();
	JTableSelectionIterator iter(&s);
	GNBNoteDir* dir				= NULL;
	JPoint cell;
	while (iter.Next(&cell))
		{
		JTreeNode* node			= GetTreeList()->GetNode(cell.y);
		GNBBaseNode* bnode		= dynamic_cast(GNBBaseNode*, node);
		assert(bnode != NULL);
		GNBBaseNode::Type type 	= bnode->GetType();
		if (type == GNBBaseNode::kNoteNode)
			{
			GNBNoteNode* nnode	= dynamic_cast(GNBNoteNode*, node);
			assert(nnode != NULL);
			if (dir == NULL)
				{
				dir = new GNBNoteDir(JXGetApplication(), nnode->GetID());
				assert(dir != NULL);
				}
			else
				{
				dir->Append(nnode->GetID());
				}
			}
		else if (type == GNBBaseNode::kToDoNode)
			{
			GNBToDoNode* tnode	= dynamic_cast(GNBToDoNode*, node);
			assert(tnode != NULL);
			JFAID_t id;
			if (tnode->GetID(&id))
				{
				if (dir == NULL)
					{
					JFAID_t id;
					
					dir = new GNBNoteDir(JXGetApplication(), id);
					assert(dir != NULL);
					}
				else
					{
					dir->Append(id);
					}
				}
			}
		}
	return dir;
}

/******************************************************************************
 MarkSelectedAsDone (virtual protected)

 ******************************************************************************/

void
GNBTreeWidgetBase::MarkSelectedAsDone
	(
	const JBoolean done
	)
{
	JTableSelection& s 			= GetTableSelection();
	JTableSelectionIterator iter(&s);

	JPoint cell;
	if ((s.GetSelectedCellCount() == 0) && GetEditedCell(&cell))
		{
		MarkItemDone(cell, done);
		return;
		}

	while (iter.Next(&cell))
		{
		MarkItemDone(cell, done);
		}
}

/******************************************************************************
 MarkItemDone (private)

 ******************************************************************************/

void
GNBTreeWidgetBase::MarkItemDone
	(
	const JPoint& 	cell,
	const JBoolean 	done
	)
{
	JTreeNode* node			= GetTreeList()->GetNode(cell.y);
	GNBBaseNode* bnode		= dynamic_cast(GNBBaseNode*, node);
	assert(bnode != NULL);
	GNBBaseNode::Type type 	= bnode->GetType();
	if (type == GNBBaseNode::kToDoNode)
		{
		GNBToDoNode* tdnode = dynamic_cast(GNBToDoNode*, node);
		assert(tdnode != NULL);
		if ((!tdnode->IsDone() && done) ||
			(tdnode->IsDone() && !done))
			{
			tdnode->ToggleDone();
			itsTree->BroadcastChange(tdnode);
			}
		}
}
