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