diff --git a/base/setup/reactos/CMakeLists.txt b/base/setup/reactos/CMakeLists.txt index 39225e758ee..8c58f9e360e 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 00000000000..1681b6189a2 --- /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 + #include + #include + #include + #include +#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<>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 +// pData : Zeiger auf die Fensterdaten +// pItem : Zeiger auf die den Datenspeicher +// Ergibt 1 wenn ok oder 0 bei einem Fehler +static unsigned TreeListGetItem(TreeListData *pData, TV_ITEM *pItem) { + + ExtraItem **pList; + ExtraItem *pExtra; + BaseItem *pEntry; + unsigned uBits; + unsigned uItem; + unsigned uSub; + unsigned uLen; + + uItem = U(pItem->hItem); + if(uItem > pData->uTreeItemsMax) + return 0; + + pEntry = pData->pTreeItems[uItem]; + if(!pEntry) + return 0; + + uBits = pItem->mask; + + if(uBits & TVIF_SUBITEM) { // Einen Extraeintrag abfragen + uSub = pItem->cChildren; + if(uSub > 0) { + if(uSub >= pData->uColumnCount) + return 0; + pList = pData->pExtraItems[uSub - 1]; + pExtra = pList[uItem]; + + if(!pExtra) { // Einen neuen Eintrag erzeugen + pExtra = new(ExtraItem, 1); + memset(pExtra, 0, sizeof(ExtraItem)); + pExtra->iImage = TV_NOIMAGE; + pExtra->uState = pEntry->uState & (TVIS_BOLD | TVIS_UNDERLINE); + pList[uItem] = pExtra; + } + + if(uBits & TVIF_PARAM) { + pItem->lParam = pEntry->lParam; + } + + if(uBits & TVIF_IMAGE) { + pItem->iImage = pExtra->iImage; + } + + if(uBits & TVIF_TEXT) { // Einen neuen Text einstellen + if(pExtra->pText == LPSTR_TEXTCALLBACK) { + pItem->pszText = LPSTR_TEXTCALLBACK; + } else + if(uBits & TVIF_TEXTPTR) { + if(!pExtra->pText) { + pItem->pszText = _T(""); + pItem->cchTextMax = 0; + } else { + pItem->pszText = pExtra->pText; + pItem->cchTextMax = pExtra->uTextSize + 1; + } + } else { + if(pExtra->pText) { + uLen = pExtra->uTextSize + 1; + if(pItem->cchTextMax < (int)uLen) { + if(pItem->cchTextMax <= 0) { + uLen = 0; + } else { + uLen = pItem->cchTextMax - 1; + pItem->pszText[uLen] = 0; + } + } + + memcpy(pItem->pszText, pExtra->pText, uLen * sizeof(TCHAR)); + } else { + if(pItem->cchTextMax > 0) { + pItem->pszText[0] = 0; + } + } + } + } + + if(uBits & TVIF_STATE) { + pItem->state = pExtra->uState&~TVIS_BASEFLAGS; + pItem->state &= pItem->stateMask; + } + + return 1; + } + + if(pEntry->bCallback & TVIF_CHILDREN) + pItem->cChildren = I_CHILDRENCALLBACK; + else + pItem->cChildren = (pEntry->uFirstChild) ? 1 : 0; + + uBits &= ~TVIF_CHILDREN; + } + + + //******************** Einen Basis Eintrag ändern ***************************** + if(uBits & TVIF_PARAM) { + pItem->lParam = pEntry->lParam; + } + + if(uBits & TVIF_IMAGE) { + pItem->iImage = pEntry->iImage; + } + + if(uBits & TVIF_SELECTEDIMAGE) { + pItem->iSelectedImage = pEntry->iSelectedImage; + } + + if(uBits & TVIF_CHILDREN) { + if(pEntry->bCallback & TVIF_CHILDREN) + pItem->cChildren = I_CHILDRENCALLBACK; + else + pItem->cChildren = (pEntry->uFirstChild) ? 1 : 0; + } + + if(uBits & TVIF_TEXT) { // Einen neuen Text einstellen + if(pEntry->pText == LPSTR_TEXTCALLBACK) { + pItem->pszText = LPSTR_TEXTCALLBACK; + } else + if(uBits & TVIF_TEXTPTR) { + pItem->pszText = pEntry->pText; + pItem->cchTextMax = pEntry->uTextSize + 1; + } else { + uLen = pEntry->uTextSize + 1; + if(pItem->cchTextMax < (int)uLen) { + if(pItem->cchTextMax <= 0) { + uLen = 0; + } else { + uLen = pItem->cchTextMax - 1; + pItem->pszText[uLen] = 0; + } + } + + memcpy(pItem->pszText, pEntry->pText, uLen * sizeof(TCHAR)); + } + } + + if(uBits & TVIF_STATE) { + pItem->state = pEntry->uState; + pItem->state &= pItem->stateMask; + } + + return 1; +} + +//***************************************************************************** +//* +//* TreeListDeleteColumn +//* +//***************************************************************************** +// Löscht eine Spalte aus dem Header +// pData : Zeiger auf die Fensterdaten +// uCol : Ist die Nummer der Spalte die gelöscht werden soll +// Ergibt 1 wenn die Spalte gelöscht wurde +static int TreeListDeleteColumn(TreeListData *pData, unsigned uCol) { + + ExtraItem **pList; + ExtraItem *pExtra; + RECT sRect; + BYTE bItem; + BYTE bByte; + unsigned uPos; + unsigned uSub; + unsigned uItem; + unsigned uIndex; + int iDelta; + int iXoff; + int iNum; + int iCnt; + int iVar; + int iSub; + int iAll; + int iFix; + + if(uCol >= pData->uColumnCount) + return 0; + + if(uCol && uCol == pData->uSelectedSub) { // Ist die Auswahl in der Spalte + TreeListSelectItem(pData, pData->uSelectedItem, 0, TVC_UNKNOWN); + } + + if(uCol && uCol == pData->uEditSub) { + pData->uEditSub = 0; + pData->uEditItem = 0; + TreeListEndLabelEdit(pData, 0); + } + + if(uCol && uCol == pData->uFocusSub) { + pData->uFocusSub = 0; + pData->uFocusItem = 0; + } + + if(uCol == pData->uTrackedSub) { + pData->uTrackedSub = 0; + pData->uTrackedItem = 0; + } + + GetClientRect(pData->hWnd, &sRect); + + iDelta = pData->aColumn[uCol].sSize; + iSub = pData->aColumn[uCol].bWeight; + iCnt = 0; + iVar = 0; + iFix = 0; + iAll = 0; + + for(uPos = 0; uPos < pData->uColumnCount; uPos++) { // Zählern der variablen Spalten + if(uPos == uCol) + continue; + if(pData->aColumn[uPos].bWeight == 0) { + iFix += pData->aColumn[uPos].sSize; + continue; + } + + iVar += pData->aColumn[uPos].sSize; + iAll += pData->aColumn[uPos].bWeight; + iCnt += 1; + } + + Header_DeleteItem(pData->hHeader, uCol); + pData->uColumnCount--; + + if(pData->uColumnCount > 0) { // Liste mit Extraeinträgen löschen + iNum = uCol - 1; + if(iNum < 0) + iNum = 0; + + pList = pData->pExtraItems[iNum]; + if(pList) { + for(uItem = 0; uItem <= pData->uTreeItemsMax; uItem++) { // Alle Einträge aus der Liste löschen + pExtra = pList[uItem]; + if(!pExtra) + continue; + + if(pExtra->pText) { + pExtra->uTextSize = 0; + delete(pExtra->pText); + } + + delete(pExtra); + } + + memmove(pData->pExtraItems + iNum, pData->pExtraItems + iNum + 1, sizeof(pList) * (MAX_COLUMNS - 1 - iNum)); + pData->pExtraItems[pData->uColumnCount] = NULL; + delete(pList); + } + } else { + iNum = MAX_COLUMNS; + } + + if(pData->aColumn[uCol].bWeight) { + pData->uColumnCountVar--; + } + + if(pData->aColumn[uCol].bMark) { + pData->uMarkedCols--; + } + + uSub = pData->aColumnPos[uCol]; + + memmove(pData->aColumn + uCol, pData->aColumn + uCol + 1, (MAX_COLUMNS - 1 - uCol)*sizeof(ColumnData)); + + for(uIndex = 0; uIndex < uSub; uIndex++) { // Zuordnungs-Array anpassen + bItem = pData->aColumnPos[uIndex - 1]; + if(bItem < uCol) + continue; + + bItem++; + pData->aColumnPos[uIndex] = bItem; + } + + for(; uIndex <= pData->uColumnCount; uIndex++) { // Spaltenpositionen verschieben + bItem = pData->aColumnPos[uIndex + 1]; + + if(bItem >= uCol) { + uCol--; + } + + pData->aColumnPos[uIndex] = bItem; + } + + for(uIndex = pData->uColumnCount; uIndex > 0;) { + uIndex--; + bByte = pData->aColumn[uIndex].bIndex; + + if(bByte >= uSub) { + bByte--; + pData->aColumn[uIndex].bIndex = bByte; + } + + pData->aColumn[uIndex].bNext = pData->aColumnPos[bByte + 1]; + } + + pData->iFixSize = iFix; + pData->iAllWeight = iAll; + pData->aColumn[pData->uColumnCount].bWeight = 0; + + if(iCnt && iDelta) { // Variable Breiten anpassen + ChangeColSize(pData, iDelta); + } else { + if(iSub && !iCnt) { + pData->iVarSize = 0; + } + } + + if(pData->uSelectedSub > uCol) { // Ist die Auswahl vor der Spalte + pData->uSelectedSub--; + } + + if(pData->uEditSub > uCol) { + pData->uEditSub--; + } + + if(pData->uFocusSub > uCol) { + pData->uFocusSub--; + } + + if(pData->uTrackedSub > uCol) { + pData->uTrackedSub--; + } + + if(!pData->uColumnCount) { // Den Header löschen + DestroyWindow(pData->hHeader); + pData->hHeader = NULL; + pData->uStartPixel = 0; + pData->iRowHeight = 1; + UpdateHeight(pData); + InvalidateRect(pData->hWnd, &sRect, FALSE); + } + + iXoff = UpdateColumns(pData); // Hat sich die Spaltenbreiten verändert + if(iXoff < 0x10000) { + sRect.left = iXoff; + sRect.left -= pData->uScrollX; + sRect.top = pData->uStartPixel; + InvalidateRect(pData->hWnd, &sRect, FALSE); + } + + UpdateScrollX(pData); + + return 1; +} + + +//***************************************************************************** +//* +//* TreeListInsertColumn +//* +//***************************************************************************** +// Adds a new column in the header +// pData : Zeiger auf die Fensterdaten +// uCol : Ist die Nummer der Spalte die eingefügt wird +// pInsert : Zeiger auf die ein zu fügenden Daten +// Returns the positio of the new column or -1 if an error occurs. +static int TreeListInsertColumn(TreeListData *pData, unsigned uCol, TV_COLUMN *pColumn) { + + ExtraItem **pList; + TV_COLSIZE sNotify; + HDITEM sItem; + RECT sRect; + short sFixed; + UINT uIndex; + BYTE bByte; + BYTE bItem; + BYTE bMark; + BYTE bMinEx; + BYTE bAlign; + int iWeight; + int iDelta; + int iStart; + int iSize; + int iXoff; + int iYoff; + int iNum; + int iVar; + int iAll; + int iAdd; + int iMin; + int iFix; + + GetClientRect(pData->hWnd, &sRect); + + if(!pData->hHeader) { // Create a new header + iStart = bDrawWithTheme ? GetSystemMetrics(SM_CYHSCROLL) : 17; //SM_CYHSCROLL is not enough tall with themes disabled apps; + iYoff = sRect.top + iStart; + + if(pData->uStyleEx & TVS_EX_HIDEHEADERS) { + iYoff = 0; + } + + pData->hHeader = CreateWindow(WC_HEADER, NULL, WS_VISIBLE | WS_CHILD | HDS_HORZ | HDS_BUTTONS | HDS_DRAGDROP, sRect.left, sRect.top, sRect.right, iYoff, pData->hWnd, (HMENU)1, NULL, NULL); + if(!pData->hHeader) + return -1; + + pData->uStartPixel = (pData->uStyleEx & TVS_EX_HIDEHEADERS) ? 0 : iStart; + pData->iRowHeight = 1; + UpdateHeight(pData); + + InvalidateRect(pData->hWnd, &sRect, FALSE); + if(pData->uStyleEx & TVS_EX_HEADEROWNIMGLIST){ + SendMessage(pData->hHeader, HDM_SETIMAGELIST, 0, (LPARAM)pData->hHeadImg); + } else { + SendMessage(pData->hHeader, HDM_SETIMAGELIST, 0, (LPARAM)pData->hImages); + } + SendMessage(pData->hHeader, WM_SETFONT, (WPARAM)hDefaultFontN, 0); + + if(pData->uSizeX <= pData->uStartPixel) + pData->uSizeYsub = 0; + else + pData->uSizeYsub = pData->uSizeX - pData->uStartPixel; + } + + if(pData->uColumnCount >= MAX_COLUMNS) { // Prüfe die Anzahl der Spalten + return -1; + } + + memset(&sItem, 0, sizeof(sItem)); // Die Spaltendaten zusammenstellen + + if(uCol >= pData->uColumnCount) { + uCol = pData->uColumnCount; + } + + if(pColumn->mask & TVCF_FMT) { // text alignment + sItem.mask |= HDI_FORMAT; + sItem.fmt = pColumn->fmt; + + switch(sItem.fmt & HDF_JUSTIFYMASK) { + case HDF_CENTER: + bAlign = DT_CENTER; + break; + case HDF_RIGHT: + bAlign = DT_RIGHT; + break; + default: + bAlign = DT_LEFT; + break; + } + } else { + bAlign = DT_LEFT; + } + + if(pColumn->mask & TVCF_IMAGE) { // Hat die Spalte auch ein Icon + sItem.mask |= HDI_IMAGE; + sItem.iImage = pColumn->iImage; + } + + if(pColumn->mask & TVCF_TEXT) { // Auch einen Text übergeben + sItem.mask |= HDI_TEXT; + sItem.pszText = pColumn->pszText; + } + + if(pColumn->mask & TVCF_MIN) { // Auch einen Min-Wert übergeben + iMin = pColumn->iOrder; + bMinEx = 1; + + if(iMin < 0) { + iMin = -iMin; + bMinEx = 0; + } + } else { + iMin = 16; + bMinEx = 0; + } + + if(pColumn->mask & TVCF_WIDTH) { // Fixe Breite für die Spalte + iWeight = 0; + sItem.mask |= HDI_WIDTH; + sItem.cxy = pColumn->cx; + iSize = pColumn->cx; + iDelta = -pColumn->cx; + iAdd = 0; + } else { // Variable vordefinierte Breite + if(pColumn->mask & TVCF_VWIDTH) + iWeight = pColumn->cx; + else + iWeight = 1; + + iVar = pData->iVarSize; + iFix = pData->iFixSize; + iAll = pData->iAllWeight; + iSize = pData->uSizeX - iVar - iFix; + + if(iWeight <= 0) + iWeight = 1; + if(iWeight > 255) + iWeight = 255; + + if(pData->uColumnCountVar) { // Gibt es schon variable Spalten + iSize = (iVar * iWeight) / (iAll + iWeight); + iDelta = -iSize; + } else { + iDelta = 0; + } + + sItem.mask |= HDI_WIDTH; + sItem.cxy = iSize; + iAdd = 1; + + if(sItem.cxy < iMin) + sItem.cxy = iMin; + } + + uCol = Header_InsertItem(pData->hHeader, uCol, &sItem); + if(uCol & 0x80000000) + return -1; + + if(pData->uColumnCount > 0) { // Liste mit Extraeinträgen erzeugen + pList = new(ExtraItem*, pData->uTreeItemsMax + 1); + if(!pList) { + Header_DeleteItem(pData->hHeader, uCol); + return -1; + } + + memset(pList, 0, sizeof(ExtraItem *) * (pData->uTreeItemsMax + 1)); + + iNum = uCol - 1; + if(iNum < 0) + iNum = 0; + + memmove(pData->pExtraItems + iNum + 1, pData->pExtraItems + iNum, sizeof(pList) * (MAX_COLUMNS - 2 - iNum)); + pData->pExtraItems[iNum] = pList; + } + + memmove(pData->aColumn + uCol + 1, pData->aColumn + uCol, (MAX_COLUMNS - 1 - uCol)*sizeof(ColumnData)); + + for(uIndex = pData->uColumnCount + 2; uIndex > uCol; uIndex--) { // Zuordnungs-Array anpassen + bItem = pData->aColumnPos[uIndex - 1]; + if(bItem >= uCol) + bItem++; + + pData->aColumnPos[uIndex] = bItem; + } + + pData->aColumnPos[uCol] = (BYTE)uCol; + + while(uIndex > 0) { + uIndex--; + + bItem = pData->aColumnPos[uIndex]; + if(bItem < uCol) + continue; + + bItem++; + pData->aColumnPos[uIndex] = bItem; + } + + for(uIndex = pData->uColumnCount;;) { // Folgende Spalten verschieben + bByte = pData->aColumn[uIndex].bIndex; + + if(bByte >= uCol) { + bByte++; + pData->aColumn[uIndex].bIndex = bByte; + } + + if(uIndex == 0) + break; + uIndex--; + } + + bMark = 0; + sFixed = 0; + + if(pColumn->mask & TVCF_MARK) // Ist die Spalte markiert + if(pColumn->fmt & TVCFMT_MARK) { + bMark = 1; + } + + if(pColumn->mask & TVCF_FIXED) // Ist die Spalte fixiert + if(pColumn->fmt & TVCFMT_FIXED) { + sFixed = (short)((sItem.cxy > 0) ? sItem.cxy : 100); + } + + pData->aColumn[uCol].bWeight = (BYTE)iWeight; + pData->aColumn[uCol].sReal = (short)sItem.cxy; + pData->aColumn[uCol].sSize = (short)iSize; + pData->aColumn[uCol].sMin = (short)iMin; + pData->aColumn[uCol].bIndex = (BYTE)uCol; + pData->aColumn[uCol].bMinEx = bMinEx; + pData->aColumn[uCol].bAlign = bAlign; + pData->aColumn[uCol].bMark = bMark; + pData->aColumn[uCol].sFixed = sFixed; + pData->uMarkedCols += bMark; + + + for(uIndex = pData->uColumnCount;;) { // Nächste sichtbare Spalten aktualisieren + bByte = pData->aColumn[uIndex].bIndex; + pData->aColumn[uIndex].bNext = pData->aColumnPos[bByte + 1]; + if(uIndex == 0) + break; + uIndex--; + } + + if(pData->uColumnCountVar) { // Variable Breiten anpassen + if(iDelta) { + ChangeColSize(pData, iDelta); + pData->iVarSize -= iDelta; + } else + if(!iAdd) { + pData->iFixSize += iSize; + } + } else { + if(iAdd) + pData->iVarSize = iSize; + else + pData->iFixSize += iSize; + } + + pData->iAllWeight += iWeight; + pData->uColumnCountVar += iAdd; + pData->uColumnCount += 1; + + if(pData->uSelectedSub > 0 && pData->uSelectedSub >= uCol) { + pData->uSelectedSub++; + } + + if(pData->uTrackedSub > 0 && pData->uTrackedSub >= uCol) { + pData->uTrackedSub++; + } + + if(pData->uFocusSub > 0 && pData->uFocusSub >= uCol) { + pData->uFocusSub++; + } + + if(pData->uEditSub > 0 && pData->uEditSub >= uCol) { + pData->uEditSub++; + } + + iXoff = UpdateColumns(pData); // Hat sich die Spaltenbreiten verändert + if(iXoff < 0x10000) { + sRect.left = iXoff; + sRect.left -= pData->uScrollX; + sRect.top = pData->uStartPixel; + InvalidateRect(pData->hWnd, &sRect, FALSE); + } + + UpdateScrollX(pData); + + if(pData->uInsertMark) { // Fehlende Infomarken einfügen + TV_ITEM sSet; + ExtraItem *pExtra; + + sSet.mask = TVIF_SUBITEM; + sSet.hItem = (HTREEITEM)pData->uInsertMark; + sSet.cChildren = uCol; + + TreeListSetItem(pData, &sSet); + + pExtra = pData->pExtraItems[uCol - 1][pData->uInsertMark]; + if(pExtra) { + pExtra->uColorBk = pData->uColors[TVC_INSERT]; + pExtra->bFlags |= TVIX_BKCOLOR; + } + } + + if(pData->uStyleEx & TVS_EX_HEADERCHGNOTIFY) { // Geänderte Spalten melden + sNotify.hdr.code = TVN_COLUMNCHANGED; + sNotify.uColumn = uCol; + sNotify.uIndex = pData->aColumn[uCol].bIndex; + sNotify.uPosX = pData->aColumnXpos[uCol]; + sNotify.iSize = pData->aColumn[uCol].sReal; + + UNLOCK(pData); + SendNotify(pData, &sNotify.hdr); + LOCK(pData); + } + + return uCol; +} + +//***************************************************************************** +//* +//* TreeListScanColumn +//* +//***************************************************************************** +// Berechnet die Breite der sichtbaren Einträge einer Spalte +// pData : Zeiger auf die Fensterdaten +// uSub : Ist die Spalte +// Ergibt sie gescannte Breite +static int TreeListScanColumn(TreeListData *pData, unsigned uSub) { + + BaseItem **pList; + BaseItem *pEntry; + ExtraItem *pExtra; + ExtraItem **pItems; + unsigned *pPList; + unsigned uPos; + int iMax; + int iPos; + + if(uSub >= pData->uColumnCount) + return 0; + + if(uSub > 0) { // Extraspalte + pItems = pData->pExtraItems[uSub - 1]; + pPList = pData->pItemPos; + iMax = 0; + + for(uPos = 0; uPos < pData->uItemPosCount; uPos++) { + pExtra = pItems[pPList[uPos]]; + if(!pExtra) { + if(iMax < 8) + iMax = 8; + continue; + } + + if(pData->hSubImg && (pExtra->bFlags & TVIX_HASIMAGE)) + iPos = pData->iSubImgXsize; + else + iPos = 0; + + iPos += pExtra->iTextPixels + 8; + if(iPos > iMax) + iMax = iPos; + } + + return iMax; + } + + pList = pData->pTreeItems; + pPList = pData->pItemPos; + iMax = 0; + + for(uPos = 0; uPos < pData->uItemPosCount; uPos++) { // Erste Spalte + pEntry = pList[pPList[uPos]]; + + if(pEntry->bFlags & TVIX_HASIMAGE) + iPos = pData->iImagesXsize; + else + iPos = 0; + + iPos += pEntry->uLevel * pData->iIndent; + iPos += pEntry->iTextPixels + 8; + if(iPos > iMax) + iMax = iPos; + } + + if(pData->uStyleEx & TVS_EX_ITEMLINES) { + iMax += 1; + } + + if(pData->cHasRootRow) { + iMax += pData->iIndent; + } + + if(pData->hStates) { + iMax += pData->iStatesXsize; + } + + return iMax; +} + + +//***************************************************************************** +//* +//* TreeListHitTest +//* +//***************************************************************************** +// Prüft wo eine Koordinate im Fenster ist +// pData : Zeiger auf die Fensterdaten +// pInfo : Zeiger auf die ein zu fügenden Daten +// Ergibt das Item auf dem die Koordinate zeigt +static unsigned TreeListHitTest(TreeListData *pData, TV_HITTESTINFO *pInfo) { + + BaseItem *pEntry; + ExtraItem *pExtra; + unsigned uItem; + unsigned uNext; + unsigned uSub; + unsigned uCol; + int iXpos; + int iYpos; + int iZpos; + int iWidth; + int iIcon; + + iXpos = pInfo->pt.x; + iYpos = pInfo->pt.y; + + if((unsigned)iXpos >= pData->uSizeX) { + pInfo->hItem = NULL; + pInfo->flags = (iXpos < 0) ? TVHT_TOLEFT : TVHT_TORIGHT; + return 0; + } + + iYpos -= pData->uStartPixel; + + if((unsigned)iYpos >= pData->uSizeY) { + pInfo->hItem = NULL; + pInfo->flags = (iYpos < 0) ? TVHT_ABOVE : TVHT_BELOW; + return 0; + } + + iZpos = iYpos / pData->iRowHeight; + iZpos += pData->uScrollY; + + if((unsigned)iZpos >= pData->uItemPosCount) { + pInfo->hItem = NULL; + pInfo->flags = TVHT_NOWHERE; + return 0; + } + + iXpos += pData->uScrollX; + uItem = pData->pItemPos [iZpos]; + pEntry = pData->pTreeItems[uItem]; + pInfo->hItem = (HTREEITEM)uItem; + + if(!pEntry) + return 0; + + uSub = pData->aColumnPos[1]; + + if(iXpos >= pData->aColumnXpos[uSub]) { // Auf Extraeintrag + for(uCol = 1; uCol < pData->uColumnCount; uCol++) { + uSub = pData->aColumnPos[uCol ]; + uNext = pData->aColumnPos[uCol + 1]; + + if(iXpos >= pData->aColumnXpos[uNext]) + continue; + iXpos -= pData->aColumnXpos[uSub ]; + + pExtra = pData->pExtraItems[uSub - 1][uItem]; + + + if(pData->aColumn[uSub].bEdit >= TVAX_CHECK) { // Hat der Extraeintrag ein Icon + iIcon = pData->iChecksXsize; + } else + if(pExtra && (pExtra->bFlags & TVIX_HASIMAGE)) { + iIcon = pData->iImagesXsize; + } else { + iIcon = 0; + } + + pInfo->flags = uSub << 24; + + if(iXpos < iIcon) { // Auf Icon + pInfo->flags |= TVHT_ONSUBICON; + return uItem; + } + + if(!pExtra || !pExtra->uTextSize) { // Auf Text wenn leerer Eintrag + pInfo->flags |= TVHT_ONSUBLABEL; + return uItem; + } + + switch(pData->aColumn[uSub].bAlign) { // Textausrichtung + default + : + if(iXpos - iIcon < pExtra->iTextPixels + 5) { + pInfo->flags |= TVHT_ONSUBLABEL; + return uItem; + } + break; + + case DT_RIGHT: + iWidth = pData->aColumnXpos[uNext]; + iWidth -= pData->aColumnXpos[uSub ]; + + if(iXpos >= iWidth - pExtra->iTextPixels - 5) { + pInfo->flags |= TVHT_ONSUBLABEL; + return uItem; + } + break; + + case DT_CENTER: + iWidth = pData->aColumnXpos[uNext]; + iWidth -= pData->aColumnXpos[uSub ]; + iWidth += iIcon; + iWidth /= 2; + + if(iXpos >= iWidth - pExtra->iTextPixels / 2 - 3) + if(iXpos <= iWidth + pExtra->iTextPixels / 2 + 3) { + pInfo->flags |= TVHT_ONSUBLABEL; + return uItem; + } + break; + } + + pInfo->flags |= TVHT_ONSUBRIGHT; + + return uItem; + } + + pInfo->flags = TVHT_ONRIGHTSPACE; + + return uItem; + } + + if(!pData->cHasRootRow) { // Root-Linien ausgleichen + iXpos += pData->iIndent; + } + + iXpos -= pData->iIndent * pEntry->uLevel; + + if(iXpos < pData->iIndent) { // Auf eingerücktem Bereich + if(pData->uStyle & TVS_HASBUTTONS) + if(pEntry->bFlags & TVIX_HASBUTTON) { + if(iXpos >= pData->iShift - 6) + if(iXpos <= pData->iShift + 7) { + iYpos %= pData->iRowHeight; + iYpos -= pData->iRowHeight / 2; + + if(iYpos >= -6 && iYpos <= 7) { + pInfo->flags = TVHT_ONITEMBUTTON; + return uItem; + } + } + } + + pInfo->flags = TVHT_ONITEMINDENT; + return uItem; + } + + iXpos -= pData->iIndent; + + if(pData->uStyleEx & TVS_EX_ITEMLINES) { + iXpos--; + } + + if(pData->hStates) { // Auf der Auswahl-Box + iXpos -= pData->iStatesXsize; + + if(iXpos < 0) { + pInfo->flags = TVHT_ONITEMSTATEICON; + return uItem; + } + } + + if(pEntry->bFlags & TVIX_HASIMAGE) { // Auf dem Icon + iXpos -= pData->iImagesXsize; + + if(pData->uStyleEx & TVS_EX_ITEMLINES) + iXpos--; + + if(iXpos < 0) { + pInfo->flags = TVHT_ONITEMICON; + return uItem; + } + } + // Auf Text + if(iXpos < pEntry->iTextPixels + 5 || !pEntry->pText || !pEntry->pText[0]) { + pInfo->flags = TVHT_ONITEMLABEL; + } else { + pInfo->flags = TVHT_ONITEMRIGHT; + } + + return uItem; +} + +//***************************************************************************** +//* +//* TreeListSetTrackItem +//* +//***************************************************************************** +// Setzt das den Einfügeeintrag +// pData : Zeiger auf die Fensterdaten +// uItem : Ist die Nummer des Eintrages bei dem eingefügt werden soll +// iMode : 0=davor einfügen 1=nachher einfügen +// Ergibt 1 wenn der Eintrag eingefügt wurde +static unsigned TreeListSetInsertMark(TreeListData *pData, unsigned uItem, int iMode) { + + TV_INSERTSTRUCT sInsert; + ExtraItem *pExtra; + BaseItem *pEntry; + unsigned uSub; + int iRet; + + if(pData->uInsertMark) { + iRet = TreeListDeleteItem(pData, pData->uInsertMark, 1); + pData->uInsertMark = 0; + } else { + iRet = 0; + } + + if(uItem == 0) + return iRet; + if(uItem > pData->uTreeItemsMax) + return 0; + + pEntry = pData->pTreeItems[uItem]; + if(!pEntry) + return 0; + + if(iMode) { + uItem = pEntry->uPrevItem; + if(!uItem) + uItem = U(TVI_FIRST); + } + + sInsert.hParent = (HTREEITEM)pEntry->uParent; + sInsert.hInsertAfter = (HTREEITEM)uItem; + sInsert.item.mask = TVIF_SELECTEDIMAGE | TVIF_IMAGE; + sInsert.item.iImage = TV_NOIMAGE; + sInsert.item.iSelectedImage = TV_NOIMAGE; + + uItem = TreeListInsertItem(pData, &sInsert); + if(!uItem) + return 0; + + pEntry = pData->pTreeItems[uItem]; + pEntry->uColorBk = pData->uColors[TVC_INSERT]; + pEntry->bFlags |= TVIX_BKCOLOR; + sInsert.item.mask |= TVIF_SUBITEM; + sInsert.item.hItem = (HTREEITEM)uItem; + + for(uSub = 1; uSub < pData->uColumnCount; uSub++) { + sInsert.item.cChildren = uSub; + + TreeListSetItem(pData, &sInsert.item); + + pExtra = pData->pExtraItems[uSub - 1][uItem]; + pExtra->uColorBk = pData->uColors[TVC_INSERT]; + pExtra->bFlags |= TVIX_BKCOLOR; + } + + pData->uInsertMark = uItem; + + return uItem; +} + +//***************************************************************************** +//* +//* TreeListGetItemColor +//* +//***************************************************************************** +// Holt die Farbe eins Feldes +// pData : Zeiger auf die Fensterdaten +// uItem : Ist die Nummer des Eintrages +// uSub : Ist die Spalte +// iMode : 0=Hintergrundfarbe 1=Textfarbe abfragen +// Ergibt die alte Farbe oder TV_NOCOLOR wenn keine Farbe eingestellt war +static LRESULT TreeListGetItemColor(TreeListData *pData, unsigned uItem, unsigned uSub, int iMode) { + + COLORREF uColor; + ExtraItem *pExtra; + BaseItem *pEntry; + + if(uItem > pData->uTreeItemsMax) + return TV_NOCOLOR; + + pEntry = pData->pTreeItems[uItem]; + if(!pEntry) + return TV_NOCOLOR; + + if(uSub) { // Extra-Eintrag abfragen + if(uSub >= pData->uColumnCount) + return TV_NOCOLOR; + + pExtra = pData->pExtraItems[uSub - 1][uItem]; + if(!pExtra) + return TV_NOCOLOR; + + if(iMode) + uColor = (pExtra->bFlags & TVIX_TEXTCOLOR) ? pExtra->uColorText : TV_NOCOLOR; + else + uColor = (pExtra->bFlags & TVIX_BKCOLOR) ? pExtra->uColorBk : TV_NOCOLOR; + } else { + if(iMode) + uColor = (pEntry->bFlags & TVIX_TEXTCOLOR) ? pEntry->uColorText : TV_NOCOLOR; + else + uColor = (pEntry->bFlags & TVIX_BKCOLOR) ? pEntry->uColorBk : TV_NOCOLOR; + } + + return (LRESULT)uColor; +} + +//***************************************************************************** +//* +//* TreeListSetItemColor +//* +//***************************************************************************** +// Setzt die Farbe eines Feldes +// pData : Zeiger auf die Fensterdaten +// uItem : Ist die Nummer des Eintrages +// uSub : Ist die Spalte +// uColor : Ist die neue Farbe +// iMode : 0=Hintergrundfarbe 1=Textfarbe einstellen +// Ergibt die alte Farbe oder TV_NOCOLOR wenn keine Farbe eingestellt war +static COLORREF TreeListSetItemColor(TreeListData *pData, unsigned uItem, unsigned uSub, COLORREF uColor, int iMode) { + + TV_ITEM sSet; + COLORREF uOld; + ExtraItem *pExtra; + BaseItem *pEntry; + + if(uItem > pData->uTreeItemsMax) + return TV_NOCOLOR; + + pEntry = pData->pTreeItems[uItem]; + if(!pEntry) + return TV_NOCOLOR; + + if(uSub >= 255) { // Die Ganze Zeile ändern + if(pData->uColumnCount) { + for(uSub = pData->uColumnCount; uSub > 0; uSub--) { + TreeListSetItemColor(pData, uItem, uSub, uColor, iMode); + } + } + + uSub = 0; + } + + if(uSub) { // Extra-Eintrag verändern + if(uSub >= pData->uColumnCount) + return TV_NOCOLOR; + + pExtra = pData->pExtraItems[uSub - 1][uItem]; + if(!pExtra) { // Extra-Eintrag erzeugen + sSet.mask = TVIF_SUBITEM; + sSet.hItem = (HTREEITEM)uItem; + sSet.cChildren = uSub; + + if(!TreeListSetItem(pData, &sSet)) + return TV_NOCOLOR; + + pExtra = pData->pExtraItems[uSub - 1][uItem]; + } + + if(iMode) { // Textfarbe + uOld = (pExtra->bFlags & TVIX_TEXTCOLOR) ? pExtra->uColorText : TV_NOCOLOR; + + if(uColor == TV_NOCOLOR) { + pExtra->bFlags &= ~TVIX_TEXTCOLOR; + } else { + pExtra->bFlags |= TVIX_TEXTCOLOR; + pExtra->uColorText = uColor; + } + } else { // Hintergrund + uOld = (pExtra->bFlags & TVIX_BKCOLOR) ? pExtra->uColorBk : TV_NOCOLOR; + + if(uColor == TV_NOCOLOR) { + pExtra->bFlags &= ~TVIX_BKCOLOR; + } else { + pExtra->bFlags |= TVIX_BKCOLOR; + pExtra->uColorBk = uColor; + } + } + } else { + if(iMode) { // Textfarbe + uOld = (pEntry->bFlags & TVIX_TEXTCOLOR) ? pEntry->uColorText : TV_NOCOLOR; + + if(uColor == TV_NOCOLOR) { + pEntry->bFlags &= ~TVIX_TEXTCOLOR; + } else { + pEntry->bFlags |= TVIX_TEXTCOLOR; + pEntry->uColorText = uColor; + } + } else { // Hintergrund + uOld = (pEntry->bFlags & TVIX_BKCOLOR) ? pEntry->uColorBk : TV_NOCOLOR; + + if(uColor == TV_NOCOLOR) { + pEntry->bFlags &= ~TVIX_BKCOLOR; + } else { + pEntry->bFlags |= TVIX_BKCOLOR; + pEntry->uColorBk = uColor; + } + } + } + + if(uColor != uOld) { // Neu zeichnen + UpdateRect(pData, uItem, uSub); + } + + return uOld; +} + +//***************************************************************************** +//* +//* TreeListSetTrackItem +//* +//***************************************************************************** +// Setzt das unterstrichene Item +// pData : Zeiger auf die Fensterdaten +// uItem : Ist die Nummer des Eintrages +// uSub : Ist die Spalte +// Ergibt 1 wenn der Eintrag unterstrichen wurde +static int TreeListSetTrackItem(TreeListData *pData, unsigned uItem, unsigned uSub) { + + ExtraItem *pExtra; + BaseItem *pEntry; + int iRet = 1; + + if(!(pData->uStyleEx & TVS_EX_SUBSELECT)) { + uSub = 0; + } else { + if(uSub >= pData->uColumnCount) { + uItem = 0; + uSub = 0; + iRet = 0; + } + } + + if(uItem > pData->uTreeItemsMax) { + uItem = 0; + uSub = 0; + iRet = 0; + } else { + if(uItem == pData->uTrackedItem) + if(uSub == pData->uTrackedSub) { + return iRet; + } + } + + if(pData->uTrackedItem) { // Den alten Eintrag zurücksetzen + if(pData->uTrackedSub) { + pExtra = pData->pExtraItems[pData->uTrackedSub - 1][pData->uTrackedItem]; + if(pExtra) { + pExtra->bFlags &= ~TVIX_TRACKED; + UpdateRect(pData, pData->uTrackedItem, pData->uTrackedSub); + } + } else { + pEntry = pData->pTreeItems[pData->uTrackedItem]; + if(pEntry) { + pEntry->bFlags &= ~TVIX_TRACKED; + UpdateRect(pData, pData->uTrackedItem, 0); + } + } + } + + if(uItem) { // Den neuen Eintrag setzen + if(uSub) { + pExtra = pData->pExtraItems[uSub - 1][uItem]; + if(pExtra) { + pData->uTrackedSub = uSub; + pData->uTrackedItem = uItem; + pExtra->bFlags |= TVIX_TRACKED; + UpdateRect(pData, uItem, uSub); + } else { + iRet = 0; + } + } else { + pEntry = pData->pTreeItems[uItem]; + if(pEntry) { + pData->uTrackedSub = 0; + pData->uTrackedItem = uItem; + pEntry->bFlags |= TVIX_TRACKED; + UpdateRect(pData, uItem, 0); + } else { + iRet = 0; + } + } + } else { // Keine Untersteichung + pData->uTrackedSub = 0; + pData->uTrackedItem = 0; + } + + return iRet; +} + +//***************************************************************************** +//* +//* TreeListGetNextItem +//* +//***************************************************************************** +// Sucht den nächsten Eintrag +// pData : Zeiger auf die Fensterdaten +// uItem : Ist die Nummer des Eintrages +// uFlags : Bestimmt nach welchem Eintrag gesucht werden soll +// Ergibt 1 wenn der Eintrag unterstrichen wurde +static unsigned TreeListGetNextItem(TreeListData *pData, unsigned uItem, unsigned uFlags) { + BaseItem *pEntry; + unsigned uStop; + unsigned uPos; + + switch(uFlags) { + case TVGN_ROOT: + return pData->uFirstChild; + + case TVGN_NEXT: + if(uItem > pData->uTreeItemsMax) { + return 0; + } + + pEntry = pData->pTreeItems[uItem]; + if(!pEntry) + return 0; + + return pEntry->uNextItem; + + case TVGN_PREVIOUS: + if(uItem > pData->uTreeItemsMax) { + return 0; + } + + pEntry = pData->pTreeItems[uItem]; + if(!pEntry) + return 0; + + return pEntry->uPrevItem; + + case TVGN_PARENT: + if(uItem > pData->uTreeItemsMax) { + return 0; + } + + pEntry = pData->pTreeItems[uItem]; + if(!pEntry) + return 0; + + return pEntry->uParent; + + case TVGN_CHILD: + if(uItem > pData->uTreeItemsMax) { + if(uItem == U(TVI_ROOT)) + return pData->uFirstChild; + return 0; + } + + pEntry = pData->pTreeItems[uItem]; + if(!pEntry) + return 0; + + return pEntry->uFirstChild; + + case TVGN_LASTCHILD: + if(uItem > pData->uTreeItemsMax) { + if(uItem == U(TVI_ROOT)) + return pData->uLastChild; + return 0; + } + + pEntry = pData->pTreeItems[uItem]; + if(!pEntry) + return 0; + + return pEntry->uLastChild; + + case TVGN_FIRSTVISIBLE: + if(pData->uItemPosCount <= 0) + return 0; + if(pData->uItemPosCount <= pData->uScrollY) + return 0; + + uItem = pData->pItemPos[pData->uScrollY]; + + if(uItem > pData->uTreeItemsMax || !pData->pTreeItems[uItem]) { + return 0; + } + + return uItem; + + case TVGN_NEXTVISIBLE: + if(uItem > pData->uTreeItemsMax) { + if(uItem != U(TVI_ROOT)) + return 0; + if(pData->uFirstChild == 0) + return 0; + + pEntry = pData->pTreeItems[pData->uFirstChild]; + + uPos = pEntry->uShowPos; + if(uPos <= pData->uScrollY) + return 0; + + return pData->uFirstChild; + } + + pEntry = pData->pTreeItems[uItem]; + if(!pEntry) + return 0; + + uPos = pEntry->uShowPos; + if(uPos <= pData->uScrollY) + return 0; + if(uPos > pData->uScrollY + pData->uPageEnties) + return 0; + + uItem = pData->pItemPos[uPos]; + + if(uItem > pData->uTreeItemsMax || !pData->pTreeItems[uItem]) { + return 0; + } + + return uItem; + + case TVGN_NEXTSELECTED: + if(uItem > pData->uTreeItemsMax) { + if(uItem != U(TVI_ROOT)) + return 0; + + uItem = pData->uFirstChild; + + pEntry = pData->pTreeItems[uItem]; + if(!pEntry) + return 0; + if(pEntry->uState & TVIS_SELECTED) + return uItem; + } + + pEntry = pData->pTreeItems[uItem]; + if(!pEntry) + return 0; + + for(;;) { + if(pEntry->uFirstChild) { + uItem = pEntry->uFirstChild; + } else + if(pEntry->uNextItem) { + uItem = pEntry->uNextItem; + } else { + for(;;) { + uItem = pEntry->uParent; + pEntry = pData ->pTreeItems[uItem]; + if(!pEntry) + return 0; + if(pEntry->uNextItem) { + uItem = pEntry->uNextItem; + break; + } + } + } + + pEntry = pData->pTreeItems[uItem]; + if(!pEntry) + break; + if(pEntry->uState & TVIS_SELECTED) + return uItem; + } + + return 0; + + case TVGN_NEXTSELCHILD: + if(uItem > pData->uTreeItemsMax) { + if(uItem != U(TVI_ROOT)) + return 0; + + uItem = pData->uFirstChild; + + pEntry = pData->pTreeItems[uItem]; + if(!pEntry) + return 0; + if(pEntry->uState & TVIS_SELECTED) + return uItem; + + uStop = 0; + } else { + pEntry = pData->pTreeItems[uItem]; + if(!pEntry || !pEntry->uFirstChild) + return 0; + + uStop = uItem; + } + + for(;;) { + if(pEntry->uFirstChild) { + uItem = pEntry->uFirstChild; + } else + if(pEntry->uNextItem) { + uItem = pEntry->uNextItem; + } else { + for(;;) { + uItem = pEntry->uParent; + if(uItem == uStop) + return 0; + pEntry = pData ->pTreeItems[uItem]; + if(!pEntry) + return 0; + if(pEntry->uNextItem) { + uItem = pEntry->uNextItem; + break; + } + } + } + + pEntry = pData ->pTreeItems[uItem]; + if(!pEntry) + break; + if(pEntry->uState & TVIS_SELECTED) + return uItem; + } + + return 0; + + case TVGN_NEXTITEM: + if(uItem > pData->uTreeItemsMax) { + if(uItem != U(TVI_ROOT)) + return 0; + + uItem = pData->uFirstChild; + + pEntry = pData->pTreeItems[uItem]; + if(!pEntry) + return 0; + + return uItem; + } + + pEntry = pData->pTreeItems[uItem]; + if(!pEntry) + return 0; + + for(;;) { + if(pEntry->uFirstChild) { + uItem = pEntry->uFirstChild; + } else + if(pEntry->uNextItem) { + uItem = pEntry->uNextItem; + } else { + for(;;) { + uItem = pEntry->uParent; + pEntry = pData ->pTreeItems[uItem]; + if(!pEntry) + return 0; + if(pEntry->uNextItem) { + uItem = pEntry->uNextItem; + break; + } + } + } + + pEntry = pData->pTreeItems[uItem]; + if(!pEntry) + break; + + return uItem; + } + + return 0; + + case TVGN_PREVIOUSVISIBLE: + if(uItem > pData->uTreeItemsMax) { + return 0; + } + + pEntry = pData->pTreeItems[uItem]; + if(!pEntry) + return 0; + + uPos = pEntry->uShowPos - 1; + if(uPos <= pData->uScrollY) + return 0; + if(uPos > pData->uScrollY + pData->uPageEnties) + return 0; + + return pData->pItemPos[uPos - 1]; + + case TVGN_LASTVISIBLE: + uPos = pData->uItemPosCount; + if(uPos <= 0) + return 0; + return pData->pItemPos[uPos - 1]; + + case TVGN_DROPHILITE: + return pData->uTrackedItem; + + case TVGN_DROPHILITESUB: + return pData->uTrackedSub; + + case TVGN_CARET: + return pData->uSelectedItem; + + case TVGN_CARETSUB: + return pData->uSelectedSub; + + case TVGN_FOCUS: + return (pData->uFocusItem) ? pData->uFocusItem : pData->uSelectedItem; + + case TVGN_FOCUSSUB: + return (pData->uFocusItem) ? pData->uFocusSub : pData->uSelectedSub; + } + + return 0; +} + + +//***************************************************************************** +//* +//* TreeListFindItem +//* +//***************************************************************************** +// Sucht einen Kindeintrag mit einem bestimmten Text +// pData : Zeiger auf die Fensterdaten +// uItem : Ist die Nummer des Elterneintrages +// pFind : Die Struktur mit den Suchparametern +// Ergibt die Nummer des Eintrages bzw. 0 wenn nicht gefunden wurde +static unsigned TreeListFindItem(TreeListData *pData, unsigned uItem, TVFIND *pFind) { + int iImage; + BaseItem *pEntry; + ExtraItem *pExtra; + LPCTSTR pCmpText; + unsigned uTextSize; + unsigned uChkParam; + unsigned uChkState; + unsigned uChkText; + unsigned uChkCase; + unsigned uTextLen; + unsigned uSub; + + if(pFind->uFlags & TVIF_CHILD) { // In den Kindern suchen + if(uItem > pData->uTreeItemsMax) { + if(uItem != U(TVI_ROOT)) + return 0; + uItem = pData->uFirstChild; + } else { + pEntry = pData->pTreeItems[uItem]; + if(!pEntry) + return 0; + + uItem = pEntry->uFirstChild; + } + } else { + if(uItem > pData->uTreeItemsMax) { // Ist der Eintrag gültig + return 0; + } + + if(pFind->uFlags & TVIF_NEXT) { // Beim nächsten Eintrag weitersuchen + pEntry = pData->pTreeItems[uItem]; + if(!pEntry) + return 0; + + uItem = pEntry->uNextItem; + } + } + + uChkParam = pFind->uFlags & TVIF_PARAM; + uChkState = pFind->uFlags & TVIF_STATE; + uChkText = pFind->uFlags & TVIF_TEXT; + uChkCase = pFind->uFlags & TVIF_CASE; + uSub = pFind->uColumn; + uTextLen = 0; + + if(uChkText) { + uTextLen = str_len(pFind->pText); + } + + for(; uItem; uItem = pEntry->uNextItem) { // Durchlaufe alle Kinder + pEntry = pData->pTreeItems[uItem]; + if(!pEntry) + return 0; + + if(uChkParam && pFind->lParam != pEntry->lParam) { // Vergleiche lParam + continue; + } + // Vergleiche die State-Bits + if(uChkState && ((pEntry->uState ^ pFind->uState)&pFind->uStateMask)) { + continue; + } + + if(uChkText == 0) + break; + + if(uSub) { // Text von Spalten + if(uSub >= pData->uColumnCount) + continue; + + pExtra = pData->pExtraItems[uSub - 1][uItem]; + if(!pExtra) { + pCmpText = _T(""); + uTextSize = 0; + } else { + pCmpText = pExtra->pText; + if(!pCmpText) + pCmpText = _T(""); + uTextSize = pExtra->uTextSize; + } + } else { // Text vom Haupteintrag + if(pEntry->bCallback & TVIF_TEXT) { + CallbackEntry(pData, pEntry, uItem, pEntry->bCallback, &iImage, &uTextSize, &pCmpText); + } else { + pCmpText = pEntry->pText; + if(!pCmpText) + pCmpText = _T(""); + uTextSize = pEntry->uTextSize; + } + } + + if(uTextLen != uTextSize) + continue; + + if(uChkCase) { // Zwischen Groß/Kleinbuchstaben unterscheiden + if(!str_icmp(pCmpText, pFind->pText)) + break; + } else { + if(!memcmp(pCmpText, pFind->pText, uTextSize * sizeof(TCHAR))) + break; + } + } + + return uItem; +} + +//***************************************************************************** +//* +//* TreeListNextSelUntil +//* +//***************************************************************************** +// Sucht rekursiv ausgewählte Einträge +// pData : Zeiger auf die Fensterdaten +// uItem : Ist die Nummer des Eintrages nach dem mit der Suche begonnen werden soll +// uStop : Bestimmt bei welchem Eintrag abgebrochen werden soll werden soll +// Ergibt die Nummer des Eintrages oder 0 wenn keiner gefunden wurde +static unsigned TreeListNextSelUntil(TreeListData *pData, unsigned uItem, unsigned uStop) { + + BaseItem *pEntry; + + if(uItem > pData->uTreeItemsMax) { + if(uItem != U(TVI_ROOT)) + return 0; + + uItem = pData->uFirstChild; + + pEntry = pData->pTreeItems[uItem]; + if(!pEntry) + return 0; + if(pEntry->uState & TVIS_SELECTED) + return uItem; + + uStop = 0; + } else { + pEntry = pData->pTreeItems[uItem]; + if(!pEntry) + return 0; + if(!pEntry->uFirstChild && uItem == uStop) + return 0; + } + + for(;;) { + if(pEntry->uFirstChild) { + uItem = pEntry->uFirstChild; + if(uItem == uStop) + return 0; + } else + if(pEntry->uNextItem) { + uItem = pEntry->uNextItem; + if(uItem == uStop) + return 0; + } else { + for(;;) { + uItem = pEntry->uParent; + if(uItem == uStop) + return 0; + pEntry = pData ->pTreeItems[uItem]; + if(!pEntry) + return 0; + if(pEntry->uNextItem) { + uItem = pEntry->uNextItem; + if(uItem == uStop) + return 0; + break; + } + } + } + + pEntry = pData ->pTreeItems[uItem]; + if(!pEntry) + break; + if(pEntry->uState & TVIS_SELECTED) + return uItem; + } + + return 0; +} + +//***************************************************************************** +//* +//* TreeListNextUnselUntil +//* +//***************************************************************************** +// Sucht rekursiv nicht ausgewählte Einträge +// pData : Zeiger auf die Fensterdaten +// uItem : Ist die Nummer des Eintrages nach dem mit der Suche begonnen werden soll +// uStop : Bestimmt bei welchem Eintrag abgebrochen werden soll werden soll +// Ergibt die Nummer des Eintrages oder 0 wenn keiner gefunden wurde +static unsigned TreeListNextUnselUntil(TreeListData *pData, unsigned uItem, unsigned uStop) { + + BaseItem *pEntry; + + if(uItem > pData->uTreeItemsMax) { + if(uItem != U(TVI_ROOT)) + return 0; + + uItem = pData->uFirstChild; + + pEntry = pData->pTreeItems[uItem]; + if(!pEntry) + return 0; + if((pEntry->uState & TVIS_SELECTED) == 0) + return uItem; + + uStop = 0; + } else { + pEntry = pData->pTreeItems[uItem]; + if(!pEntry) + return 0; + if(!pEntry->uFirstChild && uItem == uStop) + return 0; + } + + for(;;) { + if(pEntry->uFirstChild) { + uItem = pEntry->uFirstChild; + if(uItem == uStop) + return 0; + } else + if(pEntry->uNextItem) { + uItem = pEntry->uNextItem; + if(uItem == uStop) + return 0; + } else { + for(;;) { + uItem = pEntry->uParent; + if(uItem == uStop) + return 0; + pEntry = pData->pTreeItems[uItem]; + if(!pEntry) + return 0; + if(pEntry->uNextItem) { + uItem = pEntry->uNextItem; + if(uItem == uStop) + return 0; + break; + } + } + } + + pEntry = pData->pTreeItems[uItem]; + if(!pEntry) + break; + if((pEntry->uState & TVIS_SELECTED) == 0) + return uItem; + } + + return 0; +} + +//***************************************************************************** +//* +//* TreeListChangeCheckbox +//* +//***************************************************************************** +// Schaltet eine Checkboc um +// pData : Zeiger auf die Fensterdaten +// uItem : Ist der Eintrag der geändert werden soll +// pInfo : +static void TreeListChangeCheckbox(TreeListData *pData, UINT uItem, int iPosX, int iPosY) { + + BaseItem *pTemp; + BaseItem *pEntry; + NMTREEVIEW sNotify; + TV_ITEM sItem; + UINT uBits; + + sNotify.itemOld.mask = 0; + sNotify.itemOld.hItem = 0; + + pEntry = pData->pTreeItems[uItem]; + uBits = pEntry->uState & TVIS_STATEIMAGEMASK; + + if(pData->uStyleEx & TVS_EX_SINGLECHECKBOX) { // Einzelauswahl + pTemp = pData->pTreeItems[pData->uSingleSel]; + + if(pData->uSingleSel == uItem) { + if(pData->uStyleEx & TVS_EX_BITCHECKBOX) { + if(uBits & 0x1000) + return; + } else { + if(uBits == 0x2000) + return; + } + } else + if(pData->uSingleSel && pTemp) { // Anderer Eintrag gewählt + sItem.hItem = (HTREEITEM)pData->uSingleSel; + sItem.mask = TVIF_STATE; + sItem.stateMask = TVIS_STATEIMAGEMASK; + sItem.state = (pData->uStyleEx & TVS_EX_BITCHECKBOX) ? 0x0000 : 0x1000; + + TreeListSetItem(pData, &sItem); + + sNotify.itemOld.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_TEXT; + sNotify.itemOld.hItem = (HTREEITEM)pData->uSingleSel; + sNotify.itemOld.stateMask = 0xFFFFFFFF; + sNotify.itemOld.state = pTemp->uState; + sNotify.itemOld.lParam = pTemp->lParam; + sNotify.itemOld.pszText = pTemp->pText; + sNotify.itemOld.cchTextMax = pTemp->uTextSize; + sNotify.itemOld.cChildren = 0; + } + } + + sItem.hItem = (HTREEITEM)uItem; + sItem.mask = TVIF_STATE; + sItem.stateMask = TVIS_STATEIMAGEMASK; + + if(pData->uStyleEx & TVS_EX_BITCHECKBOX) + sItem.state = (uBits ^ 0x1000); + else + sItem.state = (uBits & 0x1000) ? 0x2000 : 0x1000; + + TreeListSetItem(pData, &sItem); + pData->uSingleSel = uItem; + + sNotify.hdr.code = TVN_CBSTATECHANGED; + sNotify.action = VK_DBLCLK; + sNotify.itemNew.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_TEXT; + sNotify.itemNew.hItem = (HTREEITEM)uItem; + sNotify.itemNew.stateMask = 0xFFFFFFFF; + sNotify.itemNew.state = pEntry->uState; + sNotify.itemNew.lParam = pEntry->lParam; + sNotify.itemNew.pszText = pEntry->pText; + sNotify.itemNew.cchTextMax = pEntry->uTextSize; + sNotify.itemNew.cChildren = 0; + sNotify.ptDrag.x = iPosX; + sNotify.ptDrag.y = iPosY; + + UNLOCK(pData); + SendNotify(pData, &sNotify.hdr); + LOCK(pData); +} + +//***************************************************************************** +//* +//* TreeListMouseNotify +//* +//***************************************************************************** +// Soll für einen Mausklick eine +// pData : Zeiger auf die Fensterdaten +// uMsg : Message des Mausklicks +// wParam : WPARAM des Mausklicks +// lParam : LPARAM des Mausklicks +static void TreeListMouseNotify(TreeListData *pData, UINT uMsg, WPARAM wParam, LPARAM lParam) { + + TV_HITTESTINFO sInfo; + NMTREEVIEW sNotify; + BaseItem *pEntry; + unsigned uItem; + + sInfo.flags = (UINT) wParam; + sInfo.pt.x = LOWORD(lParam); + sInfo.pt.y = HIWORD(lParam); + uItem = TreeListHitTest(pData, &sInfo); + + if(uItem) { + pEntry = pData->pTreeItems[uItem]; + sNotify.itemNew.stateMask = 0xFFFFFFFF; + sNotify.itemNew.state = pEntry->uState; + sNotify.itemNew.lParam = pEntry->lParam; + sNotify.itemNew.cChildren = TVHT_SUBTOCOL(sInfo.flags); + } else { + sNotify.itemNew.stateMask = 0; + sNotify.itemNew.state = 0; + sNotify.itemNew.lParam = 0; + } + + sNotify.action = 0; + sNotify.hdr.code = uMsg; + sNotify.itemNew.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_SUBNUMBER; + sNotify.itemNew.hItem = (HTREEITEM)uItem; + sNotify.itemNew.pszText = (LPTSTR) - 1; + sNotify.itemNew.cchTextMax = -1; + sNotify.itemOld.mask = 0; + sNotify.ptDrag.x = LOWORD(lParam); + sNotify.ptDrag.y = HIWORD(lParam); + + SendNotify(pData, &sNotify.hdr); + +} + +//***************************************************************************** +//* +//* TreeListMouseClick +//* +//***************************************************************************** +// Soll ein Mausklick ausgeführt werden. ACHTUNG UNLOCK wird ausgeführt +// pData : Zeiger auf die Fensterdaten +// uMsg : Message des Mausklicks +// wParam : WPARAM des Mausklicks +// lParam : LPARAM des Mausklicks +static void TreeListMouseClick(TreeListData *pData, UINT uMsg, WPARAM wParam, LPARAM lParam) { + ExtraItem *pExtra; + BaseItem *pEntry; + BaseItem *pTemp; + TV_HITTESTINFO sInfo; + NMTREEVIEW sNotify; + unsigned uOldSub; + unsigned uOldItem; + unsigned uMaskItem; + unsigned uMaskSub; + unsigned uMsgOld; + unsigned uToggle; + unsigned uItem; + unsigned uLine; + unsigned uTemp; + unsigned uStop; + unsigned uSub; + unsigned uSel; + unsigned uPos; + unsigned uNum; + int iMode; + int iAdd; + + if(!pData->cIsEnabled) { // Ist das Fenster freigegeben + return; + } + + if(!pData->cHasFocus) { // Hat das Fenster den Focus + if(GetFocus() != pData->hWnd) { + UNLOCK(pData); + SetFocus(pData->hWnd); + LOCK(pData); + + if(GetFocus() != pData->hWnd) { + UNLOCK(pData); + return; + } + } else { + pData->cHasFocus = 1; + } + } + + sInfo.flags = (UINT) wParam; + sInfo.pt.x = LOWORD(lParam); + sInfo.pt.y = HIWORD(lParam); + uItem = TreeListHitTest(pData, &sInfo); + uMsgOld = uMsg; + uToggle = 0; + + if(uItem) { // Wurde auf einen Eintrag getrückt + pEntry = pData->pTreeItems[uItem]; + + if(pData->uStyle & TVS_FULLROWSELECT) { + uMaskItem = TVHT_ONITEMICON | TVHT_ONITEMLABEL | TVHT_ONITEMSTATEICON | TVHT_ONITEMRIGHT; + uMaskSub = TVHT_ONSUBICON | TVHT_ONSUBLABEL | TVHT_ONSUBRIGHT; + } else { + uMaskItem = TVHT_ONITEMICON | TVHT_ONITEMLABEL | TVHT_ONITEMSTATEICON; + uMaskSub = TVHT_ONSUBICON | TVHT_ONSUBLABEL; + } + + if(sInfo.flags & TVHT_ONITEMBUTTON) { // Eintrag aufklappen + if(uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONDBLCLK) { + pEntry = pData->pTreeItems[uItem]; + if(pEntry && (pEntry->uState & (TVIS_EXPANDED | TVIS_EXPANDPARTIAL)) == (TVIS_EXPANDED | TVIS_EXPANDPARTIAL)) { + TreeListToggleItem(pData, uItem, 0); // Von + auf - umschalten + } else { + uToggle = 1; // Eintrag auflappen + } + } + } else + if(sInfo.flags & TVHT_ONITEMSTATEICON) { // Checkbox umschalten + if(pData->uStyle & TVS_CHECKBOXES) + if(uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONDBLCLK) { + TreeListChangeCheckbox(pData, uItem, sInfo.pt.x, sInfo.pt.y); + } + } else + if(sInfo.flags & uMaskItem) { // Eintrag auswählen + if(!(pData->uStyle & TVS_DISABLEDRAGDROP)) { + if(uMsg == WM_LBUTTONDOWN) { + pData->uDragFlags = MK_LBUTTON; + pData->uDragItem = uItem; + pData->uDragSub = 0; + } + + if(uMsg == WM_RBUTTONDOWN) { + pData->uDragFlags = MK_RBUTTON; + pData->uDragItem = uItem; + pData->uDragSub = 0; + } + } + + if(pData->uStyleEx & TVS_EX_MULTISELECT) { // Mehrfachauswahl erlaubt + if(uMsg == WM_RBUTTONDOWN) // Keine Abwahl wenn mehrer Einträge aus gewählt + if(pData->uSelectedCount > 1) { + pEntry = pData->pTreeItems[uItem]; + if(pEntry->uState & TVIS_SELECTED) { + goto End; + } + } + + if(uMsg == WM_LBUTTONDOWN) // Spezialsteuerung für Multiselect bei Darg + if(!(wParam & (MK_CONTROL | MK_SHIFT))) + if(pData->uSelectedCount > 0) { + pEntry = pData->pTreeItems[uItem]; + if(pEntry && (pEntry->uState & TVIS_SELECTED)) { + pData->cClickFlag = 1; + pData->cClickEdit = 0; + goto End; + } + } + + if(uMsg == WM_LBUTTONUP && pData->cClickFlag) { + wParam &= ~(MK_CONTROL | MK_SHIFT); + uMsg = WM_LBUTTONDOWN; + } + + if(wParam & MK_SHIFT) { // Bis zum angeklicken Auswählen + if(uMsg != WM_LBUTTONDOWN) + if(uMsg != WM_LBUTTONDBLCLK) + goto End; + + pData->cClickEdit = 0; + + uTemp = pData->uSelectedItem; + if(!uTemp) { + uTemp = pData->uFocusItem; + if(!uTemp || !pData->cReSelect) + goto End; + } + + pEntry = pData->pTreeItems[uTemp]; + uLine = pEntry->uShowPos; + if(!uLine) + goto End; + + pEntry = pData->pTreeItems[uItem]; + uStop = pEntry->uShowPos; + if(!uStop) + goto End; + + if(pData->uSelectedCount <= 1) { + pData->uSelectedBase = uTemp; + } + + // Shift-Select neu auswählen + if(pData->cReSelect && pData->uSelectedBase) { + TreeListSelectItem(pData, uItem, 0, TVC_BYMOUSE | TVC_DESELECT); + TreeListEnsureVisible(pData, pData->uSelectedItem, pData->uSelectedSub); + + uTemp = pData->uSelectedBase; + pTemp = pData->pTreeItems[uTemp]; + uStop = uItem; + + while(pTemp && pTemp->uShowPos == 0) { + pTemp = pData->pTreeItems[pTemp->uParent]; + if(!pTemp) + break; + } + + if(pTemp) { + if(!(pTemp->uState & TVIS_SELECTED)) { + TreeListXorSelectItem(pData, uTemp, TVC_BYKEYBOARD); + } + + if(pTemp->uShowPos < pEntry->uShowPos) { + uStop = uItem; + } else { + uStop = uTemp; + uTemp = uItem; + } + + for(;;) { + uTemp = TreeListNextUnselUntil(pData, uTemp, uStop); + if(!uTemp) + break; + TreeListXorSelectItem(pData, uTemp, TVC_BYKEYBOARD); + } + } + + pData->cReSelect = 0; + goto End; + } + + TreeListSelectItem(pData, uItem, 0, TVC_BYMOUSE); + + iAdd = (uLine > uStop) ? -1 : 1; + uPos = uLine; + + if(uPos != uStop) { + pEntry = pData->pTreeItems[pData->uSelectedBase]; + + while(pEntry && pEntry->uShowPos == 0) { + pEntry = pData->pTreeItems[pEntry->uParent]; + } + + if(pEntry) { + if(iAdd < 0){ + if(pEntry->uShowPos > uPos && uPos > uStop) + uPos--; + } + else { + if(pEntry->uShowPos < uPos && uPos < uStop) + uPos++; + } + } + } + + for(;; uPos += iAdd) { // Einträge wählen + uTemp = pData->pItemPos [uPos - 1]; + pEntry = pData->pTreeItems[uTemp ]; + uSel = pEntry->uState & TVIS_SELECTED; + + if(uTemp != pData->uSelectedBase || !uSel) { + TreeListXorSelectItem(pData, uTemp, TVC_BYMOUSE); + uSel = pEntry->uState & TVIS_SELECTED; + } + + // Auch unsichtbare Kinder wählen + pTemp = pData->pTreeItems[pEntry->uFirstChild]; + if(pTemp && !pTemp->uShowPos) { + if(uSel) { + for(uNum = uTemp;;) { // Kinder auswählen + uNum = TreeListNextUnselUntil(pData, uNum, uTemp); + if(!uNum) + break; + TreeListXorSelectItem(pData, uNum, TVC_BYMOUSE); + } + } else { + for(uNum = uTemp;;) { // Kinder abwählen + uNum = TreeListNextSelUntil(pData, uNum, uTemp); + if(!uNum) + break; + TreeListXorSelectItem(pData, uNum, TVC_BYMOUSE); + } + } + } + + if(uPos == uStop) + break; + } + + TreeListRemoveFocus(pData); + + pEntry = pData->pTreeItems[uItem]; + uSub = TVHT_SUBTOCOL(sInfo.flags); + if(!(pData->uStyleEx & TVS_EX_SUBSELECT)) + uSub = 0; + + pData->uFocusSub = uSub; + pData->uFocusItem = uItem; + + if(uSub) { + pExtra = pData->pExtraItems[uSub - 1][uItem]; + if(pExtra) + pExtra->bFlags |= TVIX_FOCUSED; + } else { + pEntry->bFlags |= TVIX_FOCUSED; + } + + UpdateRect(pData, uItem, uSub); + + TreeListSelectItem(pData, uItem, 0, TVC_BYMOUSE); + TreeListEnsureVisible(pData, pData->uSelectedItem, pData->uSelectedSub); + + goto End; + } + + if(wParam & MK_CONTROL) { // Einzelen Eintrag umschalten + pData->uSelectedBase = uItem; + pData->cReSelect = 1; + pData->cClickEdit = 0; + + pEntry = pData->pTreeItems[uItem]; + if(pEntry && (pEntry->uState & TVIS_SELECTED)) { + if(uMsg != WM_LBUTTONDOWN) + if(uMsg != WM_LBUTTONDBLCLK) + goto End; + + TreeListSelectItem(pData, 0, 0, TVC_BYMOUSE); + + if(TreeListXorSelectItem(pData, uItem, TVC_BYMOUSE)) { + // Auch unsichtbare Kinder abwählen + pTemp = pData->pTreeItems[pEntry->uFirstChild]; + if(pTemp && !pTemp->uShowPos) + for(uTemp = uItem;;) { + uTemp = TreeListNextSelUntil(pData, uTemp, uItem); + if(!uTemp) + break; + TreeListXorSelectItem(pData, uTemp, TVC_BYMOUSE); + } + } + + uTemp = pData->uFocusItem; + if(uTemp) { // Ist ein Focus definiert + TreeListRemoveFocus(pData); + } + + uSub = TVHT_SUBTOCOL(sInfo.flags); + if(!(pData->uStyleEx & TVS_EX_SUBSELECT)) + uSub = 0; + + pData->uFocusItem = uItem; + pData->uFocusSub = uSub; + + if(uSub) { + pExtra = pData->pExtraItems[uSub - 1][uItem]; + if(pExtra) + pExtra->bFlags |= TVIX_FOCUSED; + } else { + pEntry->bFlags |= TVIX_FOCUSED; + } + + UpdateRect(pData, uItem, uSub); + + goto End; + } + } + } + + if(wParam & MK_CONTROL) { // Ist die Ctrl-Taste gedrückt + iMode = TVC_BYMOUSE; + } else { + iMode = TVC_BYMOUSE | TVC_DESELECT; + } + + uOldSub = pData->uSelectedSub; + uOldItem = pData->uSelectedItem; + + TreeListRemoveFocus(pData); + TreeListSelectItem(pData, uItem, 0, iMode); + TreeListEnsureVisible(pData, pData->uSelectedItem, pData->uSelectedSub); + pEntry = pData->pTreeItems[uItem]; + + if(uMsg == WM_LBUTTONDBLCLK) { + // Auto-Edit mit Doppelklick + if(pData->aColumn[0].bFlags & TVAE_DBLCLICK && pData->aColumn[0].bEdit) { + TreeListStartAutoEdit(pData, 0, VK_DBLCLK, lParam); + } else + if(TreeListStartNotifyEdit(pData, pData->uSelectedItem, 0, VK_DBLCLK, lParam)) { + + } else + if(pEntry && (pEntry->uState & (TVIS_EXPANDED | TVIS_EXPANDPARTIAL)) == (TVIS_EXPANDED | TVIS_EXPANDPARTIAL)) { + TreeListToggleItem(pData, uItem, 0); // Von + auf - umschalten + } else { + uToggle = 1; // Eintrag auflappen + } + } else + if(uMsgOld == WM_LBUTTONDOWN) { + if(pData->uStyleEx & TVS_EX_EDITCLICK) + if(!(wParam & (MK_CONTROL | MK_SHIFT))) + if(sInfo.flags & uMaskItem&~TVHT_ONITEMICON) + if(uOldSub == pData->uSelectedSub && uOldItem == pData->uSelectedItem) { + pData->cClickEdit = 1; + } + } else + if(uMsgOld == WM_LBUTTONUP) { + if(pData->uStyleEx & TVS_EX_EDITCLICK) + if(pData->cClickFlag && !pData->cClickEdit) + if(!(wParam & (MK_CONTROL | MK_SHIFT))) + if(sInfo.flags & uMaskItem&~TVHT_ONITEMICON) + if(uOldSub == pData->uSelectedSub && uOldItem == pData->uSelectedItem) { + pData->cClickEdit = 1; + } + + if(pData->cClickEdit) { + pData->cClickEdit = 0; + + if(pData->aColumn[0].bEdit) { + TreeListStartAutoEdit(pData, 0, VK_EDITCLK, lParam); + } else { + TreeListStartNotifyEdit(pData, pData->uSelectedItem, 0, VK_EDITCLK, lParam); + } + } + } + } else + if(sInfo.flags & uMaskSub) { // Extra-Eintrag auswählen + if(pData->uStyleEx & TVS_EX_SUBSELECT) + uSub = TVHT_SUBTOCOL(sInfo.flags); + else + uSub = 0; + + if(!(pData->uStyle & TVS_DISABLEDRAGDROP)) { + if(uMsg == WM_LBUTTONDOWN) { + pData->uDragFlags = MK_LBUTTON; + pData->uDragItem = uItem; + pData->uDragSub = uSub; + } + + if(uMsg == WM_RBUTTONDOWN) { + pData->uDragFlags = MK_RBUTTON; + pData->uDragItem = uItem; + pData->uDragSub = uSub; + } + } + + if(wParam & MK_CONTROL) { + if(pData->uStyleEx & TVS_EX_MULTISELECT) { // Auswahl umschalten + pEntry = pData->pTreeItems[uItem]; + if(pEntry && (pEntry->uState & TVIS_SELECTED)) { + if(uMsg != WM_LBUTTONDOWN) + if(uMsg != WM_LBUTTONDBLCLK) + goto End; + + TreeListSelectItem(pData, 0, 0, TVC_BYMOUSE); + + if(TreeListXorSelectItem(pData, uItem, TVC_BYMOUSE)) { + // Auch unsichtbare Kinder abwählen + pTemp = pData->pTreeItems[pEntry->uFirstChild]; + if(pTemp && !pTemp->uShowPos) + for(uTemp = uItem;;) { + uTemp = TreeListNextSelUntil(pData, uTemp, uItem); + if(!uTemp) + break; + TreeListXorSelectItem(pData, uTemp, TVC_BYMOUSE); + } + } + + TreeListRemoveFocus(pData); + + pData->cClickEdit = 0; + pData->uFocusItem = uItem; + pData->uFocusSub = uSub; + + if(uSub) { + pExtra = pData->pExtraItems[uSub - 1][uItem]; + if(pExtra) + pExtra->bFlags |= TVIX_FOCUSED; + } else { + pEntry->bFlags |= TVIX_FOCUSED; + } + + UpdateRect(pData, uItem, uSub); + + goto End; + } + } + + iMode = TVC_BYMOUSE; + } else { + iMode = TVC_BYMOUSE | TVC_DESELECT; + } + + uOldSub = pData->uSelectedSub; + uOldItem = pData->uSelectedItem; + + TreeListRemoveFocus(pData); + TreeListSelectItem(pData, uItem, uSub, (wParam & MK_CONTROL) ? TVC_BYMOUSE : TVC_BYMOUSE | TVC_DESELECT); + TreeListEnsureVisible(pData, pData->uSelectedItem, pData->uSelectedSub); + pEntry = pData->pTreeItems[uItem]; + + if(uMsg == WM_LBUTTONDBLCLK) { + if(pData->aColumn[uSub].bEdit && // Auto-Edit mit Doppelklick + ((pData->aColumn[uSub].bFlags & TVAE_DBLCLICK) || + ((pData->aColumn[uSub].bFlags & TVAE_ICONCLICK) && (sInfo.flags & TVHT_ONSUBICON)))) { + TreeListStartAutoEdit(pData, uSub, (sInfo.flags & TVHT_ONSUBICON) ? VK_ICONCLK : VK_DBLCLK, lParam); + } else + if(TreeListStartNotifyEdit(pData, uItem, uSub, VK_DBLCLK, lParam)) { + + } else { + uToggle = 1; // Eintrag auflappen + } + } else + if(uMsg == WM_LBUTTONDOWN) { + if(pData->aColumn[uSub].bEdit) // Auto-Edit mit Klick auf Icon + if(pData->aColumn[uSub].bFlags & TVAE_ICONCLICK) + if(sInfo.flags & TVHT_ONSUBICON) { + TreeListStartAutoEdit(pData, uSub, VK_ICONCLK, lParam); + } + + if(pData->uStyleEx & TVS_EX_EDITCLICK) + if(uSub && !(wParam & (MK_CONTROL | MK_SHIFT))) + if(sInfo.flags & uMaskSub&~TVHT_ONSUBICON) + if(uOldSub == pData->uSelectedSub && uOldItem == pData->uSelectedItem) { + pData->cClickEdit = 1; + } + } else + if(uMsg == WM_LBUTTONUP) { + if(pData->cClickEdit) { + pData->cClickEdit = 0; + + if(pData->aColumn[uSub].bEdit) { + TreeListStartAutoEdit(pData, uSub, VK_EDITCLK, lParam); + } else { + TreeListStartNotifyEdit(pData, uItem, uSub, VK_EDITCLK, lParam); + } + } + } + } + } else { + pEntry = NULL; + uSub = 0; + } + +End: + + switch(uMsgOld) { + case WM_LBUTTONUP: + sNotify.hdr.code = TVN_LBUTTONUP; + break; + case WM_LBUTTONDOWN: + sNotify.hdr.code = NM_CLICK; + break; + case WM_LBUTTONDBLCLK: + sNotify.hdr.code = NM_DBLCLK; + break; + case WM_RBUTTONUP: + sNotify.hdr.code = TVN_RBUTTONUP; + break; + case WM_RBUTTONDOWN: + sNotify.hdr.code = NM_RCLICK; + break; + case WM_RBUTTONDBLCLK: + sNotify.hdr.code = NM_RDBLCLK; + break; + default + : + UNLOCK(pData); + return; + } + + if(pEntry) { + sNotify.itemNew.stateMask = 0xFFFFFFFF; + sNotify.itemNew.state = pEntry->uState; + sNotify.itemNew.lParam = pEntry->lParam; + sNotify.itemNew.cChildren = TVHT_SUBTOCOL(sInfo.flags); + } else { + sNotify.itemNew.stateMask = 0; + sNotify.itemNew.state = 0; + sNotify.itemNew.lParam = 0; + } + + sNotify.action = 0; + sNotify.itemNew.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_SUBNUMBER; + sNotify.itemNew.hItem = (HTREEITEM)uItem; + sNotify.itemNew.pszText = (LPTSTR) - 1; + sNotify.itemNew.cchTextMax = -1; + sNotify.itemOld.mask = 0; + sNotify.ptDrag.x = LOWORD(lParam); + sNotify.ptDrag.y = HIWORD(lParam); + + UNLOCK(pData); + + if(!SendNotify(pData, &sNotify.hdr)) { + if(uToggle) { // Aufklappen nur wenn Notify nicht abgefangen + LOCK(pData); + TreeListToggleItem(pData, uItem, 0); + UNLOCK(pData); + } + } + +} + +//***************************************************************************** +//* +//* TreeListChar +//* +//***************************************************************************** +// Eingabe von Buchstaben über die WM_CHAR Nachricht +// pData : Zeiger auf die Fensterdaten +// nChar : Ist das Zeichen das eingegeben wurde +static void TreeListChar(TreeListData *pData, UINT nChar, LPARAM lParam) { + + LPTSTR pName; + ExtraItem *pExtra; + BaseItem *pEntry; + unsigned uDelta; + unsigned uItem; + unsigned uTick; + unsigned uSub; + unsigned uPos; + unsigned uVal; + int iNum; + int iMax; + int i; + + if(nChar >= ' ') { + if(pData->cKeyIgnore) + return; // Taste soll ignoriert werden + + iMax = pData->uItemPosCount; + uSub = pData->uSelectedSub; + i = 0; + + if(TVIS_EDIT(pData->aColumn[uSub].bEdit)) // Bei AutoEdit keine Auswahl + if(!(pData->aColumn[uSub].bFlags & TVAE_ONLYRETURN)) { + TreeListStartAutoEdit(pData, uSub, nChar, 0); + return; + } + + if(TreeListStartNotifyEdit(pData, pData->uSelectedItem, uSub, nChar | VK_ISACHAR, lParam)) { + return; + } + + if(iMax <= 0) + return; + + iNum = pData->uSelectedItem; // Hole die Startzeile + if(iNum > 0) { + iNum = pData->pTreeItems[iNum]->uShowPos - 1; + if(iNum < 0) + iNum = 0; + } + + if((nChar >= 'a' && nChar <= 'z') || // In Grosbuchstaben umwandeln + (nChar >= 224 && nChar <= 254)) { + nChar -= 'a' - 'A'; + } + + if(!(pData->uStyleEx & TVS_EX_NOCHARSELCET)) + for(;;) { // Suche Anfangsbuchstaben + uTick = GetTickCount(); + uDelta = (uKeyPos > 0) ? 750 : 500; + + if(uKeyPos >= 3) + uDelta = 1000; + if(uTick - uKeyLast > uDelta) + uKeyPos = 0; + if(uKeyPos >= 16) + uKeyPos = 0; + if(uKeyPos == 1 && cKeyData[0] == (TCHAR)nChar) { + uKeyPos = 0; + } + + cKeyData[uKeyPos] = (TCHAR)nChar; + uKeyLast = uTick; + uKeyPos += 1; + + if(uKeyPos > 1) { + iNum--; + if(iNum < 0) + iNum = iMax - 1; + } + + if(iMax <= 0) + break; + + for(i = iNum + 1; i != iNum; i++) { // Suche Übereinstimmung + if(i >= iMax) {i = -1; continue;} + uItem = pData->pItemPos[i]; + + if(uSub) { +#ifndef __REACTOS__ + pExtra = pExtra = pData->pExtraItems[uSub - 1][uItem]; +#else + pExtra = pData->pExtraItems[uSub - 1][uItem]; +#endif + pName = (pExtra && pExtra->pText) ? pExtra->pText : _T(""); + } else { + pEntry = pData->pTreeItems[uItem]; + pName = (pEntry && pEntry->pText) ? pEntry->pText : _T(""); + } + + for(uPos = 0; uPos < uKeyPos; uPos++) { // Vergleiche die Texte +#if UNICODE + uVal = pName[uPos]; +#else + uVal = ((unsigned char *)pName)[uPos]; +#endif + + if((uVal >= 'a' && uVal <= 'z') || // In Grosbuchstaben umwandeln + (uVal >= 224 && uVal <= 254)) { + uVal -= 'a' - 'A'; + } + + if(cKeyData[uPos] != (TCHAR)uVal) { + break; + } + } + + if(uPos < uKeyPos) + continue; + + if(TreeListSelectItem(pData, uItem, uSub, TVC_UNKNOWN | TVC_DESELECT)) { + TreeListEnsureVisible(pData, uItem, uSub); + } + + return; + } + + if(i != iNum) + break; + if(uKeyPos <= 1) + break; + + iNum++; + if(iNum >= iMax) + iNum = iMax - 1; + + uKeyPos = 0; + } + } + +} + +//***************************************************************************** +//* +//* TreeListKeyDown +//* +//***************************************************************************** +// Soll ein Tastentruck ausgeführt werden. ACHTUNG UNLOCK wird ausgeführt +// pData : Zeiger auf die Fensterdaten +// wParam : WPARAM des Mausklicks +// lParam : LPARAM des Mausklicks +// Ergibt 0 wenn die Taste verarbeitet wurde +static void TreeListKeyDown(TreeListData *pData, WPARAM wParam, LPARAM lParam) { + + BaseItem *pTemp; + BaseItem *pEntry; + TV_KEYDOWN_EX sNotify; + unsigned uTemp; + unsigned uItem; + unsigned uStop; + unsigned uVal; + int iLineCmp; + int iOldLine; + int iOldCol; + int iFocus; + int iShift; + int iBase; + int iLine; + int iAdd; + int iPos; + int iSub; + int iCol; + int iMax; + int iDel; + int iScr; + int iSel; + + if(!pData->cIsEnabled) { // Ist das Fenster freigegeben + return; + } + + if(!pData->cHasFocus) { // Hat das Fenster den Focus + if(GetFocus() != pData->hWnd) { + UNLOCK(pData); + SetFocus(pData->hWnd); + LOCK(pData); + + if(GetFocus() != pData->hWnd) { + return; + } + } else { + pData->cHasFocus = 1; + } + } + + if(wParam == VK_RETURN) { // Wurde Enter gedrückt + UNLOCK(pData); + + sNotify.hdr.code = NM_RETURN; + sNotify.wVKey = (WORD)(wParam); + sNotify.wScan = (WORD)(wParam >> 16); + sNotify.flags = (UINT)(lParam); + + SendNotify(pData, &sNotify.hdr); + + iSub = pData->uSelectedSub; + + if(pData->aColumn[iSub].bEdit) { // Auto-Edit starten + if(GetAsyncKeyState(VK_CONTROL) & 0x8000) { + wParam = VK_ICONCLK; + lParam = 0; + } + + TreeListStartAutoEdit(pData, iSub, wParam, lParam); + } else { + TreeListStartNotifyEdit(pData, pData->uSelectedItem, iSub, wParam, lParam); + } + + return; + } + + pEntry = pData->pTreeItems[pData->uSelectedItem]; + iDel = (GetAsyncKeyState(VK_SHIFT) & 0x8000) ? 0 : TVC_DESELECT; + iScr = GetAsyncKeyState(VK_CONTROL) & 0x8000; + + if(iDel && iScr) // Ende oder Anfang wählen + if(wParam == VK_HOME || wParam == VK_END) + if(pData->uStyleEx & TVS_EX_HOMEENDSELECT) { + iDel = TVC_DESELECT; + iScr = 0; + } + + if(iDel && iScr) { // Das Fenster scrollen + iLine = pData->uScrollY; + iSub = pData->uScrollX; + + switch(wParam) { + case VK_END: + iLine = pData->uTreeItemsCount; // Ans Ende scrollen + iLine -= pData->uPageEnties; + break; + + case VK_HOME: + iLine = 0; // Zum Anfang scrollen + break; + + case VK_LEFT: + iSub -= 16; // Links scrollen + break; + + case VK_RIGHT: + iSub += 16; // Rechts scrollen + break; + + case VK_UP: + iLine--; // Nach oben scrollen + break; + + case VK_DOWN: + iLine++; // Nach unten scrollen + break; + + case VK_PRIOR: + iLine -= pData->uSizeX; // Eine Seite nach oben + break; + + case VK_NEXT: + iLine += pData->uSizeX; // Eine Seite nach unten + break; + + case VK_SPACE: + if(pEntry == NULL) + break; // Expantieren und schließen + if(pEntry->bFlags & TVIX_HASBUTTON) { + TreeListToggleItem(pData, pData->uSelectedItem, 0); + } + + break; + + default + : + goto Exit; + } + + if(iLine != (int)pData->uScrollY) { + iMax = pData->uItemPosCount; + iMax -= pData->uPageEnties - 1; + + if(iLine >= iMax) + iLine = iMax; + if(iLine < 0) + iLine = 0; + if(iLine != (int)pData->uScrollY) + if(!(pData->uStyle & TVS_NOSCROLL)) { + pData->uScrollY = iLine; + SetScrollPos(pData->hWnd, SB_VERT, iLine, TRUE); + UpdateView(pData); + } + } + + if(iSub != (int)pData->uScrollX) { + uVal = pData->uColumnCount; + if(uVal) + iMax = pData->aColumnXpos[uVal] - pData->uSizeX / 2; + else + iMax = pData->iMaxSizeX; + iMax -= pData->uSizeX - pData->uSizeX / 2; + + if(iSub >= iMax) + iSub = iMax; + if(iSub < 0) + iSub = 0; + if(iSub != (int)pData->uScrollX) + if(!(pData->uStyle & TVS_NOSCROLL)) { + pData->uScrollX = iSub; + SetScrollPos(pData->hWnd, SB_HORZ, iSub, TRUE); + UpdateView(pData); + + if(pData->hHeader) { + MoveWindow(pData->hHeader, -iSub, 0, pData->uSizeX + iSub, pData->uStartPixel, TRUE); + } + } + } + } else { // Einen anderen Eintrag auswählen + iSub = pData->uSelectedSub; + iCol = pData->aColumn[iSub].bIndex; + + if(!pEntry) { + iLine = 0; + iCol = 0; + iOldCol = 0; + iOldLine = -1; + + uTemp = pData->uFocusItem; + if(uTemp) { // Ist ein Focus definiert + pTemp = pData->pTreeItems[uTemp]; + + while(pTemp && !pTemp->uShowPos) { + pTemp = pData->pTreeItems[pTemp->uParent]; + } + + if(pTemp) { + iLine = pTemp->uShowPos - 1; + iSub = pData->uFocusSub; + iCol = pData->aColumn[iSub].bIndex; + } + + if(pData->uSelectedCount > 1) { + iOldCol = iCol; + iOldLine = iLine; + } else { + TreeListRemoveFocus(pData); + } + } + } else { + iLine = pEntry->uShowPos - 1; + if(iLine < 0) + iLine = 0; + + uTemp = pData->uSelectedItem; + iOldLine = iLine; + iOldCol = iCol; + } + + switch(wParam) { + case VK_END: + iLine = pData->uItemPosCount - 1; + break; + case VK_HOME: + iLine = 0; + break; + case VK_LEFT: + if(pData->uColumnCount > 1 && (pData->uStyleEx & TVS_EX_SUBSELECT)) { + while(iCol > 0) { + iCol--; + iSub = pData->aColumnPos[iCol]; + if(pData->aColumn[iSub].sReal > 0) + break; + } + } else { + if(! pEntry) + break; + if(!(pEntry->uState & TVIS_EXPANDED) || !pEntry->uFirstChild) { + pTemp = pData->pTreeItems[pEntry->uParent]; + if(pTemp) + iLine = pTemp->uShowPos - 1; + break; + } + + if(pEntry->bFlags & TVIX_HASBUTTON) { + TreeListToggleItem(pData, pData->uSelectedItem, 0); + } + } + + break; + + case VK_RIGHT: + if(pData->uColumnCount > 1 && (pData->uStyleEx & TVS_EX_SUBSELECT)) { + while(iCol + 1 < (int)pData->uColumnCount) { + iCol++; + iSub = pData->aColumnPos[iCol]; + if(pData->aColumn[iSub].sReal > 0) + break; + } + } else { + if(!pEntry) + break; + if(pEntry->uState & TVIS_EXPANDED) { + iLine++; + break; + } + + if(pEntry->bFlags & TVIX_HASBUTTON) { + TreeListToggleItem(pData, pData->uSelectedItem, 0); + } + } + + break; + + case VK_UP: + iLine--; + break; + case VK_DOWN: + iLine++; + break; + + case VK_PRIOR: + iAdd = pData->uPageEnties - 1; // Eine Seite nach oben + if(iAdd <= 0) + iAdd = 1; + iLine -= iAdd; + break; + + case VK_NEXT: + iAdd = pData->uPageEnties - 1; // Eine Seite nach unten + if(iAdd <= 0) + iAdd = 1; + iLine += iAdd; + break; + + case VK_BACK: // Eine Ebene höher + if(pEntry) { + uItem = pEntry->uParent; + if(!uItem) + iLine = 0; + else { + iLine = pData->pTreeItems[uItem]->uShowPos - 1; + if(iLine < 0) + iLine = 0; + } + } + + break; + + case VK_SPACE: + if(pEntry && iCol == 0 && (pData->uStyle & TVS_CHECKBOXES)) { + TreeListChangeCheckbox(pData, pData->uSelectedItem, 0, 0); + } + + break; + + default + : + goto Exit; + } + + if(iCol >= (int)pData->uColumnCount) + iCol = pData->uColumnCount - 1; + if(iCol < 0) + iCol = 0; + if(iLine >= (int)pData->uItemPosCount) + iLine = pData->uItemPosCount - 1; + if(iLine < 0) + iLine = 0; + + if(!(pData->uStyleEx & TVS_EX_SUBSELECT)) + iCol = 0; + + if(pData->uItemPosCount > 0) + if(iLine != iOldLine || iCol != iOldCol) { // Wurde ein anderer Eintrag ausgewählt + if(pData->uSelectedCount > 1) { + TreeListRemoveFocus(pData); + } else { + pData->uSelectedBase = uTemp; + } + + iShift = iDel; + + if(pData->cReSelect) { // Die Shift-Auswahl neu erstellen + iDel = TVC_DESELECT; + } + + uItem = pData->pItemPos [iLine]; + pTemp = pData->pTreeItems[uItem]; + iSub = pData->aColumnPos[iCol ]; + iFocus = (pTemp && (pTemp->uState & TVIS_SELECTED)) ? TVC_ONLYFOCUS : 0; + + TreeListSelectItem(pData, uItem, iSub, TVC_BYKEYBOARD | iDel | iFocus); + + if(!(pData->uStyle & TVS_NOSCROLL)) { + TreeListEnsureVisible(pData, pData->uSelectedItem, pData->uSelectedSub); + } + + if(pData->cReSelect && pData->uSelectedBase) { // Shift-Select neu auswählen + uTemp = pData->uSelectedBase; + pTemp = pData->pTreeItems[uTemp]; + uStop = uItem; + + while(pTemp && pTemp->uShowPos == 0) { + pTemp = pData->pTreeItems[pTemp->uParent]; + if(!pTemp) + break; + } + + if(pTemp && !iShift) { + if(!(pTemp->uState & TVIS_SELECTED)) { + TreeListXorSelectItem(pData, uTemp, TVC_BYKEYBOARD); + } + + iLineCmp = pTemp->uShowPos - 1; + if(iLineCmp < iLine) { + uStop = uItem; + } else { + uStop = uTemp; + uTemp = uItem; + } + + for(;;) { + uTemp = TreeListNextUnselUntil(pData, uTemp, uStop); + if(!uTemp) + break; + TreeListXorSelectItem(pData, uTemp, TVC_BYKEYBOARD); + } + } + + pData->cReSelect = 0; + } + + if((pData->uStyleEx & TVS_EX_MULTISELECT) && !iDel) { + uVal = pData->uSelectedBase; + pTemp = pData->pTreeItems[uVal]; + iBase = (pTemp) ? pTemp->uShowPos - 1 : -1; + + if(iLine > iOldLine) { // Nach oben oder nach unten + iMax = (iOldLine > iBase) ? iOldLine + 1 : iOldLine; + if(iMax < 0) + iMax = 0; + iPos = iLine; + } else { + iMax = iLine; + iPos = (iOldLine < iBase) ? iOldLine - 1 : iOldLine; + if(iPos < 0) + iPos = 0; + } + + for(; iPos >= iMax; iPos--) { // Übersprungene Einträge mit aus/abwählen + uItem = pData->pItemPos[iPos]; + + if(iPos != iLine && iPos != iBase) { + if(!TreeListXorSelectItem(pData, uItem, TVC_BYKEYBOARD)) + continue; + } + + pTemp = pData->pTreeItems[uItem]; // Auch unsichtbare Kinder abwählen + if(pTemp) { + iSel = pTemp->uState & TVIS_SELECTED; + pTemp = pData->pTreeItems[pTemp->uFirstChild]; + if(!pTemp || pTemp->uShowPos) + continue; + if(!iSel) { + for(uTemp = uItem;;) { + uTemp = TreeListNextSelUntil(pData, uTemp, uItem); + if(!uTemp) + break; + TreeListXorSelectItem(pData, uTemp, TVC_BYKEYBOARD); + } + } else { + for(uTemp = uItem;;) { + uTemp = TreeListNextUnselUntil(pData, uTemp, uItem); + if(!uTemp) + break; + TreeListXorSelectItem(pData, uTemp, TVC_BYKEYBOARD); + } + } + } + } + } + } + } + +Exit: + + sNotify.hdr.code = TVN_KEYDOWN; + sNotify.wVKey = (WORD)(wParam); + sNotify.wScan = (WORD)(wParam >> 16); + sNotify.flags = (UINT)(lParam); + + UNLOCK(pData); + + pData->cKeyIgnore = (char)SendNotify(pData, &sNotify.hdr); + +} + +//***************************************************************************** +//* +//* TreeListSortItemsEx +//* +//***************************************************************************** +// Sortiert die Kindereinträge eines Eintrages mitteles einer Funktion +// pData : Zeiger auf die Fensterdaten +// pSortData : Ist ein Zeiger auf die Sortiertaten +// iMode : 1=Rekursiv sortieren +// Ergibt 1 wenn ok sonst 0 +static int TreeListSortItemsEx(TreeListData *pData, TV_SORTEX *pSortData, int iMode) { + + TV_SORTEX sSort; + unsigned uNum; + unsigned uItem; + unsigned uLast; + unsigned uFirst; + unsigned uParent; + BaseItem *pParent; + BaseItem *pEntry; + BaseItem *pNext; + BaseItem **pList; + + unsigned *pItemList; + unsigned *pItemNew; + unsigned uEnties[128]; + unsigned uPos; + unsigned uMax; + + PFNTVCOMPAREEX pCompare; + HTREEITEM hItemTemp; + LPARAM lParamTemp; + LPARAM lParamSort; + HWND hTreeWnd; + int iLower, iUpper, iMiddle, iCmp; + int uMemL[30], uMemU[30]; + int iStart, iLast; + int iLevel; + unsigned uTemp; + + + pList = pData->pTreeItems; + + uParent = U(pSortData->hParent); + if(uParent > pData->uTreeItemsMax) { // Root-Eintrag sortieren + if(uParent != U(TVI_ROOT)) + return 0; + + uLast = pData->uLastChild; + uFirst = pData->uFirstChild; + if(uFirst == 0) + return 1; + if(uFirst == uLast) { // Einzelner Eintrag + pNext = pList[uFirst]; + if(!pNext->uFirstChild) + return 1; + + sSort.hParent = (HTREEITEM)uFirst; + sSort.lParam = pSortData->lParam; + sSort.lpfnCompare = pSortData->lpfnCompare; + TreeListSortItemsEx(pData, &sSort, iMode); + return 1; + } + + pParent = 0; + uParent = 0; + } else { // Untereintrag sortieren + pParent = pList[uParent]; + if(pParent == NULL) + return 0; + + uLast = pParent->uLastChild; + uFirst = pParent->uFirstChild; + if(uFirst == 0) + return 1; + if(uFirst == uLast) { // Einzelner Eintrag + pNext = pList[uFirst]; + if(!pNext->uFirstChild) + return 1; + + sSort.hParent = (HTREEITEM)uFirst; + sSort.lParam = pSortData->lParam; + sSort.lpfnCompare = pSortData->lpfnCompare; + TreeListSortItemsEx(pData, &sSort, iMode); + return 1; + } + } + + if(iMode) { // Sortiere die Untereinträge + pNext = pList[uFirst]; + sSort.hParent = (HTREEITEM)uFirst; + sSort.lParam = pSortData->lParam; + sSort.lpfnCompare = pSortData->lpfnCompare; + + while(pNext) { + if(pNext->uFirstChild) { + TreeListSortItemsEx(pData, &sSort, SORT_NOUPDATE); + } + + sSort.hParent = (HTREEITEM)pNext->uNextItem; + pNext = pList[pNext->uNextItem]; + } + } + + if(uFirst == uLast) + return 1; + +//******************** Erzeuge Eintragsliste ********************************** + uItem = uFirst; + pItemList = uEnties; + uMax = 128; + uPos = 0; + + do { // Alle Kindeinträge suchen + if(uPos >= uMax) { + uMax *= 2; + pItemNew = new(unsigned, uMax); + memcpy(pItemNew, pItemList, uPos * sizeof(pItemList[0])); + if(uPos > 128) + delete(pItemList); + pItemList = pItemNew; + } + + pItemList[uPos] = uItem; + pNext = pList[uItem]; + uItem = pNext->uNextItem; + uPos++; + } while(uItem); + + +//************************* Qsort-Algorithmus ********************************* +#define XCHANGE_MEM(a,b) uTemp=pItemList[a];pItemList[a]=pItemList[b];pItemList[b]=uTemp; + + pData->cLockChanges = 1; + UNLOCK(pData); + + hTreeWnd = pData->hWnd; + pCompare = pSortData->lpfnCompare; + lParamSort = pSortData->lParam; + iLast = uPos - 1; + iStart = 0; + iLevel = 0; + + for(;;) { + iLower = iStart; + iMiddle = (iStart + iLast) >> 1; // Mitte bereichnen + iUpper = iLast + 1; + + XCHANGE_MEM(iMiddle, iLower); + + uItem = pItemList[iStart]; + hItemTemp = (HTREEITEM)uItem; + lParamTemp = pList[uItem]->lParam; + + for(;;) { + do { + iLower++; + if(iLower > iLast) + break; + uItem = pItemList[iLower]; + iCmp = pCompare(hTreeWnd, (HTREEITEM)uItem, hItemTemp, pList[uItem]->lParam, lParamTemp, lParamSort); + } while(iCmp <= 0); + + do { + iUpper--; + if(iUpper <= iStart) + break; + uItem = pItemList[iUpper]; + iCmp = pCompare(hTreeWnd, (HTREEITEM)uItem, hItemTemp, pList[uItem]->lParam, lParamTemp, lParamSort); + } while(iCmp >= 0); + + if(iUpper < iLower) + break; + + XCHANGE_MEM(iUpper, iLower); + } + + XCHANGE_MEM(iStart, iUpper); + + if(iUpper - 1 - iStart >= iLast - iLower) { + if(iStart + 1 < iUpper) { + uMemL[iLevel] = iStart; + uMemU[iLevel] = iUpper - 1; + iLevel++; + } + if(iLower < iLast) { + iStart = iLower; + continue; + } + } else { + if(iLower < iLast) { + uMemL[iLevel] = iLower; + uMemU[iLevel] = iLast; + iLevel++; + } + + if(iStart + 1 < iUpper) { + iLast = iUpper - 1; + continue; + } + } + // Eine Ebene absteigen + iLevel--; + + if(iLevel >= 0) { // Noch Ebenen vorhanden + iStart = uMemL[iLevel]; + iLast = uMemU[iLevel]; + continue; + } + + break; + } + + LOCK(pData); + pData->cLockChanges = 0; + +//******************** Einträge neu einsortirenen ***************************** + uPos--; + + pEntry = pList[uParent]; + if(!pEntry) { + pData->uFirstChild = pItemList[ 0 ]; + pData->uLastChild = pItemList[uPos]; + } else { + pEntry->uFirstChild = pItemList[ 0 ]; + pEntry->uLastChild = pItemList[uPos]; + } + + uLast = 0; + uItem = pItemList[0]; + + for(uNum = 0; uNum < uPos;) { // Kinder neu einhängen + pEntry = pList[uItem]; + pEntry->uPrevItem = uLast; + + uNum++; + uLast = uItem; + uItem = pItemList[uNum]; + + pEntry->uNextItem = uItem; + } + + pEntry = pList[uItem]; + pEntry->uPrevItem = uLast; + pEntry->uNextItem = 0; + + if(iMode != SORT_NOUPDATE) // Ausgabeliste neuerstellen + if(uParent == 0 || (pParent->uShowPos && (pParent->uState & TVIS_EXPANDED))) { + UpdateItems(pData, uParent); + + if(pData->uStyle & TVS_SHOWSELALWAYS) + if(pData->uSelectedItem) { + TreeListEnsureVisible(pData, pData->uSelectedItem, pData->uSelectedSub); + } + } + + if(uMax > 128) + delete(pItemList); + + return 1; +} + +//***************************************************************************** +//* +//* TreeListSortItemsCb +//* +//***************************************************************************** +// Sortiert die Kindereinträge eines Eintrages mitteles einer Funktion +// pData : Zeiger auf die Fensterdaten +// pSortData : Ist ein Zeiger auf die Sortiertaten +// iMode : 1=Rekursiv sortieren +// Ergibt 1 wenn ok sonst 0 +static int TreeListSortItemsCb(TreeListData *pData, TV_SORTCB *pSortData, int iMode) { + + TV_SORTCB sSort; + unsigned uNum; + unsigned uItem; + unsigned uLast; + unsigned uFirst; + unsigned uParent; + BaseItem *pParent; + BaseItem *pEntry; + BaseItem *pNext; + BaseItem **pList; + + unsigned *pItemList; + unsigned *pItemNew; + unsigned uEnties[128]; + unsigned uPos; + unsigned uMax; + + PFNTVCOMPARE pCompare; + LPARAM lParamTemp; + LPARAM lParamSort; + int iLower, iUpper, iMiddle, iCmp; + int uMemL[30], uMemU[30]; + int iStart, iLast; + int iLevel; + unsigned uTemp; + + pList = pData->pTreeItems; + + uParent = U(pSortData->hParent); + if(uParent > pData->uTreeItemsMax) { // Root-Eintrag sortieren + if(uParent != U(TVI_ROOT)) + return 0; + + uLast = pData->uLastChild; + uFirst = pData->uFirstChild; + if(uFirst == 0) + return 1; + if(uFirst == uLast) { // Einzelner Eintrag + pNext = pList[uFirst]; + if(!pNext->uFirstChild) + return 1; + + sSort.hParent = (HTREEITEM)uFirst; + sSort.lParam = pSortData->lParam; + sSort.lpfnCompare = pSortData->lpfnCompare; + TreeListSortItemsCb(pData, &sSort, iMode); + return 1; + } + + pParent = 0; + uParent = 0; + } else { // Untereintrag sortieren + pParent = pList[uParent]; + if(pParent == NULL) + return 0; + + uLast = pData->uLastChild; + uFirst = pParent->uFirstChild; + if(uFirst == 0) + return 1; + if(uFirst == uLast) { // Einzelner Eintrag + pNext = pList[uFirst]; + if(!pNext->uFirstChild) + return 1; + + sSort.hParent = (HTREEITEM)uFirst; + sSort.lParam = pSortData->lParam; + sSort.lpfnCompare = pSortData->lpfnCompare; + TreeListSortItemsCb(pData, &sSort, iMode); + return 1; + } + } + + if(iMode) { // Sortiere die Untereinträge + pNext = pList[uFirst]; + sSort.hParent = (HTREEITEM)uFirst; + sSort.lParam = pSortData->lParam; + sSort.lpfnCompare = pSortData->lpfnCompare; + + while(pNext) { + if(pNext->uFirstChild) { + TreeListSortItemsCb(pData, &sSort, SORT_NOUPDATE); + } + + sSort.hParent = (HTREEITEM)pNext->uNextItem; + pNext = pList[pNext->uNextItem]; + } + } + + if(uFirst == uLast) + return 1; + +//******************** Erzeuge Eintragsliste ********************************** + uItem = uFirst; + pItemList = uEnties; + uMax = 128; + uPos = 0; + + do { // Alle Kindeinträge suchen + if(uPos >= uMax) { + uMax *= 2; + pItemNew = new(unsigned, uMax); + memcpy(pItemNew, pItemList, uPos * sizeof(pItemList[0])); + if(uPos > 128) + delete(pItemList); + pItemList = pItemNew; + } + + pItemList[uPos] = uItem; + pNext = pList[uItem]; + uItem = pNext->uNextItem; + uPos++; + } while(uItem); + +//************************* Qsort-Algorithmus ********************************* +#define XCHANGE_MEM(a,b) uTemp=pItemList[a];pItemList[a]=pItemList[b];pItemList[b]=uTemp; + + pData->cLockChanges = 1; + UNLOCK(pData); + + pCompare = pSortData->lpfnCompare; + lParamSort = pSortData->lParam; + iLast = uPos - 1; + iStart = 0; + iLevel = 0; + + for(;;) { + iLower = iStart; + iMiddle = (iStart + iLast) >> 1; // Mitte bereichnen + iUpper = iLast + 1; + + XCHANGE_MEM(iMiddle, iLower); + + uItem = pItemList[iStart]; + lParamTemp = pList[uItem]->lParam; + + for(;;) { + do { + iLower++; + if(iLower > iLast) + break; + uItem = pItemList[iLower]; + iCmp = pCompare(pList[uItem]->lParam, lParamTemp, lParamSort); + } while(iCmp <= 0); + + do { + iUpper--; + if(iUpper <= iStart) + break; + uItem = pItemList[iUpper]; + iCmp = pCompare(pList[uItem]->lParam, lParamTemp, lParamSort); + } while(iCmp >= 0); + + if(iUpper < iLower) + break; + + XCHANGE_MEM(iUpper, iLower); + } + + XCHANGE_MEM(iStart, iUpper); + + if(iUpper - 1 - iStart >= iLast - iLower) { + if(iStart + 1 < iUpper) { + uMemL[iLevel] = iStart; + uMemU[iLevel] = iUpper - 1; + iLevel++; + } + if(iLower < iLast) { + iStart = iLower; + continue; + } + } else { + if(iLower < iLast) { + uMemL[iLevel] = iLower; + uMemU[iLevel] = iLast; + iLevel++; + } + + if(iStart + 1 < iUpper) { + iLast = iUpper - 1; + continue; + } + } + // Eine Ebene absteigen + iLevel--; + + if(iLevel >= 0) { // Noch Ebenen vorhanden + iStart = uMemL[iLevel]; + iLast = uMemU[iLevel]; + continue; + } + + break; + } + + LOCK(pData); + pData->cLockChanges = 0; + +//******************** Einträge neu einsortirenen ***************************** + uPos--; + + pEntry = pList[uParent]; + if(!pEntry) { + pData->uFirstChild = pItemList[ 0 ]; + pData->uLastChild = pItemList[uPos]; + } else { + pEntry->uFirstChild = pItemList[ 0 ]; + pEntry->uLastChild = pItemList[uPos]; + } + + uLast = 0; + uItem = pItemList[0]; + + for(uNum = 0; uNum < uPos;) { // Kinder neu einhängen + pEntry = pList[uItem]; + pEntry->uPrevItem = uLast; + + uNum++; + uLast = uItem; + uItem = pItemList[uNum]; + + pEntry->uNextItem = uItem; + } + + pEntry = pList[uItem]; + pEntry->uPrevItem = uLast; + pEntry->uNextItem = 0; + + if(iMode != SORT_NOUPDATE) // Ausgabeliste neuerstellen + if(uParent == 0 || (pParent->uShowPos && (pParent->uState & TVIS_EXPANDED))) { + UpdateItems(pData, uParent); + + if(pData->uStyle & TVS_SHOWSELALWAYS) + if(pData->uSelectedItem) { + TreeListEnsureVisible(pData, pData->uSelectedItem, pData->uSelectedSub); + } + } + + if(uMax > 128) + delete(pItemList); + + return 1; +} + +//***************************************************************************** +//* +//* TreeListSortItems +//* +//***************************************************************************** +// Sortiert die Kindereinträge eines Eintrages via Text +// pData : Zeiger auf die Fensterdaten +// pSortData : Ist ein Zeiger auf die Sortiertaten +// iMode : 1=Rekursiv sortieren +// Ergibt 1 wenn ok sonst 0 +static int TreeListSortItems(TreeListData *pData, unsigned uParent, int iMode) { + + unsigned uNum; + unsigned uItem; + unsigned uLast; + unsigned uFirst; + BaseItem *pParent; + BaseItem *pEntry; + BaseItem *pNext; + BaseItem **pList; + + unsigned *pItemList; + unsigned *pItemNew; + unsigned uEnties[128]; + unsigned uPos; + unsigned uMax; + + LPCTSTR pTextTemp; + LPCTSTR pText; + int iLower, iUpper, iMiddle, iCmp; + int uMemL[30], uMemU[30]; + int iStart, iLast; + int iLevel; + int iNone; + unsigned uSize; + unsigned uTemp; + + pList = pData->pTreeItems; + + if(uParent > pData->uTreeItemsMax) { // Root-Eintrag sortieren + if(uParent != U(TVI_ROOT)) + return 0; + + uLast = pData->uLastChild; + uFirst = pData->uFirstChild; + if(uFirst == 0) + return 1; + if(uFirst == pData->uLastChild) { // Einzelner Eintrag + pNext = pList[uFirst]; + if(!pNext->uFirstChild) + return 1; + + TreeListSortItems(pData, uFirst, iMode); + return 1; + } + + pList = pData->pTreeItems; + pParent = 0; + uParent = 0; + } else { // Untereintrag sortieren + pParent = pList[uParent]; + if(pParent == NULL) + return 0; + + uLast = pParent->uLastChild; + uFirst = pParent->uFirstChild; + if(uFirst == 0) + return 1; + if(uFirst == pParent->uLastChild) { // Einzelner Eintrag + pNext = pList[uFirst]; + if(!pNext->uFirstChild) + return 1; + + TreeListSortItems(pData, uFirst, iMode); + return 1; + } + } + + if(iMode) { // Sortiere die Untereinträge + uItem = uFirst; + + while(uItem) { + pNext = pList[uItem]; + if(pNext->uFirstChild) { + TreeListSortItems(pData, uItem, SORT_NOUPDATE); + } + + uItem = pNext->uNextItem; + } + } + + if(uLast == uFirst) + return 1; + +//******************** Erzeuge Eintragsliste ********************************** + uItem = uFirst; + pItemList = uEnties; + uMax = 128; + uPos = 0; + + do { // Alle Kindeinträge suchen + if(uPos >= uMax) { + uMax *= 2; + pItemNew = new(unsigned, uMax); + memcpy(pItemNew, pItemList, uPos * sizeof(pItemList[0])); + if(uPos > 128) + delete(pItemList); + pItemList = pItemNew; + } + + pItemList[uPos] = uItem; + pNext = pList[uItem]; + uItem = pNext->uNextItem; + uPos++; + } while(uItem); + +//************************* Qsort-Algorithmus ********************************* +#define XCHANGE_MEM(a,b) uTemp=pItemList[a];pItemList[a]=pItemList[b];pItemList[b]=uTemp; + + pData->cLockChanges = 1; + UNLOCK(pData); + + iLast = uPos - 1; + iStart = 0; + iLevel = 0; + + for(;;) { + iLower = iStart; + iMiddle = (iStart + iLast) >> 1; // Mitte bereichnen + iUpper = iLast + 1; + + XCHANGE_MEM(iMiddle, iLower); + + uItem = pItemList[iStart]; + pEntry = pList[uItem]; + if(pEntry->bCallback & TVIF_TEXT) { + uSize = 0; + LOCK(pData); + CallbackEntry(pData, pEntry, uItem, TVIF_TEXT, &iNone, &uSize, &pTextTemp); + UNLOCK(pData); + } else { + pTextTemp = pEntry->pText; + } + + for(;;) { + do { + iLower++; + if(iLower > iLast) + break; + + uItem = pItemList[iLower]; + pEntry = pList[uItem]; + if(pEntry->bCallback & TVIF_TEXT) { + uSize = 1; + LOCK(pData); + CallbackEntry(pData, pEntry, uItem, TVIF_TEXT, &iNone, &uSize, &pText); + UNLOCK(pData); + } else { + pText = pEntry->pText; + } + + iCmp = str_icmp(pText, pTextTemp); + } while(iCmp <= 0); + + do { + iUpper--; + + if(iUpper <= iStart) + break; + uItem = pItemList[iUpper]; + pEntry = pList[uItem]; + if(pEntry->bCallback & TVIF_TEXT) { + LOCK(pData); + CallbackEntry(pData, pEntry, uItem, TVIF_TEXT, &iNone, &uSize, &pText); + UNLOCK(pData); + } else { + pText = pEntry->pText; + } + + iCmp = str_icmp(pText, pTextTemp); + } while(iCmp >= 0); + + if(iUpper < iLower) + break; + + XCHANGE_MEM(iUpper, iLower); + } + + XCHANGE_MEM(iStart, iUpper); + + if(iUpper - 1 - iStart >= iLast - iLower) { + if(iStart + 1 < iUpper) { + uMemL[iLevel] = iStart; + uMemU[iLevel] = iUpper - 1; + iLevel++; + } + if(iLower < iLast) { + iStart = iLower; + continue; + } + } else { + if(iLower < iLast) { + uMemL[iLevel] = iLower; + uMemU[iLevel] = iLast; + iLevel++; + } + + if(iStart + 1 < iUpper) { + iLast = iUpper - 1; + continue; + } + } + // Eine Ebene absteigen + iLevel--; + + if(iLevel >= 0) { // Noch Ebenen vorhanden + iStart = uMemL[iLevel]; + iLast = uMemU[iLevel]; + continue; + } + + break; + } + + LOCK(pData); + pData->cLockChanges = 0; + +//******************** Einträge neu einsortirenen ***************************** + uPos--; + + pEntry = pList[uParent]; + if(!pEntry) { + pData->uFirstChild = pItemList[ 0 ]; + pData->uLastChild = pItemList[uPos]; + } else { + pEntry->uFirstChild = pItemList[ 0 ]; + pEntry->uLastChild = pItemList[uPos]; + } + + uLast = 0; + uItem = pItemList[0]; + + for(uNum = 0; uNum < uPos;) { // Kinder neu einhängen + pEntry = pList[uItem]; + pEntry->uPrevItem = uLast; + + uNum++; + uLast = uItem; + uItem = pItemList[uNum]; + + pEntry->uNextItem = uItem; + } + + pEntry = pList[uItem]; + pEntry->uPrevItem = uLast; + pEntry->uNextItem = 0; + + if(iMode != SORT_NOUPDATE) // Ausgabeliste neuerstellen + if(uParent == 0 || (pParent->uShowPos && (pParent->uState & TVIS_EXPANDED))) { + UpdateItems(pData, uParent); + + if(pData->uStyle & TVS_SHOWSELALWAYS) + if(pData->uSelectedItem) { + TreeListEnsureVisible(pData, pData->uSelectedItem, pData->uSelectedSub); + } + } + + if(uMax > 128) + delete(pItemList); + + return 1; +} + +//***************************************************************************** +//* +//* TreeListEndLabelEdit +//* +//***************************************************************************** +// Beendet das editiert eines Eintrages +// pData : Zeiger auf Fensterdaten +// iMode : Gibt an wie das Editieren beendet wurde +// 0 = Es gab es einen Abbruch mit ESC +// 1 = Eingabe ohne Enter +// 2 = Eingabe mit Enter +// Ergibt das Handle des Edit-Controlls oder NULL bei einem Fehler +static int TreeListEndLabelEdit(TreeListData *pData, int iMode) { + TCHAR cText[2052]; + NMTVDISPINFO sNotify; + TV_ITEM sSet; + LRESULT lRet; + unsigned uSub; + unsigned uItem; + ExtraItem *pExtra; + BaseItem *pEntry; + LPCTSTR *pList; + LPCTSTR pText; + LPTSTR pGetT; + TCHAR cChar; + int iAuto; + int iIcon; + int iLine; + int iPos; + int iLen; + int iMax; + int iSel; + char cCb; + + uItem = pData->uEditItem; + uSub = pData->uEditSub; + cCb = pData->cEditCb; + + pData->uEditItem = 0; + pData->uEditSub = 0; + pData->cEditCb = 0; + + if(uItem > pData->uTreeItemsMax || uSub >= pData->uColumnCount) { + return 0; + } + + pEntry = pData->pTreeItems[uItem]; + if(!pEntry) { + return 0; + } + + if(iMode) { // Eingabe + GetWindowText(pData->hEdit, cText, sizeof(cText) / sizeof(cText[0])); + cText[sizeof(cText) / sizeof(cText[0]) - 1] = 0; + sNotify.item.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_TEXT | TVIF_SUBITEM; + sNotify.item.cchTextMax = sizeof(cText) / sizeof(cText[0]) - 1; + sNotify.item.pszText = cText; + + if(pData->cColumnStart) { // Wurde ein Text eingegeben + sNotify.item.mask |= TVIF_TEXTCHANGED; + } + + if(iMode == 2) { // Wurde der Text mit RETURN eingegeben + sNotify.item.mask |= TVIF_RETURNEXIT; + } + } else { // Abbruch + sNotify.item.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_CANCELED | TVIF_SUBITEM; + sNotify.item.pszText = NULL; + sNotify.item.cchTextMax = 0; + } + + sNotify.hdr.code = TVN_ENDLABELEDIT; + sNotify.item.hItem = (HTREEITEM)uItem; + sNotify.item.stateMask = 0xFFFFFFFF; + sNotify.item.cChildren = uSub; + + if(uSub) { // Wurde der Text in einer Sub-Spalte geändert + pExtra = pData->pExtraItems[uSub - 1][uItem]; + if(!pExtra) { + sNotify.item.state = 0; + sNotify.item.lParam = 0; + } else { + sNotify.item.state = pEntry->uState & TVIS_BASEFLAGS; + sNotify.item.state |= pExtra->uState; + sNotify.item.lParam = pEntry->lParam; + } + } else { + sNotify.item.state = pEntry->uState; + sNotify.item.lParam = pEntry->lParam; + } + + UNLOCK(pData); + ShowWindow(pData->hEdit, SW_HIDE); + lRet = SendNotify(pData, &sNotify.hdr); + LOCK(pData); + + if(lRet || !iMode) + return 0; + + if(cCb) { // Callback aufrufen + sNotify.hdr.code = TVN_SETDISPINFO; + sNotify.item.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_TEXT | TVIF_SUBITEM; + sNotify.item.stateMask = (UINT)~TVIS_BASEFLAGS; + sNotify.item.hItem = (HTREEITEM)uItem; + sNotify.item.cChildren = uSub; + + UNLOCK(pData); + SendNotify(pData, &sNotify.hdr); + LOCK(pData); + } else { // Neuen Text eingeben + pGetT = sNotify.item.pszText; + sSet.mask = TVIF_SUBITEM | TVIF_TEXT; + sSet.cchTextMax = sNotify.item.cchTextMax; + sSet.hItem = (HTREEITEM)uItem; + sSet.pszText = pGetT; + sSet.cChildren = uSub; + + iIcon = pData->aColumn[uSub].iCbIcon; + iAuto = pData->aColumn[uSub].bEdit; + + if(iIcon >= 0 && iAuto != TVAX_NONE) { // Auch ein Icon zuweisen + iPos = -1; + + if((1 << iAuto) & ((1 << TVAX_CBLIST) | (1 << TVAX_COMBO))) { + iPos = (int)SendMessage(pData->hEdit, CB_GETCURSEL, 0, 0); + sSet.mask |= TVIF_IMAGE | TVIF_SELECTEDIMAGE; + sSet.iImage = iIcon + iPos; + sSet.iSelectedImage = sSet.iImage; + } + + if(iPos <= -1) { + iMax = pData->aColumn[uSub].bCbSize; + if(iMax <= 0) + iMax = 1024; + + iSel = -1; + + if(pData->aColumn[uSub].bFlags & TVAE_PTRLIST) { // Zeigerliste char *pTexte[]={"1","2",NULL}; + pList = (LPCTSTR *)pData->aColumn[uSub].pCbData; + + for(iPos = 0; iPos < iMax; iPos++) { + if(!pList[iPos]) + break; + if(!str_cmp(pGetT, pList[iPos])) + iSel = iPos; + } + } else { // Textliste char *pText="1|2|3"; + pText = (LPTSTR)pData->aColumn[uSub].pCbData; + cChar = (TCHAR)pData->aColumn[uSub].bCbChar; + + for(iPos = 0; iPos < iMax; iPos++) { + for(iLen = 0; pText[iLen]; iLen++) { + if(pText[iLen] == cChar) + break; + } + + if(str_ncmp(pGetT, pText, iLen) == 0 && !pGetT[iLen]) { + iSel = iPos; + break; + } + + pText += iLen; + if(pText[0] == cChar) + pText++; + if(pText[0] == 0) + break; + } + } + + sSet.mask |= TVIF_IMAGE | TVIF_SELECTEDIMAGE; + sSet.iImage = iIcon + iSel; + sSet.iSelectedImage = sSet.iImage; + } + } + + if(pData->aColumn[uSub].bFlags & TVAE_NEXTLINE && // In die nächste Zeile springen + iMode == 2 && iAuto != TVAX_NONE && iAuto != TVAX_STEP) { + if(!pData->cColumnStart) { + if(uSub) { // Hat sich der Text verändert ? + pExtra = pData->pExtraItems[uSub - 1][uItem]; + if(pExtra && pExtra->pText) { + if(!_tcscmp(pGetT, pExtra->pText)) + return 1; + } else { + if(!*pGetT) + return 1; + } + } else { + pEntry = pData->pTreeItems[uItem]; + if(pEntry && pEntry->pText) { + if(!_tcscmp(pGetT, pEntry->pText)) + return 1; + } else { + if(!*pGetT) + return 1; + } + } + } + + TreeListSetItem(pData, &sSet); + + iLine = pEntry->uShowPos; + if(iLine < (int)pData->uItemPosCount) { + uItem = pData->pItemPos[iLine]; + TreeListSelectItem(pData, uItem, pData->uSelectedSub, TVC_BYKEYBOARD | TVC_DESELECT); + } + } else { + TreeListSetItem(pData, &sSet); + } + } + + return 1; +} + +//***************************************************************************** +//* +//* TreeListEditLabel +//* +//***************************************************************************** +// Startet das editiert eines Eintrages +// pData : Zeiger auf Fensterdaten +// uItem : Item das editiert werden soll +// uSub : Spalte die editiert werden soll und andere Flags +// TVIR_EDITCOL(n) = Spalte angeben +// TVIR_SELAREA(a,b) = Einen Textbereich auswählen +// TVIR_SETCURSOR(n) = Den Cursor auf eine bestimmte Textstelle +// TVIR_SETAT(n) = Den Cursor auf eine bestimmte Pixelstelle +// TVIR_SELALL = Den gesammten Text wählen +// TVIR_EDITCOMBOLIST = Statt dem Edit-Fenster eine ComboBox nur mit Listenauswahl einblenden +// TVIR_EDITFULL = Das Edit-Fenster über die volle Breite einblenden +// TVIR_EDITCOMBOBOX = Statt dem Edit-Fenster eine ComboBox einblenden +// Ergibt das Handle des Edit-Controlls oder NULL bei einem Fehler +static HWND TreeListEditLabel(TreeListData *pData, unsigned uItem, unsigned uSub) { + + HDC hDc; + HWND hWnd; + LRESULT lRet; + WNDPROC pProc; + char cTemp; + LPARAM lParam; + ExtraItem *pExtra; + BaseItem *pEntry; + NMTVDISPINFO sNotify; + LPCTSTR pText; + RECT sRect; + SIZE sSize; + HFONT hFont; + unsigned uCol; + unsigned uSel; + unsigned uNext; + unsigned uBits; + unsigned uSize; + unsigned uStart; + unsigned uState; + unsigned uFlags; + unsigned uHeight; + int iPixels; + int iWidth; + int iTemp; + + uBits = uSub >> 29; + uSel = (uSub >> 8) & 0x0FFFFF; + uSub &= 0xFF; + + if(uSub >= pData->uColumnCount) + return NULL; + if(uItem > pData->uTreeItemsMax) + return NULL; + pEntry = pData->pTreeItems[uItem]; + if(!pEntry) + return NULL; + + if(uItem != pData->uSelectedItem || uSub != pData->uSelectedSub) { + uCol = (pData->uStyleEx & TVS_EX_SUBSELECT) ? uSub : 0; + iTemp = TreeListSelectItem(pData, uItem, uCol, TVC_UNKNOWN); + } + + TreeListEnsureVisible(pData, uItem, uSub); + + if(pData->hEdit) { // Editfenster löschen + DestroyWindow(pData->hEdit); + pData->hEdit = 0; + } + + switch(uBits & 3) { // Editfenster neu erzeugen + case 1: + pData->hEdit = CreateWindow(_T("COMBOBOX"), NULL, WS_BORDER | WS_CHILD | CBS_AUTOHSCROLL | CBS_DROPDOWN | WS_VSCROLL, 0, 0, 0, 0, pData->hWnd, (HMENU)3, NULL, NULL); + pData->uEditMode = 1; + SendMessage(pData->hEdit, CB_LIMITTEXT, 2048, 0); + break; + + case 2: + pData->hEdit = CreateWindow(_T("COMBOBOX"), NULL, WS_BORDER | WS_CHILD | CBS_AUTOHSCROLL | CBS_DROPDOWNLIST | WS_VSCROLL, 0, 0, 0, 0, pData->hWnd, (HMENU)3, NULL, NULL); + pData->uEditMode = 2; + SendMessage(pData->hEdit, CB_LIMITTEXT, 2048, 0); + break; + + default + : + uFlags = ES_LEFT; + + if(uBits & 4) { + if(pData->aColumn[uSub].bAlign == DT_RIGHT) + uFlags = ES_RIGHT; + if(pData->aColumn[uSub].bAlign == DT_CENTER) + uFlags = ES_CENTER; + } + + pData->hEdit = CreateWindow(_T("EDIT"), NULL, WS_BORDER | WS_CHILD | ES_AUTOHSCROLL | uFlags, 0, 0, 0, 0, pData->hWnd, (HMENU)3, NULL, NULL); + pData->uEditMode = 0; + SendMessage(pData->hEdit, EM_SETLIMITTEXT, 2048, 0); + break; + } + + if(!pData->hEdit) + return NULL; + + if(pSetWindowTheme) { // Remove the Visual-Styles (XP+) + pSetWindowTheme(pData->hEdit, L"", L""); + } + + pData->pProcId3 = (WNDPROC)GetWindowLongPtr(pData->hEdit, GWLP_WNDPROC); + SetWindowLongPtr(pData->hEdit, GWLP_USERDATA, (LPARAM)pData); + SetWindowLongPtr(pData->hEdit, GWLP_WNDPROC , (LPARAM)EditProc); + + hWnd = GetWindow(pData->hEdit, GW_CHILD); + + while(hWnd) { + pProc = (WNDPROC)GetWindowLongPtr(hWnd, GWLP_WNDPROC); + SetWindowLongPtr(hWnd, GWLP_USERDATA, (LPARAM)pProc); + SetWindowLongPtr(hWnd, GWLP_WNDPROC , (LPARAM)EditProc); + hWnd = GetNextWindow(hWnd, GW_HWNDNEXT); + } + + pData->cEditCb = 0; + + if(pData->uEditMode >= 1) { // ComboBox leeren + SendMessage(pData->hEdit, CB_RESETCONTENT, 0, 0); + } + + if(uSub == 0) { // Haupteintrag bearbeiten + TreeListGetItemRect(pData, uItem, TVIR_GETCOLUMN | TVIR_TEXT, &sRect); + + pText = pEntry->pText; + uSize = pEntry->uTextSize; + iPixels = pEntry->iTextPixels + 10; + lParam = pEntry->lParam; + uState = pEntry->uState; + hFont = (uState & TVIS_BOLD) ? pData->hFontB : pData->hFontN; + + if(pEntry->bCallback & TVIF_TEXT) { + CallbackEntry(pData, pEntry, uItem, TVIF_TEXT, &iTemp, &uSize, &pText); + hDc = GetDC(pData->hWnd); + SelectObject(hDc, hFont); + DrawText(hDc, pText, uSize, &sRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX | DT_CALCRECT); + ReleaseDC(pData->hWnd, hDc); + iPixels = sRect.right - sRect.left + 10; + pData->cEditCb = 1; + } + + if(uBits & 4) { // Volle Spaltenbreite + if(pEntry->iImage != TV_NOIMAGE) { + uNext = pData->aColumnPos[1]; + sRect.right = pData->aColumnXpos[uNext]; + sRect.right -= pData->uScrollX; + } + + iPixels = sRect.right - sRect.left - 2; + } else { + if(pData->uEditMode) { + if(iPixels < 60) + iPixels = 60; + } else { + if(iPixels < 48) + iPixels = 48; + } + + if(pText && *pText) { + iPixels += str_len(pText); + } + + if(pData->uEditMode) { + iPixels += GetSystemMetrics(SM_CXHSCROLL); + } + } + } else { // Extraeintrag bearbeiten + if(uBits & 4) { + TreeListGetItemRect(pData, uItem, TVIR_GETCOLUMN | TVIR_COLTOSUB(uSub), &sRect); + + if(pData->aColumn[uSub].bEdit >= TVAX_CHECK) { + sRect.left += pData->iChecksXsize; + if(sRect.right < sRect.left) + sRect.right = sRect.left + 1; + } + } else { + TreeListGetItemRect(pData, uItem, TVIR_GETCOLUMN | TVIR_TEXT | TVIR_COLTOSUB(uSub), &sRect); + } + + pExtra = pData->pExtraItems[uSub - 1][uItem]; + if(!pExtra) { + pData->cTempText1[0] = 0; + pText = pData->cTempText1; + uSize = sizeof(pData->cTempText1) / sizeof(pData->cTempText1[0]); + iPixels = sRect.right - sRect.left + 10; + hFont = pData->hFontN; + uState = pEntry->uState & TVIS_BASEFLAGS; + lParam = 0; + } else { + pText = pExtra->pText; + uSize = pExtra->uTextSize; + iPixels = pExtra->iTextPixels + 10; + lParam = pEntry->lParam; + uState = pExtra->uState; + uState |= pEntry->uState & TVIS_BASEFLAGS; + hFont = (uState & TVIS_BOLD) ? pData->hFontB : pData->hFontN; + + if(pExtra->bCallback & TVIF_TEXT) { + CallbackExtra(pData, pEntry, pExtra, uItem, uSub, TVIF_TEXT, &iTemp, &uSize, &pText); + hDc = GetDC(pData->hWnd); + SelectObject(hDc, hFont); + DrawText(hDc, pText, uSize, &sRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX | DT_CALCRECT); + ReleaseDC(pData->hWnd, hDc); + iPixels = sRect.right - sRect.left; + pData->cEditCb = 1; + } + } + + if(uBits & 4) { // Volle Spaltenbreite + if(pExtra && pExtra->iImage != TV_NOIMAGE) { + sRect.left += pData->iImagesXsize + 1; + } + + iPixels = sRect.right - sRect.left - 2; + } else { + if(pData->uEditMode) { + if(iPixels < 60) + iPixels = 60; + } else { + if(iPixels < 48) + iPixels = 48; + } + + if(pText && *pText) { + iPixels += str_len(pText); + } + + if(pData->uEditMode) { + iPixels += GetSystemMetrics(SM_CXHSCROLL); + } + + switch(pData->aColumn[uSub].bAlign) { + case DT_RIGHT: + iWidth = sRect.right - sRect.left; + sRect.left += iWidth - iPixels; + break; + + case DT_CENTER: + iWidth = sRect.right - sRect.left; + sRect.left += (iWidth - iPixels) / 2; + break; + } + } + } + + UNLOCK(pData); + + sNotify.hdr.code = TVN_BEGINLABELEDIT; + sNotify.item.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_TEXT | TVIF_SUBITEM; + sNotify.item.hItem = (HTREEITEM)uItem; + sNotify.item.pszText = (LPTSTR)pText; + sNotify.item.lParam = lParam; + sNotify.item.state = uState; + sNotify.item.cchTextMax = uSize; + sNotify.item.stateMask = 0xFFFFFFFF; + sNotify.item.cChildren = uSub; + + lRet = SendNotify(pData, &sNotify.hdr); + + LOCK(pData); + + if(lRet) { // Das Editieren abbrechen + TreeListEndLabelEdit(pData, 0); + return NULL; + } + + pEntry = pData->pTreeItems[uItem]; + if(!pEntry) + return NULL; + + if(pData->uToolTipItem) { // Ein offenes Tooltip verstecken + UpdateToolTip(pData, 0, 0); + } + + UNLOCK(pData); + SetFocus(pData->hEdit); + LOCK(pData); + + if(pData->uEditMode) { + sRect.top--; + uHeight = 260; + } else { + uHeight = pData->iFontHeight + 4; + } + + if(pData->uEditMode) { + if(iPixels < 60) + iPixels = 60; + } else { + if(iPixels < 48) + iPixels = 48; + } + + cTemp = pData->cColumnStart; + SetWindowPos(pData->hEdit, HWND_TOP, sRect.left + 2, sRect.top + 1, iPixels, uHeight, SWP_SHOWWINDOW); + SendMessage(pData->hEdit, WM_SETFONT, (WPARAM)hFont, 0); + SetWindowText(pData->hEdit, pText); + pData->cColumnStart = cTemp; + + switch(uSel >> 18) { // Welche Textauswahl + case 3: + uStart = (uSel) & 0x01FF; // Einen Textbereich markieren + uSize = (uSel >> 9) & 0x01FF; + break; + + case 2: + uStart = uSel & 0x3FFFF; // Cursor auf eine bestimmte Stelle + uSize = uStart; + break; + + case 1: + uSel &= 0x3FFFF; + if(uSel > 4) + uSel -= 4; + hDc = GetDC(pData->hEdit); + SelectObject(hDc, hFont); + GetTextExtentExPoint(hDc, pText, uSize, uSel, &iWidth, NULL, &sSize); + + if(uSize > 0 && iWidth < (int)uSize - 1) { // Halben Buchstaben addieren + GetTextExtentExPoint(hDc, pText + iWidth, 1, uSel, NULL, NULL, &sSize); + uSel += sSize.cx / 2; + GetTextExtentExPoint(hDc, pText, uSize, uSel, &iWidth, NULL, &sSize); + } + + ReleaseDC(pData->hEdit, hDc); + + if(sSize.cx <= (int)uSel) { + uStart = 0; + break; + } + + uStart = iWidth; + uSize = iWidth; + break; + + default + : + uStart = 0; // Alles markieren + break; + } + + switch(pData->uEditMode) { + case 0: + SendMessage(pData->hEdit, EM_SETSEL, uStart, uSize); + break; + case 1: + SendMessage(pData->hEdit, CB_SETEDITSEL, 0, MAKELPARAM(uStart, uSize)); + break; + default + : + uStart = 0; + uSize = 0xFFFF; + break; + } + + RedrawWindow(pData->hEdit, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE); + + pData->uLastSel = MAKELPARAM(uStart, uSize); + pData->uEditItem = uItem; + pData->uEditSub = uSub; + + return pData->hEdit; +} + + +//***************************************************************************** +//* +//* TreeListStartAutoEdit +//* +//***************************************************************************** +// Startet die Editierung für einen Wert via Notyfy-Rückfrage. +// pData : Zeiger auf die Fensterdaten +// uItem : Ist der Eintrag der Editiert werden soll +// uSub : Ist die Nummer der Spalte +// wParam : Ist der W-Parameter des Tastendrucks +// VK_EDITCLK bei einem Clickauf das ausgewählte Feld +// VK_DBLCLK bei einem Doppelclick +// VK_RETURN bei einen Enter-Druck +// VK_ISACHAR bei WM_CHAR Nachrichten +// bei einer Zeicheneingabe +// lParam : Ist der L-Parameter des Tastendrucks (bzw. die Koordinaten) +// Ergibt 1 das Editieren gestartet wurde, ansonsten 0 +// 0 wenn der Eintrag nicht gewählt wurde +static int TreeListStartNotifyEdit(TreeListData *pData, unsigned uItem, unsigned uSub, WPARAM wParam, LPARAM lParam) { + + TCHAR cText[1024]; + TV_STARTEDIT sNotify; + ExtraItem *pExtra; + BaseItem *pEntry; + unsigned uBits; + unsigned uCnt; + unsigned uMax; + unsigned uLen; + unsigned uNum; + LPCTSTR pText; + RECT sRect; + TCHAR cChar; + LRESULT lRet; + MSG sMsg; + HWND hWnd; + INT iSel; + + if(!(pData->uStyle & TVS_EDITLABELS)) + return 0; + if(uItem == 0 || uItem > pData->uTreeItemsMax) + return 0; + if(uSub > 0 && uSub >= pData->uColumnCount) + return 0; + + pEntry = pData->pTreeItems[uItem]; + if(!pEntry) + return 0; + + if(wParam < ' ' && wParam != VK_RETURN) { // Falsche Taste + return 0; + } + + if(wParam >= ' ' && wParam <= 0xFFFF) { // Shift und Cltr prüfen + if(GetKeyState(VK_MENU) & 0x8000) { + return 0; + } + + if(GetKeyState(VK_CONTROL) & 0x8000) { + return 0; + } + } + + sNotify.hdr.code = TVN_STARTEDIT; + sNotify.uAction = (UINT)wParam; + sNotify.item.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_SUBITEM; + sNotify.item.hItem = (HTREEITEM)uItem; + sNotify.item.stateMask = 0xFFFFFFFF; + sNotify.item.lParam = pEntry->lParam; + sNotify.item.cChildren = uSub; + sNotify.uHeight = 0; + sNotify.pTextEntries = 0; + sNotify.pTextList = 0; + sNotify.uMaxEntries = 255; + sNotify.ptAction.x = LOWORD(lParam); + sNotify.ptAction.y = HIWORD(lParam); + + if(uSub) { // Spalte verwenden + pExtra = pData->pExtraItems[uSub - 1][pData->uSelectedItem]; + + if(pExtra) { + sNotify.item.state = pExtra->uState&~TVIS_BASEFLAGS; + sNotify.item.pszText = pExtra->pText; + sNotify.item.cchTextMax = pExtra->uTextSize; + } else { + sNotify.item.state = 0; + sNotify.item.cchTextMax = 0; + sNotify.item.pszText = _T(""); + } + + sNotify.item.state |= pEntry->uState & TVIS_BASEFLAGS; + } else { // Haupteintrag + sNotify.item.state = pEntry->uState; + sNotify.item.pszText = pEntry->pText; + sNotify.item.cchTextMax = pEntry->uTextSize; + } + + UNLOCK(pData); + lRet = SendNotify(pData, &sNotify.hdr); + LOCK(pData); + + if(!lRet) + return 0; + + if(wParam == VK_RETURN) { + wParam = VK_DBLCLK; + uBits = 0; + } else + if(wParam == VK_EDITCLK) { + if(pData->uStyleEx & TVS_EX_NOCURSORSET) { + uBits = 0; + } else { + TreeListGetItemRect(pData, uItem, TVIR_COLTOSUB(uSub) | TVIR_GETCOLUMN | TVIR_TEXT, &sRect); + uBits = TVIR_SETAT(LOWORD(lParam) - sRect.left); + } + } else { + uBits = 0; + } + + cChar = (TCHAR)((lRet >> 8) & 0xFF); + uBits |= U(lRet)&TVIR_EDITFULL; + + if(U(lRet)&TVIR_EDITCOMBOBOX) { // Eine Combobox anzeigen + uBits |= (U(lRet)&TVIR_EDITCOMBOLIST) ? 0x40000000 : 0x20000000; + } + + hWnd = TreeListEditLabel(pData, uItem, uSub | uBits); + if(!hWnd) + return 0; + + if(lRet & TVIR_EDITCOMBOBOX) { // Die Combobox füllen + uMax = sNotify.uMaxEntries; + iSel = -1; + + if(sNotify.pTextList) { // Texte über Listenfeld + for(uCnt = 0; uCnt < uMax; uCnt++) { + pText = sNotify.pTextList[uCnt]; + if(!pText) + break; + + SendMessage(hWnd, CB_ADDSTRING, 1, (LPARAM)pText); + + if(sNotify.item.pszText && !_tcscmp(pText, sNotify.item.pszText)) { + iSel = uCnt; + } + } + } else { // Text mit + pText = sNotify.pTextEntries; + + if(!pText) + pText = _T("\0"); + + for(uCnt = 0; uCnt < uMax; uCnt++) { + for(uLen = 0; pText[uLen]; uLen++) { // Subtextlänge holen + if(pText[uLen] == cChar) + break; + } + + if(!uLen && !pText[uLen]) + break; + + uNum = uLen; + if(uNum >= sizeof(cText) / sizeof(TCHAR)) { + uNum = sizeof(cText) / sizeof(TCHAR) - 1; + } + + memcpy(cText, pText, uNum * sizeof(TCHAR)); + cText[uNum] = 0; + SendMessage(hWnd, CB_ADDSTRING, 1, (LPARAM)cText); + + if(sNotify.item.pszText && wParam >= 0x10000 && !_tcscmp(cText, sNotify.item.pszText)) { + iSel = uCnt; + } + + pText += uLen; + if(!cChar || *pText) + pText++; + } + + if(lRet & TVIR_EDITCOMBODEL) { // Den Puffer löschen + delete((TCHAR*)sNotify.pTextEntries); + } + } + + if(iSel >= 0) { // Listeneintrag auswählen + SendMessage(hWnd, CB_SETCURSEL, iSel, 0); + } + + if(sNotify.uHeight) { // Höhe der Dropdown-Liste einstellen + SendMessage(hWnd, CB_SETDROPPEDWIDTH, sNotify.uHeight, 0); + } + + if(lRet & TVIR_EDITCOMBODOWN) { // Dropdown-Liste einblenden + SendMessage(hWnd, CB_SHOWDROPDOWN, 1, 0); + } + + if(!(lRet & TVIR_EDITCOMBOLIST)) { // Textauswahl wiederherstellen + SetWindowText(hWnd, sNotify.item.pszText); + SendMessage(hWnd, CB_SETEDITSEL, 0, pData->uLastSel); + } + } + + if(wParam < 0x10000) { // Taste an Fenster senden + pData->cColumnStart = 1; + sMsg.hwnd = hWnd; + sMsg.lParam = lParam; + sMsg.wParam = wParam; + sMsg.message = WM_KEYDOWN; + + TranslateMessage(&sMsg); + } else + if(wParam & VK_ISACHAR) { + SendMessage(hWnd, WM_CHAR, wParam & 0xFFFF, lParam); + } else { + pData->cColumnStart = 0; + } + + return 1; +} + +//***************************************************************************** +//* +//* TreeListStartAutoEdit +//* +//***************************************************************************** +// Startet die Autoeditierung für eine Spalte +// pData : Zeiger auf die Fensterdaten +// uColumn : Ist die Nummer der Spalte +// wParam : Ist der W-Parameter des Tastendrucks +// VK_EDITCLK bei einem Clickauf das ausgewählte Feld +// VK_ICONCLK bei einem Clickauf das Iion +// VK_DBLCLK bei einem Doppelclick +// lParam : Ist der L-Parameter des Tastendrucks (bzw. die Koordinaten) +// Ergibt 1 das Editieren gestartet wurde, ansonsten 0 +// 0 wenn der Eintrag nicht gewählt wurde +static int TreeListStartAutoEdit(TreeListData *pData, unsigned uColumn, WPARAM wParam, LPARAM lParam) { + + TCHAR cBuffer[256]; + NMTREEVIEW sNotify; + ExtraItem *pExtra; + BaseItem *pEntry; + LPTSTR *pList; + LPTSTR pText; + TV_ITEM sItem; + RECT sRect; + TCHAR cChar; + unsigned uMode; + unsigned uBits; + unsigned uFlag; + unsigned uMax; + unsigned uPos; + unsigned uLen; + unsigned uSub; + HWND hWnd; + int iSel; + int iRet; + int iIcon; + + if(pData->uEditItem) + return 0; + if(pData->uColumnCount <= uColumn) + return 0; + if(!(pData->uStyle & TVS_EDITLABELS)) + return 0; + + uBits = pData->aColumn[uColumn].bFlags; + uMode = pData->aColumn[uColumn].bEdit; + + if(uMode == 0) + return 0; + + if(uBits & TVAE_STATEENABLE) { // Kann das Editieren gesperrt werden + if(uColumn) { + pExtra = pData->pExtraItems[uColumn - 1][pData->uSelectedItem]; + if(pExtra && (pExtra->uState & TVIS_DISABLEBIT)) + return 0; + } else { + pEntry = pData->pTreeItems[pData->uSelectedItem]; + if(!pEntry || (pEntry->uState & TVIS_DISABLEBIT)) + return 0; + } + } + + if(wParam != VK_RETURN && wParam < 0x10000) { // Zeicheneingabe + if(!TVIS_EDIT(uMode)) + return 0; + if(uBits & TVAE_ONLYRETURN) + return 0; + if(wParam <= ' ') + return 0; + pData->cColumnStart = 1; + } else { + pData->cColumnStart = 0; + } + + uMax = pData->aColumn[uColumn].bCbSize; + if(!uMax) + uMax = 1024; + +//******************** Weiterschalten mit Return ****************************** + if(uMode >= TVAX_STEP) { + sItem.mask = TVIF_TEXT | TVIF_SUBITEM | TVIF_TEXTPTR | ((uMode < TVAX_CHECK) ? 0 : TVIF_STATE | TVIF_PARAM); + sItem.stateMask = 0xFFFFFFFF; + sItem.hItem = (HTREEITEM)pData->uSelectedItem; + sItem.cChildren = uColumn; + + if(!TreeListGetItem(pData, &sItem)) + return 0; + + if((uMode & 1) && wParam != VK_ICONCLK) { // Ein Edit-Feld öffnen + uMode = TVAX_EDIT; + goto EditField; + } + + if(uMode >= TVAX_CHECK) { // State-Bits bei Checkboxen setzen + if(uBits & TVAE_STATEENABLE) + uFlag = sItem.state & (~TVIS_STATEIMAGEMASK | TVIS_DISABLEBIT); + else + uFlag = sItem.state & (~TVIS_STATEIMAGEMASK); + + if(pData->uStyleEx & TVS_EX_BITCHECKBOX) + sItem.state = (sItem.state ^ 0x1000); + else + sItem.state = (sItem.state & 0x1000) ? 0x2000 : 0x1000; + + sItem.state |= uFlag; + } else { + sItem.mask &= ~TVIF_STATE; + } + + if(uBits & TVAE_PTRLIST) { // Zeigerliste char *pTexte[]={"1","2",NULL}; + pList = (LPTSTR *)pData->aColumn[uColumn].pCbData; + + if(!pList) { + if(uMode < TVAX_CHECK) + return 0; + sItem.mask &= ~TVIF_TEXT; + pText = sItem.pszText; + uPos = 0; + goto NoTextChange; + } + + for(uPos = 0;; uPos++) { + if(uPos >= uMax || !pList[uPos]){ + uPos = 0; break; + } + if(str_cmp(sItem.pszText, pList[uPos])) + continue; + uPos++; + if(uPos >= uMax || !pList[uPos]) + uPos = 0; + break; + } + + pText = pList[uPos]; + if(!pText) { + if(uMode < TVAX_CHECK) + return 0; + sItem.mask &= ~TVIF_TEXT; + pText = sItem.pszText; + } + } else { // Textliste char *pText="1|2|3"; + pText = (LPTSTR)pData->aColumn[uColumn].pCbData; + cChar = (TCHAR)pData->aColumn[uColumn].bCbChar; + sItem.cchTextMax--; + + if(!pText) { + if(uMode < TVAX_CHECK) + return 0; + sItem.mask &= ~TVIF_TEXT; + pText = sItem.pszText; + uPos = 0; + goto NoTextChange; + } + + for(uPos = 0;; uPos++) { + if(uPos >= uMax) { + pText = (LPTSTR)pData->aColumn[uColumn].pCbData; + uPos = 0; + break; + } + + for(uLen = 0; pText[uLen]; uLen++) { // Subtextlänge bestimmen + if(pText[uLen] == cChar) + break; + } + + if(sItem.cchTextMax == (int)uLen) // Ist der Subtext der aktulle Text + if(str_ncmp(sItem.pszText, pText, uLen) == 0) { + uPos++; + + if(uPos >= uMax) { + pText = (LPTSTR)pData->aColumn[uColumn].pCbData; + uPos = 0; + break; + } + + pText += uLen; + if(pText[0] == cChar) + pText++; + if(pText[0] == 0) { + pText = (LPTSTR)pData->aColumn[uColumn].pCbData; + uPos = 0; + } + + break; + } + + pText += uLen; + if(pText[0] == cChar) + pText++; + if(pText[0] == 0) { + pText = (LPTSTR)pData->aColumn[uColumn].pCbData; + uPos = 0; + break; + } + + } + + for(uLen = 0; pText[uLen] && uLen < 256; uLen++) { // Ausgewählten Text kopiren + if(pText[uLen] == cChar) + break; + cBuffer[uLen] = pText[uLen]; + } + + cBuffer[uLen] = 0; + pText = cBuffer; + } + +NoTextChange: + + sItem.mask &= ~TVIF_TEXTPTR; + sItem.stateMask &= TVIS_STATEIMAGEMASK; + sItem.hItem = (HTREEITEM)pData->uSelectedItem; + sItem.pszText = pText; + sItem.cchTextMax = 256; + sItem.cChildren = uColumn; + iIcon = pData->aColumn[uColumn].iCbIcon; + + if(iIcon >= 0) { // Auch ein Icon zuweisen + sItem.mask |= TVIF_IMAGE | TVIF_SELECTEDIMAGE; + sItem.iImage = iIcon + uPos; + sItem.iSelectedImage = sItem.iImage; + } + + iRet = TreeListSetItem(pData, &sItem); + + sNotify.hdr.code = (uMode < TVAX_CHECK) ? TVN_STEPSTATECHANGED : TVN_CBSTATECHANGED; + sNotify.action = (UINT)wParam; + sNotify.itemNew.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_TEXT | TVIF_SUBITEM; + sNotify.itemNew.stateMask = 0xFFFFFFFF; + sNotify.itemNew.hItem = sItem.hItem; + sNotify.itemNew.state = sItem.state; + sNotify.itemNew.lParam = sItem.lParam; + sNotify.itemNew.pszText = sItem.pszText; + sNotify.itemNew.cchTextMax = sItem.cchTextMax; + sNotify.itemNew.cChildren = uColumn; + sNotify.itemOld.mask = 0; + sNotify.ptDrag.x = LOWORD(lParam); + sNotify.ptDrag.y = HIWORD(lParam); + + UNLOCK(pData); + SendNotify(pData, &sNotify.hdr); + LOCK(pData); + + return iRet; + } + +//******************** Edit oder Combobox ************************************* +EditField: + + uSub = (uMode - 1) << 29; + uSub |= uColumn; + + if(uBits & TVAE_FULLWIDTH) { + uSub |= TVIR_EDITFULL; + } + + if(wParam == VK_EDITCLK) // Cursor auf Klickposition + if(!(pData->uStyleEx & TVS_EX_NOCURSORSET)) { + TreeListGetItemRect(pData, pData->uSelectedItem, TVIR_COLTOSUB(uColumn) | TVIR_GETCOLUMN | TVIR_TEXT, &sRect); + uSub |= TVIR_SETAT(LOWORD(lParam) - sRect.left); + } + + hWnd = TreeListEditLabel(pData, pData->uSelectedItem, uSub); + if(!hWnd) + return 0; + + if(uMode != TVAX_EDIT) { + iSel = (uMode != TVAX_CBLIST) ? -1 : 0; + sItem.mask = TVIF_TEXT | TVIF_SUBITEM | TVIF_TEXTPTR; + sItem.hItem = (HTREEITEM)pData->uSelectedItem; + sItem.cChildren = uColumn; + + if(!TreeListGetItem(pData, &sItem)) + return 0; + + if(uBits & TVAE_PTRLIST) { // Zeigerliste char *pTexte[]={"1","2",NULL}; + pList = (LPTSTR *)pData->aColumn[uColumn].pCbData; + + for(uPos = 0; uPos < uMax; uPos++) { + if(!pList[uPos]) + break; + if(!str_cmp(sItem.pszText, pList[uPos])) + iSel = uPos; + SendMessage(hWnd, CB_ADDSTRING, 0, (LPARAM)pList[uPos]); + } + } else { // Textliste char *pText="1|2|3"; + pText = (LPTSTR)pData->aColumn[uColumn].pCbData; + cChar = (TCHAR)pData->aColumn[uColumn].bCbChar; + sItem.cchTextMax--; + + for(uPos = 0; uPos < uMax; uPos++) { + for(uLen = 0; pText[uLen]; uLen++) { + if(pText[uLen] == cChar) + break; + } + + if(sItem.cchTextMax == (int)uLen) + if(str_ncmp(sItem.pszText, pText, uLen) == 0) { + iSel = uPos; + } + + if(cChar) { + if(uLen < 256){ + memcpy(cBuffer, pText, sizeof(TCHAR)*uLen); + cBuffer[uLen] = 0; + } + else { + memcpy(cBuffer, pText, sizeof(TCHAR) * 255); + cBuffer[255 ] = 0; + } + SendMessage(hWnd, CB_ADDSTRING, 0, (LPARAM)cBuffer); + } else { + SendMessage(hWnd, CB_ADDSTRING, 0, (LPARAM)pText); + } + + pText += uLen; + if(pText[0] == cChar) + pText++; + if(pText[0] == 0) + break; + } + } + + if(iSel >= 0) { + SendMessage(hWnd, CB_SETCURSEL, iSel, 0); + } + } + + if((uBits & TVAE_DROPDOWN) && (uMode&~1) == 2) { // Dropdownliste aufklappen + SendMessage(hWnd, CB_SHOWDROPDOWN, 1, 0); + } + + // Ersten Buchstaben ans Fenster übergeben + if(TVIS_EDIT(uMode) && pData->cColumnStart && wParam != VK_EDITCLK) { + SendMessage(hWnd, WM_CHAR, wParam, 0); + } + + return 1; +} + +//***************************************************************************** +//* +//* TreeListProc +//* +//***************************************************************************** +// Ist die Fensterfunktion für das TreeList Fenster +static LRESULT CALLBACK TreeListProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + + TreeListData *pData; + MSG *pMsg; + LPARAM lRet; + TV_HITTESTINFO sInfo; + POINT sPoint; + SCROLLINFO sScroll; + unsigned uChange; + unsigned uDelta; + unsigned uTime; + unsigned uFlag; + unsigned uMode; + unsigned uVal; + unsigned uOld; + int iDif; + int iPos; + int iMax; + HDC hDc; + + switch(uMsg) { + case WM_CREATE: // Das Fenster erzeugen + + pData = new(TreeListData, 1); + if(!pData) + return -1; + + memset(pData, 0, sizeof(TreeListData)); + pData->pToolTipText = new(TCHAR, 256); + pData->uToolTipSize = 256; + + if(!pData->pToolTipText) { + delete(pData); + return -1; + } + + pData->pTreeItems = new(BaseItem*, 1); + if(!pData->pTreeItems) { + delete(pData->pToolTipText); + delete(pData); + return -1; + } + + pData->pItemPos = new(unsigned, 1); + if(!pData->pItemPos) { + delete(pData->pToolTipText); + delete(pData->pTreeItems); + delete(pData); + return -1; + } + + GlobalInit(); + + pData->pItemPos [0] = 0; + pData->pTreeItems[0] = NULL; + + SetWindowLongPtr(hWnd, 0, (LONG_PTR)pData); + pData->iIndent = DEFAULT_IDENT; + pData->iShift = DEFAULT_SHIFT; + pData->uStyle = GetWindowLong(hWnd, GWL_STYLE); + pData->hSem = CreateSemaphore(0, 1, 0x70000000, 0); + pData->hWnd = hWnd; + pData->cIsEnabled = (char)IsWindowEnabled(hWnd); + pData->iAutoAdd = 1; + pData->aColumnPos[0] = 0; + pData->aColumnPos[1] = 1; + + if(!(pData->uStyle & (TVS_HASBUTTONS | TVS_HASLINES))) { + pData->cHasRootRow = 0; + } else { + pData->cHasRootRow = (char)((pData->uStyle & TVS_LINESATROOT) ? 1 : 0); + } + + if(!(pData->uStyle & TVS_NOTOOLTIPS)) { + CreateToolTip(pData); + } + + UpdateFont(pData); + UpdateHeight(pData); + UpdateColorsList(pData); + UpdateScrollY(pData); + + if(pOpenThemeData) // Soll ein Thema angezeigt werden + pData->hTheme = pOpenThemeData(hWnd, L"TREEVIEW"); + else + pData->hTheme = NULL; + + pData->cGlyphOk = (char)((pData->hTheme) ? 1 : 0); + + if(pData->uStyle & TVS_CHECKBOXES) { + CreateStateImageList(pData, 0); + } + + return 0; + + case WM_DESTROY: // Das Fenster zerstören + + pData = GetHandle(hWnd); + + LOCK(pData); + + TreeListDeleteItem(pData, U(TVI_ROOT), 0); + if(pData->hStates == THEMEIMGLIST) + pData->hStates = 0; + if(pData->hChecks == THEMEIMGLIST) + pData->hChecks = 0; + if(pData->hEdit){ + DestroyWindow(pData->hEdit); + pData->hEdit = 0; + } + if(pData->hHeader){ + DestroyWindow(pData->hHeader); + pData->hHeader = 0; + } + if(pData->hToolTip){ + DestroyWindow(pData->hToolTip); + pData->hToolTip = 0; + } + if(pData->hStates){ + ImageList_Destroy(pData->hStates); + pData->hStates = 0; + } + + if(pData->uStyleEx & TVS_EX_SHAREIMAGELISTS) { + if(pData->hStates && pData->iStatesMode) { + ImageList_Destroy(pData->hStates); + pData->hStates = 0; + } + if(pData->hChecks && pData->iChecksMode) { + ImageList_Destroy(pData->hChecks); + pData->hChecks = 0; + } + pData->hImages = 0; + pData->hSubImg = 0; + pData->hHeadImg = 0; + } else { + if(pData->hStates){ + ImageList_Destroy(pData->hStates); + pData->hStates = 0; + } + if(pData->hChecks){ + ImageList_Destroy(pData->hChecks); + pData->hChecks = 0; + } + if(pData->hImages){ + ImageList_Destroy(pData->hImages); + pData->hImages = 0; + } + if(pData->hSubImg){ + ImageList_Destroy(pData->hSubImg); + pData->hSubImg = 0; + } + if(pData->hHeadImg){ + if(pData->uStyleEx & TVS_EX_HEADEROWNIMGLIST) + ImageList_Destroy(pData->hHeadImg); + pData->hHeadImg = 0; + } + } + + if(pData->hThemeBt){ + pCloseThemeData(pData->hThemeBt); + pData->hThemeBt = 0; + } + if(pData->hTheme){ + pCloseThemeData(pData->hTheme); + pData->hTheme = 0; + } + + for(uVal = 1; uVal < pData->uColumnCount; uVal++) { + delete(pData->pExtraItems[uVal - 1]); + pData->pExtraItems[uVal - 1] = 0; + } + + if(pData->hFontB){ + DeleteObject(pData->hFontB); + pData->hFontB = 0; + } + + pData->uColumnCount = 0; + + UNLOCK(pData); + + return 0; + + case WM_NCDESTROY: // Das Fenster zerstören + + pData = GetHandle(hWnd); + + LOCK(pData); + + delete(pData->pToolTipText); + delete(pData->pTreeItems); + delete(pData->pItemPos); + + UNLOCK(pData); + + SetWindowLongPtr(hWnd, 0, 0); + CloseHandle(pData->hSem); + memset(pData, 0, sizeof(TreeListData)); + delete(pData); + + GlobalDeinit(); + + return 0; + + case WM_SHOWWINDOW: // Fenster ein/ausblenden + + if(wParam) { + pData = GetHandle(hWnd); + + sScroll.cbSize = sizeof(SCROLLINFO); + sScroll.fMask = SIF_ALL; + sScroll.nMin = 0; + sScroll.nMax = 0; + sScroll.nPage = 0; + sScroll.nPos = 0; + sScroll.nTrackPos = 0; + + SetScrollInfo(pData->hWnd, SB_VERT, &sScroll, TRUE); + SetScrollInfo(pData->hWnd, SB_HORZ, &sScroll, TRUE); + + pData->uOldXCount = 0xFFFF; + pData->uOldYCount = 0xFFFF; + + UpdateScrollY(pData); + UpdateScrollX(pData); + } + + return 0; + + case WM_SIZE: // Die Fenstergröße wurde verändert + + pData = GetHandle(hWnd); + uFlag = 0; + + LOCK(pData); + + uVal = LOWORD(lParam); + if(uVal && uVal != pData->uSizeX) { + uOld = pData->uSizeX; + pData->uSizeX = uVal; + + if(pData->uColumnCountVar) { // Spalten mit variabler Breite nach führen + RECT sRect; + int iDelta; + int iNum; + + iDelta = uVal - uOld; + + ChangeColSize(pData, iDelta); + + iNum = UpdateColumns(pData); + GetClientRect(hWnd, &sRect); + sRect.left = iNum; + sRect.left -= pData->uScrollX; + sRect.top = pData->uStartPixel; + InvalidateRect(hWnd, &sRect, FALSE); + } + + if(uVal > uOld) { // Hat sich die Breite vergrößert + RECT sRect; + + GetClientRect(hWnd, &sRect); + sRect.right = uVal; + sRect.left = uOld; + InvalidateRect(hWnd, &sRect, FALSE); + } + + pData->aColumnXpos[pData->uColumnCount + 1] = uVal + 1; + + MoveWindow(pData->hHeader, -(int)pData->uScrollX, 0, uVal + pData->uScrollX, pData->uStartPixel, uVal > uOld); + UpdateScrollX(pData); + } + + uVal = HIWORD(lParam); + if(uVal && uVal != pData->uSizeY) { + if(uVal > pData->uSizeY) { // Hat sich die Höhe vergrößert + RECT sRect; + + GetClientRect(hWnd, &sRect); + uFlag = 1; + sRect.bottom = uVal; + sRect.top = pData->uSizeY; + InvalidateRect(hWnd, &sRect, FALSE); + } else { + uFlag = 0; + } + + pData->uSizeY = uVal; + pData->uSizeYsub = (uVal <= pData->uStartPixel) ? 0 : uVal - pData->uStartPixel; + + if(pData->uSizeY > pData->uStartPixel) { + pData->uMaxEnties = pData->uSizeY; + pData->uMaxEnties -= pData->uStartPixel; + } else { + pData->uMaxEnties = 0; + } + + pData->uPageEnties = pData->uMaxEnties; + pData->uMaxEnties += pData->iRowHeight - 1; + pData->uMaxEnties /= pData->iRowHeight; + pData->uPageEnties /= pData->iRowHeight; + // Wenn Höhe vergrößert dann Scroll-Position prüfen + if(uFlag && pData->uPageEnties > 2 && pData->uScrollY > 0) { + if(pData->uScrollY + pData->uPageEnties + 1 > pData->uItemPosCount) { + iPos = pData->uItemPosCount - pData->uPageEnties + 1; + if(iPos < 0) + iPos = 0; + if(U(iPos) != pData->uScrollY) { + pData->uScrollY = iPos; + UpdateView(pData); + } + } + } + UpdateScrollY(pData); + } + + UNLOCK(pData); + + if(uFlag) { + RedrawWindow(pData->hHeader, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE); + } + + return 0; + + case WM_ENABLE: // Das Fenster feischalten oder sperren + + pData = GetHandle(hWnd); + if(pData->cIsEnabled != ((U(wParam)) ? 1 : 0)) { + uVal = GetWindowLong(hWnd, GWL_STYLE); + uVal &= (0xFFFF0000 & ~WS_DISABLED); + uVal |= (0x0000FFFF | WS_DISABLED) & (pData->uStyle ^ WS_DISABLED); + SetWindowLong(hWnd, GWL_STYLE, uVal); + } + + return 0; + + case WM_SETFOCUS: // Das Fenster bekommt den Focus + + pData = GetHandle(hWnd); + if(!pData->cHasFocus) { + NMHDR sNotify; + + LOCK(pData); + pData->cHasFocus = 1; + + if(pData->uSelectedCount <= 1) { + if(pData->uSelectedItem) { + if(pData->uStyleEx & TVS_EX_FULLROWMARK) + UpdateRow(pData, pData->uSelectedItem); + else + UpdateRect(pData, pData->uSelectedItem, pData->uSelectedSub); + } + } else { + UpdateView(pData); + } + + UNLOCK(pData); + + sNotify.code = NM_SETFOCUS; + SendNotify(pData, &sNotify); + } + + return 0; + + case WM_KILLFOCUS: // Das Fenster bekommt den Focus + + pData = GetHandle(hWnd); + if(pData->cHasFocus) { + NMHDR sNotify; + + LOCK(pData); + pData->cHasFocus = 0; + + if(pData->uSelectedCount <= 1) { + if(pData->uSelectedItem) { + if(pData->uStyleEx & TVS_EX_FULLROWMARK) + UpdateRow(pData, pData->uSelectedItem); + else + UpdateRect(pData, pData->uSelectedItem, pData->uSelectedSub); + } + } else { + UpdateView(pData); + } + + UNLOCK(pData); + + sNotify.code = NM_KILLFOCUS; + SendNotify(pData, &sNotify); + } + + return 0; + + case WM_MOUSEMOVE: // Gab es eine Mausbewegung + + pData = GetHandle(hWnd); + sInfo.hItem = 0; + sInfo.flags = 0; + + if(!(pData->uStyle & TVS_NOTOOLTIPS)) { + if(!pData->cIsEnabled) { // Ist das Fenster freigegeben + return 0; + } + + LOCK(pData); + + sInfo.pt.x = LOWORD(lParam); + sInfo.pt.y = HIWORD(lParam); + TreeListHitTest(pData, &sInfo); + UpdateToolTip(pData, U(sInfo.hItem), sInfo.flags); + + UNLOCK(pData); + } + + if(pData->uStyle & TVS_TRACKSELECT) { + if(!pData->cIsEnabled) { // Ist das Fenster freigegeben + return 0; + } + + LOCK(pData); + + if(!sInfo.hItem) { + sInfo.pt.x = LOWORD(lParam); + sInfo.pt.y = HIWORD(lParam); + TreeListHitTest(pData, &sInfo); + } + + if(sInfo.hItem && (sInfo.flags & (TVHT_ONSUBITEM | TVHT_ONSUBLABEL | TVHT_ONITEM))) { + TreeListSetTrackItem(pData, U(sInfo.hItem), TVHT_SUBTOCOL(sInfo.flags)); + } + + UNLOCK(pData); + } + + if(wParam & pData->uDragFlags) // Drag beginnen + if(U(lParam) != pData->uLastMove) { + NMTREEVIEW sNotify; + BaseItem *pEntry; + int iDiffX; + int iDiffY; + + iDiffX = (short)(lParam) - (short)(pData->uLastMove); + iDiffY = (short)(lParam >> 16) - (short)(pData->uLastMove >> 16); + + if(iDiffX < 0) + iDiffX = -iDiffX; + if(iDiffY < 0) + iDiffY = -iDiffY; + + if(iDiffX <= 2 && iDiffY <= 2) { // Mehr als zwei Pixel-Verschub + return 0; + } + + LOCK(pData); + + if(pData->uDragItem > pData->uTreeItemsMax) { + UNLOCK(pData); + return 0; + } + + pEntry = pData->pTreeItems[pData->uDragItem]; + if(!pEntry) { + UNLOCK(pData); + return 0; + } + + sNotify.hdr.code = TVN_BEGINDRAG; + sNotify.action = (pEntry->uState & TVIS_EXPANDED) ? TVE_COLLAPSE : TVE_EXPAND; + sNotify.itemNew.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_SUBNUMBER; + sNotify.itemNew.hItem = (HTREEITEM)pData->uDragItem; + sNotify.itemNew.stateMask = 0xFFFFFFFF; + sNotify.itemNew.state = pEntry->uState; + sNotify.itemNew.lParam = pEntry->lParam; + sNotify.itemNew.cChildren = pData->uDragSub; + sNotify.itemNew.pszText = (LPTSTR) - 1; + sNotify.itemNew.cchTextMax = -1; + sNotify.itemOld.mask = 0; + sNotify.ptDrag.x = LOWORD(lParam); + sNotify.ptDrag.y = HIWORD(lParam); + pData->uDragFlags = 0; + pData->cClickFlag = 0; + pData->cClickEdit = 0; + + UNLOCK(pData); + + SendNotify(pData, &sNotify.hdr); + } + + pData->uLastMove = U(lParam); + + return 0; + + case WM_LBUTTONUP: // Linken Mausklick aufheben + + pData = GetHandle(hWnd); + pData->uDragFlags &= ~MK_LBUTTON; + + if(pData->cClickFlag || pData->cClickEdit) { + TreeListMouseClick(pData, uMsg, wParam, lParam); + pData->cClickFlag = 0; + pData->cClickEdit = 0; + } else { + TreeListMouseNotify(pData, TVN_LBUTTONUP, wParam, lParam); + } + + return 0; + + case WM_RBUTTONUP: // Linken Mausklick aufheben + + pData = GetHandle(hWnd); + pData->uDragFlags &= ~MK_RBUTTON; + + TreeListMouseNotify(pData, TVN_RBUTTONUP, wParam, lParam); + + break; + + case WM_LBUTTONDOWN: // Mausklick + + pData = GetHandle(hWnd); + + LOCK(pData); + + uTime = GetTickCount(); + + if(pData->cButtonFlag && pData->uToolTipItem) { // Doppelklick simulieren über ToolTip + pData->cButtonFlag = 0; + uDelta = uTime - pData->uButtonLast; + + if(uDelta < 700) { + iPos = abs(LOWORD(lParam) - LOWORD(pData->uButtonPos)); + iPos += abs(HIWORD(lParam) - HIWORD(pData->uButtonPos)); + + if(iPos <= 6) { + TreeListMouseClick(pData, WM_LBUTTONDBLCLK, wParam, lParam); + return 0; + } + } + } + + pData->cButtonFlag = 1; + pData->uButtonLast = uTime; + pData->uButtonPos = U(lParam); + + TreeListMouseClick(pData, uMsg, wParam, lParam); + + return 0; + + case WM_LBUTTONDBLCLK: + case WM_RBUTTONDOWN: + case WM_RBUTTONDBLCLK: + + pData = GetHandle(hWnd); + + LOCK(pData); + + pData->cButtonFlag = 0; + TreeListMouseClick(pData, uMsg, wParam, lParam); + + return 0; + + case WM_KEYDOWN: // Tastendruck + + pData = GetHandle(hWnd); + LOCK(pData); + TreeListKeyDown(pData, wParam, lParam); + + return 0; + + case WM_KEYUP: // Tastendruck + + DefWindowProc(hWnd, uMsg, wParam, lParam); + return 1; + + case WM_CHAR: // Zeicheneingabe + + pData = GetHandle(hWnd); + iMax = lParam & 0xFFFF; + + for(iPos = 0; iPos < iMax; iPos++) { + TreeListChar(pData, (UINT)wParam, lParam); + } + + return 0; + + case WM_DRAWITEM: // Weietleiten von OwnerDraw des Headers + + if(wParam == 1) { + pData = GetHandle(hWnd); + return SendMessage(GetParent(hWnd), uMsg, GetWindowLong(hWnd, GWL_ID), lParam); + } + + return 0; + + case WM_MOUSEWHEEL: // Scrollen mit dem Mausrad + + if(!(LOWORD(wParam) & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON))) { + pData = GetHandle(hWnd); + + if(pData->uEditItem) + return 0; + + iDif = (short)HIWORD(wParam) / (WHEEL_DELTA / 2); + + if(wParam & MK_CONTROL) { + iDif *= 9; + } else + if(wParam & MK_SHIFT) { + iDif *= 3; + } + + iPos = pData->uScrollY - iDif; + iMax = pData->uItemPosCount; + iMax -= pData->uPageEnties - 1; + + if(iPos >= iMax) + iPos = iMax; + if(iPos < 0) + iPos = 0; + if(iPos != (int)pData->uScrollY) + if(!(pData->uStyle & TVS_NOSCROLL)) { + pData->uScrollY = iPos; + SetScrollPos(hWnd, SB_VERT, iPos, TRUE); + UpdateView(pData); + } + + return 1; + } + + return 0; + + case WM_HSCROLL: // Vertikalles scrollen + + pData = GetHandle(hWnd); + iPos = pData->uScrollX; + + if(pData->uEditItem) + return 0; + + switch(LOWORD(wParam)) { + case SB_LINEDOWN: + iPos += 16; + break; + case SB_LINEUP: + iPos -= 16; + break; + case SB_PAGEDOWN: + iPos += pData->uSizeX; + break; + case SB_PAGEUP: + iPos -= pData->uSizeX; + break; + case SB_THUMBPOSITION: + iPos = HIWORD(wParam); + break; + case SB_THUMBTRACK: + iPos = HIWORD(wParam); + break; + } + + uVal = pData->uColumnCount; + if(uVal) + iMax = pData->aColumnXpos[uVal] - pData->uSizeX / 2; + else + iMax = pData->iMaxSizeX; + iMax -= pData->uSizeX / 2; + + if(iPos >= iMax) + iPos = iMax; + if(iPos < 0) + iPos = 0; + if(iPos != (int)pData->uScrollX) { + pData->uScrollX = iPos; + SetScrollPos(hWnd, SB_HORZ, iPos, TRUE); + UpdateView(pData); + + if(pData->hHeader) { + MoveWindow(pData->hHeader, -iPos, 0, pData->uSizeX + iPos, pData->uStartPixel, TRUE); + } + } + + return 0; + + case WM_VSCROLL: // Vertikalles scrollen + + pData = GetHandle(hWnd); + iPos = pData->uScrollY; + + if(pData->uEditItem) + return 0; + + switch(LOWORD(wParam)) { + case SB_LINEDOWN: + iPos++; + break; + case SB_LINEUP: + iPos--; + break; + case SB_PAGEDOWN: + iPos += (pData->uPageEnties > 1) ? pData->uPageEnties - 1 : 1; + break; + case SB_PAGEUP: + iPos -= (pData->uPageEnties > 1) ? pData->uPageEnties - 1 : 1; + break; + case SB_THUMBPOSITION: + case SB_THUMBTRACK: + if(pData->uItemPosCount < 0x7F00) { + iPos = HIWORD(wParam); + } else { + sScroll.cbSize = sizeof(SCROLLINFO); + sScroll.fMask = SIF_TRACKPOS; + GetScrollInfo(hWnd, SB_VERT, &sScroll); + iPos = sScroll.nTrackPos; + } + } + + iMax = pData->uItemPosCount; + iMax -= pData->uPageEnties - 1; + + if(iPos >= iMax) + iPos = iMax; + if(iPos < 0) + iPos = 0; + if(iPos != (int)pData->uScrollY) { + pData->uScrollY = iPos; + SetScrollPos(hWnd, SB_VERT, iPos, TRUE); + UpdateView(pData); + } + + return 0; + + case WM_SYSCOLORCHANGE: // Wurden die Systemfarben verändert + + pData = GetHandle(hWnd); + + LOCK(pData); + + if(pData->hTheme) { + pCloseThemeData(pData->hTheme); + pData->hTheme = NULL; + } + + if(pData->hThemeBt) { + pCloseThemeData(pData->hThemeBt); + pData->hThemeBt = NULL; + } + + if(pOpenThemeData) { + pData->hTheme = pOpenThemeData(hWnd, L"TREEVIEW"); + pData->cGlyphOk = (char)((pData->hTheme) ? 1 : 0); + } + + if(pData->iStatesMode) + CreateStateImageList(pData, 0); + if(pData->iChecksMode) + CreateStateImageList(pData, 1); + + if(pData->hHeader) + SendMessage(pData->hHeader, WM_SYSCOLORCHANGE, 0, 0); + UpdateColorsList(pData); + UNLOCK(pData); + + return 0; + + case WM_GETFONT: //tell what font we are using + { + HFONT hFont; + pData = GetHandle(hWnd); + + LOCK(pData); + hFont = pData->hFontN; + UNLOCK(pData); + return (LRESULT)hFont; + } + + case WM_SETFONT: // Einen neuen Font zuweisen + + pData = GetHandle(hWnd); + + LOCK(pData); + + if(CreateFontset(pData, (HFONT)wParam)){ + if(UpdateFont(pData)) + UpdateView(pData); + } + + UNLOCK(pData); + + return 0; + + case WM_STYLECHANGED: // Hat sich der Fenstersytle geändert + + if(wParam == GWL_STYLE) { + pData = GetHandle(hWnd); + lParam = ((STYLESTRUCT *)lParam)->styleNew; + + LOCK(pData); + uChange = U(lParam) ^ pData->uStyle; + pData->uStyle = U(lParam); + + if(uChange & (TVS_CHECKBOXES | TVS_NONEVENHEIGHT)) { + if(lParam & TVS_CHECKBOXES) { + if(pData->hStates == NULL) { + CreateStateImageList(pData, 0); + } + } else { + if(pData->iStatesMode && pData->hStates) { + if(pData->hStates != THEMEIMGLIST) { + ImageList_Destroy(pData->hStates); + } + + pData->hStates = NULL; + pData->iStatesMode = 0; + } + } + + UpdateHeight(pData); + UpdateScrollY(pData); + } + + if(uChange & (TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT | TVS_RTLREADING | TVS_CHECKBOXES | TVS_TRACKSELECT)) { + if(!(pData->uStyle & (TVS_HASBUTTONS | TVS_HASLINES))) { + pData->cHasRootRow = 0; + } else { + pData->cHasRootRow = (char)((pData->uStyle & TVS_LINESATROOT) ? 1 : 0); + } + + UpdateView(pData); + } + + if(uChange & TVS_NOSCROLL) { + pData->uOldYCount = 0xFFFF; + pData->uOldXCount = 0xFFFF; + + UpdateScrollX(pData); + UpdateScrollY(pData); + } + + if(uChange & TVS_NOTOOLTIPS) { + if(pData->uStyle & TVS_NOTOOLTIPS) { + UpdateToolTip(pData, 0, 0); + } else { + CreateToolTip(pData); + } + } + + if(uChange & WS_DISABLED) { + pData->cIsEnabled = (char)((pData->uStyle & WS_DISABLED) ? 0 : 1); + UpdateView(pData); + if(!pData->cIsEnabled) + UpdateToolTip(pData, 0, 0); + } + + UNLOCK(pData); + } + + return 0; + + case WM_PAINT: // Das Fenster zeichnen + + if(wParam) { + TreeListDraw(hWnd, (HDC)wParam, NULL); + } else { + PAINTSTRUCT sPaint; + + hDc = BeginPaint(hWnd, &sPaint); + + if(pBeginBufferedPt && pEndBufferedPt) { + RECT sRect; + HDC hDcBuffer; + HANDLE hBufferedPaint; + + GetClientRect(hWnd, &sRect); + hBufferedPaint = pBeginBufferedPt(hDc, &sRect, BPBF_COMPATIBLEBITMAP, NULL, &hDcBuffer); + + if(hBufferedPaint) { + TreeListDraw(hWnd, hDcBuffer, &sRect); + pEndBufferedPt(hBufferedPaint, TRUE); + } else { + TreeListDraw(hWnd, hDc, &sRect); + } + } else { + TreeListDraw(hWnd, hDc, &sPaint.rcPaint); + } + + EndPaint(hWnd, &sPaint); + } + + return 0; + + case WM_TIMER: // Timer Funktion + + if(wParam == ID_TOOLTIPCHECK) { + pData = GetHandle(hWnd); + if(pData->uToolTipItem) { + if(pData->uToolTipShow) { // Verzögertes einblenden + pData->uToolTipShow--; + if(pData->uToolTipShow) + return 0; + + LOCK(pData); + + GetCursorPos(&sPoint); + + sInfo.pt.x = sPoint.x; + sInfo.pt.y = sPoint.y; + + ScreenToClient(hWnd, &sInfo.pt); + + if(WindowFromPoint(sPoint) == hWnd) { + TreeListHitTest(pData, &sInfo); + } else { + sInfo.hItem = 0; + sInfo.flags = 0; + } + + if(sInfo.flags & TVHT_ONITEM) + if(pData->uToolTipItem == U(sInfo.hItem) && pData->uToolTipSub == 0) { + TOOLINFO sInfo; + + UNLOCK(pData); + + sInfo.cbSize = sizeof(sInfo); + sInfo.hwnd = pData->hWnd; + sInfo.uId = (UINT_PTR)pData->hWnd; + + if(pData->uToolTipItem) { + SendMessage(pData->hToolTip, TTM_TRACKACTIVATE, 0, (LPARAM)&sInfo); + } + + SendMessage(pData->hToolTip, TTM_TRACKPOSITION, 0, MAKELONG(pData->sToolTipPos.x, pData->sToolTipPos.y)); + SendMessage(pData->hToolTip, TTM_TRACKACTIVATE, 1, (LPARAM)&sInfo); + + return 0; + } + + if(sInfo.flags & TVHT_ONSUBITEM) + if(pData->uToolTipItem == U(sInfo.hItem) && TVHT_SUBTOCOL(sInfo.flags) == pData->uToolTipSub) { + TOOLINFO sInfo; + + UNLOCK(pData); + + sInfo.cbSize = sizeof(sInfo); + sInfo.hwnd = pData->hWnd; + sInfo.uId = (UINT_PTR)pData->hWnd; + + if(pData->uToolTipItem) { + SendMessage(pData->hToolTip, TTM_TRACKACTIVATE, 0, (LPARAM)&sInfo); + } + + SendMessage(pData->hToolTip, TTM_TRACKPOSITION, 0, MAKELONG(pData->sToolTipPos.x, pData->sToolTipPos.y)); + SendMessage(pData->hToolTip, TTM_TRACKACTIVATE, 1, (LPARAM)&sInfo); + + return 0; + } + + // ToolTip wieder entferenen + UpdateToolTip(pData, U(sInfo.hItem), sInfo.flags); + UNLOCK(pData); + } + + LOCK(pData); + + GetCursorPos(&sInfo.pt); + ScreenToClient(hWnd, &sInfo.pt); + TreeListHitTest(pData, &sInfo); + UpdateToolTip(pData, U(sInfo.hItem), sInfo.flags); + + UNLOCK(pData); + } + } + + return 0; + + case WM_COMMAND: // Kommando Nachrichnten + + if(wParam == MAKELONG(3, EN_KILLFOCUS) || wParam == MAKELONG(3, EN_RETURN) || wParam == MAKELONG(3, CBN_KILLFOCUS)) { + pData = GetHandle(hWnd); + if(pData->uEditItem) { + LOCK(pData); + TreeListEndLabelEdit(pData, (wParam == MAKELONG(3, EN_RETURN)) ? 2 : 1); + UNLOCK(pData); + if(pData->uEditMode) + if(wParam != MAKELONG(3, EN_KILLFOCUS)) { + SetFocus(pData->hWnd); + } + } + } else + if(wParam == MAKELONG(3, EN_ESCAPE)) { // ESC-Taste in Edit-Fenster + pData = GetHandle(hWnd); + if(pData->uEditItem) { + LOCK(pData); + TreeListEndLabelEdit(pData, 0); + UNLOCK(pData); + if(pData->uEditMode) + SetFocus(pData->hWnd); + } + } // Änderung in Edit-Fenster + else + if(wParam == MAKELONG(3, EN_CHANGE) || wParam == MAKELONG(3, CBN_EDITCHANGE) || wParam == MAKELONG(3, CBN_SELCHANGE) || wParam == MAKELONG(3, EN_SETTEXT)) { + pData = GetHandle(hWnd); + pData->cColumnStart = 1; + } + + return 0; + + case WM_NOTIFY: // Notify Nachrichnten + + if(wParam == 1) { // Nachricht vom Header + NMHEADER *pHdr = (NMHEADER *)lParam; + HDITEM sItem; + HDITEM sTemp; + RECT sRect; + int iDelta; + int iCode; + int iSize; + int iNext; + int iCol; + int iSub; + + iCode = pHdr->hdr.code; + // Hat sich die Spaltenbreite verändert + if(iCode == HDN_ITEMCHANGED && (pHdr->pitem->mask & HDI_WIDTH)) { + pData = GetHandle(hWnd); + iCol = pHdr->iItem; + + if(pData->aColumn[iCol].sReal != pHdr->pitem->cxy) { + iCode = HDN_TRACK; + } + } + + if(iCode == HDN_BEGINDRAG) { // Drag&Drop im Header + pData = GetHandle(hWnd); + + if(!(pData->uStyleEx & TVS_EX_HEADERDRAGDROP) || pHdr->iItem == 0) { + return 1; + } + + return 0; + } + + if(iCode == HDN_ENDDRAG) { // Drag&Drop im Header fertig + if(pHdr->pitem->iOrder == 0 || pHdr->iItem == 0) { + return 1; + } + + pData = GetHandle(hWnd); + + if(!(pData->uStyleEx & TVS_EX_HEADERDRAGDROP)) { + return 1; + } + + PostMessage(hWnd, TVM_SETCOLUMNORDERARRAY, FROM_HEADER, 0); + + return 0; + } + + if(iCode == HDN_BEGINTRACK) { // User will die Spaltenbreite ändern + pData = GetHandle(hWnd); + + if(!pData->cHasFocus) { + SetFocus(pData->hWnd); + } + + if(pData->uStyleEx & TVS_EX_NOCOLUMNRESIZE) { // Darf der User die Spaltenbreite ändern + return 1; + } + + if(pData->uStyleEx & TVS_EX_FIXEDCOLSIZE) { // Fixe gesammte Spaltenbreite + iNext = pData->aColumn[pHdr->iItem].bIndex; + + for(iNext++;; iNext++) { // Suche nächste veränerbare Spalte + if(U(iNext) >= pData->uColumnCount) { + return 1; + } + + iSub = pData->aColumnPos[iNext]; + + if(pData->aColumn[iSub].sFixed == 0) { + break; + } + } + } + + if(U(pHdr->iItem) < pData->uColumnCount) // Darf die Spalte verändert werden + if(pData->aColumn[pHdr->iItem].sFixed) { + POINT sPoint; + int iCol; + int iSub; + + if(pData->aColumn[pHdr->iItem].sReal || pHdr->iItem < 0) { + return 1; + } + + GetCursorPos(&sPoint); // Die nächste veränderbare Spalte greifen + ScreenToClient(pData->hHeader, &sPoint); + + PostMessage(pData->hHeader, WM_LBUTTONUP, 0, MAKELONG(sPoint.x, sPoint.y)); + + iCol = pData->aColumn[pHdr->iItem].bIndex; + iSub = pData->aColumnPos[iCol - 1]; + + PostMessage(pData->hHeader, WM_LBUTTONDOWN, 0, MAKELONG(pData->aColumnXpos[iSub + 1] - 2, sPoint.y)); + + return 1; + } + } + + if(iCode == HDN_DIVIDERDBLCLICK) { // Doppelcklick auf Spaltentrenner + pData = GetHandle(hWnd); + + if(!pData->cHasFocus) { + SetFocus(pData->hWnd); + } + + if(pData->uStyleEx & TVS_EX_NOCOLUMNRESIZE) { // Darf der User die Spaltenbreite ändern + return 0; + } + + if(U(pHdr->iItem) < pData->uColumnCount) // Darf die Spalte verändert werden + if(pData->aColumn[pHdr->iItem].sFixed) { + return 0; + } + + LOCK(pData); + + iSize = TreeListScanColumn(pData, pHdr->iItem); + if(iSize) { // Spalte auf maximale Textbreite + sItem.cxy = iSize; + pHdr->pitem = &sItem; + iCode = HDN_ENDTRACK; + + if(pData->aColumn[pHdr->iItem].bMinEx) // Minimale Breite prüfen + if(pData->aColumn[pHdr->iItem].sMin > sItem.cxy) { + sItem.cxy = pData->aColumn[pHdr->iItem].sMin; + } + } + + UNLOCK(pData); + } + + if(iCode == HDN_TRACK || iCode == HDN_ENDTRACK) { // Wurde die Spaltenbreite verändert + pData = GetHandle(hWnd); + + if(!pData->cHasFocus && iCode == HDN_ENDTRACK) { + SetFocus(pData->hWnd); + } + + LOCK(pData); + + iCol = pHdr->iItem; + sItem.mask = HDI_WIDTH; + sItem.cxy = pHdr->pitem->cxy; + + if(pData->aColumn[iCol].bMinEx) // Minimale Breite prüfen + if(pData->aColumn[iCol].sMin > sItem.cxy) { + sItem.cxy = pData->aColumn[iCol].sMin; + pHdr->pitem->cxy = sItem.cxy; + + if(sItem.cxy == pData->aColumn[iCol].sSize) { + UNLOCK(pData); + return 0; + } + } + + if(pData->uStyleEx & TVS_EX_FIXEDCOLSIZE) { // Fixe gesammte Spaltenbreite + if(iCol == 0 && sItem.cxy <= 0) { + sItem.cxy = 1; + pHdr->pitem->cxy = 1; + + if(sItem.cxy == pData->aColumn[iCol].sSize) { + UNLOCK(pData); + return 0; + } + } + + iNext = pData->aColumn[iCol].bIndex; + + for(iNext++;; iNext++) { // Überspringe fixierte Spalten + if(U(iNext) >= pData->uColumnCount) { + UNLOCK(pData); + return 0; + } + + iSub = pData->aColumnPos[iNext]; + + if(pData->aColumn[iSub].sFixed == 0) { + break; + } + } + + iDelta = pData->aColumn[iCol].sReal - sItem.cxy; + sTemp.cxy = pData->aColumn[iSub].sReal + iDelta; + + if(iDelta < 0) { // Nächste Spalte wird zu klein + if(sTemp.cxy < 0) { + sTemp.cxy = 0; + iDelta = -pData->aColumn[iSub].sReal; + sItem.cxy = pData->aColumn[iCol].sReal - iDelta; + } + + if(pData->aColumn[iSub].bMinEx) // Minimale Breite prüfen + if(pData->aColumn[iSub].sMin > sTemp.cxy) { + iDelta += pData->aColumn[iSub].sMin - sTemp.cxy; + sTemp.cxy = pData->aColumn[iSub].sMin; + sItem.cxy = pData->aColumn[iCol].sReal - iDelta; + } + + pHdr->pitem->cxy = sItem.cxy; + + if(iDelta >= 0) { // Keine Änderung + UNLOCK(pData); + return 0; + } + } + + if(pData->aColumn[iSub].bWeight) { // Variable Spalte + pData->iVarSize -= pData->aColumn[iSub].sSize; + pData->iVarSize += sTemp.cxy; + } else { // Fixe Spalte + pData->iFixSize -= pData->aColumn[iSub].sSize; + pData->iFixSize += sTemp.cxy; + } + + sTemp.mask = HDI_WIDTH; + + iDif = pData->aColumn[iSub].sReal; + pData->aColumn[iSub].sSize = (short)sTemp.cxy; + pData->aColumn[iSub].sReal = (short)sTemp.cxy; + Header_SetItem(pData->hHeader, iSub, &sTemp); + + // Breite verändert + if(iDif != sTemp.cxy && (pData->uStyleEx & TVS_EX_HEADERCHGNOTIFY)) { + TV_COLSIZE sNotify; + + sNotify.hdr.code = TVN_COLUMNCHANGED; + sNotify.uColumn = iSub; + sNotify.uIndex = pData->aColumn[iSub].bIndex; + sNotify.uPosX = pData->aColumnXpos[iSub]; + sNotify.iSize = sTemp.cxy; + + UNLOCK(pData); + SendNotify(pData, &sNotify.hdr); + LOCK(pData); + } + } + + if(pData->aColumn[iCol].bWeight) { // Ist es eine variable Spalte + pData->iVarSize -= pData->aColumn[iCol].sSize; + pData->iVarSize += sItem.cxy; + } else { // Fixe Spalte + pData->iFixSize -= pData->aColumn[iCol].sSize; + pData->iFixSize += sItem.cxy; + } + + iDif = pData->aColumn[iCol].sReal; + pData->aColumn[iCol].sSize = (short)sItem.cxy; + pData->aColumn[iCol].sReal = (short)sItem.cxy; + Header_SetItem(pData->hHeader, iCol, &sItem); + + iSize = UpdateColumns(pData); + if(iSize < 0x10000) { // Spalten neu zeichnen + GetClientRect(hWnd, &sRect); + sRect.left = iSize; + sRect.left -= pData->uScrollX; + InvalidateRect(hWnd, &sRect, FALSE); + } + + UpdateScrollX(pData); + UNLOCK(pData); + + // Breite verändert + if(iDif != sItem.cxy && (pData->uStyleEx & TVS_EX_HEADERCHGNOTIFY)) { + TV_COLSIZE sNotify; + + sNotify.hdr.code = TVN_COLUMNCHANGED; + sNotify.uColumn = iCol; + sNotify.uIndex = pData->aColumn[iCol].bIndex; + sNotify.uPosX = pData->aColumnXpos[iCol]; + sNotify.iSize = sItem.cxy; + + UNLOCK(pData); + SendNotify(pData, &sNotify.hdr); + LOCK(pData); + } + } + // Weiterleiten an Elternfenster + if(iCode == HDN_ITEMCLICK || iCode == HDN_ITEMDBLCLICK) { + pData = GetHandle(hWnd); + + if(!pData->cHasFocus) { + SetFocus(pData->hWnd); + } + + SendNotify(pData, &pHdr->hdr); + } + } else { + NMHDR *pHdr = (NMHDR *)lParam; + NMTTDISPINFO *pInfo; + + switch(pHdr->code) { + case TTN_GETDISPINFO: + + pData = GetHandle(hWnd); + pInfo = (NMTTDISPINFO *)pHdr; + pInfo->lpszText = pData->pToolTipText; + SendMessage(pData->hToolTip, WM_SETFONT, (WPARAM)pData->hFontT, 0); + break; + } + } + + return 0; + + case WM_GETDLGCODE: // Welche Tasten werden im Dialog benutzt + + pMsg = (MSG *)lParam; + if(pMsg && pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RETURN) { + return DLGC_WANTALLKEYS; + } + + return DLGC_WANTCHARS | DLGC_WANTARROWS; + + case WM_PASTE: // Weiterleiten an Edit-Control + case WM_CLEAR: + case WM_COPY: + case WM_CUT: + + pData = GetHandle(hWnd); + + if(pData->hEdit) { + PostMessage(pData->hEdit, uMsg, 0, 0); + return 0; + } + + break; + + case TVM_SETIMAGELIST: // Die Image-Liste einstellen + + pData = GetHandle(hWnd); + + LOCK(pData); +#ifdef _DEBUG + char dbg[255]; + sprintf(dbg, "TVM_SETIMAGELIST %d", (int)wParam); + OutputDebugStringA(dbg); +#endif + switch((int)wParam) { + case TVSIL_NORMAL: + lRet = (LPARAM)pData->hImages; + if(lRet == lParam) + break; + + pData->hImages = (HIMAGELIST)lParam; + if(!pData->hImages) { + pData->iImagesXsize = 0; + pData->iImagesYsize = 0; + } else { + IMAGEINFO sInfo; + ImageList_GetImageInfo(pData->hImages, 0, &sInfo); + pData->iImagesXsize = sInfo.rcImage.right - sInfo.rcImage.left; + pData->iImagesYsize = sInfo.rcImage.bottom - sInfo.rcImage.top; + if(pData->hHeader && ((pData->uStyleEx & TVS_EX_HEADEROWNIMGLIST) == 0)) + SendMessage(pData->hHeader, HDM_SETIMAGELIST, 0, (LPARAM)pData->hImages); + } + + if(!pData->iSubImgMode || pData->hSubImg == pData->hImages) { + pData->iSubImgMode = 0; + pData->hSubImg = pData->hImages; + pData->iSubImgXsize = pData->iImagesXsize; + pData->iSubImgYsize = pData->iImagesXsize; + } + + if(!pData->cFixedHeight) { + pData->iRowHeight = 1; + UpdateHeight(pData); + UpdateScrollY(pData); + } else { + UpdateView(pData); + } + + break; + + case TVSIL_HEADER: + lRet = pData->uStyleEx & TVS_EX_HEADEROWNIMGLIST; + if(lRet == 0) + break; + + lRet = (LPARAM)pData->hHeadImg; + if(lRet == lParam) + break; + + pData->hHeadImg = (HIMAGELIST)lParam; + + if(pData->hHeader) + SendMessage(pData->hHeader, HDM_SETIMAGELIST, 0, (LPARAM)pData->hHeadImg); + + if(!pData->cFixedHeight) { + pData->iRowHeight = 1; + UpdateHeight(pData); + UpdateScrollY(pData); + } else { + UpdateView(pData); + } + break; + + case TVSIL_STATE: + lRet = (LPARAM)pData->hStates; + if(lRet == lParam) + break; + + if(pData->iStatesMode) { + if(pData->hStates != THEMEIMGLIST) { + ImageList_Destroy(pData->hStates); + } + + pData->iStatesMode = 0; + } + + pData->hStates = (HIMAGELIST)lParam; + if(!pData->hStates) { + pData->iStatesXsize = 0; + pData->iStatesYsize = 0; + + if(pData->uStyle & TVS_CHECKBOXES) { + CreateStateImageList(pData, 0); + } + } else + if(pData->hStates == THEMEIMGLIST) { + pData->iStatesXsize = 16; + pData->iStatesYsize = 16; + pData->iStatesMode = 1; + if(!pData->hThemeBt) + CreateStateImageList(pData, 0); + } else { + IMAGEINFO sInfo; + ImageList_GetImageInfo(pData->hStates, 0, &sInfo); + pData->iStatesXsize = sInfo.rcImage.right - sInfo.rcImage.left; + pData->iStatesYsize = sInfo.rcImage.bottom - sInfo.rcImage.top; + pData->iStatesMode = 0; + } + + if(!pData->cFixedHeight) { + pData->iRowHeight = 1; + UpdateHeight(pData); + UpdateScrollY(pData); + } else { + UpdateView(pData); + } + + break; + + case TVSIL_CHECK: + lRet = (LPARAM)pData->hChecks; + if(lRet == lParam) + break; + + if(pData->iChecksMode) { + if(pData->hChecks != THEMEIMGLIST) { + ImageList_Destroy(pData->hChecks); + } + + pData->iChecksMode = 0; + } + + pData->hChecks = (HIMAGELIST)lParam; + if(!pData->hChecks) { + pData->iChecksXsize = 0; + pData->iChecksYsize = 0; + + for(uVal = 0; uVal < pData->uColumnCount; uVal++) { + if(pData->aColumn[uVal].bEdit < TVSIL_CHECK) + continue; + CreateStateImageList(pData, 1); + break; + } + } else + if(pData->hChecks == THEMEIMGLIST) { + pData->iChecksXsize = 16; + pData->iChecksYsize = 16; + pData->iChecksMode = 1; + if(!pData->hThemeBt) + CreateStateImageList(pData, 1); + } else { + IMAGEINFO sInfo; + ImageList_GetImageInfo(pData->hChecks, 0, &sInfo); + pData->iChecksXsize = sInfo.rcImage.right - sInfo.rcImage.left; + pData->iChecksYsize = sInfo.rcImage.bottom - sInfo.rcImage.top; + pData->iChecksMode = 0; + } + + if(!pData->cFixedHeight) { + pData->iRowHeight = 1; + UpdateHeight(pData); + UpdateScrollY(pData); + } else { + UpdateView(pData); + } + + break; + + case TVSIL_SUBIMAGES: + lRet = (LPARAM)pData->hSubImg; + if(lRet == lParam) + break; + + if(pData->iSubImgMode) { + ImageList_Destroy(pData->hSubImg); + pData->iSubImgMode = 0; + } + + pData->hSubImg = (HIMAGELIST)lParam; + if(!pData->hSubImg || pData->hSubImg == pData->hImages) { + pData->iSubImgMode = 0; + pData->hSubImg = pData->hImages; + pData->iSubImgXsize = pData->iImagesXsize; + pData->iSubImgYsize = pData->iImagesXsize; + } else { + IMAGEINFO sInfo; + ImageList_GetImageInfo(pData->hSubImg, 0, &sInfo); + pData->iSubImgXsize = sInfo.rcImage.right - sInfo.rcImage.left; + pData->iSubImgYsize = sInfo.rcImage.bottom - sInfo.rcImage.top; + pData->iSubImgMode = 1; + } + + if(!pData->cFixedHeight) { + pData->iRowHeight = 1; + UpdateHeight(pData); + UpdateScrollY(pData); + } else { + UpdateView(pData); + } + + break; + + default: + lRet = 0; + } + + UNLOCK(pData); + + return lRet; + + case TVM_GETIMAGELIST: // Die Image-Liste abfragen + + pData = GetHandle(hWnd); + if(!pData) + return 0; + + switch((int)wParam) { + case TVSIL_NORMAL: + lRet = (LPARAM)pData->hImages; + break; + case TVSIL_STATE: + lRet = (LPARAM)pData->hImages; + break; + case TVSIL_CHECK: + lRet = (LPARAM)pData->hChecks; + break; + case TVSIL_HEADER: + lRet = (LPARAM)pData->hHeadImg; + break; + default + : + lRet = 0; + } + + return lRet; + + case TVM_GETSETOPTION: // Diverse Optionen einstellen + + pData = GetHandle(hWnd); + lRet = 0; + + switch(U(wParam) & ~TVOP_WRITEOPTION) { + case TVOP_AUTOEXPANDOFF: // Icon Offset für TVS_EX_AUTOEXPANDICON + + lRet = pData->iAutoAdd; + + if((wParam & TVOP_WRITEOPTION) && lParam != lRet) { + LOCK(pData); + + pData->iAutoAdd = (int)lParam; + if(pData->uItemPosCount > 0) { + UpdateView(pData); + } + + UNLOCK(pData); + } + + break; + } + + return lRet; + + case TVM_GETITEMSTATE: // Die Statusbits eines Eintrags abfragen + + pData = GetHandle(hWnd); + LOCK(pData); + + if(U(wParam) <= pData->uTreeItemsMax) { + BaseItem *pEntry; + + pEntry = pData->pTreeItems[U(wParam)]; + if(!pEntry) + lRet = 0; + { + lRet = pEntry->uState; + } + } else { + lRet = 0; + } + + UNLOCK(pData); + + return lRet; + + case TVM_GETITEM: // Einträge abfragen + + pData = GetHandle(hWnd); + LOCK(pData); + lRet = TreeListGetItem(pData, (TV_ITEM *)lParam); + UNLOCK(pData); + + return lRet; + + case TVM_SETITEM: // Einträge einfügen + + pData = GetHandle(hWnd); + LOCK(pData); + lRet = TreeListSetItem(pData, (TV_ITEM *)lParam); + UNLOCK(pData); + + return lRet; + + case TVM_INSERTITEM: // Einträge einfügen + + pData = GetHandle(hWnd); + LOCK(pData); + lRet = TreeListInsertItem(pData, (TV_INSERTSTRUCT *)lParam); + UNLOCK(pData); + + return lRet; + + case TVM_DELETEITEM: // Einträge löschen + + pData = GetHandle(hWnd); + LOCK(pData); + lRet = TreeListDeleteItem(pData, U(lParam), (U(wParam) == 0x88) ? 2 : 1); + UNLOCK(pData); + + return lRet; + + case TVM_FINDITEM: // Einträge suchen + + pData = GetHandle(hWnd); + LOCK(pData); + lRet = TreeListFindItem(pData, U(wParam), (TV_FIND *)lParam); + UNLOCK(pData); + + return lRet; + + case TVM_DELETECOLUMN: // Löschen einer Salte im Header + + pData = GetHandle(hWnd); + LOCK(pData); + lRet = TreeListDeleteColumn(pData, U(wParam)); + UNLOCK(pData); + + return lRet; + + case TVM_INSERTCOLUMN: // Einfügen einer Salte im Header + + pData = GetHandle(hWnd); + LOCK(pData); + lRet = TreeListInsertColumn(pData, (int)wParam, (TV_COLUMN *)lParam); + UNLOCK(pData); + + return lRet; + + case TVM_GETCOLUMNCOUNT: // Abfragen der Spaltenanzahl + + pData = GetHandle(hWnd); + return (LRESULT)pData->uColumnCount; + + case TVM_GETHEADER: // Abfragen des Header-Fensters + + pData = GetHandle(hWnd); + return (LRESULT)pData->hHeader; + + case TVM_GETEXTENDEDSTYLE: // Abfragen der erweiterten Style-Bits + + pData = GetHandle(hWnd); + return pData->uStyleEx; + + case TVM_SETEXTENDEDSTYLE: // Einstellen der erweiterten Style-Bits + + pData = GetHandle(hWnd); + if(!wParam) + wParam = 0xFFFFFFFF; + + uVal = pData->uStyleEx & ~U(wParam); + uVal |= U(lParam) & U(wParam); + + if(pData->uStyleEx != uVal) { // Has it changed? + LOCK(pData); + uChange = pData->uStyleEx ^ uVal; + pData->uStyleEx = uVal; + + if(uChange & TVS_EX_AUTOHSCROLL) { + UpdateScrollX(pData); + } + + if((uChange & TVS_EX_GRAYEDDISABLE) && !pData->cIsEnabled) { + UpdateView(pData); + } + + if(uChange & TVS_EX_ITEMLINES) { + UpdateHeight(pData); + UpdateScrollY(pData); + } + + if(uChange & (TVS_EX_ALTERNATECOLOR | TVS_EX_SUBSELECT | TVS_EX_MULTISELECT | TVS_EX_FULLROWMARK | TVS_EX_FULLROWITEMS)) { + UpdateView(pData); + } + + if((uChange&~U(lParam)) & TVS_EX_SUBSELECT) { + TreeListSelectItem(pData, pData->uSelectedItem, 0, TVC_UNKNOWN); + } + + if(pData->hHeader){ + if((uChange & TVS_EX_HIDEHEADERS) || (uChange & TVS_EX_HEADEROWNIMGLIST)) { + pData->uStartPixel = (pData->uStyleEx & TVS_EX_HIDEHEADERS) ? 0 : bDrawWithTheme ? GetSystemMetrics(SM_CYHSCROLL) : 17; + MoveWindow(pData->hHeader, -(int)pData->uScrollX, 0, pData->uSizeX + pData->uScrollX, pData->uStartPixel, TRUE); + + if(pData->uStyleEx & TVS_EX_HEADEROWNIMGLIST){ + SendMessage(pData->hHeader, HDM_SETIMAGELIST, 0, (LPARAM)pData->hHeadImg); + } else { + SendMessage(pData->hHeader, HDM_SETIMAGELIST, 0, (LPARAM)pData->hImages); + } + + UpdateView(pData); + } + } + + UNLOCK(pData); + } + + return uVal; + + case TVM_GETINSERTMARKCOLOR: // Farben abfragen + wParam = TVC_INSERT; + goto ColGet; + case TVM_GETLINECOLOR: + wParam = TVC_LINE; + goto ColGet; + case TVM_GETTEXTCOLOR: + wParam = TVC_TEXT; + case TVM_GETBKCOLOR: + +ColGet: + if(wParam < 0 || wParam >= MAX_COLORS) + return 0xFFFFFFFF; + pData = GetHandle(hWnd); + return pData->uColors[U(wParam)]; + + case TVM_SETINSERTMARKCOLOR: // Farben einstellen + wParam = TVC_INSERT; + goto ColSet; + case TVM_SETLINECOLOR: + wParam = TVC_LINE; + goto ColSet; + case TVM_SETTEXTCOLOR: + wParam = TVC_TEXT; + case TVM_SETBKCOLOR: + +ColSet: + if(wParam < 0 || wParam >= MAX_COLORS) + return 0xFFFFFFFF; + + pData = GetHandle(hWnd); + + LOCK(pData); + + lRet = pData->uColors[U(wParam)]; + + if((COLORREF)lParam == TV_NOCOLOR) { // Standartfarbe einstellen + if(pData->cColorChanged[U(wParam)]) { + pData->cColorChanged[U(wParam)] = 0; + UpdateColorsList(pData); + UpdateView(pData); + } + } else { + pData->cColorChanged[U(wParam)] = 1; + pData->uColors [U(wParam)] = (COLORREF)lParam; + + if(lRet != lParam) { + UpdateColorsList(pData); + UpdateView(pData); + } + } + + UNLOCK(pData); + + return lRet; + + case TVM_HITTEST: // Abfragen von Koortinatenpositionen + + pData = GetHandle(hWnd); + + LOCK(pData); + lRet = TreeListHitTest(pData, (LPTV_HITTESTINFO)lParam); + UNLOCK(pData); + + return lRet; + + case TVM_SELECTCHILDS: // Mehrere Einträge auswählen + + pData = GetHandle(hWnd); + + LOCK(pData); + lRet = TreeListSelectChilds(pData, U(lParam), U(wParam)); + UNLOCK(pData); + + return lRet; + + case TVM_SELECTSUBITEM: // Einen (Sub)Eintrag auswählen + + pData = GetHandle(hWnd); + + LOCK(pData); + if(!(pData->uStyleEx & TVS_EX_SUBSELECT)) + wParam = 0; + lRet = TreeListSelectItem(pData, U(lParam), U(wParam), TVC_UNKNOWN); + if(lRet > 1) + lRet = 1; + UNLOCK(pData); + + return lRet; + + case TVM_SELECTDROP: // Den unterstrichenen Eintrags auswählen + + pData = GetHandle(hWnd); + + LOCK(pData); + lRet = TreeListSetTrackItem(pData, U(lParam), U(wParam)); + UNLOCK(pData); + + return lRet; + + case TVM_SELECTITEM: // Einen Eintrag auswählen + + pData = GetHandle(hWnd); + + LOCK(pData); + + switch(wParam) { + case TVGN_CARET: + lRet = TreeListSelectItem(pData, U(lParam), 0, TVC_UNKNOWN); + if(lRet > 1) + lRet = 1; + break; + case TVGN_DROPHILITE: + lRet = TreeListSetTrackItem(pData, U(lParam), 0); + break; + + case TVGN_FIRSTVISIBLE: + lRet = TreeListEnsureVisible(pData, U(lParam), FIRST_LINE); + lRet = (lRet < 0) ? 0 : 1; + break; + + default + : + lRet = 0; + } + + UNLOCK(pData); + + return lRet; + + case TVM_GETCOLUMNORDERARRAY: // Spaltensortierung abfragen + + if(!lParam) + return 0; + + pData = GetHandle(hWnd); + + if(!pData->hHeader) + return 0; + + return Header_GetOrderArray(pData->hHeader, U(wParam), lParam); + + case TVM_SETCOLUMNORDERARRAY: // Spalten sortieren + + pData = GetHandle(hWnd); + LOCK(pData); + lRet = TreeListSetOrderArray(pData, U(wParam), (unsigned *)lParam); + UNLOCK(pData); + + return lRet; + + case TVM_GETCOUNT: // Anzahl der Einträge abfragen + + pData = GetHandle(hWnd); + return pData->uTreeItemsCount; + + case TVM_GETINDENT: // Einrückung abfragen + + pData = GetHandle(hWnd); + return pData->iIndent; + + case TVM_SETINDENT: // Einrückung einstellen + + pData = GetHandle(hWnd); + lRet = pData->iIndent; + if(wParam < 5) + wParam = 5; + if(wParam > 64) + wParam = 64; + if(lRet != (LPARAM)wParam) { + LOCK(pData); + pData->iIndent = (int)wParam; + UpdateView(pData); + UNLOCK(pData); + } + + return lRet; + + case TVM_GETITEMHEIGHT: // Zeilenhöhe abfragen + + pData = GetHandle(hWnd); + return pData->iRowHeight; + + case TVM_SETITEMHEIGHT: // Zeilenhöhe abfragen + + pData = GetHandle(hWnd); + lRet = pData->iRowHeight; + + if(wParam == -1) { + LOCK(pData); + pData->cFixedHeight = 0; + UpdateHeight(pData); + UpdateScrollY(pData); + UNLOCK(pData); + return lRet; + } + + if(wParam & 1) + if(!(pData->uStyleEx & TVS_NONEVENHEIGHT)) + wParam--; + if(wParam < 1) + wParam = 1; + if(wParam > 256) + wParam = 256; + + if(lRet != (LPARAM)wParam) { + LOCK(pData); + pData->cFixedHeight = 1; + pData->iRowHeight = (int)wParam; + UpdateView(pData); + UNLOCK(pData); + } + + return lRet; + + case TVM_GETVISIBLECOUNT: // Abfragen der sichtbaren Zeilen + + pData = GetHandle(hWnd); + return pData->uPageEnties; + + case TVM_ENSUREVISIBLE: // Einen Eintrag ins Sichtfenster legen + + pData = GetHandle(hWnd); + LOCK(pData); + lRet = TreeListEnsureVisible(pData, U(lParam), (int)wParam); + UNLOCK(pData); + + return lRet; + + case TVM_ISITEMVISIBLE: // Ist ein Eintrag sichtbar + + pData = GetHandle(hWnd); + LOCK(pData); + lRet = TreeListIsVisible(pData, U(lParam), (int)wParam); + UNLOCK(pData); + + return lRet; + + case TVM_GETNEXTITEM: // Einen Eintrag suchen + + pData = GetHandle(hWnd); + LOCK(pData); + lRet = TreeListGetNextItem(pData, U(lParam), (int)wParam); + UNLOCK(pData); + + return lRet; + + case TVM_GETITEMRECT: // Das Rechteck eines Eintrages abfragen + + pData = GetHandle(hWnd); + LOCK(pData); + uVal = *(unsigned *)lParam; + lRet = TreeListGetItemRect(pData, uVal, U(wParam), (RECT *)lParam); + UNLOCK(pData); + + return lRet; + + case TVM_EXPAND: // Einen Eintrag umklappen + + pData = GetHandle(hWnd); + LOCK(pData); + lRet = 0; +NextExp: + if(U(lParam) <= pData->uTreeItemsMax) { + BaseItem *pEntry; +#ifndef __REACTOS__ + POINT sPoint; +#endif + + pEntry = pData->pTreeItems[U(lParam)]; + if(pEntry) { + sPoint.x = 0; + sPoint.y = 0; + + if(wParam & TVE_EXPANDNEXT) { // Bei erstem Eltereneintrag beginnen + if(!pEntry->uParent) { + UNLOCK(pData); + return 1; + } + + lParam = pEntry->uParent; + pEntry = pData->pTreeItems[U(lParam)]; + wParam &= ~TVE_EXPANDNEXT; + } + + switch(wParam & 0x0F) { + case TVE_COLLAPSE: + if(pEntry->uState & TVIS_EXPANDED) { + lRet = (TreeListToggleItem(pData, U(lParam), 0)) ? 0 : 1; + } else { // Nur Flag löschen + pEntry->uState &= ~TVIS_EXPANDPARTIAL; + lRet = 1; + } + // Kinder löschen + if(wParam & TVE_COLLAPSERESET) { + pEntry->uState &= TVIS_EXPANDEDONCE; + + while(pEntry->uLastChild) { + if(!TreeListDeleteItem(pData, pEntry->uLastChild, 1)) + break; + pEntry = pData->pTreeItems[U(lParam)]; + } + } + // Auch Eltern zuklappen + if(!(wParam & TVE_EXPANDRECURSIVE)) { + break; + } + + if(!pEntry->uParent) { + break; + } + + lParam = pEntry->uParent; + goto NextExp; + break; + + case TVE_EXPAND: + if(!(pEntry->uState & TVIS_EXPANDED)) { + uVal = (wParam & TVE_EXPANDPARTIAL) ? TVIS_EXPANDPARTIAL : 0; + lRet = (TreeListToggleItem(pData, U(lParam), uVal)) ? 0 : 1; + } else { // Schon aufgeklappt + lRet = 1; + + if(wParam & TVE_EXPANDPARTIAL) { + if(!(wParam & TVE_EXPANDFORCE)) + if((pEntry->uState & (TVIS_EXPANDPARTIAL | TVIS_EXPANDEDONCE)) != (TVIS_EXPANDPARTIAL | TVIS_EXPANDEDONCE)) { + lRet = (TreeListToggleItem(pData, U(lParam), TVE_EXPAND | TVIS_EXPANDPARTIAL)) ? 0 : 1; + } + } else { + if(pEntry->uState & TVIS_EXPANDPARTIAL) { + lRet = (TreeListToggleItem(pData, U(lParam), TVE_EXPAND)) ? 0 : 1; + } + } + } + // Auch Eltern aufklappen + if(!(wParam & TVE_EXPANDRECURSIVE)) { + break; + } + + if(!pEntry->uParent) { + break; + } + + lParam = pEntry->uParent; + goto NextExp; + break; + + case TVE_TOGGLE: + lRet = (TreeListToggleItem(pData, U(lParam), 0)) ? 0 : 1; + break; + } + + // Aktion auch auf alle Kinder anwenden + if((wParam & TVE_ALLCHILDS) && pEntry->uFirstChild) { + lParam = pEntry->uFirstChild; + wParam &= ~(TVE_EXPANDRECURSIVE | TVE_EXPANDNEXT | TVE_ONLYCHILDS); + sPoint.x = 0; + sPoint.y = 0; + + for(;;) { + UNLOCK(pData); + + TreeListProc(hWnd, uMsg, wParam, lParam); + + LOCK(pData); + + if(U(lParam) > pData->uTreeItemsMax) { + break; + } + + pEntry = pData->pTreeItems[U(lParam)]; + if(!pEntry || !pEntry->uNextItem) + break; + + lParam = pEntry->uNextItem; + } + } + } + } + + UNLOCK(pData); + + return lRet; + + case TVM_SETINSERTMARK: // Einfügemarke eintellen + + pData = GetHandle(hWnd); + + LOCK(pData); + lRet = TreeListSetInsertMark(pData, U(lParam), (int)wParam); + UNLOCK(pData); + + return lRet; + + case TVM_SETITEMBKCOLOR: // Hintergrundfarbe eines Eintrages ändern + + pData = GetHandle(hWnd); + + LOCK(pData); + lRet = TreeListSetItemColor(pData, U(wParam & 0xFFFFFF), U(wParam) >> 24, (COLORREF)lParam, 0); + UNLOCK(pData); + + return lRet; + + case TVM_SETITEMTEXTCOLOR: // Textfarbe eines Eintrages ändern + + pData = GetHandle(hWnd); + + LOCK(pData); + lRet = TreeListSetItemColor(pData, U(wParam & 0xFFFFFF), U(wParam) >> 24, (COLORREF)lParam, 1); + UNLOCK(pData); + + return lRet; + + case TVM_GETITEMBKCOLOR: // Hintergrundfarbe eines Eintrages abfragen + + pData = GetHandle(hWnd); + + LOCK(pData); + lRet = TreeListGetItemColor(pData, U(wParam), U(lParam), 0); + UNLOCK(pData); + + return lRet; + + case TVM_GETITEMTEXTCOLOR: // Textfarbe eines Eintrages abfragen + + pData = GetHandle(hWnd); + + LOCK(pData); + lRet = TreeListGetItemColor(pData, U(wParam), U(lParam), 1); + UNLOCK(pData); + + return lRet; + + case TVM_SORTCHILDRENEX: // Sortieren mit Funktion + + pData = GetHandle(hWnd); + + LOCK(pData); + lRet = TreeListSortItemsEx(pData, (TV_SORTEX *)lParam, (int)wParam); + UNLOCK(pData); + + return lRet; + + case TVM_SORTCHILDRENCB: // Sortieren mit Funktion + + pData = GetHandle(hWnd); + + LOCK(pData); + lRet = TreeListSortItemsCb(pData, (TV_SORTCB *)lParam, (int)wParam); + UNLOCK(pData); + + return lRet; + + case TVM_SORTCHILDREN: // Sortieren der Kindereinträge + + pData = GetHandle(hWnd); + + LOCK(pData); + lRet = TreeListSortItems(pData, U(lParam), (int)wParam); + UNLOCK(pData); + + return lRet; + + case TVM_GETITEMOFROW: // Hole den Eintrag von einer Reihe + + pData = GetHandle(hWnd); + + LOCK(pData); + if(U(lParam) < pData->uItemPosCount) + lRet = pData->pItemPos[U(lParam)]; + else + lRet = 0; + UNLOCK(pData); + + return lRet; + + case TVM_GETROWCOUNT: // Hole die Anzahl der sichtbaren Reihen + + pData = GetHandle(hWnd); + + return pData->uItemPosCount; + + case TVM_GETCOUNTPERPAGE: // Hole die Anzahl der darstelbaren Reihen + + pData = GetHandle(hWnd); + + return pData->uMaxEnties; + + case TVM_GETROWOFITEM: // Suche Reihe zu einem Eintrag + + pData = GetHandle(hWnd); + + LOCK(pData); + if(U(lParam) <= pData->uTreeItemsMax) { + BaseItem *pEntry; + + pEntry = pData->pTreeItems[U(lParam)]; + if(!pEntry) + lRet = -1; + else + lRet = pEntry->uShowPos - 1; + } else { + lRet = -1; + } + UNLOCK(pData); + + return lRet; + + case TVM_EDITLABEL: // Einen Eintrag editieren + + pData = GetHandle(hWnd); + if(!(pData->uStyle & TVS_EDITLABELS)) + return 0; + + LOCK(pData); + + if((wParam & 0xFFFFFF00) == TVLE_DONOTIFY) { // Notify starten + lRet = TreeListStartNotifyEdit(pData, U(lParam), TVIR_EDITCOL(wParam), VK_RETURN, 0); + } else { + pData->cColumnStart = 0; + lRet = (LRESULT)TreeListEditLabel(pData, U(lParam), U(wParam)); + } + + UNLOCK(pData); + + return lRet; + + case TVM_GETEDITCONTROL: // Das Handle des Edit-Fensters abfragen + + pData = GetHandle(hWnd); + return (LRESULT)pData->hEdit; + + case TVM_GETTOOLTIPS: // Das Handle des ToolTip-Fensters abfragen + + pData = GetHandle(hWnd); + return (LRESULT)pData->hToolTip; + + case TVM_SETTOOLTIPS: // Das Handle für das ToolTip-Fensters setzen + + pData = GetHandle(hWnd); + lRet = (LRESULT)pData->hToolTip; + pData->hToolTip = (HWND)wParam; + return lRet; + + case TVM_SETUSERDATASIZE: // Einstellen der Größe der User-Daten + + if(lParam < 0 || lParam > 0x1000000) + return -1; + pData = GetHandle(hWnd); + + LOCK(pData); + + if(pData->uTreeItemsCount > 0) { + lRet = 0; + } else { + pData->uUserDataSize = U(lParam); + lRet = lParam; + } + + UNLOCK(pData); + + return lRet; + + case TVM_GETUSERDATASIZE: // Abfragen der Größe der User-Daten + + pData = GetHandle(hWnd); + return pData->uUserDataSize; + + case TVM_GETUSERDATA: // Einen Zeiger auf die User-Daten holen + + pData = GetHandle(hWnd); + + if(pData->uUserDataSize && U(lParam) <= pData->uTreeItemsMax) { + BaseItem *pEntry; + + pEntry = pData->pTreeItems[U(lParam)]; + if(pEntry) + return (LRESULT)(pEntry + 1); + } + + return 0; + + case TVM_SETCOLUMN: // Einen Spalten-Header einstellen + + pData = GetHandle(hWnd); + + if(pData->hHeader && U(wParam) < pData->uColumnCount) { + HDITEM sItem; + unsigned uBit; + unsigned uCol; + TV_COLUMN *pCol; + + pCol = (TV_COLUMN *)lParam; + uCol = U(wParam); + + uVal = pCol->cx; + if(uVal == TVCF_LASTSIZE) { // Breite vor Fixierung benutzen + if(pData->aColumn[uCol].sFixed > 0) { + uVal = pData->aColumn[uCol].sFixed; + } else { + uVal = pData->aColumn[uCol].sReal; + } + } + + if(pCol->mask & TVCF_FIXED) { // Die Spalte fixieren + uBit = pCol->fmt & TVCFMT_FIXED; + + if(uBit != 0 && pData->aColumn[uCol].sFixed == 0) { + pData->aColumn[uCol].sFixed = pData->aColumn[uCol].sReal; + if(!pData->aColumn[uCol].sFixed) + pData->aColumn[uCol].sFixed = 100; + } + + if(uBit == 0 && pData->aColumn[uCol].sFixed != 0) { + pData->aColumn[uCol].sFixed = 0; + } + } + + sItem.mask = 0; + if(pCol->mask & TVCF_FMT){ + sItem.mask |= HDI_FORMAT; + sItem.fmt = pCol->fmt | HDF_STRING; + } + if(pCol->mask & TVCF_IMAGE){ + sItem.mask |= HDI_IMAGE; + sItem.iImage = pCol->iImage; + } + if(pCol->mask & TVCF_WIDTH){ + sItem.mask |= HDI_WIDTH; + sItem.cxy = uVal; + pData->aColumn[uCol].sSize = (short)sItem.cxy; + } + if(pCol->mask & TVCF_TEXT){ + sItem.mask |= HDI_TEXT; + sItem.pszText = pCol->pszText; + sItem.cchTextMax = pCol->cchTextMax; + } + + if(sItem.mask) { + lRet = SendMessage(pData->hHeader, HDM_SETITEM, uCol, (LPARAM)&sItem); + } else { + lRet = 1; + } + + if(lRet && (pCol->mask & TVCF_FMT)) { // Hat sich die Ausrichtung verändert + BYTE bAlign; + + switch(pCol->fmt) { + case TVCFMT_CENTER: bAlign = DT_CENTER; break; + case TVCFMT_RIGHT: bAlign = DT_RIGHT; break; + default: bAlign = DT_LEFT; break; + } + + if(pData->aColumn[uCol].bAlign != bAlign) { + pData->aColumn[uCol].bAlign = bAlign; + UpdateColRect(pData, uCol); + } + } + + if(lRet && (pCol->mask & TVCF_MARK)) { // Die Spalte markieren + RECT sRect; + unsigned uNext; + + uVal = (pCol->fmt & TVCFMT_MARK) ? 1 : 0; + if(uVal != pData->aColumn[uCol].bMark) { + pData->aColumn[uCol].bMark = (BYTE)uVal; + pData->uMarkedCols += uVal * 2 - 1; + + uNext = pData->aColumn [uCol ].bNext; + sRect.left = pData->aColumnXpos[uCol ]; + sRect.right = pData->aColumnXpos[uNext] + 1; + sRect.bottom = pData->uScrollX; + sRect.top = pData->uSizeY; + InvalidateRect(pData->hWnd, &sRect, FALSE); + } + } + } else { + lRet = 0; + } + + return lRet; + + case TVM_GETCOLUMN: // Einen Spalten-Header abfragen + + pData = GetHandle(hWnd); + if(pData->hHeader) { + HDITEM sItem; + TV_COLUMN *pCol = (TV_COLUMN *)lParam; + int bWantMark; + unsigned uCol; + + uCol = U(wParam); + sItem.mask = 0; + if(pCol->mask & TVCF_FMT) { + sItem.mask |= HDI_FORMAT; + bWantMark = pCol->fmt & TVCFMT_MARK; //memorize if we want the marked state + } + if(pCol->mask & TVCF_IMAGE){ + sItem.mask |= HDI_IMAGE; + } + if(pCol->mask & TVCF_WIDTH){ + sItem.mask |= HDI_WIDTH; + } + if(pCol->mask & TVCF_TEXT){ + sItem.mask |= HDI_TEXT; + sItem.pszText = pCol->pszText; + sItem.cchTextMax = pCol->cchTextMax; + } + + lRet = SendMessage(pData->hHeader, HDM_GETITEM, wParam, (LPARAM)&sItem); + + pCol->mask = 0; + + if(sItem.mask & HDI_FORMAT) { + pCol->mask |= TVCF_FMT; + pCol->fmt = sItem.fmt; + + if(bWantMark && pData->aColumn[uCol].bMark) + pCol->fmt |= TVCFMT_MARK; + if(!pData->aColumn[uCol].sReal && pData->aColumn[uCol].sFixed) + pCol->fmt |= TVCFMT_FIXED; + } + if(sItem.mask & HDI_IMAGE){ + pCol->mask |= TVCF_IMAGE; + pCol->iImage = sItem.iImage; + } + if(sItem.mask & HDI_WIDTH){ + pCol->mask |= TVCF_WIDTH; + pCol->cx = sItem.cxy; + } + if(sItem.mask & HDI_TEXT){ + pCol->mask |= TVCF_TEXT; + pCol->pszText = sItem.pszText; + pCol->cchTextMax = sItem.cchTextMax; + } + + } else { + lRet = 0; + } + + return lRet; + + case TVM_SETFOCUSITEM: // Focus einstellen + + pData = GetHandle(hWnd); + + LOCK(pData); + lRet = TreeListSetFocus(pData, U(lParam), U(wParam)); + UNLOCK(pData); + + return lRet; + + case TVM_SETCOLUMNWIDTH: // Die Spaltenbreite einstellen + + pData = GetHandle(hWnd); + + if(pData->hHeader && U(wParam) < pData->uColumnCount && (int)lParam >= 0) { + HDITEM sItem; + + sItem.mask = HDI_WIDTH; + sItem.cxy = (int)lParam; + lRet = SendMessage(pData->hHeader, HDM_SETITEM, wParam, (LPARAM)&sItem); + } else { + lRet = 0; + } + + return lRet; + + case TVM_COLUMNAUTOEDIT: // AutoEdit für eine Spalte einstellen + + pData = GetHandle(hWnd); + uVal = (wParam >> 11) & 0x3F; + + if(uVal >= pData->uColumnCount) { + return 0; + } + + uMode = (U(wParam) >> TVAE_MODEPOS) & 7; + + if(uMode == TVAX_NONE) { + wParam = 0; + } else + if(uVal == 0 && uMode >= TVAX_CHECK) { // Checkboxes are not allowed in the first column + return 0; + } + + LOCK(pData); + + uChange = pData->aColumn[uVal].bEdit << TVAE_MODEPOS; + uChange |= pData->aColumn[uVal].bFlags & TVAE_STATEENABLE; + + pData->aColumn[uVal].bEdit = (BYTE)(uMode); + pData->aColumn[uVal].bFlags = (BYTE)(wParam); + pData->aColumn[uVal].bCbChar = (BYTE)(wParam >> 17); + pData->aColumn[uVal].bCbSize = (BYTE)(wParam >> 25); + pData->aColumn[uVal].pCbData = (void *)lParam; + pData->aColumn[uVal].iCbIcon = -1; + + if(uMode >= TVAX_CHECK) { // Sollen Checkboxen dargestetllt werden + if(!pData->hChecks) { + CreateStateImageList(pData, 1); + UpdateHeight(pData); + } + + if((uChange ^ wParam) & ((TVAE_MODEMASK ^ TVAE_CHECK ^ TVAE_CHECKED) | TVAE_STATEENABLE)) { + UpdateColRect(pData, uVal); + } + } else + if((uChange & TVAE_MODEMASK) >= TVAE_CHECK) { // Waren Checkboxen dargestetllt + if((uChange ^ wParam) & ((TVAE_MODEMASK ^ TVAE_CHECK ^ TVAE_CHECKED) | TVAE_STATEENABLE)) { + UpdateColRect(pData, uVal); + } + } + + UNLOCK(pData); + + return 1; + + case TVM_COLUMNAUTOICON: // Icons für AutoEdit einstellen + + pData = GetHandle(hWnd); + uVal = U(wParam); + + if(uVal >= pData->uColumnCount || !pData->aColumn[uVal].bEdit) { + return 0; + } + + LOCK(pData); + + pData->aColumn[uVal].iCbIcon = (int)lParam; + + UNLOCK(pData); + + return 1; + + case TVM_GETCOLUMNWIDTH: // Die Spaltenbreite abfragen + + pData = GetHandle(hWnd); + if(pData->hHeader) { + HDITEM sItem; + + sItem.mask = HDI_WIDTH; + sItem.cxy = (int)lParam; + SendMessage(pData->hHeader, HDM_GETITEM, wParam, (LPARAM)&sItem); + lRet = sItem.cxy; + } else { + lRet = 0; + } + + return lRet; + + case TVM_CREATEDRAGIMAGE: // Ein Drag-Image erzeugen + + pData = GetHandle(hWnd); + LOCK(pData); + lRet = (LRESULT)CreateDragImage(pData, U(lParam), U(wParam)); + UNLOCK(pData); + return lRet; + + case TVM_ENDEDITLABELNOW: // Die aktuelle Eingabe abbrechen + + pData = GetHandle(hWnd); + LOCK(pData); + lRet = (TreeListEndLabelEdit(pData, (wParam) ? 0 : 1)) ? 1 : 0; + UNLOCK(pData); + return 0; + + case TVM_GETISEARCHSTRING: // Holt den aktuellen Suchtext + + uDelta = GetTickCount() - uKeyLast; + + if(!lParam) { + return (uDelta <= 750) ? uKeyPos : 0; + } + + if(uDelta > 750) { + ((TCHAR *)lParam)[0] = 0; + return FALSE; + } + + memcpy((TCHAR *)lParam, cKeyData, uKeyPos * sizeof(TCHAR)); + ((TCHAR *)lParam)[uKeyPos] = 0; + return TRUE; + + case TVM_GETUNICODEFORMAT: // Wird gerade UNI-Code verwendet + +#if UNICODE + return 1; +#else + return 0; +#endif + + } + + return DefWindowProc(hWnd, uMsg, wParam, lParam); +} + + +//***************************************************************************** +//* +//* TreeListDraw +//* +//***************************************************************************** +// Zeichnet das Fenster +static void TreeListDraw(HWND hWnd, HDC hDc, RECT *pRect) { + + COLORREF uEcColor; + COLORREF uEvColor; + COLORREF uBkColor; + COLORREF uBtColor; + COLORREF uOdColor; + COLORREF uOcColor; + COLORREF uFrColor; + COLORREF uInColor; + COLORREF uOldColor; + COLORREF uOutColor; + COLORREF uNextColor; + COLORREF uTempColor; + HRGN hRgnMain; + HRGN aRgn[MAX_COLUMNS + 1]; + SIZE sSize; + RECT sRect; + RECT sArea; + RECT sButton; + TreeListData *pData; + BaseItem *pTemp; + BaseItem *pEntry; + ExtraItem *pExtra; + LPCTSTR pText; + HIMAGELIST hImgList; + unsigned uTextSize; + unsigned uRgnCount; + unsigned uAutoMask; + unsigned uFirstPos; + unsigned uStyleEx; + unsigned uColMark; + unsigned uColumn; + unsigned uState; + unsigned uStyle; + unsigned uExtra; + unsigned uNext; + unsigned uMark; + unsigned uItem; + unsigned uBits; + unsigned uPos; + unsigned uMax; + int *pOffsets; + int iRnType[MAX_COLUMNS + 1]; + int iXscroll; + int iHeight; + int iIndent; + int iDelta; + int iImage; + int iShift; + int iStart; + int iCount; + int iLevel; + int iLast; + int iSize; + int iXpos; + int iYpos; + int iMaxX; + int iAdd; + int i; + + pData = GetHandle(hWnd); + + LOCK(pData); + + GetClientRect(hWnd, &sRect); + + if(!pRect) + pRect = &sRect; + + iXscroll = -(int)pData->uScrollX; + pOffsets = pData->aColumnXpos; + hRgnMain = CreateRectRgn(pRect->left, pRect->top, pRect->right, pRect->bottom); + + uMax = pData->uColumnCount; + if(!uMax) { + aRgn [ 0 ] = CreateRectRgn(sRect.left, sRect.top, sRect.right, sRect.bottom); + iRnType[ 0 ] = CombineRgn(aRgn[0], aRgn[0], hRgnMain, RGN_AND); + uRgnCount = 1; + } else { + for(uPos = 0; uPos < uMax; uPos++) { + uExtra = pData->aColumnPos[uPos ]; + uNext = pData->aColumnPos[uPos + 1]; + aRgn [uExtra] = CreateRectRgn(sRect.left + pOffsets[uExtra] + iXscroll, sRect.top, sRect.left + pOffsets[uNext] + iXscroll, sRect.bottom); + iRnType[uExtra] = CombineRgn(aRgn[uExtra], aRgn[uExtra], hRgnMain, RGN_AND); + } + + aRgn [uPos] = CreateRectRgn(sRect.left + pOffsets[uPos] + iXscroll, sRect.top, sRect.right, sRect.bottom); + iRnType[uPos] = CombineRgn(aRgn[uPos], aRgn[uPos], hRgnMain, RGN_AND); + + uRgnCount = uMax + 1; + } + + iHeight = pData->iRowHeight; + uStyleEx = pData->uStyleEx; + uStyle = pData->uStyle; + iIndent = pData->iIndent; + iShift = pData->iShift; + uPos = pData->uScrollY; + uMax = pData->uMaxEnties + uPos; + uNext = (pData->uColumnCount <= 1) ? 1 : pData->aColumn[1].bIndex; + uFirstPos = pData->aColumnXpos[uNext]; + + if(iRnType[0] == NULLREGION) + iMaxX = pData->iMaxSizeX; + else + iMaxX = uFirstPos - 1; + + if(uStyleEx & TVS_EX_ITEMLINES) { + iHeight--; + } + + if(uStyleEx & TVS_EX_AUTOEXPANDICON) { + uAutoMask = TVIS_EXPANDED; + } else { + uAutoMask = 0; + } + + if(uMax > pData->uItemPosCount) { + uMax = pData->uItemPosCount; + } + + uBkColor = pData->uColors[TVC_BK ]; + uFrColor = pData->uColors[TVC_FRAME]; + + if(pData->uStyleEx & TVS_EX_ALTERNATECOLOR) { // Abwechselnde Farben + uOdColor = pData->uColors[TVC_ODD ]; + uEvColor = pData->uColors[TVC_EVEN ]; + uOcColor = pData->uColors[TVC_COLODD ]; + uEcColor = pData->uColors[TVC_COLEVEN]; + } else { + uOdColor = uBkColor; + uEvColor = uBkColor; + uOcColor = pData->uColors[TVC_COLBK]; + uEcColor = pData->uColors[TVC_COLBK]; + } + + if(!pData->cIsEnabled) // Wenn Fenster gessperrt grau zeichnen + if(pData->uStyleEx & TVS_EX_GRAYEDDISABLE) { + uBkColor = pData->uColors[TVC_GRAYED]; + uEvColor = uEcColor; + uOdColor = uOcColor; + } + + uInColor = pData->uColors[TVC_LINE]; + uBtColor = pData->uColors[TVC_BOX ]; + iStart = 0; + iLast = 0; + + sArea.top = sRect.top + pData->uStartPixel; + SelectObject(hDc, pData->hFontN); + SelectObject(hDc, hPatternPen); + SetBkColor(hDc, uBkColor); + SetBkMode(hDc, TRANSPARENT); + SetTextAlign(hDc, TA_LEFT | TA_TOP); + SetTextColor(hDc, pData->uColors[TVC_TEXT]); + +//******************** Einträge zeichnen ************************************** + for(; uPos < uMax; uPos++) { // Alle Einträge ausgeben + uItem = pData->pItemPos[uPos]; + + pEntry = pData->pTreeItems[uItem]; + if(!pEntry) + break; + + if((pEntry->uState & TVIS_SELECTED) && (uStyleEx & TVS_EX_FULLROWMARK)) { + if(uStyleEx & TVS_EX_ALTERNATECOLOR) + uOutColor = (uPos & 1) ? pData->uColors[TVC_MARKODD] : pData->uColors[TVC_MARKEVEN]; + else + uOutColor = pData->uColors[TVC_MARK]; + + uMark = (unsigned)~TVIS_BKCOLOR; + uColMark = 0; + } else + if(uPos & 1) { // Farbe wechselweise ändern + uColMark = pData->aColumn[0].bMark; + uOutColor = (uColMark) ? uOcColor : uOdColor; + uMark = 0xFFFFFFFF; + } else { + uColMark = pData->aColumn[0].bMark; + uOutColor = (uColMark) ? uEcColor : uEvColor; + uMark = 0xFFFFFFFF; + } + + sArea.bottom = sArea.top + pData->iRowHeight; + sArea.left = iXscroll; + iLevel = pEntry->uLevel; + + if(iRnType[0] == NULLREGION) { + goto ExtraDraw; + } + + uBits = pEntry->uState & 0xFFFF; + uBits |= pEntry->bFlags << 16; + uBits &= uMark; + iImage = (uBits & LVIS_SELECTED) ? pEntry->iSelectedImage : pEntry->iImage; + pText = pEntry->pText; + uTextSize = pEntry->uTextSize; + + if(pData->uSelectedSub && uItem == pData->uSelectedItem) { + if(pData->uSelectedCount <= 1 || !(pData->uStyleEx & TVS_EX_SUBSELECT)) { + uBits &= ~TVIS_SELECTED; + } + } + + if(pEntry->bCallback) { + CallbackEntry(pData, pEntry, uItem, pEntry->bCallback, &iImage, &uTextSize, &pText); + + pEntry = pData->pTreeItems[uItem]; + if(!pEntry) + break; + } + + SelectObject(hDc, aRgn[0]); + + if((uStyleEx & (TVS_EX_ITEMLINES | TVS_EX_FULLROWITEMS)) == (TVS_EX_ITEMLINES | TVS_EX_FULLROWITEMS)) { + sButton.left = 0; + sButton.right = iIndent * (iLevel + 1) + 2; + sButton.bottom = sArea.bottom + 1; + sButton.top = sArea.bottom - 2; + + SetBkColor(hDc, uFrColor); + ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sButton, NULL, 0, NULL); + } + + if(pData->aColumn[0].bMark) { // Ist die erste Spalte markiert + uBkColor = pData->uColors[TVC_COLBK]; + } + + SetBkColor(hDc, (uStyleEx & TVS_EX_FULLROWITEMS) ? uOutColor : uBkColor); + + if(pData->cHasRootRow) + iLevel++; + + if(iLevel <= 0) + goto NoRootLines; + + if(uStyle & (TVS_HASBUTTONS | TVS_HASLINES)) { + iLevel--; + } + + if(uStyleEx & TVS_EX_FULLROWITEMS) { + sArea.bottom--; + iAdd = 1; + } else { + iAdd = 0; + } + + if(uStyle & TVS_HASLINES) { + pTemp = pData->pTreeItems[pEntry->uParent]; + sArea.right = sArea.left + 1; // Eine leerer Pixelreihe am Anfang + ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL); + sArea.left += iIndent * iLevel + 1; + + for(i = iLevel; i > 0; i--) { // Bereich vor Schaltflächen + sArea.right = sArea.left; + sArea.left -= iIndent; + + ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL); + + iXpos = sArea.left + iShift; + + if(pTemp) { + if(pTemp->uNextItem) { // Zeichne vertikale Linien + MoveToEx(hDc, iXpos, sArea.top | 1, NULL); + LineTo(hDc, iXpos, sArea.bottom + iAdd); + } + + pTemp = pData->pTreeItems[pTemp->uParent]; + } + } + + sArea.left += iIndent * iLevel; + } else { // Ohne Linien zeichnen + if(iLevel > 0) { + sArea.right = sArea.left + iIndent * iLevel; + ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL); + sArea.left += sArea.right; + } + } + + if(uStyle & TVS_HASBUTTONS) { // Fenster mit Schaltflächen ? + sArea.right = sArea.left + iIndent; + iXpos = sArea.left + iShift; + iYpos = sArea.top + pData->iRowHeight / 2; + + ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL); + + if(uStyle & TVS_HASLINES) { // Linien unter Schaltflächen + MoveToEx(hDc, iXpos, sArea.top | 1, NULL); + + if(pEntry->uNextItem) + LineTo(hDc, iXpos, sArea.bottom + iAdd); + else + LineTo(hDc, iXpos, iYpos + 1); + + MoveToEx(hDc, iXpos + 1 + (iYpos & 1), iYpos, NULL); + LineTo(hDc, sArea.right , iYpos); + } + + if(pEntry->bFlags & TVIX_HASBUTTON) { // Schaltflächen zeichnen + sButton.left = iXpos - 4; + sButton.top = iYpos - 4; + sButton.right = iXpos + 5; + sButton.bottom = iYpos + 5; + + if(pData->cGlyphOk) { // Thema benutzen + uState = ((uBits ^ TVIS_EXPANDED) & (TVIS_EXPANDED | TVIS_EXPANDPARTIAL)) ? GLPS_CLOSED : GLPS_OPENED; + pDrawThemeBackg(pData->hTheme, hDc, TVP_GLYPH, uState, &sButton, 0); + } else { + SetBkColor(hDc, uBtColor); + ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sButton, NULL, 0, NULL); + + sButton.left += 1; + sButton.top += 1; + sButton.right -= 1; + sButton.bottom -= 1; + + SetBkColor(hDc, pData->uColors[TVC_BOXBG]); + ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sButton, NULL, 0, NULL); + + sButton.left = iXpos - 2; + sButton.top = iYpos ; + sButton.right = iXpos + 3; + sButton.bottom = iYpos + 1; + + SetBkColor(hDc, uInColor); + ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sButton, NULL, 0, NULL); + + // '+' statt '-' Schaltfläsche zeichnenen + if((uBits ^ TVIS_EXPANDED) & (TVIS_EXPANDED | TVIS_EXPANDPARTIAL)) { + sButton.left = iXpos ; + sButton.top = iYpos - 2; + sButton.right = iXpos + 1; + sButton.bottom = iYpos + 3; + ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sButton, NULL, 0, NULL); + } + } + + SetBkColor(hDc, uBkColor); + } + + sArea.left += iIndent; + } else + if(uStyle & TVS_HASLINES) { // Nur Linien zeichnen ohne Schaltflächen + sArea.right = sArea.left + iIndent; + ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL); + + iYpos = sArea.top + pData->iRowHeight / 2; + iXpos = sArea.left + iShift; + MoveToEx(hDc, iXpos, sArea.top | 1, NULL); + + if(pEntry->uNextItem) + LineTo(hDc, iXpos, sArea.bottom); + else + LineTo(hDc, iXpos, iYpos + 1); + + MoveToEx(hDc, iXpos + 1 + (iYpos & 1), iYpos, NULL); + LineTo(hDc, sArea.right , iYpos); + + sArea.left += iIndent; + } + + if(uStyleEx & TVS_EX_FULLROWITEMS) + sArea.bottom++; + +NoRootLines: + + if(uStyleEx & TVS_EX_ITEMLINES) { // Linien um den Eintrag zeichnen + iAdd = 1; + sArea.right = sArea.left + 1; + + if(uStyleEx & TVS_EX_FULLROWITEMS) { + iStart = sArea.left; + iAdd = 0; + } else + if(iLevel >= 0) { + SetBkColor(hDc, uFrColor); + ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL); + + sArea.left++; + sArea.bottom--; + iStart = sArea.left; + } else { + ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL); + + sArea.left++; + sArea.bottom--; + iStart = sArea.left - 1; + } + } else { + iAdd = 0; + } + + SetBkColor(hDc, (uBits & TVIS_BKCOLOR) ? pEntry->uColorBk : uOutColor); + SelectObject(hDc, (uBits & TVIS_BOLD) ? pData->hFontB : pData->hFontN); + + if(pData->hStates) { // State-Icons anzeigen + sArea.right = sArea.left + pData->iStatesXsize; + iYpos = sArea.top + (iHeight - pData->iStatesYsize) / 2; + i = (uBits & LVIS_STATEIMAGEMASK) >> 12; + + sArea.right += iAdd; + ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL); + sArea.left += iAdd; + + if(pData->hStates == THEMEIMGLIST) { // Mit Thema zeichnen + if(pData->uStyleEx & TVS_EX_BITCHECKBOX) + i++; + + if(i >= 1 && i <= 2) { + uState = (i == 1) ? CBS_UNCHECKEDNORMAL : CBS_CHECKEDNORMAL; + pDrawThemeBackg(pData->hThemeBt, hDc, BP_CHECKBOX, uState, &sArea, 0); + } + } else { + ImageList_Draw(pData->hStates, i, hDc, sArea.left, iYpos, ILD_TRANSPARENT); + } + + sArea.left += pData->iStatesXsize; + iAdd = 0; + } + + if(pData->hImages && iImage != TV_NOIMAGE) { // Icon zeichnen vom Haupteintrag + if(iImage >= TV_SECONDICON) { // Sub-Image-Liste verwenden + if(iImage & TV_NOAUTOEXPAND) { + iImage &= ~TV_NOAUTOEXPAND; // Kein Auto-Expant bei diesem Icon + } else + if((pEntry->uState & uAutoMask) && pEntry->uFirstChild) { + iImage += 1; // Auto-Expant aktivieren + } + + iImage -= TV_SECONDICON; + sArea.right = sArea.left + pData->iSubImgXsize; + iYpos = sArea.top + (iHeight - pData->iSubImgYsize) / 2; + pEntry->bFlags |= TVIX_HASIMAGE; + + sArea.right += iAdd; + ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL); + sArea.left += iAdd; + ImageList_Draw(pData->hSubImg, iImage, hDc, sArea.left, iYpos, ILD_TRANSPARENT | (uBits & (TVIS_OVERLAYMASK | LVIS_CUT))); + + sArea.left += pData->iSubImgXsize; + iAdd = 0; + } else { // Haup-Image-Liste verwenden + if(iImage & TV_NOAUTOEXPAND) { + iImage &= ~TV_NOAUTOEXPAND; // Kein Auto-Expant bei diesem Icon + } else + if((pEntry->uState & uAutoMask) && pEntry->uFirstChild) { + iImage += pData->iAutoAdd; // Auto-Expant aktivieren + } + + sArea.right = sArea.left + pData->iImagesXsize; + iYpos = sArea.top + (iHeight - pData->iImagesYsize) / 2; + pEntry->bFlags |= TVIX_HASIMAGE; + + sArea.right += iAdd; + ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL); + sArea.left += iAdd; + ImageList_Draw(pData->hImages, iImage, hDc, sArea.left, iYpos, ILD_TRANSPARENT | (uBits & (TVIS_OVERLAYMASK | LVIS_CUT))); + + sArea.left += pData->iImagesXsize; + iAdd = 0; + } + } else { + pEntry->bFlags &= ~TVIX_HASIMAGE; + } + + sArea.right = uFirstPos; // Text ausgeben vom Haupteintrag + iYpos = sArea.top + (iHeight - pData->iFontHeight) / 2; + + if(uBits & (TVIS_SELECTED | TVIS_DROPHILITED | TVIS_UNDERLINE | TVIS_TRACKED | TVIS_TEXTCOLOR | TVIS_FOCUSED)) { + // Das Feld speziel zeichnen + TCHAR *pPtr = (TCHAR *)new(TCHAR, uTextSize + 4); + INT *pPos = (INT *)new(INT, uTextSize + 4); + + ExtTextOut(hDc, 0, 0, ETO_OPAQUE | ETO_CLIPPED, &sArea, NULL, 0, NULL); + + sButton.top = iYpos; + sButton.left = sArea.left + 4; + sButton.right = sArea.right; + sButton.bottom = iYpos + pData->iFontHeight + 2; + + if(!uTextSize) { // Feld ohne Text ? + sButton.right -= 2; + sButton.bottom--; + pEntry->iTextPixels = 0; + } else { + if(pData->uStyleEx & TVS_EX_FULLROWMARK) + if(pData->cHasFocus == 0 || uItem != pData->uSelectedItem || pData->uSelectedSub) + if((uBits & TVIS_SELECTED) && !(uBits & (TVIS_DROPHILITED | TVIS_FOCUSED))) { + sButton.left -= 1; + } + + DrawText(hDc, pText, uTextSize, &sButton, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX | DT_CALCRECT); + pEntry->iTextPixels = sButton.right - sButton.left; + } + + // Passt der Text in die Spalte + if(sButton.left + pEntry->iTextPixels >= (int)(sArea.right - pData->uScrollX)) { + iSize = sArea.right - pData->uScrollX - sButton.left - 2; + iSize -= (uBits & TVIS_BOLD) ? pData->uTrippleB : pData->uTrippleN; + if(iSize < 3) { + iCount = 0; + } else { + GetTextExtentExPoint(hDc, pText, uTextSize, iSize, &iCount, pPos, &sSize); + } + // Temporären Text mit "..." erzeugen + memcpy(pPtr , pText, iCount * sizeof(TCHAR)); + memcpy(pPtr + iCount, _T("..."), 4 * sizeof(TCHAR)); + + pText = pPtr; + uTextSize = iCount + 3; + sButton.right = sArea.right - 2; + } + + // Das Feld selektiert zeichnen + if((uBits & TVIS_SELECTED) && pData->cHasFocus && uItem == pData->uSelectedItem && !pData->uSelectedSub) { + uTempColor = GetSysColor(COLOR_HIGHLIGHTTEXT); + SelectObject(hDc, GetSysColorBrush(COLOR_HIGHLIGHT)); + Rectangle(hDc, sButton.left - 2, sButton.top - 1, sButton.right + 2, sButton.bottom + 1); + } else { + if(uBits & TVIS_DROPHILITED) { + uTempColor = GetSysColor(COLOR_HIGHLIGHTTEXT); + SelectObject(hDc, GetSysColorBrush(COLOR_HIGHLIGHT)); + SelectObject(hDc, GetStockObject(NULL_PEN)); + Rectangle(hDc, sButton.left - 2, sButton.top - 1, sButton.right + 2, sButton.bottom + 1); + } else + if(uBits & TVIS_SELECTED) { // Ist das Feld ohne Focus ausgewählt + if(pData->cHasFocus) { + uTempColor = GetSysColor(COLOR_HIGHLIGHTTEXT); + SelectObject(hDc, GetSysColorBrush(COLOR_HIGHLIGHT)); + SelectObject(hDc, GetStockObject(NULL_PEN)); + } else { + if(uBits & TVIS_TEXTCOLOR) + uTempColor = pEntry->uColorText; + else + uTempColor = pData ->uColors[TVC_TEXT]; + + if(pData->uStyleEx & TVS_EX_FULLROWMARK) + SelectObject(hDc, GetStockObject(NULL_BRUSH)); + else + SelectObject(hDc, GetSysColorBrush(COLOR_3DFACE)); + + SelectObject(hDc, GetStockObject(NULL_PEN)); + } + + Rectangle(hDc, sButton.left - 2, sButton.top - 1, sButton.right + 2, sButton.bottom + 1); + } else { // Das Feld normal zeichnen + if(uBits & TVIS_TRACKED) + uTempColor = pData ->uColors[TVC_TRACK]; + else + if(uBits & TVIS_TEXTCOLOR) + uTempColor = pEntry->uColorText; + else + uTempColor = pData ->uColors[TVC_TEXT ]; + sButton.right--; + sButton.left --; + } + + SelectObject(hDc, hPatternPen); + + if(uBits & TVIS_FOCUSED) { // Einen punktierten Rahmen um den Text zeichnen + SelectObject(hDc, GetStockObject(NULL_BRUSH)); + Rectangle(hDc, sButton.left - 2, sButton.top - 1, sButton.right + 2, sButton.bottom + 1); + } + } + + SetTextColor(hDc, uTempColor); + sButton.left += pData->iFontOff; + DrawText(hDc, pText, uTextSize, &sButton, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX); + + if(uBits & (TVIS_UNDERLINE | TVIS_TRACKED)) // Text unterstreichen + if(pText && *pText) { + sButton.left -= pData->iFontOff; + sButton.right -= pData->iFontOff + 1; + sButton.top += pData->iFontLine; + sButton.bottom = sButton.top + 1; + uOldColor = SetBkColor(hDc, uTempColor); + ExtTextOut(hDc, 0, 0, ETO_OPAQUE | ETO_CLIPPED, &sButton, NULL, 0, NULL); + SetBkColor(hDc, uOldColor); + } + + SetTextColor(hDc, pData->uColors[TVC_TEXT]); + + delete(pPos); + delete(pPtr); + } else { // Das Feld normal ausgeben + if(!pEntry->iTextPixels && uTextSize) { + sButton.top = iYpos; + sButton.left = sArea.left + 4; + sButton.right = sArea.right; + sButton.bottom = iYpos + pData->iFontHeight + 2; + // Textbreite berechen + DrawText(hDc, pText, uTextSize, &sButton, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX | DT_CALCRECT); + + pEntry->iTextPixels = sButton.right - sButton.left; + } + + // Ist der Text größer als die Spalte + if(sArea.left + pEntry->iTextPixels >= (int)(sArea.right - pData->uScrollX)) { + INT *pPos = (INT *)new(INT, uTextSize); + + iSize = sArea.right - sArea.left - pData->uScrollX; + iSize -= (uBits & TVIS_BOLD) ? pData->uTrippleB : pData->uTrippleN; + if(iSize < 3) { + iCount = 0; + } else { + GetTextExtentExPoint(hDc, pText, uTextSize, iSize, &iCount, pPos, &sSize); + } + + if(iCount > 0) { // Passen noch Buchstaben vor "..." + sButton.right = sArea.right; + sArea.right = sArea.left + 2 + pPos[iCount - 1]; + + ExtTextOut(hDc, sArea.left + 2, iYpos, ETO_OPAQUE | ETO_CLIPPED, &sArea, pText, iCount, NULL); + + sArea.left = sArea.right; + sArea.right = sButton.right; + + ExtTextOut(hDc, sArea.left , iYpos, ETO_OPAQUE | ETO_CLIPPED, &sArea, _T("..."), 3, NULL); + } else { + ExtTextOut(hDc, sArea.left + 2, iYpos, ETO_OPAQUE | ETO_CLIPPED, &sArea, _T("..."), 3, NULL); + } + + delete(pPos); + } else { + ExtTextOut(hDc, sArea.left + 2, iYpos, ETO_OPAQUE | ETO_CLIPPED, &sArea, pText, uTextSize, NULL); + } + } + + i = sArea.left - iXscroll; + i += pEntry->iTextPixels + 5; + if(i > iMaxX) + iMaxX = i; + + if(uStyleEx & TVS_EX_ITEMLINES) { // Linien um den Eintrag zeichnen + SetBkColor(hDc, uFrColor); + + if(iLast > iStart) { // Ist das Feld nach links eingerückt gegenüber dem oberen + sArea.top--; + sArea.bottom = sArea.top + 1; + sArea.left = iStart - 1; + ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL); + sArea.top++; + } + + iLast = iStart; // Linie unter Feld zeichnen + sArea.top += iHeight; + sArea.left = iStart; + sArea.bottom = sArea.top + 1; + + ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL); + + sArea.top -= iHeight; + } + + //************ Extraeinträge zeichnen ********************************* + +ExtraDraw: + + uNextColor = uOutColor; // Hintergrundfarbe für die nächste Spalte + + if(pData->aColumn[0].bMark) { // Ist die erste Spalte markiert + uBkColor = pData->uColors[TVC_BK]; + } + + for(uColumn = 1; uColumn <= pData->uColumnCount; uColumn++) { // Extra Spalten zeichnen + uExtra = pData->aColumnPos[uColumn ]; + uNext = pData->aColumnPos[uColumn + 1]; + + if(pData->aColumn[uExtra].sReal == 0) // Ist die Spalte sichtbar + if(uColumn < pData->uColumnCount) { + continue; + } + + if(uColMark != pData->aColumn[uExtra].bMark) // Ist die Spalte anderst markiert + if(!(pEntry->uState & TVIS_SELECTED) || !(uStyleEx & TVS_EX_FULLROWMARK)) { + if(uPos & 1) { + uColMark = pData->aColumn[uExtra].bMark; + uOutColor = (uColMark) ? uOcColor : uOdColor; + } else { + uColMark = pData->aColumn[uExtra].bMark; + uOutColor = (uColMark) ? uEcColor : uEvColor; + } + } + + GetRgnBox(aRgn[uExtra], &sButton); + + if(iRnType[uExtra] == NULLREGION) + continue; + + SelectObject(hDc, aRgn[uExtra]); + + sArea.left = pData->aColumnXpos[uExtra]; + sArea.left += iXscroll; + + if(uStyleEx & TVS_EX_ITEMLINES) { // Linie um den Eintrag zeichnen + SetBkColor(hDc, uFrColor); + // Linke Linie + sArea.right = sArea.left + 1; + sArea.bottom += 1; + + ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL); + + sArea.left += 1; // Untere Linie + sArea.top += iHeight; + sArea.bottom = sArea.top + 1; + sArea.right = pData->aColumnXpos[uNext]; + if(uColumn < pData->uColumnCount) + sArea.right += iXscroll; + + ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL); + + sArea.top -= iHeight; + sArea.bottom -= 1; + iAdd = 1; + } + + if(sArea.left > (int)pData->uSizeX) + break; // Noch im sichtbaren Bereich + + sArea.right = pData->aColumnXpos[uNext]; + + if(uColumn < pData->uColumnCount) { // Ist es die letze Spalte ? + sArea.right += iXscroll; + pExtra = pData->pExtraItems[uExtra - 1][uItem]; + if(!pExtra) + uNextColor = uOutColor; + } else { + pExtra = 0; + } + + if(!pExtra) { // Leeres Feld zeichnen + SetBkColor(hDc, uNextColor); + ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL); + } else { + iSize = pData->iSubImgXsize; + hImgList = pData->hSubImg; + iImage = pExtra->iImage; + pText = pExtra->pText; + uTextSize = pExtra->uTextSize; + uBits = (pExtra->uState & 0xFFFF); + uBits |= (pExtra->bFlags << 16); + uBits |= pEntry->uState & TVIS_BASEFLAGS; + uBits &= uMark; + + if(uExtra != pData->uSelectedSub) { + uBits &= ~TVIS_SELECTED; + } + + if(pExtra->bCallback) { // Text über Callback holen + CallbackExtra(pData, pEntry, pExtra, uItem, uExtra, pExtra->bCallback, &iImage, &uTextSize, &pText); + pExtra = pData->pExtraItems[uExtra - 1][uItem]; + if(!pExtra) + break; + } + + uNextColor = (uBits & TVIS_BKCOLOR) ? pExtra->uColorBk : uOutColor; + SetBkColor(hDc, uNextColor); + + if(pData->aColumn[uExtra].bEdit >= TVAX_CHECK) { // Checkboxen statt Icons + hImgList = pData->hChecks; + iSize = pData->iChecksXsize; + iImage = (pExtra->uState & TVIS_STATEIMAGEMASK) >> 12; + uBits &= ~TVIS_OVERLAYMASK; + + if(iImage & 8) + if(pData->aColumn[uExtra].bFlags & TVAE_STATEENABLE) { + iImage &= 7; + } + } + + if(hImgList && iImage > TV_NOIMAGE) { // Icon zeichnen + sArea.right = sArea.left + iSize + 2; + iYpos = sArea.top + (iHeight - iSize) / 2; + pExtra->bFlags |= TVIX_HASIMAGE; + + SelectObject(hDc, (uBits & TVIS_BOLD) ? pData->hFontB : pData->hFontN); + ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL); + + if(hImgList == THEMEIMGLIST) { // Mit Thema zeichnen + if(pData->uStyleEx & TVS_EX_BITCHECKBOX) + iImage++; + + if(iImage == 1 || iImage == 2) { + uState = (iImage == 1) ? CBS_UNCHECKEDNORMAL : CBS_CHECKEDNORMAL; + pDrawThemeBackg(pData->hThemeBt, hDc, BP_CHECKBOX, uState, &sArea, 0); + } + } else { + ImageList_Draw(hImgList, iImage, hDc, sArea.left + 1, iYpos, ILD_TRANSPARENT | (uBits & (TVIS_OVERLAYMASK | LVIS_CUT))); + } + + sArea.left += iSize + 1; + sArea.right = pData->aColumnXpos[uNext]; + sArea.right += iXscroll; + } else { + pExtra->bFlags &= ~TVIX_HASIMAGE; + } + + iYpos = sArea.top + (iHeight - pData->iFontHeight) / 2; + SelectObject(hDc, (uBits & TVIS_BOLD) ? pData->hFontB : pData->hFontN); + + if(uBits & (TVIS_SELECTED | TVIS_DROPHILITED | TVIS_UNDERLINE | TVIS_TRACKED | TVIS_TEXTCOLOR | TVIS_FOCUSED)) { + // Text ausgeben in spezilem Format + TCHAR *pPtr = (TCHAR *)new(TCHAR, uTextSize + 4); + INT *pPos = (INT *)new(INT, uTextSize + 4); + + ExtTextOut(hDc, 0, 0, ETO_OPAQUE | ETO_CLIPPED, &sArea, NULL, 0, NULL); + + sButton.top = iYpos; + sButton.left = sArea.left + 4; + sButton.right = sArea.right; + sButton.bottom = iYpos + pData->iFontHeight + 2; + + if(!uTextSize) { + sButton.left--; + sButton.right -= 2; + sButton.bottom--; + pExtra->iTextPixels = 0; + } else { + if(pData->uStyleEx & TVS_EX_FULLROWMARK) + if(pData->cHasFocus == 0 || uItem != pData->uSelectedItem) + if((uBits & TVIS_SELECTED) && !(uBits & (TVIS_DROPHILITED | TVIS_FOCUSED))) { + sButton.left -= 2; + } + + DrawText(hDc, pText, uTextSize, &sButton, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX | DT_CALCRECT); + pExtra->iTextPixels = sButton.right - sButton.left; + } + + // Passt der Text in die Spalte + if(sButton.left + pExtra->iTextPixels >= sArea.right) { + if(uTextSize > 253) + uTextSize = 253; + + iSize = sArea.right - sButton.left - 2; + iSize -= (uBits & TVIS_BOLD) ? pData->uTrippleB : pData->uTrippleN; + if(iSize < 3) { + iCount = 0; + } else { + GetTextExtentExPoint(hDc, pText, uTextSize, iSize, &iCount, pPos, &sSize); + } + + memcpy(pPtr , pText, iCount * sizeof(TCHAR)); + memcpy(pPtr + iCount, _T("..."), 4 * sizeof(TCHAR)); + + pText = pPtr; + uTextSize = iCount + 3; + sButton.right = sArea.right - 2; + } + + switch(pData->aColumn[uExtra].bAlign) { // Textausrichtung ausgleichen + case DT_CENTER: + iDelta = sArea .right - sArea .left; + iDelta -= sButton.right - sButton.left; + iDelta -= 6; + iDelta /= 2; + sButton.right += iDelta; + sButton.left += iDelta; + break; + + case DT_RIGHT: + iDelta = sArea .right - sArea .left; + iDelta -= sButton.right - sButton.left; + iDelta -= 6; + sButton.right += iDelta; + sButton.left += iDelta; + break; + + } + // Ist das Feld ohne Focus ausgewählt + if((uBits & TVIS_SELECTED) && pData->cHasFocus && uItem == pData->uSelectedItem) { + uTempColor = GetSysColor(COLOR_HIGHLIGHTTEXT); + SelectObject(hDc, GetSysColorBrush(COLOR_HIGHLIGHT)); + Rectangle(hDc, sButton.left - 2, sButton.top - 1, sButton.right + 2, sButton.bottom + 1); + } else { + if(uBits & TVIS_DROPHILITED) { + uTempColor = GetSysColor(COLOR_HIGHLIGHTTEXT); + SelectObject(hDc, GetSysColorBrush(COLOR_HIGHLIGHT)); + SelectObject(hDc, GetStockObject(NULL_PEN)); + Rectangle(hDc, sButton.left - 2, sButton.top - 1, sButton.right + 2, sButton.bottom + 1); + } else + if(uBits & TVIS_SELECTED && uItem == pData->uSelectedItem) { + if(uBits & TVIS_TEXTCOLOR) + uTempColor = pEntry->uColorText; + else + uTempColor = pData->uColors[TVC_TEXT]; + + if(pData->uStyleEx & TVS_EX_FULLROWMARK) + SelectObject(hDc, GetStockObject(NULL_BRUSH)); + else + SelectObject(hDc, GetSysColorBrush(COLOR_3DFACE)); + + SelectObject(hDc, GetStockObject(NULL_PEN)); + Rectangle(hDc, sButton.left - 2, sButton.top - 1, sButton.right + 2, sButton.bottom + 1); + } else { + if(uBits & TVIS_TRACKED) + uTempColor = pData ->uColors[TVC_TRACK]; + else + if(uBits & TVIS_TEXTCOLOR) + uTempColor = pExtra->uColorText; + else + uTempColor = pData ->uColors[TVC_TEXT ]; + sButton.right--; + sButton.left --; + } + + SelectObject(hDc, hPatternPen); + + if(uBits & TVIS_FOCUSED) { // Punktierten Rahmen um Text zeichnen + SelectObject(hDc, GetStockObject(NULL_BRUSH)); + Rectangle(hDc, sButton.left - 2, sButton.top - 1, sButton.right + 2, sButton.bottom + 1); + } + } + + SetTextColor(hDc, uTempColor); + sButton.left += pData->iFontOff; + + DrawText(hDc, pText, uTextSize, &sButton, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX); + + if(uBits & (TVIS_UNDERLINE | TVIS_TRACKED)) // Text unterstreichen + if(pText && *pText) { + sButton.left -= pData->iFontOff; + sButton.right -= pData->iFontOff + 1; + sButton.top += pData->iFontLine; + sButton.bottom = sButton.top + 1; + uOldColor = SetBkColor(hDc, uTempColor); + ExtTextOut(hDc, 0, 0, ETO_OPAQUE | ETO_CLIPPED, &sButton, NULL, 0, NULL); + SetBkColor(hDc, uOldColor); + } + + SetTextColor(hDc, pData->uColors[TVC_TEXT]); + + delete(pPos); + delete(pPtr); + } else { // Den Text ganz normal ausgeben + if(!pExtra->iTextPixels) { + sButton.top = iYpos; + sButton.left = sArea.left + 4; + sButton.right = sArea.right; + sButton.bottom = iYpos + pData->iFontHeight + 2; + + if(uTextSize) { // Textbreite berechen + DrawText(hDc, pText, uTextSize, &sButton, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX | DT_CALCRECT); + pExtra->iTextPixels = sButton.right - sButton.left; + } else { + pExtra->iTextPixels = 0; + } + } + // Ist der Text größer als die Spalte + if(sArea.left + pExtra->iTextPixels >= sArea.right) { + INT *pPos = (INT *)new(INT, uTextSize); + + iSize = sArea.right - sArea.left; + iSize -= (uBits & TVIS_BOLD) ? pData->uTrippleB : pData->uTrippleN; + if(iSize < 3) { + iCount = 0; + } else { + GetTextExtentExPoint(hDc, pText, uTextSize, iSize, &iCount, pPos, &sSize); + } + + if(iCount > 0) { // Passen noch Buchstaben vor "..." + sButton.right = sArea.right; + sArea.right = sArea.left + 2 + pPos[iCount - 1]; + + ExtTextOut(hDc, sArea.left + 2, iYpos, ETO_OPAQUE | ETO_CLIPPED, &sArea, pText, iCount, NULL); + + sArea.left = sArea.right; + sArea.right = sButton.right; + + ExtTextOut(hDc, sArea.left , iYpos, ETO_OPAQUE | ETO_CLIPPED, &sArea, _T("..."), 3, NULL); + } else { + ExtTextOut(hDc, sArea.left + 2, iYpos, ETO_OPAQUE | ETO_CLIPPED, &sArea, _T("..."), 3, NULL); + } + + delete(pPos); + } else { + switch(pData->aColumn[uExtra].bAlign) { // Textausrichtung + case DT_CENTER: + SetTextAlign(hDc, TA_CENTER | TA_TOP); + ExtTextOut(hDc, (sArea.right + sArea.left) / 2, iYpos, ETO_OPAQUE | ETO_CLIPPED, &sArea, pText, uTextSize, NULL); + SetTextAlign(hDc, TA_LEFT | TA_TOP); + break; + + case DT_RIGHT: + SetTextAlign(hDc, TA_RIGHT | TA_TOP); + ExtTextOut(hDc, sArea.right - 2, iYpos, ETO_OPAQUE | ETO_CLIPPED, &sArea, pText, uTextSize, NULL); + SetTextAlign(hDc, TA_LEFT | TA_TOP); + break; + + default + : + ExtTextOut(hDc, sArea.left + 2, iYpos, ETO_OPAQUE | ETO_CLIPPED, &sArea, pText, uTextSize, NULL); + break; + } + } + } + } + } + + sArea.top += pData->iRowHeight; + } + + if(sArea.top < sRect.bottom) { // Untere Fläche ohne Einträge füllen + SelectObject(hDc, hRgnMain); + + // Gibt es markierte Spalten + if(pData->uMarkedCols > 0 && (pData->cIsEnabled || !(pData->uStyleEx & TVS_EX_GRAYEDDISABLE))) { + sRect.right = 0 - pData->uScrollX; + + for(uColumn = 0; uColumn < pData->uColumnCount; uColumn++) { + uExtra = pData->aColumnPos[uColumn ]; + uNext = pData->aColumnPos[uColumn + 1]; + uMark = pData->aColumn[uExtra].bMark; + sRect.top = sArea.top; + sRect.left = sRect.right; + sRect.right = pData->aColumnXpos[uNext] + uMark; + sRect.right -= pData->uScrollX; + if(sRect.right == sRect.left + 1) + uMark = 0; + SetBkColor(hDc, (uMark) ? pData->uColors[TVC_COLBK] : uBkColor); + ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sRect, NULL, 0, NULL); + } + + sRect.top = sArea.top; + sRect.left = sRect.right; + sRect.right = pData->uSizeX; + SetBkColor(hDc, uBkColor); + ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sRect, NULL, 0, NULL); + } else { // Keine markierten Spalten + sRect.top = sArea.top; + SetBkColor(hDc, uBkColor); + ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sRect, NULL, 0, NULL); + } + } + + if(pData->iMaxSizeX != iMaxX) { // X-Scrollbar aktuallisieren + pData->iMaxSizeX = iMaxX; + if(pData->uColumnCount == 0) + if(pData->iMaxSizeX != (int)pData->uOldXCount) { + UpdateScrollX(pData); + } + } + + UNLOCK(pData); + + DeleteObject(hRgnMain); + + for(uPos = 0; uPos < uRgnCount; uPos++) { + DeleteObject(aRgn[uPos]); + } + +} diff --git a/base/setup/reactos/treelist.h b/base/setup/reactos/treelist.h new file mode 100644 index 00000000000..c6dd4dbac08 --- /dev/null +++ b/base/setup/reactos/treelist.h @@ -0,0 +1,493 @@ +/* + * 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.h +//* +//* +//***************************************************************************** +#ifndef __TREELISTWND_H__ +#define __TREELISTWND_H__ +#if _MSC_VER > 1000 +#pragma once +#endif + +#include + +/* Window Messaging */ +#ifndef SNDMSG +#ifdef __cplusplus +#define SNDMSG ::SendMessage +#else +#define SNDMSG SendMessage +#endif +#endif // ifndef SNDMSG + + +typedef int (CALLBACK *PFNTVCOMPAREEX)(HWND hWnd,HTREEITEM hItem1,HTREEITEM hItem2,LPARAM lParam1,LPARAM lParam2,LPARAM lParam); +typedef int (CALLBACK *PFNTVSORTEX )(HWND hWnd,HTREEITEM hItem ,LPCTSTR pTextItem,LPCTSTR pTextInsert,LPARAM lParamItem,LPARAM lParamInsert); + +typedef struct _TVSORTEX { + HTREEITEM hParent; + PFNTVCOMPAREEX lpfnCompare; + LPARAM lParam; +} TVSORTEX, *LPTVSORTEX; + +typedef struct _TVFIND { + UINT uFlags; + UINT uColumn; + UINT uState; + UINT uStateMask; + LPARAM lParam; + LPCTSTR pText; +} TVFIND, *LPTVFIND; + +typedef struct _TV_KEYDOWN_EX { + NMHDR hdr; + WORD wVKey; + WORD wScan; + UINT flags; +} TV_KEYDOWN_EX, *LPTV_KEYDOWN_EX; + +typedef struct _TV_STARTEDIT { + NMHDR hdr; + TVITEM item; + UINT uAction; + UINT uHeight; + UINT uMaxEntries; + LPCTSTR pTextEntries; + LPCTSTR *pTextList; + POINT ptAction; +} TV_STARTEDIT, *LPTV_STARTEDIT; + +typedef struct _TV_COLSIZE { + NMHDR hdr; + UINT uColumn; + UINT uIndex; + UINT uPosX; + INT iSize; +} TV_COLSIZE, *LPTV_COLSIZE; + +typedef TVSORTEX *LPTVSORTEX; +typedef TVSORTEX *LPTV_SORTEX; +typedef TVSORTEX TV_SORTEX; + + +#define TVCOLUMN LV_COLUMN +#define TV_COLUMN LV_COLUMN +#define TV_FIND TVFIND +#define TV_NOIMAGE (-2) +#define TV_NOCOLOR 0xFFFFFFFF +#define TV_NOAUTOEXPAND 0x20000000 +#define TV_SECONDICON 0x40000000 +#define TVLE_DONOTIFY 0xF5A5A500 +#define TVIF_TEXTPTR 0x80000000 +#define TVIF_TOOLTIPTIME 0x40000000 +#define TVIF_TEXTCHANGED 0x20000000 +#define TVIF_RETURNEXIT 0x10000000 +#define TVIF_CASE 0x08000000 +#define TVIF_NEXT 0x04000000 +#define TVIF_CHILD 0x02000000 +#define TVIF_CANCELED 0x01000000 +#define TVIF_ONLYFOCUS 0x00800000 +#define TVIF_SUBITEM 0x8000 +#define TVIF_SUBNUMBER 0x4000 +#define TVIS_UNDERLINE 0x0001 +#define TVSIL_CHECK 0x0003 +#define TVSIL_SUBIMAGES 0x0004 +#define TVSIL_HEADER 0x0005 +#define TVN_COLUMNCLICK HDN_ITEMCLICK +#define TVN_COLUMNDBLCLICK HDN_ITEMDBLCLICK +#define TVE_EXPANDRECURSIVE (0x80000000) +#define TVE_EXPANDFORCE (0x40000000) +#define TVE_EXPANDNEXT (0x20000000) +#define TVE_ALLCHILDS (0x10000000) +#define TVE_ONLYCHILDS (0x00000008) +#define TVI_SORTEX ((HTREEITEM) 0xFFFF0007) +#define TVI_BEFORE ((HTREEITEM) 0xFFFF0006) +#define TVI_AFTER ((HTREEITEM) 0xFFFF0005) +#define TVI_ROW(n) ((HTREEITEM)(0xFFE00000+(n))) +#ifndef VK_DBLCLK +#define VK_DBLCLK 0x10000 // Edit with doubleclick +#endif +#ifndef VK_ICONCLK +#define VK_ICONCLK 0x10001 // Edit with click on icon +#endif +#ifndef VK_EDITCLK +#define VK_EDITCLK 0x10002 // Edit with click on augewähltes Element //visble element ? +#endif +#ifdef UNICODE +#define TV_UINICODE 1 +#else +#define TV_UINICODE 0 +#endif + +#define TVC_CLASSNAME "SysTreeList32" + +// Color Constants (TVM_SETBKCOLOR) +#define TVC_BK 0 // Background +#define TVC_ODD 1 // alternate colors / odd (see TVS_EX_ALTERNATECOLOR) +#define TVC_EVEN 2 // alternate colors / even (see TVS_EX_ALTERNATECOLOR) +#define TVC_FRAME 3 // separator lines (see TVS_EX_ITEMLINES) +#define TVC_TEXT 4 // text +#define TVC_LINE 5 // interior of the buttons +#define TVC_BOX 6 // exterior of the buttons +#define TVC_TRACK 7 // tracked item text +#define TVC_MARK 8 // selected line +#define TVC_MARKODD 8 // selected line (odd) +#define TVC_MARKEVEN 9 // selected line (even) +#define TVC_INSERT 10 // insertion point +#define TVC_BOXBG 11 // background of buttons +#define TVC_COLBK 12 // background of marked column +#define TVC_COLODD 13 // alternate odd color of marked column +#define TVC_COLEVEN 14 // alternate even color of marked column +#define TVC_GRAYED 15 // background when disabled + + +// constants for GetNextItem (TVM_GETNEXTITEM) +#define TVGN_DROPHILITESUB 0x000C // get selected column +#define TVGN_CARETSUB 0x000D // Drophighilite Spalte holen +#ifndef TVGN_NEXTSELECTED +#define TVGN_NEXTSELECTED 0x000E // next selected entry +#endif +#define TVGN_FOCUS 0x000F // entry that has focus +#define TVGN_FOCUSSUB 0x0010 // column that has focus +#define TVGN_NEXTSELCHILD 0x0011 // next selected child entry +#define TVGN_LASTCHILD 0x0012 // last child entry +#define TVGN_NEXTITEM 0x0013 // to enumerate the entries + + +// constants for InsertColumn (mask) +#define TVCF_FMT LVCF_FMT // set the text alignment +#define TVCF_IMAGE LVCF_IMAGE // set column image +#define TVCF_TEXT LVCF_TEXT // set column text +#define TVCF_WIDTH LVCF_WIDTH // set fixed width +#define TVCF_VWIDTH LVCF_SUBITEM // set variable width +#define TVCF_MIN LVCF_ORDER // set minimum width +#define TVCF_MARK 0x80000000 // to mark a column +#define TVCF_FIXED 0x40000000 // can the column width can be changed +#define TVCF_LASTSIZE 0x44332211 // Breite vor dem Fixieren wieder herstellen + + +// constants for InsertColumn (format mask) +#define TVCFMT_BITMAP_ON_RIGHT LVCFMT_BITMAP_ON_RIGHT +#define TVCFMT_COL_HAS_IMAGES LVCFMT_COL_HAS_IMAGES +#define TVCFMT_CENTER LVCFMT_CENTER +#define TVCFMT_IMAGE LVCFMT_IMAGE +#define TVCFMT_LEFT LVCFMT_LEFT +#define TVCFMT_RIGHT LVCFMT_RIGHT +#define TVCFMT_FIXED 0x20000000 // flag for fixing the column +#define TVCFMT_MARK 0x10000000 // flag for marking the column + + +// constants for Column AutoEdit +#define TVAE_NONE (0<>24) // Convert column to column numbers mask +#define TVHT_SUBMASK 0xFF000000 // Maske in der die Spalte gespeichert wird +#define TVHT_ONRIGHTSPACE 0x00800000 // Auf rechtem Rand nach den Exträgen +#define TVHT_ONSUBLABEL 0x00400000 // Koordinate ist auf dem Text eines Extraeintrages +#define TVHT_ONSUBICON 0x00200000 // Koordinate ist auf einem Extraeintrag +#define TVHT_ONSUBRIGHT 0x00100000 // Koordinate ist auf einem Extraeintrag rechts vom Text +#define TVHT_ONSUBITEM (TVHT_ONSUBICON|TVHT_ONSUBLABEL) + + +// constants for GetItemRect (TVM_GETITEMRECT) +#define TVIR_COLTOSUB(u) ((u)<<24) // specify column +#define TVIR_GETCOLUMN 0x00000080 // get only column +#define TVIR_TEXT 0x00000001 // get only text area + + +// constants for SelectChilds (TVM_SELECTCHILDS) +#define TVIS_WITHCHILDS 0x00000001 // get also childs +#define TVIS_DESELECT 0x00000002 // deselect all items + +// constants for Options (TVM_GETSETOPTION) +#define TVOP_AUTOEXPANDOFF 0x00000001 // Icon Offset for TVS_EX_AUTOEXPANDICON +#define TVOP_WRITEOPTION 0x80000000 // write too + + +// constants for EditLabel (LVM_EDITLABEL) +#define TVIR_EDITCOMBOCHAR(n) (((n)&0xFF)<<8) // Separator of the combobox entries (only for Notify message) +#define TVIR_EDITCOMBODEL 0x00000080 // Clears the buffer for the entries (only for Notify message) +#define TVIR_EDITCOMBODOWN 0x10000000 // open the combobox (only for Notify message) +#define TVIR_EDITCOMBOBOX 0x20000000 // instead of an edit, display a combobox +#define TVIR_EDITCOMBOLIST 0x40000000 // instead of an edit, display a combobox with list selection +#define TVIR_EDITFULL 0x80000000 // the edit window takes the full width +#define TVIR_EDITTEXT 0x00000001 // show the edit window over the text (only for Notify message) +#define TVIR_EDITCOL(u) ((u)&0xFF) // auto edit column +#define TVIR_SELALL 0x00000000 // select all +#ifndef __REACTOS__ +#define TVIR_SELAREA(a,b) ((0x0C0000|(a&0x1FF)|((b&0x1FF)<<9))<<8) // select text area +#define TVIR_SETCURSOR(a) ((0x080000|(a&0x3FFFF))<<8) // Cursor auf Textstelle +#define TVIR_SETAT(a) ((0x040000|(a&0x3FFFF))<<8) // Cursor auf Pixel-Offset +#else // __REACTOS__ +#define TVIR_SELAREA(a,b) ((0x0C0000|((a)&0x1FF)|(((b)&0x1FF)<<9))<<8) // select text area +#define TVIR_SETCURSOR(a) ((0x080000|((a)&0x3FFFF))<<8) // Cursor auf Textstelle +#define TVIR_SETAT(a) ((0x040000|((a)&0x3FFFF))<<8) // Cursor auf Pixel-Offset +#endif // __REACTOS__ + +// constants for uStyleEx +#define TVS_EX_HEADEROWNIMGLIST 0x00000400 // the header has its own TVSIL_HEADER image list (else it is shared with TVSIL_NORMAL) +#define TVS_EX_HEADERCHGNOTIFY 0x00000800 // notify when a column has been resized +#define TVS_EX_HEADERDRAGDROP 0x00001000 // columns over 1 can be sorted using drag and drop +#define TVS_EX_SINGLECHECKBOX 0x00002000 // checkboxes with single selection +#define TVS_EX_STEPOUT 0x00004000 // can leave an edit control with the cursor buttons +#define TVS_EX_BITCHECKBOX 0x00008000 // change only the first bit of the state +#define TVS_EX_ITEMLINES 0x00010000 // draw the item separating lines +#define TVS_EX_ALTERNATECOLOR 0x00020000 // use alternate lines background color +#define TVS_EX_SUBSELECT 0x00040000 // allow to select columns +#define TVS_EX_FULLROWMARK 0x00080000 // the row mark fill the entire row +#define TVS_EX_TOOLTIPNOTIFY 0x00100000 // send a TVN_ITEMTOOLTIP notify to query for tooltip +#define TVS_EX_AUTOEXPANDICON 0x00200000 // use automaticaly the next icon for expanded items +#define TVS_EX_NOCHARSELCET 0x00400000 // disable looping selection of items via keyboard +#define TVS_EX_NOCOLUMNRESIZE 0x00800000 // user cannot change the columns width +#define TVS_EX_HIDEHEADERS 0x01000000 // hide the header +#define TVS_EX_GRAYEDDISABLE 0x02000000 // gray out the control when disabled +#define TVS_EX_FULLROWITEMS 0x04000000 // backgrounds and lines takes the whole line (use with TVS_EX_ALTERNATECOLOR and TVS_EX_ITEMLINES) +#define TVS_EX_FIXEDCOLSIZE 0x08000000 // the width of the whole columns stays constant when resizing columns (the right margin of last column do not change) +#define TVS_EX_HOMEENDSELECT 0x10000000 // move to first / last item with Home / End keys +#define TVS_EX_SHAREIMAGELISTS 0x20000000 // image list is not deleted when the control is destroyed +#define TVS_EX_EDITCLICK 0x40000000 // enter edit mode with a single click +#define TVS_EX_NOCURSORSET 0x80000000 // VK_EDITCLK always select the entire text. Don't set the cursor to the click point, if TVS_EX_EDITCLICK is used +#ifndef TVS_EX_MULTISELECT +#define TVS_EX_MULTISELECT 0x00000002 // allow multiple selections +#endif +#ifndef TVS_EX_AUTOHSCROLL +#define TVS_EX_AUTOHSCROLL 0x00000020 // auto scroll to selected item (horizontal scrollbar stays hidden) +#endif + + +// constants for notify messages +#define TVN_ITEMTOOLTIP (TVN_FIRST-32) // tooltip query +#define TVN_CBSTATECHANGED (TVN_FIRST-33) // checkbox change +#define TVN_STEPSTATECHANGED (TVN_FIRST-34) // autoedit state changed +#define TVN_STARTEDIT (TVN_FIRST-35) // field edit +#define TVN_LBUTTONUP (TVN_FIRST-36) // left button released +#define TVN_RBUTTONUP (TVN_FIRST-37) // right button released +#define TVN_COLUMNCHANGED (TVN_FIRST-38) // a column modified + + +// constants for new messages +#define TVM_GETHEADER (TV_FIRST+128-1) +#define TVM_GETCOLUMNCOUNT (TV_FIRST+128-2) +#define TVM_DELETECOLUMN (TV_FIRST+128-3) +#define TVM_INSERTCOLUMN (TV_FIRST+128-4-TV_UINICODE) +#define TVM_SELECTSUBITEM (TV_FIRST+128-6) +#define TVM_SELECTDROP (TV_FIRST+128-7) +#define TVM_SETITEMBKCOLOR (TV_FIRST+128-8) +#define TVM_GETITEMBKCOLOR (TV_FIRST+128-9) +#define TVM_SETITEMTEXTCOLOR (TV_FIRST+128-10) +#define TVM_GETITEMTEXTCOLOR (TV_FIRST+128-11) +#define TVM_GETITEMOFROW (TV_FIRST+128-12) +#define TVM_GETROWCOUNT (TV_FIRST+128-13) +#define TVM_GETROWOFITEM (TV_FIRST+128-14) +#define TVM_SETCOLUMN (TV_FIRST+128-15-TV_UINICODE) +#define TVM_GETCOLUMN (TV_FIRST+128-17-TV_UINICODE) +#define TVM_SETCOLUMNWIDTH (TV_FIRST+128-19) +#define TVM_GETCOLUMNWIDTH (TV_FIRST+128-20) +#define TVM_SETUSERDATASIZE (TV_FIRST+128-21) +#define TVM_GETUSERDATASIZE (TV_FIRST+128-22) +#define TVM_GETUSERDATA (TV_FIRST+128-23) +#define TVM_SORTCHILDRENEX (TV_FIRST+128-24) +#define TVM_COLUMNAUTOEDIT (TV_FIRST+128-25-TV_UINICODE) +#define TVM_COLUMNAUTOICON (TV_FIRST+128-27) +#define TVM_GETCOUNTPERPAGE (TV_FIRST+128-28) +#define TVM_FINDITEM (TV_FIRST+128-29-TV_UINICODE) +#define TVM_SELECTCHILDS (TV_FIRST+128-31) +#define TVM_GETSETOPTION (TV_FIRST+128-32) +#define TVM_ISITEMVISIBLE (TV_FIRST+128-33) +#define TVM_SETFOCUSITEM (TV_FIRST+128-34) +#define TVM_GETCOLUMNORDERARRAY (TV_FIRST+128-35) +#define TVM_SETCOLUMNORDERARRAY (TV_FIRST+128-36) +#ifndef TVM_GETITEMSTATE +#define TVM_GETITEMSTATE (TV_FIRST+39) +#endif +#ifndef TVM_GETEXTENDEDSTYLE +#define TVM_GETEXTENDEDSTYLE (TV_FIRST+45) +#endif +#ifndef TVM_SETEXTENDEDSTYLE +#define TVM_SETEXTENDEDSTYLE (TV_FIRST+44) +#endif +#ifndef TVM_GETLINECOLOR +#define TVM_GETLINECOLOR (TV_FIRST+41) +#endif +#ifndef TVM_SETLINECOLOR +#define TVM_SETLINECOLOR (TV_FIRST+40) +#endif + + +#ifndef TVNRET_DEFAULT +#define TVNRET_DEFAULT 0 +#endif +#ifndef TVNRET_SKIPOLD +#define TVNRET_SKIPOLD 1 +#endif +#ifndef TVNRET_SKIPNEW +#define TVNRET_SKIPNEW 2 +#endif + +#define TreeList_DeleteChildItems(h,i) ((BOOL )SNDMSG(h,TVM_DELETEITEM,0x88,(LPARAM)i)) +#define TreeList_DeleteAllItems(h) ((BOOL )SNDMSG(h,TVM_DELETEITEM,0,(LPARAM)TVI_ROOT)) +#define TreeList_DeleteItem(h,i) ((BOOL )SNDMSG(h,TVM_DELETEITEM,0,(LPARAM)(HTREEITEM)(i))) +#define TreeList_Expand(h,i,c) ((BOOL )SNDMSG(h,TVM_EXPAND,(WPARAM)(c),(LPARAM)(HTREEITEM)(i))) +#define TreeList_GetHeader(h) ((HWND )SNDMSG(h,TVM_GETHEADER,0,0)) +#define TreeList_DeleteColumn(h,i) ((BOOL )SNDMSG(h,TVM_DELETECOLUMN,(WPARAM)(int)(i),0)) +#define TreeList_InsertColumn(h,i,p) ((INT )SNDMSG(h,TVM_INSERTCOLUMN,(WPARAM)(int)(i),(LPARAM)(const TV_COLUMN*)(p))) +#define TreeList_GetColumnCount(h) ((INT )SNDMSG(h,TVM_GETCOLUMNCOUNT,0,0)) +#define TreeList_HitTest(h,p) ((HTREEITEM )SNDMSG(h,TVM_HITTEST,0,(LPARAM)(LPTV_HITTESTINFO)(p))) +#define TreeList_GetItemOfRow(h,r) ((HTREEITEM )SNDMSG(h,TVM_GETITEMOFROW,0,r)) +#define TreeList_GetRowOfItem(h,i) ((INT )SNDMSG(h,TVM_GETROWOFITEM,0,(LPARAM)(i))) +#define TreeList_GetRowCount(h) ((INT )SNDMSG(h,TVM_GETROWCOUNT ,0,0)) +#define TreeList_GetCountPerPage(h) ((INT )SNDMSG(h,TVM_GETCOUNTPERPAGE ,0,0)) +#define TreeList_GetExtendedStyle(h) ((DWORD )SNDMSG(h,TVM_GETEXTENDEDSTYLE,0,0)) +#define TreeList_SetExtendedStyle(h,d) ((DWORD )SNDMSG(h,TVM_SETEXTENDEDSTYLE,0,d)) +#define TreeList_SetExtendedStyleEx(h,d,m) ((DWORD )SNDMSG(h,TVM_SETEXTENDEDSTYLE,m,d)) +#define TreeList_GetColor(h,i) ((COLORREF )SNDMSG(h,TVM_GETBKCOLOR,(WPARAM)(i),0)) +#define TreeList_SetColor(h,i,c) ((COLORREF )SNDMSG(h,TVM_SETBKCOLOR,(WPARAM)(i),c)) +#define TreeList_GetItemBkColor(h,i,s) ((COLORREF )SNDMSG(h,TVM_GETITEMBKCOLOR,(WPARAM)(i),s)) +#define TreeList_SetItemBkColor(h,i,s,c) ((COLORREF )SNDMSG(h,TVM_SETITEMBKCOLOR,((UINT)(i))|((s)<<24),c)) +#define TreeList_GetItemTextColor(h,i,s) ((COLORREF )SNDMSG(h,TVM_GETITEMTEXTCOLOR,(WPARAM)(i),s)) +#define TreeList_SetItemTextColor(h,i,s,c) ((COLORREF )SNDMSG(h,TVM_SETITEMTEXTCOLOR,((UINT)(i))|((s)<<24),c)) +#define TreeList_IsItemVisible(h,i,s) ((INT )SNDMSG(h,TVM_ISITEMVISIBLE,s,(LPARAM)(HTREEITEM)(i))) +#define TreeList_EnsureVisible(h,i) ((BOOL )SNDMSG(h,TVM_ENSUREVISIBLE,0,(LPARAM)(HTREEITEM)(i))) +#define TreeList_EnsureVisibleEx(h,i,s) ((BOOL )SNDMSG(h,TVM_ENSUREVISIBLE,s,(LPARAM)(HTREEITEM)(i))) +#define TreeList_SelectDropTargetEx(h,i,s) ((BOOL )SNDMSG(h,TVM_SELECTDROP,(WPARAM)(s),(LPARAM)(HTREEITEM)(i))) +#define TreeList_SelectSubItem(h,i,s) ((BOOL )SNDMSG(h,TVM_SELECTSUBITEM,(WPARAM)(s),(LPARAM)(HTREEITEM)(i))) +#define TreeList_SelectChilds(h,i,s) ((BOOL )SNDMSG(h,TVM_SELECTCHILDS,(WPARAM)(s),(LPARAM)(HTREEITEM)(i))) +#define TreeList_Select(h,i,c) ((BOOL )SNDMSG(h,TVM_SELECTITEM,(WPARAM)(c),(LPARAM)(HTREEITEM)(i))) +#define TreeList_EditLabel(h,i,s) ((HWND )SNDMSG(h,TVM_EDITLABEL,s,(LPARAM)(HTREEITEM)(i))) +#define TreeList_StartEdit(h,i,s) ((BOOL )SNDMSG(h,TVM_EDITLABEL,TVIR_EDITCOL(s)|TVLE_DONOTIFY,(LPARAM)(HTREEITEM)(i))) +#define TreeList_EndEditLabelNow(h,c) ((BOOL )SNDMSG(h,TVM_ENDEDITLABELNOW,c,0)) +#define TreeList_GetItem(h,p) ((BOOL )SNDMSG(h,TVM_GETITEM,0,(LPARAM)(TV_ITEM*)(p))) +#define TreeList_GetCount() ((BOOL )SNDMSG(h,TVM_GETCOUNT,0,0)) +#define TreeList_GetEditControl(h) ((HWND )SNDMSG(h,TVM_GETEDITCONTROL,0,0)) +#define TreeList_GetImageList(h,i) ((HIMAGELIST)SNDMSG(h,TVM_GETIMAGELIST,i,0)) +#define TreeList_GetUserData(h,i) ((LPVOID )SNDMSG(h,TVM_GETUSERDATA,0,(LPARAM)(HTREEITEM)(i))) +#define TreeList_GetUserDataSize(h) ((INT )SNDMSG(h,TVM_GETUSERDATASIZE,0,0)) +#define TreeList_SetUserDataSize(h,s) ((INT )SNDMSG(h,TVM_SETUSERDATASIZE,0,s)) +#define TreeList_GetIndent ((UINT )SNDMSG(h,TVM_GETINDENT,0,0)) +#define TreeList_GetVisibleCount ((UINT )SNDMSG(h,TVM_GETVISIBLECOUNT,0,0)) +#define TreeList_InsertItem(h,p) ((HTREEITEM )SNDMSG(h,TVM_INSERTITEM,0,(LPARAM)(LPTV_INSERTSTRUCT)(p))) +#define TreeList_FindItem(h,p,f) ((HTREEITEM )SNDMSG(h,TVM_FINDITEM ,(WPARAM)p,(LPARAM)f)) +#define TreeList_CreateDragImage(h,i) ((HIMAGELIST)SNDMSG(h,TVM_CREATEDRAGIMAGE, 0, (LPARAM)(HTREEITEM)(i))) +#define TreeList_CreateDragImageEx(h,i,s) ((HIMAGELIST)SNDMSG(h,TVM_CREATEDRAGIMAGE, s, (LPARAM)(HTREEITEM)(i))) +#define TreeList_SetImageList(h,l,i) ((HIMAGELIST)SNDMSG(h,TVM_SETIMAGELIST,i,(LPARAM)(UINT)(HIMAGELIST)(l))) +#define TreeList_SetIndent(h,i) ((BOOL )SNDMSG(h,TVM_SETINDENT,(WPARAM)(i),0)) +#define TreeList_SetItem(h,p) ((BOOL )SNDMSG(h,TVM_SETITEM,0,(LPARAM)(const TV_ITEM*)(p))) +#define TreeList_SortChildren(h,i,r) ((BOOL )SNDMSG(h,TVM_SORTCHILDREN ,(WPARAM)r,(LPARAM)(HTREEITEM)(i))) +#define TreeList_SortChildrenCB(h,p,r) ((BOOL )SNDMSG(h,TVM_SORTCHILDRENCB,(WPARAM)r,(LPARAM)(LPTV_SORTCB)(p))) +#define TreeList_SortChildrenEX(h,p,r) ((BOOL )SNDMSG(h,TVM_SORTCHILDRENEX,(WPARAM)r,(LPARAM)(LPTV_SORTEX)(p))) +#define TreeList_SetColumn(h,i,p) ((BOOL )SNDMSG(h,TVM_SETCOLUMN,i,(LPARAM)(const TV_COLUMN*)(p))) +#define TreeList_GetColumn(h,i,p) ((BOOL )SNDMSG(h,TVM_GETCOLUMN,i,(LPARAM)(TV_COLUMN*)(p))) +#define TreeList_SetColumnWidth(h,i,w) ((BOOL )SNDMSG(h,TVM_SETCOLUMNWIDTH,i,w)) +#define TreeList_GetColumnWidth(h,i) ((INT )SNDMSG(h,TVM_GETCOLUMNWIDTH,i,0)) +#define TreeList_SetColumnAutoEdit(h,i,f,p) ((BOOL )SNDMSG(h,TVM_COLUMNAUTOEDIT,(WPARAM)((f)&~TVAE_COL(-1))|TVAE_COL(i),(LPARAM)(p))) +#define TreeList_SetColumnAutoIcon(h,i,n) ((BOOL )SNDMSG(h,TVM_COLUMNAUTOICON,i,n)) +#define TreeList_SetFocusItem(h,i,c) ((BOOL )SNDMSG(h,TVM_SETFOCUSITEM,c,(LPARAM)(i))) +#define TreeList_SetOption(h,i,o) ((INT )SNDMSG(h,TVM_GETSETOPTION,(i)|TVOP_WRITEOPTION,(LPARAM)(o))) +#define TreeList_GetOption(h,i) ((INT )SNDMSG(h,TVM_GETSETOPTION,i,0)) +#define TreeList_SetColumnOrderArray(h,n,p) ((BOOL )SNDMSG(h,TVM_SETCOLUMNORDERARRAY,n,(LPARAM)(p))) +#define TreeList_GetColumnOrderArray(h,n,p) ((BOOL )SNDMSG(h,TVM_GETCOLUMNORDERARRAY,n,(LPARAM)(p))) +#define TreeList_GetStyle(h) ((DWORD )::GetWindowLong(h,GWL_STYLE)) +#define TreeList_SetStyle(h,d) ((DWORD )::SetWindowLong(h,GWL_STYLE,d)) +#define TreeList_SetStyleEx(h,d,m) ((DWORD )::SetWindowLong(h,GWL_STYLE,((d)&(m))|(::GetWindowLong(h,GWL_STYLE)&~(m)))) +#define TreeList_GetItemRect(h,i,s,p,c) (*(HTREEITEM*)p =(i),(BOOL)SNDMSG(h,TVM_GETITEMRECT,(WPARAM)((c)|(TVIR_COLTOSUB(s))),(LPARAM)(RECT*)(p))) + + +#define TreeList_SelectItem(h,i) TreeList_Select(h,i,TVGN_CARET) +#define TreeList_SelectDropTarget(h,i) TreeList_Select(h,i,TVGN_DROPHILITE) +#define TreeList_SelectSetFirstVisible(h,i) TreeList_Select(h,i,TVGN_FIRSTVISIBLE) + +#define TreeList_GetNextItem(h,i,c) TreeView_GetNextItem(h, i, c) +#define TreeList_GetChild(h,i) TreeView_GetNextItem(h, i, TVGN_CHILD) +#define TreeList_GetParent(h, i) TreeView_GetNextItem(h, i, TVGN_PARENT) +#define TreeList_GetNextSibling(h,i) TreeView_GetNextItem(h, i, TVGN_NEXT) +#define TreeList_GetPrevSibling(h,i) TreeView_GetNextItem(h, i, TVGN_PREVIOUS) +#define TreeList_GetNextSelected(h,i) TreeView_GetNextItem(h, i, TVGN_NEXTSELECTED) +#define TreeList_GetNextSelectedChild(h,i) TreeView_GetNextItem(h, i, TVGN_NEXTSELCHILD) +#define TreeList_GetNextVisible(h,i) TreeView_GetNextItem(h, i, TVGN_NEXTVISIBLE) +#define TreeList_GetPrevVisible(h,i) TreeView_GetNextItem(h, i, TVGN_PREVIOUSVISIBLE) +#define TreeList_GetLastChild(h,i) TreeView_GetNextItem(h, i, TVGN_LASTCHILD) +#define TreeList_GetSelection(h) TreeView_GetNextItem(h, NULL, TVGN_CARET) +#define TreeList_GetDropHilight(h) TreeView_GetNextItem(h, NULL, TVGN_DROPHILITE) +#define TreeList_GetFirstVisible(h) TreeView_GetNextItem(h, NULL, TVGN_FIRSTVISIBLE) +#define TreeList_GetLastVisible(h) TreeView_GetNextItem(h, NULL, TVGN_LASTVISIBLE) +#define TreeList_GetRoot(h) TreeView_GetNextItem(h, NULL, TVGN_ROOT) +#define TreeList_GetFocus(h) TreeView_GetNextItem(h, NULL, TVGN_FOCUS) +#define TreeList_GetFocusColumn(h) ((int)TreeView_GetNextItem(h, NULL, TVGN_FOCUSSUB)) +#define TreeList_GetSelectionColumn(h) ((int)TreeView_GetNextItem(h, NULL, TVGN_CARETSUB)) +#define TreeList_GetDropHilightColumn(h) ((int)TreeView_GetNextItem(h, NULL, TVGN_DROPHILITESUB)) + +extern int TreeListRegister(HINSTANCE hInstance); +extern BOOL TreeListUnregister(HINSTANCE hInstance); + + +/* Compat with my old code... */ +#define TLCOLUMN TVCOLUMN +#define HTLITEM HTREEITEM +#define TL_INSERTSTRUCTA TV_INSERTSTRUCTA +#define TL_INSERTSTRUCTW TV_INSERTSTRUCTW +#define TL_INSERTSTRUCT TV_INSERTSTRUCT + +/* New stuff */ +#ifndef __REACTOS__ +#define TreeList_SetItemText(hwndLV,hItem_,iSubItem_,pszText_) \ +{ \ + TV_ITEM _ms_tvi; \ + _ms_tvi.mask = TVIF_SUBITEM | TVIF_TEXT; \ + _ms_tvi.hItem = (hItem_); \ + _ms_tvi.stateMask = 0; \ + _ms_tvi.pszText = (pszText_); \ + _ms_tvi.cchTextMax = (pszText_) ? 256 : 0; \ + _ms_tvi.cChildren = (iSubItem_); \ + TreeList_SetItem((hwndLV), &_ms_tvi); \ +} +#else // __REACTOS__ +#define TreeList_SetItemText(hwndLV,hItem_,iSubItem_,pszText_) \ +{ \ + TV_ITEM _ms_tvi; \ + LPWSTR _my_pszText = (pszText_); \ + _ms_tvi.mask = TVIF_SUBITEM | TVIF_TEXT; \ + _ms_tvi.hItem = (hItem_); \ + _ms_tvi.stateMask = 0; \ + _ms_tvi.pszText = _my_pszText; \ + _ms_tvi.cchTextMax = _my_pszText ? wcslen(_my_pszText) : 0; \ + _ms_tvi.cChildren = (iSubItem_); \ + TreeList_SetItem((hwndLV), &_ms_tvi); \ +} +#endif // __REACTOS__ + +#endif