Fixed #11212 [RichText sample] Assert/Crash when Styles combo box opened
[wxWidgets.git] / src / richtext / richtextstyles.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/richtext/richtextstyles.cpp
3 // Purpose: Style management for wxRichTextCtrl
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 2005-09-30
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
14
15 #ifdef __BORLANDC__
16 #pragma hdrstop
17 #endif
18
19 #if wxUSE_RICHTEXT
20
21 #include "wx/richtext/richtextstyles.h"
22
23 #ifndef WX_PRECOMP
24 #include "wx/wx.h"
25 #endif
26
27 #include "wx/filename.h"
28 #include "wx/clipbrd.h"
29 #include "wx/wfstream.h"
30 #include "wx/settings.h"
31
32 #include "wx/richtext/richtextctrl.h"
33
34 IMPLEMENT_CLASS(wxRichTextStyleDefinition, wxObject)
35 IMPLEMENT_CLASS(wxRichTextCharacterStyleDefinition, wxRichTextStyleDefinition)
36 IMPLEMENT_CLASS(wxRichTextParagraphStyleDefinition, wxRichTextStyleDefinition)
37 IMPLEMENT_CLASS(wxRichTextListStyleDefinition, wxRichTextParagraphStyleDefinition)
38
39 /*!
40 * A definition
41 */
42
43 void wxRichTextStyleDefinition::Copy(const wxRichTextStyleDefinition& def)
44 {
45 m_name = def.m_name;
46 m_baseStyle = def.m_baseStyle;
47 m_style = def.m_style;
48 m_description = def.m_description;
49 }
50
51 bool wxRichTextStyleDefinition::Eq(const wxRichTextStyleDefinition& def) const
52 {
53 return (m_name == def.m_name && m_baseStyle == def.m_baseStyle && m_style == def.m_style);
54 }
55
56 /// Gets the style combined with the base style
57 wxTextAttr wxRichTextStyleDefinition::GetStyleMergedWithBase(const wxRichTextStyleSheet* sheet) const
58 {
59 if (!m_baseStyle.IsEmpty())
60 {
61 wxRichTextStyleDefinition* baseStyle = sheet->FindStyle(m_baseStyle);
62 if (baseStyle)
63 {
64 wxTextAttr baseAttr = baseStyle->GetStyleMergedWithBase(sheet);
65 baseAttr.Apply(m_style, NULL);
66 return baseAttr;
67 }
68 }
69 return m_style;
70 }
71
72 /*!
73 * Paragraph style definition
74 */
75
76 void wxRichTextParagraphStyleDefinition::Copy(const wxRichTextParagraphStyleDefinition& def)
77 {
78 wxRichTextStyleDefinition::Copy(def);
79
80 m_nextStyle = def.m_nextStyle;
81 }
82
83 bool wxRichTextParagraphStyleDefinition::operator ==(const wxRichTextParagraphStyleDefinition& def) const
84 {
85 return (Eq(def) && m_nextStyle == def.m_nextStyle);
86 }
87
88 /*!
89 * List style definition
90 */
91
92 void wxRichTextListStyleDefinition::Copy(const wxRichTextListStyleDefinition& def)
93 {
94 wxRichTextParagraphStyleDefinition::Copy(def);
95
96 int i;
97 for (i = 0; i < 10; i++)
98 m_levelStyles[i] = def.m_levelStyles[i];
99 }
100
101 bool wxRichTextListStyleDefinition::operator ==(const wxRichTextListStyleDefinition& def) const
102 {
103 if (!Eq(def))
104 return false;
105 int i;
106 for (i = 0; i < 10; i++)
107 if (!(m_levelStyles[i] == def.m_levelStyles[i]))
108 return false;
109
110 return true;
111 }
112
113 /// Sets/gets the attributes for the given level
114 void wxRichTextListStyleDefinition::SetLevelAttributes(int i, const wxTextAttr& attr)
115 {
116 wxASSERT( (i >= 0 && i < 10) );
117 if (i >= 0 && i < 10)
118 m_levelStyles[i] = attr;
119 }
120
121 const wxTextAttr* wxRichTextListStyleDefinition::GetLevelAttributes(int i) const
122 {
123 wxASSERT( (i >= 0 && i < 10) );
124 if (i >= 0 && i < 10)
125 return & m_levelStyles[i];
126 else
127 return NULL;
128 }
129
130 wxTextAttr* wxRichTextListStyleDefinition::GetLevelAttributes(int i)
131 {
132 wxASSERT( (i >= 0 && i < 10) );
133 if (i >= 0 && i < 10)
134 return & m_levelStyles[i];
135 else
136 return NULL;
137 }
138
139 /// Convenience function for setting the major attributes for a list level specification
140 void wxRichTextListStyleDefinition::SetAttributes(int i, int leftIndent, int leftSubIndent, int bulletStyle, const wxString& bulletSymbol)
141 {
142 wxASSERT( (i >= 0 && i < 10) );
143 if (i >= 0 && i < 10)
144 {
145 wxTextAttr attr;
146
147 attr.SetBulletStyle(bulletStyle);
148 attr.SetLeftIndent(leftIndent, leftSubIndent);
149
150 if (!bulletSymbol.IsEmpty())
151 {
152 if (bulletStyle & wxTEXT_ATTR_BULLET_STYLE_SYMBOL)
153 attr.SetBulletText(bulletSymbol);
154 else
155 attr.SetBulletName(bulletSymbol);
156 }
157
158 m_levelStyles[i] = attr;
159 }
160 }
161
162 /// Finds the level corresponding to the given indentation
163 int wxRichTextListStyleDefinition::FindLevelForIndent(int indent) const
164 {
165 int i;
166 for (i = 0; i < 10; i++)
167 {
168 if (indent < m_levelStyles[i].GetLeftIndent())
169 {
170 if (i > 0)
171 return i - 1;
172 else
173 return 0;
174 }
175 }
176 return 9;
177 }
178
179 /// Combine the list style with a paragraph style, using the given indent (from which
180 /// an appropriate level is found)
181 wxTextAttr wxRichTextListStyleDefinition::CombineWithParagraphStyle(int indent, const wxTextAttr& paraStyle, wxRichTextStyleSheet* styleSheet)
182 {
183 int listLevel = FindLevelForIndent(indent);
184
185 wxTextAttr attr(*GetLevelAttributes(listLevel));
186 int oldLeftIndent = attr.GetLeftIndent();
187 int oldLeftSubIndent = attr.GetLeftSubIndent();
188
189 // First apply the overall paragraph style, if any
190 if (styleSheet)
191 attr.Apply(GetStyleMergedWithBase(styleSheet));
192 else
193 attr.Apply(GetStyle());
194
195 // Then apply paragraph style, e.g. from paragraph style definition
196 attr.Apply(paraStyle);
197
198 // We override the indents according to the list definition
199 attr.SetLeftIndent(oldLeftIndent, oldLeftSubIndent);
200
201 return attr;
202 }
203
204 /// Combine the base and list style, using the given indent (from which
205 /// an appropriate level is found)
206 wxTextAttr wxRichTextListStyleDefinition::GetCombinedStyle(int indent, wxRichTextStyleSheet* styleSheet)
207 {
208 int listLevel = FindLevelForIndent(indent);
209 return GetCombinedStyleForLevel(listLevel, styleSheet);
210 }
211
212 /// Combine the base and list style, using the given indent (from which
213 /// an appropriate level is found)
214 wxTextAttr wxRichTextListStyleDefinition::GetCombinedStyleForLevel(int listLevel, wxRichTextStyleSheet* styleSheet)
215 {
216 wxTextAttr attr(*GetLevelAttributes(listLevel));
217 int oldLeftIndent = attr.GetLeftIndent();
218 int oldLeftSubIndent = attr.GetLeftSubIndent();
219
220 // Apply the overall paragraph style, if any
221 if (styleSheet)
222 attr.Apply(GetStyleMergedWithBase(styleSheet));
223 else
224 attr.Apply(GetStyle());
225
226 // We override the indents according to the list definition
227 attr.SetLeftIndent(oldLeftIndent, oldLeftSubIndent);
228
229 return attr;
230 }
231
232 /// Is this a numbered list?
233 bool wxRichTextListStyleDefinition::IsNumbered(int i) const
234 {
235 return (0 != (GetLevelAttributes(i)->GetFlags() &
236 (wxTEXT_ATTR_BULLET_STYLE_ARABIC|wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER|wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER|
237 wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER|wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER)));
238 }
239
240 /*!
241 * The style manager
242 */
243
244 IMPLEMENT_CLASS(wxRichTextStyleSheet, wxObject)
245
246 wxRichTextStyleSheet::~wxRichTextStyleSheet()
247 {
248 DeleteStyles();
249
250 if (m_nextSheet)
251 m_nextSheet->m_previousSheet = m_previousSheet;
252
253 if (m_previousSheet)
254 m_previousSheet->m_nextSheet = m_nextSheet;
255
256 m_previousSheet = NULL;
257 m_nextSheet = NULL;
258 }
259
260 /// Initialisation
261 void wxRichTextStyleSheet::Init()
262 {
263 m_previousSheet = NULL;
264 m_nextSheet = NULL;
265 }
266
267 /// Add a definition to one of the style lists
268 bool wxRichTextStyleSheet::AddStyle(wxList& list, wxRichTextStyleDefinition* def)
269 {
270 if (!list.Find(def))
271 list.Append(def);
272 return true;
273 }
274
275 /// Remove a style
276 bool wxRichTextStyleSheet::RemoveStyle(wxList& list, wxRichTextStyleDefinition* def, bool deleteStyle)
277 {
278 wxList::compatibility_iterator node = list.Find(def);
279 if (node)
280 {
281 wxRichTextStyleDefinition* def = (wxRichTextStyleDefinition*) node->GetData();
282 list.Erase(node);
283 if (deleteStyle)
284 delete def;
285 return true;
286 }
287 else
288 return false;
289 }
290
291 /// Remove a style
292 bool wxRichTextStyleSheet::RemoveStyle(wxRichTextStyleDefinition* def, bool deleteStyle)
293 {
294 if (RemoveParagraphStyle(def, deleteStyle))
295 return true;
296 if (RemoveCharacterStyle(def, deleteStyle))
297 return true;
298 if (RemoveListStyle(def, deleteStyle))
299 return true;
300 return false;
301 }
302
303 /// Find a definition by name
304 wxRichTextStyleDefinition* wxRichTextStyleSheet::FindStyle(const wxList& list, const wxString& name, bool recurse) const
305 {
306 for (wxList::compatibility_iterator node = list.GetFirst(); node; node = node->GetNext())
307 {
308 wxRichTextStyleDefinition* def = (wxRichTextStyleDefinition*) node->GetData();
309 if (def->GetName().Lower() == name.Lower())
310 return def;
311 }
312
313 if (m_nextSheet && recurse)
314 return m_nextSheet->FindStyle(list, name, recurse);
315
316 return NULL;
317 }
318
319 /// Delete all styles
320 void wxRichTextStyleSheet::DeleteStyles()
321 {
322 WX_CLEAR_LIST(wxList, m_characterStyleDefinitions);
323 WX_CLEAR_LIST(wxList, m_paragraphStyleDefinitions);
324 WX_CLEAR_LIST(wxList, m_listStyleDefinitions);
325 }
326
327 /// Insert into list of style sheets
328 bool wxRichTextStyleSheet::InsertSheet(wxRichTextStyleSheet* before)
329 {
330 m_previousSheet = before->m_previousSheet;
331 m_nextSheet = before;
332
333 before->m_previousSheet = this;
334 return true;
335 }
336
337 /// Append to list of style sheets
338 bool wxRichTextStyleSheet::AppendSheet(wxRichTextStyleSheet* after)
339 {
340 wxRichTextStyleSheet* last = after;
341 while (last && last->m_nextSheet)
342 {
343 last = last->m_nextSheet;
344 }
345
346 if (last)
347 {
348 m_previousSheet = last;
349 last->m_nextSheet = this;
350
351 return true;
352 }
353 else
354 return false;
355 }
356
357 /// Unlink from the list of style sheets
358 void wxRichTextStyleSheet::Unlink()
359 {
360 if (m_previousSheet)
361 m_previousSheet->m_nextSheet = m_nextSheet;
362 if (m_nextSheet)
363 m_nextSheet->m_previousSheet = m_previousSheet;
364
365 m_previousSheet = NULL;
366 m_nextSheet = NULL;
367 }
368
369 /// Add a definition to the character style list
370 bool wxRichTextStyleSheet::AddCharacterStyle(wxRichTextCharacterStyleDefinition* def)
371 {
372 def->GetStyle().SetCharacterStyleName(def->GetName());
373 return AddStyle(m_characterStyleDefinitions, def);
374 }
375
376 /// Add a definition to the paragraph style list
377 bool wxRichTextStyleSheet::AddParagraphStyle(wxRichTextParagraphStyleDefinition* def)
378 {
379 def->GetStyle().SetParagraphStyleName(def->GetName());
380 return AddStyle(m_paragraphStyleDefinitions, def);
381 }
382
383 /// Add a definition to the list style list
384 bool wxRichTextStyleSheet::AddListStyle(wxRichTextListStyleDefinition* def)
385 {
386 def->GetStyle().SetListStyleName(def->GetName());
387 return AddStyle(m_listStyleDefinitions, def);
388 }
389
390 /// Add a definition to the appropriate style list
391 bool wxRichTextStyleSheet::AddStyle(wxRichTextStyleDefinition* def)
392 {
393 wxRichTextListStyleDefinition* listDef = wxDynamicCast(def, wxRichTextListStyleDefinition);
394 if (listDef)
395 return AddListStyle(listDef);
396
397 wxRichTextParagraphStyleDefinition* paraDef = wxDynamicCast(def, wxRichTextParagraphStyleDefinition);
398 if (paraDef)
399 return AddParagraphStyle(paraDef);
400
401 wxRichTextCharacterStyleDefinition* charDef = wxDynamicCast(def, wxRichTextCharacterStyleDefinition);
402 if (charDef)
403 return AddCharacterStyle(charDef);
404
405 return false;
406 }
407
408 /// Find any definition by name
409 wxRichTextStyleDefinition* wxRichTextStyleSheet::FindStyle(const wxString& name, bool recurse) const
410 {
411 wxRichTextListStyleDefinition* listDef = FindListStyle(name, recurse);
412 if (listDef)
413 return listDef;
414
415 wxRichTextParagraphStyleDefinition* paraDef = FindParagraphStyle(name, recurse);
416 if (paraDef)
417 return paraDef;
418
419 wxRichTextCharacterStyleDefinition* charDef = FindCharacterStyle(name, recurse);
420 if (charDef)
421 return charDef;
422
423 return NULL;
424 }
425
426 /// Copy
427 void wxRichTextStyleSheet::Copy(const wxRichTextStyleSheet& sheet)
428 {
429 DeleteStyles();
430
431 wxList::compatibility_iterator node;
432
433 for (node = sheet.m_characterStyleDefinitions.GetFirst(); node; node = node->GetNext())
434 {
435 wxRichTextCharacterStyleDefinition* def = (wxRichTextCharacterStyleDefinition*) node->GetData();
436 AddCharacterStyle(new wxRichTextCharacterStyleDefinition(*def));
437 }
438
439 for (node = sheet.m_paragraphStyleDefinitions.GetFirst(); node; node = node->GetNext())
440 {
441 wxRichTextParagraphStyleDefinition* def = (wxRichTextParagraphStyleDefinition*) node->GetData();
442 AddParagraphStyle(new wxRichTextParagraphStyleDefinition(*def));
443 }
444
445 for (node = sheet.m_listStyleDefinitions.GetFirst(); node; node = node->GetNext())
446 {
447 wxRichTextListStyleDefinition* def = (wxRichTextListStyleDefinition*) node->GetData();
448 AddListStyle(new wxRichTextListStyleDefinition(*def));
449 }
450
451 SetName(sheet.GetName());
452 SetDescription(sheet.GetDescription());
453 }
454
455 /// Equality
456 bool wxRichTextStyleSheet::operator==(const wxRichTextStyleSheet& WXUNUSED(sheet)) const
457 {
458 // TODO
459 return false;
460 }
461
462
463 #if wxUSE_HTML
464 /*!
465 * wxRichTextStyleListBox: a listbox to display styles.
466 */
467
468 IMPLEMENT_CLASS(wxRichTextStyleListBox, wxHtmlListBox)
469
470 BEGIN_EVENT_TABLE(wxRichTextStyleListBox, wxHtmlListBox)
471 EVT_LEFT_DOWN(wxRichTextStyleListBox::OnLeftDown)
472 EVT_LEFT_DCLICK(wxRichTextStyleListBox::OnLeftDoubleClick)
473 EVT_IDLE(wxRichTextStyleListBox::OnIdle)
474 END_EVENT_TABLE()
475
476 wxRichTextStyleListBox::wxRichTextStyleListBox(wxWindow* parent, wxWindowID id, const wxPoint& pos,
477 const wxSize& size, long style)
478 {
479 Init();
480 Create(parent, id, pos, size, style);
481 }
482
483 bool wxRichTextStyleListBox::Create(wxWindow* parent, wxWindowID id, const wxPoint& pos,
484 const wxSize& size, long style)
485 {
486 return wxHtmlListBox::Create(parent, id, pos, size, style);
487 }
488
489 wxRichTextStyleListBox::~wxRichTextStyleListBox()
490 {
491 }
492
493 /// Returns the HTML for this item
494 wxString wxRichTextStyleListBox::OnGetItem(size_t n) const
495 {
496 if (!GetStyleSheet())
497 return wxEmptyString;
498
499 wxRichTextStyleDefinition* def = GetStyle(n);
500 if (def)
501 return CreateHTML(def);
502
503 return wxEmptyString;
504 }
505
506 // Get style for index
507 wxRichTextStyleDefinition* wxRichTextStyleListBox::GetStyle(size_t i) const
508 {
509 if (!GetStyleSheet())
510 return NULL;
511
512 if (i >= m_styleNames.GetCount() /* || i < 0 */ )
513 return NULL;
514
515 return GetStyleSheet()->FindStyle(m_styleNames[i]);
516 }
517
518 /// Updates the list
519 void wxRichTextStyleListBox::UpdateStyles()
520 {
521 if (GetStyleSheet())
522 {
523 SetSelection(wxNOT_FOUND);
524
525 m_styleNames.Clear();
526
527 size_t i;
528 if (GetStyleType() == wxRICHTEXT_STYLE_ALL || GetStyleType() == wxRICHTEXT_STYLE_PARAGRAPH)
529 {
530 for (i = 0; i < GetStyleSheet()->GetParagraphStyleCount(); i++)
531 m_styleNames.Add(GetStyleSheet()->GetParagraphStyle(i)->GetName());
532 }
533 if (GetStyleType() == wxRICHTEXT_STYLE_ALL || GetStyleType() == wxRICHTEXT_STYLE_CHARACTER)
534 {
535 for (i = 0; i < GetStyleSheet()->GetCharacterStyleCount(); i++)
536 m_styleNames.Add(GetStyleSheet()->GetCharacterStyle(i)->GetName());
537 }
538 if (GetStyleType() == wxRICHTEXT_STYLE_ALL || GetStyleType() == wxRICHTEXT_STYLE_LIST)
539 {
540 for (i = 0; i < GetStyleSheet()->GetListStyleCount(); i++)
541 m_styleNames.Add(GetStyleSheet()->GetListStyle(i)->GetName());
542 }
543
544 m_styleNames.Sort();
545 SetItemCount(m_styleNames.GetCount());
546
547 Refresh();
548
549 if (GetItemCount() > 0)
550 {
551 SetSelection(0);
552 SendSelectedEvent();
553 }
554 }
555 }
556
557 // Get index for style name
558 int wxRichTextStyleListBox::GetIndexForStyle(const wxString& name) const
559 {
560 return m_styleNames.Index(name);
561 }
562
563 /// Set selection for string
564 int wxRichTextStyleListBox::SetStyleSelection(const wxString& name)
565 {
566 int i = GetIndexForStyle(name);
567 if (i > -1)
568 SetSelection(i);
569 return i;
570 }
571
572 // Convert a colour to a 6-digit hex string
573 static wxString ColourToHexString(const wxColour& col)
574 {
575 wxString hex;
576
577 hex += wxDecToHex(col.Red());
578 hex += wxDecToHex(col.Green());
579 hex += wxDecToHex(col.Blue());
580
581 return hex;
582 }
583
584 /// Creates a suitable HTML fragment for a definition
585 wxString wxRichTextStyleListBox::CreateHTML(wxRichTextStyleDefinition* def) const
586 {
587 // TODO: indicate list format for list style types
588
589 wxString str;
590
591 bool isCentred = false;
592
593 wxTextAttr attr(def->GetStyleMergedWithBase(GetStyleSheet()));
594
595 if (attr.HasAlignment() && attr.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
596 isCentred = true;
597
598 if (isCentred)
599 str << wxT("<center>");
600
601
602 str << wxT("<table><tr>");
603
604 if (attr.GetLeftIndent() > 0)
605 {
606 wxClientDC dc((wxWindow*) this);
607
608 str << wxT("<td width=") << wxMin(50, (ConvertTenthsMMToPixels(dc, attr.GetLeftIndent())/2)) << wxT("></td>");
609 }
610
611 if (isCentred)
612 str << wxT("<td nowrap align=\"center\">");
613 else
614 str << wxT("<td nowrap>");
615
616 #ifdef __WXMSW__
617 int size = 2;
618 #else
619 int size = 3;
620 #endif
621
622 // Guess a standard font size
623 int stdFontSize = 0;
624
625 // First see if we have a default/normal style to base the size on
626 wxString normalTranslated(_("normal"));
627 wxString defaultTranslated(_("default"));
628 size_t i;
629 for (i = 0; i < m_styleNames.GetCount(); i++)
630 {
631 wxString name = m_styleNames[i].Lower();
632 if (name.Find(wxT("normal")) != wxNOT_FOUND || name.Find(normalTranslated) != wxNOT_FOUND ||
633 name.Find(wxT("default")) != wxNOT_FOUND || name.Find(defaultTranslated) != wxNOT_FOUND)
634 {
635 wxRichTextStyleDefinition* d = GetStyleSheet()->FindStyle(m_styleNames[i]);
636 if (d)
637 {
638 wxRichTextAttr attr2(d->GetStyleMergedWithBase(GetStyleSheet()));
639 if (attr2.HasFontSize())
640 {
641 stdFontSize = attr2.GetFontSize();
642 break;
643 }
644 }
645 }
646 }
647
648 if (stdFontSize == 0)
649 {
650 // Look at sizes up to 20 points, and see which is the most common
651 wxArrayInt sizes;
652 size_t maxSize = 20;
653 for (i = 0; i <= maxSize; i++)
654 sizes.Add(0);
655 for (i = 0; i < m_styleNames.GetCount(); i++)
656 {
657 wxRichTextStyleDefinition* d = GetStyleSheet()->FindStyle(m_styleNames[i]);
658 if (d)
659 {
660 wxRichTextAttr attr2(d->GetStyleMergedWithBase(GetStyleSheet()));
661 if (attr2.HasFontSize())
662 {
663 if (attr2.GetFontSize() <= (int) maxSize)
664 sizes[attr2.GetFontSize()] ++;
665 }
666 }
667 }
668 int mostCommonSize = 0;
669 for (i = 0; i <= maxSize; i++)
670 {
671 if (sizes[i] > mostCommonSize)
672 mostCommonSize = i;
673 }
674 if (mostCommonSize > 0)
675 stdFontSize = mostCommonSize;
676 }
677
678 if (stdFontSize == 0)
679 stdFontSize = 12;
680
681 int thisFontSize = ((attr.GetFlags() & wxTEXT_ATTR_FONT_SIZE) != 0) ? attr.GetFontSize() : stdFontSize;
682
683 if (thisFontSize < stdFontSize)
684 size --;
685 else if (thisFontSize > stdFontSize)
686 size ++;
687
688 str += wxT("<font");
689
690 str << wxT(" size=") << size;
691
692 if (!attr.GetFontFaceName().IsEmpty())
693 str << wxT(" face=\"") << attr.GetFontFaceName() << wxT("\"");
694
695 if (attr.GetTextColour().Ok())
696 str << wxT(" color=\"#") << ColourToHexString(attr.GetTextColour()) << wxT("\"");
697
698 str << wxT(">");
699
700 bool hasBold = false;
701 bool hasItalic = false;
702 bool hasUnderline = false;
703
704 if (attr.GetFontWeight() == wxBOLD)
705 hasBold = true;
706 if (attr.GetFontStyle() == wxITALIC)
707 hasItalic = true;
708 if (attr.GetFontUnderlined())
709 hasUnderline = true;
710
711 if (hasBold)
712 str << wxT("<b>");
713 if (hasItalic)
714 str << wxT("<i>");
715 if (hasUnderline)
716 str << wxT("<u>");
717
718 str += def->GetName();
719
720 if (hasUnderline)
721 str << wxT("</u>");
722 if (hasItalic)
723 str << wxT("</i>");
724 if (hasBold)
725 str << wxT("</b>");
726
727 if (isCentred)
728 str << wxT("</centre>");
729
730 str << wxT("</font>");
731
732 str << wxT("</td></tr></table>");
733
734 if (isCentred)
735 str << wxT("</center>");
736
737 return str;
738 }
739
740 // Convert units in tends of a millimetre to device units
741 int wxRichTextStyleListBox::ConvertTenthsMMToPixels(wxDC& dc, int units) const
742 {
743 int ppi = dc.GetPPI().x;
744
745 // There are ppi pixels in 254.1 "1/10 mm"
746
747 double pixels = ((double) units * (double)ppi) / 254.1;
748
749 return (int) pixels;
750 }
751
752 void wxRichTextStyleListBox::OnLeftDown(wxMouseEvent& event)
753 {
754 wxVListBox::OnLeftDown(event);
755
756 int item = VirtualHitTest(event.GetPosition().y);
757 if (item != wxNOT_FOUND && GetApplyOnSelection())
758 ApplyStyle(item);
759 }
760
761 void wxRichTextStyleListBox::OnLeftDoubleClick(wxMouseEvent& event)
762 {
763 wxVListBox::OnLeftDown(event);
764
765 int item = VirtualHitTest(event.GetPosition().y);
766 if (item != wxNOT_FOUND && !GetApplyOnSelection())
767 ApplyStyle(item);
768 }
769
770 /// Helper for listbox and combo control
771 wxString wxRichTextStyleListBox::GetStyleToShowInIdleTime(wxRichTextCtrl* ctrl, wxRichTextStyleType styleType)
772 {
773 int adjustedCaretPos = ctrl->GetAdjustedCaretPosition(ctrl->GetCaretPosition());
774
775 wxString styleName;
776
777 wxTextAttr attr;
778 ctrl->GetStyle(adjustedCaretPos, attr);
779
780 // Take into account current default style just chosen by user
781 if (ctrl->IsDefaultStyleShowing())
782 {
783 wxRichTextApplyStyle(attr, ctrl->GetDefaultStyleEx());
784
785 if ((styleType == wxRICHTEXT_STYLE_ALL || styleType == wxRICHTEXT_STYLE_CHARACTER) &&
786 !attr.GetCharacterStyleName().IsEmpty())
787 styleName = attr.GetCharacterStyleName();
788 else if ((styleType == wxRICHTEXT_STYLE_ALL || styleType == wxRICHTEXT_STYLE_PARAGRAPH) &&
789 !attr.GetParagraphStyleName().IsEmpty())
790 styleName = attr.GetParagraphStyleName();
791 else if ((styleType == wxRICHTEXT_STYLE_ALL || styleType == wxRICHTEXT_STYLE_LIST) &&
792 !attr.GetListStyleName().IsEmpty())
793 styleName = attr.GetListStyleName();
794 }
795 else if ((styleType == wxRICHTEXT_STYLE_ALL || styleType == wxRICHTEXT_STYLE_CHARACTER) &&
796 !attr.GetCharacterStyleName().IsEmpty())
797 {
798 styleName = attr.GetCharacterStyleName();
799 }
800 else if ((styleType == wxRICHTEXT_STYLE_ALL || styleType == wxRICHTEXT_STYLE_PARAGRAPH) &&
801 !attr.GetParagraphStyleName().IsEmpty())
802 {
803 styleName = attr.GetParagraphStyleName();
804 }
805 else if ((styleType == wxRICHTEXT_STYLE_ALL || styleType == wxRICHTEXT_STYLE_LIST) &&
806 !attr.GetListStyleName().IsEmpty())
807 {
808 styleName = attr.GetListStyleName();
809 }
810
811 return styleName;
812 }
813
814 /// Auto-select from style under caret in idle time
815 void wxRichTextStyleListBox::OnIdle(wxIdleEvent& event)
816 {
817 if (CanAutoSetSelection() && GetRichTextCtrl() && IsShownOnScreen() && wxWindow::FindFocus() != this)
818 {
819 wxString styleName = GetStyleToShowInIdleTime(GetRichTextCtrl(), GetStyleType());
820
821 int sel = GetSelection();
822 if (!styleName.IsEmpty())
823 {
824 // Don't do the selection if it's already set
825 if (sel == GetIndexForStyle(styleName))
826 return;
827
828 SetStyleSelection(styleName);
829 }
830 else if (sel != -1)
831 SetSelection(-1);
832 }
833 event.Skip();
834 }
835
836 /// Do selection
837 void wxRichTextStyleListBox::ApplyStyle(int item)
838 {
839 if ( item != wxNOT_FOUND )
840 {
841 wxRichTextStyleDefinition* def = GetStyle(item);
842 if (def && GetRichTextCtrl())
843 {
844 GetRichTextCtrl()->ApplyStyle(def);
845 GetRichTextCtrl()->SetFocus();
846 }
847 }
848 }
849
850 /*!
851 * wxRichTextStyleListCtrl class: manages a listbox and a choice control to
852 * switch shown style types
853 */
854
855 IMPLEMENT_CLASS(wxRichTextStyleListCtrl, wxControl)
856
857 BEGIN_EVENT_TABLE(wxRichTextStyleListCtrl, wxControl)
858 EVT_CHOICE(wxID_ANY, wxRichTextStyleListCtrl::OnChooseType)
859 EVT_SIZE(wxRichTextStyleListCtrl::OnSize)
860 END_EVENT_TABLE()
861
862 wxRichTextStyleListCtrl::wxRichTextStyleListCtrl(wxWindow* parent, wxWindowID id, const wxPoint& pos,
863 const wxSize& size, long style)
864 {
865 Init();
866 Create(parent, id, pos, size, style);
867 }
868
869 bool wxRichTextStyleListCtrl::Create(wxWindow* parent, wxWindowID id, const wxPoint& pos,
870 const wxSize& size, long style)
871 {
872 if ((style & wxBORDER_MASK) == wxBORDER_DEFAULT)
873 style |= wxBORDER_THEME;
874
875 wxControl::Create(parent, id, pos, size, style);
876
877 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
878 if (size != wxDefaultSize)
879 SetInitialSize(size);
880
881 bool showSelector = ((style & wxRICHTEXTSTYLELIST_HIDE_TYPE_SELECTOR) == 0);
882
883 wxBorder listBoxStyle;
884 if (showSelector)
885 listBoxStyle = wxBORDER_THEME;
886 else
887 listBoxStyle = wxBORDER_NONE;
888
889 m_styleListBox = new wxRichTextStyleListBox(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, listBoxStyle);
890
891 wxBoxSizer* boxSizer = new wxBoxSizer(wxVERTICAL);
892
893 if (showSelector)
894 {
895 wxArrayString choices;
896 choices.Add(_("All styles"));
897 choices.Add(_("Paragraph styles"));
898 choices.Add(_("Character styles"));
899 choices.Add(_("List styles"));
900
901 m_styleChoice = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, choices);
902
903 boxSizer->Add(m_styleListBox, 1, wxALL|wxEXPAND, 5);
904 boxSizer->Add(m_styleChoice, 0, wxLEFT|wxRIGHT|wxBOTTOM|wxEXPAND, 5);
905 }
906 else
907 {
908 boxSizer->Add(m_styleListBox, 1, wxALL|wxEXPAND, 0);
909 }
910
911 SetSizer(boxSizer);
912 Layout();
913
914 m_dontUpdate = true;
915
916 if (m_styleChoice)
917 {
918 int i = StyleTypeToIndex(m_styleListBox->GetStyleType());
919 m_styleChoice->SetSelection(i);
920 }
921
922 m_dontUpdate = false;
923
924 return true;
925 }
926
927 wxRichTextStyleListCtrl::~wxRichTextStyleListCtrl()
928 {
929
930 }
931
932 /// React to style type choice
933 void wxRichTextStyleListCtrl::OnChooseType(wxCommandEvent& event)
934 {
935 if (event.GetEventObject() != m_styleChoice)
936 event.Skip();
937 else
938 {
939 if (m_dontUpdate)
940 return;
941
942 wxRichTextStyleListBox::wxRichTextStyleType styleType = StyleIndexToType(event.GetSelection());
943 m_styleListBox->SetStyleType(styleType);
944 }
945 }
946
947 /// Lay out the controls
948 void wxRichTextStyleListCtrl::OnSize(wxSizeEvent& WXUNUSED(event))
949 {
950 if (GetAutoLayout())
951 Layout();
952 }
953
954 /// Get the choice index for style type
955 int wxRichTextStyleListCtrl::StyleTypeToIndex(wxRichTextStyleListBox::wxRichTextStyleType styleType)
956 {
957 if (styleType == wxRichTextStyleListBox::wxRICHTEXT_STYLE_ALL)
958 {
959 return 0;
960 }
961 else if (styleType == wxRichTextStyleListBox::wxRICHTEXT_STYLE_PARAGRAPH)
962 {
963 return 1;
964 }
965 else if (styleType == wxRichTextStyleListBox::wxRICHTEXT_STYLE_CHARACTER)
966 {
967 return 2;
968 }
969 else if (styleType == wxRichTextStyleListBox::wxRICHTEXT_STYLE_LIST)
970 {
971 return 3;
972 }
973 return 0;
974 }
975
976 /// Get the style type for choice index
977 wxRichTextStyleListBox::wxRichTextStyleType wxRichTextStyleListCtrl::StyleIndexToType(int i)
978 {
979 if (i == 1)
980 return wxRichTextStyleListBox::wxRICHTEXT_STYLE_PARAGRAPH;
981 else if (i == 2)
982 return wxRichTextStyleListBox::wxRICHTEXT_STYLE_CHARACTER;
983 else if (i == 3)
984 return wxRichTextStyleListBox::wxRICHTEXT_STYLE_LIST;
985
986 return wxRichTextStyleListBox::wxRICHTEXT_STYLE_ALL;
987 }
988
989 /// Associates the control with a style manager
990 void wxRichTextStyleListCtrl::SetStyleSheet(wxRichTextStyleSheet* styleSheet)
991 {
992 if (m_styleListBox)
993 m_styleListBox->SetStyleSheet(styleSheet);
994 }
995
996 wxRichTextStyleSheet* wxRichTextStyleListCtrl::GetStyleSheet() const
997 {
998 if (m_styleListBox)
999 return m_styleListBox->GetStyleSheet();
1000 else
1001 return NULL;
1002 }
1003
1004 /// Associates the control with a wxRichTextCtrl
1005 void wxRichTextStyleListCtrl::SetRichTextCtrl(wxRichTextCtrl* ctrl)
1006 {
1007 if (m_styleListBox)
1008 m_styleListBox->SetRichTextCtrl(ctrl);
1009 }
1010
1011 wxRichTextCtrl* wxRichTextStyleListCtrl::GetRichTextCtrl() const
1012 {
1013 if (m_styleListBox)
1014 return m_styleListBox->GetRichTextCtrl();
1015 else
1016 return NULL;
1017 }
1018
1019 /// Set/get the style type to display
1020 void wxRichTextStyleListCtrl::SetStyleType(wxRichTextStyleListBox::wxRichTextStyleType styleType)
1021 {
1022 if ( !m_styleListBox )
1023 return;
1024
1025 m_styleListBox->SetStyleType(styleType);
1026
1027 m_dontUpdate = true;
1028
1029 if (m_styleChoice)
1030 {
1031 int i = StyleTypeToIndex(m_styleListBox->GetStyleType());
1032 m_styleChoice->SetSelection(i);
1033 }
1034
1035 m_dontUpdate = false;
1036 }
1037
1038 wxRichTextStyleListBox::wxRichTextStyleType wxRichTextStyleListCtrl::GetStyleType() const
1039 {
1040 if (m_styleListBox)
1041 return m_styleListBox->GetStyleType();
1042 else
1043 return wxRichTextStyleListBox::wxRICHTEXT_STYLE_ALL;
1044 }
1045
1046 /// Updates the style list box
1047 void wxRichTextStyleListCtrl::UpdateStyles()
1048 {
1049 if (m_styleListBox)
1050 m_styleListBox->UpdateStyles();
1051 }
1052
1053 #if wxUSE_COMBOCTRL
1054
1055 /*!
1056 * Style drop-down for a wxComboCtrl
1057 */
1058
1059
1060 BEGIN_EVENT_TABLE(wxRichTextStyleComboPopup, wxRichTextStyleListBox)
1061 EVT_MOTION(wxRichTextStyleComboPopup::OnMouseMove)
1062 EVT_LEFT_DOWN(wxRichTextStyleComboPopup::OnMouseClick)
1063 END_EVENT_TABLE()
1064
1065 bool wxRichTextStyleComboPopup::Create( wxWindow* parent )
1066 {
1067 int borderStyle = GetDefaultBorder();
1068 if (borderStyle == wxBORDER_SUNKEN)
1069 borderStyle = wxBORDER_SIMPLE;
1070
1071 return wxRichTextStyleListBox::Create(parent, wxID_ANY,
1072 wxPoint(0,0), wxDefaultSize,
1073 borderStyle);
1074 }
1075
1076 void wxRichTextStyleComboPopup::SetStringValue( const wxString& s )
1077 {
1078 m_value = SetStyleSelection(s);
1079 }
1080
1081 wxString wxRichTextStyleComboPopup::GetStringValue() const
1082 {
1083 int sel = m_value;
1084 if (sel > -1)
1085 {
1086 wxRichTextStyleDefinition* def = GetStyle(sel);
1087 if (def)
1088 return def->GetName();
1089 }
1090 return wxEmptyString;
1091 }
1092
1093 //
1094 // Popup event handlers
1095 //
1096
1097 // Mouse hot-tracking
1098 void wxRichTextStyleComboPopup::OnMouseMove(wxMouseEvent& event)
1099 {
1100 // Move selection to cursor if it is inside the popup
1101
1102 int itemHere = wxRichTextStyleListBox::VirtualHitTest(event.GetPosition().y);
1103 if ( itemHere >= 0 )
1104 {
1105 wxRichTextStyleListBox::SetSelection(itemHere);
1106 m_itemHere = itemHere;
1107 }
1108 event.Skip();
1109 }
1110
1111 // On mouse left, set the value and close the popup
1112 void wxRichTextStyleComboPopup::OnMouseClick(wxMouseEvent& WXUNUSED(event))
1113 {
1114 if (m_itemHere >= 0)
1115 m_value = m_itemHere;
1116
1117 // Ordering is important, so we don't dismiss this popup accidentally
1118 // by setting the focus elsewhere e.g. in ApplyStyle
1119 Dismiss();
1120
1121 if (m_itemHere >= 0)
1122 wxRichTextStyleListBox::ApplyStyle(m_itemHere);
1123 }
1124
1125 /*!
1126 * wxRichTextStyleComboCtrl
1127 * A combo for applying styles.
1128 */
1129
1130 IMPLEMENT_CLASS(wxRichTextStyleComboCtrl, wxComboCtrl)
1131
1132 BEGIN_EVENT_TABLE(wxRichTextStyleComboCtrl, wxComboCtrl)
1133 EVT_IDLE(wxRichTextStyleComboCtrl::OnIdle)
1134 END_EVENT_TABLE()
1135
1136 bool wxRichTextStyleComboCtrl::Create(wxWindow* parent, wxWindowID id, const wxPoint& pos,
1137 const wxSize& size, long style)
1138 {
1139 if (!wxComboCtrl::Create(parent, id, wxEmptyString, pos, size, style))
1140 return false;
1141
1142 SetPopupMaxHeight(400);
1143
1144 m_stylePopup = new wxRichTextStyleComboPopup;
1145
1146 SetPopupControl(m_stylePopup);
1147
1148 return true;
1149 }
1150
1151 /// Auto-select from style under caret in idle time
1152
1153 // TODO: must be able to show italic, bold, combinations
1154 // in style box. Do we have a concept of automatic, temporary
1155 // styles that are added whenever we wish to show a style
1156 // that doesn't exist already? E.g. "Bold, Italic, Underline".
1157 // Word seems to generate these things on the fly.
1158 // If there's a named style already, it uses e.g. Heading1 + Bold, Italic
1159 // If you unembolden text in a style that has bold, it uses the
1160 // term "Not bold".
1161 // TODO: order styles alphabetically. This means indexes can change,
1162 // so need a different way to specify selections, i.e. by name.
1163
1164 void wxRichTextStyleComboCtrl::OnIdle(wxIdleEvent& event)
1165 {
1166 event.Skip();
1167
1168 if ( !m_stylePopup )
1169 return;
1170
1171 wxRichTextCtrl * const richtext = GetRichTextCtrl();
1172 if ( !richtext )
1173 return;
1174
1175 if ( !IsPopupShown() && IsShownOnScreen() && wxWindow::FindFocus() != this )
1176 {
1177 wxString styleName =
1178 wxRichTextStyleListBox::GetStyleToShowInIdleTime(richtext, m_stylePopup->GetStyleType());
1179
1180 wxString currentValue = GetValue();
1181 if (!styleName.IsEmpty())
1182 {
1183 // Don't do the selection if it's already set
1184 if (currentValue == styleName)
1185 return;
1186
1187 SetValue(styleName);
1188 }
1189 else if (!currentValue.IsEmpty())
1190 SetValue(wxEmptyString);
1191 }
1192 }
1193
1194 #endif
1195 // wxUSE_COMBOCTRL
1196
1197 #endif
1198 // wxUSE_HTML
1199
1200 #endif
1201 // wxUSE_RICHTEXT