1079 lines
32 KiB
C++
1079 lines
32 KiB
C++
#include "stdafx.h"
|
|
#include "CListControl.h"
|
|
#include "CListControlHeaderImpl.h" // redundant but makes intelisense quit showing false errors
|
|
#include "CListControl-Cells.h"
|
|
#include "PaintUtils.h"
|
|
#include "GDIUtils.h"
|
|
#include "win32_utility.h"
|
|
|
|
enum {
|
|
lineBelowHeaderCY = 1
|
|
};
|
|
|
|
static bool testDrawLineBelowHeader() {
|
|
// Win10
|
|
return GetOSVersionCode() >= 0xA00;
|
|
}
|
|
|
|
void CListControlHeaderImpl::InitializeHeaderCtrl(DWORD flags) {
|
|
PFC_ASSERT(!IsHeaderEnabled());
|
|
WIN32_OP_D( m_header.Create(*this,NULL,NULL,WS_CHILD | flags) != NULL );
|
|
m_header.SetFont( GetFont() );
|
|
|
|
if (testDrawLineBelowHeader()) {
|
|
WIN32_OP_D( m_headerLine.Create( *this, NULL, NULL, WS_CHILD ) != NULL );
|
|
}
|
|
|
|
UpdateHeaderLayout();
|
|
}
|
|
|
|
void CListControlHeaderImpl::UpdateHeaderLayout() {
|
|
CRect client; WIN32_OP_D( GetClientRect(client) );
|
|
m_clientWidth = client.Width();
|
|
if (IsHeaderEnabled()) {
|
|
auto rc = client;
|
|
rc.left -= GetViewOffset().x;
|
|
WINDOWPOS wPos = {};
|
|
HDLAYOUT layout = {&rc, &wPos};
|
|
if (m_header.Layout(&layout)) {
|
|
m_header.SetWindowPos(wPos.hwndInsertAfter,wPos.x,wPos.y,wPos.cx,wPos.cy,wPos.flags | SWP_SHOWWINDOW);
|
|
if (m_headerLine != NULL) m_headerLine.SetWindowPos(m_header, wPos.x, wPos.y + wPos.cy, wPos.cx, lineBelowHeaderCY, wPos.flags | SWP_SHOWWINDOW);
|
|
} else {
|
|
m_header.ShowWindow(SW_HIDE);
|
|
if (m_headerLine != NULL) m_headerLine.ShowWindow(SW_HIDE);
|
|
}
|
|
}
|
|
}
|
|
|
|
int CListControlHeaderImpl::GetItemWidth() const {
|
|
if (IsHeaderEnabled()) return m_itemWidth;
|
|
else return m_clientWidth;
|
|
}
|
|
|
|
LRESULT CListControlHeaderImpl::OnSizePassThru(UINT,WPARAM,LPARAM p_lp) {
|
|
UpdateHeaderLayout();
|
|
|
|
ProcessAutoWidth();
|
|
|
|
SetMsgHandled(FALSE);
|
|
return 0;
|
|
}
|
|
|
|
void CListControlHeaderImpl::OnViewOriginChange(CPoint p_delta) {
|
|
TParent::OnViewOriginChange(p_delta);
|
|
if (p_delta.x != 0) UpdateHeaderLayout();
|
|
}
|
|
|
|
void CListControlHeaderImpl::SetHeaderFont(HFONT font) {
|
|
if (IsHeaderEnabled()) {
|
|
m_header.SetFont(font); UpdateHeaderLayout();
|
|
}
|
|
}
|
|
|
|
LRESULT CListControlHeaderImpl::OnDividerDoubleClick(int,LPNMHDR hdr,BOOL&) {
|
|
const NMHEADER * info = (const NMHEADER *) hdr;
|
|
if (info->iButton == 0) {
|
|
AutoColumnWidth((t_size)info->iItem);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CListControlHeaderImpl::OnHeaderItemClick(int,LPNMHDR p_hdr,BOOL&) {
|
|
const NMHEADER * info = (const NMHEADER *) p_hdr;
|
|
if (info->iButton == 0) {
|
|
OnColumnHeaderClick((t_uint32)info->iItem);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CListControlHeaderImpl::OnHeaderItemChanged(int,LPNMHDR p_hdr,BOOL&) {
|
|
const NMHEADER * info = (const NMHEADER*) p_hdr;
|
|
if (info->pitem->mask & (HDI_WIDTH | HDI_ORDER)) {
|
|
if(!m_ownColumnsChange) ProcessColumnsChange();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CListControlHeaderImpl::OnHeaderEndDrag(int,LPNMHDR hdr,BOOL&) {
|
|
NMHEADER * info = (NMHEADER*) hdr;
|
|
return OnColumnHeaderDrag(info->iItem,info->pitem->iOrder) ? TRUE : FALSE;
|
|
}
|
|
|
|
bool CListControlHeaderImpl::OnColumnHeaderDrag(t_size index, t_size newPos) {
|
|
index = GetSubItemOrder(index);
|
|
const t_size count = this->GetColumnCount();
|
|
if ( count == 0 ) return false;
|
|
std::vector<size_t> perm; perm.resize(count); pfc::create_move_items_permutation(&perm[0],count, pfc::bit_array_one(index), (int) newPos - (int) index );
|
|
std::vector<int> order, newOrder; order.resize(count); newOrder.resize(count);
|
|
WIN32_OP_D(m_header.GetOrderArray((int)count, &order[0]));
|
|
for(t_size walk = 0; walk < count; ++walk) newOrder[walk] = order[perm[walk]];
|
|
WIN32_OP_D(m_header.SetOrderArray((int)count, &newOrder[0]));
|
|
OnColumnsChanged();
|
|
return true;
|
|
}
|
|
t_size CListControlHeaderImpl::SubItemFromPointAbs(CPoint pt) const {
|
|
|
|
auto order = GetColumnOrderArray();
|
|
const t_size colCount = order.size();
|
|
|
|
size_t item;
|
|
if (! ItemFromPointAbs(pt, item ) ) item = pfc_infinite;
|
|
|
|
long xWalk = 0;
|
|
for(t_size _walk = 0; _walk < colCount; ) {
|
|
const t_size walk = (t_size) order[_walk];
|
|
|
|
size_t span = 1;
|
|
if (item != pfc_infinite) span = this->GetSubItemSpan(item, walk);
|
|
|
|
PFC_ASSERT( span == 1 || _walk == walk );
|
|
|
|
if ( walk + span > colCount ) span = colCount - walk;
|
|
|
|
long width = 0;
|
|
for( size_t sub = 0; sub < span; ++ sub ) {
|
|
width += (long)this->GetSubItemWidth(walk + sub);
|
|
}
|
|
|
|
if (xWalk + width > pt.x) return walk;
|
|
xWalk += width;
|
|
_walk += span;
|
|
}
|
|
return pfc_infinite;
|
|
}
|
|
|
|
bool CListControlHeaderImpl::OnClickedSpecial(DWORD status, CPoint pt) {
|
|
|
|
const DWORD maskButtons = MK_LBUTTON | MK_RBUTTON | MK_MBUTTON | MK_XBUTTON1 | MK_XBUTTON2;
|
|
|
|
if ( (status & maskButtons) != MK_LBUTTON ) return false;
|
|
|
|
if (!GetCellTypeSupported()) return false;
|
|
|
|
size_t item; size_t subItem;
|
|
if (! this->GetItemAtPointAbsEx( pt + GetViewOffset(), item, subItem ) ) {
|
|
return false;
|
|
}
|
|
|
|
bool bCapture = false;
|
|
auto cellType = GetCellType(item, subItem);
|
|
if ( !CellTypeReactsToMouseOver( cellType ) ) return false;
|
|
auto rcHot = CellHotRect( item, subItem, cellType );
|
|
if (!rcHot.PtInRect( pt )) return false;
|
|
|
|
SetPressedItem(item, subItem);
|
|
SetCaptureEx([=](UINT, DWORD newStatus, CPoint pt) {
|
|
|
|
{
|
|
CRect rc = this->GetClientRectHook();
|
|
if (!rc.PtInRect(pt)) {
|
|
ClearPressedItem(); return false;
|
|
}
|
|
}
|
|
|
|
size_t newItem, newSubItem;
|
|
if (!this->GetItemAtPointAbsEx(pt + GetViewOffset(), newItem, newSubItem) || newItem != item || newSubItem != subItem) {
|
|
ClearPressedItem(); return false;
|
|
}
|
|
|
|
DWORD buttons = newStatus & maskButtons;
|
|
if (buttons == 0) {
|
|
// button released?
|
|
this->defer( [=] {
|
|
OnSubItemClicked(item, subItem, pt);
|
|
} );
|
|
ClearPressedItem(); return false;
|
|
}
|
|
if (buttons != MK_LBUTTON) {
|
|
// another button pressed?
|
|
ClearPressedItem(); return false;
|
|
}
|
|
|
|
return true;
|
|
});
|
|
return true;
|
|
}
|
|
|
|
bool CListControlHeaderImpl::CellTypeUsesSpecialHitTests( cellType_t ct ) {
|
|
if ( ct == nullptr ) return false;
|
|
return ct->SuppressRowSelect();
|
|
}
|
|
|
|
bool CListControlHeaderImpl::OnClickedSpecialHitTest(CPoint pt) {
|
|
if ( ! GetCellTypeSupported() ) return false;
|
|
auto ct = GetCellTypeAtPointAbs( pt + GetViewOffset() );
|
|
return CellTypeUsesSpecialHitTests(ct);
|
|
}
|
|
|
|
void CListControlHeaderImpl::OnItemClicked(t_size item, CPoint pt) {
|
|
t_size subItem = SubItemFromPointAbs(pt + GetViewOffset());
|
|
if (subItem != ~0) {
|
|
if ( this->GetCellTypeSupported() ) {
|
|
auto ct = this->GetCellType(item, subItem );
|
|
// we don't handle hyperlink & button clicks thru here
|
|
if (CellTypeUsesSpecialHitTests(ct)) return;
|
|
}
|
|
OnSubItemClicked(item, subItem, pt);
|
|
}
|
|
}
|
|
|
|
std::vector<int> CListControlHeaderImpl::GetColumnOrderArray() const {
|
|
const size_t cCount = this->GetColumnCount();
|
|
std::vector<int> order;
|
|
if ( cCount > 0 ) {
|
|
order.resize(cCount);
|
|
if (IsHeaderEnabled()) {
|
|
WIN32_OP_D(m_header.GetOrderArray((int)cCount, &order[0]));
|
|
} else {
|
|
for (size_t c = 0; c < cCount; ++c) order[c] = (int)c;
|
|
}
|
|
}
|
|
return order;
|
|
}
|
|
|
|
void CListControlHeaderImpl::RenderItemText(t_size item,const CRect & itemRect,const CRect & updateRect,CDCHandle dc, bool allowColors) {
|
|
|
|
t_uint32 xWalk = itemRect.left;
|
|
CRect subItemRect(itemRect);
|
|
auto order = GetColumnOrderArray();
|
|
const size_t cCount = order.size();
|
|
SelectObjectScope fontScope(dc,GetFont());
|
|
for(t_size _walk = 0; _walk < cCount; ) {
|
|
const t_size walk = order[_walk];
|
|
|
|
size_t span = GetSubItemSpan(item, walk);
|
|
|
|
PFC_ASSERT( walk == _walk || span == 1 );
|
|
|
|
t_uint32 width = GetSubItemWidth(walk);
|
|
|
|
if ( span > 1 ) {
|
|
if ( walk + span > cCount ) span = cCount - walk;
|
|
for( size_t extraWalk = 1; extraWalk < span; ++ extraWalk ) {
|
|
width += GetSubItemWidth(walk + extraWalk);
|
|
}
|
|
}
|
|
|
|
subItemRect.left = xWalk; subItemRect.right = xWalk + width;
|
|
CRect subUpdate;
|
|
if (subUpdate.IntersectRect(subItemRect, updateRect)) {
|
|
DCStateScope scope(dc);
|
|
if (dc.IntersectClipRect(subItemRect) != NULLREGION) {
|
|
RenderSubItemText(item,walk,subItemRect,subUpdate,dc, allowColors);
|
|
}
|
|
}
|
|
xWalk += width;
|
|
|
|
_walk += span;
|
|
}
|
|
}
|
|
|
|
t_size CListControlHeaderImpl::GetSubItemOrder(t_size subItem) const {
|
|
if ( ! IsHeaderEnabled( ) ) return subItem;
|
|
HDITEM hditem = {};
|
|
hditem.mask = HDI_ORDER;
|
|
WIN32_OP_D( m_header.GetItem( (int) subItem, &hditem ) );
|
|
return (t_size) hditem.iOrder;
|
|
}
|
|
|
|
size_t CListControlHeaderImpl::GetSubItemSpan(size_t row, size_t column) const {
|
|
return 1;
|
|
}
|
|
|
|
uint32_t CListControlHeaderImpl::GetSubItemWidth(t_size subItem) const {
|
|
if ( ! IsHeaderEnabled( ) ) {
|
|
// Should be overridden for custom columns layout
|
|
PFC_ASSERT( GetColumnCount() == 1 );
|
|
PFC_ASSERT( subItem == 0 );
|
|
return GetItemWidth();
|
|
}
|
|
|
|
if ( subItem < m_colRuntime.size() ) return m_colRuntime[subItem].m_widthPixels;
|
|
PFC_ASSERT( !"bad column idx");
|
|
return 0;
|
|
}
|
|
|
|
int CListControlHeaderImpl::GetHeaderItemWidth( int which ) {
|
|
HDITEM hditem = {};
|
|
hditem.mask = HDI_WIDTH;
|
|
WIN32_OP_D( m_header.GetItem( which, &hditem) );
|
|
return hditem.cxy;
|
|
}
|
|
|
|
void CListControlHeaderImpl::OnColumnsChanged() {
|
|
if ( IsHeaderEnabled() ) {
|
|
for( size_t walk = 0; walk < m_colRuntime.size(); ++ walk ) {
|
|
m_colRuntime[walk].m_widthPixels = GetHeaderItemWidth( (int) walk );
|
|
}
|
|
RecalcItemWidth();
|
|
}
|
|
this->OnViewAreaChanged();
|
|
}
|
|
|
|
void CListControlHeaderImpl::ResetColumns(bool update) {
|
|
m_colRuntime.clear();
|
|
m_itemWidth = 0;
|
|
PFC_ASSERT(IsHeaderEnabled());
|
|
for(;;) {
|
|
int count = m_header.GetItemCount();
|
|
if (count <= 0) break;
|
|
m_header.DeleteItem(count - 1);
|
|
}
|
|
if (update) OnColumnsChanged();
|
|
}
|
|
|
|
void CListControlHeaderImpl::SetColumn( size_t which, const char * label, DWORD fmtFlags, bool updateView) {
|
|
PFC_ASSERT( IsHeaderEnabled() );
|
|
pfc::stringcvt::string_os_from_utf8 labelOS(label);
|
|
|
|
HDITEM item = {};
|
|
item.mask = HDI_TEXT | HDI_FORMAT;
|
|
item.fmt = fmtFlags | HDF_STRING;
|
|
item.pszText = const_cast<TCHAR*>(labelOS.get_ptr());
|
|
m_header.SetItem( (int) which, &item );
|
|
|
|
if (which < m_colRuntime.size()) m_colRuntime[which].m_text = label;
|
|
|
|
if (updateView) OnColumnsChanged();
|
|
}
|
|
|
|
void CListControlHeaderImpl::GetColumnText(size_t which, pfc::string_base & out) const {
|
|
if (which < m_colRuntime.size()) {
|
|
out = m_colRuntime[which].m_text.c_str();
|
|
} else {
|
|
out = "";
|
|
}
|
|
}
|
|
|
|
void CListControlHeaderImpl::ResizeColumn(t_size index, t_uint32 widthPixels, bool updateView) {
|
|
PFC_ASSERT( IsHeaderEnabled() );
|
|
PFC_ASSERT( index < m_colRuntime.size() );
|
|
HDITEM item = {};
|
|
item.mask = HDI_WIDTH;
|
|
item.cxy = widthPixels;
|
|
{ pfc::vartoggle_t<bool> scope(m_ownColumnsChange, true); m_header.SetItem( (int) index, &item ); }
|
|
m_colRuntime[index].m_widthPixels = widthPixels;
|
|
RecalcItemWidth();
|
|
if (updateView) OnColumnsChanged();
|
|
}
|
|
|
|
void CListControlHeaderImpl::DeleteColumns( pfc::bit_array const & mask, bool updateView ) {
|
|
int nDeleted = 0;
|
|
const size_t oldCount = GetColumnCount();
|
|
mask.for_each(true, 0, oldCount, [&] (size_t idx) {
|
|
int iDelete = (int) idx - nDeleted;
|
|
bool bDeleted = m_header.DeleteItem( iDelete );
|
|
PFC_ASSERT( bDeleted );
|
|
if ( bDeleted ) ++ nDeleted;
|
|
} );
|
|
|
|
pfc::remove_mask_t( m_colRuntime, mask );
|
|
|
|
ColumnWidthFix();
|
|
|
|
if (updateView) {
|
|
OnColumnsChanged();
|
|
}
|
|
|
|
}
|
|
|
|
bool CListControlHeaderImpl::DeleteColumn(size_t index, bool update) {
|
|
PFC_ASSERT( IsHeaderEnabled() );
|
|
|
|
if (!m_header.DeleteItem( (int) index )) return false;
|
|
|
|
pfc::remove_mask_t( m_colRuntime, pfc::bit_array_one( index ) );
|
|
|
|
ColumnWidthFix();
|
|
|
|
if (update) {
|
|
OnColumnsChanged();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CListControlHeaderImpl::SetSortIndicator( size_t whichColumn, bool isUp ) {
|
|
HeaderControl_SetSortIndicator( GetHeaderCtrl(), (int) whichColumn, isUp );
|
|
}
|
|
|
|
void CListControlHeaderImpl::ClearSortIndicator() {
|
|
HeaderControl_SetSortIndicator(GetHeaderCtrl(), -1, false);
|
|
}
|
|
|
|
bool CListControlHeaderImpl::HaveAutoWidthContentColumns() const {
|
|
for( auto i = m_colRuntime.begin(); i != m_colRuntime.end(); ++ i ) {
|
|
if ( i->autoWidthContent() ) return true;
|
|
}
|
|
return false;
|
|
}
|
|
bool CListControlHeaderImpl::HaveAutoWidthColumns() const {
|
|
for( auto i = m_colRuntime.begin(); i != m_colRuntime.end(); ++ i ) {
|
|
if ( i->autoWidth() ) return true;
|
|
}
|
|
return false;
|
|
}
|
|
void CListControlHeaderImpl::AddColumnEx( const char * label, uint32_t widthPixelsAt96DPI, DWORD fmtFlags, bool update ) {
|
|
uint32_t w = widthPixelsAt96DPI;
|
|
if ( w <= columnWidthMax ) {
|
|
w = MulDiv( w, m_dpi.cx, 96 );
|
|
}
|
|
AddColumn( label, w, fmtFlags, update );
|
|
}
|
|
|
|
void CListControlHeaderImpl::AddColumnDLU( const char * label, uint32_t widthDLU, DWORD fmtFlags, bool update ) {
|
|
uint32_t w = widthDLU;
|
|
if ( w <= columnWidthMax ) {
|
|
w = ::MapDialogWidth( GetParent(), w );
|
|
}
|
|
AddColumn( label, w, fmtFlags, update );
|
|
}
|
|
void CListControlHeaderImpl::AddColumnF( const char * label, float widthF, DWORD fmtFlags, bool update) {
|
|
uint32_t w = columnWidthMax;
|
|
if ( widthF >= 0 ) {
|
|
w = pfc::rint32( widthF * m_dpi.cx / 96.0f );
|
|
}
|
|
AddColumn( label, w, fmtFlags, update );
|
|
}
|
|
void CListControlHeaderImpl::AddColumn(const char * label, uint32_t width, DWORD fmtFlags,bool update) {
|
|
if (! IsHeaderEnabled( ) ) InitializeHeaderCtrl();
|
|
|
|
pfc::stringcvt::string_os_from_utf8 labelOS(label);
|
|
HDITEM item = {};
|
|
item.mask = HDI_TEXT | HDI_FORMAT;
|
|
if ( width != UINT32_MAX ) {
|
|
item.cxy = width;
|
|
item.mask |= HDI_WIDTH;
|
|
}
|
|
|
|
item.pszText = const_cast<TCHAR*>(labelOS.get_ptr());
|
|
item.fmt = HDF_STRING | fmtFlags;
|
|
int iColumn;
|
|
WIN32_OP_D( (iColumn = m_header.InsertItem(m_header.GetItemCount(),&item) ) >= 0 );
|
|
colRuntime_t rt;
|
|
rt.m_text = label;
|
|
rt.m_userWidth = width;
|
|
if ( width <= columnWidthMax ) {
|
|
m_itemWidth += width;
|
|
rt.m_widthPixels = width;
|
|
}
|
|
m_colRuntime.push_back( std::move(rt) );
|
|
|
|
if (update) OnColumnsChanged();
|
|
|
|
ProcessAutoWidth();
|
|
}
|
|
|
|
float CListControlHeaderImpl::GetColumnWidthF(size_t subItem) const {
|
|
auto w = GetSubItemWidth(subItem);
|
|
return (float) w * 96.0f / (float)m_dpi.cx;
|
|
}
|
|
|
|
void CListControlHeaderImpl::RenderBackground(CDCHandle dc, CRect const& rc) {
|
|
__super::RenderBackground(dc,rc);
|
|
#if 0
|
|
if ( m_drawLineBelowHeader && IsHeaderEnabled()) {
|
|
CRect rcHeader;
|
|
if (m_header.GetWindowRect(rcHeader)) {
|
|
// Draw a grid line below header
|
|
int y = rcHeader.Height();
|
|
if ( y >= rc.top && y < rc.bottom ) {
|
|
CDCPen pen(dc, GridColor());
|
|
SelectObjectScope scope(dc, pen);
|
|
dc.MoveTo(rc.left, y);
|
|
dc.LineTo(rc.right, y);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
CRect CListControlHeaderImpl::GetClientRectHook() const {
|
|
CRect rcClient = __super::GetClientRectHook();
|
|
if (m_header != NULL) {
|
|
PFC_ASSERT( m_header.IsWindow() );
|
|
CRect rcHeader;
|
|
if (m_header.GetWindowRect(rcHeader)) {
|
|
int h = rcHeader.Height();
|
|
if ( m_headerLine != NULL ) h += lineBelowHeaderCY;
|
|
rcClient.top = pfc::min_t(rcClient.bottom,rcClient.top + h);
|
|
}
|
|
}
|
|
return rcClient;
|
|
}
|
|
|
|
CRect CListControlHeaderImpl::GetItemTextRectHook(size_t, size_t, CRect const & rc) const {
|
|
return GetItemTextRect( rc );
|
|
}
|
|
|
|
CRect CListControlHeaderImpl::GetItemTextRect(const CRect & itemRect) const {
|
|
CRect rc ( itemRect );
|
|
rc.DeflateRect(GetColumnSpacing(),0);
|
|
return rc;
|
|
}
|
|
|
|
void CListControlHeaderImpl::SetColumnSort(t_size which, bool isUp) {
|
|
HeaderControl_SetSortIndicator(m_header,(int)which,isUp);
|
|
}
|
|
|
|
void CListControlHeaderImpl::SetColumnFormat(t_size which, DWORD format) {
|
|
HDITEM item = {};
|
|
item.mask = HDI_FORMAT;
|
|
item.fmt = HDF_STRING | format;
|
|
WIN32_OP_D( m_header.SetItem((int)which,&item) );
|
|
}
|
|
|
|
DWORD CListControlHeaderImpl::GetColumnFormat(t_size which) const {
|
|
if (!IsHeaderEnabled()) return HDF_LEFT;
|
|
HDITEM hditem = {};
|
|
hditem.mask = HDI_FORMAT;
|
|
WIN32_OP_D( m_header.GetItem( (int) which, &hditem) );
|
|
return hditem.fmt;
|
|
}
|
|
|
|
BOOL CListControlHeaderImpl::OnSetCursor(CWindow wnd, UINT nHitTest, UINT message) {
|
|
#if 0
|
|
// no longer meaningful since SetCapture on mouse over was added to track hot status
|
|
if ( message != 0 && GetCellTypeSupported() ) {
|
|
CPoint pt( (LPARAM) GetMessagePos() );
|
|
WIN32_OP_D( ScreenToClient( &pt ) );
|
|
if ( GetCellTypeAtPointAbs( pt + GetViewOffset() ) == cell_hyperlink ) {
|
|
SetCursor(LoadCursor(NULL, IDC_HAND)); return TRUE;
|
|
}
|
|
}
|
|
#endif
|
|
SetMsgHandled(FALSE); return FALSE;
|
|
}
|
|
|
|
bool CListControlHeaderImpl::GetItemAtPointAbsEx(CPoint pt, size_t & outItem, size_t & outSubItem) const {
|
|
size_t item, subItem;
|
|
if (ItemFromPointAbs(pt, item)) {
|
|
subItem = SubItemFromPointAbs(pt);
|
|
if (subItem != pfc_infinite) {
|
|
outItem = item; outSubItem = subItem; return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
CListControlHeaderImpl::cellType_t CListControlHeaderImpl::GetCellTypeAtPointAbs(CPoint pt) const {
|
|
size_t item, subItem;
|
|
if ( GetItemAtPointAbsEx( pt, item, subItem) ) {
|
|
return GetCellType( item, subItem );
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void CListControlHeaderImpl::RenderSubItemTextInternal(t_size subItem, const CRect & subItemRect, CDCHandle dc, const char * text, bool allowColors) {
|
|
pfc::stringcvt::string_os_from_utf8 cvt(text);
|
|
CRect clip = GetItemTextRect(subItemRect);
|
|
const t_uint32 format = PaintUtils::DrawText_TranslateHeaderAlignment(GetColumnFormat(subItem));
|
|
if (true) {
|
|
const t_uint32 bk = dc.GetBkColor();
|
|
const t_uint32 fg = dc.GetTextColor();
|
|
const t_uint32 hl = (allowColors ? GetSysColorHook(colorHighlight) : fg);
|
|
const t_uint32 colors[3] = {PaintUtils::BlendColor(bk, fg, 33), fg, hl};
|
|
|
|
PaintUtils::TextOutColorsEx(dc, cvt, clip, format, colors);
|
|
|
|
dc.SetTextColor(fg);
|
|
} else {
|
|
dc.DrawText(cvt,(int)cvt.length(),clip,DT_NOPREFIX | DT_END_ELLIPSIS | DT_SINGLELINE | DT_VCENTER | format );
|
|
}
|
|
}
|
|
CListCell * CListControlHeaderImpl::GetCellType( size_t item, size_t subItem ) const {
|
|
return &PFC_SINGLETON(CListCell_Text);
|
|
}
|
|
void CListControlHeaderImpl::RenderSubItemText(t_size item, t_size subItem,const CRect & subItemRect,const CRect & updateRect,CDCHandle dc, bool allowColors) {
|
|
const auto cellType = GetCellType( item, subItem );
|
|
if ( cellType == nullptr ) {
|
|
PFC_ASSERT( !"Should not get here" );
|
|
return;
|
|
}
|
|
pfc::string_formatter label;
|
|
const bool bHaveText = GetSubItemText(item,subItem,label);
|
|
if (! bHaveText ) {
|
|
label = ""; //sanity
|
|
}
|
|
|
|
pfc::stringcvt::string_os_from_utf8_fast labelOS ( label );
|
|
CListCell::DrawContentArg_t arg;
|
|
arg.hdrFormat = GetColumnFormat( subItem );
|
|
arg.subItemRect = subItemRect;
|
|
arg.dc = dc;
|
|
arg.text = labelOS.get_ptr();
|
|
arg.allowColors = allowColors;
|
|
bool bPressed;
|
|
if ( cellType->IsToggle() ) bPressed = this->GetCellCheckState(item, subItem);
|
|
else bPressed = (item == m_pressedItem) && (subItem == m_pressedSubItem);
|
|
bool bHot = (item == m_hotItem) && ( subItem == m_hotSubItem );
|
|
if ( bPressed ) arg.cellState |= CListCell::cellState_pressed;
|
|
if ( bHot ) arg.cellState|= CListCell::cellState_hot;
|
|
arg.rcText = GetItemTextRectHook(item, subItem, subItemRect);
|
|
arg.rcHot = CellHotRect(item, subItem, cellType, subItemRect);
|
|
|
|
auto strTheme = cellType->Theme();
|
|
if ( strTheme != nullptr ) {
|
|
arg.theme = themeFor( strTheme ).m_theme;
|
|
}
|
|
arg.colorHighlight = GetSysColorHook(colorHighlight);
|
|
|
|
arg.thisWnd = m_hWnd;
|
|
|
|
if (this->IsSubItemGrayed( item, subItem ) ) {
|
|
arg.cellState |= CListCell::cellState_disabled;
|
|
}
|
|
|
|
CFontHandle fontRestore;
|
|
CFont fontOverride;
|
|
|
|
LOGFONT data;
|
|
if (dc.GetCurrentFont().GetLogFont(&data)) {
|
|
if ( cellType->ApplyTextStyle( data, CellTextScale(item, subItem ), arg.cellState ) ) {
|
|
if (fontOverride.CreateFontIndirect( & data )) {
|
|
fontRestore = dc.SelectFont( fontOverride );
|
|
}
|
|
}
|
|
}
|
|
cellType->DrawContent( arg );
|
|
|
|
if ( fontRestore != NULL ) dc.SelectFont( fontRestore );
|
|
}
|
|
|
|
void CListControlHeaderImpl::RenderGroupHeaderText(int id,const CRect & headerRect,const CRect & updateRect,CDCHandle dc) {
|
|
pfc::string_formatter label;
|
|
if (GetGroupHeaderText(id,label)) {
|
|
SelectObjectScope fontScope(dc,GetGroupHeaderFont());
|
|
pfc::stringcvt::string_os_from_utf8 cvt(label);
|
|
CRect contentRect(GetItemTextRect(headerRect));
|
|
dc.DrawText(cvt,(int)cvt.length(),contentRect,DT_NOPREFIX | DT_END_ELLIPSIS | DT_SINGLELINE | DT_VCENTER | DT_LEFT );
|
|
SIZE txSize;
|
|
const int lineSpacing = contentRect.Height() / 2;
|
|
if (dc.GetTextExtent(cvt,(int)cvt.length(),&txSize)) {
|
|
if (txSize.cx + lineSpacing < contentRect.Width()) {
|
|
const CPoint center = contentRect.CenterPoint();
|
|
const CPoint pt1(contentRect.left + txSize.cx + lineSpacing, center.y), pt2(contentRect.right, center.y);
|
|
const COLORREF lineColor = PaintUtils::BlendColor(dc.GetTextColor(),dc.GetBkColor(),25);
|
|
|
|
#ifndef CListControl_ScrollWindowFix
|
|
#error FIXME CMemoryDC needed
|
|
#endif
|
|
PaintUtils::DrawSmoothedLine(dc, pt1, pt2, lineColor, 1.0 * (double)m_dpi.cy / 96.0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
uint32_t CListControlHeaderImpl::GetOptimalColumnWidthFixed(const char * fixedText) const {
|
|
CWindowDC dc(*this);
|
|
SelectObjectScope fontScope(dc, GetFont());
|
|
GetOptimalWidth_Cache cache;
|
|
cache.m_dc = dc;
|
|
cache.m_stringTemp = fixedText;
|
|
return cache.GetStringTempWidth() + this->GetColumnSpacing() * 2;
|
|
}
|
|
|
|
t_uint32 CListControlHeaderImpl::GetOptimalSubItemWidth(t_size item, t_size subItem, GetOptimalWidth_Cache & cache) const {
|
|
const t_uint32 base = this->GetColumnSpacing() * 2;
|
|
if (GetSubItemText(item,subItem,cache.m_stringTemp)) {
|
|
return base + cache.GetStringTempWidth();
|
|
} else {
|
|
return base;
|
|
}
|
|
}
|
|
|
|
t_uint32 CListControlHeaderImpl::GetOptimalWidth_Cache::GetStringTempWidth() {
|
|
if (m_stringTemp.replace_string_ex(m_stringTempUnfuckAmpersands, "&", "&&") > 0) {
|
|
m_convertTemp.convert(m_stringTempUnfuckAmpersands);
|
|
} else {
|
|
m_convertTemp.convert(m_stringTemp);
|
|
}
|
|
return PaintUtils::TextOutColors_CalcWidth(m_dc, m_convertTemp);
|
|
}
|
|
|
|
t_uint32 CListControlHeaderImpl::GetOptimalColumnWidth(t_size which, GetOptimalWidth_Cache & cache) const {
|
|
const t_size totalItems = GetItemCount();
|
|
t_uint32 val = 0;
|
|
for(t_size item = 0; item < totalItems; ++item) {
|
|
pfc::max_acc( val, GetOptimalSubItemWidth( item, which, cache ) );
|
|
}
|
|
return val;
|
|
}
|
|
|
|
t_uint32 CListControlHeaderImpl::GetOptimalSubItemWidthSimple(t_size item, t_size subItem) const {
|
|
CWindowDC dc(*this);
|
|
SelectObjectScope fontScope(dc, GetFont() );
|
|
GetOptimalWidth_Cache cache;
|
|
cache.m_dc = dc;
|
|
return GetOptimalSubItemWidth(item, subItem, cache);
|
|
}
|
|
|
|
LRESULT CListControlHeaderImpl::OnKeyDown(UINT,WPARAM wp,LPARAM,BOOL& bHandled) {
|
|
switch(wp) {
|
|
case VK_ADD:
|
|
if (IsKeyPressed(VK_CONTROL)) {
|
|
AutoColumnWidths();
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
bHandled = FALSE;
|
|
return 0;
|
|
}
|
|
|
|
uint32_t CListControlHeaderImpl::GetOptimalColumnWidth(size_t colIndex) const {
|
|
CWindowDC dc(*this);
|
|
SelectObjectScope fontScope(dc, GetFont());
|
|
GetOptimalWidth_Cache cache;
|
|
cache.m_dc = dc;
|
|
uint32_t ret = 0;
|
|
const auto itemCount = GetItemCount();
|
|
for (t_size walk = 0; walk < itemCount; ++walk) {
|
|
pfc::max_acc(ret, GetOptimalSubItemWidth(walk, colIndex, cache));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void CListControlHeaderImpl::AutoColumnWidths(const pfc::bit_array & mask, bool expandLast) {
|
|
PFC_ASSERT( IsHeaderEnabled() );
|
|
if (!IsHeaderEnabled()) return;
|
|
const t_size itemCount = GetItemCount();
|
|
if (itemCount == 0) return;
|
|
const t_size columnCount = (t_size) m_header.GetItemCount();
|
|
if (columnCount == 0) return;
|
|
pfc::array_t<t_uint32> widths; widths.set_size(columnCount); widths.fill_null();
|
|
{
|
|
CWindowDC dc(*this);
|
|
SelectObjectScope fontScope(dc,GetFont());
|
|
GetOptimalWidth_Cache cache;
|
|
cache.m_dc = dc;
|
|
for(t_size walk = 0; walk < itemCount; ++walk) {
|
|
for(t_size colWalk = mask.find_first(true,0,columnCount); colWalk < columnCount; colWalk = mask.find_next(true,colWalk,columnCount)) {
|
|
pfc::max_acc(widths[colWalk], GetOptimalSubItemWidth(walk,colWalk,cache));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (expandLast) {
|
|
uint32_t usedWidth = 0; size_t lastCol = SIZE_MAX;
|
|
pfc::array_t<int> order; order.set_size(columnCount);
|
|
WIN32_OP_D( m_header.GetOrderArray((int)columnCount,order.get_ptr()) );
|
|
for(size_t _walk = 0; _walk < columnCount; ++_walk) {
|
|
const size_t colWalk = (size_t) order[_walk];
|
|
PFC_ASSERT( colWalk < columnCount );
|
|
if (mask[colWalk]) {
|
|
lastCol = colWalk;
|
|
usedWidth += widths[colWalk];
|
|
} else {
|
|
usedWidth += GetSubItemWidth(colWalk);
|
|
}
|
|
}
|
|
if (lastCol != SIZE_MAX) {
|
|
t_uint32 clientWidth = this->GetClientRectHook().Width();
|
|
if (clientWidth > 0) --clientWidth; // $!@# scrollbar hack
|
|
if (usedWidth < clientWidth) {
|
|
widths[lastCol] += clientWidth - usedWidth;
|
|
}
|
|
}
|
|
}
|
|
for(t_size colWalk = mask.find_first(true,0,columnCount); colWalk < columnCount; colWalk = mask.find_next(true,colWalk,columnCount)) {
|
|
ResizeColumn(colWalk,widths[colWalk],false);
|
|
}
|
|
ProcessColumnsChange();
|
|
}
|
|
|
|
t_uint32 CListControlHeaderImpl::GetOptimalGroupHeaderWidth(int which) const {
|
|
CWindowDC dc(*this);
|
|
SelectObjectScope fontScope(dc,GetGroupHeaderFont());
|
|
GetOptimalWidth_Cache cache; cache.m_dc = dc;
|
|
const t_uint32 base = this->GetColumnSpacing() * 2;
|
|
if (GetGroupHeaderText(which,cache.m_stringTemp)) {
|
|
return base + cache.GetStringTempWidth();
|
|
} else {
|
|
return base;
|
|
}
|
|
}
|
|
|
|
size_t CListControlHeaderImpl::GetColumnCount() const {
|
|
if ( ! IsHeaderEnabled() ) return 1;
|
|
#if PFC_DEBUG
|
|
int iHeaderCount = m_header.GetItemCount();
|
|
PFC_ASSERT( m_colRuntime.size() == (size_t) iHeaderCount );
|
|
#endif
|
|
return m_colRuntime.size();
|
|
}
|
|
|
|
void CListControlHeaderImpl::ColumnWidthFix() {
|
|
if ( this->HaveAutoWidthColumns() ) {
|
|
ProcessAutoWidth();
|
|
} else {
|
|
RecalcItemWidth();
|
|
}
|
|
}
|
|
|
|
void CListControlHeaderImpl::ProcessAutoWidth() {
|
|
if ( HaveAutoWidthColumns() ) {
|
|
const int clientWidth = this->GetClientRectHook().Width();
|
|
|
|
if ( ! this->HaveAutoWidthContentColumns( ) && clientWidth == m_itemWidth) return;
|
|
|
|
const size_t count = GetColumnCount();
|
|
uint32_t totalNonAuto = 0;
|
|
size_t numAutoWidth = 0;
|
|
for(size_t walk = 0; walk < count; ++ walk ) {
|
|
if ( m_colRuntime[walk].autoWidth() ) {
|
|
++ numAutoWidth;
|
|
} else {
|
|
totalNonAuto += GetSubItemWidth(walk);
|
|
}
|
|
}
|
|
int toDivide = clientWidth - totalNonAuto;
|
|
if ( toDivide < 0 ) toDivide = 0;
|
|
|
|
size_t numLeft = numAutoWidth;
|
|
auto worker = [&] ( size_t iCol ) {
|
|
auto & rt = m_colRuntime[iCol];
|
|
int lo = this->GetOptimalColumnWidthFixed( rt.m_text.c_str() );
|
|
if ( rt.autoWidthContent() ) {
|
|
int lo2 = this->GetOptimalColumnWidth( iCol );
|
|
if ( lo < lo2 ) lo = lo2;
|
|
}
|
|
int width = (int)(toDivide / numLeft);
|
|
if ( width < lo ) width = lo;
|
|
|
|
HDITEM item = {};
|
|
item.mask = HDI_WIDTH;
|
|
item.cxy = width;
|
|
WIN32_OP_D( m_header.SetItem( iCol, &item ) );
|
|
rt.m_widthPixels = width;
|
|
|
|
if ( toDivide > width ) {
|
|
toDivide -= width;
|
|
} else {
|
|
toDivide = 0;
|
|
}
|
|
-- numLeft;
|
|
|
|
};
|
|
for( size_t iCol = 0; iCol < count; ++ iCol ) {
|
|
if (m_colRuntime[iCol].autoWidthContent() ) worker(iCol);
|
|
}
|
|
for( size_t iCol = 0; iCol < count; ++ iCol ) {
|
|
if ( m_colRuntime[iCol].autoWidthPlain() ) worker(iCol);
|
|
}
|
|
|
|
RecalcItemWidth();
|
|
OnColumnsChanged();
|
|
m_header.Invalidate();
|
|
}
|
|
}
|
|
|
|
void CListControlHeaderImpl::RecalcItemWidth() {
|
|
int total = 0;
|
|
const t_size count = GetColumnCount();
|
|
for(t_size walk = 0; walk < count; ++walk) total += GetSubItemWidth(walk);
|
|
m_itemWidth = total;
|
|
}
|
|
|
|
CRect CListControlHeaderImpl::GetSubItemRectAbs(t_size item,t_size subItem) const {
|
|
CRect rc = GetItemRectAbs(item);
|
|
auto order = GetColumnOrderArray();
|
|
const t_size colCount = order.size();
|
|
for(t_size _walk = 0; _walk < colCount; ++_walk) {
|
|
const t_size walk = (t_size) order[_walk];
|
|
|
|
t_size width = this->GetSubItemWidth(walk);
|
|
if (subItem == walk) {
|
|
|
|
size_t span = GetSubItemSpan(item, walk);
|
|
if ( walk + span > colCount ) span = colCount - walk;
|
|
for( size_t extra = 1; extra < span; ++ extra ) {
|
|
width += GetSubItemWidth( walk + extra);
|
|
}
|
|
|
|
rc.right = rc.left + (long)width;
|
|
|
|
return rc;
|
|
} else {
|
|
rc.left += (long)width;
|
|
}
|
|
}
|
|
throw pfc::exception_invalid_params();
|
|
}
|
|
|
|
CRect CListControlHeaderImpl::GetSubItemRect(t_size item,t_size subItem) const {
|
|
CRect rc = GetSubItemRectAbs(item,subItem); rc.OffsetRect(-GetViewOffset()); return rc;
|
|
}
|
|
|
|
void CListControlHeaderImpl::SetHotItem(size_t row, size_t column) {
|
|
if ( m_hotItem != row || m_hotSubItem != column ) {
|
|
if (m_hotItem != pfc_infinite) InvalidateRect(GetSubItemRect(m_hotItem, m_hotSubItem));
|
|
m_hotItem = row; m_hotSubItem = column;
|
|
if (m_hotItem != pfc_infinite) InvalidateRect(GetSubItemRect(m_hotItem, m_hotSubItem));
|
|
}
|
|
}
|
|
|
|
void CListControlHeaderImpl::SetPressedItem(size_t row, size_t column) {
|
|
if (m_pressedItem != row || m_pressedSubItem != column) {
|
|
if (m_pressedItem != pfc_infinite) InvalidateRect(GetSubItemRect(m_pressedItem, m_pressedSubItem));
|
|
m_pressedItem = row; m_pressedSubItem = column;
|
|
if (m_pressedItem != pfc_infinite) InvalidateRect(GetSubItemRect(m_pressedItem, m_pressedSubItem));
|
|
}
|
|
}
|
|
|
|
void CListControlHeaderImpl::SetCellCheckState(size_t item, size_t subItem, bool value) {
|
|
ReloadItem(item); (void)subItem; (void)value;
|
|
// Subclass deals with keeping track of state
|
|
}
|
|
|
|
bool CListControlHeaderImpl::ToggleSelectedItemsHook(const pfc::bit_array & mask) {
|
|
if (this->GetCellTypeSupported() ) {
|
|
bool handled = false;
|
|
bool setTo = true;
|
|
|
|
mask.walk(GetItemCount(), [&](size_t idx) {
|
|
auto ct = this->GetCellType(idx, 0);
|
|
if ( ct != nullptr && ct->IsToggle() ) {
|
|
if ( ct->IsRadioToggle() ) {
|
|
if (!handled) {
|
|
handled = true;
|
|
setTo = !this->GetCellCheckState(idx, 0);
|
|
this->SetCellCheckState(idx, 0, setTo);
|
|
}
|
|
} else {
|
|
if (!handled) {
|
|
handled = true;
|
|
setTo = ! this->GetCellCheckState(idx,0);
|
|
}
|
|
this->SetCellCheckState(idx,0,setTo);
|
|
}
|
|
}
|
|
});
|
|
|
|
if (handled) return true;
|
|
}
|
|
return __super::ToggleSelectedItemsHook(mask);
|
|
}
|
|
|
|
void CListControlHeaderImpl::OnSubItemClicked(t_size item, t_size subItem, CPoint pt) {
|
|
auto ct = GetCellType(item, subItem);
|
|
if ( ct != nullptr && ct->IsToggle() ) {
|
|
if ( ct->HotRect(GetSubItemRect(item, subItem)).PtInRect(pt) ) {
|
|
this->SetCellCheckState( item, subItem, ! GetCellCheckState( item, subItem ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
bool CListControlHeaderImpl::AllowTypeFindInCell(size_t item, size_t subItem) const {
|
|
auto cell = GetCellType( item, subItem );
|
|
if ( cell == nullptr ) return false;
|
|
return cell->AllowTypeFind();
|
|
}
|
|
|
|
bool CListControlHeaderImpl::CellTypeReactsToMouseOver(cellType_t ct) {
|
|
return ct != nullptr && ct->IsInteractive();
|
|
}
|
|
|
|
CRect CListControlHeaderImpl::CellHotRect( size_t, size_t, cellType_t ct, CRect rcCell) {
|
|
if ( ct != nullptr ) {
|
|
return ct->HotRect(rcCell);
|
|
}
|
|
return rcCell;
|
|
}
|
|
CRect CListControlHeaderImpl::CellHotRect(size_t item, size_t subItem, cellType_t ct) {
|
|
return CellHotRect( item, subItem, ct, GetSubItemRect( item, subItem ) );
|
|
}
|
|
void CListControlHeaderImpl::OnMouseMove(UINT nFlags, CPoint pt) {
|
|
const DWORD maskButtons = MK_LBUTTON | MK_RBUTTON | MK_MBUTTON | MK_XBUTTON1 | MK_XBUTTON2;
|
|
if (GetCellTypeSupported() && (nFlags & maskButtons) == 0 ) {
|
|
size_t item; size_t subItem;
|
|
if (this->GetItemAtPointAbsEx(pt + GetViewOffset(), item, subItem)) {
|
|
auto ct = this->GetCellType( item, subItem );
|
|
if (CellTypeReactsToMouseOver(ct) ) {
|
|
auto rc = CellHotRect( item, subItem, ct );
|
|
if ( PtInRect( rc, pt ) ) {
|
|
{
|
|
auto c = ct->HotCursor();
|
|
if ( c != NULL ) SetCursor(c);
|
|
}
|
|
SetHotItem(item, subItem);
|
|
SetCaptureEx([=](UINT msg, DWORD newStatus, CPoint pt) {
|
|
if ((newStatus & maskButtons) != 0 || msg == WM_MOUSEWHEEL || msg == WM_MOUSEHWHEEL ) {
|
|
// A button has been pressed or wheel has been moved
|
|
this->ClearHotItem();
|
|
SetCaptureMsgHandled(FALSE);
|
|
return false;
|
|
}
|
|
|
|
if ( ! PtInRect( rc, pt ) ) {
|
|
// Left the rect
|
|
this->ClearHotItem();
|
|
SetCaptureMsgHandled(FALSE);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
SetMsgHandled(FALSE);
|
|
}
|
|
|
|
bool CListControlHeaderImpl::AllowScrollbar(bool vertical) const {
|
|
if ( vertical ) {
|
|
// vertical
|
|
return true;
|
|
} else {
|
|
// horizontal
|
|
if (! IsHeaderEnabled( ) ) return false; // no header?
|
|
return true;
|
|
}
|
|
}
|
|
|
|
void CListControlHeaderImpl::OnDestroy() {
|
|
m_colRuntime.clear();
|
|
m_header = NULL;
|
|
m_headerLine = NULL;
|
|
SetMsgHandled(FALSE);
|
|
}
|
|
|
|
|
|
uint32_t CListControlHeaderImpl::GetColumnsBlankWidth( size_t colExclude ) const {
|
|
auto client = this->GetClientRectHook().Width();
|
|
int item = GetItemWidth();
|
|
if (colExclude) item -= GetSubItemWidth(colExclude);
|
|
if ( item < 0 ) item = 0;
|
|
if ( item < client ) return (uint32_t)( client - item );
|
|
else return 0;
|
|
}
|
|
|
|
void CListControlHeaderImpl::SizeColumnToContent( size_t which, uint32_t minWidth ) {
|
|
auto width = this->GetOptimalColumnWidth( which );
|
|
if ( width < minWidth ) width = minWidth;
|
|
this->ResizeColumn( which, width );
|
|
}
|
|
|
|
void CListControlHeaderImpl::SizeColumnToContentFillBlank( size_t which ) {
|
|
this->SizeColumnToContent( which, this->GetColumnsBlankWidth(which) );
|
|
}
|
|
|
|
HBRUSH CListControlHeaderImpl::OnCtlColorStatic(CDCHandle dc, CStatic wndStatic) {
|
|
if ( wndStatic == m_headerLine ) {
|
|
COLORREF col = GridColor();
|
|
dc.SetDCBrushColor( col );
|
|
return (HBRUSH) GetStockObject(DC_BRUSH);
|
|
}
|
|
SetMsgHandled(FALSE);
|
|
return NULL;
|
|
}
|
|
|
|
void CListControlHeaderImpl::ReloadData() {
|
|
__super::ReloadData();
|
|
if ( this->HaveAutoWidthContentColumns( ) ) {
|
|
this->ColumnWidthFix();
|
|
}
|
|
}
|