1
/* Bluefish HTML Editor
2
* snippetsmenu.c - menubar that represents the treemodel as menu
4
* Copyright (C) 2009 Olivier Sessink
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22
#include "../bluefish.h"
23
#include "snippetsmenu.h"
26
static GtkMenuItem *menushell_nth_child(GtkMenuShell *menushell, guint n) {
27
GtkMenuItem *menuitem;
28
GList *list = gtk_container_get_children(GTK_CONTAINER(menushell));
29
menuitem = g_list_nth_data(list, n);
31
/*g_print("menushell_nth_child, return child %p for n=%d\n",menuitem,n);*/
35
/* this is an ugly hack to expose the treepath internals to this widget */
41
#define TREEPATH(var) ((TreePath *)(var))
43
static GtkMenuItem *menuitem_from_path(SnippetsMenu *sm, GtkTreePath *path) {
45
GtkMenuItem *mitem=NULL;
46
GtkMenuShell *mshell = (GtkMenuShell *)sm;
48
DEBUG_MSG("menuitem_from_path, path=NULL, return NULL\n");
51
/*DEBUG_MSG("menuitem_from_path, depth=%d\n",TREEPATH(path)->depth);*/
52
for (i=0;mshell && i<TREEPATH(path)->depth;i++) {
53
/* all menu's have a tearoff entry as first entry, except the main menu */
54
mitem = menushell_nth_child(mshell, i==0?TREEPATH(path)->indices[i]:TREEPATH(path)->indices[i]+1);
56
mshell = (GtkMenuShell *)gtk_menu_item_get_submenu(mitem);
60
DEBUG_MSG("indices[%d]=%d mitem=%p, mshell=%p\n",i,TREEPATH(path)->indices[i],mitem, mshell);
61
/*g_print("return mitem=%p with label %s\n", mitem, gtk_label_get_text(GTK_LABEL(GTK_BIN(mitem)->child)));*/
65
static void snippets_menu_row_inserted(GtkTreeModel * tree_model,
66
GtkTreePath * path, GtkTreeIter * iter, gpointer user_data)
68
SnippetsMenu *sm = user_data;
69
GtkMenuItem *item, *newitem;
71
DEBUG_MSG("row inserted, path=%s\n", gtk_tree_path_to_string(path));
72
parent = gtk_tree_path_copy(path);
73
if (!gtk_tree_path_up(parent) || gtk_tree_path_get_depth(parent)==0) {
75
/* main menu entry ! */
76
gtk_widget_size_request((GtkWidget *)sm,&req);
77
DEBUG_MSG("have %d pixels in use, %d available\n",req.width, sm->maxwidth);
78
if (req.width < (sm->maxwidth-100)) { /* reserve at least 100 pixels for any new entry */
79
newitem = (GtkMenuItem *) gtk_menu_item_new_with_label("");
80
gtk_menu_shell_insert((GtkMenuShell *)sm, (GtkWidget *)newitem, TREEPATH(path)->indices[0]);
81
gtk_widget_show((GtkWidget *)newitem);
85
item = menuitem_from_path(sm, parent);
87
mshell = (GtkMenuShell *)gtk_menu_item_get_submenu(item);
88
DEBUG_MSG("row inserted, item=%p, mshell=%p\n",item, mshell);
91
mshell = (GtkMenuShell *)gtk_menu_new();
92
DEBUG_MSG("append mshell %p to item %p\n",mshell, item);
93
gtk_menu_item_set_submenu(item, (GtkWidget *)mshell);
94
tearoff = (GtkMenuItem *) gtk_tearoff_menu_item_new();
95
gtk_menu_shell_insert((GtkMenuShell *)mshell, (GtkWidget *)tearoff, 0);
96
gtk_widget_show((GtkWidget *)tearoff);
98
DEBUG_MSG("row inserted, insert in mshell=%p at position %d\n",mshell, TREEPATH(path)->indices[TREEPATH(path)->depth-1]+1);
99
newitem = (GtkMenuItem *) gtk_menu_item_new_with_label("");
100
/* add 1 to the index number for the tearoff item */
101
gtk_menu_shell_insert((GtkMenuShell *)mshell, (GtkWidget *)newitem, TREEPATH(path)->indices[TREEPATH(path)->depth-1]+1);
102
gtk_widget_show((GtkWidget *)newitem);
105
gtk_tree_path_free(parent);
108
/*static void snippets_menu_rows_reordered(GtkTreeModel * tree_model,
110
GtkTreeIter * iter, gint *neworder, gpointer user_data)
112
/ * an array of integers mapping the current position of each child to its
113
old position before the re-ordering, i.e. neworder[newpos] = oldpos. * /
115
DEBUG_MSG("TODO rows reordered\n");
118
static void snippets_menu_row_has_child_toggled(GtkTreeModel * tree_model,
119
GtkTreePath * path, GtkTreeIter * iter, gpointer user_data)
121
DEBUG_MSG("todo? or nothing todo? row has child toggled\n");
124
static void snippets_menu_row_deleted(GtkTreeModel * tree_model, GtkTreePath * path, gpointer user_data)
126
SnippetsMenu *sm = user_data;
128
DEBUG_MSG("row deleted\n");
129
mitem = menuitem_from_path(sm, path);
131
gtk_widget_destroy((GtkWidget *)mitem);
133
DEBUG_MSG("row deleted, no mitem for path %s\n",gtk_tree_path_to_string(path));
142
static void menuitem_activate(GtkMenuItem *mitem, gpointer user_data) {
143
Tsmdata *smdata=user_data;
144
smdata->sm->callback(smdata->sm->user_data, smdata->pointer);
147
static void smdata_free(gpointer smdata) {
148
g_slice_free(Tsmdata, smdata);
151
static void snippets_menu_row_changed(GtkTreeModel * tree_model,
152
GtkTreePath * path, GtkTreeIter * iter, gpointer user_data)
154
SnippetsMenu *sm = user_data;
156
mitem = menuitem_from_path(sm, path);
161
gtk_tree_model_get(tree_model, iter, sm->name_column, &name, sm->data_column, &pointer, -1);
162
if (GTK_BIN(mitem)->child) {
163
g_signal_handlers_disconnect_matched(mitem, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, menuitem_activate, NULL);
164
gtk_label_set_text(GTK_LABEL(GTK_BIN(mitem)->child),name);
166
smdata = g_object_get_data(G_OBJECT(mitem),"smdata");
168
smdata = g_slice_new(Tsmdata);
170
g_object_weak_ref(G_OBJECT(mitem),(GWeakNotify) smdata_free,smdata);
171
g_object_set_data(G_OBJECT(mitem),"smdata",smdata);
173
smdata->pointer = pointer;
174
g_signal_connect(mitem, "activate", G_CALLBACK(menuitem_activate), smdata);
176
DEBUG_MSG("row changed, no mitem for path %s\n",gtk_tree_path_to_string(path));
180
gboolean snippets_menu_set_model_foreach(GtkTreeModel *model,GtkTreePath *path,GtkTreeIter *iter,gpointer data) {
181
snippets_menu_row_inserted(model,path,iter, data);
182
snippets_menu_row_changed(model,path, iter, data);
185
void snippets_menu_set_model(SnippetsMenu * sm, GtkTreeModel * model, SnippetMenuCallback callback, gpointer user_data, gint name_column, gint data_column)
187
sm->name_column = name_column;
188
sm->data_column = data_column;
189
sm->callback = callback;
190
sm->user_data = user_data;
191
g_signal_connect(model, "row-changed", G_CALLBACK(snippets_menu_row_changed), sm);
192
g_signal_connect(model, "row-deleted", G_CALLBACK(snippets_menu_row_deleted), sm);
193
/*g_signal_connect(model, "row-has-child-toggled", G_CALLBACK(snippets_menu_row_has_child_toggled), sm);
194
g_signal_connect(model, "rows-reordered", G_CALLBACK(snippets_menu_rows_reordered), sm);*/
195
g_signal_connect(model, "row-inserted", G_CALLBACK(snippets_menu_row_inserted), sm);
197
gtk_tree_model_foreach(model, snippets_menu_set_model_foreach, sm);
201
/************ widget stuff *****************/
203
G_DEFINE_TYPE(SnippetsMenu, snippets_menu, GTK_TYPE_MENU_BAR)
205
static void snippets_menu_finalize(GObject * object)
207
/*g_print("finalize\n");*/
210
static void snippets_menu_class_init(SnippetsMenuClass * klass)
212
GObjectClass *object_class = G_OBJECT_CLASS(klass);
213
/*GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);*/
214
object_class->finalize = snippets_menu_finalize;
217
static void snippets_menu_init(SnippetsMenu * sm)
222
GtkWidget *snippets_menu_new(gint maxwidth)
224
SnippetsMenu *sm = (SnippetsMenu *) g_object_new(SNIPPETS_TYPE_MENU, NULL);
225
g_return_val_if_fail(sm != NULL, NULL);
226
sm->maxwidth = maxwidth;
227
return GTK_WIDGET(sm);