/////////////////////////////////////////////////////////////////////////////
// Name:        No names yet.
// Purpose:     Contrib. demo
// Author:      Aleksandras Gluchovas (email: alex@soften.ktu.lt)
// Modified by:
// Created:     2000/02/18
// RCS-ID:      $Id: outlctrl.cpp,v 1.2 2001/04/21 11:48:18 georgetasker Exp $
// Copyright:   (c) PM-Soft
// RCS-ID:      $Id: outlctrl.cpp,v 1.2 2001/04/21 11:48:18 georgetasker Exp $
/////////////////////////////////////////////////////////////////////////////
   
#ifdef __GNUG__
#pragma implementation "outlctrl.cpp"
#pragma interface "outlctrl.h"
#endif

// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
#pragma hdrstop
#endif

#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif

#include "wx/wspp/outlctrl.h"
#include "wx/debug.h"
#include "wx/image.h"

/***** Implementation for class wxTextParagraph *****/

wxTextParagraph::wxTextParagraph()

 : mAlign(wxRIGHT),
   mIsVertical(FALSE),
   mWordWrap(TRUE),
   mpDc(NULL),
   mpCachedVertBmp(NULL)
{}

wxTextParagraph::wxTextParagraph(const wxString& text)

 : mAlign(wxRIGHT),
   mIsVertical(TRUE),
   mText (text),
   mWordWrap(TRUE),
   mpDc(NULL),
   mpCachedVertBmp(NULL)
{}

wxTextParagraph::~wxTextParagraph()
{
 WX_CLEAR_ARRAY(mLines);
}

void wxTextParagraph::SetWordWrap(bool on)
{
 mWordWrap = on;
}

void wxTextParagraph::SetAlignment(int align)
{
 mAlign = align;
}

void wxTextParagraph::SetVertical(bool isVertical)
{
 mIsVertical = isVertical;
}

bool wxTextParagraph::IsVertical()
{
 return mIsVertical;
}

void wxTextParagraph::SetText(const wxString& text)
{
 mText = text;
}

void wxTextParagraph::SetDc(wxDC* pDc)
{
 mpDc = pDc;
}

void wxTextParagraph::LayoutText(int outterWidth)
{
 WX_CLEAR_ARRAY(mLines);

 if (mWordWrap == FALSE)
 {
  // simple... single line fits all text

  wxParagraphLineInfo* pPar = new wxParagraphLineInfo();

  pPar->mText = mText;
  int width = 0, height = 0;
  GetStringDim(mText, &width, &height);

  pPar->mSize.x  = width;
  pPar->mSize.y = height;

  mPrefSize = pPar->mSize;

  mLines.Add(pPar);

  return;
 }

 // get words (no word-breaking algs., simple space/tab separtion)

 wxStringList words;
 GetWords(mText, words);

 // do line-breaking

 wxParagraphLineInfo* pPar = NULL;

 int curWidth = 0, curHeight = 0;
 int spaceWidth = GetSpaceCharWidth();
 size_t wordCount = words.GetCount();;

 for(size_t wordIdx = 0; wordIdx < wordCount; ++wordIdx)
 {
  if (pPar == NULL) 
  {
   pPar = new wxParagraphLineInfo();
   curWidth  = 0;
   curHeight = 0;
  }

  wxString word = words[wordIdx];

  int width = 0, height = 0;
  GetStringDim(word, &width, &height);

  if (height > curHeight) curHeight = height;

  if (pPar->mText.Length() != 0) width += spaceWidth;

  if (curWidth + width <= outterWidth || 
    pPar->mText.Length() == 0  // should be at least one word per-line
    )
  {
   if (pPar->mText.Length() > 0) pPar->mText += ' ';

   pPar->mText += word;

   curWidth += width;
  }
  else
  {
   pPar->mSize.x  = curWidth;
   pPar->mSize.y = curHeight;
   mLines.Add(pPar);
   pPar = NULL; // start a new line (if any)

   --wordIdx; // keep crunching the same word which
        // has got passed eol
  }

 } // end of for(...)

 if (pPar) // last line
 {
  pPar->mSize.x = curWidth;
  pPar->mSize.y = curHeight;
  mLines.Add(pPar);
 }

 // calculate maximal extent of the constructed line-list

 mPrefSize.x = mPrefSize.y = 0;

 for(size_t i = 0; i != mLines.Count(); ++i)
 {
  wxParagraphLineInfo& line = *mLines[i];

  if (mPrefSize.x < line.mSize.x)

   mPrefSize.x = line.mSize.x;

  mPrefSize.y += line.mSize.y;
 }

 if (IsVertical())
 {
  int tmp = mPrefSize.x;
  mPrefSize.x = mPrefSize.y;
  mPrefSize.y = tmp;
 }
}

wxSize& wxTextParagraph::GetPreferedSize()
{
 return mPrefSize;
}

void wxTextParagraph::DrawText(wxDC& dc, const wxRect& rect)
{
 int y = rect.y;

 if (IsVertical())
 {
  // should the vert.-image cach be invalidated?

  if (mpCachedVertBmp == NULL        ||
    mCachedText != mText   || 
    (mPrefSize.x ==mCachedPrefSize.x &&
      mPrefSize.y ==mCachedPrefSize.y)
    )
  {
   if (mpCachedVertBmp) delete mpCachedVertBmp;
   mpCachedVertBmp = NULL;

   mCachedPrefSize = mPrefSize;
   mCachedText = mText;

   wxMemoryDC memDc;
   wxBitmap horizBmp((int)mPrefSize.y, (int)mPrefSize.x);
   memDc.SelectObject(horizBmp);

   memDc.SetBackgroundMode(wxTRANSPARENT); 

   wxBrush br(dc.GetTextBackground(), wxSOLID);
   memDc.SetBrush(br);
   memDc.SetPen(*wxTRANSPARENT_PEN);
   memDc.SetTextForeground(dc.GetTextForeground());
   memDc.SetFont(dc.GetFont());

   memDc.DrawRectangle(0,0, mPrefSize.y + 1, mPrefSize.x + 1);

   y = 0;
   int x = 0;

   for(size_t i = 0; i != mLines.Count(); ++i)
   {
    wxParagraphLineInfo& line = *mLines[i];
    x = 0;

    if (mAlign == wxCENTER)

     x = wxGetCenterPos(rect.width, line.mSize.x);
    else
    if (mAlign == wxRIGHT)

     x = rect.x + rect.width - line.mSize.x;

    memDc.DrawText(line.mText, x,y);

    y += line.mSize.y;
   }

   wxImage horizImg(horizBmp);
   wxImage vertImg((int)mPrefSize.x, (int)mPrefSize.y);
   unsigned char* srcData  = horizImg.GetData();
   unsigned char* destData = vertImg.GetData();
   int srcHeight = horizImg.GetHeight();
   int srcWidth  = horizImg.GetWidth();
   int destHeight = vertImg.GetHeight();
   int destWidth  = vertImg.GetWidth();

   // do rotation of image

   for(int srcX = 0; srcX != srcWidth; ++ srcX)

    for(int srcY = 0; srcY != srcHeight; ++srcY)
    {
     unsigned char* src  = srcData  + srcX * 3 + (srcY * srcWidth) * 3;

     int destX = srcY; // spin vertically
     int destY = srcX; // -/-

     destY = destHeight - destY - 1; // spin vert. alignment

     unsigned char* dest = destData + destX * 3 + (destY * destWidth) * 3;

     *dest++ = *src++; // R
     *dest++ = *src++; // G
     *dest++ = *src++; // B
    }

   mpCachedVertBmp = new wxBitmap(vertImg.ConvertToBitmap());

   dc.DrawBitmap(horizBmp, 0,0, FALSE);
  }

  //dc.DrawBitmap(*mpCachedVertBmp, 0,0, FALSE);
  dc.DrawBitmap(*mpCachedVertBmp, rect.x, rect.y, FALSE);
 }
 else
 {
  dc.SetBackgroundMode(wxTRANSPARENT); 

  for(size_t i = 0; i != mLines.Count(); ++i)
  {
   wxParagraphLineInfo& line = *mLines[i];
   int x = rect.x;

   if (mAlign == wxCENTER)
   
    x += wxGetCenterPos(rect.width, line.mSize.x);
   else
   if (mAlign == wxRIGHT)

    x = rect.x + rect.width - line.mSize.x;

   dc.DrawText(line.mText, x,y);

   y += line.mSize.y;
  }
 }
}

/*** protected methods ***/

void wxTextParagraph::GetWords(const wxString& text, wxStringList& lst)
{
 size_t cur = 0, len = text.Length();

 while(cur < len)
 {
  // skip leading whitespace

  while(cur < len && 
      (text[cur] == ' ' || text[cur] == '\t')) ++cur;

  size_t start = cur;

  while(cur < len &&
      (text[cur] != ' ' && text[cur] != '\t' )) ++cur;

  if (start != cur)
  
   lst.Add(wxString(text, start, cur - start));
 }
}

