#include "stdafx.h"
#include "ReportDict.h"
#include <math.h>

#define PI 3.1415926535897932

int compare_ILS_TimeLineByBegin(const void *arg1, const void *arg2)
{
	Timeline_values_struct* elem1 = *(Timeline_values_struct**)arg1;	
	Timeline_values_struct* elem2 = *(Timeline_values_struct**)arg2;	

	int result=0;
	if(elem1->begin > elem2->begin) 
	{
		result=1;
	}
	else if(elem1->begin == elem2->begin)
	{
		if(elem1->end > elem2->end)
		{
			result=1;
		}
		else if(elem1->end == elem2->end)
		{
			result=0;
		}
		else
		{
			result=-1;
		}
	}
	else
	{
		result=-1;
	}
	return result;
}

int compare_ILS_TimeLineByEnd(const void *arg1, const void *arg2)
{
	Timeline_values_struct* elem1 = *(Timeline_values_struct**)arg1;	
	Timeline_values_struct* elem2 = *(Timeline_values_struct**)arg2;	

	int result=0;
	if(elem1->end > elem2->end) 
	{
		result=1;
	}
	else if(elem1->end == elem2->end)
	{
		if(elem1->begin > elem2->begin)
		{
			result=1;
		}
		else if(elem1->begin == elem2->begin)
		{
			result=0;
		}
		else
		{
			result=-1;
		}
	}
	else
	{
		result=-1;
	}
	return result;
}

bool Report_SortTimeLine(CArray<Timeline_values_struct>& values, bool bEnd /*= false*/)
{
	CArray<Timeline_values_struct> copy;
	CArray<Timeline_values_struct*> sort;
	for(int i = 0; i < values.GetSize(); i++)
		copy.Add(values[i]);
	for(int i = 0; i < copy.GetSize(); i++)
		sort.Add(&copy[i]);
	if(bEnd == false)
		qsort(sort.GetData(), sort.GetSize(), 4, compare_ILS_TimeLineByBegin);
	else
		qsort(sort.GetData(), sort.GetSize(), 4, compare_ILS_TimeLineByEnd);
	values.RemoveAll();
	for(int i = 0; i < sort.GetSize(); i++)
		values.Add(*sort.GetAt(i));

	return true;
}

CString DoubleToTimeString(double val)
{
	bool negative = false;
	if(val < 0)
	{
		negative = true;
		val = -val;
	}

	long lHours = (long)val;
	long lMinutes = (long)((val-lHours)*60+0.5);
	if (lMinutes < 0) lMinutes = 0;
	long lSeconds = (long)((val-lHours-lMinutes/60.0)*3600+0.5);
	if (lSeconds < 0) lSeconds = 0;

	TCHAR buf[128];
	buf[0] = 0;

	if(lSeconds)
		_stprintf(buf, _T("%d:%.2d:%.2d"),lHours, lMinutes, lSeconds);
	else
		_stprintf(buf, _T("%d:%.2d"), lHours, lMinutes);

	CString time;
	if(negative)
		time = buf[0] != 0 ? CString(_T("-")) + buf : _T("");
	else
		time = buf[0] != 0 ? buf : _T("");
	return time;
}

CGistogramElement::CGistogramElement(CaplReportMgr* ReportMgr)
		:CReportElement(ReportMgr, E_GISTOGRAM), m_XLine(ReportMgr), m_YLine(ReportMgr),
		m_Legend(ReportMgr), m_DataSource(NULL, ReportMgr)
{
	m_name = APL_T("Гистограмма");
	
	m_pGroupColumn = NULL;

	m_ViewType = 0;
	
	m_XLine.m_name = _T("X");
	m_YLine.m_name = _T("Y");
	m_YLine.m_bCalcFromZero = true;
	m_XLine.m_pGistogram = this;
	m_YLine.m_pGistogram = this;
	m_YLine.m_skip_elements = 1;
	
	m_background_color = RGB(220, 220, 220);

	m_Angel = 45;
	m_Volume = 30;
	m_SortDirection = aplAZ;

	m_UseColorRef = 0xffffffff;
	m_UserColorParts = -1;
	m_UseColors.RemoveAll();

#ifdef _DEBUG
	m_assert_count = 0;
#endif
}

CGistogramElement::~CGistogramElement()
{
}

Gdiplus::Graphics* CGistogramElement::CreateGDIPlusGraphics(HDC hDC, Gdiplus::GraphicsContainer& container)
{
	Gdiplus::Graphics* graphics = new Gdiplus::Graphics(hDC);
	container = graphics->BeginContainer();
	graphics->SetSmoothingMode(Gdiplus::SmoothingModeHighQuality);
	graphics->SetInterpolationMode(Gdiplus::InterpolationModeHighQualityBicubic);
	graphics->SetCompositingQuality(Gdiplus::CompositingQualityHighQuality);
	graphics->SetPixelOffsetMode(Gdiplus::PixelOffsetModeHighQuality);
//	graphics->SetCompositingMode(Gdiplus::CompositingModeSourceCopy); // не дружит с DrawString в Windows 7
	graphics->SetPageUnit(Gdiplus::UnitMillimeter);
	graphics->SetPageScale((float)0.1);
	return graphics;
}

bool CGistogramElement::DeleteGDIPlusGraphics(Gdiplus::Graphics** graphics, Gdiplus::GraphicsContainer& container)
{
	ASSERT(graphics);
	ASSERT(*graphics);

	HDC gdiDC = (*graphics)->GetHDC();
	(*graphics)->ReleaseHDC(gdiDC);

	(*graphics)->EndContainer(container);
	delete *graphics;
	*graphics = NULL;

	return true;
}

BOOL CGistogramElement::Draw(CDC* pDC, double scale, int draw_mode, UINT flag /* = DRAW_SEL|DRAW_PICTURE|DRAW_TEXT */)
{
	CReportElement::Draw(pDC, scale, draw_mode, flag);

	if(m_ViewType == APL_GISTOGRAM_VIEW_TYPE_COLUMN)
		return DrawColumn(pDC, scale, draw_mode, flag);
	else if(m_ViewType == APL_GISTOGRAM_VIEW_TYPE_TIMELINE)
		return DrawTimeLine(pDC, scale, draw_mode, flag);
	else if(m_ViewType == APL_GISTOGRAM_VIEW_TYPE_CYCLE)
		return DrawCycle(pDC, scale, draw_mode, flag);
	else
		return FALSE;
}

BOOL CGistogramElement::DrawColumn(CDC* pDC, double scale, int draw_mode, UINT flag)
{
	int rows, cols;
	m_DataSource.GetSize(rows, cols);

	if(draw_mode==0 && rows<1 || cols==0)//Значит это режим редактирования
	{
		CPoint pt[6];
		CPen obj_pen, *old_pen;
		CPen simple_pen;
		CRect rect;
		CBrush blue_br, red_br, *old_brush;
		int i, dx, width;

		CRgn rgn1, rgn2;
		HRGN OldRgn = CreateRectRgn(0,0,1,1);
		int res = GetClipRgn(pDC->m_hDC, OldRgn);
		CRect RgnRect;
		GetRect(rect, scale, pDC->IsPrinting());
		RgnRect = rect;
		RgnRect.NormalizeRect();
		RgnRect.DeflateRect(m_line_width, m_line_width);
		ConvertLOMETRICtoTEXT(pDC, RgnRect);
		rgn1.CreateRectRgnIndirect(RgnRect);

		obj_pen.CreatePen(PS_SOLID | PS_GEOMETRIC, (int)((double)m_line_width*scale), m_color);
		simple_pen.CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
		blue_br.CreateSolidBrush(RGB(10, 10, 200));
		red_br.CreateSolidBrush(RGB(200, 10, 10));
		old_pen = pDC->SelectObject(&obj_pen);
		
		DrawRect(pDC, rect);
		if(m_select && !pDC->IsPrinting() && flag&DRAW_SEL) 
			DrawSelPoints(pDC, scale); // отрисовка рамки выделения

		if(res==1)
		{
			rgn2.Attach(OldRgn);
			rgn1.CombineRgn(&rgn1, &rgn2, RGN_AND);
			rgn2.Detach();
		}
		int Result = pDC->SelectClipRgn(&rgn1);

		//рисуем подобие гистограммы
		rect.DeflateRect(m_line_width, (int)(-m_line_width-m_Volume*scale), m_line_width, -m_line_width);

		pt[0].y = rect.bottom-(int)(rect.Height()*0.7);
		pt[1].y = rect.bottom-(int)(rect.Height()*0.5);
		pt[2].y = rect.bottom-(int)(rect.Height()*0.9);
		pt[3].y = rect.bottom-(int)(rect.Height()*0.4);
		pt[4].y = rect.bottom-(int)(rect.Height()*0.8);
		pt[5].y = rect.bottom-(int)(rect.Height()*0.9);

		pt[0].x = rect.bottom-(int)(rect.Height()*0.6);
		pt[1].x = rect.bottom-(int)(rect.Height()*0.8);
		pt[2].x = rect.bottom-(int)(rect.Height()*0.8);
		pt[3].x = rect.bottom-(int)(rect.Height()*0.1);
		pt[4].x = rect.bottom-(int)(rect.Height()*0.7);
		pt[5].x = rect.bottom-(int)(rect.Height()*0.95);

		dx = rect.Width()/6;

		width = dx/3;
		pDC->SelectObject(&simple_pen);
		for(i=0; i<6; i++)
		{
			old_brush = pDC->SelectObject(&blue_br);
			Draw3DRect(rect.left+dx/6+i*dx, rect.bottom, rect.left+dx/6+i*dx+width, pt[i].x, scale, pDC, RGB(10, 10, 200), rect.bottom, pt[i].x, false);
			old_brush = pDC->SelectObject(&red_br);
			Draw3DRect(rect.left+dx/6+i*dx+width, rect.bottom, rect.left+dx/6+i*dx+width*2, pt[i].y, scale, pDC, RGB(200, 10, 10), rect.bottom, pt[i].y, false);
		}
		
		pDC->SelectObject(old_pen);
		pDC->SelectObject(old_brush);

		rgn1.DeleteObject();
		rgn1.Attach(OldRgn);
		if(res==1)
			pDC->SelectClipRgn(&rgn1);
		else
			pDC->SelectClipRgn(NULL);
		rgn1.DeleteObject();
	}
	else
	{
		CRect rect;
		int i;
		int iDI;
		int ind;
		int x, y;

		double dVal;
		CString sVal;
		
		CRgn rgn1, rgn2;
		HRGN OldRgn = CreateRectRgn(0,0,1,1);
		int res = GetClipRgn(pDC->m_hDC, OldRgn);
		ASSERT(res>-1);
		CRect RgnRect;
		GetRect(rect, scale, pDC->IsPrinting());
		RgnRect = rect;
		RgnRect.DeflateRect(-(m_line_width), m_line_width);
		ConvertLOMETRICtoTEXT(pDC, RgnRect);
		rgn1.CreateRectRgnIndirect(RgnRect);
		if(res==1)
		{
			rgn2.Attach(OldRgn);
			rgn1.CombineRgn(&rgn1, &rgn2, RGN_AND);
			rgn2.Detach();
		}
		
		CPen obj_pen, *old_pen;
		
		GetRect(rect, scale, pDC->IsPrinting());
		
		if(m_line_width!=0)
			obj_pen.CreatePen(PS_SOLID|PS_GEOMETRIC, (int)((double)m_line_width*scale), m_color);
		else if(draw_mode==0)
			obj_pen.CreatePen(PS_SOLID|PS_GEOMETRIC, (int)1, RGB(192,192,192));
		old_pen = pDC->SelectObject(&obj_pen);

		if(draw_mode==0 || m_line_width>0)
			DrawRect(pDC, rect);
		if(m_select && !pDC->IsPrinting() && flag&DRAW_SEL && draw_mode==0) 
			DrawSelPoints(pDC, scale); // отрисовка рамки выделения

		int Result = pDC->SelectClipRgn(&rgn1);

		//Отрисовываем гистограмму
		//пропорционально

		if(rows>0 && m_Elements.GetSize()>0)
		{
			//отрисовка
			DrawLegeng(pDC, rect);
			int defl = (int)(rect.Width()>-rect.Height()?-rect.Height()*0.03:rect.Width()*0.03);
			defl = defl>50?50:defl;

			rect.DeflateRect(defl/2, -defl/2);
			m_background_color = RGB(255, 255, 255);
			pDC->FillSolidRect(rect, m_background_color);
			rect.DeflateRect(defl/2, -defl/2);
			
			m_XLine.OffsetDrawRect(pDC, rect, scale, &m_DataSource, m_dXMin, m_dXRange);
			m_YLine.OffsetDrawRect(pDC, rect, scale, &m_DataSource, m_dYMin, m_dYRange);
			
			m_XLine.Draw(pDC, rect, scale, &m_DataSource, m_dXMin, m_dXRange);
			m_YLine.Draw(pDC, rect, scale, &m_DataSource, m_dYMin, m_dYRange);

			double angel = ((double)m_Angel)/180.0*PI;
			int dx = (int)(m_Volume*sin(angel)/cos(angel));
			double width = (double)(rect.Width()-dx)/(rows*m_Elements.GetSize());
			for(iDI=0; iDI<m_Elements.GetSize(); iDI++)
			{
				ind = m_DataSource.GetHeader()->Find(m_Elements[iDI]->GetColumnName());
				if(ind<0) continue;

				COLORREF DefaultColor = m_Elements[iDI]->m_color;
				if(m_ReportMgr->m_data_source.GetUseUserColors())
				{
					CArray<COLORREF> clrs;
					m_ReportMgr->m_data_source.GetUserColors(clrs);
					if(clrs.GetSize() > 0)
						DefaultColor = clrs[0];
				}
				CPen pen, *old;
				CBrush br, *old_br;
				br.CreateSolidBrush(DefaultColor);
				pen.CreatePen(PS_SOLID, (int)(m_Elements[iDI]->m_line_width*scale), RGB(0, 0, 0));
				old = pDC->SelectObject(&pen);
				old_br = pDC->SelectObject(&br);
				
				for(i=0; i<rows; i++)
				{
					x = (int)(rect.left+i*m_Elements.GetSize()*width+width/4+width*iDI);
					m_DataSource.GetAt(i, ind, sVal);
					if(sVal.Find(_T(';'))!=-1) // если в столбике выводится несколько значений
					{
						std::vector<std::pair<std::pair<double, COLORREF>, std::pair<int, int>>> val_str;
						CStringArray sArr, AdditionalCoordinateArray;
						bool bAdditionalLine;
						Report_ConvertStrAggr2Array(sVal, sArr, AdditionalCoordinateArray, bAdditionalLine);

						dVal = 0.0;
						double dSumVal = 0.0;
						for(int s=0; s<sArr.GetSize();s++)
							dSumVal += __atof(sArr[s]);
						int ySum = rect.bottom-(int)((dSumVal-m_dYMin)*(double)(rect.Height()+m_Volume)/m_dYRange);

						CArray<COLORREF> column_colors;
						bool ret_c = GetColor(m_Elements[iDI]->m_color, sArr.GetSize(), column_colors);
						ASSERT(ret_c);

						int bottom = rect.bottom;
						for(int s=0; s<sArr.GetSize();s++)
						{
							double val = __atof(sArr[s]);
							if(val==0.0) continue;
							dVal += val;
							y = rect.bottom-(int)((dVal-m_dYMin)*(double)(rect.Height()+m_Volume)/m_dYRange);

							COLORREF color_brush = column_colors[s];
							br.DeleteObject();
							pDC->SelectObject(old_br);
							br.CreateSolidBrush(color_brush);
							old_br = pDC->SelectObject(&br);
							Draw3DRect(x, bottom, (int)(x+width/2), y, scale, pDC, color_brush, rect.bottom, ySum, bAdditionalLine);
							val_str.push_back(std::make_pair(std::make_pair(val, color_brush), std::make_pair(bottom, y)));

							bottom = y;
						}
						if(dVal==0.0) // если все значения равны 0, надо нарисовать хотя бы "площадку"
						{
							y = rect.bottom-(int)((dVal-m_dYMin)*(double)(rect.Height()+m_Volume)/m_dYRange);

							COLORREF color_brush = DefaultColor;
							br.DeleteObject();
							pDC->SelectObject(old_br);
							br.CreateSolidBrush(color_brush);
							old_br = pDC->SelectObject(&br);
							Draw3DRect(x, rect.bottom, (int)(x+width/2), y, scale, pDC, color_brush, rect.bottom, y, bAdditionalLine);
						}
						// отрисовка значений столбика
						for(std::vector<std::pair<std::pair<double, COLORREF>, std::pair<int, int>>>::iterator it = val_str.begin(); it != val_str.end(); it++)
						{
							bool bLast = false;
							std::vector<std::pair<std::pair<double, COLORREF>, std::pair<int, int>>>::iterator it1 = it;
							it1++;
							if(it1 == val_str.end()) bLast = true;
							DrawRectValueString(it->first.first, x, it->second.first, (int)(x+width/2), it->second.second, scale, pDC, it->first.second, rect.bottom, rect.top, bLast);
						}
					}
					else  // если в столбике выводится только одно значение
					{
						m_DataSource.GetAt(i, ind, dVal);
						double dVal1= (dVal-m_dYMin)*((double)(rect.Height()+m_Volume)/m_dYRange);
						y = rect.bottom-(int)(dVal1);
						Draw3DRect(x, rect.bottom, (int)(x+width/2), y, scale, pDC, DefaultColor, rect.bottom, y, false);
						// отрисовка значений столбика
						DrawRectValueString(dVal, x, rect.bottom, (int)(x+width/2), y, scale, pDC, DefaultColor, rect.bottom, rect.top, true);
					}
				}
				pDC->SelectObject(old_br);
				pDC->SelectObject(old);
				pen.DeleteObject();
				br.DeleteObject();
			}
		}
		pDC->SelectObject(old_pen);

		rgn1.DeleteObject();
		rgn1.Attach(OldRgn);
		if(res==1)
			pDC->SelectClipRgn(&rgn1);
		else
			pDC->SelectClipRgn(NULL);
		rgn1.DeleteObject();
	}

	return TRUE;
}

