Implements +/-, PgUp/PgDown, Home/End, Left/Right, Up/Down.

diff --git a/dlls/comctl32/treeview.c b/dlls/comctl32/treeview.c
index 72a5245..3ae8227 100644
--- a/dlls/comctl32/treeview.c
+++ b/dlls/comctl32/treeview.c
@@ -97,10 +97,12 @@
    FIXME: check other failures.
  */
 
-
-
-static TREEVIEW_ITEM *
-TREEVIEW_ValidItem (TREEVIEW_INFO *infoPtr,HTREEITEM  handle)
+/***************************************************************************
+ * This method returns the TREEVIEW_ITEM object given the handle
+ */
+static TREEVIEW_ITEM* TREEVIEW_ValidItem(
+  TREEVIEW_INFO *infoPtr,
+  HTREEITEM  handle)
 {
  
  if ((!handle) || (handle>infoPtr->uMaxHandle)) return NULL;
@@ -109,75 +111,159 @@
  return & infoPtr->items[(INT)handle];
 }
 
-
-
-static TREEVIEW_ITEM *TREEVIEW_GetPrevListItem (TREEVIEW_INFO *infoPtr, 
-					TREEVIEW_ITEM *tvItem)
+/***************************************************************************
+ * This method returns the last expanded child item of a tree node
+ */
+static TREEVIEW_ITEM *TREEVIEW_GetLastListItem(
+  TREEVIEW_INFO *infoPtr,
+  TREEVIEW_ITEM *tvItem)
 
 {
- TREEVIEW_ITEM *wineItem;
+  TREEVIEW_ITEM *wineItem = tvItem;
 
- if (tvItem->upsibling) {
-		wineItem=& infoPtr->items[(INT)tvItem->upsibling];
-		if ((wineItem->firstChild) && (wineItem->state & TVIS_EXPANDED)) {
-			wineItem=& infoPtr->items[(INT)wineItem->firstChild];
-			while (wineItem->sibling)
-				 wineItem= & infoPtr->items[(INT)wineItem->sibling];
-		}
-		return wineItem;
- }
+  /* 
+   * Get this item last sibling 
+   */
+  while (wineItem->sibling) 
+ 	  wineItem=& infoPtr->items [(INT)wineItem->sibling];
 
- wineItem=tvItem;
- while (wineItem->parent) {
-	wineItem=& infoPtr->items[(INT)wineItem->parent];
-	if (wineItem->upsibling) 
-                return (& infoPtr->items[(INT)wineItem->upsibling]);
- } 
+  /* 
+   * If the last sibling has expanded children, restart.
+   */
+  if ( ( wineItem->cChildren > 0 ) && ( wineItem->state & TVIS_EXPANDED) )
+    return TREEVIEW_GetLastListItem(
+             infoPtr, 
+             &(infoPtr->items[(INT)wineItem->firstChild]));
 
- return wineItem;
+  return wineItem;
+}
+
+/***************************************************************************
+ * This method returns the previous physical item in the list not 
+ * considering the tree hierarchy.
+ */
+static TREEVIEW_ITEM *TREEVIEW_GetPrevListItem(
+  TREEVIEW_INFO *infoPtr, 
+  TREEVIEW_ITEM *tvItem)
+{
+  if (tvItem->upsibling) 
+  {
+    /* 
+     * This item has a upsibling, get the last item.  Since, GetLastListItem
+     * first looks at siblings, we must feed it with the first child.
+     */
+    TREEVIEW_ITEM *upItem = &infoPtr->items[(INT)tvItem->upsibling];
+    
+    if ( ( upItem->cChildren > 0 ) && ( upItem->state & TVIS_EXPANDED) )
+      return TREEVIEW_GetLastListItem( 
+               infoPtr, 
+               &infoPtr->items[(INT)upItem->firstChild]);
+    else
+      return upItem;
+  }
+  else
+  {
+    /*
+     * this item does not have a upsibling, get the parent
+     */
+    if (tvItem->parent) 
+      return &infoPtr->items[(INT)tvItem->parent];
+  }
+
+  return NULL;
 }
 
 
