From ab14c7aa5727f3248e64e625ac19412799ca69bc Mon Sep 17 00:00:00 2001 From: Julian Smart Date: Tue, 12 Sep 2006 08:42:40 +0000 Subject: [PATCH] Setting the current style from the UI is a mode that is cancelled when moving away from the current caret position. This must be done to avoid confusing default attributes and attributes at the current caret position, in normal use. Style feedback now correct when caret is at the start of a paragraph (don't use previous paragraph's style), and out-by-one style feedback bug fixed (start/end of styled content). Added ApplyStyle for applying a character or paragraph style. Some coding style cleanup. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@41169 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- include/wx/richtext/richtextbuffer.h | 24 +++---- include/wx/richtext/richtextctrl.h | 38 ++++++++++- src/richtext/richtextbuffer.cpp | 94 +++++++++++++--------------- src/richtext/richtextctrl.cpp | 76 +++++++++++++++++++--- 4 files changed, 157 insertions(+), 75 deletions(-) diff --git a/include/wx/richtext/richtextbuffer.h b/include/wx/richtext/richtextbuffer.h index 8009ef9fe9..0e90377f23 100644 --- a/include/wx/richtext/richtextbuffer.h +++ b/include/wx/richtext/richtextbuffer.h @@ -303,8 +303,8 @@ public: bool HasParagraphSpacingAfter() const { return HasFlag(wxTEXT_ATTR_PARA_SPACING_AFTER); } bool HasParagraphSpacingBefore() const { return HasFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE); } bool HasLineSpacing() const { return HasFlag(wxTEXT_ATTR_LINE_SPACING); } - bool HasCharacterStyleName() const { return HasFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME); } - bool HasParagraphStyleName() const { return HasFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME); } + bool HasCharacterStyleName() const { return HasFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME) || !m_characterStyleName.IsEmpty(); } + bool HasParagraphStyleName() const { return HasFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME) || !m_paragraphStyleName.IsEmpty(); } bool HasBulletStyle() const { return HasFlag(wxTEXT_ATTR_BULLET_STYLE); } bool HasBulletNumber() const { return HasFlag(wxTEXT_ATTR_BULLET_NUMBER); } bool HasBulletSymbol() const { return HasFlag(wxTEXT_ATTR_BULLET_SYMBOL); } @@ -399,14 +399,14 @@ public: void SetFlags(long flags) { m_flags = flags; } - void SetCharacterStyleName(const wxString& name) { m_characterStyleName = name; } - void SetParagraphStyleName(const wxString& name) { m_paragraphStyleName = name; } - void SetParagraphSpacingAfter(int spacing) { m_paragraphSpacingAfter = spacing; } - void SetParagraphSpacingBefore(int spacing) { m_paragraphSpacingBefore = spacing; } - void SetLineSpacing(int spacing) { m_lineSpacing = spacing; } - void SetBulletStyle(int style) { m_bulletStyle = style; } - void SetBulletNumber(int n) { m_bulletNumber = n; } - void SetBulletSymbol(wxChar symbol) { m_bulletSymbol = symbol; } + void SetCharacterStyleName(const wxString& name) { m_characterStyleName = name; m_flags |= wxTEXT_ATTR_CHARACTER_STYLE_NAME; } + void SetParagraphStyleName(const wxString& name) { m_paragraphStyleName = name; m_flags |= wxTEXT_ATTR_PARAGRAPH_STYLE_NAME; } + void SetParagraphSpacingAfter(int spacing) { m_paragraphSpacingAfter = spacing; m_flags |= wxTEXT_ATTR_PARA_SPACING_AFTER; } + void SetParagraphSpacingBefore(int spacing) { m_paragraphSpacingBefore = spacing; m_flags |= wxTEXT_ATTR_PARA_SPACING_BEFORE; } + void SetLineSpacing(int spacing) { m_lineSpacing = spacing; m_flags |= wxTEXT_ATTR_LINE_SPACING; } + void SetBulletStyle(int style) { m_bulletStyle = style; m_flags |= wxTEXT_ATTR_BULLET_STYLE; } + void SetBulletNumber(int n) { m_bulletNumber = n; m_flags |= wxTEXT_ATTR_BULLET_NUMBER; } + void SetBulletSymbol(wxChar symbol) { m_bulletSymbol = symbol; m_flags |= wxTEXT_ATTR_BULLET_NUMBER; } const wxColour& GetTextColour() const { return m_colText; } const wxColour& GetBackgroundColour() const { return m_colBack; } @@ -449,8 +449,8 @@ public: bool HasParagraphSpacingAfter() const { return (m_flags & wxTEXT_ATTR_PARA_SPACING_AFTER) != 0; } bool HasParagraphSpacingBefore() const { return (m_flags & wxTEXT_ATTR_PARA_SPACING_BEFORE) != 0; } bool HasLineSpacing() const { return (m_flags & wxTEXT_ATTR_LINE_SPACING) != 0; } - bool HasCharacterStyleName() const { return (m_flags & wxTEXT_ATTR_CHARACTER_STYLE_NAME) != 0; } - bool HasParagraphStyleName() const { return (m_flags & wxTEXT_ATTR_PARAGRAPH_STYLE_NAME) != 0; } + bool HasCharacterStyleName() const { return (m_flags & wxTEXT_ATTR_CHARACTER_STYLE_NAME) != 0 || !m_characterStyleName.IsEmpty(); } + bool HasParagraphStyleName() const { return (m_flags & wxTEXT_ATTR_PARAGRAPH_STYLE_NAME) != 0 || !m_paragraphStyleName.IsEmpty(); } bool HasBulletStyle() const { return (m_flags & wxTEXT_ATTR_BULLET_STYLE) != 0; } bool HasBulletNumber() const { return (m_flags & wxTEXT_ATTR_BULLET_NUMBER) != 0; } bool HasBulletSymbol() const { return (m_flags & wxTEXT_ATTR_BULLET_SYMBOL) != 0; } diff --git a/include/wx/richtext/richtextctrl.h b/include/wx/richtext/richtextctrl.h index a2abcbd989..e3c2cb4e6a 100644 --- a/include/wx/richtext/richtextctrl.h +++ b/include/wx/richtext/richtextctrl.h @@ -20,7 +20,7 @@ #include "wx/caret.h" #if wxCHECK_VERSION(2,7,0) -#define wxRICHTEXT_DERIVES_FROM_TEXTCTRLBASE 0 +#define wxRICHTEXT_DERIVES_FROM_TEXTCTRLBASE 1 #else #define wxRICHTEXT_DERIVES_FROM_TEXTCTRLBASE 0 #endif @@ -29,6 +29,8 @@ #include "wx/textctrl.h" #endif +class WXDLLIMPEXP_RICHTEXT wxRichTextStyleDefinition; + /*! * Styles and flags */ @@ -508,6 +510,9 @@ public: /// Apply alignment to the selection virtual bool ApplyAlignmentToSelection(wxTextAttrAlignment alignment); + /// Apply a named style to the selection + virtual void ApplyStyle(wxRichTextStyleDefinition* def); + /// Set style sheet, if any. void SetStyleSheet(wxRichTextStyleSheet* styleSheet) { GetBuffer().SetStyleSheet(styleSheet); } wxRichTextStyleSheet* GetStyleSheet() const { return GetBuffer().GetStyleSheet(); } @@ -630,6 +635,11 @@ public: void SetCaretPosition(long position, bool showAtLineStart = false) ; long GetCaretPosition() const { return m_caretPosition; } + /// The adjusted caret position is the character position adjusted to take + /// into account whether we're at the start of a paragraph, in which case + /// style information should be taken from the next position, not current one. + long GetAdjustedCaretPosition(long caretPos) const; + /// Move caret one visual step forward: this may mean setting a flag /// and keeping the same position if we're going from the end of one line /// to the start of the next, which may be the exact same caret position. @@ -673,6 +683,27 @@ public: /// Returns the first visible position in the current view long GetFirstVisiblePosition() const; + /// Returns the caret position since the default formatting was changed. As + /// soon as this position changes, we no longer reflect the default style + /// in the UI. A value of -2 means that we should only reflect the style of the + /// content under the caret. + long GetCaretPositionForDefaultStyle() const { return m_caretPositionForDefaultStyle; } + + /// Set the caret position for the default style that the user is selecting. + void SetCaretPositionForDefaultStyle(long pos) { m_caretPositionForDefaultStyle = pos; } + + /// Should the UI reflect the default style chosen by the user, rather than the style under + /// the caret? + bool IsDefaultStyleShowing() const { return m_caretPositionForDefaultStyle != -2; } + + /// Convenience function that tells the control to start reflecting the default + /// style, since the user is changing it. + void SetAndShowDefaultStyle(const wxRichTextAttr& attr) + { + SetDefaultStyle(attr); + SetCaretPositionForDefaultStyle(GetCaretPosition()); + } + #if wxRICHTEXT_DERIVES_FROM_TEXTCTRLBASE WX_FORWARD_TO_SCROLL_HELPER() #endif @@ -703,6 +734,11 @@ private: /// first caret position). long m_caretPosition; + /// Caret position when the default formatting has been changed. As + /// soon as this position changes, we no longer reflect the default style + /// in the UI. + long m_caretPositionForDefaultStyle; + /// Selection range in character positions. -2, -2 means no selection. wxRichTextRange m_selectionRange; diff --git a/src/richtext/richtextbuffer.cpp b/src/richtext/richtextbuffer.cpp index e350f524cb..d3fe3cfdf3 100644 --- a/src/richtext/richtextbuffer.cpp +++ b/src/richtext/richtextbuffer.cpp @@ -2955,27 +2955,13 @@ bool wxRichTextPlainText::Draw(wxDC& dc, const wxRichTextRange& range, const wxR // (a) All selected. if (selectionRange.GetStart() <= range.GetStart() && selectionRange.GetEnd() >= range.GetEnd()) - {/* - // Draw all selected - dc.SetBrush(*wxBLACK_BRUSH); - dc.SetPen(*wxBLACK_PEN); - wxCoord w, h; - dc.GetTextExtent(stringChunk, & w, & h); - wxRect selRect(x, rect.y, w, rect.GetHeight()); - dc.DrawRectangle(selRect); - dc.SetTextForeground(*wxWHITE); - dc.SetBackgroundMode(wxTRANSPARENT); - dc.DrawText(stringChunk, x, y);*/ + { DrawTabbedString(dc, rect,stringChunk, x, y, true); } // (b) None selected. else if (selectionRange.GetEnd() < range.GetStart() || selectionRange.GetStart() > range.GetEnd()) { // Draw all unselected - /* - dc.SetTextForeground(GetAttributes().GetTextColour()); - dc.SetBackgroundMode(wxTRANSPARENT); - dc.DrawText(stringChunk, x, y);*/ DrawTabbedString(dc, rect,stringChunk, x, y, false); } else @@ -2994,13 +2980,7 @@ bool wxRichTextPlainText::Draw(wxDC& dc, const wxRichTextRange& range, const wxR if (fragmentLen < 0) wxLogDebug(wxT("Mid(%d, %d"), (int)(r1 - offset), (int)fragmentLen); wxString stringFragment = m_text.Mid(r1 - offset, fragmentLen); -/* - dc.SetTextForeground(GetAttributes().GetTextColour()); - dc.DrawText(stringFragment, x, y); - wxCoord w, h; - dc.GetTextExtent(stringFragment, & w, & h); - x += w;*/ DrawTabbedString(dc, rect,stringFragment, x, y, false); } @@ -3014,18 +2994,7 @@ bool wxRichTextPlainText::Draw(wxDC& dc, const wxRichTextRange& range, const wxR if (fragmentLen < 0) wxLogDebug(wxT("Mid(%d, %d"), (int)(s1 - offset), (int)fragmentLen); wxString stringFragment = m_text.Mid(s1 - offset, fragmentLen); -/* - wxCoord w, h; - dc.GetTextExtent(stringFragment, & w, & h); - wxRect selRect(x, rect.y, w, rect.GetHeight()); - - dc.SetBrush(*wxBLACK_BRUSH); - dc.SetPen(*wxBLACK_PEN); - dc.DrawRectangle(selRect); - dc.SetTextForeground(*wxWHITE); - dc.DrawText(stringFragment, x, y); - x += w;*/ DrawTabbedString(dc, rect,stringFragment, x, y, true); } @@ -3039,9 +3008,7 @@ bool wxRichTextPlainText::Draw(wxDC& dc, const wxRichTextRange& range, const wxR if (fragmentLen < 0) wxLogDebug(wxT("Mid(%d, %d"), (int)(s2 - offset), (int)fragmentLen); wxString stringFragment = m_text.Mid(s2 - offset, fragmentLen); -/* - dc.SetTextForeground(GetAttributes().GetTextColour()); - dc.DrawText(stringFragment, x, y);*/ + DrawTabbedString(dc, rect,stringFragment, x, y, false); } } @@ -3052,43 +3019,57 @@ bool wxRichTextPlainText::Draw(wxDC& dc, const wxRichTextRange& range, const wxR bool wxRichTextPlainText::DrawTabbedString(wxDC& dc,const wxRect& rect,wxString& str, wxCoord& x, wxCoord& y, bool selected) { wxArrayInt tab_array = GetAttributes().GetTabs(); - if(tab_array.IsEmpty()){// create a default tab list at 10 mm each. - for( int i = 0; i < 20; ++i){ + if (tab_array.IsEmpty()) + { + // create a default tab list at 10 mm each. + for (int i = 0; i < 20; ++i) + { tab_array.Add(i*100); } } int map_mode = dc.GetMapMode(); dc.SetMapMode(wxMM_LOMETRIC ); int num_tabs = tab_array.GetCount(); - for( int i = 0; i < num_tabs; ++i){ + for (int i = 0; i < num_tabs; ++i) + { tab_array[i] = dc.LogicalToDeviceXRel(tab_array[i]); } + dc.SetMapMode(map_mode ); int next_tab_pos = -1; int tab_pos = -1; wxCoord w, h; - if(selected){ + + if(selected) + { dc.SetBrush(*wxBLACK_BRUSH); dc.SetPen(*wxBLACK_PEN); dc.SetTextForeground(*wxWHITE); dc.SetBackgroundMode(wxTRANSPARENT); } - else{ + else + { dc.SetTextForeground(GetAttributes().GetTextColour()); dc.SetBackgroundMode(wxTRANSPARENT); } - while(str.Find(wxT('\t')) >= 0){// the string has a tab + + while (str.Find(wxT('\t')) >= 0) + { + // the string has a tab // break up the string at the Tab wxString stringChunk = str.BeforeFirst(wxT('\t')); str = str.AfterFirst(wxT('\t')); dc.GetTextExtent(stringChunk, & w, & h); tab_pos = x + w; bool not_found = true; - for( int i = 0; i < num_tabs && not_found; ++i){ + for (int i = 0; i < num_tabs && not_found; ++i) + { next_tab_pos = tab_array.Item(i); - if( next_tab_pos > tab_pos){ + if (next_tab_pos > tab_pos) + { not_found = false; - if(selected){ + if (selected) + { w = next_tab_pos - x; wxRect selRect(x, rect.y, w, rect.GetHeight()); dc.DrawRectangle(selRect); @@ -3100,7 +3081,8 @@ bool wxRichTextPlainText::DrawTabbedString(wxDC& dc,const wxRect& rect,wxString& } dc.GetTextExtent(str, & w, & h); - if(selected){ + if (selected) + { wxRect selRect(x, rect.y, w, rect.GetHeight()); dc.DrawRectangle(selRect); } @@ -3149,27 +3131,33 @@ bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange& range, wxSize& siz wxString stringChunk = m_text.Mid(startPos, (size_t) len); wxCoord w, h; int width = 0; - if(stringChunk.Find(wxT('\t')) >= 0){// the string has a tab + if (stringChunk.Find(wxT('\t')) >= 0) + { + // the string has a tab wxArrayInt tab_array = GetAttributes().GetTabs(); - if(tab_array.IsEmpty()) + if (tab_array.IsEmpty()) { // create a default tab list at 10 mm each. - for( int i = 0; i < 20; ++i) + for (int i = 0; i < 20; ++i) { tab_array.Add(i*100); } } + int map_mode = dc.GetMapMode(); dc.SetMapMode(wxMM_LOMETRIC ); int num_tabs = tab_array.GetCount(); - for( int i = 0; i < num_tabs; ++i) + + for (int i = 0; i < num_tabs; ++i) { tab_array[i] = dc.LogicalToDeviceXRel(tab_array[i]); } dc.SetMapMode(map_mode ); int next_tab_pos = -1; - while(stringChunk.Find(wxT('\t')) >= 0){// the string has a tab + while (stringChunk.Find(wxT('\t')) >= 0) + { + // the string has a tab // break up the string at the Tab wxString stringFragment = stringChunk.BeforeFirst(wxT('\t')); stringChunk = stringChunk.AfterFirst(wxT('\t')); @@ -3177,9 +3165,11 @@ bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange& range, wxSize& siz width += w; int absolute_width = width + position.x; bool not_found = true; - for( int i = 0; i < num_tabs && not_found; ++i){ + for (int i = 0; i < num_tabs && not_found; ++i) + { next_tab_pos = tab_array.Item(i); - if( next_tab_pos > absolute_width){ + if (next_tab_pos > absolute_width) + { not_found = false; width = next_tab_pos - position.x; } diff --git a/src/richtext/richtextctrl.cpp b/src/richtext/richtextctrl.cpp index c312aed560..32d4a691e8 100644 --- a/src/richtext/richtextctrl.cpp +++ b/src/richtext/richtextctrl.cpp @@ -19,6 +19,7 @@ #if wxUSE_RICHTEXT #include "wx/richtext/richtextctrl.h" +#include "wx/richtext/richtextstyles.h" #ifndef WX_PRECOMP #include "wx/settings.h" @@ -186,6 +187,7 @@ void wxRichTextCtrl::Init() m_fullLayoutTime = 0; m_fullLayoutSavedPosition = 0; m_delayedLayoutThreshold = wxRICHTEXT_DEFAULT_DELAYED_LAYOUT_THRESHOLD; + m_caretPositionForDefaultStyle = -2; } /// Call Freeze to prevent refresh @@ -212,6 +214,7 @@ void wxRichTextCtrl::Clear() m_buffer.Reset(); m_buffer.SetDirty(true); m_caretPosition = -1; + m_caretPositionForDefaultStyle = -2; m_caretAtLineStart = false; m_selectionRange.SetRange(-2, -2); @@ -1428,6 +1431,15 @@ void wxRichTextCtrl::OnIdle(wxIdleEvent& event) ShowPosition(m_fullLayoutSavedPosition); Refresh(false); } + + if (m_caretPositionForDefaultStyle != -2) + { + // If the caret position has changed, no longer reflect the default style + // in the UI. + if (GetCaretPosition() != m_caretPositionForDefaultStyle) + m_caretPositionForDefaultStyle = -2; + } + event.Skip(); } @@ -1968,6 +1980,7 @@ void wxRichTextCtrl::MarkDirty() void wxRichTextCtrl::DiscardEdits() { + m_caretPositionForDefaultStyle = -2; m_buffer.Modify(false); m_buffer.GetCommandProcessor()->ClearCommands(); } @@ -2420,9 +2433,12 @@ bool wxRichTextCtrl::IsSelectionBold() const // to see what the effect would be if we started typing. wxRichTextAttr attr; attr.SetFlags(wxTEXT_ATTR_FONT_WEIGHT); - if (GetStyle(GetCaretPosition()+1, attr)) + + long pos = GetAdjustedCaretPosition(GetCaretPosition()); + if (GetStyle(pos, attr)) { - wxRichTextApplyStyle(attr, GetDefaultStyleEx()); + if (IsDefaultStyleShowing()) + wxRichTextApplyStyle(attr, GetDefaultStyleEx()); return attr.GetFontWeight() == wxBOLD; } } @@ -2447,9 +2463,12 @@ bool wxRichTextCtrl::IsSelectionItalics() const // to see what the effect would be if we started typing. wxRichTextAttr attr; attr.SetFlags(wxTEXT_ATTR_FONT_ITALIC); - if (GetStyle(GetCaretPosition()+1, attr)) + + long pos = GetAdjustedCaretPosition(GetCaretPosition()); + if (GetStyle(pos, attr)) { - wxRichTextApplyStyle(attr, GetDefaultStyleEx()); + if (IsDefaultStyleShowing()) + wxRichTextApplyStyle(attr, GetDefaultStyleEx()); return attr.GetFontStyle() == wxITALIC; } } @@ -2474,9 +2493,12 @@ bool wxRichTextCtrl::IsSelectionUnderlined() const // to see what the effect would be if we started typing. wxRichTextAttr attr; attr.SetFlags(wxTEXT_ATTR_FONT_UNDERLINE); - if (GetStyle(GetCaretPosition()+1, attr)) + + long pos = GetAdjustedCaretPosition(GetCaretPosition()); + if (GetStyle(pos, attr)) { - wxRichTextApplyStyle(attr, GetDefaultStyleEx()); + if (IsDefaultStyleShowing()) + wxRichTextApplyStyle(attr, GetDefaultStyleEx()); return attr.GetFontUnderlined(); } } @@ -2493,7 +2515,7 @@ bool wxRichTextCtrl::ApplyBoldToSelection() if (HasSelection()) return SetStyle(GetSelectionRange(), attr); else - SetDefaultStyle(attr); + SetAndShowDefaultStyle(attr); return true; } @@ -2507,7 +2529,7 @@ bool wxRichTextCtrl::ApplyItalicToSelection() if (HasSelection()) return SetStyle(GetSelectionRange(), attr); else - SetDefaultStyle(attr); + SetAndShowDefaultStyle(attr); return true; } @@ -2521,7 +2543,7 @@ bool wxRichTextCtrl::ApplyUnderlineToSelection() if (HasSelection()) return SetStyle(GetSelectionRange(), attr); else - SetDefaultStyle(attr); + SetAndShowDefaultStyle(attr); return true; } @@ -2562,13 +2584,35 @@ bool wxRichTextCtrl::ApplyAlignmentToSelection(wxTextAttrAlignment alignment) return true; } +/// Apply a named style to the selection +void wxRichTextCtrl::ApplyStyle(wxRichTextStyleDefinition* def) +{ + // Flags are defined within each definition, so only certain + // attributes are applied. + wxRichTextAttr attr(def->GetStyle()); + + // Make sure the attr has the style name + if (def->IsKindOf(CLASSINFO(wxRichTextParagraphStyleDefinition))) + attr.SetParagraphStyleName(def->GetName()); + else + attr.SetCharacterStyleName(def->GetName()); + + if (HasSelection()) + SetStyle(GetSelectionRange(), attr); + else + SetAndShowDefaultStyle(attr); +} + /// Sets the default style to the style under the cursor bool wxRichTextCtrl::SetDefaultStyleToCursorStyle() { wxTextAttrEx attr; attr.SetFlags(wxTEXT_ATTR_CHARACTER); - if (GetStyle(GetCaretPosition(), attr)) + // If at the start of a paragraph, use the next position. + long pos = GetAdjustedCaretPosition(GetCaretPosition()); + + if (GetStyle(pos, attr)) { SetDefaultStyle(attr); return true; @@ -2587,5 +2631,17 @@ long wxRichTextCtrl::GetFirstVisiblePosition() const return 0; } +/// The adjusted caret position is the character position adjusted to take +/// into account whether we're at the start of a paragraph, in which case +/// style information should be taken from the next position, not current one. +long wxRichTextCtrl::GetAdjustedCaretPosition(long caretPos) const +{ + wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(caretPos+1); + + if (para && (caretPos+1 == para->GetRange().GetStart())) + caretPos ++; + return caretPos; +} + #endif // wxUSE_RICHTEXT -- 2.47.2