/*
 * PROJECT:     ReactOS GUI first stage setup application
 * LICENSE:     GPL-3.0-or-later (https://spdx.org/licenses/GPL-3.0-or-later)
 * PURPOSE:     Implements a TreeList control: a tree window with columns.
 * COPYRIGHT:   Copyright (C) Anton Zechner (az_software@inode.at) 2007
 *              Copyright (C) Sébastien Kirche (sebastien.kirche@free.fr) 2014
 *
 * NOTE: Taken from the TreeList code found at https://github.com/sebkirche/treelist
 */

//*****************************************************************************
//*
//*
//*		TreeListWnd.cpp
//*
//*
//*****************************************************************************
//
//	This code creates a tree window with a list
//
//
//	Copyright (C) Anton Zechner (az_software@inode.at) 2007
//	Copyright (C) Sébastien Kirche (sebastien.kirche@free.fr) 2014
//
//	TreeListWnd is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
//	Sourcecode which use TreeListWnd must be published. Commercial users
//	must published their code too, or make an licence agreement with me.
//
//
//	TreeListWnd wird unter GNU GENERAL PUBLIC LICENSE (GPL) vertreiben.
//	Sourcecode welcher TreeListWnd verwendet muss veröffendlicht werden.
//	Komerzielle Nutzer müssen ihren Code ebenfalls veröffentlichen, oder
//	eine Nutzungsvereinbarung mit mir treffen.
//
//
//	Version:	2.04
//
#ifdef		UNICODE
#ifndef	   _UNICODE
#define    _UNICODE
#endif
#endif

#if 0
    #include	<stdio.h>
    #include	<windows.h>
    #include	<string.h>
    #include	<malloc.h>
    #include	<tchar.h>
#else
    #include "reactos.h"
#endif

#define new(TYPE, numElems) \
    HeapAlloc(GetProcessHeap(), 0, (numElems) * sizeof(TYPE))
#define delete(ptr) \
    HeapFree(GetProcessHeap(), 0, (ptr))


#include	"treelist.h"

#ifndef		GWLP_USERDATA
#define 	GWLP_USERDATA		GWL_USERDATA
#endif
#ifndef 	GWLP_WNDPROC
#define 	GWLP_WNDPROC		GWL_WNDPROC
#endif
#ifndef	   _WIN64
#ifndef		SetWindowLongPtr
#define		SetWindowLongPtr	SetWindowLong
#endif
#ifndef		GetWindowLongPtr
#define		GetWindowLongPtr	GetWindowLong
#endif
#ifndef		DWORD_PTR
#define		DWORD_PTR			DWORD
#endif
#ifndef		LONG_PTR
#define		LONG_PTR			LONG
#endif
#endif
#ifdef		UNICODE
#define 	str_len				(unsigned)wcslen
#define 	str_cmp				wcscmp
#define 	str_ncpy			wcsncpy
#define 	str_ncmp			wcsncmp
#define 	str_icmp		   _wcsicmp
#else
#define 	str_len				(unsigned)strlen
#define 	str_cmp				strcmp
#define 	str_ncpy			strncpy
#define 	str_ncmp			strncmp
#define 	str_icmp		   _stricmp
#endif
#ifndef		WM_MOUSEWHEEL
#define		WM_MOUSEWHEEL		0x020A
#endif
#ifndef		WHEEL_DELTA
#define		WHEEL_DELTA			120
#endif
#ifndef		MAX_COLUMNS
#define 	MAX_COLUMNS			32
#endif
#define		MAX_COLORS			16
#define 	EN_SETTEXT			0x1000
#define 	EN_RETURN			0x1578
#define 	EN_ESCAPE			0x1579
#define 	ID_TOOLTIPCHECK		0x3912
#define 	SORT_NOUPDATE		1234567
#define 	VK_ISACHAR			0x01000000
#define 	FIRST_LINE			0xFFFFFFFE
#define 	FROM_HEADER			0x88776655
#define 	I_CCB				I_CHILDRENCALLBACK
#define 	U(h)				((unsigned)(ULONG_PTR)(h))
#define 	THEMEIMGLIST		((HIMAGELIST)1)
#define 	GetHandle(h)		((TreeListData*)GetWindowLongPtr(h,0))
#define 	TVIF_ALL			(TVIF_CHILDREN|TVIF_HANDLE|TVIF_IMAGE|TVIF_PARAM|TVIF_SELECTEDIMAGE|TVIF_STATE|TVIF_TEXT)
#define 	UNLOCK(d)			ReleaseSemaphore(d->hSem,1,NULL)
#define 	LOCK(d)				WaitForSingleObject(d->hSem,INFINITE)
#define 	TVIS_EDIT(m)		((1<<m)&((1<<TVAX_EDIT)|(1<<TVAX_COMBO)|(1<<TVAX_STEPED)|(1<<TVAX_CHECKED)))
#define 	TVIS_BASEFLAGS		(TVIS_EXPANDED|TVIS_EXPANDEDONCE|TVIS_EXPANDPARTIAL|TVIS_SELECTED)
#define 	TVIS_TRACKED		(TVIX_TRACKED<<16)
#define 	TVIS_BKCOLOR		(TVIX_BKCOLOR<<16)
#undef TVIS_FOCUSED
#define 	TVIS_FOCUSED		(TVIX_FOCUSED<<16)
#define 	TVIS_TEXTCOLOR		(TVIX_TEXTCOLOR<<16)
#define 	TVC_ONLYFOCUS		TVIF_ONLYFOCUS
#define 	TVC_UNSELECT		0x4000
#define 	TVC_DESELECT		0x8000
#define 	DEFAULT_IDENT		19
#define 	DEFAULT_SHIFT		7
#ifndef		BPBF_COMPATIBLEBITMAP
#define 	BPBF_COMPATIBLEBITMAP	0
#endif
#ifndef		TVP_GLYPH
#define		TVP_GLYPH			2
#endif
#ifndef		GLPS_CLOSED
#define		GLPS_CLOSED			1
#endif
#ifndef		GLPS_OPENED
#define		GLPS_OPENED			2
#endif
#ifndef		BP_CHECKBOX
#define		BP_CHECKBOX			3
#endif
#ifndef		CBS_UNCHECKEDNORMAL
#define		CBS_UNCHECKEDNORMAL 1
#endif
#ifndef		CBS_CHECKEDNORMAL
#define		CBS_CHECKEDNORMAL	5
#endif


#define 	TVAX_NONE			(TVAE_NONE   >>TVAE_MODEPOS)// No automatic edit
#define 	TVAX_EDIT			(TVAE_EDIT	 >>TVAE_MODEPOS)// automatic edit with edit
#define 	TVAX_COMBO			(TVAE_COMBO	 >>TVAE_MODEPOS)// automatic edit with ComboBox
#define 	TVAX_CBLIST			(TVAE_CBLIST >>TVAE_MODEPOS)// automatic edit with  ComboListBox
#define 	TVAX_STEP			(TVAE_STEP	 >>TVAE_MODEPOS)// Einzelnes Weiterschalten mit Enter
#define 	TVAX_STEPED			(TVAE_STEPED >>TVAE_MODEPOS)// Einzelnes Weiterschalten mit Enter und Edit
#define 	TVAX_CHECK			(TVAE_CHECK	 >>TVAE_MODEPOS)// automatic edit with  CheckBox
#define 	TVAX_CHECKED		(TVAE_CHECKED>>TVAE_MODEPOS)// automatic edit with CheckBox and Edit

#define 	TVIX_VARBUTTON		0x01						// buttons are not permanent
#define 	TVIX_HASBUTTON		0x02						// entry has button
#define 	TVIX_HASIMAGE		0x04						// entry has icon
#define 	TVIX_TRACKED		0x08						// entry under the cursor
#define 	TVIX_TEXTCOLOR		0x10						// entry has its own text color
#define 	TVIX_BKCOLOR		0x20						// entry has its own backround color
#define 	TVIX_FOCUSED		0x40						// entry has the focus

typedef struct {
	LPARAM		lParam;										// LPARAM argument for the item
	LPTSTR		pText;										// pointer to the item text
	UINT		uState;										// item state
	int			iImage;										// item image index 
	int			iSelectedImage; 							// item selected image index
	unsigned	uShowPos;									// Ist die Position in der Sichtbarliste (0=unsichtbar)
	unsigned	uFirstChild;								// Ist die Nummer des ersten Kind-Eintrages (0=keines)
	unsigned	uLastChild;									// Ist die Nummer des letzten Kind-Eintrages (0=keines)
	unsigned	uPrevItem;									// Ist die Nummer des vorherigen Eintrages (0=keines)
	unsigned	uNextItem; 									// Ist die Nummer des nächsten Eintrages (0=keines)
	unsigned	uParent; 									// Ist die Nummer des Elterneintrages (0=Root)
	unsigned 	uLevel;										// Ist die Ebene des Eintrages (0=Root)
	int 		iTextPixels;								// Ist die Breites des Textes in Pixel
	WORD		uTextSize;									// Länge des Textes in Zeichen
	BYTE		bCallback;									// Sind Bits für Callbacks
	BYTE		bFlags;										// Diverse Flags
	COLORREF	uColorText;									// Spezielle Textfarbe
	COLORREF	uColorBk;									// Spezielle Hintergrundfarbe
} BaseItem;

typedef struct {
	LPTSTR		pText;										// Zeiger auf Tree-Text
	UINT		uState;										// Zustand des Eintrages
	int			iImage;										// Ist die Nummer des an zu zeigenden Icons
	int 		iTextPixels;								// Ist die Breites des Textes in Pixel
	WORD		uTextSize;									// Länge des Textes in Zeichen
	BYTE		bCallback;									// Sind Bits für Callbacks
	BYTE		bFlags;										// Diverse Flags
	COLORREF	uColorText;									// Spezielle Textfarbe
	COLORREF	uColorBk;									// Spezielle Hintergrundfarbe
} ExtraItem;

typedef struct {
	void 	   *pCbData;									// Data for autoedit
	INT  	    iCbIcon;									// Starting offset for in icon list for autoedit
	short 		sSize;										// width of the column
	short 		sReal;										// real width of the column
	short 		sMin;										// minimum width
	short  	    sFixed;										// fixed width
	BYTE		bMinEx;										// the width cannot be less than min width
	BYTE		bWeight;									// weight for variable columns
	BYTE		bNext;										// Ist die Spalte die nach der eigenen sichtbar ist	(gespeicherte Reihenfolge)
	BYTE		bIndex;										// Ist die Spalte in der diese Reihe sichtbar ist (sichtbarer Index)
	BYTE  	    bAlign;										// Text alignment
	BYTE  	    bEdit;										// Automaisches Editiern einer Spalte (siehe TVAE_???>>7)
	BYTE  	    bFlags;										// Automaisches Editiern einer Spalte (siehe TVAE_???)
	BYTE  	    bEnable;									// Automaisches einer mit Statebits aktivieren
	BYTE  	    bCbSize;									// Maximum number of entries in the data list
	BYTE  	    bCbChar;									// separator for the data list
	BYTE  	    bMark;										// is column marked ?
	BYTE  	    bDummy[32 - 23 - sizeof(void *)];			// padding bytes - 32 bytes alignment
} ColumnData;

typedef struct {
	HWND		hWnd;										// handle of the control
	HANDLE		hSem;										// access semaphore
	LPVOID		hTheme;										// Handle für benutztes Thema (TREELIST)
	LPVOID		hThemeBt;									// Handle für benutztes Thema (BUTTON)
	WNDPROC		pProcId3;									// Fenster Funktion für ID3 Fenster
	HIMAGELIST	hStates;									// Handle der Icon-Liste für States und Overlay
	HIMAGELIST	hImages;									// Handle der Icon-Liste
	HIMAGELIST	hChecks;									// Handle der Icon-Liste für die Checkboxen in den Spalten
	HIMAGELIST	hSubImg;									// Handle der Icon-Liste für die Spalten
	HIMAGELIST	hHeadImg;									// Handle for header images
	HFONT		hFontN;										// Normal font
	HFONT		hFontB;										// Bold fonts
	HFONT		hFontL;										// Last used font
	HFONT		hFontT;										// Tooltip font
	HWND		hEdit;										// Handle des Edit-Fensters
	HWND		hHeader;									// Handle des Header Fensters
	HWND		hToolTip;									// Handle des Tooltip-Fensters
	WNDPROC		pToolProc;									// Alte Fensterfunktion des Tooltips
	COLORREF	uColors[MAX_COLORS];						// 0=Hintergrundfarbe  1=Abwechselnte Farbe  2=Farbe für Trennlinien  3=Textfarbe
	int			iFontHeight;								// Ist die Höhe des Fonts
	int			iFontLine;									// Ist die Position der Linie beim unterstreichen
	int			iFontOff;									// Ist die Position um der ein Text horizontal verschoben wird
	int			iStatesMode;								// Die hStates Image-Liste wurde für die Checkboxen erzeugt
	int			iStatesXsize;								// Breite der States und Overlay Icons
	int			iStatesYsize;								// Höhe   der States und Overlay Icons
	int			iChecksMode;								// Die hChecks Image-Liste wurde für die Checkboxen erzeugt
	int			iChecksXsize;								// Breite der States und Overlay Icons
	int			iChecksYsize;								// Höhe   der States und Overlay Icons
	int			iImagesXsize;								// Breite der Icons
	int			iImagesYsize;								// Höhe   der Icons
	int			iSubImgMode;								// Die SubImg Image-Liste ist nicht die hImages Liste
	int			iSubImgXsize;								// Breite der Icons
	int			iSubImgYsize;								// Höhe   der Icons
	int			iRowHeight;									// Ist die Höhe einer Zeile
	int			iAllWeight;									// Das Gewicht aller variablen Spalten
	int			iVarSize;									// Ist die Breite aller variablen Spalten
	int			iFixSize;									// Ist die Breite aller fixen Spalten
	int			iIndent;									// Einrückung der Kindereintäge
	int			iShift;										// Einrückung der vertikalen Linien
	int			iAutoAdd;									// Offset zum Open-Icon  für TVS_EX_AUTOEXPANDICON
	int			iMaxSizeX;									// Die Größe des breitesten sichtbaren Eintrages
	unsigned	uItemPosCount;								// Anzahl der sichtbaren Einträge
	unsigned   *pItemPos;									// Liste mit den Offsets der sichtbaren Einträge
	BaseItem  **pTreeItems;									// Zeiger auf Item Zeiger
	ExtraItem **pExtraItems[MAX_COLUMNS - 1];				// Zeiger auf die Spalteneinträge
	unsigned	uTreeItemsMax;								// Größe der Liste mit den vorhanden Einträge (alociert um 1 größer)
	unsigned	uTreeItemsCount;							// Anzahl der vorhanden Einträge
	unsigned	uNextSeachPos;								// Nächste Position zum suchen von freien Einträgen
	unsigned	uUserDataSize;								// Ist die Größe der Userdaten in einem Eintrag
	unsigned	uFirstChild;								// Ist die Nummer des ersten Kind-Eintrages (0=keines)
	unsigned	uLastChild;									// Ist die Nummer des letzten Kind-Eintrages (0=keines)
	unsigned	uSingleSel;									// Ist die Nummer des gewählten Eintrages (bei Checkboxen)
	unsigned	uScrollX;									// Aktuelle X-Scroll-Position
	unsigned	uScrollY;									// Aktuelle Y-Scroll-Position
	unsigned	uSizeX;										// Aktuelle X-Fenster-Größe
	unsigned	uSizeY;										// Aktuelle Y-Fenster-Größe
	unsigned	uSizeYsub;									// Aktuelle Y-Fenster-Größe ohne Header
	unsigned	uStyle;										// Ist der aktuele Style des Fensters
	unsigned	uStyleEx;									// Erweiterte Sytle-Flags (siehe TVS_EX_???)
	unsigned	uStartPixel;								// Ist die Y-Koordinate bei der der erste Eintrag beginnt
	unsigned	uMaxEnties;									// Anzahl der sichtbaren Einträge (inkl. halbsichtbare)
	unsigned	uPageEnties;								// Anzahl der sichtbaren Einträge (ohne  halbsichtbare)
	unsigned	uColumnCount;								// Anzahl der Spalten
	unsigned	uColumnCountVar;							// Anzahl der variabeln Spalten
	unsigned	uSelectedCount;								// Anzahl der ausgewählten Einträge
	unsigned	uSelectedBase;								// Ist der Eintrag ab dem gewählt wurde
	unsigned	uSelectedItem;								// Ist der Eintrag der gerade gewählt ist
	unsigned	uSelectedSub;								// Ist die Spalte die gerade gewählt ist
	unsigned	uFocusItem;									// Ist der Eintrag der einen leeren Focus hat
	unsigned	uFocusSub;									// Ist die Spalte die einen leeren Focus hat
	unsigned	uToolTipItem;								// Ist der ToolTip-Eintrag der gerade gewählt ist
	unsigned	uToolTipShow;								// Ist die Zeitverzögerung in 500 ms Schritten für das Tooltip
	unsigned	uToolTipSub;								// Ist die ToolTip-Spalte die gerade gewählt ist
	POINT		sToolTipPos;								// Ist die globale Koordinate des ToolTips
	unsigned	uEditMode;									// Ist der Modus des Editfensters (0=Edit 1=ComboBox 2=ComboBox fix)
	unsigned	uEditItem;									// Ist der Eintrag der gerade editiert wird
	unsigned	uEditSub;									// Ist die Spalte die gerade editiert wird
	unsigned	uOldXPage;									// Alte Werte für X-Scroll-Bar
	unsigned	uOldXCount;									//			*
	unsigned	uOldYPage;									// Alte Werte für Y-Scroll-Bar
	unsigned	uOldYCount;									//			*
	unsigned	uTrippleB;									// Bereite des "..." Strings für den fetten Fonts
	unsigned	uTrippleN;									// Bereite des "..." Strings für den normalen Fonts
	unsigned	uTrackedItem;								// Ist der Eintrag der unterstrichen werden soll
	unsigned	uTrackedSub;								// Ist die Spalte des Eintrages der unterstrichen werden soll
	unsigned	uInsertMark;								// Ist der Eintrag mit der Einfügemarke
	unsigned	uMarkedCols;								// Anzahl der markierten Spalten
	unsigned	uDragFlags;									// Welche Maustasten sind an
	unsigned	uDragItem;									// Eintrag für Dragoperation
	unsigned	uDragSub;									// Untereintrag für Dragoperation
	unsigned	uLastSel;									// Letzte Textauswahl beim Editieren
	unsigned	uLastMove;									// Letzte Cursorposition bei WM_MOUSEMOVE
	unsigned 	uButtonPos;									// Wo wurde eine Maustaste wurde zuletzt gedrückt
	unsigned 	uButtonLast;								// Wann wurde eine Maustaste wurde zuletzt gedrückt
	unsigned	uToolTipSize;								// Textspeichergröße für Tooltip
	LPTSTR		pToolTipText;								// Textspeicher für Tooltip
	TCHAR		cTempText1   [260];							// Erster Textpuffer für Callbacks
	TCHAR		cTempText2   [260];							// Zeiter Textpuffer für Callbacks
	ColumnData	aColumn      [MAX_COLUMNS];					// Daten der Spalten
	int			aColumnXpos  [MAX_COLUMNS + 2];				// Array mit den Positionen der Spalten
	BYTE		aColumnPos   [MAX_COLUMNS + 2];				// Array mit Anzeigepositionen der Spalten
	char		cColorChanged[MAX_COLORS ];					// Welche Farbe wurden verändert
	char   	    cColumnStart;								// Wurde das Autoeditiren mit einer WM_CHAR Eingabe gestartet
	char 		cFixedHeight;								// Ist eine fixe Höhe eingestellt
	char		cLockChanges;								// Sperren von Fensteränderungen
	char		cHasRootRow;								// Wird gesetzt wenn eine Root-Spalte eingefügt wird
	char		cKeyIgnore;									// Die nächste Taste nicht für Sucher verwenden
	char		cClickFlag;									// Merker für LBUTTON-DOWN bei Multiselect
	char		cClickEdit;									// Merker für LBUTTON-DOWN bei Edit-Click
	char		cIsEnabled;									// Ist das Fenster freigegeben
	char		cHasFocus;									// Hat das Fenster den Focus
	char		cReSelect;									// Soll die Auswahl neu selektiert werden
	char		cGlyphOk;									// Die Schaltfäche über Themen zeichnen
	char		cEditCb;									// Muss das Edit-Fenster einen Callback aufrufen
	char		cButtonFlag;								// Welche Maustaste wurde zuletzt gedrückt
} TreeListData;

typedef	HRESULT(WINAPI *SetWindowThemeT)(HWND, LPCWSTR, LPCWSTR);
typedef	HRESULT(WINAPI *EndBufferedPtT)(HANDLE, BOOL);
typedef	HANDLE(WINAPI *BeginBufferedPnT)(HDC, RECT *, DWORD, LPVOID, HDC *);
typedef	HRESULT(WINAPI *BufferedPtInitT)(VOID);
typedef	HRESULT(WINAPI *BufferedPtInitT)(VOID);
typedef	LPVOID (WINAPI *OpenThemeDataT)(HWND hwnd, LPCWSTR pszClassList);
typedef	HRESULT(WINAPI *CloseThemeDataT)(LPVOID);
typedef	HRESULT(WINAPI *DrawThemeBackgT)(LPVOID, HDC, int, int, const RECT *, const RECT *);
typedef	HRESULT(WINAPI *GetThemeBackgRcT)(LPVOID, HDC, int, int, LPCRECT, LPRECT);
typedef BOOL (WINAPI *IsAppThemedT)();
typedef BOOL (WINAPI *IsThemeActiveT)();

static HMODULE			hUxThemeDll		= NULL;
static SetWindowThemeT	pSetWindowTheme	= NULL;
static EndBufferedPtT	pEndBufferedPt  = NULL;
static BeginBufferedPnT	pBeginBufferedPt = NULL;
static BufferedPtInitT	pBufferedPtInit = NULL;
static BufferedPtInitT	pBufferedPtExit = NULL;
static OpenThemeDataT	pOpenThemeData	= NULL;
static CloseThemeDataT	pCloseThemeData	= NULL;
static DrawThemeBackgT	pDrawThemeBackg	= NULL;
static GetThemeBackgRcT	pGetThemeBackgRc = NULL;
static IsAppThemedT		pIsAppThemed	= NULL;
static IsThemeActiveT	pIsThemeActive  = NULL;
static HPEN				hPatternPen		= NULL;
static HFONT			hDefaultFontN	= NULL;
static HFONT			hDefaultFontB	= NULL;
static LONG				lWindowCount	= -1;
#ifndef __REACTOS__
static RECT				sToolRect = { -2, 0, 2, 64};
#endif
static TCHAR			cKeyData[16];
static unsigned 		uKeyLast;
static unsigned 		uKeyPos;
static void				TreeListDraw(HWND hWnd, HDC hDc, RECT *pRect);
static LRESULT CALLBACK TreeListProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
static int				TreeListSelectItem(TreeListData *pData, unsigned uItem, unsigned uSubItem, int iMode);
static int				TreeListGetItemRect(TreeListData *pData, unsigned uItem, unsigned uFlags, RECT *pRect);
static int				TreeListStartNotifyEdit(TreeListData *pData, unsigned uItem, unsigned uSub, WPARAM wParam, LPARAM lParam);
static int				TreeListStartAutoEdit(TreeListData *pData, unsigned uColumn, WPARAM wParam, LPARAM lParam);
static int				TreeListEndLabelEdit(TreeListData *pData, int iMode);
static BOOL				bDrawWithTheme = FALSE;

//*****************************************************************************
//*
//*		TreeListRegister
//*
//*****************************************************************************
//	Registiert das TreeList Fenster.
//	Ergibt 1 wenn das Fenster erfolgreich registiert wurde.
int TreeListRegister(HINSTANCE hInstance) {

	static int		iIsRegistered = FALSE;
	WNDCLASSEX		sClass;

	OutputDebugString(TEXT("TreeListRegister() - before checking\n"));

	if(iIsRegistered)
		return TRUE;

	OutputDebugString(TEXT("TreeListRegister() - before registration\n"));

	memset(&sClass, 0, sizeof(sClass));
	sClass.cbSize           = sizeof(sClass);
	sClass.style            = CS_DBLCLKS | CS_GLOBALCLASS;
	sClass.lpfnWndProc      = TreeListProc;
	sClass.cbClsExtra       = 0;
	sClass.cbWndExtra       = sizeof(TreeListData *);
	sClass.hInstance        = hInstance;
	sClass.hIcon            = NULL;
	sClass.hCursor          = LoadCursor(NULL, IDC_ARROW);
	sClass.hbrBackground	= NULL;
	sClass.lpszMenuName     = NULL;
	sClass.hIconSm          = NULL;
	sClass.lpszClassName	= _T(TVC_CLASSNAME);

	if(!RegisterClassEx(&sClass))
		return 0;

	OutputDebugString(TEXT("TreeListRegister() - registration done\n"));
	iIsRegistered = TRUE;

	return TRUE;
}

BOOL TreeListUnregister(HINSTANCE hInstance){
	return UnregisterClass(_T(TVC_CLASSNAME),hInstance);
}

//*****************************************************************************
//*
//*		GlobalInit
//*
//*****************************************************************************
static void GlobalInit() {

	LOGBRUSH	sLog;
	long		lCount;



	lCount = InterlockedIncrement(&lWindowCount);
	if(lCount > 0)
		return;

	sLog.lbColor = GetSysColor(COLOR_BTNSHADOW);
	sLog.lbStyle = PS_SOLID;
	sLog.lbHatch = 0;
	hPatternPen  = ExtCreatePen(PS_COSMETIC | PS_ALTERNATE, 1, &sLog, 0, NULL);

	if(!hPatternPen) {
		hPatternPen = CreatePen(PS_DOT, 1, RGB(0, 0, 0));
	}


	if(!hUxThemeDll) {
		hUxThemeDll = LoadLibrary(_T("UxTheme.dll"));
		if(hUxThemeDll) {
			pSetWindowTheme  = (SetWindowThemeT)GetProcAddress(hUxThemeDll, "SetWindowTheme");
			pEndBufferedPt   = (EndBufferedPtT)GetProcAddress(hUxThemeDll, "EndBufferedPaint");
			pBeginBufferedPt = (BeginBufferedPnT)GetProcAddress(hUxThemeDll, "BeginBufferedPaint");
			pBufferedPtInit	 = (BufferedPtInitT)GetProcAddress(hUxThemeDll, "BufferedPaintInit");
			pBufferedPtExit  = (BufferedPtInitT)GetProcAddress(hUxThemeDll, "BufferedPaintUnInit");
			pOpenThemeData	 = (OpenThemeDataT)GetProcAddress(hUxThemeDll, "OpenThemeData");
			pCloseThemeData	 = (CloseThemeDataT)GetProcAddress(hUxThemeDll, "CloseThemeData");
			pDrawThemeBackg	 = (DrawThemeBackgT)GetProcAddress(hUxThemeDll, "DrawThemeBackground");
			pGetThemeBackgRc = (GetThemeBackgRcT)GetProcAddress(hUxThemeDll, "GetThemeBackgroundContentRect");
			pIsAppThemed     = (IsAppThemedT)GetProcAddress(hUxThemeDll, "IsAppThemed");
			pIsThemeActive   = (IsThemeActiveT)GetProcAddress(hUxThemeDll, "IsThemeActive");

			if(pIsAppThemed && pIsThemeActive)
				bDrawWithTheme = pIsAppThemed() && pIsThemeActive();
		}
	}

	if(pBufferedPtInit) {
		pBufferedPtInit();
	}

}


//*****************************************************************************
//*
//*		GlobalDeinit
//*
//*****************************************************************************
static void GlobalDeinit() {

	int	lCount;

	lCount = InterlockedDecrement(&lWindowCount);
	if(lCount >= 0)
		return;

	if(hDefaultFontN) {
		DeleteObject(hDefaultFontN);
		hDefaultFontN = NULL;
	}

	if(hDefaultFontB) {
		DeleteObject(hDefaultFontB);
		hDefaultFontB = NULL;
	}

	if(hPatternPen) {
		DeleteObject(hPatternPen);
		hPatternPen = NULL;
	}

	if(pBufferedPtExit) {
		pBufferedPtExit();
	}

}


//*****************************************************************************
//*
//*		SendNotify
//*
//*****************************************************************************
//	Sendet eine WM_NOTIFY Nachricht and das Elternfenster
//	pData		: Zeiger auf die Fensterdaten
//	pNotify		: Zeiger auf die Notify-Daten
//	Ergibt den Rückgabewert der WM_NOTIFY Nachrich
static LRESULT SendNotify(TreeListData *pData, NMHDR	*pNotify) {

	pNotify->hwndFrom	= pData->hWnd;
	pNotify->idFrom		= GetWindowLong(pNotify->hwndFrom, GWL_ID);

	return SendMessage(GetParent(pNotify->hwndFrom), WM_NOTIFY, pNotify->idFrom, (LPARAM)pNotify);
}


//*****************************************************************************
//*
//*		CallbackEntry
//*
//*****************************************************************************
//	Sendet eine WM_NOTIFY Nachricht and das Elternfenster um Daten zuholen
//	pData		: Zeiger auf die Fensterdaten
//	pEntry		: Zeiger auf den Eintrag
//	uItem		: Nummer des Eintrages
//	uFlags		: Welche Daten sollen abgefragt werden
//	Ergibt den Rückgabewert der WM_NOTIFY Nachrich
static void CallbackEntry(TreeListData *pData, BaseItem *pEntry, unsigned uItem, unsigned uFlags, int *iImage, unsigned *uTextSize, LPCTSTR *pText) {

	NMTVDISPINFO	sInfo;

	sInfo.item.mask				= TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | uFlags;
	sInfo.item.lParam			= pEntry->lParam;
	sInfo.item.hItem			= (HTREEITEM)(ULONG_PTR)uItem;
	sInfo.item.state			= pEntry->uState;
	sInfo.item.stateMask		= 0xFFFFFFFF;
	sInfo.item.iImage			= I_IMAGECALLBACK;
	sInfo.item.iSelectedImage	= I_IMAGECALLBACK;
	sInfo.item.cChildren		= I_CHILDRENCALLBACK;

	if(uFlags & TVIF_TEXT) {
		if(*uTextSize) {
			pData->cTempText2[sizeof(pData->cTempText2) / sizeof(TCHAR) - 1]	= 0;
			pData->cTempText2[0]	= 0;
			sInfo.item.pszText		= pData->cTempText2;
			sInfo.item.cchTextMax	= sizeof(pData->cTempText2) / sizeof(TCHAR) - 1;
		} else {
			pData->cTempText1[sizeof(pData->cTempText1) / sizeof(TCHAR) - 1]	= 0;
			pData->cTempText1[0]	= 0;
			sInfo.item.pszText		= pData->cTempText1;
			sInfo.item.cchTextMax	= sizeof(pData->cTempText1) / sizeof(TCHAR) - 1;
		}
	} else {
		sInfo.item.pszText		= 0;
		sInfo.item.cchTextMax	= 0;
	}

	sInfo.hdr.hwndFrom	= pData->hWnd;
	sInfo.hdr.idFrom	= GetWindowLong(pData->hWnd, GWL_ID);
	sInfo.hdr.code		= TVN_GETDISPINFO;

	UNLOCK(pData);
	SendMessage(GetParent(sInfo.hdr.hwndFrom), WM_NOTIFY, sInfo.hdr.idFrom, (LPARAM)&sInfo);
	LOCK(pData);

	if(uFlags & TVIF_IMAGE) {
		if(!(pEntry->uState & TVIS_SELECTED))
			*iImage = sInfo.item.iImage;
	} else
		if(uFlags & TVIF_SELECTEDIMAGE) {
			if(pEntry->uState & TVIS_SELECTED)
				*iImage = sInfo.item.iSelectedImage;
		}

	if(uFlags & TVIF_CHILDREN) {
		switch(sInfo.item.cChildren) {
			case  0:
				pEntry->bFlags    &= ~TVIX_HASBUTTON;
				pEntry->bFlags    |=  TVIX_VARBUTTON;
				break;

			case  1:
				pEntry->bFlags    &= ~TVIX_VARBUTTON;
				pEntry->bFlags    |=  TVIX_HASBUTTON;
				break;

			default
					:
				pEntry->bFlags    |=  TVIX_VARBUTTON;

				if(pEntry->uFirstChild)
					pEntry->bFlags |=  TVIX_HASBUTTON;
				else
					pEntry->bFlags &= ~TVIX_HASBUTTON;
		}
	}

	if(uFlags & TVIF_TEXT) {
		*pText		= sInfo.item.pszText;
		*uTextSize	= str_len(sInfo.item.pszText);
		pEntry->iTextPixels = 0;
	}

}

//*****************************************************************************
//*
//*		CallbackExtra
//*
//*****************************************************************************
//	Sendet eine WM_NOTIFY Nachricht and das Elternfenster um Daten zuholen
//	pData		: Zeiger auf die Fensterdaten
//	pEntry		: Zeiger auf den Eintrag
//	uItem		: Nummer des Eintrages
//	uFlags		: Welche Daten sollen abgefragt werden
//	Ergibt den Rückgabewert der WM_NOTIFY Nachrich
static void CallbackExtra(TreeListData *pData, BaseItem *pEntry, ExtraItem *pExtra, unsigned uItem, unsigned uSub, unsigned uFlags, int *iImage, unsigned *uTextSize, LPCTSTR *pText) {

	NMTVDISPINFO	sInfo;

	sInfo.item.mask				= TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_SUBITEM | uFlags;
	sInfo.item.lParam			= pEntry->lParam;
	sInfo.item.hItem			= (HTREEITEM)(ULONG_PTR)uItem;
	sInfo.item.state			= pExtra->uState;
	sInfo.item.state		   |= (pEntry->uState & TVIS_BASEFLAGS);
	sInfo.item.stateMask		= 0xFFFFFFFF;
	sInfo.item.iImage			= I_IMAGECALLBACK;
	sInfo.item.iSelectedImage	= I_IMAGECALLBACK;
	sInfo.item.cChildren		= uSub;

	if(uFlags & TVIF_TEXT) {
		pData->cTempText1[sizeof(pData->cTempText1) / sizeof(TCHAR) - 1]	= 0;
		pData->cTempText1[0]	= 0;
		sInfo.item.pszText		= pData->cTempText1;
		sInfo.item.cchTextMax	= sizeof(pData->cTempText1) / sizeof(TCHAR) - 1;
	} else {
		sInfo.item.pszText		= 0;
		sInfo.item.cchTextMax	= 0;
	}

	sInfo.hdr.hwndFrom	= pData->hWnd;
	sInfo.hdr.idFrom	= GetWindowLong(pData->hWnd, GWL_ID);
	sInfo.hdr.code		= TVN_GETDISPINFO;

	UNLOCK(pData);
	SendMessage(GetParent(sInfo.hdr.hwndFrom), WM_NOTIFY, sInfo.hdr.idFrom, (LPARAM)&sInfo);
	LOCK(pData);


	if(uFlags & TVIF_IMAGE)
		*iImage = sInfo.item.iImage;
	if(uFlags & TVIF_TEXT) {
		*pText		= sInfo.item.pszText;
		*uTextSize	= str_len(sInfo.item.pszText);
	}

}

//*****************************************************************************
//*
//*		EditProc
//*
//*****************************************************************************
//	Ist die Fensterfunktion für das Edit Fenster
static LRESULT CALLBACK EditProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {

	TreeListData   *pData;
	WNDPROC			pProc;
	DWORD			dwStop;
	DWORD			dwStart;
	DWORD			dwCount;
	LRESULT			lResult;
	HWND			hParent;
	HWND			hCombo;
	int				iDelta;
	int				iState;
	int				iPos;
	int				iId;


	hParent = GetParent(hWnd);
	iId		= GetWindowLong(hWnd, GWL_ID);

	if(iId == 3) {
		hCombo  = hWnd;
		pData   = GetHandle(hParent);
		pProc   = pData->pProcId3;
	} else {
		hCombo  = hParent;
		hParent = GetParent(hParent);
		pData   = GetHandle(hParent);
		pProc   = (WNDPROC)GetWindowLongPtr(hWnd, GWLP_USERDATA);
	}

	if(uMsg == WM_KEYDOWN) {
		if(wParam == VK_RETURN) {
			SendMessage(hParent, WM_COMMAND, MAKELONG(3, EN_RETURN), (LPARAM)hWnd);
			return 0;
		}

		if(wParam == VK_ESCAPE) {
			SendMessage(hParent, WM_COMMAND, MAKELONG(3, EN_ESCAPE), (LPARAM)hWnd);
			return 0;
		}

		if((pData->uStyleEx & TVS_EX_STEPOUT) && !(GetAsyncKeyState(VK_SHIFT) & 0x8000)) {
			switch(wParam) {										// Aus Fenster springen

				case VK_UP:
					if(pData->uEditMode)
						break;
					PostMessage(hParent, WM_COMMAND, MAKELONG(3, EN_RETURN), (LPARAM)hWnd);
					PostMessage(hParent, WM_KEYDOWN, VK_UP  , 0x00500001);
					PostMessage(hParent, WM_KEYUP  , VK_UP  , 0x00500001);
					return 0;

				case VK_DOWN:
					if(pData->uEditMode)
						break;
					PostMessage(hParent, WM_COMMAND, MAKELONG(3, EN_RETURN), (LPARAM)hWnd);
					PostMessage(hParent, WM_KEYDOWN, VK_DOWN, 0x00500001);
					PostMessage(hParent, WM_KEYUP  , VK_DOWN, 0x00500001);
					return 0;

				case VK_LEFT:
					if(pData->uEditMode && iId == 3)
						break;
					SendMessage(hWnd, EM_GETSEL, (WPARAM)&dwStart, (LPARAM)&dwStop);
					if(dwStart || dwStop)
						break;
					PostMessage(hParent, WM_COMMAND, MAKELONG(3, EN_RETURN), (LPARAM)hWnd);
					PostMessage(hParent, WM_KEYDOWN, VK_LEFT, 0x00500001);
					PostMessage(hParent, WM_KEYUP  , VK_LEFT, 0x00500001);
					return 0;

				case VK_RIGHT:
					if(pData->uEditMode && iId == 3)
						break;
					SendMessage(hWnd, EM_GETSEL, (WPARAM)&dwStart, (LPARAM)&dwStop);
					dwCount = (DWORD)SendMessage(hWnd, EM_LINELENGTH, 0, 0);
					if(dwCount > dwStart)
						break;
					if(dwCount > dwStop)
						break;

					PostMessage(hParent, WM_COMMAND, MAKELONG(3, EN_RETURN), (LPARAM)hWnd);
					PostMessage(hParent, WM_KEYDOWN, VK_RIGHT, 0x00500001);
					PostMessage(hParent, WM_KEYUP  , VK_RIGHT, 0x00500001);
					return 0;
			}
		}

		if(wParam == VK_DOWN && pData->uEditMode) {
			if(!SendMessage(hCombo, CB_GETDROPPEDSTATE, 0, 0)) {
				SendMessage(hCombo, CB_SHOWDROPDOWN, 1, 0);
				return 0;
			}
		}
	} else
		if(uMsg == WM_CHAR) {
			if(wParam == VK_RETURN) {
				return 0;
			}

			if(wParam == VK_ESCAPE) {
				return 0;
			}
		} else
			if(uMsg == WM_COMMAND) {
				if(wParam == MAKELONG(3, EN_ESCAPE) || wParam == MAKELONG(3, EN_RETURN)) {
					SendMessage(hParent, WM_COMMAND, wParam, (LPARAM)hWnd);
					return 0;
				}
			} else
				if(uMsg == WM_MOUSEWHEEL) {
					iState = (int)CallWindowProc(pProc, hWnd, CB_GETDROPPEDSTATE, 0, 0);
					if(iState) {
						iDelta  = (short)HIWORD(wParam);
						iDelta /= WHEEL_DELTA;
						iPos	= (int)CallWindowProc(pProc, hWnd, CB_GETTOPINDEX, 0, 0);
						iPos   -= iDelta;
						CallWindowProc(pProc, hWnd, CB_SETTOPINDEX, iPos, 0);
						return 0;
					}
				} else
					if(uMsg == WM_GETDLGCODE) {								// Welche Tasten werden im Dialog benutzt
						return DLGC_WANTALLKEYS;
					} else
						if(uMsg == WM_SETTEXT) {
							lResult = CallWindowProc(pProc, hWnd, uMsg, wParam, lParam);;
							SendMessage(hParent, WM_COMMAND, MAKELONG(3, EN_SETTEXT), (LPARAM)hWnd);
							return lResult;
						}

	return CallWindowProc(pProc, hWnd, uMsg, wParam, lParam);
}

//*****************************************************************************
//*
//*		ToolProc
//*
//*****************************************************************************
//	Ist die Fensterfunktion für das ToolTip Fenster
static LRESULT CALLBACK ToolProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {

	TreeListData   *pData;
	POINT			sPoint;
	UINT			uPos;

	if(uMsg == WM_SETFOCUS) {
		SetFocus((HWND)lParam);
		return 0;
	}

	pData = (TreeListData *)GetWindowLongPtr(hWnd, GWLP_USERDATA);

	if(uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST) {				// Mausklicks auf Tooltip zum Elternfenster
		sPoint.x = LOWORD(lParam);
		sPoint.y = HIWORD(lParam);

		ClientToScreen(hWnd       , &sPoint);
		ScreenToClient(pData->hWnd, &sPoint);

		uPos = MAKELONG(sPoint.x, sPoint.y);

		return TreeListProc(pData->hWnd, uMsg, wParam, uPos);
	}

	return CallWindowProc(pData->pToolProc, hWnd, uMsg, wParam, lParam);
}

//*****************************************************************************
//*
//*		ChangeColSize
//*
//*****************************************************************************
//	Ändert die Größe der variablen Spalten
//	pData		: Zeiger auf die Fensterdaten
//	iDelta		: Ist die Größenänderung in Pixel
static void ChangeColSize(TreeListData *pData, int iDelta) {

	unsigned 	uPos;
	HDITEM	 	sItem;
	RECT		sRect;
	TV_COLSIZE	sNotify;
	int			iWeight;
	int			iValue;
	int			iPoint;
	int			iStart;
	int			iSize;
	int			iRest;
	int			iOff;
	int			iNum;
	int			iCnt;
	int			iAll;
	int			iVar;
	int			iFix;

	sItem.mask	= HDI_WIDTH;
	iAll		= pData->iAllWeight;
	iCnt		= pData->uColumnCountVar;

	if(iCnt <= 1) {											// Nur eine variable Spalte
		for(uPos = 0; uPos < pData->uColumnCount; uPos++) {
			iWeight = pData->aColumn[uPos].bWeight;
			if(!iWeight)
				continue;

			iValue    =	pData->aColumn[uPos].sSize;
			iValue   += iDelta;
			sItem.cxy = iValue;

			pData->aColumn[uPos].sSize = (short)iValue;
			pData->iVarSize            =        iValue;

			if(sItem.cxy < pData->aColumn[uPos].sMin) {
				sItem.cxy = pData->aColumn[uPos].sMin;
			}

			if(pData->aColumn[uPos].sReal != sItem.cxy) {	// Ändert sich die Breite
				pData->aColumn[uPos].sReal	= (short)sItem.cxy;
				Header_SetItem(pData->hHeader, uPos, &sItem);

				if(pData->uStyleEx & TVS_EX_HEADERCHGNOTIFY) {
					sNotify.hdr.code			= TVN_COLUMNCHANGED;
					sNotify.uColumn				= uPos;
					sNotify.uIndex				= pData->aColumn[uPos].bIndex;
					sNotify.uPosX				= pData->aColumnXpos[uPos];
					sNotify.iSize				= sItem.cxy;

					UNLOCK(pData);
					SendNotify(pData, &sNotify.hdr);
					LOCK(pData);
				}
			}

			break;
		}

		return;
	}

	if(iDelta > 0)
		iStart = (pData->uSizeX) % iAll;
	else
		iStart = (pData->uSizeX - iDelta) % iAll;

	iOff   = 0;

	for(uPos = 0;; uPos++) {									// Suchen die Anfangsspalte
		iWeight = pData->aColumn[uPos].bWeight;
		if(!iWeight)
			continue;

		iOff += iWeight;
		if(iOff > iStart)
			break;
	}


	iPoint		= 0;
	iSize		= iDelta / iAll;
	iRest		= iDelta % iAll;
	iNum		= iRest;
	iOff	   -= iStart;

	iWeight		= iOff;
	iValue		= pData->aColumn[uPos].sSize;
	iValue	   += iSize * iWeight;
	iPoint	   += iRest * iWeight;
	iValue	   += iPoint / iAll;
	iNum	   -= iPoint / iAll;
	iPoint     %= iAll;

	pData->aColumn[uPos].sSize = (short)iValue;


	if(iWeight >= pData->aColumn[uPos].bWeight) {			// Wurde die ganze Spalte berechnet
		iCnt--;
		iOff = 0;
	}

	while(iCnt > 0) {
		uPos++;

		if(uPos >= pData->uColumnCount)
			uPos = 0;
		iWeight = pData->aColumn[uPos].bWeight;
		if(!iWeight)
			continue;

		iValue		= pData->aColumn[uPos].sSize;

		iCnt--;
		if(iCnt) {
			iValue += iSize * iWeight;
			iPoint += iRest * iWeight;
			iValue += iPoint / iAll;
			iNum   -= iPoint / iAll;
			iPoint %= iAll;
		} else {
			iWeight -= iOff;
			iValue += iSize * iWeight;
			iValue += iNum;
		}

		pData->aColumn[uPos].sSize = (short)iValue;
	}

	iVar   =  0;
	iFix   =  0;
	iCnt   =  pData->uColumnCountVar;

	for(uPos = 0; iCnt > 0; uPos++) {							// Ausgeben der neuen Breiten
		iWeight = pData->aColumn[uPos].bWeight;
		if(!iWeight) {
			iFix     +=	pData->aColumn[uPos].sSize;
			continue;
		}

		iVar     +=	pData->aColumn[uPos].sSize;
		sItem.cxy = pData->aColumn[uPos].sSize;

		if(sItem.cxy < pData->aColumn[uPos].sMin) {
			sItem.cxy = pData->aColumn[uPos].sMin;
		}

		if(pData->aColumn[uPos].sReal != sItem.cxy) {		// Ändert sich die Breite
			pData->aColumn[uPos].sReal  = (short)sItem.cxy;
			Header_SetItem(pData->hHeader, uPos, &sItem);

			if(pData->uStyleEx & TVS_EX_HEADERCHGNOTIFY) {
				sNotify.hdr.code			= TVN_COLUMNCHANGED;
				sNotify.uColumn				= uPos;
				sNotify.uIndex				= pData->aColumn[uPos].bIndex;
				sNotify.uPosX				= pData->aColumnXpos[uPos];
				sNotify.iSize				= sItem.cxy;

				UNLOCK(pData);
				SendNotify(pData, &sNotify.hdr);
				LOCK(pData);
			}
		}

		iCnt--;
	}

	pData->iFixSize = iFix;
	pData->iVarSize = iVar;

	if(iDelta > 0) {
		GetClientRect(pData->hHeader, &sRect);
		InvalidateRect(pData->hHeader, NULL, FALSE);
	}

}

//*****************************************************************************
//*
//*		CreateToolTip
//*
//*****************************************************************************
//	Erzeugt ein ToolTip Fenster
//	pData		: Zeiger auf die Fensterdaten
static void CreateToolTip(TreeListData *pData) {

	TOOLINFO	sInfo;

	if(pData->hToolTip)
		return;

	pData->hToolTip	   = CreateWindow(TOOLTIPS_CLASS, NULL, WS_POPUP, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, pData->hWnd, NULL, NULL, NULL);
	pData->pToolProc   = (WNDPROC)GetWindowLongPtr(pData->hToolTip, GWLP_WNDPROC);

	sInfo.cbSize       = sizeof(TOOLINFO);
	sInfo.uFlags       = TTF_ABSOLUTE | TTF_TRACK | TTF_IDISHWND;
	sInfo.hwnd         = pData->hWnd;
	sInfo.hinst		   = NULL;
	sInfo.uId          = (LPARAM)(pData->hWnd);
	sInfo.lpszText     = LPSTR_TEXTCALLBACK;

	GetClientRect(pData->hWnd, &sInfo.rect);
	SendMessage(pData->hToolTip, TTM_ADDTOOL, 0, (LPARAM)&sInfo);
	SendMessage(pData->hToolTip, TTM_SETMAXTIPWIDTH, 0, 10000);
	SetWindowLong(pData->hToolTip, GWL_ID, 2);
	SetWindowLongPtr(pData->hToolTip, GWLP_USERDATA, (LPARAM)pData);
	SetWindowLongPtr(pData->hToolTip, GWLP_WNDPROC , (LPARAM)ToolProc);
}

//*****************************************************************************
//*
//*		CreateStateImageList
//*
//*****************************************************************************
//	Erzeugt eine Image-Liste mit zwei Checkboxen
//	pData		: Zeiger auf die Fensterdaten
//	iMode		: Welche Imageliste soll erzeugt werden (0=hStates 1=hChecks)
static void CreateStateImageList(TreeListData *pData, int iMode) {

	BITMAPINFO	sInfo;
	BYTE		aMem[0x1000];
	HDC			hDcSrc;
	HDC			hDc;
	HBITMAP		hBmp;
	HBITMAP		hBmpNew;
	RECT		sRect;
	int			iBits;

	if(pOpenThemeData) {									// Über Thema zeichnen
		if(!pData->hThemeBt) {
			pData->hThemeBt = pOpenThemeData(pData->hWnd, L"BUTTON");
		}

		if(pData->hThemeBt) {
			if(iMode) {
				if(pData->hChecks && pData->hChecks != THEMEIMGLIST) {
					ImageList_Destroy(pData->hChecks);
				}

				pData->hChecks		= THEMEIMGLIST;
				pData->iChecksXsize	= 16;
				pData->iChecksYsize	= 16;
				pData->iChecksMode	= 1;
			} else {
				if(pData->hStates && pData->hStates != THEMEIMGLIST) {
					ImageList_Destroy(pData->hStates);
				}

				pData->hStates		= THEMEIMGLIST;
				pData->iStatesXsize	= 16;
				pData->iStatesYsize	= 16;
				pData->iStatesMode	= 1;
			}

			return;
		}
	}

	if(iMode) {
		if(pData->hChecks && pData->hChecks != THEMEIMGLIST)
			return;
	} else {
		if(pData->hStates && pData->hStates != THEMEIMGLIST)
			return;
	}

	hDcSrc	= GetDC(NULL);
	hDc		= CreateCompatibleDC(NULL);
	hBmp    = CreateCompatibleBitmap(hDcSrc, 16 * 3, 16);

	SelectObject(hDc, hBmp);
	SelectObject(hDc, GetStockObject(NULL_PEN));
	SetBkMode(hDc, OPAQUE);
	SetBkColor(hDc, GetSysColor(COLOR_WINDOW));
	SelectObject(hDc, GetSysColorBrush(COLOR_HIGHLIGHT));
	Rectangle(hDc, -1, -1, 16 * 3 + 2, 16 + 2);

	sRect.top	  = 8 - 6;
	sRect.bottom  = 8 + 7;
	sRect.left    = 16 * 1 + 8 - 7;
	sRect.right   = 16 * 1 + 8 + 6;

	DrawFrameControl(hDc, &sRect, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_FLAT);

	sRect.left    = 16 * 2 + 8 - 7;
	sRect.right   = 16 * 2 + 8 + 6;

	DrawFrameControl(hDc, &sRect, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_CHECKED | DFCS_FLAT);

	iBits = GetDeviceCaps(hDc, BITSPIXEL);

	sInfo.bmiHeader.biSize		  	= sizeof(BITMAPINFOHEADER);
	sInfo.bmiHeader.biWidth			= 16 * 3;
	sInfo.bmiHeader.biHeight		= 16;
	sInfo.bmiHeader.biPlanes	  	= 1;
	sInfo.bmiHeader.biBitCount	  	= (WORD)iBits;
	sInfo.bmiHeader.biCompression 	= BI_RGB;
	sInfo.bmiHeader.biSizeImage		= 0;
	sInfo.bmiHeader.biXPelsPerMeter	= 0;
	sInfo.bmiHeader.biYPelsPerMeter	= 0;
	sInfo.bmiHeader.biClrUsed		= (iBits > 8) ? 0 : 1 << iBits;;
	sInfo.bmiHeader.biClrImportant	= (iBits > 8) ? 0 : 1 << iBits;;

	GetDIBits(hDc, hBmp, 0, 0 , NULL, &sInfo, (iBits > 8) ? DIB_RGB_COLORS : DIB_PAL_COLORS);
	GetDIBits(hDc, hBmp, 0, 16, aMem, &sInfo, (iBits > 8) ? DIB_RGB_COLORS : DIB_PAL_COLORS);

	hBmpNew = CreateCompatibleBitmap(hDc, 16 * 3, 16);

	SetDIBits(hDc, hBmpNew, 0, 16, aMem, &sInfo, (iBits > 8) ? DIB_RGB_COLORS : DIB_PAL_COLORS);

	if(iMode == 0) {
		pData->hStates		= ImageList_Create(16, 16, ILC_COLORDDB | ILC_MASK, 3, 14);
		pData->iStatesXsize	= 16;
		pData->iStatesYsize	= 16;
		pData->iStatesMode	= 1;

		ImageList_AddMasked(pData->hStates, hBmpNew, GetSysColor(COLOR_HIGHLIGHT));
	} else {
		pData->hChecks		= ImageList_Create(16, 16, ILC_COLORDDB | ILC_MASK, 3, 14);
		pData->iChecksXsize	= 16;
		pData->iChecksYsize	= 16;
		pData->iChecksMode	= 1;

		ImageList_AddMasked(pData->hChecks, hBmpNew, GetSysColor(COLOR_HIGHLIGHT));
	}

	DeleteObject(hBmpNew);
	DeleteObject(hBmp);
	DeleteDC(hDc);
	ReleaseDC(NULL, hDcSrc);
}

//*****************************************************************************
//*
//*		CreateDragImage
//*
//*****************************************************************************
//	Erzeugt eine Image-Liste mit zwei Checkboxen
//	pData		: Zeiger auf die Fensterdaten
//	uSub		: Ist die Spalte für die das Drag-Image erzeugt werden soll
//	Ergibt ein Handle mit der Imageliste oder NULL bei einem Fehler
static HIMAGELIST CreateDragImage(TreeListData *pData, unsigned uItem, unsigned uSub) {

	ExtraItem  *pExtra;
	BaseItem   *pEntry;
	HIMAGELIST	hList;
	BITMAPINFO	sInfo;
	BYTE	   *pMem;
	HDC			hDcSrc;
	HDC			hDc;
	HBITMAP		hBmp;
	HBITMAP		hBmpNew;
	RECT		sRect;
	unsigned	uTSize;
	int			iAdd;
	int			iBits;
	int			iWidth;
	int			iHeigh;
	int			iImage;
	int			iYpos;
	LPCTSTR		pText;

	if(uItem > pData->uTreeItemsMax)
		return NULL;

	pEntry = pData->pTreeItems[uItem];
	if(!pEntry)
		return 0;

	iHeigh = pData->iFontHeight;

	if(uSub) {												// Image für Extraeintrag erzeugen
		if(uSub >= pData->uColumnCount)
			return 0;

		pExtra = pData->pExtraItems[uSub - 1][uItem];
		if(!pExtra) {
			pText  = _T("????");
			uTSize = 4;
			iImage = -1;
			iWidth = pData->iFontHeight * 4;
		} else {
			pText  = pExtra->pText;
			uTSize = pExtra->uTextSize;
			iImage = pExtra->iImage;
			iWidth = pExtra->iTextPixels;

			if(pExtra->bCallback & (TVIF_IMAGE | TVIF_TEXT)) {
				CallbackExtra(pData, pEntry, pExtra, uItem, uSub, pExtra->bCallback, &iImage, &uTSize, &pText);
			}
		}
	} else {													// Image für Haupteintrag erzeugen
		pText  = pEntry->pText;
		uTSize = pEntry->uTextSize;
		iImage = pEntry->iImage;
		iWidth = pEntry->iTextPixels;

		if(pEntry->bCallback & (TVIF_IMAGE | TVIF_TEXT)) {
			CallbackEntry(pData, pEntry, uItem, pEntry->bCallback, &iImage, &uTSize, &pText);
		}
	}

	if(pData->hImages && iImage >= 0) {							// Größen für Images anpassen
		if(iHeigh < pData->iImagesYsize)
			iHeigh = pData->iImagesYsize;
		iAdd    = pData->iImagesXsize + 2;
		iWidth += iAdd;
	} else {
		iAdd   = 0;
		iImage = 1;
	}

	if(iWidth > 240)
		iWidth = 240;
	if(iHeigh > 32)
		iHeigh = 32;

	pMem = new(BYTE, iHeigh * (iWidth + 4) * 4 + 1024);
	if(!pMem)
		return NULL;

	hDcSrc	= GetDC(NULL);
	hDc		= CreateCompatibleDC(NULL);
	hBmp    = CreateCompatibleBitmap(hDcSrc, iWidth, iHeigh);

	SelectObject(hDc, hBmp);
	SelectObject(hDc, GetStockObject(NULL_PEN));
	SelectObject(hDc, (pEntry->uState & TVIS_BOLD) ? pData->hFontB : pData->hFontN);
	SetTextColor(hDc, pData->uColors[TVC_TEXT]);
	SetBkColor(hDc, RGB(123, 77, 91));

	sRect.top	  = 0;
	sRect.bottom  = iHeigh;
	sRect.left    = 0;
	sRect.right   = iWidth;
	iYpos		  = (iHeigh - pData->iFontHeight) / 2;

	ExtTextOut(hDc, iAdd, iYpos, ETO_OPAQUE | ETO_CLIPPED, &sRect, pText, uTSize, NULL);

	if(iImage >= 0) {
		SetBkColor(hDc, GetSysColor(COLOR_WINDOW));
		ImageList_Draw(pData->hImages, iImage, hDc, 0, 0, ILD_TRANSPARENT);
	}

	iBits = GetDeviceCaps(hDc, BITSPIXEL);

	sInfo.bmiHeader.biSize		  	= sizeof(BITMAPINFOHEADER);
	sInfo.bmiHeader.biWidth			= iWidth;
	sInfo.bmiHeader.biHeight		= iHeigh;
	sInfo.bmiHeader.biPlanes	  	= 1;
	sInfo.bmiHeader.biBitCount	  	= (WORD)iBits;
	sInfo.bmiHeader.biCompression 	= BI_RGB;
	sInfo.bmiHeader.biSizeImage		= 0;
	sInfo.bmiHeader.biXPelsPerMeter	= 0;
	sInfo.bmiHeader.biYPelsPerMeter	= 0;
	sInfo.bmiHeader.biClrUsed		= (iBits > 8) ? 0 : 1 << iBits;;
	sInfo.bmiHeader.biClrImportant	= (iBits > 8) ? 0 : 1 << iBits;;

	GetDIBits(hDc, hBmp, 0, 0     , NULL, &sInfo, (iBits > 8) ? DIB_RGB_COLORS : DIB_PAL_COLORS);
	GetDIBits(hDc, hBmp, 0, iHeigh, pMem, &sInfo, (iBits > 8) ? DIB_RGB_COLORS : DIB_PAL_COLORS);

	hBmpNew = CreateCompatibleBitmap(hDc, iWidth, iHeigh);

	SetDIBits(hDc, hBmpNew, 0, iHeigh, pMem, &sInfo, (iBits > 8) ? DIB_RGB_COLORS : DIB_PAL_COLORS);

	hList = ImageList_Create(iWidth, iHeigh, ILC_COLORDDB | ILC_MASK, 1, 0);

	ImageList_AddMasked(hList, hBmpNew, RGB(123, 77, 91));

	DeleteObject(hBmpNew);
	DeleteObject(hBmp);
	DeleteDC(hDc);
	ReleaseDC(NULL, hDcSrc);

	delete(pMem);

	return hList;
}


//*****************************************************************************
//*
//*		UpdateColorsList
//*
//*****************************************************************************
//	Aktualisiert alle Farben
//	pData		: Zeiger auf die Fensterdaten
static void UpdateColorsList(TreeListData *pData) {

	unsigned	uColOdd;
	unsigned 	uColor;
	int 		iDiff;
	int 		iSum;

	if(!pData->cColorChanged[TVC_BK     ])
		pData->uColors[TVC_BK     ]  = GetSysColor(COLOR_WINDOW);
	if(!pData->cColorChanged[TVC_BOX    ])
		pData->uColors[TVC_BOX    ]  = GetSysColor(COLOR_BTNSHADOW);
	if(!pData->cColorChanged[TVC_EVEN   ])
		pData->uColors[TVC_EVEN   ]  = GetSysColor(COLOR_WINDOW);
	if(!pData->cColorChanged[TVC_TEXT   ])
		pData->uColors[TVC_TEXT   ]  = GetSysColor(COLOR_WINDOWTEXT);
	if(!pData->cColorChanged[TVC_LINE   ])
		pData->uColors[TVC_LINE   ]  = GetSysColor(COLOR_WINDOWTEXT);
	if(!pData->cColorChanged[TVC_FRAME  ])
		pData->uColors[TVC_FRAME  ]  = GetSysColor(COLOR_3DFACE);
	if(!pData->cColorChanged[TVC_TRACK  ])
		pData->uColors[TVC_TRACK  ]  = GetSysColor(COLOR_WINDOWTEXT) ^ RGB(0, 0, 255);
	if(!pData->cColorChanged[TVC_INSERT ])
		pData->uColors[TVC_INSERT ]  = GetSysColor(COLOR_INFOBK);
	if(!pData->cColorChanged[TVC_ODD    ])
		pData->uColors[TVC_ODD    ]  = GetSysColor(COLOR_INFOBK);
	if(!pData->cColorChanged[TVC_BOXBG  ])
		pData->uColors[TVC_BOXBG  ]  = GetSysColor(COLOR_WINDOW);
	if(!pData->cColorChanged[TVC_COLBK  ])
		pData->uColors[TVC_COLBK  ]  = GetSysColor(COLOR_WINDOW);
	if(!pData->cColorChanged[TVC_COLODD ])
		pData->uColors[TVC_COLODD ]  = GetSysColor(COLOR_BTNSHADOW);
	if(!pData->cColorChanged[TVC_COLEVEN])
		pData->uColors[TVC_COLEVEN]  = GetSysColor(COLOR_WINDOW);
	if(!pData->cColorChanged[TVC_GRAYED ])
		pData->uColors[TVC_GRAYED ]  = GetSysColor(COLOR_SCROLLBAR);


	if(pData->hTheme && !pData->cColorChanged[TVC_BOXBG] && !pData->cColorChanged[TVC_BOX] && !pData->cColorChanged[TVC_LINE]) {
		pData->cGlyphOk = 1;
	} else {
		pData->cGlyphOk = 0;
	}


	if(!pData->cColorChanged[TVC_GRAYED]) {
		pData->uColors[TVC_GRAYED]  = (GetSysColor(COLOR_SCROLLBAR) & 0x00FEFEFE) >> 1;
		pData->uColors[TVC_GRAYED] += (GetSysColor(COLOR_WINDOW) & 0x00FEFEFE) >> 1;
	}

	if(!pData->cColorChanged[TVC_ODD]) {
		uColOdd = pData->uColors[TVC_ODD];
		iDiff   = ((uColOdd) & 0xFF) - ((pData->uColors[TVC_EVEN]) & 0xFF);
		iSum  = iDiff * iDiff;
		iDiff   = ((uColOdd >> 8) & 0xFF) - ((pData->uColors[TVC_EVEN] >> 8) & 0xFF);
		iSum += iDiff * iDiff;
		iDiff   = ((uColOdd >> 16) & 0xFF) - ((pData->uColors[TVC_EVEN] >> 16) & 0xFF);
		iSum += iDiff * iDiff;

		if(iSum < 64) {												// Ist die alternierente Farbe fast gleich ?
			uColOdd  =   pData->uColors[TVC_EVEN] & 0x0000FFFF;
			uColOdd |= ((pData->uColors[TVC_EVEN] & 0x00FF0000) - 0x00080000) & 0x00FF0000;
		}

		pData->uColors[TVC_ODD] = uColOdd;
	}

	if(!pData->cColorChanged[TVC_COLBK]) {
		uColor = GetSysColor(COLOR_WINDOW);
		if(uColor & 0x00F00000)
			uColor -= 0x00100000;
		if(uColor & 0x0000F000)
			uColor -= 0x00001000;
		if(uColor & 0x000000F0)
			uColor -= 0x00000010;

		pData->uColors[TVC_COLBK] = uColor;
	}

	if(!pData->cColorChanged[TVC_COLODD]) {
		uColor = pData->uColors[TVC_ODD];
		if(uColor & 0x00F00000)
			uColor -= 0x00100000;
		if(uColor & 0x0000F000)
			uColor -= 0x00001000;
		if(uColor & 0x000000F0)
			uColor -= 0x00000010;

		pData->uColors[TVC_COLODD] = uColor;
	}

	if(!pData->cColorChanged[TVC_COLEVEN]) {
		uColor = GetSysColor(COLOR_WINDOW);
		if(uColor & 0x00F00000)
			uColor -= 0x00100000;
		if(uColor & 0x0000F000)
			uColor -= 0x00001000;
		if(uColor & 0x000000F0)
			uColor -= 0x00000010;

		pData->uColors[TVC_COLEVEN] = uColor;
	}

	if(!pData->cColorChanged[TVC_MARKODD ])
		pData->uColors[TVC_MARKODD ]  = ((pData->uColors[TVC_ODD     ] >> 3) & 0x1F1F1F) * 7;
	if(!pData->cColorChanged[TVC_MARKODD ])
		pData->uColors[TVC_MARKODD ] += ((GetSysColor(COLOR_HIGHLIGHT) >> 3) & 0x1F1F1F) * 1;
	if(!pData->cColorChanged[TVC_MARKEVEN])
		pData->uColors[TVC_MARKEVEN]  = ((pData->uColors[TVC_EVEN    ] >> 3) & 0x1F1F1F) * 7;
	if(!pData->cColorChanged[TVC_MARKEVEN])
		pData->uColors[TVC_MARKEVEN] += ((GetSysColor(COLOR_HIGHLIGHT) >> 3) & 0x1F1F1F) * 1;
}

//*****************************************************************************
//*
//*		UpdateHeight
//*
//*****************************************************************************
//	Checks if the row height has changed
//	pData		: pointer to the window data
//	Returns 1 if changed or 0 else.
static int UpdateHeight(TreeListData *pData) {

	int		iHeight;
	RECT	sRect;

	if(pData->cFixedHeight)
		return 0;

	iHeight = 10;

	if(pData->hChecks)
		if(iHeight < pData->iChecksYsize + 2)
			iHeight = pData->iChecksYsize + 2;
	if(pData->hStates)
		if(iHeight < pData->iStatesYsize + 2)
			iHeight = pData->iStatesYsize + 2;
	if(iHeight < pData->iSubImgYsize + 2)
		iHeight = pData->iSubImgYsize + 2;
	if(iHeight < pData->iImagesYsize + 2)
		iHeight = pData->iImagesYsize + 2;
	if(iHeight < pData->iFontHeight + 2)
		iHeight = pData->iFontHeight + 2;
	if(pData->uStyleEx & TVS_EX_ITEMLINES)
		iHeight++;
	if(pData->uStyle  & TVS_NONEVENHEIGHT && (iHeight & 1))
		iHeight++;
	if(pData->iRowHeight == iHeight)
		return 0;

	pData->iRowHeight	 = iHeight;

	if(pData->uSizeY > pData->uStartPixel) {
		pData->uMaxEnties  = pData->uSizeY;
		pData->uMaxEnties -= pData->uStartPixel;
	} else {
		pData->uMaxEnties  = 0;
	}

	pData->uPageEnties	 = pData->uMaxEnties;
	pData->uMaxEnties	+= pData->iRowHeight - 1;
	pData->uMaxEnties   /= pData->iRowHeight;
	pData->uPageEnties  /= pData->iRowHeight;

	GetClientRect(pData->hWnd, &sRect);
	InvalidateRect(pData->hWnd, &sRect, FALSE);

	return 1;
}

//*****************************************************************************
//*
//*		UpdateRect
//*
//*****************************************************************************
//	Zeichnet einen Eintrag neu
//	pData		: Zeiger auf die Fensterdaten
//	uItem		: Ist die Nummer des Eintrages
//	uSub		: Ist die Spaltennummer
//	Ergibt 1 wenn der Eintrag sichtbar war
static int UpdateRect(TreeListData *pData, unsigned uItem, unsigned uSub) {

	BaseItem   *pEntry;
	RECT		sRect;
	UINT		uNext;
	UINT		uPos;

	pEntry = pData->pTreeItems[uItem];
	if(!pEntry || !pEntry->uShowPos)
		return 0;				// Ist der Eintrag aufgeklappt

	uPos = pEntry->uShowPos - pData->uScrollY - 1;
	if(uPos >= pData->uMaxEnties)
		return 0;					// Eintrag im Fenster sichtbar

	uNext		  = pData->aColumn[uSub].bNext;
	sRect.left    = pData->aColumnXpos[uSub ];
	sRect.left   -= pData->uScrollX;
	sRect.right	  = pData->aColumnXpos[uNext];
	sRect.right  -= pData->uScrollX;
	sRect.top     = pData->uStartPixel;
	sRect.top    += pData->iRowHeight * uPos;
	sRect.bottom  = pData->iRowHeight + sRect.top;

	InvalidateRect(pData->hWnd, &sRect, FALSE);

	return 1;
}

//*****************************************************************************
//*
//*		UpdateColRect
//*
//*****************************************************************************
//	Zeichnet einen ganze Spalte neu
//	pData		: Zeiger auf die Fensterdaten
//	uColumn		: Die nummer der Spalte
//	Ergibt 1 wenn der Eintrag sichtbar war
static int UpdateColRect(TreeListData *pData, unsigned uColumn) {

	RECT	sRect;
	UINT	uNext;

	if(uColumn >= pData->uColumnCount)
		return 0;

	sRect.left  = pData->aColumnXpos[uColumn];
	sRect.left -= pData->uScrollX;
	if(sRect.left > (int)pData->uSizeX)
		return 0;

	uNext = pData->aColumn[uColumn].bNext;

	sRect.right = pData->aColumnXpos[uNext];
	sRect.right -= pData->uScrollX;
	if(sRect.right < 0)
		return 0;

	sRect.top      = 0;
	sRect.bottom   = pData->uSizeY;

	InvalidateRect(pData->hWnd, &sRect, FALSE);

	return 1;
}

//*****************************************************************************
//*
//*		UpdateRow
//*
//*****************************************************************************
//	Zeichnet einen Eintrag über die ganze Zeile neu
//	pData		: Zeiger auf die Fensterdaten
//	uItem		: Ist die Nummer des Eintrages
//	Ergibt 1 wenn der Eintrag sichtbar war
static int UpdateRow(TreeListData *pData, unsigned uItem) {

	BaseItem   *pEntry;
	RECT		sRect;
	unsigned	uPos;

	pEntry = pData->pTreeItems[uItem];
	if(!pEntry || !pEntry->uShowPos)
		return 0;				// Ist der Eintrag aufgeklappt

	uPos = pEntry->uShowPos - pData->uScrollY - 1;
	if(uPos >= pData->uMaxEnties)
		return 0;					// Eintrag im Fenster sichtbar

	sRect.left    = 0;
	sRect.right   = pData->uSizeX;
	sRect.top     = pData->uStartPixel;
	sRect.top    += pData->iRowHeight * uPos;
	sRect.bottom  = pData->iRowHeight + sRect.top;
	InvalidateRect(pData->hWnd, &sRect, FALSE);

	return 1;
}

//*****************************************************************************
//*
//*		UpdateView
//*
//*****************************************************************************
//	Redraw the whole window
//	pData		: Zeiger auf die Fensterdaten
//	Ergibt 1 wenn der Eintrag sichtbar war
static void UpdateView(TreeListData *pData) {

	RECT	sRect;

	GetClientRect(pData->hWnd, &sRect);
	sRect.top =    pData->uStartPixel;
	InvalidateRect(pData->hWnd, &sRect, FALSE);

	if(pData->hHeader && ((pData->uStyleEx & TVS_EX_HIDEHEADERS) == 0)){
		GetClientRect(pData->hHeader, &sRect);
		InvalidateRect(pData->hHeader, &sRect, FALSE);
	}
}

//*****************************************************************************
//*
//*		UpdateScrollX
//*
//*****************************************************************************
//	Aktualisiert die X-Scroolbar
//	pData		: Zeiger auf die Fensterdaten
//	Ergibt 1 wenn der sich Einstellungen verändert haben
static void UpdateScrollX(TreeListData *pData) {

	SCROLLINFO  sInfo;
	unsigned	uSize;
	unsigned	uCols;

	uCols = pData->uColumnCount;
	if(uCols)
		uSize = pData->aColumnXpos[uCols] - 1;
	else
		uSize = pData->iMaxSizeX - 1;


	if(pData->uOldXCount == uSize)
		if(pData->uOldXPage == pData->uSizeX) {
			return;
		}

	pData->uOldXPage	= pData->uSizeX;
	pData->uOldXCount	= uSize;

	UNLOCK(pData);

	sInfo.cbSize	= sizeof(SCROLLINFO);
	sInfo.fMask		= SIF_ALL;
	sInfo.nMin		= 0;
	sInfo.nMax  	= uSize;
	sInfo.nPage 	= pData->uSizeX;
	sInfo.nPos  	= pData->uScrollX;
	sInfo.nTrackPos	= 0;

	if(pData->uStyle & TVS_NOSCROLL) {
		sInfo.nMax = 0;
	} else
		if(pData->uStyleEx & TVS_EX_AUTOHSCROLL) {
			sInfo.nMax = 0;
		} else
			if(sInfo.nMax > 0) {
				sInfo.nMax--;
			}


	if((int)sInfo.nPage >= sInfo.nMax && pData->uScrollX > 0) {
		sInfo.nPos      = 0;
		pData->uScrollX = 0;

		UpdateView(pData);

		if(pData->hHeader) {
			MoveWindow(pData->hHeader, 0, 0, pData->uSizeX, pData->uStartPixel, TRUE);
		}
	}

	SetScrollInfo(pData->hWnd, SB_HORZ, &sInfo, TRUE);

	LOCK(pData);
}

//*****************************************************************************
//*
//*		UpdateScrollY
//*
//*****************************************************************************
//	Aktualisiert die Y-Scroolbar
//	pData		: Zeiger auf die Fensterdaten
//	Ergibt 1 wenn der sich Einstellungen verändert haben
static void UpdateScrollY(TreeListData *pData) {

	SCROLLINFO  sInfo;

	if(pData->uOldYCount == pData->uItemPosCount)
		if(pData->uOldYPage == pData->uPageEnties) {
			return;
		}

	pData->uOldYPage	= pData->uPageEnties;
	pData->uOldYCount	= pData->uItemPosCount;

	UNLOCK(pData);

	sInfo.cbSize	= sizeof(SCROLLINFO);
	sInfo.fMask		= SIF_ALL;
	sInfo.nMin		= 0;
	sInfo.nMax  	= pData->uItemPosCount;
	sInfo.nPage 	= pData->uPageEnties;
	sInfo.nPos  	= pData->uScrollY;
	sInfo.nTrackPos	= 0;

	if(pData->uStyle & TVS_NOSCROLL) {
		sInfo.nMax = 0;
	}

	if((int)sInfo.nPage >= sInfo.nMax && pData->uScrollY > 0) {
		sInfo.nPos      = 0;
		pData->uScrollY = 0;

		UpdateView(pData);
	}

	SetScrollInfo(pData->hWnd, SB_VERT, &sInfo, TRUE);

	LOCK(pData);
}

//*****************************************************************************
//*
//*		UpdateToolTip
//*
//*****************************************************************************
//	Aktualisiert den Text für das Tootip
//	pData	: Zeiger auf Fensterdaten
//	uItem	: Item auf den der Mauszeiger zeigt
//	uFlags	: Flags vom HitTest
static void UpdateToolTip(TreeListData *pData, unsigned uItem, unsigned uFlags) {

	TCHAR			cTemp[INFOTIPSIZE];
#ifndef __REACTOS__
	HWND			hToolTip;
#endif
	NMTVGETINFOTIP	sToolNv;
	NMTREEVIEW		sNotify;
	TOOLINFO		sInfo;
	ExtraItem	   *pExtra;
	BaseItem	   *pEntry;
	LPCTSTR	    	pText;
	RECT			sRect;
	unsigned		uSize;
	unsigned		uCol;
	unsigned		uLen;
	LRESULT			lRet;
	int				iTemp;
	// Tooltip ausbelnden
	if(!uItem || (uItem == pData->uEditItem && TVHT_SUBTOCOL(uFlags) == pData->uEditSub)) {
		if(pData->uToolTipItem)
			goto ExitTip;
		return;
	}

	pEntry = pData->pTreeItems[uItem];

	if(uFlags & TVHT_ONITEM) {
		if(pData->uToolTipItem != uItem || pData->uToolTipSub != 0) {
			if(!pData->pTreeItems[uItem]) {					// Existiert der Eintag noch ?
				goto ExitTip;
			}

			TreeListGetItemRect(pData, uItem, TVIR_GETCOLUMN | TVIR_TEXT, &sRect);

			if(sRect.right > (int)pData->uSizeX) {
				sRect.right = pData->uSizeX;
			}

			lRet = 0;

			if(pData->uStyleEx & TVS_EX_TOOLTIPNOTIFY) {		// Tooltip-Daten via speziellem Notify holen
				sNotify.hdr.code			= TVN_ITEMTOOLTIP;
				sNotify.action				= 0;
				sNotify.itemNew.mask		= TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_SUBITEM;
				sNotify.itemNew.hItem		= (HTREEITEM)(ULONG_PTR)uItem;
				sNotify.itemNew.stateMask	= 0xFFFFFFFF;
				sNotify.itemNew.state		= pEntry->uState;
				sNotify.itemNew.lParam		= pEntry->lParam;
				sNotify.itemNew.pszText		= pEntry->pText;
				sNotify.itemNew.cchTextMax	= pEntry->uTextSize;
				sNotify.itemNew.cChildren	= 0;
				sNotify.itemOld.mask		= 0;
				sNotify.ptDrag.x			= sRect.left;
				sNotify.ptDrag.y			= sRect.top;

				UNLOCK(pData);
				lRet = SendNotify(pData, &sNotify.hdr);
				LOCK(pData);

				if(lRet)
					goto UserTip;
			} else
				if(pData->uStyle & TVS_INFOTIP) {				// Tooltip-Daten via normalem Notify holen
					sToolNv.hdr.code			= TVN_GETINFOTIP;
					sToolNv.cchTextMax			= INFOTIPSIZE;
					sToolNv.hItem				= (HTREEITEM)(ULONG_PTR)uItem;
					sToolNv.lParam				= pEntry->lParam;
					sToolNv.pszText				= cTemp;

					str_ncpy(cTemp, pEntry->pText, INFOTIPSIZE);
					cTemp[INFOTIPSIZE - 1] = 0;

					UNLOCK(pData);
					lRet = SendNotify(pData, &sNotify.hdr);
					LOCK(pData);

					sNotify.itemNew.mask		= TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_SUBITEM;
					sNotify.itemNew.hItem		= (HTREEITEM)(ULONG_PTR)uItem;
					sNotify.itemNew.pszText		= sToolNv.pszText;
					sNotify.itemNew.cchTextMax	= str_len(sToolNv.pszText);
					sNotify.itemNew.cChildren	= 0;
					sNotify.itemNew.stateMask	= 0xFFFFFFFF;
					sNotify.itemNew.state		= pEntry->uState;
					sNotify.itemNew.lParam		= pEntry->lParam;
					sNotify.ptDrag.x			= sRect.left;
					sNotify.ptDrag.y			= sRect.top;

					goto UserTip;
				}
			// Passt der Text in die Spalte
			if(sRect.right - sRect.left <= pEntry->iTextPixels + 4) {
				pText	= pEntry->pText;
				uSize	= pEntry->uTextSize;

				if(pEntry->bCallback & TVIF_TEXT) {
					CallbackEntry(pData, pEntry, uItem, TVIF_TEXT, &iTemp, &uSize, &pText);
				}

				if(!pText || *pText == 0)
					goto ExitTip;

				uLen = str_len(pText) + 1;
				if(uLen >= pData->uToolTipSize) {				// Tooltipspeicher vergrößern
					delete(pData->pToolTipText);
					pData->uToolTipSize = (uLen + 255)&~0xFF;
					pData->pToolTipText = new(TCHAR, pData->uToolTipSize + 4);
				}

				memcpy(pData->pToolTipText, pText, uLen * sizeof(TCHAR));
				pData->hFontT = (pEntry->uState & TVIS_BOLD) ? pData->hFontB : pData->hFontN;

#ifndef __REACTOS__
				hToolTip				= pData->hToolTip;
#endif
				pData->sToolTipPos.x	= sRect.left;
				pData->sToolTipPos.y	= sRect.top;

				ClientToScreen(pData->hWnd, &pData->sToolTipPos);

				UNLOCK(pData);

				sInfo.cbSize			= sizeof(sInfo);
				sInfo.hwnd				= pData->hWnd;
				sInfo.uId				= (UINT_PTR)pData->hWnd;

				if(pData->uToolTipItem) {
					SendMessage(pData->hToolTip, TTM_TRACKACTIVATE, 0, (LPARAM)&sInfo);
					pData->uToolTipItem = 0;
				}

				SendMessage(pData->hToolTip, TTM_TRACKPOSITION, 0, MAKELONG(pData->sToolTipPos.x, pData->sToolTipPos.y));
				SendMessage(pData->hToolTip, TTM_TRACKACTIVATE, 1, (LPARAM)&sInfo);

				LOCK(pData);

				pData->uToolTipItem		= uItem;
				pData->uToolTipShow 	= 0;
				pData->uToolTipSub		= 0;

				SetTimer(pData->hWnd, ID_TOOLTIPCHECK, 1500, NULL);
			} else {
				if(pData->uToolTipItem)
					goto ExitTip;
			}
		}

		return;
	}

	if(uFlags & (TVHT_ONSUBICON | TVHT_ONSUBLABEL)) {
		if(pData->uToolTipItem != uItem || TVHT_SUBTOCOL(uFlags) != pData->uToolTipSub) {
			lRet   = 0;
			uCol   = TVHT_SUBTOCOL(uFlags);
			pExtra = pData->pExtraItems[uCol - 1][uItem];

			if(pData->uStyleEx & TVS_EX_TOOLTIPNOTIFY) {		// Tooltip-Daten via Notify holen
				TreeListGetItemRect(pData, uItem, TVIR_GETCOLUMN | TVIR_TEXT | TVIR_COLTOSUB(uCol), &sRect);

				if(sRect.right > (int)pData->uSizeX) {
					sRect.right = pData->uSizeX;
				}

				if(pExtra) {
					sNotify.itemNew.state		= pExtra->uState;
					sNotify.itemNew.pszText		= pExtra->pText;
					sNotify.itemNew.cchTextMax	= pExtra->uTextSize;
				} else {
					sNotify.itemNew.state		= 0;
					sNotify.itemNew.cchTextMax	= 0;
					sNotify.itemNew.pszText		= _T("");
				}

				sNotify.hdr.code			= TVN_ITEMTOOLTIP;
				sNotify.action				= 0;
				sNotify.itemNew.lParam		= pEntry->lParam;
				sNotify.itemNew.mask		= TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_SUBITEM;
				sNotify.itemNew.hItem		= (HTREEITEM)(ULONG_PTR)uItem;
				sNotify.itemNew.stateMask	= 0xFFFFFFFF;
				sNotify.itemNew.cChildren	= uCol;
				sNotify.itemOld.mask		= 0;
				sNotify.ptDrag.x			= sRect.left;
				sNotify.ptDrag.y			= sRect.top;

				UNLOCK(pData);
				lRet = SendNotify(pData, &sNotify.hdr);
				LOCK(pData);

				if(lRet)
					goto UserTip;
			}

			if(pExtra) {										// Tooltip auf Unterspalte ?
				TreeListGetItemRect(pData, uItem, TVIR_GETCOLUMN | TVIR_TEXT | TVIR_COLTOSUB(uCol), &sRect);

				if(sRect.right > (int)pData->uSizeX) {
					sRect.right = pData->uSizeX;
				}

				if(sRect.right - sRect.left <= pExtra->iTextPixels + 4) {
					pText	= pExtra->pText;
					uSize	= pExtra->uTextSize;

					if(pExtra->bCallback & TVIF_TEXT) {
						CallbackExtra(pData, pEntry, pExtra, uItem, uCol, TVIF_TEXT, &iTemp, &uSize, &pText);
					}

					if(!pText || *pText == 0)
						goto ExitTip;

					uLen = str_len(pText) + 1;
					if(uLen >= pData->uToolTipSize) {			// Tooltipspeicher vergrößern
						delete(pData->pToolTipText);
						pData->uToolTipSize = (uLen + 255)&~0xFF;
						pData->pToolTipText = new(TCHAR, pData->uToolTipSize + 4);
					}

					memcpy(pData->pToolTipText, pText, uLen * sizeof(TCHAR));
					pData->hFontT = (pExtra->uState & TVIS_BOLD) ? pData->hFontB : pData->hFontN;

					pData->sToolTipPos.x	= sRect.left;
					pData->sToolTipPos.y	= sRect.top;
#ifndef __REACTOS__
					hToolTip				= pData->hToolTip;
#endif

					ClientToScreen(pData->hWnd, &pData->sToolTipPos);

					UNLOCK(pData);

					sInfo.cbSize			= sizeof(sInfo);
					sInfo.hwnd				= pData->hWnd;
					sInfo.uId				= (UINT_PTR)pData->hWnd;

					if(pData->uToolTipItem) {
						SendMessage(pData->hToolTip, TTM_TRACKACTIVATE, 0, (LPARAM)&sInfo);
						pData->uToolTipItem = 0;
					}

					SendMessage(pData->hToolTip, TTM_TRACKPOSITION, 0, MAKELONG(pData->sToolTipPos.x, pData->sToolTipPos.y));
					SendMessage(pData->hToolTip, TTM_TRACKACTIVATE, 1, (LPARAM)&sInfo);

					LOCK(pData);

					pData->uToolTipItem		= uItem;
					pData->uToolTipSub		= uCol;
					pData->uToolTipShow 	= 0;

					SetTimer(pData->hWnd, ID_TOOLTIPCHECK, 1500, NULL);
				} else {
					if(pData->uToolTipItem)
						goto ExitTip;
				}
			} else {
				if(pData->uToolTipItem)
					goto ExitTip;
			}
		}

		return;
	}

ExitTip:

	if(pData->uToolTipItem) {									// Tooltip ausblenden
		UNLOCK(pData);

		sInfo.cbSize			= sizeof(sInfo);
		sInfo.hwnd				= pData->hWnd;
		sInfo.uId				= (UINT_PTR)pData->hWnd;

		SendMessage(pData->hToolTip, TTM_TRACKACTIVATE, 0, (LPARAM)&sInfo);

		LOCK(pData);

		pData->uToolTipItem = 0;
		pData->uToolTipSub	= 0;
		pData->uToolTipShow = 0;

		KillTimer(pData->hWnd, ID_TOOLTIPCHECK);
	}

	return;

UserTip:

	pText	   = sNotify.itemNew.pszText;					// Soll ein User-Tooltip angezeigt werden
	uSize	   = sNotify.itemNew.cchTextMax;
	sRect.left = sNotify.ptDrag.x;
	sRect.top  = sNotify.ptDrag.y;

	if(!pText || *pText == 0)
		goto ExitTip;

	uLen = str_len(pText) + 1;
	if(uLen >= pData->uToolTipSize) {							// Tooltipspeicher vergrößern

		delete(pData->pToolTipText);
		pData->uToolTipSize = (uLen + 255)&~0xFF;
		pData->pToolTipText = new(TCHAR, pData->uToolTipSize + 4);
	}

	memcpy(pData->pToolTipText, pText, uLen * sizeof(TCHAR));
	pData->hFontT		= (pEntry->uState & TVIS_BOLD) ? pData->hFontB : pData->hFontN;

	ClientToScreen(pData->hWnd, &sNotify.ptDrag);
	pData->sToolTipPos	= sNotify.ptDrag;
#ifndef __REACTOS__
	hToolTip			= pData->hToolTip;
#endif
	// Tooltip verzögert anzeigen
	if((sNotify.itemNew.mask & TVIF_TOOLTIPTIME) && sNotify.itemNew.lParam > 0) {
		pData->uToolTipShow = (unsigned)(sNotify.itemNew.lParam + 499) / 500;
		pData->uToolTipSub	= sNotify.itemNew.cChildren;
		pData->uToolTipItem = uItem;

		SetTimer(pData->hWnd, ID_TOOLTIPCHECK, 500, NULL);

		return;
	}

	UNLOCK(pData);

	sInfo.cbSize		= sizeof(sInfo);
	sInfo.hwnd			= pData->hWnd;
	sInfo.uId			= (UINT_PTR)pData->hWnd;

	if(pData->uToolTipItem) {									// Tooltip Fenster aktivieren
		SendMessage(pData->hToolTip, TTM_TRACKACTIVATE, 0, (LPARAM)&sInfo);
		pData->uToolTipItem = 0;
	}

	SendMessage(pData->hToolTip, TTM_TRACKPOSITION, 0, MAKELONG(sNotify.ptDrag.x, sNotify.ptDrag.y));
	SendMessage(pData->hToolTip, TTM_TRACKACTIVATE, 1, (LPARAM)&sInfo);

	LOCK(pData);

	pData->uToolTipShow = 0;
	pData->uToolTipItem = uItem;
	pData->uToolTipSub	= sNotify.itemNew.cChildren;

	SetTimer(pData->hWnd, ID_TOOLTIPCHECK, 1500, NULL);
}

//*****************************************************************************
//*
//*		CreateFontset
//*
//*****************************************************************************
//  Create the font set (normal, bold, etc...) from the given HFONT
static int CreateFontset(TreeListData *pData, HFONT hFont){
	LOGFONT sLog;
	HFONT hBold;
	int iRet = 0;
	
	if(GetObject(hFont, sizeof(sLog), &sLog)){
		sLog.lfWeight = FW_BOLD;
		if((hBold = CreateFontIndirect(&sLog))){
			pData->hFontN = hFont;	//store the given font
			if(pData->hFontB != hDefaultFontB){
				//if the current bold is not the default bold, free it
				DeleteObject(pData->hFontB);
			}
			pData->hFontB = hBold;	//store the created bold 
			iRet = 1;
		}
	}
	return iRet;
}

//*****************************************************************************
//*
//*		UpdateFont
//*
//*****************************************************************************
//	Erzeugt einen den fetten Font für das Fenster
//	pData		: Zeiger auf die Fensterdaten
//	iRedraw		: Soll das Fenster neugezeichnet werden
//	Ergibt 1 wenn der Font verändert wurde
static int UpdateFont(TreeListData *pData) {

	int			iPos;
	int			iRet;
	HDC			hDc;
	LOGFONT		sLog;
	SIZE		sSize;
	TEXTMETRIC	sMetrics;
	BaseItem   *pEntry;
	BaseItem  **pList;
	ExtraItem  *pExtra;
	ExtraItem **pItems;
	unsigned	uSub;

	if(!hDefaultFontN) {										// Den Standard-Font erzeugen
		SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(sLog), &sLog, 0);
		sLog.lfWeight = FW_NORMAL;
		hDefaultFontN = CreateFontIndirect(&sLog);
		sLog.lfWeight = FW_BOLD;
		hDefaultFontB = CreateFontIndirect(&sLog);
	}


	if(!pData->hFontN)
		pData->hFontN = hDefaultFontN;
	if(!pData->hFontB)
		pData->hFontB = hDefaultFontB;

/*
	if(pData->hFontN == hDefaultFontN) {						// Ist der Standard-Font eingestellt
		pData->hFontB = hDefaultFontB;
	} else {
		pData->hFontB = pData->hFontN;
	}
*/

	if(pData->hFontN != pData->hFontL) {
		pData->hFontL = pData->hFontN;

		hDc = GetDC(NULL);
		SelectObject(hDc, pData->hFontN);
		GetTextMetrics(hDc, &sMetrics);
		pData->iFontHeight =  sMetrics.tmHeight;
		pData->iFontLine   =  sMetrics.tmAscent + 1;
		pData->iFontOff    = (sMetrics.tmPitchAndFamily & TMPF_FIXED_PITCH) ? 0 : -1;
		ReleaseDC(NULL, hDc);

		pList = pData->pTreeItems;
		iPos  = pData->uTreeItemsMax;

		for(; iPos >= 0; iPos--) {								// Alle Textbreiten zurücksetzen
			pEntry = pList[iPos];
			if(!pEntry)
				continue;

			pEntry->iTextPixels = 0;
		}


		for(uSub = 1; uSub < pData->uColumnCount; uSub++) {
			iPos    = pData->uTreeItemsMax;
			pItems 	= pData->pExtraItems[uSub - 1];

			for(; iPos >= 0; iPos--) {
				pExtra = pItems[iPos];
				if(!pExtra)
					continue;

				pExtra->iTextPixels = 0;
			}
		}

		iRet = 1;
	} else {
		iRet = 0;
	}

	// compute Width of "..." text
	hDc = GetDC(NULL);
	SelectObject(hDc, pData->hFontN);
	GetTextExtentExPoint(hDc, _T("..."), 3, 256, NULL, NULL, &sSize);
	pData->uTrippleN =  sSize.cx;
	SelectObject(hDc, pData->hFontB);
	GetTextExtentExPoint(hDc, _T("..."), 3, 256, NULL, NULL, &sSize);
	pData->uTrippleB =  sSize.cx;
	ReleaseDC(NULL, hDc);

	return iRet;
}


//*****************************************************************************
//*
//*		UpdateItems
//*
//*****************************************************************************
//	Berechnet die Positionen der Zeilen für die sichtbaren Einträge
//	pData		: Zeiger auf die Fensterdaten
//	uItem		: Ist der Eintrag ab dem begonnen wird
static void UpdateItems(TreeListData *pData, unsigned uItem) {

	unsigned	uPos;
	unsigned	uOld;
	unsigned	uNum;
	unsigned	uTemp;
	unsigned	uStart;
	unsigned   *pLines;
	BaseItem  **pItems;
	BaseItem   *pEntry;
	BaseItem   *pTemp;
	RECT		sRect;

	uOld	= pData->uItemPosCount;
	pLines	= pData->pItemPos;
	pItems	= pData->pTreeItems;

	if(!uItem) {												// Am Anfang beginnen
		uItem = pData->uFirstChild;
		if(!uItem) {											// Leere Liste
			if(!uOld)
				return;

			for(uNum = 0; uNum < uOld; uNum++) {					// Die alten Einträge zurücksetzen
				uTemp = pLines[uNum];
				if(!uTemp)
					continue;
				pLines[uNum] = 0;
				pTemp = pItems[uTemp];
				if(!pTemp)
					continue;
				pTemp->uShowPos = 0;
			}

			pData->uItemPosCount = 0;

			GetClientRect(pData->hWnd, &sRect);
			InvalidateRect(pData->hWnd, &sRect, TRUE);

			memset(pLines, 0, sizeof(unsigned)*uOld);
			return;
		}

		for(uNum = 0; uNum < uOld; uNum++) {						// Die alten Einträge zurücksetzen
			uTemp = pLines[uNum];
			if(!uTemp)
				continue;
			pLines[uNum] = 0;
			pTemp = pItems[uTemp];
			if(!pTemp)
				continue;
			pTemp->uShowPos = 0;
		}

		pEntry			 = pItems[uItem];
		pEntry->uShowPos = 1;
		pLines[0]		 = uItem;
		uPos			 = 1;
		uStart			 = 0;
	} else {													// Bei einem Eintrag beginnen
		pEntry			 = pItems[uItem];
		uPos			 = pEntry->uShowPos;
		if(uPos)
			uStart	 = uPos - 1;
		else
			uStart	 = 0;

		for(uNum = uPos; uNum < uOld; uNum++) {						// Die alten Einträge zurücksetzen
			uTemp = pLines[uNum];
			if(!uTemp)
				continue;
			pLines[uNum] = 0;
			pTemp = pItems[uTemp];
			if(!pTemp)
				continue;
			pTemp->uShowPos = 0;
		}
	}

	for(;;) {													// Die Zeilen neu zuordnen
		if(pEntry->uFirstChild && (pEntry->uState & TVIS_EXPANDED)) {
			uItem = pEntry->uFirstChild;
		} else
			if(pEntry->uNextItem) {
				uItem = pEntry->uNextItem;
			} else {
				for(;;) {
					uItem = pEntry->uParent;
					if(!uItem)
						break;

					pEntry = pItems[uItem];
					if(pEntry->uNextItem) {						// Gibt es etwas in der gleichen Ebene
						uItem = pEntry->uNextItem;
						break;
					}
				}

				if(!uItem)
					break;
			}

		pEntry = pItems[uItem];

		if(pLines[uPos] != uItem) {
			pLines[uPos] = uItem;
		} else {
			if(uStart == uPos)
				uStart++;
		}

		uPos++;
		pEntry->uShowPos = uPos;
	}

	pData->uItemPosCount = uPos;

	if(uStart > pData->uScrollY)								// Neu zu zeichnenten Bereich bestimmen
		uStart -= pData->uScrollY;
	else
		uStart = 0;

	GetClientRect(pData->hWnd, &sRect);

	sRect.top = pData->uStartPixel + pData->iRowHeight * uStart;

	if(sRect.top <= sRect.bottom) {
		InvalidateRect(pData->hWnd, &sRect, FALSE);
	}

	if(uOld != uPos)
		UpdateScrollY(pData);
}

//*****************************************************************************
//*
//*		UpdateColumns
//*
//*****************************************************************************
//	Prüft ob es Veränderungen in Spaltenbreiten gab
//	pData		: Zeiger auf die Fensterdaten
//	Ergibt die Breite ab der die Spalten verändert wurden oder 0x10000
static int UpdateColumns(TreeListData *pData) {
#ifndef __REACTOS__
	HWND	hHeader;
#endif
	UINT	uNext;
	UINT	uCol;
	UINT	uSub;
	int		iSize;
	int		iNum;
	int		iNow;
	int		iOld;
	int		iRet;

#ifndef __REACTOS__
	hHeader = pData->hHeader;
#endif
	pData->aColumnXpos[0] = 0;

	iRet = 0x10000;
	iOld = 0;
	iNow = 0;

	for(uCol = 0; uCol < pData->uColumnCount;) {					// Suche die erste geänderte Spalte
		uSub  = pData->aColumnPos[uCol];
		iSize = pData->aColumn[uSub].sReal;
		uSub  = pData->aColumn[uSub].bNext;
		iOld  = iNow;
		iNow += iSize;
		uCol += 1;

		if(uCol == 1)
			iNow -= 1;
		if(iNow < iOld)
			iNow  = iOld;
		if(uSub == pData->uColumnCount)
			if(iNow >= (int)pData->uSizeX - 1) {
				iNow++;
			}

		iNum = pData->aColumnXpos[uSub];

		if(iNum == iNow)
			continue;
		if(iNum == 0)
			iNum = iOld;
		if(iNum >= iNow) {
			iRet = iOld;
		} else {
			iRet = iOld;

			if(pData->uSelectedItem) {						// Problem bei ausgewählten leeren Einträgen
				uNext = pData->aColumn[pData->uSelectedSub].bNext;
				if(uNext == uSub) {
					UpdateRect(pData, pData->uSelectedItem, pData->uSelectedSub);
				}
			}

			if(pData->uTrackedItem) {
				uNext = pData->aColumn[pData->uTrackedSub].bNext;
				if(uNext == uSub) {
					UpdateRect(pData, pData->uTrackedItem, pData->uTrackedSub);
				}
			}
		}

		pData->aColumnXpos[uSub] = iNow;
		break;
	}

	while(uCol < pData->uColumnCount) {							// Restliche Spalten berechen
		iOld  = iNow;
		uSub  = pData->aColumnPos[uCol];
		iNow += pData->aColumn[uSub].sReal;
		uSub  = pData->aColumn[uSub].bNext;
		uCol += 1;

		if(uCol == pData->uColumnCount)
			if(iNow >= (int)pData->uSizeX - 1) {
				iNow++;
			}

		pData->aColumnXpos[uSub] = iNow;
	}

	pData->aColumnXpos[pData->uColumnCount + 1] = pData->uSizeX + 1;

	return iRet;
}

//*****************************************************************************
//*
//*		TreeListSetOrderArray
//*
//*****************************************************************************
//	Stellt die anzeige Reihenfolge der Spalten ein
//	pData		: Zeiger auf die Fensterdaten
//	uItems		: Ist die Nummer des Eintrages
//	pArray		: Zeiger auf die Einträge. Null steht für die Standartreihenfolge.
//				  z.B. {0,2,1} meint die sichtbare Reihenfolge Col0,Col2,Col1
//				  Der erste Eintrag muss 0 immer sein.
//	Ergibt   1 = Ok
//			 0 = Fehler
static int TreeListSetOrderArray(TreeListData *pData, unsigned uItems, unsigned *pArray) {

	BYTE		aFlags[MAX_COLUMNS + 1];
	UINT		aArray[MAX_COLUMNS + 1];
	TV_COLSIZE	sNotify;
	UINT		uDiff;
	UINT		uCol;
	UINT 		uSub;

	if(!pArray) {												// Spezialreihenfolge setzen
		if(uItems == FROM_HEADER) {								// Array aus Header holen
			if(!Header_GetOrderArray(pData->hHeader, pData->uColumnCount, aArray)) {
				return 0;
			}

			if(aArray[0] != 0) {
				return 0;
			}
		} else {
			for(uCol = pData->uColumnCount; uCol > 0; uCol++) {		// Standartreihenfolge
				uCol--;
				aArray[uCol] = uCol;
			}
		}

		uItems = pData->uColumnCount;
		pArray = aArray;
	} else {													// Prüfe Array
		if(pData->uColumnCount != uItems || uItems == 0 || *pArray) {
			return 0;
		}
	}

	memset(aFlags, 0, sizeof(aFlags) - 1);

	for(uCol = 0, uDiff = 0; uCol < uItems; uCol++) {					// Die Einträge prüfen
		uSub	= pArray[uCol];
		if(uSub >= uItems)
			return 0;
		if(aFlags[uSub])
			return 0;

		aFlags[uSub] = (BYTE)uCol;

		uDiff |= uCol ^ pData->aColumnPos[uSub];
	}

	if(uDiff == 0) {											// Alles blieb gleich
		return 1;
	}

	aFlags[0     ] = 0;
	aFlags[uItems] = (BYTE)uItems;

	for(uCol = 1; uCol < uItems; uCol++) {							// Die Einträge anpassen
		pData->aColumnPos[uCol]		= (BYTE)pArray[uCol];
	}

	for(uCol = 0; uCol < uItems; uCol++) {
		uSub						= aFlags[uCol];
		pData->aColumn[uCol].bIndex	= (BYTE)uSub;
		pData->aColumn[uCol].bNext	= pData->aColumnPos[uSub + 1];
	}

	Header_SetOrderArray(pData->hHeader, uItems, pArray);
	UpdateColumns(pData);
	UpdateView(pData);

	if(pData->uStyleEx & TVS_EX_HEADERCHGNOTIFY) {				// Alle Spalten haben sich verändert
		UNLOCK(pData);

		for(uCol = 0; uCol < uItems; uCol++) {
			sNotify.hdr.code	= TVN_COLUMNCHANGED;
			sNotify.uColumn		= uCol;
			sNotify.uIndex		= pData->aColumn[uCol].bIndex;
			sNotify.uPosX		= pData->aColumnXpos[uCol];
			sNotify.iSize		= pData->aColumn[uCol].sReal;

			SendNotify(pData, &sNotify.hdr);
		}

		LOCK(pData);
	}

	return 1;
}

//*****************************************************************************
//*
//*		TreeListToggleItem
//*
//*****************************************************************************
//	Klappt bei einem Eintrag die Kinder um, und schickt alle Notify-Nachrichten.
//	pData		: Zeiger auf die Fensterdaten
//	uItem		: Ist die Nummer des Eintrages
//	uAddFlags	: Sind die State-Flags die hinzugefügt werden sollen
//				  Bits 0..3 entahlen das Kommando, bei automatisch ermitteln
//	Ergibt  -1 = Fehler
//			 0 = Ausgeführt
//			 1 = Abbruch
static int TreeListToggleItem(TreeListData *pData, unsigned uItem, unsigned uAddFlags) {

	NMTREEVIEW	sNotify;
	BaseItem  **pList;
	BaseItem   *pEntry;
	BaseItem   *pTemp;
	unsigned	uAction;
	unsigned	uLevel;
	unsigned	uNext;
	LRESULT		lRet;
	BOOL		bDo;

	if(uItem > pData->uTreeItemsMax)
		return 0;

	pList  = pData->pTreeItems;
	pEntry = pList[uItem];
	if(!pEntry)
		return -1;

	uAction = uAddFlags & 0x0F;
	if(!uAction) {
		uAction	= ((pEntry->uState ^ TVIS_EXPANDED) & (TVIS_EXPANDED | TVIS_EXPANDPARTIAL)) ? TVE_EXPAND : TVE_COLLAPSE;
	}

	sNotify.action				=  uAction;
	sNotify.hdr.code			=  TVN_ITEMEXPANDING;
	sNotify.itemNew.mask		=  TVIF_HANDLE | TVIF_PARAM | TVIF_STATE;
	sNotify.itemNew.hItem		= (HTREEITEM)(ULONG_PTR)uItem;
	sNotify.itemNew.stateMask	=  0xFFFFFFFF;
	sNotify.itemNew.state		=  pEntry->uState;
	sNotify.itemNew.lParam		=  pEntry->lParam;
	sNotify.itemNew.pszText		= (LPTSTR) - 1;
	sNotify.itemNew.cchTextMax	=  -1;
	sNotify.itemOld.mask		=  0;
	sNotify.ptDrag.x			=  0;
	sNotify.ptDrag.y			=  0;

	UNLOCK(pData);

	lRet = SendNotify(pData, &sNotify.hdr);

	LOCK(pData);

	pList  = pData->pTreeItems;
	pEntry = pList[uItem];

	if(pEntry == 0)
		return -1;									// Eintrag inzischen gelöscht ?
	if(lRet  != 0)
		return  1;									// User-Abbruch ?

	if(uAction == TVE_EXPAND) {									// Aufklappen
		if(pEntry->uState & TVIS_EXPANDED) {
			bDo = FALSE;									// Nur von + auf -
		} else {
			pEntry->uState |= TVIS_EXPANDED;				// Kinder Aufklappen
			bDo = TRUE;
		}
	} else {													// Zuklappen
		pEntry->uState &= ~TVIS_EXPANDED;
		bDo	   = TRUE;
	}

	pEntry->uState &= ~TVIS_EXPANDPARTIAL;
	pEntry->uState |=  uAddFlags&~0x0F;

	if(pEntry->uShowPos && bDo) {
		if(pEntry->uState & TVIS_EXPANDED) {					// Kinderfenster aktuallisieren
			uLevel			=  0;
			uNext			=  pEntry->uFirstChild;

			while(uNext) {
				pTemp = pList[uNext];
				pTemp->uShowPos = 0;

				if(pTemp->uFirstChild) {
					uNext = pTemp->uFirstChild;
					uLevel++;
					continue;
				}

				if(pTemp->uNextItem) {
					uNext = pTemp->uNextItem;
					continue;
				}

				if(uLevel == 0)
					break;

				uNext = pList[pTemp->uParent]->uNextItem;
				uLevel--;
			}
		}

		UpdateItems(pData, uItem);
	}

	sNotify.action				=  uAction;
	sNotify.hdr.code			=  TVN_ITEMEXPANDED;
	sNotify.itemNew.mask		=  TVIF_HANDLE | TVIF_PARAM | TVIF_STATE;
	sNotify.itemNew.hItem		= (HTREEITEM)(ULONG_PTR)uItem;
	sNotify.itemNew.stateMask	=  0xFFFFFFFF;
	sNotify.itemNew.state		=  pEntry->uState;
	sNotify.itemNew.lParam		=  pEntry->lParam;
	sNotify.itemNew.pszText		= (LPTSTR) - 1;
	sNotify.itemNew.cchTextMax	=  -1;
	sNotify.itemOld.mask		=  0;
	sNotify.ptDrag.x			=  0;
	sNotify.ptDrag.y			=  0;

	UNLOCK(pData);

	SendNotify(pData, &sNotify.hdr);

	LOCK(pData);

	pList  = pData->pTreeItems;
	pEntry = pData->pTreeItems[uItem];

	if(!pEntry)
		return -1;									// Eintrag inzischen gelöscht ?

	if(uAction == TVE_EXPAND) {									// ONCE setzen nach Expandieren
		pEntry->uState |= TVIS_EXPANDEDONCE;
	}

	if(pData->uSelectedItem && bDo) {							// Ist der ausgewählten Eintrag sichtbar ?
		pEntry = pList[pData->uSelectedItem];
		if(!pEntry) {
			pData->uSelectedItem = 0;
			pData->uSelectedSub	 = 0;
		} else
			if(!pEntry->uShowPos) {
				while(!pEntry->uShowPos) {
					uItem  = pEntry->uParent;
					pEntry = pList[uItem];
				}

				TreeListSelectItem(pData, uItem, pData->uSelectedSub, TVC_UNKNOWN);
			}
	}

	if(bDo == FALSE) {											// Nur von + auf -
		UpdateRect(pData, uItem, 0);
	}

	return 0;
}

//*****************************************************************************
//*
//*		TreeListGetItemRect
//*
//*****************************************************************************
//	Holt das Rechteck eines Eintrages
//	pData		: Zeiger auf die Fensterdaten
//	uItem		: Ist die Nummer des Eintrages
//	uFlags		: Bit 0   : 0=volle Zeile  1=nur Text
//				  Bit 7   : 1=nur Spalte
//				  Bit 24.. : Spaltennummer
//	Ergibt 1 wenn der Eintrag sichtbar war
static int TreeListGetItemRect(TreeListData *pData, unsigned uItem, unsigned uFlags, RECT *pRect) {

	ExtraItem  *pExtra;
	BaseItem   *pEntry;
	unsigned	uNext;
	unsigned	uPos;
	unsigned	uSub;

	if(uItem > pData->uTreeItemsMax) {
		memset(pRect, 0, sizeof(RECT));
		return 0;
	}

	pEntry = pData->pTreeItems[uItem];
	if(!pEntry->uShowPos) {									// Ist der Eintrag aufgeklappt
		memset(pRect, 0, sizeof(RECT));
		return 0;
	}

	uPos = pEntry->uShowPos - pData->uScrollY - 1;
	if(uPos >= pData->uMaxEnties) {								// Eintrag im Fenster sichtbar
		memset(pRect, 0, sizeof(RECT));
		return 0;
	}

	pRect->top     = pData->uStartPixel;
	pRect->top    += pData->iRowHeight * uPos;
	pRect->bottom  = pData->iRowHeight + pRect->top;

	if((uFlags & 0xFC) == TVIR_GETCOLUMN) {						// Nur Spalten
		uSub = uFlags >> 24;
		if(uSub >= pData->uColumnCount)
			uSub = 0;

		uNext		   = pData->aColumn[uSub].bNext;
		pRect->left    = pData->aColumnXpos[uSub];
		pRect->left   -= pData->uScrollX;
		pRect->right   = pData->aColumnXpos[uNext];
		pRect->right  -= pData->uScrollX;
	} else {
		uSub		   = 0;
		pRect->left    = 0;
		pRect->left   -= pData->uScrollX;
		pRect->right   = pData->uSizeX;
	}

	if(uFlags & TVIR_TEXT) {									// Nur Text ausgeben
		if(uSub > 0) {
			pExtra =  pData ->pExtraItems[uSub - 1][uItem];

			if(pData->aColumn[uSub].bEdit == TVAX_CHECK) {
				pRect->left += pData->iChecksXsize;
				if(pRect->left > pRect->right)
					pRect->left = pRect->right;
			} else
				if(pExtra && pExtra->bFlags & TVIX_HASIMAGE) {
					pRect->left += pData->iImagesXsize;
					if(pRect->left > pRect->right)
						pRect->left = pRect->right;
				}
		} else {
			if(pData->cHasRootRow) {							// Root-Linien ausgleichen
				pRect->left += pData->iIndent;
			}

			pRect->left += pData->iIndent * pEntry->uLevel;

			if(pData->hStates) {
				pRect->left += pData->iStatesXsize;
			}

			if(!(pData->uStyle & TVS_HASLINES)) {
				pRect->left -= 1;
			}

			if(pData->uStyleEx & TVS_EX_ITEMLINES) {
				pRect->left += 1;
				if(pEntry->bFlags & TVIX_HASIMAGE)
					pRect->left++;
			}

			if(pEntry->bFlags & TVIX_HASIMAGE) {
				pRect->left += pData->iImagesXsize;
			}

			if(pRect->left > pRect->right) {
				pRect->left = pRect->right;
			}
		}
	}

	return 1;
}

//*****************************************************************************
//*
//*		TreeListEnsureVisible
//*
//*****************************************************************************
//	Macht einen Eintrag sichtbar
//	pData		: Zeiger auf die Fensterdaten
//	uItem		: Ist die Nummer des Eintrages
//	uSub		: Untereintrag der sichtbar sein soll
//						0xFFFFFFFF nur Zeile
//						FIRST_LINE als oberster Eintrag
//	Ergibt 1 wenn nur zum Eintrag gescrollt wurde bzw. 0 wenn aufgeklapt wurde
static int TreeListEnsureVisible(TreeListData *pData, unsigned uItem, unsigned uSub) {

	BaseItem   *pEntry;
	BaseItem   *pTemp;
	unsigned	uTemp;
	unsigned	uNext;
	unsigned	uPos;
	int			iNum;
	int			iAnf;
	int			iOff;
	int			iEnd;
	int			iMax;
	int			iRet;

	if(uItem > pData->uTreeItemsMax)
		return -1;

	pEntry = pData->pTreeItems[uItem];
	if(!pEntry)
		return -1;

	uPos = pEntry->uShowPos;
	if(!uPos) {												// Zweige aufklappen wenn Eintrag zugeklappt

		iRet = 0;

		for(pTemp = pEntry;;) {
			uTemp = pTemp->uParent;
			pTemp = pData->pTreeItems[uTemp];
			if(!pTemp)
				break;
			if((pTemp->uState & TVIS_EXPANDED) == 0) {
				if(TreeListToggleItem(pData, uTemp, 0))
					return 0;
			}
		}

		pEntry = pData->pTreeItems[uItem];
		if(!pEntry)
			return 0;

		uPos = pEntry->uShowPos;
		if(!uPos)
			return 0;
	} else {													// Nur Scrollen
		iRet = 1;
	}

	uPos--;
	if(uPos < pData->uScrollY) {								// Vor erster Zeile
		pData->uScrollY	= uPos;
		SetScrollPos(pData->hWnd, SB_VERT, uPos, TRUE);
		UpdateView(pData);
	} else
		if(uSub == FIRST_LINE) {								// Als ersten Eintrag
			if(uPos != pData->uScrollY) {
				pData->uScrollY	= uPos;
				SetScrollPos(pData->hWnd, SB_VERT, uPos, TRUE);
				UpdateView(pData);
			}

			return iRet;
		} else
			if(uPos >= pData->uScrollY + pData->uPageEnties) {		// Nach letzter Zeile
				iOff  = uPos - (pData->uPageEnties - 1);
				iMax  = pData->uItemPosCount;
				iMax -=	pData->uPageEnties - 1;

				if(iOff >= iMax)
					iOff = iMax;
				if(iOff < 0)
					iOff = 0;
				if(iOff != (int)pData->uScrollY) {
					pData->uScrollY	= iOff;
					SetScrollPos(pData->hWnd, SB_VERT, iOff, TRUE);
					UpdateView(pData);
				}
			}

	if(uSub < pData->uColumnCount) {							// Horizontal einrichten
		uNext = pData->aColumn[uSub].bNext;
		iNum  = pData->uSizeX;
		iOff  = pData->uScrollX;
		iAnf  = pData->aColumnXpos[uSub ];
		iEnd  = pData->aColumnXpos[uNext];

		if(iOff + iNum < iAnf)
			iOff = iAnf;
		if(iOff     >= iEnd)
			iOff = iAnf;
		if(iOff + iNum < iEnd)
			iOff = iEnd - iNum;
		if(iOff     > iAnf)
			iOff = iAnf;

		iMax  = pData->aColumnXpos[pData->uColumnCount];
		iMax -= pData->uSizeX / 2;

		if(iOff > iMax)
			iOff = iMax;
		if(iOff <   0)
			iOff = 0;
		if(iOff != (int)pData->uScrollX) {
			pData->uScrollX	= iOff;
			SetScrollPos(pData->hWnd, SB_HORZ, iOff, TRUE);
			UpdateView(pData);
			MoveWindow(pData->hHeader, -iOff, 0, iNum + iOff, pData->uStartPixel, TRUE);
		}
	}
	return iRet;
}

//*****************************************************************************
//*
//*		TreeListIsVisible
//*
//*****************************************************************************
//	Prüft ob ein Eintrag sichtbar ist
//	pData		: Zeiger auf die Fensterdaten
//	uItem		: Ist die Nummer des Eintrages
//	uSub		: Untereintrag der geprüft werden soll
//						0xFFFFFFFF nur Zeile prüfen
//	Ergibt den Zustand des Eintrages:
//				   -1 = Unbekannter Eintrag
//					0 = Eintrag ist zugeklappt
//					1 = Eintrag ist aufgeklappt aber nicht sichtbar
//					2 = Eintrag ist aufgeklappt und teilweise sichtbar
//					3 = Eintrag ist aufgeklappt und Spalte ist nur teilweise sichtbar
//					4 = Eintrag ist aufgeklappt und ganz sichtbar
static int TreeListIsVisible(TreeListData *pData, unsigned uItem, unsigned uSub) {

	BaseItem   *pEntry;
	unsigned	uNext;
	unsigned	uPos;
	int			iNum;
	int			iAnf;
	int			iOff;
	int			iEnd;

	if(uItem > pData->uTreeItemsMax)
		return -1;

	pEntry = pData->pTreeItems[uItem];
	if(!pEntry)
		return -1;

	uPos = pEntry->uShowPos;
	if(!uPos) {												// Ist der Eintrag zugeklappt
		return 0;
	}

	uPos--;
	if(uPos < pData->uScrollY) {								// Vor erster Zeile

		return 1;
	}

	if(uPos >= pData->uScrollY + pData->uMaxEnties) {				// Nach letzter Zeile
		return 1;
	}

	if(uPos == pData->uScrollY + pData->uPageEnties) {			// Auf halbsichtbarer Zeile
		if(uSub < pData->uColumnCount) {
			uNext = pData->aColumn[uSub].bNext;
			iNum  = pData->uSizeX;
			iOff  = pData->uScrollX;
			iAnf  = pData->aColumnXpos[uSub ];
			iEnd  = pData->aColumnXpos[uNext];

			if(iOff + iNum < iAnf)
				return 1;
			if(iOff     >= iEnd)
				return 1;
		}

		return 2;
	}

	if(uSub < pData->uColumnCount) {							// Spalte prüfen
		uNext = pData->aColumn[uSub].bNext;
		iNum  = pData->uSizeX;
		iOff  = pData->uScrollX;
		iAnf  = pData->aColumnXpos[uSub ];
		iEnd  = pData->aColumnXpos[uNext];

		if(iOff + iNum < iAnf)
			return 1;
		if(iOff     >= iEnd)
			return 1;
		if(iOff + iNum < iEnd)
			return 3;
		if(iOff     > iAnf)
			return 3;
	}

	return 4;
}

//*****************************************************************************
//*
//*		TreeListDeleteItem
//*
//*****************************************************************************
//	Löscht einen Eintrag aus dem Fenster
//	pData		: Zeiger auf die Fensterdaten
//	uItem		: Ist die Nummer des Eintrages der gelöscht werden soll
//	iMode		: Wie soll der Eintrag gelöscht werden
//					0 = Eintrag löschen und nicht neu zeichnen
//					1 = Eintrag löschen und neu zeichnen
//					2 = Nur Kindereinträge löschen und neu zeichnen
//	Ergibt 1 wenn der Eintrag gelöscht wurde.
static int TreeListDeleteItem(TreeListData *pData, unsigned uItem, int iMode) {

	NMTREEVIEW	sNotify;
	ExtraItem **pList;
	ExtraItem  *pExtra;
	BaseItem   *pEntry;
	BaseItem   *pTemp;
	unsigned	uPos;
	int			iOff;
	int			iMax;

	if(pData->cLockChanges)
		return 0;

	if(uItem > pData->uTreeItemsMax) {							// Prüfe den Eintrag
		if(uItem != U(TVI_ROOT))
			return 0;					// Alles löschen
		if(pData->uLastChild == 0)
			return 0;

		while(pData->uLastChild) {
			TreeListDeleteItem(pData, pData->uLastChild, 0);
		}

		pData->uItemPosCount = 0;

		UpdateScrollY(pData);
		UpdateView(pData);

		return 1;
	}

	pEntry = pData->pTreeItems[uItem];
	if(!pEntry) {												// Prüfe den Eintrag
		if(uItem != 0)
			return 0;							// Alles löschen
		if(pData->uLastChild == 0)
			return 0;

		while(pData->uLastChild) {
			TreeListDeleteItem(pData, pData->uLastChild, 0);
		}

		pData->uItemPosCount = 0;

		UpdateScrollY(pData);
		UpdateView(pData);

		return 1;
	}

	if(iMode == 2) {											// Nur Kindereinträge löschen
		if(!pEntry->uFirstChild) {
			return 0;
		}

		while(pEntry->uLastChild) {							// Alle Kinder löschen
			TreeListDeleteItem(pData, pEntry->uLastChild, 0);
		}

		uPos = pEntry->uShowPos;
		if(uPos) {
			UpdateItems(pData, uItem);
		}

		return 1;
	}

	while(pEntry->uLastChild) {								// Alle Kinder löschen
		TreeListDeleteItem(pData, pEntry->uLastChild, 0);
	}

	if(uItem == pData->uSelectedItem) {							// Einen ausgewählten Eintrag löschen
		sNotify.hdr.code			= TVN_SELCHANGED;
		sNotify.action				= TVC_UNKNOWN;
		sNotify.itemOld.mask		= TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_SUBITEM | TVIF_PARAM;
		sNotify.itemOld.hItem		= (HTREEITEM)(ULONG_PTR)uItem;
		sNotify.itemOld.stateMask	= 0xFFFFFFFF;
		sNotify.itemOld.state		= pEntry->uState&~TVIS_SELECTED;
		sNotify.itemOld.lParam		= pEntry->lParam;
		sNotify.itemOld.cChildren	= 0;
		sNotify.itemOld.pszText		= (LPTSTR) - 1;
		sNotify.itemOld.cchTextMax	= -1;
		sNotify.itemNew.mask		= TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_SUBITEM | TVIF_PARAM;
		sNotify.itemNew.hItem		= NULL;
		sNotify.itemNew.stateMask	= 0xFFFFFFFF;
		sNotify.itemNew.state		= 0;
		sNotify.itemNew.lParam		= 0;
		sNotify.itemNew.cChildren	= 0;
		sNotify.itemNew.pszText		= (LPTSTR) - 1;
		sNotify.itemNew.cchTextMax	= -1;
		sNotify.ptDrag.x			= 0;
		sNotify.ptDrag.y			= 0;

		UNLOCK(pData);
		SendNotify(pData, &sNotify.hdr);						// Bekant geben das der Eintrag nicht mehr ausgewählt ist
		LOCK(pData);

		pData->uSelectedItem = 0;
		pData->uSelectedSub  = 0;
	}

	sNotify.hdr.code			= TVN_DELETEITEM;
	sNotify.itemNew.mask		= 0;
	sNotify.itemOld.mask		= TVIF_HANDLE | TVIF_PARAM | TVIF_STATE;
	sNotify.itemOld.hItem		= (HTREEITEM)(ULONG_PTR)uItem;
	sNotify.itemOld.lParam		= pEntry->lParam;
	sNotify.itemOld.pszText		= (LPTSTR) - 1;
	sNotify.itemNew.stateMask	= 0xFFFFFFFF;
	sNotify.itemNew.state		= pEntry->uState;
	sNotify.itemOld.cchTextMax	= -1;
	sNotify.ptDrag.x			= 0;
	sNotify.ptDrag.y			= 0;

	UNLOCK(pData);
	SendNotify(pData, &sNotify.hdr);
	LOCK(pData);

	pEntry = pData->pTreeItems[uItem];					// Prüfen ob der Eintrag noch existiert
	if(!pEntry)
		return 0;

	if(uItem == pData->uTrackedItem) {							// Einen unterstrichenen Eintrag löschen
		pData->uTrackedItem  = 0;
		pData->uTrackedSub   = 0;
	}

	if(pData->uInsertMark == uItem) {
		pData->uInsertMark = 0;
	}

	if(pData->uSingleSel == uItem) {
		pData->uSingleSel = 0;
	}

	if(pEntry->uPrevItem) {									// Gibt es einen vorherigen Eintrag
		pTemp			 = pData->pTreeItems[pEntry->uPrevItem];
		pTemp->uNextItem = pEntry->uNextItem;
	} else {
		if(pEntry->uParent) {									// Neues erstes Kind in Elterneintrag
			pTemp			   = pData->pTreeItems[pEntry->uParent];
			pTemp->uFirstChild = pEntry->uNextItem;
		} else {
			pData->uFirstChild = pEntry->uNextItem;
		}
	}

	if(pEntry->uNextItem) {									// Gibt es einen vorherigen Eintrag
		pTemp			 = pData->pTreeItems[pEntry->uNextItem];
		pTemp->uPrevItem = pEntry->uPrevItem;
	} else {
		if(pEntry->uParent) {									// Neues letztes Kind in Elterneintrag
			pTemp			  = pData->pTreeItems[pEntry->uParent];
			pTemp->uLastChild = pEntry->uPrevItem;

			if(pTemp->uFirstChild == 0 && pTemp->uLastChild == 0) {
				pTemp->bFlags &= ~TVIX_HASBUTTON;
			}
		} else {
			pData->uLastChild = pEntry->uPrevItem;
		}
	}

	for(uPos = 1; uPos < pData->uColumnCount; uPos++) {				// Alle Extraeinträge löschen
		pList = pData->pExtraItems[uPos - 1];

		pExtra = pList[uItem];
		if(!pExtra)
			continue;

		pList[uItem] = NULL;

		if(pExtra->pText) {
			pExtra->uTextSize = 0;
			delete(pExtra->pText);
		}

		delete(pExtra);
	}


	pData->pTreeItems[uItem] = NULL;						// Den Eintrag löschen

	if(pEntry->pText) {
		pEntry->uTextSize = 0;
		delete(pEntry->pText);
	}

	if(iMode) {												// Den Eintrag neuzeichnen
		uItem =   pEntry->uPrevItem;
		if(!uItem && !pEntry->uNextItem) {
			uItem = pEntry->uParent;
			if(!uItem)
				uPos = 1;
			else
				uPos = pData->pTreeItems[uItem]->uShowPos;
		} else {
			uPos = pEntry->uShowPos;
		}

		if(uPos) {
			UpdateItems(pData, uItem);
		}
	}

	if(pEntry->uState & TVIS_SELECTED)						// Ausgewählte Einträge runterzählen
		if(pData->uSelectedCount > 0) {
			pData->uSelectedCount--;
		}

	delete(pEntry);

	pData->uTreeItemsCount--;

	iOff  = pData->uScrollY;								// Prüfe die Scrollposition
	iMax  = pData->uItemPosCount;
	iMax -=	pData->uPageEnties - 1;

	if(iOff >= iMax)
		iOff = iMax;
	if(iOff < 0)
		iOff = 0;
	if(iOff != (int)pData->uScrollY) {
		pData->uScrollY	= iOff;
		SetScrollPos(pData->hWnd, SB_VERT, iOff, TRUE);
		UpdateView(pData);
	}

	return 1;
}

//*****************************************************************************
//*
//*		TreeListXorSelectItem
//*
//*****************************************************************************
//	Wählt einen Eintrag ab bzw. an
//	pData		: Zeiger auf die Fensterdaten
//	uItem		: Ist die Nummer des Eintrages der ausgewählt werden soll
//	iMode		: Ist der Grund für die Änderung
//						TVC_BYKEYBOARD
//						TVC_BYMOUSE
//						TVC_UNKNOWN
//	Ergibt		1 wenn der Eintrag ab/angewählt wurde
//				0 wenn der Eintrag nicht verändert wurde
static int TreeListXorSelectItem(TreeListData *pData, unsigned uItem, int iMode) {

	NMTREEVIEW	sNotify;
	BaseItem   *pEntry;
	unsigned	uOld;
	unsigned	uRet;

	pEntry = pData->pTreeItems[uItem];
	if(!pEntry)
		return 0;
	if(uItem == pData->uSelectedItem)
		return 0;

	uOld = pEntry->uState;

	sNotify.hdr.code			= TVN_SELCHANGING;
	sNotify.action				= iMode;
	sNotify.itemNew.mask		= TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_SUBITEM | TVIF_PARAM;
	sNotify.itemNew.hItem		= (HTREEITEM)(ULONG_PTR)uItem;
	sNotify.itemNew.stateMask	= 0xFFFFFFFF;
	sNotify.itemNew.state		= pEntry->uState;
	sNotify.itemNew.lParam		= pEntry->lParam;
	sNotify.itemNew.cChildren	= 0;
	sNotify.itemNew.pszText		= (LPTSTR) - 1;
	sNotify.itemNew.cchTextMax	= -1;
	sNotify.itemOld.mask		= TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_SUBITEM | TVIF_PARAM;
	sNotify.itemOld.hItem		= NULL;
	sNotify.itemOld.stateMask	= 0xFFFFFFFF;
	sNotify.itemOld.state		= 0;
	sNotify.itemOld.lParam		= 0;
	sNotify.itemOld.cChildren	= 0;
	sNotify.itemOld.pszText		= (LPTSTR) - 1;
	sNotify.itemOld.cchTextMax	= -1;
	sNotify.ptDrag.x			= 0;
	sNotify.ptDrag.y			= 0;

	UNLOCK(pData);
	uRet = U(SendNotify(pData, &sNotify.hdr));
	LOCK(pData);

	if(uRet)
		return 0;

	pEntry = pData->pTreeItems[uItem];
	if(!pEntry)
		return 0;
	pEntry->uState ^= TVIS_SELECTED;

	sNotify.hdr.code			= TVN_SELCHANGED;
	sNotify.action				= iMode;
	sNotify.itemNew.mask		= TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_SUBITEM | TVIF_PARAM;
	sNotify.itemNew.hItem		= (HTREEITEM)(ULONG_PTR)uItem;
	sNotify.itemNew.stateMask	= 0xFFFFFFFF;
	sNotify.itemNew.state		= pEntry->uState;
	sNotify.itemNew.lParam		= pEntry->lParam;
	sNotify.itemNew.cChildren	= 0;
	sNotify.itemNew.pszText		= (LPTSTR) - 1;
	sNotify.itemNew.cchTextMax	= -1;
	sNotify.itemOld.mask		= TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_SUBITEM | TVIF_PARAM;
	sNotify.itemOld.hItem		= NULL;
	sNotify.itemOld.stateMask	= 0xFFFFFFFF;
	sNotify.itemOld.state		= 0;
	sNotify.itemOld.lParam		= 0;
	sNotify.itemOld.cChildren	= 0;
	sNotify.itemOld.pszText		= (LPTSTR) - 1;
	sNotify.itemOld.cchTextMax	= -1;
	sNotify.ptDrag.x			= 0;
	sNotify.ptDrag.y			= 0;

	UNLOCK(pData);
	SendNotify(pData, &sNotify.hdr);
	LOCK(pData);

	pEntry = pData->pTreeItems[uItem];
	if(!pEntry)
		return 0;
	if(pEntry->uShowPos) {
		if(pData->uStyleEx & TVS_EX_FULLROWMARK)
			UpdateRow(pData, uItem);
		else
			UpdateRect(pData, uItem, 0);
	}

	if((uOld ^ pEntry->uState)&TVIS_SELECTED) {
		if(pEntry->uState & TVIS_SELECTED)
			pData->uSelectedCount++;
		else
			pData->uSelectedCount--;
	}

	return 1;
}


//*****************************************************************************
//*
//*		TreeListRemoveFocus
//*
//*****************************************************************************
//	Wählt den Focus ab
//	pData		: Zeiger auf die Fensterdaten
static void TreeListRemoveFocus(TreeListData *pData) {

	ExtraItem  *pExtra;
	BaseItem   *pEntry;
	unsigned	uItem;
	unsigned	uSub;

	if(!pData->uFocusItem)
		return;

	uItem  = pData->uFocusItem;
	pEntry = pData->pTreeItems[uItem];

	if(pEntry) {
		pEntry->bFlags &= ~TVIX_FOCUSED;

		uSub = pData->uFocusSub;

		if(uSub) {
			pExtra = pData->pExtraItems[uSub - 1][uItem];
			if(pExtra)
				pExtra->bFlags &= ~TVIX_FOCUSED;
		}

		UpdateRect(pData, uItem, uSub);
	}

	pData->uFocusItem = 0;
	pData->uFocusSub  = 0;
}

//*****************************************************************************
//*
//*		TreeListSetFocus
//*
//*****************************************************************************
//	Wählt den Focus-Eintrag
//	pData		: Zeiger auf die Fensterdaten
//	uItem		: Eintrag für den Focus (0xFFFFFFFF=keine Änderung)
//	uSub		: Spalte für den Focus	(0xFFFFFFFF=keine Änderung)
//	Ergibt 1 wenn der Focus gesetzt wurde, bzw 0 bei einem Fehler
static int  TreeListSetFocus(TreeListData *pData, unsigned uItem, unsigned uSub) {

	ExtraItem  *pExtra;
	BaseItem   *pEntry;
	BaseItem   *pTemp;
	unsigned	uTemp;
	unsigned	uCol;

	if(pData->uFocusItem) {
		if(uSub == 0xFFFFFFFF)
			uSub = pData->uFocusSub;
		if(uItem == 0xFFFFFFFF)
			uItem = pData->uFocusItem;
	} else {
		if(uSub == 0xFFFFFFFF)
			uSub = pData->uSelectedSub;
		if(uItem == 0xFFFFFFFF)
			uItem = pData->uSelectedItem;
	}

	if(pData->uFocusItem == uItem)
		if(pData->uFocusSub == uSub)
			return 1;

	if(!uItem) {												// Focus abwählen
		TreeListRemoveFocus(pData);
		return 1;
	}


	if(uItem > pData->uTreeItemsMax) {							// Den Eintrag prüfen
		return 0;
	}

	pEntry = pData->pTreeItems[uItem];
	if(!pEntry) {
		return 0;
	}

	if(!(pData->uStyleEx & TVS_EX_SUBSELECT))
		uSub = 0;

	if(!(pData->uStyleEx & TVS_EX_MULTISELECT)) {				// Einzel auswahl
		return TreeListSelectItem(pData, uItem, uSub, TVC_UNKNOWN);
	}

	uTemp  = pData->uFocusItem;
	pTemp  = pData->pTreeItems[uTemp];

	if(pTemp) {												// Den alten Eintrag abwählen
		pTemp->bFlags &= ~TVIX_FOCUSED;
		uCol = pData->uFocusSub;

		if(uCol) {
			pExtra = pData->pExtraItems[uCol - 1][uTemp];
			if(pExtra)
				pExtra->bFlags &= ~TVIX_FOCUSED;
		}

		UpdateRect(pData, uItem, uSub);
	}


	if(uSub) {												// Neuen Eintrag wählen
		pExtra = pData->pExtraItems[uSub - 1][uItem];
		if(pExtra)
			pExtra->bFlags |= TVIX_FOCUSED;
	} else {
		pEntry->bFlags |= TVIX_FOCUSED;
	}

	pData->uFocusItem = uItem;
	pData->uFocusSub  = uSub;

	if(pEntry->uState & TVIS_SELECTED) {						// Auch die Auswahl nachziehen
		if(pData->uSelectedItem != uItem) {
			uTemp = pData->uSelectedItem;
			uCol  = pData->uSelectedSub;
		} else {
			uTemp = 0;
			uCol  = pData->uSelectedSub;
		}

		pData->uSelectedItem = uItem;
		pData->uSelectedSub  = uSub;

		if(pData->uStyleEx & TVS_EX_FULLROWMARK) {
			uCol = uSub + 1;
		}

		if(uTemp) {
			if(uCol != uSub)
				UpdateRow(pData, uTemp);
			else
				UpdateRect(pData, uTemp, uCol);
		}


		if(uCol != uSub)
			UpdateRow(pData, uItem);
		else
			UpdateRect(pData, uItem, uCol);
	} else {
		UpdateRect(pData, uItem, uSub);
	}

	return 1;
}

//*****************************************************************************
//*
//*		TreeListSelectItem
//*
//*****************************************************************************
//	Wählt einen Eintrag aus
//	pData		: Zeiger auf die Fensterdaten
//	uItem		: Ist die Nummer des Eintrages der ausgewählt werden soll
//	uSubItem	: Ist die Spalte die gewählt werden soll
//	iMode		: Ist der Grund für die Änderung
//						TVC_BYKEYBOARD
//						TVC_BYMOUSE
//						TVC_UNKNOWN
//						TVC_ONLYFOCUS	(nur der Focus hat sich verändert)
//						TVC_DESELECT	(dieses Flag löscht die alte Auswahl)
//						TVC_UNSELECT	(dieses Flag löscht Auswahl bei MultiSel)
//	Ergibt		2 wenn der Eintrag gewählt und umgeklapt wurde
//				1 wenn der Eintrag gewählt wurde
//				0 wenn der Eintrag nicht gewählt wurde
static int TreeListSelectItem(TreeListData *pData, unsigned uItem, unsigned uSubItem, int iMode) {

	NMTREEVIEW	sNotify;
	ExtraItem  *pExtra;
	BaseItem   *pEntry;
	BaseItem   *pTemp;
	LPARAM		lParam;
	LPARAM		lPaOld;
	unsigned	uState;
	unsigned	uStOld;
	unsigned	uNext;
	unsigned	uPos;
	unsigned	uOld;
	unsigned	uSub;
	unsigned	uRet;
	int			iDel;
	int			iSel;

	uOld = pData->uSelectedItem;
	uSub = pData->uSelectedSub;

	if(uSubItem >= pData->uColumnCount && uSubItem > 0)
		return 0;
	if(uItem   > pData->uTreeItemsMax)
		return 0;
	if(uItem   == uOld)
		if(uSubItem == uSub)
			if(pData->uSelectedCount <= 1 || !(pData->uStyleEx & TVS_EX_MULTISELECT)) {
				return 1;
			}

	if(pData->uStyleEx & TVS_EX_MULTISELECT) {					// Ist die Mehrfachauswahl möglich
		iSel = iMode & TVC_UNSELECT;
		iDel = iMode & TVC_DESELECT;
		if(!iDel) {
			if(pData->uStyleEx & (TVS_EX_FULLROWMARK | TVS_EX_SUBSELECT))
				UpdateRow(pData, uOld);
			else
				UpdateRect(pData, uOld, uSub);

			uOld = 0;
			uSub = 0;
		} else {												// Alle gewählten Einträge abwählen
			if(pData->uSelectedCount > 1 && pData->uTreeItemsMax) {
				for(uPos = pData->uTreeItemsMax; uPos; uPos--) {
					pEntry = pData->pTreeItems[uPos];
					if(!pEntry || !(pEntry->uState & TVIS_SELECTED))
						continue;
					if(TreeListXorSelectItem(pData, uPos, iMode))
						if(!pData->uSelectedCount)
							break;		// Wurden alle Einträge abgewählt
				}
			}
		}
	} else {													// Altes Select löschen
		iMode &= ~TVC_ONLYFOCUS;
		iDel   =  1;
		iSel   =  0;
	}

	iMode &= ~(TVC_DESELECT | TVC_UNSELECT);

	pEntry = pData->pTreeItems[uItem];
	if(!pEntry) {												// Neuen Statatus holen
		if(uItem)
			return 0;
		uState = 0;
		lParam = 0;
	} else {
		uState = pEntry->uState;
		lParam = pEntry->lParam;

		if(uSubItem) {
			uState &= TVIS_BASEFLAGS;
			pExtra  = pData->pExtraItems[uSubItem - 1][uItem];
			if(pExtra)
				uState |= pExtra->uState;
		}
	}

	pTemp = pData->pTreeItems[uOld];
	if(!pTemp) {												// Alten Status holen
		uStOld = 0;
		lPaOld = 0;
	} else {
		uStOld = pTemp->uState;
		lPaOld = pTemp->lParam;

		if(uSub) {
			uStOld &= TVIS_BASEFLAGS;
			pExtra  = pData->pExtraItems[uSub - 1][uOld];
			if(pExtra)
				uStOld |= pExtra->uState;
		}
	}

	sNotify.hdr.code			= TVN_SELCHANGING;
	sNotify.action				= iMode;
	sNotify.itemNew.mask		= TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_SUBITEM | TVIF_PARAM;
	sNotify.itemNew.hItem		= (HTREEITEM)(ULONG_PTR)uItem;
	sNotify.itemNew.stateMask	= 0xFFFFFFFF;
	sNotify.itemNew.state		= uState;
	sNotify.itemNew.lParam		= lParam;
	sNotify.itemNew.cChildren	= uSubItem;
	sNotify.itemNew.pszText		= (LPTSTR) - 1;
	sNotify.itemNew.cchTextMax	= -1;
	sNotify.itemOld.mask		= TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_SUBITEM | TVIF_PARAM;
	sNotify.itemOld.hItem		= (HTREEITEM)(ULONG_PTR)uOld;
	sNotify.itemOld.stateMask	= 0xFFFFFFFF;
	sNotify.itemOld.state		= uStOld;
	sNotify.itemNew.lParam		= lPaOld;
	sNotify.itemOld.cChildren	= uSub;
	sNotify.itemOld.pszText		= (LPTSTR) - 1;
	sNotify.itemOld.cchTextMax	= -1;
	sNotify.ptDrag.x			= 0;
	sNotify.ptDrag.y			= 0;


	UNLOCK(pData);

	if(SendNotify(pData, &sNotify.hdr)) {						// Abfragen ob der Eintrag gewählt werden darf
		LOCK(pData);
		return 0;
	}

	LOCK(pData);

	if(uItem) {												// Prüfen ob der Eintrag noch existiert
		pEntry = pData->pTreeItems[uItem];
		if(!pEntry)
			return 0;
	}

	if(iDel) {
		uOld    = pData->uSelectedItem;
		pTemp	= pData->pTreeItems[uOld];
	}


	if(pTemp) {												// Den alten Eintrag abwählen
		if(pTemp->uShowPos) {									// Den Eintrag neu zeichnen
			if((pData->uStyleEx & TVS_EX_FULLROWMARK) || pData->uSelectedSub)
				UpdateRow(pData, uOld);
			else
				UpdateRect(pData, uOld, uSub);
		}

		if(pTemp->uState & TVIS_SELECTED) {
			uStOld		  &= ~TVIS_SELECTED;
			pTemp->uState &= ~TVIS_SELECTED;

			if(pData->uSelectedCount > 0) {
				pData->uSelectedCount -= 1;
			}
		}

		pData->uSelectedSub    = 0;
		pData->uSelectedItem   = 0;
	} else {
		uOld  = 0;
	}


	if(uItem) {												// Den neuen Eintrag wählen
		if(iSel) {
			if(pEntry->uState & TVIS_SELECTED) {
				uState				  &= ~TVIS_SELECTED;
				pEntry->uState        &= ~TVIS_SELECTED;
				if(pData->uSelectedCount)
					pData->uSelectedCount--;
			}
		} else {
			if(!(pEntry->uState & TVIS_SELECTED)) {
				uState				  |= TVIS_SELECTED;
				pEntry->uState        |= TVIS_SELECTED;
				pData->uSelectedCount += 1;
			}
		}

		if(uSubItem && uSubItem < pData->uColumnCount) {
			pExtra = pData->pExtraItems[uSubItem - 1][uItem];
			if(!pExtra) {
				pExtra  = new(ExtraItem, 1);
				memset(pExtra, 0, sizeof(ExtraItem));
				pExtra->iImage = TV_NOIMAGE;
				pExtra->uState = pEntry->uState & (TVIS_BOLD | TVIS_UNDERLINE);
				pData->pExtraItems[uSubItem - 1][uItem] = pExtra;
			}

			uState  = pExtra->uState;
			uState |= pEntry->uState & TVIS_BASEFLAGS;
		} else {
			uState  = pEntry->uState;
		}

		if(pEntry->uShowPos) {								// Den Eintrag neu zeichnen
			if(pData->uStyleEx & (TVS_EX_FULLROWMARK | TVS_EX_SUBSELECT))
				UpdateRow(pData, uItem);
			else
				UpdateRect(pData, uItem, uSubItem);
		}

		pData->uSelectedSub  = uSubItem;
		pData->uSelectedItem = uItem;
	} else {
		pData->uSelectedItem = 0;
		pData->uSelectedSub  = 0;
		uState				 = 0;
	}

	sNotify.hdr.code			= TVN_SELCHANGED;
	sNotify.action				= iMode;
	sNotify.itemNew.mask		= TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_SUBITEM | TVIF_PARAM;
	sNotify.itemNew.hItem		= (HTREEITEM)(ULONG_PTR)uItem;
	sNotify.itemNew.stateMask	= 0xFFFFFFFF;
	sNotify.itemNew.state		= uState;
	sNotify.itemNew.lParam		= lParam;
	sNotify.itemNew.cChildren	= uSubItem;
	sNotify.itemNew.pszText		= (LPTSTR) - 1;
	sNotify.itemNew.cchTextMax	= -1;
	sNotify.itemOld.mask		= TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_SUBITEM | TVIF_PARAM;
	sNotify.itemOld.hItem		= (HTREEITEM)(ULONG_PTR)uOld;
	sNotify.itemOld.stateMask	= 0xFFFFFFFF;
	sNotify.itemOld.state		= uStOld;
	sNotify.itemOld.lParam		= lPaOld;
	sNotify.itemOld.cChildren	= uSub;
	sNotify.itemOld.pszText		= (LPTSTR) - 1;
	sNotify.itemOld.cchTextMax	= -1;
	sNotify.ptDrag.x			= 0;
	sNotify.ptDrag.y			= 0;

	UNLOCK(pData);
	SendNotify(pData, &sNotify.hdr);
	LOCK(pData);

	if(!(pData->uStyle & TVS_SINGLEEXPAND)) {					// Einzelmodus aktiv
		if(pData->uStyle & TVS_SHOWSELALWAYS)
			if(pData->uSelectedItem) {
				TreeListEnsureVisible(pData, pData->uSelectedItem, pData->uSelectedSub);
			}

		return 1;
	}


	//*****************************************************************************


	sNotify.hdr.code			= TVN_SINGLEEXPAND;
	sNotify.action				= iMode;
	sNotify.itemNew.mask		= TVIF_HANDLE | TVIF_PARAM | TVIF_STATE;
	sNotify.itemNew.hItem		= (HTREEITEM)(ULONG_PTR)uItem;
	sNotify.itemNew.stateMask	= 0xFFFFFFFF;
	sNotify.itemNew.state		= (pEntry) ? pEntry->uState : 0;
	sNotify.itemNew.cChildren	= 0;
	sNotify.itemNew.pszText		= (LPTSTR) - 1;
	sNotify.itemNew.cchTextMax	= -1;
	sNotify.itemOld.mask		= TVIF_HANDLE | TVIF_PARAM | TVIF_STATE;
	sNotify.itemOld.hItem		= (HTREEITEM)(ULONG_PTR)uOld;
	sNotify.itemOld.stateMask	= 0xFFFFFFFF;
	sNotify.itemOld.state		= (pTemp) ? pTemp->uState : 0;
	sNotify.itemOld.cChildren	= 0;
	sNotify.itemOld.pszText		= (LPTSTR) - 1;
	sNotify.itemOld.cchTextMax	= -1;
	sNotify.ptDrag.x			= 0;
	sNotify.ptDrag.y			= 0;

	UNLOCK(pData);
	uRet = U(SendNotify(pData, &sNotify.hdr));					// Anfragen ob die Zweige umgeklappt werden dürfen
	LOCK(pData);


	pTemp	= pData->pTreeItems[uOld ];						// Zeiger neu holen falls es Änderungen gab
	pEntry	= pData->pTreeItems[uItem];

	while(pTemp && pEntry) {									// Beide Zweige sysnchronisieren
		if(pEntry->uLevel > pTemp->uLevel) {
			uNext  = pEntry->uParent;

			if(!(uRet & TVNRET_SKIPNEW))
				if(!(pEntry->uState & TVIS_EXPANDED)) {
					TreeListToggleItem(pData, uItem, 0);
				}

			pEntry = pData->pTreeItems[uNext];
			uItem  = uNext;

			if(!uItem)
				break;

			continue;
		}

		if(uItem == uOld)
			goto EndSel;							// Bis zum gleichen Knoten

		uNext  = pTemp->uParent;

		if(!(uRet & TVNRET_SKIPOLD))
			if(pTemp->uState & TVIS_EXPANDED) {
				TreeListToggleItem(pData, uOld, 0);
			}

		pTemp  = pData->pTreeItems[uNext];
		uOld   = uNext;
	}

	if(!uItem) {
		if(!(uRet & TVNRET_SKIPOLD))
			while(pTemp) {										// Alten Zweig zuklappen
				uNext = pTemp->uParent;

				if(pTemp->uState & TVIS_EXPANDED) {
					TreeListToggleItem(pData, uOld, 0);
				}

				pTemp = pData->pTreeItems[uNext];
				uOld  = uNext;
			}

		goto EndSel;
	}

	if(!uOld) {
		if(!(uRet & TVNRET_SKIPNEW))
			while(pEntry) {										// Neuen Zweig aufklappen
				uNext = pEntry->uParent;

				if(!(pEntry->uState & TVIS_EXPANDED)) {
					TreeListToggleItem(pData, uItem, 0);
				}

				pEntry = pData->pTreeItems[uNext];
				uItem  = uNext;
			}
	}

EndSel:

	if(pData->uStyle & TVS_SHOWSELALWAYS)
		if(pData->uSelectedItem) {
			TreeListEnsureVisible(pData, pData->uSelectedItem, pData->uSelectedSub);
		}

	return 2;
}

//*****************************************************************************
//*
//*		TreeListSelectChilds
//*
//*****************************************************************************
//	Wählt einen Eintrag aus
//	pData		: Zeiger auf die Fensterdaten
//	uItem		: Ist die Nummer des Eintrages der ausgewählt werden soll
//	iMode		: Bit 0 = Untereintäge auch ändern
//				  Bit 1 = Einträge abwählen
//	Ergibt 1 wenn die Auswahl funktioniert hat, bzw. 0 bei einem Fehler
static int TreeListSelectChilds(TreeListData *pData, unsigned uItem, int iMode) {

	BaseItem   *pEntry;
	unsigned	uLevel;
	unsigned	uXor;

	if(!(pData->uStyleEx & TVS_EX_MULTISELECT))
		return 0;

	uLevel = 0;

	if(uItem == U(TVI_ROOT)) {
		uItem  = pData->uFirstChild;
	} else {
		if(uItem > pData->uTreeItemsMax)
			return 0;
	}

	if(!pData->pTreeItems[uItem]) {
		return 0;
	}


	uXor   = (iMode & TVIS_DESELECT) ? 0 : TVIS_SELECTED;
	iMode &= TVIS_WITHCHILDS;

	for(;;) {
		pEntry = pData->pTreeItems[uItem];

		if((pEntry->uState ^ uXor)&TVIS_SELECTED) {
			TreeListXorSelectItem(pData, uItem, TVC_UNKNOWN);
		}

		if(iMode && pEntry->uFirstChild) {					// Auch Kinder ändern
			uItem = pEntry->uFirstChild;
			uLevel++;
			continue;
		}

		for(;;) {												// Eine Ebene höher
			uItem = pEntry->uNextItem;
			if(uItem != 0)
				break;
			if(uLevel == 0)
				return 1;

			uLevel--;

			uItem  = pEntry->uParent;
			pEntry = pData->pTreeItems[uItem];
		}
	}
}

//*****************************************************************************
//*
//*		TreeListInsertItem
//*
//*****************************************************************************
//	Fügt einen Eintrag ins Fenster ein
//	pData		: Zeiger auf die Fensterdaten
//	pInsert		: Zeiger auf die ein zu fügenden Daten
//	Ergibt die Einfügeposition des neuen Eintrages oder 0 bei einem Fehler
static unsigned TreeListInsertItem(TreeListData *pData, TV_INSERTSTRUCT *pInsert) {

	char	   *pTemp;
	BYTE		bFlag;
	LPCTSTR	    pText;
	LPCTSTR	    pTextTemp;
	PFNTVSORTEX pCompare;
	ExtraItem **pExOld[MAX_COLUMNS];
	ExtraItem **pExNew[MAX_COLUMNS];
	BaseItem   *pNew;
	BaseItem  **pOld;
	BaseItem  **pItems;
	BaseItem   *pEntry;
	BaseItem   *pParent;
	unsigned   *pPosNew;
	unsigned   *pPosOld;
	unsigned   *pFirst;
	unsigned   *pLast;
	unsigned	uBefore;
	unsigned	uParent;
	unsigned	uAfter;
	unsigned	uFirst;
	unsigned	uSize;
	unsigned	uBits;
	unsigned	uItem;
	unsigned	uNext;
	unsigned	uMax;
	unsigned	uPos;
	unsigned	uNum;
	int			iCmp;
	int			iNone;
	int			iCount;
	int			iShift;

	if(pData->cLockChanges)
		return 0;

	uParent = U(pInsert->hParent);
	if(uParent > pData->uTreeItemsMax) {						// Prüfe das Elternelement
		if(pInsert->hParent != TVI_ROOT) {
			return 0;
		}

		pParent = NULL;
	} else {
		pParent = pData->pTreeItems[uParent];
		if(!pParent) {
			if(uParent)
				return 0;
			pParent = NULL;
		}
	}

	if(pData->uTreeItemsCount + 1 > pData->uTreeItemsMax) {		// Größe der Liste erhöhen
		pPosOld = pData->pItemPos;
		pOld	= pData->pTreeItems;
		uMax	= pData->uTreeItemsMax;
		uMax   += pData->uTreeItemsMax / 2;
		uMax   += 64;
		pItems  = new(BaseItem*, uMax + 1);

		if(!pItems) {
			return 0;
		}

		pPosNew = new(unsigned, uMax);
		if(!pPosNew) {
			delete(pItems);
			return 0;
		}

		for(uPos = 1; uPos < pData->uColumnCount; uPos++) {
			pExOld[uPos] = pData->pExtraItems[uPos - 1];
			pExNew[uPos] = new(ExtraItem*, uMax + 1);

			if(!pExNew[uPos]) {
				for(uPos--; uPos > 0; uPos--)
					delete(pExNew[uPos]);
				delete(pPosNew);
				delete(pItems);
				return 0;
			}
		}

		memcpy(pItems , pData->pTreeItems       , sizeof(BaseItem *) * (pData->uTreeItemsMax + 1));
		memset(pItems + pData->uTreeItemsMax + 1, 0, sizeof(BaseItem *) * (uMax - pData->uTreeItemsMax));
		memcpy(pPosNew, pData->pItemPos         , sizeof(unsigned) * (pData->uTreeItemsCount));
		memset(pPosNew + pData->uTreeItemsCount, 0, sizeof(unsigned) * (uMax - pData->uTreeItemsCount));

		for(uPos = 1; uPos < pData->uColumnCount; uPos++) {
			memcpy(pExNew[uPos], pExOld[uPos]            , sizeof(ExtraItem *) * (pData->uTreeItemsMax + 1));
			memset(pExNew[uPos] + pData->uTreeItemsMax + 1, 0, sizeof(ExtraItem *) * (uMax - pData->uTreeItemsMax));
			pData->pExtraItems[uPos - 1] = pExNew[uPos];
			delete(pExOld[uPos]);
		}

		pData->uTreeItemsMax = uMax;
		pData->pTreeItems	 = pItems;
		pData->pItemPos		 = pPosNew;
		delete(pPosOld);
		delete(pOld);
	}

	//******************** Den neuen Eintrag erzeugen *****************************
	pItems	= pData->pTreeItems;
	uPos	= pData->uNextSeachPos + 1;
	pTemp	= new(char, sizeof(BaseItem) + pData->uUserDataSize);
	pNew	= (BaseItem *)pTemp;

	if(!pNew) {												// Konnte der Speicher reserviert werden
		return 0;
	}

	if(pData->uUserDataSize) {								// Die Userdaten auf 0 setzen
		memset(pTemp + sizeof(BaseItem), 0, pData->uUserDataSize);
	}

	for(;; uPos++) {											// Suche freie Position
		if(uPos > pData->uTreeItemsMax)
			uPos = 1;
		if(pItems[uPos] == NULL)
			break;
	}

	pData->uNextSeachPos = uPos;

	memset(pNew, 0, sizeof(BaseItem));						// Erstelle den neuen Eintrag
	pNew->iImage = TV_NOIMAGE;
	pNew->iSelectedImage = TV_NOIMAGE;

	uBits = pInsert->item.mask;

	if(uBits & TVIF_STATE) {
		pNew->uState = pInsert->item.state & pInsert->item.stateMask;
	} else {
		if(pData->uStyle  & TVS_CHECKBOXES)
			if(!(pData->uStyleEx & TVS_EX_BITCHECKBOX)) {
				pNew->uState = 0x1000;
			}
	}

	if(uBits & TVIF_PARAM) {
		pNew->lParam = pInsert->item.lParam;
	}

	if(uBits & TVIF_IMAGE) {
		pNew->iImage = pInsert->item.iImage;
		if(pNew->iImage == I_IMAGECALLBACK)
			pNew->bCallback |= TVIF_IMAGE;
	}

	if(uBits & TVIF_SELECTEDIMAGE) {
		pNew->iSelectedImage = pInsert->item.iSelectedImage;
		if(pNew->iSelectedImage == I_IMAGECALLBACK)
			pNew->bCallback |= TVIF_SELECTEDIMAGE;
	}

	if(uBits & TVIF_CHILDREN) {									// Art der Schaltflächen
		switch(pInsert->item.cChildren) {
			case  0:
				break;
			case  1:
				pNew->bFlags   |= TVIX_HASBUTTON;
				break;
			case  I_CCB:
				pNew->bCallback |= TVIF_CHILDREN;
				break;
			default
					:
				pNew->bFlags   |= TVIX_VARBUTTON;
				break;
		}
	} else {
		pNew->bFlags |= TVIX_VARBUTTON;
	}

	if(pData->uStyle & TVS_SINGLEEXPAND) {						// Nicht aufklappen bei Einzelmodus
		pNew->uState &= ~TVIS_EXPANDED;
	}

	if(uBits & TVIF_TEXT) {										// Text einfügen
		if(pInsert->item.pszText == LPSTR_TEXTCALLBACK) {
			pNew->bCallback |= TVIF_TEXT;
			pNew->uTextSize = 0;
			pNew->pText		= 0;
		} else {
			pNew->uTextSize	= (WORD)str_len(pInsert->item.pszText);
			pNew->pText		= new(TCHAR, pNew->uTextSize + 1);
			memcpy(pNew->pText, pInsert->item.pszText, sizeof(TCHAR) * (pNew->uTextSize + 1));
		}
	} else {
		pNew->pText		= new(TCHAR, 1);
		pNew->pText[0]	= 0;
		pNew->uTextSize	= 0;
	}

	if(!pParent) {											// Einen Root-Eintrag einfügen
		pNew->uParent	= 0;
		uParent			= 0;
		bFlag			= 0;
		uFirst			= 0xFFFFFFFF;
		pFirst			= &pData->uFirstChild;
		pLast			= &pData->uLastChild;
	} else {													// Einen Tree-Eintrag einfügen
		pNew->uParent	=  uParent;
		pNew->uLevel	=  pParent->uLevel + 1;
		uFirst			=  pParent->uFirstChild;
		pFirst			= &pParent->uFirstChild;
		pLast			= &pParent->uLastChild;
		bFlag			=  pParent->bFlags;

		if(pParent->bFlags & TVIX_VARBUTTON) {
			pParent->bFlags |= TVIX_HASBUTTON;
		}
	}

	//******************** Eintrage einfügen **************************************
	uAfter = U(pInsert->hInsertAfter);

	switch(uAfter) {
		case U(TVI_BEFORE):										// Nach einem Eintrag einfügen
			if(pParent) {										// Einen Root-Eintrag einfügen
				pEntry  = pParent;
				pParent ->bFlags  = bFlag;
				uParent = pParent->uParent;
				pParent = pItems  [uParent];

				if(!pParent) {
					pNew->uParent	= 0;
					pNew->uLevel	= 0;
					uParent			= 0;
					uFirst			= 0xFFFFFFFF;
					pFirst			= &pData->uFirstChild;
					pLast			= &pData->uLastChild;
				} else {										// Einen Tree-Eintrag einfügen
					pNew->uParent	=  uParent;
					pNew->uLevel	=  pParent->uLevel + 1;
					uFirst			=  pParent->uFirstChild;
					pFirst			= &pParent->uFirstChild;
					pLast			= &pParent->uLastChild;

					if(pParent->bFlags & TVIX_VARBUTTON) {
						pParent->bFlags |= TVIX_HASBUTTON;
					}
				}

				if(pEntry->uPrevItem) {
					uAfter = pEntry->uPrevItem;
					goto DoInsert;
				}
			}

		case U(TVI_FIRST):										// Am Anfang einfügen
			if(pFirst[0]) {									// Gibt es schon Einträge
				pEntry = pItems[pFirst[0]];
				pEntry->uPrevItem = uPos;
			} else {
				pFirst[0] = uPos;
				pLast [0] = uPos;
				break;
			}

			pNew ->uNextItem = pFirst[0];					// Eintrag einfügen
			pFirst[0]		 = uPos;
			break;

		case U(TVI_ROOT):										// Als Root-Eintrag einfügen
			pNew->uParent	= 0;
			uParent			= 0;
			pFirst			= &pData->uFirstChild;
			pLast			= &pData->uLastChild;

		case U(TVI_LAST):										// Am Ende einfügen
			if(pLast[0]) {									// Gibt es schon Einträge
				pEntry = pItems[pLast[0]];
				pEntry->uNextItem = uPos;
			} else {
				pFirst[0] = uPos;
				pLast [0] = uPos;
				break;
			}

			pNew ->uPrevItem = pLast[0];					// Eintrag einfügen
			pLast[0]		 = uPos;
			break;


		case U(TVI_SORTEX):										// Einfügen mittels Funktion
			uItem = pFirst[0];
			if(!uItem) {										// Gibt es keine Kindeinträge
				pFirst[0] = uPos;
				pLast [0] = uPos;
				break;
			}

			if(pNew->bCallback & TVIF_TEXT) {					// Text über Callback holen
				uSize = 1;
				LOCK(pData);
				CallbackEntry(pData, pNew, uPos, TVIF_TEXT, &iNone, &uSize, &pText);
				UNLOCK(pData);
			} else {
				pText = pNew->pText;
			}

			pData->cLockChanges = 1;

			pCompare = (PFNTVSORTEX)(pInsert->item.hItem);
			if(!pCompare)
				break;
			uNext	 = uItem;
			iCount	 = 0;
			uBefore  = 0;

			while(uNext) {									// Zähle die Einträge
				iCount++;
				uNext = pItems[uNext]->uNextItem;
			}

			while(iCount > 0) {											// Binary-Seach Algorithnus
				iShift = iCount / 2;
				uNext  = uItem;

				while(iShift > 0) {
					uNext  = pItems[uNext]->uNextItem;
					iShift--;
				}

				pEntry  = pItems[uNext];
				if(pEntry->bCallback & TVIF_TEXT) {				// Text über Callback holen
					uSize = 0;
					LOCK(pData);
					CallbackEntry(pData, pEntry, uItem, TVIF_TEXT, &iNone, &uSize, &pTextTemp);
					UNLOCK(pData);
				} else {
					pTextTemp = pEntry->pText;
				}

				iCmp = pCompare(pData->hWnd, (HTREEITEM)(ULONG_PTR)uNext, pTextTemp, pText, pEntry->lParam, pInsert->item.lParam);
				if(iCmp < 0) {
					iCount -= (iCount + 1) / 2;
					continue;
				}

				if(iCmp > 0) {
					iCount -= iCount / 2 + 1;
					uBefore = uNext;
					uItem   = pItems[uNext]->uNextItem;
					continue;
				}

				uBefore	= pEntry->uPrevItem;
				uItem	= uNext;
				break;
			}

			pData->cLockChanges = 0;

			pNew->uNextItem = uItem;
			pNew->uPrevItem = uBefore;

			if(uBefore) {										// Vorherigen Eintrag anpassen
				pEntry = pItems[uBefore];
				pEntry->uNextItem = uPos;
			} else {											// Am Anfang einfügen
				pFirst[0] = uPos;
			}

			if(uItem) {										// Nächsten Eintrag anpassen
				pEntry = pItems[uItem];
				pEntry->uPrevItem = uPos;
			} else {											// Am Ende anhängen
				pLast[0] = uPos;
			}
			break;

		case U(TVI_SORT):										// Alphapetisch einfügen
			uItem = pFirst[0];
			if(!uItem) {										// Gibt es keine Kindeinträge
				pFirst[0] = uPos;
				pLast [0] = uPos;
				break;
			}

			if(pNew->bCallback & TVIF_TEXT) {					// Text über Callback holen
				uSize = 1;
				LOCK(pData);
				CallbackEntry(pData, pNew, uPos, TVIF_TEXT, &iNone, &uSize, &pText);
				UNLOCK(pData);
			} else {
				pText = pNew->pText;
			}

			pData->cLockChanges = 1;

			uNext	= uItem;
			iCount	= 0;
			uBefore = 0;

			while(uNext) {									// Zähle die Einträge
				iCount++;
				uNext = pItems[uNext]->uNextItem;
			}

			while(iCount > 0) {											// Binary-Seach Algorithnus
				iShift = iCount / 2;
				uNext  = uItem;

				while(iShift > 0) {
					uNext  = pItems[uNext]->uNextItem;
					iShift--;
				}


				pEntry  = pItems[uNext];
				if(pEntry->bCallback & TVIF_TEXT) {				// Text über Callback holen
					uSize = 0;
					LOCK(pData);
					CallbackEntry(pData, pEntry, uItem, TVIF_TEXT, &iNone, &uSize, &pTextTemp);
					UNLOCK(pData);
				} else {
					pTextTemp = pEntry->pText;
				}

				iCmp = str_icmp(pText, pTextTemp);

				if(iCmp < 0) {
					iCount -= (iCount + 1) / 2;
					continue;
				}

				if(iCmp > 0) {
					iCount -= iCount / 2 + 1;
					uBefore = uNext;
					uItem   = pItems[uNext]->uNextItem;
					continue;
				}

				uBefore	= pEntry->uPrevItem;
				uItem	= uNext;
				break;
			}


			pData->cLockChanges = 0;

			pNew->uNextItem = uItem;
			pNew->uPrevItem = uBefore;

			if(uBefore) {										// Vorherigen Eintrag anpassen
				pEntry = pItems[uBefore];
				pEntry->uNextItem = uPos;
			} else {											// Am Anfang einfügen
				pFirst[0] = uPos;
			}

			if(uItem) {										// Nächsten Eintrag anpassen
				pEntry = pItems[uItem];
				pEntry->uPrevItem = uPos;
			} else {											// Am Ende anhängen
				pLast[0] = uPos;
			}
			break;

		case U(TVI_AFTER):										// Nach einem Eintrag einfügen
			uAfter  = uParent;

			if(pParent) {										// Einen Root-Eintrag einfügen
				pParent ->bFlags  = bFlag;
				uParent = pParent->uParent;
				pParent = pItems  [uParent];

				if(!pParent) {
					pNew->uParent	= 0;
					pNew->uLevel	= 0;
					uParent			= 0;
					uFirst			= 0xFFFFFFFF;
					pFirst			= &pData->uFirstChild;
					pLast			= &pData->uLastChild;
				} else {										// Einen Tree-Eintrag einfügen
					pNew->uParent	=  uParent;
					pNew->uLevel	=  pParent->uLevel + 1;
					uFirst			=  pParent->uFirstChild;
					pFirst			= &pParent->uFirstChild;
					pLast			= &pParent->uLastChild;

					if(pParent->bFlags & TVIX_VARBUTTON) {
						pParent->bFlags |= TVIX_HASBUTTON;
					}
				}
			}

		default
				:												// Hinter einen Eintrag einfügen
DoInsert:
			uItem = pFirst[0];
			if(!uItem) {										// Gibt es keine Kindeinträge
				pFirst[0] = uPos;
				pLast [0] = uPos;
				break;
			}

			if(uAfter > pData->uTreeItemsMax) {
				if((uAfter & 0xFFF00000) == 0xFFE00000) {			// In einer genauen Reihe nach Patent einfügen
					uAfter &= 0xFFFFF;

					uItem = pFirst[0];
					if(!uItem) {								// Gibt es keine Kindeinträge
						pFirst[0] = uPos;
						pLast [0] = uPos;
						break;
					}

					if(uAfter == 0) {							// In die erste Reihe einfügen
						pEntry = pItems[uItem];
						pEntry->uPrevItem = uPos;
						pNew  ->uNextItem = uItem;
						pFirst[0]		  = uPos;
						break;
					}

					uNum    = 1;
					uBefore	= 0;
					// Suche Einfügereihe
					for(; uItem; uItem = pItems[uItem]->uNextItem) {
						uBefore = uItem;

						if(uNum == uAfter) {
							uItem = pItems[uItem]->uNextItem;
							break;
						}

						uNum++;
					}

					pNew->uNextItem = uItem;
					pNew->uPrevItem = uBefore;

					if(uBefore) {								// Vorherigen Eintrag anpassen
						pEntry = pItems[uBefore];
						pEntry->uNextItem = uPos;
					} else {									// Am Anfang einfügen
						pFirst[0] = uPos;
					}

					if(uItem) {								// Nächsten Eintrag anpassen
						pEntry = pItems[uItem];
						pEntry->uPrevItem = uPos;
					} else {									// Am Ende anhängen
						pLast[0] = uPos;
					}

					break;
				}

				pEntry  = NULL;
			} else {
				pEntry  = pItems[uAfter];
			}

			if(pEntry && uParent == pEntry->uParent) {			// Stimmt der Elterneintrag ?
				uItem   = pEntry->uNextItem;
				uBefore = uAfter;
			} else {
				uItem   = 0;
				uBefore = pLast[0];
				pEntry  = pItems[uBefore];
			}

			pNew->uNextItem = uItem;
			pNew->uPrevItem = uBefore;

			if(uBefore) {										// Vorherigen Eintrag anpassen
				pEntry->uNextItem = uPos;
			} else {											// Am Anfang einfügen
				pFirst[0] = uPos;
			}

			if(uItem) {										// Nächsten Eintrag anpassen
				pEntry = pItems[uItem];
				pEntry->uPrevItem = uPos;
			} else {											// Am Ende anhängen
				pLast[0] = uPos;
			}

			break;
	}

	pItems[uPos] = pNew;
	pData->uTreeItemsCount++;
	// Die Anzeigezeilen akualisieren
	if(!pParent || !uFirst || (pParent->uState & TVIS_EXPANDED)) {
		uItem = pNew->uPrevItem;
		if(!uItem)
			uItem = uParent;

		if(!uItem)
			UpdateItems(pData, 0);
		else {
			pEntry = pItems[uItem];
			if(pEntry && pEntry->uShowPos)
				UpdateItems(pData, uItem);
		}
	}

	if(pNew->uState & TVIS_SELECTED) {							// Den ausgewählten Eintrag auswählen
		TreeListSelectItem(pData, uPos, 0, TVC_UNKNOWN);
	}

	return uPos;
}

//*****************************************************************************
//*
//*		TreeListSetItem
//*
//*****************************************************************************
//	Ändert einen Eintrag im Fenster
//	pData		: Zeiger auf die Fensterdaten
//	pItem		: Zeiger auf die ein zu ändernden Daten
//	Ergibt 1 wenn ok oder 0 bei einem Fehler
static int TreeListSetItem(TreeListData *pData, const TV_ITEM *pItem) {

	BYTE			bCall;
	BYTE			bFlags;
	ExtraItem	  **pList;
	ExtraItem	   *pExtra;
	BaseItem	   *pEntry;
	unsigned		uChange;
	unsigned		uMask;
	unsigned		uBits;
	unsigned		uItem;
	unsigned		uSub;
	unsigned		uLen;
	int				iVal;
	int				iRet;

	uChange = 0;

	uItem = U(pItem->hItem);
	if(uItem > pData->uTreeItemsMax)
		return 0;

	pEntry = pData->pTreeItems[uItem];
	if(!pEntry)
		return 0;

	uBits = pItem->mask;

	if(uBits & TVIF_SUBITEM) {									// Einen Extraeintrag ändern
		uSub = pItem->cChildren;
		if(uSub > 0) {
			if(uSub >= pData->uColumnCount)
				return 0;
			pList	= pData->pExtraItems[uSub - 1];
			pExtra	= pList[uItem];

			if(!pExtra) {										// Einen neuen Eintrag erzeugen
				pExtra  = new(ExtraItem, 1);
				memset(pExtra, 0, sizeof(ExtraItem));
				pExtra->iImage = TV_NOIMAGE;
				pExtra->uState = pEntry->uState & (TVIS_BOLD | TVIS_UNDERLINE);
				pList[uItem]   = pExtra;
			}

			if(uBits & TVIF_PARAM) {
				pEntry->lParam	= pItem->lParam;
			}

			if((uBits & TVIF_IMAGE) && pExtra->iImage != pItem->iImage) {
				if(pData->hImages)
					uChange = 1;
				pExtra->iImage = pItem->iImage;
				if(pExtra->iImage == I_IMAGECALLBACK)
					pExtra->bCallback |= TVIF_IMAGE;
				else
					pExtra->bCallback &= TVIF_IMAGE;
			}

			if(uBits & TVIF_TEXT) {								// Einen neuen Text einstellen
				if(pItem->pszText == LPSTR_TEXTCALLBACK) {
					if(pExtra->pText)
						delete(pExtra->pText);
					pExtra->bCallback |= TVIF_TEXT;
					pExtra->uTextSize = 0;
					pExtra->pText	  = 0;
					uChange			  = 1;
				} else {
					uLen = str_len(pItem->pszText);

					if(uLen > pExtra->uTextSize || !pExtra->pText) {
						if(pExtra->pText)
							delete(pExtra->pText);
						pExtra->pText = new(TCHAR, uLen + 1);
					}

					memcpy(pExtra->pText, pItem->pszText, (uLen + 1)*sizeof(TCHAR));
					pExtra->bCallback  &= ~TVIF_TEXT;
					pExtra->uTextSize   = (WORD)uLen;
					pExtra->iTextPixels = 0;
					uChange = 1;
				}
			}

			if(uBits & TVIF_STATE) {							// Den Status ändern
				uMask			=  pItem->stateMask&~TVIS_BASEFLAGS;
				uBits			=  uMask & (pExtra->uState ^ pItem->state);
				uBits		   |= (pItem->stateMask & TVIS_BASEFLAGS) & (pEntry->uState ^ pItem->state);
				pExtra->uState &= ~uMask;
				pExtra->uState |=  uMask & pItem->state;

				if((uBits & (TVIS_OVERLAYMASK | TVIS_CUT)) && (pData->hImages || pData->aColumn[uSub].bEdit >= TVAX_CHECK)) {
					uChange = 1;								// Ein Icon hats sich verändert
				}

				if(uBits & (TVIS_BOLD | TVIS_DROPHILITED)) {
					pExtra->iTextPixels = 0;
					uChange = 1;
				}

				if((uBits & TVIS_EXPANDED) && pEntry->uFirstChild) {
					iVal = TreeListToggleItem(pData, uItem, 0);
					if(iVal < 0)
						return 0;

					pEntry = pData->pTreeItems[uItem];
					if(!pEntry)
						return 0;
				}

				if(uBits & TVIS_SELECTED) {						// Hat sich die Auswahl geändert
					iVal = (pData->uStyleEx & TVS_EX_SUBSELECT) ? uSub : 0;

					if(pItem->state & TVIS_SELECTED) {
						iRet = TreeListSelectItem(pData, uItem, iVal, TVC_UNKNOWN);
					} else
						if(pData->uStyleEx & TVS_EX_MULTISELECT) {
							TreeListSelectItem(pData, 0  , 0, TVC_UNKNOWN);
							iRet = TreeListXorSelectItem(pData, uItem, TVC_UNKNOWN);
						} else {
							iRet = TreeListSelectItem(pData, 0, 0, TVC_UNKNOWN);
						}

					pEntry = pData->pTreeItems[uItem];
					if(!pEntry)
						return 0;

					if(iRet >= 2) {
						pList	= pData->pExtraItems[uSub - 1];
						pExtra	= pList[uItem];
						if(!pExtra)
							return 0;
					} else
						if(iRet == 1) {
							uChange = 1;
						}
				}
			}

			if(!uChange || !pEntry->uShowPos)
				return 1;		// Neuzeichnen des Eintrages

			UpdateRect(pData, uItem, uSub);

			return 1;
		}

		uBits &= ~TVIF_CHILDREN;
	}

	//******************** Einen Basis Eintrag ändern *****************************
	if(uBits & TVIF_PARAM) {
		pEntry->lParam	= pItem->lParam;
	}

	if((uBits & TVIF_IMAGE) && pEntry->iImage != pItem->iImage) {
		pEntry->iImage = pItem->iImage;
		if(!(pEntry->uState & TVIS_SELECTED) && pData->hImages)
			uChange = 1;
		if(pEntry->iImage == I_IMAGECALLBACK)
			pEntry->bCallback |= TVIF_IMAGE;
		else
			pEntry->bCallback &= TVIF_IMAGE;
	}

	if((uBits & TVIF_SELECTEDIMAGE) && pEntry->iSelectedImage != pItem->iSelectedImage) {
		pEntry->iSelectedImage = pItem->iSelectedImage;
		if((pEntry->uState & TVIS_SELECTED) && pData->hImages)
			uChange = 1;
		if(pEntry->iSelectedImage == I_IMAGECALLBACK)
			pEntry->bCallback |= TVIF_SELECTEDIMAGE;
		else
			pEntry->bCallback &= TVIF_SELECTEDIMAGE;
	}

	if(uBits & TVIF_CHILDREN) {
		bCall  = pEntry->bCallback;
		bFlags = pEntry->bFlags;

		switch(pItem->cChildren) {
			case  0:
				pEntry->bCallback &= ~TVIF_CHILDREN;
				pEntry->bFlags    &= ~TVIX_HASBUTTON;
				pEntry->bFlags    |=  TVIX_VARBUTTON;
				break;

			case  1:
				pEntry->bCallback &= ~TVIF_CHILDREN;
				pEntry->bFlags    &=  TVIX_VARBUTTON;
				pEntry->bFlags    |=  TVIX_HASBUTTON;
				break;

			case  I_CCB:
				pEntry->bCallback |=  TVIF_CHILDREN;
				pEntry->bFlags    &= ~TVIX_VARBUTTON;
				break;

			default
					:
				pEntry->bCallback &= ~TVIF_CHILDREN;
				pEntry->bFlags    |=  TVIX_VARBUTTON;

				if(pEntry->uFirstChild)
					pEntry->bFlags |= TVIX_HASBUTTON;
				else
					pEntry->bFlags &= ~TVIX_HASBUTTON;
		}

		if(bCall != pEntry->bCallback || bFlags != pEntry->bFlags) {
			uChange = 1;
		}
	}

	if(uBits & TVIF_TEXT) {										// Einen neuen Text einstellen
		if(pItem->pszText == LPSTR_TEXTCALLBACK) {
			if(pEntry->pText)
				delete(pEntry->pText);
			pEntry->bCallback |= TVIF_TEXT;
			pEntry->uTextSize = 0;
			pEntry->pText	  = 0;
			uChange			  = 1;
		} else {
			uLen = str_len(pItem->pszText);

			if(uLen > pEntry->uTextSize) {
				if(pEntry->pText)
					delete(pEntry->pText);
				pEntry->pText = new(TCHAR, uLen + 1);
			}

			memcpy(pEntry->pText, pItem->pszText, (uLen + 1)*sizeof(TCHAR));
			pEntry->bCallback  &= ~TVIF_TEXT;
			pEntry->uTextSize   = (WORD)uLen;
			pEntry->iTextPixels = 0;
			uChange = 1;
		}
	}

	if(uBits & TVIF_STATE) {
		uMask = pItem->stateMask;

		if(pData->uStyle & TVS_SINGLEEXPAND) {					// Nicht aufklappen bei Einzelmodus
			uMask &= ~TVIS_EXPANDED;
		}

		uBits			=  uMask & (pEntry->uState ^ pItem->state);
		pEntry->uState &= ~uMask;
		pEntry->uState |=  uMask & pItem->state;


		if((uBits & (TVIS_OVERLAYMASK | TVIS_CUT)) && pData->hImages) {
			uChange = 1;
		}

		if(uBits & TVIS_STATEIMAGEMASK) {						// Haben sich die State-Bits verändert
			if(pData->hStates) {
				uChange = 1;
			}

			if(pData->uStyleEx & TVS_EX_BITCHECKBOX) {
				if(pEntry->uState & 0x1000) {
					pData->uSingleSel = uItem;
				} else
					if(pData->uSingleSel == uItem) {
						pData->uSingleSel = 0;
					}
			} else {
				if((pEntry->uState & TVIS_STATEIMAGEMASK) == 0x2000) {
					pData->uSingleSel = uItem;
				} else
					if(pData->uSingleSel == uItem) {
						pData->uSingleSel = 0;
					}
			}
		}

		if(uBits & (TVIS_BOLD | TVIS_DROPHILITED)) {
			pEntry->iTextPixels = 0;
			uChange = 1;
		}

		if(uBits & TVIS_SELECTED) {								// Hat sich die Auswahl geändert
			pEntry->uState ^= TVIS_SELECTED;

			if(pItem->state & TVIS_SELECTED) {
				iRet = TreeListSelectItem(pData, uItem, 0, TVC_UNKNOWN);
			} else
				if(pData->uStyleEx & TVS_EX_MULTISELECT) {
					TreeListSelectItem(pData, 0  , 0, TVC_UNKNOWN);
					iRet = TreeListXorSelectItem(pData, uItem, TVC_UNKNOWN);
				} else {
					iRet = TreeListSelectItem(pData, 0, 0, TVC_UNKNOWN);
				}

			pEntry = pData->pTreeItems[uItem];
			if(!pEntry)
				return 0;

			if(iRet == 1) {
				uChange = 1;
			}
		}

		if((uBits & TVIS_EXPANDED) && pEntry->uFirstChild) {	// Sollen Teile auf/zugeklappt werden
			uMask		   &= TVIS_EXPANDED | TVIS_EXPANDPARTIAL;
			pEntry->uState ^= TVIS_EXPANDED;
			pEntry->uState ^= uBits & uMask;
			iVal			= uMask & pItem->state;

			iRet = TreeListToggleItem(pData, uItem, iVal);
			if(iRet) {										// Abbruch oder Fehler beim Auf/Zuklappen
				if(uChange && pEntry->uShowPos) {				// Neuzeichnen des Eintrages
					UpdateRect(pData, uItem, 0);
				}

				return 0;
			}

			pEntry->uState &= ~uMask;
			pEntry->uState |=  iVal;
		}
	}

	if(uChange && pEntry->uShowPos) {							// Neuzeichnen des Eintrages
		UpdateRect(pData, uItem, 0);
	}
	return 1;
}


//*****************************************************************************
//*
//*		TreeListGetItem
//*
//*****************************************************************************
//	Daten vone einem Eintrag abfragen
//	pData		: Zeiger auf die Fensterdaten
//	pItem		: Zeiger auf die den Datenspeicher
//	Ergibt 1 wenn ok oder 0 bei einem Fehler
static unsigned TreeListGetItem(TreeListData *pData, TV_ITEM *pItem) {

	ExtraItem	  **pList;
	ExtraItem	   *pExtra;
	BaseItem	   *pEntry;
	unsigned		uBits;
	unsigned		uItem;
	unsigned		uSub;
	unsigned		uLen;

	uItem = U(pItem->hItem);
	if(uItem > pData->uTreeItemsMax)
		return 0;

	pEntry = pData->pTreeItems[uItem];
	if(!pEntry)
		return 0;

	uBits = pItem->mask;

	if(uBits & TVIF_SUBITEM) {									// Einen Extraeintrag abfragen
		uSub = pItem->cChildren;
		if(uSub > 0) {
			if(uSub >= pData->uColumnCount)
				return 0;
			pList	= pData->pExtraItems[uSub - 1];
			pExtra	= pList[uItem];

			if(!pExtra) {										// Einen neuen Eintrag erzeugen
				pExtra  = new(ExtraItem, 1);
				memset(pExtra, 0, sizeof(ExtraItem));
				pExtra->iImage = TV_NOIMAGE;
				pExtra->uState = pEntry->uState & (TVIS_BOLD | TVIS_UNDERLINE);
				pList[uItem]   = pExtra;
			}

			if(uBits & TVIF_PARAM) {
				pItem->lParam = pEntry->lParam;
			}

			if(uBits & TVIF_IMAGE) {
				pItem->iImage = pExtra->iImage;
			}

			if(uBits & TVIF_TEXT) {								// Einen neuen Text einstellen
				if(pExtra->pText == LPSTR_TEXTCALLBACK) {
					pItem->pszText = LPSTR_TEXTCALLBACK;
				} else
					if(uBits & TVIF_TEXTPTR) {
						if(!pExtra->pText) {
							pItem->pszText    = _T("");
							pItem->cchTextMax = 0;
						} else {
							pItem->pszText    = pExtra->pText;
							pItem->cchTextMax = pExtra->uTextSize + 1;
						}
					} else {
						if(pExtra->pText) {
							uLen = pExtra->uTextSize + 1;
							if(pItem->cchTextMax < (int)uLen) {
								if(pItem->cchTextMax <= 0) {
									uLen = 0;
								} else {
									uLen = pItem->cchTextMax - 1;
									pItem->pszText[uLen] = 0;
								}
							}

							memcpy(pItem->pszText, pExtra->pText, uLen * sizeof(TCHAR));
						} else {
							if(pItem->cchTextMax > 0) {
								pItem->pszText[0] = 0;
							}
						}
					}
			}

			if(uBits & TVIF_STATE) {
				pItem->state  = pExtra->uState&~TVIS_BASEFLAGS;
				pItem->state &= pItem->stateMask;
			}

			return 1;
		}

		if(pEntry->bCallback & TVIF_CHILDREN)
			pItem->cChildren = I_CHILDRENCALLBACK;
		else
			pItem->cChildren = (pEntry->uFirstChild) ? 1 : 0;

		uBits &= ~TVIF_CHILDREN;
	}


	//******************** Einen Basis Eintrag ändern *****************************
	if(uBits & TVIF_PARAM) {
		pItem->lParam = pEntry->lParam;
	}

	if(uBits & TVIF_IMAGE) {
		pItem->iImage = pEntry->iImage;
	}

	if(uBits & TVIF_SELECTEDIMAGE) {
		pItem->iSelectedImage = pEntry->iSelectedImage;
	}

	if(uBits & TVIF_CHILDREN) {
		if(pEntry->bCallback & TVIF_CHILDREN)
			pItem->cChildren = I_CHILDRENCALLBACK;
		else
			pItem->cChildren = (pEntry->uFirstChild) ? 1 : 0;
	}

	if(uBits & TVIF_TEXT) {										// Einen neuen Text einstellen
		if(pEntry->pText == LPSTR_TEXTCALLBACK) {
			pItem->pszText = LPSTR_TEXTCALLBACK;
		} else
			if(uBits & TVIF_TEXTPTR) {
				pItem->pszText    = pEntry->pText;
				pItem->cchTextMax = pEntry->uTextSize + 1;
			} else {
				uLen = pEntry->uTextSize + 1;
				if(pItem->cchTextMax < (int)uLen) {
					if(pItem->cchTextMax <= 0) {
						uLen = 0;
					} else {
						uLen = pItem->cchTextMax - 1;
						pItem->pszText[uLen] = 0;
					}
				}

				memcpy(pItem->pszText, pEntry->pText, uLen * sizeof(TCHAR));
			}
	}

	if(uBits & TVIF_STATE) {
		pItem->state  = pEntry->uState;
		pItem->state &= pItem->stateMask;
	}

	return 1;
}

//*****************************************************************************
//*
//*		TreeListDeleteColumn
//*
//*****************************************************************************
//	Löscht eine Spalte aus dem Header
//	pData		: Zeiger auf die Fensterdaten
//	uCol		: Ist die Nummer der Spalte die gelöscht werden soll
//	Ergibt 1 wenn die Spalte gelöscht wurde
static int TreeListDeleteColumn(TreeListData *pData, unsigned uCol) {

	ExtraItem **pList;
	ExtraItem  *pExtra;
	RECT		sRect;
	BYTE		bItem;
	BYTE		bByte;
	unsigned	uPos;
	unsigned	uSub;
	unsigned	uItem;
	unsigned	uIndex;
	int			iDelta;
	int			iXoff;
	int			iNum;
	int			iCnt;
	int			iVar;
	int			iSub;
	int			iAll;
	int			iFix;

	if(uCol >= pData->uColumnCount)
		return 0;

	if(uCol && uCol == pData->uSelectedSub) {					// Ist die Auswahl in der Spalte
		TreeListSelectItem(pData, pData->uSelectedItem, 0, TVC_UNKNOWN);
	}

	if(uCol && uCol == pData->uEditSub) {
		pData->uEditSub  = 0;
		pData->uEditItem = 0;
		TreeListEndLabelEdit(pData, 0);
	}

	if(uCol && uCol == pData->uFocusSub) {
		pData->uFocusSub  = 0;
		pData->uFocusItem = 0;
	}

	if(uCol == pData->uTrackedSub) {
		pData->uTrackedSub  = 0;
		pData->uTrackedItem = 0;
	}

	GetClientRect(pData->hWnd, &sRect);

	iDelta	= pData->aColumn[uCol].sSize;
	iSub	= pData->aColumn[uCol].bWeight;
	iCnt	= 0;
	iVar	= 0;
	iFix	= 0;
	iAll	= 0;

	for(uPos = 0; uPos < pData->uColumnCount; uPos++) {				// Zählern der variablen Spalten
		if(uPos == uCol)
			continue;
		if(pData->aColumn[uPos].bWeight == 0) {
			iFix +=	pData->aColumn[uPos].sSize;
			continue;
		}

		iVar +=	pData->aColumn[uPos].sSize;
		iAll += pData->aColumn[uPos].bWeight;
		iCnt += 1;
	}

	Header_DeleteItem(pData->hHeader, uCol);
	pData->uColumnCount--;

	if(pData->uColumnCount > 0) {								// Liste mit Extraeinträgen löschen
		iNum = uCol - 1;
		if(iNum < 0)
			iNum = 0;

		pList = pData->pExtraItems[iNum];
		if(pList) {
			for(uItem = 0; uItem <= pData->uTreeItemsMax; uItem++) { // Alle Einträge aus der Liste löschen
				pExtra = pList[uItem];
				if(!pExtra)
					continue;

				if(pExtra->pText) {
					pExtra->uTextSize = 0;
					delete(pExtra->pText);
				}

				delete(pExtra);
			}

			memmove(pData->pExtraItems + iNum, pData->pExtraItems + iNum + 1, sizeof(pList) * (MAX_COLUMNS - 1 - iNum));
			pData->pExtraItems[pData->uColumnCount] = NULL;
			delete(pList);
		}
	} else {
		iNum = MAX_COLUMNS;
	}

	if(pData->aColumn[uCol].bWeight) {
		pData->uColumnCountVar--;
	}

	if(pData->aColumn[uCol].bMark) {
		pData->uMarkedCols--;
	}

	uSub = pData->aColumnPos[uCol];

	memmove(pData->aColumn + uCol, pData->aColumn + uCol + 1, (MAX_COLUMNS - 1 - uCol)*sizeof(ColumnData));

	for(uIndex = 0; uIndex < uSub; uIndex++) {						// Zuordnungs-Array anpassen
		bItem = pData->aColumnPos[uIndex - 1];
		if(bItem < uCol)
			continue;

		bItem++;
		pData->aColumnPos[uIndex] = bItem;
	}

	for(; uIndex <= pData->uColumnCount; uIndex++) {				// Spaltenpositionen verschieben
		bItem = pData->aColumnPos[uIndex + 1];

		if(bItem >= uCol) {
			uCol--;
		}

		pData->aColumnPos[uIndex] = bItem;
	}

	for(uIndex = pData->uColumnCount; uIndex > 0;) {
		uIndex--;
		bByte = pData->aColumn[uIndex].bIndex;

		if(bByte >= uSub) {
			bByte--;
			pData->aColumn[uIndex].bIndex = bByte;
		}

		pData->aColumn[uIndex].bNext = pData->aColumnPos[bByte + 1];
	}

	pData->iFixSize   = iFix;
	pData->iAllWeight = iAll;
	pData->aColumn[pData->uColumnCount].bWeight = 0;

	if(iCnt && iDelta) {										// Variable Breiten anpassen
		ChangeColSize(pData, iDelta);
	} else {
		if(iSub && !iCnt) {
			pData->iVarSize = 0;
		}
	}

	if(pData->uSelectedSub > uCol) {							// Ist die Auswahl vor der Spalte
		pData->uSelectedSub--;
	}

	if(pData->uEditSub > uCol) {
		pData->uEditSub--;
	}

	if(pData->uFocusSub > uCol) {
		pData->uFocusSub--;
	}

	if(pData->uTrackedSub > uCol) {
		pData->uTrackedSub--;
	}

	if(!pData->uColumnCount) {								// Den Header löschen
		DestroyWindow(pData->hHeader);
		pData->hHeader	   = NULL;
		pData->uStartPixel = 0;
		pData->iRowHeight  = 1;
		UpdateHeight(pData);
		InvalidateRect(pData->hWnd, &sRect, FALSE);
	}

	iXoff = UpdateColumns(pData);							// Hat sich die Spaltenbreiten verändert
	if(iXoff < 0x10000) {
		sRect.left  = iXoff;
		sRect.left -= pData->uScrollX;
		sRect.top   = pData->uStartPixel;
		InvalidateRect(pData->hWnd, &sRect, FALSE);
	}

	UpdateScrollX(pData);

	return 1;
}


//*****************************************************************************
//*
//*		TreeListInsertColumn
//*
//*****************************************************************************
//	Adds a new column in the header
//	pData		: Zeiger auf die Fensterdaten
//	uCol		: Ist die Nummer der Spalte die eingefügt wird
//	pInsert		: Zeiger auf die ein zu fügenden Daten
//	Returns the positio of the new column or -1 if an error occurs.
static int TreeListInsertColumn(TreeListData *pData, unsigned uCol, TV_COLUMN *pColumn) {

	ExtraItem **pList;
	TV_COLSIZE	sNotify;
	HDITEM	 	sItem;
	RECT		sRect;
	short 		sFixed;
	UINT		uIndex;
	BYTE		bByte;
	BYTE		bItem;
	BYTE		bMark;
	BYTE		bMinEx;
	BYTE		bAlign;
	int			iWeight;
	int			iDelta;
	int			iStart;
	int			iSize;
	int			iXoff;
	int			iYoff;
	int			iNum;
	int			iVar;
	int			iAll;
	int			iAdd;
	int			iMin;
	int			iFix;

	GetClientRect(pData->hWnd, &sRect);

	if(!pData->hHeader) {										// Create a new header
		iStart = bDrawWithTheme ? GetSystemMetrics(SM_CYHSCROLL) : 17; //SM_CYHSCROLL is not enough tall with themes disabled apps;
		iYoff  = sRect.top + iStart;

		if(pData->uStyleEx & TVS_EX_HIDEHEADERS) {
			iYoff = 0;
		}

		pData->hHeader = CreateWindow(WC_HEADER, NULL, WS_VISIBLE | WS_CHILD | HDS_HORZ | HDS_BUTTONS | HDS_DRAGDROP, sRect.left, sRect.top, sRect.right, iYoff, pData->hWnd, (HMENU)1, NULL, NULL);
		if(!pData->hHeader)
			return -1;

		pData->uStartPixel = (pData->uStyleEx & TVS_EX_HIDEHEADERS) ? 0 : iStart;
		pData->iRowHeight  = 1;
		UpdateHeight(pData);

		InvalidateRect(pData->hWnd, &sRect, FALSE);
		if(pData->uStyleEx & TVS_EX_HEADEROWNIMGLIST){
			SendMessage(pData->hHeader, HDM_SETIMAGELIST, 0, (LPARAM)pData->hHeadImg);
		} else {
			SendMessage(pData->hHeader, HDM_SETIMAGELIST, 0, (LPARAM)pData->hImages);
		}
		SendMessage(pData->hHeader, WM_SETFONT, (WPARAM)hDefaultFontN, 0);

		if(pData->uSizeX <= pData->uStartPixel)
			pData->uSizeYsub = 0;
		else
			pData->uSizeYsub = pData->uSizeX - pData->uStartPixel;
	}

	if(pData->uColumnCount >= MAX_COLUMNS) {					// Prüfe die Anzahl der Spalten
		return -1;
	}

	memset(&sItem, 0, sizeof(sItem));							// Die Spaltendaten zusammenstellen

	if(uCol >= pData->uColumnCount) {
		uCol = pData->uColumnCount;
	}

	if(pColumn->mask & TVCF_FMT) {								// text alignment
		sItem.mask    |= HDI_FORMAT;
		sItem.fmt      = pColumn->fmt;

		switch(sItem.fmt & HDF_JUSTIFYMASK) {
			case HDF_CENTER:
				bAlign = DT_CENTER;
				break;
			case HDF_RIGHT:
				bAlign = DT_RIGHT;
				break;
			default:
				bAlign = DT_LEFT;
				break;
		}
	} else {
		bAlign = DT_LEFT;
	}

	if(pColumn->mask & TVCF_IMAGE) {							// Hat die Spalte auch ein Icon
		sItem.mask    |=  HDI_IMAGE;
		sItem.iImage   =  pColumn->iImage;
	}

	if(pColumn->mask & TVCF_TEXT) {								// Auch einen Text übergeben
		sItem.mask    |=  HDI_TEXT;
		sItem.pszText  =  pColumn->pszText;
	}

	if(pColumn->mask & TVCF_MIN) {								// Auch einen Min-Wert übergeben
		iMin		   =  pColumn->iOrder;
		bMinEx		   =  1;

		if(iMin < 0) {
			iMin	   = -iMin;
			bMinEx	   =  0;
		}
	} else {
		iMin		   =  16;
		bMinEx		   =  0;
	}

	if(pColumn->mask & TVCF_WIDTH) {							// Fixe Breite für die Spalte
		iWeight		   =  0;
		sItem.mask    |=  HDI_WIDTH;
		sItem.cxy	   =  pColumn->cx;
		iSize		   =  pColumn->cx;
		iDelta		   = -pColumn->cx;
		iAdd		   =  0;
	} else {													// Variable vordefinierte Breite
		if(pColumn->mask & TVCF_VWIDTH)
			iWeight =  pColumn->cx;
		else
			iWeight =  1;

		iVar  = pData->iVarSize;
		iFix  = pData->iFixSize;
		iAll  = pData->iAllWeight;
		iSize = pData->uSizeX - iVar - iFix;

		if(iWeight <= 0)
			iWeight = 1;
		if(iWeight > 255)
			iWeight = 255;

		if(pData->uColumnCountVar) {							// Gibt es schon variable Spalten
			iSize  = (iVar * iWeight) / (iAll + iWeight);
			iDelta = -iSize;
		} else {
			iDelta = 0;
		}

		sItem.mask    |= HDI_WIDTH;
		sItem.cxy      = iSize;
		iAdd		   = 1;

		if(sItem.cxy < iMin)
			sItem.cxy = iMin;
	}

	uCol = Header_InsertItem(pData->hHeader, uCol, &sItem);
	if(uCol & 0x80000000)
		return -1;

	if(pData->uColumnCount > 0) {								// Liste mit Extraeinträgen erzeugen
		pList = new(ExtraItem*, pData->uTreeItemsMax + 1);
		if(!pList) {
			Header_DeleteItem(pData->hHeader, uCol);
			return -1;
		}

		memset(pList, 0, sizeof(ExtraItem *) * (pData->uTreeItemsMax + 1));

		iNum = uCol - 1;
		if(iNum < 0)
			iNum = 0;

		memmove(pData->pExtraItems + iNum + 1, pData->pExtraItems + iNum, sizeof(pList) * (MAX_COLUMNS - 2 - iNum));
		pData->pExtraItems[iNum] = pList;
	}

	memmove(pData->aColumn + uCol + 1, pData->aColumn + uCol, (MAX_COLUMNS - 1 - uCol)*sizeof(ColumnData));

	for(uIndex = pData->uColumnCount + 2; uIndex > uCol; uIndex--) {	// Zuordnungs-Array anpassen
		bItem = pData->aColumnPos[uIndex - 1];
		if(bItem >= uCol)
			bItem++;

		pData->aColumnPos[uIndex] = bItem;
	}

	pData->aColumnPos[uCol] = (BYTE)uCol;

	while(uIndex > 0) {
		uIndex--;

		bItem = pData->aColumnPos[uIndex];
		if(bItem < uCol)
			continue;

		bItem++;
		pData->aColumnPos[uIndex] = bItem;
	}

	for(uIndex = pData->uColumnCount;;) {						// Folgende Spalten verschieben
		bByte = pData->aColumn[uIndex].bIndex;

		if(bByte >= uCol) {
			bByte++;
			pData->aColumn[uIndex].bIndex = bByte;
		}

		if(uIndex == 0)
			break;
		uIndex--;
	}

	bMark  = 0;
	sFixed = 0;

	if(pColumn->mask & TVCF_MARK)							// Ist die Spalte markiert
		if(pColumn->fmt & TVCFMT_MARK) {
			bMark = 1;
		}

	if(pColumn->mask & TVCF_FIXED)							// Ist die Spalte fixiert
		if(pColumn->fmt & TVCFMT_FIXED) {
			sFixed = (short)((sItem.cxy > 0) ? sItem.cxy : 100);
		}

	pData->aColumn[uCol].bWeight = (BYTE)iWeight;
	pData->aColumn[uCol].sReal   = (short)sItem.cxy;
	pData->aColumn[uCol].sSize   = (short)iSize;
	pData->aColumn[uCol].sMin    = (short)iMin;
	pData->aColumn[uCol].bIndex  = (BYTE)uCol;
	pData->aColumn[uCol].bMinEx  =  bMinEx;
	pData->aColumn[uCol].bAlign  =  bAlign;
	pData->aColumn[uCol].bMark   =  bMark;
	pData->aColumn[uCol].sFixed  =  sFixed;
	pData->uMarkedCols			+= 	bMark;


	for(uIndex = pData->uColumnCount;;) {						// Nächste sichtbare Spalten aktualisieren
		bByte						 = pData->aColumn[uIndex].bIndex;
		pData->aColumn[uIndex].bNext = pData->aColumnPos[bByte + 1];
		if(uIndex == 0)
			break;
		uIndex--;
	}

	if(pData->uColumnCountVar) {								// Variable Breiten anpassen
		if(iDelta) {
			ChangeColSize(pData, iDelta);
			pData->iVarSize  -= iDelta;
		} else
			if(!iAdd) {
				pData->iFixSize  += iSize;
			}
	} else {
		if(iAdd)
			pData->iVarSize  = iSize;
		else
			pData->iFixSize += iSize;
	}

	pData->iAllWeight			+=  iWeight;
	pData->uColumnCountVar      +=  iAdd;
	pData->uColumnCount	        +=  1;

	if(pData->uSelectedSub > 0 && pData->uSelectedSub >= uCol) {
		pData->uSelectedSub++;
	}

	if(pData->uTrackedSub > 0 && pData->uTrackedSub >= uCol) {
		pData->uTrackedSub++;
	}

	if(pData->uFocusSub > 0 && pData->uFocusSub >= uCol) {
		pData->uFocusSub++;
	}

	if(pData->uEditSub > 0 && pData->uEditSub >= uCol) {
		pData->uEditSub++;
	}

	iXoff = UpdateColumns(pData);							// Hat sich die Spaltenbreiten verändert
	if(iXoff < 0x10000) {
		sRect.left  = iXoff;
		sRect.left -= pData->uScrollX;
		sRect.top   = pData->uStartPixel;
		InvalidateRect(pData->hWnd, &sRect, FALSE);
	}

	UpdateScrollX(pData);

	if(pData->uInsertMark) {									// Fehlende Infomarken einfügen
		TV_ITEM		sSet;
		ExtraItem  *pExtra;

		sSet.mask		= TVIF_SUBITEM;
		sSet.hItem		= (HTREEITEM)(ULONG_PTR)pData->uInsertMark;
		sSet.cChildren	= uCol;

		TreeListSetItem(pData, &sSet);

		pExtra = pData->pExtraItems[uCol - 1][pData->uInsertMark];
		if(pExtra) {
			pExtra->uColorBk  = pData->uColors[TVC_INSERT];
			pExtra->bFlags	 |= TVIX_BKCOLOR;
		}
	}

	if(pData->uStyleEx & TVS_EX_HEADERCHGNOTIFY) {				// Geänderte Spalten melden
		sNotify.hdr.code			= TVN_COLUMNCHANGED;
		sNotify.uColumn				= uCol;
		sNotify.uIndex				= pData->aColumn[uCol].bIndex;
		sNotify.uPosX				= pData->aColumnXpos[uCol];
		sNotify.iSize				= pData->aColumn[uCol].sReal;

		UNLOCK(pData);
		SendNotify(pData, &sNotify.hdr);
		LOCK(pData);
	}

	return uCol;
}

//*****************************************************************************
//*
//*		TreeListScanColumn
//*
//*****************************************************************************
//	Berechnet die Breite der sichtbaren Einträge einer Spalte
//	pData		: Zeiger auf die Fensterdaten
//	uSub		: Ist die Spalte
//	Ergibt sie gescannte Breite
static int TreeListScanColumn(TreeListData *pData, unsigned uSub) {

	BaseItem  **pList;
	BaseItem   *pEntry;
	ExtraItem  *pExtra;
	ExtraItem **pItems;
	unsigned   *pPList;
	unsigned	uPos;
	int			iMax;
	int			iPos;

	if(uSub >= pData->uColumnCount)
		return 0;

	if(uSub > 0) {												// Extraspalte
		pItems = pData->pExtraItems[uSub - 1];
		pPList = pData->pItemPos;
		iMax   = 0;

		for(uPos = 0; uPos < pData->uItemPosCount; uPos++) {
			pExtra = pItems[pPList[uPos]];
			if(!pExtra) {
				if(iMax < 8)
					iMax = 8;
				continue;
			}

			if(pData->hSubImg && (pExtra->bFlags & TVIX_HASIMAGE))
				iPos = pData->iSubImgXsize;
			else
				iPos = 0;

			iPos += pExtra->iTextPixels + 8;
			if(iPos > iMax)
				iMax = iPos;
		}

		return iMax;
	}

	pList  = pData->pTreeItems;
	pPList = pData->pItemPos;
	iMax   = 0;

	for(uPos = 0; uPos < pData->uItemPosCount; uPos++) {			// Erste Spalte
		pEntry = pList[pPList[uPos]];

		if(pEntry->bFlags & TVIX_HASIMAGE)
			iPos = pData->iImagesXsize;
		else
			iPos = 0;

		iPos += pEntry->uLevel * pData->iIndent;
		iPos += pEntry->iTextPixels + 8;
		if(iPos > iMax)
			iMax = iPos;
	}

	if(pData->uStyleEx & TVS_EX_ITEMLINES) {
		iMax += 1;
	}

	if(pData->cHasRootRow) {
		iMax += pData->iIndent;
	}

	if(pData->hStates) {
		iMax += pData->iStatesXsize;
	}

	return iMax;
}


//*****************************************************************************
//*
//*		TreeListHitTest
//*
//*****************************************************************************
//	Prüft wo eine Koordinate im Fenster ist
//	pData		: Zeiger auf die Fensterdaten
//	pInfo		: Zeiger auf die ein zu fügenden Daten
//	Ergibt das Item auf dem die Koordinate zeigt
static unsigned TreeListHitTest(TreeListData *pData, TV_HITTESTINFO *pInfo) {

	BaseItem   *pEntry;
	ExtraItem  *pExtra;
	unsigned	uItem;
	unsigned	uNext;
	unsigned	uSub;
	unsigned	uCol;
	int			iXpos;
	int			iYpos;
	int			iZpos;
	int			iWidth;
	int			iIcon;

	iXpos = pInfo->pt.x;
	iYpos = pInfo->pt.y;

	if((unsigned)iXpos >= pData->uSizeX) {
		pInfo->hItem = NULL;
		pInfo->flags = (iXpos < 0) ? TVHT_TOLEFT : TVHT_TORIGHT;
		return 0;
	}

	iYpos -= pData->uStartPixel;

	if((unsigned)iYpos >= pData->uSizeY) {
		pInfo->hItem = NULL;
		pInfo->flags = (iYpos < 0) ? TVHT_ABOVE : TVHT_BELOW;
		return 0;
	}

	iZpos  = iYpos / pData->iRowHeight;
	iZpos +=       pData->uScrollY;

	if((unsigned)iZpos >= pData->uItemPosCount) {
		pInfo->hItem = NULL;
		pInfo->flags = TVHT_NOWHERE;
		return 0;
	}

	iXpos		+= pData->uScrollX;
	uItem		 = pData->pItemPos  [iZpos];
	pEntry		 = pData->pTreeItems[uItem];
	pInfo->hItem = (HTREEITEM)(ULONG_PTR)uItem;

	if(!pEntry)
		return 0;

	uSub = pData->aColumnPos[1];

	if(iXpos >= pData->aColumnXpos[uSub]) {						// Auf Extraeintrag
		for(uCol = 1; uCol < pData->uColumnCount; uCol++) {
			uSub  = pData->aColumnPos[uCol  ];
			uNext = pData->aColumnPos[uCol + 1];

			if(iXpos >= pData->aColumnXpos[uNext])
				continue;
			iXpos -= pData->aColumnXpos[uSub ];

			pExtra = pData->pExtraItems[uSub - 1][uItem];


			if(pData->aColumn[uSub].bEdit >= TVAX_CHECK) {		// Hat der Extraeintrag ein Icon
				iIcon = pData->iChecksXsize;
			} else
				if(pExtra && (pExtra->bFlags & TVIX_HASIMAGE)) {
					iIcon = pData->iImagesXsize;
				} else {
					iIcon = 0;
				}

			pInfo->flags = uSub << 24;

			if(iXpos < iIcon) {									// Auf Icon
				pInfo->flags |= TVHT_ONSUBICON;
				return uItem;
			}

			if(!pExtra || !pExtra->uTextSize) {				// Auf Text wenn leerer Eintrag
				pInfo->flags |= TVHT_ONSUBLABEL;
				return uItem;
			}

			switch(pData->aColumn[uSub].bAlign) {				// Textausrichtung
				default
						:
					if(iXpos - iIcon < pExtra->iTextPixels + 5) {
						pInfo->flags |= TVHT_ONSUBLABEL;
						return uItem;
					}
					break;

				case DT_RIGHT:
					iWidth  = pData->aColumnXpos[uNext];
					iWidth -= pData->aColumnXpos[uSub ];

					if(iXpos >= iWidth - pExtra->iTextPixels - 5) {
						pInfo->flags |= TVHT_ONSUBLABEL;
						return uItem;
					}
					break;

				case DT_CENTER:
					iWidth  = pData->aColumnXpos[uNext];
					iWidth -= pData->aColumnXpos[uSub ];
					iWidth += iIcon;
					iWidth /= 2;

					if(iXpos >= iWidth - pExtra->iTextPixels / 2 - 3)
						if(iXpos <= iWidth + pExtra->iTextPixels / 2 + 3) {
							pInfo->flags |= TVHT_ONSUBLABEL;
							return uItem;
						}
					break;
			}

			pInfo->flags |= TVHT_ONSUBRIGHT;

			return uItem;
		}

		pInfo->flags = TVHT_ONRIGHTSPACE;

		return uItem;
	}

	if(!pData->cHasRootRow) {									// Root-Linien ausgleichen
		iXpos += pData->iIndent;
	}

	iXpos -= pData->iIndent * pEntry->uLevel;

	if(iXpos < pData->iIndent) {								// Auf eingerücktem Bereich
		if(pData->uStyle & TVS_HASBUTTONS)
			if(pEntry->bFlags & TVIX_HASBUTTON) {
				if(iXpos >= pData->iShift - 6)
					if(iXpos <= pData->iShift + 7) {
						iYpos %= pData->iRowHeight;
						iYpos -= pData->iRowHeight / 2;

						if(iYpos >= -6 && iYpos <= 7) {
							pInfo->flags = TVHT_ONITEMBUTTON;
							return uItem;
						}
					}
			}

		pInfo->flags = TVHT_ONITEMINDENT;
		return uItem;
	}

	iXpos -= pData->iIndent;

	if(pData->uStyleEx & TVS_EX_ITEMLINES) {
		iXpos--;
	}

	if(pData->hStates) {										// Auf der Auswahl-Box
		iXpos -= pData->iStatesXsize;

		if(iXpos < 0) {
			pInfo->flags = TVHT_ONITEMSTATEICON;
			return uItem;
		}
	}

	if(pEntry->bFlags & TVIX_HASIMAGE) {						// Auf dem Icon
		iXpos -= pData->iImagesXsize;

		if(pData->uStyleEx & TVS_EX_ITEMLINES)
			iXpos--;

		if(iXpos < 0) {
			pInfo->flags = TVHT_ONITEMICON;
			return uItem;
		}
	}
	// Auf Text
	if(iXpos < pEntry->iTextPixels + 5 || !pEntry->pText || !pEntry->pText[0]) {
		pInfo->flags = TVHT_ONITEMLABEL;
	} else {
		pInfo->flags = TVHT_ONITEMRIGHT;
	}

	return uItem;
}

//*****************************************************************************
//*
//*		TreeListSetTrackItem
//*
//*****************************************************************************
//	Setzt das den Einfügeeintrag
//	pData	: Zeiger auf die Fensterdaten
//	uItem	: Ist die Nummer des Eintrages bei dem eingefügt werden soll
//	iMode	: 0=davor einfügen  1=nachher einfügen
//	Ergibt 1 wenn der Eintrag eingefügt wurde
static unsigned TreeListSetInsertMark(TreeListData *pData, unsigned uItem, int iMode) {

	TV_INSERTSTRUCT	sInsert;
	ExtraItem	   *pExtra;
	BaseItem	   *pEntry;
	unsigned		uSub;
	int				iRet;

	if(pData->uInsertMark) {
		iRet = TreeListDeleteItem(pData, pData->uInsertMark, 1);
		pData->uInsertMark = 0;
	} else {
		iRet = 0;
	}

	if(uItem == 0)
		return iRet;
	if(uItem > pData->uTreeItemsMax)
		return 0;

	pEntry = pData->pTreeItems[uItem];
	if(!pEntry)
		return 0;

	if(iMode) {
		uItem = pEntry->uPrevItem;
		if(!uItem)
			uItem = U(TVI_FIRST);
	}

	sInsert.hParent		 		= (HTREEITEM)(ULONG_PTR)pEntry->uParent;
	sInsert.hInsertAfter 		= (HTREEITEM)(ULONG_PTR)uItem;
	sInsert.item.mask	 		= TVIF_SELECTEDIMAGE | TVIF_IMAGE;
	sInsert.item.iImage			= TV_NOIMAGE;
	sInsert.item.iSelectedImage	= TV_NOIMAGE;

	uItem = TreeListInsertItem(pData, &sInsert);
	if(!uItem)
		return 0;

	pEntry				= pData->pTreeItems[uItem];
	pEntry->uColorBk	= pData->uColors[TVC_INSERT];
	pEntry->bFlags	   |= TVIX_BKCOLOR;
	sInsert.item.mask  |= TVIF_SUBITEM;
	sInsert.item.hItem	= (HTREEITEM)(ULONG_PTR)uItem;

	for(uSub = 1; uSub < pData->uColumnCount; uSub++) {
		sInsert.item.cChildren	= uSub;

		TreeListSetItem(pData, &sInsert.item);

		pExtra = pData->pExtraItems[uSub - 1][uItem];
		pExtra->uColorBk	= pData->uColors[TVC_INSERT];
		pExtra->bFlags	   |= TVIX_BKCOLOR;
	}

	pData->uInsertMark = uItem;

	return uItem;
}

//*****************************************************************************
//*
//*		TreeListGetItemColor
//*
//*****************************************************************************
//	Holt die Farbe eins Feldes
//	pData	: Zeiger auf die Fensterdaten
//	uItem	: Ist die Nummer des Eintrages
//	uSub	: Ist die Spalte
//	iMode	: 0=Hintergrundfarbe 1=Textfarbe abfragen
//	Ergibt die alte Farbe oder TV_NOCOLOR wenn keine Farbe eingestellt war
static LRESULT TreeListGetItemColor(TreeListData *pData, unsigned uItem, unsigned uSub, int iMode) {

	COLORREF	uColor;
	ExtraItem  *pExtra;
	BaseItem   *pEntry;

	if(uItem > pData->uTreeItemsMax)
		return TV_NOCOLOR;

	pEntry = pData->pTreeItems[uItem];
	if(!pEntry)
		return TV_NOCOLOR;

	if(uSub) {												// Extra-Eintrag abfragen
		if(uSub >= pData->uColumnCount)
			return TV_NOCOLOR;

		pExtra = pData->pExtraItems[uSub - 1][uItem];
		if(!pExtra)
			return TV_NOCOLOR;

		if(iMode)
			uColor = (pExtra->bFlags & TVIX_TEXTCOLOR) ? pExtra->uColorText : TV_NOCOLOR;
		else
			uColor = (pExtra->bFlags & TVIX_BKCOLOR) ? pExtra->uColorBk  : TV_NOCOLOR;
	} else {
		if(iMode)
			uColor = (pEntry->bFlags & TVIX_TEXTCOLOR) ? pEntry->uColorText : TV_NOCOLOR;
		else
			uColor = (pEntry->bFlags & TVIX_BKCOLOR) ? pEntry->uColorBk  : TV_NOCOLOR;
	}

	return (LRESULT)uColor;
}

//*****************************************************************************
//*
//*		TreeListSetItemColor
//*
//*****************************************************************************
//	Setzt die Farbe eines Feldes
//	pData	: Zeiger auf die Fensterdaten
//	uItem	: Ist die Nummer des Eintrages
//	uSub	: Ist die Spalte
//	uColor	: Ist die neue Farbe
//	iMode	: 0=Hintergrundfarbe 1=Textfarbe einstellen
//	Ergibt die alte Farbe oder TV_NOCOLOR wenn keine Farbe eingestellt war
static COLORREF TreeListSetItemColor(TreeListData *pData, unsigned uItem, unsigned uSub, COLORREF uColor, int iMode) {

	TV_ITEM		sSet;
	COLORREF	uOld;
	ExtraItem  *pExtra;
	BaseItem   *pEntry;

	if(uItem > pData->uTreeItemsMax)
		return TV_NOCOLOR;

	pEntry = pData->pTreeItems[uItem];
	if(!pEntry)
		return TV_NOCOLOR;

	if(uSub >= 255) {											// Die Ganze Zeile ändern
		if(pData->uColumnCount) {
			for(uSub = pData->uColumnCount; uSub > 0; uSub--) {
				TreeListSetItemColor(pData, uItem, uSub, uColor, iMode);
			}
		}

		uSub = 0;
	}

	if(uSub) {												// Extra-Eintrag verändern
		if(uSub >= pData->uColumnCount)
			return TV_NOCOLOR;

		pExtra = pData->pExtraItems[uSub - 1][uItem];
		if(!pExtra) {											// Extra-Eintrag erzeugen
			sSet.mask		= TVIF_SUBITEM;
			sSet.hItem		= (HTREEITEM)(ULONG_PTR)uItem;
			sSet.cChildren	= uSub;

			if(!TreeListSetItem(pData, &sSet))
				return TV_NOCOLOR;

			pExtra = pData->pExtraItems[uSub - 1][uItem];
		}

		if(iMode) {											// Textfarbe
			uOld = (pExtra->bFlags & TVIX_TEXTCOLOR) ? pExtra->uColorText : TV_NOCOLOR;

			if(uColor == TV_NOCOLOR) {
				pExtra->bFlags	   &= ~TVIX_TEXTCOLOR;
			} else {
				pExtra->bFlags	   |=  TVIX_TEXTCOLOR;
				pExtra->uColorText  =  uColor;
			}
		} else {												// Hintergrund
			uOld = (pExtra->bFlags & TVIX_BKCOLOR) ? pExtra->uColorBk : TV_NOCOLOR;

			if(uColor == TV_NOCOLOR) {
				pExtra->bFlags	   &= ~TVIX_BKCOLOR;
			} else {
				pExtra->bFlags	   |=  TVIX_BKCOLOR;
				pExtra->uColorBk    =  uColor;
			}
		}
	} else {
		if(iMode) {											// Textfarbe
			uOld = (pEntry->bFlags & TVIX_TEXTCOLOR) ? pEntry->uColorText : TV_NOCOLOR;

			if(uColor == TV_NOCOLOR) {
				pEntry->bFlags	   &= ~TVIX_TEXTCOLOR;
			} else {
				pEntry->bFlags	   |=  TVIX_TEXTCOLOR;
				pEntry->uColorText  =  uColor;
			}
		} else {												// Hintergrund
			uOld = (pEntry->bFlags & TVIX_BKCOLOR) ? pEntry->uColorBk : TV_NOCOLOR;

			if(uColor == TV_NOCOLOR) {
				pEntry->bFlags	   &= ~TVIX_BKCOLOR;
			} else {
				pEntry->bFlags	   |=  TVIX_BKCOLOR;
				pEntry->uColorBk    =  uColor;
			}
		}
	}

	if(uColor != uOld) {										// Neu zeichnen
		UpdateRect(pData, uItem, uSub);
	}

	return uOld;
}

//*****************************************************************************
//*
//*		TreeListSetTrackItem
//*
//*****************************************************************************
//	Setzt das unterstrichene Item
//	pData	: Zeiger auf die Fensterdaten
//	uItem	: Ist die Nummer des Eintrages
//	uSub	: Ist die Spalte
//	Ergibt 1 wenn der Eintrag unterstrichen wurde
static int TreeListSetTrackItem(TreeListData *pData, unsigned uItem, unsigned uSub) {

	ExtraItem	   *pExtra;
	BaseItem	   *pEntry;
	int				iRet = 1;

	if(!(pData->uStyleEx & TVS_EX_SUBSELECT)) {
		uSub = 0;
	} else {
		if(uSub >= pData->uColumnCount) {
			uItem = 0;
			uSub  = 0;
			iRet  = 0;
		}
	}

	if(uItem > pData->uTreeItemsMax) {
		uItem = 0;
		uSub  = 0;
		iRet  = 0;
	} else {
		if(uItem == pData->uTrackedItem)
			if(uSub == pData->uTrackedSub) {
				return iRet;
			}
	}

	if(pData->uTrackedItem) {									// Den alten Eintrag zurücksetzen
		if(pData->uTrackedSub) {
			pExtra = pData->pExtraItems[pData->uTrackedSub - 1][pData->uTrackedItem];
			if(pExtra) {
				pExtra->bFlags &= ~TVIX_TRACKED;
				UpdateRect(pData, pData->uTrackedItem, pData->uTrackedSub);
			}
		} else {
			pEntry = pData->pTreeItems[pData->uTrackedItem];
			if(pEntry) {
				pEntry->bFlags &= ~TVIX_TRACKED;
				UpdateRect(pData, pData->uTrackedItem, 0);
			}
		}
	}

	if(uItem) {												// Den neuen Eintrag setzen
		if(uSub) {
			pExtra = pData->pExtraItems[uSub - 1][uItem];
			if(pExtra) {
				pData->uTrackedSub  = uSub;
				pData->uTrackedItem = uItem;
				pExtra->bFlags     |= TVIX_TRACKED;
				UpdateRect(pData, uItem, uSub);
			} else {
				iRet = 0;
			}
		} else {
			pEntry = pData->pTreeItems[uItem];
			if(pEntry) {
				pData->uTrackedSub  = 0;
				pData->uTrackedItem = uItem;
				pEntry->bFlags     |= TVIX_TRACKED;
				UpdateRect(pData, uItem, 0);
			} else {
				iRet = 0;
			}
		}
	} else {													// Keine Untersteichung
		pData->uTrackedSub  = 0;
		pData->uTrackedItem = 0;
	}

	return iRet;
}

//*****************************************************************************
//*
//*		TreeListGetNextItem
//*
//*****************************************************************************
//	Sucht den nächsten Eintrag
//	pData	: Zeiger auf die Fensterdaten
//	uItem	: Ist die Nummer des Eintrages
//	uFlags	: Bestimmt nach welchem Eintrag gesucht werden soll
//	Ergibt 1 wenn der Eintrag unterstrichen wurde
static unsigned TreeListGetNextItem(TreeListData *pData, unsigned uItem, unsigned uFlags) {
	BaseItem	   *pEntry;
	unsigned		uStop;
	unsigned		uPos;

	switch(uFlags) {
		case TVGN_ROOT:
			return pData->uFirstChild;

		case TVGN_NEXT:
			if(uItem > pData->uTreeItemsMax) {
				return 0;
			}

			pEntry = pData->pTreeItems[uItem];
			if(!pEntry)
				return 0;

			return pEntry->uNextItem;

		case TVGN_PREVIOUS:
			if(uItem > pData->uTreeItemsMax) {
				return 0;
			}

			pEntry = pData->pTreeItems[uItem];
			if(!pEntry)
				return 0;

			return pEntry->uPrevItem;

		case TVGN_PARENT:
			if(uItem > pData->uTreeItemsMax) {
				return 0;
			}

			pEntry = pData->pTreeItems[uItem];
			if(!pEntry)
				return 0;

			return pEntry->uParent;

		case TVGN_CHILD:
			if(uItem > pData->uTreeItemsMax) {
				if(uItem == U(TVI_ROOT))
					return pData->uFirstChild;
				return 0;
			}

			pEntry = pData->pTreeItems[uItem];
			if(!pEntry)
				return 0;

			return pEntry->uFirstChild;

		case TVGN_LASTCHILD:
			if(uItem > pData->uTreeItemsMax) {
				if(uItem == U(TVI_ROOT))
					return pData->uLastChild;
				return 0;
			}

			pEntry = pData->pTreeItems[uItem];
			if(!pEntry)
				return 0;

			return pEntry->uLastChild;

		case TVGN_FIRSTVISIBLE:
			if(pData->uItemPosCount <= 0)
				return 0;
			if(pData->uItemPosCount <= pData->uScrollY)
				return 0;

			uItem = pData->pItemPos[pData->uScrollY];

			if(uItem > pData->uTreeItemsMax || !pData->pTreeItems[uItem]) {
				return 0;
			}

			return uItem;

		case TVGN_NEXTVISIBLE:
			if(uItem > pData->uTreeItemsMax) {
				if(uItem != U(TVI_ROOT))
					return 0;
				if(pData->uFirstChild == 0)
					return 0;

				pEntry = pData->pTreeItems[pData->uFirstChild];

				uPos = pEntry->uShowPos;
				if(uPos <= pData->uScrollY)
					return 0;

				return pData->uFirstChild;
			}

			pEntry = pData->pTreeItems[uItem];
			if(!pEntry)
				return 0;

			uPos = pEntry->uShowPos;
			if(uPos <= pData->uScrollY)
				return 0;
			if(uPos > pData->uScrollY + pData->uPageEnties)
				return 0;

			uItem = pData->pItemPos[uPos];

			if(uItem > pData->uTreeItemsMax || !pData->pTreeItems[uItem]) {
				return 0;
			}

			return uItem;

		case TVGN_NEXTSELECTED:
			if(uItem > pData->uTreeItemsMax) {
				if(uItem != U(TVI_ROOT))
					return 0;

				uItem = pData->uFirstChild;

				pEntry = pData->pTreeItems[uItem];
				if(!pEntry)
					return 0;
				if(pEntry->uState & TVIS_SELECTED)
					return uItem;
			}

			pEntry = pData->pTreeItems[uItem];
			if(!pEntry)
				return 0;

			for(;;) {
				if(pEntry->uFirstChild) {
					uItem = pEntry->uFirstChild;
				} else
					if(pEntry->uNextItem) {
						uItem = pEntry->uNextItem;
					} else {
						for(;;) {
							uItem  = pEntry->uParent;
							pEntry = pData ->pTreeItems[uItem];
							if(!pEntry)
								return 0;
							if(pEntry->uNextItem) {
								uItem = pEntry->uNextItem;
								break;
							}
						}
					}

				pEntry = pData->pTreeItems[uItem];
				if(!pEntry)
					break;
				if(pEntry->uState & TVIS_SELECTED)
					return uItem;
			}

			return 0;

		case TVGN_NEXTSELCHILD:
			if(uItem > pData->uTreeItemsMax) {
				if(uItem != U(TVI_ROOT))
					return 0;

				uItem = pData->uFirstChild;

				pEntry = pData->pTreeItems[uItem];
				if(!pEntry)
					return 0;
				if(pEntry->uState & TVIS_SELECTED)
					return uItem;

				uStop = 0;
			} else {
				pEntry = pData->pTreeItems[uItem];
				if(!pEntry || !pEntry->uFirstChild)
					return 0;

				uStop = uItem;
			}

			for(;;) {
				if(pEntry->uFirstChild) {
					uItem = pEntry->uFirstChild;
				} else
					if(pEntry->uNextItem) {
						uItem = pEntry->uNextItem;
					} else {
						for(;;) {
							uItem = pEntry->uParent;
							if(uItem == uStop)
								return 0;
							pEntry = pData ->pTreeItems[uItem];
							if(!pEntry)
								return 0;
							if(pEntry->uNextItem) {
								uItem = pEntry->uNextItem;
								break;
							}
						}
					}

				pEntry = pData ->pTreeItems[uItem];
				if(!pEntry)
					break;
				if(pEntry->uState & TVIS_SELECTED)
					return uItem;
			}

			return 0;

		case TVGN_NEXTITEM:
			if(uItem > pData->uTreeItemsMax) {
				if(uItem != U(TVI_ROOT))
					return 0;

				uItem = pData->uFirstChild;

				pEntry = pData->pTreeItems[uItem];
				if(!pEntry)
					return 0;

				return uItem;
			}

			pEntry = pData->pTreeItems[uItem];
			if(!pEntry)
				return 0;

			for(;;) {
				if(pEntry->uFirstChild) {
					uItem = pEntry->uFirstChild;
				} else
					if(pEntry->uNextItem) {
						uItem = pEntry->uNextItem;
					} else {
						for(;;) {
							uItem  = pEntry->uParent;
							pEntry = pData ->pTreeItems[uItem];
							if(!pEntry)
								return 0;
							if(pEntry->uNextItem) {
								uItem = pEntry->uNextItem;
								break;
							}
						}
					}

				pEntry = pData->pTreeItems[uItem];
				if(!pEntry)
					break;

				return uItem;
			}

			return 0;

		case TVGN_PREVIOUSVISIBLE:
			if(uItem > pData->uTreeItemsMax) {
				return 0;
			}

			pEntry = pData->pTreeItems[uItem];
			if(!pEntry)
				return 0;

			uPos = pEntry->uShowPos - 1;
			if(uPos <= pData->uScrollY)
				return 0;
			if(uPos > pData->uScrollY + pData->uPageEnties)
				return 0;

			return pData->pItemPos[uPos - 1];

		case TVGN_LASTVISIBLE:
			uPos = pData->uItemPosCount;
			if(uPos <= 0)
				return 0;
			return pData->pItemPos[uPos - 1];

		case TVGN_DROPHILITE:
			return pData->uTrackedItem;

		case TVGN_DROPHILITESUB:
			return pData->uTrackedSub;

		case TVGN_CARET:
			return pData->uSelectedItem;

		case TVGN_CARETSUB:
			return pData->uSelectedSub;

		case TVGN_FOCUS:
			return (pData->uFocusItem) ? pData->uFocusItem : pData->uSelectedItem;

		case TVGN_FOCUSSUB:
			return (pData->uFocusItem) ? pData->uFocusSub : pData->uSelectedSub;
	}

	return 0;
}


//*****************************************************************************
//*
//*		TreeListFindItem
//*
//*****************************************************************************
//	Sucht einen Kindeintrag mit einem bestimmten Text
//	pData	: Zeiger auf die Fensterdaten
//	uItem	: Ist die Nummer des Elterneintrages
//	pFind	: Die Struktur mit den Suchparametern
//	Ergibt die Nummer des Eintrages bzw. 0 wenn nicht gefunden wurde
static unsigned TreeListFindItem(TreeListData *pData, unsigned uItem, TVFIND *pFind) {
	int				iImage;
	BaseItem	   *pEntry;
	ExtraItem	   *pExtra;
	LPCTSTR			pCmpText;
	unsigned		uTextSize;
	unsigned		uChkParam;
	unsigned		uChkState;
	unsigned		uChkText;
	unsigned		uChkCase;
	unsigned		uTextLen;
	unsigned		uSub;

	if(pFind->uFlags & TVIF_CHILD) {						// In den Kindern suchen
		if(uItem > pData->uTreeItemsMax) {
			if(uItem != U(TVI_ROOT))
				return 0;
			uItem = pData->uFirstChild;
		} else {
			pEntry = pData->pTreeItems[uItem];
			if(!pEntry)
				return 0;

			uItem = pEntry->uFirstChild;
		}
	} else {
		if(uItem > pData->uTreeItemsMax) {					// Ist der Eintrag gültig
			return 0;
		}

		if(pFind->uFlags & TVIF_NEXT) {						// Beim nächsten Eintrag weitersuchen
			pEntry = pData->pTreeItems[uItem];
			if(!pEntry)
				return 0;

			uItem = pEntry->uNextItem;
		}
	}

	uChkParam = pFind->uFlags & TVIF_PARAM;
	uChkState = pFind->uFlags & TVIF_STATE;
	uChkText  = pFind->uFlags & TVIF_TEXT;
	uChkCase  = pFind->uFlags & TVIF_CASE;
	uSub	  = pFind->uColumn;
	uTextLen  = 0;

	if(uChkText) {
		uTextLen = str_len(pFind->pText);
	}

	for(; uItem; uItem = pEntry->uNextItem) {					// Durchlaufe alle Kinder
		pEntry = pData->pTreeItems[uItem];
		if(!pEntry)
			return 0;

		if(uChkParam && pFind->lParam != pEntry->lParam) {	// Vergleiche lParam
			continue;
		}
		// Vergleiche die State-Bits
		if(uChkState && ((pEntry->uState ^ pFind->uState)&pFind->uStateMask)) {
			continue;
		}

		if(uChkText == 0)
			break;

		if(uSub) {										// Text von Spalten
			if(uSub >= pData->uColumnCount)
				continue;

			pExtra = pData->pExtraItems[uSub - 1][uItem];
			if(!pExtra) {
				pCmpText = _T("");
				uTextSize = 0;
			} else {
				pCmpText = pExtra->pText;
				if(!pCmpText)
					pCmpText = _T("");
				uTextSize = pExtra->uTextSize;
			}
		} else {												// Text vom Haupteintrag
			if(pEntry->bCallback & TVIF_TEXT) {
				CallbackEntry(pData, pEntry, uItem, pEntry->bCallback, &iImage, &uTextSize, &pCmpText);
			} else {
				pCmpText = pEntry->pText;
				if(!pCmpText)
					pCmpText = _T("");
				uTextSize = pEntry->uTextSize;
			}
		}

		if(uTextLen != uTextSize)
			continue;

		if(uChkCase) {									// Zwischen Groß/Kleinbuchstaben unterscheiden
			if(!str_icmp(pCmpText, pFind->pText))
				break;
		} else {
			if(!memcmp(pCmpText, pFind->pText, uTextSize * sizeof(TCHAR)))
				break;
		}
	}

	return uItem;
}

//*****************************************************************************
//*
//*		TreeListNextSelUntil
//*
//*****************************************************************************
//	Sucht rekursiv ausgewählte Einträge
//	pData	: Zeiger auf die Fensterdaten
//	uItem	: Ist die Nummer des Eintrages nach dem mit der Suche begonnen werden soll
//	uStop	: Bestimmt bei welchem Eintrag abgebrochen werden soll werden soll
//	Ergibt die Nummer des Eintrages oder 0 wenn keiner gefunden wurde
static unsigned TreeListNextSelUntil(TreeListData *pData, unsigned uItem, unsigned uStop) {

	BaseItem	   *pEntry;

	if(uItem > pData->uTreeItemsMax) {
		if(uItem != U(TVI_ROOT))
			return 0;

		uItem = pData->uFirstChild;

		pEntry = pData->pTreeItems[uItem];
		if(!pEntry)
			return 0;
		if(pEntry->uState & TVIS_SELECTED)
			return uItem;

		uStop = 0;
	} else {
		pEntry = pData->pTreeItems[uItem];
		if(!pEntry)
			return 0;
		if(!pEntry->uFirstChild && uItem == uStop)
			return 0;
	}

	for(;;) {
		if(pEntry->uFirstChild) {
			uItem = pEntry->uFirstChild;
			if(uItem == uStop)
				return 0;
		} else
			if(pEntry->uNextItem) {
				uItem = pEntry->uNextItem;
				if(uItem == uStop)
					return 0;
			} else {
				for(;;) {
					uItem = pEntry->uParent;
					if(uItem == uStop)
						return 0;
					pEntry = pData ->pTreeItems[uItem];
					if(!pEntry)
						return 0;
					if(pEntry->uNextItem) {
						uItem = pEntry->uNextItem;
						if(uItem == uStop)
							return 0;
						break;
					}
				}
			}

		pEntry = pData ->pTreeItems[uItem];
		if(!pEntry)
			break;
		if(pEntry->uState & TVIS_SELECTED)
			return uItem;
	}

	return 0;
}

//*****************************************************************************
//*
//*		TreeListNextUnselUntil
//*
//*****************************************************************************
//	Sucht rekursiv nicht ausgewählte Einträge
//	pData	: Zeiger auf die Fensterdaten
//	uItem	: Ist die Nummer des Eintrages nach dem mit der Suche begonnen werden soll
//	uStop	: Bestimmt bei welchem Eintrag abgebrochen werden soll werden soll
//	Ergibt die Nummer des Eintrages oder 0 wenn keiner gefunden wurde
static unsigned TreeListNextUnselUntil(TreeListData *pData, unsigned uItem, unsigned uStop) {

	BaseItem	   *pEntry;

	if(uItem > pData->uTreeItemsMax) {
		if(uItem != U(TVI_ROOT))
			return 0;

		uItem = pData->uFirstChild;

		pEntry = pData->pTreeItems[uItem];
		if(!pEntry)
			return 0;
		if((pEntry->uState & TVIS_SELECTED) == 0)
			return uItem;

		uStop = 0;
	} else {
		pEntry = pData->pTreeItems[uItem];
		if(!pEntry)
			return 0;
		if(!pEntry->uFirstChild && uItem == uStop)
			return 0;
	}

	for(;;) {
		if(pEntry->uFirstChild) {
			uItem = pEntry->uFirstChild;
			if(uItem == uStop)
				return 0;
		} else
			if(pEntry->uNextItem) {
				uItem = pEntry->uNextItem;
				if(uItem == uStop)
					return 0;
			} else {
				for(;;) {
					uItem = pEntry->uParent;
					if(uItem == uStop)
						return 0;
					pEntry = pData->pTreeItems[uItem];
					if(!pEntry)
						return 0;
					if(pEntry->uNextItem) {
						uItem = pEntry->uNextItem;
						if(uItem == uStop)
							return 0;
						break;
					}
				}
			}

		pEntry = pData->pTreeItems[uItem];
		if(!pEntry)
			break;
		if((pEntry->uState & TVIS_SELECTED) == 0)
			return uItem;
	}

	return 0;
}

//*****************************************************************************
//*
//*		TreeListChangeCheckbox
//*
//*****************************************************************************
//	Schaltet eine Checkboc um
//	pData	: Zeiger auf die Fensterdaten
//	uItem	: Ist der Eintrag der geändert werden soll
//	pInfo	:
static void TreeListChangeCheckbox(TreeListData *pData, UINT uItem, int iPosX, int iPosY) {

	BaseItem   *pTemp;
	BaseItem   *pEntry;
	NMTREEVIEW	sNotify;
	TV_ITEM		sItem;
	UINT		uBits;

	sNotify.itemOld.mask  = 0;
	sNotify.itemOld.hItem = 0;

	pEntry = pData->pTreeItems[uItem];
	uBits  = pEntry->uState & TVIS_STATEIMAGEMASK;

	if(pData->uStyleEx & TVS_EX_SINGLECHECKBOX) {			// Einzelauswahl
		pTemp = pData->pTreeItems[pData->uSingleSel];

		if(pData->uSingleSel == uItem) {
			if(pData->uStyleEx & TVS_EX_BITCHECKBOX) {
				if(uBits & 0x1000)
					return;
			} else {
				if(uBits == 0x2000)
					return;
			}
		} else
			if(pData->uSingleSel && pTemp) {				// Anderer Eintrag gewählt
				sItem.hItem		= (HTREEITEM)(ULONG_PTR)pData->uSingleSel;
				sItem.mask		= TVIF_STATE;
				sItem.stateMask	= TVIS_STATEIMAGEMASK;
				sItem.state		= (pData->uStyleEx & TVS_EX_BITCHECKBOX) ?	0x0000 : 0x1000;

				TreeListSetItem(pData, &sItem);

				sNotify.itemOld.mask		= TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_TEXT;
				sNotify.itemOld.hItem		= (HTREEITEM)(ULONG_PTR)pData->uSingleSel;
				sNotify.itemOld.stateMask	= 0xFFFFFFFF;
				sNotify.itemOld.state		= pTemp->uState;
				sNotify.itemOld.lParam		= pTemp->lParam;
				sNotify.itemOld.pszText		= pTemp->pText;
				sNotify.itemOld.cchTextMax	= pTemp->uTextSize;
				sNotify.itemOld.cChildren	= 0;
			}
	}

	sItem.hItem		= (HTREEITEM)(ULONG_PTR)uItem;
	sItem.mask		= TVIF_STATE;
	sItem.stateMask	= TVIS_STATEIMAGEMASK;

	if(pData->uStyleEx & TVS_EX_BITCHECKBOX)
		sItem.state	= (uBits ^ 0x1000);
	else
		sItem.state	= (uBits & 0x1000) ? 0x2000 : 0x1000;

	TreeListSetItem(pData, &sItem);
	pData->uSingleSel = uItem;

	sNotify.hdr.code			= TVN_CBSTATECHANGED;
	sNotify.action				= VK_DBLCLK;
	sNotify.itemNew.mask		= TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_TEXT;
	sNotify.itemNew.hItem		= (HTREEITEM)(ULONG_PTR)uItem;
	sNotify.itemNew.stateMask	= 0xFFFFFFFF;
	sNotify.itemNew.state		= pEntry->uState;
	sNotify.itemNew.lParam		= pEntry->lParam;
	sNotify.itemNew.pszText		= pEntry->pText;
	sNotify.itemNew.cchTextMax	= pEntry->uTextSize;
	sNotify.itemNew.cChildren	= 0;
	sNotify.ptDrag.x			= iPosX;
	sNotify.ptDrag.y			= iPosY;

	UNLOCK(pData);
	SendNotify(pData, &sNotify.hdr);
	LOCK(pData);
}

//*****************************************************************************
//*
//*		TreeListMouseNotify
//*
//*****************************************************************************
//	Soll für einen Mausklick eine
//	pData	: Zeiger auf die Fensterdaten
//	uMsg	: Message des Mausklicks
//	wParam	: WPARAM des Mausklicks
//	lParam	: LPARAM des Mausklicks
static void TreeListMouseNotify(TreeListData *pData, UINT uMsg, WPARAM wParam, LPARAM lParam) {

	TV_HITTESTINFO	sInfo;
	NMTREEVIEW		sNotify;
	BaseItem	   *pEntry;
	unsigned		uItem;

	sInfo.flags = (UINT) wParam;
	sInfo.pt.x	= LOWORD(lParam);
	sInfo.pt.y	= HIWORD(lParam);
	uItem		= TreeListHitTest(pData, &sInfo);

	if(uItem) {
		pEntry						= pData->pTreeItems[uItem];
		sNotify.itemNew.stateMask	= 0xFFFFFFFF;
		sNotify.itemNew.state		= pEntry->uState;
		sNotify.itemNew.lParam		= pEntry->lParam;
		sNotify.itemNew.cChildren	= TVHT_SUBTOCOL(sInfo.flags);
	} else {
		sNotify.itemNew.stateMask	= 0;
		sNotify.itemNew.state		= 0;
		sNotify.itemNew.lParam		= 0;
	}

	sNotify.action					= 0;
	sNotify.hdr.code				= uMsg;
	sNotify.itemNew.mask			= TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_SUBNUMBER;
	sNotify.itemNew.hItem			= (HTREEITEM)(ULONG_PTR)uItem;
	sNotify.itemNew.pszText			= (LPTSTR) - 1;
	sNotify.itemNew.cchTextMax		= -1;
	sNotify.itemOld.mask			= 0;
	sNotify.ptDrag.x				= LOWORD(lParam);
	sNotify.ptDrag.y				= HIWORD(lParam);

	SendNotify(pData, &sNotify.hdr);

}

//*****************************************************************************
//*
//*		TreeListMouseClick
//*
//*****************************************************************************
//	Soll ein Mausklick ausgeführt werden. ACHTUNG UNLOCK wird ausgeführt
//	pData	: Zeiger auf die Fensterdaten
//	uMsg	: Message des Mausklicks
//	wParam	: WPARAM des Mausklicks
//	lParam	: LPARAM des Mausklicks
static void TreeListMouseClick(TreeListData *pData, UINT uMsg, WPARAM wParam, LPARAM lParam) {
	ExtraItem      *pExtra;
	BaseItem	   *pEntry;
	BaseItem	   *pTemp;
	TV_HITTESTINFO	sInfo;
	NMTREEVIEW		sNotify;
	unsigned		uOldSub;
	unsigned		uOldItem;
	unsigned		uMaskItem;
	unsigned		uMaskSub;
	unsigned		uMsgOld;
	unsigned		uToggle;
	unsigned		uItem;
	unsigned		uLine;
	unsigned		uTemp;
	unsigned		uStop;
	unsigned		uSub;
	unsigned		uSel;
	unsigned		uPos;
	unsigned		uNum;
	int				iMode;
	int				iAdd;

	if(!pData->cIsEnabled) {								// Ist das Fenster freigegeben
		return;
	}

	if(!pData->cHasFocus) {								// Hat das Fenster den Focus
		if(GetFocus() != pData->hWnd) {
			UNLOCK(pData);
			SetFocus(pData->hWnd);
			LOCK(pData);

			if(GetFocus() != pData->hWnd) {
				UNLOCK(pData);
				return;
			}
		} else {
			pData->cHasFocus = 1;
		}
	}

	sInfo.flags = (UINT) wParam;
	sInfo.pt.x	= LOWORD(lParam);
	sInfo.pt.y	= HIWORD(lParam);
	uItem		= TreeListHitTest(pData, &sInfo);
	uMsgOld		= uMsg;
	uToggle		= 0;

	if(uItem) {											// Wurde auf einen Eintrag getrückt
		pEntry = pData->pTreeItems[uItem];

		if(pData->uStyle & TVS_FULLROWSELECT) {
			uMaskItem = TVHT_ONITEMICON | TVHT_ONITEMLABEL | TVHT_ONITEMSTATEICON | TVHT_ONITEMRIGHT;
			uMaskSub  =	TVHT_ONSUBICON | TVHT_ONSUBLABEL | TVHT_ONSUBRIGHT;
		} else {
			uMaskItem = TVHT_ONITEMICON | TVHT_ONITEMLABEL | TVHT_ONITEMSTATEICON;
			uMaskSub  =	TVHT_ONSUBICON | TVHT_ONSUBLABEL;
		}

		if(sInfo.flags & TVHT_ONITEMBUTTON) {				// Eintrag aufklappen
			if(uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONDBLCLK) {
				pEntry = pData->pTreeItems[uItem];
				if(pEntry && (pEntry->uState & (TVIS_EXPANDED | TVIS_EXPANDPARTIAL)) == (TVIS_EXPANDED | TVIS_EXPANDPARTIAL)) {
					TreeListToggleItem(pData, uItem, 0);		// Von + auf - umschalten
				} else {
					uToggle = 1;							// Eintrag auflappen
				}
			}
		} else
			if(sInfo.flags & TVHT_ONITEMSTATEICON) {		// Checkbox umschalten
				if(pData->uStyle & TVS_CHECKBOXES)
					if(uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONDBLCLK) {
						TreeListChangeCheckbox(pData, uItem, sInfo.pt.x, sInfo.pt.y);
					}
			} else
				if(sInfo.flags & uMaskItem) {					// Eintrag auswählen
					if(!(pData->uStyle & TVS_DISABLEDRAGDROP)) {
						if(uMsg == WM_LBUTTONDOWN) {
							pData->uDragFlags = MK_LBUTTON;
							pData->uDragItem  = uItem;
							pData->uDragSub   = 0;
						}

						if(uMsg == WM_RBUTTONDOWN) {
							pData->uDragFlags = MK_RBUTTON;
							pData->uDragItem  = uItem;
							pData->uDragSub   = 0;
						}
					}

					if(pData->uStyleEx & TVS_EX_MULTISELECT) {		// Mehrfachauswahl erlaubt
						if(uMsg == WM_RBUTTONDOWN)					// Keine Abwahl wenn mehrer Einträge aus gewählt
							if(pData->uSelectedCount > 1) {
								pEntry = pData->pTreeItems[uItem];
								if(pEntry->uState & TVIS_SELECTED) {
									goto End;
								}
							}

						if(uMsg == WM_LBUTTONDOWN)					// Spezialsteuerung für Multiselect bei Darg
							if(!(wParam & (MK_CONTROL | MK_SHIFT)))
								if(pData->uSelectedCount > 0) {
									pEntry =   pData->pTreeItems[uItem];
									if(pEntry && (pEntry->uState & TVIS_SELECTED)) {
										pData->cClickFlag = 1;
										pData->cClickEdit = 0;
										goto End;
									}
								}

						if(uMsg == WM_LBUTTONUP && pData->cClickFlag) {
							wParam &= ~(MK_CONTROL | MK_SHIFT);
							uMsg    =   WM_LBUTTONDOWN;
						}

						if(wParam & MK_SHIFT) {						// Bis zum angeklicken Auswählen
							if(uMsg != WM_LBUTTONDOWN)
								if(uMsg != WM_LBUTTONDBLCLK)
									goto End;

							pData->cClickEdit = 0;

							uTemp = pData->uSelectedItem;
							if(!uTemp) {
								uTemp = pData->uFocusItem;
								if(!uTemp || !pData->cReSelect)
									goto End;
							}

							pEntry = pData->pTreeItems[uTemp];
							uLine  = pEntry->uShowPos;
							if(!uLine)
								goto End;

							pEntry = pData->pTreeItems[uItem];
							uStop  = pEntry->uShowPos;
							if(!uStop)
								goto End;

							if(pData->uSelectedCount <= 1) {
								pData->uSelectedBase = uTemp;
							}

							// Shift-Select neu auswählen
							if(pData->cReSelect && pData->uSelectedBase) {
								TreeListSelectItem(pData, uItem, 0, TVC_BYMOUSE | TVC_DESELECT);
								TreeListEnsureVisible(pData, pData->uSelectedItem, pData->uSelectedSub);

								uTemp = pData->uSelectedBase;
								pTemp = pData->pTreeItems[uTemp];
								uStop = uItem;

								while(pTemp && pTemp->uShowPos == 0) {
									pTemp = pData->pTreeItems[pTemp->uParent];
									if(!pTemp)
										break;
								}

								if(pTemp) {
									if(!(pTemp->uState & TVIS_SELECTED)) {
										TreeListXorSelectItem(pData, uTemp, TVC_BYKEYBOARD);
									}

									if(pTemp->uShowPos < pEntry->uShowPos) {
										uStop = uItem;
									} else {
										uStop = uTemp;
										uTemp = uItem;
									}

									for(;;) {
										uTemp = TreeListNextUnselUntil(pData, uTemp, uStop);
										if(!uTemp)
											break;
										TreeListXorSelectItem(pData, uTemp, TVC_BYKEYBOARD);
									}
								}

								pData->cReSelect = 0;
								goto End;
							}

							TreeListSelectItem(pData, uItem, 0, TVC_BYMOUSE);

							iAdd = (uLine > uStop) ? -1 : 1;
							uPos =  uLine;

							if(uPos != uStop) {
								pEntry = pData->pTreeItems[pData->uSelectedBase];

								while(pEntry && pEntry->uShowPos == 0) {
									pEntry = pData->pTreeItems[pEntry->uParent];
								}

								if(pEntry) {
									if(iAdd < 0){
										if(pEntry->uShowPos > uPos && uPos > uStop)
											uPos--;
									}
									else {
										if(pEntry->uShowPos < uPos && uPos < uStop)
											uPos++;
									}
								}
							}

							for(;; uPos += iAdd) {					// Einträge wählen
								uTemp  = pData->pItemPos  [uPos - 1];
								pEntry = pData->pTreeItems[uTemp ];
								uSel   = pEntry->uState & TVIS_SELECTED;

								if(uTemp != pData->uSelectedBase || !uSel) {
									TreeListXorSelectItem(pData, uTemp, TVC_BYMOUSE);
									uSel = pEntry->uState & TVIS_SELECTED;
								}

								// Auch unsichtbare Kinder wählen
								pTemp =   pData->pTreeItems[pEntry->uFirstChild];
								if(pTemp && !pTemp->uShowPos) {
									if(uSel) {
										for(uNum = uTemp;;) {		// Kinder auswählen
											uNum = TreeListNextUnselUntil(pData, uNum, uTemp);
											if(!uNum)
												break;
											TreeListXorSelectItem(pData, uNum, TVC_BYMOUSE);
										}
									} else {
										for(uNum = uTemp;;) {		// Kinder abwählen
											uNum = TreeListNextSelUntil(pData, uNum, uTemp);
											if(!uNum)
												break;
											TreeListXorSelectItem(pData, uNum, TVC_BYMOUSE);
										}
									}
								}

								if(uPos == uStop)
									break;
							}

							TreeListRemoveFocus(pData);

							pEntry = pData->pTreeItems[uItem];
							uSub   = TVHT_SUBTOCOL(sInfo.flags);
							if(!(pData->uStyleEx & TVS_EX_SUBSELECT))
								uSub = 0;

							pData->uFocusSub     = uSub;
							pData->uFocusItem    = uItem;

							if(uSub) {
								pExtra = pData->pExtraItems[uSub - 1][uItem];
								if(pExtra)
									pExtra->bFlags |= TVIX_FOCUSED;
							} else {
								pEntry->bFlags |= TVIX_FOCUSED;
							}

							UpdateRect(pData, uItem, uSub);

							TreeListSelectItem(pData, uItem, 0, TVC_BYMOUSE);
							TreeListEnsureVisible(pData, pData->uSelectedItem, pData->uSelectedSub);

							goto End;
						}

						if(wParam & MK_CONTROL) {					// Einzelen Eintrag umschalten
							pData->uSelectedBase = uItem;
							pData->cReSelect	 = 1;
							pData->cClickEdit	 = 0;

							pEntry = pData->pTreeItems[uItem];
							if(pEntry && (pEntry->uState & TVIS_SELECTED)) {
								if(uMsg != WM_LBUTTONDOWN)
									if(uMsg != WM_LBUTTONDBLCLK)
										goto End;

								TreeListSelectItem(pData, 0, 0, TVC_BYMOUSE);

								if(TreeListXorSelectItem(pData, uItem, TVC_BYMOUSE)) {
									// Auch unsichtbare Kinder abwählen
									pTemp =   pData->pTreeItems[pEntry->uFirstChild];
									if(pTemp && !pTemp->uShowPos)
										for(uTemp = uItem;;) {
											uTemp = TreeListNextSelUntil(pData, uTemp, uItem);
											if(!uTemp)
												break;
											TreeListXorSelectItem(pData, uTemp, TVC_BYMOUSE);
										}
								}

								uTemp = pData->uFocusItem;
								if(uTemp) {						// Ist ein Focus definiert
									TreeListRemoveFocus(pData);
								}

								uSub = TVHT_SUBTOCOL(sInfo.flags);
								if(!(pData->uStyleEx & TVS_EX_SUBSELECT))
									uSub = 0;

								pData->uFocusItem = uItem;
								pData->uFocusSub  = uSub;

								if(uSub) {
									pExtra = pData->pExtraItems[uSub - 1][uItem];
									if(pExtra)
										pExtra->bFlags |= TVIX_FOCUSED;
								} else {
									pEntry->bFlags |= TVIX_FOCUSED;
								}

								UpdateRect(pData, uItem, uSub);

								goto End;
							}
						}
					}

					if(wParam & MK_CONTROL) {						// Ist die Ctrl-Taste gedrückt
						iMode = TVC_BYMOUSE;
					} else {
						iMode = TVC_BYMOUSE | TVC_DESELECT;
					}

					uOldSub	  = pData->uSelectedSub;
					uOldItem  = pData->uSelectedItem;

					TreeListRemoveFocus(pData);
					TreeListSelectItem(pData, uItem, 0, iMode);
					TreeListEnsureVisible(pData, pData->uSelectedItem, pData->uSelectedSub);
					pEntry = pData->pTreeItems[uItem];

					if(uMsg == WM_LBUTTONDBLCLK) {
						// Auto-Edit mit Doppelklick
						if(pData->aColumn[0].bFlags & TVAE_DBLCLICK && pData->aColumn[0].bEdit) {
							TreeListStartAutoEdit(pData, 0, VK_DBLCLK, lParam);
						} else
							if(TreeListStartNotifyEdit(pData, pData->uSelectedItem, 0, VK_DBLCLK, lParam)) {

							} else
								if(pEntry && (pEntry->uState & (TVIS_EXPANDED | TVIS_EXPANDPARTIAL)) == (TVIS_EXPANDED | TVIS_EXPANDPARTIAL)) {
									TreeListToggleItem(pData, uItem, 0);		// Von + auf - umschalten
								} else {
									uToggle = 1;							// Eintrag auflappen
								}
					} else
						if(uMsgOld == WM_LBUTTONDOWN) {
							if(pData->uStyleEx & TVS_EX_EDITCLICK)
								if(!(wParam & (MK_CONTROL | MK_SHIFT)))
									if(sInfo.flags & uMaskItem&~TVHT_ONITEMICON)
										if(uOldSub == pData->uSelectedSub && uOldItem == pData->uSelectedItem) {
											pData->cClickEdit = 1;
										}
						} else
							if(uMsgOld == WM_LBUTTONUP) {
								if(pData->uStyleEx & TVS_EX_EDITCLICK)
									if(pData->cClickFlag && !pData->cClickEdit)
										if(!(wParam & (MK_CONTROL | MK_SHIFT)))
											if(sInfo.flags & uMaskItem&~TVHT_ONITEMICON)
												if(uOldSub == pData->uSelectedSub && uOldItem == pData->uSelectedItem) {
													pData->cClickEdit = 1;
												}

								if(pData->cClickEdit) {
									pData->cClickEdit = 0;

									if(pData->aColumn[0].bEdit) {
										TreeListStartAutoEdit(pData, 0, VK_EDITCLK, lParam);
									} else {
										TreeListStartNotifyEdit(pData, pData->uSelectedItem, 0, VK_EDITCLK, lParam);
									}
								}
							}
				} else
					if(sInfo.flags & uMaskSub) {					// Extra-Eintrag auswählen
						if(pData->uStyleEx & TVS_EX_SUBSELECT)
							uSub = TVHT_SUBTOCOL(sInfo.flags);
						else
							uSub = 0;

						if(!(pData->uStyle & TVS_DISABLEDRAGDROP)) {
							if(uMsg == WM_LBUTTONDOWN) {
								pData->uDragFlags = MK_LBUTTON;
								pData->uDragItem  = uItem;
								pData->uDragSub   = uSub;
							}

							if(uMsg == WM_RBUTTONDOWN) {
								pData->uDragFlags = MK_RBUTTON;
								pData->uDragItem  = uItem;
								pData->uDragSub   = uSub;
							}
						}

						if(wParam & MK_CONTROL) {
							if(pData->uStyleEx & TVS_EX_MULTISELECT) {	// Auswahl umschalten
								pEntry = pData->pTreeItems[uItem];
								if(pEntry && (pEntry->uState & TVIS_SELECTED)) {
									if(uMsg != WM_LBUTTONDOWN)
										if(uMsg != WM_LBUTTONDBLCLK)
											goto End;

									TreeListSelectItem(pData, 0, 0, TVC_BYMOUSE);

									if(TreeListXorSelectItem(pData, uItem, TVC_BYMOUSE)) {
										// Auch unsichtbare Kinder abwählen
										pTemp =   pData->pTreeItems[pEntry->uFirstChild];
										if(pTemp && !pTemp->uShowPos)
											for(uTemp = uItem;;) {
												uTemp = TreeListNextSelUntil(pData, uTemp, uItem);
												if(!uTemp)
													break;
												TreeListXorSelectItem(pData, uTemp, TVC_BYMOUSE);
											}
									}

									TreeListRemoveFocus(pData);

									pData->cClickEdit = 0;
									pData->uFocusItem = uItem;
									pData->uFocusSub  = uSub;

									if(uSub) {
										pExtra = pData->pExtraItems[uSub - 1][uItem];
										if(pExtra)
											pExtra->bFlags |= TVIX_FOCUSED;
									} else {
										pEntry->bFlags |= TVIX_FOCUSED;
									}

									UpdateRect(pData, uItem, uSub);

									goto End;
								}
							}

							iMode = TVC_BYMOUSE;
						} else {
							iMode = TVC_BYMOUSE | TVC_DESELECT;
						}

						uOldSub	  = pData->uSelectedSub;
						uOldItem  = pData->uSelectedItem;

						TreeListRemoveFocus(pData);
						TreeListSelectItem(pData, uItem, uSub, (wParam & MK_CONTROL) ? TVC_BYMOUSE : TVC_BYMOUSE | TVC_DESELECT);
						TreeListEnsureVisible(pData, pData->uSelectedItem, pData->uSelectedSub);
						pEntry = pData->pTreeItems[uItem];

						if(uMsg == WM_LBUTTONDBLCLK) {
							if(pData->aColumn[uSub].bEdit &&			// Auto-Edit mit Doppelklick
							        ((pData->aColumn[uSub].bFlags & TVAE_DBLCLICK) ||
							                ((pData->aColumn[uSub].bFlags & TVAE_ICONCLICK) && (sInfo.flags & TVHT_ONSUBICON)))) {
								TreeListStartAutoEdit(pData, uSub, (sInfo.flags & TVHT_ONSUBICON) ? VK_ICONCLK : VK_DBLCLK, lParam);
							} else
								if(TreeListStartNotifyEdit(pData, uItem, uSub, VK_DBLCLK, lParam)) {

								} else {
									uToggle = 1;							// Eintrag auflappen
								}
						} else
							if(uMsg == WM_LBUTTONDOWN) {
								if(pData->aColumn[uSub].bEdit)				// Auto-Edit mit Klick auf Icon
									if(pData->aColumn[uSub].bFlags & TVAE_ICONCLICK)
										if(sInfo.flags & TVHT_ONSUBICON) {
											TreeListStartAutoEdit(pData, uSub, VK_ICONCLK, lParam);
										}

								if(pData->uStyleEx & TVS_EX_EDITCLICK)
									if(uSub && !(wParam & (MK_CONTROL | MK_SHIFT)))
										if(sInfo.flags & uMaskSub&~TVHT_ONSUBICON)
											if(uOldSub == pData->uSelectedSub && uOldItem == pData->uSelectedItem) {
												pData->cClickEdit = 1;
											}
							} else
								if(uMsg == WM_LBUTTONUP) {
									if(pData->cClickEdit) {
										pData->cClickEdit = 0;

										if(pData->aColumn[uSub].bEdit) {
											TreeListStartAutoEdit(pData, uSub, VK_EDITCLK, lParam);
										} else {
											TreeListStartNotifyEdit(pData, uItem, uSub, VK_EDITCLK, lParam);
										}
									}
								}
					}
	} else {
		pEntry = NULL;
		uSub   = 0;
	}

End:

	switch(uMsgOld) {
		case WM_LBUTTONUP:
			sNotify.hdr.code = TVN_LBUTTONUP;
			break;
		case WM_LBUTTONDOWN:
			sNotify.hdr.code = NM_CLICK;
			break;
		case WM_LBUTTONDBLCLK:
			sNotify.hdr.code = NM_DBLCLK;
			break;
		case WM_RBUTTONUP:
			sNotify.hdr.code = TVN_RBUTTONUP;
			break;
		case WM_RBUTTONDOWN:
			sNotify.hdr.code = NM_RCLICK;
			break;
		case WM_RBUTTONDBLCLK:
			sNotify.hdr.code = NM_RDBLCLK;
			break;
		default
				:
			UNLOCK(pData);
			return;
	}

	if(pEntry) {
		sNotify.itemNew.stateMask	= 0xFFFFFFFF;
		sNotify.itemNew.state		= pEntry->uState;
		sNotify.itemNew.lParam		= pEntry->lParam;
		sNotify.itemNew.cChildren	= TVHT_SUBTOCOL(sInfo.flags);
	} else {
		sNotify.itemNew.stateMask	= 0;
		sNotify.itemNew.state		= 0;
		sNotify.itemNew.lParam		= 0;
	}

	sNotify.action					= 0;
	sNotify.itemNew.mask			= TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_SUBNUMBER;
	sNotify.itemNew.hItem			= (HTREEITEM)(ULONG_PTR)uItem;
	sNotify.itemNew.pszText			= (LPTSTR) - 1;
	sNotify.itemNew.cchTextMax		= -1;
	sNotify.itemOld.mask			= 0;
	sNotify.ptDrag.x				= LOWORD(lParam);
	sNotify.ptDrag.y				= HIWORD(lParam);

	UNLOCK(pData);

	if(!SendNotify(pData, &sNotify.hdr)) {
		if(uToggle) {										// Aufklappen nur wenn Notify nicht abgefangen
			LOCK(pData);
			TreeListToggleItem(pData, uItem, 0);
			UNLOCK(pData);
		}
	}

}

//*****************************************************************************
//*
//*		TreeListChar
//*
//*****************************************************************************
//	Eingabe von Buchstaben über die WM_CHAR Nachricht
//	pData	: Zeiger auf die Fensterdaten
//	nChar	: Ist das Zeichen das eingegeben wurde
static void TreeListChar(TreeListData *pData, UINT nChar, LPARAM lParam) {

	LPTSTR			pName;
	ExtraItem	   *pExtra;
	BaseItem	   *pEntry;
	unsigned		uDelta;
	unsigned		uItem;
	unsigned 		uTick;
	unsigned		uSub;
	unsigned		uPos;
	unsigned		uVal;
	int				iNum;
	int				iMax;
	int				i;

	if(nChar >= ' ') {
		if(pData->cKeyIgnore)
			return;						// Taste soll ignoriert werden

		iMax	= pData->uItemPosCount;
		uSub	= pData->uSelectedSub;
		i		= 0;

		if(TVIS_EDIT(pData->aColumn[uSub].bEdit))			// Bei AutoEdit keine Auswahl
			if(!(pData->aColumn[uSub].bFlags & TVAE_ONLYRETURN)) {
				TreeListStartAutoEdit(pData, uSub, nChar, 0);
				return;
			}

		if(TreeListStartNotifyEdit(pData, pData->uSelectedItem, uSub, nChar | VK_ISACHAR, lParam)) {
			return;
		}

		if(iMax <= 0)
			return;

		iNum = pData->uSelectedItem;						// Hole die Startzeile
		if(iNum > 0) {
			iNum = pData->pTreeItems[iNum]->uShowPos - 1;
			if(iNum < 0)
				iNum = 0;
		}

		if((nChar >= 'a' && nChar <= 'z') ||					// In Grosbuchstaben umwandeln
		        (nChar >= 224 && nChar <= 254)) {
			nChar -= 'a' - 'A';
		}

		if(!(pData->uStyleEx & TVS_EX_NOCHARSELCET))
			for(;;) {										// Suche Anfangsbuchstaben
				uTick  = GetTickCount();
				uDelta = (uKeyPos > 0) ? 750 : 500;

				if(uKeyPos >= 3)
					uDelta = 1000;
				if(uTick - uKeyLast > uDelta)
					uKeyPos = 0;
				if(uKeyPos >= 16)
					uKeyPos = 0;
				if(uKeyPos == 1 && cKeyData[0] == (TCHAR)nChar) {
					uKeyPos = 0;
				}

				cKeyData[uKeyPos] = (TCHAR)nChar;
				uKeyLast          = uTick;
				uKeyPos          += 1;

				if(uKeyPos > 1) {
					iNum--;
					if(iNum < 0)
						iNum = iMax - 1;
				}

				if(iMax <= 0)
					break;

				for(i = iNum + 1; i != iNum; i++) {				// Suche Übereinstimmung
					if(i >= iMax) {i = -1; continue;}
					uItem = pData->pItemPos[i];

					if(uSub) {
#ifndef __REACTOS__
						pExtra =  pExtra = pData->pExtraItems[uSub - 1][uItem];
#else
						pExtra = pData->pExtraItems[uSub - 1][uItem];
#endif
						pName  = (pExtra && pExtra->pText) ? pExtra->pText : _T("");
					} else {
						pEntry =  pData->pTreeItems[uItem];
						pName  = (pEntry && pEntry->pText) ? pEntry->pText : _T("");
					}

					for(uPos = 0; uPos < uKeyPos; uPos++) {		// Vergleiche die Texte
#if UNICODE
						uVal = pName[uPos];
#else
						uVal = ((unsigned char *)pName)[uPos];
#endif

						if((uVal >= 'a' && uVal <= 'z') ||		// In Grosbuchstaben umwandeln
						        (uVal >= 224 && uVal <= 254)) {
							uVal -= 'a' - 'A';
						}

						if(cKeyData[uPos] != (TCHAR)uVal) {
							break;
						}
					}

					if(uPos < uKeyPos)
						continue;

					if(TreeListSelectItem(pData, uItem, uSub, TVC_UNKNOWN | TVC_DESELECT)) {
						TreeListEnsureVisible(pData, uItem, uSub);
					}

					return;
				}

				if(i != iNum)
					break;
				if(uKeyPos <= 1)
					break;

				iNum++;
				if(iNum >= iMax)
					iNum = iMax - 1;

				uKeyPos = 0;
			}
	}

}

//*****************************************************************************
//*
//*		TreeListKeyDown
//*
//*****************************************************************************
//	Soll ein Tastentruck ausgeführt werden. ACHTUNG UNLOCK wird ausgeführt
//	pData	: Zeiger auf die Fensterdaten
//	wParam	: WPARAM des Mausklicks
//	lParam	: LPARAM des Mausklicks
//	Ergibt 0 wenn die Taste verarbeitet wurde
static void TreeListKeyDown(TreeListData *pData, WPARAM wParam, LPARAM lParam) {
	
	BaseItem	   *pTemp;
	BaseItem	   *pEntry;
	TV_KEYDOWN_EX	sNotify;
	unsigned		uTemp;
	unsigned		uItem;
	unsigned		uStop;
	unsigned		uVal;
	int				iLineCmp;
	int				iOldLine;
	int				iOldCol;
	int				iFocus;
	int				iShift;
	int				iBase;
	int				iLine;
	int				iAdd;
	int				iPos;
	int				iSub;
	int				iCol;
	int				iMax;
	int				iDel;
	int				iScr;
	int				iSel;

	if(!pData->cIsEnabled) {								// Ist das Fenster freigegeben
		return;
	}

	if(!pData->cHasFocus) {								// Hat das Fenster den Focus
		if(GetFocus() != pData->hWnd) {
			UNLOCK(pData);
			SetFocus(pData->hWnd);
			LOCK(pData);

			if(GetFocus() != pData->hWnd) {
				return;
			}
		} else {
			pData->cHasFocus = 1;
		}
	}

	if(wParam == VK_RETURN) {								// Wurde Enter gedrückt
		UNLOCK(pData);

		sNotify.hdr.code	=  NM_RETURN;
		sNotify.wVKey		= (WORD)(wParam);
		sNotify.wScan		= (WORD)(wParam >> 16);
		sNotify.flags		= (UINT)(lParam);

		SendNotify(pData, &sNotify.hdr);

		iSub = pData->uSelectedSub;

		if(pData->aColumn[iSub].bEdit) {					// Auto-Edit starten
			if(GetAsyncKeyState(VK_CONTROL) & 0x8000) {
				wParam = VK_ICONCLK;
				lParam = 0;
			}

			TreeListStartAutoEdit(pData, iSub, wParam, lParam);
		} else {
			TreeListStartNotifyEdit(pData, pData->uSelectedItem, iSub, wParam, lParam);
		}

		return;
	}

	pEntry = pData->pTreeItems[pData->uSelectedItem];
	iDel   = (GetAsyncKeyState(VK_SHIFT) & 0x8000) ? 0 : TVC_DESELECT;
	iScr   =  GetAsyncKeyState(VK_CONTROL) & 0x8000;

	if(iDel && iScr)										// Ende oder Anfang wählen
		if(wParam == VK_HOME || wParam == VK_END)
			if(pData->uStyleEx & TVS_EX_HOMEENDSELECT) {
				iDel = TVC_DESELECT;
				iScr = 0;
			}

	if(iDel && iScr) {									// Das Fenster scrollen
		iLine = pData->uScrollY;
		iSub  = pData->uScrollX;

		switch(wParam) {
			case VK_END:
				iLine  = pData->uTreeItemsCount;	// Ans Ende scrollen
				iLine -= pData->uPageEnties;
				break;

			case VK_HOME:
				iLine = 0;							// Zum Anfang scrollen
				break;

			case VK_LEFT:
				iSub -= 16;							// Links scrollen
				break;

			case VK_RIGHT:
				iSub += 16;							// Rechts scrollen
				break;

			case VK_UP:
				iLine--;							// Nach oben scrollen
				break;

			case VK_DOWN:
				iLine++;							// Nach unten scrollen
				break;

			case VK_PRIOR:
				iLine -= pData->uSizeX;				// Eine Seite nach oben
				break;

			case VK_NEXT:
				iLine += pData->uSizeX;				// Eine Seite nach unten
				break;

			case VK_SPACE:
				if(pEntry == NULL)
					break;				// Expantieren und schließen
				if(pEntry->bFlags & TVIX_HASBUTTON) {
					TreeListToggleItem(pData, pData->uSelectedItem, 0);
				}

				break;

			default
					:
				goto Exit;
		}

		if(iLine != (int)pData->uScrollY) {
			iMax  = pData->uItemPosCount;
			iMax -=	pData->uPageEnties - 1;

			if(iLine >= iMax)
				iLine = iMax;
			if(iLine < 0)
				iLine = 0;
			if(iLine != (int)pData->uScrollY)
				if(!(pData->uStyle & TVS_NOSCROLL)) {
					pData->uScrollY = iLine;
					SetScrollPos(pData->hWnd, SB_VERT, iLine, TRUE);
					UpdateView(pData);
				}
		}

		if(iSub != (int)pData->uScrollX) {
			uVal = pData->uColumnCount;
			if(uVal)
				iMax  = pData->aColumnXpos[uVal] - pData->uSizeX / 2;
			else
				iMax  = pData->iMaxSizeX;
			iMax -=	pData->uSizeX - pData->uSizeX / 2;

			if(iSub >= iMax)
				iSub = iMax;
			if(iSub < 0)
				iSub = 0;
			if(iSub != (int)pData->uScrollX)
				if(!(pData->uStyle & TVS_NOSCROLL)) {
					pData->uScrollX = iSub;
					SetScrollPos(pData->hWnd, SB_HORZ, iSub, TRUE);
					UpdateView(pData);

					if(pData->hHeader) {
						MoveWindow(pData->hHeader, -iSub, 0, pData->uSizeX + iSub, pData->uStartPixel, TRUE);
					}
				}
		}
	} else {													// Einen anderen Eintrag auswählen
		iSub  = pData->uSelectedSub;
		iCol  = pData->aColumn[iSub].bIndex;

		if(!pEntry) {
			iLine    =  0;
			iCol     =  0;
			iOldCol	 =  0;
			iOldLine = -1;

			uTemp = pData->uFocusItem;
			if(uTemp) {									// Ist ein Focus definiert
				pTemp = pData->pTreeItems[uTemp];

				while(pTemp && !pTemp->uShowPos) {
					pTemp = pData->pTreeItems[pTemp->uParent];
				}

				if(pTemp) {
					iLine = pTemp->uShowPos - 1;
					iSub  = pData->uFocusSub;
					iCol  = pData->aColumn[iSub].bIndex;
				}

				if(pData->uSelectedCount > 1) {
					iOldCol	 = iCol;
					iOldLine = iLine;
				} else {
					TreeListRemoveFocus(pData);
				}
			}
		} else {
			iLine =  pEntry->uShowPos - 1;
			if(iLine < 0)
				iLine = 0;

			uTemp	 =  pData->uSelectedItem;
			iOldLine =  iLine;
			iOldCol	 =  iCol;
		}

		switch(wParam) {
			case VK_END:
				iLine = pData->uItemPosCount - 1;
				break;
			case VK_HOME:
				iLine = 0;
				break;
			case VK_LEFT:
				if(pData->uColumnCount > 1 && (pData->uStyleEx & TVS_EX_SUBSELECT)) {
					while(iCol > 0) {
						iCol--;
						iSub = pData->aColumnPos[iCol];
						if(pData->aColumn[iSub].sReal > 0)
							break;
					}
				} else {
					if(! pEntry)
						break;
					if(!(pEntry->uState & TVIS_EXPANDED) || !pEntry->uFirstChild) {
						pTemp = pData->pTreeItems[pEntry->uParent];
						if(pTemp)
							iLine = pTemp->uShowPos - 1;
						break;
					}

					if(pEntry->bFlags & TVIX_HASBUTTON) {
						TreeListToggleItem(pData, pData->uSelectedItem, 0);
					}
				}

				break;

			case VK_RIGHT:
				if(pData->uColumnCount > 1 && (pData->uStyleEx & TVS_EX_SUBSELECT)) {
					while(iCol + 1 < (int)pData->uColumnCount) {
						iCol++;
						iSub = pData->aColumnPos[iCol];
						if(pData->aColumn[iSub].sReal > 0)
							break;
					}
				} else {
					if(!pEntry)
						break;
					if(pEntry->uState & TVIS_EXPANDED) {
						iLine++;
						break;
					}

					if(pEntry->bFlags & TVIX_HASBUTTON) {
						TreeListToggleItem(pData, pData->uSelectedItem, 0);
					}
				}

				break;

			case VK_UP:
				iLine--;
				break;
			case VK_DOWN:
				iLine++;
				break;

			case VK_PRIOR:
				iAdd = pData->uPageEnties - 1;			// Eine Seite nach oben
				if(iAdd <= 0)
					iAdd = 1;
				iLine -= iAdd;
				break;

			case VK_NEXT:
				iAdd = pData->uPageEnties - 1;			// Eine Seite nach unten
				if(iAdd <= 0)
					iAdd = 1;
				iLine += iAdd;
				break;

			case VK_BACK:		  								// Eine Ebene höher
				if(pEntry) {
					uItem = pEntry->uParent;
					if(!uItem)
						iLine = 0;
					else {
						iLine = pData->pTreeItems[uItem]->uShowPos - 1;
						if(iLine < 0)
							iLine = 0;
					}
				}

				break;

			case VK_SPACE:
				if(pEntry && iCol == 0 && (pData->uStyle & TVS_CHECKBOXES)) {
					TreeListChangeCheckbox(pData, pData->uSelectedItem, 0, 0);
				}

				break;

			default
					:
				goto Exit;
		}

		if(iCol >= (int)pData->uColumnCount)
			iCol = pData->uColumnCount - 1;
		if(iCol < 0)
			iCol = 0;
		if(iLine >= (int)pData->uItemPosCount)
			iLine = pData->uItemPosCount - 1;
		if(iLine < 0)
			iLine = 0;

		if(!(pData->uStyleEx & TVS_EX_SUBSELECT))
			iCol = 0;

		if(pData->uItemPosCount > 0)
			if(iLine != iOldLine || iCol != iOldCol) {			// Wurde ein anderer Eintrag ausgewählt
				if(pData->uSelectedCount > 1) {
					TreeListRemoveFocus(pData);
				} else {
					pData->uSelectedBase = uTemp;
				}

				iShift = iDel;

				if(pData->cReSelect) {						// Die Shift-Auswahl neu erstellen
					iDel = TVC_DESELECT;
				}

				uItem  = pData->pItemPos  [iLine];
				pTemp  = pData->pTreeItems[uItem];
				iSub   = pData->aColumnPos[iCol ];
				iFocus = (pTemp && (pTemp->uState & TVIS_SELECTED)) ? TVC_ONLYFOCUS : 0;

				TreeListSelectItem(pData, uItem, iSub, TVC_BYKEYBOARD | iDel | iFocus);

				if(!(pData->uStyle & TVS_NOSCROLL)) {
					TreeListEnsureVisible(pData, pData->uSelectedItem, pData->uSelectedSub);
				}

				if(pData->cReSelect && pData->uSelectedBase) {	// Shift-Select neu auswählen
					uTemp = pData->uSelectedBase;
					pTemp = pData->pTreeItems[uTemp];
					uStop = uItem;

					while(pTemp && pTemp->uShowPos == 0) {
						pTemp = pData->pTreeItems[pTemp->uParent];
						if(!pTemp)
							break;
					}

					if(pTemp && !iShift) {
						if(!(pTemp->uState & TVIS_SELECTED)) {
							TreeListXorSelectItem(pData, uTemp, TVC_BYKEYBOARD);
						}

						iLineCmp = pTemp->uShowPos - 1;
						if(iLineCmp < iLine) {
							uStop = uItem;
						} else {
							uStop = uTemp;
							uTemp = uItem;
						}

						for(;;) {
							uTemp = TreeListNextUnselUntil(pData, uTemp, uStop);
							if(!uTemp)
								break;
							TreeListXorSelectItem(pData, uTemp, TVC_BYKEYBOARD);
						}
					}

					pData->cReSelect = 0;
				}

				if((pData->uStyleEx & TVS_EX_MULTISELECT) && !iDel) {
					uVal  = pData->uSelectedBase;
					pTemp = pData->pTreeItems[uVal];
					iBase = (pTemp) ? pTemp->uShowPos - 1 : -1;

					if(iLine > iOldLine) {						// Nach oben oder nach unten
						iMax = (iOldLine > iBase) ? iOldLine + 1 : iOldLine;
						if(iMax < 0)
							iMax = 0;
						iPos = iLine;
					} else {
						iMax = iLine;
						iPos = (iOldLine < iBase) ? iOldLine - 1 : iOldLine;
						if(iPos < 0)
							iPos = 0;
					}

					for(; iPos >= iMax; iPos--) {					// Übersprungene Einträge mit aus/abwählen
						uItem = pData->pItemPos[iPos];

						if(iPos != iLine && iPos != iBase) {
							if(!TreeListXorSelectItem(pData, uItem, TVC_BYKEYBOARD))
								continue;
						}

						pTemp = pData->pTreeItems[uItem];	// Auch unsichtbare Kinder abwählen
						if(pTemp) {
							iSel  =  pTemp->uState & TVIS_SELECTED;
							pTemp =  pData->pTreeItems[pTemp->uFirstChild];
							if(!pTemp || pTemp->uShowPos)
								continue;
							if(!iSel) {
								for(uTemp = uItem;;) {
									uTemp = TreeListNextSelUntil(pData, uTemp, uItem);
									if(!uTemp)
										break;
									TreeListXorSelectItem(pData, uTemp, TVC_BYKEYBOARD);
								}
							} else {
								for(uTemp = uItem;;) {
									uTemp = TreeListNextUnselUntil(pData, uTemp, uItem);
									if(!uTemp)
										break;
									TreeListXorSelectItem(pData, uTemp, TVC_BYKEYBOARD);
								}
							}
						}
					}
				}
			}
	}

Exit:

	sNotify.hdr.code	=  TVN_KEYDOWN;
	sNotify.wVKey		= (WORD)(wParam);
	sNotify.wScan		= (WORD)(wParam >> 16);
	sNotify.flags		= (UINT)(lParam);

	UNLOCK(pData);

	pData->cKeyIgnore	= (char)SendNotify(pData, &sNotify.hdr);

}

//*****************************************************************************
//*
//*		TreeListSortItemsEx
//*
//*****************************************************************************
//	Sortiert die Kindereinträge eines Eintrages mitteles einer Funktion
//	pData		: Zeiger auf die Fensterdaten
//	pSortData	: Ist ein Zeiger auf die Sortiertaten
//	iMode		: 1=Rekursiv sortieren
//	Ergibt 1 wenn ok sonst 0
static int TreeListSortItemsEx(TreeListData *pData, TV_SORTEX *pSortData, int iMode) {

	TV_SORTEX	sSort;
	unsigned	uNum;
	unsigned	uItem;
	unsigned	uLast;
	unsigned	uFirst;
	unsigned	uParent;
	BaseItem   *pParent;
	BaseItem   *pEntry;
	BaseItem   *pNext;
	BaseItem  **pList;

    unsigned   *pItemList;
	unsigned   *pItemNew;
	unsigned	uEnties[128];
	unsigned	uPos;
	unsigned	uMax;

	PFNTVCOMPAREEX	pCompare;
	HTREEITEM		hItemTemp;
	LPARAM			lParamTemp;
	LPARAM			lParamSort;
	HWND			hTreeWnd;
	int				iLower, iUpper, iMiddle, iCmp;
	int				uMemL[30], uMemU[30];
	int				iStart, iLast;
	int				iLevel;
	unsigned		uTemp;


	pList = pData->pTreeItems;

	uParent = U(pSortData->hParent);
	if(uParent > pData->uTreeItemsMax) {					// Root-Eintrag sortieren
		if(uParent != U(TVI_ROOT))
			return 0;

		uLast  = pData->uLastChild;
		uFirst = pData->uFirstChild;
		if(uFirst == 0)
			return 1;
		if(uFirst == uLast) {								// Einzelner Eintrag
			pNext = pList[uFirst];
			if(!pNext->uFirstChild)
				return 1;

			sSort.hParent		= (HTREEITEM)(ULONG_PTR)uFirst;
			sSort.lParam		= pSortData->lParam;
			sSort.lpfnCompare	= pSortData->lpfnCompare;
			TreeListSortItemsEx(pData, &sSort, iMode);
			return 1;
		}

		pParent = 0;
		uParent = 0;
	} else {													// Untereintrag sortieren
		pParent = pList[uParent];
		if(pParent == NULL)
			return 0;

		uLast  = pParent->uLastChild;
		uFirst = pParent->uFirstChild;
		if(uFirst == 0)
			return 1;
		if(uFirst == uLast) {								// Einzelner Eintrag
			pNext = pList[uFirst];
			if(!pNext->uFirstChild)
				return 1;

			sSort.hParent		= (HTREEITEM)(ULONG_PTR)uFirst;
			sSort.lParam		= pSortData->lParam;
			sSort.lpfnCompare	= pSortData->lpfnCompare;
			TreeListSortItemsEx(pData, &sSort, iMode);
			return 1;
		}
	}

	if(iMode) {											// Sortiere die Untereinträge
		pNext				= pList[uFirst];
		sSort.hParent		= (HTREEITEM)(ULONG_PTR)uFirst;
		sSort.lParam		= pSortData->lParam;
		sSort.lpfnCompare	= pSortData->lpfnCompare;

		while(pNext) {
			if(pNext->uFirstChild) {
				TreeListSortItemsEx(pData, &sSort, SORT_NOUPDATE);
			}

			sSort.hParent	= (HTREEITEM)(ULONG_PTR)pNext->uNextItem;
			pNext			= pList[pNext->uNextItem];
		}
	}
	
	if(uFirst == uLast)
		return 1;

//******************** Erzeuge Eintragsliste **********************************
	uItem		= uFirst;
	pItemList	= uEnties;
	uMax		= 128;
	uPos		= 0;

	do	{													// Alle Kindeinträge suchen
		if(uPos >= uMax) {
			uMax *= 2;
			pItemNew	= new(unsigned, uMax);
			memcpy(pItemNew, pItemList, uPos * sizeof(pItemList[0]));
			if(uPos > 128)
				delete(pItemList);
			pItemList = pItemNew;
		}

		pItemList[uPos] = uItem;
		pNext			= pList[uItem];
		uItem			= pNext->uNextItem;
		uPos++;
	} while(uItem);
	

//************************* Qsort-Algorithmus *********************************
#define XCHANGE_MEM(a,b)	uTemp=pItemList[a];pItemList[a]=pItemList[b];pItemList[b]=uTemp;

	pData->cLockChanges = 1;
	UNLOCK(pData);

	hTreeWnd	= pData->hWnd;
	pCompare	= pSortData->lpfnCompare;
	lParamSort	= pSortData->lParam;
	iLast		= uPos - 1;
	iStart		= 0;
	iLevel		= 0;

	for(;;) {
		iLower	=  iStart;
		iMiddle = (iStart + iLast) >> 1;						// Mitte bereichnen
		iUpper	=  iLast + 1;

		XCHANGE_MEM(iMiddle, iLower);

		uItem	   = pItemList[iStart];
		hItemTemp  = (HTREEITEM)(ULONG_PTR)uItem;
		lParamTemp = pList[uItem]->lParam;

		for(;;) {
			do  {
				iLower++;
				if(iLower > iLast)
					break;
				uItem = pItemList[iLower];
				iCmp = pCompare(hTreeWnd, (HTREEITEM)(ULONG_PTR)uItem, hItemTemp, pList[uItem]->lParam, lParamTemp, lParamSort);
			} while(iCmp <= 0);

			do  {
				iUpper--;
				if(iUpper <= iStart)
					break;
				uItem = pItemList[iUpper];
				iCmp = pCompare(hTreeWnd, (HTREEITEM)(ULONG_PTR)uItem, hItemTemp, pList[uItem]->lParam, lParamTemp, lParamSort);
			} while(iCmp >= 0);

			if(iUpper < iLower)
				break;

			XCHANGE_MEM(iUpper, iLower);
		}

		XCHANGE_MEM(iStart, iUpper);

		if(iUpper - 1 - iStart >= iLast - iLower) {
			if(iStart + 1 < iUpper) {
				uMemL[iLevel] = iStart;
				uMemU[iLevel] = iUpper - 1;
				iLevel++;
			}
			if(iLower < iLast) {
				iStart = iLower;
				continue;
			}
		} else {
			if(iLower < iLast) {
				uMemL[iLevel] = iLower;
				uMemU[iLevel] = iLast;
				iLevel++;
			}

			if(iStart + 1 < iUpper) {
				iLast = iUpper - 1;
				continue;
			}
		}
		// Eine Ebene absteigen
		iLevel--;

		if(iLevel >= 0) {									// Noch Ebenen vorhanden
			iStart = uMemL[iLevel];
			iLast  = uMemU[iLevel];
			continue;
		}

		break;
	}

	LOCK(pData);
	pData->cLockChanges = 0;

//******************** Einträge neu einsortirenen *****************************
	uPos--;

	pEntry = pList[uParent];
	if(!pEntry) {
		pData->uFirstChild  = pItemList[ 0  ];
		pData->uLastChild   = pItemList[uPos];
	} else {
		pEntry->uFirstChild = pItemList[ 0  ];
		pEntry->uLastChild  = pItemList[uPos];
	}

	uLast  = 0;
	uItem  = pItemList[0];

	for(uNum = 0; uNum < uPos;) {								// Kinder neu einhängen
		pEntry = pList[uItem];
		pEntry->uPrevItem = uLast;

		uNum++;
		uLast  = uItem;
		uItem  = pItemList[uNum];

		pEntry->uNextItem = uItem;
	}

	pEntry = pList[uItem];
	pEntry->uPrevItem = uLast;
	pEntry->uNextItem = 0;

	if(iMode != SORT_NOUPDATE)								// Ausgabeliste neuerstellen
		if(uParent == 0 || (pParent->uShowPos && (pParent->uState & TVIS_EXPANDED))) {
			UpdateItems(pData, uParent);

			if(pData->uStyle & TVS_SHOWSELALWAYS)
				if(pData->uSelectedItem) {
					TreeListEnsureVisible(pData, pData->uSelectedItem, pData->uSelectedSub);
				}
		}

	if(uMax > 128)
		delete(pItemList);

	return 1;
}

//*****************************************************************************
//*
//*		TreeListSortItemsCb
//*
//*****************************************************************************
//	Sortiert die Kindereinträge eines Eintrages mitteles einer Funktion
//	pData		: Zeiger auf die Fensterdaten
//	pSortData	: Ist ein Zeiger auf die Sortiertaten
//	iMode		: 1=Rekursiv sortieren
//	Ergibt 1 wenn ok sonst 0
static int TreeListSortItemsCb(TreeListData *pData, TV_SORTCB *pSortData, int iMode) {

	TV_SORTCB	sSort;
	unsigned	uNum;
	unsigned	uItem;
	unsigned	uLast;
	unsigned	uFirst;
	unsigned	uParent;
	BaseItem   *pParent;
	BaseItem   *pEntry;
	BaseItem   *pNext;
	BaseItem  **pList;

	unsigned   *pItemList;
	unsigned   *pItemNew;
	unsigned	uEnties[128];
	unsigned	uPos;
	unsigned	uMax;

    PFNTVCOMPARE	pCompare;
	LPARAM			lParamTemp;
	LPARAM			lParamSort;
	int				iLower, iUpper, iMiddle, iCmp;
	int				uMemL[30], uMemU[30];
	int				iStart, iLast;
	int				iLevel;
	unsigned		uTemp;

	pList = pData->pTreeItems;

	uParent = U(pSortData->hParent);
	if(uParent > pData->uTreeItemsMax) {					// Root-Eintrag sortieren
		if(uParent != U(TVI_ROOT))
			return 0;

		uLast  = pData->uLastChild;
		uFirst = pData->uFirstChild;
		if(uFirst == 0)
			return 1;
		if(uFirst == uLast) {								// Einzelner Eintrag
			pNext = pList[uFirst];
			if(!pNext->uFirstChild)
				return 1;

			sSort.hParent		= (HTREEITEM)(ULONG_PTR)uFirst;
			sSort.lParam		= pSortData->lParam;
			sSort.lpfnCompare	= pSortData->lpfnCompare;
			TreeListSortItemsCb(pData, &sSort, iMode);
			return 1;
		}

		pParent = 0;
		uParent = 0;
	} else {													// Untereintrag sortieren
		pParent = pList[uParent];
		if(pParent == NULL)
			return 0;

		uLast  = pData->uLastChild;
		uFirst = pParent->uFirstChild;
		if(uFirst == 0)
			return 1;
		if(uFirst == uLast) {								// Einzelner Eintrag
			pNext = pList[uFirst];
			if(!pNext->uFirstChild)
				return 1;

			sSort.hParent		= (HTREEITEM)(ULONG_PTR)uFirst;
			sSort.lParam		= pSortData->lParam;
			sSort.lpfnCompare	= pSortData->lpfnCompare;
			TreeListSortItemsCb(pData, &sSort, iMode);
			return 1;
		}
	}

	if(iMode) {											// Sortiere die Untereinträge
		pNext				= pList[uFirst];
		sSort.hParent		= (HTREEITEM)(ULONG_PTR)uFirst;
		sSort.lParam		= pSortData->lParam;
		sSort.lpfnCompare	= pSortData->lpfnCompare;

		while(pNext) {
			if(pNext->uFirstChild) {
				TreeListSortItemsCb(pData, &sSort, SORT_NOUPDATE);
			}

			sSort.hParent	= (HTREEITEM)(ULONG_PTR)pNext->uNextItem;
			pNext			= pList[pNext->uNextItem];
		}
	}

	if(uFirst == uLast)
		return 1;

//******************** Erzeuge Eintragsliste **********************************
	uItem		= uFirst;
	pItemList	= uEnties;
	uMax		= 128;
	uPos		= 0;

	do	{													// Alle Kindeinträge suchen
		if(uPos >= uMax) {
			uMax *= 2;
			pItemNew	= new(unsigned, uMax);
			memcpy(pItemNew, pItemList, uPos * sizeof(pItemList[0]));
			if(uPos > 128)
				delete(pItemList);
			pItemList = pItemNew;
		}

		pItemList[uPos] = uItem;
		pNext			= pList[uItem];
		uItem			= pNext->uNextItem;
		uPos++;
	} while(uItem);

//************************* Qsort-Algorithmus *********************************
#define XCHANGE_MEM(a,b)	uTemp=pItemList[a];pItemList[a]=pItemList[b];pItemList[b]=uTemp;

	pData->cLockChanges = 1;
	UNLOCK(pData);

	pCompare	= pSortData->lpfnCompare;
	lParamSort	= pSortData->lParam;
	iLast		= uPos - 1;
	iStart		= 0;
	iLevel		= 0;

	for(;;) {
		iLower	=  iStart;
		iMiddle = (iStart + iLast) >> 1;						// Mitte bereichnen
		iUpper	=  iLast + 1;

		XCHANGE_MEM(iMiddle, iLower);

		uItem	   = pItemList[iStart];
		lParamTemp = pList[uItem]->lParam;

		for(;;) {
			do  {
				iLower++;
				if(iLower > iLast)
					break;
				uItem = pItemList[iLower];
				iCmp = pCompare(pList[uItem]->lParam, lParamTemp, lParamSort);
			} while(iCmp <= 0);

			do  {
				iUpper--;
				if(iUpper <= iStart)
					break;
				uItem = pItemList[iUpper];
				iCmp = pCompare(pList[uItem]->lParam, lParamTemp, lParamSort);
			} while(iCmp >= 0);

			if(iUpper < iLower)
				break;

			XCHANGE_MEM(iUpper, iLower);
		}

		XCHANGE_MEM(iStart, iUpper);

		if(iUpper - 1 - iStart >= iLast - iLower) {
			if(iStart + 1 < iUpper) {
				uMemL[iLevel] = iStart;
				uMemU[iLevel] = iUpper - 1;
				iLevel++;
			}
			if(iLower < iLast) {
				iStart = iLower;
				continue;
			}
		} else {
			if(iLower < iLast) {
				uMemL[iLevel] = iLower;
				uMemU[iLevel] = iLast;
				iLevel++;
			}

			if(iStart + 1 < iUpper) {
				iLast = iUpper - 1;
				continue;
			}
		}
		// Eine Ebene absteigen
		iLevel--;

		if(iLevel >= 0) {									// Noch Ebenen vorhanden
			iStart = uMemL[iLevel];
			iLast  = uMemU[iLevel];
			continue;
		}

		break;
	}

	LOCK(pData);
	pData->cLockChanges = 0;

//******************** Einträge neu einsortirenen *****************************
	uPos--;

	pEntry = pList[uParent];
	if(!pEntry) {
		pData->uFirstChild  = pItemList[ 0  ];
		pData->uLastChild   = pItemList[uPos];
	} else {
		pEntry->uFirstChild = pItemList[ 0  ];
		pEntry->uLastChild  = pItemList[uPos];
	}

	uLast  = 0;
	uItem  = pItemList[0];

	for(uNum = 0; uNum < uPos;) {								// Kinder neu einhängen
		pEntry = pList[uItem];
		pEntry->uPrevItem = uLast;

		uNum++;
		uLast  = uItem;
		uItem  = pItemList[uNum];

		pEntry->uNextItem = uItem;
	}

	pEntry = pList[uItem];
	pEntry->uPrevItem = uLast;
	pEntry->uNextItem = 0;

	if(iMode != SORT_NOUPDATE)								// Ausgabeliste neuerstellen
		if(uParent == 0 || (pParent->uShowPos && (pParent->uState & TVIS_EXPANDED))) {
			UpdateItems(pData, uParent);

			if(pData->uStyle & TVS_SHOWSELALWAYS)
				if(pData->uSelectedItem) {
					TreeListEnsureVisible(pData, pData->uSelectedItem, pData->uSelectedSub);
				}
		}

	if(uMax > 128)
		delete(pItemList);

	return 1;
}

//*****************************************************************************
//*
//*		TreeListSortItems
//*
//*****************************************************************************
//	Sortiert die Kindereinträge eines Eintrages via Text
//	pData		: Zeiger auf die Fensterdaten
//	pSortData	: Ist ein Zeiger auf die Sortiertaten
//	iMode		: 1=Rekursiv sortieren
//	Ergibt 1 wenn ok sonst 0
static int TreeListSortItems(TreeListData *pData, unsigned uParent, int iMode) {

	unsigned	uNum;
	unsigned	uItem;
	unsigned	uLast;
	unsigned	uFirst;
	BaseItem   *pParent;
	BaseItem   *pEntry;
	BaseItem   *pNext;
	BaseItem  **pList;

	unsigned   *pItemList;
	unsigned   *pItemNew;
	unsigned	uEnties[128];
	unsigned	uPos;
	unsigned	uMax;

	LPCTSTR			pTextTemp;
	LPCTSTR			pText;
	int				iLower, iUpper, iMiddle, iCmp;
	int				uMemL[30], uMemU[30];
	int				iStart, iLast;
	int				iLevel;
	int				iNone;
	unsigned		uSize;
	unsigned		uTemp;

	pList = pData->pTreeItems;

	if(uParent > pData->uTreeItemsMax) {					// Root-Eintrag sortieren
		if(uParent != U(TVI_ROOT))
			return 0;

		uLast  = pData->uLastChild;
		uFirst = pData->uFirstChild;
		if(uFirst == 0)
			return 1;
		if(uFirst == pData->uLastChild) {					// Einzelner Eintrag
			pNext = pList[uFirst];
			if(!pNext->uFirstChild)
				return 1;

			TreeListSortItems(pData, uFirst, iMode);
			return 1;
		}

		pList   = pData->pTreeItems;
		pParent = 0;
		uParent = 0;
	} else {													// Untereintrag sortieren
		pParent = pList[uParent];
		if(pParent == NULL)
			return 0;

		uLast  = pParent->uLastChild;
		uFirst = pParent->uFirstChild;
		if(uFirst == 0)
			return 1;
		if(uFirst == pParent->uLastChild) {					// Einzelner Eintrag
			pNext = pList[uFirst];
			if(!pNext->uFirstChild)
				return 1;

			TreeListSortItems(pData, uFirst, iMode);
			return 1;
		}
	}

	if(iMode) {											// Sortiere die Untereinträge
		uItem = uFirst;

		while(uItem) {
			pNext = pList[uItem];
			if(pNext->uFirstChild) {
				TreeListSortItems(pData, uItem, SORT_NOUPDATE);
			}

			uItem = pNext->uNextItem;
		}
	}

	if(uLast == uFirst)
		return 1;

//******************** Erzeuge Eintragsliste **********************************
	uItem		= uFirst;
	pItemList	= uEnties;
	uMax		= 128;
	uPos		= 0;

	do	{													// Alle Kindeinträge suchen
		if(uPos >= uMax) {
			uMax *= 2;
			pItemNew	= new(unsigned, uMax);
			memcpy(pItemNew, pItemList, uPos * sizeof(pItemList[0]));
			if(uPos > 128)
				delete(pItemList);
			pItemList = pItemNew;
		}

		pItemList[uPos] = uItem;
		pNext			= pList[uItem];
		uItem			= pNext->uNextItem;
		uPos++;
	} while(uItem);

//************************* Qsort-Algorithmus *********************************
#define XCHANGE_MEM(a,b)	uTemp=pItemList[a];pItemList[a]=pItemList[b];pItemList[b]=uTemp;

	pData->cLockChanges = 1;
	UNLOCK(pData);

	iLast		= uPos - 1;
	iStart		= 0;
	iLevel		= 0;

	for(;;) {
		iLower	=  iStart;
		iMiddle = (iStart + iLast) >> 1;						// Mitte bereichnen
		iUpper	=  iLast + 1;

		XCHANGE_MEM(iMiddle, iLower);

		uItem  = pItemList[iStart];
		pEntry = pList[uItem];
		if(pEntry->bCallback & TVIF_TEXT) {
			uSize = 0;
			LOCK(pData);
			CallbackEntry(pData, pEntry, uItem, TVIF_TEXT, &iNone, &uSize, &pTextTemp);
			UNLOCK(pData);
		} else {
			pTextTemp = pEntry->pText;
		}

		for(;;) {
			do  {
				iLower++;
				if(iLower > iLast)
					break;

				uItem  = pItemList[iLower];
				pEntry = pList[uItem];
				if(pEntry->bCallback & TVIF_TEXT) {
					uSize = 1;
					LOCK(pData);
					CallbackEntry(pData, pEntry, uItem, TVIF_TEXT, &iNone, &uSize, &pText);
					UNLOCK(pData);
				} else {
					pText = pEntry->pText;
				}

				iCmp = str_icmp(pText, pTextTemp);
			} while(iCmp <= 0);

			do  {
				iUpper--;

				if(iUpper <= iStart)
					break;
				uItem  = pItemList[iUpper];
				pEntry = pList[uItem];
				if(pEntry->bCallback & TVIF_TEXT) {
					LOCK(pData);
					CallbackEntry(pData, pEntry, uItem, TVIF_TEXT, &iNone, &uSize, &pText);
					UNLOCK(pData);
				} else {
					pText = pEntry->pText;
				}

				iCmp = str_icmp(pText, pTextTemp);
			} while(iCmp >= 0);

			if(iUpper < iLower)
				break;

			XCHANGE_MEM(iUpper, iLower);
		}

		XCHANGE_MEM(iStart, iUpper);

		if(iUpper - 1 - iStart >= iLast - iLower) {
			if(iStart + 1 < iUpper) {
				uMemL[iLevel] = iStart;
				uMemU[iLevel] = iUpper - 1;
				iLevel++;
			}
			if(iLower < iLast) {
				iStart = iLower;
				continue;
			}
		} else {
			if(iLower < iLast) {
				uMemL[iLevel] = iLower;
				uMemU[iLevel] = iLast;
				iLevel++;
			}

			if(iStart + 1 < iUpper) {
				iLast = iUpper - 1;
				continue;
			}
		}
		// Eine Ebene absteigen
		iLevel--;

		if(iLevel >= 0) {									// Noch Ebenen vorhanden
			iStart = uMemL[iLevel];
			iLast  = uMemU[iLevel];
			continue;
		}

		break;
	}

	LOCK(pData);
	pData->cLockChanges = 0;

//******************** Einträge neu einsortirenen *****************************
	uPos--;

	pEntry = pList[uParent];
	if(!pEntry) {
		pData->uFirstChild  = pItemList[ 0  ];
		pData->uLastChild   = pItemList[uPos];
	} else {
		pEntry->uFirstChild = pItemList[ 0  ];
		pEntry->uLastChild  = pItemList[uPos];
	}

	uLast  = 0;
	uItem  = pItemList[0];

	for(uNum = 0; uNum < uPos;) {								// Kinder neu einhängen
		pEntry = pList[uItem];
		pEntry->uPrevItem = uLast;

		uNum++;
		uLast  = uItem;
		uItem  = pItemList[uNum];

		pEntry->uNextItem = uItem;
	}

	pEntry = pList[uItem];
	pEntry->uPrevItem = uLast;
	pEntry->uNextItem = 0;

	if(iMode != SORT_NOUPDATE)								// Ausgabeliste neuerstellen
		if(uParent == 0 || (pParent->uShowPos && (pParent->uState & TVIS_EXPANDED))) {
			UpdateItems(pData, uParent);

			if(pData->uStyle & TVS_SHOWSELALWAYS)
				if(pData->uSelectedItem) {
					TreeListEnsureVisible(pData, pData->uSelectedItem, pData->uSelectedSub);
				}
		}

	if(uMax > 128)
		delete(pItemList);
	
	return 1;
}

//*****************************************************************************
//*
//*		TreeListEndLabelEdit
//*
//*****************************************************************************
//	Beendet das editiert eines Eintrages
//	pData	: Zeiger auf Fensterdaten
//	iMode	: Gibt an wie das Editieren beendet wurde
//				0 = Es gab es einen Abbruch mit ESC
//				1 = Eingabe ohne Enter
//				2 = Eingabe mit  Enter
//	Ergibt das Handle des Edit-Controlls oder NULL bei einem Fehler
static int TreeListEndLabelEdit(TreeListData *pData, int iMode) {
	TCHAR			cText[2052];
	NMTVDISPINFO	sNotify;
	TV_ITEM			sSet;
	LRESULT			lRet;
	unsigned		uSub;
	unsigned		uItem;
	ExtraItem	   *pExtra;
	BaseItem	   *pEntry;
	LPCTSTR		   *pList;
	LPCTSTR			pText;
	LPTSTR			pGetT;
	TCHAR			cChar;
	int 			iAuto;
	int				iIcon;
	int				iLine;
	int				iPos;
	int				iLen;
	int				iMax;
	int				iSel;
	char			cCb;

	uItem = pData->uEditItem;
	uSub  = pData->uEditSub;
	cCb	  =	pData->cEditCb;

	pData->uEditItem = 0;
	pData->uEditSub  = 0;
	pData->cEditCb	 = 0;

	if(uItem > pData->uTreeItemsMax || uSub >= pData->uColumnCount) {
		return 0;
	}

	pEntry = pData->pTreeItems[uItem];
	if(!pEntry) {
		return 0;
	}

	if(iMode) {											// Eingabe
		GetWindowText(pData->hEdit, cText, sizeof(cText) / sizeof(cText[0]));
		cText[sizeof(cText) / sizeof(cText[0]) - 1] = 0;
		sNotify.item.mask		= TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_TEXT | TVIF_SUBITEM;
		sNotify.item.cchTextMax = sizeof(cText) / sizeof(cText[0]) - 1;
		sNotify.item.pszText	= cText;

		if(pData->cColumnStart) {							// Wurde ein Text eingegeben
			sNotify.item.mask  |= TVIF_TEXTCHANGED;
		}

		if(iMode == 2) {									// Wurde der Text mit RETURN eingegeben
			sNotify.item.mask  |= TVIF_RETURNEXIT;
		}
	} else {													// Abbruch
		sNotify.item.mask		= TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_CANCELED | TVIF_SUBITEM;
		sNotify.item.pszText	= NULL;
		sNotify.item.cchTextMax = 0;
	}

	sNotify.hdr.code			= TVN_ENDLABELEDIT;
	sNotify.item.hItem			= (HTREEITEM)(ULONG_PTR)uItem;
	sNotify.item.stateMask		= 0xFFFFFFFF;
	sNotify.item.cChildren		= uSub;

	if(uSub) {											// Wurde der Text in einer Sub-Spalte geändert
		pExtra = pData->pExtraItems[uSub - 1][uItem];
		if(!pExtra) {
			sNotify.item.state	= 0;
			sNotify.item.lParam	= 0;
		} else {
			sNotify.item.state  = pEntry->uState & TVIS_BASEFLAGS;
			sNotify.item.state |= pExtra->uState;
			sNotify.item.lParam	= pEntry->lParam;
		}
	} else {
		sNotify.item.state	= pEntry->uState;
		sNotify.item.lParam	= pEntry->lParam;
	}

	UNLOCK(pData);
	ShowWindow(pData->hEdit, SW_HIDE);
	lRet = SendNotify(pData, &sNotify.hdr);
	LOCK(pData);

	if(lRet || !iMode)
		return 0;

	if(cCb) {												// Callback aufrufen
		sNotify.hdr.code		= TVN_SETDISPINFO;
		sNotify.item.mask		= TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_TEXT | TVIF_SUBITEM;
		sNotify.item.stateMask	= (UINT)~TVIS_BASEFLAGS;
		sNotify.item.hItem		= (HTREEITEM)(ULONG_PTR)uItem;
		sNotify.item.cChildren	= uSub;

		UNLOCK(pData);
		SendNotify(pData, &sNotify.hdr);
		LOCK(pData);
	} else {													// Neuen Text eingeben
		pGetT			= sNotify.item.pszText;
		sSet.mask		= TVIF_SUBITEM | TVIF_TEXT;
		sSet.cchTextMax	= sNotify.item.cchTextMax;
		sSet.hItem		= (HTREEITEM)(ULONG_PTR)uItem;
		sSet.pszText	= pGetT;
		sSet.cChildren	= uSub;

		iIcon			= pData->aColumn[uSub].iCbIcon;
		iAuto			= pData->aColumn[uSub].bEdit;

		if(iIcon >= 0 && iAuto != TVAX_NONE) {				// Auch ein Icon zuweisen
			iPos = -1;

			if((1 << iAuto) & ((1 << TVAX_CBLIST) | (1 << TVAX_COMBO))) {
				iPos = (int)SendMessage(pData->hEdit, CB_GETCURSEL, 0, 0);
				sSet.mask		   |= TVIF_IMAGE | TVIF_SELECTEDIMAGE;
				sSet.iImage			= iIcon + iPos;
				sSet.iSelectedImage	= sSet.iImage;
			}

			if(iPos <= -1) {
				iMax = pData->aColumn[uSub].bCbSize;
				if(iMax <= 0)
					iMax = 1024;

				iSel = -1;

				if(pData->aColumn[uSub].bFlags & TVAE_PTRLIST) { // Zeigerliste  char *pTexte[]={"1","2",NULL};
					pList = (LPCTSTR *)pData->aColumn[uSub].pCbData;

					for(iPos = 0; iPos < iMax; iPos++) {
						if(!pList[iPos])
							break;
						if(!str_cmp(pGetT, pList[iPos]))
							iSel = iPos;
					}
				} else {										// Textliste  char *pText="1|2|3";
					pText = (LPTSTR)pData->aColumn[uSub].pCbData;
					cChar = (TCHAR)pData->aColumn[uSub].bCbChar;

					for(iPos = 0; iPos < iMax; iPos++) {
						for(iLen = 0; pText[iLen]; iLen++) {
							if(pText[iLen] == cChar)
								break;
						}

						if(str_ncmp(pGetT, pText, iLen) == 0 && !pGetT[iLen]) {
							iSel = iPos;
							break;
						}

						pText += iLen;
						if(pText[0] == cChar)
							pText++;
						if(pText[0] == 0)
							break;
					}
				}

				sSet.mask		   |= TVIF_IMAGE | TVIF_SELECTEDIMAGE;
				sSet.iImage			= iIcon + iSel;
				sSet.iSelectedImage	= sSet.iImage;
			}
		}

		if(pData->aColumn[uSub].bFlags & TVAE_NEXTLINE &&			// In die nächste Zeile springen
		        iMode == 2 && iAuto != TVAX_NONE && iAuto != TVAX_STEP) {
			if(!pData->cColumnStart) {
				if(uSub) {									// Hat sich der Text verändert ?
					pExtra = pData->pExtraItems[uSub - 1][uItem];
					if(pExtra && pExtra->pText) {
						if(!_tcscmp(pGetT, pExtra->pText))
							return 1;
					} else {
						if(!*pGetT)
							return 1;
					}
				} else {
					pEntry = pData->pTreeItems[uItem];
					if(pEntry && pEntry->pText) {
						if(!_tcscmp(pGetT, pEntry->pText))
							return 1;
					} else {
						if(!*pGetT)
							return 1;
					}
				}
			}

			TreeListSetItem(pData, &sSet);

			iLine = pEntry->uShowPos;
			if(iLine < (int)pData->uItemPosCount) {
				uItem = pData->pItemPos[iLine];
				TreeListSelectItem(pData, uItem, pData->uSelectedSub, TVC_BYKEYBOARD | TVC_DESELECT);
			}
		} else {
			TreeListSetItem(pData, &sSet);
		}
	}
	
	return 1;
}

//*****************************************************************************
//*
//*		TreeListEditLabel
//*
//*****************************************************************************
//	Startet das editiert eines Eintrages
//	pData	: Zeiger auf Fensterdaten
//	uItem	: Item das editiert werden soll
//	uSub	: Spalte die editiert werden soll und andere Flags
//				TVIR_EDITCOL(n)		= Spalte angeben
//				TVIR_SELAREA(a,b)	= Einen Textbereich auswählen
//				TVIR_SETCURSOR(n)	= Den Cursor auf eine bestimmte Textstelle
//				TVIR_SETAT(n)		= Den Cursor auf eine bestimmte Pixelstelle
//				TVIR_SELALL			= Den gesammten Text wählen
//				TVIR_EDITCOMBOLIST	= Statt dem Edit-Fenster eine ComboBox nur mit Listenauswahl einblenden
//				TVIR_EDITFULL		= Das Edit-Fenster über die volle Breite einblenden
//				TVIR_EDITCOMBOBOX	= Statt dem Edit-Fenster eine ComboBox einblenden
//	Ergibt das Handle des Edit-Controlls oder NULL bei einem Fehler
static HWND TreeListEditLabel(TreeListData *pData, unsigned uItem, unsigned uSub) {

	HDC				hDc;
	HWND			hWnd;
	LRESULT			lRet;
	WNDPROC			pProc;
	char			cTemp;
	LPARAM			lParam;
	ExtraItem	   *pExtra;
	BaseItem	   *pEntry;
	NMTVDISPINFO	sNotify;
	LPCTSTR			pText;
	RECT			sRect;
	SIZE			sSize;
	HFONT			hFont;
	unsigned		uCol;
	unsigned		uSel;
	unsigned		uNext;
	unsigned		uBits;
	unsigned		uSize;
	unsigned		uStart;
	unsigned		uState;
	unsigned		uFlags;
	unsigned		uHeight;
	int				iPixels;
	int				iWidth;
	int				iTemp;

	uBits	=  uSub >> 29;
	uSel    = (uSub >> 8) & 0x0FFFFF;
	uSub   &=  0xFF;

	if(uSub >= pData->uColumnCount)
		return NULL;
	if(uItem > pData->uTreeItemsMax)
		return NULL;
	pEntry = pData->pTreeItems[uItem];
	if(!pEntry)
		return NULL;

	if(uItem != pData->uSelectedItem || uSub != pData->uSelectedSub) {
		uCol  = (pData->uStyleEx & TVS_EX_SUBSELECT) ? uSub : 0;
		iTemp = TreeListSelectItem(pData, uItem, uCol, TVC_UNKNOWN);
	}

	TreeListEnsureVisible(pData, uItem, uSub);

	if(pData->hEdit) {									// Editfenster löschen
		DestroyWindow(pData->hEdit);
		pData->hEdit = 0;
	}

	switch(uBits & 3) {										// Editfenster neu erzeugen
		case 1:
			pData->hEdit = CreateWindow(_T("COMBOBOX"), NULL, WS_BORDER | WS_CHILD | CBS_AUTOHSCROLL | CBS_DROPDOWN | WS_VSCROLL, 0, 0, 0, 0, pData->hWnd, (HMENU)3, NULL, NULL);
			pData->uEditMode = 1;
			SendMessage(pData->hEdit, CB_LIMITTEXT, 2048, 0);
			break;

		case 2:
			pData->hEdit = CreateWindow(_T("COMBOBOX"), NULL, WS_BORDER | WS_CHILD | CBS_AUTOHSCROLL | CBS_DROPDOWNLIST | WS_VSCROLL, 0, 0, 0, 0, pData->hWnd, (HMENU)3, NULL, NULL);
			pData->uEditMode = 2;
			SendMessage(pData->hEdit, CB_LIMITTEXT, 2048, 0);
			break;

		default
				:
			uFlags = ES_LEFT;

			if(uBits & 4) {
				if(pData->aColumn[uSub].bAlign == DT_RIGHT)
					uFlags = ES_RIGHT;
				if(pData->aColumn[uSub].bAlign == DT_CENTER)
					uFlags = ES_CENTER;
			}

			pData->hEdit = CreateWindow(_T("EDIT"), NULL, WS_BORDER | WS_CHILD | ES_AUTOHSCROLL | uFlags, 0, 0, 0, 0, pData->hWnd, (HMENU)3, NULL, NULL);
			pData->uEditMode = 0;
			SendMessage(pData->hEdit, EM_SETLIMITTEXT, 2048, 0);
			break;
	}
	
	if(!pData->hEdit)
		return NULL;

	if(pSetWindowTheme) {									// Remove the Visual-Styles (XP+)
		pSetWindowTheme(pData->hEdit, L"", L"");
	}

	pData->pProcId3 = (WNDPROC)GetWindowLongPtr(pData->hEdit, GWLP_WNDPROC);
	SetWindowLongPtr(pData->hEdit, GWLP_USERDATA, (LPARAM)pData);
	SetWindowLongPtr(pData->hEdit, GWLP_WNDPROC , (LPARAM)EditProc);

	hWnd = GetWindow(pData->hEdit, GW_CHILD);

	while(hWnd) {
		pProc = (WNDPROC)GetWindowLongPtr(hWnd, GWLP_WNDPROC);
		SetWindowLongPtr(hWnd, GWLP_USERDATA, (LPARAM)pProc);
		SetWindowLongPtr(hWnd, GWLP_WNDPROC , (LPARAM)EditProc);
		hWnd  = GetNextWindow(hWnd, GW_HWNDNEXT);
	}

	pData->cEditCb = 0;

	if(pData->uEditMode >= 1) {								// ComboBox leeren
		SendMessage(pData->hEdit, CB_RESETCONTENT, 0, 0);
	}

	if(uSub == 0) {											// Haupteintrag bearbeiten
		TreeListGetItemRect(pData, uItem, TVIR_GETCOLUMN | TVIR_TEXT, &sRect);

		pText	= pEntry->pText;
		uSize	= pEntry->uTextSize;
		iPixels	= pEntry->iTextPixels + 10;
		lParam	= pEntry->lParam;
		uState	= pEntry->uState;
		hFont	= (uState & TVIS_BOLD) ? pData->hFontB : pData->hFontN;

		if(pEntry->bCallback & TVIF_TEXT) {
			CallbackEntry(pData, pEntry, uItem, TVIF_TEXT, &iTemp, &uSize, &pText);
			hDc = GetDC(pData->hWnd);
			SelectObject(hDc, hFont);
			DrawText(hDc, pText, uSize, &sRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX | DT_CALCRECT);
			ReleaseDC(pData->hWnd, hDc);
			iPixels  = sRect.right - sRect.left + 10;
			pData->cEditCb = 1;
		}

		if(uBits & 4) {										// Volle Spaltenbreite
			if(pEntry->iImage != TV_NOIMAGE) {
				uNext		 = pData->aColumnPos[1];
				sRect.right  = pData->aColumnXpos[uNext];
				sRect.right -= pData->uScrollX;
			}

			iPixels	= sRect.right - sRect.left - 2;
		} else {
			if(pData->uEditMode) {
				if(iPixels < 60)
					iPixels = 60;
			} else {
				if(iPixels < 48)
					iPixels = 48;
			}

			if(pText && *pText) {
				iPixels += str_len(pText);
			}

			if(pData->uEditMode) {
				iPixels += GetSystemMetrics(SM_CXHSCROLL);
			}
		}
	} else {													// Extraeintrag bearbeiten
		if(uBits & 4) {
			TreeListGetItemRect(pData, uItem, TVIR_GETCOLUMN | TVIR_COLTOSUB(uSub), &sRect);

			if(pData->aColumn[uSub].bEdit >= TVAX_CHECK) {
				sRect.left += pData->iChecksXsize;
				if(sRect.right < sRect.left)
					sRect.right = sRect.left + 1;
			}
		} else {
			TreeListGetItemRect(pData, uItem, TVIR_GETCOLUMN | TVIR_TEXT | TVIR_COLTOSUB(uSub), &sRect);
		}

		pExtra = pData->pExtraItems[uSub - 1][uItem];
		if(!pExtra) {
			pData->cTempText1[0] = 0;
			pText	= pData->cTempText1;
			uSize	= sizeof(pData->cTempText1) / sizeof(pData->cTempText1[0]);
			iPixels	= sRect.right - sRect.left + 10;
			hFont	= pData->hFontN;
			uState  = pEntry->uState & TVIS_BASEFLAGS;
			lParam	= 0;
		} else {
			pText	= pExtra->pText;
			uSize	= pExtra->uTextSize;
			iPixels	= pExtra->iTextPixels + 10;
			lParam	= pEntry->lParam;
			uState	= pExtra->uState;
			uState |= pEntry->uState & TVIS_BASEFLAGS;
			hFont	= (uState & TVIS_BOLD) ? pData->hFontB : pData->hFontN;

			if(pExtra->bCallback & TVIF_TEXT) {
				CallbackExtra(pData, pEntry, pExtra, uItem, uSub, TVIF_TEXT, &iTemp, &uSize, &pText);
				hDc = GetDC(pData->hWnd);
				SelectObject(hDc, hFont);
				DrawText(hDc, pText, uSize, &sRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX | DT_CALCRECT);
				ReleaseDC(pData->hWnd, hDc);
				iPixels = sRect.right - sRect.left;
				pData->cEditCb = 1;
			}
		}

		if(uBits & 4) {										// Volle Spaltenbreite
			if(pExtra && pExtra->iImage != TV_NOIMAGE) {
				sRect.left += pData->iImagesXsize + 1;
			}

			iPixels	= sRect.right - sRect.left - 2;
		} else {
			if(pData->uEditMode) {
				if(iPixels < 60)
					iPixels = 60;
			} else {
				if(iPixels < 48)
					iPixels = 48;
			}

			if(pText && *pText) {
				iPixels += str_len(pText);
			}

			if(pData->uEditMode) {
				iPixels += GetSystemMetrics(SM_CXHSCROLL);
			}

			switch(pData->aColumn[uSub].bAlign) {
				case DT_RIGHT:
					iWidth		= sRect.right - sRect.left;
					sRect.left += iWidth - iPixels;
					break;

				case DT_CENTER:
					iWidth		= sRect.right - sRect.left;
					sRect.left += (iWidth - iPixels) / 2;
					break;
			}
		}
	}

	UNLOCK(pData);

	sNotify.hdr.code			= TVN_BEGINLABELEDIT;
	sNotify.item.mask			= TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_TEXT | TVIF_SUBITEM;
	sNotify.item.hItem			= (HTREEITEM)(ULONG_PTR)uItem;
	sNotify.item.pszText		= (LPTSTR)pText;
	sNotify.item.lParam			= lParam;
	sNotify.item.state			= uState;
	sNotify.item.cchTextMax		= uSize;
	sNotify.item.stateMask		= 0xFFFFFFFF;
	sNotify.item.cChildren		= uSub;

	lRet = SendNotify(pData, &sNotify.hdr);

	LOCK(pData);

	if(lRet) {											// Das Editieren abbrechen
		TreeListEndLabelEdit(pData, 0);
		return NULL;
	}

	pEntry = pData->pTreeItems[uItem];
	if(!pEntry)
		return NULL;

	if(pData->uToolTipItem) {								// Ein offenes Tooltip verstecken
		UpdateToolTip(pData, 0, 0);
	}
	
	UNLOCK(pData);
	SetFocus(pData->hEdit);
	LOCK(pData);

	if(pData->uEditMode) {
		sRect.top--;
		uHeight	= 260;
	} else {
		uHeight = pData->iFontHeight + 4;
	}

	if(pData->uEditMode) {
		if(iPixels < 60)
			iPixels = 60;
	} else {
		if(iPixels < 48)
			iPixels = 48;
	}

	cTemp = pData->cColumnStart;
	SetWindowPos(pData->hEdit, HWND_TOP, sRect.left + 2, sRect.top + 1, iPixels, uHeight, SWP_SHOWWINDOW);
	SendMessage(pData->hEdit, WM_SETFONT, (WPARAM)hFont, 0);
	SetWindowText(pData->hEdit, pText);
	pData->cColumnStart = cTemp;

	switch(uSel >> 18) {									// Welche Textauswahl
		case 3:
			uStart = (uSel) & 0x01FF;						// Einen Textbereich markieren
			uSize  = (uSel >> 9) & 0x01FF;
			break;

		case 2:
			uStart = uSel & 0x3FFFF;							// Cursor auf eine bestimmte Stelle
			uSize  = uStart;
			break;

		case 1:
			uSel  &= 0x3FFFF;
			if(uSel > 4)
				uSel -= 4;
			hDc = GetDC(pData->hEdit);
			SelectObject(hDc, hFont);
			GetTextExtentExPoint(hDc, pText, uSize, uSel, &iWidth, NULL, &sSize);

			if(uSize > 0 && iWidth < (int)uSize - 1) {			// Halben Buchstaben addieren
				GetTextExtentExPoint(hDc, pText + iWidth, 1, uSel, NULL, NULL, &sSize);
				uSel += sSize.cx / 2;
				GetTextExtentExPoint(hDc, pText, uSize, uSel, &iWidth, NULL, &sSize);
			}

			ReleaseDC(pData->hEdit, hDc);

			if(sSize.cx <= (int)uSel) {
				uStart = 0;
				break;
			}

			uStart	= iWidth;
			uSize	= iWidth;
			break;

		default
				:
			uStart = 0;										// Alles markieren
			break;
	}

	switch(pData->uEditMode) {
		case 0:
			SendMessage(pData->hEdit, EM_SETSEL, uStart, uSize);
			break;
		case 1:
			SendMessage(pData->hEdit, CB_SETEDITSEL, 0, MAKELPARAM(uStart, uSize));
			break;
		default
				:
			uStart = 0;
			uSize = 0xFFFF;
			break;
	}

	RedrawWindow(pData->hEdit, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE);

	pData->uLastSel  = MAKELPARAM(uStart, uSize);
	pData->uEditItem = uItem;
	pData->uEditSub	 = uSub;
	
	return pData->hEdit;
}


//*****************************************************************************
//*
//*		TreeListStartAutoEdit
//*
//*****************************************************************************
//	Startet die Editierung für einen Wert via Notyfy-Rückfrage.
//	pData		: Zeiger auf die Fensterdaten
//	uItem		: Ist der Eintrag der Editiert werden soll
//	uSub		: Ist die Nummer der Spalte
//	wParam		: Ist der W-Parameter des Tastendrucks
//					VK_EDITCLK	bei einem Clickauf das ausgewählte Feld
//					VK_DBLCLK	bei einem Doppelclick
//					VK_RETURN	bei einen Enter-Druck
//					VK_ISACHAR	bei WM_CHAR Nachrichten
//					<char>		bei einer Zeicheneingabe
//	lParam		: Ist der L-Parameter des Tastendrucks (bzw. die Koordinaten)
//	Ergibt 1 das Editieren gestartet wurde, ansonsten 0
//				0 wenn der Eintrag nicht gewählt wurde
static int TreeListStartNotifyEdit(TreeListData *pData, unsigned uItem, unsigned uSub, WPARAM wParam, LPARAM lParam) {

	TCHAR			cText[1024];
	TV_STARTEDIT	sNotify;
	ExtraItem      *pExtra;
	BaseItem       *pEntry;
	unsigned		uBits;
	unsigned		uCnt;
	unsigned		uMax;
	unsigned		uLen;
	unsigned		uNum;
	LPCTSTR			pText;
	RECT			sRect;
	TCHAR			cChar;
	LRESULT			lRet;
	MSG				sMsg;
	HWND			hWnd;
	INT				iSel;

	if(!(pData->uStyle & TVS_EDITLABELS))
		return 0;
	if(uItem == 0 || uItem > pData->uTreeItemsMax)
		return 0;
	if(uSub  > 0 && uSub >= pData->uColumnCount)
		return 0;

	pEntry  =  pData->pTreeItems[uItem];
	if(!pEntry)
		return 0;

	if(wParam < ' ' && wParam != VK_RETURN) {					// Falsche Taste
		return 0;
	}

	if(wParam >= ' ' && wParam <= 0xFFFF) {					// Shift und Cltr prüfen
		if(GetKeyState(VK_MENU) & 0x8000) {
			return 0;
		}

		if(GetKeyState(VK_CONTROL) & 0x8000) {
			return 0;
		}
	}

	sNotify.hdr.code			= TVN_STARTEDIT;
	sNotify.uAction				= (UINT)wParam;
	sNotify.item.mask			= TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_SUBITEM;
	sNotify.item.hItem			= (HTREEITEM)(ULONG_PTR)uItem;
	sNotify.item.stateMask		= 0xFFFFFFFF;
	sNotify.item.lParam			= pEntry->lParam;
	sNotify.item.cChildren		= uSub;
	sNotify.uHeight				= 0;
	sNotify.pTextEntries		= 0;
	sNotify.pTextList			= 0;
	sNotify.uMaxEntries			= 255;
	sNotify.ptAction.x			= LOWORD(lParam);
	sNotify.ptAction.y			= HIWORD(lParam);

	if(uSub) {											// Spalte verwenden
		pExtra  =  pData->pExtraItems[uSub - 1][pData->uSelectedItem];

		if(pExtra) {
			sNotify.item.state		= pExtra->uState&~TVIS_BASEFLAGS;
			sNotify.item.pszText	= pExtra->pText;
			sNotify.item.cchTextMax	= pExtra->uTextSize;
		} else {
			sNotify.item.state		= 0;
			sNotify.item.cchTextMax	= 0;
			sNotify.item.pszText	= _T("");
		}

		sNotify.item.state		   |= pEntry->uState & TVIS_BASEFLAGS;
	} else {													// Haupteintrag
		sNotify.item.state			= pEntry->uState;
		sNotify.item.pszText		= pEntry->pText;
		sNotify.item.cchTextMax		= pEntry->uTextSize;
	}

	UNLOCK(pData);
	lRet = SendNotify(pData, &sNotify.hdr);
	LOCK(pData);

	if(!lRet)
		return 0;

	if(wParam == VK_RETURN) {
		wParam = VK_DBLCLK;
		uBits  = 0;
	} else
		if(wParam == VK_EDITCLK) {
			if(pData->uStyleEx & TVS_EX_NOCURSORSET) {
				uBits = 0;
			} else {
				TreeListGetItemRect(pData, uItem, TVIR_COLTOSUB(uSub) | TVIR_GETCOLUMN | TVIR_TEXT, &sRect);
				uBits = TVIR_SETAT(LOWORD(lParam) - sRect.left);
			}
		} else {
			uBits = 0;
		}

	cChar  = (TCHAR)((lRet >> 8) & 0xFF);
	uBits |= U(lRet)&TVIR_EDITFULL;

	if(U(lRet)&TVIR_EDITCOMBOBOX) {						// Eine Combobox anzeigen
		uBits |= (U(lRet)&TVIR_EDITCOMBOLIST) ? 0x40000000 : 0x20000000;
	}

	hWnd = TreeListEditLabel(pData, uItem, uSub | uBits);
	if(!hWnd)
		return 0;

	if(lRet & TVIR_EDITCOMBOBOX) {							// Die Combobox füllen
		uMax = sNotify.uMaxEntries;
		iSel = -1;

		if(sNotify.pTextList) {							// Texte über Listenfeld
			for(uCnt = 0; uCnt < uMax; uCnt++) {
				pText = sNotify.pTextList[uCnt];
				if(!pText)
					break;

				SendMessage(hWnd, CB_ADDSTRING, 1, (LPARAM)pText);

				if(sNotify.item.pszText && !_tcscmp(pText, sNotify.item.pszText)) {
					iSel = uCnt;
				}
			}
		} else {												// Text mit
			pText = sNotify.pTextEntries;

			if(!pText)
				pText = _T("\0");

			for(uCnt = 0; uCnt < uMax; uCnt++) {
				for(uLen = 0; pText[uLen]; uLen++) {			// Subtextlänge holen
					if(pText[uLen] == cChar)
						break;
				}

				if(!uLen && !pText[uLen])
					break;

				uNum = uLen;
				if(uNum >= sizeof(cText) / sizeof(TCHAR)) {
					uNum = sizeof(cText) / sizeof(TCHAR) - 1;
				}

				memcpy(cText, pText, uNum * sizeof(TCHAR));
				cText[uNum] = 0;
				SendMessage(hWnd, CB_ADDSTRING, 1, (LPARAM)cText);

				if(sNotify.item.pszText && wParam >= 0x10000 && !_tcscmp(cText, sNotify.item.pszText)) {
					iSel = uCnt;
				}

				pText += uLen;
				if(!cChar || *pText)
					pText++;
			}

			if(lRet & TVIR_EDITCOMBODEL) {					// Den Puffer löschen
				delete((TCHAR*)sNotify.pTextEntries);
			}
		}

		if(iSel >= 0) {										// Listeneintrag auswählen
			SendMessage(hWnd, CB_SETCURSEL, iSel, 0);
		}

		if(sNotify.uHeight) {								// Höhe der Dropdown-Liste einstellen
			SendMessage(hWnd, CB_SETDROPPEDWIDTH, sNotify.uHeight, 0);
		}

		if(lRet & TVIR_EDITCOMBODOWN) {						// Dropdown-Liste einblenden
			SendMessage(hWnd, CB_SHOWDROPDOWN, 1, 0);
		}

		if(!(lRet & TVIR_EDITCOMBOLIST)) {					// Textauswahl wiederherstellen
			SetWindowText(hWnd, sNotify.item.pszText);
			SendMessage(hWnd, CB_SETEDITSEL, 0, pData->uLastSel);
		}
	}

	if(wParam < 0x10000) {									// Taste an Fenster senden
		pData->cColumnStart = 1;
		sMsg.hwnd			= hWnd;
		sMsg.lParam			= lParam;
		sMsg.wParam			= wParam;
		sMsg.message		= WM_KEYDOWN;

		TranslateMessage(&sMsg);
	} else
		if(wParam & VK_ISACHAR) {
			SendMessage(hWnd, WM_CHAR, wParam & 0xFFFF, lParam);
		} else {
			pData->cColumnStart = 0;
		}
		
	return 1;
}

//*****************************************************************************
//*
//*		TreeListStartAutoEdit
//*
//*****************************************************************************
//	Startet die Autoeditierung für eine Spalte
//	pData		: Zeiger auf die Fensterdaten
//	uColumn		: Ist die Nummer der Spalte
//	wParam		: Ist der W-Parameter des Tastendrucks
//					VK_EDITCLK	bei einem Clickauf das ausgewählte Feld
//					VK_ICONCLK	bei einem Clickauf das Iion
//					VK_DBLCLK	bei einem Doppelclick
//	lParam		: Ist der L-Parameter des Tastendrucks (bzw. die Koordinaten)
//	Ergibt 1 das Editieren gestartet wurde, ansonsten 0
//				0 wenn der Eintrag nicht gewählt wurde
static int TreeListStartAutoEdit(TreeListData *pData, unsigned uColumn, WPARAM wParam, LPARAM lParam) {
	
	TCHAR		cBuffer[256];
	NMTREEVIEW	sNotify;
	ExtraItem  *pExtra;
	BaseItem   *pEntry;
	LPTSTR	   *pList;
	LPTSTR	    pText;
	TV_ITEM		sItem;
	RECT		sRect;
	TCHAR		cChar;
	unsigned	uMode;
	unsigned	uBits;
	unsigned	uFlag;
	unsigned	uMax;
	unsigned	uPos;
	unsigned	uLen;
	unsigned	uSub;
	HWND		hWnd;
	int			iSel;
	int			iRet;
	int			iIcon;

	if(pData->uEditItem)
		return 0;
	if(pData->uColumnCount <= uColumn)
		return 0;
	if(!(pData->uStyle & TVS_EDITLABELS))
		return 0;

	uBits = pData->aColumn[uColumn].bFlags;
	uMode = pData->aColumn[uColumn].bEdit;

	if(uMode == 0)
		return 0;

	if(uBits & TVAE_STATEENABLE) {								// Kann das Editieren gesperrt werden
		if(uColumn) {
			pExtra  =  pData->pExtraItems[uColumn - 1][pData->uSelectedItem];
			if(pExtra && (pExtra->uState & TVIS_DISABLEBIT))
				return 0;
		} else {
			pEntry  =  pData->pTreeItems[pData->uSelectedItem];
			if(!pEntry || (pEntry->uState & TVIS_DISABLEBIT))
				return 0;
		}
	}

	if(wParam != VK_RETURN && wParam < 0x10000) {					// Zeicheneingabe
		if(!TVIS_EDIT(uMode))
			return 0;
		if(uBits & TVAE_ONLYRETURN)
			return 0;
		if(wParam <= ' ')
			return 0;
		pData->cColumnStart = 1;
	} else {
		pData->cColumnStart = 0;
	}
	
	uMax = pData->aColumn[uColumn].bCbSize;
	if(!uMax)
		uMax = 1024;

//******************** Weiterschalten mit Return ******************************
	if(uMode >= TVAX_STEP) {
		sItem.mask		 = TVIF_TEXT | TVIF_SUBITEM | TVIF_TEXTPTR | ((uMode < TVAX_CHECK) ? 0 : TVIF_STATE | TVIF_PARAM);
		sItem.stateMask	 = 0xFFFFFFFF;
		sItem.hItem		 = (HTREEITEM)(ULONG_PTR)pData->uSelectedItem;
		sItem.cChildren	 = uColumn;

		if(!TreeListGetItem(pData, &sItem))
			return 0;

		if((uMode & 1) && wParam != VK_ICONCLK) {					// Ein Edit-Feld öffnen
			uMode = TVAX_EDIT;
			goto EditField;
		}

		if(uMode >= TVAX_CHECK) {								// State-Bits bei Checkboxen setzen
			if(uBits & TVAE_STATEENABLE)
				uFlag = sItem.state & (~TVIS_STATEIMAGEMASK | TVIS_DISABLEBIT);
			else
				uFlag = sItem.state & (~TVIS_STATEIMAGEMASK);

			if(pData->uStyleEx & TVS_EX_BITCHECKBOX)
				sItem.state	= (sItem.state ^ 0x1000);
			else
				sItem.state	= (sItem.state & 0x1000) ?	0x2000 : 0x1000;

			sItem.state |= uFlag;
		} else {
			sItem.mask  &= ~TVIF_STATE;
		}

		if(uBits & TVAE_PTRLIST) {								// Zeigerliste  char *pTexte[]={"1","2",NULL};
			pList = (LPTSTR *)pData->aColumn[uColumn].pCbData;

			if(!pList) {
				if(uMode < TVAX_CHECK)
					return 0;
				sItem.mask &= ~TVIF_TEXT;
				pText       =  sItem.pszText;
				uPos		=  0;
				goto NoTextChange;
			}

			for(uPos = 0;; uPos++) {
				if(uPos >= uMax || !pList[uPos]){
					uPos = 0; break;
				}
				if(str_cmp(sItem.pszText, pList[uPos]))
					continue;
				uPos++;
				if(uPos >= uMax || !pList[uPos])
					uPos = 0;
				break;
			}

			pText = pList[uPos];
			if(!pText) {
				if(uMode < TVAX_CHECK)
					return 0;
				sItem.mask &= ~TVIF_TEXT;
				pText       =  sItem.pszText;
			}
		} else {													// Textliste  char *pText="1|2|3";
			pText = (LPTSTR)pData->aColumn[uColumn].pCbData;
			cChar = (TCHAR)pData->aColumn[uColumn].bCbChar;
			sItem.cchTextMax--;

			if(!pText) {
				if(uMode < TVAX_CHECK)
					return 0;
				sItem.mask &= ~TVIF_TEXT;
				pText       =  sItem.pszText;
				uPos		=  0;
				goto NoTextChange;
			}

			for(uPos = 0;; uPos++) {
				if(uPos >= uMax) {
					pText = (LPTSTR)pData->aColumn[uColumn].pCbData;
					uPos  = 0;
					break;
				}

				for(uLen = 0; pText[uLen]; uLen++) {				// Subtextlänge bestimmen
					if(pText[uLen] == cChar)
						break;
				}

				if(sItem.cchTextMax == (int)uLen)					// Ist der Subtext der aktulle Text
					if(str_ncmp(sItem.pszText, pText, uLen) == 0) {
						uPos++;

						if(uPos >= uMax) {
							pText = (LPTSTR)pData->aColumn[uColumn].pCbData;
							uPos  = 0;
							break;
						}

						pText += uLen;
						if(pText[0] == cChar)
							pText++;
						if(pText[0] == 0) {
							pText = (LPTSTR)pData->aColumn[uColumn].pCbData;
							uPos  = 0;
						}

						break;
					}

				pText += uLen;
				if(pText[0] == cChar)
					pText++;
				if(pText[0] == 0) {
					pText = (LPTSTR)pData->aColumn[uColumn].pCbData;
					uPos  = 0;
					break;
				}

			}

			for(uLen = 0; pText[uLen] && uLen < 256; uLen++) {		// Ausgewählten Text kopiren
				if(pText[uLen] == cChar)
					break;
				cBuffer[uLen] = pText[uLen];
			}

			cBuffer[uLen] = 0;
			pText = cBuffer;
		}

NoTextChange:

		sItem.mask		&= ~TVIF_TEXTPTR;
		sItem.stateMask	&=  TVIS_STATEIMAGEMASK;
		sItem.hItem		 = (HTREEITEM)(ULONG_PTR)pData->uSelectedItem;
		sItem.pszText	 =  pText;
		sItem.cchTextMax =  256;
		sItem.cChildren	 =  uColumn;
		iIcon			 =  pData->aColumn[uColumn].iCbIcon;

		if(iIcon >= 0) {										// Auch ein Icon zuweisen
			sItem.mask	|= TVIF_IMAGE | TVIF_SELECTEDIMAGE;
			sItem.iImage			= iIcon + uPos;
			sItem.iSelectedImage	= sItem.iImage;
		}

		iRet = TreeListSetItem(pData, &sItem);

		sNotify.hdr.code			= (uMode < TVAX_CHECK) ? TVN_STEPSTATECHANGED : TVN_CBSTATECHANGED;
		sNotify.action				= (UINT)wParam;
		sNotify.itemNew.mask		= TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_TEXT | TVIF_SUBITEM;
		sNotify.itemNew.stateMask	= 0xFFFFFFFF;
		sNotify.itemNew.hItem		= sItem.hItem;
		sNotify.itemNew.state		= sItem.state;
		sNotify.itemNew.lParam		= sItem.lParam;
		sNotify.itemNew.pszText		= sItem.pszText;
		sNotify.itemNew.cchTextMax	= sItem.cchTextMax;
		sNotify.itemNew.cChildren	= uColumn;
		sNotify.itemOld.mask		= 0;
		sNotify.ptDrag.x			= LOWORD(lParam);
		sNotify.ptDrag.y			= HIWORD(lParam);

		UNLOCK(pData);
		SendNotify(pData, &sNotify.hdr);
		LOCK(pData);

		return iRet;
	}

//******************** Edit oder Combobox *************************************
EditField:

	uSub	 = (uMode - 1) << 29;
	uSub    |=  uColumn;

	if(uBits & TVAE_FULLWIDTH) {
		uSub |= TVIR_EDITFULL;
	}

	if(wParam == VK_EDITCLK)										// Cursor auf Klickposition
		if(!(pData->uStyleEx & TVS_EX_NOCURSORSET)) {
			TreeListGetItemRect(pData, pData->uSelectedItem, TVIR_COLTOSUB(uColumn) | TVIR_GETCOLUMN | TVIR_TEXT, &sRect);
			uSub |= TVIR_SETAT(LOWORD(lParam) - sRect.left);
		}

	hWnd = TreeListEditLabel(pData, pData->uSelectedItem, uSub);
	if(!hWnd)
		return 0;

	if(uMode != TVAX_EDIT) {
		iSel			 = (uMode != TVAX_CBLIST) ? -1 : 0;
		sItem.mask		 = TVIF_TEXT | TVIF_SUBITEM | TVIF_TEXTPTR;
		sItem.hItem		 = (HTREEITEM)(ULONG_PTR)pData->uSelectedItem;
		sItem.cChildren	 = uColumn;

		if(!TreeListGetItem(pData, &sItem))
			return 0;

		if(uBits & TVAE_PTRLIST) {								// Zeigerliste  char *pTexte[]={"1","2",NULL};
			pList = (LPTSTR *)pData->aColumn[uColumn].pCbData;

			for(uPos = 0; uPos < uMax; uPos++) {
				if(!pList[uPos])
					break;
				if(!str_cmp(sItem.pszText, pList[uPos]))
					iSel = uPos;
				SendMessage(hWnd, CB_ADDSTRING, 0, (LPARAM)pList[uPos]);
			}
		} else {													// Textliste  char *pText="1|2|3";
			pText = (LPTSTR)pData->aColumn[uColumn].pCbData;
			cChar = (TCHAR)pData->aColumn[uColumn].bCbChar;
			sItem.cchTextMax--;

			for(uPos = 0; uPos < uMax; uPos++) {
				for(uLen = 0; pText[uLen]; uLen++) {
					if(pText[uLen] == cChar)
						break;
				}

				if(sItem.cchTextMax == (int)uLen)
					if(str_ncmp(sItem.pszText, pText, uLen) == 0) {
						iSel = uPos;
					}

				if(cChar) {
					if(uLen < 256){
						memcpy(cBuffer, pText, sizeof(TCHAR)*uLen); 
						cBuffer[uLen] = 0;
					}
					else {
						memcpy(cBuffer, pText, sizeof(TCHAR) * 255); 
						cBuffer[255 ] = 0;
					}
					SendMessage(hWnd, CB_ADDSTRING, 0, (LPARAM)cBuffer);
				} else {
					SendMessage(hWnd, CB_ADDSTRING, 0, (LPARAM)pText);
				}

				pText += uLen;
				if(pText[0] == cChar)
					pText++;
				if(pText[0] == 0)
					break;
			}
		}

		if(iSel >= 0) {
			SendMessage(hWnd, CB_SETCURSEL, iSel, 0);
		}
	}
	
	if((uBits & TVAE_DROPDOWN) && (uMode&~1) == 2) {				// Dropdownliste aufklappen
		SendMessage(hWnd, CB_SHOWDROPDOWN, 1, 0);
	}

	// Ersten Buchstaben ans Fenster übergeben
	if(TVIS_EDIT(uMode) && pData->cColumnStart && wParam != VK_EDITCLK) {
		SendMessage(hWnd, WM_CHAR, wParam, 0);
	}
	
	return 1;
}

//*****************************************************************************
//*
//*		TreeListProc
//*
//*****************************************************************************
//	Ist die Fensterfunktion für das TreeList Fenster
static LRESULT CALLBACK TreeListProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
	
	TreeListData   *pData;
	MSG			   *pMsg;
	LPARAM			lRet;
	TV_HITTESTINFO	sInfo;
	POINT			sPoint;
	SCROLLINFO		sScroll;
	unsigned		uChange;
	unsigned		uDelta;
	unsigned		uTime;
	unsigned		uFlag;
	unsigned		uMode;
	unsigned		uVal;
	unsigned		uOld;
	int 			iDif;
	int				iPos;
	int				iMax;
	HDC				hDc;
	
	switch(uMsg) {
		case WM_CREATE:												// Das Fenster erzeugen

			pData = new(TreeListData, 1);
			if(!pData)
				return -1;

			memset(pData, 0, sizeof(TreeListData));
			pData->pToolTipText = new(TCHAR, 256);
			pData->uToolTipSize = 256;

			if(!pData->pToolTipText) {
				delete(pData);
				return -1;
			}

			pData->pTreeItems = new(BaseItem*, 1);
			if(!pData->pTreeItems) {
				delete(pData->pToolTipText);
				delete(pData);
				return -1;
			}

			pData->pItemPos  = new(unsigned, 1);
			if(!pData->pItemPos) {
				delete(pData->pToolTipText);
				delete(pData->pTreeItems);
				delete(pData);
				return -1;
			}

			GlobalInit();

			pData->pItemPos  [0] = 0;
			pData->pTreeItems[0] = NULL;

			SetWindowLongPtr(hWnd, 0, (LONG_PTR)pData);
			pData->iIndent		 = DEFAULT_IDENT;
			pData->iShift		 = DEFAULT_SHIFT;
			pData->uStyle		 = GetWindowLong(hWnd, GWL_STYLE);
			pData->hSem			 = CreateSemaphore(0, 1, 0x70000000, 0);
			pData->hWnd			 = hWnd;
			pData->cIsEnabled	 = (char)IsWindowEnabled(hWnd);
			pData->iAutoAdd		 = 1;
			pData->aColumnPos[0] = 0;
			pData->aColumnPos[1] = 1;

			if(!(pData->uStyle & (TVS_HASBUTTONS | TVS_HASLINES))) {
				pData->cHasRootRow = 0;
			} else {
				pData->cHasRootRow = (char)((pData->uStyle & TVS_LINESATROOT) ? 1 : 0);
			}

			if(!(pData->uStyle & TVS_NOTOOLTIPS)) {
				CreateToolTip(pData);
			}

			UpdateFont(pData);
			UpdateHeight(pData);
			UpdateColorsList(pData);
			UpdateScrollY(pData);

			if(pOpenThemeData)									// Soll ein Thema angezeigt werden
				pData->hTheme = pOpenThemeData(hWnd, L"TREEVIEW");
			else
				pData->hTheme = NULL;

			pData->cGlyphOk = (char)((pData->hTheme) ? 1 : 0);

			if(pData->uStyle & TVS_CHECKBOXES) {
				CreateStateImageList(pData, 0);
			}

			return 0;

		case WM_DESTROY:											// Das Fenster zerstören

			pData = GetHandle(hWnd);

			LOCK(pData);

			TreeListDeleteItem(pData, U(TVI_ROOT), 0);
			if(pData->hStates == THEMEIMGLIST)
				pData->hStates = 0;
			if(pData->hChecks == THEMEIMGLIST)
				pData->hChecks = 0;
			if(pData->hEdit){
				DestroyWindow(pData->hEdit); 
				pData->hEdit   = 0;
			}
			if(pData->hHeader){
				DestroyWindow(pData->hHeader); 
				pData->hHeader = 0;
			}
			if(pData->hToolTip){
				DestroyWindow(pData->hToolTip); 
				pData->hToolTip = 0;
			}
			if(pData->hStates){
				ImageList_Destroy(pData->hStates); 
				pData->hStates = 0;
			}

			if(pData->uStyleEx & TVS_EX_SHAREIMAGELISTS) {
				if(pData->hStates && pData->iStatesMode) {
					ImageList_Destroy(pData->hStates); 
					pData->hStates = 0;
				}
				if(pData->hChecks && pData->iChecksMode) {
					ImageList_Destroy(pData->hChecks); 
					pData->hChecks = 0;
				}
				pData->hImages = 0;
				pData->hSubImg = 0;
				pData->hHeadImg = 0;
			} else {
				if(pData->hStates){
					ImageList_Destroy(pData->hStates); 
					pData->hStates = 0;
				}
				if(pData->hChecks){
					ImageList_Destroy(pData->hChecks); 
					pData->hChecks = 0;
				}
				if(pData->hImages){
					ImageList_Destroy(pData->hImages); 
					pData->hImages = 0;
				}
				if(pData->hSubImg){
					ImageList_Destroy(pData->hSubImg); 
					pData->hSubImg = 0;
				}
				if(pData->hHeadImg){
					if(pData->uStyleEx & TVS_EX_HEADEROWNIMGLIST)
						ImageList_Destroy(pData->hHeadImg); 
					pData->hHeadImg = 0;
				}
			}

			if(pData->hThemeBt){
				pCloseThemeData(pData->hThemeBt);
				pData->hThemeBt = 0;
			}
			if(pData->hTheme){
				pCloseThemeData(pData->hTheme); 
				pData->hTheme  = 0;
			}

			for(uVal = 1; uVal < pData->uColumnCount; uVal++) {
				delete(pData->pExtraItems[uVal - 1]);
				pData->pExtraItems[uVal - 1] = 0;
			}

			if(pData->hFontB){
				DeleteObject(pData->hFontB);
				pData->hFontB = 0;
			}

			pData->uColumnCount = 0;

			UNLOCK(pData);

			return 0;

		case WM_NCDESTROY:										// Das Fenster zerstören

			pData = GetHandle(hWnd);

			LOCK(pData);

			delete(pData->pToolTipText);
			delete(pData->pTreeItems);
			delete(pData->pItemPos);

			UNLOCK(pData);

			SetWindowLongPtr(hWnd, 0, 0);
			CloseHandle(pData->hSem);
			memset(pData, 0, sizeof(TreeListData));
			delete(pData);

			GlobalDeinit();

			return 0;

		case WM_SHOWWINDOW:											// Fenster ein/ausblenden

			if(wParam) {
				pData = GetHandle(hWnd);

				sScroll.cbSize		= sizeof(SCROLLINFO);
				sScroll.fMask		= SIF_ALL;
				sScroll.nMin		= 0;
				sScroll.nMax  		= 0;
				sScroll.nPage 		= 0;
				sScroll.nPos  		= 0;
				sScroll.nTrackPos	= 0;

				SetScrollInfo(pData->hWnd, SB_VERT, &sScroll, TRUE);
				SetScrollInfo(pData->hWnd, SB_HORZ, &sScroll, TRUE);

				pData->uOldXCount	= 0xFFFF;
				pData->uOldYCount	= 0xFFFF;

				UpdateScrollY(pData);
				UpdateScrollX(pData);
			}

			return 0;

		case WM_SIZE:												// Die Fenstergröße wurde verändert

			pData = GetHandle(hWnd);
			uFlag = 0;

			LOCK(pData);

			uVal = LOWORD(lParam);
			if(uVal && uVal != pData->uSizeX) {
				uOld			= pData->uSizeX;
				pData->uSizeX	= uVal;

				if(pData->uColumnCountVar) {					// Spalten mit variabler Breite nach führen
					RECT	sRect;
					int		iDelta;
					int		iNum;

					iDelta = uVal - uOld;

					ChangeColSize(pData, iDelta);

					iNum = UpdateColumns(pData);
					GetClientRect(hWnd, &sRect);
					sRect.left  = iNum;
					sRect.left -= pData->uScrollX;
					sRect.top   = pData->uStartPixel;
					InvalidateRect(hWnd, &sRect, FALSE);
				}

				if(uVal > uOld) {								// Hat sich die Breite vergrößert
					RECT	sRect;

					GetClientRect(hWnd, &sRect);
					sRect.right  = uVal;
					sRect.left   = uOld;
					InvalidateRect(hWnd, &sRect, FALSE);
				}

				pData->aColumnXpos[pData->uColumnCount + 1] = uVal + 1;

				MoveWindow(pData->hHeader, -(int)pData->uScrollX, 0, uVal + pData->uScrollX, pData->uStartPixel, uVal > uOld);
				UpdateScrollX(pData);
			}

			uVal = HIWORD(lParam);
			if(uVal && uVal != pData->uSizeY) {
				if(uVal > pData->uSizeY) {						// Hat sich die Höhe vergrößert
					RECT	sRect;

					GetClientRect(hWnd, &sRect);
					uFlag			 = 1;
					sRect.bottom	 = uVal;
					sRect.top		 = pData->uSizeY;
					InvalidateRect(hWnd, &sRect, FALSE);
				} else {
					uFlag			 = 0;
				}

				pData->uSizeY		 =  uVal;
				pData->uSizeYsub	 = (uVal <= pData->uStartPixel) ? 0 : uVal - pData->uStartPixel;

				if(pData->uSizeY > pData->uStartPixel) {
					pData->uMaxEnties  = pData->uSizeY;
					pData->uMaxEnties -= pData->uStartPixel;
				} else {
					pData->uMaxEnties  = 0;
				}

				pData->uPageEnties	 = pData->uMaxEnties;
				pData->uMaxEnties	+= pData->iRowHeight - 1;
				pData->uMaxEnties   /= pData->iRowHeight;
				pData->uPageEnties  /= pData->iRowHeight;
				// Wenn Höhe vergrößert dann Scroll-Position prüfen
				if(uFlag && pData->uPageEnties > 2 && pData->uScrollY > 0) {
					if(pData->uScrollY + pData->uPageEnties + 1 > pData->uItemPosCount) {
						iPos = pData->uItemPosCount - pData->uPageEnties + 1;
						if(iPos < 0)
							iPos = 0;
						if(U(iPos) != pData->uScrollY) {
							pData->uScrollY = iPos;
							UpdateView(pData);
						}
					}
				}
				UpdateScrollY(pData);
			}

			UNLOCK(pData);

			if(uFlag) {
				RedrawWindow(pData->hHeader, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE);
			}

			return 0;

		case WM_ENABLE:												// Das Fenster feischalten oder sperren

			pData = GetHandle(hWnd);
			if(pData->cIsEnabled != ((U(wParam)) ? 1 : 0)) {
				uVal  = GetWindowLong(hWnd, GWL_STYLE);
				uVal &= (0xFFFF0000 & ~WS_DISABLED);
				uVal |= (0x0000FFFF | WS_DISABLED) & (pData->uStyle ^ WS_DISABLED);
				SetWindowLong(hWnd, GWL_STYLE, uVal);
			}

			return 0;

		case WM_SETFOCUS:											// Das Fenster bekommt den Focus

			pData = GetHandle(hWnd);
			if(!pData->cHasFocus) {
				NMHDR	sNotify;

				LOCK(pData);
				pData->cHasFocus = 1;

				if(pData->uSelectedCount <= 1) {
					if(pData->uSelectedItem) {
						if(pData->uStyleEx & TVS_EX_FULLROWMARK)
							UpdateRow(pData, pData->uSelectedItem);
						else
							UpdateRect(pData, pData->uSelectedItem, pData->uSelectedSub);
					}
				} else {
					UpdateView(pData);
				}

				UNLOCK(pData);

				sNotify.code = NM_SETFOCUS;
				SendNotify(pData, &sNotify);
			}

			return 0;

		case WM_KILLFOCUS:											// Das Fenster bekommt den Focus

			pData = GetHandle(hWnd);
			if(pData->cHasFocus) {
				NMHDR	sNotify;

				LOCK(pData);
				pData->cHasFocus = 0;

				if(pData->uSelectedCount <= 1) {
					if(pData->uSelectedItem) {
						if(pData->uStyleEx & TVS_EX_FULLROWMARK)
							UpdateRow(pData, pData->uSelectedItem);
						else
							UpdateRect(pData, pData->uSelectedItem, pData->uSelectedSub);
					}
				} else {
					UpdateView(pData);
				}

				UNLOCK(pData);

				sNotify.code = NM_KILLFOCUS;
				SendNotify(pData, &sNotify);
			}

			return 0;

		case WM_MOUSEMOVE:											// Gab es eine Mausbewegung

			pData = GetHandle(hWnd);
			sInfo.hItem = 0;
			sInfo.flags = 0;

			if(!(pData->uStyle & TVS_NOTOOLTIPS)) {
				if(!pData->cIsEnabled) {						// Ist das Fenster freigegeben
					return 0;
				}

				LOCK(pData);

				sInfo.pt.x	= LOWORD(lParam);
				sInfo.pt.y	= HIWORD(lParam);
				TreeListHitTest(pData, &sInfo);
				UpdateToolTip(pData, U(sInfo.hItem), sInfo.flags);

				UNLOCK(pData);
			}

			if(pData->uStyle & TVS_TRACKSELECT) {
				if(!pData->cIsEnabled) {						// Ist das Fenster freigegeben
					return 0;
				}

				LOCK(pData);

				if(!sInfo.hItem) {
					sInfo.pt.x	= LOWORD(lParam);
					sInfo.pt.y	= HIWORD(lParam);
					TreeListHitTest(pData, &sInfo);
				}

				if(sInfo.hItem && (sInfo.flags & (TVHT_ONSUBITEM | TVHT_ONSUBLABEL | TVHT_ONITEM))) {
					TreeListSetTrackItem(pData, U(sInfo.hItem), TVHT_SUBTOCOL(sInfo.flags));
				}

				UNLOCK(pData);
			}

			if(wParam & pData->uDragFlags)					// Drag beginnen
				if(U(lParam) != pData->uLastMove) {
					NMTREEVIEW	sNotify;
					BaseItem   *pEntry;
					int			iDiffX;
					int			iDiffY;

					iDiffX = (short)(lParam) - (short)(pData->uLastMove);
					iDiffY = (short)(lParam >> 16) - (short)(pData->uLastMove >> 16);

					if(iDiffX < 0)
						iDiffX = -iDiffX;
					if(iDiffY < 0)
						iDiffY = -iDiffY;

					if(iDiffX <= 2 && iDiffY <= 2) {					// Mehr als zwei Pixel-Verschub
						return 0;
					}

					LOCK(pData);

					if(pData->uDragItem > pData->uTreeItemsMax) {
						UNLOCK(pData);
						return 0;
					}

					pEntry = pData->pTreeItems[pData->uDragItem];
					if(!pEntry) {
						UNLOCK(pData);
						return 0;
					}

					sNotify.hdr.code			= TVN_BEGINDRAG;
					sNotify.action				= (pEntry->uState & TVIS_EXPANDED) ? TVE_COLLAPSE : TVE_EXPAND;
					sNotify.itemNew.mask		= TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_SUBNUMBER;
					sNotify.itemNew.hItem		= (HTREEITEM)(ULONG_PTR)pData->uDragItem;
					sNotify.itemNew.stateMask	= 0xFFFFFFFF;
					sNotify.itemNew.state		= pEntry->uState;
					sNotify.itemNew.lParam		= pEntry->lParam;
					sNotify.itemNew.cChildren	= pData->uDragSub;
					sNotify.itemNew.pszText		= (LPTSTR) - 1;
					sNotify.itemNew.cchTextMax	= -1;
					sNotify.itemOld.mask		= 0;
					sNotify.ptDrag.x			= LOWORD(lParam);
					sNotify.ptDrag.y			= HIWORD(lParam);
					pData->uDragFlags			= 0;
					pData->cClickFlag			= 0;
					pData->cClickEdit			= 0;

					UNLOCK(pData);

					SendNotify(pData, &sNotify.hdr);
				}

			pData->uLastMove = U(lParam);

			return 0;

		case WM_LBUTTONUP:											// Linken Mausklick aufheben

			pData = GetHandle(hWnd);
			pData->uDragFlags &= ~MK_LBUTTON;

			if(pData->cClickFlag || pData->cClickEdit) {
				TreeListMouseClick(pData, uMsg, wParam, lParam);
				pData->cClickFlag = 0;
				pData->cClickEdit = 0;
			} else {
				TreeListMouseNotify(pData, TVN_LBUTTONUP, wParam, lParam);
			}

			return 0;

		case WM_RBUTTONUP:											// Linken Mausklick aufheben

			pData = GetHandle(hWnd);
			pData->uDragFlags &= ~MK_RBUTTON;

			TreeListMouseNotify(pData, TVN_RBUTTONUP, wParam, lParam);

			break;

		case WM_LBUTTONDOWN:										// Mausklick

			pData = GetHandle(hWnd);

			LOCK(pData);

			uTime = GetTickCount();

			if(pData->cButtonFlag && pData->uToolTipItem) {	// Doppelklick simulieren über ToolTip
				pData->cButtonFlag = 0;
				uDelta = uTime - pData->uButtonLast;

				if(uDelta < 700) {
					iPos  = abs(LOWORD(lParam) - LOWORD(pData->uButtonPos));
					iPos += abs(HIWORD(lParam) - HIWORD(pData->uButtonPos));

					if(iPos <= 6) {
						TreeListMouseClick(pData, WM_LBUTTONDBLCLK, wParam, lParam);
						return 0;
					}
				}
			}

			pData->cButtonFlag = 1;
			pData->uButtonLast = uTime;
			pData->uButtonPos  = U(lParam);

			TreeListMouseClick(pData, uMsg, wParam, lParam);

			return 0;

		case WM_LBUTTONDBLCLK:
		case WM_RBUTTONDOWN:
		case WM_RBUTTONDBLCLK:

			pData	= GetHandle(hWnd);

			LOCK(pData);

			pData->cButtonFlag = 0;
			TreeListMouseClick(pData, uMsg, wParam, lParam);

			return 0;

		case WM_KEYDOWN:											// Tastendruck

			pData	= GetHandle(hWnd);
			LOCK(pData);
			TreeListKeyDown(pData, wParam, lParam);

			return 0;

		case WM_KEYUP:												// Tastendruck

			DefWindowProc(hWnd, uMsg, wParam, lParam);
			return 1;

		case WM_CHAR:												// Zeicheneingabe

			pData	= GetHandle(hWnd);
			iMax	= lParam & 0xFFFF;

			for(iPos = 0; iPos < iMax; iPos++) {
				TreeListChar(pData, (UINT)wParam, lParam);
			}

			return 0;

		case WM_DRAWITEM:											// Weietleiten von OwnerDraw des Headers

			if(wParam == 1) {
				pData	= GetHandle(hWnd);
				return SendMessage(GetParent(hWnd), uMsg, GetWindowLong(hWnd, GWL_ID), lParam);
			}

			return 0;

		case WM_MOUSEWHEEL:											// Scrollen mit dem Mausrad

			if(!(LOWORD(wParam) & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON))) {
				pData = GetHandle(hWnd);

				if(pData->uEditItem)
					return 0;

				iDif  =	(short)HIWORD(wParam) / (WHEEL_DELTA / 2);

				if(wParam & MK_CONTROL) {
					iDif *= 9;
				} else
					if(wParam & MK_SHIFT) {
						iDif *= 3;
					}

				iPos  = pData->uScrollY - iDif;
				iMax  = pData->uItemPosCount;
				iMax -=	pData->uPageEnties - 1;

				if(iPos >= iMax)
					iPos = iMax;
				if(iPos < 0)
					iPos = 0;
				if(iPos != (int)pData->uScrollY)
					if(!(pData->uStyle & TVS_NOSCROLL)) {
						pData->uScrollY = iPos;
						SetScrollPos(hWnd, SB_VERT, iPos, TRUE);
						UpdateView(pData);
					}

				return 1;
			}

			return 0;

		case WM_HSCROLL:											// Vertikalles scrollen

			pData	= GetHandle(hWnd);
			iPos	= pData->uScrollX;

			if(pData->uEditItem)
				return 0;

			switch(LOWORD(wParam)) {
				case SB_LINEDOWN:
					iPos += 16;
					break;
				case SB_LINEUP:
					iPos -= 16;
					break;
				case SB_PAGEDOWN:
					iPos += pData->uSizeX;
					break;
				case SB_PAGEUP:
					iPos -= pData->uSizeX;
					break;
				case SB_THUMBPOSITION:
					iPos = HIWORD(wParam);
					break;
				case SB_THUMBTRACK:
					iPos = HIWORD(wParam);
					break;
			}

			uVal = pData->uColumnCount;
			if(uVal)
				iMax  = pData->aColumnXpos[uVal] - pData->uSizeX / 2;
			else
				iMax  = pData->iMaxSizeX;
			iMax -=	pData->uSizeX / 2;

			if(iPos >= iMax)
				iPos = iMax;
			if(iPos < 0)
				iPos = 0;
			if(iPos != (int)pData->uScrollX) {
				pData->uScrollX = iPos;
				SetScrollPos(hWnd, SB_HORZ, iPos, TRUE);
				UpdateView(pData);

				if(pData->hHeader) {
					MoveWindow(pData->hHeader, -iPos, 0, pData->uSizeX + iPos, pData->uStartPixel, TRUE);
				}
			}

			return 0;

		case WM_VSCROLL:											// Vertikalles scrollen

			pData	= GetHandle(hWnd);
			iPos	= pData->uScrollY;

			if(pData->uEditItem)
				return 0;

			switch(LOWORD(wParam)) {
				case SB_LINEDOWN:
					iPos++;
					break;
				case SB_LINEUP:
					iPos--;
					break;
				case SB_PAGEDOWN:
					iPos += (pData->uPageEnties > 1) ? pData->uPageEnties - 1 : 1;
					break;
				case SB_PAGEUP:
					iPos -= (pData->uPageEnties > 1) ? pData->uPageEnties - 1 : 1;
					break;
				case SB_THUMBPOSITION:
				case SB_THUMBTRACK:
					if(pData->uItemPosCount < 0x7F00) {
						iPos = HIWORD(wParam);
					} else {
						sScroll.cbSize = sizeof(SCROLLINFO);
						sScroll.fMask  = SIF_TRACKPOS;
						GetScrollInfo(hWnd, SB_VERT, &sScroll);
						iPos = sScroll.nTrackPos;
					}
			}

			iMax  = pData->uItemPosCount;
			iMax -=	pData->uPageEnties - 1;

			if(iPos >= iMax)
				iPos = iMax;
			if(iPos < 0)
				iPos = 0;
			if(iPos != (int)pData->uScrollY) {
				pData->uScrollY = iPos;
				SetScrollPos(hWnd, SB_VERT, iPos, TRUE);
				UpdateView(pData);
			}

			return 0;

		case WM_SYSCOLORCHANGE:										// Wurden die Systemfarben verändert

			pData	= GetHandle(hWnd);

			LOCK(pData);

			if(pData->hTheme) {
				pCloseThemeData(pData->hTheme);
				pData->hTheme   = NULL;
			}

			if(pData->hThemeBt) {
				pCloseThemeData(pData->hThemeBt);
				pData->hThemeBt = NULL;
			}

			if(pOpenThemeData) {
				pData->hTheme   = pOpenThemeData(hWnd, L"TREEVIEW");
				pData->cGlyphOk = (char)((pData->hTheme) ? 1 : 0);
			}

			if(pData->iStatesMode)
				CreateStateImageList(pData, 0);
			if(pData->iChecksMode)
				CreateStateImageList(pData, 1);

			if(pData->hHeader)
				SendMessage(pData->hHeader, WM_SYSCOLORCHANGE, 0, 0);
			UpdateColorsList(pData);
			UNLOCK(pData);

			return 0;

		case WM_GETFONT:											//tell what font we are using
        {
			HFONT hFont;
			pData	= GetHandle(hWnd);

			LOCK(pData);
			hFont = pData->hFontN;
			UNLOCK(pData);
			return (LRESULT)hFont;
        }

		case WM_SETFONT:											// Einen neuen Font zuweisen

			pData	= GetHandle(hWnd);

			LOCK(pData);

			if(CreateFontset(pData, (HFONT)wParam)){
				if(UpdateFont(pData))
					UpdateView(pData);
			}

			UNLOCK(pData);

			return 0;

		case WM_STYLECHANGED:										// Hat sich der Fenstersytle geändert

			if(wParam == GWL_STYLE) {
				pData	= GetHandle(hWnd);
				lParam	= ((STYLESTRUCT *)lParam)->styleNew;

				LOCK(pData);
				uChange		   = U(lParam) ^ pData->uStyle;
				pData->uStyle  = U(lParam);

				if(uChange & (TVS_CHECKBOXES | TVS_NONEVENHEIGHT)) {
					if(lParam & TVS_CHECKBOXES) {
						if(pData->hStates == NULL) {
							CreateStateImageList(pData, 0);
						}
					} else {
						if(pData->iStatesMode && pData->hStates) {
							if(pData->hStates != THEMEIMGLIST) {
								ImageList_Destroy(pData->hStates);
							}

							pData->hStates     = NULL;
							pData->iStatesMode = 0;
						}
					}

					UpdateHeight(pData);
					UpdateScrollY(pData);
				}

				if(uChange & (TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT | TVS_RTLREADING | TVS_CHECKBOXES | TVS_TRACKSELECT)) {
					if(!(pData->uStyle & (TVS_HASBUTTONS | TVS_HASLINES))) {
						pData->cHasRootRow = 0;
					} else {
						pData->cHasRootRow = (char)((pData->uStyle & TVS_LINESATROOT) ? 1 : 0);
					}

					UpdateView(pData);
				}

				if(uChange & TVS_NOSCROLL) {
					pData->uOldYCount = 0xFFFF;
					pData->uOldXCount = 0xFFFF;

					UpdateScrollX(pData);
					UpdateScrollY(pData);
				}

				if(uChange & TVS_NOTOOLTIPS) {
					if(pData->uStyle & TVS_NOTOOLTIPS) {
						UpdateToolTip(pData, 0, 0);
					} else {
						CreateToolTip(pData);
					}
				}

				if(uChange & WS_DISABLED) {
					pData->cIsEnabled = (char)((pData->uStyle & WS_DISABLED) ? 0 : 1);
					UpdateView(pData);
					if(!pData->cIsEnabled)
						UpdateToolTip(pData, 0, 0);
				}

				UNLOCK(pData);
			}

			return 0;

		case WM_PAINT:												// Das Fenster zeichnen

			if(wParam) {
				TreeListDraw(hWnd, (HDC)wParam, NULL);
			} else {
				PAINTSTRUCT sPaint;

				hDc = BeginPaint(hWnd, &sPaint);

				if(pBeginBufferedPt && pEndBufferedPt) {
					RECT	sRect;
					HDC		hDcBuffer;
					HANDLE	hBufferedPaint;

					GetClientRect(hWnd, &sRect);
					hBufferedPaint = pBeginBufferedPt(hDc, &sRect, BPBF_COMPATIBLEBITMAP, NULL, &hDcBuffer);

					if(hBufferedPaint) {
						TreeListDraw(hWnd, hDcBuffer, &sRect);
						pEndBufferedPt(hBufferedPaint, TRUE);
					} else {
						TreeListDraw(hWnd, hDc, &sRect);
					}
				} else {
					TreeListDraw(hWnd, hDc, &sPaint.rcPaint);
				}

				EndPaint(hWnd, &sPaint);
			}

			return 0;

		case WM_TIMER:												// Timer Funktion

			if(wParam == ID_TOOLTIPCHECK) {
				pData	= GetHandle(hWnd);
				if(pData->uToolTipItem) {
					if(pData->uToolTipShow) {					// Verzögertes einblenden
						pData->uToolTipShow--;
						if(pData->uToolTipShow)
							return 0;

						LOCK(pData);

						GetCursorPos(&sPoint);

						sInfo.pt.x = sPoint.x;
						sInfo.pt.y = sPoint.y;

						ScreenToClient(hWnd, &sInfo.pt);

						if(WindowFromPoint(sPoint) == hWnd) {
							TreeListHitTest(pData, &sInfo);
						} else {
							sInfo.hItem = 0;
							sInfo.flags = 0;
						}

						if(sInfo.flags & TVHT_ONITEM)
							if(pData->uToolTipItem == U(sInfo.hItem) && pData->uToolTipSub == 0) {
								TOOLINFO sInfo;

								UNLOCK(pData);

								sInfo.cbSize	= sizeof(sInfo);
								sInfo.hwnd		= pData->hWnd;
								sInfo.uId		= (UINT_PTR)pData->hWnd;

								if(pData->uToolTipItem) {
									SendMessage(pData->hToolTip, TTM_TRACKACTIVATE, 0, (LPARAM)&sInfo);
								}

								SendMessage(pData->hToolTip, TTM_TRACKPOSITION, 0, MAKELONG(pData->sToolTipPos.x, pData->sToolTipPos.y));
								SendMessage(pData->hToolTip, TTM_TRACKACTIVATE, 1, (LPARAM)&sInfo);

								return 0;
							}

						if(sInfo.flags & TVHT_ONSUBITEM)
							if(pData->uToolTipItem == U(sInfo.hItem) && TVHT_SUBTOCOL(sInfo.flags) == pData->uToolTipSub) {
								TOOLINFO sInfo;

								UNLOCK(pData);

								sInfo.cbSize	= sizeof(sInfo);
								sInfo.hwnd		= pData->hWnd;
								sInfo.uId		= (UINT_PTR)pData->hWnd;

								if(pData->uToolTipItem) {
									SendMessage(pData->hToolTip, TTM_TRACKACTIVATE, 0, (LPARAM)&sInfo);
								}

								SendMessage(pData->hToolTip, TTM_TRACKPOSITION, 0, MAKELONG(pData->sToolTipPos.x, pData->sToolTipPos.y));
								SendMessage(pData->hToolTip, TTM_TRACKACTIVATE, 1, (LPARAM)&sInfo);

								return 0;
							}

						// ToolTip wieder entferenen
						UpdateToolTip(pData, U(sInfo.hItem), sInfo.flags);
						UNLOCK(pData);
					}

					LOCK(pData);

					GetCursorPos(&sInfo.pt);
					ScreenToClient(hWnd, &sInfo.pt);
					TreeListHitTest(pData, &sInfo);
					UpdateToolTip(pData, U(sInfo.hItem), sInfo.flags);

					UNLOCK(pData);
				}
			}

			return 0;

		case WM_COMMAND:											// Kommando Nachrichnten

			if(wParam == MAKELONG(3, EN_KILLFOCUS) || wParam == MAKELONG(3, EN_RETURN) || wParam == MAKELONG(3, CBN_KILLFOCUS)) {
				pData = GetHandle(hWnd);
				if(pData->uEditItem) {
					LOCK(pData);
					TreeListEndLabelEdit(pData, (wParam == MAKELONG(3, EN_RETURN)) ? 2 : 1);
					UNLOCK(pData);
					if(pData->uEditMode)
						if(wParam != MAKELONG(3, EN_KILLFOCUS)) {
							SetFocus(pData->hWnd);
						}
				}
			} else
				if(wParam == MAKELONG(3, EN_ESCAPE)) {				// ESC-Taste in Edit-Fenster
					pData = GetHandle(hWnd);
					if(pData->uEditItem) {
						LOCK(pData);
						TreeListEndLabelEdit(pData, 0);
						UNLOCK(pData);
						if(pData->uEditMode)
							SetFocus(pData->hWnd);
					}
				}													// Änderung in Edit-Fenster
				else
					if(wParam == MAKELONG(3, EN_CHANGE) || wParam == MAKELONG(3, CBN_EDITCHANGE) || wParam == MAKELONG(3, CBN_SELCHANGE) || wParam == MAKELONG(3, EN_SETTEXT)) {
						pData = GetHandle(hWnd);
						pData->cColumnStart = 1;
					}

			return 0;

		case WM_NOTIFY:												// Notify Nachrichnten

			if(wParam == 1) {									// Nachricht vom Header
				NMHEADER   *pHdr = (NMHEADER *)lParam;
				HDITEM		sItem;
				HDITEM		sTemp;
				RECT		sRect;
				int			iDelta;
				int			iCode;
				int			iSize;
				int			iNext;
				int			iCol;
				int			iSub;

				iCode = pHdr->hdr.code;
				// Hat sich die Spaltenbreite verändert
				if(iCode == HDN_ITEMCHANGED && (pHdr->pitem->mask & HDI_WIDTH)) {
					pData	= GetHandle(hWnd);
					iCol	= pHdr->iItem;

					if(pData->aColumn[iCol].sReal != pHdr->pitem->cxy) {
						iCode = HDN_TRACK;
					}
				}

				if(iCode == HDN_BEGINDRAG) {					// Drag&Drop im Header
					pData	= GetHandle(hWnd);

					if(!(pData->uStyleEx & TVS_EX_HEADERDRAGDROP) || pHdr->iItem == 0) {
						return 1;
					}

					return 0;
				}

				if(iCode == HDN_ENDDRAG) {						// Drag&Drop im Header fertig
					if(pHdr->pitem->iOrder == 0 || pHdr->iItem == 0) {
						return 1;
					}

					pData	= GetHandle(hWnd);

					if(!(pData->uStyleEx & TVS_EX_HEADERDRAGDROP)) {
						return 1;
					}

					PostMessage(hWnd, TVM_SETCOLUMNORDERARRAY, FROM_HEADER, 0);

					return 0;
				}

				if(iCode == HDN_BEGINTRACK) {					// User will die Spaltenbreite ändern
					pData	= GetHandle(hWnd);

					if(!pData->cHasFocus) {
						SetFocus(pData->hWnd);
					}

					if(pData->uStyleEx & TVS_EX_NOCOLUMNRESIZE) {	// Darf der User die Spaltenbreite ändern
						return 1;
					}

					if(pData->uStyleEx & TVS_EX_FIXEDCOLSIZE) {	// Fixe gesammte Spaltenbreite
						iNext = pData->aColumn[pHdr->iItem].bIndex;

						for(iNext++;; iNext++) {				// Suche nächste veränerbare Spalte
							if(U(iNext) >= pData->uColumnCount) {
								return 1;
							}

							iSub = pData->aColumnPos[iNext];

							if(pData->aColumn[iSub].sFixed == 0) {
								break;
							}
						}
					}

					if(U(pHdr->iItem) < pData->uColumnCount)		// Darf die Spalte verändert werden
						if(pData->aColumn[pHdr->iItem].sFixed) {
							POINT	sPoint;
							int		iCol;
							int		iSub;

							if(pData->aColumn[pHdr->iItem].sReal || pHdr->iItem < 0) {
								return 1;
							}

							GetCursorPos(&sPoint);					// Die nächste veränderbare Spalte greifen
							ScreenToClient(pData->hHeader, &sPoint);

							PostMessage(pData->hHeader, WM_LBUTTONUP, 0, MAKELONG(sPoint.x, sPoint.y));

							iCol = pData->aColumn[pHdr->iItem].bIndex;
							iSub = pData->aColumnPos[iCol - 1];

							PostMessage(pData->hHeader, WM_LBUTTONDOWN, 0, MAKELONG(pData->aColumnXpos[iSub + 1] - 2, sPoint.y));

							return 1;
						}
				}

				if(iCode == HDN_DIVIDERDBLCLICK) {				// Doppelcklick auf Spaltentrenner
					pData	= GetHandle(hWnd);

					if(!pData->cHasFocus) {
						SetFocus(pData->hWnd);
					}

					if(pData->uStyleEx & TVS_EX_NOCOLUMNRESIZE) {	// Darf der User die Spaltenbreite ändern
						return 0;
					}

					if(U(pHdr->iItem) < pData->uColumnCount)		// Darf die Spalte verändert werden
						if(pData->aColumn[pHdr->iItem].sFixed) {
							return 0;
						}

					LOCK(pData);

					iSize = TreeListScanColumn(pData, pHdr->iItem);
					if(iSize) {								// Spalte auf maximale Textbreite
						sItem.cxy	= iSize;
						pHdr->pitem	= &sItem;
						iCode		= HDN_ENDTRACK;

						if(pData->aColumn[pHdr->iItem].bMinEx)	// Minimale Breite prüfen
							if(pData->aColumn[pHdr->iItem].sMin > sItem.cxy) {
								sItem.cxy	 = pData->aColumn[pHdr->iItem].sMin;
							}
					}

					UNLOCK(pData);
				}

				if(iCode == HDN_TRACK || iCode == HDN_ENDTRACK) {	// Wurde die Spaltenbreite verändert
					pData	= GetHandle(hWnd);

					if(!pData->cHasFocus && iCode == HDN_ENDTRACK) {
						SetFocus(pData->hWnd);
					}

					LOCK(pData);

					iCol		= pHdr->iItem;
					sItem.mask	= HDI_WIDTH;
					sItem.cxy	= pHdr->pitem->cxy;

					if(pData->aColumn[iCol].bMinEx)				// Minimale Breite prüfen
						if(pData->aColumn[iCol].sMin > sItem.cxy) {
							sItem.cxy		 = pData->aColumn[iCol].sMin;
							pHdr->pitem->cxy = sItem.cxy;

							if(sItem.cxy == pData->aColumn[iCol].sSize) {
								UNLOCK(pData);
								return 0;
							}
						}

					if(pData->uStyleEx & TVS_EX_FIXEDCOLSIZE) {	// Fixe gesammte Spaltenbreite
						if(iCol == 0 && sItem.cxy <= 0) {
							sItem.cxy		 = 1;
							pHdr->pitem->cxy = 1;

							if(sItem.cxy == pData->aColumn[iCol].sSize) {
								UNLOCK(pData);
								return 0;
							}
						}

						iNext = pData->aColumn[iCol].bIndex;

						for(iNext++;; iNext++) {				// Überspringe fixierte Spalten
							if(U(iNext) >= pData->uColumnCount) {
								UNLOCK(pData);
								return 0;
							}

							iSub = pData->aColumnPos[iNext];

							if(pData->aColumn[iSub].sFixed == 0) {
								break;
							}
						}

						iDelta    = pData->aColumn[iCol].sReal - sItem.cxy;
						sTemp.cxy = pData->aColumn[iSub].sReal + iDelta;

						if(iDelta < 0) {						// Nächste Spalte wird zu klein
							if(sTemp.cxy < 0) {
								sTemp.cxy  =  0;
								iDelta     = -pData->aColumn[iSub].sReal;
								sItem.cxy  =  pData->aColumn[iCol].sReal - iDelta;
							}

							if(pData->aColumn[iSub].bMinEx)		// Minimale Breite prüfen
								if(pData->aColumn[iSub].sMin > sTemp.cxy) {
									iDelta    +=  pData->aColumn[iSub].sMin - sTemp.cxy;
									sTemp.cxy  =  pData->aColumn[iSub].sMin;
									sItem.cxy  =  pData->aColumn[iCol].sReal - iDelta;
								}

							pHdr->pitem->cxy = sItem.cxy;

							if(iDelta >= 0) {					// Keine Änderung
								UNLOCK(pData);
								return 0;
							}
						}

						if(pData->aColumn[iSub].bWeight) {	// Variable Spalte
							pData->iVarSize -= pData->aColumn[iSub].sSize;
							pData->iVarSize += sTemp.cxy;
						} else {									// Fixe Spalte
							pData->iFixSize -= pData->aColumn[iSub].sSize;
							pData->iFixSize += sTemp.cxy;
						}

						sTemp.mask	= HDI_WIDTH;

						iDif						= pData->aColumn[iSub].sReal;
						pData->aColumn[iSub].sSize	= (short)sTemp.cxy;
						pData->aColumn[iSub].sReal	= (short)sTemp.cxy;
						Header_SetItem(pData->hHeader, iSub, &sTemp);

						// Breite verändert
						if(iDif != sTemp.cxy && (pData->uStyleEx & TVS_EX_HEADERCHGNOTIFY)) {
							TV_COLSIZE	sNotify;

							sNotify.hdr.code			= TVN_COLUMNCHANGED;
							sNotify.uColumn				= iSub;
							sNotify.uIndex				= pData->aColumn[iSub].bIndex;
							sNotify.uPosX				= pData->aColumnXpos[iSub];
							sNotify.iSize				= sTemp.cxy;

							UNLOCK(pData);
							SendNotify(pData, &sNotify.hdr);
							LOCK(pData);
						}
					}

					if(pData->aColumn[iCol].bWeight) {		// Ist es eine variable Spalte
						pData->iVarSize -= pData->aColumn[iCol].sSize;
						pData->iVarSize += sItem.cxy;
					} else {										// Fixe Spalte
						pData->iFixSize -= pData->aColumn[iCol].sSize;
						pData->iFixSize += sItem.cxy;
					}

					iDif					   = pData->aColumn[iCol].sReal;
					pData->aColumn[iCol].sSize = (short)sItem.cxy;
					pData->aColumn[iCol].sReal = (short)sItem.cxy;
					Header_SetItem(pData->hHeader, iCol, &sItem);

					iSize = UpdateColumns(pData);
					if(iSize < 0x10000) {						// Spalten neu zeichnen
						GetClientRect(hWnd, &sRect);
						sRect.left  = iSize;
						sRect.left -= pData->uScrollX;
						InvalidateRect(hWnd, &sRect, FALSE);
					}

					UpdateScrollX(pData);
					UNLOCK(pData);

					// Breite verändert
					if(iDif != sItem.cxy && (pData->uStyleEx & TVS_EX_HEADERCHGNOTIFY)) {
						TV_COLSIZE	sNotify;

						sNotify.hdr.code			= TVN_COLUMNCHANGED;
						sNotify.uColumn				= iCol;
						sNotify.uIndex				= pData->aColumn[iCol].bIndex;
						sNotify.uPosX				= pData->aColumnXpos[iCol];
						sNotify.iSize				= sItem.cxy;

						UNLOCK(pData);
						SendNotify(pData, &sNotify.hdr);
						LOCK(pData);
					}
				}
				// Weiterleiten an Elternfenster
				if(iCode == HDN_ITEMCLICK || iCode == HDN_ITEMDBLCLICK) {
					pData	= GetHandle(hWnd);

					if(!pData->cHasFocus) {
						SetFocus(pData->hWnd);
					}

					SendNotify(pData, &pHdr->hdr);
				}
			} else {
				NMHDR		   *pHdr = (NMHDR *)lParam;
				NMTTDISPINFO   *pInfo;

				switch(pHdr->code) {
					case TTN_GETDISPINFO:

						pData			= GetHandle(hWnd);
						pInfo			= (NMTTDISPINFO *)pHdr;
						pInfo->lpszText = pData->pToolTipText;
						SendMessage(pData->hToolTip, WM_SETFONT, (WPARAM)pData->hFontT, 0);
						break;
				}
			}

			return 0;

		case WM_GETDLGCODE:											// Welche Tasten werden im Dialog benutzt

			pMsg = (MSG *)lParam;
			if(pMsg && pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RETURN) {
				return DLGC_WANTALLKEYS;
			}

			return DLGC_WANTCHARS | DLGC_WANTARROWS;

		case WM_PASTE:												// Weiterleiten an Edit-Control
		case WM_CLEAR:
		case WM_COPY:
		case WM_CUT:

			pData = GetHandle(hWnd);

			if(pData->hEdit) {
				PostMessage(pData->hEdit, uMsg, 0, 0);
				return 0;
			}

			break;

		case TVM_SETIMAGELIST:										// Die Image-Liste einstellen

			pData	= GetHandle(hWnd);

			LOCK(pData);
#ifdef _DEBUG
			char dbg[255];
			sprintf(dbg, "TVM_SETIMAGELIST %d", (int)wParam);
			OutputDebugStringA(dbg);
#endif
			switch((int)wParam) {
				case TVSIL_NORMAL:
					lRet = (LPARAM)pData->hImages;
					if(lRet == lParam)
						break;

					pData->hImages = (HIMAGELIST)lParam;
					if(!pData->hImages) {
						pData->iImagesXsize = 0;
						pData->iImagesYsize = 0;
					} else {
						IMAGEINFO sInfo;
						ImageList_GetImageInfo(pData->hImages, 0, &sInfo);
						pData->iImagesXsize = sInfo.rcImage.right - sInfo.rcImage.left;
						pData->iImagesYsize = sInfo.rcImage.bottom - sInfo.rcImage.top;
						if(pData->hHeader && ((pData->uStyleEx & TVS_EX_HEADEROWNIMGLIST) == 0))
							SendMessage(pData->hHeader, HDM_SETIMAGELIST, 0, (LPARAM)pData->hImages);
					}

					if(!pData->iSubImgMode || pData->hSubImg == pData->hImages) {
						pData->iSubImgMode  = 0;
						pData->hSubImg      = pData->hImages;
						pData->iSubImgXsize = pData->iImagesXsize;
						pData->iSubImgYsize = pData->iImagesXsize;
					}

					if(!pData->cFixedHeight) {
						pData->iRowHeight = 1;
						UpdateHeight(pData);
						UpdateScrollY(pData);
					} else {
						UpdateView(pData);
					}

					break;

				case TVSIL_HEADER:
					lRet = pData->uStyleEx & TVS_EX_HEADEROWNIMGLIST;
					if(lRet == 0)
						break;

					lRet = (LPARAM)pData->hHeadImg;
					if(lRet == lParam)
						break;

					pData->hHeadImg = (HIMAGELIST)lParam;

					if(pData->hHeader) 
						SendMessage(pData->hHeader, HDM_SETIMAGELIST, 0, (LPARAM)pData->hHeadImg);

					if(!pData->cFixedHeight) {
						pData->iRowHeight = 1;
						UpdateHeight(pData);
						UpdateScrollY(pData);
					} else {
						UpdateView(pData);
					}
					break;

				case TVSIL_STATE:
					lRet = (LPARAM)pData->hStates;
					if(lRet == lParam)
						break;

					if(pData->iStatesMode) {
						if(pData->hStates != THEMEIMGLIST) {
							ImageList_Destroy(pData->hStates);
						}

						pData->iStatesMode = 0;
					}

					pData->hStates = (HIMAGELIST)lParam;
					if(!pData->hStates) {
						pData->iStatesXsize = 0;
						pData->iStatesYsize = 0;

						if(pData->uStyle & TVS_CHECKBOXES) {
							CreateStateImageList(pData, 0);
						}
					} else
						if(pData->hStates == THEMEIMGLIST) {
							pData->iStatesXsize = 16;
							pData->iStatesYsize = 16;
							pData->iStatesMode  = 1;
							if(!pData->hThemeBt)
								CreateStateImageList(pData, 0);
						} else {
							IMAGEINFO sInfo;
							ImageList_GetImageInfo(pData->hStates, 0, &sInfo);
							pData->iStatesXsize = sInfo.rcImage.right - sInfo.rcImage.left;
							pData->iStatesYsize = sInfo.rcImage.bottom - sInfo.rcImage.top;
							pData->iStatesMode  = 0;
						}

					if(!pData->cFixedHeight) {
						pData->iRowHeight = 1;
						UpdateHeight(pData);
						UpdateScrollY(pData);
					} else {
						UpdateView(pData);
					}

					break;

				case TVSIL_CHECK:
					lRet = (LPARAM)pData->hChecks;
					if(lRet == lParam)
						break;

					if(pData->iChecksMode) {
						if(pData->hChecks != THEMEIMGLIST) {
							ImageList_Destroy(pData->hChecks);
						}

						pData->iChecksMode = 0;
					}

					pData->hChecks = (HIMAGELIST)lParam;
					if(!pData->hChecks) {
						pData->iChecksXsize = 0;
						pData->iChecksYsize = 0;

						for(uVal = 0; uVal < pData->uColumnCount; uVal++) {
							if(pData->aColumn[uVal].bEdit < TVSIL_CHECK)
								continue;
							CreateStateImageList(pData, 1);
							break;
						}
					} else
						if(pData->hChecks == THEMEIMGLIST) {
							pData->iChecksXsize = 16;
							pData->iChecksYsize = 16;
							pData->iChecksMode  = 1;
							if(!pData->hThemeBt)
								CreateStateImageList(pData, 1);
						} else {
							IMAGEINFO sInfo;
							ImageList_GetImageInfo(pData->hChecks, 0, &sInfo);
							pData->iChecksXsize = sInfo.rcImage.right - sInfo.rcImage.left;
							pData->iChecksYsize = sInfo.rcImage.bottom - sInfo.rcImage.top;
							pData->iChecksMode  = 0;
						}

					if(!pData->cFixedHeight) {
						pData->iRowHeight = 1;
						UpdateHeight(pData);
						UpdateScrollY(pData);
					} else {
						UpdateView(pData);
					}

					break;

				case TVSIL_SUBIMAGES:
					lRet = (LPARAM)pData->hSubImg;
					if(lRet == lParam)
						break;

					if(pData->iSubImgMode) {
						ImageList_Destroy(pData->hSubImg);
						pData->iSubImgMode = 0;
					}

					pData->hSubImg = (HIMAGELIST)lParam;
					if(!pData->hSubImg || pData->hSubImg == pData->hImages) {
						pData->iSubImgMode  = 0;
						pData->hSubImg      = pData->hImages;
						pData->iSubImgXsize = pData->iImagesXsize;
						pData->iSubImgYsize = pData->iImagesXsize;
					} else {
						IMAGEINFO sInfo;
						ImageList_GetImageInfo(pData->hSubImg, 0, &sInfo);
						pData->iSubImgXsize = sInfo.rcImage.right - sInfo.rcImage.left;
						pData->iSubImgYsize = sInfo.rcImage.bottom - sInfo.rcImage.top;
						pData->iSubImgMode  = 1;
					}

					if(!pData->cFixedHeight) {
						pData->iRowHeight = 1;
						UpdateHeight(pData);
						UpdateScrollY(pData);
					} else {
						UpdateView(pData);
					}

					break;

				default:
					lRet = 0;
			}

			UNLOCK(pData);

			return lRet;

		case TVM_GETIMAGELIST:										// Die Image-Liste abfragen

			pData	= GetHandle(hWnd);
			if(!pData)
				return 0;

			switch((int)wParam) {
				case TVSIL_NORMAL:
					lRet = (LPARAM)pData->hImages;
					break;
				case TVSIL_STATE:
					lRet = (LPARAM)pData->hImages;
					break;
				case TVSIL_CHECK:
					lRet = (LPARAM)pData->hChecks;
					break;
				case TVSIL_HEADER:
					lRet = (LPARAM)pData->hHeadImg;
					break;
				default
						:
					lRet = 0;
			}

			return lRet;

		case TVM_GETSETOPTION:										// Diverse Optionen einstellen

			pData	= GetHandle(hWnd);
			lRet	= 0;

			switch(U(wParam) & ~TVOP_WRITEOPTION) {
				case TVOP_AUTOEXPANDOFF:							// Icon Offset für TVS_EX_AUTOEXPANDICON

					lRet = pData->iAutoAdd;

					if((wParam & TVOP_WRITEOPTION) && lParam != lRet) {
						LOCK(pData);

						pData->iAutoAdd = (int)lParam;
						if(pData->uItemPosCount > 0) {
							UpdateView(pData);
						}

						UNLOCK(pData);
					}

					break;
			}

			return lRet;

		case TVM_GETITEMSTATE:										// Die Statusbits eines Eintrags abfragen

			pData = GetHandle(hWnd);
			LOCK(pData);

			if(U(wParam) <= pData->uTreeItemsMax) {
				BaseItem *pEntry;

				pEntry = pData->pTreeItems[U(wParam)];
				if(!pEntry)
					lRet = 0;
				{
					lRet = pEntry->uState;
				}
			} else {
				lRet = 0;
			}

			UNLOCK(pData);

			return lRet;

		case TVM_GETITEM:											// Einträge abfragen

			pData = GetHandle(hWnd);
			LOCK(pData);
			lRet  = TreeListGetItem(pData, (TV_ITEM *)lParam);
			UNLOCK(pData);

			return lRet;

		case TVM_SETITEM:											// Einträge einfügen

			pData = GetHandle(hWnd);
			LOCK(pData);
			lRet  = TreeListSetItem(pData, (TV_ITEM *)lParam);
			UNLOCK(pData);

			return lRet;

		case TVM_INSERTITEM:										// Einträge einfügen

			pData = GetHandle(hWnd);
			LOCK(pData);
			lRet  = TreeListInsertItem(pData, (TV_INSERTSTRUCT *)lParam);
			UNLOCK(pData);

			return lRet;

		case TVM_DELETEITEM:										// Einträge löschen

			pData = GetHandle(hWnd);
			LOCK(pData);
			lRet  = TreeListDeleteItem(pData, U(lParam), (U(wParam) == 0x88) ? 2 : 1);
			UNLOCK(pData);

			return lRet;

		case TVM_FINDITEM:											// Einträge suchen

			pData = GetHandle(hWnd);
			LOCK(pData);
			lRet  = TreeListFindItem(pData, U(wParam), (TV_FIND *)lParam);
			UNLOCK(pData);

			return lRet;

		case TVM_DELETECOLUMN:										// Löschen einer Salte im Header

			pData = GetHandle(hWnd);
			LOCK(pData);
			lRet  = TreeListDeleteColumn(pData, U(wParam));
			UNLOCK(pData);

			return lRet;

		case TVM_INSERTCOLUMN:										// Einfügen einer Salte im Header

			pData = GetHandle(hWnd);
			LOCK(pData);
			lRet  = TreeListInsertColumn(pData, (int)wParam, (TV_COLUMN *)lParam);
			UNLOCK(pData);

			return lRet;

		case TVM_GETCOLUMNCOUNT:									// Abfragen der Spaltenanzahl

			pData = GetHandle(hWnd);
			return (LRESULT)pData->uColumnCount;

		case TVM_GETHEADER:											// Abfragen des Header-Fensters

			pData = GetHandle(hWnd);
			return (LRESULT)pData->hHeader;

		case TVM_GETEXTENDEDSTYLE:									// Abfragen der erweiterten Style-Bits

			pData = GetHandle(hWnd);
			return pData->uStyleEx;

		case TVM_SETEXTENDEDSTYLE:									// Einstellen der erweiterten Style-Bits

			pData = GetHandle(hWnd);
			if(!wParam)
				wParam = 0xFFFFFFFF;

			uVal  = pData->uStyleEx & ~U(wParam);
			uVal |=	U(lParam) & U(wParam);

			if(pData->uStyleEx != uVal) {						// Has it changed?
				LOCK(pData);
				uChange = pData->uStyleEx ^ uVal;
				pData->uStyleEx = uVal;

				if(uChange & TVS_EX_AUTOHSCROLL) {
					UpdateScrollX(pData);
				}

				if((uChange & TVS_EX_GRAYEDDISABLE) && !pData->cIsEnabled) {
					UpdateView(pData);
				}

				if(uChange & TVS_EX_ITEMLINES) {
					UpdateHeight(pData);
					UpdateScrollY(pData);
				}

				if(uChange & (TVS_EX_ALTERNATECOLOR | TVS_EX_SUBSELECT | TVS_EX_MULTISELECT | TVS_EX_FULLROWMARK | TVS_EX_FULLROWITEMS)) {
					UpdateView(pData);
				}

				if((uChange&~U(lParam)) & TVS_EX_SUBSELECT) {
					TreeListSelectItem(pData, pData->uSelectedItem, 0, TVC_UNKNOWN);
				}

				if(pData->hHeader){
					if((uChange & TVS_EX_HIDEHEADERS) || (uChange & TVS_EX_HEADEROWNIMGLIST)) {
						pData->uStartPixel = (pData->uStyleEx & TVS_EX_HIDEHEADERS) ? 0 : bDrawWithTheme ? GetSystemMetrics(SM_CYHSCROLL) : 17;
						MoveWindow(pData->hHeader, -(int)pData->uScrollX, 0, pData->uSizeX + pData->uScrollX, pData->uStartPixel, TRUE);
						
						if(pData->uStyleEx & TVS_EX_HEADEROWNIMGLIST){
							SendMessage(pData->hHeader, HDM_SETIMAGELIST, 0, (LPARAM)pData->hHeadImg);
						} else {
							SendMessage(pData->hHeader, HDM_SETIMAGELIST, 0, (LPARAM)pData->hImages);
						}

						UpdateView(pData);
					}
				}

				UNLOCK(pData);
			}

			return uVal;

		case TVM_GETINSERTMARKCOLOR:								// Farben abfragen
			wParam = TVC_INSERT;
			goto ColGet;
		case TVM_GETLINECOLOR:
			wParam = TVC_LINE;
			goto ColGet;
		case TVM_GETTEXTCOLOR:
			wParam = TVC_TEXT;
		case TVM_GETBKCOLOR:

ColGet:
			if(wParam < 0 || wParam >= MAX_COLORS)
				return 0xFFFFFFFF;
			pData = GetHandle(hWnd);
			return pData->uColors[U(wParam)];

		case TVM_SETINSERTMARKCOLOR:								// Farben einstellen
			wParam = TVC_INSERT;
			goto ColSet;
		case TVM_SETLINECOLOR:
			wParam = TVC_LINE;
			goto ColSet;
		case TVM_SETTEXTCOLOR:
			wParam = TVC_TEXT;
		case TVM_SETBKCOLOR:

ColSet:
			if(wParam < 0 || wParam >= MAX_COLORS)
				return 0xFFFFFFFF;

			pData = GetHandle(hWnd);

			LOCK(pData);

			lRet = pData->uColors[U(wParam)];

			if((COLORREF)lParam == TV_NOCOLOR) {				// Standartfarbe einstellen
				if(pData->cColorChanged[U(wParam)]) {
					pData->cColorChanged[U(wParam)] = 0;
					UpdateColorsList(pData);
					UpdateView(pData);
				}
			} else {
				pData->cColorChanged[U(wParam)] = 1;
				pData->uColors      [U(wParam)] = (COLORREF)lParam;

				if(lRet != lParam) {
					UpdateColorsList(pData);
					UpdateView(pData);
				}
			}

			UNLOCK(pData);

			return lRet;

		case TVM_HITTEST:											// Abfragen von Koortinatenpositionen

			pData = GetHandle(hWnd);

			LOCK(pData);
			lRet = TreeListHitTest(pData, (LPTV_HITTESTINFO)lParam);
			UNLOCK(pData);

			return lRet;

		case TVM_SELECTCHILDS:										// Mehrere Einträge auswählen

			pData = GetHandle(hWnd);

			LOCK(pData);
			lRet = TreeListSelectChilds(pData, U(lParam), U(wParam));
			UNLOCK(pData);

			return lRet;

		case TVM_SELECTSUBITEM:										// Einen (Sub)Eintrag auswählen

			pData = GetHandle(hWnd);

			LOCK(pData);
			if(!(pData->uStyleEx & TVS_EX_SUBSELECT))
				wParam = 0;
			lRet = TreeListSelectItem(pData, U(lParam), U(wParam), TVC_UNKNOWN);
			if(lRet > 1)
				lRet = 1;
			UNLOCK(pData);

			return lRet;

		case TVM_SELECTDROP:										// Den unterstrichenen Eintrags auswählen

			pData = GetHandle(hWnd);

			LOCK(pData);
			lRet = TreeListSetTrackItem(pData, U(lParam), U(wParam));
			UNLOCK(pData);

			return lRet;

		case TVM_SELECTITEM:										// Einen Eintrag auswählen

			pData = GetHandle(hWnd);

			LOCK(pData);

			switch(wParam) {
				case TVGN_CARET:
					lRet = TreeListSelectItem(pData, U(lParam), 0, TVC_UNKNOWN);
					if(lRet > 1)
						lRet = 1;
					break;
				case TVGN_DROPHILITE:
					lRet = TreeListSetTrackItem(pData, U(lParam), 0);
					break;

				case TVGN_FIRSTVISIBLE:
					lRet = TreeListEnsureVisible(pData, U(lParam), FIRST_LINE);
					lRet = (lRet < 0) ? 0 : 1;
					break;

				default
						:
					lRet = 0;
			}

			UNLOCK(pData);

			return lRet;

		case TVM_GETCOLUMNORDERARRAY:								// Spaltensortierung abfragen

			if(!lParam)
				return 0;

			pData = GetHandle(hWnd);

			if(!pData->hHeader)
				return 0;

			return Header_GetOrderArray(pData->hHeader, U(wParam), lParam);

		case TVM_SETCOLUMNORDERARRAY:								// Spalten sortieren

			pData = GetHandle(hWnd);
			LOCK(pData);
			lRet  = TreeListSetOrderArray(pData, U(wParam), (unsigned *)lParam);
			UNLOCK(pData);

			return lRet;

		case TVM_GETCOUNT:											// Anzahl der Einträge abfragen

			pData = GetHandle(hWnd);
			return pData->uTreeItemsCount;

		case TVM_GETINDENT:											// Einrückung abfragen

			pData = GetHandle(hWnd);
			return pData->iIndent;

		case TVM_SETINDENT:											// Einrückung einstellen

			pData = GetHandle(hWnd);
			lRet  = pData->iIndent;
			if(wParam < 5)
				wParam = 5;
			if(wParam > 64)
				wParam = 64;
			if(lRet != (LPARAM)wParam) {
				LOCK(pData);
				pData->iIndent = (int)wParam;
				UpdateView(pData);
				UNLOCK(pData);
			}

			return lRet;

		case TVM_GETITEMHEIGHT:										// Zeilenhöhe abfragen

			pData = GetHandle(hWnd);
			return pData->iRowHeight;

		case TVM_SETITEMHEIGHT:										// Zeilenhöhe abfragen

			pData = GetHandle(hWnd);
			lRet  = pData->iRowHeight;

			if(wParam == -1) {
				LOCK(pData);
				pData->cFixedHeight = 0;
				UpdateHeight(pData);
				UpdateScrollY(pData);
				UNLOCK(pData);
				return lRet;
			}

			if(wParam & 1)
				if(!(pData->uStyleEx & TVS_NONEVENHEIGHT))
					wParam--;
			if(wParam <  1)
				wParam =  1;
			if(wParam > 256)
				wParam = 256;

			if(lRet != (LPARAM)wParam) {
				LOCK(pData);
				pData->cFixedHeight = 1;
				pData->iRowHeight   = (int)wParam;
				UpdateView(pData);
				UNLOCK(pData);
			}

			return lRet;

		case TVM_GETVISIBLECOUNT:									// Abfragen der sichtbaren Zeilen

			pData = GetHandle(hWnd);
			return pData->uPageEnties;

		case TVM_ENSUREVISIBLE:										// Einen Eintrag ins Sichtfenster legen

			pData = GetHandle(hWnd);
			LOCK(pData);
			lRet = TreeListEnsureVisible(pData, U(lParam), (int)wParam);
			UNLOCK(pData);

			return lRet;

		case TVM_ISITEMVISIBLE:										// Ist ein Eintrag sichtbar

			pData = GetHandle(hWnd);
			LOCK(pData);
			lRet = TreeListIsVisible(pData, U(lParam), (int)wParam);
			UNLOCK(pData);

			return lRet;

		case TVM_GETNEXTITEM:										// Einen Eintrag suchen

			pData = GetHandle(hWnd);
			LOCK(pData);
			lRet = TreeListGetNextItem(pData, U(lParam), (int)wParam);
			UNLOCK(pData);

			return lRet;

		case TVM_GETITEMRECT:										// Das Rechteck eines Eintrages abfragen

			pData = GetHandle(hWnd);
			LOCK(pData);
			uVal = *(unsigned *)lParam;
			lRet = TreeListGetItemRect(pData, uVal, U(wParam), (RECT *)lParam);
			UNLOCK(pData);

			return lRet;

		case TVM_EXPAND:											// Einen Eintrag umklappen

			pData = GetHandle(hWnd);
			LOCK(pData);
			lRet = 0;
NextExp:
			if(U(lParam) <= pData->uTreeItemsMax) {
				BaseItem *pEntry;
#ifndef __REACTOS__
				POINT	  sPoint;
#endif

				pEntry = pData->pTreeItems[U(lParam)];
				if(pEntry) {
					sPoint.x = 0;
					sPoint.y = 0;

					if(wParam & TVE_EXPANDNEXT) {				// Bei erstem Eltereneintrag beginnen
						if(!pEntry->uParent) {
							UNLOCK(pData);
							return 1;
						}

						lParam  = pEntry->uParent;
						pEntry  = pData->pTreeItems[U(lParam)];
						wParam &= ~TVE_EXPANDNEXT;
					}

					switch(wParam & 0x0F) {
						case TVE_COLLAPSE:
							if(pEntry->uState & TVIS_EXPANDED) {
								lRet = (TreeListToggleItem(pData, U(lParam), 0)) ? 0 : 1;
							} else {					// Nur Flag löschen
								pEntry->uState &= ~TVIS_EXPANDPARTIAL;
								lRet = 1;
							}
							// Kinder löschen
							if(wParam & TVE_COLLAPSERESET) {
								pEntry->uState &= TVIS_EXPANDEDONCE;

								while(pEntry->uLastChild) {
									if(!TreeListDeleteItem(pData, pEntry->uLastChild, 1))
										break;
									pEntry = pData->pTreeItems[U(lParam)];
								}
							}
							// Auch Eltern zuklappen
							if(!(wParam & TVE_EXPANDRECURSIVE)) {
								break;
							}

							if(!pEntry->uParent) {
								break;
							}

							lParam = pEntry->uParent;
							goto NextExp;
							break;

						case TVE_EXPAND:
							if(!(pEntry->uState & TVIS_EXPANDED)) {
								uVal = (wParam & TVE_EXPANDPARTIAL) ? TVIS_EXPANDPARTIAL : 0;
								lRet = (TreeListToggleItem(pData, U(lParam), uVal)) ? 0 : 1;
							} else {					// Schon aufgeklappt
								lRet = 1;

								if(wParam & TVE_EXPANDPARTIAL) {
									if(!(wParam & TVE_EXPANDFORCE))
										if((pEntry->uState & (TVIS_EXPANDPARTIAL | TVIS_EXPANDEDONCE)) != (TVIS_EXPANDPARTIAL | TVIS_EXPANDEDONCE)) {
											lRet = (TreeListToggleItem(pData, U(lParam), TVE_EXPAND | TVIS_EXPANDPARTIAL)) ? 0 : 1;
										}
								} else {
									if(pEntry->uState & TVIS_EXPANDPARTIAL) {
										lRet = (TreeListToggleItem(pData, U(lParam), TVE_EXPAND)) ? 0 : 1;
									}
								}
							}
							// Auch Eltern aufklappen
							if(!(wParam & TVE_EXPANDRECURSIVE)) {
								break;
							}

							if(!pEntry->uParent) {
								break;
							}

							lParam = pEntry->uParent;
							goto NextExp;
							break;

						case TVE_TOGGLE:
							lRet = (TreeListToggleItem(pData, U(lParam), 0)) ? 0 : 1;
							break;
					}

					// Aktion auch auf alle Kinder anwenden
					if((wParam & TVE_ALLCHILDS) && pEntry->uFirstChild) {
						lParam   = pEntry->uFirstChild;
						wParam  &= ~(TVE_EXPANDRECURSIVE | TVE_EXPANDNEXT | TVE_ONLYCHILDS);
						sPoint.x = 0;
						sPoint.y = 0;

						for(;;) {
							UNLOCK(pData);

							TreeListProc(hWnd, uMsg, wParam, lParam);

							LOCK(pData);

							if(U(lParam) > pData->uTreeItemsMax) {
								break;
							}

							pEntry = pData->pTreeItems[U(lParam)];
							if(!pEntry || !pEntry->uNextItem)
								break;

							lParam = pEntry->uNextItem;
						}
					}
				}
			}

			UNLOCK(pData);

			return lRet;

		case TVM_SETINSERTMARK:										// Einfügemarke eintellen

			pData = GetHandle(hWnd);

			LOCK(pData);
			lRet = TreeListSetInsertMark(pData, U(lParam), (int)wParam);
			UNLOCK(pData);

			return lRet;

		case TVM_SETITEMBKCOLOR:									// Hintergrundfarbe eines Eintrages ändern

			pData = GetHandle(hWnd);

			LOCK(pData);
			lRet = TreeListSetItemColor(pData, U(wParam & 0xFFFFFF), U(wParam) >> 24, (COLORREF)lParam, 0);
			UNLOCK(pData);

			return lRet;

		case TVM_SETITEMTEXTCOLOR:									// Textfarbe eines Eintrages ändern

			pData = GetHandle(hWnd);

			LOCK(pData);
			lRet = TreeListSetItemColor(pData, U(wParam & 0xFFFFFF), U(wParam) >> 24, (COLORREF)lParam, 1);
			UNLOCK(pData);

			return lRet;

		case TVM_GETITEMBKCOLOR:									// Hintergrundfarbe eines Eintrages abfragen

			pData = GetHandle(hWnd);

			LOCK(pData);
			lRet = TreeListGetItemColor(pData, U(wParam), U(lParam), 0);
			UNLOCK(pData);

			return lRet;

		case TVM_GETITEMTEXTCOLOR:									// Textfarbe eines Eintrages abfragen

			pData = GetHandle(hWnd);

			LOCK(pData);
			lRet = TreeListGetItemColor(pData, U(wParam), U(lParam), 1);
			UNLOCK(pData);

			return lRet;

		case TVM_SORTCHILDRENEX:									// Sortieren mit Funktion

			pData = GetHandle(hWnd);

			LOCK(pData);
			lRet = TreeListSortItemsEx(pData, (TV_SORTEX *)lParam, (int)wParam);
			UNLOCK(pData);

			return lRet;

		case TVM_SORTCHILDRENCB:									// Sortieren mit Funktion

			pData = GetHandle(hWnd);

			LOCK(pData);
			lRet = TreeListSortItemsCb(pData, (TV_SORTCB *)lParam, (int)wParam);
			UNLOCK(pData);

			return lRet;

		case TVM_SORTCHILDREN:										// Sortieren der Kindereinträge

			pData = GetHandle(hWnd);

			LOCK(pData);
			lRet = TreeListSortItems(pData, U(lParam), (int)wParam);
			UNLOCK(pData);

			return lRet;

		case TVM_GETITEMOFROW:										// Hole den Eintrag von einer Reihe

			pData = GetHandle(hWnd);

			LOCK(pData);
			if(U(lParam) < pData->uItemPosCount)
				lRet = pData->pItemPos[U(lParam)];
			else
				lRet = 0;
			UNLOCK(pData);

			return lRet;

		case TVM_GETROWCOUNT:										// Hole die Anzahl der sichtbaren Reihen

			pData = GetHandle(hWnd);

			return pData->uItemPosCount;

		case TVM_GETCOUNTPERPAGE:									// Hole die Anzahl der darstelbaren Reihen

			pData = GetHandle(hWnd);

			return pData->uMaxEnties;

		case TVM_GETROWOFITEM:										// Suche Reihe zu einem Eintrag

			pData = GetHandle(hWnd);

			LOCK(pData);
			if(U(lParam) <= pData->uTreeItemsMax) {
				BaseItem *pEntry;

				pEntry = pData->pTreeItems[U(lParam)];
				if(!pEntry)
					lRet = -1;
				else
					lRet = pEntry->uShowPos - 1;
			} else {
				lRet = -1;
			}
			UNLOCK(pData);

			return lRet;

		case TVM_EDITLABEL:											// Einen Eintrag editieren

			pData = GetHandle(hWnd);
			if(!(pData->uStyle & TVS_EDITLABELS))
				return 0;

			LOCK(pData);

			if((wParam & 0xFFFFFF00) == TVLE_DONOTIFY) {			// Notify starten
				lRet = TreeListStartNotifyEdit(pData, U(lParam), TVIR_EDITCOL(wParam), VK_RETURN, 0);
			} else {
				pData->cColumnStart = 0;
				lRet = (LRESULT)TreeListEditLabel(pData, U(lParam), U(wParam));
			}

			UNLOCK(pData);

			return lRet;

		case TVM_GETEDITCONTROL:									// Das Handle des Edit-Fensters abfragen

			pData = GetHandle(hWnd);
			return (LRESULT)pData->hEdit;

		case TVM_GETTOOLTIPS:										// Das Handle des ToolTip-Fensters abfragen

			pData = GetHandle(hWnd);
			return (LRESULT)pData->hToolTip;

		case TVM_SETTOOLTIPS:										// Das Handle für das ToolTip-Fensters setzen

			pData = GetHandle(hWnd);
			lRet  = (LRESULT)pData->hToolTip;
			pData->hToolTip = (HWND)wParam;
			return lRet;

		case TVM_SETUSERDATASIZE:									// Einstellen der Größe der User-Daten

			if(lParam < 0 || lParam > 0x1000000)
				return -1;
			pData = GetHandle(hWnd);

			LOCK(pData);

			if(pData->uTreeItemsCount > 0) {
				lRet = 0;
			} else {
				pData->uUserDataSize = U(lParam);
				lRet = lParam;
			}

			UNLOCK(pData);

			return lRet;

		case TVM_GETUSERDATASIZE:									// Abfragen der Größe der User-Daten

			pData = GetHandle(hWnd);
			return pData->uUserDataSize;

		case TVM_GETUSERDATA:										// Einen Zeiger auf die User-Daten holen

			pData = GetHandle(hWnd);

			if(pData->uUserDataSize && U(lParam) <= pData->uTreeItemsMax) {
				BaseItem   *pEntry;

				pEntry = pData->pTreeItems[U(lParam)];
				if(pEntry)
					return (LRESULT)(pEntry + 1);
			}

			return 0;

		case TVM_SETCOLUMN:											// Einen Spalten-Header einstellen

			pData = GetHandle(hWnd);

			if(pData->hHeader && U(wParam) < pData->uColumnCount) {
				HDITEM		sItem;
				unsigned	uBit;
				unsigned	uCol;
				TV_COLUMN  *pCol;

				pCol	= (TV_COLUMN *)lParam;
				uCol	= U(wParam);

				uVal = pCol->cx;
				if(uVal == TVCF_LASTSIZE) {						// Breite vor Fixierung benutzen
					if(pData->aColumn[uCol].sFixed > 0) {
						uVal = pData->aColumn[uCol].sFixed;
					} else {
						uVal = pData->aColumn[uCol].sReal;
					}
				}

				if(pCol->mask & TVCF_FIXED) {					// Die Spalte fixieren
					uBit = pCol->fmt & TVCFMT_FIXED;

					if(uBit != 0 && pData->aColumn[uCol].sFixed == 0) {
						pData->aColumn[uCol].sFixed = pData->aColumn[uCol].sReal;
						if(!pData->aColumn[uCol].sFixed)
							pData->aColumn[uCol].sFixed = 100;
					}

					if(uBit == 0 && pData->aColumn[uCol].sFixed != 0) {
						pData->aColumn[uCol].sFixed = 0;
					}
				}

				sItem.mask = 0;
				if(pCol->mask & TVCF_FMT){
					sItem.mask |= HDI_FORMAT; 
					sItem.fmt    = pCol->fmt | HDF_STRING;
				}
				if(pCol->mask & TVCF_IMAGE){
					sItem.mask |= HDI_IMAGE;  
					sItem.iImage = pCol->iImage;        
				}
				if(pCol->mask & TVCF_WIDTH){
					sItem.mask |= HDI_WIDTH;  
					sItem.cxy    = uVal; 
					pData->aColumn[uCol].sSize = (short)sItem.cxy;
				}
				if(pCol->mask & TVCF_TEXT){
					sItem.mask |= HDI_TEXT;  
					sItem.pszText = pCol->pszText;  
					sItem.cchTextMax = pCol->cchTextMax;
				}

				if(sItem.mask) {
					lRet = SendMessage(pData->hHeader, HDM_SETITEM, uCol, (LPARAM)&sItem);
				} else {
					lRet = 1;
				}

				if(lRet && (pCol->mask & TVCF_FMT)) {			// Hat sich die Ausrichtung verändert
					BYTE bAlign;

					switch(pCol->fmt) {
						case TVCFMT_CENTER: bAlign = DT_CENTER;	break;
						case TVCFMT_RIGHT:  bAlign = DT_RIGHT; 	break;
						default:            bAlign = DT_LEFT;   break;
					}

					if(pData->aColumn[uCol].bAlign != bAlign) {
						pData->aColumn[uCol].bAlign = bAlign;
						UpdateColRect(pData, uCol);
					}
				}

				if(lRet && (pCol->mask & TVCF_MARK)) {			// Die Spalte markieren
					RECT		sRect;
					unsigned	uNext;

					uVal = (pCol->fmt & TVCFMT_MARK) ? 1 : 0;
					if(uVal != pData->aColumn[uCol].bMark) {
						pData->aColumn[uCol].bMark = (BYTE)uVal;
						pData->uMarkedCols		  +=  uVal * 2 - 1;

						uNext		 = pData->aColumn    [uCol ].bNext;
						sRect.left   = pData->aColumnXpos[uCol ];
						sRect.right  = pData->aColumnXpos[uNext] + 1;
						sRect.bottom = pData->uScrollX;
						sRect.top    = pData->uSizeY;
						InvalidateRect(pData->hWnd, &sRect, FALSE);
					}
				}
			} else {
				lRet = 0;
			}

			return lRet;

		case TVM_GETCOLUMN:											// Einen Spalten-Header abfragen

			pData = GetHandle(hWnd);
			if(pData->hHeader) {
				HDITEM		sItem;
				TV_COLUMN  *pCol = (TV_COLUMN *)lParam;
				int bWantMark;
				unsigned	uCol;
				
				uCol = U(wParam);
				sItem.mask = 0;
				if(pCol->mask & TVCF_FMT)   {
					sItem.mask |= HDI_FORMAT;
					bWantMark = pCol->fmt & TVCFMT_MARK;	//memorize if we want the marked state
				}
				if(pCol->mask & TVCF_IMAGE){
					sItem.mask |= HDI_IMAGE; 
				}
				if(pCol->mask & TVCF_WIDTH){
					sItem.mask |= HDI_WIDTH;
				}
				if(pCol->mask & TVCF_TEXT){
					sItem.mask |= HDI_TEXT; 
					sItem.pszText = pCol->pszText;  
					sItem.cchTextMax = pCol->cchTextMax;
				}

				lRet = SendMessage(pData->hHeader, HDM_GETITEM, wParam, (LPARAM)&sItem);

				pCol->mask = 0;

				if(sItem.mask & HDI_FORMAT) {
					pCol->mask |= TVCF_FMT;
					pCol->fmt    = sItem.fmt;

					if(bWantMark && pData->aColumn[uCol].bMark)
						pCol->fmt |= TVCFMT_MARK;
					if(!pData->aColumn[uCol].sReal && pData->aColumn[uCol].sFixed)
						pCol->fmt |= TVCFMT_FIXED;
				}
				if(sItem.mask & HDI_IMAGE){
					pCol->mask |= TVCF_IMAGE; 
					pCol->iImage = sItem.iImage; 
				}
				if(sItem.mask & HDI_WIDTH){
					pCol->mask |= TVCF_WIDTH; 
					pCol->cx = sItem.cxy;    
				}
				if(sItem.mask & HDI_TEXT){
					pCol->mask |= TVCF_TEXT;
					pCol->pszText = sItem.pszText;  
					pCol->cchTextMax = sItem.cchTextMax;
				}

			} else {
				lRet = 0;
			}

			return lRet;

		case TVM_SETFOCUSITEM:										// Focus einstellen

			pData = GetHandle(hWnd);

			LOCK(pData);
			lRet = TreeListSetFocus(pData, U(lParam), U(wParam));
			UNLOCK(pData);

			return lRet;

		case TVM_SETCOLUMNWIDTH:									// Die Spaltenbreite einstellen

			pData = GetHandle(hWnd);

			if(pData->hHeader && U(wParam) < pData->uColumnCount && (int)lParam >= 0) {
				HDITEM sItem;

				sItem.mask = HDI_WIDTH;
				sItem.cxy  = (int)lParam;
				lRet = SendMessage(pData->hHeader, HDM_SETITEM, wParam, (LPARAM)&sItem);
			} else {
				lRet = 0;
			}

			return lRet;

		case TVM_COLUMNAUTOEDIT:									// AutoEdit für eine Spalte einstellen

			pData = GetHandle(hWnd);
			uVal  = (wParam >> 11) & 0x3F;

			if(uVal >= pData->uColumnCount) {
				return 0;
			}

			uMode = (U(wParam) >> TVAE_MODEPOS) & 7;

			if(uMode == TVAX_NONE) {
				wParam = 0;
			} else
				if(uVal == 0 && uMode >= TVAX_CHECK) {			// Checkboxes are not allowed in the first column
					return 0;
				}

			LOCK(pData);

			uChange  = pData->aColumn[uVal].bEdit << TVAE_MODEPOS;
			uChange |= pData->aColumn[uVal].bFlags & TVAE_STATEENABLE;

			pData->aColumn[uVal].bEdit   = (BYTE)(uMode);
			pData->aColumn[uVal].bFlags  = (BYTE)(wParam);
			pData->aColumn[uVal].bCbChar = (BYTE)(wParam >> 17);
			pData->aColumn[uVal].bCbSize = (BYTE)(wParam >> 25);
			pData->aColumn[uVal].pCbData = (void *)lParam;
			pData->aColumn[uVal].iCbIcon = -1;

			if(uMode >= TVAX_CHECK) {							// Sollen Checkboxen dargestetllt werden
				if(!pData->hChecks) {
					CreateStateImageList(pData, 1);
					UpdateHeight(pData);
				}

				if((uChange ^ wParam) & ((TVAE_MODEMASK ^ TVAE_CHECK ^ TVAE_CHECKED) | TVAE_STATEENABLE)) {
					UpdateColRect(pData, uVal);
				}
			} else
				if((uChange & TVAE_MODEMASK) >= TVAE_CHECK) {	// Waren Checkboxen dargestetllt
					if((uChange ^ wParam) & ((TVAE_MODEMASK ^ TVAE_CHECK ^ TVAE_CHECKED) | TVAE_STATEENABLE)) {
						UpdateColRect(pData, uVal);
					}
				}

			UNLOCK(pData);

			return 1;

		case TVM_COLUMNAUTOICON:									// Icons für AutoEdit einstellen

			pData = GetHandle(hWnd);
			uVal  = U(wParam);

			if(uVal >= pData->uColumnCount || !pData->aColumn[uVal].bEdit) {
				return 0;
			}

			LOCK(pData);

			pData->aColumn[uVal].iCbIcon = (int)lParam;

			UNLOCK(pData);

			return 1;

		case TVM_GETCOLUMNWIDTH:									// Die Spaltenbreite abfragen

			pData = GetHandle(hWnd);
			if(pData->hHeader) {
				HDITEM sItem;

				sItem.mask = HDI_WIDTH;
				sItem.cxy  = (int)lParam;
				SendMessage(pData->hHeader, HDM_GETITEM, wParam, (LPARAM)&sItem);
				lRet = sItem.cxy;
			} else {
				lRet = 0;
			}

			return lRet;

		case TVM_CREATEDRAGIMAGE:									// Ein Drag-Image erzeugen

			pData = GetHandle(hWnd);
			LOCK(pData);
			lRet = (LRESULT)CreateDragImage(pData, U(lParam), U(wParam));
			UNLOCK(pData);
			return lRet;

		case TVM_ENDEDITLABELNOW:									// Die aktuelle Eingabe abbrechen

			pData = GetHandle(hWnd);
			LOCK(pData);
			lRet = (TreeListEndLabelEdit(pData, (wParam) ? 0 : 1)) ? 1 : 0;
			UNLOCK(pData);
			return 0;

		case TVM_GETISEARCHSTRING:									// Holt den aktuellen Suchtext

			uDelta = GetTickCount() - uKeyLast;

			if(!lParam) {
				return (uDelta <= 750) ? uKeyPos : 0;
			}

			if(uDelta > 750) {
				((TCHAR *)lParam)[0] = 0;
				return FALSE;
			}

			memcpy((TCHAR *)lParam, cKeyData, uKeyPos * sizeof(TCHAR));
			((TCHAR *)lParam)[uKeyPos] = 0;
			return TRUE;

		case TVM_GETUNICODEFORMAT:									// Wird gerade UNI-Code verwendet

#if UNICODE
			return 1;
#else
			return 0;
#endif

	}

	return DefWindowProc(hWnd, uMsg, wParam, lParam);
}


//*****************************************************************************
//*
//*		TreeListDraw
//*
//*****************************************************************************
//	Zeichnet das Fenster
static void TreeListDraw(HWND hWnd, HDC hDc, RECT *pRect) {

	COLORREF		uEcColor;
	COLORREF		uEvColor;
	COLORREF		uBkColor;
	COLORREF		uBtColor;
	COLORREF		uOdColor;
	COLORREF		uOcColor;
	COLORREF		uFrColor;
	COLORREF		uInColor;
	COLORREF		uOldColor;
	COLORREF		uOutColor;
	COLORREF		uNextColor;
	COLORREF		uTempColor;
	HRGN			hRgnMain;
	HRGN			aRgn[MAX_COLUMNS + 1];
	SIZE			sSize;
	RECT			sRect;
	RECT			sArea;
	RECT			sButton;
	TreeListData   *pData;
	BaseItem	   *pTemp;
	BaseItem	   *pEntry;
	ExtraItem	   *pExtra;
	LPCTSTR			pText;
	HIMAGELIST		hImgList;
	unsigned		uTextSize;
	unsigned		uRgnCount;
	unsigned		uAutoMask;
	unsigned		uFirstPos;
	unsigned		uStyleEx;
	unsigned		uColMark;
	unsigned		uColumn;
	unsigned		uState;
	unsigned		uStyle;
	unsigned		uExtra;
	unsigned		uNext;
	unsigned		uMark;
	unsigned		uItem;
	unsigned		uBits;
	unsigned		uPos;
	unsigned		uMax;
	int			   *pOffsets;
	int				iRnType[MAX_COLUMNS + 1];
	int				iXscroll;
	int				iHeight;
	int				iIndent;
	int				iDelta;
	int				iImage;
	int				iShift;
	int				iStart;
	int				iCount;
	int				iLevel;
	int				iLast;
	int				iSize;
	int				iXpos;
	int				iYpos;
	int				iMaxX;
	int				iAdd;
	int				i;

	pData = GetHandle(hWnd);

	LOCK(pData);

	GetClientRect(hWnd, &sRect);

	if(!pRect)
		pRect = &sRect;

	iXscroll = -(int)pData->uScrollX;
	pOffsets = pData->aColumnXpos;
	hRgnMain = CreateRectRgn(pRect->left, pRect->top, pRect->right, pRect->bottom);

	uMax = pData->uColumnCount;
	if(!uMax) {
		aRgn   [  0 ] = CreateRectRgn(sRect.left, sRect.top, sRect.right, sRect.bottom);
		iRnType[  0 ] = CombineRgn(aRgn[0], aRgn[0], hRgnMain, RGN_AND);
		uRgnCount	  = 1;
	} else {
		for(uPos = 0; uPos < uMax; uPos++) {
			uExtra = pData->aColumnPos[uPos  ];
			uNext  = pData->aColumnPos[uPos + 1];
			aRgn   [uExtra] = CreateRectRgn(sRect.left + pOffsets[uExtra] + iXscroll, sRect.top, sRect.left + pOffsets[uNext] + iXscroll, sRect.bottom);
			iRnType[uExtra] = CombineRgn(aRgn[uExtra], aRgn[uExtra], hRgnMain, RGN_AND);
		}

		aRgn   [uPos] = CreateRectRgn(sRect.left + pOffsets[uPos] + iXscroll, sRect.top, sRect.right, sRect.bottom);
		iRnType[uPos] = CombineRgn(aRgn[uPos], aRgn[uPos], hRgnMain, RGN_AND);

		uRgnCount = uMax + 1;
	}
	
	iHeight		  = pData->iRowHeight;
	uStyleEx 	  = pData->uStyleEx;
	uStyle	 	  = pData->uStyle;
	iIndent	 	  = pData->iIndent;
	iShift	 	  = pData->iShift;
	uPos	 	  = pData->uScrollY;
	uMax	 	  = pData->uMaxEnties + uPos;
	uNext		  = (pData->uColumnCount <= 1) ? 1 : pData->aColumn[1].bIndex;
	uFirstPos     = pData->aColumnXpos[uNext];

	if(iRnType[0] == NULLREGION)
		iMaxX = pData->iMaxSizeX;
	else
		iMaxX = uFirstPos - 1;

	if(uStyleEx & TVS_EX_ITEMLINES) {
		iHeight--;
	}

	if(uStyleEx & TVS_EX_AUTOEXPANDICON) {
		uAutoMask = TVIS_EXPANDED;
	} else {
		uAutoMask = 0;
	}

	if(uMax > pData->uItemPosCount) {
		uMax = pData->uItemPosCount;
	}

	uBkColor  = pData->uColors[TVC_BK   ];
	uFrColor  = pData->uColors[TVC_FRAME];

	if(pData->uStyleEx & TVS_EX_ALTERNATECOLOR) {				// Abwechselnde Farben
		uOdColor = pData->uColors[TVC_ODD    ];
		uEvColor = pData->uColors[TVC_EVEN   ];
		uOcColor = pData->uColors[TVC_COLODD ];
		uEcColor = pData->uColors[TVC_COLEVEN];
	} else {
		uOdColor = uBkColor;
		uEvColor = uBkColor;
		uOcColor = pData->uColors[TVC_COLBK];
		uEcColor = pData->uColors[TVC_COLBK];
	}

	if(!pData->cIsEnabled)										// Wenn Fenster gessperrt grau zeichnen
		if(pData->uStyleEx & TVS_EX_GRAYEDDISABLE) {
			uBkColor = pData->uColors[TVC_GRAYED];
			uEvColor = uEcColor;
			uOdColor = uOcColor;
		}

	uInColor = pData->uColors[TVC_LINE];
	uBtColor = pData->uColors[TVC_BOX ];
	iStart	 = 0;
	iLast	 = 0;
	
	sArea.top = sRect.top + pData->uStartPixel;
	SelectObject(hDc, pData->hFontN);
	SelectObject(hDc, hPatternPen);
	SetBkColor(hDc, uBkColor);
	SetBkMode(hDc, TRANSPARENT);
	SetTextAlign(hDc, TA_LEFT | TA_TOP);
	SetTextColor(hDc, pData->uColors[TVC_TEXT]);

//******************** Einträge zeichnen **************************************
	for(; uPos < uMax; uPos++) {									// Alle Einträge ausgeben
		uItem = pData->pItemPos[uPos];

		pEntry	= pData->pTreeItems[uItem];
		if(!pEntry)
			break;

		if((pEntry->uState & TVIS_SELECTED) && (uStyleEx & TVS_EX_FULLROWMARK)) {
			if(uStyleEx & TVS_EX_ALTERNATECOLOR)
				uOutColor = (uPos & 1) ? pData->uColors[TVC_MARKODD] : pData->uColors[TVC_MARKEVEN];
			else
				uOutColor = pData->uColors[TVC_MARK];

			uMark     = (unsigned)~TVIS_BKCOLOR;
			uColMark  =  0;
		} else
			if(uPos & 1) {										// Farbe wechselweise ändern
				uColMark  =  pData->aColumn[0].bMark;
				uOutColor = (uColMark) ? uOcColor : uOdColor;
				uMark     = 0xFFFFFFFF;
			} else {
				uColMark  =  pData->aColumn[0].bMark;
				uOutColor = (uColMark) ? uEcColor : uEvColor;
				uMark     = 0xFFFFFFFF;
			}

		sArea.bottom  = sArea.top + pData->iRowHeight;
		sArea.left	  = iXscroll;
		iLevel		  = pEntry->uLevel;

		if(iRnType[0] == NULLREGION) {
			goto ExtraDraw;
		}

		uBits		=  pEntry->uState & 0xFFFF;
		uBits      |=  pEntry->bFlags << 16;
		uBits      &=  uMark;
		iImage		= (uBits & LVIS_SELECTED) ? pEntry->iSelectedImage : pEntry->iImage;
		pText		=  pEntry->pText;
		uTextSize	=  pEntry->uTextSize;

		if(pData->uSelectedSub && uItem == pData->uSelectedItem) {
			if(pData->uSelectedCount <= 1 || !(pData->uStyleEx & TVS_EX_SUBSELECT)) {
				uBits &= ~TVIS_SELECTED;
			}
		}

		if(pEntry->bCallback) {
			CallbackEntry(pData, pEntry, uItem, pEntry->bCallback, &iImage, &uTextSize, &pText);

			pEntry	= pData->pTreeItems[uItem];
			if(!pEntry)
				break;
		}

		SelectObject(hDc, aRgn[0]);

		if((uStyleEx & (TVS_EX_ITEMLINES | TVS_EX_FULLROWITEMS)) == (TVS_EX_ITEMLINES | TVS_EX_FULLROWITEMS)) {
			sButton.left	= 0;
			sButton.right	= iIndent * (iLevel + 1) + 2;
			sButton.bottom	= sArea.bottom + 1;
			sButton.top		= sArea.bottom - 2;

			SetBkColor(hDc, uFrColor);
			ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sButton, NULL, 0, NULL);
		}

		if(pData->aColumn[0].bMark) {							// Ist die erste Spalte markiert
			uBkColor = pData->uColors[TVC_COLBK];
		}

		SetBkColor(hDc, (uStyleEx & TVS_EX_FULLROWITEMS) ? uOutColor : uBkColor);

		if(pData->cHasRootRow)
			iLevel++;

		if(iLevel <= 0)
			goto NoRootLines;

		if(uStyle & (TVS_HASBUTTONS | TVS_HASLINES)) {
			iLevel--;
		}

		if(uStyleEx & TVS_EX_FULLROWITEMS) {
			sArea.bottom--;
			iAdd = 1;
		} else {
			iAdd = 0;
		}

		if(uStyle & TVS_HASLINES) {
			pTemp		= pData->pTreeItems[pEntry->uParent];
			sArea.right = sArea.left + 1;							// Eine leerer Pixelreihe am Anfang
			ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL);
			sArea.left += iIndent * iLevel + 1;

			for(i = iLevel; i > 0; i--) {							// Bereich vor Schaltflächen
				sArea.right = sArea.left;
				sArea.left -= iIndent;

				ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL);

				iXpos = sArea.left + iShift;

				if(pTemp) {
					if(pTemp->uNextItem) {					// Zeichne vertikale Linien
						MoveToEx(hDc, iXpos, sArea.top | 1, NULL);
						LineTo(hDc, iXpos, sArea.bottom + iAdd);
					}

					pTemp = pData->pTreeItems[pTemp->uParent];
				}
			}

			sArea.left += iIndent * iLevel;
		} else {													// Ohne Linien zeichnen
			if(iLevel > 0) {
				sArea.right = sArea.left + iIndent * iLevel;
				ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL);
				sArea.left += sArea.right;
			}
		}

		if(uStyle & TVS_HASBUTTONS) {							// Fenster mit Schaltflächen ?
			sArea.right = sArea.left + iIndent;
			iXpos		= sArea.left + iShift;
			iYpos		= sArea.top + pData->iRowHeight / 2;

			ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL);

			if(uStyle & TVS_HASLINES) {							// Linien unter Schaltflächen
				MoveToEx(hDc, iXpos, sArea.top | 1, NULL);

				if(pEntry->uNextItem)
					LineTo(hDc, iXpos, sArea.bottom + iAdd);
				else
					LineTo(hDc, iXpos, iYpos + 1);

				MoveToEx(hDc, iXpos + 1 + (iYpos & 1), iYpos, NULL);
				LineTo(hDc, sArea.right      , iYpos);
			}

			if(pEntry->bFlags & TVIX_HASBUTTON) {				// Schaltflächen zeichnen
				sButton.left    = iXpos - 4;
				sButton.top	    = iYpos - 4;
				sButton.right   = iXpos + 5;
				sButton.bottom  = iYpos + 5;

				if(pData->cGlyphOk) {							// Thema benutzen
					uState = ((uBits ^ TVIS_EXPANDED) & (TVIS_EXPANDED | TVIS_EXPANDPARTIAL)) ? GLPS_CLOSED : GLPS_OPENED;
					pDrawThemeBackg(pData->hTheme, hDc, TVP_GLYPH, uState, &sButton, 0);
				} else {
					SetBkColor(hDc, uBtColor);
					ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sButton, NULL, 0, NULL);

					sButton.left   += 1;
					sButton.top	   += 1;
					sButton.right  -= 1;
					sButton.bottom -= 1;

					SetBkColor(hDc, pData->uColors[TVC_BOXBG]);
					ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sButton, NULL, 0, NULL);

					sButton.left    = iXpos - 2;
					sButton.top	    = iYpos  ;
					sButton.right   = iXpos + 3;
					sButton.bottom  = iYpos + 1;

					SetBkColor(hDc, uInColor);
					ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sButton, NULL, 0, NULL);

					// '+' statt '-' Schaltfläsche zeichnenen
					if((uBits ^ TVIS_EXPANDED) & (TVIS_EXPANDED | TVIS_EXPANDPARTIAL)) {
						sButton.left    = iXpos  ;
						sButton.top	    = iYpos - 2;
						sButton.right   = iXpos + 1;
						sButton.bottom  = iYpos + 3;
						ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sButton, NULL, 0, NULL);
					}
				}

				SetBkColor(hDc, uBkColor);
			}

			sArea.left += iIndent;
		} else
			if(uStyle & TVS_HASLINES) {						// Nur Linien zeichnen ohne Schaltflächen
				sArea.right = sArea.left + iIndent;
				ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL);

				iYpos	= sArea.top + pData->iRowHeight / 2;
				iXpos	= sArea.left + iShift;
				MoveToEx(hDc, iXpos, sArea.top | 1, NULL);

				if(pEntry->uNextItem)
					LineTo(hDc, iXpos, sArea.bottom);
				else
					LineTo(hDc, iXpos, iYpos + 1);

				MoveToEx(hDc, iXpos + 1 + (iYpos & 1), iYpos, NULL);
				LineTo(hDc, sArea.right      , iYpos);

				sArea.left += iIndent;
			}

		if(uStyleEx & TVS_EX_FULLROWITEMS)
			sArea.bottom++;

NoRootLines:

		if(uStyleEx & TVS_EX_ITEMLINES) {						// Linien um den Eintrag zeichnen
			iAdd		= 1;
			sArea.right = sArea.left + 1;

			if(uStyleEx & TVS_EX_FULLROWITEMS) {
				iStart = sArea.left;
				iAdd   = 0;
			} else
				if(iLevel >= 0) {
					SetBkColor(hDc, uFrColor);
					ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL);

					sArea.left++;
					sArea.bottom--;
					iStart = sArea.left;
				} else {
					ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL);

					sArea.left++;
					sArea.bottom--;
					iStart = sArea.left - 1;
				}
		} else {
			iAdd = 0;
		}

		SetBkColor(hDc, (uBits & TVIS_BKCOLOR) ? pEntry->uColorBk : uOutColor);
		SelectObject(hDc, (uBits & TVIS_BOLD) ? pData->hFontB : pData->hFontN);

		if(pData->hStates) {									// State-Icons anzeigen
			sArea.right		= sArea.left + pData->iStatesXsize;
			iYpos			= sArea.top + (iHeight - pData->iStatesYsize) / 2;
			i				= (uBits & LVIS_STATEIMAGEMASK) >> 12;

			sArea.right    += iAdd;
			ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL);
			sArea.left     += iAdd;

			if(pData->hStates == THEMEIMGLIST) {				// Mit Thema zeichnen
				if(pData->uStyleEx & TVS_EX_BITCHECKBOX)
					i++;

				if(i >= 1 && i <= 2) {
					uState = (i == 1) ?  CBS_UNCHECKEDNORMAL : CBS_CHECKEDNORMAL;
					pDrawThemeBackg(pData->hThemeBt, hDc, BP_CHECKBOX, uState, &sArea, 0);
				}
			} else {
				ImageList_Draw(pData->hStates, i, hDc, sArea.left, iYpos, ILD_TRANSPARENT);
			}

			sArea.left     += pData->iStatesXsize;
			iAdd		    = 0;
		}

		if(pData->hImages && iImage != TV_NOIMAGE) {			// Icon zeichnen vom Haupteintrag
			if(iImage >= TV_SECONDICON) {						// Sub-Image-Liste verwenden
				if(iImage & TV_NOAUTOEXPAND) {
					iImage     &= ~TV_NOAUTOEXPAND;				// Kein Auto-Expant bei diesem Icon
				} else
					if((pEntry->uState & uAutoMask) && pEntry->uFirstChild) {
						iImage     += 1;							// Auto-Expant aktivieren
					}

				iImage		   -= TV_SECONDICON;
				sArea.right		= sArea.left + pData->iSubImgXsize;
				iYpos			= sArea.top + (iHeight - pData->iSubImgYsize) / 2;
				pEntry->bFlags |= TVIX_HASIMAGE;

				sArea.right    += iAdd;
				ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL);
				sArea.left     += iAdd;
				ImageList_Draw(pData->hSubImg, iImage, hDc, sArea.left, iYpos, ILD_TRANSPARENT | (uBits & (TVIS_OVERLAYMASK | LVIS_CUT)));

				sArea.left     += pData->iSubImgXsize;
				iAdd		    = 0;
			} else {												// Haup-Image-Liste verwenden
				if(iImage & TV_NOAUTOEXPAND) {
					iImage     &= ~TV_NOAUTOEXPAND;				// Kein Auto-Expant bei diesem Icon
				} else
					if((pEntry->uState & uAutoMask) && pEntry->uFirstChild) {
						iImage	   += pData->iAutoAdd;				// Auto-Expant aktivieren
					}

				sArea.right		= sArea.left + pData->iImagesXsize;
				iYpos			= sArea.top + (iHeight - pData->iImagesYsize) / 2;
				pEntry->bFlags |= TVIX_HASIMAGE;

				sArea.right    += iAdd;
				ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL);
				sArea.left     += iAdd;
				ImageList_Draw(pData->hImages, iImage, hDc, sArea.left, iYpos, ILD_TRANSPARENT | (uBits & (TVIS_OVERLAYMASK | LVIS_CUT)));

				sArea.left     += pData->iImagesXsize;
				iAdd		    = 0;
			}
		} else {
			pEntry->bFlags &= ~TVIX_HASIMAGE;
		}

		sArea.right = uFirstPos;								// Text ausgeben vom Haupteintrag
		iYpos		= sArea.top + (iHeight - pData->iFontHeight) / 2;

		if(uBits & (TVIS_SELECTED | TVIS_DROPHILITED | TVIS_UNDERLINE | TVIS_TRACKED | TVIS_TEXTCOLOR | TVIS_FOCUSED)) {
			// Das Feld speziel zeichnen
			TCHAR	*pPtr = (TCHAR *)new(TCHAR, uTextSize + 4);
			INT		*pPos = (INT *)new(INT, uTextSize + 4);

			ExtTextOut(hDc, 0, 0, ETO_OPAQUE | ETO_CLIPPED, &sArea, NULL, 0, NULL);

			sButton.top		= iYpos;
			sButton.left    = sArea.left + 4;
			sButton.right	= sArea.right;
			sButton.bottom	= iYpos + pData->iFontHeight + 2;

			if(!uTextSize) {									// Feld ohne Text ?
				sButton.right -= 2;
				sButton.bottom--;
				pEntry->iTextPixels = 0;
			} else {
				if(pData->uStyleEx & TVS_EX_FULLROWMARK)
					if(pData->cHasFocus == 0 || uItem != pData->uSelectedItem || pData->uSelectedSub)
						if((uBits & TVIS_SELECTED) && !(uBits & (TVIS_DROPHILITED | TVIS_FOCUSED))) {
							sButton.left -= 1;
						}

				DrawText(hDc, pText, uTextSize, &sButton, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX | DT_CALCRECT);
				pEntry->iTextPixels = sButton.right - sButton.left;
			}

			// Passt der Text in die Spalte
			if(sButton.left + pEntry->iTextPixels >= (int)(sArea.right - pData->uScrollX)) {
				iSize  = sArea.right - pData->uScrollX - sButton.left - 2;
				iSize -= (uBits & TVIS_BOLD) ? pData->uTrippleB : pData->uTrippleN;
				if(iSize < 3) {
					iCount = 0;
				} else {
					GetTextExtentExPoint(hDc, pText, uTextSize, iSize, &iCount, pPos, &sSize);
				}
				// Temporären Text mit "..." erzeugen
				memcpy(pPtr       , pText, iCount * sizeof(TCHAR));
				memcpy(pPtr + iCount, _T("..."), 4 * sizeof(TCHAR));

				pText		  = pPtr;
				uTextSize	  = iCount + 3;
				sButton.right = sArea.right - 2;
			}

			// Das Feld selektiert zeichnen
			if((uBits & TVIS_SELECTED) && pData->cHasFocus && uItem == pData->uSelectedItem && !pData->uSelectedSub) {
				uTempColor = GetSysColor(COLOR_HIGHLIGHTTEXT);
				SelectObject(hDc, GetSysColorBrush(COLOR_HIGHLIGHT));
				Rectangle(hDc, sButton.left - 2, sButton.top - 1, sButton.right + 2, sButton.bottom + 1);
			} else {
				if(uBits & TVIS_DROPHILITED) {
					uTempColor =     GetSysColor(COLOR_HIGHLIGHTTEXT);
					SelectObject(hDc, GetSysColorBrush(COLOR_HIGHLIGHT));
					SelectObject(hDc, GetStockObject(NULL_PEN));
					Rectangle(hDc, sButton.left - 2, sButton.top - 1, sButton.right + 2, sButton.bottom + 1);
				} else
					if(uBits & TVIS_SELECTED) {				// Ist das Feld ohne Focus ausgewählt
						if(pData->cHasFocus) {
							uTempColor =     GetSysColor(COLOR_HIGHLIGHTTEXT);
							SelectObject(hDc, GetSysColorBrush(COLOR_HIGHLIGHT));
							SelectObject(hDc, GetStockObject(NULL_PEN));
						} else {
							if(uBits & TVIS_TEXTCOLOR)
								uTempColor = pEntry->uColorText;
							else
								uTempColor = pData ->uColors[TVC_TEXT];

							if(pData->uStyleEx & TVS_EX_FULLROWMARK)
								SelectObject(hDc, GetStockObject(NULL_BRUSH));
							else
								SelectObject(hDc, GetSysColorBrush(COLOR_3DFACE));

							SelectObject(hDc, GetStockObject(NULL_PEN));
						}

						Rectangle(hDc, sButton.left - 2, sButton.top - 1, sButton.right + 2, sButton.bottom + 1);
					} else {											// Das Feld normal zeichnen
						if(uBits & TVIS_TRACKED)
							uTempColor = pData ->uColors[TVC_TRACK];
						else
							if(uBits & TVIS_TEXTCOLOR)
								uTempColor = pEntry->uColorText;
							else
								uTempColor = pData ->uColors[TVC_TEXT ];
						sButton.right--;
						sButton.left --;
					}

				SelectObject(hDc, hPatternPen);

				if(uBits & TVIS_FOCUSED) {						// Einen punktierten Rahmen um den Text zeichnen
					SelectObject(hDc, GetStockObject(NULL_BRUSH));
					Rectangle(hDc, sButton.left - 2, sButton.top - 1, sButton.right + 2, sButton.bottom + 1);
				}
			}

			SetTextColor(hDc, uTempColor);
			sButton.left += pData->iFontOff;
			DrawText(hDc, pText, uTextSize, &sButton, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX);

			if(uBits & (TVIS_UNDERLINE | TVIS_TRACKED))				// Text unterstreichen
				if(pText && *pText) {
					sButton.left   -= pData->iFontOff;
					sButton.right  -= pData->iFontOff + 1;
					sButton.top	   += pData->iFontLine;
					sButton.bottom  = sButton.top + 1;
					uOldColor = SetBkColor(hDc, uTempColor);
					ExtTextOut(hDc, 0, 0, ETO_OPAQUE | ETO_CLIPPED, &sButton, NULL, 0, NULL);
					SetBkColor(hDc, uOldColor);
				}

			SetTextColor(hDc, pData->uColors[TVC_TEXT]);

            delete(pPos);
            delete(pPtr);
		} else {													// Das Feld normal ausgeben
			if(!pEntry->iTextPixels && uTextSize) {
				sButton.top		= iYpos;
				sButton.left    = sArea.left + 4;
				sButton.right	= sArea.right;
				sButton.bottom	= iYpos + pData->iFontHeight + 2;
				// Textbreite berechen
				DrawText(hDc, pText, uTextSize, &sButton, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX | DT_CALCRECT);

				pEntry->iTextPixels = sButton.right - sButton.left;
			}

			// Ist der Text größer als die Spalte
			if(sArea.left + pEntry->iTextPixels >= (int)(sArea.right - pData->uScrollX)) {
				INT	*pPos = (INT *)new(INT, uTextSize);

				iSize  = sArea.right - sArea.left - pData->uScrollX;
				iSize -= (uBits & TVIS_BOLD) ? pData->uTrippleB : pData->uTrippleN;
				if(iSize < 3) {
					iCount = 0;
				} else {
					GetTextExtentExPoint(hDc, pText, uTextSize, iSize, &iCount, pPos, &sSize);
				}

				if(iCount > 0) {								// Passen noch Buchstaben vor "..."
					sButton.right	= sArea.right;
					sArea.right		= sArea.left + 2 + pPos[iCount - 1];

					ExtTextOut(hDc, sArea.left + 2, iYpos, ETO_OPAQUE | ETO_CLIPPED, &sArea, pText, iCount, NULL);

					sArea.left		= sArea.right;
					sArea.right		= sButton.right;

					ExtTextOut(hDc, sArea.left  , iYpos, ETO_OPAQUE | ETO_CLIPPED, &sArea, _T("..."), 3, NULL);
				} else {
					ExtTextOut(hDc, sArea.left + 2, iYpos, ETO_OPAQUE | ETO_CLIPPED, &sArea, _T("..."), 3, NULL);
				}

                delete(pPos);
			} else {
				ExtTextOut(hDc, sArea.left + 2, iYpos, ETO_OPAQUE | ETO_CLIPPED, &sArea, pText, uTextSize, NULL);
			}
		}

		i  = sArea.left - iXscroll;
		i += pEntry->iTextPixels + 5;
		if(i > iMaxX)
			iMaxX = i;

		if(uStyleEx & TVS_EX_ITEMLINES) {						// Linien um den Eintrag zeichnen
			SetBkColor(hDc, uFrColor);

			if(iLast > iStart) {								// Ist das Feld nach links eingerückt gegenüber dem oberen
				sArea.top--;
				sArea.bottom = sArea.top + 1;
				sArea.left	 = iStart - 1;
				ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL);
				sArea.top++;
			}

			iLast		  = iStart;								// Linie unter Feld zeichnen
			sArea.top    += iHeight;
			sArea.left	  = iStart;
			sArea.bottom  = sArea.top + 1;

			ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL);

			sArea.top    -= iHeight;
		}

		//************ Extraeinträge zeichnen *********************************

ExtraDraw:

		uNextColor		 = uOutColor;							// Hintergrundfarbe für die nächste Spalte

		if(pData->aColumn[0].bMark) {							// Ist die erste Spalte markiert
			uBkColor = pData->uColors[TVC_BK];
		}

		for(uColumn = 1; uColumn <= pData->uColumnCount; uColumn++) {	// Extra Spalten zeichnen
			uExtra = pData->aColumnPos[uColumn  ];
			uNext  = pData->aColumnPos[uColumn + 1];

			if(pData->aColumn[uExtra].sReal == 0)					// Ist die Spalte sichtbar
				if(uColumn < pData->uColumnCount) {
					continue;
				}

			if(uColMark != pData->aColumn[uExtra].bMark)			// Ist die Spalte anderst markiert
				if(!(pEntry->uState & TVIS_SELECTED) || !(uStyleEx & TVS_EX_FULLROWMARK)) {
					if(uPos & 1) {
						uColMark  =  pData->aColumn[uExtra].bMark;
						uOutColor = (uColMark) ? uOcColor : uOdColor;
					} else {
						uColMark  =  pData->aColumn[uExtra].bMark;
						uOutColor = (uColMark) ? uEcColor : uEvColor;
					}
				}

			GetRgnBox(aRgn[uExtra], &sButton);

			if(iRnType[uExtra] == NULLREGION)
				continue;

			SelectObject(hDc, aRgn[uExtra]);

			sArea.left  = pData->aColumnXpos[uExtra];
			sArea.left += iXscroll;

			if(uStyleEx & TVS_EX_ITEMLINES) {					// Linie um den Eintrag zeichnen
				SetBkColor(hDc, uFrColor);
				// Linke Linie
				sArea.right   = sArea.left + 1;
				sArea.bottom += 1;

				ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL);

				sArea.left   += 1;								// Untere Linie
				sArea.top    += iHeight;
				sArea.bottom  = sArea.top + 1;
				sArea.right	  = pData->aColumnXpos[uNext];
				if(uColumn < pData->uColumnCount)
					sArea.right += iXscroll;

				ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL);

				sArea.top    -= iHeight;
				sArea.bottom -= 1;
				iAdd		  = 1;
			}

			if(sArea.left > (int)pData->uSizeX)
				break;				// Noch im sichtbaren Bereich

			sArea.right		 = pData->aColumnXpos[uNext];

			if(uColumn < pData->uColumnCount) {					// Ist es die letze Spalte ?
				sArea.right += iXscroll;
				pExtra       = pData->pExtraItems[uExtra - 1][uItem];
				if(!pExtra)
					uNextColor = uOutColor;
			} else {
				pExtra		 = 0;
			}

			if(!pExtra) {										// Leeres Feld zeichnen
				SetBkColor(hDc, uNextColor);
				ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL);
			} else {
				iSize		 =  pData->iSubImgXsize;
				hImgList	 =  pData->hSubImg;
				iImage		 =  pExtra->iImage;
				pText		 =  pExtra->pText;
				uTextSize	 =  pExtra->uTextSize;
				uBits		 = (pExtra->uState & 0xFFFF);
				uBits	    |= (pExtra->bFlags << 16);
				uBits	    |=  pEntry->uState & TVIS_BASEFLAGS;
				uBits	    &=  uMark;

				if(uExtra != pData->uSelectedSub) {
					uBits &= ~TVIS_SELECTED;
				}

				if(pExtra->bCallback) {						// Text über Callback holen
					CallbackExtra(pData, pEntry, pExtra, uItem, uExtra, pExtra->bCallback, &iImage, &uTextSize, &pText);
					pExtra = pData->pExtraItems[uExtra - 1][uItem];
					if(!pExtra)
						break;
				}

				uNextColor   = (uBits & TVIS_BKCOLOR) ? pExtra->uColorBk : uOutColor;
				SetBkColor(hDc, uNextColor);

				if(pData->aColumn[uExtra].bEdit >= TVAX_CHECK) {	// Checkboxen statt Icons
					hImgList =  pData->hChecks;
					iSize	 =  pData->iChecksXsize;
					iImage   = (pExtra->uState & TVIS_STATEIMAGEMASK) >> 12;
					uBits   &= ~TVIS_OVERLAYMASK;

					if(iImage & 8)
						if(pData->aColumn[uExtra].bFlags & TVAE_STATEENABLE) {
							iImage &= 7;
						}
				}

				if(hImgList && iImage > TV_NOIMAGE) {			// Icon zeichnen
					sArea.right     = sArea.left + iSize + 2;
					iYpos		    = sArea.top + (iHeight - iSize) / 2;
					pExtra->bFlags |= TVIX_HASIMAGE;

					SelectObject(hDc, (uBits & TVIS_BOLD) ? pData->hFontB : pData->hFontN);
					ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL);

					if(hImgList == THEMEIMGLIST) {				// Mit Thema zeichnen
						if(pData->uStyleEx & TVS_EX_BITCHECKBOX)
							iImage++;

						if(iImage == 1 || iImage == 2) {
							uState = (iImage == 1) ?  CBS_UNCHECKEDNORMAL : CBS_CHECKEDNORMAL;
							pDrawThemeBackg(pData->hThemeBt, hDc, BP_CHECKBOX, uState, &sArea, 0);
						}
					} else {
						ImageList_Draw(hImgList, iImage, hDc, sArea.left + 1, iYpos, ILD_TRANSPARENT | (uBits & (TVIS_OVERLAYMASK | LVIS_CUT)));
					}

					sArea.left  += iSize + 1;
					sArea.right  = pData->aColumnXpos[uNext];
					sArea.right += iXscroll;
				} else {
					pExtra->bFlags &= ~TVIX_HASIMAGE;
				}

				iYpos = sArea.top + (iHeight - pData->iFontHeight) / 2;
				SelectObject(hDc, (uBits & TVIS_BOLD) ? pData->hFontB : pData->hFontN);

				if(uBits & (TVIS_SELECTED | TVIS_DROPHILITED | TVIS_UNDERLINE | TVIS_TRACKED | TVIS_TEXTCOLOR | TVIS_FOCUSED)) {
					// Text ausgeben in spezilem Format
					TCHAR	*pPtr = (TCHAR *)new(TCHAR, uTextSize + 4);
					INT		*pPos = (INT *)new(INT, uTextSize + 4);

					ExtTextOut(hDc, 0, 0, ETO_OPAQUE | ETO_CLIPPED, &sArea, NULL, 0, NULL);

					sButton.top		= iYpos;
					sButton.left    = sArea.left + 4;
					sButton.right	= sArea.right;
					sButton.bottom	= iYpos + pData->iFontHeight + 2;

					if(!uTextSize) {
						sButton.left--;
						sButton.right -= 2;
						sButton.bottom--;
						pExtra->iTextPixels = 0;
					} else {
						if(pData->uStyleEx & TVS_EX_FULLROWMARK)
							if(pData->cHasFocus == 0 || uItem != pData->uSelectedItem)
								if((uBits & TVIS_SELECTED) && !(uBits & (TVIS_DROPHILITED | TVIS_FOCUSED))) {
									sButton.left -= 2;
								}

						DrawText(hDc, pText, uTextSize, &sButton, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX | DT_CALCRECT);
						pExtra->iTextPixels = sButton.right - sButton.left;
					}

					// Passt der Text in die Spalte
					if(sButton.left + pExtra->iTextPixels >= sArea.right) {
						if(uTextSize > 253)
							uTextSize = 253;

						iSize  = sArea.right - sButton.left - 2;
						iSize -= (uBits & TVIS_BOLD) ? pData->uTrippleB : pData->uTrippleN;
						if(iSize < 3) {
							iCount = 0;
						} else {
							GetTextExtentExPoint(hDc, pText, uTextSize, iSize, &iCount, pPos, &sSize);
						}

						memcpy(pPtr       , pText, iCount * sizeof(TCHAR));
						memcpy(pPtr + iCount, _T("..."), 4 * sizeof(TCHAR));

						pText		  = pPtr;
						uTextSize	  = iCount + 3;
						sButton.right = sArea.right - 2;
					}

					switch(pData->aColumn[uExtra].bAlign) {	// Textausrichtung ausgleichen
						case DT_CENTER:
							iDelta	= sArea  .right - sArea  .left;
							iDelta -= sButton.right - sButton.left;
							iDelta -= 6;
							iDelta /= 2;
							sButton.right += iDelta;
							sButton.left  += iDelta;
							break;

						case DT_RIGHT:
							iDelta	= sArea  .right - sArea  .left;
							iDelta -= sButton.right - sButton.left;
							iDelta -= 6;
							sButton.right += iDelta;
							sButton.left  += iDelta;
							break;

					}
					// Ist das Feld ohne Focus ausgewählt
					if((uBits & TVIS_SELECTED) && pData->cHasFocus && uItem == pData->uSelectedItem) {
						uTempColor = GetSysColor(COLOR_HIGHLIGHTTEXT);
						SelectObject(hDc, GetSysColorBrush(COLOR_HIGHLIGHT));
						Rectangle(hDc, sButton.left - 2, sButton.top - 1, sButton.right + 2, sButton.bottom + 1);
					} else {
						if(uBits & TVIS_DROPHILITED) {
							uTempColor =     GetSysColor(COLOR_HIGHLIGHTTEXT);
							SelectObject(hDc, GetSysColorBrush(COLOR_HIGHLIGHT));
							SelectObject(hDc, GetStockObject(NULL_PEN));
							Rectangle(hDc, sButton.left - 2, sButton.top - 1, sButton.right + 2, sButton.bottom + 1);
						} else
							if(uBits & TVIS_SELECTED && uItem == pData->uSelectedItem) {
								if(uBits & TVIS_TEXTCOLOR)
									uTempColor = pEntry->uColorText;
								else
									uTempColor = pData->uColors[TVC_TEXT];

								if(pData->uStyleEx & TVS_EX_FULLROWMARK)
									SelectObject(hDc, GetStockObject(NULL_BRUSH));
								else
									SelectObject(hDc, GetSysColorBrush(COLOR_3DFACE));

								SelectObject(hDc, GetStockObject(NULL_PEN));
								Rectangle(hDc, sButton.left - 2, sButton.top - 1, sButton.right + 2, sButton.bottom + 1);
							} else {
								if(uBits & TVIS_TRACKED)
									uTempColor = pData ->uColors[TVC_TRACK];
								else
									if(uBits & TVIS_TEXTCOLOR)
										uTempColor = pExtra->uColorText;
									else
										uTempColor = pData ->uColors[TVC_TEXT ];
								sButton.right--;
								sButton.left --;
							}

						SelectObject(hDc, hPatternPen);

						if(uBits & TVIS_FOCUSED) {				// Punktierten Rahmen um Text zeichnen
							SelectObject(hDc, GetStockObject(NULL_BRUSH));
							Rectangle(hDc, sButton.left - 2, sButton.top - 1, sButton.right + 2, sButton.bottom + 1);
						}
					}

					SetTextColor(hDc, uTempColor);
					sButton.left += pData->iFontOff;

					DrawText(hDc, pText, uTextSize, &sButton, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX);

					if(uBits & (TVIS_UNDERLINE | TVIS_TRACKED))		// Text unterstreichen
						if(pText && *pText) {
							sButton.left   -= pData->iFontOff;
							sButton.right  -= pData->iFontOff + 1;
							sButton.top	   += pData->iFontLine;
							sButton.bottom  = sButton.top + 1;
							uOldColor = SetBkColor(hDc, uTempColor);
							ExtTextOut(hDc, 0, 0, ETO_OPAQUE | ETO_CLIPPED, &sButton, NULL, 0, NULL);
							SetBkColor(hDc, uOldColor);
						}

					SetTextColor(hDc, pData->uColors[TVC_TEXT]);

                    delete(pPos);
                    delete(pPtr);
				} else {											// Den Text ganz normal ausgeben
					if(!pExtra->iTextPixels) {
						sButton.top		= iYpos;
						sButton.left    = sArea.left + 4;
						sButton.right	= sArea.right;
						sButton.bottom	= iYpos + pData->iFontHeight + 2;

						if(uTextSize) {						// Textbreite berechen
							DrawText(hDc, pText, uTextSize, &sButton, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX | DT_CALCRECT);
							pExtra->iTextPixels = sButton.right - sButton.left;
						} else {
							pExtra->iTextPixels = 0;
						}
					}
					// Ist der Text größer als die Spalte
					if(sArea.left + pExtra->iTextPixels >= sArea.right) {
						INT	*pPos = (INT *)new(INT, uTextSize);

						iSize  = sArea.right - sArea.left;
						iSize -= (uBits & TVIS_BOLD) ? pData->uTrippleB : pData->uTrippleN;
						if(iSize < 3) {
							iCount = 0;
						} else {
							GetTextExtentExPoint(hDc, pText, uTextSize, iSize, &iCount, pPos, &sSize);
						}

						if(iCount > 0) {						// Passen noch Buchstaben vor "..."
							sButton.right	= sArea.right;
							sArea.right		= sArea.left + 2 + pPos[iCount - 1];

							ExtTextOut(hDc, sArea.left + 2, iYpos, ETO_OPAQUE | ETO_CLIPPED, &sArea, pText, iCount, NULL);

							sArea.left		= sArea.right;
							sArea.right		= sButton.right;

							ExtTextOut(hDc, sArea.left  , iYpos, ETO_OPAQUE | ETO_CLIPPED, &sArea, _T("..."), 3, NULL);
						} else {
							ExtTextOut(hDc, sArea.left + 2, iYpos, ETO_OPAQUE | ETO_CLIPPED, &sArea, _T("..."), 3, NULL);
						}

                        delete(pPos);
					} else {
						switch(pData->aColumn[uExtra].bAlign) {	// Textausrichtung
							case DT_CENTER:
								SetTextAlign(hDc, TA_CENTER | TA_TOP);
								ExtTextOut(hDc, (sArea.right + sArea.left) / 2, iYpos, ETO_OPAQUE | ETO_CLIPPED, &sArea, pText, uTextSize, NULL);
								SetTextAlign(hDc, TA_LEFT | TA_TOP);
								break;

							case DT_RIGHT:
								SetTextAlign(hDc, TA_RIGHT | TA_TOP);
								ExtTextOut(hDc, sArea.right - 2, iYpos, ETO_OPAQUE | ETO_CLIPPED, &sArea, pText, uTextSize, NULL);
								SetTextAlign(hDc, TA_LEFT | TA_TOP);
								break;

							default
									:
								ExtTextOut(hDc, sArea.left + 2, iYpos, ETO_OPAQUE | ETO_CLIPPED, &sArea, pText, uTextSize, NULL);
								break;
						}
					}
				}
			}
		}

		sArea.top += pData->iRowHeight;
	}

	if(sArea.top < sRect.bottom) {								// Untere Fläche ohne Einträge füllen
		SelectObject(hDc, hRgnMain);

		// Gibt es markierte Spalten
		if(pData->uMarkedCols > 0 && (pData->cIsEnabled || !(pData->uStyleEx & TVS_EX_GRAYEDDISABLE))) {
			sRect.right = 0 - pData->uScrollX;

			for(uColumn = 0; uColumn < pData->uColumnCount; uColumn++) {
				uExtra		 = pData->aColumnPos[uColumn  ];
				uNext		 = pData->aColumnPos[uColumn + 1];
				uMark		 = pData->aColumn[uExtra].bMark;
				sRect.top    = sArea.top;
				sRect.left   = sRect.right;
				sRect.right  = pData->aColumnXpos[uNext] + uMark;
				sRect.right -= pData->uScrollX;
				if(sRect.right == sRect.left + 1)
					uMark = 0;
				SetBkColor(hDc, (uMark) ? pData->uColors[TVC_COLBK] : uBkColor);
				ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sRect, NULL, 0, NULL);
			}

			sRect.top   = sArea.top;
			sRect.left  = sRect.right;
			sRect.right = pData->uSizeX;
			SetBkColor(hDc, uBkColor);
			ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sRect, NULL, 0, NULL);
		} else {													// Keine markierten Spalten
			sRect.top	= sArea.top;
			SetBkColor(hDc, uBkColor);
			ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sRect, NULL, 0, NULL);
		}
	}

	if(pData->iMaxSizeX != iMaxX) {								// X-Scrollbar aktuallisieren
		pData->iMaxSizeX = iMaxX;
		if(pData->uColumnCount == 0)
			if(pData->iMaxSizeX != (int)pData->uOldXCount) {
				UpdateScrollX(pData);
			}
	}

	UNLOCK(pData);

	DeleteObject(hRgnMain);

	for(uPos = 0; uPos < uRgnCount; uPos++) {
		DeleteObject(aRgn[uPos]);
	}

}
