diff --git a/samples/richedit/README b/samples/richedit/README index 5539268bc7..48f7aeff7c 100644 --- a/samples/richedit/README +++ b/samples/richedit/README @@ -10,6 +10,14 @@ by Karsten Ballueder . This is still work in progress, so if you want to make any significant changes, please get in touch with me before. + +*** THIS SOURCE IS USUALLY OUTDATED AND FOR DEMONSTRATION ONLY *** +*** GET THE VERY LATEST VERSION OF THE WXLAYOUT CLASSES FROM *** +*** THE SOURCE OF THE MAHOGANY MAIL/NEWS CLIENT, AVAILABLE FROM *** +*** http://www.wxwindows.org/Mahogany/ *** + + + There are three building blocks for rich text editing: wxllist : diff --git a/samples/richedit/wxllist.cpp b/samples/richedit/wxllist.cpp index 3a8e75082e..3a6cdd1b64 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$ *******************************************************************/ @@ -60,6 +60,7 @@ #include + /// This should never really get created #define WXLLIST_TEMPFILE "__wxllist.tmp" @@ -72,16 +73,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 +169,7 @@ bool Contains(const wxRect &r, const wxPoint &p) //@} +static void ReadString(wxString &to, wxString &from) { to = ""; @@ -187,7 +192,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) @@ -370,11 +375,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 @@ -412,7 +420,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); } @@ -449,11 +457,7 @@ wxLayoutObjectIcon::wxLayoutObjectIcon(wxBitmap *icon) { m_Icon = icon; if(! m_Icon) -#if wxICON_IS_BITMAP - m_Icon = new wxIcon; -#else m_Icon = new wxBitmap; -#endif } void @@ -557,25 +561,25 @@ wxLayoutObjectCmd::Copy(void) void wxLayoutObjectCmd::Write(wxString &ostr) { - ostr << WXLO_TYPE_CMD << '\n' - << m_StyleInfo->family << '\n' - << m_StyleInfo->size << '\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 */ @@ -665,10 +669,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); @@ -1273,7 +1276,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; @@ -1304,6 +1309,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) { @@ -1389,7 +1518,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 { @@ -1418,25 +1547,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(), @@ -1445,7 +1572,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 @@ -1592,9 +1721,22 @@ wxLayoutList::Read(wxString &istr) while(istr.Length()) { - wxLayoutObject *obj = wxLayoutObject::Read(istr); - if(obj) - Insert(obj); + // 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)); @@ -1746,6 +1888,7 @@ wxLayoutList::MoveCursorVertically(int n) { n--; m_CursorPos.y ++; + last = m_CursorLine; m_CursorLine = m_CursorLine->GetNextLine(); } if(! m_CursorLine) @@ -1983,9 +2126,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; @@ -2076,29 +2220,22 @@ wxLayoutList::LineBreak(void) bool wxLayoutList::WrapLine(CoordType column) { - if(m_CursorPos.x <= column || column < 1) - return false; // do nothing yet - else + return m_CursorLine->Wrap(column, this); +} + +bool +wxLayoutList::WrapAll(CoordType column) +{ + wxLayoutLine *line = m_FirstLine; + if(! line) + return FALSE; + bool rc = TRUE; + while(line && rc) { - 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 true; + rc &= line->Wrap(column, this); + line = line->GetNextLine(); } + return rc; } bool @@ -2262,6 +2399,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) { @@ -2271,6 +2411,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 @@ -2278,6 +2420,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()) @@ -2299,17 +2444,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(); } @@ -2385,7 +2533,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)); @@ -2500,7 +2648,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())); @@ -2522,7 +2670,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 { @@ -2883,7 +3032,7 @@ 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; diff --git a/samples/richedit/wxllist.h b/samples/richedit/wxllist.h index 71e7d381ae..0669c25804 100644 --- a/samples/richedit/wxllist.h +++ b/samples/richedit/wxllist.h @@ -1,7 +1,7 @@ /*-*- c++ -*-******************************************************** * wxLayoutList.h - a formatted text rendering engine for wxWindows * * * - * (C) 1999 by Karsten Ballüder (ballueder@gmx.net) * + * (C) 1999-2000 by Karsten Ballüder (ballueder@gmx.net) * * * * $Id$ *******************************************************************/ @@ -38,17 +38,38 @@ # define WXLAYOUT_USE_CARET 1 #endif // __WXMSW__ -// do not enable debug mode within Mahogany -#if defined(__WXDEBUG__) && ! defined(M_BASEDIR) +// do not enable debug mode within Mahogany unless in debug mode +#if defined(__WXDEBUG__) && (( ! defined(M_BASEDIR) )|| defined(DEBUG)) # define WXLAYOUT_DEBUG #endif #ifdef WXLAYOUT_DEBUG # define WXLO_TRACE(x) wxLogDebug(x) +// activate profiling: # define WXLO_PROFILE #else # define WXLO_TRACE(x) #endif +/* Some profiling code: */ +#if defined (WXLO_PROFILE) +#include +#include + +# define WXLO_TIMER_DEFINE(x) static struct timeval x +# define WXLO_TIMER_START(x) gettimeofday(&x,NULL) +# define WXLO_TIMER_STOP(x) { struct timeval y; \ + gettimeofday(&y,NULL); \ + x.tv_sec -= y.tv_sec; x.tv_usec -= y.tv_usec; } +# define WXLO_TIMER_PRINT(x) wxLogDebug("Timer " #x " elapsed: %ld", \ + (long)(x.tv_sec * -1000 - x.tv_usec)); +#else +# define WXLO_TIMER_DEFINE(x) +# define WXLO_TIMER_START(x) +# define WXLO_TIMER_STOP(x) +# define WXLO_TIMER_PRINT(x) +#endif + + #define WXLO_DEBUG_URECT 0 #ifndef WXLO_DEFAULTFONTSIZE @@ -71,7 +92,9 @@ enum wxLayoutObjectType /// command object, containing font or colour changes WXLO_TYPE_CMD, /// icon object, any kind of image - WXLO_TYPE_ICON + WXLO_TYPE_ICON, + /// a linebreak, does not exist as an object + WXLO_TYPE_LINEBREAK }; /// Type used for coordinates in drawing. Must be signed. @@ -171,7 +194,7 @@ public: virtual ~wxLayoutObject() { if(m_UserData) m_UserData->DecRef(); } #ifdef WXLAYOUT_DEBUG - virtual void Debug(void); + virtual wxString DebugDump(void) const; #endif /** Tells the object about some user data. This data is associated @@ -266,7 +289,7 @@ public: static wxLayoutObjectText *Read(wxString &istr); #ifdef WXLAYOUT_DEBUG - virtual void Debug(void); + virtual wxString DebugDump(void) const; #endif virtual CoordType GetLength(void) const { return strlen(m_Text.c_str()); } @@ -475,11 +498,18 @@ public: void Append(wxLayoutObject * obj) { wxASSERT(obj); - m_ObjectList.push_back(obj); m_Length += obj->GetLength(); } + /** This function prepends an object to the line. */ + void Prepend(wxLayoutObject * obj) + { + wxASSERT(obj); + m_ObjectList.push_front(obj); + m_Length += obj->GetLength(); + } + /** This function appens the next line to this, i.e. joins the two lines into one. */ @@ -498,6 +528,13 @@ public: */ wxLayoutLine *Break(CoordType xpos, wxLayoutList *llist); + /** This function wraps the line: breaks it at a suitable point + and merges it with the next. + @param wrapmargin + @return TRUE if broken + */ + bool Wrap(CoordType wrapmargin, wxLayoutList *llist); + /** Deletes the next word from this position, including leading whitespace. This function does not delete over font changes, i.e. a word @@ -662,7 +699,7 @@ public: CoordType to = -1); #ifdef WXLAYOUT_DEBUG - void Debug(void); + void Debug(void) const; #endif wxLayoutStyleInfo const & GetStyleInfo() const { return m_StyleInfo; } @@ -680,6 +717,7 @@ public: } m_Dirty = true; + if(m_Next) m_Next->MarkDirty(); } /// Reset the dirty flag void MarkClean() { m_Dirty = false; m_updateLeft = -1; } @@ -815,7 +853,8 @@ public: /// Returns current cursor position. const wxPoint &GetCursorPos(wxDC &dc) const { return m_CursorPos; } const wxPoint &GetCursorPos() const { return m_CursorPos; } - + wxLayoutLine * GetCursorLine(void) { return m_CursorLine; } + /// move cursor to the end of text void MoveCursorToEnd(void) { @@ -845,6 +884,12 @@ public: @return true if line got broken */ bool WrapLine(CoordType column); + + /** Wraps the complete buffer. + @param column the break position for the line, maximum length + @return true if line got broken + */ + bool WrapAll(CoordType column); /** This function deletes npos cursor positions. @param npos how many positions @return true if everything got deleted @@ -1142,7 +1187,8 @@ public: /// get the line by number wxLayoutLine *GetLine(CoordType index) const; - /** Reads objects from a string and inserts them. + /** Reads objects from a string and inserts them. Returns NULL if + string is empty or a linebreak was found. @param istr stream to read from, will bee changed */ void Read(wxString &istr); diff --git a/samples/richedit/wxlwindow.cpp b/samples/richedit/wxlwindow.cpp index 0993e2ca99..125790f178 100644 --- a/samples/richedit/wxlwindow.cpp +++ b/samples/richedit/wxlwindow.cpp @@ -1,7 +1,7 @@ /*-*- c++ -*-******************************************************** * wxLwindow.h : a scrolled Window for displaying/entering rich text* * * - * (C) 1998, 1999 by Karsten Ballüder (karsten@phy.hw.ac.uk) * + * (C) 1998-2000 by Karsten Ballüder (ballueder@gmx.net) * * * * $Id$ *******************************************************************/ @@ -56,6 +56,7 @@ #include + // ---------------------------------------------------------------------------- // macros // ---------------------------------------------------------------------------- @@ -66,6 +67,13 @@ # define WXLO_DEBUG(x) #endif +// for profiling in debug mode: +WXLO_TIMER_DEFINE(UpdateTimer); +WXLO_TIMER_DEFINE(BlitTimer); +WXLO_TIMER_DEFINE(LayoutTimer); +WXLO_TIMER_DEFINE(TmpTimer); +WXLO_TIMER_DEFINE(DrawTimer); + // ---------------------------------------------------------------------------- // constants // ---------------------------------------------------------------------------- @@ -160,6 +168,7 @@ wxLayoutWindow::wxLayoutWindow(wxWindow *parent) #ifndef __WXMSW__ m_FocusFollowMode = false; #endif + SetWordWrap(false); SetWrapMargin(0); // no scrollbars initially @@ -230,10 +239,11 @@ wxLayoutWindow::OnMouse(int eventId, wxMouseEvent& event) { wxClientDC dc( this ); PrepareDC( dc ); - if ( eventId != WXLOWIN_MENU_MOUSEMOVE + if ( (eventId != WXLOWIN_MENU_MOUSEMOVE #ifndef __WXMSW__ || m_FocusFollowMode #endif + ) && (wxWindow::FindFocus() != this) ) SetFocus(); @@ -479,6 +489,7 @@ wxLayoutWindow::OnChar(wxKeyEvent& event) bool deletedSelection = false; // pressing any non-arrow key optionally replaces the selection: if(m_AutoDeleteSelection + && IsEditable() && !m_Selecting && m_llist->HasSelection() && ! IsDirectionKey(keyCode) @@ -635,10 +646,21 @@ wxLayoutWindow::OnChar(wxKeyEvent& event) case 'x': Cut(); break; + case 'w': + if(m_WrapMargin > 0) + m_llist->WrapLine(m_WrapMargin); + break; + case 'q': + if(m_WrapMargin > 0) + m_llist->WrapAll(m_WrapMargin); + break; #ifdef WXLAYOUT_DEBUG case WXK_F1: m_llist->SetFont(-1,-1,-1,-1,true); // underlined break; + case 'l': + Refresh(TRUE); + break; #endif default: // we don't handle it, maybe an accelerator? @@ -688,7 +710,9 @@ wxLayoutWindow::OnChar(wxKeyEvent& event) } break; case WXK_RETURN: - if(m_WrapMargin > 0) + if(m_DoWordWrap && + m_WrapMargin > 0 + && m_llist->GetCursorPos().x > m_WrapMargin) m_llist->WrapLine(m_WrapMargin); m_llist->LineBreak(); SetDirty(); @@ -706,23 +730,19 @@ wxLayoutWindow::OnChar(wxKeyEvent& event) SetDirty(); } break; - + default: if((!(event.ControlDown() || event.AltDown() )) && (keyCode < 256 && keyCode >= 32) ) { - if(m_WrapMargin > 0 && isspace(keyCode)) - { - bool wrapped = m_llist->WrapLine(m_WrapMargin); - // don´t insert space as first thing in line - // after wrapping: - if(! wrapped || m_llist->GetCursorPos().x != 0) - m_llist->Insert((char)keyCode); - } - else - m_llist->Insert((char)keyCode); + if(m_DoWordWrap + && m_WrapMargin > 0 + && m_llist->GetCursorPos().x > m_WrapMargin + && isspace(keyCode)) + m_llist->WrapLine(m_WrapMargin); + m_llist->Insert((char)keyCode); SetDirty(); } else @@ -765,7 +785,10 @@ wxLayoutWindow::ScrollToCursor(void) { //is always needed to make sure we know where the cursor is //if(IsDirty()) - RequestUpdate(m_llist->GetUpdateRect()); + //RequestUpdate(m_llist->GetUpdateRect()); + + + ResizeScrollbars(); int x0,y0,x1,y1, dx, dy; @@ -802,7 +825,7 @@ wxLayoutWindow::ScrollToCursor(void) ny = 0; } - if ( nx != -1 || ny != -1 ) + if( nx != -1 || ny != -1 ) { // set new view start Scroll(nx == -1 ? -1 : (nx+dx-1)/dx, ny == -1 ? -1 : (ny+dy-1)/dy); @@ -836,6 +859,7 @@ wxLayoutWindow::RequestUpdate(const wxRect *updateRect) void wxLayoutWindow::InternalPaint(const wxRect *updateRect) { + wxPaintDC dc( this ); PrepareDC( dc ); @@ -864,9 +888,9 @@ wxLayoutWindow::InternalPaint(const wxRect *updateRect) updateRect->y+updateRect->height)); } - ResizeScrollbars(); + ResizeScrollbars(true); - + WXLO_TIMER_START(TmpTimer); /* Check whether the window has grown, if so, we need to reallocate the bitmap to be larger. */ if(x1 > m_bitmapSize.x || y1 > m_bitmapSize.y) @@ -887,7 +911,8 @@ wxLayoutWindow::InternalPaint(const wxRect *updateRect) 0,wxTRANSPARENT)); m_memDC->SetLogicalFunction(wxCOPY); m_memDC->Clear(); - + WXLO_TIMER_STOP(TmpTimer); + // fill the background with the background bitmap if(m_BGbitmap) { @@ -922,7 +947,7 @@ wxLayoutWindow::InternalPaint(const wxRect *updateRect) // update rectangle (although they are drawn on the memDC, this is // needed to erase it): m_llist->InvalidateUpdateRect(); - if(m_CursorVisibility != 0) + if(m_CursorVisibility == 1) { // draw a thick cursor for editable windows with focus m_llist->DrawCursor(*m_memDC, @@ -930,6 +955,7 @@ wxLayoutWindow::InternalPaint(const wxRect *updateRect) offset); } + WXLO_TIMER_START(BlitTimer); // Now copy everything to the screen: #if 0 // This somehow doesn't work, but even the following bit with the @@ -955,6 +981,8 @@ wxLayoutWindow::InternalPaint(const wxRect *updateRect) // y1 += WXLO_YOFFSET; //FIXME might not be needed dc.Blit(x0,y0,x1,y1,m_memDC,0,0,wxCOPY,FALSE); } + WXLO_TIMER_STOP(BlitTimer); + #ifdef WXLAYOUT_USE_CARET // show the caret back after everything is redrawn @@ -979,6 +1007,10 @@ wxLayoutWindow::InternalPaint(const wxRect *updateRect) m_StatusBar->SetStatusText(label, m_StatusFieldCursor); } } + + WXLO_TIMER_PRINT(LayoutTimer); + WXLO_TIMER_PRINT(BlitTimer); + WXLO_TIMER_PRINT(TmpTimer); } void @@ -999,14 +1031,20 @@ as needed. void wxLayoutWindow::ResizeScrollbars(bool exact) { - - if(! IsDirty()) - return; - wxClientDC dc( this ); PrepareDC( dc ); -// m_llist->ForceTotalLayout(); - m_llist->Layout(dc); +// m_llist->ForceTotalLayout(); + + if(! IsDirty()) + { + // we are laying out just the minimum, but always up to the + // cursor line, so the cursor position is updated. + m_llist->Layout(dc, 0); + return; + } + WXLO_TIMER_START(LayoutTimer); + m_llist->Layout(dc, -1); + WXLO_TIMER_STOP(LayoutTimer); ResetDirty(); wxPoint max = m_llist->GetSize(); @@ -1025,8 +1063,8 @@ wxLayoutWindow::ResizeScrollbars(bool exact) // TODO why do we set both at once? they're independent... if( max.x > m_maxx - WXLO_ROFFSET || max.y > m_maxy - WXLO_BOFFSET - || max.x < m_maxx - X_SCROLL_PAGE - || max.y < m_maxy - Y_SCROLL_PAGE + || (max.x < m_maxx - X_SCROLL_PAGE) + || (max.y < m_maxy - Y_SCROLL_PAGE) || exact ) { // text became too large @@ -1038,31 +1076,36 @@ wxLayoutWindow::ResizeScrollbars(bool exact) } bool done = FALSE; - if(max.x < X_SCROLL_PAGE) + if(max.x < X_SCROLL_PAGE && m_hasHScrollbar) { SetScrollbars(0,-1,0,-1,0,-1,true); m_hasHScrollbar = FALSE; done = TRUE; } - if(max.y < Y_SCROLL_PAGE) + if(max.y < Y_SCROLL_PAGE && m_hasVScrollbar) { SetScrollbars(-1,0,-1,0,-1,0,true); m_hasVScrollbar = FALSE; done = TRUE; } - if(! done) + if(! done && +// (max.x > X_SCROLL_PAGE || max.y > Y_SCROLL_PAGE) + (max.x > size.x - X_SCROLL_PAGE|| max.y > size.y - Y_SCROLL_PAGE) + ) { ViewStart(&m_ViewStartX, &m_ViewStartY); SetScrollbars(X_SCROLL_PAGE, Y_SCROLL_PAGE, - max.x / X_SCROLL_PAGE + 1, - max.y / Y_SCROLL_PAGE + 1, - m_ViewStartX, m_ViewStartY, + max.x / X_SCROLL_PAGE + 2, + max.y / Y_SCROLL_PAGE + 2, + m_ViewStartX, + m_ViewStartY, true); m_hasHScrollbar = m_hasVScrollbar = true; +// ScrollToCursor(); } - + m_maxx = max.x + X_SCROLL_PAGE; m_maxy = max.y + Y_SCROLL_PAGE; } diff --git a/samples/richedit/wxlwindow.h b/samples/richedit/wxlwindow.h index fa0ba4b291..8958e62fab 100644 --- a/samples/richedit/wxlwindow.h +++ b/samples/richedit/wxlwindow.h @@ -1,7 +1,7 @@ /*-*- c++ -*-******************************************************** * wxLwindow.h : a scrolled Window for displaying/entering rich text* * * - * (C) 1998-1999 by Karsten Ballüder (karsten@phy.hw.ac.uk) * + * (C) 1998-2000 by Karsten Ballüder (ballueder@gmx.net) * * * * $Id$ *******************************************************************/ @@ -118,6 +118,11 @@ public: */ void SetWrapMargin(CoordType margin) { m_WrapMargin = margin; } + /** Toggle wordwrap as we type. + @param on true to activate word wrap + */ + void SetWordWrap(bool on = true) { m_DoWordWrap = on; } + /** Redraws the window. Internally, this stores the parameter and calls a refresh on wxMSW, draws directly on wxGTK. @@ -230,6 +235,10 @@ protected: int m_maxx; int m_maxy; int m_lineHeight; + /// do we want automatic word wrap? + bool m_DoWordWrap; + /// wrap margin + CoordType m_WrapMargin; /// do we have the corresponding scrollbar? bool m_hasHScrollbar, @@ -253,8 +262,6 @@ private: bool m_Editable; /// Are we currently building a selection with the keyboard? bool m_Selecting; - /// wrap margin - CoordType m_WrapMargin; /// Has list changed since last redraw, e.g. in size? bool m_Dirty; /// Has the list ever been modified?