///////////////////////////////////////////////////////////////////////////// // Name: src/richtext/richtextstyles.cpp // Purpose: Style management for wxRichTextCtrl // Author: Julian Smart // Modified by: // Created: 2005-09-30 // RCS-ID: $Id$ // 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" IMPLEMENT_CLASS(wxRichTextStyleDefinition, wxObject) IMPLEMENT_CLASS(wxRichTextCharacterStyleDefinition, wxRichTextStyleDefinition) IMPLEMENT_CLASS(wxRichTextParagraphStyleDefinition, wxRichTextStyleDefinition) IMPLEMENT_CLASS(wxRichTextListStyleDefinition, wxRichTextParagraphStyleDefinition) /*! * A definition */ void wxRichTextStyleDefinition::Copy(const wxRichTextStyleDefinition& def) { m_name = def.m_name; m_baseStyle = def.m_baseStyle; m_style = def.m_style; } bool wxRichTextStyleDefinition::Eq(const wxRichTextStyleDefinition& def) const { return (m_name == def.m_name && m_baseStyle == def.m_baseStyle && m_style == def.m_style); } /*! * 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); } /*! * 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) { int listLevel = FindLevelForIndent(indent); wxRichTextAttr attr(*GetLevelAttributes(listLevel)); int oldLeftIndent = attr.GetLeftIndent(); int oldLeftSubIndent = attr.GetLeftSubIndent(); // First apply the overall paragraph style, if any wxRichTextApplyStyle(attr, GetStyle()); // Then apply paragraph style, e.g. from paragraph style definition wxRichTextApplyStyle(attr, 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) { int listLevel = FindLevelForIndent(indent); return GetCombinedStyleForLevel(listLevel); } /// Combine the base and list style, using the given indent (from which /// an appropriate level is found) wxRichTextAttr wxRichTextListStyleDefinition::GetCombinedStyleForLevel(int listLevel) { wxRichTextAttr attr(*GetLevelAttributes(listLevel)); int oldLeftIndent = attr.GetLeftIndent(); int oldLeftSubIndent = attr.GetLeftSubIndent(); // Apply the overall paragraph style, if any wxRichTextApplyStyle(attr, 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 */ IMPLEMENT_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* def = (wxRichTextStyleDefinition*) node->GetData(); list.Erase(node); if (deleteStyle) delete def; return true; } else 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().Lower() == name.Lower()) 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); } /// 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); } /// 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)); } } /// Equality bool wxRichTextStyleSheet::operator==(const wxRichTextStyleSheet& WXUNUSED(sheet)) const { // TODO return false; } #if wxUSE_HTML /*! * wxRichTextStyleListBox: a listbox to display styles. */ IMPLEMENT_CLASS(wxRichTextStyleListBox, wxHtmlListBox) BEGIN_EVENT_TABLE(wxRichTextStyleListBox, wxHtmlListBox) EVT_LEFT_DOWN(wxRichTextStyleListBox::OnLeftDown) EVT_LEFT_DCLICK(wxRichTextStyleListBox::OnLeftDoubleClick) EVT_IDLE(wxRichTextStyleListBox::OnIdle) END_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 (GetStyleType() == wxRICHTEXT_STYLE_ALL) { // First paragraph styles, then character, then list if (i < GetStyleSheet()->GetParagraphStyleCount()) return GetStyleSheet()->GetParagraphStyle(i); if ((i - GetStyleSheet()->GetParagraphStyleCount()) < GetStyleSheet()->GetCharacterStyleCount()) return GetStyleSheet()->GetCharacterStyle(i - GetStyleSheet()->GetParagraphStyleCount()); if ((i - GetStyleSheet()->GetParagraphStyleCount() - GetStyleSheet()->GetCharacterStyleCount()) < GetStyleSheet()->GetListStyleCount()) return GetStyleSheet()->GetListStyle(i - GetStyleSheet()->GetParagraphStyleCount() - GetStyleSheet()->GetCharacterStyleCount()); } else if ((GetStyleType() == wxRICHTEXT_STYLE_PARAGRAPH) && (i < GetStyleSheet()->GetParagraphStyleCount())) { return GetStyleSheet()->GetParagraphStyle(i); } else if ((GetStyleType() == wxRICHTEXT_STYLE_CHARACTER) && (i < GetStyleSheet()->GetCharacterStyleCount())) { return GetStyleSheet()->GetCharacterStyle(i); } else if ((GetStyleType() == wxRICHTEXT_STYLE_LIST) && (i < GetStyleSheet()->GetListStyleCount())) { return GetStyleSheet()->GetListStyle(i); } return NULL; } /// Updates the list void wxRichTextStyleListBox::UpdateStyles() { if (GetStyleSheet()) { SetSelection(wxNOT_FOUND); if (GetStyleType() == wxRICHTEXT_STYLE_ALL) SetItemCount(GetStyleSheet()->GetParagraphStyleCount()+GetStyleSheet()->GetCharacterStyleCount()+GetStyleSheet()->GetListStyleCount()); else if (GetStyleType() == wxRICHTEXT_STYLE_PARAGRAPH) SetItemCount(GetStyleSheet()->GetParagraphStyleCount()); else if (GetStyleType() == wxRICHTEXT_STYLE_CHARACTER) SetItemCount(GetStyleSheet()->GetCharacterStyleCount()); else if (GetStyleType() == wxRICHTEXT_STYLE_LIST) SetItemCount(GetStyleSheet()->GetListStyleCount()); Refresh(); if (GetItemCount() > 0) { SetSelection(0); SendSelectedEvent(); } } } // Get index for style name int wxRichTextStyleListBox::GetIndexForStyle(const wxString& name) const { if (GetStyleSheet()) { int count = GetItemCount(); int i; for (i = 0; i < (int) count; i++) { wxRichTextStyleDefinition* def = GetStyle(i); if (def->GetName() == name) return i; } } return -1; } /// Set selection for string int wxRichTextStyleListBox::SetStyleSelection(const wxString& name) { int i = GetIndexForStyle(name); if (i > -1) SetSelection(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(wxT("
| "); } str << wxT(" | "); #ifdef __WXMSW__ int size = 3; #else int size = 4; #endif int stdFontSize = 12; int thisFontSize = ((def->GetStyle().GetFlags() & wxTEXT_ATTR_FONT_SIZE) != 0) ? def->GetStyle().GetFontSize() : stdFontSize; if (thisFontSize < stdFontSize) size ++; else if (thisFontSize > stdFontSize) size --; str += wxT("GetStyle().GetFontFaceName() << wxT("\""); if (def->GetStyle().GetTextColour().Ok()) str << wxT(" color=\"#") << ColourToHexString(def->GetStyle().GetTextColour()) << wxT("\""); str << wxT(">"); bool hasBold = false; bool hasItalic = false; bool hasUnderline = false; if (def->GetStyle().GetFontWeight() == wxBOLD) hasBold = true; if (def->GetStyle().GetFontStyle() == wxITALIC) hasItalic = true; if (def->GetStyle().GetFontUnderlined()) hasUnderline = true; if (hasBold) str << wxT(""); if (hasItalic) str << wxT(""); if (hasUnderline) str << wxT(""); str += def->GetName(); if (hasUnderline) str << wxT(""); if (hasItalic) str << wxT(""); if (hasBold) str << wxT(""); str << wxT(""); str += wxT(" |