X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/9ffdee8074b1581d2c8ad6e6b16f536a42e42dd1..75d7ef36eb97f84a53715721d11708cc33420cc2:/samples/richedit/wxllist.cpp?ds=sidebyside diff --git a/samples/richedit/wxllist.cpp b/samples/richedit/wxllist.cpp index 8391d67407..3e696cde7e 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 @@ -45,7 +45,11 @@ #endif #ifndef USE_PCH -# include +#if wxUSE_IOSTREAMH + #include +#else + #include +#endif # include # include @@ -60,6 +64,7 @@ #include + /// This should never really get created #define WXLLIST_TEMPFILE "__wxllist.tmp" @@ -72,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("%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 @@ -165,6 +173,7 @@ bool Contains(const wxRect &r, const wxPoint &p) //@} +static void ReadString(wxString &to, wxString &from) { to = ""; @@ -187,7 +196,7 @@ wxLayoutObject::Read(wxString &istr) { wxString tmp; ReadString(tmp, istr); - int type = -1; + int type = WXLO_TYPE_INVALID; sscanf(tmp.c_str(),"%d", &type); switch(type) @@ -329,6 +338,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 @@ -360,11 +379,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(" `%s`", m_Text.c_str()); + return str+str2; } #endif @@ -402,7 +424,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); } @@ -438,6 +460,8 @@ wxLayoutObjectIcon::Copy(void) wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap *icon) { m_Icon = icon; + if(! m_Icon) + m_Icon = new wxBitmap; } void @@ -514,12 +538,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, @@ -534,25 +565,25 @@ 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 */ @@ -563,10 +594,10 @@ wxLayoutObjectCmd::Read(wxString &istr) wxString tmp; ReadString(tmp, istr); - sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->size); - ReadString(tmp, istr); sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->family); ReadString(tmp, istr); + sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->size); + ReadString(tmp, istr); sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->style); ReadString(tmp, istr); sscanf(tmp.c_str(),"%d", &obj->m_StyleInfo->weight); @@ -642,10 +673,9 @@ wxLayoutLine::wxLayoutLine(wxLayoutLine *prev, wxLayoutList *llist) m_Length = 0; m_updateLeft = -1; - MarkDirty(0); - m_Previous = prev; m_Next = NULL; + MarkDirty(0); m_LineNumber = 0; RecalculatePosition(llist); @@ -1220,7 +1250,7 @@ wxLayoutLine::Layout(wxDC &dc, { // 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); @@ -1250,7 +1280,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; @@ -1281,6 +1313,130 @@ wxLayoutLine::Break(CoordType xpos, wxLayoutList *llist) return newLine; } +bool +wxLayoutLine::Wrap(CoordType wrapmargin, wxLayoutList *llist) +{ + if(GetLength() < wrapmargin) + return FALSE; // nothing to do + + // find the object which covers the wrapmargin: + CoordType offset; + wxLOiterator i = FindObject(wrapmargin, &offset); + wxCHECK_MSG( i != NULLIT, FALSE, "Cannot find object covering wrapmargin."); + + // from this object on, the rest of the line must be copied to the + // next one: + wxLOiterator copyObject = NULLIT; + // if we split a text-object, we must pre-pend some text to the + // next line later on, remember it here: + wxString prependText = ""; + // we might need to adjust the cursor position later, so remember it + size_t xpos = llist->GetCursorPos().x; + // by how much did we shorten the current line: + size_t shorter = 0; + // remember cursor location of object + size_t objectCursorPos = 0; + + size_t breakpos = offset; + + if( (**i).GetType() != WXLO_TYPE_TEXT ) + { + // break before a non-text object + copyObject = i; + } + else + { + bool foundSpace = FALSE; + do + { +// while(i != NULLIT && (**i).GetType() != WXLO_TYPE_TEXT) +// i--; + // try to find a suitable place to split the object: + wxLayoutObjectText *tobj = (wxLayoutObjectText *)*i; + if((**i).GetType() == WXLO_TYPE_TEXT + && tobj->GetText().Length() >= breakpos) + { + do + { + foundSpace = isspace(tobj->GetText()[breakpos]) != 0; + if ( foundSpace ) + break; + } + while ( breakpos-- > 0 ); + } + else + { + breakpos = 0; + } + + if(! foundSpace) // breakpos == 0! + { + if(i == m_ObjectList.begin()) + return FALSE; // could not break line + else + { + i--; + while(i != m_ObjectList.begin() + && (**i).GetType() != WXLO_TYPE_TEXT ) + { + i--; + } + breakpos = (**i).GetLength(); + } + } + }while(! foundSpace); + // before we actually break the object, we need to know at which + // cursorposition it starts, so we can restore the cursor if needed: + if( this == llist->GetCursorLine() && xpos >= breakpos ) + { + for(wxLOiterator j = m_ObjectList.begin(); + j != NULLIT && j != i; j++) + objectCursorPos += (**j).GetLength(); + } + // now we know where to break it: + wxLayoutObjectText *tobj = (wxLayoutObjectText *)*i; + shorter = tobj->GetLength() - breakpos; + // remember text to copy from this object + prependText = tobj->GetText().Mid(breakpos+1); + tobj->SetText(tobj->GetText().Left(breakpos)); + // copy every following object: + copyObject = i; copyObject ++; + } + + // make sure there is an empty m_Next line: + (void) new wxLayoutLine(this, llist); + wxASSERT(m_Next); + // We need to move this and all following objects to the next + // line. Starting from the end of line, to keep the order right. + if(copyObject != NULLIT) + { + wxLOiterator j; + for(j = m_ObjectList.tail(); j != copyObject; j--) + m_Next->Prepend(*j); + m_Next->Prepend(*copyObject); + // and now remove them from this list: + while( copyObject != m_ObjectList.end() ) + { + shorter += (**copyObject).GetLength(); + m_ObjectList.remove(copyObject); // remove without deleting it + } + } + m_Length -= shorter; + + if(prependText.Length() > 0) + m_Next->Insert(0, prependText); + + // do we need to adjust the cursor position? + if( this == llist->GetCursorLine() && xpos >= breakpos) + { + xpos = objectCursorPos + (xpos - objectCursorPos - breakpos - + ((xpos > breakpos) ? 1 : 0 )); + wxASSERT(xpos >= 0); + llist->MoveCursorTo( wxPoint( xpos, m_Next->GetLineNumber()) ); + } + return TRUE; // we wrapped the line +} + void wxLayoutLine::ReNumber(void) { @@ -1366,7 +1522,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 { @@ -1395,25 +1551,23 @@ 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", (long int) GetLineNumber(), @@ -1422,7 +1576,9 @@ wxLayoutLine::Debug(void) (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 @@ -1558,6 +1714,39 @@ wxLayoutList::InternalClear(void) m_CursorStyleInfo = m_DefaultStyleInfo; } +void +wxLayoutList::Read(wxString &istr) +{ + /* In order to handle input of formatted string "nicely", we need + to restore our current font settings after the string. So first + of all, we create a StyleInfo structure with our current + settings. */ + wxLayoutStyleInfo current_si = GetStyleInfo(); + + while(istr.Length()) + { + // check for a linebreak: + wxString tmp; + tmp = istr.BeforeFirst('\n'); + int type = WXLO_TYPE_INVALID; + sscanf(tmp.c_str(),"%d", &type); + if(type == WXLO_TYPE_LINEBREAK) + { + LineBreak(); + istr = istr.AfterFirst('\n'); + } + else + { + wxLayoutObject *obj = wxLayoutObject::Read(istr); + if(obj) + Insert(obj); + } + } + /* Now we use the current_si to restore our last font settings: */ + Insert(new wxLayoutObjectCmd(current_si)); +} + + void wxLayoutList::SetFont(int family, int size, int style, int weight, int underline, wxColour *fg, @@ -1703,6 +1892,7 @@ wxLayoutList::MoveCursorVertically(int n) { n--; m_CursorPos.y ++; + last = m_CursorLine; m_CursorLine = m_CursorLine->GetNextLine(); } if(! m_CursorLine) @@ -1940,9 +2130,10 @@ wxLayoutList::Insert(wxString const &text) 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; @@ -2009,8 +2200,7 @@ wxLayoutList::LineBreak(void) m_CursorLine = m_CursorLine->Break(m_CursorPos.x, this); if(m_CursorLine->GetPreviousLine() == NULL) m_FirstLine = m_CursorLine; - if(m_CursorPos.x > 0) - m_CursorPos.y++; + m_CursorPos.y++; m_CursorPos.x = 0; // The following code will produce a height which is guaranteed to @@ -2034,29 +2224,22 @@ 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->MarkDirty(); - - 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 @@ -2220,6 +2403,9 @@ wxLayoutList::Layout(wxDC &dc, CoordType bottom, bool forceAll, // 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) { @@ -2229,6 +2415,8 @@ wxLayoutList::Layout(wxDC &dc, CoordType bottom, bool forceAll, // 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 @@ -2236,6 +2424,9 @@ wxLayoutList::Layout(wxDC &dc, CoordType bottom, bool forceAll, || 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()) @@ -2257,17 +2448,20 @@ wxLayoutList::Layout(wxDC &dc, CoordType bottom, bool forceAll, if ( csize ) *csize = m_CursorSize; } + cursorReached = TRUE; } else + { if(cpos && line->GetLineNumber() == cpos->y) + { line->Layout(dc, this, cpos, csize, NULL, cpos->x); - else - line->Layout(dc, this); - // little condition to speed up redrawing: - if(bottom != -1 && line->GetPosition().y > bottom) - break; + cursorReached = TRUE; + } + else + line->Layout(dc, this); + } } line = line->GetNextLine(); } @@ -2343,7 +2537,7 @@ wxLayoutList::Draw(wxDC &dc, } InvalidateUpdateRect(); - WXLO_DEBUG(("Selection is %s : l%d,%ld/%ld,%ld", + WXLO_DEBUG(("Selection is %s : %ld,%ld/%ld,%ld", m_Selection.m_valid ? "valid" : "invalid", m_Selection.m_CursorA.x, m_Selection.m_CursorA.y, m_Selection.m_CursorB.x, m_Selection.m_CursorB.y)); @@ -2458,7 +2652,7 @@ wxLayoutList::DrawCursor(wxDC &dc, bool active, wxPoint const &translate) #ifdef WXLAYOUT_DEBUG WXLO_DEBUG(("Drawing cursor (%ld,%ld) at %ld,%ld, size %ld,%ld, line: %ld, len %ld", (long)m_CursorPos.x, (long)m_CursorPos.y, - (long)coords.x, (long)coords.y, + (long)coords.x, (long)coords.y, (long)m_CursorSize.x, (long)m_CursorSize.y, (long)m_CursorLine->GetLineNumber(), (long)m_CursorLine->GetLength())); @@ -2469,6 +2663,8 @@ wxLayoutList::DrawCursor(wxDC &dc, bool active, wxPoint const &translate) #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)); @@ -2478,7 +2674,8 @@ wxLayoutList::DrawCursor(wxDC &dc, bool active, wxPoint const &translate) 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 { @@ -2534,16 +2731,16 @@ wxLayoutList::ContinueSelection(const wxPoint& cposOrig, const wxPoint& spos) wxASSERT(m_Selection.m_valid == false); WXLO_DEBUG(("Continuing selection at %ld/%ld", cpos.x, cpos.y)); - m_Selection.m_ScreenB = spos; - m_Selection.m_CursorB = 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); + if(cpos.x == -1) cpos = m_CursorPos; + ContinueSelection(cpos, spos); WXLO_DEBUG(("Ending selection at %ld/%ld", cpos.x, cpos.y)); // we always want m_CursorA <= m_CursorB! if( m_Selection.m_CursorA > m_Selection.m_CursorB ) @@ -2839,14 +3036,12 @@ wxLayoutList::GetSelection(wxLayoutDataObject *wxlo, bool invalidate) while((exp = wxLayoutExport( &status, WXLO_EXPORT_AS_OBJECTS)) != NULL) { if(exp->type == WXLO_EXPORT_EMPTYLINE) - ; //FIXME missing support for linebreaks in string format + string << (int) WXLO_TYPE_LINEBREAK << '\n'; else exp->content.object->Write(string); delete exp; } -#if 0 // FIXME: DnD/Clipboard API has changed, what should this be? - wxlo->SetData(string.c_str(), string.Length()+1); -#endif + wxlo->SetLayoutData(string); } return llist; } @@ -2870,11 +3065,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); } } @@ -3003,7 +3200,7 @@ 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__ +#if defined(__WXMSW__) wxPrinterDC *psdc = new wxPrinterDC("","",WXLLIST_TEMPFILE,false); #else wxPostScriptDC *psdc = new wxPostScriptDC(WXLLIST_TEMPFILE,false);