1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/richtext/richtextstyles.cpp
3 // Purpose: Style management for wxRichTextCtrl
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
21 #include "wx/richtext/richtextstyles.h"
24 #include "wx/dcclient.h"
25 #include "wx/module.h"
28 #include "wx/filename.h"
29 #include "wx/clipbrd.h"
30 #include "wx/wfstream.h"
32 #include "wx/richtext/richtextctrl.h"
34 IMPLEMENT_CLASS(wxRichTextStyleDefinition
, wxObject
)
35 IMPLEMENT_CLASS(wxRichTextCharacterStyleDefinition
, wxRichTextStyleDefinition
)
36 IMPLEMENT_CLASS(wxRichTextParagraphStyleDefinition
, wxRichTextStyleDefinition
)
42 IMPLEMENT_CLASS(wxRichTextStyleSheet
, wxObject
)
45 void wxRichTextStyleSheet::Init()
49 /// Add a definition to one of the style lists
50 bool wxRichTextStyleSheet::AddStyle(wxList
& list
, wxRichTextStyleDefinition
* def
)
58 bool wxRichTextStyleSheet::RemoveStyle(wxList
& list
, wxRichTextStyleDefinition
* def
, bool deleteStyle
)
60 wxList::compatibility_iterator node
= list
.Find(def
);
63 wxRichTextStyleDefinition
* def
= (wxRichTextStyleDefinition
*) node
->GetData();
73 /// Find a definition by name
74 wxRichTextStyleDefinition
* wxRichTextStyleSheet::FindStyle(const wxList
& list
, const wxString
& name
) const
76 for (wxList::compatibility_iterator node
= list
.GetFirst(); node
; node
= node
->GetNext())
78 wxRichTextStyleDefinition
* def
= (wxRichTextStyleDefinition
*) node
->GetData();
79 if (def
->GetName().Lower() == name
.Lower())
86 void wxRichTextStyleSheet::DeleteStyles()
88 WX_CLEAR_LIST(wxList
, m_characterStyleDefinitions
);
89 WX_CLEAR_LIST(wxList
, m_paragraphStyleDefinitions
);
94 * wxRichTextStyleListBox class declaration
95 * A listbox to display styles.
98 IMPLEMENT_CLASS(wxRichTextStyleListBox
, wxHtmlListBox
)
100 BEGIN_EVENT_TABLE(wxRichTextStyleListBox
, wxHtmlListBox
)
101 EVT_LISTBOX(wxID_ANY
, wxRichTextStyleListBox::OnSelect
)
102 EVT_LEFT_DOWN(wxRichTextStyleListBox::OnLeftDown
)
103 EVT_IDLE(wxRichTextStyleListBox::OnIdle
)
106 wxRichTextStyleListBox::wxRichTextStyleListBox(wxWindow
* parent
, wxWindowID id
, const wxPoint
& pos
,
107 const wxSize
& size
, long style
)
110 Create(parent
, id
, pos
, size
, style
);
113 bool wxRichTextStyleListBox::Create(wxWindow
* parent
, wxWindowID id
, const wxPoint
& pos
,
114 const wxSize
& size
, long style
)
116 return wxHtmlListBox::Create(parent
, id
, pos
, size
, style
);
119 wxRichTextStyleListBox::~wxRichTextStyleListBox()
123 /// Returns the HTML for this item
124 wxString
wxRichTextStyleListBox::OnGetItem(size_t n
) const
126 if (!GetStyleSheet())
127 return wxEmptyString
;
129 // First paragraph styles, then character
130 if (n
< GetStyleSheet()->GetParagraphStyleCount())
132 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->GetParagraphStyle(n
);
134 wxString str
= CreateHTML(def
);
138 if ((n
- GetStyleSheet()->GetParagraphStyleCount()) < GetStyleSheet()->GetCharacterStyleCount())
140 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->GetCharacterStyle(n
- GetStyleSheet()->GetParagraphStyleCount());
142 wxString str
= CreateHTML(def
);
145 return wxEmptyString
;
148 // Get style for index
149 wxRichTextStyleDefinition
* wxRichTextStyleListBox::GetStyle(size_t i
) const
151 if (!GetStyleSheet())
154 // First paragraph styles, then character
155 if (i
< GetStyleSheet()->GetParagraphStyleCount())
156 return GetStyleSheet()->GetParagraphStyle(i
);
158 if ((i
- GetStyleSheet()->GetParagraphStyleCount()) < GetStyleSheet()->GetCharacterStyleCount())
159 return GetStyleSheet()->GetCharacterStyle(i
- GetStyleSheet()->GetParagraphStyleCount());
165 void wxRichTextStyleListBox::UpdateStyles()
169 SetItemCount(GetStyleSheet()->GetParagraphStyleCount()+GetStyleSheet()->GetCharacterStyleCount());
174 // Get index for style name
175 int wxRichTextStyleListBox::GetIndexForStyle(const wxString
& name
) const
180 for (i
= 0; i
< (int) GetStyleSheet()->GetParagraphStyleCount(); i
++)
182 wxRichTextParagraphStyleDefinition
* def
= GetStyleSheet()->GetParagraphStyle(i
);
183 if (def
->GetName() == name
)
186 for (i
= 0; i
< (int) GetStyleSheet()->GetCharacterStyleCount(); i
++)
188 wxRichTextCharacterStyleDefinition
* def
= GetStyleSheet()->GetCharacterStyle(i
);
189 if (def
->GetName() == name
)
190 return i
+ (int) GetStyleSheet()->GetParagraphStyleCount();
196 /// Set selection for string
197 int wxRichTextStyleListBox::SetStyleSelection(const wxString
& name
)
199 int i
= GetIndexForStyle(name
);
205 // Convert a colour to a 6-digit hex string
206 static wxString
ColourToHexString(const wxColour
& col
)
210 hex
+= wxDecToHex(col
.Red());
211 hex
+= wxDecToHex(col
.Green());
212 hex
+= wxDecToHex(col
.Blue());
217 /// Creates a suitable HTML fragment for a definition
218 wxString
wxRichTextStyleListBox::CreateHTML(wxRichTextStyleDefinition
* def
) const
220 wxString
str(wxT("<table><tr>"));
222 if (def
->GetStyle().GetLeftIndent() > 0)
224 wxClientDC
dc((wxWindow
*) this);
226 str
<< wxT("<td width=") << ConvertTenthsMMToPixels(dc
, def
->GetStyle().GetLeftIndent()) << wxT("></td>");
229 str
<< wxT("<td nowrap>");
233 // Standard size is 12, say
234 size
+= 12 - def
->GetStyle().GetFontSize();
238 str
<< wxT(" size=") << size
;
240 if (!def
->GetStyle().GetFontFaceName().IsEmpty())
241 str
<< wxT(" face=\"") << def
->GetStyle().GetFontFaceName() << wxT("\"");
243 if (def
->GetStyle().GetTextColour().Ok())
244 str
<< wxT(" color=\"#") << ColourToHexString(def
->GetStyle().GetTextColour()) << wxT("\"");
248 bool hasBold
= false;
249 bool hasItalic
= false;
250 bool hasUnderline
= false;
252 if (def
->GetStyle().GetFontWeight() == wxBOLD
)
254 if (def
->GetStyle().GetFontStyle() == wxITALIC
)
256 if (def
->GetStyle().GetFontUnderlined())
266 str
+= def
->GetName();
275 str
<< wxT("</font>");
277 str
+= wxT("</td></tr></table>");
281 // Convert units in tends of a millimetre to device units
282 int wxRichTextStyleListBox::ConvertTenthsMMToPixels(wxDC
& dc
, int units
) const
284 int ppi
= dc
.GetPPI().x
;
286 // There are ppi pixels in 254.1 "1/10 mm"
288 double pixels
= ((double) units
* (double)ppi
) / 254.1;
293 /// React to selection
294 void wxRichTextStyleListBox::OnSelect(wxCommandEvent
& WXUNUSED(event
))
297 wxRichTextStyleDefinition
* def
= GetStyle(event
.GetSelection());
300 wxMessageBox(def
->GetName());
305 void wxRichTextStyleListBox::OnLeftDown(wxMouseEvent
& event
)
307 wxVListBox::OnLeftDown(event
);
309 int item
= HitTest(event
.GetPosition());
310 if (item
!= wxNOT_FOUND
)
314 /// Auto-select from style under caret in idle time
315 void wxRichTextStyleListBox::OnIdle(wxIdleEvent
& event
)
317 if (CanAutoSetSelection() && GetRichTextCtrl())
319 wxRichTextParagraph
* para
= GetRichTextCtrl()->GetBuffer().GetParagraphAtPosition(GetRichTextCtrl()->GetCaretPosition());
320 wxRichTextObject
* obj
= GetRichTextCtrl()->GetBuffer().GetLeafObjectAtPosition(GetRichTextCtrl()->GetCaretPosition());
324 // Take into account current default style just chosen by user
325 if (GetRichTextCtrl()->IsDefaultStyleShowing())
327 if (!GetRichTextCtrl()->GetDefaultStyleEx().GetCharacterStyleName().IsEmpty())
328 styleName
= GetRichTextCtrl()->GetDefaultStyleEx().GetCharacterStyleName();
329 else if (!GetRichTextCtrl()->GetDefaultStyleEx().GetParagraphStyleName().IsEmpty())
330 styleName
= GetRichTextCtrl()->GetDefaultStyleEx().GetParagraphStyleName();
332 else if (obj
&& !obj
->GetAttributes().GetCharacterStyleName().IsEmpty())
334 styleName
= obj
->GetAttributes().GetCharacterStyleName();
336 else if (para
&& !para
->GetAttributes().GetParagraphStyleName().IsEmpty())
338 styleName
= para
->GetAttributes().GetParagraphStyleName();
341 int sel
= GetSelection();
342 if (!styleName
.IsEmpty())
344 // Don't do the selection if it's already set
345 if (sel
== GetIndexForStyle(styleName
))
348 SetStyleSelection(styleName
);
357 void wxRichTextStyleListBox::DoSelection(int item
)
359 if ( item
!= wxNOT_FOUND
)
361 wxRichTextStyleDefinition
* def
= GetStyle(item
);
362 if (def
&& GetRichTextCtrl())
364 GetRichTextCtrl()->ApplyStyle(def
);
365 GetRichTextCtrl()->SetFocus();
371 wxColour
wxRichTextStyleListBox::GetSelectedTextColour(const wxColour
& colFg
) const
376 wxColour
wxRichTextStyleListBox::GetSelectedTextBgColour(const wxColour
& colBg
) const
385 * Style drop-down for a wxComboCtrl
389 BEGIN_EVENT_TABLE(wxRichTextStyleComboPopup
, wxRichTextStyleListBox
)
390 EVT_MOTION(wxRichTextStyleComboPopup::OnMouseMove
)
391 EVT_LEFT_DOWN(wxRichTextStyleComboPopup::OnMouseClick
)
394 void wxRichTextStyleComboPopup::SetStringValue( const wxString
& s
)
396 m_value
= SetStyleSelection(s
);
399 wxString
wxRichTextStyleComboPopup::GetStringValue() const
404 wxRichTextStyleDefinition
* def
= GetStyle(sel
);
406 return def
->GetName();
408 return wxEmptyString
;
412 // Popup event handlers
415 // Mouse hot-tracking
416 void wxRichTextStyleComboPopup::OnMouseMove(wxMouseEvent
& event
)
418 // Move selection to cursor if it is inside the popup
420 int itemHere
= wxRichTextStyleListBox::HitTest(event
.GetPosition());
423 wxRichTextStyleListBox::SetSelection(itemHere
);
424 m_itemHere
= itemHere
;
429 // On mouse left, set the value and close the popup
430 void wxRichTextStyleComboPopup::OnMouseClick(wxMouseEvent
& WXUNUSED(event
))
433 m_value
= m_itemHere
;
435 // Ordering is important, so we don't dismiss this popup accidentally
436 // by setting the focus elsewhere e.g. in DoSelection
440 wxRichTextStyleListBox::DoSelection(m_itemHere
);
444 * wxRichTextStyleComboCtrl
445 * A combo for applying styles.
448 IMPLEMENT_CLASS(wxRichTextStyleComboCtrl
, wxComboCtrl
)
450 BEGIN_EVENT_TABLE(wxRichTextStyleComboCtrl
, wxComboCtrl
)
451 EVT_IDLE(wxRichTextStyleComboCtrl::OnIdle
)
454 bool wxRichTextStyleComboCtrl::Create(wxWindow
* parent
, wxWindowID id
, const wxPoint
& pos
,
455 const wxSize
& size
, long style
)
457 if (!wxComboCtrl::Create(parent
, id
, wxEmptyString
, pos
, size
, style
))
460 SetPopupMaxHeight(400);
462 m_stylePopup
= new wxRichTextStyleComboPopup
;
464 SetPopupControl(m_stylePopup
);
469 /// Auto-select from style under caret in idle time
471 // TODO: must be able to show italic, bold, combinations
472 // in style box. Do we have a concept of automatic, temporary
473 // styles that are added whenever we wish to show a style
474 // that doesn't exist already? E.g. "Bold, Italic, Underline".
475 // Word seems to generate these things on the fly.
476 // If there's a named style already, it uses e.g. Heading1 + Bold, Italic
477 // If you unembolden text in a style that has bold, it uses the
479 // TODO: order styles alphabetically. This means indexes can change,
480 // so need a different way to specify selections, i.e. by name.
482 void wxRichTextStyleComboCtrl::OnIdle(wxIdleEvent
& event
)
484 if (GetRichTextCtrl() && !IsPopupShown())
486 wxRichTextParagraph
* para
= GetRichTextCtrl()->GetBuffer().GetParagraphAtPosition(GetRichTextCtrl()->GetCaretPosition());
487 wxRichTextObject
* obj
= GetRichTextCtrl()->GetBuffer().GetLeafObjectAtPosition(GetRichTextCtrl()->GetCaretPosition());
491 // Take into account current default style just chosen by user
492 if (GetRichTextCtrl()->IsDefaultStyleShowing())
494 if (!GetRichTextCtrl()->GetDefaultStyleEx().GetCharacterStyleName().IsEmpty())
495 styleName
= GetRichTextCtrl()->GetDefaultStyleEx().GetCharacterStyleName();
496 else if (!GetRichTextCtrl()->GetDefaultStyleEx().GetParagraphStyleName().IsEmpty())
497 styleName
= GetRichTextCtrl()->GetDefaultStyleEx().GetParagraphStyleName();
499 else if (obj
&& !obj
->GetAttributes().GetCharacterStyleName().IsEmpty())
501 styleName
= obj
->GetAttributes().GetCharacterStyleName();
503 else if (para
&& !para
->GetAttributes().GetParagraphStyleName().IsEmpty())
505 styleName
= para
->GetAttributes().GetParagraphStyleName();
508 wxString currentValue
= GetValue();
509 if (!styleName
.IsEmpty())
511 // Don't do the selection if it's already set
512 if (currentValue
== styleName
)
517 else if (!currentValue
.IsEmpty())
518 SetValue(wxEmptyString
);