1 // SciTE - Scintilla based Text Editor
2 /** @file SciTEProps.cxx
3  ** Properties management.
4  **/
5 // Copyright 1998-2011 by Neil Hodgson <neilh@scintilla.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
7 
8 #include <cstddef>
9 #include <cstdlib>
10 #include <cstdint>
11 #include <cstring>
12 #include <cstdio>
13 #include <ctime>
14 #include <clocale>
15 
16 #include <string>
17 #include <string_view>
18 #include <vector>
19 #include <map>
20 #include <set>
21 #include <algorithm>
22 #include <memory>
23 #include <chrono>
24 #include <atomic>
25 #include <mutex>
26 
27 #include <fcntl.h>
28 
29 #include "ILexer.h"
30 
31 #include "ScintillaTypes.h"
32 #include "ScintillaMessages.h"
33 #include "ScintillaCall.h"
34 
35 #include "Scintilla.h"
36 #include "SciLexer.h"
37 
38 #include "GUI.h"
39 #include "ScintillaWindow.h"
40 
41 #if defined(__unix__) || defined(__APPLE__)
42 
43 const GUI::gui_char menuAccessIndicator[] = GUI_TEXT("_");
44 
45 #else
46 
47 const GUI::gui_char menuAccessIndicator[] = GUI_TEXT("&");
48 
49 #endif
50 
51 #include "StringList.h"
52 #include "StringHelpers.h"
53 #include "FilePath.h"
54 #include "LexillaLibrary.h"
55 #include "StyleDefinition.h"
56 #include "PropSetFile.h"
57 #include "StyleWriter.h"
58 #include "Extender.h"
59 #include "SciTE.h"
60 #include "JobQueue.h"
61 #include "Cookie.h"
62 #include "Worker.h"
63 #include "MatchMarker.h"
64 #include "EditorConfig.h"
65 #include "SciTEBase.h"
66 #include "IFaceTable.h"
67 
SetImportMenu()68 void SciTEBase::SetImportMenu() {
69 	for (int i = 0; i < importMax; i++) {
70 		DestroyMenuItem(menuOptions, importCmdID + i);
71 	}
72 	if (!importFiles.empty()) {
73 		for (int stackPos = 0; stackPos < static_cast<int>(importFiles.size()) && stackPos < importMax; stackPos++) {
74 			const int itemID = importCmdID + stackPos;
75 			if (importFiles[stackPos].IsSet()) {
76 				GUI::gui_string entry = localiser.Text("Open");
77 				entry += GUI_TEXT(" ");
78 				entry += importFiles[stackPos].Name().AsInternal();
79 				SetMenuItem(menuOptions, IMPORT_START + stackPos, itemID, entry.c_str());
80 			}
81 		}
82 	}
83 }
84 
ImportMenu(int pos)85 void SciTEBase::ImportMenu(int pos) {
86 	if (pos >= 0) {
87 		if (importFiles[pos].IsSet()) {
88 			Open(importFiles[pos]);
89 		}
90 	}
91 }
92 
SetLanguageMenu()93 void SciTEBase::SetLanguageMenu() {
94 	for (int i = 0; i < 100; i++) {
95 		DestroyMenuItem(menuLanguage, languageCmdID + i);
96 	}
97 	for (unsigned int item = 0; item < languageMenu.size(); item++) {
98 		const int itemID = languageCmdID + item;
99 		GUI::gui_string entry = localiser.Text(languageMenu[item].menuItem.c_str());
100 		if (languageMenu[item].menuKey.length()) {
101 #if defined(GTK)
102 			entry += GUI_TEXT(" ");
103 #else
104 			entry += GUI_TEXT("\t");
105 #endif
106 			entry += GUI::StringFromUTF8(languageMenu[item].menuKey);
107 		}
108 		if (entry.size() && entry[0] != '#') {
109 			SetMenuItem(menuLanguage, item, itemID, entry.c_str());
110 		}
111 	}
112 }
113 
114 // Null except on Windows where it may be overridden
ReadEmbeddedProperties()115 void SciTEBase::ReadEmbeddedProperties() {
116 }
117 
118 const GUI::gui_char propLocalFileName[] = GUI_TEXT("SciTE.properties");
119 const GUI::gui_char propDirectoryFileName[] = GUI_TEXT("SciTEDirectory.properties");
120 
ReadEnvironment()121 void SciTEBase::ReadEnvironment() {
122 #if defined(__unix__) || defined(__APPLE__)
123 	extern char **environ;
124 	char **e = environ;
125 #else
126 	char **e = _environ;
127 #endif
128 	for (; e && *e; e++) {
129 		char key[1024] = "";
130 		char *k = *e;
131 		char *v = strchr(k, '=');
132 		if (v && (static_cast<size_t>(v - k) < sizeof(key))) {
133 			memcpy(key, k, v - k);
134 			key[v - k] = '\0';
135 			propsPlatform.Set(key, v + 1);
136 		}
137 	}
138 }
139 
140 /**
141 Read global and user properties files.
142 */
ReadGlobalPropFile()143 void SciTEBase::ReadGlobalPropFile() {
144 	// Appearance and Contrast may be read in embedded or global properties
145 	// so set them in deepest property set propsPlatform.
146 	propsPlatform.Set("Appearance", StdStringFromInteger(appearance.dark));
147 	propsPlatform.Set("Contrast", StdStringFromInteger(appearance.highContrast));
148 
149 	std::string excludes;
150 	std::string includes;
151 
152 	// Want to apply imports.exclude and imports.include but these may well be in
153 	// user properties.
154 
155 	for (int attempt=0; attempt<2; attempt++) {
156 
157 		std::string excludesRead = props.GetString("imports.exclude");
158 		std::string includesRead = props.GetString("imports.include");
159 		if ((attempt > 0) && ((excludesRead == excludes) && (includesRead == includes)))
160 			break;
161 
162 		excludes = excludesRead;
163 		includes = includesRead;
164 
165 		filter.SetFilter(excludes.c_str(), includes.c_str());
166 
167 		importFiles.clear();
168 
169 		ReadEmbeddedProperties();
170 
171 		propsBase.Clear();
172 		FilePath propfileBase = GetDefaultPropertiesFileName();
173 		propsBase.Read(propfileBase, propfileBase.Directory(), filter, &importFiles, 0);
174 
175 		propsUser.Clear();
176 		FilePath propfileUser = GetUserPropertiesFileName();
177 		propsUser.Read(propfileUser, propfileUser.Directory(), filter, &importFiles, 0);
178 	}
179 
180 	if (!localiser.read) {
181 		ReadLocalization();
182 	}
183 }
184 
ReadAbbrevPropFile()185 void SciTEBase::ReadAbbrevPropFile() {
186 	propsAbbrev.Clear();
187 	propsAbbrev.Read(pathAbbreviations, pathAbbreviations.Directory(), filter, &importFiles, 0);
188 }
189 
190 /**
191 Reads the directory properties file depending on the variable
192 "properties.directory.enable". Also sets the variable $(SciteDirectoryHome) to the path
193 where this property file is found. If it is not found $(SciteDirectoryHome) will
194 be set to $(FilePath).
195 */
ReadDirectoryPropFile()196 void SciTEBase::ReadDirectoryPropFile() {
197 	propsDirectory.Clear();
198 
199 	if (props.GetInt("properties.directory.enable") != 0) {
200 		FilePath propfile = GetDirectoryPropertiesFileName();
201 		props.Set("SciteDirectoryHome", propfile.Directory().AsUTF8().c_str());
202 
203 		propsDirectory.Read(propfile, propfile.Directory(), filter, nullptr, 0);
204 	}
205 }
206 
207 /**
208 Read local and directory properties file.
209 */
ReadLocalPropFile()210 void SciTEBase::ReadLocalPropFile() {
211 	// The directory properties acts like a base local properties file.
212 	// Therefore it must be read always before reading the local prop file.
213 	ReadDirectoryPropFile();
214 
215 	FilePath propfile = GetLocalPropertiesFileName();
216 
217 	propsLocal.Clear();
218 	propsLocal.Read(propfile, propfile.Directory(), filter, nullptr, 0);
219 
220 	props.Set("Chrome", "#C0C0C0");
221 	props.Set("ChromeHighlight", "#FFFFFF");
222 
223 	FilePath fileDirectory = filePath.Directory();
224 	editorConfig->Clear();
225 	if (props.GetInt("editor.config.enable", 0)) {
226 		editorConfig->ReadFromDirectory(fileDirectory);
227 	}
228 }
229 
ColourOfProperty(const PropSetFile & props,const char * key,SA::Colour colourDefault)230 SA::Colour ColourOfProperty(const PropSetFile &props, const char *key, SA::Colour colourDefault) {
231 	std::string colour = props.GetExpandedString(key);
232 	if (colour.length()) {
233 		return ColourFromString(colour);
234 	}
235 	return colourDefault;
236 }
237 
238 /**
239  * Put the next property item from the given property string
240  * into the buffer pointed by @a pPropItem.
241  * @return NULL if the end of the list is met, else, it points to the next item.
242  */
GetNextPropItem(const char * pStart,char * pPropItem,int maxLen)243 const char *SciTEBase::GetNextPropItem(
244 	const char *pStart,	/**< the property string to parse for the first call,
245 						 * pointer returned by the previous call for the following. */
246 	char *pPropItem,	///< pointer on a buffer receiving the requested prop item
247 	int maxLen) {		///< size of the above buffer
248 	ptrdiff_t size = maxLen - 1;
249 
250 	*pPropItem = '\0';
251 	if (!pStart) {
252 		return nullptr;
253 	}
254 	const char *pNext = strchr(pStart, ',');
255 	if (pNext) {	// Separator is found
256 		if (size > pNext - pStart) {
257 			// Found string fits in buffer
258 			size = pNext - pStart;
259 		}
260 		pNext++;
261 	}
262 	strncpy(pPropItem, pStart, size);
263 	pPropItem[size] = '\0';
264 	return pNext;
265 }
266 
StyleString(const char * lang,int style) const267 std::string SciTEBase::StyleString(const char *lang, int style) const {
268 	char key[200];
269 	sprintf(key, "style.%s.%0d", lang, style);
270 	return props.GetExpandedString(key);
271 }
272 
StyleDefinitionFor(int style)273 StyleDefinition SciTEBase::StyleDefinitionFor(int style) {
274 	const std::string languageName = !StartsWith(language, "lpeg_") ? language : "lpeg";
275 
276 	const std::string ssDefault = StyleString("*", style);
277 	std::string ss = StyleString(languageName.c_str(), style);
278 
279 	if (!subStyleBases.empty()) {
280 		const int baseStyle = wEditor.StyleFromSubStyle(style);
281 		if (baseStyle != style) {
282 			const int primaryStyle = wEditor.PrimaryStyleFromStyle(style);
283 			const int distanceSecondary = (style == primaryStyle) ? 0 : wEditor.DistanceToSecondaryStyles();
284 			const int primaryBase = baseStyle - distanceSecondary;
285 			const int subStylesStart = wEditor.SubStylesStart(primaryBase);
286 			const int subStylesLength = wEditor.SubStylesLength(primaryBase);
287 			const int subStyle = style - (subStylesStart + distanceSecondary);
288 			if (subStyle < subStylesLength) {
289 				char key[200];
290 				sprintf(key, "style.%s.%0d.%0d", languageName.c_str(), baseStyle, subStyle + 1);
291 				ss = props.GetNewExpandString(key);
292 			}
293 		}
294 	}
295 
296 	StyleDefinition sd(ssDefault);
297 	sd.ParseStyleDefinition(ss);
298 	return sd;
299 }
300 
SetOneStyle(GUI::ScintillaWindow & win,int style,const StyleDefinition & sd)301 void SciTEBase::SetOneStyle(GUI::ScintillaWindow &win, int style, const StyleDefinition &sd) {
302 	if (sd.specified & StyleDefinition::sdItalics)
303 		win.StyleSetItalic(style, sd.italics);
304 	if (sd.specified & StyleDefinition::sdWeight)
305 		win.StyleSetWeight(style, sd.weight);
306 	if (sd.specified & StyleDefinition::sdFont)
307 		win.StyleSetFont(style, sd.font.c_str());
308 	if (sd.specified & StyleDefinition::sdFore)
309 		win.StyleSetFore(style, sd.Fore());
310 	if (sd.specified & StyleDefinition::sdBack)
311 		win.StyleSetBack(style, sd.Back());
312 	if (sd.specified & StyleDefinition::sdSize)
313 		win.StyleSetSizeFractional(style, sd.FractionalSize());
314 	if (sd.specified & StyleDefinition::sdEOLFilled)
315 		win.StyleSetEOLFilled(style, sd.eolfilled);
316 	if (sd.specified & StyleDefinition::sdUnderlined)
317 		win.StyleSetUnderline(style, sd.underlined);
318 	if (sd.specified & StyleDefinition::sdCaseForce)
319 		win.StyleSetCase(style, sd.caseForce);
320 	if (sd.specified & StyleDefinition::sdVisible)
321 		win.StyleSetVisible(style, sd.visible);
322 	if (sd.specified & StyleDefinition::sdChangeable)
323 		win.StyleSetChangeable(style, sd.changeable);
324 	win.StyleSetCharacterSet(style, characterSet);
325 }
326 
SetStyleBlock(GUI::ScintillaWindow & win,const char * lang,int start,int last)327 void SciTEBase::SetStyleBlock(GUI::ScintillaWindow &win, const char *lang, int start, int last) {
328 	for (int style = start; style <= last; style++) {
329 		if (style != StyleDefault) {
330 			char key[200];
331 			sprintf(key, "style.%s.%0d", lang, style-start);
332 			std::string sval = props.GetExpandedString(key);
333 			if (sval.length()) {
334 				SetOneStyle(win, style, StyleDefinition(sval));
335 			}
336 		}
337 	}
338 }
339 
SetStyleFor(GUI::ScintillaWindow & win,const char * lang)340 void SciTEBase::SetStyleFor(GUI::ScintillaWindow &win, const char *lang) {
341 	SetStyleBlock(win, lang, 0, StyleMax);
342 }
343 
SetOneIndicator(GUI::ScintillaWindow & win,int indicator,const IndicatorDefinition & ind)344 void SciTEBase::SetOneIndicator(GUI::ScintillaWindow &win, int indicator, const IndicatorDefinition &ind) {
345 	win.IndicSetStyle(indicator, ind.style);
346 	win.IndicSetFore(indicator, ind.colour);
347 	win.IndicSetAlpha(indicator, ind.fillAlpha);
348 	win.IndicSetOutlineAlpha(indicator, ind.outlineAlpha);
349 	win.IndicSetUnder(indicator, ind.under);
350 }
351 
ExtensionFileName() const352 std::string SciTEBase::ExtensionFileName() const {
353 	if (CurrentBufferConst()->overrideExtension.length()) {
354 		return CurrentBufferConst()->overrideExtension;
355 	} else {
356 		FilePath name = FileNameExt();
357 		if (name.IsSet()) {
358 #if !defined(GTK)
359 			// Force extension to lower case
360 			std::string extension = name.Extension().AsUTF8();
361 			if (extension.empty()) {
362 				return name.AsUTF8();
363 			} else {
364 				LowerCaseAZ(extension);
365 				return name.BaseName().AsUTF8() + "." + extension;
366 			}
367 #else
368 			return name.AsUTF8();
369 #endif
370 		} else {
371 			return props.GetString("default.file.ext");
372 		}
373 	}
374 }
375 
ForwardPropertyToEditor(const char * key)376 void SciTEBase::ForwardPropertyToEditor(const char *key) {
377 	if (props.Exists(key)) {
378 		std::string value = props.GetExpandedString(key);
379 		wEditor.SetProperty(key, value.c_str());
380 		wOutput.SetProperty(key, value.c_str());
381 	}
382 }
383 
DefineMarker(SA::MarkerOutline marker,SA::MarkerSymbol markerType,SA::Colour fore,SA::Colour back,SA::Colour backSelected)384 void SciTEBase::DefineMarker(SA::MarkerOutline marker, SA::MarkerSymbol markerType, SA::Colour fore, SA::Colour back, SA::Colour backSelected) {
385 	const int markerNumber = static_cast<int>(marker);
386 	wEditor.MarkerDefine(markerNumber, markerType);
387 	wEditor.MarkerSetFore(markerNumber, fore);
388 	wEditor.MarkerSetBack(markerNumber, back);
389 	wEditor.MarkerSetBackSelected(markerNumber, backSelected);
390 }
391 
ReadAPI(const std::string & fileNameForExtension)392 void SciTEBase::ReadAPI(const std::string &fileNameForExtension) {
393 	std::string sApiFileNames = props.GetNewExpandString("api.",
394 				    fileNameForExtension.c_str());
395 	if (sApiFileNames.length() > 0) {
396 		std::vector<std::string> vApiFileNames = StringSplit(sApiFileNames, ';');
397 		std::vector<char> data;
398 
399 		// Load files into data
400 		for (const std::string &vApiFileName : vApiFileNames) {
401 			std::string contents = FilePath(GUI::StringFromUTF8(vApiFileName)).Read();
402 			data.insert(data.end(), contents.begin(), contents.end());
403 		}
404 
405 		// Initialise apis
406 		if (data.size() > 0) {
407 			apis.Set(data);
408 		}
409 	}
410 }
411 
FindLanguageProperty(const char * pattern,const char * defaultValue)412 std::string SciTEBase::FindLanguageProperty(const char *pattern, const char *defaultValue) {
413 	std::string key = pattern;
414 	Substitute(key, "*", language.c_str());
415 	std::string ret = props.GetExpandedString(key.c_str());
416 	if (ret == "")
417 		ret = props.GetExpandedString(pattern);
418 	if (ret == "")
419 		ret = defaultValue;
420 	return ret;
421 }
422 
423 /**
424  * A list of all the properties that should be forwarded to Scintilla lexers.
425  */
426 static const char *propertiesToForward[] = {
427 	"fold.lpeg.by.indentation",
428 	"lexer.lpeg.color.theme",
429 	"lexer.lpeg.home",
430 	"lexer.lpeg.script",
431 //++Autogenerated -- run ../scripts/RegenerateSource.py to regenerate
432 //**\(\t"\*",\n\)
433 	"asp.default.language",
434 	"fold",
435 	"fold.abl.comment.multiline",
436 	"fold.abl.syntax.based",
437 	"fold.asm.comment.explicit",
438 	"fold.asm.comment.multiline",
439 	"fold.asm.explicit.anywhere",
440 	"fold.asm.explicit.end",
441 	"fold.asm.explicit.start",
442 	"fold.asm.syntax.based",
443 	"fold.at.else",
444 	"fold.baan.inner.level",
445 	"fold.baan.keywords.based",
446 	"fold.baan.sections",
447 	"fold.baan.syntax.based",
448 	"fold.basic.comment.explicit",
449 	"fold.basic.explicit.anywhere",
450 	"fold.basic.explicit.end",
451 	"fold.basic.explicit.start",
452 	"fold.basic.syntax.based",
453 	"fold.cil.comment.multiline",
454 	"fold.coffeescript.comment",
455 	"fold.comment",
456 	"fold.comment.nimrod",
457 	"fold.comment.yaml",
458 	"fold.compact",
459 	"fold.cpp.comment.explicit",
460 	"fold.cpp.comment.multiline",
461 	"fold.cpp.explicit.anywhere",
462 	"fold.cpp.explicit.end",
463 	"fold.cpp.explicit.start",
464 	"fold.cpp.preprocessor.at.else",
465 	"fold.cpp.syntax.based",
466 	"fold.d.comment.explicit",
467 	"fold.d.comment.multiline",
468 	"fold.d.explicit.anywhere",
469 	"fold.d.explicit.end",
470 	"fold.d.explicit.start",
471 	"fold.d.syntax.based",
472 	"fold.dataflex.compilerlist",
473 	"fold.directive",
474 	"fold.haskell.imports",
475 	"fold.html",
476 	"fold.html.preprocessor",
477 	"fold.hypertext.comment",
478 	"fold.hypertext.heredoc",
479 	"fold.perl.at.else",
480 	"fold.perl.comment.explicit",
481 	"fold.perl.package",
482 	"fold.perl.pod",
483 	"fold.preprocessor",
484 	"fold.quotes.nimrod",
485 	"fold.quotes.python",
486 	"fold.raku.comment.multiline",
487 	"fold.raku.comment.pod",
488 	"fold.rust.comment.explicit",
489 	"fold.rust.comment.multiline",
490 	"fold.rust.explicit.anywhere",
491 	"fold.rust.explicit.end",
492 	"fold.rust.explicit.start",
493 	"fold.rust.syntax.based",
494 	"fold.sql.at.else",
495 	"fold.sql.only.begin",
496 	"fold.verilog.flags",
497 	"fold.xml.at.tag.open",
498 	"html.tags.case.sensitive",
499 	"lexer.as.comment.character",
500 	"lexer.asm.comment.delimiter",
501 	"lexer.baan.styling.within.preprocessor",
502 	"lexer.caml.magic",
503 	"lexer.cpp.allow.dollars",
504 	"lexer.cpp.backquoted.strings",
505 	"lexer.cpp.escape.sequence",
506 	"lexer.cpp.hashquoted.strings",
507 	"lexer.cpp.track.preprocessor",
508 	"lexer.cpp.triplequoted.strings",
509 	"lexer.cpp.update.preprocessor",
510 	"lexer.cpp.verbatim.strings.allow.escapes",
511 	"lexer.css.hss.language",
512 	"lexer.css.less.language",
513 	"lexer.css.scss.language",
514 	"lexer.d.fold.at.else",
515 	"lexer.edifact.highlight.un.all",
516 	"lexer.errorlist.escape.sequences",
517 	"lexer.errorlist.value.separate",
518 	"lexer.flagship.styling.within.preprocessor",
519 	"lexer.haskell.allow.hash",
520 	"lexer.haskell.allow.questionmark",
521 	"lexer.haskell.allow.quotes",
522 	"lexer.haskell.cpp",
523 	"lexer.haskell.import.safe",
524 	"lexer.html.django",
525 	"lexer.html.mako",
526 	"lexer.json.allow.comments",
527 	"lexer.json.escape.sequence",
528 	"lexer.metapost.comment.process",
529 	"lexer.metapost.interface.default",
530 	"lexer.nim.raw.strings.highlight.ident",
531 	"lexer.pascal.smart.highlighting",
532 	"lexer.props.allow.initial.spaces",
533 	"lexer.python.keywords2.no.sub.identifiers",
534 	"lexer.python.literals.binary",
535 	"lexer.python.strings.b",
536 	"lexer.python.strings.f",
537 	"lexer.python.strings.over.newline",
538 	"lexer.python.strings.u",
539 	"lexer.python.unicode.identifiers",
540 	"lexer.rust.fold.at.else",
541 	"lexer.sql.allow.dotted.word",
542 	"lexer.sql.backticks.identifier",
543 	"lexer.sql.numbersign.comment",
544 	"lexer.tex.auto.if",
545 	"lexer.tex.comment.process",
546 	"lexer.tex.interface.default",
547 	"lexer.tex.use.keywords",
548 	"lexer.verilog.allupperkeywords",
549 	"lexer.verilog.fold.preprocessor.else",
550 	"lexer.verilog.portstyling",
551 	"lexer.verilog.track.preprocessor",
552 	"lexer.verilog.update.preprocessor",
553 	"lexer.xml.allow.scripts",
554 	"nsis.ignorecase",
555 	"nsis.uservars",
556 	"ps.level",
557 	"sql.backslash.escapes",
558 	"styling.within.preprocessor",
559 	"tab.timmy.whinge.level",
560 
561 //--Autogenerated -- end of automatically generated section
562 
563 	nullptr,
564 };
565 
566 /* XPM */
567 static const char *bookmarkBluegem[] = {
568 /* width height num_colors chars_per_pixel */
569 "    15    15      64            1",
570 /* colors */
571 "  c none",
572 ". c #0c0630",
573 "# c #8c8a8c",
574 "a c #244a84",
575 "b c #545254",
576 "c c #cccecc",
577 "d c #949594",
578 "e c #346ab4",
579 "f c #242644",
580 "g c #3c3e3c",
581 "h c #6ca6fc",
582 "i c #143789",
583 "j c #204990",
584 "k c #5c8dec",
585 "l c #707070",
586 "m c #3c82dc",
587 "n c #345db4",
588 "o c #619df7",
589 "p c #acacac",
590 "q c #346ad4",
591 "r c #1c3264",
592 "s c #174091",
593 "t c #5482df",
594 "u c #4470c4",
595 "v c #2450a0",
596 "w c #14162c",
597 "x c #5c94f6",
598 "y c #b7b8b7",
599 "z c #646464",
600 "A c #3c68b8",
601 "B c #7cb8fc",
602 "C c #7c7a7c",
603 "D c #3462b9",
604 "E c #7c7eac",
605 "F c #44464c",
606 "G c #a4a4a4",
607 "H c #24224c",
608 "I c #282668",
609 "J c #5c5a8c",
610 "K c #7c8ebc",
611 "L c #dcd7e4",
612 "M c #141244",
613 "N c #1c2e5c",
614 "O c #24327c",
615 "P c #4472cc",
616 "Q c #6ca2fc",
617 "R c #74b2fc",
618 "S c #24367c",
619 "T c #b4b2c4",
620 "U c #403e58",
621 "V c #4c7fd6",
622 "W c #24428c",
623 "X c #747284",
624 "Y c #142e7c",
625 "Z c #64a2fc",
626 "0 c #3c72dc",
627 "1 c #bcbebc",
628 "2 c #6c6a6c",
629 "3 c #848284",
630 "4 c #2c5098",
631 "5 c #1c1a1c",
632 "6 c #243250",
633 "7 c #7cbefc",
634 "8 c #d4d2d4",
635 /* pixels */
636 "    yCbgbCy    ",
637 "   #zGGyGGz#   ",
638 "  #zXTLLLTXz#  ",
639 " p5UJEKKKEJU5p ",
640 " lfISa444aSIfl ",
641 " wIYij444jsYIw ",
642 " .OsvnAAAnvsO. ",
643 " MWvDuVVVPDvWM ",
644 " HsDPVkxxtPDsH ",
645 " UiAtxohZxtuiU ",
646 " pNnkQRBRhkDNp ",
647 " 1FrqoR7Bo0rF1 ",
648 " 8GC6aemea6CG8 ",
649 "  cG3l2z2l3Gc  ",
650 "    1GdddG1    "
651 };
652 
GetFileNameProperty(const char * name)653 std::string SciTEBase::GetFileNameProperty(const char *name) {
654 	std::string namePlusDot = name;
655 	namePlusDot.append(".");
656 	std::string valueForFileName = props.GetNewExpandString(namePlusDot.c_str(),
657 				       ExtensionFileName().c_str());
658 	if (valueForFileName.length() != 0) {
659 		return valueForFileName;
660 	} else {
661 		return props.GetString(name);
662 	}
663 }
664 
ReadProperties()665 void SciTEBase::ReadProperties() {
666 	if (extender)
667 		extender->Clear();
668 
669 	const std::string lexillaPath = props.GetExpandedString("lexilla.path");
670 	LexillaLoad(lexillaPath.empty() ? "." : lexillaPath);
671 
672 	std::vector<std::string> libraryProperties = LexillaLibraryProperties();
673 	for (std::string property : libraryProperties) {
674 		std::string key("lexilla.context.");
675 		key += property;
676 		std::string value = props.GetExpandedString(key.c_str());
677 		LexillaSetProperty(property.c_str(), value.c_str());
678 	}
679 
680 	const std::string fileNameForExtension = ExtensionFileName();
681 
682 	std::string modulePath = props.GetNewExpandString("lexerpath.",
683 				 fileNameForExtension.c_str());
684 	if (modulePath.length())
685 		wEditor.LoadLexerLibrary(modulePath.c_str());
686 	language = props.GetNewExpandString("lexer.", fileNameForExtension.c_str());
687 	if (static_cast<int>(wEditor.DocumentOptions()) & static_cast<int>(SA::DocumentOption::StylesNone)) {
688 		language = "";
689 	}
690 	if (language.length()) {
691 		if (StartsWith(language, "script_")) {
692 			wEditor.SetLexer(SCLEX_CONTAINER);
693 		} else {
694 			std::string languageCurrent = wEditor.LexerLanguage();
695 			if (language != languageCurrent) {
696 				Scintilla::ILexer5 *plexer = LexillaCreateLexer(language);
697 				if (plexer) {
698 					wEditor.SetILexer(plexer);
699 				} else {
700 					wEditor.SetLexerLanguage(language.c_str());
701 				}
702 			}
703 		}
704 	} else {
705 		wEditor.SetLexer(SCLEX_NULL);
706 	}
707 
708 	props.Set("Language", language.c_str());
709 
710 	lexLanguage = wEditor.Lexer();
711 
712 	Scintilla::ILexer5 *plexerErrorlist = LexillaCreateLexer("errorlist");
713 	if (plexerErrorlist) {
714 		wOutput.SetILexer(plexerErrorlist);
715 	} else {
716 		wOutput.SetLexerLanguage("errorlist");
717 	}
718 
719 	const std::string kw0 = props.GetNewExpandString("keywords.", fileNameForExtension.c_str());
720 	wEditor.SetKeyWords(0, kw0.c_str());
721 
722 	for (int wl = 1; wl <= SA::KeywordsetMax; wl++) {
723 		std::string kwk = StdStringFromInteger(wl+1);
724 		kwk += '.';
725 		kwk.insert(0, "keywords");
726 		const std::string kw = props.GetNewExpandString(kwk.c_str(), fileNameForExtension.c_str());
727 		wEditor.SetKeyWords(wl, kw.c_str());
728 	}
729 
730 	subStyleBases = wEditor.SubStyleBases();
731 	if (!subStyleBases.empty()) {
732 		wEditor.FreeSubStyles();
733 
734 		for (const unsigned char subStyleBase : subStyleBases) {
735 			//substyles.cpp.11=2
736 			const std::string sStyleBase = StdStringFromInteger(subStyleBase);
737 			std::string ssSubStylesKey = "substyles.";
738 			ssSubStylesKey += language;
739 			ssSubStylesKey += ".";
740 			ssSubStylesKey += sStyleBase;
741 			std::string ssNumber = props.GetNewExpandString(ssSubStylesKey.c_str());
742 			int subStyleIdentifiers = atoi(ssNumber.c_str());
743 
744 			int subStyleIdentifiersStart = 0;
745 			if (subStyleIdentifiers) {
746 				subStyleIdentifiersStart = wEditor.AllocateSubStyles(subStyleBase, subStyleIdentifiers);
747 				if (subStyleIdentifiersStart < 0)
748 					subStyleIdentifiers = 0;
749 			}
750 			for (int subStyle=0; subStyle<subStyleIdentifiers; subStyle++) {
751 				// substylewords.11.1.$(file.patterns.cpp)=CharacterSet LexAccessor SString WordList
752 				std::string ssWordsKey = "substylewords.";
753 				ssWordsKey += sStyleBase;
754 				ssWordsKey += ".";
755 				ssWordsKey += StdStringFromInteger(subStyle + 1);
756 				ssWordsKey += ".";
757 				std::string ssWords = props.GetNewExpandString(ssWordsKey.c_str(), fileNameForExtension.c_str());
758 				wEditor.SetIdentifiers(subStyleIdentifiersStart + subStyle, ssWords.c_str());
759 			}
760 		}
761 	}
762 
763 	FilePath homepath = GetSciteDefaultHome();
764 	props.Set("SciteDefaultHome", homepath.AsUTF8().c_str());
765 	homepath = GetSciteUserHome();
766 	props.Set("SciteUserHome", homepath.AsUTF8().c_str());
767 
768 	for (size_t i=0; propertiesToForward[i]; i++) {
769 		ForwardPropertyToEditor(propertiesToForward[i]);
770 	}
771 
772 	if (apisFileNames != props.GetNewExpandString("api.", fileNameForExtension.c_str())) {
773 		apis.Clear();
774 		ReadAPI(fileNameForExtension);
775 		apisFileNames = props.GetNewExpandString("api.", fileNameForExtension.c_str());
776 	}
777 
778 	props.Set("APIPath", apisFileNames.c_str());
779 
780 	FilePath fileAbbrev = GUI::StringFromUTF8(props.GetNewExpandString("abbreviations.", fileNameForExtension.c_str()));
781 	if (!fileAbbrev.IsSet())
782 		fileAbbrev = GetAbbrevPropertiesFileName();
783 	if (!pathAbbreviations.SameNameAs(fileAbbrev)) {
784 		pathAbbreviations = fileAbbrev;
785 		ReadAbbrevPropFile();
786 	}
787 
788 	props.Set("AbbrevPath", pathAbbreviations.AsUTF8().c_str());
789 
790 	const SA::Technology tech = static_cast<SA::Technology>(props.GetInt("technology"));
791 	wEditor.SetTechnology(tech);
792 	wOutput.SetTechnology(tech);
793 
794 	const SA::Bidirectional bidirectional = static_cast<SA::Bidirectional>(props.GetInt("bidirectional"));
795 	wEditor.SetBidirectional(bidirectional);
796 	wOutput.SetBidirectional(bidirectional);
797 
798 	codePage = props.GetInt("code.page");
799 	if (CurrentBuffer()->unicodeMode != uni8Bit) {
800 		// Override properties file to ensure Unicode displayed.
801 		codePage = SA::CpUtf8;
802 	}
803 	wEditor.SetCodePage(codePage);
804 	const int outputCodePage = props.GetInt("output.code.page", codePage);
805 	wOutput.SetCodePage(outputCodePage);
806 
807 	characterSet = static_cast<SA::CharacterSet>(props.GetInt("character.set", static_cast<int>(SA::CharacterSet::Default)));
808 
809 #if defined(__unix__) || defined(__APPLE__)
810 	const std::string localeCType = props.GetString("LC_CTYPE");
811 	if (localeCType.length())
812 		setlocale(LC_CTYPE, localeCType.c_str());
813 	else
814 		setlocale(LC_CTYPE, "C");
815 #endif
816 
817 	std::string imeInteraction = props.GetString("ime.interaction");
818 	if (imeInteraction.length()) {
819 		CallChildren(SA::Message::SetIMEInteraction, props.GetInt("ime.interaction", static_cast<int>(SA::IMEInteraction::Windowed)));
820 	}
821 	imeAutoComplete = props.GetInt("ime.autocomplete", 0) == 1;
822 
823 	const SA::Accessibility accessibility = static_cast<SA::Accessibility>(props.GetInt("accessibility", 1));
824 	wEditor.SetAccessibility(accessibility);
825 	wOutput.SetAccessibility(accessibility);
826 
827 	wrapStyle = static_cast<SA::Wrap>(props.GetInt("wrap.style", static_cast<int>(SA::Wrap::Word)));
828 
829 	CallChildren(SA::Message::SetCaretFore,
830 		     ColourOfProperty(props, "caret.fore", ColourRGB(0, 0, 0)));
831 
832 	CallChildren(SA::Message::SetMouseSelectionRectangularSwitch, props.GetInt("selection.rectangular.switch.mouse", 0));
833 	CallChildren(SA::Message::SetMultipleSelection, props.GetInt("selection.multiple", 1));
834 	CallChildren(SA::Message::SetAdditionalSelectionTyping, props.GetInt("selection.additional.typing", 1));
835 	CallChildren(SA::Message::SetMultiPaste, props.GetInt("selection.multipaste", 1));
836 	CallChildren(SA::Message::SetAdditionalCaretsBlink, props.GetInt("caret.additional.blinks", 1));
837 	CallChildren(SA::Message::SetVirtualSpaceOptions, props.GetInt("virtual.space"));
838 
839 	wEditor.SetMouseDwellTime(props.GetInt("dwell.period", SA::TimeForever));
840 
841 	const SA::CaretStyle caretStyle = static_cast<SA::CaretStyle>(props.GetInt("caret.style", static_cast<int>(SA::CaretStyle::Line)));
842 	wEditor.SetCaretStyle(caretStyle);
843 	wOutput.SetCaretStyle(caretStyle);
844 	wEditor.SetCaretWidth(props.GetInt("caret.width", 1));
845 	wOutput.SetCaretWidth(props.GetInt("caret.width", 1));
846 
847 	std::string caretLineBack = props.GetExpandedString("caret.line.back");
848 	if (caretLineBack.length()) {
849 		wEditor.SetCaretLineVisible(true);
850 		wEditor.SetCaretLineBack(ColourFromString(caretLineBack));
851 	} else {
852 		wEditor.SetCaretLineVisible(false);
853 	}
854 	wEditor.SetCaretLineBackAlpha(
855 		static_cast<SA::Alpha>(props.GetInt("caret.line.back.alpha", static_cast<int>(SA::Alpha::NoAlpha))));
856 
857 	int indicatorsAlpha = props.GetInt("indicators.alpha", 30);
858 	if (indicatorsAlpha < 0 || 255 < indicatorsAlpha) // If invalid value,
859 		indicatorsAlpha = 30; //then set default value.
860 	alphaIndicator = static_cast<SA::Alpha>(indicatorsAlpha);
861 	underIndicator = props.GetInt("indicators.under", 0) == 1;
862 
863 	closeFind = static_cast<CloseFind>(props.GetInt("find.close.on.find", 1));
864 
865 	const std::string controlCharSymbol = props.GetString("control.char.symbol");
866 	if (controlCharSymbol.length()) {
867 		wEditor.SetControlCharSymbol(static_cast<unsigned char>(controlCharSymbol[0]));
868 	} else {
869 		wEditor.SetControlCharSymbol(0);
870 	}
871 
872 	const std::string caretPeriod = props.GetString("caret.period");
873 	if (caretPeriod.length()) {
874 		wEditor.SetCaretPeriod(atoi(caretPeriod.c_str()));
875 		wOutput.SetCaretPeriod(atoi(caretPeriod.c_str()));
876 	}
877 
878 	const int caretZoneX = props.GetInt("caret.policy.width", 50);
879 	int caretPolicyX = 0;
880 	if (props.GetInt("caret.policy.xslop", 1))
881 		caretPolicyX |= static_cast<int>(SA::CaretPolicy::Slop);
882 	if (props.GetInt("caret.policy.xstrict"))
883 		caretPolicyX |= static_cast<int>(SA::CaretPolicy::Strict);
884 	if (props.GetInt("caret.policy.xeven", 1))
885 		caretPolicyX |= static_cast<int>(SA::CaretPolicy::Even);
886 	if (props.GetInt("caret.policy.xjumps"))
887 		caretPolicyX |= static_cast<int>(SA::CaretPolicy::Jumps);
888 	//wEditor.SetXCaretPolicy(caretStrict | caretSlop | caretEven | caretJumps);
889 	wEditor.SetXCaretPolicy(static_cast<SA::CaretPolicy>(caretPolicyX), caretZoneX);
890 
891 	const int caretZoneY = props.GetInt("caret.policy.lines");
892 	int caretPolicyY = 0;
893 	if (props.GetInt("caret.policy.yslop", 1))
894 		caretPolicyY |= static_cast<int>(SA::CaretPolicy::Slop);
895 	if (props.GetInt("caret.policy.ystrict"))
896 		caretPolicyY |= static_cast<int>(SA::CaretPolicy::Strict);
897 	if (props.GetInt("caret.policy.yeven", 1))
898 		caretPolicyY |= static_cast<int>(SA::CaretPolicy::Even);
899 	if (props.GetInt("caret.policy.yjumps"))
900 		caretPolicyY |= static_cast<int>(SA::CaretPolicy::Jumps);
901 	wEditor.SetYCaretPolicy(static_cast<SA::CaretPolicy>(caretPolicyY), caretZoneY);
902 
903 	int visiblePolicy = 0;
904 	if (props.GetInt("visible.policy.strict"))
905 		visiblePolicy |= static_cast<int>(SA::VisiblePolicy::Strict);
906 	if (props.GetInt("visible.policy.slop", 1))
907 		visiblePolicy |= static_cast<int>(SA::VisiblePolicy::Slop);
908 	const int visibleLines = props.GetInt("visible.policy.lines");
909 	wEditor.SetVisiblePolicy(static_cast<SA::VisiblePolicy>(visiblePolicy), visibleLines);
910 
911 	wEditor.SetEdgeColumn(props.GetInt("edge.column", 0));
912 	wEditor.SetEdgeMode(static_cast<SA::EdgeVisualStyle>(
913 				    props.GetInt("edge.mode", static_cast<int>(SA::EdgeVisualStyle::None))));
914 	wEditor.SetEdgeColour(
915 		ColourOfProperty(props, "edge.colour", ColourRGB(0xff, 0xda, 0xda)));
916 
917 	std::string selFore = props.GetExpandedString("selection.fore");
918 	if (selFore.length()) {
919 		CallChildren(SA::Message::SetSelFore, 1, ColourFromString(selFore));
920 	} else {
921 		CallChildren(SA::Message::SetSelFore, 0, 0);
922 	}
923 	std::string selBack = props.GetExpandedString("selection.back");
924 	if (selBack.length()) {
925 		CallChildren(SA::Message::SetSelBack, 1, ColourFromString(selBack));
926 	} else {
927 		if (selFore.length())
928 			CallChildren(SA::Message::SetSelBack, 0, 0);
929 		else	// Have to show selection somehow
930 			CallChildren(SA::Message::SetSelBack, 1, ColourRGB(0xC0, 0xC0, 0xC0));
931 	}
932 	constexpr int NoAlpha = static_cast<int>(SA::Alpha::NoAlpha);
933 	const int selectionAlpha = props.GetInt("selection.alpha", NoAlpha);
934 	CallChildren(SA::Message::SetSelAlpha, selectionAlpha);
935 
936 	std::string selAdditionalFore = props.GetString("selection.additional.fore");
937 	if (selAdditionalFore.length()) {
938 		CallChildren(SA::Message::SetAdditionalSelFore, ColourFromString(selAdditionalFore));
939 	}
940 	std::string selAdditionalBack = props.GetString("selection.additional.back");
941 	if (selAdditionalBack.length()) {
942 		CallChildren(SA::Message::SetAdditionalSelBack, ColourFromString(selAdditionalBack));
943 	}
944 	const int selectionAdditionalAlpha = (selectionAlpha == NoAlpha) ? NoAlpha : selectionAlpha / 2;
945 	CallChildren(SA::Message::SetAdditionalSelAlpha, props.GetInt("selection.additional.alpha", selectionAdditionalAlpha));
946 
947 	foldColour = props.GetExpandedString("fold.margin.colour");
948 	if (foldColour.length()) {
949 		CallChildren(SA::Message::SetFoldMarginColour, 1, ColourFromString(foldColour));
950 	} else {
951 		CallChildren(SA::Message::SetFoldMarginColour, 0, 0);
952 	}
953 	foldHiliteColour = props.GetExpandedString("fold.margin.highlight.colour");
954 	if (foldHiliteColour.length()) {
955 		CallChildren(SA::Message::SetFoldMarginHiColour, 1, ColourFromString(foldHiliteColour));
956 	} else {
957 		CallChildren(SA::Message::SetFoldMarginHiColour, 0, 0);
958 	}
959 
960 	std::string whitespaceFore = props.GetExpandedString("whitespace.fore");
961 	if (whitespaceFore.length()) {
962 		CallChildren(SA::Message::SetWhitespaceFore, 1, ColourFromString(whitespaceFore));
963 	} else {
964 		CallChildren(SA::Message::SetWhitespaceFore, 0, 0);
965 	}
966 	std::string whitespaceBack = props.GetExpandedString("whitespace.back");
967 	if (whitespaceBack.length()) {
968 		CallChildren(SA::Message::SetWhitespaceBack, 1, ColourFromString(whitespaceBack));
969 	} else {
970 		CallChildren(SA::Message::SetWhitespaceBack, 0, 0);
971 	}
972 
973 	char bracesStyleKey[200];
974 	sprintf(bracesStyleKey, "braces.%s.style", language.c_str());
975 	bracesStyle = props.GetInt(bracesStyleKey, 0);
976 
977 	char key[200] = "";
978 	std::string sval;
979 
980 	sval = FindLanguageProperty("calltip.*.ignorecase");
981 	callTipIgnoreCase = sval == "1";
982 	sval = FindLanguageProperty("calltip.*.use.escapes");
983 	callTipUseEscapes = sval == "1";
984 
985 	calltipWordCharacters = FindLanguageProperty("calltip.*.word.characters",
986 				"_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
987 	calltipParametersStart = FindLanguageProperty("calltip.*.parameters.start", "(");
988 	calltipParametersEnd = FindLanguageProperty("calltip.*.parameters.end", ")");
989 	calltipParametersSeparators = FindLanguageProperty("calltip.*.parameters.separators", ",;");
990 
991 	calltipEndDefinition = FindLanguageProperty("calltip.*.end.definition");
992 
993 	sprintf(key, "autocomplete.%s.start.characters", language.c_str());
994 	autoCompleteStartCharacters = props.GetExpandedString(key);
995 	if (autoCompleteStartCharacters == "")
996 		autoCompleteStartCharacters = props.GetExpandedString("autocomplete.*.start.characters");
997 	// "" is a quite reasonable value for this setting
998 
999 	sprintf(key, "autocomplete.%s.fillups", language.c_str());
1000 	autoCompleteFillUpCharacters = props.GetExpandedString(key);
1001 	if (autoCompleteFillUpCharacters == "")
1002 		autoCompleteFillUpCharacters =
1003 			props.GetExpandedString("autocomplete.*.fillups");
1004 	wEditor.AutoCSetFillUps(autoCompleteFillUpCharacters.c_str());
1005 
1006 	sprintf(key, "autocomplete.%s.typesep", language.c_str());
1007 	autoCompleteTypeSeparator = props.GetExpandedString(key);
1008 	if (autoCompleteTypeSeparator == "")
1009 		autoCompleteTypeSeparator =
1010 			props.GetExpandedString("autocomplete.*.typesep");
1011 	if (autoCompleteTypeSeparator.length()) {
1012 		wEditor.AutoCSetTypeSeparator(
1013 			static_cast<unsigned char>(autoCompleteTypeSeparator[0]));
1014 	}
1015 
1016 	sprintf(key, "autocomplete.%s.ignorecase", "*");
1017 	sval = props.GetNewExpandString(key);
1018 	autoCompleteIgnoreCase = sval == "1";
1019 	sprintf(key, "autocomplete.%s.ignorecase", language.c_str());
1020 	sval = props.GetNewExpandString(key);
1021 	if (sval != "")
1022 		autoCompleteIgnoreCase = sval == "1";
1023 	wEditor.AutoCSetIgnoreCase(autoCompleteIgnoreCase);
1024 	wOutput.AutoCSetIgnoreCase(true);
1025 
1026 	const int autoCChooseSingle = props.GetInt("autocomplete.choose.single");
1027 	wEditor.AutoCSetChooseSingle(autoCChooseSingle);
1028 
1029 	wEditor.AutoCSetCancelAtStart(false);
1030 	wEditor.AutoCSetDropRestOfWord(false);
1031 
1032 	if (firstPropertiesRead) {
1033 		ReadPropertiesInitial();
1034 	}
1035 
1036 	ReadFontProperties();
1037 
1038 	wEditor.SetPrintMagnification(props.GetInt("print.magnification"));
1039 	wEditor.SetPrintColourMode(static_cast<SA::PrintOption>(props.GetInt("print.colour.mode")));
1040 
1041 	jobQueue.clearBeforeExecute = props.GetInt("clear.before.execute");
1042 	jobQueue.timeCommands = props.GetInt("time.commands");
1043 
1044 	const int blankMarginLeft = props.GetInt("blank.margin.left", 1);
1045 	const int blankMarginLeftOutput = props.GetInt("output.blank.margin.left", blankMarginLeft);
1046 	const int blankMarginRight = props.GetInt("blank.margin.right", 1);
1047 	wEditor.SetMarginLeft(blankMarginLeft);
1048 	wEditor.SetMarginRight(blankMarginRight);
1049 	wOutput.SetMarginLeft(blankMarginLeftOutput);
1050 	wOutput.SetMarginRight(blankMarginRight);
1051 
1052 	marginWidth = props.GetInt("margin.width");
1053 	if (marginWidth == 0)
1054 		marginWidth = marginWidthDefault;
1055 	wEditor.SetMarginWidthN(1, margin ? marginWidth : 0);
1056 
1057 	const std::string lineMarginProp = props.GetString("line.margin.width");
1058 	lineNumbersWidth = atoi(lineMarginProp.c_str());
1059 	if (lineNumbersWidth == 0)
1060 		lineNumbersWidth = lineNumbersWidthDefault;
1061 	lineNumbersExpand = lineMarginProp.find('+') != std::string::npos;
1062 
1063 	SetLineNumberWidth();
1064 
1065 	bufferedDraw = props.GetInt("buffered.draw");
1066 	wEditor.SetBufferedDraw(bufferedDraw);
1067 	wOutput.SetBufferedDraw(bufferedDraw);
1068 
1069 	const SA::PhasesDraw phasesDraw = static_cast<SA::PhasesDraw>(
1070 			props.GetInt("phases.draw", static_cast<int>(SA::PhasesDraw::Two)));
1071 	wEditor.SetPhasesDraw(phasesDraw);
1072 	wOutput.SetPhasesDraw(phasesDraw);
1073 
1074 	wEditor.SetLayoutCache(static_cast<SA::LineCache>(
1075 				       props.GetInt("cache.layout", static_cast<int>(SA::LineCache::Caret))));
1076 	wOutput.SetLayoutCache(static_cast<SA::LineCache>(
1077 				       props.GetInt("output.cache.layout", static_cast<int>(SA::LineCache::Caret))));
1078 
1079 	bracesCheck = props.GetInt("braces.check");
1080 	bracesSloppy = props.GetInt("braces.sloppy");
1081 
1082 	wEditor.SetCharsDefault();
1083 	wordCharacters = props.GetNewExpandString("word.characters.", fileNameForExtension.c_str());
1084 	if (wordCharacters.length()) {
1085 		wEditor.SetWordChars(wordCharacters.c_str());
1086 	} else {
1087 		wordCharacters = "_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
1088 	}
1089 
1090 	whitespaceCharacters = props.GetNewExpandString("whitespace.characters.", fileNameForExtension.c_str());
1091 	if (whitespaceCharacters.length()) {
1092 		wEditor.SetWhitespaceChars(whitespaceCharacters.c_str());
1093 	}
1094 
1095 	const std::string viewIndentExamine = GetFileNameProperty("view.indentation.examine");
1096 	indentExamine = viewIndentExamine.length() ? static_cast<SA::IndentView>(atoi(viewIndentExamine.c_str())) : SA::IndentView::Real;
1097 	wEditor.SetIndentationGuides(props.GetInt("view.indentation.guides") ?
1098 				     indentExamine : SA::IndentView::None);
1099 
1100 	wEditor.SetTabIndents(props.GetInt("tab.indents", 1));
1101 	wEditor.SetBackSpaceUnIndents(props.GetInt("backspace.unindents", 1));
1102 
1103 	wEditor.CallTipUseStyle(32);
1104 
1105 	std::string useStripTrailingSpaces = props.GetNewExpandString("strip.trailing.spaces.", ExtensionFileName().c_str());
1106 	if (useStripTrailingSpaces.length() > 0) {
1107 		stripTrailingSpaces = atoi(useStripTrailingSpaces.c_str()) != 0;
1108 	} else {
1109 		stripTrailingSpaces = props.GetInt("strip.trailing.spaces") != 0;
1110 	}
1111 	ensureFinalLineEnd = props.GetInt("ensure.final.line.end") != 0;
1112 	ensureConsistentLineEnds = props.GetInt("ensure.consistent.line.ends") != 0;
1113 
1114 	indentOpening = props.GetInt("indent.opening");
1115 	indentClosing = props.GetInt("indent.closing");
1116 	indentMaintain = atoi(props.GetNewExpandString("indent.maintain.", fileNameForExtension.c_str()).c_str());
1117 
1118 	const std::string lookback = props.GetNewExpandString("statement.lookback.", fileNameForExtension.c_str());
1119 	statementLookback = atoi(lookback.c_str());
1120 	statementIndent = GetStyleAndWords("statement.indent.");
1121 	statementEnd = GetStyleAndWords("statement.end.");
1122 	blockStart = GetStyleAndWords("block.start.");
1123 	blockEnd = GetStyleAndWords("block.end.");
1124 
1125 	struct PropToPPC {
1126 		const char *propName;
1127 		PreProc ppc;
1128 	};
1129 	PropToPPC propToPPC[] = {
1130 		{"preprocessor.start.", PreProc::Start},
1131 		{"preprocessor.middle.", PreProc::Middle},
1132 		{"preprocessor.end.", PreProc::End},
1133 	};
1134 	const std::string ppSymbol = props.GetNewExpandString("preprocessor.symbol.", fileNameForExtension.c_str());
1135 	preprocessorSymbol = ppSymbol.empty() ? 0 : ppSymbol[0];
1136 	preprocOfString.clear();
1137 	for (const PropToPPC &preproc : propToPPC) {
1138 		const std::string list = props.GetNewExpandString(preproc.propName, fileNameForExtension.c_str());
1139 		const std::vector<std::string> words = StringSplit(list, ' ');
1140 		for (const std::string &word : words) {
1141 			preprocOfString[word] = preproc.ppc;
1142 		}
1143 	}
1144 
1145 	memFiles.AppendList(props.GetNewExpandString("find.files").c_str());
1146 
1147 	wEditor.SetWrapVisualFlags(static_cast<SA::WrapVisualFlag>(props.GetInt("wrap.visual.flags")));
1148 	wEditor.SetWrapVisualFlagsLocation(static_cast<SA::WrapVisualLocation>(props.GetInt("wrap.visual.flags.location")));
1149 	wEditor.SetWrapStartIndent(props.GetInt("wrap.visual.startindent"));
1150 	wEditor.SetWrapIndentMode(static_cast<SA::WrapIndentMode>(props.GetInt("wrap.indent.mode")));
1151 
1152 	idleStyling = static_cast<SA::IdleStyling>(props.GetInt("idle.styling", static_cast<int>(SA::IdleStyling::None)));
1153 	wEditor.SetIdleStyling(idleStyling);
1154 	wOutput.SetIdleStyling(static_cast<SA::IdleStyling>(props.GetInt("output.idle.styling", static_cast<int>(SA::IdleStyling::None))));
1155 
1156 	if (props.GetInt("os.x.home.end.keys")) {
1157 		AssignKey(SA::Keys::Home, SA::KeyMod::Norm, SCI_SCROLLTOSTART);
1158 		AssignKey(SA::Keys::Home, SA::KeyMod::Shift, SCI_NULL);
1159 		AssignKey(SA::Keys::Home, SA::KeyMod::Shift | SA::KeyMod::Alt, SCI_NULL);
1160 		AssignKey(SA::Keys::End, SA::KeyMod::Norm, SCI_SCROLLTOEND);
1161 		AssignKey(SA::Keys::End, SA::KeyMod::Shift, SCI_NULL);
1162 	} else {
1163 		if (props.GetInt("wrap.aware.home.end.keys", 0)) {
1164 			if (props.GetInt("vc.home.key", 1)) {
1165 				AssignKey(SA::Keys::Home, SA::KeyMod::Norm, SCI_VCHOMEWRAP);
1166 				AssignKey(SA::Keys::Home, SA::KeyMod::Shift, SCI_VCHOMEWRAPEXTEND);
1167 				AssignKey(SA::Keys::Home, SA::KeyMod::Shift | SA::KeyMod::Alt, SCI_VCHOMERECTEXTEND);
1168 			} else {
1169 				AssignKey(SA::Keys::Home, SA::KeyMod::Norm, SCI_HOMEWRAP);
1170 				AssignKey(SA::Keys::Home, SA::KeyMod::Shift, SCI_HOMEWRAPEXTEND);
1171 				AssignKey(SA::Keys::Home, SA::KeyMod::Shift | SA::KeyMod::Alt, SCI_HOMERECTEXTEND);
1172 			}
1173 			AssignKey(SA::Keys::End, SA::KeyMod::Norm, SCI_LINEENDWRAP);
1174 			AssignKey(SA::Keys::End, SA::KeyMod::Shift, SCI_LINEENDWRAPEXTEND);
1175 		} else {
1176 			if (props.GetInt("vc.home.key", 1)) {
1177 				AssignKey(SA::Keys::Home, SA::KeyMod::Norm, SCI_VCHOME);
1178 				AssignKey(SA::Keys::Home, SA::KeyMod::Shift, SCI_VCHOMEEXTEND);
1179 				AssignKey(SA::Keys::Home, SA::KeyMod::Shift | SA::KeyMod::Alt, SCI_VCHOMERECTEXTEND);
1180 			} else {
1181 				AssignKey(SA::Keys::Home, SA::KeyMod::Norm, SCI_HOME);
1182 				AssignKey(SA::Keys::Home, SA::KeyMod::Shift, SCI_HOMEEXTEND);
1183 				AssignKey(SA::Keys::Home, SA::KeyMod::Shift | SA::KeyMod::Alt, SCI_HOMERECTEXTEND);
1184 			}
1185 			AssignKey(SA::Keys::End, SA::KeyMod::Norm, SCI_LINEEND);
1186 			AssignKey(SA::Keys::End, SA::KeyMod::Shift, SCI_LINEENDEXTEND);
1187 		}
1188 	}
1189 
1190 	AssignKey(static_cast<SA::Keys>('L'), SA::KeyMod::Shift | SA::KeyMod::Ctrl, SCI_LINEDELETE);
1191 
1192 
1193 	scrollOutput = props.GetInt("output.scroll", 1);
1194 
1195 	tabHideOne = props.GetInt("tabbar.hide.one");
1196 
1197 	SetToolsMenu();
1198 
1199 	wEditor.SetFoldFlags(static_cast<SA::FoldFlag>(props.GetInt("fold.flags")));
1200 
1201 	// To put the folder markers in the line number region
1202 	//wEditor.SetMarginMaskN(0, SC_MASK_FOLDERS);
1203 
1204 	wEditor.SetModEventMask(SA::ModificationFlags::ChangeFold);
1205 
1206 	if (0==props.GetInt("undo.redo.lazy")) {
1207 		// Trap for insert/delete notifications (also fired by undo
1208 		// and redo) so that the buttons can be enabled if needed.
1209 		const SA::ModificationFlags flagsCurrent = wEditor.ModEventMask();
1210 		const SA::ModificationFlags flags =
1211 				flagsCurrent |
1212 				SA::ModificationFlags::InsertText |
1213 				SA::ModificationFlags::DeleteText |
1214 				SA::ModificationFlags::LastStepInUndoRedo;
1215 		wEditor.SetModEventMask(flags);
1216 
1217 		// LastStepInUndoRedo is probably not needed in the mask; it
1218 		// doesn't seem to fire as an event of its own; just modifies the
1219 		// insert and delete events.
1220 	}
1221 
1222 	// Create a margin column for the folding symbols
1223 	wEditor.SetMarginTypeN(2, SA::MarginType::Symbol);
1224 
1225 	foldMarginWidth = props.GetInt("fold.margin.width");
1226 	if (foldMarginWidth == 0)
1227 		foldMarginWidth = foldMarginWidthDefault;
1228 	wEditor.SetMarginWidthN(2, foldMargin ? foldMarginWidth : 0);
1229 
1230 	wEditor.SetMarginMaskN(2, SA::MaskFolders);
1231 	wEditor.SetMarginSensitiveN(2, true);
1232 
1233 	// Define foreground (outline) and background (fill) colour of folds
1234 	const int foldSymbols = props.GetInt("fold.symbols");
1235 	std::string foldFore = props.GetExpandedString("fold.fore");
1236 	if (foldFore.length() == 0) {
1237 		// Set default colour for outline
1238 		switch (foldSymbols) {
1239 		case 0: // Arrows
1240 			foldFore = "#000000";
1241 			break;
1242 		case 1: // + -
1243 			foldFore = "#FFFFFF";
1244 			break;
1245 		case 2: // Circles
1246 			foldFore = "#404040";
1247 			break;
1248 		case 3: // Squares
1249 			foldFore = "#808080";
1250 			break;
1251 		}
1252 	}
1253 	const SA::Colour colourFoldFore = ColourFromString(foldFore);
1254 
1255 	std::string foldBack = props.GetExpandedString("fold.back");
1256 	// Set default colour for fill
1257 	if (foldBack.length() == 0) {
1258 		switch (foldSymbols) {
1259 		case 0:
1260 		case 1:
1261 			foldBack = "#000000";
1262 			break;
1263 		case 2:
1264 		case 3:
1265 			foldBack = "#FFFFFF";
1266 			break;
1267 		}
1268 	}
1269 	const SA::Colour colourFoldBack = ColourFromString(foldBack);
1270 
1271 	// Enable/disable highlight for current folding block (smallest one that contains the caret)
1272 	const int isHighlightEnabled = props.GetInt("fold.highlight", 0);
1273 	// Define the colour of highlight
1274 	const SA::Colour colourFoldBlockHighlight = ColourOfProperty(props, "fold.highlight.colour", ColourRGB(0xFF, 0, 0));
1275 
1276 	switch (foldSymbols) {
1277 	case 0:
1278 		// Arrow pointing right for contracted folders, arrow pointing down for expanded
1279 		DefineMarker(SA::MarkerOutline::FolderOpen, SA::MarkerSymbol::ArrowDown,
1280 			     colourFoldFore, colourFoldBack, colourFoldBlockHighlight);
1281 		DefineMarker(SA::MarkerOutline::Folder, SA::MarkerSymbol::Arrow,
1282 			     colourFoldFore, colourFoldBack, colourFoldBlockHighlight);
1283 		DefineMarker(SA::MarkerOutline::FolderSub, SA::MarkerSymbol::Empty,
1284 			     colourFoldFore, colourFoldBack, colourFoldBlockHighlight);
1285 		DefineMarker(SA::MarkerOutline::FolderTail, SA::MarkerSymbol::Empty,
1286 			     colourFoldFore, colourFoldBack, colourFoldBlockHighlight);
1287 		DefineMarker(SA::MarkerOutline::FolderEnd, SA::MarkerSymbol::Empty,
1288 			     colourFoldFore, colourFoldBack, colourFoldBlockHighlight);
1289 		DefineMarker(SA::MarkerOutline::FolderOpenMid, SA::MarkerSymbol::Empty,
1290 			     colourFoldFore, colourFoldBack, colourFoldBlockHighlight);
1291 		DefineMarker(SA::MarkerOutline::FolderMidTail, SA::MarkerSymbol::Empty,
1292 			     colourFoldFore, colourFoldBack, colourFoldBlockHighlight);
1293 		// The highlight is disabled for arrow.
1294 		wEditor.MarkerEnableHighlight(false);
1295 		break;
1296 	case 1:
1297 		// Plus for contracted folders, minus for expanded
1298 		DefineMarker(SA::MarkerOutline::FolderOpen, SA::MarkerSymbol::Minus,
1299 			     colourFoldFore, colourFoldBack, colourFoldBlockHighlight);
1300 		DefineMarker(SA::MarkerOutline::Folder, SA::MarkerSymbol::Plus,
1301 			     colourFoldFore, colourFoldBack, colourFoldBlockHighlight);
1302 		DefineMarker(SA::MarkerOutline::FolderSub, SA::MarkerSymbol::Empty,
1303 			     colourFoldFore, colourFoldBack, colourFoldBlockHighlight);
1304 		DefineMarker(SA::MarkerOutline::FolderTail, SA::MarkerSymbol::Empty,
1305 			     colourFoldFore, colourFoldBack, colourFoldBlockHighlight);
1306 		DefineMarker(SA::MarkerOutline::FolderEnd, SA::MarkerSymbol::Empty,
1307 			     colourFoldFore, colourFoldBack, colourFoldBlockHighlight);
1308 		DefineMarker(SA::MarkerOutline::FolderOpenMid, SA::MarkerSymbol::Empty,
1309 			     colourFoldFore, colourFoldBack, colourFoldBlockHighlight);
1310 		DefineMarker(SA::MarkerOutline::FolderMidTail, SA::MarkerSymbol::Empty,
1311 			     colourFoldFore, colourFoldBack, colourFoldBlockHighlight);
1312 		// The highlight is disabled for plus/minus.
1313 		wEditor.MarkerEnableHighlight(false);
1314 		break;
1315 	case 2:
1316 		// Like a flattened tree control using circular headers and curved joins
1317 		DefineMarker(SA::MarkerOutline::FolderOpen, SA::MarkerSymbol::CircleMinus,
1318 			     colourFoldBack, colourFoldFore, colourFoldBlockHighlight);
1319 		DefineMarker(SA::MarkerOutline::Folder, SA::MarkerSymbol::CirclePlus,
1320 			     colourFoldBack, colourFoldFore, colourFoldBlockHighlight);
1321 		DefineMarker(SA::MarkerOutline::FolderSub, SA::MarkerSymbol::VLine,
1322 			     colourFoldBack, colourFoldFore, colourFoldBlockHighlight);
1323 		DefineMarker(SA::MarkerOutline::FolderTail, SA::MarkerSymbol::LCornerCurve,
1324 			     colourFoldBack, colourFoldFore, colourFoldBlockHighlight);
1325 		DefineMarker(SA::MarkerOutline::FolderEnd, SA::MarkerSymbol::CirclePlusConnected,
1326 			     colourFoldBack, colourFoldFore, colourFoldBlockHighlight);
1327 		DefineMarker(SA::MarkerOutline::FolderOpenMid, SA::MarkerSymbol::CircleMinusConnected,
1328 			     colourFoldBack, colourFoldFore, colourFoldBlockHighlight);
1329 		DefineMarker(SA::MarkerOutline::FolderMidTail, SA::MarkerSymbol::TCornerCurve,
1330 			     colourFoldBack, colourFoldFore, colourFoldBlockHighlight);
1331 		wEditor.MarkerEnableHighlight(isHighlightEnabled);
1332 		break;
1333 	case 3:
1334 		// Like a flattened tree control using square headers
1335 		DefineMarker(SA::MarkerOutline::FolderOpen, SA::MarkerSymbol::BoxMinus,
1336 			     colourFoldBack, colourFoldFore, colourFoldBlockHighlight);
1337 		DefineMarker(SA::MarkerOutline::Folder, SA::MarkerSymbol::BoxPlus,
1338 			     colourFoldBack, colourFoldFore, colourFoldBlockHighlight);
1339 		DefineMarker(SA::MarkerOutline::FolderSub, SA::MarkerSymbol::VLine,
1340 			     colourFoldBack, colourFoldFore, colourFoldBlockHighlight);
1341 		DefineMarker(SA::MarkerOutline::FolderTail, SA::MarkerSymbol::LCorner,
1342 			     colourFoldBack, colourFoldFore, colourFoldBlockHighlight);
1343 		DefineMarker(SA::MarkerOutline::FolderEnd, SA::MarkerSymbol::BoxPlusConnected,
1344 			     colourFoldBack, colourFoldFore, colourFoldBlockHighlight);
1345 		DefineMarker(SA::MarkerOutline::FolderOpenMid, SA::MarkerSymbol::BoxMinusConnected,
1346 			     colourFoldBack, colourFoldFore, colourFoldBlockHighlight);
1347 		DefineMarker(SA::MarkerOutline::FolderMidTail, SA::MarkerSymbol::TCorner,
1348 			     colourFoldBack, colourFoldFore, colourFoldBlockHighlight);
1349 		wEditor.MarkerEnableHighlight(isHighlightEnabled);
1350 		break;
1351 	}
1352 
1353 	wEditor.MarkerSetFore(markerBookmark,
1354 			      ColourOfProperty(props, "bookmark.fore", ColourRGB(0xbe, 0, 0)));
1355 	wEditor.MarkerSetBack(markerBookmark,
1356 			      ColourOfProperty(props, "bookmark.back", ColourRGB(0xe2, 0x40, 0x40)));
1357 	wEditor.MarkerSetAlpha(markerBookmark,
1358 			       static_cast<SA::Alpha>(props.GetInt("bookmark.alpha", static_cast<int>(SA::Alpha::NoAlpha))));
1359 	const std::string bookMarkXPM = props.GetString("bookmark.pixmap");
1360 	if (bookMarkXPM.length()) {
1361 		wEditor.MarkerDefinePixmap(markerBookmark, bookMarkXPM.c_str());
1362 	} else if (props.GetString("bookmark.fore").length()) {
1363 		wEditor.MarkerDefine(markerBookmark, static_cast<SA::MarkerSymbol>(
1364 					     props.GetInt("bookmark.symbol", static_cast<int>(SA::MarkerSymbol::Bookmark))));
1365 	} else {
1366 		// No bookmark.fore setting so display default pixmap.
1367 		wEditor.MarkerDefinePixmap(markerBookmark, reinterpret_cast<const char *>(bookmarkBluegem));
1368 	}
1369 
1370 	wEditor.SetScrollWidth(props.GetInt("horizontal.scroll.width", 2000));
1371 	wEditor.SetScrollWidthTracking(props.GetInt("horizontal.scroll.width.tracking", 1));
1372 	wOutput.SetScrollWidth(props.GetInt("output.horizontal.scroll.width", 2000));
1373 	wOutput.SetScrollWidthTracking(props.GetInt("output.horizontal.scroll.width.tracking", 1));
1374 
1375 	// Do these last as they force a style refresh
1376 	wEditor.SetHScrollBar(props.GetInt("horizontal.scrollbar", 1));
1377 	wOutput.SetHScrollBar(props.GetInt("output.horizontal.scrollbar", 1));
1378 
1379 	wEditor.SetEndAtLastLine(props.GetInt("end.at.last.line", 1));
1380 	wEditor.SetCaretSticky(static_cast<SA::CaretSticky>(props.GetInt("caret.sticky", 0)));
1381 
1382 	// Clear all previous indicators.
1383 	wEditor.SetIndicatorCurrent(indicatorHighlightCurrentWord);
1384 	wEditor.IndicatorClearRange(0, wEditor.Length());
1385 	wOutput.SetIndicatorCurrent(indicatorHighlightCurrentWord);
1386 	wOutput.IndicatorClearRange(0, wOutput.Length());
1387 	currentWordHighlight.statesOfDelay = currentWordHighlight.noDelay;
1388 
1389 	currentWordHighlight.isEnabled = props.GetInt("highlight.current.word", 0) == 1;
1390 	if (currentWordHighlight.isEnabled) {
1391 		const std::string highlightCurrentWordIndicatorString = props.GetExpandedString("highlight.current.word.indicator");
1392 		IndicatorDefinition highlightCurrentWordIndicator(highlightCurrentWordIndicatorString.c_str());
1393 		if (highlightCurrentWordIndicatorString.length() == 0) {
1394 			highlightCurrentWordIndicator.style = SA::IndicatorStyle::RoundBox;
1395 			std::string highlightCurrentWordColourString = props.GetExpandedString("highlight.current.word.colour");
1396 			if (highlightCurrentWordColourString.length() == 0) {
1397 				// Set default colour for highlight.
1398 				highlightCurrentWordColourString = "#A0A000";
1399 			}
1400 			highlightCurrentWordIndicator.colour = ColourFromString(highlightCurrentWordColourString);
1401 			highlightCurrentWordIndicator.fillAlpha = alphaIndicator;
1402 			highlightCurrentWordIndicator.under = underIndicator;
1403 		}
1404 		SetOneIndicator(wEditor, indicatorHighlightCurrentWord, highlightCurrentWordIndicator);
1405 		SetOneIndicator(wOutput, indicatorHighlightCurrentWord, highlightCurrentWordIndicator);
1406 		currentWordHighlight.isOnlyWithSameStyle = props.GetInt("highlight.current.word.by.style", 0) == 1;
1407 		HighlightCurrentWord(true);
1408 	}
1409 
1410 	std::map<std::string, std::string> eConfig = editorConfig->MapFromAbsolutePath(filePath);
1411 	for (const std::pair<const std::string, std::string> &pss : eConfig) {
1412 		if (pss.first == "indent_style") {
1413 			wEditor.SetUseTabs(pss.second == "tab");
1414 		} else if (pss.first == "indent_size") {
1415 			wEditor.SetIndent(std::stoi(pss.second));
1416 		} else if (pss.first == "tab_width") {
1417 			wEditor.SetTabWidth(std::stoi(pss.second));
1418 		} else if (pss.first == "end_of_line") {
1419 			if (pss.second == "lf") {
1420 				wEditor.SetEOLMode(SA::EndOfLine::Lf);
1421 			} else if (pss.second == "cr") {
1422 				wEditor.SetEOLMode(SA::EndOfLine::Cr);
1423 			} else if (pss.second == "crlf") {
1424 				wEditor.SetEOLMode(SA::EndOfLine::Lf);
1425 			}
1426 		} else if (pss.first == "charset") {
1427 			if (pss.second == "latin1") {
1428 				CurrentBuffer()->unicodeMode = uni8Bit;
1429 				codePage = 0;
1430 			} else {
1431 				if (pss.second == "utf-8")
1432 					CurrentBuffer()->unicodeMode = uniCookie;
1433 				if (pss.second == "utf-8-bom")
1434 					CurrentBuffer()->unicodeMode = uniUTF8;
1435 				if (pss.second == "utf-16be")
1436 					CurrentBuffer()->unicodeMode = uni16BE;
1437 				if (pss.second == "utf-16le")
1438 					CurrentBuffer()->unicodeMode = uni16LE;
1439 				codePage = SA::CpUtf8;
1440 			}
1441 			wEditor.SetCodePage(codePage);
1442 		} else if (pss.first == "trim_trailing_whitespace") {
1443 			stripTrailingSpaces = pss.second == "true";
1444 		} else if (pss.first == "insert_final_newline") {
1445 			ensureFinalLineEnd = pss.second == "true";
1446 		}
1447 	}
1448 
1449 	if (extender) {
1450 		FilePath defaultDir = GetDefaultDirectory();
1451 		FilePath scriptPath;
1452 
1453 		// Check for an extension script
1454 		GUI::gui_string extensionFile = GUI::StringFromUTF8(
1455 							props.GetNewExpandString("extension.", fileNameForExtension.c_str()));
1456 		if (extensionFile.length()) {
1457 			// find file in local directory
1458 			FilePath docDir = filePath.Directory();
1459 			if (Exists(docDir.AsInternal(), extensionFile.c_str(), &scriptPath)) {
1460 				// Found file in document directory
1461 				extender->Load(scriptPath.AsUTF8().c_str());
1462 			} else if (Exists(defaultDir.AsInternal(), extensionFile.c_str(), &scriptPath)) {
1463 				// Found file in global directory
1464 				extender->Load(scriptPath.AsUTF8().c_str());
1465 			} else if (Exists(GUI_TEXT(""), extensionFile.c_str(), &scriptPath)) {
1466 				// Found as completely specified file name
1467 				extender->Load(scriptPath.AsUTF8().c_str());
1468 			}
1469 		}
1470 	}
1471 
1472 	delayBeforeAutoSave = props.GetInt("save.on.timer");
1473 	if (delayBeforeAutoSave) {
1474 		TimerStart(timerAutoSave);
1475 	} else {
1476 		TimerEnd(timerAutoSave);
1477 	}
1478 
1479 	firstPropertiesRead = false;
1480 	needReadProperties = false;
1481 }
1482 
ReadFontProperties()1483 void SciTEBase::ReadFontProperties() {
1484 	char key[200] = "";
1485 	const char *languageName = language.c_str();
1486 
1487 	if (lexLanguage == lexLPeg) {
1488 		// Retrieve style info.
1489 		char propStr[256] = "";
1490 		for (int i = 0; i < StyleMax; i++) {
1491 			sprintf(key, "style.lpeg.%0d", i);
1492 			wEditor.PrivateLexerCall(i - StyleMax,
1493 						 const_cast<char *>(propStr));
1494 			props.Set(key, static_cast<const char *>(propStr));
1495 		}
1496 		languageName = "lpeg";
1497 	}
1498 
1499 	// Set styles
1500 	// For each window set the global default style, then the language default style, then the other global styles, then the other language styles
1501 
1502 	const SA::FontQuality fontQuality = static_cast<SA::FontQuality>(props.GetInt("font.quality"));
1503 	wEditor.SetFontQuality(fontQuality);
1504 	wOutput.SetFontQuality(fontQuality);
1505 
1506 	wEditor.StyleResetDefault();
1507 	wOutput.StyleResetDefault();
1508 
1509 	sprintf(key, "style.%s.%0d", "*", StyleDefault);
1510 	std::string sval = props.GetNewExpandString(key);
1511 	SetOneStyle(wEditor, StyleDefault, StyleDefinition(sval));
1512 	SetOneStyle(wOutput, StyleDefault, StyleDefinition(sval));
1513 
1514 	sprintf(key, "style.%s.%0d", languageName, StyleDefault);
1515 	sval = props.GetNewExpandString(key);
1516 	SetOneStyle(wEditor, StyleDefault, StyleDefinition(sval));
1517 
1518 	wEditor.StyleClearAll();
1519 
1520 	SetStyleFor(wEditor, "*");
1521 	SetStyleFor(wEditor, languageName);
1522 	if (props.GetInt("error.inline")) {
1523 		wEditor.ReleaseAllExtendedStyles();
1524 		diagnosticStyleStart = wEditor.AllocateExtendedStyles(diagnosticStyles);
1525 		SetStyleBlock(wEditor, "error", diagnosticStyleStart, diagnosticStyleStart+diagnosticStyles-1);
1526 	}
1527 
1528 	const int diffToSecondary = static_cast<int>(wEditor.DistanceToSecondaryStyles());
1529 	for (const unsigned char subStyleBase : subStyleBases) {
1530 		const int subStylesStart = wEditor.SubStylesStart(subStyleBase);
1531 		const int subStylesLength = wEditor.SubStylesLength(subStyleBase);
1532 		for (int subStyle=0; subStyle<subStylesLength; subStyle++) {
1533 			for (int active=0; active<(diffToSecondary?2:1); active++) {
1534 				const int activity = active * diffToSecondary;
1535 				sprintf(key, "style.%s.%0d.%0d", languageName, subStyleBase + activity, subStyle+1);
1536 				sval = props.GetNewExpandString(key);
1537 				SetOneStyle(wEditor, subStylesStart + subStyle + activity, StyleDefinition(sval));
1538 			}
1539 		}
1540 	}
1541 
1542 	// Turn grey while loading
1543 	if (CurrentBuffer()->lifeState == Buffer::reading)
1544 		wEditor.StyleSetBack(StyleDefault, 0xEEEEEE);
1545 
1546 	wOutput.StyleClearAll();
1547 
1548 	sprintf(key, "style.%s.%0d", "errorlist", StyleDefault);
1549 	sval = props.GetNewExpandString(key);
1550 	SetOneStyle(wOutput, StyleDefault, StyleDefinition(sval));
1551 
1552 	wOutput.StyleClearAll();
1553 
1554 	SetStyleFor(wOutput, "*");
1555 	SetStyleFor(wOutput, "errorlist");
1556 
1557 	if (CurrentBuffer()->useMonoFont) {
1558 		sval = props.GetExpandedString("font.monospace");
1559 		StyleDefinition sd(sval.c_str());
1560 		for (int style = 0; style <= StyleMax; style++) {
1561 			if (style != static_cast<int>(SA::StylesCommon::LineNumber)) {
1562 				if (sd.specified & StyleDefinition::sdFont) {
1563 					wEditor.StyleSetFont(style, sd.font.c_str());
1564 				}
1565 				if (sd.specified & StyleDefinition::sdSize) {
1566 					wEditor.StyleSetSizeFractional(style, sd.FractionalSize());
1567 				}
1568 			}
1569 		}
1570 	}
1571 }
1572 
1573 // Properties that are interactively modifiable are only read from the properties file once.
SetPropertiesInitial()1574 void SciTEBase::SetPropertiesInitial() {
1575 	splitVertical = props.GetInt("split.vertical");
1576 	openFilesHere = props.GetInt("check.if.already.open");
1577 	wrap = props.GetInt("wrap");
1578 	wrapOutput = props.GetInt("output.wrap");
1579 	indentationWSVisible = props.GetInt("view.indentation.whitespace", 1);
1580 	sbVisible = props.GetInt("statusbar.visible");
1581 	tbVisible = props.GetInt("toolbar.visible");
1582 	tabVisible = props.GetInt("tabbar.visible");
1583 	tabMultiLine = props.GetInt("tabbar.multiline");
1584 	lineNumbers = props.GetInt("line.margin.visible");
1585 	margin = props.GetInt("margin.width");
1586 	foldMargin = props.GetInt("fold.margin.width", foldMarginWidthDefault);
1587 
1588 	matchCase = props.GetInt("find.replace.matchcase");
1589 	regExp = props.GetInt("find.replace.regexp");
1590 	unSlash = props.GetInt("find.replace.escapes");
1591 	wrapFind = props.GetInt("find.replace.wrap", 1);
1592 	focusOnReplace = props.GetInt("find.replacewith.focus", 1);
1593 }
1594 
Text(const char * s,bool retainIfNotFound)1595 GUI::gui_string Localization::Text(const char *s, bool retainIfNotFound) {
1596 	const std::string sEllipse("...");	// An ASCII ellipse
1597 	const std::string utfEllipse("\xe2\x80\xa6");	// A UTF-8 ellipse
1598 	std::string translation = s;
1599 	const int ellipseIndicator = Remove(translation, sEllipse);
1600 	const int utfEllipseIndicator = Remove(translation, utfEllipse);
1601 	const std::string menuAccessIndicatorChar(1, static_cast<char>(menuAccessIndicator[0]));
1602 	const int accessKeyPresent = Remove(translation, menuAccessIndicatorChar);
1603 	LowerCaseAZ(translation);
1604 	Substitute(translation, "\n", "\\n");
1605 	translation = GetString(translation.c_str());
1606 	if (translation.length()) {
1607 		if (ellipseIndicator)
1608 			translation += sEllipse;
1609 		if (utfEllipseIndicator)
1610 			translation += utfEllipse;
1611 		if (0 == accessKeyPresent) {
1612 #if !defined(GTK)
1613 			// Following codes are required because accelerator is not always
1614 			// part of alphabetical word in several language. In these cases,
1615 			// accelerator is written like "(&O)".
1616 			const size_t posOpenParenAnd = translation.find("(&");
1617 			if ((posOpenParenAnd != std::string::npos) && (translation.find(")", posOpenParenAnd) == posOpenParenAnd + 3)) {
1618 				translation.erase(posOpenParenAnd, 4);
1619 			} else {
1620 				Remove(translation, std::string("&"));
1621 			}
1622 #else
1623 			Remove(translation, std::string("&"));
1624 #endif
1625 		}
1626 		Substitute(translation, "&", menuAccessIndicatorChar);
1627 		Substitute(translation, "\\n", "\n");
1628 	} else {
1629 		translation = missing;
1630 	}
1631 	if ((translation.length() > 0) || !retainIfNotFound) {
1632 		return GUI::StringFromUTF8(translation);
1633 	}
1634 	return GUI::StringFromUTF8(s);
1635 }
1636 
LocaliseMessage(const char * s,const GUI::gui_char * param0,const GUI::gui_char * param1,const GUI::gui_char * param2)1637 GUI::gui_string SciTEBase::LocaliseMessage(const char *s, const GUI::gui_char *param0, const GUI::gui_char *param1, const GUI::gui_char *param2) {
1638 	GUI::gui_string translation = localiser.Text(s);
1639 	if (param0)
1640 		Substitute(translation, GUI_TEXT("^0"), param0);
1641 	if (param1)
1642 		Substitute(translation, GUI_TEXT("^1"), param1);
1643 	if (param2)
1644 		Substitute(translation, GUI_TEXT("^2"), param2);
1645 	return translation;
1646 }
1647 
ReadLocalization()1648 void SciTEBase::ReadLocalization() {
1649 	localiser.Clear();
1650 	GUI::gui_string title = GUI_TEXT("locale.properties");
1651 	const std::string localeProps = props.GetExpandedString("locale.properties");
1652 	if (localeProps.length()) {
1653 		title = GUI::StringFromUTF8(localeProps);
1654 	}
1655 	FilePath propdir = GetSciteDefaultHome();
1656 	FilePath localePath(propdir, title);
1657 	localiser.Read(localePath, propdir, filter, &importFiles, 0);
1658 	localiser.SetMissing(props.GetString("translation.missing"));
1659 	localiser.read = true;
1660 }
1661 
ReadPropertiesInitial()1662 void SciTEBase::ReadPropertiesInitial() {
1663 	SetPropertiesInitial();
1664 	const int sizeHorizontal = props.GetInt("output.horizontal.size", 0);
1665 	const int sizeVertical = props.GetInt("output.vertical.size", 0);
1666 	const int hideOutput = props.GetInt("output.initial.hide", 0);
1667 	if ((!splitVertical && (sizeVertical > 0) && (heightOutput < sizeVertical)) ||
1668 			(splitVertical && (sizeHorizontal > 0) && (heightOutput < sizeHorizontal))) {
1669 		previousHeightOutput = splitVertical ? sizeHorizontal : sizeVertical;
1670 		if (!hideOutput) {
1671 			heightOutput = NormaliseSplit(previousHeightOutput);
1672 			SizeSubWindows();
1673 			Redraw();
1674 		}
1675 	}
1676 	ViewWhitespace(props.GetInt("view.whitespace"));
1677 	wEditor.SetIndentationGuides(props.GetInt("view.indentation.guides") ?
1678 				     indentExamine : SA::IndentView::None);
1679 
1680 	wEditor.SetViewEOL(props.GetInt("view.eol"));
1681 	wEditor.SetZoom(props.GetInt("magnification"));
1682 	wOutput.SetZoom(props.GetInt("output.magnification"));
1683 	wEditor.SetWrapMode(wrap ? wrapStyle : SA::Wrap::None);
1684 	wOutput.SetWrapMode(wrapOutput ? wrapStyle : SA::Wrap::None);
1685 
1686 	std::string menuLanguageProp = props.GetExpandedString("menu.language");
1687 	std::replace(menuLanguageProp.begin(), menuLanguageProp.end(), '|', '\0');
1688 	const char *sMenuLanguage = menuLanguageProp.c_str();
1689 	while (*sMenuLanguage) {
1690 		LanguageMenuItem lmi;
1691 		lmi.menuItem = sMenuLanguage;
1692 		sMenuLanguage += strlen(sMenuLanguage) + 1;
1693 		lmi.extension = sMenuLanguage;
1694 		sMenuLanguage += strlen(sMenuLanguage) + 1;
1695 		lmi.menuKey = sMenuLanguage;
1696 		sMenuLanguage += strlen(sMenuLanguage) + 1;
1697 		languageMenu.push_back(lmi);
1698 	}
1699 	SetLanguageMenu();
1700 
1701 	// load the user defined short cut props
1702 	std::string shortCutProp = props.GetNewExpandString("user.shortcuts");
1703 	if (shortCutProp.length()) {
1704 		const size_t pipes = std::count(shortCutProp.begin(), shortCutProp.end(), '|');
1705 		std::replace(shortCutProp.begin(), shortCutProp.end(), '|', '\0');
1706 		const char *sShortCutProp = shortCutProp.c_str();
1707 		for (size_t item = 0; item < pipes/2; item++) {
1708 			ShortcutItem sci;
1709 			sci.menuKey = sShortCutProp;
1710 			sShortCutProp += strlen(sShortCutProp) + 1;
1711 			sci.menuCommand = sShortCutProp;
1712 			sShortCutProp += strlen(sShortCutProp) + 1;
1713 			shortCutItemList.push_back(sci);
1714 		}
1715 	}
1716 	// end load the user defined short cut props
1717 
1718 	FilePath homepath = GetSciteDefaultHome();
1719 	props.Set("SciteDefaultHome", homepath.AsUTF8().c_str());
1720 	homepath = GetSciteUserHome();
1721 	props.Set("SciteUserHome", homepath.AsUTF8().c_str());
1722 }
1723 
GetDefaultPropertiesFileName()1724 FilePath SciTEBase::GetDefaultPropertiesFileName() {
1725 	return FilePath(GetSciteDefaultHome(), propGlobalFileName);
1726 }
1727 
GetAbbrevPropertiesFileName()1728 FilePath SciTEBase::GetAbbrevPropertiesFileName() {
1729 	return FilePath(GetSciteUserHome(), propAbbrevFileName);
1730 }
1731 
GetUserPropertiesFileName()1732 FilePath SciTEBase::GetUserPropertiesFileName() {
1733 	return FilePath(GetSciteUserHome(), propUserFileName);
1734 }
1735 
GetLocalPropertiesFileName()1736 FilePath SciTEBase::GetLocalPropertiesFileName() {
1737 	return FilePath(filePath.Directory(), propLocalFileName);
1738 }
1739 
GetDirectoryPropertiesFileName()1740 FilePath SciTEBase::GetDirectoryPropertiesFileName() {
1741 	FilePath propfile;
1742 
1743 	if (filePath.IsSet()) {
1744 		propfile.Set(filePath.Directory(), propDirectoryFileName);
1745 
1746 		// if this file does not exist try to find the prop file in a parent directory
1747 		while (!propfile.Directory().IsRoot() && !propfile.Exists()) {
1748 			propfile.Set(propfile.Directory().Directory(), propDirectoryFileName);
1749 		}
1750 
1751 		// not found -> set it to the initial directory
1752 		if (!propfile.Exists()) {
1753 			propfile.Set(filePath.Directory(), propDirectoryFileName);
1754 		}
1755 	}
1756 	return propfile;
1757 }
1758 
OpenProperties(int propsFile)1759 void SciTEBase::OpenProperties(int propsFile) {
1760 	FilePath propfile;
1761 	switch (propsFile) {
1762 	case IDM_OPENLOCALPROPERTIES:
1763 		propfile = GetLocalPropertiesFileName();
1764 		Open(propfile, ofQuiet);
1765 		break;
1766 	case IDM_OPENUSERPROPERTIES:
1767 		propfile = GetUserPropertiesFileName();
1768 		Open(propfile, ofQuiet);
1769 		break;
1770 	case IDM_OPENABBREVPROPERTIES:
1771 		propfile = pathAbbreviations;
1772 		Open(propfile, ofQuiet);
1773 		break;
1774 	case IDM_OPENGLOBALPROPERTIES:
1775 		propfile = GetDefaultPropertiesFileName();
1776 		Open(propfile, ofQuiet);
1777 		break;
1778 	case IDM_OPENLUAEXTERNALFILE: {
1779 			GUI::gui_string extlua = GUI::StringFromUTF8(props.GetExpandedString("ext.lua.startup.script"));
1780 			if (extlua.length()) {
1781 				Open(extlua, ofQuiet);
1782 			}
1783 			break;
1784 		}
1785 	case IDM_OPENDIRECTORYPROPERTIES: {
1786 			propfile = GetDirectoryPropertiesFileName();
1787 			const bool alreadyExists = propfile.Exists();
1788 			Open(propfile, ofQuiet);
1789 			if (!alreadyExists)
1790 				SaveAsDialog();
1791 		}
1792 		break;
1793 	}
1794 }
1795 
1796 // return the int value of the command name passed in.
GetMenuCommandAsInt(std::string commandName)1797 int SciTEBase::GetMenuCommandAsInt(std::string commandName) {
1798 	int i = IFaceTable::FindConstant(commandName.c_str());
1799 	if (i != -1) {
1800 		return IFaceTable::constants[i].value;
1801 	}
1802 
1803 	// Check also for a SCI command, as long as it has no parameters
1804 	i = IFaceTable::FindFunctionByConstantName(commandName.c_str());
1805 	if (i != -1 &&
1806 			IFaceTable::functions[i].paramType[0] == iface_void &&
1807 			IFaceTable::functions[i].paramType[1] == iface_void) {
1808 		return IFaceTable::functions[i].value;
1809 	}
1810 
1811 	// Otherwise we might have entered a number as command to access a "SCI_" command
1812 	return atoi(commandName.c_str());
1813 }
1814