changeset 54:c4e1139d9872

Added new tree functions, and fixed a memory leak as well as use of invalid memory when a button destroys itself.
author bsmith@81767d24-ef19-dc11-ae90-00e081727c95
date Sat, 17 Nov 2001 17:40:16 +0000
parents 8add9a616d0e
children b6948eac375a
files dw.def dw.h dww.def gtk/dw.c os2/dw.c win/dw.c
diffstat 6 files changed, 712 insertions(+), 161 deletions(-) [+]
line wrap: on
line diff
--- a/dw.def	Tue Nov 13 11:14:00 2001 +0000
+++ b/dw.def	Sat Nov 17 17:40:16 2001 +0000
@@ -60,6 +60,7 @@
   dw_window_raise                        @76
   dw_window_lower                        @77
   dw_window_click_default                @78
+  dw_window_redraw                       @79
 
   dw_bitmap_new                          @80
 
@@ -202,6 +203,11 @@
   dw_tree_insert                         @371
   dw_tree_clear                          @372
   dw_tree_delete                         @373
+  dw_tree_set                            @374
+  dw_tree_expand                         @375
+  dw_tree_collapse                       @376
+  dw_tree_item_select                    @377
+  dw_tree_set_data                       @378
 
   dw_font_text_extents                   @380
 
--- a/dw.h	Tue Nov 13 11:14:00 2001 +0000
+++ b/dw.h	Sat Nov 17 17:40:16 2001 +0000
@@ -594,6 +594,7 @@
 int dw_window_raise(HWND handle);
 int dw_window_lower(HWND handle);
 int dw_window_destroy(HWND handle);
+void dw_window_redraw(HWND handle);
 int dw_window_set_font(HWND handle, char *fontname);
 int dw_window_set_color(HWND handle, unsigned long fore, unsigned long back);
 HWND dw_window_new(HWND hwndOwner, char *title, unsigned long flStyle);
@@ -664,9 +665,14 @@
 long dw_spinbutton_query(HWND handle);
 int dw_checkbox_query(HWND handle);
 void dw_checkbox_set(HWND handle, int value);
-HWND dw_tree_insert(HWND handle, char *title, unsigned long icon, HWND parent);
+HWND dw_tree_insert(HWND handle, char *title, unsigned long icon, HWND parent, void *itemdata);
 void dw_tree_clear(HWND handle);
 void dw_tree_delete(HWND handle, HWND item);
+void dw_tree_set(HWND handle, HWND item, char *title, unsigned long icon);
+void dw_tree_expand(HWND handle, HWND item);
+void dw_tree_collapse(HWND handle, HWND item);
+void dw_tree_item_select(HWND handle, HWND item);
+void dw_tree_set_data(HWND handle, HWND item, void *itemdata);
 int dw_container_setup(HWND handle, unsigned long *flags, char **titles, int count, int separator);
 unsigned long dw_icon_load(unsigned long module, unsigned long id);
 void dw_icon_free(unsigned long handle);
--- a/dww.def	Tue Nov 13 11:14:00 2001 +0000
+++ b/dww.def	Sat Nov 17 17:40:16 2001 +0000
@@ -57,6 +57,7 @@
   dw_window_raise                        @76
   dw_window_lower                        @77
   dw_window_click_default                @78
+  dw_window_redraw                       @79
 
   dw_bitmap_new                          @80
 
@@ -199,6 +200,11 @@
   dw_tree_insert                         @371
   dw_tree_clear                          @372
   dw_tree_delete                         @373
+  dw_tree_set                            @374
+  dw_tree_expand                         @375
+  dw_tree_collapse                       @376
+  dw_tree_item_select                    @377
+  dw_tree_set_data                       @378
 
   dw_font_text_extents                   @380
 
--- a/gtk/dw.c	Tue Nov 13 11:14:00 2001 +0000
+++ b/gtk/dw.c	Sat Nov 17 17:40:16 2001 +0000
@@ -90,6 +90,7 @@
 void _expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data);
 void _set_focus_event(GtkWindow *window, GtkWidget *widget, gpointer data);
 void _tree_select_event(GtkTree *tree, GtkWidget *child, gpointer data);
+void _tree_context_event(GtkWidget *widget, GdkEventButton *event, gpointer data);
 
 void msleep(long period);
 
@@ -108,7 +109,7 @@
 
 } SignalHandler;
 
-#define SIGNALMAX 14
+#define SIGNALMAX 15
 
 /* A list of signal forwarders, to account for paramater differences. */
 SignalList SignalTranslate[SIGNALMAX] = {
@@ -123,6 +124,7 @@
 	{ _generic_event, "clicked" },
 	{ _container_select_event, "container-select" },
 	{ _container_context_event, "container-context" },
+	{ _tree_context_event, "tree-context" },
 	{ _item_select_event, "item-select" },
 	{ _tree_select_event, "tree-select" },
 	{ _set_focus_event, "set-focus" }
@@ -235,7 +237,7 @@
 	{
 		int (*closefunc)(HWND, void *) = work->func;
 
-		closefunc(widget, data);
+		closefunc(widget, work->data);
 	}
 	return TRUE;
 }
@@ -367,16 +369,53 @@
 	}
 }
 
+void _tree_context_event(GtkWidget *widget, GdkEventButton *event, gpointer data)
+{
+	SignalHandler *work = (SignalHandler *)data;
+
+	if(work)
+	{
+		if(event->button == 3)
+		{
+			void (*contextfunc)(HWND, char *, int, int, void *, void *) = work->func;
+			char *text = (char *)gtk_object_get_data(GTK_OBJECT(widget), "text");
+			void *itemdata = (void *)gtk_object_get_data(GTK_OBJECT(widget), "itemdata");
+
+			if(widget != work->window)
+			{
+				GtkWidget *tree = (GtkWidget *)gtk_object_get_user_data(GTK_OBJECT(work->window));
+
+				if(tree && GTK_IS_TREE(tree))
+				{
+					GtkWidget *lastselect = (GtkWidget *)gtk_object_get_data(GTK_OBJECT(tree), "lastselect");
+
+					if(lastselect && GTK_IS_TREE_ITEM(lastselect))
+					{
+						text = (char *)gtk_object_get_data(GTK_OBJECT(lastselect), "text");
+						itemdata = (void *)gtk_object_get_data(GTK_OBJECT(lastselect), "itemdata");
+					}
+				}
+			}
+
+			contextfunc(work->window, text, event->x, event->y, work->data, itemdata);
+		}
+	}
+}
+
 void _tree_select_event(GtkTree *tree, GtkWidget *child, gpointer data)
 {
 	SignalHandler *work = (SignalHandler *)data;
 
 	if(work)
 	{
-		void (*treeselectfunc)(HWND, HWND, char *, void *) = work->func;
+		void (*treeselectfunc)(HWND, HWND, char *, void *, void *) = work->func;
 		char *text = (char *)gtk_object_get_data(GTK_OBJECT(child), "text");
-
-		treeselectfunc(work->window, child, text, work->data);
+		void *itemdata = (char *)gtk_object_get_data(GTK_OBJECT(child), "itemdata");
+		GtkWidget *treeroot = (GtkWidget *)gtk_object_get_data(GTK_OBJECT(child), "tree");
+
+		if(treeroot && GTK_IS_TREE(treeroot))
+			gtk_object_set_data(GTK_OBJECT(treeroot), "lastselect", (gpointer)child);
+		treeselectfunc(work->window, child, text, itemdata, work->data);
 	}
 }
 
