X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/a5e13868ca700aa39bae26b8fb13c640f466d9f0..c0d1d789c127cf5d56e4c8b115f2891c61403c20:/user/wxLayout/wxllist.cpp?ds=sidebyside diff --git a/user/wxLayout/wxllist.cpp b/user/wxLayout/wxllist.cpp index d44ee4bcbd..caf366bcb2 100644 --- a/user/wxLayout/wxllist.cpp +++ b/user/wxLayout/wxllist.cpp @@ -1,1369 +1,2346 @@ /*-*- 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$ *******************************************************************/ /* - - each Object knows its size and how to draw itself - - the list is responsible for calculating positions - - the draw coordinates for each object are the top left corner - - coordinates only get calculated when things get redrawn - - The cursor position is the position before an object, i.e. if the - buffer starts with a text-object, cursor 0,0 is just before the - first character. For all non-text objects, the cursor positions - are 0==before or 1==behind. So that all non-text objects count as - one cursor position. -*/ - -/* - TODO: - - - new cursor movement calculations - - moving lines and moving across linebreaks still broken - - - blinking cursor - - mouse click positions cursor - - selection (SetMark(), GetSelection()) - - DND acceptance of text - - - wxlwindow: formatting menu: problem with checked/unchecked consistency gtk bug? -*/ - -#define USE_NEW_CURSORCODE 1 - -/* - Known wxGTK bugs: - - MaxX()/MaxY() don't get set -*/ - + + */ #ifdef __GNUG__ #pragma implementation "wxllist.h" #endif -//#include "Mpch.h" +#include "Mpch.h" + + +#include "wx/wxprec.h" +#ifdef __BORLANDC__ +# pragma hdrstop +#endif + #ifdef M_BASEDIR # include "gui/wxllist.h" +# include "gui/wxlparser.h" +# define SHOW_SELECTIONS 1 #else # include "wxllist.h" +# include "wxlparser.h" +# define SHOW_SELECTIONS 1 #endif #ifndef USE_PCH # include "iostream.h" # include -# include +# include # include # include +# include #endif -#define BASELINESTRETCH 12 +#include -// This should never really get created +/// This should never really get created #define WXLLIST_TEMPFILE "__wxllist.tmp" #ifdef WXLAYOUT_DEBUG -static const char *g_aTypeStrings[] = -{ - "invalid", "text", "cmd", "icon", "linebreak" -}; - -# define wxLayoutDebug wxLogDebug -# define WXL_VAR(x) cerr << #x " = " << x << endl; -# define WXL_DBG_POINT(p) wxLogDebug(#p ": (%d, %d)", p.x, p.y) -# define WXL_TRACE(f) wxLogDebug(#f ": ") -# define TypeString(t) g_aTypeStrings[t] -void -wxLayoutObjectBase::Debug(void) -{ - CoordType bl = 0; - wxLogDebug("%s: size = %dx%d, bl = %d", - TypeString(GetType()), GetSize(&bl).x, GetSize(&bl).y, bl); -} +# 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 WXL_VAR(x) -# define WXL_DBG_POINT(p) -# define WXL_TRACE(f) -# define ShowCurrentObject() -# define TypeString(t) "" -inline void wxLayoutDebug(const char *, ...) { } +# define TypewxString(t) "" +# define WXLO_DEBUG(x) #endif -//-------------------------- wxLayoutObjectText +/// Cursors smaller than this disappear in XOR drawing mode +#define WXLO_MINIMUM_CURSOR_WIDTH 4 -wxLayoutObjectText::wxLayoutObjectText(const String &txt) +/// 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) { - m_Text = txt; - m_Width = 0; - m_Height = 0; - m_Position = wxPoint(-1,-1); + return p1.x == p2.x && p1.y == p2.y; } - -wxPoint -wxLayoutObjectText::GetSize(CoordType *baseLine) const +/// allows me to compare to wxPoints +bool operator !=(wxPoint const &p1, wxPoint const &p2) { - if(baseLine) *baseLine = m_BaseLine; - return wxPoint(m_Width, m_Height); + return p1.x != p2.x || p1.y != p2.y; } -void -wxLayoutObjectText::Draw(wxDC &dc, wxPoint const &translate) +/// allows me to compare to wxPoints +bool operator <=(wxPoint const &p1, wxPoint const &p2) { - dc.DrawText(Str(m_Text), m_Position.x + translate.x, m_Position.y+translate.y); - m_IsDirty = false; + return p1.y < p2.y || (p1.y == p2.y && p1.x <= p2.x); } +/// grows a wxRect so that it includes the given point -void -wxLayoutObjectText::Layout(wxDC &dc, wxPoint position, CoordType baseLine) +static +void GrowRect(wxRect &r, CoordType x, CoordType y) { - long descent = 0l; - - if(m_Position.x != position.x || m_Position.y != position.y) - m_IsDirty = true; + if(r.x > x) + r.x = x; + else if(r.x + r.width < x) + r.width = x - r.x; - m_Position = position; - dc.GetTextExtent(Str(m_Text),&m_Width, &m_Height, &descent); - m_BaseLine = m_Height - descent; - if(m_Position.x != position.x || m_Position.y != position.y) - m_IsDirty = true; + if(r.y > y) + r.y = y; + else if(r.y + r.height < y) + r.height = y - r.y; } -#ifdef WXLAYOUT_DEBUG -void -wxLayoutObjectText::Debug(void) +#if 0 +// unused +/// returns true if the point is in the rectangle +static +bool Contains(const wxRect &r, const wxPoint &p) { - wxLayoutObjectBase::Debug(); - wxLogDebug(" `%s`", m_Text.c_str()); + return r.x <= p.x && r.y <= p.y && (r.x+r.width) >= p.x && (r.y + r.height) >= p.y; } #endif -//-------------------------- wxLayoutObjectIcon -wxLayoutObjectIcon::wxLayoutObjectIcon(wxIcon const &icon) -{ - m_Position = wxPoint(-1,-1); - m_Icon = new wxIcon(icon); -} +//@} -wxLayoutObjectIcon::wxLayoutObjectIcon(wxIcon *icon) -{ - m_Icon = icon; -} -void -wxLayoutObjectIcon::Draw(wxDC &dc, wxPoint const &translate) +void ReadString(wxString &to, wxString &from) { - dc.DrawIcon(m_Icon,m_Position.x+translate.x, m_Position.y+translate.y); + to = ""; + const char *cptr = from.c_str(); + while(*cptr && *cptr != '\n') + to += *cptr++; + if(*cptr) cptr++; + from = cptr; } -void -wxLayoutObjectIcon::Layout(wxDC &dc, wxPoint position, CoordType baseLine) -{ - if(m_Position.x != position.x || m_Position.y != position.y) - m_IsDirty = true; - m_Position = position; -} +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -wxPoint -wxLayoutObjectIcon::GetSize(CoordType *baseLine) const + wxLayoutObject + + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* static */ +wxLayoutObject * +wxLayoutObject::Read(wxString &istr) { - if(baseLine) *baseLine = m_Icon->GetHeight(); - return wxPoint(m_Icon->GetWidth(), m_Icon->GetHeight()); + wxString tmp; + ReadString(tmp, istr); + int type = -1; + sscanf(tmp.c_str(),"%d", &type); + + switch(type) + { + case WXLO_TYPE_TEXT: + return wxLayoutObjectText::Read(istr); + case WXLO_TYPE_CMD: + return wxLayoutObjectCmd::Read(istr); + case WXLO_TYPE_ICON: + return wxLayoutObjectIcon::Read(istr); + default: + return NULL; + } } -//-------------------------- wxLayoutObjectCmd +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + wxLayoutObjectText -wxLayoutObjectCmd::wxLayoutObjectCmd(int size, int family, int style, int - weight, bool underline, - wxColour const *fg, wxColour const *bg) - -{ - m_font = new wxFont(size,family,style,weight,underline); - m_ColourFG = fg; - m_ColourBG = bg; -} + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -wxLayoutObjectCmd::~wxLayoutObjectCmd() +wxLayoutObjectText::wxLayoutObjectText(const wxString &txt) { - delete m_font; + m_Text = txt; + m_Width = 0; + m_Height = 0; + m_Top = 0; + m_Bottom = 0; } -wxLayoutStyleInfo * -wxLayoutObjectCmd::GetStyle(void) const +wxLayoutObject * +wxLayoutObjectText::Copy(void) { - 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; + wxLayoutObjectText *obj = new wxLayoutObjectText(m_Text); + obj->m_Width = m_Width; + obj->m_Height = m_Height; + obj->m_Top = m_Top; + obj->m_Bottom = m_Bottom; + obj->SetUserData(m_UserData); + return obj; } + void -wxLayoutObjectCmd::Draw(wxDC &dc, wxPoint const &translate) +wxLayoutObjectText::Write(wxString &ostr) { - wxASSERT(m_font); - dc.SetFont(m_font); - if(m_ColourFG) - dc.SetTextForeground(*m_ColourFG); - if(m_ColourBG) - dc.SetTextBackground(*m_ColourBG); + ostr << (int) WXLO_TYPE_TEXT << '\n' + << m_Text << '\n'; } -void -wxLayoutObjectCmd::Layout(wxDC &dc, wxPoint p, CoordType baseline) +/* static */ +wxLayoutObjectText * +wxLayoutObjectText::Read(wxString &istr) { - m_Position = p; // required so we can find the right object for cursor - // this get called, so that recalculation uses right font sizes - Draw(dc,wxPoint(0,0)); + wxString text; + ReadString(text, istr); + + return new wxLayoutObjectText(text); } -//-------------------------- wxLayoutList - -wxLayoutList::wxLayoutList() +wxPoint +wxLayoutObjectText::GetSize(CoordType *top, CoordType *bottom) const { - m_DefaultSetting = NULL; - Clear(); -} -wxLayoutList::~wxLayoutList() -{ - if(m_DefaultSetting) - delete m_DefaultSetting; - // no deletion of objects, they are owned by the list + *top = m_Top; *bottom = m_Bottom; + return wxPoint(m_Width, m_Height); } void -wxLayoutList::LineBreak(void) +wxLayoutObjectText::Draw(wxDC &dc, wxPoint const &coords, + wxLayoutList *wxllist, + CoordType begin, CoordType end) { - Insert(new wxLayoutObjectLineBreak); - m_CursorPos.x = 0; m_CursorPos.y++; + if( end <= 0) + dc.DrawText(m_Text, coords.x, coords.y-m_Top); + else + { + // highlight the bit between begin and len + wxString str; + CoordType + xpos = coords.x, + ypos = coords.y-m_Top; + long width, height, descent; + + if(begin < 0) begin = 0; + if( end > (signed)m_Text.Length() ) + end = m_Text.Length(); + + str = m_Text.Mid(0, begin); + dc.DrawText(str, xpos, ypos); + dc.GetTextExtent(str, &width, &height, &descent); + xpos += width; + wxllist->StartHighlighting(dc); + str = m_Text.Mid(begin, end-begin); + dc.DrawText(str, xpos, ypos); + dc.GetTextExtent(str, &width, &height, &descent); + xpos += width; + wxllist->EndHighlighting(dc); + str = m_Text.Mid(end, m_Text.Length()-end); + dc.DrawText(str, xpos, ypos); + } } -void -wxLayoutList::SetFont(int family, int size, int style, int weight, - int underline, wxColour const *fg, - wxColour const *bg) +CoordType +wxLayoutObjectText::GetOffsetScreen(wxDC &dc, CoordType xpos) const { - 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; + CoordType + offs = 1, + maxlen = m_Text.Length(); + long + width = 0, + height, descent = 0l; + + if(xpos == 0) return 0; // easy - Insert( - new wxLayoutObjectCmd(m_FontPtSize,m_FontFamily,m_FontStyle,m_FontWeight,m_FontUnderline, - m_ColourFG, m_ColourBG)); + 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 -wxLayoutList::SetFont(int family, int size, int style, int weight, - int underline, char const *fg, char const *bg) - +wxLayoutObjectText::Layout(wxDC &dc, class wxLayoutList * ) { - wxColour const - * cfg = NULL, - * cbg = NULL; + long descent = 0l; - if( fg ) - cfg = wxTheColourDatabase->FindColour(fg); - if( bg ) - cbg = wxTheColourDatabase->FindColour(bg); - - SetFont(family,size,style,weight,underline,cfg,cbg); + dc.GetTextExtent(m_Text,&m_Width, &m_Height, &descent); + m_Bottom = descent; + m_Top = m_Height - m_Bottom; } -/// for access by wxLayoutWindow: +#ifdef WXLAYOUT_DEBUG void -wxLayoutList::GetSize(CoordType *max_x, CoordType *max_y, - CoordType *lineHeight) +wxLayoutObjectText::Debug(void) { - - if(max_x) *max_x = m_MaxX; - if(max_y) *max_y = m_MaxY; - if(lineHeight) *lineHeight = m_LineHeight; + wxLayoutObject::Debug(); + WXLO_DEBUG((" `%s`", m_Text.c_str())); } +#endif -void -wxLayoutList::ResetSettings(wxDC &dc) +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + + wxLayoutObjectIcon + + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap const &icon) { - // setting up the default: - dc.SetTextForeground( *wxBLACK ); - dc.SetTextBackground( *wxWHITE ); - dc.SetBackgroundMode( wxSOLID ); // to enable setting of text background - dc.SetFont( *wxNORMAL_FONT ); - if(m_DefaultSetting) - m_DefaultSetting->Draw(dc,wxPoint(0,0)); + m_Icon = new wxBitmap(icon); } + void -wxLayoutList::Layout(wxDC &dc, wxLayoutMargins *margins) +wxLayoutObjectIcon::Write(wxString &ostr) { - iterator i; + /* Exports icon through a temporary file. */ - // first object in current line - wxLayoutObjectList::iterator headOfLine; - // where we draw next - wxPoint position, position_HeadOfLine; - // size of last object - wxPoint size; - CoordType baseLine = m_FontPtSize; - CoordType baseLineSkip = (BASELINESTRETCH * baseLine)/10; - CoordType objBaseLine = baseLine; - wxLayoutObjectType type; - - // we need to count cursor positions - wxPoint cursorPos = wxPoint(0,0); + wxString file = wxGetTempFileName("wxloexport"); + + ostr << WXLO_TYPE_ICON << '\n' + << file << '\n'; + m_Icon->SaveFile(file, WXLO_BITMAP_FORMAT); +} +/* static */ +wxLayoutObjectIcon * +wxLayoutObjectIcon::Read(wxString &istr) +{ + wxString file; + ReadString(file, istr); - wxLayoutObjectBase *cursorObject = NULL; // let's find it again + if(! wxFileExists(file)) + return NULL; + wxLayoutObjectIcon *obj = new wxLayoutObjectIcon; - if(margins) + if(!obj->m_Icon->LoadFile(file, WXLO_BITMAP_FORMAT)) { - position.y = margins->top; - position.x = margins->left; + delete obj; + return NULL; } else - { - position.y = 0; - position.x = 0; - } - - ResetSettings(dc); - - i = begin(); - headOfLine = i; - position_HeadOfLine = position; + return obj; +} - do - { - if(i == end()) - return; - - type = (*i)->GetType(); - (*i)->Layout(dc, position, baseLine); - size = (*i)->GetSize(&objBaseLine); - // calculate next object's position: - position.x += size.x; - - // do we need to increase the line's height? - if(size.y > baseLineSkip) - { - baseLineSkip = size.y; - i = headOfLine; position = position_HeadOfLine; - continue; - } - if(objBaseLine > baseLine) - { - baseLine = objBaseLine; - i = headOfLine; position = position_HeadOfLine; - continue; - } +wxLayoutObject * +wxLayoutObjectIcon::Copy(void) +{ + wxLayoutObjectIcon *obj = new wxLayoutObjectIcon(new + wxBitmap(*m_Icon)); + obj->SetUserData(m_UserData); + return obj; +} - // when we reach here, the coordinates are valid, this part of - // the loop gets run only once per object - if(position.x > m_MaxX) - m_MaxX = position.x; - if(type == WXLO_TYPE_LINEBREAK) - { - cursorPos.x = 0; cursorPos.y ++; - } - else - cursorPos.x += (**i).CountPositions(); - - // now check whether we have finished handling this line: - if(type == WXLO_TYPE_LINEBREAK && i != tail()) - { - position.x = margins ? margins->left : 0; - position.y += baseLineSkip; - baseLine = m_FontPtSize; - objBaseLine = baseLine; // not all objects set it - baseLineSkip = (BASELINESTRETCH * baseLine)/10; - headOfLine = i; - headOfLine++; - position_HeadOfLine = position; - } - if(cursorObject == NULL && cursorPos.y == m_CursorPos.y) // look for cursor - { - if(cursorPos.x >= m_CursorPos.x && - m_CursorPos.x-cursorPos.x+(**i).CountPositions()) // cursor is in current object - { - cursorObject = *i; - CalculateCursor(dc); - } - } - i++; - } - while(i != end()); - m_MaxY = position.y + baseLineSkip; +wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap *icon) +{ + m_Icon = icon; } void -wxLayoutList::Draw(wxDC &dc, - CoordType fromLine, CoordType toLine, - iterator start, - wxPoint const &translate) +wxLayoutObjectIcon::Draw(wxDC &dc, wxPoint const &coords, + wxLayoutList *wxllist, + CoordType begin, CoordType /* len */) { - Layout(dc); // FIXME just for now + dc.DrawBitmap(*m_Icon, coords.x, coords.y-m_Icon->GetHeight(), + (m_Icon->GetMask() == NULL) ? FALSE : TRUE); +} - ResetSettings(dc); +void +wxLayoutObjectIcon::Layout(wxDC & /* dc */, class wxLayoutList * ) +{ +} - wxLayoutObjectList::iterator i; - - if(start == iterator(NULL)) - start = begin(); - else // we need to restore font settings +wxPoint +wxLayoutObjectIcon::GetSize(CoordType *top, CoordType *bottom) const +{ + *top = m_Icon->GetHeight(); + *bottom = 0; + return wxPoint(m_Icon->GetWidth(), m_Icon->GetHeight()); +} + + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + + wxLayoutObjectCmd + + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + + +wxLayoutStyleInfo::wxLayoutStyleInfo(int ifamily, + int isize, + int istyle, + int iweight, + int iul, + wxColour *fg, + wxColour *bg) +{ + family = ifamily; size = isize; + style = istyle; weight = iweight; + underline = iul; + if(fg) { - for( i = begin() ; i != start; i++) - if((**i).GetType() == WXLO_TYPE_CMD) - (**i).Draw(dc,translate); // apply font settings + m_fg = *fg; + m_fg_valid = TRUE; } - - while( start != end() && (**start).GetPosition().y < fromLine) + else + m_fg = *wxBLACK; + if(bg) { - if((**start).GetType() == WXLO_TYPE_CMD) - (**start).Draw(dc,translate); // apply font settings - start++; + m_bg = *bg; + m_bg_valid = TRUE; } - for( i = start ; - i != end() && (toLine == -1 || (**i).GetPosition().y < toLine) ; - i++ ) - (*i)->Draw(dc,translate); + else + m_bg = *wxWHITE; } -/** Erase at least to end of line */ -void -wxLayoutList::EraseAndDraw(wxDC &dc, iterator start) -{ - //look for begin of line - while(start != end() && start != begin() && (**start).GetType() != - WXLO_TYPE_LINEBREAK) - start--; - if(start == iterator(NULL)) - start = begin(); - if(start == iterator(NULL)) - return; - - wxPoint p = (**start).GetPosition(); +#define COPY_SI_(what) if(right.what != -1) what = right.what; - WXL_VAR(p.x);WXL_VAR(p.y); - //FIXME: wxGTK: MaxX()/MaxY() broken - //WXL_VAR(dc.MaxX()); WXL_VAR(dc.MaxY()); - dc.SetBrush(*wxWHITE_BRUSH); - dc.SetPen(wxPen(*wxWHITE,0,wxTRANSPARENT)); - dc.DrawRectangle(p.x,p.y,2000,2000); //dc.MaxX(),dc.MaxY()); - Draw(dc,-1,-1,start,wxPoint(0,0)); - //dc.DrawRectangle(p.x,p.y,2000,2000); //dc.MaxX(),dc.MaxY()); +wxLayoutStyleInfo & +wxLayoutStyleInfo::operator=(const wxLayoutStyleInfo &right) +{ + COPY_SI_(family); + COPY_SI_(style); + COPY_SI_(size); + COPY_SI_(weight); + COPY_SI_(underline); + if(right.m_fg_valid) m_fg = right.m_fg; + if(right.m_bg_valid) m_bg = right.m_bg; + return *this; } - -void -wxLayoutList::CalculateCursor(wxDC &dc) +wxLayoutObjectCmd::wxLayoutObjectCmd(int family, int size, int style, int + weight, int underline, + wxColour *fg, wxColour *bg) + { - CoordType width, height, descent; - CoordType baseLineSkip = 20; //FIXME + m_StyleInfo = new wxLayoutStyleInfo(family, size,style,weight,underline,fg,bg); +} - CoordType offset; - if( FindCurrentObject() == iterator(NULL)) // empty list - { - DrawCursor(dc,true); // erase it - m_CursorCoords = wxPoint(0,0); - m_CursorSize = wxPoint(2,baseLineSkip); - m_CursorMoved = false; // coords are valid - return; - } - wxLayoutObjectBase &obj = **FindCurrentObject(&offset); +wxLayoutObject * +wxLayoutObjectCmd::Copy(void) +{ + wxLayoutObjectCmd *obj = new wxLayoutObjectCmd( + m_StyleInfo->size, + m_StyleInfo->family, + m_StyleInfo->style, + m_StyleInfo->weight, + m_StyleInfo->underline, + m_StyleInfo->m_fg_valid ? + &m_StyleInfo->m_fg : NULL, + m_StyleInfo->m_bg_valid ? + &m_StyleInfo->m_bg : NULL); + obj->SetUserData(m_UserData); + return obj; +} - WXL_VAR(offset); - DrawCursor(dc,true); // erase it - m_CursorCoords = obj.GetPosition(); - WXL_VAR(m_CursorCoords.x); - if(obj.GetType() == WXLO_TYPE_TEXT) - { - wxLayoutObjectText *tobj = (wxLayoutObjectText *)&obj; - String & str = tobj->GetText(); - String sstr = str.substr(0,offset); - WXL_VAR(sstr); - dc.GetTextExtent(sstr,&width,&height,&descent); - WXL_VAR(width); - m_CursorCoords = wxPoint(m_CursorCoords.x+width, - m_CursorCoords.y); - m_CursorSize = wxPoint(2,height); - } - else if(obj.GetType() == WXLO_TYPE_LINEBREAK) +void +wxLayoutObjectCmd::Write(wxString &ostr) +{ + ostr << WXLO_TYPE_CMD << '\n' + << m_StyleInfo->size << '\n' + << m_StyleInfo->family << '\n' + << m_StyleInfo->style << '\n' + << m_StyleInfo->weight << '\n' + << m_StyleInfo->underline << '\n' + << m_StyleInfo->m_fg_valid << '\n' + << m_StyleInfo->m_bg_valid << '\n'; + if(m_StyleInfo->m_fg_valid) { - m_CursorCoords = wxPoint(0, m_CursorCoords.y); - m_CursorSize = wxPoint(2,baseLineSkip); + ostr << m_StyleInfo->m_fg.Red() << '\n' + << m_StyleInfo->m_fg.Green() << '\n' + << m_StyleInfo->m_fg.Blue() << '\n'; } - else + if(m_StyleInfo->m_bg_valid) { - // this is not necessarily the most "beautiful" solution: - //cursorPosition = wxPoint(position.x, position.y); - //cursorSize = wxPoint(size.x > 0 ? size.x : 1,size.y > 0 ? size.y : baseLineSkip); - m_CursorCoords = wxPoint(m_CursorCoords.x+obj.GetSize().x, m_CursorCoords.y); - m_CursorSize = wxPoint(2, obj.GetSize().y); - if(m_CursorSize.y < 1) m_CursorSize.y = baseLineSkip; + ostr << m_StyleInfo->m_bg.Red() << '\n' + << m_StyleInfo->m_bg.Green() << '\n' + << m_StyleInfo->m_bg.Blue() << '\n'; } - WXL_VAR(m_CursorCoords.x); - m_CursorMoved = false; // coords are valid } - -void -wxLayoutList::DrawCursor(wxDC &dc, bool erase) +/* static */ +wxLayoutObjectCmd * +wxLayoutObjectCmd::Read(wxString &istr) { - if(! m_Editable) - return; - - if(erase) + wxLayoutObjectCmd *obj = new wxLayoutObjectCmd; + + wxString tmp; + ReadString(tmp, istr); + sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->size); + ReadString(tmp, istr); + sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->family); + ReadString(tmp, istr); + sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->style); + ReadString(tmp, istr); + sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->weight); + ReadString(tmp, istr); + sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->underline); + ReadString(tmp, istr); + sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->m_fg_valid); + ReadString(tmp, istr); + sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->m_bg_valid); + if(obj->m_StyleInfo->m_fg_valid) { - //dc.SetBrush(*wxWHITE_BRUSH); - //dc.SetPen(wxPen(*wxWHITE,1,wxSOLID)); - //dc.DrawRectangle(m_CursorCoords.x, m_CursorCoords.y, m_CursorSize.x, m_CursorSize.y); - dc.Blit(m_CursorCoords.x, m_CursorCoords.y, m_CursorSize.x, - m_CursorSize.y, &m_CursorMemDC, - 0, 0, 0, 0); + int red, green, blue; + ReadString(tmp, istr); + sscanf(tmp.c_str(),"%d", &red); + ReadString(tmp, istr); + sscanf(tmp.c_str(),"%d", &green); + ReadString(tmp, istr); + sscanf(tmp.c_str(),"%d", &blue); + obj->m_StyleInfo->m_fg = wxColour(red, green, blue); } - else + if(obj->m_StyleInfo->m_bg_valid) { - if(IsDirty() || CursorMoved()) - { - DrawCursor(dc,true); - Layout(dc); - } - // Save background: - wxBitmap bm(m_CursorSize.x+1,m_CursorSize.y+1); - m_CursorMemDC.SelectObject(bm); - m_CursorMemDC.Blit(0, 0, m_CursorSize.x, m_CursorSize.y, - &dc, m_CursorCoords.x, - m_CursorCoords.y, 0, 0); - dc.SetBrush(*wxBLACK_BRUSH); - dc.SetPen(wxPen(*wxBLACK,1,wxSOLID)); - dc.DrawRectangle(m_CursorCoords.x, m_CursorCoords.y, - m_CursorSize.x, m_CursorSize.y); + int red, green, blue; + ReadString(tmp, istr); + sscanf(tmp.c_str(),"%d", &red); + ReadString(tmp, istr); + sscanf(tmp.c_str(),"%d", &green); + ReadString(tmp, istr); + sscanf(tmp.c_str(),"%d", &blue); + obj->m_StyleInfo->m_bg = wxColour(red, green, blue); } + return obj; } +wxLayoutObjectCmd::~wxLayoutObjectCmd() +{ + delete m_StyleInfo; +} +wxLayoutStyleInfo * +wxLayoutObjectCmd::GetStyle(void) const +{ + return m_StyleInfo; +} - - - -#ifdef WXLAYOUT_DEBUG void -wxLayoutList::Debug(void) +wxLayoutObjectCmd::Draw(wxDC &dc, wxPoint const & /* coords */, + wxLayoutList *wxllist, + CoordType begin, CoordType /* len */) { - CoordType offs; - wxLayoutObjectList::iterator i; - - wxLogDebug("------------------------debug start-------------------------"); - for(i = begin(); i != end(); i++) - (*i)->Debug(); - wxLogDebug("-----------------------debug end----------------------------"); - - // show current object: - ShowCurrentObject(); - i = FindCurrentObject(&offs); - wxLogDebug(" line length: %l", (long int) GetLineLength(i,offs)); - if(i == end()) - { - wxLogDebug("<>"); - return; // FIXME we should set cursor position to maximum allowed - // value then - } - if((*i)->GetType() == WXLO_TYPE_TEXT) - wxLogDebug(" \"%s\", offs=%d",((wxLayoutObjectText *)(*i))->GetText().c_str(), (int) offs); - else - wxLogDebug(g_aTypeStrings[(*i)->GetType()]); - + wxASSERT(m_StyleInfo); + wxllist->ApplyStyle(m_StyleInfo, dc); } void -wxLayoutList::ShowCurrentObject() +wxLayoutObjectCmd::Layout(wxDC &dc, class wxLayoutList * llist) { - CoordType offs; - wxLayoutObjectList::iterator i = FindCurrentObject(&offs); - - i = FindCurrentObject(&offs); - wxLogDebug(" Line length: %d", GetLineLength(i)); - - if(i == end()) - { - wxLogDebug("<>"); - return; // FIXME we should set cursor position to maximum allowed -// value then - } - if((*i)->GetType() == WXLO_TYPE_TEXT) - wxLogDebug(" \"%s\", offs: %d", - ((wxLayoutObjectText *)(*i))->GetText().c_str(), offs); - else - wxLogDebug(" %s", TypeString((*i)->GetType())); - wxLayoutDebug("CursorPos (%d, %d)", (int) m_CursorPos.x, (int) m_CursorPos.y); - wxLayoutDebug("CursorOffset = %d", (int) m_CursorOffset); - wxLayoutDebug("CursorObject = %p", m_CursorObject); - + // this get called, so that recalculation uses right font sizes + Draw(dc, wxPoint(0,0), llist); } -#endif -/******************** editing stuff ********************/ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -// don't change this, I know how to optimise this and will do it real -// soon (KB) + The wxLayoutLine object -/* - * FindObjectCursor: - * Finds the object belonging to a given cursor position cpos and - * returns an iterator to that object and stores the relative cursor - * position in offset. - * - * For linebreaks, the offset can be 0=before or 1=after. - * - * If the cpos coordinates don't exist, they are modified. - */ + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -wxLayoutObjectList::iterator -wxLayoutList::FindObjectCursor(wxPoint *cpos, CoordType *offset) +wxLayoutLine::wxLayoutLine(wxLayoutLine *prev, wxLayoutList *llist) { - wxPoint object = wxPoint(0,0); // runs along the objects - CoordType width = 0; - wxLayoutObjectList::iterator i, begin_it; - int go_up; - -//#ifdef WXLAYOUT_DEBUG -// wxLayoutDebug("Looking for object at (%d, %d)", cpos->x, cpos->y); -//#endif - - // optimisation: compare to last looked at object: - if(cpos->y > m_FoundCursor.y || (cpos->y == m_FoundCursor.y && - cpos->x >= m_FoundCursor.x)) - go_up = 1; - else - go_up = 0; - - //broken at the moment - //begin_it = m_FoundIterator; - //m_FoundCursor = *cpos; - begin_it = begin(); - go_up = 1; - for(i = begin_it; i != end() && object.y <= cpos->y; ) + 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) { - width = (**i).CountPositions(); - if(cpos->y == object.y) // a possible candidate - { - if((**i).GetType() ==WXLO_TYPE_LINEBREAK) - { - if(cpos->x == object.x) - { - if(offset) *offset = 0; - return m_FoundIterator = i; - } - if(offset) *offset=1; - cpos->x = object.x; - return m_FoundIterator = i; - } - if(cpos->x >= object.x && cpos->x <= object.x+width) // overlap - { - if(offset) *offset = cpos->x-object.x; -//#ifdef WXLAYOUT_DEBUG -// wxLayoutDebug(" found object at (%d, %d), type: %s", -// object.x, object.y, TypeString((*i)->GetType())); -//#endif - return m_FoundIterator = i; - } - } - // no overlap, increment coordinates - object.x += width; - if((**i).GetType() == WXLO_TYPE_LINEBREAK) - { - object.x = 0; - object.y++; - } - if(go_up) - i++; - else - i--; - }//for -//#ifdef WXLAYOUT_DEBUG -// wxLayoutDebug(" not found"); -//#endif -// return last object, coordinates of that one: - i = tail(); - if(i == end()) - return m_FoundIterator = i; - if((**i).GetType()==WXLO_TYPE_LINEBREAK) + m_LineNumber = m_Previous->GetLineNumber()+1; + m_Next = m_Previous->GetNextLine(); + m_Previous->m_Next = this; + } + if(m_Next) { - if(offset) - *offset = 1; - return m_FoundIterator = i; + m_Next->m_Previous = this; + m_Next->MoveLines(+1); + m_Next->RecalculatePositions(1,llist); } - cpos->x = object.x; // would be the coordinate of next object - cpos->y = object.y; - cpos->x += width; // last object's width - if(*offset) *offset = cpos->x-object.x; - return m_FoundIterator = i; // not found } -wxLayoutObjectList::iterator -wxLayoutList::FindCurrentObject(CoordType *offset) +wxLayoutLine::~wxLayoutLine() { -#if ! defined( USE_NEW_CURSORCODE ) - wxLayoutObjectList::iterator obj = end(); - - obj = FindObjectCursor(&m_CursorPos, offset); - if(obj == end()) // not ideal yet - { - obj = tail(); - if(obj != end()) // tail really exists - *offset = (*obj)->CountPositions(); // at the end of it - } - return obj; -#else - if(offset) - *offset = m_CursorOffset; - return m_CursorObject; -#endif + // kbList cleans itself } -#if ! defined( USE_NEW_CURSORCODE ) - -bool -wxLayoutList::MoveCursor(int dx, int dy) +wxPoint +wxLayoutLine::RecalculatePosition(wxLayoutList *llist) { - CoordType offs, lineLength; - wxLayoutObjectList::iterator i; - - m_CursorMoved = true; - - bool rc = true; // have we moved? + wxASSERT(m_Previous || GetLineNumber() == 0); - //FIXME calculate cursor object & offset for y movements - if(dy > 0 && m_CursorPos.y < m_MaxLine) - m_CursorPos.y += dy; - else if(dy < 0 && m_CursorPos.y > 0) - m_CursorPos.y += dy; // dy is negative - if(m_CursorPos.y < 0) + if(m_Previous) { - m_CursorPos.y = 0; - rc = false; + m_Position = m_Previous->GetPosition(); + m_Position.y += m_Previous->GetHeight(); } - else if (m_CursorPos.y > m_MaxLine) + else + m_Position = wxPoint(0,0); + llist->SetUpdateRect(m_Position); + 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) { - m_CursorPos.y = m_MaxLine; - rc = false; + if(recurse > 0) + m_Next->RecalculatePositions(--recurse, llist); + else if(pos != m_Position || m_Height != height) + m_Next->RecalculatePositions(0, llist); } +} + +wxLayoutObjectList::iterator +wxLayoutLine::FindObject(CoordType xpos, CoordType *offset) const +{ + wxASSERT(xpos >= 0); + wxASSERT(offset); + wxLayoutObjectList::iterator + i, + found = NULLIT; + CoordType x = 0, len; - while(dx > 0) + /* 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++) { - i = FindCurrentObject(&offs); - lineLength = GetLineLength(i,offs); - if(m_CursorPos.x < lineLength) + len = (**i).GetLength(); + if( x <= xpos && xpos <= x + len ) { - m_CursorPos.x ++; - dx--; - continue; + *offset = xpos-x; + if(xpos == x + len) // is there another object behind? + found = i; + else // we are really inside this object + return i; + } + x += (**i).GetLength(); + } + return found; // ==NULL if really none found +} + +wxLayoutObjectList::iterator +wxLayoutLine::FindObjectScreen(wxDC &dc, + CoordType xpos, CoordType *cxpos, + bool *found) const +{ + wxASSERT(cxpos); + wxASSERT(cxpos); + wxLayoutObjectList::iterator i; + CoordType x = 0, cx = 0, width; + + for(i = m_ObjectList.begin(); i != NULLIT; i++) + { + width = (**i).GetWidth(); + if( x <= xpos && xpos <= x + width ) + { + *cxpos = cx + (**i).GetOffsetScreen(dc, xpos-x); + if(found) *found = true; + return i; + } + x += (**i).GetWidth(); + cx += (**i).GetLength(); + } + // behind last object: + *cxpos = cx; + if(found) *found = false; + return m_ObjectList.tail(); +} + +/** Finds text in this line. + @param needle the text to find + @param xpos the position where to start the search + @return the cursoor coord where it was found or -1 +*/ +CoordType +wxLayoutLine::FindText(const wxString &needle, CoordType xpos) const +{ + int + cpos = 0, + relpos = -1; + wxString const *text; + + for(wxLOiterator i = m_ObjectList.begin(); i != m_ObjectList.end(); i++) + { + if(cpos >= xpos) // search from here! + { + if((**i).GetType() == WXLO_TYPE_TEXT) + { + text = & ((wxLayoutObjectText*)(*i))->GetText(); + relpos = text->Find(needle); + if(relpos >= cpos-xpos) // -1 if not found + { + return cpos+relpos; + } + } + cpos += (**i).GetLength(); + } + } + return -1; // not found +} + +bool +wxLayoutLine::Insert(CoordType xpos, wxLayoutObject *obj) +{ + wxASSERT(xpos >= 0); + wxASSERT(obj != NULL); + //FIXME: this could be optimised, for now be prudent: + m_Dirty = true; + CoordType offset; + wxLOiterator i = FindObject(xpos, &offset); + if(i == NULLIT) + { + if(xpos == 0 ) // aha, empty line! + { + m_ObjectList.push_back(obj); + m_Length += obj->GetLength(); + return true; + } + else + return false; + } + + CoordType len = (**i).GetLength(); + if(offset == 0 /*&& i != m_ObjectList.begin()*/) // why? + { // insert before this object + m_ObjectList.insert(i,obj); + m_Length += obj->GetLength(); + return true; + } + if(offset == len ) + { + if( i == m_ObjectList.tail()) // last object? + m_ObjectList.push_back(obj); + else + { // insert after current object + i++; + m_ObjectList.insert(i,obj); + } + m_Length += obj->GetLength(); + return true; + } + /* Otherwise we need to split the current object. + Fortunately this can only be a text object. */ + wxASSERT((**i).GetType() == WXLO_TYPE_TEXT); + wxString left, right; + wxLayoutObjectText *tobj = (wxLayoutObjectText *) *i; + left = tobj->GetText().substr(0,offset); + right = tobj->GetText().substr(offset,len-offset); + // current text object gets set to right half + tobj->GetText() = right; // set new text + // before it we insert the new object + m_ObjectList.insert(i,obj); + m_Length += obj->GetLength(); + // and before that we insert the left half + m_ObjectList.insert(i,new wxLayoutObjectText(left)); + return true; +} + +bool +wxLayoutLine::Insert(CoordType xpos, wxString text) +{ + wxASSERT(xpos >= 0); + //FIXME: this could be optimised, for now be prudent: + m_Dirty = true; + CoordType offset; + wxLOiterator i = FindObject(xpos, &offset); + if(i != NULLIT && (**i).GetType() == WXLO_TYPE_TEXT) + { + wxLayoutObjectText *tobj = (wxLayoutObjectText *) *i; + tobj->GetText().insert(offset, text); + m_Length += text.Length(); + + return true; + } + else + return Insert(xpos, new wxLayoutObjectText(text)); +} + +CoordType +wxLayoutLine::Delete(CoordType xpos, CoordType npos) +{ + CoordType offset, len; + + wxASSERT(xpos >= 0); + wxASSERT(npos >= 0); + //FIXME: this could be optimised, for now be prudent: + m_Dirty = true; + wxLOiterator i = FindObject(xpos, &offset); + while(npos > 0) + { + if(i == NULLIT) return npos; + // now delete from that object: + if((**i).GetType() != WXLO_TYPE_TEXT) + { + if(offset != 0) // at end of line after a non-text object + return npos; + // always len == 1: + len = (**i).GetLength(); + m_Length -= len; + npos -= len; + m_ObjectList.erase(i); } else { - if(m_CursorPos.y < m_MaxLine) + // tidy up: remove empty text objects + if((**i).GetLength() == 0) { - m_CursorPos.y++; - m_CursorPos.x = 0; - dx--; + m_ObjectList.erase(i); + continue; } - else + // Text object: + CoordType max = (**i).GetLength() - offset; + if(npos < max) max = npos; + if(max == 0) { - rc = false; - break; // cannot move there + 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); } } - while(dx < 0) + return npos; +} + +bool +wxLayoutLine::DeleteWord(CoordType xpos) +{ + wxASSERT(xpos >= 0); + CoordType offset; + //FIXME: this could be optimised, for now be prudent: + m_Dirty = true; + + wxLOiterator i = FindObject(xpos, &offset); + + for(;;) { - if(m_CursorPos.x > 0) + if(i == NULLIT) return false; + if((**i).GetType() != WXLO_TYPE_TEXT) { - m_CursorPos.x --; - dx++; + // 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 - { - if(m_CursorPos.y > 0) + { // text object: + if(offset == (**i).GetLength()) // at end of object { - m_CursorPos.y --; - m_CursorPos.x = 0; - i = FindCurrentObject(&offs); - lineLength = GetLineLength(i,offs); - m_CursorPos.x = lineLength; - dx++; + i++; offset = 0; continue; } + wxLayoutObjectText *tobj = (wxLayoutObjectText *)*i; + size_t count = 0; + wxString str = tobj->GetText(); + str = str.substr(offset,str.Length()-offset); + // Find out how many positions we need to delete: + // 1. eat leading space + while(isspace(str.c_str()[count])) count++; + // 2. eat the word itself: + while(isalnum(str.c_str()[count])) count++; + // now delete it: + wxASSERT(count+offset <= (size_t) (**i).GetLength()); + ((wxLayoutObjectText *)*i)->GetText().erase(offset,count); + m_Length -= count; + return true; + } + } + wxASSERT(0); // we should never arrive here +} + +wxLayoutLine * +wxLayoutLine::DeleteLine(bool update, wxLayoutList *llist) +{ + if(m_Next) m_Next->m_Previous = m_Previous; + if(m_Previous) m_Previous->m_Next = m_Next; + if(update) + { + m_Next->MoveLines(-1); + m_Next->RecalculatePositions(1, llist); + } + wxLayoutLine *next = m_Next; + delete this; + return next; +} + +void +wxLayoutLine::Draw(wxDC &dc, + wxLayoutList *llist, + const wxPoint & offset) const +{ + wxLayoutObjectList::iterator i; + wxPoint pos = offset; + pos = pos + GetPosition(); + + pos.y += m_BaseLine; + + CoordType xpos = 0; // cursorpos, lenght of line + + CoordType from, to, tempto; + //FIXME This doesn't work yet, needs updating afterr default + //settings for list or a wxLayoutObjectCmd have changed: + //llist->ApplyStyle(&((wxLayoutLine *)this)->m_StyleInfo, dc); + int highlight = llist->IsSelected(this, &from, &to); +// WXLO_DEBUG(("highlight=%d", highlight )); + if(highlight == 1) // we need to draw the whole line inverted! + llist->StartHighlighting(dc); + else + llist->EndHighlighting(dc); + + for(i = m_ObjectList.begin(); i != NULLIT; i++) + { + if(highlight == -1) // partially highlight line + { + // parts of the line need highlighting + tempto = xpos+(**i).GetLength(); + (**i).Draw(dc, pos, llist, from-xpos, to-xpos); + } + else + (**i).Draw(dc, pos, llist); + pos.x += (**i).GetWidth(); + xpos += (**i).GetLength(); + } +} + +void +wxLayoutLine::Layout(wxDC &dc, + wxLayoutList *llist, + wxPoint *cursorPos, + wxPoint *cursorSize, + int cx) +{ + wxLayoutObjectList::iterator 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; + + m_Dirty = false; + + if(cursorPos) + { + *cursorPos = m_Position; + if(cursorSize) *cursorSize = wxPoint(0,0); + } + + //FIXME This doesn't work yet, needs updating afterr default + //settings for list or a wxLayoutObjectCmd have changed: + //llist->ApplyStyle(&m_StyleInfo, dc); + for(i = m_ObjectList.begin(); i != NULLIT; i++) + { + (**i).Layout(dc, llist); + size = (**i).GetSize(&objTopHeight, &objBottomHeight); + + if(cursorPos && ! cursorFound) + { // we need to check whether the text cursor is here + len = (**i).GetLength(); + if(count <= cx && count+len > cx) + { + if((**i).GetType() == WXLO_TYPE_TEXT) + { + len = cx - count; // pos in object + CoordType width, height, descent; + dc.GetTextExtent((*(wxLayoutObjectText*)*i).GetText().substr(0,len), + &width, &height, &descent); + cursorPos->x += width; + cursorPos->y = m_Position.y; + wxString str; + if(len < (**i).GetLength()) + str = (*(wxLayoutObjectText*)*i).GetText().substr(len,1); + else + str = WXLO_CURSORCHAR; + dc.GetTextExtent(str, &width, &height, &descent); + wxASSERT(cursorSize); + // Just in case some joker inserted an empty string object: + if(width == 0) width = WXLO_MINIMUM_CURSOR_WIDTH; + if(height == 0) height = objHeight; + cursorSize->x = width; + cursorSize->y = height; + cursorFound = true; // no more checks + } + else + { // on some other object + CoordType top, bottom; // unused + *cursorSize = (**i).GetSize(&top,&bottom); + cursorPos->y = m_Position.y; + cursorFound = true; // no more checks + } + } else { - rc = false; - break; // cannot move left any more + count += len; + cursorPos->x += (**i).GetWidth(); } + } // cursor finding + objHeight = size.y; + m_Width += size.x; + if(objHeight > m_Height) m_Height = objHeight; + if(objTopHeight > topHeight) topHeight = objTopHeight; + if(objBottomHeight > bottomHeight) bottomHeight = objBottomHeight; + } + if(topHeight + bottomHeight > m_Height) m_Height = + topHeight+bottomHeight; + m_BaseLine = topHeight; + + if(m_Height == 0) + { + CoordType width, height, descent; + dc.GetTextExtent(WXLO_CURSORCHAR, &width, &height, &descent); + m_Height = height; + m_BaseLine = m_Height - descent; + } + + + // tell next line about coordinate change + if(m_Next && objHeight != oldHeight) + m_Next->RecalculatePositions(0, llist); + + // We need to check whether we found a valid cursor size: + if(cursorPos) + { + // this might be the case if the cursor is at the end of the + // line or on a command object: + if(cursorSize->y < WXLO_MINIMUM_CURSOR_WIDTH) + { + CoordType width, height, descent; + dc.GetTextExtent(WXLO_CURSORCHAR, &width, &height, &descent); + cursorSize->x = width; + cursorSize->y = height; } + if(m_BaseLine >= cursorSize->y) // the normal case anyway + cursorPos->y += m_BaseLine-cursorSize->y; } -// final adjustment: - i = FindCurrentObject(&offs); - lineLength = GetLineLength(i,offs); - if(m_CursorPos.x > lineLength) + RecalculatePositions(1, llist); +} + + +wxLayoutLine * +wxLayoutLine::Break(CoordType xpos, wxLayoutList *llist) +{ + wxASSERT(xpos >= 0); + //FIXME: this could be optimised, for now be prudent: + m_Dirty = true; + + /* If we are at the begin of a line, we want to move all other + lines down and stay with the cursor where we are. However, if we + are in an empty line, we want to move down with it. */ + if(xpos == 0 && GetLength() > 0) + { // insert an empty line before this one + wxLayoutLine *prev = new wxLayoutLine(m_Previous, llist); + if(m_Previous == NULL) + { // We were in first line, need to link in new empty line + // before this. + prev->m_Next = this; + m_Previous = prev; + m_Previous->m_Height = 0; // this is a wild guess + } + if(m_Next) + m_Next->RecalculatePositions(1, llist); + return m_Previous; + } + + CoordType offset; + wxLOiterator i = FindObject(xpos, &offset); + if(i == NULLIT) + // must be at the end of the line then + return new wxLayoutLine(this, llist); + // split this line: + + wxLayoutLine *newLine = new wxLayoutLine(this, llist); + // split object at i: + if((**i).GetType() == WXLO_TYPE_TEXT && offset != 0) { - m_CursorPos.x = lineLength; - rc = false; + 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 } -#ifdef WXLAYOUT_DEBUG - ShowCurrentObject(); -#endif - return rc; + else + if(offset > 0) + i++; // move objects from here to new list + + while(i != m_ObjectList.end()) + { + newLine->Append(*i); + m_Length -= (**i).GetLength(); + m_ObjectList.remove(i); // remove without deleting it + } + if(m_Next) + m_Next->RecalculatePositions(2, llist); + return newLine; } + -#else +void +wxLayoutLine::MergeNextLine(wxLayoutList *llist) +{ + wxCHECK_RET(GetNextLine(),"wxLayout internal error: no next line to merge"); + wxLayoutObjectList &list = GetNextLine()->m_ObjectList; + wxLOiterator i; + //FIXME: this could be optimised, for now be prudent: + m_Dirty = true; + + for(i = list.begin(); i != list.end();) + { + Append(*i); + list.remove(i); // remove without deleting it + } + wxASSERT(list.empty()); + wxLayoutLine *oldnext = GetNextLine(); + SetNext(GetNextLine()->GetNextLine()); + delete oldnext; + GetNextLine()->MoveLines(-1); + RecalculatePositions(1, llist); +} -bool -wxLayoutList::MoveCursor(int dx, int dy) +CoordType +wxLayoutLine::GetWrapPosition(CoordType column) { - CoordType diff; + CoordType offset; + wxLOiterator i = FindObject(column, &offset); + if(i == NULLIT) return -1; // cannot wrap - m_CursorMoved = true; + // go backwards through the list and look for space in text objects + do + { + if((**i).GetType() == WXLO_TYPE_TEXT) + { + do + { + if( isspace(((wxLayoutObjectText*)*i)->GetText().c_str()[(size_t)offset])) + return column; + else + { + offset--; + column--; + } + }while(offset != -1); + i--; // move on to previous object + } + else + { + column -= (**i).GetLength(); + i--; + } + if( i != NULLIT) + offset = (**i).GetLength(); + }while(i != NULLIT); + /* If we reached the begin of the list and have more than one + object, that one is longer than the margin, so break behind + it. */ + CoordType pos = 0; + i = m_ObjectList.begin(); + while(i != NULLIT && (**i).GetType() != WXLO_TYPE_TEXT) + { + pos += (**i).GetLength(); + i++; + } + if(i == NULLIT) return -1; //why should this happen? + pos += (**i).GetLength(); + i++; + while(i != NULLIT && (**i).GetType() != WXLO_TYPE_TEXT) + { + pos += (**i).GetLength(); + i++; + } + if(i == NULLIT) return -1; //this is possible, if there is only one text object + // now we are at the second text object: + pos -= (**i).GetLength(); + return pos; // in front of it +} - enum { up, down} direction; - wxPoint newPos = wxPoint(m_CursorPos.x + dx, - m_CursorPos.y + dy); +#ifdef WXLAYOUT_DEBUG +void +wxLayoutLine::Debug(void) +{ + wxString tmp; + wxPoint pos = GetPosition(); + WXLO_DEBUG(("Line %ld, Pos (%ld,%ld), Height %ld", + (long int) GetLineNumber(), + (long int) pos.x, (long int) pos.y, + (long int) GetHeight())); + if(m_ObjectList.begin() != NULLIT) + (**m_ObjectList.begin()).Debug(); + +} +#endif - // check for bounds - if(newPos.x < 0) newPos.x = 0; - if(newPos.y < 0) newPos.y = 0; - else if(newPos.y > m_MaxLine) newPos.y = m_MaxLine; +void +wxLayoutLine::Copy(wxLayoutList *llist, + CoordType from, + CoordType to) +{ + CoordType firstOffset, lastOffset; - if(newPos.y > m_CursorPos.y || - newPos.y == m_CursorPos.y && - newPos.x >= m_CursorPos.x) - direction = up; - else - direction = down; + if(to == -1) to = GetLength(); + if(from == to) return; - // now move cursor forwards until at the new position: + wxLOiterator first = FindObject(from, &firstOffset); + wxLOiterator last = FindObject(to, &lastOffset); - // first, go to the right line: - while(newPos.y != m_CursorPos.y) + // Common special case: only one object + if( first != NULLIT && last != NULLIT && *first == *last ) { - if(direction == up) + if( (**first).GetType() == WXLO_TYPE_TEXT ) { - m_CursorPos.x += - (**m_CursorObject).CountPositions() - m_CursorOffset; - if(m_CursorObject == tail()) - break; // can't go any further - if((**m_CursorObject).GetType() == WXLO_TYPE_LINEBREAK - && m_CursorOffset == 1) - { - m_CursorPos.y++; m_CursorPos.x = 0; - } - m_CursorObject ++; m_CursorOffset = 0; + llist->Insert(new wxLayoutObjectText( + ((wxLayoutObjectText + *)*first)->GetText().substr(firstOffset, + lastOffset-firstOffset)) + ); + return; } - else // down + else // what can we do? { - m_CursorPos.x -= m_CursorOffset; - if(m_CursorObject == begin()) - break; // can't go any further - if((**m_CursorObject).GetType() == WXLO_TYPE_LINEBREAK && - m_CursorOffset == 0) - { - m_CursorPos.y--; - m_CursorPos.x = GetLineLength(m_CursorObject); - } - m_CursorObject --; m_CursorOffset = (**m_CursorObject).CountPositions(); + if(lastOffset > firstOffset) // i.e. +1 :-) + llist->Insert( (**first).Copy() ); + return; } } - if(newPos.y != m_CursorPos.y) // reached begin/end of list, - newPos.y = m_CursorPos.y; // exited by break - - // now line is right, go to right column: - direction = newPos.x >= m_CursorPos.x ? up : down; - while(newPos.x != m_CursorPos.x) + + // If we reach here, we can safely copy the whole first object from + // the firstOffset position on: + if((**first).GetType() == WXLO_TYPE_TEXT && firstOffset != 0) { - if(direction == up) + llist->Insert(new wxLayoutObjectText( + ((wxLayoutObjectText *)*first)->GetText().substr(firstOffset)) + ); + } + else if(firstOffset == 0) + llist->Insert( (**first).Copy() ); + // else nothing to copy :-( + + // Now we copy all objects before the last one: + wxLOiterator i = first; i++; + for( ; i != last; i++) + llist->Insert( (**i).Copy() ); + + // And now the last object: + if(lastOffset != 0) + { + if( (**last).GetType() == WXLO_TYPE_TEXT ) { - diff = newPos.x - m_CursorPos.x; - if(diff >= (**m_CursorObject).CountPositions()) - { - m_CursorPos.x += (**m_CursorObject).CountPositions(); - if(m_CursorObject == tail()) - { - m_CursorOffset = (**m_CursorObject).CountPositions(); - break; // cannot go further - } - m_CursorObject++; m_CursorOffset = 0; - } - else - { - m_CursorPos.x += diff; - m_CursorOffset += diff; - } + llist->Insert(new wxLayoutObjectText( + ((wxLayoutObjectText *)*last)->GetText().substr(0,lastOffset)) + ); + } + else + llist->Insert( (**last).Copy() ); + } +} + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + + The wxLayoutList object + + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +wxLayoutList::wxLayoutList() +{ + m_FirstLine = NULL; + InvalidateUpdateRect(); + Clear(); +} + +wxLayoutList::~wxLayoutList() +{ + InternalClear(); + m_FirstLine->DeleteLine(false, this); +} + +void +wxLayoutList::Empty(void) +{ + while(m_FirstLine) + m_FirstLine = m_FirstLine->DeleteLine(false, this); + + m_CursorPos = wxPoint(0,0); + m_CursorScreenPos = wxPoint(0,0); + m_CursorSize = wxPoint(0,0); + m_FirstLine = new wxLayoutLine(NULL, this); // empty first line + m_CursorLine = m_FirstLine; + InvalidateUpdateRect(); +} + + +void +wxLayoutList::InternalClear(void) +{ + Empty(); + m_Selection.m_selecting = false; + m_Selection.m_valid = false; + + m_DefaultSetting.family = wxSWISS; + m_DefaultSetting.size = WXLO_DEFAULTFONTSIZE; + m_DefaultSetting.style = wxNORMAL; + m_DefaultSetting.weight = wxNORMAL; + m_DefaultSetting.underline = 0; + m_DefaultSetting.m_fg_valid = TRUE; + m_DefaultSetting.m_fg = *wxBLACK; + m_DefaultSetting.m_bg_valid = TRUE; + m_DefaultSetting.m_bg = *wxWHITE; + + m_CurrentSetting = m_DefaultSetting; +} + +void +wxLayoutList::SetFont(int family, int size, int style, int weight, + int underline, wxColour *fg, + wxColour *bg) +{ + if(family != -1) m_CurrentSetting.family = family; + if(size != -1) m_CurrentSetting.size = size; + if(style != -1) m_CurrentSetting.style = style; + if(weight != -1) m_CurrentSetting.weight = weight; + if(underline != -1) m_CurrentSetting.underline = underline != 0; + if(fg) m_CurrentSetting.m_fg = *fg; + if(bg) m_CurrentSetting.m_bg = *bg; + Insert( + new wxLayoutObjectCmd( + m_CurrentSetting.family, + m_CurrentSetting.size, + m_CurrentSetting.style, + m_CurrentSetting.weight, + m_CurrentSetting.underline, + fg, bg)); +} + +void +wxLayoutList::SetFont(int family, int size, int style, int weight, + int underline, char const *fg, char const *bg) + +{ + wxColour + *cfg = NULL, + *cbg = NULL; + + if( fg ) + cfg = wxTheColourDatabase->FindColour(fg); + if( bg ) + cbg = wxTheColourDatabase->FindColour(bg); + + SetFont(family,size,style,weight,underline,cfg,cbg); +} + +void +wxLayoutList::Clear(int family, int size, int style, int weight, + int underline, wxColour *fg, wxColour *bg) +{ + InternalClear(); + m_DefaultSetting = wxLayoutStyleInfo(family, size, style, weight, + underline, fg, bg); + m_CurrentSetting = m_DefaultSetting; +} + +wxPoint +wxLayoutList::FindText(const wxString &needle, const wxPoint &cpos) const +{ + int xpos; + + wxLayoutLine *line; + for(line = m_FirstLine; + line; + line = line->GetNextLine()) + { + if(line->GetLineNumber() >= cpos.y) + { + xpos = line->FindText(needle, + (line->GetLineNumber() == cpos.y) ? + cpos.x : 0); + if(xpos != -1) + return wxPoint(xpos, line->GetLineNumber()); + } + } + return wxPoint(-1,-1); +} + + +bool +wxLayoutList::MoveCursorTo(wxPoint const &p) +{ + 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; + } + } + else // move down + { + wxLayoutLine *last = m_CursorLine; + if(! m_CursorLine->GetNextLine()) return false; + while(n > 0 && m_CursorLine) + { + n--; + m_CursorPos.y ++; + m_CursorLine = m_CursorLine->GetNextLine(); + } + if(! m_CursorLine) + { + m_CursorLine = last; + m_CursorPos.y ++; + rc = false; + } + else + { + if(m_CursorPos.x > m_CursorLine->GetLength()) + m_CursorPos.x = m_CursorLine->GetLength(); + rc = true; + } + } + return rc; +} + +bool +wxLayoutList::MoveCursorHorizontally(int n) +{ + SetUpdateRect(m_CursorScreenPos); + SetUpdateRect(m_CursorScreenPos+m_CursorSize); + int move; + while(n < 0) + { + if(m_CursorPos.x == 0) // at begin of line + { + if(! MoveCursorVertically(-1)) + break; + MoveCursorToEndOfLine(); + n++; + continue; + } + move = -n; + if(move > m_CursorPos.x) move = m_CursorPos.x; + m_CursorPos.x -= move; n += move; + } + + while(n > 0) + { + int len = m_CursorLine->GetLength(); + if(m_CursorPos.x == len) // at end of line + { + if(! MoveCursorVertically(1)) + break; + MoveCursorToBeginOfLine(); + n--; + continue; + } + move = n; + if( move >= len-m_CursorPos.x) move = len-m_CursorPos.x; + m_CursorPos.x += move; + n -= move; + } + return n == 0; +} + +bool +wxLayoutList::Insert(wxString const &text) +{ + wxASSERT(m_CursorLine); + SetUpdateRect(m_CursorScreenPos); + SetUpdateRect(m_CursorScreenPos+m_CursorSize); + m_CursorLine->Insert(m_CursorPos.x, text); + m_CursorPos.x += text.Length(); + m_CursorLine->RecalculatePositions(true, this); //FIXME needed? + return true; +} + +bool +wxLayoutList::Insert(wxLayoutObject *obj) +{ + wxASSERT(m_CursorLine); + if(! m_CursorLine) m_CursorLine = GetFirstLine(); + SetUpdateRect(m_CursorScreenPos); + SetUpdateRect(m_CursorScreenPos+m_CursorSize); + m_CursorLine->Insert(m_CursorPos.x, obj); + m_CursorPos.x += obj->GetLength(); + m_CursorLine->RecalculatePositions(true, this); //FIXME needed? + return true; +} + +bool +wxLayoutList::Insert(wxLayoutList *llist) +{ + wxASSERT(llist); + bool rc = TRUE; + + for(wxLayoutLine *line = llist->GetFirstLine(); + line; + line = line->GetNextLine() + ) + { + for(wxLOiterator i = line->GetFirstObject(); + i != NULLIT; + i++) + rc |= Insert(*i); + LineBreak(); + } + return rc; +} + + + + +bool +wxLayoutList::LineBreak(void) +{ + wxASSERT(m_CursorLine); + bool setFirst = (m_CursorLine == m_FirstLine && m_CursorPos.x == 0); + 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(); + if(m_CursorPos.x != 0) + m_CursorPos.y++; + m_CursorPos.x = 0; +// doesn't help m_CursorLine.MarkDirty(); + m_CursorLine->RecalculatePositions(true, this); //FIXME needed? + return true; +} + +bool +wxLayoutList::WrapLine(CoordType column) +{ + if(m_CursorPos.x <= column || column < 1) + return false; // do nothing yet + else + { + CoordType xpos = m_CursorLine->GetWrapPosition(column); + if(xpos == -1) + return false; // cannot break line + //else: + CoordType newpos = m_CursorPos.x - xpos - 1; + m_CursorPos.x = xpos; + SetUpdateRect(m_CursorScreenPos); + SetUpdateRect(m_CursorScreenPos+m_CursorSize); + LineBreak(); + Delete(1); // delete the space + m_CursorPos.x = newpos; + m_CursorLine->RecalculatePositions(true, this); //FIXME needed? + return true; + } +} + +bool +wxLayoutList::Delete(CoordType npos) +{ + wxASSERT(m_CursorLine); + SetUpdateRect(m_CursorScreenPos); + SetUpdateRect(m_CursorScreenPos+m_CursorSize); + CoordType left; + do + { + left = m_CursorLine->Delete(m_CursorPos.x, npos); + if(left == 0) + return true; + // More to delete, continue on next line. + // First, check if line is empty: + if(m_CursorLine->GetLength() == 0) + { // in this case, updating could probably be optimised +#ifdef WXLO_DEBUG + wxASSERT(DeleteLines(1) == 0); +#else + DeleteLines(1); +#endif + + left--; } - else // down + else { - diff = m_CursorPos.x - newPos.x; - if(diff >= m_CursorOffset) - { - if(m_CursorObject == begin()) - { - m_CursorOffset = 0; - m_CursorPos.x = 0; - break; // cannot go further - } - m_CursorObject--; - m_CursorOffset = (**m_CursorObject).CountPositions(); - } + // Need to join next line + if(! m_CursorLine->GetNextLine()) + break; // cannot else { - m_CursorPos.x -= diff; - m_CursorOffset -= diff; + m_CursorLine->MergeNextLine(this); + left--; } } } - return true; // FIXME: when return what? + while(left); + m_CursorLine->RecalculatePositions(true, this); //FIXME needed? + 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(); + m_CursorLine->RecalculatePositions(2, this); + return n-1; + } + //else: + line = m_CursorLine; + m_CursorLine = m_CursorLine->DeleteLine(true, this); + n--; + if(line == m_FirstLine) m_FirstLine = m_CursorLine; + wxASSERT(m_FirstLine); + wxASSERT(m_CursorLine); + } + m_CursorLine->RecalculatePositions(2, this); + return n; } -#endif void -wxLayoutList::Delete(CoordType count) +wxLayoutList::Recalculate(wxDC &dc, CoordType bottom) { - WXL_TRACE(Delete); + wxLayoutLine *line = m_FirstLine; - if(!m_Editable) - return; + // first, make sure everything is calculated - this might not be + // needed, optimise it later + ApplyStyle(&m_DefaultSetting, dc); + while(line) + { + line->RecalculatePosition(this); // so we don't need to do it all the time + // little condition to speed up redrawing: + if(bottom != -1 && line->GetPosition().y > bottom) break; + line = line->GetNextLine(); + } +} + +void +wxLayoutList::UpdateCursorScreenPos(wxDC &dc) +{ + wxASSERT(m_CursorLine); + m_CursorLine->Layout(dc, this, (wxPoint *)&m_CursorScreenPos, (wxPoint *)&m_CursorSize, m_CursorPos.x); +} - WXL_VAR(count); +wxPoint +wxLayoutList::GetCursorScreenPos(wxDC &dc) +{ + UpdateCursorScreenPos(dc); + return m_CursorScreenPos; +} - m_bModified = true; +/* + Is called before each Draw(). Now, it will re-layout all lines which + have changed. +*/ +void +wxLayoutList::Layout(wxDC &dc, CoordType bottom, bool forceAll) +{ + wxLayoutLine *line = m_FirstLine; - CoordType offs; - wxLayoutObjectList::iterator i; - - do + // first, make sure everything is calculated - this might not be + // needed, optimise it later + ApplyStyle(&m_DefaultSetting, dc); + while(line) { - i = FindCurrentObject(&offs); - startover: // ugly, but easiest way to do it - if(i == end()) - return; // we cannot delete anything more - -/* Here we need to treat linebreaks differently. - If offs==0 we are before the linebreak, otherwise behind. */ - if((*i)->GetType() == WXLO_TYPE_LINEBREAK) - { - if(offs == 0) - { - m_MaxLine--; - erase(i); - m_CursorObject = i; // new i! - m_CursorOffset = 0; // Pos unchanged - count--; - continue; // we're done - } - else // delete the object behind the linebreak - { - i++; // we increment and continue as normal - offs=0; - goto startover; - } - } - else if((*i)->GetType() == WXLO_TYPE_TEXT) + if(forceAll || line->IsDirty()) { - wxLayoutObjectText *tobj = (wxLayoutObjectText *)*i; - CoordType len = tobj->CountPositions(); -// If we find the end of a text object, this means that we -// have to delete from the object following it. - if(len == offs) - { - i++; - offs = 0; - goto startover; - } - else if(len <= count) // delete this object - { - count -= len; - erase(i); - m_CursorObject = i; - m_CursorOffset = 0; - continue; - } + line->GetStyleInfo() = m_CurrentSetting; + if(line == m_CursorLine) + line->Layout(dc, this, (wxPoint *)&m_CursorScreenPos, + (wxPoint *)&m_CursorSize, m_CursorPos.x); else - { - len = count; - tobj->GetText().erase(offs,len); - // cursor unchanged - return; // we are done - } - } - else// all other objects: delete the object -// this only works as expected if the non-text object has 0/1 -// as offset values. Not tested with "longer" objects. - { - CoordType len = (*i)->CountPositions(); - if(offs == 0) - { - count = count > len ? count -= len : 0; - erase(i); // after this, i is the iterator for the - // following object - m_CursorObject = i; - m_CursorOffset = 0; - continue; - } - else // delete the following object - { - i++; // we increment and continue as normal - offs=0; - goto startover; - } + line->Layout(dc, this); + // little condition to speed up redrawing: + if(bottom != -1 && line->GetPosition().y > bottom) break; } + line->RecalculatePositions(1,this); + line = line->GetNextLine(); } - while(count && i != end()); + +///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(wxLayoutObjectBase *obj) +wxLayoutList::Draw(wxDC &dc, + wxPoint const &offset, + CoordType top, + CoordType bottom) { - wxCHECK_RET( obj, "no object to insert" ); + wxLayoutLine *line = m_FirstLine; - m_bModified = true; - - CoordType offs; - wxLayoutObjectList::iterator i = FindCurrentObject(&offs); + Layout(dc); + ApplyStyle(&m_DefaultSetting, dc); + wxBrush brush(m_CurrentSetting.m_bg, wxSOLID); + dc.SetBrush(brush); + dc.SetBackgroundMode(wxTRANSPARENT); + + while(line) + { + // only draw if between top and bottom: + if((top == -1 || line->GetPosition().y + line->GetHeight() >= top)) + line->Draw(dc, this, offset); + else + line->Layout(dc, this); + // little condition to speed up redrawing: + if(bottom != -1 && line->GetPosition().y > bottom) break; + line = line->GetNextLine(); + } + InvalidateUpdateRect(); -// WXL_TRACE(Insert(obj)); + WXLO_DEBUG(("Selection is %s : l%d,%ld/%ld,%ld", + m_Selection.m_valid ? "valid" : "invalid", + m_Selection.m_CursorA.x, m_Selection.m_CursorA.y, + m_Selection.m_CursorB.x, m_Selection.m_CursorB.y)); +} - if(i == end()) +wxLayoutObject * +wxLayoutList::FindObjectScreen(wxDC &dc, wxPoint const pos, + wxPoint *cursorPos, + bool *found) +{ + // First, find the right line: + wxLayoutLine *line = m_FirstLine; + wxPoint p; + + // we need to run a layout here to get font sizes right :-( + ApplyStyle(&m_DefaultSetting, dc); + while(line) { - push_back(obj); - m_CursorObject = tail(); + p = line->GetPosition(); + if(p.y <= pos.y && p.y+line->GetHeight() >= pos.y) + break; + line->Layout(dc, this); + line = line->GetNextLine(); } - else if(offs == 0) + if(line == NULL) { - insert(i,obj); - m_CursorObject = i; + if(found) *found = false; + return NULL; // not found } -// do we have to split a text object? - else if((*i)->GetType() == WXLO_TYPE_TEXT && offs != (*i)->CountPositions()) + if(cursorPos) cursorPos->y = line->GetLineNumber(); + // Now, find the object in the line: + wxLOiterator i = line->FindObjectScreen(dc, pos.x, + cursorPos ? & cursorPos->x : NULL , + found); + return (i == NULLIT) ? NULL : *i; + +} + +wxPoint +wxLayoutList::GetSize(void) const +{ + wxLayoutLine + *line = m_FirstLine, + *last = line; + if(! line) + return wxPoint(0,0); + + wxPoint maxPoint(0,0); + + // find last line: + while(line) { - wxLayoutObjectText *tobj = (wxLayoutObjectText *) *i; + if(line->GetWidth() > maxPoint.x) + maxPoint.x = line->GetWidth(); + last = line; + line = line->GetNextLine(); + } + + maxPoint.y = last->GetPosition().y + last->GetHeight(); + return maxPoint; +} + + +void +wxLayoutList::DrawCursor(wxDC &dc, bool active, wxPoint const &translate) +{ + wxPoint coords; + coords = m_CursorScreenPos; + coords.x += translate.x; + coords.y += translate.y; + #ifdef WXLAYOUT_DEBUG - wxLayoutDebug("text: %s", tobj->GetText().c_str()); - WXL_VAR(offs); + 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 - String left = tobj->GetText().substr(0,offs); // get part before cursor - WXL_VAR(left.c_str()); - tobj->GetText() = tobj->GetText().substr(offs,(*i)->CountPositions()-offs); // keeps the right half - WXL_VAR(tobj->GetText().c_str()); - insert(i,obj); - insert(i,new wxLayoutObjectText(left)); // inserts before + + dc.SetBrush(*wxBLACK_BRUSH); + dc.SetLogicalFunction(wxXOR); + dc.SetPen(wxPen(*wxBLACK,1,wxSOLID)); + if(active) + { + dc.DrawRectangle(coords.x, coords.y, + m_CursorSize.x, m_CursorSize.y); + SetUpdateRect(coords.x, coords.y); + SetUpdateRect(coords.x+m_CursorSize.x, coords.y+m_CursorSize.y); } else { -// all other cases, append after object: - wxLayoutObjectList::iterator j = i; // we want to apend after this object - j++; - if(j != end()) - { - insert(j, obj); - m_CursorObject = j; - } - else - { - push_back(obj); - m_CursorObject = tail(); - } + dc.DrawLine(coords.x, coords.y+m_CursorSize.y-1, + coords.x, coords.y); + SetUpdateRect(coords.x, coords.y+m_CursorSize.y-1); + SetUpdateRect(coords.x, coords.y); } - m_CursorPos.x += obj->CountPositions(); - m_CursorOffset = obj->CountPositions(); - - if(obj->GetType() == WXLO_TYPE_LINEBREAK) - m_MaxLine++; - m_CursorMoved = true; + dc.SetLogicalFunction(wxCOPY); + //dc.SetBrush(wxNullBrush); } void -wxLayoutList::Insert(String const &text) +wxLayoutList::SetUpdateRect(CoordType x, CoordType y) { - wxLayoutObjectText *tobj = NULL; - wxLayoutObjectList::iterator j; - -// WXL_TRACE(Insert(text)); - - if(! m_Editable) - return; - - m_bModified = true; - - CoordType offs; - wxLayoutObjectList::iterator i = FindCurrentObject(&offs); - - if(i == end()) + if(m_UpdateRectValid) + GrowRect(m_UpdateRect, x, y); + else { - Insert(new wxLayoutObjectText(text)); - return; + m_UpdateRect.x = x; + m_UpdateRect.y = y; + m_UpdateRect.width = 4; // large enough to avoid surprises from + m_UpdateRect.height = 4;// wxGTK :-) + m_UpdateRectValid = true; } +} + +void +wxLayoutList::StartSelection(wxPoint cpos) +{ + if(cpos.x == -1) + cpos = m_CursorPos; + WXLO_DEBUG(("Starting selection at %ld/%ld", cpos.x, cpos.y)); + m_Selection.m_CursorA = cpos; + m_Selection.m_CursorB = cpos; + m_Selection.m_selecting = true; + m_Selection.m_valid = false; +} - switch((**i).GetType()) +void +wxLayoutList::ContinueSelection(wxPoint cpos) +{ + if(cpos.x == -1) + cpos = m_CursorPos; + wxASSERT(m_Selection.m_selecting == true); + wxASSERT(m_Selection.m_valid == false); + WXLO_DEBUG(("Continuing selection at %ld/%ld", cpos.x, cpos.y)); + if(m_Selection.m_CursorB <= cpos) + m_Selection.m_CursorB = cpos; + else + m_Selection.m_CursorA = cpos; + // We always want m_CursorA <= m_CursorB! + if(! (m_Selection.m_CursorA <= m_Selection.m_CursorB)) { - case WXLO_TYPE_TEXT: -// insert into an existing text object: - WXL_TRACE(inserting into existing object); - tobj = (wxLayoutObjectText *)*i ; - wxASSERT(tobj); - tobj->GetText().insert(offs,text); - m_CursorObject = i; - m_CursorOffset = offs + text.length(); - m_CursorPos.x += text.length(); - break; - case WXLO_TYPE_LINEBREAK: - default: - j = i; - if(offs == 0) // try to append to previous object - { - j--; - if(j != end() && (**j).GetType() == WXLO_TYPE_TEXT) - { - tobj = (wxLayoutObjectText *)*j; - tobj->GetText()+=text; - m_CursorObject = j; - m_CursorOffset = (**j).CountPositions(); - m_CursorPos.x += text.length(); - } - else - { - insert(i,new wxLayoutObjectText(text)); - m_CursorObject = i; - m_CursorOffset = (**i).CountPositions(); - m_CursorPos.x += m_CursorOffset; - } - } - else // offset == 1 : cursor after linebreak - { - j++; - m_CursorPos.x ++; - m_CursorObject = j; - m_CursorOffset = 0; - if(j != end() && (**j).GetType() == WXLO_TYPE_TEXT) - { - tobj = (wxLayoutObjectText *)*j; - tobj->GetText()=text+tobj->GetText(); - m_CursorOffset = text.length(); - m_CursorPos.x += m_CursorOffset; - } - else - { - if(j == end()) - { - push_back(new wxLayoutObjectText(text)); - m_CursorObject = tail(); - m_CursorOffset = (**m_CursorObject).CountPositions(); - m_CursorPos.x += text.length(); - } - else - { - insert(j,new wxLayoutObjectText(text)); - m_CursorObject = j; - m_CursorOffset = (**j).CountPositions(); - m_CursorPos.x += text.length(); - } - } - } - break; + wxPoint help = m_Selection.m_CursorB; + m_Selection.m_CursorB = m_Selection.m_CursorA; + m_Selection.m_CursorA = help; } - m_CursorMoved = true; } -CoordType -wxLayoutList::GetLineLength(wxLayoutObjectList::iterator i, CoordType offs) +void +wxLayoutList::EndSelection(wxPoint cpos) { - if(i == end()) - return 0; + 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; +} + + +bool +wxLayoutList::IsSelecting(void) +{ + return m_Selection.m_selecting; +} + +bool +wxLayoutList::IsSelected(const wxPoint &cursor) +{ + if(! m_Selection.m_valid && ! m_Selection.m_selecting) + return false; + return m_Selection.m_CursorA <= cursor + && cursor <= m_Selection.m_CursorB; +} + - CoordType len = 0; +/** Tests whether this layout line is selected and needs + highlighting. + @param line to test for + @return 0 = not selected, 1 = fully selected, -1 = partially + selected + */ +int +wxLayoutList::IsSelected(const wxLayoutLine *line, CoordType *from, + CoordType *to) +{ + wxASSERT(line); wxASSERT(to); wxASSERT(from); - if(offs == 0 && (**i).GetType() == WXLO_TYPE_LINEBREAK) -// we are before a linebrak + if(! m_Selection.m_valid && ! m_Selection.m_selecting) return 0; -// 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) + + CoordType y = line->GetLineNumber(); + if(m_Selection.m_CursorA.y < y && m_Selection.m_CursorB.y > y) + return 1; + else if(m_Selection.m_CursorA.y == y) { - len += (*i)->CountPositions(); - i++; + *from = m_Selection.m_CursorA.x; + if(m_Selection.m_CursorB.y == y) + *to = m_Selection.m_CursorB.x; + else + *to = line->GetLength(); + return -1; + } + else if(m_Selection.m_CursorB.y == y) + { + *to = m_Selection.m_CursorB.x; + if(m_Selection.m_CursorA.y == y) + *from = m_Selection.m_CursorA.x; + else + *from = 0; + return -1; } - return len; + else + return 0; } void -wxLayoutList::Clear(int family, int size, int style, int weight, - int underline, char const *fg, char const *bg) -{ - m_bModified = true; - m_CursorMoved = true; - m_dirty = true; // force redraw/recalc - wxLayoutObjectList::iterator i = begin(); - - wxBitmap bm(1,1); - m_CursorMemDC.SelectObject(bm); - - while(i != end()) // == while valid - erase(i); - - // 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); - - if(! m_ColourFG) m_ColourFG = wxBLACK; - if(! m_ColourBG) m_ColourBG = wxWHITE; - - m_Position = wxPoint(0,0); - m_CursorPos = wxPoint(0,0); - m_CursorObject = iterator(NULL); - m_CursorOffset = 0; +wxLayoutList::DeleteSelection(void) +{ + if(! m_Selection.m_valid) + return; + + m_Selection.m_valid = false; - m_MaxLine = 0; - m_LineHeight = (BASELINESTRETCH*m_FontPtSize)/10; - m_MaxX = 0; m_MaxY = 0; + // Only delete part of the current line? + if(m_Selection.m_CursorA.y == m_Selection.m_CursorB.y) + { + MoveCursorTo(m_Selection.m_CursorA); + Delete(m_Selection.m_CursorB.x - m_Selection.m_CursorA.x); + return; + } + + wxLayoutLine + * firstLine = NULL, + * lastLine = NULL; - m_FoundCursor = wxPoint(0,0); - m_FoundIterator = begin(); + for(firstLine = m_FirstLine; + firstLine && firstLine->GetLineNumber() < m_Selection.m_CursorA.y; + firstLine=firstLine->GetNextLine()) + ; + if(!firstLine || firstLine->GetLineNumber() != m_Selection.m_CursorA.y) + return; + + + for(lastLine = m_FirstLine; + lastLine && lastLine->GetLineNumber() < m_Selection.m_CursorB.y; + lastLine=lastLine->GetNextLine()) + ; + if(!lastLine || lastLine->GetLineNumber() != m_Selection.m_CursorB.y) + return; + + + // We now know that the two lines are different: + + // First, delete what's left of this line: + MoveCursorTo(m_Selection.m_CursorA); + DeleteToEndOfLine(); + + wxLayoutLine *nextLine = firstLine->GetNextLine(); + while(nextLine && nextLine != lastLine) + nextLine = nextLine->DeleteLine(false, this); + + // Now nextLine = lastLine; + Delete(1); // This joins firstLine and nextLine + Delete(m_Selection.m_CursorB.x); // This deletes the first x + // positions + + /// Recalculate: + firstLine->RecalculatePositions(1, this); +} - if(m_DefaultSetting) - delete m_DefaultSetting; +/// Starts highlighting the selection +void +wxLayoutList::StartHighlighting(wxDC &dc) +{ +#if SHOW_SELECTIONS + dc.SetTextForeground(m_CurrentSetting.m_bg); + dc.SetTextBackground(m_CurrentSetting.m_fg); + dc.SetBackgroundMode(wxSOLID); +#endif +} - m_DefaultSetting = new - wxLayoutObjectCmd(m_FontPtSize,m_FontFamily,m_FontStyle, - m_FontWeight,m_FontUnderline, - m_ColourFG, m_ColourBG); +/// Ends highlighting the selection +void +wxLayoutList::EndHighlighting(wxDC &dc) +{ +#if SHOW_SELECTIONS + dc.SetTextForeground(m_CurrentSetting.m_fg); + dc.SetTextBackground(m_CurrentSetting.m_bg); + dc.SetBackgroundMode(wxTRANSPARENT); +#endif } -wxLayoutObjectBase * -wxLayoutList::Find(wxPoint coords) const +wxLayoutList * +wxLayoutList::Copy(const wxPoint &from, + const wxPoint &to) +{ + wxLayoutLine + * firstLine = NULL, + * lastLine = NULL; + + for(firstLine = m_FirstLine; + firstLine && firstLine->GetLineNumber() < from.y; + firstLine=firstLine->GetNextLine()) + ; + if(!firstLine || firstLine->GetLineNumber() != from.y) + return NULL; + + for(lastLine = m_FirstLine; + lastLine && lastLine->GetLineNumber() < to.y; + lastLine=lastLine->GetNextLine()) + ; + if(!lastLine || lastLine->GetLineNumber() != to.y) + return NULL; + + if(to <= from) + { + wxLayoutLine *tmp = firstLine; + firstLine = lastLine; + lastLine = tmp; + } + + wxLayoutList *llist = new wxLayoutList(); + + if(firstLine == lastLine) + { + firstLine->Copy(llist, from.x, to.x); + } + else + { + // Extract objects from first line + firstLine->Copy(llist, from.x); + llist->LineBreak(); + // Extract all lines between + for(wxLayoutLine *line = firstLine->GetNextLine(); + line != lastLine; + line = line->GetNextLine()) + { + line->Copy(llist); + llist->LineBreak(); + } + // Extract objects from last line + lastLine->Copy(llist, 0, to.x); + } + return llist; +} + +wxLayoutList * +wxLayoutList::GetSelection(wxLayoutDataObject *wxlo, bool invalidate) { - wxLayoutObjectList::iterator i = begin(); + if(! m_Selection.m_valid) + { + if(m_Selection.m_selecting) + EndSelection(); + else + return NULL; + } + + if(invalidate) m_Selection.m_valid = false; - wxPoint topleft, bottomright; + wxLayoutList *llist = Copy( m_Selection.m_CursorA, + m_Selection.m_CursorB ); - while(i != end()) // == while valid + if(llist && wxlo) // export as data object, too { - wxLayoutObjectBase *object = *i; - topleft = object->GetPosition(); - if(coords.y >= topleft.y && coords.x >= topleft.x) + wxString string; + + wxLayoutExportObject *export; + wxLayoutExportStatus status(llist); + while((export = wxLayoutExport( &status, WXLO_EXPORT_AS_OBJECTS)) != NULL) { - bottomright = topleft; - bottomright.x += object->GetSize().x; - bottomright.y += object->GetSize().y; - if(coords.x <= bottomright.x && coords.y <= bottomright.y) - return *i; + if(export->type == WXLO_EXPORT_EMPTYLINE) + ; //FIXME missing support for linebreaks in string format + else + export->content.object->Write(string); + delete export; } - i++; + wxlo->SetData(string.c_str(), string.Length()+1); + } + return llist; +} + + + +#define COPY_SI(what) if(si->what != -1) { m_CurrentSetting.what = si->what; fontChanged = TRUE; } + +void +wxLayoutList::ApplyStyle(wxLayoutStyleInfo *si, wxDC &dc) +{ + bool fontChanged = FALSE; + COPY_SI(family); + COPY_SI(size); + COPY_SI(style); + COPY_SI(weight); + COPY_SI(underline); + if(fontChanged) + dc.SetFont( m_FontCache.GetFont(m_CurrentSetting) ); + + if(si->m_fg_valid) + { + m_CurrentSetting.m_fg = si->m_fg; + dc.SetTextForeground(m_CurrentSetting.m_fg); } - return NULL; + if(si->m_bg_valid) + { + m_CurrentSetting.m_bg = si->m_bg; + dc.SetTextBackground(m_CurrentSetting.m_bg); + } +} + + +#ifdef WXLAYOUT_DEBUG + +void +wxLayoutList::Debug(void) +{ + wxLayoutLine *line; + + + for(line = m_FirstLine; + line; + line = line->GetNextLine()) + line->Debug(); } +#endif + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -/******************** printing stuff ********************/ + wxLayoutPrintout -wxLayoutPrintout::wxLayoutPrintout(wxLayoutList &llist, + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +wxLayoutPrintout::wxLayoutPrintout(wxLayoutList *llist, wxString const & title) :wxPrintout(title) { - m_llist = &llist; + m_llist = llist; m_title = title; + // remove any highlighting which could interfere with printing: + m_llist->StartSelection(); + m_llist->EndSelection(); +} + +wxLayoutPrintout::~wxLayoutPrintout() +{ +} + +float +wxLayoutPrintout::ScaleDC(wxDC *dc) +{ + // The following bit is taken from the printing sample, let's see + // whether it works for us. + + /* You might use THIS code to set the printer DC to ROUGHLY reflect + * the screen text size. This page also draws lines of actual length 5cm + * on the page. + */ + // Get the logical pixels per inch of screen and printer + int ppiScreenX, ppiScreenY; + GetPPIScreen(&ppiScreenX, &ppiScreenY); + int ppiPrinterX, ppiPrinterY; + GetPPIPrinter(&ppiPrinterX, &ppiPrinterY); + + if(ppiScreenX == 0) // not yet set, need to guess + { + ppiScreenX = 100; + ppiScreenY = 100; + } + if(ppiPrinterX == 0) // not yet set, need to guess + { + ppiPrinterX = 72; + ppiPrinterY = 72; + } + + // This scales the DC so that the printout roughly represents the + // the screen scaling. The text point size _should_ be the right size + // but in fact is too small for some reason. This is a detail that will + // need to be addressed at some point but can be fudged for the + // moment. + float scale = (float)((float)ppiPrinterX/(float)ppiScreenX); + + // Now we have to check in case our real page size is reduced + // (e.g. because we're drawing to a print preview memory DC) + int pageWidth, pageHeight; + int w, h; + dc->GetSize(&w, &h); + GetPageSizePixels(&pageWidth, &pageHeight); + if(pageWidth != 0) // doesn't work always + { + // If printer pageWidth == current DC width, then this doesn't + // change. But w might be the preview bitmap width, so scale down. + scale = scale * (float)(w/(float)pageWidth); + } + dc->SetUserScale(scale, scale); + return scale; } bool wxLayoutPrintout::OnPrintPage(int page) { wxDC *dc = GetDC(); + + ScaleDC(dc); + if (dc) { - DrawHeader(*dc,wxPoint(m_Margins.left,m_Margins.top/2),wxPoint(m_Margins.right,m_Margins.top),page); int top, bottom; top = (page - 1)*m_PrintoutHeight; bottom = top + m_PrintoutHeight; // SetDeviceOrigin() doesn't work here, so we need to manually // translate all coordinates. - wxPoint translate(m_Margins.left,-top+m_Margins.top); - m_llist->Draw(*dc,top,bottom,wxLayoutObjectList::iterator(NULL),translate); + wxPoint translate(m_Offset.x,m_Offset.y-top); + m_llist->Draw(*dc, translate, top, bottom); return true; } else return false; } -bool wxLayoutPrintout::OnBeginDocument(int startPage, int endPage) -{ - if (!wxPrintout::OnBeginDocument(startPage, endPage)) - return false; - - return true; -} - -void -wxLayoutPrintout::OnPreparePrinting(void) -{ - -} - - void wxLayoutPrintout::GetPageInfo(int *minPage, int *maxPage, int *selPageFrom, int *selPageTo) { - // ugly hack to get number of pages + /* We allocate a temporary wxDC for printing, so that we can + determine the correct paper size and scaling. We don't actually + print anything on it. */ #ifdef __WXMSW__ - wxPrinterDC psdc("", "", WXLLIST_TEMPFILE,false); + wxPrinterDC psdc("","",WXLLIST_TEMPFILE,false); #else wxPostScriptDC psdc(WXLLIST_TEMPFILE,false); #endif - psdc.GetSize(&m_PageWidth, &m_PageHeight); // that's all we need it for - // We do 5% margins on top and bottom, and a 5% high header line. - m_Margins.top = m_PageHeight / 10 ; // 10%, half of it header - m_Margins.bottom = m_PageHeight - m_PageHeight / 20; // 95% - // On the sides we reserve 10% each for the margins. - m_Margins.left = m_PageWidth / 10; - m_Margins.right = m_PageWidth - m_PageWidth / 10; + float scale = ScaleDC(&psdc); - // This is the length of the printable area. - m_PrintoutHeight = m_PageHeight - (int) (m_PageHeight * 0.15); + psdc.GetSize(&m_PageWidth, &m_PageHeight); + // This sets a left/top origin of 15% and 20%: + m_Offset = wxPoint((15*m_PageWidth)/100, m_PageHeight/20); - //FIXME this is wrong but not used at the moment - m_PageWidth = m_Margins.right - m_Margins.left; + // This is the length of the printable area. + m_PrintoutHeight = m_PageHeight - (int) (m_PageHeight * 0.15); + m_PrintoutHeight = (int)( m_PrintoutHeight / scale); // we want to use the real paper height + + + m_NumOfPages = 1 + + (int)( m_llist->GetSize().y / (float)(m_PrintoutHeight)); - m_NumOfPages = (int)( m_llist->GetSize().y / (float)(m_PrintoutHeight) + 0.5); *minPage = 1; *maxPage = m_NumOfPages; @@ -1374,20 +2351,23 @@ void wxLayoutPrintout::GetPageInfo(int *minPage, int *maxPage, int *selPageFrom, bool wxLayoutPrintout::HasPage(int pageNum) { - return pageNum < m_NumOfPages; + return pageNum <= m_NumOfPages; } - +/* + Stupid wxWindows doesn't draw proper ellipses, so we comment this + out. It's a waste of paper anyway. +*/ +#if 0 void wxLayoutPrintout::DrawHeader(wxDC &dc, wxPoint topleft, wxPoint bottomright, int pageno) { // make backups of all essential parameters - wxBrush *brush = dc.GetBrush(); - wxPen *pen = dc.GetPen(); - wxFont *font = dc.GetFont(), - *myfont;; + const wxBrush& brush = dc.GetBrush(); + const wxPen& pen = dc.GetPen(); + const wxFont& font = dc.GetFont(); dc.SetBrush(*wxWHITE_BRUSH); dc.SetPen(wxPen(*wxBLACK,0,wxSOLID)); @@ -1395,8 +2375,9 @@ wxLayoutPrintout::DrawHeader(wxDC &dc, topleft.y,bottomright.x-topleft.x, bottomright.y-topleft.y); dc.SetBrush(*wxBLACK_BRUSH); - myfont = new wxFont((WXLO_DEFAULTFONTSIZE*12)/10,wxSWISS,wxNORMAL,wxBOLD,false,"Helvetica"); - dc.SetFont(*myfont); + wxFont myfont = wxFont((WXLO_DEFAULTFONTSIZE*12)/10, + wxSWISS,wxNORMAL,wxBOLD,false,"Helvetica"); + dc.SetFont(myfont); wxString page; page = "9999/9999 "; // many pages... @@ -1408,16 +2389,25 @@ wxLayoutPrintout::DrawHeader(wxDC &dc, dc.DrawText(m_title, topleft.x+w,topleft.y+h/2); // restore settings - dc.SetPen(*pen); - dc.SetBrush(*brush); - dc.SetFont(*font); - - delete myfont; + dc.SetPen(pen); + dc.SetBrush(brush); + dc.SetFont(font); } +#endif -wxLayoutPrintout * -wxLayoutList::MakePrintout(wxString const &name) +wxFont & +wxFontCache::GetFont(int family, int size, int style, int weight, + bool underline) { - return new wxLayoutPrintout(*this,name); + for(wxFCEList::iterator i = m_FontList.begin(); + i != m_FontList.end(); i++) + if( (**i).Matches(family, size, style, weight, underline) ) + return (**i).GetFont(); + // not found: + wxFontCacheEntry *fce = new wxFontCacheEntry(family, size, style, + weight, underline); + m_FontList.push_back(fce); + return fce->GetFont(); } +