+/*-*- c++ -*-********************************************************
+ * wxllist: wxLayoutList, a layout engine for text and graphics *
+ * *
+ * (C) 1998-1999 by Karsten Ballüder (Ballueder@usa.net) *
+ * *
+ * $Id$
+ *******************************************************************/
+
+/*
+
+ 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 "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
+# include <iostream.h>
+
+# 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"
+ };
+ void
+ wxLayoutObject::Debug(void)
+ {
+ WXLO_DEBUG(("%s",g_aTypeStrings[GetType()]));
+ }
+#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
+
+
+//@}
+
+
+void ReadString(wxString &to, wxString &from)
+{
+ to = "";
+ const char *cptr = from.c_str();
+ while(*cptr && *cptr != '\n')
+ to += *cptr++;
+ if(*cptr) cptr++;
+ from = cptr;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+
+ wxLayoutObject
+
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/* static */
+wxLayoutObject *
+wxLayoutObject::Read(wxString &istr)
+{
+ wxString tmp;
+ ReadString(tmp, istr);
+ int type = -1;
+ sscanf(tmp.c_str(),"%d", &type);
+
+ 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
+
+ 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
+void
+wxLayoutObjectText::Debug(void)
+{
+ wxLayoutObject::Debug();
+ WXLO_DEBUG((" `%s`", m_Text.c_str()));
+}
+#endif
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+
+ wxLayoutObjectIcon
+
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap const &icon)
+{
+ m_Icon = new wxBitmap(icon);
+}
+
+
+void
+wxLayoutObjectIcon::Write(wxString &ostr)
+{
+ /* Exports icon through a temporary file. */
+
+ wxString file = wxGetTempFileName("wxloexport");
+
+ ostr << 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;
+}
+
+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;
+ if(fg)
+ {
+ m_fg = *fg;
+ m_fg_valid = TRUE;
+ }
+ else
+ m_fg = *wxBLACK;
+ if(bg)
+ {
+ m_bg = *bg;
+ m_bg_valid = TRUE;
+ }
+ else
+ m_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);
+}
+
+wxLayoutObject *
+wxLayoutObjectCmd::Copy(void)
+{
+ wxLayoutObjectCmd *obj = new wxLayoutObjectCmd(
+ m_StyleInfo->size,
+ m_StyleInfo->family,
+ 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 << WXLO_TYPE_CMD << '\n'
+ << m_StyleInfo->size << '\n'
+ << m_StyleInfo->family << '\n'
+ << m_StyleInfo->style << '\n'
+ << m_StyleInfo->weight << '\n'
+ << m_StyleInfo->underline << '\n'
+ << m_StyleInfo->m_fg_valid << '\n'
+ << m_StyleInfo->m_bg_valid << '\n';
+ if(m_StyleInfo->m_fg_valid)
+ {
+ ostr << m_StyleInfo->m_fg.Red() << '\n'
+ << m_StyleInfo->m_fg.Green() << '\n'
+ << m_StyleInfo->m_fg.Blue() << '\n';
+ }
+ if(m_StyleInfo->m_bg_valid)
+ {
+ ostr << m_StyleInfo->m_bg.Red() << '\n'
+ << m_StyleInfo->m_bg.Green() << '\n'
+ << m_StyleInfo->m_bg.Blue() << '\n';
+ }
+}
+/* static */
+wxLayoutObjectCmd *
+wxLayoutObjectCmd::Read(wxString &istr)
+{
+ wxLayoutObjectCmd *obj = new wxLayoutObjectCmd;
+
+ wxString tmp;
+ ReadString(tmp, istr);
+ sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->size);
+ ReadString(tmp, istr);
+ sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->family);
+ ReadString(tmp, istr);
+ sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->style);
+ ReadString(tmp, istr);
+ sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->weight);
+ ReadString(tmp, istr);
+ sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->underline);
+ ReadString(tmp, istr);
+ sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->m_fg_valid);
+ ReadString(tmp, istr);
+ sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->m_bg_valid);
+ if(obj->m_StyleInfo->m_fg_valid)
+ {
+ int red, green, blue;
+ ReadString(tmp, istr);
+ sscanf(tmp.c_str(),"%d", &red);
+ ReadString(tmp, istr);
+ sscanf(tmp.c_str(),"%d", &green);
+ ReadString(tmp, istr);
+ sscanf(tmp.c_str(),"%d", &blue);
+ obj->m_StyleInfo->m_fg = wxColour(red, green, blue);
+ }
+ if(obj->m_StyleInfo->m_bg_valid)
+ {
+ int red, green, blue;
+ ReadString(tmp, istr);
+ sscanf(tmp.c_str(),"%d", &red);
+ ReadString(tmp, istr);
+ sscanf(tmp.c_str(),"%d", &green);
+ ReadString(tmp, istr);
+ sscanf(tmp.c_str(),"%d", &blue);
+ 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_LineNumber = 0;
+ m_Width = m_Height = 0;
+ m_Length = 0;
+ MarkDirty(0);
+ m_Previous = prev;
+ m_Next = NULL;
+ RecalculatePosition(llist);
+ 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->MoveLines(+1);
+ m_Next->RecalculatePositions(1,llist);
+ }
+}
+
+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;
+}
+
+void
+wxLayoutLine::RecalculatePositions(int recurse, wxLayoutList *llist)
+{
+ //FIXME: is this really needed? We run Layout() anyway.
+ // Recursing here, drives computation time up exponentially, as
+ // each line will cause all following lines to be recalculated.
+ // Yes, or linenumbers go wrong.
+
+ wxASSERT(recurse >= 0);
+ wxPoint pos = m_Position;
+ CoordType height = m_Height;
+
+// WXLO_TRACE("RecalculatePositions()");
+ RecalculatePosition(llist);
+ if(m_Next)
+ {
+ if(recurse > 0)
+ m_Next->RecalculatePositions(--recurse, llist);
+ else if(pos != m_Position || m_Height != height)
+ m_Next->RecalculatePositions(0, llist);
+ }
+}
+
+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,
+ CoordType xpos, CoordType *cxpos,
+ bool *found) const
+{
+ wxASSERT(cxpos);
+ wxASSERT(cxpos);
+ wxLayoutObjectList::iterator i;
+ CoordType x = 0, cx = 0, width;
+
+ for(i = m_ObjectList.begin(); i != NULLIT; i++)
+ {
+ width = (**i).GetWidth();
+ if( x <= xpos && xpos <= x + width )
+ {
+ *cxpos = cx + (**i).GetOffsetScreen(dc, xpos-x);
+ if(found) *found = true;
+ return i;
+ }
+ x += (**i).GetWidth();
+ cx += (**i).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);
+
+ // If we insert a command object, we need to recalculate all lines
+ // to update their styleinfo structure.
+ if(obj->GetType() == WXLO_TYPE_CMD)
+ MarkNextDirty(-1);
+
+ 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;
+ // If we delete a command object, we need to recalculate all lines
+ // to update their styleinfo structure.
+ if((**i).GetType() == WXLO_TYPE_CMD)
+ MarkNextDirty(-1);
+ 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;
+}
+
+void
+wxLayoutLine::MarkNextDirty(int recurse)
+{
+ wxLayoutLine *line = GetNextLine();
+ while(line && (recurse == -1 || recurse >= 0))
+ {
+ line->MarkDirty();
+ line = line->GetNextLine();
+ if(recurse > 0) recurse --;
+ }
+}
+
+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("unreachable");
+}
+
+wxLayoutLine *
+wxLayoutLine::DeleteLine(bool update, wxLayoutList *llist)
+{
+ if(m_Next) m_Next->m_Previous = m_Previous;
+ if(m_Previous) m_Previous->m_Next = m_Next;
+ if(update)
+ {
+ m_Next->MoveLines(-1);
+ m_Next->RecalculatePositions(1, llist);
+ /* We assume that if we have more than one object in the list,
+ this means that we have a command object, so we need to
+ update the following lines. */
+ if(m_ObjectList.size() > 1 ||
+ ( m_ObjectList.begin() != NULLIT &&
+ (**m_ObjectList.begin()).GetType() == WXLO_TYPE_CMD)
+ )
+ MarkNextDirty(-1);
+ }
+ wxLayoutLine *next = m_Next;
+ delete this;
+ 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,
+ 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;
+
+ 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);
+ wxASSERT(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
+ *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)
+ {
+ // FIXME isn't this done in RecalculatePositions() below anyhow?
+ m_Next->RecalculatePositions(0, llist);
+ }
+
+ // We need to check whether we found a valid cursor size:
+ if(cursorPos)
+ {
+ // this might be the case if the cursor is at the end of the
+ // line or on a command object:
+ if(cursorSize->y < 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;
+ }
+ RecalculatePositions(1, llist);
+ MarkClean();
+}
+
+
+wxLayoutLine *
+wxLayoutLine::Break(CoordType xpos, wxLayoutList *llist)
+{
+ wxASSERT(xpos >= 0);
+
+ MarkDirty(xpos);
+
+ /* If we are at the begin of a line, we want to move all other
+ lines down and stay with the cursor where we are. However, if we
+ are in an empty line, we want to move down with it. */
+ if(xpos == 0 && GetLength() > 0)
+ { // insert an empty line before this one
+ wxLayoutLine *prev = new wxLayoutLine(m_Previous, llist);
+ if(m_Previous == NULL)
+ { // We were in first line, need to link in new empty line
+ // before this.
+ prev->m_Next = this;
+ m_Previous = prev;
+ m_Previous->m_Height = 0; // this is a wild guess
+ }
+ if(m_Next)
+ m_Next->RecalculatePositions(1, llist);
+ return m_Previous;
+ }
+
+ 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)
+ {
+ 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->RecalculatePositions(2, llist);
+ return newLine;
+}
+
+
+void
+wxLayoutLine::MergeNextLine(wxLayoutList *llist)
+{
+ wxCHECK_RET(GetNextLine(),"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->MoveLines(-1);
+ }
+ 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
+ }
+
+ 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?
+ pos += (**i).GetLength();
+ i++;
+ while(i != NULLIT && (**i).GetType() != WXLO_TYPE_TEXT)
+ {
+ pos += (**i).GetLength();
+ i++;
+ }
+ if(i == NULLIT) return -1; //this is possible, if there is only one text object
+ // now we are at the second text object:
+ pos -= (**i).GetLength();
+ return pos; // in front of it
+}
+
+
+#ifdef WXLAYOUT_DEBUG
+void
+wxLayoutLine::Debug(void)
+{
+ wxString tmp;
+ wxPoint pos = GetPosition();
+ WXLO_DEBUG(("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)
+ (**m_ObjectList.begin()).Debug();
+
+}
+#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_FirstLine = NULL;
+ InvalidateUpdateRect();
+ Clear();
+}
+
+wxLayoutList::~wxLayoutList()
+{
+ InternalClear();
+ m_FirstLine->DeleteLine(false, this);
+}
+
+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)
+{
+ Empty();
+ 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;
+}
+
+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;
+}
+
+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 ++;
+ 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)
+{
+ wxCHECK_MSG( m_CursorLine, false, "no current line" );
+ wxCHECK_MSG( n == -1 || n == +1, false, "not implemented yet" );
+
+ CoordType moveDistance = 0;
+ CoordType offset;
+ for ( wxLOiterator i = m_CursorLine->FindObject(m_CursorPos.x, &offset);
+ n != 0;
+ n > 0 ? i++ : i-- )
+ {
+ if ( i == NULLIT )
+ return false;
+
+ wxLayoutObject *obj = *i;
+ if( obj->GetType() != WXLO_TYPE_TEXT )
+ {
+ // any non text objects count as one word
+ n > 0 ? n-- : n++;
+
+ moveDistance += obj->GetLength();
+ }
+ else
+ {
+ // text object
+ wxLayoutObjectText *tobj = (wxLayoutObjectText *)obj;
+
+ if ( offset == tobj->GetLength() )
+ {
+ // at end of object
+ n > 0 ? n-- : n++;
+ }
+ else
+ {
+ const char *start = tobj->GetText().c_str();
+ const char *p = start + offset;
+
+ // to the beginning/end of the next/prev word
+ while ( isspace(*p) )
+ {
+ n > 0 ? p++ : p--;
+ }
+
+ // go to the end/beginning of the word (in a broad sense...)
+ while ( p >= start && !isspace(*p) )
+ {
+ n > 0 ? p++ : p--;
+ }
+
+ if ( n > 0 )
+ {
+ // now advance to the beginning of the next word
+ while ( isspace(*p) )
+ p++;
+ }
+
+ n > 0 ? n-- : n++;
+
+ moveDistance = p - start - offset;
+ }
+ }
+
+ // except for the first iteration, offset is 0
+ offset = 0;
+ }
+
+ MoveCursorHorizontally(moveDistance);
+
+ return true;
+}
+
+bool
+wxLayoutList::Insert(wxString const &text)
+{
+ wxASSERT(m_CursorLine);
+ wxASSERT_MSG( text.Find('\n') == wxNOT_FOUND, "use wxLayoutImportText!" );
+
+ if ( !text )
+ return true;
+
+ AddCursorPosToUpdateRect();
+
+ if ( !m_CursorLine->Insert(m_CursorPos.x, text) )
+ return false;
+
+ m_CursorPos.x += text.Length();
+
+ m_movedCursor = true;
+
+ m_CursorLine->RecalculatePositions(0, this);
+
+ 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;
+
+ m_CursorLine->RecalculatePositions(0, this);
+
+ 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);
+ bool setFirst = (m_CursorLine == m_FirstLine && m_CursorPos.x == 0);
+
+ 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(setFirst) // we were at beginning of first line
+ m_FirstLine = m_CursorLine->GetPreviousLine();
+ if(m_CursorPos.x != 0)
+ m_CursorPos.y++;
+ m_CursorPos.x = 0;
+
+ wxLayoutLine *prev = m_CursorLine->GetPreviousLine();
+ wxCHECK_MSG(prev, false, "just broke the line, where is the previous one?");
+
+ height += prev->GetHeight();
+
+ m_movedCursor = true;
+
+ SetUpdateRect(position);
+ SetUpdateRect(position.x + width + MSW_CORRECTION,
+ position.y + height + MSW_CORRECTION);
+
+ return true;
+}
+
+bool
+wxLayoutList::WrapLine(CoordType column)
+{
+ if(m_CursorPos.x <= column || column < 1)
+ return false; // do nothing yet
+ else
+ {
+ CoordType xpos = m_CursorLine->GetWrapPosition(column);
+ if(xpos == -1)
+ return false; // cannot break line
+ //else:
+ CoordType newpos = m_CursorPos.x - xpos - 1;
+ m_CursorPos.x = xpos;
+
+ AddCursorPosToUpdateRect();
+
+ LineBreak();
+ Delete(1); // delete the space
+ m_CursorPos.x = newpos;
+
+ m_CursorLine->RecalculatePositions(1, this);
+
+ m_movedCursor = true;
+
+ return true;
+ }
+}
+
+bool
+wxLayoutList::Delete(CoordType npos)
+{
+ wxCHECK_MSG(m_CursorLine, false, "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("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();
+ m_CursorLine->RecalculatePositions(2, this);
+ 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);
+ }
+ m_CursorLine->RecalculatePositions(2, this);
+ return n;
+}
+
+void
+wxLayoutList::Recalculate(wxDC &dc, CoordType bottom)
+{
+ 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();
+ }
+}
+
+void
+wxLayoutList::UpdateCursorScreenPos(wxDC &dc,
+ bool resetCursorMovedFlag,
+ const wxPoint& translate)
+{
+ wxCHECK_RET( m_CursorLine, "no cursor line" );
+
+ if ( m_movedCursor )
+ {
+ // we need to save the current style, in case the layout() of
+ // the line changes it
+ wxLayoutStyleInfo SiBackup = m_CurrentStyleInfo;
+ m_CursorLine->Layout(dc, this,
+ &m_CursorScreenPos, &m_CursorSize,
+ m_CursorPos.x,
+ /* suppress update */ true);
+ ApplyStyle(SiBackup, dc); // restore it
+
+ if ( resetCursorMovedFlag )
+ {
+#ifdef WXLAYOUT_USE_CARET
+ // adjust the caret position
+ wxPoint coords(m_CursorScreenPos);
+ coords += translate;
+
+ // and set it
+ m_caret->Move(coords);
+#endif // WXLAYOUT_USE_CARET
+
+ m_movedCursor = false;
+ }
+ }
+}
+
+wxPoint
+wxLayoutList::GetCursorScreenPos(wxDC &dc)
+{
+ // this function is called with wxMemoryDC argument from ScrollToCursor(),
+ // for example, so it shouldn't clear "cursor moved" flag - or else the
+ // cursor won't be moved when UpdateCursorScreenPos() is called with the
+ // "real" (i.e. the one used for drawing) wxDC.
+ UpdateCursorScreenPos(dc, false /* don't reset the flag */);
+
+ 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)
+{
+ // first, make sure everything is calculated - this might not be
+ // needed, optimise it later
+ ApplyStyle(m_DefaultStyleInfo, dc);
+
+ // This one we always Layout() to get the current cursor
+ // coordinates on the screen:
+ m_CursorLine->MarkDirty();
+ bool wasDirty = false;
+ wxLayoutLine *line = m_FirstLine;
+ while(line)
+ {
+ if(! wasDirty)
+ ApplyStyle(line->GetStyleInfo(), dc);
+ if(forceAll || line->IsDirty())
+ {
+ // 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_CursorPos.x);
+ else
+ line->Layout(dc, this);
+
+ // little condition to speed up redrawing:
+ if(bottom != -1 && line->GetPosition().y > bottom)
+ break;
+ wasDirty = true;
+ }
+ line->RecalculatePositions(1, this);
+ line = line->GetNextLine();
+ }
+
+ // 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));
+ AddCursorPosToUpdateRect();
+}
+
+void
+wxLayoutList::Draw(wxDC &dc,
+ wxPoint const &offset,
+ CoordType top,
+ CoordType bottom)
+{
+ wxLayoutLine *line = m_FirstLine;
+
+ /* We need to re-layout all dirty lines to update styleinfos
+ etc. However, somehow we don't find all dirty lines... */
+ Layout(dc); //,-1,true); //FIXME
+ ApplyStyle(m_DefaultStyleInfo, dc);
+ wxBrush brush(m_CurrentStyleInfo.m_bg, wxSOLID);
+ dc.SetBrush(brush);
+ dc.SetBackgroundMode(wxTRANSPARENT);
+
+ bool style_set = false;
+ while(line)
+ {
+ // only draw if between top and bottom:
+ if((top == -1 ||
+ line->GetPosition().y + line->GetHeight() >= top))
+ {
+// if(! style_set)
+ {
+ ApplyStyle(line->GetStyleInfo(), dc);
+ style_set = true;
+ }
+ line->Draw(dc, this, offset);
+ }
+#if 0
+ else
+ line->Layout(dc, this);
+#endif
+ // little condition to speed up redrawing:
+ if(bottom != -1 && line->GetPosition().y > bottom) break;
+ line = line->GetNextLine();
+ }
+ InvalidateUpdateRect();
+
+ WXLO_DEBUG(("Selection is %s : l%d,%ld/%ld,%ld",
+ m_Selection.m_valid ? "valid" : "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;
+ wxPoint p;
+
+ ApplyStyle(m_DefaultStyleInfo, dc);
+ while(line)
+ {
+ p = line->GetPosition();
+ if(p.y <= pos.y && p.y+line->GetHeight() >= pos.y)
+ break;
+#if 0
+ // we need to run a layout here to get font sizes right :-(
+
+ // VZ: we can't call Layout() from here because it marks the line as
+ // clean and it is not refreshed when it's called from wxLayoutList::
+ // Layout() - if we really need to do this, we should introduce an
+ // extra argument to Layout() to prevent the line from MarkClean()ing
+ // itself here
+ line->Layout(dc, this);
+#endif
+ line = line->GetNextLine();
+ }
+ if(line == NULL)
+ {
+ if(found) *found = false;
+ return NULL; // not found
+ }
+ if(cursorPos) cursorPos->y = line->GetLineNumber();
+ // Now, find the object in the line:
+ wxLOiterator i = line->FindObjectScreen(dc, pos.x,
+ cursorPos ? & cursorPos->x : NULL ,
+ found);
+ 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)
+{
+ wxPoint coords(m_CursorScreenPos);
+ coords += translate;
+
+#ifdef WXLAYOUT_DEBUG
+ WXLO_DEBUG(("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("Cursor is at (%d, %d)", m_CursorPos.x, m_CursorPos.y);
+#endif
+
+#ifndef WXLAYOUT_USE_CARET
+ dc.SetBrush(*wxBLACK_BRUSH);
+ dc.SetLogicalFunction(wxXOR);
+ dc.SetPen(wxPen(*wxBLACK,1,wxSOLID));
+ if(active)
+ {
+ 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.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(("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(("Continuing selection at %ld/%ld", cpos.x, cpos.y));
+
+ if ( m_Selection.m_CursorB <= cpos )
+ {
+ m_Selection.m_ScreenB = spos;
+ m_Selection.m_CursorB = cpos;
+ }
+ else
+ {
+ m_Selection.m_ScreenA = spos;
+ m_Selection.m_CursorA = cpos;
+ }
+
+ // 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;
+ }
+}
+
+void
+wxLayoutList::EndSelection(const wxPoint& cposOrig, const wxPoint& spos)
+{
+ wxPoint cpos(cposOrig);
+ if(cpos.x == -1)
+ cpos = m_CursorPos;
+ ContinueSelection(cpos);
+ WXLO_DEBUG(("Ending selection at %ld/%ld", cpos.x, cpos.y));
+ m_Selection.m_selecting = false;
+ m_Selection.m_valid = true;
+}
+
+void
+wxLayoutList::DiscardSelection()
+{
+ if ( !HasSelection() )
+ return;
+
+ m_Selection.m_valid =
+ m_Selection.m_selecting = false;
+
+ // invalidate the area which was previousle selected - and which is not
+ // selected any more
+ if ( m_Selection.HasValidScreenCoords() )
+ {
+ SetUpdateRect(m_Selection.m_ScreenA);
+ SetUpdateRect(m_Selection.m_ScreenB);
+ }
+ else
+ {
+ // TODO
+ }
+}
+
+bool
+wxLayoutList::IsSelecting(void)
+{
+ return m_Selection.m_selecting;
+}
+
+bool
+wxLayoutList::IsSelected(const wxPoint &cursor)
+{
+ if ( !HasSelection() )
+ return false;
+
+ return m_Selection.m_CursorA <= cursor && cursor <= m_Selection.m_CursorB;
+}
+
+
+/** 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)
+ 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
+ *to = line->GetLength();
+ 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
+ *from = 0;
+ 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;
+ }
+
+
+ wxLayoutLine
+ * firstLine = NULL,
+ * lastLine = NULL;
+
+ for(firstLine = m_FirstLine;
+ firstLine && firstLine->GetLineNumber() < m_Selection.m_CursorA.y;
+ firstLine=firstLine->GetNextLine())
+ ;
+ if(!firstLine || firstLine->GetLineNumber() != m_Selection.m_CursorA.y)
+ return;
+
+
+ for(lastLine = m_FirstLine;
+ lastLine && lastLine->GetLineNumber() < m_Selection.m_CursorB.y;
+ lastLine=lastLine->GetNextLine())
+ ;
+ if(!lastLine || lastLine->GetLineNumber() != m_Selection.m_CursorB.y)
+ return;
+
+
+ // We now know that the two lines are different:
+
+ // First, delete what's left of this line:
+ MoveCursorTo(m_Selection.m_CursorA);
+ DeleteToEndOfLine();
+
+ wxLayoutLine *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:
+ firstLine->RecalculatePositions(1, this);
+}
+
+/// 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
+}
+
+
+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 *export;
+ wxLayoutExportStatus status(llist);
+ while((export = wxLayoutExport( &status, WXLO_EXPORT_AS_OBJECTS)) != NULL)
+ {
+ if(export->type == WXLO_EXPORT_EMPTYLINE)
+ ; //FIXME missing support for linebreaks in string format
+ else
+ export->content.object->Write(string);
+ delete export;
+ }
+
+ wxlo->SetData(string.c_str(), string.Length()+1);
+ }
+ 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;
+ dc.SetTextForeground(m_CurrentStyleInfo.m_fg);
+ }
+ if(si.m_bg_valid)
+ {
+ m_CurrentStyleInfo.m_bg = si.m_bg;
+ dc.SetTextBackground(m_CurrentStyleInfo.m_bg);
+ }
+}
+
+
+#ifdef WXLAYOUT_DEBUG
+
+void
+wxLayoutList::Debug(void)
+{
+ WXLO_DEBUG(("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();
+}
+
+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;
+ // 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);
+ 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. */
+#ifdef __WXMSW__
+ wxPrinterDC psdc("","",WXLLIST_TEMPFILE,false);
+#else
+ wxPostScriptDC psdc(WXLLIST_TEMPFILE,false);
+#endif
+
+ float scale = ScaleDC(&psdc);
+
+ psdc.GetSize(&m_PageWidth, &m_PageHeight);
+ // This sets a left/top origin of 15% and 20%:
+ m_Offset = wxPoint((15*m_PageWidth)/100, m_PageHeight/20);
+
+ // This is the length of the printable area.
+ m_PrintoutHeight = m_PageHeight - (int) (m_PageHeight * 0.15);
+ 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;
+ 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();
+}
+