BOOL CGistogramElement::DrawTimeLine(CDC* pDC, double scale, int draw_mode, UINT flag)
{
	int rows, cols;
	m_DataSource.GetSize(rows, cols);

	if(draw_mode==0 && rows<1 || cols==0)//Значит это режим редактирования
	{
		CPen obj_pen, *old_pen;
		CPen simple_pen;
		CRect rect;
		CBrush* old_brush;
		CBrush br[6];

		CRgn rgn1, rgn2;
		HRGN OldRgn = CreateRectRgn(0,0,1,1);
		int res = GetClipRgn(pDC->m_hDC, OldRgn);
		CRect RgnRect;
		GetRect(rect, scale, pDC->IsPrinting());
		RgnRect = rect;
		RgnRect.NormalizeRect();
		RgnRect.DeflateRect(m_line_width, m_line_width);
		ConvertLOMETRICtoTEXT(pDC, RgnRect);
		rgn1.CreateRectRgnIndirect(RgnRect);

		double angel = ((double)m_Angel)/180.0*PI;
		int width = rect.Width() - (int)(m_Volume*scale*(sin(angel)/cos(angel))) - 15;

		obj_pen.CreatePen(PS_SOLID | PS_GEOMETRIC, (int)((double)m_line_width*scale), m_color);
		simple_pen.CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
		br[0].CreateSolidBrush(RGB(10, 10, 200));
		br[1].CreateSolidBrush(RGB(200, 10, 10));
		br[2].CreateSolidBrush(RGB(10, 200, 10));
		br[3].CreateSolidBrush(RGB(10, 10, 100));
		br[4].CreateSolidBrush(RGB(100, 10, 10));
		br[5].CreateSolidBrush(RGB(10, 100, 10));
		old_pen = pDC->SelectObject(&obj_pen);

		DrawRect(pDC, rect);
		if(m_select && !pDC->IsPrinting() && flag&DRAW_SEL) 
			DrawSelPoints(pDC, scale); // отрисовка рамки выделения

		if(res==1)
		{
			rgn2.Attach(OldRgn);
			rgn1.CombineRgn(&rgn1, &rgn2, RGN_AND);
			rgn2.Detach();
		}
		int Result = pDC->SelectClipRgn(&rgn1);

		//рисуем подобие гистограммы
		rect.DeflateRect(m_line_width, (int)(-m_line_width-m_Volume*scale), m_line_width, -m_line_width);

		pDC->SelectObject(&simple_pen);

		old_brush = pDC->SelectObject(&br[0]);
		Draw3DRect(rect.left, rect.bottom, rect.left+(int)(width*0.2), rect.top, scale, pDC, RGB(10, 10, 200), rect.bottom, rect.top, false);
		pDC->SelectObject(&br[1]);
		Draw3DRect(rect.left+(int)(width*0.2), rect.bottom, rect.left+(int)(width*0.3), rect.top, scale, pDC, RGB(200, 10, 10), rect.bottom, rect.top, false);
		pDC->SelectObject(&br[2]);
		Draw3DRect(rect.left+(int)(width*0.3), rect.bottom, rect.left+(int)(width*0.45), rect.top, scale, pDC, RGB(10, 200, 10), rect.bottom, rect.top, false);
		pDC->SelectObject(&br[3]);
		Draw3DRect(rect.left+(int)(width*0.45), rect.bottom, rect.left+(int)(width*0.8), rect.top, scale, pDC, RGB(10, 10, 100), rect.bottom, rect.top, false);
		pDC->SelectObject(&br[4]);
		Draw3DRect(rect.left+(int)(width*0.8), rect.bottom, rect.left+(int)(width*0.9), rect.top, scale, pDC, RGB(100, 10, 10), rect.bottom, rect.top, false);
		pDC->SelectObject(&br[5]);
		Draw3DRect(rect.left+(int)(width*0.9), rect.bottom, rect.left+(int)(width*1.0), rect.top, scale, pDC, RGB(10, 100, 10), rect.bottom, rect.top, false);

		pDC->SelectObject(old_pen);
		pDC->SelectObject(old_brush);

		rgn1.DeleteObject();
		rgn1.Attach(OldRgn);
		if(res==1)
			pDC->SelectClipRgn(&rgn1);
		else
			pDC->SelectClipRgn(NULL);
		rgn1.DeleteObject();
	}
	else
	{
		CRect rect;
		int i;

		CRgn rgn1, rgn2;
		HRGN OldRgn = CreateRectRgn(0,0,1,1);
		int res = GetClipRgn(pDC->m_hDC, OldRgn);
		ASSERT(res>-1);
		CRect RgnRect;
		GetRect(rect, scale, pDC->IsPrinting());
		RgnRect = rect;
		RgnRect.DeflateRect(-(m_line_width), m_line_width);
		ConvertLOMETRICtoTEXT(pDC, RgnRect);
		rgn1.CreateRectRgnIndirect(RgnRect);
		if(res==1)
		{
			rgn2.Attach(OldRgn);
			rgn1.CombineRgn(&rgn1, &rgn2, RGN_AND);
			rgn2.Detach();
		}

		CPen obj_pen, *old_pen;

		GetRect(rect, scale, pDC->IsPrinting());

		if(m_line_width!=0)
			obj_pen.CreatePen(PS_SOLID|PS_GEOMETRIC, (int)((double)m_line_width*scale), m_color);
		else if(draw_mode==0)
			obj_pen.CreatePen(PS_SOLID|PS_GEOMETRIC, (int)1, RGB(192,192,192));
		old_pen = pDC->SelectObject(&obj_pen);

		if(draw_mode==0 || m_line_width>0)
			DrawRect(pDC, rect);
		if(m_select && !pDC->IsPrinting() && flag&DRAW_SEL && draw_mode==0) 
			DrawSelPoints(pDC, scale); // отрисовка рамки выделения

		int Result = pDC->SelectClipRgn(&rgn1);

		//Отрисовываем гистограмму

		if(rows>0 && m_Elements.GetSize()>0)
		{
			//отрисовка
			DrawLegeng(pDC, rect);
			int defl = (int)(rect.Width()>-rect.Height()?-rect.Height()*0.03:rect.Width()*0.03);
			defl = defl>50?50:defl;

			rect.DeflateRect(defl/2, -defl/2);
			m_background_color = RGB(255, 255, 255);
			pDC->FillSolidRect(rect, m_background_color);
			rect.DeflateRect(defl/2, -defl/2);

			m_XLine.OffsetDrawRect(pDC, rect, scale, &m_DataSource, m_dXMin, m_dXRange);
			m_YLine.OffsetDrawRect(pDC, rect, scale, &m_DataSource, m_dYMin, m_dYRange);

			m_XLine.Draw(pDC, rect, scale, &m_DataSource, m_dXMin, m_dXRange);
			m_YLine.Draw(pDC, rect, scale, &m_DataSource, m_dYMin, m_dYRange);

			CPen pen, *old;
			double angel = ((double)m_Angel)/180.0*PI;
			int dx = (int)(m_Volume*sin(angel)/cos(angel));
			double width = (double)(rect.Width()-dx);

			int ind = m_DataSource.GetHeader()->Find(m_Elements[0]->GetColumnName());
			if(ind>=0)
			{
				pen.CreatePen(PS_SOLID, (int)(m_Elements[0]->m_line_width*scale), RGB(0, 0, 0));
				old = pDC->SelectObject(&pen);

				// прочитаем переданные значения
				bool bUseValues = false;
				CArray<Timeline_values_struct> values;
				CString sVal;
				for(i=0; i<rows; i++)
				{
					m_DataSource.GetAt(i, ind, sVal);
					CStringArray sArr, AdditionalCoordinateArray;
					bool bAdditionalLine;
					Report_ConvertStrAggr2Array(sVal, sArr, AdditionalCoordinateArray, bAdditionalLine);
					Timeline_values_struct new_val;
					Report_ConvertAggr2TimeLine(sArr, new_val);
					values.Add(new_val);
					if(new_val.is_value_begin == true || new_val.is_value_end == true) bUseValues = true;
				}
				Report_SortTimeLine(values);

				// найдем минимальное и максимальное значение времени
				double min_time = 0.0;
				double max_time = 0.0;
				Report_GetMinMaxTime(values, min_time, max_time);

				// вычисление количества возможных цветов (берется из легенды)
				int color_cnt = rows;
				CStringArray strArr;
				if(m_Legend.m_LegendPosition!=aplLegendPositionNone)
				{
					m_ReportMgr->m_data_source.GetUserLegendText(strArr);
				}
				else if(m_owner_sheet) // поищем, может на странице существует другая гистограмма с легендой?
				{
					for(int i=0;i<m_owner_sheet->m_els.GetSize();i++)
					{
						CReportElement* elem = m_owner_sheet->m_els.GetAt(i);
						if(elem==0) continue;
						CGistogramElement* g_elem = dynamic_cast<CGistogramElement*>(elem);
						if(g_elem==0) continue;
						if(g_elem==this) continue;
						if(g_elem->m_ViewType!=APL_GISTOGRAM_VIEW_TYPE_TIMELINE) continue;
						if(g_elem->m_XLine.m_bHideValues == true) continue;
						if(g_elem->m_Elements.Size == 0) continue;
						CRect r_cur, r_base;
						GetRect(r_cur, scale, 1);
						g_elem->GetRect(r_base, scale, 1);
						if(r_cur.left != r_base.left || r_cur.right != r_base.right) continue;
						if(g_elem->m_Legend.m_LegendPosition!=aplLegendPositionNone)
							g_elem->m_ReportMgr->m_data_source.GetUserLegendText(strArr);
						if(strArr.GetSize())
							break;
					}
				}
				if(strArr.GetSize())
					color_cnt = strArr.GetSize();
				else
				{
					int ccnt = -1;
					for(i=0; i<rows; i++)
					{
						if((int)values[i].type >= ccnt)
							ccnt = (int)values[i].type+1;
					}
					if(ccnt > 0)
						color_cnt = ccnt;
				}

				if(max_time-min_time>0.0)
				{
					CArray<COLORREF> column_colors;
					bool ret_c = GetColor(m_Elements[0]->m_color, color_cnt, column_colors);
					ASSERT(ret_c);

					CArray<int> x1, x2, y1, y2;
					CArray<COLORREF> color_a;
					int y = rect.bottom-(rect.Height()+m_Volume);
					for(i=0; i<rows; i++)
					{
						if(values[i].begin==values[i].end) continue;
						if((int)values[i].type < 0) continue;

						color_a.Add(column_colors[(int)values[i].type]);
						x1.Add((int)(width*(values[i].begin-min_time)/(max_time-min_time)));
						x2.Add((int)(width*(values[i].end-min_time)/(max_time-min_time)));

						if(bUseValues == false) // рисуем столбик высотой = 1 с горизонтальной "крышей"
						{
							y1.Add(y);
							y2.Add(y);
						}
						else // рисуем столбик заданной высоты с наклонной "крышей"
						{
							double dVal = 0.0;
							if(values[i].is_value_begin == true)
								dVal = values[i].value_begin;
							double dVal1 = (dVal-m_dYMin)*((double)(rect.Height()+m_Volume)/m_dYRange);
							if(values[i].is_value_end == true)
								dVal = values[i].value_end;
							else
								dVal = 0.0;
							double dVal2 = (dVal-m_dYMin)*((double)(rect.Height()+m_Volume)/m_dYRange);

							y1.Add((int)(dVal1));
							y2.Add((int)(dVal2));
						}
					}

					CBrush br, *old_br;
					COLORREF last_color = m_Elements[0]->m_color;
					if(!m_ReportMgr->m_data_source.GetUseGDIPlus())
					{
						br.CreateSolidBrush(last_color);
						old_br = pDC->SelectObject(&br);
					}
					for(i = 0; i < x1.GetSize(); i++)
					{
						if(!m_ReportMgr->m_data_source.GetUseGDIPlus() && last_color != color_a[i])
						{
							br.DeleteObject();
							last_color = color_a[i];
							br.CreateSolidBrush(last_color);
							pDC->SelectObject(&br);
						}

						if(bUseValues == false) // рисуем столбик высотой = 1 с горизонтальной "крышей"
						{
							Draw3DRect(x1[i]+rect.left, rect.bottom, x2[i]+rect.left, y1[i], scale, pDC, color_a[i], rect.bottom, y, false);
						}
						else // рисуем столбик заданной высоты с наклонной "крышей"
						{
							bool bRight = true;
							if(i < x1.GetSize()-1) // не последняя
							{
								if(y2[i] >= y1[i+1])
								{
									if(x1[i] == x1[i+1])
										continue;
									else if(x2[i] == x1[i+1] || x2[i] == x1[i+1]-1)
										bRight = false;
								}
							}

							Draw3DRectDuo(rect.bottom, x1[i]+rect.left, rect.bottom-y1[i], x2[i]+rect.left, rect.bottom-y2[i], scale, pDC, color_a[i], bRight);
//							// отрисовка значений столбика
//							DrawRectValueString(dVal, x, rect.bottom, (int)(x+width/2), y, scale, pDC, m_Elements[iDI]->m_color, rect.bottom, rect.top, true);
						}
					}
					if(!m_ReportMgr->m_data_source.GetUseGDIPlus())
					{
						pDC->SelectObject(old_br);
						br.DeleteObject();
					}
				}

				pDC->SelectObject(old);
				pen.DeleteObject();
			}
		}
		pDC->SelectObject(old_pen);

		rgn1.DeleteObject();
		rgn1.Attach(OldRgn);
		if(res==1)
			pDC->SelectClipRgn(&rgn1);
		else
			pDC->SelectClipRgn(NULL);
		rgn1.DeleteObject();
	}

	return TRUE;
}

COLORREF CGistogramElement::HSV2RGBeasy(double Hue, double Saturation, double ValueBr) //просто получает RGB из HSV
{
	ValueBr = ValueBr * 255;
	double f = Hue/60 - (int)(Hue/60);
	double p = ValueBr * (1-Saturation);
	double q = ValueBr * (1- f*Saturation);
	double t = ValueBr * (1-(1-f)*Saturation);
	switch ((int)(Hue/60) % 6)
	{
		case 0:
			return RGB(ValueBr, t, p);
		case 1:
			return RGB(q, ValueBr, p);
		case 2:
			return RGB(p, ValueBr, t);
		case 3:
			return RGB(p, q, ValueBr);
		case 4:
			return RGB(t, p, ValueBr);
		case 5:
			return RGB(ValueBr, p, q);
	}
	return 0;
}

