You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1685 lines
48 KiB
1685 lines
48 KiB
/////////////////////////////////////////////////////////////////////////////
|
|
// Copyright (C) 1993-1996 Microsoft Corporation. All Rights Reserved.
|
|
//
|
|
// MODULE: fldbar.cpp
|
|
//
|
|
// PURPOSE: Implements CFolderBar
|
|
//
|
|
|
|
|
|
#include "pch.hxx"
|
|
#include <iert.h>
|
|
#include "fldbar.h"
|
|
#include "resource.h"
|
|
#include <shlwapi.h>
|
|
#include "treeview.h"
|
|
#include "ourguid.h"
|
|
#include "goptions.h"
|
|
#include "browser.h"
|
|
#include "imnglobl.h"
|
|
#include "inpobj.h"
|
|
#include "storutil.h"
|
|
#include <strconst.h>
|
|
#include "demand.h"
|
|
#include "dragdrop.h"
|
|
#include "multiusr.h"
|
|
#include "instance.h"
|
|
#include "mirror.h"
|
|
// Margins
|
|
#define CX_MARGIN_CHILDINDICATOR 4 // space between folder text and child indicator
|
|
#define CX_MARGIN_TEXT 5 // space between left edge and folder text
|
|
#define CX_MARGIN_ICON 4 // 5 space between left edge and icon
|
|
#define CX_MARGIN_ICONTEXT 5 // space between icon and view text
|
|
#define CY_MARGIN_ICON 4 // border around icon
|
|
#define CY_MARGIN_TEXTTOP 2 // space between folder name and top edge of bar
|
|
#define CY_MARGIN_TEXTBOTTOM 2 // space between folder name and bottom edge of bar
|
|
#define CY_MARGIN 4 // 4? margin below control
|
|
#define CX_MARGIN_RIGHTEDGE 2 // margin between the right edge of the bar and the right edge of the window
|
|
#define CX_MARGIN_FOLDERVIEWTEXT 5 // space between folder and view text
|
|
#define CXY_MARGIN_FLYOUT 4 // 4? margin around flyout scope pane
|
|
|
|
// Width/Height of child indicator bitmap
|
|
#define CX_LARGE_CHILDINDICATOR 8
|
|
#define CY_LARGE_CHILDINDICATOR 4
|
|
#define CX_SMALL_CHILDINDICATOR 4
|
|
#define CY_SMALL_CHILDINDICATOR 2
|
|
|
|
#define CX_SMALLICON 16
|
|
#define CY_SMALLICON 16
|
|
|
|
#define DY_SMALLLARGE_CUTOFF 12 // folder bar is small when it takes up more than
|
|
// DY_SMALLLARGE_CUTOFF percent of the available space
|
|
|
|
// Fly out constants
|
|
#define FLYOUT_INCREMENT 5
|
|
|
|
// Minimum width of flyout scope pane
|
|
#define CX_MINWIDTH_FLYOUT 200
|
|
|
|
// Mouse-Over timer ID and interval
|
|
#define IDT_MOUSEOVERCHECK 456
|
|
#define ELAPSE_MOUSEOVERCHECK 250
|
|
|
|
// Drag/Drop mouse-over dropdown timer ID (interval is defined by OLE)
|
|
#define IDT_DROPDOWNCHECK 457
|
|
|
|
// Drag/Drop mouse leave dropdown removal timer ID and interval
|
|
#define IDT_SCOPECLOSECHECK 458
|
|
#define ELAPSE_SCOPECLOSECHECK 500
|
|
|
|
CFolderBar::CFolderBar()
|
|
{
|
|
m_cRef = 1;
|
|
|
|
m_fShow = FALSE;
|
|
m_fRecalc = TRUE;
|
|
m_fHighlightIndicator = FALSE;
|
|
m_fHoverTimer = FALSE;
|
|
|
|
m_idFolder = FOLDERID_INVALID;
|
|
|
|
m_pSite = NULL;
|
|
|
|
m_hwnd = NULL;
|
|
m_hwndFrame = NULL;
|
|
m_hwndParent = NULL;
|
|
m_hwndScopeDropDown = NULL;
|
|
|
|
m_hfFolderName = 0;
|
|
m_hfViewText = 0;
|
|
m_hIconSmall = 0;
|
|
|
|
m_pszFolderName = NULL;
|
|
m_cchFolderName = 0;
|
|
m_pszViewText = NULL;
|
|
m_cchViewText = 0;
|
|
|
|
m_pDataObject = NULL;
|
|
m_pDTCur = NULL;
|
|
m_dwEffectCur = 0;
|
|
m_grfKeyState = 0;
|
|
}
|
|
|
|
CFolderBar::~CFolderBar()
|
|
{
|
|
Assert(m_cRef == 0);
|
|
|
|
SafeRelease(m_pSite);
|
|
SafeRelease(m_pDataObject);
|
|
SafeRelease(m_pDTCur);
|
|
SafeMemFree(m_pszFolderName);
|
|
SafeMemFree(m_pszViewText);
|
|
SafeRelease(m_pBrowser);
|
|
|
|
if (IsWindow(m_hwndFrame))
|
|
DestroyWindow(m_hwndFrame);
|
|
|
|
if (m_hfFolderName)
|
|
DeleteObject(m_hfFolderName);
|
|
if (m_hfViewText)
|
|
DeleteObject(m_hfViewText);
|
|
}
|
|
|
|
HRESULT CFolderBar::HrInit(IAthenaBrowser *pBrowser)
|
|
{
|
|
m_pBrowser = pBrowser;
|
|
|
|
// Don't addref this. It creates a circular ref count with the browser.
|
|
// m_pBrowser->AddRef();
|
|
|
|
BOOL fInfoColumn = FALSE;
|
|
if (SUCCEEDED(m_pBrowser->GetViewLayout(DISPID_MSGVIEW_FOLDERLIST, 0, &fInfoColumn, 0, 0)))
|
|
m_fDropDownIndicator = !fInfoColumn;
|
|
|
|
return (S_OK);
|
|
}
|
|
|
|
HRESULT CFolderBar::QueryInterface(REFIID riid, LPVOID *ppvObj)
|
|
{
|
|
if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IOleWindow) ||
|
|
IsEqualIID(riid, IID_IDockingWindow))
|
|
{
|
|
*ppvObj = (IDockingWindow *) this;
|
|
m_cRef++;
|
|
return (S_OK);
|
|
}
|
|
else if (IsEqualIID(riid, IID_IObjectWithSite))
|
|
{
|
|
*ppvObj = (IObjectWithSite *)this;
|
|
m_cRef++;
|
|
return (S_OK);
|
|
}
|
|
else if (IsEqualIID(riid, IID_IDropTarget))
|
|
{
|
|
*ppvObj = (IDropTarget *) this;
|
|
m_cRef++;
|
|
return (S_OK);
|
|
}
|
|
|
|
*ppvObj = NULL;
|
|
return (E_NOINTERFACE);
|
|
}
|
|
|
|
ULONG CFolderBar::AddRef(void)
|
|
{
|
|
return (m_cRef++);
|
|
}
|
|
|
|
ULONG CFolderBar::Release(void)
|
|
{
|
|
m_cRef--;
|
|
|
|
if (m_cRef > 0)
|
|
return (m_cRef);
|
|
|
|
delete this;
|
|
return (0);
|
|
}
|
|
|
|
HRESULT CFolderBar::GetWindow(HWND *pHwnd)
|
|
{
|
|
if (m_hwnd)
|
|
{
|
|
*pHwnd = m_hwnd;
|
|
return (S_OK);
|
|
}
|
|
else
|
|
{
|
|
*pHwnd = NULL;
|
|
return (E_FAIL);
|
|
}
|
|
}
|
|
|
|
HRESULT CFolderBar::ContextSensitiveHelp(BOOL fEnterMode)
|
|
{
|
|
return (E_NOTIMPL);
|
|
}
|
|
|
|
//
|
|
// FUNCTION: CFolderBar::ShowDW()
|
|
//
|
|
// PURPOSE: Causes the folder bar to be either shown or hidden.
|
|
//
|
|
// PARAMETERS:
|
|
// <in> fShow - TRUE if the folder bar should be shown, FALSE to hide.
|
|
//
|
|
// RETURN VALUE:
|
|
// HRESULT
|
|
//
|
|
#define FOLDERBARCLASS TEXT("FolderBar Window")
|
|
#define FRAMECLASS TEXT("FolderBar Frame")
|
|
HRESULT CFolderBar::ShowDW(BOOL fShow)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
TCHAR szName[CCHMAX_STRINGRES] = {0};
|
|
DWORD dwErr;
|
|
|
|
// If we have a site pointer, but haven't been created yet, create the window
|
|
if (!m_hwndFrame && m_pSite)
|
|
{
|
|
m_hwndParent = NULL;
|
|
hr = m_pSite->GetWindow(&m_hwndParent);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
WNDCLASSEX wc = {0};
|
|
|
|
// Check to see if we need to register the class first
|
|
wc.cbSize = sizeof(WNDCLASSEX);
|
|
if (!GetClassInfoEx(g_hInst, FOLDERBARCLASS, &wc))
|
|
{
|
|
wc.lpfnWndProc = FolderWndProc;
|
|
wc.hInstance = g_hInst;
|
|
wc.hCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));
|
|
wc.lpszClassName = FOLDERBARCLASS;
|
|
|
|
if (!RegisterClassEx(&wc))
|
|
{
|
|
dwErr = GetLastError();
|
|
}
|
|
|
|
wc.lpfnWndProc = FrameWndProc;
|
|
wc.hbrBackground = (HBRUSH) (COLOR_3DFACE + 1);
|
|
wc.lpszClassName = FRAMECLASS;
|
|
|
|
if (!RegisterClassEx(&wc))
|
|
{
|
|
dwErr = GetLastError();
|
|
}
|
|
}
|
|
|
|
m_hwndFrame = CreateWindow(FRAMECLASS, NULL, WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
|
|
0, 0, 1, 1, m_hwndParent, (HMENU) 0, g_hInst, (LPVOID *) this);
|
|
if (!m_hwndFrame)
|
|
{
|
|
GetLastError();
|
|
return (E_OUTOFMEMORY);
|
|
}
|
|
|
|
LoadString(g_hLocRes, idsFolderBar, szName, ARRAYSIZE(szName));
|
|
|
|
m_hwnd = CreateWindow(FOLDERBARCLASS, szName, WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE,
|
|
0, 0, 0, 0, m_hwndFrame, (HMENU) 0, g_hInst, (LPVOID*) this);
|
|
if (!m_hwnd)
|
|
{
|
|
GetLastError();
|
|
return (E_OUTOFMEMORY);
|
|
}
|
|
RegisterDragDrop(m_hwnd, (IDropTarget *) this);
|
|
}
|
|
}
|
|
|
|
// Set our state flags
|
|
m_fShow = fShow;
|
|
|
|
// Resize the folder bar based on its new hidden / visible state
|
|
if (m_hwndFrame)
|
|
{
|
|
ResizeBorderDW(NULL, NULL, FALSE);
|
|
ShowWindow(m_hwndFrame, fShow ? SW_SHOW : SW_HIDE);
|
|
}
|
|
|
|
return (hr);
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: CFolderBar::CloseDW()
|
|
//
|
|
// PURPOSE: Destroys the folder bar.
|
|
//
|
|
HRESULT CFolderBar::CloseDW(DWORD dwReserved)
|
|
{
|
|
if (m_hwndFrame)
|
|
{
|
|
DestroyWindow(m_hwndFrame);
|
|
m_hwndFrame = NULL;
|
|
m_hwnd = NULL;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// FUNCTION: CFolderBar::ResizeBorderDW()
|
|
//
|
|
// PURPOSE: This is called when the folder bar needs to resize. The bar
|
|
// in return figures out how much border space will be required
|
|
// from the parent frame and tells the parent to reserve that
|
|
// space. The bar then resizes itself to those dimensions.
|
|
//
|
|
// PARAMETERS:
|
|
// <in> prcBorder - Rectangle containing the border space for the
|
|
// parent.
|
|
// <in> punkToolbarSite - Pointer to the IDockingWindowSite that we are
|
|
// part of.
|
|
// <in> fReserved - Ignored.
|
|
//
|
|
// RETURN VALUE:
|
|
// HRESULT
|
|
//
|
|
HRESULT CFolderBar::ResizeBorderDW(LPCRECT prcBorder,
|
|
IUnknown* punkToolbarSite,
|
|
BOOL fReserved)
|
|
{
|
|
RECT rcRequest = {0, 0, 0, 0};
|
|
BOOL fFontChange;
|
|
|
|
if (!m_pSite)
|
|
return (E_FAIL);
|
|
|
|
if (m_fShow)
|
|
{
|
|
RECT rcBorder;
|
|
|
|
if (!prcBorder)
|
|
{
|
|
// Find out how big our parent's border space is
|
|
m_pSite->GetBorderDW((IDockingWindow *) this, &rcBorder);
|
|
prcBorder = &rcBorder;
|
|
}
|
|
|
|
if (m_fRecalc)
|
|
{
|
|
fFontChange = TRUE;
|
|
InvalidateRect(m_hwnd, NULL, TRUE);
|
|
}
|
|
else
|
|
{
|
|
fFontChange = FALSE;
|
|
InvalidateRect(m_hwnd, NULL, TRUE);
|
|
}
|
|
|
|
// Recalc our internal sizing info
|
|
Recalc(NULL, prcBorder, fFontChange);
|
|
|
|
// Position ourself
|
|
rcRequest.top = m_cyControl + CY_MARGIN;
|
|
|
|
SetWindowPos(m_hwndFrame, NULL, prcBorder->left, prcBorder->top, prcBorder->right - prcBorder->left,
|
|
rcRequest.top, SWP_NOACTIVATE | SWP_NOZORDER);
|
|
}
|
|
|
|
m_pSite->SetBorderSpaceDW((IDockingWindow *) this, &rcRequest);
|
|
|
|
return (S_OK);
|
|
}
|
|
|
|
//
|
|
// FUNCTION: CFolderBar::SetSite()
|
|
//
|
|
// PURPOSE: Allows the owner of the coolbar to tell it what the current
|
|
// IDockingWindowSite interface to use is.
|
|
//
|
|
// PARAMETERS:
|
|
// <in> punkSite - Pointer of the IUnknown to query for IDockingWindowSite.
|
|
// If this is NULL, we just release our current pointer.
|
|
//
|
|
// RETURN VALUE:
|
|
// S_OK - Everything worked
|
|
// E_FAIL - Could not get IDockingWindowSite from the punkSite provided.
|
|
//
|
|
HRESULT CFolderBar::SetSite(IUnknown* punkSite)
|
|
{
|
|
// If we had a previous pointer, release it.
|
|
if (m_pSite)
|
|
{
|
|
m_pSite->Release();
|
|
m_pSite = NULL;
|
|
}
|
|
|
|
// If a new site was provided, get the IDockingWindowSite interface from it.
|
|
if (punkSite)
|
|
{
|
|
if (FAILED(punkSite->QueryInterface(IID_IDockingWindowSite,
|
|
(LPVOID*) &m_pSite)))
|
|
{
|
|
Assert(m_pSite);
|
|
return E_FAIL;
|
|
}
|
|
}
|
|
|
|
return (S_OK);
|
|
}
|
|
|
|
HRESULT CFolderBar::GetSite(REFIID riid, LPVOID *ppvSite)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: CFolderBar::SetCurrentFolder()
|
|
//
|
|
// PURPOSE: Tells the control to display information for a different folder
|
|
//
|
|
// PARAMETERS:
|
|
// <in> pidl - PIDL for the new folder
|
|
//
|
|
// RETURN VALUE:
|
|
// HRESULT
|
|
//
|
|
HRESULT CFolderBar::SetCurrentFolder(FOLDERID idFolder)
|
|
{
|
|
// NOTE - This routine never fails. It will just show everything blank
|
|
UINT uIndex = -1;
|
|
TCHAR sz[CCHMAX_STRINGRES];
|
|
FOLDERINFO Folder;
|
|
|
|
// Invalidate and let the paint routine know that we're going to need to
|
|
// recalc
|
|
m_fRecalc = TRUE;
|
|
InvalidateRect(m_hwnd, NULL, TRUE);
|
|
|
|
// Save the Folder Id
|
|
m_idFolder = idFolder;
|
|
|
|
// Get Folder Info
|
|
if (FAILED(g_pStore->GetFolderInfo(idFolder, &Folder)))
|
|
return (S_OK);
|
|
|
|
// Set Icon
|
|
uIndex = GetFolderIcon(&Folder);
|
|
|
|
// Clear the view text
|
|
SetFolderText(MU_GetCurrentIdentityName());
|
|
|
|
if ((g_dwAthenaMode & MODE_NEWSONLY) && (Folder.tyFolder == FOLDER_ROOTNODE))
|
|
{
|
|
//Change the name from OutLookExpress to Outlook News
|
|
ZeroMemory(sz, sizeof(TCHAR) * CCHMAX_STRINGRES);
|
|
LoadString(g_hLocRes, idsOutlookNewsReader, sz, ARRAYSIZE(sz));
|
|
|
|
SetFolderName(sz);
|
|
}
|
|
else
|
|
{
|
|
// Set the folder name
|
|
SetFolderName(Folder.pszName);
|
|
}
|
|
|
|
// Free the previous icons
|
|
if (m_hIconSmall)
|
|
{
|
|
DestroyIcon(m_hIconSmall);
|
|
m_hIconSmall = 0;
|
|
}
|
|
|
|
if (-1 != uIndex)
|
|
{
|
|
// Load the small icon
|
|
HIMAGELIST himl = ImageList_LoadBitmap(g_hLocRes, MAKEINTRESOURCE(idbFolders), 16, 0,
|
|
RGB(255, 0, 255));
|
|
if (NULL != himl)
|
|
{
|
|
m_hIconSmall = ImageList_GetIcon(himl, uIndex, ILD_NORMAL);
|
|
ImageList_Destroy(himl);
|
|
}
|
|
}
|
|
|
|
// If this folder is moderated or blocked, say so
|
|
TCHAR szRes[CCHMAX_STRINGRES];
|
|
if (Folder.dwFlags & FOLDER_MODERATED)
|
|
{
|
|
AthLoadString(idsModerated, szRes, ARRAYSIZE(szRes));
|
|
SetFolderText(szRes);
|
|
}
|
|
else if (Folder.dwFlags & FOLDER_BLOCKED)
|
|
{
|
|
AthLoadString(idsBlocked, szRes, ARRAYSIZE(szRes));
|
|
SetFolderText(szRes);
|
|
}
|
|
|
|
g_pStore->FreeRecord(&Folder);
|
|
|
|
return (S_OK);
|
|
}
|
|
|
|
|
|
void CFolderBar::SetFolderText(LPCTSTR pszText)
|
|
{
|
|
// Invalidate and let the paint routine know we are going to need to
|
|
// recalc
|
|
m_fRecalc = TRUE;
|
|
InvalidateRect(m_hwnd, NULL, TRUE);
|
|
|
|
// Free an old text
|
|
SafeMemFree(m_pszViewText);
|
|
m_cchViewText = 0;
|
|
|
|
if (pszText && *pszText)
|
|
{
|
|
m_pszViewText = PszDupA(pszText);
|
|
m_cchViewText = lstrlen(pszText);
|
|
}
|
|
}
|
|
|
|
|
|
void CFolderBar::SetFolderName(LPCTSTR pszFolderName)
|
|
{
|
|
// Free the old folder name
|
|
SafeMemFree(m_pszFolderName);
|
|
m_cchFolderName = 0;
|
|
|
|
// Copy the new one
|
|
if (pszFolderName)
|
|
{
|
|
m_pszFolderName = PszDupA(pszFolderName);
|
|
m_cchFolderName = lstrlen(m_pszFolderName);
|
|
}
|
|
}
|
|
|
|
// Calculates the rectangle which surrounds the folder name
|
|
void CFolderBar::GetFolderNameRect(LPRECT prc)
|
|
{
|
|
Assert(prc);
|
|
|
|
GetClientRect(m_hwnd, prc);
|
|
prc->right = m_cxFolderNameRight;
|
|
}
|
|
|
|
|
|
void CFolderBar::Recalc(HDC hDC, LPCRECT prcAvailableSpace, BOOL fSizeChange)
|
|
{
|
|
int cyIcon = CY_SMALLICON,
|
|
cxIcon = CX_SMALLICON;
|
|
BOOL fReleaseDC;
|
|
TEXTMETRIC tmFolderName,
|
|
tmViewText;
|
|
RECT rcClient;
|
|
SIZE sFolderName,
|
|
sViewText;
|
|
HFONT hFontOld;
|
|
|
|
// Signal that we don't need to recalc again
|
|
m_fRecalc = FALSE;
|
|
|
|
if (prcAvailableSpace)
|
|
{
|
|
rcClient.left = 0;
|
|
rcClient.top = 0;
|
|
rcClient.right = prcAvailableSpace->right - prcAvailableSpace->left;
|
|
rcClient.bottom = prcAvailableSpace->bottom - prcAvailableSpace->top;
|
|
}
|
|
else
|
|
GetClientRect(m_hwnd, &rcClient);
|
|
|
|
// Get a device context if we were not given one
|
|
if (hDC)
|
|
fReleaseDC = FALSE;
|
|
else
|
|
{
|
|
hDC = GetDC(m_hwnd);
|
|
fReleaseDC = TRUE;
|
|
}
|
|
|
|
// Create the fonts
|
|
if (fSizeChange || !m_hfFolderName || !m_hfViewText)
|
|
{
|
|
if (m_hfFolderName)
|
|
DeleteObject(m_hfFolderName);
|
|
if (m_hfViewText)
|
|
DeleteObject(m_hfViewText);
|
|
|
|
|
|
// Create the font we are going to use for the folder name
|
|
m_hfFolderName = GetFont(idsFontFolderSmall, FW_BOLD);
|
|
m_hfViewText = GetFont(idsFontViewTextSmall, FW_BOLD);
|
|
|
|
|
|
// Determine the height of the control, which is whatever is larger of i
|
|
// the following two things
|
|
// 1) The icon height plus the icon margin
|
|
// 2) The text height plus the text margin
|
|
hFontOld = SelectFont(hDC, m_hfFolderName);
|
|
GetTextMetrics(hDC, &tmFolderName);
|
|
SelectFont(hDC, hFontOld);
|
|
m_cyControl = max(cyIcon + CY_MARGIN_ICON,
|
|
tmFolderName.tmHeight + CY_MARGIN_TEXTTOP + CY_MARGIN_TEXTBOTTOM);
|
|
|
|
// The top of the folder name text is position so that we have the correct
|
|
// amount of border at the bottom of the control
|
|
m_dyFolderName = m_cyControl - tmFolderName.tmHeight - CY_MARGIN_TEXTBOTTOM;
|
|
|
|
// Get the height of the view text
|
|
hFontOld = SelectFont(hDC, m_hfViewText);
|
|
GetTextMetrics(hDC, &tmViewText);
|
|
SelectFont(hDC, hFontOld);
|
|
|
|
// The view text is positioned such that it's baseline matches the baseline
|
|
// of the folder name
|
|
m_dyViewText = m_dyFolderName + tmFolderName.tmAscent - tmViewText.tmAscent;
|
|
|
|
// The child indicator is positioned such that the bottom of the bitmap lines
|
|
// up with the baseline of the folder name
|
|
m_dyChildIndicator = m_cyControl - CY_MARGIN_TEXTBOTTOM - tmFolderName.tmDescent - GetYChildIndicator();
|
|
|
|
// The folder icon is centered within the control
|
|
m_dyIcon = (m_cyControl - cyIcon) / 2;
|
|
|
|
// Number must be even to ensure good-looking triangular drop arrow.
|
|
Assert(GetXChildIndicator() % 2 == 0);
|
|
|
|
// Width must be multiple of height for triangle to look smooth.
|
|
Assert(GetXChildIndicator() % GetYChildIndicator() == 0);
|
|
}
|
|
|
|
// The view text is right justified within the folder bar
|
|
if (m_cchViewText)
|
|
{
|
|
m_rcViewText.top = m_dyViewText;
|
|
m_rcViewText.right = rcClient.right - CX_MARGIN_TEXT;
|
|
|
|
m_rcViewText.bottom = rcClient.bottom;
|
|
m_nFormatViewText = DT_RIGHT | DT_TOP | DT_SINGLELINE | DT_NOPREFIX;
|
|
hFontOld = SelectFont(hDC, m_hfViewText);
|
|
GetTextExtentPoint32(hDC, m_pszViewText, m_cchViewText, &sViewText);
|
|
SelectFont(hDC, hFontOld);
|
|
m_rcViewText.left = m_rcViewText.right - sViewText.cx;
|
|
}
|
|
|
|
|
|
// The folder name is left justified within the folder bar. It is clipped
|
|
// so that it does not overlap the view text
|
|
if (m_cchFolderName)
|
|
{
|
|
m_rcFolderName.left = CX_MARGIN_ICONTEXT + cxIcon + CX_MARGIN_ICON;
|
|
m_rcFolderName.top = m_dyFolderName;
|
|
|
|
if (m_cchViewText)
|
|
m_rcFolderName.right = m_rcViewText.left - CX_MARGIN_FOLDERVIEWTEXT;
|
|
else
|
|
m_rcFolderName.right = rcClient.right;
|
|
|
|
m_rcFolderName.bottom = rcClient.bottom;
|
|
|
|
if (FDropDownEnabled())
|
|
m_rcFolderName.right -= GetXChildIndicator() + CX_MARGIN_CHILDINDICATOR;
|
|
|
|
m_nFormatFolderName = DT_LEFT | DT_BOTTOM | DT_SINGLELINE | DT_NOPREFIX;
|
|
|
|
hFontOld = SelectFont(hDC, m_hfFolderName);
|
|
GetTextExtentPoint32(hDC, m_pszFolderName, m_cchFolderName, &sFolderName);
|
|
|
|
if (sFolderName.cx > (m_rcFolderName.right - m_rcFolderName.left))
|
|
{
|
|
m_nFormatFolderName |= DT_END_ELLIPSIS;
|
|
#ifndef WIN16
|
|
DrawTextEx(hDC, m_pszFolderName, m_cchFolderName, &m_rcFolderName,
|
|
m_nFormatFolderName | DT_CALCRECT, NULL);
|
|
#else
|
|
DrawText(hDC, m_pszFolderName, m_cchFolderName, &m_rcFolderName,
|
|
m_nFormatFolderName | DT_CALCRECT);
|
|
#endif // !WIN16
|
|
}
|
|
else
|
|
{
|
|
m_nFormatFolderName |= DT_NOCLIP;
|
|
m_rcFolderName.right = m_rcFolderName.left + sFolderName.cx;
|
|
}
|
|
|
|
SelectFont(hDC, hFontOld);
|
|
m_cxFolderNameRight = m_rcFolderName.right + CX_MARGIN_TEXT;
|
|
|
|
if (FDropDownEnabled())
|
|
m_cxFolderNameRight += GetXChildIndicator() + CX_MARGIN_CHILDINDICATOR + 2;
|
|
}
|
|
|
|
|
|
// When the folder name is clipped it will always display at least one letter
|
|
// followed by ellipsis. Make sure not to draw the view text over this.
|
|
if (m_cchViewText)
|
|
{
|
|
if (m_rcViewText.left < m_rcFolderName.right + CX_MARGIN_FOLDERVIEWTEXT)
|
|
m_rcViewText.left = m_rcFolderName.right + CX_MARGIN_FOLDERVIEWTEXT;
|
|
else
|
|
m_nFormatViewText |= DT_NOCLIP;
|
|
}
|
|
|
|
if (fReleaseDC)
|
|
ReleaseDC(m_hwnd, hDC);
|
|
}
|
|
|
|
HFONT CFolderBar::GetFont(UINT idsFont, int nWeight)
|
|
{
|
|
// The font info is stored as a string in the resources so the localizers
|
|
// can get to it. The format of the string is "face,size"
|
|
TCHAR sz[CCHMAX_STRINGRES];
|
|
LPTSTR pszFace, pszTok;
|
|
LONG lSize;
|
|
|
|
// Load the setting
|
|
AthLoadString(idsFont, sz, ARRAYSIZE(sz));
|
|
|
|
// Parse out the face name
|
|
pszTok = sz;
|
|
pszFace = StrTokEx(&pszTok, g_szComma);
|
|
|
|
// Parse out the size
|
|
lSize = StrToInt(StrTokEx(&pszTok, g_szComma));
|
|
return(GetFont(/* pszFace*/ NULL, lSize, nWeight)); // (YST) szFace parametr was always ignored in OE 4.0,
|
|
}
|
|
|
|
HFONT CFolderBar::GetFont(LPTSTR pszFace, LONG lSize, int nWeight)
|
|
{
|
|
HFONT hf;
|
|
HDC hdc = GetDC(m_hwnd);
|
|
#ifndef WIN16
|
|
ICONMETRICS icm;
|
|
#else
|
|
LOGFONT lf;
|
|
#endif
|
|
|
|
lSize = -MulDiv(lSize, GetDeviceCaps(hdc, LOGPIXELSY), 720);
|
|
|
|
#ifndef WIN16
|
|
// Get the title bar font from the system
|
|
icm.cbSize = sizeof(ICONMETRICS);
|
|
SystemParametersInfo(SPI_GETICONMETRICS, sizeof(ICONMETRICS),
|
|
(LPVOID) &icm, FALSE);
|
|
|
|
// Create the font
|
|
hf = CreateFont(lSize, 0, 0, 0, nWeight, 0, 0, 0, DEFAULT_CHARSET,
|
|
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
|
|
icm.lfFont.lfPitchAndFamily, (pszFace ? pszFace : icm.lfFont.lfFaceName));
|
|
#else
|
|
// Get the logical font infomation for the current icon-title font.
|
|
SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &lf, FALSE);
|
|
|
|
|
|
// Create the font
|
|
hf = CreateFont(lSize, 0, 0, 0, nWeight /* FW_NORMAL*/, 0, 0, 0, DEFAULT_CHARSET,
|
|
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
|
|
lf.lfPitchAndFamily, (pszFace ? pszFace : icm.lfFont.lfFaceName));
|
|
#endif // !WIN16
|
|
|
|
ReleaseDC(m_hwnd, hdc);
|
|
|
|
return (hf);
|
|
|
|
}
|
|
|
|
void CFolderBar::OnPaint(HWND hwnd)
|
|
{
|
|
HDC hdc;
|
|
PAINTSTRUCT ps;
|
|
RECT rcClient,
|
|
rc;
|
|
POINT pt[3];
|
|
HBRUSH hBrush,
|
|
hBrushOld;
|
|
HPEN hPen,
|
|
hPenOld;
|
|
HFONT hFontOld;
|
|
COLORREF crFG = GetSysColor(COLOR_WINDOW);
|
|
COLORREF crWindowText = GetSysColor(COLOR_WINDOWTEXT);
|
|
#ifndef WIN16
|
|
COLORREF crBtnHighlight = GetSysColor(COLOR_BTNHILIGHT);
|
|
#else
|
|
COLORREF crBtnHighlight = GetSysColor(COLOR_BTNHIGHLIGHT);
|
|
#endif // !WIN16
|
|
|
|
GetClientRect(m_hwnd, &rcClient);
|
|
|
|
hdc = BeginPaint(m_hwnd, &ps);
|
|
|
|
// Recalc the text positions
|
|
if (m_fRecalc)
|
|
Recalc(hdc, NULL, FALSE);
|
|
|
|
// Paint the background
|
|
hBrush = CreateSolidBrush(GetSysColor(COLOR_3DSHADOW));
|
|
hBrushOld = SelectBrush(hdc, hBrush);
|
|
PatBlt(hdc, rcClient.left, rcClient.top, rcClient.right - rcClient.left,
|
|
rcClient.bottom - rcClient.top, PATCOPY);
|
|
SelectBrush(hdc, hBrushOld);
|
|
DeleteBrush(hBrush);
|
|
|
|
// Set the foreground and background color
|
|
SetBkColor(hdc, GetSysColor(COLOR_3DSHADOW));
|
|
SetTextColor(hdc, crFG);
|
|
|
|
// Folder name
|
|
if (m_cchFolderName)
|
|
{
|
|
hFontOld = SelectFont(hdc, m_hfFolderName);
|
|
|
|
// Use IDrawText because DrawTextEx() doesn't handle DBCS.
|
|
// Note, the "bottom - top" nonsense for the last param is to undo some
|
|
// vertical centering that IDrawText is trying to do that we don't want.
|
|
IDrawText(hdc, m_pszFolderName, &m_rcFolderName, m_nFormatFolderName & DT_END_ELLIPSIS,
|
|
m_rcFolderName.bottom - m_rcFolderName.top);
|
|
SelectFont(hdc, hFontOld);
|
|
|
|
}
|
|
|
|
// Drop-down indicator
|
|
if (FDropDownEnabled())
|
|
{
|
|
pt[0].x = m_rcFolderName.right + CX_MARGIN_CHILDINDICATOR;
|
|
pt[0].y = m_dyChildIndicator;
|
|
pt[1].x = pt[0].x + GetXChildIndicator();
|
|
pt[1].y = pt[0].y;
|
|
pt[2].x = pt[0].x + GetXChildIndicator() / 2;
|
|
pt[2].y = pt[0].y + GetYChildIndicator();
|
|
|
|
hPen = CreatePen(PS_SOLID, 1, crFG);
|
|
hBrush = CreateSolidBrush(crFG);
|
|
hPenOld = SelectPen(hdc, hPen);
|
|
hBrushOld = SelectBrush(hdc, hBrush);
|
|
Polygon(hdc, pt, 3);
|
|
SelectPen(hdc, hPenOld);
|
|
SelectBrush(hdc, hBrushOld);
|
|
DeleteObject(hPen);
|
|
DeleteObject(hBrush);
|
|
}
|
|
|
|
// Mouse-over highlight
|
|
if (m_fHighlightIndicator || m_hwndScopeDropDown)
|
|
{
|
|
hPen = CreatePen(PS_SOLID, 1, m_hwndScopeDropDown ? crWindowText : crBtnHighlight);
|
|
hPenOld = SelectPen(hdc, hPen);
|
|
pt[0].x = rcClient.left;
|
|
pt[0].y = rcClient.bottom - 1; // - CY_MARGIN;
|
|
pt[1].x = rcClient.left;
|
|
pt[1].y = rcClient.top;
|
|
pt[2].x = m_cxFolderNameRight - 1;
|
|
pt[2].y = rcClient.top;
|
|
Polyline(hdc, (POINT *)&pt, 3);
|
|
SelectPen(hdc, hPenOld);
|
|
DeleteObject(hPen);
|
|
|
|
hPen = CreatePen(PS_SOLID, 1, m_hwndScopeDropDown ? crBtnHighlight : crWindowText);
|
|
hPenOld = SelectPen(hdc, hPen);
|
|
pt[1].x = m_cxFolderNameRight - 1;
|
|
pt[1].y = rcClient.bottom - 1; // - CY_MARGIN;
|
|
pt[2].x = pt[1].x;
|
|
pt[2].y = rcClient.top - 1;
|
|
Polyline(hdc, (POINT *)&pt, 3);
|
|
SelectPen(hdc, hPenOld);
|
|
DeleteObject(hPen);
|
|
}
|
|
|
|
// View text
|
|
if (m_cchViewText)
|
|
{
|
|
SetTextColor(hdc, crFG);
|
|
hFontOld = SelectFont(hdc, m_hfViewText);
|
|
ExtTextOut(hdc, m_rcViewText.left, m_rcViewText.top, ETO_OPAQUE | ETO_CLIPPED,
|
|
&m_rcViewText, m_pszViewText, m_cchViewText, NULL);
|
|
SelectFont(hdc, hFontOld);
|
|
}
|
|
|
|
// Folder Icon
|
|
if (m_hIconSmall)
|
|
{
|
|
int x = rcClient.left + CX_MARGIN_ICON;
|
|
int y = m_dyIcon;
|
|
|
|
DrawIconEx(hdc, x, y, m_hIconSmall, CX_SMALLICON, CY_SMALLICON, 0, NULL, DI_NORMAL);
|
|
}
|
|
|
|
EndPaint(m_hwnd, &ps);
|
|
}
|
|
|
|
|
|
BOOL CFolderBar::FDropDownEnabled(void)
|
|
{
|
|
return (m_fDropDownIndicator);
|
|
}
|
|
|
|
void CFolderBar::InvalidateFolderName(void)
|
|
{
|
|
RECT rcFolderName;
|
|
|
|
if (m_fRecalc)
|
|
InvalidateRect(m_hwnd, NULL, TRUE);
|
|
else
|
|
{
|
|
GetFolderNameRect(&rcFolderName);
|
|
InvalidateRect(m_hwnd, &rcFolderName, TRUE);
|
|
}
|
|
}
|
|
|
|
int CFolderBar::GetXChildIndicator()
|
|
{
|
|
return CX_SMALL_CHILDINDICATOR;
|
|
}
|
|
|
|
int CFolderBar::GetYChildIndicator()
|
|
{
|
|
return CY_SMALL_CHILDINDICATOR;
|
|
}
|
|
|
|
LRESULT CALLBACK CFolderBar::FolderWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
CFolderBar *pThis = (CFolderBar *) GetWndThisPtr(hwnd);
|
|
|
|
switch (uMsg)
|
|
{
|
|
case WM_NCCREATE:
|
|
{
|
|
pThis = (CFolderBar *) ((LPCREATESTRUCT) lParam)->lpCreateParams;
|
|
SetWndThisPtr(hwnd, (LONG_PTR) pThis);
|
|
return (TRUE);
|
|
}
|
|
|
|
HANDLE_MSG(hwnd, WM_PAINT, pThis->OnPaint);
|
|
HANDLE_MSG(hwnd, WM_MOUSEMOVE, pThis->OnMouseMove);
|
|
HANDLE_MSG(hwnd, WM_LBUTTONDOWN, pThis->OnLButtonDown);
|
|
HANDLE_MSG(hwnd, WM_TIMER, pThis->OnTimer);
|
|
|
|
case WM_CREATE:
|
|
{
|
|
#ifndef WIN16
|
|
if (g_pConMan)
|
|
g_pConMan->Advise((IConnectionNotify*)pThis);
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
case WM_DESTROY:
|
|
{
|
|
#ifndef WIN16
|
|
if (g_pConMan)
|
|
g_pConMan->Unadvise((IConnectionNotify*)pThis);
|
|
#endif
|
|
RevokeDragDrop(hwnd);
|
|
break;
|
|
}
|
|
|
|
case WM_SYSCOLORCHANGE:
|
|
case WM_WININICHANGE:
|
|
case WM_FONTCHANGE:
|
|
{
|
|
pThis->Recalc(NULL, NULL, TRUE);
|
|
InvalidateRect(pThis->m_hwnd, NULL, TRUE);
|
|
return (0);
|
|
}
|
|
|
|
case WM_PALETTECHANGED:
|
|
|
|
InvalidateRect(pThis->m_hwnd, NULL, TRUE);
|
|
break;
|
|
|
|
case WM_QUERYNEWPALETTE:
|
|
InvalidateRect(pThis->m_hwnd, NULL, TRUE);
|
|
return(TRUE);
|
|
}
|
|
|
|
return (DefWindowProc(hwnd, uMsg, wParam, lParam));
|
|
}
|
|
|
|
void CFolderBar::OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags)
|
|
{
|
|
POINT pt = {x, y};
|
|
#if 0
|
|
if (!IsRectEmtpy(&m_rcDragDetect) && !PtInRect(m_rcDragDetect, pt))
|
|
{
|
|
SetRectEmpty(m_rcDragDetect);
|
|
HrBeginDrag();
|
|
}
|
|
else
|
|
#endif
|
|
DoMouseOver(&pt, MO_NORMAL);
|
|
}
|
|
|
|
|
|
void CFolderBar::OnLButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags)
|
|
{
|
|
POINT pt = {x, y};
|
|
DoMouseClick(pt, keyFlags);
|
|
}
|
|
|
|
|
|
void CFolderBar::OnTimer(HWND hwnd, UINT id)
|
|
{
|
|
RECT rcClient;
|
|
POINT pt;
|
|
DWORD dwMP;
|
|
BOOL fHighlightOff = FALSE;
|
|
|
|
dwMP = GetMessagePos();
|
|
pt.x = LOWORD(dwMP);
|
|
pt.y = HIWORD(dwMP);
|
|
ScreenToClient(m_hwnd, &pt);
|
|
|
|
if (id == IDT_MOUSEOVERCHECK)
|
|
{
|
|
GetClientRect(m_hwnd, &rcClient);
|
|
// No need to handle mouse in client area, OnMouseMove will catch this. We
|
|
// only need to catch the mouse moving out of the client area.
|
|
if (!PtInRect(&rcClient, pt))
|
|
{
|
|
KillTimer(m_hwnd, IDT_MOUSEOVERCHECK);
|
|
fHighlightOff = TRUE;
|
|
}
|
|
}
|
|
else if (id == IDT_DROPDOWNCHECK)
|
|
{
|
|
DoMouseClick(pt, 0);
|
|
// ??? DoDeferredCall(DEFERREDCALL_REGISTERTARGET);
|
|
fHighlightOff = TRUE;
|
|
}
|
|
else if (id == IDT_SCOPECLOSECHECK)
|
|
{
|
|
KillScopeDropDown();
|
|
}
|
|
#ifdef DEBUG
|
|
else
|
|
AssertSz(FALSE, "Hey! Who has another timer going here?");
|
|
#endif
|
|
|
|
if (fHighlightOff)
|
|
{
|
|
m_fHighlightIndicator = FALSE;
|
|
InvalidateFolderName();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
void CFolderBar::DoMouseOver(LPPOINT ppt, MOMODE moMode)
|
|
{
|
|
HWND hwndActive;
|
|
RECT rcFolderName;
|
|
|
|
if (!FDropDownEnabled() || m_hwndScopeDropDown)
|
|
return;
|
|
|
|
if (moMode == MO_NORMAL)
|
|
{
|
|
// Only do mouse-over if we are the active window and not d&d
|
|
hwndActive = GetActiveWindow();
|
|
if (!hwndActive || (hwndActive != m_hwndParent && IsChild(m_hwndParent, hwndActive)))
|
|
return;
|
|
}
|
|
|
|
GetFolderNameRect(&rcFolderName);
|
|
|
|
if (moMode == MO_DRAGLEAVE || moMode == MO_DRAGDROP)
|
|
ppt->x = rcFolderName.left - 1; // Force point to be outside
|
|
|
|
if (m_fHighlightIndicator != PtInRect(&rcFolderName, *ppt))
|
|
{
|
|
m_fHighlightIndicator = !m_fHighlightIndicator;
|
|
InvalidateFolderName();
|
|
|
|
if (moMode == MO_DRAGOVER)
|
|
{
|
|
if (!m_hwndScopeDropDown && m_fHoverTimer != m_fHighlightIndicator)
|
|
{
|
|
KillHoverTimer();
|
|
if (m_fHighlightIndicator)
|
|
m_fHoverTimer = (0 != SetTimer(m_hwnd, IDT_DROPDOWNCHECK, GetDoubleClickTime(), NULL));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
KillTimer(m_hwnd, IDT_MOUSEOVERCHECK);
|
|
if (m_fHighlightIndicator)
|
|
SetTimer(m_hwnd, IDT_MOUSEOVERCHECK, ELAPSE_MOUSEOVERCHECK, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CFolderBar::KillHoverTimer()
|
|
{
|
|
if (m_fHoverTimer)
|
|
{
|
|
KillTimer(m_hwnd, IDT_DROPDOWNCHECK);
|
|
m_fHoverTimer = fFalse;
|
|
}
|
|
}
|
|
|
|
|
|
void CFolderBar::DoMouseClick(POINT pt, DWORD grfKeyState)
|
|
{
|
|
RECT rcFolderName;
|
|
|
|
if (!FDropDownEnabled())
|
|
return;
|
|
|
|
GetFolderNameRect(&rcFolderName);
|
|
if (PtInRect(&rcFolderName, pt))
|
|
{
|
|
if (IsWindow(m_hwndScopeDropDown))
|
|
KillScopeDropDown();
|
|
else
|
|
{
|
|
KillHoverTimer();
|
|
HrShowScopeFlyOut();
|
|
}
|
|
}
|
|
}
|
|
|
|
void CFolderBar::KillScopeDropDown(void)
|
|
{
|
|
POINT pt;
|
|
|
|
// During window destruction hwndScopeDropDown gets set to NULL.
|
|
if (IsWindow(m_hwndScopeDropDown))
|
|
{
|
|
KillScopeCloseTimer();
|
|
DestroyWindow(m_hwndScopeDropDown);
|
|
m_hwndScopeDropDown = NULL;
|
|
pt.x = pt.y = 0;
|
|
DoMouseOver(&pt, MO_DRAGLEAVE);
|
|
}
|
|
}
|
|
|
|
void CFolderBar::SetScopeCloseTimer(void)
|
|
{
|
|
KillScopeCloseTimer();
|
|
|
|
// If we can't set the timer, we just do it immediately.
|
|
if (!SetTimer(m_hwnd, IDT_SCOPECLOSECHECK, ELAPSE_SCOPECLOSECHECK, NULL))
|
|
SendMessage(m_hwnd, WM_TIMER, (WPARAM) IDT_SCOPECLOSECHECK, NULL);
|
|
}
|
|
|
|
void CFolderBar::KillScopeCloseTimer(void)
|
|
{
|
|
KillTimer(m_hwnd, IDT_SCOPECLOSECHECK);
|
|
}
|
|
|
|
|
|
HRESULT CFolderBar::HrShowScopeFlyOut(void)
|
|
{
|
|
IAthenaBrowser *pBrowser = NULL;
|
|
CFlyOutScope *pFlyOutScope;
|
|
|
|
m_pSite->QueryInterface(IID_IAthenaBrowser, (LPVOID *) &pBrowser);
|
|
Assert(pBrowser);
|
|
|
|
pFlyOutScope = new CFlyOutScope;
|
|
if (pFlyOutScope && pBrowser)
|
|
{
|
|
pFlyOutScope->HrDisplay(pBrowser, this, m_hwndParent,
|
|
&m_hwndScopeDropDown);
|
|
InvalidateFolderName();
|
|
}
|
|
SafeRelease(pBrowser);
|
|
|
|
return (S_OK);
|
|
}
|
|
|
|
|
|
void CFolderBar::Update(BOOL fDisplayNameChanged, BOOL fShowDropDownIndicator)
|
|
{
|
|
if (fDisplayNameChanged)
|
|
{
|
|
SetFolderName(NULL);
|
|
}
|
|
|
|
if (fShowDropDownIndicator)
|
|
{
|
|
BOOL fInfoColumn = FALSE;
|
|
if (SUCCEEDED(m_pBrowser->GetViewLayout(DISPID_MSGVIEW_FOLDERLIST, 0, &fInfoColumn, 0, 0)))
|
|
m_fDropDownIndicator = !fInfoColumn;
|
|
}
|
|
|
|
if (fDisplayNameChanged || fShowDropDownIndicator)
|
|
{
|
|
m_fRecalc = TRUE;
|
|
InvalidateRect(m_hwnd, NULL, TRUE);
|
|
}
|
|
}
|
|
|
|
|
|
HRESULT STDMETHODCALLTYPE CFolderBar::DragEnter(IDataObject* pDataObject,
|
|
DWORD grfKeyState,
|
|
POINTL pt, DWORD* pdwEffect)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
DOUTL(32, _T("CFolderBar::DragEnter() - Starting"));
|
|
|
|
// Release Current Data Object
|
|
SafeRelease(m_pDataObject);
|
|
|
|
// Initialize our state
|
|
SafeRelease(m_pDTCur);
|
|
|
|
// Let's get a drop target
|
|
if (FOLDERID_INVALID == m_idFolder)
|
|
return (E_FAIL);
|
|
|
|
// Create the a Drop Target
|
|
CDropTarget *pTarget = new CDropTarget();
|
|
if (pTarget)
|
|
{
|
|
if (FAILED(hr = pTarget->Initialize(m_hwnd, m_idFolder)))
|
|
{
|
|
pTarget->Release();
|
|
return (hr);
|
|
}
|
|
}
|
|
m_pDTCur = pTarget;
|
|
|
|
// Save the Data Object
|
|
m_pDataObject = pDataObject;
|
|
m_pDataObject->AddRef();
|
|
|
|
hr = m_pDTCur->DragEnter(m_pDataObject, grfKeyState, pt, &m_dwEffectCur);
|
|
|
|
// Save Key State
|
|
m_grfKeyState = grfKeyState;
|
|
|
|
// Set the default return value to be failure
|
|
*pdwEffect = m_dwEffectCur;
|
|
|
|
return (S_OK);
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: CFolderBar::DragOver()
|
|
//
|
|
// PURPOSE: This is called as the user drags an object over our target.
|
|
// If we allow this object to be dropped on us, then we will have
|
|
// a pointer in m_pDataObject.
|
|
//
|
|
// PARAMETERS:
|
|
// <in> grfKeyState - Pointer to the current key states
|
|
// <in> pt - Point in screen coordinates of the mouse
|
|
// <out> pdwEffect - Where we return whether this is a valid place for
|
|
// pDataObject to be dropped and if so what type of
|
|
// drop.
|
|
//
|
|
// RETURN VALUE:
|
|
// S_OK - The function succeeded.
|
|
//
|
|
HRESULT STDMETHODCALLTYPE CFolderBar::DragOver(DWORD grfKeyState, POINTL pt,
|
|
DWORD* pdwEffect)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
// If we don't have a stored data object from DragEnter()
|
|
if (m_pDataObject && NULL != m_pDTCur)
|
|
{
|
|
// If the keys changed, we need to re-query the drop target
|
|
if ((m_grfKeyState != grfKeyState) && m_pDTCur)
|
|
{
|
|
m_dwEffectCur = *pdwEffect;
|
|
hr = m_pDTCur->DragOver(grfKeyState, pt, &m_dwEffectCur);
|
|
}
|
|
else
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
|
|
*pdwEffect = m_dwEffectCur;
|
|
m_grfKeyState = grfKeyState;
|
|
}
|
|
|
|
ScreenToClient(m_hwnd, (LPPOINT) &pt);
|
|
DoMouseOver((LPPOINT) &pt, MO_DRAGOVER);
|
|
|
|
return (hr);
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: CFolderBar::DragLeave()
|
|
//
|
|
// PURPOSE: Allows us to release any stored data we have from a successful
|
|
// DragEnter()
|
|
//
|
|
// RETURN VALUE:
|
|
// S_OK - Everything is groovy
|
|
//
|
|
HRESULT STDMETHODCALLTYPE CFolderBar::DragLeave(void)
|
|
{
|
|
POINT pt = {0, 0};
|
|
DOUTL(32, _T("CFolderBarView::DragLeave()"));
|
|
|
|
KillHoverTimer();
|
|
DoMouseOver(&pt, MO_DRAGLEAVE);
|
|
// SetScopeCloseTimer();
|
|
|
|
SafeRelease(m_pDTCur);
|
|
SafeRelease(m_pDataObject);
|
|
|
|
return (S_OK);
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: CFolderBar::Drop()
|
|
//
|
|
// PURPOSE: The user has let go of the object over our target. If we
|
|
// can accept this object we will already have the pDataObject
|
|
// stored in m_pDataObject. If this is a copy or move, then
|
|
// we go ahead and update the store. Otherwise, we bring up
|
|
// a send note with the object attached.
|
|
//
|
|
// PARAMETERS:
|
|
// <in> pDataObject - Pointer to the data object being dragged
|
|
// <in> grfKeyState - Pointer to the current key states
|
|
// <in> pt - Point in screen coordinates of the mouse
|
|
// <out> pdwEffect - Where we return whether this is a valid place for
|
|
// pDataObject to be dropped and if so what type of
|
|
// drop.
|
|
//
|
|
// RETURN VALUE:
|
|
// S_OK - Everything worked OK
|
|
//
|
|
HRESULT STDMETHODCALLTYPE CFolderBar::Drop(IDataObject* pDataObject,
|
|
DWORD grfKeyState, POINTL pt,
|
|
DWORD* pdwEffect)
|
|
{
|
|
HRESULT hr;
|
|
|
|
Assert(m_pDataObject == pDataObject);
|
|
|
|
if (m_pDTCur)
|
|
{
|
|
hr = m_pDTCur->Drop(pDataObject, grfKeyState, pt, pdwEffect);
|
|
}
|
|
else
|
|
{
|
|
*pdwEffect = 0;
|
|
hr = S_OK;
|
|
}
|
|
|
|
KillHoverTimer();
|
|
ScreenToClient(m_hwnd, (LPPOINT) &pt);
|
|
DoMouseOver((LPPOINT) &pt, MO_DRAGDROP);
|
|
|
|
SafeRelease(m_pDataObject);
|
|
SafeRelease(m_pDTCur);
|
|
|
|
return (hr);
|
|
}
|
|
|
|
|
|
|
|
LRESULT CALLBACK CFolderBar::FrameWndProc(HWND hwnd, UINT uMsg, WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
CFolderBar *pThis = (CFolderBar *) GetWndThisPtr(hwnd);
|
|
|
|
switch (uMsg)
|
|
{
|
|
case WM_NCCREATE:
|
|
{
|
|
pThis = (CFolderBar *) ((LPCREATESTRUCT) lParam)->lpCreateParams;
|
|
SetWndThisPtr(hwnd, (LONG_PTR) pThis);
|
|
pThis->m_hwnd = hwnd;
|
|
return (TRUE);
|
|
}
|
|
|
|
case WM_SIZE:
|
|
{
|
|
SetWindowPos(pThis->m_hwnd, NULL, 0, 0, LOWORD(lParam) - CX_MARGIN_RIGHTEDGE,
|
|
HIWORD(lParam) - CY_MARGIN, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
|
|
return (TRUE);
|
|
}
|
|
}
|
|
|
|
return (DefWindowProc(hwnd, uMsg, wParam, lParam));
|
|
}
|
|
|
|
|
|
#define FLYOUTSCOPECLASS _T("FlyOutScope")
|
|
HRESULT CFlyOutScope::HrDisplay(IAthenaBrowser *pBrowser, CFolderBar *pFolderBar,
|
|
HWND hwndParent, HWND *phwndScope)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
RECT rc,
|
|
rcFrame,
|
|
rcView;
|
|
int cx,
|
|
cy,
|
|
cyMax,
|
|
increments,
|
|
dyOffset;
|
|
const cmsAvail = 250;
|
|
DWORD cmsUsed,
|
|
cmsLeft,
|
|
cmsThreshold,
|
|
cmsStart,
|
|
cmsNow;
|
|
|
|
Assert(pBrowser);
|
|
Assert(pFolderBar);
|
|
|
|
m_pBrowser = pBrowser;
|
|
m_pBrowser->AddRef();
|
|
m_pFolderBar = pFolderBar;
|
|
m_fResetParent = FALSE;
|
|
m_hwndParent = hwndParent;
|
|
|
|
m_pFolderBar->GetWindow(&m_hwndFolderBar);
|
|
m_hwndFocus = GetFocus();
|
|
|
|
// Create the control
|
|
WNDCLASSEX wc = {0};
|
|
|
|
// Check to see if we need to register the class first
|
|
wc.cbSize = sizeof(WNDCLASSEX);
|
|
if (!GetClassInfoEx(g_hInst, FLYOUTSCOPECLASS, &wc))
|
|
{
|
|
wc.style = 0;
|
|
wc.lpfnWndProc = FlyWndProc;
|
|
wc.cbClsExtra = 0;
|
|
wc.cbWndExtra = 0;
|
|
wc.hInstance = g_hInst;
|
|
wc.hCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));
|
|
wc.hbrBackground = NULL;
|
|
wc.hIcon = NULL;
|
|
IF_WIN32( wc.hIconSm = NULL; )
|
|
wc.lpszClassName = FLYOUTSCOPECLASS;
|
|
|
|
SideAssert(RegisterClassEx(&wc));
|
|
}
|
|
m_hwnd = CreateWindowEx(WS_EX_TOOLWINDOW, FLYOUTSCOPECLASS, NULL,
|
|
WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0, 0, 0, 0,
|
|
m_hwndParent, 0, g_hInst, (LPVOID) this);
|
|
if (!m_hwnd)
|
|
{
|
|
GetLastError();
|
|
return (E_OUTOFMEMORY);
|
|
}
|
|
|
|
// Get the scope pane from the browser
|
|
m_pBrowser->GetTreeView(&m_pTreeView);
|
|
m_pTreeView->GetWindow(&m_hwndTree);
|
|
m_hwndTreeParent = GetParent(m_hwndTree);
|
|
|
|
// Turn on the pin button
|
|
SendMessage(m_hwndTree, WM_TOGGLE_CLOSE_PIN, 0, TRUE);
|
|
|
|
// Set the focus before changing the parent. In some cases, setting the
|
|
// focus, causes a selection change notification to come through. This
|
|
// makes the explorer split pane think the user has made their selection
|
|
// and shuts down the drop down scope pane before it is even shown!
|
|
|
|
HWND hwndT = GetWindow(m_hwndTree, GW_CHILD);
|
|
SetFocus(m_hwndTree);
|
|
SetParent(m_hwndTree, m_hwnd);
|
|
m_fResetParent = TRUE;
|
|
SetWindowPos(m_hwndTree, NULL, CXY_MARGIN_FLYOUT, CXY_MARGIN_FLYOUT, 0, 0,
|
|
SWP_NOSIZE | SWP_NOZORDER);
|
|
ShowWindow(m_hwndTree, SW_SHOW);
|
|
m_pTreeView->RegisterFlyOut(m_pFolderBar);
|
|
|
|
// Clear the parent for better redraw
|
|
SetParent(m_hwnd, NULL);
|
|
|
|
// Set up for the slide, the final position for the flyout will match the
|
|
// left/top/bottom edges of the view. The width of the scope pane is either
|
|
// 1/3 of the width of the view or CX_MINWIDTH_FLYOUT, whichever is larger.
|
|
|
|
// Get the position & size of the view window
|
|
m_pBrowser->GetViewRect(&rcView);
|
|
MapWindowPoints(m_hwndParent, GetDesktopWindow(), (LPPOINT) &rcView, 2);
|
|
|
|
// Determine the width of the fly-out
|
|
cx = max(CX_MINWIDTH_FLYOUT, ((rcView.right - rcView.left) / 3) + 2 * CXY_MARGIN_FLYOUT);
|
|
|
|
// Calculate the fly-out increments
|
|
cyMax = cy = (rcView.bottom - rcView.top) + (CXY_MARGIN_FLYOUT * 2);
|
|
increments = cy / FLYOUT_INCREMENT;
|
|
cy -= increments * FLYOUT_INCREMENT;
|
|
|
|
// Scope pane is positioned at it's final size so that it's size does not
|
|
// change as we drop the flyout down. This gives better redraw than resizing
|
|
// as the window drops
|
|
SetWindowPos(m_hwndTree, NULL, 0, 0, cx - CXY_MARGIN_FLYOUT * 2, cyMax - CXY_MARGIN_FLYOUT * 2,
|
|
SWP_NOMOVE | SWP_NOZORDER);
|
|
|
|
// Move the window to its initial position
|
|
GetWindowRect(m_hwndFolderBar, &rc);
|
|
MoveWindow(m_hwnd, IS_WINDOW_RTL_MIRRORED(m_hwndParent) ? (rc.right + CXY_MARGIN_FLYOUT - cx) : (rc.left - CXY_MARGIN_FLYOUT), rcView.top - CXY_MARGIN_FLYOUT,
|
|
cx, cy, FALSE);
|
|
ShowWindow(m_hwnd, SW_SHOW);
|
|
SetWindowPos(m_hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
|
|
|
|
#ifndef WIN16
|
|
if (GetSystemMetrics(SM_SLOWMACHINE))
|
|
{
|
|
// On a slow machine, just show the thing
|
|
SetWindowPos(m_hwnd, NULL, 0, 0, cx, cyMax, SWP_NOMOVE | SWP_NOZORDER);
|
|
}
|
|
else
|
|
#endif // !WIN16
|
|
{
|
|
// Whoosh down to the bottom of the frame. We want to do this in ~250ms on any
|
|
// CPU. In order to make this work on differing machine speeds, we double
|
|
// the slide speed everytime the remaining time is halved. If the remaining time
|
|
// is negative, it will finish the slide in one step.
|
|
|
|
dyOffset = FLYOUT_INCREMENT;
|
|
cmsStart = ::GetTickCount();
|
|
cmsThreshold = cmsAvail;
|
|
|
|
while (cy <= cyMax)
|
|
{
|
|
// Slide the window down
|
|
cy += dyOffset;
|
|
SetWindowPos(m_hwnd, NULL, 0, 0, cx, min(cy, cyMax), SWP_NOMOVE | SWP_NOZORDER);
|
|
UpdateWindow(m_hwnd);
|
|
UpdateWindow(m_hwndTree);
|
|
|
|
// Determine the next increment based on time remaining
|
|
cmsNow = GetTickCount();
|
|
cmsUsed = cmsNow - cmsStart;
|
|
if (cmsUsed > cmsAvail && cy < cyMax)
|
|
{
|
|
// Finish it in one step
|
|
cy = cyMax;
|
|
}
|
|
else
|
|
{
|
|
// Double scroll step if time remaining is halved since the
|
|
// last time we double the scroll step
|
|
cmsLeft = cmsAvail - cmsUsed;
|
|
if (cmsLeft < cmsThreshold)
|
|
{
|
|
dyOffset *= 2;
|
|
cmsThreshold /= 2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
*phwndScope = m_hwnd;
|
|
return (hr);
|
|
}
|
|
|
|
|
|
LRESULT CALLBACK CFlyOutScope::FlyWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
CFlyOutScope *pThis = (CFlyOutScope *) GetWndThisPtr(hwnd);
|
|
|
|
switch (uMsg)
|
|
{
|
|
case WM_NCCREATE:
|
|
{
|
|
pThis = (CFlyOutScope *) ((LPCREATESTRUCT) lParam)->lpCreateParams;
|
|
SetWndThisPtr(hwnd, (LONG_PTR) pThis);
|
|
pThis->m_hwnd = hwnd;
|
|
return (TRUE);
|
|
}
|
|
|
|
HANDLE_MSG(hwnd, WM_PAINT, pThis->OnPaint);
|
|
HANDLE_MSG(hwnd, WM_NOTIFY, pThis->OnNotify);
|
|
HANDLE_MSG(hwnd, WM_DESTROY, pThis->OnDestroy);
|
|
HANDLE_MSG(hwnd, WM_SIZE, pThis->OnSize);
|
|
|
|
case WM_NCDESTROY:
|
|
{
|
|
pThis->Release();
|
|
break;
|
|
}
|
|
}
|
|
|
|
return (DefWindowProc(hwnd, uMsg, wParam, lParam));
|
|
}
|
|
|
|
BOOL CFlyOutScope::OnNotify(HWND hwnd, int idFrom, LPNMHDR pnmhdr)
|
|
{
|
|
return (0);
|
|
}
|
|
|
|
|
|
void CFlyOutScope::OnPaint(HWND hwnd)
|
|
{
|
|
HDC hdc;
|
|
RECT rcClient;
|
|
PAINTSTRUCT ps;
|
|
|
|
GetClientRect(hwnd, &rcClient);
|
|
|
|
// Paint the background
|
|
hdc = BeginPaint(hwnd, &ps);
|
|
SetBkColor(hdc, GetSysColor(COLOR_3DFACE));
|
|
ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &ps.rcPaint, NULL, 0, NULL);
|
|
|
|
// Draw the 3D edge
|
|
DrawEdge(hdc, &rcClient, EDGE_RAISED, BF_RECT);
|
|
EndPaint(hwnd, &ps);
|
|
}
|
|
|
|
|
|
void CFlyOutScope::OnSize(HWND hwnd, UINT state, int cx, int cy)
|
|
{
|
|
RECT rc;
|
|
|
|
GetClientRect(hwnd, &rc);
|
|
InvalidateRect(hwnd, &rc, FALSE);
|
|
InflateRect(&rc, -CXY_MARGIN_FLYOUT, -CXY_MARGIN_FLYOUT);
|
|
ValidateRect(hwnd, &rc);
|
|
}
|
|
|
|
|
|
void CFlyOutScope::OnDestroy(HWND hwnd)
|
|
{
|
|
// Make sure to kill any bogus timers still lying around
|
|
m_pFolderBar->KillScopeCloseTimer();
|
|
m_pFolderBar->ScopePaneDied();
|
|
|
|
// Reset the parent of the scope pane back to the browser
|
|
if (m_fResetParent)
|
|
{
|
|
ShowWindow(m_hwndTree, SW_HIDE);
|
|
SendMessage(m_hwndTree, WM_TOGGLE_CLOSE_PIN, 0, FALSE);
|
|
SetParent(m_hwndTree, m_hwndTreeParent);
|
|
m_pTreeView->RevokeFlyOut();
|
|
}
|
|
|
|
// Set the parent of the drop down pane itself back
|
|
SetParent(m_hwnd, m_hwndFolderBar);
|
|
|
|
// $TODO - Review where the focus is supposed to go
|
|
HWND hwndBrowser;
|
|
if (m_pBrowser)
|
|
{
|
|
m_pBrowser->GetWindow(&hwndBrowser);
|
|
PostMessage(hwndBrowser, WM_OESETFOCUS, (WPARAM) m_hwndFocus, 0);
|
|
}
|
|
}
|
|
|
|
|
|
CFlyOutScope::CFlyOutScope()
|
|
{
|
|
m_cRef = 1;
|
|
m_pBrowser = 0;
|
|
m_pFolderBar = 0;
|
|
m_fResetParent = 0;
|
|
m_pTreeView = NULL;
|
|
m_hwnd = NULL;
|
|
m_hwndParent = NULL;
|
|
m_hwndTree = NULL;
|
|
m_hwndFolderBar = NULL;
|
|
}
|
|
|
|
CFlyOutScope::~CFlyOutScope()
|
|
{
|
|
SafeRelease(m_pBrowser);
|
|
SafeRelease(m_pTreeView);
|
|
}
|
|
|
|
ULONG CFlyOutScope::AddRef(void)
|
|
{
|
|
return (++m_cRef);
|
|
}
|
|
|
|
ULONG CFlyOutScope::Release(void)
|
|
{
|
|
ULONG cRefT = --m_cRef;
|
|
|
|
if (m_cRef == 0)
|
|
delete this;
|
|
|
|
return (cRefT);
|
|
}
|
|
|
|
HRESULT CFolderBar::OnConnectionNotify(CONNNOTIFY nCode, LPVOID pvData, CConnectionManager *pConMan)
|
|
{
|
|
m_fRecalc = TRUE;
|
|
InvalidateRect(m_hwnd, NULL, TRUE);
|
|
return S_OK;
|
|
}
|