1/*
2 * Copyright (C) 2011 Igalia S.L.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "DragIcon.h"
28#include "GdkCairoUtilities.h"
29
30#include <gtk/gtk.h>
31
32namespace WebCore {
33
34#ifdef GTK_API_VERSION_2
35static gboolean dragIconWindowDrawEventCallback(GtkWidget*, GdkEventExpose* event, DragIcon* icon)
36{
37    RefPtr<cairo_t> context = adoptRef(gdk_cairo_create(event->window));
38    icon->draw(context.get());
39    return TRUE;
40}
41#else
42static gboolean dragIconWindowDrawEventCallback(GtkWidget*, cairo_t* context, DragIcon* icon)
43{
44    if (!gdk_cairo_get_clip_rectangle(context, 0))
45        return FALSE;
46    icon->draw(context);
47    return TRUE;
48}
49#endif // GTK_API_VERSION_2
50
51DragIcon::DragIcon()
52    : isComposited(gdk_screen_is_composited(gdk_screen_get_default()))
53    , m_window(0)
54{
55    if (!isComposited)
56        return;
57
58    m_window = gtk_window_new(GTK_WINDOW_POPUP);
59#ifdef GTK_API_VERSION_2
60    g_signal_connect(m_window, "expose-event", G_CALLBACK(dragIconWindowDrawEventCallback), this);
61#else
62    g_signal_connect(m_window, "draw", G_CALLBACK(dragIconWindowDrawEventCallback), this);
63#endif
64
65    // This strategy originally comes from Chromium: src/chrome/browser/gtk/tab_contents_drag_source.cc
66    GdkScreen* screen = gtk_widget_get_screen(m_window);
67#ifdef GTK_API_VERSION_2
68    GdkColormap* rgba = gdk_screen_get_rgba_colormap(screen);
69    if (rgba)
70        gtk_widget_set_colormap(m_window, rgba);
71#else
72    GdkVisual* visual = gdk_screen_get_rgba_visual(screen);
73    if (!visual)
74        visual = gdk_screen_get_system_visual(screen);
75    gtk_widget_set_visual(m_window, visual);
76#endif // GTK_API_VERSION_2
77}
78
79DragIcon::~DragIcon()
80{
81    if (m_window)
82        gtk_widget_destroy(m_window);
83}
84
85void DragIcon::draw(cairo_t* context)
86{
87    cairo_rectangle(context, 0, 0,
88                    cairo_image_surface_get_width(m_image.get()),
89                    cairo_image_surface_get_height(m_image.get()));
90    cairo_set_operator(context, CAIRO_OPERATOR_SOURCE);
91    cairo_set_source_surface(context, m_image.get(), 0, 0);
92    cairo_fill(context);
93}
94
95void DragIcon::setImage(cairo_surface_t* image)
96{
97    ASSERT(image);
98    m_image = image;
99    m_imageSize = IntSize(cairo_image_surface_get_width(image), cairo_image_surface_get_height(image));
100    if (isComposited) {
101        gtk_window_resize(GTK_WINDOW(m_window), m_imageSize.width(), m_imageSize.height());
102        return;
103    }
104
105#ifdef GTK_API_VERSION_2
106    m_pixbuf = adoptGRef(cairoImageSurfaceToGdkPixbuf(image));
107#endif
108}
109
110void DragIcon::useForDrag(GdkDragContext* context)
111{
112    IntPoint hotspot(m_imageSize);
113    hotspot.scale(0.5, 0.5);
114    useForDrag(context, hotspot);
115}
116
117void DragIcon::useForDrag(GdkDragContext* context, const IntPoint& hotspot)
118{
119    if (isComposited) {
120        gtk_drag_set_icon_widget(context, m_window, hotspot.x(), hotspot.y());
121        return;
122    }
123#ifdef GTK_API_VERSION_2
124    gtk_drag_set_icon_pixbuf(context, m_pixbuf.get(), hotspot.x(), hotspot.y());
125#else
126    gtk_drag_set_icon_surface(context, m_image.get());
127#endif
128}
129
130} // namespace WebCore
131