bool CGistogramElement::GetColor(COLORREF base_color, int parts, CArray<COLORREF>& colors)
{
	colors.RemoveAll();

	// если раньше было посчитано, берем из кеша
	if(base_color == m_UseColorRef && parts == m_UserColorParts && m_UserColorParts == m_UseColors.GetSize())
	{
		for(int i = 0; i < m_UseColors.GetSize(); i++)
			colors.Add(m_UseColors[i]);
		return colors.GetSize() == parts;
	}

	// если были заданы пользовательские цвета и их полностью хватает для выполнения запроса
	if(m_ReportMgr->m_data_source.GetUseUserColors())
	{
		CArray<COLORREF> clrs;
		m_ReportMgr->m_data_source.GetUserColors(clrs);
		if(clrs.GetSize() >= parts)
		{
			for(int i = 0; i < clrs.GetSize(); i++)
				colors.Add(clrs[i]);
			return colors.GetSize() == parts;
		}
	}

	if (parts <= 10) // 2018-05-18 - предустановленные первые 10 цветов
	{
		CArray<COLORREF> static_colors;
		static_colors.Add(RGB(155, 155, 255));
		static_colors.Add(RGB(35, 233, 101));
		static_colors.Add(RGB(255, 0, 29));
		static_colors.Add(RGB(225, 225, 0));
		static_colors.Add(RGB(0, 255, 216));
		static_colors.Add(RGB(237, 110, 54));
		static_colors.Add(RGB(220, 145, 250));
		static_colors.Add(RGB(70, 76, 152));
		static_colors.Add(RGB(212, 131, 131));
		static_colors.Add(RGB(157, 161, 60));
		for (int i = 0; i < parts; i++)
			colors.Add(static_colors[i]);
	} // 2018-05-18
	else
	{
		std::set<COLORREF> UsedColor;
		COLORREF color = base_color;
		int color_base_num[10] = { 3, 5, 9, 11, 16, 27, 30, 39, 43, 58 };
		int color_num = parts;
		if (color_num < color_base_num[0]) color_num = color_base_num[0];
		else
		{
			for (int i = 1; i < 9; i++)
			{
				if (color_num > color_base_num[i - 1] && color_num <= color_base_num[i])
				{
					color_num = color_base_num[i];
					break;
				}
			}
		}

		if (parts >= 0)
			colors.Add(color);

		int col_parts = parts;
		if (color_num == 9 && parts == 8)
			col_parts++;
		for (int i = 0; i < col_parts - 1; i++)
		{
			UsedColor.insert(color);
			for (int iClr = 0; iClr < (int)UsedColor.size(); iClr++)
			{
				bool bFound = false;
				color = HSV2RGBeasy((double)color + 180.0 - 360.0 / UsedColor.size(), 1, 1);
				if (color_num > 3 && color_num < 10)
				{
					int max_color_used = 0;
					int max_color = 0;
					if ((int)GetRValue(*UsedColor.rbegin()) > (int)GetGValue(*UsedColor.rbegin())
						&&
						(int)GetRValue(*UsedColor.rbegin()) > (int)GetBValue(*UsedColor.rbegin()))
						max_color_used = 1;
					if ((int)GetGValue(*UsedColor.rbegin()) > (int)GetRValue(*UsedColor.rbegin())
						&&
						(int)GetGValue(*UsedColor.rbegin()) > (int)GetBValue(*UsedColor.rbegin()))
						max_color_used = 2;
					if ((int)GetBValue(*UsedColor.rbegin()) > (int)GetRValue(*UsedColor.rbegin())
						&&
						(int)GetBValue(*UsedColor.rbegin()) > (int)GetGValue(*UsedColor.rbegin()))
						max_color_used = 3;
					if ((int)GetRValue(color) > (int)GetGValue(color)
						&&
						(int)GetRValue(color) > (int)GetBValue(color))
						max_color = 1;
					if ((int)GetGValue(color) > (int)GetRValue(color)
						&&
						(int)GetGValue(color) > (int)GetBValue(color))
						max_color = 2;
					if ((int)GetBValue(color) > (int)GetRValue(color)
						&&
						(int)GetBValue(color) > (int)GetGValue(color))
						max_color = 3;
					if (max_color_used == max_color)
					{
						if (i % 2)
							color = RGB(GetBValue(color), GetRValue(color), GetGValue(color));
						else
							color = RGB(GetGValue(color), GetBValue(color), GetRValue(color));
					}
				}
				for (std::set<COLORREF>::iterator it = UsedColor.begin(); it != UsedColor.end(); it++)
				{
					int clr = abs((int)GetRValue(*it) - (int)GetRValue(color));
					clr += abs((int)GetGValue(*it) - (int)GetGValue(color));
					clr += abs((int)GetBValue(*it) - (int)GetBValue(color));
					if (clr < 500 / sqrt((double)UsedColor.size()))
					{
						bFound = true;
						color = RGB(GetGValue(color), GetBValue(color), GetRValue(color));
						if (iClr % 2)
							color = HSV2RGBeasy((double)color + 180.0 - 360.0 / UsedColor.size(), 1.1 - (double)i / color_num, 0.6 + (double)i / UsedColor.size());
						else
							color = HSV2RGBeasy((double)color + 180.0 - 360.0 / color_num, 0.9 + (double)i / UsedColor.size(), 1.4 - (double)i / color_num);
						break;
					}
				}
				if (bFound == false)
					break;
			}
			colors.Add(color);
		}

		if (color_num == 9)
		{
			if (colors.GetSize() > 8)
			{
				if (parts == 8)
				{
					colors[7] = colors[8];
					colors.RemoveAt(8);
				}
				else if (parts == 9)
				{
					color = colors[7];
					colors[7] = colors[8];
					colors[8] = color;
				}
			}
		}
	}

	// если были заданы пользовательские цвета, но их меньше, чем нужно для выполнения запроса
	if(m_ReportMgr->m_data_source.GetUseUserColors())
	{
		CArray<COLORREF> clrs;
		m_ReportMgr->m_data_source.GetUserColors(clrs);
		ASSERT(clrs.GetSize() < colors.GetSize());
		for(int i = 0; i < clrs.GetSize(); i++)
		{
			ASSERT(colors.GetSize() > i);
			colors[i]  = clrs[i];
		}
	}

	// сохраним в кеш
	m_UseColors.RemoveAll();
	for(int i = 0; i < colors.GetSize(); i++)
		m_UseColors.Add(colors[i]);
	m_UseColorRef = base_color;
	m_UserColorParts = parts;

	return colors.GetSize() == parts;
}

void CGistogramElement::GetGradientColors(COLORREF base_color, Gdiplus::Color& color_main, Gdiplus::Color& color_dark, Gdiplus::Color& color_light)
{
	BYTE coef = 43;
	BYTE div = 6;
	BYTE max = 255-coef;
	BYTE min = coef;

	BYTE RValue = GetRValue(base_color);
	BYTE GValue = GetGValue(base_color);
	BYTE BValue = GetBValue(base_color);
	color_main = Gdiplus::Color(RValue, GValue, BValue);

	BYTE R2 = RValue, R3 = RValue;
	BYTE G2 = GValue, G3 = GValue;
	BYTE B2 = BValue, B3 = BValue;
	if(RValue < max && RValue > min)
	{
		R2 = R2 - R2/div;
		R3 = R3 + R3/div;
	}
	else if(RValue < max)
	{
		R2 = 0;
		R3 = R3 + R3/div;
	}
	else if(RValue > min)
	{
		R2 = R2 - R2/div;
		R3 = 255;
	}
	if(GValue < max && GValue > min)
	{
		G2 = G2 - G2/div;
		G3 = G3 + G3/div;
	}
	else if(GValue < max)
	{
		G2 = 0;
		G3 = G3 + G3/div;
	}
	else if(GValue > min)
	{
		G2 = G2 - G2/div;
		G3 = 255;
	}
	if(BValue < max && BValue > min)
	{
		B2 = B2 - B2/div;
		B3 = B3 + B3/div;
	}
	else if(BValue < max)
	{
		B2 = 0;
		B3 = B3 + B3/div;
	}
	else if(BValue > min)
	{
		B2 = B2 - B2/div;
		B3 = 255;
	}

	BYTE MinCorrect = coef/div;
	BYTE MaxCorrect = 255 - coef/div;
	if(RValue > MaxCorrect && GValue < MinCorrect)
	{
		if(BValue > max)
			B2 = BValue - coef;
		else if(BValue < min)
			B3 = BValue + coef;
	}
	else if(RValue > MaxCorrect && BValue < MinCorrect)
	{
		if(GValue > max)
			G2 = GValue - coef;
		else if(GValue < min)
			G3 = GValue + coef;
	}
	else if(GValue > MaxCorrect && RValue < MinCorrect)
	{
		if(BValue > max)
			B2 = BValue - coef;
		else if(BValue < min)
			B3 = BValue + coef;
	}
	else if(GValue > MaxCorrect && BValue < MinCorrect)
	{
		if(RValue > max)
			R2 = RValue - coef;
		else if(RValue < min)
			R3 = RValue + coef;
	}
	else if(BValue > MaxCorrect && RValue < MinCorrect)
	{
		if(GValue > max)
			G2 = GValue - coef;
		else if(GValue < min)
			G3 = GValue + coef;
	}
	else if(BValue > MaxCorrect && GValue < MinCorrect)
	{
		if(RValue > max)
			R2 = RValue - coef;
		else if(RValue < min)
			R3 = RValue + coef;
	}

	color_dark = Gdiplus::Color(R2, G2, B2);
	color_light = Gdiplus::Color(R3, G3, B3);
}

