gtk2/gtk/gtkcssnodedeclaration.c
Matthias Clasen d1a85fc9e4 css node declaration: Allow both name and type to be set
The widget path machinery assumes that we always have types,
and without this change, it will start spewing warnings when
we start to introduce node names.
2015-10-22 20:58:52 -04:00

646 lines
15 KiB
C

/*
* Copyright © 2014 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 "gtkcssnodedeclarationprivate.h"
#include <string.h>
typedef struct _GtkRegion GtkRegion;
struct _GtkRegion
{
GQuark class_quark;
GtkRegionFlags flags;
};
struct _GtkCssNodeDeclaration {
guint refcount;
GtkJunctionSides junction_sides;
GType type;
const /* interned */ char *name;
const /* interned */ char *id;
GtkStateFlags state;
guint n_classes;
guint n_regions;
/* GQuark classes[n_classes]; */
/* GtkRegion region[n_regions]; */
};
static inline GQuark *
get_classes (const GtkCssNodeDeclaration *decl)
{
return (GQuark *) (decl + 1);
}
static inline GtkRegion *
get_regions (const GtkCssNodeDeclaration *decl)
{
return (GtkRegion *) (get_classes (decl) + decl->n_classes);
}
static inline gsize
sizeof_node (guint n_classes,
guint n_regions)
{
return sizeof (GtkCssNodeDeclaration)
+ sizeof (GQuark) * n_classes
+ sizeof (GtkRegion) * n_regions;
}
static inline gsize
sizeof_this_node (GtkCssNodeDeclaration *decl)
{
return sizeof_node (decl->n_classes, decl->n_regions);
}
static void
gtk_css_node_declaration_make_writable (GtkCssNodeDeclaration **decl)
{
if ((*decl)->refcount == 1)
return;
(*decl)->refcount--;
*decl = g_memdup (*decl, sizeof_this_node (*decl));
(*decl)->refcount = 1;
}
static void
gtk_css_node_declaration_make_writable_resize (GtkCssNodeDeclaration **decl,
gsize offset,
gsize bytes_added,
gsize bytes_removed)
{
gsize old_size = sizeof_this_node (*decl);
gsize new_size = old_size + bytes_added - bytes_removed;
if ((*decl)->refcount == 1)
{
if (bytes_removed > 0 && old_size - offset - bytes_removed > 0)
memmove (((char *) *decl) + offset, ((char *) *decl) + offset + bytes_removed, old_size - offset - bytes_removed);
*decl = g_realloc (*decl, new_size);
if (bytes_added > 0 && old_size - offset > 0)
memmove (((char *) *decl) + offset + bytes_added, ((char *) *decl) + offset, old_size - offset);
}
else
{
GtkCssNodeDeclaration *old = *decl;
old->refcount--;
*decl = g_malloc (new_size);
memcpy (*decl, old, offset);
if (old_size - offset - bytes_removed > 0)
memcpy (((char *) *decl) + offset + bytes_added, ((char *) old) + offset + bytes_removed, old_size - offset - bytes_removed);
(*decl)->refcount = 1;
}
}
GtkCssNodeDeclaration *
gtk_css_node_declaration_new (void)
{
static GtkCssNodeDeclaration empty = {
1, /* need to own a ref ourselves so the copy-on-write path kicks in when people change things */
0,
0,
NULL,
NULL,
0,
0,
0
};
return gtk_css_node_declaration_ref (&empty);
}
GtkCssNodeDeclaration *
gtk_css_node_declaration_ref (GtkCssNodeDeclaration *decl)
{
decl->refcount++;
return decl;
}
void
gtk_css_node_declaration_unref (GtkCssNodeDeclaration *decl)
{
decl->refcount--;
if (decl->refcount > 0)
return;
g_free (decl);
}
gboolean
gtk_css_node_declaration_set_junction_sides (GtkCssNodeDeclaration **decl,
GtkJunctionSides junction_sides)
{
if ((*decl)->junction_sides == junction_sides)
return FALSE;
gtk_css_node_declaration_make_writable (decl);
(*decl)->junction_sides = junction_sides;
return TRUE;
}
GtkJunctionSides
gtk_css_node_declaration_get_junction_sides (const GtkCssNodeDeclaration *decl)
{
return decl->junction_sides;
}
gboolean
gtk_css_node_declaration_set_type (GtkCssNodeDeclaration **decl,
GType type)
{
if ((*decl)->type == type)
return FALSE;
gtk_css_node_declaration_make_writable (decl);
(*decl)->type = type;
return TRUE;
}
GType
gtk_css_node_declaration_get_type (const GtkCssNodeDeclaration *decl)
{
return decl->type;
}
gboolean
gtk_css_node_declaration_set_name (GtkCssNodeDeclaration **decl,
/*interned*/ const char *name)
{
if ((*decl)->name == name)
return FALSE;
gtk_css_node_declaration_make_writable (decl);
(*decl)->name = name;
return TRUE;
}
/*interned*/ const char *
gtk_css_node_declaration_get_name (const GtkCssNodeDeclaration *decl)
{
return decl->name;
}
gboolean
gtk_css_node_declaration_set_id (GtkCssNodeDeclaration **decl,
const char *id)
{
id = g_intern_string (id);
if ((*decl)->id == id)
return FALSE;
gtk_css_node_declaration_make_writable (decl);
(*decl)->id = id;
return TRUE;
}
const char *
gtk_css_node_declaration_get_id (const GtkCssNodeDeclaration *decl)
{
return decl->id;
}
gboolean
gtk_css_node_declaration_set_state (GtkCssNodeDeclaration **decl,
GtkStateFlags state)
{
if ((*decl)->state == state)
return FALSE;
gtk_css_node_declaration_make_writable (decl);
(*decl)->state = state;
return TRUE;
}
GtkStateFlags
gtk_css_node_declaration_get_state (const GtkCssNodeDeclaration *decl)
{
return decl->state;
}
static gboolean
find_class (const GtkCssNodeDeclaration *decl,
GQuark class_quark,
guint *position)
{
gint min, max, mid;
gboolean found = FALSE;
GQuark *classes;
guint pos;
*position = 0;
if (decl->n_classes == 0)
return FALSE;
min = 0;
max = decl->n_classes - 1;
classes = get_classes (decl);
do
{
GQuark item;
mid = (min + max) / 2;
item = classes[mid];
if (class_quark == item)
{
found = TRUE;
pos = mid;
break;
}
else if (class_quark > item)
min = pos = mid + 1;
else
{
max = mid - 1;
pos = mid;
}
}
while (min <= max);
*position = pos;
return found;
}
gboolean
gtk_css_node_declaration_add_class (GtkCssNodeDeclaration **decl,
GQuark class_quark)
{
guint pos;
if (find_class (*decl, class_quark, &pos))
return FALSE;
gtk_css_node_declaration_make_writable_resize (decl,
(char *) &get_classes (*decl)[pos] - (char *) *decl,
sizeof (GQuark),
0);
(*decl)->n_classes++;
get_classes(*decl)[pos] = class_quark;
return TRUE;
}
gboolean
gtk_css_node_declaration_remove_class (GtkCssNodeDeclaration **decl,
GQuark class_quark)
{
guint pos;
if (!find_class (*decl, class_quark, &pos))
return FALSE;
gtk_css_node_declaration_make_writable_resize (decl,
(char *) &get_classes (*decl)[pos] - (char *) *decl,
0,
sizeof (GQuark));
(*decl)->n_classes--;
return TRUE;
}
gboolean
gtk_css_node_declaration_clear_classes (GtkCssNodeDeclaration **decl)
{
if ((*decl)->n_classes == 0)
return FALSE;
gtk_css_node_declaration_make_writable_resize (decl,
(char *) get_classes (*decl) - (char *) *decl,
0,
sizeof (GQuark) * (*decl)->n_classes);
(*decl)->n_classes = 0;
return TRUE;
}
gboolean
gtk_css_node_declaration_has_class (const GtkCssNodeDeclaration *decl,
GQuark class_quark)
{
guint pos;
GQuark *classes = get_classes (decl);
switch (decl->n_classes)
{
case 3:
if (classes[2] == class_quark)
return TRUE;
case 2:
if (classes[1] == class_quark)
return TRUE;
case 1:
if (classes[0] == class_quark)
return TRUE;
case 0:
return FALSE;
default:
return find_class (decl, class_quark, &pos);
}
}
const GQuark *
gtk_css_node_declaration_get_classes (const GtkCssNodeDeclaration *decl,
guint *n_classes)
{
*n_classes = decl->n_classes;
return get_classes (decl);
}
static gboolean
find_region (const GtkCssNodeDeclaration *decl,
GQuark region_quark,
guint *position)
{
gint min, max, mid;
gboolean found = FALSE;
GtkRegion *regions;
guint pos;
if (position)
*position = 0;
if (decl->n_regions == 0)
return FALSE;
min = 0;
max = decl->n_regions - 1;
regions = get_regions (decl);
do
{
GQuark item;
mid = (min + max) / 2;
item = regions[mid].class_quark;
if (region_quark == item)
{
found = TRUE;
pos = mid;
break;
}
else if (region_quark > item)
min = pos = mid + 1;
else
{
max = mid - 1;
pos = mid;
}
}
while (min <= max);
if (position)
*position = pos;
return found;
}
gboolean
gtk_css_node_declaration_add_region (GtkCssNodeDeclaration **decl,
GQuark region_quark,
GtkRegionFlags flags)
{
GtkRegion *regions;
guint pos;
if (find_region (*decl, region_quark, &pos))
return FALSE;
gtk_css_node_declaration_make_writable_resize (decl,
(char *) &get_regions (*decl)[pos] - (char *) *decl,
sizeof (GtkRegion),
0);
(*decl)->n_regions++;
regions = get_regions(*decl);
regions[pos].class_quark = region_quark;
regions[pos].flags = flags;
return TRUE;
}
gboolean
gtk_css_node_declaration_remove_region (GtkCssNodeDeclaration **decl,
GQuark region_quark)
{
guint pos;
if (!find_region (*decl, region_quark, &pos))
return FALSE;
gtk_css_node_declaration_make_writable_resize (decl,
(char *) &get_regions (*decl)[pos] - (char *) *decl,
0,
sizeof (GtkRegion));
(*decl)->n_regions--;
return TRUE;
}
gboolean
gtk_css_node_declaration_clear_regions (GtkCssNodeDeclaration **decl)
{
if ((*decl)->n_regions == 0)
return FALSE;
gtk_css_node_declaration_make_writable_resize (decl,
(char *) get_regions (*decl) - (char *) *decl,
0,
sizeof (GtkRegion) * (*decl)->n_regions);
(*decl)->n_regions = 0;
return TRUE;
}
gboolean
gtk_css_node_declaration_has_region (const GtkCssNodeDeclaration *decl,
GQuark region_quark,
GtkRegionFlags *flags_return)
{
guint pos;
if (!find_region (decl, region_quark, &pos))
{
if (flags_return)
*flags_return = 0;
return FALSE;
}
if (flags_return)
*flags_return = get_regions (decl)[pos].flags;
return TRUE;
}
GList *
gtk_css_node_declaration_list_regions (const GtkCssNodeDeclaration *decl)
{
GtkRegion *regions;
GList *result;
guint i;
regions = get_regions (decl);
result = NULL;
for (i = 0; i < decl->n_regions; i++)
{
result = g_list_prepend (result, GUINT_TO_POINTER (regions[i].class_quark));
}
return result;
}
guint
gtk_css_node_declaration_hash (gconstpointer elem)
{
const GtkCssNodeDeclaration *decl = elem;
GQuark *classes;
GtkRegion *regions;
guint hash, i;
hash = (guint) decl->type;
hash ^= GPOINTER_TO_UINT (decl->name);
hash <<= 5;
hash ^= GPOINTER_TO_UINT (decl->id);
classes = get_classes (decl);
for (i = 0; i < decl->n_classes; i++)
{
hash <<= 5;
hash += classes[i];
}
regions = get_regions (decl);
for (i = 0; i < decl->n_regions; i++)
{
hash <<= 5;
hash += regions[i].class_quark;
hash += regions[i].flags;
}
hash ^= ((guint) decl->junction_sides) << (sizeof (guint) * 8 - 5);
hash ^= decl->state;
return hash;
}
gboolean
gtk_css_node_declaration_equal (gconstpointer elem1,
gconstpointer elem2)
{
const GtkCssNodeDeclaration *decl1 = elem1;
const GtkCssNodeDeclaration *decl2 = elem2;
GQuark *classes1, *classes2;
GtkRegion *regions1, *regions2;
guint i;
if (decl1 == decl2)
return TRUE;
if (decl1->type != decl2->type)
return FALSE;
if (decl1->name != decl2->name)
return FALSE;
if (decl1->state != decl2->state)
return FALSE;
if (decl1->id != decl2->id)
return FALSE;
if (decl1->n_classes != decl2->n_classes)
return FALSE;
classes1 = get_classes (decl1);
classes2 = get_classes (decl2);
for (i = 0; i < decl1->n_classes; i++)
{
if (classes1[i] != classes2[i])
return FALSE;
}
if (decl1->n_regions != decl2->n_regions)
return FALSE;
regions1 = get_regions (decl1);
regions2 = get_regions (decl2);
for (i = 0; i < decl1->n_regions; i++)
{
if (regions1[i].class_quark != regions2[i].class_quark ||
regions1[i].flags != regions2[i].flags)
return FALSE;
}
if (decl1->junction_sides != decl2->junction_sides)
return FALSE;
return TRUE;
}
void
gtk_css_node_declaration_add_to_widget_path (const GtkCssNodeDeclaration *decl,
GtkWidgetPath *path,
guint pos)
{
GQuark *classes;
GtkRegion *regions;
guint i;
/* Set widget regions */
regions = get_regions (decl);
for (i = 0; i < decl->n_regions; i++)
{
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
gtk_widget_path_iter_add_region (path, pos,
g_quark_to_string (regions[i].class_quark),
regions[i].flags);
G_GNUC_END_IGNORE_DEPRECATIONS
}
/* Set widget classes */
classes = get_classes (decl);
for (i = 0; i < decl->n_classes; i++)
{
gtk_widget_path_iter_add_qclass (path, pos, classes[i]);
}
/* Set widget state */
gtk_widget_path_iter_set_state (path, pos, decl->state);
}