grim/guifications2
Clone
Summary
Browse
Changes
Graph
Some more changes, including the GAIM_PLUGINS to PURPLE_PLUGINS that
org.guifications.gf2
2007-04-01, John Bailey
b5c2e9cb069a
Some more changes, including the GAIM_PLUGINS to PURPLE_PLUGINS that
sadrul caught without even looking at the source :)
/*
* Guifications - The end all, be all, toaster popup plugin
* Copyright (C) 2003-2005 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef _WIN32
#
include
<gdk/gdkx.h>
#
include
<X11/Xlib.h>
#
include
<X11/Xutil.h>
#
include
<X11/Xatom.h>
#else
# ifndef WINVER
# define WINVER 0x0500
/* This is Windows 2000 */
# endif
/* WINVER */
#
include
<windows.h>
typedef
HMONITOR
WINAPI
GF_MonitorFromRect
(
LPCRECT
,
DWORD
);
typedef
BOOL
WINAPI
GF_GetMonitorInfo
(
HMONITOR
,
LPMONITORINFO
);
#endif
/* _WIN32 */
#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
<gaim-compat.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
(
gaim_prefs_get_string
(
pref
));
if
(
!
action
)
return
FALSE
;
gf_action_execute
(
action
,
display
,
e
);
return
TRUE
;
}
return
FALSE
;
}
/*******************************************************************************
* Uses _NET_WORKAREA or SPI_GETWORKAREA to get the geometry of a screen
******************************************************************************/
#ifdef _WIN32
static
gboolean
win32_get_workarea_fallback
(
GdkRectangle
*
rect
)
{
RECT
rcWork
;
if
(
SystemParametersInfo
(
SPI_GETWORKAREA
,
0
,
&
rcWork
,
FALSE
))
{
rect
->
x
=
rcWork
.
left
;
rect
->
y
=
rcWork
.
top
;
rect
->
width
=
rcWork
.
right
-
rcWork
.
left
;
rect
->
height
=
rcWork
.
bottom
-
rcWork
.
top
;
return
TRUE
;
}
return
FALSE
;
}
/**
* This will only work on Win98+ and Win2K+
*/
static
gboolean
win32_adjust_workarea_multi_monitor
(
GdkRectangle
*
rect
)
{
GF_GetMonitorInfo
*
the_GetMonitorInfo
;
GF_MonitorFromRect
*
the_MonitorFromRect
;
HMODULE
hmod
;
HMONITOR
monitor
;
MONITORINFO
info
;
RECT
monitorRect
;
gint
virtual_screen_x
,
virtual_screen_y
;
monitorRect
.
left
=
rect
->
x
;
monitorRect
.
right
=
rect
->
x
+
rect
->
width
;
monitorRect
.
top
=
rect
->
y
;
monitorRect
.
bottom
=
rect
->
y
+
rect
->
height
;
/* Convert the coordinates so that 0, 0 is the top left of the Primary
* Monitor, not the top left of the virtual screen
*/
virtual_screen_x
=
GetSystemMetrics
(
SM_XVIRTUALSCREEN
);
virtual_screen_y
=
GetSystemMetrics
(
SM_YVIRTUALSCREEN
);
if
(
virtual_screen_x
!=
0
)
{
monitorRect
.
left
+=
virtual_screen_x
;
monitorRect
.
right
+=
virtual_screen_x
;
}
if
(
virtual_screen_y
!=
0
)
{
monitorRect
.
top
+=
virtual_screen_y
;
monitorRect
.
bottom
+=
virtual_screen_y
;
}
if
(
!
(
hmod
=
GetModuleHandle
(
"user32"
)))
{
return
FALSE
;
}
if
(
!
(
the_MonitorFromRect
=
(
GF_MonitorFromRect
*
)
GetProcAddress
(
hmod
,
"MonitorFromRect"
)))
{
return
FALSE
;
}
if
(
!
(
the_GetMonitorInfo
=
(
GF_GetMonitorInfo
*
)
GetProcAddress
(
hmod
,
"GetMonitorInfoA"
)))
{
return
FALSE
;
}
monitor
=
the_MonitorFromRect
(
&
monitorRect
,
MONITOR_DEFAULTTONEAREST
);
info
.
cbSize
=
sizeof
(
info
);
if
(
!
the_GetMonitorInfo
(
monitor
,
&
info
))
{
return
FALSE
;
}
rect
->
x
=
info
.
rcWork
.
left
;
rect
->
y
=
info
.
rcWork
.
top
;
rect
->
width
=
info
.
rcWork
.
right
-
info
.
rcWork
.
left
;
rect
->
height
=
info
.
rcWork
.
bottom
-
info
.
rcWork
.
top
;
/** Convert the coordinates so that 0, 0 is the top left of the virtual screen,
* not the top left of the primary monitor */
if
(
virtual_screen_x
!=
0
)
{
rect
->
x
+=
-
virtual_screen_x
;
}
if
(
virtual_screen_y
!=
0
)
{
rect
->
y
+=
-
virtual_screen_y
;
}
return
TRUE
;
}
static
void
win32_adjust_workarea
(
GdkRectangle
*
rect
)
{
if
(
!
win32_adjust_workarea_multi_monitor
(
rect
))
{
if
(
!
win32_get_workarea_fallback
(
rect
))
{
/* I don't think this will ever happen */
rect
->
x
=
0
;
rect
->
y
=
0
;
rect
->
height
=
GetSystemMetrics
(
SM_CXSCREEN
);
rect
->
width
=
GetSystemMetrics
(
SM_CYSCREEN
);
}
}
}
#endif
/** _WIN32 */
gboolean
gf_display_get_workarea
(
GdkRectangle
*
rect
)
{
#ifndef _WIN32
Atom
xa_desktops
,
xa_current
,
xa_workarea
,
xa_type
;
Display
*
x_display
;
Window
x_root
;
guint32
desktops
=
0
,
current
=
0
;
gulong
*
workareas
,
len
,
fill
;
guchar
*
data
;
gint
format
;
# if !GTK_CHECK_VERSION(2,2,0)
x_display
=
GDK_DISPLAY
();
x_root
=
XDefaultRootWindow
(
x_display
);
# else
/* GTK 2.2.0 and up */
GdkDisplay
*
g_display
;
GdkScreen
*
g_screen
;
Screen
*
x_screen
;
/* get the gdk display */
g_display
=
gdk_display_get_default
();
if
(
!
g_display
)
return
FALSE
;
/* get the x display from the gdk display */
x_display
=
gdk_x11_display_get_xdisplay
(
g_display
);
if
(
!
x_display
)
return
FALSE
;
/* get the screen according to the prefs */
g_screen
=
gdk_display_get_screen
(
g_display
,
disp_screen
);
if
(
!
g_screen
)
return
FALSE
;
/* get the x screen from the gdk screen */
x_screen
=
gdk_x11_screen_get_xscreen
(
g_screen
);
if
(
!
x_screen
)
return
FALSE
;
/* get the root window from the screen */
x_root
=
XRootWindowOfScreen
(
x_screen
);
# endif
/* !GTK_CHECK_VERSION(2,2,0) */
/* find the _NET_NUMBER_OF_DESKTOPS atom */
xa_desktops
=
XInternAtom
(
x_display
,
"_NET_NUMBER_OF_DESKTOPS"
,
True
);
if
(
xa_desktops
==
None
)
return
FALSE
;
/* get the number of desktops */
if
(
XGetWindowProperty
(
x_display
,
x_root
,
xa_desktops
,
0
,
1
,
False
,
XA_CARDINAL
,
&
xa_type
,
&
format
,
&
len
,
&
fill
,
&
data
)
!=
Success
)
{
return
FALSE
;
}
if
(
!
data
)
return
FALSE
;
desktops
=
*
(
guint32
*
)
data
;
XFree
(
data
);
/* find the _NET_CURRENT_DESKTOP atom */
xa_current
=
XInternAtom
(
x_display
,
"_NET_CURRENT_DESKTOP"
,
True
);
if
(
xa_current
==
None
)
return
FALSE
;
/* get the current desktop */
if
(
XGetWindowProperty
(
x_display
,
x_root
,
xa_current
,
0
,
1
,
False
,
XA_CARDINAL
,
&
xa_type
,
&
format
,
&
len
,
&
fill
,
&
data
)
!=
Success
)
{
return
FALSE
;
}
if
(
!
data
)
return
FALSE
;
current
=
*
(
guint32
*
)
data
;
XFree
(
data
);
/* find the _NET_WORKAREA atom */
xa_workarea
=
XInternAtom
(
x_display
,
"_NET_WORKAREA"
,
True
);
if
(
xa_workarea
==
None
)
return
FALSE
;
if
(
XGetWindowProperty
(
x_display
,
x_root
,
xa_workarea
,
0
,
(
glong
)(
4
*
32
),
False
,
AnyPropertyType
,
&
xa_type
,
&
format
,
&
len
,
&
fill
,
&
data
)
!=
Success
)
{
return
FALSE
;
}
/* make sure the type and format are good */
if
(
xa_type
==
None
||
format
==
0
)
return
FALSE
;
/* make sure we don't have any leftovers */
if
(
fill
)
return
FALSE
;
/* make sure len divides evenly by 4 */
if
(
len
%
4
)
return
FALSE
;
/* it's good, lets use it */
workareas
=
(
gulong
*
)(
guint32
*
)
data
;
rect
->
x
=
(
guint32
)
workareas
[
current
*
4
];
rect
->
y
=
(
guint32
)
workareas
[
current
*
4
+
1
];
rect
->
width
=
(
guint32
)
workareas
[
current
*
4
+
2
];
rect
->
height
=
(
guint32
)
workareas
[
current
*
4
+
3
];
/* clean up our memory */
XFree
(
data
);
#else
GdkDisplay
*
display
;
GdkScreen
*
screen
;
display
=
gdk_display_get_default
();
screen
=
gdk_display_get_screen
(
display
,
disp_screen
);
gdk_screen_get_monitor_geometry
(
screen
,
disp_monitor
,
rect
);
win32_adjust_workarea
(
rect
);
#endif
/* _WIN32 */
return
TRUE
;
}
/*******************************************************************************
* 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
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
)
{
gaim_debug_info
(
"guifications"
,
"failed to created 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
);
gtk_widget_set_size_request
(
display
->
event
,
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
=
gtk_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
;
}
/* Yes this big ugly function has a purpose...
* It checks for existing notification from the same buddy, or conv with
* with the same target, and condenses contacts.
*
* 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
;
GaimBuddy
*
buddy1
=
NULL
,
*
buddy2
=
NULL
;
GaimContact
*
contact1
=
NULL
,
*
contact2
=
NULL
;
GaimConversation
*
conv1
=
NULL
,
*
conv2
=
NULL
;
GList
*
l
=
NULL
,
*
ll
=
NULL
;
gchar
*
ck1
=
NULL
,
*
ck2
=
NULL
;
const
gchar
*
target1
=
NULL
,
*
target2
=
NULL
;
gboolean
ret
=
FALSE
;
event1
=
gf_event_info_get_event
(
info
);
priority1
=
gf_event_get_priority
(
event1
);
buddy1
=
gf_event_info_get_buddy
(
info
);
conv1
=
gf_event_info_get_conversation
(
info
);
target1
=
gf_event_info_get_target
(
info
);
if
(
buddy1
)
contact1
=
gaim_buddy_get_contact
(
buddy1
);
if
(
target1
)
ck1
=
g_utf8_collate_key
(
target1
,
-1
);
for
(
l
=
displays
;
l
;
l
=
ll
)
{
display
=
GF_DISPLAY
(
l
->
data
);
ll
=
l
->
next
;
event2
=
gf_event_info_get_event
(
display
->
info
);
priority2
=
gf_event_get_priority
(
event2
);
conv2
=
gf_event_info_get_conversation
(
display
->
info
);
if
(
buddy1
)
{
buddy2
=
gf_event_info_get_buddy
(
display
->
info
);
/* check contacts */
if
(
buddy2
)
{
contact2
=
gaim_buddy_get_contact
(
buddy2
);
if
(
contact1
&&
contact1
==
contact2
)
{
ck2
=
g_utf8_collate_key
(
buddy2
->
name
,
-1
);
if
(
!
(
buddy1
->
account
==
buddy2
->
account
&&
!
strcmp
(
ck1
,
ck2
)))
{
if
(
priority1
>=
priority2
)
{
gf_event_info_set_is_contact
(
info
,
TRUE
);
gf_display_destroy
(
display
);
continue
;
}
else
{
ret
=
TRUE
;
break
;
}
}
g_free
(
ck2
);
}
}
/* check for duplicate buddies */
if
(
buddy1
==
buddy2
)
{
if
(
priority1
>=
priority2
)
{
gf_display_destroy
(
display
);
continue
;
}
else
{
ret
=
TRUE
;
break
;
}
}
}
/* check for duplicate targets from the same conv */
if
(
ck1
&&
conv1
&&
conv1
==
conv2
)
{
target2
=
gf_event_info_get_target
(
display
->
info
);
if
(
target2
)
ck2
=
g_utf8_collate_key
(
target2
,
-1
);
if
(
ck2
&&
!
strcmp
(
ck1
,
ck2
))
{
g_free
(
ck2
);
if
(
priority1
>=
priority2
)
{
gf_display_destroy
(
display
);
continue
;
}
else
{
ret
=
TRUE
;
break
;
}
}
}
}
if
(
ck1
)
g_free
(
ck1
);
/* 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
=
gaim_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
;
}
gboolean
gf_display_screen_saver_is_running
()
{
gboolean
ret
=
FALSE
;
#ifndef _WIN32
static
Atom
xss
,
locked
,
blanked
;
static
gboolean
init
=
FALSE
;
Atom
ratom
;
gint
rtatom
;
guchar
*
data
=
NULL
;
gulong
items
,
padding
;
if
(
!
init
)
{
xss
=
XInternAtom
(
GDK_DISPLAY
(),
"_SCREENSAVER_STATUS"
,
FALSE
);
locked
=
XInternAtom
(
GDK_DISPLAY
(),
"LOCK"
,
FALSE
);
blanked
=
XInternAtom
(
GDK_DISPLAY
(),
"BLANK"
,
FALSE
);
init
=
TRUE
;
}
if
(
XGetWindowProperty
(
GDK_DISPLAY
(),
GDK_ROOT_WINDOW
(),
xss
,
0
,
999
,
FALSE
,
XA_INTEGER
,
&
ratom
,
&
rtatom
,
&
items
,
&
padding
,
&
data
)
==
Success
)
{
if
(
ratom
==
XA_INTEGER
||
items
>=
3
)
{
guint
*
item_data
=
(
guint
*
)
data
;
if
(
item_data
[
0
]
==
locked
||
item_data
[
0
]
==
blanked
)
ret
=
TRUE
;
}
XFree
(
data
);
}
#else
if
(
!
SystemParametersInfo
(
SPI_GETSCREENSAVERRUNNING
,
(
unsigned
int
)
NULL
,
&
ret
,
FALSE
))
{
ret
=
FALSE
;
}
#endif
/* _WIN32 */
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
));
gaim_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
);
gaim_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
*
gaim_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
,
GaimPrefType
type
,
gconstpointer
val
,
gpointer
data
)
{
vertical
=
gaim_prefs_get_bool
(
GF_PREF_APPEARANCE_VERTICAL
);
position
=
gaim_prefs_get_int
(
GF_PREF_APPEARANCE_POSITION
);
gf_displays_position
();
}
static
void
gf_display_animate_changed_cb
(
const
gchar
*
name
,
GaimPrefType
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
,
GaimPrefType
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
,
GaimPrefType
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
(
GaimPlugin
*
plugin
)
{
/* since we just use the callbacks to get the changes we need to know
* what these are initially at.
*/
position
=
gaim_prefs_get_int
(
GF_PREF_APPEARANCE_POSITION
);
vertical
=
gaim_prefs_get_bool
(
GF_PREF_APPEARANCE_VERTICAL
);
animate
=
gaim_prefs_get_bool
(
GF_PREF_APPEARANCE_ANIMATE
);
/* Connect our pref callbacks */
pos_chg_id
=
gaim_prefs_connect_callback
(
plugin
,
GF_PREF_APPEARANCE_POSITION
,
gf_display_position_changed_cb
,
NULL
);
ver_chg_id
=
gaim_prefs_connect_callback
(
plugin
,
GF_PREF_APPEARANCE_VERTICAL
,
gf_display_position_changed_cb
,
NULL
);
ani_chg_id
=
gaim_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
=
gaim_prefs_get_int
(
GF_PREF_ADVANCED_SCREEN
);
disp_monitor
=
gaim_prefs_get_int
(
GF_PREF_ADVANCED_MONITOR
);
scr_chg_id
=
gaim_prefs_connect_callback
(
plugin
,
GF_PREF_ADVANCED_SCREEN
,
gf_display_screen_changed_cb
,
NULL
);
mon_chg_id
=
gaim_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
()
{
gaim_prefs_disconnect_callback
(
pos_chg_id
);
gaim_prefs_disconnect_callback
(
ver_chg_id
);
#if GTK_CHECK_VERSION(2,2,0)
gaim_prefs_disconnect_callback
(
scr_chg_id
);
gaim_prefs_disconnect_callback
(
mon_chg_id
);
#endif
/* GTK_CHECK_VERSION(2,2,0) */
}