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