Drag and drop, motion_notify_event, mouse coordinate shaking - c

Then i start to drag an icon (code below), it starts jumping between 2 positions.
Can anybody help me resolve this shaking problem ?
Code:
#include <stdlib.h>
#include <stdio.h>
#include <gtk/gtk.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <string.h>
#define ICON_BUFFER 5
struct Icon {
GtkWidget *image;
int x,y,width,height;
};
GtkWidget *window;
GtkWidget *fixed;
struct Icon icons[ICON_BUFFER];
gboolean drag = FALSE;
static gboolean handleMove( GtkWidget *widget, GdkEventButton *event, gpointer data ){
struct Icon *icon = (struct Icon*) data;
icon->x = (int)event->x;
icon->y = (int)event->y;
gtk_fixed_move (GTK_FIXED (fixed), widget, icon->x, icon->y);
printf("move (%d , %d) %d\n", icon->x, icon->y,event->time);
return TRUE;
}
static gboolean handleRelease( GtkWidget *widget, GdkEventButton *event, gpointer data ){
g_signal_handlers_disconnect_by_func(widget, handleMove, data);
printf("%s\n", "disconnect_handleMove");
struct Icon *icon = (struct Icon*) data;
printf("position (%d , %d)\n", icon->x, icon->y);
drag = FALSE;
return TRUE;
}
static gboolean handleClick( GtkWidget *widget, GdkEventButton *event, gpointer data ){
//g_print ("Event box clicked at coordinates %f,%f\n", event->x, event->y);
struct Icon *icon = (struct Icon*) data;
if(!drag){
drag = TRUE;
printf("%s\n", "connect_handleMove");
g_signal_connect (G_OBJECT (widget), "motion_notify_event" ,G_CALLBACK(handleMove), data);
g_signal_connect (G_OBJECT (widget), "button-release-event",G_CALLBACK(handleRelease), data);
printf("position (%d , %d)\n", icon->x, icon->y);
return TRUE;
}else
return TRUE;
}
int main( int argc, char *argv[] ){
/* GtkWidget is the storage type for widgets */
GtkWidget *event;
GtkWidget *image;
GtkWidget *background;
GtkStyle *style;
GdkPixbuf *pixbuf;
gint i;
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (window), "Fixed Container");
g_signal_connect (window, "destroy", G_CALLBACK (gtk_main_quit), NULL);
gtk_container_set_border_width (GTK_CONTAINER (window), 0);
fixed = gtk_fixed_new ();
gtk_container_add (GTK_CONTAINER (window), fixed);
gtk_widget_set_size_request(fixed,480,480);
gtk_widget_show (fixed);
for (i = 0 ; i < 4 ; i++) {
event = gtk_event_box_new ();
image = gtk_image_new_from_file("test2.jpg");
icons[i].image = image;
icons[i].x = i*50;
icons[i].y = i*50;
icons[i].width = 50;
icons[i].height = 50;
gtk_container_add (GTK_CONTAINER (event), image);
gtk_fixed_put (GTK_FIXED (fixed), event, i*50, i*50);
gtk_event_box_set_above_child(GTK_EVENT_BOX(event), FALSE);
g_signal_connect (G_OBJECT (event), "button_press_event",
G_CALLBACK (handleClick), (gpointer) &icons[i]);
gtk_event_box_set_visible_window(GTK_EVENT_BOX(event),(gboolean)FALSE);
gtk_widget_show (image);
gtk_widget_show (event);
}
gtk_widget_show (window);
gtk_main ();
return 0;
}
Code can be compiled and run with (or gtk+-2.0 instead of gtk+-3.0):
cc pkg-config --cflags --libs gdk-pixbuf-2.0 gtk+-3.0 FixedDragAndDrop.c -o FixedDragAndDrop.o && ./FixedDragAndDrop.o

Related

How to print a continuous message while mouse button pressed (g_timeout_add)?

