1 /* Copyright (C) 2008 The goocanvasmm Development Team
2  *
3  * This library is free software; you can redistribute it and/or
4  * modify it under the terms of the GNU Lesser General Public
5  * License as published by the Free Software Foundation; either
6  * version 2.1 of the License, or (at your option) any later version.
7  *
8  * This library is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public
14  * License along with this library; if not, write to the Free
15  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16  */
17 
18 #include "examplewindow.h"
19 #include <iostream>
20 
21 const int DRAG_DATA_FORMAT = 8; // 8 bits format
22 
ExampleWindow()23 ExampleWindow::ExampleWindow()
24 : m_vbox(false, 6),
25   m_hbox(false, 6),
26   m_button_rect("Rectangle"),
27   m_button_ellipse("Ellipse"),
28   m_drag_preview_requested(false)
29 {
30   set_title("goocanvasmm - Simple Example");
31 
32   m_drag_targets.push_back( Gtk::TargetEntry("goocanvasmm_example_drag_item", Gtk::TARGET_SAME_APP) );
33 
34   add(m_vbox);
35   m_vbox.pack_start(m_hbox, Gtk::PACK_SHRINK);
36 
37   m_hbox.pack_start(m_button_rect, Gtk::PACK_SHRINK);
38   make_widget_draggable(m_button_rect, DRAG_ITEM_RECTANGLE);
39   m_hbox.pack_start(m_button_ellipse, Gtk::PACK_SHRINK);
40   make_widget_draggable(m_button_ellipse, DRAG_ITEM_ELLIPSE);
41 
42   m_canvas.set_size_request(640, 480);
43   m_canvas.set_bounds(0, 0, 1000, 1000);
44 
45 
46   Gtk::ScrolledWindow* sw = Gtk::manage(new Gtk::ScrolledWindow());
47   sw->add(m_canvas);
48   m_vbox.pack_start(*sw);
49 
50   // Make the canvas a drag-and-drop destination. As we are implementing
51   // custom handlers for all (motion, highlight, drop), we set the second
52   // argument of drag_dest_set to 0. See the documentation of
53   // drag_dest_set for details. It would be nice if Gtk::DestDefaults did
54   // contain a GTK_DEST_DEFAULT_NONE, but short of that we can still brute
55   // force it to 0.
56   m_canvas.drag_dest_set(m_drag_targets, (Gtk::DestDefaults)0, Gdk::ACTION_COPY);
57   m_canvas.signal_drag_motion().connect(
58       sigc::mem_fun(*this, &ExampleWindow::on_canvas_drag_motion) );
59   m_canvas.signal_drag_drop().connect(
60       sigc::mem_fun(*this, &ExampleWindow::on_canvas_drag_drop) );
61 
62   m_canvas.signal_drag_data_received().connect(
63       sigc::mem_fun(*this, &ExampleWindow::on_canvas_drag_data_received) );
64 
65   show_all_children();
66 }
67 
make_widget_draggable(Gtk::Widget & widget,DragItem drag_item)68 void ExampleWindow::make_widget_draggable(Gtk::Widget& widget, DragItem drag_item)
69 {
70   widget.drag_source_set(m_drag_targets, Gdk::BUTTON1_MASK, Gdk::ACTION_COPY);
71 
72   //Set the icon to be shown when dragging:
73   //Glib::RefPtr<Gdk::Pixbuf> pixbuf = get_icon_for_toolbar_item(*item);
74   //if(pixbuf)
75   //  widget.drag_source_set_icon(pixbuf);
76 
77   //widget.signal_drag_begin().connect(
78   //  sigc::mem_fun(*this, &ExampleWindow::on_toolbar_item_drag_begin) );
79 
80   //widget.signal_drag_end().connect(
81   //  sigc::mem_fun(*this, &ExampleWindow::on_toolbar_item_drag_end) );
82 
83   //Let the item supply some data when the destination asks for it:
84   widget.signal_drag_data_get().connect(
85     sigc::bind( sigc::mem_fun(*this, &ExampleWindow::on_button_drag_data_get), drag_item) );
86 }
87 
on_canvas_drag_drop(const Glib::RefPtr<Gdk::DragContext> & drag_context,int,int,guint timestamp)88 bool ExampleWindow::on_canvas_drag_drop(const Glib::RefPtr<Gdk::DragContext>& drag_context, int /* x */, int /* y */, guint timestamp)
89 {
90   std::cout << "ExampleWindow::on_canvas_drag_drop" << std::endl;
91   Glib::ustring target = m_canvas.drag_dest_find_target(drag_context);
92   if(target.empty())
93     return false;
94 
95   //Get the details, to create the appropriate canvas item:
96   //This will cause our drag_data_received callback to be called, with that information:
97   m_drag_preview_requested = false;
98   m_canvas.drag_get_data(drag_context, target, timestamp);
99   return true; //Allow the drop.
100 }
101 
102 
on_canvas_drag_motion(const Glib::RefPtr<Gdk::DragContext> & drag_context,int x,int y,guint timestamp)103 bool ExampleWindow::on_canvas_drag_motion(const Glib::RefPtr<Gdk::DragContext>& drag_context, int x, int y, guint timestamp)
104 {
105   std::cout << "ExampleWindow::on_canvas_drag_motion" << std::endl;
106   Glib::ustring target = m_canvas.drag_dest_find_target(drag_context);
107   if(target.empty())
108     return false;
109 
110   m_canvas.drag_highlight();
111 
112   //Create the temporary canvas item if necesary:
113   if(!m_layout_item_dropping)
114   {
115     std::cout << "  ExampleWindow::on_canvas_drag_motion(): Calling drag_get_data()" << std::endl;
116 
117     //TODO: This stops the drop (or any further motion events) from happening:
118 
119     //We need to examine the SelectionData:
120     //This will cause our drag_data_received callback to be called, with that information:
121     m_drag_preview_requested = true;
122     m_canvas.drag_get_data(drag_context, target, timestamp);
123     return true;
124   }
125 
126   std::cout << "  ExampleWindow::on_canvas_drag_motion(): item already created." << std::endl;
127 
128   drag_context->drag_status(Gdk::ACTION_COPY, timestamp);
129 
130   //Move the temporary canvas item to the new position:
131   double item_x = x;
132   double item_y = y;
133   m_canvas.convert_from_pixels(item_x, item_y);
134 
135   gdouble t_x, t_y, t_scale, t_rotation;
136   m_layout_item_dropping->get_simple_transform(t_x, t_y, t_scale, t_rotation);
137   m_layout_item_dropping->set_simple_transform(item_x, item_y, t_scale, t_rotation);
138 
139   return true; //Allow the drop.
140 }
141 
get_drag_item_from_selection_data(const Gtk::SelectionData & selection_data)142 ExampleWindow::DragItem ExampleWindow::get_drag_item_from_selection_data(const Gtk::SelectionData& selection_data)
143 {
144   DragItem item_type = DRAG_ITEM_NONE;
145   if((selection_data.get_length() >= 0) && (selection_data.get_format() == DRAG_DATA_FORMAT))
146   {
147     const guint8* data = selection_data.get_data();
148     if(data)
149       item_type = (DragItem)(data[0]);
150   }
151 
152   return item_type;
153 }
154 
on_canvas_drag_data_received(const Glib::RefPtr<Gdk::DragContext> & drag_context,int x,int y,const Gtk::SelectionData & selection_data,guint,guint timestamp)155 void ExampleWindow::on_canvas_drag_data_received(const Glib::RefPtr<Gdk::DragContext>& drag_context, int x, int y, const Gtk::SelectionData& selection_data, guint /* info */, guint timestamp)
156 {
157   std::cout << "ExampleWindow::on_canvas_drag_data_received" << std::endl;
158 
159   //This is called when an item is dropped on the canvas,
160   //or after our drag_motion handler has called drag_get_data()):
161 
162   //Discover what toolbar item was dragged:
163   const DragItem drag_item = get_drag_item_from_selection_data(selection_data);
164 
165 
166   //Create the temporary drag item if necessary:
167   if(m_drag_preview_requested && !m_layout_item_dropping)
168   {
169     m_layout_item_dropping = create_canvas_item(drag_item, x, y);
170   }
171   else
172   {
173     if(m_layout_item_dropping)
174       m_layout_item_dropping->remove();
175 
176     m_layout_item_dropping.reset();
177     create_canvas_item(drag_item, x, y);
178   }
179 
180   if(m_drag_preview_requested)
181   {
182     std::cout << "ExampleWindow::on_canvas_drag_data_received: m_drag_preview_requested" << std::endl;
183 
184     drag_context->drag_status(Gdk::ACTION_COPY, timestamp);
185     m_drag_preview_requested = false;
186   }
187   else
188   {
189     drag_context->drag_finish(false, false, timestamp);
190     m_canvas.drag_unhighlight();
191   }
192 }
193 
on_button_drag_data_get(const Glib::RefPtr<Gdk::DragContext> &,Gtk::SelectionData & selection_data,guint,guint,DragItem drag_item)194 void ExampleWindow::on_button_drag_data_get(const Glib::RefPtr<Gdk::DragContext>& /* drag_context */, Gtk::SelectionData& selection_data, guint /* info */, guint /* time */, DragItem drag_item)
195 {
196   std::cout << "ExampleWindow::on_button_drag_data_get" << std::endl;
197 
198   selection_data.set(selection_data.get_target(), DRAG_DATA_FORMAT,
199           (const guchar*)&drag_item,
200           1 /* 1 byte */);
201 }
202 
create_canvas_item(DragItem drag_item,int x,int y)203 Glib::RefPtr<Goocanvas::Item> ExampleWindow::create_canvas_item(DragItem drag_item, int x, int y)
204 {
205   Glib::RefPtr<Goocanvas::Item> result;
206 
207   if(drag_item == DRAG_ITEM_RECTANGLE)
208   {
209     Glib::RefPtr<Goocanvas::Rect> rect = Goocanvas::Rect::create(0, 0, 20, 20);
210 #ifdef GLIBMM_PROPERTIES_ENABLED
211     rect->property_line_width() = 10.0;
212     rect->property_stroke_color() = "yellow";
213     rect->property_fill_color() = "red";
214 #else
215     rect->set_property("line_width", 10.0);
216     rect->set_property("stroke_color", Glib::ustring("yellow"));
217     rect->set_property("fill_color", Glib::ustring("red"));
218 #endif //GLIBMM_PROPERTIES_ENABLED
219     result = rect;
220   }
221   else if(drag_item == DRAG_ITEM_ELLIPSE)
222   {
223     Glib::RefPtr<Goocanvas::Ellipse> ellipse = Goocanvas::Ellipse::create();
224 #ifdef GLIBMM_PROPERTIES_ENABLED
225     ellipse->property_line_width() = 10.0;
226     ellipse->property_radius_x() = 20.0;
227     ellipse->property_radius_y() = 20.0;
228     ellipse->property_stroke_color() = "yellow";
229     ellipse->property_fill_color() = "red";
230 #else
231     ellipse->set_property("line_width", 10.0);
232     ellipse->set_property("radius_x", 20.0);
233     ellipse->set_property("radius_y", 20.0);
234     ellipse->set_property("stroke_color", Glib::ustring("yellow"));
235     ellipse->set_property("fill_color", Glib::ustring("red"));
236 #endif //GLIBMM_PROPERTIES_ENABLED
237     result = ellipse;
238   }
239 
240   if(result)
241   {
242     Glib::RefPtr<Goocanvas::Item> root = m_canvas.get_root_item();
243     root->add_child(result);
244 
245     //Show it on the canvas, at the position:
246     double item_x = x;
247     double item_y = y;
248     m_canvas.convert_from_pixels(item_x, item_y);
249     result->translate(item_x, item_y);
250   }
251 
252   return result;
253 }
254 
255