// aplListHeaderCtrl.cpp : implementation file
//

#include "stdafx.h"
#include "apl_gui.h"
#include "resource.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CaplListHeaderCtrl



COLORREF CaplListHeaderCtrl::m_headerColor=::GetSysColor(COLOR_BTNFACE);
int CaplListHeaderCtrl::m_defItemWidth=100;

CaplListHeaderCtrl::CaplListHeaderCtrl()
{
	//initialise variables
	m_pImageList = NULL;
	m_RTL = FALSE;

	m_xIndent=0;
	m_yIndent=0;
	m_nRowsCount=0;
	m_defItemHeigth=0;
	m_nColumnsWidth=0;
	m_lFont=0;
	m_sumItemsHeight=0;
	m_bAutofit=false;
	m_LastMouseMovePoint.x=0;m_LastMouseMovePoint.y=0;
	m_defItemWidth=10;
	m_minItemWidth=2;
	m_view=0;
}

CaplListHeaderCtrl::~CaplListHeaderCtrl()
{

}

/////////////////////////////////////////////////////////////////////////////
// CaplListHeaderCtrl message handlers
//**********************************************************************


CImageList* CaplListHeaderCtrl::SetImageList( CImageList* pImageList )
{
	CImageList *pPrevList = m_pImageList;
	m_pImageList = NULL;
	if(pImageList && pImageList->m_hImageList)
		m_pImageList = pImageList;
	return pPrevList;
}

//**********************************************************************
int CaplListHeaderCtrl::GetItemImage( int nItem )
{
	int imageIndex;
	if( m_mapImageIndex.Lookup( nItem, imageIndex ) )
		return imageIndex;
	return -1;
}

//**********************************************************************
void CaplListHeaderCtrl::SetItemImage( int nItem, int nImage )
{
	// Save the image index
	m_mapImageIndex[nItem] = nImage;
}

//**********************************************************************
void CaplListHeaderCtrl::Autofit(int nOverrideItemData, int nOverrideWidth )
{
	if(!m_view) return;
	int nItemCount = m_headerItems.GetSize();
	int nDifferenceInWidth;

	if (!m_bAutofit)
		return;

	CRect rClient;
	m_view->GetClientRect(&rClient);

	if (GetColumnsWidth() != rClient.Width())
	{
		nDifferenceInWidth = abs(GetColumnsWidth()-rClient.Width());	// We need to shrink/expand all columns!
		
		// Shrink/expand all columns proportionally based on their current size
		for(int i=0;i<m_headerItems.GetSize();i++)
		{
			m_headerItems[i].m_nWidth=(m_headerItems[i].m_nWidth * rClient.Width()) / GetColumnsWidth();
		}
		m_view->RecalcColumnsWidth();
	}



}

int CaplListHeaderCtrl::InsertRow(int nPos)
{
	if(nPos>m_nRowsCount) return -1;
	for(int i=0;i<m_headerItems.GetSize();i++)
	{
		CRect rect(0,0,0,0);
		m_headerItems[i].m_rowText.InsertAt(nPos,_T(""));
		m_headerItems[i].m_textRect.Add(rect);
		m_headerItems[i].m_itemRect.Add(rect);
		bool bShowTooltip=false;
		m_headerItems[i].m_bShowTooltip.Add(bShowTooltip);
	}
	m_nRowsCount++;
	return nPos;
}

bool CaplListHeaderCtrl::SetHeaderItemText(int nRow,int nCol,LPCTSTR lpszText)
{
	if(nRow>m_nRowsCount) return false;
	if(nCol>m_headerItems.GetSize()) return false;
	m_headerItems[nCol].m_rowText.SetAt(nRow,lpszText);
	return true;
}
bool CaplListHeaderCtrl::SetHeaderColumnColor(int nCol,COLORREF color)
{
	if(nCol>m_headerItems.GetSize()) return false;
	m_headerItems[nCol].m_itemColor=color;
	return true;
}

