grim/guifications2

This was compatibility code for Pidgin 1.x.y; because it was written as a check
for "not 2.0.0" this code would be compiled in for 3.0.0. It looks like the
supporting code elsewhere was removed, causing this to leave an unresolved
symbol in guifications.so, thus causing Pidgin to refuse to load. Now we load
in Pidgin 3.0.0. Whether it works or not, I have no clue.
/*
* Guifications - The end all, be all, toaster popup plugin
* Copyright (C) 2003-2008 Gary Kramlich
*
* This program 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
* of the License, or (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301, USA.
*/
#ifdef HAVE_CONFIG_H
# include "../gf_config.h"
#endif
#include "gf_internal.h"
#include <glib.h>
#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include <math.h>
#include <string.h>
#include <debug.h>
#include <prefs.h>
#include <version.h>
#include "gf_action.h"
#include "gf_event_info.h"
#include "gf_notification.h"
#include "gf_preferences.h"
typedef enum {
GF_DISPLAY_STATE_UNKNOWN = 0,
GF_DISPLAY_STATE_SHOWING,
GF_DISPLAY_STATE_SHOWN,
GF_DISPLAY_STATE_HIDING,
GF_DISPLAY_STATE_DESTROYED
} GfDisplayState;
struct _GfDisplay {
/* Widgets */
GtkWidget *window;
GtkWidget *event;
GtkWidget *image;
/* Display stuff */
GfDisplayState state;
GdkPixbuf *pixbuf;
GdkRectangle partial;
gboolean has_alpha;
gint height;
gint width;
gint x;
gint y;
/* Timer intervals */
gint anim_time;
gint disp_time;
/* Animation stuff */
gint round;
gint rounds;
/* Action stuff */
GfEventInfo *info;
guint button;
};
#include "gf_display.h"
/*******************************************************************************
* consts/macros
******************************************************************************/
#define GF_DISPLAY_ROUND(x) ((x)+0.5)
static const double DELTA_SIZE = 1.333;
static const gint DELTA_TIME = 33.33;
/*******************************************************************************
* Globals
******************************************************************************/
static GList *displays = NULL;
static gboolean vertical, animate;
static GfDisplayPosition position;
#if GTK_CHECK_VERSION(2,2,0)
static gint disp_screen = 0, disp_monitor = 0;
#endif /* GTK_CHECK_VERSION(2,2,0) */
/*******************************************************************************
* Prototypes...
*
* Yeah, I know they're bad, but sometimes you just can't avoid them...
*
* Well, I could, but it'd add more complication...
******************************************************************************/
static gboolean gf_display_shown_cb(gpointer data);
static gboolean gf_display_animate_cb(gpointer data);
/*******************************************************************************
* Callbacks
******************************************************************************/
static gboolean
gf_display_button_press_cb(GtkWidget *w, GdkEventButton *e, gpointer data) {
GfDisplay *display = GF_DISPLAY(data);
gint x = 0, y = 0;
if(e->type == GDK_BUTTON_PRESS) {
display->button = e->button;
return TRUE;
} else if(e->type == GDK_BUTTON_RELEASE) {
GfAction *action = NULL;
const gchar *pref = NULL;
gdk_window_get_pointer(w->window, &x, &y, NULL);
if(x < 0 || x > display->width || y < 0 || y > display->height)
return FALSE;
switch(display->button) {
case 1: pref = GF_PREF_MOUSE_LEFT; break;
case 2: pref = GF_PREF_MOUSE_MIDDLE; break;
case 3: pref = GF_PREF_MOUSE_RIGHT; break;
default: pref = NULL; break;
}
if(!pref)
return FALSE;
action = gf_action_find_with_name(purple_prefs_get_string(pref));
if(!action)
return FALSE;
gf_action_execute(action, display, e);
return TRUE;
}
return FALSE;
}
/*******************************************************************************
* Gtk 2.0.0 stuff
*
* This is the default behavior of 2.0 and 1.x
******************************************************************************/
#if !GTK_CHECK_VERSION(2,2,0)
static void
gf_display_get_geometry(gint *x, gint *y, gint *width, gint *height) {
*x = *y = 0;
*width = gdk_screen_width();
*height = gdk_screen_height();
}
static void
gf_display_shape(GfDisplay *display) {
if(display->has_alpha) {
GdkBitmap *bitmap = NULL;
GdkPixbuf *pixbuf;
if(display->state == GF_DISPLAY_STATE_SHOWING ||
display->state == GF_DISPLAY_STATE_HIDING)
{
pixbuf = gtk_image_get_pixbuf(GTK_IMAGE(display->image));
if(!pixbuf) {
/* image has no pixbuf.. so we have no business continuing */
return;
}
} else {
pixbuf = display->pixbuf;
}
gdk_pixbuf_render_pixmap_and_mask(pixbuf, NULL, &bitmap, 255);
if(bitmap) {
gtk_widget_shape_combine_mask(display->window, bitmap, 0, 0);
g_object_unref(G_OBJECT(bitmap));
}
}
}
static void
gf_display_move(GfDisplay *display) {
gtk_window_move(GTK_WINDOW(display->window), display->x, display->y);
}
#else /* !GTK_CHECK_VERSION(2,2,0) */
/*******************************************************************************
* Gtk 2.2.0 and up stuff
*
* Allows users to specify which screen to display the notifications on and
* in the case of xinerama, to position it correctly
******************************************************************************/
static void
gf_display_get_geometry(gint *x, gint *y, gint *width, gint *height) {
GdkDisplay *display;
GdkScreen *screen;
GdkRectangle geo, m_geo, w_geo;
display = gdk_display_get_default();
screen = gdk_display_get_screen(display, disp_screen);
gdk_screen_get_monitor_geometry(screen, disp_monitor, &m_geo);
if(gf_display_get_workarea(&w_geo)) {
gdk_rectangle_intersect(&w_geo, &m_geo, &geo);
} else {
geo.x = m_geo.x;
geo.y = m_geo.y;
geo.width = m_geo.width;
geo.height = m_geo.height;
}
*x = geo.x;
*y = geo.y;
*width = geo.width;
*height = geo.height;
}
static void
gf_display_shape(GfDisplay *display) {
if(display->has_alpha) {
GdkBitmap *bmap;
GdkColormap *cmap;
GdkPixbuf *pixbuf;
GdkScreen *screen;
screen = gdk_display_get_screen(gdk_display_get_default(), disp_screen);
cmap = gdk_screen_get_system_colormap(screen);
if(display->state == GF_DISPLAY_STATE_SHOWING ||
display->state == GF_DISPLAY_STATE_HIDING)
{
pixbuf = gtk_image_get_pixbuf(GTK_IMAGE(display->image));
if(!pixbuf) {
/* image has no pixbuf.. so we have no business continuing */
return;
}
} else {
pixbuf = display->pixbuf;
}
gdk_pixbuf_render_pixmap_and_mask_for_colormap(pixbuf, cmap, NULL,
&bmap, 255);
if(bmap) {
gtk_widget_shape_combine_mask(display->window, bmap, 0, 0);
g_object_unref(G_OBJECT(bmap));
}
}
}
static void
gf_display_move(GfDisplay *display) {
GdkScreen *screen_t, *screen_s;
screen_t = gdk_display_get_screen(gdk_display_get_default(), disp_screen);
screen_s = gtk_window_get_screen(GTK_WINDOW(display->window));
if(gdk_screen_get_number(screen_s) != gdk_screen_get_number(screen_t)) {
if(display->has_alpha)
gtk_widget_shape_combine_mask(display->window, NULL, 0, 0);
gtk_window_set_screen(GTK_WINDOW(display->window), screen_t);
if(display->has_alpha)
gf_display_shape(display);
}
gtk_window_move(GTK_WINDOW(display->window), display->x, display->y);
}
gint
gf_display_get_default_screen() {
GdkScreen *screen;
screen = gdk_screen_get_default();
return gdk_screen_get_number(screen);
}
gint
gf_display_get_screen_count() {
return gdk_display_get_n_screens(gdk_display_get_default()) - 1;
}
gint
gf_display_get_default_monitor() {
return 0;
}
gint
gf_display_get_monitor_count() {
GdkDisplay *display;
GdkScreen *screen = NULL;
gint screens = 0, monitors = 0, i = 0;
display = gdk_display_get_default();
screens = gdk_display_get_n_screens(display);
for(i = 0; i < screens; i++) {
screen = gdk_display_get_screen(display, i);
monitors = MAX(monitors, gdk_screen_get_n_monitors(screen));
}
return monitors - 1;
}
#endif /* GTK_CHECK_VERSION(2,2,0) */
/*******************************************************************************
* The normal code (tm)
******************************************************************************/
static void
gf_display_position(GfDisplay *new_display) {
GfDisplay *display;
GList *l = NULL;
gint pad_x = 0, pad_y = 0;
gint disp_width = 0, disp_height = 0;
gint width = 0, height = 0;
gint total = 0;
g_return_if_fail(new_display);
gf_display_get_geometry(&pad_x, &pad_y, &width, &height);
for(l = displays; l; l = l->next) {
display = GF_DISPLAY(l->data);
if(display == new_display)
break;
if(vertical)
total += display->height;
else
total += display->width;
}
if(new_display->state == GF_DISPLAY_STATE_SHOWING ||
new_display->state == GF_DISPLAY_STATE_HIDING)
{
disp_width = new_display->partial.width;
disp_height = new_display->partial.height;
} else {
disp_width = new_display->width;
disp_height = new_display->height;
}
/* set the correct size for the window */
gtk_widget_set_size_request(new_display->window, disp_width, disp_height);
switch(position) {
case GF_DISPLAY_POSITION_NW:
if(vertical) {
new_display->x = pad_x;
new_display->y = pad_y + total;
} else {
new_display->x = pad_x + total;
new_display->y = pad_y;
}
break;
case GF_DISPLAY_POSITION_NE:
if(vertical) {
new_display->x = pad_x + width - disp_width;
new_display->y = pad_y + total;
} else {
new_display->x = pad_x + width - (total + disp_width);
new_display->y = pad_y;
}
break;
case GF_DISPLAY_POSITION_SW:
if(vertical) {
new_display->x = pad_x;
new_display->y = pad_y + height - (total + disp_height);
} else {
new_display->x = pad_x + total;
new_display->y = pad_y + height - (disp_height);
}
break;
case GF_DISPLAY_POSITION_SE:
if(vertical) {
new_display->x = pad_x + width - disp_width;
new_display->y = pad_y + height - (total + disp_height);
} else {
new_display->x = pad_x + width - (total + disp_width);
new_display->y = pad_y + height - disp_height;
}
break;
default:
break;
}
gf_display_move(new_display);
}
static void
gf_displays_position() {
GfDisplay *display;
GList *l;
for(l = displays; l; l = l->next) {
display = GF_DISPLAY(l->data);
gf_display_position(display);
}
}
GfDisplay *
gf_display_new() {
GfDisplay *display;
display = g_new0(GfDisplay, 1);
return display;
}
void
gf_display_destroy(GfDisplay *display) {
g_return_if_fail(display);
displays = g_list_remove(displays, display);
if(display->window) {
gtk_widget_destroy(display->window);
display->window = NULL;
}
if(display->pixbuf) {
g_object_unref(G_OBJECT(display->pixbuf));
display->pixbuf = NULL;
}
if(display->info) {
gf_event_info_destroy(display->info);
display->info = NULL;
}
g_free(display);
display = NULL;
gf_displays_position();
}
static gboolean
gf_display_shown_cb(gpointer data) {
GfDisplay *display = GF_DISPLAY(data);
guint timeout_id;
/* in case something wicked happened */
g_return_val_if_fail(display, FALSE);
display->state = GF_DISPLAY_STATE_HIDING;
timeout_id = g_timeout_add(DELTA_TIME, gf_display_animate_cb, data);
gf_event_info_set_timeout_id(display->info, timeout_id);
return FALSE;
}
static inline gint
gf_display_calculate_change(GfDisplay *display, gint maximum) {
/* This function calculates an an exponential change.
*
* It's a basic scalable exponential movement formula, given to us by
* Nathaniel Waters.
*/
gint ret;
/* this could probably use some type casting somewhere.. */
ret = GF_DISPLAY_ROUND(((gdouble)maximum /
pow(DELTA_SIZE, display->rounds)) *
pow(DELTA_SIZE, display->round));
return ret;
}
static gboolean
gf_display_animate(GfDisplay *display) {
GdkPixbuf *pixbuf;
GdkRectangle full;
gint total, current;
gboolean ret = TRUE;
/* figure out what we're modifing */
if(vertical)
total = display->height;
else
total = display->width;
/* calculate the change */
current = gf_display_calculate_change(display, total);
/* create our rects */
full.x = 0;
full.y = 0;
full.width = display->width;
full.height = display->height;
/* ugh too many possibilities */
switch(position) {
case GF_DISPLAY_POSITION_NW:
if(vertical) {
display->partial.x = full.x;
display->partial.y = full.height - current;
display->partial.width = full.width;
display->partial.height = current;
} else {
display->partial.x = full.width - current;
display->partial.y = full.y;
display->partial.width = current;
display->partial.height = full.height;
}
break;
case GF_DISPLAY_POSITION_NE:
if(vertical) {
display->partial.x = full.x;
display->partial.y = full.y;
display->partial.width = full.width;
display->partial.height = current;
} else {
display->partial.x = full.x;
display->partial.y = full.y;
display->partial.width = current;
display->partial.height = full.height;
}
break;
case GF_DISPLAY_POSITION_SW:
if(vertical) {
display->partial.x = full.x;
display->partial.y = full.y;
display->partial.width = full.width;
display->partial.height = current;
} else {
display->partial.x = full.width - current;
display->partial.y = full.y;
display->partial.width = current;
display->partial.height = full.height;
}
break;
case GF_DISPLAY_POSITION_SE:
if(vertical) {
display->partial.x = full.x;
display->partial.y = full.y;
display->partial.width = full.width;
display->partial.height = current;
} else {
display->partial.x = full.x;
display->partial.y = full.y;
display->partial.width = current;
display->partial.height = full.height;
}
break;
default:
display->partial.x = full.x;
display->partial.y = full.y;
display->partial.width = full.width;
display->partial.height = full.height;
break;
}
/* Add some sanity checks */
if(display->partial.width <= 0)
display->partial.width = 1;
if(display->partial.height <= 0)
display->partial.height = 1;
/* create our partial pixbuf and fill it */
pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, display->has_alpha, 8,
display->partial.width, display->partial.height);
if(!pixbuf) {
purple_debug_info("guifications", "failed to create partial pixbuf, "
"destroying display %p\n", display);
gf_display_destroy(display);
return FALSE;
}
gdk_pixbuf_copy_area(display->pixbuf,
display->partial.x, display->partial.y,
display->partial.width, display->partial.height,
pixbuf, 0, 0);
/* force the image and event box to be the same size as the partial
* pixbuf
*/
gtk_widget_set_size_request(display->image, display->partial.width,
display->partial.height);
/* update the image's pixbuf */
gtk_image_set_from_pixbuf(GTK_IMAGE(display->image), pixbuf);
/* unref the partial pixbuf */
g_object_unref(G_OBJECT(pixbuf));
/* update the display and force gdk to redraw it */
gf_display_shape(display);
gf_display_position(display);
gdk_window_process_updates(GDK_WINDOW(display->window->window), TRUE);
/* Finish up */
if(display->state == GF_DISPLAY_STATE_SHOWING) {
display->round++;
if(display->round > display->rounds) {
guint timeout_id;
/* set rounds back to the max */
display->round = display->rounds - 1;
display->state = GF_DISPLAY_STATE_SHOWN;
timeout_id = g_timeout_add(display->disp_time, gf_display_shown_cb,
(gpointer)display);
gf_event_info_set_timeout_id(display->info, timeout_id);
ret = FALSE;
}
} else { /* if(display->state == GF_DISPLAY_STATE_HIDING) { */
display->round--;
if(display->round <= 0) {
gf_display_destroy(display);
ret = FALSE;
}
}
return ret;
}
static gboolean
gf_display_animate_cb(gpointer data) {
GfDisplay *display = GF_DISPLAY(data);
gboolean ret;
/* in the event something wicked happened... */
g_return_val_if_fail(display, FALSE);
/* we call a function to do our animation here to modularize the process
* so we can do other types of animation, like fading depending on other
* conditions. These conditions should be checked here, and then called
* accordingly.
*
* The animation function should return a boolean so we can tell the
* timer handler whether or not to keep the timer around.
*/
ret = gf_display_animate(display);
return ret;
}
static gboolean
gf_display_destroy_cb(gpointer data) {
GfDisplay *display = GF_DISPLAY(data);
gf_display_destroy(display);
return FALSE;
}
static inline gboolean
gf_display_condense_check_buddy(GfEventInfo *info1, GfEventInfo *info2) {
PurpleAccount *account1 = NULL, *account2 = NULL;
PurpleBuddy *buddy1 = NULL, *buddy2 = NULL;
const gchar *name1 = NULL, *name2 = NULL;
buddy1 = gf_event_info_get_buddy(info1);
buddy2 = gf_event_info_get_buddy(info2);
/* if we don't have any buddies, we can't do anything, so bail */
if(!buddy1 || !buddy2)
return FALSE;
name1 = purple_buddy_get_name(buddy1);
name2 = purple_buddy_get_name(buddy2);
account1 = purple_buddy_get_account(buddy1);
account2 = purple_buddy_get_account(buddy2);
return (!g_utf8_collate(name1, name2) && account1 == account2);
}
static inline gboolean
gf_display_condense_check_contact(GfEventInfo *info1, GfEventInfo *info2) {
PurpleBuddy *buddy1 = NULL, *buddy2 = NULL;
PurpleContact *contact1 = NULL, *contact2 = NULL;
buddy1 = gf_event_info_get_buddy(info1);
buddy2 = gf_event_info_get_buddy(info2);
/* if we don't have any buddies, we can't do anything, so bail */
if(!buddy1 || !buddy2)
return FALSE;
contact1 = purple_buddy_get_contact(buddy1);
contact2 = purple_buddy_get_contact(buddy2);
return (contact1 == contact2);
}
static inline gboolean
gf_display_condense_check_target(GfEventInfo *info1, GfEventInfo *info2) {
PurpleConversation *conv1 = NULL, *conv2 = NULL;
const gchar *target1 = NULL, *target2 = NULL;
conv1 = gf_event_info_get_conversation(info1);
conv2 = gf_event_info_get_conversation(info2);
/* if we don't have any conversations, we can't do anythin, so bail */
if(!conv1 || !conv2)
return FALSE;
if(conv1 != conv2)
return FALSE;
target1 = gf_event_info_get_target(info1);
target2 = gf_event_info_get_target(info2);
return (!g_utf8_collate(target1, target2));
}
#define GF_DISPLAY_CHECK_PRIORITY G_STMT_START { \
if(priority1 >= priority2) { \
gf_display_destroy(display); \
continue; \
} else { \
ret = TRUE; \
} \
} G_STMT_END
/* Yes this big ugly function... isn't so big and ugly anymore...
* It checks for existing notification from the same buddy, or conv with
* with the same target, and condenses contacts by the use of helper funcs.
*
* It returns TRUE if the new notification/event should be destroyed because
* theres a matching notification that has a higher priority than the new
* notification/event
*/
static gboolean
gf_display_condense(GfEventInfo *info) {
GfDisplay *display = NULL;
GfEvent *event1 = NULL, *event2 = NULL;
GfEventPriority priority1, priority2;
GList *l = NULL, *ll = NULL;
gboolean ret = FALSE;
event1 = gf_event_info_get_event(info);
priority1 = gf_event_get_priority(event1);
for(l = displays; l; l = ll) {
display = GF_DISPLAY(l->data);
ll = l->next;
if(!display)
continue;
if(!display->info)
continue;
event2 = gf_event_info_get_event(display->info);
priority2 = gf_event_get_priority(event2);
if(gf_display_condense_check_buddy(info, display->info))
GF_DISPLAY_CHECK_PRIORITY;
if(gf_display_condense_check_contact(info, display->info))
GF_DISPLAY_CHECK_PRIORITY;
if(gf_display_condense_check_target(info, display->info))
GF_DISPLAY_CHECK_PRIORITY;
}
/* we only check the stack count if ret is true, because that means one
* has been removed, so we have space.
*/
if(ret == FALSE) {
gint throttle = purple_prefs_get_int(GF_PREF_BEHAVIOR_THROTTLE);
if(throttle > 0 && g_list_length(displays) + 1 > throttle) {
display = GF_DISPLAY(g_list_nth_data(displays, 0));
if(display)
gf_display_destroy(display);
gf_displays_position();
}
}
return ret;
}
void
gf_display_show_event(GfEventInfo *info, GfNotification *notification) {
GfDisplay *display = NULL;
gint display_time;
guint timeout_id = 0;
g_return_if_fail(info);
/* Here we kill the notification if the screen saver is running */
if(gf_display_screen_saver_is_running()) {
gf_event_info_destroy(info);
return;
}
/* we should never make it here with out a notification, but just in
* case we do....
*/
if(!notification) {
const gchar *event_name = gf_event_get_name(gf_event_info_get_event(info));
purple_debug_info("Guifications",
"could not find a notification for the event \"%s\"\n",
event_name ? event_name : "");
return;
}
/* condense contacts and same buddy notifications, a return value of TRUE
* means theres a higher priority notification already being displayed
*/
if(gf_display_condense(info)) {
gf_event_info_destroy(info);
return;
}
/* create the display */
display = gf_display_new();
display->info = info;
/* Render the pixbuf, if we get NULL destroy the display and return */
display->pixbuf = gf_notification_render(notification, display->info);
if(!display->pixbuf) {
GfTheme *theme = gf_notification_get_theme(notification);
GfThemeInfo *info = gf_theme_get_theme_info(theme);
purple_debug_info("Guifications", "render '%s' failed for theme '%s'\n",
gf_notification_get_type(notification),
gf_theme_info_get_name(info));
gf_display_destroy(display);
return;
}
/* grab info about the pixbuf */
display->has_alpha = gdk_pixbuf_get_has_alpha(display->pixbuf);
display->height = gdk_pixbuf_get_height(display->pixbuf);
display->width = gdk_pixbuf_get_width(display->pixbuf);
/* if we've made it this far, we can create the window safely */
display->window = gtk_window_new(GTK_WINDOW_POPUP);
gtk_window_set_role(GTK_WINDOW(display->window), "guification");
/* Create the event box, you know, so the mouse works! */
display->event = gtk_event_box_new();
if(!gtk_check_version(2,4,0))
g_object_set(G_OBJECT(display->event), "visible-window", FALSE, NULL);
gtk_container_add(GTK_CONTAINER(display->window), display->event);
/* now we create all our mouse signals that should play nice with
* other apps..
*/
g_signal_connect(G_OBJECT(display->window), "button-press-event",
G_CALLBACK(gf_display_button_press_cb),
(gpointer)display);
g_signal_connect(G_OBJECT(display->window), "button-release-event",
G_CALLBACK(gf_display_button_press_cb),
(gpointer)display);
/* create the image but don't put the pixbuf in it quite yet. */
display->image = gtk_image_new();
gtk_container_add(GTK_CONTAINER(display->event), display->image);
/* grab the display time */
display_time = 1000 * purple_prefs_get_int(GF_PREF_BEHAVIOR_DISPLAY_TIME);
/* animation is set, this is a new notification, so we animate it */
if(animate) {
/* we explicitly set the size request because the window is created
* with an image with no child, so it defaults to some insane size.
*/
gtk_widget_set_size_request(display->window,
display->width, display->height);
/* calculate our timer intervals. anim_time is used for showing and
* hiding, so that's 2/8ths which is 1/4th.
*
* Remember display time is stored in seconds!
*/
display->anim_time = display_time / 8;
display->disp_time = display_time * 3 / 4;
/* Calculate and store some more information about animating */
display->rounds = GF_DISPLAY_ROUND((gfloat)display->anim_time /
(gfloat)DELTA_TIME);
display->round = 0;
/* Set the state of the display */
display->state = GF_DISPLAY_STATE_SHOWING;
/* setup a timeout of DELTA_TIME ms for animating */
timeout_id = g_timeout_add(DELTA_TIME, gf_display_animate_cb,
(gpointer)display);
} else { /* no animation! */
/* set the image since we're not animating */
gtk_image_set_from_pixbuf(GTK_IMAGE(display->image), display->pixbuf);
/* shape it */
gf_display_shape(display);
/* We're showing! */
display->state = GF_DISPLAY_STATE_SHOWN;
/* build our timeout */
timeout_id = g_timeout_add(display_time, gf_display_destroy_cb,
(gpointer)display);
}
gf_event_info_set_timeout_id(info, timeout_id);
/* position and show the widget */
gf_display_position(display);
gtk_widget_show_all(display->window);
/* add the display to the list */
displays = g_list_append(displays, display);
}
GfEventInfo *
gf_display_get_event_info(GfDisplay *display) {
g_return_val_if_fail(display, NULL);
return display->info;
}
static void
gf_display_position_changed_cb(const gchar *name, PurplePrefType type,
gconstpointer val, gpointer data)
{
vertical = purple_prefs_get_bool(GF_PREF_APPEARANCE_VERTICAL);
position = purple_prefs_get_int(GF_PREF_APPEARANCE_POSITION);
gf_displays_position();
}
static void
gf_display_animate_changed_cb(const gchar *name, PurplePrefType type,
gconstpointer val, gpointer data)
{
animate = GPOINTER_TO_INT(val);
}
/*******************************************************************************
* Pref callbacks for the display/screen/monitor stuff
******************************************************************************/
#if GTK_CHECK_VERSION(2,2,0)
static void
gf_display_screen_changed_cb(const gchar *name, PurplePrefType type,
gconstpointer val, gpointer data)
{
disp_screen = GPOINTER_TO_INT(val);
gf_item_text_uninit();
gf_displays_position();
gf_item_text_init();
}
static void
gf_display_monitor_changed_cb(const gchar *name, PurplePrefType type,
gconstpointer val, gpointer data)
{
disp_monitor = GPOINTER_TO_INT(val);
gf_displays_position();
}
static guint scr_chg_id = 0, mon_chg_id = 0;
#endif /* GTK_CHECK_VERSION(2,2,0) */
/*******************************************************************************
* Regular pref callbacks
******************************************************************************/
static guint pos_chg_id = 0, ver_chg_id = 0, ani_chg_id = 0;
void
gf_display_init(PurplePlugin *plugin) {
/* since we just use the callbacks to get the changes we need to know
* what these are initially at.
*/
position = purple_prefs_get_int(GF_PREF_APPEARANCE_POSITION);
vertical = purple_prefs_get_bool(GF_PREF_APPEARANCE_VERTICAL);
animate = purple_prefs_get_bool(GF_PREF_APPEARANCE_ANIMATE);
/* Connect our pref callbacks */
pos_chg_id = purple_prefs_connect_callback(plugin,
GF_PREF_APPEARANCE_POSITION,
gf_display_position_changed_cb,
NULL);
ver_chg_id = purple_prefs_connect_callback(plugin,
GF_PREF_APPEARANCE_VERTICAL,
gf_display_position_changed_cb,
NULL);
ani_chg_id = purple_prefs_connect_callback(plugin,
GF_PREF_APPEARANCE_ANIMATE,
gf_display_animate_changed_cb,
NULL);
#if GTK_CHECK_VERSION(2,2,0)
/* setup our multi screen prefs */
disp_screen = purple_prefs_get_int(GF_PREF_ADVANCED_SCREEN);
disp_monitor = purple_prefs_get_int(GF_PREF_ADVANCED_MONITOR);
scr_chg_id = purple_prefs_connect_callback(plugin,
GF_PREF_ADVANCED_SCREEN,
gf_display_screen_changed_cb,
NULL);
mon_chg_id = purple_prefs_connect_callback(plugin,
GF_PREF_ADVANCED_MONITOR,
gf_display_monitor_changed_cb,
NULL);
#endif /* GTK_CHECK_VERSION(2,2,0) */
}
void
gf_display_uninit() {
purple_prefs_disconnect_callback(pos_chg_id);
purple_prefs_disconnect_callback(ver_chg_id);
#if GTK_CHECK_VERSION(2,2,0)
purple_prefs_disconnect_callback(scr_chg_id);
purple_prefs_disconnect_callback(mon_chg_id);
#endif /* GTK_CHECK_VERSION(2,2,0) */
}
gint
gf_display_get_monitor(void) {
return disp_monitor;
}
gint
gf_display_get_screen(void) {
return disp_screen;
}