X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/9480f5d151d53094f6f25df21196f9fc7f10f338..41ca191f4b76957735a885b620d54c5f51524522:/user/wxLayout/wxllist.cpp diff --git a/user/wxLayout/wxllist.cpp b/user/wxLayout/wxllist.cpp index 92e91f8e94..6e972e3ebc 100644 --- a/user/wxLayout/wxllist.cpp +++ b/user/wxLayout/wxllist.cpp @@ -1,127 +1,256 @@ /*-*- c++ -*-******************************************************** - * wxFTCanvas: a canvas for editing formatted text * + * 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$ * + * $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 - - during redraw each line gets iterated over twice, first just - calculating baselines and positions, second to actually draw it - - 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 -*/ - + + */ + #ifdef __GNUG__ #pragma implementation "wxllist.h" #endif -#include "wxllist.h" -#include "iostream" +//#include "Mpch.h" + + +#include "wx/wxprec.h" +#ifdef __BORLANDC__ +# pragma hdrstop +#endif + +#ifdef M_PREFIX +# include "gui/wxllist.h" +#else +# include "wxllist.h" +#endif -#include -#include -#include +#ifndef USE_PCH +# include "iostream.h" +# include +# include +# include +# include +#endif -#define BASELINESTRETCH 12 +#include -#define VAR(x) cerr << #x"=" << x << endl; -#define DBG_POINT(p) cerr << #p << ": " << p.x << ',' << p.y << endl -#define TRACE(f) cerr << #f":" << endl; +/// This should never really get created +#define WXLLIST_TEMPFILE "__wxllist.tmp" #ifdef WXLAYOUT_DEBUG -static const char *_t[] = { "invalid", "text", "cmd", "icon", - "linebreak"}; -void -wxLayoutObjectBase::Debug(void) +# 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) { - CoordType bl = 0; - cerr << _t[GetType()] << ": size=" << GetSize(&bl).x << "," - << GetSize(&bl).y << " bl=" << bl; + return p1.x == p2.x && p1.y == p2.y; } -#endif -//-------------------------- wxLayoutObjectText +/// 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; + + if(r.y > p.y) + r.y = p.y; + else if(r.y + r.height < p.y) + r.height = p.y - r.y; +} + +/// returns true if the point is in the rectangle +static +bool Contains(const wxRect &r, const wxPoint &p) +{ + return r.x <= p.x && r.y <= p.y && (r.x+r.width) >= p.x && (r.y + r.height) >= p.y; +} +//@} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + + wxLayoutObjectText -wxLayoutObjectText::wxLayoutObjectText(const String &txt) + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +wxLayoutObjectText::wxLayoutObjectText(const wxString &txt) { m_Text = txt; m_Width = 0; m_Height = 0; + m_Top = 0; + m_Bottom = 0; } +wxLayoutObject * +wxLayoutObjectText::Copy(void) +{ + wxLayoutObjectText *obj = new wxLayoutObjectText(m_Text); + obj->m_Width = m_Width; + obj->m_Height = m_Height; + obj->m_Top = m_Top; + obj->m_Bottom = m_Bottom; + obj->SetUserData(m_UserData); + return obj; +} 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 position, CoordType baseLine, - bool draw) +wxLayoutObjectText::Draw(wxDC &dc, wxPoint const &coords) +{ + 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) { long descent = 0l; - dc.GetTextExtent(Str(m_Text),&m_Width, &m_Height, &descent); - //FIXME: wxGTK does not set descent to a descent value yet. - if(descent == 0) - descent = (2*m_Height)/10; // crude fix - m_BaseLine = m_Height - descent; - position.y += baseLine-m_BaseLine; - if(draw) - dc.DrawText(Str(m_Text),position.x,position.y); -# ifdef WXLAYOUT_DEBUG -// dc.DrawRectangle(position.x, position.y, m_Width, m_Height); -# endif + + 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(); - cerr << " `" << m_Text << '\''; + wxLayoutObject::Debug(); + WXLO_DEBUG((" `%s`", m_Text.c_str())); } #endif -//-------------------------- wxLayoutObjectIcon +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + + wxLayoutObjectIcon + + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap const &icon) +{ + m_Icon = new wxBitmap(icon); +} + +wxLayoutObject * +wxLayoutObjectIcon::Copy(void) +{ + 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 position, CoordType baseLine, - bool draw) +wxLayoutObjectIcon::Draw(wxDC &dc, wxPoint const &coords) +{ + dc.DrawBitmap(*m_Icon, coords.x, coords.y-m_Icon->GetHeight(), + (m_Icon->GetMask() == NULL) ? FALSE : TRUE); +} + +void +wxLayoutObjectIcon::Layout(wxDC & /* dc */) { - position.y += baseLine - m_Icon->GetHeight(); - if(draw) - dc.DrawIcon(m_Icon,position.x,position.y); } wxPoint -wxLayoutObjectIcon::GetSize(CoordType *baseLine) const +wxLayoutObjectIcon::GetSize(CoordType *top, CoordType *bottom) const { - wxASSERT(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); @@ -129,731 +258,1375 @@ wxLayoutObjectCmd::wxLayoutObjectCmd(int size, int family, int style, int 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 position, CoordType lineHeight, - bool draw) +wxLayoutObjectCmd::Draw(wxDC &dc, wxPoint const & /* coords */) { wxASSERT(m_font); - // this get called even when draw==false, so that recalculation - // uses right font sizes - 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); } -//-------------------------- wxwxLayoutList - -wxLayoutList::wxLayoutList() +void +wxLayoutObjectCmd::Layout(wxDC &dc) { - m_DefaultSetting = NULL; - Clear(); + // this get called, so that recalculation uses right font sizes + Draw(dc, wxPoint(0,0)); } -wxLayoutList::~wxLayoutList() -{ - if(m_DefaultSetting) - delete m_DefaultSetting; -} +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -void -wxLayoutList::LineBreak(void) + The wxLayoutLine object + + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +wxLayoutLine::wxLayoutLine(wxLayoutLine *prev, wxLayoutList *llist) { - Insert(new wxLayoutObjectLineBreak); - m_CursorPosition.x = 0; m_CursorPosition.y++; + 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::SetFont(int family, int size, int style, int weight, - int underline, wxColour const *fg, - wxColour const *bg) +wxLayoutLine::~wxLayoutLine() { - 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; + // kbList cleans itself +} - 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)); +wxPoint +wxLayoutLine::RecalculatePosition(wxLayoutList *llist) +{ + 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(max_x); wxASSERT(max_y); wxASSERT(lineHeight); - *max_x = m_MaxX; - *max_y = m_MaxY; - *lineHeight = m_LineHeight; + wxASSERT(xpos >= 0); + wxASSERT(offset); + wxLayoutObjectList::iterator + i, + found = NULLIT; + CoordType x = 0, len; + + /* We search through the objects. As we don't like returning the + object that the cursor is behind, we just remember such an + object in "found" so we can return it if there is really no + further object following it. */ + for(i = m_ObjectList.begin(); i != NULLIT; i++) + { + len = (**i).GetLength(); + if( x <= xpos && xpos <= x + len ) + { + *offset = xpos-x; + if(xpos == x + len) // is there another object behind? + found = i; + else // we are really inside this object + return i; + } + x += (**i).GetLength(); + } + return found; // ==NULL if really none found } -wxLayoutObjectBase * -wxLayoutList::Draw(wxDC &dc, bool findObject, wxPoint const &findCoords) +wxLayoutObjectList::iterator +wxLayoutLine::FindObjectScreen(wxDC &dc, CoordType xpos, CoordType *cxpos) const { + wxASSERT(cxpos); + wxASSERT(cxpos); wxLayoutObjectList::iterator i; - - // in case we need to look for an object - wxLayoutObjectBase *foundObject = NULL; - - // first object in current line - wxLayoutObjectList::iterator headOfLine; - - // do we need to recalculate current line? - bool recalculate = false; - - // do we calculate or draw? Either true or false. - bool draw = false; - // drawing parameters: - wxPoint position = wxPoint(0,0); - wxPoint position_HeadOfLine; - CoordType baseLine = m_FontPtSize; - CoordType baseLineSkip = (BASELINESTRETCH * baseLine)/10; - - // we trace the objects' cursor positions so we can draw the cursor - wxPoint cursor = wxPoint(0,0); - // the cursor position inside the object - CoordType cursorOffset = 0; - // object under cursor - wxLayoutObjectList::iterator cursorObject = FindCurrentObject(&cursorOffset); + CoordType x = 0, cx = 0, width; - // queried from each object: - wxPoint size = wxPoint(0,0); - CoordType objBaseLine = baseLine; - wxLayoutObjectType type; - - // used temporarily - wxLayoutObjectText *tobj = NULL; - + 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(); +} - // this is needed for printing to a printer: - // only interesting for printer/PS output - int pageWidth, pageHeight; //GetSize() still needs int at the moment - struct +bool +wxLayoutLine::Insert(CoordType xpos, wxLayoutObject *obj) +{ + wxASSERT(xpos >= 0); + wxASSERT(obj != NULL); + CoordType offset; + wxLOiterator i = FindObject(xpos, &offset); + if(i == NULLIT) { - int top, bottom, left, right; - } margins; + if(xpos == 0 ) // aha, empty line! + { + m_ObjectList.push_back(obj); + m_Length += obj->GetLength(); + return true; + } + else + return false; + } - if( -#ifdef __WXMSW__ - dc.IsKindOf(CLASSINFO(wxPrinterDC)) || -#endif - dc.IsKindOf(CLASSINFO(wxPostScriptDC))) - { - VAR(wxThePrintSetupData); - - dc.GetSize(&pageWidth, &pageHeight); - dc.StartDoc(_("Printing...")); - dc.StartPage(); - margins.top = (1*pageHeight)/10; // 10% - margins.bottom = (9*pageHeight)/10; // 90% - margins.left = (1*pageWidth)/10; - margins.right = (9*pageWidth)/10; + 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; } - else + if(offset == len ) { - margins.top = 0; margins.left = 0; - margins.right = -1; - margins.bottom = -1; + 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; } - position.y = margins.right; - position.x = margins.left; - - VAR(findObject); VAR(findCoords.x); VAR(findCoords.y); - // if the cursorobject is a cmd, we need to find the first - // printable object: - while(cursorObject != end() - && (*cursorObject)->GetType() == WXLO_TYPE_CMD) - cursorObject++; - - headOfLine = begin(); - position_HeadOfLine = position; - - // setting up the default: - dc.SetTextForeground( *wxBLACK ); - dc.SetFont( *wxNORMAL_FONT ); - - if(m_DefaultSetting) - m_DefaultSetting->Draw(dc,wxPoint(0,0),0,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; +} - // we calculate everything for drawing a line, then rewind to the - // begin of line and actually draw it - i = begin(); - for(;;) +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) { - recalculate = false; + wxLayoutObjectText *tobj = (wxLayoutObjectText *) *i; + tobj->GetText().insert(offset, text); + m_Length += text.Length(); - if(i == end()) - break; - type = (*i)->GetType(); + return true; + } + else + return Insert(xpos, new wxLayoutObjectText(text)); +} - // to initialise sizes of objects, we need to call Draw - (*i)->Draw(dc, position, baseLine, draw); +CoordType +wxLayoutLine::Delete(CoordType xpos, CoordType npos) +{ + CoordType offset, len; - // update coordinates for next object: - size = (*i)->GetSize(&objBaseLine); - if(findObject && draw) // we need to look for an object + 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) { - if(findCoords.y >= position.y - && findCoords.y <= position.y+size.y - && findCoords.x >= position.x - && findCoords.x <= position.x+size.x) - { - foundObject = *i; - findObject = false; // speeds things up - } + 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); } - // draw the cursor - if(m_Editable && draw && i == cursorObject) + else { - if(type == WXLO_TYPE_TEXT) // special treatment + // tidy up: remove empty text objects + if((**i).GetLength() == 0) { - long descent = 0l; long width, height; - tobj = (wxLayoutObjectText *)*i; - String str = tobj->GetText(); - VAR(m_CursorPosition.x); VAR(cursor.x); - str = str.substr(0, cursorOffset); - VAR(str); - dc.GetTextExtent(Str(str), &width,&height, &descent); - VAR(height); - VAR(width); VAR(descent); - dc.DrawLine(position.x+width, - position.y+(baseLineSkip-height), - position.x+width, position.y+baseLineSkip); + m_ObjectList.erase(i); + continue; } - else + // Text object: + CoordType max = (**i).GetLength() - offset; + if(npos < max) max = npos; + if(max == 0) { - if(type == WXLO_TYPE_LINEBREAK) - dc.DrawLine(0, position.y+baseLineSkip, 0, position.y+2*baseLineSkip); - else - { - if(size.x == 0) - { - if(size.y == 0) - dc.DrawLine(position.x, position.y, position.x, position.y+baseLineSkip); - else - dc.DrawLine(position.x, position.y, position.x, position.y+size.y); - } - else - dc.DrawRectangle(position.x, position.y, size.x, size.y); + if(xpos == GetLength()) + return npos; + else + { // at the end of an object + // move to begin of next object: + i++; offset = 0; + continue; // start over } } + npos -= max; + m_Length -= max; + if(offset == 0 && max == (**i).GetLength()) + m_ObjectList.erase(i); // remove the whole object + else + ((wxLayoutObjectText *)(*i))->GetText().Remove(offset,max); } + } + return npos; +} - // 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; - recalculate = true; - } - if(objBaseLine > baseLine) - { - baseLine = objBaseLine; - recalculate = true; - } +bool +wxLayoutLine::DeleteWord(CoordType xpos) +{ + wxASSERT(xpos >= 0); + CoordType offset; - // now check whether we have finished handling this line: - if(type == WXLO_TYPE_LINEBREAK || i == tail()) - { - if(recalculate) // do this line again - { - position.x = position_HeadOfLine.x; - i = headOfLine; - continue; - } + wxLOiterator i = FindObject(xpos, &offset); - if(! draw) // finished calculating sizes + for(;;) + { + if(i == NULLIT) return false; + if((**i).GetType() != WXLO_TYPE_TEXT) + { + // This should only happen when at end of line, behind a non-text + // object: + if(offset == (**i).GetLength()) return false; + m_Length -= (**i).GetLength(); // -1 + m_ObjectList.erase(i); + return true; // we are done + } + else + { // text object: + if(offset == (**i).GetLength()) // at end of object { - // if the this line needs to go onto a new page, we need - // to change pages before drawing it: - if(margins.bottom != -1 && position.y > margins.bottom) - { - dc.EndPage(); - position_HeadOfLine.y = margins.top; - dc.StartPage(); - } - // do this line again, this time drawing it - position = position_HeadOfLine; - draw = true; - i = headOfLine; + i++; offset = 0; continue; } - else // we have drawn a line, so continue calculating next one - draw = false; - } + 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; + } + } + wxASSERT(0); // we should never arrive here +} - if(position.x+size.x > m_MaxX) - m_MaxX = position.x+size.x; - // is it a linebreak? - if(type == WXLO_TYPE_LINEBREAK || i == tail()) - { - position.x = margins.left; - position.y += baseLineSkip; - baseLine = m_FontPtSize; - objBaseLine = baseLine; // not all objects set it - baseLineSkip = (BASELINESTRETCH * baseLine)/10; - headOfLine = i; - headOfLine++; - position_HeadOfLine = position; - } - i++; +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); } - dc.EndDoc(); - m_MaxY = position.y; - return foundObject; + wxLayoutLine *next = m_Next; + delete this; + return next; } -#ifdef WXLAYOUT_DEBUG void -wxLayoutList::Debug(void) +wxLayoutLine::Draw(wxDC &dc, + wxLayoutList *llist, + const wxPoint & offset) const { - CoordType offs; wxLayoutObjectList::iterator i; - - cerr << - "------------------------debug start-------------------------" << endl; - for(i = begin(); i != end(); i++) - { - (*i)->Debug(); - cerr << endl; - } - cerr << - "-----------------------debug end----------------------------" - << endl; - // show current object: - cerr << "Cursor: " - << m_CursorPosition.x << ',' - << m_CursorPosition.y; + wxPoint pos = offset; + pos = pos + GetPosition(); - i = FindCurrentObject(&offs); - cerr << " line length: " << GetLineLength(i) << " "; - if(i == end()) - { - cerr << "<>" << endl; - return; // FIXME we should set cursor position to maximum allowed - // value then - } - if((*i)->GetType() == WXLO_TYPE_TEXT) + pos.y += m_BaseLine; + + for(i = m_ObjectList.begin(); i != NULLIT; i++) { - cerr << " \"" << ((wxLayoutObjectText *)(*i))->GetText() << "\", offs: " - << offs << endl; + (**i).Draw(dc, pos); + pos.x += (**i).GetWidth(); } - else - cerr << ' ' << _t[(*i)->GetType()] << endl; - } -#endif - -/******************** editing stuff ********************/ -wxLayoutObjectList::iterator -wxLayoutList::FindObjectCursor(wxPoint const &cpos, CoordType *offset) +void +wxLayoutLine::Layout(wxDC &dc, + wxLayoutList *llist, + wxPoint *cursorPos, + wxPoint *cursorSize, + int cx) { - wxPoint cursor = wxPoint(0,0); // runs along the objects - CoordType width; wxLayoutObjectList::iterator i; -#ifdef WXLAYOUT_DEBUG - cerr << "Looking for object at " << cpos.x << ',' << cpos.y << - endl; -#endif - for(i = begin(); i != end() && cursor.y <= cpos.y; i++) + 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; + + if(cursorPos) { - width = 0; - if((*i)->GetType() == WXLO_TYPE_LINEBREAK) - { - if(cpos.y == cursor.y) + *cursorPos = m_Position; + if(cursorSize) *cursorSize = wxPoint(0,0); + } + + for(i = m_ObjectList.begin(); i != NULLIT; i++) + { + (**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) { - --i; - if(offset) - *offset = (*i)->CountPositions(); - return i; + 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.x = 0; cursor.y ++; + } // 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; + } + if(topHeight + bottomHeight > m_Height) m_Height = + topHeight+bottomHeight; + m_BaseLine = topHeight; + + if(m_Height == 0) + { + if(GetPreviousLine()) // empty line + { + m_Height = GetPreviousLine()->GetHeight(); + m_BaseLine = GetPreviousLine()->m_BaseLine; } else - cursor.x += (width = (*i)->CountPositions()); - if(cursor.y == cpos.y && (cursor.x > cpos.x || - ((*i)->GetType() != WXLO_TYPE_CMD && cursor.x == cpos.x)) - ) // found it ? { - if(offset) - *offset = cpos.x-(cursor.x-width); // 0==cursor before - // the object -#ifdef WXLAYOUT_DEBUG - cerr << " found object at " << cursor.x-width << ',' << - cursor.y << ", type:" << _t[(*i)->GetType()] <RecalculatePositions(0, llist); + + // We need to check whether we found a valid cursor size: + if(cursorPos) + { + // this might be the case if the cursor is at the end of the + // line or on a command object: + if(cursorSize->y < WXLO_MINIMUM_CURSOR_WIDTH) + { + CoordType width, height, descent; + dc.GetTextExtent(WXLO_CURSORCHAR, &width, &height, &descent); + cursorSize->x = width; + cursorSize->y = height; + } + if(m_BaseLine >= cursorSize->y) // the normal case anyway + cursorPos->y += m_BaseLine-cursorSize->y; + } } -wxLayoutObjectList::iterator -wxLayoutList::FindCurrentObject(CoordType *offset) + +wxLayoutLine * +wxLayoutLine::Break(CoordType xpos, wxLayoutList *llist) { - wxLayoutObjectList::iterator obj = 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; + } + + 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 - obj = FindObjectCursor(m_CursorPosition, offset); - if(obj == end()) // not ideal yet + while(i != m_ObjectList.end()) { - obj = tail(); - if(obj != end()) // tail really exists - *offset = (*obj)->CountPositions(); // at the end of it + newLine->Append(*i); + m_Length -= (**i).GetLength(); + m_ObjectList.remove(i); // remove without deleting it } - return obj; + if(m_Next) + m_Next->RecalculatePositions(2, llist); + return newLine; } + void -wxLayoutList::MoveCursor(int dx, int dy) +wxLayoutLine::MergeNextLine(wxLayoutList *llist) { - CoordType offs, lineLength; - wxLayoutObjectList::iterator i; - - - if(dy > 0 && m_CursorPosition.y < m_MaxLine) - m_CursorPosition.y += dy; - else if(dy < 0 && m_CursorPosition.y > 0) - m_CursorPosition.y += dy; // dy is negative - if(m_CursorPosition.y < 0) - m_CursorPosition.y = 0; - else if (m_CursorPosition.y > m_MaxLine) - m_CursorPosition.y = m_MaxLine; + wxASSERT(GetNextLine()); + wxLayoutObjectList &list = GetNextLine()->m_ObjectList; + wxLOiterator i; - while(dx > 0) + for(i = list.begin(); i != list.end();) { - i = FindCurrentObject(&offs); - lineLength = GetLineLength(i); - if(m_CursorPosition.x < lineLength) + Append(*i); + list.remove(i); // remove without deleting it + } + wxASSERT(list.empty()); + wxLayoutLine *oldnext = GetNextLine(); + SetNext(GetNextLine()->GetNextLine()); + delete oldnext; + RecalculatePositions(1, llist); +} + +CoordType +wxLayoutLine::GetWrapPosition(CoordType column) +{ + CoordType offset; + wxLOiterator i = FindObject(column, &offset); + if(i == NULLIT) return -1; // cannot wrap + + // go backwards through the list and look for space in text objects + do + { + if((**i).GetType() == WXLO_TYPE_TEXT) { - m_CursorPosition.x ++; - dx--; - continue; + do + { + if( isspace(((wxLayoutObjectText*)*i)->GetText().c_str()[(size_t)offset])) + return column; + else + { + offset--; + column--; + } + }while(offset != -1); + i--; // move on to previous object } else { - if(m_CursorPosition.y < m_MaxLine) - { - m_CursorPosition.y++; - m_CursorPosition.x = 0; - dx--; - } - else - break; // cannot move there + column -= (**i).GetLength(); + i--; + } + if( i != NULLIT) + offset = (**i).GetLength(); + }while(i != NULLIT); + /* If we reached the begin of the list and have more than one + object, that one is longer than the margin, so break behind + it. */ + CoordType pos = 0; + i = m_ObjectList.begin(); + while(i != NULLIT && (**i).GetType() != WXLO_TYPE_TEXT) + { + pos += (**i).GetLength(); + i++; + } + if(i == NULLIT) return -1; //why should this happen? + pos += (**i).GetLength(); + i++; + while(i != NULLIT && (**i).GetType() != WXLO_TYPE_TEXT) + { + pos += (**i).GetLength(); + i++; + } + if(i == NULLIT) return -1; //this is possible, if there is only one text object + // now we are at the second text object: + pos -= (**i).GetLength(); + return pos; // in front of it +} + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + + The wxLayoutList object + + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +wxLayoutList::wxLayoutList() +{ + m_DefaultSetting = NULL; + m_FirstLine = NULL; + m_ColourFG = *wxBLACK; + m_ColourBG = *wxWHITE; + InvalidateUpdateRect(); + Clear(); +} + +wxLayoutList::~wxLayoutList() +{ + InternalClear(); + m_FirstLine->DeleteLine(false, this); +} + +void +wxLayoutList::Empty(void) +{ + while(m_FirstLine) + m_FirstLine = m_FirstLine->DeleteLine(false, this); + + m_CursorPos = wxPoint(0,0); + m_CursorScreenPos = wxPoint(0,0); + m_CursorSize = wxPoint(0,0); + m_FirstLine = new wxLayoutLine(NULL, this); // empty first line + m_CursorLine = m_FirstLine; + InvalidateUpdateRect(); +} + + +void +wxLayoutList::InternalClear(void) +{ + Empty(); + if(m_DefaultSetting) + { + delete m_DefaultSetting; + m_DefaultSetting = NULL; + } +} + +void +wxLayoutList::SetFont(int family, int size, int style, int weight, + int underline, wxColour *fg, + wxColour *bg) +{ + 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) + +{ + wxColour + *cfg = NULL, + *cbg = NULL; + + if( fg ) + cfg = wxTheColourDatabase->FindColour(fg); + if( bg ) + cbg = wxTheColourDatabase->FindColour(bg); + + SetFont(family,size,style,weight,underline,cfg,cbg); +} + +void +wxLayoutList::Clear(int family, int size, int style, int weight, + int /* underline */, wxColour *fg, wxColour *bg) +{ + InternalClear(); + + // 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_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); +} + + + +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.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; +} + +bool +wxLayoutList::MoveCursorVertically(int n) +{ + SetUpdateRect(m_CursorScreenPos); + SetUpdateRect(m_CursorScreenPos+m_CursorSize); + bool rc; + if(n < 0) // move up + { + if(m_CursorLine == m_FirstLine) return false; + while(n < 0 && m_CursorLine) + { + m_CursorLine = m_CursorLine->GetPreviousLine(); + m_CursorPos.y--; + n++; + } + if(! m_CursorLine) + { + m_CursorLine = m_FirstLine; + m_CursorPos.y = 0; + rc = false; + } + else + { + if(m_CursorPos.x > m_CursorLine->GetLength()) + m_CursorPos.x = m_CursorLine->GetLength(); + rc = true; } } - while(dx < 0) + else // move down { - if(m_CursorPosition.x > 0) + wxLayoutLine *last = m_CursorLine; + if(! m_CursorLine->GetNextLine()) return false; + while(n > 0 && m_CursorLine) { - m_CursorPosition.x --; - dx++; + n--; + m_CursorPos.y ++; + m_CursorLine = m_CursorLine->GetNextLine(); + } + if(! m_CursorLine) + { + m_CursorLine = last; + m_CursorPos.y ++; + rc = false; } else { - if(m_CursorPosition.y > 0) - { - m_CursorPosition.y --; - m_CursorPosition.x = 0; - i = FindCurrentObject(&offs); - lineLength = GetLineLength(i); - m_CursorPosition.x = lineLength; - dx++; - continue; - } - else - break; // cannot move left any more + if(m_CursorPos.x > m_CursorLine->GetLength()) + m_CursorPos.x = m_CursorLine->GetLength(); + rc = true; } } - // final adjustment: - i = FindCurrentObject(&offs); - lineLength = GetLineLength(i); - if(m_CursorPosition.x > lineLength) - m_CursorPosition.x = lineLength; - -#ifdef WXLAYOUT_DEBUG - i = FindCurrentObject(&offs); - cerr << "Cursor: " - << m_CursorPosition.x << ',' - << m_CursorPosition.y; + return rc; +} - if(i == end()) +bool +wxLayoutList::MoveCursorHorizontally(int n) +{ + SetUpdateRect(m_CursorScreenPos); + SetUpdateRect(m_CursorScreenPos+m_CursorSize); + int move; + while(n < 0) { - cerr << "<>" << endl; - return; // FIXME we should set cursor position to maximum allowed - // value then + 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; } - if((*i)->GetType() == WXLO_TYPE_TEXT) + + while(n > 0) { - cerr << " \"" << ((wxLayoutObjectText *)(*i))->GetText() << "\", offs: " - << offs << endl; + 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; } - else - cerr << ' ' << _t[(*i)->GetType()] << endl; -#endif + return n == 0; } -void -wxLayoutList::Delete(CoordType count) +bool +wxLayoutList::Insert(wxString const &text) { - TRACE(Delete); + 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; +} - if(!m_Editable) - return; +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; +} - VAR(count); +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; +} - CoordType offs, len; - wxLayoutObjectList::iterator i; - +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; + SetUpdateRect(m_CursorScreenPos); + SetUpdateRect(m_CursorScreenPos+m_CursorSize); + LineBreak(); + Delete(1); // delete the space + m_CursorPos.x = newpos; + return true; + } +} + +bool +wxLayoutList::Delete(CoordType npos) +{ + wxASSERT(m_CursorLine); + SetUpdateRect(m_CursorScreenPos); + SetUpdateRect(m_CursorScreenPos+m_CursorSize); + CoordType left; do { - i = FindCurrentObject(&offs); - if(i == end()) - return; -#ifdef WXLAYOUT_DEBUG - cerr << "trying to delete: " << _t[(*i)->GetType()] << endl; + 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 - if((*i)->GetType() == WXLO_TYPE_LINEBREAK) - m_MaxLine--; - if((*i)->GetType() == WXLO_TYPE_TEXT) + + left--; + } + else { - wxLayoutObjectText *tobj = (wxLayoutObjectText *)*i; - 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(offs == len) - { - i++; - if((*i)->GetType() == WXLO_TYPE_TEXT) - { - offs = 0; // delete from begin of next string - tobj = (wxLayoutObjectText *)*i; - len = tobj->CountPositions(); - } - else - { - erase(i); - return; - } - } - if(len <= count) // delete this object - { - count -= len; - erase(i); - } + // Need to join next line + if(! m_CursorLine->GetNextLine()) + break; // cannot else { - len = count; - VAR(offs); VAR(len); - tobj->GetText().erase(offs,len); - return; // we are done + m_CursorLine->MergeNextLine(this); + left--; } } - else // delete the object - { - len = (*i)->CountPositions(); - erase(i); // after this, i is the iterator for the following object - if(count > len) - count -= len; - else - count = 0; + } + while(left); + return left == 0; +} + +int +wxLayoutList::DeleteLines(int n) +{ + 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); } - while(count && i != end()); + m_CursorLine->RecalculatePositions(2, this); + return n; } void -wxLayoutList::Insert(wxLayoutObjectBase *obj) +wxLayoutList::Recalculate(wxDC &dc, CoordType bottom) { - wxASSERT(obj); - CoordType offs; - wxLayoutObjectList::iterator i = FindCurrentObject(&offs); + wxLayoutLine *line = m_FirstLine; + + // first, make sure everything is calculated - this might not be + // needed, optimise it later + m_DefaultSetting->Layout(dc); + while(line) + { + line->RecalculatePosition(this); // so we don't need to do it all the time + // little condition to speed up redrawing: + if(bottom != -1 && line->GetPosition().y > bottom) break; + line = line->GetNextLine(); + } +} - TRACE(Insert(obj)); +void +wxLayoutList::UpdateCursorScreenPos(wxDC &dc) +{ + wxASSERT(m_CursorLine); + m_CursorLine->Layout(dc, this, (wxPoint *)&m_CursorScreenPos, (wxPoint *)&m_CursorSize, m_CursorPos.x); +} - if(i == end()) - push_back(obj); - else - { - // do we have to split a text object? - if((*i)->GetType() == WXLO_TYPE_TEXT && offs != 0 && offs != (*i)->CountPositions()) - { - wxLayoutObjectText *tobj = (wxLayoutObjectText *) *i; -#ifdef WXLAYOUT_DEBUG - cerr << "text: '" << tobj->GetText() << "'" << endl; - VAR(offs); -#endif - String left = tobj->GetText().substr(0,offs); // get part before cursor - VAR(left); - tobj->GetText() = tobj->GetText().substr(offs,(*i)->CountPositions()-offs); // keeps the right half - VAR(tobj->GetText()); - insert(i,obj); - insert(i,new wxLayoutObjectText(left)); // inserts before - } +wxPoint +wxLayoutList::GetCursorScreenPos(wxDC &dc) +{ + UpdateCursorScreenPos(dc); + return m_CursorScreenPos; +} + +void +wxLayoutList::Layout(wxDC &dc, CoordType bottom) +{ + wxLayoutLine *line = m_FirstLine; + + // 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 - { - wxLayoutObjectList::iterator j = i; // we want to apend after this object - j++; - if(j != end()) - insert(j, obj); - else - push_back(obj); - } + line->Layout(dc, this); + // little condition to speed up redrawing: + if(bottom != -1 && line->GetPosition().y > bottom) break; + line = line->GetNextLine(); } - m_CursorPosition.x += obj->CountPositions(); - if(obj->GetType() == WXLO_TYPE_LINEBREAK) - m_MaxLine++; + +///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::Insert(String const &text) +wxLayoutList::Draw(wxDC &dc, wxPoint const &offset, + CoordType top, CoordType bottom) { - wxLayoutObjectText *tobj = NULL; - TRACE(Insert(text)); - - if(! m_Editable) - return; + wxLayoutLine *line = m_FirstLine; - CoordType offs; - wxLayoutObjectList::iterator i = FindCurrentObject(&offs); + Layout(dc, bottom); + m_DefaultSetting->Draw(dc, wxPoint(0,0)); + wxBrush brush(m_ColourBG, wxSOLID); + dc.SetBrush(brush); + + 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(); +} - if(i != end() && (*i)->GetType() == WXLO_TYPE_TEXT) - { // insert into an existing text object: - TRACE(inserting into existing object); - tobj = (wxLayoutObjectText *)*i ; - wxASSERT(tobj); - tobj->GetText().insert(offs,text); +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(); } - else // check whether the previous object is text: + 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; + +} + +wxPoint +wxLayoutList::GetSize(void) const +{ + wxLayoutLine + *line = m_FirstLine, + *last = line; + if(! line) + return wxPoint(0,0); + + wxPoint maxPoint(0,0); + + // find last line: + while(line) { - wxLayoutObjectList::iterator j = i; - j--; - TRACE(checking previous object); - if(0 && j != end() && (*j)->GetType() == WXLO_TYPE_TEXT) - { - tobj = (wxLayoutObjectText *)*i; - wxASSERT(tobj); - tobj->GetText()+=text; - } - else // insert a new text object - { - TRACE(creating new object); - Insert(new wxLayoutObjectText(text)); //FIXME not too optimal, slow - return; // position gets incremented in Insert(obj) - } + if(line->GetWidth() > maxPoint.x) + maxPoint.x = line->GetWidth(); + last = line; + line = line->GetNextLine(); } - m_CursorPosition.x += strlen(text.c_str()); + + maxPoint.y = last->GetPosition().y + last->GetHeight(); + return maxPoint; } -CoordType -wxLayoutList::GetLineLength(wxLayoutObjectList::iterator i) +void +wxLayoutList::DrawCursor(wxDC &dc, bool active, wxPoint const &translate) { - if(i == end()) - return 0; + wxPoint coords; + coords = m_CursorScreenPos; + coords.x += translate.x; + coords.y += translate.y; - CoordType len = 0; +#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); +} - // 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) +/** Called by the objects to update the update rectangle. + @param p a point to include in it +*/ +void +wxLayoutList::SetUpdateRect(const wxPoint &p) +{ + if(m_UpdateRectValid) + GrowRect(m_UpdateRect, p); + else { - len += (*i)->CountPositions(); - i++; + 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; } - return len; } void -wxLayoutList::Clear(int family, int size, int style, int weight, - int underline, char const *fg, char const *bg) +wxLayoutList::StartSelection(void) { - wxLayoutObjectList::iterator i = begin(); + wxLogDebug("Starting selection at %ld/%ld", m_CursorPos.x, m_CursorPos.y); + m_Selection.m_CursorA = m_CursorPos; +} - while(i != end()) // == while valid - erase(i); +void +wxLayoutList::EndSelection(void) +{ + wxLogDebug("Ending selection at %ld/%ld", m_CursorPos.x, m_CursorPos.y); + m_Selection.m_CursorB = m_CursorPos; +} - // 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); +bool +wxLayoutList::IsSelected(const wxPoint &cursor) +{ + return m_Selection.m_CursorA <= cursor + && cursor <= m_Selection.m_CursorB; +} - m_Position = wxPoint(0,0); - m_CursorPosition = wxPoint(0,0); - m_MaxLine = 0; - m_LineHeight = (BASELINESTRETCH*m_FontPtSize)/10; - m_MaxX = 0; m_MaxY = 0; - if(m_DefaultSetting) - delete m_DefaultSetting; - m_DefaultSetting = new - wxLayoutObjectCmd(m_FontPtSize,m_FontFamily,m_FontStyle, - m_FontWeight,m_FontUnderline, - m_ColourFG, m_ColourBG); +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + + wxLayoutPrintout + + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +wxLayoutPrintout::wxLayoutPrintout(wxLayoutList *llist, + wxString const & title) +:wxPrintout(title) +{ + 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) + { + int top, bottom; + top = (page - 1)*m_PrintoutHeight; + bottom = top + m_PrintoutHeight; + // SetDeviceOrigin() doesn't work here, so we need to manually + // translate all coordinates. + wxPoint translate(m_Offset.x,m_Offset.y-top); + m_llist->Draw(*dc, translate, top, bottom); + return true; + } + else + return false; +} + +void wxLayoutPrintout::GetPageInfo(int *minPage, int *maxPage, int *selPageFrom, int *selPageTo) +{ + /* We allocate a temporary wxDC for printing, so that we can + determine the correct paper size and scaling. We don't actually + print anything on it. */ +#ifdef __WXMSW__ + wxPrinterDC psdc("","",WXLLIST_TEMPFILE,false); +#else + wxPostScriptDC psdc(WXLLIST_TEMPFILE,false); +#endif + + float scale = ScaleDC(&psdc); + + psdc.GetSize(&m_PageWidth, &m_PageHeight); + // This sets a left/top origin of 15% and 20%: + m_Offset = wxPoint((15*m_PageWidth)/100, m_PageHeight/20); + + // This is the length of the printable area. + m_PrintoutHeight = m_PageHeight - (int) (m_PageHeight * 0.15); + m_PrintoutHeight = (int)( m_PrintoutHeight / scale); // we want to use the real paper height + + + m_NumOfPages = 1 + + (int)( m_llist->GetSize().y / (float)(m_PrintoutHeight)); + + *minPage = 1; + *maxPage = m_NumOfPages; + + *selPageFrom = 1; + *selPageTo = m_NumOfPages; + wxRemoveFile(WXLLIST_TEMPFILE); +} + +bool wxLayoutPrintout::HasPage(int pageNum) +{ + return pageNum <= m_NumOfPages; +} + +/* + Stupid wxWindows doesn't draw proper ellipses, so we comment this + out. It's a waste of paper anyway. +*/ +#if 0 +void +wxLayoutPrintout::DrawHeader(wxDC &dc, + wxPoint topleft, wxPoint bottomright, + int pageno) +{ + // make backups of all essential parameters + const wxBrush& brush = dc.GetBrush(); + const wxPen& pen = dc.GetPen(); + const wxFont& font = dc.GetFont(); + + dc.SetBrush(*wxWHITE_BRUSH); + dc.SetPen(wxPen(*wxBLACK,0,wxSOLID)); + dc.DrawRoundedRectangle(topleft.x, + topleft.y,bottomright.x-topleft.x, + bottomright.y-topleft.y); + dc.SetBrush(*wxBLACK_BRUSH); + wxFont myfont = wxFont((WXLO_DEFAULTFONTSIZE*12)/10, + wxSWISS,wxNORMAL,wxBOLD,false,"Helvetica"); + dc.SetFont(myfont); + + wxString page; + page = "9999/9999 "; // many pages... + long w,h; + dc.GetTextExtent(page,&w,&h); + page.Printf("%d/%d", pageno, (int) m_NumOfPages); + dc.DrawText(page,bottomright.x-w,topleft.y+h/2); + dc.GetTextExtent("XXXX", &w,&h); + dc.DrawText(m_title, topleft.x+w,topleft.y+h/2); + + // restore settings + dc.SetPen(pen); + dc.SetBrush(brush); + dc.SetFont(font); +} +#endif + +