forked from AuroraMiddleware/gtk
d1a85fc9e4
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.
646 lines
15 KiB
C
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);
|
|
}
|
|
|