Cleanup statusbar template code a bit
[geany-mirror.git] / src / highlighting.c
blobee823a4760a27cd22fc1a2e82390ba3c8bd29356
1 /*
2 * highlighting.c - this file is part of Geany, a fast and lightweight IDE
4 * Copyright 2005-2012 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
5 * Copyright 2006-2012 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
6 * Copyright 2011-2012 Colomban Wendling <ban(at)herbesfolles(dot)org>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 /**
24 * @file highlighting.h
25 * Syntax highlighting for the different filetypes, using the Scintilla lexers.
28 #include "geany.h"
30 #include <stdlib.h>
31 #include <ctype.h>
32 #include <string.h>
34 #include "SciLexer.h"
35 #include "highlighting.h"
36 #include "editor.h"
37 #include "utils.h"
38 #include "filetypes.h"
39 #include "symbols.h"
40 #include "ui_utils.h"
41 #include "utils.h"
42 #include "main.h"
43 #include "support.h"
44 #include "sciwrappers.h"
45 #include "document.h"
46 #include "dialogs.h"
47 #include "filetypesprivate.h"
49 #include "highlightingmappings.h"
52 #define GEANY_COLORSCHEMES_SUBDIR "colorschemes"
54 /* Whitespace has to be set after setting wordchars. */
55 #define GEANY_WHITESPACE_CHARS " \t" "!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~"
58 static gchar *whitespace_chars;
61 typedef struct
63 gsize count; /* number of styles */
64 GeanyLexerStyle *styling; /* array of styles, NULL if not used or uninitialised */
65 gchar **keywords;
66 gchar *wordchars; /* NULL used for style sets with no styles */
67 gchar **property_keys;
68 gchar **property_values;
69 } StyleSet;
71 /* each filetype has a styleset but GEANY_FILETYPES_NONE uses common_style_set for styling */
72 static StyleSet *style_sets = NULL;
75 enum /* Geany common styling */
77 GCS_DEFAULT,
78 GCS_SELECTION,
79 GCS_BRACE_GOOD,
80 GCS_BRACE_BAD,
81 GCS_MARGIN_LINENUMBER,
82 GCS_MARGIN_FOLDING,
83 GCS_FOLD_SYMBOL_HIGHLIGHT,
84 GCS_CURRENT_LINE,
85 GCS_CARET,
86 GCS_INDENT_GUIDE,
87 GCS_WHITE_SPACE,
88 GCS_LINE_WRAP_VISUALS,
89 GCS_LINE_WRAP_INDENT,
90 GCS_TRANSLUCENCY,
91 GCS_MARKER_LINE,
92 GCS_MARKER_SEARCH,
93 GCS_MARKER_MARK,
94 GCS_MARKER_TRANSLUCENCY,
95 GCS_LINE_HEIGHT,
96 GCS_CALLTIPS,
97 GCS_MAX
100 static struct
102 GeanyLexerStyle styling[GCS_MAX];
104 /* icon style, 1-4 */
105 gint fold_marker;
106 /* vertical line style, 0-2 */
107 gint fold_lines;
108 /* horizontal line when folded, 0-2 */
109 gint fold_draw_line;
111 gchar *wordchars;
112 } common_style_set;
115 /* For filetypes.common [named_styles] section.
116 * 0xBBGGRR format.
117 * e.g. "comment" => &GeanyLexerStyle{0x0000d0, 0xffffff, FALSE, FALSE} */
118 static GHashTable *named_style_hash = NULL;
120 /* 0xBBGGRR format, set by "default" named style. */
121 static GeanyLexerStyle gsd_default = {0x000000, 0xffffff, FALSE, FALSE};
124 /* Note: use sciwrappers.h instead where possible.
125 * Do not use SSM in files unrelated to scintilla. */
126 #define SSM(s, m, w, l) scintilla_send_message(s, m, w, l)
128 /* filetypes should use the filetypes.foo [lexer_properties] group instead of hardcoding */
129 static void sci_set_property(ScintillaObject *sci, const gchar *name, const gchar *value)
131 SSM(sci, SCI_SETPROPERTY, (uptr_t) name, (sptr_t) value);
135 static void new_styleset(guint file_type_id, gsize styling_count)
137 StyleSet *set = &style_sets[file_type_id];
139 set->count = styling_count;
140 set->styling = g_new0(GeanyLexerStyle, styling_count);
144 static void free_styleset(guint file_type_id)
146 StyleSet *style_ptr;
147 style_ptr = &style_sets[file_type_id];
149 style_ptr->count = 0;
150 g_free(style_ptr->styling);
151 style_ptr->styling = NULL;
152 g_strfreev(style_ptr->keywords);
153 style_ptr->keywords = NULL;
154 g_free(style_ptr->wordchars);
155 style_ptr->wordchars = NULL;
156 g_strfreev(style_ptr->property_keys);
157 style_ptr->property_keys = NULL;
158 g_strfreev(style_ptr->property_values);
159 style_ptr->property_values = NULL;
163 static void get_keyfile_keywords(GKeyFile *config, GKeyFile *configh,
164 const gchar *key, guint ft_id, guint pos)
166 style_sets[ft_id].keywords[pos] =
167 utils_get_setting(string, configh, config, "keywords", key, "");
171 static void get_keyfile_wordchars(GKeyFile *config, GKeyFile *configh, gchar **wordchars)
173 *wordchars = utils_get_setting(string, configh, config,
174 "settings", "wordchars", GEANY_WORDCHARS);
178 static gboolean read_named_style(const gchar *named_style, GeanyLexerStyle *style)
180 GeanyLexerStyle *cs;
181 gchar *comma, *name = NULL;
182 const gchar *bold = NULL;
183 const gchar *italic = NULL;
185 g_return_val_if_fail(named_style, FALSE);
186 name = utils_strdupa(named_style); /* named_style must not be written to, may be a static string */
188 comma = strstr(name, ",");
189 if (comma)
191 bold = strstr(comma, ",bold");
192 italic = strstr(comma, ",italic");
193 *comma = '\0'; /* terminate name to make lookup work */
195 cs = g_hash_table_lookup(named_style_hash, name);
197 if (cs)
199 *style = *cs;
200 if (bold)
201 style->bold = !style->bold;
202 if (italic)
203 style->italic = !style->italic;
205 else
207 *style = gsd_default;
209 return (cs != NULL);
213 /* Parses a color in `str` which can be an HTML color (ex. #0099cc),
214 * an abbreviated HTML color (ex. #09c) or a hex string color
215 * (ex. 0x0099cc). The result of the conversion is stored into the
216 * location pointed to by `clr`. */
217 static void parse_color(GKeyFile *kf, const gchar *str, gint *clr)
219 gint c;
220 gchar hex_clr[9] = { 0 };
221 gchar *named_color = NULL;
222 const gchar *start;
224 g_return_if_fail(clr != NULL);
226 if (G_UNLIKELY(! NZV(str)))
227 return;
229 named_color = g_key_file_get_string(kf, "named_colors", str, NULL);
230 if (named_color)
231 str = named_color;
233 if (str[0] == '#')
234 start = str + 1;
235 else if (strlen(str) > 1 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))
236 start = str + 2;
237 else
239 geany_debug("Bad color '%s'", str);
240 g_free(named_color);
241 return;
244 if (strlen(start) == 3)
246 snprintf(hex_clr, 9, "0x%c%c%c%c%c%c", start[0], start[0],
247 start[1], start[1], start[2], start[2]);
249 else
250 snprintf(hex_clr, 9, "0x%s", start);
252 g_free(named_color);
254 c = utils_strtod(hex_clr, NULL, FALSE);
256 if (c > -1)
258 *clr = c;
259 return;
261 geany_debug("Bad color '%s'", str);
265 static void parse_keyfile_style(GKeyFile *kf, gchar **list,
266 const GeanyLexerStyle *default_style, GeanyLexerStyle *style)
268 gsize len;
270 g_return_if_fail(default_style);
271 g_return_if_fail(style);
273 *style = *default_style;
275 if (!list)
276 return;
278 len = g_strv_length(list);
279 if (len == 0)
280 return;
281 else if (len == 1)
283 gchar **items = g_strsplit(list[0], ",", 0);
284 if (items != NULL)
286 if (g_strv_length(items) > 0)
288 if (g_hash_table_lookup(named_style_hash, items[0]) != NULL)
290 if (!read_named_style(list[0], style))
291 geany_debug("Unable to read named style '%s'", items[0]);
292 g_strfreev(items);
293 return;
295 else if (strchr(list[0], ',') != NULL)
297 geany_debug("Unknown named style '%s'", items[0]);
298 g_strfreev(items);
299 return;
302 g_strfreev(items);
306 switch (len)
308 case 4:
309 style->italic = utils_atob(list[3]);
310 case 3:
311 style->bold = utils_atob(list[2]);
312 case 2:
313 parse_color(kf, list[1], &style->background);
314 case 1:
315 parse_color(kf, list[0], &style->foreground);
320 static void get_keyfile_style(GKeyFile *config, GKeyFile *configh,
321 const gchar *key_name, GeanyLexerStyle *style)
323 gchar **list;
324 gsize len;
326 g_return_if_fail(config);
327 g_return_if_fail(configh);
328 g_return_if_fail(key_name);
329 g_return_if_fail(style);
331 list = g_key_file_get_string_list(configh, "styling", key_name, &len, NULL);
332 if (list == NULL)
334 list = g_key_file_get_string_list(config, "styling", key_name, &len, NULL);
335 parse_keyfile_style(config, list, &gsd_default, style);
337 else
338 parse_keyfile_style(configh, list, &gsd_default, style);
340 g_strfreev(list);
344 /* Convert 0xRRGGBB to 0xBBGGRR, which scintilla expects. */
345 static gint rotate_rgb(gint color)
347 return ((color & 0xFF0000) >> 16) +
348 (color & 0x00FF00) +
349 ((color & 0x0000FF) << 16);
353 static void convert_int(const gchar *int_str, gint *val)
355 gchar *end;
356 gint v = strtol(int_str, &end, 10);
358 if (int_str != end)
359 *val = v;
363 /* Get first and second integer numbers, store in foreground and background fields of @a style. */
364 static void get_keyfile_int(GKeyFile *config, GKeyFile *configh, const gchar *section,
365 const gchar *key, gint fdefault_val, gint sdefault_val,
366 GeanyLexerStyle *style)
368 gchar **list;
369 gsize len;
370 GeanyLexerStyle def = {fdefault_val, sdefault_val, FALSE, FALSE};
372 g_return_if_fail(config);
373 g_return_if_fail(configh);
374 g_return_if_fail(section);
375 g_return_if_fail(key);
377 list = g_key_file_get_string_list(configh, section, key, &len, NULL);
378 if (list == NULL)
379 list = g_key_file_get_string_list(config, section, key, &len, NULL);
381 *style = def;
382 if (!list)
383 return;
385 if (list[0])
387 convert_int(list[0], &style->foreground);
388 if (list[1])
390 convert_int(list[1], &style->background);
393 g_strfreev(list);
397 /* first or second can be NULL. */
398 static void get_keyfile_ints(GKeyFile *config, GKeyFile *configh, const gchar *section,
399 const gchar *key,
400 gint fdefault_val, gint sdefault_val,
401 gint *first, gint *second)
403 GeanyLexerStyle tmp_style;
405 get_keyfile_int(config, configh, section, key, fdefault_val, sdefault_val, &tmp_style);
406 if (first)
407 *first = tmp_style.foreground;
408 if (second)
409 *second = tmp_style.background;
413 static guint invert(guint icolour)
415 if (interface_prefs.highlighting_invert_all)
416 return utils_invert_color(icolour);
418 return icolour;
422 static GeanyLexerStyle *get_style(guint ft_id, guint styling_index)
424 g_assert(ft_id < filetypes_array->len);
426 if (G_UNLIKELY(ft_id == GEANY_FILETYPES_NONE))
428 g_assert(styling_index < GCS_MAX);
429 return &common_style_set.styling[styling_index];
431 else
433 StyleSet *set = &style_sets[ft_id];
435 g_assert(styling_index < set->count);
436 return &set->styling[styling_index];
441 static void set_sci_style(ScintillaObject *sci, guint style, guint ft_id, guint styling_index)
443 GeanyLexerStyle *style_ptr = get_style(ft_id, styling_index);
445 SSM(sci, SCI_STYLESETFORE, style, invert(style_ptr->foreground));
446 SSM(sci, SCI_STYLESETBACK, style, invert(style_ptr->background));
447 SSM(sci, SCI_STYLESETBOLD, style, style_ptr->bold);
448 SSM(sci, SCI_STYLESETITALIC, style, style_ptr->italic);
452 void highlighting_free_styles()
454 guint i;
456 for (i = 0; i < filetypes_array->len; i++)
457 free_styleset(i);
459 if (named_style_hash)
460 g_hash_table_destroy(named_style_hash);
462 g_free(style_sets);
466 static GString *get_global_typenames(gint lang)
468 GString *s = NULL;
470 if (app->tm_workspace)
472 GPtrArray *tags_array = app->tm_workspace->global_tags;
474 if (tags_array)
476 s = symbols_find_tags_as_string(tags_array, TM_GLOBAL_TYPE_MASK, lang);
479 return s;
483 static gchar*
484 get_keyfile_whitespace_chars(GKeyFile *config, GKeyFile *configh)
486 return utils_get_setting(string, configh, config,
487 "settings", "whitespace_chars", GEANY_WHITESPACE_CHARS);
491 static void add_named_style(GKeyFile *config, const gchar *key)
493 const gchar group[] = "named_styles";
494 gchar **list;
495 gsize len;
497 list = g_key_file_get_string_list(config, group, key, &len, NULL);
498 /* we allow a named style to reference another style above it */
499 if (list && len >= 1)
501 GeanyLexerStyle *style = g_new0(GeanyLexerStyle, 1);
503 parse_keyfile_style(config, list, &gsd_default, style);
504 g_hash_table_insert(named_style_hash, g_strdup(key), style);
506 g_strfreev(list);
510 static void get_named_styles(GKeyFile *config)
512 const gchar group[] = "named_styles";
513 gchar **keys = g_key_file_get_keys(config, group, NULL, NULL);
514 gchar **ptr = keys;
516 if (!ptr)
517 return;
519 while (1)
521 const gchar *key = *ptr;
523 if (!key)
524 break;
526 /* don't replace already read default style with system one */
527 if (!g_str_equal(key, "default"))
528 add_named_style(config, key);
530 ptr++;
532 g_strfreev(keys);
536 static GKeyFile *utils_key_file_new(const gchar *filename)
538 GKeyFile *config = g_key_file_new();
540 g_key_file_load_from_file(config, filename, G_KEY_FILE_KEEP_COMMENTS, NULL);
541 return config;
545 static void load_named_styles(GKeyFile *config, GKeyFile *config_home)
547 const gchar *scheme = editor_prefs.color_scheme;
548 gboolean free_kf = FALSE;
550 if (named_style_hash)
551 g_hash_table_destroy(named_style_hash); /* reloading */
553 named_style_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
555 if (NZV(scheme))
557 gchar *path, *path_home;
559 path = g_build_path(G_DIR_SEPARATOR_S, app->datadir, GEANY_COLORSCHEMES_SUBDIR, scheme, NULL);
560 path_home = g_build_path(G_DIR_SEPARATOR_S, app->configdir, GEANY_COLORSCHEMES_SUBDIR, scheme, NULL);
562 if (g_file_test(path, G_FILE_TEST_EXISTS) || g_file_test(path_home, G_FILE_TEST_EXISTS))
564 config = utils_key_file_new(path);
565 config_home = utils_key_file_new(path_home);
566 free_kf = TRUE;
568 /* if color scheme is missing, use default */
569 g_free(path);
570 g_free(path_home);
572 /* first set default to the "default" named style */
573 add_named_style(config, "default");
574 read_named_style("default", &gsd_default); /* in case user overrides but not with both colors */
575 add_named_style(config_home, "default");
576 read_named_style("default", &gsd_default);
578 get_named_styles(config);
579 /* home overrides any system named style */
580 get_named_styles(config_home);
582 if (free_kf)
584 g_key_file_free(config);
585 g_key_file_free(config_home);
590 static void styleset_common_init(GKeyFile *config, GKeyFile *config_home)
592 load_named_styles(config, config_home);
594 get_keyfile_style(config, config_home, "default", &common_style_set.styling[GCS_DEFAULT]);
595 get_keyfile_style(config, config_home, "selection", &common_style_set.styling[GCS_SELECTION]);
596 get_keyfile_style(config, config_home, "brace_good", &common_style_set.styling[GCS_BRACE_GOOD]);
597 get_keyfile_style(config, config_home, "brace_bad", &common_style_set.styling[GCS_BRACE_BAD]);
598 get_keyfile_style(config, config_home, "margin_linenumber", &common_style_set.styling[GCS_MARGIN_LINENUMBER]);
599 get_keyfile_style(config, config_home, "margin_folding", &common_style_set.styling[GCS_MARGIN_FOLDING]);
600 get_keyfile_style(config, config_home, "fold_symbol_highlight", &common_style_set.styling[GCS_FOLD_SYMBOL_HIGHLIGHT]);
601 get_keyfile_style(config, config_home, "current_line", &common_style_set.styling[GCS_CURRENT_LINE]);
602 get_keyfile_style(config, config_home, "caret", &common_style_set.styling[GCS_CARET]);
603 get_keyfile_style(config, config_home, "indent_guide", &common_style_set.styling[GCS_INDENT_GUIDE]);
604 get_keyfile_style(config, config_home, "white_space", &common_style_set.styling[GCS_WHITE_SPACE]);
605 get_keyfile_style(config, config_home, "marker_line", &common_style_set.styling[GCS_MARKER_LINE]);
606 get_keyfile_style(config, config_home, "marker_search", &common_style_set.styling[GCS_MARKER_SEARCH]);
607 get_keyfile_style(config, config_home, "marker_mark", &common_style_set.styling[GCS_MARKER_MARK]);
608 get_keyfile_style(config, config_home, "calltips", &common_style_set.styling[GCS_CALLTIPS]);
610 get_keyfile_ints(config, config_home, "styling", "folding_style",
611 1, 1, &common_style_set.fold_marker, &common_style_set.fold_lines);
612 get_keyfile_ints(config, config_home, "styling", "folding_horiz_line",
613 2, 0, &common_style_set.fold_draw_line, NULL);
614 get_keyfile_ints(config, config_home, "styling", "caret_width",
615 1, 0, &common_style_set.styling[GCS_CARET].background, NULL); /* caret.foreground used earlier */
616 get_keyfile_int(config, config_home, "styling", "line_wrap_visuals",
617 3, 0, &common_style_set.styling[GCS_LINE_WRAP_VISUALS]);
618 get_keyfile_int(config, config_home, "styling", "line_wrap_indent",
619 0, 0, &common_style_set.styling[GCS_LINE_WRAP_INDENT]);
620 get_keyfile_int(config, config_home, "styling", "translucency",
621 256, 256, &common_style_set.styling[GCS_TRANSLUCENCY]);
622 get_keyfile_int(config, config_home, "styling", "marker_translucency",
623 256, 256, &common_style_set.styling[GCS_MARKER_TRANSLUCENCY]);
624 get_keyfile_int(config, config_home, "styling", "line_height",
625 0, 0, &common_style_set.styling[GCS_LINE_HEIGHT]);
627 get_keyfile_wordchars(config, config_home, &common_style_set.wordchars);
628 whitespace_chars = get_keyfile_whitespace_chars(config, config_home);
632 static void set_character_classes(ScintillaObject *sci, guint ft_id)
634 const gchar *word = (ft_id == GEANY_FILETYPES_NONE ?
635 common_style_set.wordchars : style_sets[ft_id].wordchars);
636 gchar *whitespace;
637 guint i, j;
639 SSM(sci, SCI_SETWORDCHARS, 0, (sptr_t) word);
641 /* setting wordchars resets character classes, so we have to set whitespaces after
642 * wordchars, but we want wordchars to have precenence over whitepace chars */
643 whitespace = g_malloc0(strlen(whitespace_chars) + 1);
644 for (i = 0, j = 0; whitespace_chars[i] != 0; i++)
646 if (! strchr(word, whitespace_chars[i]))
647 whitespace[j++] = whitespace_chars[i];
649 whitespace[j] = 0;
651 SSM(sci, SCI_SETWHITESPACECHARS, 0, (sptr_t) whitespace);
653 g_free(whitespace);
657 static void styleset_common(ScintillaObject *sci, guint ft_id)
659 GeanyLexerStyle *style;
661 SSM(sci, SCI_STYLECLEARALL, 0, 0);
663 set_character_classes(sci, ft_id);
665 /* caret colour, style and width */
666 SSM(sci, SCI_SETCARETFORE, invert(common_style_set.styling[GCS_CARET].foreground), 0);
667 SSM(sci, SCI_SETCARETWIDTH, common_style_set.styling[GCS_CARET].background, 0);
668 if (common_style_set.styling[GCS_CARET].bold)
669 SSM(sci, SCI_SETCARETSTYLE, CARETSTYLE_BLOCK, 0);
670 else
671 SSM(sci, SCI_SETCARETSTYLE, CARETSTYLE_LINE, 0);
673 /* line height */
674 SSM(sci, SCI_SETEXTRAASCENT, common_style_set.styling[GCS_LINE_HEIGHT].foreground, 0);
675 SSM(sci, SCI_SETEXTRADESCENT, common_style_set.styling[GCS_LINE_HEIGHT].background, 0);
677 /* colourise the current line */
678 SSM(sci, SCI_SETCARETLINEBACK, invert(common_style_set.styling[GCS_CURRENT_LINE].background), 0);
679 /* bold=enable current line */
680 SSM(sci, SCI_SETCARETLINEVISIBLE, common_style_set.styling[GCS_CURRENT_LINE].bold, 0);
682 /* Translucency for current line and selection */
683 SSM(sci, SCI_SETCARETLINEBACKALPHA, common_style_set.styling[GCS_TRANSLUCENCY].foreground, 0);
684 SSM(sci, SCI_SETSELALPHA, common_style_set.styling[GCS_TRANSLUCENCY].background, 0);
686 /* line wrapping visuals */
687 SSM(sci, SCI_SETWRAPVISUALFLAGS,
688 common_style_set.styling[GCS_LINE_WRAP_VISUALS].foreground, 0);
689 SSM(sci, SCI_SETWRAPVISUALFLAGSLOCATION,
690 common_style_set.styling[GCS_LINE_WRAP_VISUALS].background, 0);
691 SSM(sci, SCI_SETWRAPSTARTINDENT, common_style_set.styling[GCS_LINE_WRAP_INDENT].foreground, 0);
692 SSM(sci, SCI_SETWRAPINDENTMODE, common_style_set.styling[GCS_LINE_WRAP_INDENT].background, 0);
694 /* Error indicator */
695 SSM(sci, SCI_INDICSETSTYLE, GEANY_INDICATOR_ERROR, INDIC_SQUIGGLEPIXMAP);
696 SSM(sci, SCI_INDICSETFORE, GEANY_INDICATOR_ERROR, invert(rotate_rgb(0xff0000)));
698 /* Search indicator, used for 'Mark' matches */
699 SSM(sci, SCI_INDICSETSTYLE, GEANY_INDICATOR_SEARCH, INDIC_ROUNDBOX);
700 SSM(sci, SCI_INDICSETFORE, GEANY_INDICATOR_SEARCH,
701 invert(common_style_set.styling[GCS_MARKER_SEARCH].background));
702 SSM(sci, SCI_INDICSETALPHA, GEANY_INDICATOR_SEARCH, 60);
704 /* define marker symbols
705 * 0 -> line marker */
706 SSM(sci, SCI_MARKERDEFINE, 0, SC_MARK_SHORTARROW);
707 SSM(sci, SCI_MARKERSETFORE, 0, invert(common_style_set.styling[GCS_MARKER_LINE].foreground));
708 SSM(sci, SCI_MARKERSETBACK, 0, invert(common_style_set.styling[GCS_MARKER_LINE].background));
709 SSM(sci, SCI_MARKERSETALPHA, 0, common_style_set.styling[GCS_MARKER_TRANSLUCENCY].foreground);
711 /* 1 -> user marker */
712 SSM(sci, SCI_MARKERDEFINE, 1, SC_MARK_PLUS);
713 SSM(sci, SCI_MARKERSETFORE, 1, invert(common_style_set.styling[GCS_MARKER_MARK].foreground));
714 SSM(sci, SCI_MARKERSETBACK, 1, invert(common_style_set.styling[GCS_MARKER_MARK].background));
715 SSM(sci, SCI_MARKERSETALPHA, 1, common_style_set.styling[GCS_MARKER_TRANSLUCENCY].background);
717 /* 2 -> folding marker, other folding settings */
718 SSM(sci, SCI_SETMARGINTYPEN, 2, SC_MARGIN_SYMBOL);
719 SSM(sci, SCI_SETMARGINMASKN, 2, SC_MASK_FOLDERS);
721 /* drawing a horizontal line when text if folded */
722 switch (common_style_set.fold_draw_line)
724 case 1:
726 SSM(sci, SCI_SETFOLDFLAGS, 4, 0);
727 break;
729 case 2:
731 SSM(sci, SCI_SETFOLDFLAGS, 16, 0);
732 break;
734 default:
736 SSM(sci, SCI_SETFOLDFLAGS, 0, 0);
737 break;
741 /* choose the folding style - boxes or circles, I prefer boxes, so it is default ;-) */
742 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEREND, SC_MARK_EMPTY);
743 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPENMID, SC_MARK_EMPTY);
744 switch (common_style_set.fold_marker)
746 case 2:
747 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPEN, SC_MARK_CIRCLEMINUS);
748 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDER, SC_MARK_CIRCLEPLUS);
749 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEREND, SC_MARK_CIRCLEPLUSCONNECTED);
750 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPENMID, SC_MARK_CIRCLEMINUSCONNECTED);
751 break;
752 default:
753 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPEN, SC_MARK_BOXMINUS);
754 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDER, SC_MARK_BOXPLUS);
755 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEREND, SC_MARK_BOXPLUSCONNECTED);
756 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPENMID, SC_MARK_BOXMINUSCONNECTED);
757 break;
758 case 3:
759 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPEN, SC_MARK_ARROWDOWN);
760 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDER, SC_MARK_ARROW);
761 break;
762 case 4:
763 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPEN, SC_MARK_MINUS);
764 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDER, SC_MARK_PLUS);
765 break;
768 /* choose the folding style - straight or curved, I prefer straight, so it is default ;-) */
769 switch (common_style_set.fold_lines)
771 case 2:
772 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDERMIDTAIL, SC_MARK_TCORNERCURVE);
773 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDERTAIL, SC_MARK_LCORNERCURVE);
774 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDERSUB, SC_MARK_VLINE);
775 break;
776 default:
777 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDERMIDTAIL, SC_MARK_TCORNER);
778 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDERTAIL, SC_MARK_LCORNER);
779 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDERSUB, SC_MARK_VLINE);
780 break;
781 case 0:
782 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDERMIDTAIL, SC_MARK_EMPTY);
783 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDERTAIL, SC_MARK_EMPTY);
784 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDERSUB, SC_MARK_EMPTY);
785 break;
788 gint markers[] = {
789 SC_MARKNUM_FOLDEROPEN,
790 SC_MARKNUM_FOLDER,
791 SC_MARKNUM_FOLDERSUB,
792 SC_MARKNUM_FOLDERTAIL,
793 SC_MARKNUM_FOLDEREND,
794 SC_MARKNUM_FOLDEROPENMID,
795 SC_MARKNUM_FOLDERMIDTAIL
797 guint i;
799 foreach_range(i, G_N_ELEMENTS(markers))
801 SSM(sci, SCI_MARKERSETFORE, markers[i],
802 invert(common_style_set.styling[GCS_FOLD_SYMBOL_HIGHLIGHT].foreground));
803 SSM(sci, SCI_MARKERSETBACK, markers[i],
804 invert(common_style_set.styling[GCS_MARGIN_FOLDING].foreground));
808 /* set some common defaults */
809 sci_set_property(sci, "fold", "1");
810 sci_set_property(sci, "fold.compact", "0");
811 sci_set_property(sci, "fold.comment", "1");
812 sci_set_property(sci, "fold.preprocessor", "1");
813 sci_set_property(sci, "fold.at.else", "1");
815 style = &common_style_set.styling[GCS_SELECTION];
816 if (!style->bold && !style->italic)
818 geany_debug("selection style is set to invisible - ignoring!");
819 style->italic = TRUE;
820 style->background = 0xc0c0c0;
822 /* bold (3rd argument) is whether to override default foreground selection */
823 SSM(sci, SCI_SETSELFORE, style->bold, invert(style->foreground));
824 /* italic (4th argument) is whether to override default background selection */
825 SSM(sci, SCI_SETSELBACK, style->italic, invert(style->background));
827 SSM(sci, SCI_SETSTYLEBITS, SSM(sci, SCI_GETSTYLEBITSNEEDED, 0, 0), 0);
829 SSM(sci, SCI_SETFOLDMARGINCOLOUR, 1, invert(common_style_set.styling[GCS_MARGIN_FOLDING].background));
830 SSM(sci, SCI_SETFOLDMARGINHICOLOUR, 1, invert(common_style_set.styling[GCS_MARGIN_FOLDING].background));
831 set_sci_style(sci, STYLE_LINENUMBER, GEANY_FILETYPES_NONE, GCS_MARGIN_LINENUMBER);
832 set_sci_style(sci, STYLE_BRACELIGHT, GEANY_FILETYPES_NONE, GCS_BRACE_GOOD);
833 set_sci_style(sci, STYLE_BRACEBAD, GEANY_FILETYPES_NONE, GCS_BRACE_BAD);
834 set_sci_style(sci, STYLE_INDENTGUIDE, GEANY_FILETYPES_NONE, GCS_INDENT_GUIDE);
836 /* bold = common whitespace settings enabled */
837 SSM(sci, SCI_SETWHITESPACEFORE, common_style_set.styling[GCS_WHITE_SPACE].bold,
838 invert(common_style_set.styling[GCS_WHITE_SPACE].foreground));
839 SSM(sci, SCI_SETWHITESPACEBACK, common_style_set.styling[GCS_WHITE_SPACE].italic,
840 invert(common_style_set.styling[GCS_WHITE_SPACE].background));
842 if (common_style_set.styling[GCS_CALLTIPS].bold)
843 SSM(sci, SCI_CALLTIPSETFORE, invert(common_style_set.styling[GCS_CALLTIPS].foreground), 1);
844 if (common_style_set.styling[GCS_CALLTIPS].italic)
845 SSM(sci, SCI_CALLTIPSETBACK, invert(common_style_set.styling[GCS_CALLTIPS].background), 1);
849 /* Merge & assign global typedefs and user secondary keywords.
850 * keyword_idx is used for both style_sets[].keywords and scintilla keyword style number */
851 static void merge_type_keywords(ScintillaObject *sci, guint ft_id, guint keyword_idx)
853 const gchar *user_words = style_sets[ft_id].keywords[keyword_idx];
854 GString *s;
856 s = get_global_typenames(filetypes[ft_id]->lang);
857 if (G_UNLIKELY(s == NULL))
858 s = g_string_sized_new(200);
859 else
860 g_string_append_c(s, ' '); /* append a space as delimiter to the existing list of words */
862 g_string_append(s, user_words);
864 sci_set_keywords(sci, keyword_idx, s->str);
865 g_string_free(s, TRUE);
869 static void styleset_init_from_mapping(guint ft_id, GKeyFile *config, GKeyFile *config_home,
870 const HLStyle *styles, gsize n_styles,
871 const HLKeyword *keywords, gsize n_keywords)
873 gsize i;
875 /* styles */
876 new_styleset(ft_id, n_styles);
877 foreach_range(i, n_styles)
879 GeanyLexerStyle *style = &style_sets[ft_id].styling[i];
881 get_keyfile_style(config, config_home, styles[i].name, style);
884 /* keywords */
885 if (n_keywords < 1)
886 style_sets[ft_id].keywords = NULL;
887 else
889 style_sets[ft_id].keywords = g_new(gchar*, n_keywords + 1);
890 foreach_range(i, n_keywords)
891 get_keyfile_keywords(config, config_home, keywords[i].key, ft_id, i);
892 style_sets[ft_id].keywords[i] = NULL;
897 /* STYLE_DEFAULT will be set to match the first style. */
898 static void styleset_from_mapping(ScintillaObject *sci, guint ft_id, guint lexer,
899 const HLStyle *styles, gsize n_styles,
900 const HLKeyword *keywords, gsize n_keywords,
901 const HLProperty *properties, gsize n_properties)
903 gsize i;
905 g_assert(ft_id != GEANY_FILETYPES_NONE);
907 /* lexer */
908 sci_set_lexer(sci, lexer);
910 /* styles */
911 styleset_common(sci, ft_id);
912 if (n_styles > 0)
914 /* first style is also default one */
915 set_sci_style(sci, STYLE_DEFAULT, ft_id, 0);
916 foreach_range(i, n_styles)
918 if (styles[i].fill_eol)
919 SSM(sci, SCI_STYLESETEOLFILLED, styles[i].style, TRUE);
920 set_sci_style(sci, styles[i].style, ft_id, i);
924 /* keywords */
925 foreach_range(i, n_keywords)
927 if (keywords[i].merge)
928 merge_type_keywords(sci, ft_id, i);
929 else
930 sci_set_keywords(sci, keywords[i].id, style_sets[ft_id].keywords[i]);
933 /* properties */
934 foreach_range(i, n_properties)
935 sci_set_property(sci, properties[i].property, properties[i].value);
940 static void styleset_default(ScintillaObject *sci, guint ft_id)
942 sci_set_lexer(sci, SCLEX_NULL);
944 /* we need to set STYLE_DEFAULT before we call SCI_STYLECLEARALL in styleset_common() */
945 set_sci_style(sci, STYLE_DEFAULT, GEANY_FILETYPES_NONE, GCS_DEFAULT);
947 styleset_common(sci, ft_id);
951 static void get_key_values(GKeyFile *config, const gchar *group, gchar **keys, gchar **values)
953 while (*keys)
955 gchar *str = g_key_file_get_string(config, group, *keys, NULL);
957 if (str)
958 SETPTR(*values, str);
960 keys++;
961 values++;
966 static void read_properties(GeanyFiletype *ft, GKeyFile *config, GKeyFile *configh)
968 gchar group[] = "lexer_properties";
969 gchar **keys;
970 gchar **keysh = g_key_file_get_keys(configh, group, NULL, NULL);
971 gchar **ptr;
973 /* remove overridden keys from system keyfile */
974 foreach_strv(ptr, keysh)
975 g_key_file_remove_key(config, group, *ptr, NULL);
977 /* merge sys and user keys */
978 keys = g_key_file_get_keys(config, group, NULL, NULL);
979 keys = utils_strv_join(keys, keysh);
981 if (keys)
983 gchar **values = g_new0(gchar*, g_strv_length(keys) + 1);
985 style_sets[ft->id].property_keys = keys;
986 style_sets[ft->id].property_values = values;
988 get_key_values(config, group, keys, values);
989 get_key_values(configh, group, keys, values);
994 static guint get_lexer_filetype(GeanyFiletype *ft)
996 ft = NVL(ft->lexer_filetype, ft);
997 return ft->id;
1001 #define init_styleset_case(LANG_NAME) \
1002 case (GEANY_FILETYPES_##LANG_NAME): \
1003 styleset_init_from_mapping(filetype_idx, config, configh, \
1004 highlighting_styles_##LANG_NAME, \
1005 HL_N_ENTRIES(highlighting_styles_##LANG_NAME), \
1006 highlighting_keywords_##LANG_NAME, \
1007 HL_N_ENTRIES(highlighting_keywords_##LANG_NAME)); \
1008 break
1010 /* Called by filetypes_load_config(). */
1011 void highlighting_init_styles(guint filetype_idx, GKeyFile *config, GKeyFile *configh)
1013 GeanyFiletype *ft = filetypes[filetype_idx];
1014 guint lexer_id = get_lexer_filetype(ft);
1015 gchar *default_str;
1017 if (!style_sets)
1018 style_sets = g_new0(StyleSet, filetypes_array->len);
1020 /* Clear old information if necessary - e.g. when reloading config */
1021 free_styleset(filetype_idx);
1023 read_properties(ft, config, configh);
1024 /* If a default style exists, check it uses a named style
1025 * Note: almost all filetypes have a "default" style, except HTML ones */
1026 default_str = utils_get_setting(string, configh, config,
1027 "styling", "default", "default");
1028 ft->priv->warn_color_scheme = !g_ascii_isalpha(*default_str);
1029 g_free(default_str);
1031 /* None filetype handled specially */
1032 if (filetype_idx == GEANY_FILETYPES_NONE)
1034 styleset_common_init(config, configh);
1035 return;
1037 /* All stylesets depend on filetypes.common */
1038 filetypes_load_config(GEANY_FILETYPES_NONE, FALSE);
1040 switch (lexer_id)
1042 init_styleset_case(ABAQUS);
1043 init_styleset_case(ADA);
1044 init_styleset_case(ASM);
1045 init_styleset_case(BASIC);
1046 init_styleset_case(C);
1047 init_styleset_case(CAML);
1048 init_styleset_case(CMAKE);
1049 init_styleset_case(COBOL);
1050 init_styleset_case(CONF);
1051 init_styleset_case(CSS);
1052 init_styleset_case(D);
1053 init_styleset_case(DIFF);
1054 init_styleset_case(LISP);
1055 init_styleset_case(ERLANG);
1056 init_styleset_case(DOCBOOK);
1057 init_styleset_case(FERITE);
1058 init_styleset_case(F77);
1059 init_styleset_case(FORTH);
1060 init_styleset_case(FORTRAN);
1061 init_styleset_case(HASKELL);
1062 init_styleset_case(HAXE);
1063 init_styleset_case(AS);
1064 init_styleset_case(HTML);
1065 init_styleset_case(JAVA);
1066 init_styleset_case(JS);
1067 init_styleset_case(LATEX);
1068 init_styleset_case(LUA);
1069 init_styleset_case(MAKE);
1070 init_styleset_case(MATLAB);
1071 init_styleset_case(MARKDOWN);
1072 init_styleset_case(NSIS);
1073 init_styleset_case(OBJECTIVEC);
1074 init_styleset_case(PASCAL);
1075 init_styleset_case(PERL);
1076 init_styleset_case(PHP);
1077 init_styleset_case(PO);
1078 init_styleset_case(PYTHON);
1079 init_styleset_case(R);
1080 init_styleset_case(RUBY);
1081 init_styleset_case(SH);
1082 init_styleset_case(SQL);
1083 init_styleset_case(TCL);
1084 init_styleset_case(TXT2TAGS);
1085 init_styleset_case(VHDL);
1086 init_styleset_case(VERILOG);
1087 init_styleset_case(XML);
1088 init_styleset_case(YAML);
1089 default:
1090 if (ft->lexer_filetype)
1091 geany_debug("Filetype %s has a recursive lexer_filetype %s set!",
1092 ft->name, ft->lexer_filetype->name);
1095 /* should be done in filetypes.c really: */
1096 get_keyfile_wordchars(config, configh, &style_sets[filetype_idx].wordchars);
1100 #define styleset_case(LANG_NAME) \
1101 case (GEANY_FILETYPES_##LANG_NAME): \
1102 styleset_from_mapping(sci, ft->id, highlighting_lexer_##LANG_NAME, \
1103 highlighting_styles_##LANG_NAME, \
1104 HL_N_ENTRIES(highlighting_styles_##LANG_NAME), \
1105 highlighting_keywords_##LANG_NAME, \
1106 HL_N_ENTRIES(highlighting_keywords_##LANG_NAME), \
1107 highlighting_properties_##LANG_NAME, \
1108 HL_N_ENTRIES(highlighting_properties_##LANG_NAME)); \
1109 break
1111 /** Sets up highlighting and other visual settings.
1112 * @param sci Scintilla widget.
1113 * @param ft Filetype settings to use. */
1114 void highlighting_set_styles(ScintillaObject *sci, GeanyFiletype *ft)
1116 guint lexer_id = get_lexer_filetype(ft);
1118 filetypes_load_config(ft->id, FALSE); /* load filetypes.ext */
1120 switch (lexer_id)
1122 styleset_case(ABAQUS);
1123 styleset_case(ADA);
1124 styleset_case(ASM);
1125 styleset_case(BASIC);
1126 styleset_case(C);
1127 styleset_case(CAML);
1128 styleset_case(CMAKE);
1129 styleset_case(COBOL);
1130 styleset_case(CONF);
1131 styleset_case(CSS);
1132 styleset_case(D);
1133 styleset_case(DIFF);
1134 styleset_case(LISP);
1135 styleset_case(ERLANG);
1136 styleset_case(DOCBOOK);
1137 styleset_case(FERITE);
1138 styleset_case(F77);
1139 styleset_case(FORTH);
1140 styleset_case(FORTRAN);
1141 styleset_case(HASKELL);
1142 styleset_case(HAXE);
1143 styleset_case(AS);
1144 styleset_case(HTML);
1145 styleset_case(JAVA);
1146 styleset_case(JS);
1147 styleset_case(LATEX);
1148 styleset_case(LUA);
1149 styleset_case(MAKE);
1150 styleset_case(MARKDOWN);
1151 styleset_case(MATLAB);
1152 styleset_case(NSIS);
1153 styleset_case(OBJECTIVEC);
1154 styleset_case(PASCAL);
1155 styleset_case(PERL);
1156 styleset_case(PHP);
1157 styleset_case(PO);
1158 styleset_case(PYTHON);
1159 styleset_case(R);
1160 styleset_case(RUBY);
1161 styleset_case(SH);
1162 styleset_case(SQL);
1163 styleset_case(TCL);
1164 styleset_case(TXT2TAGS);
1165 styleset_case(VHDL);
1166 styleset_case(VERILOG);
1167 styleset_case(XML);
1168 styleset_case(YAML);
1169 case GEANY_FILETYPES_NONE:
1170 default:
1171 styleset_default(sci, ft->id);
1173 /* [lexer_properties] settings */
1174 if (style_sets[ft->id].property_keys)
1176 gchar **prop = style_sets[ft->id].property_keys;
1177 gchar **val = style_sets[ft->id].property_values;
1179 while (*prop)
1181 sci_set_property(sci, *prop, *val);
1182 prop++;
1183 val++;
1189 /** Retrieves a style @a style_id for the filetype @a ft_id.
1190 * If the style was not already initialised
1191 * (e.g. by by opening a file of this type), it will be initialised. The returned pointer is
1192 * owned by Geany and must not be freed.
1193 * @param ft_id Filetype ID, e.g. @c GEANY_FILETYPES_DIFF.
1194 * @param style_id A Scintilla lexer style, e.g. @c SCE_DIFF_ADDED. See scintilla/include/SciLexer.h.
1195 * @return A pointer to the style struct.
1196 * @see Scintilla messages @c SCI_STYLEGETFORE, etc, for use with scintilla_send_message(). */
1197 const GeanyLexerStyle *highlighting_get_style(gint ft_id, gint style_id)
1199 g_return_val_if_fail(ft_id >= 0 && (guint) ft_id < filetypes_array->len, NULL);
1200 g_return_val_if_fail(style_id >= 0, NULL);
1202 /* ensure filetype loaded */
1203 filetypes_load_config((guint) ft_id, FALSE);
1205 /* TODO: style_id might not be the real array index (Scintilla styles are not always synced
1206 * with array indices) */
1207 return get_style((guint) ft_id, (guint) style_id);
1211 static GtkWidget *scheme_tree = NULL;
1213 enum
1215 SCHEME_MARKUP,
1216 SCHEME_FILE,
1217 SCHEME_COLUMNS
1220 static void on_color_scheme_changed(GtkTreeSelection *treesel, gpointer dummy)
1222 GtkTreeModel *model;
1223 GtkTreeIter iter;
1224 gchar *fname;
1225 gchar *path;
1227 if (!gtk_tree_selection_get_selected(treesel, &model, &iter))
1228 return;
1229 gtk_tree_model_get(model, &iter, SCHEME_FILE, &fname, -1);
1231 /* check if default item */
1232 if (!fname)
1234 SETPTR(editor_prefs.color_scheme, NULL);
1235 filetypes_reload();
1236 return;
1238 SETPTR(fname, utils_get_locale_from_utf8(fname));
1240 /* fname is just the basename from the menu item, so prepend the custom files path */
1241 path = g_build_path(G_DIR_SEPARATOR_S, app->configdir, GEANY_COLORSCHEMES_SUBDIR, fname, NULL);
1242 if (!g_file_test(path, G_FILE_TEST_EXISTS))
1244 /* try the system path */
1245 g_free(path);
1246 path = g_build_path(G_DIR_SEPARATOR_S, app->datadir, GEANY_COLORSCHEMES_SUBDIR, fname, NULL);
1248 if (g_file_test(path, G_FILE_TEST_EXISTS))
1250 SETPTR(editor_prefs.color_scheme, fname);
1251 fname = NULL;
1252 filetypes_reload();
1254 else
1256 SETPTR(fname, utils_get_utf8_from_locale(fname));
1257 ui_set_statusbar(TRUE, _("Could not find file '%s'."), fname);
1259 g_free(path);
1260 g_free(fname);
1264 static gchar *utils_get_setting_locale_string(GKeyFile *keyfile,
1265 const gchar *group, const gchar *key, const gchar *default_value)
1267 gchar *result = g_key_file_get_locale_string(keyfile, group, key, NULL, NULL);
1269 return NVL(result, g_strdup(default_value));
1273 static void add_color_scheme_item(GtkListStore *store,
1274 gchar *name, gchar *desc, const gchar *fn)
1276 GtkTreeIter iter;
1277 gchar *markup;
1279 /* reuse parameters */
1280 name = g_markup_escape_text(name, -1);
1281 desc = g_markup_escape_text(desc, -1);
1282 markup = g_strdup_printf("<big><b>%s</b></big>\n%s", name, desc);
1283 g_free(name);
1284 g_free(desc);
1286 gtk_list_store_append(store, &iter);
1287 gtk_list_store_set(store, &iter, SCHEME_MARKUP, markup,
1288 SCHEME_FILE, fn, -1);
1289 g_free(markup);
1291 if (utils_str_equal(fn, editor_prefs.color_scheme))
1293 GtkTreeSelection *treesel =
1294 gtk_tree_view_get_selection(GTK_TREE_VIEW(scheme_tree));
1296 gtk_tree_selection_select_iter(treesel, &iter);
1301 static void add_color_scheme_file(GtkListStore *store, const gchar *fname)
1303 GKeyFile *hkeyfile, *skeyfile;
1304 gchar *path, *theme_name, *theme_desc;
1305 gchar *theme_fn = utils_get_utf8_from_locale(fname);
1307 path = g_build_filename(app->configdir, GEANY_COLORSCHEMES_SUBDIR, fname, NULL);
1308 hkeyfile = utils_key_file_new(path);
1309 SETPTR(path, g_build_filename(app->datadir, GEANY_COLORSCHEMES_SUBDIR, fname, NULL));
1310 skeyfile = utils_key_file_new(path);
1312 theme_name = utils_get_setting(locale_string, hkeyfile, skeyfile, "theme_info", "name", theme_fn);
1313 theme_desc = utils_get_setting(locale_string, hkeyfile, skeyfile, "theme_info", "description", NULL);
1314 add_color_scheme_item(store, theme_name, theme_desc, theme_fn);
1316 g_free(path);
1317 g_free(theme_fn);
1318 g_free(theme_name);
1319 g_free(theme_desc);
1320 g_key_file_free(hkeyfile);
1321 g_key_file_free(skeyfile);
1325 static gboolean add_color_scheme_items(GtkListStore *store)
1327 GSList *list, *node;
1329 add_color_scheme_item(store, _("Default"), _("Default"), NULL);
1330 list = utils_get_config_files(GEANY_COLORSCHEMES_SUBDIR);
1332 foreach_slist(node, list)
1334 gchar *fname = node->data;
1336 if (g_str_has_suffix(fname, ".conf"))
1337 add_color_scheme_file(store, fname);
1339 g_free(fname);
1341 g_slist_free(list);
1342 return list != NULL;
1346 static void on_color_scheme_dialog_response(GtkWidget *dialog,
1347 gint response, gpointer *dialog_ptr)
1349 *dialog_ptr = NULL;
1350 gtk_widget_destroy(dialog);
1354 void highlighting_show_color_scheme_dialog(void)
1356 static GtkWidget *dialog = NULL;
1357 GtkListStore *store = gtk_list_store_new(SCHEME_COLUMNS,
1358 G_TYPE_STRING, G_TYPE_STRING);
1359 GtkCellRenderer *text_renderer;
1360 GtkTreeViewColumn *column;
1361 GtkTreeSelection *treesel;
1362 GtkWidget *vbox, *swin, *tree;
1363 GeanyDocument *doc;
1365 doc = document_get_current();
1366 if (doc && doc->file_type->priv->warn_color_scheme)
1367 dialogs_show_msgbox_with_secondary(GTK_MESSAGE_WARNING,
1368 _("The current filetype overrides the default style."),
1369 _("This may cause color schemes to display incorrectly."));
1371 scheme_tree = tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1372 g_object_unref(store);
1373 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(tree), TRUE);
1374 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree), FALSE);
1376 text_renderer = gtk_cell_renderer_text_new();
1377 g_object_set(text_renderer, "wrap-mode", PANGO_WRAP_WORD, NULL);
1378 column = gtk_tree_view_column_new_with_attributes(
1379 NULL, text_renderer, "markup", SCHEME_MARKUP, NULL);
1380 gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
1382 add_color_scheme_items(store);
1384 treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
1385 g_signal_connect(treesel, "changed", G_CALLBACK(on_color_scheme_changed), NULL);
1387 /* old dialog may still be showing */
1388 if (dialog)
1389 gtk_widget_destroy(dialog);
1390 dialog = gtk_dialog_new_with_buttons(_("Color Schemes"),
1391 GTK_WINDOW(main_widgets.window), GTK_DIALOG_DESTROY_WITH_PARENT,
1392 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL);
1393 vbox = ui_dialog_vbox_new(GTK_DIALOG(dialog));
1394 gtk_box_set_spacing(GTK_BOX(vbox), 6);
1395 gtk_widget_set_name(dialog, "GeanyDialog");
1396 gtk_window_set_default_size(GTK_WINDOW(dialog),
1397 GEANY_DEFAULT_DIALOG_HEIGHT * 7/4, GEANY_DEFAULT_DIALOG_HEIGHT);
1399 swin = gtk_scrolled_window_new(NULL, NULL);
1400 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(swin), GTK_SHADOW_ETCHED_IN);
1401 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin),
1402 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1403 gtk_container_add(GTK_CONTAINER(swin), tree);
1404 gtk_box_pack_start(GTK_BOX(vbox), swin, TRUE, TRUE, 0);
1405 g_signal_connect(dialog, "response", G_CALLBACK(on_color_scheme_dialog_response), &dialog);
1406 gtk_widget_show_all(dialog);
1410 /** Checks whether the given style is a string for the given lexer.
1412 * @param lexer Scintilla lexer type (@c SCLEX_*).
1413 * @param style Scintilla style (@c SCE_*).
1415 * @return @c TRUE if the style is a string, @c FALSE otherwise.
1417 gboolean highlighting_is_string_style(gint lexer, gint style)
1419 /* Don't forget STRINGEOL, to prevent completion whilst typing a string with no closing char. */
1421 switch (lexer)
1423 case SCLEX_CPP:
1424 return (style == SCE_C_CHARACTER ||
1425 style == SCE_C_STRING ||
1426 style == SCE_C_STRINGEOL ||
1427 style == SCE_C_STRINGRAW ||
1428 style == SCE_C_VERBATIM ||
1429 style == SCE_C_TRIPLEVERBATIM ||
1430 style == SCE_C_HASHQUOTEDSTRING);
1432 case SCLEX_PASCAL:
1433 return (style == SCE_PAS_CHARACTER ||
1434 style == SCE_PAS_STRING ||
1435 style == SCE_PAS_STRINGEOL);
1437 case SCLEX_D:
1438 return (style == SCE_D_STRING ||
1439 style == SCE_D_STRINGEOL ||
1440 style == SCE_D_CHARACTER ||
1441 style == SCE_D_STRINGB ||
1442 style == SCE_D_STRINGR);
1444 case SCLEX_PYTHON:
1445 return (style == SCE_P_STRING ||
1446 style == SCE_P_TRIPLE ||
1447 style == SCE_P_TRIPLEDOUBLE ||
1448 style == SCE_P_CHARACTER ||
1449 style == SCE_P_STRINGEOL);
1451 case SCLEX_F77:
1452 case SCLEX_FORTRAN:
1453 return (style == SCE_F_STRING1 ||
1454 style == SCE_F_STRING2 ||
1455 style == SCE_F_STRINGEOL);
1457 case SCLEX_PERL:
1458 return (style == SCE_PL_STRING ||
1459 style == SCE_PL_CHARACTER ||
1460 style == SCE_PL_HERE_DELIM ||
1461 style == SCE_PL_HERE_Q ||
1462 style == SCE_PL_HERE_QQ ||
1463 style == SCE_PL_HERE_QX ||
1464 style == SCE_PL_POD ||
1465 style == SCE_PL_STRING_Q ||
1466 style == SCE_PL_STRING_QQ ||
1467 style == SCE_PL_STRING_QX ||
1468 style == SCE_PL_STRING_QR ||
1469 style == SCE_PL_STRING_QW ||
1470 style == SCE_PL_POD_VERB ||
1471 style == SCE_PL_XLAT
1472 /* we don't include any STRING_*_VAR for autocompletion */);
1474 case SCLEX_PO:
1475 return (style == SCE_PO_MSGCTXT_TEXT ||
1476 style == SCE_PO_MSGCTXT_TEXT_EOL ||
1477 style == SCE_PO_MSGID_TEXT ||
1478 style == SCE_PO_MSGID_TEXT_EOL ||
1479 style == SCE_PO_MSGSTR_TEXT ||
1480 style == SCE_PO_MSGSTR_TEXT_EOL);
1482 case SCLEX_R:
1483 return (style == SCE_R_STRING);
1485 case SCLEX_RUBY:
1486 return (style == SCE_RB_CHARACTER ||
1487 style == SCE_RB_STRING ||
1488 style == SCE_RB_HERE_DELIM ||
1489 style == SCE_RB_HERE_Q ||
1490 style == SCE_RB_HERE_QQ ||
1491 style == SCE_RB_HERE_QX ||
1492 style == SCE_RB_POD);
1494 case SCLEX_BASH:
1495 return (style == SCE_SH_STRING);
1497 case SCLEX_SQL:
1498 return (style == SCE_SQL_STRING);
1500 case SCLEX_TCL:
1501 return (style == SCE_TCL_IN_QUOTE);
1503 case SCLEX_LUA:
1504 return (style == SCE_LUA_LITERALSTRING ||
1505 style == SCE_LUA_CHARACTER ||
1506 style == SCE_LUA_STRINGEOL ||
1507 style == SCE_LUA_STRING);
1509 case SCLEX_HASKELL:
1510 return (style == SCE_HA_CHARACTER ||
1511 style == SCE_HA_STRING);
1513 case SCLEX_FREEBASIC:
1514 return (style == SCE_B_STRING ||
1515 style == SCE_B_STRINGEOL);
1517 case SCLEX_OCTAVE:
1518 return (style == SCE_MATLAB_STRING ||
1519 style == SCE_MATLAB_DOUBLEQUOTESTRING);
1521 case SCLEX_XML:
1522 case SCLEX_HTML:
1523 return (
1524 style == SCE_HBA_STRING ||
1525 style == SCE_HBA_STRINGEOL ||
1526 style == SCE_HB_STRING ||
1527 style == SCE_HB_STRINGEOL ||
1528 style == SCE_H_CDATA ||
1529 style == SCE_H_DOUBLESTRING ||
1530 style == SCE_HJA_DOUBLESTRING ||
1531 style == SCE_HJA_SINGLESTRING ||
1532 style == SCE_HJA_STRINGEOL ||
1533 style == SCE_HJ_DOUBLESTRING ||
1534 style == SCE_HJ_SINGLESTRING ||
1535 style == SCE_HJ_STRINGEOL ||
1536 style == SCE_HPA_CHARACTER ||
1537 style == SCE_HPA_STRING ||
1538 style == SCE_HPA_TRIPLE ||
1539 style == SCE_HPA_TRIPLEDOUBLE ||
1540 style == SCE_HP_CHARACTER ||
1541 style == SCE_HPHP_HSTRING || /* HSTRING is a heredoc */
1542 style == SCE_HPHP_HSTRING_VARIABLE ||
1543 style == SCE_HPHP_SIMPLESTRING ||
1544 style == SCE_HP_STRING ||
1545 style == SCE_HP_TRIPLE ||
1546 style == SCE_HP_TRIPLEDOUBLE ||
1547 style == SCE_H_SGML_DOUBLESTRING ||
1548 style == SCE_H_SGML_SIMPLESTRING ||
1549 style == SCE_H_SINGLESTRING);
1551 case SCLEX_CMAKE:
1552 return (style == SCE_CMAKE_STRINGDQ ||
1553 style == SCE_CMAKE_STRINGLQ ||
1554 style == SCE_CMAKE_STRINGRQ ||
1555 style == SCE_CMAKE_STRINGVAR);
1557 case SCLEX_NSIS:
1558 return (style == SCE_NSIS_STRINGDQ ||
1559 style == SCE_NSIS_STRINGLQ ||
1560 style == SCE_NSIS_STRINGRQ ||
1561 style == SCE_NSIS_STRINGVAR);
1563 case SCLEX_ADA:
1564 return (style == SCE_ADA_CHARACTER ||
1565 style == SCE_ADA_STRING ||
1566 style == SCE_ADA_CHARACTEREOL ||
1567 style == SCE_ADA_STRINGEOL);
1569 case SCLEX_ABAQUS:
1570 return (style == SCE_ABAQUS_STRING);
1572 return FALSE;
1576 /** Checks whether the given style is a comment for the given lexer.
1578 * @param lexer Scintilla lexer type (@c SCLEX_*).
1579 * @param style Scintilla style (@c SCE_*).
1581 * @return @c TRUE if the style is a comment, @c FALSE otherwise.
1583 gboolean highlighting_is_comment_style(gint lexer, gint style)
1585 switch (lexer)
1587 case SCLEX_COBOL:
1588 case SCLEX_CPP:
1589 return (style == SCE_C_COMMENT ||
1590 style == SCE_C_COMMENTLINE ||
1591 style == SCE_C_COMMENTDOC ||
1592 style == SCE_C_PREPROCESSORCOMMENT ||
1593 style == SCE_C_COMMENTLINEDOC ||
1594 style == SCE_C_COMMENTDOCKEYWORD ||
1595 style == SCE_C_COMMENTDOCKEYWORDERROR);
1597 case SCLEX_PASCAL:
1598 return (style == SCE_PAS_COMMENT ||
1599 style == SCE_PAS_COMMENT2 ||
1600 style == SCE_PAS_COMMENTLINE);
1602 case SCLEX_D:
1603 return (style == SCE_D_COMMENT ||
1604 style == SCE_D_COMMENTLINE ||
1605 style == SCE_D_COMMENTDOC ||
1606 style == SCE_D_COMMENTNESTED ||
1607 style == SCE_D_COMMENTLINEDOC ||
1608 style == SCE_D_COMMENTDOCKEYWORD ||
1609 style == SCE_D_COMMENTDOCKEYWORDERROR);
1611 case SCLEX_PYTHON:
1612 return (style == SCE_P_COMMENTLINE ||
1613 style == SCE_P_COMMENTBLOCK);
1615 case SCLEX_F77:
1616 case SCLEX_FORTRAN:
1617 return (style == SCE_F_COMMENT);
1619 case SCLEX_PERL:
1620 return (style == SCE_PL_COMMENTLINE);
1622 case SCLEX_PROPERTIES:
1623 return (style == SCE_PROPS_COMMENT);
1625 case SCLEX_PO:
1626 return (style == SCE_PO_COMMENT ||
1627 style == SCE_PO_PROGRAMMER_COMMENT);
1629 case SCLEX_LATEX:
1630 return (style == SCE_L_COMMENT ||
1631 style == SCE_L_COMMENT2);
1633 case SCLEX_MAKEFILE:
1634 return (style == SCE_MAKE_COMMENT);
1636 case SCLEX_RUBY:
1637 return (style == SCE_RB_COMMENTLINE);
1639 case SCLEX_BASH:
1640 return (style == SCE_SH_COMMENTLINE);
1642 case SCLEX_R:
1643 return (style == SCE_R_COMMENT);
1645 case SCLEX_SQL:
1646 return (style == SCE_SQL_COMMENT ||
1647 style == SCE_SQL_COMMENTLINE ||
1648 style == SCE_SQL_COMMENTDOC ||
1649 style == SCE_SQL_COMMENTLINEDOC ||
1650 style == SCE_SQL_COMMENTDOCKEYWORD ||
1651 style == SCE_SQL_COMMENTDOCKEYWORDERROR);
1653 case SCLEX_TCL:
1654 return (style == SCE_TCL_COMMENT ||
1655 style == SCE_TCL_COMMENTLINE ||
1656 style == SCE_TCL_COMMENT_BOX ||
1657 style == SCE_TCL_BLOCK_COMMENT);
1659 case SCLEX_OCTAVE:
1660 return (style == SCE_MATLAB_COMMENT);
1662 case SCLEX_LUA:
1663 return (style == SCE_LUA_COMMENT ||
1664 style == SCE_LUA_COMMENTLINE ||
1665 style == SCE_LUA_COMMENTDOC);
1667 case SCLEX_HASKELL:
1668 return (style == SCE_HA_COMMENTLINE ||
1669 style == SCE_HA_COMMENTBLOCK ||
1670 style == SCE_HA_COMMENTBLOCK2 ||
1671 style == SCE_HA_COMMENTBLOCK3);
1673 case SCLEX_FREEBASIC:
1674 return (style == SCE_B_COMMENT);
1676 case SCLEX_YAML:
1677 return (style == SCE_YAML_COMMENT);
1679 case SCLEX_XML:
1680 case SCLEX_HTML:
1681 return (
1682 style == SCE_HBA_COMMENTLINE ||
1683 style == SCE_HB_COMMENTLINE ||
1684 style == SCE_H_COMMENT ||
1685 style == SCE_HJA_COMMENT ||
1686 style == SCE_HJA_COMMENTDOC ||
1687 style == SCE_HJA_COMMENTLINE ||
1688 style == SCE_HJ_COMMENT ||
1689 style == SCE_HJ_COMMENTDOC ||
1690 style == SCE_HJ_COMMENTLINE ||
1691 style == SCE_HPA_COMMENTLINE ||
1692 style == SCE_HP_COMMENTLINE ||
1693 style == SCE_HPHP_COMMENT ||
1694 style == SCE_HPHP_COMMENTLINE ||
1695 style == SCE_H_SGML_COMMENT);
1697 case SCLEX_CMAKE:
1698 return (style == SCE_CMAKE_COMMENT);
1700 case SCLEX_NSIS:
1701 return (style == SCE_NSIS_COMMENT ||
1702 style == SCE_NSIS_COMMENTBOX);
1704 case SCLEX_ADA:
1705 return (style == SCE_ADA_COMMENTLINE ||
1706 style == SCE_NSIS_COMMENTBOX);
1708 case SCLEX_ABAQUS:
1709 return (style == SCE_ABAQUS_COMMENT ||
1710 style == SCE_ABAQUS_COMMENTBLOCK);
1712 case SCLEX_ASM:
1713 return (style == SCE_ASM_COMMENT ||
1714 style == SCE_ASM_COMMENTBLOCK ||
1715 style == SCE_ASM_COMMENTDIRECTIVE);
1717 return FALSE;
1721 /** Checks whether the given style is normal code (not string, comment, preprocessor, etc).
1723 * @param lexer Scintilla lexer type (@c SCLEX_*).
1724 * @param style Scintilla style (@c SCE_*).
1726 * @return @c TRUE if the style is code, @c FALSE otherwise.
1728 gboolean highlighting_is_code_style(gint lexer, gint style)
1730 switch (lexer)
1732 case SCLEX_CPP:
1733 if (style == SCE_C_PREPROCESSOR)
1734 return FALSE;
1735 break;
1737 return !(highlighting_is_comment_style(lexer, style) ||
1738 highlighting_is_string_style(lexer, style));