void wxTextParagraph::GetStringDim(const wxString& text, int* w, int* h)
{
 wxASSERT(mpDc != NULL); // DBG::

 long width = 0, height = 0;
 mpDc->GetTextExtent(text, &width, &height);

 *w = (int)width;
 *h = (int)height;
}

int wxTextParagraph::GetSpaceCharWidth()
{
 wxASSERT(mpDc != NULL); // DBG::

 long w = 0, h = 0;
 mpDc->GetTextExtent(" ", &w, &h);

 return w;
}

/*** couple of global layouting helpers ***/

int wxGetCenterPos(int outterWidth, int innerWidth)
{
 return (outterWidth - innerWidth) / 2;
}
   
int wxGetAlignedPos(long align, int outterWidth, int innerWidth)
{
 switch (align)
 {
  case wxLEFT   : return 0;
  case wxRIGHT  : return outterWidth - innerWidth;
  case wxCENTER : return (outterWidth - innerWidth) / 2;
  default : 
    wxFAIL_MSG("invalid alignment type"); // DBG::
    return 0;
 }
}

int wxGetHorizCenterPos(const wxSize& outterSize, const wxSize& innerSize)
{
 return (outterSize.x - innerSize.x) / 2; 
}

int wxGetVertCenterPos(const wxSize& outterSize, const wxSize& innerSize)
{
 return (outterSize.y - innerSize.y) / 2; 
}

void wxGetMutalCenter(int* pos1, int width1, int* pos2, int width2)
{
 if (width1 > width2)
 {
  *pos1 = 0;
  *pos2 = wxGetCenterPos(width1, width2);
 }
 else
 {
  *pos2 = 0;
  *pos1 = wxGetCenterPos(width2, width1);
 }
}

void DrawThin3DOutline(wxDC& dc, wxRect rect, bool isPressed)
{
 if (!isPressed)
 
  dc.SetPen(*wxLIGHT_GREY_PEN);
 else
  dc.SetPen(*wxBLACK_PEN);

 dc.DrawLine(rect.x, rect.y, rect.x + rect.width - 1, rect.y);
 dc.DrawLine(rect.x, rect.y + 1, rect.x, rect.y + rect.height - 1);

 if (!isPressed)
 
  dc.SetPen(*wxBLACK_PEN);
 else
  dc.SetPen(*wxLIGHT_GREY_PEN);

 dc.DrawLine(rect.x + rect.width - 1, rect.y, 
     rect.x + rect.width - 1, rect.y + rect.height);

 dc.DrawLine(rect.x, rect.y + rect.height - 1,
     rect.x + rect.width , rect.y + rect.height - 1);
}

void DrawThick3DOutline(wxDC& dc, const wxRect& rect, bool isPressed)
{
 // FOR NOW:: code converted from newsash.cpp (another contrib. of mine)

 int x = rect.x, y = rect.y, 
  BTN_BOX_WIDTH = rect.width, BTN_BOX_HEIGHT = rect.height;

 dc.SetPen(*wxTRANSPARENT_PEN);

 dc.SetBrush(*wxLIGHT_GREY_BRUSH);

 dc.DrawRectangle(x + 1, y + 1, BTN_BOX_WIDTH - 2, BTN_BOX_HEIGHT - 2);

 // "hard-code" metafile

 if (!isPressed)

  dc.SetPen(*wxWHITE_PEN);
 else
  dc.SetPen(*wxBLACK_PEN);

 dc.DrawLine(x, y, x + BTN_BOX_WIDTH, y);
 dc.DrawLine(x, y, x, y + BTN_BOX_HEIGHT);

 dc.SetPen(*wxGREY_PEN);

 if (!isPressed)
 {
  dc.DrawLine(x + 1, y + BTN_BOX_HEIGHT - 2, 
      x + BTN_BOX_WIDTH - 1, y + BTN_BOX_HEIGHT - 2);
  
  dc.DrawLine(x + BTN_BOX_WIDTH - 2, y + 1, 
      x + BTN_BOX_WIDTH - 2, y + BTN_BOX_HEIGHT - 1);
 }
 else
 {
  dc.DrawLine(x + 1, y + 1, 
            x + BTN_BOX_WIDTH - 2, y + 1);
      
  dc.DrawLine(x + 1, y + 1,
            x + 1, y + BTN_BOX_HEIGHT - 2);
 }

 if (!isPressed)

  dc.SetPen(*wxBLACK_PEN);
 else
  dc.SetPen(*wxWHITE_PEN);

 dc.DrawLine(x, y + BTN_BOX_HEIGHT - 1,  
           x + BTN_BOX_WIDTH, y + BTN_BOX_HEIGHT - 1);

 dc.DrawLine(x + BTN_BOX_WIDTH - 1, y ,
           x + BTN_BOX_WIDTH - 1, y + BTN_BOX_HEIGHT);
}

void Fill3DInterior(wxDC& dc, const wxRect& rect, bool isPressed)
{

}

/***** Implementation for class wxOLGlyph *****/

wxOLGlyph::wxOLGlyph()

 : mpCtrl(NULL),
   mCommandId(-1)
{}

wxOLGlyph::~wxOLGlyph()
{}

void wxOLGlyph::SetId(int commandId)
{
 mCommandId = commandId;
}

int wxOLGlyph::GetId()
{
 return mCommandId;
}

void wxOLGlyph::SetControl(wxOutlookListCtrl* pCtrl)
{
 mpCtrl = pCtrl;
}

wxOutlookListCtrl& wxOLGlyph::GetControl()
{
 wxASSERT(mpCtrl); // DBG::

 return *mpCtrl;
}

void wxOLGlyph::SetBounds(const wxRect& bounds)
{
 mBounds = bounds;
}

wxRect& wxOLGlyph::GetBounds()
{
 return mBounds;
}

bool wxOLGlyph::HitTest(const wxPoint& point)
{
 return point.x >= mBounds.x && point.x < mBounds.x + mBounds.width &&
     point.y >= mBounds.y && point.y < mBounds.y + mBounds.height;
}

wxSize wxOLGlyph::CalcPreferedSize(const wxSize& outterSize)
{
 wxFAIL_MSG("wxOLGlyph::CalcPreferedSize() should be overriden in the derived classes!");

 return wxSize();
}

void wxOLGlyph::Layout()
{
 OLGlyphListT* pLst = GetChildren();

 if (!pLst) return;

 for(size_t i = 0; i != pLst->Count(); ++i)

  (*pLst)[i]->Layout();
}

void wxOLGlyph::Draw(wxDC& dc)
{
 OLGlyphListT* pLst = GetChildren();

 if (!pLst) return;

 for(size_t i = 0; i != pLst->Count(); ++i)

  (*pLst)[i]->Draw(dc);
}

void wxOLGlyph::OnTimerTick(wxDC& dc, int tickNo)
{
 OLGlyphListT* pLst = GetChildren();

 if (!pLst) return;

 for(size_t i = 0; i != pLst->Count(); ++i)

  (*pLst)[i]->OnTimerTick(dc, tickNo);
}

bool wxOLGlyph::IsAnimated()
{
 return FALSE;
}

bool wxOLGlyph::OnLeftDown(const wxPoint& pos)
{
 return DoDispatchEvent(0, pos);
}

bool wxOLGlyph::OnLeftUp(const wxPoint& pos)
{
 return DoDispatchEvent(1, pos);
}

bool wxOLGlyph::OnRightDown(const wxPoint& pos)
{
 return DoDispatchEvent(2, pos);
}

bool wxOLGlyph::OnRightUp(const wxPoint& pos)
{
 return DoDispatchEvent(3, pos);
}

bool wxOLGlyph::OnMotion(const wxPoint& pos)
{
 return DoDispatchEvent(4, pos);
}


OLGlyphListT* wxOLGlyph::GetChildren()
{
 return NULL; // no children by default
}

/*** protected methods ***/

bool wxOLGlyph::DoDispatchEvent(int evtType, const wxPoint& pos)
{
 OLGlyphListT* pLst = GetChildren();

 if (!pLst) return FALSE;

 for(size_t i = 0; i != pLst->Count(); ++i)
 {
  wxOLGlyph& obj = *(*pLst)[i];

  if (obj.HitTest(pos))
  {
   bool handled = FALSE;

   switch(evtType)
   {
    case 0  : handled = obj.OnLeftDown(pos); break;
    case 1  : handled = obj.OnLeftUp(pos);   break;
    case 2  : handled = obj.OnRightDown(pos);break;
    case 3  : handled = obj.OnRightUp(pos);  break;
    case 4  : handled = obj.OnMotion(pos);  break;
    default : break;
   }

   if (handled) return TRUE;
  }
 }

 return FALSE;
}


/***** Implementation for class wxOLImage *****/

wxOLImage::wxOLImage()

 : mCurrentFrame(-1)
{
}

wxOLImage::wxOLImage(const wxBitmap& largeBitmap)

 : mCurrentFrame(-1)
{
 mLargeBitmaps.Add(CloneBitmap(largeBitmap));
}

wxOLImage::wxOLImage(const wxBitmap& largeBitmap, const wxBitmap& smallBitmap)

 : mCurrentFrame(-1)
{
 mLargeBitmaps.Add(CloneBitmap(largeBitmap));
 mSmallBitmaps.Add(CloneBitmap(smallBitmap));
}

