mirror of
https://github.com/VincentWei/MiniGUI.git
synced 2026-09-28 16:17:09 +08:00
1744 lines
56 KiB
C
1744 lines
56 KiB
C
/*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*
|
|
* 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
|
|
* <http://www.minigui.com/en/about/licensing-policy/>.
|
|
*/
|
|
/*
|
|
** treeview.c: TreeView Control.
|
|
*/
|
|
|
|
#include <string.h>
|
|
|
|
#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 */
|
|
|