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