I have this code:
#include <stdio.h>
#include <gtk/gtk.h>
#include <glib/gi18n.h>
guint threadID = 0;
guint serial_counter = 0;
static gboolean
serial_data (gpointer user_data)
{
printf("hello\n");
printf("counter: %d\n", serial_counter);
serial_counter++;
return user_data;
}
static void
on_pressed (GtkButton* button, gpointer user_data)
{
if (user_data == 1)
{
threadID = g_timeout_add(250, serial_data, user_data);
}
else if (user_data == 0)
{
g_source_remove(threadID);
threadID = 0;
}
}
int
main (int argc, char *argv[])
{
GtkWidget *window;
gtk_init (&argc, &argv);
GtkWidget *stop_button;
GtkWidget *box;
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (window), "test.c");
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5);
stop_button = gtk_button_new_with_label (_("Stop"));
gtk_box_pack_start (GTK_BOX (box), stop_button, FALSE, FALSE, 0);
gtk_container_add (GTK_CONTAINER (window), box);
g_signal_connect (window, "button-press-event", G_CALLBACK (on_pressed), 1);
g_signal_connect (stop_button, "clicked", G_CALLBACK (on_pressed), 0);
g_signal_connect (window, "destroy", G_CALLBACK (gtk_main_quit), NULL);
gtk_widget_show_all (window);
gtk_main ();
return 0;
}
Which can be compiled by using:
gcc prueba.c `pkg-config --cflags gtk+-3.0` `pkg-config --libs gtk+-3.0` -o test
I want to print a message only when the button of the mouse is pressed (clicked but not released). Can this be achieved by modifying this code?
The objective is to press the right button of the mouse in any part of the current window and get a message printed, until the button is released.
I have no idea where you got the declaration of your signal handler function. It should look different according to GTK manual
This is some stripped example where I use "pressed" and "released" events to indicate button state and depending on the state I handle "mouse moved" events accordingly.
For your purpose you can add the print function and the timer.
static gboolean on_mouse_button_event(GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
if (event->button.button != 3) // right button
return FALSE;
if (event->button.type == GDK_BUTTON_PRESS)
{
if (!button_pressed)
{
button_pressed = TRUE;
// Start timer
}
}
else if (event->button.type == GDK_BUTTON_RELEASE)
{
if (button_pressed)
{
button_pressed = FALSE;
// Stop timer
}
}
return TRUE;
}
...
g_signal_connect(G_OBJECT(widget), "button-press-event", G_CALLBACK(on_mouse_button_event), NULL);
g_signal_connect(G_OBJECT(widget), "button-release-event", G_CALLBACK(on_mouse_button_event), NULL);

How to handle double-click events in GTK+3?

