SF.net SVN: geany: [2297] branches/plugin-keybindings

ntrel at users.sourceforge.net ntrel at xxxxx
Fri Feb 29 19:30:29 UTC 2008


Revision: 2297
          http://geany.svn.sourceforge.net/geany/?rev=2297&view=rev
Author:   ntrel
Date:     2008-02-29 11:30:28 -0800 (Fri, 29 Feb 2008)

Log Message:
-----------
Change keybindings code to use keybinding groups, each with a
separate enum set, which will make the plugin ABI more stable,
and lay the groundwork for plugin keybindings support.
Note: this breaks the plugin API.
Disable setting menu accelerators (temporary).
Disable Keyboard Shortcuts dialog (temporary).
Disable snippets completion (temporary).
Disable focus commands in the VTE (temporary).
Make Tags group use separate cb_func_tags callback.
Rename keybindings_cmd() to keybindings_send_command().
Add keybindings_lookup_item().

Modified Paths:
--------------
    branches/plugin-keybindings/ChangeLog
    branches/plugin-keybindings/plugins/filebrowser.c
    branches/plugin-keybindings/src/build.c
    branches/plugin-keybindings/src/callbacks.c
    branches/plugin-keybindings/src/keybindings.c
    branches/plugin-keybindings/src/keybindings.h
    branches/plugin-keybindings/src/plugindata.h
    branches/plugin-keybindings/src/plugins.c
    branches/plugin-keybindings/src/prefs.c
    branches/plugin-keybindings/src/tools.c

Modified: branches/plugin-keybindings/ChangeLog
===================================================================
--- branches/plugin-keybindings/ChangeLog	2008-02-29 18:56:41 UTC (rev 2296)
+++ branches/plugin-keybindings/ChangeLog	2008-02-29 19:30:28 UTC (rev 2297)
@@ -1,3 +1,21 @@
+2008-02-29  Nick Treleaven  <nick(dot)treleaven(at)btinternet(dot)com>
+
+ * src/build.c, src/keybindings.c, src/keybindings.h, src/tools.c,
+   src/prefs.c, src/plugindata.h, src/callbacks.c, src/plugins.c,
+   plugins/filebrowser.c:
+   Change keybindings code to use keybinding groups, each with a
+   separate enum set, which will make the plugin ABI more stable,
+   and lay the groundwork for plugin keybindings support.
+   Note: this breaks the plugin API.
+   Disable setting menu accelerators (temporary).
+   Disable Keyboard Shortcuts dialog (temporary).
+   Disable snippets completion (temporary).
+   Disable focus commands in the VTE (temporary).
+   Make Tags group use separate cb_func_tags callback.
+   Rename keybindings_cmd() to keybindings_send_command().
+   Add keybindings_lookup_item().
+
+
 2008-02-28  Enrico Tröger  <enrico(dot)troeger(at)uvena(dot)de>
 
  * src/utils.c, src/utils.h, tagmanager/parse.h:

Modified: branches/plugin-keybindings/plugins/filebrowser.c
===================================================================
--- branches/plugin-keybindings/plugins/filebrowser.c	2008-02-29 18:56:41 UTC (rev 2296)
+++ branches/plugin-keybindings/plugins/filebrowser.c	2008-02-29 19:30:28 UTC (rev 2297)
@@ -495,6 +495,12 @@
 }
 
 
+static void on_hide_sidebar(void)
+{
+	p_keybindings->send_command(GEANY_KEYGROUP_VIEW, GEANY_KEYS_MENU_SIDEBAR);
+}
+
+
 static GtkWidget *create_popup_menu(void)
 {
 	GtkWidget *item, *menu, *image;
@@ -545,9 +551,7 @@
 		gtk_image_new_from_stock("gtk-close", GTK_ICON_SIZE_MENU));
 	gtk_widget_show(item);
 	gtk_container_add(GTK_CONTAINER(menu), item);
-	g_signal_connect_swapped((gpointer) item, "activate",
-		G_CALLBACK(p_keybindings->send_command),
-		GINT_TO_POINTER(GEANY_KEYS_MENU_SIDEBAR));
+	g_signal_connect((gpointer) item, "activate", G_CALLBACK(on_hide_sidebar), NULL);
 
 	return menu;
 }

Modified: branches/plugin-keybindings/src/build.c
===================================================================
--- branches/plugin-keybindings/src/build.c	2008-02-29 18:56:41 UTC (rev 2296)
+++ branches/plugin-keybindings/src/build.c	2008-02-29 19:30:28 UTC (rev 2297)
@@ -1087,10 +1087,14 @@
 }
 
 
+#if 1	/* tmp */
+#define GEANY_ADD_WIDGET_ACCEL(gkey, menuitem)
+#else
 #define GEANY_ADD_WIDGET_ACCEL(gkey, menuitem) \
 	if (keys[(gkey)]->key != 0) \
 		gtk_widget_add_accelerator(menuitem, "activate", accel_group, \
 			keys[(gkey)]->key, keys[(gkey)]->mods, GTK_ACCEL_VISIBLE)
