/*-*- c++ -*-********************************************************
 * wxllist: wxLayoutList, a layout engine for text and graphics     *
 *                                                                  *
 * (C) 1998-2000 by Karsten Ballüder (Ballueder@gmx.net)            *
 *                                                                  *
 * $Id: wxllist.cpp,v 1.34 2002/03/27 18:44:50 VZ Exp $
 *******************************************************************/

/*

  Some docs:

  Layout() recalculates the objects, sizes, etc.
  Draw()   just draws them with the current settings, without
           re-layout()ing them again

  Each line has its own wxLayoutStyleInfo structure which gets updated
  from within Layout(). Thanks to this, we don't need to re-layout all
  lines if we want to draw one, but can just use its styleinfo to set
  the right font.

 */

#ifdef __GNUG__
#   pragma implementation "wxllist.h"
#endif

#include "wx/wxprec.h"

#ifdef __BORLANDC__
#  pragma hdrstop
#endif

#include "Mpch.h"

#ifdef M_BASEDIR
#   include "Mcommon.h"
#   include "gui/wxllist.h"
#   include "gui/wxlparser.h"
#   define  SHOW_SELECTIONS 1
#else
#   include "wxllist.h"
#   include "wxlparser.h"
#   define SHOW_SELECTIONS 1
#endif

#ifndef USE_PCH
#if wxUSE_IOSTREAMH
    #include <iostream.h>
#else
    #include <iostream>
#endif

#   include <wx/dc.h>
#   include <wx/dcps.h>
#   include <wx/print.h>
#   include <wx/log.h>
#   include <wx/filefn.h>
#endif

#ifdef WXLAYOUT_USE_CARET
#   include <wx/caret.h>
#endif // WXLAYOUT_USE_CARET

#include <ctype.h>


/// This should never really get created
#define   WXLLIST_TEMPFILE   "__wxllist.tmp"

#ifdef WXLAYOUT_DEBUG

#  define   TypeString(t)      g_aTypeStrings[t]
#  define   WXLO_DEBUG(x)      wxLogDebug x

   static const char *g_aTypeStrings[] =
   {
      "invalid", "text", "cmd", "icon"
   };
   wxString
   wxLayoutObject::DebugDump(void) const
   {
      wxString str;
      str.Printf(wxT("%s"), g_aTypeStrings[GetType()]);
      return str;
   }
#else
#   define   TypeString(t)        ""
#   define   WXLO_DEBUG(x)
#endif


// FIXME under MSW, this constant is needed to make the thing properly redraw
//       itself - I don't know where the size calculation error is and I can't
//       waste time looking for it right now. Search for occurences of
//       MSW_CORRECTION to find all the places where I did it.
#ifdef __WXMSW__
   static const int MSW_CORRECTION = 10;
#else
   static const int MSW_CORRECTION = 0;
#endif

/// Cursors smaller than this disappear in XOR drawing mode
#define WXLO_MINIMUM_CURSOR_WIDTH   4

/// Use this character to estimate a cursor size when none is available.
#define WXLO_CURSORCHAR   "E"
/** @name Helper functions */
//@{
/// allows me to compare to wxPoints
bool operator <=(wxPoint const &p1, wxPoint const &p2)
{
   return p1.y < p2.y || (p1.y == p2.y && p1.x <= p2.x);
}

/*
  The following STAY HERE until we have a working wxGTK again!!!
*/
#ifndef wxWANTS_CHARS
/// allows me to compare to wxPoints
bool operator ==(wxPoint const &p1, wxPoint const &p2)
{
   return p1.x == p2.x && p1.y == p2.y;
}

/// allows me to compare to wxPoints
bool operator !=(wxPoint const &p1, wxPoint const &p2)
{
   return p1.x != p2.x || p1.y != p2.y;
}

wxPoint & operator += (wxPoint &p1, wxPoint const &p2)
{
   p1.x += p2.x;
   p1.y += p2.y;
   return p1;
}
#endif // old wxGTK

/// allows me to compare to wxPoints
bool operator>(wxPoint const &p1, wxPoint const &p2)
{
   return !(p1 <= p2);
}

/// grows a wxRect so that it includes the given point

static
void GrowRect(wxRect &r, CoordType x, CoordType y)
{
   if(r.x > x)
      r.x = x;
   else if(r.x + r.width < x)
      r.width = x - r.x;

   if(r.y > y)
      r.y = y;
   else if(r.y + r.height < y)
      r.height = y - r.y;
}

#if 0
// unused
/// returns true if the point is in the rectangle
static
bool Contains(const wxRect &r, const wxPoint &p)
{
   return r.x <= p.x && r.y <= p.y && (r.x+r.width) >= p.x && (r.y + r.height) >= p.y;
}
#endif


//@}


static
void ReadString(wxString &to, wxString &from)
{
    to = wxT("");
    const wxChar *cptr = from.c_str();
    while(*cptr && *cptr != wxT('\n'))
    {
        to += cptr;
        cptr++;
    }

    if(*cptr) cptr++;

    from = cptr;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

   wxLayoutObject

   * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/* static */
wxLayoutObject *
wxLayoutObject::Read(wxString &istr)
{
    wxString tmp;
    ReadString(tmp, istr);
    long l = WXLO_TYPE_INVALID;
    tmp.ToLong(&l);
    int type = (int) l;

    switch(type)
    {
    case WXLO_TYPE_TEXT:
        return wxLayoutObjectText::Read(istr);
    case WXLO_TYPE_CMD:
        return wxLayoutObjectCmd::Read(istr);
    case WXLO_TYPE_ICON:
        return wxLayoutObjectIcon::Read(istr);
    default:
        return NULL;
    }
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

   wxLayoutObjectText

   * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

wxLayoutObjectText::wxLayoutObjectText(const wxString &txt)
{
   m_Text = txt;
   m_Width = 0;
   m_Height = 0;
   m_Top = 0;
   m_Bottom = 0;
}

wxLayoutObject *
wxLayoutObjectText::Copy(void)
{
   wxLayoutObjectText *obj = new wxLayoutObjectText(m_Text);
   obj->m_Width = m_Width;
   obj->m_Height = m_Height;
   obj->m_Top = m_Top;
   obj->m_Bottom = m_Bottom;
   obj->SetUserData(m_UserData);
   return obj;
}


void
wxLayoutObjectText::Write(wxString &ostr)
{
   ostr << (int) WXLO_TYPE_TEXT << '\n'
        << m_Text << '\n';
}
/* static */
wxLayoutObjectText *
wxLayoutObjectText::Read(wxString &istr)
{
   wxString text;
   ReadString(text, istr);

   return new wxLayoutObjectText(text);
}

wxPoint
wxLayoutObjectText::GetSize(CoordType *top, CoordType *bottom) const
{

   *top = m_Top; *bottom = m_Bottom;
   return wxPoint(m_Width, m_Height);
}

void
wxLayoutObjectText::Draw(wxDC &dc, wxPoint const &coords,
                         wxLayoutList *wxllist,
                         CoordType begin, CoordType end)
{
   if( end <= 0 )
   {
      // draw the whole object normally
      dc.DrawText(m_Text, coords.x, coords.y-m_Top);
   }
   else
   {
      // highlight the bit between begin and len
      CoordType
         xpos = coords.x,
         ypos = coords.y-m_Top;
      long width, height, descent;

      if(begin < 0) begin = 0;
      if( end > (signed)m_Text.Length() )
         end = m_Text.Length();

      wxString str = m_Text.Mid(0, begin);
      dc.DrawText(str, xpos, ypos);
      dc.GetTextExtent(str, &width, &height, &descent);
      xpos += width;
      wxllist->StartHighlighting(dc);
      str = m_Text.Mid(begin, end-begin);
      dc.DrawText(str, xpos, ypos);
      dc.GetTextExtent(str, &width, &height, &descent);
      xpos += width;
      wxllist->EndHighlighting(dc);
      str = m_Text.Mid(end, m_Text.Length()-end);
      dc.DrawText(str, xpos, ypos);
   }
}

CoordType
wxLayoutObjectText::GetOffsetScreen(wxDC &dc, CoordType xpos) const
{
   CoordType
      offs = 1,
      maxlen = m_Text.Length();
   long
      width = 0,
      height, descent = 0l;

   if(xpos == 0) return 0; // easy

   while(width < xpos && offs < maxlen)
   {
      dc.GetTextExtent(m_Text.substr(0,offs),
                       &width, &height, &descent);
      offs++;
   }
   /* We have to substract 1 to compensate for the offs++, and another
      one because we don't want to position the cursor behind the
      object what we clicked on, but before - otherwise it looks
      funny. */
   return (xpos > 2) ? offs-2 : 0;
}

void
wxLayoutObjectText::Layout(wxDC &dc, class wxLayoutList *llist)
{
   long descent = 0l;

   // now this is done in wxLayoutLine::Layout(), but this code might be
   // reenabled later - in principle, it's more efficient
#if 0
   CoordType widthOld = m_Width,
             heightOld = m_Height;
#endif // 0

#ifdef __WXDEBUG__
   CoordType a,b,c,d,e,f;
   dc.GetTextExtent("test ", &a, &b, &c);
   dc.GetTextExtent("test", &d, &e, &f);
   wxASSERT(a != d);
   wxASSERT(b == e);
   wxASSERT(c == f);
   dc.GetTextExtent(" ", &d, &e, &f);
   wxASSERT(a > 0);
#endif
   dc.GetTextExtent(m_Text, &m_Width, &m_Height, &descent);

#if 0
   if ( widthOld != m_Width || heightOld != m_Height )
   {
      // as the text length changed, it must be refreshed
      wxLayoutLine *line = GetLine();

      wxCHECK_RET( line, "wxLayoutObjectText can't refresh itself" );

      // as our size changed, we need to repaint the part which was appended
      wxPoint position(line->GetPosition());

      // this is not the most efficient way (we repaint the whole line), but
      // it's not too slow and is *simple*
      if ( widthOld < m_Width )
         widthOld = m_Width;
      if ( heightOld < m_Height )
         heightOld = m_Height;

      llist->SetUpdateRect(position.x + widthOld + MSW_CORRECTION,
                           position.y + heightOld + MSW_CORRECTION);
   }
#endif // 0

   m_Bottom = descent;
   m_Top = m_Height - m_Bottom;
}


#ifdef WXLAYOUT_DEBUG
wxString
wxLayoutObjectText::DebugDump(void) const
{
   wxString str;
   str = wxLayoutObject::DebugDump();
   wxString str2;
   str2.Printf(wxT(" `%s`"), m_Text.c_str());
   return str+str2;
}
#endif

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

   wxLayoutObjectIcon

   * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap const &icon)
{
   if ( !icon.Ok() )
   {
      wxFAIL_MSG(wxT("invalid icon"));

      m_Icon = NULL;

      return;
   }

#ifdef __WXMSW__
   // FIXME ugly, ugly, ugly - but the only way to avoid slicing
   m_Icon = icon.GetHBITMAP() ? new wxBitmap(icon)
                              : new wxBitmap(wxBitmap((const wxBitmap &)icon));
#else // !MSW
   m_Icon = new wxBitmap(icon);
#endif // MSW/!MSW
}


void
wxLayoutObjectIcon::Write(wxString &ostr)
{
   /* Exports icon through a temporary file. */

   wxString file = wxGetTempFileName("wxloexport");

   ostr << (int) WXLO_TYPE_ICON << '\n'
        << file << '\n';
   m_Icon->SaveFile(file, WXLO_BITMAP_FORMAT);
}
/* static */
wxLayoutObjectIcon *
wxLayoutObjectIcon::Read(wxString &istr)
{
   wxString file;
   ReadString(file, istr);

   if(! wxFileExists(file))
      return NULL;
   wxLayoutObjectIcon *obj = new wxLayoutObjectIcon;

   if(!obj->m_Icon->LoadFile(file, WXLO_BITMAP_FORMAT))
   {
      delete obj;
      return NULL;
   }
   else
      return obj;
}

wxLayoutObject *
wxLayoutObjectIcon::Copy(void)
{
   wxLayoutObjectIcon *obj = new wxLayoutObjectIcon(new
                                                    wxBitmap(*m_Icon));
   obj->SetUserData(m_UserData);
   return obj;
}

wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap *icon)
{
   m_Icon = icon;
   if(! m_Icon)
      m_Icon = new wxBitmap;
}

