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