/*

Miranda IM: the free IM client for Microsoft* Windows*

Copyright 2000-2008 Miranda ICQ/IM project, 
all portions of this codebase are copyrighted to the people 
listed in contributors.txt.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

Created by Pescuma, modified by Artem Shpynov

*/
#include "hdr/modern_commonheaders.h"
#include "hdr/modern_rowheight_funcs.h"
#include "hdr/modern_commonprototypes.h"
#include "hdr/modern_row.h"
#include "hdr/modern_clcpaint.h"

int g_mutex_nCalcRowHeightLock=0;
int mod_CalcRowHeight_worker(struct ClcData *dat, HWND hwnd, struct ClcContact *contact, int item);
void RowHeights_CalcRowHeights_Worker(struct ClcData *dat, HWND hwnd);
int RowHeights_GetRowHeight_worker(struct ClcData *dat, HWND hwnd, struct ClcContact *contact, int item);

/*
*
*/

ROWCELL	* gl_RowTabAccess[TC_ELEMENTSCOUNT+1]={0};	// ������, ����� ������� �������������� ������ � ��������� ��������.
ROWCELL * gl_RowRoot;

void FreeRowCell ()
{
	if (gl_RowRoot) 
		cppDeleteTree(gl_RowRoot);
}

void RowHeight_InitModernRow()
{
  gl_RowRoot=cppInitModernRow(gl_RowTabAccess);
  return;
}

SIZE GetAvatarSize(int imageWidth, int imageHeight, int maxWidth, int maxHeight)
{
  float scalefactor=0;
  SIZE sz={0};
  if (imageWidth==0 || imageHeight==0) return sz;
  if (maxWidth==0) maxWidth=maxHeight;
  scalefactor=min((float)maxWidth/imageWidth,(float)maxHeight/imageHeight); 
  sz.cx=(LONG)(imageWidth*scalefactor);
  sz.cy=(LONG)(imageHeight*scalefactor);
  return sz;
}