void
wxLayoutObjectIcon::Draw(wxDC &dc, wxPoint const &coords,
                         wxLayoutList *wxllist,
                         CoordType begin, CoordType /* len */)
{
   dc.DrawBitmap(*m_Icon, coords.x, coords.y-m_Icon->GetHeight(),
                 (m_Icon->GetMask() == NULL) ? FALSE : TRUE);
}

void
wxLayoutObjectIcon::Layout(wxDC & /* dc */, class wxLayoutList * )
{
}

wxPoint
wxLayoutObjectIcon::GetSize(CoordType *top, CoordType *bottom) const
{
   *top = m_Icon->GetHeight();
   *bottom = 0;
   return wxPoint(m_Icon->GetWidth(), m_Icon->GetHeight());
}



/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

   wxLayoutObjectCmd

   * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */


wxLayoutStyleInfo::wxLayoutStyleInfo(int ifamily,
                                     int isize,
                                     int istyle,
                                     int iweight,
                                     int iul,
                                     wxColour *fg,
                                     wxColour *bg)
{
   family = ifamily;
   size = isize;
   style = istyle;
   weight = iweight;
   underline = iul != 0;

   m_fg_valid = fg != 0;
   m_bg_valid = bg != 0;
   m_fg = m_fg_valid ? *fg : *wxBLACK;
   m_bg = m_bg_valid ? *bg : *wxWHITE;
}

#define COPY_SI_(what) if(right.what != -1) what = right.what;

wxLayoutStyleInfo &
wxLayoutStyleInfo::operator=(const wxLayoutStyleInfo &right)
{
   COPY_SI_(family);
   COPY_SI_(style);
   COPY_SI_(size);
   COPY_SI_(weight);
   COPY_SI_(underline);
   if(right.m_fg_valid) m_fg = right.m_fg;
   if(right.m_bg_valid) m_bg = right.m_bg;
   return *this;
}

wxLayoutObjectCmd::wxLayoutObjectCmd(int family, int size, int style, int
                                     weight, int underline,
                                     wxColour *fg, wxColour *bg)

{
   m_StyleInfo = new wxLayoutStyleInfo(family, size,style,weight,underline,fg,bg);
}

wxLayoutObjectCmd::wxLayoutObjectCmd(const wxLayoutStyleInfo &si)

{
   m_StyleInfo = new wxLayoutStyleInfo;
   *m_StyleInfo = si;
}

wxLayoutObject *
wxLayoutObjectCmd::Copy(void)
{
   wxLayoutObjectCmd *obj = new wxLayoutObjectCmd(
      m_StyleInfo->family,
      m_StyleInfo->size,
      m_StyleInfo->style,
      m_StyleInfo->weight,
      m_StyleInfo->underline,
      m_StyleInfo->m_fg_valid ?
      &m_StyleInfo->m_fg : NULL,
      m_StyleInfo->m_bg_valid ?
      &m_StyleInfo->m_bg : NULL);
   obj->SetUserData(m_UserData);
   return obj;
}

void
wxLayoutObjectCmd::Write(wxString &ostr)
{
   ostr << (int) WXLO_TYPE_CMD << '\n'
        << (int) m_StyleInfo->family << '\n'
        << (int) m_StyleInfo->size << '\n'
        << (int) m_StyleInfo->style << '\n'
        << (int) m_StyleInfo->weight << '\n'
        << (int) m_StyleInfo->underline << '\n'
        << (int) m_StyleInfo->m_fg_valid << '\n'
        << (int) m_StyleInfo->m_bg_valid << '\n';
   if(m_StyleInfo->m_fg_valid)
   {
      ostr << (int) m_StyleInfo->m_fg.Red() << '\n'
           << (int) m_StyleInfo->m_fg.Green() << '\n'
           << (int) m_StyleInfo->m_fg.Blue() << '\n';
   }
   if(m_StyleInfo->m_bg_valid)
   {
      ostr << (int) m_StyleInfo->m_bg.Red() << '\n'
           << (int) m_StyleInfo->m_bg.Green() << '\n'
           << (int) m_StyleInfo->m_bg.Blue() << '\n';
   }
}
/* static */
wxLayoutObjectCmd *
wxLayoutObjectCmd::Read(wxString &istr)
{
    wxLayoutObjectCmd *obj = new wxLayoutObjectCmd;

    long l = 0;
    wxString tmp;
    ReadString(tmp, istr);
    tmp.ToLong(&l);
    obj->m_StyleInfo->family = (int) l;


    ReadString(tmp, istr);
    tmp.ToLong(&l);
    obj->m_StyleInfo->size = (int) l;

    ReadString(tmp, istr);
    tmp.ToLong(&l);
    obj->m_StyleInfo->style = (int) l;

    ReadString(tmp, istr);
    tmp.ToLong(&l);
    obj->m_StyleInfo->weight = (int) l;

    ReadString(tmp, istr);
    tmp.ToLong(&l);
    obj->m_StyleInfo->underline = (int) l;

    ReadString(tmp, istr);
    tmp.ToLong(&l);
    obj->m_StyleInfo->m_fg_valid = (int) l;

    ReadString(tmp, istr);
    tmp.ToLong(&l);
    obj->m_StyleInfo->m_bg_valid = (int) l;

    if(obj->m_StyleInfo->m_fg_valid)
    {
        int red, green, blue;
        ReadString(tmp, istr);
        tmp.ToLong(&l);
        red = (int) l;

        ReadString(tmp, istr);
        tmp.ToLong(&l);
        green = (int) l;

        ReadString(tmp, istr);
        tmp.ToLong(&l);
        blue = (int) l;

        obj->m_StyleInfo->m_fg = wxColour(red, green, blue);
    }

    if(obj->m_StyleInfo->m_bg_valid)
    {
        int red, green, blue;
        ReadString(tmp, istr);
        tmp.ToLong(&l);
        red = (int) l;

        ReadString(tmp, istr);
        tmp.ToLong(&l);
        green = (int) l;

        ReadString(tmp, istr);
        tmp.ToLong(&l);
        blue = (int) l;

        obj->m_StyleInfo->m_bg = wxColour(red, green, blue);
    }

    return obj;
}


wxLayoutObjectCmd::~wxLayoutObjectCmd()
{
   delete m_StyleInfo;
}

wxLayoutStyleInfo *
wxLayoutObjectCmd::GetStyle(void) const
{
   return m_StyleInfo;
}

void
wxLayoutObjectCmd::Draw(wxDC &dc, wxPoint const & /* coords */,
                        wxLayoutList *wxllist,
                        CoordType begin, CoordType /* len */)
{
   wxASSERT(m_StyleInfo);
   wxllist->ApplyStyle(*m_StyleInfo, dc);
}

void
wxLayoutObjectCmd::Layout(wxDC &dc, class wxLayoutList * llist)
{
   // this get called, so that recalculation uses right font sizes
   Draw(dc, wxPoint(0,0), llist);
}


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

   The wxLayoutLine object

   * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

wxLayoutLine::wxLayoutLine(wxLayoutLine *prev, wxLayoutList *llist)
{
   m_Width = m_Height = 0;
   m_Length = 0;

   m_updateLeft = -1;
   m_Previous = prev;
   m_Next = NULL;
   MarkDirty(0);

   m_LineNumber = 0;
   RecalculatePosition(llist);

   MarkDirty();
   if(m_Previous)
   {
      m_LineNumber = m_Previous->GetLineNumber() + 1;
      m_Next = m_Previous->GetNextLine();
      m_Previous->m_Next = this;
   }

   if(m_Next)
   {
      m_Next->m_Previous = this;
      m_Next->ReNumber();
   }

   m_StyleInfo = llist->GetDefaultStyleInfo();

   llist->IncNumLines();
}

wxLayoutLine::~wxLayoutLine()
{
   // kbList cleans itself
}

wxPoint
wxLayoutLine::RecalculatePosition(wxLayoutList *llist)
{
   wxASSERT(m_Previous || GetLineNumber() == 0);

   wxPoint posOld(m_Position);

   if(m_Previous)
   {
      m_Position = m_Previous->GetPosition();
      m_Position.y += m_Previous->GetHeight();
   }
   else
      m_Position = wxPoint(0,0);

   if ( m_Position != posOld )
   {
       // the whole line moved and must be repainted
       llist->SetUpdateRect(m_Position);
       llist->SetUpdateRect(m_Position.x + GetWidth() + MSW_CORRECTION,
                            m_Position.y + GetHeight() + MSW_CORRECTION);
       llist->SetUpdateRect(posOld);
       llist->SetUpdateRect(posOld.x + GetWidth() + MSW_CORRECTION,
                            posOld.y + GetHeight() + MSW_CORRECTION);
   }

   return m_Position;
}


wxLayoutObjectList::iterator
wxLayoutLine::FindObject(CoordType xpos, CoordType *offset) const
{
   wxASSERT(xpos >= 0);
   wxASSERT(offset);
   wxLayoutObjectList::iterator
      i,
      found = NULLIT;
   CoordType x = 0, len;

   /* We search through the objects. As we don't like returning the
      object that the cursor is behind, we just remember such an
      object in "found" so we can return it if there is really no
      further object following it. */
   for(i = m_ObjectList.begin(); i != NULLIT; i++)
   {
      len = (**i).GetLength();
      if( x <= xpos && xpos <= x + len )
      {
         *offset = xpos-x;
         if(xpos == x + len) // is there another object behind?
            found = i;
         else  // we are really inside this object
            return i;
      }
      x += (**i).GetLength();
   }
   return found;  // ==NULL if really none found
}

wxLayoutObjectList::iterator
wxLayoutLine::FindObjectScreen(wxDC &dc, wxLayoutList *llist,
                               CoordType xpos, CoordType *cxpos,
                               bool *found) const
{
   wxASSERT(cxpos);

   llist->ApplyStyle(GetStyleInfo(), dc);

   wxLayoutObjectList::iterator i;
   CoordType x = 0, cx = 0, width;

   for(i = m_ObjectList.begin(); i != NULLIT; i++)
   {
      wxLayoutObject *obj = *i;
      if ( obj->GetType() == WXLO_TYPE_CMD )
      {
         // this will set the correct font for the objects which follow
         obj->Layout(dc, llist);
      }

      width = obj->GetWidth();
      if( x <= xpos && xpos <= x + width )
      {
         *cxpos = cx + obj->GetOffsetScreen(dc, xpos-x);

         if ( found )
             *found = true;
         return i;
      }

      x += obj->GetWidth();
      cx += obj->GetLength();
   }

   // behind last object:
   *cxpos = cx;

   if (found)
       *found = false;
   return m_ObjectList.tail();
}

/** Finds text in this line.
    @param needle the text to find
    @param xpos the position where to start the search
    @return the cursoor coord where it was found or -1
*/
CoordType
wxLayoutLine::FindText(const wxString &needle, CoordType xpos) const
{
   int
      cpos = 0,
      relpos = -1;
   wxString const *text;

   for(wxLOiterator i = m_ObjectList.begin(); i != m_ObjectList.end(); i++)
   {
      if(cpos >= xpos) // search from here!
      {
         if((**i).GetType() == WXLO_TYPE_TEXT)
         {
            text = & ((wxLayoutObjectText*)(*i))->GetText();
            relpos = text->Find(needle);
            if(relpos >= cpos-xpos) // -1 if not found
            {
               return cpos+relpos;
            }
         }
         cpos += (**i).GetLength();
      }
   }
   return -1; // not found
}

