1 /*
2  * glade-dnd.c
3  *
4  * Copyright (C) 2013  Juan Pablo Ugarte
5  *
6  * Authors:
7  *   Juan Pablo Ugarte <juanpablougarte@gmail.com>
8  *
9  * This library is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as
11  * published by the Free Software Foundation; either version 2.1 of
12  * the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22  *
23  */
24 
25 #include "glade.h"
26 #include "glade-dnd.h"
27 
28 GtkTargetEntry *
_glade_dnd_get_target(void)29 _glade_dnd_get_target (void)
30 {
31   static GtkTargetEntry target = {GLADE_DND_TARGET_DATA, GTK_TARGET_SAME_APP, GLADE_DND_INFO_DATA};
32   return &target;
33 }
34 
35 void
_glade_dnd_dest_set(GtkWidget * target)36 _glade_dnd_dest_set (GtkWidget *target)
37 {
38   gtk_drag_dest_set (target, 0, _glade_dnd_get_target (), 1, GDK_ACTION_COPY);
39 }
40 
41 GObject *
_glade_dnd_get_data(GdkDragContext * context,GtkSelectionData * selection,guint info)42 _glade_dnd_get_data (GdkDragContext   *context,
43                      GtkSelectionData *selection,
44                      guint             info)
45 {
46   GdkAtom target = gtk_selection_data_get_target (selection);
47 
48   if (info == GLADE_DND_INFO_DATA &&
49       g_strcmp0 (gdk_atom_name (target), GLADE_DND_TARGET_DATA) == 0)
50     {
51       const guchar *data = gtk_selection_data_get_data (selection);
52       if (data)
53         return *((GObject **)data);
54     }
55   return NULL;
56 }
57 
58 
59 void
_glade_dnd_set_data(GtkSelectionData * selection,GObject * data)60 _glade_dnd_set_data (GtkSelectionData *selection, GObject *data)
61 {
62   static GdkAtom type = 0;
63 
64   if (!type)
65     type = gdk_atom_intern_static_string (GLADE_DND_TARGET_DATA);
66 
67   gtk_selection_data_set (selection, type, sizeof (gpointer),
68                           (const guchar *)&data,
69                           sizeof (gpointer));
70 }
71 
72 static gboolean
on_drag_icon_draw(GtkWidget * widget,cairo_t * cr)73 on_drag_icon_draw (GtkWidget *widget, cairo_t *cr)
74 {
75   GtkStyleContext *context = gtk_widget_get_style_context (widget);
76   cairo_pattern_t *gradient;
77   GtkAllocation alloc;
78   gint x, y, w, h;
79   gdouble h2;
80   GdkRGBA bg;
81 
82   /* Not needed acording to GtkWidget:draw documentation
83    * But seems like there is a bug when used as a drag_icon that makes the
84    * cairo translation used here persist when drawing children.
85    */
86   cairo_save (cr);
87 
88   /* Clear BG */
89   cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
90   cairo_paint (cr);
91   cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
92 
93   gtk_widget_get_allocation (widget, &alloc);
94   x = alloc.x;
95   y = alloc.y;
96   w = alloc.width;
97   h = alloc.height;
98   h2 = h/2.0;
99 
100   gtk_style_context_get_background_color (context, gtk_style_context_get_state (context), &bg);
101 
102   gradient = cairo_pattern_create_linear (x, y, x, y+h);
103   cairo_pattern_add_color_stop_rgba (gradient, 0, bg.red, bg.green, bg.blue, 0);
104   cairo_pattern_add_color_stop_rgba (gradient, .5, bg.red, bg.green, bg.blue, .8);
105   cairo_pattern_add_color_stop_rgba (gradient, 1, bg.red, bg.green, bg.blue, 0);
106 
107   cairo_set_source (cr, gradient);
108   cairo_rectangle (cr, x+h2, y, w-h, h);
109   cairo_fill (cr);
110   cairo_pattern_destroy (gradient);
111 
112   gradient = cairo_pattern_create_radial (x+h2, y+h2, 0, x+h2, y+h2, h2);
113   cairo_pattern_add_color_stop_rgba (gradient, 0, bg.red, bg.green, bg.blue, .8);
114   cairo_pattern_add_color_stop_rgba (gradient, 1, bg.red, bg.green, bg.blue, 0);
115 
116   cairo_set_source (cr, gradient);
117   cairo_rectangle (cr, x, y, h2, h);
118   cairo_fill (cr);
119 
120   cairo_translate (cr, w-h, 0);
121   cairo_set_source (cr, gradient);
122   cairo_rectangle (cr, x+h2, y, h2, h);
123   cairo_fill (cr);
124 
125   cairo_pattern_destroy (gradient);
126   cairo_restore (cr);
127 
128   return FALSE;
129 }
130 
131 void
_glade_dnd_set_icon_widget(GdkDragContext * context,const gchar * icon_name,const gchar * description)132 _glade_dnd_set_icon_widget (GdkDragContext *context,
133                             const gchar *icon_name,
134                             const gchar *description)
135 {
136   GtkWidget *window, *box, *label, *icon;
137   GdkScreen *screen;
138   GdkVisual *visual;
139 
140   screen = gdk_window_get_screen (gdk_drag_context_get_source_window (context));
141   window = gtk_window_new (GTK_WINDOW_POPUP);
142 
143   gtk_window_set_type_hint (GTK_WINDOW (window), GDK_WINDOW_TYPE_HINT_DND);
144   gtk_window_set_screen (GTK_WINDOW (window), screen);
145 
146   box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
147   gtk_container_set_border_width (GTK_CONTAINER (box), 12);
148 
149   icon = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_BUTTON);
150   gtk_widget_set_opacity (icon, .8);
151 
152   label = gtk_label_new (description);
153 
154   gtk_box_pack_start (GTK_BOX (box), icon, FALSE, TRUE, 0);
155   gtk_box_pack_start (GTK_BOX (box), label, FALSE, TRUE, 0);
156 
157   gtk_widget_show_all (box);
158   gtk_container_add (GTK_CONTAINER (window), box);
159 
160   if ((visual = gdk_screen_get_rgba_visual (screen)))
161     {
162       gtk_widget_set_visual (window, visual);
163       gtk_widget_set_app_paintable (window, TRUE);
164       g_signal_connect (window, "draw", G_CALLBACK (on_drag_icon_draw), NULL);
165     }
166 
167   g_object_ref_sink (window);
168   gtk_drag_set_icon_widget (context, window, 0, 0);
169   g_object_unref (window);
170 }
171