/* -*- 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.
 */

// RDFLiner Implementation.  Created by Dave Hyatt.

#include "stdafx.h"
#include "mainfrm.h"
#include "hiddenfr.h"
#include "xp_ncent.h"
#include "rdf.h"
#include "htrdf.h"
#include "rdfliner.h"
#include "pain.h"
#include "shcut.h"
#include "shcutdlg.h"
#include "prefapi.h"
#include "netsvw.h"
#include "cxsave.h"
#include "fegui.h"
#include "nethelp.h"
#include "time.h"

CMapStringToPtr CHTFEData::m_LocalFileCache(40);
CMapStringToPtr CHTFEData::m_CustomURLCache(20);

extern "C" {
#include "xpgetstr.h"
extern int XP_BKMKS_LESS_THAN_ONE_HOUR_AGO;
extern int XP_BKMKS_DAYS_AGO;
extern int XP_BKMKS_HOURS_AGO;
};

// Some tooltip stuff
#define TIP_WAITING	1
#define TIP_SHOWING	2
#define TIP_SHOWN	3
#define TIP_HEARTBEAT	100
#define TIP_DELAY		250

// Focus/timer stuff for edit wnds
#define IDT_EDITFOCUS 16412
#define IDT_SPRINGLOAD 16413
#define IDT_DRAGRECT 16414

#define EDIT_DELAY 500
#define SPRINGLOAD_DELAY 500
#define RDF_DRAG_HEARTBEAT 125 // Faster than COutliner's DRAG_HEARTBEAT

// Click location stuff
#define CLICKED_BACKGROUND 0
#define CLICKED_TRIGGER 1
#define CLICKED_LINE 2
#define CLICKED_BAR 3

// Used in painting
#define COL_LEFT_MARGIN	((m_cxChar+1)/2)
#define OUTLINE_TEXT_OFFSET 8

#define HTFE_USE_CUSTOM_IMAGE -1
#define HTFE_LOCAL_FILE -2

#define HT_NO_SORT -1
#define HT_SORT_ASCENDING 1
#define HT_SORT_DESCENDING 0

// The Nav Center vocab element
extern "C" RDF_NCVocab gNavCenter;

// Function to make a pretty date string
#define SECONDS_PER_DAY		86400L
void FormatDateTime( CTime &tmDate, CString &csFormatDate )
{
    //
	// Get locale info
    //
	static char cName[] = "intl" ;
	TCHAR szSepDate[2], szSepTime[2];    
	GetProfileString( cName, "sDate", "/", szSepDate, sizeof(szSepDate) );
	GetProfileString( cName, "sTime", ":", szSepTime, sizeof(szSepTime) );    

    TCHAR szAM[9], szPM[9];
    GetProfileString( cName, "s1159", "AM", szAM, sizeof(szAM) );
    GetProfileString( cName, "s2359", "PM", szPM, sizeof(szPM) );    

	int iDate = GetProfileInt( cName, "iDate", 0 );
    int iTime = GetProfileInt( cName, "iTime", 0 );
  
    TCHAR szBuffer[64];
    switch( iDate )
    {
        default:
        case 0: // Month-Day-Year 
        {
            wsprintf( szBuffer, "%d%s%d%s%d", tmDate.GetMonth(), szSepDate, tmDate.GetDay(), szSepDate, tmDate.GetYear() );
            break;
        }
        
        case 1: // Day-Month-Year 
        {
            wsprintf( szBuffer, "%d%s%d%s%d", tmDate.GetDay(), szSepDate, tmDate.GetMonth(), szSepDate, tmDate.GetYear() );
            break;
        }

        case 2: // Year-Month-Day     
        {
            wsprintf( szBuffer, "%d%s%d%s%d", tmDate.GetYear(), szSepDate, tmDate.GetMonth(), szSepDate, tmDate.GetDay() );
            break;
        }
    }
    
    _tcscat( szBuffer, " " );
    
    TCHAR *pszTime = &szBuffer[_tcslen( szBuffer )];
    
    switch( iTime )
    {
        default:
        case 0: // AM/PM 12-hour
        {
            BOOL bPM = FALSE;
            int iHour = tmDate.GetHour();
            if( iHour > 11 )
            {
                bPM = TRUE;
                iHour -= 12;
            }
            
            iHour = iHour ? iHour : 12;
            
            wsprintf( pszTime, "%d%s%.2d %s", iHour, szSepTime, tmDate.GetMinute(), (TCHAR *)(bPM ? szPM : szAM) );
            break;
        }
        
        case 1: // 24-hour
        {
            wsprintf( pszTime, "%d%s%.2d", tmDate.GetHour(), szSepTime, tmDate.GetMinute() );
            break;
        }
    }
    
    csFormatDate = (const char *)szBuffer;
}


void HTFE_MakePrettyDate(char* buffer, time_t lastVisited)
{
  buffer[0] = 0;
  time_t today = XP_TIME();
  int elapsed = today - lastVisited;

  if (elapsed < SECONDS_PER_DAY) 
  {
    int32 hours = (elapsed + 1800L) / 3600L;
    if (hours < 1) 
	{
	  strcpy(buffer, XP_GetString(XP_BKMKS_LESS_THAN_ONE_HOUR_AGO));
	}
    sprintf(buffer, XP_GetString(XP_BKMKS_HOURS_AGO), hours);
  } 
  else if (elapsed < (SECONDS_PER_DAY * 31)) 
  {
	  sprintf(buffer, XP_GetString(XP_BKMKS_DAYS_AGO),
			  (elapsed + (SECONDS_PER_DAY / 2)) / SECONDS_PER_DAY);
  } 
  else 
  {
      CString csFormatDate;
      CTime ctDate( lastVisited );
      FormatDateTime( ctDate, csFormatDate );
      _tcscpy( buffer, (const char *)csFormatDate );
  }
}

BEGIN_MESSAGE_MAP(CRDFOutliner, COutliner)
	//{{AFX_MSG_MAP(CMainFrame)
		// NOTE - the ClassWizard will add and remove mapping macros here.
		//    DO NOT EDIT what you see in these blocks of generated code !
	ON_WM_PAINT()
	ON_WM_SETFOCUS()
	ON_WM_KILLFOCUS()
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONUP()
	ON_WM_MOUSEMOVE()
	ON_WM_RBUTTONDOWN()
	ON_WM_RBUTTONUP()
	ON_WM_CREATE()
	ON_WM_TIMER()
	ON_WM_SIZE()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

CRDFOutliner::CRDFOutliner (CRDFOutlinerParent* theParent, HT_Pane thePane, HT_View theView)
:COutliner(FALSE), m_pAncestor(NULL), m_Pane(thePane), m_View(theView), m_Parent(theParent), m_EditField(NULL),
m_nSortType(HT_NO_SORT), m_nSortColumn(HT_NO_SORT), m_hEditTimer(0), m_hDragRectTimer(0),
m_bNeedToClear(FALSE), m_nSelectedColumn(0), m_bDoubleClick(FALSE), m_Node(NULL), 
m_bDataSourceInWindow(FALSE), m_NavTitleBar(NULL), m_bIsPopup(FALSE)
{
    ApiApiPtr(api);
    m_pUnkUserImage = api->CreateClassInstance(APICLASS_IMAGEMAP,NULL,(APISIGNATURE)IDB_BOOKMARKS);
    if (m_pUnkUserImage) {
        m_pUnkUserImage->QueryInterface(IID_IImageMap,(LPVOID*)&m_pIUserImage);
        ASSERT(m_pIUserImage);
		if (!m_pIUserImage->GetResourceID())
			m_pIUserImage->Initialize(IDB_BOOKMARKS,16,16);
    }
}

CRDFOutliner::~CRDFOutliner ()
{
    if (m_pUnkUserImage) {
        if (m_pIUserImage)
            m_pUnkUserImage->Release();
    }

	delete []m_pAncestor;
}

int CRDFOutliner::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    int iRetVal = COutliner::OnCreate(lpCreateStruct);
	DragAcceptFiles(FALSE);
    // RDF widget only take UTF8 
    SetCSID(CS_UTF8);	
    return iRetVal;
}

void CRDFOutliner::OnSize( UINT nType, int cx, int cy )
{
    SqueezeColumns( -1, 0, FALSE );
    m_iPaintLines = ( cy / m_itemHeight ) + 1;
    EnableScrollBars ( );
}

void CRDFOutliner::HandleEvent(HT_Notification ns, HT_Resource n, HT_Event whatHappened) 
{
	if (whatHappened == HT_EVENT_VIEW_MODECHANGED)
	{
		ToggleModes();
	}
	else if (whatHappened == HT_EVENT_NODE_OPENCLOSE_CHANGED)
	{
		FinishExpansion(HT_GetNodeIndex(m_View, n));
	}
	else if (whatHappened == HT_EVENT_NODE_OPENCLOSE_CHANGING)
	{
		PRBool openState;
		HT_GetOpenState(n, &openState);
		if (openState)
		{
			// Node is closing.  We need to iterate over our list of loading images and
			// if the node(s) that are observing the resource are children of this node,
			// we need to remove them from the observation list of the image.
			for (POSITION pos = loadingImagesList.GetHeadPosition(); pos != NULL; )
			{
				// Enumerate over the list of loading images.
				CRDFImage* pImage = (CRDFImage*)loadingImagesList.GetNext(pos);
				
				// Figure out which of our resources are listening to this image.
				for (POSITION pos2 = pImage->resourceList.GetHeadPosition();
						pos2 != NULL; )
				{
					// Enumerate over the list and call remove listener on each image.
					CIconCallbackInfo* pInfo = (CIconCallbackInfo*)(pImage->resourceList.GetNext(pos2));
					if (pInfo->pObject == this)
					{
						// We are currently observing this image.
						// Find out if the resource that is observing the image is a direct
						// descendant of the parent node that is closing.
						
						HT_Resource child;
						for (child = pInfo->pResource;
							 child != NULL && child != n;
							 child = HT_GetParent(child));
						if (child == n)
						{
							// Do a removal.
							if (pos2 != NULL) // Not at the end
							{
								pImage->resourceList.GetPrev(pos2);
								if (pos2 == NULL)
									pos2 = pImage->resourceList.GetHeadPosition();
							}
							pImage->RemoveListenerForSpecificResource(this, pInfo->pResource);
						}
					}
				}
			}
		}
	}
	else if (whatHappened == HT_EVENT_NODE_SELECTION_CHANGED)
	{
		int i = HT_GetNodeIndex(m_View, n);
		InvalidateLine(i);
	}
	else if (whatHappened == HT_EVENT_NODE_ADDED)
	{
		HT_Resource par = HT_GetParent(n);
		int toggle = -1;

		if (par != NULL)
			toggle = HT_GetNodeIndex(m_View, par);

		FinishExpansion(toggle);
	}
	else if (whatHappened == HT_EVENT_VIEW_SORTING_CHANGED)
	{
		if (m_Node)
		{		
			int index =	HT_GetNodeIndex(m_View, m_Node);
			m_iFocus = index;
		}
		Invalidate();
	}
	else if (whatHappened == HT_EVENT_NODE_EDIT)
	{
		m_Node = n;
		m_iSelection = m_iFocus = HT_GetNodeIndex(m_View, n);
		m_nSelectedColumn = GetColumnAtPos(0);

		AddTextEdit();

	}
	else if (whatHappened == HT_EVENT_NODE_DELETED_DATA ||
			 whatHappened == HT_EVENT_NODE_DELETED_NODATA)
	{
		m_iFocus = -1;
	}
	else if (whatHappened == HT_EVENT_VIEW_REFRESH)
	{
		SetTotalLines(HT_GetItemListCount(m_View));
		Invalidate();
		UpdateWindow();
	}
	else if (whatHappened == HT_EVENT_NODE_VPROP_CHANGED)
	{
		// Invalidate a single line
		if (n)
		{		
			int index =	HT_GetNodeIndex(m_View, n);
			InvalidateLine(index);
		}
	}
}

void CRDFOutliner::InvalidateIconForResource(HT_Resource r)
{
	int iLineNo = HT_GetNodeIndex(m_View, r);

	CRect rect, out;
    GetClientRect ( &rect );
    RectFromLine ( iLineNo - m_iTopLine, rect, out );
	
	int iImageWidth = GetIndentationWidth();
	int indent = HT_GetItemIndentation(r);
	out.left = indent*iImageWidth;
	out.right = out.left + iImageWidth;

    InvalidateRect ( &out );

}

void CRDFOutliner::InvalidateDragLine ( int iLineNo, BOOL useThirds, int dragFraction)
{
	if (iLineNo == -1)
		return;
    CRect rect, out;
    GetClientRect ( &rect );
    RectFromLine ( iLineNo - m_iTopLine, rect, out );

	if (dragFraction == 1)
	{
		CRect topLine(out);
		topLine.bottom = out.top;
		InvalidateRect(&topLine);
	}
	
	if (dragFraction == 2)
	{
		if (useThirds)
			InvalidateRect(out);
		else dragFraction = 3;
	}
	
	if (dragFraction == 3)
	{
		CRect bottomLine(out);
		bottomLine.top = out.bottom-1;
		InvalidateRect ( &bottomLine );
	}
}

LPCTSTR CRDFOutliner::GetColumnText ( UINT iColumn, void * pLineData )
{
	void* nodeData;
	time_t dateVal;
	struct tm* time;
	
	HT_Resource pEntry = (HT_Resource)pLineData;
	CRDFCommandMap& map = m_Parent->GetColumnCommandMap(); 
	CRDFColumn* theColumn = (CRDFColumn*)(map.GetCommand(iColumn));
		
	if (pEntry && HT_GetNodeData(pEntry, theColumn->GetToken(), theColumn->GetDataType(), &nodeData)
		&& nodeData)
	{
		CString& buffer = theColumn->GetStorageBuffer();
		char buf2[500]; // More than big enough for dates and ints
		
		switch (theColumn->GetDataType())
		{
			case HT_COLUMN_DATE_STRING:
				if ((dateVal = (time_t)atol((char *)nodeData)) == 0)	break;
				if ((time = localtime(&dateVal)) == NULL)	break;
				HTFE_MakePrettyDate(buf2, dateVal);
				buffer = buf2;
				break;
			case HT_COLUMN_DATE_INT:
				if ((time = localtime((time_t *) &nodeData)) == NULL)	break;
				HTFE_MakePrettyDate(buf2, (time_t)nodeData);
				buffer = buf2;
				break;
			case HT_COLUMN_INT:
				sprintf(buf2,"%d",(int)nodeData);
				buffer = buf2;
				break;
			case HT_COLUMN_STRING:
				buffer = (char*)nodeData;
				break;
		}

		return buffer;
	}
    return NULL;
}

void * CRDFOutliner::AcquireLineData ( int iLine )
{ 
	// Grab this resource
	if (GetTotalLines() <= iLine)
		return NULL;

	HT_Resource r = HT_GetNthItem(m_View, iLine);

	if (r != NULL)
	{
		int indent = HT_GetItemIndentation(r);
		BOOL hasPrev = HT_ItemHasBackwardSibling(r);
		BOOL hasNext = HT_ItemHasForwardSibling(r);

		if (m_bPaintingFirstObject || indent > m_nSizeOfAncestorArray)
		{
			OutlinerAncestorInfo* oldInfo = m_pAncestor;
			
			if (m_bPaintingFirstObject)
			{
				m_nSizeOfAncestorArray = indent;
				delete []oldInfo;
				oldInfo = NULL;
			}

			m_pAncestor = new OutlinerAncestorInfo[indent];
			
			// Fill in the ancestor values.
			copyAncestorValues(r, m_nSizeOfAncestorArray, oldInfo, m_pAncestor);
			m_nSizeOfAncestorArray = indent;

			delete []oldInfo;
		}
		
		
		m_pAncestor[indent-1].has_next = hasNext;
		m_pAncestor[indent-1].has_prev = hasPrev;
	}		

	return r;
}

void CRDFOutliner::ReleaseLineData ( void * pLineData )
{
}

void CRDFOutliner::GetTreeInfo( int iLine, uint32 * pFlags, int * iDepth, 
									 OutlinerAncestorInfo ** pAncestor )
{
	HT_Resource r = HT_GetNthItem(m_View, iLine);
	if (r == NULL)
		return;

	if (pFlags)
        *pFlags = (uint32)HT_IsContainer(r);
    if (iDepth)
        *iDepth = HT_GetItemIndentation(r)-1;
    
	if (pAncestor)
		*pAncestor = m_pAncestor;
}

void CRDFOutliner::copyAncestorValues(HT_Resource r, int numItems, OutlinerAncestorInfo* oldInfo, OutlinerAncestorInfo* newInfo)
{
	if (oldInfo != NULL)
	{
		for (int i = 0; i < numItems; i++)
		{
			newInfo[i].has_next = oldInfo[i].has_next;
			newInfo[i].has_prev = oldInfo[i].has_prev;
		}
	}
	else
	{
		// Have to acquire ALL the data.
		HT_Resource currNode = r;
		for (int i = numItems-1; i >= 0; i--)
		{
			newInfo[i].has_next = HT_ItemHasForwardSibling(currNode);
			newInfo[i].has_prev = HT_ItemHasBackwardSibling(currNode);
			currNode = HT_GetParent(currNode);
		}
	}
}

BOOL CRDFOutliner::IsSelected(int iLine)
{
	HT_Resource r = HT_GetNthItem(m_View, iLine);
	return (r != NULL) && (HT_IsSelected(r));
}

HFONT CRDFOutliner::GetLineFont ( void * pLineData )
{
	// Will need to hack this for aliases/shortcuts? (italics!)
	/*HT_Resource r = (HT_Resource)pLineData;
	char* url = HT_GetNodeURL(r);

	if (url && !IsLocalFile(url))
		return m_hItalFont;
	*/

	return m_hRegFont;
}

int CRDFOutliner::TranslateIcon ( void * pData )
{
   HT_Resource data = (HT_Resource)pData;
   char* pURL = HT_GetNodeSmallIconURL(data);

   //TRACE("%s\n", pURL);
	
   if (strncmp("icon", pURL, 4) == 0)
   {
	   // Icon URL
		if ((strcmp("icon/small:folder/closed", pURL) == 0) ||
			(strcmp("icon/small:folder,history/closed", pURL) == 0))
			return IDX_BOOKMARKCLOSED;

		if ((strcmp("icon/small:folder/open", pURL) == 0) ||
			(strcmp("icon/small:folder,history/open", pURL) == 0))
			return IDX_BOOKMARKOPEN;

		if (strcmp("icon/small:url", pURL) == 0)
			return IDX_BOOKMARK;

		if ((strcmp("icon/small:file/local", pURL) == 0) ||
		    (strcmp("icon/small:folder/local,closed", pURL) == 0) ||
			(strcmp("icon/small:folder/local,open", pURL) == 0))
			return HTFE_LOCAL_FILE;

		// Catchall code for unknown ICON urls
		if (HT_IsContainer(data))
		{
			if (HT_IsContainerOpen(data))
				return IDX_BOOKMARKOPEN;
			else return IDX_BOOKMARKCLOSED;
		}

		return IDX_BOOKMARK;
   }

   // Handle the arbitrary URL case
   return HTFE_USE_CUSTOM_IMAGE;   
}