int RowHeight_CalcRowHeight(struct ClcData *dat, HWND hwnd, struct ClcContact *contact, int item)
{
    int res;
    if (MirandaExiting()) return 0;
    g_mutex_nCalcRowHeightLock++;
    res=mod_CalcRowHeight_worker(dat,hwnd,contact,item);
    g_mutex_nCalcRowHeightLock--;
    return res;
}
int mod_CalcRowHeight_worker(struct ClcData *dat, HWND hwnd, struct ClcContact *contact, int item)
{
  BYTE i=0;
  int res=0;
  int height=0;
  displayNameCacheEntry * pdnce; 
  BOOL hasAvatar=FALSE;
  DWORD style;
  style=GetWindowLong(hwnd,GWL_STYLE);
  pdnce=(displayNameCacheEntry*)pcli->pfnGetCacheEntry(contact->hContact);
  if (!RowHeights_Alloc(dat, item + 1))
    return -1;
  if (!pcli->hwndContactTree) return 0;
  //if (!gl_RowRoot) RowHeight_InitModernRow();
  if (dat->hWnd!=pcli->hwndContactTree || !gl_RowRoot || contact->type == CLCIT_GROUP) 
  {
    int tmp=0;
    tmp = dat->fontModernInfo[g_clcPainter.GetBasicFontID(contact)].fontHeight;
    if (dat->text_replace_smileys && dat->first_line_draw_smileys && !dat->text_resize_smileys)
      tmp = max(tmp, contact->ssText.iMaxSmileyHeight);
    if (contact->type == CLCIT_GROUP)
    {
      char * szCounts = pcli->pfnGetGroupCountsText(dat, contact);
      // Has the count?
      if(szCounts && szCounts[0])
        tmp=max(tmp,dat->fontModernInfo[contact->group->expanded?FONTID_OPENGROUPCOUNTS:FONTID_CLOSEDGROUPCOUNTS].fontHeight);
    }
    tmp=max(tmp, ICON_HEIGHT);       
    tmp=max(tmp,dat->row_min_heigh);
    tmp+=dat->row_border*2;
	if (contact->type == CLCIT_GROUP && 
      contact->group->parent->groupId==0 && 
      contact->group->parent->cl.items[0]!=contact)
		tmp+=dat->row_before_group_space;
    if (item!=-1) dat->row_heights[item] = tmp;
    return tmp;
  }
  hasAvatar=(dat->use_avatar_service && contact->avatar_data != NULL) ||(!dat->use_avatar_service && contact->avatar_pos != AVATAR_POS_DONT_HAVE);
  while (gl_RowTabAccess[i]!=NULL)
  {
    if (gl_RowTabAccess[i]->type!=TC_SPACE)
    {
      gl_RowTabAccess[i]->h=0;
      gl_RowTabAccess[i]->w=0;
      SetRect(&(gl_RowTabAccess[i]->r),0,0,0,0);
      switch (gl_RowTabAccess[i]->type)
      {
      case TC_TEXT1:
        {
          int tmp=0;
          tmp = dat->fontModernInfo[g_clcPainter.GetBasicFontID(contact)].fontHeight;
          if (dat->text_replace_smileys && dat->first_line_draw_smileys && !dat->text_resize_smileys)
            tmp = max(tmp, contact->ssText.iMaxSmileyHeight);
          if (item==-1)
          {
            //calculate text width here
            SIZE size={0};
            RECT dummyRect={0,0,1024,tmp};
            HDC hdc=CreateCompatibleDC(NULL);
            g_clcPainter.ChangeToFont(hdc,dat,g_clcPainter.GetBasicFontID(contact),NULL);
            g_clcPainter.GetTextSize(&size,hdc,dummyRect,contact->szText,contact->ssText.plText,0, dat->text_resize_smileys ? 0 : contact->ssText.iMaxSmileyHeight);
            if (contact->type==CLCIT_GROUP)
            {
              char * szCounts = pcli->pfnGetGroupCountsText(dat, contact);
              if(szCounts && strlen(szCounts)>0) 
              {
                RECT count_rc={0};
                // calc width and height
                g_clcPainter.ChangeToFont(hdc,dat,contact->group->expanded?FONTID_OPENGROUPCOUNTS:FONTID_CLOSEDGROUPCOUNTS,NULL);
                ske_DrawText(hdc,_T(" "),1,&count_rc,DT_CALCRECT | DT_NOPREFIX);
                size.cx +=count_rc.right-count_rc.left;
                count_rc.right=0;
                count_rc.left=0;
                ske_DrawTextA(hdc,szCounts,lstrlenA(szCounts),&count_rc,DT_CALCRECT);
                size.cx +=count_rc.right-count_rc.left;
                tmp = max(tmp,count_rc.bottom-count_rc.top);
              }
            }
            gl_RowTabAccess[i]->w=size.cx;
			SelectObject(hdc,GetStockObject(DEFAULT_GUI_FONT));
			ske_ResetTextEffect(hdc);
			mod_DeleteDC(hdc);
          }
          gl_RowTabAccess[i]->h=tmp;

          break;
        }
      case TC_TEXT2:
        {
          int tmp=0;
		  HANDLE hContact=pdnce->m_cache_hContact;
          if (dat->second_line_show && pdnce->szSecondLineText && pdnce->szSecondLineText[0] )
          {
            tmp = dat->fontModernInfo[FONTID_SECONDLINE].fontHeight;
            if (dat->text_replace_smileys && dat->second_line_draw_smileys && !dat->text_resize_smileys)
              tmp = max(tmp, pdnce->ssSecondLine.iMaxSmileyHeight);
            if (item==-1)
            {
              //calculate text width here

              SIZE size={0};
              RECT dummyRect={0,0,1024,tmp};
              HDC hdc=CreateCompatibleDC(NULL);
              g_clcPainter.ChangeToFont(hdc,dat,FONTID_SECONDLINE,NULL);
              g_clcPainter.GetTextSize(&size,hdc,dummyRect,pdnce->szSecondLineText,pdnce->ssSecondLine.plText,0, dat->text_resize_smileys ? 0 : pdnce->ssSecondLine.iMaxSmileyHeight);
              gl_RowTabAccess[i]->w=size.cx;
              SelectObject(hdc,GetStockObject(DEFAULT_GUI_FONT));
			  ske_ResetTextEffect(hdc);
			  mod_DeleteDC(hdc);
            }
          }
          gl_RowTabAccess[i]->h=tmp;
          break;
        }
      case TC_TEXT3:
        {
          int tmp=0;
		  HANDLE hContact=pdnce->m_cache_hContact;
          if (dat->third_line_show && pdnce->szThirdLineText && pdnce->szThirdLineText[0])
          {
            tmp = dat->fontModernInfo[FONTID_THIRDLINE].fontHeight;
            if (dat->text_replace_smileys && dat->third_line_draw_smileys && !dat->text_resize_smileys)
              tmp = max(tmp, pdnce->ssThirdLine.iMaxSmileyHeight);
            if (item==-1)
            {
              //calculate text width here
              SIZE size={0};
              RECT dummyRect={0,0,1024,tmp};
              HDC hdc=CreateCompatibleDC(NULL);
              g_clcPainter.ChangeToFont(hdc,dat,FONTID_THIRDLINE,NULL);
              g_clcPainter.GetTextSize(&size,hdc,dummyRect,pdnce->szThirdLineText,pdnce->ssThirdLine.plText,0, dat->text_resize_smileys ? 0 : pdnce->ssThirdLine.iMaxSmileyHeight);
              gl_RowTabAccess[i]->w=size.cx;
              SelectObject(hdc,GetStockObject(DEFAULT_GUI_FONT));
			  ske_ResetTextEffect(hdc);
			  mod_DeleteDC(hdc);
            }
          }
          gl_RowTabAccess[i]->h=tmp;			    
          break;
        }
      case TC_STATUS:
        {
          if (
            (contact->type == CLCIT_GROUP && !dat->row_hide_group_icon)||
            (	contact->type == CLCIT_CONTACT && contact->iImage != -1 &&
            !(dat->icon_hide_on_avatar && dat->avatars_show	&& (hasAvatar|| (!hasAvatar && dat->icon_draw_on_avatar_space && contact->iImage != -1)) && !contact->image_is_special))
            )
          {
            gl_RowTabAccess[i]->h=ICON_HEIGHT;
            gl_RowTabAccess[i]->w=ICON_HEIGHT;
          }
          break;
        }
      case TC_AVATAR:
        {
          if (dat->avatars_show && 
            contact->type == CLCIT_CONTACT && 
            (hasAvatar || (dat->icon_hide_on_avatar && dat->icon_draw_on_avatar_space && contact->iImage != -1)	)
            )
          {
            SIZE sz={0};
            int iW=0, iH=0;
            if (dat->use_avatar_service)         
            {
              if (contact->avatar_data)
              {
                iH=contact->avatar_data->bmHeight;
                iW=contact->avatar_data->bmWidth;
              }
            }
            else if (dat->avatar_cache.nodes) 
            {
              iW = dat->avatar_cache.nodes[contact->avatar_pos].width;
              iH = dat->avatar_cache.nodes[contact->avatar_pos].height;
            }					
            sz=GetAvatarSize(iW,iH,dat->avatars_maxwidth_size,dat->avatars_maxheight_size);
            if ((sz.cx==0 || sz.cy==0) &&  dat->icon_hide_on_avatar && dat->icon_draw_on_avatar_space && contact->iImage != -1)
            { sz.cx=ICON_HEIGHT; sz.cy=ICON_HEIGHT; }

            gl_RowTabAccess[i]->h=sz.cy;
            gl_RowTabAccess[i]->w=sz.cx;
          }

          break;
        }
      case TC_EXTRA:
        {
          // Draw extra icons

          if (contact->type == CLCIT_CONTACT && 
            (!contact->isSubcontact || ModernGetSettingByte(NULL,"CLC","MetaHideExtra",SETTING_METAHIDEEXTRA_DEFAULT) == 0 && dat->extraColumnsCount > 0))
          {
            BOOL hasExtra=FALSE;
            int k;
            int width=0;
            for (k=0; k<dat->extraColumnsCount; k++)
              if (contact->iExtraImage[k]!=0xFF || contact->iWideExtraImage[k]!=0xFFFF  || !dat->MetaIgnoreEmptyExtra) 
              {
                hasExtra=TRUE;
                if (item!=-1) break;
                width+=(width>0)?dat->extraColumnSpacing:(dat->extraColumnSpacing-2);
              }
              if (hasExtra) 
              {
                gl_RowTabAccess[i]->h=ICON_HEIGHT;
                gl_RowTabAccess[i]->w=width;
              }
          }
          break;
        }
      case TC_EXTRA1:
	  case TC_EXTRA2:
	  case TC_EXTRA3:
	  case TC_EXTRA4:
	  case TC_EXTRA5:
	  case TC_EXTRA6:
	  case TC_EXTRA7:
	  case TC_EXTRA8:
	  case TC_EXTRA9:
		  {
			if (contact->type == CLCIT_CONTACT && 
				(!contact->isSubcontact || ModernGetSettingByte(NULL,"CLC","MetaHideExtra",SETTING_METAHIDEEXTRA_DEFAULT) == 0 && dat->extraColumnsCount > 0))
			{
				int eNum=gl_RowTabAccess[i]->type-TC_EXTRA1;
				if (eNum<dat->extraColumnsCount)
					if (contact->iExtraImage[eNum]!=255 || contact->iWideExtraImage[eNum]!=0xFFFF || !dat->MetaIgnoreEmptyExtra)
					{
						gl_RowTabAccess[i]->h=ICON_HEIGHT;
						gl_RowTabAccess[i]->w=ICON_HEIGHT;
					}
			}
			break;
		  }
	  case TC_TIME:
        {
          if (contact->type == CLCIT_CONTACT && dat->contact_time_show && pdnce->hTimeZone)
          {
            gl_RowTabAccess[i]->h=dat->fontModernInfo[FONTID_CONTACT_TIME].fontHeight;
            if (item==-1)
            {
              TCHAR szResult[80];

			  if (!tmi.printDateTime(pdnce->hTimeZone, _T("t"), szResult, SIZEOF(szResult), 0))
              {
                SIZE text_size={0};
                RECT rc={0};
                // Select font
                HDC hdc=CreateCompatibleDC(NULL);
                g_clcPainter.ChangeToFont(hdc,dat,FONTID_CONTACT_TIME,NULL);

                // Get text size
                text_size.cy = ske_DrawText(hdc, szResult, lstrlen(szResult), &rc, DT_CALCRECT | DT_NOPREFIX | DT_SINGLELINE);
                SelectObject(hdc,GetStockObject(DEFAULT_GUI_FONT));
				ske_ResetTextEffect(hdc);
				mod_DeleteDC(hdc);
                text_size.cx = rc.right - rc.left;
                gl_RowTabAccess[i]->w=text_size.cx;

              }
            }
          }				  
          break;
        }
      }
    }
    i++;
  }

  height=cppCalculateRowHeight(gl_RowRoot);
  height+=dat->row_border*2;
  height=max(height,dat->row_min_heigh);
  if (item!=-1) dat->row_heights[item] = height;
  return height;
}




