gecko-dev/cmd/winfe/rdfliner.cpp

4360 строки
117 KiB
C++

/* -*- 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"
#include "csid.h"
#include "libi18n.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), m_bTemporarilyRetainPopup(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 isToolbarIcon, HT_IconType state)
{
CRDFImage* pImage = FetchCustomIcon(r, pObject, isToolbarIcon, state);
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, 0);
}
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 isToolbarIcon,
HT_IconType state)
{
CRDFImage* pImage = NULL;
CString hashString = HT_GetIconURL(r, isToolbarIcon, state);
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 isToolbar, HT_IconType state)
{
IconType returnValue;
if (pNode != NULL)
{
char* pURL = HT_GetIconURL(pNode, isToolbar, state);
//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* pParent = (CRDFOutlinerParent*)GetParent();
pParent->EnableHeaders(show);
// Need to invalidate the title strip and figure out exactly how tall it is.
CRDFContentView* pView = (CRDFContentView*)(GetParent()->GetParent());
CRect parentRect;
pParent->GetClientRect(&parentRect);
int titleHeight = pView->GetTitleBar()->GetHeightBasedOnProperties();
pView->GetTitleBar()->MoveWindow(CRect(0, 0, parentRect.Width(), titleHeight));
pParent->MoveWindow(CRect(0, titleHeight, parentRect.Width(), parentRect.Height() - titleHeight));
}
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. (Disable for bookmarks until we have a concrete plan.)
if (IsLocalFile(url) && HT_GetParent(m_Node) != NULL
&& IsLocalFile(HT_GetNodeURL(HT_GetParent(m_Node))))
{
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);
SetTemporaryRetainOnPopup(FALSE);
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() && !ShouldRetainPopup())
{
// 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;
// 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->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, HT_SMALLICON);
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)
{
// we need to convert to UTF8 from the
// Edit Control charset
return (char*) INTL_ConvertLineWithoutAutoDetect(
INTL_GetCharSetID(INTL_DefaultTextWidgetCsidSel),
CS_UTF8,(unsigned char*) str, size );
}
}
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* pUTF8Text = (char*)GetColumnText(m_nSelectedColumn, HT_GetNthItem(m_View, m_iSelection));
if (pUTF8Text)
{
// we need to convert from UTF8 from the
// Edit Control charset
char* pEditText = (char*) INTL_ConvertLineWithoutAutoDetect(
CS_UTF8,
INTL_GetCharSetID(INTL_DefaultTextWidgetCsidSel),
(unsigned char*)pUTF8Text, strlen(pUTF8Text) );
m_EditField->SetSel(0,-1);
m_EditField->ReplaceSel(pEditText);
m_EditField->SetSel(0,-1);
XP_FREEIF(pEditText);
}
}
}
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;
int visibleCount = 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);
PRBool visible = HT_GetColumnVisibility(theView, token, tokenType);
if (visible)
visibleCount++;
}
if (visibleCount == 0)
visibleCount++;
HT_DeleteColumnCursor(columnCursor);
// If column headers aren't shown, assume only one visible column.
// Whether or not to show column headers
void* data;
HT_GetTemplateData(HT_TopNode(theView), gNavCenter->showColumnHeaders, HT_COLUMN_STRING, &data);
BOOL show = TRUE;
if (data)
{
char* answer = (char*)data;
show = stricmp(answer, "No");
}
if (!show)
visibleCount = 1;
m_pOutliner->SetVisibleColumns(visibleCount);
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 = wfe_CreateNavCenterHTMLPain(m_hWnd);
m_pNavBar->Create(NULL, "", WS_CHILD | WS_VISIBLE, CRect(0,0,100,100), this, NC_IDW_NAVMENU);
CRDFOutliner* pOutliner = (CRDFOutliner*)(GetOutlinerParent()->GetOutliner());
HT_View v = pOutliner->GetHTView();
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;
}
void CRDFContentView::OnSize ( UINT nType, int cx, int cy )
{
CView::OnSize ( nType, cx, cy );
if (IsWindow(m_pOutlinerParent->m_hWnd))
{
int titleHeight = m_pNavBar->GetHeightBasedOnProperties();
m_pNavBar->MoveWindow(0,0, cx, titleHeight);
int htmlPaneHeight = 0;
CRDFOutliner* pOutliner = (CRDFOutliner*)(m_pOutlinerParent->GetOutliner());
HT_View theView = pOutliner->GetHTView();
BOOL percent = FALSE;
if (theView)
{
HT_Resource topNode = HT_TopNode(theView);
if (HT_HasHTMLPane(theView))
{
htmlPaneHeight = 150;
// Need to get the HTML pane's height.
char* data = HT_HTMLPaneHeight(theView);
int height = 0;
if (data)
{
CString strData(data);
int length = strData.GetLength();
if (strData[length-1] == '%')
{
percent = TRUE;
strData = strData.Left(length-1);
}
else if (strData[0] == '*')
{
percent = TRUE;
strData = "100";
}
else
percent = FALSE;
height = atoi(strData);
if (percent)
htmlPaneHeight = (int)((height/100.0) * (cy - titleHeight));
else htmlPaneHeight = height;
}
}
}
int outlinerHeight = cy-titleHeight-htmlPaneHeight;
m_pOutlinerParent->MoveWindow ( 0, titleHeight, cx, outlinerHeight);
::MoveWindow(m_pHTMLView->GetPane(), 0, titleHeight + outlinerHeight, cx,
htmlPaneHeight, TRUE);
}
}
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, xPos+width, yPos+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);
// Register the HTML pane to load the URL
if (HT_HasHTMLPane(HT_GetSelectedView(thePane)))
{
XP_RegisterViewHTMLPane(HT_GetSelectedView(thePane), newView->GetHTMLView()->GetContext());
char* data;
HT_Resource top = HT_TopNode(HT_GetSelectedView(thePane));
HT_GetTemplateData (top, gNavCenter->RDF_HTMLURL, HT_COLUMN_STRING, (void**)&data);
if (data)
{
XP_GetURLForView(HT_GetSelectedView(thePane), data);
}
}
theApp.m_pRDFCX->TrackRDFWindow(newView);
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;
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, char* templateType,
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;
// Construct the pane and give it our notification struct
HT_Pane thePane = HT_PaneFromURL(pContext, url, templateType, 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 NULL;
else
{
// Need to redesign
if (m_pCurrentRDFWindow->GetParent() != NULL &&
m_pCurrentRDFWindow->GetParent()->GetParent() != NULL &&
m_pCurrentRDFWindow->GetParent()->GetParent()->IsKindOf(RUNTIME_CLASS(CRDFContentView)))
{
CRDFOutliner* pOutliner = (CRDFOutliner*)m_pCurrentRDFWindow;
pOutliner->SetTemporaryRetainOnPopup(TRUE);
}
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()