int CRDFOutliner::TranslateIconFolder( void *pData )
{
   HT_Resource data = (HT_Resource)pData;

   if (data) 
   {
	   if (HT_IsContainer(data))
	   {
		   if (HT_IsContainerOpen(data))
		   {
			   return OUTLINER_OPENFOLDER;
		   }
		   else
		   {
			   return OUTLINER_CLOSEDFOLDER;
		   }
	   }
   }
    
   return OUTLINER_ITEM;
}

void CRDFOutliner::LoadComplete(HT_Resource pResource)
{
	if (pResource == NULL)	// Background image loaded.
		Invalidate();
	else InvalidateIconForResource(pResource);  // Individual line had an image load.
}

// Functions used to draw custom images (either local file system icons or arbitrary image URLs)
HICON DrawLocalFileIcon(HT_Resource r, int left, int top, HDC hDC) 
{
	char* pLocalName = NULL;
	HICON hIcon = FetchLocalFileIcon(r);
	
	if (hIcon)
	{
		// Now we draw this bad boy.
		DrawIconEx(hDC, left, top, hIcon, 0, 0, 0, NULL, DI_NORMAL);	
	}

	return hIcon;
}

CRDFImage* DrawArbitraryURL(HT_Resource r, int left, int top, int imageWidth, int imageHeight, HDC hDC, COLORREF bkColor, 
					  CCustomImageObject* pObject, BOOL largeIcon)
{
	CRDFImage* pImage = FetchCustomIcon(r, pObject, largeIcon);
	return DrawRDFImage(pImage, left, top, imageWidth, imageHeight, hDC, bkColor);
}

CRDFImage* DrawRDFImage(CRDFImage* pImage, int left, int top, int imageWidth, int imageHeight, HDC hDC,
						COLORREF bkColor)
{
	if (pImage && pImage->FrameLoaded()) 
	{
		// Now we draw this bad boy.
		if (pImage->m_BadImage) 
		{ 
			// display broken icon.
			HDC tempDC = ::CreateCompatibleDC(hDC);
			HBITMAP hOldBmp = (HBITMAP)::SelectObject(tempDC,  CRDFImage::m_hBadImageBitmap);
			::StretchBlt(hDC, 
					 left, top,
					 imageWidth, imageHeight, 
					 tempDC, 0, 0, 
					 imageWidth, imageHeight, SRCCOPY);
			::SelectObject(tempDC, hOldBmp);
			::DeleteDC(tempDC);
		}
		else if (pImage->bits ) 
		{
			// Center the image. 
			long width = pImage->bmpInfo->bmiHeader.biWidth;
			long height = pImage->bmpInfo->bmiHeader.biHeight;
			int xoffset = (imageWidth-width)/2;
			int yoffset = (imageHeight-height)/2;
			if (xoffset < 0) xoffset = 0;
			if (yoffset < 0) yoffset = 0;
			if (width > imageWidth) width = imageWidth;
			if (height > imageHeight) height = imageHeight;

			HPALETTE hPal = WFE_GetUIPalette(NULL);
			HPALETTE hOldPal = ::SelectPalette(hDC, hPal, TRUE);

			::RealizePalette(hDC);
			
			if (pImage->maskbits) 
			{
				WFE_StretchDIBitsWithMask(hDC, TRUE, NULL,
					left+xoffset, top+xoffset,
					width, height,
					0, 0, width, height,
					pImage->bits, pImage->bmpInfo,
					pImage->maskbits, FALSE, bkColor);
			}
			else 
			{
				::StretchDIBits(hDC,
					left+xoffset, top+xoffset,
					width, height,
					0, 0, width, height, pImage->bits, pImage->bmpInfo, DIB_RGB_COLORS,
					SRCCOPY);
			}

		::SelectPalette(hDC, hOldPal, TRUE);
		}
	}

	return pImage;
}

BOOL IsLocalFile(const char* pURL)
{
	return strncmp(pURL, "file:", 5) == 0;
}

BOOL IsExecutableURL(const char* pURL)
{
	char* pLocalName = NULL;
	if (!XP_ConvertUrlToLocalFile(pURL, &pLocalName))
		return FALSE;
	
	pLocalName = NET_UnEscape(pLocalName);

	// Do a FindExecutable.  If the filenames match, we win.
	char execName[_MAX_PATH];
	if (FEU_FindExecutable(pLocalName, execName, FALSE))
	{
		// Well, there's at least SOMETHING.
		// See if it matches us.
		char answerString[_MAX_PATH];
		strcpy(answerString, pLocalName);
		char execString[_MAX_PATH];
		strcpy(execString, execName);
		
#ifdef _WIN32
		GetShortPathName(pLocalName, answerString, _MAX_PATH);
		GetShortPathName(execName, execString, _MAX_PATH);
#endif // _WIN32

		if (!stricmp(answerString, execString))
		  return TRUE;
	}

	if (pLocalName)
		XP_FREE(pLocalName);

	return FALSE;
}

// The expansion handler
int CRDFOutliner::ToggleExpansion (int iLine)
{
	PRBool openp;
	HT_Resource pEntry = HT_GetNthItem(m_View, iLine);
	if (pEntry && HT_IsContainer(pEntry))
	{
		HT_GetOpenState(pEntry, &openp);
		HT_SetOpenState(pEntry, (PRBool)(!openp));
	}
	return 0;
}

HICON FetchLocalFileIcon(HT_Resource r)
{
	HICON hIcon = NULL;
	CString hashString(HT_GetNodeURL(r));
		
	if (hashString.GetLength() < 12) // Hack.  It's a disk volume.
	{
	    // Hash disk drives based on URL.
	}
	else if (HT_IsContainer(r))
	{
		// We're a directory.
		if (HT_IsContainerOpen(r))
			hashString = "open folder";
		else hashString = "closed folder";
	}
	else
	{
		// We're a file.
		int index = hashString.ReverseFind('.');
		if (index == -1)
			hashString = "no extension";
		else hashString = hashString.Right(hashString.GetLength() - index);
	}
		
	void* hashPtr;
	char* pLocalName = NULL;
	if (CHTFEData::m_LocalFileCache.Lookup(hashString, hashPtr))
	{
		// We found an icon in the icon cache.
		hIcon = (HICON)hashPtr;
	}
	else 
	{
		XP_ConvertUrlToLocalFile(HT_GetNodeURL(r), &pLocalName);
		pLocalName = NET_UnEscape(pLocalName);

		SHFILEINFO shInfo;
		shInfo.hIcon = NULL;

		if (HT_IsContainer(r) && HT_IsContainerOpen(r))
			SHGetFileInfo(pLocalName, 0, &shInfo, sizeof(shInfo), SHGFI_OPENICON | SHGFI_ICON | SHGFI_SMALLICON);
		else SHGetFileInfo(pLocalName, 0, &shInfo, sizeof(shInfo), SHGFI_ICON | SHGFI_SMALLICON);
		
		if(shInfo.hIcon)
		{
			hIcon = shInfo.hIcon;

			// Add to the local file cache.
			CHTFEData::m_LocalFileCache.SetAt(hashString, (void*)hIcon);
		}

		if (pLocalName)
			XP_FREE(pLocalName);
	}

	return hIcon;
}

CRDFImage* FetchCustomIcon(HT_Resource r, CCustomImageObject* imageObject, BOOL largeIcon)
{
	CRDFImage* pImage = NULL;
	CString hashString;
	if (largeIcon)
		hashString = HT_GetNodeLargeIconURL(r);
	else hashString = HT_GetNodeSmallIconURL(r);
	
	if (strncmp("icon/large:workspace", hashString, 20) == 0)
	{
		char name[8];
		int ret = sscanf(hashString, "icon/large:workspace,%s", name);
		if (ret != 0) 
			hashString = CString("about:") + name + ".gif";
	}
	else if (strncmp("icon/large:folder", hashString, 17) == 0)
	{
		hashString = "about:personal.gif";
	}

	pImage = imageObject->LookupImage(hashString, r);
	return pImage;
}


IconType DetermineIconType(HT_Resource pNode, BOOL largeIcon)
{ 
   IconType returnValue;
   if (pNode != NULL)
   {
	    char* pURL = largeIcon? HT_GetNodeLargeIconURL(pNode) : HT_GetNodeSmallIconURL(pNode);
		//TRACE("%s\n", pURL);
		if (strncmp("icon", pURL, 4) != 0)
		{
			// Time to load a custom image.
			returnValue = ARBITRARY_URL;
		}
		else if ((strncmp("icon/large:workspace", pURL, 20) == 0) ||
				 (strncmp("icon/large:folder", pURL, 17) == 0))
		{
			returnValue = ARBITRARY_URL;
		}
		else if (IsLocalFile(HT_GetNodeURL(pNode)))
		{
			returnValue = LOCAL_FILE;
		}
		else returnValue = BUILTIN_BITMAP;
   }
   else returnValue = BUILTIN_BITMAP;

   return returnValue;
}

// The callback for finishing the expansion
void CRDFOutliner::FinishExpansion(int toggleIndex)
{
	int iCount = GetTotalLines();
	int newCount = HT_GetItemListCount(m_View);
	SetTotalLines(newCount);  // Update the outliner's visible lines
	
	int iDelta = newCount - iCount;
	int iSel = GetFocusLine();
	if (iSel > toggleIndex) {
		if (iDelta > 0) {
			iSel += iDelta;
		} else {
			if (iSel < (toggleIndex - iDelta)) {
				iSel = toggleIndex;
			} else {
				iSel += iDelta;
			}
		}
		if (iSel != GetFocusLine()) 
		{
			m_iFocus = m_iSelection = iSel;
			InvalidateLine(m_iFocus);
		}
	}
	
	if ( iDelta > 0 ) 
	{
		iDelta = iDelta > m_iPaintLines - 2 ? m_iPaintLines - 2 : iDelta;
		
		if (!m_bDataSourceInWindow) // Springloading folders go haywire if we shift lines around, so don't do it
			EnsureVisible( toggleIndex + iDelta );
	}

	Invalidate();
}

void CRDFOutliner::SelectItem(int iSel,int mode,UINT flags)
{
	int oldColumn = GetSelectedColumn();
	
	if (m_iColHit == -1)
		return;

	UINT command = m_pColumn[ m_iColHit ]->iCommand;

	RemoveTextEdit();

	m_Node = HT_GetNthItem(m_View, iSel);

    if (!m_Node)
	{
		HT_SetSelectionAll(m_View, (PRBool)FALSE);
		
		if (mode != OUTLINER_RBUTTONDOWN)
			return;

		m_Node = HT_TopNode(m_View);
		iSel = -1;
		
	}

    switch (mode) 
	{
		case OUTLINER_RETURN:
		case OUTLINER_LBUTTONDBLCLK:
			OnSelDblClk(iSel);
			m_bDoubleClick = TRUE;
			if (m_hEditTimer)
			{
				KillTimer(m_hEditTimer);
				m_hEditTimer = 0;
			}
			break;
		case OUTLINER_LBUTTONUP:
			if (m_bNeedToClear)
			{
				HT_SetSelection(m_Node);
				if (m_bNeedToEdit && !m_bDoubleClick && m_bUseInlineEditing)
				{

					CRDFCommandMap& map = m_Parent->GetColumnCommandMap(); 
					CRDFColumn* theColumn = (CRDFColumn*)(map.GetCommand(m_nSelectedColumn));
		
					BOOL isEditable = HT_IsNodeDataEditable(m_Node, theColumn->GetToken(), 
															theColumn->GetDataType());

					// Get down.... get down... time for a column edit.
					if (isEditable)
						m_hEditTimer = SetTimer(IDT_EDITFOCUS, EDIT_DELAY, NULL);
				}
			}
			m_bNeedToClear = FALSE;
			m_bNeedToEdit = FALSE;
			m_bDoubleClick = FALSE;
			if (m_bUseSingleClick)
				OnSelDblClk(iSel);
			break;	
		case OUTLINER_LBUTTONDOWN:
		case OUTLINER_KEYDOWN:
		case OUTLINER_SET:
 			m_iFocus = iSel;
			m_bNeedToClear = FALSE;
			m_bNeedToEdit = FALSE;
			SetSelectedColumn((int)command);
			
			if (flags & MK_CONTROL) 
			{
				// Causes a selection toggle.
				HT_ToggleSelection(m_Node);	
			}
			else if (flags & MK_SHIFT) 
			{
				// A range of items is being selected.
				HT_Resource node = HT_GetNthItem(m_View, m_iSelection);
				if (node != NULL)
				{
					HT_SetSelectionRange(node, m_Node);
				}
			}
			else 
			{
				m_iSelection = iSel;

				if (!IsSelected(m_iFocus))
				{
					HT_SetSelection(m_Node);
					
					//if (!HT_IsContainer(m_Node) && !HT_IsSeparator(m_Node) && IsDocked())
					//	DisplayURL();  For now do double-click behavior
				}
				else 
				{
					m_bNeedToClear = TRUE;
					if (oldColumn == m_nSelectedColumn)
						m_bNeedToEdit = TRUE;
				}
			}	
			break;

		case OUTLINER_RBUTTONDOWN:
 			m_iFocus = iSel;
			SetSelectedColumn((int)command);	
			m_iSelection = iSel;

			if (m_iFocus >= 0 && !IsSelected(m_iFocus))
			{
				HT_SetSelection(m_Node);
			}	
			break;
		default:
			break;
	}
}

BOOL CRDFOutliner::DeleteItem(int iSel)
{
	HT_DoMenuCmd(m_Pane, HT_CMD_CUT);
	return TRUE;
}

BOOL CRDFOutliner::IsDocked()
{
	CGenericFrame* theFrame = (CGenericFrame*)FEU_GetLastActiveFrame();
	
	if (theFrame == NULL)
	  return FALSE;

	return theFrame->IsChild(this);
}

void CRDFOutliner::ToggleModes()
{
	void* data;

	// Whether or not to show column headers
	HT_GetTemplateData(HT_TopNode(m_View), gNavCenter->showColumnHeaders, HT_COLUMN_STRING, &data);
	BOOL show = TRUE;
	if (data)
	{
		char* answer = (char*)data;
		show = stricmp(answer, "No");
	}

	((CRDFOutlinerParent*)GetParent())->EnableHeaders(show);

	// Need to invalidate the title strip.
	CRDFContentView* pView = (CRDFContentView*)(GetParent()->GetParent());
	pView->GetTitleBar()->Invalidate();

	Invalidate();
}

void CRDFOutliner::OnSelDblClk(int iLine)
{
	if (m_Node)
	{
		if (HT_IsContainer(m_Node))
		{
			ToggleExpansion(iLine);
		}
		else if (!HT_IsSeparator(m_Node))// && !IsDocked()) For now always do double-click behavior
		{
			DisplayURL();
			if (IsPopup())
			{	
				// Destroy the entire tree.
				CFrameWnd* pFrameWnd = GetParentFrame();
				if (pFrameWnd->IsKindOf(RUNTIME_CLASS(CNSNavFrame)))
					((CNSNavFrame*)pFrameWnd)->DeleteNavCenter();
			}
		}
	}
}

void CRDFOutliner::SetHTView(HT_View v)
{ 
	m_Pane = HT_GetPane(v); 
	m_View = v; 
	m_iSelection = m_iFocus = -1;
	int newCount = HT_GetItemListCount(m_View);
	SetTotalLines(newCount);  // Update the outliner's visible lines
	m_iTopLine = 0;
}

void CRDFOutliner::DisplayURL()
{
	char* url = HT_GetNodeURL(m_Node);
	
	// Hacking the window target (Added by Dave on 7/12/98)
	CAbstractCX * pCX = FEU_GetLastActiveFrameContext();

    //Check for whether we need new browser window.
	if (pCX == NULL)
    {
        // Fake "open in new window".
        HT_DoMenuCmd(m_Pane, HT_CMD_OPEN_NEW_WIN);
		return;
    }

	// Let HT handle some URLs.
	if (HT_Launch(m_Node, pCX->GetContext()))
		return;

	// Shell execute all local file URLs.
	if (IsLocalFile(url))
	{
		char* pLocalName = NULL;
		XP_ConvertUrlToLocalFile(url, &pLocalName);
		pLocalName = NET_UnEscape(pLocalName);

		SHELLEXECUTEINFO    sei;
		  
		// Use ShellExecuteEx to launch
		sei.cbSize = sizeof(sei);
		sei.fMask = SEE_MASK_FLAG_NO_UI | SEE_MASK_NOCLOSEPROCESS;
		sei.hwnd = NULL;
		sei.lpVerb = NULL;  // default to Open
		sei.lpFile = pLocalName;
		sei.lpParameters = NULL;
		sei.lpDirectory = NULL;
		sei.nShow = SW_SHOW;
		ShellExecuteEx(&sei);
		int uSpawn = (UINT)sei.hInstApp;
		if(uSpawn <= 32)	
		{
			char szMsg[80];
			switch(uSpawn) 
			{
				case 0:
				case 8:
					sprintf(szMsg, szLoadString(IDS_WINEXEC_0_8));
					break;
				case 2:                                      
				case 3:
					sprintf(szMsg, szLoadString(IDS_WINEXEC_2_3));
					break;
				case 10:
				case 11:
				case 12:
				case 13:
				case 14:
				case 15:
					sprintf(szMsg, szLoadString(IDS_WINEXEC_10_THRU_15));
					break;
				case 16:
					sprintf(szMsg, szLoadString(IDS_WINEXEC_16));
					break;
				case 21:
					sprintf(szMsg, szLoadString(IDS_WINEXEC_21));
					break;
				default:
					sprintf(szMsg, szLoadString(IDS_WINEXEC_XX), uSpawn);
					break;
			}        
			
			CString s;
			if (s.LoadString( IDS_BOOKMARK_ADDRESSPROPERTIES ))
			{
				::MessageBox(GetParentFrame()->m_hWnd, szMsg, s, MB_OK | MB_APPLMODAL);
			}
		}
		
		if (pLocalName)
			XP_FREE(pLocalName);
	}
	else if (m_WindowTarget == "")
		pCX->NormalGetUrl((LPTSTR) url, "rdftree"); // Do a normal fetch.
	else pCX->NormalGetUrl((LPTSTR) url, "rdftree", (char*)(const char*)m_WindowTarget);
}

BOOL CRDFOutliner::TestRowCol(POINT point, int &iRow, int &iCol)
{
	RECT rcClient;
	GetClientRect(&rcClient);
	BOOL answer = TRUE;

	if (::PtInRect(&rcClient, point)) 
	{
		void *pLineData = NULL;
		int iSel = point.y  / m_itemHeight;
    	int iNewSel = m_iTopLine + iSel;
	    if ( ( pLineData = AcquireLineData ( iNewSel ) ) != NULL ) 
		{
			ReleaseLineData( pLineData );
			iRow = iNewSel;
		}
		else answer = FALSE;

		int i, offset;
		int y = iSel * m_itemHeight;

	    for ( i = 0, offset = 0; i < m_iVisColumns; i++ )
	    {
	       CRect rect ( offset, y,
						offset + m_pColumn[ i ]->iCol, y + m_itemHeight );

		   if ( rect.PtInRect(point) ) 
		   {
				iCol = i;
				m_rcHit = rect;
				return answer;
		   }
	        	
		   offset += m_pColumn[ i ]->iCol;
	    }
	}
	return FALSE;
}