bool
wxLayoutLine::Insert(CoordType xpos, wxLayoutObject *obj)
{
   wxASSERT(xpos >= 0);
   wxASSERT(obj != NULL);

   MarkDirty(xpos);

   CoordType offset;
   wxLOiterator i = FindObject(xpos, &offset);
   if(i == NULLIT)
   {
      if(xpos == 0 ) // aha, empty line!
      {
         m_ObjectList.push_back(obj);
         m_Length += obj->GetLength();
         return true;
      }
      else
         return false;
   }

   CoordType len = (**i).GetLength();
   if(offset == 0 /*&& i != m_ObjectList.begin()*/) // why?
   {  // insert before this object
      m_ObjectList.insert(i,obj);
      m_Length += obj->GetLength();
      return true;
   }
   if(offset == len )
   {
      if( i == m_ObjectList.tail()) // last object?
         m_ObjectList.push_back(obj);
      else
      {  // insert after current object
         i++;
         m_ObjectList.insert(i,obj);
      }
         m_Length += obj->GetLength();
      return true;
   }
   /* Otherwise we need to split the current object.
      Fortunately this can only be a text object. */
   wxASSERT((**i).GetType() == WXLO_TYPE_TEXT);
   wxString left, right;
   wxLayoutObjectText *tobj = (wxLayoutObjectText *) *i;
   left = tobj->GetText().substr(0,offset);
   right = tobj->GetText().substr(offset,len-offset);
   // current text object gets set to right half
   tobj->GetText() = right; // set new text
   // before it we insert the new object
   m_ObjectList.insert(i,obj);
   m_Length += obj->GetLength();
   // and before that we insert the left half
   m_ObjectList.insert(i,new wxLayoutObjectText(left));
   return true;
}

bool
wxLayoutLine::Insert(CoordType xpos, const wxString& text)
{
   wxASSERT(xpos >= 0);

   MarkDirty(xpos);

   CoordType offset;
   wxLOiterator i = FindObject(xpos, &offset);
   if(i != NULLIT && (**i).GetType() == WXLO_TYPE_TEXT)
   {
      wxLayoutObjectText *tobj = (wxLayoutObjectText *) *i;
      tobj->GetText().insert(offset, text);
      m_Length += text.Length();
   }
   else
   {
      if ( !Insert(xpos, new wxLayoutObjectText(text)) )
         return false;
   }

   return true;
}

CoordType
wxLayoutLine::Delete(CoordType xpos, CoordType npos)
{
   CoordType offset, len;

   wxASSERT(xpos >= 0);
   wxASSERT(npos >= 0);
   MarkDirty(xpos);
   wxLOiterator i = FindObject(xpos, &offset);
   while(npos > 0)
   {
      if(i == NULLIT)  return npos;
      // now delete from that object:
      if((**i).GetType() != WXLO_TYPE_TEXT)
      {
         if(offset != 0) // at end of line after a non-text object
            return npos;
         // always len == 1:
         len = (**i).GetLength();
         m_Length -= len;
         npos -= len;
         m_ObjectList.erase(i);
      }
      else
      {
         // tidy up: remove empty text objects
         if((**i).GetLength() == 0)
         {
            m_ObjectList.erase(i);
            continue;
         }
         // Text object:
         CoordType max = (**i).GetLength() - offset;
         if(npos < max) max = npos;
         if(max == 0)
         {
            if(xpos == GetLength())
               return npos;
            else
            {  // at    the end of an object
               // move to    begin of next object:
               i++; offset = 0;
               continue; // start over
            }
         }
         npos -= max;
         m_Length -= max;
         if(offset == 0 && max == (**i).GetLength())
            m_ObjectList.erase(i);  // remove the whole object
         else
            ((wxLayoutObjectText *)(*i))->GetText().Remove(offset,max);
      }
   }

   return npos;
}

bool
wxLayoutLine::DeleteWord(CoordType xpos)
{
    wxASSERT(xpos >= 0);
    CoordType offset;
    MarkDirty(xpos);

    wxLOiterator i = FindObject(xpos, &offset);

    for(;;)
    {
        if(i == NULLIT) return false;
        if((**i).GetType() != WXLO_TYPE_TEXT)
        {
            // This should only happen when at end of line, behind a non-text
            // object:
            if(offset == (**i).GetLength()) return false;
            m_Length -= (**i).GetLength(); // -1
            m_ObjectList.erase(i);
            return true; // we are done
        }
        else
        {  // text object:
            if(offset == (**i).GetLength()) // at end of object
            {
                i++; offset = 0;
                continue;
            }

            wxLayoutObjectText *tobj = (wxLayoutObjectText *)*i;
            size_t count = 0;
            wxString str = tobj->GetText();
            str = str.substr(offset,str.Length()-offset);
            // Find out how many positions we need to delete:
            // 1. eat leading space
            while(isspace(str.c_str()[count])) count++;
            // 2. eat the word itself:
            while(isalnum(str.c_str()[count])) count++;
            // now delete it:
            wxASSERT(count+offset <= (size_t) (**i).GetLength());
            ((wxLayoutObjectText *)*i)->GetText().erase(offset,count);
            m_Length -= count;

            return true;
        }
    }

    wxFAIL_MSG(wxT("unreachable"));
}

wxLayoutLine *
wxLayoutLine::DeleteLine(bool update, wxLayoutList *llist)
{
   // maintain linked list integrity
   if(m_Next)
       m_Next->m_Previous = m_Previous;
   if(m_Previous)
       m_Previous->m_Next = m_Next;

   // get the line numbers right again
   if ( update && m_Next)
      m_Next->ReNumber();

   MarkDirty();

   // we can't use m_Next after "delete this", so we must save this pointer
   // first
   wxLayoutLine *next = m_Next;
   delete this;

   llist->DecNumLines();

   return next;
}

void
wxLayoutLine::Draw(wxDC &dc,
                   wxLayoutList *llist,
                   const wxPoint & offset) const
{
   wxLayoutObjectList::iterator i;
   wxPoint pos = offset;
   pos = pos + GetPosition();

   pos.y += m_BaseLine;

   CoordType xpos = 0; // cursorpos, lenght of line

   CoordType from, to, tempto;

   int highlight = llist->IsSelected(this, &from, &to);
//   WXLO_DEBUG(("highlight=%d",  highlight ));
   if(highlight == 1) // we need to draw the whole line inverted!
      llist->StartHighlighting(dc);
   else
      llist->EndHighlighting(dc);

   for(i = m_ObjectList.begin(); i != NULLIT; i++)
   {
      if(highlight == -1) // partially highlight line
      {
         // parts of the line need highlighting
         tempto = xpos+(**i).GetLength();
         (**i).Draw(dc, pos, llist, from-xpos, to-xpos);
      }
      else
         (**i).Draw(dc, pos, llist);
      pos.x += (**i).GetWidth();
      xpos += (**i).GetLength();
   }
}

/*
  This function does all the recalculation, that is, it should only be
  called from within wxLayoutList::Layout(), as it uses the current
  list's styleinfo and updates it.
*/
void
wxLayoutLine::Layout(wxDC &dc,
                     wxLayoutList *llist,
                     wxPoint *cursorPos,
                     wxPoint *cursorSize,
                     wxLayoutStyleInfo *cursorStyle,
                     int cx,
                     bool suppressSIupdate)
{
   wxLayoutObjectList::iterator i;

   // when a line becomes dirty, we redraw it from the place where it was
   // changed till the end of line (because the following wxLayoutObjects are
   // moved when the preceding one changes) - calculate the update rectangle.
   CoordType updateTop = m_Position.y,
             updateLeft = -1,
             updateWidth = m_Width,
             updateHeight = m_Height;

   CoordType
      topHeight = 0,
      bottomHeight = 0;  // above and below baseline
   CoordType
      objTopHeight, objBottomHeight; // above and below baseline
   CoordType
      len, count = 0;

   CoordType heightOld = m_Height;

   m_Height = 0;
   m_Width = 0;
   m_BaseLine = 0;

   bool cursorFound = false;

   RecalculatePosition(llist);
   
   if(cursorPos)
   {
      *cursorPos = m_Position;
      if(cursorSize) *cursorSize = wxPoint(0,0);
   }

   m_StyleInfo = llist->GetStyleInfo(); // save current style
   for(i = m_ObjectList.begin(); i != NULLIT; i++)
   {
      wxLayoutObject *obj = *i;
      obj->Layout(dc, llist);
      wxPoint sizeObj = obj->GetSize(&objTopHeight, &objBottomHeight);

      if(cursorPos && ! cursorFound)
      {
         // we need to check whether the text cursor is here
         len = obj->GetLength();
         if(count <= cx && count+len > cx)
         {
            if(obj->GetType() == WXLO_TYPE_TEXT)
            {
               len = cx - count; // pos in object
               CoordType width, height, descent;
               dc.GetTextExtent((*(wxLayoutObjectText*)*i).GetText().substr(0,len),
                                &width, &height, &descent);
               cursorPos->x += width;
               cursorPos->y = m_Position.y;
               wxString str;
               if(len < obj->GetLength())
                  str = (*(wxLayoutObjectText*)*i).GetText().substr(len,1);
               else
                  str = WXLO_CURSORCHAR;
               dc.GetTextExtent(str, &width, &height, &descent);

               if(cursorStyle) // set style info
                  *cursorStyle = llist->GetStyleInfo();
               if ( cursorSize )
               {
                  // Just in case some joker inserted an empty string object:
                  if(width == 0)
                     width = WXLO_MINIMUM_CURSOR_WIDTH;
                  if(height == 0)
                     height = sizeObj.y;
                  cursorSize->x = width;
                  cursorSize->y = height;
               }
               
               cursorFound = true; // no more checks
            }
            else
            {
               // on some other object
               CoordType top, bottom; // unused
               if(cursorSize)
                  *cursorSize = obj->GetSize(&top,&bottom);
               cursorPos->y = m_Position.y;
               cursorFound = true; // no more checks
            }
         }
         else
         {
            count += len;
            cursorPos->x += obj->GetWidth();
         }
      } // cursor finding

      m_Width += sizeObj.x;
      if(sizeObj.y > m_Height)
      {
         m_Height = sizeObj.y;
      }

      if(objTopHeight > topHeight)
         topHeight = objTopHeight;
      if(objBottomHeight > bottomHeight)
         bottomHeight = objBottomHeight;
   }

   if ( IsDirty() )
   {
      if ( updateHeight < m_Height )
         updateHeight = m_Height;
      if ( updateWidth < m_Width )
         updateWidth = m_Width;

      // update all line if we don't know where to start from
      if ( updateLeft == -1 )
          updateLeft = 0;

      llist->SetUpdateRect(updateLeft, updateTop);
      llist->SetUpdateRect(updateLeft + updateWidth + MSW_CORRECTION,
                           updateTop + updateHeight + MSW_CORRECTION);
   }

   if(topHeight + bottomHeight > m_Height)
   {
      m_Height = topHeight+bottomHeight;
   }

   m_BaseLine = topHeight;

   if(m_Height == 0)
   {
      CoordType width, height, descent;
      dc.GetTextExtent(WXLO_CURSORCHAR, &width, &height, &descent);
      m_Height = height;
      m_BaseLine = m_Height - descent;
   }

   // tell next line about coordinate change
   if(m_Next && m_Height != heightOld)
   {
      m_Next->MarkDirty();
   }
   
   // We need to check whether we found a valid cursor size:
   if(cursorPos && cursorSize)
   {
      // this might be the case if the cursor is at the end of the
      // line or on a command object:
      if(cursorSize->x < WXLO_MINIMUM_CURSOR_WIDTH)
      {
         CoordType width, height, descent;
         dc.GetTextExtent(WXLO_CURSORCHAR, &width, &height, &descent);
         cursorSize->x = width;
         cursorSize->y = height;
      }
      if(m_BaseLine >= cursorSize->y) // the normal case anyway
         cursorPos->y += m_BaseLine-cursorSize->y;
   }
   MarkClean();
}


