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