/*
*
*/




BOOL RowHeights_Initialize(struct	ClcData	*dat)
{
  dat->max_row_height	=	0;
  dat->row_heights_size	=	0;
  dat->row_heights_allocated = 0;
  dat->row_heights = NULL;

  return TRUE;
}

void RowHeights_Free(struct ClcData *dat)
{
  if (dat->row_heights != NULL) 
  {
    free(dat->row_heights);
    dat->row_heights = NULL;
  }

  dat->row_heights_allocated = 0;
  dat->row_heights_size = 0;
}

void RowHeights_Clear(struct ClcData *dat)
{
  dat->row_heights_size = 0;
}


BOOL RowHeights_Alloc(struct ClcData *dat, int size)
{
  if (size > dat->row_heights_size)
  {
    if (size > dat->row_heights_allocated)
    {
      int size_grow = size;

      size_grow += 100 - (size_grow % 100);

      if (dat->row_heights != NULL)
      {
        int *tmp = (int *) realloc((void *)dat->row_heights, sizeof(int) * size_grow);

        if (tmp == NULL)
        {
          TRACE("Out of memory: realloc returned NULL (RowHeights_Alloc)");
          RowHeights_Free(dat);
          return FALSE;
        }

        dat->row_heights = tmp;
        memset(dat->row_heights+(dat->row_heights_allocated),0,sizeof(int) * (size_grow-dat->row_heights_allocated));
      }
      else
      {
        dat->row_heights = (int *) malloc(sizeof(int) * size_grow);

        if (dat->row_heights == NULL)
        {
          TRACE("Out of memory: alloc returned NULL (RowHeights_Alloc)");
          RowHeights_Free(dat);
          return FALSE;
        }
        memset(dat->row_heights,0,sizeof(int) * size_grow);
      }

      dat->row_heights_allocated = size_grow;
    }

    dat->row_heights_size = size;
  }

  return TRUE;
}


