/*
* This file is part of Code::Blocks Studio, an open-source cross-platform IDE
* Copyright (C) 2003 Yiannis An. Mandravellos
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Contact e-mail: Yiannis An. Mandravellos <mandrav@codeblocks.org>
* Program URL : http://www.codeblocks.org
*
* $Id: editorcolorset.cpp,v 1.15.2.1 2005/10/25 07:59:03 mandrav Exp $
* $Date: 2005/10/25 07:59:03 $
*/
#include "sdk_precomp.h"
#include <wx/log.h>
#include <wx/dir.h>
#include "globals.h"
#include "cbeditor.h"
#include "editorcolorset.h"
#include "editorlexerloader.h"
#include "configmanager.h"
#include "messagemanager.h"
#include "manager.h"
#define cbHIGHLIGHT_LINE -98 // highlight line under caret virtual style
#define cbSELECTION -99 // selection virtual style
EditorColorSet::EditorColorSet(const wxString& setName)
: m_LanguageID(0),
m_Name(setName)
{
LoadAvailableSets();
if (setName.IsEmpty())
m_Name = COLORSET_DEFAULT;
else
Load();
}
EditorColorSet::EditorColorSet(const EditorColorSet& other) // copy ctor
{
m_Name = other.m_Name;
for (int x = 0; x < HL_LAST; ++x)
{
m_Sets[x].m_Langs = other.m_Sets[x].m_Langs;
m_Sets[x].m_Lexers = other.m_Sets[x].m_Lexers;
m_Sets[x].m_Keywords[0] = other.m_Sets[x].m_Keywords[0];
m_Sets[x].m_Keywords[1] = other.m_Sets[x].m_Keywords[1];
m_Sets[x].m_Keywords[2] = other.m_Sets[x].m_Keywords[2];
m_Sets[x].m_FileMasks = other.m_Sets[x].m_FileMasks;
m_Sets[x].m_SampleCode = other.m_Sets[x].m_SampleCode;
m_Sets[x].m_BreakLine = other.m_Sets[x].m_BreakLine;
m_Sets[x].m_DebugLine = other.m_Sets[x].m_DebugLine;
m_Sets[x].m_ErrorLine = other.m_Sets[x].m_ErrorLine;
const OptionColors& value = other.m_Sets[x].m_Colors;
for (unsigned int i = 0; i < value.GetCount(); ++i)
{
AddOption(x, value[i]);
}
}
}
EditorColorSet::~EditorColorSet()
{
ClearAllOptionColors();
}
void EditorColorSet::ClearAllOptionColors()
{
for (int i = 0; i < HL_LAST; ++i)
{
WX_CLEAR_ARRAY(m_Sets[i].m_Colors);
m_Sets[i].m_Colors.Clear();
m_Sets[i].m_Langs.Clear();
m_Sets[i].m_Lexers = wxSCI_LEX_NULL;
m_Sets[i].m_Keywords[0].Clear();
m_Sets[i].m_Keywords[1].Clear();
m_Sets[i].m_Keywords[2].Clear();
m_Sets[i].m_FileMasks.Clear();
m_Sets[i].m_SampleCode.Clear();
m_Sets[i].m_BreakLine = -1;
m_Sets[i].m_DebugLine = -1;
m_Sets[i].m_ErrorLine = -1;
}
}
void EditorColorSet::LoadAvailableSets()
{
m_LanguageID = 0;
wxString path = ConfigManager::Get()->Read(_T("data_path")) + _T("/lexers");
wxDir dir(path);
if (!dir.IsOpened())
return;
EditorLexerLoader lex(this);
wxString filename;
bool ok = dir.GetFirst(&filename, _T("lexer_*.xml"), wxDIR_FILES);
while (ok)
{
lex.Load(path + _T("/") + filename);
ok = dir.GetNext(&filename);
}
}
HighlightLanguage EditorColorSet::AddHighlightLanguage(int lexer, const wxString& name)
{
if (m_LanguageID == HL_LAST ||
lexer <= wxSCI_LEX_NULL ||
lexer > wxSCI_LEX_CSOUND ||
name.IsEmpty() ||
GetHighlightLanguage(name) != HL_NONE)
{
return HL_NONE;
}
m_Sets[m_LanguageID].m_Langs = name;
m_Sets[m_LanguageID].m_Lexers = lexer;
++m_LanguageID;
return m_LanguageID - 1;
}
HighlightLanguage EditorColorSet::GetHighlightLanguage(const wxString& name)
{
for (int i = 0; i < HL_LAST; ++i)
{
if (m_Sets[i].m_Langs.Matches(name))
return i;
}
return HL_NONE;
}
wxArrayString EditorColorSet::GetAllHighlightLanguages()
{
wxArrayString ret;
for (int i = 0; i < HL_LAST; ++i)
{
if (!m_Sets[i].m_Langs.IsEmpty())
ret.Add(m_Sets[i].m_Langs);
}
return ret;
}
void EditorColorSet::UpdateOptionsWithSameName(HighlightLanguage lang, OptionColor* base)
{
if (!base)
return;
// first find the index of this option
int idx = -1;
for (unsigned int i = 0; i < m_Sets[lang].m_Colors.GetCount(); ++i)
{
OptionColor* opt = m_Sets[lang].m_Colors.Item(i);
if (opt == base)
{
idx = i;
break;
}
}
if (idx == -1)
return;
// now loop again, but update the other options with the same name
for (unsigned int i = 0; i < m_Sets[lang].m_Colors.GetCount(); ++i)
{
if ((int)i == idx)
continue; // skip the base option
OptionColor* opt = m_Sets[lang].m_Colors.Item(i);
if (!opt->name.Matches(base->name))
continue;
opt->fore = base->fore;
opt->back = base->back;
opt->bold = base->bold;
opt->italics = base->italics;
opt->underlined = base->underlined;
}
}
bool EditorColorSet::AddOption(HighlightLanguage lang, OptionColor* option, bool checkIfExists)
{
if (lang == HL_NONE)
return false;
if (checkIfExists && GetOptionByValue(lang, option->value))
return false;
OptionColors& colors = m_Sets[lang].m_Colors;
colors.Add(new OptionColor(*option));
return true;
}
void EditorColorSet::AddOption(HighlightLanguage lang,
const wxString& name,
int value,
wxColour fore,
wxColour back,
bool bold,
bool italics,
bool underlined,
bool isStyle)
{
if (lang == HL_NONE)
return;
OptionColor* opt = new OptionColor;
opt->name = name;
opt->value = value;
opt->fore = fore;
opt->back = back;
opt->bold = bold;
opt->italics = italics;
opt->underlined = underlined;
opt->isStyle = isStyle;
if (!AddOption(lang, opt))
delete opt;
}
OptionColor* EditorColorSet::GetOptionByName(HighlightLanguage lang, const wxString& name)
{
if (lang == HL_NONE)
return 0L;
for (unsigned int i = 0; i < m_Sets[lang].m_Colors.GetCount(); ++i)
{
OptionColor* opt = m_Sets[lang].m_Colors.Item(i);
if (opt->name == name)
return opt;
}
return 0L;
}
OptionColor* EditorColorSet::GetOptionByValue(HighlightLanguage lang, int value)
{
if (lang == HL_NONE)
return 0L;
for (unsigned int i = 0; i < m_Sets[lang].m_Colors.GetCount(); ++i)
{
OptionColor* opt = m_Sets[lang].m_Colors.Item(i);
if (opt->value == value)
return opt;
}
return 0L;
}
OptionColor* EditorColorSet::GetOptionByIndex(HighlightLanguage lang, int index)
{
if (lang == HL_NONE)
return 0L;
return m_Sets[lang].m_Colors.Item(index);
}
int EditorColorSet::GetOptionCount(HighlightLanguage lang)
{
return m_Sets[lang].m_Colors.GetCount();
}
HighlightLanguage EditorColorSet::GetLanguageForFilename(const wxString& filename)
{
// convert filename to lowercase first (m_FileMasks already contains
// lowercase-only strings)
wxString lfname = filename.Lower();
// first search in filemasks
for (int i = 0; i < HL_LAST; ++i)
{
for (unsigned int x = 0; x < m_Sets[i].m_FileMasks.GetCount(); ++x)
{
if (lfname.Matches(m_Sets[i].m_FileMasks.Item(x)))
return i;
}
}
return HL_NONE;
}
wxString EditorColorSet::GetLanguageName(HighlightLanguage lang)
{
if (lang == HL_NONE)
return _("Unknown");
wxString name = m_Sets[lang].m_Langs;
if (!name.IsEmpty())
return name;
return _("Unknown");
}
void EditorColorSet::DoApplyStyle(cbStyledTextCtrl* control, int value, OptionColor* option)
{
// option->value is ignored here...
// value is used instead
if (option->fore != wxNullColour)
control->StyleSetForeground(value, option->fore);
if (option->back != wxNullColour)
control->StyleSetBackground(value, option->back);
control->StyleSetBold(value, option->bold);
control->StyleSetItalic(value, option->italics);
control->StyleSetUnderline(value, option->underlined);
}
void EditorColorSet::Apply(cbEditor* editor)
{
if (!editor)
return;
HighlightLanguage lang = GetLanguageForFilename(editor->GetFilename());
if (lang == HL_NONE)
return;
Apply(lang, editor->GetControl());
}
void EditorColorSet::Apply(HighlightLanguage lang, cbStyledTextCtrl* control)
{
control->StyleClearAll();
// first load the default colors to all styles (ignoring some built-in styles)
OptionColor* defaults = GetOptionByName(lang, _("Default"));
if (defaults)
{
for (int i = 0; i < wxSCI_STYLE_MAX; ++i)
{
if (i < 33 || i > 39)
DoApplyStyle(control, i, defaults);
}
// also set the caret color, same as the default foreground
control->SetCaretForeground(defaults->fore);
}
// for some strange reason, when switching styles, the line numbering changes color
// too, though we didn't ask it to...
// this makes sure it stays the correct color
control->StyleSetForeground(wxSCI_STYLE_LINENUMBER, wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT));
for (unsigned int i = 0; i < m_Sets[lang].m_Colors.GetCount(); ++i)
{
OptionColor* opt = m_Sets[lang].m_Colors.Item(i);
if (opt->isStyle)
{
DoApplyStyle(control, opt->value, opt);
}
else
{
if (opt->value == cbHIGHLIGHT_LINE)
{
control->SetCaretLineBackground(opt->back);
ConfigManager::Get()->Write(_T("/editor/highlight_caret_line_color/red"), opt->back.Red());
ConfigManager::Get()->Write(_T("/editor/highlight_caret_line_color/green"), opt->back.Green());
ConfigManager::Get()->Write(_T("/editor/highlight_caret_line_color/blue"), opt->back.Blue());
}
else if (opt->value == cbSELECTION)
{
if (opt->back != wxNullColour)
{
control->SetSelBackground(true, opt->back);
ConfigManager::Get()->Write(_T("/editor/selection_color/red"), opt->back.Red());
ConfigManager::Get()->Write(_T("/editor/selection_color/green"), opt->back.Green());
ConfigManager::Get()->Write(_T("/editor/selection_color/blue"), opt->back.Blue());
}
else
control->SetSelBackground(false, wxColour(0xC0, 0xC0, 0xC0));
}
else
{
control->MarkerDefine(-opt->value, 1);
control->MarkerSetBackground(-opt->value, opt->back);
}
}
}
control->SetLexer(m_Sets[lang].m_Lexers);
for (int i = 0; i < 3; ++i)
control->SetKeyWords(i, m_Sets[lang].m_Keywords[i]);
control->Colourise(0, -1); // the *most* important part!
}
void EditorColorSet::Save()
{
wxString key;
for (int x = 0; x < HL_LAST; ++x)
{
wxString lang = m_Sets[x].m_Langs;
lang.Replace(_T("/"), _T("_"));
lang.Replace(_T("\\"), _T("_"));
if (lang.IsEmpty())
continue;
key.Clear();
key << _T("/editor/color_sets/") << m_Name << _T("/") << lang;
for (unsigned int i = 0; i < m_Sets[x].m_Colors.GetCount(); ++i)
{
OptionColor* opt = m_Sets[x].m_Colors.Item(i);
wxString tmpKey;
tmpKey << key << _T("/") << opt->name;
if (opt->fore != wxNullColour)
{
ConfigManager::Get()->Write(tmpKey + _T("/fore/red"), opt->fore.Red());
ConfigManager::Get()->Write(tmpKey + _T("/fore/green"), opt->fore.Green());
ConfigManager::Get()->Write(tmpKey + _T("/fore/blue"), opt->fore.Blue());
}
if (opt->back != wxNullColour)
{
ConfigManager::Get()->Write(tmpKey + _T("/back/red"), opt->back.Red());
ConfigManager::Get()->Write(tmpKey + _T("/back/green"), opt->back.Green());
ConfigManager::Get()->Write(tmpKey + _T("/back/blue"), opt->back.Blue());
}
ConfigManager::Get()->Write(tmpKey + _T("/bold"), opt->bold);
ConfigManager::Get()->Write(tmpKey + _T("/italics"), opt->italics);
ConfigManager::Get()->Write(tmpKey + _T("/underlined"), opt->underlined);
ConfigManager::Get()->Write(tmpKey + _T("/isStyle"), opt->isStyle);
}
for (int i = 0; i < 3; ++i)
{
wxString tmpkey;
tmpkey.Printf(_T("%s/editor/keywords/%d"), key.c_str(), i);
ConfigManager::Get()->Write(tmpkey, m_Sets[x].m_Keywords[i]);
}
}
}
void EditorColorSet::Load()
{
wxString key;
for (int x = 0; x < HL_LAST; ++x)
{
wxString lang = m_Sets[x].m_Langs;
lang.Replace(_T("/"), _T("_"));
lang.Replace(_T("\\"), _T("_"));
if (lang.IsEmpty())
continue;
key.Clear();
key << _T("/editor/color_sets/") << m_Name << _T("/") << lang;
if (!ConfigManager::Get()->HasGroup(key))
continue;
for (unsigned int i = 0; i < m_Sets[x].m_Colors.GetCount(); ++i)
{
OptionColor* opt = m_Sets[x].m_Colors.Item(i);
wxString tmpKey;
tmpKey << key << _T("/") << opt->name;
if (ConfigManager::Get()->HasGroup(tmpKey + _T("/fore")))
{
opt->fore = wxColour(ConfigManager::Get()->Read(tmpKey + _T("/fore/red"), opt->fore.Red()),
ConfigManager::Get()->Read(tmpKey + _T("/fore/green"), opt->fore.Green()),
ConfigManager::Get()->Read(tmpKey + _T("/fore/blue"), opt->fore.Blue())
);
}
if (ConfigManager::Get()->HasGroup(tmpKey + _T("/back")))
{
opt->back = wxColour(ConfigManager::Get()->Read(tmpKey+ _T("/back/red"), opt->back.Red()),
ConfigManager::Get()->Read(tmpKey + _T("/back/green"), opt->back.Green()),
ConfigManager::Get()->Read(tmpKey + _T("/back/blue"), opt->back.Blue())
);
}
opt->bold = ConfigManager::Get()->Read(tmpKey + _T("/bold"), opt->bold);
opt->italics = ConfigManager::Get()->Read(tmpKey + _T("/italics"), opt->italics);
opt->underlined = ConfigManager::Get()->Read(tmpKey + _T("/underlined"), opt->underlined);
opt->isStyle = ConfigManager::Get()->Read(tmpKey + _T("/isStyle"), opt->underlined);
}
for (int i = 0; i < 3; ++i)
{
wxString tmpkey;
tmpkey.Printf(_T("%s/editor/keywords/%d"), key.c_str(), i);
m_Sets[x].m_Keywords[i] = ConfigManager::Get()->Read(tmpkey, _T(""));
}
}
}
void EditorColorSet::Reset(HighlightLanguage lang)
{
wxLogNull ln;
wxString key;
key << _T("/editor/color_sets/") << m_Name << _T("/") << GetLanguageName(lang);
if (ConfigManager::Get()->HasGroup(key))
ConfigManager::Get()->DeleteGroup(key);
ClearAllOptionColors();
LoadAvailableSets();
}
wxString& EditorColorSet::GetKeywords(HighlightLanguage lang, int idx)
{
return m_Sets[lang].m_Keywords[idx];
}
void EditorColorSet::SetKeywords(HighlightLanguage lang, int idx, const wxString& keywords)
{
if (lang != HL_NONE && idx >=0 && idx < 3)
{
m_Sets[lang].m_Keywords[idx] = keywords;
m_Sets[lang].m_Keywords[idx].Replace(_T("\r"), _T(" "));
m_Sets[lang].m_Keywords[idx].Replace(_T("\n"), _T(" "));
m_Sets[lang].m_Keywords[idx].Replace(_T("\t"), _T(" "));
while (m_Sets[lang].m_Keywords[idx].Replace(_T(" "), _T(" ")))
;
}
}
const wxArrayString& EditorColorSet::GetFileMasks(HighlightLanguage lang)
{
return m_Sets[lang].m_FileMasks;
}
void EditorColorSet::SetFileMasks(HighlightLanguage lang, const wxString& masks, const wxString& separator)
{
if (lang != HL_NONE)
m_Sets[lang].m_FileMasks = GetArrayFromString(masks.Lower(), separator);
}
wxString EditorColorSet::GetSampleCode(HighlightLanguage lang, int* breakLine, int* debugLine, int* errorLine)
{
if (lang == HL_NONE)
return wxEmptyString;
if (breakLine)
*breakLine = m_Sets[lang].m_BreakLine;
if (debugLine)
*debugLine = m_Sets[lang].m_DebugLine;
if (errorLine)
*errorLine = m_Sets[lang].m_ErrorLine;
wxString path = ConfigManager::Get()->Read(_T("data_path")) + _T("/lexers/");
if (!m_Sets[lang].m_SampleCode.IsEmpty())
return path + m_Sets[lang].m_SampleCode;
return wxEmptyString;
}
void EditorColorSet::SetSampleCode(HighlightLanguage lang, const wxString& sample, int breakLine, int debugLine, int errorLine)
{
if (lang == HL_NONE)
return;
m_Sets[lang].m_SampleCode = sample;
m_Sets[lang].m_BreakLine = breakLine;
m_Sets[lang].m_DebugLine = debugLine;
m_Sets[lang].m_ErrorLine = errorLine;
}
syntax highlighted by Code2HTML, v. 0.9.1