Logo Search packages:      
Sourcecode: nautilus version File versions  Download package

nautilus-desktop-background.c

/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */

/*
 * nautilus-desktop-background.c: Helper object to handle desktop background
 *                                changes.
 *
 * Copyright (C) 2000 Eazel, Inc.
 * Copyright (C) 2010 Cosimo Cecchi <cosimoc@gnome.org>
 *
 * 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.
 *
 * Authors: Darin Adler <darin@bentspoon.com>
 *          Cosimo Cecchi <cosimoc@gnome.org>
 */

#include <config.h>

#include "nautilus-desktop-background.h"

#include <eel/eel-gdk-extensions.h>
#include <eel/eel-gtk-extensions.h>

#include "nautilus-global-preferences.h"

#define GNOME_DESKTOP_USE_UNSTABLE_API
#include <libgnome-desktop/gnome-bg.h>
#include <gdesktop-enums.h>

#include <gtk/gtk.h>
#include <string.h>

static void init_fade (NautilusDesktopBackground *self);
static void free_fade (NautilusDesktopBackground *self);
static void queue_background_change (NautilusDesktopBackground *self);

static NautilusDesktopBackground *singleton = NULL;

G_DEFINE_TYPE (NautilusDesktopBackground, nautilus_desktop_background, G_TYPE_OBJECT);

enum {
        PROP_WIDGET = 1,
        NUM_PROPERTIES,
};

00058 struct NautilusDesktopBackgroundDetails {

      GtkWidget *widget;
        GnomeBG *bg;

      /* Realized data: */
      cairo_surface_t *background_surface;
      GnomeBGCrossfade *fade;
      int background_entire_width;
      int background_entire_height;
      GdkColor default_color;

      /* Desktop screen size watcher */
      gulong screen_size_handler;
      /* Desktop monitors configuration watcher */
      gulong screen_monitors_handler;
      guint change_idle_id;
};


static gboolean
background_settings_change_event_cb (GSettings *settings,
                                     gpointer   keys,
                                     gint       n_keys,
                                     gpointer   user_data);


static void
free_fade (NautilusDesktopBackground *self)
{
      if (self->details->fade != NULL) {
            g_object_unref (self->details->fade);
            self->details->fade = NULL;
      }
}

static void
free_background_surface (NautilusDesktopBackground *self)
{
      cairo_surface_t *surface;

      surface = self->details->background_surface;
      if (surface != NULL) {
            cairo_surface_destroy (surface);
            self->details->background_surface = NULL;
      }
}

static void
nautilus_desktop_background_finalize (GObject *object)
{
      NautilusDesktopBackground *self;

      self = NAUTILUS_DESKTOP_BACKGROUND (object);

      g_signal_handlers_disconnect_by_func (gnome_background_preferences,
                                    background_settings_change_event_cb,
                                    self);

      free_background_surface (self);
      free_fade (self);

      G_OBJECT_CLASS (nautilus_desktop_background_parent_class)->finalize (object);
}

static void
nautilus_desktop_background_unrealize (NautilusDesktopBackground *self)
{
      free_background_surface (self);

      self->details->background_entire_width = 0;
      self->details->background_entire_height = 0;
      self->details->default_color.red = 0xffff;
      self->details->default_color.green = 0xffff;
      self->details->default_color.blue = 0xffff;
}

static void
nautilus_desktop_background_set_image_uri (NautilusDesktopBackground *self,
                                           const char *image_uri)
{
      char *filename;

      if (image_uri != NULL) {
            filename = g_filename_from_uri (image_uri, NULL, NULL);
      }
      else {
            filename = NULL;
      }

      gnome_bg_set_filename (self->details->bg, filename);

      g_free (filename);
}

static void
init_fade (NautilusDesktopBackground *self)
{
      GtkWidget *widget;
      gboolean do_fade;

      widget = self->details->widget;

      if (widget == NULL || !gtk_widget_get_realized (widget))
            return;

      do_fade = g_settings_get_boolean (nautilus_desktop_preferences,
                                NAUTILUS_PREFERENCES_DESKTOP_BACKGROUND_FADE);

      if (!do_fade) {
            return;
      }

      if (self->details->fade == NULL) {
            GdkWindow *window;
            GdkScreen *screen;
            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);
            old_width = gdk_window_get_width (window);
            old_height = gdk_window_get_height (window);

            screen = gtk_widget_get_screen (widget);
            width = gdk_screen_get_width (screen);
            height = gdk_screen_get_height (screen);

            if (old_width == width && old_height == height) {
                  self->details->fade = gnome_bg_crossfade_new (width, height);
                  g_signal_connect_swapped (self->details->fade,
                                                  "finished",
                                                  G_CALLBACK (free_fade),
                                                  self);
            }
      }

      if (self->details->fade != NULL && !gnome_bg_crossfade_is_started (self->details->fade)) {
            cairo_surface_t *start_surface;

            if (self->details->background_surface == NULL) {
                  start_surface = gnome_bg_get_surface_from_root (gtk_widget_get_screen (widget));
            } else {
                  start_surface = cairo_surface_reference (self->details->background_surface);
            }
            gnome_bg_crossfade_set_start_surface (self->details->fade,
                                          start_surface);
                cairo_surface_destroy (start_surface);
      }
}

static void
screen_size_changed (GdkScreen *screen,
                     NautilusDesktopBackground *self)
{
      queue_background_change (self);
}

