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