1 /*
2 * This file is part of the Code::Blocks IDE and licensed under the GNU Lesser General Public License, version 3
3 * http://www.gnu.org/licenses/lgpl-3.0.html
4 *
5 * $Revision: 11482 $
6 * $Id: editorcolourset.cpp 11482 2018-09-29 12:20:40Z fuscated $
7 * $HeadURL: svn://svn.code.sf.net/p/codeblocks/code/branches/release-20.xx/src/sdk/editorcolourset.cpp $
8 */
9
10 #include "sdk_precomp.h"
11
12 #ifndef CB_PRECOMP
13 #include <wx/dir.h>
14 #include <wx/settings.h>
15
16 #include "globals.h"
17 #include "cbeditor.h"
18 #include "configmanager.h"
19 #include "logmanager.h"
20 #include "filemanager.h"
21 #include "manager.h"
22 #endif
23
24 #include <wx/regex.h>
25 #include <wx/txtstrm.h> // wxTextInputStream
26 #include <wx/wfstream.h> // wxFileInputStream
27
28 #include "cbcolourmanager.h"
29 #include "cbstyledtextctrl.h"
30
31 #include "editorcolourset.h"
32 #include "editorlexerloader.h"
33 #include "filefilters.h"
34
EditorColourSet(const wxString & setName)35 EditorColourSet::EditorColourSet(const wxString& setName)
36 : m_Name(setName)
37 {
38 LoadAvailableSets();
39
40 if (setName.IsEmpty())
41 m_Name = COLORSET_DEFAULT;
42 else
43 Load();
44 }
45
EditorColourSet(const EditorColourSet & other)46 EditorColourSet::EditorColourSet(const EditorColourSet& other) :// copy ctor
47 m_Name(other.m_Name),
48 m_PlainTextLexerID(other.m_PlainTextLexerID)
49 {
50 m_Sets.clear();
51
52 for (OptionSetsMap::const_iterator it = other.m_Sets.begin(); it != other.m_Sets.end(); ++it)
53 {
54 OptionSet& mset = m_Sets[it->first];
55
56 mset.m_Langs = it->second.m_Langs;
57 mset.m_Lexers = it->second.m_Lexers;
58 for (int i = 0; i <= wxSCI_KEYWORDSET_MAX; ++i)
59 {
60 mset.m_Keywords[i] = it->second.m_Keywords[i];
61 mset.m_originalKeywords[i] = it->second.m_originalKeywords[i];
62 }
63 mset.m_FileMasks = it->second.m_FileMasks;
64 mset.m_originalFileMasks = it->second.m_originalFileMasks;
65 mset.m_SampleCode = it->second.m_SampleCode;
66 mset.m_BreakLine = it->second.m_BreakLine;
67 mset.m_DebugLine = it->second.m_DebugLine;
68 mset.m_ErrorLine = it->second.m_ErrorLine;
69 mset.comment = it->second.comment;
70 mset.m_CaseSensitive = it->second.m_CaseSensitive;
71 const OptionColours& value = it->second.m_Colours;
72 for (unsigned int i = 0; i < value.GetCount(); ++i)
73 {
74 AddOption(it->first, value[i]);
75 }
76 }
77 }
78
~EditorColourSet()79 EditorColourSet::~EditorColourSet()
80 {
81 ClearAllOptionColours();
82 }
83
ClearAllOptionColours()84 void EditorColourSet::ClearAllOptionColours()
85 {
86 for (OptionSetsMap::iterator map_it = m_Sets.begin();
87 map_it != m_Sets.end(); ++map_it)
88 {
89 for (OptionColours::iterator vec_it = (*map_it).second.m_Colours.begin();
90 vec_it != (*map_it).second.m_Colours.end(); ++vec_it)
91 {
92 delete (*vec_it);
93 }
94 }
95 m_Sets.clear();
96 }
97
LoadAvailableSets()98 void EditorColourSet::LoadAvailableSets()
99 {
100 // no need for syntax highlighting if batch building
101 if (Manager::IsBatchBuild())
102 return;
103
104 wxDir dir;
105 wxString filename;
106 FileManager *fm = FileManager::Get();
107 std::list<LoaderBase*> loaders;
108 int count = 0;
109
110 // user paths first
111 wxString path = ConfigManager::GetFolder(sdDataUser) + _T("/lexers/");
112 if (wxDirExists(path) && dir.Open(path))
113 {
114 Manager::Get()->GetLogManager()->Log(F(_("Scanning for lexers in %s..."), path.wx_str()));
115 bool ok = dir.GetFirst(&filename, _T("lexer_*.xml"), wxDIR_FILES);
116 while (ok)
117 {
118 loaders.push_back(fm->Load(path + filename));
119 ok = dir.GetNext(&filename);
120 ++count;
121 }
122 Manager::Get()->GetLogManager()->Log(F(_("Found %d lexers"), count));
123 count = 0;
124 }
125
126 // global paths next
127 path = ConfigManager::GetFolder(sdDataGlobal) + _T("/lexers/");
128 if (wxDirExists(path) && dir.Open(path))
129 {
130 Manager::Get()->GetLogManager()->Log(F(_("Scanning for lexers in %s..."), path.wx_str()));
131 bool ok = dir.GetFirst(&filename, _T("lexer_*.xml"), wxDIR_FILES);
132 while (ok)
133 {
134 loaders.push_back(fm->Load(path + filename));
135 ok = dir.GetNext(&filename);
136 ++count;
137 }
138 Manager::Get()->GetLogManager()->Log(F(_("Found %d lexers"), count));
139 }
140
141 EditorLexerLoader lex(this);
142 for (std::list<LoaderBase*>::iterator it = loaders.begin(); it != loaders.end(); ++it)
143 lex.Load(*it);
144
145 ::Delete(loaders);
146
147
148 for (OptionSetsMap::iterator it = m_Sets.begin(); it != m_Sets.end(); ++it)
149 {
150 wxString lang = it->second.m_Langs;
151 if (lang.IsEmpty())
152 continue;
153
154 // keep the original filemasks and keywords, so we know what needs saving later
155 for (int i = 0; i <= wxSCI_KEYWORDSET_MAX; ++i)
156 {
157 it->second.m_originalKeywords[i] = it->second.m_Keywords[i];
158 }
159 it->second.m_originalFileMasks = it->second.m_FileMasks;
160
161 // remove old settings, no longer used
162 unsigned int i = 0;
163 while (i < it->second.m_Colours.GetCount())
164 {
165 OptionColour* opt = it->second.m_Colours.Item(i);
166 // valid values are:
167 if (opt->value < 0 && // styles >= 0
168 opt->value != cbSELECTION && // cbSELECTION
169 opt->value != cbHIGHLIGHT_LINE) // cbHIGHLIGHT_LINE
170 {
171 it->second.m_Colours.Remove(opt);
172 delete opt;
173 }
174 else
175 ++i;
176 }
177 }
178 }
179
AddHighlightLanguage(int lexer,const wxString & name)180 HighlightLanguage EditorColourSet::AddHighlightLanguage(int lexer, const wxString& name)
181 {
182 if ( lexer < wxSCI_LEX_NULL
183 || lexer > wxSCI_LEX_LAST // this is a C::B extension to wxscintilla.h
184 || name.IsEmpty() )
185 {
186 return HL_NONE;
187 }
188
189 // fix name to be XML compliant
190 wxString newID;
191 size_t pos = 0;
192 while (pos < name.Length())
193 {
194 wxChar ch = name[pos];
195 if (wxIsalnum(ch) || ch == _T('_'))
196 newID.Append(ch); // valid character
197 else if (wxIsspace(ch))
198 newID.Append(_T('_')); // convert spaces to underscores
199 ++pos;
200 }
201 // make sure it's not starting with a number or underscore.
202 // if it is, prepend an 'A'
203 if (wxIsdigit(newID.GetChar(0)) || newID.GetChar(0) == _T('_'))
204 newID.Prepend(_T('A'));
205
206 if (GetHighlightLanguage(newID) != HL_NONE)
207 return HL_NONE;
208
209 m_Sets[newID].m_Langs = name;
210 m_Sets[newID].m_Lexers = lexer;
211
212 if (lexer == wxSCI_LEX_NULL && name == wxT("Plain text files"))
213 m_PlainTextLexerID = newID;
214
215 return newID;
216 }
217
GetHighlightLanguage(const wxString & name)218 HighlightLanguage EditorColourSet::GetHighlightLanguage(const wxString& name)
219 {
220 for (OptionSetsMap::iterator it = m_Sets.begin(); it != m_Sets.end(); ++it)
221 {
222 if (it->second.m_Langs.CmpNoCase(name) == 0)
223 return it->first;
224 }
225 return HL_NONE;
226 }
227
228 // from scintilla lexer (wxSCI_LEX_*)
229 // Warning: the first one found is returned!
GetHighlightLanguage(int lexer)230 HighlightLanguage EditorColourSet::GetHighlightLanguage(int lexer)
231 {
232 for (OptionSetsMap::iterator it = m_Sets.begin(); it != m_Sets.end(); ++it)
233 {
234 if (it->second.m_Lexers == lexer)
235 return it->first;
236 }
237 return HL_NONE;
238 }
239
240 // sorting helper function
CompareStringNoCase(const wxString & first,const wxString & second)241 static int CompareStringNoCase(const wxString& first, const wxString& second)
242 {
243 return first.CmpNoCase(second);
244 }
245
GetAllHighlightLanguages()246 wxArrayString EditorColourSet::GetAllHighlightLanguages()
247 {
248 wxArrayString ret;
249 for (OptionSetsMap::iterator it = m_Sets.begin(); it != m_Sets.end(); ++it)
250 {
251 // Make sure to skip the "plain text files", because we don't want it in the list. We
252 // manually add "Plain text" to the appropriate places.
253 if (!it->second.m_Langs.IsEmpty() && it->first != m_PlainTextLexerID)
254 ret.Add(it->second.m_Langs);
255 }
256 ret.Sort(CompareStringNoCase);
257 return ret;
258 }
259
UpdateOptionsWithSameName(HighlightLanguage lang,OptionColour * base)260 void EditorColourSet::UpdateOptionsWithSameName(HighlightLanguage lang, OptionColour* base)
261 {
262 if (!base)
263 return;
264 // first find the index of this option
265 int idx = -1;
266 OptionSet& mset = m_Sets[lang];
267 for (unsigned int i = 0; i < mset.m_Colours.GetCount(); ++i)
268 {
269 OptionColour* opt = mset.m_Colours.Item(i);
270 if (opt == base)
271 {
272 idx = i;
273 break;
274 }
275 }
276 if (idx == -1)
277 return;
278
279 // now loop again, but update the other options with the same name
280 for (unsigned int i = 0; i < mset.m_Colours.GetCount(); ++i)
281 {
282 if ((int)i == idx)
283 continue; // skip the base option
284 OptionColour* opt = mset.m_Colours.Item(i);
285 if (!opt->name.Matches(base->name))
286 continue;
287 opt->fore = base->fore;
288 opt->back = base->back;
289 opt->bold = base->bold;
290 opt->italics = base->italics;
291 opt->underlined = base->underlined;
292 }
293 }
294
AddOption(HighlightLanguage lang,OptionColour * option,bool checkIfExists)295 bool EditorColourSet::AddOption(HighlightLanguage lang, OptionColour* option, bool checkIfExists)
296 {
297 if (lang == HL_NONE)
298 lang = m_PlainTextLexerID;
299
300 if (checkIfExists && GetOptionByValue(lang, option->value))
301 return false;
302
303 OptionColours& colours = m_Sets[lang].m_Colours;
304 colours.Add(new OptionColour(*option));
305 return true;
306 }
307
AddOption(HighlightLanguage lang,const wxString & name,int value,wxColour fore,wxColour back,bool bold,bool italics,bool underlined,bool isStyle)308 void EditorColourSet::AddOption(HighlightLanguage lang,
309 const wxString& name,
310 int value,
311 wxColour fore,
312 wxColour back,
313 bool bold,
314 bool italics,
315 bool underlined,
316 bool isStyle)
317 {
318 if (lang == HL_NONE)
319 lang = m_PlainTextLexerID;
320
321 OptionColour* opt = new OptionColour;
322 opt->name = name;
323 opt->value = value;
324 opt->fore = fore;
325 opt->back = back;
326 opt->bold = bold;
327 opt->italics = italics;
328 opt->underlined = underlined;
329 opt->isStyle = isStyle;
330
331 opt->originalfore = fore;
332 opt->originalback = back;
333 opt->originalbold = bold;
334 opt->originalitalics = italics;
335 opt->originalunderlined = underlined;
336 opt->originalisStyle = isStyle;
337
338 AddOption(lang, opt);
339 delete opt;
340 }
341
GetOptionByName(HighlightLanguage lang,const wxString & name)342 OptionColour* EditorColourSet::GetOptionByName(HighlightLanguage lang, const wxString& name)
343 {
344 if (lang == HL_NONE)
345 lang = m_PlainTextLexerID;
346
347 OptionSet& mset = m_Sets[lang];
348 for (unsigned int i = 0; i < mset.m_Colours.GetCount(); ++i)
349 {
350 OptionColour* opt = mset.m_Colours.Item(i);
351 if (opt->name == name)
352 return opt;
353 }
354 return nullptr;
355 }
356
GetOptionByValue(HighlightLanguage lang,int value)357 OptionColour* EditorColourSet::GetOptionByValue(HighlightLanguage lang, int value)
358 {
359 if (lang == HL_NONE)
360 lang = m_PlainTextLexerID;
361
362 OptionSet& mset = m_Sets[lang];
363 for (unsigned int i = 0; i < mset.m_Colours.GetCount(); ++i)
364 {
365 OptionColour* opt = mset.m_Colours.Item(i);
366 if (opt->value == value)
367 return opt;
368 }
369 return nullptr;
370 }
371
GetOptionByIndex(HighlightLanguage lang,int index)372 OptionColour* EditorColourSet::GetOptionByIndex(HighlightLanguage lang, int index)
373 {
374 if (lang == HL_NONE)
375 return m_Sets[m_PlainTextLexerID].m_Colours.Item(index);
376 return m_Sets[lang].m_Colours.Item(index);
377 }
378
GetOptionCount(HighlightLanguage lang)379 int EditorColourSet::GetOptionCount(HighlightLanguage lang)
380 {
381 if (lang != HL_NONE)
382 return m_Sets[lang].m_Colours.GetCount();
383 else
384 return m_Sets[m_PlainTextLexerID].m_Colours.GetCount();
385 }
386
387 // Encapsulate the getter for the default option. The default option
388 // is the option named "Default" and if there is no such option we use
389 // the option with value/index equal to 0.
GetDefaultOption(HighlightLanguage lang)390 OptionColour* EditorColourSet::GetDefaultOption(HighlightLanguage lang)
391 {
392 if (lang == HL_NONE)
393 lang = m_PlainTextLexerID;
394
395 OptionSet& mset = m_Sets[lang];
396 OptionColour *defaultOpt = nullptr;
397 for (size_t i = 0; i < mset.m_Colours.GetCount(); ++i)
398 {
399 OptionColour* opt = mset.m_Colours.Item(i);
400 if (opt->name == wxT("Default"))
401 return opt;
402 if (opt->value == 0)
403 defaultOpt = opt;
404 }
405 return defaultOpt;
406 }
407
GetLanguageForFilename(const wxString & filename)408 HighlightLanguage EditorColourSet::GetLanguageForFilename(const wxString& filename)
409 {
410 // convert filename to lowercase first (m_FileMasks already contains
411 // lowercase-only strings) and allow for filemasks like Makefile.*:
412 wxString lfname = wxFileName(filename.Lower()).GetFullName();
413
414 // first search in filemasks
415 for (OptionSetsMap::iterator it = m_Sets.begin(); it != m_Sets.end(); ++it)
416 {
417 for (unsigned int x = 0; x < it->second.m_FileMasks.GetCount(); ++x)
418 {
419 if (lfname.Matches(it->second.m_FileMasks.Item(x)))
420 return it->first;
421 }
422 }
423 // parse #! directive
424 if ( wxFileExists(filename) )
425 {
426 wxFileInputStream input(filename);
427 wxTextInputStream text(input);
428 wxString line;
429 if (input.IsOk() && !input.Eof() )
430 line = text.ReadLine();
431 if (!line.IsEmpty())
432 {
433 wxRegEx reSheBang(wxT("#![ \t]*([a-zA-Z/]+)[ \t]*([a-zA-Z/]*)"));
434 if (reSheBang.Matches(line))
435 {
436 wxString prog = reSheBang.GetMatch(line, 1);
437 if (prog.EndsWith(wxT("env")))
438 prog = reSheBang.GetMatch(line, 2);
439 if (prog.Find(wxT('/')) != wxNOT_FOUND)
440 prog = prog.AfterLast(wxT('/'));
441 if (prog == wxT("sh"))
442 prog = wxT("bash");
443 HighlightLanguage lang = GetHighlightLanguage(prog);
444 if (lang != HL_NONE)
445 return lang;
446 }
447 else if (line.Trim().StartsWith(wxT("<?xml")))
448 return GetHighlightLanguage(wxT("XML"));
449 }
450 }
451 // standard headers
452 const wxString cppNames = wxT("|"
453 "algorithm|" "atomic|" "array|" "bitset|"
454 "chrono|" "complex|" "condition_variable|" "deque|"
455 "exception|" "fstream|" "forward_list|" "future|"
456 "functional|" "hash_map|" "hash_set|" "initializer_list|"
457 "iomanip|" "ios|" "iostream|" "istream|"
458 "iterator|" "limits|" "list|" "locale|"
459 "map|" "memory|" "mutex|" "new|"
460 "numeric|" "ostream|" "queue|" "random|"
461 "ratio|" "regex|" "set|" "sstream|"
462 "stack|" "stdexcept|" "streambuf|" "string|"
463 "strstream|" "system_error|" "thread|" "tuple|"
464 "typeinfo|" "type_traits|" "unordered_map|" "unordered_set|"
465 "utility|" "valarray|" "vector|"
466
467 "cassert|" "cctype|" "cerrno|" "cfenv|" "cfloat|"
468 "cinttypes|" "ciso646|" "climits|" "clocale|" "cmath|"
469 "csetjmp|" "csignal|" "cstdarg|" "cstdbool|" "cstddef|"
470 "cstdint|" "cstdio|" "cstdlib|" "cstring|" "ctgmath|"
471 "ctime|" "cuchar|" "cwchar|" "cwctype|" );
472 if (cppNames.Find(wxT("|") + lfname + wxT("|")) != wxNOT_FOUND)
473 return GetHighlightLanguage(wxT("C/C++"));
474
475 return HL_NONE;
476 }
477
GetLanguageName(HighlightLanguage lang)478 wxString EditorColourSet::GetLanguageName(HighlightLanguage lang)
479 {
480 if (lang != HL_NONE && lang != m_PlainTextLexerID)
481 {
482 wxString name = m_Sets[lang].m_Langs;
483 if (!name.empty())
484 return name;
485 }
486
487 return _("Plain text");
488 }
489
DoApplyStyle(cbStyledTextCtrl * control,int value,OptionColour * option)490 void EditorColourSet::DoApplyStyle(cbStyledTextCtrl* control, int value, OptionColour* option)
491 {
492 // option->value is ignored here...
493 // value is used instead
494 if (option->fore != wxNullColour)
495 control->StyleSetForeground(value, option->fore);
496 if (option->back != wxNullColour)
497 control->StyleSetBackground(value, option->back);
498 control->StyleSetBold(value, option->bold);
499 control->StyleSetItalic(value, option->italics);
500 control->StyleSetUnderline(value, option->underlined);
501 }
502
Apply(cbEditor * editor,HighlightLanguage lang,bool colourise)503 HighlightLanguage EditorColourSet::Apply(cbEditor* editor, HighlightLanguage lang, bool colourise)
504 {
505 if (!editor)
506 return HL_NONE;
507
508 if (lang == HL_AUTO)
509 lang = GetLanguageForFilename(editor->GetFilename());
510
511 const bool isC = ( Manager::Get()->GetConfigManager(wxT("editor"))->ReadBool(wxT("no_stl_in_c"), true)
512 && lang == GetHighlightLanguage(wxT("C/C++"))
513 && editor->GetFilename().Lower().EndsWith(wxT(".c")) );
514
515 Apply(lang, editor->GetLeftSplitViewControl(), isC, colourise);
516 Apply(lang, editor->GetRightSplitViewControl(), isC, colourise);
517
518 return lang;
519 }
520
521
522
523 // Encapsulate the getter for the default option. The default option
524 // is the option named "Default" and if there is no such option we use
525 // the option with value/index equal to 0.
GetDefaultOption(OptionSet & mset)526 static OptionColour* GetDefaultOption(OptionSet& mset)
527 {
528 OptionColour *defaultOpt = nullptr;
529 for (size_t i = 0; i < mset.m_Colours.GetCount(); ++i)
530 {
531 OptionColour* opt = mset.m_Colours.Item(i);
532 if (opt->name == wxT("Default"))
533 return opt;
534 if (opt->value == 0)
535 defaultOpt = opt;
536 }
537 return defaultOpt;
538 }
539
Apply(HighlightLanguage lang,cbStyledTextCtrl * control,bool isC,bool colourise)540 void EditorColourSet::Apply(HighlightLanguage lang, cbStyledTextCtrl* control, bool isC,
541 bool colourise)
542 {
543 if (!control)
544 return;
545 control->StyleClearAll();
546
547 if (lang == HL_NONE)
548 {
549 if (m_PlainTextLexerID.empty())
550 return;
551 lang = m_PlainTextLexerID;
552 }
553
554 // first load the default colours to all styles used by the actual lexer (ignoring some built-in styles)
555 OptionSet& mset = m_Sets[lang];
556 OptionColour* defaults = ::GetDefaultOption(mset);
557 control->SetLexer(mset.m_Lexers);
558 control->SetStyleBits(control->GetStyleBitsNeeded());
559 if (defaults)
560 {
561 int countStyles = 1 << control->GetStyleBits();
562 // walk until countStyles, otherwise the background-colour is only set for characters,
563 // not for empty background
564 for (int i = 0; i <= countStyles; ++i)
565 {
566 if (i < 33 || (i > 39 && i < wxSCI_STYLE_MAX))
567 DoApplyStyle(control, i, defaults);
568 }
569 }
570
571 // Calling StyleClearAll above clears the style for the line numbers, so we have to re-apply it.
572 ColourManager *colours = Manager::Get()->GetColourManager();
573 control->StyleSetForeground(wxSCI_STYLE_LINENUMBER, colours->GetColour(wxT("editor_linenumbers_fg")));
574 control->StyleSetBackground(wxSCI_STYLE_LINENUMBER, colours->GetColour(wxT("editor_linenumbers_bg")));
575
576 for (unsigned int i = 0; i < mset.m_Colours.GetCount(); ++i)
577 {
578 OptionColour* opt = mset.m_Colours.Item(i);
579
580 if (opt->isStyle)
581 {
582 DoApplyStyle(control, opt->value, opt);
583 }
584 else
585 {
586 if (opt->value == cbHIGHLIGHT_LINE)
587 {
588 control->SetCaretLineBackground(opt->back);
589 }
590 else if (opt->value == cbSELECTION)
591 {
592 if (opt->back != wxNullColour)
593 {
594 control->SetSelBackground(true, opt->back);
595 // Manager::Get()->GetConfigManager(_T("editor"))->Write(_T("/selection_colour"), opt->back);
596 }
597 else
598 control->SetSelBackground(false, wxColour(0xD9, 0xD9, 0xD9));
599
600 if (opt->fore != wxNullColour)
601 {
602 control->SetSelForeground(true, opt->fore);
603 // Manager::Get()->GetConfigManager(_T("editor"))->Write(_T("/selection_fgcolour"), opt->fore);
604 }
605 else
606 control->SetSelForeground(false, *wxBLACK);
607 }
608 // else
609 // {
610 // control->MarkerDefine(-opt->value, 1);
611 // control->MarkerSetBackground(-opt->value, opt->back);
612 // }
613 }
614 }
615 for (int i = 0; i <= wxSCI_KEYWORDSET_MAX; ++i)
616 {
617 if (!isC || i != 1) // exclude stl highlights for C
618 control->SetKeyWords(i, mset.m_Keywords[i]);
619 }
620
621 if (colourise)
622 control->Colourise(0, -1); // the *most* important part!
623 }
624
Save()625 void EditorColourSet::Save()
626 {
627 // no need for syntax highlighting if batch building
628 if (Manager::IsBatchBuild())
629 return;
630
631 wxString key;
632 ConfigManager* cfg = Manager::Get()->GetConfigManager(_T("editor"));
633
634 //FIXME: Commenting out the following line is no definite cure, but it hides the annoying disappearing colourset for now
635 //NOTE (mandrav): uncommenting it doesn't seem to cause any trouble (at least now). What was the problem?
636 cfg->DeleteSubPath(_T("/colour_sets/") + m_Name);
637
638 // write the theme name
639 cfg->Write(_T("/colour_sets/") + m_Name + _T("/name"), m_Name);
640
641 for (OptionSetsMap::iterator it = m_Sets.begin(); it != m_Sets.end(); ++it)
642 {
643 if (it->first == HL_NONE || it->first == HL_AUTO)
644 continue;
645 wxString lang = it->first;
646
647 bool gsaved = false;
648
649 key.Clear();
650 key << _T("/colour_sets/") << m_Name << _T('/') << lang;
651 for (unsigned int i = 0; i < it->second.m_Colours.GetCount(); ++i)
652 {
653 OptionColour* opt = it->second.m_Colours.Item(i);
654 wxString tmpKey;
655 tmpKey << key << _T("/style") << wxString::Format(_T("%u"), i);
656
657 bool saved = false;
658
659 if (opt->fore != opt->originalfore)
660 {
661 cfg->Write(tmpKey + _T("/fore"), opt->fore);
662 saved = true;
663 }
664 if (opt->back != opt->originalback)
665 {
666 cfg->Write(tmpKey + _T("/back"), opt->back);
667 saved = true;
668 }
669 if (opt->bold != opt->originalbold)
670 {
671 cfg->Write(tmpKey + _T("/bold"), opt->bold);
672 saved = true;
673 }
674 if (opt->italics != opt->originalitalics)
675 {
676 cfg->Write(tmpKey + _T("/italics"), opt->italics);
677 saved = true;
678 }
679 if (opt->underlined != opt->originalunderlined)
680 {
681 cfg->Write(tmpKey + _T("/underlined"), opt->underlined);
682 saved = true;
683 }
684 if (opt->isStyle != opt->originalisStyle)
685 {
686 cfg->Write(tmpKey + _T("/isStyle"), opt->isStyle);
687 saved = true;
688 }
689
690 if (saved)
691 {
692 cfg->Write(tmpKey + _T("/name"), opt->name, true);
693 gsaved = true;
694 }
695 }
696 wxString tmpkey;
697 for (int i = 0; i <= wxSCI_KEYWORDSET_MAX; ++i)
698 {
699 if (it->second.m_Keywords[i] != it->second.m_originalKeywords[i])
700 {
701 tmpkey.Printf(_T("%s/editor/keywords/set%d"), key.c_str(), i);
702 cfg->Write(tmpkey, it->second.m_Keywords[i]);
703 gsaved = true;
704 }
705 }
706 tmpkey.Printf(_T("%s/editor/filemasks"), key.c_str());
707 wxString tmparr = GetStringFromArray(it->second.m_FileMasks, _T(","));
708 wxString tmparrorig = GetStringFromArray(it->second.m_originalFileMasks, _T(","));
709 if (tmparr != tmparrorig)
710 {
711 cfg->Write(tmpkey, tmparr);
712 gsaved = true;
713 }
714
715 if (gsaved)
716 cfg->Write(key + _T("/name"), it->second.m_Langs);
717 }
718 }
719
Load()720 void EditorColourSet::Load()
721 {
722 // no need for syntax highlighting if batch building
723 if (Manager::IsBatchBuild())
724 return;
725
726 static bool s_notifiedUser = false;
727
728 wxString key;
729 ConfigManager* cfg = Manager::Get()->GetConfigManager(_T("editor"));
730
731 // read the theme name
732 m_Name = cfg->Read(_T("/colour_sets/") + m_Name + _T("/name"), m_Name);
733
734 int x = 0;
735 for (OptionSetsMap::iterator it = m_Sets.begin(); it != m_Sets.end(); ++it)
736 {
737 if (it->first == HL_NONE || it->first == HL_AUTO)
738 continue;
739
740 // look for old-style configuration
741 key.Clear();
742 key << _T("/colour_sets/") << m_Name << _T("/set") << wxString::Format(_T("%d"), x++);
743 if (cfg->Exists(key + _T("/name")))
744 {
745 // old-style configuration
746 // delete it and tell the user about it
747 cfg->DeleteSubPath(key);
748 if (!s_notifiedUser)
749 {
750 cbMessageBox(_("The way editor syntax highlighting configuration is saved, has changed.\n"
751 "Syntax highlighting for all supported languages will revert to defaults now.\n"
752 "We 're sorry for the inconvenience..."),
753 _("Information"),
754 wxICON_INFORMATION);
755 s_notifiedUser = true;
756 }
757 continue;
758 }
759 // make sure we didn't create it accidentally
760 cfg->DeleteSubPath(key);
761
762 // new-style configuration key
763 key.Clear();
764 key << _T("/colour_sets/") << m_Name << _T('/') << it->first;
765 if (!cfg->Exists(key + _T("/name")))
766 {
767 // make sure we didn't create it accidentally
768 cfg->DeleteSubPath(key);
769 continue;
770 }
771
772 for (unsigned int i = 0; i < it->second.m_Colours.GetCount(); ++i)
773 {
774 wxString tmpKey;
775 tmpKey << key << _T("/style") << wxString::Format(_T("%u"), i);
776 if (!cfg->Exists(tmpKey + _T("/name")))
777 {
778 // make sure we didn't create it accidentally
779 cfg->DeleteSubPath(tmpKey);
780 continue;
781 }
782 wxString name = cfg->Read(tmpKey + _T("/name"));
783 for (size_t j = 0; j < it->second.m_Colours.GetCount(); ++j)
784 {
785 OptionColour* opt = it->second.m_Colours.Item(j);
786 if (!opt || opt->name != name)
787 continue;
788
789 if (cfg->Exists(tmpKey + _T("/fore")))
790 opt->fore = cfg->ReadColour(tmpKey + _T("/fore"), opt->fore);
791 if (cfg->Exists(tmpKey + _T("/back")))
792 opt->back = cfg->ReadColour(tmpKey + _T("/back"), opt->back);
793 if (cfg->Exists(tmpKey + _T("/bold")))
794 opt->bold = cfg->ReadBool(tmpKey + _T("/bold"), opt->bold);
795 if (cfg->Exists(tmpKey + _T("/italics")))
796 opt->italics = cfg->ReadBool(tmpKey + _T("/italics"), opt->italics);
797 if (cfg->Exists(tmpKey + _T("/underlined")))
798 opt->underlined = cfg->ReadBool(tmpKey + _T("/underlined"), opt->underlined);
799 if (cfg->Exists(tmpKey + _T("/isStyle")))
800 opt->isStyle = cfg->ReadBool(tmpKey + _T("/isStyle"), opt->isStyle);
801 }
802 }
803 wxString tmpkey;
804 for (int i = 0; i <= wxSCI_KEYWORDSET_MAX; ++i)
805 {
806 tmpkey.Printf(_T("%s/editor/keywords/set%d"), key.c_str(), i);
807 if (cfg->Exists(tmpkey))
808 it->second.m_Keywords[i] = cfg->Read(tmpkey, wxEmptyString);
809 }
810 tmpkey.Printf(_T("%s/editor/filemasks"), key.c_str());
811 if (cfg->Exists(tmpkey))
812 it->second.m_FileMasks = GetArrayFromString(cfg->Read(tmpkey, wxEmptyString), _T(","));
813 }
814 }
815
Reset(HighlightLanguage lang)816 void EditorColourSet::Reset(HighlightLanguage lang)
817 {
818 // no need for syntax highlighting if batch building
819 if (Manager::IsBatchBuild())
820 return;
821
822 wxString key;
823 key << _T("/colour_sets/") << m_Name << _T('/') << lang;
824 if (Manager::Get()->GetConfigManager(_T("editor"))->Exists(key + _T("/name")))
825 Manager::Get()->GetConfigManager(_T("editor"))->DeleteSubPath(key);
826
827 ClearAllOptionColours();
828 LoadAvailableSets();
829 Load();
830 }
831
GetKeywords(HighlightLanguage lang,int idx)832 wxString& EditorColourSet::GetKeywords(HighlightLanguage lang, int idx)
833 {
834 if (idx < 0 || idx > wxSCI_KEYWORDSET_MAX)
835 idx = 0;
836 return m_Sets[lang].m_Keywords[idx];
837 }
838
SetKeywords(HighlightLanguage lang,int idx,const wxString & keywords)839 void EditorColourSet::SetKeywords(HighlightLanguage lang, int idx, const wxString& keywords)
840 {
841 if (lang != HL_NONE && idx >=0 && idx <= wxSCI_KEYWORDSET_MAX)
842 {
843 wxString tmp(_T(' '), keywords.length()); // faster than using Alloc()
844
845 const wxChar *src = keywords.c_str();
846 #if wxCHECK_VERSION(3, 0, 0)
847 wxStringCharType *dst = const_cast<wxStringCharType*>(tmp.wx_str());
848 #else
849 wxChar *dst = (wxChar *) tmp.c_str();
850 #endif
851 wxChar c;
852 size_t len = 0;
853
854 while ((c = *src))
855 {
856 ++src;
857 if (c > _T(' '))
858 *dst = c;
859 else // white space
860 {
861 *dst = _T(' ');
862 while (*src && *src < _T(' '))
863 ++src;
864 }
865
866 ++dst;
867 ++len;
868 }
869
870 tmp.Truncate(len);
871
872 OptionSet& mset = m_Sets[lang];
873 mset.m_Keywords[idx] = tmp;
874 }
875 }
876
GetFileMasks(HighlightLanguage lang)877 const wxArrayString& EditorColourSet::GetFileMasks(HighlightLanguage lang)
878 {
879 return m_Sets[lang].m_FileMasks;
880 }
881
SetFileMasks(HighlightLanguage lang,const wxString & masks,const wxString & separator)882 void EditorColourSet::SetFileMasks(HighlightLanguage lang, const wxString& masks, const wxString& separator)
883 {
884 if (lang == HL_NONE)
885 lang = m_PlainTextLexerID;
886
887 m_Sets[lang].m_FileMasks = GetArrayFromString(masks.Lower(), separator);
888
889 // let's add these filemasks in the file filters master list ;)
890 FileFilters::Add(wxString::Format(_("%s files"), m_Sets[lang].m_Langs.c_str()), masks);
891 }
892
GetSampleCode(HighlightLanguage lang,int * breakLine,int * debugLine,int * errorLine)893 wxString EditorColourSet::GetSampleCode(HighlightLanguage lang, int* breakLine, int* debugLine, int* errorLine)
894 {
895 if (lang == HL_NONE)
896 lang = m_PlainTextLexerID;
897 OptionSet& mset = m_Sets[lang];
898 if (breakLine)
899 *breakLine = mset.m_BreakLine;
900 if (debugLine)
901 *debugLine = mset.m_DebugLine;
902 if (errorLine)
903 *errorLine = mset.m_ErrorLine;
904
905 wxString shortname;
906 if (mset.m_SampleCode.IsEmpty())
907 shortname = _T("lexer_") + lang + _T(".sample");
908 else
909 shortname = mset.m_SampleCode;
910
911 // user path first
912 wxString path = ConfigManager::GetFolder(sdDataUser) + _T("/lexers/");
913 if (wxFileExists(path + shortname))
914 return path + shortname;
915 else
916 {
917 // global path next
918 path = ConfigManager::GetFolder(sdDataGlobal) + _T("/lexers/");
919 if (wxFileExists(path + shortname))
920 return path + shortname;
921 }
922 return wxEmptyString;
923 }
924
SetSampleCode(HighlightLanguage lang,const wxString & sample,int breakLine,int debugLine,int errorLine)925 void EditorColourSet::SetSampleCode(HighlightLanguage lang, const wxString& sample, int breakLine, int debugLine, int errorLine)
926 {
927 if (lang == HL_NONE)
928 lang = m_PlainTextLexerID;
929 OptionSet& mset = m_Sets[lang];
930 mset.m_SampleCode = sample;
931 mset.m_BreakLine = breakLine;
932 mset.m_DebugLine = debugLine;
933 mset.m_ErrorLine = errorLine;
934 }
935
SetCommentToken(HighlightLanguage lang,CommentToken token)936 void EditorColourSet::SetCommentToken(HighlightLanguage lang, CommentToken token)
937 {
938 if (lang == HL_NONE)
939 lang = m_PlainTextLexerID;
940
941 m_Sets[lang].comment = token;
942 }
943
GetCommentToken(HighlightLanguage lang)944 CommentToken EditorColourSet::GetCommentToken(HighlightLanguage lang)
945 {
946 CommentToken com;
947 com.lineComment = _T("");
948 com.doxygenLineComment = _T("");
949 com.streamCommentStart = _T("");
950 com.streamCommentEnd = _T("");
951 com.doxygenStreamCommentStart = _T("");
952 com.doxygenStreamCommentEnd = _T("");
953 com.boxCommentStart = _T("");
954 com.boxCommentMid = _T("");
955 com.boxCommentEnd = _T("");
956
957 if (lang != HL_NONE)
958 com = m_Sets[lang].comment;
959
960 return com;
961 }
962
SetCaseSensitivity(HighlightLanguage lang,bool CaseSensitive)963 void EditorColourSet::SetCaseSensitivity(HighlightLanguage lang, bool CaseSensitive)
964 {
965 if ( lang == HL_NONE )
966 lang = m_PlainTextLexerID;
967
968 m_Sets[lang].m_CaseSensitive = CaseSensitive;
969 }
970
GetCaseSensitivity(HighlightLanguage lang)971 bool EditorColourSet::GetCaseSensitivity(HighlightLanguage lang)
972 {
973 if ( lang == HL_NONE )
974 lang = m_PlainTextLexerID;
975
976 return m_Sets[lang].m_CaseSensitive;
977 }
978
SetStringLexerStyles(HighlightLanguage lang,const std::set<int> & styles)979 void EditorColourSet::SetStringLexerStyles(HighlightLanguage lang, const std::set<int> &styles)
980 {
981 if ( lang == HL_NONE )
982 lang = m_PlainTextLexerID;
983
984 cbStyledTextCtrl::GetStringLexerStyles()[m_Sets[lang].m_Lexers] = styles;
985 }
986
SetCommentLexerStyles(HighlightLanguage lang,const std::set<int> & styles)987 void EditorColourSet::SetCommentLexerStyles(HighlightLanguage lang, const std::set<int> &styles)
988 {
989 if ( lang == HL_NONE )
990 lang = m_PlainTextLexerID;
991
992 cbStyledTextCtrl::GetCommentLexerStyles()[m_Sets[lang].m_Lexers] = styles;
993 }
994
SetCharacterLexerStyles(HighlightLanguage lang,const std::set<int> & styles)995 void EditorColourSet::SetCharacterLexerStyles(HighlightLanguage lang, const std::set<int> &styles)
996 {
997 if ( lang == HL_NONE )
998 lang = m_PlainTextLexerID;
999
1000 cbStyledTextCtrl::GetCharacterLexerStyles()[m_Sets[lang].m_Lexers] = styles;
1001 }
1002
SetPreprocessorLexerStyles(HighlightLanguage lang,const std::set<int> & styles)1003 void EditorColourSet::SetPreprocessorLexerStyles(HighlightLanguage lang, const std::set<int> &styles)
1004 {
1005 if ( lang == HL_NONE )
1006 lang = m_PlainTextLexerID;
1007
1008 cbStyledTextCtrl::GetPreprocessorLexerStyles()[m_Sets[lang].m_Lexers] = styles;
1009 }
1010