-static TREEVIEW_ITEM *TREEVIEW_GetNextListItem (TREEVIEW_INFO *infoPtr, 
-					TREEVIEW_ITEM *tvItem)
-
+/***************************************************************************
+ * This method returns the next physical item in the treeview not 
+ * considering the tree hierarchy.
+ */
+static TREEVIEW_ITEM *TREEVIEW_GetNextListItem(
+  TREEVIEW_INFO *infoPtr, 
+  TREEVIEW_ITEM *tvItem)
 {
- TREEVIEW_ITEM *wineItem;
+  TREEVIEW_ITEM *wineItem = NULL;
 
+  /* 
+   * If this item has children and is expanded, return the first child
+   */
   if ((tvItem->firstChild) && (tvItem->state & TVIS_EXPANDED)) 
 		return (& infoPtr->items[(INT)tvItem->firstChild]);
 
-
- if (tvItem->sibling) 
+  /*
+   * try to get the sibling
+   */
+  if (tvItem->sibling) 
 		return (& infoPtr->items[(INT)tvItem->sibling]);
 
- wineItem=tvItem;
- while (wineItem->parent) {
-	wineItem=& infoPtr->items [(INT)wineItem->parent];
-	if (wineItem->sibling) 
-                return (& infoPtr->items [(INT)wineItem->sibling]);
- } 
+  /*
+   * Otherwise, get the parent's sibling.
+   */
+  wineItem=tvItem;
+  while (wineItem->parent) {
+    wineItem=& infoPtr->items [(INT)wineItem->parent];
+  	if (wineItem->sibling) 
+      return (& infoPtr->items [(INT)wineItem->sibling]);
+  } 
 
- return NULL;  /* was wineItem */
+  return NULL;  
 }
 
-static TREEVIEW_ITEM *TREEVIEW_GetLastListItem (TREEVIEW_INFO *infoPtr,
-					TREEVIEW_ITEM *tvItem)
-
+/***************************************************************************
+ * This method returns the nth item starting at the given item.  It returns 
+ * the last item (or first) we we run out of items.
+ *
+ * Will scroll backward if count is <0.
+ *             forward if count is >0.
+ */
+static TREEVIEW_ITEM *TREEVIEW_GetListItem(
+  TREEVIEW_INFO *infoPtr, 
+  TREEVIEW_ITEM *tvItem,
+  LONG          count)
 {
- TREEVIEW_ITEM *wineItem;
+  TREEVIEW_ITEM *previousItem = NULL;
+  TREEVIEW_ITEM *wineItem     = tvItem;
+  LONG          iter          = 0;
 
- wineItem=tvItem;
- while (wineItem->sibling) 
-	wineItem=& infoPtr->items [(INT)wineItem->sibling];
+  if      (count > 0)
+  {
+    /* Find count item downward */
+    while ((iter++ < count) && (wineItem != NULL))
+    {
+      /* Keep a pointer to the previous in case we ask for more than we got */
+      previousItem = wineItem; 
+      wineItem     = TREEVIEW_GetNextListItem(infoPtr, wineItem);
+    } 
 
- return wineItem;
+    if (wineItem == NULL)
+      wineItem = previousItem;
+  }
+  else if (count < 0)
+  {
+    /* Find count item upward */
+    while ((iter-- > count) && (wineItem != NULL))
+    {
+      /* Keep a pointer to the previous in case we ask for more than we got */
+      previousItem = wineItem; 
+      wineItem = TREEVIEW_GetPrevListItem(infoPtr, wineItem);
+    }
+
+    if (wineItem == NULL)
+      wineItem = previousItem;
+  }
+  else
+    wineItem = NULL;
+
+  return wineItem;
 }
-	
- 
-static void TREEVIEW_RemoveAllChildren (HWND hwnd,
-					   TREEVIEW_ITEM *parentItem)
 
+ 
+/***************************************************************************
+ * This method 
+ */
+static void TREEVIEW_RemoveAllChildren(
+  HWND hwnd,
+  TREEVIEW_ITEM *parentItem)
 {
  TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
  TREEVIEW_ITEM *killItem;
@@ -722,8 +808,6 @@
   return TRUE;
 }
 
-
-
 static LRESULT
 TREEVIEW_GetVisibleCount (HWND hwnd,  WPARAM wParam, LPARAM lParam)
 
