From f176f5c7713be823f03a172393361b8a8fd61e9f Mon Sep 17 00:00:00 2001 From: Asif Amin <122637911+asifamin13@users.noreply.github.com> Date: Tue, 25 Apr 2023 18:18:27 -0500 Subject: [PATCH] Bracket Colors: Support user defined colors (#5) * simple UI * start hooking up signals * mechanism to save settings from a conf file * save load settings for defaults button * less globals * better config color name * hook up colors * glib types * use colors from plugin config * update colors when settings change * reorganze, try to avoid giant files * update document colors when they switch * update headers * fix bug when some brackets werent getting added to bracket map * fix bug that was deleting newly inserted brackets * std::map insert doesnt overwrite, use insert_or_assign * handle when removing a bracket completes another * handle invalid color specs in config file * avoid static initialization fiasco * if there are problems loading config, use defaults * cleanup headers * mark GUI labels as translatable --------- Co-authored-by: Asif Amin --- bracketcolors/src/Configuration.cc | 293 +++++++++++++++++++++ bracketcolors/src/Configuration.h | 117 +++++++++ bracketcolors/src/Makefile.am | 6 +- bracketcolors/src/Utils.cc | 103 ++++++++ bracketcolors/src/Utils.h | 62 +++++ bracketcolors/src/bracketcolors.cc | 401 +++++++++++++++++++---------- 6 files changed, 843 insertions(+), 139 deletions(-) create mode 100644 bracketcolors/src/Configuration.cc create mode 100644 bracketcolors/src/Configuration.h create mode 100644 bracketcolors/src/Utils.cc create mode 100644 bracketcolors/src/Utils.h diff --git a/bracketcolors/src/Configuration.cc b/bracketcolors/src/Configuration.cc new file mode 100644 index 000000000..7343a7093 --- /dev/null +++ b/bracketcolors/src/Configuration.cc @@ -0,0 +1,293 @@ +/* + * Configuration.cc + * + * Copyright 2023 Asif Amin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + + +/* --------------------------------- INCLUDES ------------------------------- */ + +#include +#include "Configuration.h" + + +/* ------------------------------ IMPLEMENTATION ---------------------------- */ + + +// ----------------------------------------------------------------------------- + BracketColorsPluginSetting::BracketColorsPluginSetting( + std::string group, + std::string key, + gpointer value + ) +/* + Constructor +----------------------------------------------------------------------------- */ +: mGroup(group), + mKey(key), + mValue(value) +{ + // nothing to do +} + + + +// ----------------------------------------------------------------------------- + BooleanSetting::BooleanSetting( + std::string group, + std::string key, + gpointer value + ) +/* + Constructor +----------------------------------------------------------------------------- */ +: BracketColorsPluginSetting(group, key, value) +{ + // nothing to do +} + + + +// ----------------------------------------------------------------------------- + ColorSetting::ColorSetting( + std::string group, + std::string key, + gpointer value + ) +/* + Constructor +----------------------------------------------------------------------------- */ +: BracketColorsPluginSetting(group, key, value) +{ + // nothing to do +} + + + +// ----------------------------------------------------------------------------- + bool BooleanSetting::read(GKeyFile *kf) +/* + +----------------------------------------------------------------------------- */ +{ + gboolean *aBool = static_cast(mValue); + *aBool = utils_get_setting_boolean( + kf, mGroup.c_str(), mKey.c_str(), *aBool + ); + return true; +} + + + +// ----------------------------------------------------------------------------- + bool BooleanSetting::write(GKeyFile *kf) +/* + +----------------------------------------------------------------------------- */ +{ + const gboolean *aBool = static_cast(mValue); + g_key_file_set_boolean( + kf, mGroup.c_str(), mKey.c_str(), *aBool + ); + return true; +} + + + +// ----------------------------------------------------------------------------- + bool ColorSetting::read(GKeyFile *kf) +/* + +----------------------------------------------------------------------------- */ +{ + std::string *strPtr = reinterpret_cast(mValue); + + gchar *str = utils_get_setting_string( + kf, mGroup.c_str(), mKey.c_str(), strPtr->c_str() + ); + + /* + * Make sure the color is valid + */ + bool ret = false; + + GdkColor color; + if (utils_parse_color(str, &color)) { + *strPtr = std::string(str); + ret = true; + } + else { + g_debug("%s: Failed to parse color '%s'", __FUNCTION__, str); + } + + g_free(str); + return ret; +} + + + +// ----------------------------------------------------------------------------- + bool ColorSetting::write(GKeyFile *kf) +/* + +----------------------------------------------------------------------------- */ +{ + std::string *strPtr = reinterpret_cast(mValue); + g_key_file_set_string( + kf, mGroup.c_str(), mKey.c_str(), strPtr->c_str() + ); + return true; +} + + + +// ----------------------------------------------------------------------------- + static gboolean read_keyfile( + GKeyFile *kf, + std::string filename, + GKeyFileFlags flags + ) +/* + +----------------------------------------------------------------------------- */ +{ + GError *error = NULL; + if (!g_key_file_load_from_file(kf, filename.c_str(), flags, &error)) { + if (error->domain != G_FILE_ERROR || error->code != G_FILE_ERROR_NOENT) { + g_debug("%s: Failed to load configuration file: %s", __FUNCTION__, error->message); + } + g_error_free(error); + return FALSE; + } + + return TRUE; +} + + + +// ----------------------------------------------------------------------------- + static gboolean write_keyfile( + GKeyFile *kf, + std::string filename + ) +/* + +----------------------------------------------------------------------------- */ +{ + gchar *dirname = g_path_get_dirname(filename.c_str()); + + gsize length; + gchar *data = g_key_file_to_data(kf, &length, NULL); + + GError *error = NULL; + gint err; + gboolean success = FALSE; + + if ((err = utils_mkdir(dirname, TRUE)) != 0) { + g_warning( + "Failed to create configuration directory \"%s\": %s", + dirname, g_strerror(err) + ); + } + else if (!g_file_set_contents(filename.c_str(), data, (gssize)length, &error)) { + g_warning("Failed to save configuration file: %s", error->message); + g_error_free(error); + } else { + success = TRUE; + } + + g_free(data); + g_free(dirname); + + return success; +} + + + +// ----------------------------------------------------------------------------- + BracketColorsPluginConfiguration::BracketColorsPluginConfiguration( + gboolean useDefaults, + BracketColorArray colors + ) +/* + +----------------------------------------------------------------------------- */ +: mUseDefaults(useDefaults), + mColors(colors), + mCustomColors(mColors) +{ + mPluginSettings.push_back( + std::make_shared("general", "defaults", &mUseDefaults) + ); + + for (guint i = 0; i < mCustomColors.size(); i++) { + std::string key = "order_" + std::to_string(i); + mPluginSettings.push_back( + std::make_shared("colors", key, &mCustomColors[i]) + ); + } +} + + + +// ----------------------------------------------------------------------------- + void BracketColorsPluginConfiguration::LoadConfig(std::string fileName) +/* + +----------------------------------------------------------------------------- */ +{ + GKeyFile *kf = g_key_file_new(); + bool success = true; + + if (read_keyfile(kf, fileName, G_KEY_FILE_NONE)) { + for (auto &it : mPluginSettings) { + if (not it->read(kf)) { + success = false; + } + } + } + else { + g_debug("%s: Unable to load '%s''", __FUNCTION__, fileName.c_str()); + success = false; + } + + if (not success) { + mUseDefaults = true; + } + + g_key_file_free(kf); +} + + + +// ----------------------------------------------------------------------------- + void BracketColorsPluginConfiguration::SaveConfig(std::string fileName) +/* + +----------------------------------------------------------------------------- */ +{ + GKeyFile *kf = g_key_file_new(); + + read_keyfile(kf, fileName, G_KEY_FILE_KEEP_COMMENTS); + + for (auto &it : mPluginSettings) { + it->write(kf); + } + write_keyfile(kf, fileName); + + g_key_file_free(kf); +} diff --git a/bracketcolors/src/Configuration.h b/bracketcolors/src/Configuration.h new file mode 100644 index 000000000..8f5337cff --- /dev/null +++ b/bracketcolors/src/Configuration.h @@ -0,0 +1,117 @@ +/* + * Configuration.h + * + * Copyright 2023 Asif Amin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef __CONFIGURATION_H__ +#define __CONFIGURATION_H__ + +/* --------------------------------- INCLUDES ------------------------------- */ + +#include +#include +#include + +#include + +#include "Utils.h" + +/* ----------------------------- CLASS DEFINITIONS -------------------------- */ + +// ----------------------------------------------------------------------------- + struct BracketColorsPluginSetting +/* + Purpose: Settings configuration base class + This is a bit overkill, but might be useful later for more settings +----------------------------------------------------------------------------- */ +{ + std::string mGroup, mKey; + gpointer mValue; + + BracketColorsPluginSetting( + std::string group, + std::string key, + gpointer value + ); + + virtual bool read(GKeyFile *kf)=0; + virtual bool write(GKeyFile *kf)=0; +}; + + + +// ----------------------------------------------------------------------------- + struct BooleanSetting : public BracketColorsPluginSetting +/* + +----------------------------------------------------------------------------- */ +{ + BooleanSetting( + std::string group, + std::string key, + gpointer value + ); + + bool read(GKeyFile *kf); + bool write(GKeyFile *kf); +}; + + + +// ----------------------------------------------------------------------------- + struct ColorSetting : public BracketColorsPluginSetting +/* + +----------------------------------------------------------------------------- */ +{ + ColorSetting( + std::string group, + std::string key, + gpointer value + ); + + bool read(GKeyFile *kf); + bool write(GKeyFile *kf); +}; + + + +// ----------------------------------------------------------------------------- + struct BracketColorsPluginConfiguration +/* + +----------------------------------------------------------------------------- */ +{ + gboolean mUseDefaults; + BracketColorArray mColors; + BracketColorArray mCustomColors; + + std::vector > mPluginSettings; + + BracketColorsPluginConfiguration( + gboolean useDefaults, + BracketColorArray colors + ); + + void LoadConfig(std::string fileName); + void SaveConfig(std::string fileName); +}; + + + +#endif diff --git a/bracketcolors/src/Makefile.am b/bracketcolors/src/Makefile.am index 9e9afa949..b7ee35b38 100644 --- a/bracketcolors/src/Makefile.am +++ b/bracketcolors/src/Makefile.am @@ -6,7 +6,11 @@ geanyplugins_LTLIBRARIES = bracketcolors.la bracketcolors_srcs = \ bracketcolors.cc \ BracketMap.cc \ - BracketMap.h + BracketMap.h \ + Utils.h \ + Utils.cc \ + Configuration.h \ + Configuration.cc bracketcolors_la_SOURCES = $(bracketcolors_srcs) bracketcolors_la_CXXFLAGS = $(AM_CXXFLAGS) $(AM_CFLAGS) -DG_LOG_DOMAIN=\"BracketColors\" diff --git a/bracketcolors/src/Utils.cc b/bracketcolors/src/Utils.cc new file mode 100644 index 000000000..e4be0d87f --- /dev/null +++ b/bracketcolors/src/Utils.cc @@ -0,0 +1,103 @@ +/* + * Utils.cc + * + * Copyright 2023 Asif Amin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + + +/* --------------------------------- INCLUDES ------------------------------- */ + +#include "Utils.h" + +/* ------------------------------ IMPLEMENTATION ---------------------------- */ + + +// ----------------------------------------------------------------------------- + gboolean utils_is_dark(guint32 color) + +/* + +----------------------------------------------------------------------------- */ +{ + guint8 b = color >> 16; + guint8 g = color >> 8; + guint8 r = color; + + // https://stackoverflow.com/questions/596216/formula-to-determine-perceived-brightness-of-rgb-color + guint8 y = ((r << 1) + r + (g << 2) + b) >> 3; + + if (y < 125) { + return TRUE; + } + + return FALSE; +} + + + +// ----------------------------------------------------------------------------- + gboolean utils_parse_color( + const gchar *spec, + GdkColor *color + ) +/* + +----------------------------------------------------------------------------- */ +{ + gchar buf[64] = {0}; + + g_return_val_if_fail(spec != NULL, -1); + + if (spec[0] == '0' && (spec[1] == 'x' || spec[1] == 'X')) + { + /* convert to # format for GDK to understand it */ + buf[0] = '#'; + strncpy(buf + 1, spec + 2, sizeof(buf) - 2); + spec = buf; + } + + return gdk_color_parse(spec, color); +} + + + +// ----------------------------------------------------------------------------- + gint utils_color_to_bgr(const GdkColor *c) +/* + +----------------------------------------------------------------------------- */ +{ + g_return_val_if_fail(c != NULL, -1); + return (c->red / 256) | ((c->green / 256) << 8) | ((c->blue / 256) << 16); +} + + + +// ----------------------------------------------------------------------------- + gint utils_parse_color_to_bgr(const gchar *spec) +/* + +----------------------------------------------------------------------------- */ +{ + GdkColor color; + if (utils_parse_color(spec, &color)) { + return utils_color_to_bgr(&color); + } + else { + return -1; + } +} diff --git a/bracketcolors/src/Utils.h b/bracketcolors/src/Utils.h new file mode 100644 index 000000000..fbf8dc0a1 --- /dev/null +++ b/bracketcolors/src/Utils.h @@ -0,0 +1,62 @@ +/* + * Utils.h + * + * Copyright 2023 Asif Amin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef __UTILS_H__ +#define __UTILS_H__ + +/* --------------------------------- INCLUDES ------------------------------- */ + +#include +#include + +#include +#include + +#define BC_NUM_COLORS 3 + +/* ----------------------------------- TYPES -------------------------------- */ + + typedef std::array BracketColorArray; + +/* --------------------------------- CONSTANTS ------------------------------ */ + + /* + * These were copied from VS Code + */ + + const BracketColorArray sDarkBackgroundColors = { + "#FF00FF", "#FFFF00", "#00FFFF" + }; + + const BracketColorArray sLightBackgroundColors = { + "#008000", "#000080", "#800000" + }; + +/* --------------------------------- PROTOTYPES ----------------------------- */ + + gboolean utils_is_dark(guint32 color); + + gboolean utils_parse_color(const gchar *spec, GdkColor *color); + + gint utils_color_to_bgr(const GdkColor *c); + + gint utils_parse_color_to_bgr(const gchar *spec); + +#endif diff --git a/bracketcolors/src/bracketcolors.cc b/bracketcolors/src/bracketcolors.cc index 27995a077..0af76bf7e 100644 --- a/bracketcolors/src/bracketcolors.cc +++ b/bracketcolors/src/bracketcolors.cc @@ -30,19 +30,13 @@ # include #endif -#include -#include -#include -#include - -#include - #include #include "sciwrappers.h" #include "BracketMap.h" +#include "Utils.h" +#include "Configuration.h" -#define BC_NUM_COLORS 3 #define BC_NO_ARG 0 #define BC_STOP_ACTION TRUE #define BC_CONTINUE_ACTION FALSE @@ -52,21 +46,6 @@ /* --------------------------------- CONSTANTS ------------------------------ */ - typedef std::array BracketColorArray; - - /* - * These were copied from VS Code - * TODO: Make this user configurable, get from theme? - */ - - static const BracketColorArray sDarkBackgroundColors = { - "#FF00FF", "#FFFF00", "#00FFFF" - }; - - static const BracketColorArray sLightBackgroundColors = { - "#008000", "#000080", "#800000" - }; - static const gchar *sPluginName = "bracketcolors"; // start index of indicators our plugin will use @@ -89,9 +68,7 @@ */ GeanyDocument *doc; - guint32 backgroundColor; - BracketColorArray bracketColors; gboolean init; @@ -106,7 +83,6 @@ BracketColorsData() : doc(NULL), - bracketColors(sLightBackgroundColors), init(FALSE), computeTimeoutID(0), computeInterval(500), @@ -132,6 +108,10 @@ void StopTimers(); }; +/* ---------------------------------- GLOBALS ------------------------------- */ + + static BracketColorsPluginConfiguration gPluginConfiguration(TRUE, sLightBackgroundColors); + /* ---------------------------------- EXTERNS ------------------------------- */ GeanyPlugin *geany_plugin; @@ -217,83 +197,6 @@ } -// ----------------------------------------------------------------------------- - static gboolean utils_is_dark(guint32 color) - -/* - ------------------------------------------------------------------------------ */ -{ - guint8 b = color >> 16; - guint8 g = color >> 8; - guint8 r = color; - - // https://stackoverflow.com/questions/596216/formula-to-determine-perceived-brightness-of-rgb-color - guint8 y = ((r << 1) + r + (g << 2) + b) >> 3; - - if (y < 125) { - return TRUE; - } - - return FALSE; -} - - - -// ----------------------------------------------------------------------------- - static gboolean utils_parse_color( - const gchar *spec, - GdkColor *color - ) -/* - ------------------------------------------------------------------------------ */ -{ - gchar buf[64] = {0}; - - g_return_val_if_fail(spec != NULL, -1); - - if (spec[0] == '0' && (spec[1] == 'x' || spec[1] == 'X')) - { - /* convert to # format for GDK to understand it */ - buf[0] = '#'; - strncpy(buf + 1, spec + 2, sizeof(buf) - 2); - spec = buf; - } - - return gdk_color_parse(spec, color); -} - - - -// ----------------------------------------------------------------------------- - static gint utils_color_to_bgr(const GdkColor *c) -/* - ------------------------------------------------------------------------------ */ -{ - g_return_val_if_fail(c != NULL, -1); - return (c->red / 256) | ((c->green / 256) << 8) | ((c->blue / 256) << 16); -} - - - -// ----------------------------------------------------------------------------- - static gint utils_parse_color_to_bgr(const gchar *spec) -/* - ------------------------------------------------------------------------------ */ -{ - GdkColor color; - if (utils_parse_color(spec, &color)) { - return utils_color_to_bgr(&color); - } - else { - return -1; - } -} - - // ----------------------------------------------------------------------------- static void assign_indicator_colors( @@ -305,9 +208,9 @@ { ScintillaObject *sci = data->doc->editor->sci; - for (guint i = 0; i < data->bracketColors.size(); i++) { + for (guint i = 0; i < gPluginConfiguration.mColors.size(); i++) { guint index = sIndicatorIndex + i; - std::string spec = data->bracketColors.at(i); + std::string spec = gPluginConfiguration.mColors.at(i); gint color = utils_parse_color_to_bgr(spec.c_str()); SSM(sci, SCI_INDICSETSTYLE, index, INDIC_TEXTFORE); SSM(sci, SCI_INDICSETFORE, index, color); @@ -629,7 +532,7 @@ for (auto position : positions) { - unsigned correctIndicatorIndex = sIndicatorIndex + \ + guint correctIndicatorIndex = sIndicatorIndex + \ ((BracketMap::GetOrder(bracket) + i) % BC_NUM_COLORS); gint curr = SSM(sci, SCI_INDICATORVALUEAT, correctIndicatorIndex, position); @@ -750,7 +653,7 @@ gchar newChar = sci_get_char_at(sci, i); if (is_bracket_type(newChar, type)) { madeChange = TRUE; - bracketColorsData.recomputeIndicies.insert(i); + indiciesToRecompute.insert(i); } } @@ -762,16 +665,39 @@ indiciesToRecompute.begin(), indiciesToRecompute.end() ); + std::set newIndicies; + auto origBracketMap = bracketMap.mBracketMap; + for (const auto &it : indiciesToAdjust) { - bracketMap.mBracketMap.insert( - std::make_pair( - it + length, - bracketMap.mBracketMap.at(it) - ) + + /* + * Move bracket, remove old position + */ + + gint newIndex = it + length; + + newIndicies.insert(newIndex); + bracketMap.mBracketMap.insert_or_assign( + newIndex, + origBracketMap.at(it) ); - bracketMap.mBracketMap.erase(it); - bracketColorsData.RemoveFromQueues(it); + // dont remove newly added indicies + if (newIndicies.find(it) == newIndicies.end()) { + bracketMap.mBracketMap.erase(it); + } + + /* + * Check if new bracket was placed into position of old adjusted bracket + * that we just deleted. If so, don't remove it from the work queue + */ + + if ( + bracketColorsData.recomputeIndicies.find(it) == \ + bracketColorsData.recomputeIndicies.end() + ) { + bracketColorsData.RemoveFromQueues(it); + } } return TRUE; @@ -796,7 +722,7 @@ BracketMap &bracketMap = bracketColorsData.bracketMaps[type]; - std::set indiciesToRemove, indiciesToRecompute; + std::set indiciesToRemove, indiciesToRecompute, orphanedIndicies; for (const auto &it : bracketMap.mBracketMap) { const auto &bracket = it.second; @@ -804,9 +730,17 @@ // start bracket was deleted if ( (it.first >= position) and (it.first < position + length) ) { indiciesToRemove.insert(it.first); + // if the end bracket is valid and still present + if (endPos > it.first and endPos >= (position + length)) { + orphanedIndicies.insert(endPos - length); + } } // end bracket removed or space removed - else if (it.first >= position or endPos >= position) { + else if ( + it.first >= position or + endPos >= position or + BracketMap::GetLength(bracket) == BracketMap::UNDEFINED + ) { indiciesToRecompute.insert(it.first); } } @@ -826,11 +760,9 @@ for (const auto &it : indiciesToRecompute) { // first bracket was moved backwards if (it >= position) { - bracketMap.mBracketMap.insert( - std::make_pair( - it - length, - bracketMap.mBracketMap.at(it) - ) + bracketMap.mBracketMap.insert_or_assign( + it - length, + bracketMap.mBracketMap.at(it) ); bracketMap.mBracketMap.erase(it); bracketColorsData.RemoveFromQueues(it); @@ -841,6 +773,11 @@ } } + // for orphaned end brackets, just recompute them + bracketColorsData.recomputeIndicies.insert( + orphanedIndicies.begin(), orphanedIndicies.end() + ); + return TRUE; } @@ -954,7 +891,6 @@ for (gint i = nt->position; i < nt->position + nt->length; i++) { gchar currChar = sci_get_char_at(sci, i); if (is_bracket_type(currChar, static_cast(bracketType))) { - //g_debug("%s: Handling style change for bracket at %d", __FUNCTION__, i); data->recomputeIndicies.insert(i); } } @@ -995,14 +931,10 @@ ScintillaObject *sci = data->doc->editor->sci; guint32 currBGColor = SSM(sci, SCI_STYLEGETBACK, STYLE_DEFAULT, BC_NO_ARG); if (currBGColor != data->backgroundColor) { - g_debug("%s: background color changed: %#04x", __FUNCTION__, currBGColor); - gboolean currDark = utils_is_dark(currBGColor); gboolean wasDark = utils_is_dark(data->backgroundColor); - - if (currDark != wasDark) { - g_debug("%s: Need to change colors scheme!", __FUNCTION__); - data->bracketColors = currDark ? sDarkBackgroundColors : sLightBackgroundColors; + if (currDark != wasDark and gPluginConfiguration.mUseDefaults) { + gPluginConfiguration.mColors = currDark ? sDarkBackgroundColors : sLightBackgroundColors; assign_indicator_colors(data); } @@ -1026,7 +958,7 @@ ----------------------------------------------------------------------------- */ { - static const unsigned sIterationLimit = 50; + static const guint sIterationLimit = 50; if (not has_document()) { return FALSE; @@ -1047,7 +979,7 @@ ScintillaObject *sci = data->doc->editor->sci; - unsigned numIterations = 0; + guint numIterations = 0; for ( auto position = data->recomputeIndicies.begin(); position != data->recomputeIndicies.end(); @@ -1077,9 +1009,6 @@ data->updateUI = TRUE; } } - - std::set updatedBrackets = bracketMap.ComputeOrder(); - data->redrawIndicies.insert(updatedBrackets.begin(), updatedBrackets.end()); } position = data->recomputeIndicies.erase(position); @@ -1087,6 +1016,14 @@ break; } } + + if (data->updateUI) { + for (gint bracketType = 0; bracketType < BracketType::COUNT; bracketType++) { + BracketMap &bracketMap = data->bracketMaps[bracketType]; + std::set updatedBrackets = bracketMap.ComputeOrder(); + data->redrawIndicies.insert(updatedBrackets.begin(), updatedBrackets.end()); + } + } } return TRUE; @@ -1130,6 +1067,7 @@ gpointer pluginData = plugin_get_document_data(geany_plugin, doc, sPluginName); if (pluginData != NULL) { BracketColorsData *data = reinterpret_cast(pluginData); + assign_indicator_colors(data); data->StartTimers(); } } @@ -1157,6 +1095,25 @@ +// ----------------------------------------------------------------------------- + static std::string get_config_filename(void) +/* + +----------------------------------------------------------------------------- */ +{ + std::string configFile(sPluginName); + configFile.append(".conf"); + + return std::string( + g_build_filename( + geany_data->app->configdir, "plugins", + sPluginName, configFile.c_str(), + NULL + ) + ); +} + + // ----------------------------------------------------------------------------- static void on_document_open( @@ -1186,8 +1143,17 @@ */ data->backgroundColor = SSM(sci, SCI_STYLEGETBACK, STYLE_DEFAULT, BC_NO_ARG); - if (utils_is_dark(data->backgroundColor)) { - data->bracketColors = sDarkBackgroundColors; + + if (gPluginConfiguration.mUseDefaults) { + if (utils_is_dark(data->backgroundColor)) { + gPluginConfiguration.mColors = sDarkBackgroundColors; + } + else { + gPluginConfiguration.mColors = sLightBackgroundColors; + } + } + else { + gPluginConfiguration.mColors = gPluginConfiguration.mCustomColors; } assign_indicator_colors(data); @@ -1201,7 +1167,7 @@ // ----------------------------------------------------------------------------- - static gboolean plugin_bracketcolors_init ( + static gboolean plugin_bracketcolors_init( GeanyPlugin *plugin, gpointer pdata ) @@ -1212,6 +1178,8 @@ geany_plugin = plugin; geany_data = plugin->geany_data; + gPluginConfiguration.LoadConfig(get_config_filename()); + gboolean inInit = TRUE; guint i = 0; @@ -1248,6 +1216,164 @@ { on_document_close(NULL, documents[i], NULL); } + + gPluginConfiguration.SaveConfig(get_config_filename()); +} + + + +// ----------------------------------------------------------------------------- + static void update_colors(void) +/* + +----------------------------------------------------------------------------- */ +{ + if (not gPluginConfiguration.mUseDefaults) { + gPluginConfiguration.mColors = gPluginConfiguration.mCustomColors; + } + + if (has_document()) { + + GtkNotebook *notebook = GTK_NOTEBOOK(geany_data->main_widgets->notebook); + gint currPage = gtk_notebook_get_current_page(notebook); + GeanyDocument *currDoc = document_get_from_page(currPage); + + if (currDoc != NULL) { + + gpointer docData = plugin_get_document_data( + geany_plugin, currDoc, sPluginName + ); + + if (docData != NULL) { + BracketColorsData *bcd = reinterpret_cast(docData); + + if (gPluginConfiguration.mUseDefaults) { + gboolean isDark = utils_is_dark(bcd->backgroundColor); + gPluginConfiguration.mColors = isDark ? sDarkBackgroundColors : sLightBackgroundColors; + } + assign_indicator_colors(bcd); + } + } + } +} + + + +// ----------------------------------------------------------------------------- + static void checkbox_toggled( + GtkWidget *checkbox, + gpointer data + ) +/* + if checkbox toggled, block the button grid +----------------------------------------------------------------------------- */ +{ + GtkWidget *colorButtonGrid = GTK_WIDGET(data); + + gboolean isActive = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbox)); + + gtk_widget_set_sensitive( + colorButtonGrid, + not isActive + ); + + gPluginConfiguration.mUseDefaults = isActive; + + update_colors(); +} + + + +// ----------------------------------------------------------------------------- + static void color_button_set( + GtkColorButton *colorButton, + gpointer data + ) +/* + callback when user changes color +----------------------------------------------------------------------------- */ +{ + std::string *strPtr = reinterpret_cast(data); + + GdkColor color; + gtk_color_button_get_color(colorButton, &color); + + gchar *colorAsStr = gdk_color_to_string(&color); + *strPtr = std::string(colorAsStr); + + g_free(colorAsStr); + + update_colors(); +} + + + +// ----------------------------------------------------------------------------- + static GtkWidget* plugin_bracketcolors_configure( + GeanyPlugin *plugin, + GtkDialog *dialog, + gpointer pdata + ) +/* + make UI elements to configure plugin +----------------------------------------------------------------------------- */ +{ + GtkWidget *grid = gtk_grid_new(); + gtk_grid_set_row_spacing(GTK_GRID(grid), 5); + + GtkWidget *colorButtonGrid = gtk_grid_new(); + gtk_grid_set_column_spacing(GTK_GRID(colorButtonGrid), 5); + gtk_widget_set_margin_start(colorButtonGrid, 5); + gtk_widget_set_margin_end(colorButtonGrid, 5); + gtk_widget_set_margin_bottom(colorButtonGrid, 5); + + GtkWidget *frame = gtk_frame_new(_("Bracket Colors")); + gtk_container_add(GTK_CONTAINER(frame), colorButtonGrid); + + for (guint i = 0; i < BC_NUM_COLORS; i++) { + + GdkColor color; + utils_parse_color(gPluginConfiguration.mCustomColors[i].c_str(), &color); + + GtkWidget *colorButton = gtk_color_button_new_with_color(&color); + + gtk_grid_attach( + GTK_GRID(colorButtonGrid), colorButton, + i, 0, 1, 1 + ); + + g_signal_connect( + G_OBJECT(colorButton), + "color-set", + G_CALLBACK(color_button_set), + reinterpret_cast(&gPluginConfiguration.mCustomColors[i]) + ); + } + + gtk_grid_attach( + GTK_GRID(grid), frame, + 0, 0, 1, 1 + ); + + GtkWidget *checkBox = gtk_check_button_new_with_label(_("Use Defaults")); + gtk_grid_attach( + GTK_GRID(grid), checkBox, + 0, 1, 1, 1 + ); + + g_signal_connect( + G_OBJECT(checkBox), + "toggled", + G_CALLBACK(checkbox_toggled), + colorButtonGrid + ); + + gtk_toggle_button_set_active( + GTK_TOGGLE_BUTTON(checkBox), + gPluginConfiguration.mUseDefaults + ); + + return grid; } @@ -1267,8 +1393,6 @@ - - // ----------------------------------------------------------------------------- extern "C" void geany_load_module(GeanyPlugin *plugin) /* @@ -1287,6 +1411,7 @@ plugin->funcs->init = plugin_bracketcolors_init; plugin->funcs->cleanup = plugin_bracketcolors_cleanup; plugin->funcs->callbacks = plugin_bracketcolors_callbacks; + plugin->funcs->configure = plugin_bracketcolors_configure; /* Register! */ GEANY_PLUGIN_REGISTER(plugin, 226);