183 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			183 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* GTK - The GIMP Toolkit
 | 
						|
 * Copyright (C) 2015 Benjamin Otte <otte@gnome.org>
 | 
						|
 *
 | 
						|
 * This library 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 of the License, or (at your option) any later version.
 | 
						|
 *
 | 
						|
 * This library 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
 | 
						|
 * Lesser General Public License for more details.
 | 
						|
 *
 | 
						|
 * You should have received a copy of the GNU Lesser General Public
 | 
						|
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
 | 
						|
 */
 | 
						|
 | 
						|
#include "config.h"
 | 
						|
 | 
						|
#include "gtkcssnodestylecacheprivate.h"
 | 
						|
 | 
						|
#include "gtkdebug.h"
 | 
						|
#include "gtkcssstaticstyleprivate.h"
 | 
						|
 | 
						|
struct _GtkCssNodeStyleCache {
 | 
						|
  guint        ref_count;
 | 
						|
  GtkCssStyle *style;
 | 
						|
  GHashTable  *children;
 | 
						|
};
 | 
						|
 | 
						|
#define UNPACK_DECLARATION(packed) ((GtkCssNodeDeclaration *) (GPOINTER_TO_SIZE (packed) & ~0x3))
 | 
						|
#define UNPACK_FLAGS(packed) (GPOINTER_TO_SIZE (packed) & 0x3)
 | 
						|
#define PACK(decl, first_child, last_child) GSIZE_TO_POINTER (GPOINTER_TO_SIZE (decl) | ((first_child) ? 0x2 : 0) | ((last_child) ? 0x1 : 0))
 | 
						|
 | 
						|
GtkCssNodeStyleCache *
 | 
						|