I was wondering how to produce double clicks?
Take the following code that draws lines using single clicks http://zetcode.com/gfx/cairo/basicdrawing/:
#include <cairo.h>
#include <gtk/gtk.h>
static void do_drawing(cairo_t *);
struct {
int count;
double coordx[100];
double coordy[100];
} glob;
static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr,
gpointer user_data)
{
do_drawing(cr);
return FALSE;
}
static void do_drawing(cairo_t *cr)
{
cairo_set_source_rgb(cr, 0, 0, 0);
cairo_set_line_width(cr, 0.5);
int i, j;
for (i = 0; i <= glob.count - 1; i++ ) {
for (j = 0; j <= glob.count - 1; j++ ) {
cairo_move_to(cr, glob.coordx[i], glob.coordy[i]);
cairo_line_to(cr, glob.coordx[j], glob.coordy[j]);
}
}
glob.count = 0;
cairo_stroke(cr);
}
static gboolean clicked(GtkWidget *widget, GdkEventButton *event,
gpointer user_data)
{
if (event->button == 1) {
glob.coordx[glob.count] = event->x;
glob.coordy[glob.count++] = event->y;
}
if (event->button == 3) {
gtk_widget_queue_draw(widget);
}
return TRUE;
}
int main(int argc, char *argv[])
{
GtkWidget *window;
GtkWidget *darea;
glob.count = 0;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
darea = gtk_drawing_area_new();
gtk_container_add(GTK_CONTAINER(window), darea);
gtk_widget_add_events(window, GDK_BUTTON_PRESS_MASK);
g_signal_connect(G_OBJECT(darea), "draw",
G_CALLBACK(on_draw_event), NULL);
g_signal_connect(window, "destroy",
G_CALLBACK(gtk_main_quit), NULL);
g_signal_connect(window, "button-press-event",
G_CALLBACK(clicked), NULL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 400, 300);
gtk_window_set_title(GTK_WINDOW(window), "Lines");
gtk_widget_show_all(window);
gtk_main();
return 0;
}
How do I get this program to respond to double clicks events instead of the single clicks?
I cannot find it in this list https://lazka.github.io/pgi-docs/Gtk-3.0/classes/Button.html#Gtk.Button.signals.clicked.
From GdkEventButton:
For double-clicks the order of events will be:
GDK_BUTTON_PRESS
GDK_BUTTON_RELEASE
GDK_BUTTON_PRESS
GDK_2BUTTON_PRESS
GDK_BUTTON_RELEASE
Note that the first click is received just like a normal button press,
while the second click results in a GDK_2BUTTON_PRESS being received
just after the GDK_BUTTON_PRESS.
(...)
For a double click to occur, the second button press must occur within 1/4 of a second of the first.
Each GdkEvent has a GdkEventType field that you can check for GDK_2BUTTON_PRESS or GDK_DOUBLE_BUTTON_PRESS (alias added in 3.6):
a mouse button has been double-clicked (clicked twice within a short
period of time). Note that each click also generates a
GDK_BUTTON_PRESS event.
in button-press-event callback.
#include <gtk/gtk.h>
gboolean clicked(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
{
if(event->type == GDK_DOUBLE_BUTTON_PRESS)
printf("double\n");
return TRUE;
}
int main (int argc, char *argv[])
{
gtk_init (&argc, &argv);
GtkWidget *window;
GtkWidget *label;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
label = gtk_label_new("label");
gtk_container_add(GTK_CONTAINER(window), label);
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_add_events(window, GDK_BUTTON_PRESS_MASK);
g_signal_connect(window, "button-press-event", G_CALLBACK(clicked), NULL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 400, 300);
gtk_widget_show_all(window);
gtk_main ();
return 0;
}

Printing value of entry box in a dialog in gtk c

I have worked recently with gtk+ in c language.
I have tried to communicate between two callbacks in a dialog and not in the main window (like here). But, unlike the solutions in the link, the program crushes (in the second callback when I try to use the widget I get from the parameters).
This is my code for This:
#include <gtk/gtk.h>
#include <stdlib.h>
#include <stdio.h>
static void print_text (GtkWidget *widget, gpointer data)
{
GtkEntry* entry = data;
printf("%s", gtk_entry_get_text(entry));
}
static void open_dialog (GtkWidget *widget, gpointer data)
{
GtkWidget *window = data;
GtkWidget *dialog;
GtkWidget *content_area;
GtkWidget *grid;
GtkWidget *label;
GtkWidget *button;
static GtkEntry *textbox;
dialog = gtk_dialog_new_with_buttons ("Get Text",
window,
GTK_DIALOG_MODAL,
GTK_STOCK_OK,
GTK_RESPONSE_OK,
NULL);
content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
grid = gtk_grid_new();
gtk_container_add (GTK_CONTAINER (content_area), grid);
label = gtk_label_new("Value: ");
gtk_grid_attach(GTK_GRID(grid), label, 0, 0, 1, 1);
textbox = gtk_entry_new();
gtk_entry_set_text(textbox, "<Value>");
gtk_grid_attach(GTK_GRID(grid), textbox, 1, 0, 1, 1);
gtk_widget_show_all (dialog);
g_signal_connect (GTK_DIALOG (dialog), "response", G_CALLBACK (print_text), textbox);
}
static void activate (GtkApplication *app, gpointer user_data)
{
GtkWidget *window;
GtkWidget *button;
GtkWidget *entry;
GtkWidget *grid;
window = gtk_application_window_new (app);
gtk_window_set_title (GTK_WINDOW (window), "Window");
gtk_container_set_border_width (GTK_CONTAINER (window), 10);
grid = gtk_grid_new ();
gtk_container_add (GTK_CONTAINER (window), grid);
button = gtk_button_new_with_label ("Print Text");
g_signal_connect (button, "clicked", G_CALLBACK (open_dialog), window);
gtk_grid_attach (GTK_GRID (grid), button, 0, 1, 1, 1);
gtk_widget_show_all (window);
}
int main (int argc, char **argv)
{
GtkApplication *app;
int status;
app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE);
g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
status = g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app);
return status;
}
The program should have printed the value of the entry box which in the dialog after closing the dialog. Note: in the main window there is a button that opens the dialog.
Thanks for helping!
Problem is on the print_text function. The prototype for GtkDialog response signal is:
void user_function (GtkDialog *dialog,
gint response_id,
gpointer user_data)
So you must change your function to:
static void print_text (GtkWidget *widget, gint response_id, gpointer data)
{
GtkEntry* entry = data;
printf("%s", gtk_entry_get_text(entry));
gtk_widget_destroy (widget); // This will close the dialog
}

