📄 listbox.cpp
字号:
{
hr = CreateMainBackBuffer(paint);
ASSERT(SUCCEEDED(hr));
}
if ((HDC)m_BackBuffer)
{
// Draw the fill color over the entire non-item part of the list view
SetRect(
&NonItemsArea,
ClientRect.left,
ItemsArea.bottom,
ClientRect.right,
ClientRect.bottom
);
//Dump the back buffer into the real hdc
BitBlt(
paint,
NonItemsArea.left,
NonItemsArea.top,
RECTWIDTH(NonItemsArea),
RECTHEIGHT(NonItemsArea),
m_BackBuffer,
NonItemsArea.left, //copy from the same location in the cached dc
NonItemsArea.top,
SRCCOPY
);
}
paint.End();
if (ItemCount > 0)
{
// Reinvalidate the invalid part of the window (let the base class handle the drawing)
InvalidateRect(m_hwnd, &UpdateRect, TRUE);
}
paint_items:
return 0;
}
/*------------------------------------------------------------------------------
ListBoxImpl_t::CreateMainBackBuffer
The background of the listview is always the same,
this function caches the entire DC for the background,
so drawing in the future is extremely fast.
Parameters:
: HDC a compatible device context to build a backbuffer for
Returns (HRESULT): indicating success or failure
------------------------------------------------------------------------------*/
HRESULT
ListBoxImpl_t::CreateMainBackBuffer(
HDC hdc
)
{
ce::auto_hbrush BackgroundBrush = CreateSolidBrush(Colors_t::ListBoxBackgroundColor());
ce::auto_hpen BorderPen = CreatePen(PS_SOLID, -1, Colors_t::DisplayItemBorderColor());
if (!BackgroundBrush || !BorderPen)
{
return HRESULT_FROM_WIN32(GetLastError());
}
HRESULT hr;
RECT ClientRect;
GetClientRect(m_hwnd, &ClientRect);
//Create a bitmap of the right dimensions for the background.
hr = m_BackBuffer.CreateCanvas(
m_hwnd,
ClientRect.right,
ClientRect.bottom
);
if (FAILED(hr))
{
return hr;
}
m_BackBuffer.SetBrush(BackgroundBrush);
m_BackBuffer.SetPen(BorderPen);
Rectangle(
m_BackBuffer,
ClientRect.left,
ClientRect.top,
ClientRect.right,
ClientRect.bottom
);
//Restore the pen/brush
m_BackBuffer.SetBrush(NULL);
m_BackBuffer.SetPen(NULL);
return hr;
}
/*------------------------------------------------------------------------------
ListBoxImpl_t::Reposition subcontrols
During a paint, if our items need to position controls over our client area
(e.g. edit controls, sliders etc). We need to update the items with
their new rectangles (and whether or not they are visible)
Notifies the IVoIPDisplayItems to positions its controls
and notifies which are onscreen and offscreen
------------------------------------------------------------------------------*/
HRESULT
ListBoxImpl_t::RepositionSubcontrols(
void
)
{
//If the displayitems don't need to position controls
//short-circuit this function
if (!m_PositionControlNotification)
{
return S_FALSE;
}
//Reposition all the items -
//include whether or not they are on-screen or off-screen
RECT ClientRect = {0};
GetClientRect(m_hwnd, &ClientRect);
RECT ItemRect;
int Index;
int ItemCount = OnGetCount();
int TopItem = OnGetTopIndex();
HRESULT hr = S_OK;
if (ItemCount == 0)
{
return S_FALSE;
}
bool IsPastBottomOfScreen = false;
//Position each subcontrol with its appropriate rect
for (Index = 0; Index < ItemCount; Index++)
{
//Get the data from the parent
IVoIPDisplayItem* pItem = reinterpret_cast<IVoIPDisplayItem*>(
OnGetItemData(Index)
);
if (pItem == NULL)
{
ASSERT(FALSE);
continue;
}
IVoIPDisplayControl* pControl;
if (FAILED(
pItem->QueryInterface(
IID_IVoIPDisplayControl,
reinterpret_cast<void**>(&pControl)
)
))
{
continue;
}
//An item is visible if we have not already iterated past the bottom of the screen
// AND we are after the top visible index
bool IsItemVisible = (Index >= TopItem) && (! IsPastBottomOfScreen);
//Even if its visible, we need to do a sanity check and ensure that
//it is not past the bottom of our screen.
if (IsItemVisible)
{
OnGetItemRect(Index, &ItemRect);
//If the top of the item is LOWER than the bottom of our client
//the item (and all items after it) are NOT visible
if (ItemRect.top > ClientRect.bottom)
{
IsPastBottomOfScreen = true;
IsItemVisible = false;
}
}
if (IsItemVisible)
{
//Notify the item to position its controls
hr = pControl->PositionControls(
m_hwnd,
&ItemRect
);
}
if (SUCCEEDED(hr))
{
hr = pControl->ShowControls(IsItemVisible);
}
}
ASSERT(SUCCEEDED(hr));
return hr;
}
/*------------------------------------------------------------------------------
ListBoxImpl_t::NotifyParentOfOffscreenItems
Notify the parent that there are items above or below the display area
Returns (HRESULT): indicating success or failure
------------------------------------------------------------------------------*/
HRESULT
ListBoxImpl_t::NotifyParentOfOffscreenItems(
void
)
{
RECT ClientRect;
RECT BottomItemRect = {0};
DWORD Notification = 0;
int TopItem = OnGetTopIndex();
int ItemCount = OnGetCount();
if ((ItemCount == 0) || (TopItem == LB_ERR))
{
goto do_notify;
}
if (OnGetItemRect(ItemCount - 1, &BottomItemRect) == LB_ERR)
{
goto do_notify;
}
GetClientRect(m_hwnd, &ClientRect);
if (TopItem > 0)
{
Notification |= VLBN_ITEMS_ABOVE_DISPLAY;
}
//notify of partial items below the display
if (BottomItemRect.bottom > ClientRect.bottom)
{
Notification |= VLBN_ITEMS_BELOW_DISPLAY;
}
do_notify:
NotifyParent(
Notification
);
return S_OK;
}
LRESULT
ListBoxImpl_t::OnNotify(
int ControlId,
NMHDR* pNotificationHeader
)
{
if (!pNotificationHeader)
{
return 0;
}
CommonUtilities_t::ControlType_e ControlType =
CommonUtilities_t::GetControlType(pNotificationHeader->hwndFrom);
switch (pNotificationHeader->code)
{
case VNM_SETFOCUS:
if ((CommonUtilities_t::ControlTypeEdit == ControlType) ||
(CommonUtilities_t::ControlTypeTrackbar == ControlType))
{
// EN_SETFOCUS handler
// Notification from a child control that it (e.g. edit box) was given focus
//Get the INT_PTR representing the displayitem - find the item and select its index
IVoIPDisplayItem* pItem = (IVoIPDisplayItem*)SendMessage(
pNotificationHeader->hwndFrom,
WM_COMMON_GETCALLBACK_PTR,
0,
0
);
if (!pItem)
{
return E_POINTER;
}
//See if we can find the item in the list
int Index = OnFindItem(pItem);
if (Index == LB_ERR)
{
return E_INVALIDARG;
}
//If we did find the item, set the current selection to be that item
return OnSetCurSel(Index);
}
break;
case EN_UPDATE:
if (CommonUtilities_t::ControlTypeEdit == ControlType)
{
//Forward to the parent as WM_COMMAND (because edit control sends
//notifications in this form)
ForwardMessageToParent(
WM_COMMAND,
MAKEWPARAM(
GetWindowLong((pNotificationHeader->hwndFrom), GWL_ID),
pNotificationHeader->code
),
(LPARAM)pNotificationHeader->hwndFrom
);
}
break;
case NM_CUSTOMDRAW:
if (CommonUtilities_t::ControlTypeTrackbar == ControlType)
{
// Reflect the notification back to the control itself
return SendMessage(
pNotificationHeader->hwndFrom,
WM_NOTIFY,
ControlId,
(LPARAM)pNotificationHeader
);
}
break;
default:
break;
}
return 0;
}
/*------------------------------------------------------------------------------
ListBoxImpl_t::AddItem
Handles LB_INSERTSTRING by ensuring there is a selected item
------------------------------------------------------------------------------*/
LRESULT
ListBoxImpl_t::OnAddItem(
int Index,
IVoIPDisplayItem* pItem
)
{
#ifdef VIRTUAL_LISTBOX
if (!pItem || (Index < -1) || (Index > m_Items.size()))
{
return LB_ERR;
}
bool Success;
if ((Index == -1) || (Index == m_Items.size()))
{
Index = m_Items.size();
Success = m_Items.push_back(pItem);
}
else
{
ASSERT(IsValidIndex(Index));
Success = m_Items.insert(m_Items[Index], 1, pItem);
}
if (!Success)
{
return LB_ERRSPACE;
}
ASSERT(m_Items[Index] == (void*)pItem);
// Force a selection if this is the first item in the list
if (m_Items.size() == 1)
{
OnSetCurSel(0);
}
return Index;
#else
LRESULT Result = DefWindowProc(LB_INSERTSTRING, Index, (LPARAM)pItem);
INT cItems = OnGetCount();
// Force a selection if this is the first item in the list
if(cItems == 1)
{
OnSetCurSel(0);
}
return (LRESULT)Result;
#endif
}
/*------------------------------------------------------------------------------
ListBoxImpl_t::OnDeleteItem
Handles LB_DELETESTRING and ensures there is a selected item
------------------------------------------------------------------------------*/
LRESULT
ListBoxImpl_t::OnDeleteItem(
int Index
)
{
#ifdef VIRTUAL_LISTBOX
if (!IsValidIndex(Index))
{
return LB_ERR;
}
if (m_Items.size() == 1)
{
// When the item count is 0, we just do a resetcontent message so that
// we can reclaim our data storage space.
OnResetContent();
}
// Send a WM_DELETEITEM message for this item
DeleteItem(Index);
m_Items.erase(&m_Items[Index]);
m_VariableHeight.erase(&m_VariableHeight[Index])
//Make sure an item is still selected
//if there was no selection, select the top index
if (m_SelectedItem == LB_ERR)
{
OnSetCurSel(m_TopItem);
}
//if we deleted the selected index, select the item immediately below it
else if (m_SelectedItem == Index)
{
OnSetCurSel((Index == 0) ? 0 : Index-1);
}
else if (m_SelectedItem > Index)
{
m_SelectedItem--;
}
//EnsureVisible(m_TopItem);
return m_Items.size();
#else
int PreviousSelection = OnGetCurSel();
bool DeletedSelectedItem = (PreviousSelection == Index);
LRESULT Result = DefWindowProc(LB_DELETESTRING, Index, 0);
//Make sure an item is still selected
//if there was no selection, select the top index
if (PreviousSelection == LB_ERR)
{
OnSetCurSel(OnGetTopIndex());
}
//if we deleted the selected index, select the item immediately below it
else if (DeletedSelectedItem && (OnGetCurSel() == LB_ERR))
{
OnSetCurSel((Index == 0) ? 0 : Index-1);
}
return Result;
#endif
}
/*------------------------------------------------------------------------------
ListBoxImpl_t::DeleteItem
Handles WM_DELETEITEM by deleting the IVoIPDisplayItem
at the specified index and removing the item from the listview
NOTE: The parent must reflect this if the listbox is to delete the item
pointer
------------------------------------------------------------------------------*/
LRESULT
ListBoxImpl_t::DeleteItem(
DELETEITEMSTRUCT* pDeleteStruct
)
{
if (!pDeleteStruct)
{
return FALSE;
}
//Get the item
IVoIPDisplayItem* pItem = reinterpret_cast<IVoIPDisplayItem*>(
pDeleteStruct->itemData
);
if (!pItem)
{
ASSERT(0);
return FALSE;
}
//Have the item remove its controls (if they exist),
//since this ListView is the parent of those controls.
IVoIPDisplayControl* pControl;
if (SUCCEEDED(pItem->QueryInterface(
IID_IVoIPDisplayControl,
reinterpret_cast<void**>(&pControl)
)))
{
pControl->RemoveControls();
}
//Delete the actual item
delete pItem;
OnSetItemData(pDeleteStruct->itemID, NULL);
return TRUE;
}
/*------------------------------------------------------------------------------
ListBoxImpl_t::DrawItem
Handles WM_DRAWITEM reflected from the parent.
NOTE: This message has to be reflected from the parent if the listbox
is to trigger the drawing of the children
Prompts the IVoIPDisplayItem in the requested item to draw itself
------------------------------------------------------------------------------*/
HRESULT
ListBoxImpl_t::DrawItem(
DRAWITEMSTRUCT* pDrawStruct
)
{
#ifdef VIRTUAL_LISTBOX
IVoIPDisplayItem* pItem = reinterpret_cast<IVoIPDisplayItem*>(
OnGetItemData(Index)
);
if (!pItem)
{
ASSERT(0);
return TRUE;
}
RECT ItemRect;
OnGetItemRect(Index, &ItemRect);
#else
if (!pDrawStruct)
{
return FALSE;
}
if (pDrawStruct->itemAction == ODA_FOCUS)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -