1 /*
2 FXiTe - The Free eXtensIble Text Editor
3 Copyright (c) 2009-2012 Jeffrey Pohlmeyer <yetanothergeek@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify it
6 under the terms of the GNU General Public License version 3 as
7 published by the Free Software Foundation.
8
9 This software is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19
20 #include <cctype>
21 #include <cerrno>
22 #include <fx.h>
23 #include <Scintilla.h>
24
25 #include "compat.h"
26 #include "lang.h"
27 #include "search.h"
28 #include "appwin_pub.h"
29 #include "menuspec.h"
30
31
32 #include "intl.h"
33 #include "prefs.h"
34
35
36 #ifdef WIN32
37 # define DEFAULT_EOL_FORMAT SC_EOL_CRLF
38 # define STYLE_FILE "styles.ini"
39 #else
40 # define STYLE_FILE "styles"
41 # define DEFAULT_EOL_FORMAT SC_EOL_LF
42 #endif
43
44
45 FXDEFMAP(Settings) SettingsMap[]={
46 FXMAPFUNCS(SEL_COMMAND,Settings::ID_TOGGLE_SMART_HOME,Settings::ID_SET_AUTO_INDENT, Settings::onChangeSetting)
47 };
48
49 FXIMPLEMENT(Settings, FXObject, SettingsMap, ARRAYNUMBER(SettingsMap));
50
51 #define LIMIT_RANGE(n,lo,hi) if (n<=lo) { n=lo; } else { if (n>=hi) { n=hi; } }
52
53 #define SHELL_COMMAND "/bin/sh -c"
54
55 #define FILE_FILTERS _("\
56 All Files (*)|\
57 C/C++ files (*.[ch],*.[ch]pp,*.[ch]xx,*.[CH],*.cc,*.hh)|\
58 Script files (*.sh,*.lua,*.rb,*.pl,*.py)|\
59 Config files (config*,*cfg,*conf,*.ini,*rc,.*)|\
60 Makefiles ([Mm]akefile*,*.mk,CMake*,*.cmake*,Jam*)|\
61 Web files (*html,*.htm,*.php*)|\
62 Text files (*.txt)|\
63 ")
64
65 static const char* default_file_filters = FILE_FILTERS;
66
67
68 class EditorFontDlg: public FXFontDialog {
69 private:
70 FXSpinner*ascent;
71 FXSpinner*descent;
72 class EditorFontSel: public FXFontSelector {
73 public:
ActionArea()74 FXHorizontalFrame* ActionArea() {
75 return static_cast<FXHorizontalFrame*>(accept->getParent());
76 }
77 };
78 public:
EditorFontDlg(FXWindow * o)79 EditorFontDlg(FXWindow*o):FXFontDialog(o, _("Select Font"), 0) {
80 FXHorizontalFrame*frm=((EditorFontSel*)fontbox)->ActionArea();
81 (new FXLabel(frm, _("Line Spacing:\n(in pixels)")))->setPadTop(0);
82 new FXLabel(frm, _(" Above:"));
83 ascent=new FXSpinner(frm,2,NULL,0,SPIN_NORMAL|FRAME_SUNKEN|FRAME_THICK);
84 new FXLabel(frm, _(" Below:"));
85 descent=new FXSpinner(frm,2,NULL,0,SPIN_NORMAL|FRAME_SUNKEN|FRAME_THICK);
86 ascent->setRange(0,16);
87 descent->setRange(0,16);
88 }
getAscent()89 FXint getAscent() { return ascent->getValue(); }
getDescent()90 FXint getDescent() { return descent->getValue(); }
setAscent(FXint a)91 void setAscent(FXint a) { ascent->setValue(a); }
setDescent(FXint d)92 void setDescent(FXint d) { descent->setValue(d); }
93 };
94
95
96
97
onChangeSetting(FXObject * o,FXSelector sel,void * p)98 long Settings::onChangeSetting(FXObject*o, FXSelector sel, void*p)
99 {
100 switch FXSELID(sel) {
101 case ID_TOGGLE_SMART_HOME: { SmartHome = !SmartHome; break; }
102 case ID_TOGGLE_WRAP_AWARE: { WrapAwareHomeEnd = !WrapAwareHomeEnd; break; }
103 case ID_TOGGLE_USE_TABS: {
104 FXSpinner*spin=(FXSpinner*)(((FXCheckButton*)o)->getUserData());
105 UseTabs = (bool)((FXival)p);
106 if (UseTabs) {
107 spin->disable();
108 spin->getNext()->disable();
109 spin->setTextColor(spin->getApp()->getBaseColor());
110 } else {
111 spin->enable();
112 spin->getNext()->enable();
113 spin->setTextColor(spin->getApp()->getForeColor());
114 }
115 break;
116 }
117 case ID_SET_BRACE_MATCHING: {
118 BraceMatch = (FXival)p;
119 LIMIT_RANGE(BraceMatch,BRACEMATCH_NONE,BRACEMATCH_AFTER);
120 break;
121 }
122 case ID_TOGGLE_ASK_CLOSE_MULTI_MENU: { PromptCloseMultiMenu = !PromptCloseMultiMenu; break; }
123 case ID_TOGGLE_ASK_CLOSE_MULTI_EXIT: { PromptCloseMultiExit = !PromptCloseMultiExit; break; }
124 case ID_TOGGLE_WATCH_EXTERN: { WatchExternChanges = !WatchExternChanges; break; }
125 case ID_TOGGLE_SMOOTH_SCROLL: { SmoothScroll = !SmoothScroll; break; }
126 case ID_TOGGLE_SEARCH_VERBOSE: { SearchVerbose = !SearchVerbose; break; }
127 case ID_TOGGLE_CARET_PAST_EOL: { CaretPastEOL = !CaretPastEOL; break; }
128 case ID_TOGGLE_VIEW_WHITESPACE_EOL: { WhitespaceShowsEOL = !WhitespaceShowsEOL; break; }
129 case ID_TOGGLE_ASCII_DEFAULT: { DefaultToAscii = !DefaultToAscii; break; }
130 case ID_TOGGLE_SBCS_DEFAULT: { DefaultToSbcs = !DefaultToSbcs; break; }
131 case ID_TOGGLE_WORD_WRAP: { WordWrap = !WordWrap; break; }
132 case ID_TOGGLE_WRAP_TOOLBAR: {
133 WrapToolbar = !WrapToolbar;
134 break;
135 }
136 case ID_TOGGLE_AUTOSAVE: {
137 FXWindow*w=(FXWindow*)o;
138 w=(FXWindow*)w->getUserData();
139 Autosave = !Autosave;
140 if (Autosave) { w->enable(); } else { w->disable(); }
141 break;
142 }
143 case ID_SAVE_ON_FILTER_SEL: { SaveBeforeFilterSel = !SaveBeforeFilterSel; break; }
144 case ID_SAVE_ON_INS_CMD: { SaveBeforeInsCmd = !SaveBeforeInsCmd; break; }
145 case ID_SAVE_ON_EXEC_CMD: { SaveBeforeExecCmd = !SaveBeforeExecCmd; break; }
146 case ID_CHOOSE_FONT: {
147 EditorFontDlg dlg(((FXWindow*)o)->getShell());
148 dlg.setAscent(FontAscent);
149 dlg.setDescent(FontDescent);
150 #ifdef WIN32 // Windows font dialog is empty, unless setwidth is zero.
151 FXushort setwidth=fontdesc.setwidth;
152 fontdesc.setwidth=0;
153 #endif
154 SetDialogFromFont(dlg,fontdesc);
155 if (dlg.execute(PLACEMENT_SCREEN)) {
156 SetFontFromDialog(fontdesc,dlg);
157 FontName=(FXchar*)(fontdesc.face);
158 FontSize=fontdesc.size;
159 FontAscent=dlg.getAscent();
160 FontDescent=dlg.getDescent();
161 }
162 #ifdef WIN32
163 else { fontdesc.setwidth=setwidth; }
164 #endif
165 break;
166 }
167 case ID_SET_MAX_FILES: {
168 FXTextField*tf=(FXTextField*)o;
169 MaxFiles=FXIntVal(tf->getText(),10);
170 LIMIT_RANGE(MaxFiles,1,999);
171 char maxfiles[8]="\0\0\0\0\0\0\0";
172 snprintf(maxfiles, sizeof(maxfiles)-1, "%d", MaxFiles);
173 tf->setText(maxfiles);
174 break;
175 }
176 case ID_SET_TAB_WIDTH: {
177 FXSpinner*spin=(FXSpinner*)o;
178 TabWidth=spin->getValue();
179 LIMIT_RANGE(TabWidth,1,16);
180 break;
181 }
182 case ID_SET_TAB_WIDTH_FOR_LANG: {
183 FXSpinner*spin=(FXSpinner*)o;
184 LangStyle*ls=(LangStyle*)spin->getUserData();
185 ls->tabwidth=spin->getValue();
186 LIMIT_RANGE(ls->tabwidth,0,16);
187 break;
188 }
189 case ID_SET_INDENT_WIDTH: {
190 FXSpinner*spin=(FXSpinner*)o;
191 IndentWidth=spin->getValue();
192 LIMIT_RANGE(IndentWidth,1,16);
193 break;
194 }
195 case ID_SET_CARET_WIDTH: {
196 FXSpinner*spin=(FXSpinner*)o;
197 CaretWidth=spin->getValue();
198 LIMIT_RANGE(CaretWidth,1,3);
199 break;
200 }
201 case ID_SET_WHEEL_LINES: {
202 FXSpinner*spin=(FXSpinner*)o;
203 WheelLines=spin->getValue();
204 LIMIT_RANGE(WheelLines,1,32);
205 break;
206 }
207 case ID_SET_TAB_TITLE_MAX_WIDTH: {
208 FXSpinner*spin=(FXSpinner*)o;
209 TabTitleMaxWidth=spin->getValue();
210 LIMIT_RANGE(TabTitleMaxWidth,0,ScreenWidth);
211 }
212 case ID_SET_SEARCH_WRAP: {
213 SearchWrap=(FXival)p;
214 LIMIT_RANGE(SearchWrap,SEARCH_WRAP_NEVER,SEARCH_WRAP_ASK);
215 break;
216 }
217 case ID_SET_SEARCH_GUI: {
218 SearchGui=(FXival)p;
219 LIMIT_RANGE(SearchGui,SEARCH_GUI_ABOVE,SEARCH_GUI_FLOAT);
220 break;
221 }
222
223 case ID_SET_AUTO_INDENT: {
224 AutoIndent=(FXival)p;
225 LIMIT_RANGE(AutoIndent,AUTO_INDENT_NONE,AUTO_INDENT_SMART);
226 break;
227 }
228
229 case ID_SET_SPLIT_VIEW: {
230 SplitView=(FXival)p;
231 LIMIT_RANGE(SplitView,SPLIT_NONE,SPLIT_BESIDE);
232 break;
233 }
234 case ID_SET_KEEP_FILE_FILTER: {
235 KeepFileFilter=(FXival)p;
236 LIMIT_RANGE(KeepFileFilter,REMEMBER_NEVER,REMEMBER_ALWAYS);
237 break;
238 }
239 case ID_SET_RIGHT_EDGE: {
240 FXSpinner*spin=(FXSpinner*)o;
241 RightEdgeColumn=spin->getValue();
242 LIMIT_RANGE(RightEdgeColumn,1,1024);
243 break;
244 }
245 case ID_SET_SHELL_CMD: {
246 FXTextField*tf=(FXTextField*)o;
247 ShellCommand=tf->getText().text();
248 break;
249 }
250 case ID_SET_AUTOSAVE_INT: {
251 FXSpinner*spin=(FXSpinner*)o;
252 AutosaveInterval=spin->getValue();
253 LIMIT_RANGE(AutosaveInterval,15,900);
254 break;
255 }
256 case ID_SET_SEARCH_OPTS: {
257 SearchOptions=(FXival)p;
258 break;
259 }
260 case ID_SET_FILETYPES: {
261 FXInputDialog*dlg=(FXInputDialog*)o;
262 LangStyle* ls=(LangStyle*)dlg->getUserData();
263 if (ls) {
264 if (ls->mask) {
265 if (strcmp(ls->mask,dlg->getText().text())==0) { break; }
266 if ( ls->mallocs & (1<<30) ) { free(ls->mask); }
267 }
268 ls->mask=strdup(dlg->getText().text());
269 }
270 break;
271 }
272 case ID_SET_SHABANGS: {
273 FXInputDialog*dlg=(FXInputDialog*)o;
274 LangStyle* ls=(LangStyle*)dlg->getUserData();
275 if (ls) {
276 if (ls->apps) {
277 if (strcmp(ls->apps,dlg->getText().text())==0) { break; }
278 if ( ls->mallocs & (1<<29) ) { free(ls->apps); }
279 }
280 ls->apps=strdup(dlg->getText().text());
281 }
282 break;
283 }
284 case ID_SET_FILE_FORMAT: {
285 DefaultFileFormat=(FXival)p;
286 LIMIT_RANGE(DefaultFileFormat,0,2);
287 break;
288 }
289 case ID_SET_TOOLBAR_BTN_SIZE: {
290 ToolbarButtonSize=(FXival)p;
291 LIMIT_RANGE(ToolbarButtonSize,0,2);
292 break;
293 }
294 }
295 return 1;
296 }
297
298
299
300 static StyleDef GlobalStyle[] = {
301 { "default", STYLE_DEFAULT, "#000000", "#ffffff", Normal },
302 { "linenumber", STYLE_LINENUMBER, "#0000c0", "#d0d0d0", Normal },
303 { "bracelight", STYLE_BRACELIGHT, "#ffff00", "#00d000", Bold },
304 { "bracebad", STYLE_BRACEBAD, "#ffff00", "#ff0000", Bold },
305 { "controlchar", STYLE_CONTROLCHAR, "#0000c0", "#d0d0d0", Normal },
306 { "indentguide", STYLE_INDENTGUIDE, "#0000c0", "#d0d0d0", Normal },
307 { "calltip", STYLE_CALLTIP, "#808000", "#e0e0d0", Normal },
308 { NULL, 0, "", "", Normal }
309 };
310
311
312 static StyleDef WhiteSpaceStyle = { "whitespace", 0, "#000000", "#ffffdd", Normal };
313 static StyleDef CaretLineStyle = { "caretline", 0, "#000000", "#f8f8f8", Normal };
314 static StyleDef RightMarginStyle = { "rightmargin", 0, "#000000", "#ff0000", Normal };
315 static StyleDef SelectionStyle = { "selection", 0, "#c0c0c0", "#c0c0c0", Normal };
316 static StyleDef CaretStyle = { "caret", 0, "#000000", "#000000", Normal };
317
318
319
CaretFG()320 const FXchar* Settings::CaretFG() { return CaretStyle.fg; }
CaretLineBG()321 const FXchar* Settings::CaretLineBG() { return CaretLineStyle.bg; }
RightMarginBG()322 const FXchar* Settings::RightMarginBG() { return RightMarginStyle.bg; }
WhiteSpaceBG()323 const FXchar* Settings::WhiteSpaceBG() { return WhiteSpaceStyle.bg; }
WhiteSpaceFG()324 const FXchar* Settings::WhiteSpaceFG() { return WhiteSpaceStyle.fg; }
SelectionBG()325 const FXchar* Settings::SelectionBG() { return SelectionStyle.bg; }
326
327
328
329 static ErrorPattern DefaultErrPats[8] = {
330 #ifdef __minix
331 { "^\"([^\\s\"]+)\",[^(]+(\\d+)", "MINIX ACK" },
332 #endif
333 { "([^\\s:]+):(\\d+)", "GCC" },
334 { "([^\\s(]+)\\((\\d+)", "FreePascal" },
335 {"",""},
336 {"",""},
337 {"",""},
338 {"",""},
339 {"",""},
340 #ifndef __minix
341 {"",""},
342 #endif
343 };
344
345 #define MAX_ERROR_PATTERNS ARRAYNUMBER(DefaultErrPats)
346
347 static ErrorPattern UserErrorPatterns[MAX_ERROR_PATTERNS];
348
349 static const char* errpats_sect="ErrorPatterns";
350
ErrorPatterns()351 ErrorPattern* Settings::ErrorPatterns() { return UserErrorPatterns; }
352
DefaultErrorPatterns()353 ErrorPattern* Settings::DefaultErrorPatterns() { return DefaultErrPats; }
354
MaxErrorPatterns()355 int Settings::MaxErrorPatterns() { return (MAX_ERROR_PATTERNS); }
356
357
ReadErrorPatterns(FXSettings * reg)358 static void ReadErrorPatterns(FXSettings*reg)
359 {
360 char pat_key[]="Pattern_\0\0";
361 char id_key[]="Comment_\0\0";
362 memset(UserErrorPatterns,0,sizeof(ErrorPattern)*MAX_ERROR_PATTERNS);
363 if (reg->existingSection(errpats_sect)) {
364 FXRex *rx=new FXRex();
365 for (FXuint n=0;n<MAX_ERROR_PATTERNS; n++) {
366 pat_key[8] = id_key[8] = n+65;
367 FXString pat=reg->readStringEntry(errpats_sect,pat_key,"");
368 FXString id=reg->readStringEntry(errpats_sect,id_key,"");
369 if (!(id.empty()||pat.empty())) {
370 FXRexError err=rx->parse(pat,REX_CAPTURE|REX_SYNTAX);
371 if (err!=REGERR_OK) {
372 fxwarning("%s: %s\n", _("Error parsing regular expression"), FXRex::getError(err));
373 }
374 strncpy(UserErrorPatterns[n].pat, pat.text(), sizeof(UserErrorPatterns[n].pat)-1);
375 strncpy(UserErrorPatterns[n].id, id.text(), sizeof(UserErrorPatterns[n].id)-1);
376 }
377 }
378 delete rx;
379 } else {
380 memcpy(UserErrorPatterns,DefaultErrPats,sizeof(ErrorPattern)*MAX_ERROR_PATTERNS);
381 }
382 }
383
384
385
SaveErrorPatterns(FXSettings * reg)386 static void SaveErrorPatterns(FXSettings*reg)
387 {
388 char pat_key[]="Pattern_\0\0";
389 char id_key[]="Comment_\0\0";
390 reg->deleteSection(errpats_sect);
391 for (int n=0;n<=9&&UserErrorPatterns[n].pat[0]; n++) {
392 pat_key[8] = id_key[8] = n+65;
393 reg->writeStringEntry(errpats_sect,pat_key,UserErrorPatterns[n].pat);
394 reg->writeStringEntry(errpats_sect,id_key,UserErrorPatterns[n].id);
395 }
396 }
397
398
399
ErrorPatternCount()400 int Settings::ErrorPatternCount()
401 {
402 FXuint rv=0;
403 for (rv=0; rv<MAX_ERROR_PATTERNS && UserErrorPatterns[rv].pat[0]; rv++) { }
404 return rv;
405 }
406
407
408
409 static const char* sysincs_sect="SystemIncludes";
410
411 #define MAX_SYSINC_PATHS 255
412
413
414 static const FXchar* DefaultSysIncs =
415 "/usr/include\n"
416 #ifdef __minix
417 "/usr/pkg/include\n"
418 "/usr/pkg/X11R6\n"
419 #endif
420 "/usr/local/include\n"
421 ;
422
423
424 static FXString SysIncs = FXString::null;
425
426
427
defaultSystemIncludePaths()428 const FXString Settings::defaultSystemIncludePaths()
429 {
430 return DefaultSysIncs;
431 }
432
433
434
SystemIncludePaths()435 const FXString Settings::SystemIncludePaths()
436 {
437 return SysIncs;
438 }
439
440
441
SystemIncludePaths(const FXString paths)442 void Settings::SystemIncludePaths(const FXString paths)
443 {
444 SysIncs=paths;
445 }
446
447
448
SaveSysIncPaths(FXSettings * reg)449 static void SaveSysIncPaths(FXSettings*reg)
450 {
451 reg->deleteSection(sysincs_sect);
452 int n=SysIncs.contains('\n');
453 if (n>MAX_SYSINC_PATHS) { n=MAX_SYSINC_PATHS; }
454 for (int i=0; i<n; i++) {
455 const FXString value=SysIncs.section('\n',i);
456 if (!value.empty()) {
457 char key[16];
458 snprintf(key, sizeof(key)-1 ,"Path_%d", i+1);
459 reg->writeStringEntry(sysincs_sect,key,value.text());
460 }
461 }
462 }
463
464
465
ReadSysIncPaths(FXSettings * reg)466 static void ReadSysIncPaths(FXSettings*reg)
467 {
468 SysIncs=FXString::null;
469 if (reg->existingSection(sysincs_sect)) {
470 for (int i=0; i<MAX_SYSINC_PATHS; i++) {
471 char key[16];
472 snprintf(key, sizeof(key)-1 ,"Path_%d", i+1);
473 if (reg->existingEntry(sysincs_sect,key)) {
474 const char* value=reg->readStringEntry(sysincs_sect,key,NULL);
475 if (value && *value) {
476 SysIncs+=value;
477 SysIncs+='\n';
478 }
479 }
480 }
481 }
482 if (SysIncs.empty()) { SysIncs=DefaultSysIncs; }
483 }
484
485
486
IsColor(const char * clr)487 static FXbool IsColor(const char*clr)
488 {
489 const char*p;
490 if ( (clr) && (clr[0]=='#') && (strlen(clr)==7) ) {
491 for (p=clr+1; *p; p++) { if (!isxdigit(*p)) { return false; } }
492 return true;
493 } else { return false; }
494 }
495
496
497
SaveStyle(FXSettings * reg,const char * section,StyleDef * style)498 static void SaveStyle(FXSettings*reg, const char*section, StyleDef *style)
499 {
500 FXString tmp="";
501 tmp.format("%s,%s", style->fg[0]?style->fg:"default",style->bg[0]?style->bg:"default");
502 if (style->style==Normal) {
503 tmp.append(",Normal");
504 } else {
505 if (style->style & Bold) {
506 tmp.append(",Bold");
507 }
508 if (style->style & Italic) {
509 tmp.append(",Italic");
510 }
511 if (style->style & Underline) {
512 tmp.append(",Underline");
513 }
514 if (style->style & EOLFill) {
515 tmp.append(",EOLFill");
516 }
517 }
518 reg->writeStringEntry(section,style->key,tmp.text());
519 }
520
521
522
SaveStyles(FXSettings * reg,const char * section,StyleDef * style)523 static void SaveStyles(FXSettings*reg, const char*section, StyleDef *style)
524 {
525 int i;
526 for (i=0; style[i].key; i++) {
527 SaveStyle(reg,section,&style[i]);
528 }
529 }
530
531
532
ParseStyle(FXSettings * reg,const char * section,StyleDef * style)533 static void ParseStyle(FXSettings*reg, const char*section, StyleDef *style)
534 {
535 if (style->key) {
536 FXString tmp=reg->readStringEntry(section, style->key, "");
537 tmp.lower();
538 tmp.substitute('\t', ' ', true);
539 tmp.trim();
540 tmp.simplify();
541 tmp.substitute(" ", "", true);
542 if (!tmp.empty()) {
543 char buf[256];
544 char *fg=buf;
545 char *bg=NULL;
546 char *fs=NULL;
547 int flags=Normal;
548 strncpy(buf, tmp.text(), sizeof(buf)-1);
549 bg=strchr(buf,',');
550 if (bg) {
551 *bg='\0';
552 bg++;
553 fs=strchr(bg,',');
554 if (fs) {
555 *fs='\0';
556 fs++;
557 if (strstr(fs,"italic")) { flags|=Italic; }
558 if (strstr(fs,"bold")) { flags|=Bold; }
559 if (strstr(fs,"underline")) { flags|=Underline; }
560 if (strstr(fs,"eolfill")) { flags|=EOLFill; }
561 }
562 if (IsColor(bg)) { strncpy(style->bg, bg, 7); }
563 }
564 if (IsColor(fg)) { strncpy(style->fg, fg, 7); }
565 style->style=(SciDocFontStyle)(flags);
566 }
567 }
568 }
569
570
571
ParseStyles(FXSettings * reg,const char * section,StyleDef * style)572 static void ParseStyles(FXSettings*reg, const char*section, StyleDef *style)
573 {
574 int i;
575 for (i=0; style[i].key; i++) {
576 ParseStyle(reg,section,&style[i]);
577 }
578 }
579
580
581
instance()582 Settings* Settings::instance() { return (Settings*)SettingsBase::instance(); }
globalStyle()583 StyleDef* Settings::globalStyle() { return GlobalStyle; }
whitespaceStyle()584 StyleDef* Settings::whitespaceStyle() { return &WhiteSpaceStyle; }
caretlineStyle()585 StyleDef* Settings::caretlineStyle() { return &CaretLineStyle; }
rightmarginStyle()586 StyleDef* Settings::rightmarginStyle() { return &RightMarginStyle; }
selectionStyle()587 StyleDef* Settings::selectionStyle() { return &SelectionStyle; }
caretStyle()588 StyleDef* Settings::caretStyle() { return &CaretStyle; }
defaultFileFilters()589 const FXchar *Settings::defaultFileFilters() { return default_file_filters; }
590
591
592
593 // List of possible font names - we check if any system
594 // font name *begins* with any of these phrases...
595 static const char* tryfonts[] = {
596 "Liberation Mono ",
597 "DejaVu Sans Mono ",
598 "FreeMono ",
599 "Courier New ",
600 "Nimbus Mono ",
601 "Courier ",
602 "LucidaTypewriter ",
603 "Fixed ",
604 "Terminal ",
605 "Clean ",
606 "Anonymous ",
607 NULL
608 };
609
610
611 // Try to guess at a suitable font - we only do this when
612 // there is no "FontName" entry in the registry.
FindFont(FXString & FontName)613 static void FindFont(FXString &FontName) {
614 FXFontDesc*fonts=NULL;
615 FXuint numfonts=0;
616 if (FXFont::listFonts(fonts,numfonts,FXString::null)) {
617 // First, try each of the items in our "tryfonts" list...
618 for (const char**tryfont=tryfonts; *tryfont; tryfont++) {
619 for (FXuint i=0; i<numfonts; i++) {
620 FXFontDesc fd=fonts[i];
621 if (strncasecmp(fd.face, *tryfont, strlen(*tryfont))==0) {
622 FontName=fd.face;
623 freeElms(fonts);
624 return;
625 }
626 }
627 } // keep looking...
628 // Try to grab the first thing that is fixed-width and scalable.
629 for (FXuint i=0; i<numfonts; i++) {
630 FXFontDesc fd=fonts[i];
631 if ((fd.flags&FXFont::Fixed) && (fd.flags&FXFont::Scalable)) {
632 FontName=fd.face;
633 freeElms(fonts);
634 return;
635 }
636 } // keep looking...
637 // Try to grab the first thing that is fixed-width and reasonably sized.
638 for (FXuint i=0; i<numfonts; i++) {
639 FXFontDesc fd=fonts[i];
640 if ((fd.flags&FXFont::Fixed)&&(fd.size>=100)&&(fd.size<200)) {
641 FontName=fd.face;
642 freeElms(fonts);
643 return;
644 }
645 }
646 freeElms(fonts);
647 }
648 FontName="fixed"; // I give up!
649 }
650
651
652 /*
653 Break some of the preference settings up into to categories, each category represents a
654 separate section header in the config file. Note that in order for the Read/Write macros
655 to work correctly, these strings must *exactly* match their respective variable names!
656 */
657
658 static const char* view_keys[] = {
659 "ShowStatusBar",
660 "ShowLineNumbers",
661 "ShowToolbar",
662 "ShowWhiteSpace",
663 "ShowOutputPane",
664 "InvertColors",
665 "ShowRightEdge",
666 "ShowIndentGuides",
667 "DocTabsPacked",
668 "ZoomFactor",
669 "DocTabPosition",
670 "ShowCaretLine",
671 NULL
672 };
673
674 static const char* edit_keys[] = {
675 "SmartHome",
676 "WrapAwareHomeEnd",
677 "AutoIndent",
678 "BraceMatch",
679 "UseTabs",
680 "CaretPastEOL",
681 "WhitespaceShowsEOL",
682 "SearchWrap",
683 "SearchVerbose",
684 "SearchOptions",
685 "SearchGui",
686 "TabWidth",
687 "IndentWidth",
688 "CaretWidth",
689 "SmoothScroll",
690 "WheelLines",
691 "FontName",
692 "FontSize",
693 "fontdesc",
694 "FontAscent",
695 "FontDescent",
696 NULL
697 };
698
699 static const char* geom_keys[] = {
700 "Maximize",
701 "Left",
702 "Top",
703 "Width",
704 "Height",
705 "OutputPaneHeight",
706 "placement",
707 "LastFocused",
708 "TabTitleMaxWidth",
709 NULL
710 };
711
712 static const char* main_keys[] = {
713 "PromptCloseMultiMenu",
714 "PromptCloseMultiExit",
715 "WatchExternChanges",
716 "Autosave",
717 "SaveBeforeFilterSel",
718 "SaveBeforeInsCmd",
719 "SaveBeforeExecCmd",
720 "SplitView",
721 "FileFilters",
722 "RightEdgeColumn",
723 "ShellCommand",
724 "MaxFiles",
725 "AutosaveInterval",
726 "DefaultFileFormat",
727 "DefaultToAscii",
728 "DefaultToSbcs",
729 "KeepFileFilter",
730 "FileFilterIndex",
731 "FileOpenMulti",
732 "WordWrap",
733 NULL
734 };
735
736 static const char* tbar_keys[] = {
737 "WrapToolbar",
738 "ToolbarButtonSize",
739 NULL
740 };
741
742
743 static const char* global_sect="GlobalStyle";
744
745 static const char* main_sect = "Settings";
746 static const char* geom_sect = "Geometry";
747 static const char* edit_sect = "Editor";
748 static const char* view_sect = "ViewMenu";
749 static const char* tbar_sect = "Toolbar";
750 static const char* keys_sect = "Keybindings";
751 static const char* popup_sect = "PopupMenu";
752
753
754 static const char* sectnames[] = { main_sect, geom_sect, edit_sect, view_sect, tbar_sect, NULL};
755 static const char** keynames[] = { main_keys, geom_keys, edit_keys, view_keys, tbar_keys, NULL};
756
757
758
GetSectForKey(const char * key)759 static const char *GetSectForKey(const char*key)
760 {
761 for (FXint i=0; keynames[i]; i++) {
762 for (FXint j=0; keynames[i][j]; j++) {
763 if (strcmp(keynames[i][j],key)==0) { return sectnames[i]; }
764 }
765 }
766 fxerror("FATAL: Section for key \"%s\" not found.\n", key);
767 FXASSERT(!key);
768 return NULL;
769 }
770
771
772
ReadRegInt(FXRegistry * reg,const char * key,FXint def,FXint min=0,FXint max=0)773 static FXint ReadRegInt(FXRegistry *reg, const char*key, FXint def, FXint min=0, FXint max=0)
774 {
775 FXint rv=reg->readIntEntry(GetSectForKey(key),key,def);
776 if (min||max) {
777 if (rv<min) { rv=min; } else if (rv>max) { rv=max; }
778 }
779 return rv;
780 }
781
782
783 #define ReadIntRng(k,df,mn,mx) { k=ReadRegInt(reg,""#k,df,mn,mx); }
784 #define ReadInt(k,df) { k=ReadRegInt(reg,""#k,df); }
785 #define WriteInt(k) {reg->writeIntEntry(GetSectForKey(""#k),""#k,k); }
786
787
788 #define ReadStr(k,df) { k=reg->readStringEntry(GetSectForKey(""#k),""#k,df); }
789 #define WriteStr(k) {reg->writeStringEntry(GetSectForKey(""#k),""#k,k.text()); }
790
791 #ifdef FOX_1_6
LocaleIsUTF8()792 FXbool LocaleIsUTF8(){
793 # ifdef WIN32
794 return GetACP()==CP_UTF8;
795 # else
796 const FXchar* str;
797 if((str=getenv("LC_CTYPE"))!=NULL || (str=getenv("LC_ALL"))!=NULL || (str=getenv("LANG"))!=NULL){
798 return (strstr(str,"utf")!=NULL || strstr(str,"UTF")!=NULL);
799 }
800 return false;
801 # endif
802 }
803 #endif
804
805
Settings(FXMainWindow * w,const FXString & configdir)806 Settings::Settings(FXMainWindow*w, const FXString &configdir):SettingsBase()
807 {
808 FXString tmp;
809 reg=&(w->getApp()->reg());
810 style_reg=new FXSettings();
811 app=w->getApp();
812 style_file=configdir+STYLE_FILE;
813 if (use_xdg_config()) { style_file.append(".rc"); }
814
815 #ifdef FOX_1_6
816 // Fox-1.6 will choke reading string entries > 2000 chars, so rewrite
817 // the styles file, in case it was written by a newer version of Fox.
818 if (FXStat::exists(style_file)) {
819 FXFile style_fh(style_file,FXIO::Reading);
820 if (style_fh.isOpen()) {
821 FXString style_data('\0', style_fh.size()+1);
822 style_fh.readBlock((char*)style_data.text(), style_fh.size());
823 style_fh.close();
824 FXint n=style_data.contains(ENDLINE);
825 bool toolong=false;
826 for (FXint i=0; i<=n; i++) {
827 if (style_data.section(ENDLINE,i).length()>1952) {
828 toolong=true;
829 break;
830 }
831 }
832 if (toolong && style_fh.open(style_file,FXIO::Writing)) {
833 FXint eoln=strlen(ENDLINE);
834 for (FXint i=0; i<=n; i++) {
835 FXString line=style_data.section(ENDLINE,i);
836 line.trunc(1952);
837 if (strlen(line.text())) { style_fh.writeBlock(line.text(),line.length()); }
838 style_fh.writeBlock(ENDLINE,eoln);
839 }
840 style_fh.close();
841 }
842 }
843 }
844 #endif
845
846 if ( (FXStat::exists(style_file)) && (!style_reg->parseFile(style_file, true))) {
847 FXMessageBox::error(app,MBOX_OK,
848 _("Configuration error"), "%s \n%s\n%s", _("Failed to read settings from"),
849 style_file.text(), SystemErrorStr()
850 );
851 }
852 ReadInt(ShowStatusBar,true);
853 ReadInt(ShowLineNumbers,false);
854 ReadInt(ShowToolbar,true);
855 ReadInt(ShowWhiteSpace,false);
856 ReadInt(ShowOutputPane,false);
857 ReadInt(InvertColors,false);
858 ReadIntRng(SplitView,0,SPLIT_NONE,SPLIT_BESIDE);
859 ReadInt(OutputPaneHeight,64);
860 ReadInt(SmartHome,false);
861 ReadInt(WrapAwareHomeEnd,false);
862 ReadIntRng(BraceMatch,BRACEMATCH_EITHER,BRACEMATCH_NONE,BRACEMATCH_AFTER);
863 ReadInt(UseTabs,true);
864 ReadInt(CaretPastEOL,false);
865 ReadIntRng(TabWidth,4,1,16);
866 ReadIntRng(IndentWidth,TabWidth,1,16);
867 ReadIntRng(CaretWidth,1,1,3);
868 ReadIntRng(RightEdgeColumn,80,1,1024);
869 ReadInt(ShowRightEdge,false);
870 ReadInt(ShowIndentGuides,false);
871 ReadInt(ShowCaretLine,true);
872 ReadInt(SmoothScroll,true);
873 ReadIntRng(WheelLines,3,1,32);
874 ReadIntRng(SearchWrap,SEARCH_WRAP_ASK,SEARCH_WRAP_NEVER,SEARCH_WRAP_ASK);
875 ReadIntRng(SearchGui,SEARCH_GUI_BELOW,SEARCH_GUI_ABOVE,SEARCH_GUI_FLOAT);
876 ReadIntRng(AutoIndent,AUTO_INDENT_NONE,AUTO_INDENT_NONE,AUTO_INDENT_SMART);
877 ReadInt(SearchVerbose,true);
878 ReadInt(SearchOptions,0);
879 ReadInt(ZoomFactor,0);
880 ReadIntRng(MaxFiles,128,1,999);
881 ReadInt(PromptCloseMultiMenu,true);
882 ReadInt(PromptCloseMultiExit,true);
883 ReadInt(WatchExternChanges,true);
884 ReadInt(Autosave,false);
885 ReadIntRng(AutosaveInterval,60,15,900);
886 ReadInt(SaveBeforeFilterSel,false);
887 ReadInt(SaveBeforeInsCmd,false);
888 ReadInt(SaveBeforeExecCmd,true);
889 ReadInt(WhitespaceShowsEOL,true);
890 ReadIntRng(DefaultFileFormat,DEFAULT_EOL_FORMAT,0,2);
891 ReadInt(DefaultToAscii,!LocaleIsUTF8());
892 ReadInt(DefaultToSbcs,true);
893 ReadIntRng(KeepFileFilter,REMEMBER_NEVER,REMEMBER_NEVER,REMEMBER_ALWAYS);
894 if (KeepFileFilter==REMEMBER_ALWAYS) {
895 ReadInt(FileFilterIndex,0);
896 } else {
897 FileFilterIndex=0;
898 }
899 ReadInt(FileOpenMulti,false);
900 ReadInt(WordWrap,false);
901 ReadInt(WrapToolbar,true);
902 ReadIntRng(ToolbarButtonSize,1,0,2);// 0=small; 1=medium; 2=large
903
904 placement=reg->existingEntry(geom_sect,"Top")?PLACEMENT_DEFAULT:PLACEMENT_SCREEN;
905 ReadInt(Left,0);
906 ReadInt(Top,0);
907 ReadInt(Width,0);
908 ReadInt(Height,0);
909
910 ScreenWidth=w->getApp()->getRootWindow()->getDefaultWidth();
911 ScreenHeight=w->getApp()->getRootWindow()->getDefaultHeight();
912 if ((Width==0)&&(Height==0)) { // First run, size based on screen dimensions
913 Width=ScreenWidth*(4.0/5.0);
914 Height=ScreenHeight*(3.0/4.0);
915 LIMIT_RANGE(Width,600,Width);
916 LIMIT_RANGE(Height,400,Height);
917 }
918
919 LIMIT_RANGE(Left,0,Left);
920 LIMIT_RANGE(Top,0,Top);
921 LIMIT_RANGE(Width,160,Width);
922 LIMIT_RANGE(Height,120,Height);
923 ReadInt(Maximize,false);
924 ReadIntRng(TabTitleMaxWidth,ScreenWidth/6,0,ScreenWidth);
925 LastFocused=reg->readStringEntry("LastFocused",FXPath::title(TopWinPub::Connector()).text(),"");
926 ReadInt(FontSize,120);
927 ReadIntRng(FontAscent,2,0,16);
928 ReadIntRng(FontDescent,0,0,16);
929
930 if (reg->existingEntry(edit_sect,"FontName")) {
931 ReadStr(FontName,"Fixed [Misc]");
932 } else {
933 FindFont(FontName);
934 }
935
936
937 tmp=reg->readStringEntry(view_sect,"DocTabPosition","T");
938 if (tmp.empty() || !strchr("TBLR",tmp.text()[0])) {
939 DocTabPosition='T';
940 } else {
941 DocTabPosition=tmp.text()[0];
942 }
943
944 tmp=reg->readStringEntry(view_sect,"DocTabsPacked","A");
945 if (tmp=="0") { tmp="U"; } else if (tmp=="1") { tmp="P"; }
946 if (tmp.empty() || !strchr("UPA",tmp.text()[0])) {
947 DocTabsPacked='A';
948 } else {
949 DocTabsPacked=tmp.text()[0];
950 }
951
952 ReadStr(FileFilters, default_file_filters);
953 FileFilters.substitute("|", "\n", true);
954 ReadStr(ShellCommand,SHELL_COMMAND);
955
956 ParseStyles(style_reg, global_sect, GlobalStyle);
957 ParseStyle(style_reg, global_sect, &WhiteSpaceStyle);
958 ParseStyle(style_reg, global_sect, &CaretLineStyle);
959 ParseStyle(style_reg, global_sect, &RightMarginStyle);
960 for (LangStyle*ls=languages; ls->name; ls++) {
961 if (ls->words) {
962 int i;
963 for (i=0; ls->words[i]; i++) {
964 char buf[256];
965 snprintf(buf,sizeof(buf)-1, "words_%d", i+1);
966 tmp=style_reg->readStringEntry(ls->name,buf,ls->words[i]);
967 SetKeywordList(ls,i,tmp);
968 }
969 }
970 ParseStyles(style_reg, ls->name, ls->styles);
971 tmp=style_reg->readStringEntry(ls->name,"FileMask",ls->mask?ls->mask:"");
972 if ( (ls->mask && (strcmp(ls->mask,tmp.text())!=0))||((ls->mask==NULL)&&(!tmp.empty())) ) {
973 ls->mask=strdup(tmp.text());
974 ls->mallocs |= LANGSTYLE_MASK_ALLOCD;
975 }
976 tmp=style_reg->readStringEntry(ls->name,"ShebangApps",ls->apps?ls->apps:"");
977 if ( (ls->apps && (strcmp(ls->apps,tmp.text())!=0))||((ls->apps==NULL)&&(!tmp.empty())) ){
978 ls->apps=strdup(tmp.text());
979 ls->mallocs |= LANGSTYLE_APPS_ALLOCD;
980 }
981 ls->tabs=(TabPolicy)(style_reg->readIntEntry(ls->name,"TabPolicy", ls->tabs));
982 ls->tabwidth=style_reg->readIntEntry(ls->name,"TabWidth", 0);
983 LIMIT_RANGE(ls->tabwidth,0,16);
984 }
985 styles=GlobalStyle;
986 MenuMgr::ReadMenuSpecs(reg,keys_sect);
987 MenuMgr::ReadToolbarButtons(reg,tbar_sect);
988 MenuMgr::ReadPopupMenu(reg,popup_sect);
989 ReadErrorPatterns(reg);
990 ReadSysIncPaths(reg);
991 }
992
993
994
995 extern "C" { void ini_sort(const char *filename); }
996
997
~Settings()998 Settings::~Settings()
999 {
1000 LangStyle*ls;
1001 for (ls=languages; ls->name; ls++) {
1002 int i;
1003 SaveStyles(style_reg, ls->name, ls->styles);
1004 style_reg->writeStringEntry(ls->name,"FileMask",ls->mask?ls->mask:"");
1005 style_reg->writeStringEntry(ls->name,"ShebangApps",ls->apps?ls->apps:"");
1006 style_reg->writeIntEntry(ls->name,"TabPolicy", ls->tabs);
1007 style_reg->writeIntEntry(ls->name,"TabWidth", ls->tabwidth);
1008 for (i=0; ls->words[i]; i++) {
1009 char key[32];
1010 snprintf(key, sizeof(key)-1, "words_%d", i+1);
1011 #ifdef FOX_1_6
1012 // fox 1.6 will choke reading string entries > 2000 chars
1013 char buf[1952];
1014 memset(buf,0,sizeof(buf));
1015 strncpy(buf, ls->words[i], sizeof(buf)-1);
1016 style_reg->writeStringEntry(ls->name,key,buf);
1017 #else
1018 style_reg->writeStringEntry(ls->name,key,ls->words[i]);
1019 #endif
1020 }
1021 }
1022 SaveStyles(style_reg, global_sect, GlobalStyle);
1023 SaveStyle(style_reg, global_sect,&WhiteSpaceStyle);
1024 SaveStyle(style_reg, global_sect,&CaretLineStyle);
1025 SaveStyle(style_reg, global_sect,&RightMarginStyle);
1026
1027 reg->deleteSection(keys_sect);
1028 reg->deleteSection(tbar_sect);
1029
1030 WriteInt(ShowStatusBar);
1031 WriteInt(ShowWhiteSpace);
1032 WriteInt(ShowOutputPane);
1033 WriteInt(InvertColors);
1034 WriteInt(SplitView);
1035 WriteInt(OutputPaneHeight);
1036 WriteInt(ShowLineNumbers);
1037 WriteInt(ShowToolbar);
1038 WriteInt(SmartHome);
1039 WriteInt(WrapAwareHomeEnd);
1040 WriteInt(AutoIndent);
1041 WriteInt(BraceMatch);
1042 WriteInt(UseTabs);
1043 WriteInt(CaretPastEOL);
1044 WriteInt(TabWidth);
1045 WriteInt(IndentWidth);
1046 WriteInt(CaretWidth);
1047 WriteInt(RightEdgeColumn);
1048 WriteInt(ShowRightEdge);
1049 WriteInt(ShowIndentGuides);
1050 WriteInt(ShowCaretLine);
1051 WriteInt(SmoothScroll);
1052 WriteInt(WheelLines);
1053 WriteInt(SearchWrap);
1054 WriteInt(SearchGui);
1055 WriteInt(SearchVerbose);
1056 WriteInt(SearchOptions);
1057 WriteInt(ZoomFactor);
1058 WriteInt(MaxFiles);
1059 WriteInt(PromptCloseMultiMenu);
1060 WriteInt(PromptCloseMultiExit);
1061 WriteInt(WatchExternChanges);
1062 WriteInt(Autosave);
1063 WriteInt(AutosaveInterval);
1064 WriteInt(SaveBeforeFilterSel);
1065 WriteInt(SaveBeforeInsCmd);
1066 WriteInt(SaveBeforeExecCmd);
1067 WriteInt(WhitespaceShowsEOL);
1068 WriteInt(DefaultFileFormat);
1069 WriteInt(WrapToolbar);
1070 WriteInt(WordWrap);
1071 WriteInt(ToolbarButtonSize);
1072 WriteInt(Left);
1073 WriteInt(Top);
1074 WriteInt(Width);
1075 WriteInt(Height);
1076 WriteInt(Maximize);
1077 reg->writeStringEntry("LastFocused",FXPath::title(TopWinPub::Connector()).text(),LastFocused.text());
1078 WriteInt(FontSize);
1079 WriteInt(FontAscent);
1080 WriteInt(FontDescent);
1081 WriteInt(DefaultToAscii);
1082 WriteInt(DefaultToSbcs);
1083 WriteInt(KeepFileFilter);
1084 WriteInt(FileFilterIndex);
1085 WriteInt(FileOpenMulti);
1086 WriteInt(TabTitleMaxWidth);
1087
1088 if (!(DocTabPosition && strchr("TBLR",DocTabPosition))) { DocTabPosition='T'; }
1089 char dtp[2]={DocTabPosition,'\0'};
1090 reg->writeStringEntry(view_sect,"DocTabPosition",dtp);
1091
1092 if (!(DocTabsPacked && strchr("UPA",DocTabsPacked))) { DocTabsPacked='T'; }
1093 dtp[0]=DocTabsPacked;
1094 reg->writeStringEntry(view_sect,"DocTabsPacked",dtp);
1095
1096 WriteStr(FontName);
1097 WriteStr(FileFilters);
1098 WriteStr(ShellCommand);
1099
1100 FreeAllKeywordLists();
1101
1102 MenuMgr::WriteMenuSpecs(reg,keys_sect);
1103 MenuMgr::WriteToolbarButtons(reg,tbar_sect);
1104 MenuMgr::WritePopupMenu(reg,popup_sect);
1105 SaveErrorPatterns(reg);
1106 SaveSysIncPaths(reg);
1107
1108 if (!style_reg->unparseFile(style_file)) {
1109 FXMessageBox::error(app,MBOX_OK,
1110 _("Configuration error"), "%s \n%s\n%s", _("Failed to save settings to"),
1111 style_file.text(), SystemErrorStr()
1112 );
1113 }
1114 delete style_reg;
1115 ini_sort(style_file.text());
1116 }
1117
1118