BOOL CGistogramElement::DrawCycle(CDC* pDC, double scale, int draw_mode, UINT flag)
{
	int rows, cols;
	m_DataSource.GetSize(rows, cols);

	if(draw_mode==0 && rows<1 || cols==0)//Значит это режим редактирования
	{
		double ang[6];
		CPen obj_pen, *old_pen;
		CPen simple_pen;
		CRect rect;
		CBrush *old_brush;
		CBrush br[6];

		CRgn rgn1, rgn2;
		HRGN OldRgn = CreateRectRgn(0,0,1,1);
		int res = GetClipRgn(pDC->m_hDC, OldRgn);
		CRect RgnRect;
		GetRect(rect, scale, pDC->IsPrinting());
		RgnRect = rect;
		RgnRect.NormalizeRect();
		RgnRect.DeflateRect(m_line_width, m_line_width);
		ConvertLOMETRICtoTEXT(pDC, RgnRect);
		rgn1.CreateRectRgnIndirect(RgnRect);

		obj_pen.CreatePen(PS_SOLID | PS_GEOMETRIC, (int)((double)m_line_width*scale), m_color);
		simple_pen.CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
		br[0].CreateSolidBrush(RGB(10, 10, 200));
		br[1].CreateSolidBrush(RGB(200, 10, 10));
		br[2].CreateSolidBrush(RGB(10, 200, 10));
		br[3].CreateSolidBrush(RGB(10, 10, 100));
		br[4].CreateSolidBrush(RGB(100, 10, 10));
		br[5].CreateSolidBrush(RGB(10, 100, 10));
		old_pen = pDC->SelectObject(&obj_pen);
		
		DrawRect(pDC, rect);
		if(m_select && !pDC->IsPrinting() && flag&DRAW_SEL) 
			DrawSelPoints(pDC, scale); // отрисовка рамки выделения

		if(res==1)
		{
			rgn2.Attach(OldRgn);
			rgn1.CombineRgn(&rgn1, &rgn2, RGN_AND);
			rgn2.Detach();
		}
		int Result = pDC->SelectClipRgn(&rgn1);

		//рисуем подобие гистограммы
		rect.DeflateRect(m_line_width, (int)(-m_line_width-m_Volume*scale), m_line_width, -m_line_width);

		// определение прямоугольной области для верхнего и нижнего эллипсов "таблетки"
		bool bNegativeArrangementHeight = rect.Height() < 0 ? true : false;
		bool bNegativeArrangementWidth = rect.Width() < 0 ? true : false;
		CRect TopEllipseRect(rect);
		CRect BottomEllipseRect(rect);
		TopEllipseRect.bottom -= (int)(cos(((double)m_Angel)/180.0*PI)*rect.Height());
		BottomEllipseRect.top += (int)(cos(((double)m_Angel)/180.0*PI)*rect.Height());

		ang[0] = 10.0;
		ang[1] = 40.0;
		ang[2] = 60.0;
		ang[3] = 190.0;
		ang[4] = 210.0;
		ang[5] = 360.0;
		// отрисовка секторов "таблетки"
		double x1, y1, x2, y2;
		x1 = TopEllipseRect.right;
		y1 = TopEllipseRect.CenterPoint().y;
		double dPieAngle = 0;

		pDC->SelectObject(&simple_pen);
		for(int i=0; i<6; i++)
		{
			old_brush = pDC->SelectObject(&br[i]);

			// определение координат начала и конца сектора на верхнем эллипсе "таблетки"
			double dOldAngle = dPieAngle;
			dPieAngle = ang[i];
			if(bNegativeArrangementWidth)
				x2 = -cos(dPieAngle/180*PI)*TopEllipseRect.Width()/2+TopEllipseRect.CenterPoint().x;
			else
				x2 = cos(dPieAngle/180*PI)*TopEllipseRect.Width()/2+TopEllipseRect.CenterPoint().x;
			if(bNegativeArrangementHeight)
				y2 = -sin(dPieAngle/180*PI)*TopEllipseRect.Height()/2+TopEllipseRect.CenterPoint().y;
			else
				y2 = sin(dPieAngle/180*PI)*TopEllipseRect.Height()/2+TopEllipseRect.CenterPoint().y;

			// заполнение боковых граней "таблетки" цветом
			if(dPieAngle>=180 && dOldAngle<=360)
			{
				CRgn rgn1, rgn2, rgn3, rgn4, rgn;
				int rx1, rx2;
				if(dOldAngle<180)
					rx1 = TopEllipseRect.left;
				else
					rx1 = (int)x1;
				if(dPieAngle>360)
					rx2 = TopEllipseRect.right;
				else
					rx2 = (int)x2;
				rgn1.CreateRectRgn(rx1, rect.top, rx2, rect.bottom);
				rgn2.CreateEllipticRgnIndirect(TopEllipseRect);
				rgn3.CreateEllipticRgnIndirect(BottomEllipseRect);
				rgn4.CreateRectRgn(0, 0, 1, 1);
				rgn.CreateRectRgn(0, 0, 1, 1);
				int retrgn = rgn4.CombineRgn(&rgn1, &rgn3, RGN_AND);
				if(retrgn==COMPLEXREGION || retrgn==SIMPLEREGION)
				{
					rgn1.SetRectRgn(rx1, TopEllipseRect.CenterPoint().y, rx2, BottomEllipseRect.CenterPoint().y);
					int retrgn = rgn3.CombineRgn(&rgn1, &rgn4, RGN_OR);
					if(retrgn==COMPLEXREGION || retrgn==SIMPLEREGION)
					{
						retrgn = rgn.CombineRgn(&rgn3, &rgn2, RGN_DIFF);
						if(retrgn==COMPLEXREGION || retrgn==SIMPLEREGION)
							pDC->PaintRgn(&rgn);
					}
				}
				if(dOldAngle>180 && dOldAngle<360)
				{
					pDC->MoveTo((int)x1, (int)y1);
					if(bNegativeArrangementHeight)
						pDC->LineTo((int)x1, (int)(y1+cos(((double)m_Angel)/180.0*PI)*rect.Height()));
					else
						pDC->LineTo((int)x1, (int)(y1-cos(((double)m_Angel)/180.0*PI)*rect.Height()));
				}
			}

			// отрисовка сектора верхнего эллипса "таблетки"
			pDC->Pie(TopEllipseRect, CPoint((int)x1, (int)y1), CPoint((int)x2, (int)y2));

			x1 = x2;
			y1 = y2;
		}

		// отрисовка границ нижнего эллипса "таблетки"
		pDC->MoveTo(TopEllipseRect.left, TopEllipseRect.CenterPoint().y);
		pDC->LineTo(BottomEllipseRect.left, BottomEllipseRect.CenterPoint().y);
		pDC->ArcTo(BottomEllipseRect, CPoint(BottomEllipseRect.left, BottomEllipseRect.CenterPoint().y), CPoint(BottomEllipseRect.right, BottomEllipseRect.CenterPoint().y));
		pDC->LineTo(TopEllipseRect.right, TopEllipseRect.CenterPoint().y);

		pDC->SelectObject(old_pen);
		pDC->SelectObject(old_brush);

		rgn1.DeleteObject();
		rgn1.Attach(OldRgn);
		if(res==1)
			pDC->SelectClipRgn(&rgn1);
		else
			pDC->SelectClipRgn(NULL);
		rgn1.DeleteObject();
	}
	else
	{
		CRect rect;
		int i;
		int iDI;
		int ind;

		double dVal;
		CString sVal;
		
		CRgn rgn1, rgn2;
		HRGN OldRgn = CreateRectRgn(0,0,1,1);
		int res = GetClipRgn(pDC->m_hDC, OldRgn);
		ASSERT(res>-1);
		CRect RgnRect;
		GetRect(rect, scale, pDC->IsPrinting());
		RgnRect = rect;
		RgnRect.DeflateRect(-(m_line_width), m_line_width);
		ConvertLOMETRICtoTEXT(pDC, RgnRect);
		rgn1.CreateRectRgnIndirect(RgnRect);
		if(res==1)
		{
			rgn2.Attach(OldRgn);
			rgn1.CombineRgn(&rgn1, &rgn2, RGN_AND);
			rgn2.Detach();
		}

		CPen obj_pen, *old_pen;
		if(m_line_width!=0)
			obj_pen.CreatePen(PS_SOLID|PS_GEOMETRIC, (int)((double)m_line_width*scale), m_color);
		else if(draw_mode==0)
			obj_pen.CreatePen(PS_SOLID|PS_GEOMETRIC, (int)1, RGB(192,192,192));
		old_pen = pDC->SelectObject(&obj_pen);

		if(draw_mode==0 || m_line_width>0)
			DrawRect(pDC, rect);
		if(m_select && !pDC->IsPrinting() && flag&DRAW_SEL && draw_mode==0) 
			DrawSelPoints(pDC, scale); // отрисовка рамки выделения

		int Result = pDC->SelectClipRgn(&rgn1);

		Gdiplus::Graphics* graphics = NULL;
		Gdiplus::GraphicsContainer container;
		if(m_ReportMgr->m_data_source.GetUseGDIPlus())
			graphics = CreateGDIPlusGraphics(pDC->m_hDC, container);

		//Отрисовываем гистограмму
		//пропорционально

		if(rows>0 && m_Elements.GetSize()>0)
		{
			//отрисовка
			DrawLegeng(pDC, rect);
			int defl;
			defl = (int)(rect.Width()>-rect.Height()?-rect.Height()*0.03:rect.Width()*0.03);
			defl = defl>50?50:defl;

			rect.DeflateRect(defl/2, -defl/2);
			m_background_color = RGB(255, 255, 255);
			pDC->FillSolidRect(rect, m_background_color);
			rect.DeflateRect(defl/2, -defl/2);

			// вычисление необходимого места для отрисовки осевых (координатных) надписей
			m_XLine.OffsetDrawRect(pDC, rect, scale, &m_DataSource, m_dXMin, m_dXRange);
			m_YLine.OffsetDrawRect(pDC, rect, scale, &m_DataSource, m_dYMin, m_dYRange);

			// определение прямоугольной области для верхнего и нижнего эллипсов "таблетки"
			CRect TopEllipseRect(rect);
			CRect BottomEllipseRect(rect);
			TopEllipseRect.bottom -= (int)(cos(((double)m_Angel)/180.0*PI)*rect.Height());
			BottomEllipseRect.top += (int)(cos(((double)m_Angel)/180.0*PI)*rect.Height());

			CPen pen, *old = NULL;
			CBrush br, *old_br = NULL;
			Gdiplus::Pen* pen_gdi = NULL;
			if(m_ReportMgr->m_data_source.GetUseGDIPlus())
			{
				pen_gdi = new Gdiplus::Pen(Gdiplus::Color(192,192,192), 1);

				// преобразование координат эллипсов "таблетки"
				POINT pt[4];
				pt[0] = TopEllipseRect.TopLeft();
				pt[1] = TopEllipseRect.BottomRight();
				pt[2] = BottomEllipseRect.TopLeft();
				pt[3] = BottomEllipseRect.BottomRight();
				::DPtoLP(pDC->m_hDC, pt, 4);
				CRect LPTopEllipseRect = CRect(pt[0], pt[1]);
				LPTopEllipseRect.bottom = -LPTopEllipseRect.bottom;
				LPTopEllipseRect.top = -LPTopEllipseRect.top;
				CRect LPBottomEllipseRect = CRect(pt[2], pt[3]);
				LPBottomEllipseRect.bottom = -LPBottomEllipseRect.bottom;
				LPBottomEllipseRect.top = -LPBottomEllipseRect.top;

				TopEllipseRect = LPTopEllipseRect;
				BottomEllipseRect = LPBottomEllipseRect;
			}

			// отрисовка секторов "таблетки"
			bool bUseDraw = false;
			for(iDI=0; iDI<m_Elements.GetSize(); iDI++)
			{
				ind = m_DataSource.GetHeader()->Find(m_Elements[iDI]->GetColumnName());
				if(ind<0) continue;

				CArray<COLORREF> column_colors;
				bool ret_c = GetColor(m_Elements[iDI]->m_color, rows, column_colors);
				ASSERT(ret_c);

				double dSumVal = 0.0;
				for(i=0; i<rows; i++)
				{
					m_DataSource.GetAt(i, ind, dVal);
					dSumVal += dVal;
				}
				double x1, y1, x2, y2;
				x1 = TopEllipseRect.right;
				y1 = TopEllipseRect.CenterPoint().y;
				double dPieAngle = 0;
				double dStartAngle = 0, dEndAngle = 0;
				for(i=0; i<rows; i++)
				{
					m_DataSource.GetAt(i, ind, dVal);
					if(dVal==0) continue;

					Gdiplus::PathGradientBrush *brush_top = NULL, *brush_top_border = NULL, *brush_bottom = NULL, *brush_bottom_rect = NULL;
					COLORREF color_base = column_colors[i];
					Gdiplus::Color color_main, color_dark, color_light, color_white(255, 255, 255);
					if(m_ReportMgr->m_data_source.GetUseGDIPlus())
					{
						// настройка кисти
						GetGradientColors(color_base, color_main, color_dark, color_light);
						Gdiplus::Color colors_top[3] = {color_dark, color_main, color_light};
						float positions_top[3] = {0.0f, 0.5f, 1.0f};
						Gdiplus::Color colors_top_border[4] = {color_white, color_dark, color_main, color_light};
						float positions_top_border[4] = {0.0f, 0.015f, 0.5f, 1.0f};
						Gdiplus::Color colors_bottom[5] = {color_white, color_light, color_main, color_main, color_dark};
						float positions_bottom[5] = {0.0f, 0.015f, 0.3f, 0.7f, 1.0f};
						Gdiplus::Color colors_bottom_rect[3] = {color_light, color_main, color_main};
						float positions_bottom_rect[3] = {0.0f, 0.3f, 1.0f};

						Gdiplus::GraphicsPath pathTop, pathBottom, pathBottomRect;
						pathTop.AddEllipse(TopEllipseRect.left-1, TopEllipseRect.bottom-1, abs(TopEllipseRect.Width())+2, abs(TopEllipseRect.Height())+2);
						pathBottom.AddArc(BottomEllipseRect.left-1, BottomEllipseRect.bottom-1, abs(BottomEllipseRect.Width())+2, abs(BottomEllipseRect.Height())+2, 178.5f, 183);
						pathBottomRect.AddRectangle(Gdiplus::Rect(BottomEllipseRect.left, BottomEllipseRect.CenterPoint().y-2, abs(BottomEllipseRect.Width()), abs(BottomEllipseRect.bottom-TopEllipseRect.bottom)+2));

						brush_top = new Gdiplus::PathGradientBrush(&pathTop);
						brush_top->SetCenterColor(color_light);
						brush_top->SetInterpolationColors(colors_top, positions_top, 3);

						brush_top_border = new Gdiplus::PathGradientBrush(&pathTop);
						brush_top_border->SetCenterColor(color_light);
						brush_top_border->SetInterpolationColors(colors_top_border, positions_top_border, 4);

						brush_bottom = new Gdiplus::PathGradientBrush(&pathBottom);
						brush_bottom->SetCenterColor(color_dark);
						brush_bottom->SetInterpolationColors(colors_bottom, positions_bottom, 5);

						brush_bottom_rect = new Gdiplus::PathGradientBrush(&pathBottomRect);
						brush_bottom_rect->SetCenterColor(color_dark);
						brush_bottom_rect->SetInterpolationColors(colors_bottom_rect, positions_bottom_rect, 3);

						// настройка пера
						pen_gdi->SetColor(color_main);
						pen_gdi->SetWidth((float)(scale*m_Elements[iDI]->m_line_width));
					}
					else
					{
						br.CreateSolidBrush(color_base);
						old_br = pDC->SelectObject(&br);
						pen.CreatePen(PS_SOLID, (int)(m_Elements[iDI]->m_line_width*scale), RGB(0, 0, 0));
						old = pDC->SelectObject(&pen);
					}

					// определение координат начала и конца сектора на верхнем эллипсе "таблетки"
					double dOldAngle = dPieAngle;
					dPieAngle += (dVal*360)/dSumVal;
					if(dPieAngle==360)
					{
						x2 = TopEllipseRect.right;
						y2 = TopEllipseRect.CenterPoint().y;
					}
					else
					{
						x2 = cos(dPieAngle/180*PI)*TopEllipseRect.Width()/2+TopEllipseRect.CenterPoint().x;
						y2 = -sin(dPieAngle/180*PI)*TopEllipseRect.Height()/2+TopEllipseRect.CenterPoint().y;
					}

					int sec_x1 = 0, sec_y1 = 0, sec_x2 = 0, sec_y2 = 0;
					// заполнение боковых граней "таблетки" цветом
					if(dPieAngle>=180 && dOldAngle<=360)
					{
						CRgn rgn1, rgn2, rgn3, rgn4, rgn;
						int rx1, rx2;
						if(dOldAngle<180)
							rx1 = TopEllipseRect.left;
						else
							rx1 = (int)x1;
						if(dPieAngle>360)
							rx2 = TopEllipseRect.right;
						else
							rx2 = (int)x2;
						rgn1.CreateRectRgn(rx1, TopEllipseRect.top, rx2, BottomEllipseRect.bottom);
						rgn2.CreateEllipticRgnIndirect(TopEllipseRect);
						rgn3.CreateEllipticRgnIndirect(BottomEllipseRect);
						rgn4.CreateRectRgn(0, 0, 1, 1);
						rgn.CreateRectRgn(0, 0, 1, 1);
						int retrgn = rgn4.CombineRgn(&rgn1, &rgn3, RGN_AND);
						if(retrgn==COMPLEXREGION || retrgn==SIMPLEREGION)
						{
							rgn1.SetRectRgn(rx1, TopEllipseRect.CenterPoint().y, rx2, BottomEllipseRect.CenterPoint().y);
							retrgn = rgn3.CombineRgn(&rgn1, &rgn4, RGN_OR);
							if(retrgn==COMPLEXREGION || retrgn==SIMPLEREGION)
							{
								retrgn = rgn.CombineRgn(&rgn3, &rgn2, RGN_DIFF);
								if(retrgn==COMPLEXREGION || retrgn==SIMPLEREGION)
								{
									if(m_ReportMgr->m_data_source.GetUseGDIPlus())
									{
										Gdiplus::Region region((HRGN)rgn);
										// по непонятным причинам FillRegion берет размер окна вывода в логических единицах
										// и делает пересчет координат функцией ::DPtoLP.
										// в результате, правая часть области заливки не заполняется цветом
										// пришлось выкручиваться, используя код ниже
//										graphics->FillRegion(brush_bottom, &region);
										Gdiplus::Matrix matrix;
										graphics->GetTransform(&matrix);
										int cnt_reg_scan = region.GetRegionScansCount(&matrix);
										Gdiplus::Rect* rects_scan = new Gdiplus::Rect[cnt_reg_scan];
										region.GetRegionScans(&matrix, rects_scan, &cnt_reg_scan);
										for(int j = 0; j < cnt_reg_scan; ++j)
										{
											graphics->FillRectangle(brush_bottom, rects_scan[j]);

											int left = rects_scan[j].GetLeft();
											int right = rects_scan[j].GetRight();
											int top = rects_scan[j].GetBottom();
											if( sec_x1 == 0 && sec_y1 == 0 && sec_x2 == 0 && sec_y2 == 0 )
											{
												sec_x1 = left;
												sec_y1 = top;
												sec_x2 = right;
												sec_y2 = top;
											}
											else
											{
												if(sec_x1 > left)
												{
													sec_x1 = left;
													sec_y1 = top;
												}
												else if(sec_x1 == left)
												{
													if(sec_y1 < top) sec_y1 = top;
												}
												if(sec_x2 < right)
												{
													sec_x2 = right;
													sec_y2 = top;
												}
												else if(sec_x2 == right)
												{
													if(sec_y2 < top) sec_y2 = top;
												}
											}
										}
										for(int j = 0; j < cnt_reg_scan; ++j)
										{
											int center_y = BottomEllipseRect.CenterPoint().y;
											if(rects_scan[j].GetTop() >= center_y)
												graphics->FillRectangle(brush_bottom_rect, rects_scan[j]);
											else if(rects_scan[j].GetBottom() >= center_y)
											{
												Gdiplus::Rect r;
												r.X = rects_scan[j].X;
												r.Width = rects_scan[j].Width;
												r.Y = center_y;
												r.Height = rects_scan[j].Height-abs(center_y-rects_scan[j].Y);
												graphics->FillRectangle(brush_bottom_rect, r);
											}
										}
										delete []rects_scan;
									}
									else
										pDC->PaintRgn(&rgn);
								}
							}
						}
						if(dOldAngle>180 && dOldAngle<360)
						{
							if(m_ReportMgr->m_data_source.GetUseGDIPlus())
							{
								graphics->DrawLine(pen_gdi, Gdiplus::Point((int)x1, (int)y1),
												Gdiplus::Point((int)x1, (int)(y1+BottomEllipseRect.bottom-TopEllipseRect.bottom+abs((BottomEllipseRect.bottom-TopEllipseRect.bottom)/10))));
							}
							else
							{
								pDC->MoveTo((int)x1, (int)y1);
								pDC->LineTo((int)x1, (int)(y1+cos(((double)m_Angel)/180.0*PI)*rect.Height()));
							}
						}
					}

					// отрисовка сектора верхнего эллипса "таблетки"
					if(m_ReportMgr->m_data_source.GetUseGDIPlus())
					{
						if(dOldAngle > 180)
						{
							if(dEndAngle > 0)
								dStartAngle = dEndAngle;
							else
							{
								if(dOldAngle < 183 || dOldAngle > 357)
									dStartAngle = atan2(y1-TopEllipseRect.CenterPoint().y, x1-TopEllipseRect.CenterPoint().x)*180.0/PI;
								else
									dStartAngle = atan2((double)sec_y1-TopEllipseRect.CenterPoint().y, sec_x1-TopEllipseRect.CenterPoint().x)*180.0/PI;
								dStartAngle += 360.0;
							}
						}
						else
							dStartAngle = atan2(y1-TopEllipseRect.CenterPoint().y, x1-TopEllipseRect.CenterPoint().x)*180.0/PI;
						if(dPieAngle > 180)
						{
							if(dPieAngle < 183 || dPieAngle > 357)
								dEndAngle = atan2(y2-TopEllipseRect.CenterPoint().y, x2-TopEllipseRect.CenterPoint().x)*180.0/PI;
							else
								dEndAngle = atan2((double)sec_y2-TopEllipseRect.CenterPoint().y, sec_x2-TopEllipseRect.CenterPoint().x)*180.0/PI;
							dEndAngle += 360.0;
						}
						else
							dEndAngle = atan2(y2-TopEllipseRect.CenterPoint().y, x2-TopEllipseRect.CenterPoint().x)*180.0/PI;

						pen_gdi->SetWidth(1.7f);
						if(dStartAngle == 0 && dEndAngle != 360)
							graphics->DrawLine(pen_gdi, TopEllipseRect.CenterPoint().x, TopEllipseRect.CenterPoint().y, TopEllipseRect.right-2, TopEllipseRect.CenterPoint().y);
						if(dStartAngle == 90)
							graphics->DrawLine(pen_gdi, TopEllipseRect.CenterPoint().x, TopEllipseRect.CenterPoint().y, TopEllipseRect.CenterPoint().x, TopEllipseRect.top-2);
						if(dStartAngle == 180)
							graphics->DrawLine(pen_gdi, TopEllipseRect.left+2, TopEllipseRect.CenterPoint().y, TopEllipseRect.CenterPoint().x, TopEllipseRect.CenterPoint().y);
						if(dStartAngle == 270)
							graphics->DrawLine(pen_gdi, TopEllipseRect.CenterPoint().x, TopEllipseRect.bottom+2, TopEllipseRect.CenterPoint().x, TopEllipseRect.CenterPoint().y);

						if(dEndAngle <= 180) // сектор полностью наверху
						{
							graphics->FillPie(brush_top_border, TopEllipseRect.left, TopEllipseRect.bottom, abs(TopEllipseRect.Width()), abs(TopEllipseRect.Height()), (float)dStartAngle, (float)(dEndAngle-dStartAngle));
							graphics->FillPie(brush_top, TopEllipseRect.left+2, TopEllipseRect.bottom+2, abs(TopEllipseRect.Width())-4, abs(TopEllipseRect.Height())-4, (float)dStartAngle, (float)(dEndAngle-dStartAngle));
						}
						else if(dStartAngle <= 180 && dEndAngle >= 180) // сектор пересекает левую границу верха и низа
						{
							// верх
							graphics->FillPie(brush_top_border, TopEllipseRect.left, TopEllipseRect.bottom, abs(TopEllipseRect.Width()), abs(TopEllipseRect.Height()), (float)dStartAngle, (float)(180-dStartAngle));
							graphics->FillPie(brush_top, TopEllipseRect.left+2, TopEllipseRect.bottom+2, abs(TopEllipseRect.Width())-4, abs(TopEllipseRect.Height())-4, (float)dStartAngle, (float)(180-dStartAngle));
							// низ
							graphics->FillPie(brush_top, TopEllipseRect.left, TopEllipseRect.bottom, abs(TopEllipseRect.Width()), abs(TopEllipseRect.Height()), (float)180, (float)(dEndAngle-180));
							graphics->DrawPie(pen_gdi, TopEllipseRect.left, TopEllipseRect.bottom, abs(TopEllipseRect.Width()), abs(TopEllipseRect.Height()), (float)180, (float)(dEndAngle-180));
							if(dEndAngle-dStartAngle > 1)
								graphics->FillPie(brush_top, TopEllipseRect.left+2, TopEllipseRect.bottom+2, abs(TopEllipseRect.Width())-4, abs(TopEllipseRect.Height())-4, (float)(dStartAngle+0.5), (float)(dEndAngle-dStartAngle-1));
							if(dStartAngle == 0 && dEndAngle == 360)
								graphics->FillPie(brush_top, TopEllipseRect.left+2, TopEllipseRect.bottom+2, abs(TopEllipseRect.Width())-4, abs(TopEllipseRect.Height())-4, 355, 10);
						}
						else // сектор полностью внизу
						{
							graphics->FillPie(brush_top, TopEllipseRect.left, TopEllipseRect.bottom, abs(TopEllipseRect.Width()), abs(TopEllipseRect.Height()), (float)dStartAngle, (float)(dEndAngle-dStartAngle));
							graphics->DrawPie(pen_gdi, TopEllipseRect.left, TopEllipseRect.bottom, abs(TopEllipseRect.Width()), abs(TopEllipseRect.Height()), (float)dStartAngle, (float)(dEndAngle-dStartAngle));
						}
					}
					else
						pDC->Pie(TopEllipseRect, CPoint((int)x1, (int)y1), CPoint((int)x2, (int)y2));

					x1 = x2;
					y1 = y2;

					if(m_ReportMgr->m_data_source.GetUseGDIPlus())
					{
						delete brush_top;
						brush_top = NULL;
						delete brush_bottom;
						brush_bottom = NULL;

						HDC gdiDC = graphics->GetHDC();
						graphics->ReleaseHDC(gdiDC);
					}
					else
					{
						pDC->SelectObject(old_br);
						pDC->SelectObject(old);
						pen.DeleteObject();
						br.DeleteObject();
					}

					bUseDraw = true;
				}
			}

			// отрисовка границ нижнего эллипса "таблетки"
			if(m_ReportMgr->m_data_source.GetUseGDIPlus())
			{
			}
			else
			{
				pen.CreatePen(PS_SOLID, (int)(m_Elements[0]->m_line_width*scale), RGB(0, 0, 0));
				old = pDC->SelectObject(&pen);
			}
			if(bUseDraw) // только если были нарисованы хоть какие-то части верхнего эллипса "таблетки"
			{
				if(m_ReportMgr->m_data_source.GetUseGDIPlus())
				{
					Gdiplus::SolidBrush br(Gdiplus::Color(255, 255, 255));
					graphics->FillRectangle(&br, Gdiplus::Rect(BottomEllipseRect.left-2, BottomEllipseRect.CenterPoint().y-20, 4, abs(BottomEllipseRect.bottom-TopEllipseRect.bottom)+50));
					graphics->FillRectangle(&br, Gdiplus::Rect(BottomEllipseRect.right-2, BottomEllipseRect.CenterPoint().y-20, 4, abs(BottomEllipseRect.bottom-TopEllipseRect.bottom)+50));

					HDC gdiDC = graphics->GetHDC();
					graphics->ReleaseHDC(gdiDC);
				}
				else
				{
					pDC->MoveTo(TopEllipseRect.left, TopEllipseRect.CenterPoint().y);
					pDC->LineTo(BottomEllipseRect.left, BottomEllipseRect.CenterPoint().y);
					pDC->ArcTo(BottomEllipseRect, CPoint(BottomEllipseRect.left, BottomEllipseRect.CenterPoint().y), CPoint(BottomEllipseRect.right, BottomEllipseRect.CenterPoint().y));
					pDC->LineTo(TopEllipseRect.right, TopEllipseRect.CenterPoint().y);
				}
			}

			// отрисовка осевых (координатных) надписей
			m_XLine.Draw(pDC, rect, scale, &m_DataSource, m_dXMin, m_dXRange, graphics);
			m_YLine.Draw(pDC, rect, scale, &m_DataSource, m_dYMin, m_dYRange, graphics);

			if(m_ReportMgr->m_data_source.GetUseGDIPlus())
			{
				delete pen_gdi;
				pen_gdi = NULL;
			}
			else
			{
				pDC->SelectObject(old);
				pen.DeleteObject();
			}
		}

		if(m_ReportMgr->m_data_source.GetUseGDIPlus())
			DeleteGDIPlusGraphics(&graphics, container);
		else
			pDC->SelectObject(old_pen);

		rgn1.DeleteObject();
		rgn1.Attach(OldRgn);
		if(res==1)
			pDC->SelectClipRgn(&rgn1);
		else
			pDC->SelectClipRgn(NULL);
		rgn1.DeleteObject();
	}

	return TRUE;
}

