/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * Copyright (C) 2005 Imendio AB * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU 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 * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * Author: * Christian Kellner */ /* * lb-frame.c */ #include #include #include #include #include "lb-frame.h" enum { PROP_0, PROP_RADIUS, PROP_FRAME, PROP_FRAME_COLOR, PROP_FRAME_ALPHA, PROP_FILL, PROP_FILL_COLOR, PROP_FILL_ALPHA }; typedef struct { GtkAllocation child_alloc; gdouble radius; gboolean draw_frame; GdkColor frame_color; guint16 frame_alpha; gboolean fill; GdkColor fill_color; guint16 fill_alpha; } LbFramePriv; static void lb_frame_finalize (GObject *object); static void lb_frame_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void lb_frame_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static gboolean frame_expose (GtkWidget *widget, GdkEventExpose *event); static void frame_size_request (GtkWidget *widget, GtkRequisition *requisition); static void frame_size_allocate (GtkWidget *widget, GtkAllocation *allocation); G_DEFINE_TYPE (LbFrame, lb_frame, GTK_TYPE_BIN); #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), LB_TYPE_FRAME, LbFramePriv)) static void lb_frame_class_init (LbFrameClass *klass) { GObjectClass *object_class; GtkWidgetClass *widget_class; object_class = G_OBJECT_CLASS (klass); widget_class = GTK_WIDGET_CLASS (klass); object_class->finalize = lb_frame_finalize; object_class->set_property = lb_frame_set_property; object_class->get_property = lb_frame_get_property; widget_class->expose_event = frame_expose; widget_class->size_request = frame_size_request; widget_class->size_allocate = frame_size_allocate; g_type_class_add_private (klass, sizeof (LbFramePriv)); g_object_class_install_property ( object_class, PROP_RADIUS, g_param_spec_double ("radius", NULL, NULL, -1.0, 90.0, 10.0, G_PARAM_READWRITE)); g_object_class_install_property ( object_class, PROP_FRAME, g_param_spec_boolean ("frame", NULL, NULL, FALSE, G_PARAM_READWRITE)); g_object_class_install_property ( object_class, PROP_FRAME_COLOR, g_param_spec_boxed ("frame-color", NULL, NULL, GDK_TYPE_COLOR, G_PARAM_READWRITE)); g_object_class_install_property ( object_class, PROP_FRAME_ALPHA, g_param_spec_uint ("frame-alpha", NULL, NULL, 0, G_MAXUINT16, G_MAXUINT16, G_PARAM_READWRITE)); g_object_class_install_property ( object_class, PROP_FILL, g_param_spec_boolean ("fill", NULL, NULL, FALSE, G_PARAM_READWRITE)); g_object_class_install_property ( object_class, PROP_FILL_COLOR, g_param_spec_boxed ("fill-color", NULL, NULL, GDK_TYPE_COLOR, G_PARAM_READWRITE)); g_object_class_install_property ( object_class, PROP_FILL_ALPHA, g_param_spec_uint ("fill-alpha", NULL, NULL, 0, G_MAXUINT16, G_MAXUINT16, G_PARAM_READWRITE)); } static void lb_frame_init (LbFrame *frame) { LbFramePriv *priv = GET_PRIV (frame); priv->fill = FALSE; priv->fill_alpha = G_MAXUINT16; priv->draw_frame = FALSE; priv->frame_alpha = G_MAXUINT16; priv->radius = -1.0; } static void lb_frame_finalize (GObject *object) { G_OBJECT_CLASS (lb_frame_parent_class)->finalize (object); } static void lb_frame_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { LbFrame *frame; gboolean vbool; guint16 alpha; gdouble radius; GdkColor *color; frame = LB_FRAME (object); switch (property_id) { case PROP_RADIUS: radius = g_value_get_double (value); /* FIXME: implement me */ /* lb_frame_set_radius (frame, radius); */ break; case PROP_FRAME: vbool = g_value_get_boolean (value); lb_frame_set_frame (frame, vbool); break; case PROP_FRAME_COLOR: color = g_value_get_boxed (value); lb_frame_set_frame_color (frame, color); break; case PROP_FRAME_ALPHA: alpha = g_value_get_uint (value); lb_frame_set_frame_alpha (frame, alpha); break; case PROP_FILL: vbool = g_value_get_boolean (value); lb_frame_set_fill (frame, vbool); break; case PROP_FILL_COLOR: color = g_value_get_boxed (value); lb_frame_set_fill_color (frame, color); break; case PROP_FILL_ALPHA: alpha = g_value_get_uint (value); lb_frame_set_fill_alpha (frame, alpha); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void lb_frame_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { LbFramePriv *priv = GET_PRIV (object); LbFrame *frame; GdkColor color; frame = LB_FRAME (object); switch (property_id) { case PROP_RADIUS: g_value_set_double (value, priv->radius); break; case PROP_FRAME: g_value_set_boolean (value, priv->draw_frame); break; case PROP_FRAME_COLOR: color.red = priv->frame_color.red; color.green = priv->frame_color.green; color.blue = priv->frame_color.blue; g_value_set_boxed (value, &color); break; case PROP_FRAME_ALPHA: g_value_set_uint (value, priv->frame_alpha); break; case PROP_FILL: g_value_set_boolean (value, priv->fill); break; case PROP_FILL_COLOR: color.red = priv->fill_color.red; color.green = priv->fill_color.green; color.blue = priv->fill_color.blue; g_value_set_boxed (value, &color); break; case PROP_FILL_ALPHA: g_value_set_uint (value, priv->fill_alpha); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void cc_rectangle_re (cairo_t *cr, double x, double y, double width, double height, double radius) { double cx, cy; width -= 2 * radius; height -= 2 * radius; cairo_move_to (cr, x + radius, y); cairo_rel_line_to (cr, width, 0.0); cairo_get_current_point (cr, &cx, &cy); cairo_arc (cr, cx, cy + radius, radius, 3.0 * G_PI_2, 0); cairo_rel_line_to (cr, 0.0, height); cairo_get_current_point (cr, &cx, &cy); cairo_arc (cr, cx - radius, cy, radius, 0, G_PI_2); cairo_rel_line_to (cr, - width, 0.0); cairo_get_current_point (cr, &cx, &cy); cairo_arc (cr, cx, cy - radius, radius, G_PI_2, G_PI); cairo_rel_line_to (cr, 0.0, -height); cairo_get_current_point (cr, &cx, &cy); cairo_arc (cr, cx + radius, cy, radius, G_PI, 3.0 * G_PI_2); cairo_close_path (cr); } static void frame_paint (GtkWidget *widget, GdkRectangle *area) { LbFramePriv *priv = GET_PRIV (widget); LbFrame *frame; cairo_t *cr; gint x, y; gint width, height; gdouble radius; frame = LB_FRAME (widget); if (!GTK_WIDGET_DRAWABLE (widget)) { return; } if (!priv->draw_frame && !priv->fill) { /* Nothing to draw. */ return; } x = priv->child_alloc.x - widget->style->xthickness; y = priv->child_alloc.y - widget->style->ythickness; width = priv->child_alloc.width + 2 * widget->style->xthickness; height = priv->child_alloc.height + 2 * widget->style->ythickness; if (priv->radius < 0.0) { radius = MIN (width, height); radius = (radius / 100) * 10; priv->radius = radius; } else { radius = priv->radius; } cr = gdk_cairo_create (widget->window); cairo_rectangle (cr, x, y, width, height); cairo_clip (cr); cc_rectangle_re (cr, x, y, width, height, radius); cairo_set_operator (cr, CAIRO_OPERATOR_OVER); if (priv->fill) { double r, g, b, a; r = (double) priv->fill_color.red / G_MAXUINT16; g = (double) priv->fill_color.green / G_MAXUINT16; b = (double) priv->fill_color.blue / G_MAXUINT16; a = (double) priv->fill_alpha / G_MAXUINT16; cairo_set_source_rgba (cr, r, g, b, a); cairo_fill_preserve (cr); } if (priv->draw_frame) { double r, g, b, a; r = (double) priv->frame_color.red / G_MAXUINT16; g = (double) priv->frame_color.green / G_MAXUINT16; b = (double) priv->frame_color.blue / G_MAXUINT16; a = (double) priv->frame_alpha / G_MAXUINT16; cairo_set_source_rgba (cr, r, g, b, a); cairo_stroke (cr); } cairo_destroy (cr); } static gboolean frame_expose (GtkWidget *widget, GdkEventExpose *event) { if (GTK_WIDGET_DRAWABLE (widget)) { frame_paint (widget, &event->area); GTK_WIDGET_CLASS (lb_frame_parent_class)->expose_event (widget, event); } return FALSE; } static void frame_size_request (GtkWidget *widget, GtkRequisition *requisition) { GtkBin *bin; GtkRequisition cr; bin = GTK_BIN (widget); requisition->width = 0; requisition->height = 0; if (bin->child && GTK_WIDGET_VISIBLE (bin->child)) { gtk_widget_size_request (bin->child, &cr); requisition->width = MAX (requisition->width, cr.width); requisition->height += cr.height; } requisition->width += (GTK_CONTAINER (widget)->border_width + GTK_WIDGET (widget)->style->xthickness) * 2; requisition->height += (GTK_CONTAINER (widget)->border_width + GTK_WIDGET (widget)->style->ythickness) * 2; } static void frame_size_allocate (GtkWidget *widget, GtkAllocation *allocation) { LbFramePriv *priv = GET_PRIV (widget); LbFrame *frame; GtkBin *bin; GtkAllocation new_alloc; frame = LB_FRAME (widget); bin = GTK_BIN (widget); widget->allocation = *allocation; new_alloc.x = (GTK_CONTAINER (widget)->border_width + widget->style->xthickness); new_alloc.width = MAX (1, ((gint) allocation->width - new_alloc.x * 2)); new_alloc.y = (GTK_CONTAINER (widget)->border_width + widget->style->ythickness); new_alloc.height = MAX (1, ((gint) allocation->height - new_alloc.y - (gint) GTK_CONTAINER (frame)->border_width - (gint) widget->style->ythickness)); new_alloc.x += allocation->x; new_alloc.y += allocation->y; if (GTK_WIDGET_MAPPED (widget) && (new_alloc.x != priv->child_alloc.x || new_alloc.y != priv->child_alloc.y || new_alloc.width != priv->child_alloc.width || new_alloc.height != priv->child_alloc.height)) { gdk_window_invalidate_rect (widget->window, &widget->allocation, FALSE); } if (bin->child && GTK_WIDGET_VISIBLE (bin->child)) { gtk_widget_size_allocate (bin->child, &new_alloc); } priv->child_alloc = new_alloc; } GtkWidget * lb_frame_new (void) { GtkWidget *frame; frame = g_object_new (LB_TYPE_FRAME, NULL); return frame; } void lb_frame_set_fill (LbFrame *frame, gboolean fill) { LbFramePriv *priv = GET_PRIV (frame); g_return_if_fail (LB_IS_FRAME (frame)); if (priv->fill == fill) { return; } priv->fill = fill; if (GTK_WIDGET_DRAWABLE (frame)) { gtk_widget_queue_draw (GTK_WIDGET (frame)); } } void lb_frame_set_fill_color (LbFrame *frame, GdkColor *color) { LbFramePriv *priv = GET_PRIV (frame); g_return_if_fail (LB_IS_FRAME (frame)); if (priv->fill_color.red == color->red && priv->fill_color.green == color->green && priv->fill_color.blue == color->blue) { return; } priv->fill_color.red = color->red; priv->fill_color.green = color->green; priv->fill_color.blue = color->blue; if (GTK_WIDGET_DRAWABLE (frame)) { gtk_widget_queue_draw (GTK_WIDGET (frame)); } } void lb_frame_set_fill_alpha (LbFrame *frame, guint16 alpha) { LbFramePriv *priv = GET_PRIV (frame); g_return_if_fail (LB_IS_FRAME (frame)); if (priv->fill_alpha == alpha) { return; } priv->fill_alpha = alpha; if (GTK_WIDGET_DRAWABLE (frame)) { gtk_widget_queue_draw (GTK_WIDGET (frame)); } } void lb_frame_set_frame (LbFrame *frame, gboolean draw_frame) { LbFramePriv *priv = GET_PRIV (frame); g_return_if_fail (LB_IS_FRAME (frame)); if (priv->draw_frame == draw_frame) { return; } priv->draw_frame = draw_frame; if (GTK_WIDGET_DRAWABLE (frame)) { gtk_widget_queue_draw (GTK_WIDGET (frame)); } } void lb_frame_set_frame_color (LbFrame *frame, GdkColor *color) { LbFramePriv *priv = GET_PRIV (frame); g_return_if_fail (LB_IS_FRAME (frame)); if (priv->frame_color.red == color->red && priv->frame_color.green == color->green && priv->frame_color.blue == color->blue) { return; } priv->frame_color.red = color->red; priv->frame_color.green = color->green; priv->frame_color.blue = color->blue; if (GTK_WIDGET_DRAWABLE (frame)) { gtk_widget_queue_draw (GTK_WIDGET (frame)); } } void lb_frame_set_frame_alpha (LbFrame *frame, guint16 alpha) { LbFramePriv *priv = GET_PRIV (frame); g_return_if_fail (LB_IS_FRAME (frame)); if (priv->frame_alpha == alpha) { return; } priv->frame_alpha = alpha; if (GTK_WIDGET_DRAWABLE (frame)) { gtk_widget_queue_draw (GTK_WIDGET (frame)); } }