// Calc and store max row height
int RowHeights_GetMaxRowHeight(struct ClcData *dat, HWND hwnd)
{
  int max_height = 0, i, tmp;
  DWORD style=GetWindowLong(hwnd,GWL_STYLE);
  
  if (!dat->text_ignore_size_for_row_height)
  {
    int contact_fonts[] = {FONTID_CONTACTS, FONTID_INVIS, FONTID_OFFLINE, FONTID_NOTONLIST, FONTID_OFFINVIS, 
      FONTID_AWAY,FONTID_DND, FONTID_NA, FONTID_OCCUPIED, FONTID_CHAT, FONTID_INVISIBLE, 
      FONTID_PHONE, FONTID_LUNCH};
    int other_fonts[] = {FONTID_OPENGROUPS, FONTID_OPENGROUPCOUNTS,FONTID_CLOSEDGROUPS, FONTID_CLOSEDGROUPCOUNTS, FONTID_DIVIDERS, FONTID_CONTACT_TIME};

    // Get contact font size
    tmp = 0;
    for (i = 0 ; i < MAX_REGS(contact_fonts) ; i++)
    {
      if (tmp < dat->fontModernInfo[contact_fonts[i]].fontHeight)
        tmp = dat->fontModernInfo[contact_fonts[i]].fontHeight;
    }
    if (dat->text_replace_smileys && dat->first_line_draw_smileys && !dat->text_resize_smileys)
    {
      tmp = max(tmp, dat->text_smiley_height);
    }
    max_height += tmp; 

    if (dat->second_line_show)
    {
      tmp = dat->fontModernInfo[FONTID_SECONDLINE].fontHeight;
      if (dat->text_replace_smileys && dat->second_line_draw_smileys && !dat->text_resize_smileys)
      {
        tmp = max(tmp, dat->text_smiley_height);
      }
      max_height += dat->second_line_top_space + tmp; 
    }

    if (dat->third_line_show)
    {
      tmp = dat->fontModernInfo[FONTID_THIRDLINE].fontHeight;
      if (dat->text_replace_smileys && dat->third_line_draw_smileys && !dat->text_resize_smileys)
      {
        tmp = max(tmp, dat->text_smiley_height);
      }
      max_height += dat->third_line_top_space + tmp; 
    }

    // Get other font sizes
    for (i = 0 ; i < MAX_REGS(other_fonts) ; i++)
    {
      if (max_height < dat->fontModernInfo[other_fonts[i]].fontHeight)
        max_height = dat->fontModernInfo[other_fonts[i]].fontHeight;
    }
  }

  // Avatar size
  if (dat->avatars_show && !dat->avatars_ignore_size_for_row_height)
  {
    max_height = max(max_height, dat->avatars_maxheight_size);
  }

  // Checkbox size
  if (style&CLS_CHECKBOXES || style&CLS_GROUPCHECKBOXES)
  {
    max_height = max(max_height, dat->checkboxSize);
  }

  // Icon size
  if (!dat->icon_ignore_size_for_row_height)
  {
    max_height = max(max_height, ICON_HEIGHT);
  }

  max_height += 2 * dat->row_border;

  // Min size
  max_height = max(max_height, dat->row_min_heigh);

  dat->max_row_height = max_height;
  
  return max_height;
}