int CRDFOutliner::DetermineClickLocation(CPoint point)
{
	// TestRowCol returns FALSE if the row clicked on contains no data. We must have clicked on the background
	// in this case.
	int iRow, iCol;

	if ( !TestRowCol( point, iRow, iCol ) )
		return CLICKED_BACKGROUND;

	// We clicked on a row/column with data.  Update our member variables to reflect our hit.
	m_iRowHit = iRow;
	m_iColHit = iCol;

	// Get the text rectangle corresponding to the hit column.
	CRect textRect = GetColumnRect(iRow, (int)(m_pColumn[iCol]->iCommand));

	// Double-check and make sure we have line data.  (We should, but let's be safe.)
	void * pLineData;
	if ( ( pLineData = AcquireLineData ( iRow ) ) == NULL)
		return CLICKED_BACKGROUND;
	int iDepth;
	GetTreeInfo ( iRow, NULL, &iDepth, NULL );
	HT_Resource r = (HT_Resource)pLineData;
	ReleaseLineData ( pLineData );
			
	int iTriggerSize = 9;
	int iBarWidth = iTriggerSize / 2 + 1; // 5 pixels out of the 9.

	// If the user clicked on the image column, they might have struck the trigger.  Check for this.
	if ( m_pColumn[ iCol ]->iCommand == m_idImageCol ) 
	{
		int iImageWidth = GetIndentationWidth();
		RECT rcToggle = m_rcHit;
		rcToggle.left += iDepth * iImageWidth;
		rcToggle.right = rcToggle.left + iImageWidth;
		rcToggle.top += (iImageWidth - iTriggerSize) / 2 + 2; // Account for the pixel of padding
		rcToggle.bottom = rcToggle.top + iTriggerSize;

		// If the data is a container and if the point is inside the toggle rect, the user clicked the
		// trigger.
		if ( ::PtInRect( &rcToggle, point ) && HT_IsContainer(r)) 
			return CLICKED_TRIGGER;
			
		// The user may have clicked on a bar.
		if (m_bHasPipes && point.x > iImageWidth && point.x < rcToggle.right) // No bars on the outermost level
		{
			int area = point.x % iImageWidth; // Determine where within the particular level the click occurred
			int left = (iImageWidth - iBarWidth) / 2;
			int right = left + iBarWidth;
			if (area >= left && area <= right) 
				return CLICKED_BAR;
		}

		// If the user clicked to the left of the trigger on a container, then treat as a background click.
		// If the user clicked where the trigger would have been (or to the left of the trigger) on a non-
		// container, then treat that as a background click also.
		if ((point.x < rcToggle.left && HT_IsContainer(r)) ||
			(point.x < rcToggle.right && !HT_IsContainer(r)))
		  return CLICKED_BACKGROUND;
	}

	// See if the user clicked on the column text. 
	const char* text = GetColumnText(m_pColumn[ iCol ]->iCommand, r);
	if (!text)
		return CLICKED_BACKGROUND;  // Column has no text. User has to have clicked on the background.

	// All this code just determines our text rectangle.
	CClientDC dc(this);
	CRect bgRect;
	dc.SelectObject( GetLineFont( r ) );
	UINT dwDTFormat = DT_NOCLIP | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER;
	switch (m_pColumn[ iCol ]->alignment) 
	{
		case AlignCenter:
			dwDTFormat |= DT_CENTER;
			break;
		case AlignRight:
			dwDTFormat |= DT_RIGHT;
			break;
		case AlignLeft:
		default:
			dwDTFormat |= DT_LEFT;
	}
		
	CIntlWin::DrawText(CS_UTF8, dc, (char*)text, -1, &bgRect, DT_CALCRECT | dwDTFormat);
	int w = bgRect.Width() + 2*COL_LEFT_MARGIN;
	if (w > textRect.Width())
		w = textRect.Width();
	bgRect.left = textRect.left;
	bgRect.top = textRect.top;
	bgRect.right = bgRect.left + w;
	bgRect.bottom = textRect.bottom;
		
	if ( m_pColumn[ iCol ]->iCommand == m_idImageCol ) 
	  bgRect.left -= (m_pIUserImage->GetImageWidth() + OUTLINE_TEXT_OFFSET);

	// Now that we've set up the rect, see if the user clicked in it.  If they didn't, it's a background
	// click.
	if (!bgRect.PtInRect(point))
		return CLICKED_BACKGROUND;
		
	// User jumped through all our hoops.  Must have clicked on some actual data.  
	return CLICKED_LINE;
}

void CRDFOutliner::OnLButtonDown ( UINT nFlags, CPoint point )
{
// Reset drag rect info
	m_bCheckForDragRect = FALSE;
	m_LastDragRect = CRect(0,0,0,0);
	m_nRectYDisplacement = 0;

	Default();
	TipHide();

	SetFocus();

    m_ptHit = point;
    
	m_iRowHit = -1;
	m_iColHit = -1;
	
	int clickResult = DetermineClickLocation(point);
	
	if (clickResult == CLICKED_TRIGGER)
		DoToggleExpansion(m_iRowHit);
	else if (clickResult == CLICKED_BAR)
	{
		int clickDepth = m_ptHit.x / GetIndentationWidth();
		HT_Resource r = (HT_Resource)AcquireLineData(m_iRowHit);
		if (r)
		{
			for (HT_Resource curr = r; curr && HT_GetItemIndentation(curr) > clickDepth; 
				 curr = HT_GetParent(curr));

			if (curr)
				HT_SetOpenState(curr, PR_FALSE);
		}
	}
	else if (clickResult == CLICKED_LINE)				
	{
		if ( ColumnCommand( m_pColumn[ m_iColHit ]->iCommand, m_iRowHit) )
			return;

		SetCapture();
		
		SelectItem( m_iRowHit, OUTLINER_LBUTTONDOWN, nFlags );
	}
	else
	{
		// user clicked on the background
		m_bCheckForDragRect = TRUE;
		SetCapture();
		m_iFocus = -1;
		if (m_iColHit != -1)
		{
			SetSelectedColumn(m_pColumn[ m_iColHit ]->iCommand);
		}
		HT_SetSelectionAll(m_View, (PRBool)FALSE);
	}
}

void CRDFOutliner::OnLButtonUp(UINT nFlags, CPoint point)
{
	if (GetCapture() == this) {
		ReleaseCapture();
		if (m_LastDragRect != CRect(0,0,0,0))
		{
			// Dragging happened
			CClientDC dc(this);
			EraseDragRect(&dc, m_LastDragRect);

			if (m_hDragRectTimer)
			{
				KillTimer(m_hDragRectTimer);
				m_hDragRectTimer = 0;
			}
		}
		else SelectItem( m_iSelection, OUTLINER_LBUTTONUP, nFlags );
	}
}

void CRDFOutliner::OnRButtonDown ( UINT nFlags, CPoint point )
{
	// Update our column hit, since we draw selected columns.
	m_iRowHit = -1;
	m_iColHit = -1;

	int clickResult = DetermineClickLocation(point);
	
	if (clickResult == CLICKED_LINE)				
	{
		// user clicked on the item.  We will go ahead and select.
		SelectItem( m_iRowHit, OUTLINER_RBUTTONDOWN, nFlags );
	}
	else
	{
		// user clicked on the background... deselect everything.		
		m_iFocus = -1;
		HT_SetSelectionAll(m_View, (PRBool)FALSE);
	}

/*
	int iRow, iCol;
	if ( TestRowCol( point, iRow, iCol ) ){
		HT_Resource pEntry = HT_GetNthItem(m_View, iRow);
		CExportRDF exportDlg;

		if (exportDlg.DoModal() == IDOK) {
			FILE* mcfStr;
			mcfStr = fopen(exportDlg.m_csAns, "w");
			outputMCFTree(RDF_GetNavCenterDB(), stdPrint, mcfStr, HT_GetRDFResource(pEntry));
			fclose(mcfStr);  
		}
	}
	*/
}

void CRDFOutliner::OnRButtonUp( UINT nFlags, CPoint point )
{
    m_ptHit = point;
	int iSel = m_iTopLine + (point.y  / m_itemHeight);
    
	int clickResult = DetermineClickLocation(point);
	PropertyMenu( iSel, clickResult );
}

void CRDFOutliner::DragRectScroll( BOOL bBackwards )
{
	int oldLine = m_iTopLine;
    OnVScroll(bBackwards ? SB_LINEUP : SB_LINEDOWN, 0, 0);
	m_nRectYDisplacement += (oldLine - m_iTopLine)*m_itemHeight;
	if (m_nRectYDisplacement != 0)
	{
		OnMouseMove(0, m_MovePoint);
	}
}

CRect CRDFOutliner::ConstructDragRect(const CPoint& pt1, const CPoint& pt2)
{
	int left = pt1.x < pt2.x ? pt1.x : pt2.x;
	int right = pt1.x > pt2.x ? pt1.x : pt2.x;

	int top = pt1.y < pt2.y ? pt1.y : pt2.y;
	int bottom = pt1.y > pt2.y ? pt1.y : pt2.y;

	return CRect(left, top, right, bottom);
}

void CRDFOutliner::EraseDragRect(CDC* pDC, CRect rect)
{
#ifdef XP_WIN32
	CRgn rgnLast, rgnOutside, rgnInside;

	// set up regions and rects for drag rect with border of size (1,1)
	rgnLast.CreateRectRgn(0, 0, 0, 0);
	rgnOutside.CreateRectRgnIndirect(&rect);
	CRect rect2 = rect;
	rect.InflateRect(-1, -1);
	rect.IntersectRect(rect, rect2);
	rgnInside.CreateRectRgnIndirect(&rect);
	rgnLast.CombineRgn(&rgnOutside, &rgnInside, RGN_XOR);

	// Do the erase
	pDC->SelectClipRgn(&rgnLast);
	pDC->GetClipBox(&rect);
	CBrush* pBrushLast = CDC::GetHalftoneBrush();
	CBrush* pBrushOld = pDC->SelectObject(pBrushLast);
	pDC->PatBlt(rect.left, rect.top, rect.Width(), rect.Height(), PATINVERT);
	pDC->SelectObject(pBrushOld);
	pDC->SelectClipRgn(NULL);
#endif
}

void CRDFOutliner::OnMouseMove(UINT nFlags, CPoint point)
{
    if (GetCapture() == this) 
	{
#ifdef XP_WIN32
		if (m_bCheckForDragRect)
		{
			CRect rect;
			GetClientRect(&rect);

			// We're dragging a rectangle, baby.
			if (point.x < 0) // HACK so hit test works
				point.x = 0;

			if (point.y < 0)
				point.y = 0;

			if (point.y > rect.bottom)
				point.y = rect.bottom;
			
			m_MovePoint = point;

			if ( point.y < m_itemHeight ) 
			{
				m_hDragRectTimer = SetTimer(IDT_DRAGRECT, GetDragHeartbeat(), NULL);
			} 
			else if ( point.y > (rect.bottom - m_itemHeight) ) 
			{
				m_hDragRectTimer = SetTimer(IDT_DRAGRECT, GetDragHeartbeat(), NULL);
			}
			else if (m_hDragRectTimer)
			{
				KillTimer(m_hDragRectTimer);
				m_hDragRectTimer = 0;
			}
			
			CClientDC dc(this);
			CRect newRect = ConstructDragRect(m_ptHit, point);

			// Determine the new selection state
			if (m_LastDragRect == CRect(0,0,0,0))
				dc.DrawDragRect(&newRect, CSize(1,1), NULL, CSize(1,1));
			else 
			{
				// Displace the rects to account for scrolling
				m_LastDragRect.OffsetRect(0, m_nRectYDisplacement);
				newRect.OffsetRect(0, m_nRectYDisplacement);

				if (m_nRectYDisplacement > 0)
					newRect.top += m_nRectYDisplacement;
				else newRect.bottom -= m_nRectYDisplacement;

				// Reset the displacement for future scroll events
				m_nRectYDisplacement = 0;
				
				// Potential to add selection.
				CPoint testPoint1 = m_LastDragRect.TopLeft();
				CPoint testPoint2 = newRect.TopLeft();
				if (m_LastDragRect.bottom != newRect.bottom)
				{
					testPoint1 = CPoint(m_LastDragRect.left, 
										m_LastDragRect.bottom);
					testPoint2 = CPoint(newRect.left, newRect.bottom);
				}
				int iRow = -1;
				int iCol = -1;
				int otherRow = -1;
				int otherCol = -1;
				TestRowCol(testPoint1, iRow, iCol);
				TestRowCol(testPoint2, otherRow, otherCol);

				// Select the item we're on.  Always do this.
				HT_Resource r = HT_GetNthItem(m_View, iRow);
				if (r)
					HT_SetSelectedState(r, PR_TRUE);

				// Both over selections.
				if (iRow != otherRow)
				{
					// Erase the old drag rect
					EraseDragRect(&dc, m_LastDragRect);
					UpdateWindow();

					// Add or remove selection...
					int start;
					int end;
					BOOL select;

					if (iRow == -1)
						iRow = otherRow+1;
					else if (otherRow == -1)
						otherRow = iRow+1;

					if (m_LastDragRect.bottom < newRect.bottom)
					{
						start = iRow+1;
						end = otherRow+1;
						select = TRUE;
					}
					else if (m_LastDragRect.bottom > newRect.bottom)
					{
						start = otherRow+1;
						end = iRow+1;
						select = FALSE;
					}
					else if (m_LastDragRect.top > newRect.top)
					{
						start = otherRow;
						end = iRow;
						select = TRUE;
					}
					else if (m_LastDragRect.top < newRect.top)
					{
						start = iRow;
						end = otherRow;
						select = FALSE;
					}

					// Going from start to end, performing selection
					for (int i = start; i < end; i++)
					{
						HT_Resource r = HT_GetNthItem(m_View, i);
					    if (r)
					  	  HT_SetSelectedState(r, (PRBool)select);
					}

					UpdateWindow();

					// Draw the new rect
					dc.DrawDragRect(&newRect, CSize(1,1), NULL, CSize(1,1));
			
				}
				else 
					dc.DrawDragRect(&newRect, CSize(1,1), &m_LastDragRect, CSize(1,1));
				
			}
			m_LastDragRect = newRect;
		}
		else
#endif // XP_WIN32

        // See if the mouse has moved far enough to start
        // a drag operation
        if ((abs(point.x - m_ptHit.x) > 3)
        || (abs(point.y - m_ptHit.y) > 3)) 
		{
			// release the mouse capture

            ReleaseCapture();
            InitiateDragDrop();

			m_bClearOnRelease = FALSE;
			m_bSelectOnRelease = FALSE;
			SelectItem( m_iSelection, OUTLINER_LBUTTONUP, nFlags );
        }
    }
		
	if (m_iTipState != TIP_SHOWING)
		m_iTipState = TIP_WAITING;
	HandleMouseMove( point );
}


void CRDFOutliner::PropertyMenu(int iLine, UINT flags)
{
// Grab the resource
	BOOL backgroundCommands = (flags == CLICKED_BACKGROUND);

	CMenu popup;
	if (popup.CreatePopupMenu() != 0)
	{
		// We have a node and a menu. Fetch those commands.
		// Remove the elements from the last property menu we pulled up.
		m_MenuCommandMap.Clear();
		HT_Cursor theCursor = HT_NewContextualMenuCursor(m_View, (PRBool)FALSE, (PRBool)backgroundCommands);
		if (theCursor != NULL)
		{
			// We have a cursor. Attempt to iterate
			HT_MenuCmd theCommand; 
			while (HT_NextContextMenuItem(theCursor, &theCommand))
			{
				char* menuName = HT_GetMenuCmdName(theCommand);
				if (theCommand == HT_CMD_SEPARATOR)
					popup.AppendMenu(MF_SEPARATOR);
				else
				{
					// Add the command to our command map
					CRDFMenuCommand* rdfCommand = new CRDFMenuCommand(menuName, theCommand);
					int index = m_MenuCommandMap.AddCommand(rdfCommand);
					popup.AppendMenu(MF_ENABLED, index+FIRST_HT_MENU_ID, menuName);
				}
			}
			HT_DeleteCursor(theCursor);
		}

		//	Track the popup now.
		POINT pt = m_ptHit;
		ClientToScreen(&pt);
		popup.TrackPopupMenu( TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, this, NULL );

		//  Cleanup
		popup.DestroyMenu();
	}
}

BOOL CRDFOutliner::OnCommand(UINT wParam, LONG lParam)
{
	if (wParam >= FIRST_HT_MENU_ID && wParam <= LAST_HT_MENU_ID)
	{
		// A selection was made from the context menu.
		// Use the menu map to get the HT command value
		CRDFMenuCommand* theCommand = (CRDFMenuCommand*)(m_MenuCommandMap.GetCommand((int)wParam-FIRST_HT_MENU_ID));
		if (theCommand)
		{
			HT_MenuCmd htCommand = theCommand->GetHTCommand();
			HT_DoMenuCmd(m_Pane, htCommand);
		}
	}

	return TRUE;
}

void CRDFOutliner::OnSetFocus ( CWnd * pOldWnd )
{
	// Update the context's stored RDF Info.  Hack for the back end.  Needs to go away eventually.
    theApp.m_pRDFCX->TrackRDFWindow(this);
	
	Default();

	if (m_iSelection >= 0)
        for (int i =0; i < (m_iTopLine+m_iPaintLines); i++)
            if (IsSelected(i))
	            InvalidateLine(i);
	
	FocusCheck(pOldWnd, TRUE);
}
	
void CRDFOutliner::OnKillFocus ( CWnd * pNewWnd )
{
	CWnd::OnKillFocus ( pNewWnd );
	if (m_iSelection >= 0)
        for (int i =0; i < (m_iTopLine+m_iPaintLines); i++)
            if (IsSelected(i))
	            InvalidateLine(i);

	((COutlinerParent*)GetParent())->UpdateFocusFrame();

	FocusCheck(pNewWnd, FALSE);
}
	
void CRDFOutliner::FocusCheck(CWnd* pWnd, BOOL gotFocus)
{
	if (m_NavTitleBar)
	{
		if (gotFocus)
			m_NavTitleBar->NotifyFocus(TRUE);
		else
		{
			CFrameWnd* pFrameWnd = GetParentFrame();
			if (pFrameWnd && !pFrameWnd->IsChild(pWnd))
			{
				// Invalidate for a redraw
				m_NavTitleBar->NotifyFocus(FALSE);
				if (IsPopup())
				{		
					// Destroy the window.
					if (pFrameWnd->IsKindOf(RUNTIME_CLASS(CNSNavFrame)))
						((CNSNavFrame*)pFrameWnd)->DeleteNavCenter();
				}
			}
		}
	}
}

COLORREF Darken(COLORREF r)
{
	COLORREF ref = GetSysColor(COLOR_WINDOW);
	float x = GetRValue(ref);
	float y = GetGValue(ref);
	float z = GetBValue(ref);
	
	x = 0.75f*(x+1);
	y = 0.75f*(y+1);
	z = 0.75f*(z+1);

	BYTE newR = (BYTE)(int)x;
	BYTE newG = (BYTE)(int)y;
	BYTE newB = (BYTE)(int)z;

	return RGB(newR, newG, newB);
}

