Files
wxWidgets/src/richtext/richtextstyles.cpp
Tobias Taschner 63803dd8d9 Fix MSVS 2015 warnings about hiding variables in wxRichText code
These warnings were apparently harmless, but really annoying, so get rid of
them by renaming the variables instead of reusing the same name in the nested
scope.

Closes #17351.
2016-02-05 22:43:23 +01:00

1375 lines
39 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: src/richtext/richtextstyles.cpp
// Purpose: Style management for wxRichTextCtrl
// Author: Julian Smart
// Modified by:
// Created: 2005-09-30
// Copyright: (c) Julian Smart
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#if wxUSE_RICHTEXT
#include "wx/richtext/richtextstyles.h"
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#include "wx/filename.h"
#include "wx/clipbrd.h"
#include "wx/wfstream.h"
#include "wx/settings.h"
#include "wx/richtext/richtextctrl.h"
wxIMPLEMENT_CLASS(wxRichTextStyleDefinition, wxObject);
wxIMPLEMENT_CLASS(wxRichTextCharacterStyleDefinition, wxRichTextStyleDefinition);
wxIMPLEMENT_CLASS(wxRichTextParagraphStyleDefinition, wxRichTextStyleDefinition);
wxIMPLEMENT_CLASS(wxRichTextListStyleDefinition, wxRichTextParagraphStyleDefinition);
wxIMPLEMENT_CLASS(wxRichTextBoxStyleDefinition, wxRichTextStyleDefinition);
/*!
* A definition
*/
void wxRichTextStyleDefinition::Copy(const wxRichTextStyleDefinition& def)
{
m_name = def.m_name;
m_baseStyle = def.m_baseStyle;
m_style = def.m_style;
m_description = def.m_description;
m_properties = def.m_properties;
}
bool wxRichTextStyleDefinition::Eq(const wxRichTextStyleDefinition& def) const
{
return (m_name == def.m_name && m_baseStyle == def.m_baseStyle && m_style == def.m_style && m_properties == def.m_properties);
}
/// Gets the style combined with the base style
wxRichTextAttr wxRichTextStyleDefinition::GetStyleMergedWithBase(const wxRichTextStyleSheet* sheet) const
{
if (m_baseStyle.IsEmpty())
return m_style;
bool isParaStyle = IsKindOf(wxCLASSINFO(wxRichTextParagraphStyleDefinition));
bool isCharStyle = IsKindOf(wxCLASSINFO(wxRichTextCharacterStyleDefinition));
bool isListStyle = IsKindOf(wxCLASSINFO(wxRichTextListStyleDefinition));
bool isBoxStyle = IsKindOf(wxCLASSINFO(wxRichTextBoxStyleDefinition));
// Collect the styles, detecting loops
wxArrayString styleNames;
wxList styles;
const wxRichTextStyleDefinition* def = this;
while (def)
{
styles.Insert((wxObject*) def);
styleNames.Add(def->GetName());
wxString baseStyleName = def->GetBaseStyle();
if (!baseStyleName.IsEmpty() && styleNames.Index(baseStyleName) == wxNOT_FOUND)
{
if (isParaStyle)
def = sheet->FindParagraphStyle(baseStyleName);
else if (isCharStyle)
def = sheet->FindCharacterStyle(baseStyleName);
else if (isListStyle)
def = sheet->FindListStyle(baseStyleName);
else if (isBoxStyle)
def = sheet->FindBoxStyle(baseStyleName);
else
def = sheet->FindStyle(baseStyleName);
}
else
def = NULL;
}
wxRichTextAttr attr;
wxList::compatibility_iterator node = styles.GetFirst();
while (node)
{
wxRichTextStyleDefinition* nodeDef = (wxRichTextStyleDefinition*) node->GetData();
attr.Apply(nodeDef->GetStyle(), NULL);
node = node->GetNext();
}
return attr;
}
/*!
* Paragraph style definition
*/
void wxRichTextParagraphStyleDefinition::Copy(const wxRichTextParagraphStyleDefinition& def)
{
wxRichTextStyleDefinition::Copy(def);
m_nextStyle = def.m_nextStyle;
}
bool wxRichTextParagraphStyleDefinition::operator ==(const wxRichTextParagraphStyleDefinition& def) const
{
return (Eq(def) && m_nextStyle == def.m_nextStyle);
}
/*!
* Box style definition
*/
void wxRichTextBoxStyleDefinition::Copy(const wxRichTextBoxStyleDefinition& def)
{
wxRichTextStyleDefinition::Copy(def);
}
bool wxRichTextBoxStyleDefinition::operator ==(const wxRichTextBoxStyleDefinition& def) const
{
return (Eq(def));
}
/*!
* List style definition
*/
void wxRichTextListStyleDefinition::Copy(const wxRichTextListStyleDefinition& def)
{
wxRichTextParagraphStyleDefinition::Copy(def);
int i;
for (i = 0; i < 10; i++)
m_levelStyles[i] = def.m_levelStyles[i];
}
bool wxRichTextListStyleDefinition::operator ==(const wxRichTextListStyleDefinition& def) const
{
if (!Eq(def))
return false;
int i;
for (i = 0; i < 10; i++)
if (!(m_levelStyles[i] == def.m_levelStyles[i]))
return false;
return true;
}
/// Sets/gets the attributes for the given level
void wxRichTextListStyleDefinition::SetLevelAttributes(int i, const wxRichTextAttr& attr)
{
wxASSERT( (i >= 0 && i < 10) );
if (i >= 0 && i < 10)
m_levelStyles[i] = attr;
}
const wxRichTextAttr* wxRichTextListStyleDefinition::GetLevelAttributes(int i) const
{
wxASSERT( (i >= 0 && i < 10) );
if (i >= 0 && i < 10)
return & m_levelStyles[i];
else
return NULL;
}
wxRichTextAttr* wxRichTextListStyleDefinition::GetLevelAttributes(int i)
{
wxASSERT( (i >= 0 && i < 10) );
if (i >= 0 && i < 10)
return & m_levelStyles[i];
else
return NULL;
}
/// Convenience function for setting the major attributes for a list level specification
void wxRichTextListStyleDefinition::SetAttributes(int i, int leftIndent, int leftSubIndent, int bulletStyle, const wxString& bulletSymbol)
{
wxASSERT( (i >= 0 && i < 10) );
if (i >= 0 && i < 10)
{
wxRichTextAttr attr;
attr.SetBulletStyle(bulletStyle);
attr.SetLeftIndent(leftIndent, leftSubIndent);
if (!bulletSymbol.IsEmpty())
{
if (bulletStyle & wxTEXT_ATTR_BULLET_STYLE_SYMBOL)
attr.SetBulletText(bulletSymbol);
else
attr.SetBulletName(bulletSymbol);
}
m_levelStyles[i] = attr;
}
}
/// Finds the level corresponding to the given indentation
int wxRichTextListStyleDefinition::FindLevelForIndent(int indent) const
{
int i;
for (i = 0; i < 10; i++)
{
if (indent < m_levelStyles[i].GetLeftIndent())
{
if (i > 0)
return i - 1;
else
return 0;
}
}
return 9;
}
/// Combine the list style with a paragraph style, using the given indent (from which
/// an appropriate level is found)
wxRichTextAttr wxRichTextListStyleDefinition::CombineWithParagraphStyle(int indent, const wxRichTextAttr& paraStyle, wxRichTextStyleSheet* styleSheet)
{
int listLevel = FindLevelForIndent(indent);
wxRichTextAttr attr(*GetLevelAttributes(listLevel));
int oldLeftIndent = attr.GetLeftIndent();
int oldLeftSubIndent = attr.GetLeftSubIndent();
// First apply the overall paragraph style, if any
if (styleSheet)
attr.Apply(GetStyleMergedWithBase(styleSheet));
else
attr.Apply(GetStyle());
// Then apply paragraph style, e.g. from paragraph style definition
attr.Apply(paraStyle);
// We override the indents according to the list definition
attr.SetLeftIndent(oldLeftIndent, oldLeftSubIndent);
return attr;
}
/// Combine the base and list style, using the given indent (from which
/// an appropriate level is found)
wxRichTextAttr wxRichTextListStyleDefinition::GetCombinedStyle(int indent, wxRichTextStyleSheet* styleSheet)
{
int listLevel = FindLevelForIndent(indent);
return GetCombinedStyleForLevel(listLevel, styleSheet);
}
/// Combine the base and list style, using the given indent (from which
/// an appropriate level is found)
wxRichTextAttr wxRichTextListStyleDefinition::GetCombinedStyleForLevel(int listLevel, wxRichTextStyleSheet* styleSheet)
{
wxRichTextAttr attr(*GetLevelAttributes(listLevel));
int oldLeftIndent = attr.GetLeftIndent();
int oldLeftSubIndent = attr.GetLeftSubIndent();
// Apply the overall paragraph style, if any
if (styleSheet)
attr.Apply(GetStyleMergedWithBase(styleSheet));
else
attr.Apply(GetStyle());
// We override the indents according to the list definition
attr.SetLeftIndent(oldLeftIndent, oldLeftSubIndent);
return attr;
}
/// Is this a numbered list?
bool wxRichTextListStyleDefinition::IsNumbered(int i) const
{
return (0 != (GetLevelAttributes(i)->GetFlags() &
(wxTEXT_ATTR_BULLET_STYLE_ARABIC|wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER|wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER|
wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER|wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER)));
}
/*!
* The style manager
*/
wxIMPLEMENT_CLASS(wxRichTextStyleSheet, wxObject);
wxRichTextStyleSheet::~wxRichTextStyleSheet()
{
DeleteStyles();
if (m_nextSheet)
m_nextSheet->m_previousSheet = m_previousSheet;
if (m_previousSheet)
m_previousSheet->m_nextSheet = m_nextSheet;
m_previousSheet = NULL;
m_nextSheet = NULL;
}
/// Initialisation
void wxRichTextStyleSheet::Init()
{
m_previousSheet = NULL;
m_nextSheet = NULL;
}
/// Add a definition to one of the style lists
bool wxRichTextStyleSheet::AddStyle(wxList& list, wxRichTextStyleDefinition* def)
{
if (!list.Find(def))
list.Append(def);
return true;
}
/// Remove a style
bool wxRichTextStyleSheet::RemoveStyle(wxList& list, wxRichTextStyleDefinition* def, bool deleteStyle)
{
wxList::compatibility_iterator node = list.Find(def);
if (node)
{
wxRichTextStyleDefinition* nodeDef = (wxRichTextStyleDefinition*) node->GetData();
list.Erase(node);
if (deleteStyle)
delete nodeDef;
return true;
}
else
return false;
}
/// Remove a style
bool wxRichTextStyleSheet::RemoveStyle(wxRichTextStyleDefinition* def, bool deleteStyle)
{
if (RemoveParagraphStyle(def, deleteStyle))
return true;
if (RemoveCharacterStyle(def, deleteStyle))
return true;
if (RemoveListStyle(def, deleteStyle))
return true;
if (RemoveBoxStyle(def, deleteStyle))
return true;
return false;
}
/// Find a definition by name
wxRichTextStyleDefinition* wxRichTextStyleSheet::FindStyle(const wxList& list, const wxString& name, bool recurse) const
{
for (wxList::compatibility_iterator node = list.GetFirst(); node; node = node->GetNext())
{
wxRichTextStyleDefinition* def = (wxRichTextStyleDefinition*) node->GetData();
if (def->GetName() == name)
return def;
}
if (m_nextSheet && recurse)
return m_nextSheet->FindStyle(list, name, recurse);
return NULL;
}
/// Delete all styles
void wxRichTextStyleSheet::DeleteStyles()
{
WX_CLEAR_LIST(wxList, m_characterStyleDefinitions);
WX_CLEAR_LIST(wxList, m_paragraphStyleDefinitions);
WX_CLEAR_LIST(wxList, m_listStyleDefinitions);
WX_CLEAR_LIST(wxList, m_boxStyleDefinitions);
}
/// Insert into list of style sheets
bool wxRichTextStyleSheet::InsertSheet(wxRichTextStyleSheet* before)
{
m_previousSheet = before->m_previousSheet;
m_nextSheet = before;
before->m_previousSheet = this;
return true;
}
/// Append to list of style sheets
bool wxRichTextStyleSheet::AppendSheet(wxRichTextStyleSheet* after)
{
wxRichTextStyleSheet* last = after;
while (last && last->m_nextSheet)
{
last = last->m_nextSheet;
}
if (last)
{
m_previousSheet = last;
last->m_nextSheet = this;
return true;
}
else
return false;
}
/// Unlink from the list of style sheets
void wxRichTextStyleSheet::Unlink()
{
if (m_previousSheet)
m_previousSheet->m_nextSheet = m_nextSheet;
if (m_nextSheet)
m_nextSheet->m_previousSheet = m_previousSheet;
m_previousSheet = NULL;
m_nextSheet = NULL;
}
/// Add a definition to the character style list
bool wxRichTextStyleSheet::AddCharacterStyle(wxRichTextCharacterStyleDefinition* def)
{
def->GetStyle().SetCharacterStyleName(def->GetName());
return AddStyle(m_characterStyleDefinitions, def);
}
/// Add a definition to the paragraph style list
bool wxRichTextStyleSheet::AddParagraphStyle(wxRichTextParagraphStyleDefinition* def)
{
def->GetStyle().SetParagraphStyleName(def->GetName());
return AddStyle(m_paragraphStyleDefinitions, def);
}
/// Add a definition to the list style list
bool wxRichTextStyleSheet::AddListStyle(wxRichTextListStyleDefinition* def)
{
def->GetStyle().SetListStyleName(def->GetName());
return AddStyle(m_listStyleDefinitions, def);
}
/// Add a definition to the box style list
bool wxRichTextStyleSheet::AddBoxStyle(wxRichTextBoxStyleDefinition* def)
{
def->GetStyle().GetTextBoxAttr().SetBoxStyleName(def->GetName());
return AddStyle(m_boxStyleDefinitions, def);
}
/// Add a definition to the appropriate style list
bool wxRichTextStyleSheet::AddStyle(wxRichTextStyleDefinition* def)
{
wxRichTextListStyleDefinition* listDef = wxDynamicCast(def, wxRichTextListStyleDefinition);
if (listDef)
return AddListStyle(listDef);
wxRichTextParagraphStyleDefinition* paraDef = wxDynamicCast(def, wxRichTextParagraphStyleDefinition);
if (paraDef)
return AddParagraphStyle(paraDef);
wxRichTextCharacterStyleDefinition* charDef = wxDynamicCast(def, wxRichTextCharacterStyleDefinition);
if (charDef)
return AddCharacterStyle(charDef);
wxRichTextBoxStyleDefinition* boxDef = wxDynamicCast(def, wxRichTextBoxStyleDefinition);
if (boxDef)
return AddBoxStyle(boxDef);
return false;
}
/// Find any definition by name
wxRichTextStyleDefinition* wxRichTextStyleSheet::FindStyle(const wxString& name, bool recurse) const
{
wxRichTextListStyleDefinition* listDef = FindListStyle(name, recurse);
if (listDef)
return listDef;
wxRichTextParagraphStyleDefinition* paraDef = FindParagraphStyle(name, recurse);
if (paraDef)
return paraDef;
wxRichTextCharacterStyleDefinition* charDef = FindCharacterStyle(name, recurse);
if (charDef)
return charDef;
wxRichTextBoxStyleDefinition* boxDef = FindBoxStyle(name, recurse);
if (boxDef)
return boxDef;
return NULL;
}
/// Copy
void wxRichTextStyleSheet::Copy(const wxRichTextStyleSheet& sheet)
{
DeleteStyles();
wxList::compatibility_iterator node;
for (node = sheet.m_characterStyleDefinitions.GetFirst(); node; node = node->GetNext())
{
wxRichTextCharacterStyleDefinition* def = (wxRichTextCharacterStyleDefinition*) node->GetData();
AddCharacterStyle(new wxRichTextCharacterStyleDefinition(*def));
}
for (node = sheet.m_paragraphStyleDefinitions.GetFirst(); node; node = node->GetNext())
{
wxRichTextParagraphStyleDefinition* def = (wxRichTextParagraphStyleDefinition*) node->GetData();
AddParagraphStyle(new wxRichTextParagraphStyleDefinition(*def));
}
for (node = sheet.m_listStyleDefinitions.GetFirst(); node; node = node->GetNext())
{
wxRichTextListStyleDefinition* def = (wxRichTextListStyleDefinition*) node->GetData();
AddListStyle(new wxRichTextListStyleDefinition(*def));
}
for (node = sheet.m_boxStyleDefinitions.GetFirst(); node; node = node->GetNext())
{
wxRichTextBoxStyleDefinition* def = (wxRichTextBoxStyleDefinition*) node->GetData();
AddBoxStyle(new wxRichTextBoxStyleDefinition(*def));
}
SetName(sheet.GetName());
SetDescription(sheet.GetDescription());
m_properties = sheet.m_properties;
}
/// Equality
bool wxRichTextStyleSheet::operator==(const wxRichTextStyleSheet& WXUNUSED(sheet)) const
{
// TODO
return false;
}
#if wxUSE_HTML
// Functions for dealing with clashing names for different kinds of style.
// Returns "P", "C", "L" or "B" (paragraph, character, list or box) for
// style name | type.
static wxString wxGetRichTextStyleType(const wxString& style)
{
return style.AfterLast(wxT('|'));
}
static wxString wxGetRichTextStyle(const wxString& style)
{
return style.BeforeLast(wxT('|'));
}
/*!
* wxRichTextStyleListBox: a listbox to display styles.
*/
wxIMPLEMENT_CLASS(wxRichTextStyleListBox, wxHtmlListBox);
wxBEGIN_EVENT_TABLE(wxRichTextStyleListBox, wxHtmlListBox)
EVT_LEFT_DOWN(wxRichTextStyleListBox::OnLeftDown)
EVT_LEFT_DCLICK(wxRichTextStyleListBox::OnLeftDoubleClick)
EVT_IDLE(wxRichTextStyleListBox::OnIdle)
wxEND_EVENT_TABLE()
wxRichTextStyleListBox::wxRichTextStyleListBox(wxWindow* parent, wxWindowID id, const wxPoint& pos,
const wxSize& size, long style)
{
Init();
Create(parent, id, pos, size, style);
}
bool wxRichTextStyleListBox::Create(wxWindow* parent, wxWindowID id, const wxPoint& pos,
const wxSize& size, long style)
{
return wxHtmlListBox::Create(parent, id, pos, size, style);
}
wxRichTextStyleListBox::~wxRichTextStyleListBox()
{
}
/// Returns the HTML for this item
wxString wxRichTextStyleListBox::OnGetItem(size_t n) const
{
if (!GetStyleSheet())
return wxEmptyString;
wxRichTextStyleDefinition* def = GetStyle(n);
if (def)
return CreateHTML(def);
return wxEmptyString;
}
// Get style for index
wxRichTextStyleDefinition* wxRichTextStyleListBox::GetStyle(size_t i) const
{
if (!GetStyleSheet())
return NULL;
if (i >= m_styleNames.GetCount() /* || i < 0 */ )
return NULL;
wxString styleType = wxGetRichTextStyleType(m_styleNames[i]);
wxString style = wxGetRichTextStyle(m_styleNames[i]);
if (styleType == wxT("P"))
return GetStyleSheet()->FindParagraphStyle(style);
else if (styleType == wxT("C"))
return GetStyleSheet()->FindCharacterStyle(style);
else if (styleType == wxT("L"))
return GetStyleSheet()->FindListStyle(style);
else if (styleType == wxT("B"))
return GetStyleSheet()->FindBoxStyle(style);
else
return GetStyleSheet()->FindStyle(style);
}
/// Updates the list
void wxRichTextStyleListBox::UpdateStyles()
{
if (GetStyleSheet())
{
int oldSel = GetSelection();
SetSelection(wxNOT_FOUND);
m_styleNames.Clear();
size_t i;
if (GetStyleType() == wxRICHTEXT_STYLE_ALL || GetStyleType() == wxRICHTEXT_STYLE_PARAGRAPH)
{
for (i = 0; i < GetStyleSheet()->GetParagraphStyleCount(); i++)
m_styleNames.Add(GetStyleSheet()->GetParagraphStyle(i)->GetName() + wxT("|P"));
}
if (GetStyleType() == wxRICHTEXT_STYLE_ALL || GetStyleType() == wxRICHTEXT_STYLE_CHARACTER)
{
for (i = 0; i < GetStyleSheet()->GetCharacterStyleCount(); i++)
m_styleNames.Add(GetStyleSheet()->GetCharacterStyle(i)->GetName() + wxT("|C"));
}
if (GetStyleType() == wxRICHTEXT_STYLE_ALL || GetStyleType() == wxRICHTEXT_STYLE_LIST)
{
for (i = 0; i < GetStyleSheet()->GetListStyleCount(); i++)
m_styleNames.Add(GetStyleSheet()->GetListStyle(i)->GetName() + wxT("|L"));
}
if (GetStyleType() == wxRICHTEXT_STYLE_ALL || GetStyleType() == wxRICHTEXT_STYLE_BOX)
{
for (i = 0; i < GetStyleSheet()->GetBoxStyleCount(); i++)
m_styleNames.Add(GetStyleSheet()->GetBoxStyle(i)->GetName() + wxT("|B"));
}
m_styleNames.Sort();
SetItemCount(m_styleNames.GetCount());
Refresh();
int newSel = -1;
if (oldSel >= 0 && oldSel < (int) GetItemCount())
newSel = oldSel;
else if (GetItemCount() > 0)
newSel = 0;
if (newSel >= 0)
{
SetSelection(newSel);
SendSelectedEvent();
}
}
else
{
m_styleNames.Clear();
SetSelection(wxNOT_FOUND);
SetItemCount(0);
Refresh();
}
}
// Get index for style name
int wxRichTextStyleListBox::GetIndexForStyle(const wxString& name) const
{
wxString s(name);
if (GetStyleType() == wxRICHTEXT_STYLE_PARAGRAPH)
s += wxT("|P");
else if (GetStyleType() == wxRICHTEXT_STYLE_CHARACTER)
s += wxT("|C");
else if (GetStyleType() == wxRICHTEXT_STYLE_LIST)
s += wxT("|L");
else if (GetStyleType() == wxRICHTEXT_STYLE_BOX)
s += wxT("|B");
else
{
if (m_styleNames.Index(s + wxT("|P")) != wxNOT_FOUND)
s += wxT("|P");
else if (m_styleNames.Index(s + wxT("|C")) != wxNOT_FOUND)
s += wxT("|C");
else if (m_styleNames.Index(s + wxT("|L")) != wxNOT_FOUND)
s += wxT("|L");
else if (m_styleNames.Index(s + wxT("|B")) != wxNOT_FOUND)
s += wxT("|B");
}
return m_styleNames.Index(s);
}
/// Set selection for string
int wxRichTextStyleListBox::SetStyleSelection(const wxString& name)
{
int i = GetIndexForStyle(name);
if (i > -1)
{
SetSelection(i);
if (!IsVisible(i))
ScrollToRow(i);
}
return i;
}
// Convert a colour to a 6-digit hex string
static wxString ColourToHexString(const wxColour& col)
{
wxString hex;
hex += wxDecToHex(col.Red());
hex += wxDecToHex(col.Green());
hex += wxDecToHex(col.Blue());
return hex;
}
/// Creates a suitable HTML fragment for a definition
wxString wxRichTextStyleListBox::CreateHTML(wxRichTextStyleDefinition* def) const
{
// TODO: indicate list format for list style types
wxString str;
bool isCentred = false;
wxRichTextAttr attr(def->GetStyleMergedWithBase(GetStyleSheet()));
if (attr.HasAlignment() && attr.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
isCentred = true;
str << wxT("<html><head></head>");
str << wxT("<body");
if (attr.GetBackgroundColour().Ok())
str << wxT(" bgcolor=\"#") << ColourToHexString(attr.GetBackgroundColour()) << wxT("\"");
str << wxT(">");
if (isCentred)
str << wxT("<center>");
str << wxT("<table");
if (attr.GetBackgroundColour().Ok())
str << wxT(" bgcolor=\"#") << ColourToHexString(attr.GetBackgroundColour()) << wxT("\"");
str << wxT("><tr>");
if (attr.GetLeftIndent() > 0)
{
wxClientDC dc((wxWindow*) this);
str << wxT("<td width=") << wxMin(50, (ConvertTenthsMMToPixels(dc, attr.GetLeftIndent())/2)) << wxT("></td>");
}
if (isCentred)
str << wxT("<td nowrap align=\"center\">");
else
str << wxT("<td nowrap>");
#ifdef __WXMSW__
int size = 2;
#else
int size = 3;
#endif
// Guess a standard font size
int stdFontSize = 0;
// First see if we have a default/normal style to base the size on
wxString normalTranslated(_("normal"));
wxString defaultTranslated(_("default"));
size_t i;
for (i = 0; i < GetStyleSheet()->GetParagraphStyleCount(); i++)
{
wxRichTextStyleDefinition* d = GetStyleSheet()->GetParagraphStyle(i);
wxString name = d->GetName().Lower();
if (name.Find(wxT("normal")) != wxNOT_FOUND || name.Find(normalTranslated) != wxNOT_FOUND ||
name.Find(wxT("default")) != wxNOT_FOUND || name.Find(defaultTranslated) != wxNOT_FOUND)
{
wxRichTextAttr attr2(d->GetStyleMergedWithBase(GetStyleSheet()));
if (attr2.HasFontPointSize())
{
stdFontSize = attr2.GetFontSize();
break;
}
}
}
if (stdFontSize == 0)
{
// Look at sizes up to 20 points, and see which is the most common
wxArrayInt sizes;
size_t maxSize = 20;
for (i = 0; i <= maxSize; i++)
sizes.Add(0);
for (i = 0; i < m_styleNames.GetCount(); i++)
{
wxRichTextStyleDefinition* d = GetStyle(i);
if (d)
{
wxRichTextAttr attr2(d->GetStyleMergedWithBase(GetStyleSheet()));
if (attr2.HasFontPointSize())
{
if (attr2.GetFontSize() <= (int) maxSize)
sizes[attr2.GetFontSize()] ++;
}
}
}
int mostCommonSize = 0;
for (i = 0; i <= maxSize; i++)
{
if (sizes[i] > mostCommonSize)
mostCommonSize = i;
}
if (mostCommonSize > 0)
stdFontSize = mostCommonSize;
}
if (stdFontSize == 0)
stdFontSize = 12;
int thisFontSize = attr.HasFontPointSize() ? attr.GetFontSize() : stdFontSize;
if (thisFontSize < stdFontSize)
size --;
else if (thisFontSize > stdFontSize)
size ++;
str += wxT("<font");
str << wxT(" size=") << size;
if (!attr.GetFontFaceName().IsEmpty())
str << wxT(" face=\"") << attr.GetFontFaceName() << wxT("\"");
if (attr.GetTextColour().IsOk() && attr.GetTextColour() != attr.GetBackgroundColour() && !(!attr.HasBackgroundColour() && attr.GetTextColour() == *wxWHITE))
str << wxT(" color=\"#") << ColourToHexString(attr.GetTextColour()) << wxT("\"");
if (attr.GetBackgroundColour().Ok())
str << wxT(" bgcolor=\"#") << ColourToHexString(attr.GetBackgroundColour()) << wxT("\"");
str << wxT(">");
bool hasBold = false;
bool hasItalic = false;
bool hasUnderline = false;
if (attr.GetFontWeight() == wxFONTWEIGHT_BOLD)
hasBold = true;
if (attr.GetFontStyle() == wxFONTSTYLE_ITALIC)
hasItalic = true;
if (attr.GetFontUnderlined())
hasUnderline = true;
if (hasBold)
str << wxT("<b>");
if (hasItalic)
str << wxT("<i>");
if (hasUnderline)
str << wxT("<u>");
wxString name(def->GetName());
if (attr.HasTextEffects() && (attr.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS)))
name = name.Upper();
str += name;
if (hasUnderline)
str << wxT("</u>");
if (hasItalic)
str << wxT("</i>");
if (hasBold)
str << wxT("</b>");
if (isCentred)
str << wxT("</centre>");
str << wxT("</font>");
str << wxT("</td></tr></table>");
if (isCentred)
str << wxT("</center>");
str << wxT("</body></html>");
return str;
}
// Convert units in tends of a millimetre to device units
int wxRichTextStyleListBox::ConvertTenthsMMToPixels(wxDC& dc, int units) const
{
int ppi = dc.GetPPI().x;
// There are ppi pixels in 254.1 "1/10 mm"
double pixels = ((double) units * (double)ppi) / 254.1;
return (int) pixels;
}
void wxRichTextStyleListBox::OnLeftDown(wxMouseEvent& event)
{
wxVListBox::OnLeftDown(event);
int item = VirtualHitTest(event.GetPosition().y);
if (item != wxNOT_FOUND && GetApplyOnSelection())
ApplyStyle(item);
}
void wxRichTextStyleListBox::OnLeftDoubleClick(wxMouseEvent& event)
{
wxVListBox::OnLeftDown(event);
int item = VirtualHitTest(event.GetPosition().y);
if (item != wxNOT_FOUND && !GetApplyOnSelection())
ApplyStyle(item);
}
/// Helper for listbox and combo control
wxString wxRichTextStyleListBox::GetStyleToShowInIdleTime(wxRichTextCtrl* ctrl, wxRichTextStyleType styleType)
{
int adjustedCaretPos = ctrl->GetAdjustedCaretPosition(ctrl->GetCaretPosition());
wxString styleName;
wxRichTextAttr attr;
ctrl->GetStyle(adjustedCaretPos, attr);
// Take into account current default style just chosen by user
if (ctrl->IsDefaultStyleShowing())
{
wxRichTextApplyStyle(attr, ctrl->GetDefaultStyleEx());
if ((styleType == wxRICHTEXT_STYLE_ALL || styleType == wxRICHTEXT_STYLE_CHARACTER) &&
!attr.GetCharacterStyleName().IsEmpty())
styleName = attr.GetCharacterStyleName();
else if ((styleType == wxRICHTEXT_STYLE_ALL || styleType == wxRICHTEXT_STYLE_PARAGRAPH) &&
!attr.GetParagraphStyleName().IsEmpty())
styleName = attr.GetParagraphStyleName();
else if ((styleType == wxRICHTEXT_STYLE_ALL || styleType == wxRICHTEXT_STYLE_LIST) &&
!attr.GetListStyleName().IsEmpty())
styleName = attr.GetListStyleName();
// TODO: when we have a concept of focused object (text box), we'll
// use the paragraph style name of the focused object as the frame style name.
#if 0
else if ((styleType == wxRICHTEXT_STYLE_ALL || styleType == wxRICHTEXT_STYLE_BOX) &&
!attr.GetBoxStyleName().IsEmpty())
styleName = attr.GetBoxStyleName();
#endif
}
else if ((styleType == wxRICHTEXT_STYLE_ALL || styleType == wxRICHTEXT_STYLE_CHARACTER) &&
!attr.GetCharacterStyleName().IsEmpty())
{
styleName = attr.GetCharacterStyleName();
}
else if ((styleType == wxRICHTEXT_STYLE_ALL || styleType == wxRICHTEXT_STYLE_PARAGRAPH) &&
!attr.GetParagraphStyleName().IsEmpty())
{
styleName = attr.GetParagraphStyleName();
}
else if ((styleType == wxRICHTEXT_STYLE_ALL || styleType == wxRICHTEXT_STYLE_LIST) &&
!attr.GetListStyleName().IsEmpty())
{
styleName = attr.GetListStyleName();
}
return styleName;
}
/// Auto-select from style under caret in idle time
void wxRichTextStyleListBox::OnIdle(wxIdleEvent& event)
{
if (CanAutoSetSelection() && GetRichTextCtrl() && IsShownOnScreen() && wxWindow::FindFocus() != this)
{
wxString styleName = GetStyleToShowInIdleTime(GetRichTextCtrl(), GetStyleType());
int sel = GetSelection();
if (!styleName.IsEmpty())
{
// Don't do the selection if it's already set
if (sel == GetIndexForStyle(styleName))
return;
SetStyleSelection(styleName);
}
else if (sel != -1)
SetSelection(-1);
}
event.Skip();
}
/// Do selection
void wxRichTextStyleListBox::ApplyStyle(int item)
{
if ( item != wxNOT_FOUND )
{
wxRichTextStyleDefinition* def = GetStyle(item);
if (def && GetRichTextCtrl())
{
GetRichTextCtrl()->ApplyStyle(def);
GetRichTextCtrl()->SetFocus();
}
}
}
/*!
* wxRichTextStyleListCtrl class: manages a listbox and a choice control to
* switch shown style types
*/
wxIMPLEMENT_CLASS(wxRichTextStyleListCtrl, wxControl);
wxBEGIN_EVENT_TABLE(wxRichTextStyleListCtrl, wxControl)
EVT_CHOICE(wxID_ANY, wxRichTextStyleListCtrl::OnChooseType)
EVT_SIZE(wxRichTextStyleListCtrl::OnSize)
wxEND_EVENT_TABLE()
wxRichTextStyleListCtrl::wxRichTextStyleListCtrl(wxWindow* parent, wxWindowID id, const wxPoint& pos,
const wxSize& size, long style)
{
Init();
Create(parent, id, pos, size, style);
}
bool wxRichTextStyleListCtrl::Create(wxWindow* parent, wxWindowID id, const wxPoint& pos,
const wxSize& size, long style)
{
if ((style & wxBORDER_MASK) == wxBORDER_DEFAULT)
style |= wxBORDER_THEME;
wxControl::Create(parent, id, pos, size, style);
SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
if (size != wxDefaultSize)
SetInitialSize(size);
bool showSelector = ((style & wxRICHTEXTSTYLELIST_HIDE_TYPE_SELECTOR) == 0);
wxBorder listBoxStyle;
if (showSelector)
listBoxStyle = wxBORDER_THEME;
else
listBoxStyle = wxBORDER_NONE;
m_styleListBox = new wxRichTextStyleListBox(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, listBoxStyle);
wxBoxSizer* boxSizer = new wxBoxSizer(wxVERTICAL);
if (showSelector)
{
wxArrayString choices;
choices.Add(_("All styles"));
choices.Add(_("Paragraph styles"));
choices.Add(_("Character styles"));
choices.Add(_("List styles"));
choices.Add(_("Box styles"));
m_styleChoice = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, choices);
boxSizer->Add(m_styleListBox, 1, wxALL|wxEXPAND, 5);
boxSizer->Add(m_styleChoice, 0, wxLEFT|wxRIGHT|wxBOTTOM|wxEXPAND, 5);
}
else
{
boxSizer->Add(m_styleListBox, 1, wxALL|wxEXPAND, 0);
}
SetSizer(boxSizer);
Layout();
m_dontUpdate = true;
if (m_styleChoice)
{
int i = StyleTypeToIndex(m_styleListBox->GetStyleType());
m_styleChoice->SetSelection(i);
}
m_dontUpdate = false;
return true;
}
wxRichTextStyleListCtrl::~wxRichTextStyleListCtrl()
{
}
/// React to style type choice
void wxRichTextStyleListCtrl::OnChooseType(wxCommandEvent& event)
{
if (event.GetEventObject() != m_styleChoice)
event.Skip();
else
{
if (m_dontUpdate)
return;
wxRichTextStyleListBox::wxRichTextStyleType styleType = StyleIndexToType(event.GetSelection());
m_styleListBox->SetSelection(-1);
m_styleListBox->SetStyleType(styleType);
}
}
/// Lay out the controls
void wxRichTextStyleListCtrl::OnSize(wxSizeEvent& WXUNUSED(event))
{
if (GetAutoLayout())
Layout();
}
/// Get the choice index for style type
int wxRichTextStyleListCtrl::StyleTypeToIndex(wxRichTextStyleListBox::wxRichTextStyleType styleType)
{
if (styleType == wxRichTextStyleListBox::wxRICHTEXT_STYLE_ALL)
{
return 0;
}
else if (styleType == wxRichTextStyleListBox::wxRICHTEXT_STYLE_PARAGRAPH)
{
return 1;
}
else if (styleType == wxRichTextStyleListBox::wxRICHTEXT_STYLE_CHARACTER)
{
return 2;
}
else if (styleType == wxRichTextStyleListBox::wxRICHTEXT_STYLE_LIST)
{
return 3;
}
else if (styleType == wxRichTextStyleListBox::wxRICHTEXT_STYLE_BOX)
{
return 4;
}
return 0;
}
/// Get the style type for choice index
wxRichTextStyleListBox::wxRichTextStyleType wxRichTextStyleListCtrl::StyleIndexToType(int i)
{
if (i == 1)
return wxRichTextStyleListBox::wxRICHTEXT_STYLE_PARAGRAPH;
else if (i == 2)
return wxRichTextStyleListBox::wxRICHTEXT_STYLE_CHARACTER;
else if (i == 3)
return wxRichTextStyleListBox::wxRICHTEXT_STYLE_LIST;
else if (i == 4)
return wxRichTextStyleListBox::wxRICHTEXT_STYLE_BOX;
return wxRichTextStyleListBox::wxRICHTEXT_STYLE_ALL;
}
/// Associates the control with a style manager
void wxRichTextStyleListCtrl::SetStyleSheet(wxRichTextStyleSheet* styleSheet)
{
if (m_styleListBox)
m_styleListBox->SetStyleSheet(styleSheet);
}
wxRichTextStyleSheet* wxRichTextStyleListCtrl::GetStyleSheet() const
{
if (m_styleListBox)
return m_styleListBox->GetStyleSheet();
else
return NULL;
}
/// Associates the control with a wxRichTextCtrl
void wxRichTextStyleListCtrl::SetRichTextCtrl(wxRichTextCtrl* ctrl)
{
if (m_styleListBox)
m_styleListBox->SetRichTextCtrl(ctrl);
}
wxRichTextCtrl* wxRichTextStyleListCtrl::GetRichTextCtrl() const
{
if (m_styleListBox)
return m_styleListBox->GetRichTextCtrl();
else
return NULL;
}
/// Set/get the style type to display
void wxRichTextStyleListCtrl::SetStyleType(wxRichTextStyleListBox::wxRichTextStyleType styleType)
{
if ( !m_styleListBox )
return;
m_styleListBox->SetStyleType(styleType);
m_dontUpdate = true;
if (m_styleChoice)
{
int i = StyleTypeToIndex(m_styleListBox->GetStyleType());
m_styleChoice->SetSelection(i);
}
m_dontUpdate = false;
}
wxRichTextStyleListBox::wxRichTextStyleType wxRichTextStyleListCtrl::GetStyleType() const
{
if (m_styleListBox)
return m_styleListBox->GetStyleType();
else
return wxRichTextStyleListBox::wxRICHTEXT_STYLE_ALL;
}
/// Updates the style list box
void wxRichTextStyleListCtrl::UpdateStyles()
{
if (m_styleListBox)
m_styleListBox->UpdateStyles();
}
#if wxUSE_COMBOCTRL
/*!
* Style drop-down for a wxComboCtrl
*/
wxBEGIN_EVENT_TABLE(wxRichTextStyleComboPopup, wxRichTextStyleListBox)
EVT_MOTION(wxRichTextStyleComboPopup::OnMouseMove)
EVT_LEFT_DOWN(wxRichTextStyleComboPopup::OnMouseClick)
wxEND_EVENT_TABLE()
bool wxRichTextStyleComboPopup::Create( wxWindow* parent )
{
int borderStyle = GetDefaultBorder();
if (borderStyle == wxBORDER_SUNKEN || borderStyle == wxBORDER_NONE)
borderStyle = wxBORDER_THEME;
return wxRichTextStyleListBox::Create(parent, wxID_ANY,
wxPoint(0,0), wxDefaultSize,
borderStyle);
}
void wxRichTextStyleComboPopup::SetStringValue( const wxString& s )
{
m_value = SetStyleSelection(s);
}
wxString wxRichTextStyleComboPopup::GetStringValue() const
{
int sel = m_value;
if (sel > -1)
{
wxRichTextStyleDefinition* def = GetStyle(sel);
if (def)
return def->GetName();
}
return wxEmptyString;
}
//
// Popup event handlers
//
// Mouse hot-tracking
void wxRichTextStyleComboPopup::OnMouseMove(wxMouseEvent& event)
{
// Move selection to cursor if it is inside the popup
int itemHere = wxRichTextStyleListBox::VirtualHitTest(event.GetPosition().y);
if ( itemHere >= 0 )
{
wxRichTextStyleListBox::SetSelection(itemHere);
m_itemHere = itemHere;
}
event.Skip();
}
// On mouse left, set the value and close the popup
void wxRichTextStyleComboPopup::OnMouseClick(wxMouseEvent& WXUNUSED(event))
{
if (m_itemHere >= 0)
m_value = m_itemHere;
// Ordering is important, so we don't dismiss this popup accidentally
// by setting the focus elsewhere e.g. in ApplyStyle
Dismiss();
if (m_itemHere >= 0)
wxRichTextStyleListBox::ApplyStyle(m_itemHere);
}
/*!
* wxRichTextStyleComboCtrl
* A combo for applying styles.
*/
wxIMPLEMENT_CLASS(wxRichTextStyleComboCtrl, wxComboCtrl);
wxBEGIN_EVENT_TABLE(wxRichTextStyleComboCtrl, wxComboCtrl)
EVT_IDLE(wxRichTextStyleComboCtrl::OnIdle)
wxEND_EVENT_TABLE()
bool wxRichTextStyleComboCtrl::Create(wxWindow* parent, wxWindowID id, const wxPoint& pos,
const wxSize& size, long style)
{
if (!wxComboCtrl::Create(parent, id, wxEmptyString, pos, size, style))
return false;
SetPopupMaxHeight(400);
m_stylePopup = new wxRichTextStyleComboPopup;
SetPopupControl(m_stylePopup);
return true;
}
/// Auto-select from style under caret in idle time
// TODO: must be able to show italic, bold, combinations
// in style box. Do we have a concept of automatic, temporary
// styles that are added whenever we wish to show a style
// that doesn't exist already? E.g. "Bold, Italic, Underline".
// Word seems to generate these things on the fly.
// If there's a named style already, it uses e.g. Heading1 + Bold, Italic
// If you unembolden text in a style that has bold, it uses the
// term "Not bold".
// TODO: order styles alphabetically. This means indexes can change,
// so need a different way to specify selections, i.e. by name.
void wxRichTextStyleComboCtrl::OnIdle(wxIdleEvent& event)
{
event.Skip();
if ( !m_stylePopup )
return;
wxRichTextCtrl * const richtext = GetRichTextCtrl();
if ( !richtext )
return;
if ( !IsPopupShown() && IsShownOnScreen() && wxWindow::FindFocus() != this )
{
wxString styleName =
wxRichTextStyleListBox::GetStyleToShowInIdleTime(richtext, m_stylePopup->GetStyleType());
wxString currentValue = GetValue();
if (!styleName.IsEmpty())
{
// Don't do the selection if it's already set
if (currentValue == styleName)
return;
SetValue(styleName);
}
else if (!currentValue.IsEmpty())
SetValue(wxEmptyString);
}
}
#endif
// wxUSE_COMBOCTRL
#endif
// wxUSE_HTML
#endif
// wxUSE_RICHTEXT