diff --git a/build/pairtaghighlighter.m4 b/build/pairtaghighlighter.m4
index 285edb2fb..40fc103dc 100644
--- a/build/pairtaghighlighter.m4
+++ b/build/pairtaghighlighter.m4
@@ -5,5 +5,6 @@ AC_DEFUN([GP_CHECK_PAIRTAGHIGHLIGHTER],
AC_CONFIG_FILES([
pairtaghighlighter/Makefile
pairtaghighlighter/src/Makefile
+ pairtaghighlighter/data/Makefile
])
])
diff --git a/pairtaghighlighter/Makefile.am b/pairtaghighlighter/Makefile.am
index 9caa92013..b02711c4b 100644
--- a/pairtaghighlighter/Makefile.am
+++ b/pairtaghighlighter/Makefile.am
@@ -1,4 +1,4 @@
include $(top_srcdir)/build/vars.auxfiles.mk
-SUBDIRS = src
+SUBDIRS = src data
plugin = pairtaghighlighter
diff --git a/pairtaghighlighter/data/Makefile.am b/pairtaghighlighter/data/Makefile.am
new file mode 100644
index 000000000..05c23c47b
--- /dev/null
+++ b/pairtaghighlighter/data/Makefile.am
@@ -0,0 +1,4 @@
+include $(top_srcdir)/build/vars.docs.mk
+plugin = pairtaghighlighter
+
+dist_plugindata_DATA = prefs.ui
diff --git a/pairtaghighlighter/data/prefs.ui b/pairtaghighlighter/data/prefs.ui
new file mode 100644
index 000000000..dcf40029e
--- /dev/null
+++ b/pairtaghighlighter/data/prefs.ui
@@ -0,0 +1,225 @@
+
+
+
+
+
+
+
diff --git a/pairtaghighlighter/src/pair_tag_highlighter.c b/pairtaghighlighter/src/pair_tag_highlighter.c
index b0081bb8a..9b7c624c5 100644
--- a/pairtaghighlighter/src/pair_tag_highlighter.c
+++ b/pairtaghighlighter/src/pair_tag_highlighter.c
@@ -11,15 +11,30 @@
#include "config.h"
#include
#include
+#include
+#include
+#include
#include "Scintilla.h" /* for the SCNotification struct */
#include "SciLexer.h"
#define INDICATOR_TAGMATCH 9
#define MAX_TAG_NAME 64
+#define HIGHLIGHT_ALPHA_MIN 0
+#define HIGHLIGHT_ALPHA_MAX 255
+#define HIGHLIGHT_ALPHA_DEFAULT 60
-#define MATCHING_PAIR_COLOR 0x00ff00 /* green */
-#define NONMATCHING_PAIR_COLOR 0xff0000 /* red */
-#define EMPTY_TAG_COLOR 0xffff00 /* yellow */
+/* Tag types */
+enum {
+ MATCHING_PAIR,
+ NONMATCHING_PAIR,
+ EMPTY_TAG,
+ TAG_TYPE_COUNT
+};
+
+/* Colors for tag highlighting */
+const gint HIGHLIGHT_COLOR_DEFAULTS[TAG_TYPE_COUNT] = {0x00ff00, 0xff0000, 0xffff00};
+static gint highlightColor_G[TAG_TYPE_COUNT];
+static glong highlightAlpha_G = HIGHLIGHT_ALPHA_DEFAULT;
/* Keyboard Shortcut */
enum {
@@ -27,6 +42,17 @@ enum {
KB_COUNT
};
+/* Preference dialog objects */
+typedef struct PrefDialogObjects PrefDialogObjects;
+struct PrefDialogObjects {
+ GtkWidget *main_prefs_frame;
+ GtkWidget *matching_pair_color_button;
+ GtkWidget *nonmatching_pair_color_button;
+ GtkWidget *empty_tag_color_button;
+ GObject *alpha_adjust;
+ GtkWidget *defaults_button;
+};
+
/* These items are set by Geany before plugin_init() is called. */
GeanyPlugin *geany_plugin;
GeanyData *geany_data;
@@ -41,7 +67,6 @@ PLUGIN_SET_TRANSLATABLE_INFO(LOCALEDIR, GETTEXT_PACKAGE, _("Pair Tag Highlighter
_("Finds and highlights matching opening/closing HTML tag"),
"1.1", "Volodymyr Kononenko ")
-
/* Searches tag brackets.
* direction variable shows sets search direction:
* TRUE - to the right
@@ -137,7 +162,7 @@ static void highlight_tag(ScintillaObject *sci, gint openingBracket,
scintilla_send_message(sci, SCI_INDICSETSTYLE,
INDICATOR_TAGMATCH, INDIC_ROUNDBOX);
scintilla_send_message(sci, SCI_INDICSETFORE, INDICATOR_TAGMATCH, rgb2bgr(color));
- scintilla_send_message(sci, SCI_INDICSETALPHA, INDICATOR_TAGMATCH, 60);
+ scintilla_send_message(sci, SCI_INDICSETALPHA, INDICATOR_TAGMATCH, highlightAlpha_G);
scintilla_send_message(sci, SCI_INDICATORFILLRANGE,
openingBracket, closingBracket-openingBracket+1);
}
@@ -146,9 +171,9 @@ static void highlight_tag(ScintillaObject *sci, gint openingBracket,
static void highlight_matching_pair(ScintillaObject *sci)
{
highlight_tag(sci, highlightedBrackets[0], highlightedBrackets[1],
- MATCHING_PAIR_COLOR);
+ highlightColor_G[MATCHING_PAIR]);
highlight_tag(sci, highlightedBrackets[2], highlightedBrackets[3],
- MATCHING_PAIR_COLOR);
+ highlightColor_G[MATCHING_PAIR]);
}
@@ -269,7 +294,7 @@ static void findMatchingOpeningTag(ScintillaObject *sci, gchar *tagName, gint op
}
}
highlight_tag(sci, highlightedBrackets[0], highlightedBrackets[1],
- NONMATCHING_PAIR_COLOR);
+ highlightColor_G[NONMATCHING_PAIR]);
}
@@ -318,7 +343,7 @@ static void findMatchingClosingTag(ScintillaObject *sci, gchar *tagName, gint cl
}
}
highlight_tag(sci, highlightedBrackets[0], highlightedBrackets[1],
- NONMATCHING_PAIR_COLOR);
+ highlightColor_G[NONMATCHING_PAIR]);
}
@@ -331,7 +356,7 @@ static void findMatchingTag(ScintillaObject *sci, gint openingBracket, gint clos
return;
if(is_tag_self_closing(sci, closingBracket) || is_tag_empty(tagName)) {
- highlight_tag(sci, openingBracket, closingBracket, EMPTY_TAG_COLOR);
+ highlight_tag(sci, openingBracket, closingBracket, highlightColor_G[EMPTY_TAG]);
} else {
if(isTagOpening)
findMatchingClosingTag(sci, tagName, closingBracket);
@@ -425,6 +450,176 @@ on_kb_goto_matching_tag (guint key_id)
}
}
+/*** Configuration file ***/
+
+/* Returns the name of the configuration file. The string must be freed by the caller */
+static gchar *get_config_filename (void)
+{
+ return g_build_filename (geany_data->app->configdir, "plugins",
+ PLUGIN, PLUGIN".conf", NULL);
+}
+
+/* Read a color setting from the key file. Returns -1 if not valid or can't be read */
+static gint key_file_get_color (GKeyFile *key_file, const gchar *group, const gchar *key)
+{
+ glong value = -1;
+ gchar *value_str = g_key_file_get_value (key_file, group, key, NULL);
+
+ if (value_str) {
+ glong temp_value;
+ gchar *index = value_str;
+
+ /* get to the first hex digit */
+ while (*index != '\0' && !isxdigit(*index))
+ index++;
+
+ /* attempt to convert the value string to an RGB color integer */
+ if (*index != '\0') {
+ errno = 0;
+ temp_value = strtol (index, NULL, 16);
+ if (!errno)
+ if (temp_value >= 0 && temp_value <= 0xffffff)
+ value = temp_value;
+ }
+ }
+
+ return (gint) value;
+}
+
+/* Write a color setting to the key file */
+static void key_file_write_color (GKeyFile *key_file, const gchar *group,
+ const gchar *key, gint color)
+{
+ if (color >= 0 && color <= 0xffffff) {
+ gchar value_str[8];
+ g_snprintf (value_str, sizeof (value_str), "#%.6X", color);
+ g_key_file_set_value (key_file, group, key, value_str);
+ } else
+ g_warning ("key_file_write_color(): Value out of bounds %d\n", color);
+}
+
+/* Read a long integer value from the key file. Returns FALSE if there is an error
+ * reading the value, TRUE otherwise
+ */
+static gboolean key_file_get_long (GKeyFile *key_file, const gchar *group,
+ const gchar *key, glong *value)
+{
+ gboolean success = FALSE;
+ gchar *value_str = g_key_file_get_value (key_file, group, key, NULL);
+
+ if (value_str) {
+ glong temp_value;
+ gchar *end_ptr;
+
+ /* attempt to convert the value string to an integer */
+ errno = 0;
+ temp_value = strtol (value_str, &end_ptr, 10);
+ if (!errno && (end_ptr != value_str)) {
+ *value = temp_value;
+ success = TRUE;
+ }
+ }
+
+ return success;
+}
+
+/* Write a long integer setting to the key file */
+static void key_file_write_long (GKeyFile *key_file, const gchar *group,
+ const gchar *key, glong value)
+{
+ gchar *value_str;
+ value_str = g_strdup_printf ("%ld", value);
+ g_key_file_set_value (key_file, group, key, value_str);
+ g_free (value_str);
+}
+
+/* Loads the key file from the config file. Returns TRUE if successful, FALSE otherwise */
+static gboolean load_key_file (GKeyFile *key_file, gchar *config_file, GKeyFileFlags flags)
+{
+ gboolean success = TRUE;
+ GError *error = NULL;
+
+ if (!g_key_file_load_from_file(key_file, config_file, flags, &error)) {
+ if (error->code != G_FILE_ERROR_NOENT)
+ g_warning ("load_key_file(): Config file error\n%s", error->message);
+ g_error_free (error);
+ success = FALSE;
+ }
+ return success;
+}
+
+/* Load the configuration from the config file, if it exists */
+static void load_config (void)
+{
+ GKeyFile *key_file = g_key_file_new ();
+ gchar *config_file = get_config_filename ();
+
+ if (load_key_file (key_file, config_file, G_KEY_FILE_NONE)) {
+ gint color;
+ glong temp_value;
+ color = key_file_get_color (key_file, "color", "matching-pair");
+ if (color != -1)
+ highlightColor_G[MATCHING_PAIR] = color;
+ else
+ g_warning("load_config(): matching-pair not loaded\n");
+ color = key_file_get_color (key_file, "color", "nonmatching-pair");
+ if (color != -1)
+ highlightColor_G[NONMATCHING_PAIR] = color;
+ else
+ g_warning("load_config(): nonmatching-pair not loaded\n");
+ color = key_file_get_color (key_file, "color", "empty-tag");
+ if (color != -1)
+ highlightColor_G[EMPTY_TAG] = color;
+ else
+ g_warning("load_config(): empty-tag not read\n");
+ if (key_file_get_long (key_file, "color", "alpha", &temp_value))
+ if (temp_value >= HIGHLIGHT_ALPHA_MIN && temp_value <= HIGHLIGHT_ALPHA_MAX)
+ highlightAlpha_G = temp_value;
+ else
+ g_warning("load_config(): alpha out of range\n");
+ else
+ g_warning("load_config(): alpha not loaded\n");
+ }
+
+ g_key_file_free (key_file);
+ g_free (config_file);
+}
+
+/* Write the configuration to the config file */
+static void write_config (void)
+{
+ GKeyFile *key_file = g_key_file_new ();
+ gchar *config_file = get_config_filename ();
+ gchar *dir_name = g_path_get_dirname (config_file);
+ gint error_code;
+ GError *error = NULL;
+
+ load_key_file (key_file, config_file, G_KEY_FILE_KEEP_COMMENTS);
+ key_file_write_color (key_file, "color", "matching-pair",
+ highlightColor_G[MATCHING_PAIR]);
+ key_file_write_color (key_file, "color", "nonmatching-pair",
+ highlightColor_G[NONMATCHING_PAIR]);
+ key_file_write_color (key_file, "color", "empty-tag",
+ highlightColor_G[EMPTY_TAG]);
+ key_file_write_long (key_file, "color", "alpha", highlightAlpha_G);
+
+ /* create the config file's directory, if it doesn't exist */
+ error_code = utils_mkdir (dir_name, TRUE);
+ if (! error_code) {
+ if (! g_key_file_save_to_file (key_file, config_file, &error)) {
+ g_warning ("write_config(): Failed to write to file '%s'\n%s",
+ config_file, error->message);
+ g_error_free (error);
+ }
+ } else
+ g_warning ("write_config(): Failed to create directory '%s'\n%s",
+ dir_name, g_strerror (error_code));
+
+ g_key_file_free (key_file);
+ g_free (dir_name);
+ g_free (config_file);
+}
+
PluginCallback plugin_callbacks[] =
{
{ "editor-notify", (GCallback) &on_editor_notify, FALSE, NULL },
@@ -438,6 +633,8 @@ void plugin_init(GeanyData *data)
group = plugin_set_key_group (geany_plugin, "Pair Tag Highlighter", KB_COUNT, NULL);
keybindings_set_item (group, KB_MATCH_TAG, on_kb_goto_matching_tag,
0, 0, "goto_matching_tag", _("Go To Matching Tag"), NULL);
+ memcpy (&highlightColor_G, &HIGHLIGHT_COLOR_DEFAULTS, sizeof (highlightColor_G));
+ load_config ();
}
@@ -451,3 +648,196 @@ void plugin_cleanup(void)
clear_previous_highlighting(doc->editor->sci, highlightedBrackets[2], highlightedBrackets[3]);
}
}
+
+/*** Preference dialog ***/
+
+/* Returns the path of the plugin's data directory. The path must be freed
+ * by the caller
+ */
+static gchar * get_data_dir_path (const gchar *filename)
+{
+ gchar *prefix = NULL;
+ gchar *path;
+
+#ifdef G_OS_WIN32
+ prefix = g_win32_get_package_installation_directory_of_module (NULL);
+#elif defined(__APPLE__)
+ if (g_getenv ("GEANY_PLUGINS_SHARE_PATH"))
+ return g_build_filename (g_getenv ("GEANY_PLUGINS_SHARE_PATH"),
+ PLUGIN, filename, NULL);
+#endif
+ path = g_build_filename (prefix ? prefix : "", PLUGINDATADIR, filename, NULL);
+ g_free (prefix);
+ return path;
+}
+
+#if GTK_CHECK_VERSION(3,4,0)
+/* Convert an RGB (0xRRGGBB) number in integer form into a GdkRGBA */
+static void rgba_from_int (GdkRGBA *color, gint num)
+{
+ gfloat conv_factor = 1.0 / 255;
+ color->red = ((num & 0xff0000) >> 16) * conv_factor;
+ color->green = ((num & 0x00ff00) >> 8) * conv_factor;
+ color->blue = (num & 0x0000ff) * conv_factor;
+ color->alpha = 1.0;
+}
+
+/* Convert a GdkRGBA into an integer RGB value (0xRRGGBB) */
+static int int_from_rbga (GdkRGBA *color)
+{
+ gfloat conv_factor = 255.0;
+ int red = (int) (color->red * conv_factor);
+ int green = (int) (color->green * conv_factor);
+ int blue = (int) (color->blue * conv_factor);
+ return ((red << 16) | (green << 8) | blue);
+}
+#else
+/* Convert an RGB (0xRRGGBB) number in integer form into a GdkColor */
+static void color_from_int (GdkColor *color, gint num)
+{
+ gint conv_factor = 65535 / 255;
+ color->red = ((num & 0xff0000) >> 16) * conv_factor;
+ color->green = ((num & 0x00ff00) >> 8) * conv_factor;
+ color->blue = (num & 0x0000ff) * conv_factor;
+}
+
+/* Convert a GdkColor into an integer RGB value (0xRRGGBB) */
+static int int_from_color (GdkColor *color)
+{
+ gfloat conv_factor = 255.0 / 65535.0;
+ int red = (int) (color->red * conv_factor);
+ int green = (int) (color->green * conv_factor);
+ int blue = (int) (color->blue * conv_factor);
+ return ((red << 16) | (green << 8) | blue);
+}
+#endif
+
+static void free_config_dialog_objects (PrefDialogObjects *dialog_objects)
+{
+ g_object_unref (dialog_objects->main_prefs_frame);
+ g_free (dialog_objects);
+}
+
+/* Set the color of the color button from an integer RGB value */
+static void set_color_button_color (GtkWidget *color_button, gint value)
+{
+#if GTK_CHECK_VERSION(3,4,0)
+ GdkRGBA color;
+ rgba_from_int (&color, value);
+ gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (color_button), &color);
+#else
+ GdkColor color;
+ color_from_int (&color, value);
+ gtk_color_button_set_color (GTK_COLOR_BUTTON (color_button), &color);
+#endif
+}
+
+/* Get the color in integer RGB form of the color button */
+static gint get_color_button_color (GtkWidget *color_button)
+{
+#if GTK_CHECK_VERSION(3,4,0)
+ GdkRGBA color;
+ gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (color_button), &color);
+ return int_from_rbga (&color);
+#else
+ GdkColor color;
+ gtk_color_button_get_color (GTK_COLOR_BUTTON (color_button), &color);
+ return int_from_color (&color);
+#endif
+}
+
+/* Handle responses to the preferences dialog */
+static void on_configure_response (GtkDialog *dialog, gint response,
+ PrefDialogObjects *dialog_objects)
+{
+ if (response == GTK_RESPONSE_APPLY || response == GTK_RESPONSE_OK) {
+ /* update configuration values from the preferences dialog */
+ highlightColor_G[MATCHING_PAIR] =
+ get_color_button_color (dialog_objects->matching_pair_color_button);
+ highlightColor_G[NONMATCHING_PAIR] =
+ get_color_button_color (dialog_objects->nonmatching_pair_color_button);
+ highlightColor_G[EMPTY_TAG] =
+ get_color_button_color (dialog_objects->empty_tag_color_button);
+ highlightAlpha_G =
+ (gint) gtk_adjustment_get_value (GTK_ADJUSTMENT (dialog_objects->alpha_adjust));
+
+ write_config ();
+
+ /* update the current document, if it's a markup document */
+ GeanyDocument *document = document_get_current ();
+ if (document) {
+ gint lexer;
+ lexer = sci_get_lexer(document->editor->sci);
+ if((lexer == SCLEX_HTML) || (lexer == SCLEX_XML) || (lexer == SCLEX_PHPSCRIPT))
+ run_tag_highlighter(document->editor->sci);
+ }
+ }
+}
+
+/* Return the settings in the preferences dialog to the defaults */
+static void on_defaults_button_pressed (GtkButton *button, PrefDialogObjects *dialog_objects)
+{
+ set_color_button_color (dialog_objects->matching_pair_color_button,
+ HIGHLIGHT_COLOR_DEFAULTS[MATCHING_PAIR]);
+ set_color_button_color (dialog_objects->nonmatching_pair_color_button,
+ HIGHLIGHT_COLOR_DEFAULTS[NONMATCHING_PAIR]);
+ set_color_button_color (dialog_objects->empty_tag_color_button,
+ HIGHLIGHT_COLOR_DEFAULTS[EMPTY_TAG]);
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (dialog_objects->alpha_adjust),
+ (gdouble) HIGHLIGHT_ALPHA_DEFAULT);
+}
+
+/* Set up the preferences dialog */
+GtkWidget *
+plugin_configure (GtkDialog *dialog)
+{
+ GError *error = NULL;
+ GtkWidget *prefs_frame = NULL;
+ GtkBuilder *builder = gtk_builder_new ();
+ gchar *path = get_data_dir_path ("prefs.ui");
+
+ /* get the builder from the UI file */
+ gtk_builder_set_translation_domain (builder, GETTEXT_PACKAGE);
+ if (! gtk_builder_add_from_file (builder, path, &error)) {
+ g_critical (_("Can't find the preferences UI file!\n%s"), error->message);
+ g_error_free (error);
+ } else {
+ /* get the dialog objects from the builder */
+ PrefDialogObjects *dialog_objects = g_malloc(sizeof (PrefDialogObjects));
+ dialog_objects->main_prefs_frame =
+ GTK_WIDGET (gtk_builder_get_object (builder, "prefs-frame"));
+ dialog_objects->matching_pair_color_button =
+ GTK_WIDGET (gtk_builder_get_object (builder, "matching-pair-color-button"));
+ set_color_button_color (dialog_objects->matching_pair_color_button,
+ highlightColor_G[MATCHING_PAIR]);
+ dialog_objects->nonmatching_pair_color_button =
+ GTK_WIDGET (gtk_builder_get_object (builder, "nonmatching-pair-color-button"));
+ set_color_button_color (dialog_objects->nonmatching_pair_color_button,
+ highlightColor_G[NONMATCHING_PAIR]);
+ dialog_objects->empty_tag_color_button =
+ GTK_WIDGET (gtk_builder_get_object (builder, "empty-tag-color-button"));
+ set_color_button_color (dialog_objects->empty_tag_color_button,
+ highlightColor_G[EMPTY_TAG]);
+ dialog_objects->alpha_adjust =
+ G_OBJECT (gtk_builder_get_object (builder, "alpha-adjustment"));
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (dialog_objects->alpha_adjust),
+ (gdouble) highlightAlpha_G);
+ dialog_objects->defaults_button =
+ GTK_WIDGET (gtk_builder_get_object (builder, "defaults-button"));
+
+ /* increase the reference count of the dialog frame */
+ prefs_frame = g_object_ref_sink (dialog_objects->main_prefs_frame);
+
+ /* connect handlers */
+ g_signal_connect_data (dialog, "response",
+ G_CALLBACK (on_configure_response), dialog_objects,
+ (GClosureNotify) free_config_dialog_objects, 0);
+ g_signal_connect (dialog_objects->defaults_button, "clicked",
+ G_CALLBACK (on_defaults_button_pressed), dialog_objects);
+ }
+
+ g_free (path);
+ g_object_unref (builder);
+
+ return prefs_frame;
+}