Force selection to change when Default or Alt colourscheme chosen
[geany-mirror.git] / src / highlighting.c
blob26e891740f3cecd28a1344c6af3f3f3bd7d411f4
1 /*
2 * highlighting.c - this file is part of Geany, a fast and lightweight IDE
4 * Copyright 2005-2011 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
5 * Copyright 2006-2011 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 /**
23 * @file highlighting.h
24 * Syntax highlighting for the different filetypes, using the Scintilla lexers.
27 #include "geany.h"
29 #include <stdlib.h>
30 #include <ctype.h>
31 #include <string.h>
33 #include "SciLexer.h"
34 #include "highlighting.h"
35 #include "editor.h"
36 #include "utils.h"
37 #include "filetypes.h"
38 #include "symbols.h"
39 #include "ui_utils.h"
40 #include "utils.h"
41 #include "main.h"
42 #include "support.h"
43 #include "sciwrappers.h"
44 #include "document.h"
45 #include "dialogs.h"
46 #include "filetypesprivate.h"
48 #include "highlightingmappings.h"
51 #define GEANY_COLORSCHEMES_SUBDIR "colorschemes"
53 /* Whitespace has to be set after setting wordchars. */
54 #define GEANY_WHITESPACE_CHARS " \t" "!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~"
57 static gchar *whitespace_chars;
60 typedef struct
62 gsize count; /* number of styles */
63 GeanyLexerStyle *styling; /* array of styles, NULL if not used or uninitialised */
64 gchar **keywords;
65 gchar *wordchars; /* NULL used for style sets with no styles */
66 gchar **property_keys;
67 gchar **property_values;
68 } StyleSet;
70 /* each filetype has a styleset but GEANY_FILETYPES_NONE uses common_style_set for styling */
71 static StyleSet *style_sets = NULL;
74 enum /* Geany common styling */
76 GCS_DEFAULT,
77 GCS_SELECTION,
78 GCS_BRACE_GOOD,
79 GCS_BRACE_BAD,
80 GCS_MARGIN_LINENUMBER,
81 GCS_MARGIN_FOLDING,
82 GCS_FOLD_SYMBOL_HIGHLIGHT,
83 GCS_CURRENT_LINE,
84 GCS_CARET,
85 GCS_INDENT_GUIDE,
86 GCS_WHITE_SPACE,
87 GCS_LINE_WRAP_VISUALS,
88 GCS_LINE_WRAP_INDENT,
89 GCS_TRANSLUCENCY,
90 GCS_MARKER_LINE,
91 GCS_MARKER_SEARCH,
92 GCS_MARKER_MARK,
93 GCS_MARKER_TRANSLUCENCY,
94 GCS_LINE_HEIGHT,
95 GCS_CALLTIPS,
96 GCS_MAX
99 static struct
101 GeanyLexerStyle styling[GCS_MAX];
103 /* icon style, 1-4 */
104 gint fold_marker;
105 /* vertical line style, 0-2 */
106 gint fold_lines;
107 /* horizontal line when folded, 0-2 */
108 gint fold_draw_line;
110 gchar *wordchars;
111 } common_style_set;
114 /* For filetypes.common [named_styles] section.
115 * 0xBBGGRR format.
116 * e.g. "comment" => &GeanyLexerStyle{0x0000d0, 0xffffff, FALSE, FALSE} */
117 static GHashTable *named_style_hash = NULL;
119 /* 0xBBGGRR format, set by "default" named style. */
120 static GeanyLexerStyle gsd_default = {0x000000, 0xffffff, FALSE, FALSE};
123 /* Note: use sciwrappers.h instead where possible.
124 * Do not use SSM in files unrelated to scintilla. */
125 #define SSM(s, m, w, l) scintilla_send_message(s, m, w, l)
127 /* filetypes should use the filetypes.foo [lexer_properties] group instead of hardcoding */
128 static void sci_set_property(ScintillaObject *sci, const gchar *name, const gchar *value)
130 SSM(sci, SCI_SETPROPERTY, (uptr_t) name, (sptr_t) value);
134 static void new_styleset(guint file_type_id, gsize styling_count)
136 StyleSet *set = &style_sets[file_type_id];
138 set->count = styling_count;
139 set->styling = g_new0(GeanyLexerStyle, styling_count);
143 static void free_styleset(guint file_type_id)
145 StyleSet *style_ptr;
146 style_ptr = &style_sets[file_type_id];
148 style_ptr->count = 0;
149 g_free(style_ptr->styling);
150 style_ptr->styling = NULL;
151 g_strfreev(style_ptr->keywords);
152 style_ptr->keywords = NULL;
153 g_free(style_ptr->wordchars);
154 style_ptr->wordchars = NULL;
155 g_strfreev(style_ptr->property_keys);
156 style_ptr->property_keys = NULL;
157 g_strfreev(style_ptr->property_values);
158 style_ptr->property_values = NULL;
162 static void get_keyfile_keywords(GKeyFile *config, GKeyFile *configh,
163 const gchar *key, guint ft_id, guint pos)
165 style_sets[ft_id].keywords[pos] =
166 utils_get_setting(string, configh, config, "keywords", key, "");
170 static void get_keyfile_wordchars(GKeyFile *config, GKeyFile *configh, gchar **wordchars)
172 *wordchars = utils_get_setting(string, configh, config,
173 "settings", "wordchars", GEANY_WORDCHARS);
177 static gboolean read_named_style(const gchar *named_style, GeanyLexerStyle *style)
179 GeanyLexerStyle *cs;
180 gchar *comma, *name = NULL;
181 const gchar *bold = NULL;
182 const gchar *italic = NULL;
184 g_return_val_if_fail(named_style, FALSE);
185 name = utils_strdupa(named_style); /* named_style must not be written to, may be a static string */
187 comma = strstr(name, ",");
188 if (comma)
190 bold = strstr(comma, ",bold");
191 italic = strstr(comma, ",italic");
192 *comma = '\0'; /* terminate name to make lookup work */
194 cs = g_hash_table_lookup(named_style_hash, name);
196 if (cs)
198 *style = *cs;
199 if (bold)
200 style->bold = !style->bold;
201 if (italic)
202 style->italic = !style->italic;
204 else
206 *style = gsd_default;
208 return (cs != NULL);
212 /* Parses a color in `str` which can be an HTML color (ex. #0099cc),
213 * an abbreviated HTML color (ex. #09c) or a hex string color
214 * (ex. 0x0099cc). The result of the conversion is stored into the
215 * location pointed to by `clr`. */
216 static void parse_color(GKeyFile *kf, const gchar *str, gint *clr)
218 gint c;
219 gchar hex_clr[9] = { 0 };
220 gchar *named_color = NULL;
221 const gchar *start;
223 g_return_if_fail(clr != NULL);
225 if (G_UNLIKELY(! NZV(str)))
226 return;
228 named_color = g_key_file_get_string(kf, "named_colors", str, NULL);
229 if (named_color)
230 str = named_color;
232 if (str[0] == '#')
233 start = str + 1;
234 else if (strlen(str) > 1 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))
235 start = str + 2;
236 else
238 geany_debug("Bad color '%s'", str);
239 g_free(named_color);
240 return;
243 if (strlen(start) == 3)
245 snprintf(hex_clr, 9, "0x%c%c%c%c%c%c", start[0], start[0],
246 start[1], start[1], start[2], start[2]);
248 else
249 snprintf(hex_clr, 9, "0x%s", start);
251 g_free(named_color);
253 c = utils_strtod(hex_clr, NULL, FALSE);
255 if (c > -1)
257 *clr = c;
258 return;
260 geany_debug("Bad color '%s'", str);
264 static void parse_keyfile_style(GKeyFile *kf, gchar **list,
265 const GeanyLexerStyle *default_style, GeanyLexerStyle *style)
267 gsize len;
269 g_return_if_fail(default_style);
270 g_return_if_fail(style);
272 *style = *default_style;
274 if (!list)
275 return;
277 len = g_strv_length(list);
278 if (len == 0)
279 return;
280 else if (len == 1)
282 gchar **items = g_strsplit(list[0], ",", 0);
283 if (items != NULL)
285 if (g_strv_length(items) > 0)
287 if (g_hash_table_lookup(named_style_hash, items[0]) != NULL)
289 if (!read_named_style(list[0], style))
290 geany_debug("Unable to read named style '%s'", items[0]);
291 g_strfreev(items);
292 return;
294 else if (strchr(list[0], ',') != NULL)
296 geany_debug("Unknown named style '%s'", items[0]);
297 g_strfreev(items);
298 return;
301 g_strfreev(items);
305 switch (len)
307 case 4:
308 style->italic = utils_atob(list[3]);
309 case 3:
310 style->bold = utils_atob(list[2]);
311 case 2:
312 parse_color(kf, list[1], &style->background);
313 case 1:
314 parse_color(kf, list[0], &style->foreground);
319 static void get_keyfile_style(GKeyFile *config, GKeyFile *configh,
320 const gchar *key_name, GeanyLexerStyle *style)
322 gchar **list;
323 gsize len;
325 g_return_if_fail(config);
326 g_return_if_fail(configh);
327 g_return_if_fail(key_name);
328 g_return_if_fail(style);
330 list = g_key_file_get_string_list(configh, "styling", key_name, &len, NULL);
331 if (list == NULL)
333 list = g_key_file_get_string_list(config, "styling", key_name, &len, NULL);
334 parse_keyfile_style(config, list, &gsd_default, style);
336 else
337 parse_keyfile_style(configh, list, &gsd_default, style);
339 g_strfreev(list);
343 /* Convert 0xRRGGBB to 0xBBGGRR, which scintilla expects. */
344 static gint rotate_rgb(gint color)
346 return ((color & 0xFF0000) >> 16) +
347 (color & 0x00FF00) +
348 ((color & 0x0000FF) << 16);
352 static void convert_int(const gchar *int_str, gint *val)
354 gchar *end;
355 gint v = strtol(int_str, &end, 10);
357 if (int_str != end)
358 *val = v;
362 /* Get first and second integer numbers, store in foreground and background fields of @a style. */
363 static void get_keyfile_int(GKeyFile *config, GKeyFile *configh, const gchar *section,
364 const gchar *key, gint fdefault_val, gint sdefault_val,
365 GeanyLexerStyle *style)
367 gchar **list;
368 gsize len;
369 GeanyLexerStyle def = {fdefault_val, sdefault_val, FALSE, FALSE};
371 g_return_if_fail(config);
372 g_return_if_fail(configh);
373 g_return_if_fail(section);
374 g_return_if_fail(key);
376 list = g_key_file_get_string_list(configh, section, key, &len, NULL);
377 if (list == NULL)
378 list = g_key_file_get_string_list(config, section, key, &len, NULL);
380 *style = def;
381 if (!list)
382 return;
384 if (list[0])
386 convert_int(list[0], &style->foreground);
387 if (list[1])
389 convert_int(list[1], &style->background);
392 g_strfreev(list);
396 /* first or second can be NULL. */
397 static void get_keyfile_ints(GKeyFile *config, GKeyFile *configh, const gchar *section,
398 const gchar *key,
399 gint fdefault_val, gint sdefault_val,
400 gint *first, gint *second)
402 GeanyLexerStyle tmp_style;
404 get_keyfile_int(config, configh, section, key, fdefault_val, sdefault_val, &tmp_style);
405 if (first)
406 *first = tmp_style.foreground;
407 if (second)
408 *second = tmp_style.background;
412 static guint invert(guint icolour)
414 if (interface_prefs.highlighting_invert_all)
415 return utils_invert_color(icolour);
417 return icolour;
421 static GeanyLexerStyle *get_style(guint ft_id, guint styling_index)
423 g_assert(ft_id < filetypes_array->len);
425 if (G_UNLIKELY(ft_id == GEANY_FILETYPES_NONE))
427 g_assert(styling_index < GCS_MAX);
428 return &common_style_set.styling[styling_index];
430 else
432 StyleSet *set = &style_sets[ft_id];
434 g_assert(styling_index < set->count);
435 return &set->styling[styling_index];
440 static void set_sci_style(ScintillaObject *sci, guint style, guint ft_id, guint styling_index)
442 GeanyLexerStyle *style_ptr = get_style(ft_id, styling_index);
444 SSM(sci, SCI_STYLESETFORE, style, invert(style_ptr->foreground));
445 SSM(sci, SCI_STYLESETBACK, style, invert(style_ptr->background));
446 SSM(sci, SCI_STYLESETBOLD, style, style_ptr->bold);
447 SSM(sci, SCI_STYLESETITALIC, style, style_ptr->italic);
451 void highlighting_free_styles()
453 guint i;
455 for (i = 0; i < filetypes_array->len; i++)
456 free_styleset(i);
458 if (named_style_hash)
459 g_hash_table_destroy(named_style_hash);
461 g_free(style_sets);
465 static GString *get_global_typenames(gint lang)
467 GString *s = NULL;
469 if (app->tm_workspace)
471 GPtrArray *tags_array = app->tm_workspace->global_tags;
473 if (tags_array)
475 s = symbols_find_tags_as_string(tags_array, TM_GLOBAL_TYPE_MASK, lang);
478 return s;
482 static gchar*
483 get_keyfile_whitespace_chars(GKeyFile *config, GKeyFile *configh)
485 return utils_get_setting(string, configh, config,
486 "settings", "whitespace_chars", GEANY_WHITESPACE_CHARS);
490 static void add_named_style(GKeyFile *config, const gchar *key)
492 const gchar group[] = "named_styles";
493 gchar **list;
494 gsize len;
496 list = g_key_file_get_string_list(config, group, key, &len, NULL);
497 /* we allow a named style to reference another style above it */
498 if (list && len >= 1)
500 GeanyLexerStyle *style = g_new0(GeanyLexerStyle, 1);
502 parse_keyfile_style(config, list, &gsd_default, style);
503 g_hash_table_insert(named_style_hash, g_strdup(key), style);
505 g_strfreev(list);
509 static void get_named_styles(GKeyFile *config)
511 const gchar group[] = "named_styles";
512 gchar **keys = g_key_file_get_keys(config, group, NULL, NULL);
513 gchar **ptr = keys;
515 if (!ptr)
516 return;
518 while (1)
520 const gchar *key = *ptr;
522 if (!key)
523 break;
525 /* don't replace already read default style with system one */
526 if (!g_str_equal(key, "default"))
527 add_named_style(config, key);
529 ptr++;
531 g_strfreev(keys);
535 static GKeyFile *utils_key_file_new(const gchar *filename)
537 GKeyFile *config = g_key_file_new();
539 g_key_file_load_from_file(config, filename, G_KEY_FILE_KEEP_COMMENTS, NULL);
540 return config;
544 static void load_named_styles(GKeyFile *config, GKeyFile *config_home)
546 const gchar *scheme = editor_prefs.color_scheme;
547 gboolean free_kf = FALSE;
549 if (named_style_hash)
550 g_hash_table_destroy(named_style_hash); /* reloading */
552 named_style_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
554 if (NZV(scheme))
556 gchar *path, *path_home;
558 path = g_build_path(G_DIR_SEPARATOR_S, app->datadir, GEANY_COLORSCHEMES_SUBDIR, scheme, NULL);
559 path_home = g_build_path(G_DIR_SEPARATOR_S, app->configdir, GEANY_COLORSCHEMES_SUBDIR, scheme, NULL);
561 if (g_file_test(path, G_FILE_TEST_EXISTS) || g_file_test(path_home, G_FILE_TEST_EXISTS))
563 config = utils_key_file_new(path);
564 config_home = utils_key_file_new(path_home);
565 free_kf = TRUE;
567 /* if color scheme is missing, use default */
568 g_free(path);
569 g_free(path_home);
571 /* first set default to the "default" named style */
572 add_named_style(config, "default");
573 read_named_style("default", &gsd_default); /* in case user overrides but not with both colors */
574 add_named_style(config_home, "default");
575 read_named_style("default", &gsd_default);
577 get_named_styles(config);
578 /* home overrides any system named style */
579 get_named_styles(config_home);
581 if (free_kf)
583 g_key_file_free(config);
584 g_key_file_free(config_home);
589 static void styleset_common_init(GKeyFile *config, GKeyFile *config_home)
591 load_named_styles(config, config_home);
593 get_keyfile_style(config, config_home, "default", &common_style_set.styling[GCS_DEFAULT]);
594 get_keyfile_style(config, config_home, "selection", &common_style_set.styling[GCS_SELECTION]);
595 get_keyfile_style(config, config_home, "brace_good", &common_style_set.styling[GCS_BRACE_GOOD]);
596 get_keyfile_style(config, config_home, "brace_bad", &common_style_set.styling[GCS_BRACE_BAD]);
597 get_keyfile_style(config, config_home, "margin_linenumber", &common_style_set.styling[GCS_MARGIN_LINENUMBER]);
598 get_keyfile_style(config, config_home, "margin_folding", &common_style_set.styling[GCS_MARGIN_FOLDING]);
599 get_keyfile_style(config, config_home, "fold_symbol_highlight", &common_style_set.styling[GCS_FOLD_SYMBOL_HIGHLIGHT]);
600 get_keyfile_style(config, config_home, "current_line", &common_style_set.styling[GCS_CURRENT_LINE]);
601 get_keyfile_style(config, config_home, "caret", &common_style_set.styling[GCS_CARET]);
602 get_keyfile_style(config, config_home, "indent_guide", &common_style_set.styling[GCS_INDENT_GUIDE]);
603 get_keyfile_style(config, config_home, "white_space", &common_style_set.styling[GCS_WHITE_SPACE]);
604 get_keyfile_style(config, config_home, "marker_line", &common_style_set.styling[GCS_MARKER_LINE]);
605 get_keyfile_style(config, config_home, "marker_search", &common_style_set.styling[GCS_MARKER_SEARCH]);
606 get_keyfile_style(config, config_home, "marker_mark", &common_style_set.styling[GCS_MARKER_MARK]);
607 get_keyfile_style(config, config_home, "calltips", &common_style_set.styling[GCS_CALLTIPS]);
609 get_keyfile_ints(config, config_home, "styling", "folding_style",
610 1, 1, &common_style_set.fold_marker, &common_style_set.fold_lines);
611 get_keyfile_ints(config, config_home, "styling", "folding_horiz_line",
612 2, 0, &common_style_set.fold_draw_line, NULL);
613 get_keyfile_ints(config, config_home, "styling", "caret_width",
614 1, 0, &common_style_set.styling[GCS_CARET].background, NULL); /* caret.foreground used earlier */
615 get_keyfile_int(config, config_home, "styling", "line_wrap_visuals",
616 3, 0, &common_style_set.styling[GCS_LINE_WRAP_VISUALS]);
617 get_keyfile_int(config, config_home, "styling", "line_wrap_indent",
618 0, 0, &common_style_set.styling[GCS_LINE_WRAP_INDENT]);
619 get_keyfile_int(config, config_home, "styling", "translucency",
620 256, 256, &common_style_set.styling[GCS_TRANSLUCENCY]);
621 get_keyfile_int(config, config_home, "styling", "marker_translucency",
622 256, 256, &common_style_set.styling[GCS_MARKER_TRANSLUCENCY]);
623 get_keyfile_int(config, config_home, "styling", "line_height",
624 0, 0, &common_style_set.styling[GCS_LINE_HEIGHT]);
626 get_keyfile_wordchars(config, config_home, &common_style_set.wordchars);
627 whitespace_chars = get_keyfile_whitespace_chars(config, config_home);
631 static void styleset_common(ScintillaObject *sci, guint ft_id)
633 SSM(sci, SCI_STYLECLEARALL, 0, 0);
635 SSM(sci, SCI_SETWORDCHARS, 0, (sptr_t) (ft_id == GEANY_FILETYPES_NONE ?
636 common_style_set.wordchars : style_sets[ft_id].wordchars));
637 /* have to set whitespace after setting wordchars */
638 SSM(sci, SCI_SETWHITESPACECHARS, 0, (sptr_t) whitespace_chars);
640 /* caret colour, style and width */
641 SSM(sci, SCI_SETCARETFORE, invert(common_style_set.styling[GCS_CARET].foreground), 0);
642 SSM(sci, SCI_SETCARETWIDTH, common_style_set.styling[GCS_CARET].background, 0);
643 if (common_style_set.styling[GCS_CARET].bold)
644 SSM(sci, SCI_SETCARETSTYLE, CARETSTYLE_BLOCK, 0);
645 else
646 SSM(sci, SCI_SETCARETSTYLE, CARETSTYLE_LINE, 0);
648 /* line height */
649 SSM(sci, SCI_SETEXTRAASCENT, common_style_set.styling[GCS_LINE_HEIGHT].foreground, 0);
650 SSM(sci, SCI_SETEXTRADESCENT, common_style_set.styling[GCS_LINE_HEIGHT].background, 0);
652 /* colourise the current line */
653 SSM(sci, SCI_SETCARETLINEBACK, invert(common_style_set.styling[GCS_CURRENT_LINE].background), 0);
654 /* bold=enable current line */
655 SSM(sci, SCI_SETCARETLINEVISIBLE, common_style_set.styling[GCS_CURRENT_LINE].bold, 0);
657 /* Translucency for current line and selection */
658 SSM(sci, SCI_SETCARETLINEBACKALPHA, common_style_set.styling[GCS_TRANSLUCENCY].foreground, 0);
659 SSM(sci, SCI_SETSELALPHA, common_style_set.styling[GCS_TRANSLUCENCY].background, 0);
661 /* line wrapping visuals */
662 SSM(sci, SCI_SETWRAPVISUALFLAGS,
663 common_style_set.styling[GCS_LINE_WRAP_VISUALS].foreground, 0);
664 SSM(sci, SCI_SETWRAPVISUALFLAGSLOCATION,
665 common_style_set.styling[GCS_LINE_WRAP_VISUALS].background, 0);
666 SSM(sci, SCI_SETWRAPSTARTINDENT, common_style_set.styling[GCS_LINE_WRAP_INDENT].foreground, 0);
667 SSM(sci, SCI_SETWRAPINDENTMODE, common_style_set.styling[GCS_LINE_WRAP_INDENT].background, 0);
669 /* Error indicator */
670 SSM(sci, SCI_INDICSETSTYLE, GEANY_INDICATOR_ERROR, INDIC_SQUIGGLE);
671 SSM(sci, SCI_INDICSETFORE, GEANY_INDICATOR_ERROR, invert(rotate_rgb(0xff0000)));
673 /* Search indicator, used for 'Mark' matches */
674 SSM(sci, SCI_INDICSETSTYLE, GEANY_INDICATOR_SEARCH, INDIC_ROUNDBOX);
675 SSM(sci, SCI_INDICSETFORE, GEANY_INDICATOR_SEARCH,
676 invert(common_style_set.styling[GCS_MARKER_SEARCH].background));
677 SSM(sci, SCI_INDICSETALPHA, GEANY_INDICATOR_SEARCH, 60);
679 /* define marker symbols
680 * 0 -> line marker */
681 SSM(sci, SCI_MARKERDEFINE, 0, SC_MARK_SHORTARROW);
682 SSM(sci, SCI_MARKERSETFORE, 0, invert(common_style_set.styling[GCS_MARKER_LINE].foreground));
683 SSM(sci, SCI_MARKERSETBACK, 0, invert(common_style_set.styling[GCS_MARKER_LINE].background));
684 SSM(sci, SCI_MARKERSETALPHA, 0, common_style_set.styling[GCS_MARKER_TRANSLUCENCY].foreground);
686 /* 1 -> user marker */
687 SSM(sci, SCI_MARKERDEFINE, 1, SC_MARK_PLUS);
688 SSM(sci, SCI_MARKERSETFORE, 1, invert(common_style_set.styling[GCS_MARKER_MARK].foreground));
689 SSM(sci, SCI_MARKERSETBACK, 1, invert(common_style_set.styling[GCS_MARKER_MARK].background));
690 SSM(sci, SCI_MARKERSETALPHA, 1, common_style_set.styling[GCS_MARKER_TRANSLUCENCY].background);
692 /* 2 -> folding marker, other folding settings */
693 SSM(sci, SCI_SETMARGINTYPEN, 2, SC_MARGIN_SYMBOL);
694 SSM(sci, SCI_SETMARGINMASKN, 2, SC_MASK_FOLDERS);
696 /* drawing a horizontal line when text if folded */
697 switch (common_style_set.fold_draw_line)
699 case 1:
701 SSM(sci, SCI_SETFOLDFLAGS, 4, 0);
702 break;
704 case 2:
706 SSM(sci, SCI_SETFOLDFLAGS, 16, 0);
707 break;
709 default:
711 SSM(sci, SCI_SETFOLDFLAGS, 0, 0);
712 break;
716 /* choose the folding style - boxes or circles, I prefer boxes, so it is default ;-) */
717 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEREND, SC_MARK_EMPTY);
718 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPENMID, SC_MARK_EMPTY);
719 switch (common_style_set.fold_marker)
721 case 2:
722 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPEN, SC_MARK_CIRCLEMINUS);
723 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDER, SC_MARK_CIRCLEPLUS);
724 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEREND, SC_MARK_CIRCLEPLUSCONNECTED);
725 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPENMID, SC_MARK_CIRCLEMINUSCONNECTED);
726 break;
727 default:
728 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPEN, SC_MARK_BOXMINUS);
729 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDER, SC_MARK_BOXPLUS);
730 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEREND, SC_MARK_BOXPLUSCONNECTED);
731 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPENMID, SC_MARK_BOXMINUSCONNECTED);
732 break;
733 case 3:
734 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPEN, SC_MARK_ARROWDOWN);
735 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDER, SC_MARK_ARROW);
736 break;
737 case 4:
738 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPEN, SC_MARK_MINUS);
739 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDER, SC_MARK_PLUS);
740 break;
743 /* choose the folding style - straight or curved, I prefer straight, so it is default ;-) */
744 switch (common_style_set.fold_lines)
746 case 2:
747 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDERMIDTAIL, SC_MARK_TCORNERCURVE);
748 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDERTAIL, SC_MARK_LCORNERCURVE);
749 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDERSUB, SC_MARK_VLINE);
750 break;
751 default:
752 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDERMIDTAIL, SC_MARK_TCORNER);
753 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDERTAIL, SC_MARK_LCORNER);
754 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDERSUB, SC_MARK_VLINE);
755 break;
756 case 0:
757 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDERMIDTAIL, SC_MARK_EMPTY);
758 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDERTAIL, SC_MARK_EMPTY);
759 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDERSUB, SC_MARK_EMPTY);
760 break;
763 gint markers[] = {
764 SC_MARKNUM_FOLDEROPEN,
765 SC_MARKNUM_FOLDER,
766 SC_MARKNUM_FOLDERSUB,
767 SC_MARKNUM_FOLDERTAIL,
768 SC_MARKNUM_FOLDEREND,
769 SC_MARKNUM_FOLDEROPENMID,
770 SC_MARKNUM_FOLDERMIDTAIL
772 guint i;
774 foreach_range(i, G_N_ELEMENTS(markers))
776 SSM(sci, SCI_MARKERSETFORE, markers[i],
777 invert(common_style_set.styling[GCS_FOLD_SYMBOL_HIGHLIGHT].foreground));
778 SSM(sci, SCI_MARKERSETBACK, markers[i],
779 invert(common_style_set.styling[GCS_MARGIN_FOLDING].foreground));
783 /* set some common defaults */
784 sci_set_property(sci, "fold", "1");
785 sci_set_property(sci, "fold.compact", "0");
786 sci_set_property(sci, "fold.comment", "1");
787 sci_set_property(sci, "fold.preprocessor", "1");
788 sci_set_property(sci, "fold.at.else", "1");
790 /* bold (3rd argument) is whether to override default foreground selection */
791 if (common_style_set.styling[GCS_SELECTION].bold)
792 SSM(sci, SCI_SETSELFORE, 1, invert(common_style_set.styling[GCS_SELECTION].foreground));
793 /* italic (4th argument) is whether to override default background selection */
794 if (common_style_set.styling[GCS_SELECTION].italic)
795 SSM(sci, SCI_SETSELBACK, 1, invert(common_style_set.styling[GCS_SELECTION].background));
797 SSM(sci, SCI_SETSTYLEBITS, SSM(sci, SCI_GETSTYLEBITSNEEDED, 0, 0), 0);
799 SSM(sci, SCI_SETFOLDMARGINCOLOUR, 1, invert(common_style_set.styling[GCS_MARGIN_FOLDING].background));
800 SSM(sci, SCI_SETFOLDMARGINHICOLOUR, 1, invert(common_style_set.styling[GCS_MARGIN_FOLDING].background));
801 set_sci_style(sci, STYLE_LINENUMBER, GEANY_FILETYPES_NONE, GCS_MARGIN_LINENUMBER);
802 set_sci_style(sci, STYLE_BRACELIGHT, GEANY_FILETYPES_NONE, GCS_BRACE_GOOD);
803 set_sci_style(sci, STYLE_BRACEBAD, GEANY_FILETYPES_NONE, GCS_BRACE_BAD);
804 set_sci_style(sci, STYLE_INDENTGUIDE, GEANY_FILETYPES_NONE, GCS_INDENT_GUIDE);
806 /* bold = common whitespace settings enabled */
807 SSM(sci, SCI_SETWHITESPACEFORE, common_style_set.styling[GCS_WHITE_SPACE].bold,
808 invert(common_style_set.styling[GCS_WHITE_SPACE].foreground));
809 SSM(sci, SCI_SETWHITESPACEBACK, common_style_set.styling[GCS_WHITE_SPACE].italic,
810 invert(common_style_set.styling[GCS_WHITE_SPACE].background));
812 if (common_style_set.styling[GCS_CALLTIPS].bold)
813 SSM(sci, SCI_CALLTIPSETFORE, invert(common_style_set.styling[GCS_CALLTIPS].foreground), 1);
814 if (common_style_set.styling[GCS_CALLTIPS].italic)
815 SSM(sci, SCI_CALLTIPSETBACK, invert(common_style_set.styling[GCS_CALLTIPS].background), 1);
819 /* Merge & assign global typedefs and user secondary keywords.
820 * keyword_idx is used for both style_sets[].keywords and scintilla keyword style number */
821 static void merge_type_keywords(ScintillaObject *sci, guint ft_id, guint keyword_idx)
823 const gchar *user_words = style_sets[ft_id].keywords[keyword_idx];
824 GString *s;
826 s = get_global_typenames(filetypes[ft_id]->lang);
827 if (G_UNLIKELY(s == NULL))
828 s = g_string_sized_new(200);
829 else
830 g_string_append_c(s, ' '); /* append a space as delimiter to the existing list of words */
832 g_string_append(s, user_words);
834 sci_set_keywords(sci, keyword_idx, s->str);
835 g_string_free(s, TRUE);
839 static void styleset_init_from_mapping(guint ft_id, GKeyFile *config, GKeyFile *config_home,
840 const HLStyle *styles, gsize n_styles,
841 const HLKeyword *keywords, gsize n_keywords)
843 gsize i;
845 /* styles */
846 new_styleset(ft_id, n_styles);
847 foreach_range(i, n_styles)
849 GeanyLexerStyle *style = &style_sets[ft_id].styling[i];
851 get_keyfile_style(config, config_home, styles[i].name, style);
854 /* keywords */
855 if (n_keywords < 1)
856 style_sets[ft_id].keywords = NULL;
857 else
859 style_sets[ft_id].keywords = g_new(gchar*, n_keywords + 1);
860 foreach_range(i, n_keywords)
861 get_keyfile_keywords(config, config_home, keywords[i].key, ft_id, i);
862 style_sets[ft_id].keywords[i] = NULL;
867 /* STYLE_DEFAULT will be set to match the first style. */
868 static void styleset_from_mapping(ScintillaObject *sci, guint ft_id, guint lexer,
869 const HLStyle *styles, gsize n_styles,
870 const HLKeyword *keywords, gsize n_keywords,
871 const HLProperty *properties, gsize n_properties)
873 gsize i;
875 g_assert(ft_id != GEANY_FILETYPES_NONE);
877 /* lexer */
878 sci_set_lexer(sci, lexer);
880 /* styles */
881 styleset_common(sci, ft_id);
882 if (n_styles > 0)
884 /* first style is also default one */
885 set_sci_style(sci, STYLE_DEFAULT, ft_id, 0);
886 foreach_range(i, n_styles)
888 if (styles[i].fill_eol)
889 SSM(sci, SCI_STYLESETEOLFILLED, styles[i].style, TRUE);
890 set_sci_style(sci, styles[i].style, ft_id, i);
894 /* keywords */
895 foreach_range(i, n_keywords)
897 if (keywords[i].merge)
898 merge_type_keywords(sci, ft_id, i);
899 else
900 sci_set_keywords(sci, keywords[i].id, style_sets[ft_id].keywords[i]);
903 /* properties */
904 foreach_range(i, n_properties)
905 sci_set_property(sci, properties[i].property, properties[i].value);
910 static void styleset_default(ScintillaObject *sci, guint ft_id)
912 sci_set_lexer(sci, SCLEX_NULL);
914 /* we need to set STYLE_DEFAULT before we call SCI_STYLECLEARALL in styleset_common() */
915 set_sci_style(sci, STYLE_DEFAULT, GEANY_FILETYPES_NONE, GCS_DEFAULT);
917 styleset_common(sci, ft_id);
921 static void get_key_values(GKeyFile *config, const gchar *group, gchar **keys, gchar **values)
923 while (*keys)
925 gchar *str = g_key_file_get_string(config, group, *keys, NULL);
927 if (str)
928 SETPTR(*values, str);
930 keys++;
931 values++;
936 static void read_properties(GeanyFiletype *ft, GKeyFile *config, GKeyFile *configh)
938 gchar group[] = "lexer_properties";
939 gchar **keys;
940 gchar **keysh = g_key_file_get_keys(configh, group, NULL, NULL);
941 gchar **ptr;
943 /* remove overridden keys from system keyfile */
944 foreach_strv(ptr, keysh)
945 g_key_file_remove_key(config, group, *ptr, NULL);
947 /* merge sys and user keys */
948 keys = g_key_file_get_keys(config, group, NULL, NULL);
949 keys = utils_strv_join(keys, keysh);
951 if (keys)
953 gchar **values = g_new0(gchar*, g_strv_length(keys) + 1);
955 style_sets[ft->id].property_keys = keys;
956 style_sets[ft->id].property_values = values;
958 get_key_values(config, group, keys, values);
959 get_key_values(configh, group, keys, values);
964 static guint get_lexer_filetype(GeanyFiletype *ft)
966 ft = NVL(ft->lexer_filetype, ft);
967 return ft->id;
971 #define init_styleset_case(LANG_NAME) \
972 case (GEANY_FILETYPES_##LANG_NAME): \
973 styleset_init_from_mapping(filetype_idx, config, configh, \
974 highlighting_styles_##LANG_NAME, \
975 HL_N_ENTRIES(highlighting_styles_##LANG_NAME), \
976 highlighting_keywords_##LANG_NAME, \
977 HL_N_ENTRIES(highlighting_keywords_##LANG_NAME)); \
978 break
980 /* Called by filetypes_load_config(). */
981 void highlighting_init_styles(guint filetype_idx, GKeyFile *config, GKeyFile *configh)
983 GeanyFiletype *ft = filetypes[filetype_idx];
984 guint lexer_id = get_lexer_filetype(ft);
985 gchar *default_str;
987 if (!style_sets)
988 style_sets = g_new0(StyleSet, filetypes_array->len);
990 /* Clear old information if necessary - e.g. when reloading config */
991 free_styleset(filetype_idx);
993 read_properties(ft, config, configh);
994 /* If a default style exists, check it uses a named style
995 * Note: almost all filetypes have a "default" style, except HTML ones */
996 default_str = utils_get_setting(string, configh, config,
997 "styling", "default", "default");
998 ft->priv->warn_color_scheme = !g_ascii_isalpha(*default_str);
999 g_free(default_str);
1001 /* None filetype handled specially */
1002 if (filetype_idx == GEANY_FILETYPES_NONE)
1004 styleset_common_init(config, configh);
1005 return;
1007 /* All stylesets depend on filetypes.common */
1008 filetypes_load_config(GEANY_FILETYPES_NONE, FALSE);
1010 switch (lexer_id)
1012 init_styleset_case(ADA);
1013 init_styleset_case(ASM);
1014 init_styleset_case(BASIC);
1015 init_styleset_case(C);
1016 init_styleset_case(CAML);
1017 init_styleset_case(CMAKE);
1018 init_styleset_case(COBOL);
1019 init_styleset_case(CONF);
1020 init_styleset_case(CSS);
1021 init_styleset_case(D);
1022 init_styleset_case(DIFF);
1023 init_styleset_case(LISP);
1024 init_styleset_case(ERLANG);
1025 init_styleset_case(DOCBOOK);
1026 init_styleset_case(FERITE);
1027 init_styleset_case(F77);
1028 init_styleset_case(FORTH);
1029 init_styleset_case(FORTRAN);
1030 init_styleset_case(HASKELL);
1031 init_styleset_case(HAXE);
1032 init_styleset_case(AS);
1033 init_styleset_case(HTML);
1034 init_styleset_case(JAVA);
1035 init_styleset_case(JS);
1036 init_styleset_case(LATEX);
1037 init_styleset_case(LUA);
1038 init_styleset_case(MAKE);
1039 init_styleset_case(MATLAB);
1040 init_styleset_case(MARKDOWN);
1041 init_styleset_case(NSIS);
1042 init_styleset_case(OBJECTIVEC);
1043 init_styleset_case(PASCAL);
1044 init_styleset_case(PERL);
1045 init_styleset_case(PHP);
1046 init_styleset_case(PO);
1047 init_styleset_case(PYTHON);
1048 init_styleset_case(R);
1049 init_styleset_case(RUBY);
1050 init_styleset_case(SH);
1051 init_styleset_case(SQL);
1052 init_styleset_case(TCL);
1053 init_styleset_case(TXT2TAGS);
1054 init_styleset_case(VHDL);
1055 init_styleset_case(VERILOG);
1056 init_styleset_case(XML);
1057 init_styleset_case(YAML);
1058 default:
1059 if (ft->lexer_filetype)
1060 geany_debug("Filetype %s has a recursive lexer_filetype %s set!",
1061 ft->name, ft->lexer_filetype->name);
1064 /* should be done in filetypes.c really: */
1065 get_keyfile_wordchars(config, configh, &style_sets[filetype_idx].wordchars);
1069 #define styleset_case(LANG_NAME) \
1070 case (GEANY_FILETYPES_##LANG_NAME): \
1071 styleset_from_mapping(sci, ft->id, highlighting_lexer_##LANG_NAME, \
1072 highlighting_styles_##LANG_NAME, \
1073 HL_N_ENTRIES(highlighting_styles_##LANG_NAME), \
1074 highlighting_keywords_##LANG_NAME, \
1075 HL_N_ENTRIES(highlighting_keywords_##LANG_NAME), \
1076 highlighting_properties_##LANG_NAME, \
1077 HL_N_ENTRIES(highlighting_properties_##LANG_NAME)); \
1078 break
1080 /** Sets up highlighting and other visual settings.
1081 * @param sci Scintilla widget.
1082 * @param ft Filetype settings to use. */
1083 void highlighting_set_styles(ScintillaObject *sci, GeanyFiletype *ft)
1085 guint lexer_id = get_lexer_filetype(ft);
1087 filetypes_load_config(ft->id, FALSE); /* load filetypes.ext */
1089 switch (lexer_id)
1091 styleset_case(ADA);
1092 styleset_case(ASM);
1093 styleset_case(BASIC);
1094 styleset_case(C);
1095 styleset_case(CAML);
1096 styleset_case(CMAKE);
1097 styleset_case(COBOL);
1098 styleset_case(CONF);
1099 styleset_case(CSS);
1100 styleset_case(D);
1101 styleset_case(DIFF);
1102 styleset_case(LISP);
1103 styleset_case(ERLANG);
1104 styleset_case(DOCBOOK);
1105 styleset_case(FERITE);
1106 styleset_case(F77);
1107 styleset_case(FORTH);
1108 styleset_case(FORTRAN);
1109 styleset_case(HASKELL);
1110 styleset_case(HAXE);
1111 styleset_case(AS);
1112 styleset_case(HTML);
1113 styleset_case(JAVA);
1114 styleset_case(JS);
1115 styleset_case(LATEX);
1116 styleset_case(LUA);
1117 styleset_case(MAKE);
1118 styleset_case(MARKDOWN);
1119 styleset_case(MATLAB);
1120 styleset_case(NSIS);
1121 styleset_case(OBJECTIVEC);
1122 styleset_case(PASCAL);
1123 styleset_case(PERL);
1124 styleset_case(PHP);
1125 styleset_case(PO);
1126 styleset_case(PYTHON);
1127 styleset_case(R);
1128 styleset_case(RUBY);
1129 styleset_case(SH);
1130 styleset_case(SQL);
1131 styleset_case(TCL);
1132 styleset_case(TXT2TAGS);
1133 styleset_case(VHDL);
1134 styleset_case(VERILOG);
1135 styleset_case(XML);
1136 styleset_case(YAML);
1137 case GEANY_FILETYPES_NONE:
1138 default:
1139 styleset_default(sci, ft->id);
1141 /* [lexer_properties] settings */
1142 if (style_sets[ft->id].property_keys)
1144 gchar **prop = style_sets[ft->id].property_keys;
1145 gchar **val = style_sets[ft->id].property_values;
1147 while (*prop)
1149 sci_set_property(sci, *prop, *val);
1150 prop++;
1151 val++;
1157 /** Retrieves a style @a style_id for the filetype @a ft_id.
1158 * If the style was not already initialised
1159 * (e.g. by by opening a file of this type), it will be initialised. The returned pointer is
1160 * owned by Geany and must not be freed.
1161 * @param ft_id Filetype ID, e.g. @c GEANY_FILETYPES_DIFF.
1162 * @param style_id A Scintilla lexer style, e.g. @c SCE_DIFF_ADDED. See scintilla/include/SciLexer.h.
1163 * @return A pointer to the style struct.
1164 * @see Scintilla messages @c SCI_STYLEGETFORE, etc, for use with scintilla_send_message(). */
1165 const GeanyLexerStyle *highlighting_get_style(gint ft_id, gint style_id)
1167 g_return_val_if_fail(ft_id >= 0 && (guint) ft_id < filetypes_array->len, NULL);
1168 g_return_val_if_fail(style_id >= 0, NULL);
1170 /* ensure filetype loaded */
1171 filetypes_load_config((guint) ft_id, FALSE);
1173 /* TODO: style_id might not be the real array index (Scintilla styles are not always synced
1174 * with array indices) */
1175 return get_style((guint) ft_id, (guint) style_id);
1179 static GtkWidget *scheme_tree = NULL;
1181 enum
1183 SCHEME_MARKUP,
1184 SCHEME_FILE,
1185 SCHEME_COLUMNS
1188 static void on_color_scheme_changed(GtkTreeSelection *treesel, gpointer dummy)
1190 GtkTreeModel *model;
1191 GtkTreeIter iter;
1192 gchar *fname;
1193 gchar *path;
1195 if (!gtk_tree_selection_get_selected(treesel, &model, &iter))
1196 return;
1197 gtk_tree_model_get(model, &iter, SCHEME_FILE, &fname, -1);
1199 /* check if default item */
1200 if (!fname)
1202 SETPTR(editor_prefs.color_scheme, NULL);
1203 filetypes_reload();
1204 return;
1206 SETPTR(fname, utils_get_locale_from_utf8(fname));
1208 /* fname is just the basename from the menu item, so prepend the custom files path */
1209 path = g_build_path(G_DIR_SEPARATOR_S, app->configdir, GEANY_COLORSCHEMES_SUBDIR, fname, NULL);
1210 if (!g_file_test(path, G_FILE_TEST_EXISTS))
1212 /* try the system path */
1213 g_free(path);
1214 path = g_build_path(G_DIR_SEPARATOR_S, app->datadir, GEANY_COLORSCHEMES_SUBDIR, fname, NULL);
1216 if (g_file_test(path, G_FILE_TEST_EXISTS))
1218 SETPTR(editor_prefs.color_scheme, fname);
1219 fname = NULL;
1220 filetypes_reload();
1222 else
1224 SETPTR(fname, utils_get_utf8_from_locale(fname));
1225 ui_set_statusbar(TRUE, _("Could not find file '%s'."), fname);
1227 g_free(path);
1228 g_free(fname);
1232 static gchar *utils_get_setting_locale_string(GKeyFile *keyfile,
1233 const gchar *group, const gchar *key, const gchar *default_value)
1235 gchar *result = g_key_file_get_locale_string(keyfile, group, key, NULL, NULL);
1237 return NVL(result, g_strdup(default_value));
1241 static void add_color_scheme_item(GtkListStore *store,
1242 gchar *name, gchar *desc, const gchar *fn)
1244 GtkTreeIter iter;
1245 gchar *markup;
1247 /* reuse parameters */
1248 name = g_markup_escape_text(name, -1);
1249 desc = g_markup_escape_text(desc, -1);
1250 markup = g_strdup_printf("<big><b>%s</b></big>\n%s", name, desc);
1251 g_free(name);
1252 g_free(desc);
1254 gtk_list_store_append(store, &iter);
1255 gtk_list_store_set(store, &iter, SCHEME_MARKUP, markup,
1256 SCHEME_FILE, fn, -1);
1257 g_free(markup);
1259 if (utils_str_equal(fn, editor_prefs.color_scheme))
1261 GtkTreeSelection *treesel =
1262 gtk_tree_view_get_selection(GTK_TREE_VIEW(scheme_tree));
1264 gtk_tree_selection_select_iter(treesel, &iter);
1269 static void add_color_scheme_file(GtkListStore *store, const gchar *fname)
1271 GKeyFile *hkeyfile, *skeyfile;
1272 gchar *path, *theme_name, *theme_desc;
1273 gchar *theme_fn = utils_get_utf8_from_locale(fname);
1275 path = g_build_filename(app->configdir, GEANY_COLORSCHEMES_SUBDIR, fname, NULL);
1276 hkeyfile = utils_key_file_new(path);
1277 SETPTR(path, g_build_filename(app->datadir, GEANY_COLORSCHEMES_SUBDIR, fname, NULL));
1278 skeyfile = utils_key_file_new(path);
1280 theme_name = utils_get_setting(locale_string, hkeyfile, skeyfile, "theme_info", "name", theme_fn);
1281 theme_desc = utils_get_setting(locale_string, hkeyfile, skeyfile, "theme_info", "description", NULL);
1282 add_color_scheme_item(store, theme_name, theme_desc, theme_fn);
1284 g_free(path);
1285 g_free(theme_fn);
1286 g_free(theme_name);
1287 g_free(theme_desc);
1288 g_key_file_free(hkeyfile);
1289 g_key_file_free(skeyfile);
1293 static gboolean add_color_scheme_items(GtkListStore *store)
1295 GSList *list, *node;
1297 add_color_scheme_item(store, _("Default"), _("Default"), NULL);
1298 list = utils_get_config_files(GEANY_COLORSCHEMES_SUBDIR);
1300 foreach_slist(node, list)
1302 gchar *fname = node->data;
1304 if (g_str_has_suffix(fname, ".conf"))
1305 add_color_scheme_file(store, fname);
1307 g_free(fname);
1309 g_slist_free(list);
1310 return list != NULL;
1314 static void on_color_scheme_dialog_response(GtkWidget *dialog,
1315 gint response, gpointer *dialog_ptr)
1317 *dialog_ptr = NULL;
1318 gtk_widget_destroy(dialog);
1322 void highlighting_show_color_scheme_dialog(void)
1324 static GtkWidget *dialog = NULL;
1325 GtkListStore *store = gtk_list_store_new(SCHEME_COLUMNS,
1326 G_TYPE_STRING, G_TYPE_STRING);
1327 GtkCellRenderer *text_renderer;
1328 GtkTreeViewColumn *column;
1329 GtkTreeSelection *treesel;
1330 GtkWidget *vbox, *swin, *tree;
1331 GeanyDocument *doc;
1333 doc = document_get_current();
1334 if (doc && doc->file_type->priv->warn_color_scheme)
1335 dialogs_show_msgbox_with_secondary(GTK_MESSAGE_WARNING,
1336 _("The current filetype overrides the default style."),
1337 _("This may cause color schemes to display incorrectly."));
1339 scheme_tree = tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1340 g_object_unref(store);
1341 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(tree), TRUE);
1342 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree), FALSE);
1344 text_renderer = gtk_cell_renderer_text_new();
1345 g_object_set(text_renderer, "wrap-mode", PANGO_WRAP_WORD, NULL);
1346 column = gtk_tree_view_column_new_with_attributes(
1347 NULL, text_renderer, "markup", SCHEME_MARKUP, NULL);
1348 gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
1350 add_color_scheme_items(store);
1352 treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
1353 g_signal_connect(treesel, "changed", G_CALLBACK(on_color_scheme_changed), NULL);
1355 /* old dialog may still be showing */
1356 if (dialog)
1357 gtk_widget_destroy(dialog);
1358 dialog = gtk_dialog_new_with_buttons(_("Color Schemes"),
1359 GTK_WINDOW(main_widgets.window), GTK_DIALOG_DESTROY_WITH_PARENT,
1360 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL);
1361 vbox = ui_dialog_vbox_new(GTK_DIALOG(dialog));
1362 gtk_box_set_spacing(GTK_BOX(vbox), 6);
1363 gtk_widget_set_name(dialog, "GeanyDialog");
1364 gtk_window_set_default_size(GTK_WINDOW(dialog),
1365 GEANY_DEFAULT_DIALOG_HEIGHT * 7/4, GEANY_DEFAULT_DIALOG_HEIGHT);
1367 swin = gtk_scrolled_window_new(NULL, NULL);
1368 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(swin), GTK_SHADOW_ETCHED_IN);
1369 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin),
1370 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1371 gtk_container_add(GTK_CONTAINER(swin), tree);
1372 gtk_container_add(GTK_CONTAINER(vbox), swin);
1373 g_signal_connect(dialog, "response", G_CALLBACK(on_color_scheme_dialog_response), &dialog);
1374 gtk_widget_show_all(dialog);
1378 /** Checks whether the given style is a string for the given lexer.
1380 * @param lexer Scintilla lexer type (@c SCLEX_*).
1381 * @param style Scintilla style (@c SCE_*).
1383 * @return @c TRUE if the style is a string, @c FALSE otherwise.
1385 gboolean highlighting_is_string_style(gint lexer, gint style)
1387 /* Don't forget STRINGEOL, to prevent completion whilst typing a string with no closing char. */
1389 switch (lexer)
1391 case SCLEX_CPP:
1392 return (style == SCE_C_CHARACTER ||
1393 style == SCE_C_STRING ||
1394 style == SCE_C_STRINGEOL ||
1395 style == SCE_C_STRINGRAW ||
1396 style == SCE_C_VERBATIM ||
1397 style == SCE_C_TRIPLEVERBATIM);
1399 case SCLEX_PASCAL:
1400 return (style == SCE_PAS_CHARACTER ||
1401 style == SCE_PAS_STRING ||
1402 style == SCE_PAS_STRINGEOL);
1404 case SCLEX_D:
1405 return (style == SCE_D_STRING ||
1406 style == SCE_D_STRINGEOL ||
1407 style == SCE_D_CHARACTER ||
1408 style == SCE_D_STRINGB ||
1409 style == SCE_D_STRINGR);
1411 case SCLEX_PYTHON:
1412 return (style == SCE_P_STRING ||
1413 style == SCE_P_TRIPLE ||
1414 style == SCE_P_TRIPLEDOUBLE ||
1415 style == SCE_P_CHARACTER ||
1416 style == SCE_P_STRINGEOL);
1418 case SCLEX_F77:
1419 case SCLEX_FORTRAN:
1420 return (style == SCE_F_STRING1 ||
1421 style == SCE_F_STRING2 ||
1422 style == SCE_F_STRINGEOL);
1424 case SCLEX_PERL:
1425 return (style == SCE_PL_STRING ||
1426 style == SCE_PL_CHARACTER ||
1427 style == SCE_PL_HERE_DELIM ||
1428 style == SCE_PL_HERE_Q ||
1429 style == SCE_PL_HERE_QQ ||
1430 style == SCE_PL_HERE_QX ||
1431 style == SCE_PL_POD ||
1432 style == SCE_PL_STRING_Q ||
1433 style == SCE_PL_STRING_QQ ||
1434 style == SCE_PL_STRING_QX ||
1435 style == SCE_PL_STRING_QR ||
1436 style == SCE_PL_STRING_QW ||
1437 style == SCE_PL_POD_VERB ||
1438 style == SCE_PL_XLAT
1439 /* we don't include any STRING_*_VAR for autocompletion */);
1441 case SCLEX_R:
1442 return (style == SCE_R_STRING);
1444 case SCLEX_RUBY:
1445 return (style == SCE_RB_CHARACTER ||
1446 style == SCE_RB_STRING ||
1447 style == SCE_RB_HERE_DELIM ||
1448 style == SCE_RB_HERE_Q ||
1449 style == SCE_RB_HERE_QQ ||
1450 style == SCE_RB_HERE_QX ||
1451 style == SCE_RB_POD);
1453 case SCLEX_BASH:
1454 return (style == SCE_SH_STRING);
1456 case SCLEX_SQL:
1457 return (style == SCE_SQL_STRING);
1459 case SCLEX_TCL:
1460 return (style == SCE_TCL_IN_QUOTE);
1462 case SCLEX_LUA:
1463 return (style == SCE_LUA_LITERALSTRING ||
1464 style == SCE_LUA_CHARACTER ||
1465 style == SCE_LUA_STRINGEOL ||
1466 style == SCE_LUA_STRING);
1468 case SCLEX_HASKELL:
1469 return (style == SCE_HA_CHARACTER ||
1470 style == SCE_HA_STRING);
1472 case SCLEX_FREEBASIC:
1473 return (style == SCE_B_STRING ||
1474 style == SCE_B_STRINGEOL);
1476 case SCLEX_OCTAVE:
1477 return (style == SCE_MATLAB_STRING ||
1478 style == SCE_MATLAB_DOUBLEQUOTESTRING);
1480 case SCLEX_XML:
1481 case SCLEX_HTML:
1482 return (
1483 style == SCE_HBA_STRING ||
1484 style == SCE_HBA_STRINGEOL ||
1485 style == SCE_HB_STRING ||
1486 style == SCE_HB_STRINGEOL ||
1487 style == SCE_H_CDATA ||
1488 style == SCE_H_DOUBLESTRING ||
1489 style == SCE_HJA_DOUBLESTRING ||
1490 style == SCE_HJA_SINGLESTRING ||
1491 style == SCE_HJA_STRINGEOL ||
1492 style == SCE_HJ_DOUBLESTRING ||
1493 style == SCE_HJ_SINGLESTRING ||
1494 style == SCE_HJ_STRINGEOL ||
1495 style == SCE_HPA_CHARACTER ||
1496 style == SCE_HPA_STRING ||
1497 style == SCE_HPA_TRIPLE ||
1498 style == SCE_HPA_TRIPLEDOUBLE ||
1499 style == SCE_HP_CHARACTER ||
1500 style == SCE_HPHP_HSTRING || /* HSTRING is a heredoc */
1501 style == SCE_HPHP_HSTRING_VARIABLE ||
1502 style == SCE_HPHP_SIMPLESTRING ||
1503 style == SCE_HP_STRING ||
1504 style == SCE_HP_TRIPLE ||
1505 style == SCE_HP_TRIPLEDOUBLE ||
1506 style == SCE_H_SGML_DOUBLESTRING ||
1507 style == SCE_H_SGML_SIMPLESTRING ||
1508 style == SCE_H_SINGLESTRING);
1510 case SCLEX_CMAKE:
1511 return (style == SCE_CMAKE_STRINGDQ ||
1512 style == SCE_CMAKE_STRINGLQ ||
1513 style == SCE_CMAKE_STRINGRQ ||
1514 style == SCE_CMAKE_STRINGVAR);
1516 case SCLEX_NSIS:
1517 return (style == SCE_NSIS_STRINGDQ ||
1518 style == SCE_NSIS_STRINGLQ ||
1519 style == SCE_NSIS_STRINGRQ ||
1520 style == SCE_NSIS_STRINGVAR);
1522 case SCLEX_ADA:
1523 return (style == SCE_ADA_CHARACTER ||
1524 style == SCE_ADA_STRING ||
1525 style == SCE_ADA_CHARACTEREOL ||
1526 style == SCE_ADA_STRINGEOL);
1528 return FALSE;
1532 /** Checks whether the given style is a comment for the given lexer.
1534 * @param lexer Scintilla lexer type (@c SCLEX_*).
1535 * @param style Scintilla style (@c SCE_*).
1537 * @return @c TRUE if the style is a comment, @c FALSE otherwise.
1539 gboolean highlighting_is_comment_style(gint lexer, gint style)
1541 switch (lexer)
1543 case SCLEX_COBOL:
1544 case SCLEX_CPP:
1545 return (style == SCE_C_COMMENT ||
1546 style == SCE_C_COMMENTLINE ||
1547 style == SCE_C_COMMENTDOC ||
1548 style == SCE_C_COMMENTLINEDOC ||
1549 style == SCE_C_COMMENTDOCKEYWORD ||
1550 style == SCE_C_COMMENTDOCKEYWORDERROR);
1552 case SCLEX_PASCAL:
1553 return (style == SCE_PAS_COMMENT ||
1554 style == SCE_PAS_COMMENT2 ||
1555 style == SCE_PAS_COMMENTLINE);
1557 case SCLEX_D:
1558 return (style == SCE_D_COMMENT ||
1559 style == SCE_D_COMMENTLINE ||
1560 style == SCE_D_COMMENTDOC ||
1561 style == SCE_D_COMMENTNESTED ||
1562 style == SCE_D_COMMENTLINEDOC ||
1563 style == SCE_D_COMMENTDOCKEYWORD ||
1564 style == SCE_D_COMMENTDOCKEYWORDERROR);
1566 case SCLEX_PYTHON:
1567 return (style == SCE_P_COMMENTLINE ||
1568 style == SCE_P_COMMENTBLOCK);
1570 case SCLEX_F77:
1571 case SCLEX_FORTRAN:
1572 return (style == SCE_F_COMMENT);
1574 case SCLEX_PERL:
1575 return (style == SCE_PL_COMMENTLINE);
1577 case SCLEX_PROPERTIES:
1578 return (style == SCE_PROPS_COMMENT);
1580 case SCLEX_PO:
1581 return (style == SCE_PO_COMMENT);
1583 case SCLEX_LATEX:
1584 return (style == SCE_L_COMMENT ||
1585 style == SCE_L_COMMENT2);
1587 case SCLEX_MAKEFILE:
1588 return (style == SCE_MAKE_COMMENT);
1590 case SCLEX_RUBY:
1591 return (style == SCE_RB_COMMENTLINE);
1593 case SCLEX_BASH:
1594 return (style == SCE_SH_COMMENTLINE);
1596 case SCLEX_R:
1597 return (style == SCE_R_COMMENT);
1599 case SCLEX_SQL:
1600 return (style == SCE_SQL_COMMENT ||
1601 style == SCE_SQL_COMMENTLINE ||
1602 style == SCE_SQL_COMMENTDOC ||
1603 style == SCE_SQL_COMMENTLINEDOC ||
1604 style == SCE_SQL_COMMENTDOCKEYWORD ||
1605 style == SCE_SQL_COMMENTDOCKEYWORDERROR);
1607 case SCLEX_TCL:
1608 return (style == SCE_TCL_COMMENT ||
1609 style == SCE_TCL_COMMENTLINE ||
1610 style == SCE_TCL_COMMENT_BOX ||
1611 style == SCE_TCL_BLOCK_COMMENT);
1613 case SCLEX_OCTAVE:
1614 return (style == SCE_MATLAB_COMMENT);
1616 case SCLEX_LUA:
1617 return (style == SCE_LUA_COMMENT ||
1618 style == SCE_LUA_COMMENTLINE ||
1619 style == SCE_LUA_COMMENTDOC);
1621 case SCLEX_HASKELL:
1622 return (style == SCE_HA_COMMENTLINE ||
1623 style == SCE_HA_COMMENTBLOCK ||
1624 style == SCE_HA_COMMENTBLOCK2 ||
1625 style == SCE_HA_COMMENTBLOCK3);
1627 case SCLEX_FREEBASIC:
1628 return (style == SCE_B_COMMENT);
1630 case SCLEX_YAML:
1631 return (style == SCE_YAML_COMMENT);
1633 case SCLEX_XML:
1634 case SCLEX_HTML:
1635 return (
1636 style == SCE_HBA_COMMENTLINE ||
1637 style == SCE_HB_COMMENTLINE ||
1638 style == SCE_H_COMMENT ||
1639 style == SCE_HJA_COMMENT ||
1640 style == SCE_HJA_COMMENTDOC ||
1641 style == SCE_HJA_COMMENTLINE ||
1642 style == SCE_HJ_COMMENT ||
1643 style == SCE_HJ_COMMENTDOC ||
1644 style == SCE_HJ_COMMENTLINE ||
1645 style == SCE_HPA_COMMENTLINE ||
1646 style == SCE_HP_COMMENTLINE ||
1647 style == SCE_HPHP_COMMENT ||
1648 style == SCE_HPHP_COMMENTLINE ||
1649 style == SCE_H_SGML_COMMENT);
1651 case SCLEX_CMAKE:
1652 return (style == SCE_CMAKE_COMMENT);
1654 case SCLEX_NSIS:
1655 return (style == SCE_NSIS_COMMENT ||
1656 style == SCE_NSIS_COMMENTBOX);
1658 case SCLEX_ADA:
1659 return (style == SCE_ADA_COMMENTLINE ||
1660 style == SCE_NSIS_COMMENTBOX);
1662 case SCLEX_ASM:
1663 return (style == SCE_ASM_COMMENT ||
1664 style == SCE_ASM_COMMENTBLOCK ||
1665 style == SCE_ASM_COMMENTDIRECTIVE);
1667 return FALSE;
1671 /** Checks whether the given style is normal code (not string, comment, preprocessor, etc).
1673 * @param lexer Scintilla lexer type (@c SCLEX_*).
1674 * @param style Scintilla style (@c SCE_*).
1676 * @return @c TRUE if the style is code, @c FALSE otherwise.
1678 gboolean highlighting_is_code_style(gint lexer, gint style)
1680 switch (lexer)
1682 case SCLEX_CPP:
1683 if (style == SCE_C_PREPROCESSOR)
1684 return FALSE;
1685 break;
1687 return !(highlighting_is_comment_style(lexer, style) ||
1688 highlighting_is_string_style(lexer, style));