bool CaplListHeaderCtrl::SetHeaderColumnSortType(int nCol,long sort_type)
{
	if(sort_type!=APL_COLUMN_STRING &&  
	   sort_type!=APL_COLUMN_NUMERIC &&
	   sort_type!=APL_COLUMN_DATE)
	{
		return false;
	}
	if(m_headerItems.GetSize()==0 || nCol<0 || nCol>m_headerItems.GetSize()) 
	{
		return false;
	}
	m_headerItems[nCol].m_sortType=sort_type;
	return true;
}

long CaplListHeaderCtrl::GetHeaderColumnSortType(int nCol)
{
	if(!m_headerItems.GetSize() || nCol<0 || nCol>m_headerItems.GetSize()) return -1;
	return m_headerItems[nCol].m_sortType;
}

bool CaplListHeaderCtrl::SetHeaderColumnTextColor(int nCol,COLORREF color)
{
	if(nCol>m_headerItems.GetSize()) return false;
	m_headerItems[nCol].m_textColor=color;
	return true;
}
bool CaplListHeaderCtrl::SetHeaderColumnTextBgrColor(int nCol,COLORREF color)
{
	if(nCol>m_headerItems.GetSize()) return false;
	m_headerItems[nCol].m_bgrColor=color;
	return true;
}

int CaplListHeaderCtrl::AddItem( LPCTSTR lpszColumnHeading, int nFormat, int nWidth )
{

	if(m_nRowsCount==0) m_nRowsCount=1;
	CaplHeaderItem headItem;
	headItem.m_lpszColumnHeading=lpszColumnHeading;
	headItem.m_nFormat=nFormat;
	headItem.m_nWidth=nWidth;
	headItem.m_rowText.Add(lpszColumnHeading);
	CRect rect(0,0,0,0);
	headItem.m_textRect.Add(rect);
	headItem.m_itemRect.Add(rect);
	bool bShowTooltip=false;
	headItem.m_bShowTooltip.Add(bShowTooltip);
	int nNewItem=m_headerItems.Add(headItem);
	
	return nNewItem;
}

BOOL CaplListHeaderCtrl::DeleteItem( int nPos)
{
	if(nPos<0 || nPos>=m_headerItems.GetSize()) return FALSE;
	m_headerItems.RemoveAt(nPos);
	return TRUE;
}

void CaplListHeaderCtrl::DeleteAllItems()
{
	if(!m_headerItems.GetSize()) return;
	m_headerItems.RemoveAll();
	m_headerItems.FreeExtra();
	m_nRowsCount=0;
}

int CaplListHeaderCtrl::GetColumnWidth(int nCol)
{
	if(nCol<0 || nCol>m_headerItems.GetSize()) return -1;
	if(m_headerItems[nCol].m_itemRect.GetSize()==0) return -1;
	return m_headerItems[nCol].m_nWidth;
}

UINT CaplListHeaderCtrl::GetColumnAlign(int nCol)
{
	if(nCol<0 || nCol>m_headerItems.GetSize()) return -1;
	UINT uFormat;

	if( m_headerItems[nCol].m_nFormat == LVCFMT_CENTER)	 
	{
		uFormat = DT_CENTER;
	}
	else if( m_headerItems[nCol].m_nFormat == LVCFMT_RIGHT) 
	{
		uFormat = DT_RIGHT;
	}
	else
	{
		uFormat = DT_LEFT;
	}

	return uFormat;
}

void CaplListHeaderCtrl::RecalcColumnsWidth()
{
	m_nColumnsWidth = 0;
	for(int i=0;i<m_headerItems.GetSize();i++)
		m_nColumnsWidth += GetColumnWidth(i);

}

void CaplListHeaderCtrl::PaintHeader(CDC &dc)
{
	if(!m_view) return;
	CRect rcClip, rcClient;

	m_view->GetClientRect(&rcClient);
	CRect wndRect;
	m_view->GetWindowRect(&wndRect);

	CPen pen(PS_SOLID,1,m_view->GetLineColor());
	CPen *oldPen=dc.SelectObject(&pen);

	CPoint cur_pos=m_view->GetScrollPosition();
	int xOffset=cur_pos.x;
	int yOffset=cur_pos.y;

	int top=m_yIndent+yOffset;
	int left=rcClient.left+m_xIndent;
	//int right = GetColumnsWidth()+m_xIndent;
	int right=max(rcClient.Width()-m_xIndent,GetColumnsWidth()+m_xIndent);//я вообще-то это не просто так написал :)
	
	int cy=top;
	int cx=left;
	int j=0;
	
	//подсчет координат (фикс ширина и изменяющаяся высота ячеек)
	CUIntArray maxItemHeights;
	int old_sumItemsHeight=m_sumItemsHeight;
	m_sumItemsHeight=0;
	int i;
	for(i=0;i<m_nRowsCount;i++)
	{
		//подсчет координат
		int maxItemHeight=0;
		cx=left;
		for(j=0;j<m_headerItems.GetSize();j++)
		{
			DrawItemText(dc,j,i,cx,cy,false);
			
			if(maxItemHeight<m_headerItems[j].m_textRect[i].Height()+m_headerItems[j].m_cy)
				maxItemHeight=m_headerItems[j].m_textRect[i].Height()+m_headerItems[j].m_cy;

			cx+=GetColumnWidth(j);
		}

		if(cx<right)
		{
			CRect rect1;
			rect1.top= cy;
			rect1.bottom= cy+maxItemHeight;
			rect1.left= cx;
			rect1.right= right;

			if(i>0) rect1.top++;
			
			dc.FillSolidRect(rect1, m_headerColor);
			
			INT bottomR=GetRValue(m_headerColor);
			INT bottomG=GetGValue(m_headerColor);
			INT bottomB=GetBValue(m_headerColor);
			INT topR=GetRValue(m_headerColor);
			INT topG=GetGValue(m_headerColor);
			INT topB=GetBValue(m_headerColor);
			
			bottomR-=20;if(bottomR<0) bottomR=0;
			bottomG-=20;if(bottomG<0) bottomG=0;
			bottomB-=20;if(bottomB<0) bottomB=0;
			
			topR+=20;if(topR>255) topR=255;
			topG+=20;if(topG>255) topG=255;
			topB+=20;if(topB>255) topB=255;
			
			COLORREF bottomColor=RGB(bottomR,bottomG,bottomB);
			COLORREF topColor=RGB(topR,topG,topB);
			CRect rect3d=rect1;
			rect3d.left++;
			//rect3d.right;
			dc.Draw3dRect(rect3d,topColor,bottomColor);			
		}

		
		if(maxItemHeight<m_defItemHeigth)
			maxItemHeight=m_defItemHeigth;
		maxItemHeights.Add((UINT)maxItemHeight);
		m_sumItemsHeight+=maxItemHeight;
		cx=left;
		for(j=0;j<m_headerItems.GetSize();j++)
		{
			// это для того, чтобы буквы "р", "у" полностью помещались в заголовке (иначе они обрезаются)
			m_headerItems[j].m_textRect[i].bottom ++;

			//расчитаем границы ячейки 
			m_headerItems[j].m_itemRect[i].top=cy;
			m_headerItems[j].m_itemRect[i].left=cx;
			m_headerItems[j].m_itemRect[i].right=cx+GetColumnWidth(j);
			m_headerItems[j].m_itemRect[i].bottom=cy+maxItemHeight;

			//скорректируем границы текста
			int v_delta=(int)((m_headerItems[j].m_itemRect[i].Height()-m_headerItems[j].m_textRect[i].Height())/2);
			m_headerItems[j].m_textRect[i].top+=v_delta;
			m_headerItems[j].m_textRect[i].bottom=m_headerItems[j].m_itemRect[i].bottom-m_headerItems[j].m_cy;
			m_headerItems[j].m_textRect[i].left+=m_headerItems[j].m_cx;
			//m_headerItems[j].m_textRect[i].right=m_headerItems[j].m_itemRect[i].right-20;//а это знаете ли аналог подгона по месту напильником - почему 20, а не 40 или 200 ?
			m_headerItems[j].m_textRect[i].right=m_headerItems[j].m_itemRect[i].right;

			//собственно отрисовка
			CRect fillRect=m_headerItems[j].m_itemRect[i];
			if(i>0) fillRect.top++;
			dc.FillSolidRect(fillRect,m_headerItems[j].m_itemColor);
			//dc.Draw3dRect(fillRect,RGB(255,255,255),m_view->GetLineColor());
			INT bottomR=GetRValue(m_headerItems[j].m_itemColor);
			INT bottomG=GetGValue(m_headerItems[j].m_itemColor);
			INT bottomB=GetBValue(m_headerItems[j].m_itemColor);
			INT topR=GetRValue(m_headerItems[j].m_itemColor);
			INT topG=GetGValue(m_headerItems[j].m_itemColor);
			INT topB=GetBValue(m_headerItems[j].m_itemColor);
			
			bottomR-=20;if(bottomR<0) bottomR=0;
			bottomG-=20;if(bottomG<0) bottomG=0;
			bottomB-=20;if(bottomB<0) bottomB=0;
			
			topR+=20;if(topR>255) topR=255;
			topG+=20;if(topG>255) topG=255;
			topB+=20;if(topB>255) topB=255;
			
			COLORREF bottomColor=RGB(bottomR,bottomG,bottomB);
			COLORREF topColor=RGB(topR,topG,topB);
			CRect rect3d=fillRect;
			rect3d.left++;
			//rect3d.right;
			dc.Draw3dRect(rect3d,topColor,bottomColor);
			DrawItemText(dc,j,i,cx,cy);

			//отрисовка иконки сортировки
			if(i==m_nRowsCount-1)
			{
				int imageIndex=GetItemImage(j);
				
				if(imageIndex!=-1)
				{
					int img_cx=0, img_cy=0;
					if( imageIndex != -1 )
					{
						IMAGEINFO imageinfo;
						if( m_pImageList!=0 && m_pImageList->GetImageInfo( imageIndex, &imageinfo ) )
						{
							img_cx=	imageinfo.rcImage.right - imageinfo.rcImage.left;
							img_cy=	imageinfo.rcImage.bottom - imageinfo.rcImage.top;
							
							CPoint point;
							point.y=m_headerItems[j].m_textRect[i].top +((m_headerItems[j].m_textRect[i].bottom-m_headerItems[j].m_textRect[i].top)-img_cy)/2;

							UINT uFormat = GetColumnAlign(j) ;
							if(uFormat & DT_RIGHT) // draw to the left of the label
							{
								point.x=m_headerItems[j].m_textRect[i].left;
							}
							else
							{
								point.x=m_headerItems[j].m_textRect[i].right - img_cx-3;
							}

							m_pImageList->Draw(&dc, imageIndex, point, ILD_TRANSPARENT );
						}
					}
				}
			}
			cx+=GetColumnWidth(j);
		}
		//отрисовка линии, отделяющей строку
		cy+=maxItemHeight;
		dc.MoveTo(left,cy);
		dc.LineTo(right,cy);
	}

	//изменим общую высоту заголовка
	if(m_sumItemsHeight<m_defItemHeigth)
		m_sumItemsHeight=m_defItemHeigth;
	
	//отрисовка вертикальных линий
	cx=left;
	int bottom=top+m_sumItemsHeight;
	if(m_xIndent)
	{
		dc.MoveTo(cx,top);
		dc.LineTo(cx,bottom);
	}
	int col_width=0;
	for(i=0;i<m_headerItems.GetSize();i++)
	{
		col_width=GetColumnWidth(i);
		cx+=col_width;
		dc.MoveTo(cx,top);
		dc.LineTo(cx,bottom);
	}
	//отрисовка правой границы заголовка, если ширина столбцов меньше клиентской области
	if(m_xIndent && GetColumnsWidth()<rcClient.right)
	{
		dc.MoveTo(right,top);
		dc.LineTo(right,bottom);
	}
	
	dc.SelectObject(&oldPen);
}