void CGistogramElement::Update(bool set /* = true */)
{
	if(set) //storing
	{
		int i;
		aplExtent ext;

		m_XLine.Update(true);
		m_YLine.Update(true);

		if(m_inst==NULL)
			m_inst = m_ReportMgr->m_data->CreateInstance(m_ReportMgr->e_apl_gistogram_element);
		m_ReportMgr->m_data->PutAttr(m_inst, m_ReportMgr->a_elem_name,m_name);
		m_ReportMgr->m_data->PutAttr(m_inst, m_ReportMgr->a_elem_line_width,m_line_width);
		m_ReportMgr->m_data->PutAttr(m_inst, m_ReportMgr->a_elem_fixed,m_fixed);

		m_ReportMgr->m_data->PutAttr(m_inst, m_ReportMgr->a_apl_gistogram_element_angel, m_Angel);
		m_ReportMgr->m_data->PutAttr(m_inst, m_ReportMgr->a_apl_gistogram_element_volume, m_Volume);

		m_ReportMgr->m_data->PutAttr(m_inst, m_ReportMgr->a_apl_gistogram_element_xline, m_XLine.m_inst);
		m_ReportMgr->m_data->PutAttr(m_inst, m_ReportMgr->a_apl_gistogram_element_yline, m_YLine.m_inst);

		m_ReportMgr->m_data->PutAttr(m_inst, m_ReportMgr->a_elem_textcolor, (int)m_color);
		m_ReportMgr->m_data->PutAttr(m_inst, m_ReportMgr->a_elem_backgroundcolor, (int)m_background_color);
		
		m_ReportMgr->PutPoint(m_inst,m_ReportMgr->a_elem_org_pt, m_org_pt);
		m_ReportMgr->PutPoint(m_inst,m_ReportMgr->a_elem_size, m_size);

		m_ReportMgr->m_data->PutAttr(m_inst, m_ReportMgr->a_apl_gistogram_element_view_type, m_ViewType);

		m_ReportMgr->m_data->PutAttr(m_inst, m_ReportMgr->a_apl_gistogram_element_sort_type, m_SortDirection);

		m_Legend.Update(set);
		m_ReportMgr->m_data->PutAttr(m_inst, m_ReportMgr->a_apl_gistogram_element_legend, m_Legend.m_inst);
		if(m_pGroupColumn)
		{
			if(m_pGroupColumn->m_inst==NULL)
			{
				m_ReportMgr->m_base_query->Update(true);
				m_ReportMgr->m_header->Update(true);
			}
			m_ReportMgr->m_data->PutAttr(m_inst, m_ReportMgr->a_apl_gistogram_element_group_column, m_pGroupColumn->m_inst);
		}
		else
			m_ReportMgr->m_data->PutAttr(m_inst, m_ReportMgr->a_apl_gistogram_element_group_column, (CaplInstance*)NULL);

		for(i=0; i<m_Elements.GetSize(); i++)
		{
			m_Elements[i]->Update(true);
			ext.Add(m_Elements[i]->m_inst);
		}
		m_ReportMgr->m_data->PutAttr(m_inst, m_ReportMgr->a_apl_gistogram_element_elements, ext);
		m_DataSource.Update(true);
		m_ReportMgr->m_data->PutAttr(m_inst, m_ReportMgr->a_apl_gistogram_element_data_source, m_DataSource.m_inst);
	}
	else
	{
		if(m_inst==0) return;
		
		int tmpInt;
		int i;
		aplExtent ext;
		CaplInstance *Inst;
		CDiagramItem* pItem;

		m_ReportMgr->m_data->GetAttr(m_inst,m_ReportMgr->a_elem_name,m_name);
		m_ReportMgr->m_data->GetAttr(m_inst,m_ReportMgr->a_elem_line_width,m_line_width);
		m_ReportMgr->m_data->GetAttr(m_inst,m_ReportMgr->a_elem_fixed,m_fixed);
		m_ReportMgr->m_data->GetAttr(m_inst, m_ReportMgr->a_apl_gistogram_element_angel, m_Angel);
		m_ReportMgr->m_data->GetAttr(m_inst, m_ReportMgr->a_apl_gistogram_element_volume, m_Volume);
		m_ReportMgr->m_data->GetAttr(m_inst, m_ReportMgr->a_apl_gistogram_element_view_type, m_ViewType);
		m_ReportMgr->m_data->GetAttr(m_inst, m_ReportMgr->a_apl_gistogram_element_sort_type, m_SortDirection);

		m_ReportMgr->m_data->GetAttr(m_inst, m_ReportMgr->a_elem_textcolor, tmpInt);
		m_color = (COLORREF)tmpInt;
		m_ReportMgr->m_data->GetAttr(m_inst, m_ReportMgr->a_elem_backgroundcolor, tmpInt);
		m_background_color= (COLORREF)tmpInt;
		
		m_ReportMgr->GetPoint(m_inst,m_ReportMgr->a_elem_org_pt,m_org_pt);
		m_ReportMgr->GetPoint(m_inst,m_ReportMgr->a_elem_size,m_size);
		
		m_ReportMgr->m_data->GetAttr(m_inst, m_ReportMgr->a_apl_gistogram_element_legend, m_Legend.m_inst);
		m_Legend.Update(set);
		m_ReportMgr->m_data->GetAttr(m_inst, m_ReportMgr->a_apl_gistogram_element_group_column, Inst);
		if(Inst)
		{
			m_pGroupColumn = m_ReportMgr->FindColumnByInst(Inst);
			if(m_pGroupColumn==NULL)
					TRACE_TO_FILE(APL_T("Не удалось восстановить ссылку на поле иcточника данных!"));
		}
		m_ReportMgr->m_data->GetAttr(m_inst, m_ReportMgr->a_apl_gistogram_element_elements, ext);
		m_Elements.Clear();
		for(i=0; i<ext.GetSize(); i++)
		{
			pItem = new CDiagramItem(m_ReportMgr);
			pItem->m_inst = ext[i];
			pItem->Update(false);
			m_Elements.Add(pItem);
		}

		m_ReportMgr->m_data->GetAttr(m_inst, m_ReportMgr->a_apl_gistogram_element_xline, m_XLine.m_inst);
		m_ReportMgr->m_data->GetAttr(m_inst, m_ReportMgr->a_apl_gistogram_element_yline, m_YLine.m_inst);

		m_XLine.Update(set);
		m_YLine.Update(set);

		m_ReportMgr->m_data->GetAttr(m_inst, m_ReportMgr->a_apl_gistogram_element_data_source, m_DataSource.m_inst);
		if(m_DataSource.m_inst)
		{
			m_DataSource.Update(false);
			m_DataSource.Sort(m_XLine.m_pDSColumn, true, m_SortDirection);
			
			RecalcLayout();
		}
	}
}

bool CGistogramElement::LoadDataFromMem(BYTE *pByte, int &ind)
{
	int* pInt = NULL;
	int int_size = sizeof(int);
	TCHAR* pCh = NULL;
	
	pInt = (int*)&pByte[ind];
	m_org_pt.x = *pInt;
	ind+=int_size;
	
	pInt = (int*)&pByte[ind];
	m_org_pt.y = *pInt;
	ind+=int_size;
	
	pInt = (int*)&pByte[ind];
	m_size.cx = *pInt;//прочитал cx;
	ind+=int_size;
	
	pInt = (int*)&pByte[ind];
	m_size.cy = *pInt;//прочитал cy;
	ind+=int_size;
	
	pInt = (int*)&pByte[ind];
	m_line_width = *pInt;//прочитал толщину линии;
	ind+=int_size;

	pInt = (int*)&pByte[ind];
	m_Volume = *pInt;//прочитал толщину линии;
	ind+=int_size;
	
	pInt = (int*)&pByte[ind];
	m_Angel = *pInt;//прочитал толщину линии;
	ind+=int_size;
	
	pInt = (int*)&pByte[ind];
	int cnt = *pInt;
	ind+=int_size;
	m_name.Empty();
	for(int i=0; i<cnt;i++)
	{
		pCh = (TCHAR*)&pByte[ind];
		m_name+=*pCh;
		ind+=sizeof(TCHAR);
	}

	m_Legend.LoadDataFromMem(pByte, ind);

	m_XLine.LoadDataFromMem(pByte, ind);
	m_YLine.LoadDataFromMem(pByte, ind);
	
	return true;
}

void CGistogramElement::CopyToMem(BYTE* pByte, int &ind)
{
	int* pInt = NULL;
	int int_size = sizeof(int);
	TCHAR* pCh = NULL;
	
	pInt = (int*)&pByte[ind];
	*pInt = m_type;//записал тип элемента;
	ind+=sizeof(TReportElementType);
	
	pInt = (int*)&pByte[ind];
	*pInt = m_org_pt.x;//записал x;
	ind+=int_size;
	
	pInt = (int*)&pByte[ind];
	*pInt = m_org_pt.y;//записал y;
	ind+=int_size;
	
	pInt = (int*)&pByte[ind];
	*pInt = m_size.cx;//записал cx;
	ind+=int_size;
	
	pInt = (int*)&pByte[ind];
	*pInt = m_size.cy;//записал cy;
	ind+=int_size;
	
	pInt = (int*)&pByte[ind];
	*pInt = m_line_width;//записал толщину линии;
	ind+=int_size;

	pInt = (int*)&pByte[ind];
	*pInt = m_Volume;
	ind+=int_size;
	pInt = (int*)&pByte[ind];
	*pInt = m_Angel;
	ind+=int_size;
	
	pInt = (int*)&pByte[ind];
	*pInt = m_name.GetLength();
	ind+=int_size;
	for(int i=0; i<m_name.GetLength();i++)
	{
		pCh = (TCHAR*)&pByte[ind];
		*pCh = m_name[i];
		ind+=sizeof(TCHAR);
	}

	m_Legend.CopyToMem(pByte, ind);

	m_XLine.CopyToMem(pByte, ind);
	m_YLine.CopyToMem(pByte, ind);
}

int CGistogramElement::GetMemSize()
{
	int int_size = sizeof(int);
	int size = 4*int_size;//размер элемента;

	size+=sizeof(TReportElementType);//тип элемента;
	size+=int_size;//толщина линии;
	size+=int_size;//наименование;
	size+=int_size;//Volume;
	size+=int_size;//Angel;
	size+=m_name.GetLength()*sizeof(TCHAR);

	//свойства легенды
	size+=m_Legend.GetMemSize();
	//свойства осей
	size+=m_XLine.GetMemSize();
	size+=m_YLine.GetMemSize();

	return size;
}

void CGistogramElement::Delete()
{
	if(m_ReportMgr==NULL) return;
	if(m_ReportMgr->m_data==NULL) return;

	if(m_inst==NULL) return;

	CaplInstance *inst;
	m_ReportMgr->m_data->GetAttr(m_inst, m_ReportMgr->a_elem_org_pt, inst);
	if(inst)
		m_ReportMgr->m_data->DeleteInstance(inst);
	m_ReportMgr->m_data->GetAttr(m_inst, m_ReportMgr->a_elem_size, inst);
	if(inst)
		m_ReportMgr->m_data->DeleteInstance(inst);

	m_ReportMgr->m_data->DeleteInstance(m_inst);
	m_DataSource.Delete();
	//Items
	for(int i=0; i<m_Elements.GetSize(); i++)
		m_Elements[i]->Delete();
	
	m_XLine.Delete();
	m_YLine.Delete();
	m_Legend.Delete();

	m_Elements.Clear();
}

CGistogramElement& CGistogramElement::operator = (const CGistogramElement& Elem)
{
	int i;
	CDiagramItem* pItem;

	*((CReportElement*)this) = *((CReportElement*)&Elem);
	m_inst = Elem.m_inst;
	m_XLine = Elem.m_XLine;
	m_YLine = Elem.m_YLine;
	m_Legend = Elem.m_Legend;
	m_pGroupColumn = Elem.m_pGroupColumn;
	m_Elements.Clear();
	for(i=0; i<Elem.m_Elements.Size; i++)
	{
		pItem = new CDiagramItem(m_ReportMgr);
		*pItem = *Elem.m_Elements[i];
		m_Elements.Add(pItem);
	}
	
	return *this;
}

bool CGistogramElement::GetLegendNeedSizes(CDC *pDC, CRect rect, CSize &sizes, int &cols)
{
	//если отображение слева или справа, то всегда отображаем в столибик, иначе пытаемся разбить столбцы
	cols = 1;
	sizes = CSize(0, 0);
	if(m_Legend.m_LegendPosition==aplLegendPositionNone) return false;

	if(m_ViewType==APL_GISTOGRAM_VIEW_TYPE_CYCLE) // круговая гистограмма (таблетка)
		return GetLegendNeedSizesCycle(pDC, rect, sizes, cols);

	int i;
	int max;
	int tmp;
	CString buf;
	CStringArray elements;
	if(m_ReportMgr->m_data_source.GetUseUserLegendText())
	{
		CStringArray strArr;
		m_ReportMgr->m_data_source.GetUserLegendText(strArr);
		for(i = 0; i < strArr.GetSize(); i++)
			elements.Add(strArr[i]);
	}
	else
	{
		for(i=0; i<m_Elements.GetSize(); i++)
			elements.Add(m_Elements[i]->m_name);
	}

	int h = pDC->GetTextExtent(_T("Ag")).cy;
	if(m_Legend.m_LegendPosition==aplLegendPositionLeft || m_Legend.m_LegendPosition==aplLegendPositionRight)
	{
		//если используется дополнительная легенда
		if(m_ReportMgr->m_data_source.GetUseAdditionalUserLegendText())
		{
			CStringArray strArr;
			m_ReportMgr->m_data_source.GetAdditionalUserLegendText(strArr);
			for(i = 0; i < strArr.GetSize(); i++)
				elements.Add(strArr[i]);
		}
		//определяем наиболее широкий элемент
		max = 0;
		for(i=0; i<elements.GetSize(); i++)
		{
			tmp = pDC->GetTextExtent(elements[i]).cx;
			max = tmp>max?tmp:max;
		}
		if(m_Legend.m_LegendMode!=aplLegendMode_3)
			max+=h*2;

		sizes.cx = max+15;
		sizes.cy = h*elements.GetSize()+h/4*(elements.GetSize()-1)+15;
	}
	else
	{
		int cnt;
		int rows;

		max = 0;
		for(i=0; i<elements.GetSize(); i++)
		{
			tmp = pDC->GetTextExtent(elements[i]).cx;
			max = tmp>max?tmp:max;
		}
		//Определяем сколько таких уместится в ширину элемента
		cnt = (rect.Width()-75)/(max+h*2);
		cols = cnt<0?1:elements.GetSize()<cnt?elements.GetSize():cnt;

		rows = cnt==0?0:elements.GetSize()/cnt;
		if(cnt && elements.GetSize()%cnt) rows++;

		//если используется дополнительная легенда
		if(m_ReportMgr->m_data_source.GetUseAdditionalUserLegendText())
		{
			CStringArray strArr;
			m_ReportMgr->m_data_source.GetAdditionalUserLegendText(strArr);
			rows += strArr.GetSize();
		}

		sizes.cx = cols*(max+h*2)+15;
		sizes.cy = h*rows+h/4*(rows-1)+15;
	}
	return true;
}

bool CGistogramElement::GetLegendNeedSizesCycle(CDC *pDC, CRect rect, CSize &sizes, int &cols)
{
	//если отображение слева или справа, то всегда отображаем в столибик, иначе пытаемся разбить столбцы
	cols = 1;
	sizes = CSize(0, 0);
	if(m_Legend.m_LegendPosition==aplLegendPositionNone) return false;
	if(m_Elements.GetSize()==0) return true;

	int indX = m_DataSource.GetHeader()->Find(m_XLine.m_pDSColumn->GetName());
	int indY = m_DataSource.GetHeader()->Find(m_Elements[0]->GetColumnName());
	int rows = m_DataSource.GetRows();

	//определяем наиболее широкий элемент
	int max = 0;
	int count_el = 0;
	for(int i=0; i<rows; i++)
	{
		double dVal;
		m_DataSource.GetAt(i, indY, dVal);
		if(dVal==0) continue;
		CString sVal;
		m_DataSource.GetAt(i, indX, sVal);

		int tmp = pDC->GetTextExtent(sVal).cx;
		max = tmp>max?tmp:max;

		count_el++;
	}

	if(m_Legend.m_LegendPosition==aplLegendPositionLeft || m_Legend.m_LegendPosition==aplLegendPositionRight)
	{
		if(m_Legend.m_LegendMode!=aplLegendMode_3)
			max+=75;

		sizes.cx = max+50;
		sizes.cy = pDC->GetTextExtent(_T("Ag")).cy*count_el+pDC->GetTextExtent(_T("Ag")).cy/4*(count_el-1)+50;
	}
	else
	{
		//Определяем сколько таких уместится в ширину элемента
		int cnt = (rect.Width()-15)/(max+15);
		cols = cnt<0?1:count_el<cnt?count_el:cnt;

		int TextRows = count_el/cnt;
		if(cnt && count_el%cnt) TextRows++;

		sizes.cx = cols*(max+75)+75;
		sizes.cy = pDC->GetTextExtent(_T("Ag")).cy*TextRows+pDC->GetTextExtent(_T("Ag")).cy/4*(TextRows-1)+75;
	}
	return true;
}