wxOLImage::~wxOLImage()
{
 WX_CLEAR_ARRAY(mSmallBitmaps);
 WX_CLEAR_ARRAY(mLargeBitmaps);
 WX_CLEAR_ARRAY(mJumpPoints  );
}

wxBitmap* wxOLImage::CloneBitmap(const wxBitmap& bmp)
{
 wxBitmap* pNew = new wxBitmap(bmp);

 return pNew;
}

void wxOLImage::AddSmallImage(const wxBitmap& smallBmp)
{
 mSmallBitmaps.Add(CloneBitmap(smallBmp));
}

void wxOLImage::AddLargeImage(const wxBitmap& largeBmp)
{
 mLargeBitmaps.Add(CloneBitmap(largeBmp));
}

void wxOLImage::SetupAnimation(wxPoint* jumpOffsets, int nOffsets,
           int animationRate,  int framesetDelay)
{
 mAnimationRate = animationRate;
 mFramesetDelay = framesetDelay;

 for(int i = 0; i != nOffsets; ++i, ++jumpOffsets)

  mJumpPoints.Add(new wxPoint(*jumpOffsets));
}

void wxOLImage::Draw(wxDC& dc)
{
 wxBitmap& bmp = GetCurrentBitmap();

 // TBD:: regard mJumpPoints if animation is ON

 bool transp =  bmp.GetMask() != NULL;

 dc.DrawBitmap(bmp, mBounds.x, mBounds.y, 
       transp // enalbe transparency if mask's available
    );
}

wxSize wxOLImage::CalcPreferedSize(const wxSize& outterSize)
{
 return GetBitmapSize(GetCurrentBitmap(0));
}

void wxOLImage::OnTimerTick(wxDC& dc, int tickNo)
{
 if (!IsAnimated()) return;

 OLBitmapListT& lst = GetCurrentList();

 // NOTE:: mAnimationRate should always be greater or equal to
 //        timer rate and devisable by it (possibly without reminder)

 int ticksPerFrame = mAnimationRate / GetControl().GetTimerRate();

 int ticksElapsed = tickNo - mFirstTick;

 if (mCurrentFrame == -1 || mCurrentFrame >= (int)lst.Count() )
 {
  if (mCurrentFrame >= (int)lst.Count())
  {
   if (ticksElapsed * GetControl().GetTimerRate() < mFramesetDelay)
   
    return; // stay in wait mode for to inter-frame-delay
  }

  // start frame set from the very start

  mCurrentFrame = 0;
  mFirstTick    = tickNo;
 }
 else

 if (ticksElapsed >= ticksPerFrame * mCurrentFrame)
 {
  Draw(dc); // animate current frame

  ++mCurrentFrame;

  if (mCurrentFrame >= (int)lst.Count())
  {
   // pass into wait mode for to inter-frame-delay

   mFirstTick = tickNo;
  }
 }
}

bool wxOLImage::IsWiggling()
{
 wxOLItem* pItem = GetControl().GetWigglingItem();

 return pItem && pItem->GetImage() == this;
}

bool wxOLImage::IsAnimated()
{

 return mSmallBitmaps.Count() > 1 ||
     mLargeBitmaps.Count() > 0 ||
     mJumpPoints.Count()   > 0 ||
     IsWiggling();
}

/*** protected methods ***/

wxSize wxOLImage::GetBitmapSize(const wxBitmap& bmp)
{
 return wxSize(bmp.GetWidth(), bmp.GetHeight());
}

wxBitmap& wxOLImage::GetCurrentBitmap(size_t index)
{
 wxBitmap* pBmp = NULL;
 // add some tollerance for available image types
 // (eg. if large is missing, use small, and v.v.)

 if (GetControl().GetItemType() == wxOL_SMALL_ITEM)
 {
  if (mSmallBitmaps.Count() > index)

   pBmp = mSmallBitmaps[0];
  else
  if (mLargeBitmaps.Count() > index)

   pBmp = mLargeBitmaps[0];
  else
   wxFAIL_MSG("Image is missing bitmap corresponding to currnet state!");
 }
 else
 {
  if (mLargeBitmaps.Count() > index)

   pBmp = mLargeBitmaps[0];
  else
  if (mSmallBitmaps.Count() > index)

   pBmp = mSmallBitmaps[0];
  else
   wxFAIL_MSG("Image is missing bitmap corresponding to currnet state!");
 }


 // create mask if not done yet

 if (pBmp && pBmp->GetMask() == NULL && GetControl().GetGenerateMasks())
 
  pBmp->SetMask(new wxMask(*pBmp, GetControl().GetMaskColor()));

 return *pBmp;
}

OLBitmapListT& wxOLImage::GetCurrentList()
{
 if (GetControl().GetItemType() == wxOL_SMALL_ITEM) 
  
  return mSmallBitmaps;
 else
  return mLargeBitmaps;
}

wxBitmap& wxOLImage::GetCurrentBitmap()
{
 if (mCurrentFrame == -1)

  return GetCurrentBitmap(0);
 else
  return GetCurrentBitmap(mCurrentFrame);
}

/***** Implementation for class wxOLItem *****/

wxOLItem::wxOLItem(const wxString& text, int commandId)

 : mText(text),
   mpImage(NULL),
   mIsFocused(FALSE),
   mIsPressed(FALSE),
   mTextHidden(FALSE)
{
 SetId(commandId);
}

wxOLItem::wxOLItem(wxOLImage* pImage, const wxString& text, int commandId)

 : mText(text),
   mpImage(pImage),
   mIsFocused(FALSE),
   mIsPressed(FALSE),
   mTextHidden(FALSE)
{
 SetId(commandId);
}

wxOLItem::~wxOLItem()
{
 if (mpImage) delete mpImage;
}

void wxOLItem::SetImage(wxOLImage* pImage)
{
 mpImage = pImage;
}

wxOLImage* wxOLItem::GetImage()
{
 return mpImage;
}

void wxOLItem::SetControl(wxOutlookListCtrl* pCtrl)
{
 wxOLGlyph::SetControl(pCtrl);
 mpImage->SetControl(pCtrl);
}

void wxOLItem::SetText(const wxString& text)
{
 mText = text;
}

wxString& wxOLItem::GetText()
{
 return mText;
}

void wxOLItem::HideText(bool hide)
{
 mTextHidden = hide;
}

void wxOLItem::SetIsFocused(bool focused)
{
 mIsFocused = focused;
}

bool wxOLItem::GetIsFocused()
{
 return mIsFocused;
}

void wxOLItem::SetIsPressed(bool pressed)
{
 mIsPressed = pressed;
}

bool wxOLItem::GetIsPressed()
{
 return mIsPressed;
}

wxRect& wxOLItem::GetTextBounds()
{
 return mTextRect;
}

wxSize wxOLItem::CalcPreferedSize(const wxSize& outterSize)
{
 wxOLMarginInfo& margins = GetControl().GetItemMargins();
 wxSize outter = outterSize;
 outter.x -= margins.mLeft + margins.mRight;
 outter.y -= margins.mTop  + margins.mBottom;

 // FIXME:: check situation when image and text are adjectent
 //         horizontally, this should be regared when passing
 //     length-constraint to LayouText 

 mParagraph.SetAlignment(GetControl().GetItemTextAlign());
 mParagraph.SetWordWrap(GetControl().GetWordWrap());
 mParagraph.SetText(this->GetText());
 mParagraph.SetDc(&GetControl().GetCurrentDc());
 mParagraph.LayoutText(outterSize.x);

 wxSize txtSz = mParagraph.GetPreferedSize();
 wxSize imgSz = wxSize(0,0);

 int txtX = 0;
 int txtY = 0;
 int imgX = 0;
 int imgY = 0;  
 int w = 0;
 int h = 0;

 if (mpImage)

  imgSz = mpImage->CalcPreferedSize(outterSize);

 switch(GetControl().GetItemTextAlign())
 {
  case wxOL_ALIGN_TOP : 
   {
    wxGetMutalCenter(&txtX, txtSz.x, &imgX, imgSz.x);
    txtY = 0;
    imgY = txtY + txtSz.y + margins.mTextToImageGap;
    h = imgY + imgSz.y;
    w = wxMax(txtSz.x, imgSz.x);
    break;
   }
  case wxOL_ALIGN_BOTTOM : 
   {
    wxGetMutalCenter(&txtX, txtSz.x, &imgX, imgSz.x);
    imgY = 0;
    txtY = imgY + imgSz.y + margins.mTextToImageGap;
    h = txtY + txtSz.y;
    w = wxMax(txtSz.x, imgSz.x);
    break;
   }
  case wxOL_ALIGN_LEFT : 
   {
    wxGetMutalCenter(&txtY, txtSz.y, &imgY, imgSz.y);
    int sumWidth = txtSz.x + margins.mTextToImageGap + imgSz.y;

    txtX = 0;
    imgX = txtX + txtSz.x + margins.mTextToImageGap;
    h = wxMax(txtSz.y, imgSz.y);
    w = sumWidth;
    break;
   }
  case wxOL_ALIGN_RIGHT : 
   {
    wxGetMutalCenter(&txtY, txtSz.y, &imgY, imgSz.y);
    int sumWidth = txtSz.x + margins.mTextToImageGap + imgSz.y;

    imgX = 0;
    txtX = imgX + imgSz.y + margins.mTextToImageGap;
    h = wxMax(txtSz.y, imgSz.y);
    w = sumWidth;
    break;
   }

  default : break;
 }

 // store relative positions

 if (mpImage)
 
  mpImage->SetBounds(wxRect(imgX,imgY, imgSz.x, imgSz.y));
 
 mTextRect = wxRect(txtX, txtY, txtSz.x, txtSz.y);

 w += margins.mLeft + margins.mRight;
 h += margins.mTop  + margins.mBottom;
 return wxSize(w,h);
}

