How to detect mouse click over an image in GTK+? - c

I'm working on a project in C using gtk+ 2.0.
I must check if the user has pressed left click on a image. I thought to call a function when left click is pressed and to get the position of the mouse, but how can I do that?

I hope I can assume you know how to connect an event to a widget, but if not: Here's a previous answer of mine that demonstrates how to do just that.
g_signal_connect for right mouse click?
As you can see there the event is passed as a GdkEventButton * (event from now on). This struct has the member fields that you are after: event->x and event->y both are gdouble fields.
Anyway, #unwind is right. As the GTK docs clearly state:
GtkImage is a “no window” widget (has no GdkWindow of its own), so by default does not receive events. If you want to receive events on the image, such as button clicks, place the image inside a GtkEventBox, then connect to the event signals on the event box.
GtkImage is not the only "windowless" widget, BTW. GtkLabel, for example, requires a similar approach if you want to handle clicks on a label. Anyway: More info here.
The man page then continues with a full code example of how to handle clicks on a GtkImage widget. Just look for the title "Handling button press events on a GtkImage." for the full explanation, but here's the code in case the link breaks:
static gboolean
button_press_callback (GtkWidget *event_box,
GdkEventButton *event,
gpointer data)
{
g_print ("Event box clicked at coordinates %f,%f\n",
event->x, event->y);
// Returning TRUE means we handled the event, so the signal
// emission should be stopped (don’t call any further callbacks
// that may be connected). Return FALSE to continue invoking callbacks.
return TRUE;
}
static GtkWidget*
create_image (void)
{
GtkWidget *image;
GtkWidget *event_box;
image = gtk_image_new_from_file ("myfile.png");
event_box = gtk_event_box_new ();
gtk_container_add (GTK_CONTAINER (event_box), image);
g_signal_connect (G_OBJECT (event_box),
"button_press_event",
G_CALLBACK (button_press_callback),
image);
return image;
}

The problem is that the GtkImage widget which is used to show an image in GTK+ does not generate events.
It's a "nowindow" widget, meaning that it's a passive container, which is used to display information and not to interact with the user.
You can fix that by wrapping the image in a GtkEventBox, which will add event support.

In GTK you can use the button-pressed-event gtk widget to do this
In pure c, from Programming Simplified
#include<graphics.h>
#include<conio.h>
#include<stdio.h>
#include<dos.h>
int initmouse();
void showmouseptr();
void hidemouseptr();
void getmousepos(int*,int*,int*);
union REGS i, o;
main()
{
int gd = DETECT, gm, status, button, x, y, tempx, tempy;
char array[50];
initgraph(&gd,&gm,"C:\\TC\\BGI");
settextstyle(DEFAULT_FONT,0,2);
status = initmouse();
if ( status == 0 )
printf("Mouse support not available.\n");
else
{
showmouseptr();
getmousepos(&button,&x,&y);
tempx = x;
tempy = y;
while(!kbhit())
{
getmousepos(&button,&x,&y);
if( x == tempx && y == tempy )
{}
else
{
cleardevice();
sprintf(array,"X = %d, Y = %d",x,y);
outtext(array);
tempx = x;
tempy = y;
}
}
}
getch();
return 0;
}
int initmouse()
{
i.x.ax = 0;
int86(0X33,&i,&o);
return ( o.x.ax );
}
void showmouseptr()
{
i.x.ax = 1;
int86(0X33,&i,&o);
}
void getmousepos(int *button, int *x, int *y)
{
i.x.ax = 3;
int86(0X33,&i,&o);
*button = o.x.bx;
*x = o.x.cx;
*y = o.x.dx;
}

Related

Drawing lines using Cairo in real time

In zetcode "Lines" code, I am trying to display the lines in real time as the mouse button is clicked. I then changed the clicked function to
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;
gtk_widget_queue_draw(widget);
}
return TRUE;
}
I was thinking it would display the lines every time button 1 was clicked, but they are not being drawn on the window at all. What am i missing here?
The function that runs on draw, do_drawing(), contains this line at its end:
glob.count = 0;
so it clears the array after drawing them all, thus making it very hard to accumulate a large number of lines like you're trying to.
If you are following the Zetcode example, you need to change the do_drawing method. This works by drawing a point in real time with each click.
static void do_drawing(cairo_t *cr)
{
cairo_set_source_rgb(cr, 0, 0, 0);//Line colour
cairo_set_line_width(cr, 0.5);//Line width
cairo_translate(cr, -170, -170);//Shift where line
//i is starting point, i+1 is next mouse coordinate
int i;
for (i = 0; i < glob.count - 1; i++ ) {
cairo_move_to(cr, glob.coordx[i], glob.coordy[i]);
cairo_line_to(cr, glob.coordx[i+1], glob.coordy[i+1]);
printf("from x:%f, y:%f\t to: x:%f, y:%f\n",glob.coordx[i],glob.coordy[i], glob.coordx[i+1], glob.coordy[i+1]);
cairo_stroke(cr);
}
}