COLORREF Brighten(COLORREF r)
{
	COLORREF ref = GetSysColor(COLOR_WINDOW);
	float x = GetRValue(ref);
	float y = GetGValue(ref);
	float z = GetBValue(ref);
	
	x = x + 0.25f*(256 - x);
	y = y + 0.25f*(256 - y);
	z = z + 0.25f*(256 - z);

	if (x > 255.0f) x = 255.0f;
	if (y > 255.0f) y = 255.0f;
	if (z > 255.0f) z = 255.0f;

	BYTE newR = (BYTE)(int)x;
	BYTE newG = (BYTE)(int)y;
	BYTE newB = (BYTE)(int)z;

	return RGB(newR, newG, newB);
}

void ResolveToPaletteColor(COLORREF& color, HPALETTE hPal)
{
	int index = ::GetNearestPaletteIndex(hPal, color);

    PALETTEENTRY palEntry;
    ::GetPaletteEntries(hPal, index, 1, &palEntry);
    unsigned uRed   = palEntry.peRed;
    unsigned uGreen = palEntry.peGreen;
    unsigned uBlue  = palEntry.peBlue;

	color = RGB(uRed, uGreen, uBlue);
}

void CRDFOutliner::OnPaint()
{
	m_bPaintingFirstObject = TRUE;

	RECT rcClient;
	GetClientRect(&rcClient);
    CPaintDC pdc ( this );

	if (m_View == NULL)
		return;

	// Read in all the properties
	HT_Resource top = HT_TopNode(m_View);
	void* data;
	PRBool foundData = FALSE;
	
	// Options for management mode style by default.
	m_ForegroundColor = RGB(0,0,0);
	m_BackgroundColor = RGB(240,240,240);
	m_SortBackgroundColor = RGB(224,224,224);
	m_SortForegroundColor = RGB(0,0,0);
	m_bHasPipes = TRUE;
	m_bDrawDividers = TRUE;
	m_bUseSingleClick = FALSE;
	m_bUseInlineEditing = TRUE;
	m_bUseSelection = TRUE;

	// Foreground color
	HT_GetTemplateData(top, gNavCenter->viewFGColor, HT_COLUMN_STRING, &data);
	if (data)
		WFE_ParseColor((char*)data, &m_ForegroundColor);
	
	// background color
	HT_GetTemplateData(top, gNavCenter->viewBGColor, HT_COLUMN_STRING, &data);
	if (data)
		WFE_ParseColor((char*)data, &m_BackgroundColor);
	
	HT_GetTemplateData(top, gNavCenter->showTreeConnections, HT_COLUMN_STRING, &data);
	if (data)
	{
		CString answer((char*)data);
		if (answer.GetLength() > 0 && (answer.GetAt(0) == 'n' || answer.GetAt(0) == 'N'))
			m_bHasPipes = FALSE;
	}
	
	// Sort foreground color
	HT_GetTemplateData(top, gNavCenter->sortColumnFGColor, HT_COLUMN_STRING, &data);
	if (data)
		WFE_ParseColor((char*)data, &m_SortForegroundColor);
	
	// Sort background color
	HT_GetTemplateData(top, gNavCenter->sortColumnBGColor, HT_COLUMN_STRING, &data);
	if (data)
		WFE_ParseColor((char*)data, &m_SortBackgroundColor);
	
	// Compute the shadow/highlight colors for sorting and for normal modes.
	Compute3DColors(m_SortBackgroundColor, m_SortHighlightColor, m_SortShadowColor);
	Compute3DColors(m_BackgroundColor, m_HighlightColor, m_ShadowColor);

	// Selection foreground color
	HT_GetTemplateData(top, gNavCenter->selectionFGColor, HT_COLUMN_STRING, &data);
	if (data)
		WFE_ParseColor((char*)data, &m_SelectionForegroundColor);
	else m_SelectionForegroundColor = RGB(255,255,255);

	// Selection background color
	HT_GetTemplateData(top, gNavCenter->selectionBGColor, HT_COLUMN_STRING, &data);
	if (data)
		WFE_ParseColor((char*)data, &m_SelectionBackgroundColor);
	else m_SelectionBackgroundColor = RGB(0,0,128);

	// Background image URL
	m_BackgroundImageURL = "";
	HT_GetTemplateData(top, gNavCenter->viewBGURL, HT_COLUMN_STRING, &data);
	if (data)
		m_BackgroundImageURL = (char*)data;
	m_pBackgroundImage = NULL; // Clear out the BG image.

	// Divider color
	m_DividerColor = RGB(255,255,255);
	HT_GetTemplateData(top, gNavCenter->dividerColor, HT_COLUMN_STRING, &data);
	if (data)
		WFE_ParseColor((char*)data, &m_DividerColor);
	
	HT_GetTemplateData(top, gNavCenter->showDivider, HT_COLUMN_STRING, &data);
	if (data)
	{
		CString answer((char*)data);
		if (answer.GetLength() > 0 && (answer.GetAt(0) == 'n' || answer.GetAt(0) == 'N'))
			m_bDrawDividers = FALSE;
	}
	
	HT_GetTemplateData(top, gNavCenter->useSelection, HT_COLUMN_STRING, &data);
	if (data)
	{
		CString answer((char*)data);
		if (answer.GetLength() > 0 && (answer.GetAt(0) == 'n' || answer.GetAt(0) == 'N'))
			m_bUseSelection = FALSE;
	}
	
	HT_GetTemplateData(top, gNavCenter->useInlineEditing, HT_COLUMN_STRING, &data);
	if (data)
	{
		CString answer((char*)data);
		if (answer.GetLength() > 0 && (answer.GetAt(0) == 'n' || answer.GetAt(0) == 'N'))
			m_bUseInlineEditing = FALSE;
	}
	
	HT_GetTemplateData(top, gNavCenter->useSingleClick, HT_COLUMN_STRING, &data);
	if (data)
	{
		CString answer((char*)data);
		if (answer.GetLength() > 0 && (answer.GetAt(0) == 'Y' || answer.GetAt(0) == 'y'))
			m_bUseSingleClick = TRUE;
	}

	HPALETTE pOldPalette = NULL;
	if (sysInfo.m_iBitsPerPixel < 16 && (::GetDeviceCaps(pdc.m_hDC, RASTERCAPS) & RC_PALETTE))
	{
		// Use the palette, since we have less than 16 bits per pixel and are
		// using a palette-based device.
		HPALETTE hPalette = WFE_GetUIPalette(GetParentFrame());
		::SelectPalette(pdc.m_hDC, hPalette, FALSE);	

		// Find the nearest match in our palette for our colors.
		ResolveToPaletteColor(m_BackgroundColor, hPalette);
		ResolveToPaletteColor(m_ForegroundColor, hPalette);
		ResolveToPaletteColor(m_SelectionForegroundColor, hPalette);
		ResolveToPaletteColor(m_SelectionBackgroundColor, hPalette);
		ResolveToPaletteColor(m_SortBackgroundColor, hPalette);
		ResolveToPaletteColor(m_SortForegroundColor, hPalette);
		ResolveToPaletteColor(m_DividerColor, hPalette);
	}

	HBRUSH hRegBrush = (HBRUSH) ::CreateSolidBrush(m_BackgroundColor); 
    HPEN hRegPen = (HPEN)::CreatePen( PS_SOLID, 0, m_BackgroundColor);

	HBRUSH hHighBrush = ::CreateSolidBrush( m_SelectionBackgroundColor );
	HPEN hHighPen = ::CreatePen( PS_SOLID, 0, m_SelectionBackgroundColor );

    HBRUSH hOldBrush = (HBRUSH) pdc.SelectObject ( hRegBrush );
    HPEN hOldPen = (HPEN) pdc.SelectObject ( hRegPen );
    COLORREF cOldText = pdc.SetTextColor ( m_ForegroundColor );
    
	int previousBkMode = pdc.SetBkMode(TRANSPARENT);

	// Construct a precise line invalidation rect.
	int start = pdc.m_ps.rcPaint.top / m_itemHeight;
	int end = pdc.m_ps.rcPaint.bottom / m_itemHeight + 1;

	CRect clientRect;
	GetClientRect(&clientRect);

	CRect bgFillRect;
	bgFillRect.left = 0;
	bgFillRect.right = clientRect.Width();
	bgFillRect.top = m_itemHeight*start;
	bgFillRect.bottom = m_itemHeight*end;

	if (m_BackgroundImageURL != "")
	{
		// There's a background that needs to be drawn.
		m_pBackgroundImage = LookupImage(m_BackgroundImageURL, NULL);
	}

	if (m_pBackgroundImage && m_pBackgroundImage->FrameSuccessfullyLoaded())
	{
		int imageHeight = m_pBackgroundImage->bmpInfo->bmiHeader.biHeight;
		int ySrcOffset = (bgFillRect.top + m_iTopLine*m_itemHeight) % imageHeight;
		PaintBackground(pdc, bgFillRect, m_pBackgroundImage, 0, ySrcOffset);
	}
	else
	{
		::FillRect(pdc, bgFillRect, hRegBrush);
	}
	
    int i;            
    for (i = start; i <= end; i++) 
	{
		int index = i + m_iTopLine;

		PaintLine ( i, pdc.m_hDC, &pdc.m_ps.rcPaint, hHighBrush, hHighPen );

		if (m_bPaintingFirstObject)
			m_bPaintingFirstObject = FALSE;
    }

    pdc.SetTextColor ( cOldText );
    pdc.SetBkMode(previousBkMode);
    pdc.SelectObject ( hOldPen );
    pdc.SelectObject ( hOldBrush );
	
	if (sysInfo.m_iBitsPerPixel < 16 && (::GetDeviceCaps(pdc.m_hDC, RASTERCAPS) & RC_PALETTE))
	{
		::SelectPalette(pdc.m_hDC, pOldPalette, FALSE);
	}

	VERIFY(DeleteObject( hRegBrush ));
	VERIFY(DeleteObject( hRegPen ));
	VERIFY(DeleteObject( hHighBrush ));
	VERIFY(DeleteObject( hHighPen ));
}

void DrawBGSubimage(CRDFImage* pImage, HDC hDC, int xSrcOffset, int ySrcOffset, int xDstOffset, int yDstOffset,
								  int width, int height)
{
	if (pImage->bits) 
	{
		HPALETTE hPal = WFE_GetUIPalette(NULL);
		HPALETTE hOldPal = ::SelectPalette(hDC, hPal, TRUE);

		::RealizePalette(hDC);
		
		int oldMode = ::SetMapMode(hDC, MM_TEXT);

		if (pImage->maskbits) 
		{
			WFE_StretchDIBitsWithMask(hDC, TRUE, NULL,
					xDstOffset, yDstOffset,
					width, height,
					xSrcOffset, ySrcOffset, width, height,
					pImage->bits, pImage->bmpInfo,
					pImage->maskbits, FALSE, RGB(0,0,0));
		}
		else 
		{
			::StretchDIBits(hDC,
					xDstOffset, yDstOffset,
					width, height,
					xSrcOffset, ySrcOffset, width, height, pImage->bits, pImage->bmpInfo, DIB_RGB_COLORS,
					SRCCOPY);
		}

		::SetMapMode(hDC, oldMode);

		::SelectPalette(hDC, hOldPal, TRUE);
	}
}

#define LIGHT_GRAY	RGB(192, 192, 192)
#define DARK_GRAY	RGB(128, 128, 128)
#define WHITE		RGB(255, 255, 255)
#define BLACK		RGB(0, 0, 0)

void
GetSystem3DColors(COLORREF rgbBackground, COLORREF& rgbLightColor, COLORREF& rgbDarkColor)
{
#ifdef XP_WIN32
	if (sysInfo.IsWin4_32()) {
		// These are Windows 95 only
		rgbLightColor = ::GetSysColor(COLOR_3DLIGHT);
		rgbDarkColor = ::GetSysColor(COLOR_3DSHADOW);
	} else {
		rgbLightColor = LIGHT_GRAY;
		rgbDarkColor = ::GetSysColor(COLOR_BTNSHADOW);
	}
#else
	rgbLightColor = LIGHT_GRAY;
	rgbDarkColor = ::GetSysColor(COLOR_BTNSHADOW);
#endif

	// We need to make sure that both colors are visible against
	// the background
	if (rgbLightColor == rgbBackground)
		rgbLightColor = rgbBackground == LIGHT_GRAY ? WHITE : LIGHT_GRAY;

	if (rgbDarkColor == rgbBackground)
		rgbDarkColor = rgbBackground == DARK_GRAY ? BLACK : DARK_GRAY;
}

// Constants for calculating Highlight (TS = "TopShadow") and
//   shadow (BS = "BottomShadow") values relative to background
// Taken from UNIX version -- Eric Bina's Visual.c
//
// Bias brightness calculation by standard color-sensitivity values
// (Percents -- UNIX used floats, but we don't need to)
//
#define RED_LUMINOSITY 30
#define GREEN_LUMINOSITY 59
#define BLUE_LUMINOSITY 11

// Percent effect of intensity, light, and luminosity & on brightness,
#define INTENSITY_FACTOR  25
#define LIGHT_FACTOR       0
#define LUMINOSITY_FACTOR 75

// LITE color model percent to interpolate RGB towards black for BS, TS

#define COLOR_LITE_BS_FACTOR   45
#define COLOR_LITE_TS_FACTOR   70

// DARK color model - percent to interpolate RGB towards white for BS, TS

#define COLOR_DARK_BS_FACTOR   30
#define COLOR_DARK_TS_FACTOR   50
#define MAX_COLOR 255

#define COLOR_DARK_THRESHOLD   51
#define COLOR_LIGHT_THRESHOLD  204

void DrawArrow(HDC hDC, COLORREF arrowColor, int type, CRect rect, BOOL enabled)
{
	HPEN hArrowPen = ::CreatePen(PS_SOLID, 1, arrowColor);
	HPEN hOldPen = (HPEN)::SelectObject(hDC, hArrowPen);

	int size = (type == UP_ARROW || type == DOWN_ARROW) ? rect.Width() : rect.Height();
	int endPoint = (type == UP_ARROW || type == DOWN_ARROW) ? rect.left : rect.top;

	if (type == UP_ARROW)
	{
        for (int j = 0; j < 4; j++)
        {
            int yPos = rect.top + (rect.Height()/2) - 2 + j;
            int x1 = rect.left + (rect.Width()/2) - j;
            int x2 = x1 + 2*j;
			::MoveToEx(hDC, x1, yPos,NULL);
			::LineTo(hDC, x2+1, yPos);
        }

    }
	else if (type == DOWN_ARROW)
	{
        // Draw a down arrow.
        for (int j = 0; j < 4; j++)
        {
            int yPos = rect.top + (rect.Height()/2) + 1 - j;
            int x1 = rect.left + (rect.Width()/2) - j;
            int x2 = x1 + 2*j;
            ::MoveToEx(hDC,x1,yPos,NULL);
			::LineTo(hDC,x2+1,yPos);
        }
	}
	else if (type == LEFT_ARROW)
	{
        for (int j = 0; j < 4; j++)
        {
            int xPos = rect.left + (rect.Width()/2) - 2 + j;
            int y1 = rect.top + (rect.Height()/2) - j;
            int y2 = y1 + 2*j;
			::MoveToEx(hDC,xPos,y1,NULL);
			::LineTo(hDC,xPos,y2+1);
        }

    }
	else if (type == RIGHT_ARROW)
	{
        // Draw a down arrow.
        for (int j = 0; j < 4; j++)
        {
            int xPos = rect.left + (rect.Width()/2) + 1 - j;
            int y1 = rect.top + (rect.Height()/2) - j;
            int y2 = y1 + 2*j;
            ::MoveToEx(hDC,xPos,y1,NULL);
			::LineTo(hDC,xPos,y2+1);
        }
	}

	::SelectObject(hDC, hOldPen);
	VERIFY(::DeleteObject(hArrowPen));
}

void Compute3DColors(COLORREF rgbColor, COLORREF &rgbLight, COLORREF &rgbDark)
{
	unsigned uRed, uGreen, uBlue;
	unsigned uRedBack = GetRValue(rgbColor);
	unsigned uGreenBack = GetGValue(rgbColor);
	unsigned uBlueBack = GetBValue(rgbColor);
	unsigned intensity = (uRedBack + uGreenBack + uBlueBack) / 3;
    unsigned luminosity = ((RED_LUMINOSITY * uRedBack)/ 100)
	                 + ((GREEN_LUMINOSITY * uGreenBack)/ 100)
                     + ((BLUE_LUMINOSITY * uBlueBack)/ 100);
	unsigned backgroundBrightness = ((intensity * INTENSITY_FACTOR) +
				   (luminosity * LUMINOSITY_FACTOR)) / 100;
    unsigned f;
	
    if (backgroundBrightness < COLOR_DARK_THRESHOLD) {
        // Dark Background - interpolate 30% toward black
		uRed =  uRedBack - (COLOR_DARK_BS_FACTOR * uRedBack / 100);
		uGreen = uGreenBack - (COLOR_DARK_BS_FACTOR * uGreenBack / 100);
		uBlue = uBlueBack - (COLOR_DARK_BS_FACTOR * uBlueBack / 100);

        rgbDark = RGB(uRed, uGreen, uBlue);

        // This interpolotes to 50% toward white
		uRed = uRedBack + (COLOR_DARK_TS_FACTOR *
			(MAX_COLOR - uRedBack) / 100);
		uGreen = uGreenBack + (COLOR_DARK_TS_FACTOR *
			(MAX_COLOR - uGreenBack) / 100);

		uBlue = uBlueBack + (COLOR_DARK_TS_FACTOR *
			(MAX_COLOR - uBlueBack) / 100);

	} else if (backgroundBrightness > COLOR_LIGHT_THRESHOLD) {
        // Interpolate 45% toward black
		uRed = uRedBack - (COLOR_LITE_BS_FACTOR * uRedBack / 100);
		uGreen = uGreenBack - (COLOR_LITE_BS_FACTOR * uGreenBack / 100);
		uBlue = uBlueBack - (COLOR_LITE_BS_FACTOR * uBlueBack / 100);
        rgbDark = RGB(uRed, uGreen, uBlue);

	    // Original algorithm (from X source: visual.c) used:
    	// uRed = uRedBack - (COLOR_LITE_TS_FACTOR * uRedBack / 100),
        //   where FACTOR is 20%, but that makes no sense!
        // I think the intention was large interpolation toward white,
        //  so use max of "medium" range (70%) for smooth continuity across threshhold
      uRed = uRedBack + (COLOR_LITE_TS_FACTOR * (MAX_COLOR - uRedBack) / 100);
      uGreen = uGreenBack + (COLOR_LITE_TS_FACTOR * (MAX_COLOR - uGreenBack) / 100);
      uBlue = uBlueBack + (COLOR_LITE_TS_FACTOR * (MAX_COLOR - uBlueBack) / 100);
	
	} else {
        // Medium Background
		f = COLOR_DARK_BS_FACTOR + (backgroundBrightness 
		                * ( COLOR_LITE_BS_FACTOR - COLOR_DARK_BS_FACTOR )
                        / MAX_COLOR);

		uRed = uRedBack - (f * uRedBack / 100);
		uGreen = uGreenBack - (f * uGreenBack / 100);
		uBlue = uBlueBack - (f * uBlueBack / 100);
        rgbDark = RGB(uRed, uGreen, uBlue);

		f = COLOR_DARK_TS_FACTOR + (backgroundBrightness
		               * ( COLOR_LITE_TS_FACTOR - COLOR_DARK_TS_FACTOR )
                       / MAX_COLOR);

        uRed = uRedBack + (f * (MAX_COLOR - uRedBack) / 100);
		uGreen = uGreenBack + (f * (MAX_COLOR - uGreenBack) / 100);
		uBlue = uBlueBack + (f * (MAX_COLOR - uBlueBack) / 100);
    }
    
	// Safety check for upper limit
	uRed = min(MAX_COLOR, uRed);
	uGreen = min(MAX_COLOR, uGreen);
	uBlue = min(MAX_COLOR, uBlue);

    rgbLight = RGB(uRed, uGreen, uBlue);

	// Special case white backgrounds and black backgrounds
	if (rgbLight == rgbColor && (uRedBack == MAX_COLOR && uGreenBack == MAX_COLOR && uBlueBack == MAX_COLOR))
	{
		// For a white separator use medium gray colors.
		rgbLight = RGB(192,192,192);
	}
	else if (rgbDark == rgbColor && rgbColor == 0)
	{
		// Use a dark gray color.
		rgbDark = RGB(64,64,64);
	}

	// If either of these colors is the same as the background color
	// then use the system 3D element colors instead
	if (rgbLight == rgbColor || rgbDark == rgbColor) {
		GetSystem3DColors(rgbColor, rgbLight, rgbDark);
    }
}

