//------------------------------------------------------------------------------
// OWL Extensions (OWLEXT) Class Library
// BROWSE.CPP
//
// TBrowse Class
//
// Original code by Daniel BERMAN (dberman@inge-com.fr)
// Written: 13th December 1995
//------------------------------------------------------------------------------
#include <owlext\pch.h>
#pragma hdrstop
#include <owl/menu.h>
#include <owlext/browse.h>
#if defined(_MSC_VER)
# pragma warning(disable: 4996) // Turn off deprecation warnings (triggered by GetVersion/GetVersionEx).
#endif
#define IDC_LISTBOX 24500
#define MULTIPLESEL (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)
namespace OwlExt {
using namespace owl;
// Default Column Style
static TStyle DefaultStyle(0);
//-- TColumn -------------------------------------------------------------------
struct TColumn {
TColumn() {}
TColumn(int id, const owl::tstring& text, int styleNo = 0)
{Id = id; Text = text; StyleNo = styleNo;}
bool operator == (const TColumn& col) const {return col.Id == Id;}
bool operator < (const TColumn& col) const {return col.Id < Id;}
int Id;
owl::tstring Text;
int StyleNo;
};
typedef TObjectArray<TColumn> TColumns;
//-- TBrowseList ---------------------------------------------------------------
class TBrowseList : public TListBox {
public:
TBrowseList(TWindow* parent, TModule* module);
protected:
void EvHScroll(uint scrollCode, uint thumbPos, HWND hWndCtl);
void EvRButtonDown(uint modKeys, const TPoint& point);
void EvRButtonUp(uint modKeys, const TPoint& point);
private:
DECLARE_RESPONSE_TABLE(TBrowseList);
};
DEFINE_RESPONSE_TABLE1(TBrowseList, TListBox)
EV_WM_HSCROLL,
EV_WM_RBUTTONDOWN,
END_RESPONSE_TABLE;
TBrowseList::TBrowseList(TWindow* parent, TModule* module)
:
TListBox(parent, IDC_LISTBOX, 0, 0, 0, 0, module)
{
ModifyStyle(WS_BORDER|LBS_SORT,LBS_OWNERDRAWFIXED|LBS_NOINTEGRALHEIGHT);
}
void TBrowseList::EvHScroll(uint scrollCode, uint thumbPos, HWND hWndCtl)
{
TListBox::EvHScroll(scrollCode, thumbPos, hWndCtl);
TRect r = Parent->GetClientRect();
r.bottom = r.top + TYPESAFE_DOWNCAST(Parent, TBrowse)->GetHeadersHeight();
Parent->InvalidateRect(r);
}
void TBrowseList::EvRButtonDown(uint modKeys, const TPoint& point)
{
TPoint p = MapWindowPoints(*Parent, point);
Parent->SendMessage(WM_RBUTTONDOWN, modKeys, MAKELPARAM(p.x, p.y));
}
void TBrowseList::EvRButtonUp(uint modKeys, const TPoint& point)
{
TPoint p = MapWindowPoints(*Parent, point);
Parent->SendMessage(WM_RBUTTONUP, modKeys, MAKELPARAM(p.x, p.y));
}
//-- THeader -------------------------------------------------------------------
THeader::THeader()
{
Id = Width = TextFormat = 0;
Visible = Pressed = false;
ExactWidth = true;
Color = TColor(0, 0, 128);
Rect.SetNull();
CompareFunc = NULL;
CompareMemFunc = NULL;
SortOrder = Undefined;
}
THeader::THeader(int id, const owl::tstring& title, int width, bool visible,
bool exactWidth)
{
Id = id;
Title = title;
Width = width;
Visible = visible;
Pressed = false;
ExactWidth = exactWidth;
Color = TColor(0, 0, 128);
TextFormat = DT_SINGLELINE | DT_VCENTER | DT_LEFT | DT_NOPREFIX;
Rect.SetNull();
CompareFunc = NULL;
CompareMemFunc = NULL;
SortOrder = Undefined;
}
//-- TStyle -------------------------------------------------------------------
TStyle::TStyle()
{
Id = 0;
IconImage = NULL;
IconMask = NULL;
}
TStyle::TStyle(int id)
{
Id = id;
TextColor = GetSysColor(COLOR_WINDOWTEXT);
BkColor = GetSysColor(COLOR_WINDOW);
HilightTextColor = GetSysColor(COLOR_HIGHLIGHTTEXT);
HilightBkColor = GetSysColor(COLOR_HIGHLIGHT);
TextAlignment = DT_SINGLELINE | DT_VCENTER | DT_LEFT | DT_NOPREFIX;
IconImage = NULL;
IconMask = NULL;
}
TStyle::~TStyle()
{
delete IconImage;
delete IconMask;
}
void TStyle::SetStyle(TDC& dc, UINT itemState)
{
if (itemState & ODS_SELECTED){
dc.SetBkColor(HilightBkColor);
dc.SetTextColor(HilightTextColor);
}
else{
dc.SetBkColor(BkColor);
dc.SetTextColor(TextColor);
}
}
//-- TBrowse -------------------------------------------------------------------
DEFINE_RESPONSE_TABLE1(TBrowse, TControl)
EV_LBN_DBLCLK(IDC_LISTBOX, EvDblClk),
EV_LBN_SELCHANGE(IDC_LISTBOX, EvSelChange),
EV_WM_LBUTTONDOWN,
EV_WM_LBUTTONUP,
EV_WM_MOUSEMOVE,
EV_WM_NCHITTEST,
EV_WM_RBUTTONDOWN,
EV_WM_RBUTTONUP,
EV_WM_SETFOCUS,
EV_WM_SIZE,
EV_WM_SYSCOLORCHANGE,
END_RESPONSE_TABLE;
TBrowse::TBrowse(TWindow* parent, int id, int x, int y, int w, int h, TModule* module)
:
TControl(parent, id, _T(""), x, y, w, h, module)
{
ModifyStyle(0,WS_BORDER|WS_TABSTOP);
Init();
}
TBrowse::TBrowse(TWindow* parent, int resourceId, TModule* module)
:
TControl(parent, resourceId, module)
{
Init();
}
TBrowse::~TBrowse()
{
delete NormalFont;
delete BoldFont;
delete Headers;
delete Styles;
}
void TBrowse::AddColumn(int id, LPCTSTR title, int width, bool visible,
TUnits units)
{
switch (units){
case Chars:
width = width < 0 ? static_cast<int>(_tcslen(title)) : width;
width *= BoldCharWidth;
break;
case Pixels:
width = width < 0 ? 0 : width;
break;
case Percent:
width = width < 0 || width > 100 ? 0 : width;
break;
}
Headers->Add(new THeader(id, title, width, visible, (units != Percent)));
}
int TBrowse::AddRow()
{
int index = LB_ERR;
if (GetNumCols() && (index = List->AddString(_T(""))) != LB_ERR)
{
TColumns *columns = new TColumns(GetNumCols(), 0, 1);
THeadersIterator i(*Headers);
while (i){
columns->Add(TColumn(i.Current()->Id, _T(""), 0));
i++;
}
List->SetItemData(index, reinterpret_cast<LPARAM>(columns));
}
return index;
}
void TBrowse::AddStyle(int id)
{
Styles->Add(new TStyle(id));
}
int TBrowse::InsertRow(int index)
{
if (GetNumCols() && (index = List->InsertString(_T(""),index)) != LB_ERR)
{
TColumns *columns = new TColumns(GetNumCols(), 0, 1);
THeadersIterator i(*Headers);
while (i){
columns->Add(TColumn(i.Current()->Id, _T(""), 0));
i++;
}
List->SetItemData(index, reinterpret_cast<LPARAM>(columns));
}
return index;
}
void TBrowse::CalculateHeadersSize()
{
TRect rect(0, 0, 0, HeadersHeight);
int Extent = 0, visibleCols = 0;
int browseWidth = List->GetWindowAttr().W;
THeadersIterator i(*Headers);
while (i){
THeader* header = i++;
if (header->Visible)
visibleCols++;
}
browseWidth -= (visibleCols * 5);
i.Restart();
while (i){
THeader* header = i++;
if (header->Visible){
if (header->ExactWidth)
rect.right = rect.left + 4 + header->Width;
else
rect.right = rect.left + 4 + (int)(header->Width / 100.0 * browseWidth);
header->Rect = rect;
Extent += rect.Width() - 1;
rect.left = rect.right - 1;
}
}
if (Extent > List->GetHorizontalExtent())
List->SetHorizontalExtent(HorzExtent=Extent);
else if (Extent <= List->GetClientRect().Width()){
List->SetHorizontalExtent(HorzExtent=Extent);
List->SetScrollPos(SB_HORZ, 0);
List->ShowScrollBar(SB_HORZ, false);
}
}
void TBrowse::ClearList()
{
List->ClearList();
}
void TBrowse::DeleteItem(DELETEITEMSTRUCT & deleteInfo)
{
delete (TColumns*)deleteInfo.itemData;
}
int TBrowse::DeleteRow(int index)
{
return List->DeleteString(index);
}
void TBrowse::DrawHeaderRect(TDC& dc, TRect& rect, bool down)
{
dc.SaveDC();
dc.FrameRect(rect, TColor(TColor::Black));
dc.SelectObject(TBrush(GetSysColor(down ? COLOR_BTNSHADOW : COLOR_BTNHIGHLIGHT)));
dc.PatBlt(rect.left + 1, rect.top + 1, rect.Width() - 2, 1, PATCOPY);
dc.PatBlt(rect.left + 1, rect.top + 1, 1, rect.Height() - 2, PATCOPY);
if (!down){
dc.SelectObject(TBrush(GetSysColor(COLOR_BTNSHADOW)));
dc.PatBlt(rect.right - 2, rect.top + 1, 1, rect.Height() - 2, PATCOPY);
dc.PatBlt(rect.left + 1, rect.bottom - 2, rect.Width() - 2, 1, PATCOPY);
}
dc.RestoreDC();
}
void TBrowse::DrawHeaderText(TDC& dc, THeader* header)
{
TRect hdrRect = header->Rect.InflatedBy(-2, -2);
if (header->TextFormat & DT_RIGHT)
hdrRect.right -= 2;
else
hdrRect.left += 2;
hdrRect.top += header->Pressed;
hdrRect.left += 2 + header->Pressed;
if (header->SortOrder != THeader::Undefined)
hdrRect.right -= 10;
dc.DrawText(header->Title.c_str(), -1, hdrRect, header->TextFormat);
if (header->SortOrder != THeader::Undefined){
hdrRect.left = hdrRect.right + 2;
hdrRect.right += 10;
const auto savedClipRgn = dc.GetClipRgn();
dc.IntersectClipRect(hdrRect);
TPoint points[3];
if (header->SortOrder == THeader::Ascending){
points[0].x = hdrRect.left + 3;
points[0].y = hdrRect.top + 3;
points[1].x = hdrRect.left;
points[1].y = hdrRect.bottom - 3 + header->Pressed;
points[2].x = hdrRect.left + 6;
points[2].y = hdrRect.bottom - 3 + header->Pressed;
}
else{
points[0].x = hdrRect.left;
points[0].y = hdrRect.top + 3;
points[1].x = hdrRect.left + 6;
points[1].y = hdrRect.top + 3;
points[2].x = hdrRect.left + 3;
points[2].y = hdrRect.bottom - 3 + header->Pressed;
}
dc.Polygon(points, 3);
dc.SelectClipRgn(savedClipRgn);
}
}
void TBrowse::DrawItem(DRAWITEMSTRUCT & drawInfo)
{
if (drawInfo.CtlType == ODT_LISTBOX)
switch (drawInfo.itemAction)
{
case ODA_DRAWENTIRE:
case ODA_SELECT: {
TDC dc(drawInfo.hDC);
dc.SelectObject(*NormalFont);
dc.SetBkColor(GetSysColor(drawInfo.itemState & ODS_SELECTED ? COLOR_HIGHLIGHT : COLOR_WINDOW));
dc.SetTextColor(GetSysColor(drawInfo.itemState & ODS_SELECTED ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT));
dc.TextRect(drawInfo.rcItem);
TColumns* cols = (TColumns*)drawInfo.itemData;
THeadersIterator i(*Headers);
while (i){
THeader* header = i.Current();
if (header->Visible){
TRect rect = header->Rect.InflatedBy(-1, -1);
rect.top = drawInfo.rcItem.top;
rect.bottom = drawInfo.rcItem.bottom;
const auto loc = cols->Find(TColumn{header->Id, _T(""), 0});
if (loc != cols->NPOS)
{
TStyle colStyle((*cols)[loc].StyleNo);
auto locStyle = Styles->Find(&colStyle);
if (locStyle == Styles->NPOS)
{
TStyle headerStyle(header->StyleNo);
locStyle = Styles->Find(&headerStyle);
}
TStyle* style;
if (locStyle == Styles->NPOS)
style = &DefaultStyle;
else
style = (*Styles)[locStyle];
if (style->IconImage){
int h, w = style->IconImage->Width();
TPoint maskOrigin, imageOrigin(0, 0);
TMemoryDC imageDC(dc);
TRect destRect ;
if (style->IconMask){
TMemoryDC maskDC(dc);
h = style->IconImage->Height();
maskOrigin = TPoint(0, 0);
TPoint topleft = rect.TopLeft();
++topleft.y;
destRect = TRect(topleft, TSize(w, h));
imageDC.SelectObject(*style->IconImage);
maskDC.SelectObject(*style->IconMask);
dc.TextRect(destRect,GetSysColor(drawInfo.itemState & ODS_SELECTED ? COLOR_HIGHLIGHT : COLOR_WINDOW));
dc.SetBkColor(TColor::White);
dc.SetTextColor(TColor::Black);
dc.BitBlt(destRect, imageDC, imageOrigin, SRCINVERT);
dc.BitBlt(destRect, maskDC, maskOrigin, SRCAND);
dc.BitBlt(destRect, imageDC, imageOrigin, SRCINVERT);
imageDC.RestoreObjects();
maskDC.RestoreObjects();
}
else{
h = style->IconImage->Height() / 2 ;
maskOrigin = TPoint(0, h);
TPoint topleft = rect.TopLeft();
++topleft.y;
destRect = TRect(topleft, TSize(w, h));
imageDC.SelectObject(*style->IconImage);
dc.BitBlt(destRect, imageDC, imageOrigin, SRCINVERT);
dc.BitBlt(destRect, imageDC, maskOrigin, SRCAND);
dc.BitBlt(destRect, imageDC, imageOrigin, SRCINVERT);
imageDC.RestoreObjects();
}
rect.left = destRect.right + 2;
dc.SetBkColor(GetSysColor(drawInfo.itemState & ODS_SELECTED ? COLOR_HIGHLIGHT : COLOR_WINDOW));
dc.SetTextColor(GetSysColor(drawInfo.itemState & ODS_SELECTED ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT));
}
style->SetStyle(dc, drawInfo.itemState);
rect.right += style->TextAlignment & DT_RIGHT ? -2 : 2;
auto size = dc.GetTextExtentPoint32((*cols)[loc].Text);
if (size.cx >= rect.Width())
{
const auto length = static_cast<int>((*cols)[loc].Text.size());
_TCHAR* longWord = new _TCHAR[length + 3];
_tcscpy(longWord, (*cols)[loc].Text.c_str());
_TCHAR* pch;
for (pch = longWord + length - 1; pch >= longWord; --pch){
_tcscpy(pch, _T("..."));
size = dc.GetTextExtentPoint32(longWord);
if (size.cx < rect.Width())
break;
}
dc.DrawText(longWord, -1, rect, style->TextAlignment);
delete[] longWord;
}
else
dc.DrawText((*cols)[loc].Text.c_str(), -1, rect,
style->TextAlignment);
}
}
i++;
}
if (drawInfo.itemState & ODS_FOCUS)
dc.DrawFocusRect(drawInfo.rcItem);
break;
}
case ODA_FOCUS:
{
TDC dc(drawInfo.hDC);
dc.DrawFocusRect(drawInfo.rcItem);
}
}
}
void TBrowse::DrawSizingLine(TPoint& point)
{
TRect rect = List->GetClientRect();
TWindowDC dc(*List);
TPen pen(TColor::Black, 1, PS_DOT);
dc.SetROP2(R2_XORPEN);
dc.SelectObject(pen);
dc.MoveTo(PrevSizingRect.TopLeft());
dc.LineTo(PrevSizingRect.BottomRight());
dc.MoveTo(point.x, rect.top);
dc.LineTo(point.x, rect.bottom);
PrevSizingRect = TRect(point.x, rect.top, point.x, rect.bottom);
}
void TBrowse::EvDblClk()
{
if (Parent)
Parent->SendNotification(GetId(), LBN_DBLCLK, GetHandle(), WM_COMMAND);
}
void TBrowse::EvLButtonDown(uint modKeys, const TPoint& point)
{
bool onBorder;
TPoint p(point);
Capture = GetHeaderFromPoint(p, onBorder);
if (Capture != NULL){
SetCapture();
if (onBorder){
ReSizing = true;
StartDrawingSizingLine(p);
}
else if ((Capture->CompareFunc!=0) || (Capture->CompareMemFunc!=0)){
ReSizing = false;
Capture->Pressed = true;
InvalidateRect(Capture->Rect.Offset(-ViewportOrgX, 0));
}
else{
Capture = NULL;
ReleaseCapture();
}
}
TControl::EvLButtonDown(modKeys, point);
}
void TBrowse::EvLButtonUp(uint modKeys, const TPoint& point)
{
if (Capture){
if (ReSizing){
TPoint p(point);
StopDrawingSizingLine(p);
TRect rect = Capture->Rect.OffsetBy(-ViewportOrgX, 0);
if (p.x < rect.right - 1 || p.x > rect.right + 1){
if (p.x < rect.left)
p.x = rect.left;
if (Capture->ExactWidth)
Capture->Width = p.x - rect.left - 4;
else
Capture->Width = (int)((p.x - rect.left - 4) * 100.0 / List->GetWindowAttr().W);
CalculateHeadersSize();
Invalidate();
}
}
else if (Capture->Pressed){
Capture->SortOrder = (modKeys & MK_CONTROL) ? THeader::Descending : THeader::Ascending;
SetCursor(0, IDC_WAIT);
SortItems(Capture->Id, (Capture->SortOrder == THeader::Ascending));
Capture->Pressed = false;
}
Capture = NULL;
ReSizing = false;
SetCursor(0, IDC_ARROW);
ReleaseCapture();
}
TControl::EvLButtonUp(modKeys, point);
}
void TBrowse::EvMouseMove(uint modKeys, const TPoint& point)
{
if (!Capture){
bool onBorder;
TPoint p(point);
if (GetHeaderFromPoint(p, onBorder) != NULL && onBorder)
SetCursor(0, IDC_SIZEWE);
else
SetCursor(0, IDC_ARROW);
}
else
if (ReSizing){
TPoint p(point);
DrawSizingLine(p);
}
else{
bool onBorder;
TPoint p(point);
THeader* header = GetHeaderFromPoint(p, onBorder);
if (header == NULL || header->Id != Capture->Id){
if (Capture->Pressed){
Capture->Pressed = false;
InvalidateRect(Capture->Rect.Offset(-ViewportOrgX, 0));
}
}
else if (!Capture->Pressed){
Capture->Pressed = true;
InvalidateRect(Capture->Rect.Offset(-ViewportOrgX, 0));
}
}
TControl::EvMouseMove(modKeys, point);
}
uint TBrowse::EvNCHitTest(const TPoint& /*point*/)
{
return HTCLIENT;
}
void TBrowse::EvRButtonDown(uint modKeys, const TPoint& point_)
{
if (Parent){
TPoint point = MapWindowPoints(*Parent, point_);
Parent->SendMessage(WM_RBUTTONDOWN, modKeys, MAKELPARAM(point.x, point.y));
}
}
void TBrowse::EvRButtonUp(uint modKeys, const TPoint& point_)
{
if (Parent){
TPoint point = MapWindowPoints(*Parent, point_);
Parent->SendMessage(WM_RBUTTONUP, modKeys, MAKELPARAM(point.x, point.y));
}
}
void TBrowse::EvSelChange()
{
if (Parent)
Parent->SendNotification(GetId(), LBN_SELCHANGE, GetHandle(), WM_COMMAND);
}
void TBrowse::EvSetFocus(HWND hWndLostFocus)
{
TControl::EvSetFocus(hWndLostFocus);
List->SetFocus();
}
void TBrowse::EvSize(uint sizeType, const TSize& size)
{
TControl::EvSize(sizeType, size);
TRect rect = GetClientRect();
rect.top += HeadersHeight;
List->MoveWindow(rect);
Invalidate();
}
void TBrowse::EvSysColorChange()
{
DefaultStyle.TextColor = GetSysColor(COLOR_WINDOWTEXT);
DefaultStyle.BkColor = GetSysColor(COLOR_WINDOW);
DefaultStyle.HilightTextColor = GetSysColor(COLOR_HIGHLIGHTTEXT);
DefaultStyle.HilightBkColor = GetSysColor(COLOR_HIGHLIGHT);
TControl::EvSysColorChange();
}
bool TBrowse::EnableWindow(bool enable)
{
TControl::EnableWindow(enable);
return List->EnableWindow(enable);
}
bool TBrowse::GetColumnText(int id, owl::tstring& text, int index)
{
bool bOk = false;
LPARAM itemData = List->GetItemData(index);
if (itemData != LB_ERR){
TColumns* cols = (TColumns*)itemData;
const auto loc = cols->Find(TColumn{id, _T(""), 0});
if (loc != cols->NPOS)
{
text = (*cols)[loc].Text;
bOk = true;
}
}
return bOk;
}
bool TBrowse::GetColumnWidth(int id, int &width, bool& exactWidth)
{
THeader idHeader(id, _T(""), 0, true, true);
const auto loc = Headers->Find(&idHeader);
if (loc != Headers->NPOS)
{
width = (*Headers)[loc]->Width;
exactWidth = (*Headers)[loc]->ExactWidth;
return true;
}
return false;
}
int TBrowse::GetCount() const
{
return List->GetCount();
}
bool TBrowse::GetHeaderColor(int id, TColor& color)
{
THeader idHeader(id, _T(""), 0, true, true);
const auto loc = Headers->Find(&idHeader);
if (loc != Headers->NPOS)
{
color = (*Headers)[loc]->Color;
return true;
}
return false;
}
THeader* TBrowse::GetHeaderFromPoint(TPoint& point, bool& onBorder)
{
THeadersIterator i(*Headers);
while (i){
THeader* header = i.Current();
if (header->Visible){
TRect rect = header->Rect.OffsetBy(-ViewportOrgX, 0);
if (point.x >= rect.left && point.x < rect.right - 1 &&
point.y >= rect.top && point.y <= rect.bottom){
onBorder = false;
return header;
}
else if (point.x >= rect.right - 1 && point.x <= rect.right + 1 &&
point.y >= rect.top && point.y <= rect.bottom){
onBorder = true;
return header;
}
}
i++;
}
return 0;
}
int TBrowse::GetNumCols()
{
return Headers->GetItemsInContainer();
}
bool TBrowse::GetSel(int index) const
{
return List->GetSel(index);
}
int TBrowse::GetSelCount() const
{
return List->GetSelCount();
}
int TBrowse::GetSelIndex() const
{
return List->GetSelIndex();
}
int TBrowse::GetSelIndexes(int* indexes, int maxCount) const
{
return List->GetSelIndexes(indexes, maxCount);
}
int TBrowse::GetTopIndex() const
{
return List->GetTopIndex();
}
void TBrowse::Init()
{
List = 0;
ReSizing = false;
Capture = NULL;
NormalFont = new TFont(_T("MS Shell Dlg"), -11);
LOGFONT lf = NormalFont->GetObject();
lf.lfWeight = FW_BOLD;
BoldFont = new TFont(lf);
{
TRect rc;
TScreenDC dc;
dc.SelectObject(*BoldFont);
dc.DrawText(_T("Abc"), -1, rc, DT_CENTER | DT_CALCRECT);
HeadersHeight = rc.Height() + 4;
TEXTMETRIC tm;
dc.GetTextMetrics(tm);
BoldCharWidth = (tm.tmAveCharWidth + tm.tmMaxCharWidth) / 2;
RowsHeight = tm.tmHeight + tm.tmExternalLeading;
}
Headers = new THeaders(2, 0, 1);
Styles = new TStyles(1, 0, 1);
SetBkgndColor(GetSysColor(COLOR_BTNFACE));
EnableTransfer();
}
bool TBrowse::IsColumnVisible(int id)
{
THeader idHeader(id, _T(""), 0, true, true);
const auto loc = Headers->Find(&idHeader);
if (loc != Headers->NPOS)
{
return (*Headers)[loc]->Visible;
}
return false;
}
void TBrowse::MeasureItem(MEASUREITEMSTRUCT & measureInfo)
{
if (measureInfo.CtlType == ODT_LISTBOX)
measureInfo.itemHeight = RowsHeight;
}
bool TBrowse::ModifyColumnText(int id, LPCTSTR text, int index)
{
if (SetColumnText(id, text, index)){
List->Invalidate(false);
return true;
}
return false;
}
void TBrowse::Paint(TDC& dc, bool /*erase*/, TRect& rect)
{
CalculateHeadersSize();
ViewportOrgX = (int)((float(HorzExtent) - List->GetClientRect().Width()) / 100 * List->GetScrollPos(SB_HORZ));
TPoint offset(-ViewportOrgX, 0);
dc.SetViewportOrg(offset);
rect -= offset;
dc.SelectObject(*BoldFont);
dc.SetBkMode(TRANSPARENT);
TRect thisRect(GetClientRect());
thisRect.bottom = thisRect.top + HeadersHeight;
dc.SelectClipRgn(TRegion{thisRect});
THeadersIterator i(*Headers);
while (i){
THeader* header = i.Current();
if (header->Visible && header->Rect.right > rect.left){
if (header->Rect.left > rect.right)
break;
dc.SetTextColor(header->Color);
DrawHeaderRect(dc, header->Rect, header->Pressed);
DrawHeaderText(dc, header);
/*
TRect r = header->Rect.InflatedBy(-2, -2);
if (header->TextFormat & DT_RIGHT)
r.right -= 2;
else
r.left += 2;
r.top += header->Pressed;
r.left += 2 + header->Pressed;
dc.DrawText(header->Title.c_str(), -1, r, header->TextFormat);
*/
}
i++;
}
}
void TBrowse::Reset()
{
ClearList();
Headers->Flush();
Styles->Flush();
Invalidate();
}
bool TBrowse::SetColumnStyle(int id, int styleNo, int index)
{
bool bOk = false;
if (GetCount() < 1)
return bOk;
if (index == -1)
index = GetCount() - 1;
LPARAM itemData = List->GetItemData(index);
if (itemData != LB_ERR){
TColumns *cols = (TColumns*)itemData;
const auto loc = cols->Find(TColumn{id, _T(""), 0});
if (loc != cols->NPOS)
{
(*cols)[loc].StyleNo = styleNo;
bOk = true;
}
}
return bOk;
}
bool TBrowse::SetColumnText(int id, LPCTSTR text, int index)
{
bool bOk = false;
if (GetCount() < 1)
return bOk;
if (index == -1)
index = GetCount() - 1;
LPARAM itemData = List->GetItemData(index);
if (itemData != LB_ERR){
TColumns *cols = (TColumns*)itemData;
const auto loc = cols->Find(TColumn{id, _T(""), 0});
if (loc != cols->NPOS)
{
(*cols)[loc].Text = text;
bOk = true;
}
}
return bOk;
}
bool TBrowse::SetColumnText(int id, LPCTSTR text, int styleNo, int index)
{
if (SetColumnText(id, text, index))
return SetColumnStyle(id, styleNo, index);
return false;
}
bool TBrowse::SetColumnWidth(int id, int width, TUnits units)
{
THeader idHeader(id, _T(""), 0, true, true);
const auto loc = Headers->Find(&idHeader);
if (loc != Headers->NPOS)
{
switch (units){
case Chars:
width = width < 0 ? static_cast<int>((*Headers)[loc]->Title.size()) : width;
width *= BoldCharWidth;
break;
case Pixels:
width = width < 0 ? 0 : width;
break;
case Percent:
width = width < 0 || width > 100 ? 0 : width;
break;
}
(*Headers)[loc]->Width = width;
(*Headers)[loc]->ExactWidth = (units != Percent);
if (GetHandle())
Invalidate();
return true;
}
return false;
}
bool TBrowse::SetCompareItemProc(int colId, TCompareFunc compareFunc)
{
THeader colIdHeader(colId, _T(""), 0, true, true);
const auto loc = Headers->Find(&colIdHeader);
if (loc != Headers->NPOS)
{
(*Headers)[loc]->CompareFunc = compareFunc;
return true;
}
return false;
}
bool TBrowse::SetCompareItemProc(int colId, TCompareMemFunc compareMemFunc)
{
THeader colIdHeader(colId, _T(""), 0, true, true);
const auto loc = Headers->Find(&colIdHeader);
if (loc != Headers->NPOS)
{
(*Headers)[loc]->CompareMemFunc = compareMemFunc;
return true;
}
return false;
}
bool TBrowse::SetDefaultStyle(int id, int styleNo)
{
THeader idHeader(id, _T(""), 0, true, true);
const auto loc = Headers->Find(&idHeader);
if (loc != Headers->NPOS)
{
(*Headers)[loc]->StyleNo = styleNo;
if (GetHandle())
Invalidate();
return true;
}
return false;
}
bool TBrowse::SetHeaderColor(int id, TColor color)
{
THeader idHeader(id, _T(""), 0, true, true);
const auto loc = Headers->Find(&idHeader);
if (loc != Headers->NPOS)
{
(*Headers)[loc]->Color = color;
if (GetHandle())
Invalidate();
return true;
}
return false;
}
bool TBrowse::SetHeaderTextFormat(int id, uint16 textFormat)
{
THeader idHeader(id, _T(""), 0, true, true);
const auto loc = Headers->Find(&idHeader);
if (loc != Headers->NPOS)
{
(*Headers)[loc]->TextFormat = textFormat;
if (GetHandle())
Invalidate();
return true;
}
return false;
}
void TBrowse::SetHeadersHeight(int height)
{
HeadersHeight = height;
if (GetHandle())
Invalidate();
}
int TBrowse::SetSel(int index, bool select)
{
return List->SetSel(index, select);
}
int TBrowse::SetSelIndex(int index)
{
return List->SetSelIndex(index);
}
bool TBrowse::SetStyleColor(int id, TColor textColor, TColor bkColor)
{
TStyle idStyle(id);
const auto loc = Styles->Find(&idStyle);
if (loc != Styles->NPOS)
{
(*Styles)[loc]->TextColor = textColor;
(*Styles)[loc]->BkColor = bkColor;
if (GetHandle())
Invalidate();
return true;
}
return false;
}
bool TBrowse::SetStyleSelectedColor(int id, TColor textColor, TColor bkColor)
{
TStyle idStyle(id);
const auto loc = Styles->Find(&idStyle);
if (loc != Styles->NPOS)
{
(*Styles)[loc]->HilightTextColor = textColor;
(*Styles)[loc]->HilightBkColor = bkColor;
if (GetHandle())
Invalidate();
return true;
}
return false;
}
// Use a image from a image/mask pair
bool TBrowse::SetStyleImage(int id, TDib& iconImageAndMask)
{
TStyle idStyle(id);
const auto loc = Styles->Find(&idStyle);
if (loc != Styles->NPOS)
{
TStyle* Style = (*Styles)[loc];
if (Style->IconImage)
delete Style->IconImage ;
Style->IconImage = new TBitmap(iconImageAndMask);
delete Style->IconMask;
Style->IconMask = NULL;
if (List){
if (Style->IconImage->Height() + 2 > List->GetItemHeight(0)){
List->SetItemHeight(0, Style->IconImage->Height() / 2 + 2);
List->Invalidate();
}
}
else {
if (Style->IconImage->Height() + 2 > RowsHeight)
RowsHeight = Style->IconImage->Height() / 2 + 2;
}
if (GetHandle())
Invalidate();
return true;
}
return false;
}
// Create a mask from a given image and a transparent colour
bool TBrowse::SetStyleImage(int id, TDib& iconImage, TColor& faceColor)
{
TStyle idStyle(id);
const auto loc = Styles->Find(&idStyle);
if (loc != Styles->NPOS)
{
TStyle* Style = (*Styles)[loc];
delete Style->IconImage;
Style->IconImage = new TBitmap(iconImage);
delete Style->IconMask;
TMemoryDC imageDC;
imageDC.SelectObject(TBitmap(iconImage));
imageDC.SetBkColor(faceColor);
TMemoryDC maskDC;
TSize maskSize = TSize(iconImage.Width(), iconImage.Height());
TBitmap *maskBitmap = new TBitmap(maskSize.cx, maskSize.cy, 1, 1, 0);
maskDC.SelectObject(*maskBitmap);
maskDC.PatBlt(0, 0, maskSize.cx, maskSize.cy, WHITENESS);
maskDC.BitBlt(0, 0, maskSize.cx, maskSize.cy, imageDC, 0, 0, SRCCOPY);
maskDC.RestoreBitmap();
imageDC.RestoreBitmap();
Style->IconMask = maskBitmap ;
if (List) {
if (Style->IconImage->Height() + 2 > List->GetItemHeight(0)) {
List->SetItemHeight(0,Style->IconImage->Height() + 2);
List->Invalidate();
}
}
else {
if (Style->IconImage->Height() + 2 > RowsHeight)
RowsHeight = Style->IconImage->Height() + 2;
}
if (GetHandle())
Invalidate();
return true;
}
return false;
}
bool TBrowse::SetStyleTextAlignment(int id, uint16 uiTextAlignment)
{
TStyle idStyle(id);
const auto loc = Styles->Find(&idStyle);
if (loc != Styles->NPOS)
{
(*Styles)[loc]->TextAlignment = uiTextAlignment;
if (GetHandle())
Invalidate();
return true;
}
return false;
}
int TBrowse::SetTopIndex(int index)
{
return List->SetTopIndex(index);
}
void TBrowse::SetupWindow()
{
List = new TBrowseList(this, GetModule());
if (Attr.Style & TBS_MULTIPLESEL)
List->GetWindowAttr().Style |= LBS_MULTIPLESEL;
if (Attr.Style & TBS_EXTENDEDSEL)
List->GetWindowAttr().Style |= LBS_EXTENDEDSEL;
TControl::SetupWindow();
ClearFlag(wfPredefinedClass);
TRect rect = GetClientRect();
rect.top += HeadersHeight;
List->MoveWindow(rect);
}
// Performs an exchange sort
void TBrowse::SortItems(int colId, bool ascending)
{
int curRow, minRow, nextRow;
int rowCount = GetCount();
if (rowCount < 2){
if (Capture)
InvalidateRect(Capture->Rect.Offset(-ViewportOrgX, 0));
return;
}
THeader colIdHeader(colId, _T(""), 0, true, true);
const auto loc = Headers->Find(&colIdHeader);
if (loc == Headers->NPOS)
{
if (Capture)
InvalidateRect(Capture->Rect.Offset(-ViewportOrgX, 0));
return;
}
TCompareFunc func = (*Headers)[loc]->CompareFunc;
TCompareMemFunc memFunc = (*Headers)[loc]->CompareMemFunc;
if (func == NULL && memFunc == NULL){
if (Capture)
InvalidateRect(Capture->Rect.Offset(-ViewportOrgX, 0));
return;
}
for (curRow = 0; curRow < rowCount; curRow++){
minRow = curRow;
for (nextRow = curRow; nextRow < rowCount; nextRow++){
owl::tstring strNextRow, strMinRow;
GetColumnText(colId, strNextRow, nextRow);
GetColumnText(colId, strMinRow, minRow);
if (func){
bool greater = func(colId, strNextRow.c_str(), strMinRow.c_str());
if ((greater && ascending) || (!greater && !ascending))
minRow = nextRow;
}
else if (memFunc){
bool greater = (this->*memFunc)(colId, strNextRow.c_str(), strMinRow.c_str());
if ((greater && ascending) || (!greater && !ascending))
minRow = nextRow;
}
}
if (minRow > curRow){
LPARAM tmp = List->GetItemData(curRow);
List->SetItemData(curRow, List->GetItemData(minRow));
List->SetItemData(minRow, tmp);
}
}
if (GetHandle())
Invalidate();
}
bool TBrowse::ShowColumn(int id, int visible)
{
THeader idHeader(id, _T(""), 0, true, true);
const auto loc = Headers->Find(&idHeader);
if (loc != Headers->NPOS)
{
(*Headers)[loc]->Visible = visible;
if (GetHandle())
Invalidate();
return true;
}
return false;
}
void TBrowse::StartDrawingSizingLine(TPoint& point)
{
TRect rect = List->GetClientRect();
TWindowDC dc(*List);
TPen pen(TColor::Black, 1, PS_DOT);
dc.SetROP2(R2_XORPEN);
dc.SelectObject(pen);
dc.MoveTo(point.x, rect.top);
dc.LineTo(point.x, rect.bottom);
PrevSizingRect = TRect(point.x, rect.top, point.x, rect.bottom);
}
void TBrowse::StopDrawingSizingLine(TPoint& /*point*/)
{
TRect rect = List->GetClientRect();
TWindowDC dc(*List);
TPen pen(TColor::Black, 1, PS_DOT);
dc.SetROP2(R2_XORPEN);
dc.SelectObject(pen);
dc.MoveTo(PrevSizingRect.TopLeft());
dc.LineTo(PrevSizingRect.BottomRight());
}
uint TBrowse::Transfer(void *buffer, TTransferDirection direction)
{
if (!buffer && direction != tdSizeData) return 0;
long style = List->GetWindowLong(GWL_STYLE);
TBrowseData* browseData = (TBrowseData*)buffer;
if (direction == tdGetData){
browseData->Clear();
for (int i = 0; i < GetCount(); i++){
TColumns* src = (TColumns*)List->GetItemData(i);
browseData->AddRow();
for (int j = 0; j < (int)src->GetItemsInContainer(); j++){
browseData->SetColumnText((*src)[j].Id, (*src)[j].Text.c_str());
browseData->SetColumnStyle((*src)[j].Id, (*src)[j].StyleNo);
}
}
browseData->ResetSelections();
if (!(style & MULTIPLESEL))
browseData->Select(GetSelIndex());
else {
int selCount = GetSelCount();
if (selCount > 0){
int* selections = new int[selCount];
GetSelIndexes(selections, selCount);
for (int selIndex = 0; selIndex < selCount; selIndex++)
browseData->Select(selections[selIndex]);
delete[] selections;
}
}
}
else if (direction == tdSetData)
{
ClearList();
const int noSelection = -1;
int selCount = browseData->GetSelCount();
int selIndex = noSelection;
for (int i = 0; i < (int)browseData->GetItemDatas().GetItemsInContainer(); i++){
int index = AddRow();
TColumns* cols = (TColumns*)browseData->GetItemDatas()[i];
for (int j = 0; j < (int)cols->GetItemsInContainer(); j++){
SetColumnText((*cols)[j].Id, (*cols)[j].Text.c_str(), index);
SetColumnStyle((*cols)[j].Id, (*cols)[j].StyleNo, index);
}
if (style & MULTIPLESEL){
for (int k = 0; k < selCount; k++)
if (browseData->GetSelIndices()[k] == i){
SetSel(index, true);
break;
}
}
else {
if (browseData->GetSelIndices()[0] == i)
selIndex = index;
else
if (selIndex != noSelection && index <= selIndex)
selIndex++;
}
}
if (selIndex != noSelection && !(style & MULTIPLESEL))
SetSelIndex(selIndex);
}
return sizeof(TBrowseData);
}
//-- TBrowseData ---------------------------------------------------------------
TBrowseData::TBrowseData()
:
ItemDatas(10, 0, 10),
SelIndices(10, 0, 10)
{
NumCols = 0;
}
TBrowseData::TBrowseData(int numCols)
:
ItemDatas(10, 0, 10), SelIndices(10, 0, 10)
{
NumCols = numCols;
}
void TBrowseData::AddRow(bool isSelected)
{
TColumns *columns = new TColumns(NumCols, 0, 1);
ItemDatas.Add((uint32*)columns);
if (isSelected)
Select(ItemDatas.GetItemsInContainer()-1);
}
void TBrowseData::SetColumnText(int id, LPCTSTR text)
{
TColumns *cols = (TColumns*)ItemDatas[ItemDatas.GetItemsInContainer()-1];
const auto loc = cols->Find(TColumn{id, _T(""), 0});
if (loc != cols->NPOS)
(*cols)[loc].Text = text;
else
cols->Add(TColumn(id, text, 0));
}
void TBrowseData::SetColumnStyle(int id, int styleNo)
{
TColumns *cols = (TColumns*)ItemDatas[ItemDatas.GetItemsInContainer()-1];
const auto loc = cols->Find(TColumn{id, _T(""), 0});
if (loc != cols->NPOS)
(*cols)[loc].StyleNo = styleNo;
else
cols->Add(TColumn(id, _T(""), styleNo));
}
void TBrowseData::Select(int index)
{
if (index != LB_ERR)
SelIndices.Add(index);
}
//-- TRowArrayIterator----------------------------------------------------------
TRowArrayIterator::TRowArrayIterator(TRowArray& array)
:
TPtrArrayIterator<uint32*,TIPtrArray<uint32*> >(array)
{
}
bool TRowArrayIterator::GetColumnText(int id, owl::tstring& text)
{
TColumns* cols = (TColumns*)Current();
const auto loc = cols->Find(TColumn{id, _T(""), 0});
if (loc != cols->NPOS)
{
text = (*cols)[loc].Text;
return true;
}
return false;
}
} // OwlExt namespace
//==============================================================================
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: StyleNo.
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: StyleNo.
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: TextAlignment.
↑ V522 There might be dereferencing of a potential null pointer 'dynamic_cast< TBrowse * > (Parent)'.
↑ V728 An excessive check can be simplified. The '(A && B) || (!A && !B)' expression is equivalent to the 'bool(A) == bool(B)' expression.
↑ V728 An excessive check can be simplified. The '(A && B) || (!A && !B)' expression is equivalent to the 'bool(A) == bool(B)' expression.
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: Id, StyleNo.
↑ V730 It is possible that not all members of a class are initialized inside the constructor. Consider inspecting: HorzExtent, ViewportOrgX.
↑ V730 It is possible that not all members of a class are initialized inside the constructor. Consider inspecting: HorzExtent, ViewportOrgX.
↑ V522 There might be dereferencing of a potential null pointer 'browseData'.
↑ V525 The code contains the collection of similar blocks. Check items 'imageOrigin', 'maskOrigin', 'imageOrigin' in lines 451, 452, 453.
↑ V803 Decreased performance. In case 'i' is iterator it's more effective to use prefix form of increment. Replace iterator++ with ++iterator.
↑ V803 Decreased performance. In case 'i' is iterator it's more effective to use prefix form of increment. Replace iterator++ with ++iterator.
↑ V803 Decreased performance. In case 'i' is iterator it's more effective to use prefix form of increment. Replace iterator++ with ++iterator.
↑ V803 Decreased performance. In case 'i' is iterator it's more effective to use prefix form of increment. Replace iterator++ with ++iterator.
↑ V803 Decreased performance. In case 'i' is iterator it's more effective to use prefix form of increment. Replace iterator++ with ++iterator.
↑ V818 It is more efficient to use an initialization list 'Text(text)' rather than an assignment operator.
↑ V818 It is more efficient to use an initialization list 'Title(title)' rather than an assignment operator.
↑ V832 It's better to use '= default;' syntax instead of empty constructor body.