void wxOLItem::Layout()
{
 // convert precalculated relative positions to absolute ones

    wxOLMarginInfo& margins = GetControl().GetItemMargins();

 wxRect imgRect = mpImage->GetBounds();
 imgRect.x += mBounds.x + margins.mLeft;
 imgRect.y += mBounds.y + margins.mTop;
 mpImage->SetBounds(imgRect);

 mTextRect.x += mBounds.x + margins.mLeft;
 mTextRect.y += mBounds.y + margins.mTop;
}

void wxOLItem::Draw(wxDC& dc)
{
 // draw in absolute positions (calculated in Layout())

 dc.SetTextForeground(GetControl().GetTextColor());

 dc.SetTextBackground(GetControl().GetBgColor());
 dc.SetBackgroundMode( wxTRANSPARENT);

 if (!mTextHidden)

  mParagraph.DrawText(dc, mTextRect);

 if (mpImage)
 {
  dc.SetBackgroundMode( wxTRANSPARENT);

  wxRect r = mpImage->GetBounds();
  r.x -= 2;
  r.width += 4;
  r.y -= 2;
  r.height += 4;

  wxBrush br(GetControl().GetBgColor(), wxSOLID);
  dc.SetBrush(br);
  dc.SetPen(*wxTRANSPARENT_PEN);

  dc.DrawRectangle(r.x, r.y, r.width+1, r.height+1);

  mpImage->Draw(dc);

  if (mIsFocused)
  {
   DrawThin3DOutline(dc, r, mIsPressed);
  }
 }
}

void wxOLItem::OnTimerTick(wxDC& dc, int tickNo)
{
 if (mpImage)

  mpImage->OnTimerTick(dc, tickNo);
}

// global helper
static bool rect_has_point(const wxRect& rect, const wxPoint& point)
{
 return point.x >= rect.x && point.x < rect.x + rect.width &&
     point.y >= rect.y && point.y < rect.y + rect.height;
}

bool wxOLItem::HitTest(const wxPoint& point)
{
 if (mpImage && rect_has_point(mpImage->GetBounds(), point))

  return TRUE;

 wxOLMarginInfo& m = GetControl().GetItemMargins();

 wxRect r= mTextRect;
 r.y -= m.mTextToImageGap;
 r.height += 2*m.mTextToImageGap;
 r.x -= m.mTextToImageGap;
 r.width += 2*m.mTextToImageGap;

 if (rect_has_point(r, point)) 

  return TRUE;

 return FALSE;
}

bool wxOLItem::OnLeftDown(const wxPoint& pos)
{
 if (mIsFocused)
 {
  mIsPressed = TRUE;
  Draw(GetControl().GetCurrentDc());
 }

 return TRUE;
}

bool wxOLItem::OnLeftUp(const  wxPoint& pos)
{
 if (mIsPressed)
 {
  mIsPressed = FALSE;
  Draw(GetControl().GetCurrentDc());

  wxCommandEvent menuEvent(wxEVT_COMMAND_MENU_SELECTED, mCommandId); 
  menuEvent.SetEventObject(mpCtrl);

  mpCtrl->ProcessEvent(menuEvent);
 }

 return TRUE;
}

bool wxOLItem::OnRightDown(const wxPoint& pos)
{
 GetControl().OnTrackItemMenu(this, pos);

 return TRUE;
}

bool wxOLItem::OnRightUp(const wxPoint& pos)
{
 return TRUE;
}

bool wxOLItem::OnMotion(const wxPoint& pos)
{
 if (mIsFocused == FALSE)
 {
  mIsFocused = TRUE;
  GetControl().CaptureEventsForGlyph(this);

  Draw(GetControl().GetCurrentDc());
 }
 else
 {
  if (HitTest(pos) == FALSE)
  {
   mIsFocused = FALSE;
   mIsPressed = FALSE;
   GetControl().ReleaseEvents(this);

   Draw(GetControl().GetCurrentDc());
  }
 }

 return TRUE;
}

/***** Implementation for class wxOLContainer *****/

wxOLContainer::wxOLContainer()

 : mCurPageTop(0)
{
}

wxOLContainer::~wxOLContainer()
{
 WX_CLEAR_ARRAY(mChildren);
}

void wxOLContainer::Add(wxOLGlyph* pItem)
{
 pItem->SetControl(&GetControl());
 mChildren.Add(pItem);
}

void wxOLContainer::Remove(wxOLGlyph* pItem)
{
 mChildren.Remove(pItem);
 delete pItem;
}

bool wxOLContainer::RemoveById(int commandId)
{
 for(size_t i = 0; i != mChildren.Count(); ++i)
 {
  if (mChildren[i]->GetId() == commandId)
  {
   delete mChildren[i];
   mChildren.Remove(i);

   return TRUE;
  }
 }

 return FALSE; // not found
}

void wxOLContainer::Scroll(int nItems)
{
 if (nItems > 0)
 {
  if (mCurPageTop + nItems >= mChildren.Count())

   nItems = mChildren.Count() - mCurPageTop - 1;

  SetPageTopItem(mCurPageTop + nItems);
 }
 else
 {
  nItems = -nItems;

  if ((int)mCurPageTop < nItems)

   SetPageTopItem(0);
  else
   SetPageTopItem(mCurPageTop - nItems);
 }
}

size_t wxOLContainer::GetPageTopItemIndex()
{
 return mCurPageTop;
}

void wxOLContainer::SetPageTopItem(size_t index)
{
 wxASSERT(index < mChildren.Count()); // DBG::

 mCurPageTop = index;
}

void wxOLContainer::RefreshNow()
{
 /*
 // OLD STUF::
 wxDC& dc = GetControl().GetCurrentDc();

 CalcPreferedSize(wxSize(mBounds.width, mBounds.height));
 Layout();
 Draw(dc);
 */

 GetControl().RefreshNow();
}

size_t wxOLContainer::GetItemsInPage()
{
 return 10000; // default impl.
}

OLGlyphListT* wxOLContainer::GetChildren()
{
 return &mChildren;
}

/*** protected methods ***/

/***** Implementation for class wxOLBoxContainer *****/

wxOLBoxContainer::wxOLBoxContainer()
{
}

wxOLBoxContainer::~wxOLBoxContainer()
{
}

wxSize wxOLBoxContainer::CalcPreferedSize(const wxSize& outterSize)
{
 // do relative-layouting here

 bool isVert = GetControl().IsVertical();
 int  align  = GetControl().GetItemAlign();

 int curPos   = 0;
 int endPos   = isVert ? outterSize.y : outterSize.x;
 mItemsInPage = 0;
 int avgHeight = 0;

 for(size_t n = 0; n != mCurPageTop; ++n)
 {
  wxOLGlyph& child = *mChildren[n];
  child.SetBounds(wxRect(10000,10000, 4,4));
 }

 for(size_t i = mCurPageTop; i < mChildren.Count() 
         ; ++i)
 {
  if (curPos <= endPos+10)
  
   ++mItemsInPage;

  wxOLGlyph& child = *mChildren[i];

  wxSize prefSz = child.CalcPreferedSize(outterSize);

  wxPoint itemPos;

  if (isVert)
  {
   itemPos = wxPoint(wxGetAlignedPos(align, outterSize.x, prefSz.x), curPos);
   curPos += prefSz.y;
   avgHeight += prefSz.y;
  }
  else 
  {
   itemPos = wxPoint(curPos, wxGetCenterPos(outterSize.y, prefSz.y));
   curPos += prefSz.x;
   avgHeight += prefSz.x;
  }

  child.SetBounds(wxRect(itemPos.x, itemPos.y, prefSz.x, prefSz.y));
 }

 return outterSize; // here, prefered size is what we're given
}

void wxOLBoxContainer::Layout()
{
 // hide items which are above view area 

 size_t i = 0;
 //for(i = 0; i < mCurPageTop && i < mChildren.Count(); ++i)
 
 // mChildren[i]->SetBounds(wxRect(-10000,-10000,1,1));

 // convert relative to absolute coords of those items
 // which are visiable

 for(i = 0; i < (size_t)mItemsInPage; ++i)
 {
  wxRect bounds = mChildren[ mCurPageTop + i ]->GetBounds();
  bounds.x += mBounds.x;
  bounds.y += mBounds.y;
  mChildren[ mCurPageTop + i ]->SetBounds(bounds);
 }

 // hide items whicha are below view area

 //for(; i + mCurPageTop < mChildren.Count(); ++i)

 // mChildren[i]->SetBounds(wxRect(-10000,-10000,1,1));

 wxOLContainer::Layout();
}

