#include "usertooltips.h"
userTooltips::userTooltips()
{
attached = NULL;
delay = 1000;
wnd = NULL;
lastUser = NULL;
}
userTooltips::~userTooltips()
{
GList *search;
attachedWidgets *att;
for (search = attached; search; search = search->next)
{
att = (attachedWidgets*)search->data;
g_signal_handlers_disconnect_by_func(att->w, (gpointer)this->cb_tooltipLeaveNotify, this);
g_signal_handlers_disconnect_by_func(att->w, (gpointer)this->cb_tooltipMoveNotify, this);
delete att;
break;
}
g_list_free(attached);
if (wnd)
gtk_widget_destroy(wnd);
if (timeoutSource)
g_source_remove(timeoutSource);
}
void userTooltips::attachWidget(GtkWidget* w, UTWidgetFunc func, gpointer data)
{
attachedWidgets *att;
att = new attachedWidgets;
att->w = w;
att->func = func;
att->data = data;
attached = g_list_append(attached, att);
// connect all neccessary functions
g_signal_connect_swapped(w, "leave-notify-event", G_CALLBACK(this->cb_tooltipLeaveNotify), this);
g_signal_connect(w, "motion-notify-event", G_CALLBACK(this->cb_tooltipMoveNotify), this);
gtk_widget_add_events(w, (GdkEventMask)(GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK));
}
void userTooltips::detachWidget(GtkWidget* w)
{
GList *search;
attachedWidgets *att;
for (search = attached; search; search = search->next)
{
att = (attachedWidgets*)search->data;
if (att->w == w)
{
g_signal_handlers_disconnect_by_func(w, (gpointer)this->cb_tooltipLeaveNotify, this);
g_signal_handlers_disconnect_by_func(w, (gpointer)this->cb_tooltipMoveNotify, this);
attached = g_list_remove(attached, att);
delete att;
break;
}
}
}
void userTooltips::setPopupDelay(gint newDelay)
{
delay = newDelay;
}
// -----------------------------------------------------------------------------
gboolean userTooltips::cb_tooltipLeaveNotify(userTooltips* self, GdkEventCrossing* event)
{
GtkWidget *newWindow;
GList *children;
gint posX, posY;
if (self->wnd)
{
// here comes the tricky part. If a user presses CTRL while going to the tooltip
// it gets managed so information can be copied
if (event->state & GDK_CONTROL_MASK)
{
newWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_widget_set_app_paintable(newWindow, TRUE);
gtk_widget_set_name(newWindow, "gtk-tooltips");
gtk_window_set_resizable(GTK_WINDOW(newWindow), FALSE);
gtk_window_set_skip_taskbar_hint(GTK_WINDOW(newWindow), TRUE);
gtk_container_set_border_width(GTK_CONTAINER(newWindow), 10);
children = gtk_container_get_children(GTK_CONTAINER(self->wnd));
gtk_widget_reparent(GTK_WIDGET(children->data), newWindow);
g_list_free(children);
gtk_window_get_position(GTK_WINDOW(self->wnd), &posX, &posY);
gtk_window_move(GTK_WINDOW(newWindow), posX, posY);
gtk_widget_show_all(newWindow);
}
gtk_widget_destroy(self->wnd);
self->wnd = NULL;
}
if (self->timeoutSource)
{
g_source_remove(self->timeoutSource);
self->timeoutSource = 0;
}
self->lastUser = NULL;
return FALSE;
}
gboolean userTooltips::cb_tooltipMoveNotify(GtkWidget* w, GdkEventMotion *event, userTooltips* self)
{
GList *search;
attachedWidgets *att;
IMUserDaemon *user;
user = NULL;
for (search = self->attached; search; search = search->next)
{
att = (attachedWidgets*)search->data;
if (att->w == w)
{
user = att->func(w, event, att->data);
break;
}
}
// no user we are pointing to? destroy the window
if (!user)
{
if (self->wnd)
{
gtk_widget_destroy(self->wnd);
self->wnd = NULL;
}
if (self->timeoutSource)
{
g_source_remove(self->timeoutSource);
self->timeoutSource = 0;
}
self->lastUser = NULL;
return FALSE;
}
// a new user -> create the tooltip
if (user != self->lastUser)
{
if (self->wnd)
{
gtk_widget_destroy(self->wnd);
self->wnd = NULL;
}
if (self->timeoutSource)
{
g_source_remove(self->timeoutSource);
self->timeoutSource = 0;
}
self->timeoutSource = g_timeout_add(self->delay, (GSourceFunc)self->cb_tooltipShowTimeout, self);
self->lastUser = user;
}
return FALSE;
}
#define MW_MAKE_TT_ITEM(txt, c) \
tmp3 = g_strdup_printf("%s: %%s", txt); \
tmp = g_strdup_printf(tmp3, c); \
label2 = gtk_label_new(tmp); \
gtk_label_set_selectable(GTK_LABEL(label2), TRUE); \
gtk_label_set_use_markup(GTK_LABEL(label2), TRUE); \
gtk_misc_set_alignment(GTK_MISC(label2), 0.0f, 0.0f); \
gtk_box_pack_start(GTK_BOX(vbox1), label2, FALSE, TRUE, 0); \
g_free(tmp); \
g_free(tmp3);
gboolean userTooltips::cb_tooltipShowTimeout(userTooltips* self)
{
GtkWidget *hbox1,
*vbox1,
*vbox2,
*label1,
*label2,
*label3,
*img,
*alignment1,
*alignment2;
gchar *tmp,
*tmp2,
*tmp3;
GString *realName;
IMAutoResponseManager *aman;
gulong tooltipsMask;
// get the mask
settings_getSettings()->getProperties("contactlist",
"tooltipsMask", &tooltipsMask,
NULL);
// create the tooltip window
self->wnd = gtk_window_new(GTK_WINDOW_POPUP);
gtk_widget_set_app_paintable(self->wnd, TRUE);
gtk_container_set_border_width(GTK_CONTAINER(self->wnd), 10);
g_signal_connect_swapped(self->wnd, "expose-event", G_CALLBACK(self->cb_tooltipPaint), self);
g_signal_connect_swapped(self->wnd, "realize", G_CALLBACK(self->cb_tooltipRealize), self);
gtk_widget_ensure_style(self->wnd);
gtk_widget_set_name(self->wnd, "gtk-tooltips");
// clear the old content
gtk_container_foreach(GTK_CONTAINER(self->wnd), (GtkCallback)(gtk_widget_destroy), NULL);
// fill the new content
if (self->lastUser)
{
vbox1 = gtk_vbox_new(FALSE, 4);
// the status image
img = gtk_image_new_from_pixbuf(i_getIcons()->getUserStatusPixbuf(self->lastUser));
alignment1 = gtk_alignment_new(0.0f, 0.0f, 0.0f, 0.0f);
gtk_alignment_set_padding(GTK_ALIGNMENT(alignment1), 5, 0, 0, 0);
gtk_container_add(GTK_CONTAINER(alignment1), img);
// the relevant information
tmp = g_strdup_printf("%s", self->lastUser->info->alias);
label1 = gtk_label_new(tmp);
gtk_label_set_selectable(GTK_LABEL(label1), TRUE);
gtk_label_set_use_markup(GTK_LABEL(label1), TRUE);
gtk_misc_set_alignment(GTK_MISC(label1), 0.0f, 0.0f);
g_free(tmp);
// insert the real name
if (tooltipsMask & TT_REALNAME)
{
realName = g_string_new(self->lastUser->info->firstname);
if (strlen(realName->str))
realName = g_string_append(realName, " ");
realName = g_string_append(realName, self->lastUser->info->lastname);
MW_MAKE_TT_ITEM(tr("Real Name"), realName->str);
g_string_free(realName, TRUE);
}
// insert the status
if (tooltipsMask & TT_STATUS)
{
if (self->lastUser->info->isInvisible)
tmp2 = g_strdup_printf("%s (Invisible)", uu_getStatusDescription(self->lastUser->info->status));
else
tmp2 = g_strdup(uu_getStatusDescription(self->lastUser->info->status));
MW_MAKE_TT_ITEM(tr("Status"), tmp2);
g_free(tmp2);
}
// insert the user id
if (tooltipsMask & TT_ID)
{
MW_MAKE_TT_ITEM(tr("User ID"), self->lastUser->info->formattedID);
}
// insert the ip address
if (tooltipsMask & TT_IP)
{
MW_MAKE_TT_ITEM(tr("IP Address"), self->lastUser->info->formattedIP);
}
// insert the protocol name
if (tooltipsMask & TT_PROTOCOL)
{
MW_MAKE_TT_ITEM(tr("Protocol"), ((IMOwnerDaemon*)self->lastUser->owner)->protocol->name);
}
// insert the client name
if (tooltipsMask & TT_CLIENTNAME) {
MW_MAKE_TT_ITEM(tr("Client-Name"), self->lastUser->info->clientName);
}
// insert the email
if (tooltipsMask & TT_EMAIL && (*self->lastUser->info->email1 || *self->lastUser->info->email2 || *self->lastUser->info->email3))
{
if (*self->lastUser->info->email1)
tmp2 = self->lastUser->info->email1;
else
if (*self->lastUser->info->email2)
tmp2 = self->lastUser->info->email2;
else
tmp2 = self->lastUser->info->email3;
label2 = gtk_label_new(tmp2);
tmp = g_strdup_printf("%s", tr("Email: "));
label3 = gtk_label_new(tmp);
g_free(tmp);
gtk_label_set_use_markup(GTK_LABEL(label3), TRUE);
gtk_misc_set_alignment(GTK_MISC(label3), 0.0f, 0.0f);
gtk_label_set_selectable(GTK_LABEL(label3), TRUE);
hbox1 = gtk_hbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(hbox1), label3, FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(hbox1), label2, FALSE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(vbox1), hbox1, FALSE, TRUE, 0);
}
// insert the online time
if (tooltipsMask & TT_ONLINETIME && self->lastUser->info->status != BUDDY_STATUS_OFFLINE)
{
tmp2 = u_getTimeDistance(time(NULL), self->lastUser->info->stats.onlineSince);
MW_MAKE_TT_ITEM(tr("Online for"), tmp2);
g_free(tmp2);
}
// insert the IDLE time
if (tooltipsMask & TT_IDLETIME && self->lastUser->info->status != BUDDY_STATUS_OFFLINE && self->lastUser->info->stats.idleSince)
{
tmp2 = u_getTimeDistance(time(NULL), self->lastUser->info->stats.idleSince);
MW_MAKE_TT_ITEM(tr("IDLE for"), tmp2);
g_free(tmp2);
}
// insert the auto response
if (tooltipsMask & TT_AUTORESPONSE && self->lastUser->info->status != BUDDY_STATUS_OFFLINE && self->lastUser->info->status != BUDDY_STATUS_ONLINE)
{
aman = new IMAutoResponseManager(self->lastUser->info, TRUE);
self->lastUser->addManager(aman);
tmp = g_strdup_printf("%s", tr("Auto Response: "));
label2 = gtk_label_new(tmp);
g_free(tmp);
gtk_label_set_use_markup(GTK_LABEL(label2), TRUE);
gtk_misc_set_alignment(GTK_MISC(label2), 0.0f, 0.0f);
alignment2 = gtk_alignment_new(0.0f, 0.0f, 1.0f, 1.0f);
if (tooltipsMask != TT_AUTORESPONSE)
gtk_alignment_set_padding(GTK_ALIGNMENT(alignment2), 5, 0, 0, 0);
gtk_container_add(GTK_CONTAINER(alignment2), label2);
tmp = aman->getCustomResponse();
label3 = gtk_label_new(tmp);
g_free(tmp);
gtk_label_set_selectable(GTK_LABEL(label3), TRUE);
gtk_label_set_line_wrap(GTK_LABEL(label3), TRUE);
gtk_misc_set_alignment(GTK_MISC(label3), 0.0f, 0.0f);
gtk_box_pack_start(GTK_BOX(vbox1), alignment2, FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(vbox1), label3, FALSE, TRUE, 0);
delete aman;
}
// pack the vbox and the icon into a hbox
hbox1 = gtk_hbox_new(FALSE, 5);
gtk_box_pack_start(GTK_BOX(hbox1), alignment1, FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(hbox1), vbox1, TRUE, TRUE, 0);
// now add the headline
vbox2 = gtk_vbox_new(FALSE, 6);
gtk_box_pack_start(GTK_BOX(vbox2), label1, FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(vbox2), hbox1, TRUE, TRUE, 0);
gtk_container_add(GTK_CONTAINER(self->wnd), vbox2);
}
// show the window
gtk_widget_show_all(self->wnd);
self->timeoutSource = 0;
return FALSE;
}
gboolean userTooltips::cb_tooltipPaint(userTooltips* self)
{
GtkRequisition req;
gtk_widget_size_request(self->wnd, &req);
gtk_paint_flat_box(self->wnd->style,
self->wnd->window,
GTK_STATE_NORMAL,
GTK_SHADOW_OUT,
NULL,
self->wnd,
"tooltip",
0, 0,
req.width, req.height);
return FALSE;
}
void userTooltips::cb_tooltipRealize(userTooltips* self)
{
gint posX,
posY,
maxX,
maxY;
GdkScreen *scr;
GtkRequisition req;
// positioning cannot be done before the content is realized, because
// the size of all children is not known. Do this after the realization of
// the window.
scr = gdk_screen_get_default();
gdk_display_get_pointer(gdk_display_get_default(), &scr, &posX, &posY, NULL);
// move the window to the right position
maxX = gdk_screen_get_width(scr);
maxY = gdk_screen_get_height(scr);
gtk_widget_size_request(self->wnd, &req);
if (posX + req.width > maxX)
posX-= req.width+2;
if (posY + req.height > maxY)
posY-= req.height+2;
gtk_window_move(GTK_WINDOW(self->wnd),posX+1,posY+1);
}