@@ -974,6 +1013,14 @@
 	return 0;
 }
 
+/* Causes entire window to be invalidated and redrawn.
+ * Parameters:
+ *           handle: Toplevel window handle to be redrawn.
+ */
+void dw_window_redraw(HWND handle)
+{
+}
+
 /*
  * Changes a window's parent to newparent.
  * Parameters:
@@ -2692,8 +2739,9 @@
  *          title: The text title of the entry.
  *          icon: Handle to coresponding icon.
  *          parent: Parent handle or 0 if root.
+ *          itemdata: Item specific data.
  */
-HWND dw_tree_insert(HWND handle, char *title, unsigned long icon, HWND parent)
+HWND dw_tree_insert(HWND handle, char *title, unsigned long icon, HWND parent, void *itemdata)
 {
 	GtkWidget *item, *tree, *subtree, *label, *hbox, *pixmap;
 	GdkPixmap *gdkpix;
@@ -2710,7 +2758,10 @@
 	item = gtk_tree_item_new();
 	label = gtk_label_new(title);
 	gtk_object_set_data(GTK_OBJECT(item), "text", (gpointer)strdup(title));
+	gtk_object_set_data(GTK_OBJECT(item), "itemdata", (gpointer)itemdata);
+	gtk_object_set_data(GTK_OBJECT(item), "tree", (gpointer)tree);
 	hbox = gtk_hbox_new(FALSE, 2);
+	gtk_object_set_data(GTK_OBJECT(item), "hbox", (gpointer)hbox);
 	gdkpix = _find_pixmap(&gdkbmp, icon, hbox);
 	pixmap = gtk_pixmap_new(gdkpix, gdkbmp);
 	gtk_container_add(GTK_CONTAINER(item), hbox);
@@ -2733,22 +2784,106 @@
 			if(thisfunc && work)
 				gtk_signal_connect(GTK_OBJECT(subtree), "select-child", GTK_SIGNAL_FUNC(thisfunc), work);
 
+			thisfunc = (void *)gtk_object_get_data(GTK_OBJECT(tree), "container-context-func");
+			work = (void *)gtk_object_get_data(GTK_OBJECT(tree), "container-context-data");
+
+			if(thisfunc && work)
+				gtk_signal_connect(GTK_OBJECT(subtree), "button_press_event", GTK_SIGNAL_FUNC(thisfunc), work);
+
 			gtk_object_set_user_data(GTK_OBJECT(parent), subtree);
 			gtk_tree_set_selection_mode(GTK_TREE(subtree), GTK_SELECTION_SINGLE);
 			gtk_tree_set_view_mode(GTK_TREE(subtree), GTK_TREE_VIEW_ITEM);
 			gtk_tree_item_set_subtree(GTK_TREE_ITEM(parent), subtree);
+			gtk_tree_item_collapse(GTK_TREE_ITEM(parent));
 			gtk_widget_show(subtree);
+			gtk_tree_item_expand(GTK_TREE_ITEM(parent));
+			gtk_tree_item_collapse(GTK_TREE_ITEM(parent));
 		}
 		gtk_tree_append(GTK_TREE(subtree), item);
 	}
 	else
 		gtk_tree_append(GTK_TREE(tree), item);