bool CGistogramElement::DrawLegeng(CDC *pDC, CRect &rect)
{
	if(m_Legend.m_LegendPosition==aplLegendPositionNone) return false;

	if(m_ViewType==APL_GISTOGRAM_VIEW_TYPE_CYCLE) // круговая гистограмма (таблетка)
		return DrawLegengCycle(pDC, rect);

	CRect MyRect = rect;
	CSize sizes;
	int i;
	int tmp;
	int h, dH;
	int dXText;
	int cols;
	int oldBkMode;
	CString buf;
	CFont font, *pOld;
	COLORREF OldColor;
	font.CreatePointFontIndirect(&m_Legend.m_LogFont, pDC);
	pOld = pDC->SelectObject(&font);
	oldBkMode = pDC->SetBkMode(TRANSPARENT);

	GetLegendNeedSizes(pDC, rect, sizes, cols);

	dH = pDC->GetTextExtent(_T("Ag")).cy/4;
	h = pDC->GetTextExtent(_T("Ag")).cy;

	// заполним текст и цвет для элементов легенды
	struct Elems_struct
	{
		CString* name;
		COLORREF color;
	};
	CStringArray strArr, strArrAdditional;
	CArray<Elems_struct> elements, additional_elements;
	if(m_ReportMgr->m_data_source.GetUseUserLegendText() && m_Elements.GetSize())
	{
		m_ReportMgr->m_data_source.GetUserLegendText(strArr);

		CArray<COLORREF> column_colors;
		bool ret_c = GetColor(m_Elements[0]->m_color, strArr.GetSize(), column_colors);
		ASSERT(ret_c);

		for(i = 0; i < strArr.GetSize(); i++)
		{
			Elems_struct new_elem;
			new_elem.name = &strArr[i];
			new_elem.color = column_colors[i];
			elements.Add(new_elem);
		}
	}
	else
	{
		for(i=0; i<m_Elements.GetSize(); i++)
		{
			Elems_struct new_elem;
			new_elem.name = &m_Elements[i]->m_name;
			new_elem.color = m_Elements[i]->m_color;
			elements.Add(new_elem);
		}
	}
	if(m_ReportMgr->m_data_source.GetUseAdditionalUserLegendText())
	{
		m_ReportMgr->m_data_source.GetAdditionalUserLegendText(strArrAdditional);
		for(i = 0; i < strArrAdditional.GetSize(); i++)
		{
			Elems_struct new_elem;
			new_elem.name = &strArrAdditional[i];
			new_elem.color = RGB(255, 0 , 0);
			additional_elements.Add(new_elem);
		}
	}

	int max = 0;
	for(i=0; i<elements.GetSize(); i++)
	{
		tmp = pDC->GetTextExtent(*elements[i].name).cx;
		max = tmp>max?tmp:max;
	}

	if(m_Legend.m_LegendPosition==aplLegendPositionLeft || m_Legend.m_LegendPosition==aplLegendPositionRight)
	{
		//для дополнительных строк легенды
		for(i=0; i<additional_elements.GetSize(); i++)
		{
			tmp = pDC->GetTextExtent(*additional_elements[i].name).cx;
			max = tmp>max?tmp:max;
		}

		if(m_Legend.m_LegendPosition==aplLegendPositionLeft)
		{
			rect.right-=sizes.cx;
			MyRect.left = MyRect.right-sizes.cx;
		}
		else
		{
			rect.left+=sizes.cx;
			MyRect.right = MyRect.left+sizes.cx;
		}
		if(m_Legend.m_LegendPosition2==aplLegendPositionLess)
			MyRect.top -= 15;
		else if(m_Legend.m_LegendPosition2==aplLegendPositionMore)
			MyRect.top = MyRect.bottom+sizes.cy;
		else
			MyRect.top = MyRect.bottom-(MyRect.Height()-sizes.cy)/2;

		for(i=0; i<elements.GetSize(); i++)
		{
			dXText = 15;
			if(m_Legend.m_LegendMode==aplLegendMode_1)
			{
				CBrush br, *old_br;
				br.CreateSolidBrush(elements[i].color);
				old_br = pDC->SelectObject(&br);
				CRect LegendRect(MyRect.left+(int)(h*0.2), MyRect.top-i*(h+dH), MyRect.left+h*2, MyRect.top-i*(h+dH)-h);
				pDC->FillRect(LegendRect, &br);
				pDC->SelectObject(old_br);
				br.DeleteObject();

				dXText = h*2;
			}
			else if(m_Legend.m_LegendMode==aplLegendMode_2)
			{
				CBrush br, *old_br;
				br.CreateSolidBrush(elements[i].color);
				old_br = pDC->SelectObject(&br);
				CRect LegendRect(MyRect.left+h*2+max, MyRect.top-i*(h+dH), MyRect.left+(int)(h*0.2)+max, MyRect.top-i*(h+dH)-h);
				pDC->FillRect(LegendRect, &br);
				pDC->SelectObject(old_br);
				br.DeleteObject();

				dXText = 0;
			}
			else if(m_Legend.m_LegendMode==aplLegendMode_3)
				OldColor = pDC->SetTextColor(elements[i].color);

			pDC->TextOut(MyRect.left+dXText, MyRect.top-i*(h+dH), *elements[i].name);

			if(m_Legend.m_LegendMode==aplLegendMode_3)
				pDC->SetTextColor(OldColor);
		}

		//для дополнительных строк легенды
		for(i=0; i<additional_elements.GetSize(); i++)
		{
			dXText = 15;
			OldColor = pDC->SetTextColor(additional_elements[i].color);
			pDC->TextOut(MyRect.left+dXText, MyRect.top-i*(h+dH), *additional_elements[i].name);
			pDC->SetTextColor(OldColor);
		}
	}
	else
	{
		int j;
		int ind;
		int rows = 0;
		if(cols)
		{
			rows = cols==0?0:elements.GetSize()/cols;
			if(rows*cols<elements.GetSize()) rows++;
		}

		if(m_Legend.m_LegendPosition==aplLegendPositionTop)
		{
			rect.top-=sizes.cy;
			MyRect.top-=15;
			MyRect.bottom = rect.top-15;
		}
		else
		{
			rect.bottom+=sizes.cy;
			MyRect.top = rect.bottom+15;
		}
		if(m_Legend.m_LegendPosition2==aplLegendPositionLess)
			MyRect.left += 15;
		else if(m_Legend.m_LegendPosition2==aplLegendPositionMore)
			MyRect.left = MyRect.right-sizes.cx-h*2;
		else
			MyRect.left = MyRect.left+(MyRect.Width()-sizes.cx)/2-h;

		for(i=0; i<cols; i++)
		{
			for(j=0; j<rows; j++)
			{
				ind = j*cols+i;
				if(ind>=elements.GetSize()) continue;
				dXText = 15;
				if(m_Legend.m_LegendMode==aplLegendMode_1)
				{
					CBrush br, *old_br;
					br.CreateSolidBrush(elements[ind].color);
					old_br = pDC->SelectObject(&br);
					CRect LegendRect(MyRect.left+(max+h*2)*i, MyRect.top-j*(h+dH), MyRect.left+(max+h*2)*i+(int)(h*1.8), MyRect.top-j*(h+dH)-h);
					pDC->FillRect(LegendRect, &br);
					pDC->SelectObject(old_br);
					br.DeleteObject();

					dXText = 0;

					OldColor = pDC->SetTextColor(m_Legend.m_TextColor);
				}
				else if(m_Legend.m_LegendMode==aplLegendMode_2)
				{
					CBrush br, *old_br;
					br.CreateSolidBrush(elements[ind].color);
					old_br = pDC->SelectObject(&br);
					CRect LegendRect(MyRect.left+(max+h*2)*(i+1), MyRect.top-j*(h+dH), MyRect.left+(max+h*2)*(i+1)+(int)(h*1.8), MyRect.top-j*(h+dH)-h);
					pDC->FillRect(LegendRect, &br);
					pDC->SelectObject(old_br);
					br.DeleteObject();

					dXText = max - pDC->GetTextExtent(*elements[ind].name).cx;

					OldColor = pDC->SetTextColor(m_Legend.m_TextColor);
				}
				else if(m_Legend.m_LegendMode==aplLegendMode_3)
					OldColor = pDC->SetTextColor(elements[ind].color);
				
				pDC->TextOut(MyRect.left+(max+h*2)*i+dXText+(int)(h*1.9), MyRect.top-j*(h+dH), *elements[ind].name);
				
				pDC->SetTextColor(OldColor);
			}
		}

		//для дополнительных строк легенды
		for(i=0; i<additional_elements.GetSize(); i++)
		{
			dXText = 15;
			OldColor = pDC->SetTextColor(additional_elements[i].color);
			pDC->TextOut(rect.left+(rect.right-rect.left-pDC->GetTextExtent(*additional_elements[i].name).cx)/2+dXText, MyRect.top-(rows+i)*(h+dH), *additional_elements[i].name);
			pDC->SetTextColor(OldColor);
		}
	}
	pDC->SetBkMode(oldBkMode);
	pDC->SelectObject(pOld);
	font.DeleteObject();
	return true;
}

bool CGistogramElement::DrawLegengCycle(CDC *pDC, CRect &rect)
{
	if(m_Legend.m_LegendPosition==aplLegendPositionNone) return false;
	if(m_Elements.GetSize()==0) return true;

	CRect MyRect = rect;
	CSize sizes;
	int i;
	int h, dH;
	int dXText;
	int cols;
	int oldBkMode;
	CString buf;
	CFont font, *pOld;
	COLORREF OldColor;
	font.CreatePointFontIndirect(&m_Legend.m_LogFont, pDC);
	pOld = pDC->SelectObject(&font);
	oldBkMode = pDC->SetBkMode(TRANSPARENT);

	GetLegendNeedSizes(pDC, rect, sizes, cols);

	dH = pDC->GetTextExtent(_T("Ag")).cy/4;
	h = pDC->GetTextExtent(_T("Ag")).cy;

	int indX = m_DataSource.GetHeader()->Find(m_XLine.m_pDSColumn->GetName());
	int indY = m_DataSource.GetHeader()->Find(m_Elements[0]->GetColumnName());
	int rows = m_DataSource.GetRows();

	if(m_Legend.m_LegendPosition==aplLegendPositionLeft || m_Legend.m_LegendPosition==aplLegendPositionRight)
	{
		if(m_Legend.m_LegendPosition==aplLegendPositionLeft)
		{
			rect.right-=sizes.cx;
			MyRect.left = MyRect.right-sizes.cx;
		}
		else
		{
			rect.left+=sizes.cx;
			MyRect.right = MyRect.left+sizes.cx;
		}
		if(m_Legend.m_LegendPosition2==aplLegendPositionLess)
			MyRect.top -= 15;
		else if(m_Legend.m_LegendPosition2==aplLegendPositionMore)
			MyRect.top = MyRect.bottom+sizes.cy;
		else
			MyRect.top = MyRect.bottom-(MyRect.Height()-sizes.cy)/2;

		CArray<COLORREF> column_colors;
		bool ret_c = GetColor(m_Elements[0]->m_color, rows, column_colors);
		ASSERT(ret_c);

		int cnt = 0;
		for(i=0; i<rows; i++)
		{
			double dVal;
			m_DataSource.GetAt(i, indY, dVal);
			if(dVal==0) continue;
			CString sVal;
			m_DataSource.GetAt(i, indX, sVal);

			dXText = 15;
			if(m_Legend.m_LegendMode==aplLegendMode_1)
			{
				CBrush br, *old_br;
				br.CreateSolidBrush(column_colors[i]);
				old_br = pDC->SelectObject(&br);
				CRect LegendRect(MyRect.left+15, MyRect.top-cnt*(h+dH), MyRect.left+70, MyRect.top-cnt*(h+dH)-h);
				pDC->FillRect(LegendRect, &br);
				pDC->SelectObject(old_br);
				br.DeleteObject();

				dXText = 75;
			}
			else if(m_Legend.m_LegendMode==aplLegendMode_2)
			{
				CBrush br, *old_br;
				br.CreateSolidBrush(column_colors[i]);
				old_br = pDC->SelectObject(&br);
				CRect LegendRect(MyRect.right-15, MyRect.top-cnt*(h+dH), MyRect.right-70, MyRect.top-cnt*(h+dH)-h);
				pDC->FillRect(LegendRect, &br);
				pDC->SelectObject(old_br);
				br.DeleteObject();
			}
			else if(m_Legend.m_LegendMode==aplLegendMode_3)
				OldColor = pDC->SetTextColor(column_colors[i]);

			pDC->TextOut(MyRect.left+dXText, MyRect.top-cnt*(h+dH), sVal);

			if(m_Legend.m_LegendMode==aplLegendMode_3)
				pDC->SetTextColor(OldColor);

			cnt++;
		}
	}
	else
	{
		if(m_Legend.m_LegendPosition==aplLegendPositionTop)
		{
			rect.top-=sizes.cy;
			MyRect.top-=15;
			MyRect.bottom = rect.top-15;
		}
		else
		{
			rect.bottom+=sizes.cy;
			MyRect.top = rect.bottom+15;
		}
		if(m_Legend.m_LegendPosition2==aplLegendPositionLess)
			MyRect.left += 15;
		else if(m_Legend.m_LegendPosition2==aplLegendPositionMore)
			MyRect.left = MyRect.right-sizes.cx;
		else
			MyRect.left = MyRect.left+(MyRect.Width()-sizes.cx)/2;

		//определяем наиболее широкий элемент
		int max = 0;
		for(i=0; i<rows; i++)
		{
			double dVal;
			m_DataSource.GetAt(i, indY, dVal);
			if(dVal==0) continue;
			CString sVal;
			m_DataSource.GetAt(i, indX, sVal);

			int tmp = pDC->GetTextExtent(sVal).cx;
			max = tmp>max?tmp:max;
		}

		CArray<COLORREF> column_colors;
		bool ret_c = GetColor(m_Elements[0]->m_color, rows, column_colors);
		ASSERT(ret_c);

		int cnt = 0;
		for(i=0; i<rows; i++)
		{
			double dVal;
			m_DataSource.GetAt(i, indY, dVal);
			if(dVal==0) continue;
			CString sVal;
			m_DataSource.GetAt(i, indX, sVal);

			dXText = 15;
			if(m_Legend.m_LegendMode==aplLegendMode_1)
			{
				CBrush br, *old_br;
				br.CreateSolidBrush(column_colors[i]);
				old_br = pDC->SelectObject(&br);
				CRect LegendRect(MyRect.left+(max+75)*(cnt%cols), MyRect.top-(cnt/cols)*(h+dH), MyRect.left+50+(max+75)*(cnt%cols), MyRect.top-(cnt/cols)*(h+dH)-h);
				pDC->FillRect(LegendRect, &br);
				pDC->SelectObject(old_br);
				br.DeleteObject();

				dXText = 50;
				OldColor = pDC->SetTextColor(m_Legend.m_TextColor);
			}
			else if(m_Legend.m_LegendMode==aplLegendMode_2)
			{
				CBrush br, *old_br;
				br.CreateSolidBrush(column_colors[i]);
				old_br = pDC->SelectObject(&br);
				CRect LegendRect(MyRect.left+(max+75)*(cnt%cols+1), MyRect.top-(cnt/cols)*(h+dH), MyRect.left+(max+75)*(cnt%cols+1)-50, MyRect.top-(cnt/cols)*(h+dH)-h);
				pDC->FillRect(LegendRect, &br);
				pDC->SelectObject(old_br);
				br.DeleteObject();

				OldColor = pDC->SetTextColor(m_Legend.m_TextColor);
			}
			else if(m_Legend.m_LegendMode==aplLegendMode_3)
				OldColor = pDC->SetTextColor(column_colors[i]);

			pDC->TextOut(MyRect.left+(max+75)*(cnt%cols)+dXText, MyRect.top-(cnt/cols)*(h+dH), sVal);

			pDC->SetTextColor(OldColor);

			cnt++;
		}
	}
	pDC->SetBkMode(oldBkMode);
	pDC->SelectObject(pOld);
	font.DeleteObject();
	return true;
}