How do I make a GTK3 image-in-a-window shrinkable?

I have a simple GTK3 app that displays an image from a file in a window.
When you resize the window, the image is scaled in the expose callback to fit the window.
However, once the window has grown, you can't shrink it again; the resize handles only let you make the window ever bigger.
With GTK2 it was trivial to allow grow and shrink with gtk_window_set_policy(w,1,1,1).
How can the same effect be achieved in GTK3?
Here's the ever-growing code example:
#include <gtk/gtk.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
gboolean resize_image(GtkWidget *widget, GdkEvent *event, void *data)
{
GdkPixbuf *pixbuf = gtk_image_get_pixbuf(GTK_IMAGE(widget));
if (pixbuf == NULL)
{
g_printerr("Failed to get pixbuf\n");
return 1;
}
pixbuf = gdk_pixbuf_scale_simple(pixbuf,
widget->allocation.width, widget->allocation.height,
GDK_INTERP_BILINEAR);
gtk_image_set_from_pixbuf(GTK_IMAGE(widget), pixbuf);
return FALSE;
}
int main(int argc, char **argv)
{
GtkWidget *window = NULL;
GtkWidget *image = NULL;
if (argc < 2 || argc > 3)
{
g_printerr("Usage: %s <image>\n", argv[0]);
return 1;
}
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
image = gtk_image_new_from_file(argv[1]);
if (image == NULL)
{
g_printerr("Could not open \"%s\"\n", argv[1]);
return 1;
}
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
g_signal_connect(image, "expose-event", G_CALLBACK(resize_image), NULL);
gtk_container_add(GTK_CONTAINER(window), image);
gtk_widget_show_all(GTK_WIDGET(window));
gtk_main();
return 0;
}
Eric Cecashon on the gtk-list mailing list suggests using a cairo drawing area inside a 1x1 grid container, which works fairly well:
/*
gcc -Wall da_resize.c -o da_resize `pkg-config gtk+-3.0 --cflags --libs`
Tested on Ubuntu16.04, GTK3.18.
*/
#include<gtk/gtk.h>
gboolean draw_picture(GtkWidget *da, cairo_t *cr, gpointer data)
{
gint width=gtk_widget_get_allocated_width(da);
gint height=gtk_widget_get_allocated_height(da);
GdkPixbuf *temp=gdk_pixbuf_scale_simple((GdkPixbuf*)data, width, height, GDK_INTERP_BILINEAR);
gdk_cairo_set_source_pixbuf(cr, temp, 0, 0);
cairo_paint(cr);
g_object_unref(temp);
return FALSE;
}
int main(int argc, char *argv[])
{
gtk_init(&argc, &argv);
GtkWidget *window=gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "Resize Picture");
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 400, 400);
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
//Needs a valid picture.
GdkPixbuf *pixbuf=gdk_pixbuf_new_from_file(argc>1 ? argv[1] : "image.jpg", NULL);
GtkWidget *da1=gtk_drawing_area_new();
gtk_widget_set_hexpand(da1, TRUE);
gtk_widget_set_vexpand(da1, TRUE);
g_signal_connect(da1, "draw", G_CALLBACK(draw_picture), pixbuf);
GtkWidget *grid=gtk_grid_new();
gtk_grid_attach(GTK_GRID(grid), da1, 0, 0, 1, 1);
gtk_container_add(GTK_CONTAINER(window), grid);
gtk_widget_show_all(window);
gtk_main();
g_object_unref(pixbuf);
return 0;
}