+	gtk_tree_item_expand(GTK_TREE_ITEM(item));
+	gtk_tree_item_collapse(GTK_TREE_ITEM(item));
 	gtk_widget_show(item);
 	DW_MUTEX_UNLOCK;
 	return item;
 }
 
 /*
+ * Sets the text and icon of an item in a tree window (widget).
+ * Parameters:
+ *          handle: Handle to the tree containing the item.
+ *          item: Handle of the item to be modified.
+ *          title: The text title of the entry.
+ *          icon: Handle to coresponding icon.
+ */
+void dw_tree_set(HWND handle, HWND item, char *title, unsigned long icon)
+{
+	GtkWidget *label, *hbox, *pixmap;
+	GdkPixmap *gdkpix;
+	GdkBitmap *gdkbmp;
+	char *oldtext;
+	int _locked_by_me = FALSE;
+
+	DW_MUTEX_LOCK;
+	oldtext = (char *)gtk_object_get_data(GTK_OBJECT(item), "text");
+	if(oldtext)
+		free(oldtext);
+	label = gtk_label_new(title);
+	gtk_object_set_data(GTK_OBJECT(item), "text", (gpointer)strdup(title));
+	hbox = (GtkWidget *)gtk_object_get_data(GTK_OBJECT(item), "hbox");
+	gtk_widget_destroy(hbox);
+	hbox = gtk_hbox_new(FALSE, 2);
+	gtk_object_set_data(GTK_OBJECT(item), "hbox", (gpointer)hbox);
+	gdkpix = _find_pixmap(&gdkbmp, icon, hbox);
+	pixmap = gtk_pixmap_new(gdkpix, gdkbmp);
+	gtk_container_add(GTK_CONTAINER(item), hbox);
+	gtk_box_pack_start(GTK_BOX(hbox), pixmap, FALSE, TRUE, 0);
+	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 0);
+	gtk_widget_show(label);
+	gtk_widget_show(pixmap);
+	gtk_widget_show(hbox);
+	DW_MUTEX_UNLOCK;
+}
+
+/*
+ * Sets the item data of a tree item.
+ * Parameters:
+ *          handle: Handle to the tree containing the item.
+ *          item: Handle of the item to be modified.
+ *          itemdata: User defined data to be associated with item.
+ */
+void dw_tree_set_data(HWND handle, HWND item, void *itemdata)
+{
+	int _locked_by_me = FALSE;
+
+	DW_MUTEX_LOCK;
+	gtk_object_set_data(GTK_OBJECT(item), "itemdata", (gpointer)itemdata);
+	DW_MUTEX_UNLOCK;
+}
+
+/*
+ * Sets this item as the active selection.
+ * Parameters:
+ *       handle: Handle to the tree window (widget) to be selected.
+ *       item: Handle to the item to be selected.
+ */
+void dw_tree_item_select(HWND handle, HWND item)
+{
+	GtkWidget *lastselect;
+	int _locked_by_me = FALSE;
+
+	DW_MUTEX_LOCK;
+	lastselect = (GtkWidget *)gtk_object_get_data(GTK_OBJECT(handle), "lastselect");
+	if(lastselect && GTK_IS_TREE_ITEM(lastselect))
+		gtk_tree_item_deselect(GTK_TREE_ITEM(lastselect));
+	gtk_tree_item_select(GTK_TREE_ITEM(item));
+	gtk_object_set_data(GTK_OBJECT(handle), "lastselect", (gpointer)item);
+	DW_MUTEX_UNLOCK;
+}
+
+/*
  * Removes all nodes from a tree.
  * Parameters:
  *       handle: Handle to the window (widget) to be cleared.
@@ -2770,6 +2905,38 @@
 }
 
 /*
+ * Expands a node on a tree.
+ * Parameters:
+ *       handle: Handle to the tree window (widget).
+ *       item: Handle to node to be expanded.
+ */
+void dw_tree_expand(HWND handle, HWND item)
+{
+	int _locked_by_me = FALSE;
+
+	DW_MUTEX_LOCK;
+	if(GTK_IS_TREE_ITEM(item))
+		gtk_tree_item_expand(GTK_TREE_ITEM(item));
+	DW_MUTEX_UNLOCK;
+}
+
+/*
+ * Collapses a node on a tree.
+ * Parameters:
+ *       handle: Handle to the tree window (widget).
+ *       item: Handle to node to be collapsed.
+ */
+void dw_tree_collapse(HWND handle, HWND item)
+{
+	int _locked_by_me = FALSE;
+
+	DW_MUTEX_LOCK;
+	if(GTK_IS_TREE_ITEM(item))
+		gtk_tree_item_collapse(GTK_TREE_ITEM(item));
+	DW_MUTEX_UNLOCK;
+}
+
+/*
  * Removes a node from a tree.
  * Parameters:
  *       handle: Handle to the window (widget) to be cleared.
@@ -2777,7 +2944,7 @@
  */
 void dw_tree_delete(HWND handle, HWND item)
 {
-	GtkWidget *tree;
+	GtkWidget *tree, *lastselect;
 	int _locked_by_me = FALSE;
 
 	DW_MUTEX_LOCK;
@@ -2787,7 +2954,13 @@
 		DW_MUTEX_UNLOCK;
 		return;
 	}
+
+	lastselect = (GtkWidget *)gtk_object_get_data(GTK_OBJECT(handle), "lastselect");
+	if(lastselect == item)
+		gtk_object_set_data(GTK_OBJECT(handle), "lastselect", NULL);
+
 	gtk_tree_remove_item(GTK_TREE(tree), item);
+	gtk_widget_destroy(item);
 	DW_MUTEX_UNLOCK;
 }
 
@@ -5200,6 +5373,21 @@
 		thisname = "button_press_event";
 		thisfunc = _findsigfunc("container-context");
 	}
+	else if(GTK_IS_TREE(thiswindow)  && strcmp(signame, "container-context") == 0)
+	{
+		thisfunc = _findsigfunc("tree-context");
+
+		work->window = window;
+		work->data = data;
+		work->func = sigfunc;
+
+		gtk_object_set_data(GTK_OBJECT(thiswindow), "container-context-func", (gpointer)thisfunc);
+		gtk_object_set_data(GTK_OBJECT(thiswindow), "container-context-data", (gpointer)work);
+		gtk_signal_connect(GTK_OBJECT(thiswindow), "button_press_event", GTK_SIGNAL_FUNC(thisfunc), work);
+		gtk_signal_connect(GTK_OBJECT(window), "button_press_event", GTK_SIGNAL_FUNC(thisfunc), work);
+		DW_MUTEX_UNLOCK;
+		return;
+	}
 	else if(GTK_IS_CLIST(thiswindow) && strcmp(signame, "container-select") == 0)
 	{
 		thisname = "button_press_event";
--- a/os2/dw.c	Tue Nov 13 11:14:00 2001 +0000
+++ b/os2/dw.c	Sat Nov 17 17:40:16 2001 +0000
@@ -106,7 +106,7 @@
 	{ CN_ENTER, "container-select" },
 	{ CN_CONTEXTMENU, "container-context" },
 	{ LN_SELECT, "item-select" },
-	{ WM_USER+1, "tree-select" },
+	{ CN_EMPHASIS, "tree-select" },
 	{ WM_SETFOCUS, "set-focus" }
 };
 
@@ -153,6 +153,15 @@
 }
 #endif
 
+typedef struct _CNRITEM
+{
+	MINIRECORDCORE rc;
+	HPOINTER       hptrIcon;
+	PVOID          user;
+
+} CNRITEM, *PCNRITEM;
+
+
 /* This function changes the owner of buttons in to the
  * dynamicwindows handle to fix a problem in notebooks.
  */
@@ -186,24 +195,22 @@
 {
 	HENUM henum;
 	HWND child;
+	void *ptr = (void *)WinQueryWindowPtr(handle, QWP_USER);
 
 #ifndef NO_SIGNALS
 	dw_signal_disconnect_by_window(handle);
 #endif
 
+	if(ptr)
+	{
+		WinSetWindowPtr(handle, QWP_USER, 0);
+		free(ptr);
+	}
+
 	henum = WinBeginEnumWindows(handle);
 	while((child = WinGetNextWindow(henum)) != NULLHANDLE)
-	{
-		void *ptr = (void *)WinQueryWindowPtr(handle, QWP_USER);
-
-		if(ptr)
-		{
-			WinSetWindowPtr(handle, QWP_USER, 0);
-			free(ptr);
-		}
-
 		_free_window_memory(child);
-	}
+
 	WinEndEnumWindows(henum);
 	return;
 }
