/*
* Copyright © 2016 Red Hat, Inc.
*
* This program 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 program 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 program; if not, see .
*
* Author: Carlos Garnacho
*/
#include "config.h"
#include
#include "cc-drawing-area.h"
typedef struct _CcDrawingArea CcDrawingArea;
struct _CcDrawingArea {
GtkEventBox parent;
GdkDevice *current_device;
cairo_surface_t *surface;
cairo_t *cr;
};
G_DEFINE_TYPE (CcDrawingArea, cc_drawing_area, GTK_TYPE_EVENT_BOX)
static void
ensure_drawing_surface (CcDrawingArea *area,
gint width,
gint height)
{
if (!area->surface ||
cairo_image_surface_get_width (area->surface) != width ||
cairo_image_surface_get_height (area->surface) != height) {
cairo_surface_t *surface;
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
width, height);
if (area->surface) {
cairo_t *cr;
cr = cairo_create (surface);
cairo_set_source_surface (cr, area->surface, 0, 0);
cairo_paint (cr);
cairo_surface_destroy (area->surface);
cairo_destroy (area->cr);
cairo_destroy (cr);
}
area->surface = surface;
area->cr = cairo_create (surface);
}
}
static void
cc_drawing_area_size_allocate (GtkWidget *widget,
GtkAllocation *allocation)
{
CcDrawingArea *area = CC_DRAWING_AREA (widget);
ensure_drawing_surface (area, allocation->width, allocation->height);
GTK_WIDGET_CLASS (cc_drawing_area_parent_class)->size_allocate (widget,
allocation);
}
static void
cc_drawing_area_map (GtkWidget *widget)
{
GtkAllocation allocation;
GTK_WIDGET_CLASS (cc_drawing_area_parent_class)->map (widget);
gtk_widget_get_allocation (widget, &allocation);
ensure_drawing_surface (CC_DRAWING_AREA (widget),
allocation.width, allocation.height);
}
static void
cc_drawing_area_unmap (GtkWidget *widget)
{
CcDrawingArea *area = CC_DRAWING_AREA (widget);
if (area->cr) {
cairo_destroy (area->cr);
area->cr = NULL;
}
if (area->surface) {
cairo_surface_destroy (area->surface);
area->surface = NULL;
}
GTK_WIDGET_CLASS (cc_drawing_area_parent_class)->unmap (widget);
}
static gboolean
cc_drawing_area_draw (GtkWidget *widget,
cairo_t *cr)
{
CcDrawingArea *area = CC_DRAWING_AREA (widget);
GtkAllocation allocation;
GTK_WIDGET_CLASS (cc_drawing_area_parent_class)->draw (widget, cr);
gtk_widget_get_allocation (widget, &allocation);
cairo_set_source_rgb (cr, 1, 1, 1);
cairo_paint (cr);
cairo_set_source_surface (cr, area->surface, 0, 0);
cairo_paint (cr);
cairo_set_source_rgb (cr, 0.6, 0.6, 0.6);
cairo_rectangle (cr, 0, 0, allocation.width, allocation.height);
cairo_stroke (cr);
return FALSE;
}
static gboolean
cc_drawing_area_event (GtkWidget *widget,
GdkEvent *event)
{
CcDrawingArea *area = CC_DRAWING_AREA (widget);
GdkInputSource source;
GdkDeviceTool *tool;
GdkDevice *device;
device = gdk_event_get_source_device (event);
if (!device)
return GDK_EVENT_PROPAGATE;
source = gdk_device_get_source (device);
tool = gdk_event_get_device_tool (event);
if (source != GDK_SOURCE_PEN && source != GDK_SOURCE_ERASER)
return GDK_EVENT_PROPAGATE;
if (area->current_device && area->current_device != device)
return GDK_EVENT_PROPAGATE;
if (event->type == GDK_BUTTON_PRESS &&
event->button.button == 1 && !area->current_device) {
area->current_device = device;
} else if (event->type == GDK_BUTTON_RELEASE &&
event->button.button == 1 && area->current_device) {
cairo_new_path (area->cr);
area->current_device = NULL;
} else if (event->type == GDK_MOTION_NOTIFY &&
event->motion.state & GDK_BUTTON1_MASK) {
gdouble x, y, pressure;
gdk_event_get_coords (event, &x, &y);
gdk_event_get_axis (event, GDK_AXIS_PRESSURE, &pressure);
if (gdk_device_tool_get_tool_type (tool) == GDK_DEVICE_TOOL_TYPE_ERASER) {
cairo_set_line_width (area->cr, 10 * pressure);
cairo_set_operator (area->cr, CAIRO_OPERATOR_DEST_OUT);
} else {
cairo_set_line_width (area->cr, 4 * pressure);
cairo_set_operator (area->cr, CAIRO_OPERATOR_SATURATE);
}
cairo_set_source_rgba (area->cr, 0, 0, 0, pressure);
cairo_line_to (area->cr, x, y);
cairo_stroke (area->cr);
cairo_move_to (area->cr, x, y);
gtk_widget_queue_draw (widget);
return GDK_EVENT_STOP;
}
return GDK_EVENT_PROPAGATE;
}
static void
cc_drawing_area_class_init (CcDrawingAreaClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
widget_class->size_allocate = cc_drawing_area_size_allocate;
widget_class->draw = cc_drawing_area_draw;
widget_class->event = cc_drawing_area_event;
widget_class->map = cc_drawing_area_map;
widget_class->unmap = cc_drawing_area_unmap;
}
static void
cc_drawing_area_init (CcDrawingArea *area)
{
gtk_event_box_set_above_child (GTK_EVENT_BOX (area), TRUE);
gtk_widget_add_events (GTK_WIDGET (area),
GDK_BUTTON_PRESS_MASK |
GDK_BUTTON_RELEASE_MASK |
GDK_POINTER_MOTION_MASK);
}
GtkWidget *
cc_drawing_area_new (void)
{
return g_object_new (CC_TYPE_DRAWING_AREA, NULL);
}