gtk_css_node_style_cache_new (GtkCssStyle *style)
 | 
						|
{
 | 
						|
  GtkCssNodeStyleCache *result;
 | 
						|
 | 
						|
  result = g_slice_new0 (GtkCssNodeStyleCache);
 | 
						|
 | 
						|
  result->ref_count = 1;
 | 
						|
  result->style = g_object_ref (style);
 | 
						|
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
GtkCssNodeStyleCache *
 | 
						|
gtk_css_node_style_cache_ref (GtkCssNodeStyleCache *cache)
 | 
						|
{
 | 
						|
  cache->ref_count++; 
 | 
						|
 | 
						|
  return cache;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
gtk_css_node_style_cache_unref (GtkCssNodeStyleCache *cache)
 | 
						|
{
 | 
						|
  cache->ref_count--; 
 | 
						|
 | 
						|
  if (cache->ref_count > 0)
 | 
						|
    return;
 | 
						|
 | 
						|
  g_object_unref (cache->style);
 | 
						|
  if (cache->children)
 | 
						|
    g_hash_table_unref (cache->children);
 | 
						|
 | 
						|
  g_slice_free (GtkCssNodeStyleCache, cache);
 | 
						|
}
 | 
						|
 | 
						|
GtkCssStyle *
 | 
						|
gtk_css_node_style_cache_get_style (GtkCssNodeStyleCache *cache)
 | 
						|
{
 | 
						|
  return cache->style;
 | 
						|
}
 | 
						|
 | 
						|
static gboolean
 | 
						|
may_be_stored_in_cache (GtkCssStyle *style)
 | 
						|
{
 | 
						|
  GtkCssChange change;
 | 
						|
 | 
						|
  /* If you run your application with
 | 
						|
   *   GTK_DEBUG=no-css-cache
 | 
						|
   * no caching will happen. This is slow (in particular
 | 
						|
   * when animating), but useful for figuring out bugs.
 | 
						|
   *
 | 
						|
   * We achieve that by disallowing any inserts into caches here.
 | 
						|
   */
 | 
						|
#ifdef G_ENABLE_DEBUG
 | 
						|
  if (GTK_DEBUG_CHECK (NO_CSS_CACHE))
 | 
						|
    return FALSE;
 | 
						|
#endif
 | 
						|
 | 
						|
  if (!GTK_IS_CSS_STATIC_STYLE (style))
 | 
						|
    return FALSE;
 | 
						|
 | 
						|
  change = gtk_css_static_style_get_change (GTK_CSS_STATIC_STYLE (style));
 | 
						|
 | 
						|
  /* The cache is shared between all children of the parent, so if a
 | 
						|
   * style depends on a sibling it is not independant of the child.
 | 
						|
   */
 | 
						|
  if (change & GTK_CSS_CHANGE_ANY_SIBLING)
 | 
						|
    return FALSE;
 | 
						|
 | 
						|
  /* Again, the cache is shared between all children of the parent.
 | 
						|
   * If the position is relevant, no child has the same style.
 | 
						|
   */
 | 
						|
  if (change & (GTK_CSS_CHANGE_NTH_CHILD | GTK_CSS_CHANGE_NTH_LAST_CHILD))
 | 
						|
    return FALSE;
 | 
						|
 | 
						|
  return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
static guint
 | 
						|
gtk_css_node_style_cache_decl_hash (gconstpointer item)
 | 
						|
{
 | 
						|
  return gtk_css_node_declaration_hash (UNPACK_DECLARATION (item)) << 2
 | 
						|
    | UNPACK_FLAGS (item);
 | 
						|
}
 | 
						|
 | 
						|
static gboolean
 | 
						|
gtk_css_node_style_cache_decl_equal (gconstpointer item1,
 | 
						|
                                     gconstpointer item2)
 | 
						|
{
 | 
						|
  if (UNPACK_FLAGS (item1) != UNPACK_FLAGS (item2))
 | 
						|
    return FALSE;
 | 
						|
 | 
						|
  return gtk_css_node_declaration_equal (UNPACK_DECLARATION (item1),
 | 
						|
                                         UNPACK_DECLARATION (item2));
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
gtk_css_node_style_cache_decl_free (gpointer item)
 | 
						|
{
 | 
						|
  gtk_css_node_declaration_unref (UNPACK_DECLARATION (item));
 | 
						|
}
 | 
						|
 | 
						|
GtkCssNodeStyleCache *
 | 
						|
gtk_css_node_style_cache_insert (GtkCssNodeStyleCache   *parent,
 | 
						|
                                 GtkCssNodeDeclaration  *decl,
 | 
						|
                                 gboolean                is_first,
 | 
						|
                                 gboolean                is_last,
 | 
						|
                                 GtkCssStyle            *style)
 | 
						|
{
 | 
						|
  GtkCssNodeStyleCache *result;
 | 
						|
 | 
						|
  if (!may_be_stored_in_cache (style))
 | 
						|
    return NULL;
 | 
						|
 | 
						|
  if (parent->children == NULL)
 | 
						|
    parent->children = g_hash_table_new_full (gtk_css_node_style_cache_decl_hash,
 | 
						|
                                              gtk_css_node_style_cache_decl_equal,
 | 
						|
                                              gtk_css_node_style_cache_decl_free,
 | 
						|
                                              (GDestroyNotify) gtk_css_node_style_cache_unref);
 | 
						|
 | 
						|
  result = gtk_css_node_style_cache_new (style);
 | 
						|
 | 
						|
  g_hash_table_insert (parent->children,
 | 
						|
                       PACK (gtk_css_node_declaration_ref (decl), is_first, is_last),
 | 
						|
                       gtk_css_node_style_cache_ref (result));
 | 
						|
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
GtkCssNodeStyleCache *
 | 
						|
gtk_css_node_style_cache_lookup (GtkCssNodeStyleCache        *parent,
 | 
						|
                                 const GtkCssNodeDeclaration *decl,
 | 
						|
                                 gboolean                     is_first,
 | 
						|
                                 gboolean                     is_last)
 | 
						|
{
 | 
						|
  GtkCssNodeStyleCache *result;
 | 
						|
 | 
						|
  if (parent->children == NULL)
 | 
						|
    return NULL;
 | 
						|
 | 
						|
  result = g_hash_table_lookup (parent->children, PACK (decl, is_first, is_last));
 | 
						|
  if (result == NULL)
 | 
						|
    return NULL;
 | 
						|
 | 
						|
  return gtk_css_node_style_cache_ref (result);
 | 
						|
}
 | 
						|
 |