static gboolean
nautilus_desktop_background_ensure_realized (NautilusDesktopBackground *self)
{
      int entire_width;
      int entire_height;
      GdkScreen *screen;
      GdkWindow *window;

      screen = gtk_widget_get_screen (self->details->widget);
      entire_height = gdk_screen_get_height (screen);
      entire_width = gdk_screen_get_width (screen);

      /* If the window size is the same as last time, don't update */
      if (entire_width == self->details->background_entire_width &&
          entire_height == self->details->background_entire_height) {
            return FALSE;
      }

      free_background_surface (self);

      window = gtk_widget_get_window (self->details->widget);
      self->details->background_surface = gnome_bg_create_surface (self->details->bg,
                                                                     window,
                                                                     entire_width, entire_height,
                                                                     TRUE);

      /* We got the surface 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 (self),
                     "ignore-pending-change", GINT_TO_POINTER (TRUE));

      self->details->background_entire_width = entire_width;
      self->details->background_entire_height = entire_height;

      return TRUE;
}

static void
on_fade_finished (GnomeBGCrossfade *fade,
              GdkWindow *window,
              gpointer user_data)
{
        NautilusDesktopBackground *self = user_data;

      nautilus_desktop_background_ensure_realized (self);
      gnome_bg_set_surface_as_root (gdk_window_get_screen (window),
                                      self->details->background_surface);
}

static gboolean
fade_to_surface (NautilusDesktopBackground *self,
             GdkWindow     *window,
             cairo_surface_t *surface)
{
      if (self->details->fade == NULL) {
            return FALSE;
      }

      if (!gnome_bg_crossfade_set_end_surface (self->details->fade,
                                         surface)) {
            return FALSE;
      }

      if (!gnome_bg_crossfade_is_started (self->details->fade)) {
            gnome_bg_crossfade_start (self->details->fade, window);
            g_signal_connect (self->details->fade,
                          "finished",
                          G_CALLBACK (on_fade_finished), self);
      }

      return gnome_bg_crossfade_is_started (self->details->fade);
}

static void
nautilus_desktop_background_set_up_widget (NautilusDesktopBackground *self)
{
      GdkWindow *window;
      gboolean in_fade = FALSE;
        GtkWidget *widget;

        widget = self->details->widget;

      if (!gtk_widget_get_realized (widget)) {
            return;
      }

      nautilus_desktop_background_ensure_realized (self);
        window = gtk_layout_get_bin_window (GTK_LAYOUT (widget));

      in_fade = fade_to_surface (self, window,
                           self->details->background_surface);

      if (!in_fade) {
            cairo_pattern_t *pattern;

            pattern = cairo_pattern_create_for_surface (self->details->background_surface);
            gdk_window_set_background_pattern (window, pattern);
            cairo_pattern_destroy (pattern);

                gnome_bg_set_surface_as_root (gtk_widget_get_screen (widget),
                                              self->details->background_surface);
      }
}

static gboolean
background_changed_cb (NautilusDesktopBackground *self)
{
      self->details->change_idle_id = 0;

      nautilus_desktop_background_unrealize (self);
      nautilus_desktop_background_set_up_widget (self);

      gtk_widget_queue_draw (self->details->widget);

      return FALSE;
}

static void
queue_background_change (NautilusDesktopBackground *self)
{
      if (self->details->change_idle_id != 0) {
                g_source_remove (self->details->change_idle_id);
      }

      self->details->change_idle_id =
                g_idle_add ((GSourceFunc) background_changed_cb, self);
}

static void
nautilus_desktop_background_changed (GnomeBG *bg,
                                     gpointer user_data)
{
        NautilusDesktopBackground *self;

        self = user_data;
      init_fade (self);
      queue_background_change (self);
}

static void
nautilus_desktop_background_transitioned (GnomeBG *bg,
                                          gpointer user_data)
{
        NautilusDesktopBackground *self;

        self = user_data;
      free_fade (self);
      queue_background_change (self);
}

static void
widget_realize_cb (GtkWidget *widget,
                   gpointer user_data)
{
      GdkScreen *screen;
        NautilusDesktopBackground *self = user_data;

      screen = gtk_widget_get_screen (widget);

      if (self->details->screen_size_handler > 0) {
            g_signal_handler_disconnect (screen,
                                   self->details->screen_size_handler);
      }
      self->details->screen_size_handler = 
            g_signal_connect (screen, "size_changed",
                          G_CALLBACK (screen_size_changed), self);

      if (self->details->screen_monitors_handler > 0) {
            g_signal_handler_disconnect (screen,
                                   self->details->screen_monitors_handler);
      }
      self->details->screen_monitors_handler =
            g_signal_connect (screen, "monitors-changed",
                          G_CALLBACK (screen_size_changed), self);

      init_fade (self);
      nautilus_desktop_background_set_up_widget (self);
}

static void
widget_unrealize_cb (GtkWidget *widget,
                     gpointer user_data)
{
        NautilusDesktopBackground *self = user_data;

      if (self->details->screen_size_handler > 0) {
                    g_signal_handler_disconnect (gtk_widget_get_screen (GTK_WIDGET (widget)),
                                             self->details->screen_size_handler);
                  self->details->screen_size_handler = 0;
      }
      if (self->details->screen_monitors_handler > 0) {
                    g_signal_handler_disconnect (gtk_widget_get_screen (GTK_WIDGET (widget)),
                                             self->details->screen_monitors_handler);
                  self->details->screen_monitors_handler = 0;
      }
}

static void
on_widget_destroyed (GtkWidget *widget,
                     gpointer user_data)
{
        NautilusDesktopBackground *self = user_data;

      if (self->details->change_idle_id != 0) {
            g_source_remove (self->details->change_idle_id);
            self->details->change_idle_id = 0;
      }

      self->details->widget = NULL;
}

static gboolean
background_change_event_idle_cb (NautilusDesktopBackground *self)
{
      gnome_bg_load_from_preferences (self->details->bg,
                              gnome_background_preferences);

      g_object_unref (self);

      return FALSE;
}

static gboolean
background_settings_change_event_cb (GSettings *settings,
                                     gpointer   keys,
                                     gint       n_keys,
                                     gpointer   user_data)
{
      NautilusDesktopBackground *self = user_data;

      /* Need to defer signal processing otherwise
       * we would make the dconf backend deadlock.
       */
      g_idle_add ((GSourceFunc) background_change_event_idle_cb,
                g_object_ref (self));

      return FALSE;
}