// Calc and store row height for all items in the list
void RowHeights_CalcRowHeights(struct ClcData *dat, HWND hwnd)
{
    if (MirandaExiting()) return;
    g_mutex_nCalcRowHeightLock++;
    RowHeights_CalcRowHeights_Worker(dat, hwnd);
    g_mutex_nCalcRowHeightLock--;
}

void RowHeights_CalcRowHeights_Worker(struct ClcData *dat, HWND hwnd)
{
  int indent, subident, subindex, line_num;
  struct ClcContact *Drawing;
  struct ClcGroup *group;
  
  if (MirandaExiting()) return;
  
  // Draw lines
  group=&dat->list;
  group->scanIndex=0;
  indent=0;
  subindex=-1;
  line_num = -1;

  RowHeights_Clear(dat);

  while(TRUE)
  {
    if (subindex==-1)
    {
      if (group->scanIndex==group->cl.count) 
      {
        group=group->parent;
        indent--;
        if(group==NULL) break;	// Finished list
        group->scanIndex++;
        continue;
      }

      // Get item to draw
      Drawing = group->cl.items[group->scanIndex];
      subident = 0;
    }
    else
    {
      // Get item to draw
      Drawing = &(group->cl.items[group->scanIndex]->subcontacts[subindex]);
      subident = dat->subIndent;
    }

    line_num++;

    // Calc row height
    if (!gl_RowRoot) RowHeights_GetRowHeight(dat, hwnd, Drawing, line_num);
    else RowHeight_CalcRowHeight(dat, hwnd, Drawing, line_num);

    //increment by subcontacts
    if (group->cl.items[group->scanIndex]->subcontacts!=NULL && group->cl.items[group->scanIndex]->type!=CLCIT_GROUP)
    {
      if (group->cl.items[group->scanIndex]->SubExpanded && dat->expandMeta)
      {
        if (subindex<group->cl.items[group->scanIndex]->SubAllocated-1)
        {
          subindex++;
        }
        else
        {
          subindex=-1;
        }
      }
    }

    if(subindex==-1)
    {
      if(group->cl.items[group->scanIndex]->type==CLCIT_GROUP && group->cl.items[group->scanIndex]->group->expanded) 
      {
        group=group->cl.items[group->scanIndex]->group;
        indent++;
        group->scanIndex=0;
        subindex=-1;
        continue;
      }
      group->scanIndex++;
    }
  }
  
}