@@ -1579,30 +1586,71 @@
 						break;
 					case CN_CONTEXTMENU:
 						{
-							int (*containercontextfunc)(HWND, char *, int, int, void *) = (int (*)(HWND, char *, int, int, void *))tmp->signalfunction;
+							int (*containercontextfunc)(HWND, char *, int, int, void *, void *) = (int (*)(HWND, char *, int, int, void *, void *))tmp->signalfunction;
 							int id = SHORT1FROMMP(mp1);
 							HWND conthwnd = dw_window_from_id(hWnd, id);
 							char *text = NULL;
+							void *user = NULL;
 							LONG x,y;
 
 							if(mp2)
 							{
-								PRECORDCORE pre;
-
-								pre = (PRECORDCORE)mp2;
-								text = pre->pszIcon;
+								PCNRITEM pci;
+
+								pci = (PCNRITEM)mp2;
+
+								text = pci->rc.pszIcon;
+								user = pci->user;
 							}
 
-
 							dw_pointer_query_pos(&x, &y);
 
 							if(tmp->window == conthwnd)
 							{
-								result = containercontextfunc(tmp->window, text, x, y, tmp->data);
+								if(mp2)
+								{
+									NOTIFYRECORDEMPHASIS pre;
+
+									dw_tree_item_select(tmp->window, (HWND)mp2);
+									pre.pRecord = mp2;
+									pre.fEmphasisMask = CRA_CURSORED;
+									pre.hwndCnr = tmp->window;
+									_run_event(hWnd, WM_CONTROL, MPFROM2SHORT(0, CN_EMPHASIS), (MPARAM)&pre);
+								}
+								result = containercontextfunc(tmp->window, text, x, y, tmp->data, user);
 								tmp = NULL;
 							}
 						}
 						break;
+					case CN_EMPHASIS:
+						{
+							PNOTIFYRECORDEMPHASIS pre = (PNOTIFYRECORDEMPHASIS)mp2;
+							static int emph_recurse = 0;
+
+							if(!emph_recurse)
+							{
+								emph_recurse = 1;
+
+								if(mp2)
+								{
+									if(tmp->window == pre->hwndCnr)
+									{
+										PCNRITEM pci = (PCNRITEM)pre->pRecord;
+
+										if(pci && pre->fEmphasisMask & CRA_CURSORED)
+										{
+											int (*treeselectfunc)(HWND, HWND, char *, void *, void *) = (int (*)(HWND, HWND, char *, void *, void *))tmp->signalfunction;
+
+											result = treeselectfunc(tmp->window, (HWND)pci, pci->rc.pszIcon, pci->user, tmp->data);
+
+											tmp = NULL;
+										}
+									}
+								}
+								emph_recurse = 0;
+							}
+						}
+						break;
 					case LN_SELECT:
 						{
 							int (*listboxselectfunc)(HWND, int, void *) = (int (*)(HWND, int, void *))tmp->signalfunction;
@@ -1650,37 +1698,6 @@
 			}
 		}
 
-		if(tmp && origmsg == WM_BUTTON1DOWN)
-		{
-			if(tmp->message == WM_USER+1)
-			{
-				if(tmp->window == hWnd)
-				{
-					QUERYRECFROMRECT rc;
-					POINTS pts = (*((POINTS*)&mp1));
-					RECORDCORE *prc;
-
-					rc.cb = sizeof(QUERYRECFROMRECT);
-					rc.rect.xLeft = pts.x;
-					rc.rect.xRight = pts.x + 1;
-					rc.rect.yTop = pts.y;
-					rc.rect.yBottom = pts.y - 1;
-					rc.fsSearch = CMA_PARTIAL | CMA_ITEMORDER;
-
-					prc = (RECORDCORE *)WinSendMsg(hWnd, CM_QUERYRECORDFROMRECT, (MPARAM)CMA_FIRST, MPFROMP(&rc));
-
-					if(prc)
-					{
-						int (*treeselectfunc)(HWND, HWND, char *, void *) = (int (*)(HWND, HWND, char *, void *))tmp->signalfunction;
-
-						result = treeselectfunc(tmp->window, (HWND)prc, prc->pszIcon, tmp->data);
-
-						tmp = NULL;
-					}
-				}
-			}
-		}
-
 		if(tmp)
 			tmp = tmp->next;
 
@@ -2222,12 +2239,15 @@
 MRESULT EXPENTRY _BtProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
 {
 	BubbleButton *bubble;
+	PFNWP oldproc;
 
 	bubble = (BubbleButton *)WinQueryWindowPtr(hwnd, QWL_USER);
 
 	if(!bubble)
 		return WinDefWindowProc(hwnd, msg, mp1, mp2);
 
+	oldproc = bubble->pOldProc;
+
 	switch(msg)
 	{
 #ifndef NO_SIGNALS
@@ -2408,9 +2428,9 @@
 		break;
 	}
 
-	if(!bubble->pOldProc)
+	if(!oldproc)
 		return WinDefWindowProc(hwnd, msg, mp1, mp2);
-	return bubble->pOldProc(hwnd, msg, mp1, mp2);
+	return oldproc(hwnd, msg, mp1, mp2);
 }
 
 MRESULT EXPENTRY _RendProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
@@ -2731,9 +2751,67 @@
  */
 int dw_window_destroy(HWND handle)
 {
+	HWND parent = WinQueryWindow(handle, QW_PARENT);
+	Box *thisbox = WinQueryWindowPtr(parent, QWP_USER);
+
+	if(!handle)
+		return -1;
+
+	if(parent != HWND_DESKTOP && thisbox && thisbox->count)
+	{
+		int z, index = -1;
+		Item *tmpitem, *thisitem = thisbox->items;
+
+		for(z=0;z<thisbox->count;z++)
+		{
+			if(thisitem[z].hwnd == handle)
+				index = z;
+		}
+
+		if(index == -1)
+			return 0;
+
+		tmpitem = malloc(sizeof(Item)*(thisbox->count-1));
+
+		/* Copy all but the current entry to the new list */
+		for(z=0;z<index;z++)
+		{
+			tmpitem[z] = thisitem[z];
+		}
+		for(z=index+1;z<thisbox->count;z++)
+		{
+			tmpitem[z-1] = thisitem[z];
+		}
+
+		thisbox->items = tmpitem;
+		free(thisitem);
+		thisbox->count--;
+	}
+	_free_window_memory(handle);
 	return WinDestroyWindow(handle);
 }
 
