lists.geany.org
Sign In
Sign Up
Sign In
Sign Up
Manage this list
×
Keyboard Shortcuts
Thread View
j
: Next unread message
k
: Previous unread message
j a
: Jump to all threads
j l
: Jump to MailingList overview
2024
November
October
September
August
July
June
May
April
March
February
January
2023
December
November
October
September
August
July
June
May
April
March
February
January
2022
December
November
October
September
August
July
June
May
April
March
February
January
2021
December
November
October
September
August
July
June
May
April
March
February
January
2020
December
November
October
September
August
July
June
May
April
March
February
January
2019
December
November
October
September
August
July
June
May
April
March
February
January
2018
December
November
October
September
August
July
June
May
April
March
February
January
2017
December
November
October
September
August
July
June
May
April
March
February
January
2016
December
November
October
September
August
July
June
May
April
March
February
January
2015
December
November
October
September
August
July
June
May
April
March
February
January
2014
December
November
October
September
August
July
June
May
April
March
February
January
2013
December
November
October
September
August
July
June
May
April
March
February
January
2012
December
November
October
September
August
July
June
May
April
March
February
January
2011
December
November
October
September
August
July
June
May
April
March
February
January
2010
December
November
October
September
August
July
June
May
April
March
February
January
2009
December
November
October
September
August
July
June
May
April
March
February
January
2008
December
November
October
September
August
July
June
May
April
List overview
Plugins-Commits
August 2024
----- 2024 -----
November 2024
October 2024
September 2024
August 2024
July 2024
June 2024
May 2024
April 2024
March 2024
February 2024
January 2024
----- 2023 -----
December 2023
November 2023
October 2023
September 2023
August 2023
July 2023
June 2023
May 2023
April 2023
March 2023
February 2023
January 2023
----- 2022 -----
December 2022
November 2022
October 2022
September 2022
August 2022
July 2022
June 2022
May 2022
April 2022
March 2022
February 2022
January 2022
----- 2021 -----
December 2021
November 2021
October 2021
September 2021
August 2021
July 2021
June 2021
May 2021
April 2021
March 2021
February 2021
January 2021
----- 2020 -----
December 2020
November 2020
October 2020
September 2020
August 2020
July 2020
June 2020
May 2020
April 2020
March 2020
February 2020
January 2020
----- 2019 -----
December 2019
November 2019
October 2019
September 2019
August 2019
July 2019
June 2019
May 2019
April 2019
March 2019
February 2019
January 2019
----- 2018 -----
December 2018
November 2018
October 2018
September 2018
August 2018
July 2018
June 2018
May 2018
April 2018
March 2018
February 2018
January 2018
----- 2017 -----
December 2017
November 2017
October 2017
September 2017
August 2017
July 2017
June 2017
May 2017
April 2017
March 2017
February 2017
January 2017
----- 2016 -----
December 2016
November 2016
October 2016
September 2016
August 2016
July 2016
June 2016
May 2016
April 2016
March 2016
February 2016
January 2016
----- 2015 -----
December 2015
November 2015
October 2015
September 2015
August 2015
July 2015
June 2015
May 2015
April 2015
March 2015
February 2015
January 2015
----- 2014 -----
December 2014
November 2014
October 2014
September 2014
August 2014
July 2014
June 2014
May 2014
April 2014
March 2014
February 2014
January 2014
----- 2013 -----
December 2013
November 2013
October 2013
September 2013
August 2013
July 2013
June 2013
May 2013
April 2013
March 2013
February 2013
January 2013
----- 2012 -----
December 2012
November 2012
October 2012
September 2012
August 2012
July 2012
June 2012
May 2012
April 2012
March 2012
February 2012
January 2012
----- 2011 -----
December 2011
November 2011
October 2011
September 2011
August 2011
July 2011
June 2011
May 2011
April 2011
March 2011
February 2011
January 2011
----- 2010 -----
December 2010
November 2010
October 2010
September 2010
August 2010
July 2010
June 2010
May 2010
April 2010
March 2010
February 2010
January 2010
----- 2009 -----
December 2009
November 2009
October 2009
September 2009
August 2009
July 2009
June 2009
May 2009
April 2009
March 2009
February 2009
January 2009
----- 2008 -----
December 2008
November 2008
October 2008
September 2008
August 2008
July 2008
June 2008
May 2008
April 2008
plugins-commits@lists.geany.org
1 participants
2 discussions
Start a n
N
ew thread
[geany/geany-plugins] dc384c: Merge pull request #1341 from techee/prjorg_goto_anywhere
by Jiří Techet
11 Aug '24
11 Aug '24
Branch: refs/heads/master Author: Jiří Techet <techet(a)gmail.com> Committer: GitHub <noreply(a)github.com> Date: Sun, 11 Aug 2024 17:38:43 UTC Commit: dc384ce55b4d71199ecd5dbbe81e9215ac54b9e1
https://github.com/geany/geany-plugins/commit/dc384ce55b4d71199ecd5dbbe81e9…
Log Message: ----------- Merge pull request #1341 from techee/prjorg_goto_anywhere projectorganizer: Add popup panel for navigation Modified Paths: -------------- po/POTFILES.in projectorganizer/README projectorganizer/src/Makefile.am projectorganizer/src/prjorg-goto-anywhere.c projectorganizer/src/prjorg-goto-anywhere.h projectorganizer/src/prjorg-goto-panel.c projectorganizer/src/prjorg-goto-panel.h projectorganizer/src/prjorg-main.c projectorganizer/src/prjorg-menu.c projectorganizer/src/prjorg-project.c Modified: po/POTFILES.in 1 lines changed, 1 insertions(+), 0 deletions(-) =================================================================== @@ -205,6 +205,7 @@ pretty-printer/src/PluginEntry.c pretty-printer/src/ConfigUI.c # ProjectOrganizer +projectorganizer/src/prjorg-goto-anywhere.c projectorganizer/src/prjorg-main.c projectorganizer/src/prjorg-menu.c projectorganizer/src/prjorg-project.c Modified: projectorganizer/README 7 lines changed, 7 insertions(+), 0 deletions(-) =================================================================== @@ -156,6 +156,13 @@ Project Organizer adds some extra entries under the Project menu: the properties, it opens a project file with the same base name (without extension) matching header patterns (and vice versa). If the files are already open, it just switches the document tabs. Nothing happens if no matching file is found. +* Go to anywhere, Go to document symbol, Go to workspace symbol, Go to line - + these items allow to perform jump to the entered destination. The popup window is + identical for all of these actions, the only difference is the pre-filled prefix + that determines the type of the go to. No prefix performs the search in open and + project files, @ performs the search in current document's symbols, # performs + the search in all workspace symbols, and : performs navigation to the specified + line. Each of these entries can be assigned a key binding under Edit->Preferences->Keybindings. Modified: projectorganizer/src/Makefile.am 6 lines changed, 5 insertions(+), 1 deletions(-) =================================================================== @@ -12,7 +12,11 @@ projectorganizer_la_SOURCES = \ prjorg-utils.h \ prjorg-utils.c \ prjorg-menu.h \ - prjorg-menu.c + prjorg-menu.c \ + prjorg-goto-panel.h \ + prjorg-goto-panel.c \ + prjorg-goto-anywhere.h \ + prjorg-goto-anywhere.c projectorganizer_la_CPPFLAGS = $(AM_CPPFLAGS) \ -DG_LOG_DOMAIN=\"ProjectOrganizer\" Modified: projectorganizer/src/prjorg-goto-anywhere.c 375 lines changed, 375 insertions(+), 0 deletions(-) =================================================================== @@ -0,0 +1,375 @@ +/* + * Copyright 2023 Jiri Techet <techet(a)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. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "prjorg-goto-anywhere.h" +#include "prjorg-goto-panel.h" +#include "prjorg-project.h" + +#include <gtk/gtk.h> +#include <geanyplugin.h> + + +#define SSM(s, m, w, l) scintilla_send_message((s), (m), (w), (l)) + + +typedef struct +{ + GeanyDocument *doc; + gchar *query; +} DocQueryData; + + +extern GeanyData *geany_data; + + +static void goto_line(GeanyDocument *doc, const gchar *line_str) +{ + GPtrArray *arr = g_ptr_array_new_full(0, (GDestroyNotify)prjorg_goto_symbol_free); + gint lineno = atoi(line_str); + gint linenum = sci_get_line_count(doc->editor->sci); + guint i; + + for (i = 0; i < 4; i++) + { + PrjorgGotoSymbol *sym = g_new0(PrjorgGotoSymbol, 1); + + sym->file_name = utils_get_utf8_from_locale(doc->real_path); + sym->icon = TM_ICON_OTHER; + + switch (i) + { + case 0: + /* For translators: Item in a list which, when selected, navigates + * to the line typed in the entry above the list */ + sym->name = g_strdup(_("line typed above")); + if (lineno == 0) + sym->line = sci_get_current_line(doc->editor->sci) + 1; + else if (lineno > linenum) + sym->line = linenum; + else + sym->line = lineno; + break; + + case 1: + /* For translators: Item in a list which, when selected, navigates + * to the beginning of the current document */ + sym->name = g_strdup(_("beginning")); + sym->line = 1; + break; + + case 2: + /* For translators: Item in a list which, when selected, navigates + * to the middle of the current document */ + sym->name = g_strdup(_("middle")); + sym->line = linenum / 2; + break; + + case 3: + /* For translators: Item in a list which, when selected, navigates + * to the end of the current document */ + sym->name = g_strdup(_("end")); + sym->line = linenum; + break; + } + + g_ptr_array_add(arr, sym); + } + + prjorg_goto_panel_fill(arr); + + g_ptr_array_free(arr, TRUE); +} + + +static void goto_file(const gchar *file_str) +{ + GPtrArray *arr = g_ptr_array_new_full(0, (GDestroyNotify)prjorg_goto_symbol_free); + GHashTable *files_added = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); + GPtrArray *filtered; + guint i; + + foreach_document(i) + { + GeanyDocument *doc = documents[i]; + PrjorgGotoSymbol *sym; + + if (!doc->real_path) + continue; + + sym = g_new0(PrjorgGotoSymbol, 1); + sym->file_name = utils_get_utf8_from_locale(doc->real_path); + sym->name = g_path_get_basename(sym->file_name); + sym->icon = TM_ICON_OTHER; + g_ptr_array_add(arr, sym); + + g_hash_table_insert(files_added, g_strdup(sym->file_name), GINT_TO_POINTER(1)); + } + + if (prj_org && prj_org->roots) + { + PrjOrgRoot *root = prj_org->roots->data; + GHashTableIter iter; + gpointer key, value; + + g_hash_table_iter_init(&iter, root->file_table); + while (g_hash_table_iter_next(&iter, &key, &value)) + { + if (!g_hash_table_lookup(files_added, key)) + { + PrjorgGotoSymbol *sym; + sym = g_new0(PrjorgGotoSymbol, 1); + sym->file_name = g_strdup(key); + sym->name = g_path_get_basename(key); + sym->icon = TM_ICON_NONE; + g_ptr_array_add(arr, sym); + } + } + } + + filtered = prjorg_goto_panel_filter(arr, file_str); + prjorg_goto_panel_fill(filtered); + + g_ptr_array_free(filtered, TRUE); + g_ptr_array_free(arr, TRUE); + g_hash_table_destroy(files_added); +} + + +/* symplified hard-coded icons because we don't have access to Geany icon mappings */ +static int get_icon(TMTagType type) +{ + switch (type) + { + case tm_tag_class_t: + return TM_ICON_CLASS; + case tm_tag_macro_t: + case tm_tag_macro_with_arg_t: + case tm_tag_undef_t: + return TM_ICON_MACRO; + case tm_tag_enum_t: + case tm_tag_struct_t: + case tm_tag_typedef_t: + case tm_tag_union_t: + return TM_ICON_STRUCT; + case tm_tag_enumerator_t: + case tm_tag_field_t: + case tm_tag_member_t: + return TM_ICON_MEMBER; + case tm_tag_method_t: + case tm_tag_function_t: + case tm_tag_prototype_t: + return TM_ICON_METHOD; + case tm_tag_interface_t: + case tm_tag_namespace_t: + case tm_tag_package_t: + return TM_ICON_NAMESPACE; + case tm_tag_variable_t: + case tm_tag_externvar_t: + case tm_tag_local_var_t: + case tm_tag_include_t: + return TM_ICON_VAR; + case tm_tag_other_t: + return TM_ICON_OTHER; + default: + return TM_ICON_NONE; + } +} + + +/* stolen from Geany */ +static gboolean langs_compatible(TMParserType lang, TMParserType other) +{ + if (lang == other) + return TRUE; + /* Accept CPP tags for C lang and vice versa - we don't have acces to + * Geany's TM_PARSER_ constants so let's hard-code 0 and 1 here (not too + * terrible things will happen if Geany changes these to something else) */ + else if (lang == 0 && other == 1) + return TRUE; + else if (lang == 1 && other == 0) + return TRUE; + + return FALSE; +} + + +static void goto_tm_symbol(const gchar *query, GPtrArray *tags, TMParserType lang) +{ + GPtrArray *converted = g_ptr_array_new_full(0, (GDestroyNotify)prjorg_goto_symbol_free); + GPtrArray *filtered; + TMTag *tag; + guint i; + + if (tags) + { + foreach_ptr_array(tag, i, tags) + { + if (tag->file && langs_compatible(tag->lang, lang) && + !(tag->type & (tm_tag_include_t | tm_tag_local_var_t))) + { + PrjorgGotoSymbol *sym = g_new0(PrjorgGotoSymbol, 1); + sym->name = g_strdup(tag->name); + sym->file_name = utils_get_utf8_from_locale(tag->file->file_name); + sym->line = tag->line; + sym->icon = get_icon(tag->type); + + g_ptr_array_add(converted, sym); + } + } + } + + filtered = prjorg_goto_panel_filter(converted, query); + prjorg_goto_panel_fill(filtered); + + g_ptr_array_free(filtered, TRUE); + g_ptr_array_free(converted, TRUE); +} + + +static void perform_lookup(const gchar *query) +{ + GeanyDocument *doc = document_get_current(); + const gchar *query_str = query ? query : ""; + + if (g_str_has_prefix(query_str, "#")) + { + if (doc) + { + // TODO: possibly improve performance by binary searching the start and the end point + goto_tm_symbol(query_str+1, geany_data->app->tm_workspace->tags_array, doc->file_type->lang); + } + } + else if (g_str_has_prefix(query_str, "@")) + { + if (doc) + { + GPtrArray *tags = doc->tm_file ? doc->tm_file->tags_array : g_ptr_array_new(); + goto_tm_symbol(query_str+1, tags, doc->file_type->lang); + if (!doc->tm_file) + g_ptr_array_free(tags, TRUE); + } + } + else if (g_str_has_prefix(query_str, ":")) + { + if (doc) + goto_line(doc, query_str+1); + } + else + goto_file(query_str); +} + + +static gchar *get_current_iden(GeanyDocument *doc, gint current_pos) +{ + //TODO: use configured wordchars (also change in Geany) + const gchar *wordchars = GEANY_WORDCHARS; + GeanyFiletypeID ft = doc->file_type->id; + ScintillaObject *sci = doc->editor->sci; + gint start_pos, end_pos, pos; + + if (ft == GEANY_FILETYPES_LATEX) + wordchars = GEANY_WORDCHARS"\\"; /* add \ to word chars if we are in a LaTeX file */ + else if (ft == GEANY_FILETYPES_CSS) + wordchars = GEANY_WORDCHARS"-"; /* add - because they are part of property names */ + + pos = current_pos; + while (TRUE) + { + gint new_pos = SSM(sci, SCI_POSITIONBEFORE, pos, 0); + if (new_pos == pos) + break; + if (pos - new_pos == 1) + { + gchar c = sci_get_char_at(sci, new_pos); + if (!strchr(wordchars, c)) + break; + } + pos = new_pos; + } + start_pos = pos; + + pos = current_pos; + while (TRUE) + { + gint new_pos = SSM(sci, SCI_POSITIONAFTER, pos, 0); + if (new_pos == pos) + break; + if (new_pos - pos == 1) + { + gchar c = sci_get_char_at(sci, pos); + if (!strchr(wordchars, c)) + break; + } + pos = new_pos; + } + end_pos = pos; + + if (start_pos == end_pos) + return NULL; + + return sci_get_contents_range(sci, start_pos, end_pos); +} + + +static void goto_panel_query(const gchar *query_type, gboolean prefill) +{ + GeanyDocument *doc = document_get_current(); + gchar *query = NULL; + gint pos; + + if (prefill && doc) + { + pos = sci_get_current_position(doc->editor->sci); + query = get_current_iden(doc, pos); + } + if (!query) + query = g_strdup(""); + SETPTR(query, g_strconcat(query_type, query, NULL)); + + prjorg_goto_panel_show(query, perform_lookup); + + g_free(query); +} + + +void prjorg_goto_anywhere_for_workspace(void) +{ + goto_panel_query("#", TRUE); +} + + +void prjorg_goto_anywhere_for_doc(void) +{ + goto_panel_query("@", TRUE); +} + + +void prjorg_goto_anywhere_for_line(void) +{ + goto_panel_query(":", FALSE); +} + + +void prjorg_goto_anywhere_for_file(void) +{ + goto_panel_query("", FALSE); +} Modified: projectorganizer/src/prjorg-goto-anywhere.h 28 lines changed, 28 insertions(+), 0 deletions(-) =================================================================== @@ -0,0 +1,28 @@ +/* + * Copyright 2023 Jiri Techet <techet(a)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. + */ + +#ifndef PRJORG_GOTO_ANYWHERE_H +#define PRJORG_GOTO_ANYWHERE_H 1 + + +void prjorg_goto_anywhere_for_workspace(void); +void prjorg_goto_anywhere_for_doc(void); +void prjorg_goto_anywhere_for_line(void); +void prjorg_goto_anywhere_for_file(void); + +#endif /* PRJORG_GOTO_ANYWHERE_H */ Modified: projectorganizer/src/prjorg-goto-panel.c 446 lines changed, 446 insertions(+), 0 deletions(-) =================================================================== @@ -0,0 +1,446 @@ +/* + * Copyright 2023 Jiri Techet <techet(a)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. + */ + +/* This file contains mostly stolen code from the Colomban Wendling's Commander + * plugin. Thanks! */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "prjorg-goto-panel.h" + +#include <gtk/gtk.h> +#include <geanyplugin.h> + + +enum { + COL_ICON, + COL_LABEL, + COL_PATH, + COL_LINENO, + COL_COUNT +}; + + +//TODO: free on plugin unload +struct { + GtkWidget *panel; + GtkWidget *entry; + GtkWidget *tree_view; + GtkListStore *store; +} panel_data = { + NULL, NULL, NULL, NULL +}; + + +static PrjorgGotoPanelLookupFunction lookup_function; + +extern GeanyData *geany_data; + + +void prjorg_goto_symbol_free(PrjorgGotoSymbol *symbol) +{ + g_free(symbol->name); + g_free(symbol->file_name); + g_free(symbol->scope); + g_free(symbol->tooltip); + g_free(symbol); +} + + +static void tree_view_set_cursor_from_iter(GtkTreeView *view, GtkTreeIter *iter) +{ + GtkTreePath *path; + + path = gtk_tree_model_get_path(gtk_tree_view_get_model(view), iter); + gtk_tree_view_set_cursor(view, path, NULL, FALSE); + gtk_tree_path_free(path); +} + + +static void tree_view_move_focus(GtkTreeView *view, GtkMovementStep step, gint amount) +{ + GtkTreeIter iter; + GtkTreePath *path; + GtkTreeModel *model = gtk_tree_view_get_model(view); + gboolean valid = FALSE; + + gtk_tree_view_get_cursor(view, &path, NULL); + if (!path) + valid = gtk_tree_model_get_iter_first(model, &iter); + else + { + switch (step) { + case GTK_MOVEMENT_BUFFER_ENDS: + valid = gtk_tree_model_get_iter_first(model, &iter); + if (valid && amount > 0) + { + GtkTreeIter prev; + + do { + prev = iter; + } while (gtk_tree_model_iter_next(model, &iter)); + iter = prev; + } + break; + + case GTK_MOVEMENT_PAGES: + /* FIXME: move by page */ + case GTK_MOVEMENT_DISPLAY_LINES: + gtk_tree_model_get_iter(model, &iter, path); + if (amount > 0) + { + while ((valid = gtk_tree_model_iter_next(model, &iter)) && --amount > 0) + ; + } + else if (amount < 0) + { + while ((valid = gtk_tree_path_prev(path)) && --amount > 0) + ; + + if (valid) + gtk_tree_model_get_iter(model, &iter, path); + } + break; + + default: + g_assert_not_reached(); + } + gtk_tree_path_free(path); + } + + if (valid) + tree_view_set_cursor_from_iter(view, &iter); + else + gtk_widget_error_bell(GTK_WIDGET(view)); +} + + +static void tree_view_activate_focused_row(GtkTreeView *view) +{ + GtkTreePath *path; + GtkTreeViewColumn *column; + + gtk_tree_view_get_cursor(view, &path, &column); + if (path) + { + gtk_tree_view_row_activated(view, path, column); + gtk_tree_path_free(path); + } +} + + +void prjorg_goto_panel_fill(GPtrArray *symbols) +{ + GtkTreeView *view = GTK_TREE_VIEW(panel_data.tree_view); + GtkTreeIter iter; + PrjorgGotoSymbol *sym; + guint i; + + gtk_list_store_clear(panel_data.store); + + foreach_ptr_array(sym, i, symbols) + { + gchar *label; + + if (!sym->file_name) + continue; + + if (sym->file_name && sym->line > 0) + label = g_markup_printf_escaped("%s\n<small><i>%s:%d</i></small>", + sym->name, sym->file_name, sym->line); + else if (sym->file_name) + label = g_markup_printf_escaped("%s\n<small><i>%s</i></small>", + sym->name, sym->file_name); + else + label = g_markup_printf_escaped("%s", sym->name); + + gtk_list_store_insert_with_values(panel_data.store, NULL, -1, + COL_ICON, symbols_get_icon_pixbuf(sym->icon), + COL_LABEL, label, + COL_PATH, sym->file_name, + COL_LINENO, sym->line, + -1); + + g_free(label); + } + + if (gtk_tree_model_get_iter_first(gtk_tree_view_get_model(view), &iter)) + tree_view_set_cursor_from_iter(GTK_TREE_VIEW(panel_data.tree_view), &iter); +} + + +static gboolean on_panel_key_press_event(GtkWidget *widget, GdkEventKey *event, + gpointer dummy) +{ + switch (event->keyval) { + case GDK_KEY_Escape: + gtk_widget_hide(widget); + return TRUE; + + case GDK_KEY_Tab: + /* avoid leaving the entry */ + return TRUE; + + case GDK_KEY_Return: + case GDK_KEY_KP_Enter: + case GDK_KEY_ISO_Enter: + tree_view_activate_focused_row(GTK_TREE_VIEW(panel_data.tree_view)); + return TRUE; + + case GDK_KEY_Page_Up: + case GDK_KEY_Page_Down: + case GDK_KEY_KP_Page_Up: + case GDK_KEY_KP_Page_Down: + { + gboolean up = event->keyval == GDK_KEY_Page_Up || event->keyval == GDK_KEY_KP_Page_Up; + tree_view_move_focus(GTK_TREE_VIEW(panel_data.tree_view), + GTK_MOVEMENT_PAGES, up ? -1 : 1); + return TRUE; + } + + case GDK_KEY_Up: + case GDK_KEY_Down: + case GDK_KEY_KP_Up: + case GDK_KEY_KP_Down: + { + gboolean up = event->keyval == GDK_KEY_Up || event->keyval == GDK_KEY_KP_Up; + tree_view_move_focus(GTK_TREE_VIEW(panel_data.tree_view), + GTK_MOVEMENT_DISPLAY_LINES, up ? -1 : 1); + return TRUE; + } + } + + return FALSE; +} + + +static void on_entry_text_notify(GObject *object, GParamSpec *pspec, gpointer dummy) +{ + GtkTreeIter iter; + GtkTreeView *view = GTK_TREE_VIEW(panel_data.tree_view); + GtkTreeModel *model = gtk_tree_view_get_model(view); + const gchar *text = gtk_entry_get_text(GTK_ENTRY(panel_data.entry)); + + lookup_function(text); + + if (gtk_tree_model_get_iter_first(model, &iter)) + tree_view_set_cursor_from_iter(view, &iter); +} + + +static void on_entry_activate(GtkEntry *entry, gpointer dummy) +{ + tree_view_activate_focused_row(GTK_TREE_VIEW(panel_data.tree_view)); +} + + +static void on_panel_hide(GtkWidget *widget, gpointer dummy) +{ + gtk_list_store_clear(panel_data.store); +} + + +static void on_panel_show(GtkWidget *widget, gpointer dummy) +{ + const gchar *text = gtk_entry_get_text(GTK_ENTRY(panel_data.entry)); + gboolean select_first = TRUE; + + if (text && (text[0] == ':' || text[0] == '#' || text[0] == '@')) + select_first = FALSE; + + gtk_widget_grab_focus(panel_data.entry); + gtk_editable_select_region(GTK_EDITABLE(panel_data.entry), select_first ? 0 : 1, -1); +} + + +static void on_view_row_activated(GtkTreeView *view, GtkTreePath *path, + GtkTreeViewColumn *column, gpointer dummy) +{ + GtkTreeModel *model = gtk_tree_view_get_model(view); + GtkTreeIter iter; + + if (gtk_tree_model_get_iter(model, &iter, path)) + { + GeanyDocument *doc; + gchar *file_path; + gint line; + + gtk_tree_model_get(model, &iter, + COL_PATH, &file_path, + COL_LINENO, &line, + -1); + + SETPTR(file_path, utils_get_locale_from_utf8(file_path)); + doc = document_open_file(file_path, FALSE, NULL, NULL); + + if (doc && line > 0) + navqueue_goto_line(document_get_current(), doc, line); + + g_free(file_path); + } + + gtk_widget_hide(panel_data.panel); +} + + +static void create_panel(void) +{ + GtkWidget *frame, *box, *scroll; + GtkTreeViewColumn *col; + GtkCellRenderer *renderer; + + panel_data.panel = g_object_new(GTK_TYPE_WINDOW, + "decorated", FALSE, + "default-width", 500, + "default-height", 350, + "transient-for", geany_data->main_widgets->window, + "window-position", GTK_WIN_POS_CENTER_ON_PARENT, + "type-hint", GDK_WINDOW_TYPE_HINT_DIALOG, + "skip-taskbar-hint", TRUE, + "skip-pager-hint", TRUE, + NULL); + g_signal_connect(panel_data.panel, "focus-out-event", + G_CALLBACK(gtk_widget_hide), NULL); + g_signal_connect(panel_data.panel, "show", + G_CALLBACK(on_panel_show), NULL); + g_signal_connect(panel_data.panel, "hide", + G_CALLBACK(on_panel_hide), NULL); + g_signal_connect(panel_data.panel, "key-press-event", + G_CALLBACK(on_panel_key_press_event), NULL); + + frame = gtk_frame_new(NULL); + gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN); + gtk_container_add(GTK_CONTAINER(panel_data.panel), frame); + + box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); + gtk_container_add(GTK_CONTAINER(frame), box); + + panel_data.entry = gtk_entry_new(); + gtk_box_pack_start(GTK_BOX(box), panel_data.entry, FALSE, TRUE, 0); + + scroll = g_object_new(GTK_TYPE_SCROLLED_WINDOW, + "hscrollbar-policy", GTK_POLICY_AUTOMATIC, + "vscrollbar-policy", GTK_POLICY_AUTOMATIC, + NULL); + gtk_box_pack_start(GTK_BOX(box), scroll, TRUE, TRUE, 0); + + panel_data.tree_view = gtk_tree_view_new(); + gtk_widget_set_can_focus(panel_data.tree_view, FALSE); + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(panel_data.tree_view), FALSE); + + panel_data.store = gtk_list_store_new(COL_COUNT, + GDK_TYPE_PIXBUF, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_INT); + gtk_tree_view_set_model(GTK_TREE_VIEW(panel_data.tree_view), GTK_TREE_MODEL(panel_data.store)); + g_object_unref(panel_data.store); + + renderer = gtk_cell_renderer_pixbuf_new(); + col = gtk_tree_view_column_new(); + gtk_tree_view_column_pack_start(col, renderer, FALSE); + gtk_tree_view_column_set_attributes(col, renderer, "pixbuf", COL_ICON, NULL); + g_object_set(renderer, "xalign", 0.0, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(panel_data.tree_view), col); + + renderer = gtk_cell_renderer_text_new(); + g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL); + col = gtk_tree_view_column_new_with_attributes(NULL, renderer, + "markup", COL_LABEL, + NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(panel_data.tree_view), col); + + g_signal_connect(panel_data.tree_view, "row-activated", + G_CALLBACK(on_view_row_activated), NULL); + gtk_container_add(GTK_CONTAINER(scroll), panel_data.tree_view); + + /* connect entry signals after the view is created as they use it */ + g_signal_connect(panel_data.entry, "notify::text", + G_CALLBACK(on_entry_text_notify), NULL); + g_signal_connect(panel_data.entry, "activate", + G_CALLBACK(on_entry_activate), NULL); + + gtk_widget_show_all(frame); +} + + +void prjorg_goto_panel_show(const gchar *query, PrjorgGotoPanelLookupFunction func) +{ + if (!panel_data.panel) + create_panel(); + + lookup_function = func; + + gtk_entry_set_text(GTK_ENTRY(panel_data.entry), query); + gtk_list_store_clear(panel_data.store); + gtk_widget_show(panel_data.panel); + + lookup_function(query); +} + + +GPtrArray *prjorg_goto_panel_filter(GPtrArray *symbols, const gchar *filter) +{ + GPtrArray *ret = g_ptr_array_new(); + gchar *case_normalized_filter; + gchar **tf_strv; + guint i; + guint j = 0; + + if (!symbols) + return ret; + + case_normalized_filter = g_utf8_normalize(filter, -1, G_NORMALIZE_ALL); + SETPTR(case_normalized_filter, g_utf8_casefold(case_normalized_filter, -1)); + + tf_strv = g_strsplit_set(case_normalized_filter, " ", -1); + + for (i = 0; i < symbols->len && j < 20; i++) + { + PrjorgGotoSymbol *symbol = symbols->pdata[i]; + gboolean filtered = FALSE; + gchar *case_normalized_name; + gchar **val; + + case_normalized_name = g_utf8_normalize(symbol->name, -1, G_NORMALIZE_ALL); + SETPTR(case_normalized_name, g_utf8_casefold(case_normalized_name, -1)); + + foreach_strv(val, tf_strv) + { + if (case_normalized_name != NULL && *val != NULL) + filtered = strstr(case_normalized_name, *val) == NULL; + + if (filtered) + break; + } + if (!filtered) + { + g_ptr_array_add(ret, symbol); + j++; + } + + g_free(case_normalized_name); + } + + g_strfreev(tf_strv); + g_free(case_normalized_filter); + + return ret; +} Modified: projectorganizer/src/prjorg-goto-panel.h 44 lines changed, 44 insertions(+), 0 deletions(-) =================================================================== @@ -0,0 +1,44 @@ +/* + * Copyright 2023 Jiri Techet <techet(a)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. + */ + +#ifndef PRJORG_GOTO_PANEL_H +#define PRJORG_GOTO_PANEL_H 1 + +#include <geanyplugin.h> + + +typedef struct +{ + gchar *name; + gchar *file_name; + gchar *scope; + gchar *tooltip; + gint line; + TMIcon icon; +} PrjorgGotoSymbol; + +void prjorg_goto_symbol_free(PrjorgGotoSymbol *symbol); + + +typedef void (*PrjorgGotoPanelLookupFunction) (const char *); + +void prjorg_goto_panel_show(const gchar *query, PrjorgGotoPanelLookupFunction func); +void prjorg_goto_panel_fill(GPtrArray *symbols); +GPtrArray *prjorg_goto_panel_filter(GPtrArray *symbols, const gchar *filter); + +#endif /* PRJORG_GOTO_PANEL_H */ Modified: projectorganizer/src/prjorg-main.c 2 lines changed, 1 insertions(+), 1 deletions(-) =================================================================== @@ -34,7 +34,7 @@ GeanyPlugin *geany_plugin; GeanyData *geany_data; -PLUGIN_VERSION_CHECK(235) +PLUGIN_VERSION_CHECK(248) PLUGIN_SET_TRANSLATABLE_INFO( LOCALEDIR, GETTEXT_PACKAGE, Modified: projectorganizer/src/prjorg-menu.c 55 lines changed, 50 insertions(+), 5 deletions(-) =================================================================== @@ -29,6 +29,7 @@ #include "prjorg-project.h" #include "prjorg-utils.h" #include "prjorg-sidebar.h" +#include "prjorg-goto-anywhere.h" #include <string.h> @@ -45,11 +46,17 @@ enum KB_FOCUS_SIDEBAR, KB_OPEN_FILE_MANAGER, KB_OPEN_TERMINAL, + KB_GOTO_ANYWHERE, + KB_GOTO_DOC_SYMBOL, + KB_GOTO_WORKSPACE_SYMBOL, + KB_GOTO_LINE, KB_COUNT }; -static GtkWidget *s_fif_item, *s_ff_item, *s_ft_item, *s_shs_item, *s_sep_item, *s_context_osf_item, *s_context_sep_item; +static GtkWidget *s_fif_item, *s_ff_item, *s_ft_item, *s_shs_item, *s_sep_item1, + *s_context_osf_item, *s_context_sep_item, + *s_sep_item2, *s_goto_any_item, *s_goto_doc_item, *s_goto_wks_item, *s_goto_line_item; static void on_swap_header_source(G_GNUC_UNUSED GtkMenuItem * menuitem, G_GNUC_UNUSED gpointer user_data) @@ -253,9 +260,9 @@ void prjorg_menu_init(void) { GeanyKeyGroup *key_group = plugin_set_key_group(geany_plugin, "ProjectOrganizer", KB_COUNT, kb_callback); - 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_sep_item1 = gtk_separator_menu_item_new(); + gtk_widget_show(s_sep_item1); + gtk_container_add(GTK_CONTAINER(geany->main_widgets->project_menu), s_sep_item1); s_fif_item = menu_item_new("edit-find", _("Find in Project Files...")); gtk_container_add(GTK_CONTAINER(geany->main_widgets->project_menu), s_fif_item); @@ -282,6 +289,38 @@ void prjorg_menu_init(void) keybindings_set_item(key_group, KB_SWAP_HEADER_SOURCE, NULL, 0, 0, "swap_header_source", _("Swap header/source"), s_shs_item); + s_sep_item2 = gtk_separator_menu_item_new(); + gtk_widget_show(s_sep_item2); + gtk_container_add(GTK_CONTAINER(geany->main_widgets->project_menu), s_sep_item2); + + s_goto_any_item = gtk_menu_item_new_with_mnemonic(_("Go to _Anywhere...")); + gtk_widget_show(s_goto_any_item); + gtk_container_add(GTK_CONTAINER(geany->main_widgets->project_menu), s_goto_any_item); + g_signal_connect(s_goto_any_item, "activate", G_CALLBACK(prjorg_goto_anywhere_for_file), NULL); + keybindings_set_item(key_group, KB_GOTO_ANYWHERE, NULL, 0, 0, "goto_anywhere", + _("Go to anywhere"), s_goto_any_item); + + s_goto_doc_item = gtk_menu_item_new_with_mnemonic(_("Go to _Document Symbol...")); + gtk_widget_show(s_goto_doc_item); + gtk_container_add(GTK_CONTAINER(geany->main_widgets->project_menu), s_goto_doc_item); + g_signal_connect(s_goto_doc_item, "activate", G_CALLBACK(prjorg_goto_anywhere_for_doc), NULL); + keybindings_set_item(key_group, KB_GOTO_DOC_SYMBOL, NULL, 0, 0, "goto_doc_symbol", + _("Go to document symbol"), s_goto_doc_item); + + s_goto_wks_item = gtk_menu_item_new_with_mnemonic(_("Go to _Workspace Symbol...")); + gtk_widget_show(s_goto_wks_item); + gtk_container_add(GTK_CONTAINER(geany->main_widgets->project_menu), s_goto_wks_item); + g_signal_connect(s_goto_wks_item, "activate", G_CALLBACK(prjorg_goto_anywhere_for_workspace), NULL); + keybindings_set_item(key_group, KB_GOTO_WORKSPACE_SYMBOL, NULL, 0, 0, "goto_workspace_symbol", + _("Go to workspace symbol"), s_goto_wks_item); + + s_goto_line_item = gtk_menu_item_new_with_mnemonic(_("Go to _Line...")); + gtk_widget_show(s_goto_line_item); + gtk_container_add(GTK_CONTAINER(geany->main_widgets->project_menu), s_goto_line_item); + g_signal_connect(s_goto_line_item, "activate", G_CALLBACK(prjorg_goto_anywhere_for_line), NULL); + keybindings_set_item(key_group, KB_GOTO_LINE, NULL, 0, 0, "goto_line", + _("Go to line"), s_goto_line_item); + keybindings_set_item(key_group, KB_FOCUS_SIDEBAR, (GeanyKeyCallback)prjorg_sidebar_focus_project_tab, 0, 0, "focus_project_sidebar", _("Focus Project Sidebar"), NULL); @@ -319,7 +358,13 @@ void prjorg_menu_cleanup(void) gtk_widget_destroy(s_ff_item); gtk_widget_destroy(s_ft_item); gtk_widget_destroy(s_shs_item); - gtk_widget_destroy(s_sep_item); + gtk_widget_destroy(s_sep_item1); + + gtk_widget_destroy(s_goto_any_item); + gtk_widget_destroy(s_goto_doc_item); + gtk_widget_destroy(s_goto_wks_item); + gtk_widget_destroy(s_goto_line_item); + gtk_widget_destroy(s_sep_item2); gtk_widget_destroy(s_context_osf_item); gtk_widget_destroy(s_context_sep_item); Modified: projectorganizer/src/prjorg-project.c 2 lines changed, 1 insertions(+), 1 deletions(-) =================================================================== @@ -70,7 +70,7 @@ static void collect_source_files(gchar *filename, TMSourceFile *sf, gpointer use } -/* path - absolute path in locale, returned list in locale */ +/* path - absolute path in locale, returned list in utf8 */ static GSList *get_file_list(const gchar *utf8_path, GSList *patterns, GSList *ignored_dirs_patterns, GSList *ignored_file_patterns, GHashTable *visited_paths) { -------------- This E-Mail was brought to you by github_commit_mail.py (Source:
https://github.com/geany/infrastructure
).
1
0
0
0
[geany/geany-plugins] d9b714: projectorganizer: Add popup panel for navigation
by Jiří Techet
11 Aug '24
11 Aug '24
Branch: refs/heads/master Author: Jiří Techet <techet(a)gmail.com> Committer: Jiří Techet <techet(a)gmail.com> Date: Sun, 11 Aug 2024 17:01:14 UTC Commit: d9b714d002c16e21f9ea35e875f2de62676710bc
https://github.com/geany/geany-plugins/commit/d9b714d002c16e21f9ea35e875f2d…
Log Message: ----------- projectorganizer: Add popup panel for navigation This feature allows quick navigation do document/workspace symbols based on their names, open files, and line numbers. The panel's code is mostly stolen from the LSP plugin which in turn stole it from the Colomban Wendling's Commander plugin. Modified Paths: -------------- po/POTFILES.in projectorganizer/README projectorganizer/src/Makefile.am projectorganizer/src/prjorg-goto-anywhere.c projectorganizer/src/prjorg-goto-anywhere.h projectorganizer/src/prjorg-goto-panel.c projectorganizer/src/prjorg-goto-panel.h projectorganizer/src/prjorg-main.c projectorganizer/src/prjorg-menu.c projectorganizer/src/prjorg-project.c Modified: po/POTFILES.in 1 lines changed, 1 insertions(+), 0 deletions(-) =================================================================== @@ -205,6 +205,7 @@ pretty-printer/src/PluginEntry.c pretty-printer/src/ConfigUI.c # ProjectOrganizer +projectorganizer/src/prjorg-goto-anywhere.c projectorganizer/src/prjorg-main.c projectorganizer/src/prjorg-menu.c projectorganizer/src/prjorg-project.c Modified: projectorganizer/README 7 lines changed, 7 insertions(+), 0 deletions(-) =================================================================== @@ -156,6 +156,13 @@ Project Organizer adds some extra entries under the Project menu: the properties, it opens a project file with the same base name (without extension) matching header patterns (and vice versa). If the files are already open, it just switches the document tabs. Nothing happens if no matching file is found. +* Go to anywhere, Go to document symbol, Go to workspace symbol, Go to line - + these items allow to perform jump to the entered destination. The popup window is + identical for all of these actions, the only difference is the pre-filled prefix + that determines the type of the go to. No prefix performs the search in open and + project files, @ performs the search in current document's symbols, # performs + the search in all workspace symbols, and : performs navigation to the specified + line. Each of these entries can be assigned a key binding under Edit->Preferences->Keybindings. Modified: projectorganizer/src/Makefile.am 6 lines changed, 5 insertions(+), 1 deletions(-) =================================================================== @@ -12,7 +12,11 @@ projectorganizer_la_SOURCES = \ prjorg-utils.h \ prjorg-utils.c \ prjorg-menu.h \ - prjorg-menu.c + prjorg-menu.c \ + prjorg-goto-panel.h \ + prjorg-goto-panel.c \ + prjorg-goto-anywhere.h \ + prjorg-goto-anywhere.c projectorganizer_la_CPPFLAGS = $(AM_CPPFLAGS) \ -DG_LOG_DOMAIN=\"ProjectOrganizer\" Modified: projectorganizer/src/prjorg-goto-anywhere.c 375 lines changed, 375 insertions(+), 0 deletions(-) =================================================================== @@ -0,0 +1,375 @@ +/* + * Copyright 2023 Jiri Techet <techet(a)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. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "prjorg-goto-anywhere.h" +#include "prjorg-goto-panel.h" +#include "prjorg-project.h" + +#include <gtk/gtk.h> +#include <geanyplugin.h> + + +#define SSM(s, m, w, l) scintilla_send_message((s), (m), (w), (l)) + + +typedef struct +{ + GeanyDocument *doc; + gchar *query; +} DocQueryData; + + +extern GeanyData *geany_data; + + +static void goto_line(GeanyDocument *doc, const gchar *line_str) +{ + GPtrArray *arr = g_ptr_array_new_full(0, (GDestroyNotify)prjorg_goto_symbol_free); + gint lineno = atoi(line_str); + gint linenum = sci_get_line_count(doc->editor->sci); + guint i; + + for (i = 0; i < 4; i++) + { + PrjorgGotoSymbol *sym = g_new0(PrjorgGotoSymbol, 1); + + sym->file_name = utils_get_utf8_from_locale(doc->real_path); + sym->icon = TM_ICON_OTHER; + + switch (i) + { + case 0: + /* For translators: Item in a list which, when selected, navigates + * to the line typed in the entry above the list */ + sym->name = g_strdup(_("line typed above")); + if (lineno == 0) + sym->line = sci_get_current_line(doc->editor->sci) + 1; + else if (lineno > linenum) + sym->line = linenum; + else + sym->line = lineno; + break; + + case 1: + /* For translators: Item in a list which, when selected, navigates + * to the beginning of the current document */ + sym->name = g_strdup(_("beginning")); + sym->line = 1; + break; + + case 2: + /* For translators: Item in a list which, when selected, navigates + * to the middle of the current document */ + sym->name = g_strdup(_("middle")); + sym->line = linenum / 2; + break; + + case 3: + /* For translators: Item in a list which, when selected, navigates + * to the end of the current document */ + sym->name = g_strdup(_("end")); + sym->line = linenum; + break; + } + + g_ptr_array_add(arr, sym); + } + + prjorg_goto_panel_fill(arr); + + g_ptr_array_free(arr, TRUE); +} + + +static void goto_file(const gchar *file_str) +{ + GPtrArray *arr = g_ptr_array_new_full(0, (GDestroyNotify)prjorg_goto_symbol_free); + GHashTable *files_added = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); + GPtrArray *filtered; + guint i; + + foreach_document(i) + { + GeanyDocument *doc = documents[i]; + PrjorgGotoSymbol *sym; + + if (!doc->real_path) + continue; + + sym = g_new0(PrjorgGotoSymbol, 1); + sym->file_name = utils_get_utf8_from_locale(doc->real_path); + sym->name = g_path_get_basename(sym->file_name); + sym->icon = TM_ICON_OTHER; + g_ptr_array_add(arr, sym); + + g_hash_table_insert(files_added, g_strdup(sym->file_name), GINT_TO_POINTER(1)); + } + + if (prj_org && prj_org->roots) + { + PrjOrgRoot *root = prj_org->roots->data; + GHashTableIter iter; + gpointer key, value; + + g_hash_table_iter_init(&iter, root->file_table); + while (g_hash_table_iter_next(&iter, &key, &value)) + { + if (!g_hash_table_lookup(files_added, key)) + { + PrjorgGotoSymbol *sym; + sym = g_new0(PrjorgGotoSymbol, 1); + sym->file_name = g_strdup(key); + sym->name = g_path_get_basename(key); + sym->icon = TM_ICON_NONE; + g_ptr_array_add(arr, sym); + } + } + } + + filtered = prjorg_goto_panel_filter(arr, file_str); + prjorg_goto_panel_fill(filtered); + + g_ptr_array_free(filtered, TRUE); + g_ptr_array_free(arr, TRUE); + g_hash_table_destroy(files_added); +} + + +/* symplified hard-coded icons because we don't have access to Geany icon mappings */ +static int get_icon(TMTagType type) +{ + switch (type) + { + case tm_tag_class_t: + return TM_ICON_CLASS; + case tm_tag_macro_t: + case tm_tag_macro_with_arg_t: + case tm_tag_undef_t: + return TM_ICON_MACRO; + case tm_tag_enum_t: + case tm_tag_struct_t: + case tm_tag_typedef_t: + case tm_tag_union_t: + return TM_ICON_STRUCT; + case tm_tag_enumerator_t: + case tm_tag_field_t: + case tm_tag_member_t: + return TM_ICON_MEMBER; + case tm_tag_method_t: + case tm_tag_function_t: + case tm_tag_prototype_t: + return TM_ICON_METHOD; + case tm_tag_interface_t: + case tm_tag_namespace_t: + case tm_tag_package_t: + return TM_ICON_NAMESPACE; + case tm_tag_variable_t: + case tm_tag_externvar_t: + case tm_tag_local_var_t: + case tm_tag_include_t: + return TM_ICON_VAR; + case tm_tag_other_t: + return TM_ICON_OTHER; + default: + return TM_ICON_NONE; + } +} + + +/* stolen from Geany */ +static gboolean langs_compatible(TMParserType lang, TMParserType other) +{ + if (lang == other) + return TRUE; + /* Accept CPP tags for C lang and vice versa - we don't have acces to + * Geany's TM_PARSER_ constants so let's hard-code 0 and 1 here (not too + * terrible things will happen if Geany changes these to something else) */ + else if (lang == 0 && other == 1) + return TRUE; + else if (lang == 1 && other == 0) + return TRUE; + + return FALSE; +} + + +static void goto_tm_symbol(const gchar *query, GPtrArray *tags, TMParserType lang) +{ + GPtrArray *converted = g_ptr_array_new_full(0, (GDestroyNotify)prjorg_goto_symbol_free); + GPtrArray *filtered; + TMTag *tag; + guint i; + + if (tags) + { + foreach_ptr_array(tag, i, tags) + { + if (tag->file && langs_compatible(tag->lang, lang) && + !(tag->type & (tm_tag_include_t | tm_tag_local_var_t))) + { + PrjorgGotoSymbol *sym = g_new0(PrjorgGotoSymbol, 1); + sym->name = g_strdup(tag->name); + sym->file_name = utils_get_utf8_from_locale(tag->file->file_name); + sym->line = tag->line; + sym->icon = get_icon(tag->type); + + g_ptr_array_add(converted, sym); + } + } + } + + filtered = prjorg_goto_panel_filter(converted, query); + prjorg_goto_panel_fill(filtered); + + g_ptr_array_free(filtered, TRUE); + g_ptr_array_free(converted, TRUE); +} + + +static void perform_lookup(const gchar *query) +{ + GeanyDocument *doc = document_get_current(); + const gchar *query_str = query ? query : ""; + + if (g_str_has_prefix(query_str, "#")) + { + if (doc) + { + // TODO: possibly improve performance by binary searching the start and the end point + goto_tm_symbol(query_str+1, geany_data->app->tm_workspace->tags_array, doc->file_type->lang); + } + } + else if (g_str_has_prefix(query_str, "@")) + { + if (doc) + { + GPtrArray *tags = doc->tm_file ? doc->tm_file->tags_array : g_ptr_array_new(); + goto_tm_symbol(query_str+1, tags, doc->file_type->lang); + if (!doc->tm_file) + g_ptr_array_free(tags, TRUE); + } + } + else if (g_str_has_prefix(query_str, ":")) + { + if (doc) + goto_line(doc, query_str+1); + } + else + goto_file(query_str); +} + + +static gchar *get_current_iden(GeanyDocument *doc, gint current_pos) +{ + //TODO: use configured wordchars (also change in Geany) + const gchar *wordchars = GEANY_WORDCHARS; + GeanyFiletypeID ft = doc->file_type->id; + ScintillaObject *sci = doc->editor->sci; + gint start_pos, end_pos, pos; + + if (ft == GEANY_FILETYPES_LATEX) + wordchars = GEANY_WORDCHARS"\\"; /* add \ to word chars if we are in a LaTeX file */ + else if (ft == GEANY_FILETYPES_CSS) + wordchars = GEANY_WORDCHARS"-"; /* add - because they are part of property names */ + + pos = current_pos; + while (TRUE) + { + gint new_pos = SSM(sci, SCI_POSITIONBEFORE, pos, 0); + if (new_pos == pos) + break; + if (pos - new_pos == 1) + { + gchar c = sci_get_char_at(sci, new_pos); + if (!strchr(wordchars, c)) + break; + } + pos = new_pos; + } + start_pos = pos; + + pos = current_pos; + while (TRUE) + { + gint new_pos = SSM(sci, SCI_POSITIONAFTER, pos, 0); + if (new_pos == pos) + break; + if (new_pos - pos == 1) + { + gchar c = sci_get_char_at(sci, pos); + if (!strchr(wordchars, c)) + break; + } + pos = new_pos; + } + end_pos = pos; + + if (start_pos == end_pos) + return NULL; + + return sci_get_contents_range(sci, start_pos, end_pos); +} + + +static void goto_panel_query(const gchar *query_type, gboolean prefill) +{ + GeanyDocument *doc = document_get_current(); + gchar *query = NULL; + gint pos; + + if (prefill && doc) + { + pos = sci_get_current_position(doc->editor->sci); + query = get_current_iden(doc, pos); + } + if (!query) + query = g_strdup(""); + SETPTR(query, g_strconcat(query_type, query, NULL)); + + prjorg_goto_panel_show(query, perform_lookup); + + g_free(query); +} + + +void prjorg_goto_anywhere_for_workspace(void) +{ + goto_panel_query("#", TRUE); +} + + +void prjorg_goto_anywhere_for_doc(void) +{ + goto_panel_query("@", TRUE); +} + + +void prjorg_goto_anywhere_for_line(void) +{ + goto_panel_query(":", FALSE); +} + + +void prjorg_goto_anywhere_for_file(void) +{ + goto_panel_query("", FALSE); +} Modified: projectorganizer/src/prjorg-goto-anywhere.h 28 lines changed, 28 insertions(+), 0 deletions(-) =================================================================== @@ -0,0 +1,28 @@ +/* + * Copyright 2023 Jiri Techet <techet(a)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. + */ + +#ifndef PRJORG_GOTO_ANYWHERE_H +#define PRJORG_GOTO_ANYWHERE_H 1 + + +void prjorg_goto_anywhere_for_workspace(void); +void prjorg_goto_anywhere_for_doc(void); +void prjorg_goto_anywhere_for_line(void); +void prjorg_goto_anywhere_for_file(void); + +#endif /* PRJORG_GOTO_ANYWHERE_H */ Modified: projectorganizer/src/prjorg-goto-panel.c 446 lines changed, 446 insertions(+), 0 deletions(-) =================================================================== @@ -0,0 +1,446 @@ +/* + * Copyright 2023 Jiri Techet <techet(a)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. + */ + +/* This file contains mostly stolen code from the Colomban Wendling's Commander + * plugin. Thanks! */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "prjorg-goto-panel.h" + +#include <gtk/gtk.h> +#include <geanyplugin.h> + + +enum { + COL_ICON, + COL_LABEL, + COL_PATH, + COL_LINENO, + COL_COUNT +}; + + +//TODO: free on plugin unload +struct { + GtkWidget *panel; + GtkWidget *entry; + GtkWidget *tree_view; + GtkListStore *store; +} panel_data = { + NULL, NULL, NULL, NULL +}; + + +static PrjorgGotoPanelLookupFunction lookup_function; + +extern GeanyData *geany_data; + + +void prjorg_goto_symbol_free(PrjorgGotoSymbol *symbol) +{ + g_free(symbol->name); + g_free(symbol->file_name); + g_free(symbol->scope); + g_free(symbol->tooltip); + g_free(symbol); +} + + +static void tree_view_set_cursor_from_iter(GtkTreeView *view, GtkTreeIter *iter) +{ + GtkTreePath *path; + + path = gtk_tree_model_get_path(gtk_tree_view_get_model(view), iter); + gtk_tree_view_set_cursor(view, path, NULL, FALSE); + gtk_tree_path_free(path); +} + + +static void tree_view_move_focus(GtkTreeView *view, GtkMovementStep step, gint amount) +{ + GtkTreeIter iter; + GtkTreePath *path; + GtkTreeModel *model = gtk_tree_view_get_model(view); + gboolean valid = FALSE; + + gtk_tree_view_get_cursor(view, &path, NULL); + if (!path) + valid = gtk_tree_model_get_iter_first(model, &iter); + else + { + switch (step) { + case GTK_MOVEMENT_BUFFER_ENDS: + valid = gtk_tree_model_get_iter_first(model, &iter); + if (valid && amount > 0) + { + GtkTreeIter prev; + + do { + prev = iter; + } while (gtk_tree_model_iter_next(model, &iter)); + iter = prev; + } + break; + + case GTK_MOVEMENT_PAGES: + /* FIXME: move by page */ + case GTK_MOVEMENT_DISPLAY_LINES: + gtk_tree_model_get_iter(model, &iter, path); + if (amount > 0) + { + while ((valid = gtk_tree_model_iter_next(model, &iter)) && --amount > 0) + ; + } + else if (amount < 0) + { + while ((valid = gtk_tree_path_prev(path)) && --amount > 0) + ; + + if (valid) + gtk_tree_model_get_iter(model, &iter, path); + } + break; + + default: + g_assert_not_reached(); + } + gtk_tree_path_free(path); + } + + if (valid) + tree_view_set_cursor_from_iter(view, &iter); + else + gtk_widget_error_bell(GTK_WIDGET(view)); +} + + +static void tree_view_activate_focused_row(GtkTreeView *view) +{ + GtkTreePath *path; + GtkTreeViewColumn *column; + + gtk_tree_view_get_cursor(view, &path, &column); + if (path) + { + gtk_tree_view_row_activated(view, path, column); + gtk_tree_path_free(path); + } +} + + +void prjorg_goto_panel_fill(GPtrArray *symbols) +{ + GtkTreeView *view = GTK_TREE_VIEW(panel_data.tree_view); + GtkTreeIter iter; + PrjorgGotoSymbol *sym; + guint i; + + gtk_list_store_clear(panel_data.store); + + foreach_ptr_array(sym, i, symbols) + { + gchar *label; + + if (!sym->file_name) + continue; + + if (sym->file_name && sym->line > 0) + label = g_markup_printf_escaped("%s\n<small><i>%s:%d</i></small>", + sym->name, sym->file_name, sym->line); + else if (sym->file_name) + label = g_markup_printf_escaped("%s\n<small><i>%s</i></small>", + sym->name, sym->file_name); + else + label = g_markup_printf_escaped("%s", sym->name); + + gtk_list_store_insert_with_values(panel_data.store, NULL, -1, + COL_ICON, symbols_get_icon_pixbuf(sym->icon), + COL_LABEL, label, + COL_PATH, sym->file_name, + COL_LINENO, sym->line, + -1); + + g_free(label); + } + + if (gtk_tree_model_get_iter_first(gtk_tree_view_get_model(view), &iter)) + tree_view_set_cursor_from_iter(GTK_TREE_VIEW(panel_data.tree_view), &iter); +} + + +static gboolean on_panel_key_press_event(GtkWidget *widget, GdkEventKey *event, + gpointer dummy) +{ + switch (event->keyval) { + case GDK_KEY_Escape: + gtk_widget_hide(widget); + return TRUE; + + case GDK_KEY_Tab: + /* avoid leaving the entry */ + return TRUE; + + case GDK_KEY_Return: + case GDK_KEY_KP_Enter: + case GDK_KEY_ISO_Enter: + tree_view_activate_focused_row(GTK_TREE_VIEW(panel_data.tree_view)); + return TRUE; + + case GDK_KEY_Page_Up: + case GDK_KEY_Page_Down: + case GDK_KEY_KP_Page_Up: + case GDK_KEY_KP_Page_Down: + { + gboolean up = event->keyval == GDK_KEY_Page_Up || event->keyval == GDK_KEY_KP_Page_Up; + tree_view_move_focus(GTK_TREE_VIEW(panel_data.tree_view), + GTK_MOVEMENT_PAGES, up ? -1 : 1); + return TRUE; + } + + case GDK_KEY_Up: + case GDK_KEY_Down: + case GDK_KEY_KP_Up: + case GDK_KEY_KP_Down: + { + gboolean up = event->keyval == GDK_KEY_Up || event->keyval == GDK_KEY_KP_Up; + tree_view_move_focus(GTK_TREE_VIEW(panel_data.tree_view), + GTK_MOVEMENT_DISPLAY_LINES, up ? -1 : 1); + return TRUE; + } + } + + return FALSE; +} + + +static void on_entry_text_notify(GObject *object, GParamSpec *pspec, gpointer dummy) +{ + GtkTreeIter iter; + GtkTreeView *view = GTK_TREE_VIEW(panel_data.tree_view); + GtkTreeModel *model = gtk_tree_view_get_model(view); + const gchar *text = gtk_entry_get_text(GTK_ENTRY(panel_data.entry)); + + lookup_function(text); + + if (gtk_tree_model_get_iter_first(model, &iter)) + tree_view_set_cursor_from_iter(view, &iter); +} + + +static void on_entry_activate(GtkEntry *entry, gpointer dummy) +{ + tree_view_activate_focused_row(GTK_TREE_VIEW(panel_data.tree_view)); +} + + +static void on_panel_hide(GtkWidget *widget, gpointer dummy) +{ + gtk_list_store_clear(panel_data.store); +} + + +static void on_panel_show(GtkWidget *widget, gpointer dummy) +{ + const gchar *text = gtk_entry_get_text(GTK_ENTRY(panel_data.entry)); + gboolean select_first = TRUE; + + if (text && (text[0] == ':' || text[0] == '#' || text[0] == '@')) + select_first = FALSE; + + gtk_widget_grab_focus(panel_data.entry); + gtk_editable_select_region(GTK_EDITABLE(panel_data.entry), select_first ? 0 : 1, -1); +} + + +static void on_view_row_activated(GtkTreeView *view, GtkTreePath *path, + GtkTreeViewColumn *column, gpointer dummy) +{ + GtkTreeModel *model = gtk_tree_view_get_model(view); + GtkTreeIter iter; + + if (gtk_tree_model_get_iter(model, &iter, path)) + { + GeanyDocument *doc; + gchar *file_path; + gint line; + + gtk_tree_model_get(model, &iter, + COL_PATH, &file_path, + COL_LINENO, &line, + -1); + + SETPTR(file_path, utils_get_locale_from_utf8(file_path)); + doc = document_open_file(file_path, FALSE, NULL, NULL); + + if (doc && line > 0) + navqueue_goto_line(document_get_current(), doc, line); + + g_free(file_path); + } + + gtk_widget_hide(panel_data.panel); +} + + +static void create_panel(void) +{ + GtkWidget *frame, *box, *scroll; + GtkTreeViewColumn *col; + GtkCellRenderer *renderer; + + panel_data.panel = g_object_new(GTK_TYPE_WINDOW, + "decorated", FALSE, + "default-width", 500, + "default-height", 350, + "transient-for", geany_data->main_widgets->window, + "window-position", GTK_WIN_POS_CENTER_ON_PARENT, + "type-hint", GDK_WINDOW_TYPE_HINT_DIALOG, + "skip-taskbar-hint", TRUE, + "skip-pager-hint", TRUE, + NULL); + g_signal_connect(panel_data.panel, "focus-out-event", + G_CALLBACK(gtk_widget_hide), NULL); + g_signal_connect(panel_data.panel, "show", + G_CALLBACK(on_panel_show), NULL); + g_signal_connect(panel_data.panel, "hide", + G_CALLBACK(on_panel_hide), NULL); + g_signal_connect(panel_data.panel, "key-press-event", + G_CALLBACK(on_panel_key_press_event), NULL); + + frame = gtk_frame_new(NULL); + gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN); + gtk_container_add(GTK_CONTAINER(panel_data.panel), frame); + + box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); + gtk_container_add(GTK_CONTAINER(frame), box); + + panel_data.entry = gtk_entry_new(); + gtk_box_pack_start(GTK_BOX(box), panel_data.entry, FALSE, TRUE, 0); + + scroll = g_object_new(GTK_TYPE_SCROLLED_WINDOW, + "hscrollbar-policy", GTK_POLICY_AUTOMATIC, + "vscrollbar-policy", GTK_POLICY_AUTOMATIC, + NULL); + gtk_box_pack_start(GTK_BOX(box), scroll, TRUE, TRUE, 0); + + panel_data.tree_view = gtk_tree_view_new(); + gtk_widget_set_can_focus(panel_data.tree_view, FALSE); + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(panel_data.tree_view), FALSE); + + panel_data.store = gtk_list_store_new(COL_COUNT, + GDK_TYPE_PIXBUF, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_INT); + gtk_tree_view_set_model(GTK_TREE_VIEW(panel_data.tree_view), GTK_TREE_MODEL(panel_data.store)); + g_object_unref(panel_data.store); + + renderer = gtk_cell_renderer_pixbuf_new(); + col = gtk_tree_view_column_new(); + gtk_tree_view_column_pack_start(col, renderer, FALSE); + gtk_tree_view_column_set_attributes(col, renderer, "pixbuf", COL_ICON, NULL); + g_object_set(renderer, "xalign", 0.0, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(panel_data.tree_view), col); + + renderer = gtk_cell_renderer_text_new(); + g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL); + col = gtk_tree_view_column_new_with_attributes(NULL, renderer, + "markup", COL_LABEL, + NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(panel_data.tree_view), col); + + g_signal_connect(panel_data.tree_view, "row-activated", + G_CALLBACK(on_view_row_activated), NULL); + gtk_container_add(GTK_CONTAINER(scroll), panel_data.tree_view); + + /* connect entry signals after the view is created as they use it */ + g_signal_connect(panel_data.entry, "notify::text", + G_CALLBACK(on_entry_text_notify), NULL); + g_signal_connect(panel_data.entry, "activate", + G_CALLBACK(on_entry_activate), NULL); + + gtk_widget_show_all(frame); +} + + +void prjorg_goto_panel_show(const gchar *query, PrjorgGotoPanelLookupFunction func) +{ + if (!panel_data.panel) + create_panel(); + + lookup_function = func; + + gtk_entry_set_text(GTK_ENTRY(panel_data.entry), query); + gtk_list_store_clear(panel_data.store); + gtk_widget_show(panel_data.panel); + + lookup_function(query); +} + + +GPtrArray *prjorg_goto_panel_filter(GPtrArray *symbols, const gchar *filter) +{ + GPtrArray *ret = g_ptr_array_new(); + gchar *case_normalized_filter; + gchar **tf_strv; + guint i; + guint j = 0; + + if (!symbols) + return ret; + + case_normalized_filter = g_utf8_normalize(filter, -1, G_NORMALIZE_ALL); + SETPTR(case_normalized_filter, g_utf8_casefold(case_normalized_filter, -1)); + + tf_strv = g_strsplit_set(case_normalized_filter, " ", -1); + + for (i = 0; i < symbols->len && j < 20; i++) + { + PrjorgGotoSymbol *symbol = symbols->pdata[i]; + gboolean filtered = FALSE; + gchar *case_normalized_name; + gchar **val; + + case_normalized_name = g_utf8_normalize(symbol->name, -1, G_NORMALIZE_ALL); + SETPTR(case_normalized_name, g_utf8_casefold(case_normalized_name, -1)); + + foreach_strv(val, tf_strv) + { + if (case_normalized_name != NULL && *val != NULL) + filtered = strstr(case_normalized_name, *val) == NULL; + + if (filtered) + break; + } + if (!filtered) + { + g_ptr_array_add(ret, symbol); + j++; + } + + g_free(case_normalized_name); + } + + g_strfreev(tf_strv); + g_free(case_normalized_filter); + + return ret; +} Modified: projectorganizer/src/prjorg-goto-panel.h 44 lines changed, 44 insertions(+), 0 deletions(-) =================================================================== @@ -0,0 +1,44 @@ +/* + * Copyright 2023 Jiri Techet <techet(a)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. + */ + +#ifndef PRJORG_GOTO_PANEL_H +#define PRJORG_GOTO_PANEL_H 1 + +#include <geanyplugin.h> + + +typedef struct +{ + gchar *name; + gchar *file_name; + gchar *scope; + gchar *tooltip; + gint line; + TMIcon icon; +} PrjorgGotoSymbol; + +void prjorg_goto_symbol_free(PrjorgGotoSymbol *symbol); + + +typedef void (*PrjorgGotoPanelLookupFunction) (const char *); + +void prjorg_goto_panel_show(const gchar *query, PrjorgGotoPanelLookupFunction func); +void prjorg_goto_panel_fill(GPtrArray *symbols); +GPtrArray *prjorg_goto_panel_filter(GPtrArray *symbols, const gchar *filter); + +#endif /* PRJORG_GOTO_PANEL_H */ Modified: projectorganizer/src/prjorg-main.c 2 lines changed, 1 insertions(+), 1 deletions(-) =================================================================== @@ -34,7 +34,7 @@ GeanyPlugin *geany_plugin; GeanyData *geany_data; -PLUGIN_VERSION_CHECK(235) +PLUGIN_VERSION_CHECK(248) PLUGIN_SET_TRANSLATABLE_INFO( LOCALEDIR, GETTEXT_PACKAGE, Modified: projectorganizer/src/prjorg-menu.c 55 lines changed, 50 insertions(+), 5 deletions(-) =================================================================== @@ -29,6 +29,7 @@ #include "prjorg-project.h" #include "prjorg-utils.h" #include "prjorg-sidebar.h" +#include "prjorg-goto-anywhere.h" #include <string.h> @@ -45,11 +46,17 @@ enum KB_FOCUS_SIDEBAR, KB_OPEN_FILE_MANAGER, KB_OPEN_TERMINAL, + KB_GOTO_ANYWHERE, + KB_GOTO_DOC_SYMBOL, + KB_GOTO_WORKSPACE_SYMBOL, + KB_GOTO_LINE, KB_COUNT }; -static GtkWidget *s_fif_item, *s_ff_item, *s_ft_item, *s_shs_item, *s_sep_item, *s_context_osf_item, *s_context_sep_item; +static GtkWidget *s_fif_item, *s_ff_item, *s_ft_item, *s_shs_item, *s_sep_item1, + *s_context_osf_item, *s_context_sep_item, + *s_sep_item2, *s_goto_any_item, *s_goto_doc_item, *s_goto_wks_item, *s_goto_line_item; static void on_swap_header_source(G_GNUC_UNUSED GtkMenuItem * menuitem, G_GNUC_UNUSED gpointer user_data) @@ -253,9 +260,9 @@ void prjorg_menu_init(void) { GeanyKeyGroup *key_group = plugin_set_key_group(geany_plugin, "ProjectOrganizer", KB_COUNT, kb_callback); - 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_sep_item1 = gtk_separator_menu_item_new(); + gtk_widget_show(s_sep_item1); + gtk_container_add(GTK_CONTAINER(geany->main_widgets->project_menu), s_sep_item1); s_fif_item = menu_item_new("edit-find", _("Find in Project Files...")); gtk_container_add(GTK_CONTAINER(geany->main_widgets->project_menu), s_fif_item); @@ -282,6 +289,38 @@ void prjorg_menu_init(void) keybindings_set_item(key_group, KB_SWAP_HEADER_SOURCE, NULL, 0, 0, "swap_header_source", _("Swap header/source"), s_shs_item); + s_sep_item2 = gtk_separator_menu_item_new(); + gtk_widget_show(s_sep_item2); + gtk_container_add(GTK_CONTAINER(geany->main_widgets->project_menu), s_sep_item2); + + s_goto_any_item = gtk_menu_item_new_with_mnemonic(_("Go to _Anywhere...")); + gtk_widget_show(s_goto_any_item); + gtk_container_add(GTK_CONTAINER(geany->main_widgets->project_menu), s_goto_any_item); + g_signal_connect(s_goto_any_item, "activate", G_CALLBACK(prjorg_goto_anywhere_for_file), NULL); + keybindings_set_item(key_group, KB_GOTO_ANYWHERE, NULL, 0, 0, "goto_anywhere", + _("Go to anywhere"), s_goto_any_item); + + s_goto_doc_item = gtk_menu_item_new_with_mnemonic(_("Go to _Document Symbol...")); + gtk_widget_show(s_goto_doc_item); + gtk_container_add(GTK_CONTAINER(geany->main_widgets->project_menu), s_goto_doc_item); + g_signal_connect(s_goto_doc_item, "activate", G_CALLBACK(prjorg_goto_anywhere_for_doc), NULL); + keybindings_set_item(key_group, KB_GOTO_DOC_SYMBOL, NULL, 0, 0, "goto_doc_symbol", + _("Go to document symbol"), s_goto_doc_item); + + s_goto_wks_item = gtk_menu_item_new_with_mnemonic(_("Go to _Workspace Symbol...")); + gtk_widget_show(s_goto_wks_item); + gtk_container_add(GTK_CONTAINER(geany->main_widgets->project_menu), s_goto_wks_item); + g_signal_connect(s_goto_wks_item, "activate", G_CALLBACK(prjorg_goto_anywhere_for_workspace), NULL); + keybindings_set_item(key_group, KB_GOTO_WORKSPACE_SYMBOL, NULL, 0, 0, "goto_workspace_symbol", + _("Go to workspace symbol"), s_goto_wks_item); + + s_goto_line_item = gtk_menu_item_new_with_mnemonic(_("Go to _Line...")); + gtk_widget_show(s_goto_line_item); + gtk_container_add(GTK_CONTAINER(geany->main_widgets->project_menu), s_goto_line_item); + g_signal_connect(s_goto_line_item, "activate", G_CALLBACK(prjorg_goto_anywhere_for_line), NULL); + keybindings_set_item(key_group, KB_GOTO_LINE, NULL, 0, 0, "goto_line", + _("Go to line"), s_goto_line_item); + keybindings_set_item(key_group, KB_FOCUS_SIDEBAR, (GeanyKeyCallback)prjorg_sidebar_focus_project_tab, 0, 0, "focus_project_sidebar", _("Focus Project Sidebar"), NULL); @@ -319,7 +358,13 @@ void prjorg_menu_cleanup(void) gtk_widget_destroy(s_ff_item); gtk_widget_destroy(s_ft_item); gtk_widget_destroy(s_shs_item); - gtk_widget_destroy(s_sep_item); + gtk_widget_destroy(s_sep_item1); + + gtk_widget_destroy(s_goto_any_item); + gtk_widget_destroy(s_goto_doc_item); + gtk_widget_destroy(s_goto_wks_item); + gtk_widget_destroy(s_goto_line_item); + gtk_widget_destroy(s_sep_item2); gtk_widget_destroy(s_context_osf_item); gtk_widget_destroy(s_context_sep_item); Modified: projectorganizer/src/prjorg-project.c 2 lines changed, 1 insertions(+), 1 deletions(-) =================================================================== @@ -70,7 +70,7 @@ static void collect_source_files(gchar *filename, TMSourceFile *sf, gpointer use } -/* path - absolute path in locale, returned list in locale */ +/* path - absolute path in locale, returned list in utf8 */ static GSList *get_file_list(const gchar *utf8_path, GSList *patterns, GSList *ignored_dirs_patterns, GSList *ignored_file_patterns, GHashTable *visited_paths) { -------------- This E-Mail was brought to you by github_commit_mail.py (Source:
https://github.com/geany/infrastructure
).
1
0
0
0
Results per page:
10
25
50
100
200