@@ -1481,25 +1565,20 @@
 
 {
   TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-  RECT parent_rect;
-  UINT  cx,cy;
-  HWND parent;
 
-  if (infoPtr->bAutoSize) {
+  if (infoPtr->bAutoSize) 
+  {
     infoPtr->bAutoSize = FALSE;
     return 0;
-    }
-    infoPtr->bAutoSize = TRUE;
+  }
+  infoPtr->bAutoSize = TRUE;
 
-    if (!wParam)  {
-    parent = GetParent (hwnd);
-    GetClientRect(parent, &parent_rect);
-    cx=LOWORD (lParam);
-  	cy=HIWORD (lParam);
-	SetWindowPos (hwnd, 0, parent_rect.left, parent_rect.top, 
-			cx, cy, SWP_NOZORDER);
-   } else {
-	FIXME (treeview,"WM_SIZE flag %x %lx not handled\n", wParam, lParam);
+  if (wParam == SIZE_RESTORED)  
+  {
+    infoPtr->uTotalWidth  = LOWORD (lParam);
+  	infoPtr->uTotalHeight = HIWORD (lParam);
+  } else {
+  	FIXME (treeview,"WM_SIZE flag %x %lx not handled\n", wParam, lParam);
   }
 
   TREEVIEW_QueueRefresh (hwnd);
@@ -1931,7 +2010,6 @@
    know if it also applies here.
 */
 
-
 static LRESULT
 TREEVIEW_Expand (HWND hwnd, WPARAM wParam, LPARAM lParam)
 {
@@ -1955,7 +2033,7 @@
     return 0;
   }
 
-  if (flag & TVE_TOGGLE) {    /* FIXME: check exact behaviour here */
+  if (flag == TVE_TOGGLE) {    /* FIXME: check exact behaviour here */
    flag &= ~TVE_TOGGLE;    /* ie: bitwise ops or 'case' ops */
    if (wineItem->state & TVIS_EXPANDED) 
      flag |= TVE_COLLAPSE;
@@ -1977,7 +2055,7 @@
       if (!wineItem->state & TVIS_EXPANDED) 
         return 0;
 
-      wineItem->state &= ~TVIS_EXPANDED;
+      wineItem->state &= ~(TVIS_EXPANDEDONCE | TVIS_EXPANDED);
       break;
 
     case TVE_EXPAND: 
@@ -2018,8 +2096,6 @@
 
 
 
-
-
 static TREEVIEW_ITEM *
 TREEVIEW_HitTestPoint (HWND hwnd, POINT pt)
 {
@@ -2426,65 +2502,6 @@
 }
 
 
-/* FIXME: does KEYDOWN also send notifications?? If so, use 
-   TREEVIEW_DoSelectItem.
-*/
-
-
-static LRESULT
-TREEVIEW_KeyDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- TREEVIEW_ITEM *prevItem,*newItem;
- int prevSelect;
-
-
- TRACE (treeview,"%x %lx\n",wParam, lParam);
- prevSelect=(INT)infoPtr->selectedItem;
- if (!prevSelect) return FALSE;
-
- prevItem= TREEVIEW_ValidItem (infoPtr, (HTREEITEM)prevSelect);
- 
- newItem=NULL;
- switch (wParam) {
-	case VK_UP: 
-		newItem=TREEVIEW_GetPrevListItem (infoPtr, prevItem);
-		if (!newItem) 
-			newItem=& infoPtr->items[(INT)infoPtr->TopRootItem];
-		break;
-	case VK_DOWN: 
-		newItem=TREEVIEW_GetNextListItem (infoPtr, prevItem);
-		if (!newItem) newItem=prevItem;
-		break;
-	case VK_HOME:
-		newItem=& infoPtr->items[(INT)infoPtr->TopRootItem];
-		break;
-	case VK_END:
-		newItem=& infoPtr->items[(INT)infoPtr->TopRootItem];
-		newItem=TREEVIEW_GetLastListItem (infoPtr, newItem);
-		break;
-	case VK_PRIOR:
-	case VK_NEXT:
-	case VK_BACK:
-	case VK_RETURN:
-		FIXME (treeview, "%x not implemented\n", wParam);
-		break;
- }
-
- if (!newItem) return FALSE;
-
- if (prevItem!=newItem) {
- 	prevItem->state &= ~TVIS_SELECTED;
- 	newItem->state |= TVIS_SELECTED;
- 	infoPtr->selectedItem=newItem->hItem;
- 	TREEVIEW_QueueRefresh (hwnd);
- 	return TRUE;
- }
-
- return FALSE;
-}
-
-
 
 static LRESULT
 TREEVIEW_VScroll (HWND hwnd, WPARAM wParam, LPARAM lParam)
@@ -2577,6 +2594,149 @@
 }
 
 
+/* FIXME: does KEYDOWN also send notifications?? If so, use 
+   TREEVIEW_DoSelectItem.
+*/
+static LRESULT
+TREEVIEW_KeyDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
+{
+ TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
+ TREEVIEW_ITEM *prevItem,*newItem;
+ int prevSelect;
+
+
+ TRACE (treeview,"%x %lx\n",wParam, lParam);
+ prevSelect=(INT)infoPtr->selectedItem;
+ if (!prevSelect) return FALSE;
+
+ prevItem= TREEVIEW_ValidItem (infoPtr, (HTREEITEM)prevSelect);
+ 
+ newItem=NULL;
+ switch (wParam) {
+	case VK_UP: 
+		newItem=TREEVIEW_GetPrevListItem (infoPtr, prevItem);
+
+		if (!newItem) 
+			newItem=& infoPtr->items[(INT)infoPtr->TopRootItem];
+
+    if (! newItem->visible)
+      TREEVIEW_VScroll(hwnd, SB_LINEUP, 0);
+
+		break;
+
+	case VK_DOWN: 
+		newItem=TREEVIEW_GetNextListItem (infoPtr, prevItem);
+
+		if (!newItem) 
+      newItem=prevItem;
+
+    if (! newItem->visible)
+      TREEVIEW_VScroll(hwnd, SB_LINEDOWN, 0);
+		break;
+
+	case VK_HOME:
+		newItem=& infoPtr->items[(INT)infoPtr->TopRootItem];
+    infoPtr->cy = 0;
+		break;
+
+	case VK_END:
+		newItem=& infoPtr->items[(INT)infoPtr->TopRootItem];
+		newItem=TREEVIEW_GetLastListItem (infoPtr, newItem);
+
+    if (! newItem->visible)
+      infoPtr->cy = infoPtr->uTotalHeight-infoPtr->uVisibleHeight; 
+
+		break;
+
+	case VK_LEFT:
+    if ( (prevItem->cChildren > 0) && (prevItem->state & TVIS_EXPANDED) )
+    {
+      TREEVIEW_Expand(hwnd, TVE_COLLAPSE, prevSelect );
+    }
+    else if ((INT)prevItem->parent) 
+    {
+      newItem = (& infoPtr->items[(INT)prevItem->parent]);
+      if (! newItem->visible) 
+        /* FIXME find a way to make this item the first visible... */
+        newItem = NULL; 
+    }
+
+    break;
+
+	case VK_RIGHT:
+    if ( ( prevItem->cChildren > 0)  || 
+         ( prevItem->cChildren == I_CHILDRENCALLBACK))
+    {
+      if (! (prevItem->state & TVIS_EXPANDED))
+        TREEVIEW_Expand(hwnd, TVE_EXPAND, prevSelect );
+      else
+        newItem = (& infoPtr->items[(INT)prevItem->firstChild]);
+    }
+    break;
+
+  case VK_ADD:
+    if (! (prevItem->state & TVIS_EXPANDED))
+      TREEVIEW_Expand(hwnd, TVE_EXPAND, prevSelect );
+    break;
+
+  case VK_SUBTRACT:
+    if (prevItem->state & TVIS_EXPANDED)
+      TREEVIEW_Expand(hwnd, TVE_COLLAPSE, prevSelect );
+    break;
+
+  case VK_PRIOR:
+    
+		newItem=TREEVIEW_GetListItem(
+              infoPtr, 
+              prevItem,
+              -1*(TREEVIEW_GetVisibleCount(hwnd,0,0)-3));
+		if (!newItem) 
+      newItem=prevItem;
+  
+    if (! newItem->visible)
+      TREEVIEW_VScroll(hwnd, SB_PAGEUP, 0);
+
+		break;
+
+  case VK_NEXT:
+		newItem=TREEVIEW_GetListItem(
+              infoPtr, 
+              prevItem,
+              TREEVIEW_GetVisibleCount(hwnd,0,0)-3);
+
+		if (!newItem) 
+      newItem=prevItem;
+
+    if (! newItem->visible)
+      TREEVIEW_VScroll(hwnd, SB_PAGEDOWN, 0);
+
+		break;
+
+	case VK_BACK:
+
+	case VK_RETURN:
+
+  default:
+		FIXME (treeview, "%x not implemented\n", wParam);
+		break;
+ }
+
+ if (!newItem) return FALSE;
+
+ if (prevItem!=newItem) {
+ 	prevItem->state &= ~TVIS_SELECTED;
+ 	newItem->state |= TVIS_SELECTED;
+ 	infoPtr->selectedItem=newItem->hItem;
+ 	TREEVIEW_QueueRefresh (hwnd);
+ 	return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+
+
 
 
 LRESULT WINAPI