+/* Causes entire window to be invalidated and redrawn.
+ * Parameters:
+ *           handle: Toplevel window handle to be redrawn.
+ */
+void dw_window_redraw(HWND handle)
+{
+	HWND window = WinWindowFromID(handle, FID_CLIENT);
+	Box *mybox = (Box *)WinQueryWindowPtr(window, QWP_USER);
+
+	if(window && mybox)
+	{
+		unsigned long width, height;
+
+		dw_window_get_pos_size(window, NULL, NULL, &width, &height);
+
+		WinShowWindow(mybox->items[0].hwnd, FALSE);
+		_do_resize(mybox, width, height);
+		WinShowWindow(mybox->items[0].hwnd, TRUE);
+	}
+}
+
 /*
  * Changes a window's parent to newparent.
  * Parameters:
@@ -4572,13 +4650,6 @@
 	WinSendMsg(handle,BM_SETCHECK,MPFROMSHORT(value),0);
 }
 
-typedef struct _CNRITEM
-{
-	MINIRECORDCORE rc;
-	HPOINTER       hptrIcon;
-
-} CNRITEM, *PCNRITEM;
-
 /*
  * Inserts an item into a tree window (widget).
  * Parameters:
@@ -4586,8 +4657,9 @@
  *          title: The text title of the entry.
  *          icon: Handle to coresponding icon.
  *          parent: Parent handle or 0 if root.
- */
-HWND dw_tree_insert(HWND handle, char *title, unsigned long icon, HWND parent)
+ *          itemdata: Item specific data.
+ */
+HWND dw_tree_insert(HWND handle, char *title, unsigned long icon, HWND parent, void *itemdata)
 {
 	ULONG        cbExtra;
 	PCNRITEM     pci;
@@ -4610,6 +4682,7 @@
 	pci->rc.hptrIcon    = icon;
 
 	pci->hptrIcon       = icon;
+	pci->user           = itemdata;
 
 	memset(&ri, 0, sizeof(RECORDINSERT));
 
@@ -4632,6 +4705,65 @@
 }
 
 /*
+ * Sets the text and icon of an item in a tree window (widget).
+ * Parameters:
+ *          handle: Handle to the tree containing the item.
+ *          item: Handle of the item to be modified.
+ *          title: The text title of the entry.
+ *          icon: Handle to coresponding icon.
+ */
+void dw_tree_set(HWND handle, HWND item, char *title, unsigned long icon)
+{
+	PCNRITEM pci = (PCNRITEM)item;
+
+	if(!pci)
+		return;
+
+	pci->rc.pszIcon     = title;
+	pci->rc.hptrIcon    = icon;
+
+	pci->hptrIcon       = icon;
+
+	WinSendMsg(handle, CM_INVALIDATERECORD, (MPARAM)&pci, MPFROM2SHORT(1, CMA_TEXTCHANGED));
+}
+
+/*
+ * Sets the item data of a tree item.
+ * Parameters:
+ *          handle: Handle to the tree containing the item.
+ *          item: Handle of the item to be modified.
+ *          itemdata: User defined data to be associated with item.
+ */
+void dw_tree_set_data(HWND handle, HWND item, void *itemdata)
+{
+	PCNRITEM pci = (PCNRITEM)item;
+
+	if(!pci)
+		return;
+
+	pci->user = itemdata;
+}
+
+/*
+ * Sets this item as the active selection.
+ * Parameters:
+ *       handle: Handle to the tree window (widget) to be selected.
+ *       item: Handle to the item to be selected.
+ */
+void dw_tree_item_select(HWND handle, HWND item)
+{
+	PRECORDCORE pCore = WinSendMsg(handle, CM_QUERYRECORD, (MPARAM)0L, MPFROM2SHORT(CMA_FIRST, CMA_ITEMORDER));
+
+	while(pCore)
+	{
+		if(pCore->flRecordAttr & CRA_SELECTED)
+			WinSendMsg(handle, CM_SETRECORDEMPHASIS, (MPARAM)pCore, MPFROM2SHORT(FALSE, CRA_SELECTED | CRA_CURSORED));
+		pCore = WinSendMsg(handle, CM_QUERYRECORD, (MPARAM)pCore, MPFROM2SHORT(CMA_NEXT, CMA_ITEMORDER));
+	}
+	WinSendMsg(handle, CM_SETRECORDEMPHASIS, (MPARAM)item, MPFROM2SHORT(TRUE, CRA_SELECTED | CRA_CURSORED));
+}
+
+/*
  * Removes all nodes from a tree.
  * Parameters:
  *       handle: Handle to the window (widget) to be cleared.
@@ -4642,6 +4774,28 @@
 }
 
 /*
+ * Expands a node on a tree.
+ * Parameters:
+ *       handle: Handle to the tree window (widget).
+ *       item: Handle to node to be expanded.
+ */
+void dw_tree_expand(HWND handle, HWND item)
+{
+	WinSendMsg(handle, CM_EXPANDTREE, MPFROMP(item), 0);
+}
+
+/*
+ * Collapses a node on a tree.
+ * Parameters:
+ *       handle: Handle to the tree window (widget).
+ *       item: Handle to node to be collapsed.
+ */
+void dw_tree_collapse(HWND handle, HWND item)
+{
+	WinSendMsg(handle, CM_COLLAPSETREE, MPFROMP(item), 0);
+}
+
+/*
  * Removes a node from a tree.
  * Parameters:
  *       handle: Handle to the window (widget) to be cleared.
--- a/win/dw.c	Tue Nov 13 11:14:00 2001 +0000
+++ b/win/dw.c	Sat Nov 17 17:40:16 2001 +0000
@@ -1154,7 +1154,7 @@
 					break;
 				case WM_NOTIFY:
 					{
-						if(tmp->message == TVN_SELCHANGED)
+						if(tmp->message == TVN_SELCHANGED || tmp->message == NM_RCLICK)
 						{
 							NMTREEVIEW FAR *tem=(NMTREEVIEW FAR *)mp2;
 							char tmpbuf[100];
@@ -1163,20 +1163,58 @@
 
 							if(strnicmp(tmpbuf, WC_TREEVIEW, strlen(WC_TREEVIEW))==0)
 							{
-								if(tem->hdr.code == TVN_SELCHANGED)
+								if(tem->hdr.code == TVN_SELCHANGED && tmp->message == TVN_SELCHANGED)
 								{
 									if(tmp->window == tem->hdr.hwndFrom)
 									{
-										int (*treeselectfunc)(HWND, HWND, char *, void *) = tmp->signalfunction;
-                                        TVITEM tvi;
+										int (*treeselectfunc)(HWND, HWND, char *, void *, void *) = tmp->signalfunction;
+										TVITEM tvi;
+										void **ptrs;
 
 										tvi.mask = TVIF_HANDLE;
 										tvi.hItem = tem->itemNew.hItem;
 
 										TreeView_GetItem(tmp->window, &tvi);
 
-										result = treeselectfunc(tmp->window, (HWND)tem->itemNew.hItem, (char *)tvi.lParam, tmp->data);
-
+										ptrs = (void **)tvi.lParam;
+										if(ptrs)
+											result = treeselectfunc(tmp->window, (HWND)tem->itemNew.hItem, (char *)ptrs[0], (void *)ptrs[1], tmp->data);
+
+										tmp = NULL;
+									}
+								}
+								else if(tem->hdr.code == NM_RCLICK && tmp->message == NM_RCLICK)
+								{
+									if(tmp->window == tem->hdr.hwndFrom)
+									{
+										int (*containercontextfunc)(HWND, char *, int, int, void *, void *) = tmp->signalfunction;
+										HTREEITEM hti;
+										TVITEM tvi;
+										TVHITTESTINFO thi;
+										void **ptrs = NULL;
+										LONG x, y;
+
+										dw_pointer_query_pos(&x, &y);
+
+										thi.pt.x = x;
+										thi.pt.y = y;
+
+										MapWindowPoints(HWND_DESKTOP, tmp->window, &thi.pt, 1);
+
+										hti = TreeView_HitTest(tmp->window, &thi);
+
+										if(hti)
+										{
+											tvi.mask = TVIF_HANDLE;
+											tvi.hItem = hti;
+
+											TreeView_GetItem(tmp->window, &tvi);
+											dw_tree_item_select(tmp->window, (HWND)hti);
+
+											ptrs = (void **)tvi.lParam;
+
+										}
+										containercontextfunc(tmp->window, ptrs ? (char *)ptrs[0] : NULL, x, y, tmp->data, ptrs ? ptrs[1] : NULL);
 										tmp = NULL;
 									}
 								}
@@ -1301,61 +1339,55 @@
 		break;
 	case WM_DESTROY:
 		/* Free memory before destroying */