void PaintBackground(HDC hdc, CRect rect, CRDFImage* pImage, int xSrcOffset, int ySrcOffset)
{ 
	int totalWidth = rect.Width();
	int totalHeight = rect.Height();

	if (!pImage->bits)
		return;

	int imageWidth = pImage->bmpInfo->bmiHeader.biWidth;
	int imageHeight = pImage->bmpInfo->bmiHeader.biHeight;
	
	// Ok, complicated math time.
	int xDstOffset = rect.left;
	int yDstOffset = rect.top;
	
	if (ySrcOffset == -1) // Assume we don't have a scrolled offset in the view we're drawing into.
	  ySrcOffset = rect.top % imageHeight;

	if (xSrcOffset == -1) // Assume we don't have a scrolled offset in the view we're drawing into.
	  xSrcOffset = rect.left % imageWidth;

	int xRemainder = imageWidth - xSrcOffset;
	int yRemainder = imageHeight - ySrcOffset;

	int xSize = xRemainder > totalWidth ? totalWidth : xRemainder;
	int ySize = yRemainder > totalHeight ? totalHeight : yRemainder;

	while (yDstOffset < rect.bottom)
	{
		// Tile vertically
		while (xDstOffset < rect.right)
		{
			int ySrc = ySrcOffset;
			if (imageHeight != ySize)
				ySrc = imageHeight - ySize - ySrcOffset;
			
			DrawBGSubimage(pImage, hdc, xSrcOffset, ySrc, xDstOffset, yDstOffset, xSize, ySize);
			
			xSrcOffset = 0;
			xDstOffset += xSize;
			
			xSize = (xDstOffset + imageWidth) > rect.right ? imageWidth - (xDstOffset + imageWidth) + rect.right : imageWidth;
		}

		xDstOffset = rect.left;
		xSize = (xDstOffset + imageWidth) > rect.right ? rect.right - xDstOffset : imageWidth;
		ySrcOffset = 0;
		yDstOffset += ySize;
		ySize = (yDstOffset + imageHeight) > rect.bottom ? rect.bottom - yDstOffset : imageHeight;
	}
}



void CRDFOutliner::PaintLine ( int iLineNo, HDC hdc, LPRECT lpPaintRect,
							   HBRUSH hHighlightBrush,
						       HPEN hHighlightPen )
{
    void * pLineData;
    int iImageWidth = GetIndentationWidth();
    CRect WinRect;
    GetClientRect(&WinRect);

    int y = m_itemHeight * iLineNo;

    int iColumn, offset;
	CRect rectColumn, rectInter;

	rectColumn.top = y;
	rectColumn.bottom = y + m_itemHeight;

	if ( !( pLineData = AcquireLineData( iLineNo + m_iTopLine )) ) 
	{
		// We're drawing a blank line.
		if (ViewerHasFocus() && HasFocus(iLineNo + m_iTopLine)) 
		{
			CRect internalRect(rectColumn);
			internalRect.top += 1; // Move in by a pixel.
			internalRect.bottom -= 2; // Move in by a pixel + the divider line.

			internalRect.left = WinRect.left;
			internalRect.right = WinRect.right;
			DrawFocusRect ( hdc, &internalRect );
		}

		// Draw the column rect
		for ( int iColumn = offset = 0; iColumn < m_iVisColumns; iColumn++ )
		{
			rectColumn.left = offset;
			rectColumn.right = offset + m_pColumn[ iColumn ]->iCol;

			if (iColumn == GetSortColumn())
			{
				// Draw the sort rect.
				HBRUSH hSortBrush = (HBRUSH)::CreateSolidBrush(m_SortBackgroundColor);
				::FillRect(hdc, &rectColumn, hSortBrush);
				VERIFY(DeleteObject(hSortBrush));
			}

			offset += m_pColumn[ iColumn ]->iCol;
		}
		return;
	}

	// Draw the divider
	if (m_bDrawDividers)
	{
		CDC* pDC = CDC::FromHandle(hdc);
		HPEN hDividerPen = ::CreatePen(PS_SOLID, 1, m_DividerColor);
		HPEN pOldPen = (HPEN)::SelectObject(hdc, hDividerPen);
		pDC->MoveTo(WinRect.left, rectColumn.bottom-1);
		pDC->LineTo(WinRect.right, rectColumn.bottom-1);
		::SelectObject(hdc, pOldPen);
		VERIFY(::DeleteObject(hDividerPen));
	}

    HFONT hOldFont =(HFONT) ::SelectObject ( hdc, GetLineFont ( pLineData ) );

    for ( iColumn = offset = 0; iColumn < m_iVisColumns; iColumn++ )
    {
		rectColumn.left = offset;
		rectColumn.right = offset + m_pColumn[ iColumn ]->iCol;

        if ( rectInter.IntersectRect ( &rectColumn, lpPaintRect ) ) 
		{
			HBRUSH hSortBrush;
			if (iColumn == GetSortColumn())
			{
				hSortBrush = (HBRUSH)::CreateSolidBrush(m_SortBackgroundColor);
				CRect tempRect(rectColumn);
				if (m_bDrawDividers)
					tempRect.bottom--;
				::FillRect(hdc, &tempRect, hSortBrush);
				VERIFY(DeleteObject(hSortBrush));
			}
			
			if ( m_pColumn[ iColumn ]->iCommand == m_idImageCol ) 
			{
				rectColumn.left = DrawPipes ( iLineNo, iColumn, offset, hdc, pLineData );
			}
		
            PaintColumn ( iLineNo, iColumn, rectColumn, hdc, pLineData, hHighlightBrush,
						  hHighlightPen );
		}

        offset += m_pColumn[ iColumn ]->iCol;
    }

    rectColumn.left = offset;
	rectColumn.right = WinRect.right;

	if (!(m_pBackgroundImage && m_pBackgroundImage->FrameSuccessfullyLoaded()))
	{
		if (m_bDrawDividers)
			rectColumn.bottom--; // Handle the divider
		::FillRect(hdc, &rectColumn, (HBRUSH) GetCurrentObject(hdc, OBJ_BRUSH));
		PaintColumn ( iLineNo, iColumn, rectColumn, hdc, pLineData, hHighlightBrush, hHighlightPen );
	}

	rectColumn.left = WinRect.left;
	rectColumn.right = WinRect.right;

	// if we are dragging we and we don't highlight we need to draw the drag line
	if(m_iDragSelection == m_iTopLine + iLineNo && !HighlightIfDragging() && GetSortColumn() == -1
		&& m_iCurrentDropAction != DROPEFFECT_NONE)
	   PaintDragLine(hdc, rectColumn);

    ::SelectObject ( hdc, hOldFont );
    ReleaseLineData ( pLineData );
}

void CRDFOutliner::PaintColumn(int iLineNo, int iColumn, LPRECT lpColumnRect, 
							   HDC hdc, void * pLineData, HBRUSH hHighlightBrush,
							   HPEN hHighlightPen)
{
    if (iColumn < m_iVisColumns) 
	{
		HT_Resource theNode = (HT_Resource)pLineData;
		const char* lpsz = GetColumnText (m_pColumn[ iColumn ]->iCommand,pLineData);
		
		if (theNode && HT_IsSeparator(theNode))
		{
			// Alter the column text name.
			lpsz = NULL;
		}

		if ( !RenderData( m_pColumn[iColumn]->iCommand, CRect(lpColumnRect), 
						  *CDC::FromHandle( hdc ), lpsz) ) 
		{
			BOOL hasFocus = FALSE;
		
			CRect WinRect;
			GetClientRect(&WinRect);
			
			if (theNode && HT_IsSeparator(theNode) && lpColumnRect->right != WinRect.right)
			{
				// Draw the horizontal line.
				CPen separatorHighlightPen;
				CPen separatorShadowPen;

				if (iColumn == GetSortColumn())
				{
					separatorHighlightPen.CreatePen(PS_SOLID, 1, m_SortHighlightColor);
					separatorShadowPen.CreatePen(PS_SOLID, 1, m_SortShadowColor);
				}
				else 
				{
					separatorHighlightPen.CreatePen(PS_SOLID, 1, m_HighlightColor);
					separatorShadowPen.CreatePen(PS_SOLID, 1, m_ShadowColor);
				}

				//HPEN sepPen = (HPEN)separatorPen.GetSafeHandle();
				//HPEN usePen = IsSelected(iLineNo) ? hHighlightPen : sepPen;

				HPEN pOldPen = (HPEN)(::SelectObject(hdc, (HPEN)separatorShadowPen.GetSafeHandle()));
				::MoveToEx(hdc, lpColumnRect->left, lpColumnRect->top+m_itemHeight/2, NULL);
				::LineTo(hdc, lpColumnRect->right, lpColumnRect->top+m_itemHeight/2);

				::SelectObject(hdc, (HPEN)separatorHighlightPen.GetSafeHandle());
				::MoveToEx(hdc, lpColumnRect->left, lpColumnRect->top+m_itemHeight/2+1, NULL);
				::LineTo(hdc, lpColumnRect->right, lpColumnRect->top+m_itemHeight/2+1);

				::SelectObject(hdc, pOldPen);
			}

			if (lpsz) 
			{
				if ((IsSelected(iLineNo + m_iTopLine) || 
					((m_iDragSelection == m_iTopLine + iLineNo) && HighlightIfDragging())) &&
					IsSelectedColumn(iColumn))
				{
					hasFocus = HasFocus(iLineNo + m_iTopLine);
					
					if (ViewerHasFocus())
					{
						HBRUSH pOldBrush = (HBRUSH)(::SelectObject ( hdc, hHighlightBrush ));
						HPEN pOldPen = (HPEN)(::SelectObject ( hdc, hHighlightPen ));
						COLORREF cOldText = ::SetTextColor ( hdc, m_SelectionForegroundColor );
						
						DrawColumn ( hdc, lpColumnRect, lpsz, 
								 m_pColumn[ iColumn ]->cropping, 
								 m_pColumn[ iColumn ]->alignment, hHighlightBrush, hasFocus );
						::SelectObject(hdc, pOldBrush);
						::SelectObject(hdc, pOldPen);
						::SetTextColor ( hdc, cOldText );
						
					}
					else DrawColumn ( hdc, lpColumnRect, lpsz, 
								 m_pColumn[ iColumn ]->cropping, 
								 m_pColumn[ iColumn ]->alignment, hHighlightBrush, hasFocus );
				}
				else DrawColumn ( hdc, lpColumnRect, lpsz, 
								 m_pColumn[ iColumn ]->cropping, 
								 m_pColumn[ iColumn ]->alignment);

			}
		}
	}
}

void CRDFOutliner::DrawColumn(HDC hdc, LPRECT lpColumnRect, LPCTSTR lpszString,
							  CropType_t cropping, AlignType_t alignment, HBRUSH theBrush,
							  BOOL hasFocus)
{
	if (!(lpColumnRect->right - lpColumnRect->left))
		return;
	ASSERT(lpszString);
    int iLength = _tcslen(lpszString);
	if (!iLength)
		return;

	UINT dwDTFormat = DT_NOCLIP | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER;
	switch (alignment) {
	case AlignCenter:
		dwDTFormat |= DT_CENTER;
		break;
	case AlignRight:
		dwDTFormat |= DT_RIGHT;
		break;
	case AlignLeft:
	default:
		dwDTFormat |= DT_LEFT;
	}

	UINT dwMoreFormat = 0;
	switch (cropping) {
	case CropCenter:
		dwMoreFormat |= WFE_DT_CROPCENTER;
		break;
	case CropLeft:
		dwMoreFormat |= WFE_DT_CROPLEFT;
		break;
	case CropRight:
	default:
		dwMoreFormat |= WFE_DT_CROPRIGHT;
		break;
	}

	CRect textRect(*lpColumnRect);
	textRect.top += 1; // Account for the slight padding.
	textRect.bottom -= 2; // Account for the slight padding and the divider line.

	CRect bgRect;

	// Compute background rectangle
	CIntlWin::DrawText(CS_UTF8, hdc, (char*)lpszString, -1, &bgRect, DT_CALCRECT | dwDTFormat);
	int w = bgRect.Width() + 2*COL_LEFT_MARGIN;
	if (w > textRect.Width())
		w = textRect.Width();
	bgRect.left = textRect.left;
	bgRect.top = textRect.top;
	bgRect.right = bgRect.left + w;
	bgRect.bottom = textRect.bottom;

	// Fill the background with the current brush (or frame if we don't have focus)
	if (theBrush)
	{
		if (ViewerHasFocus())
		{
			CRect newRect(bgRect);
			if (hasFocus)
			{	
				newRect.top +=1;
				newRect.bottom-=1;
				newRect.left+=1;
				newRect.right-=1;
			}
			::FillRect(hdc, &newRect, theBrush);
		}
		
		::FrameRect( hdc, &bgRect, theBrush);
	}

	// Adjust the text rectangle for the left and right margins
	textRect.left += COL_LEFT_MARGIN;
	textRect.right -= COL_LEFT_MARGIN;

	WFE_DrawTextEx( CS_UTF8, hdc, (LPTSTR) lpszString, iLength, &textRect, dwDTFormat, dwMoreFormat );
}

int CRDFOutliner::DrawPipes ( int iLineNo, int iColNo, int offset, HDC hdc, void * pLineData )
{
	int iImageWidth = m_itemHeight - 3; // Account for the two pixel padding + the divider
	int iTriggerSize = 9;	// This will need to be changed when custom triggers arrive.
	int iBarWidth = iTriggerSize / 2 + 1; // 5 pixels out of the 9.
	int iMaxX = offset + m_pColumn[ iColNo ]->iCol;
	int idx;

	int iDepth;
	uint32 flags;
	OutlinerAncestorInfo * pAncestor;
	GetTreeInfo ( m_iTopLine + iLineNo, &flags, &iDepth, &pAncestor );

	RECT rect;
	rect.left = offset;
	rect.right = rect.left + iImageWidth;
	rect.top = iLineNo * m_itemHeight;
	rect.bottom = rect.top + m_itemHeight;

	for ( int i = 0; i < iDepth; i++ )  
	{
		if ( rect.right <= iMaxX ) 
		{
			if ( m_bHasPipes && pAncestor && pAncestor[ i ].has_next && i > 0) // Ignore the outermost level.
			{
				// Draw the appropriate vertical bar.
				// bar should be 5 pixels wide. 1 black - 3 gray - 1 black.
				CDC* pDC = CDC::FromHandle(hdc);
				CBrush innerBrush(RGB(192,192,192));
				CBrush outerBrush(RGB(128,128,128));
				CRect barRect(rect);
				barRect.left += (iImageWidth - iBarWidth) / 2;
				barRect.right = barRect.left + iBarWidth;
				pDC->FrameRect(barRect, &outerBrush);
				barRect.left += 1;
				barRect.right -= 1;
				pDC->FillRect(barRect, &innerBrush);
			} 
		}
		
		rect.left += iImageWidth;
		rect.right += iImageWidth;
	}
    
	if (rect.right <= iMaxX) 
	{
		if (iDepth && m_bHasPipes)
		{
			// Draw the vertical bar.
			CDC* pDC = CDC::FromHandle(hdc);
			CBrush innerBrush(RGB(192,192,192));
			CBrush outerBrush(RGB(128,128,128));
			CRect barRect(rect);
			barRect.left += (iImageWidth - iBarWidth) / 2;
			barRect.right = barRect.left + iBarWidth;
			if (!pAncestor[iDepth].has_next)
			{
				barRect.bottom -= 2; // Move away from the divider and even supply a little padding.
			}
			if (!pAncestor[iDepth].has_prev)
			{
				barRect.top += 1; // Supply a little padding.
			}
			pDC->FrameRect(barRect, &outerBrush);
			barRect.left += 1;
			barRect.right -= 1;
			if (!pAncestor[iDepth].has_next)
			{
				barRect.bottom -= 1; // Actually get that frame onto the end
			}
			if (!pAncestor[iDepth].has_prev)
			{
				barRect.top += 1; // Get that frame onto the top.
			}

			pDC->FillRect(barRect, &innerBrush);
		}
	
		HT_Resource r = (HT_Resource)pLineData;
		if (r && HT_IsContainer(r) && m_bHasPipes)
		{
			// Draw the trigger
			CBrush outerTrigger(RGB(128,128,128));
			CBrush innerTrigger(RGB(255,255,255));
			CRect triggerRect(rect);
			triggerRect.top += (iImageWidth - iTriggerSize) / 2 + 2; // Account for the pixel of padding
			triggerRect.bottom = triggerRect.top + iTriggerSize;
			triggerRect.left += (iImageWidth - iTriggerSize) / 2;
			triggerRect.right = triggerRect.left + iTriggerSize;
			CDC* pDC = CDC::FromHandle(hdc);
			pDC->FillRect(triggerRect, &innerTrigger);
			pDC->FrameRect(triggerRect, &outerTrigger);

			// Draw the horizontal portion of the trigger cross
			HPEN pen = ::CreatePen(PS_SOLID, 1, RGB(0,0,0));
			HPEN pOldPen = (HPEN)::SelectObject(hdc, pen);
			pDC->MoveTo(triggerRect.left + 2, triggerRect.top + (iTriggerSize / 2));
			pDC->LineTo(triggerRect.right - 2, triggerRect.top + (iTriggerSize / 2));

			// Draw the vertical portion of the trigger cross (only for closed containers)
			if (!HT_IsContainerOpen(r))
			{
				pDC->MoveTo(triggerRect.left + (iTriggerSize / 2), triggerRect.top + 2);
				pDC->LineTo(triggerRect.left + (iTriggerSize / 2), triggerRect.bottom - 2);
			}

			::SelectObject(hdc, pOldPen);
			VERIFY(::DeleteObject(pen));
		}

		rect.left += iImageWidth;
		rect.right += iImageWidth;
	}

    if ( rect.right <= iMaxX ) 
	{
        idx = TranslateIcon (pLineData);
		HT_Resource r = (HT_Resource)pLineData;
		if (!HT_IsSeparator(r))
		{
			rect.top += 1; // Handle the padding.

			if (idx == HTFE_USE_CUSTOM_IMAGE)
				DrawArbitraryURL(r, rect.left, rect.top, 16, 16, hdc, ::GetBkColor(hdc), this, FALSE);
			else if (idx == HTFE_LOCAL_FILE)
				DrawLocalFileIcon(r, rect.left, rect.top, hdc);
			else m_pIUserImage->DrawTransImage ( idx, rect.left, rect.top, hdc );
		}
		else return rect.left;
    }

	return rect.right + OUTLINE_TEXT_OFFSET;
}


