/*-*- c++ -*-********************************************************
* wxllist: wxLayoutList, a layout engine for text and graphics *
* *
- * (C) 1998 by Karsten Ballüder (Ballueder@usa.net) *
+ * (C) 1998-1999 by Karsten Ballüder (Ballueder@usa.net) *
* *
* $Id$
*******************************************************************/
/*
- - each Object knows its size and how to draw itself
- - the list is responsible for calculating positions
- - the draw coordinates for each object are the top left corner
- - coordinates only get calculated when things get redrawn
- - The cursor position is the position before an object, i.e. if the
- buffer starts with a text-object, cursor 0,0 is just before the
- first character. For all non-text objects, the cursor positions
- are 0==before or 1==behind. So that all non-text objects count as
- one cursor position.
- - Linebreaks are at the end of a line, that is a line like "abc\n"
- is four cursor positions long. This makes sure that cursor
- positions are "as expected", i.e. in "abc\ndef" the 'd' would be
- at positions (x=0,y=1).
-*/
-
-/*
- TODO:
-
- - cursor redraw problems
- - blinking cursor
- - mouse click positions cursor
- - selection (SetMark(), GetSelection())
- - DND acceptance of text / clipboard support
- - wxlwindow: formatting menu: problem with checked/unchecked consistency gtk bug?
-*/
-
-/*
- Known wxGTK bugs:
- - MaxX()/MaxY() don't get set
-*/
-
+
+ */
#ifdef __GNUG__
#pragma implementation "wxllist.h"
#endif
//#include "Mpch.h"
-#ifdef M_BASEDIR
+
+
+#include "wx/wxprec.h"
+#ifdef __BORLANDC__
+# pragma hdrstop
+#endif
+
+#ifdef M_PREFIX
# include "gui/wxllist.h"
#else
# include "wxllist.h"
#ifndef USE_PCH
# include "iostream.h"
# include <wx/dc.h>
-# include <wx/postscrp.h>
+# include <wx/dcps.h>
# include <wx/print.h>
# include <wx/log.h>
#endif
-#define BASELINESTRETCH 12
+#include <ctype.h>
-// This should never really get created
+/// This should never really get created
#define WXLLIST_TEMPFILE "__wxllist.tmp"
#ifdef WXLAYOUT_DEBUG
-static const char *g_aTypeStrings[] =
-{
- "invalid", "text", "cmd", "icon", "linebreak"
-};
+
+# define TypewxString(t) g_aTypewxStrings[t]
+# define WXLO_DEBUG(x) wxLogDebug x
+
+ static const char *g_aTypewxStrings[] =
+ {
+ "invalid", "text", "cmd", "icon"
+ };
+ void
+ wxLayoutObject::Debug(void)
+ {
+ WXLO_DEBUG(("%s",g_aTypewxStrings[GetType()]));
+ }
+#else
+# define TypewxString(t) ""
+# define WXLO_DEBUG(x)
+#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.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;
+}
+
+/// 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);
+}
+
+/// grows a wxRect so that it includes the given point
+
+static
+void GrowRect(wxRect &r, const wxPoint & p)
+{
+ if(r.x > p.x)
+ r.x = p.x;
+ else if(r.x + r.width < p.x)
+ r.width = p.x - r.x;
-# define wxLayoutDebug wxLogDebug
-# define WXL_VAR(x) cerr << #x " = " << x << endl;
-# define WXL_DBG_POINT(p) wxLayoutDebug(#p ": (%d, %d)", p.x, p.y)
-# define WXL_TRACE(f) wxLayoutDebug(#f ": ")
-# define TypeString(t) g_aTypeStrings[t]
+ if(r.y > p.y)
+ r.y = p.y;
+ else if(r.y + r.height < p.y)
+ r.height = p.y - r.y;
+}
-void
-wxLayoutObjectBase::Debug(void)
+/// returns true if the point is in the rectangle
+static
+bool Contains(const wxRect &r, const wxPoint &p)
{
- CoordType bl = 0;
- wxLayoutDebug("%s: size = %dx%d, pos=%d,%d, bl = %d",
- TypeString(GetType()), GetSize(&bl).x,
- GetSize(&bl).y,
- GetPosition().x, GetPosition().y, bl);
+ return r.x <= p.x && r.y <= p.y && (r.x+r.width) >= p.x && (r.y + r.height) >= p.y;
}
+//@}
-#else
-# define WXL_VAR(x)
-# define WXL_DBG_POINT(p)
-# define WXL_TRACE(f)
-# define ShowCurrentObject()
-# define TypeString(t) ""
-inline void wxLayoutDebug(const char *, ...) { }
-#endif
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ wxLayoutObjectText
-//-------------------------- wxLayoutObjectText
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-wxLayoutObjectText::wxLayoutObjectText(const String &txt)
+wxLayoutObjectText::wxLayoutObjectText(const wxString &txt)
{
m_Text = txt;
m_Width = 0;
m_Height = 0;
- m_Position = wxPoint(-1,-1);
+ 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;
+}
wxPoint
-wxLayoutObjectText::GetSize(CoordType *baseLine) const
+wxLayoutObjectText::GetSize(CoordType *top, CoordType *bottom) const
{
- if(baseLine) *baseLine = m_BaseLine;
+
+ *top = m_Top; *bottom = m_Bottom;
return wxPoint(m_Width, m_Height);
}
void
-wxLayoutObjectText::Draw(wxDC &dc, wxPoint const &translate)
+wxLayoutObjectText::Draw(wxDC &dc, wxPoint const &coords)
{
- dc.DrawText(Str(m_Text), m_Position.x + translate.x, m_Position.y+translate.y);
- m_IsDirty = false;
+ dc.DrawText(m_Text, coords.x, coords.y-m_Top);
}
+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, wxPoint position, CoordType baseLine)
+wxLayoutObjectText::Layout(wxDC &dc)
{
long descent = 0l;
- if(m_Position.x != position.x || m_Position.y != position.y)
- m_IsDirty = true;
-
- m_Position = position;
- dc.GetTextExtent(Str(m_Text),&m_Width, &m_Height, &descent);
- m_BaseLine = m_Height - descent;
- if(m_Position.x != position.x || m_Position.y != position.y)
- m_IsDirty = true;
+ dc.GetTextExtent(m_Text,&m_Width, &m_Height, &descent);
+ m_Bottom = descent;
+ m_Top = m_Height - m_Bottom;
}
#ifdef WXLAYOUT_DEBUG
void
wxLayoutObjectText::Debug(void)
{
- wxLayoutObjectBase::Debug();
- wxLayoutDebug(" `%s`", m_Text.c_str());
+ wxLayoutObject::Debug();
+ WXLO_DEBUG((" `%s`", m_Text.c_str()));
}
#endif
-//-------------------------- wxLayoutObjectIcon
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+
+ wxLayoutObjectIcon
-wxLayoutObjectIcon::wxLayoutObjectIcon(wxIcon const &icon)
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap const &icon)
+{
+ m_Icon = new wxBitmap(icon);
+}
+
+wxLayoutObject *
+wxLayoutObjectIcon::Copy(void)
{
- m_Position = wxPoint(-1,-1);
- m_Icon = new wxIcon(icon);
+ wxLayoutObjectIcon *obj = new wxLayoutObjectIcon(new
+ wxBitmap(*m_Icon));
+ obj->SetUserData(m_UserData);
+ return obj;
}
-wxLayoutObjectIcon::wxLayoutObjectIcon(wxIcon *icon)
+wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap *icon)
{
m_Icon = icon;
}
void
-wxLayoutObjectIcon::Draw(wxDC &dc, wxPoint const &translate)
+wxLayoutObjectIcon::Draw(wxDC &dc, wxPoint const &coords)
{
- dc.DrawIcon(m_Icon,m_Position.x+translate.x, m_Position.y+translate.y);
+ dc.DrawBitmap(*m_Icon, coords.x, coords.y-m_Icon->GetHeight(),
+ (m_Icon->GetMask() == NULL) ? FALSE : TRUE);
}
void
-wxLayoutObjectIcon::Layout(wxDC &dc, wxPoint position, CoordType baseLine)
+wxLayoutObjectIcon::Layout(wxDC & /* dc */)
{
- if(m_Position.x != position.x || m_Position.y != position.y)
- m_IsDirty = true;
- m_Position = position;
}
wxPoint
-wxLayoutObjectIcon::GetSize(CoordType *baseLine) const
+wxLayoutObjectIcon::GetSize(CoordType *top, CoordType *bottom) const
{
- if(baseLine) *baseLine = m_Icon->GetHeight();
+ *top = m_Icon->GetHeight();
+ *bottom = 0;
return wxPoint(m_Icon->GetWidth(), m_Icon->GetHeight());
}
-//-------------------------- wxLayoutObjectCmd
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+
+ wxLayoutObjectIcon
+
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
wxLayoutObjectCmd::wxLayoutObjectCmd(int size, int family, int style, int
weight, bool underline,
- wxColour const *fg, wxColour const *bg)
+ wxColour &fg, wxColour &bg)
{
m_font = new wxFont(size,family,style,weight,underline);
m_ColourBG = bg;
}
+wxLayoutObject *
+wxLayoutObjectCmd::Copy(void)
+{
+ wxLayoutStyleInfo si;
+ GetStyle(&si);
+
+ wxLayoutObjectCmd *obj = new wxLayoutObjectCmd(
+ si.size, si.family, si.style, si.weight, si.underline,
+ m_ColourFG, m_ColourBG);
+ obj->SetUserData(m_UserData);
+ return obj;
+}
+
+
wxLayoutObjectCmd::~wxLayoutObjectCmd()
{
delete m_font;
}
-wxLayoutStyleInfo *
-wxLayoutObjectCmd::GetStyle(void) const
+void
+wxLayoutObjectCmd::GetStyle(wxLayoutStyleInfo *si) const
{
- wxLayoutStyleInfo *si = new wxLayoutStyleInfo();
-
-
si->size = m_font->GetPointSize();
si->family = m_font->GetFamily();
si->style = m_font->GetStyle();
si->underline = m_font->GetUnderlined();
si->weight = m_font->GetWeight();
- si->fg_red = m_ColourFG->Red();
- si->fg_green = m_ColourFG->Green();
- si->fg_blue = m_ColourFG->Blue();
- si->bg_red = m_ColourBG->Red();
- si->bg_green = m_ColourBG->Green();
- si->bg_blue = m_ColourBG->Blue();
-
- return si;
+ si->fg_red = m_ColourFG.Red();
+ si->fg_green = m_ColourFG.Green();
+ si->fg_blue = m_ColourFG.Blue();
+ si->bg_red = m_ColourBG.Red();
+ si->bg_green = m_ColourBG.Green();
+ si->bg_blue = m_ColourBG.Blue();
}
void
-wxLayoutObjectCmd::Draw(wxDC &dc, wxPoint const &translate)
+wxLayoutObjectCmd::Draw(wxDC &dc, wxPoint const & /* coords */)
{
wxASSERT(m_font);
- dc.SetFont(m_font);
- if(m_ColourFG)
- dc.SetTextForeground(*m_ColourFG);
- if(m_ColourBG)
- dc.SetTextBackground(*m_ColourBG);
+ dc.SetFont(*m_font);
+ dc.SetTextForeground(m_ColourFG);
+ dc.SetTextBackground(m_ColourBG);
}
+
void
-wxLayoutObjectCmd::Layout(wxDC &dc, wxPoint p, CoordType baseline)
+wxLayoutObjectCmd::Layout(wxDC &dc)
{
- m_Position = p; // required so we can find the right object for cursor
// this get called, so that recalculation uses right font sizes
- Draw(dc,wxPoint(0,0));
+ Draw(dc, wxPoint(0,0));
}
-//-------------------------- wxLayoutList
-wxLayoutList::wxLayoutList()
-{
- m_DefaultSetting = NULL;
- m_WrapMargin = -1;
- Clear();
-}
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-wxLayoutList::~wxLayoutList()
+ The wxLayoutLine object
+
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+wxLayoutLine::wxLayoutLine(wxLayoutLine *prev, wxLayoutList *llist)
{
- if(m_DefaultSetting)
- delete m_DefaultSetting;
- // no deletion of objects, they are owned by the list
+ m_LineNumber = 0;
+ m_Width = m_Height = 0;
+ m_Length = 0;
+ m_Dirty = true;
+ 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;
+ m_Height = m_Previous->GetHeight();
+ }
+ if(m_Next)
+ {
+ m_Next->m_Previous = this;
+ m_Next->MoveLines(+1);
+ m_Next->RecalculatePositions(1,llist);
+ }
}
-void
-wxLayoutList::LineBreak(void)
+wxLayoutLine::~wxLayoutLine()
{
- Insert(new wxLayoutObjectLineBreak);
-// m_CursorPos.x = 0; m_CursorPos.y++;
+ // kbList cleans itself
}
-void
-wxLayoutList::SetFont(int family, int size, int style, int weight,
- int underline, wxColour const *fg,
- wxColour const *bg)
+wxPoint
+wxLayoutLine::RecalculatePosition(wxLayoutList *llist)
{
- if(family != -1) m_FontFamily = family;
- if(size != -1) m_FontPtSize = size;
- if(style != -1) m_FontStyle = style;
- if(weight != -1) m_FontWeight = weight;
- if(underline != -1) m_FontUnderline = underline != 0;
-
- if(fg != NULL) m_ColourFG = fg;
- if(bg != NULL) m_ColourBG = bg;
-
- Insert(
- new wxLayoutObjectCmd(m_FontPtSize,m_FontFamily,m_FontStyle,m_FontWeight,m_FontUnderline,
- m_ColourFG, m_ColourBG));
+ if(m_Previous)
+ m_Position = m_Previous->GetPosition() +
+ wxPoint(0,m_Previous->GetHeight());
+ else
+ m_Position = wxPoint(0,0);
+ llist->SetUpdateRect(m_Position);
+ return m_Position;
}
void
-wxLayoutList::SetFont(int family, int size, int style, int weight,
- int underline, char const *fg, char const *bg)
-
+wxLayoutLine::RecalculatePositions(int recurse, wxLayoutList *llist)
{
- wxColour const
- * cfg = NULL,
- * cbg = NULL;
-
- if( fg )
- cfg = wxTheColourDatabase->FindColour(fg);
- if( bg )
- cbg = wxTheColourDatabase->FindColour(bg);
+ wxASSERT(recurse >= 0);
+ wxPoint pos = m_Position;
+ CoordType height = m_Height;
- SetFont(family,size,style,weight,underline,cfg,cbg);
+// 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);
+ }
}
-
-/// for access by wxLayoutWindow:
-void
-wxLayoutList::GetSize(CoordType *max_x, CoordType *max_y,
- CoordType *lineHeight)
+wxLayoutObjectList::iterator
+wxLayoutLine::FindObject(CoordType xpos, CoordType *offset) const
{
+ wxASSERT(xpos >= 0);
+ wxASSERT(offset);
+ wxLayoutObjectList::iterator
+ i,
+ found = NULLIT;
+ CoordType x = 0, len;
- if(max_x) *max_x = m_MaxX;
- if(max_y) *max_y = m_MaxY;
- if(lineHeight) *lineHeight = m_LineHeight;
+ /* 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
}
-void
-wxLayoutList::ResetSettings(wxDC &dc)
+wxLayoutObjectList::iterator
+wxLayoutLine::FindObjectScreen(wxDC &dc, CoordType xpos, CoordType *cxpos) const
{
- // setting up the default:
- dc.SetTextForeground( *wxBLACK );
- dc.SetTextBackground( *wxWHITE );
- dc.SetBackgroundMode( wxSOLID ); // to enable setting of text background
- dc.SetFont( *wxNORMAL_FONT );
- if(m_DefaultSetting)
- m_DefaultSetting->Draw(dc,wxPoint(0,0));
+ wxASSERT(cxpos);
+ wxASSERT(cxpos);
+ wxLayoutObjectList::iterator i;
+ CoordType x = 0, cx = 0, width;
+
+ for(i = m_ObjectList.begin(); i != NULLIT; i++)
+ {
+ (**i).Layout(dc);
+ width = (**i).GetWidth();
+ if( x <= xpos && xpos <= x + width )
+ {
+ *cxpos = cx + (**i).GetOffsetScreen(dc, xpos-x);
+ wxLogDebug("wxLayoutLine::FindObjectScreen: cursor xpos = %ld", *cxpos);
+ return i;
+ }
+ x += (**i).GetWidth();
+ cx += (**i).GetLength();
+ }
+ // behind last object:
+ *cxpos = cx;
+ return m_ObjectList.tail();
}
-void
-wxLayoutList::Layout(wxDC &dc, wxLayoutMargins *margins)
+bool
+wxLayoutLine::Insert(CoordType xpos, wxLayoutObject *obj)
{
- iterator i;
-
- // first object in current line
- wxLayoutObjectList::iterator headOfLine;
- // where we draw next
- wxPoint position, position_HeadOfLine;
- // size of last object
- wxPoint size;
- CoordType baseLine = m_FontPtSize;
- CoordType baseLineSkip = (BASELINESTRETCH * baseLine)/10;
- CoordType objBaseLine = baseLine;
- wxLayoutObjectType type;
-
- // we need to count cursor positions
- wxPoint cursorPos = wxPoint(0,0);
-
- if(margins)
+ wxASSERT(xpos >= 0);
+ wxASSERT(obj != NULL);
+ CoordType offset;
+ wxLOiterator i = FindObject(xpos, &offset);
+ if(i == NULLIT)
{
- position.y = margins->top;
- position.x = margins->left;
+ if(xpos == 0 ) // aha, empty line!
+ {
+ m_ObjectList.push_back(obj);
+ m_Length += obj->GetLength();
+ return true;
+ }
+ else
+ return false;
}
- else
+
+ 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 )
{
- position.y = 0;
- position.x = 0;
+ 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;
+}
- ResetSettings(dc);
-
- i = begin();
- headOfLine = i;
- position_HeadOfLine = position;
-
- do
+bool
+wxLayoutLine::Insert(CoordType xpos, wxString text)
+{
+ wxASSERT(xpos >= 0);
+ CoordType offset;
+ wxLOiterator i = FindObject(xpos, &offset);
+ if(i != NULLIT && (**i).GetType() == WXLO_TYPE_TEXT)
{
- if(i == end())
- return;
-
- type = (*i)->GetType();
- (*i)->Layout(dc, position, baseLine);
- size = (*i)->GetSize(&objBaseLine);
- // calculate next object's position:
- position.x += size.x;
-
- // do we need to increase the line's height?
- if(size.y > baseLineSkip)
- {
- baseLineSkip = size.y;
- i = headOfLine; position = position_HeadOfLine;
- continue;
- }
- if(objBaseLine > baseLine)
- {
- baseLine = objBaseLine;
- i = headOfLine; position = position_HeadOfLine;
- continue;
- }
+ wxLayoutObjectText *tobj = (wxLayoutObjectText *) *i;
+ tobj->GetText().insert(offset, text);
+ m_Length += text.Length();
+
+ return true;
+ }
+ else
+ return Insert(xpos, new wxLayoutObjectText(text));
+}
+
+CoordType
+wxLayoutLine::Delete(CoordType xpos, CoordType npos)
+{
+ CoordType offset, len;
- // when we reach here, the coordinates are valid, this part of
- // the loop gets run only once per object
- if(position.x > m_MaxX)
- m_MaxX = position.x;
- if(type == WXLO_TYPE_LINEBREAK)
+ wxASSERT(xpos >= 0);
+ wxASSERT(npos >= 0);
+ wxLOiterator i = FindObject(xpos, &offset);
+ while(npos > 0)
+ {
+ if(i == NULLIT) return npos;
+ // now delete from that object:
+ if((**i).GetType() != WXLO_TYPE_TEXT)
{
- cursorPos.x = 0; cursorPos.y ++;
+ 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
- cursorPos.x += (**i).CountPositions();
-
- // now check whether we have finished handling this line:
- if(type == WXLO_TYPE_LINEBREAK && i != tail())
{
- position.x = margins ? margins->left : 0;
- position.y += baseLineSkip;
- baseLine = m_FontPtSize;
- objBaseLine = baseLine; // not all objects set it
- baseLineSkip = (BASELINESTRETCH * baseLine)/10;
- headOfLine = i;
- headOfLine++;
- position_HeadOfLine = position;
+ // 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);
}
- if(i == m_CursorObject)
- CalculateCursor(dc);
- i++;
}
- while(i != end());
- m_MaxY = position.y + baseLineSkip;
+ return npos;
}
-void
-wxLayoutList::Draw(wxDC &dc,
- CoordType fromLine, CoordType toLine,
- iterator start,
- wxPoint const &translate)
+bool
+wxLayoutLine::DeleteWord(CoordType xpos)
{
- Layout(dc); // FIXME just for now
+ wxASSERT(xpos >= 0);
+ CoordType offset;
- ResetSettings(dc);
+ wxLOiterator i = FindObject(xpos, &offset);
- wxLayoutObjectList::iterator i;
-
- if(start == iterator(NULL))
- start = begin();
- else // we need to restore font settings
+ for(;;)
{
- for( i = begin() ; i != start; i++)
- if((**i).GetType() == WXLO_TYPE_CMD)
- (**i).Draw(dc,translate); // apply font settings
+ 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;
+ }
}
-
- while( start != end() && (**start).GetPosition().y < fromLine)
+ wxASSERT(0); // we should never arrive here
+}
+
+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)
{
- if((**start).GetType() == WXLO_TYPE_CMD)
- (**start).Draw(dc,translate); // apply font settings
- start++;
+ m_Next->MoveLines(-1);
+ m_Next->RecalculatePositions(1, llist);
}
- for( i = start ;
- i != end() && (toLine == -1 || (**i).GetPosition().y < toLine) ;
- i++ )
- (*i)->Draw(dc,translate);
+ wxLayoutLine *next = m_Next;
+ delete this;
+ return next;
}
-/** Erase at least to end of line */
void
-wxLayoutList::EraseAndDraw(wxDC &dc, iterator start)
-{
- //look for begin of line
- while(start != end() && start != begin() && (**start).GetType() !=
- WXLO_TYPE_LINEBREAK)
- start--;
- if(start == iterator(NULL))
- start = begin();
- if(start == iterator(NULL))
- return;
+wxLayoutLine::Draw(wxDC &dc,
+ wxLayoutList *llist,
+ const wxPoint & offset) const
+{
+ wxLayoutObjectList::iterator i;
+ wxPoint pos = offset;
+ pos = pos + GetPosition();
- wxPoint p = (**start).GetPosition();
-
- //FIXME: wxGTK: MaxX()/MaxY() broken
- //WXL_VAR(dc.MaxX()); WXL_VAR(dc.MaxY());
- dc.SetBrush(*wxWHITE_BRUSH);
- dc.SetPen(wxPen(*wxWHITE,0,wxTRANSPARENT));
- dc.DrawRectangle(p.x,p.y,2000,2000); //dc.MaxX(),dc.MaxY());
- Draw(dc,-1,-1,start,wxPoint(0,0));
- //dc.DrawRectangle(p.x,p.y,2000,2000); //dc.MaxX(),dc.MaxY());
+ pos.y += m_BaseLine;
+
+ for(i = m_ObjectList.begin(); i != NULLIT; i++)
+ {
+ (**i).Draw(dc, pos);
+ pos.x += (**i).GetWidth();
+ }
}
-
void
-wxLayoutList::CalculateCursor(wxDC &dc)
+wxLayoutLine::Layout(wxDC &dc,
+ wxLayoutList *llist,
+ wxPoint *cursorPos,
+ wxPoint *cursorSize,
+ int cx)
{
- CoordType width, height, descent;
- CoordType baseLineSkip = 20; //FIXME
+ wxLayoutObjectList::iterator i;
- if( m_CursorObject == iterator(NULL)) // empty list
- {
- m_CursorCoords = wxPoint(0,0);
- m_CursorSize = wxPoint(2,baseLineSkip);
- m_CursorMoved = false; // coords are valid
- return;
- }
- wxLayoutObjectBase &obj = **m_CursorObject;
+ CoordType
+ oldHeight = m_Height;
+ CoordType
+ topHeight, bottomHeight; // above and below baseline
+ CoordType
+ objHeight = 0,
+ objTopHeight, objBottomHeight;
+ CoordType
+ len, count = 0;
+ m_Height = 0; m_BaseLine = 0;
+ m_Width = 0;
+ topHeight = 0; bottomHeight = 0;
+ wxPoint size;
+ bool cursorFound = false;
- m_CursorCoords = obj.GetPosition();
- if(obj.GetType() == WXLO_TYPE_TEXT)
+ if(cursorPos)
{
- wxLayoutObjectText *tobj = (wxLayoutObjectText *)&obj;
- String & str = tobj->GetText();
- String sstr = str.substr(0,m_CursorOffset);
- dc.GetTextExtent(sstr,&width,&height,&descent);
- m_CursorCoords = wxPoint(m_CursorCoords.x+width,
- m_CursorCoords.y);
- m_CursorSize = wxPoint(2,height);
+ *cursorPos = m_Position;
+ if(cursorSize) *cursorSize = wxPoint(0,0);
}
- else if(obj.GetType() == WXLO_TYPE_LINEBREAK)
+
+ for(i = m_ObjectList.begin(); i != NULLIT; i++)
{
- if(m_CursorOffset == 1) // behind linebreak
- m_CursorCoords = wxPoint(0, m_CursorCoords.y + baseLineSkip);
- //m_CursorCoords = wxPoint(0, m_CursorCoords.y);
- m_CursorSize = wxPoint(2,baseLineSkip);
+ (**i).Layout(dc);
+ size = (**i).GetSize(&objTopHeight, &objBottomHeight);
+
+ if(cursorPos && ! cursorFound)
+ { // we need to check whether the text cursor is here
+ len = (**i).GetLength();
+ if(count <= cx && count+len > cx)
+ {
+ if((**i).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 < (**i).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 = objHeight;
+ cursorSize->x = width;
+ cursorSize->y = height;
+ cursorFound = true; // no more checks
+ }
+ else
+ { // on some other object
+ CoordType top, bottom; // unused
+ *cursorSize = (**i).GetSize(&top,&bottom);
+ cursorPos->y = m_Position.y;
+ cursorFound = true; // no more checks
+ }
+ }
+ else
+ {
+ count += len;
+ cursorPos->x += (**i).GetWidth();
+ }
+ } // cursor finding
+ objHeight = size.y;
+ m_Width += size.x;
+ if(objHeight > m_Height) m_Height = objHeight;
+ if(objTopHeight > topHeight) topHeight = objTopHeight;
+ if(objBottomHeight > bottomHeight) bottomHeight = objBottomHeight;
}
- else
+ if(topHeight + bottomHeight > m_Height) m_Height =
+ topHeight+bottomHeight;
+ m_BaseLine = topHeight;
+
+ if(m_Height == 0)
{
- // this is not necessarily the most "beautiful" solution:
- //cursorPosition = wxPoint(position.x, position.y);
- //cursorSize = wxPoint(size.x > 0 ? size.x : 1,size.y > 0 ? size.y : baseLineSkip);
- m_CursorCoords = wxPoint(m_CursorCoords.x+obj.GetSize().x, m_CursorCoords.y);
- m_CursorSize = wxPoint(2, obj.GetSize().y);
- if(m_CursorSize.y < 1) m_CursorSize.y = baseLineSkip;
+ if(GetPreviousLine()) // empty line
+ {
+ m_Height = GetPreviousLine()->GetHeight();
+ m_BaseLine = GetPreviousLine()->m_BaseLine;
+ }
+ else
+ {
+ CoordType width, height, descent;
+ dc.GetTextExtent(WXLO_CURSORCHAR, &width, &height, &descent);
+ m_Height = height;
+ m_BaseLine = m_Height - descent;
+ }
}
- m_CursorMoved = false; // coords are valid
-}
-void
-wxLayoutList::DrawCursor(wxDC &dc, bool erase)
-{
- if(! m_Editable)
- return;
- if(erase)
- {
- //dc.SetBrush(*wxWHITE_BRUSH);
- //dc.SetPen(wxPen(*wxWHITE,1,wxSOLID));
- //dc.DrawRectangle(m_CursorCoords.x, m_CursorCoords.y, m_CursorSize.x, m_CursorSize.y);
- dc.Blit(m_CursorCoords.x, m_CursorCoords.y, m_CursorSize.x,
- m_CursorSize.y, &m_CursorMemDC,
- 0, 0, 0, 0);
- }
- else
+ // tell next line about coordinate change
+ if(m_Next && objHeight != oldHeight)
+ m_Next->RecalculatePositions(0, llist);
+
+ // We need to check whether we found a valid cursor size:
+ if(cursorPos)
{
- if(IsDirty() || CursorMoved())
+ // 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)
{
- DrawCursor(dc,true);
- Layout(dc);
+ CoordType width, height, descent;
+ dc.GetTextExtent(WXLO_CURSORCHAR, &width, &height, &descent);
+ cursorSize->x = width;
+ cursorSize->y = height;
}
- // Save background:
- wxBitmap bm(m_CursorSize.x+1,m_CursorSize.y+1);
- m_CursorMemDC.SelectObject(bm);
- m_CursorMemDC.Blit(0, 0, m_CursorSize.x, m_CursorSize.y,
- &dc, m_CursorCoords.x,
- m_CursorCoords.y, 0, 0);
- dc.SetBrush(*wxBLACK_BRUSH);
- dc.SetPen(wxPen(*wxBLACK,1,wxSOLID));
- dc.DrawRectangle(m_CursorCoords.x, m_CursorCoords.y,
- m_CursorSize.x, m_CursorSize.y);
+ if(m_BaseLine >= cursorSize->y) // the normal case anyway
+ cursorPos->y += m_BaseLine-cursorSize->y;
}
}
-
-
-
-
-
-#ifdef WXLAYOUT_DEBUG
-void
-wxLayoutList::Debug(void)
+wxLayoutLine *
+wxLayoutLine::Break(CoordType xpos, wxLayoutList *llist)
{
- wxLayoutObjectList::iterator i;
-
- wxLayoutDebug("------------------------ debug start ------------------------");
- for(i = begin(); i != end(); i++)
- (*i)->Debug();
- wxLayoutDebug("-------------------------- list end -------------------------");
+ wxASSERT(xpos >= 0);
+
+ if(xpos == 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 = GetHeight(); // this is a wild guess
+ }
+ MoveLines(+1);
+ if(m_Next)
+ m_Next->RecalculatePositions(1, llist);
+ return this;
+ }
- // show current object:
- ShowCurrentObject();
- wxLayoutDebug("------------------------- debug end -------------------------");
+ 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())
+ {
+ newLine->Append(*i);
+ m_Length -= (**i).GetLength();
+ m_ObjectList.remove(i); // remove without deleting it
+ }
+ if(m_Next)
+ m_Next->RecalculatePositions(2, llist);
+ return newLine;
}
+
void
-wxLayoutList::ShowCurrentObject()
+wxLayoutLine::MergeNextLine(wxLayoutList *llist)
{
- wxLayoutDebug("CursorPos (%d, %d)", (int) m_CursorPos.x, (int) m_CursorPos.y);
- wxLayoutDebug("CursorOffset = %d", (int) m_CursorOffset);
- wxLayoutDebug("CursorObject = %p", m_CursorObject);
- if(m_CursorObject == iterator(NULL))
- wxLayoutDebug("<<no object found>>");
- else
+ wxASSERT(GetNextLine());
+ wxLayoutObjectList &list = GetNextLine()->m_ObjectList;
+ wxLOiterator i;
+
+ for(i = list.begin(); i != list.end();)
{
- if((*m_CursorObject)->GetType() == WXLO_TYPE_TEXT)
- wxLayoutDebug(" \"%s\", offs: %d",
- ((wxLayoutObjectText *)(*m_CursorObject))->GetText().c_str(),
- m_CursorOffset);
- else
- wxLayoutDebug(" %s", TypeString((*m_CursorObject)->GetType()));
+ Append(*i);
+ list.remove(i); // remove without deleting it
}
- wxLayoutDebug("Line length: %d", GetLineLength(m_CursorObject));
-
+ wxASSERT(list.empty());
+ wxLayoutLine *oldnext = GetNextLine();
+ SetNext(GetNextLine()->GetNextLine());
+ delete oldnext;
+ RecalculatePositions(1, llist);
}
-#endif
-
-/******************** editing stuff ********************/
-
-// don't change this, I know how to optimise this and will do it real
-// soon (KB)
-
-/*
- * FindObjectCursor:
- * Finds the object belonging to a given cursor position cpos and
- * returns an iterator to that object and stores the relative cursor
- * position in offset.
- *
- * For linebreaks, the offset can be 0=before or 1=after.
- *
- * If the cpos coordinates don't exist, they are modified.
- */
-
-wxLayoutObjectList::iterator
-wxLayoutList::FindObjectCursor(wxPoint *cpos, CoordType *offset)
+CoordType
+wxLayoutLine::GetWrapPosition(CoordType column)
{
- wxPoint object = wxPoint(0,0); // runs along the objects
- CoordType width = 0;
- wxLayoutObjectList::iterator i, begin_it;
- int go_up;
-
-//#ifdef WXLAYOUT_DEBUG
-// wxLayoutDebug("Looking for object at (%d, %d)", cpos->x, cpos->y);
-//#endif
-
- // optimisation: compare to last looked at object:
- if(cpos->y > m_FoundCursor.y || (cpos->y == m_FoundCursor.y &&
- cpos->x >= m_FoundCursor.x))
- go_up = 1;
- else
- go_up = 0;
-
- //broken at the moment
- //begin_it = m_FoundIterator;
- //m_FoundCursor = *cpos;
- begin_it = begin();
- go_up = 1;
- for(i = begin_it; i != end() && object.y <= cpos->y; )
+ 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
{
- width = (**i).CountPositions();
- if(cpos->y == object.y) // a possible candidate
+ if((**i).GetType() == WXLO_TYPE_TEXT)
{
- if((**i).GetType() ==WXLO_TYPE_LINEBREAK)
+ do
{
- if(cpos->x == object.x)
+ if( isspace(((wxLayoutObjectText*)*i)->GetText().c_str()[(size_t)offset]))
+ return column;
+ else
{
- if(offset) *offset = 0;
- return m_FoundIterator = i;
+ offset--;
+ column--;
}
- if(offset) *offset=1;
- cpos->x = object.x;
- return m_FoundIterator = i;
- }
- if(cpos->x >= object.x && cpos->x <= object.x+width) // overlap
- {
- if(offset) *offset = cpos->x-object.x;
-//#ifdef WXLAYOUT_DEBUG
-// wxLayoutDebug(" found object at (%d, %d), type: %s",
-// object.x, object.y, TypeString((*i)->GetType()));
-//#endif
- return m_FoundIterator = i;
- }
- }
- // no overlap, increment coordinates
- object.x += width;
- if((**i).GetType() == WXLO_TYPE_LINEBREAK)
- {
- object.x = 0;
- object.y++;
+ }while(offset != -1);
+ i--; // move on to previous object
}
- if(go_up)
- i++;
else
+ {
+ column -= (**i).GetLength();
i--;
- }//for
-//#ifdef WXLAYOUT_DEBUG
-// wxLayoutDebug(" not found");
-//#endif
-// return last object, coordinates of that one:
- i = tail();
- if(i == end())
- return m_FoundIterator = i;
- if((**i).GetType()==WXLO_TYPE_LINEBREAK)
+ }
+ 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)
{
- if(offset)
- *offset = 1;
- return m_FoundIterator = i;
+ pos += (**i).GetLength();
+ i++;
}
- cpos->x = object.x; // would be the coordinate of next object
- cpos->y = object.y;
- cpos->x += width; // last object's width
- if(*offset) *offset = cpos->x-object.x;
- return m_FoundIterator = i; // not found
+ 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
}
+
-bool
-wxLayoutList::MoveCursor(int dx, int dy)
-{
- CoordType diff;
-
- m_CursorMoved = true;
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+
+ The wxLayoutList object
- enum { up, down} direction;
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- wxPoint newPos = wxPoint(m_CursorPos.x + dx,
- m_CursorPos.y + dy);
+wxLayoutList::wxLayoutList()
+{
+ m_DefaultSetting = NULL;
+ m_FirstLine = NULL;
+ m_ColourFG = *wxBLACK;
+ m_ColourBG = *wxWHITE;
+ InvalidateUpdateRect();
+ Clear();
+}
- // check for bounds
- //if(newPos.x < 0) newPos.x = 0;
- if(newPos.y < 0) newPos.y = 0;
- else if(newPos.y > m_MaxLine) newPos.y = m_MaxLine;
+wxLayoutList::~wxLayoutList()
+{
+ InternalClear();
+ m_FirstLine->DeleteLine(false, this);
+}
- if(newPos.y > m_CursorPos.y ||
- newPos.y == m_CursorPos.y &&
- newPos.x >= m_CursorPos.x)
- direction = down;
- else
- direction = up;
+void
+wxLayoutList::Empty(void)
+{
+ while(m_FirstLine)
+ m_FirstLine = m_FirstLine->DeleteLine(false, this);
- wxASSERT(m_CursorObject);
- // now move cursor forwards until at the new position:
+ m_CursorPos = wxPoint(0,0);
+ m_CursorScreenPos = wxPoint(0,0);
+ m_CursorSize = wxPoint(0,0);
+ m_FirstLine = new wxLayoutLine(NULL, this); // empty first line
+ m_CursorLine = m_FirstLine;
+ InvalidateUpdateRect();
+}
- // first, go to the right line:
- while(newPos.y != m_CursorPos.y)
- {
- if(direction == down)
- {
- m_CursorPos.x +=
- (**m_CursorObject).CountPositions() - m_CursorOffset;
- if(m_CursorObject == tail())
- break; // can't go any further
- if((**m_CursorObject).GetType() == WXLO_TYPE_LINEBREAK
- && m_CursorOffset == 0)
- {
- m_CursorPos.y++; m_CursorPos.x = 0;
- }
- m_CursorObject ++; m_CursorOffset = 0;
- }
- else // up
- {
- if(m_CursorObject == begin())
- break; // can't go any further
- if((**m_CursorObject).GetType() == WXLO_TYPE_LINEBREAK &&
- m_CursorOffset == 1)
- {
- m_CursorPos.y--;
- m_CursorPos.x = GetLineLength(m_CursorObject);
- }
- m_CursorPos.x -= m_CursorOffset;
- m_CursorObject --; m_CursorOffset = (**m_CursorObject).CountPositions();
- }
- }
- if(newPos.y != m_CursorPos.y) // reached begin/end of list,
- newPos.y = m_CursorPos.y; // exited by break
-
- // now line is right, go to right column:
- direction = newPos.x >= m_CursorPos.x ? down : up;
- while(newPos.x != m_CursorPos.x)
+void
+wxLayoutList::InternalClear(void)
+{
+ Empty();
+ if(m_DefaultSetting)
{
- if(direction == down)
- {
- diff = newPos.x - m_CursorPos.x;
- if(diff > (**m_CursorObject).CountPositions()-m_CursorOffset)
- {
- m_CursorPos.x +=
- (**m_CursorObject).CountPositions()-m_CursorOffset;
- if((**m_CursorObject).GetType() == WXLO_TYPE_LINEBREAK &&
- m_CursorOffset == 0)
- m_CursorPos = wxPoint(0, m_CursorPos.y+1);
- if(m_CursorObject == tail())
- break; // cannot go further
- m_CursorObject++; m_CursorOffset = 0;
- }
- else
- {
- if((**m_CursorObject).GetType() == WXLO_TYPE_LINEBREAK)
- {
- newPos.y++; newPos.x -= m_CursorPos.x+1;
- m_CursorPos = wxPoint(0,m_CursorPos.y+1);
- }
- else
- m_CursorPos.x += diff;
- m_CursorOffset += diff;
- }
- }
- else // up
- {
- if(m_CursorPos.x == 0 && m_CursorOffset == 1 &&
- (**m_CursorObject).GetType() == WXLO_TYPE_LINEBREAK) // can we go further up?
- {
- m_CursorPos.y--;
- newPos.y--;
- m_CursorOffset = 0;
- m_CursorPos.x = GetLineLength(m_CursorObject)-1;
- newPos.x += m_CursorPos.x+1;
- continue;
- }
- diff = m_CursorPos.x - newPos.x;
- if(diff >= m_CursorOffset)
- {
- if(m_CursorObject == begin())
- {
- m_CursorOffset = 0;
- m_CursorPos.x = 0;
- break; // cannot go further
- }
- m_CursorObject--;
- m_CursorPos.x -= m_CursorOffset;
- m_CursorOffset = (**m_CursorObject).CountPositions();
- }
- else
- {
- m_CursorPos.x -= diff;
- m_CursorOffset -= diff;
- }
- }
+ delete m_DefaultSetting;
+ m_DefaultSetting = NULL;
}
- return true; // FIXME: when return what?
}
void
-wxLayoutList::Delete(CoordType count)
+wxLayoutList::SetFont(int family, int size, int style, int weight,
+ int underline, wxColour *fg,
+ wxColour *bg)
{
- WXL_TRACE(Delete);
+ if(family != -1) m_FontFamily = family;
+ if(size != -1) m_FontPtSize = size;
+ if(style != -1) m_FontStyle = style;
+ if(weight != -1) m_FontWeight = weight;
+ if(underline != -1) m_FontUnderline = underline != 0;
+
+ if(fg != NULL) m_ColourFG = *fg;
+ if(bg != NULL) m_ColourBG = *bg;
+
+ Insert(
+ new wxLayoutObjectCmd(m_FontPtSize,m_FontFamily,m_FontStyle,m_FontWeight,m_FontUnderline,
+ m_ColourFG, m_ColourBG));
+}
+
+void
+wxLayoutList::SetFont(int family, int size, int style, int weight,
+ int underline, char const *fg, char const *bg)
- if(!m_Editable)
- return;
+{
+ wxColour
+ *cfg = NULL,
+ *cbg = NULL;
- m_bModified = true;
+ if( fg )
+ cfg = wxTheColourDatabase->FindColour(fg);
+ if( bg )
+ cbg = wxTheColourDatabase->FindColour(bg);
- CoordType offs;
- wxLayoutObjectList::iterator i;
-
- do
- {
- i = m_CursorObject;
- startover: // ugly, but easiest way to do it
- if(i == end())
- return; // we cannot delete anything more
-
-/* Here we need to treat linebreaks differently.
- If m_CursorOffset==0 we are before the linebreak, otherwise behind. */
- if((*i)->GetType() == WXLO_TYPE_LINEBREAK)
- {
- if(m_CursorOffset == 0)
- {
- m_MaxLine--;
- erase(i);
- m_CursorObject = i; // new i!
- m_CursorOffset = 0; // Pos unchanged
- count--;
- continue; // we're done
- }
- else // delete the object behind the linebreak
- {
- i++; // we increment and continue as normal
- m_CursorOffset=0;
- goto startover;
- }
- }
- else if((*i)->GetType() == WXLO_TYPE_TEXT)
- {
- wxLayoutObjectText *tobj = (wxLayoutObjectText *)*i;
- CoordType len = tobj->CountPositions();
-// If we find the end of a text object, this means that we
-// have to delete from the object following it.
- if(len == m_CursorOffset)
- {
- i++;
- m_CursorOffset = 0;
- goto startover;
- }
- else if(len <= count) // delete this object
- {
- count -= len;
- erase(i);
- m_CursorObject = i;
- m_CursorOffset = 0;
- continue;
- }
- else
- {
- len = count;
- tobj->GetText().erase(m_CursorOffset,len);
- // cursor unchanged
- return; // we are done
- }
- }
- else// all other objects: delete the object
-// this only works as expected if the non-text object has 0/1
-// as offset values. Not tested with "longer" objects.
- {
- CoordType len = (*i)->CountPositions();
- if(offs == 0)
- {
- count = count > len ? count -= len : 0;
- erase(i); // after this, i is the iterator for the
- // following object
- m_CursorObject = i;
- m_CursorOffset = 0;
- continue;
- }
- else // delete the following object
- {
- i++; // we increment and continue as normal
- m_CursorOffset=0;
- goto startover;
- }
- }
- }
- while(count && i != end());
+ SetFont(family,size,style,weight,underline,cfg,cbg);
}
void
-wxLayoutList::Insert(wxLayoutObjectBase *obj)
+wxLayoutList::Clear(int family, int size, int style, int weight,
+ int /* underline */, wxColour *fg, wxColour *bg)
{
- wxCHECK_RET( obj, "no object to insert" );
+ InternalClear();
+
+ // set defaults
+ m_FontPtSize = size;
+ m_FontUnderline = false;
+ m_FontFamily = family;
+ m_FontStyle = style;
+ m_FontWeight = weight;
+ if(fg) m_ColourFG = *fg;
+ if(bg) m_ColourBG = *bg;
- m_bModified = true;
+ m_ColourFG = *wxBLACK;
+ m_ColourBG = *wxWHITE;
+
+ if(m_DefaultSetting)
+ delete m_DefaultSetting;
+
+ m_DefaultSetting = new
+ wxLayoutObjectCmd(m_FontPtSize,m_FontFamily,m_FontStyle,
+ m_FontWeight,m_FontUnderline,
+ m_ColourFG, m_ColourBG);
+}
- wxLayoutObjectList::iterator i = m_CursorObject;
- if(i != iterator(NULL) && (*obj).GetType() == WXLO_TYPE_LINEBREAK)
+
+bool
+wxLayoutList::MoveCursorTo(wxPoint const &p)
+{
+ SetUpdateRect(m_CursorScreenPos);
+ SetUpdateRect(m_CursorScreenPos+m_CursorSize);
+ wxLayoutLine *line = m_FirstLine;
+ while(line && line->GetLineNumber() != p.y)
+ line = line->GetNextLine();
+ if(line && line->GetLineNumber() == p.y) // found it
{
- m_CursorPos.x = 0; m_CursorPos.y ++;
+ m_CursorPos.y = p.y;
+ m_CursorLine = line;
+ CoordType len = line->GetLength();
+ if(len >= p.x)
+ {
+ m_CursorPos.x = p.x;
+ return true;
+ }
+ else
+ {
+ m_CursorPos.x = len;
+ return false;
+ }
}
+ return false;
+}
- if(i == end())
- {
- push_back(obj);
- m_CursorObject = tail();
- }
- else if(m_CursorOffset == 0)
- {
- insert(i,obj);
- m_CursorObject = i;
- }
- // do we have to split a text object?
- else if((*i)->GetType() == WXLO_TYPE_TEXT && m_CursorOffset != (*i)->CountPositions())
+bool
+wxLayoutList::MoveCursorVertically(int n)
+{
+ SetUpdateRect(m_CursorScreenPos);
+ SetUpdateRect(m_CursorScreenPos+m_CursorSize);
+ bool rc;
+ if(n < 0) // move up
{
- wxLayoutObjectText *tobj = (wxLayoutObjectText *) *i;
- String left = tobj->GetText().substr(0,m_CursorOffset); // get part before cursor
- tobj->GetText() = tobj->GetText().substr(m_CursorOffset,(*i)->CountPositions()-m_CursorOffset); // keeps the right half
- insert(i,obj);
- m_CursorObject = i; // == obj
- insert(i,new wxLayoutObjectText(left)); // inserts before
+ 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
+ else // move down
{
- // all other cases, append after object:
- wxLayoutObjectList::iterator j = i; // we want to apend after this object
- j++;
- if(j != end())
+ wxLayoutLine *last = m_CursorLine;
+ if(! m_CursorLine->GetNextLine()) return false;
+ while(n > 0 && m_CursorLine)
{
- insert(j, obj);
- m_CursorObject = j;
+ n--;
+ m_CursorPos.y ++;
+ m_CursorLine = m_CursorLine->GetNextLine();
+ }
+ if(! m_CursorLine)
+ {
+ m_CursorLine = last;
+ m_CursorPos.y ++;
+ rc = false;
}
else
{
- push_back(obj);
- m_CursorObject = tail();
+ if(m_CursorPos.x > m_CursorLine->GetLength())
+ m_CursorPos.x = m_CursorLine->GetLength();
+ rc = true;
}
}
-
- if(obj->GetType() != WXLO_TYPE_LINEBREAK) // handled separately above
- m_CursorPos.x += obj->CountPositions();
- // applies also for linebreak:
- m_CursorOffset = obj->CountPositions();
-
- if(obj->GetType() == WXLO_TYPE_LINEBREAK)
- m_MaxLine++;
- m_CursorMoved = true;
+ return rc;
}
-void
-wxLayoutList::Insert(String const &text)
+bool
+wxLayoutList::MoveCursorHorizontally(int n)
{
- wxLayoutObjectText *tobj = NULL;
- wxLayoutObjectList::iterator j;
+ SetUpdateRect(m_CursorScreenPos);
+ SetUpdateRect(m_CursorScreenPos+m_CursorSize);
+ 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;
+ }
-// WXL_TRACE(Insert(text));
+ 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;
+ }
+ return n == 0;
+}
- if(! m_Editable)
- return;
+bool
+wxLayoutList::Insert(wxString const &text)
+{
+ wxASSERT(m_CursorLine);
+ SetUpdateRect(m_CursorScreenPos);
+ SetUpdateRect(m_CursorScreenPos+m_CursorSize);
+ m_CursorLine->Insert(m_CursorPos.x, text);
+ m_CursorPos.x += text.Length();
+ return true;
+}
- m_bModified = true;
+bool
+wxLayoutList::Insert(wxLayoutObject *obj)
+{
+ wxASSERT(m_CursorLine);
+ SetUpdateRect(m_CursorScreenPos);
+ SetUpdateRect(m_CursorScreenPos+m_CursorSize);
+ m_CursorLine->Insert(m_CursorPos.x, obj);
+ m_CursorPos.x += obj->GetLength();
+ return true;
+}
- wxLayoutObjectList::iterator i = m_CursorObject;
+bool
+wxLayoutList::LineBreak(void)
+{
+ wxASSERT(m_CursorLine);
+ bool setFirst = (m_CursorLine == m_FirstLine && m_CursorPos.x == 0);
+ SetUpdateRect(m_CursorScreenPos);
+ SetUpdateRect(m_CursorScreenPos+m_CursorSize);
+ m_CursorLine = m_CursorLine->Break(m_CursorPos.x, this);
+ if(setFirst) // we were at beginning of first line
+ m_FirstLine = m_CursorLine->GetPreviousLine();
+ m_CursorPos.y++;
+ m_CursorPos.x = 0;
+ return true;
+}
- if(i == end())
+bool
+wxLayoutList::WrapLine(CoordType column)
+{
+ if(m_CursorPos.x <= column || column < 1)
+ return false; // do nothing yet
+ else
{
- Insert(new wxLayoutObjectText(text));
- return;
+ 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;
+ SetUpdateRect(m_CursorScreenPos);
+ SetUpdateRect(m_CursorScreenPos+m_CursorSize);
+ LineBreak();
+ Delete(1); // delete the space
+ m_CursorPos.x = newpos;
+ return true;
}
+}
- switch((**i).GetType())
+bool
+wxLayoutList::Delete(CoordType npos)
+{
+ wxASSERT(m_CursorLine);
+ SetUpdateRect(m_CursorScreenPos);
+ SetUpdateRect(m_CursorScreenPos+m_CursorSize);
+ CoordType left;
+ do
{
- case WXLO_TYPE_TEXT:
-// insert into an existing text object:
- tobj = (wxLayoutObjectText *)*i ;
- wxASSERT(tobj);
- tobj->GetText().insert(m_CursorOffset,text);
- m_CursorObject = i;
- m_CursorOffset = m_CursorOffset + text.length();
- m_CursorPos.x += text.length();
- break;
- case WXLO_TYPE_LINEBREAK:
- default:
- j = i;
- if(m_CursorOffset == 0) // try to append to previous object
- {
- j--;
- if(j != end() && (**j).GetType() == WXLO_TYPE_TEXT)
- {
- tobj = (wxLayoutObjectText *)*j;
- tobj->GetText()+=text;
- m_CursorObject = j;
- m_CursorOffset = (**j).CountPositions();
- m_CursorPos.x += text.length();
- }
- else
- {
- insert(i,new wxLayoutObjectText(text));
- m_CursorObject = i;
- m_CursorOffset = (**i).CountPositions();
- m_CursorPos.x += m_CursorOffset;
- }
+ left = m_CursorLine->Delete(m_CursorPos.x, npos);
+ if(left == 0)
+ return true;
+ // 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 // offset == 1 : cursor after linebreak
+ else
{
- j++;
- m_CursorObject = j;
- m_CursorOffset = 0;
- if(j != end() && (**j).GetType() == WXLO_TYPE_TEXT)
- {
- tobj = (wxLayoutObjectText *)*j;
- tobj->GetText()=text+tobj->GetText();
- m_CursorOffset = text.length();
- m_CursorPos.x += m_CursorOffset;
- }
+ // Need to join next line
+ if(! m_CursorLine->GetNextLine())
+ break; // cannot
else
{
- if(j == end())
- {
- push_back(new wxLayoutObjectText(text));
- m_CursorObject = tail();
- m_CursorOffset = (**m_CursorObject).CountPositions();
- m_CursorPos.x += text.length();
- }
- else
- {
- insert(j,new wxLayoutObjectText(text));
- m_CursorObject = j;
- m_CursorOffset = (**j).CountPositions();
- m_CursorPos.x += text.length();
- }
+ m_CursorLine->MergeNextLine(this);
+ left--;
}
}
- break;
}
- m_CursorMoved = true;
+ while(left);
+ return left == 0;
}
-CoordType
-wxLayoutList::GetLineLength(wxLayoutObjectList::iterator i, CoordType offs)
+int
+wxLayoutList::DeleteLines(int n)
{
- if(i == end())
- return 0;
+ wxASSERT(m_CursorLine);
+ wxLayoutLine *line;
+ SetUpdateRect(m_CursorScreenPos);
+ SetUpdateRect(m_CursorScreenPos+m_CursorSize);
+ while(n > 0)
+ {
+ if(!m_CursorLine->GetNextLine())
+ { // we cannot delete this line, but we can clear it
+ MoveCursorToBeginOfLine();
+ DeleteToEndOfLine();
+ 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;
+}
- CoordType len = 0;
+void
+wxLayoutList::Recalculate(wxDC &dc, CoordType bottom)
+{
+ wxLayoutLine *line = m_FirstLine;
- if(offs == 0 && (**i).GetType() == WXLO_TYPE_LINEBREAK)
- i--;
-
-// search backwards for beginning of line:
- while(i != begin() && (*i)->GetType() != WXLO_TYPE_LINEBREAK)
- i--;
- if((*i)->GetType() == WXLO_TYPE_LINEBREAK)
- i++;
-// now we can start counting:
- while(i != end() && (*i)->GetType() != WXLO_TYPE_LINEBREAK)
+ // first, make sure everything is calculated - this might not be
+ // needed, optimise it later
+ m_DefaultSetting->Layout(dc);
+ while(line)
{
- len += (*i)->CountPositions();
- i++;
+ 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();
}
- len++; // one extra for the linebreak
- return len;
}
void
-wxLayoutList::Clear(int family, int size, int style, int weight,
- int underline, char const *fg, char const *bg)
+wxLayoutList::UpdateCursorScreenPos(wxDC &dc)
{
- m_bModified = true;
- m_CursorMoved = true;
- m_dirty = true; // force redraw/recalc
- wxLayoutObjectList::iterator i = begin();
+ wxASSERT(m_CursorLine);
+ m_CursorLine->Layout(dc, this, (wxPoint *)&m_CursorScreenPos, (wxPoint *)&m_CursorSize, m_CursorPos.x);
+}
- wxBitmap bm(1,1);
- m_CursorMemDC.SelectObject(bm);
+wxPoint
+wxLayoutList::GetCursorScreenPos(wxDC &dc)
+{
+ UpdateCursorScreenPos(dc);
+ return m_CursorScreenPos;
+}
- while(i != end()) // == while valid
- erase(i);
+void
+wxLayoutList::Layout(wxDC &dc, CoordType bottom)
+{
+ wxLayoutLine *line = m_FirstLine;
- // set defaults
- m_FontPtSize = size;
- m_FontUnderline = false;
- m_FontFamily = family;
- m_FontStyle = style;
- m_FontWeight = weight;
- m_ColourFG = wxTheColourDatabase->FindColour(fg);
- m_ColourBG = wxTheColourDatabase->FindColour(bg);
+ // first, make sure everything is calculated - this might not be
+ // needed, optimise it later
+ m_DefaultSetting->Layout(dc);
+ while(line)
+ {
+ 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;
+ line = line->GetNextLine();
+ }
- if(! m_ColourFG) m_ColourFG = wxBLACK;
- if(! m_ColourBG) m_ColourBG = wxWHITE;
-
- m_Position = wxPoint(0,0);
- m_CursorPos = wxPoint(0,0);
- m_CursorObject = iterator(NULL);
- m_CursorOffset = 0;
- m_CursorSize = wxPoint(2,(BASELINESTRETCH*m_FontPtSize)/10);
-
- m_MaxLine = 1;
- m_LineHeight = (BASELINESTRETCH*m_FontPtSize)/10;
- m_MaxX = 0; m_MaxY = 0;
+///FIXME: disabled for now
+#if 0
+ // 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
+ SetUpdateRect(m_CursorScreenPos);
+ SetUpdateRect(m_CursorScreenPos+m_CursorSize);
+}
+void
+wxLayoutList::Draw(wxDC &dc, wxPoint const &offset,
+ CoordType top, CoordType bottom)
+{
+ wxLayoutLine *line = m_FirstLine;
- m_FoundCursor = wxPoint(0,0);
- m_FoundIterator = begin();
+ Layout(dc, bottom);
+ m_DefaultSetting->Draw(dc, wxPoint(0,0));
+ wxBrush brush(m_ColourBG, wxSOLID);
+ dc.SetBrush(brush);
- if(m_DefaultSetting)
- delete m_DefaultSetting;
-
- m_DefaultSetting = new
- wxLayoutObjectCmd(m_FontPtSize,m_FontFamily,m_FontStyle,
- m_FontWeight,m_FontUnderline,
- m_ColourFG, m_ColourBG);
+ while(line)
+ {
+ // only draw if between top and bottom:
+ if((top == -1 || line->GetPosition().y >= top))
+ line->Draw(dc, this, offset);
+ // little condition to speed up redrawing:
+ if(bottom != -1 && line->GetPosition().y + line->GetHeight() > bottom) break;
+ 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));
+ InvalidateUpdateRect();
}
+wxLayoutObject *
+wxLayoutList::FindObjectScreen(wxDC &dc, wxPoint const pos, wxPoint *cursorPos)
+{
+ // First, find the right line:
+ wxLayoutLine *line = m_FirstLine;
+ wxPoint p;
+
+ // we need to run a layout here to get font sizes right :-(
+ m_DefaultSetting->Layout(dc);
+ while(line)
+ {
+ p = line->GetPosition();
+ if(p.y <= pos.y && p.y+line->GetHeight() >= pos.y)
+ break;
+ line->Layout(dc, this);
+ line = line->GetNextLine();
+ }
+ if(line == NULL) 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->x);
+ return (i == NULLIT) ? NULL : *i;
+
+}
-wxLayoutObjectBase *
-wxLayoutList::Find(wxPoint coords) const
+wxPoint
+wxLayoutList::GetSize(void) const
{
- wxLayoutObjectList::iterator i = begin();
+ wxLayoutLine
+ *line = m_FirstLine,
+ *last = line;
+ if(! line)
+ return wxPoint(0,0);
- wxPoint topleft, bottomright;
+ wxPoint maxPoint(0,0);
- while(i != end()) // == while valid
+ // find last line:
+ while(line)
{
- wxLayoutObjectBase *object = *i;
- topleft = object->GetPosition();
- if(coords.y >= topleft.y && coords.x >= topleft.x)
- {
- bottomright = topleft;
- bottomright.x += object->GetSize().x;
- bottomright.y += object->GetSize().y;
- if(coords.x <= bottomright.x && coords.y <= bottomright.y)
- return *i;
- }
- i++;
+ if(line->GetWidth() > maxPoint.x)
+ maxPoint.x = line->GetWidth();
+ last = line;
+ line = line->GetNextLine();
}
- return NULL;
-}
+ maxPoint.y = last->GetPosition().y + last->GetHeight();
+ return maxPoint;
+}
void
-wxLayoutList::SetWrapMargin(long n = -1)
+wxLayoutList::DrawCursor(wxDC &dc, bool active, wxPoint const &translate)
{
- m_WrapMargin = n;
+ wxPoint coords;
+ coords = m_CursorScreenPos;
+ coords.x += translate.x;
+ coords.y += translate.y;
+
+#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()));
+#endif
+
+ 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);
+ else
+ dc.DrawLine(coords.x, coords.y+m_CursorSize.y-1,
+ coords.x+m_CursorSize.x, coords.y+m_CursorSize.y-1);
+ dc.SetLogicalFunction(wxCOPY);
+ //dc.SetBrush(wxNullBrush);
}
+/** Called by the objects to update the update rectangle.
+ @param p a point to include in it
+*/
void
-wxLayoutList::WrapLine(void)
+wxLayoutList::SetUpdateRect(const wxPoint &p)
{
- wxASSERT(m_CursorObject);
+ if(m_UpdateRectValid)
+ GrowRect(m_UpdateRect, p);
+ else
+ {
+ m_UpdateRect.x = p.x;
+ m_UpdateRect.y = p.y;
+ m_UpdateRect.width = 4; // large enough to avoid surprises from
+ m_UpdateRect.height = 4;// wxGTK :-)
+ m_UpdateRectValid = true;
+ }
+}
- iterator i = m_CursorObject;
+void
+wxLayoutList::StartSelection(void)
+{
+ wxLogDebug("Starting selection at %ld/%ld", m_CursorPos.x, m_CursorPos.y);
+ m_Selection.m_CursorA = m_CursorPos;
+}
- if(!DoWordWrap() || !i ) // empty list
- return;
- int cursorpos = m_CursorPos.x, cpos, offset;
-
- if(cursorpos < m_WrapMargin)
- return;
+void
+wxLayoutList::EndSelection(void)
+{
+ wxLogDebug("Ending selection at %ld/%ld", m_CursorPos.x, m_CursorPos.y);
+ m_Selection.m_CursorB = m_CursorPos;
+}
- // else: break line
+bool
+wxLayoutList::IsSelected(const wxPoint &cursor)
+{
+ return m_Selection.m_CursorA <= cursor
+ && cursor <= m_Selection.m_CursorB;
+}
- // find the right object to break:
- // is it the current one?
- i = m_CursorObject;
- cpos = cursorpos-m_CursorOffset;
- while(i != begin() && cpos >= m_WrapMargin)
- {
- i--;
- cpos -= (**i).CountPositions();
- }
- // now i is the object to break and cpos its position
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- offset = m_WrapMargin - cpos;
- wxASSERT(offset <= (**i).CountPositions());
+ wxLayoutPrintout
- // split it
- if((**i).GetType() == WXLO_TYPE_TEXT)
- {
- wxLayoutObjectText &t = *(wxLayoutObjectText *)*i;
- for(; offset > 0; offset--)
- if(t.GetText().c_str()[offset] == ' ' || t.GetText().c_str()[offset] == '\t')
- {
- String left = t.GetText().substr(0,offset-1); // get part before cursor
- t.GetText() = t.GetText().substr(offset,t.CountPositions()-offset); // keeps the right halve
- insert(i,new wxLayoutObjectLineBreak);
- insert(i,new wxLayoutObjectText(left)); // inserts before
- break;
- }
- if(offset == 0)
- {
- // only insert a line break if there isn't already one
- iterator j = i; j--;
- if(j && j != begin() && (**j).GetType() != WXLO_TYPE_LINEBREAK)
- insert(i,new wxLayoutObjectLineBreak);
- else
- return; // do nothing
- }
- }
- else
- insert(i,new wxLayoutObjectLineBreak);
- m_MaxLine++;
- m_CursorPos.y++;
- m_CursorPos.x -= offset;
- m_CursorOffset -= offset;
-}
-/******************** printing stuff ********************/
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-wxLayoutPrintout::wxLayoutPrintout(wxLayoutList &llist,
+wxLayoutPrintout::wxLayoutPrintout(wxLayoutList *llist,
wxString const & title)
:wxPrintout(title)
{
- m_llist = &llist;
+ m_llist = llist;
m_title = title;
}
+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)
{
- DrawHeader(*dc,wxPoint(m_Margins.left,m_Margins.top/2),wxPoint(m_Margins.right,m_Margins.top),page);
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_Margins.left,-top+m_Margins.top);
- m_llist->Draw(*dc,top,bottom,wxLayoutObjectList::iterator(NULL),translate);
+ wxPoint translate(m_Offset.x,m_Offset.y-top);
+ m_llist->Draw(*dc, translate, top, bottom);
return true;
}
else
return false;
}
-bool wxLayoutPrintout::OnBeginDocument(int startPage, int endPage)
-{
- if (!wxPrintout::OnBeginDocument(startPage, endPage))
- return false;
-
- return true;
-}
-
-void
-wxLayoutPrintout::OnPreparePrinting(void)
-{
-
-}
-
-
void wxLayoutPrintout::GetPageInfo(int *minPage, int *maxPage, int *selPageFrom, int *selPageTo)
{
- // ugly hack to get number of pages
+ /* 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
- psdc.GetSize(&m_PageWidth, &m_PageHeight); // that's all we need it for
- // We do 5% margins on top and bottom, and a 5% high header line.
- m_Margins.top = m_PageHeight / 10 ; // 10%, half of it header
- m_Margins.bottom = m_PageHeight - m_PageHeight / 20; // 95%
- // On the sides we reserve 10% each for the margins.
- m_Margins.left = m_PageWidth / 10;
- m_Margins.right = m_PageWidth - m_PageWidth / 10;
+ float scale = ScaleDC(&psdc);
- // This is the length of the printable area.
- m_PrintoutHeight = m_PageHeight - (int) (m_PageHeight * 0.15);
+ 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);
- //FIXME this is wrong but not used at the moment
- m_PageWidth = m_Margins.right - m_Margins.left;
+ // 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));
- m_NumOfPages = (int)( m_llist->GetSize().y / (float)(m_PrintoutHeight) + 0.5);
*minPage = 1;
*maxPage = m_NumOfPages;
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
- wxBrush *brush = dc.GetBrush();
- wxPen *pen = dc.GetPen();
- wxFont *font = dc.GetFont(),
- *myfont;;
+ 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));
topleft.y,bottomright.x-topleft.x,
bottomright.y-topleft.y);
dc.SetBrush(*wxBLACK_BRUSH);
- myfont = new wxFont((WXLO_DEFAULTFONTSIZE*12)/10,wxSWISS,wxNORMAL,wxBOLD,false,"Helvetica");
- dc.SetFont(*myfont);
+ wxFont myfont = wxFont((WXLO_DEFAULTFONTSIZE*12)/10,
+ wxSWISS,wxNORMAL,wxBOLD,false,"Helvetica");
+ dc.SetFont(myfont);
wxString page;
page = "9999/9999 "; // many pages...
dc.DrawText(m_title, topleft.x+w,topleft.y+h/2);
// restore settings
- dc.SetPen(*pen);
- dc.SetBrush(*brush);
- dc.SetFont(*font);
-
- delete myfont;
+ dc.SetPen(pen);
+ dc.SetBrush(brush);
+ dc.SetFont(font);
}
+#endif
-wxLayoutPrintout *
-wxLayoutList::MakePrintout(wxString const &name)
-{
- return new wxLayoutPrintout(*this,name);
-}