From e637208a3210dfae3ee044cc5b7179fd76082769 Mon Sep 17 00:00:00 2001 From: Julian Smart Date: Tue, 12 Sep 2006 10:19:45 +0000 Subject: [PATCH] Style listbox now shows current style Added combo control for selecting styles Updated the sample to show the combo control git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@41174 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- include/wx/richtext/richtextstyles.h | 151 +++++++++++++++- samples/richtext/richtext.cpp | 7 + src/richtext/richtextstyles.cpp | 250 +++++++++++++++++++++++++-- 3 files changed, 389 insertions(+), 19 deletions(-) diff --git a/include/wx/richtext/richtextstyles.h b/include/wx/richtext/richtextstyles.h index 08ff7e683b..997d3e9744 100644 --- a/include/wx/richtext/richtextstyles.h +++ b/include/wx/richtext/richtextstyles.h @@ -2,9 +2,9 @@ // Name: richtextstyles.h // Purpose: Style management for wxRichTextCtrl // Author: Julian Smart -// Modified by: +// Modified by: // Created: 2005-09-30 -// RCS-ID: +// RCS-ID: // Copyright: (c) Julian Smart // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// @@ -24,6 +24,10 @@ #include "wx/htmllbox.h" #endif +#if wxUSE_COMBOCTRL +#include "wx/combo.h" +#endif + /*! * Forward declarations */ @@ -188,10 +192,23 @@ class WXDLLIMPEXP_RICHTEXT wxRichTextStyleListBox: public wxHtmlListBox DECLARE_EVENT_TABLE() public: + wxRichTextStyleListBox() + { + Init(); + } wxRichTextStyleListBox(wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = 0); virtual ~wxRichTextStyleListBox(); + void Init() + { + m_styleSheet = NULL; + m_richTextCtrl = NULL; + } + + bool Create(wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, long style = 0); + /// Creates a suitable HTML fragment for a definition wxString CreateHTML(wxRichTextStyleDefinition* def) const; @@ -203,26 +220,42 @@ public: void SetRichTextCtrl(wxRichTextCtrl* ctrl) { m_richTextCtrl = ctrl; } wxRichTextCtrl* GetRichTextCtrl() const { return m_richTextCtrl; } - // Get style for index + /// Get style for index wxRichTextStyleDefinition* GetStyle(size_t i) const ; + /// Get index for style name + int GetIndexForStyle(const wxString& name) const ; + + /// Set selection for string, returning the index. + int SetStyleSelection(const wxString& name); + /// Updates the list void UpdateStyles(); + /// Do selection + void DoSelection(int i); + /// React to selection void OnSelect(wxCommandEvent& event); /// Left click void OnLeftDown(wxMouseEvent& event); + /// Auto-select from style under caret in idle time + void OnIdle(wxIdleEvent& event); + #if 0 virtual wxColour GetSelectedTextColour(const wxColour& colFg) const; virtual wxColour GetSelectedTextBgColour(const wxColour& colBg) const; #endif - // Convert units in tends of a millimetre to device units + /// Convert units in tends of a millimetre to device units int ConvertTenthsMMToPixels(wxDC& dc, int units) const; + /// Can we set the selection based on the editor caret position? + /// Need to override this if being used in a combobox popup + virtual bool CanAutoSetSelection() { return true; } + protected: /// Returns the HTML for this item virtual wxString OnGetItem(size_t n) const; @@ -232,7 +265,117 @@ private: wxRichTextStyleSheet* m_styleSheet; wxRichTextCtrl* m_richTextCtrl; }; + +#if wxUSE_COMBOCTRL + +/*! + * Style drop-down for a wxComboCtrl + */ + +class wxRichTextStyleComboPopup : public wxRichTextStyleListBox, public wxComboPopup +{ +public: + virtual void Init() + { + m_itemHere = -1; // hot item in list + m_value = -1; + } + + virtual bool Create( wxWindow* parent ) + { + return wxRichTextStyleListBox::Create(parent, wxID_ANY, + wxPoint(0,0), wxDefaultSize, + wxSIMPLE_BORDER); + } + + virtual wxWindow *GetControl() { return this; } + + virtual void SetStringValue( const wxString& s ); + + virtual wxString GetStringValue() const; + + /// Can we set the selection based on the editor caret position? + // virtual bool CanAutoSetSelection() { return ((m_combo == NULL) || !m_combo->IsPopupShown()); } + virtual bool CanAutoSetSelection() { return false; } + + // + // Popup event handlers + // + + // Mouse hot-tracking + void OnMouseMove(wxMouseEvent& event); + + // On mouse left, set the value and close the popup + void OnMouseClick(wxMouseEvent& WXUNUSED(event)); + +protected: + + int m_itemHere; // hot item in popup + int m_value; + +private: + DECLARE_EVENT_TABLE() +}; + +/*! + * wxRichTextStyleComboCtrl + * A combo for applying styles. + */ + +class WXDLLIMPEXP_RICHTEXT wxRichTextStyleComboCtrl: public wxComboCtrl +{ + DECLARE_CLASS(wxRichTextStyleComboCtrl) + DECLARE_EVENT_TABLE() + +public: + wxRichTextStyleComboCtrl() + { + Init(); + } + + wxRichTextStyleComboCtrl(wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, long style = wxCB_READONLY) + { + Init(); + Create(parent, id, pos, size, style); + } + + virtual ~wxRichTextStyleComboCtrl() {} + + void Init() + { + m_stylePopup = NULL; + } + + bool Create(wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, long style = 0); + + /// Updates the list + void UpdateStyles() { m_stylePopup->UpdateStyles(); } + + /// Associates the control with a style manager + void SetStyleSheet(wxRichTextStyleSheet* styleSheet) { m_stylePopup->SetStyleSheet(styleSheet); } + wxRichTextStyleSheet* GetStyleSheet() const { return m_stylePopup->GetStyleSheet(); } + + /// Associates the control with a wxRichTextCtrl + void SetRichTextCtrl(wxRichTextCtrl* ctrl) { m_stylePopup->SetRichTextCtrl(ctrl); } + wxRichTextCtrl* GetRichTextCtrl() const { return m_stylePopup->GetRichTextCtrl(); } + + /// Gets the style popup + wxRichTextStyleComboPopup* GetStylePopup() const { return m_stylePopup; } + + /// Auto-select from style under caret in idle time + void OnIdle(wxIdleEvent& event); + +protected: + wxRichTextStyleComboPopup* m_stylePopup; +}; + +#endif + // wxUSE_COMBOCTRL + #endif + // wxUSE_HTML #endif // wxUSE_RICHTEXT diff --git a/samples/richtext/richtext.cpp b/samples/richtext/richtext.cpp index 2c8d0a3df6..37f0089f6b 100644 --- a/samples/richtext/richtext.cpp +++ b/samples/richtext/richtext.cpp @@ -490,6 +490,9 @@ MyFrame::MyFrame(const wxString& title, wxWindowID id, const wxPoint& pos, toolBar->AddSeparator(); toolBar->AddTool(ID_FORMAT_FONT, wxBitmap(font_xpm), wxNullBitmap, false, -1, -1, (wxObject *) NULL, _("Font")); + wxRichTextStyleComboCtrl* combo = new wxRichTextStyleComboCtrl(toolBar, wxID_ANY, wxDefaultPosition, wxSize(200, -1)); + toolBar->AddControl(combo); + toolBar->Realize(); wxSplitterWindow* splitter = new wxSplitterWindow(this, wxID_ANY, wxDefaultPosition, GetClientSize(), wxSP_NO_XP_THEME|wxSP_3D|wxSP_LIVE_UPDATE); @@ -503,6 +506,10 @@ MyFrame::MyFrame(const wxString& title, wxWindowID id, const wxPoint& pos, m_richTextCtrl->SetFont(font); + combo->SetStyleSheet(wxGetApp().GetStyleSheet()); + combo->SetRichTextCtrl(m_richTextCtrl); + combo->UpdateStyles(); + wxRichTextStyleListBox* styleListBox = new wxRichTextStyleListBox(splitter, wxID_ANY); wxSize display = wxGetDisplaySize(); diff --git a/src/richtext/richtextstyles.cpp b/src/richtext/richtextstyles.cpp index 309a110176..183daea53a 100644 --- a/src/richtext/richtextstyles.cpp +++ b/src/richtext/richtextstyles.cpp @@ -100,13 +100,20 @@ IMPLEMENT_CLASS(wxRichTextStyleListBox, wxHtmlListBox) BEGIN_EVENT_TABLE(wxRichTextStyleListBox, wxHtmlListBox) EVT_LISTBOX(wxID_ANY, wxRichTextStyleListBox::OnSelect) EVT_LEFT_DOWN(wxRichTextStyleListBox::OnLeftDown) + EVT_IDLE(wxRichTextStyleListBox::OnIdle) END_EVENT_TABLE() wxRichTextStyleListBox::wxRichTextStyleListBox(wxWindow* parent, wxWindowID id, const wxPoint& pos, - const wxSize& size, long style): wxHtmlListBox(parent, id, pos, size, style) + const wxSize& size, long style) { - m_styleSheet = NULL; - m_richTextCtrl = NULL; + Init(); + Create(parent, id, pos, size, style); +} + +bool wxRichTextStyleListBox::Create(wxWindow* parent, wxWindowID id, const wxPoint& pos, + const wxSize& size, long style) +{ + return wxHtmlListBox::Create(parent, id, pos, size, style); } wxRichTextStyleListBox::~wxRichTextStyleListBox() @@ -164,6 +171,37 @@ void wxRichTextStyleListBox::UpdateStyles() } } +// Get index for style name +int wxRichTextStyleListBox::GetIndexForStyle(const wxString& name) const +{ + if (GetStyleSheet()) + { + int i; + for (i = 0; i < (int) GetStyleSheet()->GetParagraphStyleCount(); i++) + { + wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->GetParagraphStyle(i); + if (def->GetName() == name) + return i; + } + for (i = 0; i < (int) GetStyleSheet()->GetCharacterStyleCount(); i++) + { + wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->GetCharacterStyle(i); + if (def->GetName() == name) + return i + (int) GetStyleSheet()->GetParagraphStyleCount(); + } + } + return -1; +} + +/// Set selection for string +int wxRichTextStyleListBox::SetStyleSelection(const wxString& name) +{ + int i = GetIndexForStyle(name); + if (i > -1) + SetSelection(i); + return i; +} + // Convert a colour to a 6-digit hex string static wxString ColourToHexString(const wxColour& col) { @@ -269,24 +307,62 @@ void wxRichTextStyleListBox::OnLeftDown(wxMouseEvent& event) wxVListBox::OnLeftDown(event); int item = HitTest(event.GetPosition()); + if (item != wxNOT_FOUND) + DoSelection(item); +} - if ( item != wxNOT_FOUND ) +/// Auto-select from style under caret in idle time +void wxRichTextStyleListBox::OnIdle(wxIdleEvent& event) +{ + if (CanAutoSetSelection() && GetRichTextCtrl()) { - wxRichTextStyleDefinition* def = GetStyle(item); - if (def && GetRichTextCtrl()) + wxRichTextParagraph* para = GetRichTextCtrl()->GetBuffer().GetParagraphAtPosition(GetRichTextCtrl()->GetCaretPosition()); + wxRichTextObject* obj = GetRichTextCtrl()->GetBuffer().GetLeafObjectAtPosition(GetRichTextCtrl()->GetCaretPosition()); + + wxString styleName; + + // Take into account current default style just chosen by user + if (GetRichTextCtrl()->IsDefaultStyleShowing()) { - wxRichTextRange range(m_richTextCtrl->GetInsertionPoint(), m_richTextCtrl->GetInsertionPoint()); + if (!GetRichTextCtrl()->GetDefaultStyleEx().GetCharacterStyleName().IsEmpty()) + styleName = GetRichTextCtrl()->GetDefaultStyleEx().GetCharacterStyleName(); + else if (!GetRichTextCtrl()->GetDefaultStyleEx().GetParagraphStyleName().IsEmpty()) + styleName = GetRichTextCtrl()->GetDefaultStyleEx().GetParagraphStyleName(); + } + else if (obj && !obj->GetAttributes().GetCharacterStyleName().IsEmpty()) + { + styleName = obj->GetAttributes().GetCharacterStyleName(); + } + else if (para && !para->GetAttributes().GetParagraphStyleName().IsEmpty()) + { + styleName = para->GetAttributes().GetParagraphStyleName(); + } - // Flags are defined within each definition, so only certain - // attributes are applied. - wxRichTextAttr attr(def->GetStyle()); + int sel = GetSelection(); + if (!styleName.IsEmpty()) + { + // Don't do the selection if it's already set + if (sel == GetIndexForStyle(styleName)) + return; - if (m_richTextCtrl->HasSelection()) - m_richTextCtrl->SetStyle(m_richTextCtrl->GetSelectionRange(), attr); - else - m_richTextCtrl->SetDefaultStyle(attr); + SetStyleSelection(styleName); + } + else if (sel != -1) + SetSelection(-1); + } + event.Skip(); +} - m_richTextCtrl->SetFocus(); +/// Do selection +void wxRichTextStyleListBox::DoSelection(int item) +{ + if ( item != wxNOT_FOUND ) + { + wxRichTextStyleDefinition* def = GetStyle(item); + if (def && GetRichTextCtrl()) + { + GetRichTextCtrl()->ApplyStyle(def); + GetRichTextCtrl()->SetFocus(); } } } @@ -303,6 +379,150 @@ wxColour wxRichTextStyleListBox::GetSelectedTextBgColour(const wxColour& colBg) } #endif +#if wxUSE_COMBOCTRL + +/*! + * Style drop-down for a wxComboCtrl + */ + + +BEGIN_EVENT_TABLE(wxRichTextStyleComboPopup, wxRichTextStyleListBox) + EVT_MOTION(wxRichTextStyleComboPopup::OnMouseMove) + EVT_LEFT_DOWN(wxRichTextStyleComboPopup::OnMouseClick) +END_EVENT_TABLE() + +void wxRichTextStyleComboPopup::SetStringValue( const wxString& s ) +{ + m_value = SetStyleSelection(s); +} + +wxString wxRichTextStyleComboPopup::GetStringValue() const +{ + int sel = m_value; + if (sel > -1) + { + wxRichTextStyleDefinition* def = GetStyle(sel); + if (def) + return def->GetName(); + } + return wxEmptyString; +} + +// +// Popup event handlers +// + +// Mouse hot-tracking +void wxRichTextStyleComboPopup::OnMouseMove(wxMouseEvent& event) +{ + // Move selection to cursor if it is inside the popup + + int itemHere = wxRichTextStyleListBox::HitTest(event.GetPosition()); + if ( itemHere >= 0 ) + { + wxRichTextStyleListBox::SetSelection(itemHere); + m_itemHere = itemHere; + } + event.Skip(); +} + +// On mouse left, set the value and close the popup +void wxRichTextStyleComboPopup::OnMouseClick(wxMouseEvent& WXUNUSED(event)) +{ + if (m_itemHere >= 0) + m_value = m_itemHere; + + // Ordering is important, so we don't dismiss this popup accidentally + // by setting the focus elsewhere e.g. in DoSelection + Dismiss(); + + if (m_itemHere >= 0) + wxRichTextStyleListBox::DoSelection(m_itemHere); +} + +/*! + * wxRichTextStyleComboCtrl + * A combo for applying styles. + */ + +IMPLEMENT_CLASS(wxRichTextStyleComboCtrl, wxComboCtrl) + +BEGIN_EVENT_TABLE(wxRichTextStyleComboCtrl, wxComboCtrl) + EVT_IDLE(wxRichTextStyleComboCtrl::OnIdle) +END_EVENT_TABLE() + +bool wxRichTextStyleComboCtrl::Create(wxWindow* parent, wxWindowID id, const wxPoint& pos, + const wxSize& size, long style) +{ + if (!wxComboCtrl::Create(parent, id, wxEmptyString, pos, size, style)) + return false; + + SetPopupMaxHeight(400); + + m_stylePopup = new wxRichTextStyleComboPopup; + + SetPopupControl(m_stylePopup); + + return true; +} + +/// Auto-select from style under caret in idle time + +// TODO: must be able to show italic, bold, combinations +// in style box. Do we have a concept of automatic, temporary +// styles that are added whenever we wish to show a style +// that doesn't exist already? E.g. "Bold, Italic, Underline". +// Word seems to generate these things on the fly. +// If there's a named style already, it uses e.g. Heading1 + Bold, Italic +// If you unembolden text in a style that has bold, it uses the +// term "Not bold". +// TODO: order styles alphabetically. This means indexes can change, +// so need a different way to specify selections, i.e. by name. + +void wxRichTextStyleComboCtrl::OnIdle(wxIdleEvent& event) +{ + if (GetRichTextCtrl() && !IsPopupShown()) + { + wxRichTextParagraph* para = GetRichTextCtrl()->GetBuffer().GetParagraphAtPosition(GetRichTextCtrl()->GetCaretPosition()); + wxRichTextObject* obj = GetRichTextCtrl()->GetBuffer().GetLeafObjectAtPosition(GetRichTextCtrl()->GetCaretPosition()); + + wxString styleName; + + // Take into account current default style just chosen by user + if (GetRichTextCtrl()->IsDefaultStyleShowing()) + { + if (!GetRichTextCtrl()->GetDefaultStyleEx().GetCharacterStyleName().IsEmpty()) + styleName = GetRichTextCtrl()->GetDefaultStyleEx().GetCharacterStyleName(); + else if (!GetRichTextCtrl()->GetDefaultStyleEx().GetParagraphStyleName().IsEmpty()) + styleName = GetRichTextCtrl()->GetDefaultStyleEx().GetParagraphStyleName(); + } + else if (obj && !obj->GetAttributes().GetCharacterStyleName().IsEmpty()) + { + styleName = obj->GetAttributes().GetCharacterStyleName(); + } + else if (para && !para->GetAttributes().GetParagraphStyleName().IsEmpty()) + { + styleName = para->GetAttributes().GetParagraphStyleName(); + } + + wxString currentValue = GetValue(); + if (!styleName.IsEmpty()) + { + // Don't do the selection if it's already set + if (currentValue == styleName) + return; + + SetValue(styleName); + } + else if (!currentValue.IsEmpty()) + SetValue(wxEmptyString); + } + event.Skip(); +} + +#endif + // wxUSE_COMBOCTRL + #endif // wxUSE_HTML -- 2.47.2