wxLayoutLine *
wxLayoutLine::Break(CoordType xpos, wxLayoutList *llist)
{
   wxASSERT(xpos >= 0);

   MarkDirty(xpos);

   CoordType offset;
   wxLOiterator i = FindObject(xpos, &offset);
   if(i == NULLIT)
      // must be at the end of the line then
      return new wxLayoutLine(this, llist);
   // split this line:

   wxLayoutLine *newLine = new wxLayoutLine(this, llist);
   // split object at i:
   if((**i).GetType() == WXLO_TYPE_TEXT
      && offset != 0
      && offset != (**i).GetLength() )
   {
      wxString left, right;
      wxLayoutObjectText *tobj = (wxLayoutObjectText *) *i;
      left = tobj->GetText().substr(0,offset);
      right = tobj->GetText().substr(offset,tobj->GetLength()-offset);
      // current text object gets set to left half
      tobj->GetText() = left; // set new text
      newLine->Append(new wxLayoutObjectText(right));
      m_Length -= right.Length();
      i++; // don't move this object to the new list
   }
   else
   {
      if(offset > 0)
         i++; // move objects from here to new list
   }

   while(i != m_ObjectList.end())
   {
      wxLayoutObject *obj = *i;
      newLine->Append(obj);
      m_Length -= obj->GetLength();

      m_ObjectList.remove(i); // remove without deleting it
   }
   if(m_Next)
      m_Next->MarkDirty();
   return newLine;
}

bool
wxLayoutLine::Wrap(CoordType wrapmargin, wxLayoutList *llist)
{
   if(GetLength() < wrapmargin)
      return FALSE; // nothing to do
   
   // find the object which covers the wrapmargin:
   CoordType offset;
   wxLOiterator i = FindObject(wrapmargin, &offset);
   wxCHECK_MSG( i != NULLIT, FALSE,
                wxT("Cannot find object covering wrapmargin."));
   
   // from this object on, the rest of the line must be copied to the
   // next one:
   wxLOiterator copyObject = NULLIT;
   // if we split a text-object, we must pre-pend some text to the
   // next line later on, remember it here:
   wxString prependText = "";
   // we might need to adjust the cursor position later, so remember it
   size_t xpos = llist->GetCursorPos().x;
   // by how much did we shorten the current line:
   size_t shorter = 0;
   // remember cursor location of object
   size_t objectCursorPos = 0;
   
   size_t breakpos = offset;

   if( (**i).GetType() != WXLO_TYPE_TEXT )
   {
      // break before a non-text object
      copyObject = i;
   }
   else
   {
      bool foundSpace = FALSE;
      do
      {
//         while(i != NULLIT && (**i).GetType() != WXLO_TYPE_TEXT)
//            i--;
         // try to find a suitable place to split the object:
         wxLayoutObjectText *tobj = (wxLayoutObjectText *)*i;
         if((**i).GetType() == WXLO_TYPE_TEXT
            && tobj->GetText().Length() >= breakpos)
         {
            do
            {
               foundSpace = isspace(tobj->GetText()[breakpos]) != 0;
               if ( foundSpace )
                  break;
            }
            while ( breakpos-- > 0 );
         }
         else
         {
            breakpos = 0;
         }

         if(! foundSpace) // breakpos == 0!
         {
            if(i == m_ObjectList.begin())
               return FALSE; // could not break line
            else
            {
               i--;
               while(i != m_ObjectList.begin()
                     && (**i).GetType() != WXLO_TYPE_TEXT )
               {
                  i--;
               }
               breakpos = (**i).GetLength();
            }
         }
      }while(! foundSpace);
      // before we actually break the object, we need to know at which
      // cursorposition it starts, so we can restore the cursor if needed:
      if( this == llist->GetCursorLine() && xpos >= breakpos )
      {
         for(wxLOiterator j = m_ObjectList.begin();
             j != NULLIT && j != i; j++)
            objectCursorPos += (**j).GetLength();
      }
      // now we know where to break it:
      wxLayoutObjectText *tobj = (wxLayoutObjectText *)*i;
      shorter = tobj->GetLength() - breakpos;
      // remember text to copy from this object
      prependText = tobj->GetText().Mid(breakpos+1);
      tobj->SetText(tobj->GetText().Left(breakpos));
      // copy every following object:
      copyObject = i; copyObject ++;
   }

   // make sure there is an empty m_Next line:
   (void) new wxLayoutLine(this, llist);
   wxASSERT(m_Next);
   // We need to move this and all following objects to the next
   // line. Starting from the end of line, to keep the order right. 
   if(copyObject != NULLIT)
   {
      wxLOiterator j;
      for(j = m_ObjectList.tail(); j != copyObject; j--)
         m_Next->Prepend(*j);
      m_Next->Prepend(*copyObject);
      // and now remove them from this list:
      while( copyObject != m_ObjectList.end() )
      {
         shorter += (**copyObject).GetLength();
         m_ObjectList.remove(copyObject); // remove without deleting it
      }
   }
   m_Length -= shorter;
   
   if(prependText.Length() > 0)
      m_Next->Insert(0, prependText);

   // do we need to adjust the cursor position?
   if( this == llist->GetCursorLine() && xpos >= breakpos)
   {
      xpos = objectCursorPos + (xpos - objectCursorPos - breakpos -
                                ((xpos > breakpos) ? 1 : 0 ));
      wxASSERT(xpos >= 0);
      llist->MoveCursorTo( wxPoint( xpos, m_Next->GetLineNumber()) );
   }
   return TRUE; // we wrapped the line
}

void
wxLayoutLine::ReNumber(void)
{
   CoordType lineNo = m_Previous ? m_Previous->m_LineNumber+1 : 0;
   m_LineNumber = lineNo++;
   
   for(wxLayoutLine *next = GetNextLine();
       next; next = next->GetNextLine())
      next->m_LineNumber = lineNo++;
}

void
wxLayoutLine::MergeNextLine(wxLayoutList *llist)
{
   wxCHECK_RET( GetNextLine(),
                wxT("wxLayout internal error: no next line to merge"));
   wxLayoutObjectList &list = GetNextLine()->m_ObjectList;
   wxLOiterator i;

   MarkDirty(GetWidth());

   wxLayoutObject *last = NULL;
   for(i = list.begin(); i != list.end();)
   {
      wxLayoutObject *current = *i;

      // merge text objects together for efficiency
      if ( last && last->GetType() == WXLO_TYPE_TEXT &&
                   current->GetType() == WXLO_TYPE_TEXT )
      {
         wxLayoutObjectText *textObj = (wxLayoutObjectText *)last;
         wxString text(textObj->GetText());
         text += ((wxLayoutObjectText *)current)->GetText();
         textObj->SetText(text);

         list.erase(i); // remove and delete it
      }
      else
      {
         // just append the object "as was"
         Append(current);

         list.remove(i); // remove without deleting it
      }
   }
   wxASSERT(list.empty());

   wxLayoutLine *oldnext = GetNextLine();
   wxLayoutLine *nextLine = oldnext->GetNextLine();
   SetNext(nextLine);
   if ( nextLine )
   {
      nextLine->ReNumber();
   }
   else
   {
       // this is now done in Delete(), but if this function is ever called
       // from elsewhere, we might have to move refresh code back here (in
       // order not to duplicate it)
#if 0
       wxPoint pos(oldnext->GetPosition());
       llist->SetUpdateRect(pos);
       llist->SetUpdateRect(pos.x + oldnext->GetWidth() + MSW_CORRECTION,
                            pos.y + oldnext->GetHeight() + MSW_CORRECTION);
#endif // 0
   }

   llist->DecNumLines();

   delete oldnext;
}

CoordType
wxLayoutLine::GetWrapPosition(CoordType column)
{
   CoordType offset;
   wxLOiterator i = FindObject(column, &offset);
   if(i == NULLIT) return -1; // cannot wrap

   // go backwards through the list and look for space in text objects
   do
   {
      if((**i).GetType() == WXLO_TYPE_TEXT)
      {
         do
         {
            if(isspace(((wxLayoutObjectText*)*i)->GetText().c_str()[(size_t)offset]))
               return column;
            else
            {
               offset--;
               column--;
            }
         }while(offset != -1);
         i--;  // move on to previous object
      }
      else
      {
         column -= (**i).GetLength();
         i--;
      }
      if( i != NULLIT)
         offset = (**i).GetLength();
   }while(i != NULLIT);
   /* If we reached the begin of the list and have more than one
      object, that one is longer than the margin, so break behind
      it. */
   CoordType pos = 0;
   i = m_ObjectList.begin();
   while(i != NULLIT && (**i).GetType() != WXLO_TYPE_TEXT)
   {
      pos += (**i).GetLength();
      i++;
   }
   if(i == NULLIT) return -1;  //why should this happen?

   // now we are behind the one long text object and need to find the
   // first space in it
   for(offset = 0; offset < (**i).GetLength(); offset++)
      if( isspace(((wxLayoutObjectText*)*i)->GetText().c_str()[(size_t)offset]))
      {
         return pos+offset;
      }
   pos += (**i).GetLength();
   return pos;
}


#ifdef WXLAYOUT_DEBUG
void
wxLayoutLine::Debug(void) const
{
   wxPoint pos = GetPosition();
   WXLO_DEBUG((wxT("Line %ld, Pos (%ld,%ld), Height %ld, BL %ld, Font: %d"),
               (long int) GetLineNumber(),
               (long int) pos.x, (long int) pos.y,
               (long int) GetHeight(),
               (long int) m_BaseLine,
               (int) m_StyleInfo.family));
   if(m_ObjectList.begin() != NULLIT)
   {
      WXLO_DEBUG(((**m_ObjectList.begin()).DebugDump().c_str()));
   }

}
#endif

void
wxLayoutLine::Copy(wxLayoutList *llist,
                   CoordType from,
                   CoordType to)
{
   CoordType firstOffset, lastOffset;

   if(to == -1) to = GetLength();
   if(from == to) return;

   wxLOiterator first = FindObject(from, &firstOffset);
   wxLOiterator last  = FindObject(to, &lastOffset);

   // Common special case: only one object
   if( first != NULLIT && last != NULLIT && *first == *last )
   {
      if( (**first).GetType() == WXLO_TYPE_TEXT )
      {
         llist->Insert(new wxLayoutObjectText(
            ((wxLayoutObjectText
              *)*first)->GetText().substr(firstOffset,
                                          lastOffset-firstOffset))
            );
         return;
      }
      else // what can we do?
      {
         if(lastOffset > firstOffset) // i.e. +1 :-)
            llist->Insert( (**first).Copy() );
         return;
      }
   }

   // If we reach here, we can safely copy the whole first object from
   // the firstOffset position on:
   if((**first).GetType() == WXLO_TYPE_TEXT && firstOffset != 0)
   {
      llist->Insert(new wxLayoutObjectText(
         ((wxLayoutObjectText *)*first)->GetText().substr(firstOffset))
         );
   }
   else if(firstOffset == 0)
      llist->Insert( (**first).Copy() );
   // else nothing to copy :-(

   // Now we copy all objects before the last one:
   wxLOiterator i = first; i++;
   for( ; i != last; i++)
      llist->Insert( (**i).Copy() );

   // And now the last object:
   if(lastOffset != 0)
   {
      if( (**last).GetType() == WXLO_TYPE_TEXT )
      {
         llist->Insert(new wxLayoutObjectText(
            ((wxLayoutObjectText *)*last)->GetText().substr(0,lastOffset))
            );
      }
      else
         llist->Insert( (**last).Copy() );
   }
}


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

   The wxLayoutList object

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

