[geany/geany-plugins] 84fa33: geanyctags: convert line ends from CRLF->LF
Jiří Techet
git-noreply at xxxxx
Mon Oct 20 17:12:23 UTC 2014
Branch: refs/heads/master
Author: Jiří Techet <techet at gmail.com>
Committer: Jiří Techet <techet at gmail.com>
Date: Mon, 20 Oct 2014 17:12:23 UTC
Commit: 84fa336fc5fb757775d4bebada24cb6b6544a9d7
https://github.com/geany/geany-plugins/commit/84fa336fc5fb757775d4bebada24cb6b6544a9d7
Log Message:
-----------
geanyctags: convert line ends from CRLF->LF
WTF?
Modified Paths:
--------------
geanyctags/src/geanyctags.c
Modified: geanyctags/src/geanyctags.c
1350 lines changed, 675 insertions(+), 675 deletions(-)
===================================================================
@@ -1,675 +1,675 @@
-/*
- * Copyright 2010-2014 Jiri Techet <techet at gmail.com>
- *
- * 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.
- */
-
-#include <sys/time.h>
-#include <string.h>
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include <geanyplugin.h>
-
-#include "readtags.h"
-
-#include <errno.h>
-#include <glib/gstdio.h>
-
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <signal.h>
-
-
-/* Pre-GTK 2.24 compatibility */
-#ifndef GTK_COMBO_BOX_TEXT
-# define GTK_COMBO_BOX_TEXT GTK_COMBO_BOX
-# define gtk_combo_box_text_new gtk_combo_box_new_text
-# define gtk_combo_box_text_append_text gtk_combo_box_append_text
-#endif
-
-
-PLUGIN_VERSION_CHECK(211)
-PLUGIN_SET_INFO("GeanyCtags",
- _("Ctags generation and search plugin for geany projects"),
- VERSION,
- "Jiri Techet <techet at gmail.com>")
-
-GeanyPlugin *geany_plugin;
-GeanyData *geany_data;
-GeanyFunctions *geany_functions;
-
-
-static GtkWidget *s_context_fdec_item, *s_context_fdef_item, *s_context_sep_item,
- *s_gt_item, *s_sep_item, *s_ft_item;
-
-static struct
-{
- GtkWidget *widget;
-
- GtkWidget *combo;
- GtkWidget *combo_match;
- GtkWidget *case_sensitive;
- GtkWidget *declaration;
-} s_ft_dialog = {NULL, NULL, NULL, NULL, NULL};
-
-
-enum
-{
- KB_FIND_TAG,
- KB_GENERATE_TAGS,
- KB_COUNT
-};
-
-
-
-void plugin_init(G_GNUC_UNUSED GeanyData * data);
-void plugin_cleanup(void);
-
-
-static void set_widgets_sensitive(gboolean sensitive)
-{
- gtk_widget_set_sensitive(GTK_WIDGET(s_gt_item), sensitive);
- gtk_widget_set_sensitive(GTK_WIDGET(s_context_fdec_item), sensitive);
- gtk_widget_set_sensitive(GTK_WIDGET(s_context_fdef_item), sensitive);
-}
-
-static void on_project_open(G_GNUC_UNUSED GObject * obj, GKeyFile * config, G_GNUC_UNUSED gpointer user_data)
-{
- set_widgets_sensitive(TRUE);
-}
-
-static void on_project_close(G_GNUC_UNUSED GObject * obj, G_GNUC_UNUSED gpointer user_data)
-{
- set_widgets_sensitive(FALSE);
-}
-
-static void on_project_save(G_GNUC_UNUSED GObject * obj, GKeyFile * config, G_GNUC_UNUSED gpointer user_data)
-{
- set_widgets_sensitive(TRUE);
-}
-
-PluginCallback plugin_callbacks[] = {
- {"project-open", (GCallback) & on_project_open, TRUE, NULL},
- {"project-close", (GCallback) & on_project_close, TRUE, NULL},
- {"project-save", (GCallback) & on_project_save, TRUE, NULL},
- {NULL, NULL, FALSE, NULL}
-};
-
-
-static void spawn_cmd(const gchar *cmd, const gchar *dir)
-{
- GError *error = NULL;
- gchar **argv;
- gchar *working_dir;
- gchar *utf8_working_dir;
- gchar *utf8_cmd_string;
- gchar *out;
- gint exitcode;
- gboolean success;
-
-#ifndef G_OS_WIN32
- /* run within shell so we can use pipes */
- argv = g_new0(gchar *, 4);
- argv[0] = g_strdup("/bin/sh");
- argv[1] = g_strdup("-c");
- argv[2] = g_strdup(cmd);
- argv[3] = NULL;
-#else
- /* no shell on windows */
- argv = g_new0(gchar *, 2);
- argv[0] = g_strdup(cmd);
- argv[1] = NULL;
-#endif
-
- utf8_cmd_string = utils_get_utf8_from_locale(cmd);
- utf8_working_dir = g_strdup(dir);
- working_dir = utils_get_locale_from_utf8(utf8_working_dir);
-
- msgwin_clear_tab(MSG_MESSAGE);
- msgwin_switch_tab(MSG_MESSAGE, TRUE);
- msgwin_msg_add(COLOR_BLUE, -1, NULL, _("%s (in directory: %s)"), utf8_cmd_string, utf8_working_dir);
- g_free(utf8_working_dir);
- g_free(utf8_cmd_string);
-
-#ifndef G_OS_WIN32
- success = utils_spawn_sync(working_dir, argv, NULL, G_SPAWN_SEARCH_PATH,
- NULL, NULL, NULL, &out, &exitcode, &error);
-#else
- success = utils_spawn_sync(working_dir, argv, NULL, G_SPAWN_SEARCH_PATH,
- NULL, NULL, &out, NULL, &exitcode, &error);
-#endif
- if (!success || exitcode != 0)
- {
- if (error != NULL)
- {
- msgwin_msg_add(COLOR_RED, -1, NULL, _("Process execution failed (%s)"), error->message);
- g_error_free(error);
- }
- msgwin_msg_add(COLOR_RED, -1, NULL, "%s", out);
- }
- else
- {
- msgwin_msg_add(COLOR_BLACK, -1, NULL, "%s", out);
- }
-
- g_strfreev(argv);
- g_free(working_dir);
- g_free(out);
-}
-
-static gchar *get_tags_filename(void)
-{
- gchar *ret = NULL;
-
- if (geany_data->app->project)
- {
- ret = utils_remove_ext_from_filename(geany_data->app->project->file_name);
- SETPTR(ret, g_strconcat(ret, ".tags", NULL));
- }
- return ret;
-}
-
-static gchar *generate_find_string(GeanyProject *prj)
-{
- gchar *ret;
-
- ret = g_strdup("find . -not -path '*/\\.*'");
-
- if (!EMPTY(prj->file_patterns))
- {
- guint i;
-
- SETPTR(ret, g_strconcat(ret, " \\( -name \"", prj->file_patterns[0], "\"", NULL));
- for (i = 1; prj->file_patterns[i]; i++)
- SETPTR(ret, g_strconcat(ret, " -o -name \"", prj->file_patterns[i], "\"", NULL));
- SETPTR(ret, g_strconcat(ret, " \\)", NULL));
- }
- return ret;
-}
-
-
-static void
-on_generate_tags(GtkMenuItem *menuitem, gpointer user_data)
-{
- GeanyProject *prj;
-
- prj = geany_data->app->project;
- if (prj)
- {
- gchar *cmd;
- gchar *tag_filename;
-
- tag_filename = get_tags_filename();
-
-#ifndef G_OS_WIN32
- gchar *find_string = generate_find_string(prj);
- cmd = g_strconcat(find_string,
- " | ctags --totals --fields=fKsSt --extra=-fq --c-kinds=+p --sort=foldcase --excmd=number -L - -f ",
- tag_filename, NULL);
- g_free(find_string);
-#else
- /* We don't have find and | on windows, generate tags for all files in the project (-R recursively) */
-
- /* Unfortunately, there's a bug in ctags - when run with -R, the first line is
- * empty, ctags doesn't recognize the tags file as a valid ctags file and
- * refuses to overwrite it. Therefore, we need to delete the tags file manually. */
- g_unlink(tag_filename);
-
- cmd = g_strconcat("ctags.exe -R --totals --fields=fKsSt --extra=-fq --c-kinds=+p --sort=foldcase --excmd=number -f ",
- tag_filename, NULL);
-#endif
-
- spawn_cmd(cmd, prj->base_path);
-
- g_free(cmd);
- g_free(tag_filename);
- }
-}
-
-static void show_entry(tagEntry *entry)
-{
- const gchar *kind;
- const gchar *signature;
- const gchar *scope;
- const gchar *file;
- const gchar *name;
- gchar *scope_str;
- gchar *kind_str;
-
- file = entry->file;
- if (!file)
- file = "";
-
- name = entry->name;
- if (!name)
- name = "";
-
- signature = tagsField(entry, "signature");
- if (!signature)
- signature = "";
-
- scope = tagsField(entry, "class");
- if (!scope)
- scope = tagsField(entry, "struct");
- if (!scope)
- scope = tagsField(entry, "union");
- if (!scope)
- scope = tagsField(entry, "enum");
-
- if (scope)
- scope_str = g_strconcat(scope, "::", NULL);
- else
- scope_str = g_strdup("");
-
- kind = entry->kind;
- if (kind)
- {
- kind_str = g_strconcat(kind, ": ", NULL);
- SETPTR(kind_str, g_strdup_printf("%-14s", kind_str));
- }
- else
- kind_str = g_strdup("");
-
- msgwin_msg_add(COLOR_BLACK, -1, NULL, "%s:%lu:\n %s%s%s%s", file,
- entry->address.lineNumber, kind_str, scope_str, name, signature);
-
- g_free(scope_str);
- g_free(kind_str);
-}
-
-
-static gchar *get_selection(void)
-{
- gchar *ret = NULL;
- GeanyDocument *doc = document_get_current();
- GeanyEditor *editor;
-
- if (!doc)
- return NULL;
-
- editor = doc->editor;
-
- if (sci_has_selection(editor->sci))
- ret = sci_get_selection_contents(editor->sci);
- else
- ret = editor_get_word_at_pos(editor, -1, GEANY_WORDCHARS);
-
- return ret;
-}
-
-/* TODO: Not possible to do it the way below because some of the API is private
- * in Geany. This means the cursor has to be placed at the symbol first and
- * then right-clicked (right-clicking without having the cursor at the symbol
- * doesn't work) */
-
-/*
-static gchar *get_selection()
-{
- gchar *ret = NULL;
- GeanyDocument *doc = document_get_current();
-
- if (!doc)
- return NULL;
-
- if (!sci_has_selection(doc->editor->sci))
- sci_set_current_position(doc->editor->sci, editor_info.click_pos, FALSE);
-
- if (sci_has_selection(doc->editor->sci))
- return sci_get_selection_contents(doc->editor->sci);
-
- gint len = sci_get_selected_text_length(doc->editor->sci);
-
- ret = g_malloc(len + 1);
- sci_get_selected_text(doc->editor->sci, ret);
-
- editor_find_current_word(doc->editor, -1,
- editor_info.current_word, GEANY_MAX_WORD_LENGTH, NULL);
-
- return editor_info.current_word != 0 ? g_strdup(editor_info.current_word) : NULL;
-}
-*/
-
-typedef enum
-{
- MATCH_FULL,
- MATCH_PREFIX,
- MATCH_PATTERN
-} MatchType;
-
-static gboolean find_first(tagFile *tf, tagEntry *entry, const gchar *name, MatchType match_type)
-{
- gboolean ret;
-
- if (match_type == MATCH_PATTERN)
- ret = tagsFirst(tf, entry) == TagSuccess;
- else
- {
- int options = TAG_IGNORECASE;
-
- options |= match_type == MATCH_PREFIX ? TAG_PARTIALMATCH : TAG_FULLMATCH;
- ret = tagsFind(tf, entry, name, options) == TagSuccess;
- }
- return ret;
-}
-
-static gboolean find_next(tagFile *tf, tagEntry *entry, MatchType match_type)
-{
- gboolean ret;
-
- if (match_type == MATCH_PATTERN)
- ret = tagsNext(tf, entry) == TagSuccess;
- else
- ret = tagsFindNext(tf, entry) == TagSuccess;
- return ret;
-}
-
-static gboolean filter_tag(tagEntry *entry, GPatternSpec *name, gboolean declaration, gboolean case_sensitive)
-{
- gboolean filter = TRUE;
- gchar *entry_name;
-
- if (!EMPTY(entry->kind))
- {
- gboolean is_prototype;
-
- is_prototype = g_strcmp0(entry->kind, "prototype") == 0;
- filter = (declaration && !is_prototype) || (!declaration && is_prototype);
- if (filter)
- return TRUE;
- }
-
- if (case_sensitive)
- entry_name = g_strdup(entry->name);
- else
- entry_name = g_utf8_strdown(entry->name, -1);
-
- filter = !g_pattern_match_string(name, entry_name);
-
- g_free(entry_name);
-
- return filter;
-}
-
-static void find_tags(const gchar *name, gboolean declaration, gboolean case_sensitive, MatchType match_type)
-{
- tagFile *tf;
- GeanyProject *prj;
- gchar *tag_filename = NULL;
- tagEntry entry;
- tagFileInfo info;
-
- prj = geany_data->app->project;
- if (!prj)
- return;
-
- msgwin_clear_tab(MSG_MESSAGE);
- msgwin_set_messages_dir(prj->base_path);
-
- tag_filename = get_tags_filename();
- tf = tagsOpen(tag_filename, &info);
-
- if (tf)
- {
- if (find_first(tf, &entry, name, match_type))
- {
- GPatternSpec *name_pat;
- gchar *name_case;
- gchar *path = NULL;
- gint num = 0;
-
- if (case_sensitive)
- name_case = g_strdup(name);
- else
- name_case = g_utf8_strdown(name, -1);
-
- SETPTR(name_case, g_strconcat("*", name_case, "*", NULL));
- name_pat = g_pattern_spec_new(name_case);
-
- if (!filter_tag(&entry, name_pat, declaration, case_sensitive))
- {
- path = g_build_filename(prj->base_path, entry.file, NULL);
- show_entry(&entry);
- num++;
- }
-
- while (find_next(tf, &entry, match_type))
- {
- if (!filter_tag(&entry, name_pat, declaration, case_sensitive))
- {
- if (!path)
- path = g_build_filename(prj->base_path, entry.file, NULL);
- show_entry(&entry);
- num++;
- }
- }
-
- if (num == 1)
- {
- GeanyDocument *doc = document_open_file(path, FALSE, NULL, NULL);
- if (doc != NULL)
- {
- navqueue_goto_line(document_get_current(), doc, entry.address.lineNumber);
- gtk_widget_grab_focus(GTK_WIDGET(doc->editor->sci));
- }
- }
-
- g_pattern_spec_free(name_pat);
- g_free(name_case);
- g_free(path);
- }
-
- tagsClose(tf);
- }
-
- msgwin_switch_tab(MSG_MESSAGE, TRUE);
-
- g_free(tag_filename);
-}
-
-static void on_find_declaration(GtkMenuItem *menuitem, gpointer user_data)
-{
- gchar *name;
-
- name = get_selection();
- if (name)
- find_tags(name, TRUE, TRUE, MATCH_FULL);
- g_free(name);
-}
-
-static void on_find_definition(GtkMenuItem *menuitem, gpointer user_data)
-{
- gchar *name;
-
- name = get_selection();
- if (name)
- find_tags(name, FALSE, TRUE, MATCH_FULL);
- g_free(name);
-}
-
-static void create_dialog_find_file(void)
-{
- GtkWidget *label, *vbox, *ebox, *entry;
- GtkSizeGroup *size_group;
-
- if (s_ft_dialog.widget)
- return;
-
- s_ft_dialog.widget = gtk_dialog_new_with_buttons(
- _("Find Tag"), GTK_WINDOW(geany->main_widgets->window),
- GTK_DIALOG_DESTROY_WITH_PARENT,
- GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL);
- gtk_dialog_add_button(GTK_DIALOG(s_ft_dialog.widget), "gtk-find", GTK_RESPONSE_ACCEPT);
- gtk_dialog_set_default_response(GTK_DIALOG(s_ft_dialog.widget), GTK_RESPONSE_ACCEPT);
-
- vbox = ui_dialog_vbox_new(GTK_DIALOG(s_ft_dialog.widget));
- gtk_box_set_spacing(GTK_BOX(vbox), 9);
-
- size_group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
-
- label = gtk_label_new(_("Search for:"));
- gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
- gtk_size_group_add_widget(size_group, label);
-
- s_ft_dialog.combo = gtk_combo_box_text_new_with_entry();
- entry = gtk_bin_get_child(GTK_BIN(s_ft_dialog.combo));
-
- ui_entry_add_clear_icon(GTK_ENTRY(entry));
- gtk_entry_set_width_chars(GTK_ENTRY(entry), 40);
- gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry);
- ui_entry_add_clear_icon(GTK_ENTRY(entry));
- gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
-
- ebox = gtk_hbox_new(FALSE, 6);
- gtk_box_pack_start(GTK_BOX(ebox), label, FALSE, FALSE, 0);
- gtk_box_pack_start(GTK_BOX(ebox), s_ft_dialog.combo, TRUE, TRUE, 0);
- gtk_box_pack_start(GTK_BOX(vbox), ebox, TRUE, FALSE, 0);
-
- label = gtk_label_new(_("Match type:"));
- gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
- gtk_size_group_add_widget(size_group, label);
-
- s_ft_dialog.combo_match = gtk_combo_box_text_new();
- gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(s_ft_dialog.combo_match), "full");
- gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(s_ft_dialog.combo_match), "prefix");
- gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(s_ft_dialog.combo_match), "pattern");
- gtk_combo_box_set_active(GTK_COMBO_BOX(s_ft_dialog.combo_match), 1);
- gtk_label_set_mnemonic_widget(GTK_LABEL(label), s_ft_dialog.combo_match);
-
- ebox = gtk_hbox_new(FALSE, 6);
- gtk_box_pack_start(GTK_BOX(ebox), label, FALSE, FALSE, 0);
- gtk_box_pack_start(GTK_BOX(ebox), s_ft_dialog.combo_match, TRUE, TRUE, 0);
- gtk_box_pack_start(GTK_BOX(vbox), ebox, TRUE, FALSE, 0);
-
- s_ft_dialog.case_sensitive = gtk_check_button_new_with_mnemonic(_("C_ase sensitive"));
- gtk_button_set_focus_on_click(GTK_BUTTON(s_ft_dialog.case_sensitive), FALSE);
-
- s_ft_dialog.declaration = gtk_check_button_new_with_mnemonic(_("_Declaration"));
- gtk_button_set_focus_on_click(GTK_BUTTON(s_ft_dialog.declaration), FALSE);
-
- g_object_unref(G_OBJECT(size_group)); /* auto destroy the size group */
-
- gtk_container_add(GTK_CONTAINER(vbox), s_ft_dialog.case_sensitive);
- gtk_container_add(GTK_CONTAINER(vbox), s_ft_dialog.declaration);
- gtk_widget_show_all(vbox);
-}
-
-static void on_find_tag(GtkMenuItem *menuitem, gpointer user_data)
-{
- gchar *selection;
- GtkWidget *entry;
-
- create_dialog_find_file();
-
- entry = gtk_bin_get_child(GTK_BIN(s_ft_dialog.combo));
-
- selection = get_selection();
- if (selection)
- gtk_entry_set_text(GTK_ENTRY(entry), selection);
- g_free(selection);
-
- gtk_widget_grab_focus(entry);
-
- if (gtk_dialog_run(GTK_DIALOG(s_ft_dialog.widget)) == GTK_RESPONSE_ACCEPT)
- {
- const gchar *name;
- gboolean case_sensitive, declaration;
- MatchType match_type;
-
- name = gtk_entry_get_text(GTK_ENTRY(entry));
- case_sensitive = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(s_ft_dialog.case_sensitive));
- declaration = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(s_ft_dialog.declaration));
- match_type = gtk_combo_box_get_active(GTK_COMBO_BOX(s_ft_dialog.combo_match));
-
- ui_combo_box_add_to_history(GTK_COMBO_BOX_TEXT(s_ft_dialog.combo), name, 0);
-
- find_tags(name, declaration, case_sensitive, match_type);
- }
-
- gtk_widget_hide(s_ft_dialog.widget);
-}
-
-static gboolean kb_callback(guint key_id)
-{
- switch (key_id)
- {
- case KB_FIND_TAG:
- on_find_tag(NULL, NULL);
- return TRUE;
- case KB_GENERATE_TAGS:
- on_generate_tags(NULL, NULL);
- return TRUE;
- }
- return FALSE;
-}
-
-void plugin_init(G_GNUC_UNUSED GeanyData * data)
-{
- GeanyKeyGroup *key_group = plugin_set_key_group(geany_plugin, "GeanyCtags", KB_COUNT, kb_callback);
-
- s_context_sep_item = gtk_separator_menu_item_new();
- gtk_widget_show(s_context_sep_item);
- gtk_menu_shell_prepend(GTK_MENU_SHELL(geany->main_widgets->editor_menu), s_context_sep_item);
-
- s_context_fdec_item = gtk_menu_item_new_with_mnemonic(_("Find Tag Declaration (geanyctags)"));
- gtk_widget_show(s_context_fdec_item);
- gtk_menu_shell_prepend(GTK_MENU_SHELL(geany->main_widgets->editor_menu), s_context_fdec_item);
- g_signal_connect((gpointer) s_context_fdec_item, "activate", G_CALLBACK(on_find_declaration), NULL);
-
- s_context_fdef_item = gtk_menu_item_new_with_mnemonic(_("Find Tag Definition (geanyctags)"));
- gtk_widget_show(s_context_fdef_item);
- gtk_menu_shell_prepend(GTK_MENU_SHELL(geany->main_widgets->editor_menu), s_context_fdef_item);
- g_signal_connect((gpointer) s_context_fdef_item, "activate", G_CALLBACK(on_find_definition), NULL);
-
- s_sep_item = gtk_separator_menu_item_new();
- gtk_widget_show(s_sep_item);
- gtk_container_add(GTK_CONTAINER(geany->main_widgets->project_menu), s_sep_item);
-
- s_gt_item = gtk_menu_item_new_with_mnemonic(_("Generate tags"));
- gtk_widget_show(s_gt_item);
- gtk_container_add(GTK_CONTAINER(geany->main_widgets->project_menu), s_gt_item);
- g_signal_connect((gpointer) s_gt_item, "activate", G_CALLBACK(on_generate_tags), NULL);
- keybindings_set_item(key_group, KB_GENERATE_TAGS, NULL,
- 0, 0, "generate_tags", _("Generate tags"), s_gt_item);
-
- s_ft_item = gtk_menu_item_new_with_mnemonic(_("Find tag"));
- gtk_widget_show(s_ft_item);
- gtk_container_add(GTK_CONTAINER(geany->main_widgets->project_menu), s_ft_item);
- g_signal_connect((gpointer) s_ft_item, "activate", G_CALLBACK(on_find_tag), NULL);
- keybindings_set_item(key_group, KB_FIND_TAG, NULL,
- 0, 0, "find_tag", _("Find tag"), s_ft_item);
-
- set_widgets_sensitive(FALSE);
-}
-
-void plugin_cleanup(void)
-{
- gtk_widget_destroy(s_context_fdec_item);
- gtk_widget_destroy(s_context_fdef_item);
- gtk_widget_destroy(s_context_sep_item);
-
- gtk_widget_destroy(s_ft_item);
- gtk_widget_destroy(s_gt_item);
- gtk_widget_destroy(s_sep_item);
-
- if (s_ft_dialog.widget)
- gtk_widget_destroy(s_ft_dialog.widget);
- s_ft_dialog.widget = NULL;
-}
+/*
+ * Copyright 2010-2014 Jiri Techet <techet at gmail.com>
+ *
+ * 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.
+ */
+
+#include <sys/time.h>
+#include <string.h>
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <geanyplugin.h>
+
+#include "readtags.h"
+
+#include <errno.h>
+#include <glib/gstdio.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <signal.h>
+
+
+/* Pre-GTK 2.24 compatibility */
+#ifndef GTK_COMBO_BOX_TEXT
+# define GTK_COMBO_BOX_TEXT GTK_COMBO_BOX
+# define gtk_combo_box_text_new gtk_combo_box_new_text
+# define gtk_combo_box_text_append_text gtk_combo_box_append_text
+#endif
+
+
+PLUGIN_VERSION_CHECK(211)
+PLUGIN_SET_INFO("GeanyCtags",
+ _("Ctags generation and search plugin for geany projects"),
+ VERSION,
+ "Jiri Techet <techet at gmail.com>")
+
+GeanyPlugin *geany_plugin;
+GeanyData *geany_data;
+GeanyFunctions *geany_functions;
+
+
+static GtkWidget *s_context_fdec_item, *s_context_fdef_item, *s_context_sep_item,
+ *s_gt_item, *s_sep_item, *s_ft_item;
+
+static struct
+{
+ GtkWidget *widget;
+
+ GtkWidget *combo;
+ GtkWidget *combo_match;
+ GtkWidget *case_sensitive;
+ GtkWidget *declaration;
+} s_ft_dialog = {NULL, NULL, NULL, NULL, NULL};
+
+
+enum
+{
+ KB_FIND_TAG,
+ KB_GENERATE_TAGS,
+ KB_COUNT
+};
+
+
+
+void plugin_init(G_GNUC_UNUSED GeanyData * data);
+void plugin_cleanup(void);
+
+
+static void set_widgets_sensitive(gboolean sensitive)
+{
+ gtk_widget_set_sensitive(GTK_WIDGET(s_gt_item), sensitive);
+ gtk_widget_set_sensitive(GTK_WIDGET(s_context_fdec_item), sensitive);
+ gtk_widget_set_sensitive(GTK_WIDGET(s_context_fdef_item), sensitive);
+}
+
+static void on_project_open(G_GNUC_UNUSED GObject * obj, GKeyFile * config, G_GNUC_UNUSED gpointer user_data)
+{
+ set_widgets_sensitive(TRUE);
+}
+
+static void on_project_close(G_GNUC_UNUSED GObject * obj, G_GNUC_UNUSED gpointer user_data)
+{
+ set_widgets_sensitive(FALSE);
+}
+
+static void on_project_save(G_GNUC_UNUSED GObject * obj, GKeyFile * config, G_GNUC_UNUSED gpointer user_data)
+{
+ set_widgets_sensitive(TRUE);
+}
+
+PluginCallback plugin_callbacks[] = {
+ {"project-open", (GCallback) & on_project_open, TRUE, NULL},
+ {"project-close", (GCallback) & on_project_close, TRUE, NULL},
+ {"project-save", (GCallback) & on_project_save, TRUE, NULL},
+ {NULL, NULL, FALSE, NULL}
+};
+
+
+static void spawn_cmd(const gchar *cmd, const gchar *dir)
+{
+ GError *error = NULL;
+ gchar **argv;
+ gchar *working_dir;
+ gchar *utf8_working_dir;
+ gchar *utf8_cmd_string;
+ gchar *out;
+ gint exitcode;
+ gboolean success;
+
+#ifndef G_OS_WIN32
+ /* run within shell so we can use pipes */
+ argv = g_new0(gchar *, 4);
+ argv[0] = g_strdup("/bin/sh");
+ argv[1] = g_strdup("-c");
+ argv[2] = g_strdup(cmd);
+ argv[3] = NULL;
+#else
+ /* no shell on windows */
+ argv = g_new0(gchar *, 2);
+ argv[0] = g_strdup(cmd);
+ argv[1] = NULL;
+#endif
+
+ utf8_cmd_string = utils_get_utf8_from_locale(cmd);
+ utf8_working_dir = g_strdup(dir);
+ working_dir = utils_get_locale_from_utf8(utf8_working_dir);
+
+ msgwin_clear_tab(MSG_MESSAGE);
+ msgwin_switch_tab(MSG_MESSAGE, TRUE);
+ msgwin_msg_add(COLOR_BLUE, -1, NULL, _("%s (in directory: %s)"), utf8_cmd_string, utf8_working_dir);
+ g_free(utf8_working_dir);
+ g_free(utf8_cmd_string);
+
+#ifndef G_OS_WIN32
+ success = utils_spawn_sync(working_dir, argv, NULL, G_SPAWN_SEARCH_PATH,
+ NULL, NULL, NULL, &out, &exitcode, &error);
+#else
+ success = utils_spawn_sync(working_dir, argv, NULL, G_SPAWN_SEARCH_PATH,
+ NULL, NULL, &out, NULL, &exitcode, &error);
+#endif
+ if (!success || exitcode != 0)
+ {
+ if (error != NULL)
+ {
+ msgwin_msg_add(COLOR_RED, -1, NULL, _("Process execution failed (%s)"), error->message);
+ g_error_free(error);
+ }
+ msgwin_msg_add(COLOR_RED, -1, NULL, "%s", out);
+ }
+ else
+ {
+ msgwin_msg_add(COLOR_BLACK, -1, NULL, "%s", out);
+ }
+
+ g_strfreev(argv);
+ g_free(working_dir);
+ g_free(out);
+}
+
+static gchar *get_tags_filename(void)
+{
+ gchar *ret = NULL;
+
+ if (geany_data->app->project)
+ {
+ ret = utils_remove_ext_from_filename(geany_data->app->project->file_name);
+ SETPTR(ret, g_strconcat(ret, ".tags", NULL));
+ }
+ return ret;
+}
+
+static gchar *generate_find_string(GeanyProject *prj)
+{
+ gchar *ret;
+
+ ret = g_strdup("find . -not -path '*/\\.*'");
+
+ if (!EMPTY(prj->file_patterns))
+ {
+ guint i;
+
+ SETPTR(ret, g_strconcat(ret, " \\( -name \"", prj->file_patterns[0], "\"", NULL));
+ for (i = 1; prj->file_patterns[i]; i++)
+ SETPTR(ret, g_strconcat(ret, " -o -name \"", prj->file_patterns[i], "\"", NULL));
+ SETPTR(ret, g_strconcat(ret, " \\)", NULL));
+ }
+ return ret;
+}
+
+
+static void
+on_generate_tags(GtkMenuItem *menuitem, gpointer user_data)
+{
+ GeanyProject *prj;
+
+ prj = geany_data->app->project;
+ if (prj)
+ {
+ gchar *cmd;
+ gchar *tag_filename;
+
+ tag_filename = get_tags_filename();
+
+#ifndef G_OS_WIN32
+ gchar *find_string = generate_find_string(prj);
+ cmd = g_strconcat(find_string,
+ " | ctags --totals --fields=fKsSt --extra=-fq --c-kinds=+p --sort=foldcase --excmd=number -L - -f ",
+ tag_filename, NULL);
+ g_free(find_string);
+#else
+ /* We don't have find and | on windows, generate tags for all files in the project (-R recursively) */
+
+ /* Unfortunately, there's a bug in ctags - when run with -R, the first line is
+ * empty, ctags doesn't recognize the tags file as a valid ctags file and
+ * refuses to overwrite it. Therefore, we need to delete the tags file manually. */
+ g_unlink(tag_filename);
+
+ cmd = g_strconcat("ctags.exe -R --totals --fields=fKsSt --extra=-fq --c-kinds=+p --sort=foldcase --excmd=number -f ",
+ tag_filename, NULL);
+#endif
+
+ spawn_cmd(cmd, prj->base_path);
+
+ g_free(cmd);
+ g_free(tag_filename);
+ }
+}
+
+static void show_entry(tagEntry *entry)
+{
+ const gchar *kind;
+ const gchar *signature;
+ const gchar *scope;
+ const gchar *file;
+ const gchar *name;
+ gchar *scope_str;
+ gchar *kind_str;
+
+ file = entry->file;
+ if (!file)
+ file = "";
+
+ name = entry->name;
+ if (!name)
+ name = "";
+
+ signature = tagsField(entry, "signature");
+ if (!signature)
+ signature = "";
+
+ scope = tagsField(entry, "class");
+ if (!scope)
+ scope = tagsField(entry, "struct");
+ if (!scope)
+ scope = tagsField(entry, "union");
+ if (!scope)
+ scope = tagsField(entry, "enum");
+
+ if (scope)
+ scope_str = g_strconcat(scope, "::", NULL);
+ else
+ scope_str = g_strdup("");
+
+ kind = entry->kind;
+ if (kind)
+ {
+ kind_str = g_strconcat(kind, ": ", NULL);
+ SETPTR(kind_str, g_strdup_printf("%-14s", kind_str));
+ }
+ else
+ kind_str = g_strdup("");
+
+ msgwin_msg_add(COLOR_BLACK, -1, NULL, "%s:%lu:\n %s%s%s%s", file,
+ entry->address.lineNumber, kind_str, scope_str, name, signature);
+
+ g_free(scope_str);
+ g_free(kind_str);
+}
+
+
+static gchar *get_selection(void)
+{
+ gchar *ret = NULL;
+ GeanyDocument *doc = document_get_current();
+ GeanyEditor *editor;
+
+ if (!doc)
+ return NULL;
+
+ editor = doc->editor;
+
+ if (sci_has_selection(editor->sci))
+ ret = sci_get_selection_contents(editor->sci);
+ else
+ ret = editor_get_word_at_pos(editor, -1, GEANY_WORDCHARS);
+
+ return ret;
+}
+
+/* TODO: Not possible to do it the way below because some of the API is private
+ * in Geany. This means the cursor has to be placed at the symbol first and
+ * then right-clicked (right-clicking without having the cursor at the symbol
+ * doesn't work) */
+
+/*
+static gchar *get_selection()
+{
+ gchar *ret = NULL;
+ GeanyDocument *doc = document_get_current();
+
+ if (!doc)
+ return NULL;
+
+ if (!sci_has_selection(doc->editor->sci))
+ sci_set_current_position(doc->editor->sci, editor_info.click_pos, FALSE);
+
+ if (sci_has_selection(doc->editor->sci))
+ return sci_get_selection_contents(doc->editor->sci);
+
+ gint len = sci_get_selected_text_length(doc->editor->sci);
+
+ ret = g_malloc(len + 1);
+ sci_get_selected_text(doc->editor->sci, ret);
+
+ editor_find_current_word(doc->editor, -1,
+ editor_info.current_word, GEANY_MAX_WORD_LENGTH, NULL);
+
+ return editor_info.current_word != 0 ? g_strdup(editor_info.current_word) : NULL;
+}
+*/
+
+typedef enum
+{
+ MATCH_FULL,
+ MATCH_PREFIX,
+ MATCH_PATTERN
+} MatchType;
+
+static gboolean find_first(tagFile *tf, tagEntry *entry, const gchar *name, MatchType match_type)
+{
+ gboolean ret;
+
+ if (match_type == MATCH_PATTERN)
+ ret = tagsFirst(tf, entry) == TagSuccess;
+ else
+ {
+ int options = TAG_IGNORECASE;
+
+ options |= match_type == MATCH_PREFIX ? TAG_PARTIALMATCH : TAG_FULLMATCH;
+ ret = tagsFind(tf, entry, name, options) == TagSuccess;
+ }
+ return ret;
+}
+
+static gboolean find_next(tagFile *tf, tagEntry *entry, MatchType match_type)
+{
+ gboolean ret;
+
+ if (match_type == MATCH_PATTERN)
+ ret = tagsNext(tf, entry) == TagSuccess;
+ else
+ ret = tagsFindNext(tf, entry) == TagSuccess;
+ return ret;
+}
+
+static gboolean filter_tag(tagEntry *entry, GPatternSpec *name, gboolean declaration, gboolean case_sensitive)
+{
+ gboolean filter = TRUE;
+ gchar *entry_name;
+
+ if (!EMPTY(entry->kind))
+ {
+ gboolean is_prototype;
+
+ is_prototype = g_strcmp0(entry->kind, "prototype") == 0;
+ filter = (declaration && !is_prototype) || (!declaration && is_prototype);
+ if (filter)
+ return TRUE;
+ }
+
+ if (case_sensitive)
+ entry_name = g_strdup(entry->name);
+ else
+ entry_name = g_utf8_strdown(entry->name, -1);
+
+ filter = !g_pattern_match_string(name, entry_name);
+
+ g_free(entry_name);
+
+ return filter;
+}
+
+static void find_tags(const gchar *name, gboolean declaration, gboolean case_sensitive, MatchType match_type)
+{
+ tagFile *tf;
+ GeanyProject *prj;
+ gchar *tag_filename = NULL;
+ tagEntry entry;
+ tagFileInfo info;
+
+ prj = geany_data->app->project;
+ if (!prj)
+ return;
+
+ msgwin_clear_tab(MSG_MESSAGE);
+ msgwin_set_messages_dir(prj->base_path);
+
+ tag_filename = get_tags_filename();
+ tf = tagsOpen(tag_filename, &info);
+
+ if (tf)
+ {
+ if (find_first(tf, &entry, name, match_type))
+ {
+ GPatternSpec *name_pat;
+ gchar *name_case;
+ gchar *path = NULL;
+ gint num = 0;
+
+ if (case_sensitive)
+ name_case = g_strdup(name);
+ else
+ name_case = g_utf8_strdown(name, -1);
+
+ SETPTR(name_case, g_strconcat("*", name_case, "*", NULL));
+ name_pat = g_pattern_spec_new(name_case);
+
+ if (!filter_tag(&entry, name_pat, declaration, case_sensitive))
+ {
+ path = g_build_filename(prj->base_path, entry.file, NULL);
+ show_entry(&entry);
+ num++;
+ }
+
+ while (find_next(tf, &entry, match_type))
+ {
+ if (!filter_tag(&entry, name_pat, declaration, case_sensitive))
+ {
+ if (!path)
+ path = g_build_filename(prj->base_path, entry.file, NULL);
+ show_entry(&entry);
+ num++;
+ }
+ }
+
+ if (num == 1)
+ {
+ GeanyDocument *doc = document_open_file(path, FALSE, NULL, NULL);
+ if (doc != NULL)
+ {
+ navqueue_goto_line(document_get_current(), doc, entry.address.lineNumber);
+ gtk_widget_grab_focus(GTK_WIDGET(doc->editor->sci));
+ }
+ }
+
+ g_pattern_spec_free(name_pat);
+ g_free(name_case);
+ g_free(path);
+ }
+
+ tagsClose(tf);
+ }
+
+ msgwin_switch_tab(MSG_MESSAGE, TRUE);
+
+ g_free(tag_filename);
+}
+
+static void on_find_declaration(GtkMenuItem *menuitem, gpointer user_data)
+{
+ gchar *name;
+
+ name = get_selection();
+ if (name)
+ find_tags(name, TRUE, TRUE, MATCH_FULL);
+ g_free(name);
+}
+
+static void on_find_definition(GtkMenuItem *menuitem, gpointer user_data)
+{
+ gchar *name;
+
+ name = get_selection();
+ if (name)
+ find_tags(name, FALSE, TRUE, MATCH_FULL);
+ g_free(name);
+}
+
+static void create_dialog_find_file(void)
+{
+ GtkWidget *label, *vbox, *ebox, *entry;
+ GtkSizeGroup *size_group;
+
+ if (s_ft_dialog.widget)
+ return;
+
+ s_ft_dialog.widget = gtk_dialog_new_with_buttons(
+ _("Find Tag"), GTK_WINDOW(geany->main_widgets->window),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL);
+ gtk_dialog_add_button(GTK_DIALOG(s_ft_dialog.widget), "gtk-find", GTK_RESPONSE_ACCEPT);
+ gtk_dialog_set_default_response(GTK_DIALOG(s_ft_dialog.widget), GTK_RESPONSE_ACCEPT);
+
+ vbox = ui_dialog_vbox_new(GTK_DIALOG(s_ft_dialog.widget));
+ gtk_box_set_spacing(GTK_BOX(vbox), 9);
+
+ size_group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
+
+ label = gtk_label_new(_("Search for:"));
+ gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
+ gtk_size_group_add_widget(size_group, label);
+
+ s_ft_dialog.combo = gtk_combo_box_text_new_with_entry();
+ entry = gtk_bin_get_child(GTK_BIN(s_ft_dialog.combo));
+
+ ui_entry_add_clear_icon(GTK_ENTRY(entry));
+ gtk_entry_set_width_chars(GTK_ENTRY(entry), 40);
+ gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry);
+ ui_entry_add_clear_icon(GTK_ENTRY(entry));
+ gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
+
+ ebox = gtk_hbox_new(FALSE, 6);
+ gtk_box_pack_start(GTK_BOX(ebox), label, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(ebox), s_ft_dialog.combo, TRUE, TRUE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), ebox, TRUE, FALSE, 0);
+
+ label = gtk_label_new(_("Match type:"));
+ gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
+ gtk_size_group_add_widget(size_group, label);
+
+ s_ft_dialog.combo_match = gtk_combo_box_text_new();
+ gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(s_ft_dialog.combo_match), "full");
+ gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(s_ft_dialog.combo_match), "prefix");
+ gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(s_ft_dialog.combo_match), "pattern");
+ gtk_combo_box_set_active(GTK_COMBO_BOX(s_ft_dialog.combo_match), 1);
+ gtk_label_set_mnemonic_widget(GTK_LABEL(label), s_ft_dialog.combo_match);
+
+ ebox = gtk_hbox_new(FALSE, 6);
+ gtk_box_pack_start(GTK_BOX(ebox), label, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(ebox), s_ft_dialog.combo_match, TRUE, TRUE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), ebox, TRUE, FALSE, 0);
+
+ s_ft_dialog.case_sensitive = gtk_check_button_new_with_mnemonic(_("C_ase sensitive"));
+ gtk_button_set_focus_on_click(GTK_BUTTON(s_ft_dialog.case_sensitive), FALSE);
+
+ s_ft_dialog.declaration = gtk_check_button_new_with_mnemonic(_("_Declaration"));
+ gtk_button_set_focus_on_click(GTK_BUTTON(s_ft_dialog.declaration), FALSE);
+
+ g_object_unref(G_OBJECT(size_group)); /* auto destroy the size group */
+
+ gtk_container_add(GTK_CONTAINER(vbox), s_ft_dialog.case_sensitive);
+ gtk_container_add(GTK_CONTAINER(vbox), s_ft_dialog.declaration);
+ gtk_widget_show_all(vbox);
+}
+
+static void on_find_tag(GtkMenuItem *menuitem, gpointer user_data)
+{
+ gchar *selection;
+ GtkWidget *entry;
+
+ create_dialog_find_file();
+
+ entry = gtk_bin_get_child(GTK_BIN(s_ft_dialog.combo));
+
+ selection = get_selection();
+ if (selection)
+ gtk_entry_set_text(GTK_ENTRY(entry), selection);
+ g_free(selection);
+
+ gtk_widget_grab_focus(entry);
+
+ if (gtk_dialog_run(GTK_DIALOG(s_ft_dialog.widget)) == GTK_RESPONSE_ACCEPT)
+ {
+ const gchar *name;
+ gboolean case_sensitive, declaration;
+ MatchType match_type;
+
+ name = gtk_entry_get_text(GTK_ENTRY(entry));
+ case_sensitive = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(s_ft_dialog.case_sensitive));
+ declaration = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(s_ft_dialog.declaration));
+ match_type = gtk_combo_box_get_active(GTK_COMBO_BOX(s_ft_dialog.combo_match));
+
+ ui_combo_box_add_to_history(GTK_COMBO_BOX_TEXT(s_ft_dialog.combo), name, 0);
+
+ find_tags(name, declaration, case_sensitive, match_type);
+ }
+
+ gtk_widget_hide(s_ft_dialog.widget);
+}
+
+static gboolean kb_callback(guint key_id)
+{
+ switch (key_id)
+ {
+ case KB_FIND_TAG:
+ on_find_tag(NULL, NULL);
+ return TRUE;
+ case KB_GENERATE_TAGS:
+ on_generate_tags(NULL, NULL);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void plugin_init(G_GNUC_UNUSED GeanyData * data)
+{
+ GeanyKeyGroup *key_group = plugin_set_key_group(geany_plugin, "GeanyCtags", KB_COUNT, kb_callback);
+
+ s_context_sep_item = gtk_separator_menu_item_new();
+ gtk_widget_show(s_context_sep_item);
+ gtk_menu_shell_prepend(GTK_MENU_SHELL(geany->main_widgets->editor_menu), s_context_sep_item);
+
+ s_context_fdec_item = gtk_menu_item_new_with_mnemonic(_("Find Tag Declaration (geanyctags)"));
+ gtk_widget_show(s_context_fdec_item);
+ gtk_menu_shell_prepend(GTK_MENU_SHELL(geany->main_widgets->editor_menu), s_context_fdec_item);
+ g_signal_connect((gpointer) s_context_fdec_item, "activate", G_CALLBACK(on_find_declaration), NULL);
+
+ s_context_fdef_item = gtk_menu_item_new_with_mnemonic(_("Find Tag Definition (geanyctags)"));
+ gtk_widget_show(s_context_fdef_item);
+ gtk_menu_shell_prepend(GTK_MENU_SHELL(geany->main_widgets->editor_menu), s_context_fdef_item);
+ g_signal_connect((gpointer) s_context_fdef_item, "activate", G_CALLBACK(on_find_definition), NULL);
+
+ s_sep_item = gtk_separator_menu_item_new();
+ gtk_widget_show(s_sep_item);
+ gtk_container_add(GTK_CONTAINER(geany->main_widgets->project_menu), s_sep_item);
+
+ s_gt_item = gtk_menu_item_new_with_mnemonic(_("Generate tags"));
+ gtk_widget_show(s_gt_item);
+ gtk_container_add(GTK_CONTAINER(geany->main_widgets->project_menu), s_gt_item);
+ g_signal_connect((gpointer) s_gt_item, "activate", G_CALLBACK(on_generate_tags), NULL);
+ keybindings_set_item(key_group, KB_GENERATE_TAGS, NULL,
+ 0, 0, "generate_tags", _("Generate tags"), s_gt_item);
+
+ s_ft_item = gtk_menu_item_new_with_mnemonic(_("Find tag"));
+ gtk_widget_show(s_ft_item);
+ gtk_container_add(GTK_CONTAINER(geany->main_widgets->project_menu), s_ft_item);
+ g_signal_connect((gpointer) s_ft_item, "activate", G_CALLBACK(on_find_tag), NULL);
+ keybindings_set_item(key_group, KB_FIND_TAG, NULL,
+ 0, 0, "find_tag", _("Find tag"), s_ft_item);
+
+ set_widgets_sensitive(FALSE);
+}
+
+void plugin_cleanup(void)
+{
+ gtk_widget_destroy(s_context_fdec_item);
+ gtk_widget_destroy(s_context_fdef_item);
+ gtk_widget_destroy(s_context_sep_item);
+
+ gtk_widget_destroy(s_ft_item);
+ gtk_widget_destroy(s_gt_item);
+ gtk_widget_destroy(s_sep_item);
+
+ if (s_ft_dialog.widget)
+ gtk_widget_destroy(s_ft_dialog.widget);
+ s_ft_dialog.widget = NULL;
+}
--------------
This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).
More information about the Plugins-Commits
mailing list