[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