+#endif
 
 static void create_build_menu_gen(BuildMenuItems *menu_items)
 {

Modified: branches/plugin-keybindings/src/callbacks.c
===================================================================
--- branches/plugin-keybindings/src/callbacks.c	2008-02-29 18:56:41 UTC (rev 2296)
+++ branches/plugin-keybindings/src/callbacks.c	2008-02-29 19:30:28 UTC (rev 2297)
@@ -654,7 +654,7 @@
 {
 	if (event->keyval == GDK_Escape)
 	{
-		keybindings_cmd(GEANY_KEYS_SWITCH_EDITOR);
+		keybindings_send_command(GEANY_KEYGROUP_FOCUS, GEANY_KEYS_SWITCH_EDITOR);
 		return TRUE;
 	}
 	return FALSE;
@@ -925,7 +925,7 @@
 
 	if (! sci_can_copy(sci))
 	{
-		keybindings_cmd(GEANY_KEYS_EDIT_SELECTWORD);
+		keybindings_send_command(GEANY_KEYGROUP_EDITING, GEANY_KEYS_EDIT_SELECTWORD);
 		keep_sel = FALSE;
 	}
 
@@ -1114,7 +1114,7 @@
 on_compile_button_clicked              (GtkToolButton   *toolbutton,
                                         gpointer         user_data)
 {
-	keybindings_cmd(GEANY_KEYS_BUILD_COMPILE);
+	keybindings_send_command(GEANY_KEYGROUP_BUILD, GEANY_KEYS_BUILD_COMPILE);
 }
 
 
@@ -1562,7 +1562,7 @@
 on_run_button_clicked                  (GtkToolButton   *toolbutton,
                                         gpointer         user_data)
 {
-	keybindings_cmd(GEANY_KEYS_BUILD_RUN);
+	keybindings_send_command(GEANY_KEYGROUP_BUILD, GEANY_KEYS_BUILD_RUN);
 }
 
 
@@ -1718,7 +1718,7 @@
 on_menu_duplicate_line1_activate       (GtkMenuItem     *menuitem,
                                         gpointer         user_data)
 {
-	keybindings_cmd(GEANY_KEYS_EDIT_DUPLICATELINE);
+	keybindings_send_command(GEANY_KEYGROUP_EDITING, GEANY_KEYS_EDIT_DUPLICATELINE);
 }
 
 

Modified: branches/plugin-keybindings/src/keybindings.c
===================================================================
--- branches/plugin-keybindings/src/keybindings.c	2008-02-29 18:56:41 UTC (rev 2296)
+++ branches/plugin-keybindings/src/keybindings.c	2008-02-29 19:30:28 UTC (rev 2297)
@@ -46,15 +46,13 @@
 #include "vte.h"
 
 
-KeyBinding *keys[GEANY_MAX_KEYS];
+GPtrArray *keybinding_groups;	/* array of KeyBindingGroup pointers */
 
+static const gchar default_group_name[] = "Bindings";
+
 static const gboolean swap_alt_tab_order = FALSE;
 
 
-/* simple convenience function to allocate and fill the struct */
-static KeyBinding *fill(KBCallback func, guint key, GdkModifierType mod, const gchar *name,
-		const gchar *label);
-
 static void cb_func_file_action(guint key_id);
 static void cb_func_menu_print(guint key_id);
 
@@ -96,11 +94,14 @@
 static void cb_func_nav_forward(guint key_id);
 static void cb_func_toggle_sidebar(guint key_id);
 
+/* common function for global editing keybindings. */
+static void cb_func_editing_global(guint key_id);
+
 /* common function for editing keybindings, only valid when scintilla has focus. */
-static void cb_func_edit(guint key_id);
+static void cb_func_editing(guint key_id);
 
-/* common function for global editing keybindings. */
-static void cb_func_edit_global(guint key_id);
+/* common function for tags keybindings, only valid when scintilla has focus. */
+static void cb_func_tags(guint key_id);
 
 /* common function for keybindings using current word */
 static void cb_func_current_word(guint key_id);
@@ -108,254 +109,364 @@
 static void add_menu_accels(void);
 
 
+/* simple convenience function to fill a KeyBinding struct item */
+static void add_kb(KeyBindingGroup *group, gsize kb_id,
+		KBCallback func, guint key, GdkModifierType mod,
+		const gchar *name, const gchar *label)
+{
+	KeyBinding *kb;
+
+	g_assert(kb_id < group->count);
+
+	kb = &group->keys[kb_id];
+
+	kb->name = name;
+	kb->label = label;
+	kb->key = key;
+	kb->mods = mod;
+	kb->cb_func = func;
+}
+
+
+static KeyBindingGroup *add_kb_group(KeyBindingGroup *group,
+		const gchar *name, const gchar *label, gsize count, KeyBinding *keys)
+{
+	g_ptr_array_add(keybinding_groups, group);
+
+	group->name = name;
+	group->label = label;
+	group->count = count;
+	group->keys = keys;
+	return group;
+}
+
+
+/* Expansion for group_id = FILE:
+ * static KeyBinding FILE_keys[GEANY_KEYS_FILE_COUNT]; */
+#define DECLARE_KEYS(group_id) \
+	static KeyBinding group_id ## _keys[GEANY_KEYS_ ## group_id ## _COUNT]
+
+/* Expansion for group_id = FILE:
+ * add_kb_group(&groups[GEANY_KEYGROUP_FILE], NULL, _("File menu"),
+ * 	GEANY_KEYS_FILE_COUNT, FILE_keys); */
+#define ADD_KB_GROUP(group_id, label) \
+	add_kb_group(&groups[GEANY_KEYGROUP_ ## group_id], default_group_name, label, \
+		GEANY_KEYS_ ## group_id ## _COUNT, group_id ## _keys)
+
+/* init all fields of keys with default values. */
 static void init_default_kb(void)
 {
-	/* init all fields of keys with default values */
-	keys[GEANY_KEYS_MENU_NEW] = fill(cb_func_file_action,
+	static KeyBindingGroup groups[GEANY_KEYGROUP_COUNT];
+	KeyBindingGroup *group;
+	DECLARE_KEYS(FILE);
+	DECLARE_KEYS(EDIT);
+	DECLARE_KEYS(SEARCH);
+	DECLARE_KEYS(VIEW);
+	DECLARE_KEYS(DOCUMENT);
+	DECLARE_KEYS(BUILD);
+	DECLARE_KEYS(TOOLS);
+	DECLARE_KEYS(HELP);
+	DECLARE_KEYS(FOCUS);
+	DECLARE_KEYS(NOTEBOOK);
+	DECLARE_KEYS(EDITING);
+	DECLARE_KEYS(TAGS);
+	DECLARE_KEYS(OTHER);
+
+	group = ADD_KB_GROUP(FILE, _("File menu"));
+
+	add_kb(group, GEANY_KEYS_MENU_NEW, cb_func_file_action,
 		GDK_n, GDK_CONTROL_MASK, "menu_new", _("New"));
-	keys[GEANY_KEYS_MENU_OPEN] = fill(cb_func_file_action,
+	add_kb(group, GEANY_KEYS_MENU_OPEN, cb_func_file_action,
 		GDK_o, GDK_CONTROL_MASK, "menu_open", _("Open"));
-	keys[GEANY_KEYS_MENU_OPENSELECTED] = fill(cb_func_file_action,
+	add_kb(group, GEANY_KEYS_MENU_OPENSELECTED, cb_func_file_action,
 		GDK_o, GDK_SHIFT_MASK | GDK_CONTROL_MASK, "menu_open_selected", _("Open selected file"));
-	keys[GEANY_KEYS_MENU_SAVE] = fill(cb_func_file_action,
+	add_kb(group, GEANY_KEYS_MENU_SAVE, cb_func_file_action,
 		GDK_s, GDK_CONTROL_MASK, "menu_save", _("Save"));
-	keys[GEANY_KEYS_MENU_SAVEAS] = fill(cb_func_file_action,
+	add_kb(group, GEANY_KEYS_MENU_SAVEAS, cb_func_file_action,
 		0, 0, "menu_saveas", _("Save as"));
-	keys[GEANY_KEYS_MENU_SAVEALL] = fill(cb_func_file_action,
+	add_kb(group, GEANY_KEYS_MENU_SAVEALL, cb_func_file_action,
 		GDK_S, GDK_SHIFT_MASK | GDK_CONTROL_MASK, "menu_saveall", _("Save all"));
-	keys[GEANY_KEYS_MENU_PRINT] = fill(cb_func_menu_print,
+	add_kb(group, GEANY_KEYS_MENU_PRINT, cb_func_menu_print,
 		GDK_p, GDK_CONTROL_MASK, "menu_print", _("Print"));
-	keys[GEANY_KEYS_MENU_CLOSE] = fill(cb_func_file_action,
+	add_kb(group, GEANY_KEYS_MENU_CLOSE, cb_func_file_action,
 		GDK_w, GDK_CONTROL_MASK, "menu_close", _("Close"));
-	keys[GEANY_KEYS_MENU_CLOSEALL] = fill(cb_func_file_action,
+	add_kb(group, GEANY_KEYS_MENU_CLOSEALL, cb_func_file_action,
 		GDK_w, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "menu_closeall", _("Close all"));
-	keys[GEANY_KEYS_MENU_RELOADFILE] = fill(cb_func_file_action,
+	add_kb(group, GEANY_KEYS_MENU_RELOADFILE, cb_func_file_action,
 		GDK_r, GDK_CONTROL_MASK, "menu_reloadfile", _("Reload file"));
-	keys[GEANY_KEYS_MENU_PROJECTPROPERTIES] = fill(cb_func_file_action,
+	add_kb(group, GEANY_KEYS_MENU_PROJECTPROPERTIES, cb_func_file_action,
 		0, 0, "project_properties", _("Project properties"));
 
-	keys[GEANY_KEYS_MENU_UNDO] = fill(cb_func_menu_undo,
+	group = ADD_KB_GROUP(EDIT, _("Edit menu"));
+
+	add_kb(group, GEANY_KEYS_MENU_UNDO, cb_func_menu_undo,
 		GDK_z, GDK_CONTROL_MASK, "menu_undo", _("Undo"));
-	keys[GEANY_KEYS_MENU_REDO] = fill(cb_func_menu_redo,
+	add_kb(group, GEANY_KEYS_MENU_REDO, cb_func_menu_redo,
 		GDK_y, GDK_CONTROL_MASK, "menu_redo", _("Redo"));
-	keys[GEANY_KEYS_MENU_CUT] = fill(cb_func_clipboard,
+	add_kb(group, GEANY_KEYS_MENU_CUT, cb_func_clipboard,
 		GDK_x, GDK_CONTROL_MASK, "menu_cut", _("Cut"));
-	keys[GEANY_KEYS_MENU_COPY] = fill(cb_func_clipboard,
+	add_kb(group, GEANY_KEYS_MENU_COPY, cb_func_clipboard,
 		GDK_c, GDK_CONTROL_MASK, "menu_copy", _("Copy"));
-	keys[GEANY_KEYS_MENU_PASTE] = fill(cb_func_clipboard,
+	add_kb(group, GEANY_KEYS_MENU_PASTE, cb_func_clipboard,
 		GDK_v, GDK_CONTROL_MASK, "menu_paste", _("Paste"));
-	keys[GEANY_KEYS_MENU_SELECTALL] = fill(cb_func_menu_selectall,
+	add_kb(group, GEANY_KEYS_MENU_SELECTALL, cb_func_menu_selectall,
 		GDK_a, GDK_CONTROL_MASK, "menu_selectall", _("Select All"));
-	keys[GEANY_KEYS_MENU_INSERTDATE] = fill(cb_func_menu_insert_date,
+	add_kb(group, GEANY_KEYS_MENU_INSERTDATE, cb_func_menu_insert_date,
 		GDK_d, GDK_SHIFT_MASK | GDK_MOD1_MASK, "menu_insert_date", _("Insert date"));
-	keys[GEANY_KEYS_MENU_PREFERENCES] = fill(cb_func_menu_preferences,
+	add_kb(group, GEANY_KEYS_MENU_PREFERENCES, cb_func_menu_preferences,
 		GDK_p, GDK_CONTROL_MASK | GDK_MOD1_MASK, "menu_preferences", _("Preferences"));
 
-	/* search */
-	keys[GEANY_KEYS_MENU_FIND] = fill(cb_func_menu_search,
+	group = ADD_KB_GROUP(SEARCH, _("Search menu"));
+
+	add_kb(group, GEANY_KEYS_MENU_FIND, cb_func_menu_search,
 		GDK_f, GDK_CONTROL_MASK, "menu_find", _("Find"));
-	keys[GEANY_KEYS_MENU_FINDNEXT] = fill(cb_func_menu_search,
+	add_kb(group, GEANY_KEYS_MENU_FINDNEXT, cb_func_menu_search,
 		GDK_g, GDK_CONTROL_MASK, "menu_findnext", _("Find Next"));
-	keys[GEANY_KEYS_MENU_FINDPREVIOUS] = fill(cb_func_menu_search,
+	add_kb(group, GEANY_KEYS_MENU_FINDPREVIOUS, cb_func_menu_search,
 		GDK_g, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "menu_findprevious", _("Find Previous"));
-	keys[GEANY_KEYS_MENU_FINDNEXTSEL] = fill(cb_func_menu_search,
+	add_kb(group, GEANY_KEYS_MENU_FINDNEXTSEL, cb_func_menu_search,
 		0, 0, "menu_findnextsel", _("Find Next Selection"));
-	keys[GEANY_KEYS_MENU_FINDPREVSEL] = fill(cb_func_menu_search,
+	add_kb(group, GEANY_KEYS_MENU_FINDPREVSEL, cb_func_menu_search,
 		0, 0, "menu_findprevsel", _("Find Previous Selection"));
-	keys[GEANY_KEYS_MENU_REPLACE] = fill(cb_func_menu_search,
+	add_kb(group, GEANY_KEYS_MENU_REPLACE, cb_func_menu_search,
 		GDK_h, GDK_CONTROL_MASK, "menu_replace", _("Replace"));
-	keys[GEANY_KEYS_MENU_FINDINFILES] = fill(cb_func_menu_search, GDK_f,
+	add_kb(group, GEANY_KEYS_MENU_FINDINFILES, cb_func_menu_search, GDK_f,
 		GDK_CONTROL_MASK | GDK_SHIFT_MASK, "menu_findinfiles", _("Find in Files"));
-	keys[GEANY_KEYS_MENU_NEXTMESSAGE] = fill(cb_func_menu_search,
+	add_kb(group, GEANY_KEYS_MENU_NEXTMESSAGE, cb_func_menu_search,
 		0, 0, "menu_nextmessage", _("Next Message"));
-	keys[GEANY_KEYS_MENU_GOTOLINE] = fill(cb_func_menu_search,
+	add_kb(group, GEANY_KEYS_MENU_GOTOLINE, cb_func_menu_search,
 		GDK_l, GDK_CONTROL_MASK, "menu_gotoline", _("Go to Line"));
 
-	keys[GEANY_KEYS_MENU_TOGGLEALL] = fill(cb_func_menu_toggle_all,
+	group = ADD_KB_GROUP(VIEW, _("View menu"));
+
+	add_kb(group, GEANY_KEYS_MENU_TOGGLEALL, cb_func_menu_toggle_all,
 		0, 0, "menu_toggleall", _("Toggle All Additional Widgets"));
-	keys[GEANY_KEYS_MENU_FULLSCREEN] = fill(cb_func_menu_fullscreen,
+	add_kb(group, GEANY_KEYS_MENU_FULLSCREEN, cb_func_menu_fullscreen,
 		GDK_F11, 0, "menu_fullscreen", _("Fullscreen"));
-	keys[GEANY_KEYS_MENU_MESSAGEWINDOW] = fill(cb_func_menu_messagewindow,
+	add_kb(group, GEANY_KEYS_MENU_MESSAGEWINDOW, cb_func_menu_messagewindow,
 		0, 0, "menu_messagewindow", _("Toggle Messages Window"));
-	keys[GEANY_KEYS_MENU_SIDEBAR] = fill(cb_func_toggle_sidebar,
+	add_kb(group, GEANY_KEYS_MENU_SIDEBAR, cb_func_toggle_sidebar,
 		0, 0, "toggle_sidebar", _("Toggle Sidebar"));
-	keys[GEANY_KEYS_MENU_ZOOMIN] = fill(cb_func_menu_zoomin,
+	add_kb(group, GEANY_KEYS_MENU_ZOOMIN, cb_func_menu_zoomin,
 		GDK_plus, GDK_CONTROL_MASK, "menu_zoomin", _("Zoom In"));
-	keys[GEANY_KEYS_MENU_ZOOMOUT] = fill(cb_func_menu_zoomout,
+	add_kb(group, GEANY_KEYS_MENU_ZOOMOUT, cb_func_menu_zoomout,
 		GDK_minus, GDK_CONTROL_MASK, "menu_zoomout", _("Zoom Out"));
 
-	keys[GEANY_KEYS_MENU_OPENCOLORCHOOSER] = fill(cb_func_menu_opencolorchooser,
-		0, 0, "menu_opencolorchooser", _("Show Color Chooser"));
-	keys[GEANY_KEYS_MENU_INSERTSPECIALCHARS] = fill(cb_func_menu_insert_specialchars,
-		0, 0, "menu_insert_specialchars", _("Insert Special HTML Characters"));
+	group = ADD_KB_GROUP(DOCUMENT, _("Document menu"));
 
-	keys[GEANY_KEYS_MENU_REPLACETABS] = fill(cb_func_menu_replacetabs,
+	add_kb(group, GEANY_KEYS_MENU_REPLACETABS, cb_func_menu_replacetabs,
 		0, 0, "menu_replacetabs", _("Replace tabs by space"));
-	keys[GEANY_KEYS_MENU_FOLDALL] = fill(cb_func_menu_foldall,
+	add_kb(group, GEANY_KEYS_MENU_FOLDALL, cb_func_menu_foldall,
 		0, 0, "menu_foldall", _("Fold all"));
-	keys[GEANY_KEYS_MENU_UNFOLDALL] = fill(cb_func_menu_unfoldall,
+	add_kb(group, GEANY_KEYS_MENU_UNFOLDALL, cb_func_menu_unfoldall,
 		0, 0, "menu_unfoldall", _("Unfold all"));
-	keys[GEANY_KEYS_RELOADTAGLIST] = fill(cb_func_reloadtaglist,
+	add_kb(group, GEANY_KEYS_RELOADTAGLIST, cb_func_reloadtaglist,
 		GDK_r, GDK_SHIFT_MASK | GDK_CONTROL_MASK, "reloadtaglist", _("Reload symbol list"));
 
-	keys[GEANY_KEYS_BUILD_COMPILE] = fill(cb_func_build_action,
+	group = ADD_KB_GROUP(BUILD, _("Build menu"));
+
+	add_kb(group, GEANY_KEYS_BUILD_COMPILE, cb_func_build_action,
 		GDK_F8, 0, "build_compile", _("Compile"));
-	keys[GEANY_KEYS_BUILD_LINK] = fill(cb_func_build_action,
+	add_kb(group, GEANY_KEYS_BUILD_LINK, cb_func_build_action,
 		GDK_F9, 0, "build_link", _("Build"));
-	keys[GEANY_KEYS_BUILD_MAKE] = fill(cb_func_build_action,
+	add_kb(group, GEANY_KEYS_BUILD_MAKE, cb_func_build_action,
 		GDK_F9, GDK_SHIFT_MASK, "build_make", _("Make all"));
-	keys[GEANY_KEYS_BUILD_MAKEOWNTARGET] = fill(cb_func_build_action,
+	add_kb(group, GEANY_KEYS_BUILD_MAKEOWNTARGET, cb_func_build_action,
 		GDK_F9, GDK_SHIFT_MASK | GDK_CONTROL_MASK, "build_makeowntarget",
 		_("Make custom target"));
-	keys[GEANY_KEYS_BUILD_MAKEOBJECT] = fill(cb_func_build_action,
+	add_kb(group, GEANY_KEYS_BUILD_MAKEOBJECT, cb_func_build_action,
 		0, 0, "build_makeobject", _("Make object"));
-	keys[GEANY_KEYS_BUILD_NEXTERROR] = fill(cb_func_build_action,
+	add_kb(group, GEANY_KEYS_BUILD_NEXTERROR, cb_func_build_action,
 		0, 0, "build_nexterror", _("Next error"));
-	keys[GEANY_KEYS_BUILD_RUN] = fill(cb_func_build_action,
+	add_kb(group, GEANY_KEYS_BUILD_RUN, cb_func_build_action,
 		GDK_F5, 0, "build_run", _("Run"));
-	keys[GEANY_KEYS_BUILD_RUN2] = fill(cb_func_build_action,
+	add_kb(group, GEANY_KEYS_BUILD_RUN2, cb_func_build_action,
 		0, 0, "build_run2", _("Run (alternative command)"));
-	keys[GEANY_KEYS_BUILD_OPTIONS] = fill(cb_func_build_action,
+	add_kb(group, GEANY_KEYS_BUILD_OPTIONS, cb_func_build_action,
 		0, 0, "build_options", _("Build options"));
 
-	keys[GEANY_KEYS_MENU_HELP] = fill(cb_func_menu_help,
+	group = ADD_KB_GROUP(TOOLS, _("Tools menu"));
+
+	add_kb(group, GEANY_KEYS_MENU_OPENCOLORCHOOSER, cb_func_menu_opencolorchooser,
+		0, 0, "menu_opencolorchooser", _("Show Color Chooser"));
+	add_kb(group, GEANY_KEYS_MENU_INSERTSPECIALCHARS, cb_func_menu_insert_specialchars,
+		0, 0, "menu_insert_specialchars", _("Insert Special HTML Characters"));
+
+	group = ADD_KB_GROUP(HELP, _("Help menu"));
+
+	add_kb(group, GEANY_KEYS_MENU_HELP, cb_func_menu_help,
 		GDK_F1, 0, "menu_help", _("Help"));
 
-	keys[GEANY_KEYS_SWITCH_EDITOR] = fill(cb_func_switch_editor,
+	group = ADD_KB_GROUP(FOCUS, _("Focus commands"));
+
+	add_kb(group, GEANY_KEYS_SWITCH_EDITOR, cb_func_switch_editor,
 		GDK_F2, 0, "switch_editor", _("Switch to Editor"));
-	keys[GEANY_KEYS_SWITCH_SCRIBBLE] = fill(cb_func_switch_scribble,
+	add_kb(group, GEANY_KEYS_SWITCH_SCRIBBLE, cb_func_switch_scribble,
 		GDK_F6, 0, "switch_scribble", _("Switch to Scribble"));
-	keys[GEANY_KEYS_SWITCH_VTE] = fill(cb_func_switch_vte,
+	add_kb(group, GEANY_KEYS_SWITCH_VTE, cb_func_switch_vte,
 		GDK_F4, 0, "switch_vte", _("Switch to VTE"));
-	keys[GEANY_KEYS_SWITCH_SEARCH_BAR] = fill(cb_func_switch_search_bar,
+	add_kb(group, GEANY_KEYS_SWITCH_SEARCH_BAR, cb_func_switch_search_bar,
 		GDK_F7, 0, "switch_search_bar", _("Switch to Search Bar"));
-	keys[GEANY_KEYS_SWITCH_TABLEFT] = fill(cb_func_switch_tableft,
+
+	group = ADD_KB_GROUP(NOTEBOOK, _("Notebook tab commands"));
+
+	add_kb(group, GEANY_KEYS_SWITCH_TABLEFT, cb_func_switch_tableft,
 		GDK_Page_Up, GDK_CONTROL_MASK, "switch_tableft", _("Switch to left document"));
-	keys[GEANY_KEYS_SWITCH_TABRIGHT] = fill(cb_func_switch_tabright,
+	add_kb(group, GEANY_KEYS_SWITCH_TABRIGHT, cb_func_switch_tabright,
 		GDK_Page_Down, GDK_CONTROL_MASK, "switch_tabright", _("Switch to right document"));
-	keys[GEANY_KEYS_SWITCH_TABLASTUSED] = fill(cb_func_switch_tablastused,
+	add_kb(group, GEANY_KEYS_SWITCH_TABLASTUSED, cb_func_switch_tablastused,
 		GDK_Tab, GDK_CONTROL_MASK, "switch_tablastused", _("Switch to last used document"));
-	keys[GEANY_KEYS_MOVE_TABLEFT] = fill(cb_func_move_tab,
+	add_kb(group, GEANY_KEYS_MOVE_TABLEFT, cb_func_move_tab,
 		GDK_Page_Up, GDK_MOD1_MASK, "move_tableft", _("Move document left"));
-	keys[GEANY_KEYS_MOVE_TABRIGHT] = fill(cb_func_move_tab,
+	add_kb(group, GEANY_KEYS_MOVE_TABRIGHT, cb_func_move_tab,
 		GDK_Page_Down, GDK_MOD1_MASK, "move_tabright", _("Move document right"));
-	keys[GEANY_KEYS_MOVE_TABFIRST] = fill(cb_func_move_tab,
+	add_kb(group, GEANY_KEYS_MOVE_TABFIRST, cb_func_move_tab,
 		0, 0, "move_tabfirst", _("Move document first"));
-	keys[GEANY_KEYS_MOVE_TABLAST] = fill(cb_func_move_tab,
+	add_kb(group, GEANY_KEYS_MOVE_TABLAST, cb_func_move_tab,
 		0, 0, "move_tablast", _("Move document last"));
-	keys[GEANY_KEYS_NAV_BACK] = fill(cb_func_nav_back,
-		0, 0, "nav_back", _("Navigate back a location"));
-	keys[GEANY_KEYS_NAV_FORWARD] = fill(cb_func_nav_forward,
-		0, 0, "nav_forward", _("Navigate forward a location"));
 
-	keys[GEANY_KEYS_EDIT_DUPLICATELINE] = fill(cb_func_edit,
+	group = ADD_KB_GROUP(EDITING, _("Editing commands"));
+
+	add_kb(group, GEANY_KEYS_EDIT_DUPLICATELINE, cb_func_editing,
 		GDK_d, GDK_CONTROL_MASK, "edit_duplicateline", _("Duplicate line or selection"));
-	keys[GEANY_KEYS_EDIT_DELETELINE] = fill(cb_func_edit,
+	add_kb(group, GEANY_KEYS_EDIT_DELETELINE, cb_func_editing,
 		GDK_k, GDK_CONTROL_MASK, "edit_deleteline", _("Delete current line(s)"));
-	keys[GEANY_KEYS_EDIT_COPYLINE] = fill(cb_func_edit,
+	add_kb(group, GEANY_KEYS_EDIT_COPYLINE, cb_func_editing,
 		GDK_c, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "edit_copyline", _("Copy current line(s)"));
-	keys[GEANY_KEYS_EDIT_CUTLINE] = fill(cb_func_edit,
+	add_kb(group, GEANY_KEYS_EDIT_CUTLINE, cb_func_editing,
 		GDK_x, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "edit_cutline", _("Cut current line(s)"));
-	keys[GEANY_KEYS_EDIT_TRANSPOSELINE] = fill(cb_func_edit,
+	add_kb(group, GEANY_KEYS_EDIT_TRANSPOSELINE, cb_func_editing,
 		GDK_t, GDK_CONTROL_MASK, "edit_transposeline", _("Transpose current line"));
-	keys[GEANY_KEYS_EDIT_TOGGLECASE] = fill(cb_func_edit,
+	add_kb(group, GEANY_KEYS_EDIT_TOGGLECASE, cb_func_editing,
 		GDK_u, GDK_CONTROL_MASK | GDK_MOD1_MASK, "edit_togglecase", _("Toggle Case of Selection"));
-	keys[GEANY_KEYS_EDIT_COMMENTLINETOGGLE] = fill(cb_func_edit,
+	add_kb(group, GEANY_KEYS_EDIT_COMMENTLINETOGGLE, cb_func_editing,
 		GDK_e, GDK_CONTROL_MASK, "edit_commentlinetoggle", _("Toggle line commentation"));
-	keys[GEANY_KEYS_EDIT_COMMENTLINE] = fill(cb_func_edit,
+	add_kb(group, GEANY_KEYS_EDIT_COMMENTLINE, cb_func_editing,
 		0, 0, "edit_commentline", _("Comment line(s)"));
-	keys[GEANY_KEYS_EDIT_UNCOMMENTLINE] = fill(cb_func_edit,
+	add_kb(group, GEANY_KEYS_EDIT_UNCOMMENTLINE, cb_func_editing,
 		0, 0, "edit_uncommentline", _("Uncomment line(s)"));
-	keys[GEANY_KEYS_EDIT_INCREASEINDENT] = fill(cb_func_edit,
+	add_kb(group, GEANY_KEYS_EDIT_INCREASEINDENT, cb_func_editing,
 		GDK_i, GDK_CONTROL_MASK, "edit_increaseindent", _("Increase indent"));
-	keys[GEANY_KEYS_EDIT_DECREASEINDENT] = fill(cb_func_edit,
+	add_kb(group, GEANY_KEYS_EDIT_DECREASEINDENT, cb_func_editing,
 		GDK_u, GDK_CONTROL_MASK, "edit_decreaseindent", _("Decrease indent"));
-	keys[GEANY_KEYS_EDIT_INCREASEINDENTBYSPACE] = fill(cb_func_edit,
+	add_kb(group, GEANY_KEYS_EDIT_INCREASEINDENTBYSPACE, cb_func_editing,
 		0, 0, "edit_increaseindentbyspace", _("Increase indent by one space"));
-	keys[GEANY_KEYS_EDIT_DECREASEINDENTBYSPACE] = fill(cb_func_edit,
+	add_kb(group, GEANY_KEYS_EDIT_DECREASEINDENTBYSPACE, cb_func_editing,
 		0, 0, "edit_decreaseindentbyspace", _("Decrease indent by one space"));
-	keys[GEANY_KEYS_EDIT_AUTOINDENT] = fill(cb_func_edit,
+	add_kb(group, GEANY_KEYS_EDIT_AUTOINDENT, cb_func_editing,
 		0, 0, "edit_autoindent", _("Smart line indent"));
-	keys[GEANY_KEYS_EDIT_SENDTOCMD1] = fill(cb_func_edit,
+	add_kb(group, GEANY_KEYS_EDIT_SENDTOCMD1, cb_func_editing,
 		GDK_1, GDK_CONTROL_MASK, "edit_sendtocmd1", _("Send to Custom Command 1"));
-	keys[GEANY_KEYS_EDIT_SENDTOCMD2] = fill(cb_func_edit,
+	add_kb(group, GEANY_KEYS_EDIT_SENDTOCMD2, cb_func_editing,
 		GDK_2, GDK_CONTROL_MASK, "edit_sendtocmd2", _("Send to Custom Command 2"));
-	keys[GEANY_KEYS_EDIT_SENDTOCMD3] = fill(cb_func_edit,
+	add_kb(group, GEANY_KEYS_EDIT_SENDTOCMD3, cb_func_editing,
 		GDK_3, GDK_CONTROL_MASK, "edit_sendtocmd3", _("Send to Custom Command 3"));
-	keys[GEANY_KEYS_EDIT_GOTOMATCHINGBRACE] = fill(cb_func_edit_global,
+	add_kb(group, GEANY_KEYS_EDIT_GOTOMATCHINGBRACE, cb_func_editing_global,
 		GDK_b, GDK_CONTROL_MASK, "edit_gotomatchingbrace",
 		_("Go to matching brace"));
-	keys[GEANY_KEYS_EDIT_TOGGLEMARKER] = fill(cb_func_edit_global,
+	add_kb(group, GEANY_KEYS_EDIT_TOGGLEMARKER, cb_func_editing_global,
 		GDK_m, GDK_CONTROL_MASK, "edit_togglemarker",
 		_("Toggle marker"));
-	keys[GEANY_KEYS_EDIT_GOTONEXTMARKER] = fill(cb_func_edit_global,
+	add_kb(group, GEANY_KEYS_EDIT_GOTONEXTMARKER, cb_func_editing_global,
 		GDK_period, GDK_CONTROL_MASK, "edit_gotonextmarker",
 		_("Go to next marker"));
-	keys[GEANY_KEYS_EDIT_GOTOPREVIOUSMARKER] = fill(cb_func_edit_global,
+	add_kb(group, GEANY_KEYS_EDIT_GOTOPREVIOUSMARKER, cb_func_editing_global,
 		GDK_comma, GDK_CONTROL_MASK, "edit_gotopreviousmarker",
 		_("Go to previous marker"));
-
-	keys[GEANY_KEYS_EDIT_AUTOCOMPLETE] = fill(cb_func_edit,
-		GDK_space, GDK_CONTROL_MASK, "edit_autocomplete", _("Complete word"));
-	keys[GEANY_KEYS_EDIT_CALLTIP] = fill(cb_func_edit,
-		GDK_space, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "edit_calltip", _("Show calltip"));
-	keys[GEANY_KEYS_EDIT_MACROLIST] = fill(cb_func_edit,
-		GDK_Return, GDK_CONTROL_MASK, "edit_macrolist", _("Show macro list"));
-	keys[GEANY_KEYS_EDIT_COMPLETESNIPPET] = fill(NULL,	/* has special callback */
-		GDK_Tab, 0, "edit_completesnippet", _("Complete snippet"));
-	keys[GEANY_KEYS_EDIT_SUPPRESSSNIPPETCOMPLETION] = fill(cb_func_edit,
-		0, 0, "edit_suppresssnippetcompletion", _("Suppress snippet completion"));
-
-	keys[GEANY_KEYS_EDIT_SELECTWORD] = fill(cb_func_edit,
+	add_kb(group, GEANY_KEYS_EDIT_SELECTWORD, cb_func_editing,
 		GDK_w, GDK_SHIFT_MASK | GDK_MOD1_MASK, "edit_selectword", _("Select current word"));
-	keys[GEANY_KEYS_EDIT_SELECTLINE] = fill(cb_func_edit,
+	add_kb(group, GEANY_KEYS_EDIT_SELECTLINE, cb_func_editing,
 		GDK_l, GDK_SHIFT_MASK | GDK_MOD1_MASK, "edit_selectline", _("Select current line(s)"));
-	keys[GEANY_KEYS_EDIT_SELECTPARAGRAPH] = fill(cb_func_edit,
+	add_kb(group, GEANY_KEYS_EDIT_SELECTPARAGRAPH, cb_func_editing,
 		GDK_p, GDK_SHIFT_MASK | GDK_MOD1_MASK, "edit_selectparagraph", _("Select current paragraph"));
-	keys[GEANY_KEYS_EDIT_SCROLLTOLINE] = fill(cb_func_edit,
+	add_kb(group, GEANY_KEYS_EDIT_SCROLLTOLINE, cb_func_editing,
 		GDK_l, GDK_SHIFT_MASK | GDK_CONTROL_MASK, "edit_scrolltoline", _("Scroll to current line"));
-	keys[GEANY_KEYS_EDIT_SCROLLLINEUP] = fill(cb_func_edit,
+	add_kb(group, GEANY_KEYS_EDIT_SCROLLLINEUP, cb_func_editing,
 		GDK_Up, GDK_MOD1_MASK, "edit_scrolllineup", _("Scroll up the view by one line"));
-	keys[GEANY_KEYS_EDIT_SCROLLLINEDOWN] = fill(cb_func_edit,
+	add_kb(group, GEANY_KEYS_EDIT_SCROLLLINEDOWN, cb_func_editing,
 		GDK_Down, GDK_MOD1_MASK, "edit_scrolllinedown", _("Scroll down the view by one line"));
-
-	keys[GEANY_KEYS_EDIT_INSERTALTWHITESPACE] = fill(cb_func_edit,
+	add_kb(group, GEANY_KEYS_EDIT_INSERTALTWHITESPACE, cb_func_editing,
 		0, 0, "edit_insertwhitespace", _("Insert alternative whitespace"));
 
-	keys[GEANY_KEYS_POPUP_FINDUSAGE] = fill(cb_func_current_word,
+	/* popup menu editing commands */
+	add_kb(group, GEANY_KEYS_POPUP_FINDUSAGE, cb_func_current_word,
 		0, 0, "popup_findusage", _("Find Usage"));
-	keys[GEANY_KEYS_POPUP_GOTOTAGDEFINITION] = fill(cb_func_current_word,
+	add_kb(group, GEANY_KEYS_POPUP_CONTEXTACTION, cb_func_current_word,
+		0, 0, "popup_contextaction", _("Context Action"));
+
+	group = ADD_KB_GROUP(TAGS, _("Tag commands"));
+
+	add_kb(group, GEANY_KEYS_EDIT_AUTOCOMPLETE, cb_func_tags,
+		GDK_space, GDK_CONTROL_MASK, "edit_autocomplete", _("Complete word"));
+	add_kb(group, GEANY_KEYS_EDIT_CALLTIP, cb_func_tags,
+		GDK_space, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "edit_calltip", _("Show calltip"));
+	add_kb(group, GEANY_KEYS_EDIT_MACROLIST, cb_func_tags,
+		GDK_Return, GDK_CONTROL_MASK, "edit_macrolist", _("Show macro list"));
+	add_kb(group, GEANY_KEYS_EDIT_COMPLETESNIPPET, NULL,	/* has special callback */
+		GDK_Tab, 0, "edit_completesnippet", _("Complete snippet"));
+	add_kb(group, GEANY_KEYS_EDIT_SUPPRESSSNIPPETCOMPLETION, cb_func_tags,
+		0, 0, "edit_suppresssnippetcompletion", _("Suppress snippet completion"));
+	add_kb(group, GEANY_KEYS_POPUP_GOTOTAGDEFINITION, cb_func_current_word,
 		0, 0, "popup_gototagdefinition", _("Go to Tag Definition"));
-	keys[GEANY_KEYS_POPUP_GOTOTAGDECLARATION] = fill(cb_func_current_word,
+	add_kb(group, GEANY_KEYS_POPUP_GOTOTAGDECLARATION, cb_func_current_word,
 		0, 0, "popup_gototagdeclaration", _("Go to Tag Declaration"));
-	keys[GEANY_KEYS_POPUP_CONTEXTACTION] = fill(cb_func_current_word,
-		0, 0, "popup_contextaction", _("Context Action"));
+
+	group = ADD_KB_GROUP(OTHER, _("Other commands"));
+
+	add_kb(group, GEANY_KEYS_NAV_BACK, cb_func_nav_back,
+		0, 0, "nav_back", _("Navigate back a location"));
+	add_kb(group, GEANY_KEYS_NAV_FORWARD, cb_func_nav_forward,
+		0, 0, "nav_forward", _("Navigate forward a location"));
 }
 
 
-static void load_user_kb(void)
+typedef void (*KBItemCallback) (KeyBindingGroup *group, KeyBinding *kb, gpointer user_data);
+
+static void keybindings_foreach(KBItemCallback cb, gpointer user_data)
 {
-	gchar *configfile = g_strconcat(app->configdir, G_DIR_SEPARATOR_S, "keybindings.conf", NULL);
+	gsize g, i;
+
+	for (g = 0; g < keybinding_groups->len; g++)
+	{
+		KeyBindingGroup *group = g_ptr_array_index(keybinding_groups, g);
+
+		for (i = 0; i < group->count; i++)
+		{
+			KeyBinding *kb = &group->keys[i];
+
+			cb(group, kb, user_data);
+		}
+	}
+}
+
+
+static void get_keyfile_kb(KeyBindingGroup *group, KeyBinding *kb, gpointer user_data)
+{
+	GKeyFile *config = user_data;
 	gchar *val;
-	guint i;
 	guint key;
 	GdkModifierType mods;
+
+	val = g_key_file_get_string(config, group->name, kb->name, NULL);
+	if (val != NULL)
+	{
+		gtk_accelerator_parse(val, &key, &mods);
+		kb->key = key;
+		kb->mods = mods;
+	}
+	g_free(val);
+}
+
+
+static void load_user_kb(void)
+{
+	gchar *configfile = g_strconcat(app->configdir, G_DIR_SEPARATOR_S, "keybindings.conf", NULL);
 	GKeyFile *config = g_key_file_new();
 
 	/* now load user defined keys */
 	if (g_key_file_load_from_file(config, configfile, G_KEY_FILE_KEEP_COMMENTS, NULL))
 	{
-		for (i = 0; i < GEANY_MAX_KEYS; i++)
-		{
-			val = g_key_file_get_string(config, "Bindings", keys[i]->name, NULL);
-			if (val != NULL)
-			{
-				gtk_accelerator_parse(val, &key, &mods);
-				keys[i]->key = key;
-				keys[i]->mods = mods;
-			}
-			g_free(val);
-		}
+		keybindings_foreach(get_keyfile_kb, config);
 	}
 	g_free(configfile);
 	g_key_file_free(config);
@@ -364,24 +475,10 @@
 
 void keybindings_init(void)
 {
+	keybinding_groups = g_ptr_array_sized_new(GEANY_KEYGROUP_COUNT);
+
 	init_default_kb();
 	load_user_kb();
-
-	/* set section name */
-	keys[GEANY_KEYS_GROUP_FILE]->section = _("File menu");
-	keys[GEANY_KEYS_GROUP_EDIT]->section = _("Edit menu");
-	keys[GEANY_KEYS_GROUP_SEARCH]->section = _("Search menu");
-	keys[GEANY_KEYS_GROUP_VIEW]->section = _("View menu");
-	keys[GEANY_KEYS_GROUP_DOCUMENT]->section = _("Document menu");
-	keys[GEANY_KEYS_GROUP_BUILD]->section = _("Build menu");
-	keys[GEANY_KEYS_GROUP_TOOLS]->section = _("Tools menu");
-	keys[GEANY_KEYS_GROUP_HELP]->section = _("Help menu");
-	keys[GEANY_KEYS_GROUP_FOCUS]->section = _("Focus commands");
-	keys[GEANY_KEYS_GROUP_TABS]->section = _("Notebook tab commands");
-	keys[GEANY_KEYS_GROUP_EDITING]->section = _("Editing commands");
-	keys[GEANY_KEYS_GROUP_TAGS]->section = _("Tag commands");
-	keys[GEANY_KEYS_GROUP_OTHER]->section = _("Other commands");
-
 	add_menu_accels();
 }
 
@@ -402,6 +499,7 @@
 
 static void add_menu_accels()
 {
+#if 0 /* tmp */
 	GtkAccelGroup *accel_group = gtk_accel_group_new();
 
 	/* apply the settings */
@@ -474,15 +572,26 @@
 	/* the build menu items are set if the build menus are created */
 
 	gtk_window_add_accel_group(GTK_WINDOW(app->window), accel_group);
+#endif
 }
 
 
+static void set_keyfile_kb(KeyBindingGroup *group, KeyBinding *kb, gpointer user_data)
+{
+	GKeyFile *config = user_data;
+	gchar *val;
+
+	val = gtk_accelerator_name(kb->key, kb->mods);
+	g_key_file_set_string(config, group->name, kb->name, val);
+	g_free(val);
+}
+
+
 /* just write the content of the keys array to the config file */
 void keybindings_write_to_file(void)
 {
 	gchar *configfile = g_strconcat(app->configdir, G_DIR_SEPARATOR_S, "keybindings.conf", NULL);
-	gchar *val, *data;
-	guint i;
+	gchar *data;
 	GKeyFile *config = g_key_file_new();
 
  	/* add comment if the file is newly created */
@@ -491,12 +600,7 @@
 		g_key_file_set_comment(config, NULL, NULL, "Keybindings for Geany\nThe format looks like \"<Control>a\" or \"<Shift><Alt>F1\".\nBut you can also change the keys in Geany's preferences dialog.", NULL);
 	}
 
-	for (i = 0; i < GEANY_MAX_KEYS; i++)
-	{
-		val = gtk_accelerator_name(keys[i]->key, keys[i]->mods);
-		g_key_file_set_string(config, "Bindings", keys[i]->name, val);
-		g_free(val);
-	}
+	keybindings_foreach(set_keyfile_kb, config);
 
 	/* write the file */
 	data = g_key_file_to_data(config, NULL, NULL);
@@ -510,17 +614,13 @@
 
 void keybindings_free(void)
 {
-	guint i;
-
-	for (i = 0; i < GEANY_MAX_KEYS; i++)
-	{
-		g_free(keys[i]);
-	}
+	g_ptr_array_free(keybinding_groups, TRUE);
 }
 
 
 static void get_shortcut_labels_text(GString **text_names_str, GString **text_keys_str)
 {
+#if 0
 	guint i;
 	GString *text_names = g_string_sized_new(600);
 	GString *text_keys = g_string_sized_new(600);
@@ -553,6 +653,7 @@
 		g_string_append(text_keys, "\n");
 		g_free(shortcut);
 	}
+#endif
 }
 
 
@@ -562,7 +663,7 @@
 	GString *text_names;
 	GString *text_keys;
 	gint height, response;
-
+return;/* tmp */
 	dialog = gtk_dialog_new_with_buttons(_("Keyboard Shortcuts"), GTK_WINDOW(app->window),
 				GTK_DIALOG_DESTROY_WITH_PARENT,
 				GTK_STOCK_EDIT, GTK_RESPONSE_APPLY,
@@ -664,8 +765,9 @@
  * return FALSE if no completion occurs, so the tab or space is handled normally. */
 static gboolean check_snippet_completion(guint keyval, guint state)
 {
+return FALSE; /* tmp */
+#if 0
 	const guint i = GEANY_KEYS_EDIT_COMPLETESNIPPET;
-
 	if (keys[i]->key == keyval && keys[i]->mods == state)
 	{
 		gint idx = document_get_cur_idx();
@@ -682,6 +784,7 @@
 		}
 	}
 	return FALSE;
+#endif
 }
 
 
@@ -695,7 +798,7 @@
 
 static gboolean check_vte(GdkModifierType state, guint keyval)
 {
-	guint i;
+	/*guint i;*/
 	GtkWidget *widget;
 
 	if (! vc->enable_bash_keys)
@@ -708,12 +811,14 @@
 	if (state == 0 && (keyval < GDK_F1 || keyval > GDK_F35))	/* e.g. backspace */
 		return FALSE;
 
+#if 0	/* tmp */
 	/* make focus commands override any bash commands */
 	for (i = GEANY_KEYS_GROUP_FOCUS; i < GEANY_KEYS_GROUP_TABS; i++)
 	{
 		if (state == keys[i]->mods && keyval == keys[i]->key)
 			return FALSE;
 	}
+#endif
 
 	/* Temporarily disable the menus to prevent conflicting menu accelerators
 	 * from overriding the VTE bash shortcuts.
@@ -733,7 +838,8 @@
 /* central keypress event handler, almost all keypress events go to this function */
 gboolean keybindings_got_event(GtkWidget *widget, GdkEventKey *ev, gpointer user_data)
 {
-	guint i, state, keyval;
+	guint state, keyval;
+	gsize g, i;
 
 	if (ev->keyval == 0)
 		return FALSE;
@@ -756,47 +862,53 @@
 	if (check_snippet_completion(keyval, state))
 		return TRUE;
 
-	for (i = 0; i < GEANY_MAX_KEYS; i++)
+	for (g = 0; g < keybinding_groups->len; g++)
 	{
-		if (keyval == keys[i]->key && state == keys[i]->mods)
+		KeyBindingGroup *group = g_ptr_array_index(keybinding_groups, g);
+
+		for (i = 0; i < group->count; i++)
 		{
-			if (keys[i]->cb_func == NULL)
-				return FALSE;	/* ignore the keybinding */
+			KeyBinding *kb = &group->keys[i];
 
-			/* call the corresponding callback function for this shortcut */
-			keys[i]->cb_func(i);
-			return TRUE;
+			if (keyval == kb->key && state == kb->mods)
+			{
+				if (kb->cb_func == NULL)
+					return FALSE;	/* ignore the keybinding */
+
+				/* call the corresponding callback function for this shortcut */
+				kb->cb_func(i);
+				return TRUE;
+			}
 		}
 	}
-	/* fixed keybindings can be overridden by user bindings */
+	/* fixed keybindings can be overridden by user bindings, so check them last */
 	if (check_fixed_kb(keyval, state))
 		return TRUE;
 	return FALSE;
 }
 
 
-/* simple convenience function to allocate and fill the struct */
-static KeyBinding *fill(KBCallback func, guint key, GdkModifierType mod, const gchar *name,
-		const gchar *label)
+KeyBinding *keybindings_lookup_item(guint group_id, guint cmd_id)
 {
-	KeyBinding *result;
+	KeyBindingGroup *group;
 
-	result = g_new0(KeyBinding, 1);
-	result->name = name;
-	result->label = label;
-	result->key = key;
-	result->mods = mod;
-	result->cb_func = func;
-	result->section = NULL;
+	g_return_val_if_fail(group_id < GEANY_KEYGROUP_COUNT, NULL);	/* plugins can't use this */
 
-	return result;
+	group = g_ptr_array_index(keybinding_groups, group_id);
+
+	g_return_val_if_fail(group && cmd_id < group->count, NULL);
+
+	return &group->keys[cmd_id];
 }
 
 
-/* Mimic a keybinding action */
-void keybindings_cmd(GeanyKeyCommand cmd_id)
+/* Mimic a (built-in only) keybinding action */
+void keybindings_send_command(gint group_id, gint cmd_id)
 {
-	keys[cmd_id]->cb_func(cmd_id);
+	KeyBinding *kb = keybindings_lookup_item(group_id, cmd_id);
+
+	if (kb)
+		kb->cb_func(cmd_id);
 }
 
 
@@ -1162,7 +1274,7 @@
 
 /* Common function for editing keybindings that don't change any text, and are
  * useful even when sci doesn't have focus. */
-static void cb_func_edit_global(guint key_id)
+static void cb_func_editing_global(guint key_id)
 {
 	gint idx = document_get_cur_idx();
 	gint cur_line;
@@ -1231,7 +1343,7 @@
 
 
 /* common function for editing keybindings, only valid when scintilla has focus. */
-static void cb_func_edit(guint key_id)
+static void cb_func_editing(guint key_id)
 {
 	gint idx = document_get_cur_idx();
 	GtkWidget *focusw = gtk_window_get_focus(GTK_WINDOW(app->window));
@@ -1274,30 +1386,6 @@
 		case GEANY_KEYS_EDIT_UNCOMMENTLINE:
 			on_menu_uncomment_line1_activate(NULL, NULL);
 			break;
-		case GEANY_KEYS_EDIT_AUTOCOMPLETE:
-			editor_start_auto_complete(idx, sci_get_current_position(doc_list[idx].sci), TRUE);
-			break;
-		case GEANY_KEYS_EDIT_CALLTIP:
-			editor_show_calltip(idx, -1);
-			break;
-		case GEANY_KEYS_EDIT_MACROLIST:
-			editor_show_macro_list(doc_list[idx].sci);
-			break;
-
-		case GEANY_KEYS_EDIT_SUPPRESSSNIPPETCOMPLETION:
-			switch (keys[GEANY_KEYS_EDIT_COMPLETESNIPPET]->key)
-			{
-				case GDK_space:
-					sci_add_text(doc_list[idx].sci, " ");
-					break;
-				case GDK_Tab:
-					sci_cmd(doc_list[idx].sci, SCI_TAB);
-					break;
-				default:
-					break;
-			}
-			break;
-
 		case GEANY_KEYS_EDIT_SELECTWORD:
 			editor_select_word(doc_list[idx].sci);
 			break;
@@ -1344,6 +1432,48 @@
 }
 
 
+/* common function for tags keybindings, only valid when scintilla has focus. */
+static void cb_func_tags(guint key_id)
+{
+	gint idx = document_get_cur_idx();
+	GtkWidget *focusw = gtk_window_get_focus(GTK_WINDOW(app->window));
+
+	/* tags keybindings only valid when scintilla widget has focus */
+	if (! DOC_IDX_VALID(idx) || focusw != GTK_WIDGET(doc_list[idx].sci)) return;
+
+	switch (key_id)
+	{
+		case GEANY_KEYS_EDIT_AUTOCOMPLETE:
+			editor_start_auto_complete(idx, sci_get_current_position(doc_list[idx].sci), TRUE);
+			break;
+		case GEANY_KEYS_EDIT_CALLTIP:
+			editor_show_calltip(idx, -1);
+			break;
+		case GEANY_KEYS_EDIT_MACROLIST:
+			editor_show_macro_list(doc_list[idx].sci);
+			break;
+		case GEANY_KEYS_EDIT_SUPPRESSSNIPPETCOMPLETION:
+		{
+			KeyBinding *kb = keybindings_lookup_item(GEANY_KEYGROUP_TAGS,
+				GEANY_KEYS_EDIT_COMPLETESNIPPET);
+
+			switch (kb->key)
+			{
+				case GDK_space:
+					sci_add_text(doc_list[idx].sci, " ");
+					break;
+				case GDK_Tab:
+					sci_cmd(doc_list[idx].sci, SCI_TAB);
+					break;
+				default:
+					break;
+			}
+			break;
+		}
+	}
+}
+
+
 static void cb_func_menu_replacetabs(G_GNUC_UNUSED guint key_id)
 {
 	on_replace_tabs_activate(NULL, NULL);

Modified: branches/plugin-keybindings/src/keybindings.h
===================================================================
--- branches/plugin-keybindings/src/keybindings.h	2008-02-29 18:56:41 UTC (rev 2296)
+++ branches/plugin-keybindings/src/keybindings.h	2008-02-29 19:30:28 UTC (rev 2297)
@@ -25,6 +25,15 @@
 #ifndef GEANY_KEYBINDINGS_H
 #define GEANY_KEYBINDINGS_H 1
 
+/* allowed modifier keys (especially NOT Caps lock, no Num lock) */
+#if GTK_CHECK_VERSION(2, 10, 0)
+# define GEANY_KEYS_MODIFIER_MASK (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK | \
+								   GDK_META_MASK | GDK_SUPER_MASK | GDK_HYPER_MASK)
+#else
+# define GEANY_KEYS_MODIFIER_MASK (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK)
+#endif
+
+
 typedef void (*KBCallback) (guint key_id);
 
 /* holds all user-definable key bindings */
@@ -39,25 +48,43 @@
 	/* function pointer to a callback function, just to keep the code in keypress event
 	 * callback function clear */
 	KBCallback cb_func;
-	/* string to use as a section name in the preferences dialog in keybinding treeview as well as
-	 * in the keybinding help dialog, set only for the first binding in the section */
-	gchar *section;
 } KeyBinding;
 
 
-/* allowed modifier keys (especially NOT Caps lock, no Num lock) */
-#if GTK_CHECK_VERSION(2, 10, 0)
-# define GEANY_KEYS_MODIFIER_MASK (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK | \
-								   GDK_META_MASK | GDK_SUPER_MASK | GDK_HYPER_MASK)
-#else
-# define GEANY_KEYS_MODIFIER_MASK (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK)
-#endif
+typedef struct KeyBindingGroup
+{
+	const gchar *name;		/* group name used for keyfile group */
+	const gchar *label;		/* group label in preferences dialog in keybinding treeview */
+	gsize count;			/* count of KeyBinding structs in keys */
+	KeyBinding *keys;		/* fixed array of KeyBinding structs */
+}
+KeyBindingGroup;
 
-/* Plugin API */
-typedef enum
+extern GPtrArray *keybinding_groups;	/* array of KeyBindingGroup pointers */
+
+
+/* Plugin API enums - append only to maintain plugin ABI */
+enum
 {
-	GEANY_KEYS_GROUP_FILE = 0,
-	GEANY_KEYS_MENU_NEW = GEANY_KEYS_GROUP_FILE,
+	GEANY_KEYGROUP_FILE,
+	GEANY_KEYGROUP_EDIT,
+	GEANY_KEYGROUP_SEARCH,
+	GEANY_KEYGROUP_VIEW,
+	GEANY_KEYGROUP_DOCUMENT,
+	GEANY_KEYGROUP_BUILD,
+	GEANY_KEYGROUP_TOOLS,
+	GEANY_KEYGROUP_HELP,
+	GEANY_KEYGROUP_FOCUS,
+	GEANY_KEYGROUP_NOTEBOOK,
+	GEANY_KEYGROUP_EDITING,
+	GEANY_KEYGROUP_TAGS,
+	GEANY_KEYGROUP_OTHER,
+	GEANY_KEYGROUP_COUNT
+};
+
+enum
+{
+	GEANY_KEYS_MENU_NEW,
 	GEANY_KEYS_MENU_OPEN,
 	GEANY_KEYS_MENU_OPENSELECTED,
 	GEANY_KEYS_MENU_SAVE,
@@ -68,9 +95,12 @@
 	GEANY_KEYS_MENU_CLOSEALL,
 	GEANY_KEYS_MENU_RELOADFILE,
 	GEANY_KEYS_MENU_PROJECTPROPERTIES,
+	GEANY_KEYS_FILE_COUNT
+};
 
-	GEANY_KEYS_GROUP_EDIT,
-	GEANY_KEYS_MENU_UNDO = GEANY_KEYS_GROUP_EDIT,
+enum
+{
+	GEANY_KEYS_MENU_UNDO,
 	GEANY_KEYS_MENU_REDO,
 	GEANY_KEYS_MENU_CUT,
 	GEANY_KEYS_MENU_COPY,
@@ -78,9 +108,12 @@
 	GEANY_KEYS_MENU_SELECTALL,
 	GEANY_KEYS_MENU_INSERTDATE,
 	GEANY_KEYS_MENU_PREFERENCES,
+	GEANY_KEYS_EDIT_COUNT
+};
 
-	GEANY_KEYS_GROUP_SEARCH,
-	GEANY_KEYS_MENU_FIND = GEANY_KEYS_GROUP_SEARCH,
+enum
+{
+	GEANY_KEYS_MENU_FIND,
 	GEANY_KEYS_MENU_FINDNEXT,
 	GEANY_KEYS_MENU_FINDPREVIOUS,
 	GEANY_KEYS_MENU_FINDINFILES,
@@ -89,23 +122,32 @@
 	GEANY_KEYS_MENU_FINDPREVSEL,
 	GEANY_KEYS_MENU_NEXTMESSAGE,
 	GEANY_KEYS_MENU_GOTOLINE,
+	GEANY_KEYS_SEARCH_COUNT
+};
 
-	GEANY_KEYS_GROUP_VIEW,
-	GEANY_KEYS_MENU_TOGGLEALL = GEANY_KEYS_GROUP_VIEW,
+enum
+{
+	GEANY_KEYS_MENU_TOGGLEALL,
 	GEANY_KEYS_MENU_FULLSCREEN,
 	GEANY_KEYS_MENU_MESSAGEWINDOW,
 	GEANY_KEYS_MENU_SIDEBAR,
 	GEANY_KEYS_MENU_ZOOMIN,
 	GEANY_KEYS_MENU_ZOOMOUT,
+	GEANY_KEYS_VIEW_COUNT
+};
 
-	GEANY_KEYS_GROUP_DOCUMENT,
-	GEANY_KEYS_MENU_REPLACETABS = GEANY_KEYS_GROUP_DOCUMENT,
+enum
+{
+	GEANY_KEYS_MENU_REPLACETABS,
 	GEANY_KEYS_MENU_FOLDALL,
 	GEANY_KEYS_MENU_UNFOLDALL,
 	GEANY_KEYS_RELOADTAGLIST,
+	GEANY_KEYS_DOCUMENT_COUNT
+};
 
-	GEANY_KEYS_GROUP_BUILD,
-	GEANY_KEYS_BUILD_COMPILE = GEANY_KEYS_GROUP_BUILD,
+enum
+{
+	GEANY_KEYS_BUILD_COMPILE,
 	GEANY_KEYS_BUILD_LINK,
 	GEANY_KEYS_BUILD_MAKE,
 	GEANY_KEYS_BUILD_MAKEOWNTARGET,
@@ -114,31 +156,46 @@
 	GEANY_KEYS_BUILD_RUN,
 	GEANY_KEYS_BUILD_RUN2,
 	GEANY_KEYS_BUILD_OPTIONS,
+	GEANY_KEYS_BUILD_COUNT
+};
 
-	GEANY_KEYS_GROUP_TOOLS,
-	GEANY_KEYS_MENU_OPENCOLORCHOOSER = GEANY_KEYS_GROUP_TOOLS,
+enum
+{
+	GEANY_KEYS_MENU_OPENCOLORCHOOSER,
 	GEANY_KEYS_MENU_INSERTSPECIALCHARS,
+	GEANY_KEYS_TOOLS_COUNT
+};
 
-	GEANY_KEYS_GROUP_HELP,
-	GEANY_KEYS_MENU_HELP = GEANY_KEYS_GROUP_HELP,
+enum
+{
+	GEANY_KEYS_MENU_HELP,
+	GEANY_KEYS_HELP_COUNT
+};
 
-	GEANY_KEYS_GROUP_FOCUS,
-	GEANY_KEYS_SWITCH_EDITOR = GEANY_KEYS_GROUP_FOCUS,
+enum
+{
+	GEANY_KEYS_SWITCH_EDITOR,
 	GEANY_KEYS_SWITCH_SCRIBBLE,
 	GEANY_KEYS_SWITCH_VTE,
 	GEANY_KEYS_SWITCH_SEARCH_BAR,
+	GEANY_KEYS_FOCUS_COUNT
+};
 
-	GEANY_KEYS_GROUP_TABS,
-	GEANY_KEYS_SWITCH_TABLEFT = GEANY_KEYS_GROUP_TABS,
+enum
+{
+	GEANY_KEYS_SWITCH_TABLEFT,
 	GEANY_KEYS_SWITCH_TABRIGHT,
 	GEANY_KEYS_SWITCH_TABLASTUSED,
 	GEANY_KEYS_MOVE_TABLEFT,
 	GEANY_KEYS_MOVE_TABRIGHT,
 	GEANY_KEYS_MOVE_TABFIRST,
 	GEANY_KEYS_MOVE_TABLAST,
+	GEANY_KEYS_NOTEBOOK_COUNT
+};
 
-	GEANY_KEYS_GROUP_EDITING,
-	GEANY_KEYS_EDIT_TOGGLECASE = GEANY_KEYS_GROUP_EDITING,
+enum
+{
+	GEANY_KEYS_EDIT_TOGGLECASE,
 	GEANY_KEYS_EDIT_DUPLICATELINE,
 	GEANY_KEYS_EDIT_DELETELINE,
 	GEANY_KEYS_EDIT_COPYLINE,
@@ -168,32 +225,37 @@
 	GEANY_KEYS_EDIT_INSERTALTWHITESPACE,
 	GEANY_KEYS_POPUP_FINDUSAGE,
 	GEANY_KEYS_POPUP_CONTEXTACTION,
+	GEANY_KEYS_EDITING_COUNT
+};
 
-	GEANY_KEYS_GROUP_TAGS,
-	GEANY_KEYS_EDIT_AUTOCOMPLETE = GEANY_KEYS_GROUP_TAGS,
+enum
+{
+	GEANY_KEYS_EDIT_AUTOCOMPLETE,
 	GEANY_KEYS_EDIT_CALLTIP,
 	GEANY_KEYS_EDIT_MACROLIST,
 	GEANY_KEYS_EDIT_COMPLETESNIPPET,
 	GEANY_KEYS_EDIT_SUPPRESSSNIPPETCOMPLETION,
 	GEANY_KEYS_POPUP_GOTOTAGDEFINITION,
 	GEANY_KEYS_POPUP_GOTOTAGDECLARATION,
+	GEANY_KEYS_TAGS_COUNT
+};
 
-	GEANY_KEYS_GROUP_OTHER,
-	GEANY_KEYS_NAV_FORWARD = GEANY_KEYS_GROUP_OTHER,
+enum
+{
+	GEANY_KEYS_NAV_FORWARD,
 	GEANY_KEYS_NAV_BACK,
-	GEANY_MAX_KEYS
-}
-GeanyKeyCommand;
+	GEANY_KEYS_OTHER_COUNT
+};
 
-extern KeyBinding *keys[GEANY_MAX_KEYS];
 
-
 void keybindings_init(void);
 
 void keybindings_free(void);
 
-void keybindings_cmd(GeanyKeyCommand cmd_id);
+void keybindings_send_command(gint group_id, gint cmd_id);
 
+KeyBinding *keybindings_lookup_item(guint group_id, guint cmd_id);
+
 /* just write the content of the keys array to the config file */
 void keybindings_write_to_file(void);
 

Modified: branches/plugin-keybindings/src/plugindata.h
===================================================================
--- branches/plugin-keybindings/src/plugindata.h	2008-02-29 18:56:41 UTC (rev 2296)
+++ branches/plugin-keybindings/src/plugindata.h	2008-02-29 19:30:28 UTC (rev 2297)
@@ -36,12 +36,12 @@
 
 /* The API version should be incremented whenever any plugin data types below are
  * modified or appended to. */
-static const gint api_version = 45;
+static const gint api_version = 46;
 
 /* The ABI version should be incremented whenever existing fields in the plugin
  * data types below have to be changed or reordered. It should stay the same if fields
  * are only appended, as this doesn't affect existing fields. */
-static const gint abi_version = 20;
+static const gint abi_version = 21;
 
 /* This performs runtime checks that try to ensure:
  * 1. Geany ABI data types are compatible with this plugin.
@@ -311,8 +311,7 @@
 
 typedef struct KeybindingFuncs
 {
-	/* See GeanyKeyCommand enum for cmd_id. */
-	void		(*send_command) (gint cmd_id);
+	void		(*send_command) (gint group_id, gint cmd_id);
 }
 KeybindingFuncs;
 

Modified: branches/plugin-keybindings/src/plugins.c
===================================================================
--- branches/plugin-keybindings/src/plugins.c	2008-02-29 18:56:41 UTC (rev 2296)
+++ branches/plugin-keybindings/src/plugins.c	2008-02-29 19:30:28 UTC (rev 2297)
@@ -53,11 +53,9 @@
 #include "encodings.h"
 #include "search.h"
 #include "highlighting.h"
+#include "keybindings.h"
 
-void keybindings_cmd(gint cmd_id);	/* don't require keybindings.h enum in plugindata.h */
 
-
-
 #ifdef G_OS_WIN32
 # define PLUGIN_EXT "dll"
 #else
@@ -189,7 +187,7 @@
 };
 
 static KeybindingFuncs keybindings_funcs = {
-	&keybindings_cmd
+	&keybindings_send_command
 };
 
 static TagManagerFuncs tagmanager_funcs = {

Modified: branches/plugin-keybindings/src/prefs.c
===================================================================
--- branches/plugin-keybindings/src/prefs.c	2008-02-29 18:56:41 UTC (rev 2296)
+++ branches/plugin-keybindings/src/prefs.c	2008-02-29 19:30:28 UTC (rev 2297)
@@ -73,7 +73,8 @@
 static void on_cell_edited(GtkCellRendererText *cellrenderertext, gchar *path, gchar *new_text, gpointer user_data);
 static gboolean on_keytype_dialog_response(GtkWidget *dialog, GdkEventKey *event, gpointer user_data);
 static void on_dialog_response(GtkWidget *dialog, gint response, gpointer user_data);
-static gboolean find_duplicate(guint idx, guint key, GdkModifierType mods, const gchar *action);
+static gboolean find_duplicate(KeyBinding *search_kb,
+		guint key, GdkModifierType mods, const gchar *action);
 static void on_toolbar_show_toggled(GtkToggleButton *togglebutton, gpointer user_data);
 static void on_show_notebook_tabs_toggled(GtkToggleButton *togglebutton, gpointer user_data);
 static void on_use_folding_toggled(GtkToggleButton *togglebutton, gpointer user_data);
@@ -127,25 +128,30 @@
 static void init_keybindings(void)
 {
 	GtkTreeIter parent, iter;
-	gint i;
-	gchar *key_string;
+	gsize g, i;
 
 	if (store == NULL)
 		init_kb_tree();
 
-	for (i = 0; i < GEANY_MAX_KEYS; i++)
+	for (g = 0; g < keybinding_groups->len; g++)
 	{
-		if (keys[i]->section != NULL)
+		KeyBindingGroup *group = g_ptr_array_index(keybinding_groups, g);
+
+		gtk_tree_store_append(store, &parent, NULL);
+		gtk_tree_store_set(store, &parent, KB_TREE_ACTION, group->label,
+			KB_TREE_INDEX, g, -1);
+
+		for (i = 0; i < group->count; i++)
 		{
-			gtk_tree_store_append(store, &parent, NULL);
-			gtk_tree_store_set(store, &parent, KB_TREE_ACTION, keys[i]->section, -1);
+			KeyBinding *kb = &group->keys[i];
+			gchar *key_string;
+
+			key_string = gtk_accelerator_name(kb->key, kb->mods);
+			gtk_tree_store_append(store, &iter, &parent);
+			gtk_tree_store_set(store, &iter, KB_TREE_ACTION, kb->label,
+				KB_TREE_SHORTCUT, key_string, KB_TREE_INDEX, i, -1);
+			g_free(key_string);
 		}
-
-		key_string = gtk_accelerator_name(keys[i]->key, keys[i]->mods);
-		gtk_tree_store_append(store, &iter, &parent);
-		gtk_tree_store_set(store, &iter, KB_TREE_ACTION, keys[i]->label,
-			KB_TREE_SHORTCUT, key_string, KB_TREE_INDEX, i, -1);
-		g_free(key_string);
 	}
 	gtk_tree_view_expand_all(GTK_TREE_VIEW(tree));
 }
@@ -1065,7 +1071,7 @@
 			return TRUE;
 		}
 
-		gtk_tree_model_get(model, &g_iter, 0, &name, -1);
+		gtk_tree_model_get(model, &g_iter, KB_TREE_ACTION, &name, -1);
 		if (name != NULL)
 		{
 			GtkWidget *dialog;
@@ -1103,33 +1109,48 @@
 }
 
 
+static KeyBinding *lookup_kb_from_iter(G_GNUC_UNUSED GtkTreeModel *model, GtkTreeIter *iter)
+{
+	guint group_idx, keybinding_idx;
+	GtkTreeIter parent;
+
+	/* get kb index */
+	gtk_tree_model_get(GTK_TREE_MODEL(store), iter, KB_TREE_INDEX, &keybinding_idx, -1);
+
+	/* lookup the parent to get group index */
+	gtk_tree_model_iter_parent(GTK_TREE_MODEL(store), &parent, iter);
+	gtk_tree_model_get(GTK_TREE_MODEL(store), iter, KB_TREE_INDEX, &group_idx, -1);
+
+	return keybindings_lookup_item(group_idx, keybinding_idx);
+}
+
+
 static void on_cell_edited(GtkCellRendererText *cellrenderertext, gchar *path, gchar *new_text, gpointer user_data)
 {
 	if (path != NULL && new_text != NULL)
 	{
-		guint idx;
+		GtkTreeIter iter;
 		guint lkey;
 		GdkModifierType lmods;
-		GtkTreeIter iter;
+		KeyBinding *kb;
 
 		gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(store), &iter, path);
 		if (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(store), &iter))
-			return;
+			return;	/* ignore group items */
 
 		gtk_accelerator_parse(new_text, &lkey, &lmods);
 
-		/* get index */
-		gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, 2, &idx, -1);
+		kb = lookup_kb_from_iter(GTK_TREE_MODEL(store), &iter);
 
-		if (find_duplicate(idx, lkey, lmods, new_text))
+		if (find_duplicate(kb, lkey, lmods, new_text))
 			return;
 
 		/* set the values here, because of the above check, setting it in
 		 * gtk_accelerator_parse would return a wrong key combination if it is duplicate */
-		keys[idx]->key = lkey;
-		keys[idx]->mods = lmods;
+		kb->key = lkey;
+		kb->mods = lmods;
 
-		gtk_tree_store_set(store, &iter, 1, new_text, -1);
+		gtk_tree_store_set(store, &iter, KB_TREE_SHORTCUT, new_text, -1);
 
 		edited = TRUE;
 	}
@@ -1155,29 +1176,28 @@
 }
 
 
-static void on_dialog_response(GtkWidget *dialog, gint response, gpointer iter)
+static void on_dialog_response(GtkWidget *dialog, gint response, G_GNUC_UNUSED gpointer iter)
 {
 	if (response == GTK_RESPONSE_ACCEPT)
 	{
-		guint idx;
 		guint lkey;
 		GdkModifierType lmods;
+		KeyBinding *kb;
 
-		/* get index */
-		gtk_tree_model_get(GTK_TREE_MODEL(store), &g_iter, 2, &idx, -1);
+		kb = lookup_kb_from_iter(GTK_TREE_MODEL(store), &g_iter);
 
 		gtk_accelerator_parse(gtk_label_get_text(GTK_LABEL(dialog_label)), &lkey, &lmods);
 
-		if (find_duplicate(idx, lkey, lmods, gtk_label_get_text(GTK_LABEL(dialog_label))))
+		if (find_duplicate(kb, lkey, lmods, gtk_label_get_text(GTK_LABEL(dialog_label))))
 			return;
 
 		/* set the values here, because of the above check, setting it in
 		 * gtk_accelerator_parse would return a wrong key combination if it is duplicate */
-		keys[idx]->key = lkey;
-		keys[idx]->mods = lmods;
+		kb->key = lkey;
+		kb->mods = lmods;
 
 		gtk_tree_store_set(store, &g_iter,
-				1, gtk_label_get_text(GTK_LABEL(dialog_label)), -1);
+			KB_TREE_SHORTCUT, gtk_label_get_text(GTK_LABEL(dialog_label)), -1);
 
 		g_free(dialog_key_name);
 		dialog_key_name = NULL;
@@ -1188,63 +1208,81 @@
 }
 
 
-static gboolean find_iter(guint i, GtkTreeIter *iter)
+/* Look for a (1st-level) child of parent whose KB_TREE_INDEX matches i,
+ * setting iter to point to the node if found.
+ * If parent is NULL, look for a parent node whose KB_TREE_INDEX matches i. */
+static gboolean find_child_iter(GtkTreeIter *parent, guint i, GtkTreeIter *iter)
 {
 	GtkTreeModel *model = GTK_TREE_MODEL(store);
 	guint idx;
-	GtkTreeIter parent;
 
-	if (! gtk_tree_model_get_iter_first(model, &parent))
-		return FALSE;	/* no items */
+	/* get first child of parent */
+	if (! gtk_tree_model_iter_children(model, iter, parent))
+		return FALSE;
 
-	while (TRUE)
+	while (TRUE)	/* foreach child */
 	{
-		if (! gtk_tree_model_iter_children(model, iter, &parent))
-			return FALSE;
-
-		while (TRUE)
-		{
-			gtk_tree_model_get(model, iter, 2, &idx, -1);
-			if (idx == i)
-				return TRUE;
-			if (! gtk_tree_model_iter_next(model, iter))
-				break;
-		}
-		if (! gtk_tree_model_iter_next(model, &parent))
-			return FALSE;
+		gtk_tree_model_get(model, iter, KB_TREE_INDEX, &idx, -1);
+		if (idx == i)
+			return TRUE;
+		if (! gtk_tree_model_iter_next(model, iter))
+			return FALSE;	/* no more children */
 	}
 }
 
 
+static void clear_tree_shortcut(gsize group_id, gsize keybinding_id)
+{
+	GtkTreeIter parent;
+	GtkTreeIter child;
+
+	/* find parent kb group */
+	if (! find_child_iter(NULL, group_id, &parent))
+		return;
+
+	/* find child kb node*/
+	if (! find_child_iter(&parent, group_id, &child))
+		return;
+
+	gtk_tree_store_set(store, &child, KB_TREE_SHORTCUT, NULL, -1);	/* clear shortcut */
+}
+
+
 /* test if the entered key combination is already used */
-static gboolean find_duplicate(guint idx, guint key, GdkModifierType mods, const gchar *action)
+static gboolean find_duplicate(KeyBinding *search_kb,
+		guint key, GdkModifierType mods, const gchar *action)
 {
-	guint i;
+	gsize g, i;
 
 	/* allow duplicate if there is no key combination */
 	if (key == 0 && mods == 0) return FALSE;
 
-	for (i = 0; i < GEANY_MAX_KEYS; i++)
+	for (g = 0; g < keybinding_groups->len; g++)
 	{
-		/* search another item with the same key,
-		 * but take not the key we are searching for(-> idx) */
-		if (keys[i]->key == key && keys[i]->mods == mods
-			&& ! (keys[i]->key == keys[idx]->key && keys[i]->mods == keys[idx]->mods))
+		KeyBindingGroup *group = g_ptr_array_index(keybinding_groups, g);
+
+		for (i = 0; i < group->count; i++)
 		{
-			if (dialogs_show_question_full(app->window, _("_Override"), GTK_STOCK_CANCEL,
-				_("Override that keybinding?"),
-				_("The combination '%s' is already used for \"%s\"."),
-				action, keys[i]->label))
+			KeyBinding *keys = group->keys;
+			KeyBinding *kb = &keys[i];
+
+			/* search another item with the same key,
+			 * but don't search the key we're looking for keys[idx] */
+			if (kb->key == key && kb->mods == mods
+				&& ! (kb->key == search_kb->key && kb->mods == search_kb->mods))
 			{
-				GtkTreeIter iter;
-
-				keys[i]->key = 0;
-				keys[i]->mods = 0;
-				if (find_iter(i, &iter))
-					gtk_tree_store_set(store, &iter, 1, NULL, -1);	/* clear item */
-				continue;
+				if (dialogs_show_question_full(app->window, _("_Override"), GTK_STOCK_CANCEL,
+					_("Override that keybinding?"),
+					_("The combination '%s' is already used for \"%s\"."),
+					action, kb->label))
+				{
+					kb->key = 0;
+					kb->mods = 0;
+					clear_tree_shortcut(g, i);
+					continue;
+				}
+				return TRUE;
 			}
-			return TRUE;
 		}
 	}
 	return FALSE;

Modified: branches/plugin-keybindings/src/tools.c
===================================================================
--- branches/plugin-keybindings/src/tools.c	2008-02-29 18:56:41 UTC (rev 2296)
+++ branches/plugin-keybindings/src/tools.c	2008-02-29 19:30:28 UTC (rev 2297)
@@ -361,6 +361,7 @@
 {
 	GtkWidget *item;
 	gint key_idx = -1;
+	KeyBinding *kb;
 
 	switch (idx)
 	{
@@ -369,10 +370,13 @@
 		case 2: key_idx = GEANY_KEYS_EDIT_SENDTOCMD3; break;
 	}
 
+	if (key_idx != -1)
+		kb = keybindings_lookup_item(GEANY_KEYGROUP_EDITING, key_idx);
+
 	item = gtk_menu_item_new_with_label(label);
 	if (key_idx != -1)
 		gtk_widget_add_accelerator(item, "activate", gtk_accel_group_new(),
-			keys[key_idx]->key, keys[key_idx]->mods, GTK_ACCEL_VISIBLE);
+			kb->key, kb->mods, GTK_ACCEL_VISIBLE);
 	gtk_container_add(GTK_CONTAINER(me), item);
 	gtk_widget_show(item);
 	g_signal_connect((gpointer) item, "activate", G_CALLBACK(cc_on_custom_command_activate),
@@ -381,7 +385,7 @@
 	item = gtk_menu_item_new_with_label(label);
 	if (key_idx != -1)
 		gtk_widget_add_accelerator(item, "activate", gtk_accel_group_new(),
-			keys[key_idx]->key, keys[key_idx]->mods, GTK_ACCEL_VISIBLE);
+			kb->key, kb->mods, GTK_ACCEL_VISIBLE);
 	gtk_container_add(GTK_CONTAINER(mp), item);
 	gtk_widget_show(item);
 	g_signal_connect((gpointer) item, "activate", G_CALLBACK(cc_on_custom_command_activate),


This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.



More information about the Commits mailing list