wxLayoutList::wxLayoutList()
{
#ifdef WXLAYOUT_USE_CARET
   m_caret = NULL;
#endif // WXLAYOUT_USE_CARET

   m_numLines = 0;
   m_FirstLine = NULL;
   SetAutoFormatting(TRUE);
   ForceTotalLayout(TRUE);  // for the first time, do all
   InvalidateUpdateRect();
   Clear();
}

wxLayoutList::~wxLayoutList()
{
   SetAutoFormatting(FALSE);
   InternalClear();
   Empty();
   m_FirstLine->DeleteLine(false, this);

   wxASSERT_MSG( m_numLines == 0, wxT("line count calculation broken"));
}

void
wxLayoutList::Empty(void)
{
   while(m_FirstLine)
      m_FirstLine = m_FirstLine->DeleteLine(false, this);

   m_CursorPos = wxPoint(0,0);
   m_CursorScreenPos = wxPoint(0,0);
   m_CursorSize = wxPoint(0,0);
   m_movedCursor = true;
   m_FirstLine = new wxLayoutLine(NULL, this); // empty first line
   m_CursorLine = m_FirstLine;
   InvalidateUpdateRect();
}


void
wxLayoutList::InternalClear(void)
{
   m_Selection.m_selecting = false;
   m_Selection.m_valid = false;

   m_DefaultStyleInfo.family = wxSWISS;
   m_DefaultStyleInfo.size = WXLO_DEFAULTFONTSIZE;
   m_DefaultStyleInfo.style = wxNORMAL;
   m_DefaultStyleInfo.weight = wxNORMAL;
   m_DefaultStyleInfo.underline = 0;
   m_DefaultStyleInfo.m_fg_valid = TRUE;
   m_DefaultStyleInfo.m_fg = *wxBLACK;
   m_DefaultStyleInfo.m_bg_valid = TRUE;
   m_DefaultStyleInfo.m_bg = *wxWHITE;

   m_CurrentStyleInfo = m_DefaultStyleInfo;
   m_CursorStyleInfo = m_DefaultStyleInfo;
}

void
wxLayoutList::Read(wxString &istr)
{
   /* In order to handle input of formatted string "nicely", we need
      to restore our current font settings after the string. So first
      of all, we create a StyleInfo structure with our current
      settings. */
   wxLayoutStyleInfo current_si = GetStyleInfo();

   while(istr.Length())
   {
      // check for a linebreak:
      wxString tmp;
      tmp = istr.BeforeFirst('\n');
      long l = WXLO_TYPE_INVALID;
      tmp.ToLong(&l);
      int type = (int) l;

      if(type == WXLO_TYPE_LINEBREAK)
      {
         LineBreak();
         istr = istr.AfterFirst('\n');
      }
      else
      {
         wxLayoutObject *obj = wxLayoutObject::Read(istr);
         if(obj)
            Insert(obj);
      }
   }
   /* Now we use the current_si to restore our last font settings: */
   Insert(new wxLayoutObjectCmd(current_si));
}


void
wxLayoutList::SetFont(int family, int size, int style, int weight,
                      int underline, wxColour *fg,
                      wxColour *bg)
{
   if(family != -1)    m_CurrentStyleInfo.family = family;
   if(size != -1)      m_CurrentStyleInfo.size = size;
   if(style != -1)     m_CurrentStyleInfo.style = style;
   if(weight != -1)    m_CurrentStyleInfo.weight = weight;
   if(underline != -1) m_CurrentStyleInfo.underline = underline != 0;
   if(fg) m_CurrentStyleInfo.m_fg = *fg;
   if(bg) m_CurrentStyleInfo.m_bg = *bg;
   Insert(
      new wxLayoutObjectCmd(
         m_CurrentStyleInfo.family,
         m_CurrentStyleInfo.size,
         m_CurrentStyleInfo.style,
         m_CurrentStyleInfo.weight,
         m_CurrentStyleInfo.underline,
         fg, bg));
}

void
wxLayoutList::SetFont(int family, int size, int style, int weight,
                      int underline, char const *fg, char const *bg)

{
   wxColour
      *cfg = NULL,
      *cbg = NULL;

   if( fg )
      cfg = wxTheColourDatabase->FindColour(fg);
   if( bg )
      cbg = wxTheColourDatabase->FindColour(bg);

   SetFont(family,size,style,weight,underline,cfg,cbg);
}

void
wxLayoutList::Clear(int family, int size, int style, int weight,
                    int underline, wxColour *fg, wxColour *bg)
{
   InternalClear();
   m_DefaultStyleInfo = wxLayoutStyleInfo(family, size, style, weight,
                                        underline, fg, bg);
   m_CurrentStyleInfo = m_DefaultStyleInfo;

   // Empty() should be called after we set m_DefaultStyleInfo because
   // otherwise the style info for the first line (created in Empty()) would be
   // incorrect
   Empty();
}

wxPoint
wxLayoutList::FindText(const wxString &needle, const wxPoint &cpos) const
{
   int xpos;

   wxLayoutLine *line;
   for(line = m_FirstLine;
       line;
       line = line->GetNextLine())
   {
      if(line->GetLineNumber() >= cpos.y)
      {
         xpos = line->FindText(needle,
                               (line->GetLineNumber() == cpos.y) ?
                               cpos.x : 0);
         if(xpos != -1)
            return wxPoint(xpos, line->GetLineNumber());
      }
   }
   return wxPoint(-1,-1);
}


bool
wxLayoutList::MoveCursorTo(wxPoint const &p)
{
   AddCursorPosToUpdateRect();

   wxPoint cursorPosOld = m_CursorPos;

   wxLayoutLine *line = m_FirstLine;
   while(line && line->GetLineNumber() != p.y)
      line = line->GetNextLine();
   if(line && line->GetLineNumber() == p.y) // found it
   {
      m_CursorPos.y = p.y;
      m_CursorLine = line;
      CoordType len = line->GetLength();
      if(len >= p.x)
      {
         m_CursorPos.x = p.x;
      }
      else
      {
         m_CursorPos.x = len;
      }
   }

   m_movedCursor = m_CursorPos != cursorPosOld;

   return m_CursorPos == p;
}

bool
wxLayoutList::MoveCursorVertically(int n)
{
   AddCursorPosToUpdateRect();

   wxPoint cursorPosOld = m_CursorPos;

   bool rc;
   if(n  < 0) // move up
   {
      if(m_CursorLine == m_FirstLine) return false;
      while(n < 0 && m_CursorLine)
      {
         m_CursorLine = m_CursorLine->GetPreviousLine();
         m_CursorPos.y--;
         n++;
      }
      if(! m_CursorLine)
      {
         m_CursorLine = m_FirstLine;
         m_CursorPos.y = 0;
         rc = false;
      }
      else
      {
         if(m_CursorPos.x > m_CursorLine->GetLength())
            m_CursorPos.x = m_CursorLine->GetLength();
         rc = true;
      }
   }
   else // move down
   {
      wxLayoutLine *last = m_CursorLine;
      if(! m_CursorLine->GetNextLine()) return false;
      while(n > 0 && m_CursorLine)
      {
         n--;
         m_CursorPos.y ++;
         last = m_CursorLine;
         m_CursorLine = m_CursorLine->GetNextLine();
      }
      if(! m_CursorLine)
      {
         m_CursorLine = last;
         m_CursorPos.y --;
         rc = false;
      }
      else
      {
         if(m_CursorPos.x > m_CursorLine->GetLength())
            m_CursorPos.x = m_CursorLine->GetLength();
         rc = true;
      }
   }

   m_movedCursor = m_CursorPos != cursorPosOld;

   return rc;
}

bool
wxLayoutList::MoveCursorHorizontally(int n)
{
   AddCursorPosToUpdateRect();

   wxPoint cursorPosOld = m_CursorPos;

   int move;
   while(n < 0)
   {
      if(m_CursorPos.x == 0) // at begin of line
      {
         if(! MoveCursorVertically(-1))
            break;
         MoveCursorToEndOfLine();
         n++;
         continue;
      }
      move = -n;
      if(move > m_CursorPos.x) move = m_CursorPos.x;
      m_CursorPos.x -= move; n += move;
   }

   while(n > 0)
   {
      int len =  m_CursorLine->GetLength();
      if(m_CursorPos.x == len) // at end of line
      {
         if(! MoveCursorVertically(1))
            break;
         MoveCursorToBeginOfLine();
         n--;
         continue;
      }
      move = n;
      if( move >= len-m_CursorPos.x) move = len-m_CursorPos.x;
      m_CursorPos.x += move;
      n -= move;
   }

   m_movedCursor = m_CursorPos != cursorPosOld;

   return n == 0;
}

bool
wxLayoutList::MoveCursorWord(int n, bool untilNext)
{
   wxCHECK_MSG( m_CursorLine, false, wxT("no current line") );
   wxCHECK_MSG( n == -1 || n == +1, false, wxT("not implemented yet") );

   CoordType moveDistance = 0;
   CoordType offset;
   wxLayoutLine *lineCur = m_CursorLine;
   for ( wxLOiterator i = lineCur->FindObject(m_CursorPos.x, &offset);
         n != 0;
         n > 0 ? i++ : i-- )
   {
      if ( i == NULLIT )
      {
         if ( n > 0 )
         {
            // moving forward, pass to the first object of the next line
            moveDistance++;
            lineCur = lineCur->GetNextLine();
            if ( lineCur )
               i = lineCur->GetFirstObject();
         }
         else
         {
            // moving backwards, pass to the last object of the prev line
            moveDistance--;
            lineCur = lineCur->GetPreviousLine();
            if ( lineCur )
               i = lineCur->GetLastObject();
         }

         if ( i == NULLIT )
         {
            // moved to the end/beginning of text
            return false;
         }

         offset = -1;
      }

      wxLayoutObject *obj = *i;

      if ( offset == -1 )
      {
         // calculate offset: we are either at the very beginning or the very
         // end of the object, so it isn't very difficult (the only time when
         // offset is != -1 is for the very first iteration when its value is
         // returned by FindObject)
         if ( n > 0 )
            offset = 0;
         else
            offset = obj->GetLength();
      }

      if( obj->GetType() != WXLO_TYPE_TEXT )
      {
         // any visible non text objects count as one word
         if ( obj->IsVisibleObject() )
         {
            n > 0 ? n-- : n++;

            moveDistance += obj->GetLength();
         }
      }
      else // text object
      {
         wxLayoutObjectText *tobj = (wxLayoutObjectText *)obj;

         bool canAdvance = true;

         if ( offset == tobj->GetLength() )
         {
            // at end of object
            if ( n > 0 )
            {
               // can't move further in this text object
               canAdvance = false;

               // still should move over the object border
               moveDistance++;
               n--;
            }
            else if ( offset > 0 )
            {
               // offset is off by 1, make it a valid index
               offset--;
            }
         }

         if ( canAdvance )
         {
            const wxString& text = tobj->GetText();
            const wxChar *start = text.c_str();
            const wxChar *end = start + text.length();
            const wxChar *p = start + offset;

            if ( n < 0 )
            {
               if ( offset > 0 )
                  p--;
            }

            // to the beginning/end of the next/prev word
            while ( p >= start && p < end && isspace(*p) )
            {
               n > 0 ? p++ : p--;
            }

            // go to the end/beginning of the word (in a broad sense...)
            while ( p >= start && p < end && !isspace(*p) )
            {
               n > 0 ? p++ : p--;
            }

            if ( n > 0 )
            {
               if ( untilNext )
               {
                  // now advance to the beginning of the next word
                  while ( isspace(*p) && p < end )
                     p++;
               }
            }
            else // backwards
            {
               // in these 2 cases we took 1 char too much
               if ( (p < start) || isspace(*p) )
               {
                  p++;
               }
            }

            CoordType moveDelta = p - start - offset;
            if ( (n < 0) && (offset == tobj->GetLength() - 1) )
            {
               // because we substracted 1 from offset in this case above, now
               // compensate for it
               moveDelta--;
            }

            if ( moveDelta != 0 )
            {
               moveDistance += moveDelta;

               n > 0 ? n-- : n++;
            }
         }
      }

      // except for the first iteration, offset is calculated in the beginning
      // of the loop
      offset = -1;
   }

   MoveCursorHorizontally(moveDistance);

   return true;
}

