https://git.reactos.org/?p=reactos.git;a=commitdiff;h=2fdfb411b69932e765ed8b...
commit 2fdfb411b69932e765ed8b64646cae76f3e3193f Author: Hermès Bélusca-Maïto hermes.belusca-maito@reactos.org AuthorDate: Sun Jan 28 22:10:51 2018 +0100 Commit: Hermès Bélusca-Maïto hermes.belusca-maito@reactos.org CommitDate: Tue Nov 6 00:09:14 2018 +0100
[REACTOS] Add a TreeList control (TreeView with columns).
Add the TreeList control from Anton Zechner and Sébastien Kirche from https://github.com/sebkirche/treelist (under GPL-3.0+) as a *TEMPORARY* solution until a better-suited control for ReactOS is developed.
- Compilation fixes for the TreeList control. --- base/setup/reactos/CMakeLists.txt | 1 + base/setup/reactos/treelist.c | 13733 ++++++++++++++++++++++++++++++++++++ base/setup/reactos/treelist.h | 493 ++ 3 files changed, 14227 insertions(+)
diff --git a/base/setup/reactos/CMakeLists.txt b/base/setup/reactos/CMakeLists.txt index 39225e758e..8c58f9e360 100644 --- a/base/setup/reactos/CMakeLists.txt +++ b/base/setup/reactos/CMakeLists.txt @@ -11,6 +11,7 @@ list(APPEND SOURCE spapisup/infsupp.c drivepage.c reactos.c + treelist.c reactos.h)
file(GLOB reactos_rc_deps res/*.*) diff --git a/base/setup/reactos/treelist.c b/base/setup/reactos/treelist.c new file mode 100644 index 0000000000..1681b6189a --- /dev/null +++ b/base/setup/reactos/treelist.c @@ -0,0 +1,13733 @@ +/* + * 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)(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)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)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)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)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)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)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)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)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)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)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)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)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)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)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)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)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)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)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)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_EXPANDPARTIAL | 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 ... 9270 lines suppressed ...