/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* Copyright (C) 2002-2007 Imendio AB
- * Copyright (C) 2007 Collabora Ltd.
+ * Copyright (C) 2007-2008 Collabora Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* Jeroen Zwartepoorte
*/
-#include <string.h>
+#include <config.h>
+#include <string.h>
+#include <X11/Xatom.h>
+#include <gdk/gdkx.h>
#include <glib/gi18n.h>
#include <gtk/gtk.h>
+#include <gio/gio.h>
#include <glade/glade.h>
-#include <libgnomevfs/gnome-vfs-utils.h>
#include <libmissioncontrol/mc-profile.h>
-#include <libempathy/empathy-debug.h>
-
#include "empathy-ui-utils.h"
#include "empathy-images.h"
-#define DEBUG_DOMAIN "UiUtils"
+#define DEBUG_FLAG EMPATHY_DEBUG_OTHER
+#include <libempathy/empathy-debug.h>
struct SizeData {
gint width;
const gchar *first_required_widget,
va_list args)
{
- gchar *path;
GladeXML *gui;
const char *name;
GtkWidget **widget_ptr;
- path = g_build_filename (DATADIR, "empathy", filename, NULL);
- gui = glade_xml_new (path, root, domain);
- g_free (path);
+ DEBUG ("Loading glade file %s", filename);
+
+ gui = glade_xml_new (filename, root, domain);
if (!gui) {
g_warning ("Couldn't find necessary glade file '%s'", filename);
- return NULL;
}
for (name = first_required_widget; name; name = va_arg (args, char *)) {
va_end (args);
- if (!gui) {
- return;
+ if (gui) {
+ g_object_unref (gui);
}
-
- g_object_unref (gui);
}
GladeXML *
va_end (args);
}
-GdkPixbuf *
-empathy_pixbuf_from_icon_name (const gchar *icon_name,
- GtkIconSize icon_size)
-{
- GtkIconTheme *theme;
- GdkPixbuf *pixbuf = NULL;
- GError *error = NULL;
- gint w, h;
- gint size = 48;
-
- theme = gtk_icon_theme_get_default ();
-
- if (gtk_icon_size_lookup (icon_size, &w, &h)) {
- size = (w + h) / 2;
- }
-
- pixbuf = gtk_icon_theme_load_icon (theme,
- icon_name,
- size,
- 0,
- &error);
- if (error) {
- empathy_debug (DEBUG_DOMAIN, "Error loading icon: %s", error->message);
- g_clear_error (&error);
- }
-
- return pixbuf;
-}
-
-GdkPixbuf *
-empathy_pixbuf_from_smiley (EmpathySmiley type,
- GtkIconSize icon_size)
-{
- const gchar *icon_id;
-
- switch (type) {
- case EMPATHY_SMILEY_NORMAL: /* :) */
- icon_id = "stock_smiley-1";
- break;
- case EMPATHY_SMILEY_WINK: /* ;) */
- icon_id = "stock_smiley-3";
- break;
- case EMPATHY_SMILEY_BIGEYE: /* =) */
- icon_id = "stock_smiley-2";
- break;
- case EMPATHY_SMILEY_NOSE: /* :-) */
- icon_id = "stock_smiley-7";
- break;
- case EMPATHY_SMILEY_CRY: /* :'( */
- icon_id = "stock_smiley-11";
- break;
- case EMPATHY_SMILEY_SAD: /* :( */
- icon_id = "stock_smiley-4";
- break;
- case EMPATHY_SMILEY_SCEPTICAL: /* :/ */
- icon_id = "stock_smiley-9";
- break;
- case EMPATHY_SMILEY_BIGSMILE: /* :D */
- icon_id = "stock_smiley-6";
- break;
- case EMPATHY_SMILEY_INDIFFERENT: /* :| */
- icon_id = "stock_smiley-8";
- break;
- case EMPATHY_SMILEY_TOUNGE: /* :p */
- icon_id = "stock_smiley-10";
- break;
- case EMPATHY_SMILEY_SHOCKED: /* :o */
- icon_id = "stock_smiley-5";
- break;
- case EMPATHY_SMILEY_COOL: /* 8) */
- icon_id = "stock_smiley-15";
- break;
- case EMPATHY_SMILEY_SORRY: /* *| */
- icon_id = "stock_smiley-12";
- break;
- case EMPATHY_SMILEY_KISS: /* :* */
- icon_id = "stock_smiley-13";
- break;
- case EMPATHY_SMILEY_SHUTUP: /* :# */
- icon_id = "stock_smiley-14";
- break;
- case EMPATHY_SMILEY_YAWN: /* |O */
- icon_id = "";
- break;
- case EMPATHY_SMILEY_CONFUSED: /* :$ */
- icon_id = "stock_smiley-17";
- break;
- case EMPATHY_SMILEY_ANGEL: /* O) */
- icon_id = "stock_smiley-18";
- break;
- case EMPATHY_SMILEY_OOOH: /* :x */
- icon_id = "stock_smiley-19";
- break;
- case EMPATHY_SMILEY_LOOKAWAY: /* *) */
- icon_id = "stock_smiley-20";
- break;
- case EMPATHY_SMILEY_BLUSH: /* *S */
- icon_id = "stock_smiley-23";
- break;
- case EMPATHY_SMILEY_COOLBIGSMILE: /* 8D */
- icon_id = "stock_smiley-25";
- break;
- case EMPATHY_SMILEY_ANGRY: /* :@ */
- icon_id = "stock_smiley-16";
- break;
- case EMPATHY_SMILEY_BOSS: /* @) */
- icon_id = "stock_smiley-21";
- break;
- case EMPATHY_SMILEY_MONKEY: /* #) */
- icon_id = "stock_smiley-22";
- break;
- case EMPATHY_SMILEY_SILLY: /* O) */
- icon_id = "stock_smiley-24";
- break;
- case EMPATHY_SMILEY_SICK: /* +o( */
- icon_id = "stock_smiley-26";
- break;
-
- default:
- g_assert_not_reached ();
- icon_id = NULL;
- }
-
-
- return empathy_pixbuf_from_icon_name (icon_id, icon_size);
-}
-
const gchar *
empathy_icon_name_from_account (McAccount *account)
{
}
const gchar *
-empathy_icon_name_for_presence_state (McPresence state)
+empathy_icon_name_for_presence (McPresence presence)
{
- switch (state) {
+ switch (presence) {
case MC_PRESENCE_AVAILABLE:
return EMPATHY_IMAGE_AVAILABLE;
case MC_PRESENCE_DO_NOT_DISTURB:
case MC_PRESENCE_EXTENDED_AWAY:
return EMPATHY_IMAGE_EXT_AWAY;
case MC_PRESENCE_HIDDEN:
+ return EMPATHY_IMAGE_HIDDEN;
case MC_PRESENCE_OFFLINE:
case MC_PRESENCE_UNSET:
return EMPATHY_IMAGE_OFFLINE;
return NULL;
}
-const gchar *
-empathy_icon_name_for_presence (EmpathyPresence *presence)
-{
- McPresence state;
-
- g_return_val_if_fail (EMPATHY_IS_PRESENCE (presence),
- EMPATHY_IMAGE_OFFLINE);
-
- state = empathy_presence_get_state (presence);
-
- return empathy_icon_name_for_presence_state (state);
-}
-
const gchar *
empathy_icon_name_for_contact (EmpathyContact *contact)
{
- EmpathyPresence *presence;
- EmpathySubscription subscription;
+ McPresence presence;
g_return_val_if_fail (EMPATHY_IS_CONTACT (contact),
EMPATHY_IMAGE_OFFLINE);
presence = empathy_contact_get_presence (contact);
- if (presence) {
- return empathy_icon_name_for_presence (presence);
- }
-
- subscription = empathy_contact_get_subscription (contact);
- if (!(subscription & EMPATHY_SUBSCRIPTION_FROM)) {
- return EMPATHY_IMAGE_PENDING;
- }
-
- return EMPATHY_IMAGE_OFFLINE;
+ return empathy_icon_name_for_presence (presence);
}
GdkPixbuf *
-empathy_pixbuf_avatar_from_contact (EmpathyContact *contact)
+empathy_pixbuf_from_data (gchar *data,
+ gsize data_size)
{
- GdkPixbuf *pixbuf;
- GdkPixbufLoader *loader;
- EmpathyAvatar *avatar;
+ GdkPixbufLoader *loader;
+ GdkPixbuf *pixbuf = NULL;
GError *error = NULL;
- g_return_val_if_fail (EMPATHY_IS_CONTACT (contact), NULL);
-
- avatar = empathy_contact_get_avatar (contact);
- if (!avatar) {
+ if (!data) {
return NULL;
}
loader = gdk_pixbuf_loader_new ();
-
- if (!gdk_pixbuf_loader_write (loader, avatar->data, avatar->len, &error)) {
- g_warning ("Couldn't write avatar image:%p with "
- "length:%" G_GSIZE_FORMAT " to pixbuf loader: %s",
- avatar->data, avatar->len, error->message);
- g_error_free (error);
+ if (!gdk_pixbuf_loader_write (loader, data, data_size, &error)) {
+ DEBUG ("Failed to write to pixbuf loader: %s",
+ error ? error->message : "No error given");
+ g_clear_error (&error);
+ g_object_unref (loader);
+ return NULL;
+ }
+ if (!gdk_pixbuf_loader_close (loader, &error)) {
+ DEBUG ("Failed to close pixbuf loader: %s",
+ error ? error->message : "No error given");
+ g_clear_error (&error);
+ g_object_unref (loader);
return NULL;
}
-
- gdk_pixbuf_loader_close (loader, NULL);
pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
+ if (pixbuf) {
+ g_object_ref (pixbuf);
+ }
- g_object_ref (pixbuf);
g_object_unref (loader);
return pixbuf;
gdk_pixbuf_loader_set_size (loader, width, height);
}
+static void
+empathy_avatar_pixbuf_roundify (GdkPixbuf *pixbuf)
+{
+ gint width, height, rowstride;
+ guchar *pixels;
+
+ width = gdk_pixbuf_get_width (pixbuf);
+ height = gdk_pixbuf_get_height (pixbuf);
+ rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+ pixels = gdk_pixbuf_get_pixels (pixbuf);
+
+ if (width < 6 || height < 6) {
+ return;
+ }
+
+ /* Top left */
+ pixels[3] = 0;
+ pixels[7] = 0x80;
+ pixels[11] = 0xC0;
+ pixels[rowstride + 3] = 0x80;
+ pixels[rowstride * 2 + 3] = 0xC0;
+
+ /* Top right */
+ pixels[width * 4 - 1] = 0;
+ pixels[width * 4 - 5] = 0x80;
+ pixels[width * 4 - 9] = 0xC0;
+ pixels[rowstride + (width * 4) - 1] = 0x80;
+ pixels[(2 * rowstride) + (width * 4) - 1] = 0xC0;
+
+ /* Bottom left */
+ pixels[(height - 1) * rowstride + 3] = 0;
+ pixels[(height - 1) * rowstride + 7] = 0x80;
+ pixels[(height - 1) * rowstride + 11] = 0xC0;
+ pixels[(height - 2) * rowstride + 3] = 0x80;
+ pixels[(height - 3) * rowstride + 3] = 0xC0;
+
+ /* Bottom right */
+ pixels[height * rowstride - 1] = 0;
+ pixels[(height - 1) * rowstride - 1] = 0x80;
+ pixels[(height - 2) * rowstride - 1] = 0xC0;
+ pixels[height * rowstride - 5] = 0x80;
+ pixels[height * rowstride - 9] = 0xC0;
+}
+
+static gboolean
+empathy_gdk_pixbuf_is_opaque (GdkPixbuf *pixbuf)
+{
+ gint width, height, rowstride, i;
+ guchar *pixels;
+ guchar *row;
+
+ width = gdk_pixbuf_get_width (pixbuf);
+ height = gdk_pixbuf_get_height (pixbuf);
+ rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+ pixels = gdk_pixbuf_get_pixels (pixbuf);
+
+ row = pixels;
+ for (i = 3; i < rowstride; i+=4) {
+ if (row[i] < 0xfe) {
+ return FALSE;
+ }
+ }
+
+ for (i = 1; i < height - 1; i++) {
+ row = pixels + (i*rowstride);
+ if (row[3] < 0xfe || row[rowstride-1] < 0xfe) {
+ return FALSE;
+ }
+ }
+
+ row = pixels + ((height-1) * rowstride);
+ for (i = 3; i < rowstride; i+=4) {
+ if (row[i] < 0xfe) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
GdkPixbuf *
empathy_pixbuf_from_avatar_scaled (EmpathyAvatar *avatar,
gint width,
gdk_pixbuf_loader_close (loader, NULL);
pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
+ if (!gdk_pixbuf_get_has_alpha (pixbuf)) {
+ GdkPixbuf *rounded_pixbuf;
+
+ rounded_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
+ gdk_pixbuf_get_width (pixbuf),
+ gdk_pixbuf_get_height (pixbuf));
+ gdk_pixbuf_copy_area (pixbuf, 0, 0,
+ gdk_pixbuf_get_width (pixbuf),
+ gdk_pixbuf_get_height (pixbuf),
+ rounded_pixbuf,
+ 0, 0);
+ pixbuf = rounded_pixbuf;
+ } else {
+ g_object_ref (pixbuf);
+ }
+
+ if (empathy_gdk_pixbuf_is_opaque (pixbuf)) {
+ empathy_avatar_pixbuf_roundify (pixbuf);
+ }
- g_object_ref (pixbuf);
g_object_unref (loader);
return pixbuf;
return empathy_pixbuf_from_avatar_scaled (avatar, width, height);
}
+
+GdkPixbuf *
+empathy_pixbuf_scale_down_if_necessary (GdkPixbuf *pixbuf, gint max_size)
+{
+ gint width, height;
+ gdouble factor;
+
+ width = gdk_pixbuf_get_width (pixbuf);
+ height = gdk_pixbuf_get_height (pixbuf);
+
+ if (width > max_size || height > max_size) {
+ factor = (gdouble) max_size / MAX (width, height);
+
+ width = width * factor;
+ height = height * factor;
+
+ return gdk_pixbuf_scale_simple (pixbuf,
+ width, height,
+ GDK_INTERP_HYPER);
+ }
+
+ return g_object_ref (pixbuf);
+}
+
+GdkPixbuf *
+empathy_pixbuf_from_icon_name (const gchar *icon_name,
+ GtkIconSize icon_size)
+{
+ GtkIconTheme *theme;
+ GdkPixbuf *pixbuf = NULL;
+ GError *error = NULL;
+ gint w, h;
+ gint size = 48;
+
+ if (!icon_name) {
+ return NULL;
+ }
+
+ theme = gtk_icon_theme_get_default ();
+
+ if (gtk_icon_size_lookup (icon_size, &w, &h)) {
+ size = (w + h) / 2;
+ }
+
+ pixbuf = gtk_icon_theme_load_icon (theme,
+ icon_name,
+ size,
+ 0,
+ &error);
+ if (error) {
+ DEBUG ("Error loading icon: %s", error->message);
+ g_clear_error (&error);
+ }
+
+ return pixbuf;
+}
+
/* Stolen from GtkSourceView, hence the weird intendation. Please keep it like
* that to make it easier to apply changes from the original code.
*/
return retval;
}
-static gboolean
-window_get_is_on_current_workspace (GtkWindow *window)
+gboolean
+empathy_window_get_is_visible (GtkWindow *window)
{
- GdkWindow *gdk_window;
+ GdkWindowState state;
+ GdkWindow *gdk_window;
+
+ g_return_val_if_fail (GTK_IS_WINDOW (window), FALSE);
gdk_window = GTK_WIDGET (window)->window;
- if (gdk_window) {
- return !(gdk_window_get_state (gdk_window) &
- GDK_WINDOW_STATE_ICONIFIED);
- } else {
+ if (!gdk_window) {
return FALSE;
}
-}
-/* Checks if the window is visible as in visible on the current workspace. */
-gboolean
-empathy_window_get_is_visible (GtkWindow *window)
-{
- gboolean visible;
+ state = gdk_window_get_state (gdk_window);
+ if (state & (GDK_WINDOW_STATE_WITHDRAWN | GDK_WINDOW_STATE_ICONIFIED)) {
+ return FALSE;
+ }
- g_return_val_if_fail (window != NULL, FALSE);
+ return TRUE;
+}
- g_object_get (window,
- "visible", &visible,
- NULL);
+void
+empathy_window_iconify (GtkWindow *window, GtkStatusIcon *status_icon)
+{
+ GdkRectangle icon_location;
+ gulong data[4];
+ Display *dpy;
+ GdkWindow *gdk_window;
- return visible && window_get_is_on_current_workspace (window);
+ gtk_status_icon_get_geometry (status_icon, NULL, &icon_location, NULL);
+ gdk_window = GTK_WIDGET (window)->window;
+ dpy = gdk_x11_drawable_get_xdisplay (gdk_window);
+
+ data[0] = icon_location.x;
+ data[1] = icon_location.y;
+ data[2] = icon_location.width;
+ data[3] = icon_location.height;
+
+ XChangeProperty (dpy,
+ GDK_WINDOW_XID (gdk_window),
+ gdk_x11_get_xatom_by_name_for_display (gdk_drawable_get_display (gdk_window),
+ "_NET_WM_ICON_GEOMETRY"),
+ XA_CARDINAL, 32, PropModeReplace,
+ (guchar *)&data, 4);
+
+ gtk_window_set_skip_taskbar_hint (window, TRUE);
+ gtk_window_iconify (window);
}
/* Takes care of moving the window to the current workspace. */
void
empathy_window_present (GtkWindow *window,
- gboolean steal_focus)
+ gboolean steal_focus)
{
- gboolean visible;
- gboolean on_current;
- guint32 timestamp;
+ guint32 timestamp;
- g_return_if_fail (window != NULL);
+ g_return_if_fail (GTK_IS_WINDOW (window));
/* There are three cases: hidden, visible, visible on another
* workspace.
*/
- g_object_get (window,
- "visible", &visible,
- NULL);
-
- on_current = window_get_is_on_current_workspace (window);
-
- if (visible && !on_current) {
+ if (!empathy_window_get_is_visible (window)) {
/* Hide it so present brings it to the current workspace. */
gtk_widget_hide (GTK_WIDGET (window));
}
timestamp = gtk_get_current_event_time ();
- if (steal_focus && timestamp != GDK_CURRENT_TIME) {
- gtk_window_present_with_time (window, timestamp);
- } else {
- gtk_window_present (window);
- }
+ gtk_window_set_skip_taskbar_hint (window, FALSE);
+ gtk_window_present_with_time (window, timestamp);
+ /* FIXME: This shouldn't be required as gtk_window_present's doc says
+ * it deiconify automatically. */
+ gtk_window_deiconify (window);
}
GtkWindow *
g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
toplevel = gtk_widget_get_toplevel (widget);
- if (GTK_IS_WINDOW (toplevel)) {
+ if (GTK_IS_WINDOW (toplevel) &&
+ GTK_WIDGET_TOPLEVEL (toplevel)) {
return GTK_WINDOW (toplevel);
}
static gchar *
fixup_url (const gchar *url)
{
- gchar *real_url;
-
- if (!g_str_has_prefix (url, "http://") &&
+ if (!g_str_has_prefix (url, "ghelp:") &&
!strstr (url, ":/") &&
!strstr (url, "@")) {
- real_url = g_strdup_printf ("http://%s", url);
+ return g_strdup_printf ("http://%s", url);
} else {
- real_url = g_strdup (url);
+ return NULL;
}
-
- return real_url;
}
void
empathy_url_show (const char *url)
{
- gchar *real_url;
- GnomeVFSResult res;
+ gchar *real_url;
+ GError *error = NULL;
real_url = fixup_url (url);
- res = gnome_vfs_url_show (real_url);
- if (res != GNOME_VFS_OK) {
- empathy_debug (DEBUG_DOMAIN, "Couldn't show URL %s: %s",
- real_url,
- gnome_vfs_result_to_string (res));
+ if (real_url) {
+ url = real_url;
+ }
+
+ /* FIXME: this does not work for multihead, we should use
+ * GdkAppLaunchContext or gtk_show_url, see bug #514396.
+ */
+ g_app_info_launch_default_for_uri (url, NULL, &error);
+ if (error) {
+ GtkWidget *dialog;
+
+ dialog = gtk_message_dialog_new (NULL, 0,
+ GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
+ _("Unable to open URI"));
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+ "%s", error->message);
+
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (gtk_widget_destroy),
+ NULL);
+ gtk_window_present (GTK_WINDOW (dialog));
+
+ g_clear_error (&error);
}
g_free (real_url);
g_signal_handlers_unblock_by_func (widget, callback, user_data);
}
+GtkTextTag *
+empathy_text_buffer_tag_set (GtkTextBuffer *buffer,
+ const gchar *tag_name,
+ const gchar *first_property_name,
+ ...)
+{
+ GtkTextTagTable *table;
+ GtkTextTag *tag;
+
+ g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
+ g_return_val_if_fail (tag_name != NULL, NULL);
+
+ table = gtk_text_buffer_get_tag_table (buffer);
+ tag = gtk_text_tag_table_lookup (table, tag_name);
+
+ if (!tag) {
+ tag = gtk_text_tag_new (tag_name);
+ gtk_text_tag_table_add (table, tag);
+ g_object_unref (tag);
+ } else {
+ /* Clear the old values so that we don't affect the new theme. */
+ g_object_set (tag,
+ "background-set", FALSE,
+ "foreground-set", FALSE,
+ "invisible-set", FALSE,
+ "justification-set", FALSE,
+ "paragraph-background-set", FALSE,
+ "pixels-above-lines-set", FALSE,
+ "pixels-below-lines-set", FALSE,
+ "rise-set", FALSE,
+ "scale-set", FALSE,
+ "size-set", FALSE,
+ "style-set", FALSE,
+ "weight-set", FALSE,
+ NULL);
+ }
+
+ if (first_property_name) {
+ va_list list;
+
+ va_start (list, first_property_name);
+ g_object_set_valist (G_OBJECT (tag), first_property_name, list);
+ va_end (list);
+ }
+
+ return tag;
+}
+