bool CGistogramElement::GenerateGistogram()
{
	//Если указано поле группировки, то сортируем сначала по этому полю, затем формируем расширенный ИД.
	int i;
	int j;
	int rows, cols;
	int ind;
	int row_ind;
	int show_mode;
	int group_ind = -1;
	int elem_ind = -1;
	CaplReportValue *val = NULL, *cur_val = NULL;
	CDiagramItem *pItem;
	CHeader *pHeader = NULL, *pOldHeader;
	CString buf;
	double dVal;
	
	m_DataSource.RemoveAll();
	
	if(m_pGroupColumn)
		m_ReportMgr->m_data_source.Sort(m_pGroupColumn, true, m_SortDirection);
	
	m_ReportMgr->m_data_source.GetSize(rows, cols);
	
	//формируем заголовок
	pHeader = new CHeader;
	for(i=0; i<m_Elements.GetSize(); i++)
	{
		pItem = m_Elements[i];
		if(pItem->m_show_mode==aplShowValue)
			pHeader->AddColumn(m_Elements[i]->m_pDSColumn->GetName(), m_Elements[i]->m_pDSColumn->m_type);
		else if(pItem->m_show_mode==aplShowCount)
			pHeader->AddColumn(m_Elements[i]->m_pDSColumn->GetName()+_T("_Count"), m_Elements[i]->m_pDSColumn->m_type);
		else if(pItem->m_show_mode==aplShowSumm)
			pHeader->AddColumn(m_Elements[i]->m_pDSColumn->GetName()+_T("_Summ"), m_Elements[i]->m_pDSColumn->m_type);
		else if(pItem->m_show_mode==aplShowAverage)
			pHeader->AddColumn(m_Elements[i]->m_pDSColumn->GetName()+_T("_Average"), m_Elements[i]->m_pDSColumn->m_type);
	}
	
	if(pHeader->Find(m_XLine.m_pDSColumn->GetName())<0)
		pHeader->AddColumn(m_XLine.m_pDSColumn->GetName(), m_XLine.m_pDSColumn->m_type);
	
	pOldHeader = m_DataSource.SetHeader(pHeader);
	if(pOldHeader) delete pOldHeader;
	
	if(m_pGroupColumn==NULL)
	{
		for(j=0; j<rows; j++)
			m_DataSource.AddRow();
	}
	else
	{
		val = NULL;
		group_ind = m_ReportMgr->m_data_source.GetHeader()->Find(m_pGroupColumn->GetName());
		for(j=0; j<rows; j++)
		{
			cur_val = m_ReportMgr->m_data_source.GetAt(j, group_ind);
			if(val==NULL || *val!=*cur_val)
			{
				val = cur_val;
				m_DataSource.AddRow();
			}
		}
	}
	
	cols = pHeader->GetSize();

	for(i=0; i<cols; i++)
	{
		buf = pHeader->GetColumn(i)->GetName();
		ind = m_ReportMgr->m_data_source.GetHeader()->Find(buf);
		if(m_pGroupColumn==NULL)
		{
			if(ind<0) continue;
			for(j=0; j<rows; j++)
			{
				buf = m_ReportMgr->m_data_source.GetRow(j)->GetAsString(ind);
				m_DataSource.SetAt(j, i, buf);
			}
		}
		else
		{
			if(ind<0)
			{
				buf.Replace(_T("_Count"), _T(""));
				buf.Replace(_T("_Summ"), _T(""));
				buf.Replace(_T("_Average"), _T(""));
				ind = m_ReportMgr->m_data_source.GetHeader()->Find(buf);
				if(ind<0) continue;
			}

			show_mode = -1;
			elem_ind = -1;
			for(j=0; j<m_Elements.GetSize(); j++)
			{
				pItem = m_Elements[j];
				if(pItem->GetColumnName()==pHeader->GetColumn(i)->GetName())
				{
					elem_ind = j;
					show_mode = m_Elements[j]->m_show_mode;
					break;
				}
			}
			if(show_mode<0)
			{
				if(m_XLine.m_pDSColumn->GetName()==pHeader->GetColumn(i)->GetName())
				{
					show_mode = aplShowValue;
				}
				else
				{
					TRACE_TO_FILE(APL_T("Не удалось определить тип отображаетмого значения!"));
					continue;
				}
			}
			val = NULL;
			row_ind = 0;
			for(j=0; j<rows; j++)
			{
				cur_val = m_ReportMgr->m_data_source.GetAt(j, group_ind);
				if(val==NULL || *val!=*cur_val)
				{
					val = cur_val;
					if(show_mode==aplShowValue)
					{
						buf = m_ReportMgr->m_data_source.GetRow(j)->GetAsString(ind);
						m_DataSource.SetAt(row_ind, i, buf);
					}
					else if(show_mode==aplShowSumm)
					{
						dVal = m_ReportMgr->m_data_source.GetSummInGroup(j, m_pGroupColumn, m_Elements[elem_ind]->m_pDSColumn);
						m_DataSource.SetAt(row_ind, i, dVal);
					}
					else if(show_mode==aplShowAverage)
					{
						dVal = m_ReportMgr->m_data_source.GetAverangeInGroup(j, m_pGroupColumn, m_Elements[elem_ind]->m_pDSColumn);
						m_DataSource.SetAt(row_ind, i, dVal);
					}
					else if(show_mode==aplShowCount)
					{
						dVal = m_ReportMgr->m_data_source.GetCountInGroup(j, m_pGroupColumn, m_Elements[elem_ind]->m_pDSColumn, _T(""));
						m_DataSource.SetAt(row_ind, i, dVal);
					}
					row_ind++;
				}
			}
		}
	}

	m_DataSource.CloneUserLegendText(m_ReportMgr->m_data_source, m_ReportMgr);

	m_DataSource.Sort(m_XLine.m_pDSColumn, true, m_SortDirection);
	m_DataSource.Update(true);

	CTableContent *pTableContent = new CTableContent(m_ReportMgr);
	pTableContent->m_sheets.Add(m_owner_sheet);
	pTableContent->m_Title = m_name;
	m_ReportMgr->m_TableContents.Add(pTableContent);
	
	return true;
}

bool CGistogramElement::RecalcLayout()
{
	int rows, cols;

	m_dXRange = 0;
	m_dXMax = m_dXMin = 0;
	m_dYRange = 0;
	m_dYMax = m_dYMin = 0;

	m_XIndex = m_DataSource.GetHeader()->Find(m_XLine.m_pDSColumn->GetName());
	if(m_XIndex<0) return false;
	
	m_DataSource.GetSize(rows, cols);
	if(!m_XLine.m_bProRata)
	{
		m_DataSource.GetAt(0, m_XIndex, m_dXMin);
		m_DataSource.GetAt(rows-1, m_XIndex, m_dXMax);
		
		m_dXRange = m_dXMax-m_dXMin>m_dXRange?m_dXMax-m_dXMin:m_dXRange;
	}

	double dVal;
	CString sVal;
	int ind = m_DataSource.GetHeader()->Find(m_Elements[0]->GetColumnName());

	if(m_ViewType == APL_GISTOGRAM_VIEW_TYPE_TIMELINE) // временная гистограмма
	{
		m_dYRange = 1;
		m_dYMax = 1;
		m_dYMin = 0;

		CArray<Timeline_values_struct> values;
		for(int i = 0; i < rows; i++)
		{
			m_DataSource.GetAt(i, ind, sVal);
			CStringArray sArr, AdditionalCoordinateArray;
			bool bAdditionalLine;
			Report_ConvertStrAggr2Array(sVal, sArr, AdditionalCoordinateArray, bAdditionalLine);
			Timeline_values_struct new_val;
			Report_ConvertAggr2TimeLine(sArr, new_val);
			values.Add(new_val);
		}

		for(int i = 0; i < values.GetSize(); i++)
		{
			if(values[i].is_value_begin == true)
			{
				if(m_dYMax < values[i].value_begin)
					m_dYMax = values[i].value_begin;
			}
			if(values[i].is_value_end == true)
			{
				if(m_dYMax < values[i].value_end)
					m_dYMax = values[i].value_end;
			}
		}
	}
	else
	{
		m_DataSource.GetAt(0, ind, sVal);
		if(sVal.Find(_T(';'))!=-1) // если в столбике выводится несколько значений
		{
			CStringArray sArr, AdditionalCoordinateArray;
			bool bAdditionalLine;
			Report_ConvertStrAggr2Array(sVal, sArr, AdditionalCoordinateArray, bAdditionalLine);
			m_dYMin = 0;
			for(int s=0; s<sArr.GetSize();s++)
				m_dYMin += __atof(sArr[s]);
		}
		else
			m_DataSource.GetAt(0, ind, m_dYMin);
		m_dYMax = m_dYMin;
		for(int iDI=0; iDI<m_Elements.GetSize(); iDI++)
		{
			ind = m_DataSource.GetHeader()->Find(m_Elements[iDI]->GetColumnName());
			if(ind<0) continue;
			for(int i=0; i<rows; i++)
			{
				m_DataSource.GetAt(i, ind, sVal);
				if(sVal.Find(_T(';'))!=-1) // если в столбике выводится несколько значений
				{
					CStringArray sArr, AdditionalCoordinateArray;
					bool bAdditionalLine;
					Report_ConvertStrAggr2Array(sVal, sArr, AdditionalCoordinateArray, bAdditionalLine);
					dVal = 0;
					for(int s=0; s<sArr.GetSize();s++)
						dVal += __atof(sArr[s]);
				}
				else
					m_DataSource.GetAt(i, ind, dVal);
				m_dYMax = dVal>m_dYMax?dVal:m_dYMax;
				m_dYMin = dVal<m_dYMin?dVal:m_dYMin;
			}
		}
		if(m_YLine.m_bCalcFromZero)
			m_dYMin = m_dYMin<0?m_dYMin:0;
		if(m_ReportMgr->m_data_source.GetIntegerMaxY())
			m_dYMax = ceil(m_dYMax);
	}
	m_dYRange = m_dYMax-m_dYMin>m_dYRange?m_dYMax-m_dYMin:m_dYRange;

	return true;
}

void CGistogramElement::Draw3DRect(int x1, int y1, int x2, int y2, double scale, CDC* pDC, COLORREF color_brush, int y_min, int y_max, bool bAdditional)
{
	Gdiplus::Graphics* graphics = NULL;
	Gdiplus::GraphicsContainer container;
	Gdiplus::Pen* pen_gdi = NULL;
	Gdiplus::Color color_main, color_dark, color_light;

	// передняя грань
	if(m_ReportMgr->m_data_source.GetUseGDIPlus())
	{
		graphics = CreateGDIPlusGraphics(pDC->m_hDC, container);
		GetGradientColors(color_brush, color_main, color_dark, color_light);

		pen_gdi = new Gdiplus::Pen(color_light, 1.7f);

		if(y1 != y2)
		{
			// преобразование координат
			POINT pt[2];
			pt[0].x = x1;
			pt[0].y = y1;
			pt[1].x = x2;
			pt[1].y = y2;
			::DPtoLP(pDC->m_hDC, pt, 2);
			Gdiplus::Rect rect(pt[0].x, -pt[0].y, abs(pt[1].x-pt[0].x), abs(pt[1].y-pt[0].y));

			Gdiplus::Brush* brush = NULL;
			Gdiplus::LinearGradientBrush* lb = NULL;
			Gdiplus::HatchBrush* hb = NULL;
			if(bAdditional)
			{
				hb = new Gdiplus::HatchBrush(Gdiplus::HatchStyleWideUpwardDiagonal, Gdiplus::Color(210, 210, 210), color_main);
				brush = hb;
			}
			else
			{
				Gdiplus::Color colors[4] = {color_light, color_main, color_main, color_dark};
				float positions[4] = {0.0f, 0.3f, 0.7f, 1.0f};
				lb = new Gdiplus::LinearGradientBrush(rect, color_dark, color_light, Gdiplus::LinearGradientModeForwardDiagonal);
				lb->SetInterpolationColors(colors, positions, 4);
				brush = lb;
			}

			graphics->FillRectangle(brush, rect);
			graphics->DrawRectangle(pen_gdi, rect);

			if(lb) delete lb;
			if(hb) delete hb;
		}

		HDC gdiDC = graphics->GetHDC();
		graphics->ReleaseHDC(gdiDC);
	}
	else
	{
		if(y1 != y2)
			pDC->Rectangle(x1, y1, x2, y2);
	}

	double angel = ((double)m_Angel)/180.0*PI;

	POINT pt[4];

	// верхняя грань
	pt[0].x = x1;
	pt[0].y = y2;
	pt[1].x = x1+(int)(m_Volume*scale*(sin(angel)/cos(angel)));
	pt[1].y = (int)(y2+m_Volume*scale);
	pt[2].x = x2+(int)(m_Volume*scale*(sin(angel)/cos(angel)));
	pt[2].y = (int)(y2+m_Volume*scale);
	pt[3].x = x2;
	pt[3].y = y2;
	if(m_ReportMgr->m_data_source.GetUseGDIPlus())
	{
		::DPtoLP(pDC->m_hDC, pt, 4);

		Gdiplus::Point points[4];
		for(int i = 0; i < 4; i++)
		{
			points[i].X = pt[i].x;
			points[i].Y = -pt[i].y;
		}

		Gdiplus::Pen* pen_top = NULL, *pen = NULL;
		Gdiplus::Color top_color_main, top_color_dark, top_color_light;
		if(m_ReportMgr->m_data_source.GetUseUserNullColor() && y1 == y2)
		{
			COLORREF null_color = m_ReportMgr->m_data_source.GetUserNullColor();
			GetGradientColors(null_color, top_color_main, top_color_dark, top_color_light);
			pen_top = new Gdiplus::Pen(top_color_light, 1.7f);
			pen = pen_top;
		}
		else
		{
			top_color_main = color_main;
			top_color_dark = color_dark;
			top_color_light = color_light;
			pen = pen_gdi;
		}

		Gdiplus::Brush* brush = NULL;
		Gdiplus::LinearGradientBrush* lb = NULL;
		Gdiplus::HatchBrush* hb = NULL;
		if(bAdditional && y1 == y2)
		{
			hb = new Gdiplus::HatchBrush(Gdiplus::HatchStyleWideUpwardDiagonal, Gdiplus::Color(210, 210, 210), top_color_main);
			brush = hb;
		}
		else
		{
			Gdiplus::Rect rect(pt[0].x, -pt[0].y, abs(pt[2].x-pt[0].x), abs(pt[2].y-pt[0].y));
			Gdiplus::Color colors[4] = {top_color_light, top_color_main, top_color_main, top_color_dark};
			float positions[4] = {0.0f, 0.2f, 0.6f, 1.0f};
			lb = new Gdiplus::LinearGradientBrush(rect, top_color_dark, top_color_light, Gdiplus::LinearGradientModeForwardDiagonal);
			lb->SetInterpolationColors(colors, positions, 4);
			brush = lb;
		}

		graphics->FillPolygon(brush, points, 4);
		graphics->DrawPolygon(pen, points, 4);

		if(lb) delete lb;
		if(hb) delete hb;
		if(pen_top) delete pen_top;

		HDC gdiDC = graphics->GetHDC();
		graphics->ReleaseHDC(gdiDC);
	}
	else
	{
		if(m_ReportMgr->m_data_source.GetUseUserNullColor() && y1 == y2)
		{
			COLORREF null_color = m_ReportMgr->m_data_source.GetUserNullColor();
			CBrush br, *old_br;
			br.CreateSolidBrush(null_color);
			old_br = pDC->SelectObject(&br);

			pDC->Polygon(&pt[0], 4);

			pDC->SelectObject(old_br);
			br.DeleteObject();
		}
		else
		{
			pDC->Polygon(&pt[0], 4);
		}
	}

	// правая грань
	if(y1 != y2)
	{
		pt[0].x = x2;
		pt[0].y = y2;
		pt[1].x = x2+(int)(m_Volume*scale*(sin(angel)/cos(angel)));
		pt[1].y = (int)(y2+m_Volume*scale);
		pt[2].x = x2+(int)(m_Volume*scale*(sin(angel)/cos(angel)));
		pt[2].y = (int)(y1+m_Volume*scale);
		pt[3].x = x2;
		pt[3].y = y1;
		if(m_ReportMgr->m_data_source.GetUseGDIPlus())
		{
			::DPtoLP(pDC->m_hDC, pt, 4);

			Gdiplus::Point points[4];
			for(int i = 0; i < 4; i++)
			{
				points[i].X = pt[i].x;
				points[i].Y = -pt[i].y;
			}

			Gdiplus::Rect rect(pt[1].x, -pt[1].y, abs(pt[3].x-pt[1].x), abs(pt[3].y-pt[1].y));
			Gdiplus::Color colors[4] = {color_light, color_main, color_main, color_dark};
			float positions[4] = {0.0f, 0.4f, 0.8f, 1.0f};
			Gdiplus::LinearGradientBrush brush(rect, color_dark, color_light, Gdiplus::LinearGradientModeBackwardDiagonal);
			brush.SetInterpolationColors(colors, positions, 4);

			graphics->FillPolygon(&brush, points, 4);
			graphics->DrawPolygon(pen_gdi, points, 4);

			HDC gdiDC = graphics->GetHDC();
			graphics->ReleaseHDC(gdiDC);
		}
		else
			pDC->Polygon(&pt[0], 4);
	}

	if(bAdditional)
	{
		if(m_ReportMgr->m_data_source.GetUseGDIPlus())
		{
		}
		else
		{
			CBrush br, *old_br;
			br.CreateSolidBrush(RGB(210, 210, 210));
			old_br = pDC->SelectObject(&br);

			pt[0].x = x1+1;
			pt[0].y = y_min;
			pt[1].x = x1+(x2-x1)/2;
			pt[1].y = y_max;
			pt[2].x = x1;
			pt[2].y = y_max;
			pt[3].x = x1;
			pt[3].y = y_min;

			pDC->Polygon(&pt[0], 4);

			pt[0].x = x1;
			pt[0].y = y_max;
			pt[1].x = x1+(int)(m_Volume*scale*(sin(angel)/cos(angel)));
			pt[1].y = (int)(y_max+m_Volume*scale);
			pt[2].x = x1+1+(int)(m_Volume*scale*(sin(angel)/cos(angel)));
			pt[2].y = (int)(y_max+m_Volume*scale);
			pt[3].x = x1+(x2-x1)/2;
			pt[3].y = y_max;

			pDC->Polygon(&pt[0], 4);

			pDC->SelectObject(old_br);
		}
	}

	if(m_ReportMgr->m_data_source.GetUseGDIPlus())
	{
		delete pen_gdi;
		DeleteGDIPlusGraphics(&graphics, container);
	}
}

