summaryrefslogtreecommitdiffstats
path: root/src/st/st-theme-context.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/st/st-theme-context.c')
-rw-r--r--src/st/st-theme-context.c492
1 files changed, 492 insertions, 0 deletions
diff --git a/src/st/st-theme-context.c b/src/st/st-theme-context.c
new file mode 100644
index 0000000..4055786
--- /dev/null
+++ b/src/st/st-theme-context.c
@@ -0,0 +1,492 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * st-theme-context.c: holds global information about a tree of styled objects
+ *
+ * Copyright 2009, 2010 Red Hat, Inc.
+ * Copyright 2009 Florian Müllner
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include "st-private.h"
+#include "st-settings.h"
+#include "st-texture-cache.h"
+#include "st-theme.h"
+#include "st-theme-context.h"
+#include "st-theme-node-private.h"
+
+struct _StThemeContext {
+ GObject parent;
+
+ PangoFontDescription *font;
+ StThemeNode *root_node;
+ StTheme *theme;
+
+ /* set of StThemeNode */
+ GHashTable *nodes;
+
+ gulong stylesheets_changed_id;
+
+ int scale_factor;
+};
+
+enum
+{
+ PROP_0,
+ PROP_SCALE_FACTOR,
+
+ N_PROPS
+};
+
+static GParamSpec *props[N_PROPS] = { NULL, };
+
+enum
+{
+ CHANGED,
+
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0, };
+
+G_DEFINE_TYPE (StThemeContext, st_theme_context, G_TYPE_OBJECT)
+
+static PangoFontDescription *get_interface_font_description (void);
+static void on_font_name_changed (StSettings *settings,
+ GParamSpec *pspec,
+ StThemeContext *context);
+static void on_icon_theme_changed (StTextureCache *cache,
+ StThemeContext *context);
+static void st_theme_context_changed (StThemeContext *context);
+
+static void st_theme_context_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void st_theme_context_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void
+st_theme_context_set_scale_factor (StThemeContext *context,
+ int scale_factor)
+{
+ if (scale_factor == context->scale_factor)
+ return;
+
+ context->scale_factor = scale_factor;
+ g_object_notify_by_pspec (G_OBJECT (context), props[PROP_SCALE_FACTOR]);
+ st_theme_context_changed (context);
+}
+
+
+static void
+st_theme_context_finalize (GObject *object)
+{
+ StThemeContext *context = ST_THEME_CONTEXT (object);
+
+ g_signal_handlers_disconnect_by_func (st_settings_get (),
+ (gpointer) on_font_name_changed,
+ context);
+ g_signal_handlers_disconnect_by_func (st_texture_cache_get_default (),
+ (gpointer) on_icon_theme_changed,
+ context);
+ g_signal_handlers_disconnect_by_func (clutter_get_default_backend (),
+ (gpointer) st_theme_context_changed,
+ context);
+
+ g_clear_signal_handler (&context->stylesheets_changed_id, context->theme);
+
+ if (context->nodes)
+ g_hash_table_unref (context->nodes);
+ if (context->root_node)
+ g_object_unref (context->root_node);
+ if (context->theme)
+ g_object_unref (context->theme);
+
+ pango_font_description_free (context->font);
+
+ G_OBJECT_CLASS (st_theme_context_parent_class)->finalize (object);
+}
+
+static void
+st_theme_context_class_init (StThemeContextClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->set_property = st_theme_context_set_property;
+ object_class->get_property = st_theme_context_get_property;
+ object_class->finalize = st_theme_context_finalize;
+
+ /**
+ * StThemeContext:scale-factor:
+ *
+ * The scaling factor used for HiDPI scaling.
+ */
+ props[PROP_SCALE_FACTOR] =
+ g_param_spec_int ("scale-factor",
+ "Scale factor",
+ "Integer scale factor used for HiDPI scaling",
+ 0, G_MAXINT, 1,
+ ST_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ g_object_class_install_properties (object_class, N_PROPS, props);
+
+ /**
+ * StThemeContext::changed:
+ * @self: a #StThemeContext
+ *
+ * Emitted when the icon theme, font, resolution, scale factor or the current
+ * theme's custom stylesheets change.
+ */
+ signals[CHANGED] =
+ g_signal_new ("changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0, /* no default handler slot */
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+}
+
+static void
+st_theme_context_init (StThemeContext *context)
+{
+ context->font = get_interface_font_description ();
+
+ g_signal_connect (st_settings_get (),
+ "notify::font-name",
+ G_CALLBACK (on_font_name_changed),
+ context);
+ g_signal_connect (st_texture_cache_get_default (),
+ "icon-theme-changed",
+ G_CALLBACK (on_icon_theme_changed),
+ context);
+ g_signal_connect_swapped (clutter_get_default_backend (),
+ "resolution-changed",
+ G_CALLBACK (st_theme_context_changed),
+ context);
+
+ context->nodes = g_hash_table_new_full ((GHashFunc) st_theme_node_hash,
+ (GEqualFunc) st_theme_node_equal,
+ g_object_unref, NULL);
+ context->scale_factor = 1;
+}
+
+static void
+st_theme_context_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ StThemeContext *context = ST_THEME_CONTEXT (object);
+
+ switch (prop_id)
+ {
+ case PROP_SCALE_FACTOR:
+ st_theme_context_set_scale_factor (context, g_value_get_int (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+st_theme_context_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ StThemeContext *context = ST_THEME_CONTEXT (object);
+
+ switch (prop_id)
+ {
+ case PROP_SCALE_FACTOR:
+ g_value_set_int (value, context->scale_factor);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/**
+ * st_theme_context_new:
+ *
+ * Create a new theme context not associated with any #ClutterStage.
+ * This can be useful in testing scenarios, or if using StThemeContext
+ * with something other than #ClutterActor objects, but you generally
+ * should use st_theme_context_get_for_stage() instead.
+ *
+ * Returns: (transfer full): a new #StThemeContext
+ */
+StThemeContext *
+st_theme_context_new (void)
+{
+ StThemeContext *context;
+
+ context = g_object_new (ST_TYPE_THEME_CONTEXT, NULL);
+
+ return context;
+}
+
+static PangoFontDescription *
+get_interface_font_description (void)
+{
+ StSettings *settings = st_settings_get ();
+ g_autofree char *font_name = NULL;
+
+ g_object_get (settings, "font-name", &font_name, NULL);
+ return pango_font_description_from_string (font_name);
+}
+
+static void
+on_stage_destroy (ClutterStage *stage)
+{
+ StThemeContext *context = st_theme_context_get_for_stage (stage);
+
+ g_object_set_data (G_OBJECT (stage), "st-theme-context", NULL);
+ g_object_unref (context);
+}
+
+static void
+st_theme_context_changed (StThemeContext *context)
+{
+ StThemeNode *old_root = context->root_node;
+ context->root_node = NULL;
+ g_hash_table_remove_all (context->nodes);
+
+ g_signal_emit (context, signals[CHANGED], 0);
+
+ if (old_root)
+ g_object_unref (old_root);
+}
+
+static void
+on_font_name_changed (StSettings *settings,
+ GParamSpec *pspect,
+ StThemeContext *context)
+{
+ PangoFontDescription *font_desc = get_interface_font_description ();
+ st_theme_context_set_font (context, font_desc);
+
+ pango_font_description_free (font_desc);
+}
+
+static gboolean
+changed_idle (gpointer userdata)
+{
+ st_theme_context_changed (userdata);
+ return FALSE;
+}
+
+static void
+on_icon_theme_changed (StTextureCache *cache,
+ StThemeContext *context)
+{
+ guint id;
+
+ /* Note that an icon theme change isn't really a change of the StThemeContext;
+ * the style information has changed. But since the style factors into the
+ * icon_name => icon lookup, faking a theme context change is a good way
+ * to force users such as StIcon to look up icons again.
+ */
+ id = g_idle_add ((GSourceFunc) changed_idle, context);
+ g_source_set_name_by_id (id, "[gnome-shell] changed_idle");
+}
+
+/**
+ * st_theme_context_get_for_stage:
+ * @stage: a #ClutterStage
+ *
+ * Gets a singleton theme context associated with the stage.
+ *
+ * Returns: (transfer none): the singleton theme context for the stage
+ */
+StThemeContext *
+st_theme_context_get_for_stage (ClutterStage *stage)
+{
+ StThemeContext *context;
+
+ g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL);
+
+ context = g_object_get_data (G_OBJECT (stage), "st-theme-context");
+ if (context)
+ return context;
+
+ context = st_theme_context_new ();
+ g_object_set_data (G_OBJECT (stage), "st-theme-context", context);
+ g_signal_connect (stage, "destroy",
+ G_CALLBACK (on_stage_destroy), NULL);
+
+ return context;
+}
+
+/**
+ * st_theme_context_set_theme:
+ * @context: a #StThemeContext
+ * @theme: a #StTheme
+ *
+ * Sets the default set of theme stylesheets for the context. This theme will
+ * be used for the root node and for nodes descending from it, unless some other
+ * style is explicitly specified.
+ */
+void
+st_theme_context_set_theme (StThemeContext *context,
+ StTheme *theme)
+{
+ g_return_if_fail (ST_IS_THEME_CONTEXT (context));
+ g_return_if_fail (theme == NULL || ST_IS_THEME (theme));
+
+ if (context->theme != theme)
+ {
+ if (context->theme)
+ g_clear_signal_handler (&context->stylesheets_changed_id, context->theme);
+
+ g_set_object (&context->theme, theme);
+
+ if (context->theme)
+ {
+ context->stylesheets_changed_id =
+ g_signal_connect_swapped (context->theme,
+ "custom-stylesheets-changed",
+ G_CALLBACK (st_theme_context_changed),
+ context);
+ }
+
+ st_theme_context_changed (context);
+ }
+}
+
+/**
+ * st_theme_context_get_theme:
+ * @context: a #StThemeContext
+ *
+ * Gets the default theme for the context. See st_theme_context_set_theme()
+ *
+ * Returns: (transfer none): the default theme for the context
+ */
+StTheme *
+st_theme_context_get_theme (StThemeContext *context)
+{
+ g_return_val_if_fail (ST_IS_THEME_CONTEXT (context), NULL);
+
+ return context->theme;
+}
+
+/**
+ * st_theme_context_set_font:
+ * @context: a #StThemeContext
+ * @font: the default font for theme context
+ *
+ * Sets the default font for the theme context. This is the font that
+ * is inherited by the root node of the tree of theme nodes. If the
+ * font is not overridden, then this font will be used. If the font is
+ * partially modified (for example, with 'font-size: 110%'), then that
+ * modification is based on this font.
+ */
+void
+st_theme_context_set_font (StThemeContext *context,
+ const PangoFontDescription *font)
+{
+ g_return_if_fail (ST_IS_THEME_CONTEXT (context));
+ g_return_if_fail (font != NULL);
+
+ if (context->font == font ||
+ pango_font_description_equal (context->font, font))
+ return;
+
+ pango_font_description_free (context->font);
+ context->font = pango_font_description_copy (font);
+ st_theme_context_changed (context);
+}
+
+/**
+ * st_theme_context_get_font:
+ * @context: a #StThemeContext
+ *
+ * Gets the default font for the theme context. See st_theme_context_set_font().
+ *
+ * Returns: the default font for the theme context.
+ */
+const PangoFontDescription *
+st_theme_context_get_font (StThemeContext *context)
+{
+ g_return_val_if_fail (ST_IS_THEME_CONTEXT (context), NULL);
+
+ return context->font;
+}
+
+/**
+ * st_theme_context_get_root_node:
+ * @context: a #StThemeContext
+ *
+ * Gets the root node of the tree of theme style nodes that associated with this
+ * context. For the node tree associated with a stage, this node represents
+ * styles applied to the stage itself.
+ *
+ * Returns: (transfer none): the root node of the context's style tree
+ */
+StThemeNode *
+st_theme_context_get_root_node (StThemeContext *context)
+{
+ if (context->root_node == NULL)
+ context->root_node = st_theme_node_new (context, NULL, context->theme,
+ G_TYPE_NONE, NULL, NULL, NULL, NULL);
+
+ return context->root_node;
+}
+
+/**
+ * st_theme_context_intern_node:
+ * @context: a #StThemeContext
+ * @node: a #StThemeNode
+ *
+ * Return an existing node matching @node, or if that isn't possible,
+ * @node itself.
+ *
+ * Returns: (transfer none): a node with the same properties as @node
+ */
+StThemeNode *
+st_theme_context_intern_node (StThemeContext *context,
+ StThemeNode *node)
+{
+ StThemeNode *mine = g_hash_table_lookup (context->nodes, node);
+
+ /* this might be node or not - it doesn't actually matter */
+ if (mine != NULL)
+ return mine;
+
+ g_hash_table_add (context->nodes, g_object_ref (node));
+ return node;
+}
+
+/**
+ * st_theme_context_get_scale_factor:
+ * @context: a #StThemeContext
+ *
+ * Return the current scale factor of @context.
+ *
+ * Returns: an integer scale factor
+ */
+int
+st_theme_context_get_scale_factor (StThemeContext *context)
+{
+ g_return_val_if_fail (ST_IS_THEME_CONTEXT (context), -1);
+
+ return context->scale_factor;
+}