void CRDFOutliner::ColumnsSwapped()
{
	SetImageColumn(m_pColumn[0]->iCommand);
}

void CRDFOutliner::DestroyColumns()
{
	m_Parent->DestroyColumns();
	COutliner::DestroyColumns();
}

CRect CRDFOutliner::GetColumnRect(int iLine, int column)
{
	CRect rectColumn;
	rectColumn.top = (iLine-m_iTopLine) * m_itemHeight;
	rectColumn.bottom = rectColumn.top + m_itemHeight;
	rectColumn.left = rectColumn.right = 0;

	int iColumn, offset;
	int iDepth;
	uint32 flags;
	OutlinerAncestorInfo * pAncestor;
	GetTreeInfo ( iLine, &flags, &iDepth, &pAncestor );

	for (iColumn = offset = 0; iColumn < m_iVisColumns; iColumn++)
    {
		if (m_pColumn[iColumn]->iCommand == m_idImageCol) 
		{
			// Handle the image column (where the indentation is)
			int iImageWidth = GetIndentationWidth();
			rectColumn.left = (iImageWidth * (iDepth+1)) + OUTLINE_TEXT_OFFSET + m_pIUserImage->GetImageWidth();
        }
		else rectColumn.left = offset;

		rectColumn.right = offset + m_pColumn[ iColumn ]->iCol;

		if (m_pColumn[iColumn]->iCommand == (UINT)column)
			break;

        offset += m_pColumn[ iColumn ]->iCol;
    }

    return rectColumn;
}

char * CRDFOutliner::GetTextEditText(void)
{
	char str[1000];

	if(m_EditField)
	{
		int size = m_EditField->GetWindowText(str, 1000);
		if(size > 0)
		{
			char *newStr = new char[size + 1];
			strcpy(newStr, str);
			return newStr;
		}
	}

	return NULL;

}

// Edit window routines
void CRDFOutliner::EditTextChanged(char* text)
{
	if (text != NULL)
	{
		// Do stuff

		CRDFCommandMap& map = m_Parent->GetColumnCommandMap(); 
		CRDFColumn* theColumn = (CRDFColumn*)(map.GetCommand(m_nSelectedColumn));
		
		HT_SetNodeData(m_Node, theColumn->GetToken(), theColumn->GetDataType(), text);

		delete []text;
	}
}

void CRDFOutliner::AddTextEdit()
{
	BOOL bRtn = TRUE;
	if(!m_EditField)
	{
		m_EditField = new CRDFEditWnd(this);
		bRtn = m_EditField->Create(WS_BORDER | ES_AUTOHSCROLL, CRect(0,0,0,0), this, 0);
	}

	CRect rect;
	HDC hDC = ::GetDC(m_hWnd);
	HFONT hFont = WFE_GetUIFont(hDC);
	HFONT hOldFont = (HFONT)::SelectObject(hDC, hFont);
	TEXTMETRIC tm;

	GetTextMetrics(hDC, &tm);
	int height = tm.tmHeight;
	::SelectObject(hDC, hOldFont);
	::ReleaseDC(m_hWnd, hDC);

	rect = GetColumnRect(m_iSelection, m_nSelectedColumn);
	rect.top += 1;
	
	if (rect.left == rect.right)
		bRtn = FALSE;

	if(!bRtn)
	{
		delete m_EditField;
		m_EditField = NULL;
	}
	else
	{
		m_EditField->MoveWindow(rect);
		m_EditField->ShowWindow(SW_SHOW);
		HDC hDC = ::GetDC(m_hWnd);
		HFONT hFont = WFE_GetUIFont(hDC);
		CFont *font = (CFont*) CFont::FromHandle(hFont);
		m_EditField->SetFont(font);
		::ReleaseDC(m_hWnd, hDC);
		m_EditField->SetFocus();

		char* pText = (char*)GetColumnText(m_nSelectedColumn, HT_GetNthItem(m_View, m_iSelection));

		if (pText)
		{
			m_EditField->SetSel(0,-1);
			m_EditField->ReplaceSel(pText);
			m_EditField->SetSel(0,-1);
		}
	}
}

void CRDFOutliner::RemoveTextEdit()
{
	if(m_EditField)
	{
		m_EditField->ShowWindow(SW_HIDE);
	}
}

void CRDFOutliner::MoveToNextColumn()
{
	int oldPos = GetColumnPos((UINT)m_nSelectedColumn);
	oldPos++;
	if (oldPos < (int)GetVisibleColumns())
	{
		m_nSelectedColumn = GetColumnAtPos(oldPos);
		RemoveTextEdit();
	}
	else 
	{
		m_nSelectedColumn = GetColumnAtPos(0);
		RemoveTextEdit();
		HT_Resource r = HT_GetNthItem(m_View, m_iSelection);
		if (r)
			HT_SetSelectedState(r, (PRBool)FALSE);
		m_iSelection++;
		m_iFocus = m_iSelection;
		HT_Resource r2 = HT_GetNthItem(m_View, m_iSelection);
		if (!r2)
		{
			m_iFocus = -1;
			m_iSelection = -1;
			m_Node = NULL;
			return;
		}

		CRDFCommandMap& map = m_Parent->GetColumnCommandMap(); 
		CRDFColumn* theColumn = (CRDFColumn*)(map.GetCommand(m_nSelectedColumn));
		
		BOOL isEditable = HT_IsNodeDataEditable(r2, theColumn->GetToken(), 
													theColumn->GetDataType());
		if (!isEditable)
		{
			m_iFocus = -1;
			m_iSelection = -1;
			m_Node = NULL;
			return;
		}

		HT_SetSelectedState(r2, (PRBool)TRUE);

		m_Node = r2;
	}
	
	AddTextEdit();
}


void CRDFOutliner::OnTimer(UINT nID)
{
	if (nID == IDT_EDITFOCUS) 
	{
		if (m_hEditTimer != 0)
		{
			KillTimer(m_hEditTimer);
			m_hEditTimer = 0;
		}	
		AddTextEdit();
		return;
	}
	else if (nID == IDT_SPRINGLOAD)
	{
		if (m_hSpringloadTimer != 0) 
		{
			KillTimer(m_hSpringloadTimer);
			m_hSpringloadTimer = 0;
		
			if (m_iDragSelection == m_iSpringloadSelection)
			{
				// Open the folder
				HT_Resource r = HT_GetNthItem(m_View, m_iDragSelection);
				if (r != NULL)
				{
					// Pop it open
					HT_SetOpenState(r, (PRBool)TRUE);

					// Add this folder to our list of springloaded folders
					m_SpringloadStack.AddHead((void*)r);
				}
			}
		}

		return;
	}
	else if (nID == IDT_DRAGRECT)
	{
		if (m_hDragRectTimer != 0)
		{
			KillTimer(m_hDragRectTimer);
			m_hDragRectTimer = 0;

			CRect rect;
			GetClientRect(&rect);

			if (m_MovePoint.y < m_itemHeight)
			{
				DragRectScroll(TRUE);
				m_hDragRectTimer = SetTimer(IDT_DRAGRECT, GetDragHeartbeat(), NULL);
			}

			else if (m_MovePoint.y > rect.bottom - m_itemHeight)
			{
				DragRectScroll(FALSE);
				m_hDragRectTimer = SetTimer(IDT_DRAGRECT, GetDragHeartbeat(), NULL);
			}
		}

		return;
	}

	COutliner::OnTimer(nID);
}

// DRAG AND DROP ROUTINES
int CRDFOutliner::GetDragHeartbeat()
{
	return RDF_DRAG_HEARTBEAT;
}

COleDataSource * CRDFOutliner::GetDataSource()
{
	m_bNeedToClear = FALSE; // Hack. Just leave it here, and don't worry about it.

    COleDataSource * pDataSource = new COleDataSource;  

    HANDLE hString = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE,1);
    pDataSource->CacheGlobalData(m_cfHTNode, hString);

	// The view is sufficient
	RDFGLOBAL_BeginDrag(pDataSource, m_View);
	
    return pDataSource;
}

void RDFGLOBAL_BeginDrag(COleDataSource* pDataSource, HT_View pView)
{
	CLIPFORMAT cfBookmark = (CLIPFORMAT) RegisterClipboardFormat( NETSCAPE_HTNODE_FORMAT );
    HGLOBAL hBookmark = GlobalAlloc( GMEM_DDESHARE | GMEM_ZEROINIT, sizeof(HT_View) );
    if( hBookmark ) 
	{
        HT_View* pBookmark = (HT_View*) GlobalLock( hBookmark );
		*pBookmark = pView;
        GlobalUnlock( hBookmark);
        pDataSource->CacheGlobalData( cfBookmark, hBookmark );
    }

	// Get the selected items and set up internet shortcut
	// drag if the item is not a container or separator.
	int selCount = 0;
	HT_Resource r = HT_GetNextSelection(pView, NULL);
	while (r != NULL)
	{
		if (!HT_IsDropTarget(r) && !HT_IsSeparator(r))
			selCount++;
		r = HT_GetNextSelection(pView, r);
	}

	CString* titleArray = new CString[selCount];
	CString* urlArray = new CString[selCount];
	int i = -1;
	r = HT_GetNextSelection(pView, NULL);
	while (r != NULL)
	{
		if (!HT_IsDropTarget(r) && !HT_IsSeparator(r))
		{
			i++; // Found one
			titleArray[i] = HT_GetNodeName(r);
			urlArray[i] = HT_GetNodeURL(r);
		}
		r = HT_GetNextSelection(pView, r);
	}
	DragMultipleShortcuts(pDataSource, titleArray, urlArray, selCount);
	delete []titleArray;
	delete []urlArray;
}

void RDFGLOBAL_DragTitleAndURL( COleDataSource *pDataSource, LPCSTR title, LPCSTR url )
{
	CLIPFORMAT cfBookmark = (CLIPFORMAT) RegisterClipboardFormat( NETSCAPE_BOOKMARK_FORMAT );
    HGLOBAL hBookmark = GlobalAlloc( GMEM_DDESHARE | GMEM_ZEROINIT, sizeof(BOOKMARKITEM) );
    if( hBookmark ) {
        LPBOOKMARKITEM pBookmark = (LPBOOKMARKITEM) GlobalLock( hBookmark );
        PR_snprintf( pBookmark->szAnchor, sizeof(pBookmark->szAnchor),"%s", url );
        PR_snprintf( pBookmark->szText, sizeof(pBookmark->szText),"%s", title );
        GlobalUnlock( hBookmark);

        pDataSource->CacheGlobalData( cfBookmark, hBookmark );
    }

	HGLOBAL hString = GlobalAlloc( GMEM_MOVEABLE|GMEM_DDESHARE, strlen( url ) + 1 );
	if ( hString ) {
		LPSTR lpszString = (LPSTR) GlobalLock( hString );
		strcpy( lpszString, url );
		GlobalUnlock( hString );

		pDataSource->CacheGlobalData( CF_TEXT, hString );
    }

	// Do an internet shortcut drag
	DragInternetShortcut(pDataSource, title, url);
}


COutlinerDropTarget* CRDFOutliner::CreateDropTarget()
{
	return new CRDFDropTarget(this);
}

BOOL CRDFOutliner::HighlightIfDragging(void)
{
	if(m_iDragSelection != -1)
	{
		HT_Resource r = HT_GetNthItem(m_View, m_iDragSelection);
		if (r != NULL && HT_IsDropTarget(r) && (m_iDragSelectionLineThird == 2 || (GetSortColumn() != -1)))
			return TRUE;
	}
	return FALSE;
}

void CRDFOutliner::PaintDragLine(HDC hdc, CRect &rectColumn)
{
	HPEN pen = ::CreatePen(PS_SOLID, 1, m_ForegroundColor);
	HPEN pOldPen = (HPEN)::SelectObject(hdc, pen);

	if(m_iDragSelectionLineThird == 1) {
#ifdef XP_WIN32
		MoveToEx(hdc, rectColumn.left, rectColumn.top, NULL);
#else
		MoveTo(hdc, rectColumn.left, rectColumn.top);
#endif
		LineTo(hdc, rectColumn.right, rectColumn.top);
	}
	else if(m_iDragSelectionLineThird == 3){
#ifdef XP_WIN32
		MoveToEx(hdc, rectColumn.left, rectColumn.bottom -1, NULL);
#else
		MoveTo(hdc, rectColumn.left, rectColumn.bottom - 1);
#endif
		LineTo(hdc, rectColumn.right, rectColumn.bottom - 1);
	}

	::SelectObject(hdc, pOldPen);
	VERIFY(::DeleteObject(pen));
}

void CRDFOutliner::AcceptDrop( int iLineNo, COleDataObject *pDataObject, DROPEFFECT dropEffect )
{
	HT_Resource theNode = NULL;
	if (iLineNo >= 0)
		theNode = HT_GetNthItem(m_View, iLineNo);
	if (theNode == NULL)
		theNode = HT_TopNode(m_View);

	RDFGLOBAL_PerformDrop(pDataObject, theNode, m_iDragSelectionLineThird);

	m_SpringloadStack.RemoveAll();
}

void RDFGLOBAL_PerformDrop(COleDataObject* pDataObject, HT_Resource theNode, int dragFraction)
{
    if(!pDataObject)
        return;

	CLIPFORMAT cfHTNode = (CLIPFORMAT)RegisterClipboardFormat(NETSCAPE_HTNODE_FORMAT);
	CLIPFORMAT cfBookmark = (CLIPFORMAT)RegisterClipboardFormat(NETSCAPE_BOOKMARK_FORMAT);
	
    if(!pDataObject->IsDataAvailable(CF_TEXT) &&
	   !pDataObject->IsDataAvailable(cfHTNode) &&
	   !pDataObject->IsDataAvailable(cfBookmark) &&
	   !pDataObject->IsDataAvailable(CF_HDROP))
	   return;

    if(pDataObject->IsDataAvailable(cfHTNode)) 
	{
		HT_View pView = NULL;

        HGLOBAL hBookmark = pDataObject->GetGlobalData(cfHTNode);
        HT_View* pBookmark = (HT_View*)GlobalLock(hBookmark);
        ASSERT(pBookmark);
		pView = *pBookmark;
		GlobalUnlock(hBookmark);

		DropPosition dropPosition = RDFGLOBAL_TranslateDropPosition(theNode, dragFraction);

		HT_Resource node = NULL;
		BOOL nothingSelected = TRUE;
		while (node = (HT_GetNextSelection(pView, node)))
		{
			nothingSelected = FALSE;
			if (dropPosition == DROP_BEFORE)
				HT_DropHTRAtPos(theNode, node, (PRBool)TRUE);
			else if (dropPosition == DROP_AFTER)
				HT_DropHTRAtPos(theNode, node, (PRBool)FALSE);
			else if (dropPosition == DROP_ON)
				HT_DropHTROn(theNode, node);
			else HT_DropHTROn(HT_GetParent(theNode), node);
		}

		if (nothingSelected)
		{
			// Use the top node of the view.
			HT_Resource node = HT_TopNode(pView);
			if (dropPosition == DROP_BEFORE)
				HT_DropHTRAtPos(theNode, node, (PRBool)TRUE);
			else if (dropPosition == DROP_AFTER)
				HT_DropHTRAtPos(theNode, node, (PRBool)FALSE);
			else if (dropPosition == DROP_ON)
				HT_DropHTROn(theNode, node);
		}
	}
	else if(pDataObject->IsDataAvailable(cfBookmark)) 
	{
        HGLOBAL hBookmark = pDataObject->GetGlobalData(cfBookmark);
        LPBOOKMARKITEM pBookmark = (LPBOOKMARKITEM)GlobalLock(hBookmark);
        ASSERT(pBookmark);
		CString title(pBookmark->szText);
		CString url(pBookmark->szAnchor);

		GlobalUnlock(hBookmark);

		DropPosition dropPosition = RDFGLOBAL_TranslateDropPosition(theNode, dragFraction);
		if (dropPosition == DROP_BEFORE)
			HT_DropURLAndTitleAtPos(theNode, (char*)(const char*)url, (char*)(const char*)title, (PRBool)TRUE);
		else if (dropPosition == DROP_AFTER)
			HT_DropURLAndTitleAtPos(theNode, (char*)(const char*)url, (char*)(const char*)title, (PRBool)FALSE);
		else if (dropPosition == DROP_ON)
			HT_DropURLAndTitleOn(theNode, (char*)(const char*)url, (char*)(const char*)title);
		else HT_DropURLAndTitleOn(HT_GetParent(theNode), (char*)(const char*)url, (char*)(const char*)title);
    }
	else if (pDataObject->IsDataAvailable(CF_HDROP))
	{
		HGLOBAL hGlobal = pDataObject->GetGlobalData(CF_HDROP);
        HDROP hDropInfo = (HDROP)hGlobal;
		
		// Get the number of files dropped
		int i_num = ::DragQueryFile(hDropInfo, (UINT)-1, NULL, 0);

		char ca_drop[_MAX_PATH];
		CString csUnescapedUrl, csEscapedUrl;

		int i_loop;
		for(i_loop = 0; i_loop < i_num; i_loop++)  
		{
			::DragQueryFile(hDropInfo, i_loop, ca_drop, _MAX_PATH);

			//  Convert to an acceptable URL.
			WFE_ConvertFile2Url(csUnescapedUrl, ca_drop);
			csEscapedUrl = WFE_EscapeURL(csUnescapedUrl);

			// Tell HT about the drop
			DropPosition dropPosition = RDFGLOBAL_TranslateDropPosition(theNode, dragFraction);
			if (dropPosition == DROP_BEFORE)
				HT_DropURLAtPos(theNode, (char*)(const char*)csEscapedUrl, (PRBool)TRUE);
			else if (dropPosition == DROP_AFTER)
				HT_DropURLAtPos(theNode, (char*)(const char*)csEscapedUrl, (PRBool)FALSE);
			else if (dropPosition == DROP_ON)
				HT_DropURLOn(theNode, (char*)(const char*)csEscapedUrl);
			else HT_DropURLOn(HT_GetParent(theNode), (char*)(const char*)csEscapedUrl);
		}
		GlobalUnlock(hGlobal);
	}
    else if (pDataObject->IsDataAvailable(CF_TEXT))
	{
        HGLOBAL hString = pDataObject->GetGlobalData(CF_TEXT);
        char * pAddress = (char*)GlobalLock(hString);
        CString url(pAddress);
		GlobalUnlock(hString);

		DropPosition dropPosition = RDFGLOBAL_TranslateDropPosition(theNode, dragFraction);
		if (dropPosition == DROP_BEFORE)
			HT_DropURLAtPos(theNode, (char*)(const char*)url, (PRBool)TRUE);
		else if (dropPosition == DROP_AFTER)
			HT_DropURLAtPos(theNode, (char*)(const char*)url, (PRBool)FALSE);
		else if (dropPosition == DROP_ON)
			HT_DropURLOn(theNode, (char*)(const char*)url);
		else HT_DropURLOn(HT_GetParent(theNode), (char*)(const char*)url);
	}
	
}

