/*-*- c++ -*-********************************************************
* wxllist: wxLayoutList, a layout engine for text and graphics *
* *
- * (C) 1998-1999 by Karsten Ballüder (Ballueder@usa.net) *
+ * (C) 1998-2000 by Karsten Ballüder (Ballueder@gmx.net) *
* *
* $Id$
*******************************************************************/
#include "Mpch.h"
#ifdef M_BASEDIR
+# include "Mcommon.h"
# include "gui/wxllist.h"
# include "gui/wxlparser.h"
# define SHOW_SELECTIONS 1
#include <ctype.h>
+
/// This should never really get created
#define WXLLIST_TEMPFILE "__wxllist.tmp"
{
"invalid", "text", "cmd", "icon"
};
- void
- wxLayoutObject::Debug(void)
+ wxString
+ wxLayoutObject::DebugDump(void) const
{
- WXLO_DEBUG(("%s",g_aTypeStrings[GetType()]));
+ wxString str;
+ str.Printf("%s",g_aTypeStrings[GetType()]);
+ return str;
}
#else
-# define TypeString(t) ""
-# define WXLO_DEBUG(x)
+# 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
//@}
+static
void ReadString(wxString &to, wxString &from)
{
to = "";
{
wxString tmp;
ReadString(tmp, istr);
- int type = -1;
+ int type = WXLO_TYPE_INVALID;
sscanf(tmp.c_str(),"%d", &type);
switch(type)
heightOld = m_Height;
#endif // 0
+#ifdef __WXDEBUG__
+ CoordType a,b,c,d,e,f;
+ dc.GetTextExtent("test ", &a, &b, &c);
+ dc.GetTextExtent("test", &d, &e, &f);
+ wxASSERT(a != d);
+ wxASSERT(b == e);
+ wxASSERT(c == f);
+ dc.GetTextExtent(" ", &d, &e, &f);
+ wxASSERT(a > 0);
+#endif
dc.GetTextExtent(m_Text, &m_Width, &m_Height, &descent);
#if 0
#ifdef WXLAYOUT_DEBUG
-void
-wxLayoutObjectText::Debug(void)
+wxString
+wxLayoutObjectText::DebugDump(void) const
{
- wxLayoutObject::Debug();
- WXLO_DEBUG((" `%s`", m_Text.c_str()));
+ wxString str;
+ str = wxLayoutObject::DebugDump();
+ wxString str2;
+ str2.Printf(" `%s`", m_Text.c_str());
+ return str+str2;
}
#endif
wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap const &icon)
{
+ if ( !icon.Ok() )
+ {
+ wxFAIL_MSG("invalid icon");
+
+ m_Icon = NULL;
+
+ return;
+ }
+
+#ifdef __WXMSW__
+ // FIXME ugly, ugly, ugly - but the only way to avoid slicing
+ m_Icon = icon.GetHBITMAP() ? new wxBitmap(icon)
+ : new wxBitmap(wxBitmap((const wxBitmap &)icon));
+#else // !MSW
m_Icon = new wxBitmap(icon);
+#endif // MSW/!MSW
}
wxString file = wxGetTempFileName("wxloexport");
- ostr << WXLO_TYPE_ICON << '\n'
+ ostr << (int) WXLO_TYPE_ICON << '\n'
<< file << '\n';
m_Icon->SaveFile(file, WXLO_BITMAP_FORMAT);
}
wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap *icon)
{
m_Icon = icon;
+ if(! m_Icon)
+ m_Icon = new wxBitmap;
}
void
m_StyleInfo = new wxLayoutStyleInfo(family, size,style,weight,underline,fg,bg);
}
+wxLayoutObjectCmd::wxLayoutObjectCmd(const wxLayoutStyleInfo &si)
+
+{
+ m_StyleInfo = new wxLayoutStyleInfo;
+ *m_StyleInfo = si;
+}
+
wxLayoutObject *
wxLayoutObjectCmd::Copy(void)
{
wxLayoutObjectCmd *obj = new wxLayoutObjectCmd(
- m_StyleInfo->size,
m_StyleInfo->family,
+ m_StyleInfo->size,
m_StyleInfo->style,
m_StyleInfo->weight,
m_StyleInfo->underline,
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';
+ ostr << (int) WXLO_TYPE_CMD << '\n'
+ << (int) m_StyleInfo->family << '\n'
+ << (int) m_StyleInfo->size << '\n'
+ << (int) m_StyleInfo->style << '\n'
+ << (int) m_StyleInfo->weight << '\n'
+ << (int) m_StyleInfo->underline << '\n'
+ << (int) m_StyleInfo->m_fg_valid << '\n'
+ << (int) m_StyleInfo->m_bg_valid << '\n';
if(m_StyleInfo->m_fg_valid)
{
- ostr << m_StyleInfo->m_fg.Red() << '\n'
- << m_StyleInfo->m_fg.Green() << '\n'
- << m_StyleInfo->m_fg.Blue() << '\n';
+ ostr << (int) m_StyleInfo->m_fg.Red() << '\n'
+ << (int) m_StyleInfo->m_fg.Green() << '\n'
+ << (int) m_StyleInfo->m_fg.Blue() << '\n';
}
if(m_StyleInfo->m_bg_valid)
{
- ostr << m_StyleInfo->m_bg.Red() << '\n'
- << m_StyleInfo->m_bg.Green() << '\n'
- << m_StyleInfo->m_bg.Blue() << '\n';
+ ostr << (int) m_StyleInfo->m_bg.Red() << '\n'
+ << (int) m_StyleInfo->m_bg.Green() << '\n'
+ << (int) m_StyleInfo->m_bg.Blue() << '\n';
}
}
/* static */
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->size);
+ ReadString(tmp, istr);
sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->style);
ReadString(tmp, istr);
sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->weight);
wxLayoutLine::wxLayoutLine(wxLayoutLine *prev, wxLayoutList *llist)
{
- m_LineNumber = 0;
m_Width = m_Height = 0;
m_Length = 0;
+
m_updateLeft = -1;
- MarkDirty(0);
m_Previous = prev;
m_Next = NULL;
+ MarkDirty(0);
+
+ m_LineNumber = 0;
RecalculatePosition(llist);
+
+ MarkDirty();
if(m_Previous)
{
- m_LineNumber = m_Previous->GetLineNumber()+1;
+ 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);
+ m_Next->ReNumber();
}
m_StyleInfo = llist->GetDefaultStyleInfo();
+
+ llist->IncNumLines();
}
wxLayoutLine::~wxLayoutLine()
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
}
wxLayoutObjectList::iterator
-wxLayoutLine::FindObjectScreen(wxDC &dc,
+wxLayoutLine::FindObjectScreen(wxDC &dc, wxLayoutList *llist,
CoordType xpos, CoordType *cxpos,
bool *found) const
{
wxASSERT(cxpos);
- wxASSERT(cxpos);
+
+ llist->ApplyStyle(GetStyleInfo(), dc);
+
wxLayoutObjectList::iterator i;
CoordType x = 0, cx = 0, width;
for(i = m_ObjectList.begin(); i != NULLIT; i++)
{
- width = (**i).GetWidth();
+ wxLayoutObject *obj = *i;
+ if ( obj->GetType() == WXLO_TYPE_CMD )
+ {
+ // this will set the correct font for the objects which follow
+ obj->Layout(dc, llist);
+ }
+
+ width = obj->GetWidth();
if( x <= xpos && xpos <= x + width )
{
- *cxpos = cx + (**i).GetOffsetScreen(dc, xpos-x);
- if(found) *found = true;
+ *cxpos = cx + obj->GetOffsetScreen(dc, xpos-x);
+
+ if ( found )
+ *found = true;
return i;
}
- x += (**i).GetWidth();
- cx += (**i).GetLength();
+
+ x += obj->GetWidth();
+ cx += obj->GetLength();
}
+
// behind last object:
*cxpos = cx;
- if(found) *found = false;
+
+ if (found)
+ *found = false;
return m_ObjectList.tail();
}
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)
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
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)
{
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);
- }
+ // maintain linked list integrity
+ if(m_Next)
+ m_Next->m_Previous = m_Previous;
+ if(m_Previous)
+ m_Previous->m_Next = m_Next;
+
+ // get the line numbers right again
+ if ( update && m_Next)
+ m_Next->ReNumber();
+
+ MarkDirty();
+
+ // we can't use m_Next after "delete this", so we must save this pointer
+ // first
wxLayoutLine *next = m_Next;
delete this;
+
+ llist->DecNumLines();
+
return next;
}
bool cursorFound = false;
+ RecalculatePosition(llist);
+
if(cursorPos)
{
*cursorPos = m_Position;
// 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);
+ m_Next->MarkDirty();
}
-
+
// We need to check whether we found a valid cursor size:
if(cursorPos && cursorSize)
{
// this might be the case if the cursor is at the end of the
// line or on a command object:
- if(cursorSize->y < WXLO_MINIMUM_CURSOR_WIDTH)
+ if(cursorSize->x < WXLO_MINIMUM_CURSOR_WIDTH)
{
CoordType width, height, descent;
dc.GetTextExtent(WXLO_CURSORCHAR, &width, &height, &descent);
if(m_BaseLine >= cursorSize->y) // the normal case anyway
cursorPos->y += m_BaseLine-cursorSize->y;
}
- RecalculatePositions(1, llist);
MarkClean();
}
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)
wxLayoutLine *newLine = new wxLayoutLine(this, llist);
// split object at i:
- if((**i).GetType() == WXLO_TYPE_TEXT && offset != 0)
+ if((**i).GetType() == WXLO_TYPE_TEXT
+ && offset != 0
+ && offset != (**i).GetLength() )
{
wxString left, right;
wxLayoutObjectText *tobj = (wxLayoutObjectText *) *i;
m_ObjectList.remove(i); // remove without deleting it
}
if(m_Next)
- m_Next->RecalculatePositions(2, llist);
+ m_Next->MarkDirty();
return newLine;
}
+bool
+wxLayoutLine::Wrap(CoordType wrapmargin, wxLayoutList *llist)
+{
+ if(GetLength() < wrapmargin)
+ return FALSE; // nothing to do
+
+ // find the object which covers the wrapmargin:
+ CoordType offset;
+ wxLOiterator i = FindObject(wrapmargin, &offset);
+ wxCHECK_MSG( i != NULLIT, FALSE, "Cannot find object covering wrapmargin.");
+
+ // from this object on, the rest of the line must be copied to the
+ // next one:
+ wxLOiterator copyObject = NULLIT;
+ // if we split a text-object, we must pre-pend some text to the
+ // next line later on, remember it here:
+ wxString prependText = "";
+ // we might need to adjust the cursor position later, so remember it
+ size_t xpos = llist->GetCursorPos().x;
+ // by how much did we shorten the current line:
+ size_t shorter = 0;
+ // remember cursor location of object
+ size_t objectCursorPos = 0;
+
+ size_t breakpos = offset;
+
+ if( (**i).GetType() != WXLO_TYPE_TEXT )
+ {
+ // break before a non-text object
+ copyObject = i;
+ }
+ else
+ {
+ bool foundSpace = FALSE;
+ do
+ {
+// while(i != NULLIT && (**i).GetType() != WXLO_TYPE_TEXT)
+// i--;
+ // try to find a suitable place to split the object:
+ wxLayoutObjectText *tobj = (wxLayoutObjectText *)*i;
+ if((**i).GetType() == WXLO_TYPE_TEXT
+ && tobj->GetText().Length() >= breakpos)
+ {
+ do
+ {
+ foundSpace = isspace(tobj->GetText()[breakpos]) != 0;
+ if ( foundSpace )
+ break;
+ }
+ while ( breakpos-- > 0 );
+ }
+ else
+ {
+ breakpos = 0;
+ }
+
+ if(! foundSpace) // breakpos == 0!
+ {
+ if(i == m_ObjectList.begin())
+ return FALSE; // could not break line
+ else
+ {
+ i--;
+ while(i != m_ObjectList.begin()
+ && (**i).GetType() != WXLO_TYPE_TEXT )
+ {
+ i--;
+ }
+ breakpos = (**i).GetLength();
+ }
+ }
+ }while(! foundSpace);
+ // before we actually break the object, we need to know at which
+ // cursorposition it starts, so we can restore the cursor if needed:
+ if( this == llist->GetCursorLine() && xpos >= breakpos )
+ {
+ for(wxLOiterator j = m_ObjectList.begin();
+ j != NULLIT && j != i; j++)
+ objectCursorPos += (**j).GetLength();
+ }
+ // now we know where to break it:
+ wxLayoutObjectText *tobj = (wxLayoutObjectText *)*i;
+ shorter = tobj->GetLength() - breakpos;
+ // remember text to copy from this object
+ prependText = tobj->GetText().Mid(breakpos+1);
+ tobj->SetText(tobj->GetText().Left(breakpos));
+ // copy every following object:
+ copyObject = i; copyObject ++;
+ }
+
+ // make sure there is an empty m_Next line:
+ (void) new wxLayoutLine(this, llist);
+ wxASSERT(m_Next);
+ // We need to move this and all following objects to the next
+ // line. Starting from the end of line, to keep the order right.
+ if(copyObject != NULLIT)
+ {
+ wxLOiterator j;
+ for(j = m_ObjectList.tail(); j != copyObject; j--)
+ m_Next->Prepend(*j);
+ m_Next->Prepend(*copyObject);
+ // and now remove them from this list:
+ while( copyObject != m_ObjectList.end() )
+ {
+ shorter += (**copyObject).GetLength();
+ m_ObjectList.remove(copyObject); // remove without deleting it
+ }
+ }
+ m_Length -= shorter;
+
+ if(prependText.Length() > 0)
+ m_Next->Insert(0, prependText);
+
+ // do we need to adjust the cursor position?
+ if( this == llist->GetCursorLine() && xpos >= breakpos)
+ {
+ xpos = objectCursorPos + (xpos - objectCursorPos - breakpos -
+ ((xpos > breakpos) ? 1 : 0 ));
+ wxASSERT(xpos >= 0);
+ llist->MoveCursorTo( wxPoint( xpos, m_Next->GetLineNumber()) );
+ }
+ return TRUE; // we wrapped the line
+}
+
+void
+wxLayoutLine::ReNumber(void)
+{
+ CoordType lineNo = m_Previous ? m_Previous->m_LineNumber+1 : 0;
+ m_LineNumber = lineNo++;
+
+ for(wxLayoutLine *next = GetNextLine();
+ next; next = next->GetNextLine())
+ next->m_LineNumber = lineNo++;
+}
void
wxLayoutLine::MergeNextLine(wxLayoutList *llist)
SetNext(nextLine);
if ( nextLine )
{
- nextLine->MoveLines(-1);
+ nextLine->ReNumber();
}
else
{
#endif // 0
}
+ llist->DecNumLines();
+
delete oldnext;
}
{
do
{
- if( isspace(((wxLayoutObjectText*)*i)->GetText().c_str()[(size_t)offset]))
+ if(isspace(((wxLayoutObjectText*)*i)->GetText().c_str()[(size_t)offset]))
return column;
else
{
i++;
}
if(i == NULLIT) return -1; //why should this happen?
+
+ // now we are behind the one long text object and need to find the
+ // first space in it
+ for(offset = 0; offset < (**i).GetLength(); offset++)
+ if( isspace(((wxLayoutObjectText*)*i)->GetText().c_str()[(size_t)offset]))
+ {
+ return pos+offset;
+ }
pos += (**i).GetLength();
- 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
+ return pos;
}
#ifdef WXLAYOUT_DEBUG
void
-wxLayoutLine::Debug(void)
+wxLayoutLine::Debug(void) const
{
- wxString tmp;
wxPoint pos = GetPosition();
WXLO_DEBUG(("Line %ld, Pos (%ld,%ld), Height %ld, BL %ld, Font: %d",
(long int) GetLineNumber(),
(long int) m_BaseLine,
(int) m_StyleInfo.family));
if(m_ObjectList.begin() != NULLIT)
- (**m_ObjectList.begin()).Debug();
+ {
+ WXLO_DEBUG(((**m_ObjectList.begin()).DebugDump().c_str()));
+ }
}
#endif
m_caret = NULL;
#endif // WXLAYOUT_USE_CARET
+ m_numLines = 0;
m_FirstLine = NULL;
+ SetAutoFormatting(TRUE);
+ ForceTotalLayout(TRUE); // for the first time, do all
InvalidateUpdateRect();
Clear();
}
wxLayoutList::~wxLayoutList()
{
+ SetAutoFormatting(FALSE);
InternalClear();
+ Empty();
m_FirstLine->DeleteLine(false, this);
+
+ wxASSERT_MSG( m_numLines == 0, "line count calculation broken" );
}
void
void
wxLayoutList::InternalClear(void)
{
- Empty();
m_Selection.m_selecting = false;
m_Selection.m_valid = false;
m_CursorStyleInfo = m_DefaultStyleInfo;
}
+void
+wxLayoutList::Read(wxString &istr)
+{
+ /* In order to handle input of formatted string "nicely", we need
+ to restore our current font settings after the string. So first
+ of all, we create a StyleInfo structure with our current
+ settings. */
+ wxLayoutStyleInfo current_si = GetStyleInfo();
+
+ while(istr.Length())
+ {
+ // check for a linebreak:
+ wxString tmp;
+ tmp = istr.BeforeFirst('\n');
+ int type = WXLO_TYPE_INVALID;
+ sscanf(tmp.c_str(),"%d", &type);
+ if(type == WXLO_TYPE_LINEBREAK)
+ {
+ LineBreak();
+ istr = istr.AfterFirst('\n');
+ }
+ else
+ {
+ wxLayoutObject *obj = wxLayoutObject::Read(istr);
+ if(obj)
+ Insert(obj);
+ }
+ }
+ /* Now we use the current_si to restore our last font settings: */
+ Insert(new wxLayoutObjectCmd(current_si));
+}
+
+
void
wxLayoutList::SetFont(int family, int size, int style, int weight,
int underline, wxColour *fg,
m_DefaultStyleInfo = wxLayoutStyleInfo(family, size, style, weight,
underline, fg, bg);
m_CurrentStyleInfo = m_DefaultStyleInfo;
+
+ // Empty() should be called after we set m_DefaultStyleInfo because
+ // otherwise the style info for the first line (created in Empty()) would be
+ // incorrect
+ Empty();
}
wxPoint
{
n--;
m_CursorPos.y ++;
+ last = m_CursorLine;
m_CursorLine = m_CursorLine->GetNextLine();
}
if(! m_CursorLine)
{
m_CursorLine = last;
- m_CursorPos.y ++;
+ m_CursorPos.y --;
rc = false;
}
else
CoordType moveDistance = 0;
CoordType offset;
- for ( wxLOiterator i = m_CursorLine->FindObject(m_CursorPos.x, &offset);
+ wxLayoutLine *lineCur = m_CursorLine;
+ for ( wxLOiterator i = lineCur->FindObject(m_CursorPos.x, &offset);
n != 0;
n > 0 ? i++ : i-- )
{
if ( i == NULLIT )
- return false;
+ {
+ if ( n > 0 )
+ {
+ // moving forward, pass to the first object of the next line
+ moveDistance++;
+ lineCur = lineCur->GetNextLine();
+ if ( lineCur )
+ i = lineCur->GetFirstObject();
+ }
+ else
+ {
+ // moving backwards, pass to the last object of the prev line
+ moveDistance--;
+ lineCur = lineCur->GetPreviousLine();
+ if ( lineCur )
+ i = lineCur->GetLastObject();
+ }
+
+ if ( i == NULLIT )
+ {
+ // moved to the end/beginning of text
+ return false;
+ }
+
+ offset = -1;
+ }
wxLayoutObject *obj = *i;
+
+ if ( offset == -1 )
+ {
+ // calculate offset: we are either at the very beginning or the very
+ // end of the object, so it isn't very difficult (the only time when
+ // offset is != -1 is for the very first iteration when its value is
+ // returned by FindObject)
+ if ( n > 0 )
+ offset = 0;
+ else
+ offset = obj->GetLength();
+ }
+
if( obj->GetType() != WXLO_TYPE_TEXT )
{
// any visible non text objects count as one word
if ( n > 0 )
{
// can't move further in this text object
- n--;
-
canAdvance = false;
+
+ // still should move over the object border
+ moveDistance++;
+ n--;
}
else if ( offset > 0 )
{
}
else // backwards
{
- if ( isspace(*p) )
+ // in these 2 cases we took 1 char too much
+ if ( (p < start) || isspace(*p) )
+ {
p++;
+ }
}
- n > 0 ? n-- : n++;
+ CoordType moveDelta = p - start - offset;
+ if ( (n < 0) && (offset == tobj->GetLength() - 1) )
+ {
+ // because we substracted 1 from offset in this case above, now
+ // compensate for it
+ moveDelta--;
+ }
+
+ if ( moveDelta != 0 )
+ {
+ moveDistance += moveDelta;
- moveDistance = p - start - offset;
+ n > 0 ? n-- : n++;
+ }
}
}
- // except for the first iteration, offset is 0
- offset = 0;
+ // except for the first iteration, offset is calculated in the beginning
+ // of the loop
+ offset = -1;
}
MoveCursorHorizontally(moveDistance);
AddCursorPosToUpdateRect();
+ wxASSERT(m_CursorLine->GetLength() >= m_CursorPos.x);
+
if ( !m_CursorLine->Insert(m_CursorPos.x, text) )
return false;
-
m_CursorPos.x += text.Length();
m_movedCursor = true;
- m_CursorLine->RecalculatePositions(0, this);
+ if(m_AutoFormat)
+ m_CursorLine->MarkDirty();
return true;
}
m_CursorPos.x += obj->GetLength();
m_movedCursor = true;
- m_CursorLine->RecalculatePositions(0, this);
+ if(m_AutoFormat)
+ m_CursorLine->MarkDirty();
return true;
}
wxLayoutList::LineBreak(void)
{
wxASSERT(m_CursorLine);
- bool setFirst = (m_CursorLine == m_FirstLine && m_CursorPos.x == 0);
AddCursorPosToUpdateRect();
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++;
+ if(m_CursorLine->GetPreviousLine() == NULL)
+ m_FirstLine = m_CursorLine;
+ m_CursorPos.y++;
m_CursorPos.x = 0;
+ // The following code will produce a height which is guaranteed to
+ // be too high: old lineheight + the height of both new lines.
+ // We can probably drop the old line height and start with height =
+ // 0. FIXME
wxLayoutLine *prev = m_CursorLine->GetPreviousLine();
- wxCHECK_MSG(prev, false, "just broke the line, where is the previous one?");
-
- height += prev->GetHeight();
+ if(prev)
+ height += prev->GetHeight();
+ height += m_CursorLine->GetHeight();
m_movedCursor = 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 m_CursorLine->Wrap(column, this);
+}
- return true;
+bool
+wxLayoutList::WrapAll(CoordType column)
+{
+ wxLayoutLine *line = m_FirstLine;
+ if(! line)
+ return FALSE;
+ bool rc = TRUE;
+ while(line && rc)
+ {
+ rc &= line->Wrap(column, this);
+ line = line->GetNextLine();
}
+ return rc;
}
bool
{ // we cannot delete this line, but we can clear it
MoveCursorToBeginOfLine();
DeleteToEndOfLine();
- m_CursorLine->RecalculatePositions(2, this);
+ if(m_AutoFormat)
+ m_CursorLine->MarkDirty();
return n-1;
}
//else:
wxASSERT(m_FirstLine);
wxASSERT(m_CursorLine);
}
- m_CursorLine->RecalculatePositions(2, this);
+ if(m_AutoFormat)
+ m_CursorLine->MarkDirty();
return n;
}
void
wxLayoutList::Recalculate(wxDC &dc, CoordType bottom)
{
+ if(! m_AutoFormat)
+ return;
wxLayoutLine *line = m_FirstLine;
// first, make sure everything is calculated - this might not be
}
wxPoint
-wxLayoutList::GetCursorScreenPos(wxDC &dc)
+wxLayoutList::GetCursorScreenPos(void) const
{
return m_CursorScreenPos;
}
// 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;
+
+ if(m_ReLayoutAll)
+ {
+ forceAll = TRUE;
+ bottom = -1;
+ }
+ ForceTotalLayout(FALSE);
+
+
+ // If one line was dirty, we need to re-calculate all
+ // following lines, too.
+ bool wasDirty = forceAll;
+ // we need to layout until we reach at least the cursor line,
+ // otherwise we won't be able to scroll to it
+ bool cursorReached = false;
wxLayoutLine *line = m_FirstLine;
while(line)
{
if(! wasDirty)
ApplyStyle(line->GetStyleInfo(), dc);
- if(forceAll || line->IsDirty()
- || (cpos && line->GetLineNumber() == cpos->y))
+ if(
+ // if any previous line was dirty, we need to layout all
+ // following lines:
+ wasDirty
+ // go on until we find the cursorline
+ || ! cursorReached
+ // layout dirty lines:
+ || line->IsDirty()
+ // always layout the cursor line toupdate the cursor
+ // position and size:
+ || line == m_CursorLine
+ // or if it's the line we are asked to look for:
+ || (cpos && line->GetLineNumber() == cpos->y)
+ // layout at least the desired region:
+ || (bottom == -1 )
+ || (line->GetPosition().y <= bottom)
+ )
{
+ if(line->IsDirty())
+ wasDirty = true;
+
// The following Layout() calls will update our
// m_CurrentStyleInfo if needed.
if(line == m_CursorLine)
+ {
line->Layout(dc, this,
(wxPoint *)&m_CursorScreenPos,
(wxPoint *)&m_CursorSize,
&m_CursorStyleInfo,
m_CursorPos.x);
- if(cpos && line->GetLineNumber() == cpos->y)
- line->Layout(dc, this,
- cpos,
- csize, NULL, cpos->x);
+ // we cannot layout the line twice, so copy the coords:
+ if(cpos && line ->GetLineNumber() == cpos->y)
+ {
+ *cpos = m_CursorScreenPos;
+ if ( csize )
+ *csize = m_CursorSize;
+ }
+ cursorReached = TRUE;
+ }
else
- line->Layout(dc, this);
- // little condition to speed up redrawing:
- if(bottom != -1 && line->GetPosition().y > bottom)
- break;
- wasDirty = true;
+ {
+ if(cpos && line->GetLineNumber() == cpos->y)
+ {
+ line->Layout(dc, this,
+ cpos,
+ csize, NULL, cpos->x);
+ cursorReached = TRUE;
+ }
+ else
+ line->Layout(dc, this);
+ }
}
- line->RecalculatePositions(1, this);
line = line->GetNextLine();
}
+#ifndef WXLAYOUT_USE_CARET
// can only be 0 if we are on the first line and have no next line
wxASSERT(m_CursorSize.x != 0 || (m_CursorLine &&
m_CursorLine->GetNextLine() == NULL &&
m_CursorLine == m_FirstLine));
+#endif // WXLAYOUT_USE_CARET
AddCursorPosToUpdateRect();
}
wxLayoutList::Draw(wxDC &dc,
wxPoint const &offset,
CoordType top,
- CoordType bottom)
+ CoordType bottom,
+ bool clipStrictly)
{
wxLayoutLine *line = m_FirstLine;
m_Selection.m_discarded = false;
}
- /* 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
+ /* This call to Layout() will re-calculate and update all lines
+ marked as dirty.
+ */
+ Layout(dc, bottom);
+
ApplyStyle(m_DefaultStyleInfo, dc);
wxBrush brush(m_CurrentStyleInfo.m_bg, wxSOLID);
dc.SetBrush(brush);
dc.SetBackgroundMode(wxTRANSPARENT);
- bool style_set = false;
while(line)
{
// only draw if between top and bottom:
if((top == -1 ||
- line->GetPosition().y + line->GetHeight() >= top))
+ line->GetPosition().y + line->GetHeight() > top))
{
-// if(! style_set)
- {
- ApplyStyle(line->GetStyleInfo(), dc);
- style_set = true;
- }
+ ApplyStyle(line->GetStyleInfo(), dc);
+ // little condition to speed up redrawing:
+ if( bottom != -1
+ && line->GetPosition().y
+ +(clipStrictly ? line->GetHeight() : 0) >= bottom)
+ break;
line->Draw(dc, this, offset);
}
-#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",
+ WXLO_DEBUG(("Selection is %s : %ld,%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));
bool *found)
{
// First, find the right line:
- wxLayoutLine *line = m_FirstLine;
+ wxLayoutLine
+ *line = m_FirstLine,
+ *lastline = m_FirstLine;
wxPoint p;
ApplyStyle(m_DefaultStyleInfo, dc);
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
+ lastline = line;
line = line->GetNextLine();
}
- if(line == NULL)
+
+ bool didFind = line != NULL;
+
+ if ( !line )
{
- if(found) *found = false;
- return NULL; // not found
+ // use the last line:
+ line = lastline;
}
- if(cursorPos) cursorPos->y = line->GetLineNumber();
+
+ if ( cursorPos )
+ cursorPos->y = line->GetLineNumber();
+
+ bool foundinline = true;
+ long cx = 0;
+
// Now, find the object in the line:
- wxLOiterator i = line->FindObjectScreen(dc, pos.x,
- cursorPos ? & cursorPos->x : NULL ,
- found);
+ wxLOiterator i;
+
+ if (cursorPos)
+ {
+ i = line->FindObjectScreen(dc, this,
+ pos.x,
+ &cx,
+ &foundinline);
+ cursorPos->x = cx;
+ }
+ else
+ i = line->FindObjectScreen(dc, this,
+ pos.x,
+ NULL,
+ &foundinline);
+ if ( found )
+ *found = didFind && foundinline;
+
return (i == NULLIT) ? NULL : *i;
}
#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)coords.x, (long)coords.y,
(long)m_CursorSize.x, (long)m_CursorSize.y,
(long)m_CursorLine->GetLineNumber(),
(long)m_CursorLine->GetLength()));
#ifdef WXLAYOUT_USE_CARET
m_caret->Move(coords);
#else // !WXLAYOUT_USE_CARET
+
+ wxASSERT(m_CursorSize.x >= WXLO_MINIMUM_CURSOR_WIDTH);
dc.SetBrush(*wxWHITE_BRUSH);
//FIXME: wxGTK XOR is borken at the moment!!!dc.SetLogicalFunction(wxXOR);
- dc.SetLogicalFunction(wxXOR);
dc.SetPen(wxPen(*wxBLACK,1,wxSOLID));
if(active)
{
+ dc.SetLogicalFunction(wxXOR);
dc.DrawRectangle(coords.x, coords.y,
m_CursorSize.x, m_CursorSize.y);
SetUpdateRect(coords.x, coords.y);
- SetUpdateRect(coords.x+m_CursorSize.x, coords.y+m_CursorSize.y);
+ SetUpdateRect(coords.x+m_CursorSize.x,
+ coords.y+m_CursorSize.y);
}
else
{
+ dc.SetLogicalFunction(wxCOPY);
dc.DrawLine(coords.x, coords.y+m_CursorSize.y-1,
coords.x, coords.y);
SetUpdateRect(coords.x, coords.y+m_CursorSize.y-1);
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;
- }
+ m_Selection.m_ScreenB = spos;
+ m_Selection.m_CursorB = cpos;
+}
+void
+wxLayoutList::EndSelection(const wxPoint& cposOrig, const wxPoint& spos)
+{
+ wxPoint cpos(cposOrig);
+ if(cpos.x == -1) cpos = m_CursorPos;
+ ContinueSelection(cpos, spos);
+ WXLO_DEBUG(("Ending selection at %ld/%ld", cpos.x, cpos.y));
// we always want m_CursorA <= m_CursorB!
if( m_Selection.m_CursorA > m_Selection.m_CursorB )
{
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;
+ /// In case we just clicked somewhere, the selection will have zero
+ /// size, so we discard it immediately.
+ if(m_Selection.m_CursorA == m_Selection.m_CursorB)
+ DiscardSelection();
}
void
if ( !HasSelection() )
return false;
- return m_Selection.m_CursorA <= cursor && cursor <= m_Selection.m_CursorB;
+ return (
+ (m_Selection.m_CursorA <= cursor
+ && cursor <= m_Selection.m_CursorB)
+ || (m_Selection.m_CursorB <= cursor
+ && cursor <= m_Selection.m_CursorA)
+ );
}
return 0;
CoordType y = line->GetLineNumber();
- if(m_Selection.m_CursorA.y < y && m_Selection.m_CursorB.y > y)
+ if(
+ (m_Selection.m_CursorA.y < y && m_Selection.m_CursorB.y > y)
+ || (m_Selection.m_CursorB.y < y && m_Selection.m_CursorA.y > y)
+ )
return 1;
else if(m_Selection.m_CursorA.y == y)
{
if(m_Selection.m_CursorB.y == y)
*to = m_Selection.m_CursorB.x;
else
- *to = line->GetLength();
+ {
+ if(m_Selection.m_CursorB > m_Selection.m_CursorA)
+ *to = line->GetLength();
+ else
+ *to = 0;
+ }
+ if(*to < *from)
+ {
+ CoordType help = *to;
+ *to = *from;
+ *from = help;
+ }
return -1;
}
else if(m_Selection.m_CursorB.y == y)
if(m_Selection.m_CursorA.y == y)
*from = m_Selection.m_CursorA.x;
else
- *from = 0;
+ {
+ if(m_Selection.m_CursorB > m_Selection.m_CursorA)
+ *from = 0;
+ else
+ *from = line->GetLength();
+ }
+ if(*to < *from)
+ {
+ CoordType help = *to;
+ *to = *from;
+ *from = help;
+ }
return -1;
}
else
return;
}
+ // We now know that the two lines are different:
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)
+ * firstLine = GetLine(m_Selection.m_CursorA.y),
+ * lastLine = GetLine(m_Selection.m_CursorB.y);
+ // be a bit paranoid:
+ if(! firstLine || ! lastLine)
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();
+ wxLayoutLine *prevLine = firstLine->GetPreviousLine(),
+ *nextLine = firstLine->GetNextLine();
while(nextLine && nextLine != lastLine)
nextLine = nextLine->DeleteLine(false, this);
// Now nextLine = lastLine;
Delete(1); // This joins firstLine and nextLine
- Delete(m_Selection.m_CursorB.x); // This deletes the first x
- // positions
+ Delete(m_Selection.m_CursorB.x); // This deletes the first x positions
- /// Recalculate:
- firstLine->RecalculatePositions(1, this);
+ // Recalculate the line positions and numbers but notice that firstLine
+ // might not exist any more - it could be deleted by Delete(1) above
+ wxLayoutLine *firstLine2 = prevLine ? prevLine->GetNextLine() : m_FirstLine;
+ firstLine2->MarkDirty();
}
/// Starts highlighting the selection
}
+wxLayoutLine *
+wxLayoutList::GetLine(CoordType index) const
+{
+ wxASSERT_MSG( (0 <= index) && (index < (CoordType)m_numLines),
+ "invalid index" );
+
+ wxLayoutLine *line;
+ CoordType n = index;
+#ifdef DEBUG
+ CoordType lineNo = 0;
+#endif
+
+ for ( line = m_FirstLine; line && n-- > 0; line =
+ line->GetNextLine() )
+ {
+#ifdef DEBUG
+wxASSERT(line->GetLineNumber() == lineNo );
+ lineNo++;
+#endif
+}
+
+ if ( line )
+ {
+ // should be the right one
+ wxASSERT( line->GetLineNumber() == index );
+ }
+
+ return line;
+}
+
+
wxLayoutList *
wxLayoutList::Copy(const wxPoint &from,
const wxPoint &to)
{
wxString string;
- wxLayoutExportObject *export;
+ wxLayoutExportObject *exp;
wxLayoutExportStatus status(llist);
- while((export = wxLayoutExport( &status, WXLO_EXPORT_AS_OBJECTS)) != NULL)
+ while((exp = wxLayoutExport( &status, WXLO_EXPORT_AS_OBJECTS)) != NULL)
{
- if(export->type == WXLO_EXPORT_EMPTYLINE)
- ; //FIXME missing support for linebreaks in string format
+ if(exp->type == WXLO_EXPORT_EMPTYLINE)
+ string << (int) WXLO_TYPE_LINEBREAK << '\n';
else
- export->content.object->Write(string);
- delete export;
+ exp->content.object->Write(string);
+ delete exp;
}
-
- wxlo->SetData(string.c_str(), string.Length()+1);
+ wxlo->SetLayoutData(string);
}
return llist;
}
if(si.m_fg_valid)
{
m_CurrentStyleInfo.m_fg = si.m_fg;
+ m_CurrentStyleInfo.m_fg_valid = true;
dc.SetTextForeground(m_CurrentStyleInfo.m_fg);
}
if(si.m_bg_valid)
{
m_CurrentStyleInfo.m_bg = si.m_bg;
+ m_CurrentStyleInfo.m_bg_valid = true;
dc.SetTextBackground(m_CurrentStyleInfo.m_bg);
}
}
// remove any highlighting which could interfere with printing:
m_llist->StartSelection();
m_llist->EndSelection();
+ // force a full layout of the list:
+ m_llist->ForceTotalLayout();
+ // layout is called in ScaleDC() when we have a DC
}
wxLayoutPrintout::~wxLayoutPrintout()
{
int top, bottom;
top = (page - 1)*m_PrintoutHeight;
- bottom = top + m_PrintoutHeight;
+ bottom = top + m_PrintoutHeight;
+
+ WXLO_DEBUG(("OnPrintPage(%d) printing from %d to %d", page, top,
+ bottom));
// SetDeviceOrigin() doesn't work here, so we need to manually
// translate all coordinates.
wxPoint translate(m_Offset.x,m_Offset.y-top);
- m_llist->Draw(*dc, translate, top, bottom);
+ m_llist->Draw(*dc, translate, top, bottom, TRUE /* clip strictly
+ */);
return true;
}
else
/* 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);
+#if defined(__WXMSW__)
+ wxPrinterDC *psdc = new wxPrinterDC("","",WXLLIST_TEMPFILE,false);
#else
- wxPostScriptDC psdc(WXLLIST_TEMPFILE,false);
+ wxPostScriptDC *psdc = new wxPostScriptDC(WXLLIST_TEMPFILE,false);
#endif
- float scale = ScaleDC(&psdc);
+ psdc->StartDoc(m_title);
+ // before we draw anything, me must make sure the list is properly
+ // laid out
+ m_llist->Layout(*psdc);
+
+ float scale = ScaleDC(psdc);
- psdc.GetSize(&m_PageWidth, &m_PageHeight);
- // This sets a left/top origin of 15% and 20%:
- m_Offset = wxPoint((15*m_PageWidth)/100, m_PageHeight/20);
+ psdc->GetSize(&m_PageWidth, &m_PageHeight);
+
+ // This sets a left/top origin of 15% and 5%:
+ m_Offset = wxPoint((15*m_PageWidth)/100, (5*m_PageHeight)/100);
// This is the length of the printable area.
- m_PrintoutHeight = m_PageHeight - (int) (m_PageHeight * 0.15);
+ m_PrintoutHeight = m_PageHeight - 2*m_Offset.y;
m_PrintoutHeight = (int)( m_PrintoutHeight / scale); // we want to use the real paper height
-
m_NumOfPages = 1 +
(int)( m_llist->GetSize().y / (float)(m_PrintoutHeight));
*selPageFrom = 1;
*selPageTo = m_NumOfPages;
+ psdc->EndDoc();
+ delete psdc;
wxRemoveFile(WXLLIST_TEMPFILE);
}