]> Pileus Git - ~andy/gtk/blob - gtk/gtktreednd.c
gtk/: fully remove gtkalias hacks
[~andy/gtk] / gtk / gtktreednd.c
1 /* gtktreednd.c
2  * Copyright (C) 2001  Red Hat, Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include "config.h"
21 #include <string.h>
22 #include "gtktreednd.h"
23 #include "gtkintl.h"
24
25
26 /**
27  * SECTION:gtktreednd
28  * @Short_description: Interfaces for drag-and-drop support in GtkTreeView
29  * @Title: GtkTreeView drag-and-drop
30  *
31  * GTK+ supports Drag-and-Drop in tree views with a high-level and a low-level
32  * API.
33  *
34  * The low-level API consists of the GTK+ DND API, augmented by some treeview
35  * utility functions: gtk_tree_view_set_drag_dest_row(),
36  * gtk_tree_view_get_drag_dest_row(), gtk_tree_view_get_dest_row_at_pos(),
37  * gtk_tree_view_create_row_drag_icon(), gtk_tree_set_row_drag_data() and
38  * gtk_tree_get_row_drag_data(). This API leaves a lot of flexibility, but
39  * nothing is done automatically, and implementing advanced features like
40  * hover-to-open-rows or autoscrolling on top of this API is a lot of work.
41  *
42  * On the other hand, if you write to the high-level API, then all the
43  * bookkeeping of rows is done for you, as well as things like hover-to-open
44  * and auto-scroll, but your models have to implement the
45  * #GtkTreeDragSource and #GtkTreeDragDest interfaces.
46  */
47
48
49 GType
50 gtk_tree_drag_source_get_type (void)
51 {
52   static GType our_type = 0;
53
54   if (!our_type)
55     {
56       const GTypeInfo our_info =
57       {
58         sizeof (GtkTreeDragSourceIface), /* class_size */
59         NULL,           /* base_init */
60         NULL,           /* base_finalize */
61         NULL,
62         NULL,           /* class_finalize */
63         NULL,           /* class_data */
64         0,
65         0,              /* n_preallocs */
66         NULL
67       };
68
69       our_type = g_type_register_static (G_TYPE_INTERFACE, 
70                                          I_("GtkTreeDragSource"),
71                                          &our_info, 0);
72     }
73   
74   return our_type;
75 }
76
77
78 GType
79 gtk_tree_drag_dest_get_type (void)
80 {
81   static GType our_type = 0;
82
83   if (!our_type)
84     {
85       const GTypeInfo our_info =
86       {
87         sizeof (GtkTreeDragDestIface), /* class_size */
88         NULL,           /* base_init */
89         NULL,           /* base_finalize */
90         NULL,
91         NULL,           /* class_finalize */
92         NULL,           /* class_data */
93         0,
94         0,              /* n_preallocs */
95         NULL
96       };
97
98       our_type = g_type_register_static (G_TYPE_INTERFACE, I_("GtkTreeDragDest"), &our_info, 0);
99     }
100   
101   return our_type;
102 }
103
104 /**
105  * gtk_tree_drag_source_row_draggable:
106  * @drag_source: a #GtkTreeDragSource
107  * @path: row on which user is initiating a drag
108  * 
109  * Asks the #GtkTreeDragSource whether a particular row can be used as
110  * the source of a DND operation. If the source doesn't implement
111  * this interface, the row is assumed draggable.
112  *
113  * Return value: %TRUE if the row can be dragged
114  **/
115 gboolean
116 gtk_tree_drag_source_row_draggable (GtkTreeDragSource *drag_source,
117                                     GtkTreePath       *path)
118 {
119   GtkTreeDragSourceIface *iface = GTK_TREE_DRAG_SOURCE_GET_IFACE (drag_source);
120
121   g_return_val_if_fail (path != NULL, FALSE);
122
123   if (iface->row_draggable)
124     return (* iface->row_draggable) (drag_source, path);
125   else
126     return TRUE;
127     /* Returning TRUE if row_draggable is not implemented is a fallback.
128        Interface implementations such as GtkTreeStore and GtkListStore really should
129        implement row_draggable. */
130 }
131
132
133 /**
134  * gtk_tree_drag_source_drag_data_delete:
135  * @drag_source: a #GtkTreeDragSource
136  * @path: row that was being dragged
137  * 
138  * Asks the #GtkTreeDragSource to delete the row at @path, because
139  * it was moved somewhere else via drag-and-drop. Returns %FALSE
140  * if the deletion fails because @path no longer exists, or for
141  * some model-specific reason. Should robustly handle a @path no
142  * longer found in the model!
143  * 
144  * Return value: %TRUE if the row was successfully deleted
145  **/
146 gboolean
147 gtk_tree_drag_source_drag_data_delete (GtkTreeDragSource *drag_source,
148                                        GtkTreePath       *path)
149 {
150   GtkTreeDragSourceIface *iface = GTK_TREE_DRAG_SOURCE_GET_IFACE (drag_source);
151
152   g_return_val_if_fail (iface->drag_data_delete != NULL, FALSE);
153   g_return_val_if_fail (path != NULL, FALSE);
154
155   return (* iface->drag_data_delete) (drag_source, path);
156 }
157
158 /**
159  * gtk_tree_drag_source_drag_data_get:
160  * @drag_source: a #GtkTreeDragSource
161  * @path: row that was dragged
162  * @selection_data: a #GtkSelectionData to fill with data from the dragged row
163  * 
164  * Asks the #GtkTreeDragSource to fill in @selection_data with a
165  * representation of the row at @path. @selection_data->target gives
166  * the required type of the data.  Should robustly handle a @path no
167  * longer found in the model!
168  * 
169  * Return value: %TRUE if data of the required type was provided 
170  **/
171 gboolean
172 gtk_tree_drag_source_drag_data_get    (GtkTreeDragSource *drag_source,
173                                        GtkTreePath       *path,
174                                        GtkSelectionData  *selection_data)
175 {
176   GtkTreeDragSourceIface *iface = GTK_TREE_DRAG_SOURCE_GET_IFACE (drag_source);
177
178   g_return_val_if_fail (iface->drag_data_get != NULL, FALSE);
179   g_return_val_if_fail (path != NULL, FALSE);
180   g_return_val_if_fail (selection_data != NULL, FALSE);
181
182   return (* iface->drag_data_get) (drag_source, path, selection_data);
183 }
184
185 /**
186  * gtk_tree_drag_dest_drag_data_received:
187  * @drag_dest: a #GtkTreeDragDest
188  * @dest: row to drop in front of
189  * @selection_data: data to drop
190  * 
191  * Asks the #GtkTreeDragDest to insert a row before the path @dest,
192  * deriving the contents of the row from @selection_data. If @dest is
193  * outside the tree so that inserting before it is impossible, %FALSE
194  * will be returned. Also, %FALSE may be returned if the new row is
195  * not created for some model-specific reason.  Should robustly handle
196  * a @dest no longer found in the model!
197  * 
198  * Return value: whether a new row was created before position @dest
199  **/
200 gboolean
201 gtk_tree_drag_dest_drag_data_received (GtkTreeDragDest  *drag_dest,
202                                        GtkTreePath      *dest,
203                                        GtkSelectionData *selection_data)
204 {
205   GtkTreeDragDestIface *iface = GTK_TREE_DRAG_DEST_GET_IFACE (drag_dest);
206
207   g_return_val_if_fail (iface->drag_data_received != NULL, FALSE);
208   g_return_val_if_fail (dest != NULL, FALSE);
209   g_return_val_if_fail (selection_data != NULL, FALSE);
210
211   return (* iface->drag_data_received) (drag_dest, dest, selection_data);
212 }
213
214
215 /**
216  * gtk_tree_drag_dest_row_drop_possible:
217  * @drag_dest: a #GtkTreeDragDest
218  * @dest_path: destination row
219  * @selection_data: the data being dragged
220  * 
221  * Determines whether a drop is possible before the given @dest_path,
222  * at the same depth as @dest_path. i.e., can we drop the data in
223  * @selection_data at that location. @dest_path does not have to
224  * exist; the return value will almost certainly be %FALSE if the
225  * parent of @dest_path doesn't exist, though.
226  * 
227  * Return value: %TRUE if a drop is possible before @dest_path
228  **/
229 gboolean
230 gtk_tree_drag_dest_row_drop_possible (GtkTreeDragDest   *drag_dest,
231                                       GtkTreePath       *dest_path,
232                                       GtkSelectionData  *selection_data)
233 {
234   GtkTreeDragDestIface *iface = GTK_TREE_DRAG_DEST_GET_IFACE (drag_dest);
235
236   g_return_val_if_fail (iface->row_drop_possible != NULL, FALSE);
237   g_return_val_if_fail (selection_data != NULL, FALSE);
238   g_return_val_if_fail (dest_path != NULL, FALSE);
239
240   return (* iface->row_drop_possible) (drag_dest, dest_path, selection_data);
241 }
242
243 typedef struct _TreeRowData TreeRowData;
244
245 struct _TreeRowData
246 {
247   GtkTreeModel *model;
248   gchar path[4];
249 };
250
251 /**
252  * gtk_tree_set_row_drag_data:
253  * @selection_data: some #GtkSelectionData
254  * @tree_model: a #GtkTreeModel
255  * @path: a row in @tree_model
256  * 
257  * Sets selection data of target type %GTK_TREE_MODEL_ROW. Normally used
258  * in a drag_data_get handler.
259  * 
260  * Return value: %TRUE if the #GtkSelectionData had the proper target type to allow us to set a tree row
261  **/
262 gboolean
263 gtk_tree_set_row_drag_data (GtkSelectionData *selection_data,
264                             GtkTreeModel     *tree_model,
265                             GtkTreePath      *path)
266 {
267   TreeRowData *trd;
268   gchar *path_str;
269   gint len;
270   gint struct_size;
271   
272   g_return_val_if_fail (selection_data != NULL, FALSE);
273   g_return_val_if_fail (GTK_IS_TREE_MODEL (tree_model), FALSE);
274   g_return_val_if_fail (path != NULL, FALSE);
275
276   if (selection_data->target != gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
277     return FALSE;
278   
279   path_str = gtk_tree_path_to_string (path);
280
281   len = strlen (path_str);
282
283   /* the old allocate-end-of-struct-to-hold-string trick */
284   struct_size = sizeof (TreeRowData) + len + 1 -
285     (sizeof (TreeRowData) - G_STRUCT_OFFSET (TreeRowData, path));
286
287   trd = g_malloc (struct_size); 
288
289   strcpy (trd->path, path_str);
290
291   g_free (path_str);
292   
293   trd->model = tree_model;
294   
295   gtk_selection_data_set (selection_data,
296                           gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"),
297                           8, /* bytes */
298                           (void*)trd,
299                           struct_size);
300
301   g_free (trd);
302   
303   return TRUE;
304 }
305
306 /**
307  * gtk_tree_get_row_drag_data:
308  * @selection_data: a #GtkSelectionData
309  * @tree_model: a #GtkTreeModel
310  * @path: row in @tree_model
311  * 
312  * Obtains a @tree_model and @path from selection data of target type
313  * %GTK_TREE_MODEL_ROW. Normally called from a drag_data_received handler.
314  * This function can only be used if @selection_data originates from the same
315  * process that's calling this function, because a pointer to the tree model
316  * is being passed around. If you aren't in the same process, then you'll
317  * get memory corruption. In the #GtkTreeDragDest drag_data_received handler,
318  * you can assume that selection data of type %GTK_TREE_MODEL_ROW is
319  * in from the current process. The returned path must be freed with
320  * gtk_tree_path_free().
321  * 
322  * Return value: %TRUE if @selection_data had target type %GTK_TREE_MODEL_ROW and
323  *  is otherwise valid
324  **/
325 gboolean
326 gtk_tree_get_row_drag_data (GtkSelectionData  *selection_data,
327                             GtkTreeModel     **tree_model,
328                             GtkTreePath      **path)
329 {
330   TreeRowData *trd;
331   
332   g_return_val_if_fail (selection_data != NULL, FALSE);  
333
334   if (tree_model)
335     *tree_model = NULL;
336
337   if (path)
338     *path = NULL;
339   
340   if (selection_data->target != gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
341     return FALSE;
342
343   if (selection_data->length < 0)
344     return FALSE;
345
346   trd = (void*) selection_data->data;
347
348   if (tree_model)
349     *tree_model = trd->model;
350
351   if (path)
352     *path = gtk_tree_path_new_from_string (trd->path);
353   
354   return TRUE;
355 }