Pause a running function pressing a button

I have a gtk2+ code in which I have a running button and a stopping one. I want that the stop button pause the RUN function (not to leave it) in a specific point until run button is pressed again, continuing in the same point.
The problem is that while loops block any kind of interaction with program interfaces and freezes.
void STOP(GtkWidget *widget, GObject *context_object_stop)
{
stop=1;
}
void RUN(GtkWidget *widget, GObject *context_object_run)
{
GtkEntry *buffer= g_object_get_data (context_object_run, "buffer");
GtkTextIter iter;
GtkTextMark *marker;
marker = gtk_text_buffer_get_insert(buffer);
gtk_text_buffer_get_iter_at_mark(buffer, &iter, marker);
stop=0;
int i=0;
for (i=0; i<5000000; i=i+1)
{
while (stop==1)
{
//here is my problem
}
gchar * stuff = g_strdup_printf("%d""\n", i);
gtk_text_buffer_insert(buffer, &iter, stuff, -1);
g_free(stuff);
while (gtk_events_pending())
gtk_main_iteration();
gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(wins), marker);
}
}
Any ideas to approach the solution?
EDIT; An approach of multithreading that does not stop running the main function:
stop=0;
int i=0;
pthread_t th1;
for (i=0; i<5000000; i=i+1)
{
void *StopRun(void *arg)
{
while (stop==1)
{
//here is my problem
}
}
if (stop==1)
{
pthread_create(&th1, NULL, (void*)StopRun, NULL);
}
gchar * stuff = g_strdup_printf("%d""\n", i);
gtk_text_buffer_insert(buffer, &iter, stuff, -1);
g_free(stuff);
while (gtk_events_pending())
gtk_main_iteration();
gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(wins), marker);
}
Have a look the documentation and examples of gtk_events_pending. It says:
This can be used to update the UI and invoke timeouts etc. while doing some time intensive computation.

Update of an integer between functions

