Merged with trunk R992.
[ardour.git] / libs / gtkmm2ext / gtkmm2ext / dndtreeview.h
1 #ifndef __gtkmm2ext_dndtreeview_h__
2 #define __gtkmm2ext_dndtreeview_h__
3
4 #include <stdint.h>
5 #include <string>
6 #include <gtkmm/treeview.h>
7 #include <gtkmm/treeselection.h>
8 #include <gtkmm/selectiondata.h>
9
10 namespace Gtkmm2ext {
11
12 template<class DataType>
13 struct SerializedObjectPointers {
14     uint32_t size;
15     uint32_t cnt;
16     char     type[32];
17     DataType data[0];
18 };
19
20 class DnDTreeViewBase : public Gtk::TreeView 
21 {
22   private:
23   public:
24         DnDTreeViewBase ();
25         ~DnDTreeViewBase() {}
26
27         void add_drop_targets (std::list<Gtk::TargetEntry>&);
28         void add_object_drag (int column, std::string type_name);
29         
30         void on_drag_leave(const Glib::RefPtr<Gdk::DragContext>& context, guint time) {
31                 suggested_action = context->get_suggested_action();
32                 TreeView::on_drag_leave (context, time);
33         }
34
35         bool on_drag_motion(const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, guint time) {
36                 suggested_action = context->get_suggested_action();
37                 return TreeView::on_drag_motion (context, x, y, time);
38         }
39
40         bool on_drag_drop(const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, guint time);
41
42   protected:
43         std::list<Gtk::TargetEntry> draggable;
44         Gdk::DragAction             suggested_action;
45         int                         data_column;
46 };
47
48 template<class DataType>
49 class DnDTreeView : public DnDTreeViewBase
50 {
51   public:
52         DnDTreeView() {} 
53         ~DnDTreeView() {}
54
55         sigc::signal<void,std::string,uint32_t,const DataType*> signal_object_drop;
56
57         void on_drag_data_get(const Glib::RefPtr<Gdk::DragContext>& context, Gtk::SelectionData& selection_data, guint info, guint time) {
58                 if (selection_data.get_target() == "GTK_TREE_MODEL_ROW") {
59                         
60                         TreeView::on_drag_data_get (context, selection_data, info, time);
61                         
62                 } else if (data_column >= 0) {
63                         
64                         Gtk::TreeSelection::ListHandle_Path selection = get_selection()->get_selected_rows ();
65                         SerializedObjectPointers<DataType>* sr = serialize_pointers (get_model(), &selection, selection_data.get_target());
66                         selection_data.set (8, (guchar*)sr, sr->size);
67                 }
68         }
69
70         void on_drag_data_received(const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, const Gtk::SelectionData& selection_data, guint info, guint time) {
71                 if (suggested_action) {
72                         /* this is a drag motion callback. just update the status to
73                            say that we are still dragging, and that's it.
74                         */
75                         suggested_action = Gdk::DragAction (0);
76                         TreeView::on_drag_data_received (context, x, y, selection_data, info, time);
77                         return;
78                 }
79                 
80                 if (selection_data.get_target() == "GTK_TREE_MODEL_ROW") {
81                         
82                         TreeView::on_drag_data_received (context, x, y, selection_data, info, time);
83                         
84                 } else if (data_column >= 0) {
85                         
86                         /* object D-n-D */
87                         
88                         const void* data = selection_data.get_data();
89                         const SerializedObjectPointers<DataType>* sr = reinterpret_cast<const SerializedObjectPointers<DataType> *>(data);
90                         
91                         if (sr) {
92                                 signal_object_drop (sr->type, sr->cnt, sr->data);
93                         }
94                         
95                 } else {
96                         /* some kind of target type added by the app, which will be handled by a signal handler */
97                 }
98         }
99
100   private:
101
102         SerializedObjectPointers<DataType>* serialize_pointers (Glib::RefPtr<Gtk::TreeModel> model, 
103                                                                 Gtk::TreeSelection::ListHandle_Path* selection,
104                                                                 Glib::ustring type) {
105
106                 /* this nasty chunk of code is here because X's DnD protocol (probably other graphics UI's too) 
107                    requires that we package up the entire data collection for DnD in a single contiguous region
108                    (so that it can be trivially copied between address spaces). We don't know the type of DataType so
109                    we have to mix-and-match C and C++ programming techniques here to get the right result.
110
111                    The C trick is to use the "someType foo[0];" declaration trick to create a zero-sized array at the
112                    end of a SerializedObjectPointers<DataType object. Then we allocate a raw memory buffer that extends
113                    past that array and thus provides space for however many DataType items we actually want to pass
114                    around.
115
116                    The C++ trick is to use the placement operator new() syntax to initialize that extra
117                    memory properly.
118                 */
119                 
120                 uint32_t cnt = selection->size();
121                 uint32_t sz = (sizeof (DataType) * cnt) + sizeof (SerializedObjectPointers<DataType>);
122
123                 char* buf = new char[sz];
124                 SerializedObjectPointers<DataType>* sr = (SerializedObjectPointers<DataType>*) buf;
125
126                 for (uint32_t i = 0; i < cnt; ++i) {
127                         new ((void *) &sr->data[i]) DataType ();
128                 }
129                 
130                 sr->cnt = cnt;
131                 sr->size = sz;
132                 snprintf (sr->type, sizeof (sr->type), "%s", type.c_str());
133                 
134                 cnt = 0;
135                 
136                 for (Gtk::TreeSelection::ListHandle_Path::iterator x = selection->begin(); x != selection->end(); ++x, ++cnt) {
137                         model->get_iter (*x)->get_value (data_column, sr->data[cnt]);
138                 }
139
140                 return sr;
141         }
142 };
143
144 } // namespace
145  
146 #endif /* __gtkmm2ext_dndtreeview_h__ */