How to create gtk menu item with shortcut displayed? - c

I am trying to integrate appindicator with gtk menu.
I want some of menu items have shortcuts like here:
Unfortunately after adding accelerator to second menu item I still get item with no icon and no shortcut.
What I expect to see is a shortcut to the right of "item2".
int main (int argc, char **argv) {
gtk_init(&argc, &argv);
// main window
GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "menu-with-shortcuts-window");
gtk_window_set_icon_name(GTK_WINDOW(window), "menu-with-shortcuts-icon");
g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all(window);
// indicator
AppIndicator* indicator = app_indicator_new("menu-with-shortcuts-appindicator", "transmission-tray-icon", APP_INDICATOR_CATEGORY_APPLICATION_STATUS);
// menu
GtkWidget* indicator_menu = gtk_menu_new();
GtkWidget* item1 = gtk_menu_item_new_with_label("item1");
gtk_menu_shell_append(GTK_MENU_SHELL(indicator_menu), item1);
g_signal_connect(item1, "activate", G_CALLBACK(Item1Clicked), NULL);
gtk_widget_show(item1);
GtkAccelGroup* accel_group = gtk_accel_group_new();
gtk_window_add_accel_group(GTK_WINDOW(window), accel_group);
GtkWidget* item2 = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, accel_group);
gtk_widget_add_accelerator(item2, "activate", accel_group, GDK_q, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
gtk_menu_shell_append(GTK_MENU_SHELL(indicator_menu), item2);
g_signal_connect(item2, "activate", G_CALLBACK(Item2Clicked), NULL);
gtk_widget_show(item2);
app_indicator_set_status(indicator, APP_INDICATOR_STATUS_ACTIVE);
app_indicator_set_attention_icon(indicator, "menu-with-shortcuts-appindicator");
app_indicator_set_menu(indicator, GTK_MENU(indicator_menu));
gtk_widget_show_all(window);
gtk_main();
return 0;
}

Related

GTK: hide and show a widget in a GtkBox show's the widget underneath the GtkBox

