diff options
Diffstat (limited to 'eel/eel-background.c')
-rw-r--r-- | eel/eel-background.c | 1273 |
1 files changed, 1273 insertions, 0 deletions
diff --git a/eel/eel-background.c b/eel/eel-background.c new file mode 100644 index 00000000..4d12b37f --- /dev/null +++ b/eel/eel-background.c @@ -0,0 +1,1273 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- + + eel-background.c: Object for the background of a widget. + + Copyright (C) 2000 Eazel, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library 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. + + Author: Darin Adler <[email protected]> +*/ + +#include <config.h> +#include "eel-background.h" +#include "eel-gdk-extensions.h" +#include "eel-gdk-pixbuf-extensions.h" +#include "eel-glib-extensions.h" +#include "eel-mate-extensions.h" +#include "eel-gtk-macros.h" +#include "eel-lib-self-check-functions.h" +#include "eel-string.h" +#include "eel-marshal.h" +#include "eel-types.h" +#include "eel-type-builtins.h" +#include <gtk/gtk.h> +#include <eel/eel-canvas.h> +#include <eel/eel-canvas-util.h> +#include <gdk/gdkx.h> +#include <gio/gio.h> +#include <math.h> +#include <stdio.h> +#define MATE_DESKTOP_USE_UNSTABLE_API +#include <libmateui/mate-bg.h> + +static void eel_background_class_init (gpointer klass); +static void eel_background_init (gpointer object, + gpointer klass); +static void eel_background_finalize (GObject *object); +static GdkPixmap *eel_background_get_pixmap_and_color (EelBackground *background, + GdkWindow *window, + GdkColor *color); + +static void set_image_properties (EelBackground *background); + +static void init_fade (EelBackground *background, GtkWidget *widget); +static void free_fade (EelBackground *background); + +EEL_CLASS_BOILERPLATE (EelBackground, eel_background, GTK_TYPE_OBJECT) + +enum +{ + APPEARANCE_CHANGED, + SETTINGS_CHANGED, + RESET, + LAST_SIGNAL +}; + +/* This is the size of the GdkRGB dither matrix, in order to avoid + * bad dithering when tiling the gradient + */ +#define GRADIENT_PIXMAP_TILE_SIZE 128 + +static guint signals[LAST_SIGNAL]; + +struct EelBackgroundDetails +{ + char *color; + + MateBG *bg; + GtkWidget *widget; + + /* Realized data: */ + GdkPixmap *background_pixmap; + gboolean background_pixmap_is_unset_root_pixmap; + MateBGCrossfade *fade; + int background_entire_width; + int background_entire_height; + GdkColor default_color; + + gboolean use_base; + + /* Is this background attached to desktop window */ + gboolean is_desktop; + /* Desktop screen size watcher */ + gulong screen_size_handler; + /* Desktop monitors configuration watcher */ + gulong screen_monitors_handler; + /* Can we use common pixmap for root window and desktop window */ + gboolean use_common_pixmap; + guint change_idle_id; + + /* activity status */ + gboolean is_active; +}; + +static void +eel_background_class_init (gpointer klass) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (klass); + + eel_type_init (); + + signals[APPEARANCE_CHANGED] = + g_signal_new ("appearance_changed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE, + G_STRUCT_OFFSET (EelBackgroundClass, + appearance_changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + signals[SETTINGS_CHANGED] = + g_signal_new ("settings_changed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE, + G_STRUCT_OFFSET (EelBackgroundClass, + settings_changed), + NULL, NULL, + g_cclosure_marshal_VOID__INT, + G_TYPE_NONE, + 1, G_TYPE_INT); + signals[RESET] = + g_signal_new ("reset", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE, + G_STRUCT_OFFSET (EelBackgroundClass, + reset), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + object_class->finalize = eel_background_finalize; +} + +static void +on_bg_changed (MateBG *bg, EelBackground *background) +{ + init_fade (background, background->details->widget); + g_signal_emit (G_OBJECT (background), + signals[APPEARANCE_CHANGED], 0); +} + +static void +on_bg_transitioned (MateBG *bg, EelBackground *background) +{ + free_fade (background); + g_signal_emit (G_OBJECT (background), + signals[APPEARANCE_CHANGED], 0); +} + +static void +eel_background_init (gpointer object, gpointer klass) +{ + EelBackground *background; + + background = EEL_BACKGROUND (object); + + background->details = g_new0 (EelBackgroundDetails, 1); + background->details->default_color.red = 0xffff; + background->details->default_color.green = 0xffff; + background->details->default_color.blue = 0xffff; + background->details->bg = mate_bg_new (); + background->details->is_active = TRUE; + + g_signal_connect (background->details->bg, "changed", + G_CALLBACK (on_bg_changed), background); + g_signal_connect (background->details->bg, "transitioned", + G_CALLBACK (on_bg_transitioned), background); + +} + +/* The safe way to clear an image from a background is: + * eel_background_set_image_uri (NULL); + * This fn is a private utility - it does NOT clear + * the details->bg_uri setting. + */ +static void +eel_background_remove_current_image (EelBackground *background) +{ + if (background->details->bg != NULL) + { + g_object_unref (G_OBJECT (background->details->bg)); + background->details->bg = NULL; + } +} + +static void +free_fade (EelBackground *background) +{ + if (background->details->fade != NULL) + { + g_object_unref (background->details->fade); + background->details->fade = NULL; + } +} + +static void +free_background_pixmap (EelBackground *background) +{ + GdkDisplay *display; + GdkPixmap *pixmap; + + pixmap = background->details->background_pixmap; + if (pixmap != NULL) + { + /* If we created a root pixmap and didn't set it as background + it will live forever, so we need to kill it manually. + If set as root background it will be killed next time the + background is changed. */ + if (background->details->background_pixmap_is_unset_root_pixmap) + { + display = gdk_drawable_get_display (GDK_DRAWABLE (pixmap)); + XKillClient (GDK_DISPLAY_XDISPLAY (display), + GDK_PIXMAP_XID (pixmap)); + } + g_object_unref (pixmap); + background->details->background_pixmap = NULL; + } +} + + +static void +eel_background_finalize (GObject *object) +{ + EelBackground *background; + + background = EEL_BACKGROUND (object); + + g_free (background->details->color); + eel_background_remove_current_image (background); + + free_background_pixmap (background); + + free_fade (background); + + g_free (background->details); + + EEL_CALL_PARENT (G_OBJECT_CLASS, finalize, (object)); +} + +static EelBackgroundImagePlacement +placement_mate_to_eel (MateBGPlacement p) +{ + switch (p) + { + case MATE_BG_PLACEMENT_CENTERED: + return EEL_BACKGROUND_CENTERED; + case MATE_BG_PLACEMENT_FILL_SCREEN: + return EEL_BACKGROUND_SCALED; + case MATE_BG_PLACEMENT_SCALED: + return EEL_BACKGROUND_SCALED_ASPECT; + case MATE_BG_PLACEMENT_ZOOMED: + return EEL_BACKGROUND_ZOOM; + case MATE_BG_PLACEMENT_TILED: + return EEL_BACKGROUND_TILED; + case MATE_BG_PLACEMENT_SPANNED: + return EEL_BACKGROUND_SPANNED; + } + + return EEL_BACKGROUND_TILED; +} + +static MateBGPlacement +placement_eel_to_mate (EelBackgroundImagePlacement p) +{ + switch (p) + { + case EEL_BACKGROUND_CENTERED: + return MATE_BG_PLACEMENT_CENTERED; + case EEL_BACKGROUND_SCALED: + return MATE_BG_PLACEMENT_FILL_SCREEN; + case EEL_BACKGROUND_SCALED_ASPECT: + return MATE_BG_PLACEMENT_SCALED; + case EEL_BACKGROUND_ZOOM: + return MATE_BG_PLACEMENT_ZOOMED; + case EEL_BACKGROUND_TILED: + return MATE_BG_PLACEMENT_TILED; + case EEL_BACKGROUND_SPANNED: + return MATE_BG_PLACEMENT_SPANNED; + } + + return MATE_BG_PLACEMENT_TILED; +} + +EelBackgroundImagePlacement +eel_background_get_image_placement (EelBackground *background) +{ + g_return_val_if_fail (EEL_IS_BACKGROUND (background), EEL_BACKGROUND_TILED); + + return placement_mate_to_eel (mate_bg_get_placement (background->details->bg)); +} + +void +eel_background_set_image_placement (EelBackground *background, + EelBackgroundImagePlacement new_placement) +{ + g_return_if_fail (EEL_IS_BACKGROUND (background)); + + mate_bg_set_placement (background->details->bg, + placement_eel_to_mate (new_placement)); +} + +EelBackground * +eel_background_new (void) +{ + return EEL_BACKGROUND (g_object_new (EEL_TYPE_BACKGROUND, NULL)); +} + +static void +eel_background_unrealize (EelBackground *background) +{ + free_background_pixmap (background); + + background->details->background_entire_width = 0; + background->details->background_entire_height = 0; + background->details->default_color.red = 0xffff; + background->details->default_color.green = 0xffff; + background->details->default_color.blue = 0xffff; +} + +static void +drawable_get_adjusted_size (EelBackground *background, + GdkDrawable *drawable, + int *width, + int *height) +{ + GdkScreen *screen; + + /* + * Screen resolution change makes root drawable have incorrect size. + */ +#if GTK_CHECK_VERSION(3, 0, 0) + width = gdk_window_get_width(GDK_WINDOW(drawable)); + height = gdk_window_get_height(GDK_WINDOW(drawable)); +#else + gdk_drawable_get_size(drawable, width, height); // FIXME: this is right? +#endif + + + if (background->details->is_desktop) + { + screen = gdk_drawable_get_screen (drawable); + *width = gdk_screen_get_width (screen); + *height = gdk_screen_get_height (screen); + } +} + +static gboolean +eel_background_ensure_realized (EelBackground *background, GdkWindow *window) +{ + gpointer data; + GtkWidget *widget; + GtkStyle *style; + gboolean changed; + int entire_width; + int entire_height; + + drawable_get_adjusted_size (background, window, &entire_width, &entire_height); + + /* Set the default color */ + + /* Get the widget to which the window belongs and its style as well */ + gdk_window_get_user_data (window, &data); + widget = GTK_WIDGET (data); + if (widget != NULL) + { + style = gtk_widget_get_style (widget); + if (background->details->use_base) + { + background->details->default_color = style->base[GTK_STATE_NORMAL]; + } + else + { + background->details->default_color = style->bg[GTK_STATE_NORMAL]; + } + + gdk_rgb_find_color (style->colormap, &(background->details->default_color)); + } + + /* If the window size is the same as last time, don't update */ + if (entire_width == background->details->background_entire_width && + entire_height == background->details->background_entire_height) + { + return FALSE; + } + + free_background_pixmap (background); + + changed = FALSE; + + set_image_properties (background); + + background->details->background_pixmap = mate_bg_create_pixmap (background->details->bg, + window, + entire_width, entire_height, + background->details->is_desktop); + background->details->background_pixmap_is_unset_root_pixmap = background->details->is_desktop; + + /* We got the pixmap and everything, so we don't care about a change + that is pending (unless things actually change after this time) */ + g_object_set_data (G_OBJECT (background->details->bg), + "ignore-pending-change", GINT_TO_POINTER (TRUE)); + changed = TRUE; + + + background->details->background_entire_width = entire_width; + background->details->background_entire_height = entire_height; + + return changed; +} + +#define CLAMP_COLOR(v) (t = (v), CLAMP (t, 0, G_MAXUSHORT)) +#define SATURATE(v) ((1.0 - saturation) * intensity + saturation * (v)) + +static void +make_color_inactive (EelBackground *background, GdkColor *color) +{ + double intensity, saturation; + gushort t; + + if (!background->details->is_active) + { + saturation = 0.7; + intensity = color->red * 0.30 + color->green * 0.59 + color->blue * 0.11; + color->red = SATURATE (color->red); + color->green = SATURATE (color->green); + color->blue = SATURATE (color->blue); + + if (intensity > G_MAXUSHORT / 2) + { + color->red *= 0.9; + color->green *= 0.9; + color->blue *= 0.9; + } + else + { + color->red *= 1.25; + color->green *= 1.25; + color->blue *= 1.25; + } + + color->red = CLAMP_COLOR (color->red); + color->green = CLAMP_COLOR (color->green); + color->blue = CLAMP_COLOR (color->blue); + } +} + +static GdkPixmap * +eel_background_get_pixmap_and_color (EelBackground *background, + GdkWindow *window, + GdkColor *color) +{ + int entire_width; + int entire_height; + + drawable_get_adjusted_size (background, window, &entire_width, &entire_height); + + eel_background_ensure_realized (background, window); + + *color = background->details->default_color; + make_color_inactive (background, color); + + if (background->details->background_pixmap != NULL) + { + return g_object_ref (background->details->background_pixmap); + } + return NULL; +} + +void +eel_background_expose (GtkWidget *widget, + GdkEventExpose *event) +{ + GdkColor color; + int window_width; + int window_height; + GdkPixmap *pixmap; + GdkGC *gc; + GdkGCValues gc_values; + GdkGCValuesMask value_mask; + GdkWindow *widget_window; + + EelBackground *background; + + widget_window = gtk_widget_get_window (widget); + if (event->window != widget_window) + { + return; + } + + background = eel_get_widget_background (widget); + + drawable_get_adjusted_size (background, widget_window, &window_width, &window_height); + + pixmap = eel_background_get_pixmap_and_color (background, + widget_window, + &color); + + if (pixmap) + { + gc_values.tile = pixmap; + gc_values.ts_x_origin = 0; + gc_values.ts_y_origin = 0; + gc_values.fill = GDK_TILED; + value_mask = GDK_GC_FILL | GDK_GC_TILE | GDK_GC_TS_X_ORIGIN | GDK_GC_TS_Y_ORIGIN; + } + else + { + gdk_rgb_find_color (gtk_widget_get_colormap (widget), &color); + gc_values.foreground = color; + gc_values.fill = GDK_SOLID; + value_mask = GDK_GC_FILL | GDK_GC_FOREGROUND; + } + + gc = gdk_gc_new_with_values (widget_window, &gc_values, value_mask); + + gdk_gc_set_clip_rectangle (gc, &event->area); + + gdk_draw_rectangle (widget_window, gc, TRUE, 0, 0, window_width, window_height); + + g_object_unref (gc); + + if (pixmap) + { + g_object_unref (pixmap); + } +} + +static void +set_image_properties (EelBackground *background) +{ + GdkColor c; + if (!background->details->color) + { + c = background->details->default_color; + make_color_inactive (background, &c); + mate_bg_set_color (background->details->bg, MATE_BG_COLOR_SOLID, + &c, NULL); + } + else if (!eel_gradient_is_gradient (background->details->color)) + { + eel_gdk_color_parse_with_white_default (background->details->color, &c); + make_color_inactive (background, &c); + mate_bg_set_color (background->details->bg, MATE_BG_COLOR_SOLID, &c, NULL); + } + else + { + GdkColor c1; + GdkColor c2; + char *spec; + + spec = eel_gradient_get_start_color_spec (background->details->color); + eel_gdk_color_parse_with_white_default (spec, &c1); + make_color_inactive (background, &c1); + g_free (spec); + + spec = eel_gradient_get_end_color_spec (background->details->color); + eel_gdk_color_parse_with_white_default (spec, &c2); + make_color_inactive (background, &c2); + g_free (spec); + + if (eel_gradient_is_horizontal (background->details->color)) + mate_bg_set_color (background->details->bg, MATE_BG_COLOR_H_GRADIENT, &c1, &c2); + else + mate_bg_set_color (background->details->bg, MATE_BG_COLOR_V_GRADIENT, &c1, &c2); + + } +} + +char * +eel_background_get_color (EelBackground *background) +{ + g_return_val_if_fail (EEL_IS_BACKGROUND (background), NULL); + + return g_strdup (background->details->color); +} + +char * +eel_background_get_image_uri (EelBackground *background) +{ + const char *filename; + + g_return_val_if_fail (EEL_IS_BACKGROUND (background), NULL); + + filename = mate_bg_get_filename (background->details->bg); + if (filename) + { + return g_filename_to_uri (filename, NULL, NULL); + } + return NULL; +} + +/* Use style->base as the default color instead of bg */ +void +eel_background_set_use_base (EelBackground *background, + gboolean use_base) +{ + background->details->use_base = use_base; +} + +void +eel_background_set_color (EelBackground *background, + const char *color) +{ + if (eel_strcmp (background->details->color, color) != 0) + { + g_free (background->details->color); + background->details->color = g_strdup (color); + + set_image_properties (background); + } +} + +static gboolean +eel_background_set_image_uri_helper (EelBackground *background, + const char *image_uri, + gboolean emit_signal) +{ + char *filename; + + if (image_uri != NULL) + { + filename = g_filename_from_uri (image_uri, NULL, NULL); + } + else + { + filename = NULL; + } + + mate_bg_set_filename (background->details->bg, filename); + + if (emit_signal) + { + g_signal_emit (GTK_OBJECT (background), signals[SETTINGS_CHANGED], 0, GDK_ACTION_COPY); + } + + set_image_properties (background); + + g_free (filename); + + return TRUE; +} + +void +eel_background_set_image_uri (EelBackground *background, const char *image_uri) +{ + + + eel_background_set_image_uri_helper (background, image_uri, TRUE); +} + +/* Use this fn to set both the image and color and avoid flash. The color isn't + * changed till after the image is done loading, that way if an update occurs + * before then, it will use the old color and image. + */ +static void +eel_background_set_image_uri_and_color (EelBackground *background, GdkDragAction action, + const char *image_uri, const char *color) +{ + eel_background_set_image_uri_helper (background, image_uri, FALSE); + eel_background_set_color (background, color); + + /* We always emit, even if the color didn't change, because the image change + * relies on us doing it here. + */ + + g_signal_emit (background, signals[SETTINGS_CHANGED], 0, action); +} + +void +eel_background_receive_dropped_background_image (EelBackground *background, + GdkDragAction action, + const char *image_uri) +{ + /* Currently, we only support tiled images. So we set the placement. + * We rely on eel_background_set_image_uri_and_color to emit + * the SETTINGS_CHANGED & APPEARANCE_CHANGE signals. + */ + eel_background_set_image_placement (background, EEL_BACKGROUND_TILED); + + eel_background_set_image_uri_and_color (background, action, image_uri, NULL); +} + +/** + * eel_background_is_set: + * + * Check whether the background's color or image has been set. + */ +gboolean +eel_background_is_set (EelBackground *background) +{ + g_assert (EEL_IS_BACKGROUND (background)); + + return background->details->color != NULL + || mate_bg_get_filename (background->details->bg) != NULL; +} + +/** + * eel_background_reset: + * + * Emit the reset signal to forget any color or image that has been + * set previously. + */ +void +eel_background_reset (EelBackground *background) +{ + g_return_if_fail (EEL_IS_BACKGROUND (background)); + + g_signal_emit (GTK_OBJECT (background), signals[RESET], 0); +} + +static void +set_root_pixmap (EelBackground *background, + GdkWindow *window) +{ + GdkPixmap *pixmap, *root_pixmap; + GdkScreen *screen; + GdkColor color; + + pixmap = eel_background_get_pixmap_and_color (background, + window, + &color); + screen = gdk_drawable_get_screen (window); + + if (background->details->use_common_pixmap) + { + background->details->background_pixmap_is_unset_root_pixmap = FALSE; + root_pixmap = g_object_ref (pixmap); + } + else + { + root_pixmap = mate_bg_create_pixmap (background->details->bg, window, + gdk_screen_get_width (screen), gdk_screen_get_height (screen), TRUE); + } + + mate_bg_set_pixmap_as_root (screen, pixmap); + + g_object_unref (pixmap); + g_object_unref (root_pixmap); +} + +static gboolean +fade_to_pixmap (EelBackground *background, + GdkWindow *window, + GdkPixmap *pixmap) +{ + if (background->details->fade == NULL) + { + return FALSE; + } + + if (!mate_bg_crossfade_set_end_pixmap (background->details->fade, + pixmap)) + { + return FALSE; + } + + if (!mate_bg_crossfade_is_started (background->details->fade)) + { + mate_bg_crossfade_start (background->details->fade, window); + if (background->details->is_desktop) + { + g_signal_connect_swapped (background->details->fade, + "finished", + G_CALLBACK (set_root_pixmap), background); + } + } + + return mate_bg_crossfade_is_started (background->details->fade); +} + + +static void +eel_background_set_up_widget (EelBackground *background, GtkWidget *widget) +{ + GtkStyle *style; + GdkPixmap *pixmap; + GdkColor color; + + int window_width; + int window_height; + + GdkWindow *window; + GdkWindow *widget_window; + gboolean in_fade; + + if (!gtk_widget_get_realized (widget)) + { + return; + } + + widget_window = gtk_widget_get_window (widget); + drawable_get_adjusted_size (background, widget_window, &window_width, &window_height); + + pixmap = eel_background_get_pixmap_and_color (background, + widget_window, + &color); + + style = gtk_widget_get_style (widget); + + gdk_rgb_find_color (style->colormap, &color); + + if (EEL_IS_CANVAS (widget)) + { + window = gtk_layout_get_bin_window (GTK_LAYOUT (widget)); + } + else + { + window = widget_window; + } + + if (background->details->fade != NULL) + { + in_fade = fade_to_pixmap (background, window, pixmap); + } + else + { + in_fade = FALSE; + } + + if (!in_fade) + { + if (background->details->is_desktop) + { + gdk_window_set_back_pixmap (window, pixmap, FALSE); + } + else + { + gdk_window_set_background (window, &color); + gdk_window_set_back_pixmap (window, pixmap, FALSE); + } + } + + if (background->details->is_desktop && !in_fade) + { + set_root_pixmap (background, window); + } + + if (pixmap) + { + g_object_unref (pixmap); + } +} + +static gboolean +on_background_changed (EelBackground *background) +{ + if (background->details->change_idle_id == 0) + { + return FALSE; + } + + background->details->change_idle_id = 0; + + eel_background_unrealize (background); + eel_background_set_up_widget (background, background->details->widget); + + gtk_widget_queue_draw (background->details->widget); + + return FALSE; +} + +static void +init_fade (EelBackground *background, GtkWidget *widget) +{ + if (widget == NULL || !gtk_widget_get_realized (widget)) + return; + + if (!background->details->is_desktop) + { + return; + } + + if (background->details->fade == NULL) + { + GdkWindow *window; + int old_width, old_height, width, height; + + /* If this was the result of a screen size change, + * we don't want to crossfade + */ + window = gtk_widget_get_window (widget); + +#if GTK_CHECK_VERSION(3, 0, 0) + old_width = gdk_window_get_width(GDK_WINDOW(window)); + old_height = gdk_window_get_height(GDK_WINDOW(window)); +#else + gdk_drawable_get_size(window, &old_width, &old_height); +#endif + + drawable_get_adjusted_size (background, window, + &width, &height); + if (old_width == width && old_height == height) + { + background->details->fade = mate_bg_crossfade_new (width, height); + g_signal_connect_swapped (background->details->fade, + "finished", + G_CALLBACK (free_fade), + background); + } + } + + if (background->details->fade != NULL && !mate_bg_crossfade_is_started (background->details->fade)) + { + GdkPixmap *start_pixmap; + + if (background->details->background_pixmap == NULL) + { + start_pixmap = mate_bg_get_pixmap_from_root (gtk_widget_get_screen (widget)); + } + else + { + start_pixmap = g_object_ref (background->details->background_pixmap); + } + mate_bg_crossfade_set_start_pixmap (background->details->fade, + start_pixmap); + g_object_unref (start_pixmap); + } +} + +static void +eel_widget_queue_background_change (GtkWidget *widget) +{ + EelBackground *background; + + background = eel_get_widget_background (widget); + + if (background->details->change_idle_id > 0) + { + return; + } + + background->details->change_idle_id = g_idle_add ((GSourceFunc) on_background_changed, background); +} + +/* Callback used when the style of a widget changes. We have to regenerate its + * EelBackgroundStyle so that it will match the chosen GTK+ theme. + */ +static void +widget_style_set_cb (GtkWidget *widget, GtkStyle *previous_style, gpointer data) +{ + EelBackground *background; + + background = EEL_BACKGROUND (data); + + if (previous_style != NULL) + { + eel_widget_queue_background_change (widget); + } +} + +static void +screen_size_changed (GdkScreen *screen, EelBackground *background) +{ + g_signal_emit (background, signals[APPEARANCE_CHANGED], 0); +} + +static void +widget_realized_setup (GtkWidget *widget, gpointer data) +{ + EelBackground *background; + + background = EEL_BACKGROUND (data); + + if (background->details->is_desktop) + { + GdkWindow *root_window; + GdkScreen *screen; + + screen = gtk_widget_get_screen (widget); + + if (background->details->screen_size_handler > 0) + { + g_signal_handler_disconnect (screen, + background->details->screen_size_handler); + } + + background->details->screen_size_handler = + g_signal_connect (screen, "size_changed", + G_CALLBACK (screen_size_changed), background); + if (background->details->screen_monitors_handler > 0) + { + g_signal_handler_disconnect (screen, + background->details->screen_monitors_handler); + } + background->details->screen_monitors_handler = + g_signal_connect (screen, "monitors-changed", + G_CALLBACK (screen_size_changed), background); + + root_window = gdk_screen_get_root_window(screen); + +#if GTK_CHECK_VERSION(3, 0, 0) + if (gdk_window_get_visual(root_window) == gtk_widget_get_visual(widget)) +#else + if (gdk_drawable_get_visual(root_window) == gtk_widget_get_visual(widget)) +#endif + { + background->details->use_common_pixmap = TRUE; + } + else + { + background->details->use_common_pixmap = FALSE; + } + + init_fade (background, widget); + } +} + +static void +widget_realize_cb (GtkWidget *widget, gpointer data) +{ + EelBackground *background; + + background = EEL_BACKGROUND (data); + + widget_realized_setup (widget, data); + + eel_background_set_up_widget (background, widget); +} + +static void +widget_unrealize_cb (GtkWidget *widget, gpointer data) +{ + EelBackground *background; + + background = EEL_BACKGROUND (data); + + if (background->details->screen_size_handler > 0) + { + g_signal_handler_disconnect (gtk_widget_get_screen (GTK_WIDGET (widget)), + background->details->screen_size_handler); + background->details->screen_size_handler = 0; + } + if (background->details->screen_monitors_handler > 0) + { + g_signal_handler_disconnect (gtk_widget_get_screen (GTK_WIDGET (widget)), + background->details->screen_monitors_handler); + background->details->screen_monitors_handler = 0; + } + background->details->use_common_pixmap = FALSE; +} + +void +eel_background_set_desktop (EelBackground *background, GtkWidget *widget, gboolean is_desktop) +{ + background->details->is_desktop = is_desktop; + + if (gtk_widget_get_realized (widget) && background->details->is_desktop) + { + widget_realized_setup (widget, background); + } + +} + +gboolean +eel_background_is_desktop (EelBackground *background) +{ + return background->details->is_desktop; +} + +static void +on_widget_destroyed (GtkWidget *widget, EelBackground *background) +{ + if (background->details->change_idle_id != 0) + { + g_source_remove (background->details->change_idle_id); + background->details->change_idle_id = 0; + } + + background->details->widget = NULL; +} + +/* Gets the background attached to a widget. + + If the widget doesn't already have a EelBackground object, + this will create one. To change the widget's background, you can + just call eel_background methods on the widget. + + If the widget is a canvas, nothing more needs to be done. For + normal widgets, you need to call eel_background_expose() from your + expose handler to draw the background. + + Later, we might want a call to find out if we already have a background, + or a way to share the same background among multiple widgets; both would + be straightforward. +*/ +EelBackground * +eel_get_widget_background (GtkWidget *widget) +{ + gpointer data; + EelBackground *background; + + g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL); + + /* Check for an existing background. */ + data = g_object_get_data (G_OBJECT (widget), "eel_background"); + if (data != NULL) + { + g_assert (EEL_IS_BACKGROUND (data)); + return data; + } + + /* Store the background in the widget's data. */ + background = eel_background_new (); + g_object_ref_sink (background); + g_object_set_data_full (G_OBJECT (widget), "eel_background", + background, g_object_unref); + background->details->widget = widget; + g_signal_connect_object (widget, "destroy", G_CALLBACK (on_widget_destroyed), background, 0); + + /* Arrange to get the signal whenever the background changes. */ + g_signal_connect_object (background, "appearance_changed", + G_CALLBACK (eel_widget_queue_background_change), widget, G_CONNECT_SWAPPED); + eel_widget_queue_background_change (widget); + + g_signal_connect_object (widget, "style_set", + G_CALLBACK (widget_style_set_cb), + background, + 0); + g_signal_connect_object (widget, "realize", + G_CALLBACK (widget_realize_cb), + background, + 0); + g_signal_connect_object (widget, "unrealize", + G_CALLBACK (widget_unrealize_cb), + background, + 0); + + return background; +} + +/* determine if a background is darker or lighter than average, to help clients know what + colors to draw on top with */ +gboolean +eel_background_is_dark (EelBackground *background) +{ + GdkScreen *screen; + GdkRectangle rect; + + /* only check for the background on the 0th monitor */ + screen = gdk_screen_get_default (); + gdk_screen_get_monitor_geometry (screen, 0, &rect); + + return mate_bg_is_dark (background->details->bg, rect.width, rect.height); +} + +/* handle dropped colors */ +void +eel_background_receive_dropped_color (EelBackground *background, + GtkWidget *widget, + GdkDragAction action, + int drop_location_x, + int drop_location_y, + const GtkSelectionData *selection_data) +{ + guint16 *channels; + char *color_spec; + char *new_gradient_spec; + int left_border, right_border, top_border, bottom_border; + GtkAllocation allocation; + + g_return_if_fail (EEL_IS_BACKGROUND (background)); + g_return_if_fail (GTK_IS_WIDGET (widget)); + g_return_if_fail (selection_data != NULL); + + /* Convert the selection data into a color spec. */ + if (gtk_selection_data_get_length ((GtkSelectionData *) selection_data) != 8 || + gtk_selection_data_get_format ((GtkSelectionData *) selection_data) != 16) + { + g_warning ("received invalid color data"); + return; + } + channels = (guint16 *) gtk_selection_data_get_data ((GtkSelectionData *) selection_data); + color_spec = g_strdup_printf ("#%02X%02X%02X", + channels[0] >> 8, + channels[1] >> 8, + channels[2] >> 8); + + /* Figure out if the color was dropped close enough to an edge to create a gradient. + For the moment, this is hard-wired, but later the widget will have to have some + say in where the borders are. + */ + gtk_widget_get_allocation (widget, &allocation); + left_border = 32; + right_border = allocation.width - 32; + top_border = 32; + bottom_border = allocation.height - 32; + if (drop_location_x < left_border && drop_location_x <= right_border) + { + new_gradient_spec = eel_gradient_set_left_color_spec (background->details->color, color_spec); + } + else if (drop_location_x >= left_border && drop_location_x > right_border) + { + new_gradient_spec = eel_gradient_set_right_color_spec (background->details->color, color_spec); + } + else if (drop_location_y < top_border && drop_location_y <= bottom_border) + { + new_gradient_spec = eel_gradient_set_top_color_spec (background->details->color, color_spec); + } + else if (drop_location_y >= top_border && drop_location_y > bottom_border) + { + new_gradient_spec = eel_gradient_set_bottom_color_spec (background->details->color, color_spec); + } + else + { + new_gradient_spec = g_strdup (color_spec); + } + + g_free (color_spec); + + eel_background_set_image_uri_and_color (background, action, NULL, new_gradient_spec); + + g_free (new_gradient_spec); +} + +void +eel_background_save_to_mateconf (EelBackground *background) +{ + MateConfClient *client = mateconf_client_get_default (); + + if (background->details->bg) + mate_bg_save_to_preferences (background->details->bg, client); +} + +void +eel_background_set_active (EelBackground *background, + gboolean is_active) +{ + if (background->details->is_active != is_active) + { + background->details->is_active = is_active; + set_image_properties (background); + } +} + +/* self check code */ + +#if !defined (EEL_OMIT_SELF_CHECK) + +void +eel_self_check_background (void) +{ + EelBackground *background; + + background = eel_background_new (); + + eel_background_set_color (background, NULL); + eel_background_set_color (background, ""); + eel_background_set_color (background, "red"); + eel_background_set_color (background, "red-blue"); + eel_background_set_color (background, "red-blue:h"); + + g_object_ref_sink (background); + g_object_unref (background); +} + +#endif |