mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
cppkore uses string/wstring as StringBase while we use std::string/std::wstring as string/wstring. Changed all types in cppkore to String/WString instead.
1428 lines
35 KiB
C++
1428 lines
35 KiB
C++
#include "stdafx.h"
|
|
#include "ListView.h"
|
|
|
|
namespace Forms
|
|
{
|
|
ListView::ListView()
|
|
: Control(), Columns(this), Items(this), _ItemActivation(ItemActivation::Standard), _ListViewAlignment(ListViewAlignment::Top), _BorderStyle(BorderStyle::Fixed3D), _ColumnHeaderStyle(ColumnHeaderStyle::Clickable), _SortOrder(SortOrder::None), _ViewStyle(View::LargeIcon), _VirtualListSize(0), _ColumnIndex(0), _DownButton(MouseButtons::None), _Flags(ListViewFlags::FlagScrollable | ListViewFlags::FlagMultiSelect | ListViewFlags::FlagLabelWrap | ListViewFlags::FlagHideSelection | ListViewFlags::FlagAutoArrange | ListViewFlags::FlagShowGroups)
|
|
{
|
|
SetStyle(ControlStyles::UserPaint | ControlStyles::StandardClick | ControlStyles::UseTextForAccessibility, false);
|
|
|
|
// Default back color is different...
|
|
this->_BackColor = Drawing::GetSystemColor(Drawing::SystemColors::Window);
|
|
|
|
// We are a ListView control.
|
|
this->_RTTI = ControlTypes::ListView;
|
|
}
|
|
|
|
ItemActivation ListView::Activation()
|
|
{
|
|
return _ItemActivation;
|
|
}
|
|
|
|
void ListView::SetActivation(ItemActivation Value)
|
|
{
|
|
if (_ItemActivation != Value)
|
|
{
|
|
_ItemActivation = Value;
|
|
UpdateExtendedStyles();
|
|
}
|
|
}
|
|
|
|
ListViewAlignment ListView::Alignment()
|
|
{
|
|
return this->_ListViewAlignment;
|
|
}
|
|
|
|
void ListView::SetAlignment(ListViewAlignment Value)
|
|
{
|
|
if (_ListViewAlignment != Value)
|
|
{
|
|
_ListViewAlignment = Value;
|
|
UpdateStyles();
|
|
}
|
|
}
|
|
|
|
bool ListView::AllowColumnReorder()
|
|
{
|
|
return GetFlag(ListViewFlags::FlagAllowColumnReorder);
|
|
}
|
|
|
|
void ListView::SetAllowColumnReorder(bool Value)
|
|
{
|
|
if (AllowColumnReorder() != Value)
|
|
{
|
|
SetFlag(ListViewFlags::FlagAllowColumnReorder, Value);
|
|
UpdateExtendedStyles();
|
|
}
|
|
}
|
|
|
|
bool ListView::AutoArrange()
|
|
{
|
|
return GetFlag(ListViewFlags::FlagAutoArrange);
|
|
}
|
|
|
|
void ListView::SetAutoArrange(bool Value)
|
|
{
|
|
if (AutoArrange() != Value)
|
|
{
|
|
SetFlag(ListViewFlags::FlagAutoArrange, Value);
|
|
UpdateStyles();
|
|
}
|
|
}
|
|
|
|
BorderStyle ListView::GetBorderStyle()
|
|
{
|
|
return this->_BorderStyle;
|
|
}
|
|
|
|
void ListView::SetBorderStyle(BorderStyle Value)
|
|
{
|
|
if (_BorderStyle != Value)
|
|
{
|
|
_BorderStyle = Value;
|
|
UpdateStyles();
|
|
}
|
|
}
|
|
|
|
int32_t ListView::FocusedItem()
|
|
{
|
|
return (int32_t)SendMessageA(this->_Handle, LVM_GETNEXTITEM, (WPARAM)-1, LVNI_FOCUSED);
|
|
}
|
|
|
|
void ListView::SetFocusedItem(int32_t Value)
|
|
{
|
|
if (GetState(ControlStates::StateCreated))
|
|
{
|
|
LVITEMA LvItem{};
|
|
LvItem.mask = LVIF_STATE;
|
|
LvItem.state = LVNI_FOCUSED;
|
|
LvItem.stateMask = LVIS_FOCUSED;
|
|
|
|
SendMessageA(this->_Handle, LVM_SETITEMSTATE, (WPARAM)Value, (LPARAM)&LvItem);
|
|
}
|
|
}
|
|
|
|
bool ListView::FullRowSelect()
|
|
{
|
|
return GetFlag(ListViewFlags::FlagFullRowSelect);
|
|
}
|
|
|
|
void ListView::SetFullRowSelect(bool Value)
|
|
{
|
|
if (FullRowSelect() != Value)
|
|
{
|
|
SetFlag(ListViewFlags::FlagFullRowSelect, Value);
|
|
UpdateExtendedStyles();
|
|
}
|
|
}
|
|
|
|
bool ListView::GridLines()
|
|
{
|
|
return GetFlag(ListViewFlags::FlagGridLines);
|
|
}
|
|
|
|
void ListView::SetGridLines(bool Value)
|
|
{
|
|
if (GridLines() != Value)
|
|
{
|
|
SetFlag(ListViewFlags::FlagGridLines, Value);
|
|
UpdateExtendedStyles();
|
|
}
|
|
}
|
|
|
|
ColumnHeaderStyle ListView::HeaderStyle()
|
|
{
|
|
return this->_ColumnHeaderStyle;
|
|
}
|
|
|
|
void ListView::SetHeaderStyle(ColumnHeaderStyle Value)
|
|
{
|
|
if (_ColumnHeaderStyle != Value)
|
|
{
|
|
_ColumnHeaderStyle = Value;
|
|
UpdateStyles();
|
|
}
|
|
}
|
|
|
|
bool ListView::HideSelection()
|
|
{
|
|
return GetFlag(ListViewFlags::FlagHideSelection);
|
|
}
|
|
|
|
void ListView::SetHideSelection(bool Value)
|
|
{
|
|
if (HideSelection() != Value)
|
|
{
|
|
SetFlag(ListViewFlags::FlagHideSelection, Value);
|
|
UpdateStyles();
|
|
}
|
|
}
|
|
|
|
bool ListView::HotTracking()
|
|
{
|
|
return GetFlag(ListViewFlags::FlagHotTracking);
|
|
}
|
|
|
|
void ListView::SetHotTracking(bool Value)
|
|
{
|
|
if (HotTracking() != Value)
|
|
{
|
|
SetFlag(ListViewFlags::FlagHotTracking, Value);
|
|
|
|
if (Value)
|
|
{
|
|
SetHoverSelection(true);
|
|
SetActivation(ItemActivation::OneClick);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool ListView::HoverSelection()
|
|
{
|
|
return GetFlag(ListViewFlags::FlagHoverSelection);
|
|
}
|
|
|
|
void ListView::SetHoverSelection(bool Value)
|
|
{
|
|
if (HoverSelection() != Value)
|
|
{
|
|
SetFlag(ListViewFlags::FlagHoverSelection, Value);
|
|
UpdateExtendedStyles();
|
|
}
|
|
}
|
|
|
|
bool ListView::LabelEdit()
|
|
{
|
|
return GetFlag(ListViewFlags::FlagLabelEdit);
|
|
}
|
|
|
|
void ListView::SetLabelEdit(bool Value)
|
|
{
|
|
if (LabelEdit() != Value)
|
|
{
|
|
SetFlag(ListViewFlags::FlagLabelEdit, Value);
|
|
UpdateStyles();
|
|
}
|
|
}
|
|
|
|
bool ListView::LabelWrap()
|
|
{
|
|
return GetFlag(ListViewFlags::FlagLabelWrap);
|
|
}
|
|
|
|
void ListView::SetLabelWrap(bool Value)
|
|
{
|
|
if (LabelWrap() != Value)
|
|
{
|
|
SetFlag(ListViewFlags::FlagLabelWrap, Value);
|
|
UpdateStyles();
|
|
}
|
|
}
|
|
|
|
bool ListView::MultiSelect()
|
|
{
|
|
return GetFlag(ListViewFlags::FlagMultiSelect);
|
|
}
|
|
|
|
void ListView::SetMultiSelect(bool Value)
|
|
{
|
|
if (MultiSelect() != Value)
|
|
{
|
|
SetFlag(ListViewFlags::FlagMultiSelect, Value);
|
|
UpdateStyles();
|
|
}
|
|
}
|
|
|
|
bool ListView::OwnerDraw()
|
|
{
|
|
return GetFlag(ListViewFlags::FlagOwnerDraw);
|
|
}
|
|
|
|
void ListView::SetOwnerDraw(bool Value)
|
|
{
|
|
if (OwnerDraw() != Value)
|
|
{
|
|
SetFlag(ListViewFlags::FlagOwnerDraw, Value);
|
|
Invalidate(true);
|
|
}
|
|
}
|
|
|
|
bool ListView::Scrollable()
|
|
{
|
|
return GetFlag(ListViewFlags::FlagScrollable);
|
|
}
|
|
|
|
void ListView::SetScrollable(bool Value)
|
|
{
|
|
if (Scrollable() != Value)
|
|
{
|
|
SetFlag(ListViewFlags::FlagScrollable, Value);
|
|
UpdateStyles();
|
|
}
|
|
}
|
|
|
|
List<uint32_t> ListView::SelectedIndices()
|
|
{
|
|
// Fetch count of selected items
|
|
int32_t SelectedCount = (int32_t)(GetState(ControlStates::StateCreated) ? SendMessageA(this->_Handle, LVM_GETSELECTEDCOUNT, NULL, NULL) : -1);
|
|
|
|
// Ignore false counts
|
|
if (SelectedCount <= 0)
|
|
return List<uint32_t>();
|
|
|
|
// Generate a new list
|
|
List<uint32_t> Result(SelectedCount, true);
|
|
|
|
int32_t LastIndex = -1;
|
|
for (int32_t i = 0; i < SelectedCount; i++)
|
|
{
|
|
LastIndex = (int32_t)SendMessageA(this->_Handle, LVM_GETNEXTITEM, (WPARAM)LastIndex, LVNI_SELECTED);
|
|
Result[i] = (uint32_t)LastIndex;
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
bool ListView::ShowGroups()
|
|
{
|
|
return GetFlag(ListViewFlags::FlagShowGroups);
|
|
}
|
|
|
|
void ListView::SetShowGroups(bool Value)
|
|
{
|
|
if (ShowGroups() != Value)
|
|
{
|
|
SetFlag(ListViewFlags::FlagShowGroups, Value);
|
|
}
|
|
}
|
|
|
|
SortOrder ListView::Sorting()
|
|
{
|
|
return this->_SortOrder;
|
|
}
|
|
|
|
void ListView::SetSorting(SortOrder Value)
|
|
{
|
|
if (_SortOrder != Value)
|
|
{
|
|
_SortOrder = Value;
|
|
UpdateStyles();
|
|
}
|
|
}
|
|
|
|
int32_t ListView::TopItem()
|
|
{
|
|
return (int32_t)(GetState(ControlStates::StateCreated) ? SendMessageA(this->_Handle, LVM_GETTOPINDEX, NULL, NULL) : -1);
|
|
}
|
|
|
|
void ListView::SetTopItem(int32_t Value)
|
|
{
|
|
if (GetState(ControlStates::StateCreated))
|
|
{
|
|
if (Value <= 0)
|
|
Value = 0;
|
|
|
|
// Make sure it's visible first
|
|
SendMessageA(this->_Handle, LVM_ENSUREVISIBLE, (WPARAM)Value, NULL);
|
|
|
|
if (Scrollable())
|
|
{
|
|
// Scroll to top
|
|
int32_t ScrollY = GetItemPosition(0).Y - GetItemPosition(Value).Y;
|
|
SendMessageA(this->_Handle, LVM_SCROLL, 0, ScrollY);
|
|
}
|
|
}
|
|
}
|
|
|
|
int32_t ListView::CountPerPage()
|
|
{
|
|
return (int32_t)SendMessageA(this->_Handle, LVM_GETCOUNTPERPAGE, NULL, NULL);
|
|
}
|
|
|
|
View ListView::GetView()
|
|
{
|
|
return this->_ViewStyle;
|
|
}
|
|
|
|
void ListView::SetView(View Value)
|
|
{
|
|
if (_ViewStyle != Value)
|
|
{
|
|
_ViewStyle = Value;
|
|
|
|
if (GetState(ControlStates::StateCreated))
|
|
{
|
|
SendMessageA(this->_Handle, LVM_SETVIEW, (WPARAM)_ViewStyle, NULL);
|
|
}
|
|
else
|
|
{
|
|
UpdateStyles();
|
|
}
|
|
}
|
|
}
|
|
|
|
int32_t ListView::VirtualListSize()
|
|
{
|
|
return this->_VirtualListSize;
|
|
}
|
|
|
|
void ListView::SetVirtualListSize(int32_t Value)
|
|
{
|
|
if (_VirtualListSize == Value)
|
|
return;
|
|
|
|
bool KeepTopItem = GetState(ControlStates::StateCreated) && this->VirtualMode() && this->_ViewStyle == View::Details;
|
|
int32_t TopIndex = -1;
|
|
|
|
if (KeepTopItem)
|
|
TopIndex = (int32_t)SendMessageA(this->_Handle, LVM_GETTOPINDEX, NULL, NULL);
|
|
|
|
_VirtualListSize = Value;
|
|
|
|
if (GetState(ControlStates::StateCreated) && this->VirtualMode())
|
|
SendMessageA(this->_Handle, LVM_SETITEMCOUNT, (WPARAM)_VirtualListSize, NULL);
|
|
|
|
if (KeepTopItem)
|
|
{
|
|
TopIndex = min(TopIndex, this->_VirtualListSize - 1);
|
|
|
|
if (TopIndex > 0)
|
|
this->SetTopItem(TopIndex);
|
|
}
|
|
}
|
|
|
|
bool ListView::VirtualMode()
|
|
{
|
|
return GetFlag(ListViewFlags::FlagVirtualMode);
|
|
}
|
|
|
|
void ListView::SetVirtualMode(bool Value)
|
|
{
|
|
if (VirtualMode() != Value)
|
|
{
|
|
SetFlag(ListViewFlags::FlagVirtualMode, Value);
|
|
UpdateStyles();
|
|
}
|
|
}
|
|
|
|
void ListView::OnHandleCreated()
|
|
{
|
|
// Call tbe base event first
|
|
Control::OnHandleCreated();
|
|
|
|
// Ensure we have a proper comctl version
|
|
int32_t Version = (int32_t)SendMessageA(this->_Handle, CCM_GETVERSION, NULL, NULL);
|
|
if (Version < 5)
|
|
SendMessageA(this->_Handle, CCM_SETVERSION, (WPARAM)5, NULL);
|
|
|
|
// Ensure extended styles are set
|
|
UpdateExtendedStyles();
|
|
|
|
// Update control colors
|
|
SendMessageA(this->_Handle, LVM_SETBKCOLOR, NULL, (LPARAM)Drawing::ColorToWin32(this->BackColor()));
|
|
SendMessageA(this->_Handle, LVM_SETTEXTCOLOR, NULL, (LPARAM)Drawing::ColorToWin32(this->ForeColor()));
|
|
SendMessageA(this->_Handle, LVM_SETTEXTBKCOLOR, NULL, CLR_NONE);
|
|
|
|
if (!Scrollable())
|
|
{
|
|
auto Style = (int32_t)GetWindowLong(this->_Handle, GWL_STYLE);
|
|
Style |= LVS_NOSCROLL;
|
|
SetWindowLong(this->_Handle, GWL_STYLE, Style);
|
|
}
|
|
|
|
SendMessageA(this->_Handle, LVM_SETVIEW, (WPARAM)_ViewStyle, NULL);
|
|
|
|
auto ColumnCount = Columns.Count();
|
|
if (ColumnCount > 0)
|
|
{
|
|
auto Indices = std::make_unique<int32_t[]>(ColumnCount);
|
|
int32_t Index = 0;
|
|
|
|
for (auto& Column : this->Columns)
|
|
{
|
|
Indices[Index] = Column.DisplayIndex();
|
|
NativeInsertColumn(Index++, Column);
|
|
}
|
|
|
|
SetDisplayIndices(Indices, ColumnCount);
|
|
}
|
|
|
|
if (!VirtualMode())
|
|
this->NativeInsertItems(0, Items.Count(), Items.ToList());
|
|
|
|
if (VirtualMode() && VirtualListSize() > -1)
|
|
SendMessageA(this->_Handle, LVM_SETITEMCOUNT, (WPARAM)VirtualListSize(), NULL);
|
|
}
|
|
|
|
void ListView::OnBackColorChanged()
|
|
{
|
|
SendMessageA(this->_Handle, LVM_SETBKCOLOR, NULL, (LPARAM)Drawing::ColorToWin32(this->BackColor()));
|
|
|
|
// We must call the base event last
|
|
Control::OnBackColorChanged();
|
|
}
|
|
|
|
void ListView::OnForeColorChanged()
|
|
{
|
|
SendMessageA(this->_Handle, LVM_SETTEXTCOLOR, NULL, (LPARAM)Drawing::ColorToWin32(this->ForeColor()));
|
|
|
|
// We must call the base event last
|
|
Control::OnForeColorChanged();
|
|
}
|
|
|
|
void ListView::OnFontChanged()
|
|
{
|
|
Control::OnFontChanged();
|
|
|
|
if (!VirtualMode() && GetState(ControlStates::StateCreated))
|
|
SendMessageA(this->_Handle, LVM_UPDATE, (WPARAM)-1, NULL);
|
|
|
|
InvalidateColumnHeaders();
|
|
}
|
|
|
|
void ListView::OnItemActivate()
|
|
{
|
|
ItemActivate.RaiseEvent(this);
|
|
}
|
|
|
|
void ListView::OnColumnClick(const std::unique_ptr<ColumnClickEventArgs>& EventArgs)
|
|
{
|
|
ColumnClick.RaiseEvent(EventArgs, this);
|
|
}
|
|
|
|
void ListView::OnBeforeLabelEdit(const std::unique_ptr<LabelEditEventArgs>& EventArgs)
|
|
{
|
|
BeforeLabelEdit.RaiseEvent(EventArgs, this);
|
|
}
|
|
|
|
void ListView::OnAfterLabelEdit(const std::unique_ptr<LabelEditEventArgs>& EventArgs)
|
|
{
|
|
AfterLabelEdit.RaiseEvent(EventArgs, this);
|
|
}
|
|
|
|
void ListView::OnDrawItem(const std::unique_ptr<DrawListViewItemEventArgs>& EventArgs)
|
|
{
|
|
DrawItem.RaiseEvent(EventArgs, this);
|
|
}
|
|
|
|
void ListView::OnDrawSubItem(const std::unique_ptr<DrawListViewSubItemEventArgs>& EventArgs)
|
|
{
|
|
DrawSubItem.RaiseEvent(EventArgs, this);
|
|
}
|
|
|
|
void ListView::OnDrawColumnHeader(const std::unique_ptr<DrawListViewColumnHeaderEventArgs>& EventArgs)
|
|
{
|
|
DrawColumnHeader.RaiseEvent(EventArgs, this);
|
|
}
|
|
|
|
void ListView::OnCacheVirtualItems(const std::unique_ptr<CacheVirtualItemsEventArgs>& EventArgs)
|
|
{
|
|
CacheVirtualItems.RaiseEvent(EventArgs, this);
|
|
}
|
|
|
|
void ListView::OnRetrieveVirtualItem(const std::unique_ptr<RetrieveVirtualItemEventArgs>& EventArgs)
|
|
{
|
|
RetrieveVirtualItem.RaiseEvent(EventArgs, this);
|
|
}
|
|
|
|
void ListView::OnVirtualItemsSelectionRangeChanged(const std::unique_ptr<ListViewVirtualItemsSelectionRangeChangedEventArgs>& EventArgs)
|
|
{
|
|
VirtualItemsSelectionRangeChanged.RaiseEvent(EventArgs, this);
|
|
}
|
|
|
|
void ListView::WndProc(Message& Msg)
|
|
{
|
|
switch (Msg.Msg)
|
|
{
|
|
case WM_REFLECT + WM_NOTIFY:
|
|
WmReflectNotify(Msg);
|
|
break;
|
|
case WM_LBUTTONDBLCLK:
|
|
SetCapture(true);
|
|
WmMouseDown(Msg, MouseButtons::Left, 2);
|
|
break;
|
|
case WM_LBUTTONDOWN:
|
|
WmMouseDown(Msg, MouseButtons::Left, 1);
|
|
_DownButton = MouseButtons::Left;
|
|
break;
|
|
|
|
case WM_LBUTTONUP:
|
|
case WM_RBUTTONUP:
|
|
case WM_MBUTTONUP:
|
|
{
|
|
LVHITTESTINFO LvHip{};
|
|
auto Index = GetIndexOfClickedItem(LvHip);
|
|
|
|
if (GetFlag(ListViewFlags::FlagDoubleclickFired) && Index != -1)
|
|
{
|
|
SetFlag(ListViewFlags::FlagDoubleclickFired, false);
|
|
OnDoubleClick();
|
|
OnMouseDoubleClick(std::make_unique<MouseEventArgs>(_DownButton, 2, (int16_t)LOWORD(Msg.LParam), (int16_t)HIWORD(Msg.LParam), 0));
|
|
}
|
|
|
|
if (!GetFlag(ListViewFlags::FlagMouseUpFired))
|
|
{
|
|
OnMouseUp(std::make_unique<MouseEventArgs>(_DownButton, 1, (int16_t)LOWORD(Msg.LParam), (int16_t)HIWORD(Msg.LParam), 0));
|
|
SetFlag(ListViewFlags::FlagExpectingMouseUp, false);
|
|
}
|
|
|
|
SetFlag(ListViewFlags::FlagMouseUpFired, true);
|
|
SetCapture(false);
|
|
}
|
|
break;
|
|
|
|
case WM_MBUTTONDBLCLK:
|
|
WmMouseDown(Msg, MouseButtons::Middle, 2);
|
|
break;
|
|
case WM_MBUTTONDOWN:
|
|
WmMouseDown(Msg, MouseButtons::Middle, 1);
|
|
_DownButton = MouseButtons::Middle;
|
|
break;
|
|
case WM_RBUTTONDBLCLK:
|
|
WmMouseDown(Msg, MouseButtons::Right, 2);
|
|
break;
|
|
case WM_RBUTTONDOWN:
|
|
WmMouseDown(Msg, MouseButtons::Right, 1);
|
|
_DownButton = MouseButtons::Right;
|
|
break;
|
|
case WM_MOUSEMOVE:
|
|
if (GetFlag(ListViewFlags::FlagExpectingMouseUp) && !GetFlag(ListViewFlags::FlagMouseUpFired) && GetMouseButtons() == MouseButtons::None)
|
|
{
|
|
OnMouseUp(std::make_unique<MouseEventArgs>(_DownButton, 1, (int16_t)LOWORD(Msg.LParam), (int16_t)HIWORD(Msg.LParam), 0));
|
|
SetFlag(ListViewFlags::FlagMouseUpFired, true);
|
|
}
|
|
|
|
SetCapture(false);
|
|
Control::WndProc(Msg);
|
|
break;
|
|
case WM_NOTIFY:
|
|
if (WmNotify(Msg))
|
|
return;
|
|
|
|
Control::WndProc(Msg);
|
|
break;
|
|
case WM_MOUSEHOVER:
|
|
if (HoverSelection())
|
|
Control::WndProc(Msg);
|
|
else
|
|
OnMouseHover();
|
|
break;
|
|
|
|
default:
|
|
Control::WndProc(Msg);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ListView::CreateControl(Control* Parent)
|
|
{
|
|
INITCOMMONCONTROLSEX iCC{};
|
|
iCC.dwSize = sizeof(iCC);
|
|
iCC.dwICC = ICC_LISTVIEW_CLASSES;
|
|
|
|
InitCommonControlsEx(&iCC);
|
|
|
|
Control::CreateControl(Parent);
|
|
}
|
|
|
|
CreateParams ListView::GetCreateParams()
|
|
{
|
|
auto Cp = Control::GetCreateParams();
|
|
|
|
Cp.ClassName = "SysListView32";
|
|
|
|
if (GetState(ControlStates::StateCreated))
|
|
{
|
|
int32_t CurrentStyle = (int32_t)GetWindowLong(this->_Handle, GWL_STYLE);
|
|
Cp.Style |= (CurrentStyle & (WS_HSCROLL | WS_VSCROLL));
|
|
}
|
|
|
|
Cp.Style |= LVS_SHAREIMAGELISTS;
|
|
|
|
switch (_ListViewAlignment)
|
|
{
|
|
case ListViewAlignment::Top:
|
|
Cp.Style |= LVS_ALIGNTOP;
|
|
break;
|
|
case ListViewAlignment::Left:
|
|
Cp.Style |= LVS_ALIGNLEFT;
|
|
break;
|
|
}
|
|
|
|
if (AutoArrange())
|
|
Cp.Style |= LVS_AUTOARRANGE;
|
|
|
|
switch (_BorderStyle)
|
|
{
|
|
case BorderStyle::Fixed3D:
|
|
Cp.ExStyle |= WS_EX_CLIENTEDGE;
|
|
break;
|
|
case BorderStyle::FixedSingle:
|
|
Cp.Style |= WS_BORDER;
|
|
break;
|
|
}
|
|
|
|
switch (_ColumnHeaderStyle)
|
|
{
|
|
case ColumnHeaderStyle::None:
|
|
Cp.Style |= LVS_NOCOLUMNHEADER;
|
|
break;
|
|
case ColumnHeaderStyle::NonClickable:
|
|
Cp.Style |= LVS_NOSORTHEADER;
|
|
break;
|
|
}
|
|
|
|
if (LabelEdit())
|
|
Cp.Style |= LVS_EDITLABELS;
|
|
if (!LabelWrap())
|
|
Cp.Style |= LVS_NOLABELWRAP;
|
|
if (!HideSelection())
|
|
Cp.Style |= LVS_SHOWSELALWAYS;
|
|
if (!MultiSelect())
|
|
Cp.Style |= LVS_SINGLESEL;
|
|
|
|
switch (_SortOrder)
|
|
{
|
|
case SortOrder::Ascending:
|
|
Cp.Style |= LVS_SORTASCENDING;
|
|
break;
|
|
case SortOrder::Descending:
|
|
Cp.Style |= LVS_SORTDESCENDING;
|
|
break;
|
|
}
|
|
|
|
if (VirtualMode())
|
|
Cp.Style |= LVS_OWNERDATA;
|
|
|
|
if (_ViewStyle != View::Tile)
|
|
Cp.Style |= (int32_t)_ViewStyle;
|
|
|
|
return Cp;
|
|
}
|
|
|
|
void ListView::ScaleControl(Drawing::SizeF Factor, BoundsSpecified Specified)
|
|
{
|
|
Control::ScaleControl(Factor, Specified);
|
|
|
|
// We override this so we can scale the current column widths
|
|
for (auto& Column : this->Columns)
|
|
{
|
|
Column.SetWidth(this->ScaleSize({ Column.Width(), 0 }, Factor.Width, Factor.Height).Width);
|
|
}
|
|
}
|
|
|
|
bool ListView::GetFlag(ListViewFlags Flag)
|
|
{
|
|
return ((int)this->_Flags & (int)Flag) == (int)Flag;
|
|
}
|
|
|
|
void ListView::SetFlag(ListViewFlags Flags, bool Value)
|
|
{
|
|
this->_Flags = Value ? (ListViewFlags)((int)this->_Flags | (int)Flags) : (ListViewFlags)((int)this->_Flags & ~(int)Flags);
|
|
}
|
|
|
|
void ListView::NativeAddColumn(ColumnHeader& Header)
|
|
{
|
|
NativeInsertColumn(this->Columns.Count(), Header);
|
|
}
|
|
|
|
void ListView::NativeInsertColumn(const int32_t Index, ColumnHeader& Header)
|
|
{
|
|
LVCOLUMNA LvColumn{};
|
|
LvColumn.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
|
|
|
|
LvColumn.fmt = (int32_t)Header.TextAlign();
|
|
LvColumn.cx = Header.Width();
|
|
LvColumn.pszText = (char*)Header.Text();
|
|
|
|
SendMessageA(this->_Handle, LVM_INSERTCOLUMNA, (WPARAM)Index, (LPARAM)&LvColumn);
|
|
}
|
|
|
|
void ListView::NativeClearColumns()
|
|
{
|
|
for (int32_t ColIdx = this->Columns.Count() - 1; ColIdx >= 0; ColIdx--)
|
|
SendMessageA(this->_Handle, LVM_DELETECOLUMN, (WPARAM)ColIdx, NULL);
|
|
}
|
|
|
|
void ListView::NativeInsertItem(int32_t Index, const ListViewItem& Item)
|
|
{
|
|
SendMessageA(this->_Handle, LVM_SETITEMCOUNT, (WPARAM)Items.Count(), NULL);
|
|
|
|
LVITEMA LvItem{};
|
|
LvItem.mask = LVIF_TEXT;
|
|
LvItem.iItem = Index;
|
|
LvItem.pszText = (char*)Item.Text();
|
|
|
|
SendMessageA(this->_Handle, LVM_INSERTITEMA, NULL, (LPARAM)&LvItem);
|
|
|
|
for (uint32_t i = 1; i <= Item.SubItemCount(); i++)
|
|
{
|
|
LVITEMA LvSub{};
|
|
LvSub.mask = LVIF_TEXT;
|
|
LvSub.iItem = Index;
|
|
LvSub.iSubItem = i;
|
|
LvSub.pszText = (char*)Item.SubItem(i);
|
|
|
|
SendMessageA(this->_Handle, LVM_SETITEMTEXTA, (WPARAM)Index, (LPARAM)&LvSub);
|
|
}
|
|
}
|
|
|
|
void ListView::NativeInsertItems(int32_t Index, int32_t Count, const List<ListViewItem>& Items)
|
|
{
|
|
SendMessageA(this->_Handle, LVM_SETITEMCOUNT, (WPARAM)Items.Count(), NULL);
|
|
|
|
for (int32_t i = 0; i < Count; i++)
|
|
{
|
|
auto& Item = Items[Index + i];
|
|
|
|
LVITEMA LvItem{};
|
|
LvItem.mask = LVIF_TEXT;
|
|
LvItem.iItem = Index + i;
|
|
LvItem.pszText = (char*)Item.Text();
|
|
|
|
SendMessageA(this->_Handle, LVM_INSERTITEMA, NULL, (LPARAM)&LvItem);
|
|
|
|
for (uint32_t j = 1; j <= Item.SubItemCount(); j++)
|
|
{
|
|
LVITEMA LvSub{};
|
|
LvSub.mask = LVIF_TEXT;
|
|
LvSub.iItem = Index + i;
|
|
LvSub.iSubItem = j;
|
|
LvSub.pszText = (char*)Item.SubItem(j);
|
|
|
|
SendMessageA(this->_Handle, LVM_SETITEMTEXTA, (WPARAM)(Index + i), (LPARAM)&LvSub);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ListView::NativeRemoveItem(int32_t Index)
|
|
{
|
|
SendMessageA(this->_Handle, LVM_DELETEITEM, (WPARAM)Index, NULL);
|
|
}
|
|
|
|
void ListView::NativeClearItems()
|
|
{
|
|
SendMessageA(this->_Handle, LVM_DELETEALLITEMS, NULL, NULL);
|
|
}
|
|
|
|
void ListView::InvalidateColumnHeaders()
|
|
{
|
|
if (GetState(ControlStates::StateCreated) && _ViewStyle == View::Details)
|
|
{
|
|
auto HwndHdr = (HWND)SendMessageA(this->_Handle, LVM_GETHEADER, NULL, NULL);
|
|
if (HwndHdr != NULL)
|
|
InvalidateRect(HwndHdr, NULL, TRUE);
|
|
}
|
|
}
|
|
|
|
Drawing::Point ListView::GetItemPosition(int32_t Index)
|
|
{
|
|
POINT Pt{};
|
|
SendMessageA(this->_Handle, LVM_GETITEMPOSITION, (WPARAM)Index, (LPARAM)&Pt);
|
|
|
|
return Drawing::Point(Pt.x, Pt.y);
|
|
}
|
|
|
|
Drawing::Rectangle ListView::GetItemRectOrEmpty(int32_t Index)
|
|
{
|
|
if (Index < 0 || Index >= (int32_t)this->Items.Count())
|
|
return Drawing::Rectangle();
|
|
if (_ViewStyle == View::Details && this->Columns.Count() == 0)
|
|
return Drawing::Rectangle();
|
|
|
|
RECT ItemRect{};
|
|
if (SendMessageA(this->_Handle, LVM_GETITEMRECT, (WPARAM)Index, (LPARAM)&ItemRect) == 0)
|
|
return Drawing::Rectangle();
|
|
|
|
return Drawing::Rectangle(ItemRect.left, ItemRect.top, ItemRect.right - ItemRect.left, ItemRect.bottom - ItemRect.top);
|
|
}
|
|
|
|
Drawing::Rectangle ListView::GetSubItemRect(int32_t Index, int32_t SubItem)
|
|
{
|
|
if (_ViewStyle != View::Details)
|
|
return Drawing::Rectangle();
|
|
if (this->Columns.Count() == 0)
|
|
return Drawing::Rectangle();
|
|
|
|
RECT ItemRect{};
|
|
ItemRect.top = SubItem;
|
|
|
|
if (SendMessageA(this->_Handle, LVM_GETSUBITEMRECT, (WPARAM)Index, (LPARAM)&ItemRect) == 0)
|
|
return Drawing::Rectangle();
|
|
|
|
return Drawing::Rectangle(ItemRect.left, ItemRect.top, ItemRect.right - ItemRect.left, ItemRect.bottom - ItemRect.top);
|
|
}
|
|
|
|
int32_t ListView::GetItemState(int32_t Index)
|
|
{
|
|
return (int32_t)SendMessageA(this->_Handle, LVM_GETITEMSTATE, (WPARAM)Index, LVIS_FOCUSED | LVIS_SELECTED | LVIS_CUT | LVIS_DROPHILITED | LVIS_OVERLAYMASK | LVIS_STATEIMAGEMASK);
|
|
}
|
|
|
|
std::pair<String, ListViewItemStyle> ListView::GetSubItem(int32_t Index, int32_t SubItem)
|
|
{
|
|
if (!VirtualMode())
|
|
{
|
|
auto& Item = Items[Index];
|
|
auto& Text = Item.SubItem(SubItem);
|
|
auto& Style = Item.SubItemStyle(SubItem);
|
|
|
|
return std::pair<String, ListViewItemStyle>(Text, Style);
|
|
}
|
|
|
|
auto EventArgs = std::make_unique<RetrieveVirtualItemEventArgs>(Index, SubItem);
|
|
OnRetrieveVirtualItem(EventArgs);
|
|
|
|
return std::pair<String, ListViewItemStyle>(EventArgs->Text, EventArgs->Style);
|
|
}
|
|
|
|
void ListView::SetColumnInfo(int32_t Mask, ColumnHeader& Header)
|
|
{
|
|
if (!GetState(ControlStates::StateCreated))
|
|
return;
|
|
|
|
LVCOLUMNA LvColumn{};
|
|
LvColumn.mask = Mask;
|
|
|
|
if ((Mask & LVCF_FMT) != 0)
|
|
{
|
|
LvColumn.fmt |= (int32_t)Header.TextAlign();
|
|
}
|
|
|
|
if ((Mask & LVCF_TEXT) != 0)
|
|
{
|
|
LvColumn.pszText = (char*)Header.Text();
|
|
}
|
|
|
|
SendMessageA(this->_Handle, LVM_SETCOLUMNA, (WPARAM)Header.Index(), (LPARAM)&LvColumn);
|
|
InvalidateColumnHeaders();
|
|
}
|
|
|
|
void ListView::SetColumnWidth(int32_t Index, int32_t Width)
|
|
{
|
|
if (GetState(ControlStates::StateCreated))
|
|
SendMessageA(this->_Handle, LVM_SETCOLUMNWIDTH, (WPARAM)Index, MAKELPARAM(Width, 0));
|
|
}
|
|
|
|
void ListView::SetDisplayIndices(const std::unique_ptr<int32_t[]>& Indices, int32_t Count)
|
|
{
|
|
auto OrderedColumns = std::make_unique<int32_t[]>(Count);
|
|
for (int32_t i = 0; i < Count; i++)
|
|
{
|
|
this->Columns[i].SetDisplayIndexInternal(Indices[i]);
|
|
OrderedColumns[Indices[i]] = i;
|
|
}
|
|
|
|
if (GetState(ControlStates::StateCreated))
|
|
SendMessageA(this->_Handle, LVM_SETCOLUMNORDERARRAY, (WPARAM)Count, (LPARAM)OrderedColumns.get());
|
|
}
|
|
|
|
void ListView::UpdateExtendedStyles()
|
|
{
|
|
if (GetState(ControlStates::StateCreated))
|
|
{
|
|
int32_t ExStyle = 0;
|
|
int32_t ExMask = LVS_EX_ONECLICKACTIVATE | LVS_EX_TWOCLICKACTIVATE |
|
|
LVS_EX_TRACKSELECT | LVS_EX_UNDERLINEHOT | LVS_EX_ONECLICKACTIVATE |
|
|
LVS_EX_HEADERDRAGDROP | LVS_EX_CHECKBOXES |
|
|
LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES |
|
|
LVS_EX_INFOTIP | LVS_EX_DOUBLEBUFFER;
|
|
|
|
switch (_ItemActivation)
|
|
{
|
|
case ItemActivation::OneClick:
|
|
ExStyle |= LVS_EX_ONECLICKACTIVATE;
|
|
break;
|
|
case ItemActivation::TwoClick:
|
|
ExStyle |= LVS_EX_TWOCLICKACTIVATE;
|
|
break;
|
|
}
|
|
|
|
if (AllowColumnReorder())
|
|
ExStyle |= LVS_EX_HEADERDRAGDROP;
|
|
if (DoubleBuffered())
|
|
ExStyle |= LVS_EX_DOUBLEBUFFER;
|
|
if (FullRowSelect())
|
|
ExStyle |= LVS_EX_FULLROWSELECT;
|
|
if (GridLines())
|
|
ExStyle |= LVS_EX_GRIDLINES;
|
|
if (HoverSelection())
|
|
ExStyle |= LVS_EX_TRACKSELECT;
|
|
if (HotTracking())
|
|
ExStyle |= LVS_EX_UNDERLINEHOT;
|
|
|
|
SendMessageA(this->_Handle, LVM_SETEXTENDEDLISTVIEWSTYLE, (WPARAM)ExMask, (LPARAM)ExStyle);
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
int32_t ListView::GetIndexOfClickedItem(LVHITTESTINFO & LvHI)
|
|
{
|
|
auto Pos = GetMousePosition();
|
|
Pos = PointToClient(Pos);
|
|
|
|
LvHI.pt.x = Pos.X;
|
|
LvHI.pt.y = Pos.Y;
|
|
|
|
return (int32_t)SendMessageA(this->_Handle, LVM_HITTEST, NULL, (LPARAM)&LvHI);
|
|
}
|
|
|
|
void ListView::WmMouseDown(Message& Msg, MouseButtons Button, uint32_t Clicks)
|
|
{
|
|
SetFlag(ListViewFlags::FlagMouseUpFired, false);
|
|
SetFlag(ListViewFlags::FlagExpectingMouseUp, true);
|
|
|
|
this->Focus();
|
|
|
|
int32_t X = LOWORD((int)Msg.LParam);
|
|
int32_t Y = HIWORD((int)Msg.LParam);
|
|
auto EventArgs = std::make_unique<MouseEventArgs>(Button, Clicks, X, Y, 0);
|
|
|
|
OnMouseDown(EventArgs);
|
|
|
|
DefWndProc(Msg);
|
|
}
|
|
|
|
void ListView::WmReflectNotify(Message& Msg)
|
|
{
|
|
auto NMHdr = (NMHDR*)Msg.LParam;
|
|
|
|
switch (NMHdr->code)
|
|
{
|
|
case NM_CUSTOMDRAW:
|
|
NmCustomDraw(Msg);
|
|
break;
|
|
|
|
case LVN_BEGINLABELEDITA:
|
|
case LVN_BEGINLABELEDITW:
|
|
{
|
|
auto DispInfo = (NMLVDISPINFOA*)Msg.LParam;
|
|
auto EventArgs = std::make_unique<LabelEditEventArgs>(DispInfo->item.iItem);
|
|
OnBeforeLabelEdit(EventArgs);
|
|
Msg.Result = (uintptr_t)(EventArgs->CancelEdit ? 1 : 0);
|
|
SetFlag(ListViewFlags::FlagInLabelEdit, !EventArgs->CancelEdit);
|
|
}
|
|
break;
|
|
|
|
case LVN_COLUMNCLICK:
|
|
SetFlag(ListViewFlags::FlagColumnClicked, true);
|
|
_ColumnIndex = ((NMLISTVIEW*)Msg.LParam)->iSubItem;
|
|
break;
|
|
|
|
case LVN_ENDLABELEDITA:
|
|
case LVN_ENDLABELEDITW:
|
|
{
|
|
auto DispInfo = (NMLVDISPINFOA*)Msg.LParam;
|
|
auto EventArgs = std::make_unique<LabelEditEventArgs>(DispInfo->item.iItem, DispInfo->item.pszText);
|
|
OnAfterLabelEdit(EventArgs);
|
|
Msg.Result = (uintptr_t)(EventArgs->CancelEdit ? 1 : 0);
|
|
|
|
if (!EventArgs->CancelEdit && DispInfo->item.pszText != nullptr)
|
|
{
|
|
LVITEMA LvItem{};
|
|
LvItem.mask = LVIF_TEXT;
|
|
LvItem.iItem = DispInfo->item.iItem;
|
|
LvItem.pszText = DispInfo->item.pszText;
|
|
SendMessageA(this->_Handle, LVM_SETITEMTEXTA, (WPARAM)DispInfo->item.iItem, (LPARAM)&LvItem);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case LVN_ITEMACTIVATE:
|
|
OnItemActivate();
|
|
break;
|
|
|
|
case NM_CLICK:
|
|
case NM_RCLICK:
|
|
{
|
|
LVHITTESTINFO LvHI{};
|
|
auto DisplayIndex = GetIndexOfClickedItem(LvHI);
|
|
auto Button = NMHdr->code == NM_CLICK ? MouseButtons::Left : MouseButtons::Right;
|
|
auto Pos = GetMousePosition();
|
|
Pos = PointToClient(Pos);
|
|
|
|
if (DisplayIndex != -1)
|
|
{
|
|
OnClick();
|
|
OnMouseClick(std::make_unique<MouseEventArgs>(Button, 1, Pos.X, Pos.Y, 0));
|
|
}
|
|
if (!GetFlag(ListViewFlags::FlagMouseUpFired))
|
|
{
|
|
OnMouseUp(std::make_unique<MouseEventArgs>(Button, 1, Pos.X, Pos.Y, 0));
|
|
SetFlag(ListViewFlags::FlagMouseUpFired, true);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case NM_DBLCLK:
|
|
case NM_RDBLCLK:
|
|
{
|
|
LVHITTESTINFO LvHI{};
|
|
auto DisplayIndex = GetIndexOfClickedItem(LvHI);
|
|
|
|
if (DisplayIndex != -1)
|
|
SetFlag(ListViewFlags::FlagDoubleclickFired, true);
|
|
SetFlag(ListViewFlags::FlagMouseUpFired, false);
|
|
|
|
SetCapture(true);
|
|
}
|
|
break;
|
|
|
|
case LVN_ODCACHEHINT:
|
|
{
|
|
auto CacheHint = (NMLVCACHEHINT*)Msg.LParam;
|
|
OnCacheVirtualItems(std::make_unique<CacheVirtualItemsEventArgs>(CacheHint->iFrom, CacheHint->iTo));
|
|
}
|
|
break;
|
|
|
|
case LVN_GETDISPINFOA:
|
|
case LVN_GETDISPINFOW:
|
|
if (VirtualMode())
|
|
{
|
|
auto DispInfo = (NMLVDISPINFOA*)Msg.LParam;
|
|
|
|
if ((DispInfo->item.mask & LVIF_TEXT) != 0)
|
|
{
|
|
auto EventArgs = std::make_unique<RetrieveVirtualItemEventArgs>(DispInfo->item.iItem, DispInfo->item.iSubItem);
|
|
OnRetrieveVirtualItem(EventArgs);
|
|
|
|
auto TextSize = min(EventArgs->Text.Length(), (uint32_t)(DispInfo->item.cchTextMax - 1));
|
|
std::memcpy(DispInfo->item.pszText, (char*)EventArgs->Text, (size_t)TextSize);
|
|
DispInfo->item.pszText[TextSize] = '\0';
|
|
}
|
|
}
|
|
break;
|
|
|
|
case LVN_ODSTATECHANGED:
|
|
{
|
|
auto OdStateChange = (NMLVODSTATECHANGE*)Msg.LParam;
|
|
if ((OdStateChange->uNewState & LVIS_SELECTED) != (OdStateChange->uOldState & LVIS_SELECTED))
|
|
{
|
|
OnVirtualItemsSelectionRangeChanged(std::make_unique<ListViewVirtualItemsSelectionRangeChangedEventArgs>(OdStateChange->iFrom, OdStateChange->iTo, (OdStateChange->uNewState & LVIS_SELECTED) != 0));
|
|
}
|
|
}
|
|
break;
|
|
|
|
case LVN_ITEMCHANGED:
|
|
{
|
|
auto ChangeInfo = (NMLISTVIEW*)Msg.LParam;
|
|
if ((ChangeInfo->uChanged & LVIF_STATE) != 0)
|
|
{
|
|
int32_t oldState = ChangeInfo->uOldState & LVIS_SELECTED;
|
|
int32_t newState = ChangeInfo->uNewState & LVIS_SELECTED;
|
|
|
|
if (newState != oldState)
|
|
{
|
|
// Trigger virtual item selection change here as well...
|
|
if (this->VirtualListSize() > 0)
|
|
{
|
|
OnVirtualItemsSelectionRangeChanged(std::make_unique<ListViewVirtualItemsSelectionRangeChangedEventArgs>(0, this->VirtualListSize() - 1, newState != 0));
|
|
}
|
|
|
|
// TODO: Trigger OnSelectedIndexChanged();
|
|
// https://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/ListView.cs,5946
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool ListView::WmNotify(Message& Msg)
|
|
{
|
|
auto NmHdr = (NMHDR*)Msg.LParam;
|
|
|
|
if (NmHdr->code == NM_CUSTOMDRAW && OwnerDraw())
|
|
{
|
|
auto NmCd = (NMCUSTOMDRAW*)Msg.LParam;
|
|
|
|
switch (NmCd->dwDrawStage)
|
|
{
|
|
case CDDS_PREPAINT:
|
|
Msg.Result = (uintptr_t)CDRF_NOTIFYITEMDRAW;
|
|
return true;
|
|
|
|
case CDDS_ITEMPREPAINT:
|
|
{
|
|
auto EventArgs = std::make_unique<DrawListViewColumnHeaderEventArgs>((HDC)NmCd->hdc, &Columns[(uint32_t)NmCd->dwItemSpec], (int32_t)NmCd->dwItemSpec, Drawing::Rectangle(NmCd->rc.left, NmCd->rc.top, NmCd->rc.right - NmCd->rc.left, NmCd->rc.bottom - NmCd->rc.top), (ListViewItemStates)NmCd->uItemState);
|
|
OnDrawColumnHeader(EventArgs);
|
|
|
|
if (EventArgs->DrawDefault)
|
|
{
|
|
Msg.Result = (uintptr_t)CDRF_DODEFAULT;
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
Msg.Result = (uintptr_t)CDRF_SKIPDEFAULT;
|
|
return true;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (NmHdr->code == NM_RELEASEDCAPTURE && GetFlag(ListViewFlags::FlagColumnClicked))
|
|
{
|
|
SetFlag(ListViewFlags::FlagColumnClicked, false);
|
|
OnColumnClick(std::make_unique<ColumnClickEventArgs>(_ColumnIndex));
|
|
}
|
|
|
|
// Still need default handling
|
|
return false;
|
|
}
|
|
|
|
void ListView::NmCustomDraw(Message& Msg)
|
|
{
|
|
bool DontMess = false;
|
|
bool ItemDrawDefault = false;
|
|
|
|
auto NmCd = (NMLVCUSTOMDRAW*)Msg.LParam;
|
|
|
|
switch (NmCd->nmcd.dwDrawStage)
|
|
{
|
|
case CDDS_PREPAINT:
|
|
if (OwnerDraw())
|
|
{
|
|
Msg.Result = (uintptr_t)CDRF_NOTIFYITEMDRAW;
|
|
return;
|
|
}
|
|
|
|
Msg.Result = (uintptr_t)CDRF_NOTIFYSUBITEMDRAW;
|
|
return;
|
|
|
|
case CDDS_ITEMPREPAINT:
|
|
{
|
|
int32_t ItemIndex = (int32_t)NmCd->nmcd.dwItemSpec;
|
|
auto ItemBounds = GetItemRectOrEmpty(ItemIndex);
|
|
|
|
if (!ClientRectangle().IntersectsWith(ItemBounds))
|
|
return;
|
|
|
|
if (OwnerDraw())
|
|
{
|
|
auto Item = this->GetSubItem(ItemIndex, 0);
|
|
auto EventArgs = std::make_unique<DrawListViewItemEventArgs>((HDC)NmCd->nmcd.hdc, Item.first, Item.second, ItemBounds, ItemIndex, (ListViewItemStates)NmCd->nmcd.uItemState);
|
|
OnDrawItem(EventArgs);
|
|
|
|
ItemDrawDefault = EventArgs->DrawDefault;
|
|
|
|
if (_ViewStyle == View::Details)
|
|
Msg.Result = (uintptr_t)CDRF_NOTIFYSUBITEMDRAW;
|
|
else if (!EventArgs->DrawDefault)
|
|
Msg.Result = (uintptr_t)CDRF_SKIPDEFAULT;
|
|
|
|
if (!EventArgs->DrawDefault)
|
|
return;
|
|
}
|
|
|
|
if (_ViewStyle == View::Details || _ViewStyle == View::Tile)
|
|
{
|
|
Msg.Result = (uintptr_t)CDRF_NOTIFYSUBITEMDRAW;
|
|
DontMess = true;
|
|
}
|
|
}
|
|
|
|
case (CDDS_SUBITEM | CDDS_ITEMPREPAINT):
|
|
{
|
|
int32_t ItemIndex = (int32_t)NmCd->nmcd.dwItemSpec;
|
|
auto ItemBounds = GetItemRectOrEmpty(ItemIndex);
|
|
|
|
if (!ClientRectangle().IntersectsWith(ItemBounds))
|
|
return;
|
|
|
|
if (OwnerDraw() && !ItemDrawDefault)
|
|
{
|
|
bool SkipCustomDrawCode = true;
|
|
|
|
if (NmCd->iSubItem < (int32_t)Columns.Count())
|
|
{
|
|
auto SubItemBounds = GetSubItemRect(ItemIndex, NmCd->iSubItem);
|
|
|
|
if (NmCd->iSubItem == 0 && Columns.Count() > 1)
|
|
{
|
|
SubItemBounds.Width = Columns[0].Width();
|
|
}
|
|
|
|
if (ClientRectangle().IntersectsWith(SubItemBounds))
|
|
{
|
|
auto Item = this->GetSubItem(ItemIndex, NmCd->iSubItem);
|
|
auto EventArgs = std::make_unique<DrawListViewSubItemEventArgs>((HDC)NmCd->nmcd.hdc, Item.first, Item.second, SubItemBounds, ItemIndex, NmCd->iSubItem, (ListViewItemStates)NmCd->nmcd.uItemState);
|
|
OnDrawSubItem(EventArgs);
|
|
|
|
SkipCustomDrawCode = !EventArgs->DrawDefault;
|
|
}
|
|
}
|
|
|
|
if (SkipCustomDrawCode)
|
|
{
|
|
Msg.Result = (uintptr_t)CDRF_SKIPDEFAULT;
|
|
return;
|
|
}
|
|
}
|
|
|
|
auto State = NmCd->nmcd.uItemState;
|
|
if (!HideSelection())
|
|
{
|
|
auto RealState = GetItemState((int32_t)NmCd->nmcd.dwItemSpec);
|
|
if ((RealState & LVIS_SELECTED) == 0)
|
|
State &= ~CDIS_SELECTED;
|
|
}
|
|
|
|
auto SubItem = ((NmCd->nmcd.dwDrawStage & CDDS_SUBITEM) != 0) ? NmCd->iSubItem : 0;
|
|
auto Item = GetSubItem(ItemIndex, SubItem);
|
|
|
|
bool ChangeColor = (GetState(ControlStates::StateEnabled) ? true : false);
|
|
if (Activation() == ItemActivation::OneClick || Activation() == ItemActivation::TwoClick)
|
|
{
|
|
if ((State & (CDIS_SELECTED | CDIS_GRAYED | CDIS_HOT | CDIS_DISABLED)) != 0)
|
|
ChangeColor = false;
|
|
}
|
|
|
|
if (ChangeColor)
|
|
{
|
|
NmCd->clrText = Drawing::ColorToWin32(Item.second.ForeColor);
|
|
NmCd->clrTextBk = Drawing::ColorToWin32(Item.second.BackColor);
|
|
}
|
|
|
|
if (!DontMess)
|
|
Msg.Result = (uintptr_t)CDRF_NEWFONT;
|
|
}
|
|
return;
|
|
|
|
default:
|
|
Msg.Result = (uintptr_t)CDRF_DODEFAULT;
|
|
return;
|
|
}
|
|
}
|
|
|
|
ListView::ColumnHeaderCollection::ColumnHeaderCollection(ListView* Owner)
|
|
: _Owner(Owner), _Columns()
|
|
{
|
|
}
|
|
|
|
void ListView::ColumnHeaderCollection::Add(const ColumnHeader& Value)
|
|
{
|
|
auto& Result = _Columns.Emplace(Value);
|
|
|
|
Result.SetListView(_Owner);
|
|
|
|
if (Result.DisplayIndex() <= -1)
|
|
Result.SetDisplayIndexInternal(_Columns.Count() - 1);
|
|
|
|
if (_Owner->GetState(ControlStates::StateCreated))
|
|
_Owner->NativeAddColumn(Result);
|
|
}
|
|
|
|
void ListView::ColumnHeaderCollection::Insert(int32_t Index, const ColumnHeader& Value)
|
|
{
|
|
_Columns.Insert(Index, Value);
|
|
|
|
auto& Result = _Columns[Index];
|
|
|
|
Result.SetListView(_Owner);
|
|
|
|
if (Result.DisplayIndex() <= -1)
|
|
Result.SetDisplayIndexInternal(Index);
|
|
|
|
if (_Owner->GetState(ControlStates::StateCreated))
|
|
_Owner->NativeInsertColumn(Index, Result);
|
|
}
|
|
|
|
void ListView::ColumnHeaderCollection::Clear()
|
|
{
|
|
_Owner->NativeClearColumns();
|
|
_Columns.Clear();
|
|
}
|
|
|
|
int32_t ListView::ColumnHeaderCollection::IndexOf(const ColumnHeader& Value)
|
|
{
|
|
return this->_Columns.IndexOf(Value);
|
|
}
|
|
|
|
uint32_t ListView::ColumnHeaderCollection::Count()
|
|
{
|
|
return this->_Columns.Count();
|
|
}
|
|
|
|
ColumnHeader* ListView::ColumnHeaderCollection::begin() const
|
|
{
|
|
return this->_Columns.begin();
|
|
}
|
|
|
|
ColumnHeader* ListView::ColumnHeaderCollection::end() const
|
|
{
|
|
return this->_Columns.end();
|
|
}
|
|
|
|
ColumnHeader& ListView::ColumnHeaderCollection::operator[](uint32_t Index)
|
|
{
|
|
return this->_Columns[Index];
|
|
}
|
|
|
|
ListView::ListViewItemCollection::ListViewItemCollection(ListView* Owner)
|
|
: _Owner(Owner)
|
|
{
|
|
}
|
|
|
|
void ListView::ListViewItemCollection::Add(const ListViewItem& Value)
|
|
{
|
|
_Items.EmplaceBack(Value);
|
|
|
|
if (_Owner->GetState(ControlStates::StateCreated))
|
|
_Owner->NativeInsertItem(_Items.Count(), Value);
|
|
}
|
|
|
|
void ListView::ListViewItemCollection::Insert(int32_t Index, const ListViewItem& Value)
|
|
{
|
|
_Items.Insert(Index, Value);
|
|
|
|
if (_Owner->GetState(ControlStates::StateCreated))
|
|
_Owner->NativeInsertItem(Index, Value);
|
|
}
|
|
|
|
void ListView::ListViewItemCollection::RemoveAt(int32_t Index)
|
|
{
|
|
_Items.RemoveAt(Index);
|
|
|
|
if (_Owner->GetState(ControlStates::StateCreated))
|
|
_Owner->NativeRemoveItem(Index);
|
|
}
|
|
|
|
void ListView::ListViewItemCollection::Clear()
|
|
{
|
|
_Items.Clear();
|
|
|
|
if (_Owner->GetState(ControlStates::StateCreated))
|
|
_Owner->NativeClearItems();
|
|
}
|
|
|
|
uint32_t ListView::ListViewItemCollection::Count()
|
|
{
|
|
return (_Owner->VirtualMode() ? (uint32_t)_Owner->_VirtualListSize : this->_Items.Count());
|
|
}
|
|
|
|
ListViewItem& ListView::ListViewItemCollection::operator[](uint32_t Index)
|
|
{
|
|
return _Items[Index];
|
|
}
|
|
|
|
ListViewItem* ListView::ListViewItemCollection::begin() const
|
|
{
|
|
return _Items.begin();
|
|
}
|
|
|
|
ListViewItem* ListView::ListViewItemCollection::end() const
|
|
{
|
|
return _Items.end();
|
|
}
|
|
|
|
const List<ListViewItem>& ListView::ListViewItemCollection::ToList()
|
|
{
|
|
return _Items;
|
|
}
|
|
}
|