void wxOLBoxContainer::Draw(wxDC& dc)
{
 for(size_t i = 0; i < (size_t)mItemsInPage; ++i)

  mChildren[ mCurPageTop + i ]->Draw(dc);
}

size_t wxOLBoxContainer::GetItemsInPage()
{
 return mItemsInPage;
}

/***** Implementation for class wxOLDecorator *****/

wxOLDecorator::wxOLDecorator()
 : wxOLContainer(),
   mpContainer(NULL),
   mBtnSize(16,16),
   mInnerPadding(16,16),
   mBtnPadding(5,5),
   mPressedBtnNo(-1),
   mFirstTick(-1)
{
}

void wxOLDecorator::SetContainer(wxOLContainer* pContainer)
{
 if (mChildren.Count() != 0)
 
  WX_CLEAR_ARRAY(mChildren);
 
 mpContainer = pContainer;
 Add(pContainer);
}

void wxOLDecorator::Layout()
{
 wxRect inner(mInnerPadding.x, mInnerPadding.y, 
      mBounds.width  - mInnerPadding.x*2,
      mBounds.height - mInnerPadding.y*2);

 mpContainer->SetBounds(inner);

 wxOLContainer::Layout();

 switch (GetControl().GetScrollShaftAlign())
 {
  case wxOL_ALIGN_TOP : 
   {
    mFirstBtnPos  = GetCornerPos(0);
    mSecondBtnPos = GetCornerPos(1); break;
   }
  case wxOL_ALIGN_BOTTOM : 
   {
    mFirstBtnPos  = GetCornerPos(3);
    mSecondBtnPos = GetCornerPos(2); break;
   }
  case wxOL_ALIGN_LEFT : 
   {
    mFirstBtnPos  = GetCornerPos(0);
    mSecondBtnPos = GetCornerPos(3); break;
   }
  case wxOL_ALIGN_RIGHT : 
   {
    mFirstBtnPos  = GetCornerPos(1);
    mSecondBtnPos = GetCornerPos(2); break;
   }

  default : break;
 }
}

wxSize wxOLDecorator::CalcPreferedSize(const wxSize& outterSize)
{
 wxRect inner(mInnerPadding.x, mInnerPadding.y, 
      mBounds.width  - mInnerPadding.x*2,
      mBounds.height - mInnerPadding.y*2);

 return mpContainer->CalcPreferedSize(wxSize(inner.width, inner.height) );
}

void wxOLDecorator::Draw(wxDC& dc)
{
 // fill interior first

 wxBrush br(GetControl().GetBgColor(), wxSOLID);
 dc.SetBrush(br);
 dc.SetPen(*wxTRANSPARENT_PEN);

 dc.DrawRectangle(0,0, mBounds.width+1, mBounds.height+1);

 // then draw cotent 

 wxOLContainer::Draw(dc);

 // and finally buttons will draw/overdraw the content (MFC-OutBar's behaviour)

 bool isVert = GetControl().IsVertical();

 if (ButtonShown(0))
 {
  DrawButtonFrame(dc, mFirstBtnPos, mPressedBtnNo == 0);
  DrawButtonShaft(dc, mFirstBtnPos, mPressedBtnNo == 0, 
       isVert ? wxOL_ALIGN_TOP : wxOL_ALIGN_LEFT);
 }
 if (ButtonShown(1))
 {
  DrawButtonFrame(dc, mSecondBtnPos, mPressedBtnNo == 1);
  DrawButtonShaft(dc, mSecondBtnPos, mPressedBtnNo == 1, 
       isVert ? wxOL_ALIGN_BOTTOM : wxOL_ALIGN_RIGHT);
 }
}

void wxOLDecorator::DrawButtonFrame(wxDC& dc, const wxPoint& at, bool pressed)
{
 #if 0
  // FOR NOW::
  wxBrush br(wxColour(*wxRED), wxSOLID);
  dc.SetBrush(br);

  if (pressed)

   dc.DrawRectangle(at.x, at.y, mBtnSize.x, mBtnSize.y);
  else
   dc.DrawRectangle(at.x+1, at.y+1, mBtnSize.x, mBtnSize.y);
 #else
  DrawThick3DOutline(dc, wxRect(at.x, at.y, mBtnSize.x, mBtnSize.y), pressed);
 #endif
}

void wxOLDecorator::DrawButtonShaft(wxDC& dc, const wxPoint& at, bool pressed, int dir)
{
 dc.SetPen(*wxBLACK_PEN);
 int x = at.x + 4, y = at.y + 5;

 if (pressed) 
 {
  x += 2; 
  y += 2;
 }

 switch(dir)
 {
  case wxOL_ALIGN_TOP :  
   {
    dc.DrawLine(x + 3, y + 0, x + 4, y + 0);
    dc.DrawLine(x + 2, y + 1, x + 5, y + 1);
    dc.DrawLine(x + 1, y + 2, x + 6, y + 2);
    dc.DrawLine(x + 0, y + 3, x + 7, y + 3); break;

   }
  case wxOL_ALIGN_BOTTOM :  
   {
    dc.DrawLine(x + 0, y + 0, x + 7, y + 0); 
    dc.DrawLine(x + 1, y + 1, x + 6, y + 1);
    dc.DrawLine(x + 2, y + 2, x + 5, y + 2);
    dc.DrawLine(x + 3, y + 3, x + 4, y + 3); break;
   }
  case wxOL_ALIGN_RIGHT :
   {
    x += 2; y -= 1;
    dc.DrawLine(x + 0, y + 0, x + 0, y + 7);
    dc.DrawLine(x + 1, y + 1, x + 1, y + 6);
    dc.DrawLine(x + 2, y + 2, x + 2, y + 5);
    dc.DrawLine(x + 3, y + 3, x + 3, y + 4); break;
   }
  case wxOL_ALIGN_LEFT  :
   {
    x += 1; y -= 1;
    dc.DrawLine(x + 0, y + 3, x + 0, y + 4);
    dc.DrawLine(x + 1, y + 2, x + 1, y + 5);
    dc.DrawLine(x + 2, y + 1, x + 2, y + 6);
    dc.DrawLine(x + 3, y + 0, x + 3, y + 7); break;
   }
  default : break;
 }
}

void wxOLDecorator::SetInnserPadding(const wxSize& padding)
{
 mInnerPadding = padding;
}

void wxOLDecorator::SetButtonPadding(const wxSize& padding)
{
 mBtnPadding = padding;
}

void wxOLDecorator::SetButtonSize(const wxSize& btnSize)
{
 mBtnSize = btnSize;
}

bool wxOLDecorator::HitsButton(const wxPoint& atPos, const wxPoint& btnPos)
{
 return (atPos.x >= btnPos.x && atPos.x < btnPos.x + mBtnSize.x &&
    atPos.y >= btnPos.y && atPos.y < btnPos.y + mBtnSize.y);
}

int wxOLDecorator::GetHittedButtonNo(const wxPoint& atPos)
{
 if (HitsButton(atPos, mFirstBtnPos)) return 0;
 else
 if (HitsButton(atPos, mSecondBtnPos)) return 1;
 else
  return -1;
}

bool wxOLDecorator::ButtonShown(int index)
{
 int pageTop = mpContainer->GetPageTopItemIndex();

 if (index == 0) return pageTop != 0; // first button
 else
  return  mpContainer->GetItemsInPage() > 1 ;
      
}

bool wxOLDecorator::OnLeftDown(const wxPoint& pos)
{
 if (wxOLContainer::OnLeftDown(pos)) return TRUE;

 mPressedBtnNo = GetHittedButtonNo(pos);

 if (mPressedBtnNo != -1 && ButtonShown(mPressedBtnNo))
 {
  GetControl().CaptureEventsForGlyph(this);

  if (mPressedBtnNo == 0) mpContainer->Scroll(-1);
        else mpContainer->Scroll(1);

  RefreshNow();

  mFirstTick = -1;
 }
 else
  mPressedBtnNo = -1;

 return TRUE;
}

bool wxOLDecorator::OnLeftUp(const  wxPoint& pos)
{
 if (mPressedBtnNo != -1)
 {
  GetControl().ReleaseEvents(this);

  mPressedBtnNo = -1; // all buttons released

  RefreshNow();

  return TRUE;
 }
 
 return wxOLContainer::OnLeftUp(pos);
}

bool wxOLDecorator::OnRightDown(const wxPoint& pos)
{
 if (wxOLContainer::OnRightDown(pos)) return TRUE;

 GetControl().OnTrackListMenu(pos);

 return TRUE;
}

bool wxOLDecorator::OnRightUp(const wxPoint& pos)
{
 if (wxOLContainer::OnRightUp(pos)) return TRUE;

 return TRUE;
}

