From what I can tell, as I'm just learning the Geany API and gtk3, I would now need to
1. Create a linked list using GSList() to store each item that's pinned 2. Before pinning, check to see if document is already in the list 3. Other stuff after that.
I'm a bit lost on how to pass some values as functions though. I'm not too experienced with callback functions and some of my experiments have gone terribly awry.
I'll ask in a line comment... You can view, comment on, or merge this pull request online at:
https://github.com/geany/geany-plugins/pull/1308
-- Commit Summary --
* Add Pinner plugin
-- File Changes --
A pinner/pinner.c (88)
-- Patch Links --
https://github.com/geany/geany-plugins/pull/1308.patch https://github.com/geany/geany-plugins/pull/1308.diff
@andy5995 commented on this pull request.
- GeanyDocument *doc = document_get_current();
+ + if (doc != NULL && pin_list == NULL) + { + 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; + GSList *pin_list = NULL;
How do I pass 'pin_list' to pin_activate_cb()? It seems like I can't just add a 3rd argument, or it's getting ignored. :(
@andy5995 pushed 1 commit.
8431a100f118aacf438af159a4b822469534077f Add Pinner plugin
Uhhh, that sounds interesting and like something I want to implement since some years already :).
@eht16 commented on this pull request.
- GeanyDocument *doc = document_get_current();
+ + if (doc != NULL && pin_list == NULL) + { + 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; + GSList *pin_list = NULL;
Yeah, a third argument doesn't work :(.
The common way is to define a struct to hold the data you want to pass, in this case the plugin and the pin_list pointers and then allocate, fill and pass the struct from `pin_init` to `pin_activate_cb` as the `data` argument.
Care should be paid to the allocation of the struct, it might get tricky to deallocate it in the handler function. I don't remember if GLib offers some utility function for this. ALternatively, the struct could be created once per plugin and the carried around and reused.
To get a rough idea, see https://github.com/geany/geany-plugins/blob/8431a100f118aacf438af159a4b82246....
I didn't test it but maybe you could also use https://www.geany.org/manual/reference/plugindata_8h.html#a143ba8805bd049f3f... to add the `pin_list` to the `plugin` instance. I'm not sure if this is a good idea, it might get clumsy.
Uhhh, that sounds interesting and like something I want to implement since some years already :).
I just found some old code and it seems I started with a similar approach some time ago: https://gist.github.com/eht16/15afd001f238f77f100a43e4129b8d20 Use what is helpful but with care :smile:. I don't remember much its state except that it was working to some extend but quite buggy and probably also crashed happily.
@andy5995 pushed 1 commit.
565320a5358b49939288035ad6566f05683f85e9 Add Pinner plugin
@andy5995 commented on this pull request.
- GeanyDocument *doc = document_get_current();
+ + if (doc != NULL && pin_list == NULL) + { + 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; + GSList *pin_list = NULL;
Thank you! I'm not good at reading code, but I got the general idea of what needed to be done.
What I have so far works as expected.
![image](https://github.com/geany/geany-plugins/assets/16764864/08fb092b-8375-4231-ba...)
@andy5995 commented on this pull request.
@@ -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
I enabled the [sanitize option](https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html), and use `LD_PRELOAD=/usr/lib64/libasan.so ./geany -c ~/src/Geany/devconf` to test the plugin. All looks good now that I moved the initialization of the 'container' to a separate function...
@andy5995 pushed 1 commit.
c8dbb27032f54b13503594bbc4da240eca827e7c Check for duplicates
@andy5995 pushed 4 commits.
50209670e24d25b8188f4d2aad5336749d72a7d0 Add Pinner plugin 5def3d767465a138aee70a8ce81787b8e83da130 Check for duplicates 3a293072756b34cee614c80ac329fd1e0bf2f3df check if doc eq NULL 4bb86a95810c5da704794d924e11cfae11a44fef use g_strdup instead of strlcpy, point better
@andy5995 commented on this pull request.
+ * + */ + +#include <geanyplugin.h> +#include <stdbool.h> + +static GtkWidget *pinned_view_vbox; +static gint page_number = 0; + +struct pindata { + GeanyPlugin *plugin; + GSList *list; +}; + +static struct pindata *init_pindata(void)
Ok to use a function like this?
Duplicate checking works now. Any feedback on how I'm implementing this so far?
One thing I'm not sure of yet is how to pass the the list to the cleanup function so I can free it.
@andy5995 pushed 1 commit.
18b58b305ff01e989ca5322dbe12323fc44a7289 Add non-working popup menu and func to clear list
@andy5995 commented on this pull request.
+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_signal_connect(pinned_view_vbox, "button-press-event",
No popup menu appears and I see in the console:
``` (geany:1064826): GLib-GObject-CRITICAL **: 10:20:24.486: invalid (NULL) pointer instance
(geany:1064826): GLib-GObject-CRITICAL **: 10:20:24.486: g_signal_connect_data: assertion 'G_TYPE_CHECK_INSTANCE (instance)' failed ```
I think using 'pinned_view_vbox' as the 2nd argument here is not correct.
I get no compiler warnings.
@andy5995 pushed 1 commit.
73322e4a185e3e50bbd546e1bccbabc5080824e9 add header to Makefile.am
@andy5995 pushed 1 commit.
c1899e4369a9f5aea2266439e4355231640957b8 Make list global
One thing I'm not sure of yet is how to pass the the list to the cleanup function so I can free it.
I made the list global.
@andy5995 pushed 1 commit.
afeeb5952c1f9a56aa15117c723ecebfe2c20ba1 Remove header, make all functions static
@andy5995 pushed 1 commit.
9ff65479c9e788045fdf4dc7fde471be404253a3 Add UNpin function
I just found some old code and it seems I started with a similar approach some time ago: https://gist.github.com/eht16/15afd001f238f77f100a43e4129b8d20 Use what is helpful but with care 😄. I don't remember much its state except that it was working to some extend but quite buggy and probably also crashed happily.
@eht16 It looks like we were working on something different but similar? ;) I think incorporating something like that into this plugin would be suitable.
@andy5995 pushed 2 commits.
093184d081fdce62111d39cddc69fda07eb74088 Add UNpin function 6a96d0c7f3932fd68bf281ff8b895126e66df719 Make documents clickable and open them
@andy5995 pushed 1 commit.
b8d66c4101dfb12d366beb30b4d1ebb2b551d0a5 Don't auto-switch to the "Pinned" tab
At this point, clicking on a document in the "Pinned" tab, will open (switch to) it.
@andy5995 pushed 3 commits.
a1fe9573bb09b841a96e01354b65a71a10c31a20 fix double-free error when freeing list cf40001168fa9e3b9bae629edf72d6af9af04027 fix 'stack-use-after-return` in pin_cleanup 5391654fb522550bfab02bbebcfb7c6b3b01254f implement unpin, use hashtable instead of GSList
@andy5995 pushed 2 commits.
87b8b4e27b856d27296727c84bacb62c0c1ebf5b Add right-click menu/option to clear pinned list 295737b2721da29ee3f6586bba6a47216876a486 Conditionally add sanitize flag
@andy5995 pushed 1 commit.
0c0a34fe8e14049b0210cf95a4bf7460dcfd610e Add credit for ChatGPT [skip ci]
@andy5995 commented on this pull request.
+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_signal_connect(pinned_view_vbox, "button-press-event",
Fixed.
@andy5995 pushed 1 commit.
259571ebebba229bffdaea28aa09e07549cf72c5 remove sanitize flag
Ready for review and testing
Do you have a final screenshot?
![image](https://github.com/geany/geany-plugins/assets/16764864/5e46462a-ff14-4d2c-9d...)
![image](https://github.com/geany/geany-plugins/assets/16764864/8c8cce8a-6a4b-464a-89...)
![image](https://github.com/geany/geany-plugins/assets/16764864/54c46d41-29de-40e4-9e...)
@andy5995 pushed 1 commit.
e0d734c2d260f6d5634df335dff103be40afb2b0 Don't use 'with_mnemonic' (fix missing underscores)
@andy5995 pushed 1 commit.
7aa35e595b1cb1cfe69f291e6597eccc78a23fbb Acommodate for long filenames by using an ellipse
Longer filenames are now handled better, and they're left-justified with a 10 pixel margin on both sides.
![image](https://github.com/geany/geany-plugins/assets/16764864/636cab6c-05d5-499b-91...)
@luigifab
@andy5995 pushed 1 commit.
cd1f6876eb21e9943b097f58d584aa9101cacb34 Implment keybindings and add README.md
@andy5995 pushed 2 commits.
b3c3b671e74e406d95374ea0fbdd301430b1783c Add "unpin document" to right-click menu b58b98b4c8948e2d9d7081ddbcdfc0cae6a7a5b1 Add "Unpin" item to right-click popup menu
"Unpin Document" available by right-clicking
![image](https://github.com/geany/geany-plugins/assets/16764864/3172aa37-1828-4bfb-af...)
Closed #1308.
I moved this to https://github.com/andy5995/pinner
@eht16 It looks like we were working on something different but similar? ;) I think incorporating something like that into this plugin would be suitable.
Yes, my approach was for a different use case. I like to have a tab/document always in the visible area of the notebook tab list for easy access. The name of your plugin sounded like this to me in the beginning :).
Couple of comments:
1. write the documentation explaining what this plugin is supposed to do, sounds like it does something different to what @eht16 understood "pin" to mean, and I have always been confused about what it was meant to do. (and do not describe it in terms of some other software, eg do not say "like firefox/eclipse/vscode/libreoffice" ;-) [end getting in ahead] That will mean that you can get relevant guidance.
2. relating to https://github.com/geany/geany/pull/3770 and depending on the answer to the above, you should be working with notebook pages, not documents. I'm pretty sure that plugins can open pages that do not have documents, let alone files.
3. And a hint always check `document_valid()` on a document pointer in any callback if you pass it via the data pointer, the user can always close pages and documents or reorder pages manually.
write the documentation explaining what this plugin is supposed to do, sounds like it does something different to what @eht16 understood "pin" to mean, and I have always been confused about what it was meant to do.
I'm happy to elaborate an idea or re-frame a goal if I haven't made myself clear to someone. In this case, I didn't know I wasn't being understood. Please let me know next time if you are confused by my description or feel I need to expand on it more.
and do not describe it in terms of some other software, eg do not say "like firefox/eclipse/vscode/libreoffice" ;-)
I feel that's an unnecessarily rigid policy and I don't really plan to stop using them (when I believe it's appropriate) based on your demand. I believe that comparisons can be a useful tool when trying to describe something; but I acknowledge that sometimes comparisons aren't always adequate and shouldn't be solely relied upon to express a desired feature.
[end getting in ahead]
I don't know what that means.
relating to [Return NULL from document_get_current() if document is 'untitled' geany#3770](https://github.com/geany/geany/pull/3770) and depending on the answer to the above, you should be working with notebook pages, not documents. I'm pretty sure that plugins can open pages that do not have documents, let alone files.
I don't know what you mean. I work with documents when I use Geany. The plugin apparently uses a sidebar notebook (`plugin->geany_data->main_widgets->sidebar_notebook`). I use the plugin to open or switch to documents. Maybe our miscommunication stems from my lack of experience developing Geany or GTK development, but feel free to elaborate.
And a hint always check `document_valid()` on a document pointer in any callback if you pass it via the data pointer, the user can always close pages and documents or reorder pages manually.
I searched for `document_valid` in the geany-plugins source code and no results came up. When I searched for `document_get_current`, many results came up. I'm confused as to why you would suggest I use a function that no other plugin developer uses... and apparently isn't even a function included in [the API](https://www.geany.org/manual/reference/globals_func_d.html#index_d).
I'm not accessing any GeanyDocument pointers passed through a callback. In one case, I pass the file_name member as a gchar, after it's been validated by another function.
Due to [#3770](https://github.com/geany/geany/pull/3770) I use this to validate the file_name after I enter a callback function:
``` GeanyDocument* doc = document_get_current(); if (!(doc && doc->is_valid)) return;
if (doc->file_name == NULL) return; ```
I forgot to mention, based on your feedback @elextr , I expanded the [README.md](https://github.com/andy5995/pinner/blob/trunk/README.md) with hopefully a better explanation of the goal of the plugin, and added a link to a demo video.
but I acknowledge that sometimes comparisons aren't always adequate and shouldn't be solely relied upon to express a desired feature.
Yes, that was what I was asking, describing a feature as "I want it to work like Vscode" is unhelpful if the reader doesn't use Vscode or that feature of Vscode, whereas "I want it to do xyz when I click blah because I have found it useful in Vscode" is of course fine.
[end getting in ahead]
I was simply saying that I was making the above point as guidance ahead of you doing anything, not that it was a criticism of anything you had done in the past.
I searched for document_valid
Sorry I meant [`DOC_VALID`](https://www.geany.org/manual/reference/document_8h.html#a064c8f47ec543aa26cb...) which does exactly your suggested code but allows for Geany to add conditions without your code needing to be changed. Thats not to say all plugins use it properly (or even Geany itself).
The important thing was to beware that state can change between callbacks to your plugin, I saw a note that you made a list (of documents I think) global, thats fine, but if you are saving information between callbacks you have to check that the information in that list is valid every time your plugin is entered because any arbitrary changes could be made (documents opening/closing) between callbacks.
Maybe our miscommunication stems from my lack of experience developing Geany or GTK development, but feel free to elaborate.
Or the problem of the description of the plugin. I had in mind "pin" as being locking the document tab to the left side of the notebook tabs, a feature available in several other applications. Your `README.md` definition of "pin" as adding to a sidebar list clears that up (and is probably simpler to implement).
Yes, that was what I was asking, describing a feature as "I want it to work like Vscode" is unhelpful if the reader doesn't use Vscode or that feature of Vscode, whereas "I want it to do xyz when I click blah because I have found it useful in Vscode" is of course fine.
Sounds like we're in agreement in that case. ;)
[end getting in ahead]
I was simply saying that I was making the above point as guidance ahead of you doing anything, not that it was a criticism of anything you had done in the past.
I searched for document_valid
Sorry I meant [`DOC_VALID`](https://www.geany.org/manual/reference/document_8h.html#a064c8f47ec543aa26cb...) which does exactly your suggested code but allows for Geany to add conditions without your code needing to be changed. Thats not to say all plugins use it properly (or even Geany itself).
I'll use that macro, thanks for pointing it out, but as I mentioned in https://github.com/geany/geany/pull/3770 , if doc->is_valid is true, doc->file_name may not necessarily be valid, it may be NULL. This happens when the 'document' in front of the user is an 'untitled' document. Although there's no point in trying to "pin" an untitled document, it's better to prevent a segfault if a user accidentally tries to pin it (like I did).
The important thing was to beware that state can change between callbacks to your plugin, I saw a note that you made a list (of documents I think) global, thats fine, but if you are saving information between callbacks you have to check that the information in that list is valid every time your plugin is entered because any arbitrary changes could be made (documents opening/closing) between callbacks.
Ok. I know I antipated that if a user closes a doc on the pin list and clicks the filename from the pin list later, the doc will be re-opened. Which isn't so terrible but it sounds like at some point I should implement checking the list as you said.
Maybe our miscommunication stems from my lack of experience developing Geany or GTK development, but feel free to elaborate.
Or the problem of the description of the plugin. I had in mind "pin" as being locking the document tab to the left side of the notebook tabs, a feature available in several other applications. Your `README.md` definition of "pin" as adding to a sidebar list clears that up (and is probably simpler to implement).
If you haven't already looked at the video, or had time to glance at my code, it is implemented. ;)
github-comments@lists.geany.org