void CRDFOutliner::InitializeClipFormats(void)
{
    m_cfHTNode = (CLIPFORMAT)RegisterClipboardFormat(NETSCAPE_HTNODE_FORMAT);
	m_cfBookmark = (CLIPFORMAT)RegisterClipboardFormat(NETSCAPE_BOOKMARK_FORMAT);
    
	m_clipFormatArray[0] = m_cfHTNode;
	m_clipFormatArray[1] = m_cfBookmark;
	m_clipFormatArray[2] = CF_HDROP;
	m_clipFormatArray[3] = CF_TEXT;
	m_clipFormatArray[4] = 0;
}

CLIPFORMAT * CRDFOutliner::GetClipFormatList(void)
{
    return m_clipFormatArray;
}

DROPEFFECT CRDFOutliner::DropSelect(int iLineNo, COleDataObject *object)
{
	HT_Resource r = NULL;
	if (iLineNo >= 0)
		r = HT_GetNthItem(m_View, iLineNo);
	if (r == NULL)
		r = HT_TopNode(m_View);

	DROPEFFECT answer = RDFGLOBAL_TranslateDropAction(r, object, 
		HT_IsDropTarget(r) ? m_iDragSelectionLineThird : m_iDragSelectionLineHalf);
	
	m_iCurrentDropAction = answer;

    if (iLineNo == m_iDragSelection && !m_bDragSectionChanged)
        return answer;

    if (m_iDragSelection != -1)
        InvalidateDragLine (m_iDragSelection, HT_IsDropTarget(r),
							HT_IsDropTarget(r) ? m_iOldLineThird : m_iOldLineHalf);
	
    m_iDragSelection = iLineNo;
    InvalidateDragLine (m_iDragSelection, HT_IsDropTarget(r), 
						HT_IsDropTarget(r) ? m_iDragSelectionLineThird : m_iDragSelectionLineHalf);
    
	// Start the hover timer for folder springloading
	
	if (m_iDragSelection != -1)
	{
		if (r != NULL && HT_IsDropTarget(r) && !HT_IsContainerOpen(r) && m_iCurrentDropAction != DROPEFFECT_NONE)
		{
	 		m_iSpringloadSelection = m_iDragSelection;
			m_hSpringloadTimer = SetTimer(IDT_SPRINGLOAD, SPRINGLOAD_DELAY, NULL);
		}
	}

	// Check for springload closing time
	if (!m_SpringloadStack.IsEmpty())
	{
		// There are some springloaded folders that might need to be closed.
		POSITION p = m_SpringloadStack.GetHeadPosition();
		HT_Resource r = (HT_Resource)m_SpringloadStack.GetNext(p);

		int count = (int)HT_GetCountVisibleChildren(r);
		int index = (int)HT_GetNodeIndex(m_View, r);
		
		if (m_iDragSelection < index ||
			m_iDragSelection == -1 ||
			m_iDragSelection > index + count)
		{
			m_SpringloadStack.RemoveHead();
			HT_SetOpenState(r, (PRBool)FALSE);
		
			if (m_iDragSelection > index + count)
				m_iDragSelection -= count;
		}
	}
	return answer;
}

DropPosition RDFGLOBAL_TranslateDropPosition(HT_Resource dropTarget, int position)
{
	HT_Resource targetParent = HT_GetParent(dropTarget);
	BOOL sortImposed = FALSE;
	if (targetParent != NULL)
		sortImposed = !HT_ContainerSupportsNaturalOrderSort(targetParent); // Is a sort imposed on the parent?
	
	DropPosition res;
	if (!sortImposed) // Can only drop/before or after if sort is not imposed on the view.
	{
		if (position == 1)
		{
			// Drop before
			res = DROP_BEFORE; // In upper half of selection (for non-containers) or bottom third 
							   // of selection (for containers).  Drop before.
		}
		else if (position == 3 || (position == 2 && !HT_IsDropTarget(dropTarget)))
		{
			// Drop after if we're in the bottom half (for non-containers) or bottom third (for containers)
			res = DROP_AFTER;  // In lower third of selection.  Drop after.
		}
		else res = DROP_ON;  // We're a container and right in the middle of selection.  Drop on.
	}
	else if (HT_IsDropTarget(dropTarget))
	{
		res = DROP_ON; // If a sort is imposed and we're over a container, drop into that.
	}
	else res = DROP_ON_PARENT;  // Otherwise, we'll drop into the parent container and let the sort
								// put it where it's supposed to go.

	return res;
}

DROPEFFECT RDFGLOBAL_TranslateDropAction(HT_Resource dropTarget, COleDataObject* pDataObject, 
										 int position)
{
	HT_DropAction res;
	CLIPFORMAT cfHTNode = (CLIPFORMAT)RegisterClipboardFormat(NETSCAPE_HTNODE_FORMAT);
	CLIPFORMAT cfBookmark = (CLIPFORMAT)RegisterClipboardFormat(NETSCAPE_BOOKMARK_FORMAT);

	if (dropTarget == NULL)
		return DROPEFFECT_NONE;

	DropPosition dropPosition = RDFGLOBAL_TranslateDropPosition(dropTarget, position);
		
	if(pDataObject->IsDataAvailable(cfHTNode)) 
	{
		HT_View pView = NULL;

        HGLOBAL hBookmark = pDataObject->GetGlobalData(cfHTNode);
        HT_View* pBookmark = (HT_View*)GlobalLock(hBookmark);
        ASSERT(pBookmark);
		pView = *pBookmark;
		GlobalUnlock(hBookmark);

		HT_Resource node = HT_GetNextSelection(pView, NULL); 
		BOOL nothingSelected = TRUE;
		while (node != NULL)
		{
			nothingSelected = FALSE;
			HT_DropAction res2;
			if (dropPosition == DROP_BEFORE)
				res2 = HT_CanDropHTRAtPos(dropTarget, node, (PRBool)TRUE); 
			else if (dropPosition == DROP_AFTER)
				res2 = HT_CanDropHTRAtPos(dropTarget, node, (PRBool)FALSE);
			else if (dropPosition == DROP_ON)
				res2 = HT_CanDropHTROn(dropTarget, node);
			else res2 = HT_CanDropHTROn(HT_GetParent(dropTarget), node);

			if (res2 == DROP_NOT_ALLOWED)
			{
				res = DROP_NOT_ALLOWED;
				break;
			}
			
			res = res2;

			node = HT_GetNextSelection(pView, node);
		}

		if (nothingSelected)
		{
			// Use the top node of the view.
			HT_Resource node = HT_TopNode(pView);
			// Should only enter this code when buttons from the selector bar are being dragged.
			if (dropPosition == DROP_BEFORE)
				res = HT_CanDropHTRAtPos(dropTarget, node, (PRBool)TRUE); 
			else if (dropPosition == DROP_AFTER)
				res = HT_CanDropHTRAtPos(dropTarget, node, (PRBool)FALSE);
			else if (dropPosition == DROP_ON)
				res = HT_CanDropHTROn(dropTarget, node);
			else res = HT_CanDropHTROn(HT_GetParent(dropTarget), node);
		}
	}
	else if (pDataObject->IsDataAvailable(cfBookmark))
	{
		HGLOBAL hBookmark = pDataObject->GetGlobalData(cfBookmark);
        LPBOOKMARKITEM pBookmark = (LPBOOKMARKITEM)GlobalLock(hBookmark);
        ASSERT(pBookmark);
		CString title(pBookmark->szText);
		CString url(pBookmark->szAnchor);

		GlobalUnlock(hBookmark);
		
		if (dropPosition == DROP_BEFORE)
		  res = HT_CanDropURLAtPos(dropTarget, (char*)(const char*)url, (PRBool)TRUE); 
		else if (dropPosition == DROP_AFTER)
		  res = HT_CanDropURLAtPos(dropTarget, (char*)(const char*)url, (PRBool)FALSE);
		else if (dropPosition == DROP_ON)
		  res = HT_CanDropURLOn(dropTarget, (char*)(const char*)url);
		else res = HT_CanDropURLOn(HT_GetParent(dropTarget), (char*)(const char*)url);
	}
	else if (pDataObject->IsDataAvailable(CF_HDROP))
	{
		HGLOBAL hGlobal = pDataObject->GetGlobalData(CF_HDROP);
        HDROP hDropInfo = (HDROP)hGlobal;
		
		// Get the number of files dropped
		int i_num = ::DragQueryFile(hDropInfo, (UINT)-1, NULL, 0);

		char ca_drop[_MAX_PATH];
		CString csUnescapedUrl, csEscapedUrl;

		int i_loop;
		HT_DropAction res2;
		for(i_loop = 0; i_loop < i_num; i_loop++)  
		{
			::DragQueryFile(hDropInfo, i_loop, ca_drop, _MAX_PATH);

			//  Convert to an acceptable URL.
			WFE_ConvertFile2Url(csUnescapedUrl, ca_drop);
			csEscapedUrl = WFE_EscapeURL(csUnescapedUrl);

			// Tell HT about the drop
			if (dropPosition == DROP_BEFORE)
				res2 = HT_CanDropURLAtPos(dropTarget, (char*)(const char*)csEscapedUrl, (PRBool)TRUE);
			else if (dropPosition == DROP_AFTER)
				res2 = HT_CanDropURLAtPos(dropTarget, (char*)(const char*)csEscapedUrl, (PRBool)FALSE);
			else if (dropPosition == DROP_ON)
				res2 = HT_CanDropURLOn(dropTarget, (char*)(const char*)csEscapedUrl);
			else res2 = HT_CanDropURLOn(HT_GetParent(dropTarget), (char*)(const char*)csEscapedUrl);

			if (res2 == DROP_NOT_ALLOWED)
			{
				res = DROP_NOT_ALLOWED;
				break;
			}
			
			res = res2;
		}
		GlobalUnlock(hGlobal);
	}
	else if (pDataObject->IsDataAvailable(CF_TEXT))
	{
        HGLOBAL hString = pDataObject->GetGlobalData(CF_TEXT);
        char * pAddress = (char*)GlobalLock(hString);
        CString url(pAddress);
		GlobalUnlock(hString);

		if (dropPosition == DROP_BEFORE)
		  res = HT_CanDropURLAtPos(dropTarget, (char*)(const char*)url, (PRBool)TRUE); 
		else if (dropPosition == DROP_AFTER)
		  res = HT_CanDropURLAtPos(dropTarget, (char*)(const char*)url, (PRBool)FALSE);
		else if (dropPosition == DROP_ON)
		  res = HT_CanDropURLOn(dropTarget, (char*)(const char*)url);
		else res = HT_CanDropURLOn(HT_GetParent(dropTarget), (char*)(const char*)url);
	}

	switch (res)
	{
		case DROP_NOT_ALLOWED:
			return DROPEFFECT_NONE;
		case COPY_MOVE_CONTENT:
		case COPY_MOVE_LINK:
			return DROPEFFECT_MOVE;
		default:
			return DROPEFFECT_COPY;
	}
}

// RDFDropTarget Overridden Funcs

BOOL CRDFDropTarget::OnDrop(
    CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point)
{
	m_dwOldTicks = 0;

	CRDFOutliner* pOutliner = (CRDFOutliner*)m_pOutliner;

    if(!pDataObject || !pOutliner)
        return(FALSE);

    if (!pOutliner->RecognizedFormat(pDataObject))
        return FALSE;

	pOutliner->m_ptHit = point;

    pOutliner->AcceptDrop( point.y < 0 ? -1 : pOutliner->GetDropLine(), pDataObject, dropEffect);
    pOutliner->EndDropSelect();

    return(TRUE);
}

DROPEFFECT CRDFDropTarget::OnDragEnter(
    CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point)
{
	DROPEFFECT res = OnDragOver(pWnd, pDataObject, dwKeyState, point);

	CRDFOutliner* pOutliner = (CRDFOutliner*)m_pOutliner;
	pOutliner->m_bDataSourceInWindow = TRUE;

	if ( res != DROPEFFECT_NONE && point.y >= 0) {
		m_pOutliner->Invalidate();
		m_pOutliner->UpdateWindow();
	}

	return res;
}

DROPEFFECT CRDFDropTarget::OnDragOver(
    CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point)
{
    DROPEFFECT effect = DROPEFFECT_NONE;
	CRDFOutliner* pOutliner = (CRDFOutliner*)m_pOutliner;

    if (pOutliner->RecognizedFormat(pDataObject)) {
		RECT rect;
		pWnd->GetClientRect(&rect);

		if ( point.y < pOutliner->m_itemHeight && point.y >= 0) {
			DragScroll(TRUE);
			effect |= DROPEFFECT_SCROLL;
		} else if ( point.y > ( rect.bottom - pOutliner->m_itemHeight ) ) {
			DragScroll(FALSE);
			effect |= DROPEFFECT_SCROLL;
		} else {
			m_dwOldTicks = 0;
		}

		pOutliner->m_ptHit = point;

		effect |= pOutliner->DropSelect(point.y < 0 ? -1 : pOutliner->LineFromPoint(point), pDataObject);

		// Default is to move
		if ( ( effect & DROPEFFECT_MOVE) && (dwKeyState & MK_CONTROL) )
			effect = (effect & ~DROPEFFECT_MOVE) | DROPEFFECT_COPY;
	}
    return effect;
}

void CRDFDropTarget::OnDragLeave(CWnd* pWnd)
{
	CRDFOutliner* pOutliner = (CRDFOutliner*)m_pOutliner;
	for (POSITION p = pOutliner->m_SpringloadStack.GetHeadPosition(); p != NULL; )
	{
		HT_Resource r = (HT_Resource)(pOutliner->m_SpringloadStack.GetNext(p));
		HT_SetOpenState(r, (PRBool)FALSE);
	}
			 
	pOutliner->m_SpringloadStack.RemoveAll();

	pOutliner->m_bDataSourceInWindow = FALSE;

	pOutliner->m_iDragSelection = -1;

	pOutliner->Invalidate();
	pOutliner->UpdateWindow();
}

// ========================================================================

BEGIN_MESSAGE_MAP(CRDFOutlinerParent,COutlinerParent)
    ON_WM_DESTROY()
	ON_WM_PAINT ( )
END_MESSAGE_MAP()

CRDFOutlinerParent::CRDFOutlinerParent(HT_Pane thePane, HT_View theView)
{
	CRDFOutliner* theOutliner = new CRDFOutliner(this, thePane, theView);
	m_pOutliner = theOutliner;
}

BOOL CRDFOutlinerParent::PreCreateWindow(CREATESTRUCT& cs)
{
    BOOL bRet = CWnd::PreCreateWindow(cs);

//#ifdef XP_WIN32
//    cs.dwExStyle |= WS_EX_CLIENTEDGE;
//#endif
    return bRet;
}

void CRDFOutlinerParent::OnDestroy()
{
	// Save prefs might happen here...
	COutlinerParent::OnDestroy();
}

COutliner* CRDFOutlinerParent::GetOutliner ( )
{
	return m_pOutliner;
}