// Calc and store row height
int RowHeights_GetRowHeight(struct ClcData *dat, HWND hwnd, struct ClcContact *contact, int item)
{
    int res;
    if (MirandaExiting()) return 0;
    g_mutex_nCalcRowHeightLock++;
    res=RowHeights_GetRowHeight_worker(dat, hwnd, contact, item);
    g_mutex_nCalcRowHeightLock--;
    return res;
}

int RowHeights_GetRowHeight_worker(struct ClcData *dat, HWND hwnd, struct ClcContact *contact, int item)
{
  int height = 0;
  if (MirandaExiting()) return 0;
  if (gl_RowRoot)
    return RowHeight_CalcRowHeight(dat, hwnd, contact, item);
  else
  {
	displayNameCacheEntry *pdnce=(displayNameCacheEntry *)pcli->pfnGetCacheEntry(contact->hContact);
    DWORD style=GetWindowLong(hwnd,GWL_STYLE);
    //TODO replace futher code with new rowheight definition
    int tmp;
	BOOL selected=((item==dat->selection) && (dat->hwndRenameEdit!=NULL || dat->showSelAlways || dat->exStyle&CLS_EX_SHOWSELALWAYS || g_clcPainter.IsForegroundWindow(hwnd)) && contact->type!=CLCIT_DIVIDER);
	BOOL hottrack=((item==dat->iHotTrack) && (dat->hwndRenameEdit!=NULL || dat->showSelAlways || dat->exStyle&CLS_EX_SHOWSELALWAYS || g_clcPainter.IsForegroundWindow(hwnd)) && contact->type!=CLCIT_DIVIDER);
	BOOL minimalistic=(g_clcPainter.CheckMiniMode(dat,selected,hottrack));
    if (!RowHeights_Alloc(dat, item + 1))
      return -1;

    if (dat->row_variable_height)
    {
      if (!dat->text_ignore_size_for_row_height)
      {
		HANDLE hContact=pdnce->m_cache_hContact;	 
        tmp = dat->fontModernInfo[g_clcPainter.GetBasicFontID(contact)].fontHeight;
        if (dat->text_replace_smileys && dat->first_line_draw_smileys && !dat->text_resize_smileys)
        {
          tmp = max(tmp, contact->ssText.iMaxSmileyHeight);
        }
        height += tmp;

        if (!minimalistic && dat->second_line_show && pdnce->szSecondLineText && pdnce->szSecondLineText[0])
        {
          tmp = dat->fontModernInfo[FONTID_SECONDLINE].fontHeight;
          if (dat->text_replace_smileys && dat->second_line_draw_smileys && !dat->text_resize_smileys)
          {
            tmp = max(tmp, pdnce->ssSecondLine.iMaxSmileyHeight);
          }
          height += dat->second_line_top_space + tmp;
        }

        if (!minimalistic && dat->third_line_show && pdnce->szThirdLineText && pdnce->szThirdLineText[0])
        {
          tmp = dat->fontModernInfo[FONTID_THIRDLINE].fontHeight;
          if (dat->text_replace_smileys && dat->third_line_draw_smileys && !dat->text_resize_smileys)
          {
            tmp = max(tmp, pdnce->ssThirdLine.iMaxSmileyHeight);
          }
          height += dat->third_line_top_space + tmp;
        }
      }

      // Avatar size
      if (dat->avatars_show && !dat->avatars_ignore_size_for_row_height && 
        contact->type == CLCIT_CONTACT && 
        (
        (dat->use_avatar_service && contact->avatar_data != NULL) ||
        (!dat->use_avatar_service && contact->avatar_pos != AVATAR_POS_DONT_HAVE)
        ) && !minimalistic )
      {
        height = max(height, dat->avatars_maxheight_size);
      }

      // Checkbox size
      if((style&CLS_CHECKBOXES && contact->type==CLCIT_CONTACT) ||
        (style&CLS_GROUPCHECKBOXES && contact->type==CLCIT_GROUP) ||
        (contact->type==CLCIT_INFO && contact->flags&CLCIIF_CHECKBOX))
      {
        height = max(height, dat->checkboxSize);
      }

      // Icon size
      if (!dat->icon_ignore_size_for_row_height)
      {
        if (contact->type == CLCIT_GROUP 
          || (contact->type == CLCIT_CONTACT && contact->iImage != -1 
          && !(dat->icon_hide_on_avatar && dat->avatars_show
          && ( (dat->use_avatar_service && contact->avatar_data != NULL) ||
          (!dat->use_avatar_service && contact->avatar_pos != AVATAR_POS_DONT_HAVE)
          )
          && !contact->image_is_special)))
        {
          height = max(height, ICON_HEIGHT);
        }
      }

      height += 2 * dat->row_border;

      // Min size
      height = max(height, dat->row_min_heigh);
    }
    else
    {
      height = dat->max_row_height;
    }

    dat->row_heights[item] = height;

    return height;
  }
}