wxPoint wxOLDecorator::GetCornerPos(int cornerNo)
{
 wxRect inner(mBtnPadding.x, mBtnPadding.y, 
      mBounds.width  - mBtnPadding.x*2,
      mBounds.height - mBtnPadding.y*2);

 switch(cornerNo)
 {
  // top-left
  case 0 : return wxPoint(inner.x, inner.y);
  // top-right
  case 1 : return wxPoint(inner.x + inner.width - mBtnSize.x, inner.y);
  // bottom-right
  case 2 : return wxPoint(inner.x + inner.width  - mBtnSize.x, 
                          inner.y + inner.height - mBtnSize.y );
  // bottom-left
  case 3 : return wxPoint(inner.x, inner.y + inner.height - mBtnSize.y );

  default : 
    return wxPoint(); // never reached
 }
}

/***** Implementation for class wxOutlookListCtrl *****/

IMPLEMENT_CLASS(wxOutlookListCtrl, wxPanel);

BEGIN_EVENT_TABLE(wxOutlookListCtrl, wxPanel)

 EVT_PAINT(wxOutlookListCtrl::OnPaint)

 EVT_ERASE_BACKGROUND(wxOutlookListCtrl::OnEraseBackground)

 EVT_LEFT_DOWN  (wxOutlookListCtrl::OnLeftDown )
 EVT_LEFT_UP    (wxOutlookListCtrl::OnLeftUp   )
 EVT_RIGHT_DOWN (wxOutlookListCtrl::OnRightDown)
 EVT_RIGHT_UP   (wxOutlookListCtrl::OnRightUp  )
 EVT_MOTION     (wxOutlookListCtrl::OnMotion   )
 EVT_LEFT_DCLICK(wxOutlookListCtrl::OnLeftDblClick   )

 EVT_MENU(wxOutlookListCtrl::ID_ALIGN_TEXT_TOP,  wxOutlookListCtrl::OnCmdAlignTextTop)
 EVT_MENU(wxOutlookListCtrl::ID_ALIGN_TEXT_BOTTOM, wxOutlookListCtrl::OnCmdAlignTextBottom)
 EVT_MENU(wxOutlookListCtrl::ID_ALIGN_TEXT_LEFT, wxOutlookListCtrl::OnCmdAlignTextLeft)
 EVT_MENU(wxOutlookListCtrl::ID_ALIGN_TEXT_RIGHT, wxOutlookListCtrl::OnCmdAlignTextRight)
 EVT_MENU(wxOutlookListCtrl::ID_ALIGN_VERTICAL,  wxOutlookListCtrl::OnCmdAlignVertical)
 EVT_MENU(wxOutlookListCtrl::ID_ALIGN_HORIZONTAL, wxOutlookListCtrl::OnCmdAlignHorizontal)
 EVT_MENU(wxOutlookListCtrl::ID_ALIGN_ITEMS_LEFT, wxOutlookListCtrl::OnCmdAlignItemsLeft)
 EVT_MENU(wxOutlookListCtrl::ID_ALIGN_ITEMS_RIGHT, wxOutlookListCtrl::OnCmdAlignItemsRight)
 EVT_MENU(wxOutlookListCtrl::ID_ALIGN_ITEMS_CENTER, wxOutlookListCtrl::OnCmdAlignItemsCenter)
 EVT_MENU(wxOutlookListCtrl::ID_LIST_VIEW,   wxOutlookListCtrl::OnCmdListView)
 EVT_MENU(wxOutlookListCtrl::ID_ICON_VIEW,   wxOutlookListCtrl::OnCmdIconView)

 EVT_MENU(wxOutlookListCtrl::ID_RENAME_ITEM,   wxOutlookListCtrl::OnCmdRenameItem)
 EVT_MENU(wxOutlookListCtrl::ID_REMOVE_ITEM,   wxOutlookListCtrl::OnCmdRemoveItem)
 EVT_MENU(wxOutlookListCtrl::ID_WORD_WRAP_ON,  wxOutlookListCtrl::OnCmdWordWrapOn)
 EVT_MENU(wxOutlookListCtrl::ID_WORD_WRAP_OFF, wxOutlookListCtrl::OnCmdWordWrapOff)

 EVT_MENU(wxOutlookListCtrl::ID_ALIGN_SCROLLER_TOP,    wxOutlookListCtrl::OnCmdAlignScrollerTop )
 EVT_MENU(wxOutlookListCtrl::ID_ALIGN_SCROLLER_BOTTOM, wxOutlookListCtrl::OnCmdAlignScrollerBottom )
 EVT_MENU(wxOutlookListCtrl::ID_ALIGN_SCROLLER_LEFT,   wxOutlookListCtrl::OnCmdAlignScrollerLeft )
 EVT_MENU(wxOutlookListCtrl::ID_ALIGN_SCROLLER_RIGTH,  wxOutlookListCtrl::OnCmdAlignScrollerRight)

 EVT_TEXT_ENTER(wxOutlookListCtrl::ID_EDITOR_CTRL, wxOutlookListCtrl::OnTextUpdated)

END_EVENT_TABLE()

wxOutlookListCtrl::wxOutlookListCtrl(wxWindow* pWindow, wxWindowID id, 
          const wxPoint& pos, const wxSize& size, 
          long style, const wxString& name)

 : wxPanel(pWindow,id,pos,size,style,name),
   mWordWrap(TRUE),
   mGenerateMasks(TRUE),
   mMaskColor(*wxWHITE),
   mTextColor(*wxWHITE),
   mBgColor(*wxBLACK),
   mTimerRate(100),
   mTickCounter(0),
   mpBgImage(NULL),
   mpContainer(NULL),
   mpDecorator(NULL),
   mpCapturedGlyph(NULL),
   mpCurrentDc(NULL),
   mpSelectedItem(NULL),
   mpWigglingItem(NULL),
   mpEditor(NULL)
{
 // FOR NOW:: margins for large/small items have no difference

 mSmallItemMargins.mTop    = 5;
 mSmallItemMargins.mBottom = 5;
 mSmallItemMargins.mLeft   = 5;
 mSmallItemMargins.mRight  = 5;
 mSmallItemMargins.mTextToImageGap = 5;

 mLargeItemMargins.mTop    = 10;
 mLargeItemMargins.mBottom = 10;
 mLargeItemMargins.mLeft   = 5;
 mLargeItemMargins.mRight  = 5;
 mLargeItemMargins.mTextToImageGap = 4;

 ChangeStyle(style, FALSE);
}

wxOutlookListCtrl::~wxOutlookListCtrl()
{
 if (mpBgImage) delete mpBgImage;
 if (mpDecorator) delete mpDecorator;

 // do not need to delete mpContainer because it's
 // a child of mpDecorator is deleted automatically

 wxASSERT(mpCapturedGlyph == NULL); // DBG::
 wxASSERT(mpCurrentDc   == NULL); // DBG::
}

void wxOutlookListCtrl::AddItem(const wxString& text, const wxBitmap& largeBitmap, 
         int commandId)
{
 wxOLImage* pImg = new wxOLImage(largeBitmap);
 GetContainer()->Add(new wxOLItem(pImg, text, commandId));
}

void wxOutlookListCtrl::AddItem(const wxString& text, 
         const wxBitmap& largeBitmap, 
         const wxBitmap& smallBitmap, 
         int commandId)
{
 wxOLImage* pImg = new wxOLImage(largeBitmap, smallBitmap);
 GetContainer()->Add(new wxOLItem(pImg, text, commandId));
}

void wxOutlookListCtrl::AddItem(wxOLGlyph* pItem)
{
 pItem->SetControl(this);
 GetContainer()->Add(pItem);
}

void wxOutlookListCtrl::RemoveItem(wxOLGlyph* pItem)
{
 mpContainer->Remove(pItem);
}

bool wxOutlookListCtrl::RemoveItemById(int commandId)
{
 return mpContainer->RemoveById(commandId);
}

void wxOutlookListCtrl::ChangeStyle(int newWindowStyle, bool refreshNow)
{
 if (newWindowStyle & wxOL_HORIZONTAL) SetOrientation(wxOL_HORIZONTAL);
 if (newWindowStyle & wxOL_VERTICAL  ) SetOrientation(wxOL_VERTICAL  );

 if (newWindowStyle & wxOL_LARGE_ITEMS) 
 {
  SetItemType(wxOL_LARGE_ITEM);
  SetItemTextAlign(wxOL_ALIGN_BOTTOM);
  SetItemAlign(wxOL_ITEM_CENTERED);
 }
 if (newWindowStyle & wxOL_SMALL_ITEMS) 
 {
  SetItemType(wxOL_SMALL_ITEM);
  
  if (IsVertical())
  {
   SetItemAlign(wxOL_ITEM_LEFT_ALIGNED);
   SetItemTextAlign(wxOL_ALIGN_RIGHT);
  }
  else
  {
   SetItemAlign(wxOL_ITEM_CENTERED);
   SetItemTextAlign(wxOL_ALIGN_BOTTOM);
  }
 }

 if (IsVertical())
  
  SetScrollShaftAlign(wxOL_ALIGN_RIGHT);
 else
  SetScrollShaftAlign(wxOL_ALIGN_BOTTOM);

 if (refreshNow)
  
  RefreshNow();
}

