grim/guifications2

flow: Merged 'autotools_mingw_cleanup' to ('develop').
/*
* 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);
if(bmap)
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;
}