InfoBar only shown on window change

Why is the GtkInfoBar only visible if I change the window (focus)?
Hit the F5 key to get the InfoBar visible. Close the InfoBar and hit F5 again.
On my debian 8 (Gnome 3.20) system the InfoBar is only visible the first time, from the second time on it's only visible if I change the focus of the application window.
// gcc `pkg-config --cflags gtk+-3.0` -o info_bar infobar_nonglade.c `pkg-config --libs gtk+-3.0`
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
// Callback to close the window with Esc key
static gboolean check_escape(GtkWidget *widget, GdkEventKey *event, gpointer data) {
if (event->keyval == GDK_KEY_Escape) {
gtk_main_quit();
return TRUE;
}
return FALSE;
}
static gboolean on_key_f5(GtkWidget *widget, GdkEventKey *event, gpointer infobar) {
if (event->keyval == GDK_KEY_F5) {
gtk_widget_set_visible(infobar, !(gtk_widget_get_visible(infobar)));
return TRUE;
}
return FALSE;
}
int main (int argc, char **argv) {
gtk_init(&argc, &argv);
GtkWidget *button, *content_area, *infobar, *message_label, *vbox, *window;
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
infobar = gtk_info_bar_new();
button = gtk_button_new_with_label ("OK");
gtk_window_set_default_size (GTK_WINDOW (window), 1024, 600);
gtk_widget_set_no_show_all (infobar, TRUE);
gtk_info_bar_add_button (GTK_INFO_BAR (infobar),
"OK",
GTK_RESPONSE_OK);
g_signal_connect (infobar, "response", G_CALLBACK (gtk_widget_hide), NULL);
gtk_container_add (GTK_CONTAINER (window), vbox);
gtk_box_pack_start (GTK_BOX (vbox), infobar, FALSE, FALSE, 0);
gtk_widget_show_all (vbox);
// Connect callback to close the window with the Esc key
g_signal_connect (window, "key_press_event", G_CALLBACK (check_escape), NULL);
g_signal_connect (window, "key_press_event", G_CALLBACK (on_key_f5), infobar);
gtk_widget_show_all (window);
gtk_main();
return 0;
}
I can not test it on my machine, but you can try:
gtk_widget_set_visible(infobar, !(gtk_widget_get_visible(infobar)));
instead of
gtk_widget_set_no_show_all (infobar, FALSE);
gtk_widget_show_all (infobar);
Also, why are you invalidating the event returning TRUE on gboolean on_key_f5?
This looks like an old well known bug :(
Simple solution is to disable animations. I can live with that perfectly fine.
int main (int argc, char **argv) {
gtk_init(&argc, &argv);
// Fix InfoBar Bug: https://bugzilla.gnome.org/show_bug.cgi?id=710888
g_object_set (gtk_settings_get_default (), "gtk-enable-animations", FALSE, NULL);
...
}

Resources