I have a big code of GTK2 in which I´m having troubles updating the value of an integer (in this case trigan, that its defined in the main body and callbacked to the two functions that are giving me this problem).
I intend to press the button RUN that calls the function RUN that starts displaying numbers in real time. Then, while text its running I will press STOP button that calls the function STOP, updating the value of trigan to 1.
Here is the piece of code in question:
void STOP(GtkWidget *widget, GObject *context_object_stp)
{
GtkEntry *trigan = g_object_get_data (context_object_stp, "trigan");
trigan=1;
}
void RUN(GtkWidget *widget, GObject *context_object)
{
GtkEntry *buffer= g_object_get_data (context_object, "buffer");
GtkEntry *wins = g_object_get_data (context_object, "wins");
GtkEntry *trigan = g_object_get_data (context_object, "trigan");
GtkWidget iter;
GtkTextIter iterscrll;
GtkTextMark *mark;
mark = gtk_text_buffer_get_insert(buffer);
gtk_text_buffer_get_iter_at_mark(buffer, &iterscrll, mark);
gtk_text_buffer_get_iter_at_offset(buffer, &iter, 0);
trigan=0;
int i=0;
int k=0;
for (i=0; i<90; i=i+1)
{
while (trigan==1)
{
}
gchar * stuff = g_strdup_printf("%d"" [%d]\n", i, trigan);
/* Inserts buffer at position iter. */
gtk_text_buffer_insert(buffer, &iter, stuff, -1);
g_free(stuff);
/* Forcing. */
while (gtk_events_pending())
gtk_main_iteration();
/* Scrolls text_view the minimum distance such that mark is contained within the visible area of the widget. */
gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(wins), mark);
for (k=0; k<50000000; k=k+1)
{
k++;
}
k=0;
}
}
My logic say that whit this value of trigan=1 the
while (trigan==1)
{
}
should "stop" the function RUN, but it doesn't.
Help please!
yes, your assumption is correct but take a look at this extract of your code:
triga=0;
int i=0;
int k=0;
for (i=0; i<90; i=i+1)
{
while (triga==1)
{
}
as you see, triga is defined, assigned to 0 an never manipoulated in between before the while... so the condition while (triga==1) is false...
that is the reason..

gtk+ text editor vim type modes (insert / command)

I'm trying to incorporate vim like functionality in a text editor I am creating in gtk+ 2.0 and gtksourceview 2.0. I have it working except when I press "i" to enter insert mode it enters this mode properly, but then it types an "i" in the text buffer. Here is my keypress function which is setup to enter command mode (leave insert mode) when the esc key is hit, and as I said before enter insert mode when the i key is pressed:
gboolean on_key_press_win_main (GtkWidget *widget, GdkEventKey *event, gpointer user_data)
{
switch (event->keyval)
{
case GDK_i:
if (event->state & GDK_CONTROL_MASK)
{
printf("key pressed: %s\n", "ctrl + i");
}
else
{
GtkTextBuffer *tbuffer;
GtkTextView *text_view;
int page = 0;
gchar *msg;
gint row, col;
GtkTextIter iter;
page = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
text_view = GTK_TEXT_VIEW(txtinput[notebookPages[page]]);
tbuffer = gtk_text_view_get_buffer (text_view);
if (insert_mode == 0)
{
insert_mode = 1;
command_mode = 0;
/* update statusbar */
gtk_statusbar_pop(GTK_STATUSBAR(statusbar), 0);
gtk_text_buffer_get_iter_at_mark(tbuffer, &iter, gtk_text_buffer_get_insert(tbuffer));
row = gtk_text_iter_get_line(&iter);
col = gtk_text_iter_get_line_offset(&iter);
msg = g_strdup_printf("INSERT\t\t Col %d Ln %d", col+1, row+1);
gtk_statusbar_push(GTK_STATUSBAR(statusbar), 0, msg);
gtk_text_view_set_editable (text_view, TRUE);
}
}
break;
/* esc key */
case 65307:
{
GtkTextBuffer *tbuffer;
GtkTextView *text_view;
int page = 0;
gchar *msg;
gint row, col;
GtkTextIter iter;
page = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
text_view = GTK_TEXT_VIEW(txtinput[notebookPages[page]]);
tbuffer = gtk_text_view_get_buffer (text_view);
insert_mode = 0;
command_mode = 1;
gtk_text_view_set_editable (text_view, FALSE);
/* update statusbar */
gtk_statusbar_pop(GTK_STATUSBAR(statusbar), 0);
gtk_text_buffer_get_iter_at_mark(tbuffer, &iter, gtk_text_buffer_get_insert(tbuffer));
row = gtk_text_iter_get_line(&iter);
col = gtk_text_iter_get_line_offset(&iter);
msg = g_strdup_printf("Col %d Ln %d", col+1, row+1);
gtk_statusbar_push(GTK_STATUSBAR(statusbar), 0, msg);
}
break;
default:
return FALSE;
}
return FALSE;
}
As you can see gtk_text_view_set_editable (text_view, FALSE) is set when entering command mode, and gtk_text_view_set_editable (text_view, TRUE) is set when entering insert mode. However, when entering insert mode the text_view is set editable before the keystroke registers in the buffer even if it is the last command in the keypress case. How can I keep the i from being placed in the text buffer when entering insert mode?
Return TRUE from the event handler instead of FALSE to block any further processing of the event. Event handlers work like a filter, you filter out the keystrokes that you don't want to pass on to the textview.
PS. Don't use constants like 65307, use GDK_KEY_Escape or whatever it is.
Wouldn't you know it, as soon as I finished writing this question a viable option pops into my head. This is probably why the key_release_event was created. Worked like a charm.

Knowing Which Button Was pressed! GTK

I'm making a board like this
GtkWidget *board[x][y];
If I do an array of buttons, how can I know which button was pressed?
Does
g_signal_connect(G_OBJECT(board[][]), "clicked",
G_CALLBACK(board_button_pressed), NULL);
// I want to know what [][] they pressed, how could I verify/check this?
return which button of the array was pressed? Or do I have to make a separate function for each of the board pieces?
For example:
OOO
OXO
OOO
How to know which button was pressed if all of the buttons are named the same?
One of the simplest way would be to just send the information when you are connecting to the callback as data. Something on these lines:
...
typedef struct _identifier{
int x;
int y;
}identifier;
static void button_clicked_cb(GtkButton *button, gpointer data)
{
(void)button; /*To get rid of compiler warning*/
identifier *id = data;
printf("\n id = %d, %d\n", id->x, id->y);
return;
}
....
identifier id[x*y]; /* Size of x*y of the board*/
unsigned int counter = 0;
for (i = 0; i < x; i++)
{
for (j = 0; j < y; j++)
{
id[counter].x = i;
id[counter].y = j;
board[i][j] = gtk_button_new ();
g_signal_connect(board[i][j], "clicked", G_CALLBACK(button_clicked_cb), &id[counter]);
counter++;
}
}
Please note that "clicked" signal is associated only with GtkButton. If you need to use with GtkWidget then look at "button-press-event" or "button-release-event", in which case the callback signature will also change.
Hope this helps!

Resources