bool
wxLayoutList::Insert(wxString const &text)
{
   wxASSERT(m_CursorLine);
   wxASSERT_MSG( text.Find(wxT('\n')) == wxNOT_FOUND,
                 wxT("use wxLayoutImportText!") );

   if ( !text )
       return true;

   AddCursorPosToUpdateRect();

   wxASSERT(m_CursorLine->GetLength() >= m_CursorPos.x);

   if ( !m_CursorLine->Insert(m_CursorPos.x, text) )
      return false;
   m_CursorPos.x += text.Length();

   m_movedCursor = true;

   if(m_AutoFormat)
      m_CursorLine->MarkDirty();

   return true;
}

bool
wxLayoutList::Insert(wxLayoutObject *obj)
{
   wxASSERT(m_CursorLine);

   if(! m_CursorLine)
      m_CursorLine = GetFirstLine();

   AddCursorPosToUpdateRect();

   m_CursorLine->Insert(m_CursorPos.x, obj);
   m_CursorPos.x += obj->GetLength();
   m_movedCursor = true;

   if(m_AutoFormat)
      m_CursorLine->MarkDirty();

   return true;
}

bool
wxLayoutList::Insert(wxLayoutList *llist)
{
   wxASSERT(llist);
   bool rc = TRUE;

   for(wxLayoutLine *line = llist->GetFirstLine();
       line;
       line = line->GetNextLine()
      )
   {
      for(wxLOiterator i = line->GetFirstObject();
          i != NULLIT;
          i++)
         rc |= Insert(*i);
      LineBreak();
   }
   return rc;
}

bool
wxLayoutList::LineBreak(void)
{
   wxASSERT(m_CursorLine);

   AddCursorPosToUpdateRect();

   wxPoint position(m_CursorLine->GetPosition());

   CoordType
      width = m_CursorLine->GetWidth(),
      height = m_CursorLine->GetHeight();

   m_CursorLine = m_CursorLine->Break(m_CursorPos.x, this);
   if(m_CursorLine->GetPreviousLine() == NULL)
      m_FirstLine = m_CursorLine;
   m_CursorPos.y++;
   m_CursorPos.x = 0;

   // The following code will produce a height which is guaranteed to
   // be too high: old lineheight + the height of both new lines.
   // We can probably drop the old line height and start with height = 
   // 0. FIXME
   wxLayoutLine *prev = m_CursorLine->GetPreviousLine();
   if(prev)
      height += prev->GetHeight();
   height += m_CursorLine->GetHeight();

   m_movedCursor = true;

   SetUpdateRect(position);
   SetUpdateRect(position.x + width + MSW_CORRECTION,
                 position.y + height + MSW_CORRECTION);

   return true;
}

bool
wxLayoutList::WrapLine(CoordType column)
{
   return m_CursorLine->Wrap(column, this);
}

bool
wxLayoutList::WrapAll(CoordType column)
{
   wxLayoutLine *line = m_FirstLine;
   if(! line)
      return FALSE;
   bool rc = TRUE;
   while(line && rc)
   {
      rc &= line->Wrap(column, this);
      line = line->GetNextLine();
   }
   return rc;
}

bool
wxLayoutList::Delete(CoordType npos)
{
   wxCHECK_MSG(m_CursorLine, false, wxT("can't delete in non existing line"));

   if ( npos == 0 )
       return true;

   AddCursorPosToUpdateRect();

   // were other lines appended to this one (this is important to know because
   // this means that our width _increased_ as the result of deletion)
   bool wasMerged = false;

   // the size of the region to update
   CoordType totalHeight = m_CursorLine->GetHeight(),
             totalWidth = m_CursorLine->GetWidth();

   CoordType left;
   do
   {
      left = m_CursorLine->Delete(m_CursorPos.x, npos);

      if( left > 0 )
      {
         // More to delete, continue on next line.

         // First, check if line is empty:
         if(m_CursorLine->GetLength() == 0)
         {
            // in this case, updating could probably be optimised
#ifdef WXLO_DEBUG
            wxASSERT(DeleteLines(1) == 0);
#else
            DeleteLines(1);
#endif

            left--;
         }
         else
         {
            // Need to join next line
            if(! m_CursorLine->GetNextLine())
               break; // cannot
            else
            {
               wasMerged = true;
               wxLayoutLine *next = m_CursorLine->GetNextLine();
               if ( next )
               {
                  totalHeight += next->GetHeight();
                  totalWidth += next->GetWidth();

                  m_CursorLine->MergeNextLine(this);
                  left--;
               }
               else
               {
                  wxFAIL_MSG(wxT("can't delete all this"));

                  return false;
               }
            }
         }
      }
   }
   while ( left> 0 );

   // we need to update the whole tail of the line and the lines which
   // disappeared
   if ( wasMerged )
   {
      wxPoint position(m_CursorLine->GetPosition());
      SetUpdateRect(position);
      SetUpdateRect(position.x + totalWidth + MSW_CORRECTION,
                    position.y + totalHeight + MSW_CORRECTION);
   }

   return left == 0;
}

int
wxLayoutList::DeleteLines(int n)
{
   wxASSERT(m_CursorLine);
   wxLayoutLine *line;

   AddCursorPosToUpdateRect();

   while(n > 0)
   {
      if(!m_CursorLine->GetNextLine())
      {  // we cannot delete this line, but we can clear it
         MoveCursorToBeginOfLine();
         DeleteToEndOfLine();
         if(m_AutoFormat)
            m_CursorLine->MarkDirty();
         return n-1;
      }
      //else:
      line = m_CursorLine;
      m_CursorLine = m_CursorLine->DeleteLine(true, this);
      n--;
      if(line == m_FirstLine) m_FirstLine = m_CursorLine;
      wxASSERT(m_FirstLine);
      wxASSERT(m_CursorLine);
   }
   if(m_AutoFormat)
      m_CursorLine->MarkDirty();
   return n;
}

void
wxLayoutList::Recalculate(wxDC &dc, CoordType bottom)
{
   if(! m_AutoFormat)
      return;
   wxLayoutLine *line = m_FirstLine;

   // first, make sure everything is calculated - this might not be
   // needed, optimise it later
   ApplyStyle(m_DefaultStyleInfo, dc);
   while(line)
   {
      line->RecalculatePosition(this); // so we don't need to do it all the time
      // little condition to speed up redrawing:
      if(bottom != -1 && line->GetPosition().y > bottom) break;
      line = line->GetNextLine();
   }
}

wxPoint
wxLayoutList::GetCursorScreenPos(void) const
{
   return m_CursorScreenPos;
}

/*
  Is called before each Draw(). Now, it will re-layout all lines which
  have changed.
*/
void
wxLayoutList::Layout(wxDC &dc, CoordType bottom, bool forceAll,
                     wxPoint *cpos, wxPoint *csize)
{
   // first, make sure everything is calculated - this might not be
   // needed, optimise it later
   ApplyStyle(m_DefaultStyleInfo, dc);

   
   if(m_ReLayoutAll)
   {
      forceAll = TRUE;
      bottom = -1;
   }
   ForceTotalLayout(FALSE);
   

   // If one line was dirty, we need to re-calculate all
   // following lines, too.
   bool wasDirty = forceAll;
   // we need to layout until we reach at least the cursor line,
   // otherwise we won't be able to scroll to it
   bool cursorReached = false;
   wxLayoutLine *line = m_FirstLine;
   while(line)
   {
      if(! wasDirty)
         ApplyStyle(line->GetStyleInfo(), dc);
      if(
         // if any previous line was dirty, we need to layout all
         // following lines:   
         wasDirty
         // go on until we find the cursorline
         || ! cursorReached
         // layout dirty lines:
         || line->IsDirty()
         // always layout the cursor line toupdate the cursor
         // position and size:
         || line == m_CursorLine
         // or if it's the line we are asked to look for:
         || (cpos && line->GetLineNumber() == cpos->y) 
         // layout at least the desired region:
         || (bottom == -1 )
         || (line->GetPosition().y <= bottom)
         )
      {
         if(line->IsDirty())
            wasDirty = true;
         
         // The following Layout() calls will update our
         // m_CurrentStyleInfo if needed.
         if(line == m_CursorLine)
         {
            line->Layout(dc, this,
                         (wxPoint *)&m_CursorScreenPos,
                         (wxPoint *)&m_CursorSize,
                         &m_CursorStyleInfo,
                         m_CursorPos.x);
            // we cannot layout the line twice, so copy the coords:
            if(cpos && line ->GetLineNumber() == cpos->y)
            {
               *cpos = m_CursorScreenPos;
               if ( csize )
                  *csize = m_CursorSize;
            }
            cursorReached = TRUE;
         } 
         else
         {
            if(cpos && line->GetLineNumber() == cpos->y)
            {
               line->Layout(dc, this,
                            cpos,
                            csize, NULL, cpos->x);
               cursorReached = TRUE;
            }
            else
               line->Layout(dc, this);
         }
      }
      line = line->GetNextLine();
   }
   
#ifndef WXLAYOUT_USE_CARET
   // can only be 0 if we are on the first line and have no next line
   wxASSERT(m_CursorSize.x != 0 || (m_CursorLine &&
                                    m_CursorLine->GetNextLine() == NULL &&
                                    m_CursorLine == m_FirstLine));
#endif // WXLAYOUT_USE_CARET
   AddCursorPosToUpdateRect();
}

wxPoint
wxLayoutList::GetScreenPos(wxDC &dc, const wxPoint &cpos, wxPoint *csize)
{
   wxPoint pos = cpos;
   Layout(dc, -1, false, &pos, csize);
   return pos;
}

void
wxLayoutList::Draw(wxDC &dc,
                   wxPoint const &offset,
                   CoordType top,
                   CoordType bottom,
                   bool clipStrictly)
{
   wxLayoutLine *line = m_FirstLine;

   if ( m_Selection.m_discarded )
   {
      // calculate them if we don't have them already
      if ( !m_Selection.HasValidScreenCoords() )
      {
         m_Selection.m_ScreenA = GetScreenPos(dc, m_Selection.m_CursorA);
         m_Selection.m_ScreenB = GetScreenPos(dc, m_Selection.m_CursorB);
      }

      // invalidate the area which was previousle selected - and which is not
      // selected any more
      SetUpdateRect(m_Selection.m_ScreenA);
      SetUpdateRect(m_Selection.m_ScreenB);

      m_Selection.m_discarded = false;
   }

   /* This call to Layout() will re-calculate and update all lines
      marked as dirty.
   */
   Layout(dc, bottom);
   
   ApplyStyle(m_DefaultStyleInfo, dc);
   wxBrush brush(m_CurrentStyleInfo.m_bg, wxSOLID);
   dc.SetBrush(brush);
   dc.SetBackgroundMode(wxTRANSPARENT);

   while(line)
   {
      // only draw if between top and bottom:
      if((top == -1 ||
          line->GetPosition().y + line->GetHeight() > top))
      {
         ApplyStyle(line->GetStyleInfo(), dc);
         // little condition to speed up redrawing:
         if( bottom != -1
             && line->GetPosition().y
                +(clipStrictly ? line->GetHeight() : 0) >= bottom)
            break;
         line->Draw(dc, this, offset);
      }
      line = line->GetNextLine();
   }
   InvalidateUpdateRect();

   WXLO_DEBUG((wxT("Selection is %s : %ld,%ld/%ld,%ld"),
               m_Selection.m_valid ? wxT("valid") : wxT("invalid"),
               m_Selection.m_CursorA.x, m_Selection.m_CursorA.y,
               m_Selection.m_CursorB.x, m_Selection.m_CursorB.y));
}

wxLayoutObject *
wxLayoutList::FindObjectScreen(wxDC &dc, wxPoint const pos,
                               wxPoint *cursorPos,
                               bool *found)
{
   // First, find the right line:
   wxLayoutLine
      *line = m_FirstLine,
      *lastline = m_FirstLine;
   wxPoint p;

   ApplyStyle(m_DefaultStyleInfo, dc);
   while(line)
   {
      p = line->GetPosition();
      if(p.y <= pos.y && p.y+line->GetHeight() >= pos.y)
         break;
      lastline = line;
      line = line->GetNextLine();
   }

   bool didFind = line != NULL;

   if ( !line )
   {
      // use the last line:
      line = lastline;
   }

   if ( cursorPos )
       cursorPos->y = line->GetLineNumber();

   bool foundinline = true;
   long cx = 0;

   // Now, find the object in the line:
   wxLOiterator i;

   if (cursorPos)
   {
     i = line->FindObjectScreen(dc, this,
                                           pos.x,
                                           &cx,
                                           &foundinline);
     cursorPos->x = cx;
   }
   else
     i = line->FindObjectScreen(dc, this,
                                           pos.x,
                                           NULL,
                                           &foundinline);
   if ( found )
      *found = didFind && foundinline;

   return (i == NULLIT) ? NULL : *i;

}

wxPoint
wxLayoutList::GetSize(void) const
{
   wxLayoutLine
      *line = m_FirstLine,
      *last = line;
   if(! line)
      return wxPoint(0,0);

   wxPoint maxPoint(0,0);

   // find last line:
   while(line)
   {
      if(line->GetWidth() > maxPoint.x)
          maxPoint.x = line->GetWidth();
      last = line;
      line = line->GetNextLine();
   }

   maxPoint.y = last->GetPosition().y + last->GetHeight();

   // if the line was just added, its height would be 0 and we can't call
   // Layout() from here because we don't have a dc and we might be not drawing
   // at all, besides... So take the cursor height by default (taking 0 is bad
   // because then the scrollbars won't be resized and the new line won't be
   // shown at all)
   if ( last->IsDirty() )
   {
      if ( last->GetHeight() == 0 )
         maxPoint.y += m_CursorSize.y;
      if ( last->GetWidth() == 0 && maxPoint.x < m_CursorSize.x )
         maxPoint.x = m_CursorSize.x;
   }

   return maxPoint;
}


void
wxLayoutList::DrawCursor(wxDC &dc, bool active, wxPoint const &translate)
{
   if ( m_movedCursor )
      m_movedCursor = false;

   wxPoint coords(m_CursorScreenPos);
   coords += translate;

#ifdef WXLAYOUT_DEBUG
   WXLO_DEBUG((wxT("Drawing cursor (%ld,%ld) at %ld,%ld, size %ld,%ld, line: %ld, len %ld"),
               (long)m_CursorPos.x, (long)m_CursorPos.y,
               (long)coords.x, (long)coords.y, 
               (long)m_CursorSize.x, (long)m_CursorSize.y,
               (long)m_CursorLine->GetLineNumber(),
               (long)m_CursorLine->GetLength()));

   wxLogStatus(wxT("Cursor is at (%d, %d)"), m_CursorPos.x, m_CursorPos.y);
#endif

#ifdef WXLAYOUT_USE_CARET
   m_caret->Move(coords);
#else // !WXLAYOUT_USE_CARET

   wxASSERT(m_CursorSize.x >= WXLO_MINIMUM_CURSOR_WIDTH);
   dc.SetBrush(*wxWHITE_BRUSH);
   //FIXME: wxGTK XOR is borken at the moment!!!dc.SetLogicalFunction(wxXOR);
   dc.SetPen(wxPen(*wxBLACK,1,wxSOLID));
   if(active)
   {
      dc.SetLogicalFunction(wxXOR);
      dc.DrawRectangle(coords.x, coords.y,
                       m_CursorSize.x, m_CursorSize.y);
      SetUpdateRect(coords.x, coords.y);
      SetUpdateRect(coords.x+m_CursorSize.x,
                    coords.y+m_CursorSize.y);
   }
   else
   {
      dc.SetLogicalFunction(wxCOPY);
      dc.DrawLine(coords.x, coords.y+m_CursorSize.y-1,
                  coords.x, coords.y);
      SetUpdateRect(coords.x, coords.y+m_CursorSize.y-1);
      SetUpdateRect(coords.x, coords.y);
   }
   dc.SetLogicalFunction(wxCOPY);
   //dc.SetBrush(wxNullBrush);
#endif // WXLAYOUT_USE_CARET/!WXLAYOUT_USE_CARET
}

void
wxLayoutList::SetUpdateRect(CoordType x, CoordType y)
{
   if(m_UpdateRectValid)
      GrowRect(m_UpdateRect, x, y);
   else
   {
      m_UpdateRect.x = x;
      m_UpdateRect.y = y;
      m_UpdateRect.width = 4; // large enough to avoid surprises from
      m_UpdateRect.height = 4;// wxGTK :-)
      m_UpdateRectValid = true;
   }
}

void
wxLayoutList::StartSelection(const wxPoint& cposOrig, const wxPoint& spos)
{
   wxPoint cpos(cposOrig);
   if ( cpos.x == -1 )
      cpos = m_CursorPos;
   WXLO_DEBUG((wxT("Starting selection at %ld/%ld"), cpos.x, cpos.y));
   m_Selection.m_CursorA = cpos;
   m_Selection.m_CursorB = cpos;
   m_Selection.m_ScreenA = spos;
   m_Selection.m_ScreenB = spos;
   m_Selection.m_selecting = true;
   m_Selection.m_valid = false;
}

void
wxLayoutList::ContinueSelection(const wxPoint& cposOrig, const wxPoint& spos)
{
   wxPoint cpos(cposOrig);
   if(cpos.x == -1)
      cpos = m_CursorPos;

   wxASSERT(m_Selection.m_selecting == true);
   wxASSERT(m_Selection.m_valid == false);
   WXLO_DEBUG((wxT("Continuing selection at %ld/%ld"), cpos.x, cpos.y));

   m_Selection.m_ScreenB = spos;
   m_Selection.m_CursorB = cpos;
}

void
wxLayoutList::EndSelection(const wxPoint& cposOrig, const wxPoint& spos)
{
   wxPoint cpos(cposOrig);
   if(cpos.x == -1) cpos = m_CursorPos;
   ContinueSelection(cpos, spos);
   WXLO_DEBUG((wxT("Ending selection at %ld/%ld"), cpos.x, cpos.y));
   // we always want m_CursorA <= m_CursorB!
   if( m_Selection.m_CursorA > m_Selection.m_CursorB )
   {
      // exchange the start/end points
      wxPoint help = m_Selection.m_CursorB;
      m_Selection.m_CursorB = m_Selection.m_CursorA;
      m_Selection.m_CursorA = help;

      help = m_Selection.m_ScreenB;
      m_Selection.m_ScreenB = m_Selection.m_ScreenA;
      m_Selection.m_ScreenA = help;
   }
   m_Selection.m_selecting = false;
   m_Selection.m_valid = true;
   /// In case we just clicked somewhere, the selection will have zero 
   /// size, so we discard it immediately.
   if(m_Selection.m_CursorA == m_Selection.m_CursorB)
      DiscardSelection();
}

void
wxLayoutList::DiscardSelection()
{
   if ( !HasSelection() )
      return;

   m_Selection.m_valid =
   m_Selection.m_selecting = false;
   m_Selection.m_discarded = true;
}

bool
wxLayoutList::IsSelecting(void) const
{
   return m_Selection.m_selecting;
}

bool
wxLayoutList::IsSelected(const wxPoint &cursor) const
{
   if ( !HasSelection() )
      return false;

   return (
      (m_Selection.m_CursorA <= cursor
       && cursor <= m_Selection.m_CursorB) 
      || (m_Selection.m_CursorB <= cursor
          && cursor <= m_Selection.m_CursorA)
      );
}


/** Tests whether this layout line is selected and needs
    highlighting.
    @param line to test for
    @return 0 = not selected, 1 = fully selected, -1 = partially
    selected
    */
int
wxLayoutList::IsSelected(const wxLayoutLine *line, CoordType *from,
                         CoordType *to)
{
   wxASSERT(line); wxASSERT(to); wxASSERT(from);

   if(! m_Selection.m_valid && ! m_Selection.m_selecting)
      return 0;

   CoordType y = line->GetLineNumber();
   if(
      (m_Selection.m_CursorA.y < y && m_Selection.m_CursorB.y > y)
      || (m_Selection.m_CursorB.y < y && m_Selection.m_CursorA.y > y)
      )
      return 1;
   else if(m_Selection.m_CursorA.y == y)
   {
      *from = m_Selection.m_CursorA.x;
      if(m_Selection.m_CursorB.y == y)
         *to = m_Selection.m_CursorB.x;
      else
      {
         if(m_Selection.m_CursorB > m_Selection.m_CursorA)
            *to = line->GetLength();
         else
            *to = 0;
      }
      if(*to < *from)
      {
         CoordType help = *to;
         *to = *from;
         *from = help;
      }
      return -1;
   }
   else if(m_Selection.m_CursorB.y == y)
   {
      *to = m_Selection.m_CursorB.x;
      if(m_Selection.m_CursorA.y == y)
         *from = m_Selection.m_CursorA.x;
      else
      {
         if(m_Selection.m_CursorB > m_Selection.m_CursorA)
            *from = 0;
         else
            *from = line->GetLength();
      }
      if(*to < *from)
      {
         CoordType help = *to;
         *to = *from;
         *from = help;
      }
      return -1;
   }
   else
      return 0;
}

void
wxLayoutList::DeleteSelection(void)
{
   if(! m_Selection.m_valid)
      return;

   m_Selection.m_valid = false;

   // Only delete part of the current line?
   if(m_Selection.m_CursorA.y == m_Selection.m_CursorB.y)
   {
      MoveCursorTo(m_Selection.m_CursorA);
      Delete(m_Selection.m_CursorB.x - m_Selection.m_CursorA.x);
      return;
   }

   // We now know that the two lines are different:

   wxLayoutLine
      * firstLine = GetLine(m_Selection.m_CursorA.y),
      * lastLine = GetLine(m_Selection.m_CursorB.y);
   // be a bit paranoid:
   if(! firstLine || ! lastLine)
      return;
   
   // First, delete what's left of this line:
   MoveCursorTo(m_Selection.m_CursorA);
   DeleteToEndOfLine();

   wxLayoutLine *prevLine = firstLine->GetPreviousLine(),
                *nextLine = firstLine->GetNextLine();
   while(nextLine && nextLine != lastLine)
      nextLine = nextLine->DeleteLine(false, this);

   // Now nextLine = lastLine;
   Delete(1); // This joins firstLine and nextLine
   Delete(m_Selection.m_CursorB.x); // This deletes the first x positions

   // Recalculate the line positions and numbers but notice that firstLine
   // might not exist any more - it could be deleted by Delete(1) above
   wxLayoutLine *firstLine2 = prevLine ? prevLine->GetNextLine() : m_FirstLine;
   firstLine2->MarkDirty();
}

/// Starts highlighting the selection
void
wxLayoutList::StartHighlighting(wxDC &dc)
{
#if SHOW_SELECTIONS
   dc.SetTextForeground(m_CurrentStyleInfo.m_bg);
   dc.SetTextBackground(m_CurrentStyleInfo.m_fg);
   dc.SetBackgroundMode(wxSOLID);
#endif
}