edit: The problem described here seems to be theme related. I'm using Greybird from XUbuntu here.
The program
In my program you can click a button to create more buttons in a vertical GtkBox. If the buttons exceed the visible space a GtkScrollbar will be shown:
Problem
When the GtkScrollbar is shown for the first time, it dos not show on the side, as it should be; It's shown below the buttons:
This is weird, because the scrollbar is actually inside a horizontal GtkBox in a cell next to those buttons.
In this state, the buttons are not clickable anymore until either of of the following happens:
click the scroll bar
scroll via mouse wheel
click the window border
focus another application
UI Architecture
This is the architecture of my UI:
Whether the scrollbar is shown or not, is decided when the "page-size" property the GtkAdjustment is changed.
Code
#include <gtk/gtk.h>
gboolean
adj_page_sizechanged (GtkAdjustment *adjustment,
GdkEvent *unused,
GtkScrollbar *scrollbar)
{
gdouble maxValue = gtk_adjustment_get_upper (adjustment);
gdouble maxSize = gtk_adjustment_get_page_size (adjustment);
gboolean show = (maxValue - maxSize >= 0.000001);
g_object_set (scrollbar, "visible", show, NULL);
return FALSE;
}
/**
* Creates a `automatic` scollbar for `widget`, that will never
* overlay (hide) the content of `widget`.
**/
GtkWidget *
gtkx_scrollable_widget_vertical_new (GtkWidget *widget,
GtkAdjustment *adjustment_nullable)
{
GtkWidget *root = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
// setup adjustment
GtkAdjustment * adj_v = adjustment_nullable;
if (NULL == adj_v)
adj_v = gtk_adjustment_new (0,0,100,1,10,10);
// setup scrollbar
{
GtkWidget * scrollbar = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, adj_v);
g_signal_connect(adj_v, "notify::page-size", G_CALLBACK (adj_page_sizechanged), scrollbar);
gtk_box_pack_end (GTK_BOX (root), scrollbar, FALSE, FALSE, 0);
}
// setup scrollable area
{
GtkWidget * scrolled_window = gtk_scrolled_window_new (NULL, adj_v);
gtk_container_add (GTK_CONTAINER (scrolled_window), widget);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
GTK_POLICY_NEVER, GTK_POLICY_EXTERNAL);
gtk_box_pack_start (GTK_BOX (root), scrolled_window, TRUE, TRUE, 0);
}
return root;
}
void
add_button (GtkButton *button,
GtkBox *box)
{
g_print("add button\n");
gtk_container_add (GTK_CONTAINER (box), gtk_button_new_with_label ("b 2"));
gtk_widget_show_all (GTK_WIDGET (box));
}
GtkWidget *
create_ui()
{
GtkWidget * action_button = NULL;
GtkWidget * box_outer = NULL;
GtkWidget * box_inner = NULL;
box_inner = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5);
action_button = gtk_button_new_with_label ("click me");
gtk_container_add (GTK_CONTAINER (box_inner), action_button);
g_signal_connect (action_button, "clicked", G_CALLBACK (add_button), box_inner);
box_outer = gtkx_scrollable_widget_vertical_new (box_inner, NULL);
return box_outer;
}
/**
* standard stuff. See `create_ui()`
**/
int main(int argc, char *argv[])
{
gtk_init (&argc, &argv);
GtkWidget *window;
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_default_size (GTK_WINDOW (window), -1, 60);
GtkWidget * ui = create_ui ();
gtk_container_add (GTK_CONTAINER (window), ui);
g_signal_connect (G_OBJECT (window), "destroy",
G_CALLBACK (gtk_main_quit), G_OBJECT (window));
gtk_widget_show_all(window);
gtk_main();
return 0;
}
Notes
I don't use GtkScrolledWindow's internal scrollbars, because they will overlay the content. I don't want that. Therefore I'm making this implementation.
I could imagine this to be a bug within GTK. Even if it is, is there a way to work around it?
I'm running Gtk3.24 under XOrg, XUb 21.04, Theme: Greybird.
edit
It looks like this behavior is triggered depending on the theme.

Setting a custom GtkAccelLabel