void CaplListHeaderCtrl::DrawItemText(CDC &dc,int nCol,int nRow,int cx,int cy,bool bDraw)
{
	if(!m_headerItems.GetSize()) return;
	if(nCol>=m_headerItems.GetSize()) return;
	if(nRow>m_nRowsCount) return;

	if(!bDraw) m_headerItems[nCol].m_textRect[nRow].left=cx+m_headerItems[nCol].m_cx;
	int right=cx+GetColumnWidth(nCol);
	if(!bDraw)
	{
		m_headerItems[nCol].m_textRect[nRow].right=right-m_headerItems[nCol].m_cx;
		m_headerItems[nCol].m_textRect[nRow].top=cy+m_headerItems[nCol].m_cy;
	}
		
	UINT colAlign;

	if(m_headerItems[nCol].m_rowText[nRow]==_T("")) 
	{
		if(!bDraw) m_headerItems[nCol].m_textRect[nRow].bottom=m_headerItems[nCol].m_textRect[nRow].top+m_defItemHeigth;
	}
	else
	{
		LOGFONT logfont;
		if(m_lFont)
			m_lFont->GetLogFont( &logfont );
		
		if(m_headerItems[nCol].m_bBold) 
			logfont.lfWeight=FW_BOLD;
		if(m_headerItems[nCol].m_bItalic) 
			logfont.lfItalic =TRUE;
		CFont chgdFontDC;
		chgdFontDC.CreateFontIndirect( &logfont );
	
		dc.SetBkColor( m_headerItems[nCol].m_bgrColor );
		dc.SetTextColor(m_headerItems[nCol].m_textColor);

		CFont *oldFont=dc.SelectObject(&chgdFontDC);

		if(!bDraw)
		{
			//текст самого хидера будем рисовать всегда по центру (если текст влезает)
			colAlign=DT_VCENTER|DT_CENTER;
			m_headerItems[nCol].m_bShowTooltip[nRow]=false;
			dc.DrawText(m_headerItems[nCol].m_rowText[nRow],m_headerItems[nCol].m_textRect[nRow],colAlign|DT_WORDBREAK|DT_EXTERNALLEADING|DT_CALCRECT);
			if(m_headerItems[nCol].m_textRect[nRow].right>right) 
			{
				m_headerItems[nCol].m_bShowTooltip[nRow]=true;
			}
			m_headerItems[nCol].m_textRect[nRow].bottom+=m_headerItems[nCol].m_cy;
		}
		if(bDraw) 
		{
			colAlign=DT_END_ELLIPSIS|DT_VCENTER;
			if(m_headerItems[nCol].m_bShowTooltip[nRow]) colAlign=colAlign|DT_LEFT;
			else colAlign=colAlign|DT_CENTER;
			dc.DrawText(m_headerItems[nCol].m_rowText[nRow],m_headerItems[nCol].m_textRect[nRow],colAlign|DT_EXTERNALLEADING|DT_WORDBREAK);
		}
		dc.SelectObject(oldFont);
	}
}

bool CaplListHeaderCtrl::GetActiveItem(CPoint &point,int &nCol,int &nRow)
{
	nCol=-1;
	nRow=-1;
	if(!m_view) return false;
	CPoint p=point;
	CPoint cur_pos=m_view->GetScrollPosition();
	p.x+=cur_pos.x;
	p.y+=cur_pos.y;
	for(int i=0;i<m_headerItems.GetSize();i++)
	{
		for(int j=0;j<m_nRowsCount;j++)
		{
			if(m_headerItems[i].m_itemRect[j].PtInRect(p))
			{
				nCol=i;
				nRow=j;
				return true;
			}

		}
	}
	return false;
}

CaplListHeaderCtrl::CaplHeaderItem* CaplListHeaderCtrl::GetResizingColumn(CPoint &point)
{
	if(!m_view) return 0;
	CPoint cur_pos=m_view->GetScrollPosition();
	RECT r;
	int x=point.x+cur_pos.x;
	for(int i=0;i<m_headerItems.GetSize();i++)
	{
		r=m_headerItems[i].m_itemRect[0];
		if(x>r.left && x>r.right-2 && x<r.right+2)
		{
			return &m_headerItems[i];
		}
	}
	return 0;
}