From 50209670e24d25b8188f4d2aad5336749d72a7d0 Mon Sep 17 00:00:00 2001 From: andy5995 Date: Tue, 13 Feb 2024 10:28:49 -0600 Subject: [PATCH 01/23] Add Pinner plugin --- Makefile.am | 4 ++ build/pinner.m4 | 9 ++++ configure.ac | 1 + pinner/Makefile.am | 25 ++++++++++ pinner/pinner.c | 113 +++++++++++++++++++++++++++++++++++++++++++++ po/POTFILES.skip | 3 ++ 6 files changed, 155 insertions(+) create mode 100644 build/pinner.m4 create mode 100644 pinner/Makefile.am create mode 100644 pinner/pinner.c diff --git a/Makefile.am b/Makefile.am index 0ed08b315..d1333cca4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -121,6 +121,10 @@ if ENABLE_PAIRTAGHIGHLIGHTER SUBDIRS += pairtaghighlighter endif +if ENABLE_PINNER +SUBDIRS += pinner +endif + if ENABLE_POHELPER SUBDIRS += pohelper endif diff --git a/build/pinner.m4 b/build/pinner.m4 new file mode 100644 index 000000000..662d781fb --- /dev/null +++ b/build/pinner.m4 @@ -0,0 +1,9 @@ +AC_DEFUN([GP_CHECK_PINNER], +[ + GP_ARG_DISABLE([pinner], [auto]) + GP_COMMIT_PLUGIN_STATUS([Pinner]) + + AC_CONFIG_FILES([ + pinner/Makefile + ]) +]) diff --git a/configure.ac b/configure.ac index 38fafcc95..6b23c9966 100644 --- a/configure.ac +++ b/configure.ac @@ -58,6 +58,7 @@ GP_CHECK_LIPSUM GP_CHECK_MARKDOWN GP_CHECK_OVERVIEW GP_CHECK_PAIRTAGHIGHLIGHTER +GP_CHECK_PINNER GP_CHECK_POHELPER GP_CHECK_PRETTYPRINTER GP_CHECK_PROJECTORGANIZER diff --git a/pinner/Makefile.am b/pinner/Makefile.am new file mode 100644 index 000000000..d5346c859 --- /dev/null +++ b/pinner/Makefile.am @@ -0,0 +1,25 @@ +include $(top_srcdir)/build/vars.build.mk + +plugin = pinner + +geanyplugins_LTLIBRARIES = pinner.la + +pinner_la_SOURCES = \ + pinner.c + +pinner_la_CPPFLAGS = $(AM_CPPFLAGS) \ + -DG_LOG_DOMAIN=\"Pinner\" + +pinner_la_CFLAGS = \ + $(AM_CFLAGS) \ + -fsanitize=address,undefined + +pinner_la_LDFLAGS = \ + $(AM_LDFLAGS) \ + -fsanitize=address,undefined + +pinner_la_LIBADD = \ + $(COMMONLIBS) + +AM_CPPCHECKFLAGS = -DSCE_PAS_DEFAULT=0 +include $(top_srcdir)/build/cppcheck.mk diff --git a/pinner/pinner.c b/pinner/pinner.c new file mode 100644 index 000000000..d8aa25cfd --- /dev/null +++ b/pinner/pinner.c @@ -0,0 +1,113 @@ +/* + * pinner.c + * + * Copyright 2024 Andy Alt + * + * 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 + +static GtkWidget *pinned_view_vbox; +static gint page_number = 0; + +struct pindata { + GeanyPlugin *plugin; + GSList *list; +}; + +static struct pindata *init_pindata(GeanyPlugin *plugin) +{ + static struct pindata container = { + NULL, + NULL + }; + return &container; +} + +static void pin_activate_cb(GtkMenuItem *menuitem, gpointer pdata) +{ + struct pindata *container = pdata; + GeanyPlugin *plugin = container->plugin; + GSList *list = container->list; + + GeanyDocument *doc = document_get_current(); + // Let's not add the pointer to the list, otherwise + // it won't be around when the function returns. + // We might find that it's better to store *doc to the list + // in which case we'll need to use malloc() + gchar tmp_file_name[strlen(doc->file_name) + 1]; + strcpy(tmp_file_name, doc->file_name); + + if (doc != NULL) + { + list = g_slist_append(list, tmp_file_name); + GtkWidget *label = gtk_label_new_with_mnemonic(doc->file_name); + gtk_widget_show(label); + gtk_box_pack_start(GTK_BOX(pinned_view_vbox), label, FALSE, FALSE, 0); + gtk_notebook_set_current_page(GTK_NOTEBOOK(plugin->geany_data->main_widgets->sidebar_notebook), page_number); + } +} + + +static gboolean pin_init(GeanyPlugin *plugin, gpointer pdata) +{ + GtkWidget *main_menu_item; + + struct pindata *container = init_pindata(plugin); + container->plugin = plugin; + + // Create a new menu item and show it + main_menu_item = gtk_menu_item_new_with_mnemonic("Pin Document"); + gtk_widget_show(main_menu_item); + gtk_container_add(GTK_CONTAINER(plugin->geany_data->main_widgets->tools_menu), + main_menu_item); + g_signal_connect(main_menu_item, "activate", + G_CALLBACK(pin_activate_cb), container); + + geany_plugin_set_data(plugin, main_menu_item, NULL); + + pinned_view_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); + gtk_widget_show_all(pinned_view_vbox); + page_number = gtk_notebook_append_page(GTK_NOTEBOOK(plugin->geany_data->main_widgets->sidebar_notebook), + pinned_view_vbox, gtk_label_new(_("Pinned"))); + return TRUE; +} + + +static void pin_cleanup(GeanyPlugin *plugin, gpointer pdata) +{ + GtkWidget *main_menu_item = (GtkWidget *) pdata; + + gtk_widget_destroy(main_menu_item); +} + + +G_MODULE_EXPORT +void geany_load_module(GeanyPlugin *plugin) +{ + plugin->info->name = "Pinner"; + plugin->info->description = "Pin a document"; + plugin->info->version = "0.1.0"; + plugin->info->author = "Andy Alt "; + + plugin->funcs->init = pin_init; + plugin->funcs->cleanup = pin_cleanup; + + GEANY_PLUGIN_REGISTER(plugin, 225); +} diff --git a/po/POTFILES.skip b/po/POTFILES.skip index 54f53c26d..c4d2b090e 100644 --- a/po/POTFILES.skip +++ b/po/POTFILES.skip @@ -2,5 +2,8 @@ # geanyvc geanyvc/src/commit.glade +# Pinner +pinner/pinner.c + # WebHelper webhelper/src/gwh-enum-types.c From 5def3d767465a138aee70a8ce81787b8e83da130 Mon Sep 17 00:00:00 2001 From: andy5995 Date: Wed, 14 Feb 2024 06:33:18 -0600 Subject: [PATCH 02/23] Check for duplicates Doesn't work --- pinner/pinner.c | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/pinner/pinner.c b/pinner/pinner.c index d8aa25cfd..b20721a30 100644 --- a/pinner/pinner.c +++ b/pinner/pinner.c @@ -22,6 +22,7 @@ */ #include +#include static GtkWidget *pinned_view_vbox; static gint page_number = 0; @@ -40,19 +41,36 @@ static struct pindata *init_pindata(GeanyPlugin *plugin) return &container; } +bool is_duplicate(GSList *list, const gchar* file_name) +{ + GSList *iter; + for (iter = list; iter != NULL; iter = g_slist_next(iter)) { + if (g_strcmp0((const gchar *)iter->data, file_name) == 0) { + /* We'll probably want to alert the user the document already + * is pinned */ + return true; + } + } + return false; +} + static void pin_activate_cb(GtkMenuItem *menuitem, gpointer pdata) { struct pindata *container = pdata; GeanyPlugin *plugin = container->plugin; GSList *list = container->list; - GeanyDocument *doc = document_get_current(); - // Let's not add the pointer to the list, otherwise - // it won't be around when the function returns. - // We might find that it's better to store *doc to the list - // in which case we'll need to use malloc() + + if (is_duplicate(list, doc->file_name)) + return; + + /* Let's not add the pointer to the list, otherwise + * it won't be around when the function returns. + * We might find that it's better to store *doc to the list + * in which case we'll need to use malloc() + */ gchar tmp_file_name[strlen(doc->file_name) + 1]; - strcpy(tmp_file_name, doc->file_name); + g_strlcpy(tmp_file_name, doc->file_name, sizeof tmp_file_name); if (doc != NULL) { From 3a293072756b34cee614c80ac329fd1e0bf2f3df Mon Sep 17 00:00:00 2001 From: andy5995 Date: Wed, 14 Feb 2024 06:37:26 -0600 Subject: [PATCH 03/23] check if doc eq NULL --- pinner/pinner.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/pinner/pinner.c b/pinner/pinner.c index b20721a30..419623fee 100644 --- a/pinner/pinner.c +++ b/pinner/pinner.c @@ -59,7 +59,10 @@ static void pin_activate_cb(GtkMenuItem *menuitem, gpointer pdata) struct pindata *container = pdata; GeanyPlugin *plugin = container->plugin; GSList *list = container->list; + GeanyDocument *doc = document_get_current(); + if (doc == NULL) + return; if (is_duplicate(list, doc->file_name)) return; @@ -72,14 +75,12 @@ static void pin_activate_cb(GtkMenuItem *menuitem, gpointer pdata) gchar tmp_file_name[strlen(doc->file_name) + 1]; g_strlcpy(tmp_file_name, doc->file_name, sizeof tmp_file_name); - if (doc != NULL) - { - list = g_slist_append(list, tmp_file_name); - GtkWidget *label = gtk_label_new_with_mnemonic(doc->file_name); - gtk_widget_show(label); - gtk_box_pack_start(GTK_BOX(pinned_view_vbox), label, FALSE, FALSE, 0); - gtk_notebook_set_current_page(GTK_NOTEBOOK(plugin->geany_data->main_widgets->sidebar_notebook), page_number); - } + list = g_slist_append(list, tmp_file_name); + GtkWidget *label = gtk_label_new_with_mnemonic(doc->file_name); + gtk_widget_show(label); + gtk_box_pack_start(GTK_BOX(pinned_view_vbox), label, FALSE, FALSE, 0); + gtk_notebook_set_current_page(GTK_NOTEBOOK(plugin->geany_data->main_widgets->sidebar_notebook), page_number); + } From 4bb86a95810c5da704794d924e11cfae11a44fef Mon Sep 17 00:00:00 2001 From: andy5995 Date: Wed, 14 Feb 2024 07:26:26 -0600 Subject: [PATCH 04/23] use g_strdup instead of strlcpy, point better --- pinner/pinner.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/pinner/pinner.c b/pinner/pinner.c index 419623fee..0d8cc8493 100644 --- a/pinner/pinner.c +++ b/pinner/pinner.c @@ -32,12 +32,13 @@ struct pindata { GSList *list; }; -static struct pindata *init_pindata(GeanyPlugin *plugin) +static struct pindata *init_pindata(void) { static struct pindata container = { NULL, NULL }; + return &container; } @@ -64,23 +65,24 @@ static void pin_activate_cb(GtkMenuItem *menuitem, gpointer pdata) if (doc == NULL) return; + // DEBUG: Print the elements in the list + //printf("List elements:\n"); + //for (GSList *iter = list; iter != NULL; iter = g_slist_next(iter)) + //printf("%s\n", (gchar *)iter->data); + if (is_duplicate(list, doc->file_name)) return; - /* Let's not add the pointer to the list, otherwise - * it won't be around when the function returns. - * We might find that it's better to store *doc to the list - * in which case we'll need to use malloc() - */ - gchar tmp_file_name[strlen(doc->file_name) + 1]; - g_strlcpy(tmp_file_name, doc->file_name, sizeof tmp_file_name); + /* This must be freed when nodes are removed from the list */ + gchar *tmp_file_name = g_strdup(doc->file_name); - list = g_slist_append(list, tmp_file_name); + container->list = g_slist_append(list, tmp_file_name); GtkWidget *label = gtk_label_new_with_mnemonic(doc->file_name); gtk_widget_show(label); gtk_box_pack_start(GTK_BOX(pinned_view_vbox), label, FALSE, FALSE, 0); gtk_notebook_set_current_page(GTK_NOTEBOOK(plugin->geany_data->main_widgets->sidebar_notebook), page_number); + return; } @@ -88,7 +90,7 @@ static gboolean pin_init(GeanyPlugin *plugin, gpointer pdata) { GtkWidget *main_menu_item; - struct pindata *container = init_pindata(plugin); + struct pindata *container = init_pindata(); container->plugin = plugin; // Create a new menu item and show it @@ -112,6 +114,7 @@ static gboolean pin_init(GeanyPlugin *plugin, gpointer pdata) static void pin_cleanup(GeanyPlugin *plugin, gpointer pdata) { GtkWidget *main_menu_item = (GtkWidget *) pdata; + // g_slist_free(list); gtk_widget_destroy(main_menu_item); } From 18b58b305ff01e989ca5322dbe12323fc44a7289 Mon Sep 17 00:00:00 2001 From: andy5995 Date: Wed, 14 Feb 2024 10:40:21 -0600 Subject: [PATCH 05/23] Add non-working popup menu and func to clear list --- pinner/pinner.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++-- pinner/pinner.h | 31 +++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 pinner/pinner.h diff --git a/pinner/pinner.c b/pinner/pinner.c index 0d8cc8493..74bebe52c 100644 --- a/pinner/pinner.c +++ b/pinner/pinner.c @@ -21,8 +21,8 @@ * */ -#include -#include +#include "pinner.h" + static GtkWidget *pinned_view_vbox; static gint page_number = 0; @@ -42,6 +42,37 @@ static struct pindata *init_pindata(void) return &container; } + +void slist_free_wrapper(gpointer pdata) +{ + struct pindata *container = pdata; + GSList *list = container->list; + GSList *iter; + for (iter = list; iter != NULL; iter = g_slist_next(iter)) + g_free(iter->data); + + g_slist_free_full(list, g_free); + container->list = NULL; +} + + +/* A function adapted (pinched) from filebrowser.c */ +static GtkWidget *create_popup_menu(gpointer pdata) +{ + GtkWidget *item, *menu; + + menu = gtk_menu_new(); + + /* 3? What should the size be? */ + /* https://docs.gtk.org/gtk3/ctor.Image.new_from_icon_name.html */ + item = gtk_image_new_from_icon_name("edit-clear", 3); + gtk_widget_show(item); + gtk_container_add(GTK_CONTAINER(menu), item); + g_signal_connect(item, "activate", G_CALLBACK(slist_free_wrapper), pdata); + + return menu; +} + bool is_duplicate(GSList *list, const gchar* file_name) { GSList *iter; @@ -85,6 +116,25 @@ static void pin_activate_cb(GtkMenuItem *menuitem, gpointer pdata) return; } +static gboolean on_button_press(GtkWidget *widget, GdkEventButton *event, gpointer pdata) +{ + //if (event->button == 1 && event->type == GDK_2BUTTON_PRESS) + //{ + //on_open_clicked(NULL, NULL); + //return TRUE; + //} + if (event->button == 3) + { + static GtkWidget *popup_menu = NULL; + + if (popup_menu == NULL) + popup_menu = create_popup_menu(pdata); + + gtk_menu_popup_at_pointer(GTK_MENU(popup_menu), (GdkEvent *) event); + /* don't return TRUE here, unless the selection won't be changed */ + } + return FALSE; +} static gboolean pin_init(GeanyPlugin *plugin, gpointer pdata) { @@ -100,6 +150,8 @@ static gboolean pin_init(GeanyPlugin *plugin, gpointer pdata) main_menu_item); g_signal_connect(main_menu_item, "activate", G_CALLBACK(pin_activate_cb), container); + g_signal_connect(pinned_view_vbox, "button-press-event", + G_CALLBACK(on_button_press), container); geany_plugin_set_data(plugin, main_menu_item, NULL); diff --git a/pinner/pinner.h b/pinner/pinner.h new file mode 100644 index 000000000..4b0d6d4ff --- /dev/null +++ b/pinner/pinner.h @@ -0,0 +1,31 @@ +/* + * pinner.h + * + * Copyright 2024 Andy Alt + * + * 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 +#include + +static struct pindata *init_pindata(void); +void slist_free_wrapper(gpointer pdata); +static GtkWidget *create_popup_menu(gpointer pdata); +bool is_duplicate(GSList *list, const gchar* file_name); +static void pin_activate_cb(GtkMenuItem *menuitem, gpointer pdata); From 73322e4a185e3e50bbd546e1bccbabc5080824e9 Mon Sep 17 00:00:00 2001 From: andy5995 Date: Wed, 14 Feb 2024 10:46:41 -0600 Subject: [PATCH 06/23] add header to Makefile.am --- pinner/Makefile.am | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pinner/Makefile.am b/pinner/Makefile.am index d5346c859..1086773f5 100644 --- a/pinner/Makefile.am +++ b/pinner/Makefile.am @@ -4,8 +4,9 @@ plugin = pinner geanyplugins_LTLIBRARIES = pinner.la -pinner_la_SOURCES = \ - pinner.c +pinner_la_SOURCES = pinner.c + +noinst_HEADERS = pinner.h pinner_la_CPPFLAGS = $(AM_CPPFLAGS) \ -DG_LOG_DOMAIN=\"Pinner\" From c1899e4369a9f5aea2266439e4355231640957b8 Mon Sep 17 00:00:00 2001 From: andy5995 Date: Wed, 14 Feb 2024 11:19:30 -0600 Subject: [PATCH 07/23] Make list global --- pinner/pinner.c | 56 +++++++++++++++---------------------------------- pinner/pinner.h | 7 +++---- 2 files changed, 20 insertions(+), 43 deletions(-) diff --git a/pinner/pinner.c b/pinner/pinner.c index 74bebe52c..db12bd186 100644 --- a/pinner/pinner.c +++ b/pinner/pinner.c @@ -26,38 +26,21 @@ static GtkWidget *pinned_view_vbox; static gint page_number = 0; +static GSList *pin_list = NULL; -struct pindata { - GeanyPlugin *plugin; - GSList *list; -}; - -static struct pindata *init_pindata(void) +void slist_free_wrapper(void) { - static struct pindata container = { - NULL, - NULL - }; - - return &container; -} - - -void slist_free_wrapper(gpointer pdata) -{ - struct pindata *container = pdata; - GSList *list = container->list; GSList *iter; - for (iter = list; iter != NULL; iter = g_slist_next(iter)) + for (iter = pin_list; iter != NULL; iter = g_slist_next(iter)) g_free(iter->data); - g_slist_free_full(list, g_free); - container->list = NULL; + g_slist_free_full(pin_list, g_free); + pin_list = NULL; } /* A function adapted (pinched) from filebrowser.c */ -static GtkWidget *create_popup_menu(gpointer pdata) +static GtkWidget *create_popup_menu(void) { GtkWidget *item, *menu; @@ -68,15 +51,15 @@ static GtkWidget *create_popup_menu(gpointer pdata) item = gtk_image_new_from_icon_name("edit-clear", 3); gtk_widget_show(item); gtk_container_add(GTK_CONTAINER(menu), item); - g_signal_connect(item, "activate", G_CALLBACK(slist_free_wrapper), pdata); + g_signal_connect(item, "activate", G_CALLBACK(slist_free_wrapper), NULL); return menu; } -bool is_duplicate(GSList *list, const gchar* file_name) +bool is_duplicate(const gchar* file_name) { GSList *iter; - for (iter = list; iter != NULL; iter = g_slist_next(iter)) { + for (iter = pin_list; iter != NULL; iter = g_slist_next(iter)) { if (g_strcmp0((const gchar *)iter->data, file_name) == 0) { /* We'll probably want to alert the user the document already * is pinned */ @@ -88,9 +71,7 @@ bool is_duplicate(GSList *list, const gchar* file_name) static void pin_activate_cb(GtkMenuItem *menuitem, gpointer pdata) { - struct pindata *container = pdata; - GeanyPlugin *plugin = container->plugin; - GSList *list = container->list; + GeanyPlugin *plugin = pdata; GeanyDocument *doc = document_get_current(); if (doc == NULL) @@ -101,13 +82,13 @@ static void pin_activate_cb(GtkMenuItem *menuitem, gpointer pdata) //for (GSList *iter = list; iter != NULL; iter = g_slist_next(iter)) //printf("%s\n", (gchar *)iter->data); - if (is_duplicate(list, doc->file_name)) + if (is_duplicate(doc->file_name)) return; /* This must be freed when nodes are removed from the list */ gchar *tmp_file_name = g_strdup(doc->file_name); - container->list = g_slist_append(list, tmp_file_name); + pin_list = g_slist_append(pin_list, tmp_file_name); GtkWidget *label = gtk_label_new_with_mnemonic(doc->file_name); gtk_widget_show(label); gtk_box_pack_start(GTK_BOX(pinned_view_vbox), label, FALSE, FALSE, 0); @@ -116,7 +97,7 @@ static void pin_activate_cb(GtkMenuItem *menuitem, gpointer pdata) return; } -static gboolean on_button_press(GtkWidget *widget, GdkEventButton *event, gpointer pdata) +static gboolean on_button_press(GtkWidget *widget, GdkEventButton *event) { //if (event->button == 1 && event->type == GDK_2BUTTON_PRESS) //{ @@ -128,7 +109,7 @@ static gboolean on_button_press(GtkWidget *widget, GdkEventButton *event, gpoint static GtkWidget *popup_menu = NULL; if (popup_menu == NULL) - popup_menu = create_popup_menu(pdata); + popup_menu = create_popup_menu(); gtk_menu_popup_at_pointer(GTK_MENU(popup_menu), (GdkEvent *) event); /* don't return TRUE here, unless the selection won't be changed */ @@ -140,18 +121,15 @@ static gboolean pin_init(GeanyPlugin *plugin, gpointer pdata) { GtkWidget *main_menu_item; - struct pindata *container = init_pindata(); - container->plugin = plugin; - // Create a new menu item and show it main_menu_item = gtk_menu_item_new_with_mnemonic("Pin Document"); gtk_widget_show(main_menu_item); gtk_container_add(GTK_CONTAINER(plugin->geany_data->main_widgets->tools_menu), main_menu_item); g_signal_connect(main_menu_item, "activate", - G_CALLBACK(pin_activate_cb), container); + G_CALLBACK(pin_activate_cb), plugin); g_signal_connect(pinned_view_vbox, "button-press-event", - G_CALLBACK(on_button_press), container); + G_CALLBACK(on_button_press), NULL); geany_plugin_set_data(plugin, main_menu_item, NULL); @@ -166,7 +144,7 @@ static gboolean pin_init(GeanyPlugin *plugin, gpointer pdata) static void pin_cleanup(GeanyPlugin *plugin, gpointer pdata) { GtkWidget *main_menu_item = (GtkWidget *) pdata; - // g_slist_free(list); + slist_free_wrapper(); gtk_widget_destroy(main_menu_item); } diff --git a/pinner/pinner.h b/pinner/pinner.h index 4b0d6d4ff..eac4d4350 100644 --- a/pinner/pinner.h +++ b/pinner/pinner.h @@ -24,8 +24,7 @@ #include #include -static struct pindata *init_pindata(void); -void slist_free_wrapper(gpointer pdata); -static GtkWidget *create_popup_menu(gpointer pdata); -bool is_duplicate(GSList *list, const gchar* file_name); +void slist_free_wrapper(void); +static GtkWidget *create_popup_menu(void); +bool is_duplicate(const gchar* file_name); static void pin_activate_cb(GtkMenuItem *menuitem, gpointer pdata); From afeeb5952c1f9a56aa15117c723ecebfe2c20ba1 Mon Sep 17 00:00:00 2001 From: andy5995 Date: Wed, 14 Feb 2024 12:05:48 -0600 Subject: [PATCH 08/23] Remove header, make all functions static --- pinner/Makefile.am | 2 -- pinner/pinner.c | 11 ++++++++--- pinner/pinner.h | 30 ------------------------------ 3 files changed, 8 insertions(+), 35 deletions(-) delete mode 100644 pinner/pinner.h diff --git a/pinner/Makefile.am b/pinner/Makefile.am index 1086773f5..4aa2f6177 100644 --- a/pinner/Makefile.am +++ b/pinner/Makefile.am @@ -6,8 +6,6 @@ geanyplugins_LTLIBRARIES = pinner.la pinner_la_SOURCES = pinner.c -noinst_HEADERS = pinner.h - pinner_la_CPPFLAGS = $(AM_CPPFLAGS) \ -DG_LOG_DOMAIN=\"Pinner\" diff --git a/pinner/pinner.c b/pinner/pinner.c index db12bd186..e4c6dfbf4 100644 --- a/pinner/pinner.c +++ b/pinner/pinner.c @@ -21,14 +21,19 @@ * */ -#include "pinner.h" +#include +#include +static void slist_free_wrapper(void); +static GtkWidget *create_popup_menu(void); +static bool is_duplicate(const gchar* file_name); +static void pin_activate_cb(GtkMenuItem *menuitem, gpointer pdata); static GtkWidget *pinned_view_vbox; static gint page_number = 0; static GSList *pin_list = NULL; -void slist_free_wrapper(void) +static void slist_free_wrapper(void) { GSList *iter; for (iter = pin_list; iter != NULL; iter = g_slist_next(iter)) @@ -56,7 +61,7 @@ static GtkWidget *create_popup_menu(void) return menu; } -bool is_duplicate(const gchar* file_name) +static bool is_duplicate(const gchar* file_name) { GSList *iter; for (iter = pin_list; iter != NULL; iter = g_slist_next(iter)) { diff --git a/pinner/pinner.h b/pinner/pinner.h deleted file mode 100644 index eac4d4350..000000000 --- a/pinner/pinner.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * pinner.h - * - * Copyright 2024 Andy Alt - * - * 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 -#include - -void slist_free_wrapper(void); -static GtkWidget *create_popup_menu(void); -bool is_duplicate(const gchar* file_name); -static void pin_activate_cb(GtkMenuItem *menuitem, gpointer pdata); From 093184d081fdce62111d39cddc69fda07eb74088 Mon Sep 17 00:00:00 2001 From: andy5995 Date: Thu, 15 Feb 2024 06:02:01 -0600 Subject: [PATCH 09/23] Add UNpin function --- pinner/pinner.c | 90 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 73 insertions(+), 17 deletions(-) diff --git a/pinner/pinner.c b/pinner/pinner.c index e4c6dfbf4..69a84c829 100644 --- a/pinner/pinner.c +++ b/pinner/pinner.c @@ -24,15 +24,33 @@ #include #include +enum { + DO_PIN, + DO_UNPIN +}; + static void slist_free_wrapper(void); static GtkWidget *create_popup_menu(void); static bool is_duplicate(const gchar* file_name); static void pin_activate_cb(GtkMenuItem *menuitem, gpointer pdata); +static void unpin_activate_cb(GtkMenuItem *menuitem, gpointer pdata); static GtkWidget *pinned_view_vbox; static gint page_number = 0; static GSList *pin_list = NULL; +/* DEBUG: Print the elements in the list + * + * This function will get removed later + */ +static void print_list(void) +{ + printf("List elements:\n"); + for (GSList *iter = pin_list; iter != NULL; iter = g_slist_next(iter)) + printf("%s\n", (gchar *)iter->data); +} + + static void slist_free_wrapper(void) { GSList *iter; @@ -64,8 +82,10 @@ static GtkWidget *create_popup_menu(void) static bool is_duplicate(const gchar* file_name) { GSList *iter; - for (iter = pin_list; iter != NULL; iter = g_slist_next(iter)) { - if (g_strcmp0((const gchar *)iter->data, file_name) == 0) { + for (iter = pin_list; iter != NULL; iter = g_slist_next(iter)) + { + if (g_strcmp0((const gchar *)iter->data, file_name) == 0) + { /* We'll probably want to alert the user the document already * is pinned */ return true; @@ -82,11 +102,6 @@ static void pin_activate_cb(GtkMenuItem *menuitem, gpointer pdata) if (doc == NULL) return; - // DEBUG: Print the elements in the list - //printf("List elements:\n"); - //for (GSList *iter = list; iter != NULL; iter = g_slist_next(iter)) - //printf("%s\n", (gchar *)iter->data); - if (is_duplicate(doc->file_name)) return; @@ -99,9 +114,38 @@ static void pin_activate_cb(GtkMenuItem *menuitem, gpointer pdata) gtk_box_pack_start(GTK_BOX(pinned_view_vbox), label, FALSE, FALSE, 0); gtk_notebook_set_current_page(GTK_NOTEBOOK(plugin->geany_data->main_widgets->sidebar_notebook), page_number); + /* remove me */ + print_list(); + return; } + +static void unpin_activate_cb(GtkMenuItem *menuitem, gpointer pdata) +{ + GeanyDocument *doc = document_get_current(); + if (doc == NULL) + return; + + GSList *iter; + for (iter = pin_list; iter != NULL; iter = g_slist_next(iter)) + { + if (g_strcmp0((const gchar *)iter->data, doc->file_name) == 0) + { + g_free(iter->data); + pin_list = g_slist_delete_link(pin_list, iter); + /* Add code to refresh the "Pinned" page */ + break; + } + } + + /* remove me */ + print_list(); + + return; +} + + static gboolean on_button_press(GtkWidget *widget, GdkEventButton *event) { //if (event->button == 1 && event->type == GDK_2BUTTON_PRESS) @@ -124,19 +168,28 @@ static gboolean on_button_press(GtkWidget *widget, GdkEventButton *event) static gboolean pin_init(GeanyPlugin *plugin, gpointer pdata) { - GtkWidget *main_menu_item; + GtkWidget *tools_item[] = { + gtk_menu_item_new_with_mnemonic("Pin Document"), + gtk_menu_item_new_with_mnemonic("Unpin Document"), + NULL + }; - // Create a new menu item and show it - main_menu_item = gtk_menu_item_new_with_mnemonic("Pin Document"); - gtk_widget_show(main_menu_item); + gtk_widget_show(tools_item[DO_PIN]); gtk_container_add(GTK_CONTAINER(plugin->geany_data->main_widgets->tools_menu), - main_menu_item); - g_signal_connect(main_menu_item, "activate", + tools_item[DO_PIN]); + g_signal_connect(tools_item[DO_PIN], "activate", G_CALLBACK(pin_activate_cb), plugin); + + gtk_widget_show(tools_item[DO_UNPIN]); + gtk_container_add(GTK_CONTAINER(plugin->geany_data->main_widgets->tools_menu), + tools_item[DO_UNPIN]); + g_signal_connect(tools_item[DO_UNPIN], "activate", + G_CALLBACK(unpin_activate_cb), NULL); + g_signal_connect(pinned_view_vbox, "button-press-event", G_CALLBACK(on_button_press), NULL); - geany_plugin_set_data(plugin, main_menu_item, NULL); + geany_plugin_set_data(plugin, tools_item, NULL); pinned_view_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); gtk_widget_show_all(pinned_view_vbox); @@ -148,10 +201,13 @@ static gboolean pin_init(GeanyPlugin *plugin, gpointer pdata) static void pin_cleanup(GeanyPlugin *plugin, gpointer pdata) { - GtkWidget *main_menu_item = (GtkWidget *) pdata; - slist_free_wrapper(); + GtkWidget **tools_item = pdata; + while (*tools_item != NULL) { + gtk_widget_destroy(*tools_item); + tools_item++; + } - gtk_widget_destroy(main_menu_item); + slist_free_wrapper(); } From 6a96d0c7f3932fd68bf281ff8b895126e66df719 Mon Sep 17 00:00:00 2001 From: andy5995 Date: Thu, 15 Feb 2024 09:59:01 -0600 Subject: [PATCH 10/23] Make documents clickable and open them --- pinner/pinner.c | 66 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 48 insertions(+), 18 deletions(-) diff --git a/pinner/pinner.c b/pinner/pinner.c index 69a84c829..9eed54e6c 100644 --- a/pinner/pinner.c +++ b/pinner/pinner.c @@ -32,10 +32,12 @@ enum { static void slist_free_wrapper(void); static GtkWidget *create_popup_menu(void); static bool is_duplicate(const gchar* file_name); +static gboolean label_clicked_cb(GtkWidget *widget, GdkEventButton *event, gpointer data); static void pin_activate_cb(GtkMenuItem *menuitem, gpointer pdata); static void unpin_activate_cb(GtkMenuItem *menuitem, gpointer pdata); static GtkWidget *pinned_view_vbox; +// static GtkWidget *event_box; static gint page_number = 0; static GSList *pin_list = NULL; @@ -107,13 +109,18 @@ static void pin_activate_cb(GtkMenuItem *menuitem, gpointer pdata) /* This must be freed when nodes are removed from the list */ gchar *tmp_file_name = g_strdup(doc->file_name); - pin_list = g_slist_append(pin_list, tmp_file_name); + + GtkWidget *event_box = gtk_event_box_new(); GtkWidget *label = gtk_label_new_with_mnemonic(doc->file_name); - gtk_widget_show(label); - gtk_box_pack_start(GTK_BOX(pinned_view_vbox), label, FALSE, FALSE, 0); + gtk_container_add(GTK_CONTAINER(event_box), label); + gtk_widget_show_all(event_box); + gtk_box_pack_start(GTK_BOX(pinned_view_vbox), event_box, FALSE, FALSE, 0); gtk_notebook_set_current_page(GTK_NOTEBOOK(plugin->geany_data->main_widgets->sidebar_notebook), page_number); + g_signal_connect(event_box, "button-press-event", + G_CALLBACK(label_clicked_cb), tmp_file_name); + /* remove me */ print_list(); @@ -146,26 +153,47 @@ static void unpin_activate_cb(GtkMenuItem *menuitem, gpointer pdata) } -static gboolean on_button_press(GtkWidget *widget, GdkEventButton *event) +static gboolean label_clicked_cb(GtkWidget *widget, GdkEventButton *event, gpointer data) { - //if (event->button == 1 && event->type == GDK_2BUTTON_PRESS) - //{ - //on_open_clicked(NULL, NULL); - //return TRUE; - //} - if (event->button == 3) + if (event->type == GDK_BUTTON_PRESS && event->button == 1) { - static GtkWidget *popup_menu = NULL; - - if (popup_menu == NULL) - popup_menu = create_popup_menu(); + // Check if the clicked widget is an event box + if (GTK_IS_EVENT_BOX(widget)) + { + GtkWidget *label = gtk_bin_get_child(GTK_BIN(widget)); - gtk_menu_popup_at_pointer(GTK_MENU(popup_menu), (GdkEvent *) event); - /* don't return TRUE here, unless the selection won't be changed */ + if (GTK_IS_LABEL(label)) + { + const gchar *file_name = gtk_label_get_text(GTK_LABEL(label)); + document_open_file(file_name, FALSE, NULL, NULL); + } + } } + return FALSE; } + +//static gboolean on_button_press(GtkWidget *widget, GdkEventButton *event) +//{ + ////if (event->button == 1 && event->type == GDK_2BUTTON_PRESS) + ////{ + ////on_open_clicked(NULL, NULL); + ////return TRUE; + ////} + //if (event->button == 3) + //{ + //static GtkWidget *popup_menu = NULL; + + //if (popup_menu == NULL) + //popup_menu = create_popup_menu(); + + //gtk_menu_popup_at_pointer(GTK_MENU(popup_menu), (GdkEvent *) event); + ///* don't return TRUE here, unless the selection won't be changed */ + //} + //return FALSE; +//} + static gboolean pin_init(GeanyPlugin *plugin, gpointer pdata) { GtkWidget *tools_item[] = { @@ -186,8 +214,10 @@ static gboolean pin_init(GeanyPlugin *plugin, gpointer pdata) g_signal_connect(tools_item[DO_UNPIN], "activate", G_CALLBACK(unpin_activate_cb), NULL); - g_signal_connect(pinned_view_vbox, "button-press-event", - G_CALLBACK(on_button_press), NULL); + //g_signal_connect(event_box, "button-press-event", + //G_CALLBACK(on_button_press), NULL); + //g_signal_connect(pinned_view_vbox, "button-press-event", + //G_CALLBACK(on_button_press), NULL); geany_plugin_set_data(plugin, tools_item, NULL); From b8d66c4101dfb12d366beb30b4d1ebb2b551d0a5 Mon Sep 17 00:00:00 2001 From: andy5995 Date: Thu, 15 Feb 2024 10:05:21 -0600 Subject: [PATCH 11/23] Don't auto-switch to the "Pinned" tab --- pinner/pinner.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pinner/pinner.c b/pinner/pinner.c index 9eed54e6c..42360c2ba 100644 --- a/pinner/pinner.c +++ b/pinner/pinner.c @@ -98,8 +98,6 @@ static bool is_duplicate(const gchar* file_name) static void pin_activate_cb(GtkMenuItem *menuitem, gpointer pdata) { - GeanyPlugin *plugin = pdata; - GeanyDocument *doc = document_get_current(); if (doc == NULL) return; @@ -116,7 +114,7 @@ static void pin_activate_cb(GtkMenuItem *menuitem, gpointer pdata) gtk_container_add(GTK_CONTAINER(event_box), label); gtk_widget_show_all(event_box); gtk_box_pack_start(GTK_BOX(pinned_view_vbox), event_box, FALSE, FALSE, 0); - gtk_notebook_set_current_page(GTK_NOTEBOOK(plugin->geany_data->main_widgets->sidebar_notebook), page_number); + // gtk_notebook_set_current_page(GTK_NOTEBOOK(plugin->geany_data->main_widgets->sidebar_notebook), page_number); g_signal_connect(event_box, "button-press-event", G_CALLBACK(label_clicked_cb), tmp_file_name); @@ -206,7 +204,7 @@ static gboolean pin_init(GeanyPlugin *plugin, gpointer pdata) gtk_container_add(GTK_CONTAINER(plugin->geany_data->main_widgets->tools_menu), tools_item[DO_PIN]); g_signal_connect(tools_item[DO_PIN], "activate", - G_CALLBACK(pin_activate_cb), plugin); + G_CALLBACK(pin_activate_cb), NULL); gtk_widget_show(tools_item[DO_UNPIN]); gtk_container_add(GTK_CONTAINER(plugin->geany_data->main_widgets->tools_menu), From a1fe9573bb09b841a96e01354b65a71a10c31a20 Mon Sep 17 00:00:00 2001 From: andy5995 Date: Fri, 16 Feb 2024 06:32:13 -0600 Subject: [PATCH 12/23] fix double-free error when freeing list --- pinner/pinner.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pinner/pinner.c b/pinner/pinner.c index 42360c2ba..2d369b8e9 100644 --- a/pinner/pinner.c +++ b/pinner/pinner.c @@ -59,8 +59,7 @@ static void slist_free_wrapper(void) for (iter = pin_list; iter != NULL; iter = g_slist_next(iter)) g_free(iter->data); - g_slist_free_full(pin_list, g_free); - pin_list = NULL; + g_slist_free(pin_list); } From cf40001168fa9e3b9bae629edf72d6af9af04027 Mon Sep 17 00:00:00 2001 From: andy5995 Date: Fri, 16 Feb 2024 06:43:04 -0600 Subject: [PATCH 13/23] fix 'stack-use-after-return` in pin_cleanup --- pinner/pinner.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pinner/pinner.c b/pinner/pinner.c index 2d369b8e9..e5535129f 100644 --- a/pinner/pinner.c +++ b/pinner/pinner.c @@ -193,11 +193,10 @@ static gboolean label_clicked_cb(GtkWidget *widget, GdkEventButton *event, gpoin static gboolean pin_init(GeanyPlugin *plugin, gpointer pdata) { - GtkWidget *tools_item[] = { - gtk_menu_item_new_with_mnemonic("Pin Document"), - gtk_menu_item_new_with_mnemonic("Unpin Document"), - NULL - }; + GtkWidget **tools_item = g_new0(GtkWidget*, 3); // Allocate memory for 3 pointers (2 items + NULL terminator) + tools_item[DO_PIN] = gtk_menu_item_new_with_mnemonic("Pin Document"); + tools_item[DO_UNPIN] = gtk_menu_item_new_with_mnemonic("Unpin Document"); + tools_item[2] = NULL; // NULL terminator gtk_widget_show(tools_item[DO_PIN]); gtk_container_add(GTK_CONTAINER(plugin->geany_data->main_widgets->tools_menu), @@ -228,13 +227,14 @@ static gboolean pin_init(GeanyPlugin *plugin, gpointer pdata) static void pin_cleanup(GeanyPlugin *plugin, gpointer pdata) { + slist_free_wrapper(); + GtkWidget **tools_item = pdata; while (*tools_item != NULL) { gtk_widget_destroy(*tools_item); tools_item++; } - - slist_free_wrapper(); + g_free(pdata); } From 5391654fb522550bfab02bbebcfb7c6b3b01254f Mon Sep 17 00:00:00 2001 From: andy5995 Date: Fri, 16 Feb 2024 07:23:31 -0600 Subject: [PATCH 14/23] implement unpin, use hashtable instead of GSList --- pinner/pinner.c | 80 ++++++++++++++++--------------------------------- 1 file changed, 25 insertions(+), 55 deletions(-) diff --git a/pinner/pinner.c b/pinner/pinner.c index e5535129f..eb62f5318 100644 --- a/pinner/pinner.c +++ b/pinner/pinner.c @@ -29,7 +29,6 @@ enum { DO_UNPIN }; -static void slist_free_wrapper(void); static GtkWidget *create_popup_menu(void); static bool is_duplicate(const gchar* file_name); static gboolean label_clicked_cb(GtkWidget *widget, GdkEventButton *event, gpointer data); @@ -37,32 +36,15 @@ static void pin_activate_cb(GtkMenuItem *menuitem, gpointer pdata); static void unpin_activate_cb(GtkMenuItem *menuitem, gpointer pdata); static GtkWidget *pinned_view_vbox; -// static GtkWidget *event_box; static gint page_number = 0; -static GSList *pin_list = NULL; +static GHashTable *doc_to_widget_map = NULL; -/* DEBUG: Print the elements in the list - * - * This function will get removed later - */ -static void print_list(void) -{ - printf("List elements:\n"); - for (GSList *iter = pin_list; iter != NULL; iter = g_slist_next(iter)) - printf("%s\n", (gchar *)iter->data); -} - - -static void slist_free_wrapper(void) +void destroy_widget(gpointer pdata) { - GSList *iter; - for (iter = pin_list; iter != NULL; iter = g_slist_next(iter)) - g_free(iter->data); - - g_slist_free(pin_list); + GtkWidget *widget = (GtkWidget *)pdata; + gtk_widget_destroy(widget); } - /* A function adapted (pinched) from filebrowser.c */ static GtkWidget *create_popup_menu(void) { @@ -75,26 +57,18 @@ static GtkWidget *create_popup_menu(void) item = gtk_image_new_from_icon_name("edit-clear", 3); gtk_widget_show(item); gtk_container_add(GTK_CONTAINER(menu), item); - g_signal_connect(item, "activate", G_CALLBACK(slist_free_wrapper), NULL); return menu; } -static bool is_duplicate(const gchar* file_name) -{ - GSList *iter; - for (iter = pin_list; iter != NULL; iter = g_slist_next(iter)) - { - if (g_strcmp0((const gchar *)iter->data, file_name) == 0) - { - /* We'll probably want to alert the user the document already - * is pinned */ - return true; - } - } - return false; + +static bool is_duplicate(const gchar* file_name) { + // Check if the file name (document) is already a key in the hash map. + // g_hash_table_contains returns TRUE if the key is found, FALSE otherwise. + return g_hash_table_contains(doc_to_widget_map, file_name); } + static void pin_activate_cb(GtkMenuItem *menuitem, gpointer pdata) { GeanyDocument *doc = document_get_current(); @@ -106,9 +80,10 @@ static void pin_activate_cb(GtkMenuItem *menuitem, gpointer pdata) /* This must be freed when nodes are removed from the list */ gchar *tmp_file_name = g_strdup(doc->file_name); - pin_list = g_slist_append(pin_list, tmp_file_name); + // pin_list = g_slist_append(pin_list, tmp_file_name); GtkWidget *event_box = gtk_event_box_new(); + g_hash_table_insert(doc_to_widget_map, tmp_file_name, event_box); GtkWidget *label = gtk_label_new_with_mnemonic(doc->file_name); gtk_container_add(GTK_CONTAINER(event_box), label); gtk_widget_show_all(event_box); @@ -118,9 +93,6 @@ static void pin_activate_cb(GtkMenuItem *menuitem, gpointer pdata) g_signal_connect(event_box, "button-press-event", G_CALLBACK(label_clicked_cb), tmp_file_name); - /* remove me */ - print_list(); - return; } @@ -131,21 +103,13 @@ static void unpin_activate_cb(GtkMenuItem *menuitem, gpointer pdata) if (doc == NULL) return; - GSList *iter; - for (iter = pin_list; iter != NULL; iter = g_slist_next(iter)) - { - if (g_strcmp0((const gchar *)iter->data, doc->file_name) == 0) - { - g_free(iter->data); - pin_list = g_slist_delete_link(pin_list, iter); - /* Add code to refresh the "Pinned" page */ - break; - } + gboolean removed = g_hash_table_remove(doc_to_widget_map, doc->file_name); + // If removed + if (!removed) + { + // Handle the case where the document was not found in the map } - /* remove me */ - print_list(); - return; } @@ -196,7 +160,9 @@ static gboolean pin_init(GeanyPlugin *plugin, gpointer pdata) GtkWidget **tools_item = g_new0(GtkWidget*, 3); // Allocate memory for 3 pointers (2 items + NULL terminator) tools_item[DO_PIN] = gtk_menu_item_new_with_mnemonic("Pin Document"); tools_item[DO_UNPIN] = gtk_menu_item_new_with_mnemonic("Unpin Document"); - tools_item[2] = NULL; // NULL terminator + tools_item[2] = NULL; // NULL sentinel + + doc_to_widget_map = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, destroy_widget); gtk_widget_show(tools_item[DO_PIN]); gtk_container_add(GTK_CONTAINER(plugin->geany_data->main_widgets->tools_menu), @@ -227,7 +193,11 @@ static gboolean pin_init(GeanyPlugin *plugin, gpointer pdata) static void pin_cleanup(GeanyPlugin *plugin, gpointer pdata) { - slist_free_wrapper(); + if (doc_to_widget_map != NULL) + { + g_hash_table_destroy(doc_to_widget_map); + doc_to_widget_map = NULL; + } GtkWidget **tools_item = pdata; while (*tools_item != NULL) { From 87b8b4e27b856d27296727c84bacb62c0c1ebf5b Mon Sep 17 00:00:00 2001 From: andy5995 Date: Fri, 16 Feb 2024 08:08:29 -0600 Subject: [PATCH 15/23] Add right-click menu/option to clear pinned list --- pinner/pinner.c | 94 ++++++++++++++++++++++++++++--------------------- 1 file changed, 54 insertions(+), 40 deletions(-) diff --git a/pinner/pinner.c b/pinner/pinner.c index eb62f5318..670947818 100644 --- a/pinner/pinner.c +++ b/pinner/pinner.c @@ -22,16 +22,17 @@ */ #include -#include enum { DO_PIN, DO_UNPIN }; +static void destroy_widget(gpointer pdata); +static void clear_pinned_documents(void); static GtkWidget *create_popup_menu(void); -static bool is_duplicate(const gchar* file_name); -static gboolean label_clicked_cb(GtkWidget *widget, GdkEventButton *event, gpointer data); +static gboolean on_button_press_cb(GtkWidget *widget, GdkEventButton *event, gpointer data); +static gboolean is_duplicate(const gchar* file_name); static void pin_activate_cb(GtkMenuItem *menuitem, gpointer pdata); static void unpin_activate_cb(GtkMenuItem *menuitem, gpointer pdata); @@ -39,33 +40,61 @@ static GtkWidget *pinned_view_vbox; static gint page_number = 0; static GHashTable *doc_to_widget_map = NULL; -void destroy_widget(gpointer pdata) +static void destroy_widget(gpointer pdata) { GtkWidget *widget = (GtkWidget *)pdata; gtk_widget_destroy(widget); } -/* A function adapted (pinched) from filebrowser.c */ -static GtkWidget *create_popup_menu(void) + +void clear_pinned_documents(void) { - GtkWidget *item, *menu; + if (doc_to_widget_map != NULL) + { + // Removes all keys and their associated values from the hash table. + // This will also call the destroy functions specified for keys and values, + // thus freeing the memory for the file names and destroying the widgets. + g_hash_table_remove_all(doc_to_widget_map); + } +} + + +static GtkWidget *create_popup_menu(void) { + GtkWidget *menu; + GtkWidget *clear_item; menu = gtk_menu_new(); - /* 3? What should the size be? */ - /* https://docs.gtk.org/gtk3/ctor.Image.new_from_icon_name.html */ - item = gtk_image_new_from_icon_name("edit-clear", 3); - gtk_widget_show(item); - gtk_container_add(GTK_CONTAINER(menu), item); + // Remove the duplicate declaration of clear_item + clear_item = gtk_menu_item_new(); // Create a menu item without a label + GtkWidget *box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); // Create a box to hold image and label + GtkWidget *image = gtk_image_new_from_icon_name("edit-clear", GTK_ICON_SIZE_MENU); // Create an image + GtkWidget *label = gtk_label_new("Clear Pinned Documents"); // Create a label + + // Pack the image and label into the box + gtk_box_pack_start(GTK_BOX(box), image, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 0); + + gtk_widget_show(image); + gtk_widget_show(label); + gtk_widget_show(box); + + // Add the box to the menu item + gtk_container_add(GTK_CONTAINER(clear_item), box); + + gtk_widget_show(clear_item); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), clear_item); + + // Connect the "activate" signal of the menu item to the clear_pinned_documents function + g_signal_connect_swapped(G_OBJECT(clear_item), "activate", G_CALLBACK(clear_pinned_documents), NULL); return menu; } -static bool is_duplicate(const gchar* file_name) { - // Check if the file name (document) is already a key in the hash map. - // g_hash_table_contains returns TRUE if the key is found, FALSE otherwise. - return g_hash_table_contains(doc_to_widget_map, file_name); +static gboolean is_duplicate(const gchar* file_name) +{ + return g_hash_table_contains(doc_to_widget_map, file_name); } @@ -91,7 +120,7 @@ static void pin_activate_cb(GtkMenuItem *menuitem, gpointer pdata) // gtk_notebook_set_current_page(GTK_NOTEBOOK(plugin->geany_data->main_widgets->sidebar_notebook), page_number); g_signal_connect(event_box, "button-press-event", - G_CALLBACK(label_clicked_cb), tmp_file_name); + G_CALLBACK(on_button_press_cb), tmp_file_name); return; } @@ -114,9 +143,9 @@ static void unpin_activate_cb(GtkMenuItem *menuitem, gpointer pdata) } -static gboolean label_clicked_cb(GtkWidget *widget, GdkEventButton *event, gpointer data) +static gboolean on_button_press_cb(GtkWidget *widget, GdkEventButton *event, gpointer data) { - if (event->type == GDK_BUTTON_PRESS && event->button == 1) + if (event->type == GDK_BUTTON_PRESS && event->button == GDK_BUTTON_PRIMARY) { // Check if the clicked widget is an event box if (GTK_IS_EVENT_BOX(widget)) @@ -130,31 +159,16 @@ static gboolean label_clicked_cb(GtkWidget *widget, GdkEventButton *event, gpoin } } } - + else if (event->type == GDK_BUTTON_PRESS && event->button == GDK_BUTTON_SECONDARY) + { + GtkWidget *menu = create_popup_menu(); + gtk_menu_popup_at_pointer(GTK_MENU(menu), (const GdkEvent *)event); + return TRUE; + } return FALSE; } -//static gboolean on_button_press(GtkWidget *widget, GdkEventButton *event) -//{ - ////if (event->button == 1 && event->type == GDK_2BUTTON_PRESS) - ////{ - ////on_open_clicked(NULL, NULL); - ////return TRUE; - ////} - //if (event->button == 3) - //{ - //static GtkWidget *popup_menu = NULL; - - //if (popup_menu == NULL) - //popup_menu = create_popup_menu(); - - //gtk_menu_popup_at_pointer(GTK_MENU(popup_menu), (GdkEvent *) event); - ///* don't return TRUE here, unless the selection won't be changed */ - //} - //return FALSE; -//} - static gboolean pin_init(GeanyPlugin *plugin, gpointer pdata) { GtkWidget **tools_item = g_new0(GtkWidget*, 3); // Allocate memory for 3 pointers (2 items + NULL terminator) From 295737b2721da29ee3f6586bba6a47216876a486 Mon Sep 17 00:00:00 2001 From: andy5995 Date: Fri, 16 Feb 2024 08:22:15 -0600 Subject: [PATCH 16/23] Conditionally add sanitize flag --- pinner/Makefile.am | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/pinner/Makefile.am b/pinner/Makefile.am index 4aa2f6177..fd64c364b 100644 --- a/pinner/Makefile.am +++ b/pinner/Makefile.am @@ -9,13 +9,17 @@ pinner_la_SOURCES = pinner.c pinner_la_CPPFLAGS = $(AM_CPPFLAGS) \ -DG_LOG_DOMAIN=\"Pinner\" +if MINGW + ASAN_FLAG = +else + ASAN_FLAG = -fsanitize=address,undefined +endif + pinner_la_CFLAGS = \ - $(AM_CFLAGS) \ - -fsanitize=address,undefined + $(AM_CFLAGS) $(ASAN_FLAG) pinner_la_LDFLAGS = \ - $(AM_LDFLAGS) \ - -fsanitize=address,undefined + $(AM_LDFLAGS) $(ASAN_FLAG) pinner_la_LIBADD = \ $(COMMONLIBS) From a0f4bc8e37569afea0390306f79793099fc674a4 Mon Sep 17 00:00:00 2001 From: andy5995 Date: Fri, 16 Feb 2024 08:25:03 -0600 Subject: [PATCH 17/23] Add credit for ChatGPT --- pinner/pinner.c | 1 + 1 file changed, 1 insertion(+) diff --git a/pinner/pinner.c b/pinner/pinner.c index 670947818..7ef0a1cd7 100644 --- a/pinner/pinner.c +++ b/pinner/pinner.c @@ -2,6 +2,7 @@ * pinner.c * * Copyright 2024 Andy Alt + * With assistance from ChatGPT * * 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 From 259571ebebba229bffdaea28aa09e07549cf72c5 Mon Sep 17 00:00:00 2001 From: andy5995 Date: Fri, 16 Feb 2024 08:30:22 -0600 Subject: [PATCH 18/23] remove sanitize flag --- pinner/Makefile.am | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pinner/Makefile.am b/pinner/Makefile.am index fd64c364b..d01aefdfa 100644 --- a/pinner/Makefile.am +++ b/pinner/Makefile.am @@ -9,11 +9,11 @@ pinner_la_SOURCES = pinner.c pinner_la_CPPFLAGS = $(AM_CPPFLAGS) \ -DG_LOG_DOMAIN=\"Pinner\" -if MINGW - ASAN_FLAG = -else - ASAN_FLAG = -fsanitize=address,undefined -endif +#if MINGW +# ASAN_FLAG = +#else +# ASAN_FLAG = -fsanitize=address,undefined +#endif pinner_la_CFLAGS = \ $(AM_CFLAGS) $(ASAN_FLAG) From e0d734c2d260f6d5634df335dff103be40afb2b0 Mon Sep 17 00:00:00 2001 From: andy5995 Date: Fri, 16 Feb 2024 12:36:20 -0600 Subject: [PATCH 19/23] Don't use 'with_mnemonic' (fix missing underscores) --- pinner/pinner.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pinner/pinner.c b/pinner/pinner.c index 7ef0a1cd7..c90f81f0c 100644 --- a/pinner/pinner.c +++ b/pinner/pinner.c @@ -114,7 +114,7 @@ static void pin_activate_cb(GtkMenuItem *menuitem, gpointer pdata) GtkWidget *event_box = gtk_event_box_new(); g_hash_table_insert(doc_to_widget_map, tmp_file_name, event_box); - GtkWidget *label = gtk_label_new_with_mnemonic(doc->file_name); + GtkWidget *label = gtk_label_new(doc->file_name); gtk_container_add(GTK_CONTAINER(event_box), label); gtk_widget_show_all(event_box); gtk_box_pack_start(GTK_BOX(pinned_view_vbox), event_box, FALSE, FALSE, 0); From 7aa35e595b1cb1cfe69f291e6597eccc78a23fbb Mon Sep 17 00:00:00 2001 From: andy5995 Date: Sat, 17 Feb 2024 07:16:02 -0600 Subject: [PATCH 20/23] Acommodate for long filenames by using an ellipse Also: * Set margins to 10 pixels and left-justify --- pinner/pinner.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pinner/pinner.c b/pinner/pinner.c index c90f81f0c..f97063441 100644 --- a/pinner/pinner.c +++ b/pinner/pinner.c @@ -114,7 +114,17 @@ static void pin_activate_cb(GtkMenuItem *menuitem, gpointer pdata) GtkWidget *event_box = gtk_event_box_new(); g_hash_table_insert(doc_to_widget_map, tmp_file_name, event_box); + GtkWidget *label = gtk_label_new(doc->file_name); + // Enable ellipsizing at the start of the filename + gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_START); + gtk_label_set_max_width_chars(GTK_LABEL(label), 30); + // Set the label's alignment to left + gtk_label_set_xalign(GTK_LABEL(label), 0.0); + // Set margins + gtk_widget_set_margin_start(label, 10); // 20 pixels margin on the start (left) + gtk_widget_set_margin_end(label, 10); // 20 pixels margin on the end (right) + gtk_container_add(GTK_CONTAINER(event_box), label); gtk_widget_show_all(event_box); gtk_box_pack_start(GTK_BOX(pinned_view_vbox), event_box, FALSE, FALSE, 0); From cd1f6876eb21e9943b097f58d584aa9101cacb34 Mon Sep 17 00:00:00 2001 From: andy5995 Date: Sat, 17 Feb 2024 09:19:34 -0600 Subject: [PATCH 21/23] Implment keybindings and add README.md --- pinner/README.md | 16 ++++++++++++++++ pinner/pinner.c | 17 +++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 pinner/README.md diff --git a/pinner/README.md b/pinner/README.md new file mode 100644 index 000000000..ca0b3a6e9 --- /dev/null +++ b/pinner/README.md @@ -0,0 +1,16 @@ +# Pinner + +Pinner is a [geany plugin](https://www.geany.org/support/plugins/) that +enables pinning documents to a sidebar tab. + +This will add two items to the Geany tools menu: + + Pin Document + Unpin Document + +To clear the list, Right click on it and left-click on "Clear". + +## Keybindings + +When the plugin is enabled, keybindings to pin and unpin documents can be +changed from the preferences menu. diff --git a/pinner/pinner.c b/pinner/pinner.c index f97063441..35f6fc422 100644 --- a/pinner/pinner.c +++ b/pinner/pinner.c @@ -99,6 +99,17 @@ static gboolean is_duplicate(const gchar* file_name) } +static void pin_document_key_cb(guint key_id) +{ + pin_activate_cb(NULL, NULL); +} + +static void unpin_document_key_cb(guint key_id) +{ + unpin_activate_cb(NULL, NULL); +} + + static void pin_activate_cb(GtkMenuItem *menuitem, gpointer pdata) { GeanyDocument *doc = document_get_current(); @@ -212,6 +223,12 @@ static gboolean pin_init(GeanyPlugin *plugin, gpointer pdata) gtk_widget_show_all(pinned_view_vbox); page_number = gtk_notebook_append_page(GTK_NOTEBOOK(plugin->geany_data->main_widgets->sidebar_notebook), pinned_view_vbox, gtk_label_new(_("Pinned"))); + + // Keybinding setup + GeanyKeyGroup *key_group = plugin_set_key_group(plugin, "pinner_keys", 2, NULL); + keybindings_set_item(key_group, DO_PIN, pin_document_key_cb, 0, 0, "pin_document", "Pin Document", NULL); + keybindings_set_item(key_group, DO_UNPIN, unpin_document_key_cb, 0, 0, "unpin_document", "Unpin Document", NULL); + return TRUE; } From b3c3b671e74e406d95374ea0fbdd301430b1783c Mon Sep 17 00:00:00 2001 From: andy5995 Date: Sat, 17 Feb 2024 09:35:57 -0600 Subject: [PATCH 22/23] Add "unpin document" to right-click menu --- pinner/pinner.c | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/pinner/pinner.c b/pinner/pinner.c index 35f6fc422..d3695d526 100644 --- a/pinner/pinner.c +++ b/pinner/pinner.c @@ -31,7 +31,7 @@ enum { static void destroy_widget(gpointer pdata); static void clear_pinned_documents(void); -static GtkWidget *create_popup_menu(void); +static GtkWidget *create_popup_menu(const gchar *file_name); static gboolean on_button_press_cb(GtkWidget *widget, GdkEventButton *event, gpointer data); static gboolean is_duplicate(const gchar* file_name); static void pin_activate_cb(GtkMenuItem *menuitem, gpointer pdata); @@ -60,7 +60,7 @@ void clear_pinned_documents(void) } -static GtkWidget *create_popup_menu(void) { +static GtkWidget *create_popup_menu(const gchar *file_name) { GtkWidget *menu; GtkWidget *clear_item; @@ -86,7 +86,13 @@ static GtkWidget *create_popup_menu(void) { gtk_widget_show(clear_item); gtk_menu_shell_append(GTK_MENU_SHELL(menu), clear_item); - // Connect the "activate" signal of the menu item to the clear_pinned_documents function + if (file_name != NULL) + { + GtkWidget *unpin_item = gtk_menu_item_new_with_label("Unpin Document"); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), unpin_item); + // Pass the file_name as user data to the callback function + g_signal_connect_data(G_OBJECT(unpin_item), "activate", G_CALLBACK(unpin_activate_cb), g_strdup(file_name), (GClosureNotify)g_free, 0); + } g_signal_connect_swapped(G_OBJECT(clear_item), "activate", G_CALLBACK(clear_pinned_documents), NULL); return menu; @@ -183,9 +189,25 @@ static gboolean on_button_press_cb(GtkWidget *widget, GdkEventButton *event, gpo } else if (event->type == GDK_BUTTON_PRESS && event->button == GDK_BUTTON_SECONDARY) { - GtkWidget *menu = create_popup_menu(); - gtk_menu_popup_at_pointer(GTK_MENU(menu), (const GdkEvent *)event); - return TRUE; + // Check if the clicked widget is an event box + if (GTK_IS_EVENT_BOX(widget)) + { + GtkWidget *label = gtk_bin_get_child(GTK_BIN(widget)); + + if (GTK_IS_LABEL(label)) + { + const gchar *file_name = gtk_label_get_text(GTK_LABEL(label)); + GtkWidget *menu = create_popup_menu(file_name); + gtk_menu_popup_at_pointer(GTK_MENU(menu), (const GdkEvent *)event); + return TRUE; + } + } + else + { + GtkWidget *menu = create_popup_menu(NULL); + gtk_menu_popup_at_pointer(GTK_MENU(menu), (const GdkEvent *)event); + return TRUE; + } } return FALSE; } From b58b98b4c8948e2d9d7081ddbcdfc0cae6a7a5b1 Mon Sep 17 00:00:00 2001 From: andy5995 Date: Sat, 17 Feb 2024 11:27:49 -0600 Subject: [PATCH 23/23] Add "Unpin" item to right-click popup menu --- pinner/pinner.c | 84 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 55 insertions(+), 29 deletions(-) diff --git a/pinner/pinner.c b/pinner/pinner.c index d3695d526..d155d3afd 100644 --- a/pinner/pinner.c +++ b/pinner/pinner.c @@ -61,39 +61,56 @@ void clear_pinned_documents(void) static GtkWidget *create_popup_menu(const gchar *file_name) { - GtkWidget *menu; - GtkWidget *clear_item; + GtkWidget *menu = gtk_menu_new(); - menu = gtk_menu_new(); + // Create a menu item without a label + GtkWidget *clear_item = gtk_menu_item_new(); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), clear_item); + g_signal_connect_swapped(clear_item, "activate", G_CALLBACK(clear_pinned_documents), NULL); + + // Create a box to contain the icon and label + GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); // 6 pixels spacing - // Remove the duplicate declaration of clear_item - clear_item = gtk_menu_item_new(); // Create a menu item without a label - GtkWidget *box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); // Create a box to hold image and label - GtkWidget *image = gtk_image_new_from_icon_name("edit-clear", GTK_ICON_SIZE_MENU); // Create an image - GtkWidget *label = gtk_label_new("Clear Pinned Documents"); // Create a label + // Create the icon + GtkWidget *clear_icon = gtk_image_new_from_icon_name("edit-clear", GTK_ICON_SIZE_MENU); - // Pack the image and label into the box - gtk_box_pack_start(GTK_BOX(box), image, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 0); + // Create the label + GtkWidget *label = gtk_label_new("Clear List"); - gtk_widget_show(image); - gtk_widget_show(label); - gtk_widget_show(box); + // Pack the icon and label into the box + gtk_box_pack_start(GTK_BOX(hbox), clear_icon, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); // Add the box to the menu item - gtk_container_add(GTK_CONTAINER(clear_item), box); + gtk_container_add(GTK_CONTAINER(clear_item), hbox); - gtk_widget_show(clear_item); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), clear_item); + // Show all widgets + gtk_widget_show_all(clear_item); if (file_name != NULL) - { - GtkWidget *unpin_item = gtk_menu_item_new_with_label("Unpin Document"); + { + // Create a menu item without a label for unpinning a document + GtkWidget *unpin_item = gtk_menu_item_new(); gtk_menu_shell_append(GTK_MENU_SHELL(menu), unpin_item); - // Pass the file_name as user data to the callback function - g_signal_connect_data(G_OBJECT(unpin_item), "activate", G_CALLBACK(unpin_activate_cb), g_strdup(file_name), (GClosureNotify)g_free, 0); + g_signal_connect_data(unpin_item, "activate", G_CALLBACK(unpin_activate_cb), g_strdup(file_name), (GClosureNotify)g_free, 0); + + // Create a horizontal box to hold the icon and label + GtkWidget *hbox_unpin = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); // 6 pixels spacing + + // Create the icon + GtkWidget *unpin_icon = gtk_image_new_from_icon_name("list-remove", GTK_ICON_SIZE_MENU); + + // Create the label + GtkWidget *label_unpin = gtk_label_new("Unpin Document"); + + // Pack the icon and label into the horizontal box + gtk_box_pack_start(GTK_BOX(hbox_unpin), unpin_icon, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox_unpin), label_unpin, FALSE, FALSE, 0); + gtk_container_add(GTK_CONTAINER(unpin_item), hbox_unpin); + + gtk_widget_show_all(unpin_item); } - g_signal_connect_swapped(G_OBJECT(clear_item), "activate", G_CALLBACK(clear_pinned_documents), NULL); + gtk_widget_show_all(menu); return menu; } @@ -118,21 +135,30 @@ static void unpin_document_key_cb(guint key_id) static void pin_activate_cb(GtkMenuItem *menuitem, gpointer pdata) { - GeanyDocument *doc = document_get_current(); - if (doc == NULL) - return; + gchar *ptr_file_name = NULL; + if (pdata == NULL) + { + GeanyDocument *doc = document_get_current(); + if (doc == NULL) + return; + else + ptr_file_name = doc->file_name; + } + else + { + ptr_file_name = pdata; + } - if (is_duplicate(doc->file_name)) + if (is_duplicate(ptr_file_name)) return; /* This must be freed when nodes are removed from the list */ - gchar *tmp_file_name = g_strdup(doc->file_name); - // pin_list = g_slist_append(pin_list, tmp_file_name); + gchar *tmp_file_name = g_strdup(ptr_file_name); GtkWidget *event_box = gtk_event_box_new(); g_hash_table_insert(doc_to_widget_map, tmp_file_name, event_box); - GtkWidget *label = gtk_label_new(doc->file_name); + GtkWidget *label = gtk_label_new(ptr_file_name); // Enable ellipsizing at the start of the filename gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_START); gtk_label_set_max_width_chars(GTK_LABEL(label), 30);