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