Mercurial > pidgin
view plugins/gestures/stroke-draw.c @ 8986:8cf32769ba1b
[gaim-migrate @ 9761]
" This patch adds a Plugin Actions menu item after the
Account Actions menu. The Plugin Actions menu is
populated from the added 'actions' slot in
GaimPluginInfo. As a demonstration, the Idle Maker
plugin has been converted to no longer require GTK code
and the Preferences interface just to perform its
actions. Instead, it uses a Plugin Action to spawn a
Fields Request.
There's also a minor fix for consistency in the menu
building for buddy actions. The pre-existing method for
instructing a menu list to display a separator was to
insert a NULL rather than a proto_buddy_menu into the
GList of actions. The code for the buddy menus was
instead checking for a proto_buddy_menu with a '-'
label. This has been fixed, and it now correctly uses
NULL to indicate a separator."
"Date: 2004-05-16 02:25
Sender: taliesein
Logged In: YES
user_id=77326
I need to add a callback to this patch to watch for
loading/unloading of plugins (to determine when to rebuild
the menu). Since the appropriate way to handle Plugin
Actions is still mildly up for debate, I'm holding of on
correcting the patch until I know for sure whether I should
fix this patch, or scrap it and write a new one using a
different method."
"Date: 2004-05-18 12:26
Sender: taliesein
Logged In: YES
user_id=77326
I've completed changes to this patch to also add plugin load
and unload signals (it looks like plugin.c actually had
pre-signal callbacks in place, but they were never used or
converted to signals)
This patch now will correctly update the Plugin Action menu
as plugins load and unload."
I'm not entirely sure i like the ui of a plugins actions menu, but i think
that having some way for plugins to add actions on an account is a good
thing, and i'm not sure that every viable action fits under the accounts
actions menu. we may want to merge the two (the existing accounts actions
and this plugins actions), but both times it came up in #gaim no one seemed
to want to comment, and on one commented to the gaim-devel post either.
committer: Tailor Script <tailor@pidgin.im>
| author | Luke Schierer <lschiere@pidgin.im> |
|---|---|
| date | Thu, 20 May 2004 05:11:44 +0000 |
| parents | e0535ba0d667 |
| children | 19fd43d52d18 |
line wrap: on
line source
/* GNOME stroke implementation Copyright (c) 2000, 2001 Dan Nicolaescu See the file COPYING for distribution information. */ #include "config.h" #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <glib.h> #include <gtk/gtk.h> #include <gdk/gdkx.h> #include "gstroke.h" #include "gstroke-internal.h" #include <X11/Xlib.h> #include <X11/Xutil.h> static void gstroke_invisible_window_init (GtkWidget *widget); /*FIXME: Maybe these should be put in a structure, and not static...*/ static Display * gstroke_disp = NULL; static Window gstroke_window; static GC gstroke_gc; static int mouse_button = 2; static gboolean draw_strokes = FALSE; #define GSTROKE_TIMEOUT_DURATION 10 #define GSTROKE_SIGNALS "gstroke_signals" struct gstroke_func_and_data { void (*func)(GtkWidget *, void *); gpointer data; }; /*FIXME: maybe it's better to just make 2 static variables, not a structure */ struct mouse_position { struct s_point last_point; gboolean invalid; }; static struct mouse_position last_mouse_position; static guint timer_id; static void gstroke_execute (GtkWidget *widget, const gchar *name); static void record_stroke_segment (GtkWidget *widget) { gint x, y; struct gstroke_metrics *metrics; gtk_widget_get_pointer (widget, &x, &y); if (last_mouse_position.invalid) last_mouse_position.invalid = FALSE; else if (gstroke_draw_strokes()) { #if 1 XDrawLine (gstroke_disp, gstroke_window, gstroke_gc, last_mouse_position.last_point.x, last_mouse_position.last_point.y, x, y); /* XFlush (gstroke_disp); */ #else /* FIXME: this does not work. It will only work if we create a corresponding GDK window for stroke_window and draw on that... */ gdk_draw_line (widget->window, widget->style->fg_gc[GTK_STATE_NORMAL], last_mouse_position.last_point.x, last_mouse_position.last_point.y, x, y); #endif } if (last_mouse_position.last_point.x != x || last_mouse_position.last_point.y != y) { last_mouse_position.last_point.x = x; last_mouse_position.last_point.y = y; metrics = (struct gstroke_metrics *)g_object_get_data(G_OBJECT(widget), GSTROKE_METRICS); _gstroke_record (x, y, metrics); } } static gint gstroke_timeout (gpointer data) { GtkWidget *widget = GTK_WIDGET (data); record_stroke_segment (widget); return TRUE; } static gint process_event (GtkWidget *widget, GdkEvent *event, gpointer data G_GNUC_UNUSED) { static GtkWidget *original_widget = NULL; static GdkCursor *cursor = NULL; switch (event->type) { case GDK_BUTTON_PRESS: if (event->button.button != gstroke_get_mouse_button()) break; original_widget = widget; /* remeber the widget where the stroke started */ gstroke_invisible_window_init (widget); record_stroke_segment (widget); if (cursor == NULL) cursor = gdk_cursor_new(GDK_PENCIL); gdk_pointer_grab (widget->window, FALSE, GDK_BUTTON_RELEASE_MASK, NULL, cursor, event->button.time); timer_id = g_timeout_add (GSTROKE_TIMEOUT_DURATION, gstroke_timeout, widget); return TRUE; case GDK_BUTTON_RELEASE: if ((event->button.button != gstroke_get_mouse_button()) || (original_widget == NULL)) { /* Nice bug when you hold down one button and press another. */ /* We'll just cancel the gesture instead. */ last_mouse_position.invalid = TRUE; original_widget = NULL; if (timer_id > 0) g_source_remove (timer_id); gdk_pointer_ungrab (event->button.time); timer_id = 0; if (gstroke_draw_strokes() && gstroke_disp != NULL) { /* get rid of the invisible stroke window */ XUnmapWindow (gstroke_disp, gstroke_window); XFlush (gstroke_disp); } break; } last_mouse_position.invalid = TRUE; original_widget = NULL; g_source_remove (timer_id); gdk_pointer_ungrab (event->button.time); timer_id = 0; { char result[GSTROKE_MAX_SEQUENCE]; struct gstroke_metrics *metrics; metrics = (struct gstroke_metrics *)g_object_get_data(G_OBJECT (widget), GSTROKE_METRICS); if (gstroke_draw_strokes()) { /* get rid of the invisible stroke window */ XUnmapWindow (gstroke_disp, gstroke_window); XFlush (gstroke_disp); } _gstroke_canonical (result, metrics); gstroke_execute (widget, result); return FALSE; } return TRUE; default: break; } return FALSE; } void gstroke_set_draw_strokes(gboolean draw) { draw_strokes = draw; } gboolean gstroke_draw_strokes(void) { return draw_strokes; } void gstroke_set_mouse_button(gint button) { mouse_button = button; } guint gstroke_get_mouse_button(void) { return mouse_button; } void gstroke_enable (GtkWidget *widget) { struct gstroke_metrics* metrics = (struct gstroke_metrics *)g_object_get_data(G_OBJECT(widget), GSTROKE_METRICS); if (metrics == NULL) { metrics = (struct gstroke_metrics *)g_malloc (sizeof (struct gstroke_metrics)); metrics->pointList = NULL; metrics->min_x = 10000; metrics->min_y = 10000; metrics->max_x = 0; metrics->max_y = 0; metrics->point_count = 0; g_object_set_data(G_OBJECT(widget), GSTROKE_METRICS, metrics); g_signal_connect(G_OBJECT(widget), "event", G_CALLBACK(process_event), NULL); } else _gstroke_init (metrics); last_mouse_position.invalid = TRUE; } guint gstroke_signal_connect (GtkWidget *widget, const gchar *name, void (*func)(GtkWidget *widget, void *data), gpointer data) { struct gstroke_func_and_data *func_and_data; GHashTable *hash_table = (GHashTable*)g_object_get_data(G_OBJECT(widget), GSTROKE_SIGNALS); if (!hash_table) { hash_table = g_hash_table_new (g_str_hash, g_str_equal); g_object_set_data(G_OBJECT(widget), GSTROKE_SIGNALS, (gpointer)hash_table); } func_and_data = g_new (struct gstroke_func_and_data, 1); func_and_data->func = func; func_and_data->data = data; g_hash_table_insert (hash_table, (gpointer)name, (gpointer)func_and_data); return TRUE; } static void gstroke_execute (GtkWidget *widget, const gchar *name) { GHashTable *hash_table = (GHashTable*)g_object_get_data(G_OBJECT(widget), GSTROKE_SIGNALS); #if 0 gaim_debug(GAIM_DEBUG_MISC, "gestures", "gstroke %s\n", name); #endif if (hash_table) { struct gstroke_func_and_data *fd = (struct gstroke_func_and_data*)g_hash_table_lookup (hash_table, name); if (fd) (*fd->func)(widget, fd->data); } } void gstroke_cleanup (GtkWidget *widget) { struct gstroke_metrics *metrics; GHashTable *hash_table = (GHashTable*)g_object_get_data(G_OBJECT(widget), GSTROKE_SIGNALS); if (hash_table) /* FIXME: does this delete the elements too? */ g_hash_table_destroy (hash_table); g_object_steal_data(G_OBJECT(widget), GSTROKE_SIGNALS); metrics = (struct gstroke_metrics*)g_object_get_data(G_OBJECT(widget), GSTROKE_METRICS); if (metrics) g_free (metrics); g_object_steal_data(G_OBJECT(widget), GSTROKE_METRICS); } /* This function should be written using Gtk+ primitives*/ static void gstroke_invisible_window_init (GtkWidget *widget) { XSetWindowAttributes w_attr; XWindowAttributes orig_w_attr; unsigned long mask, col_border, col_background; unsigned int border_width; XSizeHints hints; Display *disp = GDK_WINDOW_XDISPLAY(widget->window); Window wind = GDK_WINDOW_XWINDOW (widget->window); int screen = DefaultScreen (disp); if (!gstroke_draw_strokes()) return; gstroke_disp = disp; /* X server should save what's underneath */ XGetWindowAttributes (gstroke_disp, wind, &orig_w_attr); hints.x = orig_w_attr.x; hints.y = orig_w_attr.y; hints.width = orig_w_attr.width; hints.height = orig_w_attr.height; mask = CWSaveUnder; w_attr.save_under = True; /* inhibit all the decorations */ mask |= CWOverrideRedirect; w_attr.override_redirect = True; /* Don't set a background, transparent window */ mask |= CWBackPixmap; w_attr.background_pixmap = None; /* Default input window look */ col_background = WhitePixel (gstroke_disp, screen); /* no border for the window */ #if 0 border_width = 5; #endif border_width = 0; col_border = BlackPixel (gstroke_disp, screen); gstroke_window = XCreateSimpleWindow (gstroke_disp, wind, 0, 0, hints.width - 2 * border_width, hints.height - 2 * border_width, border_width, col_border, col_background); gstroke_gc = XCreateGC (gstroke_disp, gstroke_window, 0, NULL); XSetFunction (gstroke_disp, gstroke_gc, GXinvert); XChangeWindowAttributes (gstroke_disp, gstroke_window, mask, &w_attr); XSetLineAttributes (gstroke_disp, gstroke_gc, 2, LineSolid, CapButt, JoinMiter); XMapRaised (gstroke_disp, gstroke_window); #if 0 /*FIXME: is this call really needed? If yes, does it need the real argc and argv? */ hints.flags = PPosition | PSize; XSetStandardProperties (gstroke_disp, gstroke_window, "gstroke_test", NULL, (Pixmap)NULL, NULL, 0, &hints); /* Receive the close window client message */ { /* FIXME: is this really needed? If yes, something should be done with wmdelete...*/ Atom wmdelete = XInternAtom (gstroke_disp, "WM_DELETE_WINDOW", False); XSetWMProtocols (gstroke_disp, gstroke_window, &wmdelete, True); } #endif }
