Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

955 lines
24 KiB

/*++
© 1998 Seagate Software, Inc. All rights reserved
Module Name:
IeList.cpp
Abstract:
CIeList is a subclassed (owner-draw) list control that groups items into
a 3D panel that have the same information in the indicated
sortColumn.
The panels are created from tiles. Each tile corresponds to one subitem
in the list, and has the appropriate 3D edges so that the tiles together
make up a panel.
NOTE: The control must be initialized with the number of columns and the
sort column. The parent dialog must implement OnMeasureItem and call
GetItemHeight to set the row height for the control.
Author:
Art Bragg [artb] 01-DEC-1997
Revision History:
--*/
#include "stdafx.h"
#include "IeList.h"
// Position of a tile in it's panel
#define POS_LEFT 100
#define POS_RIGHT 101
#define POS_TOP 102
#define POS_BOTTOM 103
#define POS_MIDDLE 104
#define POS_SINGLE 105
/////////////////////////////////////////////////////////////////////////////
// CIeList
BEGIN_MESSAGE_MAP(CIeList, CListCtrl)
//{{AFX_MSG_MAP(CIeList)
ON_NOTIFY_REFLECT(NM_CLICK, OnClick)
ON_WM_SETFOCUS()
ON_WM_KILLFOCUS()
//}}AFX_MSG_MAP
ON_WM_SYSCOLORCHANGE()
END_MESSAGE_MAP()
CIeList::CIeList()
/*++
Routine Description:
Sets default dimensions for the control.
Arguments:
none.
Return Value:
none.
--*/
{
//
// Initializations
//
m_ColCount = 0;
m_SortCol = 0;
m_pVertPos = NULL;
//
// Drawing dimensions
//
// If these are altered, the visual aspects of the control
// should be checked (especially the focus rectangle),
// as some minor adjustments may need to be made.
//
m_VertRaisedSpace = 1;
m_BorderThickness = 2;
m_VerticalTextOffsetTop = 1;
// The text height will be set later (based on the font size)
m_Textheight = 0;
m_VerticalTextOffsetBottom = 1;
// Total height will be set later
m_TotalHeight = 0;
m_HorzRaisedSpace = 1;
m_HorzTextOffset = 3;
}
CIeList::~CIeList()
/*++
Routine Description:
Cleanup.
Arguments:
none.
Return Value:
none.
--*/
{
// Cleanup the array of vertical positions
if( m_pVertPos ) free ( m_pVertPos );
}
/////////////////////////////////////////////////////////////////////////////
// CIeList message handlers
void CIeList::Initialize(
IN int colCount,
IN int sortCol
)
/*++
Routine Description:
Sets the number of columns (not easily available from MFC) and
the sort column.
Arguments:
colCount - number of columns to display
sortCol - column to sort on
Return Value:
none.
--*/
{
m_ColCount = colCount;
m_SortCol = sortCol;
}
void CIeList::DrawItem(
IN LPDRAWITEMSTRUCT lpDrawItemStruct
)
/*++
Routine Description:
This is the callback for an owner draw control.
Draws the appropriate text and/or 3D lines depending on the
item number and clipping rectangle supplied by MFC in lpDrawItemStruct
Arguments:
lpDrawItemStruct - MFC structure that tells us what and where to draw
Return Value:
none.
--*/
{
CDC dc;
int saveDc;
int colWidth = 0; // Width of current column
int horzPos = POS_MIDDLE; // Horz position in the panel
int vertPos = POS_SINGLE; // Vert position in the panel
BOOL bSelected = FALSE; // Is this item selected
CRect rcAllLabels; // Used to find left position of focus rectangle
CRect itemRect; // Rectangle supplied in lpDrawItemStruct
CRect textRect; // Rectangle for text
CRect boxRect; // Rectangle for 3D box (the panel)
CRect clipRect; // Current clipping rectangle
LPTSTR pszText; // Text to display
COLORREF clrTextSave = 0; // Save the current color
COLORREF clrBkSave = 0; // Save the background color
int leftStart = 0; // Left edge of where we're currently drawing
BOOL bFocus = (GetFocus() == this); // Do we have focus?
//
// Get the current scroll position
//
int nHScrollPos = GetScrollPos( SB_HORZ );
//
// Get the item ID from the list for the item we're drawing
//
int itemID = lpDrawItemStruct->itemID;
//
// Get item data for the item we're drawing
//
LV_ITEM lvi;
lvi.mask = LVIF_IMAGE | LVIF_STATE;
lvi.iItem = itemID;
lvi.iSubItem = 0;
lvi.stateMask = 0xFFFF; // get all state flags
GetItem(&lvi);
//
// Determine focus and selected states
//
bSelected = (bFocus || (GetStyle() & LVS_SHOWSELALWAYS)) && lvi.state & LVIS_SELECTED;
//
// Get the rectangle to draw in
//
itemRect = lpDrawItemStruct->rcItem;
dc.Attach( lpDrawItemStruct->hDC );
saveDc = dc.SaveDC();
//
// Get the clipping rectangle - we use it's vertical edges
// to optimize what we draw
//
dc.GetClipBox( &clipRect );
boxRect = clipRect;
//
// For each column, paint it's text and the section of the 3D panel
//
for ( int col = 0; col < m_ColCount; col++ ) {
colWidth = GetColumnWidth( col );
//
// Only paint this column if it's in the clipping rectangle
//
if( ( ( leftStart + colWidth ) > clipRect.left ) || ( leftStart < clipRect.right ) ) {
//
// Determine the horizontal position based on the column
//
horzPos = POS_MIDDLE;
if( col == 0 ) horzPos = POS_LEFT;
if( col == m_ColCount - 1 ) horzPos = POS_RIGHT;
//
// Calculate the rectangle for this tile
//
boxRect.top = itemRect.top;
boxRect.bottom = itemRect.bottom;
boxRect.left = itemRect.left + leftStart;
boxRect.right = itemRect.left + leftStart + colWidth;
//
// Get the vertical position from the array. It was saved there
// during SortItem for performance reasons.
//
if( m_pVertPos ) {
vertPos = m_pVertPos[ itemID ];
}
//
// Draw the tile for this item.
//
Draw3dRectx ( &dc, boxRect, horzPos, vertPos, bSelected );
//
// If this item is selected, change the text colors
//
if( bSelected ) {
clrTextSave = dc.SetTextColor( m_clrHighlightText );
clrBkSave = dc.SetBkColor( m_clrHighlight );
}
//
// Calculate the text rectangle
//
textRect.top = itemRect.top + m_VertRaisedSpace + m_BorderThickness + m_VerticalTextOffsetTop;
textRect.bottom = itemRect.bottom; // Text is top justified, no need to adjust bottom
textRect.left = leftStart - nHScrollPos + m_HorzRaisedSpace + m_BorderThickness + m_HorzTextOffset;
textRect.right = itemRect.right;
//
// Get the text and put in the "..." if we need them
//
CString pszLongText = GetItemText( itemID, col );
pszText = NULL;
MakeShortString(&dc, (LPCTSTR) pszLongText,
textRect.right - textRect.left, 4, &pszText);
BOOL bFree = TRUE;
if (pszText == NULL) {
// Failure of some kind...
pszText = (LPTSTR)(LPCTSTR)pszLongText;
bFree = FALSE;
}
//
// Now draw the text using the correct color
//
COLORREF saveTextColor;
if( bSelected ) {
saveTextColor = dc.SetTextColor( m_clrHighlightText );
} else {
saveTextColor = dc.SetTextColor( m_clrText );
}
int textheight = dc.DrawText( pszText, textRect, DT_NOCLIP | DT_LEFT | DT_TOP | DT_SINGLELINE );
dc.SetTextColor( saveTextColor );
if (pszText && bFree) {
free(pszText);
}
}
//
// Move to the next column
//
leftStart += colWidth;
}
//
// draw focus rectangle if item has focus. Use LVIR_BOUNDS rectangle
// to bound it.
//
GetItemRect(itemID, rcAllLabels, LVIR_BOUNDS);
if( lvi.state & LVIS_FOCUSED && bFocus ) {
CRect focusRect;
focusRect.left = rcAllLabels.left + m_HorzRaisedSpace + m_BorderThickness;
focusRect.right = min( rcAllLabels.right, (itemRect.right - m_HorzRaisedSpace * 2 - 3) );
focusRect.top = boxRect.top + m_VertRaisedSpace + m_BorderThickness;
focusRect.bottom = boxRect.top + m_TotalHeight - m_BorderThickness + 1;
dc.DrawFocusRect( focusRect );
}
// Restore colors
if( bSelected ) {
dc.SetTextColor( clrTextSave );
dc.SetBkColor( clrBkSave );
}
dc.RestoreDC( saveDc );
dc.Detach();
}
void CIeList::MakeShortString(
IN CDC* pDC,
IN LPCTSTR lpszLong,
IN int nColumnLen,
IN int nDotOffset,
OUT LPTSTR *ppszShort
)
/*++
Routine Description:
Determines it the supplied string fits in it's column. If not truncates
it and adds "...". From MS sample code.
Arguments:
pDC - Device context
lpszLong - Original String
nColumnLen - Width of column
nDotOffset - Space before dots
Return Value:
Shortened string
--*/
{
static const _TCHAR szThreeDots[] = _T("...");
int nStringLen = lstrlen(lpszLong);
*ppszShort = (_TCHAR *)malloc((nStringLen + 1) * sizeof(_TCHAR) + sizeof(szThreeDots));
if (*ppszShort == NULL)
return;
_TCHAR *szShort = *ppszShort;
if(nStringLen == 0) {
lstrcpy(szShort, _T(""));
} else {
lstrcpy(szShort, lpszLong);
}
if(nStringLen == 0 ||
(pDC->GetTextExtent(lpszLong, nStringLen).cx + nDotOffset) <= nColumnLen)
{
// return long format
return;
}
int nAddLen = pDC->GetTextExtent(szThreeDots,sizeof(szThreeDots)).cx;
for(int i = nStringLen-1; i > 0; i--)
{
szShort[i] = 0;
if((pDC->GetTextExtent(szShort, i).cx + nDotOffset + nAddLen)
<= nColumnLen)
{
break;
}
}
lstrcat(szShort, szThreeDots);
return;
}
void CIeList::Draw3dRectx (
IN CDC *pDc,
IN CRect &rect,
IN int horzPos,
IN int vertPos,
IN BOOL bSelected
)
/*++
Routine Description:
Draws the appropriate portion (tile) of a panel for a given cell in the
list. The edges of the panel portion are determined by the horzPos
and vertPos parameters.
Arguments:
pDc - Device context
rect - Rectangle to draw the panel portion in
horzPos - Where the portion is horizontally
vertPos - Where the portion is vertically
bSelected - Is the item selected
Return Value:
none.
--*/
{
CPen *pSavePen;
int topOffset = 0;
int rightOffset = 0;
int leftOffset = 0;
//
// If a given edge of the tile is to be drawn, set an offset to that
// edge. If we don't draw a given edge, the offset is 0.
//
switch ( horzPos )
{
case POS_LEFT:
leftOffset = m_HorzRaisedSpace;
rightOffset = 0;
break;
case POS_MIDDLE:
leftOffset = 0;
rightOffset = 0;
break;
case POS_RIGHT:
leftOffset = 0;
rightOffset = m_HorzRaisedSpace + 3;
break;
}
switch ( vertPos )
{
case POS_TOP:
topOffset = m_VertRaisedSpace;
break;
case POS_MIDDLE:
topOffset = 0;
break;
case POS_BOTTOM:
topOffset = 0;
break;
case POS_SINGLE:
topOffset = m_VertRaisedSpace;
break;
}
//
// Erase
//
if( !bSelected ) pDc->FillSolidRect( rect, m_clrBkgnd );
//
// Highlight the selected area
//
if (bSelected)
{
CRect selectRect;
if (leftOffset == 0)
selectRect.left = rect.left;
else
selectRect.left = rect.left + leftOffset + m_BorderThickness;
if (rightOffset == 0)
selectRect.right = rect.right;
else
selectRect.right = rect.right - rightOffset - m_BorderThickness + 1;
selectRect.top = rect.top + m_VertRaisedSpace + m_BorderThickness;
selectRect.bottom = rect.top + m_TotalHeight - m_BorderThickness + 1;
pDc->FillSolidRect( selectRect, m_clrHighlight );
}
// Select a pen to save the original pen
pSavePen = pDc->SelectObject( &m_ShadowPen );
// left edge
if( horzPos == POS_LEFT ) {
// Outside lighter line
pDc->SelectObject( &m_ShadowPen );
pDc->MoveTo( rect.left + leftOffset, rect.top + topOffset );
pDc->LineTo( rect.left + leftOffset, rect.top + m_TotalHeight + 1);
// Inside edge - darker line
pDc->SelectObject( &m_DarkShadowPen );
pDc->MoveTo( rect.left + leftOffset + 1, rect.top + topOffset);
pDc->LineTo( rect.left + leftOffset + 1, rect.top + m_TotalHeight + 1);
}
// right edge
if( horzPos == POS_RIGHT ) {
// Outside line
pDc->SelectObject( &m_HiLightPen );
pDc->MoveTo( rect.right - rightOffset, rect.top + topOffset );
pDc->LineTo( rect.right - rightOffset, rect.top + m_TotalHeight + 1 );
// Inside line
pDc->SelectObject( &m_LightPen );// note - this is usually the same color as btnface
if( vertPos == POS_TOP )
pDc->MoveTo( rect.right - rightOffset - 1, rect.top + topOffset + 1 );
else
pDc->MoveTo( rect.right - rightOffset - 1, rect.top + topOffset );
pDc->LineTo( rect.right - rightOffset - 1, rect.top + m_TotalHeight + 2 );
}
// top edge
if( ( vertPos == POS_TOP ) || ( vertPos == POS_SINGLE ) ) {
// Outside lighter
pDc->SelectObject( &m_ShadowPen );
pDc->MoveTo( rect.left + leftOffset, rect.top + topOffset );
pDc->LineTo( rect.right - rightOffset + 1, rect.top + topOffset );
// Inside edge darker
pDc->SelectObject( &m_DarkShadowPen );
if( horzPos == POS_LEFT )
pDc->MoveTo( rect.left + leftOffset + 1, rect.top + topOffset + 1 );
else
pDc->MoveTo( rect.left + leftOffset - 3, rect.top + topOffset + 1 );
pDc->LineTo( rect.right - rightOffset, rect.top + topOffset + 1);
}
// bottom edge
if( ( vertPos == POS_BOTTOM ) || ( vertPos == POS_SINGLE ) ) {
// Outside line
pDc->SelectObject( &m_HiLightPen );
if( horzPos == POS_LEFT )
pDc->MoveTo( rect.left + leftOffset + 1, rect.top + m_TotalHeight );
else
pDc->MoveTo( rect.left + leftOffset - 1, rect.top + m_TotalHeight );
pDc->LineTo( rect.right - rightOffset, rect.top + m_TotalHeight );
// Inside line
pDc->SelectObject( &m_LightPen );
if( horzPos == POS_LEFT )
pDc->MoveTo( rect.left + leftOffset + 2, rect.top + m_TotalHeight - 1 );
else
pDc->MoveTo( rect.left + leftOffset - 2, rect.top + m_TotalHeight - 1 );
pDc->LineTo( rect.right - rightOffset - 1, rect.top + m_TotalHeight - 1 );
}
pDc->SelectObject( pSavePen );
}
void CIeList::OnClick(
NMHDR* /* pNMHDR */, LRESULT* pResult
)
/*++
Routine Description:
When the list is clicked, we invalidate the
rectangle for the currently selected item
Arguments:
pResult - ununsed
Return Value:
none.
--*/
{
CRect rect;
// Get the selected item
int curIndex = GetNextItem( -1, LVNI_SELECTED );
if( curIndex != -1 ) {
GetItemRect( curIndex, &rect, LVIR_BOUNDS );
InvalidateRect( rect );
UpdateWindow();
}
*pResult = 0;
}
/*++
Routine Description:
Repaint the currently selected item if the style is LVS_SHOWSELALWAYS.
Arguments:
none.
Return Value:
none.
--*/
void CIeList::RepaintSelectedItems()
{
CRect rcItem, rcLabel;
//
// invalidate focused item so it can repaint properly
//
int nItem = GetNextItem(-1, LVNI_FOCUSED);
if(nItem != -1)
{
GetItemRect(nItem, rcItem, LVIR_BOUNDS);
GetItemRect(nItem, rcLabel, LVIR_LABEL);
rcItem.left = rcLabel.left;
InvalidateRect(rcItem, FALSE);
}
//
// if selected items should not be preserved, invalidate them
//
if(!(GetStyle() & LVS_SHOWSELALWAYS))
{
for(nItem = GetNextItem(-1, LVNI_SELECTED);
nItem != -1; nItem = GetNextItem(nItem, LVNI_SELECTED))
{
GetItemRect(nItem, rcItem, LVIR_BOUNDS);
GetItemRect(nItem, rcLabel, LVIR_LABEL);
rcItem.left = rcLabel.left;
InvalidateRect(rcItem, FALSE);
}
}
// update changes
UpdateWindow();
}
int CIeList::GetItemHeight(
IN LONG fontHeight
)
/*++
Routine Description:
Calculates the item height (the height of each drawing
rectangle in the control) based on the supplied fontHeight. This
function is used by the parent to set the item height for the
control.
Arguments:
fontHeight - The height of the current font.
Return Value:
Item height.
--*/
{
int itemHeight =
m_VertRaisedSpace +
m_BorderThickness +
m_VerticalTextOffsetTop +
fontHeight +
2 +
m_VerticalTextOffsetBottom +
m_BorderThickness +
1;
return itemHeight;
}
void CIeList::OnSetFocus(
CWnd* pOldWnd
)
/*++
Routine Description:
Repaint the selected item.
Arguments:
pOldWnd - Not used by this function
Return Value:
none
--*/
{
CListCtrl::OnSetFocus(pOldWnd);
// repaint items that should change appearance
RepaintSelectedItems();
}
void CIeList::OnKillFocus(
CWnd* pNewWnd
)
/*++
Routine Description:
Repaint the selected item.
Arguments:
pOldWnd - Not used by this function
Return Value:
none
--*/
{
CListCtrl::OnKillFocus(pNewWnd);
// repaint items that should change appearance
RepaintSelectedItems();
}
void CIeList::PreSubclassWindow()
/*++
Routine Description:
Calculate height parameters based on the font size. Set
colors for the control.
Arguments:
none.
Return Value:
none
--*/
{
CFont *pFont;
LOGFONT logFont;
pFont = GetFont( );
pFont->GetLogFont( &logFont );
LONG fontHeight = abs ( logFont.lfHeight );
m_Textheight = fontHeight + 2;
m_TotalHeight =
m_VertRaisedSpace +
m_BorderThickness +
m_VerticalTextOffsetTop +
m_Textheight +
m_VerticalTextOffsetBottom +
m_BorderThickness;
SetColors();
CListCtrl::PreSubclassWindow();
}
void CIeList::OnSysColorChange()
/*++
Routine Description:
Set the system colors and invalidate the control.
Arguments:
none.
Return Value:
none
--*/
{
SetColors();
Invalidate();
}
void CIeList::SetColors()
/*++
Routine Description:
Store the system colors and create pens.
Arguments:
none.
Return Value:
none
--*/
{
// Text colors
m_clrText = ::GetSysColor(COLOR_WINDOWTEXT);
m_clrTextBk = ::GetSysColor(COLOR_BTNFACE);
m_clrBkgnd = ::GetSysColor(COLOR_BTNFACE);
m_clrHighlightText = ::GetSysColor(COLOR_HIGHLIGHTTEXT);
m_clrHighlight = ::GetSysColor(COLOR_HIGHLIGHT);
// Line colors
m_clr3DDkShadow = ::GetSysColor( COLOR_3DDKSHADOW );
m_clr3DShadow = ::GetSysColor( COLOR_3DSHADOW );
m_clr3DLight = ::GetSysColor( COLOR_3DLIGHT );
m_clr3DHiLight = ::GetSysColor( COLOR_3DHIGHLIGHT );
SetBkColor( m_clrBkgnd );
SetTextColor( m_clrText );
SetTextBkColor( m_clrTextBk );
// Pens for 3D rectangles
if( m_DarkShadowPen.GetSafeHandle() != NULL )
m_DarkShadowPen.DeleteObject();
m_DarkShadowPen.CreatePen ( PS_SOLID, 1, m_clr3DDkShadow );
if( m_ShadowPen.GetSafeHandle() != NULL )
m_ShadowPen.DeleteObject();
m_ShadowPen.CreatePen ( PS_SOLID, 1, m_clr3DShadow );
if( m_LightPen.GetSafeHandle() != NULL )
m_LightPen.DeleteObject();
m_LightPen.CreatePen ( PS_SOLID, 1, m_clr3DLight );
if( m_HiLightPen.GetSafeHandle() != NULL )
m_HiLightPen.DeleteObject();
m_HiLightPen.CreatePen ( PS_SOLID, 1, m_clr3DHiLight );
}
BOOL CIeList::SortItems(
IN PFNLVCOMPARE pfnCompare,
IN DWORD dwData
)
/*++
Routine Description:
Override for SortItems. Checks the text of the sortColumn
for each line in the control against it's neighbors (above and
below) and assigns each line a position within it's panel.
Arguments:
pfnCompare - sort callback function
dwData - Unused
Return Value:
TRUE, FALSE
--*/
{
BOOL retVal = FALSE;
BOOL bEqualAbove = FALSE;
BOOL bEqualBelow = FALSE;
CString thisText;
CString aboveText;
CString belowText;
int numItems = GetItemCount();
//
// Call the base class to sort the items
//
if( CListCtrl::SortItems( pfnCompare, dwData ) ) {
//
// Get the vertical position (position within a panel) by comparing the text
// of the sort column and stash it in the array of vertical positions
//
if( m_pVertPos ) {
free( m_pVertPos );
}
m_pVertPos = (int *) malloc( numItems * sizeof( int ) );
if( m_pVertPos ) {
retVal = TRUE;
for( int itemID = 0; itemID < numItems; itemID++ ) {
//
// Get the text of the item and it's neighbors
//
thisText = GetItemText( itemID, m_SortCol );
aboveText = GetItemText( itemID - 1, m_SortCol );
belowText = GetItemText( itemID + 1, m_SortCol );
//
// Set booleans for the relationship of this item to it's
// neighbors
//
if( ( itemID == 0) || ( thisText.CompareNoCase( aboveText ) != 0 ) ){
bEqualAbove = FALSE;
} else {
bEqualAbove = TRUE;
}
if( ( itemID == GetItemCount() - 1 ) || ( thisText.CompareNoCase( belowText ) != 0 ) ) {
bEqualBelow = FALSE;
} else {
bEqualBelow = TRUE;
}
//
// Determine the position in the panel
//
if ( bEqualAbove && bEqualBelow ) m_pVertPos[ itemID ] = POS_MIDDLE;
else if( bEqualAbove && !bEqualBelow ) m_pVertPos[ itemID ] = POS_BOTTOM;
else if( !bEqualAbove && bEqualBelow ) m_pVertPos[ itemID ] = POS_TOP;
else m_pVertPos[ itemID ] = POS_SINGLE;
}
}
}
return( retVal );
}