]> git.llucax.com Git - z.facultad/75.42/plaqui.git/blob - tests/gtkmm/dnd_pixmap/dnd.c
* Cliente carga todos los widgets (falta exclusa!)
[z.facultad/75.42/plaqui.git] / tests / gtkmm / dnd_pixmap / dnd.c
1 /*
2         GTK Drag & Drop Tutorial - Example Source Code
3
4         Copyright (C) 2000-2002 WolfPack Entertainment
5         http://wolfpack.twu.net/
6
7
8         To compile:
9
10                 cc `gtk-config --cflags --libs` dnd.c -o dnd
11
12
13         Important: DND does not appear to work properly with GTK+ 1.2.9
14         or older!!  Please try to obtain GTK+ 1.2.10 or newer before
15         attempting to run this demo.
16
17
18         Order of GTK DND events are as follows (for one complete drag
19         and drop operation):
20
21         1. "drag_begin_event"   notifies source that drag started
22         2. "drag_motion"        notifies target about drag pointer motion
23         3. "drag_data_get"      request for drag data from source
24         4. "drag_data_received" source has sent target the requested data
25         5. "drag_data_delete"   source should/can delete data
26         6. "drag_end_event"     notifies source that drag is done
27
28         Sequences 1, 3, 5, and 6 are sent to the source widget, while
29         sequences 2 and 4 are sent to the target widget.
30
31  */
32
33 #include <gdk/gdkx.h>   /* Needed for pixmap loading. */
34 #include <gtk/gtk.h>
35
36 /* Data for the pixmaps that we will be using in the GtkCList cells
37  * and DND icons.
38  */
39 #include "sphere.xpm"
40 #include "cube.xpm"
41 #include "cone.xpm"
42
43
44 /*
45  *      DND data format type idenficaition, it identifies what format
46  *      the drag data is in for internal parsing by this program.
47  *      Remember that this is the data format type (not to be confused
48  *      with data type, like string, binary, etc).
49  *
50  *      These values are passed to gtk_drag_dest_set() and
51  *      gtk_drag_source_set() and will be given as inputs in DND signal
52  *      callbacks.
53  *
54  *      In each callback, we have the choice of using either the name 
55  *      (a string) or the info (an int).  In this example we will use
56  *      the info (an int) since it is easier and more commonly practiced.
57  *
58  *      Below we define the name and info pairs that are commonly used
59  *      in most GTK+ and GNOME applications:
60  */
61 #define DRAG_TAR_NAME_0         "text/plain"
62 #define DRAG_TAR_INFO_0         0
63
64 #define DRAG_TAR_NAME_1         "text/uri-list"         /* not url-list */
65 #define DRAG_TAR_INFO_1         1
66
67 #define DRAG_TAR_NAME_2         "STRING"
68 #define DRAG_TAR_INFO_2         2
69
70
71 /*
72  *      Our window structure, it's just a toplevel GtkWindow and a
73  *      GtkCList.  The GtkCList will not be set reorderable, so that
74  *      we can demostrate the (more advanced) DND abilities of this
75  *      program.
76  */
77 typedef struct {
78
79         GtkWidget *toplevel;
80         GtkWidget *clist;
81
82 } my_prog_window_struct;
83
84
85 /* General event/signal callbacks. */
86 static gint HandleCloseCB(
87         GtkWidget *widget, gpointer *event, gpointer data
88 );
89 static void CListSelectRowCB(
90         GtkWidget *widget,
91         gint row, gint column,
92         GdkEventButton *event, gpointer data
93 );
94 static void CListUnselectRowCB(
95         GtkWidget *widget,
96         gint row, gint column,
97         GdkEventButton *event, gpointer data
98 );
99
100 /* DND specific event/signal callbacks. */
101 static void DNDBeginCB(
102         GtkWidget *widget, GdkDragContext *dc, gpointer data
103 );
104 static void DNDEndCB(
105         GtkWidget *widget, GdkDragContext *dc, gpointer data
106 );
107 static gboolean DNDDragMotionCB(
108         GtkWidget *widget, GdkDragContext *dc,
109         gint x, gint y, guint t,
110         gpointer data
111 );
112 static void DNDDataRequestCB(
113         GtkWidget *widget, GdkDragContext *dc,
114         GtkSelectionData *selection_data, guint info, guint t,
115         gpointer data
116 );
117 static void DNDDataRecievedCB(
118         GtkWidget *widget, GdkDragContext *dc,
119         gint x, gint y, GtkSelectionData *selection_data,  
120         guint info, guint t, gpointer data
121 );
122 static void DNDDataDeleteCB(
123         GtkWidget *widget, GdkDragContext *dc, gpointer data
124 );
125
126 /* Some convience functions for our example. */
127 static my_prog_window_struct *WindowNew(const gchar *title);
128 static void WindowDelete(my_prog_window_struct *win);
129
130
131 /* Pixmap and mask pairs used for GtkCList cell pixmaps and
132  * DND icons.
133  */
134 static GdkPixmap        *sphere_pixmap,
135                         *cube_pixmap,
136                         *cone_pixmap;
137 static GdkBitmap        *sphere_mask,
138                         *cube_mask,
139                         *cone_mask;
140
141
142 /*
143  *      "delete_event" signal callback.
144  *
145  *      Called when a GtkWindow closes.
146  */
147 static gint HandleCloseCB(
148         GtkWidget *widget, gpointer *event, gpointer data
149 )
150 {
151         /* Make it simple, when one window closes we just effectively
152          * close them all by breaking out of the last GTK+ main loop
153          * so that they will all be destroyed.
154          */
155         gtk_main_quit();
156
157         return(TRUE);
158 }
159
160 /*
161  *      "select_row" signal callback.
162  *
163  *      Called when a row is selected on a GtkCList.
164  */
165 static void CListSelectRowCB(
166         GtkWidget *widget,
167         gint row, gint column,
168         GdkEventButton *event, gpointer data
169 )
170 {
171         GtkCList *clist;
172         my_prog_window_struct *win = (my_prog_window_struct *)data;
173         if((widget == NULL) || (win == NULL))
174                 return;
175
176         clist = GTK_CLIST(widget);
177
178         /* Selected row in bounds? */
179         if((row >= 0) && (row < clist->rows))
180         {
181                 gint cell_type;
182                 guint8 spacing = 0;
183                 gchar *text = NULL;
184                 GdkPixmap *pixmap = NULL;
185                 GdkBitmap *mask = NULL;
186
187
188                 /* Obtain the pixmap and mask pair of the icon of the
189                  * selected row and update the DND icon.
190                  */
191                 cell_type = gtk_clist_get_cell_type(clist, row, 0);
192                 switch(cell_type)
193                 {
194                   case GTK_CELL_PIXMAP:
195                     gtk_clist_get_pixmap(
196                         clist, row, 0, &pixmap, &mask
197                     );
198                   case GTK_CELL_PIXTEXT:
199                     gtk_clist_get_pixtext(
200                         clist, row, 0,
201                         &text, &spacing, &pixmap, &mask
202                     );
203                 }
204                 /* Selected row has pixmap? */
205                 if(pixmap != NULL)
206                 {
207                         gint w = 15, h = 15;
208
209                         /* Get size of pixmap. */
210                         gdk_window_get_size(
211                                 (GdkWindow *)pixmap, &w, &h
212                         );
213                         /* Update the DND icon to be used on the next
214                          * drag based on this pixmap. Set the hot spot
215                          * to be at the center of this pixmap.
216                          */
217                         gtk_drag_set_default_icon(
218                                 gdk_colormap_get_system(),
219                                 pixmap, mask,
220                                 w / 2, h / 2
221                         );
222                 }
223
224                 /* If the selected row is not fully visible, attempt to
225                  * scroll and make it visible in the center of the
226                  * list.
227                  */
228                 if(gtk_clist_row_is_visible(clist, row) !=
229                         GTK_VISIBILITY_FULL
230                 )
231                         gtk_clist_moveto(
232                                 clist,
233                                 row, 0,         /* Row, column. */
234                                 0.5, 0.0        /* Row, column. */
235                         );
236         }
237 }
238
239 /*
240  *      "unselect_row" signal callback.
241  *
242  *      Called when a row is selected on a GtkCList.
243  */
244 static void CListUnselectRowCB(
245         GtkWidget *widget,
246         gint row, gint column,
247         GdkEventButton *event, gpointer data
248 )
249 {
250         /* Ignore this. */
251
252
253
254 /*
255  *      DND "drag_begin" handler, this is called whenever a drag starts.
256  */
257 static void DNDBeginCB(
258         GtkWidget *widget, GdkDragContext *dc, gpointer data
259 )
260 {
261         my_prog_window_struct *win = (my_prog_window_struct *)data;
262         if((widget == NULL) || (win == NULL) || (dc == NULL))
263                 return;
264
265         /* Put any needed drag begin setup code here. */
266 }
267
268 /*
269  *      DND "drag_end" handler, this is called when a drag and drop has
270  *      completed. So this function is the last one to be called in
271  *      any given DND operation.
272  */
273 static void DNDEndCB(
274         GtkWidget *widget, GdkDragContext *dc, gpointer data
275 )
276 {
277         my_prog_window_struct *win = (my_prog_window_struct *)data;
278         if((win == NULL) || (dc == NULL))
279                 return;
280
281         /* Put any needed drag end cleanup code here. */
282 }
283
284 /*
285  *      DND "drag_motion" handler, this is called whenever the 
286  *      pointer is dragging over the target widget.
287  */
288 static gboolean DNDDragMotionCB(
289         GtkWidget *widget, GdkDragContext *dc,
290         gint x, gint y, guint t,
291         gpointer data
292 )
293 {
294         gboolean same_widget;
295         GdkDragAction suggested_action;
296         GtkWidget *src_widget, *tar_widget;
297         my_prog_window_struct *win = (my_prog_window_struct *)data;
298         if((win == NULL) || (dc == NULL))
299                 return(FALSE);
300
301         /* Get source widget and target widget. */
302         src_widget = gtk_drag_get_source_widget(dc);
303         tar_widget = widget;
304
305         /* Note if source widget is the same as the target. */
306         same_widget = (src_widget == tar_widget) ? TRUE : FALSE;
307
308
309         /* If this is the same widget, our suggested action should be
310          * move.  For all other case we assume copy.
311          */
312         if(same_widget)
313                 suggested_action = GDK_ACTION_MOVE;
314         else
315                 suggested_action = GDK_ACTION_COPY;
316
317         /* Respond with default drag action (status). First we check
318          * the dc's list of actions. If the list only contains
319          * move, copy, or link then we select just that, otherwise we
320          * return with our default suggested action.
321          * If no valid actions are listed then we respond with 0.
322          */
323
324         /* Only move? */
325         if(dc->actions == GDK_ACTION_MOVE)
326             gdk_drag_status(dc, GDK_ACTION_MOVE, t);
327         /* Only copy? */
328         else if(dc->actions == GDK_ACTION_COPY)
329             gdk_drag_status(dc, GDK_ACTION_COPY, t);
330         /* Only link? */
331         else if(dc->actions == GDK_ACTION_LINK)
332             gdk_drag_status(dc, GDK_ACTION_LINK, t);
333         /* Other action, check if listed in our actions list? */
334         else if(dc->actions & suggested_action)
335             gdk_drag_status(dc, suggested_action, t);
336         /* All else respond with 0. */
337         else
338             gdk_drag_status(dc, 0, t);
339
340         return(FALSE);
341 }
342
343 /*
344  *      DND "drag_data_get" handler, for handling requests for DND
345  *      data on the specified widget. This function is called when
346  *      there is need for DND data on the source, so this function is
347  *      responsable for setting up the dynamic data exchange buffer
348  *      (DDE as sometimes it is called) and sending it out.
349  */
350 static void DNDDataRequestCB(
351         GtkWidget *widget, GdkDragContext *dc,
352         GtkSelectionData *selection_data, guint info, guint t,
353         gpointer data
354 )
355 {
356         gboolean data_sent = FALSE;
357         gint row;
358         GList *glist;
359         GtkCList *clist;
360         my_prog_window_struct *win = (my_prog_window_struct *)data;
361         if((widget == NULL) || (win == NULL) || (dc == NULL))
362                 return;
363
364         clist = GTK_CLIST(widget);
365
366         /* Get last selected row on the clist. */
367         glist = clist->selection_end;
368         row = (glist != NULL) ? (gint)glist->data : -1;
369
370         /* Selected row in bounds? */
371         if((row >= 0) && (row < clist->rows))
372         {
373                 gint cell_type;
374                 guint8 spacing = 0;
375                 gchar *text = NULL;
376                 GdkPixmap *pixmap = NULL;
377                 GdkBitmap *mask = NULL;
378
379
380                 /* Obtain the text of the first cell of the selected
381                  * row, then use this text and send it out to the DDE.
382                  */
383                 cell_type = gtk_clist_get_cell_type(clist, row, 0);
384                 switch(cell_type)
385                 {
386                   case GTK_CELL_TEXT:
387                     gtk_clist_get_text(
388                         clist, row, 0, &text
389                     );
390                   case GTK_CELL_PIXTEXT:
391                     gtk_clist_get_pixtext(
392                         clist, row, 0,
393                         &text, &spacing, &pixmap, &mask
394                     );
395                 }
396                 /* Selected row has text? */
397                 if(text != NULL)
398                 {
399                         /* Send out the data using the selection system,
400                          * this is also used for cut and paste but
401                          * GTK uses it for drag and drop as well. When
402                          * sending a string, GTK will ensure that a null
403                          * terminating byte is added to the end so we
404                          * do not need to add it. GTK also coppies the
405                          * data so the original will never be modified.
406                          */
407                         gtk_selection_data_set(
408                                 selection_data,
409                                 GDK_SELECTION_TYPE_STRING,
410                                 8,      /* 8 bits per character. */
411                                 text, strlen(text)
412                         );
413                         data_sent = TRUE;
414                 }
415         }
416
417         /* If we did not send out any data (for whatever reason),
418          * then send out an error response since this function
419          * needs to gaurantee a response when reasonably possible.
420          */
421         if(!data_sent)
422         {
423                 const gchar *cstrptr = "Error";
424
425                 gtk_selection_data_set(
426                         selection_data,
427                         GDK_SELECTION_TYPE_STRING,
428                         8,      /* 8 bits per character. */
429                         cstrptr, strlen(cstrptr)
430                 );
431                 data_sent = TRUE;
432         }
433 }
434
435 /*
436  *      DND "drag_data_received" handler. When DNDDataRequestCB()
437  *      calls gtk_selection_data_set() to send out the data, this function
438  *      recieves it and is responsible for handling it.
439  *
440  *      This is also the only DND callback function where the given
441  *      inputs may reflect those of the drop target so we need to check
442  *      if this is the same structure or not.
443  */
444 static void DNDDataRecievedCB(
445         GtkWidget *widget, GdkDragContext *dc,
446         gint x, gint y, GtkSelectionData *selection_data,
447         guint info, guint t, gpointer data
448 )
449 {
450         gboolean same;
451         gint row, column;
452         GtkWidget *source_widget;
453         GtkCList *clist;
454         my_prog_window_struct *win = (my_prog_window_struct *)data;
455         if((widget == NULL) || (win == NULL) || (dc == NULL))
456                 return;
457
458         /* Important, check if we actually got data.  Sometimes errors
459          * occure and selection_data will be NULL.
460          */
461         if(selection_data == NULL)
462                 return;
463         if(selection_data->length < 0)
464                 return;
465
466         /* Source and target widgets are the same? */
467         source_widget = gtk_drag_get_source_widget(dc);
468         same = (source_widget == widget) ? TRUE : FALSE;
469
470         clist = GTK_CLIST(widget);
471
472         /* Calculate the row and column at which the drop has occured
473          * over on the clist based on the given x and y coordinates.
474          */
475         if(!gtk_clist_get_selection_info(
476                 clist,
477                 x,
478                 y - ((clist->flags & GTK_CLIST_SHOW_TITLES) ?
479                         clist->column_title_area.height +
480                         clist->column_title_area.y : 0),
481                 &row, &column
482         ))
483         {
484                 row = -1;
485                 column = 0;
486         }
487
488         /* Now check if the data format type is one that we support
489          * (remember, data format type, not data type).
490          *
491          * We check this by testing if info matches one of the info
492          * values that we have defined.
493          *
494          * Note that we can also iterate through the atoms in:
495          *      GList *glist = dc->targets;
496          *
497          *      while(glist != NULL)
498          *      {
499          *          gchar *name = gdk_atom_name((GdkAtom)glist->data);
500          *           * strcmp the name to see if it matches
501          *           * one that we support
502          *           *
503          *          glist = glist->next;
504          *      }
505          */
506         if((info == DRAG_TAR_INFO_0) ||
507            (info == DRAG_TAR_INFO_1) ||
508            (info == DRAG_TAR_INFO_2)
509         )
510         {
511                 gint new_row;
512                 gchar *new_text = selection_data->data;
513                 gchar *text[1];
514                 GdkPixmap *pixmap = NULL;
515                 GdkBitmap *mask = NULL;
516
517
518                 /* Reset cells text array for adding a new row, we'll
519                  * set the actual text after the row has been created.
520                  */
521                 text[0] = "";
522
523                 /* Append or insert the new row? */
524                 if((row >= 0) && (row < clist->rows))
525                         new_row = gtk_clist_insert(clist, row, text);
526                 else
527                         new_row = gtk_clist_append(clist, text);
528
529                 /* Check the name of the cell obtained from the received
530                  * data and decipher which pixmap and mask pair to
531                  * use for the new cell (if any).
532                  */
533                 if(!strcmp(new_text, "Sphere"))
534                 {
535                         pixmap = sphere_pixmap;
536                         mask = sphere_mask;
537                 }
538                 else if(!strcmp(new_text, "Cube"))
539                 {
540                         pixmap = cube_pixmap;
541                         mask = cube_mask;
542                 }
543                 else if(!strcmp(new_text, "Cone"))
544                 {
545                         pixmap = cone_pixmap;
546                         mask = cone_mask;
547                 }
548                 /* Update the new row's cell with the pixmap (if
549                  * available) and the received data as the text.
550                  */
551                 if(pixmap != NULL)
552                         gtk_clist_set_pixtext(
553                                 clist, new_row, 0,
554                                 new_text, 2, pixmap, mask
555                         );
556                 else
557                         gtk_clist_set_text(
558                                 clist, new_row, 0,
559                                 new_text
560                         );
561         }
562 }
563
564 /*
565  *      DND "drag_data_delete" handler, this function is called when
566  *      the data on the source `should' be deleted (ie if the DND was
567  *      a move).
568  */
569 static void DNDDataDeleteCB(
570         GtkWidget *widget, GdkDragContext *dc, gpointer data
571 )
572 {
573         gint row;
574         GList *glist;
575         GtkCList *clist;
576         my_prog_window_struct *win = (my_prog_window_struct *)data;
577         if((widget == NULL) || (win == NULL) || (dc == NULL))
578                 return;
579
580         clist = GTK_CLIST(widget);
581
582         /* Get last selected row on the clist. */
583         glist = clist->selection_end;
584         row = (glist != NULL) ? (gint)glist->data : -1;
585
586         /* Remove the last selected row. */
587         gtk_clist_remove(clist, row);
588 }  
589
590
591 /*
592  *      Creates a new window with everything set up.
593  */
594 static my_prog_window_struct *WindowNew(const gchar *title)
595 {
596         gchar *heading[1];
597         GtkWidget *w, *parent, *scrolled_window;
598         GtkCList *clist;
599         GtkTargetEntry target_entry[3];
600         my_prog_window_struct *win = (my_prog_window_struct *)g_malloc0(
601                 sizeof(my_prog_window_struct)
602         );
603         if(win == NULL)
604             return(NULL);
605
606         /* Create toplevel. */
607         w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
608         win->toplevel = w;
609         gtk_signal_connect(
610             GTK_OBJECT(w), "delete_event",
611             GTK_SIGNAL_FUNC(HandleCloseCB), win
612         );
613         gtk_widget_set_usize(w, 200, 150);
614         gtk_window_set_title(
615             GTK_WINDOW(w),
616             (title == NULL) ? "Untitled" : title
617         );
618         /* Do not show the toplevel just yet. */
619         parent = w;
620
621         /* Main vbox to hold clist. */
622         w = gtk_vbox_new(FALSE, 0);
623         gtk_container_add(GTK_CONTAINER(parent), w);
624         gtk_widget_show(w);
625         parent = w;
626
627         /* Create the scrolled window for the clist and the clist
628          * itself.
629          */
630         scrolled_window = gtk_scrolled_window_new(NULL, NULL);
631         gtk_box_pack_start(GTK_BOX(parent), scrolled_window, TRUE, TRUE, 0);
632         gtk_scrolled_window_set_policy(
633                 GTK_SCROLLED_WINDOW (scrolled_window),
634                 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC
635         );
636         gtk_widget_show(scrolled_window);
637
638         heading[0] = "Name";
639         w = gtk_clist_new_with_titles(1, heading);
640         clist = GTK_CLIST(w);
641         win->clist = w;
642         gtk_clist_set_selection_mode(clist, GTK_SELECTION_SINGLE);
643         gtk_clist_set_shadow_type(clist, GTK_SHADOW_IN);
644         gtk_clist_column_titles_passive(clist);
645         gtk_clist_set_column_justification(
646             clist, 0, GTK_JUSTIFY_LEFT
647         );
648         gtk_clist_set_row_height(clist, 20);
649         gtk_signal_connect(
650                 GTK_OBJECT(w), "select_row",
651                 GTK_SIGNAL_FUNC(CListSelectRowCB), win
652         );
653         gtk_signal_connect(
654                 GTK_OBJECT(w), "unselect_row",
655                 GTK_SIGNAL_FUNC(CListUnselectRowCB), win
656         );
657         gtk_container_add(GTK_CONTAINER(scrolled_window), w);
658
659         /* Realize the clist widget and make sure it has a window,
660          * this will be for DND setup.
661          */
662         gtk_widget_realize(w);
663         if(!GTK_WIDGET_NO_WINDOW(w))
664         {
665                 /* DND: Set up the clist as a potential DND destination.
666                  * First we set up target_entry which is a sequence of of
667                  * structure which specify the kinds (which we define) of
668                  * drops accepted on this widget.
669                  */
670
671                 /* Set up the list of data format types that our DND
672                  * callbacks will accept.
673                  */
674                 target_entry[0].target = DRAG_TAR_NAME_0;
675                 target_entry[0].flags = 0;
676                 target_entry[0].info = DRAG_TAR_INFO_0;
677                 target_entry[1].target = DRAG_TAR_NAME_1;
678                 target_entry[1].flags = 0;
679                 target_entry[1].info = DRAG_TAR_INFO_1;
680                 target_entry[2].target = DRAG_TAR_NAME_2;
681                 target_entry[2].flags = 0;
682                 target_entry[2].info = DRAG_TAR_INFO_2;
683
684                 /* Set the drag destination for this widget, using the
685                  * above target entry types, accept move's and coppies'.
686                  */
687                 gtk_drag_dest_set(
688                         w,
689                         GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT |
690                         GTK_DEST_DEFAULT_DROP,
691                         target_entry,
692                         sizeof(target_entry) / sizeof(GtkTargetEntry),
693                         GDK_ACTION_MOVE | GDK_ACTION_COPY
694                 );
695                 gtk_signal_connect(
696                         GTK_OBJECT(w), "drag_motion",
697                         GTK_SIGNAL_FUNC(DNDDragMotionCB),
698                         win
699                 );
700
701                 /* Set the drag source for this widget, allowing the user
702                  * to drag items off of this clist.
703                  */
704                 gtk_drag_source_set(
705                         w,
706                         GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
707                         target_entry,
708                         sizeof(target_entry) / sizeof(GtkTargetEntry),
709                         GDK_ACTION_MOVE | GDK_ACTION_COPY
710                 );
711                 /* Set DND signals on clist. */
712                 gtk_signal_connect(
713                         GTK_OBJECT(w), "drag_begin",
714                         GTK_SIGNAL_FUNC(DNDBeginCB), win
715                 );
716                 gtk_signal_connect(
717                         GTK_OBJECT(w), "drag_end",
718                         GTK_SIGNAL_FUNC(DNDEndCB), win
719                 );
720                 gtk_signal_connect(
721                         GTK_OBJECT(w), "drag_data_get",
722                         GTK_SIGNAL_FUNC(DNDDataRequestCB), win
723                 );
724                 gtk_signal_connect(
725                         GTK_OBJECT(w), "drag_data_received",
726                         GTK_SIGNAL_FUNC(DNDDataRecievedCB), win
727                 );
728                 gtk_signal_connect(
729                         GTK_OBJECT(w), "drag_data_delete",
730                         GTK_SIGNAL_FUNC(DNDDataDeleteCB), win
731                 );
732         }
733
734
735         gtk_widget_show(w);
736
737
738         /* Now show the toplevel. */
739         gtk_widget_show(win->toplevel);
740
741         return(win);
742 }
743
744 /*
745  *      Dealloactes the given window and all its resources.
746  */
747 static void WindowDelete(my_prog_window_struct *win)
748 {
749         GtkWidget *w;
750
751
752         if(win == NULL)
753                 return;
754
755         /* Destroy the GtkCList, first remove the DND settings on it
756          * and then destroy it.
757          */
758         w = win->clist;
759         if(w != NULL)
760         {
761                 /* Remove DND settings on this widget. */
762                 gtk_drag_dest_unset(w);
763                 gtk_drag_source_unset(w);
764
765                 win->clist = NULL;
766                 gtk_widget_destroy(w);
767         }
768
769         /* Destroy toplevel GtkWindow, thus destroying all of its
770          * child widgets.
771          */
772         w = win->toplevel;
773         if(w != NULL)
774         {
775                 win->toplevel = NULL;
776                 gtk_widget_destroy(w);
777         }
778
779         /* Deallocate the structure itself. */
780         g_free(win);
781 }
782
783
784 int main(int argc, char **argv)
785 {
786         GtkStyle *style;
787         GdkWindow *window;
788         GdkPixmap **pixmap;
789         GdkBitmap **mask;
790         gchar **pixmap_data;
791         my_prog_window_struct *win[2];
792
793
794         gtk_init(&argc, &argv);
795
796
797         /* Load the pixmaps that we will be using as GtkCList cell
798          * pixmaps and DND icons.
799          *
800          * This requires that we use the root window for loading of
801          * the pixmaps (hence the need to include gdk/gdkx.h).
802          */
803         style = gtk_widget_get_default_style();
804         window = (GdkWindow *)GDK_ROOT_PARENT();
805
806         pixmap = &sphere_pixmap;
807         mask = &sphere_mask;
808         pixmap_data = sphere_xpm;
809         *pixmap = gdk_pixmap_create_from_xpm_d(
810             window, mask,
811             &style->bg[GTK_STATE_NORMAL],
812             pixmap_data
813         );
814
815         pixmap = &cube_pixmap;
816         mask = &cube_mask;
817         pixmap_data = cube_xpm;
818         *pixmap = gdk_pixmap_create_from_xpm_d(
819             window, mask,
820             &style->bg[GTK_STATE_NORMAL],
821             pixmap_data
822         );
823
824         pixmap = &cone_pixmap;
825         mask = &cone_mask;
826         pixmap_data = cone_xpm;
827         *pixmap = gdk_pixmap_create_from_xpm_d(
828             window, mask,
829             &style->bg[GTK_STATE_NORMAL],
830             pixmap_data
831         );
832
833
834         /* Create two windows. */
835         win[0] = WindowNew("List 1");
836         win[1] = WindowNew("List 2");
837
838         /* Add some items to the first list. */
839         if(win[0] != NULL)
840         {
841                 GtkCList *clist = (GtkCList *)win[0]->clist;
842                 if(clist != NULL)
843                 {
844                         gint new_row;
845                         gchar *text[1];
846
847
848                         /* Reset cells text array for adding a new row,
849                          * we'll set the actual text after the row has
850                          * been created.
851                          */
852                         text[0] = "";
853
854                         /* Begin creating new rows. */
855
856                         new_row = gtk_clist_append(clist, text);
857                         gtk_clist_set_pixtext(
858                                 clist, new_row, 0,
859                                 "Sphere", 2,
860                                 sphere_pixmap, sphere_mask
861                         );
862
863                         new_row = gtk_clist_append(clist, text);
864                         gtk_clist_set_pixtext(
865                                 clist, new_row, 0,
866                                 "Cube", 2,
867                                 cube_pixmap, cube_mask
868                         );
869
870                         new_row = gtk_clist_append(clist, text);
871                         gtk_clist_set_pixtext(
872                                 clist, new_row, 0,
873                                 "Cone", 2,
874                                 cone_pixmap, cone_mask
875                         );
876                 }
877         }
878
879
880         /* Enter main gtk loop. */
881         gtk_main();
882
883
884         /* Application now shutting down, begin deallocating
885          * resources.
886          */
887         WindowDelete(win[0]);
888         WindowDelete(win[1]);
889
890         gdk_pixmap_unref(sphere_pixmap);
891         gdk_bitmap_unref(sphere_mask);
892         gdk_pixmap_unref(cube_pixmap);
893         gdk_bitmap_unref(cube_mask);
894         gdk_pixmap_unref(cone_pixmap);
895         gdk_bitmap_unref(cone_mask);
896
897
898         return(0);
899 }