gtk2/gtk/gtktextviewchild.c
Matthias Clasen ae7cefd97d Drop style class defines
We document the supported style classes by name,
not by macro name, and these macros don't really
add any value. Drop them for GTK 4.
2020-08-14 07:03:27 -04:00

501 lines
14 KiB
C

/* -*- Mode: C; c-file-style: "gnu"; tab-width: 8 -*- */
/* GTK - The GIMP Toolkit
* gtk_text_view_child.c Copyright (C) 2019 Red Hat, Inc.
*
* 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 "gtkcssnodeprivate.h"
#include "gtkintl.h"
#include "gtkprivate.h"
#include "gtkstylecontext.h"
#include "gtktextview.h"
#include "gtktextviewchildprivate.h"
#include "gtktypebuiltins.h"
#include "gtkwidgetprivate.h"
typedef struct
{
GList link;
GtkWidget *widget;
int x;
int y;
} Overlay;
struct _GtkTextViewChild
{
GtkWidget parent_instance;
GtkTextWindowType window_type;
GQueue overlays;
int xoffset;
int yoffset;
GtkWidget *child;
};
enum {
PROP_0,
PROP_WINDOW_TYPE,
N_PROPS
};
G_DEFINE_TYPE (GtkTextViewChild, gtk_text_view_child, GTK_TYPE_WIDGET)
static GParamSpec *properties[N_PROPS];
static Overlay *
overlay_new (GtkWidget *widget,
int x,
int y)
{
Overlay *overlay;
overlay = g_slice_new0 (Overlay);
overlay->link.data = overlay;
overlay->widget = g_object_ref (widget);
overlay->x = x;
overlay->y = y;
return overlay;
}
static void
overlay_free (Overlay *overlay)
{
g_assert (overlay->link.prev == NULL);
g_assert (overlay->link.next == NULL);
g_object_unref (overlay->widget);
g_slice_free (Overlay, overlay);
}
static void
gtk_text_view_child_remove_overlay (GtkTextViewChild *self,
Overlay *overlay)
{
g_queue_unlink (&self->overlays, &overlay->link);
gtk_widget_unparent (overlay->widget);
overlay_free (overlay);
}
static Overlay *
gtk_text_view_child_get_overlay (GtkTextViewChild *self,
GtkWidget *widget)
{
GList *iter;
for (iter = self->overlays.head; iter; iter = iter->next)
{
Overlay *overlay = iter->data;
if (overlay->widget == widget)
return overlay;
}
return NULL;
}
void
gtk_text_view_child_add (GtkTextViewChild *self,
GtkWidget *widget)
{
if (self->child != NULL)
{
g_warning ("%s allows a single child and already contains a %s",
G_OBJECT_TYPE_NAME (self),
G_OBJECT_TYPE_NAME (widget));
return;
}
self->child = g_object_ref (widget);
gtk_widget_set_parent (widget, GTK_WIDGET (self));
}
void
gtk_text_view_child_remove (GtkTextViewChild *self,
GtkWidget *widget)
{
if (widget == self->child)
{
self->child = NULL;
gtk_widget_unparent (widget);
g_object_unref (widget);
}
else
{
Overlay *overlay = gtk_text_view_child_get_overlay (self, widget);
if (overlay != NULL)
gtk_text_view_child_remove_overlay (self, overlay);
}
}
static void
gtk_text_view_child_measure (GtkWidget *widget,
GtkOrientation orientation,
int for_size,
int *min_size,
int *nat_size,
int *min_baseline,
int *nat_baseline)
{
GtkTextViewChild *self = GTK_TEXT_VIEW_CHILD (widget);
const GList *iter;
int real_min_size = 0;
int real_nat_size = 0;
if (self->child != NULL)
gtk_widget_measure (self->child,
orientation,
for_size,
&real_min_size,
&real_nat_size,
NULL,
NULL);
for (iter = self->overlays.head; iter; iter = iter->next)
{
Overlay *overlay = iter->data;
int child_min_size = 0;
int child_nat_size = 0;
gtk_widget_measure (overlay->widget,
orientation,
for_size,
&child_min_size,
&child_nat_size,
NULL,
NULL);
if (child_min_size > real_min_size)
real_min_size = child_min_size;
if (child_nat_size > real_nat_size)
real_nat_size = child_nat_size;
}
if (min_size)
*min_size = real_min_size;
if (nat_size)
*nat_size = real_nat_size;
if (min_baseline)
*min_baseline = -1;
if (nat_baseline)
*nat_baseline = -1;
}
static void
gtk_text_view_child_size_allocate (GtkWidget *widget,
int width,
int height,
int baseline)
{
GtkTextViewChild *self = GTK_TEXT_VIEW_CHILD (widget);
GtkRequisition min_req;
GdkRectangle rect;
const GList *iter;
GTK_WIDGET_CLASS (gtk_text_view_child_parent_class)->size_allocate (widget, width, height, baseline);
if (self->child != NULL)
{
rect.x = 0;
rect.y = 0;
rect.width = width;
rect.height = height;
gtk_widget_size_allocate (self->child, &rect, baseline);
}
for (iter = self->overlays.head; iter; iter = iter->next)
{
Overlay *overlay = iter->data;
gtk_widget_get_preferred_size (overlay->widget, &min_req, NULL);
rect.width = min_req.width;
rect.height = min_req.height;
if (self->window_type == GTK_TEXT_WINDOW_TEXT ||
self->window_type == GTK_TEXT_WINDOW_TOP ||
self->window_type == GTK_TEXT_WINDOW_BOTTOM)
rect.x = overlay->x - self->xoffset;
else
rect.x = overlay->x;
if (self->window_type == GTK_TEXT_WINDOW_TEXT ||
self->window_type == GTK_TEXT_WINDOW_RIGHT ||
self->window_type == GTK_TEXT_WINDOW_LEFT)
rect.y = overlay->y - self->yoffset;
else
rect.y = overlay->y;
gtk_widget_size_allocate (overlay->widget, &rect, -1);
}
}
static void
gtk_text_view_child_snapshot (GtkWidget *widget,
GtkSnapshot *snapshot)
{
GtkTextViewChild *self = GTK_TEXT_VIEW_CHILD (widget);
const GList *iter;
GTK_WIDGET_CLASS (gtk_text_view_child_parent_class)->snapshot (widget, snapshot);
if (self->child)
gtk_widget_snapshot_child (widget, self->child, snapshot);
for (iter = self->overlays.head; iter; iter = iter->next)
{
Overlay *overlay = iter->data;
gtk_widget_snapshot_child (widget, overlay->widget, snapshot);
}
}
static void
gtk_text_view_child_constructed (GObject *object)
{
GtkTextViewChild *self = GTK_TEXT_VIEW_CHILD (object);
GtkCssNode *css_node;
G_OBJECT_CLASS (gtk_text_view_child_parent_class)->constructed (object);
css_node = gtk_widget_get_css_node (GTK_WIDGET (self));
switch (self->window_type)
{
case GTK_TEXT_WINDOW_LEFT:
gtk_css_node_set_name (css_node, g_quark_from_static_string ("border"));
gtk_css_node_add_class (css_node, g_quark_from_static_string ("left"));
break;
case GTK_TEXT_WINDOW_RIGHT:
gtk_css_node_set_name (css_node, g_quark_from_static_string ("border"));
gtk_css_node_add_class (css_node, g_quark_from_static_string ("right"));
break;
case GTK_TEXT_WINDOW_TOP:
gtk_css_node_set_name (css_node, g_quark_from_static_string ("border"));
gtk_css_node_add_class (css_node, g_quark_from_static_string ("top"));
break;
case GTK_TEXT_WINDOW_BOTTOM:
gtk_css_node_set_name (css_node, g_quark_from_static_string ("border"));
gtk_css_node_add_class (css_node, g_quark_from_static_string ("bottom"));
break;
case GTK_TEXT_WINDOW_TEXT:
gtk_css_node_set_name (css_node, g_quark_from_static_string ("child"));
break;
case GTK_TEXT_WINDOW_WIDGET:
default:
break;
}
}
static void
gtk_text_view_child_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkTextViewChild *self = GTK_TEXT_VIEW_CHILD (object);
switch (prop_id)
{
case PROP_WINDOW_TYPE:
g_value_set_enum (value, self->window_type);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
gtk_text_view_child_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkTextViewChild *self = GTK_TEXT_VIEW_CHILD (object);
switch (prop_id)
{
case PROP_WINDOW_TYPE:
self->window_type = g_value_get_enum (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
gtk_text_view_child_dispose (GObject *object)
{
GtkTextViewChild *self = GTK_TEXT_VIEW_CHILD (object);
GtkWidget *child;
while ((child = gtk_widget_get_first_child (GTK_WIDGET (self))))
gtk_text_view_child_remove (self, child);
G_OBJECT_CLASS (gtk_text_view_child_parent_class)->dispose (object);
}
static void
gtk_text_view_child_class_init (GtkTextViewChildClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
object_class->dispose = gtk_text_view_child_dispose;
object_class->constructed = gtk_text_view_child_constructed;
object_class->get_property = gtk_text_view_child_get_property;
object_class->set_property = gtk_text_view_child_set_property;
widget_class->measure = gtk_text_view_child_measure;
widget_class->size_allocate = gtk_text_view_child_size_allocate;
widget_class->snapshot = gtk_text_view_child_snapshot;
/**
* GtkTextViewChild:window-type:
*
* The "window-type" property is the #GtkTextWindowType of the
* #GtkTextView that the child is attached.
*/
properties[PROP_WINDOW_TYPE] =
g_param_spec_enum ("window-type",
P_("Window Type"),
P_("The GtkTextWindowType"),
GTK_TYPE_TEXT_WINDOW_TYPE,
GTK_TEXT_WINDOW_TEXT,
GTK_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
gtk_text_view_child_init (GtkTextViewChild *self)
{
self->window_type = GTK_TEXT_WINDOW_TEXT;
gtk_widget_set_overflow (GTK_WIDGET (self), GTK_OVERFLOW_HIDDEN);
}
GtkWidget *
gtk_text_view_child_new (GtkTextWindowType window_type)
{
g_return_val_if_fail (window_type == GTK_TEXT_WINDOW_LEFT ||
window_type == GTK_TEXT_WINDOW_RIGHT ||
window_type == GTK_TEXT_WINDOW_TOP ||
window_type == GTK_TEXT_WINDOW_BOTTOM ||
window_type == GTK_TEXT_WINDOW_TEXT,
NULL);
return g_object_new (GTK_TYPE_TEXT_VIEW_CHILD,
"window-type", window_type,
NULL);
}
void
gtk_text_view_child_add_overlay (GtkTextViewChild *self,
GtkWidget *widget,
int xpos,
int ypos)
{
Overlay *overlay;
g_return_if_fail (GTK_IS_TEXT_VIEW_CHILD (self));
g_return_if_fail (GTK_IS_WIDGET (widget));
overlay = overlay_new (widget, xpos, ypos);
g_queue_push_tail (&self->overlays, &overlay->link);
gtk_widget_set_parent (widget, GTK_WIDGET (self));
}
void
gtk_text_view_child_move_overlay (GtkTextViewChild *self,
GtkWidget *widget,
int xpos,
int ypos)
{
Overlay *overlay;
g_return_if_fail (GTK_IS_TEXT_VIEW_CHILD (self));
g_return_if_fail (GTK_IS_WIDGET (widget));
overlay = gtk_text_view_child_get_overlay (self, widget);
if (overlay != NULL)
{
overlay->x = xpos;
overlay->y = ypos;
if (gtk_widget_get_visible (GTK_WIDGET (self)) &&
gtk_widget_get_visible (widget))
gtk_widget_queue_allocate (GTK_WIDGET (self));
}
}
GtkTextWindowType
gtk_text_view_child_get_window_type (GtkTextViewChild *self)
{
g_return_val_if_fail (GTK_IS_TEXT_VIEW_CHILD (self), 0);
return self->window_type;
}
void
gtk_text_view_child_set_offset (GtkTextViewChild *self,
int xoffset,
int yoffset)
{
gboolean changed = FALSE;
g_return_if_fail (GTK_IS_TEXT_VIEW_CHILD (self));
if (self->window_type == GTK_TEXT_WINDOW_TEXT ||
self->window_type == GTK_TEXT_WINDOW_TOP ||
self->window_type == GTK_TEXT_WINDOW_BOTTOM)
{
if (self->xoffset != xoffset)
{
self->xoffset = xoffset;
changed = TRUE;
}
}
if (self->window_type == GTK_TEXT_WINDOW_TEXT ||
self->window_type == GTK_TEXT_WINDOW_LEFT ||
self->window_type == GTK_TEXT_WINDOW_RIGHT)
{
if (self->yoffset != yoffset)
{
self->yoffset = yoffset;
changed = TRUE;
}
}
if (changed)
gtk_widget_queue_draw (GTK_WIDGET (self));
}