static void
nautilus_desktop_background_constructed (GObject *obj)
{
        NautilusDesktopBackground *self;
        GtkWidget *widget;

        self = NAUTILUS_DESKTOP_BACKGROUND (obj);

        if (G_OBJECT_CLASS (nautilus_desktop_background_parent_class)->constructed != NULL) {
                G_OBJECT_CLASS (nautilus_desktop_background_parent_class)->constructed (obj);
        }

        widget = self->details->widget;

        g_assert (widget != NULL);

      g_signal_connect_object (widget, "destroy",
                                 G_CALLBACK (on_widget_destroyed), self, 0);
      g_signal_connect_object (widget, "realize",
                         G_CALLBACK (widget_realize_cb), self, 0);
      g_signal_connect_object (widget, "unrealize",
                         G_CALLBACK (widget_unrealize_cb), self, 0);

        gnome_bg_load_from_preferences (self->details->bg,
                                        gnome_background_preferences);

        /* Let's receive batch change events instead of every single one */
        g_signal_connect (gnome_background_preferences,
                          "change-event",
                          G_CALLBACK (background_settings_change_event_cb),
                          self);

      queue_background_change (self);
}

static void
nautilus_desktop_background_set_property (GObject *object,
                                          guint property_id,
                                          const GValue *value,
                                          GParamSpec *pspec)
{
        NautilusDesktopBackground *self;

        self = NAUTILUS_DESKTOP_BACKGROUND (object);

        switch (property_id) {
        case PROP_WIDGET:
                self->details->widget = g_value_get_object (value);
                break;
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
                break;
        }
}

static GObject *
nautilus_desktop_background_constructor (GType type,
                                         guint n_construct_params,
                                         GObjectConstructParam *construct_params)
{
        GObject *retval;

        if (singleton != NULL) {
                return g_object_ref (singleton);
        }

        retval = G_OBJECT_CLASS (nautilus_desktop_background_parent_class)->constructor
                (type, n_construct_params, construct_params);

        singleton = NAUTILUS_DESKTOP_BACKGROUND (retval);
        g_object_add_weak_pointer (retval, (gpointer) &singleton);

        return retval;
}

static void
nautilus_desktop_background_class_init (NautilusDesktopBackgroundClass *klass)
{
      GObjectClass *object_class;
        GParamSpec *pspec;

      object_class = G_OBJECT_CLASS (klass);
      object_class->finalize = nautilus_desktop_background_finalize;
        object_class->set_property = nautilus_desktop_background_set_property;
        object_class->constructor = nautilus_desktop_background_constructor;
        object_class->constructed = nautilus_desktop_background_constructed;

        pspec = g_param_spec_object ("widget", "The widget for this background",
                                     "The widget that gets its background set",
                                     NAUTILUS_TYPE_ICON_CONTAINER,
                                     G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
        g_object_class_install_property (object_class, PROP_WIDGET, pspec);

      g_type_class_add_private (klass, sizeof (NautilusDesktopBackgroundDetails));
}

static void
nautilus_desktop_background_init (NautilusDesktopBackground *self)
{
      self->details =
            G_TYPE_INSTANCE_GET_PRIVATE (self,
                                   NAUTILUS_TYPE_DESKTOP_BACKGROUND,
                                   NautilusDesktopBackgroundDetails);

        self->details->bg = gnome_bg_new ();
      self->details->default_color.red = 0xffff;
      self->details->default_color.green = 0xffff;
      self->details->default_color.blue = 0xffff;

      g_signal_connect (self->details->bg, "changed",
                    G_CALLBACK (nautilus_desktop_background_changed), self);
      g_signal_connect (self->details->bg, "transitioned",
                    G_CALLBACK (nautilus_desktop_background_transitioned), self);
}

void
nautilus_desktop_background_receive_dropped_background_image (NautilusDesktopBackground *self,
                                                              const char *image_uri)
{
      /* Currently, we only support tiled images. So we set the placement.
       */
      gnome_bg_set_placement (self->details->bg,
                        G_DESKTOP_BACKGROUND_STYLE_WALLPAPER);
      gnome_bg_set_draw_background (self->details->bg, TRUE);
      nautilus_desktop_background_set_image_uri (self, image_uri);

      gnome_bg_save_to_preferences (self->details->bg,
                              gnome_background_preferences);
}

NautilusDesktopBackground *
nautilus_desktop_background_new (NautilusIconContainer *container)
{
        return g_object_new (NAUTILUS_TYPE_DESKTOP_BACKGROUND,
                             "widget", container,
                             NULL);
}

Generated by  Doxygen 1.6.0   Back to index