// PropertyViewer.cpp #include "stdafx.h" #include "PropertyViewer.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif IMPLEMENT_DYNAMIC(CPropertyViewer, CListBox) BEGIN_MESSAGE_MAP(CPropertyViewer, CListBox) ON_WM_LBUTTONDBLCLK() ON_WM_LBUTTONDOWN() ON_WM_RBUTTONDOWN() ON_WM_FONTCHANGE() ON_WM_ERASEBKGND() END_MESSAGE_MAP() void CPropertyViewer::AddNamedDouble(const CString& Name, double Value) { CString S; S.Format("%0.8f", Value); // 0.100000001 => 0.1 int i=S.ReverseFind('.'); if(i==-1) { if(S=="-0") S="0"; }else{ // Format decimals: S.TrimRight('0'); if(S.Right(1)==".") { if(S=="-0.") S="0"; else S.Truncate(S.GetLength()-1); } } AddString(Name+": "+S); } bool CPropertyViewer::Adjust(CDC* pDC, CWidths& Columns, int Row, CString& S) { CString Name,Str,Int,Dec; if(!GetColText(Row, S,Name,Str,Int,Dec)) return false; Columns.Adjust(pDC, S,Name,Str,Int,Dec); return true; } bool CPropertyViewer::GetColText(int Row, CString& S, CString& Name, CString& Str, CString& Int, CString& Dec) const { if(!Valid(Row)) {S=Name=Str=Int=Dec=""; return false;} GetText(Row,S); return GetColText(S,Name,Str,Int,Dec); } bool CPropertyViewer::GetColText(CString& S, CString& Name, CString& Str, CString& Int, CString& Dec) { S.TrimRight(); if(S.IsEmpty()) return false; // Blank line int i=S.Find(':')+1; if(i) { Str=S.Mid(i); // Assume Text Value: Str.TrimLeft(); if(IsNumeric(Str)) { int d=Str.Find('.'); if(d==-1) Int=(Str=="-0" ? "0" : Str); // Don't want "-0" else { Int=Str.Left(d); Dec=Str.Mid(d); if(!Dec.IsEmpty()) { Dec.TrimRight('0'); if(Dec.GetAt(Dec.GetLength()-1)=='.') Dec+='0'; } if((Int=="-0") && (Dec==".0")) Int="0"; // Don't want "-0.0" } Str.Empty(); } if(Str.IsEmpty() && Int.IsEmpty() && Dec.IsEmpty()) return true; // Just a title string Name=S.Left(i); S.Empty(); } return true; } bool CPropertyViewer::IsNumeric(const char* ptr) { const CString OK("0123456789.-+"); if(*ptr==0) return false; for(char c; c=*ptr; ++ptr) if(OK.Find(c)==-1) return false; return true; } bool CPropertyViewer::IsProperty(int Row) { // Checks for a ':' in the string: if(!Valid(Row)) return false; CString S; GetText(Row, S); S.TrimRight(); int i=S.Find(':')+1; return i && (iReplaceSel(S,TRUE); } } SetCurSel(Item); } if(OldWnd) ::SetFocus(OldWnd); } void CPropertyViewer::OnFontChange() { FontChanged=true; CListBox::OnFontChange(); } void CPropertyViewer::GetRects(int Row, CRect* RectS, CRect* RectName, CRect* RectInt, CRect* RectDec, CRect* RectStr) { CClientDC dc(this); CFont* OldFont=(CFont*)dc.SelectObject(GetFont()); // Select the Font into a compatible dc to get the TextMetrics properly CRect Rect; // Rect.left is always 0 GetClientRect(&Rect); int WindowWidth=Rect.right; // The width of the window that can be drawn on. GetItemRect(Row,&Rect); int PaperWidth=Rect.right; // The rightmost visible pixel that needs to be drawn (includes scrolling) CString S; // The text for the whole row when there is no ':' CString Name; // The first field of each Row. Column[0] up to the first ':' CString Str; // Text which may span the final two columns CString Int; // The integer column[1] CString Dec; // The decimal column[2] if(Dirty) { // Find the HorizontalExtent and column widths if any text was changed: Dirty=false; if(FontChanged) { TEXTMETRIC Metrics; dc.GetTextMetrics(&Metrics); if(GetItemHeight(0)!=Metrics.tmHeight) SetItemHeight(0,Metrics.tmHeight); // Correct the Item Height } Widths.Clear(); for(int r=GetCount()-1; r>=0; --r) Adjust(&dc,Widths,r,S); Widths.Finish(); SetHorizontalExtent(Widths.HorizontalExtent); if(FontChanged) { // We can't quit this set of DrawItem calls, but we'll need another, so queue another Redraw: RedrawWindow(0,0, RDW_INVALIDATE | RDW_ERASE); FontChanged=false; } } CWidths WidthsNow(Widths); if(SectionalWidths && Widths.HorizontalExtent) { // Find the column widths for this section: CWidths WidthsMin; int r=Row; for(; r=Overlap) WidthsNow=WidthsMin; else WidthsNow.Name+=Overlap; } } GetColText(Row, S,Name,Str,Int,Dec); int Left=Rect.left++; // ++ is for Focus Rectangle if(!S.IsEmpty()) { if(RectS) *RectS=Rect; }else{ // Draw the text in each column: Rect.right=Rect.left+WidthsNow.Name; if(!Name.IsEmpty() && (RectName!=0)) *RectName=Rect; Rect.left=Rect.right; if(Str.IsEmpty()) { Rect.right+=WidthsNow.Int; if(!Int.IsEmpty() && (RectInt!=0)) *RectInt=Rect; Rect.left=Rect.right+1; // +1 for "2.": in some Fonts (eg. MS Sans Serif) the '2' and the '.' are too close, so render an extra pixel between the int and any points. Rect.right=PaperWidth; if(!Dec.IsEmpty() && (RectDec!=0)) *RectDec=Rect; }else{ // Text Value: Rect.right=PaperWidth; if(!Str.IsEmpty() && (RectStr!=0)) *RectStr=Rect; } } Rect.left=Left; dc.SelectObject(OldFont); } void CPropertyViewer::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) { CDC* pDC=CDC::FromHandle(lpDrawItemStruct->hDC); int Row= lpDrawItemStruct->itemID; CRect Rect(lpDrawItemStruct->rcItem); CRect RectS, RectName, RectInt, RectDec, RectStr; GetRects(Row, &RectS,&RectName,&RectInt,&RectDec,&RectStr); pDC->SetTextColor(OnSetForeGroundColor(Row)); pDC->SetBkColor (OnSetBackGroundColor(Row)); pDC->SetBkMode (TRANSPARENT); pDC->FillSolidRect(Rect, OnSetBackGroundColor(Row)); if(Lines) { --Rect.bottom; CPen Pen(PS_SOLID, 1, OnSetLineColor()); // Row separating Lines CPen* OldPen=pDC->SelectObject(&Pen); pDC->MoveTo(Rect.left , Rect.bottom); pDC->LineTo(Rect.right, Rect.bottom); pDC->SelectObject(OldPen); } if((lpDrawItemStruct->itemState & ODS_SELECTED) && (lpDrawItemStruct->itemAction & (ODA_SELECT | ODA_DRAWENTIRE))) { ++Rect.left; // Draw focus rectangle if item has focus: Rect.right=max(Rect.right, Widths.HorizontalExtent); // Stops a windows redraw bug when scrolling horizontally CBrush Brush(OnSetFocusColor()); pDC->FrameRect(Rect, &Brush); // pDC->DrawFocusRect(Rect); // Horizontal scrolling is messy with this } CString S; // The text for the whole row when there is no ':' CString Name; // The first field of each Row. Column[0] up to the first ':' CString Str; // Text which may span the final two columns CString Int; // The integer column[1] CString Dec; // The decimal column[2] GetColText(Row, S,Name,Str,Int,Dec); if(! S.IsEmpty()) pDC->DrawText( S, RectS , DT_LEFT | DT_SINGLELINE | DT_NOPREFIX | DT_VCENTER); else { // Draw the text in each column: if(! Name.IsEmpty()) pDC->DrawText(Name, RectName, DT_LEFT | DT_SINGLELINE | DT_NOPREFIX | DT_VCENTER); if(Str.IsEmpty()) { if(!Int.IsEmpty()) pDC->DrawText( Int, RectInt , DT_RIGHT | DT_SINGLELINE | DT_NOPREFIX | DT_VCENTER); if(!Dec.IsEmpty()) pDC->DrawText( Dec, RectDec , DT_LEFT | DT_SINGLELINE | DT_NOPREFIX | DT_VCENTER); }else{ // Text Value: if(!Str.IsEmpty()) pDC->DrawText( Str, RectStr , DT_LEFT | DT_SINGLELINE | DT_NOPREFIX | DT_VCENTER); } } } BOOL CPropertyViewer::OnEraseBkgnd(CDC* pDC) { ShowScrollBar(SB_BOTH); // CListBox doesn't like showing it's horizontal scroll bar, so force it. CRect Rect; GetClientRect(&Rect); int Count=GetCount(); if(Count!=0) { // Don't overdraw the Items: CRect ItemRect; GetItemRect(Count-1, &ItemRect); if(Rect.bottomFillSolidRect(Rect, OnSetBackGroundColor()); return TRUE; // CListBox::OnEraseBkgnd(pDC); }