I am trying to set a custom GtkAccelLabel to my GtkMenuItem, though I cant figure out how to use that widget ... for me just nothing is displayed.
Why are the following calls wrong / not sufficient ?
...
fileMi = gtk_menu_item_new_with_label("File");
label = gtk_accel_label_new ("Strg+X");
gtk_accel_label_set_accel_widget (label, quitMi);
...
Here a complete reproducer:
int main(int argc, char *argv[]) {
GtkWidget *window;
GtkWidget *vbox;
GtkWidget *menubar;
GtkWidget *fileMenu;
GtkWidget *fileMi;
GtkWidget *quitMi;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
gtk_window_set_title(GTK_WINDOW(window), "Simple menu");
vbox = gtk_vbox_new(FALSE, 0);
gtk_container_add(GTK_CONTAINER(window), vbox);
GtkAccelGroup *accel_group;
// Create a GtkAccelGroup and add it to the window.
accel_group = gtk_accel_group_new ();
gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
menubar = gtk_menu_bar_new();
fileMenu = gtk_menu_new();
fileMi = gtk_menu_item_new_with_label("File");
quitMi = gtk_menu_item_new_with_label("Quit");
gtk_menu_item_set_submenu(GTK_MENU_ITEM(fileMi), fileMenu);
gtk_menu_shell_append(GTK_MENU_SHELL(fileMenu), quitMi);
gtk_menu_shell_append(GTK_MENU_SHELL(menubar), fileMi);
gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
GtkWidget *label = gtk_accel_label_new ("Strg+X");
gtk_accel_label_set_accel_widget (label, quitMi);
gtk_accel_label_refetch (label);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
g_signal_connect(G_OBJECT(quitMi), "activate",
G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
Hah, I found a solution .. though no idea if it actually should be done like that.
The key is, that there is already a gtk_accel_label on the MenuItem, and I can modify it:
for (GList* lp = gtk_container_get_children (quitMi); lp != NULL; lp = lp->next)
{
if (GTK_IS_ACCEL_LABEL (lp->data))
gtk_accel_label_set_accel (lp->data, GDK_KEY_0, GDK_MOD1_MASK);
}

Set the state of a menu item on shortcut press

A program has a menu bar, which has a menu, which has a menu item. The menu item is enabled or disabled depending on some circumstances. Checking of circumstances and enabling/disabling of the menu item is done when a user clicks on the menu that holds that menu item.
But, there is also a shortcut associated with that menu item. The shortcut functions only when the menu item is enabled.
How can we set the state of a menu item without clicking on the menu that holds it, when we want to use a shortcut?
Here is an example program that will hopefully make my question more clear:
#include <gtk/gtk.h>
struct check_sensitivity
{
GtkWidget *menuitem;
GtkTextBuffer *buffer;
};
void sensitivity(GtkWidget *menu, struct check_sensitivity *sens)
{
if (gtk_text_buffer_get_modified(sens->buffer))
gtk_widget_set_sensitive(sens->menuitem, TRUE);
else
gtk_widget_set_sensitive(sens->menuitem, FALSE);
}
void menuitem_click(GtkWidget *menuitem, GtkTextBuffer *buffer)
{
gtk_text_buffer_set_text(buffer, "", -1);
gtk_text_buffer_set_modified(buffer, FALSE);
}
int main(int argc, char **argv)
{
gtk_init(&argc, &argv);
struct check_sensitivity sens;
GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
g_signal_connect(window, "delete-event", G_CALLBACK(gtk_main_quit), NULL);
GtkWidget *view = gtk_text_view_new();
sens.buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
GtkWidget *menubar = gtk_menu_bar_new();
GtkWidget *menu = gtk_menu_new();
GtkAccelGroup *shortcuts = gtk_accel_group_new();
gtk_window_add_accel_group(GTK_WINDOW(window), shortcuts);
GtkWidget *menuitem = gtk_menu_item_new_with_label("Menu");
gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menu);
gtk_menu_shell_append(GTK_MENU_SHELL(menubar), menuitem);
g_signal_connect(menuitem, "activate", G_CALLBACK(sensitivity), &sens);
menuitem = gtk_menu_item_new_with_label("Clear");
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
gtk_widget_add_accelerator(menuitem, "activate", shortcuts, GDK_KEY_D, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
sens.menuitem = menuitem;
g_signal_connect(sens.menuitem, "activate", G_CALLBACK(menuitem_click), sens.buffer);
GtkWidget *box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
gtk_box_pack_start(GTK_BOX(box), menubar, FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(box), view, TRUE, TRUE, 0);
gtk_container_add(GTK_CONTAINER(window), box);
gtk_widget_show_all(window);
gtk_main();
}
In this program, the state of menu item "Clear" depends on that whether the buffer is modified or not. The state is, of course, set when the user clicks on the menu "Menu".
There is also a shortcut Ctrl+D that does the same thing as clicking on "Clear". The shortcut works as it should depending on the state of the menu item, but what is wrong is the state of the menu item. Let me explain:
Since the menu item is (un)set when a user click on "Menu", to update the state of "Clear" before using a shortcut, a user must click on it. If the shortcut is used before updating of the state of the menu item, it may not be able to do its function when it should.
Here is what you can try: open the program and write something. Try clearing it with Ctrl+D. You'll see that nothing happens even though it should, because the state of the menu item is not updated. If you click on "Menu", then try using the shortcut again, the text buffer will clear.
I'm just asking for a why to update the state when the shortcut is pressed as well. I tried a few ways that base on setting the state when Ctrl is pressed (I can't check only for Ctrl+D, I may have another shortcuts in a program), but all of them failed or produced bugs. One of those ways you can see here.
If you want to update the menuitem sensitivity depending on if there is text in your textbuffer, connect to the textbuffer changed or modified-changed signal. Since I am not good at C, but normally use Python:
textbuffer.connect("changed", update_menuitem_sensitivity)
changed
modified-changed
Finally found a working method.
#include <gtk/gtk.h>
struct check_sensitivity
{
GtkWidget *menuitem;
GtkTextBuffer *buffer;
};
void sensitivity(GtkWidget *menu, struct check_sensitivity *sens);
void check_sensitivity_on_ctrl(GtkWidget *window, GdkEventKey *key, struct check_sensitivity *sens)
{
if(key->keyval == GDK_KEY_Control_L || key->keyval == GDK_KEY_Control_R)
{
sensitivity(NULL, sens);
}
}
void sensitivity(GtkWidget *menu, struct check_sensitivity *sens)
{
if (gtk_text_buffer_get_modified(sens->buffer))
gtk_widget_set_sensitive(sens->menuitem, TRUE);
else
gtk_widget_set_sensitive(sens->menuitem, FALSE);
}
void menuitem_click(GtkWidget *menuitem, GtkTextBuffer *buffer)
{
gtk_text_buffer_set_text(buffer, "", -1);
gtk_text_buffer_set_modified(buffer, FALSE);
}
int main(int argc, char **argv)
{
gtk_init(&argc, &argv);
struct check_sensitivity sens;
GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_default_size(GTK_WINDOW(window), 400, 400);
g_signal_connect(window, "delete-event", G_CALLBACK(gtk_main_quit), NULL);
GtkWidget *view = gtk_text_view_new();
sens.buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
gtk_text_buffer_set_modified(sens.buffer, FALSE);
GtkWidget *menubar = gtk_menu_bar_new();
GtkWidget *menu = gtk_menu_new();
GtkAccelGroup *shortcuts = gtk_accel_group_new();
gtk_window_add_accel_group(GTK_WINDOW(window), shortcuts);
GtkWidget *menuitem = gtk_menu_item_new_with_label("Menu");
gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menu);
gtk_menu_shell_append(GTK_MENU_SHELL(menubar), menuitem);
g_signal_connect(menuitem, "activate", G_CALLBACK(sensitivity), &sens);
menuitem = gtk_menu_item_new_with_label("Clear");
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
gtk_widget_add_accelerator(menuitem, "activate", shortcuts, GDK_KEY_D, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
sens.menuitem = menuitem;
g_signal_connect(sens.menuitem, "activate", G_CALLBACK(menuitem_click), sens.buffer);
g_signal_connect_after(window, "key-press-event", G_CALLBACK(check_sensitivity_on_ctrl), &sens);
GtkWidget *box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
gtk_box_pack_start(GTK_BOX(box), menubar, FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(box), view, TRUE, TRUE, 0);
gtk_container_add(GTK_CONTAINER(window), box);
gtk_widget_show_all(window);
gtk_main();
}

How can I add GtkEventBox in scrolled windows layout?

I have a GTK+2 program which combining GtkScrolledWindow and GtkLayout. It works fine. But, I found if I put a GtkEvent object to GtkLayout, it will not display properly. Anybody knows why ? Thanks.
#include <gtk/gtk.h>
int main( int argc, char *argv[] )
{
GtkWidget *window, *button, *layout, *eventbox, *scrollwindow;
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_widget_set_size_request (window, 150, 150);
button = gtk_button_new_with_label ("button");
layout = gtk_layout_new(NULL, NULL);
gtk_layout_set_size( GTK_LAYOUT(layout), 300, 300);
scrollwindow = gtk_scrolled_window_new(GTK_LAYOUT(layout)->hadjustment, GTK_LAYOUT(layout)->vadjustment);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrollwindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_layout_put( GTK_LAYOUT(layout), button, 50, 250 );
/* uncomment to use eventbox
eventbox = gtk_event_box_new();
gtk_container_add( GTK_CONTAINER(eventbox), layout);
gtk_scrolled_window_add_with_viewport( GTK_SCROLLED_WINDOW(scrollwindow), eventbox);
*/
// comment this line when use eventbox
gtk_container_add( GTK_CONTAINER(scrollwindow), layout);
gtk_container_add((GtkContainer*)window, scrollwindow);
gtk_widget_show_all(window);
gtk_main ();
return 0;
}
The picture if the eventbox is used.
I found it does not have to use GtkEventBox here. The GtkLayout can capture events directly.
The following widgets do not have an associated window. So, It should be used with the GtkEventBox if you want to capture events.
GtkAlignment
GtkArrow
GtkBin
GtkBox
GtkImage
GtkItem
GtkLabel
GtkPaned
GtkPixmap
GtkScrolledWindow
GtkSeparator
GtkTable
GtkViewport
GtkAspectFrame
GtkFrame
GtkVPaned
GtkHPaned
GtkVBox
GtkHBox
GtkVSeparator
GtkHSeparator
The fixed program :
#include <gtk/gtk.h>
static gboolean button_press_event( GtkWidget *widget, GdkEventButton *event ) {
if (event->button == 1 ) printf("but down %i , %i\n", (int)event->x, (int)event->y);
return TRUE;
}
int main( int argc, char *argv[] )
{
GtkWidget *window, *button, *layout, *eventbox, *scrollwindow;
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_widget_set_size_request (window, 150, 150);
button = gtk_button_new_with_label ("button");
layout = gtk_layout_new(NULL, NULL);
gtk_layout_set_size( GTK_LAYOUT(layout), 300, 300);
scrollwindow = gtk_scrolled_window_new(gtk_layout_get_hadjustment(layout), gtk_layout_get_vadjustment(layout));
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrollwindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_layout_put( GTK_LAYOUT(layout), button, 50, 250 );
gtk_container_add( GTK_CONTAINER(scrollwindow), layout);
gtk_container_add(GTK_CONTAINER(window), scrollwindow);
g_signal_connect (layout, "button_press_event", G_CALLBACK (button_press_event), NULL);
gtk_widget_set_events(layout, GDK_EXPOSURE_MASK
| GDK_LEAVE_NOTIFY_MASK | GDK_BUTTON_PRESS_MASK
| GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
gtk_widget_show_all(window);
gtk_main ();
return 0;
}
The result of clicking on GtkLayout :

Make the menu items of menu created, functionable using GTK+

Here in this code I have created a menu bar and added menus and menu items to it...
But i dont know how to make the created menu items functionable... for eg: I want the 'Open' menu item of 'File' in the menu bar, open a file and display it...
I'm making a project on a Text Editor using GTK+ 3
#include <gtk/gtk.h>
GtkWidget *window;
GdkPixbuf *icon;
GtkWidget *vbox;
GtkWidget *menubar;
GtkWidget *fileMenu;
GtkWidget *fileMi;
GtkWidget *editMenu;
GtkWidget *editMi;
GtkWidget *searchMenu;
GtkWidget *searchMi;
GtkWidget *newMi;
GtkWidget *openMi;
GtkWidget *saveMi;
GtkWidget *saveasMi;
GtkWidget *quitMi;
GtkWidget *undoMi;
GtkWidget *redoMi;
GtkWidget *cutMi;
GtkWidget *copyMi;
GtkWidget *pasteMi;
GtkWidget *fontMi;
GtkWidget *findMi;
GtkWidget *replaceMi;
GtkWidget *text_view;
GtkTextBuffer *buffer;
void txt(){
text_view = gtk_text_view_new ();
gtk_box_pack_start (GTK_BOX (vbox), text_view, TRUE, TRUE, 0);
}
GdkPixbuf *create_pixbuf (const gchar *filename)
{
GdkPixbuf *pixbuf;
GError *error = NULL;
pixbuf = gdk_pixbuf_new_from_file (filename, &error);
if (!pixbuf)
{
fprintf (stderr, "%s\n", error->message);
g_error_free (error);
}
return pixbuf;
}
static void fileOpen(GtkWidget *load, gpointer window)
{
GtkWidget *choose;
GtkFileChooserAction action= GTK_FILE_CHOOSER_ACTION_OPEN;
choose = gtk_file_chooser_dialog_new("Choose a file to open", GTK_WINDOW(window), GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_OK, GTK_RESPONSE_OK, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL);
if (gtk_dialog_run(GTK_DIALOG(choose)) == GTK_RESPONSE_ACCEPT)
{
char *path = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(choose));
gtk_text_buffer_set_text(buffer, path, -1);
}
gtk_widget_destroy(choose);
}
static void fileSave(GtkWidget *save, gpointer window)
{
GtkWidget *saved;
saved = gtk_file_chooser_dialog_new("Choose a file to open", GTK_WINDOW(window), GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_OK, GTK_RESPONSE_OK, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL);
gtk_widget_show_all(saved);
gint resp = gtk_dialog_run(GTK_DIALOG(saved));
gtk_widget_destroy(saved);
}
int main (int argc, char *argv[])
{
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
icon = create_pixbuf ("Web.png");
gtk_window_set_title (GTK_WINDOW (window), "Write Pad");
gtk_window_set_default_size (GTK_WINDOW (window), 500, 400);
gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER);
gtk_window_set_icon (GTK_WINDOW (window), icon);
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_container_add (GTK_CONTAINER (window), vbox);
menubar = gtk_menu_bar_new ();
fileMenu = gtk_menu_new ();
fileMi = gtk_menu_item_new_with_label ("File");
newMi = gtk_menu_item_new_with_label ("New");
openMi = gtk_menu_item_new_with_label ("Open");
saveMi = gtk_menu_item_new_with_label ("Save");
saveasMi = gtk_menu_item_new_with_label ("Save As");
quitMi = gtk_menu_item_new_with_label ("Quit");
menubar = gtk_menu_bar_new ();
editMenu = gtk_menu_new ();
editMi = gtk_menu_item_new_with_label ("Edit");
undoMi = gtk_menu_item_new_with_label ("Undo");
redoMi = gtk_menu_item_new_with_label ("Redo");
cutMi = gtk_menu_item_new_with_label ("Cut");
copyMi = gtk_menu_item_new_with_label ("Copy");
pasteMi = gtk_menu_item_new_with_label ("Paste");
fontMi = gtk_menu_item_new_with_label ("Font");
menubar = gtk_menu_bar_new ();
searchMenu = gtk_menu_new ();
searchMi = gtk_menu_item_new_with_label ("Search");
findMi = gtk_menu_item_new_with_label ("Find");
replaceMi = gtk_menu_item_new_with_label ("Replace");
gtk_menu_item_set_submenu (GTK_MENU_ITEM (fileMi), fileMenu);
gtk_menu_shell_append(GTK_MENU_SHELL(fileMenu), newMi);
gtk_menu_shell_append(GTK_MENU_SHELL(fileMenu), openMi);
gtk_menu_shell_append(GTK_MENU_SHELL(fileMenu), saveMi);
gtk_menu_shell_append(GTK_MENU_SHELL(fileMenu), saveasMi);
gtk_menu_shell_append (GTK_MENU_SHELL (fileMenu), quitMi);
gtk_menu_shell_append (GTK_MENU_SHELL (menubar), fileMi);
gtk_menu_item_set_submenu (GTK_MENU_ITEM (editMi), editMenu);
gtk_menu_shell_append(GTK_MENU_SHELL(editMenu), undoMi);
gtk_menu_shell_append(GTK_MENU_SHELL(editMenu), redoMi);
gtk_menu_shell_append(GTK_MENU_SHELL(editMenu), cutMi);
gtk_menu_shell_append(GTK_MENU_SHELL(editMenu), copyMi);
gtk_menu_shell_append (GTK_MENU_SHELL (editMenu), pasteMi);
gtk_menu_shell_append (GTK_MENU_SHELL (editMenu), fontMi);
gtk_menu_shell_append (GTK_MENU_SHELL (menubar), editMi);
gtk_menu_item_set_submenu (GTK_MENU_ITEM (searchMi), searchMenu);
gtk_menu_shell_append(GTK_MENU_SHELL(searchMenu), findMi);
gtk_menu_shell_append(GTK_MENU_SHELL(searchMenu), replaceMi);
gtk_box_pack_start (GTK_BOX (vbox), menubar, FALSE, FALSE, 0);
txt();
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (gtk_main_quit), NULL);
g_signal_connect (G_OBJECT (quitMi), "activate", G_CALLBACK (gtk_main_quit), NULL);
g_signal_connect (G_OBJECT (newMi), "activate", G_CALLBACK (main), NULL);
g_signal_connect(G_OBJECT (openMi), "activate", G_CALLBACK(fileOpen), NULL);
g_signal_connect(G_OBJECT (saveMi), "activate", G_CALLBACK(fileSave), NULL);
gtk_widget_show_all (window);
gtk_main ();
return 0;
}
Basically, the problem you have is caused by the fact that all of your widgets are global variables, but your fileOpen function takes a gpointer window argument:
//global
GtkWidget *window;
//gpointer window argument -> local variable, not global
static void fileOpen(GtkWidget *load, gpointer window)
{
GtkWidget *choose;
GtkFileChooserAction action= GTK_FILE_CHOOSER_ACTION_OPEN;
choose = gtk_file_chooser_dialog_new(
"Choose a file to open",
GTK_WINDOW(window),//window is not global, but function argument here !
GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_OK, GTK_RESPONSE_OK, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL
);
if (gtk_dialog_run(GTK_DIALOG(choose)) == GTK_RESPONSE_ACCEPT)
{
char *path = gtk_file_chooser_get_filename(
GTK_FILE_CHOOSER(choose)
);
gtk_text_buffer_set_text(buffer, path, -1);
}
gtk_widget_destroy(choose);
}
A quick fix would be to rename the argument to something like gp_data or something. A better fix would be to not use a global variable, and instead rely on the gpointer to pass on the objects you need:
g_signal_connect(
G_OBJECT (openMi),
"activate",
G_CALLBACK(fileOpen),
(gpointer)text_view // text_view and buffer needn't be global now
);
And then use the gpointer argument to get the window and anything else you might need. You could also use a custom struct or array of widgets to pass to the callback in case you need more stuff to work with, but in this case, you should be OK with just passing the text_view widget, and changing the callback to something like this:
static void fileOpen(GtkWidget *load, gpointer widget)
{
//get the buffer
GtkTextBuffer *buffer = gtk_text_view_get_buffer(
GTK_TEXT_VIEW (widget)
);
//get window from text_View widget passed to callback
//so window doesn't have to be global either...
GtkWidget *window = gtk_widget_get_toplevel(
widget
), *choose;
GtkFileChooserAction action= GTK_FILE_CHOOSER_ACTION_OPEN;
choose = gtk_file_chooser_dialog_new(
"Choose a file to open",
GTK_WINDOW(window),
GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_OK, GTK_RESPONSE_OK, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL
);
if (gtk_dialog_run(GTK_DIALOG(choose)) == GTK_RESPONSE_ACCEPT)
{
char *path = gtk_file_chooser_get_filename(
GTK_FILE_CHOOSER(choose)
);
gtk_text_buffer_set_text(buffer, path, -1);
}
gtk_widget_destroy(choose);
}
Note that I haven't tested this code, but this should be enough to get you started. You'll have to change the code you have a bit, but you should be able to get rid of all of those global variables simply by passing the right widget to the callbacks.
As always: Read the documentation, just adding the link to the answer for future reference

Resources