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