// Calc item top Y (using stored data)
int cliGetRowTopY(struct ClcData *dat, int item)
{
  int i;
  int y = 0;
  if (item >= dat->row_heights_size)
    return cliGetRowBottomY(dat,item-1);

  for (i = 0 ; i < item ; i++)
  {
    y += dat->row_heights[i];
  }

  return y;
}


// Calc item bottom Y (using stored data)
int cliGetRowBottomY(struct ClcData *dat, int item)
{
  int i;
  int y = 0;

  if (item >= dat->row_heights_size)
    return -1;

  for (i = 0 ; i <= item ; i++)
  {
    y += dat->row_heights[i];
  }

  return y;
}


// Calc total height of rows (using stored data)
int cliGetRowTotalHeight(struct ClcData *dat)
{
  int i;
  int y = 0;

  for (i = 0 ; i < dat->row_heights_size ; i++)
  {
    y += dat->row_heights[i];
  }

  return y;
}

// Return the line that pos_y is at or -1 (using stored data)
int cliRowHitTest(struct ClcData *dat, int pos_y)
{
  int i;
  int y = 0;

  if (pos_y < 0)
    return -1;

  for (i = 0 ; i < dat->row_heights_size ; i++)
  {
    y += dat->row_heights[i];

    if (pos_y < y)
      return i;
  }

  return -1;
}

int cliGetRowHeight(struct ClcData *dat, int item)
{	
  if ( item >= dat->row_heights_size || item <0 )
    return dat->max_row_height;
  return dat->row_heights[ item ];
}