/// Ends highlighting the selection
void
wxLayoutList::EndHighlighting(wxDC &dc)
{
#if SHOW_SELECTIONS
   dc.SetTextForeground(m_CurrentStyleInfo.m_fg);
   dc.SetTextBackground(m_CurrentStyleInfo.m_bg);
   dc.SetBackgroundMode(wxTRANSPARENT);
#endif
}


wxLayoutLine *
wxLayoutList::GetLine(CoordType index) const
{
   wxASSERT_MSG( (0 <= index) && (index < (CoordType)m_numLines),
                 wxT("invalid index") );

   wxLayoutLine *line;
   CoordType n = index;
#ifdef DEBUG
   CoordType lineNo = 0;
#endif
       
   for ( line = m_FirstLine; line && n-- > 0; line =
            line->GetNextLine() )
   {
#ifdef DEBUG
wxASSERT(line->GetLineNumber() == lineNo );
      lineNo++;
#endif
}

   if ( line )
   {
      // should be the right one
      wxASSERT( line->GetLineNumber() == index );
   }

   return line;
}


wxLayoutList *
wxLayoutList::Copy(const wxPoint &from,
                   const wxPoint &to)
{
   wxLayoutLine
      * firstLine = NULL,
      * lastLine = NULL;

   for(firstLine = m_FirstLine;
       firstLine && firstLine->GetLineNumber() < from.y;
       firstLine=firstLine->GetNextLine())
      ;
   if(!firstLine || firstLine->GetLineNumber() != from.y)
      return NULL;

   for(lastLine = m_FirstLine;
       lastLine && lastLine->GetLineNumber() < to.y;
       lastLine=lastLine->GetNextLine())
      ;
   if(!lastLine || lastLine->GetLineNumber() != to.y)
      return NULL;

   if(to <= from)
   {
      wxLayoutLine *tmp = firstLine;
      firstLine = lastLine;
      lastLine = tmp;
   }

   wxLayoutList *llist = new wxLayoutList();

   if(firstLine == lastLine)
   {
      firstLine->Copy(llist, from.x, to.x);
   }
   else
   {
      // Extract objects from first line
      firstLine->Copy(llist, from.x);
      llist->LineBreak();
      // Extract all lines between
      for(wxLayoutLine *line = firstLine->GetNextLine();
          line != lastLine;
          line = line->GetNextLine())
      {
         line->Copy(llist);
         llist->LineBreak();
      }
      // Extract objects from last line
      lastLine->Copy(llist, 0, to.x);
   }
   return llist;
}

wxLayoutList *
wxLayoutList::GetSelection(wxLayoutDataObject *wxlo, bool invalidate)
{
   if(! m_Selection.m_valid)
   {
      if(m_Selection.m_selecting)
         EndSelection();
      else
         return NULL;
   }

   if(invalidate) m_Selection.m_valid = false;

   wxLayoutList *llist = Copy( m_Selection.m_CursorA,
                               m_Selection.m_CursorB );

   if(llist && wxlo) // export as data object, too
   {
      wxString string;

      wxLayoutExportObject *exp;
      wxLayoutExportStatus status(llist);
      while((exp = wxLayoutExport( &status, WXLO_EXPORT_AS_OBJECTS)) != NULL)
      {
         if(exp->type == WXLO_EXPORT_EMPTYLINE)
            string << (int) WXLO_TYPE_LINEBREAK << '\n';
         else
            exp->content.object->Write(string);
         delete exp;
      }
      wxlo->SetLayoutData(string);
   }
   return llist;
}



#define COPY_SI(what) if(si.what != -1) { m_CurrentStyleInfo.what = si.what; fontChanged = TRUE; }

void
wxLayoutList::ApplyStyle(wxLayoutStyleInfo const &si, wxDC &dc)
{
   bool fontChanged = FALSE;
   COPY_SI(family);
   COPY_SI(size);
   COPY_SI(style);
   COPY_SI(weight);
   COPY_SI(underline);
   if(fontChanged)
      dc.SetFont( m_FontCache.GetFont(m_CurrentStyleInfo) );

   if(si.m_fg_valid)
   {
      m_CurrentStyleInfo.m_fg = si.m_fg;
      m_CurrentStyleInfo.m_fg_valid = true;
      dc.SetTextForeground(m_CurrentStyleInfo.m_fg);
   }
   if(si.m_bg_valid)
   {
      m_CurrentStyleInfo.m_bg = si.m_bg;
      m_CurrentStyleInfo.m_bg_valid = true;
      dc.SetTextBackground(m_CurrentStyleInfo.m_bg);
   }
}


#ifdef WXLAYOUT_DEBUG

void
wxLayoutList::Debug(void)
{
   WXLO_DEBUG((wxT("Cursor is in line %d, screen pos = (%d, %d)"),
               m_CursorLine->GetLineNumber(),
               m_CursorScreenPos.x, m_CursorScreenPos.y));

   wxLayoutLine *line;
   for(line = m_FirstLine; line; line = line->GetNextLine())
   {
      line->Debug();
   }
}

#endif


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

   wxLayoutPrintout

   * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

wxLayoutPrintout::wxLayoutPrintout(wxLayoutList *llist,
                                   wxString const & title)
:wxPrintout(title)
{
   m_llist = llist;
   m_title = title;
   // remove any highlighting which could interfere with printing:
   m_llist->StartSelection();
   m_llist->EndSelection();
   // force a full layout of the list:
   m_llist->ForceTotalLayout();
   // layout  is called in ScaleDC() when we have a DC
}

wxLayoutPrintout::~wxLayoutPrintout()
{
}

float
wxLayoutPrintout::ScaleDC(wxDC *dc)
{
   // The following bit is taken from the printing sample, let's see
   // whether it works for us.

   /* You might use THIS code to set the printer DC to ROUGHLY reflect
    * the screen text size. This page also draws lines of actual length 5cm
    * on the page.
    */
  // Get the logical pixels per inch of screen and printer
   int ppiScreenX, ppiScreenY;
   GetPPIScreen(&ppiScreenX, &ppiScreenY);
   int ppiPrinterX, ppiPrinterY;
   GetPPIPrinter(&ppiPrinterX, &ppiPrinterY);

   if(ppiScreenX == 0) // not yet set, need to guess
   {
      ppiScreenX = 100;
      ppiScreenY = 100;
   }
   if(ppiPrinterX == 0) // not yet set, need to guess
   {
      ppiPrinterX = 72;
      ppiPrinterY = 72;
   }

  // This scales the DC so that the printout roughly represents the
  // the screen scaling. The text point size _should_ be the right size
  // but in fact is too small for some reason. This is a detail that will
  // need to be addressed at some point but can be fudged for the
  // moment.
  float scale = (float)((float)ppiPrinterX/(float)ppiScreenX);

  // Now we have to check in case our real page size is reduced
  // (e.g. because we're drawing to a print preview memory DC)
  int pageWidth, pageHeight;
  int w, h;
  dc->GetSize(&w, &h);
  GetPageSizePixels(&pageWidth, &pageHeight);
  if(pageWidth != 0) // doesn't work always
  {
     // If printer pageWidth == current DC width, then this doesn't
     // change. But w might be the preview bitmap width, so scale down.
     scale = scale * (float)(w/(float)pageWidth);
  }
  dc->SetUserScale(scale, scale);
  return scale;
}

bool wxLayoutPrintout::OnPrintPage(int page)
{
   wxDC *dc = GetDC();

   ScaleDC(dc);

   if (dc)
   {
      int top, bottom;
      top = (page - 1)*m_PrintoutHeight;
      bottom = top + m_PrintoutHeight; 

      WXLO_DEBUG((wxT("OnPrintPage(%d) printing from %d to %d"), page, top, 
                  bottom));
      // SetDeviceOrigin() doesn't work here, so we need to manually
      // translate all coordinates.
      wxPoint translate(m_Offset.x,m_Offset.y-top);
      m_llist->Draw(*dc, translate, top, bottom, TRUE /* clip strictly 
                                                       */);
      return true;
   }
   else
      return false;
}

void wxLayoutPrintout::GetPageInfo(int *minPage, int *maxPage, int *selPageFrom, int *selPageTo)
{
   /* We allocate a temporary wxDC for printing, so that we can
      determine the correct paper size and scaling. We don't actually
      print anything on it. */
#if defined(__WXMSW__)
   wxPrinterDC *psdc = new wxPrinterDC("","",WXLLIST_TEMPFILE,false);
#else
   wxPostScriptDC *psdc = new wxPostScriptDC(WXLLIST_TEMPFILE,false);
#endif

   psdc->StartDoc(m_title);
   // before we draw anything, me must make sure the list is properly
   // laid out
   m_llist->Layout(*psdc);

   float scale = ScaleDC(psdc);

   psdc->GetSize(&m_PageWidth, &m_PageHeight);

   // This sets a left/top origin of 15% and 5%:
   m_Offset = wxPoint((15*m_PageWidth)/100, (5*m_PageHeight)/100);

   // This is the length of the printable area.
   m_PrintoutHeight = m_PageHeight - 2*m_Offset.y;
   m_PrintoutHeight = (int)( m_PrintoutHeight / scale); // we want to use the real paper height

   m_NumOfPages = 1 +
      (int)( m_llist->GetSize().y / (float)(m_PrintoutHeight));

   *minPage = 1;
   *maxPage = m_NumOfPages;

   *selPageFrom = 1;
   *selPageTo = m_NumOfPages;
   psdc->EndDoc();
   delete psdc;
   wxRemoveFile(WXLLIST_TEMPFILE);
}

bool wxLayoutPrintout::HasPage(int pageNum)
{
   return pageNum <= m_NumOfPages;
}

/*
  Stupid wxWindows doesn't draw proper ellipses, so we comment this
  out. It's a waste of paper anyway.
*/
#if 0
void
wxLayoutPrintout::DrawHeader(wxDC &dc,
                             wxPoint topleft, wxPoint bottomright,
                             int pageno)
{
   // make backups of all essential parameters
   const wxBrush& brush = dc.GetBrush();
   const wxPen&   pen = dc.GetPen();
   const wxFont&  font = dc.GetFont();

   dc.SetBrush(*wxWHITE_BRUSH);
   dc.SetPen(wxPen(*wxBLACK,0,wxSOLID));
   dc.DrawRoundedRectangle(topleft.x,
                           topleft.y,bottomright.x-topleft.x,
                           bottomright.y-topleft.y);
   dc.SetBrush(*wxBLACK_BRUSH);
   wxFont myfont = wxFont((WXLO_DEFAULTFONTSIZE*12)/10,
                          wxSWISS,wxNORMAL,wxBOLD,false,"Helvetica");
   dc.SetFont(myfont);

   wxString page;
   page = "9999/9999  ";  // many pages...
   long w,h;
   dc.GetTextExtent(page,&w,&h);
   page.Printf("%d/%d", pageno, (int) m_NumOfPages);
   dc.DrawText(page,bottomright.x-w,topleft.y+h/2);
   dc.GetTextExtent("XXXX", &w,&h);
   dc.DrawText(m_title, topleft.x+w,topleft.y+h/2);

   // restore settings
   dc.SetPen(pen);
   dc.SetBrush(brush);
   dc.SetFont(font);
}
#endif


wxFont &
wxFontCache::GetFont(int family, int size, int style, int weight,
                     bool underline)
{
   for(wxFCEList::iterator i = m_FontList.begin();
       i != m_FontList.end(); i++)
      if( (**i).Matches(family, size, style, weight, underline) )
         return (**i).GetFont();
   // not found:
   wxFontCacheEntry *fce = new wxFontCacheEntry(family, size, style,
                                                weight, underline);
   m_FontList.push_back(fce);
   return fce->GetFont();
}

