[xiph-commits] r12121 - trunk/sushivision
xiphmont at svn.xiph.org
xiphmont at svn.xiph.org
Thu Nov 16 23:19:00 PST 2006
Author: xiphmont
Date: 2006-11-16 23:18:57 -0800 (Thu, 16 Nov 2006)
New Revision: 12121
Added:
trunk/sushivision/gtksucks.c
trunk/sushivision/gtksucks.h
Modified:
trunk/sushivision/Makefile
trunk/sushivision/main.c
trunk/sushivision/panel-2d.c
trunk/sushivision/panel-2d.h
Log:
Move all the gtk fixups into a new source module out of the main code
Begin addition of right-click context menus
Modified: trunk/sushivision/Makefile
===================================================================
--- trunk/sushivision/Makefile 2006-11-17 04:20:52 UTC (rev 12120)
+++ trunk/sushivision/Makefile 2006-11-17 07:18:57 UTC (rev 12121)
@@ -14,10 +14,10 @@
SOCFLAGS = -fPIC
SOLDFLAGS = -shared -nostdlib
-SRC = main.c scale.c plot.c slider.c slice.c panel.c panel-2d.c mapping.c dimension.c objective.c example_submain.c example_fractal.c
-OBJ = main.o scale.o plot.o slider.o slice.o panel.o panel-2d.o mapping.o dimension.o objective.o
-OBJ_EX1 = main.o scale.o plot.o slider.o slice.o panel.o panel-2d.o mapping.o dimension.o objective.o example_submain.o
-OBJ_EX2 = main.o scale.o plot.o slider.o slice.o panel.o panel-2d.o mapping.o dimension.o objective.o example_fractal.o
+SRC = main.c scale.c plot.c slider.c slice.c panel.c panel-2d.c mapping.c dimension.c objective.c gtksucks.c example_submain.c example_fractal.c
+OBJ = main.o scale.o plot.o slider.o slice.o panel.o panel-2d.o mapping.o dimension.o objective.o gtksucks.o
+OBJ_EX1 = main.o scale.o plot.o slider.o slice.o panel.o panel-2d.o mapping.o dimension.o objective.o gtksucks.o example_submain.o
+OBJ_EX2 = main.o scale.o plot.o slider.o slice.o panel.o panel-2d.o mapping.o dimension.o objective.o gtksucks.o example_fractal.o
INC = sushivision.h
LIBS = -lpthread -ldl
CAIROVER = >= 1.0.0
Added: trunk/sushivision/gtksucks.c
===================================================================
--- trunk/sushivision/gtksucks.c 2006-11-17 04:20:52 UTC (rev 12120)
+++ trunk/sushivision/gtksucks.c 2006-11-17 07:18:57 UTC (rev 12121)
@@ -0,0 +1,231 @@
+/*
+ *
+ * sushivision copyright (C) 2006 Monty <monty at xiph.org>
+ *
+ * sushivision is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * sushivision is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with sushivision; see the file COPYING. If not, write to the
+ * Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ */
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <gtk/gtk.h>
+#include <pthread.h>
+#include "sushivision.h"
+#include "internal.h"
+#include "gtksucks.h"
+
+/* well, no, gtk doesn't actually suck, but it does have a number of
+ default behaviors that get in the way of building a UI the way
+ users have come to expect. This module encapsulates a number of
+ generic fixups that eliminate inconsistencies or obstructive
+ behaviors. */
+
+/**********************************************************************/
+/* fixup number 1: insensitive widgets (except children of viewports,
+ a special case) eat mouse events. This doesn't seem like a big
+ deal, but if you're implementing a right-click context menu, this
+ means you cannot pop a menu if the user was unlucky enough to have
+ right-clicked over an insensitive widget. Wrap the widget
+ sensitivity setting method to also also set/unset the
+ GDK_BUTTON_PRESS_MASK on a widget; when insensitive, this will
+ remove it's ability to silently swallow mouse events. */
+
+/* Note that this only works after the widget is realized, as
+ realization will clobber the event mask */
+
+void gtk_widget_set_sensitive_fixup(GtkWidget *w, gboolean state){
+ gdk_threads_enter();
+ gtk_widget_set_sensitive(w,state);
+
+ if(state)
+ gtk_widget_add_events (w, GDK_ALL_EVENTS_MASK);
+ else
+ gtk_widget_remove_events (w, GDK_ALL_EVENTS_MASK);
+ gdk_threads_leave();
+}
+
+static void remove_events_internal (GtkWidget *widget,
+ gint events,
+ GList *window_list){
+ GList *l;
+
+ for (l = window_list; l != NULL; l = l->next){
+ GdkWindow *window = l->data;
+ gpointer user_data;
+
+ gdk_window_get_user_data (window, &user_data);
+ if (user_data == widget){
+ GList *children;
+
+ gdk_window_set_events (window, gdk_window_get_events(window) & (~events));
+
+ children = gdk_window_get_children (window);
+ remove_events_internal (widget, events, children);
+ g_list_free (children);
+ }
+ }
+}
+
+/* gtk provides a 'gtk_widget_add_events' but not a converse to remove
+ events. 'gtk_widget_set_events' only works pre-realization, so it
+ can't be used instead. */
+void gtk_widget_remove_events (GtkWidget *widget,
+ gint events){
+
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+
+ GQuark quark_event_mask = g_quark_from_static_string ("gtk-event-mask");
+ gint *eventp = g_object_get_qdata (G_OBJECT (widget), quark_event_mask);
+ gint original_events = events;
+
+ if (!eventp){
+ eventp = g_slice_new (gint);
+ *eventp = 0;
+ }
+
+ events = ~events;
+ events &= *eventp;
+
+ if(events){
+ *eventp = events;
+ g_object_set_qdata (G_OBJECT (widget), quark_event_mask, eventp);
+ }else{
+ g_slice_free (gint, eventp);
+ g_object_set_qdata (G_OBJECT (widget), quark_event_mask, NULL);
+ }
+
+ if (GTK_WIDGET_REALIZED (widget)){
+ GList *window_list;
+
+ if (GTK_WIDGET_NO_WINDOW (widget))
+ window_list = gdk_window_get_children (widget->window);
+ else
+ window_list = g_list_prepend (NULL, widget->window);
+
+ remove_events_internal (widget, original_events, window_list);
+
+ g_list_free (window_list);
+ }
+
+ g_object_notify (G_OBJECT (widget), "events");
+}
+
+/**********************************************************************/
+/* fixup number 2: Buttons (and their subclasses) don't do anything
+ with mousebutton3, but they swallow the events anyway preventing
+ them from cascading. The 'supported' way of implementing a
+ context-sensitive right-click menu is to recursively bind a new
+ handler to each and every button on a toplevel. This is mad
+ whack. The below 'fixes' buttons at the class level by ramming a
+ new button press handler into the GtkButtonClass structure (and,
+ unfortunately, button subclasses as thir classes have also already
+ initialized and made a copy of the Button's class structure and
+ handlers */
+
+static gboolean gtk_button_button_press_new (GtkWidget *widget,
+ GdkEventButton *event){
+
+ if (event->type == GDK_BUTTON_PRESS){
+ GtkButton *button = GTK_BUTTON (widget);
+
+ if (event->button == 3)
+ return FALSE;
+
+ if (button->focus_on_click && !GTK_WIDGET_HAS_FOCUS (widget))
+ gtk_widget_grab_focus (widget);
+
+ if (event->button == 1)
+ gtk_button_pressed (button);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean gtk_button_button_release_new (GtkWidget *widget,
+ GdkEventButton *event){
+ if (event->button == 1) {
+ GtkButton *button = GTK_BUTTON (widget);
+ gtk_button_released (button);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/* does not currently handle all button types, just the ones we use */
+void gtk_button3_fixup(){
+
+ GtkWidget *bb = gtk_button_new();
+ GtkWidgetClass *bc = GTK_WIDGET_GET_CLASS(bb);
+ bc->button_press_event = gtk_button_button_press_new;
+ bc->button_release_event = gtk_button_button_release_new;
+
+ bb = gtk_radio_button_new(NULL);
+ bc = GTK_WIDGET_GET_CLASS(bb);
+ bc->button_press_event = gtk_button_button_press_new;
+ bc->button_release_event = gtk_button_button_release_new;
+
+ bb = gtk_toggle_button_new();
+ bc = GTK_WIDGET_GET_CLASS(bb);
+ bc->button_press_event = gtk_button_button_press_new;
+ bc->button_release_event = gtk_button_button_release_new;
+
+ bb = gtk_check_button_new();
+ bc = GTK_WIDGET_GET_CLASS(bb);
+ bc->button_press_event = gtk_button_button_press_new;
+ bc->button_release_event = gtk_button_button_release_new;
+
+ // just leak 'em. they'll go away on exit.
+
+}
+
+/**********************************************************************/
+/* fixup number 3: GDK uses whatever default mutex type offered by the
+ system, and this usually means non-recursive ('fast') mutextes.
+ The problem with this is that gdk_threads_enter() and
+ gdk_threads_leave() cannot be used in any call originating from the
+ main loop, but are required in calls from idle handlers and other
+ threads. In effect we would need seperate identical versions of
+ each widget method, one locked, one unlocked, depending on where
+ the call originated. Eliminate this problem by installing a
+ recursive mutex. */
+
+static pthread_mutex_t gdkm;
+static pthread_mutexattr_t gdkma;
+
+static void recursive_gdk_lock(void){
+ pthread_mutex_lock(&gdkm);
+}
+
+static void recursive_gdk_unlock(void){
+ pthread_mutex_unlock(&gdkm);
+
+}
+
+void gtk_mutex_fixup(){
+ pthread_mutexattr_init(&gdkma);
+ pthread_mutexattr_settype(&gdkma,PTHREAD_MUTEX_RECURSIVE);
+ pthread_mutex_init(&gdkm,&gdkma);
+ gdk_threads_set_lock_functions(recursive_gdk_lock,recursive_gdk_unlock);
+}
+
+
+
+
Added: trunk/sushivision/gtksucks.h
===================================================================
--- trunk/sushivision/gtksucks.h 2006-11-17 04:20:52 UTC (rev 12120)
+++ trunk/sushivision/gtksucks.h 2006-11-17 07:18:57 UTC (rev 12121)
@@ -0,0 +1,30 @@
+/*
+ *
+ * sushivision copyright (C) 2006 Monty <monty at xiph.org>
+ *
+ * sushivision is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * sushivision is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with sushivision; see the file COPYING. If not, write to the
+ * Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ */
+
+#ifndef _GTK_SUCKS_H_
+#define _GTK_SUCKS_H_
+
+extern void gtk_widget_set_sensitive_fixup(GtkWidget *w, gboolean state);
+extern void gtk_widget_remove_events (GtkWidget *widget, gint events);
+extern void gtk_button3_fixup();
+extern void gtk_mutex_fixup();
+
+#endif
Modified: trunk/sushivision/main.c
===================================================================
--- trunk/sushivision/main.c 2006-11-17 04:20:52 UTC (rev 12120)
+++ trunk/sushivision/main.c 2006-11-17 07:18:57 UTC (rev 12121)
@@ -33,10 +33,9 @@
#include <pthread.h>
#include "sushivision.h"
#include "internal.h"
+#include "gtksucks.h"
-static pthread_mutex_t gdkm;
-static pthread_mutexattr_t gdkma;
static pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t mc = PTHREAD_COND_INITIALIZER;
sig_atomic_t _sushiv_exiting=0;
@@ -135,29 +134,13 @@
sushiv_realize_instance(instance_list[i]);
}
-static void recursive_gdk_lock(void){
- pthread_mutex_lock(&gdkm);
-}
-
-static void recursive_gdk_unlock(void){
- pthread_mutex_unlock(&gdkm);
-
-}
-
int main (int argc, char *argv[]){
int ret;
gtk_init (&argc, &argv);
g_thread_init (NULL);
- // correct an annoying default of gdk (use of non-recursive mutexes)
- // otherwise we'd need seperate versions of each widget method that
- // would be called outside the main thread
- pthread_mutexattr_init(&gdkma);
- pthread_mutexattr_settype(&gdkma,PTHREAD_MUTEX_RECURSIVE);
- pthread_mutex_init(&gdkm,&gdkma);
- gdk_threads_set_lock_functions(recursive_gdk_lock,recursive_gdk_unlock);
-
+ gtk_mutex_fixup();
gdk_threads_init ();
gtk_rc_parse_string(gtkrc_string());
gtk_rc_add_default_file("sushi-gtkrc");
@@ -176,6 +159,8 @@
signal(SIGINT,_sushiv_clean_exit);
//signal(SIGSEGV,_sushiv_clean_exit);
+
+ gtk_button3_fixup();
gtk_main ();
Modified: trunk/sushivision/panel-2d.c
===================================================================
--- trunk/sushivision/panel-2d.c 2006-11-17 04:20:52 UTC (rev 12120)
+++ trunk/sushivision/panel-2d.c 2006-11-17 07:18:57 UTC (rev 12121)
@@ -38,12 +38,21 @@
#include "slider.h"
#include "panel-2d.h"
#include "internal.h"
+#include "gtksucks.h"
static void panel2d_undo_log(sushiv_panel_t *p);
static void panel2d_undo_push(sushiv_panel_t *p);
static void panel2d_undo_suspend(sushiv_panel_t *p);
static void panel2d_undo_resume(sushiv_panel_t *p);
+static char *menulist[]={
+ "Undo [Backspace]",
+ "Redo [Space]",
+ "Find Peaks [p]",
+ "Quit [q]",
+ NULL
+};
+
static void render_checks(int w, int y, u_int32_t *render){
int x,j;
/* default checked background */
@@ -244,7 +253,8 @@
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(p2->dim_xb[i]))){
// make the y insensitive
if(p2->dim_yb[i])
- gtk_widget_set_sensitive(p2->dim_yb[i],FALSE);
+ gtk_widget_set_sensitive_fixup(p2->dim_yb[i],FALSE);
+
// set the x dim flag
p2->x_d = p->dimension_list[i];
p2->x_scale = p2->dim_scales[i];
@@ -258,13 +268,14 @@
}else{
// if there is a y, make it sensitive
if(p2->dim_yb[i])
- gtk_widget_set_sensitive(p2->dim_yb[i],TRUE);
+ gtk_widget_set_sensitive_fixup(p2->dim_yb[i],TRUE);
}
if(p2->dim_yb[i] &&
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(p2->dim_yb[i]))){
// make the x insensitive
if(p2->dim_xb[i])
- gtk_widget_set_sensitive(p2->dim_xb[i],FALSE);
+ gtk_widget_set_sensitive_fixup(p2->dim_xb[i],FALSE);
+
// set the y dim
p2->y_d = p->dimension_list[i];
p2->y_scale = p2->dim_scales[i];
@@ -278,7 +289,7 @@
}else{
// if there is a x, make it sensitive
if(p2->dim_xb[i])
- gtk_widget_set_sensitive(p2->dim_xb[i],TRUE);
+ gtk_widget_set_sensitive_fixup(p2->dim_xb[i],TRUE);
}
if((p2->dim_xb[i] &&
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(p2->dim_xb[i]))) ||
@@ -1216,6 +1227,20 @@
return FALSE;
}
+static gint popup_callback (GtkWidget *widget, GdkEvent *event){
+
+ GtkMenu *menu = GTK_MENU (widget);
+ GdkEventButton *event_button = (GdkEventButton *) event;
+
+ if (event_button->button == 3){
+ gtk_menu_popup (menu, NULL, NULL, NULL, NULL,
+ event_button->button, event_button->time);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
void _sushiv_realize_panel2d(sushiv_panel_t *p){
sushiv_panel2d_t *p2 = (sushiv_panel2d_t *)p->internal;
int i;
@@ -1363,6 +1388,24 @@
}
update_xy_availability(p);
+ // right mouse menu
+ {
+ char **ptr = menulist;
+ p2->popmenu = gtk_menu_new();
+
+ while(*ptr){
+ GtkWidget *mi = gtk_menu_item_new_with_label(*ptr);
+ gtk_menu_shell_append(GTK_MENU_SHELL(p2->popmenu),mi);
+ gtk_widget_show(mi);
+ ptr++;
+ }
+
+ gtk_widget_add_events(p2->toplevel, GDK_BUTTON_PRESS_MASK);
+ g_signal_connect_swapped (G_OBJECT(p2->toplevel), "button-press-event",
+ G_CALLBACK (popup_callback), p2->popmenu);
+
+ }
+
g_signal_connect (G_OBJECT (p2->toplevel), "key-press-event",
G_CALLBACK (panel2d_keypress), p);
gtk_window_set_title (GTK_WINDOW (p2->toplevel), p->name);
@@ -1370,6 +1413,11 @@
gtk_widget_realize(p2->toplevel);
gtk_widget_realize(p2->graph);
gtk_widget_show_all(p2->toplevel);
+ update_xy_availability(p); // yes, this was already done; however,
+ // gtk clobbered the event setup on the
+ // insensitive buttons when it realized
+ // them. This call will restore them.
+
panel2d_undo_resume(p);
}
Modified: trunk/sushivision/panel-2d.h
===================================================================
--- trunk/sushivision/panel-2d.h 2006-11-17 04:20:52 UTC (rev 12120)
+++ trunk/sushivision/panel-2d.h 2006-11-17 07:18:57 UTC (rev 12121)
@@ -39,6 +39,7 @@
GtkWidget *graph;
GtkWidget *top_table;
GtkWidget *dim_table;
+ GtkWidget *popmenu;
int data_w;
int data_h;
More information about the commits
mailing list