X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/6c11b5265004937450f8e08d55b8a1476e1452dd..a2fa5040877d70eb01a023db317dda875633aca4:/samples/richedit/wxllist.cpp diff --git a/samples/richedit/wxllist.cpp b/samples/richedit/wxllist.cpp index f28ef6a871..3a2ff3a604 100644 --- a/samples/richedit/wxllist.cpp +++ b/samples/richedit/wxllist.cpp @@ -1,7 +1,7 @@ /*-*- c++ -*-******************************************************** * wxllist: wxLayoutList, a layout engine for text and graphics * * * - * (C) 1998-1999 by Karsten Ballüder (Ballueder@usa.net) * + * (C) 1998-2000 by Karsten Ballüder (Ballueder@gmx.net) * * * * $Id$ *******************************************************************/ @@ -25,7 +25,7 @@ # pragma implementation "wxllist.h" #endif -#include +#include "wx/wxprec.h" #ifdef __BORLANDC__ # pragma hdrstop @@ -34,6 +34,7 @@ #include "Mpch.h" #ifdef M_BASEDIR +# include "Mcommon.h" # include "gui/wxllist.h" # include "gui/wxlparser.h" # define SHOW_SELECTIONS 1 @@ -44,7 +45,11 @@ #endif #ifndef USE_PCH -# include +#if wxUSE_IOSTREAMH + #include +#else + #include +#endif # include # include @@ -59,6 +64,7 @@ #include + /// This should never really get created #define WXLLIST_TEMPFILE "__wxllist.tmp" @@ -71,16 +77,19 @@ { "invalid", "text", "cmd", "icon" }; - void - wxLayoutObject::Debug(void) + wxString + wxLayoutObject::DebugDump(void) const { - WXLO_DEBUG(("%s",g_aTypeStrings[GetType()])); + wxString str; + str.Printf(wxT("%s"), g_aTypeStrings[GetType()]); + return str; } #else -# define TypeString(t) "" -# define WXLO_DEBUG(x) +# define TypeString(t) "" +# define WXLO_DEBUG(x) #endif + // FIXME under MSW, this constant is needed to make the thing properly redraw // itself - I don't know where the size calculation error is and I can't // waste time looking for it right now. Search for occurences of @@ -164,14 +173,20 @@ bool Contains(const wxRect &r, const wxPoint &p) //@} +static void ReadString(wxString &to, wxString &from) { - to = ""; - const char *cptr = from.c_str(); - while(*cptr && *cptr != '\n') - to += *cptr++; - if(*cptr) cptr++; - from = cptr; + to = wxT(""); + const wxChar *cptr = from.c_str(); + while(*cptr && *cptr != wxT('\n')) + { + to += cptr; + cptr++; + } + + if(*cptr) cptr++; + + from = cptr; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * @@ -184,22 +199,23 @@ void ReadString(wxString &to, wxString &from) wxLayoutObject * wxLayoutObject::Read(wxString &istr) { - wxString tmp; - ReadString(tmp, istr); - int type = -1; - sscanf(tmp.c_str(),"%d", &type); - - switch(type) - { - case WXLO_TYPE_TEXT: - return wxLayoutObjectText::Read(istr); - case WXLO_TYPE_CMD: - return wxLayoutObjectCmd::Read(istr); - case WXLO_TYPE_ICON: - return wxLayoutObjectIcon::Read(istr); - default: - return NULL; - } + wxString tmp; + ReadString(tmp, istr); + long l = WXLO_TYPE_INVALID; + tmp.ToLong(&l); + int type = (int) l; + + 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; + } } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * @@ -328,6 +344,16 @@ wxLayoutObjectText::Layout(wxDC &dc, class wxLayoutList *llist) heightOld = m_Height; #endif // 0 +#ifdef __WXDEBUG__ + CoordType a,b,c,d,e,f; + dc.GetTextExtent("test ", &a, &b, &c); + dc.GetTextExtent("test", &d, &e, &f); + wxASSERT(a != d); + wxASSERT(b == e); + wxASSERT(c == f); + dc.GetTextExtent(" ", &d, &e, &f); + wxASSERT(a > 0); +#endif dc.GetTextExtent(m_Text, &m_Width, &m_Height, &descent); #if 0 @@ -359,11 +385,14 @@ wxLayoutObjectText::Layout(wxDC &dc, class wxLayoutList *llist) #ifdef WXLAYOUT_DEBUG -void -wxLayoutObjectText::Debug(void) +wxString +wxLayoutObjectText::DebugDump(void) const { - wxLayoutObject::Debug(); - WXLO_DEBUG((" `%s`", m_Text.c_str())); + wxString str; + str = wxLayoutObject::DebugDump(); + wxString str2; + str2.Printf(wxT(" `%s`"), m_Text.c_str()); + return str+str2; } #endif @@ -375,7 +404,22 @@ wxLayoutObjectText::Debug(void) wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap const &icon) { + if ( !icon.Ok() ) + { + wxFAIL_MSG(wxT("invalid icon")); + + m_Icon = NULL; + + return; + } + +#ifdef __WXMSW__ + // FIXME ugly, ugly, ugly - but the only way to avoid slicing + m_Icon = icon.GetHBITMAP() ? new wxBitmap(icon) + : new wxBitmap(wxBitmap((const wxBitmap &)icon)); +#else // !MSW m_Icon = new wxBitmap(icon); +#endif // MSW/!MSW } @@ -386,7 +430,7 @@ wxLayoutObjectIcon::Write(wxString &ostr) wxString file = wxGetTempFileName("wxloexport"); - ostr << WXLO_TYPE_ICON << '\n' + ostr << (int) WXLO_TYPE_ICON << '\n' << file << '\n'; m_Icon->SaveFile(file, WXLO_BITMAP_FORMAT); } @@ -422,6 +466,8 @@ wxLayoutObjectIcon::Copy(void) wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap *icon) { m_Icon = icon; + if(! m_Icon) + m_Icon = new wxBitmap; } void @@ -463,23 +509,16 @@ wxLayoutStyleInfo::wxLayoutStyleInfo(int ifamily, wxColour *fg, wxColour *bg) { - family = ifamily; size = isize; - style = istyle; weight = iweight; + family = ifamily; + size = isize; + style = istyle; + weight = iweight; underline = iul != 0; - if(fg) - { - m_fg = *fg; - m_fg_valid = TRUE; - } - else - m_fg = *wxBLACK; - if(bg) - { - m_bg = *bg; - m_bg_valid = TRUE; - } - else - m_bg = *wxWHITE; + + m_fg_valid = fg != 0; + m_bg_valid = bg != 0; + m_fg = m_fg_valid ? *fg : *wxBLACK; + m_bg = m_bg_valid ? *bg : *wxWHITE; } #define COPY_SI_(what) if(right.what != -1) what = right.what; @@ -505,12 +544,19 @@ wxLayoutObjectCmd::wxLayoutObjectCmd(int family, int size, int style, int m_StyleInfo = new wxLayoutStyleInfo(family, size,style,weight,underline,fg,bg); } +wxLayoutObjectCmd::wxLayoutObjectCmd(const wxLayoutStyleInfo &si) + +{ + m_StyleInfo = new wxLayoutStyleInfo; + *m_StyleInfo = si; +} + wxLayoutObject * wxLayoutObjectCmd::Copy(void) { wxLayoutObjectCmd *obj = new wxLayoutObjectCmd( - m_StyleInfo->size, m_StyleInfo->family, + m_StyleInfo->size, m_StyleInfo->style, m_StyleInfo->weight, m_StyleInfo->underline, @@ -525,71 +571,101 @@ wxLayoutObjectCmd::Copy(void) void wxLayoutObjectCmd::Write(wxString &ostr) { - ostr << WXLO_TYPE_CMD << '\n' - << m_StyleInfo->size << '\n' - << m_StyleInfo->family << '\n' - << m_StyleInfo->style << '\n' - << m_StyleInfo->weight << '\n' - << m_StyleInfo->underline << '\n' - << m_StyleInfo->m_fg_valid << '\n' - << m_StyleInfo->m_bg_valid << '\n'; + ostr << (int) WXLO_TYPE_CMD << '\n' + << (int) m_StyleInfo->family << '\n' + << (int) m_StyleInfo->size << '\n' + << (int) m_StyleInfo->style << '\n' + << (int) m_StyleInfo->weight << '\n' + << (int) m_StyleInfo->underline << '\n' + << (int) m_StyleInfo->m_fg_valid << '\n' + << (int) m_StyleInfo->m_bg_valid << '\n'; if(m_StyleInfo->m_fg_valid) { - ostr << m_StyleInfo->m_fg.Red() << '\n' - << m_StyleInfo->m_fg.Green() << '\n' - << m_StyleInfo->m_fg.Blue() << '\n'; + ostr << (int) m_StyleInfo->m_fg.Red() << '\n' + << (int) m_StyleInfo->m_fg.Green() << '\n' + << (int) m_StyleInfo->m_fg.Blue() << '\n'; } if(m_StyleInfo->m_bg_valid) { - ostr << m_StyleInfo->m_bg.Red() << '\n' - << m_StyleInfo->m_bg.Green() << '\n' - << m_StyleInfo->m_bg.Blue() << '\n'; + ostr << (int) m_StyleInfo->m_bg.Red() << '\n' + << (int) m_StyleInfo->m_bg.Green() << '\n' + << (int) m_StyleInfo->m_bg.Blue() << '\n'; } } /* static */ wxLayoutObjectCmd * wxLayoutObjectCmd::Read(wxString &istr) { - wxLayoutObjectCmd *obj = new wxLayoutObjectCmd; - - wxString tmp; - ReadString(tmp, istr); - sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->size); - ReadString(tmp, istr); - sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->family); - ReadString(tmp, istr); - sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->style); - ReadString(tmp, istr); - sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->weight); - ReadString(tmp, istr); - sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->underline); - ReadString(tmp, istr); - sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->m_fg_valid); - ReadString(tmp, istr); - sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->m_bg_valid); - if(obj->m_StyleInfo->m_fg_valid) - { - int red, green, blue; - ReadString(tmp, istr); - sscanf(tmp.c_str(),"%d", &red); - ReadString(tmp, istr); - sscanf(tmp.c_str(),"%d", &green); - ReadString(tmp, istr); - sscanf(tmp.c_str(),"%d", &blue); - obj->m_StyleInfo->m_fg = wxColour(red, green, blue); - } - if(obj->m_StyleInfo->m_bg_valid) - { - int red, green, blue; - ReadString(tmp, istr); - sscanf(tmp.c_str(),"%d", &red); - ReadString(tmp, istr); - sscanf(tmp.c_str(),"%d", &green); - ReadString(tmp, istr); - sscanf(tmp.c_str(),"%d", &blue); - obj->m_StyleInfo->m_bg = wxColour(red, green, blue); - } - return obj; + wxLayoutObjectCmd *obj = new wxLayoutObjectCmd; + + long l = 0; + wxString tmp; + ReadString(tmp, istr); + tmp.ToLong(&l); + obj->m_StyleInfo->family = (int) l; + + + ReadString(tmp, istr); + tmp.ToLong(&l); + obj->m_StyleInfo->size = (int) l; + + ReadString(tmp, istr); + tmp.ToLong(&l); + obj->m_StyleInfo->style = (int) l; + + ReadString(tmp, istr); + tmp.ToLong(&l); + obj->m_StyleInfo->weight = (int) l; + + ReadString(tmp, istr); + tmp.ToLong(&l); + obj->m_StyleInfo->underline = (int) l; + + ReadString(tmp, istr); + tmp.ToLong(&l); + obj->m_StyleInfo->m_fg_valid = (int) l; + + ReadString(tmp, istr); + tmp.ToLong(&l); + obj->m_StyleInfo->m_bg_valid = (int) l; + + if(obj->m_StyleInfo->m_fg_valid) + { + int red, green, blue; + ReadString(tmp, istr); + tmp.ToLong(&l); + red = (int) l; + + ReadString(tmp, istr); + tmp.ToLong(&l); + green = (int) l; + + ReadString(tmp, istr); + tmp.ToLong(&l); + blue = (int) l; + + obj->m_StyleInfo->m_fg = wxColour(red, green, blue); + } + + if(obj->m_StyleInfo->m_bg_valid) + { + int red, green, blue; + ReadString(tmp, istr); + tmp.ToLong(&l); + red = (int) l; + + ReadString(tmp, istr); + tmp.ToLong(&l); + green = (int) l; + + ReadString(tmp, istr); + tmp.ToLong(&l); + blue = (int) l; + + obj->m_StyleInfo->m_bg = wxColour(red, green, blue); + } + + return obj; } @@ -629,25 +705,34 @@ wxLayoutObjectCmd::Layout(wxDC &dc, class wxLayoutList * llist) wxLayoutLine::wxLayoutLine(wxLayoutLine *prev, wxLayoutList *llist) { - m_LineNumber = 0; m_Width = m_Height = 0; m_Length = 0; - MarkDirty(0); + + m_updateLeft = -1; m_Previous = prev; m_Next = NULL; + MarkDirty(0); + + m_LineNumber = 0; RecalculatePosition(llist); + + MarkDirty(); if(m_Previous) { - m_LineNumber = m_Previous->GetLineNumber()+1; + m_LineNumber = m_Previous->GetLineNumber() + 1; m_Next = m_Previous->GetNextLine(); m_Previous->m_Next = this; } + if(m_Next) { m_Next->m_Previous = this; - m_Next->MoveLines(+1); - m_Next->RecalculatePositions(1,llist); + m_Next->ReNumber(); } + + m_StyleInfo = llist->GetDefaultStyleInfo(); + + llist->IncNumLines(); } wxLayoutLine::~wxLayoutLine() @@ -684,28 +769,6 @@ wxLayoutLine::RecalculatePosition(wxLayoutList *llist) return m_Position; } -void -wxLayoutLine::RecalculatePositions(int recurse, wxLayoutList *llist) -{ - //FIXME: is this really needed? We run Layout() anyway. - // Recursing here, drives computation time up exponentially, as - // each line will cause all following lines to be recalculated. - // Yes, or linenumbers go wrong. - - wxASSERT(recurse >= 0); - wxPoint pos = m_Position; - CoordType height = m_Height; - -// WXLO_TRACE("RecalculatePositions()"); - RecalculatePosition(llist); - if(m_Next) - { - if(recurse > 0) - m_Next->RecalculatePositions(--recurse, llist); - else if(pos != m_Position || m_Height != height) - m_Next->RecalculatePositions(0, llist); - } -} wxLayoutObjectList::iterator wxLayoutLine::FindObject(CoordType xpos, CoordType *offset) const @@ -738,30 +801,45 @@ wxLayoutLine::FindObject(CoordType xpos, CoordType *offset) const } wxLayoutObjectList::iterator -wxLayoutLine::FindObjectScreen(wxDC &dc, +wxLayoutLine::FindObjectScreen(wxDC &dc, wxLayoutList *llist, CoordType xpos, CoordType *cxpos, bool *found) const { wxASSERT(cxpos); - wxASSERT(cxpos); + + llist->ApplyStyle(GetStyleInfo(), dc); + wxLayoutObjectList::iterator i; CoordType x = 0, cx = 0, width; for(i = m_ObjectList.begin(); i != NULLIT; i++) { - width = (**i).GetWidth(); + wxLayoutObject *obj = *i; + if ( obj->GetType() == WXLO_TYPE_CMD ) + { + // this will set the correct font for the objects which follow + obj->Layout(dc, llist); + } + + width = obj->GetWidth(); if( x <= xpos && xpos <= x + width ) { - *cxpos = cx + (**i).GetOffsetScreen(dc, xpos-x); - if(found) *found = true; + *cxpos = cx + obj->GetOffsetScreen(dc, xpos-x); + + if ( found ) + *found = true; return i; } - x += (**i).GetWidth(); - cx += (**i).GetLength(); + + x += obj->GetWidth(); + cx += obj->GetLength(); } + // behind last object: *cxpos = cx; - if(found) *found = false; + + if (found) + *found = false; return m_ObjectList.tail(); } @@ -805,11 +883,6 @@ wxLayoutLine::Insert(CoordType xpos, wxLayoutObject *obj) MarkDirty(xpos); - // If we insert a command object, we need to recalculate all lines - // to update their styleinfo structure. - if(obj->GetType() == WXLO_TYPE_CMD) - MarkNextDirty(-1); - CoordType offset; wxLOiterator i = FindObject(xpos, &offset); if(i == NULLIT) @@ -905,10 +978,6 @@ wxLayoutLine::Delete(CoordType xpos, CoordType npos) len = (**i).GetLength(); m_Length -= len; npos -= len; - // If we delete a command object, we need to recalculate all lines - // to update their styleinfo structure. - if((**i).GetType() == WXLO_TYPE_CMD) - MarkNextDirty(-1); m_ObjectList.erase(i); } else @@ -945,86 +1014,78 @@ wxLayoutLine::Delete(CoordType xpos, CoordType npos) return npos; } -void -wxLayoutLine::MarkNextDirty(int recurse) -{ - wxLayoutLine *line = GetNextLine(); - while(line && (recurse == -1 || recurse >= 0)) - { - line->MarkDirty(); - line = line->GetNextLine(); - if(recurse > 0) recurse --; - } -} - bool wxLayoutLine::DeleteWord(CoordType xpos) { - wxASSERT(xpos >= 0); - CoordType offset; - MarkDirty(xpos); + wxASSERT(xpos >= 0); + CoordType offset; + MarkDirty(xpos); + + wxLOiterator i = FindObject(xpos, &offset); + + for(;;) + { + if(i == NULLIT) return false; + if((**i).GetType() != WXLO_TYPE_TEXT) + { + // This should only happen when at end of line, behind a non-text + // object: + if(offset == (**i).GetLength()) return false; + m_Length -= (**i).GetLength(); // -1 + m_ObjectList.erase(i); + return true; // we are done + } + else + { // text object: + if(offset == (**i).GetLength()) // at end of object + { + i++; offset = 0; + continue; + } - wxLOiterator i = FindObject(xpos, &offset); + 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; - for(;;) - { - if(i == NULLIT) return false; - if((**i).GetType() != WXLO_TYPE_TEXT) - { - // This should only happen when at end of line, behind a non-text - // object: - if(offset == (**i).GetLength()) return false; - m_Length -= (**i).GetLength(); // -1 - m_ObjectList.erase(i); - return true; // we are done - } - else - { // text object: - if(offset == (**i).GetLength()) // at end of object - { - i++; offset = 0; - continue; - } - wxLayoutObjectText *tobj = (wxLayoutObjectText *)*i; - size_t count = 0; - wxString str = tobj->GetText(); - str = str.substr(offset,str.Length()-offset); - // Find out how many positions we need to delete: - // 1. eat leading space - while(isspace(str.c_str()[count])) count++; - // 2. eat the word itself: - while(isalnum(str.c_str()[count])) count++; - // now delete it: - wxASSERT(count+offset <= (size_t) (**i).GetLength()); - ((wxLayoutObjectText *)*i)->GetText().erase(offset,count); - m_Length -= count; - return true; - } - } + return true; + } + } - wxFAIL_MSG("unreachable"); + wxFAIL_MSG(wxT("unreachable")); } wxLayoutLine * wxLayoutLine::DeleteLine(bool update, wxLayoutList *llist) { - if(m_Next) m_Next->m_Previous = m_Previous; - if(m_Previous) m_Previous->m_Next = m_Next; - if(update) - { - m_Next->MoveLines(-1); - m_Next->RecalculatePositions(1, llist); - /* We assume that if we have more than one object in the list, - this means that we have a command object, so we need to - update the following lines. */ - if(m_ObjectList.size() > 1 || - ( m_ObjectList.begin() != NULLIT && - (**m_ObjectList.begin()).GetType() == WXLO_TYPE_CMD) - ) - MarkNextDirty(-1); - } + // maintain linked list integrity + if(m_Next) + m_Next->m_Previous = m_Previous; + if(m_Previous) + m_Previous->m_Next = m_Next; + + // get the line numbers right again + if ( update && m_Next) + m_Next->ReNumber(); + + MarkDirty(); + + // we can't use m_Next after "delete this", so we must save this pointer + // first wxLayoutLine *next = m_Next; delete this; + + llist->DecNumLines(); + return next; } @@ -1075,6 +1136,7 @@ wxLayoutLine::Layout(wxDC &dc, wxLayoutList *llist, wxPoint *cursorPos, wxPoint *cursorSize, + wxLayoutStyleInfo *cursorStyle, int cx, bool suppressSIupdate) { @@ -1104,6 +1166,8 @@ wxLayoutLine::Layout(wxDC &dc, bool cursorFound = false; + RecalculatePosition(llist); + if(cursorPos) { *cursorPos = m_Position; @@ -1137,19 +1201,28 @@ wxLayoutLine::Layout(wxDC &dc, else str = WXLO_CURSORCHAR; dc.GetTextExtent(str, &width, &height, &descent); - wxASSERT(cursorSize); - // Just in case some joker inserted an empty string object: - if(width == 0) width = WXLO_MINIMUM_CURSOR_WIDTH; - if(height == 0) height = sizeObj.y; - cursorSize->x = width; - cursorSize->y = height; + + if(cursorStyle) // set style info + *cursorStyle = llist->GetStyleInfo(); + if ( cursorSize ) + { + // Just in case some joker inserted an empty string object: + if(width == 0) + width = WXLO_MINIMUM_CURSOR_WIDTH; + if(height == 0) + height = sizeObj.y; + cursorSize->x = width; + cursorSize->y = height; + } + cursorFound = true; // no more checks } else { // on some other object CoordType top, bottom; // unused - *cursorSize = obj->GetSize(&top,&bottom); + if(cursorSize) + *cursorSize = obj->GetSize(&top,&bottom); cursorPos->y = m_Position.y; cursorFound = true; // no more checks } @@ -1207,16 +1280,15 @@ wxLayoutLine::Layout(wxDC &dc, // tell next line about coordinate change if(m_Next && m_Height != heightOld) { - // FIXME isn't this done in RecalculatePositions() below anyhow? - m_Next->RecalculatePositions(0, llist); + m_Next->MarkDirty(); } - + // We need to check whether we found a valid cursor size: - if(cursorPos) + if(cursorPos && cursorSize) { // this might be the case if the cursor is at the end of the // line or on a command object: - if(cursorSize->y < WXLO_MINIMUM_CURSOR_WIDTH) + if(cursorSize->x < WXLO_MINIMUM_CURSOR_WIDTH) { CoordType width, height, descent; dc.GetTextExtent(WXLO_CURSORCHAR, &width, &height, &descent); @@ -1226,7 +1298,6 @@ wxLayoutLine::Layout(wxDC &dc, if(m_BaseLine >= cursorSize->y) // the normal case anyway cursorPos->y += m_BaseLine-cursorSize->y; } - RecalculatePositions(1, llist); MarkClean(); } @@ -1238,24 +1309,6 @@ wxLayoutLine::Break(CoordType xpos, wxLayoutList *llist) MarkDirty(xpos); - /* If we are at the begin of a line, we want to move all other - lines down and stay with the cursor where we are. However, if we - are in an empty line, we want to move down with it. */ - if(xpos == 0 && GetLength() > 0) - { // insert an empty line before this one - wxLayoutLine *prev = new wxLayoutLine(m_Previous, llist); - if(m_Previous == NULL) - { // We were in first line, need to link in new empty line - // before this. - prev->m_Next = this; - m_Previous = prev; - m_Previous->m_Height = 0; // this is a wild guess - } - if(m_Next) - m_Next->RecalculatePositions(1, llist); - return m_Previous; - } - CoordType offset; wxLOiterator i = FindObject(xpos, &offset); if(i == NULLIT) @@ -1265,7 +1318,9 @@ wxLayoutLine::Break(CoordType xpos, wxLayoutList *llist) wxLayoutLine *newLine = new wxLayoutLine(this, llist); // split object at i: - if((**i).GetType() == WXLO_TYPE_TEXT && offset != 0) + if((**i).GetType() == WXLO_TYPE_TEXT + && offset != 0 + && offset != (**i).GetLength() ) { wxString left, right; wxLayoutObjectText *tobj = (wxLayoutObjectText *) *i; @@ -1292,15 +1347,151 @@ wxLayoutLine::Break(CoordType xpos, wxLayoutList *llist) m_ObjectList.remove(i); // remove without deleting it } if(m_Next) - m_Next->RecalculatePositions(2, llist); + m_Next->MarkDirty(); return newLine; } +bool +wxLayoutLine::Wrap(CoordType wrapmargin, wxLayoutList *llist) +{ + if(GetLength() < wrapmargin) + return FALSE; // nothing to do + + // find the object which covers the wrapmargin: + CoordType offset; + wxLOiterator i = FindObject(wrapmargin, &offset); + wxCHECK_MSG( i != NULLIT, FALSE, + wxT("Cannot find object covering wrapmargin.")); + + // from this object on, the rest of the line must be copied to the + // next one: + wxLOiterator copyObject = NULLIT; + // if we split a text-object, we must pre-pend some text to the + // next line later on, remember it here: + wxString prependText = ""; + // we might need to adjust the cursor position later, so remember it + size_t xpos = llist->GetCursorPos().x; + // by how much did we shorten the current line: + size_t shorter = 0; + // remember cursor location of object + size_t objectCursorPos = 0; + + size_t breakpos = offset; + + if( (**i).GetType() != WXLO_TYPE_TEXT ) + { + // break before a non-text object + copyObject = i; + } + else + { + bool foundSpace = FALSE; + do + { +// while(i != NULLIT && (**i).GetType() != WXLO_TYPE_TEXT) +// i--; + // try to find a suitable place to split the object: + wxLayoutObjectText *tobj = (wxLayoutObjectText *)*i; + if((**i).GetType() == WXLO_TYPE_TEXT + && tobj->GetText().Length() >= breakpos) + { + do + { + foundSpace = isspace(tobj->GetText()[breakpos]) != 0; + if ( foundSpace ) + break; + } + while ( breakpos-- > 0 ); + } + else + { + breakpos = 0; + } + + if(! foundSpace) // breakpos == 0! + { + if(i == m_ObjectList.begin()) + return FALSE; // could not break line + else + { + i--; + while(i != m_ObjectList.begin() + && (**i).GetType() != WXLO_TYPE_TEXT ) + { + i--; + } + breakpos = (**i).GetLength(); + } + } + }while(! foundSpace); + // before we actually break the object, we need to know at which + // cursorposition it starts, so we can restore the cursor if needed: + if( this == llist->GetCursorLine() && xpos >= breakpos ) + { + for(wxLOiterator j = m_ObjectList.begin(); + j != NULLIT && j != i; j++) + objectCursorPos += (**j).GetLength(); + } + // now we know where to break it: + wxLayoutObjectText *tobj = (wxLayoutObjectText *)*i; + shorter = tobj->GetLength() - breakpos; + // remember text to copy from this object + prependText = tobj->GetText().Mid(breakpos+1); + tobj->SetText(tobj->GetText().Left(breakpos)); + // copy every following object: + copyObject = i; copyObject ++; + } + + // make sure there is an empty m_Next line: + (void) new wxLayoutLine(this, llist); + wxASSERT(m_Next); + // We need to move this and all following objects to the next + // line. Starting from the end of line, to keep the order right. + if(copyObject != NULLIT) + { + wxLOiterator j; + for(j = m_ObjectList.tail(); j != copyObject; j--) + m_Next->Prepend(*j); + m_Next->Prepend(*copyObject); + // and now remove them from this list: + while( copyObject != m_ObjectList.end() ) + { + shorter += (**copyObject).GetLength(); + m_ObjectList.remove(copyObject); // remove without deleting it + } + } + m_Length -= shorter; + + if(prependText.Length() > 0) + m_Next->Insert(0, prependText); + + // do we need to adjust the cursor position? + if( this == llist->GetCursorLine() && xpos >= breakpos) + { + xpos = objectCursorPos + (xpos - objectCursorPos - breakpos - + ((xpos > breakpos) ? 1 : 0 )); + wxASSERT(xpos >= 0); + llist->MoveCursorTo( wxPoint( xpos, m_Next->GetLineNumber()) ); + } + return TRUE; // we wrapped the line +} + +void +wxLayoutLine::ReNumber(void) +{ + CoordType lineNo = m_Previous ? m_Previous->m_LineNumber+1 : 0; + m_LineNumber = lineNo++; + + for(wxLayoutLine *next = GetNextLine(); + next; next = next->GetNextLine()) + next->m_LineNumber = lineNo++; +} void wxLayoutLine::MergeNextLine(wxLayoutList *llist) { - wxCHECK_RET(GetNextLine(),"wxLayout internal error: no next line to merge"); + wxCHECK_RET( GetNextLine(), + wxT("wxLayout internal error: no next line to merge")); wxLayoutObjectList &list = GetNextLine()->m_ObjectList; wxLOiterator i; @@ -1337,7 +1528,7 @@ wxLayoutLine::MergeNextLine(wxLayoutList *llist) SetNext(nextLine); if ( nextLine ) { - nextLine->MoveLines(-1); + nextLine->ReNumber(); } else { @@ -1352,6 +1543,8 @@ wxLayoutLine::MergeNextLine(wxLayoutList *llist) #endif // 0 } + llist->DecNumLines(); + delete oldnext; } @@ -1369,7 +1562,7 @@ wxLayoutLine::GetWrapPosition(CoordType column) { do { - if( isspace(((wxLayoutObjectText*)*i)->GetText().c_str()[(size_t)offset])) + if(isspace(((wxLayoutObjectText*)*i)->GetText().c_str()[(size_t)offset])) return column; else { @@ -1398,34 +1591,34 @@ wxLayoutLine::GetWrapPosition(CoordType column) i++; } if(i == NULLIT) return -1; //why should this happen? + + // now we are behind the one long text object and need to find the + // first space in it + for(offset = 0; offset < (**i).GetLength(); offset++) + if( isspace(((wxLayoutObjectText*)*i)->GetText().c_str()[(size_t)offset])) + { + return pos+offset; + } pos += (**i).GetLength(); - i++; - while(i != NULLIT && (**i).GetType() != WXLO_TYPE_TEXT) - { - pos += (**i).GetLength(); - i++; - } - if(i == NULLIT) return -1; //this is possible, if there is only one text object - // now we are at the second text object: - pos -= (**i).GetLength(); - return pos; // in front of it + return pos; } #ifdef WXLAYOUT_DEBUG void -wxLayoutLine::Debug(void) +wxLayoutLine::Debug(void) const { - wxString tmp; wxPoint pos = GetPosition(); - WXLO_DEBUG(("Line %ld, Pos (%ld,%ld), Height %ld, BL %ld, Font: %d", + WXLO_DEBUG((wxT("Line %ld, Pos (%ld,%ld), Height %ld, BL %ld, Font: %d"), (long int) GetLineNumber(), (long int) pos.x, (long int) pos.y, (long int) GetHeight(), (long int) m_BaseLine, (int) m_StyleInfo.family)); if(m_ObjectList.begin() != NULLIT) - (**m_ObjectList.begin()).Debug(); + { + WXLO_DEBUG(((**m_ObjectList.begin()).DebugDump().c_str())); + } } #endif @@ -1507,15 +1700,22 @@ wxLayoutList::wxLayoutList() m_caret = NULL; #endif // WXLAYOUT_USE_CARET + m_numLines = 0; m_FirstLine = NULL; + SetAutoFormatting(TRUE); + ForceTotalLayout(TRUE); // for the first time, do all InvalidateUpdateRect(); Clear(); } wxLayoutList::~wxLayoutList() { + SetAutoFormatting(FALSE); InternalClear(); + Empty(); m_FirstLine->DeleteLine(false, this); + + wxASSERT_MSG( m_numLines == 0, wxT("line count calculation broken")); } void @@ -1537,7 +1737,6 @@ wxLayoutList::Empty(void) void wxLayoutList::InternalClear(void) { - Empty(); m_Selection.m_selecting = false; m_Selection.m_valid = false; @@ -1552,8 +1751,44 @@ wxLayoutList::InternalClear(void) m_DefaultStyleInfo.m_bg = *wxWHITE; m_CurrentStyleInfo = m_DefaultStyleInfo; + m_CursorStyleInfo = m_DefaultStyleInfo; +} + +void +wxLayoutList::Read(wxString &istr) +{ + /* In order to handle input of formatted string "nicely", we need + to restore our current font settings after the string. So first + of all, we create a StyleInfo structure with our current + settings. */ + wxLayoutStyleInfo current_si = GetStyleInfo(); + + while(istr.Length()) + { + // check for a linebreak: + wxString tmp; + tmp = istr.BeforeFirst('\n'); + long l = WXLO_TYPE_INVALID; + tmp.ToLong(&l); + int type = (int) l; + + if(type == WXLO_TYPE_LINEBREAK) + { + LineBreak(); + istr = istr.AfterFirst('\n'); + } + else + { + wxLayoutObject *obj = wxLayoutObject::Read(istr); + if(obj) + Insert(obj); + } + } + /* Now we use the current_si to restore our last font settings: */ + Insert(new wxLayoutObjectCmd(current_si)); } + void wxLayoutList::SetFont(int family, int size, int style, int weight, int underline, wxColour *fg, @@ -1601,6 +1836,11 @@ wxLayoutList::Clear(int family, int size, int style, int weight, m_DefaultStyleInfo = wxLayoutStyleInfo(family, size, style, weight, underline, fg, bg); m_CurrentStyleInfo = m_DefaultStyleInfo; + + // Empty() should be called after we set m_DefaultStyleInfo because + // otherwise the style info for the first line (created in Empty()) would be + // incorrect + Empty(); } wxPoint @@ -1694,12 +1934,13 @@ wxLayoutList::MoveCursorVertically(int n) { n--; m_CursorPos.y ++; + last = m_CursorLine; m_CursorLine = m_CursorLine->GetNextLine(); } if(! m_CursorLine) { m_CursorLine = last; - m_CursorPos.y ++; + m_CursorPos.y --; rc = false; } else @@ -1761,70 +2002,158 @@ wxLayoutList::MoveCursorHorizontally(int n) } bool -wxLayoutList::MoveCursorWord(int n) +wxLayoutList::MoveCursorWord(int n, bool untilNext) { - wxCHECK_MSG( m_CursorLine, false, "no current line" ); - wxCHECK_MSG( n == -1 || n == +1, false, "not implemented yet" ); + wxCHECK_MSG( m_CursorLine, false, wxT("no current line") ); + wxCHECK_MSG( n == -1 || n == +1, false, wxT("not implemented yet") ); CoordType moveDistance = 0; CoordType offset; - for ( wxLOiterator i = m_CursorLine->FindObject(m_CursorPos.x, &offset); + wxLayoutLine *lineCur = m_CursorLine; + for ( wxLOiterator i = lineCur->FindObject(m_CursorPos.x, &offset); n != 0; n > 0 ? i++ : i-- ) { if ( i == NULLIT ) - return false; + { + if ( n > 0 ) + { + // moving forward, pass to the first object of the next line + moveDistance++; + lineCur = lineCur->GetNextLine(); + if ( lineCur ) + i = lineCur->GetFirstObject(); + } + else + { + // moving backwards, pass to the last object of the prev line + moveDistance--; + lineCur = lineCur->GetPreviousLine(); + if ( lineCur ) + i = lineCur->GetLastObject(); + } + + if ( i == NULLIT ) + { + // moved to the end/beginning of text + return false; + } + + offset = -1; + } wxLayoutObject *obj = *i; + + if ( offset == -1 ) + { + // calculate offset: we are either at the very beginning or the very + // end of the object, so it isn't very difficult (the only time when + // offset is != -1 is for the very first iteration when its value is + // returned by FindObject) + if ( n > 0 ) + offset = 0; + else + offset = obj->GetLength(); + } + if( obj->GetType() != WXLO_TYPE_TEXT ) { - // any non text objects count as one word - n > 0 ? n-- : n++; + // any visible non text objects count as one word + if ( obj->IsVisibleObject() ) + { + n > 0 ? n-- : n++; - moveDistance += obj->GetLength(); + moveDistance += obj->GetLength(); + } } - else + else // text object { - // text object wxLayoutObjectText *tobj = (wxLayoutObjectText *)obj; + bool canAdvance = true; + if ( offset == tobj->GetLength() ) { // at end of object - n > 0 ? n-- : n++; + if ( n > 0 ) + { + // can't move further in this text object + canAdvance = false; + + // still should move over the object border + moveDistance++; + n--; + } + else if ( offset > 0 ) + { + // offset is off by 1, make it a valid index + offset--; + } } - else + + if ( canAdvance ) { - const char *start = tobj->GetText().c_str(); - const char *p = start + offset; + const wxString& text = tobj->GetText(); + const wxChar *start = text.c_str(); + const wxChar *end = start + text.length(); + const wxChar *p = start + offset; + + if ( n < 0 ) + { + if ( offset > 0 ) + p--; + } // to the beginning/end of the next/prev word - while ( isspace(*p) ) + while ( p >= start && p < end && isspace(*p) ) { n > 0 ? p++ : p--; } // go to the end/beginning of the word (in a broad sense...) - while ( p >= start && !isspace(*p) ) + while ( p >= start && p < end && !isspace(*p) ) { n > 0 ? p++ : p--; } if ( n > 0 ) { - // now advance to the beginning of the next word - while ( isspace(*p) ) + if ( untilNext ) + { + // now advance to the beginning of the next word + while ( isspace(*p) && p < end ) + p++; + } + } + else // backwards + { + // in these 2 cases we took 1 char too much + if ( (p < start) || isspace(*p) ) + { p++; + } } - n > 0 ? n-- : n++; + CoordType moveDelta = p - start - offset; + if ( (n < 0) && (offset == tobj->GetLength() - 1) ) + { + // because we substracted 1 from offset in this case above, now + // compensate for it + moveDelta--; + } - moveDistance = p - start - offset; + if ( moveDelta != 0 ) + { + moveDistance += moveDelta; + + n > 0 ? n-- : n++; + } } } - // except for the first iteration, offset is 0 - offset = 0; + // except for the first iteration, offset is calculated in the beginning + // of the loop + offset = -1; } MoveCursorHorizontally(moveDistance); @@ -1836,21 +2165,24 @@ bool wxLayoutList::Insert(wxString const &text) { wxASSERT(m_CursorLine); - wxASSERT_MSG( text.Find('\n') == wxNOT_FOUND, "use wxLayoutImportText!" ); + wxASSERT_MSG( text.Find(wxT('\n')) == wxNOT_FOUND, + wxT("use wxLayoutImportText!") ); if ( !text ) return true; AddCursorPosToUpdateRect(); + wxASSERT(m_CursorLine->GetLength() >= m_CursorPos.x); + if ( !m_CursorLine->Insert(m_CursorPos.x, text) ) return false; - m_CursorPos.x += text.Length(); m_movedCursor = true; - m_CursorLine->RecalculatePositions(0, this); + if(m_AutoFormat) + m_CursorLine->MarkDirty(); return true; } @@ -1869,7 +2201,8 @@ wxLayoutList::Insert(wxLayoutObject *obj) m_CursorPos.x += obj->GetLength(); m_movedCursor = true; - m_CursorLine->RecalculatePositions(0, this); + if(m_AutoFormat) + m_CursorLine->MarkDirty(); return true; } @@ -1898,7 +2231,6 @@ bool wxLayoutList::LineBreak(void) { wxASSERT(m_CursorLine); - bool setFirst = (m_CursorLine == m_FirstLine && m_CursorPos.x == 0); AddCursorPosToUpdateRect(); @@ -1909,16 +2241,19 @@ wxLayoutList::LineBreak(void) height = m_CursorLine->GetHeight(); m_CursorLine = m_CursorLine->Break(m_CursorPos.x, this); - if(setFirst) // we were at beginning of first line - m_FirstLine = m_CursorLine->GetPreviousLine(); - if(m_CursorPos.x != 0) - m_CursorPos.y++; + if(m_CursorLine->GetPreviousLine() == NULL) + m_FirstLine = m_CursorLine; + m_CursorPos.y++; m_CursorPos.x = 0; + // The following code will produce a height which is guaranteed to + // be too high: old lineheight + the height of both new lines. + // We can probably drop the old line height and start with height = + // 0. FIXME wxLayoutLine *prev = m_CursorLine->GetPreviousLine(); - wxCHECK_MSG(prev, false, "just broke the line, where is the previous one?"); - - height += prev->GetHeight(); + if(prev) + height += prev->GetHeight(); + height += m_CursorLine->GetHeight(); m_movedCursor = true; @@ -1932,35 +2267,28 @@ wxLayoutList::LineBreak(void) bool wxLayoutList::WrapLine(CoordType column) { - if(m_CursorPos.x <= column || column < 1) - return false; // do nothing yet - else - { - CoordType xpos = m_CursorLine->GetWrapPosition(column); - if(xpos == -1) - return false; // cannot break line - //else: - CoordType newpos = m_CursorPos.x - xpos - 1; - m_CursorPos.x = xpos; - - AddCursorPosToUpdateRect(); - - LineBreak(); - Delete(1); // delete the space - m_CursorPos.x = newpos; - - m_CursorLine->RecalculatePositions(1, this); - - m_movedCursor = true; + return m_CursorLine->Wrap(column, this); +} - return true; +bool +wxLayoutList::WrapAll(CoordType column) +{ + wxLayoutLine *line = m_FirstLine; + if(! line) + return FALSE; + bool rc = TRUE; + while(line && rc) + { + rc &= line->Wrap(column, this); + line = line->GetNextLine(); } + return rc; } bool wxLayoutList::Delete(CoordType npos) { - wxCHECK_MSG(m_CursorLine, false, "can't delete in non existing line"); + wxCHECK_MSG(m_CursorLine, false, wxT("can't delete in non existing line")); if ( npos == 0 ) return true; @@ -2015,7 +2343,7 @@ wxLayoutList::Delete(CoordType npos) } else { - wxFAIL_MSG("can't delete all this"); + wxFAIL_MSG(wxT("can't delete all this")); return false; } @@ -2052,7 +2380,8 @@ wxLayoutList::DeleteLines(int n) { // we cannot delete this line, but we can clear it MoveCursorToBeginOfLine(); DeleteToEndOfLine(); - m_CursorLine->RecalculatePositions(2, this); + if(m_AutoFormat) + m_CursorLine->MarkDirty(); return n-1; } //else: @@ -2063,13 +2392,16 @@ wxLayoutList::DeleteLines(int n) wxASSERT(m_FirstLine); wxASSERT(m_CursorLine); } - m_CursorLine->RecalculatePositions(2, this); + if(m_AutoFormat) + m_CursorLine->MarkDirty(); return n; } void wxLayoutList::Recalculate(wxDC &dc, CoordType bottom) { + if(! m_AutoFormat) + return; wxLayoutLine *line = m_FirstLine; // first, make sure everything is calculated - this might not be @@ -2084,49 +2416,9 @@ wxLayoutList::Recalculate(wxDC &dc, CoordType bottom) } } -void -wxLayoutList::UpdateCursorScreenPos(wxDC &dc, - bool resetCursorMovedFlag, - const wxPoint& translate) -{ - wxCHECK_RET( m_CursorLine, "no cursor line" ); - - if ( m_movedCursor ) - { - // we need to save the current style, in case the layout() of - // the line changes it - wxLayoutStyleInfo SiBackup = m_CurrentStyleInfo; - m_CursorLine->Layout(dc, this, - &m_CursorScreenPos, &m_CursorSize, - m_CursorPos.x, - /* suppress update */ true); - ApplyStyle(SiBackup, dc); // restore it - - if ( resetCursorMovedFlag ) - { -#ifdef WXLAYOUT_USE_CARET - // adjust the caret position - wxPoint coords(m_CursorScreenPos); - coords += translate; - - // and set it - m_caret->Move(coords); -#endif // WXLAYOUT_USE_CARET - - m_movedCursor = false; - } - } -} - wxPoint -wxLayoutList::GetCursorScreenPos(wxDC &dc) +wxLayoutList::GetCursorScreenPos(void) const { - // this function is called with wxMemoryDC argument from ScrollToCursor(), - // for example, so it shouldn't clear "cursor moved" flag - or else the - // cursor won't be moved when UpdateCursorScreenPos() is called with the - // "real" (i.e. the one used for drawing) wxDC. - UpdateCursorScreenPos(dc, false /* don't reset the flag */); - return m_CursorScreenPos; } @@ -2135,90 +2427,161 @@ wxLayoutList::GetCursorScreenPos(wxDC &dc) have changed. */ void -wxLayoutList::Layout(wxDC &dc, CoordType bottom, bool forceAll) +wxLayoutList::Layout(wxDC &dc, CoordType bottom, bool forceAll, + wxPoint *cpos, wxPoint *csize) { // first, make sure everything is calculated - this might not be // needed, optimise it later ApplyStyle(m_DefaultStyleInfo, dc); - // This one we always Layout() to get the current cursor - // coordinates on the screen: - m_CursorLine->MarkDirty(); - bool wasDirty = false; + + if(m_ReLayoutAll) + { + forceAll = TRUE; + bottom = -1; + } + ForceTotalLayout(FALSE); + + + // If one line was dirty, we need to re-calculate all + // following lines, too. + bool wasDirty = forceAll; + // we need to layout until we reach at least the cursor line, + // otherwise we won't be able to scroll to it + bool cursorReached = false; wxLayoutLine *line = m_FirstLine; while(line) { if(! wasDirty) ApplyStyle(line->GetStyleInfo(), dc); - if(forceAll || line->IsDirty()) + if( + // if any previous line was dirty, we need to layout all + // following lines: + wasDirty + // go on until we find the cursorline + || ! cursorReached + // layout dirty lines: + || line->IsDirty() + // always layout the cursor line toupdate the cursor + // position and size: + || line == m_CursorLine + // or if it's the line we are asked to look for: + || (cpos && line->GetLineNumber() == cpos->y) + // layout at least the desired region: + || (bottom == -1 ) + || (line->GetPosition().y <= bottom) + ) { + if(line->IsDirty()) + wasDirty = true; + // The following Layout() calls will update our // m_CurrentStyleInfo if needed. if(line == m_CursorLine) + { line->Layout(dc, this, (wxPoint *)&m_CursorScreenPos, - (wxPoint *)&m_CursorSize, m_CursorPos.x); + (wxPoint *)&m_CursorSize, + &m_CursorStyleInfo, + m_CursorPos.x); + // we cannot layout the line twice, so copy the coords: + if(cpos && line ->GetLineNumber() == cpos->y) + { + *cpos = m_CursorScreenPos; + if ( csize ) + *csize = m_CursorSize; + } + cursorReached = TRUE; + } else - line->Layout(dc, this); - - // little condition to speed up redrawing: - if(bottom != -1 && line->GetPosition().y > bottom) - break; - wasDirty = true; + { + if(cpos && line->GetLineNumber() == cpos->y) + { + line->Layout(dc, this, + cpos, + csize, NULL, cpos->x); + cursorReached = TRUE; + } + else + line->Layout(dc, this); + } } - line->RecalculatePositions(1, this); line = line->GetNextLine(); } - + +#ifndef WXLAYOUT_USE_CARET // can only be 0 if we are on the first line and have no next line wxASSERT(m_CursorSize.x != 0 || (m_CursorLine && m_CursorLine->GetNextLine() == NULL && m_CursorLine == m_FirstLine)); +#endif // WXLAYOUT_USE_CARET AddCursorPosToUpdateRect(); } +wxPoint +wxLayoutList::GetScreenPos(wxDC &dc, const wxPoint &cpos, wxPoint *csize) +{ + wxPoint pos = cpos; + Layout(dc, -1, false, &pos, csize); + return pos; +} + void wxLayoutList::Draw(wxDC &dc, wxPoint const &offset, CoordType top, - CoordType bottom) + CoordType bottom, + bool clipStrictly) { wxLayoutLine *line = m_FirstLine; - /* We need to re-layout all dirty lines to update styleinfos - etc. However, somehow we don't find all dirty lines... */ - Layout(dc); //,-1,true); //FIXME + if ( m_Selection.m_discarded ) + { + // calculate them if we don't have them already + if ( !m_Selection.HasValidScreenCoords() ) + { + m_Selection.m_ScreenA = GetScreenPos(dc, m_Selection.m_CursorA); + m_Selection.m_ScreenB = GetScreenPos(dc, m_Selection.m_CursorB); + } + + // invalidate the area which was previousle selected - and which is not + // selected any more + SetUpdateRect(m_Selection.m_ScreenA); + SetUpdateRect(m_Selection.m_ScreenB); + + m_Selection.m_discarded = false; + } + + /* This call to Layout() will re-calculate and update all lines + marked as dirty. + */ + Layout(dc, bottom); + ApplyStyle(m_DefaultStyleInfo, dc); wxBrush brush(m_CurrentStyleInfo.m_bg, wxSOLID); dc.SetBrush(brush); dc.SetBackgroundMode(wxTRANSPARENT); - bool style_set = false; while(line) { // only draw if between top and bottom: if((top == -1 || - line->GetPosition().y + line->GetHeight() >= top)) + line->GetPosition().y + line->GetHeight() > top)) { -// if(! style_set) - { - ApplyStyle(line->GetStyleInfo(), dc); - style_set = true; - } + ApplyStyle(line->GetStyleInfo(), dc); + // little condition to speed up redrawing: + if( bottom != -1 + && line->GetPosition().y + +(clipStrictly ? line->GetHeight() : 0) >= bottom) + break; line->Draw(dc, this, offset); } -#if 0 - else - line->Layout(dc, this); -#endif - // little condition to speed up redrawing: - if(bottom != -1 && line->GetPosition().y > bottom) break; line = line->GetNextLine(); } InvalidateUpdateRect(); - WXLO_DEBUG(("Selection is %s : l%d,%ld/%ld,%ld", - m_Selection.m_valid ? "valid" : "invalid", + WXLO_DEBUG((wxT("Selection is %s : %ld,%ld/%ld,%ld"), + m_Selection.m_valid ? wxT("valid") : wxT("invalid"), m_Selection.m_CursorA.x, m_Selection.m_CursorA.y, m_Selection.m_CursorB.x, m_Selection.m_CursorB.y)); } @@ -2229,7 +2592,9 @@ wxLayoutList::FindObjectScreen(wxDC &dc, wxPoint const pos, bool *found) { // First, find the right line: - wxLayoutLine *line = m_FirstLine; + wxLayoutLine + *line = m_FirstLine, + *lastline = m_FirstLine; wxPoint p; ApplyStyle(m_DefaultStyleInfo, dc); @@ -2238,28 +2603,43 @@ wxLayoutList::FindObjectScreen(wxDC &dc, wxPoint const pos, p = line->GetPosition(); if(p.y <= pos.y && p.y+line->GetHeight() >= pos.y) break; -#if 0 - // we need to run a layout here to get font sizes right :-( - - // VZ: we can't call Layout() from here because it marks the line as - // clean and it is not refreshed when it's called from wxLayoutList:: - // Layout() - if we really need to do this, we should introduce an - // extra argument to Layout() to prevent the line from MarkClean()ing - // itself here - line->Layout(dc, this); -#endif + lastline = line; line = line->GetNextLine(); } - if(line == NULL) + + bool didFind = line != NULL; + + if ( !line ) { - if(found) *found = false; - return NULL; // not found + // use the last line: + line = lastline; } - if(cursorPos) cursorPos->y = line->GetLineNumber(); + + if ( cursorPos ) + cursorPos->y = line->GetLineNumber(); + + bool foundinline = true; + long cx = 0; + // Now, find the object in the line: - wxLOiterator i = line->FindObjectScreen(dc, pos.x, - cursorPos ? & cursorPos->x : NULL , - found); + wxLOiterator i; + + if (cursorPos) + { + i = line->FindObjectScreen(dc, this, + pos.x, + &cx, + &foundinline); + cursorPos->x = cx; + } + else + i = line->FindObjectScreen(dc, this, + pos.x, + NULL, + &foundinline); + if ( found ) + *found = didFind && foundinline; + return (i == NULLIT) ? NULL : *i; } @@ -2306,33 +2686,43 @@ wxLayoutList::GetSize(void) const void wxLayoutList::DrawCursor(wxDC &dc, bool active, wxPoint const &translate) { + if ( m_movedCursor ) + m_movedCursor = false; + wxPoint coords(m_CursorScreenPos); coords += translate; #ifdef WXLAYOUT_DEBUG - WXLO_DEBUG(("Drawing cursor (%ld,%ld) at %ld,%ld, size %ld,%ld, line: %ld, len %ld", + WXLO_DEBUG((wxT("Drawing cursor (%ld,%ld) at %ld,%ld, size %ld,%ld, line: %ld, len %ld"), (long)m_CursorPos.x, (long)m_CursorPos.y, - (long)coords.x, (long)coords.y, + (long)coords.x, (long)coords.y, (long)m_CursorSize.x, (long)m_CursorSize.y, (long)m_CursorLine->GetLineNumber(), (long)m_CursorLine->GetLength())); - wxLogStatus("Cursor is at (%d, %d)", m_CursorPos.x, m_CursorPos.y); + wxLogStatus(wxT("Cursor is at (%d, %d)"), m_CursorPos.x, m_CursorPos.y); #endif -#ifndef WXLAYOUT_USE_CARET - dc.SetBrush(*wxBLACK_BRUSH); - dc.SetLogicalFunction(wxXOR); +#ifdef WXLAYOUT_USE_CARET + m_caret->Move(coords); +#else // !WXLAYOUT_USE_CARET + + wxASSERT(m_CursorSize.x >= WXLO_MINIMUM_CURSOR_WIDTH); + dc.SetBrush(*wxWHITE_BRUSH); + //FIXME: wxGTK XOR is borken at the moment!!!dc.SetLogicalFunction(wxXOR); dc.SetPen(wxPen(*wxBLACK,1,wxSOLID)); if(active) { + dc.SetLogicalFunction(wxXOR); dc.DrawRectangle(coords.x, coords.y, m_CursorSize.x, m_CursorSize.y); SetUpdateRect(coords.x, coords.y); - SetUpdateRect(coords.x+m_CursorSize.x, coords.y+m_CursorSize.y); + SetUpdateRect(coords.x+m_CursorSize.x, + coords.y+m_CursorSize.y); } else { + dc.SetLogicalFunction(wxCOPY); dc.DrawLine(coords.x, coords.y+m_CursorSize.y-1, coords.x, coords.y); SetUpdateRect(coords.x, coords.y+m_CursorSize.y-1); @@ -2364,7 +2754,7 @@ wxLayoutList::StartSelection(const wxPoint& cposOrig, const wxPoint& spos) wxPoint cpos(cposOrig); if ( cpos.x == -1 ) cpos = m_CursorPos; - WXLO_DEBUG(("Starting selection at %ld/%ld", cpos.x, cpos.y)); + WXLO_DEBUG((wxT("Starting selection at %ld/%ld"), cpos.x, cpos.y)); m_Selection.m_CursorA = cpos; m_Selection.m_CursorB = cpos; m_Selection.m_ScreenA = spos; @@ -2382,19 +2772,19 @@ wxLayoutList::ContinueSelection(const wxPoint& cposOrig, const wxPoint& spos) wxASSERT(m_Selection.m_selecting == true); wxASSERT(m_Selection.m_valid == false); - WXLO_DEBUG(("Continuing selection at %ld/%ld", cpos.x, cpos.y)); + WXLO_DEBUG((wxT("Continuing selection at %ld/%ld"), cpos.x, cpos.y)); - if ( m_Selection.m_CursorB <= cpos ) - { - m_Selection.m_ScreenB = spos; - m_Selection.m_CursorB = cpos; - } - else - { - m_Selection.m_ScreenA = spos; - m_Selection.m_CursorA = cpos; - } + m_Selection.m_ScreenB = spos; + m_Selection.m_CursorB = cpos; +} +void +wxLayoutList::EndSelection(const wxPoint& cposOrig, const wxPoint& spos) +{ + wxPoint cpos(cposOrig); + if(cpos.x == -1) cpos = m_CursorPos; + ContinueSelection(cpos, spos); + WXLO_DEBUG((wxT("Ending selection at %ld/%ld"), cpos.x, cpos.y)); // we always want m_CursorA <= m_CursorB! if( m_Selection.m_CursorA > m_Selection.m_CursorB ) { @@ -2407,18 +2797,12 @@ wxLayoutList::ContinueSelection(const wxPoint& cposOrig, const wxPoint& spos) m_Selection.m_ScreenB = m_Selection.m_ScreenA; m_Selection.m_ScreenA = help; } -} - -void -wxLayoutList::EndSelection(const wxPoint& cposOrig, const wxPoint& spos) -{ - wxPoint cpos(cposOrig); - if(cpos.x == -1) - cpos = m_CursorPos; - ContinueSelection(cpos); - WXLO_DEBUG(("Ending selection at %ld/%ld", cpos.x, cpos.y)); m_Selection.m_selecting = false; m_Selection.m_valid = true; + /// In case we just clicked somewhere, the selection will have zero + /// size, so we discard it immediately. + if(m_Selection.m_CursorA == m_Selection.m_CursorB) + DiscardSelection(); } void @@ -2429,33 +2813,27 @@ wxLayoutList::DiscardSelection() m_Selection.m_valid = m_Selection.m_selecting = false; - - // invalidate the area which was previousle selected - and which is not - // selected any more - if ( m_Selection.HasValidScreenCoords() ) - { - SetUpdateRect(m_Selection.m_ScreenA); - SetUpdateRect(m_Selection.m_ScreenB); - } - else - { - // TODO - } + m_Selection.m_discarded = true; } bool -wxLayoutList::IsSelecting(void) +wxLayoutList::IsSelecting(void) const { return m_Selection.m_selecting; } bool -wxLayoutList::IsSelected(const wxPoint &cursor) +wxLayoutList::IsSelected(const wxPoint &cursor) const { if ( !HasSelection() ) return false; - return m_Selection.m_CursorA <= cursor && cursor <= m_Selection.m_CursorB; + return ( + (m_Selection.m_CursorA <= cursor + && cursor <= m_Selection.m_CursorB) + || (m_Selection.m_CursorB <= cursor + && cursor <= m_Selection.m_CursorA) + ); } @@ -2475,7 +2853,10 @@ wxLayoutList::IsSelected(const wxLayoutLine *line, CoordType *from, return 0; CoordType y = line->GetLineNumber(); - if(m_Selection.m_CursorA.y < y && m_Selection.m_CursorB.y > y) + if( + (m_Selection.m_CursorA.y < y && m_Selection.m_CursorB.y > y) + || (m_Selection.m_CursorB.y < y && m_Selection.m_CursorA.y > y) + ) return 1; else if(m_Selection.m_CursorA.y == y) { @@ -2483,7 +2864,18 @@ wxLayoutList::IsSelected(const wxLayoutLine *line, CoordType *from, if(m_Selection.m_CursorB.y == y) *to = m_Selection.m_CursorB.x; else - *to = line->GetLength(); + { + if(m_Selection.m_CursorB > m_Selection.m_CursorA) + *to = line->GetLength(); + else + *to = 0; + } + if(*to < *from) + { + CoordType help = *to; + *to = *from; + *from = help; + } return -1; } else if(m_Selection.m_CursorB.y == y) @@ -2492,7 +2884,18 @@ wxLayoutList::IsSelected(const wxLayoutLine *line, CoordType *from, if(m_Selection.m_CursorA.y == y) *from = m_Selection.m_CursorA.x; else - *from = 0; + { + if(m_Selection.m_CursorB > m_Selection.m_CursorA) + *from = 0; + else + *from = line->GetLength(); + } + if(*to < *from) + { + CoordType help = *to; + *to = *from; + *from = help; + } return -1; } else @@ -2515,44 +2918,32 @@ wxLayoutList::DeleteSelection(void) return; } + // We now know that the two lines are different: wxLayoutLine - * firstLine = NULL, - * lastLine = NULL; - - for(firstLine = m_FirstLine; - firstLine && firstLine->GetLineNumber() < m_Selection.m_CursorA.y; - firstLine=firstLine->GetNextLine()) - ; - if(!firstLine || firstLine->GetLineNumber() != m_Selection.m_CursorA.y) - return; - - - for(lastLine = m_FirstLine; - lastLine && lastLine->GetLineNumber() < m_Selection.m_CursorB.y; - lastLine=lastLine->GetNextLine()) - ; - if(!lastLine || lastLine->GetLineNumber() != m_Selection.m_CursorB.y) + * firstLine = GetLine(m_Selection.m_CursorA.y), + * lastLine = GetLine(m_Selection.m_CursorB.y); + // be a bit paranoid: + if(! firstLine || ! lastLine) return; - - - // We now know that the two lines are different: - + // First, delete what's left of this line: MoveCursorTo(m_Selection.m_CursorA); DeleteToEndOfLine(); - wxLayoutLine *nextLine = firstLine->GetNextLine(); + wxLayoutLine *prevLine = firstLine->GetPreviousLine(), + *nextLine = firstLine->GetNextLine(); while(nextLine && nextLine != lastLine) nextLine = nextLine->DeleteLine(false, this); // Now nextLine = lastLine; Delete(1); // This joins firstLine and nextLine - Delete(m_Selection.m_CursorB.x); // This deletes the first x - // positions + Delete(m_Selection.m_CursorB.x); // This deletes the first x positions - /// Recalculate: - firstLine->RecalculatePositions(1, this); + // Recalculate the line positions and numbers but notice that firstLine + // might not exist any more - it could be deleted by Delete(1) above + wxLayoutLine *firstLine2 = prevLine ? prevLine->GetNextLine() : m_FirstLine; + firstLine2->MarkDirty(); } /// Starts highlighting the selection @@ -2578,6 +2969,37 @@ wxLayoutList::EndHighlighting(wxDC &dc) } +wxLayoutLine * +wxLayoutList::GetLine(CoordType index) const +{ + wxASSERT_MSG( (0 <= index) && (index < (CoordType)m_numLines), + wxT("invalid index") ); + + wxLayoutLine *line; + CoordType n = index; +#ifdef DEBUG + CoordType lineNo = 0; +#endif + + for ( line = m_FirstLine; line && n-- > 0; line = + line->GetNextLine() ) + { +#ifdef DEBUG +wxASSERT(line->GetLineNumber() == lineNo ); + lineNo++; +#endif +} + + if ( line ) + { + // should be the right one + wxASSERT( line->GetLineNumber() == index ); + } + + return line; +} + + wxLayoutList * wxLayoutList::Copy(const wxPoint &from, const wxPoint &to) @@ -2652,18 +3074,17 @@ wxLayoutList::GetSelection(wxLayoutDataObject *wxlo, bool invalidate) { wxString string; - wxLayoutExportObject *export; + wxLayoutExportObject *exp; wxLayoutExportStatus status(llist); - while((export = wxLayoutExport( &status, WXLO_EXPORT_AS_OBJECTS)) != NULL) + while((exp = wxLayoutExport( &status, WXLO_EXPORT_AS_OBJECTS)) != NULL) { - if(export->type == WXLO_EXPORT_EMPTYLINE) - ; //FIXME missing support for linebreaks in string format + if(exp->type == WXLO_EXPORT_EMPTYLINE) + string << (int) WXLO_TYPE_LINEBREAK << '\n'; else - export->content.object->Write(string); - delete export; + exp->content.object->Write(string); + delete exp; } - - wxlo->SetData(string.c_str(), string.Length()+1); + wxlo->SetLayoutData(string); } return llist; } @@ -2687,11 +3108,13 @@ wxLayoutList::ApplyStyle(wxLayoutStyleInfo const &si, wxDC &dc) if(si.m_fg_valid) { m_CurrentStyleInfo.m_fg = si.m_fg; + m_CurrentStyleInfo.m_fg_valid = true; dc.SetTextForeground(m_CurrentStyleInfo.m_fg); } if(si.m_bg_valid) { m_CurrentStyleInfo.m_bg = si.m_bg; + m_CurrentStyleInfo.m_bg_valid = true; dc.SetTextBackground(m_CurrentStyleInfo.m_bg); } } @@ -2702,7 +3125,7 @@ wxLayoutList::ApplyStyle(wxLayoutStyleInfo const &si, wxDC &dc) void wxLayoutList::Debug(void) { - WXLO_DEBUG(("Cursor is in line %d, screen pos = (%d, %d)", + WXLO_DEBUG((wxT("Cursor is in line %d, screen pos = (%d, %d)"), m_CursorLine->GetLineNumber(), m_CursorScreenPos.x, m_CursorScreenPos.y)); @@ -2731,6 +3154,9 @@ wxLayoutPrintout::wxLayoutPrintout(wxLayoutList *llist, // remove any highlighting which could interfere with printing: m_llist->StartSelection(); m_llist->EndSelection(); + // force a full layout of the list: + m_llist->ForceTotalLayout(); + // layout is called in ScaleDC() when we have a DC } wxLayoutPrintout::~wxLayoutPrintout() @@ -2797,11 +3223,15 @@ bool wxLayoutPrintout::OnPrintPage(int page) { int top, bottom; top = (page - 1)*m_PrintoutHeight; - bottom = top + m_PrintoutHeight; + bottom = top + m_PrintoutHeight; + + WXLO_DEBUG((wxT("OnPrintPage(%d) printing from %d to %d"), page, top, + bottom)); // SetDeviceOrigin() doesn't work here, so we need to manually // translate all coordinates. wxPoint translate(m_Offset.x,m_Offset.y-top); - m_llist->Draw(*dc, translate, top, bottom); + m_llist->Draw(*dc, translate, top, bottom, TRUE /* clip strictly + */); return true; } else @@ -2813,23 +3243,28 @@ void wxLayoutPrintout::GetPageInfo(int *minPage, int *maxPage, int *selPageFrom, /* We allocate a temporary wxDC for printing, so that we can determine the correct paper size and scaling. We don't actually print anything on it. */ -#ifdef __WXMSW__ - wxPrinterDC psdc("","",WXLLIST_TEMPFILE,false); +#if defined(__WXMSW__) + wxPrinterDC *psdc = new wxPrinterDC("","",WXLLIST_TEMPFILE,false); #else - wxPostScriptDC psdc(WXLLIST_TEMPFILE,false); + wxPostScriptDC *psdc = new wxPostScriptDC(WXLLIST_TEMPFILE,false); #endif - float scale = ScaleDC(&psdc); + psdc->StartDoc(m_title); + // before we draw anything, me must make sure the list is properly + // laid out + m_llist->Layout(*psdc); - 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); + float scale = ScaleDC(psdc); + + psdc->GetSize(&m_PageWidth, &m_PageHeight); + + // This sets a left/top origin of 15% and 5%: + m_Offset = wxPoint((15*m_PageWidth)/100, (5*m_PageHeight)/100); // This is the length of the printable area. - m_PrintoutHeight = m_PageHeight - (int) (m_PageHeight * 0.15); + m_PrintoutHeight = m_PageHeight - 2*m_Offset.y; m_PrintoutHeight = (int)( m_PrintoutHeight / scale); // we want to use the real paper height - m_NumOfPages = 1 + (int)( m_llist->GetSize().y / (float)(m_PrintoutHeight)); @@ -2838,6 +3273,8 @@ void wxLayoutPrintout::GetPageInfo(int *minPage, int *maxPage, int *selPageFrom, *selPageFrom = 1; *selPageTo = m_NumOfPages; + psdc->EndDoc(); + delete psdc; wxRemoveFile(WXLLIST_TEMPFILE); }