+/*
+ GTK Drag & Drop Tutorial - Example Source Code
+
+ Copyright (C) 2000-2002 WolfPack Entertainment
+ http://wolfpack.twu.net/
+
+
+ To compile:
+
+ cc `gtk-config --cflags --libs` dnd.c -o dnd
+
+
+ Important: DND does not appear to work properly with GTK+ 1.2.9
+ or older!! Please try to obtain GTK+ 1.2.10 or newer before
+ attempting to run this demo.
+
+
+ Order of GTK DND events are as follows (for one complete drag
+ and drop operation):
+
+ 1. "drag_begin_event" notifies source that drag started
+ 2. "drag_motion" notifies target about drag pointer motion
+ 3. "drag_data_get" request for drag data from source
+ 4. "drag_data_received" source has sent target the requested data
+ 5. "drag_data_delete" source should/can delete data
+ 6. "drag_end_event" notifies source that drag is done
+
+ Sequences 1, 3, 5, and 6 are sent to the source widget, while
+ sequences 2 and 4 are sent to the target widget.
+
+ */
+
+#include <gdk/gdkx.h> /* Needed for pixmap loading. */
+#include <gtk/gtk.h>
+
+/* Data for the pixmaps that we will be using in the GtkCList cells
+ * and DND icons.
+ */
+#include "sphere.xpm"
+#include "cube.xpm"
+#include "cone.xpm"
+
+
+/*
+ * DND data format type idenficaition, it identifies what format
+ * the drag data is in for internal parsing by this program.
+ * Remember that this is the data format type (not to be confused
+ * with data type, like string, binary, etc).
+ *
+ * These values are passed to gtk_drag_dest_set() and
+ * gtk_drag_source_set() and will be given as inputs in DND signal
+ * callbacks.
+ *
+ * In each callback, we have the choice of using either the name
+ * (a string) or the info (an int). In this example we will use
+ * the info (an int) since it is easier and more commonly practiced.
+ *
+ * Below we define the name and info pairs that are commonly used
+ * in most GTK+ and GNOME applications:
+ */
+#define DRAG_TAR_NAME_0 "text/plain"
+#define DRAG_TAR_INFO_0 0
+
+#define DRAG_TAR_NAME_1 "text/uri-list" /* not url-list */
+#define DRAG_TAR_INFO_1 1
+
+#define DRAG_TAR_NAME_2 "STRING"
+#define DRAG_TAR_INFO_2 2
+
+
+/*
+ * Our window structure, it's just a toplevel GtkWindow and a
+ * GtkCList. The GtkCList will not be set reorderable, so that
+ * we can demostrate the (more advanced) DND abilities of this
+ * program.
+ */
+typedef struct {
+
+ GtkWidget *toplevel;
+ GtkWidget *clist;
+
+} my_prog_window_struct;
+
+
+/* General event/signal callbacks. */
+static gint HandleCloseCB(
+ GtkWidget *widget, gpointer *event, gpointer data
+);
+static void CListSelectRowCB(
+ GtkWidget *widget,
+ gint row, gint column,
+ GdkEventButton *event, gpointer data
+);
+static void CListUnselectRowCB(
+ GtkWidget *widget,
+ gint row, gint column,
+ GdkEventButton *event, gpointer data
+);
+
+/* DND specific event/signal callbacks. */
+static void DNDBeginCB(
+ GtkWidget *widget, GdkDragContext *dc, gpointer data
+);
+static void DNDEndCB(
+ GtkWidget *widget, GdkDragContext *dc, gpointer data
+);
+static gboolean DNDDragMotionCB(
+ GtkWidget *widget, GdkDragContext *dc,
+ gint x, gint y, guint t,
+ gpointer data
+);
+static void DNDDataRequestCB(
+ GtkWidget *widget, GdkDragContext *dc,
+ GtkSelectionData *selection_data, guint info, guint t,
+ gpointer data
+);
+static void DNDDataRecievedCB(
+ GtkWidget *widget, GdkDragContext *dc,
+ gint x, gint y, GtkSelectionData *selection_data,
+ guint info, guint t, gpointer data
+);
+static void DNDDataDeleteCB(
+ GtkWidget *widget, GdkDragContext *dc, gpointer data
+);
+
+/* Some convience functions for our example. */
+static my_prog_window_struct *WindowNew(const gchar *title);
+static void WindowDelete(my_prog_window_struct *win);
+
+
+/* Pixmap and mask pairs used for GtkCList cell pixmaps and
+ * DND icons.
+ */
+static GdkPixmap *sphere_pixmap,
+ *cube_pixmap,
+ *cone_pixmap;
+static GdkBitmap *sphere_mask,
+ *cube_mask,
+ *cone_mask;
+
+
+/*
+ * "delete_event" signal callback.
+ *
+ * Called when a GtkWindow closes.
+ */
+static gint HandleCloseCB(
+ GtkWidget *widget, gpointer *event, gpointer data
+)
+{
+ /* Make it simple, when one window closes we just effectively
+ * close them all by breaking out of the last GTK+ main loop
+ * so that they will all be destroyed.
+ */
+ gtk_main_quit();
+
+ return(TRUE);
+}
+
+/*
+ * "select_row" signal callback.
+ *
+ * Called when a row is selected on a GtkCList.
+ */
+static void CListSelectRowCB(
+ GtkWidget *widget,
+ gint row, gint column,
+ GdkEventButton *event, gpointer data
+)
+{
+ GtkCList *clist;
+ my_prog_window_struct *win = (my_prog_window_struct *)data;
+ if((widget == NULL) || (win == NULL))
+ return;
+
+ clist = GTK_CLIST(widget);
+
+ /* Selected row in bounds? */
+ if((row >= 0) && (row < clist->rows))
+ {
+ gint cell_type;
+ guint8 spacing = 0;
+ gchar *text = NULL;
+ GdkPixmap *pixmap = NULL;
+ GdkBitmap *mask = NULL;
+
+
+ /* Obtain the pixmap and mask pair of the icon of the
+ * selected row and update the DND icon.
+ */
+ cell_type = gtk_clist_get_cell_type(clist, row, 0);
+ switch(cell_type)
+ {
+ case GTK_CELL_PIXMAP:
+ gtk_clist_get_pixmap(
+ clist, row, 0, &pixmap, &mask
+ );
+ case GTK_CELL_PIXTEXT:
+ gtk_clist_get_pixtext(
+ clist, row, 0,
+ &text, &spacing, &pixmap, &mask
+ );
+ }
+ /* Selected row has pixmap? */
+ if(pixmap != NULL)
+ {
+ gint w = 15, h = 15;
+
+ /* Get size of pixmap. */
+ gdk_window_get_size(
+ (GdkWindow *)pixmap, &w, &h
+ );
+ /* Update the DND icon to be used on the next
+ * drag based on this pixmap. Set the hot spot
+ * to be at the center of this pixmap.
+ */
+ gtk_drag_set_default_icon(
+ gdk_colormap_get_system(),
+ pixmap, mask,
+ w / 2, h / 2
+ );
+ }
+
+ /* If the selected row is not fully visible, attempt to
+ * scroll and make it visible in the center of the
+ * list.
+ */
+ if(gtk_clist_row_is_visible(clist, row) !=
+ GTK_VISIBILITY_FULL
+ )
+ gtk_clist_moveto(
+ clist,
+ row, 0, /* Row, column. */
+ 0.5, 0.0 /* Row, column. */
+ );
+ }
+}
+
+/*
+ * "unselect_row" signal callback.
+ *
+ * Called when a row is selected on a GtkCList.
+ */
+static void CListUnselectRowCB(
+ GtkWidget *widget,
+ gint row, gint column,
+ GdkEventButton *event, gpointer data
+)
+{
+ /* Ignore this. */
+}
+
+
+/*
+ * DND "drag_begin" handler, this is called whenever a drag starts.
+ */
+static void DNDBeginCB(
+ GtkWidget *widget, GdkDragContext *dc, gpointer data
+)
+{
+ my_prog_window_struct *win = (my_prog_window_struct *)data;
+ if((widget == NULL) || (win == NULL) || (dc == NULL))
+ return;
+
+ /* Put any needed drag begin setup code here. */
+}
+
+/*
+ * DND "drag_end" handler, this is called when a drag and drop has
+ * completed. So this function is the last one to be called in
+ * any given DND operation.
+ */
+static void DNDEndCB(
+ GtkWidget *widget, GdkDragContext *dc, gpointer data
+)
+{
+ my_prog_window_struct *win = (my_prog_window_struct *)data;
+ if((win == NULL) || (dc == NULL))
+ return;
+
+ /* Put any needed drag end cleanup code here. */
+}
+
+/*
+ * DND "drag_motion" handler, this is called whenever the
+ * pointer is dragging over the target widget.
+ */
+static gboolean DNDDragMotionCB(
+ GtkWidget *widget, GdkDragContext *dc,
+ gint x, gint y, guint t,
+ gpointer data
+)
+{
+ gboolean same_widget;
+ GdkDragAction suggested_action;
+ GtkWidget *src_widget, *tar_widget;
+ my_prog_window_struct *win = (my_prog_window_struct *)data;
+ if((win == NULL) || (dc == NULL))
+ return(FALSE);
+
+ /* Get source widget and target widget. */
+ src_widget = gtk_drag_get_source_widget(dc);
+ tar_widget = widget;
+
+ /* Note if source widget is the same as the target. */
+ same_widget = (src_widget == tar_widget) ? TRUE : FALSE;
+
+
+ /* If this is the same widget, our suggested action should be
+ * move. For all other case we assume copy.
+ */
+ if(same_widget)
+ suggested_action = GDK_ACTION_MOVE;
+ else
+ suggested_action = GDK_ACTION_COPY;
+
+ /* Respond with default drag action (status). First we check
+ * the dc's list of actions. If the list only contains
+ * move, copy, or link then we select just that, otherwise we
+ * return with our default suggested action.
+ * If no valid actions are listed then we respond with 0.
+ */
+
+ /* Only move? */
+ if(dc->actions == GDK_ACTION_MOVE)
+ gdk_drag_status(dc, GDK_ACTION_MOVE, t);
+ /* Only copy? */
+ else if(dc->actions == GDK_ACTION_COPY)
+ gdk_drag_status(dc, GDK_ACTION_COPY, t);
+ /* Only link? */
+ else if(dc->actions == GDK_ACTION_LINK)
+ gdk_drag_status(dc, GDK_ACTION_LINK, t);
+ /* Other action, check if listed in our actions list? */
+ else if(dc->actions & suggested_action)
+ gdk_drag_status(dc, suggested_action, t);
+ /* All else respond with 0. */
+ else
+ gdk_drag_status(dc, 0, t);
+
+ return(FALSE);
+}
+
+/*
+ * DND "drag_data_get" handler, for handling requests for DND
+ * data on the specified widget. This function is called when
+ * there is need for DND data on the source, so this function is
+ * responsable for setting up the dynamic data exchange buffer
+ * (DDE as sometimes it is called) and sending it out.
+ */
+static void DNDDataRequestCB(
+ GtkWidget *widget, GdkDragContext *dc,
+ GtkSelectionData *selection_data, guint info, guint t,
+ gpointer data
+)
+{
+ gboolean data_sent = FALSE;
+ gint row;
+ GList *glist;
+ GtkCList *clist;
+ my_prog_window_struct *win = (my_prog_window_struct *)data;
+ if((widget == NULL) || (win == NULL) || (dc == NULL))
+ return;
+
+ clist = GTK_CLIST(widget);
+
+ /* Get last selected row on the clist. */
+ glist = clist->selection_end;
+ row = (glist != NULL) ? (gint)glist->data : -1;
+
+ /* Selected row in bounds? */
+ if((row >= 0) && (row < clist->rows))
+ {
+ gint cell_type;
+ guint8 spacing = 0;
+ gchar *text = NULL;
+ GdkPixmap *pixmap = NULL;
+ GdkBitmap *mask = NULL;
+
+
+ /* Obtain the text of the first cell of the selected
+ * row, then use this text and send it out to the DDE.
+ */
+ cell_type = gtk_clist_get_cell_type(clist, row, 0);
+ switch(cell_type)
+ {
+ case GTK_CELL_TEXT:
+ gtk_clist_get_text(
+ clist, row, 0, &text
+ );
+ case GTK_CELL_PIXTEXT:
+ gtk_clist_get_pixtext(
+ clist, row, 0,
+ &text, &spacing, &pixmap, &mask
+ );
+ }
+ /* Selected row has text? */
+ if(text != NULL)
+ {
+ /* Send out the data using the selection system,
+ * this is also used for cut and paste but
+ * GTK uses it for drag and drop as well. When
+ * sending a string, GTK will ensure that a null
+ * terminating byte is added to the end so we
+ * do not need to add it. GTK also coppies the
+ * data so the original will never be modified.
+ */
+ gtk_selection_data_set(
+ selection_data,
+ GDK_SELECTION_TYPE_STRING,
+ 8, /* 8 bits per character. */
+ text, strlen(text)
+ );
+ data_sent = TRUE;
+ }
+ }
+
+ /* If we did not send out any data (for whatever reason),
+ * then send out an error response since this function
+ * needs to gaurantee a response when reasonably possible.
+ */
+ if(!data_sent)
+ {
+ const gchar *cstrptr = "Error";
+
+ gtk_selection_data_set(
+ selection_data,
+ GDK_SELECTION_TYPE_STRING,
+ 8, /* 8 bits per character. */
+ cstrptr, strlen(cstrptr)
+ );
+ data_sent = TRUE;
+ }
+}
+
+/*
+ * DND "drag_data_received" handler. When DNDDataRequestCB()
+ * calls gtk_selection_data_set() to send out the data, this function
+ * recieves it and is responsible for handling it.
+ *
+ * This is also the only DND callback function where the given
+ * inputs may reflect those of the drop target so we need to check
+ * if this is the same structure or not.
+ */
+static void DNDDataRecievedCB(
+ GtkWidget *widget, GdkDragContext *dc,
+ gint x, gint y, GtkSelectionData *selection_data,
+ guint info, guint t, gpointer data
+)
+{
+ gboolean same;
+ gint row, column;
+ GtkWidget *source_widget;
+ GtkCList *clist;
+ my_prog_window_struct *win = (my_prog_window_struct *)data;
+ if((widget == NULL) || (win == NULL) || (dc == NULL))
+ return;
+
+ /* Important, check if we actually got data. Sometimes errors
+ * occure and selection_data will be NULL.
+ */
+ if(selection_data == NULL)
+ return;
+ if(selection_data->length < 0)
+ return;
+
+ /* Source and target widgets are the same? */
+ source_widget = gtk_drag_get_source_widget(dc);
+ same = (source_widget == widget) ? TRUE : FALSE;
+
+ clist = GTK_CLIST(widget);
+
+ /* Calculate the row and column at which the drop has occured
+ * over on the clist based on the given x and y coordinates.
+ */
+ if(!gtk_clist_get_selection_info(
+ clist,
+ x,
+ y - ((clist->flags & GTK_CLIST_SHOW_TITLES) ?
+ clist->column_title_area.height +
+ clist->column_title_area.y : 0),
+ &row, &column
+ ))
+ {
+ row = -1;
+ column = 0;
+ }
+
+ /* Now check if the data format type is one that we support
+ * (remember, data format type, not data type).
+ *
+ * We check this by testing if info matches one of the info
+ * values that we have defined.
+ *
+ * Note that we can also iterate through the atoms in:
+ * GList *glist = dc->targets;
+ *
+ * while(glist != NULL)
+ * {
+ * gchar *name = gdk_atom_name((GdkAtom)glist->data);
+ * * strcmp the name to see if it matches
+ * * one that we support
+ * *
+ * glist = glist->next;
+ * }
+ */
+ if((info == DRAG_TAR_INFO_0) ||
+ (info == DRAG_TAR_INFO_1) ||
+ (info == DRAG_TAR_INFO_2)
+ )
+ {
+ gint new_row;
+ gchar *new_text = selection_data->data;
+ gchar *text[1];
+ GdkPixmap *pixmap = NULL;
+ GdkBitmap *mask = NULL;
+
+
+ /* Reset cells text array for adding a new row, we'll
+ * set the actual text after the row has been created.
+ */
+ text[0] = "";
+
+ /* Append or insert the new row? */
+ if((row >= 0) && (row < clist->rows))
+ new_row = gtk_clist_insert(clist, row, text);
+ else
+ new_row = gtk_clist_append(clist, text);
+
+ /* Check the name of the cell obtained from the received
+ * data and decipher which pixmap and mask pair to
+ * use for the new cell (if any).
+ */
+ if(!strcmp(new_text, "Sphere"))
+ {
+ pixmap = sphere_pixmap;
+ mask = sphere_mask;
+ }
+ else if(!strcmp(new_text, "Cube"))
+ {
+ pixmap = cube_pixmap;
+ mask = cube_mask;
+ }
+ else if(!strcmp(new_text, "Cone"))
+ {
+ pixmap = cone_pixmap;
+ mask = cone_mask;
+ }
+ /* Update the new row's cell with the pixmap (if
+ * available) and the received data as the text.
+ */
+ if(pixmap != NULL)
+ gtk_clist_set_pixtext(
+ clist, new_row, 0,
+ new_text, 2, pixmap, mask
+ );
+ else
+ gtk_clist_set_text(
+ clist, new_row, 0,
+ new_text
+ );
+ }
+}
+
+/*
+ * DND "drag_data_delete" handler, this function is called when
+ * the data on the source `should' be deleted (ie if the DND was
+ * a move).
+ */
+static void DNDDataDeleteCB(
+ GtkWidget *widget, GdkDragContext *dc, gpointer data
+)
+{
+ gint row;
+ GList *glist;
+ GtkCList *clist;
+ my_prog_window_struct *win = (my_prog_window_struct *)data;
+ if((widget == NULL) || (win == NULL) || (dc == NULL))
+ return;
+
+ clist = GTK_CLIST(widget);
+
+ /* Get last selected row on the clist. */
+ glist = clist->selection_end;
+ row = (glist != NULL) ? (gint)glist->data : -1;
+
+ /* Remove the last selected row. */
+ gtk_clist_remove(clist, row);
+}
+
+
+/*
+ * Creates a new window with everything set up.
+ */
+static my_prog_window_struct *WindowNew(const gchar *title)
+{
+ gchar *heading[1];
+ GtkWidget *w, *parent, *scrolled_window;
+ GtkCList *clist;
+ GtkTargetEntry target_entry[3];
+ my_prog_window_struct *win = (my_prog_window_struct *)g_malloc0(
+ sizeof(my_prog_window_struct)
+ );
+ if(win == NULL)
+ return(NULL);
+
+ /* Create toplevel. */
+ w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ win->toplevel = w;
+ gtk_signal_connect(
+ GTK_OBJECT(w), "delete_event",
+ GTK_SIGNAL_FUNC(HandleCloseCB), win
+ );
+ gtk_widget_set_usize(w, 200, 150);
+ gtk_window_set_title(
+ GTK_WINDOW(w),
+ (title == NULL) ? "Untitled" : title
+ );
+ /* Do not show the toplevel just yet. */
+ parent = w;
+
+ /* Main vbox to hold clist. */
+ w = gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(parent), w);
+ gtk_widget_show(w);
+ parent = w;
+
+ /* Create the scrolled window for the clist and the clist
+ * itself.
+ */
+ scrolled_window = gtk_scrolled_window_new(NULL, NULL);
+ gtk_box_pack_start(GTK_BOX(parent), scrolled_window, TRUE, TRUE, 0);
+ gtk_scrolled_window_set_policy(
+ GTK_SCROLLED_WINDOW (scrolled_window),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC
+ );
+ gtk_widget_show(scrolled_window);
+
+ heading[0] = "Name";
+ w = gtk_clist_new_with_titles(1, heading);
+ clist = GTK_CLIST(w);
+ win->clist = w;
+ gtk_clist_set_selection_mode(clist, GTK_SELECTION_SINGLE);
+ gtk_clist_set_shadow_type(clist, GTK_SHADOW_IN);
+ gtk_clist_column_titles_passive(clist);
+ gtk_clist_set_column_justification(
+ clist, 0, GTK_JUSTIFY_LEFT
+ );
+ gtk_clist_set_row_height(clist, 20);
+ gtk_signal_connect(
+ GTK_OBJECT(w), "select_row",
+ GTK_SIGNAL_FUNC(CListSelectRowCB), win
+ );
+ gtk_signal_connect(
+ GTK_OBJECT(w), "unselect_row",
+ GTK_SIGNAL_FUNC(CListUnselectRowCB), win
+ );
+ gtk_container_add(GTK_CONTAINER(scrolled_window), w);
+
+ /* Realize the clist widget and make sure it has a window,
+ * this will be for DND setup.
+ */
+ gtk_widget_realize(w);
+ if(!GTK_WIDGET_NO_WINDOW(w))
+ {
+ /* DND: Set up the clist as a potential DND destination.
+ * First we set up target_entry which is a sequence of of
+ * structure which specify the kinds (which we define) of
+ * drops accepted on this widget.
+ */
+
+ /* Set up the list of data format types that our DND
+ * callbacks will accept.
+ */
+ target_entry[0].target = DRAG_TAR_NAME_0;
+ target_entry[0].flags = 0;
+ target_entry[0].info = DRAG_TAR_INFO_0;
+ target_entry[1].target = DRAG_TAR_NAME_1;
+ target_entry[1].flags = 0;
+ target_entry[1].info = DRAG_TAR_INFO_1;
+ target_entry[2].target = DRAG_TAR_NAME_2;
+ target_entry[2].flags = 0;
+ target_entry[2].info = DRAG_TAR_INFO_2;
+
+ /* Set the drag destination for this widget, using the
+ * above target entry types, accept move's and coppies'.
+ */
+ gtk_drag_dest_set(
+ w,
+ GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT |
+ GTK_DEST_DEFAULT_DROP,
+ target_entry,
+ sizeof(target_entry) / sizeof(GtkTargetEntry),
+ GDK_ACTION_MOVE | GDK_ACTION_COPY
+ );
+ gtk_signal_connect(
+ GTK_OBJECT(w), "drag_motion",
+ GTK_SIGNAL_FUNC(DNDDragMotionCB),
+ win
+ );
+
+ /* Set the drag source for this widget, allowing the user
+ * to drag items off of this clist.
+ */
+ gtk_drag_source_set(
+ w,
+ GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
+ target_entry,
+ sizeof(target_entry) / sizeof(GtkTargetEntry),
+ GDK_ACTION_MOVE | GDK_ACTION_COPY
+ );
+ /* Set DND signals on clist. */
+ gtk_signal_connect(
+ GTK_OBJECT(w), "drag_begin",
+ GTK_SIGNAL_FUNC(DNDBeginCB), win
+ );
+ gtk_signal_connect(
+ GTK_OBJECT(w), "drag_end",
+ GTK_SIGNAL_FUNC(DNDEndCB), win
+ );
+ gtk_signal_connect(
+ GTK_OBJECT(w), "drag_data_get",
+ GTK_SIGNAL_FUNC(DNDDataRequestCB), win
+ );
+ gtk_signal_connect(
+ GTK_OBJECT(w), "drag_data_received",
+ GTK_SIGNAL_FUNC(DNDDataRecievedCB), win
+ );
+ gtk_signal_connect(
+ GTK_OBJECT(w), "drag_data_delete",
+ GTK_SIGNAL_FUNC(DNDDataDeleteCB), win
+ );
+ }
+
+
+ gtk_widget_show(w);
+
+
+ /* Now show the toplevel. */
+ gtk_widget_show(win->toplevel);
+
+ return(win);
+}
+
+/*
+ * Dealloactes the given window and all its resources.
+ */
+static void WindowDelete(my_prog_window_struct *win)
+{
+ GtkWidget *w;
+
+
+ if(win == NULL)
+ return;
+
+ /* Destroy the GtkCList, first remove the DND settings on it
+ * and then destroy it.
+ */
+ w = win->clist;
+ if(w != NULL)
+ {
+ /* Remove DND settings on this widget. */
+ gtk_drag_dest_unset(w);
+ gtk_drag_source_unset(w);
+
+ win->clist = NULL;
+ gtk_widget_destroy(w);
+ }
+
+ /* Destroy toplevel GtkWindow, thus destroying all of its
+ * child widgets.
+ */
+ w = win->toplevel;
+ if(w != NULL)
+ {
+ win->toplevel = NULL;
+ gtk_widget_destroy(w);
+ }
+
+ /* Deallocate the structure itself. */
+ g_free(win);
+}
+
+
+int main(int argc, char **argv)
+{
+ GtkStyle *style;
+ GdkWindow *window;
+ GdkPixmap **pixmap;
+ GdkBitmap **mask;
+ gchar **pixmap_data;
+ my_prog_window_struct *win[2];
+
+
+ gtk_init(&argc, &argv);
+
+
+ /* Load the pixmaps that we will be using as GtkCList cell
+ * pixmaps and DND icons.
+ *
+ * This requires that we use the root window for loading of
+ * the pixmaps (hence the need to include gdk/gdkx.h).
+ */
+ style = gtk_widget_get_default_style();
+ window = (GdkWindow *)GDK_ROOT_PARENT();
+
+ pixmap = &sphere_pixmap;
+ mask = &sphere_mask;
+ pixmap_data = sphere_xpm;
+ *pixmap = gdk_pixmap_create_from_xpm_d(
+ window, mask,
+ &style->bg[GTK_STATE_NORMAL],
+ pixmap_data
+ );
+
+ pixmap = &cube_pixmap;
+ mask = &cube_mask;
+ pixmap_data = cube_xpm;
+ *pixmap = gdk_pixmap_create_from_xpm_d(
+ window, mask,
+ &style->bg[GTK_STATE_NORMAL],
+ pixmap_data
+ );
+
+ pixmap = &cone_pixmap;
+ mask = &cone_mask;
+ pixmap_data = cone_xpm;
+ *pixmap = gdk_pixmap_create_from_xpm_d(
+ window, mask,
+ &style->bg[GTK_STATE_NORMAL],
+ pixmap_data
+ );
+
+
+ /* Create two windows. */
+ win[0] = WindowNew("List 1");
+ win[1] = WindowNew("List 2");
+
+ /* Add some items to the first list. */
+ if(win[0] != NULL)
+ {
+ GtkCList *clist = (GtkCList *)win[0]->clist;
+ if(clist != NULL)
+ {
+ gint new_row;
+ gchar *text[1];
+
+
+ /* Reset cells text array for adding a new row,
+ * we'll set the actual text after the row has
+ * been created.
+ */
+ text[0] = "";
+
+ /* Begin creating new rows. */
+
+ new_row = gtk_clist_append(clist, text);
+ gtk_clist_set_pixtext(
+ clist, new_row, 0,
+ "Sphere", 2,
+ sphere_pixmap, sphere_mask
+ );
+
+ new_row = gtk_clist_append(clist, text);
+ gtk_clist_set_pixtext(
+ clist, new_row, 0,
+ "Cube", 2,
+ cube_pixmap, cube_mask
+ );
+
+ new_row = gtk_clist_append(clist, text);
+ gtk_clist_set_pixtext(
+ clist, new_row, 0,
+ "Cone", 2,
+ cone_pixmap, cone_mask
+ );
+ }
+ }
+
+
+ /* Enter main gtk loop. */
+ gtk_main();
+
+
+ /* Application now shutting down, begin deallocating
+ * resources.
+ */
+ WindowDelete(win[0]);
+ WindowDelete(win[1]);
+
+ gdk_pixmap_unref(sphere_pixmap);
+ gdk_bitmap_unref(sphere_mask);
+ gdk_pixmap_unref(cube_pixmap);
+ gdk_bitmap_unref(cube_mask);
+ gdk_pixmap_unref(cone_pixmap);
+ gdk_bitmap_unref(cone_mask);
+
+
+ return(0);
+}