void CRDFOutlinerParent::OnPaint ( )
{                            
    CPaintDC pdc ( this );

    if ( !m_pOutliner || m_bDisableHeaders )
        return;

	// Read in our values.
	CRDFOutliner* pRDFLiner = (CRDFOutliner*)m_pOutliner;
	HT_View view = pRDFLiner->GetHTView();
	if (view == NULL)
		return;

	HT_Resource top = HT_TopNode(view);

	// Foreground color
	void* data;
	HT_GetTemplateData(top, gNavCenter->columnHeaderFGColor, HT_COLUMN_STRING, &data);
	if (data)
		WFE_ParseColor((char*)data, &m_ForegroundColor);
	else m_ForegroundColor = RGB(0,0,0);

	// background color
	HT_GetTemplateData(top, gNavCenter->columnHeaderBGColor, HT_COLUMN_STRING, &data);
	if (data)
		WFE_ParseColor((char*)data, &m_BackgroundColor);
	else m_BackgroundColor = RGB(192,192,192);

	// Background image URL
	m_BackgroundImageURL = "";
	HT_GetTemplateData(top, gNavCenter->columnHeaderBGURL, HT_COLUMN_STRING, &data);
	if (data)
		m_BackgroundImageURL = (char*)data;
	m_pBackgroundImage = NULL; // Clear out the BG image.

	HPALETTE pOldPalette = NULL;
	if (sysInfo.m_iBitsPerPixel < 16 && (::GetDeviceCaps(pdc.m_hDC, RASTERCAPS) & RC_PALETTE))
	{
		// Use the palette, since we have less than 16 bits per pixel and are
		// using a palette-based device.
		HPALETTE hPalette = WFE_GetUIPalette(GetParentFrame());
		::SelectPalette(pdc.m_hDC, hPalette, FALSE);	

		// Find the nearest match in our palette for our colors.
		ResolveToPaletteColor(m_BackgroundColor, hPalette);
		ResolveToPaletteColor(m_ForegroundColor, hPalette);
	}


    int i, offset;

	// we might use these in the for() loop below --- make sure
	//  they stay in scope since we don't restore into the CDC until
	//  the end of the routine
    HFONT hOldFont = (HFONT) pdc.SelectObject ( m_hToolFont );
    COLORREF cOldText = pdc.SetTextColor(m_ForegroundColor);
    int nOldMode = pdc.SetBkMode(TRANSPARENT);
	CBrush bgBrush(m_BackgroundColor);
	CBrush fgBrush(m_ForegroundColor);

	CRect rectClient;
	GetClientRect ( &rectClient );

	// Do the background painting
	if (m_BackgroundImageURL != "")
	{
		// There's a background that needs to be drawn.
		m_pBackgroundImage = LookupImage(m_BackgroundImageURL, NULL);
	}

	BOOL shouldPaintBG = m_pBackgroundImage && m_pBackgroundImage->FrameSuccessfullyLoaded();
	if (shouldPaintBG)
	{
		PaintBackground(pdc, rectClient, m_pBackgroundImage);
	}

	int iMaxHeaderWidth = rectClient.right - m_iPusherWidth;

    for ( i = offset = 0; (i < (int)m_pOutliner->GetVisibleColumns()) && (offset < iMaxHeaderWidth); i++ )
    {
        BOOL bDep = m_pOutliner->m_pColumn[ i ]->bDepressed &&
					m_pOutliner->m_pColumn[ i ]->bIsButton;
		CRect rect( offset, 0, m_pOutliner->m_pColumn[ i ]->iCol + offset, m_iHeaderHeight );

		if (rect.right > iMaxHeaderWidth ) {
			rect.right = iMaxHeaderWidth;
		}

        CRect rcInter;
        if ( ::IntersectRect ( &rcInter, &pdc.m_ps.rcPaint, &rect ) )
        {
			CRect rcText = rect;
			
			if (!shouldPaintBG)
				::FillRect(pdc.m_hDC, &rcText, bgBrush );
			rcText.InflateRect(1, 0, 0, 0);
			::FrameRect(pdc.m_hDC, &rcText, fgBrush);

			DrawColumnHeader( pdc.m_hDC, rcText, i );
		}

        offset += m_pOutliner->m_pColumn[ i ]->iCol;
    }

	// Fill in the gap on the right

	if (offset < iMaxHeaderWidth) {
		RECT rect = {offset, 0, iMaxHeaderWidth, m_iHeaderHeight};
		CRect rcInter;

		if ( IntersectRect( &rcInter, &pdc.m_ps.rcPaint, &rect ) ) {
			CRect rcText = rect;
			if (!shouldPaintBG)
				::FillRect(pdc.m_hDC, &rcText, bgBrush );
			rcText.InflateRect(1, 0, 0, 0);
			::FrameRect(pdc.m_hDC, &rcText, fgBrush );
		}
	}

	CRect rect(rectClient.right - m_iPusherWidth, 0, rectClient.right, m_iHeaderHeight );
    CRect iRect;

    if ( iRect.IntersectRect ( &pdc.m_ps.rcPaint, &rect ) ) {
		int idxImage;
		rect.InflateRect(1,0,1,0);
		if (!shouldPaintBG)
			::FillRect ( pdc.m_hDC, &rect, bgBrush );
		::FrameRect( pdc.m_hDC, &rect, fgBrush );

		m_iPusherState = pusherNone;
		if ( m_pOutliner->m_iNumColumns > 1 ) {
			if (m_pOutliner->GetVisibleColumns() > 1) {
				m_iPusherState |= pusherRight;
			}
			if (int(m_pOutliner->GetVisibleColumns()) < m_pOutliner->m_iNumColumns) {
				m_iPusherState |= pusherLeft;
			}
		}

		CRect divider(rect.left, rect.top, rect.left + rect.Width()/2, rect.bottom);
		::FrameRect(pdc.m_hDC, &divider, fgBrush);

		POINT ptBitmap;
		RECT rect2 = rect;

		// Draw left pusher
		rect2.right = (rect.left + rect.right + 1) / 2;

		ptBitmap.x = (rect2.left + rect2.right + 1) / 2 - 4;
		ptBitmap.y = (rect2.top + rect2.bottom + 1) / 2 - 4;
		if ( m_iPusherRgn == pusherLeft ) {
			ptBitmap.x++;
			ptBitmap.y++;
		}
		idxImage = m_iPusherState & pusherLeft ? 
				   IDX_PUSHLEFT : IDX_PUSHLEFTI;
		m_pIImage->DrawTransImage( idxImage, ptBitmap.x, ptBitmap.y, &pdc);
		//DrawButtonRect( pdc.m_hDC, rect2, m_iPusherRgn == pusherLeft );
		
		// Draw right pusher
		rect2.left = rect2.right;
		rect2.right = rect.right;
		ptBitmap.x = (rect2.left + rect2.right + 1) / 2 - 4;
		ptBitmap.y = (rect2.top + rect2.bottom + 1) / 2 - 4;
		if ( m_iPusherRgn == pusherRight ) {
			ptBitmap.x++;
			ptBitmap.y++;
		}
 		idxImage = m_iPusherState & pusherRight ? 
				   IDX_PUSHRIGHT : IDX_PUSHRIGHTI;
		m_pIImage->DrawTransImage( idxImage, ptBitmap.x, ptBitmap.y, &pdc);
		//DrawButtonRect( pdc.m_hDC, rect2, m_iPusherRgn == pusherRight );
	}

    pdc.SelectObject ( hOldFont );
    pdc.SetTextColor ( cOldText );
    pdc.SetBkMode( nOldMode );

	if (m_bEnableFocusFrame)
	{
		HBRUSH hBrush = NULL;
		if (GetFocus() == m_pOutliner)
			hBrush = ::CreateSolidBrush( GetSysColor( COLOR_HIGHLIGHT ) );
		else
			hBrush = ::CreateSolidBrush( GetSysColor( COLOR_WINDOW ) );

		RECT clientRect;
		GetClientRect(&clientRect);
		::FrameRect( pdc.m_hDC, &clientRect, hBrush );	 
		VERIFY(DeleteObject( hBrush ));
	}

	if (sysInfo.m_iBitsPerPixel < 16 && (::GetDeviceCaps(pdc.m_hDC, RASTERCAPS) & RC_PALETTE))
	{
		::SelectPalette(pdc.m_hDC, pOldPalette, FALSE);
	}

}

void CRDFOutlinerParent::CreateColumns ( void )
{
	// Retrieve the columns from RDF
	CRDFOutliner* theOutliner = (CRDFOutliner*)m_pOutliner;
	HT_View theView = theOutliner->GetHTView();
	
	HT_Cursor columnCursor = HT_NewColumnCursor(theView);
	char* columnName;
	uint32 columnWidth;
	void* token;
	uint32 tokenType;
	UINT index = 0;

	while (HT_GetNextColumn(columnCursor, &columnName, &columnWidth, &token, &tokenType))
	{
		// We have retrieved a new column.  Contruct a front end column object
		CRDFColumn* newColumn = new CRDFColumn(columnName, columnWidth, token, tokenType);
		index = (UINT)columnMap.AddCommand(newColumn);
		m_pOutliner->AddColumn(columnName, index, 50, 10000, ColumnVariable, 100);
	}
	HT_DeleteColumnCursor(columnCursor);
	m_pOutliner->SetVisibleColumns(1); // For now... TODO: Get visible/invisible info!
	m_pOutliner->SetImageColumn(m_pOutliner->m_pColumn[0]->iCommand);

	// Make it so
	RECT rcClient;
	m_pOutliner->GetClientRect(&rcClient);
	((CRDFOutliner*)m_pOutliner)->OnSize(0, rcClient.right, rcClient.bottom);

	Invalidate();

	theOutliner->ToggleModes();
}

BOOL CRDFOutlinerParent::RenderData( int iColumn, CRect & rect, CDC &dc, LPCTSTR text )
{
	CRDFOutliner *pOutliner = (CRDFOutliner *)m_pOutliner;
    
    if( !pOutliner )
    {
       return FALSE;
    }
     
	if( pOutliner->GetSortType() == HT_NO_SORT)
    {
       return FALSE;
    }
    
	MSG_COMMAND_CHECK_STATE sortType = (iColumn == pOutliner->GetSortColumn()) ? MSG_Checked : MSG_Unchecked;
    
    if( sortType != MSG_Checked )
    {
       return FALSE;
    }

	int idxImage = (pOutliner->GetSortType() % 2) ? IDX_SORTINDICATORUP : IDX_SORTINDICATORDOWN;

	UINT dwDTFormat = DT_NOCLIP | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER;
	RECT rectText = rect;
	rectText.left += 4;
	rectText.right -= 5;

	rectText.right -= 22;
    m_pIImage->DrawTransImage( idxImage,
							   rectText.right + 4,
							   (rect.top + rect.bottom) / 2 - 4,
							   &dc );

	WFE_DrawTextEx( CS_UTF8, dc.m_hDC, (LPTSTR) text, -1, 
					&rectText, dwDTFormat, WFE_DT_CROPRIGHT );

    return TRUE;
}

BOOL CRDFOutlinerParent::ColumnCommand( int iColumn )
{	
	CRDFOutliner *pOutliner = (CRDFOutliner *)m_pOutliner;
    
	int enSortType = pOutliner->GetSortType();
	if (pOutliner->GetSortColumn() != iColumn)
	{
		// May need to resort in back end (not sure)
		enSortType = HT_NO_SORT;
	}

    if (enSortType == HT_SORT_ASCENDING)
		enSortType = HT_SORT_DESCENDING;
	else if (enSortType == HT_SORT_DESCENDING)
		enSortType = HT_NO_SORT;
	else enSortType = HT_SORT_ASCENDING;

    pOutliner->SetSortType( enSortType );
	
	if (enSortType == HT_NO_SORT)
		pOutliner->SetSortColumn(-1);
	else pOutliner->SetSortColumn( iColumn );
     
	CRDFColumn* theColumn = (CRDFColumn*)(columnMap.GetCommand(iColumn));
		
	BOOL sort = TRUE;
	if (enSortType == HT_SORT_ASCENDING)
		sort = FALSE;

	pOutliner->SetSelectedColumn(iColumn);

	HT_SetSortColumn(pOutliner->GetHTView(), 
		             enSortType == HT_NO_SORT ? NULL : theColumn->GetToken(), 
					 theColumn->GetDataType(), (PRBool)sort);
    
	return TRUE;
}

void CRDFOutlinerParent::Initialize()
{
	CreateColumns ( );

	CRDFOutliner* theOutliner = (CRDFOutliner*)m_pOutliner;
	m_pOutliner->SetTotalLines( HT_GetItemListCount(theOutliner->GetHTView()) );
}

// =======================================================================
// RDFEditWnd - Class that controls the in-place editing of column values
// =======================================================================

BOOL CRDFEditWnd::PreTranslateMessage ( MSG * msg )
{
   if ( msg->message == WM_KEYDOWN)
   {
      switch(msg->wParam)
      {
         case VK_RETURN:
         {
			m_pOwner->EditTextChanged(m_pOwner->GetTextEditText());
			m_pOwner->RemoveTextEdit();
            return TRUE;

         }
		 case VK_ESCAPE:
			 m_pOwner->RemoveTextEdit();
			 break;
		 case VK_TAB:
			 m_pOwner->EditTextChanged(m_pOwner->GetTextEditText());
			 m_pOwner->MoveToNextColumn();
			 return TRUE;
	  }
   }
   
   return CEdit::PreTranslateMessage ( msg );
}

//////////////////////////////////////////////////////////////////////////
//					Messages for CRDFEditWnd
//////////////////////////////////////////////////////////////////////////

BEGIN_MESSAGE_MAP(CRDFEditWnd, CEdit)
	//{{AFX_MSG_MAP(CWnd)
 	ON_WM_DESTROY()
	ON_WM_SETFOCUS()
	ON_WM_KILLFOCUS()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

void CRDFEditWnd::OnDestroy( )
{
	delete this;

}
  
void CRDFEditWnd::OnSetFocus(CWnd* pOldWnd)
{
	CEdit::OnSetFocus(pOldWnd);
}

void CRDFEditWnd::OnKillFocus( CWnd* pNewWnd )
{
	CEdit::OnKillFocus(pNewWnd);
	m_pOwner->FocusCheck(pNewWnd, FALSE);
	m_pOwner->RemoveTextEdit(); 
}

// =========================================================================
// RDF Content View
IMPLEMENT_DYNAMIC(CRDFContentView, CView)
BEGIN_MESSAGE_MAP(CRDFContentView, CView)
	//{{AFX_MSG_MAP(CMainFrame)
		// NOTE - the ClassWizard will add and remove mapping macros here.
		//    DO NOT EDIT what you see in these blocks of generated code !
	ON_WM_CREATE()
    ON_WM_SIZE()
    ON_WM_SETFOCUS()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

CRDFContentView::CRDFContentView()
{ m_pOutlinerParent = NULL; m_pHTMLView = NULL; m_pNavBar = NULL; }

int CRDFContentView::OnCreate ( LPCREATESTRUCT lpCreateStruct )
{
    int iRetVal = CView::OnCreate ( lpCreateStruct );
	LPCTSTR lpszClass = AfxRegisterWndClass( CS_VREDRAW, ::LoadCursor(NULL, IDC_ARROW));

	m_pNavBar = new CNavTitleBar();

	m_pOutlinerParent = new CRDFOutlinerParent();
    m_pOutlinerParent->Create( lpszClass, _T("NSOutlinerParent"), 
							   WS_VISIBLE|WS_CHILD|WS_CLIPCHILDREN,
							   CRect(0,0,100,100), this, 101 );

	m_pHTMLView = NULL; //wfe_CreateNavCenterHTMLPain(m_hWnd);

	m_pNavBar->Create(NULL, "", WS_CHILD | WS_VISIBLE, CRect(0,0,100,100), this, NC_IDW_NAVMENU);
	
    return iRetVal;
}

CRDFContentView::~CRDFContentView()
{
	CRDFOutliner* pOutliner = (CRDFOutliner*)(GetOutlinerParent()->GetOutliner());
	HT_View v = pOutliner->GetHTView();
	HT_SetViewFEData(v, NULL);

	XP_UnregisterNavCenter(HT_GetPane(v));

	HT_DeletePane(HT_GetPane(v));

	delete m_pNavBar;
	delete m_pOutlinerParent;
	delete m_pHTMLView;
}

void CRDFContentView::OnSize ( UINT nType, int cx, int cy )
{
	CView::OnSize ( nType, cx, cy );
	if (IsWindow(m_pOutlinerParent->m_hWnd)) 
	{
		m_pNavBar->MoveWindow(0,0, cx, NAVBAR_TOTAL_HEIGHT);
		m_pOutlinerParent->MoveWindow ( 0, NAVBAR_TOTAL_HEIGHT, cx, cy-NAVBAR_TOTAL_HEIGHT);
	}
}

void CRDFContentView::OnSetFocus ( CWnd * pOldWnd )
{
   m_pOutlinerParent->SetFocus ( );
}

void CRDFContentView::SwitchHTViews(HT_View newView)
{
	m_pOutlinerParent->SetHTView(newView);
	m_pOutlinerParent->GetOutliner()->DestroyColumns();
	CreateColumns();
}

void CRDFContentView::OnKillFocus(CWnd* pNewWnd)
{
}

// Functionality for the RDF Tree Embedded in HTML (Added 3/10/98 by Dave Hyatt).

// The event handler.  Only cares about tree events, so we'll just pass everything to the
// tree view.
void embeddedTreeNotifyProcedure (HT_Notification ns, HT_Resource n, HT_Event whatHappened,
					void *token, uint32 tokenType) 
{
	CRDFOutliner* theOutliner = (CRDFOutliner*)HT_GetViewFEData(HT_GetView(n));
	if (theOutliner)
		theOutliner->HandleEvent(ns, n, whatHappened);
}

CRDFContentView* CRDFContentView::DisplayRDFTreeFromPane(CWnd* pParent, int xPos, int yPos, 
	int width, int height, HT_Pane thePane, CCreateContext* pContext)
{
	// Create the windows
	CRect rClient(xPos, yPos, width, height);
	
	CRDFContentView* newView = new CRDFContentView();

	newView->Create(NULL, "", WS_CHILD | WS_VISIBLE, rClient, pParent, NC_IDW_OUTLINER, pContext);

	// Get the parent
	COutlinerParent* newParent = newView->GetOutlinerParent();
	((CRDFOutlinerParent*)newParent)->SetHTView(HT_GetSelectedView(thePane));

	// Initialize the columns, etc.
	((CRDFOutlinerParent*)newParent)->Initialize();

	// Retrieve the RDF Outliner.
	CRDFOutliner* pOutliner = (CRDFOutliner*)newParent->GetOutliner();
	
	// Set our FE data to be the outliner.
	HT_SetViewFEData(HT_GetSelectedView(thePane), pOutliner);
    
	// Explain who the title bar belongs to.
	CNavTitleBar* pTitleBar = newView->GetTitleBar();
	pTitleBar->SetHTView(HT_GetSelectedView(thePane));
	pOutliner->SetTitleBar(pTitleBar);

	return newView;
}

CRDFContentView* CRDFContentView::DisplayRDFTreeFromResource(CWnd* pParent, 
	int xPos, int yPos, int width, int height, HT_Resource node, CCreateContext* pContext)
{
	HT_Notification ns = new HT_NotificationStruct;
	XP_BZERO(ns, sizeof(HT_NotificationStruct));
	ns->notifyProc = embeddedTreeNotifyProcedure;
	ns->data = NULL;
	theApp.m_pRDFCX->TrackRDFWindow(pParent);

	HT_Pane thePane = HT_PaneFromResource(HT_GetRDFResource(node), ns, PR_FALSE, PR_TRUE, PR_TRUE);

	// Now call our helper function
	return DisplayRDFTreeFromPane(pParent, xPos, yPos, width, height, thePane, pContext);
}

CRDFContentView* CRDFContentView::DisplayRDFTreeFromSHACK(MWContext *pContext, CWnd* pParent, int xPos, int yPos, int width, int height, char* url, int32 param_count, char** param_names, char** param_values)
{
	HT_Notification ns = new HT_NotificationStruct;
	XP_BZERO(ns, sizeof(HT_NotificationStruct));
	ns->notifyProc = embeddedTreeNotifyProcedure;
	ns->data = NULL;
	theApp.m_pRDFCX->TrackRDFWindow(pParent);
	
	// Construct the pane and give it our notification struct
	HT_Pane thePane = HT_PaneFromURL(pContext, url, ns, 0, param_count, param_names, param_values);  
	
	// Now call our helper function
	return DisplayRDFTreeFromPane(pParent, xPos, yPos, width, height, thePane);
}

// Function to grab an RDF context

extern "C" MWContext* FE_GetRDFContext(void) 
{
    MWContext *pRetval = NULL;
    
    if(theApp.m_pRDFCX) 
	{
        pRetval = theApp.m_pRDFCX->GetContext();
    }
    
    return(pRetval);
}


// The context's GetDialogOwner() function.
CWnd* CRDFCX::GetDialogOwner() const
{
	if (m_pCurrentRDFWindow == NULL || m_pCurrentRDFWindow->m_hWnd == NULL)
		return CWnd::GetDesktopWindow();
	return m_pCurrentRDFWindow->GetTopLevelFrame();
}

// ==================================================================
// HTFEDATA Functions (found in rdfacc.h)
// ==================================================================

void CHTFEData::FlushIconInfo()
{ 
	// Just remove the HICONs from the local file cache.
	m_LocalFileCache.RemoveAll();  
	
	// Need to iterate over all the elements in the custom URL cache and destroy the images.
	POSITION pos = m_CustomURLCache.GetStartPosition();
	void* pData;
	CString key;
	while (pos != NULL)
	{
		m_CustomURLCache.GetNextAssoc(pos, key, pData);
		CRDFImage* pImage = (CRDFImage*)pData;
		delete pImage;
	}
}


/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// CDialogPRMT dialog

void stdPrint (void* data, char* str) {
  fprintf((FILE*)data, str);
}

CExportRDF::CExportRDF(CWnd* pParent /*=NULL*/)
    : CDialog(CExportRDF::IDD, pParent)
{
    m_csTitle = _T("");
    
    //{{AFX_DATA_INIT(CDialogPRMT)
    m_csAns = "";
    //}}AFX_DATA_INIT
}

void CExportRDF::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
    //{{AFX_DATA_MAP(CDialogPRMT)
    DDX_Text(pDX, IDC_PROMPT_ANS, m_csAns);
    //}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CExportRDF, CDialog)
    //{{AFX_MSG_MAP(CDialogPRMT)
        // NOTE: the ClassWizard will add message map macros here
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()
