pidgin/pidgin
Clone
Summary
Browse
Changes
Graph
Restore Insert Horizontal Rule functionality.
2012-06-03, Elliott Sales de Andrade
3a742f00a0f4
Restore Insert Horizontal Rule functionality.
/**
* @file pidgintooltip.c Pidgin Tooltip API
* @ingroup pidgin
*/
/* pidgin
*
* Pidgin is the legal property of its developers, whose names are too numerous
* to list here. Please refer to the COPYRIGHT file distributed with this
* source distribution.
*
* 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
*/
#include
"internal.h"
#include
"prefs.h"
#include
"pidgin.h"
#include
"pidgintooltip.h"
#include
"debug.h"
static
gboolean
enable_tooltips
;
static
int
tooltip_delay
=
-1
;
struct
{
GtkWidget
*
widget
;
int
timeout
;
GdkRectangle
tip_rect
;
GtkWidget
*
tipwindow
;
PidginTooltipPaint
paint_tooltip
;
}
pidgin_tooltip
;
typedef
struct
{
GtkWidget
*
widget
;
gpointer
userdata
;
PidginTooltipPaint
paint_tooltip
;
union
{
struct
{
PidginTooltipCreateForTree
create_tooltip
;
GtkTreePath
*
path
;
}
treeview
;
struct
{
PidginTooltipCreate
create_tooltip
;
}
widget
;
}
common
;
}
PidginTooltipData
;
static
void
initialize_tooltip_delay
()
{
#if GTK_CHECK_VERSION(2,14,0)
GtkSettings
*
settings
;
#endif
if
(
tooltip_delay
!=
-1
)
return
;
#if GTK_CHECK_VERSION(2,14,0)
settings
=
gtk_settings_get_default
();
g_object_get
(
settings
,
"gtk-enable-tooltips"
,
&
enable_tooltips
,
NULL
);
g_object_get
(
settings
,
"gtk-tooltip-timeout"
,
&
tooltip_delay
,
NULL
);
#else
tooltip_delay
=
purple_prefs_get_int
(
PIDGIN_PREFS_ROOT
"/blist/tooltip_delay"
);
enable_tooltips
=
(
tooltip_delay
!=
0
);
#endif
}
static
void
destroy_tooltip_data
(
PidginTooltipData
*
data
)
{
if
(
data
->
common
.
treeview
.
path
)
gtk_tree_path_free
(
data
->
common
.
treeview
.
path
);
pidgin_tooltip_destroy
();
g_free
(
data
);
}
void
pidgin_tooltip_destroy
()
{
if
(
pidgin_tooltip
.
timeout
>
0
)
{
g_source_remove
(
pidgin_tooltip
.
timeout
);
pidgin_tooltip
.
timeout
=
0
;
}
if
(
pidgin_tooltip
.
tipwindow
)
{
gtk_widget_destroy
(
pidgin_tooltip
.
tipwindow
);
pidgin_tooltip
.
tipwindow
=
NULL
;
}
}
static
gboolean
pidgin_tooltip_expose_event
(
GtkWidget
*
widget
,
GdkEventExpose
*
event
,
gpointer
data
)
{
if
(
pidgin_tooltip
.
paint_tooltip
)
{
gtk_paint_flat_box
(
widget
->
style
,
widget
->
window
,
GTK_STATE_NORMAL
,
GTK_SHADOW_OUT
,
NULL
,
widget
,
"tooltip"
,
0
,
0
,
-1
,
-1
);
pidgin_tooltip
.
paint_tooltip
(
widget
,
data
);
}
return
FALSE
;
}
static
GtkWidget
*
setup_tooltip_window
(
void
)
{
const
char
*
name
;
GtkWidget
*
tipwindow
;
tipwindow
=
gtk_window_new
(
GTK_WINDOW_POPUP
);
name
=
gtk_window_get_title
(
GTK_WINDOW
(
pidgin_tooltip
.
widget
));
gtk_window_set_type_hint
(
GTK_WINDOW
(
tipwindow
),
GDK_WINDOW_TYPE_HINT_TOOLTIP
);
gtk_widget_set_app_paintable
(
tipwindow
,
TRUE
);
gtk_window_set_title
(
GTK_WINDOW
(
tipwindow
),
name
?
name
:
_
(
"Pidgin Tooltip"
));
gtk_window_set_resizable
(
GTK_WINDOW
(
tipwindow
),
FALSE
);
gtk_widget_set_name
(
tipwindow
,
"gtk-tooltips"
);
gtk_widget_ensure_style
(
tipwindow
);
gtk_widget_realize
(
tipwindow
);
return
tipwindow
;
}
static
void
setup_tooltip_window_position
(
gpointer
data
,
int
w
,
int
h
)
{
int
sig
;
int
scr_w
,
scr_h
,
x
,
y
,
dy
;
int
preserved_x
,
preserved_y
;
int
mon_num
;
GdkScreen
*
screen
=
NULL
;
GdkRectangle
mon_size
;
GtkWidget
*
tipwindow
=
pidgin_tooltip
.
tipwindow
;
gdk_display_get_pointer
(
gdk_display_get_default
(),
&
screen
,
&
x
,
&
y
,
NULL
);
mon_num
=
gdk_screen_get_monitor_at_point
(
screen
,
x
,
y
);
gdk_screen_get_monitor_geometry
(
screen
,
mon_num
,
&
mon_size
);
scr_w
=
mon_size
.
width
+
mon_size
.
x
;
scr_h
=
mon_size
.
height
+
mon_size
.
y
;
dy
=
gdk_display_get_default_cursor_size
(
gdk_display_get_default
())
/
2
;
if
(
w
>
mon_size
.
width
)
w
=
mon_size
.
width
-
10
;
if
(
h
>
mon_size
.
height
)
h
=
mon_size
.
height
-
10
;
preserved_x
=
x
;
preserved_y
=
y
;
x
-=
((
w
>>
1
)
+
4
);
if
((
y
+
h
+
4
)
>
scr_h
)
y
=
y
-
h
-
dy
-
5
;
else
y
=
y
+
dy
+
6
;
if
(
y
<
mon_size
.
y
)
y
=
mon_size
.
y
;
if
(
y
!=
mon_size
.
y
)
{
if
((
x
+
w
)
>
scr_w
)
x
-=
(
x
+
w
+
5
)
-
scr_w
;
else
if
(
x
<
mon_size
.
x
)
x
=
mon_size
.
x
;
}
else
{
x
-=
(
w
/
2
+
10
);
if
(
x
<
mon_size
.
x
)
x
=
mon_size
.
x
;
}
/* If the mouse covered by the tipwindow, move the tipwindow
* to the righ side of the it */
if
((
preserved_x
>=
x
)
&&
(
preserved_x
<=
(
x
+
w
))
&&
(
preserved_y
>=
y
)
&&
(
preserved_y
<=
(
y
+
h
)))
x
=
preserved_x
+
dy
;
gtk_widget_set_size_request
(
tipwindow
,
w
,
h
);
gtk_window_move
(
GTK_WINDOW
(
tipwindow
),
x
,
y
);
gtk_widget_show
(
tipwindow
);
g_signal_connect
(
G_OBJECT
(
tipwindow
),
"expose_event"
,
G_CALLBACK
(
pidgin_tooltip_expose_event
),
data
);
/* Hide the tooltip when the widget is destroyed */
sig
=
g_signal_connect
(
G_OBJECT
(
pidgin_tooltip
.
widget
),
"destroy"
,
G_CALLBACK
(
pidgin_tooltip_destroy
),
NULL
);
g_signal_connect_swapped
(
G_OBJECT
(
tipwindow
),
"destroy"
,
G_CALLBACK
(
g_source_remove
),
GINT_TO_POINTER
(
sig
));
}
void
pidgin_tooltip_show
(
GtkWidget
*
widget
,
gpointer
userdata
,
PidginTooltipCreate
create_tooltip
,
PidginTooltipPaint
paint_tooltip
)
{
GtkWidget
*
tipwindow
;
int
w
,
h
;
pidgin_tooltip_destroy
();
pidgin_tooltip
.
widget
=
gtk_widget_get_toplevel
(
widget
);
pidgin_tooltip
.
tipwindow
=
tipwindow
=
setup_tooltip_window
();
pidgin_tooltip
.
paint_tooltip
=
paint_tooltip
;
if
(
!
create_tooltip
(
tipwindow
,
userdata
,
&
w
,
&
h
))
{
pidgin_tooltip_destroy
();
return
;
}
setup_tooltip_window_position
(
userdata
,
w
,
h
);
}
static
void
reset_data_treepath
(
PidginTooltipData
*
data
)
{
gtk_tree_path_free
(
data
->
common
.
treeview
.
path
);
data
->
common
.
treeview
.
path
=
NULL
;
}
static
void
pidgin_tooltip_draw
(
PidginTooltipData
*
data
)
{
GtkWidget
*
tipwindow
;
int
w
,
h
;
pidgin_tooltip_destroy
();
pidgin_tooltip
.
widget
=
gtk_widget_get_toplevel
(
data
->
widget
);
pidgin_tooltip
.
tipwindow
=
tipwindow
=
setup_tooltip_window
();
pidgin_tooltip
.
paint_tooltip
=
data
->
paint_tooltip
;
if
(
!
data
->
common
.
widget
.
create_tooltip
(
tipwindow
,
data
->
userdata
,
&
w
,
&
h
))
{
if
(
tipwindow
==
pidgin_tooltip
.
tipwindow
)
pidgin_tooltip_destroy
();
return
;
}
setup_tooltip_window_position
(
data
->
userdata
,
w
,
h
);
}
static
void
pidgin_tooltip_draw_tree
(
PidginTooltipData
*
data
)
{
GtkWidget
*
tipwindow
;
GtkTreePath
*
path
=
NULL
;
int
w
,
h
;
if
(
!
gtk_tree_view_get_path_at_pos
(
GTK_TREE_VIEW
(
data
->
widget
),
pidgin_tooltip
.
tip_rect
.
x
,
pidgin_tooltip
.
tip_rect
.
y
+
(
pidgin_tooltip
.
tip_rect
.
height
/
2
),
&
path
,
NULL
,
NULL
,
NULL
))
{
pidgin_tooltip_destroy
();
return
;
}
if
(
data
->
common
.
treeview
.
path
)
{
if
(
gtk_tree_path_compare
(
data
->
common
.
treeview
.
path
,
path
)
==
0
)
{
gtk_tree_path_free
(
path
);
return
;
}
gtk_tree_path_free
(
data
->
common
.
treeview
.
path
);
data
->
common
.
treeview
.
path
=
NULL
;
}
pidgin_tooltip_destroy
();
pidgin_tooltip
.
widget
=
gtk_widget_get_toplevel
(
data
->
widget
);
pidgin_tooltip
.
tipwindow
=
tipwindow
=
setup_tooltip_window
();
pidgin_tooltip
.
paint_tooltip
=
data
->
paint_tooltip
;
if
(
!
data
->
common
.
treeview
.
create_tooltip
(
tipwindow
,
path
,
data
->
userdata
,
&
w
,
&
h
))
{
if
(
tipwindow
==
pidgin_tooltip
.
tipwindow
)
pidgin_tooltip_destroy
();
gtk_tree_path_free
(
path
);
return
;
}
setup_tooltip_window_position
(
data
->
userdata
,
w
,
h
);
data
->
common
.
treeview
.
path
=
path
;
g_signal_connect_swapped
(
G_OBJECT
(
pidgin_tooltip
.
tipwindow
),
"destroy"
,
G_CALLBACK
(
reset_data_treepath
),
data
);
}
static
gboolean
pidgin_tooltip_timeout
(
gpointer
data
)
{
PidginTooltipData
*
tdata
=
data
;
pidgin_tooltip
.
timeout
=
0
;
if
(
GTK_IS_TREE_VIEW
(
tdata
->
widget
))
pidgin_tooltip_draw_tree
(
data
);
else
pidgin_tooltip_draw
(
data
);
return
FALSE
;
}
static
gboolean
row_motion_cb
(
GtkWidget
*
tv
,
GdkEventMotion
*
event
,
gpointer
userdata
)
{
GtkTreePath
*
path
;
if
(
event
->
window
!=
gtk_tree_view_get_bin_window
(
GTK_TREE_VIEW
(
tv
)))
return
FALSE
;
/* The cursor is probably on the TreeView's header. */
initialize_tooltip_delay
();
if
(
!
enable_tooltips
)
return
FALSE
;
if
(
pidgin_tooltip
.
timeout
)
{
if
((
event
->
y
>=
pidgin_tooltip
.
tip_rect
.
y
)
&&
((
event
->
y
-
pidgin_tooltip
.
tip_rect
.
height
)
<=
pidgin_tooltip
.
tip_rect
.
y
))
return
FALSE
;
/* We've left the cell. Remove the timeout and create a new one below */
pidgin_tooltip_destroy
();
}
gtk_tree_view_get_path_at_pos
(
GTK_TREE_VIEW
(
tv
),
event
->
x
,
event
->
y
,
&
path
,
NULL
,
NULL
,
NULL
);
if
(
path
==
NULL
)
{
pidgin_tooltip_destroy
();
return
FALSE
;
}
gtk_tree_view_get_cell_area
(
GTK_TREE_VIEW
(
tv
),
path
,
NULL
,
&
pidgin_tooltip
.
tip_rect
);
gtk_tree_path_free
(
path
);
pidgin_tooltip
.
timeout
=
g_timeout_add
(
tooltip_delay
,
(
GSourceFunc
)
pidgin_tooltip_timeout
,
userdata
);
return
FALSE
;
}
static
gboolean
widget_leave_cb
(
GtkWidget
*
tv
,
GdkEvent
*
event
,
gpointer
userdata
)
{
pidgin_tooltip_destroy
();
return
FALSE
;
}
gboolean
pidgin_tooltip_setup_for_treeview
(
GtkWidget
*
tree
,
gpointer
userdata
,
PidginTooltipCreateForTree
create_tooltip
,
PidginTooltipPaint
paint_tooltip
)
{
PidginTooltipData
*
tdata
=
g_new0
(
PidginTooltipData
,
1
);
tdata
->
widget
=
tree
;
tdata
->
userdata
=
userdata
;
tdata
->
common
.
treeview
.
create_tooltip
=
create_tooltip
;
tdata
->
paint_tooltip
=
paint_tooltip
;
g_signal_connect
(
G_OBJECT
(
tree
),
"motion-notify-event"
,
G_CALLBACK
(
row_motion_cb
),
tdata
);
g_signal_connect
(
G_OBJECT
(
tree
),
"leave-notify-event"
,
G_CALLBACK
(
widget_leave_cb
),
NULL
);
g_signal_connect
(
G_OBJECT
(
tree
),
"scroll-event"
,
G_CALLBACK
(
widget_leave_cb
),
NULL
);
g_signal_connect_swapped
(
G_OBJECT
(
tree
),
"destroy"
,
G_CALLBACK
(
destroy_tooltip_data
),
tdata
);
return
TRUE
;
}
static
gboolean
widget_motion_cb
(
GtkWidget
*
widget
,
GdkEvent
*
event
,
gpointer
data
)
{
initialize_tooltip_delay
();
pidgin_tooltip_destroy
();
if
(
!
enable_tooltips
)
return
FALSE
;
pidgin_tooltip
.
timeout
=
g_timeout_add
(
tooltip_delay
,
(
GSourceFunc
)
pidgin_tooltip_timeout
,
data
);
return
FALSE
;
}
gboolean
pidgin_tooltip_setup_for_widget
(
GtkWidget
*
widget
,
gpointer
userdata
,
PidginTooltipCreate
create_tooltip
,
PidginTooltipPaint
paint_tooltip
)
{
PidginTooltipData
*
wdata
=
g_new0
(
PidginTooltipData
,
1
);
wdata
->
widget
=
widget
;
wdata
->
userdata
=
userdata
;
wdata
->
common
.
widget
.
create_tooltip
=
create_tooltip
;
wdata
->
paint_tooltip
=
paint_tooltip
;
g_signal_connect
(
G_OBJECT
(
widget
),
"motion-notify-event"
,
G_CALLBACK
(
widget_motion_cb
),
wdata
);
g_signal_connect
(
G_OBJECT
(
widget
),
"leave-notify-event"
,
G_CALLBACK
(
widget_leave_cb
),
NULL
);
g_signal_connect
(
G_OBJECT
(
widget
),
"scroll-event"
,
G_CALLBACK
(
widget_leave_cb
),
NULL
);
g_signal_connect_swapped
(
G_OBJECT
(
widget
),
"destroy"
,
G_CALLBACK
(
destroy_tooltip_data
),
wdata
);
return
TRUE
;
}