-#if 0
-		/* Is this the right message? I seem to be
-		 * getting WM_DESTROY on windows that aren't
-		 * being destroyed.
-		 */
 		_free_window_memory(hWnd, 0);
 		EnumChildWindows(hWnd, _free_window_memory, 0);
-#endif
 		break;
-		case WM_CTLCOLORSTATIC:
-		case WM_CTLCOLORLISTBOX:
-		case WM_CTLCOLORBTN:
-		case WM_CTLCOLOREDIT:
-		case WM_CTLCOLORMSGBOX:
-		case WM_CTLCOLORSCROLLBAR:
-		case WM_CTLCOLORDLG:
+	case WM_CTLCOLORSTATIC:
+	case WM_CTLCOLORLISTBOX:
+	case WM_CTLCOLORBTN:
+	case WM_CTLCOLOREDIT:
+	case WM_CTLCOLORMSGBOX:
+	case WM_CTLCOLORSCROLLBAR:
+	case WM_CTLCOLORDLG:
+		{
+			ColorInfo *thiscinfo = (ColorInfo *)GetWindowLong((HWND)mp2, GWL_USERDATA);
+			if(thiscinfo && thiscinfo->fore != -1 && thiscinfo->back != -1)
 			{
-				ColorInfo *thiscinfo = (ColorInfo *)GetWindowLong((HWND)mp2, GWL_USERDATA);
-				if(thiscinfo && thiscinfo->fore != -1 && thiscinfo->back != -1)
+				if(thiscinfo->fore > -1 && thiscinfo->back > -1 &&
+				   thiscinfo->fore < 18 && thiscinfo->back < 18)
 				{
-					if(thiscinfo->fore > -1 && thiscinfo->back > -1 &&
-					   thiscinfo->fore < 18 && thiscinfo->back < 18)
-					{
-						SetTextColor((HDC)mp1, RGB(_red[thiscinfo->fore],
-												   _green[thiscinfo->fore],
+					SetTextColor((HDC)mp1, RGB(_red[thiscinfo->fore],
+											   _green[thiscinfo->fore],
 											   _blue[thiscinfo->fore]));
-						SetBkColor((HDC)mp1, RGB(_red[thiscinfo->back],
-												 _green[thiscinfo->back],
-												 _blue[thiscinfo->back]));
-						DeleteObject(thiscinfo->hbrush);
-						thiscinfo->hbrush = CreateSolidBrush(RGB(_red[thiscinfo->back],
-																 _green[thiscinfo->back],
-																 _blue[thiscinfo->back]));
-						SelectObject((HDC)mp1, thiscinfo->hbrush);
-						return (LONG)thiscinfo->hbrush;
-					}
-					if((thiscinfo->fore & DW_RGB_COLOR) == DW_RGB_COLOR && (thiscinfo->back & DW_RGB_COLOR) == DW_RGB_COLOR)
-					{
-						SetTextColor((HDC)mp1, RGB(DW_RED_VALUE(thiscinfo->fore),
-												   DW_GREEN_VALUE(thiscinfo->fore),
-												   DW_BLUE_VALUE(thiscinfo->fore)));
-						SetBkColor((HDC)mp1, RGB(DW_RED_VALUE(thiscinfo->back),
+					SetBkColor((HDC)mp1, RGB(_red[thiscinfo->back],
+											 _green[thiscinfo->back],
+											 _blue[thiscinfo->back]));
+					DeleteObject(thiscinfo->hbrush);
+					thiscinfo->hbrush = CreateSolidBrush(RGB(_red[thiscinfo->back],
+															 _green[thiscinfo->back],
+															 _blue[thiscinfo->back]));
+					SelectObject((HDC)mp1, thiscinfo->hbrush);
+					return (LONG)thiscinfo->hbrush;
+				}
+				if((thiscinfo->fore & DW_RGB_COLOR) == DW_RGB_COLOR && (thiscinfo->back & DW_RGB_COLOR) == DW_RGB_COLOR)
+				{
+					SetTextColor((HDC)mp1, RGB(DW_RED_VALUE(thiscinfo->fore),
+											   DW_GREEN_VALUE(thiscinfo->fore),
+											   DW_BLUE_VALUE(thiscinfo->fore)));
+					SetBkColor((HDC)mp1, RGB(DW_RED_VALUE(thiscinfo->back),
 												 DW_GREEN_VALUE(thiscinfo->back),
 												 DW_BLUE_VALUE(thiscinfo->back)));
-						DeleteObject(thiscinfo->hbrush);
-						thiscinfo->hbrush = CreateSolidBrush(RGB(DW_RED_VALUE(thiscinfo->back),
-																 DW_GREEN_VALUE(thiscinfo->back),
-																 DW_BLUE_VALUE(thiscinfo->back)));
-						SelectObject((HDC)mp1, thiscinfo->hbrush);
-						return (LONG)thiscinfo->hbrush;
-					}
+					DeleteObject(thiscinfo->hbrush);
+					thiscinfo->hbrush = CreateSolidBrush(RGB(DW_RED_VALUE(thiscinfo->back),
+															 DW_GREEN_VALUE(thiscinfo->back),
+															 DW_BLUE_VALUE(thiscinfo->back)));
+					SelectObject((HDC)mp1, thiscinfo->hbrush);
+					return (LONG)thiscinfo->hbrush;
 				}
-
 			}
-			break;
+
+		}
+		break;
 	}
 	if(result != -1)
 		return result;
@@ -1572,7 +1604,10 @@
 		switch( msg )
 		{
 		case WM_SETFOCUS:
-			_wndproc(hWnd, msg, mp1, mp2);
+            if(cinfo->combo)
+				_wndproc(cinfo->combo, msg, mp1, mp2);
+			else
+				_wndproc(hWnd, msg, mp1, mp2);
 			break;
 		case WM_CHAR:
 			if(LOWORD(mp1) == '\t')
@@ -1754,43 +1789,47 @@
 		break;
 	case WM_CONTEXTMENU:
 		{
-			LONG x,y;
-			LV_ITEM lvi;
-			int iItem;
-
-			iItem = ListView_GetNextItem(hWnd, -1, LVNI_FOCUSED);
-
-			if(iItem > -1)
-			{
-				lvi.iItem = iItem;
-				lvi.mask = LVIF_PARAM;
-
-				ListView_GetItem(hWnd, &lvi);
-			}
-			else
-				lvi.lParam = (LPARAM)NULL;
-
-			dw_pointer_query_pos(&x, &y);
-
+			SignalHandler *tmp = Root;
+
+			while(tmp)
 			{
-				SignalHandler *tmp = Root;
-
-				while(tmp)
+				if(tmp->message == NM_RCLICK && tmp->window == hWnd)
 				{
-					if(tmp->message == NM_RCLICK && tmp->window == hWnd)
+					int (*containercontextfunc)(HWND, char *, int, int, void *) = tmp->signalfunction;
+					LONG x,y;
+					LV_ITEM lvi;
+					int iItem;
+					LVHITTESTINFO lhi;
+
+					dw_pointer_query_pos(&x, &y);
+
+					lhi.pt.x = x;
+					lhi.pt.y = y;
+
+					MapWindowPoints(HWND_DESKTOP, tmp->window, &lhi.pt, 1);
+
+					iItem = ListView_HitTest(tmp->window, &lhi);
+
+					if(iItem > -1)
 					{
-						int (*containercontextfunc)(HWND, char *, int, int, void *) = tmp->signalfunction;
-
-						/* Seems to be having lParam as 1 which really sucks */
-						if(lvi.lParam < 100)
-							lvi.lParam = 0;
-
-						containercontextfunc(tmp->window, (char *)lvi.lParam, x, y, tmp->data);
-						tmp = NULL;
+						lvi.iItem = iItem;
+						lvi.mask = LVIF_PARAM;
+
+						ListView_GetItem(tmp->window, &lvi);
+						ListView_SetSelectionMark(tmp->window, iItem);
 					}
-					if(tmp)
-						tmp = tmp->next;
+					else
+						lvi.lParam = (LPARAM)NULL;
+
+					/* Seems to be having lParam as 1 which really sucks */
+					if(lvi.lParam < 100)
+						lvi.lParam = 0;
+
+					containercontextfunc(tmp->window, (char *)lvi.lParam, x, y, tmp->data);
+					tmp = NULL;
 				}
+				if(tmp)
+					tmp = tmp->next;
 			}
 		}
 		break;
@@ -2100,12 +2139,20 @@
 	static int bMouseOver = 0;
 	POINT point;
 	RECT rect;
+	WNDPROC pOldProc;
 
 	bubble = (BubbleButton *)GetWindowLong(hwnd, GWL_USERDATA);
 
 	if(!bubble)
 		return DefWindowProc(hwnd, msg, mp1, mp2);
 
+	/* We must save a pointer to the old
+	 * window procedure because if a signal
+	 * handler attached here destroys this
+	 * window it will then be invalid.
+	 */
+	pOldProc = bubble->pOldProc;
+
 	switch(msg)
 	{
 #ifndef NO_SIGNALS
@@ -2286,9 +2333,9 @@
 		break;
 	}
 
-	if(!bubble->pOldProc)
+	if(!pOldProc)
 		return DefWindowProc(hwnd, msg, mp1, mp2);
-	return CallWindowProc(bubble->pOldProc, hwnd, msg, mp1, mp2);
+	return CallWindowProc(pOldProc, hwnd, msg, mp1, mp2);
 }
 
 /* This function recalculates a notebook page for example
@@ -2650,9 +2697,62 @@
  */
 int dw_window_destroy(HWND handle)
 {
+	HWND parent = GetParent(handle);
+	Box *thisbox = (Box *)GetWindowLong(parent, GWL_USERDATA);
+
+	if(parent != HWND_DESKTOP && thisbox && thisbox->count)
+	{
+		int z, index = -1;
+		Item *tmpitem, *thisitem = thisbox->items;
+
+		for(z=0;z<thisbox->count;z++)
+		{
+			if(thisitem[z].hwnd == handle)
+				index = z;
+		}
+
+		if(index == -1)
+			return 0;
+
+		tmpitem = malloc(sizeof(Item)*(thisbox->count-1));
+
+		/* Copy all but the current entry to the new list */
+		for(z=0;z<index;z++)
+		{
+			tmpitem[z] = thisitem[z];
+		}
+		for(z=index+1;z<thisbox->count;z++)
+		{
+			tmpitem[z-1] = thisitem[z];
+		}
+
+		thisbox->items = tmpitem;
+		free(thisitem);
+		thisbox->count--;
+	}
 	return DestroyWindow(handle);
 }
 
+/* Causes entire window to be invalidated and redrawn.
+ * Parameters:
+ *           handle: Toplevel window handle to be redrawn.
+ */
+void dw_window_redraw(HWND handle)
+{
+	Box *mybox = (Box *)GetWindowLong(handle, GWL_USERDATA);
+
+	if(mybox)
+	{
+		RECT rect;
+
+		GetClientRect(handle, &rect);
+
+		ShowWindow(mybox->items[0].hwnd, SW_HIDE);
+		_do_resize(mybox, rect.right - rect.left, rect.bottom - rect.top);
+		ShowWindow(mybox->items[0].hwnd, SW_SHOW);
+	}
+}
+
 /*
  * Changes a window's parent to newparent.
  * Parameters:
@@ -3402,6 +3502,7 @@
 							NULL,
 							NULL);
 	ColorInfo *cinfo = (ColorInfo *)calloc(1, sizeof(ColorInfo));
+	ColorInfo *cinfo2 = (ColorInfo *)calloc(1, sizeof(ColorInfo));
 
 	if(!cinfo)
 	{
@@ -3409,10 +3510,10 @@
 		return NULL;
 	}
 
-	cinfo->fore = -1;
-	cinfo->back = -1;
-	cinfo->combo = tmp;
-	EnumChildWindows(tmp, _subclass_child, (LPARAM)cinfo);
+	cinfo2->fore = cinfo->fore = -1;
+	cinfo2->back = cinfo->back = -1;
+	cinfo2->combo = cinfo->combo = tmp;
+	EnumChildWindows(tmp, _subclass_child, (LPARAM)cinfo2);
 
 	SetWindowLong(tmp, GWL_USERDATA, (ULONG)cinfo);
 	dw_window_set_font(tmp, DefaultFont);
@@ -4700,16 +4801,21 @@
  *          title: The text title of the entry.
  *          icon: Handle to coresponding icon.
  *          parent: Parent handle or 0 if root.
- */
-HWND dw_tree_insert(HWND handle, char *title, unsigned long icon, HWND parent)
+ *          itemdata: Item specific data.
+ */
+HWND dw_tree_insert(HWND handle, char *title, unsigned long icon, HWND parent, void *itemdata)
 {
 	TVITEM tvi;
 	TVINSERTSTRUCT tvins;
 	HTREEITEM hti;
+	void **ptrs= malloc(sizeof(void *) * 2);
+
+	ptrs[0] = title;
+	ptrs[1] = itemdata;
 
 	tvi.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM;
 	tvi.pszText = title;
-	tvi.lParam = (LONG)title;
+	tvi.lParam = (LONG)ptrs;
 	tvi.cchTextMax = strlen(title);
 	tvi.iSelectedImage = tvi.iImage = _lookup_icon(handle, (HICON)icon, 1);
 
@@ -4723,6 +4829,69 @@
 }
 
 /*
+ * Sets the text and icon of an item in a tree window (widget).
+ * Parameters:
+ *          handle: Handle to the tree containing the item.
+ *          item: Handle of the item to be modified.
+ *          title: The text title of the entry.
+ *          icon: Handle to coresponding icon.
+ */
+void dw_tree_set(HWND handle, HWND item, char *title, unsigned long icon)
+{
+	TVITEM tvi;
+	void **ptrs;
+
+	tvi.mask = TVIF_HANDLE;
+	tvi.hItem = (HTREEITEM)item;
+
+	TreeView_GetItem(handle, &tvi);
+
+	ptrs = (void **)tvi.lParam;
+	ptrs[0] = title;
+
+	tvi.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
+	tvi.pszText = title;
+	tvi.cchTextMax = strlen(title);
+	tvi.iSelectedImage = tvi.iImage = _lookup_icon(handle, (HICON)icon, 1);
+	tvi.hItem = (HTREEITEM)item;
+
+	TreeView_SetItem(handle, &tvi);
+}
+
+/*
+ * Sets the item data of a tree item.
+ * Parameters:
+ *          handle: Handle to the tree containing the item.
+ *          item: Handle of the item to be modified.
+ *          itemdata: User defined data to be associated with item.
+ */
+void dw_tree_set_data(HWND handle, HWND item, void *itemdata)
+{
+	TVITEM tvi;
+	void **ptrs;
+
+	tvi.mask = TVIF_HANDLE;
+	tvi.hItem = (HTREEITEM)item;
+
+	TreeView_GetItem(handle, &tvi);
+
+	ptrs = (void **)tvi.lParam;
+	ptrs[1] = itemdata;
+}
+
+/*
+ * Sets this item as the active selection.
+ * Parameters:
+ *       handle: Handle to the tree window (widget) to be selected.
+ *       item: Handle to the item to be selected.
+ */
+void dw_tree_item_select(HWND handle, HWND item)
+{
+	TreeView_SelectItem(handle, (HTREEITEM)item);
+	SetFocus(handle);
+}
+
+/*
  * Removes all nodes from a tree.
  * Parameters:
  *       handle: Handle to the window (widget) to be cleared.
@@ -4733,6 +4902,28 @@
 }
 
 /*
+ * Expands a node on a tree.
+ * Parameters:
+ *       handle: Handle to the tree window (widget).
+ *       item: Handle to node to be expanded.
+ */
+void dw_tree_expand(HWND handle, HWND item)
+{
+	TreeView_Expand(handle, (HTREEITEM)item, TVE_EXPAND);
+}
+
+/*
+ * Collapses a node on a tree.
+ * Parameters:
+ *       handle: Handle to the tree window (widget).
+ *       item: Handle to node to be collapsed.
+ */
+void dw_tree_collapse(HWND handle, HWND item)
+{
+	TreeView_Expand(handle, (HTREEITEM)item, TVE_COLLAPSE);
+}
+
+/*
  * Removes a node from a tree.
  * Parameters:
  *       handle: Handle to the window (widget) to be cleared.