Skip to content

Instantly share code, notes, and snippets.

@b4n
Created April 12, 2014 01:21
Show Gist options
  • Save b4n/10513496 to your computer and use it in GitHub Desktop.
Save b4n/10513496 to your computer and use it in GitHub Desktop.
Internal Mnemonics Checker -- Checks Geany's internal mnemonics for conflicts
/*
* Copyright 2014 Colomban Wendling <[email protected]>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* a plugin to check Geany's internal mnemonic conflicts */
#include "geanyplugin.h"
#include <string.h>
GeanyPlugin *geany_plugin;
GeanyData *geany_data;
GeanyFunctions *geany_functions;
PLUGIN_VERSION_CHECK (GEANY_API_VERSION)
PLUGIN_SET_INFO (_("Internal Mnemonics Checker"),
_("Checks Geany's internal mnemonics for conflicts."),
"0.1",
"Colomban Wendling <[email protected]>")
static gunichar
get_mnemonic (const gchar *str)
{
const gchar *p = strchr (str, '_');
gunichar ch = g_utf8_get_char (p + 1);
return g_unichar_tolower (ch);
}
static gint
sort_by_mnemonics (gconstpointer a,
gconstpointer b)
{
return get_mnemonic (a) - get_mnemonic (b);
}
static guint
check_mnemonics (GtkContainer *container,
const gchar *parent)
{
GList *children = gtk_container_get_children (container);
GList *mnemonics = NULL;
guint n_errors = 0;
guint total_errors = 0;
GList *node;
gunichar ch;
for (node = children; node; node = node->next) {
const gchar *name = NULL;
gpointer widget = node->data;
/* for some reason, code below breaks separator items, WTF?
* so skip them altogether, they are not interesting anyway */
if (GTK_IS_SEPARATOR_MENU_ITEM (widget)) {
continue;
}
if (GTK_IS_MENU_ITEM (widget)) {
name = gtk_menu_item_get_label (widget);
}
if (! name) {
name = G_OBJECT_TYPE_NAME (widget);
}
if (GTK_IS_MENU_ITEM (widget) &&
gtk_menu_item_get_use_underline (widget)) {
GtkStockItem item;
const gchar *str = gtk_menu_item_get_label (widget);
if (GTK_IS_IMAGE_MENU_ITEM (widget) &&
gtk_image_menu_item_get_use_stock (widget) &&
gtk_stock_lookup (str, &item)) {
str = item.label;
}
if (str) {
const gchar *p = strchr (str, '_');
if (! p) {
g_debug (_("Item \"%s\"::\"%s\" uses mnemonic but don't have one"),
parent, str);
} else if (! p[1]) {
g_warning (_("Item \"%s\"::\"%s\" mnemonic is at the end!"),
parent, str);
} else {
mnemonics = g_list_insert_sorted (mnemonics, (gchar *) str,
sort_by_mnemonics);
}
}
}
if (GTK_IS_MENU_ITEM (widget)) {
GtkWidget *submenu = gtk_menu_item_get_submenu (widget);
if (submenu) {
widget = submenu;
}
}
if (GTK_IS_CONTAINER (widget)) {
total_errors += check_mnemonics (widget, name);
}
}
ch = 0;
for (node = mnemonics; node; node = node->next) {
gunichar mn = get_mnemonic (node->data);
if (ch != mn && node->next && get_mnemonic (node->next->data) == mn) {
gchar str[7];
str[g_unichar_to_utf8 (mn, str)] = 0;
if (n_errors == 0) {
g_debug (_("Conflicting mnemonics inside \"%s\":"), parent);
}
g_debug (" * %s", str);
g_debug (" * %s", (gchar *) node->data);
n_errors++;
} else if (ch == mn) {
g_debug (" * %s", (gchar *) node->data);
n_errors++;
}
ch = mn;
}
if (n_errors > 0) {
g_debug (" + %s", _("this menu already uses the following mnemonics:"));
ch = 0;
for (node = mnemonics; node; node = node->next) {
gunichar mn = get_mnemonic (node->data);
if (ch != mn) {
gchar str[7];
str[g_unichar_to_utf8 (mn, str)] = 0;
g_debug(" * %s", (gchar *) str);
}
ch = mn;
}
}
g_list_free (mnemonics);
g_list_free (children);
return total_errors + n_errors;
}
static gboolean
on_idle_check_mnemoics (gpointer dummy)
{
guint errors = 0;
errors += check_mnemonics (GTK_CONTAINER (geany_data->main_widgets->window),
_("<Main Window>"));
errors += check_mnemonics (GTK_CONTAINER (geany_data->main_widgets->editor_menu),
_("<Editor Menu>"));
g_debug (_("Total count of mnemonic conflicts: %u"), errors);
return FALSE;
}
void
plugin_init (GeanyData *data)
{
plugin_idle_add (geany_plugin, on_idle_check_mnemoics, NULL);
}
void
plugin_cleanup (void)
{
}
PLUGIN := internal-mnemonics-check
VPATH ?= .
# requires https://github.com/b4n/geany-plugin.mk
include $(VPATH)/geany-plugin.mk
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment