// ====================================================================================
// Class Implementation :
// COXSHBDropSource & COXSHBDropTarget & COXSHBEdit & COXSHBListCtrl & COXShortcutBar
// ====================================================================================
// Source file : OXShortcutBar.cpp
// Version: 9.3
// This software along with its related components, documentation and files ("The Libraries")
// is � 1994-2007 The Code Project (1612916 Ontario Limited) and use of The Libraries is
// governed by a software license agreement ("Agreement"). Copies of the Agreement are
// available at The Code Project (www.codeproject.com), as part of the package you downloaded
// to obtain this file, or directly from our office. For a copy of the license governing
// this software, you may contact us at legalaffairs@codeproject.com, or by calling 416-849-8900.
//////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include <windowsx.h>
// v93 update 03 - 64-bit
#include "UTB64Bit.h"
#include "OXShortcutBar.h"
#include "OXSkins.h"
#include "UTBStrOp.h"
#pragma warning(disable : 4100 4786 4345)
#include <functional>
#include <set>
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
struct LVITEM_less : public std::binary_function<LVITEM*, LVITEM*, bool>
{
bool operator()(const LVITEM* pItem1, const LVITEM* pItem2) const
{
CString strItem1(pItem1->pszText);
CString strItem2(pItem2->pszText);
return (strItem1 < strItem2);
}
};
// format for drag'n'drop item
CLIPFORMAT COXShortcutBar::m_nChildWndItemFormat=
(CLIPFORMAT)::RegisterClipboardFormat(_T("Shortcut bar child window item"));
/////////////////////////////////////////////////////////////////////////////
// COXSHBEdit
COXSHBEdit::COXSHBEdit()
{
m_pSHBED=NULL;
// by default use standard window background color
COLORREF clrBackground=::GetSysColor(COLOR_WINDOW);
// create brush used to fill background
VERIFY(m_brush.CreateSolidBrush(clrBackground));
m_bIsEditing=FALSE;
}
COXSHBEdit::~COXSHBEdit()
{
delete m_pSHBED;
}
BEGIN_MESSAGE_MAP(COXSHBEdit, CEdit)
//{{AFX_MSG_MAP(COXSHBEdit)
ON_WM_KILLFOCUS()
ON_WM_SETFOCUS()
ON_CONTROL_REFLECT(EN_UPDATE, OnUpdate)
ON_WM_CTLCOLOR_REFLECT()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// COXSHBEdit message handlers
LRESULT COXSHBEdit::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
BOOL bFinishEdit=FALSE;
// we finish editing if user click any mouse button or press Enter or ESC key
switch(message)
{
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_MOUSEMOVE:
case WM_LBUTTONDBLCLK:
case WM_RBUTTONDBLCLK:
case WM_MBUTTONDBLCLK:
{
// check if mouse cursor point is over the control's window
CRect rectClient;
GetClientRect(rectClient);
POINTS points=MAKEPOINTS(lParam);
CPoint point(points.x,points.y);
if(!rectClient.PtInRect(point))
{
::SetCursor(LoadCursor(NULL, IDC_ARROW));
// if the mouse cursor is over some other window then we should forward the
// message to this window and if the mouse message is other than WM_MOUSEMOVE
// then we should finish the editing
bFinishEdit=(message==WM_MOUSEMOVE) ? FALSE : TRUE;
// find target window
ClientToScreen(&point);
CWnd* pWnd=WindowFromPoint(point);
ASSERT_VALID(pWnd);
ASSERT(::IsWindow(pWnd->GetSafeHwnd()));
ASSERT(pWnd!=this);
// clarify type of message that should be sent: WM_NC* and WM_* messages
int nHitTest=(int)pWnd->SendMessage(
WM_NCHITTEST,0,MAKELONG(point.x,point.y));
if(nHitTest==HTCLIENT)
{
pWnd->ScreenToClient(&point);
lParam=MAKELONG(point.x,point.y);
}
else
{
// convert the messages in their WM_NC* counterparts
switch(message)
{
case WM_LBUTTONDOWN:
{
message=WM_NCLBUTTONDOWN;
break;
}
case WM_RBUTTONDOWN:
{
message=WM_NCRBUTTONDOWN;
break;
}
case WM_MBUTTONDOWN:
{
message=WM_NCMBUTTONDOWN;
break;
}
case WM_LBUTTONDBLCLK:
{
message=WM_NCLBUTTONDBLCLK;
break;
}
case WM_RBUTTONDBLCLK:
{
message=WM_NCRBUTTONDBLCLK;
break;
}
case WM_MBUTTONDBLCLK:
{
message=WM_NCMBUTTONDBLCLK;
break;
}
case WM_MOUSEMOVE:
{
message=WM_NCMOUSEMOVE;
break;
}
}
lParam=MAKELONG(point.x,point.y);
}
wParam=nHitTest;
// finish editing if needed
if(bFinishEdit)
{
FinishEdit(TRUE);
}
// forward message to underlaying window
pWnd->SendMessage(message,wParam,lParam);
// change cursor if needed
pWnd->SendMessage(WM_SETCURSOR,(WPARAM)pWnd->GetSafeHwnd(),
(LPARAM)MAKELONG(nHitTest,message));
/* // if we haven't finished editing and we've lost capture then
// recapture mouse messages
if(m_bIsEditing && ::IsWindow(GetSafeHwnd()) &&
::GetCapture()!=GetSafeHwnd())
{
SetCapture();
}*/
}
else
{
::SetCursor(LoadCursor(NULL, IDC_IBEAM));
}
break;
}
case WM_KEYDOWN:
{
// successfully finish editing if Enter key was pressed
if(wParam == VK_RETURN)
{
bFinishEdit=TRUE;
FinishEdit(TRUE);
}
// cancel editing if ESC key was pressed
else if(wParam == VK_ESCAPE)
{
bFinishEdit=TRUE;
FinishEdit(FALSE);
}
break;
}
}
// if we've finished editing then there is no sense to handle message
if(bFinishEdit)
{
return TRUE;
}
LRESULT lResult=CEdit::WindowProc(message,wParam,lParam);
// restore capture if we lost it as a result of
// processing the current message
if(m_bIsEditing && ::IsWindow(GetSafeHwnd()) && ::GetCapture()!=GetSafeHwnd())
{
SetCapture();
}
return lResult;
}
void COXSHBEdit::OnKillFocus(CWnd* pNewWnd)
{
CEdit::OnKillFocus(pNewWnd);
// TODO: Add your message handler code here
// finish editing if we haven't done it previously
if(::GetCapture()==GetSafeHwnd())
{
FinishEdit(TRUE);
}
}
void COXSHBEdit::OnSetFocus(CWnd* pOldWnd)
{
CEdit::OnSetFocus(pOldWnd);
// TODO: Add your message handler code here
// capture mouse messages
SetCapture();
}
void COXSHBEdit::OnUpdate()
{
// TODO: If this is a RICHEDIT control, the control will not
// send this notification unless you override the CEdit::OnInitDialog()
// function to send the EM_SETEVENTMASK message to the control
// with the ENM_UPDATE flag ORed into the lParam mask.
// TODO: Add your control notification handler code here
// here is the place where we enlarge edit control window if needed
//
if(m_pSHBED && (m_pSHBED->nMask&SHBEIF_ENLARGE)!=0 &&
(m_pSHBED->nMask&SHBEIF_RECTMAX)!=0 && m_pSHBED->bEnlargeAsTyping)
{
// get window rect
CRect rectWindow;
GetWindowRect(rectWindow);
// get typed text
CString sText;
GetWindowText(sText);
// get edit style to take into account text allignment while recalculating
// control's window coordinates
DWORD dwStyle=GetStyle();
// define min rect that edit control window can be (if min rect is not specified
// explicitly then we use as min the rect of window that was set in StartEdit
// function)
CRect rectMin;
if((m_pSHBED->nMask&SHBEIF_RECTMIN)!=0)
{
rectMin=m_pSHBED->rectMin;
}
else
{
rectMin=m_pSHBED->rectDisplay;
}
// min rect mustn't be empty
ASSERT(!rectMin.IsRectEmpty());
if(!sText.IsEmpty())
{
// window rect mustn't be more than max rect
ASSERT(rectWindow.Width()<=m_pSHBED->rectMax.Width() &&
rectWindow.Height()<=m_pSHBED->rectMax.Height() &&
rectWindow.Width()>=rectMin.Width() &&
rectWindow.Height()>=rectMin.Height());
// get the rect used to display text
CRect rectDisplay;
GetRect(rectDisplay);
ClientToScreen(&rectDisplay);
// get margins between display and window rects to keep them in
// enlarged/shrinked window
CRect rectMargins(rectDisplay.left-rectWindow.left,
rectWindow.right-rectDisplay.right,rectDisplay.top-rectWindow.top,
rectWindow.bottom-rectDisplay.bottom);
// set the font used to draw text
CClientDC dc(this);
CFont* pOldFont=NULL;
if((HFONT)m_font)
{
pOldFont=dc.SelectObject(&m_font);
}
// calculate the size of updated window text constrained by the size
// of max rect
CRect rectMax=m_pSHBED->rectMax;
rectMax-=rectMax.TopLeft();
CRect rectText=rectMax;
rectText.right-=(rectMargins.left+rectMargins.right);
rectText.bottom-=(rectMargins.top+rectMargins.bottom);
// calculate
dc.DrawText(sText,rectText,DT_CALCRECT|DT_VCENTER|DT_CENTER|
((dwStyle&ES_MULTILINE)!=0 ? DT_WORDBREAK|DT_EDITCONTROL : 0));
// take into account original margins
rectText.right+=(rectMargins.left+rectMargins.right);
rectText.bottom+=(rectMargins.top+rectMargins.bottom);
// don't forget about max rect
VERIFY(rectText.IntersectRect(rectMax,rectText));
// make sure text rect is no less than min rect
//
if(rectText.Width()<rectMin.Width())
{
rectText.right=rectText.left+rectMin.Width();
}
if(rectText.Height()<rectMin.Height())
{
rectText.bottom=rectText.top+rectMin.Height();
}
// calculate enlarged/shrinked edit control window rect taking into
// account text alignment
//
CRect rectEnlarged=m_pSHBED->rectMax;
// width
int nXMargin=rectMax.Width()-rectText.Width();
ASSERT(nXMargin>=0);
if(nXMargin>0)
{
if((dwStyle&ES_RIGHT)!=0)
{
rectEnlarged.left+=nXMargin;
}
else if((dwStyle&ES_CENTER)!=0)
{
rectEnlarged.right-=nXMargin/2;
rectEnlarged.left=rectEnlarged.right-rectText.Width();
}
else
{
//ES_LEFT
rectEnlarged.right-=nXMargin;
}
}
// height
int nYMargin=rectMax.Height()-rectText.Height();
ASSERT(nYMargin>=0);
if(nYMargin>=0)
{
rectEnlarged.bottom-=nYMargin;
}
// resize it!
MoveWindow(rectEnlarged);
if(pOldFont)
{
dc.SelectObject(pOldFont);
}
}
else
{
// resize it!
MoveWindow(rectMin);
}
// get client rect
CRect rectClient;
GetClientRect(rectClient);
// set the rect used to display text
SetRectNP(rectClient);
}
}
HBRUSH COXSHBEdit::CtlColor(CDC* pDC, UINT nCtlColor)
{
// TODO: Change any attributes of the DC here
UNREFERENCED_PARAMETER(nCtlColor);
ASSERT(nCtlColor==CTLCOLOR_EDIT);
// fill the background
COLORREF clrBackground=::GetSysColor(COLOR_WINDOW);
if((m_pSHBED->nMask&SHBEIF_CLRBACK))
{
clrBackground=m_pSHBED->clrBack;
}
pDC->SetBkColor(clrBackground);
if((m_pSHBED->nMask&SHBEIF_CLRTEXT))
{
pDC->SetTextColor(m_pSHBED->clrText);
}
// use our brush to fill the part of the control that is not covered by text
return (HBRUSH)m_brush;
// TODO: Return a non-NULL brush if the parent's handler should not be called
}
BOOL COXSHBEdit::StartEdit(LPSHBE_DISPLAY pSHBED)
{
// window have to be created at this moment
ASSERT(::IsWindow(m_hWnd));
// verify SHBE_DISPLAY structure
if(pSHBED && !VerifyDisplayInfo(pSHBED))
{
TRACE(_T("COXSHBEdit::StartEdit: LPSHBE_DISPLAY is invalid"));
return FALSE;
}
// save style, text, ctrlID, parent window and size
DWORD dwStyle=GetStyle();
CString sText;
GetWindowText(sText);
CRect rectWindow;
GetWindowRect(rectWindow);
UINT nCtrlID=GetDlgCtrlID();
CWnd* pWnd=GetParent();
ASSERT(pWnd);
// if pSHBED is not NULL then get new attributes
if(pSHBED)
{
// save SHBE_DISPLAY structure
if(m_pSHBED==NULL)
{
m_pSHBED=new SHBE_DISPLAY;
}
m_pSHBED->Copy(pSHBED);
// get new window text
if((m_pSHBED->nMask&SHBEIF_TEXT)!=0)
{
sText=m_pSHBED->lpText;
}
// get new window rect
if((m_pSHBED->nMask&SHBEIF_RECTDISPLAY)!=0)
{
rectWindow=m_pSHBED->rectDisplay;
}
// get new edit control style
if((m_pSHBED->nMask&SHBEIF_STYLE)!=0)
{
dwStyle=m_pSHBED->dwStyle;
}
// get font to be used to draw text
if((m_pSHBED->nMask&SHBEIF_FONT)!=0)
{
if((HFONT)m_font)
{
m_font.DeleteObject();
}
if(m_pSHBED->pFont)
{
LOGFONT lf;
if(m_pSHBED->pFont->GetLogFont(&lf)!=0)
{
VERIFY(m_font.CreateFontIndirect(&lf));
}
}
}
// adjust window to be compliant with min rect
if((m_pSHBED->nMask&SHBEIF_RECTMIN)!=0)
{
rectWindow.left=__min(m_pSHBED->rectMin.left,rectWindow.left);
rectWindow.top=__min(m_pSHBED->rectMin.top,rectWindow.top);
rectWindow.right=__max(m_pSHBED->rectMin.right,rectWindow.right);
rectWindow.bottom=__max(m_pSHBED->rectMin.bottom,rectWindow.bottom);
}
}
else
{
delete m_pSHBED;
}
// destroy the edit control
if(DestroyWindow()==0)
{
TRACE(_T("COXSHBEdit::StartEdit: failed to destroy window"));
return FALSE;
}
// recreate it using saved and updated properties
if(!Create(dwStyle,rectWindow,pWnd,nCtrlID))
{
TRACE(_T("COXSHBEdit::StartEdit: failed to create window"));
return FALSE;
}
// set new font to window
if((HFONT)m_font)
{
SetFont(&m_font,TRUE);
}
// change the text of window
SetWindowText(sText);
// set background brush
if((m_pSHBED->nMask&SHBEIF_CLRBACK))
{
COLORREF clrBackground=m_pSHBED->clrBack;
if((HBRUSH)m_brush!=NULL)
{
m_brush.DeleteObject();
}
VERIFY(m_brush.CreateSolidBrush(clrBackground));
}
// set selection
if(m_pSHBED && (m_pSHBED->nMask&SHBEIF_SELRANGE)!=0)
{
SetSel(m_pSHBED->ptSelRange.x,m_pSHBED->ptSelRange.y,TRUE);
}
// get client rect
CRect rectClient;
GetClientRect(rectClient);
// set the rect used to display text
SetRect(rectClient);
// show edit control and set the focus to it
m_bIsEditing=TRUE;
ShowWindow(SW_SHOW);
SetFocus();
return TRUE;
}
void COXSHBEdit::FinishEdit(BOOL bOK)
{
// window should be created at this moment
ASSERT(::IsWindow(GetSafeHwnd()));
m_bIsEditing=FALSE;
// release capture if we've still got it
if(GetCapture()==this)
{
ReleaseCapture();
}
// Send Notification to parent
NMSHBEDIT nmshbe;
nmshbe.hdr.code=SHBEN_FINISHEDIT;
nmshbe.bOK=bOK;
SendSHBENotification(&nmshbe);
// hide the window
ShowWindow(SW_HIDE);
}
// helper function to send edit control notifications
LRESULT COXSHBEdit::SendSHBENotification(LPNMSHBEDIT pNMSHBE)
{
// notify parent
CWnd* pParentWnd=GetParent();
VERIFY(pParentWnd);
// fill notification structure
pNMSHBE->hdr.hwndFrom=GetSafeHwnd();
pNMSHBE->hdr.idFrom=GetDlgCtrlID();
// send the notification message
return (pParentWnd->SendMessage(
WM_NOTIFY,(WPARAM)GetDlgCtrlID(),(LPARAM)pNMSHBE));
}
BOOL COXSHBEdit::VerifyDisplayInfo(LPSHBE_DISPLAY pSHBED)
{
ASSERT(pSHBED);
// verify mask
if((pSHBED->nMask&~(SHBEIF_TEXT|SHBEIF_RECTDISPLAY|SHBEIF_RECTMAX|
SHBEIF_STYLE|SHBEIF_ENLARGE|SHBEIF_SELRANGE|SHBEIF_FONT|SHBEIF_RECTMIN|
SHBEIF_CLRTEXT|SHBEIF_CLRBACK))!=0)
{
TRACE(_T("COXSHBEdit::VerifyDisplayInfo: unspecified mask used"));
return FALSE;
}
// verify if max rect have to be specified
if((pSHBED->nMask&SHBEIF_ENLARGE)!=0 && (pSHBED->nMask&SHBEIF_RECTMAX)==0)
{
TRACE(_T("COXSHBEdit::VerifyDisplayInfo: enlargement as typing is specified while max rect is not"));
return FALSE;
}
// verify selection if any set
if((pSHBED->nMask&SHBEIF_SELRANGE)!=0 && !(pSHBED->ptSelRange.x==0 &&
pSHBED->ptSelRange.y==-1) && !(pSHBED->ptSelRange.x==-1))
{
CString sText;
if((pSHBED->nMask&SHBEIF_TEXT)!=0)
{
sText=pSHBED->lpText;
}
else
{
GetWindowText(sText);
}
int nLength=sText.GetLength();
if(pSHBED->ptSelRange.x>=nLength || pSHBED->ptSelRange.y>=nLength)
{
TRACE(_T("COXSHBEdit::VerifyDisplayInfo: invalid selection range"));
return FALSE;
}
}
// verify if rectDisplay is not empty
CRect rectDisplay;
GetClientRect(rectDisplay);
if(((pSHBED->nMask&SHBEIF_RECTDISPLAY)!=0 && pSHBED->rectDisplay.IsRectEmpty()) ||
((pSHBED->nMask&SHBEIF_RECTDISPLAY)==0 && rectDisplay.IsRectEmpty()))
{
TRACE(_T("COXSHBEdit::VerifyDisplayInfo: empty display rect specified"));
return FALSE;
}
else
{
rectDisplay=pSHBED->rectDisplay;
}
// verify if display rect is within max rect
CRect rectIntersect;
if((pSHBED->nMask&SHBEIF_RECTMAX)!=0)
{
rectIntersect.IntersectRect(pSHBED->rectMax,rectDisplay);
if(rectIntersect!=rectDisplay)
{
TRACE(_T("COXSHBEdit::VerifyDisplayInfo: display rect is bigger than specified max rect"));
return FALSE;
}
}
// verify if min rect is within max rect
if((pSHBED->nMask&SHBEIF_RECTMIN)!=0 && (pSHBED->nMask&SHBEIF_RECTMAX)!=0)
{
rectIntersect.IntersectRect(pSHBED->rectMax,pSHBED->rectMin);
if(rectIntersect!=pSHBED->rectMin)
{
TRACE(_T("COXSHBEdit::VerifyDisplayInfo: max rect doesn't include min rect"));
return FALSE;
}
}
// verify if min rect is not empty
if((pSHBED->nMask&SHBEIF_RECTMIN)!=0 && (pSHBED->rectMin.IsRectEmpty()))
{
TRACE(_T("COXSHBEdit::VerifyDisplayInfo: min rect mustn't be empty"));
return FALSE;
}
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// COXSHBListCtrl
DWORD COXSHBListCtrl::m_dwComCtlVersion=0;
const int IDT_OXSHBLIST_CHECKMOUSE=275;
const int ID_OXSHBLIST_CHECKMOUSE_DELAY=200;
IMPLEMENT_DYNCREATE(COXSHBListCtrl, CListCtrl)
COXSHBListCtrl::COXSHBListCtrl()
{
m_pShortcutBar=NULL;
m_hGroup=NULL;
m_nSelectedItem=-1;
m_nHotItem=-1;
m_nLastHotItem=-1;
m_nActiveItem=-1;
m_nDropHilightItem=-1;
m_nDragItem=-1;
m_nEditItem=-1;
m_rectTopScrollButton.SetRectEmpty();
m_rectBottomScrollButton.SetRectEmpty();
m_ptOrigin=CPoint(0,0);
m_bMouseIsOver=FALSE;
m_bScrollingUp=FALSE;
m_bScrollingDown=FALSE;
m_bAutoScrolling=FALSE;
m_bCreatingDragImage=FALSE;
m_pDragImage=NULL;
m_nSuspectDragItem=-1;
m_bDragDropOwner=FALSE;
m_bDragDropOperation=FALSE;
m_nScrollTimerID=0;
m_nCheckMouseTimerID=0;
m_nInsertItemBefore=-1;
if(m_dwComCtlVersion==0)
{
DWORD dwMajor, dwMinor;
if(SUCCEEDED(GetComCtlVersion(&dwMajor, &dwMinor)))
{
m_dwComCtlVersion=MAKELONG((WORD)dwMinor, (WORD)dwMajor);
}
else
{
// assume that neither IE 3.0 nor IE 4.0 installed
m_dwComCtlVersion=0x00040000;
}
}
}
COXSHBListCtrl::~COXSHBListCtrl()
{
// clean up maps of all rectangles
m_mapItemToBoundRect.RemoveAll();
m_mapItemToImageRect.RemoveAll();
m_mapItemToTextRect.RemoveAll();
delete m_pDragImage;
if(::IsWindow(GetSafeHwnd()))
DestroyWindow();
}
BEGIN_MESSAGE_MAP(COXSHBListCtrl, CListCtrl)
//{{AFX_MSG_MAP(COXSHBListCtrl)
ON_WM_ERASEBKGND()
ON_WM_PAINT()
ON_WM_SIZE()
ON_WM_MOUSEMOVE()
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONUP()
ON_WM_RBUTTONDOWN()
ON_WM_RBUTTONUP()
ON_WM_MBUTTONDOWN()
ON_WM_MBUTTONUP()
ON_WM_TIMER()
ON_WM_DESTROY()
ON_WM_LBUTTONDBLCLK()
//}}AFX_MSG_MAP
ON_MESSAGE(SHBLCM_HANDLEDRAG, OnHandleDrag)
ON_MESSAGE(SHBLCM_CHECKCAPTURE, OnCheckCapture)
ON_NOTIFY_EX(TTN_NEEDTEXTA, 0, OnItemToolTip)
ON_NOTIFY_EX(TTN_NEEDTEXTW, 0, OnItemToolTip)
ON_MESSAGE(SHBDTM_DRAGENTER, OnDragEnter)
ON_MESSAGE(SHBDTM_DRAGLEAVE, OnDragLeave)
ON_MESSAGE(SHBDTM_DRAGOVER, OnDragOver)
ON_MESSAGE(SHBDTM_DROP, OnDrop)
ON_MESSAGE(SHBM_SETSHBGROUP, OnSetSHBGroup)
ON_MESSAGE(SHBM_SHBINFOCHANGED, OnSHBInfoChanged)
ON_MESSAGE(SHBM_GROUPINFOCHANGED, OnGroupInfoChanged)
ON_MESSAGE(SHBM_POPULATECONTEXTMENU, OnPopulateContextMenu)
ON_NOTIFY(SHBEN_FINISHEDIT, SHB_IDCEDIT, OnChangeItemText)
ON_MESSAGE(LVM_CREATEDRAGIMAGE, OnCreateDragImage)
ON_MESSAGE(LVM_DELETEALLITEMS, OnDeleteAllItems)
ON_MESSAGE(LVM_DELETEITEM, OnDeleteItem)
ON_MESSAGE(LVM_EDITLABEL, OnEditLabel)
ON_MESSAGE(LVM_ENSUREVISIBLE, OnEnsureVisible)
ON_MESSAGE(LVM_FINDITEM, OnFindItem)
ON_MESSAGE(LVM_GETBKCOLOR, OnGetBkColor)
ON_MESSAGE(LVM_GETCOUNTPERPAGE, OnGetCountPerPage)
ON_MESSAGE(LVM_GETEDITCONTROL, OnGetEditControl)
ON_MESSAGE(LVM_GETHOTITEM, OnGetHotItem)
ON_MESSAGE(LVM_GETITEMPOSITION, OnGetItemPosition)
ON_MESSAGE(LVM_GETITEMRECT, OnGetItemRect)
ON_MESSAGE(LVM_GETORIGIN, OnGetOrigin)
ON_MESSAGE(LVM_GETTEXTBKCOLOR, OnGetTextBkColor)
ON_MESSAGE(LVM_GETTEXTCOLOR, OnGetTextColor)
ON_MESSAGE(LVM_GETTOPINDEX, OnGetTopIndex)
ON_MESSAGE(LVM_GETVIEWRECT, OnGetViewRect)
ON_MESSAGE(LVM_HITTEST, OnHitTest)
ON_MESSAGE(LVM_INSERTITEM, OnInsertItem)
ON_MESSAGE(LVM_REDRAWITEMS, OnRedrawItems)
ON_MESSAGE(LVM_SCROLL, OnScroll)
ON_MESSAGE(LVM_SETBKCOLOR, OnSetBkColor)
ON_MESSAGE(LVM_SETHOTITEM, OnSetHotItem)
ON_MESSAGE(LVM_SETITEM, OnSetItem)
ON_MESSAGE(LVM_SETITEMTEXT, OnSetItemText)
ON_MESSAGE(LVM_SETTEXTBKCOLOR, OnSetTextBkColor)
ON_MESSAGE(LVM_SETTEXTCOLOR, OnSetTextColor)
ON_MESSAGE(LVM_SORTITEMS, OnSortItems)
ON_MESSAGE(LVM_UPDATE, OnUpdate)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// COXSHBListCtrl message handlers
void COXSHBListCtrl::PreSubclassWindow()
{
// TODO: Add your specialized code here and/or call the base class
// Currently we support only LVS_ICON and LVS_SMALLICON views
DWORD dwStyle=GetStyle();
if((dwStyle&LVS_LIST)!=0)
{
ModifyStyle(LVS_LIST,0);
ModifyStyle(0,LVS_ICON);
}
if((dwStyle&LVS_REPORT)!=0)
{
ModifyStyle(LVS_REPORT,0);
ModifyStyle(0,LVS_ICON);
}
_AFX_THREAD_STATE* pThreadState=AfxGetThreadState();
// hook not already in progress
if(pThreadState->m_pWndInit==NULL)
{
if(!InitListControl())
{
TRACE(_T("COXSHBListCtrl::PreSubclassWindow: failed to initialize list control\n"));
}
}
CListCtrl::PreSubclassWindow();
}
BOOL COXSHBListCtrl::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID)
{
BOOL bResult=CListCtrl::Create(dwStyle, rect, pParentWnd, nID);
if(bResult)
{
// Initialize edit control
if(!InitListControl())
{
TRACE(_T("COXSHBListCtrl::Create: failed to initialize list control"));
return FALSE;
}
}
return bResult;
}
OXINTRET COXSHBListCtrl::OnToolHitTest(CPoint point, TOOLINFO* pTI) const
{
ASSERT_VALID(this);
ASSERT(::IsWindow(m_hWnd));
if((GetBarStyle()&SHBS_INFOTIP)==0)
return -1;
// now hit test against COXSHBListCtrl items
UINT nHitFlags;
int nHit=HitTest(point,&nHitFlags,TRUE);
if(nHitFlags==SHBLC_ONITEM)
nHit=-1;
if(nHit!=-1 && pTI!= NULL)
{
#if _MFC_VER<=0x0421
if(pTI->cbSize>=sizeof(TOOLINFO))
return nHit;
#endif
// set window handle
pTI->hwnd=GetSafeHwnd();
// get item bounding rect
CRect rect;
VERIFY(GetItemRect(nHit,rect,LVIR_BOUNDS));
pTI->rect=rect;
// found matching item, return its index + 1, we cannot use zero index
nHit++;
pTI->uId=nHit;
// set text to LPSTR_TEXTCALLBACK in order to get TTN_NEEDTEXT message when
// it's time to display tool tip
pTI->lpszText=LPSTR_TEXTCALLBACK;
}
return nHit;
}
BOOL COXSHBListCtrl::OnEraseBkgnd(CDC* pDC)
{
// TODO: Add your message handler code here and/or call default
UNREFERENCED_PARAMETER(pDC);
return TRUE;
}
void COXSHBListCtrl::OnPaint()
{
CPaintDC dc(this); // device context for painting
// TODO: Add your message handler code here
// We provide all drawing functionality. We optimized it as good as it possible.
// get client rect
CRect rect;
GetClientRect(rect);
// Save DC
int nSavedDC=dc.SaveDC();
ASSERT(nSavedDC);
// draw scroll buttons if we have to
if((GetBarStyle()&SHBS_NOSCROLL)==0)
{
DrawScrollButtons(&dc);
dc.ExcludeClipRect(m_rectTopScrollButton);
dc.ExcludeClipRect(m_rectBottomScrollButton);
}
// fill background with color assoicated with control
FillBackground(&dc);
// check integrity
int nCount=GetItemCount();
VERIFY(m_mapItemToBoundRect.GetCount()==nCount);
VERIFY(m_mapItemToTextRect.GetCount()==nCount);
// draw items
for(int nIndex=0; nIndex<nCount; nIndex++)
{
VERIFY(m_mapItemToBoundRect.Lookup(nIndex,rect));
// take into account view origin
rect-=m_ptOrigin;
// draw only if visible
if(dc.RectVisible(&rect))
{
// to reduce flickering use compatible device context to draw in
//
CRect rectItem=rect;
rectItem-=rect.TopLeft();
CDC dcCompatible;
if(!dcCompatible.CreateCompatibleDC(&dc))
{
TRACE(_T("COXSHBListCtrl::OnPaint:Failed to create compatible DC"));
return;
}
CBitmap bitmap;
if(!bitmap.CreateCompatibleBitmap(&dc, rectItem.Width(),
rectItem.Height()))
{
TRACE(_T("COXSHBListCtrl::OnPaint:Failed to create compatible bitmap"));
return;
}
CBitmap* pOldBitmap=dcCompatible.SelectObject(&bitmap);
// fill standard DRAWITEMSTRUCT struct
DRAWITEMSTRUCT dis;
dis.CtlType=ODT_LISTVIEW;
dis.CtlID=GetDlgCtrlID();
dis.itemID=(UINT)nIndex;
dis.itemAction=ODA_DRAWENTIRE;
dis.itemState=ODS_DEFAULT;
dis.hwndItem=GetSafeHwnd();
dis.hDC=dcCompatible.GetSafeHdc();
dis.rcItem=rectItem;
dis.itemData=(DWORD)0;
if (m_pShortcutBar != NULL)
{
if (HasBkColor())
m_pShortcutBar->GetShortcutBarSkin()->FillBackground(&dcCompatible, rect, this,
TRUE, GetBkColor());
else
m_pShortcutBar->GetShortcutBarSkin()->FillBackground(&dcCompatible, rect, this);
}
else
{
COLORREF clrBackground=GetBkColor();
CBrush brush(clrBackground);
dc.FillRect(rectItem,&brush);
}
// this function is responsible for drawing of any item
DrawItem(&dis);
// copy drawn image
dc.BitBlt(rect.left, rect.top, rect.Width(),
rect.Height(), &dcCompatible, 0, 0, SRCCOPY);
if(pOldBitmap)
dcCompatible.SelectObject(pOldBitmap);
}
}
DrawPlaceHolder(&dc);
// Restore dc
dc.RestoreDC(nSavedDC);
// Do not call CWnd::OnPaint() for painting messages
}
void COXSHBListCtrl::OnSize(UINT nType, int cx, int cy)
{
CListCtrl::OnSize(nType, cx, cy);
// TODO: Add your message handler code here
// recalculate items positions
CalculateBoundRects();
}
void COXSHBListCtrl::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CListCtrl::OnMouseMove(nFlags, point);
// as soon as mouse is over the control we capture all mouse messages
// (don't do anything if we are editing group header text)
if(GetEditItem()==-1 && ((m_pShortcutBar==NULL) ? TRUE :
(m_pShortcutBar->GetEditGroup()==NULL)))
{
CheckCapture();
}
}
void COXSHBListCtrl::OnLButtonDblClk(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
// we don't handle double clicks
COXSHBListCtrl::OnLButtonDown(nFlags, point);
}
void COXSHBListCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
UNREFERENCED_PARAMETER(nFlags);
// we should probably activate underneath window
CWnd* pWnd=GetParent();
ASSERT(pWnd);
pWnd->ShowWindow(SW_SHOW);
// find if mouse left button was pressed over any item
UINT nHitFlags;
int nItem=HitTest(point,&nHitFlags);
if(nItem!=-1 && nHitFlags!=SHBLC_ONITEM)
{
// mark this item as selected
int nOldSelectedItem=SelectItem(nItem);
// notify parent of starting drag'n'drop if needed
if((GetBarStyle()&SHBS_DISABLEDRAGITEM)==0)
{
// in result of selecting we could have done any reposition
// so we'd better double check if selected item is still there
int nSuspectItem=HitTest(point,&nHitFlags);
if(nItem!=-1 && nHitFlags!=SHBLC_ONITEM && nSuspectItem==nItem)
{
// there shouldn't be any item currently being dragged
ASSERT(GetDragItem()==-1);
m_ptClickedLButton=point;
// define the point of hot spot on future drag image
if((GetBarStyle()&SHBS_DRAWITEMDRAGIMAGE)!=0)
{
CRect rectImageText;
VERIFY(GetItemRect(nItem,&rectImageText,LVIR_BOUNDS));
ASSERT(rectImageText.PtInRect(m_ptClickedLButton));
m_ptDragImage=m_ptClickedLButton-rectImageText.TopLeft();
}
// mark the item as possible drag item (we initialize drag and drop
// operation when user move cursor while mouse left button is down)
m_nSuspectDragItem=nItem;
}
}
// update selected and old selected items
Update(nItem);
if(nOldSelectedItem!=-1 && nOldSelectedItem!=nItem)
Update(nOldSelectedItem);
}
// check if we have to start scrolling
if(nHitFlags==SHBLC_ONTOPSCROLLBUTTON)
{
StartScrolling(TRUE);
}
else if(nHitFlags==SHBLC_ONBOTTOMSCROLLBUTTON)
{
StartScrolling(FALSE);
}
else if(m_bAutoScrolling)
{
StopScrolling();
}
// check if we lost mouse capture
PostCheckCapture();
}
void COXSHBListCtrl::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CWnd::OnLButtonUp(nFlags, point);
// any drag'n'drop operation must be finished at this moment
ASSERT(GetDragItem()==-1);
// reset suspect drag item index
m_nSuspectDragItem=-1;
// find if mouse left button was unpressed over any item
UINT nHitFlags;
int nItem=HitTest(point,&nHitFlags);
if(nItem!=-1 && nHitFlags!=SHBLC_ONITEM)
{
// if it is the selected item then activate it
if(GetSelectedItem()==nItem)
{
int nOldItem=ActivateItem(nItem);
if(nOldItem!=-1 && nOldItem!=nItem)
// redraw old active item (only one item can be active at the moment)
Update(nOldItem);
}
// redraw new active item
Update(nItem);
}
// double check
ASSERT(!(m_bScrollingUp&m_bScrollingDown));
// stop any scrolling
if(m_bScrollingUp || m_bScrollingDown)
StopScrolling();
// check if we lost mouse capture
PostCheckCapture();
}
void COXSHBListCtrl::OnMButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
UNREFERENCED_PARAMETER(nFlags);
UNREFERENCED_PARAMETER(point);
// check if we lost mouse capture
PostCheckCapture();
}
void COXSHBListCtrl::OnMButtonUp(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CListCtrl::OnMButtonUp(nFlags, point);
// check if we lost mouse capture
PostCheckCapture();
}
void COXSHBListCtrl::OnRButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
UNREFERENCED_PARAMETER(nFlags);
UNREFERENCED_PARAMETER(point);
// check if we lost mouse capture
PostCheckCapture();
}
void COXSHBListCtrl::OnRButtonUp(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CListCtrl::OnRButtonUp(nFlags, point);
if(m_dwComCtlVersion<_IE40_COMCTL_VERSION)
{
ClientToScreen(&point);
PostMessage(WM_CONTEXTMENU,(WPARAM)GetSafeHwnd(),
(LPARAM)MAKELONG(point.x,point.y));
}
// check if we lost mouse capture
PostCheckCapture();
}
/////////////////////////////////////////////////////////////////////////////
// OnHandleDrag sets up the drag image, calls DoDragDrop and cleans up
// after the drag drop operation finishes.
// v9.3 - update 03 - 64-bit - changed LONG ret type to LRESULT
LRESULT COXSHBListCtrl::OnHandleDrag(WPARAM wParam, LPARAM lParam)
{
// In the case this control was created as a child window of shortcut bar group
// fill structure for notification of parent window of this shortcut bar
NMSHORTCUTBAR nmshb;
nmshb.hdr.code=SHBN_HANDLEDRAGITEM;
nmshb.hGroup=m_hGroup;
nmshb.nItem=(int)wParam;
nmshb.lParam=lParam;
// check if parent of the shortcut bar would like to handle drag'n'drop itself
if(SendSHBNotification(&nmshb))
return (LONG)0;
// get data source object that is represented by lParam. It's our responsibility
// to eventually delete this object
COleDataSource* pDataSource=(COleDataSource*)lParam;
ASSERT(pDataSource!=NULL);
// get item index which is represented by wParam
int nItem=(int)wParam;
// validate item index
ASSERT(GetDragItem()==nItem);
ASSERT(nItem>=0 && nItem<GetItemCount());
// unselect the item that will be dragged
VERIFY(SelectItem(-1)==nItem);
Update(nItem);
// get current cursor position
POINT point;
GetCursorPos(&point);
// get the drop source object
COleDropSource* pOleDropSource=GetDropSource();
ASSERT(pOleDropSource!=NULL);
// mark the control as the one which launched drag'n'drop operation
m_bDragDropOwner=TRUE;
// create drag image and start dragging
if((GetBarStyle()&SHBS_DRAWITEMDRAGIMAGE)!=0)
{
// delete previously created drag image
if(m_pDragImage)
{
delete m_pDragImage;
m_pDragImage=NULL;
}
m_pDragImage=CreateDragImage(nItem);
ASSERT(m_pDragImage);
// changes the cursor to the drag image
VERIFY(m_pDragImage->BeginDrag(0,m_ptDragImage));
}
// start drag'n'drop operation
DROPEFFECT dropEffect=((COleDataSource*)pDataSource)->
DoDragDrop(DROPEFFECT_COPY|DROPEFFECT_MOVE,NULL,pOleDropSource);
if(DROPEFFECT_MOVE==dropEffect)
{
// if item was moved in to theCalendar same list control and inserted index
// is less or equal to the dragged one then increase the index by one to
// remove the item with right index
if(m_nInsertItemBefore!=-1 && m_nInsertItemBefore<=GetDragItem())
nItem++;
// delete item if it was moved
DeleteItem(nItem);
}
int nOldInsertItemBefore=m_nInsertItemBefore;
m_nInsertItemBefore=-1;
// unmark as the control which launched drag'n'drop operation
m_bDragDropOwner=FALSE;
// remove drag image
if((GetBarStyle()&SHBS_DRAWITEMDRAGIMAGE)!=0)
{
ASSERT(m_pDragImage);
// end dragging
m_pDragImage->EndDrag();
}
// reset drag item
SetDragItem(-1);
//delete drag source (we are responsible to do that)
delete pDataSource;
// redraw the list control if any item was deleted and/or inserted
if(DROPEFFECT_MOVE==dropEffect ||
(DROPEFFECT_COPY==dropEffect && nOldInsertItemBefore!=-1))
RedrawWindow();
GetCursorPos(&point);
ScreenToClient(&point);
// send WM_LBUTTONUP message
SendMessage(WM_LBUTTONUP,((GetKeyState(VK_CONTROL)<0) ? MK_CONTROL : 0)|
((GetKeyState(VK_SHIFT)<0) ? MK_SHIFT : 0),(LPARAM)MAKELONG(point.x,point.y));
// In the case this control was created as a child window of shortcut bar group
// fill structure for notification of parent window of this shortcut bar
::ZeroMemory((void*)&nmshb,sizeof(nmshb));
nmshb.hdr.code=SHBN_ENDDRAGDROPITEM;
nmshb.hGroup=m_hGroup;
nmshb.lParam=dropEffect;
// notify parent
SendSHBNotification(&nmshb);
return (LONG)0;
}
// v9.3 - update 03 - 64-bit - changed LONG ret type to LRESULT
LRESULT COXSHBListCtrl::OnCheckCapture(WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(wParam);
UNREFERENCED_PARAMETER(lParam);
// the only purpose of this handler is to set mouse capture back in case we lost it
return ((LONG)CheckCapture());
}
BOOL COXSHBListCtrl::OnItemToolTip(UINT id, NMHDR* pNMHDR, LRESULT* pResult)
{
UNREFERENCED_PARAMETER(id);
ASSERT(pNMHDR->code==TTN_NEEDTEXTA || pNMHDR->code==TTN_NEEDTEXTW);
if(pNMHDR->idFrom>0)
{
// need to handle both ANSI and UNICODE versions of the message
TOOLTIPTEXTA* pTTTA=(TOOLTIPTEXTA*)pNMHDR;
TOOLTIPTEXTW* pTTTW=(TOOLTIPTEXTW*)pNMHDR;
// idFrom must be the item index
if (pNMHDR->code==TTN_NEEDTEXTA)
ASSERT((pTTTA->uFlags&TTF_IDISHWND)==0);
else
ASSERT((pTTTW->uFlags&TTF_IDISHWND)==0);
SHBINFOTIP shbit;
::ZeroMemory(&shbit,sizeof(shbit));
// fill structure for notification
NMSHORTCUTBAR nmshb;
nmshb.hdr.code=SHBN_GETITEMINFOTIP;
nmshb.hGroup=m_hGroup;
nmshb.nItem=(int)pNMHDR->idFrom-1;
nmshb.lParam=(LPARAM)(&shbit);
*pResult=SendSHBNotification(&nmshb);
// copy the text
#ifndef _UNICODE
if(pNMHDR->code==TTN_NEEDTEXTA)
lstrcpyn(pTTTA->szText,shbit.szText,countof(pTTTA->szText));
else
_mbstowcsz(pTTTW->szText,shbit.szText,countof(pTTTW->szText));
#else
if (pNMHDR->code==TTN_NEEDTEXTA)
_wcstombsz(pTTTA->szText,shbit.szText,countof(pTTTA->szText));
else
lstrcpyn(pTTTW->szText,shbit.szText,countof(pTTTW->szText));
#endif
if(pNMHDR->code==TTN_NEEDTEXTA)
{
if(shbit.lpszText==NULL)
pTTTA->lpszText=pTTTA->szText;
else
pTTTA->lpszText=(LPSTR)shbit.lpszText;
pTTTA->hinst=shbit.hinst;
}
else
{
if(shbit.lpszText==NULL)
pTTTW->lpszText=pTTTW->szText;
else
pTTTW->lpszText=(LPWSTR)shbit.lpszText;
pTTTW->hinst=shbit.hinst;
}
return TRUE; // message was handled
}
else
{
*pResult=0;
return TRUE;
}
}
// v9.3 - update 03 - 64-bit - changed LONG ret type to LRESULT
LRESULT COXSHBListCtrl::OnDragEnter(WPARAM wParam, LPARAM lParam)
{
// list control should be valid drop target
if((GetBarStyle()&SHBS_DISABLEDROPITEM)!=0)
return (LONG)FALSE;
// set flag that specifies that drag'n'drop operation is active
m_bDragDropOperation=TRUE;
// reset the hot item
int nOldHotItem=SetHotItem(-1);
if(nOldHotItem!=-1)
Update(nOldHotItem);
// In the case this control was created as a child window of shortcut bar group
// fill structure for notification of parent window of this shortcut bar
NMSHORTCUTBAR nmshb;
nmshb.hdr.code=SHBN_DRAGENTER;
nmshb.hGroup=m_hGroup;
nmshb.lParam=lParam;
// check if parent of the shortcut bar would like to handle drag'n'drop itself
if(SendSHBNotification(&nmshb))
return (LONG)TRUE;
// lParam is the pointer to SHBDROPTARGETACTION structure
LPSHBDROPTARGETACTION pSHBDTAction=(LPSHBDROPTARGETACTION)lParam;
ASSERT(pSHBDTAction!=NULL);
ASSERT(pSHBDTAction->pWnd);
ASSERT(pSHBDTAction->pWnd->GetSafeHwnd()==GetSafeHwnd());
// if this control launched the drag'n'drop operation then
// display the drag image if needed
if((GetBarStyle()&SHBS_DRAWITEMDRAGIMAGE)!=0 && m_bDragDropOwner)
{
CPoint point=pSHBDTAction->point;
ClientToScreen(&point);
ASSERT(m_pDragImage);
VERIFY(m_pDragImage->DragEnter(GetDesktopWindow(),point));
}
return (LONG)OnDragOver(wParam,lParam);
}
// v9.3 - update 03 - 64-bit - changed LONG ret type to LRESULT
LRESULT COXSHBListCtrl::OnDragOver(WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(wParam);
if((GetBarStyle()&SHBS_DISABLEDROPITEM)!=0)
return (LONG)FALSE;
// In the case this control was created as a child window of shortcut bar group
// fill structure for notification of parent window of this shortcut bar
NMSHORTCUTBAR nmshb;
nmshb.hdr.code=SHBN_DRAGOVER;
nmshb.hGroup=m_hGroup;
nmshb.lParam=lParam;
// check if parent of the shortcut bar would like to handle drag'n'drop itself
if(SendSHBNotification(&nmshb))
return (LONG)TRUE;
// lParam is the pointer to SHBDROPTARGETACTION structure
LPSHBDROPTARGETACTION pSHBDTAction=(LPSHBDROPTARGETACTION)lParam;
ASSERT(pSHBDTAction!=NULL);
ASSERT(pSHBDTAction->pWnd);
ASSERT(pSHBDTAction->pWnd->GetSafeHwnd()==GetSafeHwnd());
BOOL bDrawDragImage=FALSE;
// if we launched the drag'n'drop operation then move drag image if needed
if((GetBarStyle()&SHBS_DRAWITEMDRAGIMAGE)!=0 && m_bDragDropOwner)
{
ASSERT(m_pDragImage);
CPoint ptScreen=pSHBDTAction->point;
ClientToScreen(&ptScreen);
// move the drag image
VERIFY(m_pDragImage->DragMove(ptScreen));
bDrawDragImage=TRUE;
}
// analize the current cursor position
//
// it must be within client area of the control
CRect rectClient;
GetClientRect(rectClient);
ASSERT(rectClient.PtInRect(pSHBDTAction->point));
// deflate the client rectangle to match the valid area where we can
// display items
rectClient.DeflateRect(ID_EDGELEFTMARGIN,ID_EDGETOPMARGIN,ID_EDGERIGHTMARGIN,
ID_EDGEBOTTOMMARGIN);
CPoint point=pSHBDTAction->point;
// take into account the control's origin
point+=m_ptOrigin;
// anylize view rectangle
CRect rectView;
GetViewRect(rectView);
int nInsertItemBefore=-1;
// Can we use this object?
if(!pSHBDTAction->pDataObject->
IsDataAvailable(COXShortcutBar::m_nChildWndItemFormat))
pSHBDTAction->result=(LRESULT)DROPEFFECT_NONE;
// if cursor is located higher then view rectangle or there is no items
// in the control
else if(point.y<=rectView.top || GetItemCount()==0)
{
// Check if the control key was pressed
if((pSHBDTAction->dwKeyState & MK_CONTROL)==MK_CONTROL)
pSHBDTAction->result=(LRESULT)DROPEFFECT_COPY;
else
pSHBDTAction->result=(LRESULT)DROPEFFECT_MOVE;
// if droped item must be inserted at the top
nInsertItemBefore=0;
}
// if cursor is located lower than last item
else if(point.y>rectView.bottom)
{
// Check if the control key was pressed
if((pSHBDTAction->dwKeyState & MK_CONTROL)==MK_CONTROL)
pSHBDTAction->result=(LRESULT)DROPEFFECT_COPY;
else
pSHBDTAction->result=(LRESULT)DROPEFFECT_MOVE;
// if droped item must be inserted at the bottom
nInsertItemBefore=GetItemCount();
}
// if cursor is out of deflated client rectangle
else if(!rectClient.PtInRect(pSHBDTAction->point))
pSHBDTAction->result=(LRESULT)DROPEFFECT_NONE;
// otherwise cursor is over list control view area
else
{
// discover the location
UINT nHitFlags;
int nItem=HitTest(pSHBDTAction->point,&nHitFlags,TRUE);
if(nItem==-1)
{
// Check if the control key was pressed
if((pSHBDTAction->dwKeyState & MK_CONTROL)==MK_CONTROL)
{
pSHBDTAction->result=(LRESULT)DROPEFFECT_COPY;
}
else
{
pSHBDTAction->result=(LRESULT)DROPEFFECT_MOVE;
}
// find the closest item
LV_FINDINFO lvfi;
lvfi.flags=LVFI_NEARESTXY;
lvfi.pt=pSHBDTAction->point;
lvfi.vkDirection=VK_DOWN;
int nItem=FindItem(&lvfi);
// assume that cursor is located lower than last item
if(nItem==-1)
{
nItem=GetItemCount();
}
ASSERT(nItem>=0 && nItem<=GetItemCount());
nInsertItemBefore=nItem;
}
else
{
pSHBDTAction->result=(LRESULT)DROPEFFECT_NONE;
}
}
// check if we need to scroll the list control in order to show more items
//
// autoscrolling margins
rectClient.DeflateRect(ID_DRAGDROPLEFTMARGIN,ID_DRAGDROPTOPMARGIN,
ID_DRAGDROPRIGHTMARGIN,ID_DRAGDROPBOTTOMMARGIN);
if(!rectClient.PtInRect(pSHBDTAction->point))
{
// check autoscrolling
if(!m_bScrollingUp && pSHBDTAction->point.y<rectClient.top &&
!m_rectTopScrollButton.IsRectEmpty())
{
// scroll up
m_bAutoScrolling=TRUE;
StartScrolling(TRUE);
}
else if(!m_bScrollingDown && pSHBDTAction->point.y>=rectClient.bottom &&
!m_rectBottomScrollButton.IsRectEmpty())
{
// scroll down
m_bAutoScrolling=TRUE;
StartScrolling(FALSE);
}
}
// check conditions to stop autoscrolling
else if(m_bAutoScrolling)
{
StopScrolling();
}
// redraw placeholders on the list control
if(m_nInsertItemBefore!=nInsertItemBefore)
{
if(bDrawDragImage)
{
// unlock window updates
VERIFY(m_pDragImage->DragShowNolock(FALSE));
}
// remove old placeholder
CRect rectPlaceHolder;
if(m_nInsertItemBefore!=-1)
{
rectPlaceHolder=GetPlaceHolderRect(m_nInsertItemBefore);
m_nInsertItemBefore=-1;
RedrawWindow(rectPlaceHolder);
}
// draw new placeholder
m_nInsertItemBefore=nInsertItemBefore;
if(m_nInsertItemBefore!=-1)
{
rectPlaceHolder=GetPlaceHolderRect(m_nInsertItemBefore);
RedrawWindow(rectPlaceHolder);
}
if(bDrawDragImage)
{
// lock window updates
VERIFY(m_pDragImage->DragShowNolock(TRUE));
}
}
return (LONG)TRUE;
}
// v9.3 - update 03 - 64-bit - changed LONG ret type to LRESULT
LRESULT COXSHBListCtrl::OnDragLeave(WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(wParam);
if((GetBarStyle()&SHBS_DISABLEDROPITEM)!=0)
return (LONG)FALSE;
// In the case this control was created as a child window of shortcut bar group
// fill structure for notification of parent window of this shortcut bar
NMSHORTCUTBAR nmshb;
nmshb.hdr.code=SHBN_DRAGLEAVE;
nmshb.hGroup=m_hGroup;
nmshb.lParam=lParam;
// check if parent of the shortcut bar would like to handle drag'n'drop itself
if(SendSHBNotification(&nmshb))
{
// reset flag that specifies that drag'n'drop operation is active
m_bDragDropOperation=FALSE;
return (LONG)TRUE;
}
// lParam is the pointer to SHBDROPTARGETACTION structure
LPSHBDROPTARGETACTION pSHBDTAction=(LPSHBDROPTARGETACTION)lParam;
ASSERT(pSHBDTAction!=NULL);
ASSERT(pSHBDTAction->pWnd);
ASSERT(pSHBDTAction->pWnd->GetSafeHwnd()==GetSafeHwnd());
// if we launched the drag'n'drop operation then remove drag image if it was used
if((GetBarStyle()&SHBS_DRAWITEMDRAGIMAGE)!=0 && m_bDragDropOwner)
{
ASSERT(m_pDragImage);
// remove dragging image
VERIFY(m_pDragImage->DragLeave(GetDesktopWindow()));
}
// remove placeholder image if any was drawn
if(m_nInsertItemBefore!=-1)
{
CRect rectPlaceHolder=GetPlaceHolderRect(m_nInsertItemBefore);
m_nInsertItemBefore=-1;
RedrawWindow(rectPlaceHolder);
}
// check conditions to stop autoscrolling
if(m_bAutoScrolling)
StopScrolling();
// reset flag that specifies that drag'n'drop operation is active
m_bDragDropOperation=FALSE;
return (LONG)TRUE;
}
// v9.3 - update 03 - 64-bit - changed LONG ret type to LRESULT
LRESULT COXSHBListCtrl::OnDrop(WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(wParam);
if((GetBarStyle()&SHBS_DISABLEDROPITEM)!=0)
return (LONG)FALSE;
// In the case this control was created as a child window of shortcut bar group
// fill structure for notification of parent window of this shortcut bar
NMSHORTCUTBAR nmshb;
nmshb.hdr.code=SHBN_DROP;
nmshb.hGroup=m_hGroup;
nmshb.lParam=lParam;
// check if parent of the shortcut bar would like to handle drag'n'drop itself
if(SendSHBNotification(&nmshb))
{
m_bDragDropOperation=FALSE;
return (LONG)TRUE;
}
// lParam is the pointer to SHBDROPTARGETACTION structure
LPSHBDROPTARGETACTION pSHBDTAction=(LPSHBDROPTARGETACTION)lParam;
ASSERT(pSHBDTAction!=NULL);
ASSERT(pSHBDTAction->pWnd);
ASSERT(pSHBDTAction->pWnd->GetSafeHwnd()==GetSafeHwnd());
// if dragged item is to be copied or moved
if((pSHBDTAction->dropEffect&DROPEFFECT_COPY)!=0 ||
(pSHBDTAction->dropEffect&DROPEFFECT_MOVE)!=0)
{
// data must be in the specific format
ASSERT(pSHBDTAction->pDataObject->
IsDataAvailable(COXShortcutBar::m_nChildWndItemFormat));
if(m_nInsertItemBefore!=-1)
{
// Get the drag item info
//
HGLOBAL hgData=pSHBDTAction->pDataObject->
GetGlobalData(COXShortcutBar::m_nChildWndItemFormat);
ASSERT(hgData);
// lock it
BYTE* lpItemData=(BYTE*)GlobalLock(hgData);
// get item text
CString sText((LPTSTR)lpItemData);
lpItemData+=(sText.GetLength()+1)*sizeof(TCHAR);
// get item lParam
DWORD lParam=*(DWORD*)lpItemData;
lpItemData+=sizeof(DWORD);
// get the count of images associated with the draged item
DWORD nImageCount=*(DWORD*)lpItemData;
lpItemData+=sizeof(DWORD);
int nImageIndex=-1;
for(int nIndex=0; nIndex<(int)nImageCount; nIndex++)
{
// get image info from dragged data
SHBIMAGEDROPINFO imageInfo;
memcpy((void*)&imageInfo,(void*)lpItemData,sizeof(SHBIMAGEDROPINFO));
lpItemData+=sizeof(SHBIMAGEDROPINFO);
DWORD nImageSize=*(DWORD*)lpItemData;
ASSERT(nImageSize>0);
lpItemData+=sizeof(DWORD);
void* pImageBuffer=malloc(nImageSize);
ASSERT(pImageBuffer!=NULL);
memcpy(pImageBuffer,(void*)lpItemData,nImageSize);
ASSERT(pImageBuffer!=NULL);
// move to the next image info structure
lpItemData+=nImageSize;
// we are interested only in "NORMAL" images
if(imageInfo.imageType==SHBIT_NORMAL)
{
if(imageInfo.imageState==SHBIS_LARGE)
{
CImageList* pil=GetImageList(LVSIL_NORMAL);
if(pil==NULL)
pil=&m_ilLarge;
int nNewItemIndex=CopyImageToIL(pil,pImageBuffer,nImageSize);
ASSERT(nNewItemIndex!=-1);
ASSERT(nImageIndex==-1 || nNewItemIndex==nImageIndex);
nImageIndex=nNewItemIndex;
if(GetImageList(LVSIL_NORMAL)==NULL)
SetImageList(&m_ilLarge,LVSIL_NORMAL);
}
else if(imageInfo.imageState==SHBIS_SMALL)
{
CImageList* pil=GetImageList(LVSIL_SMALL);
if(pil==NULL)
pil=&m_ilSmall;
int nNewItemIndex=CopyImageToIL(pil,pImageBuffer,nImageSize);
ASSERT(nNewItemIndex!=-1);
ASSERT(nImageIndex==-1 || nNewItemIndex==nImageIndex);
nImageIndex=nNewItemIndex;
if(GetImageList(LVSIL_SMALL)==NULL)
SetImageList(&m_ilSmall,LVSIL_SMALL);
}
}
ASSERT(pImageBuffer!=NULL);
free(pImageBuffer);
}
// insert new item
//
LV_ITEM lvi;
ZeroMemory((void*)&lvi,sizeof(lvi));
lvi.mask=LVIF_TEXT;
lvi.pszText=(LPTSTR)((LPCTSTR)sText);
lvi.iSubItem=0;
lvi.iItem=m_nInsertItemBefore;
// set image info
if(nImageIndex!=-1)
{
lvi.mask|=LVIF_IMAGE;
lvi.iImage=nImageIndex;
}
// set lParam info
if(m_bDragDropOwner)
{
lvi.mask|=LVIF_PARAM;
lvi.lParam=lParam;
}
int nInsertedItem=InsertItem(&lvi);
ASSERT(nInsertedItem!=-1);
//////////////////////////////
// get the amount of bytes of additional info
SHBADDITIONALDROPINFO shbadi;
shbadi.nBufferLength=*(DWORD*)lpItemData;
lpItemData+=sizeof(DWORD);
if(shbadi.nBufferLength>0)
{
// get additional info
shbadi.pBuffer=(void*)lpItemData;
// In the case this control was created as a child window
// of shortcut bar group fill structure for notification of
// parent window of this shortcut bar
NMSHORTCUTBAR nmshb;
nmshb.hdr.code=SHBN_SETADDITIONALDROPINFO;
nmshb.hGroup=m_hGroup;
nmshb.nItem=m_nInsertItemBefore;
nmshb.lParam=(LPARAM)&shbadi;
SendSHBNotification(&nmshb);
lpItemData+=shbadi.nBufferLength;
}
// unlock it
GlobalUnlock(hgData);
// free it
GlobalFree(hgData);
// if we haven't launched the drag'n'drop operation then we have to redraw
// the control here to display newly inserted item
if(!m_bDragDropOwner)
{
m_nInsertItemBefore=-1;
RedrawWindow();
}
// drag'n'drop operation completed successfully
pSHBDTAction->result=(LRESULT)TRUE;
}
else
pSHBDTAction->result=(LRESULT)FALSE;
}
else
pSHBDTAction->result=(LRESULT)FALSE;
m_bDragDropOperation=FALSE;
if(!m_bDragDropOwner)
{
// In the case this control was created as a child window of shortcut bar group
// fill structure for notification of parent window of this shortcut bar
NMSHORTCUTBAR nmshb;
nmshb.hdr.code=SHBN_ENDDRAGDROPITEM;
nmshb.hGroup=m_hGroup;
nmshb.lParam=pSHBDTAction->result!=0 ?
pSHBDTAction->dropEffect : DROPEFFECT_NONE;
// notify parent
SendSHBNotification(&nmshb);
}
// we handled the message
return (LONG)TRUE;
}
void COXSHBListCtrl::OnChangeItemText(NMHDR* pNotifyStruct, LRESULT* result)
{
// this function handles SHBEN_FINISHEDIT that is sent by edit control
// to notify its parent that editing was finished
ASSERT(::IsWindow(m_edit.GetSafeHwnd()));
ASSERT(m_nEditItem>=0 && m_nEditItem<GetItemCount());
LPNMSHBEDIT lpNMSHBE=(LPNMSHBEDIT)pNotifyStruct;
// check if editing wasn't canceled
if(lpNMSHBE->bOK)
{
// get the edit control text
CString sText;
m_edit.GetWindowText(sText);
// In the case this control was created as a child window
// of shortcut bar group fill structure for notification of
// parent window of this shortcut bar
NMSHORTCUTBAR nmshb;
nmshb.hdr.code=SHBN_ENDITEMEDIT;
nmshb.hGroup=m_hGroup;
nmshb.nItem=m_nEditItem;
nmshb.lParam=(LPARAM)((LPCTSTR)sText);
// check if parent rejects the updated text
if(!SendSHBNotification(&nmshb))
{
// change the text of item
if(!SetItemText(m_nEditItem,0,sText.GetBuffer(sText.GetLength())))
{
TRACE(_T("COXSHBListCtrl::OnChangeItemText: failed to set text to item: %s"),sText);
}
sText.ReleaseBuffer();
}
}
// clear edit item index
m_nEditItem=-1;
*result=0;
}
// v9.3 - update 03 - 64-bit - changed LONG ret type to LRESULT
LRESULT COXSHBListCtrl::OnPopulateContextMenu(WPARAM wParam, LPARAM lParam)
{
// Handles SHBM_POPULATECONTEXTMENU message that is fired by parent shortcut bar
// in order to populate context menu with our own items
UNREFERENCED_PARAMETER(wParam);
LPSHBCONTEXTMENU pSHBCM=(LPSHBCONTEXTMENU)lParam;
// double check that the message came from its parent shortcut bar and
// corresponding group
if(pSHBCM->pShortcutBar==m_pShortcutBar && pSHBCM->hGroup==m_hGroup)
{
CPoint point=pSHBCM->point;
ScreenToClient(&point);
// find if right mouse was clicked over any item
UINT nFlags;
int nItem=HitTest(point,&nFlags);
if(nItem!=-1 && nFlags!=SHBLC_ONITEM)
{
ASSERT(nItem>=0 && nItem<GetItemCount());
// add two items to menu to edit and remove items from the control
CMenu* pMenu=pSHBCM->pMenu;
if((GetBarStyle()&SHBS_EDITITEMS)!=0)
{
CString sItem;
VERIFY(sItem.LoadString(IDS_OX_SHBLC_IDMRENAMEITEM_TEXT));
if(pMenu->GetMenuItemCount()>0)
pMenu->AppendMenu(MF_SEPARATOR);
pMenu->AppendMenu(MF_STRING,SHBLC_IDMRENAMEITEM,
sItem);
VERIFY(sItem.LoadString(IDS_OX_SHBLC_IDMREMOVEITEM_TEXT));
pMenu->AppendMenu(MF_STRING,SHBLC_IDMREMOVEITEM,
sItem);
}
}
}
return ((LONG)0);
}
// v9.3 - update 03 - 64-bit - changed LONG ret type to LRESULT
LRESULT COXSHBListCtrl::OnSetSHBGroup(WPARAM wParam, LPARAM lParam)
{
// Handles SHBM_SETSHBGROUP message that is fired by parent shortcut bar
// as soon as this control associated with any group
UNREFERENCED_PARAMETER(wParam);
// cast parent to shortcut bar
CWnd* pWnd=GetParent();
ASSERT(pWnd);
// check if it's really shortcut bar
if(pWnd->IsKindOf(RUNTIME_CLASS(COXShortcutBar)))
{
m_hGroup=(HSHBGROUP)lParam;
ASSERT(m_hGroup);
m_pShortcutBar=(COXShortcutBar*)pWnd;
}
return ((LONG)0);
}
// v9.3 - update 03 - 64-bit - changed LONG ret type to LRESULT
LRESULT COXSHBListCtrl::OnSHBInfoChanged(WPARAM wParam, LPARAM lParam)
{
// Handles SHBM_SHBINFOCHANGED message that is fired by parent shortcut bar
// to notify its child window that some of shortcut bar properties have changed.
// lParam specifies mask of updated properties
UNREFERENCED_PARAMETER(wParam);
ASSERT(m_pShortcutBar!=NULL);
ASSERT(m_hGroup!=NULL);
UINT nMask=(UINT)lParam;
// if size of scroll buttons has changed then we should recalculate
// corresponding rectangles
if((nMask&SHBIF_SCRLBTNSIZE)!=0)
{
CalculateScrollRects();
}
// if style of shortcut bar has been changed then we should recalculate
// corresponding rectangles and redraw the window
if((nMask&SHBIF_SHBSTYLE)!=0)
{
CalculateScrollRects();
RedrawWindow();
}
return ((LONG)0);
}
// v9.3 - update 03 - 64-bit - changed LONG ret type to LRESULT
LRESULT COXSHBListCtrl::OnGroupInfoChanged(WPARAM wParam, LPARAM lParam)
{
// Handles SHBM_GROUPINFOCHANGED message that is fired by parent shortcut bar
// to notify its child window that some of associated with this control
// shortcut bar group properties have changed. lParam specifies mask of updated
// properties
UNREFERENCED_PARAMETER(wParam);
ASSERT(m_pShortcutBar!=NULL);
ASSERT(m_hGroup!=NULL);
UINT nMask=(UINT)lParam;
// if descriptor changed then reposition all items
if((nMask&SHBIF_DESCRIPTOR)!=0)
{
CalculateBoundRects();
}
// if view chnged then reset the view origin and redraw the control
if((nMask&SHBIF_VIEW)!=0)
{
Scroll(-m_ptOrigin);
CalculateBoundRects();
}
return ((LONG)0);
}
LRESULT COXSHBListCtrl::OnCreateDragImage(WPARAM wParam, LPARAM lParam)
{
return (LRESULT)CreateDragImage((int)wParam,(LPPOINT)lParam);
}
LRESULT COXSHBListCtrl::OnDeleteAllItems(WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(wParam);
UNREFERENCED_PARAMETER(lParam);
// fill structure for notification
NMSHORTCUTBAR nmshb;
nmshb.hdr.code=SHBN_DELETEALLITEMS;
nmshb.hGroup=m_hGroup;
SendSHBNotification(&nmshb);
LRESULT result=Default();
// recalculate all item position (actually remove all information about items)
if((BOOL)result)
{
m_ptOrigin=CPoint(0,0);
SelectItem(-1);
ActivateItem(-1);
CalculateBoundRects();
RedrawWindow();
}
return result;
}
LRESULT COXSHBListCtrl::OnDeleteItem(WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
// In the case this control was created as a child window of shortcut bar group
// fill structure for notification of parent window of this shortcut bar
NMSHORTCUTBAR nmshb;
nmshb.hdr.code=SHBN_DELETEITEM;
nmshb.hGroup=m_hGroup;
nmshb.nItem=(int)wParam;
SendSHBNotification(&nmshb);
LRESULT result=Default();
// reposition items and redraw the control
if((BOOL)result)
{
// change the origin of the control if the last item was deleted
// and it was the top item at the moment
if(GetTopIndex()==-1)
m_ptOrigin=CPoint(0,0);
// if deleted item had lower index than the selected item index
// then decrease it by one
if(GetSelectedItem()>(int)wParam)
m_nSelectedItem--;
// if deleted item was the selected one then reset the index of selected item
else if(GetSelectedItem()==(int)wParam)
m_nSelectedItem=-1;
// if deleted item had lower index than the active item index
// then decrease it by one
if(GetActiveItem()>(int)wParam)
m_nActiveItem--;
// if deleted item was the active one then reset the index of active item item
else if(GetActiveItem()==(int)wParam)
m_nActiveItem=-1;
// reposition the items and redraw the control if there is no active
// drag'n'drop operation
CalculateBoundRects();
if(!m_bDragDropOperation)
{
RedrawWindow();
}
}
return result;
}
LRESULT COXSHBListCtrl::OnEditLabel(WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
return (LRESULT)EditLabel((int)wParam);
}
LRESULT COXSHBListCtrl::OnEnsureVisible(WPARAM wParam, LPARAM lParam)
{
return (LRESULT)EnsureVisible((int)wParam,(BOOL)lParam);
}
LRESULT COXSHBListCtrl::OnFindItem(WPARAM wParam, LPARAM lParam)
{
// we provide our own functionality to find items dpecified by their position
if(((LV_FINDINFO*)lParam)->flags&LVFI_NEARESTXY)
return (LONG)FindItem((LV_FINDINFO*)lParam,(int)wParam);
else
return (LRESULT)Default();
}
LRESULT COXSHBListCtrl::OnGetBkColor(WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
UNREFERENCED_PARAMETER(wParam);
if(m_pShortcutBar)
return (LRESULT)GetBkColor();
else
return Default();
}
LRESULT COXSHBListCtrl::OnGetCountPerPage(WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
UNREFERENCED_PARAMETER(wParam);
return (LRESULT)GetCountPerPage();
}
LRESULT COXSHBListCtrl::OnGetEditControl(WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
UNREFERENCED_PARAMETER(wParam);
return (LRESULT)GetEditControl();
}
// v9.3 - update 03 - 64-bit - changed LONG ret type to LRESULT
LRESULT COXSHBListCtrl::OnGetHotItem(WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
UNREFERENCED_PARAMETER(wParam);
return (LONG)GetHotItem();
}
LRESULT COXSHBListCtrl::OnGetItemPosition(WPARAM wParam, LPARAM lParam)
{
return GetItemPosition((int)wParam,(LPPOINT)lParam);
}
LRESULT COXSHBListCtrl::OnGetItemRect(WPARAM wParam, LPARAM lParam)
{
return GetItemRect((int)wParam,(LPRECT)lParam,((LPRECT)lParam)->left);
}
LRESULT COXSHBListCtrl::OnGetOrigin(WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(wParam);
return GetOrigin((LPPOINT)lParam);
}
LRESULT COXSHBListCtrl::OnGetTextBkColor(WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
UNREFERENCED_PARAMETER(wParam);
if(m_pShortcutBar)
return GetTextBkColor();
else
return Default();
}
LRESULT COXSHBListCtrl::OnGetTextColor(WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
UNREFERENCED_PARAMETER(wParam);
if(m_pShortcutBar)
return GetTextColor();
else
return Default();
}
LRESULT COXSHBListCtrl::OnGetTopIndex(WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
UNREFERENCED_PARAMETER(wParam);
return (LRESULT)GetTopIndex();
}
LRESULT COXSHBListCtrl::OnGetViewRect(WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(wParam);
return (LRESULT)GetViewRect((LPRECT)lParam);
}
LRESULT COXSHBListCtrl::OnHitTest(WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(wParam);
return (LRESULT)HitTest((LPLVHITTESTINFO)lParam);
}
LRESULT COXSHBListCtrl::OnInsertItem(WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(wParam);
LRESULT result=Default();
// reposition items and redraw the control
if((int)result!=-1)
{
// In the case this control was created as a child window of shortcut bar group
// fill structure for notification of parent window of this shortcut bar
NMSHORTCUTBAR nmshb;
nmshb.hdr.code=SHBN_INSERTITEM;
nmshb.hGroup=m_hGroup;
nmshb.nItem=((LV_ITEM*)lParam)->iItem;
nmshb.lParam=lParam;
SendSHBNotification(&nmshb);
// if inserted item had lower or equal index as the selected item index
// then increase the last by one
if(GetSelectedItem()>=(int)result)
m_nSelectedItem++;
// if inserted item had lower or equal index as the active item index
// then increase the last by one
if(GetActiveItem()>=(int)result)
m_nActiveItem++;
// reposition the items and redraw the control if there is no active
// drag'n'drop operation
CalculateBoundRects();
if(!m_bDragDropOperation)
{
RedrawWindow();
}
}
return result;
}
LRESULT COXSHBListCtrl::OnRedrawItems(WPARAM wParam, LPARAM lParam)
{
return RedrawItems((int)wParam,(int)lParam);
}
LRESULT COXSHBListCtrl::OnScroll(WPARAM wParam, LPARAM lParam)
{
return Scroll((int)wParam,(int)lParam);
}
LRESULT COXSHBListCtrl::OnSetBkColor(WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(wParam);
if(m_pShortcutBar)
return SetBkColor((COLORREF)lParam);
else
return Default();
}
LRESULT COXSHBListCtrl::OnSetHotItem(WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
return SetHotItem((int)wParam);
}
LRESULT COXSHBListCtrl::OnSetItem(WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(wParam);
UNREFERENCED_PARAMETER(lParam);
LRESULT result=Default();
if((BOOL)result)
{
// reposition items and redraw the control
CalculateBoundRects();
RedrawWindow();
}
return result;
}
LRESULT COXSHBListCtrl::OnSetItemText(WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(wParam);
UNREFERENCED_PARAMETER(lParam);
LRESULT result= Default();
if((BOOL)result)
{
// reposition items and redraw the control
CalculateBoundRects();
RedrawWindow();
}
return result;
}
LRESULT COXSHBListCtrl::OnSetTextBkColor(WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(wParam);
if(m_pShortcutBar)
return SetTextBkColor((COLORREF)lParam);
else
return Default();
}
LRESULT COXSHBListCtrl::OnSetTextColor(WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(wParam);
if(m_pShortcutBar)
return SetTextColor((COLORREF)lParam);
else
return Default();
}
LRESULT COXSHBListCtrl::OnSortItems(WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(wParam);
UNREFERENCED_PARAMETER(lParam);
LRESULT result=Default();
// reposition items and redraw the control
if((BOOL)result)
{
// reset active and selected items
SelectItem(-1);
ActivateItem(-1);
// reposition items and redraw the control
CalculateBoundRects();
RedrawWindow();
}
return result;
}
LRESULT COXSHBListCtrl::OnUpdate(WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
return Update((int)wParam);
}
// v9.3 - update 03 - 64-bit - using OXTPARAM here - see UTB64Bit.h
void COXSHBListCtrl::OnTimer(OXTPARAM nIDEvent)
{
// TODO: Add your message handler code here and/or call default
// we use timer to provide scrolling functionality
if(m_nScrollTimerID==nIDEvent)
{
// for pshycho
ASSERT(m_bScrollingUp || m_bScrollingDown);
CRect rectClient;
GetClientRect(rectClient);
// deflate the client rectangle to match the valid area where we can
// display items
rectClient.DeflateRect(ID_EDGELEFTMARGIN,ID_EDGETOPMARGIN,ID_EDGERIGHTMARGIN,
ID_EDGEBOTTOMMARGIN);
ASSERT(rectClient.top<rectClient.bottom);
UINT nHitFlags;
// get cursor position when the last mouse message was fired
DWORD dwMessagePos=::GetMessagePos();
CPoint point(GET_X_LPARAM(dwMessagePos),GET_Y_LPARAM(dwMessagePos));
ScreenToClient(&point);
HitTest(point,&nHitFlags);
// if any drag and drop operation is undergoing then cheat the control
if(m_bDragDropOperation)
{
if(m_bScrollingUp)
nHitFlags=SHBLC_ONTOPSCROLLBUTTON;
else
nHitFlags=SHBLC_ONBOTTOMSCROLLBUTTON;
if((GetBarStyle()&SHBS_DRAWITEMDRAGIMAGE)!=0 && m_bDragDropOwner)
{
ASSERT(m_pDragImage);
// unlock window updates
VERIFY(m_pDragImage->DragShowNolock(FALSE));
}
}
// if we are still over the scroll butoon
if(m_bScrollingUp && nHitFlags==SHBLC_ONTOPSCROLLBUTTON)
{
if(!m_bDragDropOperation)
ASSERT(!m_rectTopScrollButton.IsRectEmpty());
RedrawWindow(m_rectTopScrollButton);
// scroll up
CRect rect;
int nItem=GetTopIndex();
if(nItem==-1)
nItem=GetItemCount()-1;
else
{
VERIFY(m_mapItemToBoundRect.Lookup(nItem,rect));
if(m_ptOrigin.y+rectClient.top<=rect.top)
{
ASSERT(nItem>0);
nItem--;
}
}
ASSERT((nItem>=0 && nItem<GetItemCount()));
VERIFY(m_mapItemToBoundRect.Lookup(nItem,rect));
int nOffset=rect.top-rectClient.top-m_ptOrigin.y;
Scroll(CSize(0,nOffset));
// check if we scrolled to the top
if(m_rectTopScrollButton.IsRectEmpty())
StopScrolling();
}
else if(m_bScrollingDown && nHitFlags==SHBLC_ONBOTTOMSCROLLBUTTON)
{
if(!m_bDragDropOperation)
ASSERT(!m_rectBottomScrollButton.IsRectEmpty());
RedrawWindow(m_rectBottomScrollButton);
// scroll down
CRect rect;
int nItem=GetTopIndex();
ASSERT(nItem>=0 && nItem<GetItemCount());
if(nItem==GetItemCount()-1)
{
VERIFY(m_mapItemToBoundRect.Lookup(nItem,rect));
int nOffset=rect.bottom-rectClient.bottom-m_ptOrigin.y;
Scroll(CSize(0,nOffset));
}
else
{
nItem++;
VERIFY(m_mapItemToBoundRect.Lookup(nItem,rect));
int nOffset=rect.top-rectClient.top-m_ptOrigin.y;
Scroll(CSize(0,nOffset));
}
// check if we scrolled to the bottom
if(m_rectBottomScrollButton.IsRectEmpty())
StopScrolling();
}
if(m_bDragDropOperation && (GetBarStyle()&SHBS_DRAWITEMDRAGIMAGE)!=0 &&
m_bDragDropOwner)
{
ASSERT(m_pDragImage);
// lock window updates
VERIFY(m_pDragImage->DragShowNolock(TRUE));
}
}
else if(nIDEvent==m_nCheckMouseTimerID)
{
if(!m_bDragDropOperation)
PostMessage(SHBLCM_CHECKCAPTURE);
}
else
CListCtrl::OnTimer(nIDEvent);
}
void COXSHBListCtrl::OnDestroy()
{
CListCtrl::OnDestroy();
// TODO: Add your message handler code here
// just kill timers
if(m_nScrollTimerID!=0)
{
KillTimer(m_nScrollTimerID);
m_nScrollTimerID=0;
}
if(m_nCheckMouseTimerID!=0)
{
KillTimer(m_nCheckMouseTimerID);
m_nCheckMouseTimerID=0;
}
}
/////////////////////////////////////////////////////////////////////////////
BOOL COXSHBListCtrl::CreateEditControl()
{
if(::IsWindow(m_edit.GetSafeHwnd()))
return TRUE;
// Create an invisible edit control
CRect rect(0,0,0,0);
BOOL bResult=m_edit.Create(WS_BORDER,rect,this,SHB_IDCEDIT);
return bResult;
}
BOOL COXSHBListCtrl::CheckCapture()
{
// As soon as we get first mouse message we have to capture all of them in order
// to handle hot items
// Check whether the mouse is over the list control
CRect rectWindow;
GetWindowRect(rectWindow);
CPoint point;
::GetCursorPos(&point);
CWnd* pWnd=CWnd::WindowFromPoint(point);
ASSERT(pWnd!=NULL);
BOOL bIsMouseOver=(pWnd->GetSafeHwnd()==GetSafeHwnd());
if(bIsMouseOver && IsWindowEnabled())
{
// Moved inside this control window. Capture the mouse
// SetCapture();
if(m_nCheckMouseTimerID==0)
{
m_nCheckMouseTimerID=SetTimer(IDT_OXSHBLIST_CHECKMOUSE,
ID_OXSHBLIST_CHECKMOUSE_DELAY,NULL);
ASSERT(m_nCheckMouseTimerID!=0);
}
m_bMouseIsOver=TRUE;
}
else
{
// ... We release the mouse capture (may have already lost it)
// we could have dragged something
if(!IsLButtonDown())
{
// ReleaseCapture();
if(m_nCheckMouseTimerID!=0)
{
KillTimer(m_nCheckMouseTimerID);
m_nCheckMouseTimerID=0;
}
m_bMouseIsOver=FALSE;
int nOldHotItem=SetHotItem(-1);
if(nOldHotItem!=-1)
Update(nOldHotItem);
return FALSE;
}
m_bMouseIsOver=FALSE;
}
// handle all mouse messages
AnticipateMouseMessages();
// return TRUE if we have all mouse input
// return (GetCapture()==this);
return TRUE;
}
void COXSHBListCtrl::PostCheckCapture()
{
// check if mouse capture was lost during last mouse message handling
}
void COXSHBListCtrl::CalculateBoundRects()
{
// clean up map of all bound rectangles
m_mapItemToBoundRect.RemoveAll();
m_mapItemToImageRect.RemoveAll();
m_mapItemToTextRect.RemoveAll();
// clean up scroll buttons rectangles
m_rectTopScrollButton.SetRectEmpty();
m_rectBottomScrollButton.SetRectEmpty();
int nCount=GetItemCount();
// if there is no item then our mission is completed
if(nCount==0)
{
return;
}
CRect rect;
GetClientRect(rect);
// if client rect is empty then our mission is completed too
if(rect.IsRectEmpty())
{
for(int nIndex=0; nIndex<nCount; nIndex++)
{
m_mapItemToBoundRect.SetAt(nIndex,rect);
m_mapItemToImageRect.SetAt(nIndex,rect);
m_mapItemToTextRect.SetAt(nIndex,rect);
}
return;
}
// deflate rect to leave some space that won't be taken by items
rect.DeflateRect(
ID_EDGELEFTMARGIN,ID_EDGETOPMARGIN,ID_EDGERIGHTMARGIN,ID_EDGEBOTTOMMARGIN);
// item positions depend on type of view
int nView=GetView();
// get corresponding item list to show items
CImageList* pil=NULL;
if(nView==SHB_LARGEICON)
{
pil=GetImageList(LVSIL_NORMAL);
}
else if(nView==SHB_SMALLICON)
{
pil=GetImageList(LVSIL_SMALL);
}
IMAGEINFO imageInfo;
CRect rectImage;
CRect rectText;
CString sItemText;
// loop through all items
for(int nIndex=0; nIndex<nCount; nIndex++)
{
// get text
sItemText=GetItemText(nIndex,0);
// get image index
int nImage=GetItemImage(nIndex);
if(pil!=NULL && nImage>=0)
{
VERIFY(pil->GetImageInfo(nImage,&imageInfo));
rectImage=imageInfo.rcImage;
rectImage.InflateRect(3,3);
rectImage-=rectImage.TopLeft();
}
else
{
rectImage.SetRectEmpty();
}
// calculate text rect and adjust it with image rect
rectText=AdjustTextRect(sItemText,rectImage,rect.Width());
if(nView==SHB_LARGEICON)
{
rect.bottom=rect.top+rectImage.Height()+rectText.Height()+
ID_IMAGETEXTVERTMARGIN;
}
else if(nView==SHB_SMALLICON)
{
rect.bottom=rect.top+__max(rectImage.Height(),rectText.Height());
}
else
{
ASSERT(FALSE);
}
if(nView==SHB_LARGEICON)
{
int nMargin=rectImage.Width()-rect.Width();
rectImage.left=rect.left-nMargin/2;
rectImage.right=rect.right+nMargin/2+nMargin%2;
}
// save calculated rects
m_mapItemToBoundRect.SetAt(nIndex,rect);
m_mapItemToImageRect.SetAt(nIndex,rectImage);
m_mapItemToTextRect.SetAt(nIndex,rectText);
rect.top=rect.bottom+((nIndex==nCount-1) ? 0 : ID_ITEMMARGIN);
}
// recalculate scroll buttons rects
CalculateScrollRects();
}
CRect COXSHBListCtrl::AdjustTextRect(CString sText,CRect& rectImage, int nMaxWidth)
{
// takes on input text, image rect an max width that it can take
//
CRect rect;
rect.SetRectEmpty();
if(sText.IsEmpty() || nMaxWidth<=0)
return rect;
CClientDC dc(this);
// Save DC
int nSavedDC=dc.SaveDC();
ASSERT(nSavedDC);
// set font
CFont* pOldFont=NULL;
CFont font;
if(m_pShortcutBar)
{
LPLOGFONT pLF=m_pShortcutBar->GetGroupTextFont(m_hGroup);
ASSERT(pLF);
VERIFY(font.CreateFontIndirect(pLF));
pOldFont=dc.SelectObject(&font);
}
else
{
CFont* pFont=GetFont();
if(pFont!=NULL)
pOldFont=dc.SelectObject(pFont);
}
int nView=GetView();
// depending on type of view calculate the text rect
if(nView==SHB_LARGEICON)
{
rect.right=nMaxWidth-(ID_TEXTLEFTMARGIN+ID_TEXTRIGHTMARGIN);
dc.DrawText(sText,rect,DT_VCENTER|DT_CENTER|DT_WORDBREAK|DT_CALCRECT|
DT_EDITCONTROL);
rect.InflateRect(0,0,ID_TEXTLEFTMARGIN+ID_TEXTRIGHTMARGIN,
ID_TEXTTOPMARGIN+ID_TEXTBOTTOMMARGIN);
// adjust text and image rects
//
int nHeight=rect.Height();
rect.top+=rectImage.bottom+
(rectImage.bottom==rectImage.top ? 0 : ID_IMAGETEXTVERTMARGIN);
rect.bottom=rect.top+nHeight;
int nWidth=rect.Width();
if(nWidth>nMaxWidth)
rect.right=nMaxWidth;
else
{
int nMargin=nMaxWidth-nWidth;
rect.left=nMargin/2;
rect.right=rect.left+nWidth;
}
}
else if(nView==SHB_SMALLICON && rectImage.Width()<nMaxWidth)
{
dc.DrawText(sText,rect,DT_VCENTER|DT_LEFT|DT_CALCRECT);
rect.InflateRect(0,0,ID_TEXTLEFTMARGIN+ID_TEXTRIGHTMARGIN,
ID_TEXTTOPMARGIN+ID_TEXTBOTTOMMARGIN);
// adjust text and image rects
//
int nHeight=rect.Height();
rect.top=rectImage.top;
rect.bottom=rect.top+nHeight;
int nMargin=nHeight-rectImage.Height();
if(nMargin>0)
{
rectImage.OffsetRect(0,nMargin/2);
}
else if(nMargin<0)
{
rect.top-=nMargin/2;
rect.bottom=rect.top+nHeight;
}
if(rectImage.left!=rectImage.right)
{
int nWidth=rect.Width();
rect.left+=rectImage.right+ID_IMAGETEXTHORZMARGIN;
rect.right=rect.left+nWidth;
}
// if there is no any image associated with the item thn in small icon view
// offset the text rectangle a little bit to make it look nicer
else
rect.left+=ID_TEXTLEFTMARGIN;
if(rect.right>nMaxWidth)
rect.right=nMaxWidth;
}
if(pOldFont!=NULL)
dc.SelectObject(pOldFont);
// Restore dc
dc.RestoreDC(nSavedDC);
return rect;
}
void COXSHBListCtrl::CalculateScrollRects()
{
// clean up scroll buttons rectangles
m_rectTopScrollButton.SetRectEmpty();
m_rectBottomScrollButton.SetRectEmpty();
// define scroll buttons rectangles
//
CRect rect;
GetClientRect(rect);
rect.DeflateRect(
ID_EDGELEFTMARGIN,ID_EDGETOPMARGIN,ID_EDGERIGHTMARGIN,ID_EDGEBOTTOMMARGIN);
if(rect.top>=rect.bottom)
{
return;
}
CSize sizeScrollButton=GetScrollButtonSize();
if(m_ptOrigin.y!=0)
{
// define rect for top scroll button
m_rectTopScrollButton.top=rect.top;
m_rectTopScrollButton.right=rect.right;
m_rectTopScrollButton.bottom=rect.top+sizeScrollButton.cy;
m_rectTopScrollButton.left=rect.right-sizeScrollButton.cx;
}
CRect rectScrollable;
if(GetScrollableRect(rectScrollable))
{
if(rect.bottom+m_ptOrigin.y<rectScrollable.bottom)
{
// define rect for bottom scroll button
m_rectBottomScrollButton.bottom=rect.bottom;
m_rectBottomScrollButton.right=rect.right;
m_rectBottomScrollButton.top=rect.bottom-sizeScrollButton.cy;
m_rectBottomScrollButton.left=rect.right-sizeScrollButton.cx;
}
}
}
void COXSHBListCtrl::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
// Draws any item. It defined as virtual in case you want to draw items yourself.
// But you should be careful - this control is highly optimized to be drawn in the
// most efficient way. Don't ruin it!
int nItem=(int)lpDrawItemStruct->itemID;
ASSERT(nItem>=0 && nItem<GetItemCount());
// we use this routine to create drag image too
if(!m_bCreatingDragImage)
{
// In the case this control was created as a child window
// of shortcut bar group fill structure for notification of
// parent window of this shortcut bar
NMSHORTCUTBAR nmshb;
nmshb.hdr.code=SHBN_DRAWITEM;
nmshb.hGroup=m_hGroup;
nmshb.nItem=nItem;
nmshb.lParam=(LPARAM)lpDrawItemStruct;
// In the case you handled this notification you could have provided all
// the drawing functionality so we check if you don't want us to run
// the standard implementation
if(SendSHBNotification(&nmshb))
{
return;
}
}
CDC dc;
dc.Attach(lpDrawItemStruct->hDC);
// Get the header rect
CRect rectItem(lpDrawItemStruct->rcItem);
// Save DC
int nSavedDC=dc.SaveDC();
ASSERT(nSavedDC);
int nView=GetView();
// Draw Image
//
int nImage=GetItemImage(nItem);
if(nImage>=0)
{
CImageList* pil=NULL;
if(nView==SHB_LARGEICON)
{
pil=GetImageList(LVSIL_NORMAL);
}
else if(nView==SHB_SMALLICON)
{
pil=GetImageList(LVSIL_SMALL);
}
if(pil!=NULL)
{
CRect rectImage;
VERIFY(m_mapItemToImageRect.Lookup(nItem,rectImage));
rectImage.DeflateRect(1,1);
if(!m_bCreatingDragImage)
{
COLORREF clrTopLeft=UpdateColor(GetBkColor(),64);
COLORREF clrBottomRight=UpdateColor(GetBkColor(),-128);
// Draw bounding rectangles if needed
if(GetHotItem()==nItem)
{
if (m_pShortcutBar != NULL)
{
// Part if a shorcut bar
BOOL bSelected = (GetSelectedItem() == nItem && IsLButtonDown()) ? TRUE : FALSE;
m_pShortcutBar->GetShortcutBarSkin()->DrawItemBorder(&dc, rectImage, TRUE, bSelected, this);
}
else
{
// Not part of a shortcut bar
if(IsLButtonDown() && GetSelectedItem()==nItem)
dc.Draw3dRect(rectImage,clrBottomRight,clrTopLeft);
else
dc.Draw3dRect(rectImage,clrTopLeft,clrBottomRight);
}
}
else if(GetActiveItem()==nItem &&
(GetBarStyle() & SHBS_SHOWACTIVEALWAYS)!=0)
{
if (m_pShortcutBar != NULL)
m_pShortcutBar->GetShortcutBarSkin()->DrawItemBorder(&dc, rectImage, FALSE, TRUE, this);
else
dc.Draw3dRect(rectImage,clrBottomRight,clrTopLeft);
}
}
// Draw icon
rectImage.DeflateRect(2,2);
pil->Draw(&dc,nImage,rectImage.TopLeft(),ILD_TRANSPARENT);
}
}
// Draw text
//
CString sText=GetItemText(nItem,0);
CRect rectText;
VERIFY(m_mapItemToTextRect.Lookup(nItem,rectText));
if(!rectText.IsRectEmpty() && !sText.IsEmpty())
{
// set transparent mode
dc.SetBkMode(TRANSPARENT);
// set text color
COLORREF clrText=GetTextColor();
VERIFY(clrText!=ID_COLOR_NONE);
dc.SetTextColor(clrText);
// set font
CFont* pOldFont=NULL;
CFont font;
if(m_pShortcutBar)
{
LPLOGFONT pLF=m_pShortcutBar->GetGroupTextFont(m_hGroup);
ASSERT(pLF);
VERIFY(font.CreateFontIndirect(pLF));
pOldFont=dc.SelectObject(&font);
}
else
{
CFont* pFont=GetFont();
if(pFont!=NULL)
{
pOldFont=dc.SelectObject(pFont);
}
}
if(!m_bCreatingDragImage)
{
// update font if special style was set
if((GetBarStyle()&SHBS_UNDERLINEHOTITEM)!=0 &&
GetHotItem()==nItem)
{
CFont* pFont=dc.GetCurrentFont();
ASSERT(pFont);
LOGFONT lf;
VERIFY(pFont->GetLogFont(&lf));
lf.lfUnderline=TRUE;
if(pOldFont!=NULL)
dc.SelectObject(pOldFont);
if((HFONT)font!=NULL)
font.DeleteObject();
VERIFY(font.CreateFontIndirect(&lf));
pOldFont=dc.SelectObject(&font);
}
}
// draw text
if(nView==SHB_LARGEICON)
{
rectText.DeflateRect(ID_TEXTLEFTMARGIN,ID_TEXTTOPMARGIN,
ID_TEXTRIGHTMARGIN,ID_TEXTBOTTOMMARGIN);
dc.DrawText(sText,rectText,DT_CENTER|DT_VCENTER|DT_WORDBREAK|
DT_EDITCONTROL|DT_END_ELLIPSIS);
}
else if(nView==SHB_SMALLICON)
{
dc.DrawText(sText,rectText,DT_LEFT|DT_VCENTER|DT_SINGLELINE);
}
else
{
ASSERT(FALSE);
}
if(pOldFont!=NULL)
{
dc.SelectObject(pOldFont);
}
}
// Restore dc
dc.RestoreDC(nSavedDC);
// Detach the dc before returning
dc.Detach();
}
void COXSHBListCtrl::DrawScrollButtons(CDC* pDC)
{
// draw scroll buttons only if needed
//
if((GetBarStyle()&SHBS_NOSCROLL)!=0)
{
TRACE(_T("COXSHBListCtrl::DrawScrollButtons: scroll buttons won't be displayed - SHBS_NOSCROLL style is set"));
return ;
}
// check if we need to draw any of scroll buttons
BOOL m_bDrawTopButton=((!m_rectTopScrollButton.IsRectEmpty()) &
pDC->RectVisible(&m_rectTopScrollButton));
BOOL m_bDrawBottomButton=((!m_rectBottomScrollButton.IsRectEmpty()) &
pDC->RectVisible(&m_rectBottomScrollButton));
if(!m_bDrawTopButton && !m_bDrawBottomButton)
{
return;
}
// Save DC
int nSavedDC=pDC->SaveDC();
ASSERT(nSavedDC);
// check if the curent cursor position is over one of the scroll button
CPoint point;
::GetCursorPos(&point);
ScreenToClient(&point);
UINT nFlags;
HitTest(point,&nFlags);
// use standard frame control as scroll button
if(m_bDrawBottomButton)
pDC->DrawFrameControl(m_rectBottomScrollButton,DFC_SCROLL,DFCS_SCROLLDOWN|
((nFlags==SHBLC_ONBOTTOMSCROLLBUTTON && m_bScrollingDown &&
(IsLButtonDown() || m_bAutoScrolling)) ? DFCS_PUSHED : 0));
if(m_bDrawTopButton)
pDC->DrawFrameControl(m_rectTopScrollButton,DFC_SCROLL,DFCS_SCROLLUP|
((nFlags==SHBLC_ONTOPSCROLLBUTTON && m_bScrollingUp &&
(IsLButtonDown() || m_bAutoScrolling)) ? DFCS_PUSHED : 0));
// Restore dc
pDC->RestoreDC(nSavedDC);
}
void COXSHBListCtrl::DrawPlaceHolder(CDC* pDC)
{
// draw place holder to drop dragged item only if needed
//
if(m_nInsertItemBefore<0 || m_nInsertItemBefore>GetItemCount())
return ;
// get rectangle that can be used to draw placeholder
CRect rectPlaceHolder=GetPlaceHolderRect(m_nInsertItemBefore);
if(!pDC->RectVisible(&rectPlaceHolder))
return;
ASSERT(!rectPlaceHolder.IsRectEmpty());
// Save DC
int nSavedDC=pDC->SaveDC();
ASSERT(nSavedDC);
// by default use black color
COLORREF clr=RGB(0,0,0);
// if the background color is too close to the black then use white color
// to draw the placeholder
if(IsColorCloseTo(clr,GetBkColor(),64))
clr=RGB(255,255,255);
CPen pen(PS_SOLID,1,clr);
CPen* pOldPen=pDC->SelectObject(&pen);
// place holder for the top item
if(m_nInsertItemBefore==0)
{
pDC->MoveTo(rectPlaceHolder.left,rectPlaceHolder.top);
pDC->LineTo(rectPlaceHolder.right,rectPlaceHolder.top);
int nHeight=2*ID_HEIGHTPLACEHOLDER/3;
for(int nIndex=1; nIndex<=nHeight; nIndex++)
{
pDC->MoveTo(rectPlaceHolder.left,rectPlaceHolder.top+nIndex);
pDC->LineTo(rectPlaceHolder.left+(nHeight-nIndex),
rectPlaceHolder.top+nIndex);
pDC->MoveTo(rectPlaceHolder.right-(nHeight-nIndex),
rectPlaceHolder.top+nIndex);
pDC->LineTo(rectPlaceHolder.right,rectPlaceHolder.top+nIndex);
}
}
// place holder for the bottom item
else if(m_nInsertItemBefore==GetItemCount())
{
pDC->MoveTo(rectPlaceHolder.left,rectPlaceHolder.bottom-1);
pDC->LineTo(rectPlaceHolder.right,rectPlaceHolder.bottom-1);
int nHeight=2*ID_HEIGHTPLACEHOLDER/3;
for(int nIndex=1; nIndex<=nHeight; nIndex++)
{
pDC->MoveTo(rectPlaceHolder.left,rectPlaceHolder.bottom-nIndex);
pDC->LineTo(rectPlaceHolder.left+(nHeight-nIndex),
rectPlaceHolder.bottom-nIndex);
pDC->MoveTo(rectPlaceHolder.right-(nHeight-nIndex),
rectPlaceHolder.bottom-nIndex);
pDC->LineTo(rectPlaceHolder.right,rectPlaceHolder.bottom-nIndex);
}
}
// place holder is located between two existing items
else
{
pDC->MoveTo(rectPlaceHolder.left,(rectPlaceHolder.top+rectPlaceHolder.bottom)/2);
pDC->LineTo(rectPlaceHolder.right,(rectPlaceHolder.top+rectPlaceHolder.bottom)/2);
int nHeight=ID_HEIGHTPLACEHOLDER/2;
for(int nIndex=1; nIndex<=nHeight; nIndex++)
{
pDC->MoveTo(rectPlaceHolder.left,
(rectPlaceHolder.top+rectPlaceHolder.bottom)/2+nIndex);
pDC->LineTo(rectPlaceHolder.left+nHeight-nIndex,
(rectPlaceHolder.top+rectPlaceHolder.bottom)/2+nIndex);
pDC->MoveTo(rectPlaceHolder.right-nHeight+nIndex,
(rectPlaceHolder.top+rectPlaceHolder.bottom)/2+nIndex);
pDC->LineTo(rectPlaceHolder.right,
(rectPlaceHolder.top+rectPlaceHolder.bottom)/2+nIndex);
pDC->MoveTo(rectPlaceHolder.left,
(rectPlaceHolder.top+rectPlaceHolder.bottom)/2-nIndex);
pDC->LineTo(rectPlaceHolder.left+nHeight-nIndex,
(rectPlaceHolder.top+rectPlaceHolder.bottom)/2-nIndex);
pDC->MoveTo(rectPlaceHolder.right-nHeight+nIndex,
(rectPlaceHolder.top+rectPlaceHolder.bottom)/2-nIndex);
pDC->LineTo(rectPlaceHolder.right,
(rectPlaceHolder.top+rectPlaceHolder.bottom)/2-nIndex);
}
}
if(pOldPen!=NULL)
pDC->SelectObject(pOldPen);
// Restore dc
pDC->RestoreDC(nSavedDC);
}
void COXSHBListCtrl::FillBackground(CDC* pDC)
{
// just fill background with preset background color
COLORREF clrBackground = GetBkColor();
VERIFY(clrBackground!=ID_COLOR_NONE);
CBrush brush(clrBackground);
CRect rect;
GetClientRect(rect);
int iSavedDC = pDC->SaveDC();
// Exclude parts that will be covered by items
int nItem;
POSITION pos=m_mapItemToBoundRect.GetStartPosition();
while(pos!=NULL)
{
CRect rectExclude;
m_mapItemToBoundRect.GetNextAssoc(pos,nItem,rectExclude);
rectExclude -= m_ptOrigin;
ASSERT(nItem>=0 && nItem<GetItemCount());
pDC->ExcludeClipRect(rectExclude);
}
// Fill only region that wasn't taken by items
if (m_pShortcutBar != NULL)
{
if (HasBkColor())
m_pShortcutBar->GetShortcutBarSkin()->FillBackground(pDC, rect, this, TRUE, GetBkColor());
else
m_pShortcutBar->GetShortcutBarSkin()->FillBackground(pDC, rect, this);
}
else
pDC->FillSolidRect(rect, clrBackground);
pDC->RestoreDC(iSavedDC);
}
int COXSHBListCtrl::HitTest(CPoint pt, UINT* pFlags, BOOL bOnlyItems/* = FALSE*/) const
{
// Scroll buttons have the highest priority (top buttom is prior to bottom button).
// As long as scroll buttons may cover list control items you can specify
// bOnlyItems argument as TRUE to test the specified point only on item locations.
//
if(!bOnlyItems && (GetBarStyle()&SHBS_NOSCROLL)==0)
{
// point is over the top scroll button
if(!m_rectTopScrollButton.IsRectEmpty() &&
m_rectTopScrollButton.PtInRect(pt))
{
*pFlags=SHBLC_ONTOPSCROLLBUTTON;
return -1;
}
// point is over the bottom scroll button
if(!m_rectBottomScrollButton.IsRectEmpty() &&
m_rectBottomScrollButton.PtInRect(pt))
{
*pFlags=SHBLC_ONBOTTOMSCROLLBUTTON;
return -1;
}
}
int nItem=-1;
*pFlags=0;
// adjust point
pt+=m_ptOrigin;
// get rect that covers all current visible items
CRect rectView;
GetViewRect(rectView,TRUE);
if(rectView.PtInRect(pt))
{
*pFlags=LVHT_NOWHERE;
CRect rect;
// try to find item rect that include the point
POSITION pos=m_mapItemToBoundRect.GetStartPosition();
while(pos!=NULL)
{
m_mapItemToBoundRect.GetNextAssoc(pos,nItem,rect);
ASSERT(nItem>=0 && nItem<GetItemCount());
if(rect.PtInRect(pt))
{
// point can be within image rect, text rect, on image and text
// united rect or on item rect
CRect rectImage;
VERIFY(m_mapItemToImageRect.Lookup(nItem,rectImage));
rectImage+=rect.TopLeft();
// point is on the image
if(rectImage.PtInRect(pt))
*pFlags=LVHT_ONITEMICON;
else
{
CRect rectText;
VERIFY(m_mapItemToTextRect.Lookup(nItem,rectText));
rectText+=rect.TopLeft();
// point is on the text
if(rectText.PtInRect(pt))
*pFlags=LVHT_ONITEMLABEL;
else
{
CRect rectImageAndText;
rectImageAndText.left=__min(rectImage.left,rectText.left);
rectImageAndText.right=__max(rectImage.right,rectText.right);
rectImageAndText.top=__min(rectImage.top,rectText.top);
rectImageAndText.bottom=__max(rectImage.bottom,rectText.bottom);
// point is on the item bounding rectangle
if(rectImageAndText.PtInRect(pt))
*pFlags=SHBLC_ONIMAGEANDTEXT;
// point is on the entire item rectangle
else
*pFlags=SHBLC_ONITEM;
}
}
break;
}
}
if(*pFlags==LVHT_NOWHERE)
nItem=-1;
}
else
{
if(pt.x<rectView.left)
*pFlags|=LVHT_TOLEFT;
else if(pt.x>rectView.right)
*pFlags|=LVHT_TORIGHT;
if(pt.y<rectView.top)
*pFlags|=LVHT_ABOVE;
else if(pt.y>rectView.bottom)
*pFlags|=LVHT_BELOW;
}
return nItem;
}
BOOL COXSHBListCtrl::GetViewRect(LPRECT lpRectView,
BOOL bOnlyVisible/* = FALSE*/) const
{
// try to find rect that cover all list control items (if bOnlyVisible is TRUE,
// then only visible items)
CRect rectClient;
GetClientRect(rectClient);
// deflate client rect by margins so there is some space in the control that
// is not covered by items
rectClient.DeflateRect(ID_EDGELEFTMARGIN,ID_EDGETOPMARGIN,ID_EDGERIGHTMARGIN,
ID_EDGEBOTTOMMARGIN);
CRect rectView=rectClient;
int nBottom=0;
CRect rect;
int nItem;
POSITION pos=m_mapItemToBoundRect.GetStartPosition();
while(pos!=NULL)
{
m_mapItemToBoundRect.GetNextAssoc(pos,nItem,rect);
ASSERT(nItem>=0 && nItem<GetItemCount());
if(nBottom<rect.bottom)
nBottom=rect.bottom;
if(bOnlyVisible && nBottom>rectClient.bottom+m_ptOrigin.y)
break;
}
rectView.bottom=nBottom;
if(bOnlyVisible)
{
rectView.top=__max(rectView.top,rectView.top+m_ptOrigin.y);
rectView.bottom=__min(rectView.bottom,rectClient.bottom+m_ptOrigin.y);
}
lpRectView->left=rectView.left;
lpRectView->right=rectView.right;
lpRectView->top=rectView.top;
lpRectView->bottom=rectView.bottom;
return TRUE;
}
BOOL COXSHBListCtrl::GetItemRect(int nItem, LPRECT lpRect, UINT nCode) const
{
ASSERT(nItem>=0 && nItem<GetItemCount());
CRect rect;
VERIFY(m_mapItemToBoundRect.Lookup(nItem,rect));
rect-=m_ptOrigin;
switch(nCode)
{
case LVIR_ICON:
{
// image rect
CRect rectImage;
rectImage.SetRectEmpty();
m_mapItemToImageRect.Lookup(nItem,rectImage);
rectImage+=rect.TopLeft();
lpRect->left=rectImage.left;
lpRect->top=rectImage.top;
lpRect->right=rectImage.right;
lpRect->bottom=rectImage.bottom;
break;
}
case LVIR_LABEL:
{
// text rect
CRect rectText;
rectText.SetRectEmpty();
VERIFY(m_mapItemToTextRect.Lookup(nItem,rectText));
rectText+=rect.TopLeft();
lpRect->left=rectText.left;
lpRect->top=rectText.top;
lpRect->right=rectText.right;
lpRect->bottom=rectText.bottom;
break;
}
case LVIR_BOUNDS:
{
// image and text united rect
CRect rectImage;
rectImage.SetRectEmpty();
m_mapItemToImageRect.Lookup(nItem,rectImage);
rectImage+=rect.TopLeft();
CRect rectText;
rectText.SetRectEmpty();
VERIFY(m_mapItemToTextRect.Lookup(nItem,rectText));
rectText+=rect.TopLeft();
lpRect->left=__min(rectText.left,rectImage.left);
lpRect->top=__min(rectText.top,rectImage.top);
lpRect->right=__max(rectText.right,rectImage.right);
lpRect->bottom=__max(rectText.bottom,rectImage.bottom);
break;
}
case SHBLC_ENTIREITEM:
{
// entire item rect
lpRect->left=rect.left;
lpRect->top=rect.top;
lpRect->right=rect.right;
lpRect->bottom=rect.bottom;
break;
}
default:
ASSERT(FALSE);
}
return TRUE;
}
BOOL COXSHBListCtrl::GetItemPosition(int nItem, LPPOINT lpPoint) const
{
ASSERT(nItem>=0 && nItem<GetItemCount());
CRect rect;
VERIFY(m_mapItemToBoundRect.Lookup(nItem,rect));
// adjust rect
rect-=m_ptOrigin;
lpPoint->x=rect.left;
lpPoint->y=rect.right;
return TRUE;
}
BOOL COXSHBListCtrl::GetScrollableRect(CRect& rectScrollable, int nTopItem /*=-1*/) const
{
// return the size of rect that list control can be scrolled to
//
CRect rectClient;
GetClientRect(rectClient);
// deflate client rect by margins so there is some space in the control that
// is not covered by items
rectClient.DeflateRect(ID_EDGELEFTMARGIN,ID_EDGETOPMARGIN,ID_EDGERIGHTMARGIN,
ID_EDGEBOTTOMMARGIN);
if(rectClient.top>=rectClient.bottom)
return FALSE;
CRect rectView;
GetViewRect(rectView);
rectScrollable=rectView;
// if all items fit the client area then just return view rect as the
// scrollable one
if((rectView.Height()+m_ptOrigin.y)<=rectClient.Height())
return TRUE;
// if the last item is visible then just return client rect plus origin offset
// as scrollable one
if(rectView.Height()<=(rectClient.Height()+m_ptOrigin.y))
{
rectScrollable.bottom+=rectClient.Height()+m_ptOrigin.y-rectView.Height();
return TRUE;
}
CRect rect;
// we can specify the top item that should be taken into account while
// calculating the scrollable rect as argument or if it is set to -1 then
// we will found it using GetTopIndex function
if(nTopItem==-1)
{
nTopItem=GetTopIndex();
VERIFY(m_mapItemToBoundRect.Lookup(nTopItem,rect));
if(m_ptOrigin.y+rectClient.top>rect.top)
if(nTopItem==GetItemCount()-1)
return TRUE;
else
nTopItem++;
}
// resulted top item must be in the valid range
ASSERT(nTopItem>=0 && nTopItem<GetItemCount());
VERIFY(m_mapItemToBoundRect.Lookup(nTopItem,rect));
int nTop=rect.top;
VERIFY(m_mapItemToBoundRect.Lookup(GetItemCount()-1,rect));
int nBottom=rect.bottom;
if(nBottom-nTop<rectClient.Height())
rectScrollable.bottom+=rectClient.Height()-(nBottom-nTop);
ASSERT(rectScrollable.Height()>=rectView.Height());
return TRUE;
}
DWORD COXSHBListCtrl::GetBarStyle() const
{
if(m_pShortcutBar)
return m_pShortcutBar->GetBarStyle();
else
{
// we use the set of our styles to define the list control functionality
//
DWORD dwBarStyle=0;
DWORD dwStyle=GetStyle();
if((dwStyle&LVS_EDITLABELS)!=0)
dwBarStyle|=SHBS_EDITITEMS;
if((dwStyle&LVS_NOSCROLL)!=0)
dwBarStyle|=SHBS_NOSCROLL;
if((dwStyle&LVS_SHOWSELALWAYS)!=0)
dwBarStyle|=SHBS_SHOWACTIVEALWAYS;
if((dwStyle&LVS_NOSCROLL)!=0)
dwBarStyle|=SHBS_NOSCROLL;
DWORD dwExStyle=GetExStyle();
if((dwExStyle&LVS_EX_UNDERLINEHOT)!=0)
dwBarStyle|=SHBS_UNDERLINEHOTITEM;
if((dwExStyle&LVS_EX_INFOTIP)!=0)
dwBarStyle|=SHBS_INFOTIP;
return dwBarStyle;
}
}
LRESULT COXSHBListCtrl::SendSHBNotification(LPNMSHORTCUTBAR pNMSHB) const
{
ASSERT(::IsWindow(GetSafeHwnd()));
// if parent window of that control is shortcut bar then send notification to
// its parent
if(m_pShortcutBar)
{
return m_pShortcutBar->SendSHBNotification(pNMSHB);
}
else
{
// send standard shortcut bar notification to list control parent using
// NMSHORTCUTBAR structure
// notify parent
CWnd* pParentWnd=GetOwner();
if(pParentWnd)
{
// fill notification structure
pNMSHB->hdr.hwndFrom=GetSafeHwnd();
pNMSHB->hdr.idFrom=GetDlgCtrlID();
return (pParentWnd->SendMessage(
WM_NOTIFY,(WPARAM)GetDlgCtrlID(),(LPARAM)pNMSHB));
}
else
{
return (LRESULT)0;
}
}
}
int COXSHBListCtrl::GetView() const
{
// At the moment we defined only two views: Large Icons & Small Icons
if(m_pShortcutBar)
{
int nView=m_pShortcutBar->GetGroupView(m_hGroup);
VERIFY(m_pShortcutBar->VerifyView(nView));
return nView;
}
else
{
DWORD dwStyle=GetStyle();
if((dwStyle&LVS_SMALLICON)!=0)
return SHB_SMALLICON;
else
return SHB_LARGEICON;
}
}
CSize COXSHBListCtrl::GetScrollButtonSize() const
{
if(m_pShortcutBar)
return m_pShortcutBar->GetScrollButtonSize();
else
return CSize(DFLT_SCROLLBUTTONWIDTH,DFLT_SCROLLBUTTONHEIGHT);
}
UINT COXSHBListCtrl::GetScrollingDelay() const
{
if(m_pShortcutBar)
return m_pShortcutBar->GetScrollingDelay();
else
return DFLT_SCROLLINGDELAY;
}
UINT COXSHBListCtrl::GetAutoScrollingDelay() const
{
if(m_pShortcutBar)
return m_pShortcutBar->GetAutoScrollingDelay();
else
return DFLT_AUTOSCROLLINGDELAY;
}
int COXSHBListCtrl::SelectItem(int nItem)
{
ASSERT(nItem<GetItemCount());
if(nItem==m_nSelectedItem)
return m_nSelectedItem;
int nOldItem=m_nSelectedItem;
// In the case this control was created as a child window
// of shortcut bar group fill structure for notification of
// parent window of this shortcut bar
NMSHORTCUTBAR nmshb;
nmshb.hdr.code=SHBN_SELECTITEM;
nmshb.hGroup=m_hGroup;
nmshb.nItem=nItem;
if(SendSHBNotification(&nmshb))
return m_nSelectedItem;
m_nSelectedItem=nItem;
return nOldItem;
}
int COXSHBListCtrl::SetHotItem(int nItem)
{
ASSERT(nItem<GetItemCount());
if(nItem==m_nHotItem)
return m_nHotItem;
int nOldItem=m_nHotItem;
// In the case this control was created as a child window
// of shortcut bar group fill structure for notification of
// parent window of this shortcut bar
NMSHORTCUTBAR nmshb;
nmshb.hdr.code=SHBN_HOTITEM;
nmshb.hGroup=m_hGroup;
nmshb.nItem=nItem;
if(SendSHBNotification(&nmshb))
return m_nHotItem;
m_nHotItem=nItem;
m_nLastHotItem=nOldItem;
return nOldItem;
}
int COXSHBListCtrl::ActivateItem(int nItem)
{
ASSERT(nItem<GetItemCount());
if(nItem==m_nActiveItem)
{
return m_nActiveItem;
}
int nOldItem=m_nActiveItem;
// In the case this control was created as a child window
// of shortcut bar group fill structure for notification of
// parent window of this shortcut bar
NMSHORTCUTBAR nmshb;
nmshb.hdr.code=SHBN_ACTIVATEITEM;
nmshb.hGroup=m_hGroup;
nmshb.nItem=nItem;
if(SendSHBNotification(&nmshb))
{
return m_nActiveItem;
}
m_nActiveItem=nItem;
Update(nOldItem);
Update(m_nActiveItem);
return nOldItem;
}
int COXSHBListCtrl::SetDropTargetItem(int nItem)
{
ASSERT(nItem<GetItemCount());
int nOldItem=m_nDropHilightItem;
m_nDropHilightItem=nItem;
return nOldItem;
}
int COXSHBListCtrl::SetDragItem(int nItem)
{
ASSERT(nItem<GetItemCount());
if(nItem==m_nDragItem)
return m_nDragItem;
int nOldItem=m_nDragItem;
m_nDragItem=nItem;
if(nItem!=-1)
{
// In the case this control was created as a child window
// of shortcut bar group fill structure for notification of
// parent window of this shortcut bar
NMSHORTCUTBAR nmshb;
nmshb.hdr.code=SHBN_BEGINDRAGITEM;
nmshb.hGroup=m_hGroup;
nmshb.nItem=nItem;
nmshb.lParam=(LPARAM)&m_ptDragImage;
SendSHBNotification(&nmshb);
// CF_TEXT format
//
// get our data ready to cache
CString sText=GetItemText(nItem,0);
// allocate our data object
COleDataSource* pDataSource;
pDataSource = new COleDataSource;
// Load a CF_TEXT to the data object
HGLOBAL hgCF_TEXTData=GlobalAlloc(GPTR,(sText.GetLength()+1)*sizeof(TCHAR));
BYTE* lpCF_TEXTData=(BYTE*)GlobalLock(hgCF_TEXTData);
lstrcpy((LPTSTR)lpCF_TEXTData,sText);
GlobalUnlock(hgCF_TEXTData);
// save the text data
pDataSource->CacheGlobalData(CF_TEXT,hgCF_TEXTData);
//////////////////////
// internal COXShortcutBar::m_nChildWndItemFormat format
//
// (text) + (NULL) + lParam + (number of images elements) +
// (SHBIMAGEDROPINFO + size of image list + ImageList) +
// (SHBIMAGEDROPINFO + size of image list + ImageList) + ...+
// (size of additional information) + (additional information)
//
// in this case we use only two images (large and small)
//
// In the case this control was created as a child window
// of shortcut bar group fill structure for notification of
// parent window of this shortcut bar
SHBADDITIONALDROPINFO shbadi;
::ZeroMemory(&nmshb,sizeof(NMSHORTCUTBAR));
nmshb.hdr.code=SHBN_GETADDITIONALDROPINFO;
nmshb.hGroup=m_hGroup;
nmshb.nItem=nItem;
nmshb.lParam=(LPARAM)&shbadi;
SendSHBNotification(&nmshb);
// check the integrity
ASSERT((shbadi.nBufferLength>0 && shbadi.pBuffer!=NULL) ||
(shbadi.nBufferLength==0 && shbadi.pBuffer==NULL));
// get image index
int nImage=GetItemImage(nItem);
DWORD nLargeImageSize=0;
void* pilLargeBuffer=NULL;
DWORD nSmallImageSize=0;
void* pilSmallBuffer=NULL;
// create image lists for large and small button correspondingly
if(nImage>=0)
{
CImageList* pil=GetImageList(LVSIL_NORMAL);
if(pil!=NULL)
pilLargeBuffer=CopyImageFromIL(pil,nImage,nLargeImageSize);
pil=GetImageList(LVSIL_SMALL);
if(pil!=NULL)
pilSmallBuffer=CopyImageFromIL(pil,nImage,nSmallImageSize);
}
int nEntryMemSize=(sText.GetLength()+1)*sizeof(TCHAR)+
sizeof(DWORD)+sizeof(DWORD);
DWORD nImageCount=0;
if(nImage>=0)
{
if(nLargeImageSize>0)
{
nEntryMemSize+=sizeof(SHBIMAGEDROPINFO)+sizeof(DWORD)+nLargeImageSize;
nImageCount++;
}
if(nSmallImageSize>0)
{
nEntryMemSize+=sizeof(SHBIMAGEDROPINFO)+sizeof(DWORD)+nSmallImageSize;
nImageCount++;
}
}
nEntryMemSize+=sizeof(DWORD)+shbadi.nBufferLength;
// allocate the memory
HGLOBAL hgItemData=GlobalAlloc(GPTR,nEntryMemSize);
BYTE* lpItemData=(BYTE*)GlobalLock(hgItemData);
ZeroMemory(lpItemData,nEntryMemSize);
// write item text into global memory
lstrcpy((LPTSTR)lpItemData,sText);
lpItemData+=(sText.GetLength()+1)*sizeof(TCHAR);
// write item lParam into global memory
(*(DWORD*)lpItemData)=(DWORD)GetItemData(nItem);
lpItemData+=sizeof(DWORD);
// write the item image info to gloabal memory
//
// set number of images associated with the item
(*(DWORD*)lpItemData)=nImageCount;
lpItemData+=sizeof(DWORD);
if(nLargeImageSize>0)
{
// fill special image info structure
SHBIMAGEDROPINFO imageInfo;
// our image is the "NORMAL" one
imageInfo.imageType=SHBIT_NORMAL;
imageInfo.imageState=SHBIS_LARGE;
// write SHBIMAGEDROPINFO structure
memcpy((void*)lpItemData,(void*)&imageInfo,sizeof(SHBIMAGEDROPINFO));
lpItemData+=sizeof(SHBIMAGEDROPINFO);
// write the size of image
(*(DWORD*)lpItemData)=nLargeImageSize;
lpItemData+=sizeof(DWORD);
// write image list
memcpy((void*)lpItemData,pilLargeBuffer,nLargeImageSize);
lpItemData+=nLargeImageSize;
}
if(nSmallImageSize>0)
{
// fill special image info structure
SHBIMAGEDROPINFO imageInfo;
// our image is the "NORMAL" one
imageInfo.imageType=SHBIT_NORMAL;
imageInfo.imageState=SHBIS_SMALL;
// write SHBIMAGEDROPINFO structure
memcpy((void*)lpItemData,(void*)&imageInfo,sizeof(SHBIMAGEDROPINFO));
lpItemData+=sizeof(SHBIMAGEDROPINFO);
// write the size of image
(*(DWORD*)lpItemData)=nSmallImageSize;
lpItemData+=sizeof(DWORD);
// write image list
memcpy((void*)lpItemData,pilSmallBuffer,nSmallImageSize);
lpItemData+=nSmallImageSize;
}
// write the additional item info to gloabal memory
//
// set the amount of bytes of additional info
(*(DWORD*)lpItemData)=shbadi.nBufferLength;
lpItemData+=sizeof(DWORD);
if(shbadi.nBufferLength>0)
{
// write additional info
memcpy((void*)lpItemData,shbadi.pBuffer,shbadi.nBufferLength);
lpItemData+=shbadi.nBufferLength;
}
GlobalUnlock(hgItemData);
// save the item data
pDataSource->CacheGlobalData(COXShortcutBar::m_nChildWndItemFormat,hgItemData);
if(pilLargeBuffer!=NULL)
free(pilLargeBuffer);
if(pilSmallBuffer!=NULL)
free(pilSmallBuffer);
// post special message to handle drag'n'drop operation
PostMessage(SHBLCM_HANDLEDRAG,(WPARAM)nItem,(LPARAM)pDataSource);
}
return nOldItem;
}
BOOL COXSHBListCtrl::Update(int nItem)
{
if(nItem<0 || nItem>=GetItemCount())
return FALSE;
// get the item rect
CRect rectItem;
VERIFY(m_mapItemToBoundRect.Lookup(nItem,rectItem));
// adjust it
rectItem-=m_ptOrigin;
CRect rect;
if(m_mapItemToImageRect.Lookup(nItem,rect))
{
rect+=rectItem.TopLeft();
// redraw image rect
RedrawWindow(rect);
}
VERIFY(m_mapItemToTextRect.Lookup(nItem,rect));
rect+=rectItem.TopLeft();
// redraw text rect
RedrawWindow(rect);
return TRUE;
}
BOOL COXSHBListCtrl::RedrawItems(int nFirst, int nLast)
{
for(int nIndex=nFirst; nIndex<=nLast; nIndex++)
if(!Update(nIndex))
return FALSE;
return TRUE;
}
void COXSHBListCtrl::AnticipateMouseMessages()
{
CPoint point;
::GetCursorPos(&point);
ScreenToClient(&point);
// test the cursor position
UINT nHitFlags;
int nItem=HitTest(point,&nHitFlags);
if(!m_bMouseIsOver || (nItem!=-1 &&
(nHitFlags==SHBLC_ONITEM || nHitFlags==SHBLC_ONIMAGEANDTEXT)))
{
nItem=-1;
}
int nOldHotItem=SetHotItem(nItem);
if(GetDragItem()==-1 && nItem!=-1 &&
(GetBarStyle()&SHBS_DISABLEDRAGITEM)==0 && m_nSuspectDragItem==nItem)
{
if(abs(point.x-m_ptClickedLButton.x)+abs(point.y-m_ptClickedLButton.y)>8)
{
m_ptDragImage+=point-m_ptClickedLButton;
// assign item being dragged
SetDragItem(nItem);
}
}
// reset suspect drag item index
if(!IsLButtonDown() || GetDragItem()!=-1)
m_nSuspectDragItem=-1;
// update changed items
//
if(nItem!=-1)
Update(nItem);
if(nItem!=nOldHotItem && nOldHotItem!=-1)
Update(nOldHotItem);
// update scroll button images
//
if(m_bScrollingUp && nHitFlags!=SHBLC_ONTOPSCROLLBUTTON)
RedrawWindow(m_rectTopScrollButton);
if(m_bScrollingDown && nHitFlags!=SHBLC_ONBOTTOMSCROLLBUTTON)
RedrawWindow(m_rectBottomScrollButton);
// check autoscrolling
if((GetBarStyle()&SHBS_AUTOSCROLL)!=0 && GetDragItem()==-1)
{
if(!m_bScrollingUp && nHitFlags==SHBLC_ONTOPSCROLLBUTTON)
{
m_bAutoScrolling=TRUE;
StartScrolling(TRUE);
}
else if(!m_bScrollingDown && nHitFlags==SHBLC_ONBOTTOMSCROLLBUTTON)
{
m_bAutoScrolling=TRUE;
StartScrolling(FALSE);
}
}
// check conditions to stop autoscrolling
if(m_bAutoScrolling && ((m_bScrollingUp && nHitFlags!=SHBLC_ONTOPSCROLLBUTTON) ||
(m_bScrollingDown && nHitFlags!=SHBLC_ONBOTTOMSCROLLBUTTON)))
{
StopScrolling();
}
}
BOOL COXSHBListCtrl::Scroll(CSize size)
{
// origin mustn't be negative
ASSERT(m_ptOrigin.x>=0 || m_ptOrigin.y>=0);
if(GetItemCount()==0)
{
return FALSE;
}
CRect rectClient;
GetClientRect(rectClient);
if(rectClient.IsRectEmpty())
{
return FALSE;
}
// deflate client rect to leave some space free of items
rectClient.DeflateRect(
ID_EDGELEFTMARGIN,ID_EDGETOPMARGIN,ID_EDGERIGHTMARGIN,ID_EDGEBOTTOMMARGIN);
// get current top index
int nTopItem=GetTopIndex();
ASSERT(nTopItem>=0 && nTopItem<GetItemCount());
// scroll up
if(size.cy<0)
nTopItem=(nTopItem>0) ? nTopItem-1 : nTopItem;
else
// scroll down
if(size.cy>0)
nTopItem=(nTopItem==GetItemCount()-1) ? nTopItem : nTopItem+1;
// get scrollbale rect to check validility of scroll operation
CRect rectScrollable;
if(!GetScrollableRect(rectScrollable,nTopItem))
return FALSE;
CRect rectAggregate=rectClient;
rectAggregate+=m_ptOrigin;
rectAggregate+=size;
// scroll up
if(size.cy<0)
{
int nTopMargin=rectAggregate.top-rectScrollable.top;
if(nTopMargin<=size.cy)
{
size.cy=0;
}
else
{
size.cy-=(nTopMargin<0) ? nTopMargin : 0;
ASSERT(size.cy<0);
}
}
else
// scroll down
if(size.cy>0)
{
int nBottomMargin=rectAggregate.bottom-rectScrollable.bottom;
if(nBottomMargin>=size.cy)
size.cy=0;
else
{
size.cy-=(nBottomMargin>0) ? nBottomMargin : 0;
ASSERT(size.cy>0);
}
}
// scroll left
if(size.cx<0)
{
int nLeftMargin=rectAggregate.left-rectScrollable.left;
if(nLeftMargin<=size.cx)
{
size.cx=0;
}
else
{
size.cx-=(nLeftMargin<0) ? nLeftMargin : 0;
ASSERT(size.cx<0);
}
}
else
// scroll right
if(size.cx>0)
{
int nRightMargin=rectAggregate.right-rectScrollable.right;
if(nRightMargin>=size.cx)
{
size.cx=0;
}
else
{
size.cx-=(nRightMargin>0) ? nRightMargin : 0;
ASSERT(size.cx>0);
}
}
if(size.cx==0 && size.cy==0)
return FALSE;
// update view origin
m_ptOrigin.x+=size.cx;
m_ptOrigin.y+=size.cy;
// reposition items and redraw the control
CalculateScrollRects();
RedrawWindow();
return TRUE;
}
int COXSHBListCtrl::FindItem(LV_FINDINFO* pFindInfo, int nStart/* = -1*/) const
{
int nCount=GetItemCount();
if(nCount==0)
return -1;
// verify nStart argument
ASSERT(nStart+1>=0 && nStart<nCount);
int nItem=-1;
// only if searching by coordinates specified we provide our own implementation
if(pFindInfo->flags&LVFI_NEARESTXY)
{
CPoint point=pFindInfo->pt;
// adjuxt the coordinates
point+=m_ptOrigin;
int nNearestDistance=-1;
int nNearestItem=-1;
CRect rect;
for(int nIndex=nStart+1; nIndex<nCount+
((pFindInfo->flags&LVFI_WRAP) ? nStart+1 : 0); nIndex++)
{
nItem=nIndex;
if(nItem>=nCount)
nItem-=nCount;
VERIFY(m_mapItemToBoundRect.Lookup(nItem,rect));
// bottom mustn't be include (the same way PtInRect works)
if(rect.PtInRect(point) || (point.y>=rect.top && point.y<rect.bottom))
{
// if the point is in entire item rect then the search is completed
nNearestItem=nItem;
break;
}
int nMargin=-1;
switch(pFindInfo->vkDirection)
{
// search for upper items
case VK_UP:
{
nMargin=point.y-rect.bottom;
break;
}
// search for down items
case VK_DOWN:
{
nMargin=rect.top-point.y;
break;
}
}
if(nMargin>0 && (nNearestDistance==-1 || nNearestDistance>nMargin))
{
nNearestDistance=nMargin;
nNearestItem=nItem;
}
}
nItem=nNearestItem;
}
else
nItem=CListCtrl::FindItem(pFindInfo,nStart);
return nItem;
}
int COXSHBListCtrl::GetTopIndex() const
{
LV_FINDINFO lvfi;
lvfi.flags=LVFI_NEARESTXY;
lvfi.pt.x=0;
lvfi.pt.y=ID_EDGETOPMARGIN;
lvfi.vkDirection=VK_DOWN;
return FindItem(&lvfi);
}
BOOL COXSHBListCtrl::EnsureVisible(int nItem, BOOL bPartialOK)
{
if(nItem<0 || nItem>=GetItemCount())
return FALSE;
// entire item rectangle
CRect rect;
VERIFY(m_mapItemToBoundRect.Lookup(nItem,rect));
// visible view rectangle
CRect rectViewVisible;
GetViewRect(rectViewVisible,TRUE);
// if entire item rectangle height is more the than the visible rectangle height
// and partial visibility is not Ok then we've got Mission Impossible
if(rect.Height()>=rectViewVisible.Height() && !bPartialOK)
return FALSE;
// if their intersection is not empty and partial visibility is Ok
// then the mission is completed
CRect rectIntersection;
if(rectIntersection.IntersectRect(rectViewVisible,rect) && bPartialOK)
return TRUE;
// if their intersection is equal to the entire item rectangle
// then the mission is completed either
if(rectIntersection==rect)
return TRUE;
// scroll the control to update view origin
//
if(rect.bottom<rectViewVisible.top)
Scroll(CSize(0,rect.top-rectViewVisible.top));
else if(rect.top>rectViewVisible.bottom)
Scroll(CSize(0,rect.bottom-rectViewVisible.bottom));
else if(rect.bottom>rectViewVisible.bottom)
Scroll(CSize(0,rect.bottom-rectViewVisible.bottom));
else if(rect.top<rectViewVisible.top)
Scroll(CSize(0,rect.top-rectViewVisible.top));
if(bPartialOK)
return TRUE;
GetViewRect(rectViewVisible,TRUE);
// check if size of item is more than visible rect
if(rectIntersection.IntersectRect(rectViewVisible,rect) && rectIntersection==rect)
return TRUE;
else
return FALSE;
}
COXSHBEdit* COXSHBListCtrl::EditLabel(int nItem)
{
ASSERT(nItem>=0 && nItem<GetItemCount());
// validate edit control
ASSERT(::IsWindow(m_edit.GetSafeHwnd()));
// check if editing is allowed
if((GetBarStyle()&SHBS_EDITITEMS)==0)
{
TRACE(_T("COXSHBListCtrl::EditLabel: cannot edit item - SHBS_EDITITEMS style wasn't set"));
return NULL;
}
// finish edit if any is undergoing
if(m_nEditItem!=-1)
{
m_edit.FinishEdit(TRUE);
m_nEditItem=-1;
}
CString sText=GetItemText(nItem,0);
// In the case this control was created as a child window
// of shortcut bar group fill structure for notification of
// parent window of this shortcut bar
NMSHORTCUTBAR nmshb;
nmshb.hdr.code=SHBN_BEGINITEMEDIT;
nmshb.hGroup=m_hGroup;
nmshb.nItem=nItem;
nmshb.lParam=(LPARAM)((LPCTSTR)sText);
// check if parent doesn't let us to edit item text
if(SendSHBNotification(&nmshb))
return NULL;
CRect rectClient;
GetClientRect(rectClient);
rectClient.DeflateRect(
ID_EDGELEFTMARGIN,ID_EDGETOPMARGIN,ID_EDGERIGHTMARGIN,ID_EDGEBOTTOMMARGIN);
CRect rectText;
if(!GetItemRect(nItem,rectText,LVIR_LABEL))
{
TRACE(_T("COXSHBListCtrl::EditLabel: failed to get item text rectangle"));
return NULL;
}
if(rectText.IsRectEmpty())
{
rectText.SetRectEmpty();
CRect rectImage;
if(!GetItemRect(nItem,rectImage,LVIR_ICON))
{
TRACE(_T("COXSHBListCtrl::EditLabel: failed to get item image rectangle"));
return NULL;
}
CRect rectItem;
if(!GetItemRect(nItem,rectItem,SHBLC_ENTIREITEM))
{
TRACE(_T("COXSHBListCtrl::EditLabel: failed to get full item rectangle"));
return NULL;
}
rectText=AdjustTextRect(_T(" "),rectImage,rectItem.Width());
ASSERT(!rectText.IsRectEmpty());
}
CRect rectIntersect;
if(!rectIntersect.IntersectRect(rectClient,rectText) ||
rectIntersect!=rectText)
{
TRACE(_T("COXSHBListCtrl::EditLabel: failed to edit - item text rectangle is not entirely visible"));
return NULL;
}
int nView=GetView();
// make some adjustments to make our edit rect look prettier
if(nView==SHB_SMALLICON)
{
rectText.OffsetRect(-ID_TEXTLEFTMARGIN,0);
VERIFY(rectIntersect.IntersectRect(rectClient,rectText));
}
// get font to use in edit control
CFont font;
LPLOGFONT pLF=NULL;
LOGFONT lf;
if(m_pShortcutBar)
{
pLF=m_pShortcutBar->GetGroupTextFont(m_hGroup);
}
else
{
CFont* pFont=GetFont();
ASSERT(pFont!=NULL);
VERIFY(pFont->GetLogFont(&lf));
pLF=&lf;
}
ASSERT(pLF!=NULL);
VERIFY(font.CreateFontIndirect(pLF));
CRect rectMax;
CRect rectMin;
// fill SHBE_DISPLAY structure to fill editing properties depending on view
SHBE_DISPLAY shbed;
shbed.nMask=SHBEIF_TEXT|SHBEIF_RECTDISPLAY|SHBEIF_RECTMAX|SHBEIF_STYLE|
SHBEIF_ENLARGE|SHBEIF_SELRANGE|SHBEIF_FONT|SHBEIF_RECTMIN;
shbed.lpText=sText;
shbed.rectDisplay=rectText;
if(nView==SHB_LARGEICON)
{
shbed.dwStyle=WS_BORDER|ES_CENTER|ES_MULTILINE|ES_AUTOVSCROLL;
rectMax=rectClient;
rectMax.top=rectText.top;
rectMin=rectText;
if(rectText.Width()<ID_EDITMINWIDTH)
{
rectMin.left=(rectText.left+rectText.right-ID_EDITMINWIDTH)/2;
rectMin.right=rectMin.left+ID_EDITMINWIDTH;
if(rectMin.left<rectMax.left)
rectMax.left=rectMin.left;
if(rectMin.right>rectMax.right)
rectMax.right=rectMin.right;
}
}
else if(nView==SHB_SMALLICON)
{
shbed.dwStyle=WS_BORDER|ES_AUTOHSCROLL|ES_LEFT;
rectMax=rectText;
rectMax.right=rectClient.right;
rectMin=rectText;
if(rectText.Width()<ID_EDITMINWIDTH)
{
rectMin.right=rectMin.left+ID_EDITMINWIDTH;
if(rectMin.right>rectMax.right)
rectMax.right=rectMin.right;
}
}
else
{
ASSERT(FALSE);
}
shbed.rectMax=rectMax;
shbed.rectMin=rectMin;
shbed.bEnlargeAsTyping=TRUE;
shbed.ptSelRange=CPoint(0,-1);
shbed.pFont=&font;
if(!m_edit.StartEdit(&shbed))
{
TRACE(_T("COXSHBListCtrl::EditLabel: COXSHBEdit::StartEdit failed"));
return NULL;
}
m_nEditItem=nItem;
return &m_edit;
}
COLORREF COXSHBListCtrl::GetBkColor() const
{
if(m_pShortcutBar)
{
ASSERT(m_hGroup);
COLORREF clrBackground=m_pShortcutBar->GetGroupBkColor(m_hGroup);
ASSERT(clrBackground!=ID_COLOR_NONE);
return clrBackground;
}
else
{
return CListCtrl::GetBkColor();
}
}
BOOL COXSHBListCtrl::HasBkColor() const
{
if(m_pShortcutBar)
{
ASSERT(m_hGroup);
return m_pShortcutBar->HasGroupBkColor(m_hGroup);
}
else
return FALSE;
}
BOOL COXSHBListCtrl::SetBkColor(COLORREF clr)
{
if(m_pShortcutBar)
{
ASSERT(m_hGroup);
return m_pShortcutBar->SetGroupBkColor(m_hGroup,clr);
}
else
return CListCtrl::SetBkColor(clr);
}
COLORREF COXSHBListCtrl::GetTextColor() const
{
if(m_pShortcutBar)
{
ASSERT(m_hGroup);
COLORREF clrText=m_pShortcutBar->GetGroupTextColor(m_hGroup);
ASSERT(clrText!=ID_COLOR_NONE);
return clrText;
}
else
return CListCtrl::GetTextColor();
}
BOOL COXSHBListCtrl::SetTextColor(COLORREF clr)
{
if(m_pShortcutBar)
{
ASSERT(m_hGroup);
return m_pShortcutBar->SetGroupTextColor(m_hGroup,clr);
}
else
return CListCtrl::SetTextColor(clr);
}
COLORREF COXSHBListCtrl::GetTextBkColor() const
{
if(m_pShortcutBar)
return GetBkColor();
else
return CListCtrl::GetTextBkColor();
}
BOOL COXSHBListCtrl::SetTextBkColor(COLORREF clr)
{
if(m_pShortcutBar)
{
return FALSE;
}
else
return CListCtrl::SetTextBkColor(clr);
}
int COXSHBListCtrl::GetCountPerPage() const
{
// result of this function depends on current origin and may vary
//
CRect rectView;
GetViewRect(rectView,TRUE);
int nCount=0;
if(!rectView.IsRectEmpty())
{
CRect rect;
int nItem;
POSITION pos=m_mapItemToBoundRect.GetStartPosition();
while(pos!=NULL)
{
m_mapItemToBoundRect.GetNextAssoc(pos,nItem,rect);
ASSERT(nItem>=0 && nItem<GetItemCount());
if(rect.IntersectRect(rectView,rect))
nCount++;
}
}
return nCount;
}
CImageList* COXSHBListCtrl::CreateDragImage(int nItem, LPPOINT lpPoint /*= NULL*/)
{
UNREFERENCED_PARAMETER(lpPoint);
ASSERT(nItem>=0 && nItem<GetItemCount());
m_bCreatingDragImage=TRUE;
// IMPORTANT //
// it's caller responsibility to delete created image list
CImageList* m_pDragImageList=new CImageList;
CRect rectImageText;
VERIFY(GetItemRect(nItem,&rectImageText,LVIR_BOUNDS));
CRect rectItem;
VERIFY(GetItemRect(nItem,&rectItem,SHBLC_ENTIREITEM));
// create image list
m_pDragImageList->Create(rectImageText.Width(),rectImageText.Height(),TRUE,0,1);
CClientDC dcClient(this);
CDC dc;
VERIFY(dc.CreateCompatibleDC(&dcClient));
CDC dcCopy;
VERIFY(dcCopy.CreateCompatibleDC(&dcClient));
CBitmap bitmapItem;
VERIFY(bitmapItem.CreateCompatibleBitmap(&dcClient,rectItem.Width(),
rectItem.Height()));
CBitmap* pOldBitmap=dc.SelectObject(&bitmapItem);
rectImageText-=rectItem.TopLeft();
rectItem-=rectItem.TopLeft();
DRAWITEMSTRUCT dis;
dis.CtlType=ODT_LISTVIEW;
dis.CtlID=GetDlgCtrlID();
dis.itemID=nItem;
dis.itemAction=ODA_DRAWENTIRE;
dis.itemState=ODS_DEFAULT;
dis.hwndItem=GetSafeHwnd();
dis.hDC=dc;
dis.rcItem=rectItem;
dis.itemData=(DWORD)0;
// use DawItem to create drag image
DrawItem(&dis);
CBitmap bitmapImageText;
VERIFY(bitmapImageText.CreateCompatibleBitmap(&dcClient,rectImageText.Width(),
rectImageText.Height()));
CBitmap* pOldCopyBitmap=dcCopy.SelectObject(&bitmapImageText);
dcCopy.BitBlt(0,0,rectImageText.Width(),rectImageText.Height(),
&dc,rectImageText.left,rectImageText.top,SRCCOPY);
if(pOldCopyBitmap)
dcCopy.SelectObject(pOldCopyBitmap);
if(pOldBitmap)
dc.SelectObject(pOldBitmap);
VERIFY(m_pDragImageList->Add(&bitmapImageText,GetBkColor())!=-1);
m_bCreatingDragImage=FALSE;
return m_pDragImageList;
}
void COXSHBListCtrl::StartScrolling(BOOL bScrollingUp)
{
if(m_bScrollingUp || m_bScrollingDown)
StopScrolling();
ASSERT(m_nScrollTimerID==0);
ASSERT(!m_bScrollingUp && !m_bScrollingDown);
ASSERT(GetItemCount()>0);
int nOldSelectedItem=SelectItem(-1);
if(nOldSelectedItem!=-1)
Update(nOldSelectedItem);
CRect rectClient;
GetClientRect(rectClient);
// deflate the client rectangle to match the valid area where we can
// display items
rectClient.DeflateRect(ID_EDGELEFTMARGIN,ID_EDGETOPMARGIN,ID_EDGERIGHTMARGIN,
ID_EDGEBOTTOMMARGIN);
ASSERT(rectClient.top<rectClient.bottom);
// if any drag'n'drop operation is active and the control has launched it
// then we need to apply some additional effort to redraw things smoothly
if(m_bDragDropOperation && (GetBarStyle()&SHBS_DRAWITEMDRAGIMAGE)!=0 &&
m_bDragDropOwner)
{
ASSERT(m_pDragImage);
// unlock window updates
VERIFY(m_pDragImage->DragShowNolock(FALSE));
}
if(bScrollingUp)
{
m_bScrollingUp=TRUE;
RedrawWindow(m_rectTopScrollButton);
if(!m_bDragDropOperation)
{
// scroll up once
CRect rect;
int nItem=GetTopIndex();
if(nItem==-1)
nItem=GetItemCount()-1;
else
{
VERIFY(m_mapItemToBoundRect.Lookup(nItem,rect));
if(m_ptOrigin.y+rectClient.top<=rect.top)
{
ASSERT(nItem>0);
nItem--;
}
}
ASSERT((nItem>=0 && nItem<GetItemCount()));
VERIFY(m_mapItemToBoundRect.Lookup(nItem,rect));
int nOffset=rect.top-rectClient.top-m_ptOrigin.y;
Scroll(CSize(0,nOffset));
}
}
else
{
m_bScrollingDown=TRUE;
RedrawWindow(m_rectBottomScrollButton);
if(!m_bDragDropOperation)
{
// scroll down once
CRect rect;
int nItem=GetTopIndex();
ASSERT(nItem>=0 && nItem<GetItemCount());
if(nItem==GetItemCount()-1)
{
VERIFY(m_mapItemToBoundRect.Lookup(nItem,rect));
int nOffset=rect.bottom-rectClient.bottom-m_ptOrigin.y;
Scroll(CSize(0,nOffset));
}
else
{
nItem++;
VERIFY(m_mapItemToBoundRect.Lookup(nItem,rect));
int nOffset=rect.top-rectClient.top-m_ptOrigin.y;
Scroll(CSize(0,nOffset));
}
}
}
if(m_bDragDropOperation && (GetBarStyle()&SHBS_DRAWITEMDRAGIMAGE)!=0 &&
m_bDragDropOwner)
{
ASSERT(m_pDragImage);
// lock window updates
VERIFY(m_pDragImage->DragShowNolock(TRUE));
}
// set timer
//
UINT nDelay=(m_bAutoScrolling ? GetAutoScrollingDelay() : GetScrollingDelay());
// adjustment for drag'n'drop operation
if(m_bDragDropOperation)
nDelay/=4;
m_nScrollTimerID=SetTimer(ID_SCROLLINGTIMER,nDelay,NULL);
ASSERT(m_nScrollTimerID!=0);
}
void COXSHBListCtrl::StopScrolling()
{
ASSERT(m_nScrollTimerID!=0);
// kill timer
KillTimer(m_nScrollTimerID);
m_nScrollTimerID=0;
// redraw scroll buttons
//
if(!m_rectTopScrollButton.IsRectEmpty())
RedrawWindow(m_rectTopScrollButton);
if(!m_rectBottomScrollButton.IsRectEmpty())
RedrawWindow(m_rectBottomScrollButton);
m_bScrollingUp=FALSE;
m_bScrollingDown=FALSE;
m_bAutoScrolling=FALSE;
}
BOOL COXSHBListCtrl::InitListControl()
{
ASSERT(::IsWindow(GetSafeHwnd()));
// create edit control used to edit header text
if(!CreateEditControl())
{
TRACE(_T("COXSHBListCtrl::InitListControl: failed to create edit control"));
return FALSE;
}
// tooltips is always enabled
EnableToolTips(TRUE);
// register OLE Drag'n'Drop
COleDropTarget* pOleDropTarget=GetDropTarget();
ASSERT(pOleDropTarget!=NULL);
if(!pOleDropTarget->Register(this))
{
TRACE(_T("COXSHBListCtrl::InitListControl: failed to register the control with COleDropTarget\n"));
TRACE(_T("COXSHBListCtrl: you've probably forgot to initialize OLE libraries using AfxOleInit function"));
}
return TRUE;
}
COleDropSource* COXSHBListCtrl::GetDropSource()
{
// In the case this control was created as a child window
// of shortcut bar group fill structure for notification of
// parent window of this shortcut bar
NMSHORTCUTBAR nmshb;
nmshb.hdr.code=SHBN_GETDROPSOURCE;
nmshb.hGroup=m_hGroup;
nmshb.lParam=(LPARAM)NULL;
COleDropSource* pOleDropSource=NULL;
// check if parent want to set its own drop target object
if(SendSHBNotification(&nmshb) && nmshb.lParam!=NULL)
pOleDropSource=(COleDropSource*)nmshb.lParam;
else
{
// we have to call this function, otherwise assertions will happen all
// the time COXSHBDropSource specific message will be fired
m_oleDropSource.SetOwner(this);
pOleDropSource=&m_oleDropSource;
}
ASSERT(pOleDropSource!=NULL);
return pOleDropSource;
}
COleDropTarget* COXSHBListCtrl::GetDropTarget()
{
// In the case this control was created as a child window
// of shortcut bar group fill structure for notification of
// parent window of this shortcut bar
NMSHORTCUTBAR nmshb;
nmshb.hdr.code=SHBN_GETDROPTARGET;
nmshb.hGroup=m_hGroup;
nmshb.lParam=(LPARAM)NULL;
COleDropTarget* pOleDropTarget=NULL;
// check if parent want to set its own drop target object
if(SendSHBNotification(&nmshb) && nmshb.lParam!=NULL)
pOleDropTarget=(COleDropTarget*)nmshb.lParam;
else
pOleDropTarget=(COleDropTarget*)&m_oleDropTarget;
ASSERT(pOleDropTarget!=NULL);
return pOleDropTarget;
}
CRect COXSHBListCtrl::GetGapBetweenItems(int nItemBefore)
{
ASSERT(nItemBefore>=0 && nItemBefore<=GetItemCount());
// returns the gap before nItemBefore item
//
CRect rect;
GetClientRect(rect);
// if the requested gap is between top border and top item or
// bottom border and bottom item
if(nItemBefore==GetItemCount() || nItemBefore==0)
{
if(GetItemCount()==0 || nItemBefore==0)
rect.bottom=rect.top+ID_EDGETOPMARGIN;
else
{
CRect rectItem;
VERIFY(GetItemRect(nItemBefore-1,rectItem,SHBLC_ENTIREITEM));
rect.top=rectItem.bottom+m_ptOrigin.y;
rect.bottom=rect.top+ID_EDGEBOTTOMMARGIN;
}
}
// if the requested gap is between items
else
{
CRect rectItem;
VERIFY(GetItemRect(nItemBefore,rectItem,SHBLC_ENTIREITEM));
rect.bottom=rectItem.top+m_ptOrigin.y;
rect.top=rect.bottom-ID_ITEMMARGIN;
}
return rect;
}
CRect COXSHBListCtrl::GetPlaceHolderRect(int nItemBefore)
{
ASSERT(nItemBefore>=0 && nItemBefore<=GetItemCount());
// place holder rectangle must lay around or within the gap between items
CRect rect=GetGapBetweenItems(nItemBefore);
rect-=m_ptOrigin;
// if the requested place holder is between top border and top item or
// bottom border and bottom item
if(nItemBefore==GetItemCount() || nItemBefore==0)
{
if(GetItemCount()==0 || nItemBefore==0)
rect.bottom=rect.top+2*ID_HEIGHTPLACEHOLDER/3;
else
rect.top=rect.bottom-2*ID_HEIGHTPLACEHOLDER/3;
}
// if the requested place holder is between items
else
{
int nGapHeight=rect.Height();
rect.bottom+=ID_HEIGHTPLACEHOLDER/2+ID_HEIGHTPLACEHOLDER%2-nGapHeight/2;
rect.top-=ID_HEIGHTPLACEHOLDER/2-nGapHeight/2;
}
return rect;
}
int COXSHBListCtrl::GetItemImage(const int nItem)
{
ASSERT(nItem>=0 && nItem<GetItemCount());
// image list used to display item depends on type of view
int nView=GetView();
// get corresponding item list to show items
CImageList* pil=NULL;
if(nView==SHB_LARGEICON)
pil=GetImageList(LVSIL_NORMAL);
else if(nView==SHB_SMALLICON)
pil=GetImageList(LVSIL_SMALL);
// if there is no any image list associated with the control then always return -1
if(pil==NULL)
return -1;
LV_ITEM lvi;
lvi.mask=LVIF_IMAGE;
lvi.iItem=nItem;
lvi.iSubItem=0;
VERIFY(GetItem(&lvi));
if(lvi.iImage<pil->GetImageCount())
return lvi.iImage;
else
return -1;
}
HRESULT COXSHBListCtrl::GetComCtlVersion(LPDWORD pdwMajor, LPDWORD pdwMinor)
{
if(IsBadWritePtr(pdwMajor, sizeof(DWORD)) ||
IsBadWritePtr(pdwMinor, sizeof(DWORD)))
{
return E_INVALIDARG;
}
// get handle of the common control DLL
BOOL bAlreadyLoaded=TRUE;
HINSTANCE hComCtl=::GetModuleHandle(_T("comctl32.dll"));
if(hComCtl==NULL)
{
// load the DLL
hComCtl=::LoadLibrary(_T("comctl32.dll"));
bAlreadyLoaded=FALSE;
}
if(hComCtl)
{
HRESULT hr=S_OK;
DLLGETVERSIONPROC pDllGetVersion;
/*
You must get this function explicitly because earlier versions of the DLL
don't implement this function. That makes the lack of implementation of the
function a version marker in itself.
*/
pDllGetVersion=(DLLGETVERSIONPROC)GetProcAddress(hComCtl, "DllGetVersion");
if(pDllGetVersion)
{
DLLVERSIONINFO dvi;
ZeroMemory(&dvi, sizeof(dvi));
dvi.cbSize=sizeof(dvi);
hr = (*pDllGetVersion)(&dvi);
if(SUCCEEDED(hr))
{
*pdwMajor = dvi.dwMajorVersion;
*pdwMinor = dvi.dwMinorVersion;
}
else
{
hr = E_FAIL;
}
}
else
{
// If GetProcAddress failed, then the DLL is a version previous
// to the one shipped with IE 3.x.
*pdwMajor = 4;
*pdwMinor = 0;
}
if(!bAlreadyLoaded)
::FreeLibrary(hComCtl);
return hr;
}
return E_FAIL;
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// COXShortcutBar
IMPLEMENT_DYNCREATE(COXShortcutBar, CWnd)
COXShortcutBar::COXShortcutBar()
{
m_pImageList=NULL;
m_hDropHilightGroup=NULL;
m_hExpandedGroup=NULL;
m_hEditGroup=NULL;
m_hHotGroup=NULL;
m_hPressedGroup=NULL;
m_hDragGroup=NULL;
m_pShortcutBarSkin = NULL;
m_nSortOrder=0;
m_dwBarStyle=0;
m_rectChildWnd.SetRectEmpty();
m_nLastHandle=0;
// create default font used to draw text in headers
if((HFONT)m_fontDefault==NULL)
{
if(!m_fontDefault.CreateFont(
-11,0,0,0,FW_REGULAR,0,0,0,0,0,0,0,0,_T("MS Sans Serif")))
{
TRACE(_T("COXShortcutBar::COXShortcutBar: failed to create default font"));
ASSERT(FALSE);
AfxThrowMemoryException();
}
}
m_bCreatingDragImage=FALSE;
m_pDragImage=NULL;
m_nGroupMargin=DFLT_GROUPMARGIN;
m_rectChildWndMargins=CRect(DFLT_LEFTMARGIN,DFLT_TOPMARGIN,
DFLT_RIGHTMARGIN,DFLT_BOTTOMMARGIN);
m_sizeMin=CSize(DFLT_MINWIDTH,DFLT_MINHEIGHT);
m_sizeScrollButton=CSize(DFLT_SCROLLBUTTONWIDTH,DFLT_SCROLLBUTTONHEIGHT);
m_nScrollingDelay=DFLT_SCROLLINGDELAY;
m_nAutoScrollingDelay=DFLT_AUTOSCROLLINGDELAY;
m_nAutoExpandDelay=DFLT_WAITFORAUTOEXPAND;
m_nWaitForDragHeaderID=0;
m_hSuspectDragGroup=NULL;
m_nWaitForAutoExpandID=0;
m_hSuspectAutoExpandGroup=NULL;
m_nCheckMouseIsOverGroupID=0;
m_hLastMouseIsOverGroup=NULL;
}
COXShortcutBar::~COXShortcutBar()
{
HSHBGROUP hGroup;
// if window is not destroyed yet then dlete all groups in the way the parent
// window will get notification
if(::IsWindow(GetSafeHwnd()))
VERIFY(DeleteAllGroups());
// delete all SHB_DESCRIPTOR structures we allocated
LPSHB_DESCRIPTOR pDescriptor;
// v9.3 - update 03 - 64-bit - switch param type - TD
#ifdef _WIN64
INT_PTR nOrder;
#else
int nOrder;
#endif
POSITION pos=m_mapDescriptors.GetStartPosition();
while(pos!=NULL)
{
m_mapDescriptors.GetNextAssoc(pos,pDescriptor,nOrder);
ASSERT(pDescriptor!=NULL);
delete pDescriptor;
}
m_mapDescriptors.RemoveAll();
// delete all SHB_GROUPINFO structures we allocated
LPSHB_GROUPINFO pGroupInfo;
pos=m_mapHandleToInfo.GetStartPosition();
while(pos!=NULL)
{
m_mapHandleToInfo.GetNextAssoc(pos,hGroup,pGroupInfo);
ASSERT(hGroup!=NULL);
ASSERT(pGroupInfo!=NULL);
delete pGroupInfo;
}
m_mapHandleToInfo.RemoveAll();
// delete all COXSHBListCtrl controls we created
COXSHBListCtrl* pListCtrl;
pos=m_mapHandleToListCtrl.GetStartPosition();
while(pos!=NULL)
{
m_mapHandleToListCtrl.GetNextAssoc(pos,hGroup,pListCtrl);
ASSERT(hGroup!=NULL);
ASSERT(pListCtrl!=NULL);
ASSERT_VALID(pListCtrl);
delete pListCtrl;
}
m_mapHandleToListCtrl.RemoveAll();
// clean up map of all bound rectangles
m_mapHandleToBoundRect.RemoveAll();
// dlete drag image if any was created
delete m_pDragImage;
// destroy window if it hasn't been destroyed yet
if(::IsWindow(GetSafeHwnd()))
{
DestroyWindow();
}
if (m_pShortcutBarSkin != NULL)
delete m_pShortcutBarSkin;
}
BEGIN_MESSAGE_MAP(COXShortcutBar, CWnd)
//{{AFX_MSG_MAP(COXShortcutBar)
ON_WM_MOUSEMOVE()
ON_WM_LBUTTONDOWN()
ON_WM_CONTEXTMENU()
ON_WM_PAINT()
ON_WM_SIZE()
ON_WM_ERASEBKGND()
ON_WM_NCHITTEST()
ON_WM_LBUTTONUP()
ON_WM_DESTROY()
ON_WM_TIMER()
//}}AFX_MSG_MAP
ON_NOTIFY_EX(TTN_NEEDTEXTA, 0, OnHeaderToolTip)
ON_NOTIFY_EX(TTN_NEEDTEXTW, 0, OnHeaderToolTip)
ON_NOTIFY(SHBEN_FINISHEDIT, SHB_IDCEDIT, OnChangeGroupHeaderText)
ON_MESSAGE(SHBDTM_DRAGOVER, OnDragOver)
ON_MESSAGE(SHBDTM_DRAGLEAVE, OnDragLeave)
ON_COMMAND(SHB_IDMLARGEICON, OnLargeIcon)
ON_COMMAND(SHB_IDMSMALLICON, OnSmallIcon)
ON_COMMAND(SHB_IDMADDNEWGROUP, OnAddNewGroup)
ON_COMMAND(SHB_IDMREMOVEGROUP, OnRemoveGroup)
ON_COMMAND(SHB_IDMRENAMEGROUP, OnRenameGroup)
ON_COMMAND(SHBLC_IDMREMOVEITEM, OnRemoveLCItem)
ON_COMMAND(SHBLC_IDMRENAMEITEM, OnRenameLCItem)
ON_UPDATE_COMMAND_UI(SHB_IDMLARGEICON, OnUpdateLargeIcon)
ON_UPDATE_COMMAND_UI(SHB_IDMSMALLICON, OnUpdateSmallIcon)
END_MESSAGE_MAP()
BOOL COXShortcutBar::Create(CWnd* pParentWnd, const RECT& rect,
DWORD dwBarStyle/* = SHBS_EDITHEADERS|SHBS_EDITITEMS|
SHBS_DISABLEDRAGDROPITEM|SHBS_DISABLEDRAGDROPHEADER*/,
UINT nID/* = 0xffff*/,
DWORD dwStyle/* = WS_CHILD|WS_VISIBLE*/,
DWORD dwExStyle/* = 0*/)
{
// create our own window class
WNDCLASS wndClass;
wndClass.style=CS_DBLCLKS;
wndClass.lpfnWndProc=AfxWndProc;
wndClass.cbClsExtra=0;
wndClass.cbWndExtra=0;
wndClass.hInstance=AfxGetInstanceHandle();
wndClass.hIcon=NULL;
wndClass.hCursor=::LoadCursor(NULL,IDC_ARROW);
wndClass.hbrBackground=(HBRUSH)(COLOR_WINDOW+1);
wndClass.lpszMenuName=NULL;
wndClass.lpszClassName=_T("OXShortcutBar");
if(!AfxRegisterClass(&wndClass))
{
TRACE(_T("COXShortcutBar::Create: failed to register shortcut bar window class"));
return FALSE;
}
// force WS_CHILD style
dwStyle|=WS_CHILD;
// create window
if(!CWnd::CreateEx(
dwExStyle,wndClass.lpszClassName,_T(""),dwStyle,rect,pParentWnd,nID,NULL))
{
TRACE(_T("COXShortcutBar::Create: failed to create the shortcut bar control\n"));
return FALSE;
}
// set Shortcut bar specific style
if(!SetBarStyle(dwBarStyle))
{
return FALSE;
}
// initialize shortcut bar
if(!InitShortcutBar())
{
TRACE(_T("COXShortcutBar::Create: failed to initialize the shortcut bar"));
return FALSE;
}
return TRUE;
}
// v9.3 - update 03 - 64-bit - using OXINTRET here
OXINTRET COXShortcutBar::OnToolHitTest(CPoint point, TOOLINFO* pTI) const
{
ASSERT_VALID(this);
ASSERT(::IsWindow(GetSafeHwnd()));
// the control should be designated to provide tooltips
if((GetBarStyle()&SHBS_INFOTIP)==0)
return -1;
int nHit=-1;
// now hit test against COXSHBListCtrl items
UINT nHitFlags;
HSHBGROUP hGroup=HitTest(point,&nHitFlags);
if(nHitFlags!=SHB_ONHEADER)
hGroup=NULL;
if(hGroup!=NULL && pTI!= NULL)
{
#if _MFC_VER<=0x0421
if(pTI->cbSize>=sizeof(TOOLINFO))
return nHit;
#endif
pTI->hwnd=GetSafeHwnd();
CRect rect;
VERIFY(GetGroupHeaderRect(hGroup,rect));
pTI->rect=rect;
//
pTI->uId=(UINT)PtrToUint(hGroup);
// set text to LPSTR_TEXTCALLBACK in order to get TTN_NEEDTEXT message when
// it's time to display tool tip
pTI->lpszText=LPSTR_TEXTCALLBACK;
// return found group handle
nHit=(int)PtrToInt(hGroup);
}
return nHit;
}
BOOL COXShortcutBar::OnCmdMsg(UINT nID, int nCode, void* pExtra,
AFX_CMDHANDLERINFO* pHandlerInfo)
{
// TODO: Add your specialized code here and/or call the base class
ASSERT(::IsWindow(GetSafeHwnd()));
// pump through parent
CWnd* pParentWnd=GetParent();
if(pParentWnd!=NULL && !pParentWnd->IsKindOf(RUNTIME_CLASS(COXShortcutBar)) &&
pParentWnd->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
{
return TRUE;
}
// then pump through itself
if (CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
{
return TRUE;
}
// pump through child window of expanded group
HSHBGROUP hGroup=GetExpandedGroup();
if(hGroup!=NULL)
{
HWND hwndChild=GetGroupChildWndHandle(hGroup);
ASSERT(hwndChild);
CWnd* pChildWnd=CWnd::FromHandle(hwndChild);
if(pChildWnd!=NULL && !pChildWnd->IsKindOf(RUNTIME_CLASS(COXShortcutBar)) &&
pChildWnd->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
{
return TRUE;
}
}
// last but not least, pump through app
CWinApp* pApp=AfxGetApp();
if(pApp!=NULL && pApp->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
{
return TRUE;
}
return FALSE;
}
void COXShortcutBar::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
UINT nHitFlags;
HSHBGROUP hGroup=HitTest(point,&nHitFlags);
// reset handle to the group that is positioned under mouse cursor
SetMouseIsOverGroup((nHitFlags==SHB_ONHEADER ? hGroup : NULL));
// reset pressed group handle
if(m_hPressedGroup!=NULL && !IsLButtonDown())
{
BOOL bFlat=((GetBarStyle() & SHBS_FLATGROUPBUTTON)==SHBS_FLATGROUPBUTTON);
// reset pressed group
HSHBGROUP hOldPressedGroup=m_hPressedGroup;
m_hPressedGroup=NULL;
if(bFlat)
{
// redraw header
UpdateHeader(hOldPressedGroup);
}
}
// if dragging any group
if(GetDragGroup()!=NULL)
{
if(nHitFlags==SHB_ONCHILD)
{
hGroup=NULL;
}
BOOL bDrawDragImage=FALSE;
ASSERT((GetBarStyle()&SHBS_DISABLEDRAGDROPHEADER)==0);
ASSERT((nFlags&MK_LBUTTON)!=0);
// move drag image if needed
if((GetBarStyle()&SHBS_DRAWHEADERDRAGIMAGE)!=0)
{
ASSERT(m_pDragImage);
CPoint ptScreen=point;
ClientToScreen(&ptScreen);
// move the drag image
VERIFY(m_pDragImage->DragMove(ptScreen));
bDrawDragImage=TRUE;
}
// Drag image could be over of one of headers while cursor not. Assuming
// that our drag image width is less or equals to the width of the
// client window of the shortcut bar control
if(hGroup==NULL)
{
CRect rectDragImage;
if((GetBarStyle()&SHBS_DRAWHEADERDRAGIMAGE)!=0)
{
// get drag image bounding rectangle
IMAGEINFO imageInfo;
m_pDragImage->GetImageInfo(0,&imageInfo);
rectDragImage=imageInfo.rcImage;
rectDragImage-=rectDragImage.TopLeft();
// get client coordinate of drag image bounding rectangle
CPoint ptTopLeft=point;
ptTopLeft-=m_ptDragImage;
rectDragImage+=ptTopLeft;
}
else
{
VERIFY(GetGroupHeaderRect(GetDragGroup(),rectDragImage));
}
CRect rect;
GetClientRect(rect);
CRect rectIntersect;
// check if make sense to search for intersection
if(rectIntersect.IntersectRect(rect,rectDragImage))
{
rectIntersect.IntersectRect(m_rectChildWnd,rectDragImage);
if(!(rectIntersect==rectDragImage))
{
int nMaxSpace=0;
HSHBGROUP hSuspectGroup;
POSITION pos=m_mapHandleToBoundRect.GetStartPosition();
while(pos!=NULL)
{
m_mapHandleToBoundRect.
GetNextAssoc(pos,hSuspectGroup,rect);
if(rectIntersect.IntersectRect(rect,rectDragImage) &&
rectIntersect.Width()*rectIntersect.Height()>nMaxSpace)
{
hGroup=hSuspectGroup;
nMaxSpace=rectIntersect.Width()*rectIntersect.Height();
}
}
}
}
}
// set drop target group
HSHBGROUP hOldDropHilightGroup=SetDropTargetGroup(hGroup);
BOOL bRedrawOldDropTarget=((hGroup!=hOldDropHilightGroup) &
(GetDragGroup()!=hOldDropHilightGroup) & (hOldDropHilightGroup!=NULL));
BOOL bRedrawNewDropTarget=((hGroup!=hOldDropHilightGroup) &
(GetDragGroup()!=hGroup) & (hGroup!=NULL));
BOOL bUnlock=bDrawDragImage && (bRedrawOldDropTarget || bRedrawNewDropTarget);
if(bUnlock)
{
// unlock window updates
VERIFY(m_pDragImage->DragShowNolock(FALSE));
}
// redraw affected headers
if(bRedrawOldDropTarget)
{
UpdateHeader(hOldDropHilightGroup);
}
if(bRedrawNewDropTarget)
{
UpdateHeader(hGroup);
}
// set corresponding cursor
if(hGroup==NULL || hGroup==GetDragGroup())
{
SetCursor(LoadCursor(NULL,IDC_NO));
}
else
{
SetCursor(LoadCursor(NULL,IDC_ARROW));
}
if(bUnlock)
{
// lock window updates
VERIFY(m_pDragImage->DragShowNolock(TRUE));
}
}
// about to drag the group
else if(nHitFlags==SHB_ONHEADER && m_nWaitForDragHeaderID==0 &&
IsLButtonDown() && hGroup==m_hSuspectDragGroup && hGroup!=NULL)
{
if((GetBarStyle()&SHBS_DISABLEDRAGDROPHEADER)==0)
{
// there shouldn't be any group currently being dragged
ASSERT(GetDragGroup()==NULL);
// create drag image and start dragging
if((GetBarStyle() & SHBS_DRAWHEADERDRAGIMAGE)!=0)
{
// delete previously created drag image
if(m_pDragImage)
{
delete m_pDragImage;
m_pDragImage=NULL;
}
m_pDragImage=CreateGroupDragImage(hGroup);
ASSERT(m_pDragImage);
// changes the cursor to the drag image
VERIFY(m_pDragImage->BeginDrag(0,m_ptDragImage));
CPoint ptScreen=point;
ClientToScreen(&ptScreen);
VERIFY(m_pDragImage->DragEnter(GetDesktopWindow(),ptScreen));
// unlock window updates
VERIFY(m_pDragImage->DragShowNolock(FALSE));
}
// capture all mouse messages
SetCapture();
// assign group being dragged
SetDragGroup(hGroup);
// redraw dragged group header
UpdateHeader(hGroup);
if((GetBarStyle()&SHBS_DRAWHEADERDRAGIMAGE)!=0)
{
// lock window updates
VERIFY(m_pDragImage->DragShowNolock(TRUE));
}
}
}
// about to auto expand the collapsed group
else if(m_nWaitForDragHeaderID==0 && !IsLButtonDown() && hGroup!=NULL &&
hGroup!=GetExpandedGroup() && (GetBarStyle()&SHBS_AUTOEXPAND)!=0 &&
GetEditGroup()==NULL)
{
if(m_hSuspectAutoExpandGroup!=hGroup || nHitFlags!=SHB_ONHEADER)
{
// kill timer
if(m_nWaitForAutoExpandID!=0)
{
ASSERT(m_hSuspectAutoExpandGroup!=NULL);
KillTimer(m_nWaitForAutoExpandID);
m_nWaitForAutoExpandID=0;
m_hSuspectAutoExpandGroup=NULL;
}
else
{
ASSERT(m_hSuspectAutoExpandGroup==NULL);
}
}
if(m_hSuspectAutoExpandGroup!=hGroup && nHitFlags==SHB_ONHEADER)
{
ASSERT(m_hSuspectAutoExpandGroup==NULL);
m_hSuspectAutoExpandGroup=hGroup;
// set timer
m_nWaitForAutoExpandID=SetTimer(
ID_WAITFORAUTOEXPANDTIMER,GetAutoExpandDelay(),NULL);
ASSERT(m_nWaitForAutoExpandID!=0);
}
}
CWnd::OnMouseMove(nFlags, point);
}
void COXShortcutBar::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
VERIFY(m_mapHandleToBoundRect.GetCount()==m_mapHandleToInfo.GetCount());
UINT nHitFlags;
HSHBGROUP hGroup=HitTest(point,&nHitFlags);
if(nHitFlags==SHB_ONHEADER)
{
BOOL bFlat=((GetBarStyle() & SHBS_FLATGROUPBUTTON)==SHBS_FLATGROUPBUTTON);
// user clicked on header
//
ASSERT(hGroup!=NULL);
ASSERT(m_hPressedGroup==NULL);
m_hPressedGroup=hGroup;
if(bFlat)
{
UpdateHeader(m_hPressedGroup);
}
else
{
ExpandGroup(hGroup);
}
// group drag'n'drop
if((GetBarStyle() & SHBS_DISABLEDRAGDROPHEADER)==0)
{
// in result of group expanding the header cordinates could have changed
HSHBGROUP hSuspectGroup=HitTest(point,&nHitFlags);
if(nHitFlags==SHB_ONHEADER && hSuspectGroup==hGroup)
{
// kill timer
if(m_nWaitForDragHeaderID!=0)
{
KillTimer(m_nWaitForDragHeaderID);
m_nWaitForDragHeaderID=0;
}
// set timer
m_nWaitForDragHeaderID=SetTimer(
ID_WAITFORDRAGHEADERTIMER,DFLT_WAITFORDRAGHEADER,NULL);
ASSERT(m_nWaitForDragHeaderID!=0);
// set the group as a suspect for drag'n'drop operation
m_hSuspectDragGroup=hGroup;
// define the point of hot spot on future drag image
if((GetBarStyle()&SHBS_DRAWHEADERDRAGIMAGE)!=0)
{
CRect rectHeader;
VERIFY(GetGroupHeaderRect(hGroup,&rectHeader));
m_ptDragImage=point;
ASSERT(rectHeader.PtInRect(m_ptDragImage));
m_ptDragImage-=rectHeader.TopLeft();
}
}
}
}
CWnd::OnLButtonDown(nFlags, point);
}
void COXShortcutBar::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
// if dragging any group
if(GetDragGroup()!=NULL)
{
if((GetBarStyle()&SHBS_DRAWHEADERDRAGIMAGE)!=0)
{
ASSERT(m_pDragImage!=NULL);
// end dragging
VERIFY(m_pDragImage->DragLeave(GetDesktopWindow()));
m_pDragImage->EndDrag();
}
// reset drag group
HSHBGROUP hOldDragGroup=SetDragGroup(NULL);
ASSERT(hOldDragGroup!=NULL);
HSHBGROUP hOldDropTargetGroup=SetDropTargetGroup(NULL);
if(hOldDropTargetGroup!=NULL)
{
// notify parent
// fill structure for notification
NMSHORTCUTBAR nmshb;
nmshb.hdr.code=SHBN_DROPGROUP;
nmshb.hGroup=hOldDragGroup;
nmshb.lParam=(LPARAM)hOldDropTargetGroup;
if(!SendSHBNotification(&nmshb))
{
// all the corresponding actions
//
// reodering of drag and drop target items
int nOrder=GetGroupOrder(hOldDropTargetGroup);
ASSERT(nOrder>=0 && nOrder<GetGroupCount());
VERIFY(SetGroupOrder(hOldDragGroup,nOrder));
RedrawBar();
}
else
{
UpdateHeader(hOldDropTargetGroup);
UpdateHeader(hOldDragGroup);
}
}
else
{
UpdateHeader(hOldDragGroup);
}
if(::GetCapture()==GetSafeHwnd())
{
// stop intercepting all mouse messages
::ReleaseCapture();
}
}
else if(m_hPressedGroup!=NULL)
{
BOOL bFlat=((GetBarStyle() & SHBS_FLATGROUPBUTTON)==SHBS_FLATGROUPBUTTON);
// reset pressed group
HSHBGROUP hOldPressedGroup=m_hPressedGroup;
m_hPressedGroup=NULL;
if(bFlat)
{
UINT nHitFlags;
HSHBGROUP hGroup=HitTest(point,&nHitFlags);
if(nHitFlags==SHB_ONHEADER && hGroup==hOldPressedGroup)
{
if(GetExpandedGroup()!=hGroup)
{
// expand
ExpandGroup(hGroup);
}
else
{
// redraw header
UpdateHeader(hGroup);
}
}
}
}
CWnd::OnLButtonUp(nFlags, point);
}
void COXShortcutBar::OnContextMenu(CWnd* pWnd, CPoint point)
{
// TODO: Add your message handler code here
UNREFERENCED_PARAMETER(pWnd);
// kill timer for group auto expanding
if(m_nWaitForAutoExpandID!=0)
{
KillTimer(m_nWaitForAutoExpandID);
m_nWaitForAutoExpandID=0;
m_hSuspectAutoExpandGroup=NULL;
}
// create popup menu
//
CMenu menu;
if(menu.CreatePopupMenu())
{
// we populate context menu depending on the place where right click happened
ScreenToClient(&point);
UINT nFlags;
HSHBGROUP hGroup=HitTest(point,&nFlags);
if(nFlags==SHB_ONHEADER)
{
ASSERT(hGroup);
}
else if(nFlags==SHB_ONCHILD)
{
hGroup=GetExpandedGroup();
ASSERT(hGroup);
}
else if(nFlags==SHB_NOWHERE)
{
hGroup=GetExpandedGroup();
}
else
{
ASSERT(FALSE);
}
// assign it as current hot group
m_hHotGroup=hGroup;
ClientToScreen(&point);
// fill in SHBCONTEXTMENU structure to use it in message and notification
SHBCONTEXTMENU shbcm;
shbcm.pMenu=&menu;
shbcm.pShortcutBar=this;
shbcm.hGroup=hGroup;
shbcm.point=point;
// Add New Group item
CString sItem;
VERIFY(sItem.LoadString(IDS_OX_SHB_IDMADDNEWGROUP_TEXT));
if((GetBarStyle()&SHBS_EDITHEADERS)!=0)
{
menu.AppendMenu(MF_STRING,SHB_IDMADDNEWGROUP,sItem);
}
// if any group exist
if(hGroup)
{
VERIFY(sItem.LoadString(IDS_OX_SHB_IDMLARGEICON_TEXT));
// view type items
if((GetBarStyle()&SHBS_EDITHEADERS)!=0)
{
menu.AppendMenu(MF_SEPARATOR);
}
menu.AppendMenu(MF_STRING|MF_CHECKED,SHB_IDMLARGEICON,sItem);
VERIFY(sItem.LoadString(IDS_OX_SHB_IDMSMALLICON_TEXT));
menu.AppendMenu(MF_STRING|MF_CHECKED,SHB_IDMSMALLICON,sItem);
if((GetBarStyle()&SHBS_EDITHEADERS)!=0)
{
menu.AppendMenu(MF_SEPARATOR);
// remove/rename group
VERIFY(sItem.LoadString(IDS_OX_SHB_IDMREMOVEGROUP_TEXT));
menu.AppendMenu(MF_STRING,SHB_IDMREMOVEGROUP,sItem);
VERIFY(sItem.LoadString(IDS_OX_SHB_IDMRENAMEGROUP_TEXT));
menu.AppendMenu(MF_STRING,SHB_IDMRENAMEGROUP,sItem);
}
// check/uncheck view type menu items
HSHBGROUP hExpandedGroup=GetExpandedGroup();
if(hExpandedGroup)
{
int nView=GetGroupView(hExpandedGroup);
menu.CheckMenuItem(SHB_IDMLARGEICON,MF_BYCOMMAND|
((nView==SHB_LARGEICON) ? MF_CHECKED : MF_UNCHECKED));
menu.CheckMenuItem(SHB_IDMSMALLICON,MF_BYCOMMAND|
((nView==SHB_LARGEICON) ? MF_UNCHECKED : MF_CHECKED));
}
// if right click was over child window the we have to give it a chance to
// populate context menu with its items
if(nFlags==SHB_ONCHILD)
{
ASSERT(hGroup==hExpandedGroup);
HWND hWnd=GetGroupChildWndHandle(hGroup);
ASSERT(hWnd);
::SendMessage(hWnd,SHBM_POPULATECONTEXTMENU,
(WPARAM)0,(LPARAM)&shbcm);
}
}
// fill structure for notification
NMSHORTCUTBAR nmshb;
nmshb.hdr.code=SHBN_CONTEXTMENU;
nmshb.hGroup=hGroup;
nmshb.lParam=(LPARAM)(&shbcm);
// if parent didn't reject to display menu then do that
if(!SendSHBNotification(&nmshb) && menu.GetMenuItemCount()>0)
{
menu.TrackPopupMenu(
TPM_LEFTALIGN|TPM_LEFTBUTTON,point.x,point.y, this);
}
}
menu.DestroyMenu();
}
void COXShortcutBar::PreSubclassWindow()
{
// TODO: Add your specialized code here and/or call the base class
_AFX_THREAD_STATE* pThreadState=AfxGetThreadState();
// hook not already in progress
if(pThreadState->m_pWndInit==NULL)
{
// set default shortcut bar style
m_dwBarStyle=SHBS_EDITHEADERS|SHBS_EDITITEMS|
SHBS_DISABLEDRAGDROPITEM|SHBS_DISABLEDRAGDROPHEADER;
DWORD dwStyle=GetStyle();
// check if subclassed window has WS_CHILD style
VERIFY((dwStyle & WS_CHILD)==WS_CHILD);
if(!InitShortcutBar())
{
TRACE(_T("COXShortcutBar::PreSubclassWindow: failed to initialize shortcut bar\n"));
}
}
CWnd::PreSubclassWindow();
}
void COXShortcutBar::OnPaint()
{
CPaintDC dc(this); // device context for painting
// TODO: Add your message handler code here
CRect rect;
GetClientRect(rect);
// Save DC
int nSavedDC=dc.SaveDC();
ASSERT(nSavedDC);
// Set clipping region
dc.IntersectClipRect(rect);
if(!m_rectChildWnd.IsRectEmpty())
{
dc.ExcludeClipRect(m_rectChildWnd);
}
// fill background with expanded group color
FillBackground(&dc);
int nCount= PtrToInt(GetGroupCount());
VERIFY(m_mapHandleToBoundRect.GetCount()==nCount);
HSHBGROUP hGroup;
// draw group headers one after another
for(int nIndex=0; nIndex<nCount; nIndex++)
{
hGroup=FindGroupByOrder(nIndex);
VERIFY(hGroup!=NULL);
VERIFY(m_mapHandleToBoundRect.Lookup(hGroup,rect));
// draw header only if it's visible
if(dc.RectVisible(&rect))
{
CRect rectHeader=rect;
rectHeader-=rect.TopLeft();
CDC dcCompatible;
if(!dcCompatible.CreateCompatibleDC(&dc))
{
TRACE(_T("COXShortcutBar::OnPaint:Failed to create compatible DC\n"));
return;
}
CBitmap bitmap;
if(!bitmap.CreateCompatibleBitmap(&dc, rectHeader.Width(),
rectHeader.Height()))
{
TRACE(_T("COXShortcutBar::OnPaint:Failed to create compatible bitmap\n"));
return;
}
CBitmap* pOldBitmap=dcCompatible.SelectObject(&bitmap);
// fill standard DRAWITEMSTRUCT to send it to DrawHeader function
//
DRAWITEMSTRUCT dis;
dis.CtlType=0;
dis.CtlID=GetDlgCtrlID();
dis.itemID=(UINT) PtrToUint(hGroup);
dis.itemAction=ODA_DRAWENTIRE;
dis.itemState=ODS_DEFAULT;
dis.hwndItem=GetSafeHwnd();
dis.hDC=dcCompatible.GetSafeHdc();
dis.rcItem=rectHeader;
dis.itemData=(DWORD)PtrToUlong(hGroup);
DrawHeader(&dis);
dc.BitBlt(rect.left, rect.top, rect.Width(),
rect.Height(), &dcCompatible, 0, 0, SRCCOPY);
if(pOldBitmap)
{
dcCompatible.SelectObject(pOldBitmap);
}
}
}
// Restore dc
dc.RestoreDC(nSavedDC);
// Do not call CWnd::OnPaint() for painting messages
}
void COXShortcutBar::OnSize(UINT nType, int cx, int cy)
{
CWnd::OnSize(nType, cx, cy);
// TODO: Add your message handler code here
// reposition groups
CalculateBoundRects();
// redraw control
RedrawBar();
}
BOOL COXShortcutBar::OnEraseBkgnd(CDC* pDC)
{
// TODO: Add your message handler code here and/or call default
UNREFERENCED_PARAMETER(pDC);
return TRUE;
}
#if _MSC_VER >= 1400
LRESULT COXShortcutBar::OnNcHitTest(CPoint point)
#else
UINT COXShortcutBar::OnNcHitTest(CPoint point)
#endif
{
UNREFERENCED_PARAMETER(point);
return HTCLIENT;
}
void COXShortcutBar::OnDestroy()
{
CWnd::OnDestroy();
// TODO: Add your message handler code here
// just kill timers
if(m_nWaitForDragHeaderID!=0)
{
KillTimer(m_nWaitForDragHeaderID);
}
if(m_nWaitForAutoExpandID!=0)
{
KillTimer(m_nWaitForAutoExpandID);
}
if(m_nCheckMouseIsOverGroupID!=0)
{
KillTimer(m_nCheckMouseIsOverGroupID);
}
}
// v9.3 - update 03 - 64-bit - using OXTPARAM here - see UTB64Bit.h
void COXShortcutBar::OnTimer(OXTPARAM nIDEvent)
{
// TODO: Add your message handler code here and/or call default
// reset pressed group handle
if(m_hPressedGroup!=NULL && !IsLButtonDown())
{
BOOL bFlat=((GetBarStyle() & SHBS_FLATGROUPBUTTON)==SHBS_FLATGROUPBUTTON);
// reset pressed group
HSHBGROUP hOldPressedGroup=m_hPressedGroup;
m_hPressedGroup=NULL;
if(bFlat)
{
// redraw header
UpdateHeader(hOldPressedGroup);
}
}
if(m_nWaitForDragHeaderID==nIDEvent)
{
ASSERT(GetDragGroup()==NULL);
// kill time after time has expired
KillTimer(m_nWaitForDragHeaderID);
m_nWaitForDragHeaderID=0;
}
else if(m_nWaitForAutoExpandID==nIDEvent)
{
ASSERT(m_hSuspectAutoExpandGroup!=NULL);
ASSERT(GetExpandedGroup()!=m_hSuspectAutoExpandGroup);
// kill time after time has expired
KillTimer(m_nWaitForAutoExpandID);
m_nWaitForAutoExpandID=0;
CPoint point;
::GetCursorPos(&point);
ScreenToClient(&point);
// test the current cursor point
UINT nHitFlags;
HSHBGROUP hGroup=HitTest(point,&nHitFlags);
// expand the m_hSuspectAutoExpandGroup group if the mouse is still over it
if(nHitFlags==SHB_ONHEADER && m_hSuspectAutoExpandGroup==hGroup)
{
ExpandGroup(hGroup);
}
else
{
m_hSuspectAutoExpandGroup=NULL;
}
}
else if(m_nCheckMouseIsOverGroupID==nIDEvent)
{
CPoint point;
::GetCursorPos(&point);
ScreenToClient(&point);
// test the current cursor point
UINT nHitFlags;
HSHBGROUP hGroup=HitTest(point,&nHitFlags);
// reset handle to the group that is positioned under mouse cursor
SetMouseIsOverGroup((nHitFlags==SHB_ONHEADER ? hGroup : NULL));
}
else
{
CWnd::OnTimer(nIDEvent);
}
}
BOOL COXShortcutBar::OnHeaderToolTip(UINT id, NMHDR* pNMHDR, LRESULT* pResult)
{
UNREFERENCED_PARAMETER(id);
ASSERT(pNMHDR->code==TTN_NEEDTEXTA || pNMHDR->code==TTN_NEEDTEXTW);
// need to handle both ANSI and UNICODE versions of the message
TOOLTIPTEXTA* pTTTA=(TOOLTIPTEXTA*)pNMHDR;
TOOLTIPTEXTW* pTTTW=(TOOLTIPTEXTW*)pNMHDR;
// idFrom must be the handle to the group
if (pNMHDR->code==TTN_NEEDTEXTA)
ASSERT((pTTTA->uFlags&TTF_IDISHWND)==0);
else
ASSERT((pTTTW->uFlags&TTF_IDISHWND)==0);
SHBINFOTIP shbit;
// fill structure for notification
NMSHORTCUTBAR nmshb;
nmshb.hdr.code=SHBN_GETHEADERINFOTIP;
nmshb.hGroup=(HSHBGROUP)pNMHDR->idFrom;
nmshb.lParam=(LPARAM)(&shbit);
*pResult=SendSHBNotification(&nmshb);
#ifndef _UNICODE
if(pNMHDR->code==TTN_NEEDTEXTA)
lstrcpyn(pTTTA->szText,shbit.szText,countof(pTTTA->szText));
else
_mbstowcsz(pTTTW->szText,shbit.szText,countof(pTTTW->szText));
#else
if (pNMHDR->code==TTN_NEEDTEXTA)
_wcstombsz(pTTTA->szText,shbit.szText,countof(pTTTA->szText));
else
lstrcpyn(pTTTW->szText,shbit.szText,countof(pTTTW->szText));
#endif
if(pNMHDR->code==TTN_NEEDTEXTA)
{
if(shbit.lpszText==NULL)
pTTTA->lpszText=pTTTA->szText;
else
pTTTA->lpszText=(LPSTR)shbit.lpszText;
pTTTA->hinst=shbit.hinst;
}
else
{
if(shbit.lpszText==NULL)
pTTTW->lpszText=pTTTW->szText;
else
pTTTW->lpszText=(LPWSTR)shbit.lpszText;
pTTTW->hinst=shbit.hinst;
}
return TRUE; // message was handled
}
void COXShortcutBar::OnChangeGroupHeaderText(NMHDR* pNotifyStruct, LRESULT* result)
{
// handle SHBEN_FINISHEDIT notification from COXSHBEdit control
ASSERT(::IsWindow(m_edit.GetSafeHwnd()));
ASSERT(m_hEditGroup);
LPNMSHBEDIT lpNMSHBE=(LPNMSHBEDIT)pNotifyStruct;
// fill structure for notification
NMSHORTCUTBAR nmshb;
nmshb.hdr.code=SHBN_ENDHEADEREDIT;
nmshb.hGroup=m_hEditGroup;
// if editing wasn't cancelled
if(lpNMSHBE->bOK)
{
// get updated text
CString sText;
m_edit.GetWindowText(sText);
nmshb.lParam=(LPARAM)((LPCTSTR)sText);
if(!SendSHBNotification(&nmshb))
{
// change header text
if(!SetGroupText(m_hEditGroup,sText))
{
TRACE(_T("COXSHBListCtrl::OnChangeGroupHeaderText: failed to set text to item: %s"),sText);
}
else
{
CRect rectHeader;
if(GetGroupHeaderRect(m_hEditGroup,rectHeader))
RedrawWindow(rectHeader);
}
sText.ReleaseBuffer();
}
}
else
{
nmshb.lParam=(LPARAM)((LPCTSTR)GetGroupText(nmshb.hGroup));
SendSHBNotification(&nmshb);
}
m_hEditGroup=NULL;
*result=0;
}
// v9.3 - update 03 - 64-bit - return value was declared as LONG - changed to LRESULT
LRESULT COXShortcutBar::OnDragOver(WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(wParam);
if((GetBarStyle()&SHBS_DISABLEDROPITEM)!=0)
{
return (LONG)FALSE;
}
// fill structure for notification of parent window of this shortcut bar
NMSHORTCUTBAR nmshb;
nmshb.hdr.code=SHBN_DRAGOVER;
nmshb.lParam=lParam;
// check if parent of the shortcut bar would like to handle drag'n'drop itself
if(SendSHBNotification(&nmshb))
{
return (LONG)TRUE;
}
// lParam is the pointer to SHBDROPTARGETACTION structure
LPSHBDROPTARGETACTION pSHBDTAction=(LPSHBDROPTARGETACTION)lParam;
ASSERT(pSHBDTAction!=NULL);
ASSERT(pSHBDTAction->pWnd);
ASSERT(pSHBDTAction->pWnd->GetSafeHwnd()==GetSafeHwnd());
// set returned drop effect
pSHBDTAction->result=(LRESULT)DROPEFFECT_NONE;
// we are not dragging any group
ASSERT(GetDragGroup()==NULL);
// and are not waiting for drag any group
ASSERT(m_nWaitForDragHeaderID==0);
// test current cursor position
UINT nHitFlags;
HSHBGROUP hGroup=HitTest(pSHBDTAction->point,&nHitFlags);
if(hGroup!=NULL && hGroup!=GetExpandedGroup())
{
// kill timer for group autoexpanding
if(m_hSuspectAutoExpandGroup!=hGroup || nHitFlags!=SHB_ONHEADER)
{
// kill timer
if(m_nWaitForAutoExpandID!=0)
{
ASSERT(m_hSuspectAutoExpandGroup!=NULL);
KillTimer(m_nWaitForAutoExpandID);
m_nWaitForAutoExpandID=0;
m_hSuspectAutoExpandGroup=NULL;
}
else
{
ASSERT(m_hSuspectAutoExpandGroup==NULL);
}
}
// set timer for group autoexpanding
if(m_hSuspectAutoExpandGroup!=hGroup && nHitFlags==SHB_ONHEADER)
{
ASSERT(m_hSuspectAutoExpandGroup==NULL);
m_hSuspectAutoExpandGroup=hGroup;
// set timer
m_nWaitForAutoExpandID=SetTimer(
ID_WAITFORAUTOEXPANDTIMER,GetAutoExpandDelay()/4,NULL);
ASSERT(m_nWaitForAutoExpandID!=0);
}
}
return (LONG)TRUE;
}
// v9.3 - update 03 - 64-bit - return value was declared as LONG - changed to LRESULT
LRESULT COXShortcutBar::OnDragLeave(WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(wParam);
if((GetBarStyle()&SHBS_DISABLEDROPITEM)!=0)
{
return (LONG)FALSE;
}
// fill structure for notification of parent window of this shortcut bar
NMSHORTCUTBAR nmshb;
nmshb.hdr.code=SHBN_DRAGLEAVE;
nmshb.lParam=lParam;
// check if parent of the shortcut bar would like to handle drag'n'drop itself
if(SendSHBNotification(&nmshb))
{
return (LONG)TRUE;
}
// lParam is the pointer to SHBDROPTARGETACTION structure
LPSHBDROPTARGETACTION pSHBDTAction=(LPSHBDROPTARGETACTION)lParam;
ASSERT(pSHBDTAction!=NULL);
ASSERT(pSHBDTAction->pWnd);
ASSERT(pSHBDTAction->pWnd->GetSafeHwnd()==GetSafeHwnd());
// kill timer
if(m_nWaitForAutoExpandID!=0)
{
ASSERT(m_hSuspectAutoExpandGroup!=NULL);
KillTimer(m_nWaitForAutoExpandID);
m_nWaitForAutoExpandID=0;
m_hSuspectAutoExpandGroup=NULL;
}
return (LONG)TRUE;
}
void COXShortcutBar::OnLargeIcon()
{
HSHBGROUP hGroup=GetExpandedGroup();
if(hGroup==NULL)
return;
int nView=GetGroupView(hGroup);
if(nView!=SHB_LARGEICON)
{
// change view in result of selecting context menu item
VERIFY(SetGroupView(hGroup,SHB_LARGEICON));
ShowChildWnd(hGroup);
}
}
void COXShortcutBar::OnSmallIcon()
{
HSHBGROUP hGroup=GetExpandedGroup();
if(hGroup==NULL)
return;
int nView=GetGroupView(hGroup);
if(nView!=SHB_SMALLICON)
{
// change view in result of selecting context menu item
VERIFY(SetGroupView(hGroup,SHB_SMALLICON));
ShowChildWnd(hGroup);
}
}
void COXShortcutBar::OnAddNewGroup()
{
SHB_GROUPINFO shbGroup;
shbGroup.nMask=SHBIF_TEXT;
shbGroup.nTextMax=0;
shbGroup.pszText=_T("");
// add empty group
HSHBGROUP hGroup=InsertGroup(&shbGroup);
if(hGroup!=NULL)
{
if(GetGroupCount()==1)
ExpandGroup(hGroup);
else
{
CalculateBoundRects();
RedrawBar();
}
// edit header
EditGroupHeader(hGroup);
}
}
void COXShortcutBar::OnRemoveGroup()
{
HSHBGROUP hGroup=GetHotGroup();
if(hGroup!=NULL)
{
VERIFY(DeleteGroup(hGroup));
}
}
void COXShortcutBar::OnRenameGroup()
{
HSHBGROUP hGroup=GetHotGroup();
if(hGroup!=NULL)
{
EditGroupHeader(hGroup);
}
}
void COXShortcutBar::OnRemoveLCItem()
{
HSHBGROUP hGroup=GetExpandedGroup();
if(hGroup==NULL || GetGroupChildWnd(hGroup)!=NULL)
return;
// remove COXSHBListCtrl item in result of selecting context menu item
//
COXSHBListCtrl* pListCtrl=GetGroupListCtrl(hGroup);
ASSERT(pListCtrl);
ASSERT(::IsWindow(pListCtrl->GetSafeHwnd()));
int nItem=pListCtrl->GetHotItem();
if(nItem==-1)
nItem=pListCtrl->GetLastHotItem();
if(nItem!=-1)
DeleteLCItem(hGroup,nItem);
}
void COXShortcutBar::OnRenameLCItem()
{
HSHBGROUP hGroup=GetExpandedGroup();
if(hGroup==NULL || GetGroupChildWnd(hGroup)!=NULL)
return;
// edit COXSHBListCtrl item text in result of selecting context menu item
//
COXSHBListCtrl* pListCtrl=GetGroupListCtrl(hGroup);
ASSERT(pListCtrl);
ASSERT(::IsWindow(pListCtrl->GetSafeHwnd()));
int nItem=pListCtrl->GetHotItem();
if(nItem==-1)
nItem=pListCtrl->GetLastHotItem();
if(nItem!=-1)
{
pListCtrl->EnsureVisible(nItem,FALSE);
EditLCItem(hGroup,nItem);
}
}
// Update handlers
//
void COXShortcutBar::OnUpdateLargeIcon(CCmdUI* pCmdUI)
{
HSHBGROUP hGroup=GetExpandedGroup();
if(hGroup==NULL)
return;
int nView=GetGroupView(hGroup);
pCmdUI->SetCheck((nView==SHB_LARGEICON));
}
void COXShortcutBar::OnUpdateSmallIcon(CCmdUI* pCmdUI)
{
HSHBGROUP hGroup=GetExpandedGroup();
if(hGroup==NULL)
return;
int nView=GetGroupView(hGroup);
pCmdUI->SetCheck((nView==SHB_SMALLICON));
}
/////////////////////////////////////////////////////////////////////////////
LPSHB_DESCRIPTOR COXShortcutBar::AddDescriptor()
{
ASSERT(::IsWindow(GetSafeHwnd()));
// Internal function to dynamically allocate descriptor structure.
// We keep track of all created descriptors and delete them in destructor
LPSHB_DESCRIPTOR pDescriptor=NULL;
try
{
pDescriptor=new SHB_DESCRIPTOR;
}
catch (CMemoryException* pException)
{
UNREFERENCED_PARAMETER(pException);
TRACE(_T("COXShortcutBar::AddDescriptor: failed to allocate memory for SHB_DESCRIPTOR"));
return NULL;
}
ASSERT(pDescriptor!=NULL);
m_mapDescriptors.SetAt(pDescriptor,m_mapDescriptors.GetCount());
return pDescriptor;
}
void COXShortcutBar::DrawHeader(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
ASSERT(::IsWindow(GetSafeHwnd()));
HSHBGROUP hGroup=(HSHBGROUP)(INT_PTR)lpDrawItemStruct->itemID;
ASSERT(hGroup);
// fill structure for notification
NMSHORTCUTBAR nmshb;
nmshb.hdr.code=SHBN_DRAWHEADER;
nmshb.hGroup=hGroup;
nmshb.lParam=(LPARAM)lpDrawItemStruct;
if(SendSHBNotification(&nmshb))
{
return;
}
GetShortcutBarSkin()->DrawHeader(lpDrawItemStruct, this);
}
/////////////////////////////////////////////////////////////////////////////
BOOL COXShortcutBar::SetBarStyle(DWORD dwBarStyle)
{
if(!VerifyBarStyle(dwBarStyle))
{
return FALSE;
}
m_dwBarStyle=dwBarStyle;
if(::IsWindow(GetSafeHwnd()))
{
// Notify child windows that style have been changed
SendMessageToDescendants(
SHBM_SHBINFOCHANGED,(WPARAM)0,(LPARAM)SHBIF_SHBSTYLE);
}
return TRUE;
}
CImageList* COXShortcutBar::SetImageList(CImageList* pImageList)
{
// it's progammer's responsibility to create and keep image list alive
CImageList* pOldImageList=m_pImageList;
m_pImageList=pImageList;
return pOldImageList;
}
// functions to work with groups
//
BOOL COXShortcutBar::ChildWndIsLC(HSHBGROUP hGroup) const
{
ASSERT(hGroup);
ASSERT(::IsWindow(GetSafeHwnd()));
// we assume child window always exist
//
HWND hwndChild=GetGroupChildWnd(hGroup);
if(hwndChild)
{
return FALSE;
}
else
{
COXSHBListCtrl* pListCtrl=GetGroupListCtrl(hGroup);
ASSERT(pListCtrl!=NULL);
return TRUE;
}
}
COXSHBListCtrl* COXShortcutBar::GetGroupListCtrl(HSHBGROUP hGroup) const
{
ASSERT(::IsWindow(GetSafeHwnd()));
ASSERT(hGroup);
COXSHBListCtrl* pListCtrl;
if(!m_mapHandleToListCtrl.Lookup(hGroup,pListCtrl))
{
return NULL;
}
ASSERT(pListCtrl!=NULL);
ASSERT_VALID(pListCtrl);
return pListCtrl;
}
BOOL COXShortcutBar::GetGroupInfo(HSHBGROUP hGroup,
LPSHB_GROUPINFO pGroupInfo,
BOOL bSubstitute/* = FALSE*/)
{
ASSERT(::IsWindow(GetSafeHwnd()));
ASSERT(hGroup);
ASSERT(pGroupInfo);
if(!VerifyGroupInfo(pGroupInfo))
{
return FALSE;
}
LPSHB_GROUPINFO pSavedGroupInfo;
if(!m_mapHandleToInfo.Lookup(hGroup,pSavedGroupInfo))
{
TRACE(_T("COXShortcutBar::GetGroupInfo: group wasn't created"));
return FALSE;
}
ASSERT(pSavedGroupInfo!=NULL);
// CopyGroupInfo provide all we need to copy information
return CopyGroupInfo(pGroupInfo,pSavedGroupInfo,
(bSubstitute ? SHB_CPYINFO_COPY|SHB_CPYINFO_GET : SHB_CPYINFO_GET));
}
BOOL COXShortcutBar::SetGroupInfo(HSHBGROUP hGroup,
const LPSHB_GROUPINFO pGroupInfo,
BOOL bSubstitute/* = FALSE*/)
{
ASSERT(::IsWindow(GetSafeHwnd()));
ASSERT(hGroup);
ASSERT(pGroupInfo);
LPSHB_GROUPINFO pSavedGroupInfo;
if(!m_mapHandleToInfo.Lookup(hGroup,pSavedGroupInfo))
{
TRACE(_T("COXShortcutBar::SetGroupInfo: group wasn't created"));
return FALSE;
}
ASSERT(pSavedGroupInfo!=NULL);
if(CopyGroupInfo(pSavedGroupInfo,pGroupInfo,
(bSubstitute ? SHB_CPYINFO_COPY|SHB_CPYINFO_SET : SHB_CPYINFO_SET)))
{
// recalc rectangles if changed order
if((pGroupInfo->nMask&(SHBIF_ORDER)))
CalculateBoundRects();
// recalc rectangles if changed descriptor both of shortcut bar and child window
if((pGroupInfo->nMask&(SHBIF_DESCRIPTOR)))
CalculateBoundRects();
// if changed view then scroll list control to the top
// if changed child window
if((pGroupInfo->nMask&SHBIF_CHILDWND))
{
if(pGroupInfo->hwndChild==NULL)
{
// if child window was removed we have to create COXSHBListCrl
// for such window because every group have to have child window
COXSHBListCtrl* pListCtrl=GetGroupListCtrl(hGroup);
if(pListCtrl!=NULL)
{
try
{
pListCtrl=new COXSHBListCtrl();
}
catch (CMemoryException* pException)
{
UNREFERENCED_PARAMETER(pException);
TRACE(_T("COXShortcutBar::SetGroupInfo: failed to allocate memory for COXSHBListCtrl"));
return FALSE;
}
m_mapHandleToListCtrl.SetAt(hGroup,pListCtrl);
ASSERT(pListCtrl);
if(!CreateListControl(hGroup))
{
m_mapHandleToListCtrl.RemoveKey(hGroup);
delete pListCtrl;
return FALSE;
}
}
}
else
{
// send message to child window about aknowledging it and set group to it
ASSERT(::IsWindow(pGroupInfo->hwndChild));
::SendMessage(pGroupInfo->hwndChild,SHBM_SETSHBGROUP,(WPARAM)0,
(LPARAM)hGroup);
}
}
//send message to child window so it has chance to react on changed group info
HWND hwndChild=GetGroupChildWndHandle(hGroup);
ASSERT(hwndChild);
::SendMessage(hwndChild,SHBM_GROUPINFOCHANGED,(WPARAM)0,
(LPARAM)pGroupInfo->nMask);
}
return TRUE;
}
///////////////////////////////////////////////////////////////////////////////
// All following SetGroup* functions eventually call SetGroupInfo function
//
///////////////////////////////////////////////////////////////////////////////
int COXShortcutBar::GetGroupImage(HSHBGROUP hGroup) const
{
ASSERT(::IsWindow(GetSafeHwnd()));
ASSERT(hGroup);
LPSHB_GROUPINFO pSavedGroupInfo;
if(!m_mapHandleToInfo.Lookup(hGroup,pSavedGroupInfo) ||
(pSavedGroupInfo->nMask&SHBIF_IMAGE)==0)
{
return -1;
}
ASSERT(pSavedGroupInfo!=NULL);
return pSavedGroupInfo->nImage;
}
BOOL COXShortcutBar::SetGroupImage(HSHBGROUP hGroup, int nImage)
{
ASSERT(hGroup);
SHB_GROUPINFO shbGroup;
shbGroup.nMask=SHBIF_IMAGE;
shbGroup.nImage=nImage;
return SetGroupInfo(hGroup,&shbGroup);
}
int COXShortcutBar::GetGroupImageExpanded(HSHBGROUP hGroup) const
{
ASSERT(::IsWindow(GetSafeHwnd()));
ASSERT(hGroup);
LPSHB_GROUPINFO pSavedGroupInfo;
if(!m_mapHandleToInfo.Lookup(hGroup,pSavedGroupInfo))
{
return -1;
}
ASSERT(pSavedGroupInfo!=NULL);
if((pSavedGroupInfo->nMask&SHBIF_IMAGEEXPANDED)!=0)
{
return pSavedGroupInfo->nImageExpanded;
}
else
{
return GetGroupImage(hGroup);
}
}
BOOL COXShortcutBar::SetGroupImageExpanded(HSHBGROUP hGroup, int nImageExpanded)
{
ASSERT(hGroup);
SHB_GROUPINFO shbGroup;
shbGroup.nMask=SHBIF_IMAGEEXPANDED;
shbGroup.nImageExpanded=nImageExpanded;
return SetGroupInfo(hGroup,&shbGroup);
}
CString COXShortcutBar::GetGroupText(HSHBGROUP hGroup) const
{
ASSERT(::IsWindow(GetSafeHwnd()));
ASSERT(hGroup);
LPSHB_GROUPINFO pSavedGroupInfo;
if(!m_mapHandleToInfo.Lookup(hGroup,pSavedGroupInfo) ||
(pSavedGroupInfo->nMask&SHBIF_TEXT)==0)
{
return _T("");
}
ASSERT(pSavedGroupInfo!=NULL);
// copy text
return CString(pSavedGroupInfo->pszText, pSavedGroupInfo->nTextMax);
}
BOOL COXShortcutBar::SetGroupText(HSHBGROUP hGroup, CString sText)
{
ASSERT(hGroup);
SHB_GROUPINFO shbGroup;
shbGroup.nMask=SHBIF_TEXT;
shbGroup.pszText=sText.GetBuffer(sText.GetLength());
shbGroup.nTextMax=sText.GetLength();
sText.ReleaseBuffer();
return SetGroupInfo(hGroup,&shbGroup);
}
BOOL COXShortcutBar::SetGroupText(HSHBGROUP hGroup, LPCTSTR pszText,
int nTextMax)
{
ASSERT(hGroup);
SHB_GROUPINFO shbGroup;
shbGroup.nMask=SHBIF_TEXT;
shbGroup.pszText=(LPTSTR)pszText;
shbGroup.nTextMax=nTextMax;
return SetGroupInfo(hGroup,&shbGroup);
}
LPARAM COXShortcutBar::GetGroupData(HSHBGROUP hGroup) const
{
ASSERT(::IsWindow(GetSafeHwnd()));
ASSERT(hGroup);
LPSHB_GROUPINFO pSavedGroupInfo;
if(!m_mapHandleToInfo.Lookup(hGroup,pSavedGroupInfo) ||
(pSavedGroupInfo->nMask&SHBIF_PARAM)==0)
{
return NULL;
}
ASSERT(pSavedGroupInfo!=NULL);
return pSavedGroupInfo->lParam;
}
BOOL COXShortcutBar::SetGroupData(HSHBGROUP hGroup, LPARAM lParam)
{
ASSERT(hGroup);
SHB_GROUPINFO shbGroup;
shbGroup.nMask=SHBIF_PARAM;
shbGroup.lParam=lParam;
return SetGroupInfo(hGroup,&shbGroup);
}
int COXShortcutBar::GetGroupOrder(HSHBGROUP hGroup) const
{
ASSERT(::IsWindow(GetSafeHwnd()));
ASSERT(hGroup);
LPSHB_GROUPINFO pSavedGroupInfo;
if(!m_mapHandleToInfo.Lookup(hGroup,pSavedGroupInfo) ||
(pSavedGroupInfo->nMask&SHBIF_ORDER)==0)
{
return -1;
}
ASSERT(pSavedGroupInfo!=NULL);
return pSavedGroupInfo->nOrder;
}
BOOL COXShortcutBar::SetGroupOrder(HSHBGROUP hGroup, int nOrder)
{
ASSERT(hGroup);
SHB_GROUPINFO shbGroup;
shbGroup.nMask=SHBIF_ORDER;
shbGroup.nOrder=nOrder;
return SetGroupInfo(hGroup,&shbGroup);
}
int COXShortcutBar::GetGroupView(HSHBGROUP hGroup) const
{
ASSERT(::IsWindow(GetSafeHwnd()));
ASSERT(hGroup);
LPSHB_GROUPINFO pSavedGroupInfo;
if(!m_mapHandleToInfo.Lookup(hGroup,pSavedGroupInfo))
{
return -1;
}
ASSERT(pSavedGroupInfo!=NULL);
if((pSavedGroupInfo->nMask&SHBIF_VIEW)==0)
{
return SHB_LARGEICON;
}
return pSavedGroupInfo->nView;
}
BOOL COXShortcutBar::SetGroupView(HSHBGROUP hGroup, int nView)
{
// this function can be called with hGroup=NULL. In this case we
// set specified view to every group in shortcut bar
SHB_GROUPINFO shbGroup;
shbGroup.nMask=SHBIF_VIEW;
shbGroup.nView=nView;
if(hGroup!=NULL)
return SetGroupInfo(hGroup,&shbGroup);
else
{
if(!VerifyView(nView))
return FALSE;
LPSHB_GROUPINFO pGroupInfo;
POSITION pos=m_mapHandleToInfo.GetStartPosition();
while(pos!=NULL)
{
m_mapHandleToInfo.GetNextAssoc(pos,hGroup,pGroupInfo);
ASSERT(hGroup!=NULL);
ASSERT(pGroupInfo!=NULL);
if(!SetGroupInfo(hGroup,&shbGroup))
return FALSE;
}
}
return TRUE;
}
LPSHB_DESCRIPTOR COXShortcutBar::GetGroupDescriptor(HSHBGROUP hGroup) const
{
ASSERT(::IsWindow(GetSafeHwnd()));
ASSERT(hGroup);
LPSHB_GROUPINFO pSavedGroupInfo;
if(!m_mapHandleToInfo.Lookup(hGroup,pSavedGroupInfo) ||
(pSavedGroupInfo->nMask&SHBIF_DESCRIPTOR)==0)
{
return NULL;
}
ASSERT(pSavedGroupInfo!=NULL);
return pSavedGroupInfo->pDescriptor;
}
BOOL COXShortcutBar::SetGroupDescriptor(HSHBGROUP hGroup,
const LPSHB_DESCRIPTOR pDescriptor)
{
// this function can be called with hGroup=NULL. In this case we
// set specified view to every group in shortcut bar
SHB_GROUPINFO shbGroup;
shbGroup.nMask=SHBIF_DESCRIPTOR;
shbGroup.pDescriptor=pDescriptor;
if(hGroup!=NULL)
{
return SetGroupInfo(hGroup,&shbGroup);
}
else
{
if(!VerifyDescriptor(pDescriptor))
return FALSE;
LPSHB_GROUPINFO pGroupInfo;
POSITION pos=m_mapHandleToInfo.GetStartPosition();
while(pos!=NULL)
{
m_mapHandleToInfo.GetNextAssoc(pos,hGroup,pGroupInfo);
ASSERT(hGroup!=NULL);
ASSERT(pGroupInfo!=NULL);
if(!SetGroupInfo(hGroup,&shbGroup))
return FALSE;
}
}
return TRUE;
}
HWND COXShortcutBar::GetGroupChildWnd(HSHBGROUP hGroup) const
{
ASSERT(::IsWindow(GetSafeHwnd()));
ASSERT(hGroup);
LPSHB_GROUPINFO pSavedGroupInfo;
if(!m_mapHandleToInfo.Lookup(hGroup,pSavedGroupInfo) ||
(pSavedGroupInfo->nMask&SHBIF_CHILDWND)==0)
{
return NULL;
}
ASSERT(pSavedGroupInfo!=NULL);
return pSavedGroupInfo->hwndChild;
}
BOOL COXShortcutBar::SetGroupChildWnd(HSHBGROUP hGroup, HWND hwndChild)
{
ASSERT(hGroup);
SHB_GROUPINFO shbGroup;
shbGroup.nMask=SHBIF_CHILDWND;
shbGroup.hwndChild=hwndChild;
return SetGroupInfo(hGroup,&shbGroup);
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// All following SetGroup* functions eventually call SetGroupDescriptor function
//
///////////////////////////////////////////////////////////////////////////////
COLORREF COXShortcutBar::GetGroupBkColor(HSHBGROUP hGroup)
{
ASSERT(::IsWindow(GetSafeHwnd()));
ASSERT(hGroup);
LPSHB_GROUPINFO pSavedGroupInfo;
if(!m_mapHandleToInfo.Lookup(hGroup,pSavedGroupInfo))
{
return ID_COLOR_NONE;
}
ASSERT(pSavedGroupInfo!=NULL);
if(pSavedGroupInfo->nMask&SHBIF_DESCRIPTOR &&
pSavedGroupInfo->pDescriptor->nMask&SHBIF_CLRBACK)
{
return pSavedGroupInfo->pDescriptor->clrBackground;
}
else
{
return GetShortcutBarSkin()->GetBackgroundColor(this);
}
}
BOOL COXShortcutBar::HasGroupBkColor(HSHBGROUP hGroup)
{
ASSERT(::IsWindow(GetSafeHwnd()));
ASSERT(hGroup);
LPSHB_GROUPINFO pSavedGroupInfo;
if(!m_mapHandleToInfo.Lookup(hGroup,pSavedGroupInfo))
return FALSE;
ASSERT(pSavedGroupInfo!=NULL);
if(pSavedGroupInfo->nMask&SHBIF_DESCRIPTOR &&
pSavedGroupInfo->pDescriptor->nMask&SHBIF_CLRBACK)
return TRUE;
else
return FALSE;
}
BOOL COXShortcutBar::SetGroupBkColor(HSHBGROUP hGroup, COLORREF clr)
{
SHB_DESCRIPTOR descriptor;
descriptor.nMask=SHBIF_CLRBACK;
descriptor.clrBackground=clr;
return SetGroupDescriptor(hGroup,&descriptor);
}
COLORREF COXShortcutBar::GetGroupTextColor(HSHBGROUP hGroup)
{
ASSERT(::IsWindow(GetSafeHwnd()));
ASSERT(hGroup);
LPSHB_GROUPINFO pSavedGroupInfo;
if(!m_mapHandleToInfo.Lookup(hGroup,pSavedGroupInfo))
{
return ID_COLOR_NONE;
}
ASSERT(pSavedGroupInfo!=NULL);
if(pSavedGroupInfo->nMask&SHBIF_DESCRIPTOR &&
pSavedGroupInfo->pDescriptor->nMask&SHBIF_CLRTEXT)
return pSavedGroupInfo->pDescriptor->clrText;
else
return GetShortcutBarSkin()->GetGroupTextColor(this);
}
BOOL COXShortcutBar::SetGroupTextColor(HSHBGROUP hGroup, COLORREF clr)
{
SHB_DESCRIPTOR descriptor;
descriptor.nMask=SHBIF_CLRTEXT;
descriptor.clrText=clr;
return SetGroupDescriptor(hGroup,&descriptor);
}
COLORREF COXShortcutBar::GetGroupHeaderBkColor(HSHBGROUP hGroup) const
{
ASSERT(::IsWindow(GetSafeHwnd()));
ASSERT(hGroup);
LPSHB_GROUPINFO pSavedGroupInfo;
if(!m_mapHandleToInfo.Lookup(hGroup,pSavedGroupInfo))
return ID_COLOR_NONE;
ASSERT(pSavedGroupInfo!=NULL);
if(pSavedGroupInfo->nMask&SHBIF_DESCRIPTOR &&
pSavedGroupInfo->pDescriptor->nMask&SHBIF_HDRCLRBACK)
{
return pSavedGroupInfo->pDescriptor->clrHeaderBackground;
}
else
{
return ::GetSysColor(COLOR_BTNFACE);
}
}
BOOL COXShortcutBar::SetGroupHeaderBkColor(HSHBGROUP hGroup, COLORREF clr)
{
SHB_DESCRIPTOR descriptor;
descriptor.nMask=SHBIF_HDRCLRBACK;
descriptor.clrHeaderBackground=clr;
return SetGroupDescriptor(hGroup,&descriptor);
}
COLORREF COXShortcutBar::GetGroupHeaderTextColor(HSHBGROUP hGroup) const
{
ASSERT(::IsWindow(GetSafeHwnd()));
ASSERT(hGroup);
LPSHB_GROUPINFO pSavedGroupInfo;
if(!m_mapHandleToInfo.Lookup(hGroup,pSavedGroupInfo))
{
return ID_COLOR_NONE;
}
ASSERT(pSavedGroupInfo!=NULL);
if(pSavedGroupInfo->nMask&SHBIF_DESCRIPTOR &&
pSavedGroupInfo->pDescriptor->nMask&SHBIF_HDRCLRTEXT)
{
return pSavedGroupInfo->pDescriptor->clrHeaderText;
}
else
{
return ::GetSysColor(COLOR_BTNTEXT);
}
}
BOOL COXShortcutBar::SetGroupHeaderTextColor(HSHBGROUP hGroup, COLORREF clr)
{
SHB_DESCRIPTOR descriptor;
descriptor.nMask=SHBIF_HDRCLRTEXT;
descriptor.clrHeaderText=clr;
return SetGroupDescriptor(hGroup,&descriptor);
}
UINT COXShortcutBar::GetGroupHeaderTextFormat(HSHBGROUP hGroup) const
{
ASSERT(::IsWindow(GetSafeHwnd()));
ASSERT(hGroup);
LPSHB_GROUPINFO pSavedGroupInfo;
if(!m_mapHandleToInfo.Lookup(hGroup,pSavedGroupInfo))
{
return 0;
}
ASSERT(pSavedGroupInfo!=NULL);
if(pSavedGroupInfo->nMask&SHBIF_DESCRIPTOR &&
pSavedGroupInfo->pDescriptor->nMask&SHBIF_HDRTEXTFMT)
{
return pSavedGroupInfo->pDescriptor->nHeaderTextFormat;
}
else
{
return DFLT_HDRTEXTFMT;
}
}
BOOL COXShortcutBar::SetGroupHeaderTextFormat(HSHBGROUP hGroup, UINT nFormat)
{
SHB_DESCRIPTOR descriptor;
descriptor.nMask=SHBIF_HDRTEXTFMT;
descriptor.nHeaderTextFormat=nFormat;
return SetGroupDescriptor(hGroup,&descriptor);
}
LPLOGFONT COXShortcutBar::GetGroupTextFont(HSHBGROUP hGroup) const
{
ASSERT(::IsWindow(GetSafeHwnd()));
ASSERT(hGroup);
LPSHB_GROUPINFO pSavedGroupInfo;
if(!m_mapHandleToInfo.Lookup(hGroup,pSavedGroupInfo))
{
return NULL;
}
ASSERT(pSavedGroupInfo!=NULL);
if(!(pSavedGroupInfo->nMask&SHBIF_DESCRIPTOR &&
pSavedGroupInfo->pDescriptor->nMask&SHBIF_FONT))
{
// we have to declare it as const to comply with const function definition
const CWnd* pWnd=this;
// get child window handle
HWND hwndChild=GetGroupChildWndHandle(hGroup);
if(hwndChild!=NULL)
{
// try child window's font first of all
pWnd=CWnd::FromHandle(hwndChild);
if(!pWnd->GetFont())
{
pWnd=this;
}
}
// get font of child or this window
CFont* pFont=pWnd->GetFont();
if(pFont)
{
static LOGFONT lf;
VERIFY(pFont->GetLogFont(&lf));
return &lf;
}
else
{
return NULL;
}
}
return &pSavedGroupInfo->pDescriptor->lfText;
}
BOOL COXShortcutBar::SetGroupTextFont(HSHBGROUP hGroup, const LPLOGFONT pLF)
{
ASSERT(pLF);
SHB_DESCRIPTOR descriptor;
descriptor.nMask=SHBIF_FONT;
if(!CopyLogFont(&descriptor.lfText,pLF))
{
TRACE(_T("COXShortcutBar::SetGroupTextFont: failed to get log font"));
return FALSE;
}
return SetGroupDescriptor(hGroup,&descriptor);
}
BOOL COXShortcutBar::SetGroupTextFont(HSHBGROUP hGroup, CFont* pFont)
{
ASSERT(pFont);
SHB_DESCRIPTOR descriptor;
descriptor.nMask=SHBIF_FONT;
if(!pFont->GetLogFont(&descriptor.lfText))
{
TRACE(_T("COXShortcutBar::SetGroupTextFont: failed to get log font"));
return FALSE;
}
return SetGroupDescriptor(hGroup,&descriptor);
}
LPLOGFONT COXShortcutBar::GetGroupHeaderTextFont(HSHBGROUP hGroup)
{
ASSERT(::IsWindow(GetSafeHwnd()));
ASSERT(hGroup);
LPSHB_GROUPINFO pSavedGroupInfo;
if(!m_mapHandleToInfo.Lookup(hGroup,pSavedGroupInfo))
{
return NULL;
}
ASSERT(pSavedGroupInfo!=NULL);
if(!(pSavedGroupInfo->nMask&SHBIF_DESCRIPTOR &&
pSavedGroupInfo->pDescriptor->nMask&SHBIF_HDRFONT))
{
// use default font
ASSERT((HFONT)m_fontDefault!=NULL);
static LOGFONT lf;
VERIFY(m_fontDefault.GetLogFont(&lf));
return &lf;
}
return &pSavedGroupInfo->pDescriptor->lfHeader;
}
BOOL COXShortcutBar::SetGroupHeaderTextFont(HSHBGROUP hGroup, const LPLOGFONT pLF)
{
ASSERT(pLF);
SHB_DESCRIPTOR descriptor;
descriptor.nMask=SHBIF_HDRFONT;
if(!CopyLogFont(&descriptor.lfHeader,pLF))
{
TRACE(_T("COXShortcutBar::SetGroupHeaderTextFont: failed to get log font"));
return FALSE;
}
return SetGroupDescriptor(hGroup,&descriptor);
}
BOOL COXShortcutBar::SetGroupHeaderTextFont(HSHBGROUP hGroup, CFont* pFont)
{
ASSERT(pFont);
SHB_DESCRIPTOR descriptor;
descriptor.nMask=SHBIF_HDRFONT;
if(!pFont->GetLogFont(&descriptor.lfHeader))
{
TRACE(_T("COXShortcutBar::SetGroupHeaderTextFont: failed to get log font"));
return FALSE;
}
return SetGroupDescriptor(hGroup,&descriptor);
}
int COXShortcutBar::GetGroupHeaderHeight(HSHBGROUP hGroup) const
{
ASSERT(::IsWindow(GetSafeHwnd()));
ASSERT(hGroup);
LPSHB_GROUPINFO pSavedGroupInfo;
if(!m_mapHandleToInfo.Lookup(hGroup,pSavedGroupInfo))
{
return -1;
}
ASSERT(pSavedGroupInfo!=NULL);
if(pSavedGroupInfo->nMask&SHBIF_DESCRIPTOR &&
pSavedGroupInfo->pDescriptor->nMask&SHBIF_HDRHEIGHT)
{
return pSavedGroupInfo->pDescriptor->nHeaderHeight;
}
else
{
COXShortcutBar * me = const_cast<COXShortcutBar *>(this);
COXShortcutBarSkin * skin = me->GetShortcutBarSkin();
switch(skin->GetType())
{
case OXSkinClassic:
return DFLT_HDRHEIGHT;
break;
case OXSkinOffice2003:
return DFLT_HDRHEIGHT2003;
break;
case OXSkinOfficeXP:
return DFLT_HDRHEIGHTXP;
break;
default:
return DFLT_HDRHEIGHT;
break;
}
}
}
BOOL COXShortcutBar::SetGroupHeaderHeight(HSHBGROUP hGroup, int nHeight)
{
ASSERT(nHeight>=0);
SHB_DESCRIPTOR descriptor;
descriptor.nMask=SHBIF_HDRHEIGHT;
descriptor.nHeaderHeight=nHeight;
return SetGroupDescriptor(hGroup,&descriptor);
}
///////////////////////////////////////////////////////////////////////////////
HSHBGROUP COXShortcutBar::InsertGroup(const LPSHB_GROUPINFO pGroupInfo)
{
ASSERT(::IsWindow(GetSafeHwnd()));
ASSERT(pGroupInfo);
// fill structure for notification
NMSHORTCUTBAR nmshb;
nmshb.hdr.code=SHBN_INSERTGROUP;
nmshb.lParam=(LPARAM)pGroupInfo;
if(SendSHBNotification(&nmshb))
{
return NULL;
}
// verify specified structure
if(!VerifyGroupInfo(pGroupInfo))
{
return NULL;
}
// request unique handle for new group
HSHBGROUP hGroup=GetUniqueGroupHandle();
if(hGroup==NULL)
{
return NULL;
}
LPSHB_GROUPINFO pSavedGroupInfo;
try
{
pSavedGroupInfo=new SHB_GROUPINFO;
}
catch (CMemoryException* pException)
{
UNREFERENCED_PARAMETER(pException);
TRACE(_T("COXShortcutBar::InsertGroup: failed to allocate memory for SHB_GROUPINFO"));
return NULL;
}
ASSERT(pSavedGroupInfo!=NULL);
// Save supplied group info. Note that the third parameter is set to TRUE,
// which means that pGroupInfo will be completely copied to pSavedGroupInfo
if(!CopyGroupInfo(pSavedGroupInfo,pGroupInfo,SHB_CPYINFO_GET|SHB_CPYINFO_COPY))
{
delete pSavedGroupInfo;
return NULL;
}
// check if we have to reorder existing groups
BOOL bNeedSorting=FALSE;
if(pSavedGroupInfo->nMask&SHBIF_ORDER)
{
switch(pSavedGroupInfo->nOrder)
{
case SHBI_FIRST:
{
UpdateHeaderOrder(0,-1,1);
pSavedGroupInfo->nOrder=0;
break;
}
case SHBI_LAST:
{
pSavedGroupInfo->nOrder=PtrToInt(GetGroupCount());
break;
}
case SHBI_SORT:
{
pSavedGroupInfo->nOrder=PtrToInt(GetGroupCount());
bNeedSorting=TRUE;
break;
}
default:
{
UpdateHeaderOrder(pSavedGroupInfo->nOrder,-1,1);
pSavedGroupInfo->nOrder=0;
break;
}
}
}
else
{
pSavedGroupInfo->nMask|=SHBIF_ORDER;
pSavedGroupInfo->nOrder=PtrToInt(GetGroupCount());
}
m_mapHandleToInfo.SetAt(hGroup,pSavedGroupInfo);
// if we inserted group using SHBI_SORT constant then we have to resort all groups
if(bNeedSorting)
{
if(!SortGroups(1))
{
VERIFY(DeleteGroup(hGroup));
return FALSE;
}
}
if((pSavedGroupInfo->nMask&SHBIF_CHILDWND)==0)
{
COXSHBListCtrl* pListCtrl=NULL;
try
{
pListCtrl=NewSHBListCtrl();
}
catch (CMemoryException* pException)
{
UNREFERENCED_PARAMETER(pException);
TRACE(_T("COXShortcutBar::InsertGroup: failed to allocate memory for COXSHBListCtrl"));
VERIFY(DeleteGroup(hGroup));
return NULL;
}
m_mapHandleToListCtrl.SetAt(hGroup,pListCtrl);
// if child window wasn't specified then we have to create default one -
// COXSHBListCtrl
if(!CreateListControl(hGroup))
{
VERIFY(DeleteGroup(hGroup));
return NULL;
}
}
else
{
// send message to child window about aknowledging it and set group to it
ASSERT(pSavedGroupInfo->hwndChild);
::SendMessage(pSavedGroupInfo->hwndChild,SHBM_SETSHBGROUP,
(WPARAM)0,(LPARAM)hGroup);
}
// reposition groups
CalculateBoundRects();
return hGroup;
}
HSHBGROUP COXShortcutBar::InsertGroup(LPCTSTR pszText,
int nOrder,
int nImage,
int nImageExpanded,
LPARAM lParam,
int nView,
const LPSHB_DESCRIPTOR pDescriptor,
HWND hwndChild)
{
SHB_GROUPINFO shbGroup;
// label text
if(pszText!=NULL)
{
CString sText(pszText);
shbGroup.nMask|=SHBIF_TEXT;
shbGroup.nTextMax=sText.GetLength();
shbGroup.pszText=(LPTSTR)pszText;
}
// order of insertion
if(nOrder!=-1)
{
shbGroup.nMask|=SHBIF_ORDER;
shbGroup.nOrder=nOrder;
}
// label image
if(nImage!=-1)
{
shbGroup.nMask|=SHBIF_IMAGE;
shbGroup.nImage=nImage;
}
// label image (expanded)
if(nImage!=-1)
{
shbGroup.nMask|=SHBIF_IMAGEEXPANDED;
shbGroup.nImageExpanded=nImageExpanded;
}
// user defined data
if(lParam!=NULL)
{
shbGroup.nMask|=SHBIF_PARAM;
shbGroup.lParam=lParam;
}
// view type
if(nView!=-1)
{
shbGroup.nMask|=SHBIF_VIEW;
shbGroup.nView=nView;
}
// group description data
if(pDescriptor!=NULL)
{
shbGroup.nMask|=SHBIF_DESCRIPTOR;
shbGroup.pDescriptor=pDescriptor;
}
// child window
if(hwndChild!=NULL)
{
shbGroup.nMask|=SHBIF_CHILDWND;
shbGroup.hwndChild=hwndChild;
}
return InsertGroup(&shbGroup);
}
BOOL COXShortcutBar::DeleteGroup(HSHBGROUP hGroup)
{
ASSERT(::IsWindow(GetSafeHwnd()));
ASSERT(hGroup);
// fill structure for notification
NMSHORTCUTBAR nmshb;
nmshb.hdr.code=SHBN_DELETEGROUP;
nmshb.hGroup=hGroup;
if(SendSHBNotification(&nmshb))
{
return FALSE;
}
LPSHB_GROUPINFO pSavedGroupInfo;
if(!m_mapHandleToInfo.Lookup(hGroup,pSavedGroupInfo))
{
return FALSE;
}
ASSERT(pSavedGroupInfo!=NULL);
int nOrder=GetGroupOrder(hGroup);
ASSERT(nOrder!=-1);
UpdateHeaderOrder(nOrder+1,-1,-1);
// hide any associated child window
HideChildWnd(hGroup);
// notify child window that group is about to be deleted
HWND hwndChild=GetGroupChildWndHandle(hGroup);
ASSERT(hwndChild);
if(::IsWindow(hwndChild))
{
::SendMessage(hwndChild,SHBM_DELETESHBGROUP,(WPARAM)0,(LPARAM)0);
}
// delete associated group info
delete pSavedGroupInfo;
m_mapHandleToInfo.RemoveKey(hGroup);
m_mapHandleToBoundRect.RemoveKey(hGroup);
// if we deleted expanded group then we have to expand the next one
if(GetExpandedGroup()==hGroup)
{
int nGroupCount=PtrToInt(GetGroupCount());
if(nGroupCount==0)
{
// if the last group was deleted then we just clean up all info
m_hExpandedGroup=NULL;
CalculateBoundRects();
RedrawWindow();
}
else
{
// searching for next group to expand
if(nOrder==nGroupCount)
{
nOrder=0;
}
else if(nOrder>nGroupCount)
{
ASSERT(FALSE);
}
HSHBGROUP hGroup=FindGroupByOrder(nOrder);
ASSERT(hGroup);
ExpandGroup(hGroup);
}
}
else
{
// reposition groups and redraw the control
CalculateBoundRects();
RedrawBar();
}
// delete list control associated with deleted group
COXSHBListCtrl* pListCtrl=NULL;
if(m_mapHandleToListCtrl.Lookup(hGroup,pListCtrl) && pListCtrl)
{
delete pListCtrl;
m_mapHandleToListCtrl.RemoveKey(hGroup);
}
return TRUE;
}
BOOL COXShortcutBar::DeleteAllGroups()
{
ASSERT(::IsWindow(GetSafeHwnd()));
// delete all groups in cycle calling DeleteGroup function
BOOL bResult=TRUE;
HSHBGROUP hGroup=NULL;
LPSHB_GROUPINFO pGroupInfo;
POSITION pos=m_mapHandleToInfo.GetStartPosition();
while(pos!=NULL)
{
m_mapHandleToInfo.GetNextAssoc(pos,hGroup,pGroupInfo);
ASSERT(hGroup!=NULL);
ASSERT(pGroupInfo!=NULL);
bResult=DeleteGroup(hGroup) ? bResult : FALSE;
}
VERIFY(m_mapHandleToInfo.GetCount()==0);
return bResult;
}
HSHBGROUP COXShortcutBar::ExpandGroup(HSHBGROUP hGroup)
{
ASSERT(::IsWindow(GetSafeHwnd()));
ASSERT(hGroup);
// reset autoexpand timer
if(m_nWaitForAutoExpandID!=0)
{
KillTimer(m_nWaitForAutoExpandID);
m_nWaitForAutoExpandID=0;
}
m_hSuspectAutoExpandGroup=NULL;
if(hGroup==m_hExpandedGroup)
{
return hGroup;
}
// fill structure for notification
NMSHORTCUTBAR nmshb;
nmshb.hdr.code=SHBN_GROUPEXPANDING;
nmshb.hGroup=hGroup;
if(SendSHBNotification(&nmshb))
{
return m_hExpandedGroup;
}
HSHBGROUP hOldExpandedGroup=m_hExpandedGroup;
m_hExpandedGroup=hGroup;
COXShortcutBar * me = const_cast<COXShortcutBar *>(this);
COXShortcutBarSkin * skin = me->GetShortcutBarSkin();
bool bExpandCells = (skin->AnimateSelection());
// animation effect
if(bExpandCells && (GetBarStyle()&SHBS_ANIMATEEXPAND)!=0 && hOldExpandedGroup!=NULL &&
m_hExpandedGroup!=NULL && !GetChildWndRect().IsRectEmpty())
{
int nOrder=GetGroupOrder(m_hExpandedGroup);
// nOldOrder can be -1 in the case the old expanded group was just deleted
int nOldOrder=GetGroupOrder(hOldExpandedGroup);
if(nOldOrder!=-1)
{
// get the direction of animated group expanding (up or down)
BOOL bMovingUp=(nOldOrder<nOrder);
// get child window handle
HWND hOldWnd=GetGroupChildWndHandle(hOldExpandedGroup);
ASSERT(hOldWnd);
ASSERT(::IsWindow(hOldWnd));
// get child window rectangle
CRect rectOldChild=GetChildWndRect();
CRect rectToRedraw;
// animation group expanding step size (in pixels)
int nOffset=DFLT_ANIMATIONOFFSET;
// the number of steps we need to take to fully animate group expanding
int nSteps=rectOldChild.Height()/nOffset;
for(int nIndex=0; nIndex<nSteps-1; nIndex++)
{
// speed up expanding after we made it half way
if(nIndex>nSteps/2)
{
nIndex++;
nOffset=2*DFLT_ANIMATIONOFFSET;
}
// recalculate headers rectangles
rectToRedraw.left=0xffff;
rectToRedraw.top=0xffff;
rectToRedraw.right=0;
rectToRedraw.bottom=0;
for(int nGroupOrder=__min(nOldOrder,nOrder)+1;
nGroupOrder<=__max(nOldOrder,nOrder); nGroupOrder++)
{
HSHBGROUP hGroup=FindGroupByOrder(nGroupOrder);
ASSERT(hGroup);
CRect rectHeader;
VERIFY(GetGroupHeaderRect(hGroup,rectHeader));
rectToRedraw.left=__min(rectToRedraw.left,rectHeader.left);
rectToRedraw.top=__min(rectToRedraw.top,rectHeader.top);
rectToRedraw.right=__max(rectToRedraw.right,rectHeader.right);
rectToRedraw.bottom=__max(rectToRedraw.bottom,rectHeader.bottom);
rectHeader.OffsetRect(0,(bMovingUp) ? -nOffset : nOffset);
rectToRedraw.top=__min(rectToRedraw.top,rectHeader.top);
rectToRedraw.bottom=__max(rectToRedraw.bottom,rectHeader.bottom);
m_mapHandleToBoundRect.SetAt(hGroup,rectHeader);
}
// recalculate child window coordinates
if(bMovingUp)
{
rectOldChild.bottom-=nOffset;
}
else
{
rectOldChild.top+=nOffset;
}
// set expanded child window rectangle to updated one
m_rectChildWnd=rectOldChild;
// resize child window
::MoveWindow(hOldWnd,rectOldChild.left,rectOldChild.top,
rectOldChild.Width(),rectOldChild.Height(),FALSE);
// redraw shortcut bar
RedrawWindow(rectToRedraw);
::Sleep(DFLT_ANIMATIONDELAY/nSteps);
}
}
}
// reposition groups
CalculateBoundRects();
// hide child window of previous expanded group
HideChildWnd(hOldExpandedGroup);
RedrawWindow();
// show child window of newly appointed expanded group
ShowChildWnd(m_hExpandedGroup);
// notify parent
// fill structure for notification
nmshb.hdr.code=SHBN_GROUPEXPANDED;
nmshb.hGroup=hGroup;
SendSHBNotification(&nmshb);
return hOldExpandedGroup;
}
HSHBGROUP COXShortcutBar::SetDropTargetGroup(HSHBGROUP hGroup)
{
ASSERT(::IsWindow(GetSafeHwnd()));
HSHBGROUP hOldDropHighlightGroup=m_hDropHilightGroup;
m_hDropHilightGroup=hGroup;
return hOldDropHighlightGroup;
}
HSHBGROUP COXShortcutBar::SetDragGroup(HSHBGROUP hGroup)
{
ASSERT(::IsWindow(GetSafeHwnd()));
if(m_hDragGroup==hGroup)
return m_hDragGroup;
HSHBGROUP hOldDragGroup=m_hDragGroup;
// fill structure for notification
NMSHORTCUTBAR nmshb;
nmshb.hdr.code=SHBN_BEGINDRAGHEADER;
nmshb.hGroup=hGroup;
nmshb.lParam=(LPARAM)&m_ptDragImage;
SendSHBNotification(&nmshb);
m_hDragGroup=hGroup;
return hOldDragGroup;
}
COXSHBEdit* COXShortcutBar::EditGroupHeader(HSHBGROUP hGroup)
{
ASSERT(::IsWindow(GetSafeHwnd()));
ASSERT(hGroup);
ASSERT(::IsWindow(m_edit.GetSafeHwnd()));
// check if we can edit headers
if((GetBarStyle()&SHBS_EDITHEADERS)==0)
{
TRACE(_T("COXShortcutBar::EditGroupHeader: cannot edit header - SHBS_EDITHEADERS style wasn't set"));
return NULL;
}
// stop editing any group if any is undergoing
if(m_hEditGroup!=NULL)
{
m_edit.FinishEdit(TRUE);
m_hEditGroup=NULL;
}
// get text
CString sText=GetGroupText(hGroup);
// fill structure for notification
NMSHORTCUTBAR nmshb;
nmshb.hdr.code=SHBN_BEGINHEADEREDIT;
nmshb.hGroup=hGroup;
nmshb.lParam=(LPARAM)((LPCTSTR)sText);
if(SendSHBNotification(&nmshb))
{
return NULL;
}
CRect rectClient;
GetClientRect(rectClient);
CRect rectHeader;
if(!GetGroupHeaderRect(hGroup,rectHeader))
{
TRACE(_T("COXShortcutBar::EditGroupHeader: failed to get header rectangle"));
return NULL;
}
if(rectHeader.IsRectEmpty())
{
TRACE(_T("COXShortcutBar::EditGroupHeader: header rect is empty"));
return NULL;
}
CRect rectIntersect;
rectIntersect.IntersectRect(rectClient,rectHeader);
if(rectIntersect!=rectHeader)
{
TRACE(_T("COXShortcutBar::EditGroupHeader: failed to edit - header rectangle is not entirely visible"));
return NULL;
}
// get font to be used in COXSHBEdit control
LPLOGFONT pLF=GetGroupHeaderTextFont(hGroup);
ASSERT(pLF);
CFont font;
VERIFY(font.CreateFontIndirect(pLF));
// fill SHBE_DISPLAY structure to specify COXSHBEdit control options
SHBE_DISPLAY shbed;
shbed.nMask=SHBEIF_TEXT|SHBEIF_RECTDISPLAY|SHBEIF_STYLE|
SHBEIF_SELRANGE|SHBEIF_FONT;
shbed.lpText=sText;
shbed.rectDisplay=rectHeader;
shbed.dwStyle=WS_BORDER|ES_AUTOHSCROLL|ES_LEFT;
shbed.ptSelRange=CPoint(0,-1);
shbed.pFont=&font;
if(!m_edit.StartEdit(&shbed))
{
TRACE(_T("COXShortcutBar::EditGroupHeader: COXSHBEdit::StartEdit failed"));
return NULL;
}
m_hEditGroup=hGroup;
return &m_edit;
}
HSHBGROUP COXShortcutBar::HitTest(CPoint pt, UINT* pFlags) const
{
ASSERT(::IsWindow(GetSafeHwnd()));
*pFlags=SHB_NOWHERE;
HSHBGROUP hGroup=NULL;
// first of all check if point is within child window rect
if(!m_rectChildWnd.IsRectEmpty() && m_rectChildWnd.PtInRect(pt))
{
hGroup=GetExpandedGroup();
ASSERT(hGroup);
*pFlags=SHB_ONCHILD;
}
else
{
// check if point is on some of headers
CRect rect;
POSITION pos=m_mapHandleToBoundRect.GetStartPosition();
while(pos!=NULL)
{
m_mapHandleToBoundRect.GetNextAssoc(pos,hGroup,rect);
ASSERT(hGroup!=NULL);
if(rect.PtInRect(pt))
{
*pFlags=SHB_ONHEADER;
break;
}
else
{
hGroup=NULL;
}
}
}
return hGroup;
}
CImageList* COXShortcutBar::CreateGroupDragImage(HSHBGROUP hGroup)
{
ASSERT(::IsWindow(GetSafeHwnd()));
ASSERT(hGroup);
m_bCreatingDragImage=TRUE;
// IMPORTANT //
// caller of this function have to eventually destroy the image list
//
CImageList* m_pDragImageList=new CImageList;
CRect rectHeader;
VERIFY(GetGroupHeaderRect(hGroup,&rectHeader));
m_pDragImageList->Create(rectHeader.Width(),rectHeader.Height(),ILC_COLOR,0,1);
CClientDC dcClient(this);
CDC dc;
VERIFY(dc.CreateCompatibleDC(&dcClient));
CBitmap bitmap;
VERIFY(bitmap.CreateCompatibleBitmap(&dcClient,rectHeader.Width(),
rectHeader.Height()));
CBitmap* pOldBitmap=dc.SelectObject(&bitmap);
rectHeader-=rectHeader.TopLeft();
// use header drawing routine to get drag image
//
DRAWITEMSTRUCT dis;
dis.CtlType=0;
dis.CtlID=GetDlgCtrlID();
dis.itemID=(UINT)PtrToUint(hGroup);
dis.itemAction=ODA_DRAWENTIRE;
dis.itemState=ODS_DEFAULT;
dis.hwndItem=GetSafeHwnd();
dis.hDC=dc;
dis.rcItem=rectHeader;
dis.itemData=(DWORD)PtrToUlong(hGroup);
DrawHeader(&dis);
if(pOldBitmap)
dc.SelectObject(pOldBitmap);
VERIFY(m_pDragImageList->Add(&bitmap,(CBitmap*)NULL)!=-1);
m_bCreatingDragImage=FALSE;
return m_pDragImageList;
}
BOOL COXShortcutBar::SortGroups(int nSortOrder)
{
// use CompareHeaders function to sort groups in alphabetical order
return SortGroupsCB(CompareHeaders,(LPARAM)this,nSortOrder);
}
BOOL COXShortcutBar::SortGroupsCB(PFNSHBCOMPARE lpfnCompare,
LPARAM lParamSort, int nSortOrder)
{
ASSERT(::IsWindow(GetSafeHwnd()));
ASSERT(lpfnCompare);
ASSERT(nSortOrder==0 || nSortOrder==1 || nSortOrder==-1);
m_nSortOrder=nSortOrder;
int nCount=PtrToInt(GetGroupCount());
if(GetGroupCount()<2 || nSortOrder==0)
{
return TRUE;
}
HSHBGROUP hGroup1;
HSHBGROUP hGroup2;
BOOL bNotDone=TRUE;
while(bNotDone)
{
bNotDone=FALSE;
// loop through all groups
for(int nIndex=0; nIndex<nCount-1; nIndex++)
{
hGroup1=FindGroupByOrder(nIndex);
hGroup2=FindGroupByOrder(nIndex+1);
ASSERT(hGroup1!=NULL && hGroup2!=NULL);
int nResult=nSortOrder*
lpfnCompare((LPARAM)hGroup1,(LPARAM)hGroup2,lParamSort);
if(nResult>0)
{
// actually we swap items in this function
SetGroupOrder(hGroup1,nIndex+1);
bNotDone=TRUE;
}
}
}
return TRUE;
}
// functions to work with group list controls (defined only if programmer didn't set
// hwndChild member of SHB_GROUPINFO structure)
//
CImageList* COXShortcutBar::SetLCImageList(HSHBGROUP hGroup,
CImageList* pImageList,
int nImageList)
{
ASSERT(::IsWindow(GetSafeHwnd()));
ASSERT(hGroup);
COXSHBListCtrl* pListCtrl=GetGroupListCtrl(hGroup);
if(pListCtrl==NULL)
{
TRACE(_T("COXShortcutBar::SetLCImageList: list control wasn't created"));
return NULL;
}
ASSERT(GetGroupChildWnd(hGroup)==NULL);
ASSERT_VALID(pListCtrl);
return pListCtrl->SetImageList(pImageList,nImageList);
}
CImageList* COXShortcutBar::GetLCImageList(HSHBGROUP hGroup, int nImageList) const
{
ASSERT(::IsWindow(GetSafeHwnd()));
ASSERT(hGroup);
COXSHBListCtrl* pListCtrl=GetGroupListCtrl(hGroup);
if(pListCtrl==NULL)
{
TRACE(_T("COXShortcutBar::GetLCImageList: list control wasn't created"));
return NULL;
}
ASSERT(GetGroupChildWnd(hGroup)==NULL);
ASSERT_VALID(pListCtrl);
return pListCtrl->GetImageList(nImageList);
}
BOOL COXShortcutBar::GetLCItem(HSHBGROUP hGroup, LV_ITEM* pItem) const
{
ASSERT(::IsWindow(GetSafeHwnd()));
ASSERT(hGroup);
ASSERT(pItem);
COXSHBListCtrl* pListCtrl=GetGroupListCtrl(hGroup);
if(pListCtrl==NULL)
{
TRACE(_T("COXShortcutBar::GetLCItem: list control wasn't created"));
return FALSE;
}
ASSERT(GetGroupChildWnd(hGroup)==NULL);
ASSERT_VALID(pListCtrl);
return pListCtrl->GetItem(pItem);
}
int COXShortcutBar::GetLCItemCount(HSHBGROUP hGroup) const
{
ASSERT(::IsWindow(GetSafeHwnd()));
ASSERT(hGroup);
COXSHBListCtrl* pListCtrl=GetGroupListCtrl(hGroup);
if(pListCtrl==NULL)
{
TRACE(_T("COXShortcutBar::GetLCItemCount: list control wasn't created"));
return FALSE;
}
ASSERT(GetGroupChildWnd(hGroup)==NULL);
ASSERT_VALID(pListCtrl);
return pListCtrl->GetItemCount();
}
BOOL COXShortcutBar::SetLCItem(HSHBGROUP hGroup, const LV_ITEM* pItem)
{
ASSERT(::IsWindow(GetSafeHwnd()));
ASSERT(hGroup);
ASSERT(pItem);
COXSHBListCtrl* pListCtrl=GetGroupListCtrl(hGroup);
if(pListCtrl==NULL)
{
TRACE(_T("COXShortcutBar::SetLCItem: list control wasn't created"));
return FALSE;
}
ASSERT(GetGroupChildWnd(hGroup)==NULL);
ASSERT_VALID(pListCtrl);
return pListCtrl->SetItem(pItem);
}
BOOL COXShortcutBar::SetLCItem(HSHBGROUP hGroup, int nItem, int nSubItem,
UINT nMask, LPCTSTR lpszItem, int nImage,
UINT nState, UINT nStateMask, LPARAM lParam)
{
ASSERT(::IsWindow(GetSafeHwnd()));
ASSERT(hGroup);
COXSHBListCtrl* pListCtrl=GetGroupListCtrl(hGroup);
if(pListCtrl==NULL)
{
TRACE(_T("COXShortcutBar::SetLCItem: list control wasn't created"));
return FALSE;
}
ASSERT(GetGroupChildWnd(hGroup)==NULL);
ASSERT_VALID(pListCtrl);
return pListCtrl->SetItem(nItem,nSubItem,nMask,lpszItem,nImage,
nState,nStateMask,lParam);
}
int COXShortcutBar::InsertLCItem(HSHBGROUP hGroup, const LV_ITEM* pItem)
{
ASSERT(::IsWindow(GetSafeHwnd()));
ASSERT(hGroup);
ASSERT(pItem);
COXSHBListCtrl* pListCtrl=GetGroupListCtrl(hGroup);
if(pListCtrl==NULL)
{
TRACE(_T("COXShortcutBar::InsertLCItem: list control wasn't created"));
return -1;
}
ASSERT(GetGroupChildWnd(hGroup)==NULL);
ASSERT_VALID(pListCtrl);
return pListCtrl->InsertItem(pItem);
}
int COXShortcutBar::InsertLCItem(HSHBGROUP hGroup, UINT nMask, int nItem,
LPCTSTR lpszItem, UINT nState, UINT nStateMask,
int nImage, LPARAM lParam)
{
ASSERT(::IsWindow(GetSafeHwnd()));
ASSERT(hGroup);
COXSHBListCtrl* pListCtrl=GetGroupListCtrl(hGroup);
if(pListCtrl==NULL)
{
TRACE(_T("COXShortcutBar::InsertLCItem: list control wasn't created"));
return -1;
}
ASSERT(GetGroupChildWnd(hGroup)==NULL);
ASSERT_VALID(pListCtrl);
return pListCtrl->InsertItem(
nMask,nItem,lpszItem,nState,nStateMask,nImage,lParam);
}
BOOL COXShortcutBar::DeleteLCItem(HSHBGROUP hGroup, int nItem)
{
ASSERT(::IsWindow(GetSafeHwnd()));
ASSERT(hGroup);
COXSHBListCtrl* pListCtrl=GetGroupListCtrl(hGroup);
if(pListCtrl==NULL)
{
TRACE(_T("COXShortcutBar::InsertLCItem: list control wasn't created"));
return FALSE;
}
ASSERT(GetGroupChildWnd(hGroup)==NULL);
ASSERT_VALID(pListCtrl);
ASSERT(nItem>=0 && nItem<pListCtrl->GetItemCount());
return pListCtrl->DeleteItem(nItem);
}
COXSHBEdit* COXShortcutBar::EditLCItem(HSHBGROUP hGroup, int nItem)
{
ASSERT(::IsWindow(GetSafeHwnd()));
ASSERT(hGroup);
// check if we are allowed to edit items
if((GetBarStyle()&SHBS_EDITITEMS)==0)
{
TRACE(_T("COXShortcutBar::EditLCItem: cannot edit item - SHBS_EDITITEMS style wasn't set"));
return NULL;
}
COXSHBListCtrl* pListCtrl=GetGroupListCtrl(hGroup);
if(pListCtrl==NULL)
{
TRACE(_T("COXShortcutBar::EditLCItem: list control wasn't created"));
return NULL;
}
ASSERT(GetGroupChildWnd(hGroup)==NULL);
ASSERT_VALID(pListCtrl);
ASSERT(nItem>=0 && nItem<pListCtrl->GetItemCount());
return pListCtrl->EditLabel(nItem);
}
BOOL COXShortcutBar::ScrollExpandedLC(CSize size)
{
ASSERT(::IsWindow(GetSafeHwnd()));
// get expanded group
HSHBGROUP hGroup=GetExpandedGroup();
if(hGroup==NULL)
return FALSE;
HWND hWnd=GetGroupChildWnd(hGroup);
if(hWnd==NULL)
{
COXSHBListCtrl* pListCtrl=GetGroupListCtrl(hGroup);
ASSERT(pListCtrl);
return pListCtrl->Scroll(size);
}
TRACE(_T("COXShortcutBar::ScrollExpanded: child window is not COXSHBListCtrl\n"));
return FALSE;
}
int COXShortcutBar::SortLCItems(HSHBGROUP hGroup)
{
ASSERT(::IsWindow(GetSafeHwnd()));
ASSERT(hGroup);
COXSHBListCtrl* pListCtrl=GetGroupListCtrl(hGroup);
if(pListCtrl==NULL)
{
TRACE(_T("COXShortcutBar::SortLCItems: list control wasn't created\n"));
return -1;
}
ASSERT(GetGroupChildWnd(hGroup)==NULL);
ASSERT_VALID(pListCtrl);
// Sort all items in ascending aphabetical order using an STL set
typedef std::set<LVITEM*, LVITEM_less> ItemSet;
ItemSet setItems;
int iCount = pListCtrl->GetItemCount();
for (int i = 0; i < iCount; i++)
{
LVITEM* pLVI = new LVITEM();
::memset(pLVI, 0, sizeof(LVITEM));
pLVI->iItem = i;
pLVI->mask = LVIF_IMAGE | LVIF_INDENT | LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
pLVI->pszText = new TCHAR[256];
pLVI->cchTextMax = 256;
pListCtrl->GetItem(pLVI);
setItems.insert(pLVI);
}
// Remove all items from the list control
pListCtrl->DeleteAllItems();
// Put the items back in the list control
int iIndex = 0;
for (ItemSet::iterator it = setItems.begin(); it != setItems.end(); ++it)
{
(*it)->iItem = iIndex++;
pListCtrl->InsertItem(*it);
delete [] (*it)->pszText;
delete *it;
}
return 0;
}
////////////////////////////////////////////////////////////////////
// Set of verification functions that check different parameters on consistent
////////////////////////////////////////////////////////////////////
BOOL COXShortcutBar::VerifyBarStyle(DWORD dwBarStyle) const
{
if(dwBarStyle & ~(SHBS_EDITHEADERS|SHBS_EDITITEMS|SHBS_INFOTIP|
SHBS_DISABLEDRAGDROPITEM|SHBS_NOSCROLL|SHBS_BOLDEXPANDEDGROUP|
SHBS_UNDERLINEHOTITEM|SHBS_SHOWACTIVEALWAYS|
SHBS_DISABLEDRAGDROPHEADER|SHBS_DRAWHEADERDRAGIMAGE|
SHBS_DRAWITEMDRAGIMAGE|SHBS_AUTOSCROLL|
SHBS_ANIMATEEXPAND|SHBS_AUTOEXPAND|SHBS_FLATGROUPBUTTON))
{
TRACE(_T("COXShortcutBar::VerifyBarStyle: unspecified Shortcut Bar style was used\n"));
return FALSE;
}
return TRUE;
}
BOOL COXShortcutBar::VerifyWindowStyle(DWORD dwStyle) const
{
if((dwStyle & WS_CHILD)==0)
{
TRACE(_T("COXShortcutBar::VerifyWindowStyle: WS_CHILD style must be specified\n"));
return FALSE;
}
return TRUE;
}
BOOL COXShortcutBar::VerifyImage(int nImage) const
{
if(!m_pImageList)
{
TRACE(_T("COXShortcutBar::VerifyImage: image list wasn't set"));
return FALSE;
}
// image index has to be in the range of possible values
if(nImage<0 || nImage>=m_pImageList->GetImageCount())
{
TRACE(_T("COXShortcutBar::VerifyImage: image number %d is out of range: 0 ... %d\n"),
nImage,m_pImageList->GetImageCount());
return FALSE;
}
return TRUE;
}
BOOL COXShortcutBar::VerifyView(int nView) const
{
// only two types of view at the moment available
if(nView&~(SHB_LARGEICON|SHB_SMALLICON))
{
TRACE(_T("COXShortcutBar::VerifyView: unknown view specified"));
return FALSE;
}
return TRUE;
}
BOOL COXShortcutBar::VerifyOrder(int nOrder) const
{
if((nOrder!=SHBI_FIRST && nOrder!=SHBI_LAST && nOrder!=SHBI_SORT) &&
(nOrder<0 || nOrder>=GetGroupCount()))
{
TRACE(_T("COXShortcutBar::VerifyOrder: order number %d is out of range: 0 ... %d\n"),
nOrder,GetGroupCount());
return FALSE;
}
return TRUE;
}
BOOL COXShortcutBar::VerifyDescriptor(LPSHB_DESCRIPTOR pDescriptor) const
{
UNREFERENCED_PARAMETER(pDescriptor);
ASSERT(pDescriptor);
return TRUE;
}
BOOL COXShortcutBar::VerifyChildWnd(HWND hwndChild) const
{
if(!::IsWindow(hwndChild))
{
TRACE(_T("COXShortcutBar::VerifyChildWnd: window wasn't created\n"));
return FALSE;
}
DWORD dwStyle=::GetWindowLongPtr(hwndChild,GWL_STYLE);
if((dwStyle&WS_CHILD)==0)
{
TRACE(_T("COXShortcutBar::VerifyChildWnd: WS_CHILD style must be specified\n"));
return FALSE;
}
HWND hwndParent=::GetParent(hwndChild);
if(hwndParent!=GetSafeHwnd())
{
TRACE(_T("COXShortcutBar::VerifyChildWnd: child window parent window is not <this>\n"));
return FALSE;
}
return TRUE;
}
BOOL COXShortcutBar::VerifyMask(UINT nMask) const
{
if(nMask&~(SHBIF_TEXT|SHBIF_IMAGE|SHBIF_IMAGEEXPANDED|SHBIF_PARAM|
SHBIF_ORDER|SHBIF_VIEW|SHBIF_DESCRIPTOR|SHBIF_CHILDWND))
{
TRACE(_T("COXShortcutBar::VerifyMask: unknown mask specified"));
return FALSE;
}
return TRUE;
}
BOOL COXShortcutBar::VerifyGroupInfo(LPSHB_GROUPINFO pGroupInfo) const
{
if(!VerifyMask(pGroupInfo->nMask))
return FALSE;
if(pGroupInfo->nMask&SHBIF_IMAGE && !VerifyImage(pGroupInfo->nImage))
return FALSE;
if(pGroupInfo->nMask&SHBIF_IMAGEEXPANDED &&
!VerifyImage(pGroupInfo->nImageExpanded))
return FALSE;
if(pGroupInfo->nMask&SHBIF_ORDER && !VerifyOrder(pGroupInfo->nOrder))
return FALSE;
if(pGroupInfo->nMask&SHBIF_VIEW && !VerifyView(pGroupInfo->nView))
return FALSE;
if(pGroupInfo->nMask&SHBIF_DESCRIPTOR &&
!VerifyDescriptor(pGroupInfo->pDescriptor))
return FALSE;
if(pGroupInfo->nMask&SHBIF_CHILDWND && !VerifyChildWnd(pGroupInfo->hwndChild))
return FALSE;
return TRUE;
}
HSHBGROUP COXShortcutBar::GetUniqueGroupHandle()
{
// we just keep track of all created groups and just increment the number
// of created groups to get a unique handle
m_nLastHandle++;
return (HSHBGROUP)m_nLastHandle;
}
BOOL COXShortcutBar::CopyGroupInfo(LPSHB_GROUPINFO pDest,
const LPSHB_GROUPINFO pSource,
int nCopyFlag/* = SHB_CPYINFO_SET*/)
{
ASSERT(pDest);
ASSERT(pSource);
// only these flags can be set
ASSERT((nCopyFlag&~(SHB_CPYINFO_COPY|SHB_CPYINFO_GET|SHB_CPYINFO_SET))==0);
// these two flags cannot be set simultenuosly
ASSERT((nCopyFlag&SHB_CPYINFO_SET)==0 || (nCopyFlag&SHB_CPYINFO_GET)==0);
// verify dest if we are going to get info from source
if((nCopyFlag&SHB_CPYINFO_GET)!=0)
VERIFY(VerifyGroupInfo(pDest));
// check if we copy source to dest with removing all existing dest contents
if((nCopyFlag&SHB_CPYINFO_COPY)!=0)
{
// delete text if it was dynamically allocated
if(pDest->IsDynCreated() && pDest->pszText!=NULL)
{
delete[] pDest->pszText;
pDest->pszText=NULL;
pDest->DynCreated(FALSE);
}
ZeroMemory(pDest,sizeof(SHB_GROUPINFO));
}
// define the group which mask will be used to copy information
LPSHB_GROUPINFO pMainGroup=(((nCopyFlag&SHB_CPYINFO_SET)!=0 ||
(nCopyFlag&SHB_CPYINFO_COPY)!=0) ? pSource : pDest);
// define whether we are going to get or set information
BOOL bGetInfo=((nCopyFlag&SHB_CPYINFO_GET)!=0 ? TRUE : FALSE);
// set header text
if(pMainGroup->nMask&SHBIF_TEXT)
{
// delete any text that was previously dynamically allocated
if(pDest->IsDynCreated() && pDest->pszText!=NULL)
{
delete[] pDest->pszText;
pDest->pszText=NULL;
pDest->DynCreated(FALSE);
}
// copy text
if(pSource->pszText!=NULL && pSource->nTextMax>0)
{
try
{
pDest->pszText=new TCHAR[pSource->nTextMax + 1];
}
catch (CMemoryException* pException)
{
UNREFERENCED_PARAMETER(pException);
TRACE(_T("COXShortcutBar::CopyGroupInfo: failed to allocate memory for text"));
pDest->pszText=NULL;
return FALSE;
}
ASSERT(pDest->pszText);
pDest->DynCreated();
UTBStr::tcsncpy(pDest->pszText, pSource->nTextMax + 1, pSource->pszText, pSource->nTextMax);
pDest->nTextMax=pSource->nTextMax;
}
pDest->nMask|=SHBIF_TEXT;
}
// set header image
if(pMainGroup->nMask&SHBIF_IMAGE)
{
if(pSource->nImage>=0 && !VerifyImage(pSource->nImage))
return FALSE;
// check if property is to set/remove
if(pSource->nImage<0)
{
pDest->nImage=-1;
pDest->nMask&=~SHBIF_IMAGE;
}
else
{
pDest->nImage=pSource->nImage;
pDest->nMask|=SHBIF_IMAGE;
}
}
// set header expanded image
if(pMainGroup->nMask&SHBIF_IMAGEEXPANDED)
{
if(pSource->nImageExpanded>=0 && !VerifyImage(pSource->nImageExpanded))
return FALSE;
// check if property is to set/remove
if(pSource->nImageExpanded<0)
{
pDest->nImageExpanded=-1;
pDest->nMask&=~SHBIF_IMAGEEXPANDED;
}
else
{
pDest->nImageExpanded=pSource->nImageExpanded;
pDest->nMask|=SHBIF_IMAGEEXPANDED;
}
}
// set user specific data
if(pMainGroup->nMask&SHBIF_PARAM)
{
pDest->lParam=pSource->lParam;
pDest->nMask|=SHBIF_PARAM;
}
// set order
if(pMainGroup->nMask&SHBIF_ORDER)
{
if(!VerifyOrder(pSource->nOrder))
return FALSE;
// reorder groups if infromation is to be set
if(!bGetInfo)
{
int nOldOrder=pDest->nOrder;
if(nOldOrder!=pSource->nOrder)
{
HSHBGROUP hSwapGroup=FindGroupByOrder(pSource->nOrder);
ASSERT(hSwapGroup!=NULL);
LPSHB_GROUPINFO pSwapSavedGroupInfo;
VERIFY(m_mapHandleToInfo.Lookup(hSwapGroup,pSwapSavedGroupInfo));
ASSERT(pSwapSavedGroupInfo!=NULL);
pSwapSavedGroupInfo->nMask|=SHBIF_ORDER;
pSwapSavedGroupInfo->nOrder=nOldOrder;
}
}
pDest->nOrder=pSource->nOrder;
pDest->nMask|=SHBIF_ORDER;
}
// set view
if(pMainGroup->nMask&SHBIF_VIEW )
{
if(!VerifyView(pSource->nView))
return FALSE;
pDest->nView=pSource->nView;
pDest->nMask|=SHBIF_VIEW;
}
// child window
if(pMainGroup->nMask&SHBIF_CHILDWND)
{
// check if property is to set/remove
if(pSource->hwndChild==NULL)
{
pDest->nMask&=~SHBIF_CHILDWND;
pDest->hwndChild=pSource->hwndChild;
}
else
{
// verify child window
if(!VerifyChildWnd(pSource->hwndChild))
return FALSE;
pDest->nMask|=SHBIF_CHILDWND;
pDest->hwndChild=pSource->hwndChild;
}
}
// set descriptor
if(pMainGroup->nMask&SHBIF_DESCRIPTOR)
{
// check if property is to set/remove
if(pSource->pDescriptor!=NULL)
{
int nDescriptorCopyFlag=nCopyFlag&~SHB_CPYINFO_COPY;
if(pDest->pDescriptor==NULL)
{
// create descriptor if haven't been created yet
pDest->pDescriptor=AddDescriptor();
nDescriptorCopyFlag|=SHB_CPYINFO_COPY;
}
ASSERT(pDest->pDescriptor);
// copy sorce descriptor to dest decriptor
if(!CopyDescriptor(pDest->pDescriptor,pSource->pDescriptor,
nDescriptorCopyFlag))
return FALSE;
pDest->nMask|=SHBIF_DESCRIPTOR;
}
else
{
// remove descriptor
if(pDest->pDescriptor!=NULL)
{
// try to find group by descriptor
SHB_GROUPINFO shbGroup;
shbGroup.nMask=SHBIF_DESCRIPTOR;
shbGroup.pDescriptor=pDest->pDescriptor;
HSHBGROUP hFindGroup=FindGroup(&shbGroup);
// if find any
if(hFindGroup==NULL)
{
// v9.3 - update 03 - 64-bit - switch param decl - TD
#ifdef _WIN64
INT_PTR nOrder;
#else
int nOrder;
#endif
UNREFERENCED_PARAMETER(nOrder);
// double check that descriptor was created using AddDescriptor
ASSERT(m_mapDescriptors.Lookup(pDest->pDescriptor,nOrder));
delete pDest->pDescriptor;
m_mapDescriptors.RemoveKey(pDest->pDescriptor);
}
}
pDest->pDescriptor=NULL;
pDest->nMask&=~SHBIF_DESCRIPTOR;
}
}
return TRUE;
}
BOOL COXShortcutBar::CopyDescriptor(LPSHB_DESCRIPTOR pDest,
const LPSHB_DESCRIPTOR pSource,
int nCopyFlag/* = SHB_CPYINFO_SET*/) const
{
ASSERT(pDest);
ASSERT(pSource);
// only these flags can be set
ASSERT((nCopyFlag&~(SHB_CPYINFO_COPY|SHB_CPYINFO_GET|SHB_CPYINFO_SET))==0);
// these two flags cannot be set simultenuosly
ASSERT((nCopyFlag&SHB_CPYINFO_SET)==0 || (nCopyFlag&SHB_CPYINFO_GET)==0);
// verify source and if needed dest descriptors
VERIFY(VerifyDescriptor(pSource));
if((nCopyFlag&SHB_CPYINFO_GET)!=0)
VERIFY(VerifyDescriptor(pDest));
// clean up the dest if SHB_CPYINFO_COPY flag is set
if((nCopyFlag&SHB_CPYINFO_COPY)!=0)
ZeroMemory(pDest,sizeof(SHB_DESCRIPTOR));
// define descriptor which mask will be used to copy information
LPSHB_DESCRIPTOR pMainDescriptor=(((nCopyFlag&SHB_CPYINFO_SET)!=0 ||
(nCopyFlag&SHB_CPYINFO_COPY)!=0) ? pSource : pDest);
if(pMainDescriptor->nMask&SHBIF_CLRBACK)
{
pDest->clrBackground=pSource->clrBackground;
pDest->nMask|=SHBIF_CLRBACK;
}
if(pMainDescriptor->nMask&SHBIF_CLRTEXT)
{
pDest->clrText=pSource->clrText;
pDest->nMask|=SHBIF_CLRTEXT;
}
if(pMainDescriptor->nMask&SHBIF_HDRCLRBACK)
{
pDest->clrHeaderBackground=pSource->clrHeaderBackground;
pDest->nMask|=SHBIF_HDRCLRBACK;
}
if(pMainDescriptor->nMask&SHBIF_HDRCLRTEXT)
{
pDest->clrHeaderText=pSource->clrHeaderText;
pDest->nMask|=SHBIF_HDRCLRTEXT;
}
if(pMainDescriptor->nMask&SHBIF_HDRTEXTFMT)
{
pDest->nHeaderTextFormat=pSource->nHeaderTextFormat;
pDest->nMask|=SHBIF_HDRTEXTFMT;
}
if(pMainDescriptor->nMask&SHBIF_HDRHEIGHT)
{
pDest->nHeaderHeight=pSource->nHeaderHeight;
pDest->nMask|=SHBIF_HDRHEIGHT;
}
if(pMainDescriptor->nMask&SHBIF_FONT)
{
VERIFY(CopyLogFont(&pDest->lfText,&pSource->lfText));
pDest->nMask|=SHBIF_FONT;
}
if(pMainDescriptor->nMask&SHBIF_HDRFONT)
{
VERIFY(CopyLogFont(&pDest->lfHeader,&pSource->lfHeader));
pDest->nMask|=SHBIF_HDRFONT;
}
return TRUE;
}
BOOL COXShortcutBar::CopyLogFont(LPLOGFONT pDest, const LPLOGFONT pSource) const
{
ASSERT(pDest);
ASSERT(pSource);
// copy all elements of LOGFONT structure
pDest->lfHeight=pSource->lfHeight;
pDest->lfWidth=pSource->lfWidth;
pDest->lfEscapement=pSource->lfEscapement;
pDest->lfOrientation=pSource->lfOrientation;
pDest->lfWeight=pSource->lfWeight;
pDest->lfItalic=pSource->lfItalic;
pDest->lfUnderline=pSource->lfUnderline;
pDest->lfStrikeOut=pSource->lfStrikeOut;
pDest->lfCharSet=pSource->lfCharSet;
pDest->lfOutPrecision=pSource->lfOutPrecision;
pDest->lfClipPrecision=pSource->lfClipPrecision;
pDest->lfQuality=pSource->lfQuality;
pDest->lfPitchAndFamily=pSource->lfPitchAndFamily;
UTBStr::tcsncpy(pDest->lfFaceName, LF_FACESIZE, pSource->lfFaceName,LF_FACESIZE);
return TRUE;
}
BOOL COXShortcutBar::CreateEditControl()
{
ASSERT(::IsWindow(GetSafeHwnd()));
if(::IsWindow(m_edit.GetSafeHwnd()))
{
return TRUE;
}
// make sure it's invisible from the beginning
CRect rect(0,0,0,0);
BOOL bResult=m_edit.Create(WS_BORDER,rect,this,SHB_IDCEDIT);
return bResult;
}
BOOL COXShortcutBar::CreateListControl(HSHBGROUP hGroup)
{
ASSERT(::IsWindow(GetSafeHwnd()));
ASSERT(hGroup);
LPSHB_GROUPINFO pSavedGroupInfo;
// group has to be created at the moment
if(!m_mapHandleToInfo.Lookup(hGroup,pSavedGroupInfo))
return FALSE;
ASSERT(pSavedGroupInfo!=NULL);
// double check group info
ASSERT(VerifyGroupInfo(pSavedGroupInfo));
// if list control already exist then destroy it
COXSHBListCtrl* pListCtrl=NULL;
m_mapHandleToListCtrl.Lookup(hGroup,pListCtrl);
if(pListCtrl)
pListCtrl->DestroyWindow();
// default styles that are compatible with default shortcut bar styles
DWORD dwStyle=LVS_SINGLESEL|LVS_SHAREIMAGELISTS|LVS_NOSCROLL|LVS_ICON;
// create initially invisible COXSHBListCtrl
CRect rect(0,0,0,0);
if(pListCtrl->Create(dwStyle,rect,this,(UINT)PtrToUint(hGroup)))
{
// notify list control that we aknowledge it as child window
// and send as lParam the handle of the corresponding group
pListCtrl->SendMessage(SHBM_SETSHBGROUP,(WPARAM)0,(LPARAM)hGroup);
return TRUE;
}
else
return FALSE;
}
COXSHBListCtrl* COXShortcutBar::NewSHBListCtrl()
{
return (new COXSHBListCtrl());
}
void COXShortcutBar::UpdateHeaderOrder(int nFirst, int nLast, int nMargin)
{
// if nLast==-1 then we loop till the last group
if(nLast==-1)
nLast=PtrToInt(GetGroupCount()-1);
// scan through all groups and update order of them correspondingly
HSHBGROUP hGroup;
LPSHB_GROUPINFO pGroupInfo;
POSITION pos=m_mapHandleToInfo.GetStartPosition();
while(pos!=NULL)
{
m_mapHandleToInfo.GetNextAssoc(pos,hGroup,pGroupInfo);
ASSERT(hGroup!=NULL);
ASSERT(pGroupInfo!=NULL);
if(pGroupInfo->nOrder>=nFirst && pGroupInfo->nOrder<=nLast)
pGroupInfo->nOrder+=nMargin;
}
}
HSHBGROUP COXShortcutBar::FindGroup(const LPSHB_GROUPINFO pGroupInfo) const
{
ASSERT(::IsWindow(GetSafeHwnd()));
// scan through all groups and check if pGroupInfo is subset of one of them
HSHBGROUP hGroup;
LPSHB_GROUPINFO pSavedGroupInfo;
POSITION pos=m_mapHandleToInfo.GetStartPosition();
while(pos!=NULL)
{
m_mapHandleToInfo.GetNextAssoc(pos,hGroup,pSavedGroupInfo);
ASSERT(hGroup!=NULL);
ASSERT(pSavedGroupInfo!=NULL);
// check if pSavedGroupInfo contains pGroupInfo as subset
if(CompareGroups(pGroupInfo,pSavedGroupInfo))
return hGroup;
}
return NULL;
}
HSHBGROUP COXShortcutBar::FindGroupByOrder(int nOrder) const
{
SHB_GROUPINFO shbGroup;
shbGroup.nMask=SHBIF_ORDER;
shbGroup.nOrder=nOrder;
return FindGroup(&shbGroup);
}
HSHBGROUP COXShortcutBar::FindGroupByParam(LPARAM lParam) const
{
SHB_GROUPINFO shbGroup;
shbGroup.nMask=SHBIF_PARAM;
shbGroup.lParam=lParam;
return FindGroup(&shbGroup);
}
HSHBGROUP COXShortcutBar::FindGroupByTitle(LPCTSTR lpszTitle) const
{
SHB_GROUPINFO shbGroup;
shbGroup.pszText=(LPTSTR)lpszTitle;
shbGroup.nTextMax=lstrlen(lpszTitle);
shbGroup.nMask=SHBIF_TEXT;
return FindGroup(&shbGroup);
}
// returns TRUE if all of elements from pGroup are the same in pGroupCompareTo
BOOL COXShortcutBar::CompareGroups(const LPSHB_GROUPINFO pGroup,
const LPSHB_GROUPINFO pGroupCompareTo) const
{
ASSERT(pGroup);
ASSERT(pGroupCompareTo);
// verify both groups
if(!VerifyGroupInfo(pGroup) || !VerifyGroupInfo(pGroupCompareTo))
return FALSE;
// pGroup should be a subset of pGroupCompareTo
if((pGroup->nMask&~pGroupCompareTo->nMask)!=0)
return FALSE;
// text
if(pGroup->nMask&SHBIF_TEXT)
if(_tcsncmp(pGroup->pszText,pGroupCompareTo->pszText,pGroup->nTextMax)!=0)
return FALSE;
// image
if(pGroup->nMask&SHBIF_IMAGE)
if(pGroup->nImage!=pGroupCompareTo->nImage)
return FALSE;
// image expanded
if(pGroup->nMask&SHBIF_IMAGEEXPANDED)
if(pGroup->nImageExpanded!=pGroupCompareTo->nImageExpanded)
return FALSE;
// lParam
if(pGroup->nMask&SHBIF_PARAM)
if(pGroup->lParam!=pGroupCompareTo->lParam)
return FALSE;
// order
if(pGroup->nMask&SHBIF_ORDER)
if(pGroup->nOrder!=pGroupCompareTo->nOrder)
return FALSE;
// view
if(pGroup->nMask&SHBIF_VIEW)
if(pGroup->nView!=pGroupCompareTo->nView)
return FALSE;
// descriptor - we check only pointers to SHB_DESCRIPTOR structures if they are the same
if(pGroup->nMask&SHBIF_DESCRIPTOR)
if(pGroup->pDescriptor!=pGroupCompareTo->pDescriptor)
return FALSE;
// child window
if(pGroup->nMask&SHBIF_CHILDWND)
if(pGroup->hwndChild!=pGroupCompareTo->hwndChild)
return FALSE;
return TRUE;
}
void COXShortcutBar::CalculateBoundRects()
{
ASSERT(::IsWindow(GetSafeHwnd()));
// clean up map of all bound rectangles
m_rectChildWnd.SetRectEmpty();
m_mapHandleToBoundRect.RemoveAll();
CRect rect;
GetClientRect(rect);
int nBottom=rect.bottom;
int nCount=PtrToInt(GetGroupCount());
if(nCount==0)
return;
COXShortcutBar * me = const_cast<COXShortcutBar *>(this);
COXShortcutBarSkin * skin = me->GetShortcutBarSkin();
bool bExpandCells = (skin->AnimateSelection());
// loop through all groups by their order
CRect rectOld;
HSHBGROUP hGroup;
SHB_GROUPINFO shbGroup;
for(int nIndex=0; nIndex<nCount; nIndex++)
{
hGroup=FindGroupByOrder(nIndex);
VERIFY(hGroup!=NULL);
// double check that everything alright with group info integrity
ASSERT(GetGroupInfo(hGroup,&shbGroup,TRUE));
ASSERT(VerifyGroupInfo(&shbGroup));
// just add the height of the group
rect.bottom=rect.top+GetGroupHeaderHeight(hGroup);
m_mapHandleToBoundRect.SetAt(hGroup,rect);
// don't forget about margin between groups
rect.top=rect.bottom+m_nGroupMargin;
}
// if there is space left to display child window then try to define
// the coordinates of its rectangle
if(rect.bottom<nBottom || bExpandCells)
{
// we display only child window of expanded group
hGroup=GetExpandedGroup();
if(hGroup!=NULL)
{
CRect rectExpanded;
VERIFY(m_mapHandleToBoundRect.Lookup(hGroup,rectExpanded));
m_rectChildWnd=rectExpanded;
m_rectChildWnd.top= bExpandCells ? m_rectChildWnd.bottom : ( (skin->AnimateSelection()) ? 0 : DFLT_TOPHDRHEIGHT2003);
m_rectChildWnd.bottom+=nBottom-rect.bottom - (bExpandCells ? ( (skin->AnimateSelection()) ? 0 : DFLT_TOPHDRHEIGHT2003 ) : m_rectChildWnd.bottom);
// deflate by margins
m_rectChildWnd.DeflateRect(m_rectChildWndMargins);
if(m_rectChildWnd.top>=m_rectChildWnd.bottom ||
m_rectChildWnd.left>=m_rectChildWnd.right)
m_rectChildWnd.SetRectEmpty();
// update coordinates of header rectangles of all groups that are placed
// to the bottom of shortcut bar of expanded group
UpdateBoundRects((bExpandCells ? rectExpanded.bottom+m_nGroupMargin : 0),nBottom-rect.bottom);
}
}
}
void COXShortcutBar::UpdateBoundRects(int nStartFrom, int nMargin)
{
ASSERT(::IsWindow(GetSafeHwnd()));
VERIFY(nMargin>=0);
CPoint ptMargin(0,nMargin);
// check shortcut bar integrity
ASSERT(m_mapHandleToBoundRect.GetCount()==m_mapHandleToInfo.GetCount());
// scan through all groups
HSHBGROUP hGroup;
CRect rect;
POSITION pos=m_mapHandleToBoundRect.GetStartPosition();
while(pos!=NULL)
{
m_mapHandleToBoundRect.GetNextAssoc(pos,hGroup,rect);
ASSERT(hGroup!=NULL);
// update those header rectangles that are placed down to the bottom of
// nStartFrom coordinate
if(rect.top>=nStartFrom)
{
rect+=ptMargin;
m_mapHandleToBoundRect.SetAt(hGroup,rect);
}
}
}
void COXShortcutBar::HideChildWnd(HSHBGROUP hGroup)
{
ASSERT(::IsWindow(GetSafeHwnd()));
if(hGroup==NULL)
return;
// check if the group is already deleted
LPSHB_GROUPINFO lpshb;
if(!m_mapHandleToInfo.Lookup(hGroup,lpshb))
return;
// get handle of any child window associated with any group (whether it is
// COXSHBListCtrl window or application defined window)
HWND hWnd=GetGroupChildWndHandle(hGroup);
ASSERT(hWnd);
// just hide it
if(::IsWindow(hWnd))
::ShowWindow(hWnd,SW_HIDE);
}
void COXShortcutBar::ShowChildWnd(HSHBGROUP hGroup, BOOL bRedraw/* = TRUE*/)
{
ASSERT(::IsWindow(GetSafeHwnd()));
if(hGroup==NULL)
return;
// get handle of any child window associated with any group (whether it is
// COXSHBListCtrl window or application defined window)
HWND hWnd=GetGroupChildWndHandle(hGroup);
ASSERT(hWnd);
// window has to exist at the moment
ASSERT(::IsWindow(hWnd));
// check if there is any space where we can display child window
// of expanded group
if(!m_rectChildWnd.IsRectEmpty())
{
CRect rectWindow;
::GetWindowRect(hWnd,rectWindow);
// probably resize it
::MoveWindow(hWnd,m_rectChildWnd.left,m_rectChildWnd.top,
m_rectChildWnd.Width(),m_rectChildWnd.Height(),FALSE);
// redraw if specified
if(bRedraw)
::RedrawWindow(hWnd,NULL,NULL,RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE);
// and show it
::ShowWindow(hWnd,SW_SHOW);
}
else
// there is no space to display it
::ShowWindow(hWnd,SW_HIDE);
}
void COXShortcutBar::FillBackground(CDC* pDC)
{
ASSERT(::IsWindow(GetSafeHwnd()));
// get background color of expanded group
COLORREF clrBackground;
HSHBGROUP hGroup=GetExpandedGroup();
if(hGroup!=NULL)
{
clrBackground=GetGroupBkColor(hGroup);
VERIFY(clrBackground!=ID_COLOR_NONE);
}
else
clrBackground = GetShortcutBarSkin()->GetBackgroundColor(this);
CBrush brush(clrBackground);
// optimize filling of background by excluding from client rectangle
// all group headers rect and child window rectangle of expanded group
CRect rect;
GetClientRect(rect);
int iSavedDC = pDC->SaveDC();
// exclude header's rectangles
POSITION pos=m_mapHandleToBoundRect.GetStartPosition();
while(pos!=NULL)
{
CRect rectExclude;
m_mapHandleToBoundRect.GetNextAssoc(pos,hGroup,rectExclude);
ASSERT(hGroup!=NULL);
pDC->ExcludeClipRect(rectExclude);
}
// exclude child window's rectangle
if(!m_rectChildWnd.IsRectEmpty())
pDC->ExcludeClipRect(m_rectChildWnd);
COXShortcutBar * me = const_cast<COXShortcutBar *>(this);
COXShortcutBarSkin * skin = me->GetShortcutBarSkin();
skin->DrawTopHeader(this, pDC);
// fill the rest of background
hGroup = GetExpandedGroup();
if (hGroup != NULL)
{
if (HasGroupBkColor(hGroup))
skin->FillBackground(pDC, rect, this, TRUE, clrBackground);
else
skin->FillBackground(pDC, rect, this);
}
else
skin->FillBackground(pDC, rect, this);
pDC->RestoreDC(iSavedDC);
}
LRESULT COXShortcutBar::SendSHBNotification(LPNMSHORTCUTBAR pNMSHB)
{
ASSERT(::IsWindow(GetSafeHwnd()));
// send standard shortcut bar notification to its parent using
// NMSHORTCUTBAR structure
// notify parent
CWnd* pParentWnd=GetOwner();
if(pParentWnd)
{
// fill notification structure
pNMSHB->hdr.hwndFrom=GetSafeHwnd();
pNMSHB->hdr.idFrom=GetDlgCtrlID();
return (pParentWnd->SendMessage(
WM_NOTIFY,(WPARAM)GetDlgCtrlID(),(LPARAM)pNMSHB));
}
else
{
return (LRESULT)0;
}
}
BOOL COXShortcutBar::GetGroupHeaderRect(HSHBGROUP hGroup, LPRECT lpRect) const
{
ASSERT(::IsWindow(GetSafeHwnd()));
CRect rect;
if(!m_mapHandleToBoundRect.Lookup(hGroup,rect))
{
TRACE(_T("COXShortcutBar::GetGroupHeaderRect: bound rect not found"));
return FALSE;
}
lpRect->left=rect.left;
lpRect->right=rect.right;
lpRect->top=rect.top;
lpRect->bottom=rect.bottom;
return TRUE;
}
BOOL COXShortcutBar::UpdateHeader(HSHBGROUP hGroup)
{
ASSERT(::IsWindow(GetSafeHwnd()));
// redraw header of specified group
//
if(hGroup==NULL)
{
return FALSE;
}
CRect rectHeader;
VERIFY(GetGroupHeaderRect(hGroup,rectHeader));
RedrawWindow(rectHeader);
return TRUE;
}
HWND COXShortcutBar::GetGroupChildWndHandle(HSHBGROUP hGroup) const
{
ASSERT(::IsWindow(GetSafeHwnd()));
// Get window's handle of any child window associated with specified group.
// COXSHBListCtrl or any application defined window have to be set to every group
// in any shortcut bar control
ASSERT(hGroup);
// first check for application defined child window
HWND hWnd=GetGroupChildWnd(hGroup);
if(hWnd==NULL)
{
// after that look for COXSHBListCtrl
COXSHBListCtrl* pListCtrl=GetGroupListCtrl(hGroup);
if(pListCtrl!=NULL)
{
hWnd=pListCtrl->GetSafeHwnd();
}
}
return (hWnd);
}
int COXShortcutBar::SetGroupMargin(int nGroupMargin)
{
int nOldGroupMargin=m_nGroupMargin;
if(m_nGroupMargin!=nGroupMargin)
{
m_nGroupMargin=nGroupMargin;
if(::IsWindow(GetSafeHwnd()))
{
// reposition groups
CalculateBoundRects();
// notify all childrens about changing in properties of their
// parent shortcut bar
SendMessageToDescendants(
SHBM_SHBINFOCHANGED,(WPARAM)0,(LPARAM)SHBIF_GROUPMARGIN);
}
}
return nOldGroupMargin;
}
CRect COXShortcutBar::SetChildWndMargins(CRect rectChildWndMargins)
{
CRect rectOldChildWndMargins=m_rectChildWndMargins;
if(m_rectChildWndMargins!=rectChildWndMargins)
{
m_rectChildWndMargins=rectChildWndMargins;
if(::IsWindow(GetSafeHwnd()))
{
// reposition groups
CalculateBoundRects();
// notify all childrens about changing in properties of their
// parent shortcut bar
SendMessageToDescendants(
SHBM_SHBINFOCHANGED,(WPARAM)0,(LPARAM)SHBIF_CHILDMARGINS);
}
}
return rectOldChildWndMargins;
}
CSize COXShortcutBar::SetBarMinSize(CSize sizeMin)
{
CSize sizeOldMin=m_sizeMin;
m_sizeMin=sizeMin;
return sizeOldMin;
}
CSize COXShortcutBar::SetScrollButtonSize(CSize sizeScrollButton)
{
CSize sizeOldScrollButton=m_sizeScrollButton;
if(m_sizeScrollButton!=sizeScrollButton)
{
m_sizeScrollButton=sizeScrollButton;
if(::IsWindow(GetSafeHwnd()))
{
// notify all childrens about changing in properties of their
// parent shortcut bar
SendMessageToDescendants(
SHBM_SHBINFOCHANGED,(WPARAM)0,(LPARAM)SHBIF_SCRLBTNSIZE);
}
}
return sizeOldScrollButton;
}
UINT COXShortcutBar::SetScrollingDelay(UINT nScrollingDelay)
{
UINT nOldScrollingDelay=m_nScrollingDelay;
if(m_nScrollingDelay!=nScrollingDelay)
{
m_nScrollingDelay=nScrollingDelay;
if(::IsWindow(GetSafeHwnd()))
{
// notify all childrens about changing in properties of their
// parent shortcut bar
SendMessageToDescendants(
SHBM_SHBINFOCHANGED,(WPARAM)0,(LPARAM)SHBIF_SCRLDELAY);
}
}
return nOldScrollingDelay;
}
UINT COXShortcutBar::SetAutoScrollingDelay(UINT nAutoScrollingDelay)
{
UINT nOldAutoScrollingDelay=m_nAutoScrollingDelay;
if(m_nAutoScrollingDelay!=nAutoScrollingDelay)
{
m_nAutoScrollingDelay=nAutoScrollingDelay;
// notify all childrens about changing in properties of their
// parent shortcut bar
if(::IsWindow(GetSafeHwnd()))
{
SendMessageToDescendants(
SHBM_SHBINFOCHANGED,(WPARAM)0,(LPARAM)SHBIF_AUTOSCRLDELAY);
}
}
return nOldAutoScrollingDelay;
}
UINT COXShortcutBar::SetAutoExpandDelay(UINT nAutoExpandDelay)
{
UINT nOldAutoExpandDelay=m_nAutoExpandDelay;
if(m_nAutoExpandDelay!=nAutoExpandDelay)
{
m_nAutoExpandDelay=nAutoExpandDelay;
if(::IsWindow(GetSafeHwnd()))
{
// notify all childrens about change in properties of their
// parent shortcut bar
SendMessageToDescendants(
SHBM_SHBINFOCHANGED,(WPARAM)0,(LPARAM)SHBIF_AUTOEXPANDDELAY);
}
}
return nOldAutoExpandDelay;
}
BOOL COXShortcutBar::InitShortcutBar()
{
ASSERT(::IsWindow(GetSafeHwnd()));
// create edit control used to edit header text
if(!CreateEditControl())
{
TRACE(_T("COXShortcutBar::InitShortcutBar: failed to create edit control"));
return FALSE;
}
// tooltips must be always enabled
EnableToolTips(TRUE);
// set timer for checking which group the mouse cursor is over
m_nCheckMouseIsOverGroupID=SetTimer(
ID_CHECKMOUSEISOVERGROUP,DFLT_CHECKMOUSEISOVERGROUP,NULL);
ASSERT(m_nCheckMouseIsOverGroupID!=0);
// register OLE Drag'n'Drop
COleDropTarget* pOleDropTarget=GetDropTarget();
ASSERT(pOleDropTarget!=NULL);
if(!pOleDropTarget->Register(this))
{
TRACE(_T("COXShortcutBar::InitShortcutBar: failed to register the shortcut bar with COleDropTarget\n"));
TRACE(_T("COXShortcutBar: you've probably forgot to initialize OLE libraries using AfxOleInit function\n"));
}
return TRUE;
}
COleDropTarget* COXShortcutBar::GetDropTarget()
{
// notify parent to give it a chance to provide application specific
// COleDropTarget
NMSHORTCUTBAR nmshb;
nmshb.hdr.code=SHBN_GETDROPTARGET;
nmshb.lParam=(LPARAM)NULL;
COleDropTarget* pOleDropTarget=NULL;
// check if parent want to set its own drop target object
if(SendSHBNotification(&nmshb) && nmshb.lParam!=NULL)
pOleDropTarget=(COleDropTarget*)nmshb.lParam;
else
pOleDropTarget=(COleDropTarget*)&m_oleDropTarget;
ASSERT(pOleDropTarget!=NULL);
return pOleDropTarget;
}
void COXShortcutBar::SetMouseIsOverGroup(HSHBGROUP hGroup)
{
// reset handle to the group that is positioned under mouse cursor
if(m_hLastMouseIsOverGroup!=hGroup)
{
BOOL bFlat=((GetBarStyle() & SHBS_FLATGROUPBUTTON)==SHBS_FLATGROUPBUTTON);
HSHBGROUP hOldMouseIsOverGroup=m_hLastMouseIsOverGroup;
m_hLastMouseIsOverGroup=hGroup;
if(bFlat)
{
UpdateHeader(hOldMouseIsOverGroup);
UpdateHeader(m_hLastMouseIsOverGroup);
}
}
}
COXShortcutBarSkin* COXShortcutBar::GetShortcutBarSkin()
{
// Check if the app is derived from COXSkinnedApp
COXSkinnedApp* pSkinnedApp = DYNAMIC_DOWNCAST(COXSkinnedApp, AfxGetApp());
if (pSkinnedApp != NULL && pSkinnedApp->GetCurrentSkin() != NULL)
return pSkinnedApp->GetCurrentSkin()->GetShortcutBarSkin();
else
{
// Create a classic skin for this class if not created already
if (m_pShortcutBarSkin == NULL)
m_pShortcutBarSkin = new COXShortcutBarSkinClassic();
return m_pShortcutBarSkin;
}
}
int CALLBACK COXShortcutBar::CompareHeaders(LPARAM lParam1, LPARAM lParam2,
LPARAM lParamSort)
{
#if defined (_WINDLL)
#if defined (_AFXDLL)
AFX_MANAGE_STATE(AfxGetAppModuleState());
#else
AFX_MANAGE_STATE(AfxGetStaticModuleState());
#endif
#endif
// lParamSort is pointer to shortcut bar control
COXShortcutBar* pShortcutBar=(COXShortcutBar*)lParamSort;
ASSERT(pShortcutBar);
// while lParam1 and lParam2 are handles of compared groups
HSHBGROUP hGroup1=(HSHBGROUP)lParam1;
HSHBGROUP hGroup2=(HSHBGROUP)lParam2;
ASSERT(hGroup1 && hGroup2);
// get the text of groups
CString sText1=pShortcutBar->GetGroupText(hGroup1);
CString sText2=pShortcutBar->GetGroupText(hGroup2);
// just use alphabetical, nocase compare function to compare group headers text
return sText1.CompareNoCase(sText2);
}
//////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////
COLORREF UpdateColor(COLORREF clr, int nOffset)
{
ASSERT(abs(nOffset)<256);
int rValue=GetRValue(clr)+nOffset;
int gValue=GetGValue(clr)+nOffset;
int bValue=GetRValue(clr)+nOffset;
if(nOffset>0)
{
rValue=rValue>255 ? 255 : rValue;
gValue=gValue>255 ? 255 : gValue;
bValue=bValue>255 ? 255 : bValue;
}
else
{
rValue=rValue<0 ? 0 : rValue;
gValue=gValue<0 ? 0 : gValue;
bValue=bValue<0 ? 0 : bValue;
}
return RGB(rValue,gValue,bValue);
}
COLORREF InvertColor(COLORREF clr)
{
return RGB(255-GetRValue(clr),255-GetGValue(clr),255-GetBValue(clr));
}
BOOL IsColorCloseTo(COLORREF clr,COLORREF clrCompareTo,UINT nMaxMargin)
{
UINT nMargin=(abs(GetRValue(clr)-GetRValue(clrCompareTo))+
abs(GetGValue(clr)-GetGValue(clrCompareTo))+
abs(GetBValue(clr)-GetBValue(clrCompareTo)))/3;
return (nMaxMargin>=nMargin);
}
void* CopyImageFromIL(CImageList* pil, int nImage, DWORD& nImageSize)
{
ASSERT(pil!=NULL);
ASSERT(nImage>=0 && nImage<pil->GetImageCount());
IMAGEINFO imageInfo;
VERIFY(pil->GetImageInfo(nImage,&imageInfo));
CRect rect=imageInfo.rcImage;
ASSERT(!rect.IsRectEmpty());
CImageList il;
VERIFY(il.Create(rect.Width(),rect.Height(),ILC_MASK,1,0));
HICON hIcon=pil->ExtractIcon(nImage);
ASSERT(hIcon!=NULL);
VERIFY(il.Add(hIcon)!=-1);
VERIFY(::DestroyIcon(hIcon));
CMemFile memFile;
CArchive ar(&memFile,CArchive::store);
VERIFY(il.Write(&ar));
ar.Close();
nImageSize = (DWORD) memFile.GetLength();
void* pBuffer=malloc(nImageSize);
ASSERT(pBuffer!=NULL);
BYTE* buf=memFile.Detach();
memcpy(pBuffer,(void*)buf,nImageSize);
ASSERT(pBuffer!=NULL);
free(buf);
return pBuffer;
}
int CopyImageToIL(CImageList* pil, void* pBuffer, DWORD nBufferLength)
{
/// ASSERT(pil!=NULL && pil->GetImageCount()>0);
ASSERT(pil!=NULL);
ASSERT(pBuffer!=NULL && nBufferLength>0);
CMemFile memFile((BYTE*)pBuffer,(UINT)nBufferLength);
CArchive ar(&memFile,CArchive::load);
CImageList il;
VERIFY(il.Read(&ar));
ar.Close();
ASSERT(il.GetImageCount()==1);
HICON hIcon=il.ExtractIcon(0);
ASSERT(hIcon!=NULL);
if(pil->GetSafeHandle()==NULL)
{
IMAGEINFO imageInfo;
VERIFY(il.GetImageInfo(0,&imageInfo));
CRect rect=imageInfo.rcImage;
ASSERT(!rect.IsRectEmpty());
VERIFY(pil->Create(rect.Width(),rect.Height(),ILC_MASK,1,0));
}
int nImageIndex=pil->Add(hIcon);
VERIFY(::DestroyIcon(hIcon));
return nImageIndex;
}