Improve handling of named styles and named colors
[geany-mirror.git] / src / highlighting.c
blobfebc493302a9c8a5c200e83cb1961d920f7ba6a2
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"
45 #include "highlightingmappings.h"
48 #define GEANY_COLORSCHEMES_SUBDIR "colorschemes"
50 /* Whitespace has to be set after setting wordchars. */
51 #define GEANY_WHITESPACE_CHARS " \t" "!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~"
54 static gchar *whitespace_chars;
57 typedef struct
59 gsize count; /* number of styles */
60 GeanyLexerStyle *styling; /* array of styles, NULL if not used or uninitialised */
61 gchar **keywords;
62 gchar *wordchars; /* NULL used for style sets with no styles */
63 gchar **property_keys;
64 gchar **property_values;
65 } StyleSet;
67 /* each filetype has a styleset but GEANY_FILETYPES_NONE uses common_style_set for styling */
68 static StyleSet *style_sets = NULL;
71 enum /* Geany common styling */
73 GCS_DEFAULT,
74 GCS_SELECTION,
75 GCS_BRACE_GOOD,
76 GCS_BRACE_BAD,
77 GCS_MARGIN_LINENUMBER,
78 GCS_MARGIN_FOLDING,
79 GCS_FOLD_SYMBOL_HIGHLIGHT,
80 GCS_CURRENT_LINE,
81 GCS_CARET,
82 GCS_INDENT_GUIDE,
83 GCS_WHITE_SPACE,
84 GCS_LINE_WRAP_VISUALS,
85 GCS_LINE_WRAP_INDENT,
86 GCS_TRANSLUCENCY,
87 GCS_MARKER_LINE,
88 GCS_MARKER_SEARCH,
89 GCS_MARKER_MARK,
90 GCS_MARKER_TRANSLUCENCY,
91 GCS_LINE_HEIGHT,
92 GCS_CALLTIPS,
93 GCS_MAX
96 static struct
98 GeanyLexerStyle styling[GCS_MAX];
100 /* icon style, 1-4 */
101 gint fold_marker;
102 /* vertical line style, 0-2 */
103 gint fold_lines;
104 /* horizontal line when folded, 0-2 */
105 gint fold_draw_line;
107 gchar *wordchars;
108 } common_style_set;
111 /* For filetypes.common [named_styles] section.
112 * 0xBBGGRR format.
113 * e.g. "comment" => &GeanyLexerStyle{0x0000d0, 0xffffff, FALSE, FALSE} */
114 static GHashTable *named_style_hash = NULL;
116 /* 0xBBGGRR format, set by "default" named style. */
117 static GeanyLexerStyle gsd_default = {0x000000, 0xffffff, FALSE, FALSE};
120 /* Note: use sciwrappers.h instead where possible.
121 * Do not use SSM in files unrelated to scintilla. */
122 #define SSM(s, m, w, l) scintilla_send_message(s, m, w, l)
124 /* filetypes should use the filetypes.foo [lexer_properties] group instead of hardcoding */
125 static void sci_set_property(ScintillaObject *sci, const gchar *name, const gchar *value)
127 SSM(sci, SCI_SETPROPERTY, (uptr_t) name, (sptr_t) value);
131 static void new_styleset(guint file_type_id, gsize styling_count)
133 StyleSet *set = &style_sets[file_type_id];
135 set->count = styling_count;
136 set->styling = g_new0(GeanyLexerStyle, styling_count);
140 static void free_styleset(guint file_type_id)
142 StyleSet *style_ptr;
143 style_ptr = &style_sets[file_type_id];
145 style_ptr->count = 0;
146 g_free(style_ptr->styling);
147 style_ptr->styling = NULL;
148 g_strfreev(style_ptr->keywords);
149 style_ptr->keywords = NULL;
150 g_free(style_ptr->wordchars);
151 style_ptr->wordchars = NULL;
152 g_strfreev(style_ptr->property_keys);
153 style_ptr->property_keys = NULL;
154 g_strfreev(style_ptr->property_values);
155 style_ptr->property_values = NULL;
159 static void get_keyfile_keywords(GKeyFile *config, GKeyFile *configh,
160 const gchar *key, guint ft_id, guint pos)
162 style_sets[ft_id].keywords[pos] =
163 utils_get_setting(string, configh, config, "keywords", key, "");
167 static void get_keyfile_wordchars(GKeyFile *config, GKeyFile *configh, gchar **wordchars)
169 *wordchars = utils_get_setting(string, configh, config,
170 "settings", "wordchars", GEANY_WORDCHARS);
174 static gboolean read_named_style(const gchar *named_style, GeanyLexerStyle *style)
176 GeanyLexerStyle *cs;
177 gchar *comma, *name = NULL;
178 const gchar *bold = NULL;
179 const gchar *italic = NULL;
181 g_return_val_if_fail(named_style, FALSE);
182 name = utils_strdupa(named_style); /* named_style must not be written to, may be a static string */
184 comma = strstr(name, ",");
185 if (comma)
187 bold = strstr(comma, ",bold");
188 italic = strstr(comma, ",italic");
189 *comma = '\0'; /* terminate name to make lookup work */
191 cs = g_hash_table_lookup(named_style_hash, name);
193 if (cs)
195 *style = *cs;
196 if (bold)
197 style->bold = !style->bold;
198 if (italic)
199 style->italic = !style->italic;
201 else
203 *style = gsd_default;
205 return (cs != NULL);
209 /* Parses a color in `str` which can be an HTML color (ex. #0099cc),
210 * an abbreviated HTML color (ex. #09c) or a hex string color
211 * (ex. 0x0099cc). The result of the conversion is stored into the
212 * location pointed to by `clr`. */
213 static void parse_color(GKeyFile *kf, const gchar *str, gint *clr)
215 gint c;
216 gchar hex_clr[9] = { 0 };
217 gchar *named_color = NULL;
218 const gchar *start;
220 g_return_if_fail(clr != NULL);
222 if (G_UNLIKELY(! NZV(str)))
223 return;
225 named_color = g_key_file_get_string(kf, "named_colors", str, NULL);
226 if (named_color)
227 str = named_color;
229 if (str[0] == '#')
230 start = str + 1;
231 else if (strlen(str) > 1 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))
232 start = str + 2;
233 else
235 geany_debug("Bad color '%s'", str);
236 g_free(named_color);
237 return;
240 if (strlen(start) == 3)
242 snprintf(hex_clr, 9, "0x%c%c%c%c%c%c", start[0], start[0],
243 start[1], start[1], start[2], start[2]);
245 else
246 snprintf(hex_clr, 9, "0x%s", start);
248 g_free(named_color);
250 c = utils_strtod(hex_clr, NULL, FALSE);
252 if (c > -1)
254 *clr = c;
255 return;
257 geany_debug("Bad color '%s'", str);
261 static void parse_keyfile_style(GKeyFile *kf, gchar **list,
262 const GeanyLexerStyle *default_style, GeanyLexerStyle *style)
264 gsize len;
266 g_return_if_fail(default_style);
267 g_return_if_fail(style);
269 *style = *default_style;
271 if (!list)
272 return;
274 len = g_strv_length(list);
275 if (len == 0)
276 return;
277 else if (len == 1)
279 gchar **items = g_strsplit(list[0], ",", 0);
280 if (items != NULL)
282 if (g_strv_length(items) > 0)
284 if (g_hash_table_lookup(named_style_hash, items[0]) != NULL)
286 if (!read_named_style(list[0], style))
287 geany_debug("Unable to read named style '%s'", items[0]);
288 g_strfreev(items);
289 return;
291 else if (strchr(list[0], ',') != NULL)
293 geany_debug("Unknown named style '%s'", items[0]);
294 g_strfreev(items);
295 return;
298 g_strfreev(items);
302 switch (len)
304 case 4:
305 style->italic = utils_atob(list[3]);
306 case 3:
307 style->bold = utils_atob(list[2]);
308 case 2:
309 parse_color(kf, list[1], &style->background);
310 case 1:
311 parse_color(kf, list[0], &style->foreground);
316 static void get_keyfile_style(GKeyFile *config, GKeyFile *configh,
317 const gchar *key_name, GeanyLexerStyle *style)
319 gchar **list;
320 gsize len;
322 g_return_if_fail(config);
323 g_return_if_fail(configh);
324 g_return_if_fail(key_name);
325 g_return_if_fail(style);
327 list = g_key_file_get_string_list(configh, "styling", key_name, &len, NULL);
328 if (list == NULL)
330 list = g_key_file_get_string_list(config, "styling", key_name, &len, NULL);
331 parse_keyfile_style(config, list, &gsd_default, style);
333 else
334 parse_keyfile_style(configh, list, &gsd_default, style);
336 g_strfreev(list);
340 /* Convert 0xRRGGBB to 0xBBGGRR, which scintilla expects. */
341 static gint rotate_rgb(gint color)
343 return ((color & 0xFF0000) >> 16) +
344 (color & 0x00FF00) +
345 ((color & 0x0000FF) << 16);
349 static void convert_int(const gchar *int_str, gint *val)
351 gchar *end;
352 gint v = strtol(int_str, &end, 10);
354 if (int_str != end)
355 *val = v;
359 /* Get first and second integer numbers, store in foreground and background fields of @a style. */
360 static void get_keyfile_int(GKeyFile *config, GKeyFile *configh, const gchar *section,
361 const gchar *key, gint fdefault_val, gint sdefault_val,
362 GeanyLexerStyle *style)
364 gchar **list;
365 gsize len;
366 GeanyLexerStyle def = {fdefault_val, sdefault_val, FALSE, FALSE};
368 g_return_if_fail(config);
369 g_return_if_fail(configh);
370 g_return_if_fail(section);
371 g_return_if_fail(key);
373 list = g_key_file_get_string_list(configh, section, key, &len, NULL);
374 if (list == NULL)
375 list = g_key_file_get_string_list(config, section, key, &len, NULL);
377 *style = def;
378 if (!list)
379 return;
381 if (list[0])
383 convert_int(list[0], &style->foreground);
384 if (list[1])
386 convert_int(list[1], &style->background);
389 g_strfreev(list);
393 /* first or second can be NULL. */
394 static void get_keyfile_ints(GKeyFile *config, GKeyFile *configh, const gchar *section,
395 const gchar *key,
396 gint fdefault_val, gint sdefault_val,
397 gint *first, gint *second)
399 GeanyLexerStyle tmp_style;
401 get_keyfile_int(config, configh, section, key, fdefault_val, sdefault_val, &tmp_style);
402 if (first)
403 *first = tmp_style.foreground;
404 if (second)
405 *second = tmp_style.background;
409 static guint invert(guint icolour)
411 if (interface_prefs.highlighting_invert_all)
412 return utils_invert_color(icolour);
414 return icolour;
418 static GeanyLexerStyle *get_style(guint ft_id, guint styling_index)
420 g_assert(ft_id < filetypes_array->len);
422 if (G_UNLIKELY(ft_id == GEANY_FILETYPES_NONE))
424 g_assert(styling_index < GCS_MAX);
425 return &common_style_set.styling[styling_index];
427 else
429 StyleSet *set = &style_sets[ft_id];
431 g_assert(styling_index < set->count);
432 return &set->styling[styling_index];
437 static void set_sci_style(ScintillaObject *sci, guint style, guint ft_id, guint styling_index)
439 GeanyLexerStyle *style_ptr = get_style(ft_id, styling_index);
441 SSM(sci, SCI_STYLESETFORE, style, invert(style_ptr->foreground));
442 SSM(sci, SCI_STYLESETBACK, style, invert(style_ptr->background));
443 SSM(sci, SCI_STYLESETBOLD, style, style_ptr->bold);
444 SSM(sci, SCI_STYLESETITALIC, style, style_ptr->italic);
448 void highlighting_free_styles()
450 guint i;
452 for (i = 0; i < filetypes_array->len; i++)
453 free_styleset(i);
455 if (named_style_hash)
456 g_hash_table_destroy(named_style_hash);
458 g_free(style_sets);
462 static GString *get_global_typenames(gint lang)
464 GString *s = NULL;
466 if (app->tm_workspace)
468 GPtrArray *tags_array = app->tm_workspace->global_tags;
470 if (tags_array)
472 s = symbols_find_tags_as_string(tags_array, TM_GLOBAL_TYPE_MASK, lang);
475 return s;
479 static gchar*
480 get_keyfile_whitespace_chars(GKeyFile *config, GKeyFile *configh)
482 return utils_get_setting(string, configh, config,
483 "settings", "whitespace_chars", GEANY_WHITESPACE_CHARS);
487 static void add_named_style(GKeyFile *config, const gchar *key)
489 const gchar group[] = "named_styles";
490 gchar **list;
491 gsize len;
493 list = g_key_file_get_string_list(config, group, key, &len, NULL);
494 /* we allow a named style to reference another style above it */
495 if (list && len >= 1)
497 GeanyLexerStyle *style = g_new0(GeanyLexerStyle, 1);
499 parse_keyfile_style(config, list, &gsd_default, style);
500 g_hash_table_insert(named_style_hash, g_strdup(key), style);
502 g_strfreev(list);
506 static void get_named_styles(GKeyFile *config)
508 const gchar group[] = "named_styles";
509 gchar **keys = g_key_file_get_keys(config, group, NULL, NULL);
510 gchar **ptr = keys;
512 if (!ptr)
513 return;
515 while (1)
517 const gchar *key = *ptr;
519 if (!key)
520 break;
522 /* don't replace already read default style with system one */
523 if (!g_str_equal(key, "default"))
524 add_named_style(config, key);
526 ptr++;
528 g_strfreev(keys);
532 static GKeyFile *utils_key_file_new(const gchar *filename)
534 GKeyFile *config = g_key_file_new();
536 g_key_file_load_from_file(config, filename, G_KEY_FILE_KEEP_COMMENTS, NULL);
537 return config;
541 static void load_named_styles(GKeyFile *config, GKeyFile *config_home)
543 const gchar *scheme = editor_prefs.color_scheme;
544 gboolean free_kf = FALSE;
546 if (named_style_hash)
547 g_hash_table_destroy(named_style_hash); /* reloading */
549 named_style_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
551 if (NZV(scheme))
553 gchar *path, *path_home;
555 path = g_build_path(G_DIR_SEPARATOR_S, app->datadir, GEANY_COLORSCHEMES_SUBDIR, scheme, NULL);
556 path_home = g_build_path(G_DIR_SEPARATOR_S, app->configdir, GEANY_COLORSCHEMES_SUBDIR, scheme, NULL);
558 if (g_file_test(path, G_FILE_TEST_EXISTS) || g_file_test(path_home, G_FILE_TEST_EXISTS))
560 config = utils_key_file_new(path);
561 config_home = utils_key_file_new(path_home);
562 free_kf = TRUE;
564 /* if color scheme is missing, use default */
565 g_free(path);
566 g_free(path_home);
568 /* first set default to the "default" named style */
569 add_named_style(config, "default");
570 read_named_style("default", &gsd_default); /* in case user overrides but not with both colors */
571 add_named_style(config_home, "default");
572 read_named_style("default", &gsd_default);
574 get_named_styles(config);
575 /* home overrides any system named style */
576 get_named_styles(config_home);
578 if (free_kf)
580 g_key_file_free(config);
581 g_key_file_free(config_home);
586 static void styleset_common_init(guint ft_id, GKeyFile *config, GKeyFile *config_home)
588 load_named_styles(config, config_home);
590 get_keyfile_style(config, config_home, "default", &common_style_set.styling[GCS_DEFAULT]);
591 get_keyfile_style(config, config_home, "selection", &common_style_set.styling[GCS_SELECTION]);
592 get_keyfile_style(config, config_home, "brace_good", &common_style_set.styling[GCS_BRACE_GOOD]);
593 get_keyfile_style(config, config_home, "brace_bad", &common_style_set.styling[GCS_BRACE_BAD]);
594 get_keyfile_style(config, config_home, "margin_linenumber", &common_style_set.styling[GCS_MARGIN_LINENUMBER]);
595 get_keyfile_style(config, config_home, "margin_folding", &common_style_set.styling[GCS_MARGIN_FOLDING]);
596 get_keyfile_style(config, config_home, "fold_symbol_highlight", &common_style_set.styling[GCS_FOLD_SYMBOL_HIGHLIGHT]);
597 get_keyfile_style(config, config_home, "current_line", &common_style_set.styling[GCS_CURRENT_LINE]);
598 get_keyfile_style(config, config_home, "caret", &common_style_set.styling[GCS_CARET]);
599 get_keyfile_style(config, config_home, "indent_guide", &common_style_set.styling[GCS_INDENT_GUIDE]);
600 get_keyfile_style(config, config_home, "white_space", &common_style_set.styling[GCS_WHITE_SPACE]);
601 get_keyfile_style(config, config_home, "marker_line", &common_style_set.styling[GCS_MARKER_LINE]);
602 get_keyfile_style(config, config_home, "marker_search", &common_style_set.styling[GCS_MARKER_SEARCH]);
603 get_keyfile_style(config, config_home, "marker_mark", &common_style_set.styling[GCS_MARKER_MARK]);
604 get_keyfile_style(config, config_home, "calltips", &common_style_set.styling[GCS_CALLTIPS]);
606 get_keyfile_ints(config, config_home, "styling", "folding_style",
607 1, 1, &common_style_set.fold_marker, &common_style_set.fold_lines);
608 get_keyfile_ints(config, config_home, "styling", "folding_horiz_line",
609 2, 0, &common_style_set.fold_draw_line, NULL);
610 get_keyfile_ints(config, config_home, "styling", "caret_width",
611 1, 0, &common_style_set.styling[GCS_CARET].background, NULL); /* caret.foreground used earlier */
612 get_keyfile_int(config, config_home, "styling", "line_wrap_visuals",
613 3, 0, &common_style_set.styling[GCS_LINE_WRAP_VISUALS]);
614 get_keyfile_int(config, config_home, "styling", "line_wrap_indent",
615 0, 0, &common_style_set.styling[GCS_LINE_WRAP_INDENT]);
616 get_keyfile_int(config, config_home, "styling", "translucency",
617 256, 256, &common_style_set.styling[GCS_TRANSLUCENCY]);
618 get_keyfile_int(config, config_home, "styling", "marker_translucency",
619 256, 256, &common_style_set.styling[GCS_MARKER_TRANSLUCENCY]);
620 get_keyfile_int(config, config_home, "styling", "line_height",
621 0, 0, &common_style_set.styling[GCS_LINE_HEIGHT]);
623 get_keyfile_wordchars(config, config_home, &common_style_set.wordchars);
624 whitespace_chars = get_keyfile_whitespace_chars(config, config_home);
628 static void styleset_common(ScintillaObject *sci, guint ft_id)
630 SSM(sci, SCI_STYLECLEARALL, 0, 0);
632 SSM(sci, SCI_SETWORDCHARS, 0, (sptr_t) (ft_id == GEANY_FILETYPES_NONE ?
633 common_style_set.wordchars : style_sets[ft_id].wordchars));
634 /* have to set whitespace after setting wordchars */
635 SSM(sci, SCI_SETWHITESPACECHARS, 0, (sptr_t) whitespace_chars);
637 /* caret colour, style and width */
638 SSM(sci, SCI_SETCARETFORE, invert(common_style_set.styling[GCS_CARET].foreground), 0);
639 SSM(sci, SCI_SETCARETWIDTH, common_style_set.styling[GCS_CARET].background, 0);
640 if (common_style_set.styling[GCS_CARET].bold)
641 SSM(sci, SCI_SETCARETSTYLE, CARETSTYLE_BLOCK, 0);
642 else
643 SSM(sci, SCI_SETCARETSTYLE, CARETSTYLE_LINE, 0);
645 /* line height */
646 SSM(sci, SCI_SETEXTRAASCENT, common_style_set.styling[GCS_LINE_HEIGHT].foreground, 0);
647 SSM(sci, SCI_SETEXTRADESCENT, common_style_set.styling[GCS_LINE_HEIGHT].background, 0);
649 /* colourise the current line */
650 SSM(sci, SCI_SETCARETLINEBACK, invert(common_style_set.styling[GCS_CURRENT_LINE].background), 0);
651 /* bold=enable current line */
652 SSM(sci, SCI_SETCARETLINEVISIBLE, common_style_set.styling[GCS_CURRENT_LINE].bold, 0);
654 /* Translucency for current line and selection */
655 SSM(sci, SCI_SETCARETLINEBACKALPHA, common_style_set.styling[GCS_TRANSLUCENCY].foreground, 0);
656 SSM(sci, SCI_SETSELALPHA, common_style_set.styling[GCS_TRANSLUCENCY].background, 0);
658 /* line wrapping visuals */
659 SSM(sci, SCI_SETWRAPVISUALFLAGS,
660 common_style_set.styling[GCS_LINE_WRAP_VISUALS].foreground, 0);
661 SSM(sci, SCI_SETWRAPVISUALFLAGSLOCATION,
662 common_style_set.styling[GCS_LINE_WRAP_VISUALS].background, 0);
663 SSM(sci, SCI_SETWRAPSTARTINDENT, common_style_set.styling[GCS_LINE_WRAP_INDENT].foreground, 0);
664 SSM(sci, SCI_SETWRAPINDENTMODE, common_style_set.styling[GCS_LINE_WRAP_INDENT].background, 0);
666 /* Error indicator */
667 SSM(sci, SCI_INDICSETSTYLE, GEANY_INDICATOR_ERROR, INDIC_SQUIGGLE);
668 SSM(sci, SCI_INDICSETFORE, GEANY_INDICATOR_ERROR, invert(rotate_rgb(0xff0000)));
670 /* Search indicator, used for 'Mark' matches */
671 SSM(sci, SCI_INDICSETSTYLE, GEANY_INDICATOR_SEARCH, INDIC_ROUNDBOX);
672 SSM(sci, SCI_INDICSETFORE, GEANY_INDICATOR_SEARCH,
673 invert(common_style_set.styling[GCS_MARKER_SEARCH].background));
674 SSM(sci, SCI_INDICSETALPHA, GEANY_INDICATOR_SEARCH, 60);
676 /* define marker symbols
677 * 0 -> line marker */
678 SSM(sci, SCI_MARKERDEFINE, 0, SC_MARK_SHORTARROW);
679 SSM(sci, SCI_MARKERSETFORE, 0, invert(common_style_set.styling[GCS_MARKER_LINE].foreground));
680 SSM(sci, SCI_MARKERSETBACK, 0, invert(common_style_set.styling[GCS_MARKER_LINE].background));
681 SSM(sci, SCI_MARKERSETALPHA, 0, common_style_set.styling[GCS_MARKER_TRANSLUCENCY].foreground);
683 /* 1 -> user marker */
684 SSM(sci, SCI_MARKERDEFINE, 1, SC_MARK_PLUS);
685 SSM(sci, SCI_MARKERSETFORE, 1, invert(common_style_set.styling[GCS_MARKER_MARK].foreground));
686 SSM(sci, SCI_MARKERSETBACK, 1, invert(common_style_set.styling[GCS_MARKER_MARK].background));
687 SSM(sci, SCI_MARKERSETALPHA, 1, common_style_set.styling[GCS_MARKER_TRANSLUCENCY].background);
689 /* 2 -> folding marker, other folding settings */
690 SSM(sci, SCI_SETMARGINTYPEN, 2, SC_MARGIN_SYMBOL);
691 SSM(sci, SCI_SETMARGINMASKN, 2, SC_MASK_FOLDERS);
693 /* drawing a horizontal line when text if folded */
694 switch (common_style_set.fold_draw_line)
696 case 1:
698 SSM(sci, SCI_SETFOLDFLAGS, 4, 0);
699 break;
701 case 2:
703 SSM(sci, SCI_SETFOLDFLAGS, 16, 0);
704 break;
706 default:
708 SSM(sci, SCI_SETFOLDFLAGS, 0, 0);
709 break;
713 /* choose the folding style - boxes or circles, I prefer boxes, so it is default ;-) */
714 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEREND, SC_MARK_EMPTY);
715 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPENMID, SC_MARK_EMPTY);
716 switch (common_style_set.fold_marker)
718 case 2:
719 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPEN, SC_MARK_CIRCLEMINUS);
720 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDER, SC_MARK_CIRCLEPLUS);
721 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEREND, SC_MARK_CIRCLEPLUSCONNECTED);
722 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPENMID, SC_MARK_CIRCLEMINUSCONNECTED);
723 break;
724 default:
725 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPEN, SC_MARK_BOXMINUS);
726 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDER, SC_MARK_BOXPLUS);
727 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEREND, SC_MARK_BOXPLUSCONNECTED);
728 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPENMID, SC_MARK_BOXMINUSCONNECTED);
729 break;
730 case 3:
731 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPEN, SC_MARK_ARROWDOWN);
732 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDER, SC_MARK_ARROW);
733 break;
734 case 4:
735 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPEN, SC_MARK_MINUS);
736 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDER, SC_MARK_PLUS);
737 break;
740 /* choose the folding style - straight or curved, I prefer straight, so it is default ;-) */
741 switch (common_style_set.fold_lines)
743 case 2:
744 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDERMIDTAIL, SC_MARK_TCORNERCURVE);
745 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDERTAIL, SC_MARK_LCORNERCURVE);
746 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDERSUB, SC_MARK_VLINE);
747 break;
748 default:
749 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDERMIDTAIL, SC_MARK_TCORNER);
750 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDERTAIL, SC_MARK_LCORNER);
751 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDERSUB, SC_MARK_VLINE);
752 break;
753 case 0:
754 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDERMIDTAIL, SC_MARK_EMPTY);
755 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDERTAIL, SC_MARK_EMPTY);
756 SSM(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDERSUB, SC_MARK_EMPTY);
757 break;
760 gint markers[] = {
761 SC_MARKNUM_FOLDEROPEN,
762 SC_MARKNUM_FOLDER,
763 SC_MARKNUM_FOLDERSUB,
764 SC_MARKNUM_FOLDERTAIL,
765 SC_MARKNUM_FOLDEREND,
766 SC_MARKNUM_FOLDEROPENMID,
767 SC_MARKNUM_FOLDERMIDTAIL
769 guint i;
771 foreach_range(i, G_N_ELEMENTS(markers))
773 SSM(sci, SCI_MARKERSETFORE, markers[i],
774 invert(common_style_set.styling[GCS_FOLD_SYMBOL_HIGHLIGHT].foreground));
775 SSM(sci, SCI_MARKERSETBACK, markers[i],
776 invert(common_style_set.styling[GCS_MARGIN_FOLDING].foreground));
780 /* set some common defaults */
781 sci_set_property(sci, "fold", "1");
782 sci_set_property(sci, "fold.compact", "0");
783 sci_set_property(sci, "fold.comment", "1");
784 sci_set_property(sci, "fold.preprocessor", "1");
785 sci_set_property(sci, "fold.at.else", "1");
787 /* bold (3rd argument) is whether to override default foreground selection */
788 if (common_style_set.styling[GCS_SELECTION].bold)
789 SSM(sci, SCI_SETSELFORE, 1, invert(common_style_set.styling[GCS_SELECTION].foreground));
790 /* italic (4th argument) is whether to override default background selection */
791 if (common_style_set.styling[GCS_SELECTION].italic)
792 SSM(sci, SCI_SETSELBACK, 1, invert(common_style_set.styling[GCS_SELECTION].background));
794 SSM(sci, SCI_SETSTYLEBITS, SSM(sci, SCI_GETSTYLEBITSNEEDED, 0, 0), 0);
796 SSM(sci, SCI_SETFOLDMARGINCOLOUR, 1, invert(common_style_set.styling[GCS_MARGIN_FOLDING].background));
797 SSM(sci, SCI_SETFOLDMARGINHICOLOUR, 1, invert(common_style_set.styling[GCS_MARGIN_FOLDING].background));
798 set_sci_style(sci, STYLE_LINENUMBER, GEANY_FILETYPES_NONE, GCS_MARGIN_LINENUMBER);
799 set_sci_style(sci, STYLE_BRACELIGHT, GEANY_FILETYPES_NONE, GCS_BRACE_GOOD);
800 set_sci_style(sci, STYLE_BRACEBAD, GEANY_FILETYPES_NONE, GCS_BRACE_BAD);
801 set_sci_style(sci, STYLE_INDENTGUIDE, GEANY_FILETYPES_NONE, GCS_INDENT_GUIDE);
803 /* bold = common whitespace settings enabled */
804 SSM(sci, SCI_SETWHITESPACEFORE, common_style_set.styling[GCS_WHITE_SPACE].bold,
805 invert(common_style_set.styling[GCS_WHITE_SPACE].foreground));
806 SSM(sci, SCI_SETWHITESPACEBACK, common_style_set.styling[GCS_WHITE_SPACE].italic,
807 invert(common_style_set.styling[GCS_WHITE_SPACE].background));
809 if (common_style_set.styling[GCS_CALLTIPS].bold)
810 SSM(sci, SCI_CALLTIPSETFORE, invert(common_style_set.styling[GCS_CALLTIPS].foreground), 1);
811 if (common_style_set.styling[GCS_CALLTIPS].italic)
812 SSM(sci, SCI_CALLTIPSETBACK, invert(common_style_set.styling[GCS_CALLTIPS].background), 1);
816 /* Merge & assign global typedefs and user secondary keywords.
817 * keyword_idx is used for both style_sets[].keywords and scintilla keyword style number */
818 static void merge_type_keywords(ScintillaObject *sci, guint ft_id, guint keyword_idx)
820 const gchar *user_words = style_sets[ft_id].keywords[keyword_idx];
821 GString *s;
823 s = get_global_typenames(filetypes[ft_id]->lang);
824 if (G_UNLIKELY(s == NULL))
825 s = g_string_sized_new(200);
826 else
827 g_string_append_c(s, ' '); /* append a space as delimiter to the existing list of words */
829 g_string_append(s, user_words);
831 sci_set_keywords(sci, keyword_idx, s->str);
832 g_string_free(s, TRUE);
836 static void styleset_init_from_mapping(guint ft_id, GKeyFile *config, GKeyFile *config_home,
837 const HLStyle *styles, gsize n_styles,
838 const HLKeyword *keywords, gsize n_keywords)
840 gsize i;
842 /* styles */
843 new_styleset(ft_id, n_styles);
844 foreach_range(i, n_styles)
846 GeanyLexerStyle *style = &style_sets[ft_id].styling[i];
848 get_keyfile_style(config, config_home, styles[i].name, style);
851 /* keywords */
852 if (n_keywords < 1)
853 style_sets[ft_id].keywords = NULL;
854 else
856 style_sets[ft_id].keywords = g_new(gchar*, n_keywords + 1);
857 foreach_range(i, n_keywords)
858 get_keyfile_keywords(config, config_home, keywords[i].key, ft_id, i);
859 style_sets[ft_id].keywords[i] = NULL;
864 /* STYLE_DEFAULT will be set to match the first style. */
865 static void styleset_from_mapping(ScintillaObject *sci, guint ft_id, guint lexer,
866 const HLStyle *styles, gsize n_styles,
867 const HLKeyword *keywords, gsize n_keywords,
868 const HLProperty *properties, gsize n_properties)
870 gsize i;
872 g_assert(ft_id != GEANY_FILETYPES_NONE);
874 /* lexer */
875 SSM(sci, SCI_SETLEXER, lexer, 0);
877 /* styles */
878 styleset_common(sci, ft_id);
879 if (n_styles > 0)
881 /* first style is also default one */
882 set_sci_style(sci, STYLE_DEFAULT, ft_id, 0);
883 foreach_range(i, n_styles)
885 if (styles[i].fill_eol)
886 SSM(sci, SCI_STYLESETEOLFILLED, styles[i].style, TRUE);
887 set_sci_style(sci, styles[i].style, ft_id, i);
891 /* keywords */
892 foreach_range(i, n_keywords)
894 if (keywords[i].merge)
895 merge_type_keywords(sci, ft_id, i);
896 else
897 sci_set_keywords(sci, keywords[i].id, style_sets[ft_id].keywords[i]);
900 /* properties */
901 foreach_range(i, n_properties)
902 sci_set_property(sci, properties[i].property, properties[i].value);
907 static void styleset_default(ScintillaObject *sci, guint ft_id)
909 SSM(sci, SCI_SETLEXER, SCLEX_NULL, 0);
911 /* we need to set STYLE_DEFAULT before we call SCI_STYLECLEARALL in styleset_common() */
912 set_sci_style(sci, STYLE_DEFAULT, GEANY_FILETYPES_NONE, GCS_DEFAULT);
914 styleset_common(sci, ft_id);
918 static void get_key_values(GKeyFile *config, const gchar *group, gchar **keys, gchar **values)
920 while (*keys)
922 gchar *str = g_key_file_get_string(config, group, *keys, NULL);
924 if (str)
925 setptr(*values, str);
927 keys++;
928 values++;
933 static void read_properties(GeanyFiletype *ft, GKeyFile *config, GKeyFile *configh)
935 gchar group[] = "lexer_properties";
936 gchar **keys;
937 gchar **keysh = g_key_file_get_keys(configh, group, NULL, NULL);
938 gchar **ptr;
940 /* remove overridden keys from system keyfile */
941 foreach_strv(ptr, keysh)
942 g_key_file_remove_key(config, group, *ptr, NULL);
944 /* merge sys and user keys */
945 keys = g_key_file_get_keys(config, group, NULL, NULL);
946 keys = utils_strv_join(keys, keysh);
948 if (keys)
950 gchar **values = g_new0(gchar*, g_strv_length(keys) + 1);
952 style_sets[ft->id].property_keys = keys;
953 style_sets[ft->id].property_values = values;
955 get_key_values(config, group, keys, values);
956 get_key_values(configh, group, keys, values);
961 static guint get_lexer_filetype(GeanyFiletype *ft)
963 ft = NVL(ft->lexer_filetype, ft);
964 return ft->id;
968 #define init_styleset_case(LANG_NAME) \
969 case (GEANY_FILETYPES_##LANG_NAME): \
970 styleset_init_from_mapping(filetype_idx, config, configh, \
971 highlighting_styles_##LANG_NAME, \
972 HL_N_ENTRIES(highlighting_styles_##LANG_NAME), \
973 highlighting_keywords_##LANG_NAME, \
974 HL_N_ENTRIES(highlighting_keywords_##LANG_NAME)); \
975 break
977 /* Called by filetypes_load_config(). */
978 void highlighting_init_styles(guint filetype_idx, GKeyFile *config, GKeyFile *configh)
980 GeanyFiletype *ft = filetypes[filetype_idx];
981 guint lexer_id = get_lexer_filetype(ft);
983 if (!style_sets)
984 style_sets = g_new0(StyleSet, filetypes_array->len);
986 /* Clear old information if necessary - e.g. when reloading config */
987 free_styleset(filetype_idx);
989 read_properties(ft, config, configh);
991 /* None filetype handled specially */
992 if (filetype_idx == GEANY_FILETYPES_NONE)
994 styleset_common_init(GEANY_FILETYPES_NONE, config, configh);
995 return;
997 /* All stylesets depend on filetypes.common */
998 filetypes_load_config(GEANY_FILETYPES_NONE, FALSE);
1000 switch (lexer_id)
1002 init_styleset_case(ADA);
1003 init_styleset_case(ASM);
1004 init_styleset_case(BASIC);
1005 init_styleset_case(C);
1006 init_styleset_case(CAML);
1007 init_styleset_case(CMAKE);
1008 init_styleset_case(COBOL);
1009 init_styleset_case(CONF);
1010 init_styleset_case(CSS);
1011 init_styleset_case(D);
1012 init_styleset_case(DIFF);
1013 init_styleset_case(LISP);
1014 init_styleset_case(ERLANG);
1015 init_styleset_case(DOCBOOK);
1016 init_styleset_case(FERITE);
1017 init_styleset_case(F77);
1018 init_styleset_case(FORTH);
1019 init_styleset_case(FORTRAN);
1020 init_styleset_case(HASKELL);
1021 init_styleset_case(HAXE);
1022 init_styleset_case(AS);
1023 init_styleset_case(HTML);
1024 init_styleset_case(JAVA);
1025 init_styleset_case(JS);
1026 init_styleset_case(LATEX);
1027 init_styleset_case(LUA);
1028 init_styleset_case(MAKE);
1029 init_styleset_case(MATLAB);
1030 init_styleset_case(MARKDOWN);
1031 init_styleset_case(NSIS);
1032 init_styleset_case(OBJECTIVEC);
1033 init_styleset_case(PASCAL);
1034 init_styleset_case(PERL);
1035 init_styleset_case(PHP);
1036 init_styleset_case(PO);
1037 init_styleset_case(PYTHON);
1038 init_styleset_case(R);
1039 init_styleset_case(RUBY);
1040 init_styleset_case(SH);
1041 init_styleset_case(SQL);
1042 init_styleset_case(TCL);
1043 init_styleset_case(TXT2TAGS);
1044 init_styleset_case(VHDL);
1045 init_styleset_case(VERILOG);
1046 init_styleset_case(XML);
1047 init_styleset_case(YAML);
1048 default:
1049 if (ft->lexer_filetype)
1050 geany_debug("Filetype %s has a recursive lexer_filetype %s set!",
1051 ft->name, ft->lexer_filetype->name);
1054 /* should be done in filetypes.c really: */
1055 get_keyfile_wordchars(config, configh, &style_sets[filetype_idx].wordchars);
1059 #define styleset_case(LANG_NAME) \
1060 case (GEANY_FILETYPES_##LANG_NAME): \
1061 styleset_from_mapping(sci, ft->id, highlighting_lexer_##LANG_NAME, \
1062 highlighting_styles_##LANG_NAME, \
1063 HL_N_ENTRIES(highlighting_styles_##LANG_NAME), \
1064 highlighting_keywords_##LANG_NAME, \
1065 HL_N_ENTRIES(highlighting_keywords_##LANG_NAME), \
1066 highlighting_properties_##LANG_NAME, \
1067 HL_N_ENTRIES(highlighting_properties_##LANG_NAME)); \
1068 break
1070 /** Sets up highlighting and other visual settings.
1071 * @param sci Scintilla widget.
1072 * @param ft Filetype settings to use. */
1073 void highlighting_set_styles(ScintillaObject *sci, GeanyFiletype *ft)
1075 guint lexer_id = get_lexer_filetype(ft);
1077 filetypes_load_config(ft->id, FALSE); /* load filetypes.ext */
1079 switch (lexer_id)
1081 styleset_case(ADA);
1082 styleset_case(ASM);
1083 styleset_case(BASIC);
1084 styleset_case(C);
1085 styleset_case(CAML);
1086 styleset_case(CMAKE);
1087 styleset_case(COBOL);
1088 styleset_case(CONF);
1089 styleset_case(CSS);
1090 styleset_case(D);
1091 styleset_case(DIFF);
1092 styleset_case(LISP);
1093 styleset_case(ERLANG);
1094 styleset_case(DOCBOOK);
1095 styleset_case(FERITE);
1096 styleset_case(F77);
1097 styleset_case(FORTH);
1098 styleset_case(FORTRAN);
1099 styleset_case(HASKELL);
1100 styleset_case(HAXE);
1101 styleset_case(AS);
1102 styleset_case(HTML);
1103 styleset_case(JAVA);
1104 styleset_case(JS);
1105 styleset_case(LATEX);
1106 styleset_case(LUA);
1107 styleset_case(MAKE);
1108 styleset_case(MARKDOWN);
1109 styleset_case(MATLAB);
1110 styleset_case(NSIS);
1111 styleset_case(OBJECTIVEC);
1112 styleset_case(PASCAL);
1113 styleset_case(PERL);
1114 styleset_case(PHP);
1115 styleset_case(PO);
1116 styleset_case(PYTHON);
1117 styleset_case(R);
1118 styleset_case(RUBY);
1119 styleset_case(SH);
1120 styleset_case(SQL);
1121 styleset_case(TCL);
1122 styleset_case(TXT2TAGS);
1123 styleset_case(VHDL);
1124 styleset_case(VERILOG);
1125 styleset_case(XML);
1126 styleset_case(YAML);
1127 case GEANY_FILETYPES_NONE:
1128 default:
1129 styleset_default(sci, ft->id);
1131 /* [lexer_properties] settings */
1132 if (style_sets[ft->id].property_keys)
1134 gchar **prop = style_sets[ft->id].property_keys;
1135 gchar **val = style_sets[ft->id].property_values;
1137 while (*prop)
1139 sci_set_property(sci, *prop, *val);
1140 prop++;
1141 val++;
1147 /** Retrieves a style @a style_id for the filetype @a ft_id.
1148 * If the style was not already initialised
1149 * (e.g. by by opening a file of this type), it will be initialised. The returned pointer is
1150 * owned by Geany and must not be freed.
1151 * @param ft_id Filetype ID, e.g. @c GEANY_FILETYPES_DIFF.
1152 * @param style_id A Scintilla lexer style, e.g. @c SCE_DIFF_ADDED. See scintilla/include/SciLexer.h.
1153 * @return A pointer to the style struct.
1154 * @see Scintilla messages @c SCI_STYLEGETFORE, etc, for use with scintilla_send_message(). */
1155 const GeanyLexerStyle *highlighting_get_style(gint ft_id, gint style_id)
1157 g_return_val_if_fail(ft_id >= 0 && (guint) ft_id < filetypes_array->len, NULL);
1158 g_return_val_if_fail(style_id >= 0, NULL);
1160 /* ensure filetype loaded */
1161 filetypes_load_config((guint) ft_id, FALSE);
1163 /* TODO: style_id might not be the real array index (Scintilla styles are not always synced
1164 * with array indices) */
1165 return get_style((guint) ft_id, (guint) style_id);
1169 static void
1170 on_color_scheme_clicked(GtkMenuItem *menuitem, gpointer user_data)
1172 gchar *fname;
1173 gchar *path;
1175 /* prevent callback on setting initial value */
1176 if (!GTK_WIDGET_MAPPED(menuitem))
1177 return;
1179 /* check if default item */
1180 if (!user_data)
1182 setptr(editor_prefs.color_scheme, NULL);
1183 filetypes_reload();
1184 return;
1186 fname = g_strdup(g_object_get_data(G_OBJECT(menuitem), "colorscheme_file"));
1187 setptr(fname, utils_get_locale_from_utf8(fname));
1189 /* fname is just the basename from the menu item, so prepend the custom files path */
1190 path = g_build_path(G_DIR_SEPARATOR_S, app->configdir, GEANY_COLORSCHEMES_SUBDIR, fname, NULL);
1191 if (!g_file_test(path, G_FILE_TEST_EXISTS))
1193 /* try the system path */
1194 g_free(path);
1195 path = g_build_path(G_DIR_SEPARATOR_S, app->datadir, GEANY_COLORSCHEMES_SUBDIR, fname, NULL);
1197 if (g_file_test(path, G_FILE_TEST_EXISTS))
1199 setptr(editor_prefs.color_scheme, fname);
1200 fname = NULL;
1201 filetypes_reload();
1203 else
1205 setptr(fname, utils_get_utf8_from_locale(fname));
1206 ui_set_statusbar(TRUE, _("Could not find file '%s'."), fname);
1208 g_free(path);
1209 g_free(fname);
1213 static gchar *utils_get_setting_locale_string(GKeyFile *keyfile,
1214 const gchar *group, const gchar *key, const gchar *default_value)
1216 gchar *result = g_key_file_get_locale_string(keyfile, group, key, NULL, NULL);
1218 return NVL(result, g_strdup(default_value));
1222 static void add_color_scheme_item(GtkWidget *menu, const gchar *fname)
1224 static GSList *group = NULL;
1225 GtkWidget *item;
1227 if (!fname)
1229 item = gtk_radio_menu_item_new_with_mnemonic(group, _("_Default"));
1231 else
1233 GKeyFile *hkeyfile, *skeyfile;
1234 gchar *path, *theme_name, *tooltip;
1235 gchar *theme_fn = utils_get_utf8_from_locale(fname);
1237 path = utils_build_path(app->configdir, GEANY_COLORSCHEMES_SUBDIR, fname, NULL);
1238 hkeyfile = utils_key_file_new(path);
1239 setptr(path, utils_build_path(app->datadir, GEANY_COLORSCHEMES_SUBDIR, fname, NULL));
1240 skeyfile = utils_key_file_new(path);
1242 theme_name = utils_get_setting(locale_string, hkeyfile, skeyfile, "theme_info", "name", theme_fn);
1243 item = gtk_radio_menu_item_new_with_label(group, theme_name);
1244 g_object_set_data_full(G_OBJECT(item), "colorscheme_file", theme_fn, g_free);
1246 tooltip = utils_get_setting(locale_string, hkeyfile, skeyfile, "theme_info", "description", NULL);
1247 if (tooltip != NULL)
1249 gtk_widget_set_tooltip_text(item, tooltip);
1250 g_free(tooltip);
1252 g_free(path);
1253 g_free(theme_name);
1254 g_key_file_free(hkeyfile);
1255 g_key_file_free(skeyfile);
1258 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item));
1259 if (utils_str_equal(editor_prefs.color_scheme, fname))
1260 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
1262 gtk_widget_show(item);
1263 gtk_container_add(GTK_CONTAINER(menu), item);
1264 g_signal_connect(item, "activate",
1265 G_CALLBACK(on_color_scheme_clicked), GINT_TO_POINTER(fname != NULL));
1269 static gboolean add_color_scheme_items(GtkWidget *menu)
1271 GSList *list, *node;
1273 g_return_val_if_fail(menu, FALSE);
1275 add_color_scheme_item(menu, NULL);
1276 list = utils_get_config_files(GEANY_COLORSCHEMES_SUBDIR);
1278 foreach_slist(node, list)
1280 gchar *fname = node->data;
1282 if (g_str_has_suffix(fname, ".conf"))
1283 add_color_scheme_item(menu, fname);
1285 g_free(fname);
1287 g_slist_free(list);
1288 return list != NULL;
1292 static void create_color_scheme_menu(void)
1294 GtkWidget *item, *menu, *root;
1296 menu = ui_lookup_widget(main_widgets.window, "menu_view_editor1_menu");
1297 item = ui_image_menu_item_new(GTK_STOCK_SELECT_COLOR, _("_Color Schemes"));
1298 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), item);
1299 root = item;
1301 menu = gtk_menu_new();
1302 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), menu);
1304 add_color_scheme_items(menu);
1305 gtk_widget_show_all(root);
1309 void highlighting_init(void)
1311 create_color_scheme_menu();
1315 /** Checks whether the given style is a string for the given lexer.
1317 * @param lexer Scintilla lexer type (@c SCLEX_*).
1318 * @param style Scintilla style (@c SCE_*).
1320 * @return @c TRUE if the style is a string, @c FALSE otherwise.
1322 gboolean highlighting_is_string_style(gint lexer, gint style)
1324 /* Don't forget STRINGEOL, to prevent completion whilst typing a string with no closing char. */
1326 switch (lexer)
1328 case SCLEX_CPP:
1329 return (style == SCE_C_CHARACTER ||
1330 style == SCE_C_STRING ||
1331 style == SCE_C_STRINGEOL ||
1332 style == SCE_C_STRINGRAW ||
1333 style == SCE_C_VERBATIM ||
1334 style == SCE_C_TRIPLEVERBATIM);
1336 case SCLEX_PASCAL:
1337 return (style == SCE_PAS_CHARACTER ||
1338 style == SCE_PAS_STRING ||
1339 style == SCE_PAS_STRINGEOL);
1341 case SCLEX_D:
1342 return (style == SCE_D_STRING ||
1343 style == SCE_D_STRINGEOL ||
1344 style == SCE_D_CHARACTER ||
1345 style == SCE_D_STRINGB ||
1346 style == SCE_D_STRINGR);
1348 case SCLEX_PYTHON:
1349 return (style == SCE_P_STRING ||
1350 style == SCE_P_TRIPLE ||
1351 style == SCE_P_TRIPLEDOUBLE ||
1352 style == SCE_P_CHARACTER ||
1353 style == SCE_P_STRINGEOL);
1355 case SCLEX_F77:
1356 case SCLEX_FORTRAN:
1357 return (style == SCE_F_STRING1 ||
1358 style == SCE_F_STRING2 ||
1359 style == SCE_F_STRINGEOL);
1361 case SCLEX_PERL:
1362 return (style == SCE_PL_STRING ||
1363 style == SCE_PL_CHARACTER ||
1364 style == SCE_PL_HERE_DELIM ||
1365 style == SCE_PL_HERE_Q ||
1366 style == SCE_PL_HERE_QQ ||
1367 style == SCE_PL_HERE_QX ||
1368 style == SCE_PL_POD ||
1369 style == SCE_PL_STRING_Q ||
1370 style == SCE_PL_STRING_QQ ||
1371 style == SCE_PL_STRING_QX ||
1372 style == SCE_PL_STRING_QR ||
1373 style == SCE_PL_STRING_QW ||
1374 style == SCE_PL_POD_VERB ||
1375 style == SCE_PL_XLAT
1376 /* we don't include any STRING_*_VAR for autocompletion */);
1378 case SCLEX_R:
1379 return (style == SCE_R_STRING);
1381 case SCLEX_RUBY:
1382 return (style == SCE_RB_CHARACTER ||
1383 style == SCE_RB_STRING ||
1384 style == SCE_RB_HERE_DELIM ||
1385 style == SCE_RB_HERE_Q ||
1386 style == SCE_RB_HERE_QQ ||
1387 style == SCE_RB_HERE_QX ||
1388 style == SCE_RB_POD);
1390 case SCLEX_BASH:
1391 return (style == SCE_SH_STRING);
1393 case SCLEX_SQL:
1394 return (style == SCE_SQL_STRING);
1396 case SCLEX_TCL:
1397 return (style == SCE_TCL_IN_QUOTE);
1399 case SCLEX_LUA:
1400 return (style == SCE_LUA_LITERALSTRING ||
1401 style == SCE_LUA_CHARACTER ||
1402 style == SCE_LUA_STRINGEOL ||
1403 style == SCE_LUA_STRING);
1405 case SCLEX_HASKELL:
1406 return (style == SCE_HA_CHARACTER ||
1407 style == SCE_HA_STRING);
1409 case SCLEX_FREEBASIC:
1410 return (style == SCE_B_STRING ||
1411 style == SCE_B_STRINGEOL);
1413 case SCLEX_OCTAVE:
1414 return (style == SCE_MATLAB_STRING ||
1415 style == SCE_MATLAB_DOUBLEQUOTESTRING);
1417 case SCLEX_HTML:
1418 return (
1419 style == SCE_HBA_STRING ||
1420 style == SCE_HBA_STRINGEOL ||
1421 style == SCE_HB_STRING ||
1422 style == SCE_HB_STRINGEOL ||
1423 style == SCE_H_CDATA ||
1424 style == SCE_H_DOUBLESTRING ||
1425 style == SCE_HJA_DOUBLESTRING ||
1426 style == SCE_HJA_SINGLESTRING ||
1427 style == SCE_HJA_STRINGEOL ||
1428 style == SCE_HJ_DOUBLESTRING ||
1429 style == SCE_HJ_SINGLESTRING ||
1430 style == SCE_HJ_STRINGEOL ||
1431 style == SCE_HPA_CHARACTER ||
1432 style == SCE_HPA_STRING ||
1433 style == SCE_HPA_TRIPLE ||
1434 style == SCE_HPA_TRIPLEDOUBLE ||
1435 style == SCE_HP_CHARACTER ||
1436 style == SCE_HPHP_HSTRING || /* HSTRING is a heredoc */
1437 style == SCE_HPHP_HSTRING_VARIABLE ||
1438 style == SCE_HPHP_SIMPLESTRING ||
1439 style == SCE_HP_STRING ||
1440 style == SCE_HP_TRIPLE ||
1441 style == SCE_HP_TRIPLEDOUBLE ||
1442 style == SCE_H_SGML_DOUBLESTRING ||
1443 style == SCE_H_SGML_SIMPLESTRING ||
1444 style == SCE_H_SINGLESTRING);
1446 case SCLEX_CMAKE:
1447 return (style == SCE_CMAKE_STRINGDQ ||
1448 style == SCE_CMAKE_STRINGLQ ||
1449 style == SCE_CMAKE_STRINGRQ ||
1450 style == SCE_CMAKE_STRINGVAR);
1452 case SCLEX_NSIS:
1453 return (style == SCE_NSIS_STRINGDQ ||
1454 style == SCE_NSIS_STRINGLQ ||
1455 style == SCE_NSIS_STRINGRQ ||
1456 style == SCE_NSIS_STRINGVAR);
1458 case SCLEX_ADA:
1459 return (style == SCE_ADA_CHARACTER ||
1460 style == SCE_ADA_STRING ||
1461 style == SCE_ADA_CHARACTEREOL ||
1462 style == SCE_ADA_STRINGEOL);
1464 return FALSE;
1468 /** Checks whether the given style is a comment for the given lexer.
1470 * @param lexer Scintilla lexer type (@c SCLEX_*).
1471 * @param style Scintilla style (@c SCE_*).
1473 * @return @c TRUE if the style is a comment, @c FALSE otherwise.
1475 gboolean highlighting_is_comment_style(gint lexer, gint style)
1477 switch (lexer)
1479 case SCLEX_COBOL:
1480 case SCLEX_CPP:
1481 return (style == SCE_C_COMMENT ||
1482 style == SCE_C_COMMENTLINE ||
1483 style == SCE_C_COMMENTDOC ||
1484 style == SCE_C_COMMENTLINEDOC ||
1485 style == SCE_C_COMMENTDOCKEYWORD ||
1486 style == SCE_C_COMMENTDOCKEYWORDERROR);
1488 case SCLEX_PASCAL:
1489 return (style == SCE_PAS_COMMENT ||
1490 style == SCE_PAS_COMMENT2 ||
1491 style == SCE_PAS_COMMENTLINE);
1493 case SCLEX_D:
1494 return (style == SCE_D_COMMENT ||
1495 style == SCE_D_COMMENTLINE ||
1496 style == SCE_D_COMMENTDOC ||
1497 style == SCE_D_COMMENTNESTED ||
1498 style == SCE_D_COMMENTLINEDOC ||
1499 style == SCE_D_COMMENTDOCKEYWORD ||
1500 style == SCE_D_COMMENTDOCKEYWORDERROR);
1502 case SCLEX_PYTHON:
1503 return (style == SCE_P_COMMENTLINE ||
1504 style == SCE_P_COMMENTBLOCK);
1506 case SCLEX_F77:
1507 case SCLEX_FORTRAN:
1508 return (style == SCE_F_COMMENT);
1510 case SCLEX_PERL:
1511 return (style == SCE_PL_COMMENTLINE);
1513 case SCLEX_PROPERTIES:
1514 return (style == SCE_PROPS_COMMENT);
1516 case SCLEX_PO:
1517 return (style == SCE_PO_COMMENT);
1519 case SCLEX_LATEX:
1520 return (style == SCE_L_COMMENT ||
1521 style == SCE_L_COMMENT2);
1523 case SCLEX_MAKEFILE:
1524 return (style == SCE_MAKE_COMMENT);
1526 case SCLEX_RUBY:
1527 return (style == SCE_RB_COMMENTLINE);
1529 case SCLEX_BASH:
1530 return (style == SCE_SH_COMMENTLINE);
1532 case SCLEX_R:
1533 return (style == SCE_R_COMMENT);
1535 case SCLEX_SQL:
1536 return (style == SCE_SQL_COMMENT ||
1537 style == SCE_SQL_COMMENTLINE ||
1538 style == SCE_SQL_COMMENTDOC ||
1539 style == SCE_SQL_COMMENTLINEDOC ||
1540 style == SCE_SQL_COMMENTDOCKEYWORD ||
1541 style == SCE_SQL_COMMENTDOCKEYWORDERROR);
1543 case SCLEX_TCL:
1544 return (style == SCE_TCL_COMMENT ||
1545 style == SCE_TCL_COMMENTLINE ||
1546 style == SCE_TCL_COMMENT_BOX ||
1547 style == SCE_TCL_BLOCK_COMMENT);
1549 case SCLEX_OCTAVE:
1550 return (style == SCE_MATLAB_COMMENT);
1552 case SCLEX_LUA:
1553 return (style == SCE_LUA_COMMENT ||
1554 style == SCE_LUA_COMMENTLINE ||
1555 style == SCE_LUA_COMMENTDOC);
1557 case SCLEX_HASKELL:
1558 return (style == SCE_HA_COMMENTLINE ||
1559 style == SCE_HA_COMMENTBLOCK ||
1560 style == SCE_HA_COMMENTBLOCK2 ||
1561 style == SCE_HA_COMMENTBLOCK3);
1563 case SCLEX_FREEBASIC:
1564 return (style == SCE_B_COMMENT);
1566 case SCLEX_YAML:
1567 return (style == SCE_YAML_COMMENT);
1569 case SCLEX_HTML:
1570 return (
1571 style == SCE_HBA_COMMENTLINE ||
1572 style == SCE_HB_COMMENTLINE ||
1573 style == SCE_H_COMMENT ||
1574 style == SCE_HJA_COMMENT ||
1575 style == SCE_HJA_COMMENTDOC ||
1576 style == SCE_HJA_COMMENTLINE ||
1577 style == SCE_HJ_COMMENT ||
1578 style == SCE_HJ_COMMENTDOC ||
1579 style == SCE_HJ_COMMENTLINE ||
1580 style == SCE_HPA_COMMENTLINE ||
1581 style == SCE_HP_COMMENTLINE ||
1582 style == SCE_HPHP_COMMENT ||
1583 style == SCE_HPHP_COMMENTLINE ||
1584 style == SCE_H_SGML_COMMENT);
1586 case SCLEX_CMAKE:
1587 return (style == SCE_CMAKE_COMMENT);
1589 case SCLEX_NSIS:
1590 return (style == SCE_NSIS_COMMENT ||
1591 style == SCE_NSIS_COMMENTBOX);
1593 case SCLEX_ADA:
1594 return (style == SCE_ADA_COMMENTLINE ||
1595 style == SCE_NSIS_COMMENTBOX);
1597 case SCLEX_ASM:
1598 return (style == SCE_ASM_COMMENT ||
1599 style == SCE_ASM_COMMENTBLOCK ||
1600 style == SCE_ASM_COMMENTDIRECTIVE);
1602 return FALSE;
1606 /** Checks whether the given style is normal code (not string, comment, preprocessor, etc).
1608 * @param lexer Scintilla lexer type (@c SCLEX_*).
1609 * @param style Scintilla style (@c SCE_*).
1611 * @return @c TRUE if the style is code, @c FALSE otherwise.
1613 gboolean highlighting_is_code_style(gint lexer, gint style)
1615 switch (lexer)
1617 case SCLEX_CPP:
1618 if (style == SCE_C_PREPROCESSOR)
1619 return FALSE;
1620 break;
1622 return !(highlighting_is_comment_style(lexer, style) ||
1623 highlighting_is_string_style(lexer, style));