void wxOutlookListCtrl::RefreshNow()
{
 wxClientDC dc(this);

 DoRefresh(dc);
}

void wxOutlookListCtrl::SetScrollShaftAlign(int align)
{
 mScrollShaftAlign = align;
}

long wxOutlookListCtrl::GetScrollShaftAlign()
{
 return mScrollShaftAlign;
}

void wxOutlookListCtrl::SetItemTextAlign(int align)
{
 mItemTextAlign = align;
}

long wxOutlookListCtrl::GetItemTextAlign()
{
 return mItemTextAlign;
}

void wxOutlookListCtrl::SetItemType(long itemType)
{
 mItemType = itemType;
}

long wxOutlookListCtrl::GetItemType()
{
 return mItemType;
}

void wxOutlookListCtrl::SetItemAlign(long align)
{
 mItemAlign = align;
}

long wxOutlookListCtrl::GetItemAlign()
{
 return mItemAlign;
}

void wxOutlookListCtrl::SetOrientation(long orient)
{
 mOrientation = orient;
}

long wxOutlookListCtrl::GetOrientation()
{
 return mOrientation;
}

bool wxOutlookListCtrl::IsVertical()
{
 return mOrientation == wxOL_VERTICAL;
}


void wxOutlookListCtrl::SetWordWrap(bool on)
{
 mWordWrap = on;
}

bool wxOutlookListCtrl::GetWordWrap()
{
 return mWordWrap;
}

void wxOutlookListCtrl::SetBgColor(const wxColour& bg)
{
 mBgColor = bg;
}

wxColour& wxOutlookListCtrl::GetBgColor()
{
 return mBgColor;
}

void wxOutlookListCtrl::SetTextColor(const wxColour& col)
{
 mTextColor = col;
}

wxColour& wxOutlookListCtrl::GetTextColor()
{
 return mTextColor;
}

void wxOutlookListCtrl::SetBgImage(wxOLImage* pImage)
{
 if (mpBgImage) delete mpBgImage;

 mpBgImage = pImage;
}

wxOLImage* wxOutlookListCtrl::GetBgImage()
{
 return mpBgImage;
}

void wxOutlookListCtrl::SetGenerateMasks(bool on)
{
 mGenerateMasks = on;
}

bool wxOutlookListCtrl::GetGenerateMasks()
{
 return mGenerateMasks;
}

void wxOutlookListCtrl::SetMaskColor(const wxColour& col)
{
 mMaskColor = col;
}

wxColour& wxOutlookListCtrl::GetMaskColor()
{
 return mMaskColor;
}

void wxOutlookListCtrl::SetTimerRate(long rateMills)
{
 mTimerRate = rateMills;
}

void wxOutlookListCtrl::SetSmallItemMargins(const wxOLMarginInfo& info)
{
 mSmallItemMargins = info;
}

wxOLMarginInfo& wxOutlookListCtrl::GetSmallItemMargins()
{
 return mSmallItemMargins;
}

void wxOutlookListCtrl::SetLargeItemMargins(const wxOLMarginInfo& info)
{
 mLargeItemMargins = info;
}

wxOLMarginInfo& wxOutlookListCtrl::GetLargeItemMargins()
{
 return mLargeItemMargins;
}

wxOLMarginInfo& wxOutlookListCtrl::GetItemMargins()
{
 if (GetItemType() == wxOL_SMALL_ITEM)

  return GetSmallItemMargins();
 else
  return GetLargeItemMargins();
}

void wxOutlookListCtrl::SetWigglingItem(wxOLItem* pItem)
{
 mpWigglingItem = pItem;
}

wxOLItem* wxOutlookListCtrl::GetWigglingItem()
{
 return mpWigglingItem;
}

void wxOutlookListCtrl::OnTrackItemMenu(wxOLItem* pItem, const wxPoint& pos)
{
 mpSelectedItem = pItem;
 DoTrackPopupMenu(pos, TRUE);
}

void wxOutlookListCtrl::OnTrackListMenu(const wxPoint& pos)
{
 DoTrackPopupMenu(pos, FALSE);
}

long wxOutlookListCtrl::GetTimerRate()
{
 return mTimerRate;
}

void wxOutlookListCtrl::SetDecorator(wxOLDecorator* pDecorator)
{
 if (mpDecorator) delete mpDecorator;

 mpDecorator = pDecorator;
 mpDecorator->SetControl(this);
}

void wxOutlookListCtrl::SetContainer(wxOLContainer* pContainer)
{
 wxASSERT(mpDecorator != NULL);

 pContainer->SetControl(this);
 mpDecorator->SetContainer(pContainer);
}

wxOLDecorator* wxOutlookListCtrl::GetDecorator()
{
 if (mpDecorator == NULL)
 {
  mpDecorator = new wxOLDecorator();

  mpDecorator->SetControl(this);
 }

 return mpDecorator;
}

wxOLContainer* wxOutlookListCtrl::GetContainer()
{
 if (mpContainer == NULL)
 {
  mpContainer = new wxOLBoxContainer();

  GetDecorator()->SetContainer(mpContainer);
 }
 return mpContainer;
}

void wxOutlookListCtrl::OnTimer(int millsElapsed)
{
 wxClientDC dc(this);
 mpCurrentDc = &dc;

 mpDecorator->OnTimerTick(dc, mTickCounter++);

 mpCurrentDc = NULL;
}

wxDC& wxOutlookListCtrl::GetCurrentDc()
{
 wxASSERT(mpCurrentDc != NULL); // DBG::

 return *mpCurrentDc;
}

void wxOutlookListCtrl::CaptureEventsForGlyph(wxOLGlyph* pReceiver)
{
 wxASSERT_MSG(mpCapturedGlyph == NULL, "Cannot capture mouse twice!"); // DBG::

 CaptureMouse();

 mpCapturedGlyph = pReceiver;
}

void wxOutlookListCtrl::ReleaseEvents(wxOLGlyph* pPrevReceiver)
{
 wxASSERT_MSG(mpCapturedGlyph != NULL, "ReleaseEvents() should follow the CaptureEvents()!"); // DBG::

 mpCapturedGlyph = NULL;

 ReleaseMouse();
}

/*** protected methods ***/

void wxOutlookListCtrl::OnPaint(wxPaintEvent& event)
{
 wxPaintDC dc(this);
 DoRefresh(dc);
}

void wxOutlookListCtrl::OnEraseBackground(wxEraseEvent& event)
{
 // nothing! (reduces flicker on MSW)
}

void wxOutlookListCtrl::OnLeftDown(wxMouseEvent& event)
{
 DoDispatchEvent(event, 0);
}

void wxOutlookListCtrl::OnLeftUp(wxMouseEvent& event)
{
 DoDispatchEvent(event, 1);
}

void wxOutlookListCtrl::OnMotion(wxMouseEvent& event)
{
 DoDispatchEvent(event, 2);
}

void wxOutlookListCtrl::OnLeftDblClick(wxMouseEvent& event)
{
 DoDispatchEvent(event, 0); // doublicks handled as regular "clicks"
         // to speed up scrolling
}

void wxOutlookListCtrl::OnRightDown(wxMouseEvent& event)
{
 DoDispatchEvent(event, 3);
}

void wxOutlookListCtrl::OnRightUp(wxMouseEvent& event)
{
 DoDispatchEvent(event, 4);
}

void wxOutlookListCtrl::Draw3DOutline(wxDC& dc, const wxRect& rect, bool isPressed)
{
 // TBD::
}

void wxOutlookListCtrl::DoRefresh(wxDC& dc)
{
 int w = 0, h = 0;
 GetClientSize(&w, &h);

 wxBitmap backBmp((int)w,int(h));
 wxMemoryDC memDc;
 memDc.SelectObject(backBmp);

 mpCurrentDc = &memDc;
 mpCurrentDc->SetFont(this->GetFont());

 wxBrush br(GetBgColor(), wxSOLID);
 (*mpCurrentDc).SetBrush(br);
 (*mpCurrentDc).SetPen(*wxTRANSPARENT_PEN);

 (*mpCurrentDc).DrawRectangle(0,0, w+1, h+1);

 mpDecorator->SetBounds(wxRect(0,0, w,h));
 mpDecorator->CalcPreferedSize(wxSize(w,h));
 mpDecorator->Layout();
 mpDecorator->Draw(*mpCurrentDc);

 mpCurrentDc = NULL;

 dc.Blit(0,0, w,h, &memDc, 0,0, wxCOPY);
}

