/* * This file is part of MiniGUI, a mature cross-platform windowing * and Graphics User Interface (GUI) support system for embedded systems * and smart IoT devices. * * Copyright (C) 2002~2018, Beijing FMSoft Technologies Co., Ltd. * Copyright (C) 1998~2002, WEI Yongming * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Or, * * As this program is a library, any link to this program must follow * GNU General Public License version 3 (GPLv3). If you cannot accept * GPLv3, you need to be licensed from FMSoft. * * If you have got a commercial license of this program, please use it * under the terms and conditions of the commercial license. * * For more information about the commercial license, please refer to * . */ /* ** treeview.c: TreeView Control. */ #include #include "common.h" #ifdef _MGCTRL_TREEVIEW #include "minigui.h" #include "gdi.h" #include "window.h" #include "control.h" #include "cliprect.h" #include "internals.h" #include "ctrlclass.h" #include "ctrl/treeview.h" #include "treeview_impl.h" #define _USE_FIXSTR 1 #define TV_BORDER 2 #define TV_BOXHALFHEIGHT 4 #define TV_BOXGAP 3 #define TV_ICONGAP 3 #define TV_IDENT 5 static HICON icon_fold, icon_unfold; #define ICON_FOLD icon_fold #define ICON_UNFOLD icon_unfold #define SYSICON_FOLD "treefold" #define SYSICON_UNFOLD "treeunfold" #define BKC_HILIGHT_NORMAL WE_BGC_HIGHLIGHT_ITEM #define FGC_HILIGHT_NORMAL WE_FGC_HIGHLIGHT_ITEM #define FGC_CONTROL_NORMAL WE_FGC_WINDOW static LRESULT TreeViewCtrlProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); BOOL RegisterTreeViewControl (void) { WNDCLASS WndClass; char szValue[MAX_NAME + 1]; const WINDOW_ELEMENT_RENDERER * rdr = GetDefaultWindowElementRenderer(); if (!(icon_fold = (HICON)RetrieveRes(gui_GetIconFile(rdr->name, SYSICON_FOLD, szValue)))) { if (!(icon_fold = LoadSystemIcon (SYSICON_FOLD, 0))) { return FALSE; } } if (!(icon_unfold = (HICON)RetrieveRes(gui_GetIconFile(rdr->name, SYSICON_UNFOLD, szValue)))) { if (!(icon_unfold = LoadSystemIcon (SYSICON_UNFOLD, 0))) { DestroyIcon (icon_fold); icon_fold = 0; return FALSE; } } WndClass.spClassName = CTRL_TREEVIEW; WndClass.dwStyle = WS_NONE; WndClass.dwExStyle = WS_EX_NONE; WndClass.hCursor = GetSystemCursor (0); WndClass.iBkColor = GetWindowElementPixel (HWND_NULL, WE_BGC_WINDOW); WndClass.WinProc = TreeViewCtrlProc; return AddNewControlClass (&WndClass) == ERR_OK; } void TreeViewControlCleanup (void) { UnregisterWindowClass (CTRL_TREEVIEW); if (icon_fold) DestroyIcon (icon_fold); if (icon_unfold) DestroyIcon (icon_unfold); } /* * Initialize Treeview. */ static BOOL tvInitTVData (HWND hwnd, PTVDATA pData, DWORD dwStyle, PTVITEMINFO root_ii) { PTVITEM tvRoot; HDC hdc; char* text; if (root_ii) { if (root_ii->text == NULL || root_ii->text[0] == '\0') text = " "; else text = root_ii->text; } else { text = "Root"; } pData->dwStyle = dwStyle; pData->id = GetDlgCtrlID (hwnd); pData->str_cmp = strncmp; if (!(tvRoot = calloc (1, sizeof (TVITEM)))) return FALSE; #if _USE_FIXSTR tvRoot->text = FixStrAlloc (strlen(text)); if (tvRoot->text == NULL) { free (tvRoot); return FALSE; } strcpy (tvRoot->text, text); #else tvRoot->text = strdup (text); #endif tvRoot->dwFlags = TVIF_ROOT | TVIF_SELECTED; if (root_ii && root_ii->dwFlags & TVIF_FOLD) { tvRoot->dwFlags |= TVIF_FOLD; } tvRoot->depth = 0; tvRoot->child = tvRoot->next = tvRoot->parent = NULL; if (pData->dwStyle & TVS_WITHICON) { if (root_ii && root_ii->hIconFold) tvRoot->hIconFold = root_ii->hIconFold; else tvRoot->hIconFold = ICON_FOLD; if (root_ii && root_ii->hIconUnfold) tvRoot->hIconUnfold = root_ii->hIconUnfold; else tvRoot->hIconUnfold = ICON_UNFOLD; } hdc = GetClientDC (hwnd); pData->nItemHeight = GetFontHeight (hdc); pData->nVisItemCount = 1; pData->nItemCount = 1; pData->root = tvRoot; pData->pItemSelected = tvRoot; pData->nItemTop = 0; pData->nLeft = 0; GetTextExtent (hdc, tvRoot->text, -1, &tvRoot->text_ext); pData->nWidth = TV_BOXGAP + pData->nItemHeight + tvRoot->text_ext.cx; if (pData->dwStyle & TVS_WITHICON) pData->nWidth += pData->nItemHeight + TV_ICONGAP; if (pData->dwStyle & WS_BORDER) pData->nWidth += TV_BORDER * 2; ReleaseDC (hdc); EnableScrollBar (hwnd, SB_HORZ, FALSE); EnableScrollBar (hwnd, SB_VERT, FALSE); return TRUE; } /* * Get the parent node of the given node. */ static PTVITEM getParent (PTVDATA pData, PTVITEM pChild) { if (pChild == pData->root || pChild == NULL) return NULL; return pChild->parent; } /* * Get the node whose next sibling is current node. */ static PTVITEM getPrev (PTVDATA pData, PTVITEM pCur) { PTVITEM p, t; p = getParent (pData, pCur); if (p == NULL || p->child == pCur) return NULL; t = p->child; while (t->next != NULL && t->next != pCur) t = t->next; if (t->next != pCur) return NULL; return t; } #if 0 static void UnfoldAll (PTVITEM p) { PTVITEM t; if (p == NULL) return; p->dwFlags &= ~TVIF_FOLD; t = p->child; while (t) { UnfoldAll (t); t = t->next; } } #endif /* * Mark all ancestor items unfolded */ static void unfold_ancestor (HWND hwnd, PTVDATA pData, PTVITEM item) { PTVITEM parent; parent = item->parent; while (parent) { parent->dwFlags &= ~TVIF_FOLD; NotifyParentEx (hwnd, pData->id, TVN_UNFOLDED, (DWORD)parent); parent = parent->parent; } } static void vis_item_count (PTVITEM p, int* count) { PTVITEM t; (*count)++; if (!(p->dwFlags & TVIF_FOLD)) { t = p->child; while (t) { vis_item_count (t, count); t = t->next; } } } static int getVisItemCount (PTVDATA pData) { int count = 0; if (pData->root == NULL) return 0; vis_item_count (pData->root, &count); return count; } static void TVWidth (PTVDATA pData, PTVITEM p, int* width) { PTVITEM t; int wtemp, h; h = pData->nItemHeight; wtemp = TV_BOXGAP + h + TV_BOXGAP + p->depth * h + p->text_ext.cx; if (pData->dwStyle & TVS_WITHICON) wtemp += h + TV_ICONGAP; if (pData->dwStyle & WS_BORDER) wtemp += TV_BORDER * 2; *width = MAX (wtemp, *width); if (!(p->dwFlags & TVIF_FOLD)) { t = p->child; while (t) { TVWidth (pData, t, width); t = t->next; } } } static int getTVWidth (PTVDATA pData) { int width = 0; TVWidth (pData, pData->root, &width); return width; } /* * Used by RemoveTree(), recursive function */ static void RemoveSubTree (PTVDATA pData, PTVITEM p) { PTVITEM q, n; if (p == NULL ) return; q = p->child; while (q != NULL) { n = q->next; RemoveSubTree (pData, q); q = n; } /* free a node which hasn't any child */ #if _USE_FIXSTR FreeFixStr (p->text); #else free (p->text); #endif free (p); p = NULL; pData->nItemCount--; } /* * Remove a tree or subtree */ static void RemoveTree (HWND hwnd, PTVDATA pData, PTVITEM pChild) { PTVITEM p, q; if (pChild == NULL) return; if (pChild == pData->root) { pData->root = NULL; pData->pItemSelected = NULL; pData->nVisItemCount = 1; pData->nWidth = 1; pData->nItemTop = pData->nLeft = 0; RemoveSubTree (pData, pChild); } else { p = pData->pItemSelected = getParent (pData, pChild); if (pChild == p->child) p->child = pChild->next; else { q = p->child; while (q !=NULL && q->next != pChild) q = q->next; q->next = pChild->next; } RemoveSubTree (pData, pChild); pData->nWidth = getTVWidth (pData); pData->nVisItemCount = getVisItemCount (pData); } } static void tvCleanTVData (HWND hwnd, PTVDATA pData) { RemoveTree (hwnd, pData, pData->root); free (pData); } static int is_descendant (PTVITEM root, PTVITEM item, int* depth) { int i; int d; if (root == NULL || item == NULL) return -1; d = item->depth - root->depth; for (i = 0; i < d; i++) { item = item->parent; } if (depth) *depth = d; if (item == root) return 0; return -1; } static PTVITEM findStringDepthFirst (PTVDATA pData, PTVITEM p, const char* string) { PTVITEM q, t = NULL; if (!pData->str_cmp (p->text, string, (size_t)-1)) return p; else { q = p->child; while (q && !(t = findStringDepthFirst (pData, q, string))) q = q->next; } return t; } static PTVITEM findStringInChildren (PTVDATA pData, PTVITEM item, const char* string) { PTVITEM p; p = item->child; while (p) { if (!pData->str_cmp (p->text, string, (size_t)-1)) return p; p = p->next; } return NULL; } static void countPrevNext (PTVDATA pData, PTVITEM prev, int* count) { PTVITEM t; (*count)++; if (!(prev->dwFlags & TVIF_FOLD)) { t = prev->child; while (t) { countPrevNext (pData, t, count); t = t->next; } } } static int getItemsWithPrev (PTVDATA pData, PTVITEM pCur) { int count = 0; PTVITEM prev = getPrev (pData, pCur); if (prev == NULL) return 0; countPrevNext (pData, prev, &count); return count - 1; } static int getItemsWithNext (PTVDATA pData, PTVITEM pCur) { int count = 0; if (pCur == NULL || pCur->next == NULL) return 0; countPrevNext (pData, pCur, &count); return count - 1; } static PTVITEM ItemFromCount (PTVITEM p, int count, int* cnt) { PTVITEM q, t = NULL; (*cnt)++; if (*cnt == count) return p; else if (p->child != NULL && !(p->dwFlags & TVIF_FOLD)) { q = p->child; while (q != NULL && !(t = ItemFromCount (q, count, cnt))) q = q->next; } else return NULL; return t; } static PTVITEM getItemFromCount (PTVDATA pData, int count) { int cnt = 0; if (pData->root == NULL) return NULL; return ItemFromCount (pData->root, count, &cnt); } static int CountFromItem (PTVITEM p, PTVITEM pTgt, int* count) { int t = 0; PTVITEM q; (*count)++; if (p == pTgt) return *count; else if (p->child != NULL && !(p->dwFlags & TVIF_FOLD)) { q = p->child; while (q != NULL && !(t = CountFromItem (q, pTgt, count))) q = q->next; } else return 0; return t; } static int getCountFromItem (PTVDATA pData, PTVITEM pTgt) { int count = 0; if (pTgt == NULL) return 0; return CountFromItem (pData->root, pTgt, &count); } /* * Add a node */ static void InsertChild (HWND hwnd, PTVDATA pData, PTVITEM pParent, PTVITEM pChild) { PTVITEM p, t = NULL; int h, w; HDC hdc; pChild->depth = pParent->depth + 1; pChild->parent = pParent; pChild->child = pChild->next = NULL; if (pParent->child == NULL) pParent->child = pChild; else if (pData->dwStyle & TVS_SORT) { p = pParent->child; while (pData->str_cmp (pChild->text, p->text, (size_t)-1) > 0 && p->next != NULL) { t = p; p = p->next; } if (pData->str_cmp (pChild->text, p->text, (size_t)-1) > 0) p->next = pChild; else if (t == NULL) { pParent->child = pChild; pChild->next = p; } else { t->next = pChild; pChild->next = p; } } else { p = pParent->child; while (p->next != NULL) p = p->next; p->next = pChild; } pData->nItemCount++; if (!(pParent->dwFlags & TVIF_FOLD)) pData->nVisItemCount++; hdc = GetClientDC (hwnd); GetTextExtent (hdc, pChild->text, -1, &pChild->text_ext); h = pData->nItemHeight; w = TV_BOXGAP + h + TV_BOXGAP + pChild->text_ext.cx + pChild->depth * h; if (pData->dwStyle & TVS_WITHICON) w += h + TV_ICONGAP; if (pData->dwStyle & WS_BORDER) w += TV_BORDER * 2; pData->nWidth = MAX (w, pData->nWidth); ReleaseDC (hdc); } static void tvDrawItem (HWND hWnd, HDC hdc, PTVDATA pData, int centerX, int centerY, PTVITEM p, int up, int down) { int h = pData->nItemHeight; SetPenColor (hdc, PIXEL_black); SetBkColor (hdc, PIXEL_lightwhite); if (p->child != NULL) { Rectangle (hdc, centerX - TV_BOXHALFHEIGHT, centerY - TV_BOXHALFHEIGHT, centerX + TV_BOXHALFHEIGHT, centerY + TV_BOXHALFHEIGHT); MoveTo (hdc, centerX - TV_BOXHALFHEIGHT + 2, centerY); LineTo (hdc, centerX + TV_BOXHALFHEIGHT - 1, centerY); if (p->dwFlags & TVIF_FOLD) { MoveTo (hdc, centerX, centerY - TV_BOXHALFHEIGHT + 2); LineTo (hdc, centerX, centerY + TV_BOXHALFHEIGHT - 2); } MoveTo (hdc, centerX, centerY - (h>>1) - h * up); LineTo (hdc, centerX, centerY - TV_BOXHALFHEIGHT); MoveTo (hdc, centerX + TV_BOXHALFHEIGHT, centerY); LineTo (hdc, centerX + (h>>1) + TV_BOXGAP, centerY); if (p->next != NULL) { MoveTo (hdc, centerX, centerY + TV_BOXHALFHEIGHT); LineTo (hdc, centerX, centerY + (h>>1) + h * down); } } else { MoveTo (hdc, centerX, centerY - (h>>1) - h * up); LineTo (hdc, centerX, centerY); LineTo (hdc, centerX + (h>>1) + TV_BOXGAP, centerY); if (p->next != NULL) { MoveTo (hdc, centerX, centerY); LineTo (hdc, centerX, centerY + (h>>1) + h * down); } } centerX += (h>>1) + TV_BOXGAP; if (pData->dwStyle & TVS_WITHICON) { if (!(pData->dwStyle & TVS_ICONFORSELECT) && (!p->child || (p->dwFlags & TVIF_FOLD))) { if ((p->hIconFold)) { DrawIcon (hdc, centerX, centerY - (h>>1), h, h, p->hIconFold); } } else if ((pData->dwStyle & TVS_ICONFORSELECT) && p->dwFlags & TVIF_SELECTED) { if ((p->hIconFold)) { DrawIcon (hdc, centerX, centerY - (h>>1), h, h, p->hIconFold); } } else if ((p->hIconUnfold)) { DrawIcon (hdc, centerX, centerY - (h>>1), h, h, p->hIconUnfold); } centerX += h + TV_ICONGAP; } if (p->dwFlags & TVIF_SELECTED) { SetBkMode (hdc, BM_OPAQUE); SetBkColor (hdc, GetWindowElementColorEx (hWnd, BKC_HILIGHT_NORMAL)); SetTextColor (hdc, GetWindowElementColorEx (hWnd, FGC_HILIGHT_NORMAL)); TextOut (hdc, centerX, centerY - (h>>1), p->text); #ifndef _GRAY_SCREEN if (pData->dwStyle & TVS_FOCUS) { FocusRect (hdc, centerX, centerY - (h>>1), centerX + p->text_ext.cx - 1, centerY + (h>>1) - 1); } #endif } else { SetBkMode (hdc, BM_TRANSPARENT); SetBkColor (hdc, GetWindowBkColor (hWnd)); SetTextColor (hdc, GetWindowElementColorEx (hWnd, FGC_CONTROL_NORMAL)); TextOut (hdc, centerX, centerY - (h>>1), p->text); } } /* * draw all items. */ static void doAll (HWND hWnd, HDC hdc, PTVDATA pData, PTVITEM p, int cenX, int cenY, int* count, int* depth) { int up, down, h = pData->nItemHeight; PTVITEM t; up = getItemsWithPrev (pData, p); down = getItemsWithNext (pData, p); tvDrawItem (hWnd, hdc, pData, cenX + *depth * h, cenY + (*count - pData->nItemTop) * h, p, up, down); (*count)++; if (!(p->dwFlags & TVIF_FOLD)) { t = p->child; (*depth)++; while (t) { doAll (hWnd, hdc, pData, t, cenX, cenY, count, depth); if (*count > pData->nItemTop + pData->nVisCount) return; t = t->next; } (*depth)--; } } static void tvOnDraw (HWND hwnd, HDC hdc, PTVDATA pData) { RECT rcClient; int x, y, w, h; int centerX, centerY; int count = 0, depth = 0; GetClientRect (hwnd, &rcClient); x = rcClient.left; y = rcClient.top; w = RECTW (rcClient); h = RECTH (rcClient); if (pData->dwStyle & WS_BORDER) { x += TV_BORDER; y += TV_BORDER; w -= (TV_BORDER<<1); h -= (TV_BORDER<<1); } centerX = x - pData->nLeft + TV_BOXGAP + (pData->nItemHeight>>1); centerY = y + (pData->nItemHeight >> 1); doAll (hwnd, hdc, pData, pData->root, centerX, centerY, &count, &depth); } static void tvSetVScrollInfo (HWND hwnd, PTVDATA pData, BOOL fRedraw) { SCROLLINFO si; if (pData->nVisCount >= pData->nVisItemCount) { pData->nItemTop = 0; SetScrollPos (hwnd, SB_VERT, 0); EnableScrollBar (hwnd, SB_VERT, FALSE); InvalidateRect (hwnd, NULL, TRUE); return; } si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS; si.nMax = pData->nVisItemCount - 1; si.nMin = 0; si.nPage = MIN (pData->nVisCount, pData->nVisItemCount); //- pData->nItemTop)); si.nPos = pData->nItemTop; SetScrollInfo (hwnd, SB_VERT, &si, fRedraw); EnableScrollBar (hwnd, SB_VERT, TRUE); } static void tvSetHScrollInfo (HWND hwnd, PTVDATA pData, BOOL fRedraw) { SCROLLINFO si; if (pData->nVisWidth >= pData->nWidth) { pData->nLeft = 0; SetScrollPos (hwnd, SB_HORZ, 0); EnableScrollBar (hwnd, SB_HORZ, FALSE); InvalidateRect (hwnd, NULL, TRUE); return; } si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS; si.nMax = pData->nWidth - 1; si.nMin = 0; si.nPage = MIN (pData->nVisWidth, pData->nWidth); // - pData->nLeft)); si.nPos = pData->nLeft; SetScrollInfo (hwnd, SB_HORZ, &si, fRedraw); EnableScrollBar (hwnd, SB_HORZ, TRUE); } static void tvRecalcWidthsHelper (HDC hdc, PTVITEM p) { while (p) { GetTextExtent (hdc, p->text, -1, &p->text_ext); if (p->child) tvRecalcWidthsHelper (hdc, p->child); p = p->next; } } static void tvRecalcWidths (HWND hwnd, PTVDATA pData) { RECT rc; HDC hdc = GetClientDC (hwnd); tvRecalcWidthsHelper (hdc, pData->root); pData->nItemHeight = GetFontHeight (hdc); ReleaseDC (hdc); GetClientRect(hwnd,&rc); pData->nVisCount = (RECTH (rc)) / pData->nItemHeight; pData->nWidth = getTVWidth (pData); pData->nVisItemCount = getVisItemCount (pData); InvalidateRect (hwnd, NULL, TRUE); tvSetVScrollInfo (hwnd, pData, TRUE); tvSetHScrollInfo (hwnd, pData, TRUE); } /* * Recalculate and redraw the control */ static void recalc_redraw (HWND hwnd, PTVDATA pData) { pData->nVisItemCount = getVisItemCount (pData); pData->nWidth = getTVWidth (pData); InvalidateRect (hwnd, NULL, TRUE); tvSetVScrollInfo (hwnd, pData, TRUE); tvSetHScrollInfo (hwnd, pData, TRUE); } static void change_selected (HWND hwnd, PTVDATA pData, PTVITEM new_sel) { PTVITEM old_sel = pData->pItemSelected; if (new_sel != old_sel) { if (old_sel) old_sel->dwFlags &= ~TVIF_SELECTED; if (new_sel) new_sel->dwFlags |= TVIF_SELECTED; pData->pItemSelected = new_sel; NotifyParent (hwnd, pData->id, TVN_SELCHANGE); } } static int set_item_text (HWND hwnd, PTVDATA pData, PTVITEM item, const char* text) { HDC hdc; #if _USE_FIXSTR FreeFixStr (item->text); item->text = FixStrAlloc (strlen (text)); if (item->text == NULL) { NotifyParent (hwnd, pData->id, TVN_ERRSPACE); return -1; } strcpy (item->text, text); #else free (item->text); item->text = strdup (text); if (item->text == NULL) { NotifyParent (hwnd, pData->id, TVN_ERRSPACE); return -1; } #endif hdc = GetClientDC (hwnd); GetTextExtent (hdc, item->text, -1, &item->text_ext); ReleaseDC(hdc); return 0; } static LRESULT TreeViewCtrlProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { PTVDATA pData = NULL; DWORD dwStyle; switch (message) { case MSG_CREATE: { PTVITEMINFO root_ii = (PTVITEMINFO)lParam; dwStyle = GetWindowStyle (hwnd); if (!(pData = (PTVDATA) calloc (1, sizeof (TVDATA)))) return -1; SetWindowAdditionalData2 (hwnd, (DWORD) pData); if (!tvInitTVData (hwnd, pData, dwStyle, root_ii)) { free (pData); return -1; } break; } case MSG_SIZECHANGED: { RECT* prc = (RECT*) lParam; pData = (PTVDATA) GetWindowAdditionalData2 (hwnd); pData->nVisCount = (RECTHP (prc)) / pData->nItemHeight; pData->nVisWidth = RECTWP (prc); //recalculate scrollbar information pData->nVisItemCount = getVisItemCount (pData); tvSetVScrollInfo (hwnd, pData, TRUE); tvSetHScrollInfo (hwnd, pData, TRUE); } break; case MSG_DESTROY: pData = (PTVDATA) GetWindowAdditionalData2 (hwnd); tvCleanTVData (hwnd, pData); break; case MSG_GETTEXTLENGTH: case MSG_GETTEXT: case MSG_SETTEXT: return -1; case MSG_PAINT: { HDC hdc; pData = (PTVDATA) GetWindowAdditionalData2 (hwnd); hdc = BeginPaint (hwnd); tvOnDraw (hwnd, hdc, pData); EndPaint (hwnd, hdc); return 0; } case TVM_SETSTRCMPFUNC: pData = (PTVDATA) GetWindowAdditionalData2 (hwnd); if (pData->nItemCount == 1 && lParam) { pData->str_cmp = (STRCMP)lParam; return 0; } return -1; case TVM_GETROOT: pData = (PTVDATA) GetWindowAdditionalData2 (hwnd); return (LRESULT) pData->root; break; case TVM_SEARCHITEM: { const char *text; PTVITEM root, matched; pData = (PTVDATA) GetWindowAdditionalData2 (hwnd); root = (PTVITEM)wParam; text = (const char *)lParam; if (root == NULL) root = pData->root; if (text == NULL || is_descendant (pData->root, root, NULL)) return 0; matched = findStringDepthFirst (pData, root, text); return (LRESULT)matched; } case TVM_FINDCHILD: { const char *text; PTVITEM item, matched; pData = (PTVDATA) GetWindowAdditionalData2 (hwnd); item = (PTVITEM)wParam; text = (const char *)lParam; if (item == NULL) item = pData->root; if (text == NULL || is_descendant (pData->root, item, NULL)) return 0; matched = findStringInChildren (pData, item, text); return (LRESULT)matched; } case TVM_GETSELITEM: pData = (PTVDATA) GetWindowAdditionalData2 (hwnd); return (LRESULT)pData->pItemSelected; case TVM_SETSELITEM: { int count; PTVITEM old_sel, new_sel = (PTVITEM)wParam; pData = (PTVDATA) GetWindowAdditionalData2 (hwnd); if (is_descendant (pData->root, new_sel, NULL)) return -1; old_sel = pData->pItemSelected; unfold_ancestor (hwnd, pData, new_sel); change_selected (hwnd, pData, new_sel); count = getCountFromItem (pData, new_sel); if (count <= pData->nItemTop) pData->nItemTop = count; else if (count >= pData->nItemTop + pData->nVisCount) pData->nItemTop = count - pData->nVisCount; recalc_redraw (hwnd, pData); #if 0 if (old_sel != new_sel) { if (is_descendant (pData->root, new_sel, NULL)) return -1; old_sel->dwFlags &= ~TVIF_SELECTED; new_sel->dwFlags |= TVIF_SELECTED; pData->pItemSelected = new_sel; NotifyParent (hwnd, pData->id, TVN_SELCHANGE); unfold_ancestor (hwnd, pData, new_sel); recalc_redraw (hwnd, pData); } #endif return (LRESULT)old_sel; } case TVM_GETRELATEDITEM: { int related = (int) wParam; PTVITEM item = (PTVITEM) lParam; pData = (PTVDATA) GetWindowAdditionalData2 (hwnd); if (is_descendant (pData->root, item, NULL)) return 0; switch (related) { case TVIR_PARENT: return (LRESULT)item->parent; case TVIR_FIRSTCHILD: return (LRESULT)item->child; case TVIR_NEXTSIBLING: return (LRESULT)item->next; case TVIR_PREVSIBLING: return (LRESULT)getPrev (pData, item); } return 0; } case TVM_GETITEMTEXTLEN: { PTVITEM item = (PTVITEM) wParam; pData = (PTVDATA) GetWindowAdditionalData2 (hwnd); if (is_descendant (pData->root, item, NULL)) return -1; return strlen (item->text); } case TVM_GETITEMTEXT: { PTVITEM item = (PTVITEM) wParam; char* buffer = (char*) lParam; pData = (PTVDATA) GetWindowAdditionalData2 (hwnd); if (is_descendant (pData->root, item, NULL)) return -1; strcpy (buffer, item->text); return strlen (buffer); } case TVM_GETITEMINFO: { PTVITEM item = (PTVITEM)wParam; PTVITEMINFO info = (PTVITEMINFO)lParam; pData = (PTVDATA) GetWindowAdditionalData2 (hwnd); if (is_descendant (pData->root, item, NULL)) return -1; if (info) { if (info->text) strcpy (info->text, item->text); info->dwFlags = item->dwFlags; info->hIconFold = item->hIconFold; info->hIconUnfold = item->hIconUnfold; info->dwAddData = item->dwAddData; } return 0; } case TVM_SETITEMINFO: { PTVITEM item = (PTVITEM)wParam; PTVITEMINFO info = (PTVITEMINFO)lParam; pData = (PTVDATA) GetWindowAdditionalData2 (hwnd); if (is_descendant (pData->root, item, NULL)) return -1; if (info) { if (info->text) { set_item_text (hwnd, pData, item, info->text); } if (info->hIconFold) item->hIconFold = info->hIconFold; if (info->hIconUnfold) item->hIconFold = info->hIconUnfold; item->dwAddData = info->dwAddData; recalc_redraw (hwnd, pData); } return 0; } case TVM_DELTREE: { PTVITEM item = (PTVITEM)wParam; pData = (PTVDATA) GetWindowAdditionalData2 (hwnd); if (item == pData->root) { PTVITEM p = pData->root->child; while (p) { RemoveTree (hwnd, pData, p); p = pData->root->child; } pData->nItemTop = 0; pData->nVisItemCount = 1; pData->nLeft = 0; change_selected (hwnd, pData, item); recalc_redraw (hwnd, pData); return 0; } if (is_descendant (pData->root, item, NULL) == -1) return -1; if (item == pData->pItemSelected) { change_selected (hwnd, pData, item->parent); } #if 0 if (is_descendant (item, pData->pItemSelected, NULL) == 0) { change_selected (hwnd, pData, item->parent); } #endif RemoveTree (hwnd, pData, item); recalc_redraw (hwnd, pData); return 0; } case TVM_ADDITEM: case TVM_INSERTITEM: { PTVITEMINFO pTVItemInfo; PTVITEM parent, newItem = NULL; pData = (PTVDATA) GetWindowAdditionalData2 (hwnd); pTVItemInfo = (PTVITEMINFO) lParam; parent = (PTVITEM) wParam; if (pTVItemInfo) { if (pTVItemInfo->text == NULL || pTVItemInfo->text[0] == '\0') pTVItemInfo->text = " "; if (pTVItemInfo->hIconFold == 0) pTVItemInfo->hIconFold = (HICON) ICON_FOLD; if (pTVItemInfo->hIconUnfold == 0) pTVItemInfo->hIconUnfold = (HICON) ICON_UNFOLD; } else { return 0; } if (parent == NULL) parent = pData->root; newItem = calloc (1, sizeof(TVITEM)); if (newItem == NULL) { NotifyParent (hwnd, pData->id, TVN_ERRSPACE); return 0; } #if _USE_FIXSTR newItem->text = FixStrAlloc (strlen(pTVItemInfo->text)); if (newItem->text == NULL) { free (newItem); NotifyParent (hwnd, pData->id, TVN_ERRSPACE); return 0; } strcpy (newItem->text, pTVItemInfo->text); #else newItem->text = strdup (pTVItemInfo->text); if (newItem->text == NULL) { free (newItem); NotifyParent (hwnd, pData->id, TVN_ERRSPACE); return 0; } #endif if (pData->dwStyle & TVS_WITHICON) { newItem->hIconFold = (HICON) pTVItemInfo->hIconFold; newItem->hIconUnfold = (HICON) pTVItemInfo->hIconUnfold; } else { newItem->hIconFold = 0L; newItem->hIconUnfold = 0L; } if (pTVItemInfo->dwFlags & TVIF_FOLD) newItem->dwFlags = TVIF_FOLD; newItem->dwAddData = pTVItemInfo->dwAddData; InsertChild (hwnd, pData, parent, newItem); tvSetVScrollInfo (hwnd, pData, TRUE); tvSetHScrollInfo (hwnd, pData, TRUE); InvalidateRect (hwnd, NULL, TRUE); return (LRESULT) newItem; } case MSG_KEYDOWN: { PTVITEM p, t; RECT rc; int count, c, rctop; GetClientRect (hwnd, &rc); pData = (PTVDATA) GetWindowAdditionalData2 (hwnd); switch (LOWORD (wParam)) { case SCANCODE_CURSORBLOCKRIGHT: p = pData->pItemSelected; count = getCountFromItem (pData, p); rctop = rc.top; if (p->child == NULL) return 0; else if (p->dwFlags & TVIF_FOLD) { p->dwFlags &= ~TVIF_FOLD; NotifyParentEx (hwnd, pData->id, TVN_UNFOLDED, (DWORD)p); pData->nWidth = getTVWidth (pData); pData->nVisItemCount = getVisItemCount (pData); fprintf(stderr, "VisItemCount is %d.\n", pData->nVisItemCount); count = count - pData->nItemTop - 1; rc.top = rctop + count * pData->nItemHeight; if (pData->dwStyle & WS_BORDER) rc.top += TV_BORDER; InvalidateRect (hwnd, &rc, TRUE); tvSetVScrollInfo (hwnd, pData, TRUE); tvSetHScrollInfo (hwnd, pData, TRUE); } else { t = p->child; change_selected (hwnd, pData, t); c = getCountFromItem (pData, t); if (c > pData->nItemTop + pData->nVisCount) { pData->nItemTop = c - pData->nVisCount; InvalidateRect (hwnd, NULL, TRUE); tvSetVScrollInfo (hwnd, pData, TRUE); break; } c = c - pData->nItemTop - 1; rc.top = rctop + c * pData->nItemHeight; if (pData->dwStyle & WS_BORDER) rc.top += TV_BORDER; rc.bottom = rc.top + pData->nItemHeight; InvalidateRect (hwnd, &rc, TRUE); count = count - pData->nItemTop - 1; rc.top = rctop + count * pData->nItemHeight; if (pData->dwStyle & WS_BORDER) rc.top += TV_BORDER; rc.bottom = rc.top + pData->nItemHeight; InvalidateRect (hwnd, &rc, TRUE); } break; case SCANCODE_CURSORBLOCKLEFT: p = pData->pItemSelected; count = getCountFromItem (pData, p); rctop = rc.top; if (p == NULL) return 0; if (p->child && !(p->dwFlags & TVIF_FOLD)) { p->dwFlags |= TVIF_FOLD; NotifyParentEx (hwnd, pData->id, TVN_FOLDED, (DWORD)p); pData->nWidth = getTVWidth (pData); pData->nVisItemCount = getVisItemCount (pData); fprintf(stderr, "Left:VisItemCount is %d.\n", pData->nVisItemCount); count = count - pData->nItemTop - 1; rc.top = rctop + count * pData->nItemHeight; if (pData->dwStyle & WS_BORDER) rc.top += TV_BORDER; InvalidateRect (hwnd, &rc, TRUE); tvSetVScrollInfo (hwnd, pData, TRUE); tvSetHScrollInfo (hwnd, pData, TRUE); } else { t = getParent (pData, p); if (t == NULL) return 0; change_selected (hwnd, pData, t); c = getCountFromItem (pData, t); if (c <= pData->nItemTop) { pData->nItemTop = c - 1; InvalidateRect (hwnd, NULL, TRUE); tvSetVScrollInfo (hwnd, pData, TRUE); break; } c = c - pData->nItemTop - 1; rc.top = rctop + c * pData->nItemHeight; if (pData->dwStyle & WS_BORDER) rc.top += TV_BORDER; rc.bottom = rc.top + pData->nItemHeight; InvalidateRect (hwnd, &rc, TRUE); count = count - pData->nItemTop - 1; rc.top = rctop + count * pData->nItemHeight; if (pData->dwStyle & WS_BORDER) rc.top += TV_BORDER; rc.bottom = rc.top + pData->nItemHeight; InvalidateRect (hwnd, &rc, TRUE); } break; case SCANCODE_CURSORBLOCKUP: p = pData->pItemSelected; count = getCountFromItem (pData, p); rctop = rc.top; if (count <= 1) break; c = count - 1; t = getItemFromCount (pData, c); if (t == NULL) break; change_selected (hwnd, pData, t); if (c > pData->nItemTop && c <= pData->nItemTop + pData->nVisCount) { c = c - pData->nItemTop - 1; rc.top = rctop + c * pData->nItemHeight; if (pData->dwStyle & WS_BORDER) rc.top += TV_BORDER; rc.bottom = rc.top + pData->nItemHeight; InvalidateRect (hwnd, &rc, TRUE); count = count - pData->nItemTop - 1; rc.top = rctop + count * pData->nItemHeight; if (pData->dwStyle & WS_BORDER) rc.top += TV_BORDER; rc.bottom = rc.top + pData->nItemHeight; InvalidateRect (hwnd, &rc, TRUE); } else { if (c <= pData->nItemTop) pData->nItemTop = c - 1; else if (c >= pData->nItemTop + pData->nVisCount) pData->nItemTop = c - pData->nVisCount; InvalidateRect (hwnd, NULL, TRUE); tvSetVScrollInfo (hwnd, pData, TRUE); } break; case SCANCODE_CURSORBLOCKDOWN: p = pData->pItemSelected; count = getCountFromItem (pData, p); rctop = rc.top; if (count >= pData->nVisItemCount) break; c = count + 1; t = getItemFromCount (pData, c); if (t == NULL) break; change_selected (hwnd, pData, t); if (c > pData->nItemTop && c <= pData->nItemTop + pData->nVisCount) { c = c - pData->nItemTop - 1; rc.top = rctop + c * pData->nItemHeight; if (pData->dwStyle & WS_BORDER) rc.top += TV_BORDER; rc.bottom = rc.top + pData->nItemHeight; InvalidateRect (hwnd, &rc, TRUE); count = count - pData->nItemTop - 1; rc.top = rctop + count * pData->nItemHeight; if (pData->dwStyle & WS_BORDER) rc.top += TV_BORDER; rc.bottom = rc.top + pData->nItemHeight; InvalidateRect (hwnd, &rc, TRUE); } else { if (c <= pData->nItemTop) pData->nItemTop = c - 1; else if (c >= pData->nItemTop + pData->nVisCount) pData->nItemTop = c - pData->nVisCount; InvalidateRect (hwnd, NULL, TRUE); tvSetVScrollInfo (hwnd, pData, TRUE); } break; case SCANCODE_PAGEDOWN: p = pData->pItemSelected; count = getCountFromItem (pData, p); c = count + pData->nVisCount; if (c > pData->nVisItemCount) c = pData->nVisItemCount; if (c <= pData->nItemTop) pData->nItemTop = c - 1; else if (c > pData->nItemTop + pData->nVisCount * 2) pData->nItemTop = c - pData->nVisCount; else if (c > pData->nItemTop + pData->nVisCount) { pData->nItemTop += pData->nVisCount; if (pData->nItemTop > pData->nVisItemCount - pData->nVisCount) pData->nItemTop = pData->nVisItemCount - pData->nVisCount; } t = getItemFromCount (pData, c); change_selected (hwnd, pData, t); InvalidateRect (hwnd, NULL, TRUE); tvSetVScrollInfo (hwnd, pData, TRUE); break; case SCANCODE_PAGEUP: p = pData->pItemSelected; count = getCountFromItem (pData, p); c = count - pData->nVisCount; if (c < 1) c = 1; if (c <= pData->nItemTop - pData->nVisCount) pData->nItemTop = c - 1; else if (c <= pData->nItemTop) { if (pData->nItemTop <= pData->nVisCount) pData->nItemTop = 0; else pData->nItemTop -= pData->nVisCount; } else if (c > pData->nItemTop + pData->nVisCount) pData->nItemTop = c - pData->nVisCount; t = getItemFromCount (pData, c); change_selected (hwnd, pData, t); InvalidateRect (hwnd, NULL, TRUE); tvSetVScrollInfo (hwnd, pData, TRUE); break; case SCANCODE_HOME: pData->nItemTop = 0; change_selected (hwnd, pData, pData->root); InvalidateRect (hwnd, NULL, TRUE); tvSetVScrollInfo (hwnd, pData, TRUE); break; case SCANCODE_END: pData->nItemTop = pData->nVisItemCount - pData->nVisCount; t = getItemFromCount (pData, pData->nVisItemCount); change_selected (hwnd, pData, t); InvalidateRect (hwnd, NULL, TRUE); tvSetVScrollInfo (hwnd, pData, TRUE); break; case SCANCODE_ENTER: NotifyParent (hwnd, pData->id, TVN_ENTER); break; default: break; } } break; case MSG_LBUTTONDOWN: { int mouseX = LOSWORD(lParam); int mouseY = HISWORD(lParam); int right, left, count, h, c, rctop; RECT rc, rcClient; PTVITEM p; pData = (PTVDATA) GetWindowAdditionalData2 (hwnd); count = mouseY / pData->nItemHeight + 1; count += pData->nItemTop; if (count > pData->nVisItemCount) break; p = getItemFromCount(pData, count); if (p == NULL) break; h = pData->nItemHeight; left = - pData->nLeft + TV_BOXGAP + p->depth * h; if (pData->dwStyle & WS_BORDER) left += TV_BORDER; right = left + h + TV_BOXGAP + p->text_ext.cx; if (pData->dwStyle & TVS_WITHICON) right += h + TV_ICONGAP; if (mouseX < left || mouseX > right) break; GetClientRect (hwnd, &rcClient); rc = rcClient; rctop = rc.top; if (mouseX <= right && mouseX >= left && (p != pData->pItemSelected) ) { PLOGFONT logfont; c = getCountFromItem (pData, pData->pItemSelected); c = c - pData->nItemTop - 1; if (c < 0) c = 0; change_selected (hwnd, pData, p); rc.top = rctop + c * pData->nItemHeight; if (pData->dwStyle & WS_BORDER) rc.top += TV_BORDER; rc.bottom = rc.top + pData->nItemHeight; /*bugfix: 4864, zhaochengzhang*/ logfont = GetWindowFont(hwnd); if (logfont->style & (FS_WEIGHT_BOOK | FS_WEIGHT_LIGHT)) { rc.left--; rc.top--; rc.right++; rc.bottom++; } InvalidateRect (hwnd, &rc, TRUE); rc.top = rctop + (count - pData->nItemTop - 1) * pData->nItemHeight; if (pData->dwStyle & WS_BORDER) rc.top += TV_BORDER; rc.bottom = rc.top + pData->nItemHeight; /*bugfix: 4864 zhaochengzhang*/ if (logfont->style & (FS_WEIGHT_BOOK | FS_WEIGHT_LIGHT)) { rc.left--; rc.top--; rc.right++; rc.bottom++; } InvalidateRect (hwnd, &rc, TRUE); NotifyParent (hwnd, pData->id, TVN_CLICKED); } if (mouseX <= left + h && mouseX >= left) { PLOGFONT logfont; if (p->child == NULL) break; if (p->dwFlags & TVIF_FOLD) { p->dwFlags &= ~TVIF_FOLD; NotifyParentEx (hwnd, pData->id, TVN_UNFOLDED, (DWORD)p); } else { p->dwFlags |= TVIF_FOLD; NotifyParentEx (hwnd, pData->id, TVN_FOLDED, (DWORD)p); } #if 0 p->dwFlags = (p->dwFlags & ~TVIF_FOLD) | (~p->dwFlags & TVIF_FOLD); #endif pData->nWidth = getTVWidth (pData); pData->nVisItemCount = getVisItemCount (pData); rc.top = rctop + (count - pData->nItemTop - 1) * pData->nItemHeight; if (pData->dwStyle & WS_BORDER) rc.top += TV_BORDER; tvSetVScrollInfo (hwnd, pData, TRUE); tvSetHScrollInfo (hwnd, pData, TRUE); rc.bottom = rcClient.bottom; /*bugfix: 4864 zhaochengzhang*/ logfont = GetWindowFont(hwnd); if (logfont->style & (FS_WEIGHT_BOOK | FS_WEIGHT_LIGHT)) { rc.left--; rc.top--; rc.right++; rc.bottom++; } InvalidateRect (hwnd, &rc, TRUE); NotifyParent (hwnd, pData->id, TVN_CLICKED); } } break; case MSG_LBUTTONDBLCLK: { int mouseX = LOSWORD(lParam); int mouseY = HISWORD(lParam); int right, left, count, h; PTVITEM p; RECT rc; pData = (PTVDATA) GetWindowAdditionalData2 (hwnd); count = mouseY / pData->nItemHeight + 1; count += pData->nItemTop; if (count > pData->nVisItemCount) break; p = getItemFromCount(pData, count); if (p == NULL) break; h = pData->nItemHeight; left = - pData->nLeft + TV_BOXGAP + h + p->depth * h; if (pData->dwStyle & WS_BORDER) left += TV_BORDER; right = left + TV_BOXGAP + p->text_ext.cx; if (pData->dwStyle & TVS_WITHICON) right += h + TV_ICONGAP; if (mouseX <= right && mouseX >= left) { NotifyParentEx (hwnd, pData->id, TVN_DBLCLK, (DWORD)p); if (p->child) { if (p->dwFlags & TVIF_FOLD) { p->dwFlags &= ~TVIF_FOLD; NotifyParentEx (hwnd, pData->id, TVN_UNFOLDED, (DWORD)p); } else { p->dwFlags |= TVIF_FOLD; NotifyParentEx (hwnd, pData->id, TVN_FOLDED, (DWORD)p); } #if 0 p->dwFlags = (p->dwFlags & ~TVIF_FOLD) | (~p->dwFlags & TVIF_FOLD); #endif pData->nWidth = getTVWidth (pData); pData->nVisItemCount = getVisItemCount (pData); tvSetVScrollInfo (hwnd, pData, TRUE); tvSetHScrollInfo (hwnd, pData, TRUE); GetClientRect (hwnd, &rc); rc.top += (count - pData->nItemTop - 1) * pData->nItemHeight; if (pData->dwStyle & WS_BORDER) rc.top += TV_BORDER; InvalidateRect (hwnd, &rc, TRUE); break; } } } break; case MSG_VSCROLL: { int newTop; int scrollHeight = 0; pData = (PTVDATA) GetWindowAdditionalData2 (hwnd); newTop = pData->nItemTop; switch(wParam) { case SB_LINEDOWN: if ((pData->nItemTop + pData->nVisCount) < (pData->nVisItemCount)) { newTop ++; scrollHeight = - pData->nItemHeight; } break; case SB_LINEUP: if (pData->nItemTop > 0) { newTop --; scrollHeight = pData->nItemHeight; } break; case SB_PAGEDOWN: if ((pData->nItemTop + (pData->nVisCount << 1)) <= pData->nItemCount) newTop += pData->nVisCount; else newTop = pData->nVisItemCount - pData->nVisCount; scrollHeight = - (newTop - pData->nItemTop) * pData->nItemHeight; if (newTop < 0) return 0; break; case SB_PAGEUP: if (pData->nItemTop >= pData->nVisCount) newTop -= pData->nVisCount; else newTop = 0; scrollHeight = (pData->nItemTop - newTop) * pData->nItemHeight; break; case SB_THUMBTRACK: newTop = MIN((int)lParam, pData->nVisItemCount - pData->nVisCount); scrollHeight = (pData->nItemTop - newTop) * pData->nItemHeight; break; } if (scrollHeight) { pData->nItemTop = newTop; tvSetVScrollInfo (hwnd, pData, TRUE); InvalidateRect (hwnd, NULL, TRUE); return 0; } } break; case MSG_HSCROLL: { int newLeft; int scrollWidth = 0; pData = (PTVDATA) GetWindowAdditionalData2 (hwnd); newLeft = pData->nLeft; switch(wParam) { case SB_LINERIGHT: if ((newLeft + pData->nVisWidth) < (pData->nWidth)) { newLeft += GetSysCharWidth (); if (newLeft > pData->nWidth) newLeft = pData->nWidth; scrollWidth = - GetSysCharWidth (); } break; case SB_LINELEFT: if (newLeft > 0) { newLeft -= GetSysCharWidth (); if (newLeft < 0) newLeft = 0; scrollWidth = GetSysCharWidth (); } break; case SB_PAGERIGHT: if ((newLeft + (pData->nVisWidth << 1)) <= pData->nWidth) newLeft += pData->nVisWidth; else newLeft = pData->nWidth - pData->nVisWidth; scrollWidth = - (newLeft - pData->nLeft) * GetSysCharWidth (); if (newLeft < 0) return 0; break; case SB_PAGELEFT: if (pData->nLeft >= pData->nVisWidth) newLeft -= pData->nVisWidth; else newLeft = 0; scrollWidth = (pData->nLeft - newLeft) * GetSysCharWidth (); break; case SB_THUMBTRACK: newLeft = MIN((int)lParam, (pData->nWidth - pData->nVisWidth)); scrollWidth = (pData->nLeft - newLeft) * GetSysCharWidth (); break; } if (scrollWidth) { pData->nLeft = newLeft; tvSetHScrollInfo (hwnd, pData, TRUE); InvalidateRect (hwnd, NULL, TRUE); return 0; } } break; case MSG_SETFOCUS: pData = (PTVDATA) GetWindowAdditionalData2 (hwnd); NotifyParent (hwnd, pData->id, TVN_SETFOCUS); if (pData->dwStyle & TVS_FOCUS) break; pData->dwStyle |= TVS_FOCUS; InvalidateRect (hwnd, NULL, TRUE); break; case MSG_KILLFOCUS: pData = (PTVDATA) GetWindowAdditionalData2 (hwnd); NotifyParent (hwnd, pData->id, TVN_KILLFOCUS); if (!(pData->dwStyle & TVS_FOCUS)) break; pData->dwStyle &= ~TVS_FOCUS; InvalidateRect (hwnd, NULL, TRUE); break; case MSG_GETDLGCODE: return DLGC_WANTARROWS | DLGC_WANTCHARS; case MSG_FONTCHANGED: pData = (PTVDATA) GetWindowAdditionalData2 (hwnd); tvRecalcWidths (hwnd, pData); return 0; default: break; } return DefaultControlProc (hwnd, message, wParam, lParam); } #endif /* _EXT_CTRL_TREEVIEW */