void CGistogramElement::Draw3DRectDuo(int y_bottom, int x_left, int y_top_left, int x_right, int y_top_rigth, double scale, CDC* pDC, COLORREF color_brush, bool bRight)
{
	Gdiplus::Graphics* graphics = NULL;
	Gdiplus::GraphicsContainer container;
	Gdiplus::Pen* pen_gdi = NULL;
	Gdiplus::Color color_main, color_dark, color_light;

	double angel = ((double)m_Angel)/180.0*PI;

	// передняя грань
	POINT pt[4];
	pt[0].x = x_left;
	pt[0].y = y_bottom;
	pt[1].x = x_left;
	pt[1].y = y_top_left;
	pt[2].x = x_right;
	pt[2].y = y_top_rigth;
	pt[3].x = x_right;
	pt[3].y = y_bottom;
	if(m_ReportMgr->m_data_source.GetUseGDIPlus())
	{
		graphics = CreateGDIPlusGraphics(pDC->m_hDC, container);
		GetGradientColors(color_brush, color_main, color_dark, color_light);

		// преобразование координат
		::DPtoLP(pDC->m_hDC, pt, 4);

		Gdiplus::Rect rect(pt[0].x, -pt[0].y, abs(pt[2].x-pt[0].x), abs(pt[2].y-pt[0].y));
		if(abs(pt[1].y-pt[0].y) > abs(pt[2].y-pt[0].y))
			rect.Height = abs(pt[1].y-pt[0].y);

		Gdiplus::Color colors[4] = {color_light, color_main, color_main, color_dark};
		float positions[4] = {0.0f, 0.3f, 0.7f, 1.0f};
		Gdiplus::LinearGradientBrush brush(rect, color_dark, color_light, Gdiplus::LinearGradientModeForwardDiagonal);
		brush.SetInterpolationColors(colors, positions, 4);

		pen_gdi = new Gdiplus::Pen(color_light, 1.7f);

		Gdiplus::Point points[4];
		for(int i = 0; i < 4; i++)
		{
			points[i].X = pt[i].x;
			points[i].Y = -pt[i].y;
		}

		graphics->FillPolygon(&brush, points, 4);
		graphics->DrawPolygon(pen_gdi, points, 4);

		HDC gdiDC = graphics->GetHDC();
		graphics->ReleaseHDC(gdiDC);
	}
	else
		pDC->Polygon(&pt[0], 4);

	// верхняя грань
	pt[0].x = x_left;
	pt[0].y = y_top_left;
	pt[1].x = x_left+(int)(m_Volume*scale*(sin(angel)/cos(angel)));
	pt[1].y = (int)(y_top_left+m_Volume*scale);
	pt[2].x = x_right+(int)(m_Volume*scale*(sin(angel)/cos(angel)));
	pt[2].y = (int)(y_top_rigth+m_Volume*scale);
	pt[3].x = x_right;
	pt[3].y = y_top_rigth;
	bool bDraw = true;
	if(pt[0].x-pt[1].x != 0 && pt[1].x-pt[2].x != 0 && pt[0].y-pt[1].y != 0 && pt[1].y-pt[2].y != 0)
	{
		double a1 = (double)(pt[0].x-pt[1].x)/(double)(pt[0].y-pt[1].y);
		double a2 = (double)(pt[1].x-pt[2].x)/(double)(pt[1].y-pt[2].y);
		if(a1 > a2)
			bDraw = false;
	}
	if(bDraw == true)
	{
		if(m_ReportMgr->m_data_source.GetUseGDIPlus())
		{
			::DPtoLP(pDC->m_hDC, pt, 4);

			Gdiplus::Point points[4];
			for(int i = 0; i < 4; i++)
			{
				points[i].X = pt[i].x;
				points[i].Y = -pt[i].y;
			}

			Gdiplus::Rect rect(pt[0].x, -pt[0].y, abs(pt[2].x-pt[0].x), abs(pt[2].y-pt[0].y));
			Gdiplus::Color colors[4] = {color_light, color_main, color_main, color_dark};
			float positions[4] = {0.0f, 0.2f, 0.6f, 1.0f};
			Gdiplus::LinearGradientBrush brush(rect, color_dark, color_light, Gdiplus::LinearGradientModeForwardDiagonal);
			brush.SetInterpolationColors(colors, positions, 4);

			graphics->FillPolygon(&brush, points, 4);
			graphics->DrawPolygon(pen_gdi, points, 4);

			HDC gdiDC = graphics->GetHDC();
			graphics->ReleaseHDC(gdiDC);
		}
		else
			pDC->Polygon(&pt[0], 4);
	}

	// правая грань
	if(bRight == true)
	{
		pt[0].x = x_right;
		pt[0].y = y_top_rigth;
		pt[1].x = x_right+(int)(m_Volume*scale*(sin(angel)/cos(angel)));
		pt[1].y = (int)(y_top_rigth+m_Volume*scale);
		pt[2].x = x_right+(int)(m_Volume*scale*(sin(angel)/cos(angel)));
		pt[2].y = (int)(y_bottom+m_Volume*scale);
		pt[3].x = x_right;
		pt[3].y = y_bottom;
		if(m_ReportMgr->m_data_source.GetUseGDIPlus())
		{
			::DPtoLP(pDC->m_hDC, pt, 4);

			Gdiplus::Point points[4];
			for(int i = 0; i < 4; i++)
			{
				points[i].X = pt[i].x;
				points[i].Y = -pt[i].y;
			}

			Gdiplus::Rect rect(pt[1].x, -pt[1].y, abs(pt[3].x-pt[1].x), abs(pt[3].y-pt[1].y));
			Gdiplus::Color colors[4] = {color_light, color_main, color_main, color_dark};
			float positions[4] = {0.0f, 0.4f, 0.8f, 1.0f};
			Gdiplus::LinearGradientBrush brush(rect, color_dark, color_light, Gdiplus::LinearGradientModeBackwardDiagonal);
			brush.SetInterpolationColors(colors, positions, 4);

			graphics->FillPolygon(&brush, points, 4);
			graphics->DrawPolygon(pen_gdi, points, 4);

			HDC gdiDC = graphics->GetHDC();
			graphics->ReleaseHDC(gdiDC);
		}
		else
			pDC->Polygon(&pt[0], 4);
	}

	if(m_ReportMgr->m_data_source.GetUseGDIPlus())
	{
		delete pen_gdi;
		DeleteGDIPlusGraphics(&graphics, container);
	}
}

void CGistogramElement::DrawRectValueString(double value, int x1, int y1, int x2, int y2, double scale, CDC *pDC, COLORREF color_brush, int y_min, int y_max, bool bLast)
{
	if(m_ReportMgr->m_data_source.GetShowValues() == false)
		return;
	bool bHorizontal = m_ReportMgr->m_data_source.GetShowValuesHorz();

	double angel = ((double)m_Angel)/180.0*PI;
	int displacement = (int)(m_Volume*scale*(sin(angel)/cos(angel)));
	int correct_y = (y_max-y_min)/200;

	LOGFONT lf = m_XLine.m_font;
	CFont font, *old_font;

	lf.lfHeight -= 40;
	lf.lfHeight = (int)(((double)lf.lfHeight)*scale);
	if (!bHorizontal)
	{
		lf.lfEscapement = -900;
		lf.lfOrientation = -900;
	}

	if (0 == font.CreatePointFontIndirect(&lf, pDC))
		TRACE_TO_FILE(APL_T("Ошибка CreatePointFontIndirect"));
	old_font = pDC->SelectObject(&font);

	CString sVal, sTmp;
	if(m_ReportMgr->m_data_source.GetTimeYFormat()) // отображать в формате времени
	{
		sVal = DoubleToTimeString(value);
		sTmp = sVal;
		sTmp.Trim(_T('0'));
		sTmp.Trim(_T(':'));
	}
	else // отображать в обычном формате
	{
		if(value >= 0.01 || value <= -0.01)
			sVal.Format(_T("%.2f"), value);
		else
			sVal.Format(_T("%f"), value);
		sVal.TrimRight(_T('0'));
		sVal.TrimRight(_T('.'));
		sTmp = sVal;
		sTmp.Trim(_T('0'));
	}
	if(!sTmp.IsEmpty())
	{
		int height = pDC->GetTextExtent(sVal).cy*3/2;
		if (bHorizontal)
			height = (int) (pDC->GetTextExtent(sVal).cx*1.05);
		while(height > 10 && height > x2-x1) // не помещается по ширине
		{
			lf.lfHeight--;
			if(lf.lfHeight <= 0) break;
			font.DeleteObject();
			if (0 == font.CreatePointFontIndirect(&lf, pDC))
				TRACE_TO_FILE(APL_T("Ошибка CreatePointFontIndirect"));
			pDC->SelectObject(&font);

			height = pDC->GetTextExtent(sVal).cy*3/2;
			if (bHorizontal)
				height = (int) (pDC->GetTextExtent(sVal).cx*1.05);
		}
		if(height >= 12)
		{
			while(true)
			{
				int width = pDC->GetTextExtent(sVal).cx+correct_y;
				if (bHorizontal)
					width = pDC->GetTextExtent(sVal).cy+correct_y;
				if( (width < y2-y1) // внутри прямоугольника
					||
					(width < y_max-y2+(int)(m_Volume*scale)+correct_y && bLast) ) // над прямоугольником
				{
					if(m_ReportMgr->m_data_source.GetUseGDIPlus())
					{
						Gdiplus::GraphicsContainer container;
						Gdiplus::Graphics* graphics = CreateGDIPlusGraphics(pDC->m_hDC, container);

						POINT pt[2];
						pt[0].x = lf.lfWidth;
						pt[0].y = lf.lfHeight;
						::LPtoDP(pDC->m_hDC, pt, 1);

						CStringW buf = lf.lfFaceName;
						Gdiplus::FontFamily fam(buf);
						int f_style = Gdiplus::FontStyleRegular;
						if(lf.lfWeight > 500) f_style |= Gdiplus::FontStyleBold;
						if(lf.lfItalic) f_style |= Gdiplus::FontStyleItalic;
						if(lf.lfUnderline) f_style |= Gdiplus::FontStyleUnderline;
						if(lf.lfStrikeOut) f_style |= Gdiplus::FontStyleStrikeout;
						Gdiplus::Font g_font(&fam, ((float)(-pt[0].y))*3/40, f_style, Gdiplus::UnitPixel);

						Gdiplus::StringFormat format;
						format.SetLineAlignment(Gdiplus::StringAlignmentCenter);
						if (bHorizontal)
							format.SetFormatFlags(Gdiplus::StringFormatFlagsNoWrap);
						else
							format.SetFormatFlags(Gdiplus::StringFormatFlagsDirectionVertical|Gdiplus::StringFormatFlagsNoWrap);

						if(width < y2-y1) // внутри прямоугольника
						{
							pt[0].x = x1;
							pt[0].y = y1;
							pt[1].x = x2;
							pt[1].y = y2;

							format.SetAlignment(Gdiplus::StringAlignmentCenter);
						}
						else if(width < y_max-y2+(int)(m_Volume*scale)+correct_y && bLast) // над прямоугольником
						{
							pt[0].x = x1+displacement;
							pt[0].y = y2+(int)(m_Volume*scale)+correct_y+5;
							pt[1].x = x2+displacement;
							if (bHorizontal)
							{
								pt[1].y = pt[0].y- (LONG) g_font.GetSize();
								if (pt[0].y-g_font.GetSize()*1.1 > y_max)
									sVal.Empty();
							}
							else
							{
								pt[1].y = 0;
								if (pt[0].y > y_max)
									sVal.Empty();
							}

							if (bHorizontal)
								format.SetAlignment(Gdiplus::StringAlignmentCenter);
							else
								format.SetAlignment(Gdiplus::StringAlignmentNear);
						}
						if (bHorizontal)
						{
							double f = g_font.GetSize()*1.1;
							pt[0].x -= (LONG) f;
							pt[0].y -= (LONG) f;
							pt[1].x -= (LONG) f;
							pt[1].y -= (LONG) f;
						}
						::DPtoLP(pDC->m_hDC, pt, 2);
						Gdiplus::RectF rect_string;
						rect_string.X = (float)(pt[0].x+g_font.GetSize()*3/8);
						rect_string.Y = (float)(-pt[0].y-g_font.GetSize()*3/8);
						rect_string.Width = (float)(abs(pt[1].x-pt[0].x)+g_font.GetSize()/2);
						rect_string.Height = (float)(abs(pt[1].y-pt[0].y)+g_font.GetSize()/2);

						Gdiplus::RectF RealStringRect;
						int codepointsFitted = 0, linesFilled = 0;
						buf = sVal;
						graphics->MeasureString(buf, buf.GetLength(), &g_font, rect_string, &format, &RealStringRect, &codepointsFitted, &linesFilled);
						if (bHorizontal)
							RealStringRect.Y += g_font.GetSize()*5/8;
						else
							RealStringRect.X -= g_font.GetSize()*5/8;
#ifdef _DEBUG
						if(codepointsFitted != sVal.GetLength())
							m_assert_count++;
						if(m_assert_count > 10)
							ASSERT(codepointsFitted == sVal.GetLength());
#endif

						if(width < y2-y1) // если внутри прямоугольника, то нарисуем эллипс вокруг строки
						{
							Gdiplus::GraphicsPath path;
							path.AddEllipse(RealStringRect);
							Gdiplus::Region region(Gdiplus::RectF((float)pt[0].x, (float)-pt[0].y, (float)abs(pt[1].x-pt[0].x), (float)abs(pt[1].y-pt[0].y)));
							region.Intersect(&path);

							Gdiplus::Color color_main, color_dark, color_light;
							GetGradientColors(color_brush, color_main, color_dark, color_light);
							while((int)color_light.GetRed()+(int)color_light.GetGreen()+(int)color_light.GetBlue() < 120*3)
							{
								Gdiplus::Color cm, cd;
								GetGradientColors(RGB(color_light.GetRed(), color_light.GetGreen(), color_light.GetBlue()), cm, cd, color_light);
							}
							Gdiplus::Color colors_ellipse[3] = {color_main, color_light, color_light};
							float positions_ellipse[3] = {0.0f, 0.1f, 1.0f};
							Gdiplus::PathGradientBrush ellipseBrush(&path);
							ellipseBrush.SetCenterColor(color_light);
							ellipseBrush.SetInterpolationColors(colors_ellipse, positions_ellipse, 3);

							// по непонятным причинам FillRegion берет размер окна вывода в логических единицах
							// и делает пересчет координат функцией ::DPtoLP.
							// в результате, правая часть области заливки не заполняется цветом
							// пришлось выкручиваться, используя код ниже
//							graphics->FillRegion(&ellipseBrush, &region);
							Gdiplus::Matrix matrix;
							graphics->GetTransform(&matrix);
							int cnt_reg_scan = region.GetRegionScansCount(&matrix);
							Gdiplus::Rect* rects_scan = new Gdiplus::Rect[cnt_reg_scan];
							region.GetRegionScans(&matrix, rects_scan, &cnt_reg_scan);
							for(int j = 0; j < cnt_reg_scan; ++j)
								graphics->FillRectangle(&ellipseBrush, rects_scan[j]);
							delete []rects_scan;
						}

						Gdiplus::SolidBrush blackBrush(Gdiplus::Color(255, 0, 0, 0));

						graphics->DrawString(buf, buf.GetLength(), &g_font, rect_string, &format, &blackBrush);

						DeleteGDIPlusGraphics(&graphics, container);
					}
					else
					{
						int x = -1;
						int y = -1;
						if(width < y2-y1) // внутри прямоугольника
						{
							if (bHorizontal)
							{
								x = x1+(x2-x1)/2-height/2;
								y = y1+(y2-y1)/2-width/2+pDC->GetTextExtent(sVal).cy/4;
							}
							else
							{
								x = x1+(x2-x1)/2-height/2+pDC->GetTextExtent(sVal).cy/4;
								y = y1+(y2-y1)/2-width/2;
							}
						}
						else if(width < y_max-y2+(int)(m_Volume*scale)+correct_y && bLast) // над прямоугольником
						{
							x = x1+displacement+(x2-x1)/2-height/2;
							y = y2+(int)(m_Volume*scale)+correct_y;
							if(y > y_max)
								sVal.Empty();
						}
						pDC->TextOut(x, y, sVal);
					}
					break;
				}
				else // куда-нибудь
				{
					if(m_ReportMgr->m_data_source.GetTimeYFormat()) // отображать в формате времени
					{
						if(sVal.Find(_T(':')) != -1) // попробуем отрезать минуты/секунды
						{
							sVal = sVal.Left(sVal.GetLength()-3);
							sTmp = sVal;
							sTmp.Trim(_T('0'));
							sTmp.Trim(_T(':'));
							if(!sTmp.IsEmpty())
								continue;
						}
					}
					else // отображать в обычном формате
					{
						if(sVal.Find(_T('.')) != -1 && sVal.Find(_T('e')) == -1 && sVal.Find(_T('E')) == -1) // попробуем отрезать дробную часть
						{
							sVal = sVal.Left(sVal.GetLength()-1);
							sVal.TrimRight(_T('0'));
							sVal.TrimRight(_T('.'));
							sTmp = sVal;
							sTmp.Trim(_T('0'));
							if(!sTmp.IsEmpty())
								continue;
						}
					}
					break;
				}
			}
		}
	}

	pDC->SelectObject(old_font);
	font.DeleteObject();
}