void wxOutlookListCtrl::DoDispatchEvent(wxMouseEvent& event, int subType)
{
 if (mpEditor) 
 {
  if (subType == 0 || // left-down
    subType == 3)  // right-down
  {
   wxCommandEvent dummy;
   OnTextUpdated(dummy); // finish editing
         // if user clicks somewhere outside
         // the edit control
  }
  else
   return; // no mouse-handling while label-editor's active
 }

 wxClientDC dc(this);
 dc.SetFont(this->GetFont());
 mpCurrentDc = &dc;

 wxPoint pos(event.GetX(), event.GetY());

 wxOLGlyph* pTarget = mpDecorator;

 if (mpCapturedGlyph) pTarget = mpCapturedGlyph;

 switch(subType)
 {
  case 0  : pTarget->OnLeftDown (pos); break;
  case 1  : pTarget->OnLeftUp   (pos); break;
  case 2  : pTarget->OnMotion   (pos); break;
  case 3  : pTarget->OnRightDown(pos); break;
  case 4  : pTarget->OnRightUp  (pos); break;
  default : break;
 }

 mpCurrentDc = NULL;
}

void wxOutlookListCtrl::OnCmdAlignTextTop(wxCommandEvent& event)
{
 SetItemTextAlign(wxOL_ALIGN_TOP); RefreshNow();
}

void wxOutlookListCtrl::OnCmdAlignTextBottom(wxCommandEvent& event)
{
 SetItemTextAlign(wxOL_ALIGN_BOTTOM); RefreshNow();
}

void wxOutlookListCtrl::OnCmdAlignTextLeft(wxCommandEvent& event)
{
 SetItemTextAlign(wxOL_ALIGN_LEFT); RefreshNow();
}

void wxOutlookListCtrl::OnCmdAlignTextRight(wxCommandEvent& event)
{
 SetItemTextAlign(wxOL_ALIGN_RIGHT); RefreshNow();
}

void wxOutlookListCtrl::OnCmdAlignVertical(wxCommandEvent& event)
{
 int style = GetItemType() == wxOL_SMALL_ITEM 
    ? wxOL_SMALL_ITEMS
    : wxOL_LARGE_ITEMS;

 style |= wxOL_ITEM_DRAG;

 ChangeStyle(style | wxOL_VERTICAL); RefreshNow();
}

void wxOutlookListCtrl::OnCmdAlignHorizontal(wxCommandEvent& event)
{
 int style = GetItemType() == wxOL_SMALL_ITEM 
    ? wxOL_SMALL_ITEMS
    : wxOL_LARGE_ITEMS;

 style |= wxOL_ITEM_DRAG;

 ChangeStyle(style | wxOL_HORIZONTAL); RefreshNow();
}

void wxOutlookListCtrl::OnCmdAlignItemsLeft(wxCommandEvent& event)
{
 SetItemAlign(wxOL_ITEM_LEFT_ALIGNED); RefreshNow();
}

void wxOutlookListCtrl::OnCmdAlignItemsRight(wxCommandEvent& event)
{
 SetItemAlign(wxOL_ITEM_RIGHT_ALIGNED); RefreshNow();
}

void wxOutlookListCtrl::OnCmdAlignItemsCenter(wxCommandEvent& event)
{
 SetItemAlign(wxOL_ITEM_CENTERED); RefreshNow();
}

void wxOutlookListCtrl::OnCmdAlignScrollerTop(wxCommandEvent& event)
{
 SetScrollShaftAlign(wxOL_ALIGN_TOP); RefreshNow();
}

void wxOutlookListCtrl::OnCmdAlignScrollerBottom(wxCommandEvent& event)
{
 SetScrollShaftAlign(wxOL_ALIGN_BOTTOM); RefreshNow();
}

void wxOutlookListCtrl::OnCmdAlignScrollerLeft(wxCommandEvent& event)
{
 SetScrollShaftAlign(wxOL_ALIGN_LEFT); RefreshNow();
}

void wxOutlookListCtrl::OnCmdAlignScrollerRight(wxCommandEvent& event)
{
 SetScrollShaftAlign(wxOL_ALIGN_RIGHT); RefreshNow();
}

void wxOutlookListCtrl::OnCmdListView(wxCommandEvent& event)
{
 int style = IsVertical() ? wxOL_VERTICAL : wxOL_HORIZONTAL;

 style |= wxOL_ITEM_DRAG;

 ChangeStyle(style | wxOL_SMALL_ITEMS); RefreshNow();
}

void wxOutlookListCtrl::OnCmdIconView(wxCommandEvent& event)
{
 int style = IsVertical() ? wxOL_VERTICAL : wxOL_HORIZONTAL;

 style |= wxOL_ITEM_DRAG;

 ChangeStyle(style | wxOL_LARGE_ITEMS); RefreshNow();
}

void wxOutlookListCtrl::OnCmdWordWrapOn(wxCommandEvent& event)
{
 SetWordWrap(TRUE); RefreshNow();
}

void wxOutlookListCtrl::OnCmdWordWrapOff(wxCommandEvent& event)
{
 SetWordWrap(FALSE); RefreshNow();
}

void wxOutlookListCtrl::OnCmdRemoveItem(wxCommandEvent& event)
{
 wxASSERT(mpSelectedItem); // DBG::

 RemoveItem(mpSelectedItem); RefreshNow();

 if (mpCapturedGlyph) ReleaseEvents(mpCapturedGlyph);

 mpSelectedItem = NULL;
}

void wxOutlookListCtrl::OnCmdRenameItem(wxCommandEvent& event)
{
 wxASSERT(mpSelectedItem);   // DBG::
 wxASSERT(mpEditor == NULL); // DBG::

 if (mpCapturedGlyph) ReleaseEvents(mpCapturedGlyph);

 mpSelectedItem->SetIsFocused(FALSE);
 mpSelectedItem->SetIsPressed(FALSE);
 mpSelectedItem->HideText(TRUE);

 RefreshNow();

 wxRect bounds = mpSelectedItem->GetTextBounds();
 wxPoint pos(bounds.x, bounds.y);
 wxSize  sz(bounds.width, bounds.height);

 int thisWidth = 0, thisHeight = 0;
 this->GetSize(&thisWidth, &thisHeight);

 if (sz.x < 2)
 {
  sz = wxSize(thisWidth, this->GetCharHeight() + 10);

  if (IsVertical())

   pos.x = 0;
  else
   pos.x = mpSelectedItem->GetBounds().x;
 }

 mpEditor = new wxTextCtrl(this, ID_EDITOR_CTRL, 
          mpSelectedItem->GetText(),  pos, sz,
          wxTE_PROCESS_ENTER);

 mpEditor->SetSelection(0, mpSelectedItem->GetText().Length());

 mpEditor->SetFont(this->GetFont());

 int w = 0, h = 0, w1 = 0, h1 = 0;
 mpEditor->GetSize(&w, &h);
 mpEditor->GetClientSize(&w1, &h1);

 mpEditor->SetSize(sz.x + (w-w1), sz.y + (h-h1));
 mpEditor->SetFocus();
}

void wxOutlookListCtrl::OnTextUpdated(wxCommandEvent& event)
{
 wxASSERT(mpEditor != NULL); // DBG::

 mpSelectedItem->SetText(mpEditor->GetValue());
 mpSelectedItem->HideText(FALSE);

 mpEditor->Destroy();
 mpEditor = NULL;

 RefreshNow();
}

void wxOutlookListCtrl::DoTrackPopupMenu(const wxPoint& pos, bool forItem)
{
 wxMenu* menu = new wxMenu();

 if (forItem)
 {
  menu->Append(ID_RENAME_ITEM, "Re&name item");
  menu->Append(ID_REMOVE_ITEM, "Rem&ove item");
  menu->AppendSeparator();
 }
 menu->Append(ID_ICON_VIEW, "Large &Icons");
 menu->Append(ID_LIST_VIEW, "&List View");
 menu->AppendSeparator();
 menu->Append(ID_ALIGN_VERTICAL, "Layout &Vertically");
 menu->Append(ID_ALIGN_HORIZONTAL, "Layout &Horizontally");
 menu->AppendSeparator();
 menu->Append(ID_ALIGN_TEXT_TOP, "Align Text &Top");
 menu->Append(ID_ALIGN_TEXT_BOTTOM, "Align Text &Bottom");
 menu->Append(ID_ALIGN_TEXT_LEFT,   "Align Text L&eft");
 menu->Append(ID_ALIGN_TEXT_RIGHT,  "Align Text &Right");
 menu->AppendSeparator();
 menu->Append(ID_ALIGN_ITEMS_LEFT,   "&Justify Items Left");
 menu->Append(ID_ALIGN_ITEMS_RIGHT,  "Justify Items Ri&ght");
 menu->Append(ID_ALIGN_ITEMS_CENTER, "Justify Items &Center");
 menu->AppendSeparator();
 menu->Append(ID_ALIGN_SCROLLER_TOP,    "Align Scroll-buttons Top");
 menu->Append(ID_ALIGN_SCROLLER_BOTTOM, "Align Scroll-buttons Bottom");
 menu->Append(ID_ALIGN_SCROLLER_LEFT,   "Align Scroll-buttons Left");
 menu->Append(ID_ALIGN_SCROLLER_RIGTH,  "Align Scroll-buttons Right");

 menu->AppendSeparator();
 menu->Append(ID_WORD_WRAP_ON,  "Word-wrap On");
 menu->Append(ID_WORD_WRAP_OFF, "Word-wrap Off");

 this->PopupMenu(menu, pos.x, pos.y);
}
