Files
reactos/base/setup/reactos/treelist.c
2019-04-13 14:07:50 +02:00

13734 lines
351 KiB
C

/*
* PROJECT: ReactOS GUI first stage setup application
* LICENSE: GPL-3.0-or-later (https://spdx.org/licenses/GPL-3.0-or-later)
* PURPOSE: Implements a TreeList control: a tree window with columns.
* COPYRIGHT: Copyright (C) Anton Zechner (az_software@inode.at) 2007
* Copyright (C) Sébastien Kirche (sebastien.kirche@free.fr) 2014
*
* NOTE: Taken from the TreeList code found at https://github.com/sebkirche/treelist
*/
//*****************************************************************************
//*
//*
//* TreeListWnd.cpp
//*
//*
//*****************************************************************************
//
// This code creates a tree window with a list
//
//
// Copyright (C) Anton Zechner (az_software@inode.at) 2007
// Copyright (C) Sébastien Kirche (sebastien.kirche@free.fr) 2014
//
// TreeListWnd is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
// Sourcecode which use TreeListWnd must be published. Commercial users
// must published their code too, or make an licence agreement with me.
//
//
// TreeListWnd wird unter GNU GENERAL PUBLIC LICENSE (GPL) vertreiben.
// Sourcecode welcher TreeListWnd verwendet muss veröffendlicht werden.
// Komerzielle Nutzer müssen ihren Code ebenfalls veröffentlichen, oder
// eine Nutzungsvereinbarung mit mir treffen.
//
//
// Version: 2.04
//
#ifdef UNICODE
#ifndef _UNICODE
#define _UNICODE
#endif
#endif
#if 0
#include <stdio.h>
#include <windows.h>
#include <string.h>
#include <malloc.h>
#include <tchar.h>
#else
#include "reactos.h"
#endif
#define new(TYPE, numElems) \
HeapAlloc(GetProcessHeap(), 0, (numElems) * sizeof(TYPE))
#define delete(ptr) \
HeapFree(GetProcessHeap(), 0, (ptr))
#include "treelist.h"
#ifndef GWLP_USERDATA
#define GWLP_USERDATA GWL_USERDATA
#endif
#ifndef GWLP_WNDPROC
#define GWLP_WNDPROC GWL_WNDPROC
#endif
#ifndef _WIN64
#ifndef SetWindowLongPtr
#define SetWindowLongPtr SetWindowLong
#endif
#ifndef GetWindowLongPtr
#define GetWindowLongPtr GetWindowLong
#endif
#ifndef DWORD_PTR
#define DWORD_PTR DWORD
#endif
#ifndef LONG_PTR
#define LONG_PTR LONG
#endif
#endif
#ifdef UNICODE
#define str_len (unsigned)wcslen
#define str_cmp wcscmp
#define str_ncpy wcsncpy
#define str_ncmp wcsncmp
#define str_icmp _wcsicmp
#else
#define str_len (unsigned)strlen
#define str_cmp strcmp
#define str_ncpy strncpy
#define str_ncmp strncmp
#define str_icmp _stricmp
#endif
#ifndef WM_MOUSEWHEEL
#define WM_MOUSEWHEEL 0x020A
#endif
#ifndef WHEEL_DELTA
#define WHEEL_DELTA 120
#endif
#ifndef MAX_COLUMNS
#define MAX_COLUMNS 32
#endif
#define MAX_COLORS 16
#define EN_SETTEXT 0x1000
#define EN_RETURN 0x1578
#define EN_ESCAPE 0x1579
#define ID_TOOLTIPCHECK 0x3912
#define SORT_NOUPDATE 1234567
#define VK_ISACHAR 0x01000000
#define FIRST_LINE 0xFFFFFFFE
#define FROM_HEADER 0x88776655
#define I_CCB I_CHILDRENCALLBACK
#define U(h) ((unsigned)(ULONG_PTR)(h))
#define THEMEIMGLIST ((HIMAGELIST)1)
#define GetHandle(h) ((TreeListData*)GetWindowLongPtr(h,0))
#define TVIF_ALL (TVIF_CHILDREN|TVIF_HANDLE|TVIF_IMAGE|TVIF_PARAM|TVIF_SELECTEDIMAGE|TVIF_STATE|TVIF_TEXT)
#define UNLOCK(d) ReleaseSemaphore(d->hSem,1,NULL)
#define LOCK(d) WaitForSingleObject(d->hSem,INFINITE)
#define TVIS_EDIT(m) ((1<<m)&((1<<TVAX_EDIT)|(1<<TVAX_COMBO)|(1<<TVAX_STEPED)|(1<<TVAX_CHECKED)))
#define TVIS_BASEFLAGS (TVIS_EXPANDED|TVIS_EXPANDEDONCE|TVIS_EXPANDPARTIAL|TVIS_SELECTED)
#define TVIS_TRACKED (TVIX_TRACKED<<16)
#define TVIS_BKCOLOR (TVIX_BKCOLOR<<16)
#undef TVIS_FOCUSED
#define TVIS_FOCUSED (TVIX_FOCUSED<<16)
#define TVIS_TEXTCOLOR (TVIX_TEXTCOLOR<<16)
#define TVC_ONLYFOCUS TVIF_ONLYFOCUS
#define TVC_UNSELECT 0x4000
#define TVC_DESELECT 0x8000
#define DEFAULT_IDENT 19
#define DEFAULT_SHIFT 7
#ifndef BPBF_COMPATIBLEBITMAP
#define BPBF_COMPATIBLEBITMAP 0
#endif
#ifndef TVP_GLYPH
#define TVP_GLYPH 2
#endif
#ifndef GLPS_CLOSED
#define GLPS_CLOSED 1
#endif
#ifndef GLPS_OPENED
#define GLPS_OPENED 2
#endif
#ifndef BP_CHECKBOX
#define BP_CHECKBOX 3
#endif
#ifndef CBS_UNCHECKEDNORMAL
#define CBS_UNCHECKEDNORMAL 1
#endif
#ifndef CBS_CHECKEDNORMAL
#define CBS_CHECKEDNORMAL 5
#endif
#define TVAX_NONE (TVAE_NONE >>TVAE_MODEPOS)// No automatic edit
#define TVAX_EDIT (TVAE_EDIT >>TVAE_MODEPOS)// automatic edit with edit
#define TVAX_COMBO (TVAE_COMBO >>TVAE_MODEPOS)// automatic edit with ComboBox
#define TVAX_CBLIST (TVAE_CBLIST >>TVAE_MODEPOS)// automatic edit with ComboListBox
#define TVAX_STEP (TVAE_STEP >>TVAE_MODEPOS)// Einzelnes Weiterschalten mit Enter
#define TVAX_STEPED (TVAE_STEPED >>TVAE_MODEPOS)// Einzelnes Weiterschalten mit Enter und Edit
#define TVAX_CHECK (TVAE_CHECK >>TVAE_MODEPOS)// automatic edit with CheckBox
#define TVAX_CHECKED (TVAE_CHECKED>>TVAE_MODEPOS)// automatic edit with CheckBox and Edit
#define TVIX_VARBUTTON 0x01 // buttons are not permanent
#define TVIX_HASBUTTON 0x02 // entry has button
#define TVIX_HASIMAGE 0x04 // entry has icon
#define TVIX_TRACKED 0x08 // entry under the cursor
#define TVIX_TEXTCOLOR 0x10 // entry has its own text color
#define TVIX_BKCOLOR 0x20 // entry has its own backround color
#define TVIX_FOCUSED 0x40 // entry has the focus
typedef struct {
LPARAM lParam; // LPARAM argument for the item
LPTSTR pText; // pointer to the item text
UINT uState; // item state
int iImage; // item image index
int iSelectedImage; // item selected image index
unsigned uShowPos; // Ist die Position in der Sichtbarliste (0=unsichtbar)
unsigned uFirstChild; // Ist die Nummer des ersten Kind-Eintrages (0=keines)
unsigned uLastChild; // Ist die Nummer des letzten Kind-Eintrages (0=keines)
unsigned uPrevItem; // Ist die Nummer des vorherigen Eintrages (0=keines)
unsigned uNextItem; // Ist die Nummer des nächsten Eintrages (0=keines)
unsigned uParent; // Ist die Nummer des Elterneintrages (0=Root)
unsigned uLevel; // Ist die Ebene des Eintrages (0=Root)
int iTextPixels; // Ist die Breites des Textes in Pixel
WORD uTextSize; // Länge des Textes in Zeichen
BYTE bCallback; // Sind Bits für Callbacks
BYTE bFlags; // Diverse Flags
COLORREF uColorText; // Spezielle Textfarbe
COLORREF uColorBk; // Spezielle Hintergrundfarbe
} BaseItem;
typedef struct {
LPTSTR pText; // Zeiger auf Tree-Text
UINT uState; // Zustand des Eintrages
int iImage; // Ist die Nummer des an zu zeigenden Icons
int iTextPixels; // Ist die Breites des Textes in Pixel
WORD uTextSize; // Länge des Textes in Zeichen
BYTE bCallback; // Sind Bits für Callbacks
BYTE bFlags; // Diverse Flags
COLORREF uColorText; // Spezielle Textfarbe
COLORREF uColorBk; // Spezielle Hintergrundfarbe
} ExtraItem;
typedef struct {
void *pCbData; // Data for autoedit
INT iCbIcon; // Starting offset for in icon list for autoedit
short sSize; // width of the column
short sReal; // real width of the column
short sMin; // minimum width
short sFixed; // fixed width
BYTE bMinEx; // the width cannot be less than min width
BYTE bWeight; // weight for variable columns
BYTE bNext; // Ist die Spalte die nach der eigenen sichtbar ist (gespeicherte Reihenfolge)
BYTE bIndex; // Ist die Spalte in der diese Reihe sichtbar ist (sichtbarer Index)
BYTE bAlign; // Text alignment
BYTE bEdit; // Automaisches Editiern einer Spalte (siehe TVAE_???>>7)
BYTE bFlags; // Automaisches Editiern einer Spalte (siehe TVAE_???)
BYTE bEnable; // Automaisches einer mit Statebits aktivieren
BYTE bCbSize; // Maximum number of entries in the data list
BYTE bCbChar; // separator for the data list
BYTE bMark; // is column marked ?
BYTE bDummy[32 - 23 - sizeof(void *)]; // padding bytes - 32 bytes alignment
} ColumnData;
typedef struct {
HWND hWnd; // handle of the control
HANDLE hSem; // access semaphore
LPVOID hTheme; // Handle für benutztes Thema (TREELIST)
LPVOID hThemeBt; // Handle für benutztes Thema (BUTTON)
WNDPROC pProcId3; // Fenster Funktion für ID3 Fenster
HIMAGELIST hStates; // Handle der Icon-Liste für States und Overlay
HIMAGELIST hImages; // Handle der Icon-Liste
HIMAGELIST hChecks; // Handle der Icon-Liste für die Checkboxen in den Spalten
HIMAGELIST hSubImg; // Handle der Icon-Liste für die Spalten
HIMAGELIST hHeadImg; // Handle for header images
HFONT hFontN; // Normal font
HFONT hFontB; // Bold fonts
HFONT hFontL; // Last used font
HFONT hFontT; // Tooltip font
HWND hEdit; // Handle des Edit-Fensters
HWND hHeader; // Handle des Header Fensters
HWND hToolTip; // Handle des Tooltip-Fensters
WNDPROC pToolProc; // Alte Fensterfunktion des Tooltips
COLORREF uColors[MAX_COLORS]; // 0=Hintergrundfarbe 1=Abwechselnte Farbe 2=Farbe für Trennlinien 3=Textfarbe
int iFontHeight; // Ist die Höhe des Fonts
int iFontLine; // Ist die Position der Linie beim unterstreichen
int iFontOff; // Ist die Position um der ein Text horizontal verschoben wird
int iStatesMode; // Die hStates Image-Liste wurde für die Checkboxen erzeugt
int iStatesXsize; // Breite der States und Overlay Icons
int iStatesYsize; // Höhe der States und Overlay Icons
int iChecksMode; // Die hChecks Image-Liste wurde für die Checkboxen erzeugt
int iChecksXsize; // Breite der States und Overlay Icons
int iChecksYsize; // Höhe der States und Overlay Icons
int iImagesXsize; // Breite der Icons
int iImagesYsize; // Höhe der Icons
int iSubImgMode; // Die SubImg Image-Liste ist nicht die hImages Liste
int iSubImgXsize; // Breite der Icons
int iSubImgYsize; // Höhe der Icons
int iRowHeight; // Ist die Höhe einer Zeile
int iAllWeight; // Das Gewicht aller variablen Spalten
int iVarSize; // Ist die Breite aller variablen Spalten
int iFixSize; // Ist die Breite aller fixen Spalten
int iIndent; // Einrückung der Kindereintäge
int iShift; // Einrückung der vertikalen Linien
int iAutoAdd; // Offset zum Open-Icon für TVS_EX_AUTOEXPANDICON
int iMaxSizeX; // Die Größe des breitesten sichtbaren Eintrages
unsigned uItemPosCount; // Anzahl der sichtbaren Einträge
unsigned *pItemPos; // Liste mit den Offsets der sichtbaren Einträge
BaseItem **pTreeItems; // Zeiger auf Item Zeiger
ExtraItem **pExtraItems[MAX_COLUMNS - 1]; // Zeiger auf die Spalteneinträge
unsigned uTreeItemsMax; // Größe der Liste mit den vorhanden Einträge (alociert um 1 größer)
unsigned uTreeItemsCount; // Anzahl der vorhanden Einträge
unsigned uNextSeachPos; // Nächste Position zum suchen von freien Einträgen
unsigned uUserDataSize; // Ist die Größe der Userdaten in einem Eintrag
unsigned uFirstChild; // Ist die Nummer des ersten Kind-Eintrages (0=keines)
unsigned uLastChild; // Ist die Nummer des letzten Kind-Eintrages (0=keines)
unsigned uSingleSel; // Ist die Nummer des gewählten Eintrages (bei Checkboxen)
unsigned uScrollX; // Aktuelle X-Scroll-Position
unsigned uScrollY; // Aktuelle Y-Scroll-Position
unsigned uSizeX; // Aktuelle X-Fenster-Größe
unsigned uSizeY; // Aktuelle Y-Fenster-Größe
unsigned uSizeYsub; // Aktuelle Y-Fenster-Größe ohne Header
unsigned uStyle; // Ist der aktuele Style des Fensters
unsigned uStyleEx; // Erweiterte Sytle-Flags (siehe TVS_EX_???)
unsigned uStartPixel; // Ist die Y-Koordinate bei der der erste Eintrag beginnt
unsigned uMaxEnties; // Anzahl der sichtbaren Einträge (inkl. halbsichtbare)
unsigned uPageEnties; // Anzahl der sichtbaren Einträge (ohne halbsichtbare)
unsigned uColumnCount; // Anzahl der Spalten
unsigned uColumnCountVar; // Anzahl der variabeln Spalten
unsigned uSelectedCount; // Anzahl der ausgewählten Einträge
unsigned uSelectedBase; // Ist der Eintrag ab dem gewählt wurde
unsigned uSelectedItem; // Ist der Eintrag der gerade gewählt ist
unsigned uSelectedSub; // Ist die Spalte die gerade gewählt ist
unsigned uFocusItem; // Ist der Eintrag der einen leeren Focus hat
unsigned uFocusSub; // Ist die Spalte die einen leeren Focus hat
unsigned uToolTipItem; // Ist der ToolTip-Eintrag der gerade gewählt ist
unsigned uToolTipShow; // Ist die Zeitverzögerung in 500 ms Schritten für das Tooltip
unsigned uToolTipSub; // Ist die ToolTip-Spalte die gerade gewählt ist
POINT sToolTipPos; // Ist die globale Koordinate des ToolTips
unsigned uEditMode; // Ist der Modus des Editfensters (0=Edit 1=ComboBox 2=ComboBox fix)
unsigned uEditItem; // Ist der Eintrag der gerade editiert wird
unsigned uEditSub; // Ist die Spalte die gerade editiert wird
unsigned uOldXPage; // Alte Werte für X-Scroll-Bar
unsigned uOldXCount; // *
unsigned uOldYPage; // Alte Werte für Y-Scroll-Bar
unsigned uOldYCount; // *
unsigned uTrippleB; // Bereite des "..." Strings für den fetten Fonts
unsigned uTrippleN; // Bereite des "..." Strings für den normalen Fonts
unsigned uTrackedItem; // Ist der Eintrag der unterstrichen werden soll
unsigned uTrackedSub; // Ist die Spalte des Eintrages der unterstrichen werden soll
unsigned uInsertMark; // Ist der Eintrag mit der Einfügemarke
unsigned uMarkedCols; // Anzahl der markierten Spalten
unsigned uDragFlags; // Welche Maustasten sind an
unsigned uDragItem; // Eintrag für Dragoperation
unsigned uDragSub; // Untereintrag für Dragoperation
unsigned uLastSel; // Letzte Textauswahl beim Editieren
unsigned uLastMove; // Letzte Cursorposition bei WM_MOUSEMOVE
unsigned uButtonPos; // Wo wurde eine Maustaste wurde zuletzt gedrückt
unsigned uButtonLast; // Wann wurde eine Maustaste wurde zuletzt gedrückt
unsigned uToolTipSize; // Textspeichergröße für Tooltip
LPTSTR pToolTipText; // Textspeicher für Tooltip
TCHAR cTempText1 [260]; // Erster Textpuffer für Callbacks
TCHAR cTempText2 [260]; // Zeiter Textpuffer für Callbacks
ColumnData aColumn [MAX_COLUMNS]; // Daten der Spalten
int aColumnXpos [MAX_COLUMNS + 2]; // Array mit den Positionen der Spalten
BYTE aColumnPos [MAX_COLUMNS + 2]; // Array mit Anzeigepositionen der Spalten
char cColorChanged[MAX_COLORS ]; // Welche Farbe wurden verändert
char cColumnStart; // Wurde das Autoeditiren mit einer WM_CHAR Eingabe gestartet
char cFixedHeight; // Ist eine fixe Höhe eingestellt
char cLockChanges; // Sperren von Fensteränderungen
char cHasRootRow; // Wird gesetzt wenn eine Root-Spalte eingefügt wird
char cKeyIgnore; // Die nächste Taste nicht für Sucher verwenden
char cClickFlag; // Merker für LBUTTON-DOWN bei Multiselect
char cClickEdit; // Merker für LBUTTON-DOWN bei Edit-Click
char cIsEnabled; // Ist das Fenster freigegeben
char cHasFocus; // Hat das Fenster den Focus
char cReSelect; // Soll die Auswahl neu selektiert werden
char cGlyphOk; // Die Schaltfäche über Themen zeichnen
char cEditCb; // Muss das Edit-Fenster einen Callback aufrufen
char cButtonFlag; // Welche Maustaste wurde zuletzt gedrückt
} TreeListData;
typedef HRESULT(WINAPI *SetWindowThemeT)(HWND, LPCWSTR, LPCWSTR);
typedef HRESULT(WINAPI *EndBufferedPtT)(HANDLE, BOOL);
typedef HANDLE(WINAPI *BeginBufferedPnT)(HDC, RECT *, DWORD, LPVOID, HDC *);
typedef HRESULT(WINAPI *BufferedPtInitT)(VOID);
typedef HRESULT(WINAPI *BufferedPtInitT)(VOID);
typedef LPVOID (WINAPI *OpenThemeDataT)(HWND hwnd, LPCWSTR pszClassList);
typedef HRESULT(WINAPI *CloseThemeDataT)(LPVOID);
typedef HRESULT(WINAPI *DrawThemeBackgT)(LPVOID, HDC, int, int, const RECT *, const RECT *);
typedef HRESULT(WINAPI *GetThemeBackgRcT)(LPVOID, HDC, int, int, LPCRECT, LPRECT);
typedef BOOL (WINAPI *IsAppThemedT)();
typedef BOOL (WINAPI *IsThemeActiveT)();
static HMODULE hUxThemeDll = NULL;
static SetWindowThemeT pSetWindowTheme = NULL;
static EndBufferedPtT pEndBufferedPt = NULL;
static BeginBufferedPnT pBeginBufferedPt = NULL;
static BufferedPtInitT pBufferedPtInit = NULL;
static BufferedPtInitT pBufferedPtExit = NULL;
static OpenThemeDataT pOpenThemeData = NULL;
static CloseThemeDataT pCloseThemeData = NULL;
static DrawThemeBackgT pDrawThemeBackg = NULL;
static GetThemeBackgRcT pGetThemeBackgRc = NULL;
static IsAppThemedT pIsAppThemed = NULL;
static IsThemeActiveT pIsThemeActive = NULL;
static HPEN hPatternPen = NULL;
static HFONT hDefaultFontN = NULL;
static HFONT hDefaultFontB = NULL;
static LONG lWindowCount = -1;
#ifndef __REACTOS__
static RECT sToolRect = { -2, 0, 2, 64};
#endif
static TCHAR cKeyData[16];
static unsigned uKeyLast;
static unsigned uKeyPos;
static void TreeListDraw(HWND hWnd, HDC hDc, RECT *pRect);
static LRESULT CALLBACK TreeListProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
static int TreeListSelectItem(TreeListData *pData, unsigned uItem, unsigned uSubItem, int iMode);
static int TreeListGetItemRect(TreeListData *pData, unsigned uItem, unsigned uFlags, RECT *pRect);
static int TreeListStartNotifyEdit(TreeListData *pData, unsigned uItem, unsigned uSub, WPARAM wParam, LPARAM lParam);
static int TreeListStartAutoEdit(TreeListData *pData, unsigned uColumn, WPARAM wParam, LPARAM lParam);
static int TreeListEndLabelEdit(TreeListData *pData, int iMode);
static BOOL bDrawWithTheme = FALSE;
//*****************************************************************************
//*
//* TreeListRegister
//*
//*****************************************************************************
// Registiert das TreeList Fenster.
// Ergibt 1 wenn das Fenster erfolgreich registiert wurde.
int TreeListRegister(HINSTANCE hInstance) {
static int iIsRegistered = FALSE;
WNDCLASSEX sClass;
OutputDebugString(TEXT("TreeListRegister() - before checking\n"));
if(iIsRegistered)
return TRUE;
OutputDebugString(TEXT("TreeListRegister() - before registration\n"));
memset(&sClass, 0, sizeof(sClass));
sClass.cbSize = sizeof(sClass);
sClass.style = CS_DBLCLKS | CS_GLOBALCLASS;
sClass.lpfnWndProc = TreeListProc;
sClass.cbClsExtra = 0;
sClass.cbWndExtra = sizeof(TreeListData *);
sClass.hInstance = hInstance;
sClass.hIcon = NULL;
sClass.hCursor = LoadCursor(NULL, IDC_ARROW);
sClass.hbrBackground = NULL;
sClass.lpszMenuName = NULL;
sClass.hIconSm = NULL;
sClass.lpszClassName = _T(TVC_CLASSNAME);
if(!RegisterClassEx(&sClass))
return 0;
OutputDebugString(TEXT("TreeListRegister() - registration done\n"));
iIsRegistered = TRUE;
return TRUE;
}
BOOL TreeListUnregister(HINSTANCE hInstance){
return UnregisterClass(_T(TVC_CLASSNAME),hInstance);
}
//*****************************************************************************
//*
//* GlobalInit
//*
//*****************************************************************************
static void GlobalInit() {
LOGBRUSH sLog;
long lCount;
lCount = InterlockedIncrement(&lWindowCount);
if(lCount > 0)
return;
sLog.lbColor = GetSysColor(COLOR_BTNSHADOW);
sLog.lbStyle = PS_SOLID;
sLog.lbHatch = 0;
hPatternPen = ExtCreatePen(PS_COSMETIC | PS_ALTERNATE, 1, &sLog, 0, NULL);
if(!hPatternPen) {
hPatternPen = CreatePen(PS_DOT, 1, RGB(0, 0, 0));
}
if(!hUxThemeDll) {
hUxThemeDll = LoadLibrary(_T("UxTheme.dll"));
if(hUxThemeDll) {
pSetWindowTheme = (SetWindowThemeT)GetProcAddress(hUxThemeDll, "SetWindowTheme");
pEndBufferedPt = (EndBufferedPtT)GetProcAddress(hUxThemeDll, "EndBufferedPaint");
pBeginBufferedPt = (BeginBufferedPnT)GetProcAddress(hUxThemeDll, "BeginBufferedPaint");
pBufferedPtInit = (BufferedPtInitT)GetProcAddress(hUxThemeDll, "BufferedPaintInit");
pBufferedPtExit = (BufferedPtInitT)GetProcAddress(hUxThemeDll, "BufferedPaintUnInit");
pOpenThemeData = (OpenThemeDataT)GetProcAddress(hUxThemeDll, "OpenThemeData");
pCloseThemeData = (CloseThemeDataT)GetProcAddress(hUxThemeDll, "CloseThemeData");
pDrawThemeBackg = (DrawThemeBackgT)GetProcAddress(hUxThemeDll, "DrawThemeBackground");
pGetThemeBackgRc = (GetThemeBackgRcT)GetProcAddress(hUxThemeDll, "GetThemeBackgroundContentRect");
pIsAppThemed = (IsAppThemedT)GetProcAddress(hUxThemeDll, "IsAppThemed");
pIsThemeActive = (IsThemeActiveT)GetProcAddress(hUxThemeDll, "IsThemeActive");
if(pIsAppThemed && pIsThemeActive)
bDrawWithTheme = pIsAppThemed() && pIsThemeActive();
}
}
if(pBufferedPtInit) {
pBufferedPtInit();
}
}
//*****************************************************************************
//*
//* GlobalDeinit
//*
//*****************************************************************************
static void GlobalDeinit() {
int lCount;
lCount = InterlockedDecrement(&lWindowCount);
if(lCount >= 0)
return;
if(hDefaultFontN) {
DeleteObject(hDefaultFontN);
hDefaultFontN = NULL;
}
if(hDefaultFontB) {
DeleteObject(hDefaultFontB);
hDefaultFontB = NULL;
}
if(hPatternPen) {
DeleteObject(hPatternPen);
hPatternPen = NULL;
}
if(pBufferedPtExit) {
pBufferedPtExit();
}
}
//*****************************************************************************
//*
//* SendNotify
//*
//*****************************************************************************
// Sendet eine WM_NOTIFY Nachricht and das Elternfenster
// pData : Zeiger auf die Fensterdaten
// pNotify : Zeiger auf die Notify-Daten
// Ergibt den Rückgabewert der WM_NOTIFY Nachrich
static LRESULT SendNotify(TreeListData *pData, NMHDR *pNotify) {
pNotify->hwndFrom = pData->hWnd;
pNotify->idFrom = GetWindowLong(pNotify->hwndFrom, GWL_ID);
return SendMessage(GetParent(pNotify->hwndFrom), WM_NOTIFY, pNotify->idFrom, (LPARAM)pNotify);
}
//*****************************************************************************
//*
//* CallbackEntry
//*
//*****************************************************************************
// Sendet eine WM_NOTIFY Nachricht and das Elternfenster um Daten zuholen
// pData : Zeiger auf die Fensterdaten
// pEntry : Zeiger auf den Eintrag
// uItem : Nummer des Eintrages
// uFlags : Welche Daten sollen abgefragt werden
// Ergibt den Rückgabewert der WM_NOTIFY Nachrich
static void CallbackEntry(TreeListData *pData, BaseItem *pEntry, unsigned uItem, unsigned uFlags, int *iImage, unsigned *uTextSize, LPCTSTR *pText) {
NMTVDISPINFO sInfo;
sInfo.item.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | uFlags;
sInfo.item.lParam = pEntry->lParam;
sInfo.item.hItem = (HTREEITEM)(ULONG_PTR)uItem;
sInfo.item.state = pEntry->uState;
sInfo.item.stateMask = 0xFFFFFFFF;
sInfo.item.iImage = I_IMAGECALLBACK;
sInfo.item.iSelectedImage = I_IMAGECALLBACK;
sInfo.item.cChildren = I_CHILDRENCALLBACK;
if(uFlags & TVIF_TEXT) {
if(*uTextSize) {
pData->cTempText2[sizeof(pData->cTempText2) / sizeof(TCHAR) - 1] = 0;
pData->cTempText2[0] = 0;
sInfo.item.pszText = pData->cTempText2;
sInfo.item.cchTextMax = sizeof(pData->cTempText2) / sizeof(TCHAR) - 1;
} else {
pData->cTempText1[sizeof(pData->cTempText1) / sizeof(TCHAR) - 1] = 0;
pData->cTempText1[0] = 0;
sInfo.item.pszText = pData->cTempText1;
sInfo.item.cchTextMax = sizeof(pData->cTempText1) / sizeof(TCHAR) - 1;
}
} else {
sInfo.item.pszText = 0;
sInfo.item.cchTextMax = 0;
}
sInfo.hdr.hwndFrom = pData->hWnd;
sInfo.hdr.idFrom = GetWindowLong(pData->hWnd, GWL_ID);
sInfo.hdr.code = TVN_GETDISPINFO;
UNLOCK(pData);
SendMessage(GetParent(sInfo.hdr.hwndFrom), WM_NOTIFY, sInfo.hdr.idFrom, (LPARAM)&sInfo);
LOCK(pData);
if(uFlags & TVIF_IMAGE) {
if(!(pEntry->uState & TVIS_SELECTED))
*iImage = sInfo.item.iImage;
} else
if(uFlags & TVIF_SELECTEDIMAGE) {
if(pEntry->uState & TVIS_SELECTED)
*iImage = sInfo.item.iSelectedImage;
}
if(uFlags & TVIF_CHILDREN) {
switch(sInfo.item.cChildren) {
case 0:
pEntry->bFlags &= ~TVIX_HASBUTTON;
pEntry->bFlags |= TVIX_VARBUTTON;
break;
case 1:
pEntry->bFlags &= ~TVIX_VARBUTTON;
pEntry->bFlags |= TVIX_HASBUTTON;
break;
default
:
pEntry->bFlags |= TVIX_VARBUTTON;
if(pEntry->uFirstChild)
pEntry->bFlags |= TVIX_HASBUTTON;
else
pEntry->bFlags &= ~TVIX_HASBUTTON;
}
}
if(uFlags & TVIF_TEXT) {
*pText = sInfo.item.pszText;
*uTextSize = str_len(sInfo.item.pszText);
pEntry->iTextPixels = 0;
}
}
//*****************************************************************************
//*
//* CallbackExtra
//*
//*****************************************************************************
// Sendet eine WM_NOTIFY Nachricht and das Elternfenster um Daten zuholen
// pData : Zeiger auf die Fensterdaten
// pEntry : Zeiger auf den Eintrag
// uItem : Nummer des Eintrages
// uFlags : Welche Daten sollen abgefragt werden
// Ergibt den Rückgabewert der WM_NOTIFY Nachrich
static void CallbackExtra(TreeListData *pData, BaseItem *pEntry, ExtraItem *pExtra, unsigned uItem, unsigned uSub, unsigned uFlags, int *iImage, unsigned *uTextSize, LPCTSTR *pText) {
NMTVDISPINFO sInfo;
sInfo.item.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_SUBITEM | uFlags;
sInfo.item.lParam = pEntry->lParam;
sInfo.item.hItem = (HTREEITEM)(ULONG_PTR)uItem;
sInfo.item.state = pExtra->uState;
sInfo.item.state |= (pEntry->uState & TVIS_BASEFLAGS);
sInfo.item.stateMask = 0xFFFFFFFF;
sInfo.item.iImage = I_IMAGECALLBACK;
sInfo.item.iSelectedImage = I_IMAGECALLBACK;
sInfo.item.cChildren = uSub;
if(uFlags & TVIF_TEXT) {
pData->cTempText1[sizeof(pData->cTempText1) / sizeof(TCHAR) - 1] = 0;
pData->cTempText1[0] = 0;
sInfo.item.pszText = pData->cTempText1;
sInfo.item.cchTextMax = sizeof(pData->cTempText1) / sizeof(TCHAR) - 1;
} else {
sInfo.item.pszText = 0;
sInfo.item.cchTextMax = 0;
}
sInfo.hdr.hwndFrom = pData->hWnd;
sInfo.hdr.idFrom = GetWindowLong(pData->hWnd, GWL_ID);
sInfo.hdr.code = TVN_GETDISPINFO;
UNLOCK(pData);
SendMessage(GetParent(sInfo.hdr.hwndFrom), WM_NOTIFY, sInfo.hdr.idFrom, (LPARAM)&sInfo);
LOCK(pData);
if(uFlags & TVIF_IMAGE)
*iImage = sInfo.item.iImage;
if(uFlags & TVIF_TEXT) {
*pText = sInfo.item.pszText;
*uTextSize = str_len(sInfo.item.pszText);
}
}
//*****************************************************************************
//*
//* EditProc
//*
//*****************************************************************************
// Ist die Fensterfunktion für das Edit Fenster
static LRESULT CALLBACK EditProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
TreeListData *pData;
WNDPROC pProc;
DWORD dwStop;
DWORD dwStart;
DWORD dwCount;
LRESULT lResult;
HWND hParent;
HWND hCombo;
int iDelta;
int iState;
int iPos;
int iId;
hParent = GetParent(hWnd);
iId = GetWindowLong(hWnd, GWL_ID);
if(iId == 3) {
hCombo = hWnd;
pData = GetHandle(hParent);
pProc = pData->pProcId3;
} else {
hCombo = hParent;
hParent = GetParent(hParent);
pData = GetHandle(hParent);
pProc = (WNDPROC)GetWindowLongPtr(hWnd, GWLP_USERDATA);
}
if(uMsg == WM_KEYDOWN) {
if(wParam == VK_RETURN) {
SendMessage(hParent, WM_COMMAND, MAKELONG(3, EN_RETURN), (LPARAM)hWnd);
return 0;
}
if(wParam == VK_ESCAPE) {
SendMessage(hParent, WM_COMMAND, MAKELONG(3, EN_ESCAPE), (LPARAM)hWnd);
return 0;
}
if((pData->uStyleEx & TVS_EX_STEPOUT) && !(GetAsyncKeyState(VK_SHIFT) & 0x8000)) {
switch(wParam) { // Aus Fenster springen
case VK_UP:
if(pData->uEditMode)
break;
PostMessage(hParent, WM_COMMAND, MAKELONG(3, EN_RETURN), (LPARAM)hWnd);
PostMessage(hParent, WM_KEYDOWN, VK_UP , 0x00500001);
PostMessage(hParent, WM_KEYUP , VK_UP , 0x00500001);
return 0;
case VK_DOWN:
if(pData->uEditMode)
break;
PostMessage(hParent, WM_COMMAND, MAKELONG(3, EN_RETURN), (LPARAM)hWnd);
PostMessage(hParent, WM_KEYDOWN, VK_DOWN, 0x00500001);
PostMessage(hParent, WM_KEYUP , VK_DOWN, 0x00500001);
return 0;
case VK_LEFT:
if(pData->uEditMode && iId == 3)
break;
SendMessage(hWnd, EM_GETSEL, (WPARAM)&dwStart, (LPARAM)&dwStop);
if(dwStart || dwStop)
break;
PostMessage(hParent, WM_COMMAND, MAKELONG(3, EN_RETURN), (LPARAM)hWnd);
PostMessage(hParent, WM_KEYDOWN, VK_LEFT, 0x00500001);
PostMessage(hParent, WM_KEYUP , VK_LEFT, 0x00500001);
return 0;
case VK_RIGHT:
if(pData->uEditMode && iId == 3)
break;
SendMessage(hWnd, EM_GETSEL, (WPARAM)&dwStart, (LPARAM)&dwStop);
dwCount = (DWORD)SendMessage(hWnd, EM_LINELENGTH, 0, 0);
if(dwCount > dwStart)
break;
if(dwCount > dwStop)
break;
PostMessage(hParent, WM_COMMAND, MAKELONG(3, EN_RETURN), (LPARAM)hWnd);
PostMessage(hParent, WM_KEYDOWN, VK_RIGHT, 0x00500001);
PostMessage(hParent, WM_KEYUP , VK_RIGHT, 0x00500001);
return 0;
}
}
if(wParam == VK_DOWN && pData->uEditMode) {
if(!SendMessage(hCombo, CB_GETDROPPEDSTATE, 0, 0)) {
SendMessage(hCombo, CB_SHOWDROPDOWN, 1, 0);
return 0;
}
}
} else
if(uMsg == WM_CHAR) {
if(wParam == VK_RETURN) {
return 0;
}
if(wParam == VK_ESCAPE) {
return 0;
}
} else
if(uMsg == WM_COMMAND) {
if(wParam == MAKELONG(3, EN_ESCAPE) || wParam == MAKELONG(3, EN_RETURN)) {
SendMessage(hParent, WM_COMMAND, wParam, (LPARAM)hWnd);
return 0;
}
} else
if(uMsg == WM_MOUSEWHEEL) {
iState = (int)CallWindowProc(pProc, hWnd, CB_GETDROPPEDSTATE, 0, 0);
if(iState) {
iDelta = (short)HIWORD(wParam);
iDelta /= WHEEL_DELTA;
iPos = (int)CallWindowProc(pProc, hWnd, CB_GETTOPINDEX, 0, 0);
iPos -= iDelta;
CallWindowProc(pProc, hWnd, CB_SETTOPINDEX, iPos, 0);
return 0;
}
} else
if(uMsg == WM_GETDLGCODE) { // Welche Tasten werden im Dialog benutzt
return DLGC_WANTALLKEYS;
} else
if(uMsg == WM_SETTEXT) {
lResult = CallWindowProc(pProc, hWnd, uMsg, wParam, lParam);;
SendMessage(hParent, WM_COMMAND, MAKELONG(3, EN_SETTEXT), (LPARAM)hWnd);
return lResult;
}
return CallWindowProc(pProc, hWnd, uMsg, wParam, lParam);
}
//*****************************************************************************
//*
//* ToolProc
//*
//*****************************************************************************
// Ist die Fensterfunktion für das ToolTip Fenster
static LRESULT CALLBACK ToolProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
TreeListData *pData;
POINT sPoint;
UINT uPos;
if(uMsg == WM_SETFOCUS) {
SetFocus((HWND)lParam);
return 0;
}
pData = (TreeListData *)GetWindowLongPtr(hWnd, GWLP_USERDATA);
if(uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST) { // Mausklicks auf Tooltip zum Elternfenster
sPoint.x = LOWORD(lParam);
sPoint.y = HIWORD(lParam);
ClientToScreen(hWnd , &sPoint);
ScreenToClient(pData->hWnd, &sPoint);
uPos = MAKELONG(sPoint.x, sPoint.y);
return TreeListProc(pData->hWnd, uMsg, wParam, uPos);
}
return CallWindowProc(pData->pToolProc, hWnd, uMsg, wParam, lParam);
}
//*****************************************************************************
//*
//* ChangeColSize
//*
//*****************************************************************************
// Ändert die Größe der variablen Spalten
// pData : Zeiger auf die Fensterdaten
// iDelta : Ist die Größenänderung in Pixel
static void ChangeColSize(TreeListData *pData, int iDelta) {
unsigned uPos;
HDITEM sItem;
RECT sRect;
TV_COLSIZE sNotify;
int iWeight;
int iValue;
int iPoint;
int iStart;
int iSize;
int iRest;
int iOff;
int iNum;
int iCnt;
int iAll;
int iVar;
int iFix;
sItem.mask = HDI_WIDTH;
iAll = pData->iAllWeight;
iCnt = pData->uColumnCountVar;
if(iCnt <= 1) { // Nur eine variable Spalte
for(uPos = 0; uPos < pData->uColumnCount; uPos++) {
iWeight = pData->aColumn[uPos].bWeight;
if(!iWeight)
continue;
iValue = pData->aColumn[uPos].sSize;
iValue += iDelta;
sItem.cxy = iValue;
pData->aColumn[uPos].sSize = (short)iValue;
pData->iVarSize = iValue;
if(sItem.cxy < pData->aColumn[uPos].sMin) {
sItem.cxy = pData->aColumn[uPos].sMin;
}
if(pData->aColumn[uPos].sReal != sItem.cxy) { // Ändert sich die Breite
pData->aColumn[uPos].sReal = (short)sItem.cxy;
Header_SetItem(pData->hHeader, uPos, &sItem);
if(pData->uStyleEx & TVS_EX_HEADERCHGNOTIFY) {
sNotify.hdr.code = TVN_COLUMNCHANGED;
sNotify.uColumn = uPos;
sNotify.uIndex = pData->aColumn[uPos].bIndex;
sNotify.uPosX = pData->aColumnXpos[uPos];
sNotify.iSize = sItem.cxy;
UNLOCK(pData);
SendNotify(pData, &sNotify.hdr);
LOCK(pData);
}
}
break;
}
return;
}
if(iDelta > 0)
iStart = (pData->uSizeX) % iAll;
else
iStart = (pData->uSizeX - iDelta) % iAll;
iOff = 0;
for(uPos = 0;; uPos++) { // Suchen die Anfangsspalte
iWeight = pData->aColumn[uPos].bWeight;
if(!iWeight)
continue;
iOff += iWeight;
if(iOff > iStart)
break;
}
iPoint = 0;
iSize = iDelta / iAll;
iRest = iDelta % iAll;
iNum = iRest;
iOff -= iStart;
iWeight = iOff;
iValue = pData->aColumn[uPos].sSize;
iValue += iSize * iWeight;
iPoint += iRest * iWeight;
iValue += iPoint / iAll;
iNum -= iPoint / iAll;
iPoint %= iAll;
pData->aColumn[uPos].sSize = (short)iValue;
if(iWeight >= pData->aColumn[uPos].bWeight) { // Wurde die ganze Spalte berechnet
iCnt--;
iOff = 0;
}
while(iCnt > 0) {
uPos++;
if(uPos >= pData->uColumnCount)
uPos = 0;
iWeight = pData->aColumn[uPos].bWeight;
if(!iWeight)
continue;
iValue = pData->aColumn[uPos].sSize;
iCnt--;
if(iCnt) {
iValue += iSize * iWeight;
iPoint += iRest * iWeight;
iValue += iPoint / iAll;
iNum -= iPoint / iAll;
iPoint %= iAll;
} else {
iWeight -= iOff;
iValue += iSize * iWeight;
iValue += iNum;
}
pData->aColumn[uPos].sSize = (short)iValue;
}
iVar = 0;
iFix = 0;
iCnt = pData->uColumnCountVar;
for(uPos = 0; iCnt > 0; uPos++) { // Ausgeben der neuen Breiten
iWeight = pData->aColumn[uPos].bWeight;
if(!iWeight) {
iFix += pData->aColumn[uPos].sSize;
continue;
}
iVar += pData->aColumn[uPos].sSize;
sItem.cxy = pData->aColumn[uPos].sSize;
if(sItem.cxy < pData->aColumn[uPos].sMin) {
sItem.cxy = pData->aColumn[uPos].sMin;
}
if(pData->aColumn[uPos].sReal != sItem.cxy) { // Ändert sich die Breite
pData->aColumn[uPos].sReal = (short)sItem.cxy;
Header_SetItem(pData->hHeader, uPos, &sItem);
if(pData->uStyleEx & TVS_EX_HEADERCHGNOTIFY) {
sNotify.hdr.code = TVN_COLUMNCHANGED;
sNotify.uColumn = uPos;
sNotify.uIndex = pData->aColumn[uPos].bIndex;
sNotify.uPosX = pData->aColumnXpos[uPos];
sNotify.iSize = sItem.cxy;
UNLOCK(pData);
SendNotify(pData, &sNotify.hdr);
LOCK(pData);
}
}
iCnt--;
}
pData->iFixSize = iFix;
pData->iVarSize = iVar;
if(iDelta > 0) {
GetClientRect(pData->hHeader, &sRect);
InvalidateRect(pData->hHeader, NULL, FALSE);
}
}
//*****************************************************************************
//*
//* CreateToolTip
//*
//*****************************************************************************
// Erzeugt ein ToolTip Fenster
// pData : Zeiger auf die Fensterdaten
static void CreateToolTip(TreeListData *pData) {
TOOLINFO sInfo;
if(pData->hToolTip)
return;
pData->hToolTip = CreateWindow(TOOLTIPS_CLASS, NULL, WS_POPUP, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, pData->hWnd, NULL, NULL, NULL);
pData->pToolProc = (WNDPROC)GetWindowLongPtr(pData->hToolTip, GWLP_WNDPROC);
sInfo.cbSize = sizeof(TOOLINFO);
sInfo.uFlags = TTF_ABSOLUTE | TTF_TRACK | TTF_IDISHWND;
sInfo.hwnd = pData->hWnd;
sInfo.hinst = NULL;
sInfo.uId = (LPARAM)(pData->hWnd);
sInfo.lpszText = LPSTR_TEXTCALLBACK;
GetClientRect(pData->hWnd, &sInfo.rect);
SendMessage(pData->hToolTip, TTM_ADDTOOL, 0, (LPARAM)&sInfo);
SendMessage(pData->hToolTip, TTM_SETMAXTIPWIDTH, 0, 10000);
SetWindowLong(pData->hToolTip, GWL_ID, 2);
SetWindowLongPtr(pData->hToolTip, GWLP_USERDATA, (LPARAM)pData);
SetWindowLongPtr(pData->hToolTip, GWLP_WNDPROC , (LPARAM)ToolProc);
}
//*****************************************************************************
//*
//* CreateStateImageList
//*
//*****************************************************************************
// Erzeugt eine Image-Liste mit zwei Checkboxen
// pData : Zeiger auf die Fensterdaten
// iMode : Welche Imageliste soll erzeugt werden (0=hStates 1=hChecks)
static void CreateStateImageList(TreeListData *pData, int iMode) {
BITMAPINFO sInfo;
BYTE aMem[0x1000];
HDC hDcSrc;
HDC hDc;
HBITMAP hBmp;
HBITMAP hBmpNew;
RECT sRect;
int iBits;
if(pOpenThemeData) { // Über Thema zeichnen
if(!pData->hThemeBt) {
pData->hThemeBt = pOpenThemeData(pData->hWnd, L"BUTTON");
}
if(pData->hThemeBt) {
if(iMode) {
if(pData->hChecks && pData->hChecks != THEMEIMGLIST) {
ImageList_Destroy(pData->hChecks);
}
pData->hChecks = THEMEIMGLIST;
pData->iChecksXsize = 16;
pData->iChecksYsize = 16;
pData->iChecksMode = 1;
} else {
if(pData->hStates && pData->hStates != THEMEIMGLIST) {
ImageList_Destroy(pData->hStates);
}
pData->hStates = THEMEIMGLIST;
pData->iStatesXsize = 16;
pData->iStatesYsize = 16;
pData->iStatesMode = 1;
}
return;
}
}
if(iMode) {
if(pData->hChecks && pData->hChecks != THEMEIMGLIST)
return;
} else {
if(pData->hStates && pData->hStates != THEMEIMGLIST)
return;
}
hDcSrc = GetDC(NULL);
hDc = CreateCompatibleDC(NULL);
hBmp = CreateCompatibleBitmap(hDcSrc, 16 * 3, 16);
SelectObject(hDc, hBmp);
SelectObject(hDc, GetStockObject(NULL_PEN));
SetBkMode(hDc, OPAQUE);
SetBkColor(hDc, GetSysColor(COLOR_WINDOW));
SelectObject(hDc, GetSysColorBrush(COLOR_HIGHLIGHT));
Rectangle(hDc, -1, -1, 16 * 3 + 2, 16 + 2);
sRect.top = 8 - 6;
sRect.bottom = 8 + 7;
sRect.left = 16 * 1 + 8 - 7;
sRect.right = 16 * 1 + 8 + 6;
DrawFrameControl(hDc, &sRect, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_FLAT);
sRect.left = 16 * 2 + 8 - 7;
sRect.right = 16 * 2 + 8 + 6;
DrawFrameControl(hDc, &sRect, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_CHECKED | DFCS_FLAT);
iBits = GetDeviceCaps(hDc, BITSPIXEL);
sInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
sInfo.bmiHeader.biWidth = 16 * 3;
sInfo.bmiHeader.biHeight = 16;
sInfo.bmiHeader.biPlanes = 1;
sInfo.bmiHeader.biBitCount = (WORD)iBits;
sInfo.bmiHeader.biCompression = BI_RGB;
sInfo.bmiHeader.biSizeImage = 0;
sInfo.bmiHeader.biXPelsPerMeter = 0;
sInfo.bmiHeader.biYPelsPerMeter = 0;
sInfo.bmiHeader.biClrUsed = (iBits > 8) ? 0 : 1 << iBits;;
sInfo.bmiHeader.biClrImportant = (iBits > 8) ? 0 : 1 << iBits;;
GetDIBits(hDc, hBmp, 0, 0 , NULL, &sInfo, (iBits > 8) ? DIB_RGB_COLORS : DIB_PAL_COLORS);
GetDIBits(hDc, hBmp, 0, 16, aMem, &sInfo, (iBits > 8) ? DIB_RGB_COLORS : DIB_PAL_COLORS);
hBmpNew = CreateCompatibleBitmap(hDc, 16 * 3, 16);
SetDIBits(hDc, hBmpNew, 0, 16, aMem, &sInfo, (iBits > 8) ? DIB_RGB_COLORS : DIB_PAL_COLORS);
if(iMode == 0) {
pData->hStates = ImageList_Create(16, 16, ILC_COLORDDB | ILC_MASK, 3, 14);
pData->iStatesXsize = 16;
pData->iStatesYsize = 16;
pData->iStatesMode = 1;
ImageList_AddMasked(pData->hStates, hBmpNew, GetSysColor(COLOR_HIGHLIGHT));
} else {
pData->hChecks = ImageList_Create(16, 16, ILC_COLORDDB | ILC_MASK, 3, 14);
pData->iChecksXsize = 16;
pData->iChecksYsize = 16;
pData->iChecksMode = 1;
ImageList_AddMasked(pData->hChecks, hBmpNew, GetSysColor(COLOR_HIGHLIGHT));
}
DeleteObject(hBmpNew);
DeleteObject(hBmp);
DeleteDC(hDc);
ReleaseDC(NULL, hDcSrc);
}
//*****************************************************************************
//*
//* CreateDragImage
//*
//*****************************************************************************
// Erzeugt eine Image-Liste mit zwei Checkboxen
// pData : Zeiger auf die Fensterdaten
// uSub : Ist die Spalte für die das Drag-Image erzeugt werden soll
// Ergibt ein Handle mit der Imageliste oder NULL bei einem Fehler
static HIMAGELIST CreateDragImage(TreeListData *pData, unsigned uItem, unsigned uSub) {
ExtraItem *pExtra;
BaseItem *pEntry;
HIMAGELIST hList;
BITMAPINFO sInfo;
BYTE *pMem;
HDC hDcSrc;
HDC hDc;
HBITMAP hBmp;
HBITMAP hBmpNew;
RECT sRect;
unsigned uTSize;
int iAdd;
int iBits;
int iWidth;
int iHeigh;
int iImage;
int iYpos;
LPCTSTR pText;
if(uItem > pData->uTreeItemsMax)
return NULL;
pEntry = pData->pTreeItems[uItem];
if(!pEntry)
return 0;
iHeigh = pData->iFontHeight;
if(uSub) { // Image für Extraeintrag erzeugen
if(uSub >= pData->uColumnCount)
return 0;
pExtra = pData->pExtraItems[uSub - 1][uItem];
if(!pExtra) {
pText = _T("????");
uTSize = 4;
iImage = -1;
iWidth = pData->iFontHeight * 4;
} else {
pText = pExtra->pText;
uTSize = pExtra->uTextSize;
iImage = pExtra->iImage;
iWidth = pExtra->iTextPixels;
if(pExtra->bCallback & (TVIF_IMAGE | TVIF_TEXT)) {
CallbackExtra(pData, pEntry, pExtra, uItem, uSub, pExtra->bCallback, &iImage, &uTSize, &pText);
}
}
} else { // Image für Haupteintrag erzeugen
pText = pEntry->pText;
uTSize = pEntry->uTextSize;
iImage = pEntry->iImage;
iWidth = pEntry->iTextPixels;
if(pEntry->bCallback & (TVIF_IMAGE | TVIF_TEXT)) {
CallbackEntry(pData, pEntry, uItem, pEntry->bCallback, &iImage, &uTSize, &pText);
}
}
if(pData->hImages && iImage >= 0) { // Größen für Images anpassen
if(iHeigh < pData->iImagesYsize)
iHeigh = pData->iImagesYsize;
iAdd = pData->iImagesXsize + 2;
iWidth += iAdd;
} else {
iAdd = 0;
iImage = 1;
}
if(iWidth > 240)
iWidth = 240;
if(iHeigh > 32)
iHeigh = 32;
pMem = new(BYTE, iHeigh * (iWidth + 4) * 4 + 1024);
if(!pMem)
return NULL;
hDcSrc = GetDC(NULL);
hDc = CreateCompatibleDC(NULL);
hBmp = CreateCompatibleBitmap(hDcSrc, iWidth, iHeigh);
SelectObject(hDc, hBmp);
SelectObject(hDc, GetStockObject(NULL_PEN));
SelectObject(hDc, (pEntry->uState & TVIS_BOLD) ? pData->hFontB : pData->hFontN);
SetTextColor(hDc, pData->uColors[TVC_TEXT]);
SetBkColor(hDc, RGB(123, 77, 91));
sRect.top = 0;
sRect.bottom = iHeigh;
sRect.left = 0;
sRect.right = iWidth;
iYpos = (iHeigh - pData->iFontHeight) / 2;
ExtTextOut(hDc, iAdd, iYpos, ETO_OPAQUE | ETO_CLIPPED, &sRect, pText, uTSize, NULL);
if(iImage >= 0) {
SetBkColor(hDc, GetSysColor(COLOR_WINDOW));
ImageList_Draw(pData->hImages, iImage, hDc, 0, 0, ILD_TRANSPARENT);
}
iBits = GetDeviceCaps(hDc, BITSPIXEL);
sInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
sInfo.bmiHeader.biWidth = iWidth;
sInfo.bmiHeader.biHeight = iHeigh;
sInfo.bmiHeader.biPlanes = 1;
sInfo.bmiHeader.biBitCount = (WORD)iBits;
sInfo.bmiHeader.biCompression = BI_RGB;
sInfo.bmiHeader.biSizeImage = 0;
sInfo.bmiHeader.biXPelsPerMeter = 0;
sInfo.bmiHeader.biYPelsPerMeter = 0;
sInfo.bmiHeader.biClrUsed = (iBits > 8) ? 0 : 1 << iBits;;
sInfo.bmiHeader.biClrImportant = (iBits > 8) ? 0 : 1 << iBits;;
GetDIBits(hDc, hBmp, 0, 0 , NULL, &sInfo, (iBits > 8) ? DIB_RGB_COLORS : DIB_PAL_COLORS);
GetDIBits(hDc, hBmp, 0, iHeigh, pMem, &sInfo, (iBits > 8) ? DIB_RGB_COLORS : DIB_PAL_COLORS);
hBmpNew = CreateCompatibleBitmap(hDc, iWidth, iHeigh);
SetDIBits(hDc, hBmpNew, 0, iHeigh, pMem, &sInfo, (iBits > 8) ? DIB_RGB_COLORS : DIB_PAL_COLORS);
hList = ImageList_Create(iWidth, iHeigh, ILC_COLORDDB | ILC_MASK, 1, 0);
ImageList_AddMasked(hList, hBmpNew, RGB(123, 77, 91));
DeleteObject(hBmpNew);
DeleteObject(hBmp);
DeleteDC(hDc);
ReleaseDC(NULL, hDcSrc);
delete(pMem);
return hList;
}
//*****************************************************************************
//*
//* UpdateColorsList
//*
//*****************************************************************************
// Aktualisiert alle Farben
// pData : Zeiger auf die Fensterdaten
static void UpdateColorsList(TreeListData *pData) {
unsigned uColOdd;
unsigned uColor;
int iDiff;
int iSum;
if(!pData->cColorChanged[TVC_BK ])
pData->uColors[TVC_BK ] = GetSysColor(COLOR_WINDOW);
if(!pData->cColorChanged[TVC_BOX ])
pData->uColors[TVC_BOX ] = GetSysColor(COLOR_BTNSHADOW);
if(!pData->cColorChanged[TVC_EVEN ])
pData->uColors[TVC_EVEN ] = GetSysColor(COLOR_WINDOW);
if(!pData->cColorChanged[TVC_TEXT ])
pData->uColors[TVC_TEXT ] = GetSysColor(COLOR_WINDOWTEXT);
if(!pData->cColorChanged[TVC_LINE ])
pData->uColors[TVC_LINE ] = GetSysColor(COLOR_WINDOWTEXT);
if(!pData->cColorChanged[TVC_FRAME ])
pData->uColors[TVC_FRAME ] = GetSysColor(COLOR_3DFACE);
if(!pData->cColorChanged[TVC_TRACK ])
pData->uColors[TVC_TRACK ] = GetSysColor(COLOR_WINDOWTEXT) ^ RGB(0, 0, 255);
if(!pData->cColorChanged[TVC_INSERT ])
pData->uColors[TVC_INSERT ] = GetSysColor(COLOR_INFOBK);
if(!pData->cColorChanged[TVC_ODD ])
pData->uColors[TVC_ODD ] = GetSysColor(COLOR_INFOBK);
if(!pData->cColorChanged[TVC_BOXBG ])
pData->uColors[TVC_BOXBG ] = GetSysColor(COLOR_WINDOW);
if(!pData->cColorChanged[TVC_COLBK ])
pData->uColors[TVC_COLBK ] = GetSysColor(COLOR_WINDOW);
if(!pData->cColorChanged[TVC_COLODD ])
pData->uColors[TVC_COLODD ] = GetSysColor(COLOR_BTNSHADOW);
if(!pData->cColorChanged[TVC_COLEVEN])
pData->uColors[TVC_COLEVEN] = GetSysColor(COLOR_WINDOW);
if(!pData->cColorChanged[TVC_GRAYED ])
pData->uColors[TVC_GRAYED ] = GetSysColor(COLOR_SCROLLBAR);
if(pData->hTheme && !pData->cColorChanged[TVC_BOXBG] && !pData->cColorChanged[TVC_BOX] && !pData->cColorChanged[TVC_LINE]) {
pData->cGlyphOk = 1;
} else {
pData->cGlyphOk = 0;
}
if(!pData->cColorChanged[TVC_GRAYED]) {
pData->uColors[TVC_GRAYED] = (GetSysColor(COLOR_SCROLLBAR) & 0x00FEFEFE) >> 1;
pData->uColors[TVC_GRAYED] += (GetSysColor(COLOR_WINDOW) & 0x00FEFEFE) >> 1;
}
if(!pData->cColorChanged[TVC_ODD]) {
uColOdd = pData->uColors[TVC_ODD];
iDiff = ((uColOdd) & 0xFF) - ((pData->uColors[TVC_EVEN]) & 0xFF);
iSum = iDiff * iDiff;
iDiff = ((uColOdd >> 8) & 0xFF) - ((pData->uColors[TVC_EVEN] >> 8) & 0xFF);
iSum += iDiff * iDiff;
iDiff = ((uColOdd >> 16) & 0xFF) - ((pData->uColors[TVC_EVEN] >> 16) & 0xFF);
iSum += iDiff * iDiff;
if(iSum < 64) { // Ist die alternierente Farbe fast gleich ?
uColOdd = pData->uColors[TVC_EVEN] & 0x0000FFFF;
uColOdd |= ((pData->uColors[TVC_EVEN] & 0x00FF0000) - 0x00080000) & 0x00FF0000;
}
pData->uColors[TVC_ODD] = uColOdd;
}
if(!pData->cColorChanged[TVC_COLBK]) {
uColor = GetSysColor(COLOR_WINDOW);
if(uColor & 0x00F00000)
uColor -= 0x00100000;
if(uColor & 0x0000F000)
uColor -= 0x00001000;
if(uColor & 0x000000F0)
uColor -= 0x00000010;
pData->uColors[TVC_COLBK] = uColor;
}
if(!pData->cColorChanged[TVC_COLODD]) {
uColor = pData->uColors[TVC_ODD];
if(uColor & 0x00F00000)
uColor -= 0x00100000;
if(uColor & 0x0000F000)
uColor -= 0x00001000;
if(uColor & 0x000000F0)
uColor -= 0x00000010;
pData->uColors[TVC_COLODD] = uColor;
}
if(!pData->cColorChanged[TVC_COLEVEN]) {
uColor = GetSysColor(COLOR_WINDOW);
if(uColor & 0x00F00000)
uColor -= 0x00100000;
if(uColor & 0x0000F000)
uColor -= 0x00001000;
if(uColor & 0x000000F0)
uColor -= 0x00000010;
pData->uColors[TVC_COLEVEN] = uColor;
}
if(!pData->cColorChanged[TVC_MARKODD ])
pData->uColors[TVC_MARKODD ] = ((pData->uColors[TVC_ODD ] >> 3) & 0x1F1F1F) * 7;
if(!pData->cColorChanged[TVC_MARKODD ])
pData->uColors[TVC_MARKODD ] += ((GetSysColor(COLOR_HIGHLIGHT) >> 3) & 0x1F1F1F) * 1;
if(!pData->cColorChanged[TVC_MARKEVEN])
pData->uColors[TVC_MARKEVEN] = ((pData->uColors[TVC_EVEN ] >> 3) & 0x1F1F1F) * 7;
if(!pData->cColorChanged[TVC_MARKEVEN])
pData->uColors[TVC_MARKEVEN] += ((GetSysColor(COLOR_HIGHLIGHT) >> 3) & 0x1F1F1F) * 1;
}
//*****************************************************************************
//*
//* UpdateHeight
//*
//*****************************************************************************
// Checks if the row height has changed
// pData : pointer to the window data
// Returns 1 if changed or 0 else.
static int UpdateHeight(TreeListData *pData) {
int iHeight;
RECT sRect;
if(pData->cFixedHeight)
return 0;
iHeight = 10;
if(pData->hChecks)
if(iHeight < pData->iChecksYsize + 2)
iHeight = pData->iChecksYsize + 2;
if(pData->hStates)
if(iHeight < pData->iStatesYsize + 2)
iHeight = pData->iStatesYsize + 2;
if(iHeight < pData->iSubImgYsize + 2)
iHeight = pData->iSubImgYsize + 2;
if(iHeight < pData->iImagesYsize + 2)
iHeight = pData->iImagesYsize + 2;
if(iHeight < pData->iFontHeight + 2)
iHeight = pData->iFontHeight + 2;
if(pData->uStyleEx & TVS_EX_ITEMLINES)
iHeight++;
if(pData->uStyle & TVS_NONEVENHEIGHT && (iHeight & 1))
iHeight++;
if(pData->iRowHeight == iHeight)
return 0;
pData->iRowHeight = iHeight;
if(pData->uSizeY > pData->uStartPixel) {
pData->uMaxEnties = pData->uSizeY;
pData->uMaxEnties -= pData->uStartPixel;
} else {
pData->uMaxEnties = 0;
}
pData->uPageEnties = pData->uMaxEnties;
pData->uMaxEnties += pData->iRowHeight - 1;
pData->uMaxEnties /= pData->iRowHeight;
pData->uPageEnties /= pData->iRowHeight;
GetClientRect(pData->hWnd, &sRect);
InvalidateRect(pData->hWnd, &sRect, FALSE);
return 1;
}
//*****************************************************************************
//*
//* UpdateRect
//*
//*****************************************************************************
// Zeichnet einen Eintrag neu
// pData : Zeiger auf die Fensterdaten
// uItem : Ist die Nummer des Eintrages
// uSub : Ist die Spaltennummer
// Ergibt 1 wenn der Eintrag sichtbar war
static int UpdateRect(TreeListData *pData, unsigned uItem, unsigned uSub) {
BaseItem *pEntry;
RECT sRect;
UINT uNext;
UINT uPos;
pEntry = pData->pTreeItems[uItem];
if(!pEntry || !pEntry->uShowPos)
return 0; // Ist der Eintrag aufgeklappt
uPos = pEntry->uShowPos - pData->uScrollY - 1;
if(uPos >= pData->uMaxEnties)
return 0; // Eintrag im Fenster sichtbar
uNext = pData->aColumn[uSub].bNext;
sRect.left = pData->aColumnXpos[uSub ];
sRect.left -= pData->uScrollX;
sRect.right = pData->aColumnXpos[uNext];
sRect.right -= pData->uScrollX;
sRect.top = pData->uStartPixel;
sRect.top += pData->iRowHeight * uPos;
sRect.bottom = pData->iRowHeight + sRect.top;
InvalidateRect(pData->hWnd, &sRect, FALSE);
return 1;
}
//*****************************************************************************
//*
//* UpdateColRect
//*
//*****************************************************************************
// Zeichnet einen ganze Spalte neu
// pData : Zeiger auf die Fensterdaten
// uColumn : Die nummer der Spalte
// Ergibt 1 wenn der Eintrag sichtbar war
static int UpdateColRect(TreeListData *pData, unsigned uColumn) {
RECT sRect;
UINT uNext;
if(uColumn >= pData->uColumnCount)
return 0;
sRect.left = pData->aColumnXpos[uColumn];
sRect.left -= pData->uScrollX;
if(sRect.left > (int)pData->uSizeX)
return 0;
uNext = pData->aColumn[uColumn].bNext;
sRect.right = pData->aColumnXpos[uNext];
sRect.right -= pData->uScrollX;
if(sRect.right < 0)
return 0;
sRect.top = 0;
sRect.bottom = pData->uSizeY;
InvalidateRect(pData->hWnd, &sRect, FALSE);
return 1;
}
//*****************************************************************************
//*
//* UpdateRow
//*
//*****************************************************************************
// Zeichnet einen Eintrag über die ganze Zeile neu
// pData : Zeiger auf die Fensterdaten
// uItem : Ist die Nummer des Eintrages
// Ergibt 1 wenn der Eintrag sichtbar war
static int UpdateRow(TreeListData *pData, unsigned uItem) {
BaseItem *pEntry;
RECT sRect;
unsigned uPos;
pEntry = pData->pTreeItems[uItem];
if(!pEntry || !pEntry->uShowPos)
return 0; // Ist der Eintrag aufgeklappt
uPos = pEntry->uShowPos - pData->uScrollY - 1;
if(uPos >= pData->uMaxEnties)
return 0; // Eintrag im Fenster sichtbar
sRect.left = 0;
sRect.right = pData->uSizeX;
sRect.top = pData->uStartPixel;
sRect.top += pData->iRowHeight * uPos;
sRect.bottom = pData->iRowHeight + sRect.top;
InvalidateRect(pData->hWnd, &sRect, FALSE);
return 1;
}
//*****************************************************************************
//*
//* UpdateView
//*
//*****************************************************************************
// Redraw the whole window
// pData : Zeiger auf die Fensterdaten
// Ergibt 1 wenn der Eintrag sichtbar war
static void UpdateView(TreeListData *pData) {
RECT sRect;
GetClientRect(pData->hWnd, &sRect);
sRect.top = pData->uStartPixel;
InvalidateRect(pData->hWnd, &sRect, FALSE);
if(pData->hHeader && ((pData->uStyleEx & TVS_EX_HIDEHEADERS) == 0)){
GetClientRect(pData->hHeader, &sRect);
InvalidateRect(pData->hHeader, &sRect, FALSE);
}
}
//*****************************************************************************
//*
//* UpdateScrollX
//*
//*****************************************************************************
// Aktualisiert die X-Scroolbar
// pData : Zeiger auf die Fensterdaten
// Ergibt 1 wenn der sich Einstellungen verändert haben
static void UpdateScrollX(TreeListData *pData) {
SCROLLINFO sInfo;
unsigned uSize;
unsigned uCols;
uCols = pData->uColumnCount;
if(uCols)
uSize = pData->aColumnXpos[uCols] - 1;
else
uSize = pData->iMaxSizeX - 1;
if(pData->uOldXCount == uSize)
if(pData->uOldXPage == pData->uSizeX) {
return;
}
pData->uOldXPage = pData->uSizeX;
pData->uOldXCount = uSize;
UNLOCK(pData);
sInfo.cbSize = sizeof(SCROLLINFO);
sInfo.fMask = SIF_ALL;
sInfo.nMin = 0;
sInfo.nMax = uSize;
sInfo.nPage = pData->uSizeX;
sInfo.nPos = pData->uScrollX;
sInfo.nTrackPos = 0;
if(pData->uStyle & TVS_NOSCROLL) {
sInfo.nMax = 0;
} else
if(pData->uStyleEx & TVS_EX_AUTOHSCROLL) {
sInfo.nMax = 0;
} else
if(sInfo.nMax > 0) {
sInfo.nMax--;
}
if((int)sInfo.nPage >= sInfo.nMax && pData->uScrollX > 0) {
sInfo.nPos = 0;
pData->uScrollX = 0;
UpdateView(pData);
if(pData->hHeader) {
MoveWindow(pData->hHeader, 0, 0, pData->uSizeX, pData->uStartPixel, TRUE);
}
}
SetScrollInfo(pData->hWnd, SB_HORZ, &sInfo, TRUE);
LOCK(pData);
}
//*****************************************************************************
//*
//* UpdateScrollY
//*
//*****************************************************************************
// Aktualisiert die Y-Scroolbar
// pData : Zeiger auf die Fensterdaten
// Ergibt 1 wenn der sich Einstellungen verändert haben
static void UpdateScrollY(TreeListData *pData) {
SCROLLINFO sInfo;
if(pData->uOldYCount == pData->uItemPosCount)
if(pData->uOldYPage == pData->uPageEnties) {
return;
}
pData->uOldYPage = pData->uPageEnties;
pData->uOldYCount = pData->uItemPosCount;
UNLOCK(pData);
sInfo.cbSize = sizeof(SCROLLINFO);
sInfo.fMask = SIF_ALL;
sInfo.nMin = 0;
sInfo.nMax = pData->uItemPosCount;
sInfo.nPage = pData->uPageEnties;
sInfo.nPos = pData->uScrollY;
sInfo.nTrackPos = 0;
if(pData->uStyle & TVS_NOSCROLL) {
sInfo.nMax = 0;
}
if((int)sInfo.nPage >= sInfo.nMax && pData->uScrollY > 0) {
sInfo.nPos = 0;
pData->uScrollY = 0;
UpdateView(pData);
}
SetScrollInfo(pData->hWnd, SB_VERT, &sInfo, TRUE);
LOCK(pData);
}
//*****************************************************************************
//*
//* UpdateToolTip
//*
//*****************************************************************************
// Aktualisiert den Text für das Tootip
// pData : Zeiger auf Fensterdaten
// uItem : Item auf den der Mauszeiger zeigt
// uFlags : Flags vom HitTest
static void UpdateToolTip(TreeListData *pData, unsigned uItem, unsigned uFlags) {
TCHAR cTemp[INFOTIPSIZE];
#ifndef __REACTOS__
HWND hToolTip;
#endif
NMTVGETINFOTIP sToolNv;
NMTREEVIEW sNotify;
TOOLINFO sInfo;
ExtraItem *pExtra;
BaseItem *pEntry;
LPCTSTR pText;
RECT sRect;
unsigned uSize;
unsigned uCol;
unsigned uLen;
LRESULT lRet;
int iTemp;
// Tooltip ausbelnden
if(!uItem || (uItem == pData->uEditItem && TVHT_SUBTOCOL(uFlags) == pData->uEditSub)) {
if(pData->uToolTipItem)
goto ExitTip;
return;
}
pEntry = pData->pTreeItems[uItem];
if(uFlags & TVHT_ONITEM) {
if(pData->uToolTipItem != uItem || pData->uToolTipSub != 0) {
if(!pData->pTreeItems[uItem]) { // Existiert der Eintag noch ?
goto ExitTip;
}
TreeListGetItemRect(pData, uItem, TVIR_GETCOLUMN | TVIR_TEXT, &sRect);
if(sRect.right > (int)pData->uSizeX) {
sRect.right = pData->uSizeX;
}
lRet = 0;
if(pData->uStyleEx & TVS_EX_TOOLTIPNOTIFY) { // Tooltip-Daten via speziellem Notify holen
sNotify.hdr.code = TVN_ITEMTOOLTIP;
sNotify.action = 0;
sNotify.itemNew.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_SUBITEM;
sNotify.itemNew.hItem = (HTREEITEM)(ULONG_PTR)uItem;
sNotify.itemNew.stateMask = 0xFFFFFFFF;
sNotify.itemNew.state = pEntry->uState;
sNotify.itemNew.lParam = pEntry->lParam;
sNotify.itemNew.pszText = pEntry->pText;
sNotify.itemNew.cchTextMax = pEntry->uTextSize;
sNotify.itemNew.cChildren = 0;
sNotify.itemOld.mask = 0;
sNotify.ptDrag.x = sRect.left;
sNotify.ptDrag.y = sRect.top;
UNLOCK(pData);
lRet = SendNotify(pData, &sNotify.hdr);
LOCK(pData);
if(lRet)
goto UserTip;
} else
if(pData->uStyle & TVS_INFOTIP) { // Tooltip-Daten via normalem Notify holen
sToolNv.hdr.code = TVN_GETINFOTIP;
sToolNv.cchTextMax = INFOTIPSIZE;
sToolNv.hItem = (HTREEITEM)(ULONG_PTR)uItem;
sToolNv.lParam = pEntry->lParam;
sToolNv.pszText = cTemp;
str_ncpy(cTemp, pEntry->pText, INFOTIPSIZE);
cTemp[INFOTIPSIZE - 1] = 0;
UNLOCK(pData);
lRet = SendNotify(pData, &sNotify.hdr);
LOCK(pData);
sNotify.itemNew.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_SUBITEM;
sNotify.itemNew.hItem = (HTREEITEM)(ULONG_PTR)uItem;
sNotify.itemNew.pszText = sToolNv.pszText;
sNotify.itemNew.cchTextMax = str_len(sToolNv.pszText);
sNotify.itemNew.cChildren = 0;
sNotify.itemNew.stateMask = 0xFFFFFFFF;
sNotify.itemNew.state = pEntry->uState;
sNotify.itemNew.lParam = pEntry->lParam;
sNotify.ptDrag.x = sRect.left;
sNotify.ptDrag.y = sRect.top;
goto UserTip;
}
// Passt der Text in die Spalte
if(sRect.right - sRect.left <= pEntry->iTextPixels + 4) {
pText = pEntry->pText;
uSize = pEntry->uTextSize;
if(pEntry->bCallback & TVIF_TEXT) {
CallbackEntry(pData, pEntry, uItem, TVIF_TEXT, &iTemp, &uSize, &pText);
}
if(!pText || *pText == 0)
goto ExitTip;
uLen = str_len(pText) + 1;
if(uLen >= pData->uToolTipSize) { // Tooltipspeicher vergrößern
delete(pData->pToolTipText);
pData->uToolTipSize = (uLen + 255)&~0xFF;
pData->pToolTipText = new(TCHAR, pData->uToolTipSize + 4);
}
memcpy(pData->pToolTipText, pText, uLen * sizeof(TCHAR));
pData->hFontT = (pEntry->uState & TVIS_BOLD) ? pData->hFontB : pData->hFontN;
#ifndef __REACTOS__
hToolTip = pData->hToolTip;
#endif
pData->sToolTipPos.x = sRect.left;
pData->sToolTipPos.y = sRect.top;
ClientToScreen(pData->hWnd, &pData->sToolTipPos);
UNLOCK(pData);
sInfo.cbSize = sizeof(sInfo);
sInfo.hwnd = pData->hWnd;
sInfo.uId = (UINT_PTR)pData->hWnd;
if(pData->uToolTipItem) {
SendMessage(pData->hToolTip, TTM_TRACKACTIVATE, 0, (LPARAM)&sInfo);
pData->uToolTipItem = 0;
}
SendMessage(pData->hToolTip, TTM_TRACKPOSITION, 0, MAKELONG(pData->sToolTipPos.x, pData->sToolTipPos.y));
SendMessage(pData->hToolTip, TTM_TRACKACTIVATE, 1, (LPARAM)&sInfo);
LOCK(pData);
pData->uToolTipItem = uItem;
pData->uToolTipShow = 0;
pData->uToolTipSub = 0;
SetTimer(pData->hWnd, ID_TOOLTIPCHECK, 1500, NULL);
} else {
if(pData->uToolTipItem)
goto ExitTip;
}
}
return;
}
if(uFlags & (TVHT_ONSUBICON | TVHT_ONSUBLABEL)) {
if(pData->uToolTipItem != uItem || TVHT_SUBTOCOL(uFlags) != pData->uToolTipSub) {
lRet = 0;
uCol = TVHT_SUBTOCOL(uFlags);
pExtra = pData->pExtraItems[uCol - 1][uItem];
if(pData->uStyleEx & TVS_EX_TOOLTIPNOTIFY) { // Tooltip-Daten via Notify holen
TreeListGetItemRect(pData, uItem, TVIR_GETCOLUMN | TVIR_TEXT | TVIR_COLTOSUB(uCol), &sRect);
if(sRect.right > (int)pData->uSizeX) {
sRect.right = pData->uSizeX;
}
if(pExtra) {
sNotify.itemNew.state = pExtra->uState;
sNotify.itemNew.pszText = pExtra->pText;
sNotify.itemNew.cchTextMax = pExtra->uTextSize;
} else {
sNotify.itemNew.state = 0;
sNotify.itemNew.cchTextMax = 0;
sNotify.itemNew.pszText = _T("");
}
sNotify.hdr.code = TVN_ITEMTOOLTIP;
sNotify.action = 0;
sNotify.itemNew.lParam = pEntry->lParam;
sNotify.itemNew.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_SUBITEM;
sNotify.itemNew.hItem = (HTREEITEM)(ULONG_PTR)uItem;
sNotify.itemNew.stateMask = 0xFFFFFFFF;
sNotify.itemNew.cChildren = uCol;
sNotify.itemOld.mask = 0;
sNotify.ptDrag.x = sRect.left;
sNotify.ptDrag.y = sRect.top;
UNLOCK(pData);
lRet = SendNotify(pData, &sNotify.hdr);
LOCK(pData);
if(lRet)
goto UserTip;
}
if(pExtra) { // Tooltip auf Unterspalte ?
TreeListGetItemRect(pData, uItem, TVIR_GETCOLUMN | TVIR_TEXT | TVIR_COLTOSUB(uCol), &sRect);
if(sRect.right > (int)pData->uSizeX) {
sRect.right = pData->uSizeX;
}
if(sRect.right - sRect.left <= pExtra->iTextPixels + 4) {
pText = pExtra->pText;
uSize = pExtra->uTextSize;
if(pExtra->bCallback & TVIF_TEXT) {
CallbackExtra(pData, pEntry, pExtra, uItem, uCol, TVIF_TEXT, &iTemp, &uSize, &pText);
}
if(!pText || *pText == 0)
goto ExitTip;
uLen = str_len(pText) + 1;
if(uLen >= pData->uToolTipSize) { // Tooltipspeicher vergrößern
delete(pData->pToolTipText);
pData->uToolTipSize = (uLen + 255)&~0xFF;
pData->pToolTipText = new(TCHAR, pData->uToolTipSize + 4);
}
memcpy(pData->pToolTipText, pText, uLen * sizeof(TCHAR));
pData->hFontT = (pExtra->uState & TVIS_BOLD) ? pData->hFontB : pData->hFontN;
pData->sToolTipPos.x = sRect.left;
pData->sToolTipPos.y = sRect.top;
#ifndef __REACTOS__
hToolTip = pData->hToolTip;
#endif
ClientToScreen(pData->hWnd, &pData->sToolTipPos);
UNLOCK(pData);
sInfo.cbSize = sizeof(sInfo);
sInfo.hwnd = pData->hWnd;
sInfo.uId = (UINT_PTR)pData->hWnd;
if(pData->uToolTipItem) {
SendMessage(pData->hToolTip, TTM_TRACKACTIVATE, 0, (LPARAM)&sInfo);
pData->uToolTipItem = 0;
}
SendMessage(pData->hToolTip, TTM_TRACKPOSITION, 0, MAKELONG(pData->sToolTipPos.x, pData->sToolTipPos.y));
SendMessage(pData->hToolTip, TTM_TRACKACTIVATE, 1, (LPARAM)&sInfo);
LOCK(pData);
pData->uToolTipItem = uItem;
pData->uToolTipSub = uCol;
pData->uToolTipShow = 0;
SetTimer(pData->hWnd, ID_TOOLTIPCHECK, 1500, NULL);
} else {
if(pData->uToolTipItem)
goto ExitTip;
}
} else {
if(pData->uToolTipItem)
goto ExitTip;
}
}
return;
}
ExitTip:
if(pData->uToolTipItem) { // Tooltip ausblenden
UNLOCK(pData);
sInfo.cbSize = sizeof(sInfo);
sInfo.hwnd = pData->hWnd;
sInfo.uId = (UINT_PTR)pData->hWnd;
SendMessage(pData->hToolTip, TTM_TRACKACTIVATE, 0, (LPARAM)&sInfo);
LOCK(pData);
pData->uToolTipItem = 0;
pData->uToolTipSub = 0;
pData->uToolTipShow = 0;
KillTimer(pData->hWnd, ID_TOOLTIPCHECK);
}
return;
UserTip:
pText = sNotify.itemNew.pszText; // Soll ein User-Tooltip angezeigt werden
uSize = sNotify.itemNew.cchTextMax;
sRect.left = sNotify.ptDrag.x;
sRect.top = sNotify.ptDrag.y;
if(!pText || *pText == 0)
goto ExitTip;
uLen = str_len(pText) + 1;
if(uLen >= pData->uToolTipSize) { // Tooltipspeicher vergrößern
delete(pData->pToolTipText);
pData->uToolTipSize = (uLen + 255)&~0xFF;
pData->pToolTipText = new(TCHAR, pData->uToolTipSize + 4);
}
memcpy(pData->pToolTipText, pText, uLen * sizeof(TCHAR));
pData->hFontT = (pEntry->uState & TVIS_BOLD) ? pData->hFontB : pData->hFontN;
ClientToScreen(pData->hWnd, &sNotify.ptDrag);
pData->sToolTipPos = sNotify.ptDrag;
#ifndef __REACTOS__
hToolTip = pData->hToolTip;
#endif
// Tooltip verzögert anzeigen
if((sNotify.itemNew.mask & TVIF_TOOLTIPTIME) && sNotify.itemNew.lParam > 0) {
pData->uToolTipShow = (unsigned)(sNotify.itemNew.lParam + 499) / 500;
pData->uToolTipSub = sNotify.itemNew.cChildren;
pData->uToolTipItem = uItem;
SetTimer(pData->hWnd, ID_TOOLTIPCHECK, 500, NULL);
return;
}
UNLOCK(pData);
sInfo.cbSize = sizeof(sInfo);
sInfo.hwnd = pData->hWnd;
sInfo.uId = (UINT_PTR)pData->hWnd;
if(pData->uToolTipItem) { // Tooltip Fenster aktivieren
SendMessage(pData->hToolTip, TTM_TRACKACTIVATE, 0, (LPARAM)&sInfo);
pData->uToolTipItem = 0;
}
SendMessage(pData->hToolTip, TTM_TRACKPOSITION, 0, MAKELONG(sNotify.ptDrag.x, sNotify.ptDrag.y));
SendMessage(pData->hToolTip, TTM_TRACKACTIVATE, 1, (LPARAM)&sInfo);
LOCK(pData);
pData->uToolTipShow = 0;
pData->uToolTipItem = uItem;
pData->uToolTipSub = sNotify.itemNew.cChildren;
SetTimer(pData->hWnd, ID_TOOLTIPCHECK, 1500, NULL);
}
//*****************************************************************************
//*
//* CreateFontset
//*
//*****************************************************************************
// Create the font set (normal, bold, etc...) from the given HFONT
static int CreateFontset(TreeListData *pData, HFONT hFont){
LOGFONT sLog;
HFONT hBold;
int iRet = 0;
if(GetObject(hFont, sizeof(sLog), &sLog)){
sLog.lfWeight = FW_BOLD;
if((hBold = CreateFontIndirect(&sLog))){
pData->hFontN = hFont; //store the given font
if(pData->hFontB != hDefaultFontB){
//if the current bold is not the default bold, free it
DeleteObject(pData->hFontB);
}
pData->hFontB = hBold; //store the created bold
iRet = 1;
}
}
return iRet;
}
//*****************************************************************************
//*
//* UpdateFont
//*
//*****************************************************************************
// Erzeugt einen den fetten Font für das Fenster
// pData : Zeiger auf die Fensterdaten
// iRedraw : Soll das Fenster neugezeichnet werden
// Ergibt 1 wenn der Font verändert wurde
static int UpdateFont(TreeListData *pData) {
int iPos;
int iRet;
HDC hDc;
LOGFONT sLog;
SIZE sSize;
TEXTMETRIC sMetrics;
BaseItem *pEntry;
BaseItem **pList;
ExtraItem *pExtra;
ExtraItem **pItems;
unsigned uSub;
if(!hDefaultFontN) { // Den Standard-Font erzeugen
SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(sLog), &sLog, 0);
sLog.lfWeight = FW_NORMAL;
hDefaultFontN = CreateFontIndirect(&sLog);
sLog.lfWeight = FW_BOLD;
hDefaultFontB = CreateFontIndirect(&sLog);
}
if(!pData->hFontN)
pData->hFontN = hDefaultFontN;
if(!pData->hFontB)
pData->hFontB = hDefaultFontB;
/*
if(pData->hFontN == hDefaultFontN) { // Ist der Standard-Font eingestellt
pData->hFontB = hDefaultFontB;
} else {
pData->hFontB = pData->hFontN;
}
*/
if(pData->hFontN != pData->hFontL) {
pData->hFontL = pData->hFontN;
hDc = GetDC(NULL);
SelectObject(hDc, pData->hFontN);
GetTextMetrics(hDc, &sMetrics);
pData->iFontHeight = sMetrics.tmHeight;
pData->iFontLine = sMetrics.tmAscent + 1;
pData->iFontOff = (sMetrics.tmPitchAndFamily & TMPF_FIXED_PITCH) ? 0 : -1;
ReleaseDC(NULL, hDc);
pList = pData->pTreeItems;
iPos = pData->uTreeItemsMax;
for(; iPos >= 0; iPos--) { // Alle Textbreiten zurücksetzen
pEntry = pList[iPos];
if(!pEntry)
continue;
pEntry->iTextPixels = 0;
}
for(uSub = 1; uSub < pData->uColumnCount; uSub++) {
iPos = pData->uTreeItemsMax;
pItems = pData->pExtraItems[uSub - 1];
for(; iPos >= 0; iPos--) {
pExtra = pItems[iPos];
if(!pExtra)
continue;
pExtra->iTextPixels = 0;
}
}
iRet = 1;
} else {
iRet = 0;
}
// compute Width of "..." text
hDc = GetDC(NULL);
SelectObject(hDc, pData->hFontN);
GetTextExtentExPoint(hDc, _T("..."), 3, 256, NULL, NULL, &sSize);
pData->uTrippleN = sSize.cx;
SelectObject(hDc, pData->hFontB);
GetTextExtentExPoint(hDc, _T("..."), 3, 256, NULL, NULL, &sSize);
pData->uTrippleB = sSize.cx;
ReleaseDC(NULL, hDc);
return iRet;
}
//*****************************************************************************
//*
//* UpdateItems
//*
//*****************************************************************************
// Berechnet die Positionen der Zeilen für die sichtbaren Einträge
// pData : Zeiger auf die Fensterdaten
// uItem : Ist der Eintrag ab dem begonnen wird
static void UpdateItems(TreeListData *pData, unsigned uItem) {
unsigned uPos;
unsigned uOld;
unsigned uNum;
unsigned uTemp;
unsigned uStart;
unsigned *pLines;
BaseItem **pItems;
BaseItem *pEntry;
BaseItem *pTemp;
RECT sRect;
uOld = pData->uItemPosCount;
pLines = pData->pItemPos;
pItems = pData->pTreeItems;
if(!uItem) { // Am Anfang beginnen
uItem = pData->uFirstChild;
if(!uItem) { // Leere Liste
if(!uOld)
return;
for(uNum = 0; uNum < uOld; uNum++) { // Die alten Einträge zurücksetzen
uTemp = pLines[uNum];
if(!uTemp)
continue;
pLines[uNum] = 0;
pTemp = pItems[uTemp];
if(!pTemp)
continue;
pTemp->uShowPos = 0;
}
pData->uItemPosCount = 0;
GetClientRect(pData->hWnd, &sRect);
InvalidateRect(pData->hWnd, &sRect, TRUE);
memset(pLines, 0, sizeof(unsigned)*uOld);
return;
}
for(uNum = 0; uNum < uOld; uNum++) { // Die alten Einträge zurücksetzen
uTemp = pLines[uNum];
if(!uTemp)
continue;
pLines[uNum] = 0;
pTemp = pItems[uTemp];
if(!pTemp)
continue;
pTemp->uShowPos = 0;
}
pEntry = pItems[uItem];
pEntry->uShowPos = 1;
pLines[0] = uItem;
uPos = 1;
uStart = 0;
} else { // Bei einem Eintrag beginnen
pEntry = pItems[uItem];
uPos = pEntry->uShowPos;
if(uPos)
uStart = uPos - 1;
else
uStart = 0;
for(uNum = uPos; uNum < uOld; uNum++) { // Die alten Einträge zurücksetzen
uTemp = pLines[uNum];
if(!uTemp)
continue;
pLines[uNum] = 0;
pTemp = pItems[uTemp];
if(!pTemp)
continue;
pTemp->uShowPos = 0;
}
}
for(;;) { // Die Zeilen neu zuordnen
if(pEntry->uFirstChild && (pEntry->uState & TVIS_EXPANDED)) {
uItem = pEntry->uFirstChild;
} else
if(pEntry->uNextItem) {
uItem = pEntry->uNextItem;
} else {
for(;;) {
uItem = pEntry->uParent;
if(!uItem)
break;
pEntry = pItems[uItem];
if(pEntry->uNextItem) { // Gibt es etwas in der gleichen Ebene
uItem = pEntry->uNextItem;
break;
}
}
if(!uItem)
break;
}
pEntry = pItems[uItem];
if(pLines[uPos] != uItem) {
pLines[uPos] = uItem;
} else {
if(uStart == uPos)
uStart++;
}
uPos++;
pEntry->uShowPos = uPos;
}
pData->uItemPosCount = uPos;
if(uStart > pData->uScrollY) // Neu zu zeichnenten Bereich bestimmen
uStart -= pData->uScrollY;
else
uStart = 0;
GetClientRect(pData->hWnd, &sRect);
sRect.top = pData->uStartPixel + pData->iRowHeight * uStart;
if(sRect.top <= sRect.bottom) {
InvalidateRect(pData->hWnd, &sRect, FALSE);
}
if(uOld != uPos)
UpdateScrollY(pData);
}
//*****************************************************************************
//*
//* UpdateColumns
//*
//*****************************************************************************
// Prüft ob es Veränderungen in Spaltenbreiten gab
// pData : Zeiger auf die Fensterdaten
// Ergibt die Breite ab der die Spalten verändert wurden oder 0x10000
static int UpdateColumns(TreeListData *pData) {
#ifndef __REACTOS__
HWND hHeader;
#endif
UINT uNext;
UINT uCol;
UINT uSub;
int iSize;
int iNum;
int iNow;
int iOld;
int iRet;
#ifndef __REACTOS__
hHeader = pData->hHeader;
#endif
pData->aColumnXpos[0] = 0;
iRet = 0x10000;
iOld = 0;
iNow = 0;
for(uCol = 0; uCol < pData->uColumnCount;) { // Suche die erste geänderte Spalte
uSub = pData->aColumnPos[uCol];
iSize = pData->aColumn[uSub].sReal;
uSub = pData->aColumn[uSub].bNext;
iOld = iNow;
iNow += iSize;
uCol += 1;
if(uCol == 1)
iNow -= 1;
if(iNow < iOld)
iNow = iOld;
if(uSub == pData->uColumnCount)
if(iNow >= (int)pData->uSizeX - 1) {
iNow++;
}
iNum = pData->aColumnXpos[uSub];
if(iNum == iNow)
continue;
if(iNum == 0)
iNum = iOld;
if(iNum >= iNow) {
iRet = iOld;
} else {
iRet = iOld;
if(pData->uSelectedItem) { // Problem bei ausgewählten leeren Einträgen
uNext = pData->aColumn[pData->uSelectedSub].bNext;
if(uNext == uSub) {
UpdateRect(pData, pData->uSelectedItem, pData->uSelectedSub);
}
}
if(pData->uTrackedItem) {
uNext = pData->aColumn[pData->uTrackedSub].bNext;
if(uNext == uSub) {
UpdateRect(pData, pData->uTrackedItem, pData->uTrackedSub);
}
}
}
pData->aColumnXpos[uSub] = iNow;
break;
}
while(uCol < pData->uColumnCount) { // Restliche Spalten berechen
iOld = iNow;
uSub = pData->aColumnPos[uCol];
iNow += pData->aColumn[uSub].sReal;
uSub = pData->aColumn[uSub].bNext;
uCol += 1;
if(uCol == pData->uColumnCount)
if(iNow >= (int)pData->uSizeX - 1) {
iNow++;
}
pData->aColumnXpos[uSub] = iNow;
}
pData->aColumnXpos[pData->uColumnCount + 1] = pData->uSizeX + 1;
return iRet;
}
//*****************************************************************************
//*
//* TreeListSetOrderArray
//*
//*****************************************************************************
// Stellt die anzeige Reihenfolge der Spalten ein
// pData : Zeiger auf die Fensterdaten
// uItems : Ist die Nummer des Eintrages
// pArray : Zeiger auf die Einträge. Null steht für die Standartreihenfolge.
// z.B. {0,2,1} meint die sichtbare Reihenfolge Col0,Col2,Col1
// Der erste Eintrag muss 0 immer sein.
// Ergibt 1 = Ok
// 0 = Fehler
static int TreeListSetOrderArray(TreeListData *pData, unsigned uItems, unsigned *pArray) {
BYTE aFlags[MAX_COLUMNS + 1];
UINT aArray[MAX_COLUMNS + 1];
TV_COLSIZE sNotify;
UINT uDiff;
UINT uCol;
UINT uSub;
if(!pArray) { // Spezialreihenfolge setzen
if(uItems == FROM_HEADER) { // Array aus Header holen
if(!Header_GetOrderArray(pData->hHeader, pData->uColumnCount, aArray)) {
return 0;
}
if(aArray[0] != 0) {
return 0;
}
} else {
for(uCol = pData->uColumnCount; uCol > 0; uCol++) { // Standartreihenfolge
uCol--;
aArray[uCol] = uCol;
}
}
uItems = pData->uColumnCount;
pArray = aArray;
} else { // Prüfe Array
if(pData->uColumnCount != uItems || uItems == 0 || *pArray) {
return 0;
}
}
memset(aFlags, 0, sizeof(aFlags) - 1);
for(uCol = 0, uDiff = 0; uCol < uItems; uCol++) { // Die Einträge prüfen
uSub = pArray[uCol];
if(uSub >= uItems)
return 0;
if(aFlags[uSub])
return 0;
aFlags[uSub] = (BYTE)uCol;
uDiff |= uCol ^ pData->aColumnPos[uSub];
}
if(uDiff == 0) { // Alles blieb gleich
return 1;
}
aFlags[0 ] = 0;
aFlags[uItems] = (BYTE)uItems;
for(uCol = 1; uCol < uItems; uCol++) { // Die Einträge anpassen
pData->aColumnPos[uCol] = (BYTE)pArray[uCol];
}
for(uCol = 0; uCol < uItems; uCol++) {
uSub = aFlags[uCol];
pData->aColumn[uCol].bIndex = (BYTE)uSub;
pData->aColumn[uCol].bNext = pData->aColumnPos[uSub + 1];
}
Header_SetOrderArray(pData->hHeader, uItems, pArray);
UpdateColumns(pData);
UpdateView(pData);
if(pData->uStyleEx & TVS_EX_HEADERCHGNOTIFY) { // Alle Spalten haben sich verändert
UNLOCK(pData);
for(uCol = 0; uCol < uItems; uCol++) {
sNotify.hdr.code = TVN_COLUMNCHANGED;
sNotify.uColumn = uCol;
sNotify.uIndex = pData->aColumn[uCol].bIndex;
sNotify.uPosX = pData->aColumnXpos[uCol];
sNotify.iSize = pData->aColumn[uCol].sReal;
SendNotify(pData, &sNotify.hdr);
}
LOCK(pData);
}
return 1;
}
//*****************************************************************************
//*
//* TreeListToggleItem
//*
//*****************************************************************************
// Klappt bei einem Eintrag die Kinder um, und schickt alle Notify-Nachrichten.
// pData : Zeiger auf die Fensterdaten
// uItem : Ist die Nummer des Eintrages
// uAddFlags : Sind die State-Flags die hinzugefügt werden sollen
// Bits 0..3 entahlen das Kommando, bei automatisch ermitteln
// Ergibt -1 = Fehler
// 0 = Ausgeführt
// 1 = Abbruch
static int TreeListToggleItem(TreeListData *pData, unsigned uItem, unsigned uAddFlags) {
NMTREEVIEW sNotify;
BaseItem **pList;
BaseItem *pEntry;
BaseItem *pTemp;
unsigned uAction;
unsigned uLevel;
unsigned uNext;
LRESULT lRet;
BOOL bDo;
if(uItem > pData->uTreeItemsMax)
return 0;
pList = pData->pTreeItems;
pEntry = pList[uItem];
if(!pEntry)
return -1;
uAction = uAddFlags & 0x0F;
if(!uAction) {
uAction = ((pEntry->uState ^ TVIS_EXPANDED) & (TVIS_EXPANDED | TVIS_EXPANDPARTIAL)) ? TVE_EXPAND : TVE_COLLAPSE;
}
sNotify.action = uAction;
sNotify.hdr.code = TVN_ITEMEXPANDING;
sNotify.itemNew.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE;
sNotify.itemNew.hItem = (HTREEITEM)(ULONG_PTR)uItem;
sNotify.itemNew.stateMask = 0xFFFFFFFF;
sNotify.itemNew.state = pEntry->uState;
sNotify.itemNew.lParam = pEntry->lParam;
sNotify.itemNew.pszText = (LPTSTR) - 1;
sNotify.itemNew.cchTextMax = -1;
sNotify.itemOld.mask = 0;
sNotify.ptDrag.x = 0;
sNotify.ptDrag.y = 0;
UNLOCK(pData);
lRet = SendNotify(pData, &sNotify.hdr);
LOCK(pData);
pList = pData->pTreeItems;
pEntry = pList[uItem];
if(pEntry == 0)
return -1; // Eintrag inzischen gelöscht ?
if(lRet != 0)
return 1; // User-Abbruch ?
if(uAction == TVE_EXPAND) { // Aufklappen
if(pEntry->uState & TVIS_EXPANDED) {
bDo = FALSE; // Nur von + auf -
} else {
pEntry->uState |= TVIS_EXPANDED; // Kinder Aufklappen
bDo = TRUE;
}
} else { // Zuklappen
pEntry->uState &= ~TVIS_EXPANDED;
bDo = TRUE;
}
pEntry->uState &= ~TVIS_EXPANDPARTIAL;
pEntry->uState |= uAddFlags&~0x0F;
if(pEntry->uShowPos && bDo) {
if(pEntry->uState & TVIS_EXPANDED) { // Kinderfenster aktuallisieren
uLevel = 0;
uNext = pEntry->uFirstChild;
while(uNext) {
pTemp = pList[uNext];
pTemp->uShowPos = 0;
if(pTemp->uFirstChild) {
uNext = pTemp->uFirstChild;
uLevel++;
continue;
}
if(pTemp->uNextItem) {
uNext = pTemp->uNextItem;
continue;
}
if(uLevel == 0)
break;
uNext = pList[pTemp->uParent]->uNextItem;
uLevel--;
}
}
UpdateItems(pData, uItem);
}
sNotify.action = uAction;
sNotify.hdr.code = TVN_ITEMEXPANDED;
sNotify.itemNew.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE;
sNotify.itemNew.hItem = (HTREEITEM)(ULONG_PTR)uItem;
sNotify.itemNew.stateMask = 0xFFFFFFFF;
sNotify.itemNew.state = pEntry->uState;
sNotify.itemNew.lParam = pEntry->lParam;
sNotify.itemNew.pszText = (LPTSTR) - 1;
sNotify.itemNew.cchTextMax = -1;
sNotify.itemOld.mask = 0;
sNotify.ptDrag.x = 0;
sNotify.ptDrag.y = 0;
UNLOCK(pData);
SendNotify(pData, &sNotify.hdr);
LOCK(pData);
pList = pData->pTreeItems;
pEntry = pData->pTreeItems[uItem];
if(!pEntry)
return -1; // Eintrag inzischen gelöscht ?
if(uAction == TVE_EXPAND) { // ONCE setzen nach Expandieren
pEntry->uState |= TVIS_EXPANDEDONCE;
}
if(pData->uSelectedItem && bDo) { // Ist der ausgewählten Eintrag sichtbar ?
pEntry = pList[pData->uSelectedItem];
if(!pEntry) {
pData->uSelectedItem = 0;
pData->uSelectedSub = 0;
} else
if(!pEntry->uShowPos) {
while(!pEntry->uShowPos) {
uItem = pEntry->uParent;
pEntry = pList[uItem];
}
TreeListSelectItem(pData, uItem, pData->uSelectedSub, TVC_UNKNOWN);
}
}
if(bDo == FALSE) { // Nur von + auf -
UpdateRect(pData, uItem, 0);
}
return 0;
}
//*****************************************************************************
//*
//* TreeListGetItemRect
//*
//*****************************************************************************
// Holt das Rechteck eines Eintrages
// pData : Zeiger auf die Fensterdaten
// uItem : Ist die Nummer des Eintrages
// uFlags : Bit 0 : 0=volle Zeile 1=nur Text
// Bit 7 : 1=nur Spalte
// Bit 24.. : Spaltennummer
// Ergibt 1 wenn der Eintrag sichtbar war
static int TreeListGetItemRect(TreeListData *pData, unsigned uItem, unsigned uFlags, RECT *pRect) {
ExtraItem *pExtra;
BaseItem *pEntry;
unsigned uNext;
unsigned uPos;
unsigned uSub;
if(uItem > pData->uTreeItemsMax) {
memset(pRect, 0, sizeof(RECT));
return 0;
}
pEntry = pData->pTreeItems[uItem];
if(!pEntry->uShowPos) { // Ist der Eintrag aufgeklappt
memset(pRect, 0, sizeof(RECT));
return 0;
}
uPos = pEntry->uShowPos - pData->uScrollY - 1;
if(uPos >= pData->uMaxEnties) { // Eintrag im Fenster sichtbar
memset(pRect, 0, sizeof(RECT));
return 0;
}
pRect->top = pData->uStartPixel;
pRect->top += pData->iRowHeight * uPos;
pRect->bottom = pData->iRowHeight + pRect->top;
if((uFlags & 0xFC) == TVIR_GETCOLUMN) { // Nur Spalten
uSub = uFlags >> 24;
if(uSub >= pData->uColumnCount)
uSub = 0;
uNext = pData->aColumn[uSub].bNext;
pRect->left = pData->aColumnXpos[uSub];
pRect->left -= pData->uScrollX;
pRect->right = pData->aColumnXpos[uNext];
pRect->right -= pData->uScrollX;
} else {
uSub = 0;
pRect->left = 0;
pRect->left -= pData->uScrollX;
pRect->right = pData->uSizeX;
}
if(uFlags & TVIR_TEXT) { // Nur Text ausgeben
if(uSub > 0) {
pExtra = pData ->pExtraItems[uSub - 1][uItem];
if(pData->aColumn[uSub].bEdit == TVAX_CHECK) {
pRect->left += pData->iChecksXsize;
if(pRect->left > pRect->right)
pRect->left = pRect->right;
} else
if(pExtra && pExtra->bFlags & TVIX_HASIMAGE) {
pRect->left += pData->iImagesXsize;
if(pRect->left > pRect->right)
pRect->left = pRect->right;
}
} else {
if(pData->cHasRootRow) { // Root-Linien ausgleichen
pRect->left += pData->iIndent;
}
pRect->left += pData->iIndent * pEntry->uLevel;
if(pData->hStates) {
pRect->left += pData->iStatesXsize;
}
if(!(pData->uStyle & TVS_HASLINES)) {
pRect->left -= 1;
}
if(pData->uStyleEx & TVS_EX_ITEMLINES) {
pRect->left += 1;
if(pEntry->bFlags & TVIX_HASIMAGE)
pRect->left++;
}
if(pEntry->bFlags & TVIX_HASIMAGE) {
pRect->left += pData->iImagesXsize;
}
if(pRect->left > pRect->right) {
pRect->left = pRect->right;
}
}
}
return 1;
}
//*****************************************************************************
//*
//* TreeListEnsureVisible
//*
//*****************************************************************************
// Macht einen Eintrag sichtbar
// pData : Zeiger auf die Fensterdaten
// uItem : Ist die Nummer des Eintrages
// uSub : Untereintrag der sichtbar sein soll
// 0xFFFFFFFF nur Zeile
// FIRST_LINE als oberster Eintrag
// Ergibt 1 wenn nur zum Eintrag gescrollt wurde bzw. 0 wenn aufgeklapt wurde
static int TreeListEnsureVisible(TreeListData *pData, unsigned uItem, unsigned uSub) {
BaseItem *pEntry;
BaseItem *pTemp;
unsigned uTemp;
unsigned uNext;
unsigned uPos;
int iNum;
int iAnf;
int iOff;
int iEnd;
int iMax;
int iRet;
if(uItem > pData->uTreeItemsMax)
return -1;
pEntry = pData->pTreeItems[uItem];
if(!pEntry)
return -1;
uPos = pEntry->uShowPos;
if(!uPos) { // Zweige aufklappen wenn Eintrag zugeklappt
iRet = 0;
for(pTemp = pEntry;;) {
uTemp = pTemp->uParent;
pTemp = pData->pTreeItems[uTemp];
if(!pTemp)
break;
if((pTemp->uState & TVIS_EXPANDED) == 0) {
if(TreeListToggleItem(pData, uTemp, 0))
return 0;
}
}
pEntry = pData->pTreeItems[uItem];
if(!pEntry)
return 0;
uPos = pEntry->uShowPos;
if(!uPos)
return 0;
} else { // Nur Scrollen
iRet = 1;
}
uPos--;
if(uPos < pData->uScrollY) { // Vor erster Zeile
pData->uScrollY = uPos;
SetScrollPos(pData->hWnd, SB_VERT, uPos, TRUE);
UpdateView(pData);
} else
if(uSub == FIRST_LINE) { // Als ersten Eintrag
if(uPos != pData->uScrollY) {
pData->uScrollY = uPos;
SetScrollPos(pData->hWnd, SB_VERT, uPos, TRUE);
UpdateView(pData);
}
return iRet;
} else
if(uPos >= pData->uScrollY + pData->uPageEnties) { // Nach letzter Zeile
iOff = uPos - (pData->uPageEnties - 1);
iMax = pData->uItemPosCount;
iMax -= pData->uPageEnties - 1;
if(iOff >= iMax)
iOff = iMax;
if(iOff < 0)
iOff = 0;
if(iOff != (int)pData->uScrollY) {
pData->uScrollY = iOff;
SetScrollPos(pData->hWnd, SB_VERT, iOff, TRUE);
UpdateView(pData);
}
}
if(uSub < pData->uColumnCount) { // Horizontal einrichten
uNext = pData->aColumn[uSub].bNext;
iNum = pData->uSizeX;
iOff = pData->uScrollX;
iAnf = pData->aColumnXpos[uSub ];
iEnd = pData->aColumnXpos[uNext];
if(iOff + iNum < iAnf)
iOff = iAnf;
if(iOff >= iEnd)
iOff = iAnf;
if(iOff + iNum < iEnd)
iOff = iEnd - iNum;
if(iOff > iAnf)
iOff = iAnf;
iMax = pData->aColumnXpos[pData->uColumnCount];
iMax -= pData->uSizeX / 2;
if(iOff > iMax)
iOff = iMax;
if(iOff < 0)
iOff = 0;
if(iOff != (int)pData->uScrollX) {
pData->uScrollX = iOff;
SetScrollPos(pData->hWnd, SB_HORZ, iOff, TRUE);
UpdateView(pData);
MoveWindow(pData->hHeader, -iOff, 0, iNum + iOff, pData->uStartPixel, TRUE);
}
}
return iRet;
}
//*****************************************************************************
//*
//* TreeListIsVisible
//*
//*****************************************************************************
// Prüft ob ein Eintrag sichtbar ist
// pData : Zeiger auf die Fensterdaten
// uItem : Ist die Nummer des Eintrages
// uSub : Untereintrag der geprüft werden soll
// 0xFFFFFFFF nur Zeile prüfen
// Ergibt den Zustand des Eintrages:
// -1 = Unbekannter Eintrag
// 0 = Eintrag ist zugeklappt
// 1 = Eintrag ist aufgeklappt aber nicht sichtbar
// 2 = Eintrag ist aufgeklappt und teilweise sichtbar
// 3 = Eintrag ist aufgeklappt und Spalte ist nur teilweise sichtbar
// 4 = Eintrag ist aufgeklappt und ganz sichtbar
static int TreeListIsVisible(TreeListData *pData, unsigned uItem, unsigned uSub) {
BaseItem *pEntry;
unsigned uNext;
unsigned uPos;
int iNum;
int iAnf;
int iOff;
int iEnd;
if(uItem > pData->uTreeItemsMax)
return -1;
pEntry = pData->pTreeItems[uItem];
if(!pEntry)
return -1;
uPos = pEntry->uShowPos;
if(!uPos) { // Ist der Eintrag zugeklappt
return 0;
}
uPos--;
if(uPos < pData->uScrollY) { // Vor erster Zeile
return 1;
}
if(uPos >= pData->uScrollY + pData->uMaxEnties) { // Nach letzter Zeile
return 1;
}
if(uPos == pData->uScrollY + pData->uPageEnties) { // Auf halbsichtbarer Zeile
if(uSub < pData->uColumnCount) {
uNext = pData->aColumn[uSub].bNext;
iNum = pData->uSizeX;
iOff = pData->uScrollX;
iAnf = pData->aColumnXpos[uSub ];
iEnd = pData->aColumnXpos[uNext];
if(iOff + iNum < iAnf)
return 1;
if(iOff >= iEnd)
return 1;
}
return 2;
}
if(uSub < pData->uColumnCount) { // Spalte prüfen
uNext = pData->aColumn[uSub].bNext;
iNum = pData->uSizeX;
iOff = pData->uScrollX;
iAnf = pData->aColumnXpos[uSub ];
iEnd = pData->aColumnXpos[uNext];
if(iOff + iNum < iAnf)
return 1;
if(iOff >= iEnd)
return 1;
if(iOff + iNum < iEnd)
return 3;
if(iOff > iAnf)
return 3;
}
return 4;
}
//*****************************************************************************
//*
//* TreeListDeleteItem
//*
//*****************************************************************************
// Löscht einen Eintrag aus dem Fenster
// pData : Zeiger auf die Fensterdaten
// uItem : Ist die Nummer des Eintrages der gelöscht werden soll
// iMode : Wie soll der Eintrag gelöscht werden
// 0 = Eintrag löschen und nicht neu zeichnen
// 1 = Eintrag löschen und neu zeichnen
// 2 = Nur Kindereinträge löschen und neu zeichnen
// Ergibt 1 wenn der Eintrag gelöscht wurde.
static int TreeListDeleteItem(TreeListData *pData, unsigned uItem, int iMode) {
NMTREEVIEW sNotify;
ExtraItem **pList;
ExtraItem *pExtra;
BaseItem *pEntry;
BaseItem *pTemp;
unsigned uPos;
int iOff;
int iMax;
if(pData->cLockChanges)
return 0;
if(uItem > pData->uTreeItemsMax) { // Prüfe den Eintrag
if(uItem != U(TVI_ROOT))
return 0; // Alles löschen
if(pData->uLastChild == 0)
return 0;
while(pData->uLastChild) {
TreeListDeleteItem(pData, pData->uLastChild, 0);
}
pData->uItemPosCount = 0;
UpdateScrollY(pData);
UpdateView(pData);
return 1;
}
pEntry = pData->pTreeItems[uItem];
if(!pEntry) { // Prüfe den Eintrag
if(uItem != 0)
return 0; // Alles löschen
if(pData->uLastChild == 0)
return 0;
while(pData->uLastChild) {
TreeListDeleteItem(pData, pData->uLastChild, 0);
}
pData->uItemPosCount = 0;
UpdateScrollY(pData);
UpdateView(pData);
return 1;
}
if(iMode == 2) { // Nur Kindereinträge löschen
if(!pEntry->uFirstChild) {
return 0;
}
while(pEntry->uLastChild) { // Alle Kinder löschen
TreeListDeleteItem(pData, pEntry->uLastChild, 0);
}
uPos = pEntry->uShowPos;
if(uPos) {
UpdateItems(pData, uItem);
}
return 1;
}
while(pEntry->uLastChild) { // Alle Kinder löschen
TreeListDeleteItem(pData, pEntry->uLastChild, 0);
}
if(uItem == pData->uSelectedItem) { // Einen ausgewählten Eintrag löschen
sNotify.hdr.code = TVN_SELCHANGED;
sNotify.action = TVC_UNKNOWN;
sNotify.itemOld.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_SUBITEM | TVIF_PARAM;
sNotify.itemOld.hItem = (HTREEITEM)(ULONG_PTR)uItem;
sNotify.itemOld.stateMask = 0xFFFFFFFF;
sNotify.itemOld.state = pEntry->uState&~TVIS_SELECTED;
sNotify.itemOld.lParam = pEntry->lParam;
sNotify.itemOld.cChildren = 0;
sNotify.itemOld.pszText = (LPTSTR) - 1;
sNotify.itemOld.cchTextMax = -1;
sNotify.itemNew.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_SUBITEM | TVIF_PARAM;
sNotify.itemNew.hItem = NULL;
sNotify.itemNew.stateMask = 0xFFFFFFFF;
sNotify.itemNew.state = 0;
sNotify.itemNew.lParam = 0;
sNotify.itemNew.cChildren = 0;
sNotify.itemNew.pszText = (LPTSTR) - 1;
sNotify.itemNew.cchTextMax = -1;
sNotify.ptDrag.x = 0;
sNotify.ptDrag.y = 0;
UNLOCK(pData);
SendNotify(pData, &sNotify.hdr); // Bekant geben das der Eintrag nicht mehr ausgewählt ist
LOCK(pData);
pData->uSelectedItem = 0;
pData->uSelectedSub = 0;
}
sNotify.hdr.code = TVN_DELETEITEM;
sNotify.itemNew.mask = 0;
sNotify.itemOld.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE;
sNotify.itemOld.hItem = (HTREEITEM)(ULONG_PTR)uItem;
sNotify.itemOld.lParam = pEntry->lParam;
sNotify.itemOld.pszText = (LPTSTR) - 1;
sNotify.itemNew.stateMask = 0xFFFFFFFF;
sNotify.itemNew.state = pEntry->uState;
sNotify.itemOld.cchTextMax = -1;
sNotify.ptDrag.x = 0;
sNotify.ptDrag.y = 0;
UNLOCK(pData);
SendNotify(pData, &sNotify.hdr);
LOCK(pData);
pEntry = pData->pTreeItems[uItem]; // Prüfen ob der Eintrag noch existiert
if(!pEntry)
return 0;
if(uItem == pData->uTrackedItem) { // Einen unterstrichenen Eintrag löschen
pData->uTrackedItem = 0;
pData->uTrackedSub = 0;
}
if(pData->uInsertMark == uItem) {
pData->uInsertMark = 0;
}
if(pData->uSingleSel == uItem) {
pData->uSingleSel = 0;
}
if(pEntry->uPrevItem) { // Gibt es einen vorherigen Eintrag
pTemp = pData->pTreeItems[pEntry->uPrevItem];
pTemp->uNextItem = pEntry->uNextItem;
} else {
if(pEntry->uParent) { // Neues erstes Kind in Elterneintrag
pTemp = pData->pTreeItems[pEntry->uParent];
pTemp->uFirstChild = pEntry->uNextItem;
} else {
pData->uFirstChild = pEntry->uNextItem;
}
}
if(pEntry->uNextItem) { // Gibt es einen vorherigen Eintrag
pTemp = pData->pTreeItems[pEntry->uNextItem];
pTemp->uPrevItem = pEntry->uPrevItem;
} else {
if(pEntry->uParent) { // Neues letztes Kind in Elterneintrag
pTemp = pData->pTreeItems[pEntry->uParent];
pTemp->uLastChild = pEntry->uPrevItem;
if(pTemp->uFirstChild == 0 && pTemp->uLastChild == 0) {
pTemp->bFlags &= ~TVIX_HASBUTTON;
}
} else {
pData->uLastChild = pEntry->uPrevItem;
}
}
for(uPos = 1; uPos < pData->uColumnCount; uPos++) { // Alle Extraeinträge löschen
pList = pData->pExtraItems[uPos - 1];
pExtra = pList[uItem];
if(!pExtra)
continue;
pList[uItem] = NULL;
if(pExtra->pText) {
pExtra->uTextSize = 0;
delete(pExtra->pText);
}
delete(pExtra);
}
pData->pTreeItems[uItem] = NULL; // Den Eintrag löschen
if(pEntry->pText) {
pEntry->uTextSize = 0;
delete(pEntry->pText);
}
if(iMode) { // Den Eintrag neuzeichnen
uItem = pEntry->uPrevItem;
if(!uItem && !pEntry->uNextItem) {
uItem = pEntry->uParent;
if(!uItem)
uPos = 1;
else
uPos = pData->pTreeItems[uItem]->uShowPos;
} else {
uPos = pEntry->uShowPos;
}
if(uPos) {
UpdateItems(pData, uItem);
}
}
if(pEntry->uState & TVIS_SELECTED) // Ausgewählte Einträge runterzählen
if(pData->uSelectedCount > 0) {
pData->uSelectedCount--;
}
delete(pEntry);
pData->uTreeItemsCount--;
iOff = pData->uScrollY; // Prüfe die Scrollposition
iMax = pData->uItemPosCount;
iMax -= pData->uPageEnties - 1;
if(iOff >= iMax)
iOff = iMax;
if(iOff < 0)
iOff = 0;
if(iOff != (int)pData->uScrollY) {
pData->uScrollY = iOff;
SetScrollPos(pData->hWnd, SB_VERT, iOff, TRUE);
UpdateView(pData);
}
return 1;
}
//*****************************************************************************
//*
//* TreeListXorSelectItem
//*
//*****************************************************************************
// Wählt einen Eintrag ab bzw. an
// pData : Zeiger auf die Fensterdaten
// uItem : Ist die Nummer des Eintrages der ausgewählt werden soll
// iMode : Ist der Grund für die Änderung
// TVC_BYKEYBOARD
// TVC_BYMOUSE
// TVC_UNKNOWN
// Ergibt 1 wenn der Eintrag ab/angewählt wurde
// 0 wenn der Eintrag nicht verändert wurde
static int TreeListXorSelectItem(TreeListData *pData, unsigned uItem, int iMode) {
NMTREEVIEW sNotify;
BaseItem *pEntry;
unsigned uOld;
unsigned uRet;
pEntry = pData->pTreeItems[uItem];
if(!pEntry)
return 0;
if(uItem == pData->uSelectedItem)
return 0;
uOld = pEntry->uState;
sNotify.hdr.code = TVN_SELCHANGING;
sNotify.action = iMode;
sNotify.itemNew.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_SUBITEM | TVIF_PARAM;
sNotify.itemNew.hItem = (HTREEITEM)(ULONG_PTR)uItem;
sNotify.itemNew.stateMask = 0xFFFFFFFF;
sNotify.itemNew.state = pEntry->uState;
sNotify.itemNew.lParam = pEntry->lParam;
sNotify.itemNew.cChildren = 0;
sNotify.itemNew.pszText = (LPTSTR) - 1;
sNotify.itemNew.cchTextMax = -1;
sNotify.itemOld.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_SUBITEM | TVIF_PARAM;
sNotify.itemOld.hItem = NULL;
sNotify.itemOld.stateMask = 0xFFFFFFFF;
sNotify.itemOld.state = 0;
sNotify.itemOld.lParam = 0;
sNotify.itemOld.cChildren = 0;
sNotify.itemOld.pszText = (LPTSTR) - 1;
sNotify.itemOld.cchTextMax = -1;
sNotify.ptDrag.x = 0;
sNotify.ptDrag.y = 0;
UNLOCK(pData);
uRet = U(SendNotify(pData, &sNotify.hdr));
LOCK(pData);
if(uRet)
return 0;
pEntry = pData->pTreeItems[uItem];
if(!pEntry)
return 0;
pEntry->uState ^= TVIS_SELECTED;
sNotify.hdr.code = TVN_SELCHANGED;
sNotify.action = iMode;
sNotify.itemNew.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_SUBITEM | TVIF_PARAM;
sNotify.itemNew.hItem = (HTREEITEM)(ULONG_PTR)uItem;
sNotify.itemNew.stateMask = 0xFFFFFFFF;
sNotify.itemNew.state = pEntry->uState;
sNotify.itemNew.lParam = pEntry->lParam;
sNotify.itemNew.cChildren = 0;
sNotify.itemNew.pszText = (LPTSTR) - 1;
sNotify.itemNew.cchTextMax = -1;
sNotify.itemOld.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_SUBITEM | TVIF_PARAM;
sNotify.itemOld.hItem = NULL;
sNotify.itemOld.stateMask = 0xFFFFFFFF;
sNotify.itemOld.state = 0;
sNotify.itemOld.lParam = 0;
sNotify.itemOld.cChildren = 0;
sNotify.itemOld.pszText = (LPTSTR) - 1;
sNotify.itemOld.cchTextMax = -1;
sNotify.ptDrag.x = 0;
sNotify.ptDrag.y = 0;
UNLOCK(pData);
SendNotify(pData, &sNotify.hdr);
LOCK(pData);
pEntry = pData->pTreeItems[uItem];
if(!pEntry)
return 0;
if(pEntry->uShowPos) {
if(pData->uStyleEx & TVS_EX_FULLROWMARK)
UpdateRow(pData, uItem);
else
UpdateRect(pData, uItem, 0);
}
if((uOld ^ pEntry->uState)&TVIS_SELECTED) {
if(pEntry->uState & TVIS_SELECTED)
pData->uSelectedCount++;
else
pData->uSelectedCount--;
}
return 1;
}
//*****************************************************************************
//*
//* TreeListRemoveFocus
//*
//*****************************************************************************
// Wählt den Focus ab
// pData : Zeiger auf die Fensterdaten
static void TreeListRemoveFocus(TreeListData *pData) {
ExtraItem *pExtra;
BaseItem *pEntry;
unsigned uItem;
unsigned uSub;
if(!pData->uFocusItem)
return;
uItem = pData->uFocusItem;
pEntry = pData->pTreeItems[uItem];
if(pEntry) {
pEntry->bFlags &= ~TVIX_FOCUSED;
uSub = pData->uFocusSub;
if(uSub) {
pExtra = pData->pExtraItems[uSub - 1][uItem];
if(pExtra)
pExtra->bFlags &= ~TVIX_FOCUSED;
}
UpdateRect(pData, uItem, uSub);
}
pData->uFocusItem = 0;
pData->uFocusSub = 0;
}
//*****************************************************************************
//*
//* TreeListSetFocus
//*
//*****************************************************************************
// Wählt den Focus-Eintrag
// pData : Zeiger auf die Fensterdaten
// uItem : Eintrag für den Focus (0xFFFFFFFF=keine Änderung)
// uSub : Spalte für den Focus (0xFFFFFFFF=keine Änderung)
// Ergibt 1 wenn der Focus gesetzt wurde, bzw 0 bei einem Fehler
static int TreeListSetFocus(TreeListData *pData, unsigned uItem, unsigned uSub) {
ExtraItem *pExtra;
BaseItem *pEntry;
BaseItem *pTemp;
unsigned uTemp;
unsigned uCol;
if(pData->uFocusItem) {
if(uSub == 0xFFFFFFFF)
uSub = pData->uFocusSub;
if(uItem == 0xFFFFFFFF)
uItem = pData->uFocusItem;
} else {
if(uSub == 0xFFFFFFFF)
uSub = pData->uSelectedSub;
if(uItem == 0xFFFFFFFF)
uItem = pData->uSelectedItem;
}
if(pData->uFocusItem == uItem)
if(pData->uFocusSub == uSub)
return 1;
if(!uItem) { // Focus abwählen
TreeListRemoveFocus(pData);
return 1;
}
if(uItem > pData->uTreeItemsMax) { // Den Eintrag prüfen
return 0;
}
pEntry = pData->pTreeItems[uItem];
if(!pEntry) {
return 0;
}
if(!(pData->uStyleEx & TVS_EX_SUBSELECT))
uSub = 0;
if(!(pData->uStyleEx & TVS_EX_MULTISELECT)) { // Einzel auswahl
return TreeListSelectItem(pData, uItem, uSub, TVC_UNKNOWN);
}
uTemp = pData->uFocusItem;
pTemp = pData->pTreeItems[uTemp];
if(pTemp) { // Den alten Eintrag abwählen
pTemp->bFlags &= ~TVIX_FOCUSED;
uCol = pData->uFocusSub;
if(uCol) {
pExtra = pData->pExtraItems[uCol - 1][uTemp];
if(pExtra)
pExtra->bFlags &= ~TVIX_FOCUSED;
}
UpdateRect(pData, uItem, uSub);
}
if(uSub) { // Neuen Eintrag wählen
pExtra = pData->pExtraItems[uSub - 1][uItem];
if(pExtra)
pExtra->bFlags |= TVIX_FOCUSED;
} else {
pEntry->bFlags |= TVIX_FOCUSED;
}
pData->uFocusItem = uItem;
pData->uFocusSub = uSub;
if(pEntry->uState & TVIS_SELECTED) { // Auch die Auswahl nachziehen
if(pData->uSelectedItem != uItem) {
uTemp = pData->uSelectedItem;
uCol = pData->uSelectedSub;
} else {
uTemp = 0;
uCol = pData->uSelectedSub;
}
pData->uSelectedItem = uItem;
pData->uSelectedSub = uSub;
if(pData->uStyleEx & TVS_EX_FULLROWMARK) {
uCol = uSub + 1;
}
if(uTemp) {
if(uCol != uSub)
UpdateRow(pData, uTemp);
else
UpdateRect(pData, uTemp, uCol);
}
if(uCol != uSub)
UpdateRow(pData, uItem);
else
UpdateRect(pData, uItem, uCol);
} else {
UpdateRect(pData, uItem, uSub);
}
return 1;
}
//*****************************************************************************
//*
//* TreeListSelectItem
//*
//*****************************************************************************
// Wählt einen Eintrag aus
// pData : Zeiger auf die Fensterdaten
// uItem : Ist die Nummer des Eintrages der ausgewählt werden soll
// uSubItem : Ist die Spalte die gewählt werden soll
// iMode : Ist der Grund für die Änderung
// TVC_BYKEYBOARD
// TVC_BYMOUSE
// TVC_UNKNOWN
// TVC_ONLYFOCUS (nur der Focus hat sich verändert)
// TVC_DESELECT (dieses Flag löscht die alte Auswahl)
// TVC_UNSELECT (dieses Flag löscht Auswahl bei MultiSel)
// Ergibt 2 wenn der Eintrag gewählt und umgeklapt wurde
// 1 wenn der Eintrag gewählt wurde
// 0 wenn der Eintrag nicht gewählt wurde
static int TreeListSelectItem(TreeListData *pData, unsigned uItem, unsigned uSubItem, int iMode) {
NMTREEVIEW sNotify;
ExtraItem *pExtra;
BaseItem *pEntry;
BaseItem *pTemp;
LPARAM lParam;
LPARAM lPaOld;
unsigned uState;
unsigned uStOld;
unsigned uNext;
unsigned uPos;
unsigned uOld;
unsigned uSub;
unsigned uRet;
int iDel;
int iSel;
uOld = pData->uSelectedItem;
uSub = pData->uSelectedSub;
if(uSubItem >= pData->uColumnCount && uSubItem > 0)
return 0;
if(uItem > pData->uTreeItemsMax)
return 0;
if(uItem == uOld)
if(uSubItem == uSub)
if(pData->uSelectedCount <= 1 || !(pData->uStyleEx & TVS_EX_MULTISELECT)) {
return 1;
}
if(pData->uStyleEx & TVS_EX_MULTISELECT) { // Ist die Mehrfachauswahl möglich
iSel = iMode & TVC_UNSELECT;
iDel = iMode & TVC_DESELECT;
if(!iDel) {
if(pData->uStyleEx & (TVS_EX_FULLROWMARK | TVS_EX_SUBSELECT))
UpdateRow(pData, uOld);
else
UpdateRect(pData, uOld, uSub);
uOld = 0;
uSub = 0;
} else { // Alle gewählten Einträge abwählen
if(pData->uSelectedCount > 1 && pData->uTreeItemsMax) {
for(uPos = pData->uTreeItemsMax; uPos; uPos--) {
pEntry = pData->pTreeItems[uPos];
if(!pEntry || !(pEntry->uState & TVIS_SELECTED))
continue;
if(TreeListXorSelectItem(pData, uPos, iMode))
if(!pData->uSelectedCount)
break; // Wurden alle Einträge abgewählt
}
}
}
} else { // Altes Select löschen
iMode &= ~TVC_ONLYFOCUS;
iDel = 1;
iSel = 0;
}
iMode &= ~(TVC_DESELECT | TVC_UNSELECT);
pEntry = pData->pTreeItems[uItem];
if(!pEntry) { // Neuen Statatus holen
if(uItem)
return 0;
uState = 0;
lParam = 0;
} else {
uState = pEntry->uState;
lParam = pEntry->lParam;
if(uSubItem) {
uState &= TVIS_BASEFLAGS;
pExtra = pData->pExtraItems[uSubItem - 1][uItem];
if(pExtra)
uState |= pExtra->uState;
}
}
pTemp = pData->pTreeItems[uOld];
if(!pTemp) { // Alten Status holen
uStOld = 0;
lPaOld = 0;
} else {
uStOld = pTemp->uState;
lPaOld = pTemp->lParam;
if(uSub) {
uStOld &= TVIS_BASEFLAGS;
pExtra = pData->pExtraItems[uSub - 1][uOld];
if(pExtra)
uStOld |= pExtra->uState;
}
}
sNotify.hdr.code = TVN_SELCHANGING;
sNotify.action = iMode;
sNotify.itemNew.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_SUBITEM | TVIF_PARAM;
sNotify.itemNew.hItem = (HTREEITEM)(ULONG_PTR)uItem;
sNotify.itemNew.stateMask = 0xFFFFFFFF;
sNotify.itemNew.state = uState;
sNotify.itemNew.lParam = lParam;
sNotify.itemNew.cChildren = uSubItem;
sNotify.itemNew.pszText = (LPTSTR) - 1;
sNotify.itemNew.cchTextMax = -1;
sNotify.itemOld.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_SUBITEM | TVIF_PARAM;
sNotify.itemOld.hItem = (HTREEITEM)(ULONG_PTR)uOld;
sNotify.itemOld.stateMask = 0xFFFFFFFF;
sNotify.itemOld.state = uStOld;
sNotify.itemNew.lParam = lPaOld;
sNotify.itemOld.cChildren = uSub;
sNotify.itemOld.pszText = (LPTSTR) - 1;
sNotify.itemOld.cchTextMax = -1;
sNotify.ptDrag.x = 0;
sNotify.ptDrag.y = 0;
UNLOCK(pData);
if(SendNotify(pData, &sNotify.hdr)) { // Abfragen ob der Eintrag gewählt werden darf
LOCK(pData);
return 0;
}
LOCK(pData);
if(uItem) { // Prüfen ob der Eintrag noch existiert
pEntry = pData->pTreeItems[uItem];
if(!pEntry)
return 0;
}
if(iDel) {
uOld = pData->uSelectedItem;
pTemp = pData->pTreeItems[uOld];
}
if(pTemp) { // Den alten Eintrag abwählen
if(pTemp->uShowPos) { // Den Eintrag neu zeichnen
if((pData->uStyleEx & TVS_EX_FULLROWMARK) || pData->uSelectedSub)
UpdateRow(pData, uOld);
else
UpdateRect(pData, uOld, uSub);
}
if(pTemp->uState & TVIS_SELECTED) {
uStOld &= ~TVIS_SELECTED;
pTemp->uState &= ~TVIS_SELECTED;
if(pData->uSelectedCount > 0) {
pData->uSelectedCount -= 1;
}
}
pData->uSelectedSub = 0;
pData->uSelectedItem = 0;
} else {
uOld = 0;
}
if(uItem) { // Den neuen Eintrag wählen
if(iSel) {
if(pEntry->uState & TVIS_SELECTED) {
uState &= ~TVIS_SELECTED;
pEntry->uState &= ~TVIS_SELECTED;
if(pData->uSelectedCount)
pData->uSelectedCount--;
}
} else {
if(!(pEntry->uState & TVIS_SELECTED)) {
uState |= TVIS_SELECTED;
pEntry->uState |= TVIS_SELECTED;
pData->uSelectedCount += 1;
}
}
if(uSubItem && uSubItem < pData->uColumnCount) {
pExtra = pData->pExtraItems[uSubItem - 1][uItem];
if(!pExtra) {
pExtra = new(ExtraItem, 1);
memset(pExtra, 0, sizeof(ExtraItem));
pExtra->iImage = TV_NOIMAGE;
pExtra->uState = pEntry->uState & (TVIS_BOLD | TVIS_UNDERLINE);
pData->pExtraItems[uSubItem - 1][uItem] = pExtra;
}
uState = pExtra->uState;
uState |= pEntry->uState & TVIS_BASEFLAGS;
} else {
uState = pEntry->uState;
}
if(pEntry->uShowPos) { // Den Eintrag neu zeichnen
if(pData->uStyleEx & (TVS_EX_FULLROWMARK | TVS_EX_SUBSELECT))
UpdateRow(pData, uItem);
else
UpdateRect(pData, uItem, uSubItem);
}
pData->uSelectedSub = uSubItem;
pData->uSelectedItem = uItem;
} else {
pData->uSelectedItem = 0;
pData->uSelectedSub = 0;
uState = 0;
}
sNotify.hdr.code = TVN_SELCHANGED;
sNotify.action = iMode;
sNotify.itemNew.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_SUBITEM | TVIF_PARAM;
sNotify.itemNew.hItem = (HTREEITEM)(ULONG_PTR)uItem;
sNotify.itemNew.stateMask = 0xFFFFFFFF;
sNotify.itemNew.state = uState;
sNotify.itemNew.lParam = lParam;
sNotify.itemNew.cChildren = uSubItem;
sNotify.itemNew.pszText = (LPTSTR) - 1;
sNotify.itemNew.cchTextMax = -1;
sNotify.itemOld.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_SUBITEM | TVIF_PARAM;
sNotify.itemOld.hItem = (HTREEITEM)(ULONG_PTR)uOld;
sNotify.itemOld.stateMask = 0xFFFFFFFF;
sNotify.itemOld.state = uStOld;
sNotify.itemOld.lParam = lPaOld;
sNotify.itemOld.cChildren = uSub;
sNotify.itemOld.pszText = (LPTSTR) - 1;
sNotify.itemOld.cchTextMax = -1;
sNotify.ptDrag.x = 0;
sNotify.ptDrag.y = 0;
UNLOCK(pData);
SendNotify(pData, &sNotify.hdr);
LOCK(pData);
if(!(pData->uStyle & TVS_SINGLEEXPAND)) { // Einzelmodus aktiv
if(pData->uStyle & TVS_SHOWSELALWAYS)
if(pData->uSelectedItem) {
TreeListEnsureVisible(pData, pData->uSelectedItem, pData->uSelectedSub);
}
return 1;
}
//*****************************************************************************
sNotify.hdr.code = TVN_SINGLEEXPAND;
sNotify.action = iMode;
sNotify.itemNew.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE;
sNotify.itemNew.hItem = (HTREEITEM)(ULONG_PTR)uItem;
sNotify.itemNew.stateMask = 0xFFFFFFFF;
sNotify.itemNew.state = (pEntry) ? pEntry->uState : 0;
sNotify.itemNew.cChildren = 0;
sNotify.itemNew.pszText = (LPTSTR) - 1;
sNotify.itemNew.cchTextMax = -1;
sNotify.itemOld.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE;
sNotify.itemOld.hItem = (HTREEITEM)(ULONG_PTR)uOld;
sNotify.itemOld.stateMask = 0xFFFFFFFF;
sNotify.itemOld.state = (pTemp) ? pTemp->uState : 0;
sNotify.itemOld.cChildren = 0;
sNotify.itemOld.pszText = (LPTSTR) - 1;
sNotify.itemOld.cchTextMax = -1;
sNotify.ptDrag.x = 0;
sNotify.ptDrag.y = 0;
UNLOCK(pData);
uRet = U(SendNotify(pData, &sNotify.hdr)); // Anfragen ob die Zweige umgeklappt werden dürfen
LOCK(pData);
pTemp = pData->pTreeItems[uOld ]; // Zeiger neu holen falls es Änderungen gab
pEntry = pData->pTreeItems[uItem];
while(pTemp && pEntry) { // Beide Zweige sysnchronisieren
if(pEntry->uLevel > pTemp->uLevel) {
uNext = pEntry->uParent;
if(!(uRet & TVNRET_SKIPNEW))
if(!(pEntry->uState & TVIS_EXPANDED)) {
TreeListToggleItem(pData, uItem, 0);
}
pEntry = pData->pTreeItems[uNext];
uItem = uNext;
if(!uItem)
break;
continue;
}
if(uItem == uOld)
goto EndSel; // Bis zum gleichen Knoten
uNext = pTemp->uParent;
if(!(uRet & TVNRET_SKIPOLD))
if(pTemp->uState & TVIS_EXPANDED) {
TreeListToggleItem(pData, uOld, 0);
}
pTemp = pData->pTreeItems[uNext];
uOld = uNext;
}
if(!uItem) {
if(!(uRet & TVNRET_SKIPOLD))
while(pTemp) { // Alten Zweig zuklappen
uNext = pTemp->uParent;
if(pTemp->uState & TVIS_EXPANDED) {
TreeListToggleItem(pData, uOld, 0);
}
pTemp = pData->pTreeItems[uNext];
uOld = uNext;
}
goto EndSel;
}
if(!uOld) {
if(!(uRet & TVNRET_SKIPNEW))
while(pEntry) { // Neuen Zweig aufklappen
uNext = pEntry->uParent;
if(!(pEntry->uState & TVIS_EXPANDED)) {
TreeListToggleItem(pData, uItem, 0);
}
pEntry = pData->pTreeItems[uNext];
uItem = uNext;
}
}
EndSel:
if(pData->uStyle & TVS_SHOWSELALWAYS)
if(pData->uSelectedItem) {
TreeListEnsureVisible(pData, pData->uSelectedItem, pData->uSelectedSub);
}
return 2;
}
//*****************************************************************************
//*
//* TreeListSelectChilds
//*
//*****************************************************************************
// Wählt einen Eintrag aus
// pData : Zeiger auf die Fensterdaten
// uItem : Ist die Nummer des Eintrages der ausgewählt werden soll
// iMode : Bit 0 = Untereintäge auch ändern
// Bit 1 = Einträge abwählen
// Ergibt 1 wenn die Auswahl funktioniert hat, bzw. 0 bei einem Fehler
static int TreeListSelectChilds(TreeListData *pData, unsigned uItem, int iMode) {
BaseItem *pEntry;
unsigned uLevel;
unsigned uXor;
if(!(pData->uStyleEx & TVS_EX_MULTISELECT))
return 0;
uLevel = 0;
if(uItem == U(TVI_ROOT)) {
uItem = pData->uFirstChild;
} else {
if(uItem > pData->uTreeItemsMax)
return 0;
}
if(!pData->pTreeItems[uItem]) {
return 0;
}
uXor = (iMode & TVIS_DESELECT) ? 0 : TVIS_SELECTED;
iMode &= TVIS_WITHCHILDS;
for(;;) {
pEntry = pData->pTreeItems[uItem];
if((pEntry->uState ^ uXor)&TVIS_SELECTED) {
TreeListXorSelectItem(pData, uItem, TVC_UNKNOWN);
}
if(iMode && pEntry->uFirstChild) { // Auch Kinder ändern
uItem = pEntry->uFirstChild;
uLevel++;
continue;
}
for(;;) { // Eine Ebene höher
uItem = pEntry->uNextItem;
if(uItem != 0)
break;
if(uLevel == 0)
return 1;
uLevel--;
uItem = pEntry->uParent;
pEntry = pData->pTreeItems[uItem];
}
}
}
//*****************************************************************************
//*
//* TreeListInsertItem
//*
//*****************************************************************************
// Fügt einen Eintrag ins Fenster ein
// pData : Zeiger auf die Fensterdaten
// pInsert : Zeiger auf die ein zu fügenden Daten
// Ergibt die Einfügeposition des neuen Eintrages oder 0 bei einem Fehler
static unsigned TreeListInsertItem(TreeListData *pData, TV_INSERTSTRUCT *pInsert) {
char *pTemp;
BYTE bFlag;
LPCTSTR pText;
LPCTSTR pTextTemp;
PFNTVSORTEX pCompare;
ExtraItem **pExOld[MAX_COLUMNS];
ExtraItem **pExNew[MAX_COLUMNS];
BaseItem *pNew;
BaseItem **pOld;
BaseItem **pItems;
BaseItem *pEntry;
BaseItem *pParent;
unsigned *pPosNew;
unsigned *pPosOld;
unsigned *pFirst;
unsigned *pLast;
unsigned uBefore;
unsigned uParent;
unsigned uAfter;
unsigned uFirst;
unsigned uSize;
unsigned uBits;
unsigned uItem;
unsigned uNext;
unsigned uMax;
unsigned uPos;
unsigned uNum;
int iCmp;
int iNone;
int iCount;
int iShift;
if(pData->cLockChanges)
return 0;
uParent = U(pInsert->hParent);
if(uParent > pData->uTreeItemsMax) { // Prüfe das Elternelement
if(pInsert->hParent != TVI_ROOT) {
return 0;
}
pParent = NULL;
} else {
pParent = pData->pTreeItems[uParent];
if(!pParent) {
if(uParent)
return 0;
pParent = NULL;
}
}
if(pData->uTreeItemsCount + 1 > pData->uTreeItemsMax) { // Größe der Liste erhöhen
pPosOld = pData->pItemPos;
pOld = pData->pTreeItems;
uMax = pData->uTreeItemsMax;
uMax += pData->uTreeItemsMax / 2;
uMax += 64;
pItems = new(BaseItem*, uMax + 1);
if(!pItems) {
return 0;
}
pPosNew = new(unsigned, uMax);
if(!pPosNew) {
delete(pItems);
return 0;
}
for(uPos = 1; uPos < pData->uColumnCount; uPos++) {
pExOld[uPos] = pData->pExtraItems[uPos - 1];
pExNew[uPos] = new(ExtraItem*, uMax + 1);
if(!pExNew[uPos]) {
for(uPos--; uPos > 0; uPos--)
delete(pExNew[uPos]);
delete(pPosNew);
delete(pItems);
return 0;
}
}
memcpy(pItems , pData->pTreeItems , sizeof(BaseItem *) * (pData->uTreeItemsMax + 1));
memset(pItems + pData->uTreeItemsMax + 1, 0, sizeof(BaseItem *) * (uMax - pData->uTreeItemsMax));
memcpy(pPosNew, pData->pItemPos , sizeof(unsigned) * (pData->uTreeItemsCount));
memset(pPosNew + pData->uTreeItemsCount, 0, sizeof(unsigned) * (uMax - pData->uTreeItemsCount));
for(uPos = 1; uPos < pData->uColumnCount; uPos++) {
memcpy(pExNew[uPos], pExOld[uPos] , sizeof(ExtraItem *) * (pData->uTreeItemsMax + 1));
memset(pExNew[uPos] + pData->uTreeItemsMax + 1, 0, sizeof(ExtraItem *) * (uMax - pData->uTreeItemsMax));
pData->pExtraItems[uPos - 1] = pExNew[uPos];
delete(pExOld[uPos]);
}
pData->uTreeItemsMax = uMax;
pData->pTreeItems = pItems;
pData->pItemPos = pPosNew;
delete(pPosOld);
delete(pOld);
}
//******************** Den neuen Eintrag erzeugen *****************************
pItems = pData->pTreeItems;
uPos = pData->uNextSeachPos + 1;
pTemp = new(char, sizeof(BaseItem) + pData->uUserDataSize);
pNew = (BaseItem *)pTemp;
if(!pNew) { // Konnte der Speicher reserviert werden
return 0;
}
if(pData->uUserDataSize) { // Die Userdaten auf 0 setzen
memset(pTemp + sizeof(BaseItem), 0, pData->uUserDataSize);
}
for(;; uPos++) { // Suche freie Position
if(uPos > pData->uTreeItemsMax)
uPos = 1;
if(pItems[uPos] == NULL)
break;
}
pData->uNextSeachPos = uPos;
memset(pNew, 0, sizeof(BaseItem)); // Erstelle den neuen Eintrag
pNew->iImage = TV_NOIMAGE;
pNew->iSelectedImage = TV_NOIMAGE;
uBits = pInsert->item.mask;
if(uBits & TVIF_STATE) {
pNew->uState = pInsert->item.state & pInsert->item.stateMask;
} else {
if(pData->uStyle & TVS_CHECKBOXES)
if(!(pData->uStyleEx & TVS_EX_BITCHECKBOX)) {
pNew->uState = 0x1000;
}
}
if(uBits & TVIF_PARAM) {
pNew->lParam = pInsert->item.lParam;
}
if(uBits & TVIF_IMAGE) {
pNew->iImage = pInsert->item.iImage;
if(pNew->iImage == I_IMAGECALLBACK)
pNew->bCallback |= TVIF_IMAGE;
}
if(uBits & TVIF_SELECTEDIMAGE) {
pNew->iSelectedImage = pInsert->item.iSelectedImage;
if(pNew->iSelectedImage == I_IMAGECALLBACK)
pNew->bCallback |= TVIF_SELECTEDIMAGE;
}
if(uBits & TVIF_CHILDREN) { // Art der Schaltflächen
switch(pInsert->item.cChildren) {
case 0:
break;
case 1:
pNew->bFlags |= TVIX_HASBUTTON;
break;
case I_CCB:
pNew->bCallback |= TVIF_CHILDREN;
break;
default
:
pNew->bFlags |= TVIX_VARBUTTON;
break;
}
} else {
pNew->bFlags |= TVIX_VARBUTTON;
}
if(pData->uStyle & TVS_SINGLEEXPAND) { // Nicht aufklappen bei Einzelmodus
pNew->uState &= ~TVIS_EXPANDED;
}
if(uBits & TVIF_TEXT) { // Text einfügen
if(pInsert->item.pszText == LPSTR_TEXTCALLBACK) {
pNew->bCallback |= TVIF_TEXT;
pNew->uTextSize = 0;
pNew->pText = 0;
} else {
pNew->uTextSize = (WORD)str_len(pInsert->item.pszText);
pNew->pText = new(TCHAR, pNew->uTextSize + 1);
memcpy(pNew->pText, pInsert->item.pszText, sizeof(TCHAR) * (pNew->uTextSize + 1));
}
} else {
pNew->pText = new(TCHAR, 1);
pNew->pText[0] = 0;
pNew->uTextSize = 0;
}
if(!pParent) { // Einen Root-Eintrag einfügen
pNew->uParent = 0;
uParent = 0;
bFlag = 0;
uFirst = 0xFFFFFFFF;
pFirst = &pData->uFirstChild;
pLast = &pData->uLastChild;
} else { // Einen Tree-Eintrag einfügen
pNew->uParent = uParent;
pNew->uLevel = pParent->uLevel + 1;
uFirst = pParent->uFirstChild;
pFirst = &pParent->uFirstChild;
pLast = &pParent->uLastChild;
bFlag = pParent->bFlags;
if(pParent->bFlags & TVIX_VARBUTTON) {
pParent->bFlags |= TVIX_HASBUTTON;
}
}
//******************** Eintrage einfügen **************************************
uAfter = U(pInsert->hInsertAfter);
switch(uAfter) {
case U(TVI_BEFORE): // Nach einem Eintrag einfügen
if(pParent) { // Einen Root-Eintrag einfügen
pEntry = pParent;
pParent ->bFlags = bFlag;
uParent = pParent->uParent;
pParent = pItems [uParent];
if(!pParent) {
pNew->uParent = 0;
pNew->uLevel = 0;
uParent = 0;
uFirst = 0xFFFFFFFF;
pFirst = &pData->uFirstChild;
pLast = &pData->uLastChild;
} else { // Einen Tree-Eintrag einfügen
pNew->uParent = uParent;
pNew->uLevel = pParent->uLevel + 1;
uFirst = pParent->uFirstChild;
pFirst = &pParent->uFirstChild;
pLast = &pParent->uLastChild;
if(pParent->bFlags & TVIX_VARBUTTON) {
pParent->bFlags |= TVIX_HASBUTTON;
}
}
if(pEntry->uPrevItem) {
uAfter = pEntry->uPrevItem;
goto DoInsert;
}
}
case U(TVI_FIRST): // Am Anfang einfügen
if(pFirst[0]) { // Gibt es schon Einträge
pEntry = pItems[pFirst[0]];
pEntry->uPrevItem = uPos;
} else {
pFirst[0] = uPos;
pLast [0] = uPos;
break;
}
pNew ->uNextItem = pFirst[0]; // Eintrag einfügen
pFirst[0] = uPos;
break;
case U(TVI_ROOT): // Als Root-Eintrag einfügen
pNew->uParent = 0;
uParent = 0;
pFirst = &pData->uFirstChild;
pLast = &pData->uLastChild;
case U(TVI_LAST): // Am Ende einfügen
if(pLast[0]) { // Gibt es schon Einträge
pEntry = pItems[pLast[0]];
pEntry->uNextItem = uPos;
} else {
pFirst[0] = uPos;
pLast [0] = uPos;
break;
}
pNew ->uPrevItem = pLast[0]; // Eintrag einfügen
pLast[0] = uPos;
break;
case U(TVI_SORTEX): // Einfügen mittels Funktion
uItem = pFirst[0];
if(!uItem) { // Gibt es keine Kindeinträge
pFirst[0] = uPos;
pLast [0] = uPos;
break;
}
if(pNew->bCallback & TVIF_TEXT) { // Text über Callback holen
uSize = 1;
LOCK(pData);
CallbackEntry(pData, pNew, uPos, TVIF_TEXT, &iNone, &uSize, &pText);
UNLOCK(pData);
} else {
pText = pNew->pText;
}
pData->cLockChanges = 1;
pCompare = (PFNTVSORTEX)(pInsert->item.hItem);
if(!pCompare)
break;
uNext = uItem;
iCount = 0;
uBefore = 0;
while(uNext) { // Zähle die Einträge
iCount++;
uNext = pItems[uNext]->uNextItem;
}
while(iCount > 0) { // Binary-Seach Algorithnus
iShift = iCount / 2;
uNext = uItem;
while(iShift > 0) {
uNext = pItems[uNext]->uNextItem;
iShift--;
}
pEntry = pItems[uNext];
if(pEntry->bCallback & TVIF_TEXT) { // Text über Callback holen
uSize = 0;
LOCK(pData);
CallbackEntry(pData, pEntry, uItem, TVIF_TEXT, &iNone, &uSize, &pTextTemp);
UNLOCK(pData);
} else {
pTextTemp = pEntry->pText;
}
iCmp = pCompare(pData->hWnd, (HTREEITEM)(ULONG_PTR)uNext, pTextTemp, pText, pEntry->lParam, pInsert->item.lParam);
if(iCmp < 0) {
iCount -= (iCount + 1) / 2;
continue;
}
if(iCmp > 0) {
iCount -= iCount / 2 + 1;
uBefore = uNext;
uItem = pItems[uNext]->uNextItem;
continue;
}
uBefore = pEntry->uPrevItem;
uItem = uNext;
break;
}
pData->cLockChanges = 0;
pNew->uNextItem = uItem;
pNew->uPrevItem = uBefore;
if(uBefore) { // Vorherigen Eintrag anpassen
pEntry = pItems[uBefore];
pEntry->uNextItem = uPos;
} else { // Am Anfang einfügen
pFirst[0] = uPos;
}
if(uItem) { // Nächsten Eintrag anpassen
pEntry = pItems[uItem];
pEntry->uPrevItem = uPos;
} else { // Am Ende anhängen
pLast[0] = uPos;
}
break;
case U(TVI_SORT): // Alphapetisch einfügen
uItem = pFirst[0];
if(!uItem) { // Gibt es keine Kindeinträge
pFirst[0] = uPos;
pLast [0] = uPos;
break;
}
if(pNew->bCallback & TVIF_TEXT) { // Text über Callback holen
uSize = 1;
LOCK(pData);
CallbackEntry(pData, pNew, uPos, TVIF_TEXT, &iNone, &uSize, &pText);
UNLOCK(pData);
} else {
pText = pNew->pText;
}
pData->cLockChanges = 1;
uNext = uItem;
iCount = 0;
uBefore = 0;
while(uNext) { // Zähle die Einträge
iCount++;
uNext = pItems[uNext]->uNextItem;
}
while(iCount > 0) { // Binary-Seach Algorithnus
iShift = iCount / 2;
uNext = uItem;
while(iShift > 0) {
uNext = pItems[uNext]->uNextItem;
iShift--;
}
pEntry = pItems[uNext];
if(pEntry->bCallback & TVIF_TEXT) { // Text über Callback holen
uSize = 0;
LOCK(pData);
CallbackEntry(pData, pEntry, uItem, TVIF_TEXT, &iNone, &uSize, &pTextTemp);
UNLOCK(pData);
} else {
pTextTemp = pEntry->pText;
}
iCmp = str_icmp(pText, pTextTemp);
if(iCmp < 0) {
iCount -= (iCount + 1) / 2;
continue;
}
if(iCmp > 0) {
iCount -= iCount / 2 + 1;
uBefore = uNext;
uItem = pItems[uNext]->uNextItem;
continue;
}
uBefore = pEntry->uPrevItem;
uItem = uNext;
break;
}
pData->cLockChanges = 0;
pNew->uNextItem = uItem;
pNew->uPrevItem = uBefore;
if(uBefore) { // Vorherigen Eintrag anpassen
pEntry = pItems[uBefore];
pEntry->uNextItem = uPos;
} else { // Am Anfang einfügen
pFirst[0] = uPos;
}
if(uItem) { // Nächsten Eintrag anpassen
pEntry = pItems[uItem];
pEntry->uPrevItem = uPos;
} else { // Am Ende anhängen
pLast[0] = uPos;
}
break;
case U(TVI_AFTER): // Nach einem Eintrag einfügen
uAfter = uParent;
if(pParent) { // Einen Root-Eintrag einfügen
pParent ->bFlags = bFlag;
uParent = pParent->uParent;
pParent = pItems [uParent];
if(!pParent) {
pNew->uParent = 0;
pNew->uLevel = 0;
uParent = 0;
uFirst = 0xFFFFFFFF;
pFirst = &pData->uFirstChild;
pLast = &pData->uLastChild;
} else { // Einen Tree-Eintrag einfügen
pNew->uParent = uParent;
pNew->uLevel = pParent->uLevel + 1;
uFirst = pParent->uFirstChild;
pFirst = &pParent->uFirstChild;
pLast = &pParent->uLastChild;
if(pParent->bFlags & TVIX_VARBUTTON) {
pParent->bFlags |= TVIX_HASBUTTON;
}
}
}
default
: // Hinter einen Eintrag einfügen
DoInsert:
uItem = pFirst[0];
if(!uItem) { // Gibt es keine Kindeinträge
pFirst[0] = uPos;
pLast [0] = uPos;
break;
}
if(uAfter > pData->uTreeItemsMax) {
if((uAfter & 0xFFF00000) == 0xFFE00000) { // In einer genauen Reihe nach Patent einfügen
uAfter &= 0xFFFFF;
uItem = pFirst[0];
if(!uItem) { // Gibt es keine Kindeinträge
pFirst[0] = uPos;
pLast [0] = uPos;
break;
}
if(uAfter == 0) { // In die erste Reihe einfügen
pEntry = pItems[uItem];
pEntry->uPrevItem = uPos;
pNew ->uNextItem = uItem;
pFirst[0] = uPos;
break;
}
uNum = 1;
uBefore = 0;
// Suche Einfügereihe
for(; uItem; uItem = pItems[uItem]->uNextItem) {
uBefore = uItem;
if(uNum == uAfter) {
uItem = pItems[uItem]->uNextItem;
break;
}
uNum++;
}
pNew->uNextItem = uItem;
pNew->uPrevItem = uBefore;
if(uBefore) { // Vorherigen Eintrag anpassen
pEntry = pItems[uBefore];
pEntry->uNextItem = uPos;
} else { // Am Anfang einfügen
pFirst[0] = uPos;
}
if(uItem) { // Nächsten Eintrag anpassen
pEntry = pItems[uItem];
pEntry->uPrevItem = uPos;
} else { // Am Ende anhängen
pLast[0] = uPos;
}
break;
}
pEntry = NULL;
} else {
pEntry = pItems[uAfter];
}
if(pEntry && uParent == pEntry->uParent) { // Stimmt der Elterneintrag ?
uItem = pEntry->uNextItem;
uBefore = uAfter;
} else {
uItem = 0;
uBefore = pLast[0];
pEntry = pItems[uBefore];
}
pNew->uNextItem = uItem;
pNew->uPrevItem = uBefore;
if(uBefore) { // Vorherigen Eintrag anpassen
pEntry->uNextItem = uPos;
} else { // Am Anfang einfügen
pFirst[0] = uPos;
}
if(uItem) { // Nächsten Eintrag anpassen
pEntry = pItems[uItem];
pEntry->uPrevItem = uPos;
} else { // Am Ende anhängen
pLast[0] = uPos;
}
break;
}
pItems[uPos] = pNew;
pData->uTreeItemsCount++;
// Die Anzeigezeilen akualisieren
if(!pParent || !uFirst || (pParent->uState & TVIS_EXPANDED)) {
uItem = pNew->uPrevItem;
if(!uItem)
uItem = uParent;
if(!uItem)
UpdateItems(pData, 0);
else {
pEntry = pItems[uItem];
if(pEntry && pEntry->uShowPos)
UpdateItems(pData, uItem);
}
}
if(pNew->uState & TVIS_SELECTED) { // Den ausgewählten Eintrag auswählen
TreeListSelectItem(pData, uPos, 0, TVC_UNKNOWN);
}
return uPos;
}
//*****************************************************************************
//*
//* TreeListSetItem
//*
//*****************************************************************************
// Ändert einen Eintrag im Fenster
// pData : Zeiger auf die Fensterdaten
// pItem : Zeiger auf die ein zu ändernden Daten
// Ergibt 1 wenn ok oder 0 bei einem Fehler
static int TreeListSetItem(TreeListData *pData, const TV_ITEM *pItem) {
BYTE bCall;
BYTE bFlags;
ExtraItem **pList;
ExtraItem *pExtra;
BaseItem *pEntry;
unsigned uChange;
unsigned uMask;
unsigned uBits;
unsigned uItem;
unsigned uSub;
unsigned uLen;
int iVal;
int iRet;
uChange = 0;
uItem = U(pItem->hItem);
if(uItem > pData->uTreeItemsMax)
return 0;
pEntry = pData->pTreeItems[uItem];
if(!pEntry)
return 0;
uBits = pItem->mask;
if(uBits & TVIF_SUBITEM) { // Einen Extraeintrag ändern
uSub = pItem->cChildren;
if(uSub > 0) {
if(uSub >= pData->uColumnCount)
return 0;
pList = pData->pExtraItems[uSub - 1];
pExtra = pList[uItem];
if(!pExtra) { // Einen neuen Eintrag erzeugen
pExtra = new(ExtraItem, 1);
memset(pExtra, 0, sizeof(ExtraItem));
pExtra->iImage = TV_NOIMAGE;
pExtra->uState = pEntry->uState & (TVIS_BOLD | TVIS_UNDERLINE);
pList[uItem] = pExtra;
}
if(uBits & TVIF_PARAM) {
pEntry->lParam = pItem->lParam;
}
if((uBits & TVIF_IMAGE) && pExtra->iImage != pItem->iImage) {
if(pData->hImages)
uChange = 1;
pExtra->iImage = pItem->iImage;
if(pExtra->iImage == I_IMAGECALLBACK)
pExtra->bCallback |= TVIF_IMAGE;
else
pExtra->bCallback &= TVIF_IMAGE;
}
if(uBits & TVIF_TEXT) { // Einen neuen Text einstellen
if(pItem->pszText == LPSTR_TEXTCALLBACK) {
if(pExtra->pText)
delete(pExtra->pText);
pExtra->bCallback |= TVIF_TEXT;
pExtra->uTextSize = 0;
pExtra->pText = 0;
uChange = 1;
} else {
uLen = str_len(pItem->pszText);
if(uLen > pExtra->uTextSize || !pExtra->pText) {
if(pExtra->pText)
delete(pExtra->pText);
pExtra->pText = new(TCHAR, uLen + 1);
}
memcpy(pExtra->pText, pItem->pszText, (uLen + 1)*sizeof(TCHAR));
pExtra->bCallback &= ~TVIF_TEXT;
pExtra->uTextSize = (WORD)uLen;
pExtra->iTextPixels = 0;
uChange = 1;
}
}
if(uBits & TVIF_STATE) { // Den Status ändern
uMask = pItem->stateMask&~TVIS_BASEFLAGS;
uBits = uMask & (pExtra->uState ^ pItem->state);
uBits |= (pItem->stateMask & TVIS_BASEFLAGS) & (pEntry->uState ^ pItem->state);
pExtra->uState &= ~uMask;
pExtra->uState |= uMask & pItem->state;
if((uBits & (TVIS_OVERLAYMASK | TVIS_CUT)) && (pData->hImages || pData->aColumn[uSub].bEdit >= TVAX_CHECK)) {
uChange = 1; // Ein Icon hats sich verändert
}
if(uBits & (TVIS_BOLD | TVIS_DROPHILITED)) {
pExtra->iTextPixels = 0;
uChange = 1;
}
if((uBits & TVIS_EXPANDED) && pEntry->uFirstChild) {
iVal = TreeListToggleItem(pData, uItem, 0);
if(iVal < 0)
return 0;
pEntry = pData->pTreeItems[uItem];
if(!pEntry)
return 0;
}
if(uBits & TVIS_SELECTED) { // Hat sich die Auswahl geändert
iVal = (pData->uStyleEx & TVS_EX_SUBSELECT) ? uSub : 0;
if(pItem->state & TVIS_SELECTED) {
iRet = TreeListSelectItem(pData, uItem, iVal, TVC_UNKNOWN);
} else
if(pData->uStyleEx & TVS_EX_MULTISELECT) {
TreeListSelectItem(pData, 0 , 0, TVC_UNKNOWN);
iRet = TreeListXorSelectItem(pData, uItem, TVC_UNKNOWN);
} else {
iRet = TreeListSelectItem(pData, 0, 0, TVC_UNKNOWN);
}
pEntry = pData->pTreeItems[uItem];
if(!pEntry)
return 0;
if(iRet >= 2) {
pList = pData->pExtraItems[uSub - 1];
pExtra = pList[uItem];
if(!pExtra)
return 0;
} else
if(iRet == 1) {
uChange = 1;
}
}
}
if(!uChange || !pEntry->uShowPos)
return 1; // Neuzeichnen des Eintrages
UpdateRect(pData, uItem, uSub);
return 1;
}
uBits &= ~TVIF_CHILDREN;
}
//******************** Einen Basis Eintrag ändern *****************************
if(uBits & TVIF_PARAM) {
pEntry->lParam = pItem->lParam;
}
if((uBits & TVIF_IMAGE) && pEntry->iImage != pItem->iImage) {
pEntry->iImage = pItem->iImage;
if(!(pEntry->uState & TVIS_SELECTED) && pData->hImages)
uChange = 1;
if(pEntry->iImage == I_IMAGECALLBACK)
pEntry->bCallback |= TVIF_IMAGE;
else
pEntry->bCallback &= TVIF_IMAGE;
}
if((uBits & TVIF_SELECTEDIMAGE) && pEntry->iSelectedImage != pItem->iSelectedImage) {
pEntry->iSelectedImage = pItem->iSelectedImage;
if((pEntry->uState & TVIS_SELECTED) && pData->hImages)
uChange = 1;
if(pEntry->iSelectedImage == I_IMAGECALLBACK)
pEntry->bCallback |= TVIF_SELECTEDIMAGE;
else
pEntry->bCallback &= TVIF_SELECTEDIMAGE;
}
if(uBits & TVIF_CHILDREN) {
bCall = pEntry->bCallback;
bFlags = pEntry->bFlags;
switch(pItem->cChildren) {
case 0:
pEntry->bCallback &= ~TVIF_CHILDREN;
pEntry->bFlags &= ~TVIX_HASBUTTON;
pEntry->bFlags |= TVIX_VARBUTTON;
break;
case 1:
pEntry->bCallback &= ~TVIF_CHILDREN;
pEntry->bFlags &= TVIX_VARBUTTON;
pEntry->bFlags |= TVIX_HASBUTTON;
break;
case I_CCB:
pEntry->bCallback |= TVIF_CHILDREN;
pEntry->bFlags &= ~TVIX_VARBUTTON;
break;
default
:
pEntry->bCallback &= ~TVIF_CHILDREN;
pEntry->bFlags |= TVIX_VARBUTTON;
if(pEntry->uFirstChild)
pEntry->bFlags |= TVIX_HASBUTTON;
else
pEntry->bFlags &= ~TVIX_HASBUTTON;
}
if(bCall != pEntry->bCallback || bFlags != pEntry->bFlags) {
uChange = 1;
}
}
if(uBits & TVIF_TEXT) { // Einen neuen Text einstellen
if(pItem->pszText == LPSTR_TEXTCALLBACK) {
if(pEntry->pText)
delete(pEntry->pText);
pEntry->bCallback |= TVIF_TEXT;
pEntry->uTextSize = 0;
pEntry->pText = 0;
uChange = 1;
} else {
uLen = str_len(pItem->pszText);
if(uLen > pEntry->uTextSize) {
if(pEntry->pText)
delete(pEntry->pText);
pEntry->pText = new(TCHAR, uLen + 1);
}
memcpy(pEntry->pText, pItem->pszText, (uLen + 1)*sizeof(TCHAR));
pEntry->bCallback &= ~TVIF_TEXT;
pEntry->uTextSize = (WORD)uLen;
pEntry->iTextPixels = 0;
uChange = 1;
}
}
if(uBits & TVIF_STATE) {
uMask = pItem->stateMask;
if(pData->uStyle & TVS_SINGLEEXPAND) { // Nicht aufklappen bei Einzelmodus
uMask &= ~TVIS_EXPANDED;
}
uBits = uMask & (pEntry->uState ^ pItem->state);
pEntry->uState &= ~uMask;
pEntry->uState |= uMask & pItem->state;
if((uBits & (TVIS_OVERLAYMASK | TVIS_CUT)) && pData->hImages) {
uChange = 1;
}
if(uBits & TVIS_STATEIMAGEMASK) { // Haben sich die State-Bits verändert
if(pData->hStates) {
uChange = 1;
}
if(pData->uStyleEx & TVS_EX_BITCHECKBOX) {
if(pEntry->uState & 0x1000) {
pData->uSingleSel = uItem;
} else
if(pData->uSingleSel == uItem) {
pData->uSingleSel = 0;
}
} else {
if((pEntry->uState & TVIS_STATEIMAGEMASK) == 0x2000) {
pData->uSingleSel = uItem;
} else
if(pData->uSingleSel == uItem) {
pData->uSingleSel = 0;
}
}
}
if(uBits & (TVIS_BOLD | TVIS_DROPHILITED)) {
pEntry->iTextPixels = 0;
uChange = 1;
}
if(uBits & TVIS_SELECTED) { // Hat sich die Auswahl geändert
pEntry->uState ^= TVIS_SELECTED;
if(pItem->state & TVIS_SELECTED) {
iRet = TreeListSelectItem(pData, uItem, 0, TVC_UNKNOWN);
} else
if(pData->uStyleEx & TVS_EX_MULTISELECT) {
TreeListSelectItem(pData, 0 , 0, TVC_UNKNOWN);
iRet = TreeListXorSelectItem(pData, uItem, TVC_UNKNOWN);
} else {
iRet = TreeListSelectItem(pData, 0, 0, TVC_UNKNOWN);
}
pEntry = pData->pTreeItems[uItem];
if(!pEntry)
return 0;
if(iRet == 1) {
uChange = 1;
}
}
if((uBits & TVIS_EXPANDED) && pEntry->uFirstChild) { // Sollen Teile auf/zugeklappt werden
uMask &= TVIS_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)(ULONG_PTR)pData->uInsertMark;
sSet.cChildren = uCol;
TreeListSetItem(pData, &sSet);
pExtra = pData->pExtraItems[uCol - 1][pData->uInsertMark];
if(pExtra) {
pExtra->uColorBk = pData->uColors[TVC_INSERT];
pExtra->bFlags |= TVIX_BKCOLOR;
}
}
if(pData->uStyleEx & TVS_EX_HEADERCHGNOTIFY) { // Geänderte Spalten melden
sNotify.hdr.code = TVN_COLUMNCHANGED;
sNotify.uColumn = uCol;
sNotify.uIndex = pData->aColumn[uCol].bIndex;
sNotify.uPosX = pData->aColumnXpos[uCol];
sNotify.iSize = pData->aColumn[uCol].sReal;
UNLOCK(pData);
SendNotify(pData, &sNotify.hdr);
LOCK(pData);
}
return uCol;
}
//*****************************************************************************
//*
//* TreeListScanColumn
//*
//*****************************************************************************
// Berechnet die Breite der sichtbaren Einträge einer Spalte
// pData : Zeiger auf die Fensterdaten
// uSub : Ist die Spalte
// Ergibt sie gescannte Breite
static int TreeListScanColumn(TreeListData *pData, unsigned uSub) {
BaseItem **pList;
BaseItem *pEntry;
ExtraItem *pExtra;
ExtraItem **pItems;
unsigned *pPList;
unsigned uPos;
int iMax;
int iPos;
if(uSub >= pData->uColumnCount)
return 0;
if(uSub > 0) { // Extraspalte
pItems = pData->pExtraItems[uSub - 1];
pPList = pData->pItemPos;
iMax = 0;
for(uPos = 0; uPos < pData->uItemPosCount; uPos++) {
pExtra = pItems[pPList[uPos]];
if(!pExtra) {
if(iMax < 8)
iMax = 8;
continue;
}
if(pData->hSubImg && (pExtra->bFlags & TVIX_HASIMAGE))
iPos = pData->iSubImgXsize;
else
iPos = 0;
iPos += pExtra->iTextPixels + 8;
if(iPos > iMax)
iMax = iPos;
}
return iMax;
}
pList = pData->pTreeItems;
pPList = pData->pItemPos;
iMax = 0;
for(uPos = 0; uPos < pData->uItemPosCount; uPos++) { // Erste Spalte
pEntry = pList[pPList[uPos]];
if(pEntry->bFlags & TVIX_HASIMAGE)
iPos = pData->iImagesXsize;
else
iPos = 0;
iPos += pEntry->uLevel * pData->iIndent;
iPos += pEntry->iTextPixels + 8;
if(iPos > iMax)
iMax = iPos;
}
if(pData->uStyleEx & TVS_EX_ITEMLINES) {
iMax += 1;
}
if(pData->cHasRootRow) {
iMax += pData->iIndent;
}
if(pData->hStates) {
iMax += pData->iStatesXsize;
}
return iMax;
}
//*****************************************************************************
//*
//* TreeListHitTest
//*
//*****************************************************************************
// Prüft wo eine Koordinate im Fenster ist
// pData : Zeiger auf die Fensterdaten
// pInfo : Zeiger auf die ein zu fügenden Daten
// Ergibt das Item auf dem die Koordinate zeigt
static unsigned TreeListHitTest(TreeListData *pData, TV_HITTESTINFO *pInfo) {
BaseItem *pEntry;
ExtraItem *pExtra;
unsigned uItem;
unsigned uNext;
unsigned uSub;
unsigned uCol;
int iXpos;
int iYpos;
int iZpos;
int iWidth;
int iIcon;
iXpos = pInfo->pt.x;
iYpos = pInfo->pt.y;
if((unsigned)iXpos >= pData->uSizeX) {
pInfo->hItem = NULL;
pInfo->flags = (iXpos < 0) ? TVHT_TOLEFT : TVHT_TORIGHT;
return 0;
}
iYpos -= pData->uStartPixel;
if((unsigned)iYpos >= pData->uSizeY) {
pInfo->hItem = NULL;
pInfo->flags = (iYpos < 0) ? TVHT_ABOVE : TVHT_BELOW;
return 0;
}
iZpos = iYpos / pData->iRowHeight;
iZpos += pData->uScrollY;
if((unsigned)iZpos >= pData->uItemPosCount) {
pInfo->hItem = NULL;
pInfo->flags = TVHT_NOWHERE;
return 0;
}
iXpos += pData->uScrollX;
uItem = pData->pItemPos [iZpos];
pEntry = pData->pTreeItems[uItem];
pInfo->hItem = (HTREEITEM)(ULONG_PTR)uItem;
if(!pEntry)
return 0;
uSub = pData->aColumnPos[1];
if(iXpos >= pData->aColumnXpos[uSub]) { // Auf Extraeintrag
for(uCol = 1; uCol < pData->uColumnCount; uCol++) {
uSub = pData->aColumnPos[uCol ];
uNext = pData->aColumnPos[uCol + 1];
if(iXpos >= pData->aColumnXpos[uNext])
continue;
iXpos -= pData->aColumnXpos[uSub ];
pExtra = pData->pExtraItems[uSub - 1][uItem];
if(pData->aColumn[uSub].bEdit >= TVAX_CHECK) { // Hat der Extraeintrag ein Icon
iIcon = pData->iChecksXsize;
} else
if(pExtra && (pExtra->bFlags & TVIX_HASIMAGE)) {
iIcon = pData->iImagesXsize;
} else {
iIcon = 0;
}
pInfo->flags = uSub << 24;
if(iXpos < iIcon) { // Auf Icon
pInfo->flags |= TVHT_ONSUBICON;
return uItem;
}
if(!pExtra || !pExtra->uTextSize) { // Auf Text wenn leerer Eintrag
pInfo->flags |= TVHT_ONSUBLABEL;
return uItem;
}
switch(pData->aColumn[uSub].bAlign) { // Textausrichtung
default
:
if(iXpos - iIcon < pExtra->iTextPixels + 5) {
pInfo->flags |= TVHT_ONSUBLABEL;
return uItem;
}
break;
case DT_RIGHT:
iWidth = pData->aColumnXpos[uNext];
iWidth -= pData->aColumnXpos[uSub ];
if(iXpos >= iWidth - pExtra->iTextPixels - 5) {
pInfo->flags |= TVHT_ONSUBLABEL;
return uItem;
}
break;
case DT_CENTER:
iWidth = pData->aColumnXpos[uNext];
iWidth -= pData->aColumnXpos[uSub ];
iWidth += iIcon;
iWidth /= 2;
if(iXpos >= iWidth - pExtra->iTextPixels / 2 - 3)
if(iXpos <= iWidth + pExtra->iTextPixels / 2 + 3) {
pInfo->flags |= TVHT_ONSUBLABEL;
return uItem;
}
break;
}
pInfo->flags |= TVHT_ONSUBRIGHT;
return uItem;
}
pInfo->flags = TVHT_ONRIGHTSPACE;
return uItem;
}
if(!pData->cHasRootRow) { // Root-Linien ausgleichen
iXpos += pData->iIndent;
}
iXpos -= pData->iIndent * pEntry->uLevel;
if(iXpos < pData->iIndent) { // Auf eingerücktem Bereich
if(pData->uStyle & TVS_HASBUTTONS)
if(pEntry->bFlags & TVIX_HASBUTTON) {
if(iXpos >= pData->iShift - 6)
if(iXpos <= pData->iShift + 7) {
iYpos %= pData->iRowHeight;
iYpos -= pData->iRowHeight / 2;
if(iYpos >= -6 && iYpos <= 7) {
pInfo->flags = TVHT_ONITEMBUTTON;
return uItem;
}
}
}
pInfo->flags = TVHT_ONITEMINDENT;
return uItem;
}
iXpos -= pData->iIndent;
if(pData->uStyleEx & TVS_EX_ITEMLINES) {
iXpos--;
}
if(pData->hStates) { // Auf der Auswahl-Box
iXpos -= pData->iStatesXsize;
if(iXpos < 0) {
pInfo->flags = TVHT_ONITEMSTATEICON;
return uItem;
}
}
if(pEntry->bFlags & TVIX_HASIMAGE) { // Auf dem Icon
iXpos -= pData->iImagesXsize;
if(pData->uStyleEx & TVS_EX_ITEMLINES)
iXpos--;
if(iXpos < 0) {
pInfo->flags = TVHT_ONITEMICON;
return uItem;
}
}
// Auf Text
if(iXpos < pEntry->iTextPixels + 5 || !pEntry->pText || !pEntry->pText[0]) {
pInfo->flags = TVHT_ONITEMLABEL;
} else {
pInfo->flags = TVHT_ONITEMRIGHT;
}
return uItem;
}
//*****************************************************************************
//*
//* TreeListSetTrackItem
//*
//*****************************************************************************
// Setzt das den Einfügeeintrag
// pData : Zeiger auf die Fensterdaten
// uItem : Ist die Nummer des Eintrages bei dem eingefügt werden soll
// iMode : 0=davor einfügen 1=nachher einfügen
// Ergibt 1 wenn der Eintrag eingefügt wurde
static unsigned TreeListSetInsertMark(TreeListData *pData, unsigned uItem, int iMode) {
TV_INSERTSTRUCT sInsert;
ExtraItem *pExtra;
BaseItem *pEntry;
unsigned uSub;
int iRet;
if(pData->uInsertMark) {
iRet = TreeListDeleteItem(pData, pData->uInsertMark, 1);
pData->uInsertMark = 0;
} else {
iRet = 0;
}
if(uItem == 0)
return iRet;
if(uItem > pData->uTreeItemsMax)
return 0;
pEntry = pData->pTreeItems[uItem];
if(!pEntry)
return 0;
if(iMode) {
uItem = pEntry->uPrevItem;
if(!uItem)
uItem = U(TVI_FIRST);
}
sInsert.hParent = (HTREEITEM)(ULONG_PTR)pEntry->uParent;
sInsert.hInsertAfter = (HTREEITEM)(ULONG_PTR)uItem;
sInsert.item.mask = TVIF_SELECTEDIMAGE | TVIF_IMAGE;
sInsert.item.iImage = TV_NOIMAGE;
sInsert.item.iSelectedImage = TV_NOIMAGE;
uItem = TreeListInsertItem(pData, &sInsert);
if(!uItem)
return 0;
pEntry = pData->pTreeItems[uItem];
pEntry->uColorBk = pData->uColors[TVC_INSERT];
pEntry->bFlags |= TVIX_BKCOLOR;
sInsert.item.mask |= TVIF_SUBITEM;
sInsert.item.hItem = (HTREEITEM)(ULONG_PTR)uItem;
for(uSub = 1; uSub < pData->uColumnCount; uSub++) {
sInsert.item.cChildren = uSub;
TreeListSetItem(pData, &sInsert.item);
pExtra = pData->pExtraItems[uSub - 1][uItem];
pExtra->uColorBk = pData->uColors[TVC_INSERT];
pExtra->bFlags |= TVIX_BKCOLOR;
}
pData->uInsertMark = uItem;
return uItem;
}
//*****************************************************************************
//*
//* TreeListGetItemColor
//*
//*****************************************************************************
// Holt die Farbe eins Feldes
// pData : Zeiger auf die Fensterdaten
// uItem : Ist die Nummer des Eintrages
// uSub : Ist die Spalte
// iMode : 0=Hintergrundfarbe 1=Textfarbe abfragen
// Ergibt die alte Farbe oder TV_NOCOLOR wenn keine Farbe eingestellt war
static LRESULT TreeListGetItemColor(TreeListData *pData, unsigned uItem, unsigned uSub, int iMode) {
COLORREF uColor;
ExtraItem *pExtra;
BaseItem *pEntry;
if(uItem > pData->uTreeItemsMax)
return TV_NOCOLOR;
pEntry = pData->pTreeItems[uItem];
if(!pEntry)
return TV_NOCOLOR;
if(uSub) { // Extra-Eintrag abfragen
if(uSub >= pData->uColumnCount)
return TV_NOCOLOR;
pExtra = pData->pExtraItems[uSub - 1][uItem];
if(!pExtra)
return TV_NOCOLOR;
if(iMode)
uColor = (pExtra->bFlags & TVIX_TEXTCOLOR) ? pExtra->uColorText : TV_NOCOLOR;
else
uColor = (pExtra->bFlags & TVIX_BKCOLOR) ? pExtra->uColorBk : TV_NOCOLOR;
} else {
if(iMode)
uColor = (pEntry->bFlags & TVIX_TEXTCOLOR) ? pEntry->uColorText : TV_NOCOLOR;
else
uColor = (pEntry->bFlags & TVIX_BKCOLOR) ? pEntry->uColorBk : TV_NOCOLOR;
}
return (LRESULT)uColor;
}
//*****************************************************************************
//*
//* TreeListSetItemColor
//*
//*****************************************************************************
// Setzt die Farbe eines Feldes
// pData : Zeiger auf die Fensterdaten
// uItem : Ist die Nummer des Eintrages
// uSub : Ist die Spalte
// uColor : Ist die neue Farbe
// iMode : 0=Hintergrundfarbe 1=Textfarbe einstellen
// Ergibt die alte Farbe oder TV_NOCOLOR wenn keine Farbe eingestellt war
static COLORREF TreeListSetItemColor(TreeListData *pData, unsigned uItem, unsigned uSub, COLORREF uColor, int iMode) {
TV_ITEM sSet;
COLORREF uOld;
ExtraItem *pExtra;
BaseItem *pEntry;
if(uItem > pData->uTreeItemsMax)
return TV_NOCOLOR;
pEntry = pData->pTreeItems[uItem];
if(!pEntry)
return TV_NOCOLOR;
if(uSub >= 255) { // Die Ganze Zeile ändern
if(pData->uColumnCount) {
for(uSub = pData->uColumnCount; uSub > 0; uSub--) {
TreeListSetItemColor(pData, uItem, uSub, uColor, iMode);
}
}
uSub = 0;
}
if(uSub) { // Extra-Eintrag verändern
if(uSub >= pData->uColumnCount)
return TV_NOCOLOR;
pExtra = pData->pExtraItems[uSub - 1][uItem];
if(!pExtra) { // Extra-Eintrag erzeugen
sSet.mask = TVIF_SUBITEM;
sSet.hItem = (HTREEITEM)(ULONG_PTR)uItem;
sSet.cChildren = uSub;
if(!TreeListSetItem(pData, &sSet))
return TV_NOCOLOR;
pExtra = pData->pExtraItems[uSub - 1][uItem];
}
if(iMode) { // Textfarbe
uOld = (pExtra->bFlags & TVIX_TEXTCOLOR) ? pExtra->uColorText : TV_NOCOLOR;
if(uColor == TV_NOCOLOR) {
pExtra->bFlags &= ~TVIX_TEXTCOLOR;
} else {
pExtra->bFlags |= TVIX_TEXTCOLOR;
pExtra->uColorText = uColor;
}
} else { // Hintergrund
uOld = (pExtra->bFlags & TVIX_BKCOLOR) ? pExtra->uColorBk : TV_NOCOLOR;
if(uColor == TV_NOCOLOR) {
pExtra->bFlags &= ~TVIX_BKCOLOR;
} else {
pExtra->bFlags |= TVIX_BKCOLOR;
pExtra->uColorBk = uColor;
}
}
} else {
if(iMode) { // Textfarbe
uOld = (pEntry->bFlags & TVIX_TEXTCOLOR) ? pEntry->uColorText : TV_NOCOLOR;
if(uColor == TV_NOCOLOR) {
pEntry->bFlags &= ~TVIX_TEXTCOLOR;
} else {
pEntry->bFlags |= TVIX_TEXTCOLOR;
pEntry->uColorText = uColor;
}
} else { // Hintergrund
uOld = (pEntry->bFlags & TVIX_BKCOLOR) ? pEntry->uColorBk : TV_NOCOLOR;
if(uColor == TV_NOCOLOR) {
pEntry->bFlags &= ~TVIX_BKCOLOR;
} else {
pEntry->bFlags |= TVIX_BKCOLOR;
pEntry->uColorBk = uColor;
}
}
}
if(uColor != uOld) { // Neu zeichnen
UpdateRect(pData, uItem, uSub);
}
return uOld;
}
//*****************************************************************************
//*
//* TreeListSetTrackItem
//*
//*****************************************************************************
// Setzt das unterstrichene Item
// pData : Zeiger auf die Fensterdaten
// uItem : Ist die Nummer des Eintrages
// uSub : Ist die Spalte
// Ergibt 1 wenn der Eintrag unterstrichen wurde
static int TreeListSetTrackItem(TreeListData *pData, unsigned uItem, unsigned uSub) {
ExtraItem *pExtra;
BaseItem *pEntry;
int iRet = 1;
if(!(pData->uStyleEx & TVS_EX_SUBSELECT)) {
uSub = 0;
} else {
if(uSub >= pData->uColumnCount) {
uItem = 0;
uSub = 0;
iRet = 0;
}
}
if(uItem > pData->uTreeItemsMax) {
uItem = 0;
uSub = 0;
iRet = 0;
} else {
if(uItem == pData->uTrackedItem)
if(uSub == pData->uTrackedSub) {
return iRet;
}
}
if(pData->uTrackedItem) { // Den alten Eintrag zurücksetzen
if(pData->uTrackedSub) {
pExtra = pData->pExtraItems[pData->uTrackedSub - 1][pData->uTrackedItem];
if(pExtra) {
pExtra->bFlags &= ~TVIX_TRACKED;
UpdateRect(pData, pData->uTrackedItem, pData->uTrackedSub);
}
} else {
pEntry = pData->pTreeItems[pData->uTrackedItem];
if(pEntry) {
pEntry->bFlags &= ~TVIX_TRACKED;
UpdateRect(pData, pData->uTrackedItem, 0);
}
}
}
if(uItem) { // Den neuen Eintrag setzen
if(uSub) {
pExtra = pData->pExtraItems[uSub - 1][uItem];
if(pExtra) {
pData->uTrackedSub = uSub;
pData->uTrackedItem = uItem;
pExtra->bFlags |= TVIX_TRACKED;
UpdateRect(pData, uItem, uSub);
} else {
iRet = 0;
}
} else {
pEntry = pData->pTreeItems[uItem];
if(pEntry) {
pData->uTrackedSub = 0;
pData->uTrackedItem = uItem;
pEntry->bFlags |= TVIX_TRACKED;
UpdateRect(pData, uItem, 0);
} else {
iRet = 0;
}
}
} else { // Keine Untersteichung
pData->uTrackedSub = 0;
pData->uTrackedItem = 0;
}
return iRet;
}
//*****************************************************************************
//*
//* TreeListGetNextItem
//*
//*****************************************************************************
// Sucht den nächsten Eintrag
// pData : Zeiger auf die Fensterdaten
// uItem : Ist die Nummer des Eintrages
// uFlags : Bestimmt nach welchem Eintrag gesucht werden soll
// Ergibt 1 wenn der Eintrag unterstrichen wurde
static unsigned TreeListGetNextItem(TreeListData *pData, unsigned uItem, unsigned uFlags) {
BaseItem *pEntry;
unsigned uStop;
unsigned uPos;
switch(uFlags) {
case TVGN_ROOT:
return pData->uFirstChild;
case TVGN_NEXT:
if(uItem > pData->uTreeItemsMax) {
return 0;
}
pEntry = pData->pTreeItems[uItem];
if(!pEntry)
return 0;
return pEntry->uNextItem;
case TVGN_PREVIOUS:
if(uItem > pData->uTreeItemsMax) {
return 0;
}
pEntry = pData->pTreeItems[uItem];
if(!pEntry)
return 0;
return pEntry->uPrevItem;
case TVGN_PARENT:
if(uItem > pData->uTreeItemsMax) {
return 0;
}
pEntry = pData->pTreeItems[uItem];
if(!pEntry)
return 0;
return pEntry->uParent;
case TVGN_CHILD:
if(uItem > pData->uTreeItemsMax) {
if(uItem == U(TVI_ROOT))
return pData->uFirstChild;
return 0;
}
pEntry = pData->pTreeItems[uItem];
if(!pEntry)
return 0;
return pEntry->uFirstChild;
case TVGN_LASTCHILD:
if(uItem > pData->uTreeItemsMax) {
if(uItem == U(TVI_ROOT))
return pData->uLastChild;
return 0;
}
pEntry = pData->pTreeItems[uItem];
if(!pEntry)
return 0;
return pEntry->uLastChild;
case TVGN_FIRSTVISIBLE:
if(pData->uItemPosCount <= 0)
return 0;
if(pData->uItemPosCount <= pData->uScrollY)
return 0;
uItem = pData->pItemPos[pData->uScrollY];
if(uItem > pData->uTreeItemsMax || !pData->pTreeItems[uItem]) {
return 0;
}
return uItem;
case TVGN_NEXTVISIBLE:
if(uItem > pData->uTreeItemsMax) {
if(uItem != U(TVI_ROOT))
return 0;
if(pData->uFirstChild == 0)
return 0;
pEntry = pData->pTreeItems[pData->uFirstChild];
uPos = pEntry->uShowPos;
if(uPos <= pData->uScrollY)
return 0;
return pData->uFirstChild;
}
pEntry = pData->pTreeItems[uItem];
if(!pEntry)
return 0;
uPos = pEntry->uShowPos;
if(uPos <= pData->uScrollY)
return 0;
if(uPos > pData->uScrollY + pData->uPageEnties)
return 0;
uItem = pData->pItemPos[uPos];
if(uItem > pData->uTreeItemsMax || !pData->pTreeItems[uItem]) {
return 0;
}
return uItem;
case TVGN_NEXTSELECTED:
if(uItem > pData->uTreeItemsMax) {
if(uItem != U(TVI_ROOT))
return 0;
uItem = pData->uFirstChild;
pEntry = pData->pTreeItems[uItem];
if(!pEntry)
return 0;
if(pEntry->uState & TVIS_SELECTED)
return uItem;
}
pEntry = pData->pTreeItems[uItem];
if(!pEntry)
return 0;
for(;;) {
if(pEntry->uFirstChild) {
uItem = pEntry->uFirstChild;
} else
if(pEntry->uNextItem) {
uItem = pEntry->uNextItem;
} else {
for(;;) {
uItem = pEntry->uParent;
pEntry = pData ->pTreeItems[uItem];
if(!pEntry)
return 0;
if(pEntry->uNextItem) {
uItem = pEntry->uNextItem;
break;
}
}
}
pEntry = pData->pTreeItems[uItem];
if(!pEntry)
break;
if(pEntry->uState & TVIS_SELECTED)
return uItem;
}
return 0;
case TVGN_NEXTSELCHILD:
if(uItem > pData->uTreeItemsMax) {
if(uItem != U(TVI_ROOT))
return 0;
uItem = pData->uFirstChild;
pEntry = pData->pTreeItems[uItem];
if(!pEntry)
return 0;
if(pEntry->uState & TVIS_SELECTED)
return uItem;
uStop = 0;
} else {
pEntry = pData->pTreeItems[uItem];
if(!pEntry || !pEntry->uFirstChild)
return 0;
uStop = uItem;
}
for(;;) {
if(pEntry->uFirstChild) {
uItem = pEntry->uFirstChild;
} else
if(pEntry->uNextItem) {
uItem = pEntry->uNextItem;
} else {
for(;;) {
uItem = pEntry->uParent;
if(uItem == uStop)
return 0;
pEntry = pData ->pTreeItems[uItem];
if(!pEntry)
return 0;
if(pEntry->uNextItem) {
uItem = pEntry->uNextItem;
break;
}
}
}
pEntry = pData ->pTreeItems[uItem];
if(!pEntry)
break;
if(pEntry->uState & TVIS_SELECTED)
return uItem;
}
return 0;
case TVGN_NEXTITEM:
if(uItem > pData->uTreeItemsMax) {
if(uItem != U(TVI_ROOT))
return 0;
uItem = pData->uFirstChild;
pEntry = pData->pTreeItems[uItem];
if(!pEntry)
return 0;
return uItem;
}
pEntry = pData->pTreeItems[uItem];
if(!pEntry)
return 0;
for(;;) {
if(pEntry->uFirstChild) {
uItem = pEntry->uFirstChild;
} else
if(pEntry->uNextItem) {
uItem = pEntry->uNextItem;
} else {
for(;;) {
uItem = pEntry->uParent;
pEntry = pData ->pTreeItems[uItem];
if(!pEntry)
return 0;
if(pEntry->uNextItem) {
uItem = pEntry->uNextItem;
break;
}
}
}
pEntry = pData->pTreeItems[uItem];
if(!pEntry)
break;
return uItem;
}
return 0;
case TVGN_PREVIOUSVISIBLE:
if(uItem > pData->uTreeItemsMax) {
return 0;
}
pEntry = pData->pTreeItems[uItem];
if(!pEntry)
return 0;
uPos = pEntry->uShowPos - 1;
if(uPos <= pData->uScrollY)
return 0;
if(uPos > pData->uScrollY + pData->uPageEnties)
return 0;
return pData->pItemPos[uPos - 1];
case TVGN_LASTVISIBLE:
uPos = pData->uItemPosCount;
if(uPos <= 0)
return 0;
return pData->pItemPos[uPos - 1];
case TVGN_DROPHILITE:
return pData->uTrackedItem;
case TVGN_DROPHILITESUB:
return pData->uTrackedSub;
case TVGN_CARET:
return pData->uSelectedItem;
case TVGN_CARETSUB:
return pData->uSelectedSub;
case TVGN_FOCUS:
return (pData->uFocusItem) ? pData->uFocusItem : pData->uSelectedItem;
case TVGN_FOCUSSUB:
return (pData->uFocusItem) ? pData->uFocusSub : pData->uSelectedSub;
}
return 0;
}
//*****************************************************************************
//*
//* TreeListFindItem
//*
//*****************************************************************************
// Sucht einen Kindeintrag mit einem bestimmten Text
// pData : Zeiger auf die Fensterdaten
// uItem : Ist die Nummer des Elterneintrages
// pFind : Die Struktur mit den Suchparametern
// Ergibt die Nummer des Eintrages bzw. 0 wenn nicht gefunden wurde
static unsigned TreeListFindItem(TreeListData *pData, unsigned uItem, TVFIND *pFind) {
int iImage;
BaseItem *pEntry;
ExtraItem *pExtra;
LPCTSTR pCmpText;
unsigned uTextSize;
unsigned uChkParam;
unsigned uChkState;
unsigned uChkText;
unsigned uChkCase;
unsigned uTextLen;
unsigned uSub;
if(pFind->uFlags & TVIF_CHILD) { // In den Kindern suchen
if(uItem > pData->uTreeItemsMax) {
if(uItem != U(TVI_ROOT))
return 0;
uItem = pData->uFirstChild;
} else {
pEntry = pData->pTreeItems[uItem];
if(!pEntry)
return 0;
uItem = pEntry->uFirstChild;
}
} else {
if(uItem > pData->uTreeItemsMax) { // Ist der Eintrag gültig
return 0;
}
if(pFind->uFlags & TVIF_NEXT) { // Beim nächsten Eintrag weitersuchen
pEntry = pData->pTreeItems[uItem];
if(!pEntry)
return 0;
uItem = pEntry->uNextItem;
}
}
uChkParam = pFind->uFlags & TVIF_PARAM;
uChkState = pFind->uFlags & TVIF_STATE;
uChkText = pFind->uFlags & TVIF_TEXT;
uChkCase = pFind->uFlags & TVIF_CASE;
uSub = pFind->uColumn;
uTextLen = 0;
if(uChkText) {
uTextLen = str_len(pFind->pText);
}
for(; uItem; uItem = pEntry->uNextItem) { // Durchlaufe alle Kinder
pEntry = pData->pTreeItems[uItem];
if(!pEntry)
return 0;
if(uChkParam && pFind->lParam != pEntry->lParam) { // Vergleiche lParam
continue;
}
// Vergleiche die State-Bits
if(uChkState && ((pEntry->uState ^ pFind->uState)&pFind->uStateMask)) {
continue;
}
if(uChkText == 0)
break;
if(uSub) { // Text von Spalten
if(uSub >= pData->uColumnCount)
continue;
pExtra = pData->pExtraItems[uSub - 1][uItem];
if(!pExtra) {
pCmpText = _T("");
uTextSize = 0;
} else {
pCmpText = pExtra->pText;
if(!pCmpText)
pCmpText = _T("");
uTextSize = pExtra->uTextSize;
}
} else { // Text vom Haupteintrag
if(pEntry->bCallback & TVIF_TEXT) {
CallbackEntry(pData, pEntry, uItem, pEntry->bCallback, &iImage, &uTextSize, &pCmpText);
} else {
pCmpText = pEntry->pText;
if(!pCmpText)
pCmpText = _T("");
uTextSize = pEntry->uTextSize;
}
}
if(uTextLen != uTextSize)
continue;
if(uChkCase) { // Zwischen Groß/Kleinbuchstaben unterscheiden
if(!str_icmp(pCmpText, pFind->pText))
break;
} else {
if(!memcmp(pCmpText, pFind->pText, uTextSize * sizeof(TCHAR)))
break;
}
}
return uItem;
}
//*****************************************************************************
//*
//* TreeListNextSelUntil
//*
//*****************************************************************************
// Sucht rekursiv ausgewählte Einträge
// pData : Zeiger auf die Fensterdaten
// uItem : Ist die Nummer des Eintrages nach dem mit der Suche begonnen werden soll
// uStop : Bestimmt bei welchem Eintrag abgebrochen werden soll werden soll
// Ergibt die Nummer des Eintrages oder 0 wenn keiner gefunden wurde
static unsigned TreeListNextSelUntil(TreeListData *pData, unsigned uItem, unsigned uStop) {
BaseItem *pEntry;
if(uItem > pData->uTreeItemsMax) {
if(uItem != U(TVI_ROOT))
return 0;
uItem = pData->uFirstChild;
pEntry = pData->pTreeItems[uItem];
if(!pEntry)
return 0;
if(pEntry->uState & TVIS_SELECTED)
return uItem;
uStop = 0;
} else {
pEntry = pData->pTreeItems[uItem];
if(!pEntry)
return 0;
if(!pEntry->uFirstChild && uItem == uStop)
return 0;
}
for(;;) {
if(pEntry->uFirstChild) {
uItem = pEntry->uFirstChild;
if(uItem == uStop)
return 0;
} else
if(pEntry->uNextItem) {
uItem = pEntry->uNextItem;
if(uItem == uStop)
return 0;
} else {
for(;;) {
uItem = pEntry->uParent;
if(uItem == uStop)
return 0;
pEntry = pData ->pTreeItems[uItem];
if(!pEntry)
return 0;
if(pEntry->uNextItem) {
uItem = pEntry->uNextItem;
if(uItem == uStop)
return 0;
break;
}
}
}
pEntry = pData ->pTreeItems[uItem];
if(!pEntry)
break;
if(pEntry->uState & TVIS_SELECTED)
return uItem;
}
return 0;
}
//*****************************************************************************
//*
//* TreeListNextUnselUntil
//*
//*****************************************************************************
// Sucht rekursiv nicht ausgewählte Einträge
// pData : Zeiger auf die Fensterdaten
// uItem : Ist die Nummer des Eintrages nach dem mit der Suche begonnen werden soll
// uStop : Bestimmt bei welchem Eintrag abgebrochen werden soll werden soll
// Ergibt die Nummer des Eintrages oder 0 wenn keiner gefunden wurde
static unsigned TreeListNextUnselUntil(TreeListData *pData, unsigned uItem, unsigned uStop) {
BaseItem *pEntry;
if(uItem > pData->uTreeItemsMax) {
if(uItem != U(TVI_ROOT))
return 0;
uItem = pData->uFirstChild;
pEntry = pData->pTreeItems[uItem];
if(!pEntry)
return 0;
if((pEntry->uState & TVIS_SELECTED) == 0)
return uItem;
uStop = 0;
} else {
pEntry = pData->pTreeItems[uItem];
if(!pEntry)
return 0;
if(!pEntry->uFirstChild && uItem == uStop)
return 0;
}
for(;;) {
if(pEntry->uFirstChild) {
uItem = pEntry->uFirstChild;
if(uItem == uStop)
return 0;
} else
if(pEntry->uNextItem) {
uItem = pEntry->uNextItem;
if(uItem == uStop)
return 0;
} else {
for(;;) {
uItem = pEntry->uParent;
if(uItem == uStop)
return 0;
pEntry = pData->pTreeItems[uItem];
if(!pEntry)
return 0;
if(pEntry->uNextItem) {
uItem = pEntry->uNextItem;
if(uItem == uStop)
return 0;
break;
}
}
}
pEntry = pData->pTreeItems[uItem];
if(!pEntry)
break;
if((pEntry->uState & TVIS_SELECTED) == 0)
return uItem;
}
return 0;
}
//*****************************************************************************
//*
//* TreeListChangeCheckbox
//*
//*****************************************************************************
// Schaltet eine Checkboc um
// pData : Zeiger auf die Fensterdaten
// uItem : Ist der Eintrag der geändert werden soll
// pInfo :
static void TreeListChangeCheckbox(TreeListData *pData, UINT uItem, int iPosX, int iPosY) {
BaseItem *pTemp;
BaseItem *pEntry;
NMTREEVIEW sNotify;
TV_ITEM sItem;
UINT uBits;
sNotify.itemOld.mask = 0;
sNotify.itemOld.hItem = 0;
pEntry = pData->pTreeItems[uItem];
uBits = pEntry->uState & TVIS_STATEIMAGEMASK;
if(pData->uStyleEx & TVS_EX_SINGLECHECKBOX) { // Einzelauswahl
pTemp = pData->pTreeItems[pData->uSingleSel];
if(pData->uSingleSel == uItem) {
if(pData->uStyleEx & TVS_EX_BITCHECKBOX) {
if(uBits & 0x1000)
return;
} else {
if(uBits == 0x2000)
return;
}
} else
if(pData->uSingleSel && pTemp) { // Anderer Eintrag gewählt
sItem.hItem = (HTREEITEM)(ULONG_PTR)pData->uSingleSel;
sItem.mask = TVIF_STATE;
sItem.stateMask = TVIS_STATEIMAGEMASK;
sItem.state = (pData->uStyleEx & TVS_EX_BITCHECKBOX) ? 0x0000 : 0x1000;
TreeListSetItem(pData, &sItem);
sNotify.itemOld.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_TEXT;
sNotify.itemOld.hItem = (HTREEITEM)(ULONG_PTR)pData->uSingleSel;
sNotify.itemOld.stateMask = 0xFFFFFFFF;
sNotify.itemOld.state = pTemp->uState;
sNotify.itemOld.lParam = pTemp->lParam;
sNotify.itemOld.pszText = pTemp->pText;
sNotify.itemOld.cchTextMax = pTemp->uTextSize;
sNotify.itemOld.cChildren = 0;
}
}
sItem.hItem = (HTREEITEM)(ULONG_PTR)uItem;
sItem.mask = TVIF_STATE;
sItem.stateMask = TVIS_STATEIMAGEMASK;
if(pData->uStyleEx & TVS_EX_BITCHECKBOX)
sItem.state = (uBits ^ 0x1000);
else
sItem.state = (uBits & 0x1000) ? 0x2000 : 0x1000;
TreeListSetItem(pData, &sItem);
pData->uSingleSel = uItem;
sNotify.hdr.code = TVN_CBSTATECHANGED;
sNotify.action = VK_DBLCLK;
sNotify.itemNew.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_TEXT;
sNotify.itemNew.hItem = (HTREEITEM)(ULONG_PTR)uItem;
sNotify.itemNew.stateMask = 0xFFFFFFFF;
sNotify.itemNew.state = pEntry->uState;
sNotify.itemNew.lParam = pEntry->lParam;
sNotify.itemNew.pszText = pEntry->pText;
sNotify.itemNew.cchTextMax = pEntry->uTextSize;
sNotify.itemNew.cChildren = 0;
sNotify.ptDrag.x = iPosX;
sNotify.ptDrag.y = iPosY;
UNLOCK(pData);
SendNotify(pData, &sNotify.hdr);
LOCK(pData);
}
//*****************************************************************************
//*
//* TreeListMouseNotify
//*
//*****************************************************************************
// Soll für einen Mausklick eine
// pData : Zeiger auf die Fensterdaten
// uMsg : Message des Mausklicks
// wParam : WPARAM des Mausklicks
// lParam : LPARAM des Mausklicks
static void TreeListMouseNotify(TreeListData *pData, UINT uMsg, WPARAM wParam, LPARAM lParam) {
TV_HITTESTINFO sInfo;
NMTREEVIEW sNotify;
BaseItem *pEntry;
unsigned uItem;
sInfo.flags = (UINT) wParam;
sInfo.pt.x = LOWORD(lParam);
sInfo.pt.y = HIWORD(lParam);
uItem = TreeListHitTest(pData, &sInfo);
if(uItem) {
pEntry = pData->pTreeItems[uItem];
sNotify.itemNew.stateMask = 0xFFFFFFFF;
sNotify.itemNew.state = pEntry->uState;
sNotify.itemNew.lParam = pEntry->lParam;
sNotify.itemNew.cChildren = TVHT_SUBTOCOL(sInfo.flags);
} else {
sNotify.itemNew.stateMask = 0;
sNotify.itemNew.state = 0;
sNotify.itemNew.lParam = 0;
}
sNotify.action = 0;
sNotify.hdr.code = uMsg;
sNotify.itemNew.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_SUBNUMBER;
sNotify.itemNew.hItem = (HTREEITEM)(ULONG_PTR)uItem;
sNotify.itemNew.pszText = (LPTSTR) - 1;
sNotify.itemNew.cchTextMax = -1;
sNotify.itemOld.mask = 0;
sNotify.ptDrag.x = LOWORD(lParam);
sNotify.ptDrag.y = HIWORD(lParam);
SendNotify(pData, &sNotify.hdr);
}
//*****************************************************************************
//*
//* TreeListMouseClick
//*
//*****************************************************************************
// Soll ein Mausklick ausgeführt werden. ACHTUNG UNLOCK wird ausgeführt
// pData : Zeiger auf die Fensterdaten
// uMsg : Message des Mausklicks
// wParam : WPARAM des Mausklicks
// lParam : LPARAM des Mausklicks
static void TreeListMouseClick(TreeListData *pData, UINT uMsg, WPARAM wParam, LPARAM lParam) {
ExtraItem *pExtra;
BaseItem *pEntry;
BaseItem *pTemp;
TV_HITTESTINFO sInfo;
NMTREEVIEW sNotify;
unsigned uOldSub;
unsigned uOldItem;
unsigned uMaskItem;
unsigned uMaskSub;
unsigned uMsgOld;
unsigned uToggle;
unsigned uItem;
unsigned uLine;
unsigned uTemp;
unsigned uStop;
unsigned uSub;
unsigned uSel;
unsigned uPos;
unsigned uNum;
int iMode;
int iAdd;
if(!pData->cIsEnabled) { // Ist das Fenster freigegeben
return;
}
if(!pData->cHasFocus) { // Hat das Fenster den Focus
if(GetFocus() != pData->hWnd) {
UNLOCK(pData);
SetFocus(pData->hWnd);
LOCK(pData);
if(GetFocus() != pData->hWnd) {
UNLOCK(pData);
return;
}
} else {
pData->cHasFocus = 1;
}
}
sInfo.flags = (UINT) wParam;
sInfo.pt.x = LOWORD(lParam);
sInfo.pt.y = HIWORD(lParam);
uItem = TreeListHitTest(pData, &sInfo);
uMsgOld = uMsg;
uToggle = 0;
if(uItem) { // Wurde auf einen Eintrag getrückt
pEntry = pData->pTreeItems[uItem];
if(pData->uStyle & TVS_FULLROWSELECT) {
uMaskItem = TVHT_ONITEMICON | TVHT_ONITEMLABEL | TVHT_ONITEMSTATEICON | TVHT_ONITEMRIGHT;
uMaskSub = TVHT_ONSUBICON | TVHT_ONSUBLABEL | TVHT_ONSUBRIGHT;
} else {
uMaskItem = TVHT_ONITEMICON | TVHT_ONITEMLABEL | TVHT_ONITEMSTATEICON;
uMaskSub = TVHT_ONSUBICON | TVHT_ONSUBLABEL;
}
if(sInfo.flags & TVHT_ONITEMBUTTON) { // Eintrag aufklappen
if(uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONDBLCLK) {
pEntry = pData->pTreeItems[uItem];
if(pEntry && (pEntry->uState & (TVIS_EXPANDED | TVIS_EXPANDPARTIAL)) == (TVIS_EXPANDED | TVIS_EXPANDPARTIAL)) {
TreeListToggleItem(pData, uItem, 0); // Von + auf - umschalten
} else {
uToggle = 1; // Eintrag auflappen
}
}
} else
if(sInfo.flags & TVHT_ONITEMSTATEICON) { // Checkbox umschalten
if(pData->uStyle & TVS_CHECKBOXES)
if(uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONDBLCLK) {
TreeListChangeCheckbox(pData, uItem, sInfo.pt.x, sInfo.pt.y);
}
} else
if(sInfo.flags & uMaskItem) { // Eintrag auswählen
if(!(pData->uStyle & TVS_DISABLEDRAGDROP)) {
if(uMsg == WM_LBUTTONDOWN) {
pData->uDragFlags = MK_LBUTTON;
pData->uDragItem = uItem;
pData->uDragSub = 0;
}
if(uMsg == WM_RBUTTONDOWN) {
pData->uDragFlags = MK_RBUTTON;
pData->uDragItem = uItem;
pData->uDragSub = 0;
}
}
if(pData->uStyleEx & TVS_EX_MULTISELECT) { // Mehrfachauswahl erlaubt
if(uMsg == WM_RBUTTONDOWN) // Keine Abwahl wenn mehrer Einträge aus gewählt
if(pData->uSelectedCount > 1) {
pEntry = pData->pTreeItems[uItem];
if(pEntry->uState & TVIS_SELECTED) {
goto End;
}
}
if(uMsg == WM_LBUTTONDOWN) // Spezialsteuerung für Multiselect bei Darg
if(!(wParam & (MK_CONTROL | MK_SHIFT)))
if(pData->uSelectedCount > 0) {
pEntry = pData->pTreeItems[uItem];
if(pEntry && (pEntry->uState & TVIS_SELECTED)) {
pData->cClickFlag = 1;
pData->cClickEdit = 0;
goto End;
}
}
if(uMsg == WM_LBUTTONUP && pData->cClickFlag) {
wParam &= ~(MK_CONTROL | MK_SHIFT);
uMsg = WM_LBUTTONDOWN;
}
if(wParam & MK_SHIFT) { // Bis zum angeklicken Auswählen
if(uMsg != WM_LBUTTONDOWN)
if(uMsg != WM_LBUTTONDBLCLK)
goto End;
pData->cClickEdit = 0;
uTemp = pData->uSelectedItem;
if(!uTemp) {
uTemp = pData->uFocusItem;
if(!uTemp || !pData->cReSelect)
goto End;
}
pEntry = pData->pTreeItems[uTemp];
uLine = pEntry->uShowPos;
if(!uLine)
goto End;
pEntry = pData->pTreeItems[uItem];
uStop = pEntry->uShowPos;
if(!uStop)
goto End;
if(pData->uSelectedCount <= 1) {
pData->uSelectedBase = uTemp;
}
// Shift-Select neu auswählen
if(pData->cReSelect && pData->uSelectedBase) {
TreeListSelectItem(pData, uItem, 0, TVC_BYMOUSE | TVC_DESELECT);
TreeListEnsureVisible(pData, pData->uSelectedItem, pData->uSelectedSub);
uTemp = pData->uSelectedBase;
pTemp = pData->pTreeItems[uTemp];
uStop = uItem;
while(pTemp && pTemp->uShowPos == 0) {
pTemp = pData->pTreeItems[pTemp->uParent];
if(!pTemp)
break;
}
if(pTemp) {
if(!(pTemp->uState & TVIS_SELECTED)) {
TreeListXorSelectItem(pData, uTemp, TVC_BYKEYBOARD);
}
if(pTemp->uShowPos < pEntry->uShowPos) {
uStop = uItem;
} else {
uStop = uTemp;
uTemp = uItem;
}
for(;;) {
uTemp = TreeListNextUnselUntil(pData, uTemp, uStop);
if(!uTemp)
break;
TreeListXorSelectItem(pData, uTemp, TVC_BYKEYBOARD);
}
}
pData->cReSelect = 0;
goto End;
}
TreeListSelectItem(pData, uItem, 0, TVC_BYMOUSE);
iAdd = (uLine > uStop) ? -1 : 1;
uPos = uLine;
if(uPos != uStop) {
pEntry = pData->pTreeItems[pData->uSelectedBase];
while(pEntry && pEntry->uShowPos == 0) {
pEntry = pData->pTreeItems[pEntry->uParent];
}
if(pEntry) {
if(iAdd < 0){
if(pEntry->uShowPos > uPos && uPos > uStop)
uPos--;
}
else {
if(pEntry->uShowPos < uPos && uPos < uStop)
uPos++;
}
}
}
for(;; uPos += iAdd) { // Einträge wählen
uTemp = pData->pItemPos [uPos - 1];
pEntry = pData->pTreeItems[uTemp ];
uSel = pEntry->uState & TVIS_SELECTED;
if(uTemp != pData->uSelectedBase || !uSel) {
TreeListXorSelectItem(pData, uTemp, TVC_BYMOUSE);
uSel = pEntry->uState & TVIS_SELECTED;
}
// Auch unsichtbare Kinder wählen
pTemp = pData->pTreeItems[pEntry->uFirstChild];
if(pTemp && !pTemp->uShowPos) {
if(uSel) {
for(uNum = uTemp;;) { // Kinder auswählen
uNum = TreeListNextUnselUntil(pData, uNum, uTemp);
if(!uNum)
break;
TreeListXorSelectItem(pData, uNum, TVC_BYMOUSE);
}
} else {
for(uNum = uTemp;;) { // Kinder abwählen
uNum = TreeListNextSelUntil(pData, uNum, uTemp);
if(!uNum)
break;
TreeListXorSelectItem(pData, uNum, TVC_BYMOUSE);
}
}
}
if(uPos == uStop)
break;
}
TreeListRemoveFocus(pData);
pEntry = pData->pTreeItems[uItem];
uSub = TVHT_SUBTOCOL(sInfo.flags);
if(!(pData->uStyleEx & TVS_EX_SUBSELECT))
uSub = 0;
pData->uFocusSub = uSub;
pData->uFocusItem = uItem;
if(uSub) {
pExtra = pData->pExtraItems[uSub - 1][uItem];
if(pExtra)
pExtra->bFlags |= TVIX_FOCUSED;
} else {
pEntry->bFlags |= TVIX_FOCUSED;
}
UpdateRect(pData, uItem, uSub);
TreeListSelectItem(pData, uItem, 0, TVC_BYMOUSE);
TreeListEnsureVisible(pData, pData->uSelectedItem, pData->uSelectedSub);
goto End;
}
if(wParam & MK_CONTROL) { // Einzelen Eintrag umschalten
pData->uSelectedBase = uItem;
pData->cReSelect = 1;
pData->cClickEdit = 0;
pEntry = pData->pTreeItems[uItem];
if(pEntry && (pEntry->uState & TVIS_SELECTED)) {
if(uMsg != WM_LBUTTONDOWN)
if(uMsg != WM_LBUTTONDBLCLK)
goto End;
TreeListSelectItem(pData, 0, 0, TVC_BYMOUSE);
if(TreeListXorSelectItem(pData, uItem, TVC_BYMOUSE)) {
// Auch unsichtbare Kinder abwählen
pTemp = pData->pTreeItems[pEntry->uFirstChild];
if(pTemp && !pTemp->uShowPos)
for(uTemp = uItem;;) {
uTemp = TreeListNextSelUntil(pData, uTemp, uItem);
if(!uTemp)
break;
TreeListXorSelectItem(pData, uTemp, TVC_BYMOUSE);
}
}
uTemp = pData->uFocusItem;
if(uTemp) { // Ist ein Focus definiert
TreeListRemoveFocus(pData);
}
uSub = TVHT_SUBTOCOL(sInfo.flags);
if(!(pData->uStyleEx & TVS_EX_SUBSELECT))
uSub = 0;
pData->uFocusItem = uItem;
pData->uFocusSub = uSub;
if(uSub) {
pExtra = pData->pExtraItems[uSub - 1][uItem];
if(pExtra)
pExtra->bFlags |= TVIX_FOCUSED;
} else {
pEntry->bFlags |= TVIX_FOCUSED;
}
UpdateRect(pData, uItem, uSub);
goto End;
}
}
}
if(wParam & MK_CONTROL) { // Ist die Ctrl-Taste gedrückt
iMode = TVC_BYMOUSE;
} else {
iMode = TVC_BYMOUSE | TVC_DESELECT;
}
uOldSub = pData->uSelectedSub;
uOldItem = pData->uSelectedItem;
TreeListRemoveFocus(pData);
TreeListSelectItem(pData, uItem, 0, iMode);
TreeListEnsureVisible(pData, pData->uSelectedItem, pData->uSelectedSub);
pEntry = pData->pTreeItems[uItem];
if(uMsg == WM_LBUTTONDBLCLK) {
// Auto-Edit mit Doppelklick
if(pData->aColumn[0].bFlags & TVAE_DBLCLICK && pData->aColumn[0].bEdit) {
TreeListStartAutoEdit(pData, 0, VK_DBLCLK, lParam);
} else
if(TreeListStartNotifyEdit(pData, pData->uSelectedItem, 0, VK_DBLCLK, lParam)) {
} else
if(pEntry && (pEntry->uState & (TVIS_EXPANDED | TVIS_EXPANDPARTIAL)) == (TVIS_EXPANDED | TVIS_EXPANDPARTIAL)) {
TreeListToggleItem(pData, uItem, 0); // Von + auf - umschalten
} else {
uToggle = 1; // Eintrag auflappen
}
} else
if(uMsgOld == WM_LBUTTONDOWN) {
if(pData->uStyleEx & TVS_EX_EDITCLICK)
if(!(wParam & (MK_CONTROL | MK_SHIFT)))
if(sInfo.flags & uMaskItem&~TVHT_ONITEMICON)
if(uOldSub == pData->uSelectedSub && uOldItem == pData->uSelectedItem) {
pData->cClickEdit = 1;
}
} else
if(uMsgOld == WM_LBUTTONUP) {
if(pData->uStyleEx & TVS_EX_EDITCLICK)
if(pData->cClickFlag && !pData->cClickEdit)
if(!(wParam & (MK_CONTROL | MK_SHIFT)))
if(sInfo.flags & uMaskItem&~TVHT_ONITEMICON)
if(uOldSub == pData->uSelectedSub && uOldItem == pData->uSelectedItem) {
pData->cClickEdit = 1;
}
if(pData->cClickEdit) {
pData->cClickEdit = 0;
if(pData->aColumn[0].bEdit) {
TreeListStartAutoEdit(pData, 0, VK_EDITCLK, lParam);
} else {
TreeListStartNotifyEdit(pData, pData->uSelectedItem, 0, VK_EDITCLK, lParam);
}
}
}
} else
if(sInfo.flags & uMaskSub) { // Extra-Eintrag auswählen
if(pData->uStyleEx & TVS_EX_SUBSELECT)
uSub = TVHT_SUBTOCOL(sInfo.flags);
else
uSub = 0;
if(!(pData->uStyle & TVS_DISABLEDRAGDROP)) {
if(uMsg == WM_LBUTTONDOWN) {
pData->uDragFlags = MK_LBUTTON;
pData->uDragItem = uItem;
pData->uDragSub = uSub;
}
if(uMsg == WM_RBUTTONDOWN) {
pData->uDragFlags = MK_RBUTTON;
pData->uDragItem = uItem;
pData->uDragSub = uSub;
}
}
if(wParam & MK_CONTROL) {
if(pData->uStyleEx & TVS_EX_MULTISELECT) { // Auswahl umschalten
pEntry = pData->pTreeItems[uItem];
if(pEntry && (pEntry->uState & TVIS_SELECTED)) {
if(uMsg != WM_LBUTTONDOWN)
if(uMsg != WM_LBUTTONDBLCLK)
goto End;
TreeListSelectItem(pData, 0, 0, TVC_BYMOUSE);
if(TreeListXorSelectItem(pData, uItem, TVC_BYMOUSE)) {
// Auch unsichtbare Kinder abwählen
pTemp = pData->pTreeItems[pEntry->uFirstChild];
if(pTemp && !pTemp->uShowPos)
for(uTemp = uItem;;) {
uTemp = TreeListNextSelUntil(pData, uTemp, uItem);
if(!uTemp)
break;
TreeListXorSelectItem(pData, uTemp, TVC_BYMOUSE);
}
}
TreeListRemoveFocus(pData);
pData->cClickEdit = 0;
pData->uFocusItem = uItem;
pData->uFocusSub = uSub;
if(uSub) {
pExtra = pData->pExtraItems[uSub - 1][uItem];
if(pExtra)
pExtra->bFlags |= TVIX_FOCUSED;
} else {
pEntry->bFlags |= TVIX_FOCUSED;
}
UpdateRect(pData, uItem, uSub);
goto End;
}
}
iMode = TVC_BYMOUSE;
} else {
iMode = TVC_BYMOUSE | TVC_DESELECT;
}
uOldSub = pData->uSelectedSub;
uOldItem = pData->uSelectedItem;
TreeListRemoveFocus(pData);
TreeListSelectItem(pData, uItem, uSub, (wParam & MK_CONTROL) ? TVC_BYMOUSE : TVC_BYMOUSE | TVC_DESELECT);
TreeListEnsureVisible(pData, pData->uSelectedItem, pData->uSelectedSub);
pEntry = pData->pTreeItems[uItem];
if(uMsg == WM_LBUTTONDBLCLK) {
if(pData->aColumn[uSub].bEdit && // Auto-Edit mit Doppelklick
((pData->aColumn[uSub].bFlags & TVAE_DBLCLICK) ||
((pData->aColumn[uSub].bFlags & TVAE_ICONCLICK) && (sInfo.flags & TVHT_ONSUBICON)))) {
TreeListStartAutoEdit(pData, uSub, (sInfo.flags & TVHT_ONSUBICON) ? VK_ICONCLK : VK_DBLCLK, lParam);
} else
if(TreeListStartNotifyEdit(pData, uItem, uSub, VK_DBLCLK, lParam)) {
} else {
uToggle = 1; // Eintrag auflappen
}
} else
if(uMsg == WM_LBUTTONDOWN) {
if(pData->aColumn[uSub].bEdit) // Auto-Edit mit Klick auf Icon
if(pData->aColumn[uSub].bFlags & TVAE_ICONCLICK)
if(sInfo.flags & TVHT_ONSUBICON) {
TreeListStartAutoEdit(pData, uSub, VK_ICONCLK, lParam);
}
if(pData->uStyleEx & TVS_EX_EDITCLICK)
if(uSub && !(wParam & (MK_CONTROL | MK_SHIFT)))
if(sInfo.flags & uMaskSub&~TVHT_ONSUBICON)
if(uOldSub == pData->uSelectedSub && uOldItem == pData->uSelectedItem) {
pData->cClickEdit = 1;
}
} else
if(uMsg == WM_LBUTTONUP) {
if(pData->cClickEdit) {
pData->cClickEdit = 0;
if(pData->aColumn[uSub].bEdit) {
TreeListStartAutoEdit(pData, uSub, VK_EDITCLK, lParam);
} else {
TreeListStartNotifyEdit(pData, uItem, uSub, VK_EDITCLK, lParam);
}
}
}
}
} else {
pEntry = NULL;
uSub = 0;
}
End:
switch(uMsgOld) {
case WM_LBUTTONUP:
sNotify.hdr.code = TVN_LBUTTONUP;
break;
case WM_LBUTTONDOWN:
sNotify.hdr.code = NM_CLICK;
break;
case WM_LBUTTONDBLCLK:
sNotify.hdr.code = NM_DBLCLK;
break;
case WM_RBUTTONUP:
sNotify.hdr.code = TVN_RBUTTONUP;
break;
case WM_RBUTTONDOWN:
sNotify.hdr.code = NM_RCLICK;
break;
case WM_RBUTTONDBLCLK:
sNotify.hdr.code = NM_RDBLCLK;
break;
default
:
UNLOCK(pData);
return;
}
if(pEntry) {
sNotify.itemNew.stateMask = 0xFFFFFFFF;
sNotify.itemNew.state = pEntry->uState;
sNotify.itemNew.lParam = pEntry->lParam;
sNotify.itemNew.cChildren = TVHT_SUBTOCOL(sInfo.flags);
} else {
sNotify.itemNew.stateMask = 0;
sNotify.itemNew.state = 0;
sNotify.itemNew.lParam = 0;
}
sNotify.action = 0;
sNotify.itemNew.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_SUBNUMBER;
sNotify.itemNew.hItem = (HTREEITEM)(ULONG_PTR)uItem;
sNotify.itemNew.pszText = (LPTSTR) - 1;
sNotify.itemNew.cchTextMax = -1;
sNotify.itemOld.mask = 0;
sNotify.ptDrag.x = LOWORD(lParam);
sNotify.ptDrag.y = HIWORD(lParam);
UNLOCK(pData);
if(!SendNotify(pData, &sNotify.hdr)) {
if(uToggle) { // Aufklappen nur wenn Notify nicht abgefangen
LOCK(pData);
TreeListToggleItem(pData, uItem, 0);
UNLOCK(pData);
}
}
}
//*****************************************************************************
//*
//* TreeListChar
//*
//*****************************************************************************
// Eingabe von Buchstaben über die WM_CHAR Nachricht
// pData : Zeiger auf die Fensterdaten
// nChar : Ist das Zeichen das eingegeben wurde
static void TreeListChar(TreeListData *pData, UINT nChar, LPARAM lParam) {
LPTSTR pName;
ExtraItem *pExtra;
BaseItem *pEntry;
unsigned uDelta;
unsigned uItem;
unsigned uTick;
unsigned uSub;
unsigned uPos;
unsigned uVal;
int iNum;
int iMax;
int i;
if(nChar >= ' ') {
if(pData->cKeyIgnore)
return; // Taste soll ignoriert werden
iMax = pData->uItemPosCount;
uSub = pData->uSelectedSub;
i = 0;
if(TVIS_EDIT(pData->aColumn[uSub].bEdit)) // Bei AutoEdit keine Auswahl
if(!(pData->aColumn[uSub].bFlags & TVAE_ONLYRETURN)) {
TreeListStartAutoEdit(pData, uSub, nChar, 0);
return;
}
if(TreeListStartNotifyEdit(pData, pData->uSelectedItem, uSub, nChar | VK_ISACHAR, lParam)) {
return;
}
if(iMax <= 0)
return;
iNum = pData->uSelectedItem; // Hole die Startzeile
if(iNum > 0) {
iNum = pData->pTreeItems[iNum]->uShowPos - 1;
if(iNum < 0)
iNum = 0;
}
if((nChar >= 'a' && nChar <= 'z') || // In Grosbuchstaben umwandeln
(nChar >= 224 && nChar <= 254)) {
nChar -= 'a' - 'A';
}
if(!(pData->uStyleEx & TVS_EX_NOCHARSELCET))
for(;;) { // Suche Anfangsbuchstaben
uTick = GetTickCount();
uDelta = (uKeyPos > 0) ? 750 : 500;
if(uKeyPos >= 3)
uDelta = 1000;
if(uTick - uKeyLast > uDelta)
uKeyPos = 0;
if(uKeyPos >= 16)
uKeyPos = 0;
if(uKeyPos == 1 && cKeyData[0] == (TCHAR)nChar) {
uKeyPos = 0;
}
cKeyData[uKeyPos] = (TCHAR)nChar;
uKeyLast = uTick;
uKeyPos += 1;
if(uKeyPos > 1) {
iNum--;
if(iNum < 0)
iNum = iMax - 1;
}
if(iMax <= 0)
break;
for(i = iNum + 1; i != iNum; i++) { // Suche Übereinstimmung
if(i >= iMax) {i = -1; continue;}
uItem = pData->pItemPos[i];
if(uSub) {
#ifndef __REACTOS__
pExtra = pExtra = pData->pExtraItems[uSub - 1][uItem];
#else
pExtra = pData->pExtraItems[uSub - 1][uItem];
#endif
pName = (pExtra && pExtra->pText) ? pExtra->pText : _T("");
} else {
pEntry = pData->pTreeItems[uItem];
pName = (pEntry && pEntry->pText) ? pEntry->pText : _T("");
}
for(uPos = 0; uPos < uKeyPos; uPos++) { // Vergleiche die Texte
#if UNICODE
uVal = pName[uPos];
#else
uVal = ((unsigned char *)pName)[uPos];
#endif
if((uVal >= 'a' && uVal <= 'z') || // In Grosbuchstaben umwandeln
(uVal >= 224 && uVal <= 254)) {
uVal -= 'a' - 'A';
}
if(cKeyData[uPos] != (TCHAR)uVal) {
break;
}
}
if(uPos < uKeyPos)
continue;
if(TreeListSelectItem(pData, uItem, uSub, TVC_UNKNOWN | TVC_DESELECT)) {
TreeListEnsureVisible(pData, uItem, uSub);
}
return;
}
if(i != iNum)
break;
if(uKeyPos <= 1)
break;
iNum++;
if(iNum >= iMax)
iNum = iMax - 1;
uKeyPos = 0;
}
}
}
//*****************************************************************************
//*
//* TreeListKeyDown
//*
//*****************************************************************************
// Soll ein Tastentruck ausgeführt werden. ACHTUNG UNLOCK wird ausgeführt
// pData : Zeiger auf die Fensterdaten
// wParam : WPARAM des Mausklicks
// lParam : LPARAM des Mausklicks
// Ergibt 0 wenn die Taste verarbeitet wurde
static void TreeListKeyDown(TreeListData *pData, WPARAM wParam, LPARAM lParam) {
BaseItem *pTemp;
BaseItem *pEntry;
TV_KEYDOWN_EX sNotify;
unsigned uTemp;
unsigned uItem;
unsigned uStop;
unsigned uVal;
int iLineCmp;
int iOldLine;
int iOldCol;
int iFocus;
int iShift;
int iBase;
int iLine;
int iAdd;
int iPos;
int iSub;
int iCol;
int iMax;
int iDel;
int iScr;
int iSel;
if(!pData->cIsEnabled) { // Ist das Fenster freigegeben
return;
}
if(!pData->cHasFocus) { // Hat das Fenster den Focus
if(GetFocus() != pData->hWnd) {
UNLOCK(pData);
SetFocus(pData->hWnd);
LOCK(pData);
if(GetFocus() != pData->hWnd) {
return;
}
} else {
pData->cHasFocus = 1;
}
}
if(wParam == VK_RETURN) { // Wurde Enter gedrückt
UNLOCK(pData);
sNotify.hdr.code = NM_RETURN;
sNotify.wVKey = (WORD)(wParam);
sNotify.wScan = (WORD)(wParam >> 16);
sNotify.flags = (UINT)(lParam);
SendNotify(pData, &sNotify.hdr);
iSub = pData->uSelectedSub;
if(pData->aColumn[iSub].bEdit) { // Auto-Edit starten
if(GetAsyncKeyState(VK_CONTROL) & 0x8000) {
wParam = VK_ICONCLK;
lParam = 0;
}
TreeListStartAutoEdit(pData, iSub, wParam, lParam);
} else {
TreeListStartNotifyEdit(pData, pData->uSelectedItem, iSub, wParam, lParam);
}
return;
}
pEntry = pData->pTreeItems[pData->uSelectedItem];
iDel = (GetAsyncKeyState(VK_SHIFT) & 0x8000) ? 0 : TVC_DESELECT;
iScr = GetAsyncKeyState(VK_CONTROL) & 0x8000;
if(iDel && iScr) // Ende oder Anfang wählen
if(wParam == VK_HOME || wParam == VK_END)
if(pData->uStyleEx & TVS_EX_HOMEENDSELECT) {
iDel = TVC_DESELECT;
iScr = 0;
}
if(iDel && iScr) { // Das Fenster scrollen
iLine = pData->uScrollY;
iSub = pData->uScrollX;
switch(wParam) {
case VK_END:
iLine = pData->uTreeItemsCount; // Ans Ende scrollen
iLine -= pData->uPageEnties;
break;
case VK_HOME:
iLine = 0; // Zum Anfang scrollen
break;
case VK_LEFT:
iSub -= 16; // Links scrollen
break;
case VK_RIGHT:
iSub += 16; // Rechts scrollen
break;
case VK_UP:
iLine--; // Nach oben scrollen
break;
case VK_DOWN:
iLine++; // Nach unten scrollen
break;
case VK_PRIOR:
iLine -= pData->uSizeX; // Eine Seite nach oben
break;
case VK_NEXT:
iLine += pData->uSizeX; // Eine Seite nach unten
break;
case VK_SPACE:
if(pEntry == NULL)
break; // Expantieren und schließen
if(pEntry->bFlags & TVIX_HASBUTTON) {
TreeListToggleItem(pData, pData->uSelectedItem, 0);
}
break;
default
:
goto Exit;
}
if(iLine != (int)pData->uScrollY) {
iMax = pData->uItemPosCount;
iMax -= pData->uPageEnties - 1;
if(iLine >= iMax)
iLine = iMax;
if(iLine < 0)
iLine = 0;
if(iLine != (int)pData->uScrollY)
if(!(pData->uStyle & TVS_NOSCROLL)) {
pData->uScrollY = iLine;
SetScrollPos(pData->hWnd, SB_VERT, iLine, TRUE);
UpdateView(pData);
}
}
if(iSub != (int)pData->uScrollX) {
uVal = pData->uColumnCount;
if(uVal)
iMax = pData->aColumnXpos[uVal] - pData->uSizeX / 2;
else
iMax = pData->iMaxSizeX;
iMax -= pData->uSizeX - pData->uSizeX / 2;
if(iSub >= iMax)
iSub = iMax;
if(iSub < 0)
iSub = 0;
if(iSub != (int)pData->uScrollX)
if(!(pData->uStyle & TVS_NOSCROLL)) {
pData->uScrollX = iSub;
SetScrollPos(pData->hWnd, SB_HORZ, iSub, TRUE);
UpdateView(pData);
if(pData->hHeader) {
MoveWindow(pData->hHeader, -iSub, 0, pData->uSizeX + iSub, pData->uStartPixel, TRUE);
}
}
}
} else { // Einen anderen Eintrag auswählen
iSub = pData->uSelectedSub;
iCol = pData->aColumn[iSub].bIndex;
if(!pEntry) {
iLine = 0;
iCol = 0;
iOldCol = 0;
iOldLine = -1;
uTemp = pData->uFocusItem;
if(uTemp) { // Ist ein Focus definiert
pTemp = pData->pTreeItems[uTemp];
while(pTemp && !pTemp->uShowPos) {
pTemp = pData->pTreeItems[pTemp->uParent];
}
if(pTemp) {
iLine = pTemp->uShowPos - 1;
iSub = pData->uFocusSub;
iCol = pData->aColumn[iSub].bIndex;
}
if(pData->uSelectedCount > 1) {
iOldCol = iCol;
iOldLine = iLine;
} else {
TreeListRemoveFocus(pData);
}
}
} else {
iLine = pEntry->uShowPos - 1;
if(iLine < 0)
iLine = 0;
uTemp = pData->uSelectedItem;
iOldLine = iLine;
iOldCol = iCol;
}
switch(wParam) {
case VK_END:
iLine = pData->uItemPosCount - 1;
break;
case VK_HOME:
iLine = 0;
break;
case VK_LEFT:
if(pData->uColumnCount > 1 && (pData->uStyleEx & TVS_EX_SUBSELECT)) {
while(iCol > 0) {
iCol--;
iSub = pData->aColumnPos[iCol];
if(pData->aColumn[iSub].sReal > 0)
break;
}
} else {
if(! pEntry)
break;
if(!(pEntry->uState & TVIS_EXPANDED) || !pEntry->uFirstChild) {
pTemp = pData->pTreeItems[pEntry->uParent];
if(pTemp)
iLine = pTemp->uShowPos - 1;
break;
}
if(pEntry->bFlags & TVIX_HASBUTTON) {
TreeListToggleItem(pData, pData->uSelectedItem, 0);
}
}
break;
case VK_RIGHT:
if(pData->uColumnCount > 1 && (pData->uStyleEx & TVS_EX_SUBSELECT)) {
while(iCol + 1 < (int)pData->uColumnCount) {
iCol++;
iSub = pData->aColumnPos[iCol];
if(pData->aColumn[iSub].sReal > 0)
break;
}
} else {
if(!pEntry)
break;
if(pEntry->uState & TVIS_EXPANDED) {
iLine++;
break;
}
if(pEntry->bFlags & TVIX_HASBUTTON) {
TreeListToggleItem(pData, pData->uSelectedItem, 0);
}
}
break;
case VK_UP:
iLine--;
break;
case VK_DOWN:
iLine++;
break;
case VK_PRIOR:
iAdd = pData->uPageEnties - 1; // Eine Seite nach oben
if(iAdd <= 0)
iAdd = 1;
iLine -= iAdd;
break;
case VK_NEXT:
iAdd = pData->uPageEnties - 1; // Eine Seite nach unten
if(iAdd <= 0)
iAdd = 1;
iLine += iAdd;
break;
case VK_BACK: // Eine Ebene höher
if(pEntry) {
uItem = pEntry->uParent;
if(!uItem)
iLine = 0;
else {
iLine = pData->pTreeItems[uItem]->uShowPos - 1;
if(iLine < 0)
iLine = 0;
}
}
break;
case VK_SPACE:
if(pEntry && iCol == 0 && (pData->uStyle & TVS_CHECKBOXES)) {
TreeListChangeCheckbox(pData, pData->uSelectedItem, 0, 0);
}
break;
default
:
goto Exit;
}
if(iCol >= (int)pData->uColumnCount)
iCol = pData->uColumnCount - 1;
if(iCol < 0)
iCol = 0;
if(iLine >= (int)pData->uItemPosCount)
iLine = pData->uItemPosCount - 1;
if(iLine < 0)
iLine = 0;
if(!(pData->uStyleEx & TVS_EX_SUBSELECT))
iCol = 0;
if(pData->uItemPosCount > 0)
if(iLine != iOldLine || iCol != iOldCol) { // Wurde ein anderer Eintrag ausgewählt
if(pData->uSelectedCount > 1) {
TreeListRemoveFocus(pData);
} else {
pData->uSelectedBase = uTemp;
}
iShift = iDel;
if(pData->cReSelect) { // Die Shift-Auswahl neu erstellen
iDel = TVC_DESELECT;
}
uItem = pData->pItemPos [iLine];
pTemp = pData->pTreeItems[uItem];
iSub = pData->aColumnPos[iCol ];
iFocus = (pTemp && (pTemp->uState & TVIS_SELECTED)) ? TVC_ONLYFOCUS : 0;
TreeListSelectItem(pData, uItem, iSub, TVC_BYKEYBOARD | iDel | iFocus);
if(!(pData->uStyle & TVS_NOSCROLL)) {
TreeListEnsureVisible(pData, pData->uSelectedItem, pData->uSelectedSub);
}
if(pData->cReSelect && pData->uSelectedBase) { // Shift-Select neu auswählen
uTemp = pData->uSelectedBase;
pTemp = pData->pTreeItems[uTemp];
uStop = uItem;
while(pTemp && pTemp->uShowPos == 0) {
pTemp = pData->pTreeItems[pTemp->uParent];
if(!pTemp)
break;
}
if(pTemp && !iShift) {
if(!(pTemp->uState & TVIS_SELECTED)) {
TreeListXorSelectItem(pData, uTemp, TVC_BYKEYBOARD);
}
iLineCmp = pTemp->uShowPos - 1;
if(iLineCmp < iLine) {
uStop = uItem;
} else {
uStop = uTemp;
uTemp = uItem;
}
for(;;) {
uTemp = TreeListNextUnselUntil(pData, uTemp, uStop);
if(!uTemp)
break;
TreeListXorSelectItem(pData, uTemp, TVC_BYKEYBOARD);
}
}
pData->cReSelect = 0;
}
if((pData->uStyleEx & TVS_EX_MULTISELECT) && !iDel) {
uVal = pData->uSelectedBase;
pTemp = pData->pTreeItems[uVal];
iBase = (pTemp) ? pTemp->uShowPos - 1 : -1;
if(iLine > iOldLine) { // Nach oben oder nach unten
iMax = (iOldLine > iBase) ? iOldLine + 1 : iOldLine;
if(iMax < 0)
iMax = 0;
iPos = iLine;
} else {
iMax = iLine;
iPos = (iOldLine < iBase) ? iOldLine - 1 : iOldLine;
if(iPos < 0)
iPos = 0;
}
for(; iPos >= iMax; iPos--) { // Übersprungene Einträge mit aus/abwählen
uItem = pData->pItemPos[iPos];
if(iPos != iLine && iPos != iBase) {
if(!TreeListXorSelectItem(pData, uItem, TVC_BYKEYBOARD))
continue;
}
pTemp = pData->pTreeItems[uItem]; // Auch unsichtbare Kinder abwählen
if(pTemp) {
iSel = pTemp->uState & TVIS_SELECTED;
pTemp = pData->pTreeItems[pTemp->uFirstChild];
if(!pTemp || pTemp->uShowPos)
continue;
if(!iSel) {
for(uTemp = uItem;;) {
uTemp = TreeListNextSelUntil(pData, uTemp, uItem);
if(!uTemp)
break;
TreeListXorSelectItem(pData, uTemp, TVC_BYKEYBOARD);
}
} else {
for(uTemp = uItem;;) {
uTemp = TreeListNextUnselUntil(pData, uTemp, uItem);
if(!uTemp)
break;
TreeListXorSelectItem(pData, uTemp, TVC_BYKEYBOARD);
}
}
}
}
}
}
}
Exit:
sNotify.hdr.code = TVN_KEYDOWN;
sNotify.wVKey = (WORD)(wParam);
sNotify.wScan = (WORD)(wParam >> 16);
sNotify.flags = (UINT)(lParam);
UNLOCK(pData);
pData->cKeyIgnore = (char)SendNotify(pData, &sNotify.hdr);
}
//*****************************************************************************
//*
//* TreeListSortItemsEx
//*
//*****************************************************************************
// Sortiert die Kindereinträge eines Eintrages mitteles einer Funktion
// pData : Zeiger auf die Fensterdaten
// pSortData : Ist ein Zeiger auf die Sortiertaten
// iMode : 1=Rekursiv sortieren
// Ergibt 1 wenn ok sonst 0
static int TreeListSortItemsEx(TreeListData *pData, TV_SORTEX *pSortData, int iMode) {
TV_SORTEX sSort;
unsigned uNum;
unsigned uItem;
unsigned uLast;
unsigned uFirst;
unsigned uParent;
BaseItem *pParent;
BaseItem *pEntry;
BaseItem *pNext;
BaseItem **pList;
unsigned *pItemList;
unsigned *pItemNew;
unsigned uEnties[128];
unsigned uPos;
unsigned uMax;
PFNTVCOMPAREEX pCompare;
HTREEITEM hItemTemp;
LPARAM lParamTemp;
LPARAM lParamSort;
HWND hTreeWnd;
int iLower, iUpper, iMiddle, iCmp;
int uMemL[30], uMemU[30];
int iStart, iLast;
int iLevel;
unsigned uTemp;
pList = pData->pTreeItems;
uParent = U(pSortData->hParent);
if(uParent > pData->uTreeItemsMax) { // Root-Eintrag sortieren
if(uParent != U(TVI_ROOT))
return 0;
uLast = pData->uLastChild;
uFirst = pData->uFirstChild;
if(uFirst == 0)
return 1;
if(uFirst == uLast) { // Einzelner Eintrag
pNext = pList[uFirst];
if(!pNext->uFirstChild)
return 1;
sSort.hParent = (HTREEITEM)(ULONG_PTR)uFirst;
sSort.lParam = pSortData->lParam;
sSort.lpfnCompare = pSortData->lpfnCompare;
TreeListSortItemsEx(pData, &sSort, iMode);
return 1;
}
pParent = 0;
uParent = 0;
} else { // Untereintrag sortieren
pParent = pList[uParent];
if(pParent == NULL)
return 0;
uLast = pParent->uLastChild;
uFirst = pParent->uFirstChild;
if(uFirst == 0)
return 1;
if(uFirst == uLast) { // Einzelner Eintrag
pNext = pList[uFirst];
if(!pNext->uFirstChild)
return 1;
sSort.hParent = (HTREEITEM)(ULONG_PTR)uFirst;
sSort.lParam = pSortData->lParam;
sSort.lpfnCompare = pSortData->lpfnCompare;
TreeListSortItemsEx(pData, &sSort, iMode);
return 1;
}
}
if(iMode) { // Sortiere die Untereinträge
pNext = pList[uFirst];
sSort.hParent = (HTREEITEM)(ULONG_PTR)uFirst;
sSort.lParam = pSortData->lParam;
sSort.lpfnCompare = pSortData->lpfnCompare;
while(pNext) {
if(pNext->uFirstChild) {
TreeListSortItemsEx(pData, &sSort, SORT_NOUPDATE);
}
sSort.hParent = (HTREEITEM)(ULONG_PTR)pNext->uNextItem;
pNext = pList[pNext->uNextItem];
}
}
if(uFirst == uLast)
return 1;
//******************** Erzeuge Eintragsliste **********************************
uItem = uFirst;
pItemList = uEnties;
uMax = 128;
uPos = 0;
do { // Alle Kindeinträge suchen
if(uPos >= uMax) {
uMax *= 2;
pItemNew = new(unsigned, uMax);
memcpy(pItemNew, pItemList, uPos * sizeof(pItemList[0]));
if(uPos > 128)
delete(pItemList);
pItemList = pItemNew;
}
pItemList[uPos] = uItem;
pNext = pList[uItem];
uItem = pNext->uNextItem;
uPos++;
} while(uItem);
//************************* Qsort-Algorithmus *********************************
#define XCHANGE_MEM(a,b) uTemp=pItemList[a];pItemList[a]=pItemList[b];pItemList[b]=uTemp;
pData->cLockChanges = 1;
UNLOCK(pData);
hTreeWnd = pData->hWnd;
pCompare = pSortData->lpfnCompare;
lParamSort = pSortData->lParam;
iLast = uPos - 1;
iStart = 0;
iLevel = 0;
for(;;) {
iLower = iStart;
iMiddle = (iStart + iLast) >> 1; // Mitte bereichnen
iUpper = iLast + 1;
XCHANGE_MEM(iMiddle, iLower);
uItem = pItemList[iStart];
hItemTemp = (HTREEITEM)(ULONG_PTR)uItem;
lParamTemp = pList[uItem]->lParam;
for(;;) {
do {
iLower++;
if(iLower > iLast)
break;
uItem = pItemList[iLower];
iCmp = pCompare(hTreeWnd, (HTREEITEM)(ULONG_PTR)uItem, hItemTemp, pList[uItem]->lParam, lParamTemp, lParamSort);
} while(iCmp <= 0);
do {
iUpper--;
if(iUpper <= iStart)
break;
uItem = pItemList[iUpper];
iCmp = pCompare(hTreeWnd, (HTREEITEM)(ULONG_PTR)uItem, hItemTemp, pList[uItem]->lParam, lParamTemp, lParamSort);
} while(iCmp >= 0);
if(iUpper < iLower)
break;
XCHANGE_MEM(iUpper, iLower);
}
XCHANGE_MEM(iStart, iUpper);
if(iUpper - 1 - iStart >= iLast - iLower) {
if(iStart + 1 < iUpper) {
uMemL[iLevel] = iStart;
uMemU[iLevel] = iUpper - 1;
iLevel++;
}
if(iLower < iLast) {
iStart = iLower;
continue;
}
} else {
if(iLower < iLast) {
uMemL[iLevel] = iLower;
uMemU[iLevel] = iLast;
iLevel++;
}
if(iStart + 1 < iUpper) {
iLast = iUpper - 1;
continue;
}
}
// Eine Ebene absteigen
iLevel--;
if(iLevel >= 0) { // Noch Ebenen vorhanden
iStart = uMemL[iLevel];
iLast = uMemU[iLevel];
continue;
}
break;
}
LOCK(pData);
pData->cLockChanges = 0;
//******************** Einträge neu einsortirenen *****************************
uPos--;
pEntry = pList[uParent];
if(!pEntry) {
pData->uFirstChild = pItemList[ 0 ];
pData->uLastChild = pItemList[uPos];
} else {
pEntry->uFirstChild = pItemList[ 0 ];
pEntry->uLastChild = pItemList[uPos];
}
uLast = 0;
uItem = pItemList[0];
for(uNum = 0; uNum < uPos;) { // Kinder neu einhängen
pEntry = pList[uItem];
pEntry->uPrevItem = uLast;
uNum++;
uLast = uItem;
uItem = pItemList[uNum];
pEntry->uNextItem = uItem;
}
pEntry = pList[uItem];
pEntry->uPrevItem = uLast;
pEntry->uNextItem = 0;
if(iMode != SORT_NOUPDATE) // Ausgabeliste neuerstellen
if(uParent == 0 || (pParent->uShowPos && (pParent->uState & TVIS_EXPANDED))) {
UpdateItems(pData, uParent);
if(pData->uStyle & TVS_SHOWSELALWAYS)
if(pData->uSelectedItem) {
TreeListEnsureVisible(pData, pData->uSelectedItem, pData->uSelectedSub);
}
}
if(uMax > 128)
delete(pItemList);
return 1;
}
//*****************************************************************************
//*
//* TreeListSortItemsCb
//*
//*****************************************************************************
// Sortiert die Kindereinträge eines Eintrages mitteles einer Funktion
// pData : Zeiger auf die Fensterdaten
// pSortData : Ist ein Zeiger auf die Sortiertaten
// iMode : 1=Rekursiv sortieren
// Ergibt 1 wenn ok sonst 0
static int TreeListSortItemsCb(TreeListData *pData, TV_SORTCB *pSortData, int iMode) {
TV_SORTCB sSort;
unsigned uNum;
unsigned uItem;
unsigned uLast;
unsigned uFirst;
unsigned uParent;
BaseItem *pParent;
BaseItem *pEntry;
BaseItem *pNext;
BaseItem **pList;
unsigned *pItemList;
unsigned *pItemNew;
unsigned uEnties[128];
unsigned uPos;
unsigned uMax;
PFNTVCOMPARE pCompare;
LPARAM lParamTemp;
LPARAM lParamSort;
int iLower, iUpper, iMiddle, iCmp;
int uMemL[30], uMemU[30];
int iStart, iLast;
int iLevel;
unsigned uTemp;
pList = pData->pTreeItems;
uParent = U(pSortData->hParent);
if(uParent > pData->uTreeItemsMax) { // Root-Eintrag sortieren
if(uParent != U(TVI_ROOT))
return 0;
uLast = pData->uLastChild;
uFirst = pData->uFirstChild;
if(uFirst == 0)
return 1;
if(uFirst == uLast) { // Einzelner Eintrag
pNext = pList[uFirst];
if(!pNext->uFirstChild)
return 1;
sSort.hParent = (HTREEITEM)(ULONG_PTR)uFirst;
sSort.lParam = pSortData->lParam;
sSort.lpfnCompare = pSortData->lpfnCompare;
TreeListSortItemsCb(pData, &sSort, iMode);
return 1;
}
pParent = 0;
uParent = 0;
} else { // Untereintrag sortieren
pParent = pList[uParent];
if(pParent == NULL)
return 0;
uLast = pData->uLastChild;
uFirst = pParent->uFirstChild;
if(uFirst == 0)
return 1;
if(uFirst == uLast) { // Einzelner Eintrag
pNext = pList[uFirst];
if(!pNext->uFirstChild)
return 1;
sSort.hParent = (HTREEITEM)(ULONG_PTR)uFirst;
sSort.lParam = pSortData->lParam;
sSort.lpfnCompare = pSortData->lpfnCompare;
TreeListSortItemsCb(pData, &sSort, iMode);
return 1;
}
}
if(iMode) { // Sortiere die Untereinträge
pNext = pList[uFirst];
sSort.hParent = (HTREEITEM)(ULONG_PTR)uFirst;
sSort.lParam = pSortData->lParam;
sSort.lpfnCompare = pSortData->lpfnCompare;
while(pNext) {
if(pNext->uFirstChild) {
TreeListSortItemsCb(pData, &sSort, SORT_NOUPDATE);
}
sSort.hParent = (HTREEITEM)(ULONG_PTR)pNext->uNextItem;
pNext = pList[pNext->uNextItem];
}
}
if(uFirst == uLast)
return 1;
//******************** Erzeuge Eintragsliste **********************************
uItem = uFirst;
pItemList = uEnties;
uMax = 128;
uPos = 0;
do { // Alle Kindeinträge suchen
if(uPos >= uMax) {
uMax *= 2;
pItemNew = new(unsigned, uMax);
memcpy(pItemNew, pItemList, uPos * sizeof(pItemList[0]));
if(uPos > 128)
delete(pItemList);
pItemList = pItemNew;
}
pItemList[uPos] = uItem;
pNext = pList[uItem];
uItem = pNext->uNextItem;
uPos++;
} while(uItem);
//************************* Qsort-Algorithmus *********************************
#define XCHANGE_MEM(a,b) uTemp=pItemList[a];pItemList[a]=pItemList[b];pItemList[b]=uTemp;
pData->cLockChanges = 1;
UNLOCK(pData);
pCompare = pSortData->lpfnCompare;
lParamSort = pSortData->lParam;
iLast = uPos - 1;
iStart = 0;
iLevel = 0;
for(;;) {
iLower = iStart;
iMiddle = (iStart + iLast) >> 1; // Mitte bereichnen
iUpper = iLast + 1;
XCHANGE_MEM(iMiddle, iLower);
uItem = pItemList[iStart];
lParamTemp = pList[uItem]->lParam;
for(;;) {
do {
iLower++;
if(iLower > iLast)
break;
uItem = pItemList[iLower];
iCmp = pCompare(pList[uItem]->lParam, lParamTemp, lParamSort);
} while(iCmp <= 0);
do {
iUpper--;
if(iUpper <= iStart)
break;
uItem = pItemList[iUpper];
iCmp = pCompare(pList[uItem]->lParam, lParamTemp, lParamSort);
} while(iCmp >= 0);
if(iUpper < iLower)
break;
XCHANGE_MEM(iUpper, iLower);
}
XCHANGE_MEM(iStart, iUpper);
if(iUpper - 1 - iStart >= iLast - iLower) {
if(iStart + 1 < iUpper) {
uMemL[iLevel] = iStart;
uMemU[iLevel] = iUpper - 1;
iLevel++;
}
if(iLower < iLast) {
iStart = iLower;
continue;
}
} else {
if(iLower < iLast) {
uMemL[iLevel] = iLower;
uMemU[iLevel] = iLast;
iLevel++;
}
if(iStart + 1 < iUpper) {
iLast = iUpper - 1;
continue;
}
}
// Eine Ebene absteigen
iLevel--;
if(iLevel >= 0) { // Noch Ebenen vorhanden
iStart = uMemL[iLevel];
iLast = uMemU[iLevel];
continue;
}
break;
}
LOCK(pData);
pData->cLockChanges = 0;
//******************** Einträge neu einsortirenen *****************************
uPos--;
pEntry = pList[uParent];
if(!pEntry) {
pData->uFirstChild = pItemList[ 0 ];
pData->uLastChild = pItemList[uPos];
} else {
pEntry->uFirstChild = pItemList[ 0 ];
pEntry->uLastChild = pItemList[uPos];
}
uLast = 0;
uItem = pItemList[0];
for(uNum = 0; uNum < uPos;) { // Kinder neu einhängen
pEntry = pList[uItem];
pEntry->uPrevItem = uLast;
uNum++;
uLast = uItem;
uItem = pItemList[uNum];
pEntry->uNextItem = uItem;
}
pEntry = pList[uItem];
pEntry->uPrevItem = uLast;
pEntry->uNextItem = 0;
if(iMode != SORT_NOUPDATE) // Ausgabeliste neuerstellen
if(uParent == 0 || (pParent->uShowPos && (pParent->uState & TVIS_EXPANDED))) {
UpdateItems(pData, uParent);
if(pData->uStyle & TVS_SHOWSELALWAYS)
if(pData->uSelectedItem) {
TreeListEnsureVisible(pData, pData->uSelectedItem, pData->uSelectedSub);
}
}
if(uMax > 128)
delete(pItemList);
return 1;
}
//*****************************************************************************
//*
//* TreeListSortItems
//*
//*****************************************************************************
// Sortiert die Kindereinträge eines Eintrages via Text
// pData : Zeiger auf die Fensterdaten
// pSortData : Ist ein Zeiger auf die Sortiertaten
// iMode : 1=Rekursiv sortieren
// Ergibt 1 wenn ok sonst 0
static int TreeListSortItems(TreeListData *pData, unsigned uParent, int iMode) {
unsigned uNum;
unsigned uItem;
unsigned uLast;
unsigned uFirst;
BaseItem *pParent;
BaseItem *pEntry;
BaseItem *pNext;
BaseItem **pList;
unsigned *pItemList;
unsigned *pItemNew;
unsigned uEnties[128];
unsigned uPos;
unsigned uMax;
LPCTSTR pTextTemp;
LPCTSTR pText;
int iLower, iUpper, iMiddle, iCmp;
int uMemL[30], uMemU[30];
int iStart, iLast;
int iLevel;
int iNone;
unsigned uSize;
unsigned uTemp;
pList = pData->pTreeItems;
if(uParent > pData->uTreeItemsMax) { // Root-Eintrag sortieren
if(uParent != U(TVI_ROOT))
return 0;
uLast = pData->uLastChild;
uFirst = pData->uFirstChild;
if(uFirst == 0)
return 1;
if(uFirst == pData->uLastChild) { // Einzelner Eintrag
pNext = pList[uFirst];
if(!pNext->uFirstChild)
return 1;
TreeListSortItems(pData, uFirst, iMode);
return 1;
}
pList = pData->pTreeItems;
pParent = 0;
uParent = 0;
} else { // Untereintrag sortieren
pParent = pList[uParent];
if(pParent == NULL)
return 0;
uLast = pParent->uLastChild;
uFirst = pParent->uFirstChild;
if(uFirst == 0)
return 1;
if(uFirst == pParent->uLastChild) { // Einzelner Eintrag
pNext = pList[uFirst];
if(!pNext->uFirstChild)
return 1;
TreeListSortItems(pData, uFirst, iMode);
return 1;
}
}
if(iMode) { // Sortiere die Untereinträge
uItem = uFirst;
while(uItem) {
pNext = pList[uItem];
if(pNext->uFirstChild) {
TreeListSortItems(pData, uItem, SORT_NOUPDATE);
}
uItem = pNext->uNextItem;
}
}
if(uLast == uFirst)
return 1;
//******************** Erzeuge Eintragsliste **********************************
uItem = uFirst;
pItemList = uEnties;
uMax = 128;
uPos = 0;
do { // Alle Kindeinträge suchen
if(uPos >= uMax) {
uMax *= 2;
pItemNew = new(unsigned, uMax);
memcpy(pItemNew, pItemList, uPos * sizeof(pItemList[0]));
if(uPos > 128)
delete(pItemList);
pItemList = pItemNew;
}
pItemList[uPos] = uItem;
pNext = pList[uItem];
uItem = pNext->uNextItem;
uPos++;
} while(uItem);
//************************* Qsort-Algorithmus *********************************
#define XCHANGE_MEM(a,b) uTemp=pItemList[a];pItemList[a]=pItemList[b];pItemList[b]=uTemp;
pData->cLockChanges = 1;
UNLOCK(pData);
iLast = uPos - 1;
iStart = 0;
iLevel = 0;
for(;;) {
iLower = iStart;
iMiddle = (iStart + iLast) >> 1; // Mitte bereichnen
iUpper = iLast + 1;
XCHANGE_MEM(iMiddle, iLower);
uItem = pItemList[iStart];
pEntry = pList[uItem];
if(pEntry->bCallback & TVIF_TEXT) {
uSize = 0;
LOCK(pData);
CallbackEntry(pData, pEntry, uItem, TVIF_TEXT, &iNone, &uSize, &pTextTemp);
UNLOCK(pData);
} else {
pTextTemp = pEntry->pText;
}
for(;;) {
do {
iLower++;
if(iLower > iLast)
break;
uItem = pItemList[iLower];
pEntry = pList[uItem];
if(pEntry->bCallback & TVIF_TEXT) {
uSize = 1;
LOCK(pData);
CallbackEntry(pData, pEntry, uItem, TVIF_TEXT, &iNone, &uSize, &pText);
UNLOCK(pData);
} else {
pText = pEntry->pText;
}
iCmp = str_icmp(pText, pTextTemp);
} while(iCmp <= 0);
do {
iUpper--;
if(iUpper <= iStart)
break;
uItem = pItemList[iUpper];
pEntry = pList[uItem];
if(pEntry->bCallback & TVIF_TEXT) {
LOCK(pData);
CallbackEntry(pData, pEntry, uItem, TVIF_TEXT, &iNone, &uSize, &pText);
UNLOCK(pData);
} else {
pText = pEntry->pText;
}
iCmp = str_icmp(pText, pTextTemp);
} while(iCmp >= 0);
if(iUpper < iLower)
break;
XCHANGE_MEM(iUpper, iLower);
}
XCHANGE_MEM(iStart, iUpper);
if(iUpper - 1 - iStart >= iLast - iLower) {
if(iStart + 1 < iUpper) {
uMemL[iLevel] = iStart;
uMemU[iLevel] = iUpper - 1;
iLevel++;
}
if(iLower < iLast) {
iStart = iLower;
continue;
}
} else {
if(iLower < iLast) {
uMemL[iLevel] = iLower;
uMemU[iLevel] = iLast;
iLevel++;
}
if(iStart + 1 < iUpper) {
iLast = iUpper - 1;
continue;
}
}
// Eine Ebene absteigen
iLevel--;
if(iLevel >= 0) { // Noch Ebenen vorhanden
iStart = uMemL[iLevel];
iLast = uMemU[iLevel];
continue;
}
break;
}
LOCK(pData);
pData->cLockChanges = 0;
//******************** Einträge neu einsortirenen *****************************
uPos--;
pEntry = pList[uParent];
if(!pEntry) {
pData->uFirstChild = pItemList[ 0 ];
pData->uLastChild = pItemList[uPos];
} else {
pEntry->uFirstChild = pItemList[ 0 ];
pEntry->uLastChild = pItemList[uPos];
}
uLast = 0;
uItem = pItemList[0];
for(uNum = 0; uNum < uPos;) { // Kinder neu einhängen
pEntry = pList[uItem];
pEntry->uPrevItem = uLast;
uNum++;
uLast = uItem;
uItem = pItemList[uNum];
pEntry->uNextItem = uItem;
}
pEntry = pList[uItem];
pEntry->uPrevItem = uLast;
pEntry->uNextItem = 0;
if(iMode != SORT_NOUPDATE) // Ausgabeliste neuerstellen
if(uParent == 0 || (pParent->uShowPos && (pParent->uState & TVIS_EXPANDED))) {
UpdateItems(pData, uParent);
if(pData->uStyle & TVS_SHOWSELALWAYS)
if(pData->uSelectedItem) {
TreeListEnsureVisible(pData, pData->uSelectedItem, pData->uSelectedSub);
}
}
if(uMax > 128)
delete(pItemList);
return 1;
}
//*****************************************************************************
//*
//* TreeListEndLabelEdit
//*
//*****************************************************************************
// Beendet das editiert eines Eintrages
// pData : Zeiger auf Fensterdaten
// iMode : Gibt an wie das Editieren beendet wurde
// 0 = Es gab es einen Abbruch mit ESC
// 1 = Eingabe ohne Enter
// 2 = Eingabe mit Enter
// Ergibt das Handle des Edit-Controlls oder NULL bei einem Fehler
static int TreeListEndLabelEdit(TreeListData *pData, int iMode) {
TCHAR cText[2052];
NMTVDISPINFO sNotify;
TV_ITEM sSet;
LRESULT lRet;
unsigned uSub;
unsigned uItem;
ExtraItem *pExtra;
BaseItem *pEntry;
LPCTSTR *pList;
LPCTSTR pText;
LPTSTR pGetT;
TCHAR cChar;
int iAuto;
int iIcon;
int iLine;
int iPos;
int iLen;
int iMax;
int iSel;
char cCb;
uItem = pData->uEditItem;
uSub = pData->uEditSub;
cCb = pData->cEditCb;
pData->uEditItem = 0;
pData->uEditSub = 0;
pData->cEditCb = 0;
if(uItem > pData->uTreeItemsMax || uSub >= pData->uColumnCount) {
return 0;
}
pEntry = pData->pTreeItems[uItem];
if(!pEntry) {
return 0;
}
if(iMode) { // Eingabe
GetWindowText(pData->hEdit, cText, sizeof(cText) / sizeof(cText[0]));
cText[sizeof(cText) / sizeof(cText[0]) - 1] = 0;
sNotify.item.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_TEXT | TVIF_SUBITEM;
sNotify.item.cchTextMax = sizeof(cText) / sizeof(cText[0]) - 1;
sNotify.item.pszText = cText;
if(pData->cColumnStart) { // Wurde ein Text eingegeben
sNotify.item.mask |= TVIF_TEXTCHANGED;
}
if(iMode == 2) { // Wurde der Text mit RETURN eingegeben
sNotify.item.mask |= TVIF_RETURNEXIT;
}
} else { // Abbruch
sNotify.item.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_CANCELED | TVIF_SUBITEM;
sNotify.item.pszText = NULL;
sNotify.item.cchTextMax = 0;
}
sNotify.hdr.code = TVN_ENDLABELEDIT;
sNotify.item.hItem = (HTREEITEM)(ULONG_PTR)uItem;
sNotify.item.stateMask = 0xFFFFFFFF;
sNotify.item.cChildren = uSub;
if(uSub) { // Wurde der Text in einer Sub-Spalte geändert
pExtra = pData->pExtraItems[uSub - 1][uItem];
if(!pExtra) {
sNotify.item.state = 0;
sNotify.item.lParam = 0;
} else {
sNotify.item.state = pEntry->uState & TVIS_BASEFLAGS;
sNotify.item.state |= pExtra->uState;
sNotify.item.lParam = pEntry->lParam;
}
} else {
sNotify.item.state = pEntry->uState;
sNotify.item.lParam = pEntry->lParam;
}
UNLOCK(pData);
ShowWindow(pData->hEdit, SW_HIDE);
lRet = SendNotify(pData, &sNotify.hdr);
LOCK(pData);
if(lRet || !iMode)
return 0;
if(cCb) { // Callback aufrufen
sNotify.hdr.code = TVN_SETDISPINFO;
sNotify.item.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_TEXT | TVIF_SUBITEM;
sNotify.item.stateMask = (UINT)~TVIS_BASEFLAGS;
sNotify.item.hItem = (HTREEITEM)(ULONG_PTR)uItem;
sNotify.item.cChildren = uSub;
UNLOCK(pData);
SendNotify(pData, &sNotify.hdr);
LOCK(pData);
} else { // Neuen Text eingeben
pGetT = sNotify.item.pszText;
sSet.mask = TVIF_SUBITEM | TVIF_TEXT;
sSet.cchTextMax = sNotify.item.cchTextMax;
sSet.hItem = (HTREEITEM)(ULONG_PTR)uItem;
sSet.pszText = pGetT;
sSet.cChildren = uSub;
iIcon = pData->aColumn[uSub].iCbIcon;
iAuto = pData->aColumn[uSub].bEdit;
if(iIcon >= 0 && iAuto != TVAX_NONE) { // Auch ein Icon zuweisen
iPos = -1;
if((1 << iAuto) & ((1 << TVAX_CBLIST) | (1 << TVAX_COMBO))) {
iPos = (int)SendMessage(pData->hEdit, CB_GETCURSEL, 0, 0);
sSet.mask |= TVIF_IMAGE | TVIF_SELECTEDIMAGE;
sSet.iImage = iIcon + iPos;
sSet.iSelectedImage = sSet.iImage;
}
if(iPos <= -1) {
iMax = pData->aColumn[uSub].bCbSize;
if(iMax <= 0)
iMax = 1024;
iSel = -1;
if(pData->aColumn[uSub].bFlags & TVAE_PTRLIST) { // Zeigerliste char *pTexte[]={"1","2",NULL};
pList = (LPCTSTR *)pData->aColumn[uSub].pCbData;
for(iPos = 0; iPos < iMax; iPos++) {
if(!pList[iPos])
break;
if(!str_cmp(pGetT, pList[iPos]))
iSel = iPos;
}
} else { // Textliste char *pText="1|2|3";
pText = (LPTSTR)pData->aColumn[uSub].pCbData;
cChar = (TCHAR)pData->aColumn[uSub].bCbChar;
for(iPos = 0; iPos < iMax; iPos++) {
for(iLen = 0; pText[iLen]; iLen++) {
if(pText[iLen] == cChar)
break;
}
if(str_ncmp(pGetT, pText, iLen) == 0 && !pGetT[iLen]) {
iSel = iPos;
break;
}
pText += iLen;
if(pText[0] == cChar)
pText++;
if(pText[0] == 0)
break;
}
}
sSet.mask |= TVIF_IMAGE | TVIF_SELECTEDIMAGE;
sSet.iImage = iIcon + iSel;
sSet.iSelectedImage = sSet.iImage;
}
}
if(pData->aColumn[uSub].bFlags & TVAE_NEXTLINE && // In die nächste Zeile springen
iMode == 2 && iAuto != TVAX_NONE && iAuto != TVAX_STEP) {
if(!pData->cColumnStart) {
if(uSub) { // Hat sich der Text verändert ?
pExtra = pData->pExtraItems[uSub - 1][uItem];
if(pExtra && pExtra->pText) {
if(!_tcscmp(pGetT, pExtra->pText))
return 1;
} else {
if(!*pGetT)
return 1;
}
} else {
pEntry = pData->pTreeItems[uItem];
if(pEntry && pEntry->pText) {
if(!_tcscmp(pGetT, pEntry->pText))
return 1;
} else {
if(!*pGetT)
return 1;
}
}
}
TreeListSetItem(pData, &sSet);
iLine = pEntry->uShowPos;
if(iLine < (int)pData->uItemPosCount) {
uItem = pData->pItemPos[iLine];
TreeListSelectItem(pData, uItem, pData->uSelectedSub, TVC_BYKEYBOARD | TVC_DESELECT);
}
} else {
TreeListSetItem(pData, &sSet);
}
}
return 1;
}
//*****************************************************************************
//*
//* TreeListEditLabel
//*
//*****************************************************************************
// Startet das editiert eines Eintrages
// pData : Zeiger auf Fensterdaten
// uItem : Item das editiert werden soll
// uSub : Spalte die editiert werden soll und andere Flags
// TVIR_EDITCOL(n) = Spalte angeben
// TVIR_SELAREA(a,b) = Einen Textbereich auswählen
// TVIR_SETCURSOR(n) = Den Cursor auf eine bestimmte Textstelle
// TVIR_SETAT(n) = Den Cursor auf eine bestimmte Pixelstelle
// TVIR_SELALL = Den gesammten Text wählen
// TVIR_EDITCOMBOLIST = Statt dem Edit-Fenster eine ComboBox nur mit Listenauswahl einblenden
// TVIR_EDITFULL = Das Edit-Fenster über die volle Breite einblenden
// TVIR_EDITCOMBOBOX = Statt dem Edit-Fenster eine ComboBox einblenden
// Ergibt das Handle des Edit-Controlls oder NULL bei einem Fehler
static HWND TreeListEditLabel(TreeListData *pData, unsigned uItem, unsigned uSub) {
HDC hDc;
HWND hWnd;
LRESULT lRet;
WNDPROC pProc;
char cTemp;
LPARAM lParam;
ExtraItem *pExtra;
BaseItem *pEntry;
NMTVDISPINFO sNotify;
LPCTSTR pText;
RECT sRect;
SIZE sSize;
HFONT hFont;
unsigned uCol;
unsigned uSel;
unsigned uNext;
unsigned uBits;
unsigned uSize;
unsigned uStart;
unsigned uState;
unsigned uFlags;
unsigned uHeight;
int iPixels;
int iWidth;
int iTemp;
uBits = uSub >> 29;
uSel = (uSub >> 8) & 0x0FFFFF;
uSub &= 0xFF;
if(uSub >= pData->uColumnCount)
return NULL;
if(uItem > pData->uTreeItemsMax)
return NULL;
pEntry = pData->pTreeItems[uItem];
if(!pEntry)
return NULL;
if(uItem != pData->uSelectedItem || uSub != pData->uSelectedSub) {
uCol = (pData->uStyleEx & TVS_EX_SUBSELECT) ? uSub : 0;
iTemp = TreeListSelectItem(pData, uItem, uCol, TVC_UNKNOWN);
}
TreeListEnsureVisible(pData, uItem, uSub);
if(pData->hEdit) { // Editfenster löschen
DestroyWindow(pData->hEdit);
pData->hEdit = 0;
}
switch(uBits & 3) { // Editfenster neu erzeugen
case 1:
pData->hEdit = CreateWindow(_T("COMBOBOX"), NULL, WS_BORDER | WS_CHILD | CBS_AUTOHSCROLL | CBS_DROPDOWN | WS_VSCROLL, 0, 0, 0, 0, pData->hWnd, (HMENU)3, NULL, NULL);
pData->uEditMode = 1;
SendMessage(pData->hEdit, CB_LIMITTEXT, 2048, 0);
break;
case 2:
pData->hEdit = CreateWindow(_T("COMBOBOX"), NULL, WS_BORDER | WS_CHILD | CBS_AUTOHSCROLL | CBS_DROPDOWNLIST | WS_VSCROLL, 0, 0, 0, 0, pData->hWnd, (HMENU)3, NULL, NULL);
pData->uEditMode = 2;
SendMessage(pData->hEdit, CB_LIMITTEXT, 2048, 0);
break;
default
:
uFlags = ES_LEFT;
if(uBits & 4) {
if(pData->aColumn[uSub].bAlign == DT_RIGHT)
uFlags = ES_RIGHT;
if(pData->aColumn[uSub].bAlign == DT_CENTER)
uFlags = ES_CENTER;
}
pData->hEdit = CreateWindow(_T("EDIT"), NULL, WS_BORDER | WS_CHILD | ES_AUTOHSCROLL | uFlags, 0, 0, 0, 0, pData->hWnd, (HMENU)3, NULL, NULL);
pData->uEditMode = 0;
SendMessage(pData->hEdit, EM_SETLIMITTEXT, 2048, 0);
break;
}
if(!pData->hEdit)
return NULL;
if(pSetWindowTheme) { // Remove the Visual-Styles (XP+)
pSetWindowTheme(pData->hEdit, L"", L"");
}
pData->pProcId3 = (WNDPROC)GetWindowLongPtr(pData->hEdit, GWLP_WNDPROC);
SetWindowLongPtr(pData->hEdit, GWLP_USERDATA, (LPARAM)pData);
SetWindowLongPtr(pData->hEdit, GWLP_WNDPROC , (LPARAM)EditProc);
hWnd = GetWindow(pData->hEdit, GW_CHILD);
while(hWnd) {
pProc = (WNDPROC)GetWindowLongPtr(hWnd, GWLP_WNDPROC);
SetWindowLongPtr(hWnd, GWLP_USERDATA, (LPARAM)pProc);
SetWindowLongPtr(hWnd, GWLP_WNDPROC , (LPARAM)EditProc);
hWnd = GetNextWindow(hWnd, GW_HWNDNEXT);
}
pData->cEditCb = 0;
if(pData->uEditMode >= 1) { // ComboBox leeren
SendMessage(pData->hEdit, CB_RESETCONTENT, 0, 0);
}
if(uSub == 0) { // Haupteintrag bearbeiten
TreeListGetItemRect(pData, uItem, TVIR_GETCOLUMN | TVIR_TEXT, &sRect);
pText = pEntry->pText;
uSize = pEntry->uTextSize;
iPixels = pEntry->iTextPixels + 10;
lParam = pEntry->lParam;
uState = pEntry->uState;
hFont = (uState & TVIS_BOLD) ? pData->hFontB : pData->hFontN;
if(pEntry->bCallback & TVIF_TEXT) {
CallbackEntry(pData, pEntry, uItem, TVIF_TEXT, &iTemp, &uSize, &pText);
hDc = GetDC(pData->hWnd);
SelectObject(hDc, hFont);
DrawText(hDc, pText, uSize, &sRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX | DT_CALCRECT);
ReleaseDC(pData->hWnd, hDc);
iPixels = sRect.right - sRect.left + 10;
pData->cEditCb = 1;
}
if(uBits & 4) { // Volle Spaltenbreite
if(pEntry->iImage != TV_NOIMAGE) {
uNext = pData->aColumnPos[1];
sRect.right = pData->aColumnXpos[uNext];
sRect.right -= pData->uScrollX;
}
iPixels = sRect.right - sRect.left - 2;
} else {
if(pData->uEditMode) {
if(iPixels < 60)
iPixels = 60;
} else {
if(iPixels < 48)
iPixels = 48;
}
if(pText && *pText) {
iPixels += str_len(pText);
}
if(pData->uEditMode) {
iPixels += GetSystemMetrics(SM_CXHSCROLL);
}
}
} else { // Extraeintrag bearbeiten
if(uBits & 4) {
TreeListGetItemRect(pData, uItem, TVIR_GETCOLUMN | TVIR_COLTOSUB(uSub), &sRect);
if(pData->aColumn[uSub].bEdit >= TVAX_CHECK) {
sRect.left += pData->iChecksXsize;
if(sRect.right < sRect.left)
sRect.right = sRect.left + 1;
}
} else {
TreeListGetItemRect(pData, uItem, TVIR_GETCOLUMN | TVIR_TEXT | TVIR_COLTOSUB(uSub), &sRect);
}
pExtra = pData->pExtraItems[uSub - 1][uItem];
if(!pExtra) {
pData->cTempText1[0] = 0;
pText = pData->cTempText1;
uSize = sizeof(pData->cTempText1) / sizeof(pData->cTempText1[0]);
iPixels = sRect.right - sRect.left + 10;
hFont = pData->hFontN;
uState = pEntry->uState & TVIS_BASEFLAGS;
lParam = 0;
} else {
pText = pExtra->pText;
uSize = pExtra->uTextSize;
iPixels = pExtra->iTextPixels + 10;
lParam = pEntry->lParam;
uState = pExtra->uState;
uState |= pEntry->uState & TVIS_BASEFLAGS;
hFont = (uState & TVIS_BOLD) ? pData->hFontB : pData->hFontN;
if(pExtra->bCallback & TVIF_TEXT) {
CallbackExtra(pData, pEntry, pExtra, uItem, uSub, TVIF_TEXT, &iTemp, &uSize, &pText);
hDc = GetDC(pData->hWnd);
SelectObject(hDc, hFont);
DrawText(hDc, pText, uSize, &sRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX | DT_CALCRECT);
ReleaseDC(pData->hWnd, hDc);
iPixels = sRect.right - sRect.left;
pData->cEditCb = 1;
}
}
if(uBits & 4) { // Volle Spaltenbreite
if(pExtra && pExtra->iImage != TV_NOIMAGE) {
sRect.left += pData->iImagesXsize + 1;
}
iPixels = sRect.right - sRect.left - 2;
} else {
if(pData->uEditMode) {
if(iPixels < 60)
iPixels = 60;
} else {
if(iPixels < 48)
iPixels = 48;
}
if(pText && *pText) {
iPixels += str_len(pText);
}
if(pData->uEditMode) {
iPixels += GetSystemMetrics(SM_CXHSCROLL);
}
switch(pData->aColumn[uSub].bAlign) {
case DT_RIGHT:
iWidth = sRect.right - sRect.left;
sRect.left += iWidth - iPixels;
break;
case DT_CENTER:
iWidth = sRect.right - sRect.left;
sRect.left += (iWidth - iPixels) / 2;
break;
}
}
}
UNLOCK(pData);
sNotify.hdr.code = TVN_BEGINLABELEDIT;
sNotify.item.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_TEXT | TVIF_SUBITEM;
sNotify.item.hItem = (HTREEITEM)(ULONG_PTR)uItem;
sNotify.item.pszText = (LPTSTR)pText;
sNotify.item.lParam = lParam;
sNotify.item.state = uState;
sNotify.item.cchTextMax = uSize;
sNotify.item.stateMask = 0xFFFFFFFF;
sNotify.item.cChildren = uSub;
lRet = SendNotify(pData, &sNotify.hdr);
LOCK(pData);
if(lRet) { // Das Editieren abbrechen
TreeListEndLabelEdit(pData, 0);
return NULL;
}
pEntry = pData->pTreeItems[uItem];
if(!pEntry)
return NULL;
if(pData->uToolTipItem) { // Ein offenes Tooltip verstecken
UpdateToolTip(pData, 0, 0);
}
UNLOCK(pData);
SetFocus(pData->hEdit);
LOCK(pData);
if(pData->uEditMode) {
sRect.top--;
uHeight = 260;
} else {
uHeight = pData->iFontHeight + 4;
}
if(pData->uEditMode) {
if(iPixels < 60)
iPixels = 60;
} else {
if(iPixels < 48)
iPixels = 48;
}
cTemp = pData->cColumnStart;
SetWindowPos(pData->hEdit, HWND_TOP, sRect.left + 2, sRect.top + 1, iPixels, uHeight, SWP_SHOWWINDOW);
SendMessage(pData->hEdit, WM_SETFONT, (WPARAM)hFont, 0);
SetWindowText(pData->hEdit, pText);
pData->cColumnStart = cTemp;
switch(uSel >> 18) { // Welche Textauswahl
case 3:
uStart = (uSel) & 0x01FF; // Einen Textbereich markieren
uSize = (uSel >> 9) & 0x01FF;
break;
case 2:
uStart = uSel & 0x3FFFF; // Cursor auf eine bestimmte Stelle
uSize = uStart;
break;
case 1:
uSel &= 0x3FFFF;
if(uSel > 4)
uSel -= 4;
hDc = GetDC(pData->hEdit);
SelectObject(hDc, hFont);
GetTextExtentExPoint(hDc, pText, uSize, uSel, &iWidth, NULL, &sSize);
if(uSize > 0 && iWidth < (int)uSize - 1) { // Halben Buchstaben addieren
GetTextExtentExPoint(hDc, pText + iWidth, 1, uSel, NULL, NULL, &sSize);
uSel += sSize.cx / 2;
GetTextExtentExPoint(hDc, pText, uSize, uSel, &iWidth, NULL, &sSize);
}
ReleaseDC(pData->hEdit, hDc);
if(sSize.cx <= (int)uSel) {
uStart = 0;
break;
}
uStart = iWidth;
uSize = iWidth;
break;
default
:
uStart = 0; // Alles markieren
break;
}
switch(pData->uEditMode) {
case 0:
SendMessage(pData->hEdit, EM_SETSEL, uStart, uSize);
break;
case 1:
SendMessage(pData->hEdit, CB_SETEDITSEL, 0, MAKELPARAM(uStart, uSize));
break;
default
:
uStart = 0;
uSize = 0xFFFF;
break;
}
RedrawWindow(pData->hEdit, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE);
pData->uLastSel = MAKELPARAM(uStart, uSize);
pData->uEditItem = uItem;
pData->uEditSub = uSub;
return pData->hEdit;
}
//*****************************************************************************
//*
//* TreeListStartAutoEdit
//*
//*****************************************************************************
// Startet die Editierung für einen Wert via Notyfy-Rückfrage.
// pData : Zeiger auf die Fensterdaten
// uItem : Ist der Eintrag der Editiert werden soll
// uSub : Ist die Nummer der Spalte
// wParam : Ist der W-Parameter des Tastendrucks
// VK_EDITCLK bei einem Clickauf das ausgewählte Feld
// VK_DBLCLK bei einem Doppelclick
// VK_RETURN bei einen Enter-Druck
// VK_ISACHAR bei WM_CHAR Nachrichten
// <char> bei einer Zeicheneingabe
// lParam : Ist der L-Parameter des Tastendrucks (bzw. die Koordinaten)
// Ergibt 1 das Editieren gestartet wurde, ansonsten 0
// 0 wenn der Eintrag nicht gewählt wurde
static int TreeListStartNotifyEdit(TreeListData *pData, unsigned uItem, unsigned uSub, WPARAM wParam, LPARAM lParam) {
TCHAR cText[1024];
TV_STARTEDIT sNotify;
ExtraItem *pExtra;
BaseItem *pEntry;
unsigned uBits;
unsigned uCnt;
unsigned uMax;
unsigned uLen;
unsigned uNum;
LPCTSTR pText;
RECT sRect;
TCHAR cChar;
LRESULT lRet;
MSG sMsg;
HWND hWnd;
INT iSel;
if(!(pData->uStyle & TVS_EDITLABELS))
return 0;
if(uItem == 0 || uItem > pData->uTreeItemsMax)
return 0;
if(uSub > 0 && uSub >= pData->uColumnCount)
return 0;
pEntry = pData->pTreeItems[uItem];
if(!pEntry)
return 0;
if(wParam < ' ' && wParam != VK_RETURN) { // Falsche Taste
return 0;
}
if(wParam >= ' ' && wParam <= 0xFFFF) { // Shift und Cltr prüfen
if(GetKeyState(VK_MENU) & 0x8000) {
return 0;
}
if(GetKeyState(VK_CONTROL) & 0x8000) {
return 0;
}
}
sNotify.hdr.code = TVN_STARTEDIT;
sNotify.uAction = (UINT)wParam;
sNotify.item.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_SUBITEM;
sNotify.item.hItem = (HTREEITEM)(ULONG_PTR)uItem;
sNotify.item.stateMask = 0xFFFFFFFF;
sNotify.item.lParam = pEntry->lParam;
sNotify.item.cChildren = uSub;
sNotify.uHeight = 0;
sNotify.pTextEntries = 0;
sNotify.pTextList = 0;
sNotify.uMaxEntries = 255;
sNotify.ptAction.x = LOWORD(lParam);
sNotify.ptAction.y = HIWORD(lParam);
if(uSub) { // Spalte verwenden
pExtra = pData->pExtraItems[uSub - 1][pData->uSelectedItem];
if(pExtra) {
sNotify.item.state = pExtra->uState&~TVIS_BASEFLAGS;
sNotify.item.pszText = pExtra->pText;
sNotify.item.cchTextMax = pExtra->uTextSize;
} else {
sNotify.item.state = 0;
sNotify.item.cchTextMax = 0;
sNotify.item.pszText = _T("");
}
sNotify.item.state |= pEntry->uState & TVIS_BASEFLAGS;
} else { // Haupteintrag
sNotify.item.state = pEntry->uState;
sNotify.item.pszText = pEntry->pText;
sNotify.item.cchTextMax = pEntry->uTextSize;
}
UNLOCK(pData);
lRet = SendNotify(pData, &sNotify.hdr);
LOCK(pData);
if(!lRet)
return 0;
if(wParam == VK_RETURN) {
wParam = VK_DBLCLK;
uBits = 0;
} else
if(wParam == VK_EDITCLK) {
if(pData->uStyleEx & TVS_EX_NOCURSORSET) {
uBits = 0;
} else {
TreeListGetItemRect(pData, uItem, TVIR_COLTOSUB(uSub) | TVIR_GETCOLUMN | TVIR_TEXT, &sRect);
uBits = TVIR_SETAT(LOWORD(lParam) - sRect.left);
}
} else {
uBits = 0;
}
cChar = (TCHAR)((lRet >> 8) & 0xFF);
uBits |= U(lRet)&TVIR_EDITFULL;
if(U(lRet)&TVIR_EDITCOMBOBOX) { // Eine Combobox anzeigen
uBits |= (U(lRet)&TVIR_EDITCOMBOLIST) ? 0x40000000 : 0x20000000;
}
hWnd = TreeListEditLabel(pData, uItem, uSub | uBits);
if(!hWnd)
return 0;
if(lRet & TVIR_EDITCOMBOBOX) { // Die Combobox füllen
uMax = sNotify.uMaxEntries;
iSel = -1;
if(sNotify.pTextList) { // Texte über Listenfeld
for(uCnt = 0; uCnt < uMax; uCnt++) {
pText = sNotify.pTextList[uCnt];
if(!pText)
break;
SendMessage(hWnd, CB_ADDSTRING, 1, (LPARAM)pText);
if(sNotify.item.pszText && !_tcscmp(pText, sNotify.item.pszText)) {
iSel = uCnt;
}
}
} else { // Text mit
pText = sNotify.pTextEntries;
if(!pText)
pText = _T("\0");
for(uCnt = 0; uCnt < uMax; uCnt++) {
for(uLen = 0; pText[uLen]; uLen++) { // Subtextlänge holen
if(pText[uLen] == cChar)
break;
}
if(!uLen && !pText[uLen])
break;
uNum = uLen;
if(uNum >= sizeof(cText) / sizeof(TCHAR)) {
uNum = sizeof(cText) / sizeof(TCHAR) - 1;
}
memcpy(cText, pText, uNum * sizeof(TCHAR));
cText[uNum] = 0;
SendMessage(hWnd, CB_ADDSTRING, 1, (LPARAM)cText);
if(sNotify.item.pszText && wParam >= 0x10000 && !_tcscmp(cText, sNotify.item.pszText)) {
iSel = uCnt;
}
pText += uLen;
if(!cChar || *pText)
pText++;
}
if(lRet & TVIR_EDITCOMBODEL) { // Den Puffer löschen
delete((TCHAR*)sNotify.pTextEntries);
}
}
if(iSel >= 0) { // Listeneintrag auswählen
SendMessage(hWnd, CB_SETCURSEL, iSel, 0);
}
if(sNotify.uHeight) { // Höhe der Dropdown-Liste einstellen
SendMessage(hWnd, CB_SETDROPPEDWIDTH, sNotify.uHeight, 0);
}
if(lRet & TVIR_EDITCOMBODOWN) { // Dropdown-Liste einblenden
SendMessage(hWnd, CB_SHOWDROPDOWN, 1, 0);
}
if(!(lRet & TVIR_EDITCOMBOLIST)) { // Textauswahl wiederherstellen
SetWindowText(hWnd, sNotify.item.pszText);
SendMessage(hWnd, CB_SETEDITSEL, 0, pData->uLastSel);
}
}
if(wParam < 0x10000) { // Taste an Fenster senden
pData->cColumnStart = 1;
sMsg.hwnd = hWnd;
sMsg.lParam = lParam;
sMsg.wParam = wParam;
sMsg.message = WM_KEYDOWN;
TranslateMessage(&sMsg);
} else
if(wParam & VK_ISACHAR) {
SendMessage(hWnd, WM_CHAR, wParam & 0xFFFF, lParam);
} else {
pData->cColumnStart = 0;
}
return 1;
}
//*****************************************************************************
//*
//* TreeListStartAutoEdit
//*
//*****************************************************************************
// Startet die Autoeditierung für eine Spalte
// pData : Zeiger auf die Fensterdaten
// uColumn : Ist die Nummer der Spalte
// wParam : Ist der W-Parameter des Tastendrucks
// VK_EDITCLK bei einem Clickauf das ausgewählte Feld
// VK_ICONCLK bei einem Clickauf das Iion
// VK_DBLCLK bei einem Doppelclick
// lParam : Ist der L-Parameter des Tastendrucks (bzw. die Koordinaten)
// Ergibt 1 das Editieren gestartet wurde, ansonsten 0
// 0 wenn der Eintrag nicht gewählt wurde
static int TreeListStartAutoEdit(TreeListData *pData, unsigned uColumn, WPARAM wParam, LPARAM lParam) {
TCHAR cBuffer[256];
NMTREEVIEW sNotify;
ExtraItem *pExtra;
BaseItem *pEntry;
LPTSTR *pList;
LPTSTR pText;
TV_ITEM sItem;
RECT sRect;
TCHAR cChar;
unsigned uMode;
unsigned uBits;
unsigned uFlag;
unsigned uMax;
unsigned uPos;
unsigned uLen;
unsigned uSub;
HWND hWnd;
int iSel;
int iRet;
int iIcon;
if(pData->uEditItem)
return 0;
if(pData->uColumnCount <= uColumn)
return 0;
if(!(pData->uStyle & TVS_EDITLABELS))
return 0;
uBits = pData->aColumn[uColumn].bFlags;
uMode = pData->aColumn[uColumn].bEdit;
if(uMode == 0)
return 0;
if(uBits & TVAE_STATEENABLE) { // Kann das Editieren gesperrt werden
if(uColumn) {
pExtra = pData->pExtraItems[uColumn - 1][pData->uSelectedItem];
if(pExtra && (pExtra->uState & TVIS_DISABLEBIT))
return 0;
} else {
pEntry = pData->pTreeItems[pData->uSelectedItem];
if(!pEntry || (pEntry->uState & TVIS_DISABLEBIT))
return 0;
}
}
if(wParam != VK_RETURN && wParam < 0x10000) { // Zeicheneingabe
if(!TVIS_EDIT(uMode))
return 0;
if(uBits & TVAE_ONLYRETURN)
return 0;
if(wParam <= ' ')
return 0;
pData->cColumnStart = 1;
} else {
pData->cColumnStart = 0;
}
uMax = pData->aColumn[uColumn].bCbSize;
if(!uMax)
uMax = 1024;
//******************** Weiterschalten mit Return ******************************
if(uMode >= TVAX_STEP) {
sItem.mask = TVIF_TEXT | TVIF_SUBITEM | TVIF_TEXTPTR | ((uMode < TVAX_CHECK) ? 0 : TVIF_STATE | TVIF_PARAM);
sItem.stateMask = 0xFFFFFFFF;
sItem.hItem = (HTREEITEM)(ULONG_PTR)pData->uSelectedItem;
sItem.cChildren = uColumn;
if(!TreeListGetItem(pData, &sItem))
return 0;
if((uMode & 1) && wParam != VK_ICONCLK) { // Ein Edit-Feld öffnen
uMode = TVAX_EDIT;
goto EditField;
}
if(uMode >= TVAX_CHECK) { // State-Bits bei Checkboxen setzen
if(uBits & TVAE_STATEENABLE)
uFlag = sItem.state & (~TVIS_STATEIMAGEMASK | TVIS_DISABLEBIT);
else
uFlag = sItem.state & (~TVIS_STATEIMAGEMASK);
if(pData->uStyleEx & TVS_EX_BITCHECKBOX)
sItem.state = (sItem.state ^ 0x1000);
else
sItem.state = (sItem.state & 0x1000) ? 0x2000 : 0x1000;
sItem.state |= uFlag;
} else {
sItem.mask &= ~TVIF_STATE;
}
if(uBits & TVAE_PTRLIST) { // Zeigerliste char *pTexte[]={"1","2",NULL};
pList = (LPTSTR *)pData->aColumn[uColumn].pCbData;
if(!pList) {
if(uMode < TVAX_CHECK)
return 0;
sItem.mask &= ~TVIF_TEXT;
pText = sItem.pszText;
uPos = 0;
goto NoTextChange;
}
for(uPos = 0;; uPos++) {
if(uPos >= uMax || !pList[uPos]){
uPos = 0; break;
}
if(str_cmp(sItem.pszText, pList[uPos]))
continue;
uPos++;
if(uPos >= uMax || !pList[uPos])
uPos = 0;
break;
}
pText = pList[uPos];
if(!pText) {
if(uMode < TVAX_CHECK)
return 0;
sItem.mask &= ~TVIF_TEXT;
pText = sItem.pszText;
}
} else { // Textliste char *pText="1|2|3";
pText = (LPTSTR)pData->aColumn[uColumn].pCbData;
cChar = (TCHAR)pData->aColumn[uColumn].bCbChar;
sItem.cchTextMax--;
if(!pText) {
if(uMode < TVAX_CHECK)
return 0;
sItem.mask &= ~TVIF_TEXT;
pText = sItem.pszText;
uPos = 0;
goto NoTextChange;
}
for(uPos = 0;; uPos++) {
if(uPos >= uMax) {
pText = (LPTSTR)pData->aColumn[uColumn].pCbData;
uPos = 0;
break;
}
for(uLen = 0; pText[uLen]; uLen++) { // Subtextlänge bestimmen
if(pText[uLen] == cChar)
break;
}
if(sItem.cchTextMax == (int)uLen) // Ist der Subtext der aktulle Text
if(str_ncmp(sItem.pszText, pText, uLen) == 0) {
uPos++;
if(uPos >= uMax) {
pText = (LPTSTR)pData->aColumn[uColumn].pCbData;
uPos = 0;
break;
}
pText += uLen;
if(pText[0] == cChar)
pText++;
if(pText[0] == 0) {
pText = (LPTSTR)pData->aColumn[uColumn].pCbData;
uPos = 0;
}
break;
}
pText += uLen;
if(pText[0] == cChar)
pText++;
if(pText[0] == 0) {
pText = (LPTSTR)pData->aColumn[uColumn].pCbData;
uPos = 0;
break;
}
}
for(uLen = 0; pText[uLen] && uLen < 256; uLen++) { // Ausgewählten Text kopiren
if(pText[uLen] == cChar)
break;
cBuffer[uLen] = pText[uLen];
}
cBuffer[uLen] = 0;
pText = cBuffer;
}
NoTextChange:
sItem.mask &= ~TVIF_TEXTPTR;
sItem.stateMask &= TVIS_STATEIMAGEMASK;
sItem.hItem = (HTREEITEM)(ULONG_PTR)pData->uSelectedItem;
sItem.pszText = pText;
sItem.cchTextMax = 256;
sItem.cChildren = uColumn;
iIcon = pData->aColumn[uColumn].iCbIcon;
if(iIcon >= 0) { // Auch ein Icon zuweisen
sItem.mask |= TVIF_IMAGE | TVIF_SELECTEDIMAGE;
sItem.iImage = iIcon + uPos;
sItem.iSelectedImage = sItem.iImage;
}
iRet = TreeListSetItem(pData, &sItem);
sNotify.hdr.code = (uMode < TVAX_CHECK) ? TVN_STEPSTATECHANGED : TVN_CBSTATECHANGED;
sNotify.action = (UINT)wParam;
sNotify.itemNew.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_TEXT | TVIF_SUBITEM;
sNotify.itemNew.stateMask = 0xFFFFFFFF;
sNotify.itemNew.hItem = sItem.hItem;
sNotify.itemNew.state = sItem.state;
sNotify.itemNew.lParam = sItem.lParam;
sNotify.itemNew.pszText = sItem.pszText;
sNotify.itemNew.cchTextMax = sItem.cchTextMax;
sNotify.itemNew.cChildren = uColumn;
sNotify.itemOld.mask = 0;
sNotify.ptDrag.x = LOWORD(lParam);
sNotify.ptDrag.y = HIWORD(lParam);
UNLOCK(pData);
SendNotify(pData, &sNotify.hdr);
LOCK(pData);
return iRet;
}
//******************** Edit oder Combobox *************************************
EditField:
uSub = (uMode - 1) << 29;
uSub |= uColumn;
if(uBits & TVAE_FULLWIDTH) {
uSub |= TVIR_EDITFULL;
}
if(wParam == VK_EDITCLK) // Cursor auf Klickposition
if(!(pData->uStyleEx & TVS_EX_NOCURSORSET)) {
TreeListGetItemRect(pData, pData->uSelectedItem, TVIR_COLTOSUB(uColumn) | TVIR_GETCOLUMN | TVIR_TEXT, &sRect);
uSub |= TVIR_SETAT(LOWORD(lParam) - sRect.left);
}
hWnd = TreeListEditLabel(pData, pData->uSelectedItem, uSub);
if(!hWnd)
return 0;
if(uMode != TVAX_EDIT) {
iSel = (uMode != TVAX_CBLIST) ? -1 : 0;
sItem.mask = TVIF_TEXT | TVIF_SUBITEM | TVIF_TEXTPTR;
sItem.hItem = (HTREEITEM)(ULONG_PTR)pData->uSelectedItem;
sItem.cChildren = uColumn;
if(!TreeListGetItem(pData, &sItem))
return 0;
if(uBits & TVAE_PTRLIST) { // Zeigerliste char *pTexte[]={"1","2",NULL};
pList = (LPTSTR *)pData->aColumn[uColumn].pCbData;
for(uPos = 0; uPos < uMax; uPos++) {
if(!pList[uPos])
break;
if(!str_cmp(sItem.pszText, pList[uPos]))
iSel = uPos;
SendMessage(hWnd, CB_ADDSTRING, 0, (LPARAM)pList[uPos]);
}
} else { // Textliste char *pText="1|2|3";
pText = (LPTSTR)pData->aColumn[uColumn].pCbData;
cChar = (TCHAR)pData->aColumn[uColumn].bCbChar;
sItem.cchTextMax--;
for(uPos = 0; uPos < uMax; uPos++) {
for(uLen = 0; pText[uLen]; uLen++) {
if(pText[uLen] == cChar)
break;
}
if(sItem.cchTextMax == (int)uLen)
if(str_ncmp(sItem.pszText, pText, uLen) == 0) {
iSel = uPos;
}
if(cChar) {
if(uLen < 256){
memcpy(cBuffer, pText, sizeof(TCHAR)*uLen);
cBuffer[uLen] = 0;
}
else {
memcpy(cBuffer, pText, sizeof(TCHAR) * 255);
cBuffer[255 ] = 0;
}
SendMessage(hWnd, CB_ADDSTRING, 0, (LPARAM)cBuffer);
} else {
SendMessage(hWnd, CB_ADDSTRING, 0, (LPARAM)pText);
}
pText += uLen;
if(pText[0] == cChar)
pText++;
if(pText[0] == 0)
break;
}
}
if(iSel >= 0) {
SendMessage(hWnd, CB_SETCURSEL, iSel, 0);
}
}
if((uBits & TVAE_DROPDOWN) && (uMode&~1) == 2) { // Dropdownliste aufklappen
SendMessage(hWnd, CB_SHOWDROPDOWN, 1, 0);
}
// Ersten Buchstaben ans Fenster übergeben
if(TVIS_EDIT(uMode) && pData->cColumnStart && wParam != VK_EDITCLK) {
SendMessage(hWnd, WM_CHAR, wParam, 0);
}
return 1;
}
//*****************************************************************************
//*
//* TreeListProc
//*
//*****************************************************************************
// Ist die Fensterfunktion für das TreeList Fenster
static LRESULT CALLBACK TreeListProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
TreeListData *pData;
MSG *pMsg;
LPARAM lRet;
TV_HITTESTINFO sInfo;
POINT sPoint;
SCROLLINFO sScroll;
unsigned uChange;
unsigned uDelta;
unsigned uTime;
unsigned uFlag;
unsigned uMode;
unsigned uVal;
unsigned uOld;
int iDif;
int iPos;
int iMax;
HDC hDc;
switch(uMsg) {
case WM_CREATE: // Das Fenster erzeugen
pData = new(TreeListData, 1);
if(!pData)
return -1;
memset(pData, 0, sizeof(TreeListData));
pData->pToolTipText = new(TCHAR, 256);
pData->uToolTipSize = 256;
if(!pData->pToolTipText) {
delete(pData);
return -1;
}
pData->pTreeItems = new(BaseItem*, 1);
if(!pData->pTreeItems) {
delete(pData->pToolTipText);
delete(pData);
return -1;
}
pData->pItemPos = new(unsigned, 1);
if(!pData->pItemPos) {
delete(pData->pToolTipText);
delete(pData->pTreeItems);
delete(pData);
return -1;
}
GlobalInit();
pData->pItemPos [0] = 0;
pData->pTreeItems[0] = NULL;
SetWindowLongPtr(hWnd, 0, (LONG_PTR)pData);
pData->iIndent = DEFAULT_IDENT;
pData->iShift = DEFAULT_SHIFT;
pData->uStyle = GetWindowLong(hWnd, GWL_STYLE);
pData->hSem = CreateSemaphore(0, 1, 0x70000000, 0);
pData->hWnd = hWnd;
pData->cIsEnabled = (char)IsWindowEnabled(hWnd);
pData->iAutoAdd = 1;
pData->aColumnPos[0] = 0;
pData->aColumnPos[1] = 1;
if(!(pData->uStyle & (TVS_HASBUTTONS | TVS_HASLINES))) {
pData->cHasRootRow = 0;
} else {
pData->cHasRootRow = (char)((pData->uStyle & TVS_LINESATROOT) ? 1 : 0);
}
if(!(pData->uStyle & TVS_NOTOOLTIPS)) {
CreateToolTip(pData);
}
UpdateFont(pData);
UpdateHeight(pData);
UpdateColorsList(pData);
UpdateScrollY(pData);
if(pOpenThemeData) // Soll ein Thema angezeigt werden
pData->hTheme = pOpenThemeData(hWnd, L"TREEVIEW");
else
pData->hTheme = NULL;
pData->cGlyphOk = (char)((pData->hTheme) ? 1 : 0);
if(pData->uStyle & TVS_CHECKBOXES) {
CreateStateImageList(pData, 0);
}
return 0;
case WM_DESTROY: // Das Fenster zerstören
pData = GetHandle(hWnd);
LOCK(pData);
TreeListDeleteItem(pData, U(TVI_ROOT), 0);
if(pData->hStates == THEMEIMGLIST)
pData->hStates = 0;
if(pData->hChecks == THEMEIMGLIST)
pData->hChecks = 0;
if(pData->hEdit){
DestroyWindow(pData->hEdit);
pData->hEdit = 0;
}
if(pData->hHeader){
DestroyWindow(pData->hHeader);
pData->hHeader = 0;
}
if(pData->hToolTip){
DestroyWindow(pData->hToolTip);
pData->hToolTip = 0;
}
if(pData->hStates){
ImageList_Destroy(pData->hStates);
pData->hStates = 0;
}
if(pData->uStyleEx & TVS_EX_SHAREIMAGELISTS) {
if(pData->hStates && pData->iStatesMode) {
ImageList_Destroy(pData->hStates);
pData->hStates = 0;
}
if(pData->hChecks && pData->iChecksMode) {
ImageList_Destroy(pData->hChecks);
pData->hChecks = 0;
}
pData->hImages = 0;
pData->hSubImg = 0;
pData->hHeadImg = 0;
} else {
if(pData->hStates){
ImageList_Destroy(pData->hStates);
pData->hStates = 0;
}
if(pData->hChecks){
ImageList_Destroy(pData->hChecks);
pData->hChecks = 0;
}
if(pData->hImages){
ImageList_Destroy(pData->hImages);
pData->hImages = 0;
}
if(pData->hSubImg){
ImageList_Destroy(pData->hSubImg);
pData->hSubImg = 0;
}
if(pData->hHeadImg){
if(pData->uStyleEx & TVS_EX_HEADEROWNIMGLIST)
ImageList_Destroy(pData->hHeadImg);
pData->hHeadImg = 0;
}
}
if(pData->hThemeBt){
pCloseThemeData(pData->hThemeBt);
pData->hThemeBt = 0;
}
if(pData->hTheme){
pCloseThemeData(pData->hTheme);
pData->hTheme = 0;
}
for(uVal = 1; uVal < pData->uColumnCount; uVal++) {
delete(pData->pExtraItems[uVal - 1]);
pData->pExtraItems[uVal - 1] = 0;
}
if(pData->hFontB){
DeleteObject(pData->hFontB);
pData->hFontB = 0;
}
pData->uColumnCount = 0;
UNLOCK(pData);
return 0;
case WM_NCDESTROY: // Das Fenster zerstören
pData = GetHandle(hWnd);
LOCK(pData);
delete(pData->pToolTipText);
delete(pData->pTreeItems);
delete(pData->pItemPos);
UNLOCK(pData);
SetWindowLongPtr(hWnd, 0, 0);
CloseHandle(pData->hSem);
memset(pData, 0, sizeof(TreeListData));
delete(pData);
GlobalDeinit();
return 0;
case WM_SHOWWINDOW: // Fenster ein/ausblenden
if(wParam) {
pData = GetHandle(hWnd);
sScroll.cbSize = sizeof(SCROLLINFO);
sScroll.fMask = SIF_ALL;
sScroll.nMin = 0;
sScroll.nMax = 0;
sScroll.nPage = 0;
sScroll.nPos = 0;
sScroll.nTrackPos = 0;
SetScrollInfo(pData->hWnd, SB_VERT, &sScroll, TRUE);
SetScrollInfo(pData->hWnd, SB_HORZ, &sScroll, TRUE);
pData->uOldXCount = 0xFFFF;
pData->uOldYCount = 0xFFFF;
UpdateScrollY(pData);
UpdateScrollX(pData);
}
return 0;
case WM_SIZE: // Die Fenstergröße wurde verändert
pData = GetHandle(hWnd);
uFlag = 0;
LOCK(pData);
uVal = LOWORD(lParam);
if(uVal && uVal != pData->uSizeX) {
uOld = pData->uSizeX;
pData->uSizeX = uVal;
if(pData->uColumnCountVar) { // Spalten mit variabler Breite nach führen
RECT sRect;
int iDelta;
int iNum;
iDelta = uVal - uOld;
ChangeColSize(pData, iDelta);
iNum = UpdateColumns(pData);
GetClientRect(hWnd, &sRect);
sRect.left = iNum;
sRect.left -= pData->uScrollX;
sRect.top = pData->uStartPixel;
InvalidateRect(hWnd, &sRect, FALSE);
}
if(uVal > uOld) { // Hat sich die Breite vergrößert
RECT sRect;
GetClientRect(hWnd, &sRect);
sRect.right = uVal;
sRect.left = uOld;
InvalidateRect(hWnd, &sRect, FALSE);
}
pData->aColumnXpos[pData->uColumnCount + 1] = uVal + 1;
MoveWindow(pData->hHeader, -(int)pData->uScrollX, 0, uVal + pData->uScrollX, pData->uStartPixel, uVal > uOld);
UpdateScrollX(pData);
}
uVal = HIWORD(lParam);
if(uVal && uVal != pData->uSizeY) {
if(uVal > pData->uSizeY) { // Hat sich die Höhe vergrößert
RECT sRect;
GetClientRect(hWnd, &sRect);
uFlag = 1;
sRect.bottom = uVal;
sRect.top = pData->uSizeY;
InvalidateRect(hWnd, &sRect, FALSE);
} else {
uFlag = 0;
}
pData->uSizeY = uVal;
pData->uSizeYsub = (uVal <= pData->uStartPixel) ? 0 : uVal - pData->uStartPixel;
if(pData->uSizeY > pData->uStartPixel) {
pData->uMaxEnties = pData->uSizeY;
pData->uMaxEnties -= pData->uStartPixel;
} else {
pData->uMaxEnties = 0;
}
pData->uPageEnties = pData->uMaxEnties;
pData->uMaxEnties += pData->iRowHeight - 1;
pData->uMaxEnties /= pData->iRowHeight;
pData->uPageEnties /= pData->iRowHeight;
// Wenn Höhe vergrößert dann Scroll-Position prüfen
if(uFlag && pData->uPageEnties > 2 && pData->uScrollY > 0) {
if(pData->uScrollY + pData->uPageEnties + 1 > pData->uItemPosCount) {
iPos = pData->uItemPosCount - pData->uPageEnties + 1;
if(iPos < 0)
iPos = 0;
if(U(iPos) != pData->uScrollY) {
pData->uScrollY = iPos;
UpdateView(pData);
}
}
}
UpdateScrollY(pData);
}
UNLOCK(pData);
if(uFlag) {
RedrawWindow(pData->hHeader, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE);
}
return 0;
case WM_ENABLE: // Das Fenster feischalten oder sperren
pData = GetHandle(hWnd);
if(pData->cIsEnabled != ((U(wParam)) ? 1 : 0)) {
uVal = GetWindowLong(hWnd, GWL_STYLE);
uVal &= (0xFFFF0000 & ~WS_DISABLED);
uVal |= (0x0000FFFF | WS_DISABLED) & (pData->uStyle ^ WS_DISABLED);
SetWindowLong(hWnd, GWL_STYLE, uVal);
}
return 0;
case WM_SETFOCUS: // Das Fenster bekommt den Focus
pData = GetHandle(hWnd);
if(!pData->cHasFocus) {
NMHDR sNotify;
LOCK(pData);
pData->cHasFocus = 1;
if(pData->uSelectedCount <= 1) {
if(pData->uSelectedItem) {
if(pData->uStyleEx & TVS_EX_FULLROWMARK)
UpdateRow(pData, pData->uSelectedItem);
else
UpdateRect(pData, pData->uSelectedItem, pData->uSelectedSub);
}
} else {
UpdateView(pData);
}
UNLOCK(pData);
sNotify.code = NM_SETFOCUS;
SendNotify(pData, &sNotify);
}
return 0;
case WM_KILLFOCUS: // Das Fenster bekommt den Focus
pData = GetHandle(hWnd);
if(pData->cHasFocus) {
NMHDR sNotify;
LOCK(pData);
pData->cHasFocus = 0;
if(pData->uSelectedCount <= 1) {
if(pData->uSelectedItem) {
if(pData->uStyleEx & TVS_EX_FULLROWMARK)
UpdateRow(pData, pData->uSelectedItem);
else
UpdateRect(pData, pData->uSelectedItem, pData->uSelectedSub);
}
} else {
UpdateView(pData);
}
UNLOCK(pData);
sNotify.code = NM_KILLFOCUS;
SendNotify(pData, &sNotify);
}
return 0;
case WM_MOUSEMOVE: // Gab es eine Mausbewegung
pData = GetHandle(hWnd);
sInfo.hItem = 0;
sInfo.flags = 0;
if(!(pData->uStyle & TVS_NOTOOLTIPS)) {
if(!pData->cIsEnabled) { // Ist das Fenster freigegeben
return 0;
}
LOCK(pData);
sInfo.pt.x = LOWORD(lParam);
sInfo.pt.y = HIWORD(lParam);
TreeListHitTest(pData, &sInfo);
UpdateToolTip(pData, U(sInfo.hItem), sInfo.flags);
UNLOCK(pData);
}
if(pData->uStyle & TVS_TRACKSELECT) {
if(!pData->cIsEnabled) { // Ist das Fenster freigegeben
return 0;
}
LOCK(pData);
if(!sInfo.hItem) {
sInfo.pt.x = LOWORD(lParam);
sInfo.pt.y = HIWORD(lParam);
TreeListHitTest(pData, &sInfo);
}
if(sInfo.hItem && (sInfo.flags & (TVHT_ONSUBITEM | TVHT_ONSUBLABEL | TVHT_ONITEM))) {
TreeListSetTrackItem(pData, U(sInfo.hItem), TVHT_SUBTOCOL(sInfo.flags));
}
UNLOCK(pData);
}
if(wParam & pData->uDragFlags) // Drag beginnen
if(U(lParam) != pData->uLastMove) {
NMTREEVIEW sNotify;
BaseItem *pEntry;
int iDiffX;
int iDiffY;
iDiffX = (short)(lParam) - (short)(pData->uLastMove);
iDiffY = (short)(lParam >> 16) - (short)(pData->uLastMove >> 16);
if(iDiffX < 0)
iDiffX = -iDiffX;
if(iDiffY < 0)
iDiffY = -iDiffY;
if(iDiffX <= 2 && iDiffY <= 2) { // Mehr als zwei Pixel-Verschub
return 0;
}
LOCK(pData);
if(pData->uDragItem > pData->uTreeItemsMax) {
UNLOCK(pData);
return 0;
}
pEntry = pData->pTreeItems[pData->uDragItem];
if(!pEntry) {
UNLOCK(pData);
return 0;
}
sNotify.hdr.code = TVN_BEGINDRAG;
sNotify.action = (pEntry->uState & TVIS_EXPANDED) ? TVE_COLLAPSE : TVE_EXPAND;
sNotify.itemNew.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_SUBNUMBER;
sNotify.itemNew.hItem = (HTREEITEM)(ULONG_PTR)pData->uDragItem;
sNotify.itemNew.stateMask = 0xFFFFFFFF;
sNotify.itemNew.state = pEntry->uState;
sNotify.itemNew.lParam = pEntry->lParam;
sNotify.itemNew.cChildren = pData->uDragSub;
sNotify.itemNew.pszText = (LPTSTR) - 1;
sNotify.itemNew.cchTextMax = -1;
sNotify.itemOld.mask = 0;
sNotify.ptDrag.x = LOWORD(lParam);
sNotify.ptDrag.y = HIWORD(lParam);
pData->uDragFlags = 0;
pData->cClickFlag = 0;
pData->cClickEdit = 0;
UNLOCK(pData);
SendNotify(pData, &sNotify.hdr);
}
pData->uLastMove = U(lParam);
return 0;
case WM_LBUTTONUP: // Linken Mausklick aufheben
pData = GetHandle(hWnd);
pData->uDragFlags &= ~MK_LBUTTON;
if(pData->cClickFlag || pData->cClickEdit) {
TreeListMouseClick(pData, uMsg, wParam, lParam);
pData->cClickFlag = 0;
pData->cClickEdit = 0;
} else {
TreeListMouseNotify(pData, TVN_LBUTTONUP, wParam, lParam);
}
return 0;
case WM_RBUTTONUP: // Linken Mausklick aufheben
pData = GetHandle(hWnd);
pData->uDragFlags &= ~MK_RBUTTON;
TreeListMouseNotify(pData, TVN_RBUTTONUP, wParam, lParam);
break;
case WM_LBUTTONDOWN: // Mausklick
pData = GetHandle(hWnd);
LOCK(pData);
uTime = GetTickCount();
if(pData->cButtonFlag && pData->uToolTipItem) { // Doppelklick simulieren über ToolTip
pData->cButtonFlag = 0;
uDelta = uTime - pData->uButtonLast;
if(uDelta < 700) {
iPos = abs(LOWORD(lParam) - LOWORD(pData->uButtonPos));
iPos += abs(HIWORD(lParam) - HIWORD(pData->uButtonPos));
if(iPos <= 6) {
TreeListMouseClick(pData, WM_LBUTTONDBLCLK, wParam, lParam);
return 0;
}
}
}
pData->cButtonFlag = 1;
pData->uButtonLast = uTime;
pData->uButtonPos = U(lParam);
TreeListMouseClick(pData, uMsg, wParam, lParam);
return 0;
case WM_LBUTTONDBLCLK:
case WM_RBUTTONDOWN:
case WM_RBUTTONDBLCLK:
pData = GetHandle(hWnd);
LOCK(pData);
pData->cButtonFlag = 0;
TreeListMouseClick(pData, uMsg, wParam, lParam);
return 0;
case WM_KEYDOWN: // Tastendruck
pData = GetHandle(hWnd);
LOCK(pData);
TreeListKeyDown(pData, wParam, lParam);
return 0;
case WM_KEYUP: // Tastendruck
DefWindowProc(hWnd, uMsg, wParam, lParam);
return 1;
case WM_CHAR: // Zeicheneingabe
pData = GetHandle(hWnd);
iMax = lParam & 0xFFFF;
for(iPos = 0; iPos < iMax; iPos++) {
TreeListChar(pData, (UINT)wParam, lParam);
}
return 0;
case WM_DRAWITEM: // Weietleiten von OwnerDraw des Headers
if(wParam == 1) {
pData = GetHandle(hWnd);
return SendMessage(GetParent(hWnd), uMsg, GetWindowLong(hWnd, GWL_ID), lParam);
}
return 0;
case WM_MOUSEWHEEL: // Scrollen mit dem Mausrad
if(!(LOWORD(wParam) & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON))) {
pData = GetHandle(hWnd);
if(pData->uEditItem)
return 0;
iDif = (short)HIWORD(wParam) / (WHEEL_DELTA / 2);
if(wParam & MK_CONTROL) {
iDif *= 9;
} else
if(wParam & MK_SHIFT) {
iDif *= 3;
}
iPos = pData->uScrollY - iDif;
iMax = pData->uItemPosCount;
iMax -= pData->uPageEnties - 1;
if(iPos >= iMax)
iPos = iMax;
if(iPos < 0)
iPos = 0;
if(iPos != (int)pData->uScrollY)
if(!(pData->uStyle & TVS_NOSCROLL)) {
pData->uScrollY = iPos;
SetScrollPos(hWnd, SB_VERT, iPos, TRUE);
UpdateView(pData);
}
return 1;
}
return 0;
case WM_HSCROLL: // Vertikalles scrollen
pData = GetHandle(hWnd);
iPos = pData->uScrollX;
if(pData->uEditItem)
return 0;
switch(LOWORD(wParam)) {
case SB_LINEDOWN:
iPos += 16;
break;
case SB_LINEUP:
iPos -= 16;
break;
case SB_PAGEDOWN:
iPos += pData->uSizeX;
break;
case SB_PAGEUP:
iPos -= pData->uSizeX;
break;
case SB_THUMBPOSITION:
iPos = HIWORD(wParam);
break;
case SB_THUMBTRACK:
iPos = HIWORD(wParam);
break;
}
uVal = pData->uColumnCount;
if(uVal)
iMax = pData->aColumnXpos[uVal] - pData->uSizeX / 2;
else
iMax = pData->iMaxSizeX;
iMax -= pData->uSizeX / 2;
if(iPos >= iMax)
iPos = iMax;
if(iPos < 0)
iPos = 0;
if(iPos != (int)pData->uScrollX) {
pData->uScrollX = iPos;
SetScrollPos(hWnd, SB_HORZ, iPos, TRUE);
UpdateView(pData);
if(pData->hHeader) {
MoveWindow(pData->hHeader, -iPos, 0, pData->uSizeX + iPos, pData->uStartPixel, TRUE);
}
}
return 0;
case WM_VSCROLL: // Vertikalles scrollen
pData = GetHandle(hWnd);
iPos = pData->uScrollY;
if(pData->uEditItem)
return 0;
switch(LOWORD(wParam)) {
case SB_LINEDOWN:
iPos++;
break;
case SB_LINEUP:
iPos--;
break;
case SB_PAGEDOWN:
iPos += (pData->uPageEnties > 1) ? pData->uPageEnties - 1 : 1;
break;
case SB_PAGEUP:
iPos -= (pData->uPageEnties > 1) ? pData->uPageEnties - 1 : 1;
break;
case SB_THUMBPOSITION:
case SB_THUMBTRACK:
if(pData->uItemPosCount < 0x7F00) {
iPos = HIWORD(wParam);
} else {
sScroll.cbSize = sizeof(SCROLLINFO);
sScroll.fMask = SIF_TRACKPOS;
GetScrollInfo(hWnd, SB_VERT, &sScroll);
iPos = sScroll.nTrackPos;
}
}
iMax = pData->uItemPosCount;
iMax -= pData->uPageEnties - 1;
if(iPos >= iMax)
iPos = iMax;
if(iPos < 0)
iPos = 0;
if(iPos != (int)pData->uScrollY) {
pData->uScrollY = iPos;
SetScrollPos(hWnd, SB_VERT, iPos, TRUE);
UpdateView(pData);
}
return 0;
case WM_SYSCOLORCHANGE: // Wurden die Systemfarben verändert
pData = GetHandle(hWnd);
LOCK(pData);
if(pData->hTheme) {
pCloseThemeData(pData->hTheme);
pData->hTheme = NULL;
}
if(pData->hThemeBt) {
pCloseThemeData(pData->hThemeBt);
pData->hThemeBt = NULL;
}
if(pOpenThemeData) {
pData->hTheme = pOpenThemeData(hWnd, L"TREEVIEW");
pData->cGlyphOk = (char)((pData->hTheme) ? 1 : 0);
}
if(pData->iStatesMode)
CreateStateImageList(pData, 0);
if(pData->iChecksMode)
CreateStateImageList(pData, 1);
if(pData->hHeader)
SendMessage(pData->hHeader, WM_SYSCOLORCHANGE, 0, 0);
UpdateColorsList(pData);
UNLOCK(pData);
return 0;
case WM_GETFONT: //tell what font we are using
{
HFONT hFont;
pData = GetHandle(hWnd);
LOCK(pData);
hFont = pData->hFontN;
UNLOCK(pData);
return (LRESULT)hFont;
}
case WM_SETFONT: // Einen neuen Font zuweisen
pData = GetHandle(hWnd);
LOCK(pData);
if(CreateFontset(pData, (HFONT)wParam)){
if(UpdateFont(pData))
UpdateView(pData);
}
UNLOCK(pData);
return 0;
case WM_STYLECHANGED: // Hat sich der Fenstersytle geändert
if(wParam == GWL_STYLE) {
pData = GetHandle(hWnd);
lParam = ((STYLESTRUCT *)lParam)->styleNew;
LOCK(pData);
uChange = U(lParam) ^ pData->uStyle;
pData->uStyle = U(lParam);
if(uChange & (TVS_CHECKBOXES | TVS_NONEVENHEIGHT)) {
if(lParam & TVS_CHECKBOXES) {
if(pData->hStates == NULL) {
CreateStateImageList(pData, 0);
}
} else {
if(pData->iStatesMode && pData->hStates) {
if(pData->hStates != THEMEIMGLIST) {
ImageList_Destroy(pData->hStates);
}
pData->hStates = NULL;
pData->iStatesMode = 0;
}
}
UpdateHeight(pData);
UpdateScrollY(pData);
}
if(uChange & (TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT | TVS_RTLREADING | TVS_CHECKBOXES | TVS_TRACKSELECT)) {
if(!(pData->uStyle & (TVS_HASBUTTONS | TVS_HASLINES))) {
pData->cHasRootRow = 0;
} else {
pData->cHasRootRow = (char)((pData->uStyle & TVS_LINESATROOT) ? 1 : 0);
}
UpdateView(pData);
}
if(uChange & TVS_NOSCROLL) {
pData->uOldYCount = 0xFFFF;
pData->uOldXCount = 0xFFFF;
UpdateScrollX(pData);
UpdateScrollY(pData);
}
if(uChange & TVS_NOTOOLTIPS) {
if(pData->uStyle & TVS_NOTOOLTIPS) {
UpdateToolTip(pData, 0, 0);
} else {
CreateToolTip(pData);
}
}
if(uChange & WS_DISABLED) {
pData->cIsEnabled = (char)((pData->uStyle & WS_DISABLED) ? 0 : 1);
UpdateView(pData);
if(!pData->cIsEnabled)
UpdateToolTip(pData, 0, 0);
}
UNLOCK(pData);
}
return 0;
case WM_PAINT: // Das Fenster zeichnen
if(wParam) {
TreeListDraw(hWnd, (HDC)wParam, NULL);
} else {
PAINTSTRUCT sPaint;
hDc = BeginPaint(hWnd, &sPaint);
if(pBeginBufferedPt && pEndBufferedPt) {
RECT sRect;
HDC hDcBuffer;
HANDLE hBufferedPaint;
GetClientRect(hWnd, &sRect);
hBufferedPaint = pBeginBufferedPt(hDc, &sRect, BPBF_COMPATIBLEBITMAP, NULL, &hDcBuffer);
if(hBufferedPaint) {
TreeListDraw(hWnd, hDcBuffer, &sRect);
pEndBufferedPt(hBufferedPaint, TRUE);
} else {
TreeListDraw(hWnd, hDc, &sRect);
}
} else {
TreeListDraw(hWnd, hDc, &sPaint.rcPaint);
}
EndPaint(hWnd, &sPaint);
}
return 0;
case WM_TIMER: // Timer Funktion
if(wParam == ID_TOOLTIPCHECK) {
pData = GetHandle(hWnd);
if(pData->uToolTipItem) {
if(pData->uToolTipShow) { // Verzögertes einblenden
pData->uToolTipShow--;
if(pData->uToolTipShow)
return 0;
LOCK(pData);
GetCursorPos(&sPoint);
sInfo.pt.x = sPoint.x;
sInfo.pt.y = sPoint.y;
ScreenToClient(hWnd, &sInfo.pt);
if(WindowFromPoint(sPoint) == hWnd) {
TreeListHitTest(pData, &sInfo);
} else {
sInfo.hItem = 0;
sInfo.flags = 0;
}
if(sInfo.flags & TVHT_ONITEM)
if(pData->uToolTipItem == U(sInfo.hItem) && pData->uToolTipSub == 0) {
TOOLINFO sInfo;
UNLOCK(pData);
sInfo.cbSize = sizeof(sInfo);
sInfo.hwnd = pData->hWnd;
sInfo.uId = (UINT_PTR)pData->hWnd;
if(pData->uToolTipItem) {
SendMessage(pData->hToolTip, TTM_TRACKACTIVATE, 0, (LPARAM)&sInfo);
}
SendMessage(pData->hToolTip, TTM_TRACKPOSITION, 0, MAKELONG(pData->sToolTipPos.x, pData->sToolTipPos.y));
SendMessage(pData->hToolTip, TTM_TRACKACTIVATE, 1, (LPARAM)&sInfo);
return 0;
}
if(sInfo.flags & TVHT_ONSUBITEM)
if(pData->uToolTipItem == U(sInfo.hItem) && TVHT_SUBTOCOL(sInfo.flags) == pData->uToolTipSub) {
TOOLINFO sInfo;
UNLOCK(pData);
sInfo.cbSize = sizeof(sInfo);
sInfo.hwnd = pData->hWnd;
sInfo.uId = (UINT_PTR)pData->hWnd;
if(pData->uToolTipItem) {
SendMessage(pData->hToolTip, TTM_TRACKACTIVATE, 0, (LPARAM)&sInfo);
}
SendMessage(pData->hToolTip, TTM_TRACKPOSITION, 0, MAKELONG(pData->sToolTipPos.x, pData->sToolTipPos.y));
SendMessage(pData->hToolTip, TTM_TRACKACTIVATE, 1, (LPARAM)&sInfo);
return 0;
}
// ToolTip wieder entferenen
UpdateToolTip(pData, U(sInfo.hItem), sInfo.flags);
UNLOCK(pData);
}
LOCK(pData);
GetCursorPos(&sInfo.pt);
ScreenToClient(hWnd, &sInfo.pt);
TreeListHitTest(pData, &sInfo);
UpdateToolTip(pData, U(sInfo.hItem), sInfo.flags);
UNLOCK(pData);
}
}
return 0;
case WM_COMMAND: // Kommando Nachrichnten
if(wParam == MAKELONG(3, EN_KILLFOCUS) || wParam == MAKELONG(3, EN_RETURN) || wParam == MAKELONG(3, CBN_KILLFOCUS)) {
pData = GetHandle(hWnd);
if(pData->uEditItem) {
LOCK(pData);
TreeListEndLabelEdit(pData, (wParam == MAKELONG(3, EN_RETURN)) ? 2 : 1);
UNLOCK(pData);
if(pData->uEditMode)
if(wParam != MAKELONG(3, EN_KILLFOCUS)) {
SetFocus(pData->hWnd);
}
}
} else
if(wParam == MAKELONG(3, EN_ESCAPE)) { // ESC-Taste in Edit-Fenster
pData = GetHandle(hWnd);
if(pData->uEditItem) {
LOCK(pData);
TreeListEndLabelEdit(pData, 0);
UNLOCK(pData);
if(pData->uEditMode)
SetFocus(pData->hWnd);
}
} // Änderung in Edit-Fenster
else
if(wParam == MAKELONG(3, EN_CHANGE) || wParam == MAKELONG(3, CBN_EDITCHANGE) || wParam == MAKELONG(3, CBN_SELCHANGE) || wParam == MAKELONG(3, EN_SETTEXT)) {
pData = GetHandle(hWnd);
pData->cColumnStart = 1;
}
return 0;
case WM_NOTIFY: // Notify Nachrichnten
if(wParam == 1) { // Nachricht vom Header
NMHEADER *pHdr = (NMHEADER *)lParam;
HDITEM sItem;
HDITEM sTemp;
RECT sRect;
int iDelta;
int iCode;
int iSize;
int iNext;
int iCol;
int iSub;
iCode = pHdr->hdr.code;
// Hat sich die Spaltenbreite verändert
if(iCode == HDN_ITEMCHANGED && (pHdr->pitem->mask & HDI_WIDTH)) {
pData = GetHandle(hWnd);
iCol = pHdr->iItem;
if(pData->aColumn[iCol].sReal != pHdr->pitem->cxy) {
iCode = HDN_TRACK;
}
}
if(iCode == HDN_BEGINDRAG) { // Drag&Drop im Header
pData = GetHandle(hWnd);
if(!(pData->uStyleEx & TVS_EX_HEADERDRAGDROP) || pHdr->iItem == 0) {
return 1;
}
return 0;
}
if(iCode == HDN_ENDDRAG) { // Drag&Drop im Header fertig
if(pHdr->pitem->iOrder == 0 || pHdr->iItem == 0) {
return 1;
}
pData = GetHandle(hWnd);
if(!(pData->uStyleEx & TVS_EX_HEADERDRAGDROP)) {
return 1;
}
PostMessage(hWnd, TVM_SETCOLUMNORDERARRAY, FROM_HEADER, 0);
return 0;
}
if(iCode == HDN_BEGINTRACK) { // User will die Spaltenbreite ändern
pData = GetHandle(hWnd);
if(!pData->cHasFocus) {
SetFocus(pData->hWnd);
}
if(pData->uStyleEx & TVS_EX_NOCOLUMNRESIZE) { // Darf der User die Spaltenbreite ändern
return 1;
}
if(pData->uStyleEx & TVS_EX_FIXEDCOLSIZE) { // Fixe gesammte Spaltenbreite
iNext = pData->aColumn[pHdr->iItem].bIndex;
for(iNext++;; iNext++) { // Suche nächste veränerbare Spalte
if(U(iNext) >= pData->uColumnCount) {
return 1;
}
iSub = pData->aColumnPos[iNext];
if(pData->aColumn[iSub].sFixed == 0) {
break;
}
}
}
if(U(pHdr->iItem) < pData->uColumnCount) // Darf die Spalte verändert werden
if(pData->aColumn[pHdr->iItem].sFixed) {
POINT sPoint;
int iCol;
int iSub;
if(pData->aColumn[pHdr->iItem].sReal || pHdr->iItem < 0) {
return 1;
}
GetCursorPos(&sPoint); // Die nächste veränderbare Spalte greifen
ScreenToClient(pData->hHeader, &sPoint);
PostMessage(pData->hHeader, WM_LBUTTONUP, 0, MAKELONG(sPoint.x, sPoint.y));
iCol = pData->aColumn[pHdr->iItem].bIndex;
iSub = pData->aColumnPos[iCol - 1];
PostMessage(pData->hHeader, WM_LBUTTONDOWN, 0, MAKELONG(pData->aColumnXpos[iSub + 1] - 2, sPoint.y));
return 1;
}
}
if(iCode == HDN_DIVIDERDBLCLICK) { // Doppelcklick auf Spaltentrenner
pData = GetHandle(hWnd);
if(!pData->cHasFocus) {
SetFocus(pData->hWnd);
}
if(pData->uStyleEx & TVS_EX_NOCOLUMNRESIZE) { // Darf der User die Spaltenbreite ändern
return 0;
}
if(U(pHdr->iItem) < pData->uColumnCount) // Darf die Spalte verändert werden
if(pData->aColumn[pHdr->iItem].sFixed) {
return 0;
}
LOCK(pData);
iSize = TreeListScanColumn(pData, pHdr->iItem);
if(iSize) { // Spalte auf maximale Textbreite
sItem.cxy = iSize;
pHdr->pitem = &sItem;
iCode = HDN_ENDTRACK;
if(pData->aColumn[pHdr->iItem].bMinEx) // Minimale Breite prüfen
if(pData->aColumn[pHdr->iItem].sMin > sItem.cxy) {
sItem.cxy = pData->aColumn[pHdr->iItem].sMin;
}
}
UNLOCK(pData);
}
if(iCode == HDN_TRACK || iCode == HDN_ENDTRACK) { // Wurde die Spaltenbreite verändert
pData = GetHandle(hWnd);
if(!pData->cHasFocus && iCode == HDN_ENDTRACK) {
SetFocus(pData->hWnd);
}
LOCK(pData);
iCol = pHdr->iItem;
sItem.mask = HDI_WIDTH;
sItem.cxy = pHdr->pitem->cxy;
if(pData->aColumn[iCol].bMinEx) // Minimale Breite prüfen
if(pData->aColumn[iCol].sMin > sItem.cxy) {
sItem.cxy = pData->aColumn[iCol].sMin;
pHdr->pitem->cxy = sItem.cxy;
if(sItem.cxy == pData->aColumn[iCol].sSize) {
UNLOCK(pData);
return 0;
}
}
if(pData->uStyleEx & TVS_EX_FIXEDCOLSIZE) { // Fixe gesammte Spaltenbreite
if(iCol == 0 && sItem.cxy <= 0) {
sItem.cxy = 1;
pHdr->pitem->cxy = 1;
if(sItem.cxy == pData->aColumn[iCol].sSize) {
UNLOCK(pData);
return 0;
}
}
iNext = pData->aColumn[iCol].bIndex;
for(iNext++;; iNext++) { // Überspringe fixierte Spalten
if(U(iNext) >= pData->uColumnCount) {
UNLOCK(pData);
return 0;
}
iSub = pData->aColumnPos[iNext];
if(pData->aColumn[iSub].sFixed == 0) {
break;
}
}
iDelta = pData->aColumn[iCol].sReal - sItem.cxy;
sTemp.cxy = pData->aColumn[iSub].sReal + iDelta;
if(iDelta < 0) { // Nächste Spalte wird zu klein
if(sTemp.cxy < 0) {
sTemp.cxy = 0;
iDelta = -pData->aColumn[iSub].sReal;
sItem.cxy = pData->aColumn[iCol].sReal - iDelta;
}
if(pData->aColumn[iSub].bMinEx) // Minimale Breite prüfen
if(pData->aColumn[iSub].sMin > sTemp.cxy) {
iDelta += pData->aColumn[iSub].sMin - sTemp.cxy;
sTemp.cxy = pData->aColumn[iSub].sMin;
sItem.cxy = pData->aColumn[iCol].sReal - iDelta;
}
pHdr->pitem->cxy = sItem.cxy;
if(iDelta >= 0) { // Keine Änderung
UNLOCK(pData);
return 0;
}
}
if(pData->aColumn[iSub].bWeight) { // Variable Spalte
pData->iVarSize -= pData->aColumn[iSub].sSize;
pData->iVarSize += sTemp.cxy;
} else { // Fixe Spalte
pData->iFixSize -= pData->aColumn[iSub].sSize;
pData->iFixSize += sTemp.cxy;
}
sTemp.mask = HDI_WIDTH;
iDif = pData->aColumn[iSub].sReal;
pData->aColumn[iSub].sSize = (short)sTemp.cxy;
pData->aColumn[iSub].sReal = (short)sTemp.cxy;
Header_SetItem(pData->hHeader, iSub, &sTemp);
// Breite verändert
if(iDif != sTemp.cxy && (pData->uStyleEx & TVS_EX_HEADERCHGNOTIFY)) {
TV_COLSIZE sNotify;
sNotify.hdr.code = TVN_COLUMNCHANGED;
sNotify.uColumn = iSub;
sNotify.uIndex = pData->aColumn[iSub].bIndex;
sNotify.uPosX = pData->aColumnXpos[iSub];
sNotify.iSize = sTemp.cxy;
UNLOCK(pData);
SendNotify(pData, &sNotify.hdr);
LOCK(pData);
}
}
if(pData->aColumn[iCol].bWeight) { // Ist es eine variable Spalte
pData->iVarSize -= pData->aColumn[iCol].sSize;
pData->iVarSize += sItem.cxy;
} else { // Fixe Spalte
pData->iFixSize -= pData->aColumn[iCol].sSize;
pData->iFixSize += sItem.cxy;
}
iDif = pData->aColumn[iCol].sReal;
pData->aColumn[iCol].sSize = (short)sItem.cxy;
pData->aColumn[iCol].sReal = (short)sItem.cxy;
Header_SetItem(pData->hHeader, iCol, &sItem);
iSize = UpdateColumns(pData);
if(iSize < 0x10000) { // Spalten neu zeichnen
GetClientRect(hWnd, &sRect);
sRect.left = iSize;
sRect.left -= pData->uScrollX;
InvalidateRect(hWnd, &sRect, FALSE);
}
UpdateScrollX(pData);
UNLOCK(pData);
// Breite verändert
if(iDif != sItem.cxy && (pData->uStyleEx & TVS_EX_HEADERCHGNOTIFY)) {
TV_COLSIZE sNotify;
sNotify.hdr.code = TVN_COLUMNCHANGED;
sNotify.uColumn = iCol;
sNotify.uIndex = pData->aColumn[iCol].bIndex;
sNotify.uPosX = pData->aColumnXpos[iCol];
sNotify.iSize = sItem.cxy;
UNLOCK(pData);
SendNotify(pData, &sNotify.hdr);
LOCK(pData);
}
}
// Weiterleiten an Elternfenster
if(iCode == HDN_ITEMCLICK || iCode == HDN_ITEMDBLCLICK) {
pData = GetHandle(hWnd);
if(!pData->cHasFocus) {
SetFocus(pData->hWnd);
}
SendNotify(pData, &pHdr->hdr);
}
} else {
NMHDR *pHdr = (NMHDR *)lParam;
NMTTDISPINFO *pInfo;
switch(pHdr->code) {
case TTN_GETDISPINFO:
pData = GetHandle(hWnd);
pInfo = (NMTTDISPINFO *)pHdr;
pInfo->lpszText = pData->pToolTipText;
SendMessage(pData->hToolTip, WM_SETFONT, (WPARAM)pData->hFontT, 0);
break;
}
}
return 0;
case WM_GETDLGCODE: // Welche Tasten werden im Dialog benutzt
pMsg = (MSG *)lParam;
if(pMsg && pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RETURN) {
return DLGC_WANTALLKEYS;
}
return DLGC_WANTCHARS | DLGC_WANTARROWS;
case WM_PASTE: // Weiterleiten an Edit-Control
case WM_CLEAR:
case WM_COPY:
case WM_CUT:
pData = GetHandle(hWnd);
if(pData->hEdit) {
PostMessage(pData->hEdit, uMsg, 0, 0);
return 0;
}
break;
case TVM_SETIMAGELIST: // Die Image-Liste einstellen
pData = GetHandle(hWnd);
LOCK(pData);
#ifdef _DEBUG
char dbg[255];
sprintf(dbg, "TVM_SETIMAGELIST %d", (int)wParam);
OutputDebugStringA(dbg);
#endif
switch((int)wParam) {
case TVSIL_NORMAL:
lRet = (LPARAM)pData->hImages;
if(lRet == lParam)
break;
pData->hImages = (HIMAGELIST)lParam;
if(!pData->hImages) {
pData->iImagesXsize = 0;
pData->iImagesYsize = 0;
} else {
IMAGEINFO sInfo;
ImageList_GetImageInfo(pData->hImages, 0, &sInfo);
pData->iImagesXsize = sInfo.rcImage.right - sInfo.rcImage.left;
pData->iImagesYsize = sInfo.rcImage.bottom - sInfo.rcImage.top;
if(pData->hHeader && ((pData->uStyleEx & TVS_EX_HEADEROWNIMGLIST) == 0))
SendMessage(pData->hHeader, HDM_SETIMAGELIST, 0, (LPARAM)pData->hImages);
}
if(!pData->iSubImgMode || pData->hSubImg == pData->hImages) {
pData->iSubImgMode = 0;
pData->hSubImg = pData->hImages;
pData->iSubImgXsize = pData->iImagesXsize;
pData->iSubImgYsize = pData->iImagesXsize;
}
if(!pData->cFixedHeight) {
pData->iRowHeight = 1;
UpdateHeight(pData);
UpdateScrollY(pData);
} else {
UpdateView(pData);
}
break;
case TVSIL_HEADER:
lRet = pData->uStyleEx & TVS_EX_HEADEROWNIMGLIST;
if(lRet == 0)
break;
lRet = (LPARAM)pData->hHeadImg;
if(lRet == lParam)
break;
pData->hHeadImg = (HIMAGELIST)lParam;
if(pData->hHeader)
SendMessage(pData->hHeader, HDM_SETIMAGELIST, 0, (LPARAM)pData->hHeadImg);
if(!pData->cFixedHeight) {
pData->iRowHeight = 1;
UpdateHeight(pData);
UpdateScrollY(pData);
} else {
UpdateView(pData);
}
break;
case TVSIL_STATE:
lRet = (LPARAM)pData->hStates;
if(lRet == lParam)
break;
if(pData->iStatesMode) {
if(pData->hStates != THEMEIMGLIST) {
ImageList_Destroy(pData->hStates);
}
pData->iStatesMode = 0;
}
pData->hStates = (HIMAGELIST)lParam;
if(!pData->hStates) {
pData->iStatesXsize = 0;
pData->iStatesYsize = 0;
if(pData->uStyle & TVS_CHECKBOXES) {
CreateStateImageList(pData, 0);
}
} else
if(pData->hStates == THEMEIMGLIST) {
pData->iStatesXsize = 16;
pData->iStatesYsize = 16;
pData->iStatesMode = 1;
if(!pData->hThemeBt)
CreateStateImageList(pData, 0);
} else {
IMAGEINFO sInfo;
ImageList_GetImageInfo(pData->hStates, 0, &sInfo);
pData->iStatesXsize = sInfo.rcImage.right - sInfo.rcImage.left;
pData->iStatesYsize = sInfo.rcImage.bottom - sInfo.rcImage.top;
pData->iStatesMode = 0;
}
if(!pData->cFixedHeight) {
pData->iRowHeight = 1;
UpdateHeight(pData);
UpdateScrollY(pData);
} else {
UpdateView(pData);
}
break;
case TVSIL_CHECK:
lRet = (LPARAM)pData->hChecks;
if(lRet == lParam)
break;
if(pData->iChecksMode) {
if(pData->hChecks != THEMEIMGLIST) {
ImageList_Destroy(pData->hChecks);
}
pData->iChecksMode = 0;
}
pData->hChecks = (HIMAGELIST)lParam;
if(!pData->hChecks) {
pData->iChecksXsize = 0;
pData->iChecksYsize = 0;
for(uVal = 0; uVal < pData->uColumnCount; uVal++) {
if(pData->aColumn[uVal].bEdit < TVSIL_CHECK)
continue;
CreateStateImageList(pData, 1);
break;
}
} else
if(pData->hChecks == THEMEIMGLIST) {
pData->iChecksXsize = 16;
pData->iChecksYsize = 16;
pData->iChecksMode = 1;
if(!pData->hThemeBt)
CreateStateImageList(pData, 1);
} else {
IMAGEINFO sInfo;
ImageList_GetImageInfo(pData->hChecks, 0, &sInfo);
pData->iChecksXsize = sInfo.rcImage.right - sInfo.rcImage.left;
pData->iChecksYsize = sInfo.rcImage.bottom - sInfo.rcImage.top;
pData->iChecksMode = 0;
}
if(!pData->cFixedHeight) {
pData->iRowHeight = 1;
UpdateHeight(pData);
UpdateScrollY(pData);
} else {
UpdateView(pData);
}
break;
case TVSIL_SUBIMAGES:
lRet = (LPARAM)pData->hSubImg;
if(lRet == lParam)
break;
if(pData->iSubImgMode) {
ImageList_Destroy(pData->hSubImg);
pData->iSubImgMode = 0;
}
pData->hSubImg = (HIMAGELIST)lParam;
if(!pData->hSubImg || pData->hSubImg == pData->hImages) {
pData->iSubImgMode = 0;
pData->hSubImg = pData->hImages;
pData->iSubImgXsize = pData->iImagesXsize;
pData->iSubImgYsize = pData->iImagesXsize;
} else {
IMAGEINFO sInfo;
ImageList_GetImageInfo(pData->hSubImg, 0, &sInfo);
pData->iSubImgXsize = sInfo.rcImage.right - sInfo.rcImage.left;
pData->iSubImgYsize = sInfo.rcImage.bottom - sInfo.rcImage.top;
pData->iSubImgMode = 1;
}
if(!pData->cFixedHeight) {
pData->iRowHeight = 1;
UpdateHeight(pData);
UpdateScrollY(pData);
} else {
UpdateView(pData);
}
break;
default:
lRet = 0;
}
UNLOCK(pData);
return lRet;
case TVM_GETIMAGELIST: // Die Image-Liste abfragen
pData = GetHandle(hWnd);
if(!pData)
return 0;
switch((int)wParam) {
case TVSIL_NORMAL:
lRet = (LPARAM)pData->hImages;
break;
case TVSIL_STATE:
lRet = (LPARAM)pData->hImages;
break;
case TVSIL_CHECK:
lRet = (LPARAM)pData->hChecks;
break;
case TVSIL_HEADER:
lRet = (LPARAM)pData->hHeadImg;
break;
default
:
lRet = 0;
}
return lRet;
case TVM_GETSETOPTION: // Diverse Optionen einstellen
pData = GetHandle(hWnd);
lRet = 0;
switch(U(wParam) & ~TVOP_WRITEOPTION) {
case TVOP_AUTOEXPANDOFF: // Icon Offset für TVS_EX_AUTOEXPANDICON
lRet = pData->iAutoAdd;
if((wParam & TVOP_WRITEOPTION) && lParam != lRet) {
LOCK(pData);
pData->iAutoAdd = (int)lParam;
if(pData->uItemPosCount > 0) {
UpdateView(pData);
}
UNLOCK(pData);
}
break;
}
return lRet;
case TVM_GETITEMSTATE: // Die Statusbits eines Eintrags abfragen
pData = GetHandle(hWnd);
LOCK(pData);
if(U(wParam) <= pData->uTreeItemsMax) {
BaseItem *pEntry;
pEntry = pData->pTreeItems[U(wParam)];
if(!pEntry)
lRet = 0;
{
lRet = pEntry->uState;
}
} else {
lRet = 0;
}
UNLOCK(pData);
return lRet;
case TVM_GETITEM: // Einträge abfragen
pData = GetHandle(hWnd);
LOCK(pData);
lRet = TreeListGetItem(pData, (TV_ITEM *)lParam);
UNLOCK(pData);
return lRet;
case TVM_SETITEM: // Einträge einfügen
pData = GetHandle(hWnd);
LOCK(pData);
lRet = TreeListSetItem(pData, (TV_ITEM *)lParam);
UNLOCK(pData);
return lRet;
case TVM_INSERTITEM: // Einträge einfügen
pData = GetHandle(hWnd);
LOCK(pData);
lRet = TreeListInsertItem(pData, (TV_INSERTSTRUCT *)lParam);
UNLOCK(pData);
return lRet;
case TVM_DELETEITEM: // Einträge löschen
pData = GetHandle(hWnd);
LOCK(pData);
lRet = TreeListDeleteItem(pData, U(lParam), (U(wParam) == 0x88) ? 2 : 1);
UNLOCK(pData);
return lRet;
case TVM_FINDITEM: // Einträge suchen
pData = GetHandle(hWnd);
LOCK(pData);
lRet = TreeListFindItem(pData, U(wParam), (TV_FIND *)lParam);
UNLOCK(pData);
return lRet;
case TVM_DELETECOLUMN: // Löschen einer Salte im Header
pData = GetHandle(hWnd);
LOCK(pData);
lRet = TreeListDeleteColumn(pData, U(wParam));
UNLOCK(pData);
return lRet;
case TVM_INSERTCOLUMN: // Einfügen einer Salte im Header
pData = GetHandle(hWnd);
LOCK(pData);
lRet = TreeListInsertColumn(pData, (int)wParam, (TV_COLUMN *)lParam);
UNLOCK(pData);
return lRet;
case TVM_GETCOLUMNCOUNT: // Abfragen der Spaltenanzahl
pData = GetHandle(hWnd);
return (LRESULT)pData->uColumnCount;
case TVM_GETHEADER: // Abfragen des Header-Fensters
pData = GetHandle(hWnd);
return (LRESULT)pData->hHeader;
case TVM_GETEXTENDEDSTYLE: // Abfragen der erweiterten Style-Bits
pData = GetHandle(hWnd);
return pData->uStyleEx;
case TVM_SETEXTENDEDSTYLE: // Einstellen der erweiterten Style-Bits
pData = GetHandle(hWnd);
if(!wParam)
wParam = 0xFFFFFFFF;
uVal = pData->uStyleEx & ~U(wParam);
uVal |= U(lParam) & U(wParam);
if(pData->uStyleEx != uVal) { // Has it changed?
LOCK(pData);
uChange = pData->uStyleEx ^ uVal;
pData->uStyleEx = uVal;
if(uChange & TVS_EX_AUTOHSCROLL) {
UpdateScrollX(pData);
}
if((uChange & TVS_EX_GRAYEDDISABLE) && !pData->cIsEnabled) {
UpdateView(pData);
}
if(uChange & TVS_EX_ITEMLINES) {
UpdateHeight(pData);
UpdateScrollY(pData);
}
if(uChange & (TVS_EX_ALTERNATECOLOR | TVS_EX_SUBSELECT | TVS_EX_MULTISELECT | TVS_EX_FULLROWMARK | TVS_EX_FULLROWITEMS)) {
UpdateView(pData);
}
if((uChange&~U(lParam)) & TVS_EX_SUBSELECT) {
TreeListSelectItem(pData, pData->uSelectedItem, 0, TVC_UNKNOWN);
}
if(pData->hHeader){
if((uChange & TVS_EX_HIDEHEADERS) || (uChange & TVS_EX_HEADEROWNIMGLIST)) {
pData->uStartPixel = (pData->uStyleEx & TVS_EX_HIDEHEADERS) ? 0 : bDrawWithTheme ? GetSystemMetrics(SM_CYHSCROLL) : 17;
MoveWindow(pData->hHeader, -(int)pData->uScrollX, 0, pData->uSizeX + pData->uScrollX, pData->uStartPixel, TRUE);
if(pData->uStyleEx & TVS_EX_HEADEROWNIMGLIST){
SendMessage(pData->hHeader, HDM_SETIMAGELIST, 0, (LPARAM)pData->hHeadImg);
} else {
SendMessage(pData->hHeader, HDM_SETIMAGELIST, 0, (LPARAM)pData->hImages);
}
UpdateView(pData);
}
}
UNLOCK(pData);
}
return uVal;
case TVM_GETINSERTMARKCOLOR: // Farben abfragen
wParam = TVC_INSERT;
goto ColGet;
case TVM_GETLINECOLOR:
wParam = TVC_LINE;
goto ColGet;
case TVM_GETTEXTCOLOR:
wParam = TVC_TEXT;
case TVM_GETBKCOLOR:
ColGet:
if(wParam < 0 || wParam >= MAX_COLORS)
return 0xFFFFFFFF;
pData = GetHandle(hWnd);
return pData->uColors[U(wParam)];
case TVM_SETINSERTMARKCOLOR: // Farben einstellen
wParam = TVC_INSERT;
goto ColSet;
case TVM_SETLINECOLOR:
wParam = TVC_LINE;
goto ColSet;
case TVM_SETTEXTCOLOR:
wParam = TVC_TEXT;
case TVM_SETBKCOLOR:
ColSet:
if(wParam < 0 || wParam >= MAX_COLORS)
return 0xFFFFFFFF;
pData = GetHandle(hWnd);
LOCK(pData);
lRet = pData->uColors[U(wParam)];
if((COLORREF)lParam == TV_NOCOLOR) { // Standartfarbe einstellen
if(pData->cColorChanged[U(wParam)]) {
pData->cColorChanged[U(wParam)] = 0;
UpdateColorsList(pData);
UpdateView(pData);
}
} else {
pData->cColorChanged[U(wParam)] = 1;
pData->uColors [U(wParam)] = (COLORREF)lParam;
if(lRet != lParam) {
UpdateColorsList(pData);
UpdateView(pData);
}
}
UNLOCK(pData);
return lRet;
case TVM_HITTEST: // Abfragen von Koortinatenpositionen
pData = GetHandle(hWnd);
LOCK(pData);
lRet = TreeListHitTest(pData, (LPTV_HITTESTINFO)lParam);
UNLOCK(pData);
return lRet;
case TVM_SELECTCHILDS: // Mehrere Einträge auswählen
pData = GetHandle(hWnd);
LOCK(pData);
lRet = TreeListSelectChilds(pData, U(lParam), U(wParam));
UNLOCK(pData);
return lRet;
case TVM_SELECTSUBITEM: // Einen (Sub)Eintrag auswählen
pData = GetHandle(hWnd);
LOCK(pData);
if(!(pData->uStyleEx & TVS_EX_SUBSELECT))
wParam = 0;
lRet = TreeListSelectItem(pData, U(lParam), U(wParam), TVC_UNKNOWN);
if(lRet > 1)
lRet = 1;
UNLOCK(pData);
return lRet;
case TVM_SELECTDROP: // Den unterstrichenen Eintrags auswählen
pData = GetHandle(hWnd);
LOCK(pData);
lRet = TreeListSetTrackItem(pData, U(lParam), U(wParam));
UNLOCK(pData);
return lRet;
case TVM_SELECTITEM: // Einen Eintrag auswählen
pData = GetHandle(hWnd);
LOCK(pData);
switch(wParam) {
case TVGN_CARET:
lRet = TreeListSelectItem(pData, U(lParam), 0, TVC_UNKNOWN);
if(lRet > 1)
lRet = 1;
break;
case TVGN_DROPHILITE:
lRet = TreeListSetTrackItem(pData, U(lParam), 0);
break;
case TVGN_FIRSTVISIBLE:
lRet = TreeListEnsureVisible(pData, U(lParam), FIRST_LINE);
lRet = (lRet < 0) ? 0 : 1;
break;
default
:
lRet = 0;
}
UNLOCK(pData);
return lRet;
case TVM_GETCOLUMNORDERARRAY: // Spaltensortierung abfragen
if(!lParam)
return 0;
pData = GetHandle(hWnd);
if(!pData->hHeader)
return 0;
return Header_GetOrderArray(pData->hHeader, U(wParam), lParam);
case TVM_SETCOLUMNORDERARRAY: // Spalten sortieren
pData = GetHandle(hWnd);
LOCK(pData);
lRet = TreeListSetOrderArray(pData, U(wParam), (unsigned *)lParam);
UNLOCK(pData);
return lRet;
case TVM_GETCOUNT: // Anzahl der Einträge abfragen
pData = GetHandle(hWnd);
return pData->uTreeItemsCount;
case TVM_GETINDENT: // Einrückung abfragen
pData = GetHandle(hWnd);
return pData->iIndent;
case TVM_SETINDENT: // Einrückung einstellen
pData = GetHandle(hWnd);
lRet = pData->iIndent;
if(wParam < 5)
wParam = 5;
if(wParam > 64)
wParam = 64;
if(lRet != (LPARAM)wParam) {
LOCK(pData);
pData->iIndent = (int)wParam;
UpdateView(pData);
UNLOCK(pData);
}
return lRet;
case TVM_GETITEMHEIGHT: // Zeilenhöhe abfragen
pData = GetHandle(hWnd);
return pData->iRowHeight;
case TVM_SETITEMHEIGHT: // Zeilenhöhe abfragen
pData = GetHandle(hWnd);
lRet = pData->iRowHeight;
if(wParam == -1) {
LOCK(pData);
pData->cFixedHeight = 0;
UpdateHeight(pData);
UpdateScrollY(pData);
UNLOCK(pData);
return lRet;
}
if(wParam & 1)
if(!(pData->uStyleEx & TVS_NONEVENHEIGHT))
wParam--;
if(wParam < 1)
wParam = 1;
if(wParam > 256)
wParam = 256;
if(lRet != (LPARAM)wParam) {
LOCK(pData);
pData->cFixedHeight = 1;
pData->iRowHeight = (int)wParam;
UpdateView(pData);
UNLOCK(pData);
}
return lRet;
case TVM_GETVISIBLECOUNT: // Abfragen der sichtbaren Zeilen
pData = GetHandle(hWnd);
return pData->uPageEnties;
case TVM_ENSUREVISIBLE: // Einen Eintrag ins Sichtfenster legen
pData = GetHandle(hWnd);
LOCK(pData);
lRet = TreeListEnsureVisible(pData, U(lParam), (int)wParam);
UNLOCK(pData);
return lRet;
case TVM_ISITEMVISIBLE: // Ist ein Eintrag sichtbar
pData = GetHandle(hWnd);
LOCK(pData);
lRet = TreeListIsVisible(pData, U(lParam), (int)wParam);
UNLOCK(pData);
return lRet;
case TVM_GETNEXTITEM: // Einen Eintrag suchen
pData = GetHandle(hWnd);
LOCK(pData);
lRet = TreeListGetNextItem(pData, U(lParam), (int)wParam);
UNLOCK(pData);
return lRet;
case TVM_GETITEMRECT: // Das Rechteck eines Eintrages abfragen
pData = GetHandle(hWnd);
LOCK(pData);
uVal = *(unsigned *)lParam;
lRet = TreeListGetItemRect(pData, uVal, U(wParam), (RECT *)lParam);
UNLOCK(pData);
return lRet;
case TVM_EXPAND: // Einen Eintrag umklappen
pData = GetHandle(hWnd);
LOCK(pData);
lRet = 0;
NextExp:
if(U(lParam) <= pData->uTreeItemsMax) {
BaseItem *pEntry;
#ifndef __REACTOS__
POINT sPoint;
#endif
pEntry = pData->pTreeItems[U(lParam)];
if(pEntry) {
sPoint.x = 0;
sPoint.y = 0;
if(wParam & TVE_EXPANDNEXT) { // Bei erstem Eltereneintrag beginnen
if(!pEntry->uParent) {
UNLOCK(pData);
return 1;
}
lParam = pEntry->uParent;
pEntry = pData->pTreeItems[U(lParam)];
wParam &= ~TVE_EXPANDNEXT;
}
switch(wParam & 0x0F) {
case TVE_COLLAPSE:
if(pEntry->uState & TVIS_EXPANDED) {
lRet = (TreeListToggleItem(pData, U(lParam), 0)) ? 0 : 1;
} else { // Nur Flag löschen
pEntry->uState &= ~TVIS_EXPANDPARTIAL;
lRet = 1;
}
// Kinder löschen
if(wParam & TVE_COLLAPSERESET) {
pEntry->uState &= TVIS_EXPANDEDONCE;
while(pEntry->uLastChild) {
if(!TreeListDeleteItem(pData, pEntry->uLastChild, 1))
break;
pEntry = pData->pTreeItems[U(lParam)];
}
}
// Auch Eltern zuklappen
if(!(wParam & TVE_EXPANDRECURSIVE)) {
break;
}
if(!pEntry->uParent) {
break;
}
lParam = pEntry->uParent;
goto NextExp;
break;
case TVE_EXPAND:
if(!(pEntry->uState & TVIS_EXPANDED)) {
uVal = (wParam & TVE_EXPANDPARTIAL) ? TVIS_EXPANDPARTIAL : 0;
lRet = (TreeListToggleItem(pData, U(lParam), uVal)) ? 0 : 1;
} else { // Schon aufgeklappt
lRet = 1;
if(wParam & TVE_EXPANDPARTIAL) {
if(!(wParam & TVE_EXPANDFORCE))
if((pEntry->uState & (TVIS_EXPANDPARTIAL | TVIS_EXPANDEDONCE)) != (TVIS_EXPANDPARTIAL | TVIS_EXPANDEDONCE)) {
lRet = (TreeListToggleItem(pData, U(lParam), TVE_EXPAND | TVIS_EXPANDPARTIAL)) ? 0 : 1;
}
} else {
if(pEntry->uState & TVIS_EXPANDPARTIAL) {
lRet = (TreeListToggleItem(pData, U(lParam), TVE_EXPAND)) ? 0 : 1;
}
}
}
// Auch Eltern aufklappen
if(!(wParam & TVE_EXPANDRECURSIVE)) {
break;
}
if(!pEntry->uParent) {
break;
}
lParam = pEntry->uParent;
goto NextExp;
break;
case TVE_TOGGLE:
lRet = (TreeListToggleItem(pData, U(lParam), 0)) ? 0 : 1;
break;
}
// Aktion auch auf alle Kinder anwenden
if((wParam & TVE_ALLCHILDS) && pEntry->uFirstChild) {
lParam = pEntry->uFirstChild;
wParam &= ~(TVE_EXPANDRECURSIVE | TVE_EXPANDNEXT | TVE_ONLYCHILDS);
sPoint.x = 0;
sPoint.y = 0;
for(;;) {
UNLOCK(pData);
TreeListProc(hWnd, uMsg, wParam, lParam);
LOCK(pData);
if(U(lParam) > pData->uTreeItemsMax) {
break;
}
pEntry = pData->pTreeItems[U(lParam)];
if(!pEntry || !pEntry->uNextItem)
break;
lParam = pEntry->uNextItem;
}
}
}
}
UNLOCK(pData);
return lRet;
case TVM_SETINSERTMARK: // Einfügemarke eintellen
pData = GetHandle(hWnd);
LOCK(pData);
lRet = TreeListSetInsertMark(pData, U(lParam), (int)wParam);
UNLOCK(pData);
return lRet;
case TVM_SETITEMBKCOLOR: // Hintergrundfarbe eines Eintrages ändern
pData = GetHandle(hWnd);
LOCK(pData);
lRet = TreeListSetItemColor(pData, U(wParam & 0xFFFFFF), U(wParam) >> 24, (COLORREF)lParam, 0);
UNLOCK(pData);
return lRet;
case TVM_SETITEMTEXTCOLOR: // Textfarbe eines Eintrages ändern
pData = GetHandle(hWnd);
LOCK(pData);
lRet = TreeListSetItemColor(pData, U(wParam & 0xFFFFFF), U(wParam) >> 24, (COLORREF)lParam, 1);
UNLOCK(pData);
return lRet;
case TVM_GETITEMBKCOLOR: // Hintergrundfarbe eines Eintrages abfragen
pData = GetHandle(hWnd);
LOCK(pData);
lRet = TreeListGetItemColor(pData, U(wParam), U(lParam), 0);
UNLOCK(pData);
return lRet;
case TVM_GETITEMTEXTCOLOR: // Textfarbe eines Eintrages abfragen
pData = GetHandle(hWnd);
LOCK(pData);
lRet = TreeListGetItemColor(pData, U(wParam), U(lParam), 1);
UNLOCK(pData);
return lRet;
case TVM_SORTCHILDRENEX: // Sortieren mit Funktion
pData = GetHandle(hWnd);
LOCK(pData);
lRet = TreeListSortItemsEx(pData, (TV_SORTEX *)lParam, (int)wParam);
UNLOCK(pData);
return lRet;
case TVM_SORTCHILDRENCB: // Sortieren mit Funktion
pData = GetHandle(hWnd);
LOCK(pData);
lRet = TreeListSortItemsCb(pData, (TV_SORTCB *)lParam, (int)wParam);
UNLOCK(pData);
return lRet;
case TVM_SORTCHILDREN: // Sortieren der Kindereinträge
pData = GetHandle(hWnd);
LOCK(pData);
lRet = TreeListSortItems(pData, U(lParam), (int)wParam);
UNLOCK(pData);
return lRet;
case TVM_GETITEMOFROW: // Hole den Eintrag von einer Reihe
pData = GetHandle(hWnd);
LOCK(pData);
if(U(lParam) < pData->uItemPosCount)
lRet = pData->pItemPos[U(lParam)];
else
lRet = 0;
UNLOCK(pData);
return lRet;
case TVM_GETROWCOUNT: // Hole die Anzahl der sichtbaren Reihen
pData = GetHandle(hWnd);
return pData->uItemPosCount;
case TVM_GETCOUNTPERPAGE: // Hole die Anzahl der darstelbaren Reihen
pData = GetHandle(hWnd);
return pData->uMaxEnties;
case TVM_GETROWOFITEM: // Suche Reihe zu einem Eintrag
pData = GetHandle(hWnd);
LOCK(pData);
if(U(lParam) <= pData->uTreeItemsMax) {
BaseItem *pEntry;
pEntry = pData->pTreeItems[U(lParam)];
if(!pEntry)
lRet = -1;
else
lRet = pEntry->uShowPos - 1;
} else {
lRet = -1;
}
UNLOCK(pData);
return lRet;
case TVM_EDITLABEL: // Einen Eintrag editieren
pData = GetHandle(hWnd);
if(!(pData->uStyle & TVS_EDITLABELS))
return 0;
LOCK(pData);
if((wParam & 0xFFFFFF00) == TVLE_DONOTIFY) { // Notify starten
lRet = TreeListStartNotifyEdit(pData, U(lParam), TVIR_EDITCOL(wParam), VK_RETURN, 0);
} else {
pData->cColumnStart = 0;
lRet = (LRESULT)TreeListEditLabel(pData, U(lParam), U(wParam));
}
UNLOCK(pData);
return lRet;
case TVM_GETEDITCONTROL: // Das Handle des Edit-Fensters abfragen
pData = GetHandle(hWnd);
return (LRESULT)pData->hEdit;
case TVM_GETTOOLTIPS: // Das Handle des ToolTip-Fensters abfragen
pData = GetHandle(hWnd);
return (LRESULT)pData->hToolTip;
case TVM_SETTOOLTIPS: // Das Handle für das ToolTip-Fensters setzen
pData = GetHandle(hWnd);
lRet = (LRESULT)pData->hToolTip;
pData->hToolTip = (HWND)wParam;
return lRet;
case TVM_SETUSERDATASIZE: // Einstellen der Größe der User-Daten
if(lParam < 0 || lParam > 0x1000000)
return -1;
pData = GetHandle(hWnd);
LOCK(pData);
if(pData->uTreeItemsCount > 0) {
lRet = 0;
} else {
pData->uUserDataSize = U(lParam);
lRet = lParam;
}
UNLOCK(pData);
return lRet;
case TVM_GETUSERDATASIZE: // Abfragen der Größe der User-Daten
pData = GetHandle(hWnd);
return pData->uUserDataSize;
case TVM_GETUSERDATA: // Einen Zeiger auf die User-Daten holen
pData = GetHandle(hWnd);
if(pData->uUserDataSize && U(lParam) <= pData->uTreeItemsMax) {
BaseItem *pEntry;
pEntry = pData->pTreeItems[U(lParam)];
if(pEntry)
return (LRESULT)(pEntry + 1);
}
return 0;
case TVM_SETCOLUMN: // Einen Spalten-Header einstellen
pData = GetHandle(hWnd);
if(pData->hHeader && U(wParam) < pData->uColumnCount) {
HDITEM sItem;
unsigned uBit;
unsigned uCol;
TV_COLUMN *pCol;
pCol = (TV_COLUMN *)lParam;
uCol = U(wParam);
uVal = pCol->cx;
if(uVal == TVCF_LASTSIZE) { // Breite vor Fixierung benutzen
if(pData->aColumn[uCol].sFixed > 0) {
uVal = pData->aColumn[uCol].sFixed;
} else {
uVal = pData->aColumn[uCol].sReal;
}
}
if(pCol->mask & TVCF_FIXED) { // Die Spalte fixieren
uBit = pCol->fmt & TVCFMT_FIXED;
if(uBit != 0 && pData->aColumn[uCol].sFixed == 0) {
pData->aColumn[uCol].sFixed = pData->aColumn[uCol].sReal;
if(!pData->aColumn[uCol].sFixed)
pData->aColumn[uCol].sFixed = 100;
}
if(uBit == 0 && pData->aColumn[uCol].sFixed != 0) {
pData->aColumn[uCol].sFixed = 0;
}
}
sItem.mask = 0;
if(pCol->mask & TVCF_FMT){
sItem.mask |= HDI_FORMAT;
sItem.fmt = pCol->fmt | HDF_STRING;
}
if(pCol->mask & TVCF_IMAGE){
sItem.mask |= HDI_IMAGE;
sItem.iImage = pCol->iImage;
}
if(pCol->mask & TVCF_WIDTH){
sItem.mask |= HDI_WIDTH;
sItem.cxy = uVal;
pData->aColumn[uCol].sSize = (short)sItem.cxy;
}
if(pCol->mask & TVCF_TEXT){
sItem.mask |= HDI_TEXT;
sItem.pszText = pCol->pszText;
sItem.cchTextMax = pCol->cchTextMax;
}
if(sItem.mask) {
lRet = SendMessage(pData->hHeader, HDM_SETITEM, uCol, (LPARAM)&sItem);
} else {
lRet = 1;
}
if(lRet && (pCol->mask & TVCF_FMT)) { // Hat sich die Ausrichtung verändert
BYTE bAlign;
switch(pCol->fmt) {
case TVCFMT_CENTER: bAlign = DT_CENTER; break;
case TVCFMT_RIGHT: bAlign = DT_RIGHT; break;
default: bAlign = DT_LEFT; break;
}
if(pData->aColumn[uCol].bAlign != bAlign) {
pData->aColumn[uCol].bAlign = bAlign;
UpdateColRect(pData, uCol);
}
}
if(lRet && (pCol->mask & TVCF_MARK)) { // Die Spalte markieren
RECT sRect;
unsigned uNext;
uVal = (pCol->fmt & TVCFMT_MARK) ? 1 : 0;
if(uVal != pData->aColumn[uCol].bMark) {
pData->aColumn[uCol].bMark = (BYTE)uVal;
pData->uMarkedCols += uVal * 2 - 1;
uNext = pData->aColumn [uCol ].bNext;
sRect.left = pData->aColumnXpos[uCol ];
sRect.right = pData->aColumnXpos[uNext] + 1;
sRect.bottom = pData->uScrollX;
sRect.top = pData->uSizeY;
InvalidateRect(pData->hWnd, &sRect, FALSE);
}
}
} else {
lRet = 0;
}
return lRet;
case TVM_GETCOLUMN: // Einen Spalten-Header abfragen
pData = GetHandle(hWnd);
if(pData->hHeader) {
HDITEM sItem;
TV_COLUMN *pCol = (TV_COLUMN *)lParam;
int bWantMark;
unsigned uCol;
uCol = U(wParam);
sItem.mask = 0;
if(pCol->mask & TVCF_FMT) {
sItem.mask |= HDI_FORMAT;
bWantMark = pCol->fmt & TVCFMT_MARK; //memorize if we want the marked state
}
if(pCol->mask & TVCF_IMAGE){
sItem.mask |= HDI_IMAGE;
}
if(pCol->mask & TVCF_WIDTH){
sItem.mask |= HDI_WIDTH;
}
if(pCol->mask & TVCF_TEXT){
sItem.mask |= HDI_TEXT;
sItem.pszText = pCol->pszText;
sItem.cchTextMax = pCol->cchTextMax;
}
lRet = SendMessage(pData->hHeader, HDM_GETITEM, wParam, (LPARAM)&sItem);
pCol->mask = 0;
if(sItem.mask & HDI_FORMAT) {
pCol->mask |= TVCF_FMT;
pCol->fmt = sItem.fmt;
if(bWantMark && pData->aColumn[uCol].bMark)
pCol->fmt |= TVCFMT_MARK;
if(!pData->aColumn[uCol].sReal && pData->aColumn[uCol].sFixed)
pCol->fmt |= TVCFMT_FIXED;
}
if(sItem.mask & HDI_IMAGE){
pCol->mask |= TVCF_IMAGE;
pCol->iImage = sItem.iImage;
}
if(sItem.mask & HDI_WIDTH){
pCol->mask |= TVCF_WIDTH;
pCol->cx = sItem.cxy;
}
if(sItem.mask & HDI_TEXT){
pCol->mask |= TVCF_TEXT;
pCol->pszText = sItem.pszText;
pCol->cchTextMax = sItem.cchTextMax;
}
} else {
lRet = 0;
}
return lRet;
case TVM_SETFOCUSITEM: // Focus einstellen
pData = GetHandle(hWnd);
LOCK(pData);
lRet = TreeListSetFocus(pData, U(lParam), U(wParam));
UNLOCK(pData);
return lRet;
case TVM_SETCOLUMNWIDTH: // Die Spaltenbreite einstellen
pData = GetHandle(hWnd);
if(pData->hHeader && U(wParam) < pData->uColumnCount && (int)lParam >= 0) {
HDITEM sItem;
sItem.mask = HDI_WIDTH;
sItem.cxy = (int)lParam;
lRet = SendMessage(pData->hHeader, HDM_SETITEM, wParam, (LPARAM)&sItem);
} else {
lRet = 0;
}
return lRet;
case TVM_COLUMNAUTOEDIT: // AutoEdit für eine Spalte einstellen
pData = GetHandle(hWnd);
uVal = (wParam >> 11) & 0x3F;
if(uVal >= pData->uColumnCount) {
return 0;
}
uMode = (U(wParam) >> TVAE_MODEPOS) & 7;
if(uMode == TVAX_NONE) {
wParam = 0;
} else
if(uVal == 0 && uMode >= TVAX_CHECK) { // Checkboxes are not allowed in the first column
return 0;
}
LOCK(pData);
uChange = pData->aColumn[uVal].bEdit << TVAE_MODEPOS;
uChange |= pData->aColumn[uVal].bFlags & TVAE_STATEENABLE;
pData->aColumn[uVal].bEdit = (BYTE)(uMode);
pData->aColumn[uVal].bFlags = (BYTE)(wParam);
pData->aColumn[uVal].bCbChar = (BYTE)(wParam >> 17);
pData->aColumn[uVal].bCbSize = (BYTE)(wParam >> 25);
pData->aColumn[uVal].pCbData = (void *)lParam;
pData->aColumn[uVal].iCbIcon = -1;
if(uMode >= TVAX_CHECK) { // Sollen Checkboxen dargestetllt werden
if(!pData->hChecks) {
CreateStateImageList(pData, 1);
UpdateHeight(pData);
}
if((uChange ^ wParam) & ((TVAE_MODEMASK ^ TVAE_CHECK ^ TVAE_CHECKED) | TVAE_STATEENABLE)) {
UpdateColRect(pData, uVal);
}
} else
if((uChange & TVAE_MODEMASK) >= TVAE_CHECK) { // Waren Checkboxen dargestetllt
if((uChange ^ wParam) & ((TVAE_MODEMASK ^ TVAE_CHECK ^ TVAE_CHECKED) | TVAE_STATEENABLE)) {
UpdateColRect(pData, uVal);
}
}
UNLOCK(pData);
return 1;
case TVM_COLUMNAUTOICON: // Icons für AutoEdit einstellen
pData = GetHandle(hWnd);
uVal = U(wParam);
if(uVal >= pData->uColumnCount || !pData->aColumn[uVal].bEdit) {
return 0;
}
LOCK(pData);
pData->aColumn[uVal].iCbIcon = (int)lParam;
UNLOCK(pData);
return 1;
case TVM_GETCOLUMNWIDTH: // Die Spaltenbreite abfragen
pData = GetHandle(hWnd);
if(pData->hHeader) {
HDITEM sItem;
sItem.mask = HDI_WIDTH;
sItem.cxy = (int)lParam;
SendMessage(pData->hHeader, HDM_GETITEM, wParam, (LPARAM)&sItem);
lRet = sItem.cxy;
} else {
lRet = 0;
}
return lRet;
case TVM_CREATEDRAGIMAGE: // Ein Drag-Image erzeugen
pData = GetHandle(hWnd);
LOCK(pData);
lRet = (LRESULT)CreateDragImage(pData, U(lParam), U(wParam));
UNLOCK(pData);
return lRet;
case TVM_ENDEDITLABELNOW: // Die aktuelle Eingabe abbrechen
pData = GetHandle(hWnd);
LOCK(pData);
lRet = (TreeListEndLabelEdit(pData, (wParam) ? 0 : 1)) ? 1 : 0;
UNLOCK(pData);
return 0;
case TVM_GETISEARCHSTRING: // Holt den aktuellen Suchtext
uDelta = GetTickCount() - uKeyLast;
if(!lParam) {
return (uDelta <= 750) ? uKeyPos : 0;
}
if(uDelta > 750) {
((TCHAR *)lParam)[0] = 0;
return FALSE;
}
memcpy((TCHAR *)lParam, cKeyData, uKeyPos * sizeof(TCHAR));
((TCHAR *)lParam)[uKeyPos] = 0;
return TRUE;
case TVM_GETUNICODEFORMAT: // Wird gerade UNI-Code verwendet
#if UNICODE
return 1;
#else
return 0;
#endif
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
//*****************************************************************************
//*
//* TreeListDraw
//*
//*****************************************************************************
// Zeichnet das Fenster
static void TreeListDraw(HWND hWnd, HDC hDc, RECT *pRect) {
COLORREF uEcColor;
COLORREF uEvColor;
COLORREF uBkColor;
COLORREF uBtColor;
COLORREF uOdColor;
COLORREF uOcColor;
COLORREF uFrColor;
COLORREF uInColor;
COLORREF uOldColor;
COLORREF uOutColor;
COLORREF uNextColor;
COLORREF uTempColor;
HRGN hRgnMain;
HRGN aRgn[MAX_COLUMNS + 1];
SIZE sSize;
RECT sRect;
RECT sArea;
RECT sButton;
TreeListData *pData;
BaseItem *pTemp;
BaseItem *pEntry;
ExtraItem *pExtra;
LPCTSTR pText;
HIMAGELIST hImgList;
unsigned uTextSize;
unsigned uRgnCount;
unsigned uAutoMask;
unsigned uFirstPos;
unsigned uStyleEx;
unsigned uColMark;
unsigned uColumn;
unsigned uState;
unsigned uStyle;
unsigned uExtra;
unsigned uNext;
unsigned uMark;
unsigned uItem;
unsigned uBits;
unsigned uPos;
unsigned uMax;
int *pOffsets;
int iRnType[MAX_COLUMNS + 1];
int iXscroll;
int iHeight;
int iIndent;
int iDelta;
int iImage;
int iShift;
int iStart;
int iCount;
int iLevel;
int iLast;
int iSize;
int iXpos;
int iYpos;
int iMaxX;
int iAdd;
int i;
pData = GetHandle(hWnd);
LOCK(pData);
GetClientRect(hWnd, &sRect);
if(!pRect)
pRect = &sRect;
iXscroll = -(int)pData->uScrollX;
pOffsets = pData->aColumnXpos;
hRgnMain = CreateRectRgn(pRect->left, pRect->top, pRect->right, pRect->bottom);
uMax = pData->uColumnCount;
if(!uMax) {
aRgn [ 0 ] = CreateRectRgn(sRect.left, sRect.top, sRect.right, sRect.bottom);
iRnType[ 0 ] = CombineRgn(aRgn[0], aRgn[0], hRgnMain, RGN_AND);
uRgnCount = 1;
} else {
for(uPos = 0; uPos < uMax; uPos++) {
uExtra = pData->aColumnPos[uPos ];
uNext = pData->aColumnPos[uPos + 1];
aRgn [uExtra] = CreateRectRgn(sRect.left + pOffsets[uExtra] + iXscroll, sRect.top, sRect.left + pOffsets[uNext] + iXscroll, sRect.bottom);
iRnType[uExtra] = CombineRgn(aRgn[uExtra], aRgn[uExtra], hRgnMain, RGN_AND);
}
aRgn [uPos] = CreateRectRgn(sRect.left + pOffsets[uPos] + iXscroll, sRect.top, sRect.right, sRect.bottom);
iRnType[uPos] = CombineRgn(aRgn[uPos], aRgn[uPos], hRgnMain, RGN_AND);
uRgnCount = uMax + 1;
}
iHeight = pData->iRowHeight;
uStyleEx = pData->uStyleEx;
uStyle = pData->uStyle;
iIndent = pData->iIndent;
iShift = pData->iShift;
uPos = pData->uScrollY;
uMax = pData->uMaxEnties + uPos;
uNext = (pData->uColumnCount <= 1) ? 1 : pData->aColumn[1].bIndex;
uFirstPos = pData->aColumnXpos[uNext];
if(iRnType[0] == NULLREGION)
iMaxX = pData->iMaxSizeX;
else
iMaxX = uFirstPos - 1;
if(uStyleEx & TVS_EX_ITEMLINES) {
iHeight--;
}
if(uStyleEx & TVS_EX_AUTOEXPANDICON) {
uAutoMask = TVIS_EXPANDED;
} else {
uAutoMask = 0;
}
if(uMax > pData->uItemPosCount) {
uMax = pData->uItemPosCount;
}
uBkColor = pData->uColors[TVC_BK ];
uFrColor = pData->uColors[TVC_FRAME];
if(pData->uStyleEx & TVS_EX_ALTERNATECOLOR) { // Abwechselnde Farben
uOdColor = pData->uColors[TVC_ODD ];
uEvColor = pData->uColors[TVC_EVEN ];
uOcColor = pData->uColors[TVC_COLODD ];
uEcColor = pData->uColors[TVC_COLEVEN];
} else {
uOdColor = uBkColor;
uEvColor = uBkColor;
uOcColor = pData->uColors[TVC_COLBK];
uEcColor = pData->uColors[TVC_COLBK];
}
if(!pData->cIsEnabled) // Wenn Fenster gessperrt grau zeichnen
if(pData->uStyleEx & TVS_EX_GRAYEDDISABLE) {
uBkColor = pData->uColors[TVC_GRAYED];
uEvColor = uEcColor;
uOdColor = uOcColor;
}
uInColor = pData->uColors[TVC_LINE];
uBtColor = pData->uColors[TVC_BOX ];
iStart = 0;
iLast = 0;
sArea.top = sRect.top + pData->uStartPixel;
SelectObject(hDc, pData->hFontN);
SelectObject(hDc, hPatternPen);
SetBkColor(hDc, uBkColor);
SetBkMode(hDc, TRANSPARENT);
SetTextAlign(hDc, TA_LEFT | TA_TOP);
SetTextColor(hDc, pData->uColors[TVC_TEXT]);
//******************** Einträge zeichnen **************************************
for(; uPos < uMax; uPos++) { // Alle Einträge ausgeben
uItem = pData->pItemPos[uPos];
pEntry = pData->pTreeItems[uItem];
if(!pEntry)
break;
if((pEntry->uState & TVIS_SELECTED) && (uStyleEx & TVS_EX_FULLROWMARK)) {
if(uStyleEx & TVS_EX_ALTERNATECOLOR)
uOutColor = (uPos & 1) ? pData->uColors[TVC_MARKODD] : pData->uColors[TVC_MARKEVEN];
else
uOutColor = pData->uColors[TVC_MARK];
uMark = (unsigned)~TVIS_BKCOLOR;
uColMark = 0;
} else
if(uPos & 1) { // Farbe wechselweise ändern
uColMark = pData->aColumn[0].bMark;
uOutColor = (uColMark) ? uOcColor : uOdColor;
uMark = 0xFFFFFFFF;
} else {
uColMark = pData->aColumn[0].bMark;
uOutColor = (uColMark) ? uEcColor : uEvColor;
uMark = 0xFFFFFFFF;
}
sArea.bottom = sArea.top + pData->iRowHeight;
sArea.left = iXscroll;
iLevel = pEntry->uLevel;
if(iRnType[0] == NULLREGION) {
goto ExtraDraw;
}
uBits = pEntry->uState & 0xFFFF;
uBits |= pEntry->bFlags << 16;
uBits &= uMark;
iImage = (uBits & LVIS_SELECTED) ? pEntry->iSelectedImage : pEntry->iImage;
pText = pEntry->pText;
uTextSize = pEntry->uTextSize;
if(pData->uSelectedSub && uItem == pData->uSelectedItem) {
if(pData->uSelectedCount <= 1 || !(pData->uStyleEx & TVS_EX_SUBSELECT)) {
uBits &= ~TVIS_SELECTED;
}
}
if(pEntry->bCallback) {
CallbackEntry(pData, pEntry, uItem, pEntry->bCallback, &iImage, &uTextSize, &pText);
pEntry = pData->pTreeItems[uItem];
if(!pEntry)
break;
}
SelectObject(hDc, aRgn[0]);
if((uStyleEx & (TVS_EX_ITEMLINES | TVS_EX_FULLROWITEMS)) == (TVS_EX_ITEMLINES | TVS_EX_FULLROWITEMS)) {
sButton.left = 0;
sButton.right = iIndent * (iLevel + 1) + 2;
sButton.bottom = sArea.bottom + 1;
sButton.top = sArea.bottom - 2;
SetBkColor(hDc, uFrColor);
ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sButton, NULL, 0, NULL);
}
if(pData->aColumn[0].bMark) { // Ist die erste Spalte markiert
uBkColor = pData->uColors[TVC_COLBK];
}
SetBkColor(hDc, (uStyleEx & TVS_EX_FULLROWITEMS) ? uOutColor : uBkColor);
if(pData->cHasRootRow)
iLevel++;
if(iLevel <= 0)
goto NoRootLines;
if(uStyle & (TVS_HASBUTTONS | TVS_HASLINES)) {
iLevel--;
}
if(uStyleEx & TVS_EX_FULLROWITEMS) {
sArea.bottom--;
iAdd = 1;
} else {
iAdd = 0;
}
if(uStyle & TVS_HASLINES) {
pTemp = pData->pTreeItems[pEntry->uParent];
sArea.right = sArea.left + 1; // Eine leerer Pixelreihe am Anfang
ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL);
sArea.left += iIndent * iLevel + 1;
for(i = iLevel; i > 0; i--) { // Bereich vor Schaltflächen
sArea.right = sArea.left;
sArea.left -= iIndent;
ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL);
iXpos = sArea.left + iShift;
if(pTemp) {
if(pTemp->uNextItem) { // Zeichne vertikale Linien
MoveToEx(hDc, iXpos, sArea.top | 1, NULL);
LineTo(hDc, iXpos, sArea.bottom + iAdd);
}
pTemp = pData->pTreeItems[pTemp->uParent];
}
}
sArea.left += iIndent * iLevel;
} else { // Ohne Linien zeichnen
if(iLevel > 0) {
sArea.right = sArea.left + iIndent * iLevel;
ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL);
sArea.left += sArea.right;
}
}
if(uStyle & TVS_HASBUTTONS) { // Fenster mit Schaltflächen ?
sArea.right = sArea.left + iIndent;
iXpos = sArea.left + iShift;
iYpos = sArea.top + pData->iRowHeight / 2;
ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL);
if(uStyle & TVS_HASLINES) { // Linien unter Schaltflächen
MoveToEx(hDc, iXpos, sArea.top | 1, NULL);
if(pEntry->uNextItem)
LineTo(hDc, iXpos, sArea.bottom + iAdd);
else
LineTo(hDc, iXpos, iYpos + 1);
MoveToEx(hDc, iXpos + 1 + (iYpos & 1), iYpos, NULL);
LineTo(hDc, sArea.right , iYpos);
}
if(pEntry->bFlags & TVIX_HASBUTTON) { // Schaltflächen zeichnen
sButton.left = iXpos - 4;
sButton.top = iYpos - 4;
sButton.right = iXpos + 5;
sButton.bottom = iYpos + 5;
if(pData->cGlyphOk) { // Thema benutzen
uState = ((uBits ^ TVIS_EXPANDED) & (TVIS_EXPANDED | TVIS_EXPANDPARTIAL)) ? GLPS_CLOSED : GLPS_OPENED;
pDrawThemeBackg(pData->hTheme, hDc, TVP_GLYPH, uState, &sButton, 0);
} else {
SetBkColor(hDc, uBtColor);
ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sButton, NULL, 0, NULL);
sButton.left += 1;
sButton.top += 1;
sButton.right -= 1;
sButton.bottom -= 1;
SetBkColor(hDc, pData->uColors[TVC_BOXBG]);
ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sButton, NULL, 0, NULL);
sButton.left = iXpos - 2;
sButton.top = iYpos ;
sButton.right = iXpos + 3;
sButton.bottom = iYpos + 1;
SetBkColor(hDc, uInColor);
ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sButton, NULL, 0, NULL);
// '+' statt '-' Schaltfläsche zeichnenen
if((uBits ^ TVIS_EXPANDED) & (TVIS_EXPANDED | TVIS_EXPANDPARTIAL)) {
sButton.left = iXpos ;
sButton.top = iYpos - 2;
sButton.right = iXpos + 1;
sButton.bottom = iYpos + 3;
ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sButton, NULL, 0, NULL);
}
}
SetBkColor(hDc, uBkColor);
}
sArea.left += iIndent;
} else
if(uStyle & TVS_HASLINES) { // Nur Linien zeichnen ohne Schaltflächen
sArea.right = sArea.left + iIndent;
ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL);
iYpos = sArea.top + pData->iRowHeight / 2;
iXpos = sArea.left + iShift;
MoveToEx(hDc, iXpos, sArea.top | 1, NULL);
if(pEntry->uNextItem)
LineTo(hDc, iXpos, sArea.bottom);
else
LineTo(hDc, iXpos, iYpos + 1);
MoveToEx(hDc, iXpos + 1 + (iYpos & 1), iYpos, NULL);
LineTo(hDc, sArea.right , iYpos);
sArea.left += iIndent;
}
if(uStyleEx & TVS_EX_FULLROWITEMS)
sArea.bottom++;
NoRootLines:
if(uStyleEx & TVS_EX_ITEMLINES) { // Linien um den Eintrag zeichnen
iAdd = 1;
sArea.right = sArea.left + 1;
if(uStyleEx & TVS_EX_FULLROWITEMS) {
iStart = sArea.left;
iAdd = 0;
} else
if(iLevel >= 0) {
SetBkColor(hDc, uFrColor);
ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL);
sArea.left++;
sArea.bottom--;
iStart = sArea.left;
} else {
ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL);
sArea.left++;
sArea.bottom--;
iStart = sArea.left - 1;
}
} else {
iAdd = 0;
}
SetBkColor(hDc, (uBits & TVIS_BKCOLOR) ? pEntry->uColorBk : uOutColor);
SelectObject(hDc, (uBits & TVIS_BOLD) ? pData->hFontB : pData->hFontN);
if(pData->hStates) { // State-Icons anzeigen
sArea.right = sArea.left + pData->iStatesXsize;
iYpos = sArea.top + (iHeight - pData->iStatesYsize) / 2;
i = (uBits & LVIS_STATEIMAGEMASK) >> 12;
sArea.right += iAdd;
ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL);
sArea.left += iAdd;
if(pData->hStates == THEMEIMGLIST) { // Mit Thema zeichnen
if(pData->uStyleEx & TVS_EX_BITCHECKBOX)
i++;
if(i >= 1 && i <= 2) {
uState = (i == 1) ? CBS_UNCHECKEDNORMAL : CBS_CHECKEDNORMAL;
pDrawThemeBackg(pData->hThemeBt, hDc, BP_CHECKBOX, uState, &sArea, 0);
}
} else {
ImageList_Draw(pData->hStates, i, hDc, sArea.left, iYpos, ILD_TRANSPARENT);
}
sArea.left += pData->iStatesXsize;
iAdd = 0;
}
if(pData->hImages && iImage != TV_NOIMAGE) { // Icon zeichnen vom Haupteintrag
if(iImage >= TV_SECONDICON) { // Sub-Image-Liste verwenden
if(iImage & TV_NOAUTOEXPAND) {
iImage &= ~TV_NOAUTOEXPAND; // Kein Auto-Expant bei diesem Icon
} else
if((pEntry->uState & uAutoMask) && pEntry->uFirstChild) {
iImage += 1; // Auto-Expant aktivieren
}
iImage -= TV_SECONDICON;
sArea.right = sArea.left + pData->iSubImgXsize;
iYpos = sArea.top + (iHeight - pData->iSubImgYsize) / 2;
pEntry->bFlags |= TVIX_HASIMAGE;
sArea.right += iAdd;
ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL);
sArea.left += iAdd;
ImageList_Draw(pData->hSubImg, iImage, hDc, sArea.left, iYpos, ILD_TRANSPARENT | (uBits & (TVIS_OVERLAYMASK | LVIS_CUT)));
sArea.left += pData->iSubImgXsize;
iAdd = 0;
} else { // Haup-Image-Liste verwenden
if(iImage & TV_NOAUTOEXPAND) {
iImage &= ~TV_NOAUTOEXPAND; // Kein Auto-Expant bei diesem Icon
} else
if((pEntry->uState & uAutoMask) && pEntry->uFirstChild) {
iImage += pData->iAutoAdd; // Auto-Expant aktivieren
}
sArea.right = sArea.left + pData->iImagesXsize;
iYpos = sArea.top + (iHeight - pData->iImagesYsize) / 2;
pEntry->bFlags |= TVIX_HASIMAGE;
sArea.right += iAdd;
ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL);
sArea.left += iAdd;
ImageList_Draw(pData->hImages, iImage, hDc, sArea.left, iYpos, ILD_TRANSPARENT | (uBits & (TVIS_OVERLAYMASK | LVIS_CUT)));
sArea.left += pData->iImagesXsize;
iAdd = 0;
}
} else {
pEntry->bFlags &= ~TVIX_HASIMAGE;
}
sArea.right = uFirstPos; // Text ausgeben vom Haupteintrag
iYpos = sArea.top + (iHeight - pData->iFontHeight) / 2;
if(uBits & (TVIS_SELECTED | TVIS_DROPHILITED | TVIS_UNDERLINE | TVIS_TRACKED | TVIS_TEXTCOLOR | TVIS_FOCUSED)) {
// Das Feld speziel zeichnen
TCHAR *pPtr = (TCHAR *)new(TCHAR, uTextSize + 4);
INT *pPos = (INT *)new(INT, uTextSize + 4);
ExtTextOut(hDc, 0, 0, ETO_OPAQUE | ETO_CLIPPED, &sArea, NULL, 0, NULL);
sButton.top = iYpos;
sButton.left = sArea.left + 4;
sButton.right = sArea.right;
sButton.bottom = iYpos + pData->iFontHeight + 2;
if(!uTextSize) { // Feld ohne Text ?
sButton.right -= 2;
sButton.bottom--;
pEntry->iTextPixels = 0;
} else {
if(pData->uStyleEx & TVS_EX_FULLROWMARK)
if(pData->cHasFocus == 0 || uItem != pData->uSelectedItem || pData->uSelectedSub)
if((uBits & TVIS_SELECTED) && !(uBits & (TVIS_DROPHILITED | TVIS_FOCUSED))) {
sButton.left -= 1;
}
DrawText(hDc, pText, uTextSize, &sButton, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX | DT_CALCRECT);
pEntry->iTextPixels = sButton.right - sButton.left;
}
// Passt der Text in die Spalte
if(sButton.left + pEntry->iTextPixels >= (int)(sArea.right - pData->uScrollX)) {
iSize = sArea.right - pData->uScrollX - sButton.left - 2;
iSize -= (uBits & TVIS_BOLD) ? pData->uTrippleB : pData->uTrippleN;
if(iSize < 3) {
iCount = 0;
} else {
GetTextExtentExPoint(hDc, pText, uTextSize, iSize, &iCount, pPos, &sSize);
}
// Temporären Text mit "..." erzeugen
memcpy(pPtr , pText, iCount * sizeof(TCHAR));
memcpy(pPtr + iCount, _T("..."), 4 * sizeof(TCHAR));
pText = pPtr;
uTextSize = iCount + 3;
sButton.right = sArea.right - 2;
}
// Das Feld selektiert zeichnen
if((uBits & TVIS_SELECTED) && pData->cHasFocus && uItem == pData->uSelectedItem && !pData->uSelectedSub) {
uTempColor = GetSysColor(COLOR_HIGHLIGHTTEXT);
SelectObject(hDc, GetSysColorBrush(COLOR_HIGHLIGHT));
Rectangle(hDc, sButton.left - 2, sButton.top - 1, sButton.right + 2, sButton.bottom + 1);
} else {
if(uBits & TVIS_DROPHILITED) {
uTempColor = GetSysColor(COLOR_HIGHLIGHTTEXT);
SelectObject(hDc, GetSysColorBrush(COLOR_HIGHLIGHT));
SelectObject(hDc, GetStockObject(NULL_PEN));
Rectangle(hDc, sButton.left - 2, sButton.top - 1, sButton.right + 2, sButton.bottom + 1);
} else
if(uBits & TVIS_SELECTED) { // Ist das Feld ohne Focus ausgewählt
if(pData->cHasFocus) {
uTempColor = GetSysColor(COLOR_HIGHLIGHTTEXT);
SelectObject(hDc, GetSysColorBrush(COLOR_HIGHLIGHT));
SelectObject(hDc, GetStockObject(NULL_PEN));
} else {
if(uBits & TVIS_TEXTCOLOR)
uTempColor = pEntry->uColorText;
else
uTempColor = pData ->uColors[TVC_TEXT];
if(pData->uStyleEx & TVS_EX_FULLROWMARK)
SelectObject(hDc, GetStockObject(NULL_BRUSH));
else
SelectObject(hDc, GetSysColorBrush(COLOR_3DFACE));
SelectObject(hDc, GetStockObject(NULL_PEN));
}
Rectangle(hDc, sButton.left - 2, sButton.top - 1, sButton.right + 2, sButton.bottom + 1);
} else { // Das Feld normal zeichnen
if(uBits & TVIS_TRACKED)
uTempColor = pData ->uColors[TVC_TRACK];
else
if(uBits & TVIS_TEXTCOLOR)
uTempColor = pEntry->uColorText;
else
uTempColor = pData ->uColors[TVC_TEXT ];
sButton.right--;
sButton.left --;
}
SelectObject(hDc, hPatternPen);
if(uBits & TVIS_FOCUSED) { // Einen punktierten Rahmen um den Text zeichnen
SelectObject(hDc, GetStockObject(NULL_BRUSH));
Rectangle(hDc, sButton.left - 2, sButton.top - 1, sButton.right + 2, sButton.bottom + 1);
}
}
SetTextColor(hDc, uTempColor);
sButton.left += pData->iFontOff;
DrawText(hDc, pText, uTextSize, &sButton, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX);
if(uBits & (TVIS_UNDERLINE | TVIS_TRACKED)) // Text unterstreichen
if(pText && *pText) {
sButton.left -= pData->iFontOff;
sButton.right -= pData->iFontOff + 1;
sButton.top += pData->iFontLine;
sButton.bottom = sButton.top + 1;
uOldColor = SetBkColor(hDc, uTempColor);
ExtTextOut(hDc, 0, 0, ETO_OPAQUE | ETO_CLIPPED, &sButton, NULL, 0, NULL);
SetBkColor(hDc, uOldColor);
}
SetTextColor(hDc, pData->uColors[TVC_TEXT]);
delete(pPos);
delete(pPtr);
} else { // Das Feld normal ausgeben
if(!pEntry->iTextPixels && uTextSize) {
sButton.top = iYpos;
sButton.left = sArea.left + 4;
sButton.right = sArea.right;
sButton.bottom = iYpos + pData->iFontHeight + 2;
// Textbreite berechen
DrawText(hDc, pText, uTextSize, &sButton, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX | DT_CALCRECT);
pEntry->iTextPixels = sButton.right - sButton.left;
}
// Ist der Text größer als die Spalte
if(sArea.left + pEntry->iTextPixels >= (int)(sArea.right - pData->uScrollX)) {
INT *pPos = (INT *)new(INT, uTextSize);
iSize = sArea.right - sArea.left - pData->uScrollX;
iSize -= (uBits & TVIS_BOLD) ? pData->uTrippleB : pData->uTrippleN;
if(iSize < 3) {
iCount = 0;
} else {
GetTextExtentExPoint(hDc, pText, uTextSize, iSize, &iCount, pPos, &sSize);
}
if(iCount > 0) { // Passen noch Buchstaben vor "..."
sButton.right = sArea.right;
sArea.right = sArea.left + 2 + pPos[iCount - 1];
ExtTextOut(hDc, sArea.left + 2, iYpos, ETO_OPAQUE | ETO_CLIPPED, &sArea, pText, iCount, NULL);
sArea.left = sArea.right;
sArea.right = sButton.right;
ExtTextOut(hDc, sArea.left , iYpos, ETO_OPAQUE | ETO_CLIPPED, &sArea, _T("..."), 3, NULL);
} else {
ExtTextOut(hDc, sArea.left + 2, iYpos, ETO_OPAQUE | ETO_CLIPPED, &sArea, _T("..."), 3, NULL);
}
delete(pPos);
} else {
ExtTextOut(hDc, sArea.left + 2, iYpos, ETO_OPAQUE | ETO_CLIPPED, &sArea, pText, uTextSize, NULL);
}
}
i = sArea.left - iXscroll;
i += pEntry->iTextPixels + 5;
if(i > iMaxX)
iMaxX = i;
if(uStyleEx & TVS_EX_ITEMLINES) { // Linien um den Eintrag zeichnen
SetBkColor(hDc, uFrColor);
if(iLast > iStart) { // Ist das Feld nach links eingerückt gegenüber dem oberen
sArea.top--;
sArea.bottom = sArea.top + 1;
sArea.left = iStart - 1;
ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL);
sArea.top++;
}
iLast = iStart; // Linie unter Feld zeichnen
sArea.top += iHeight;
sArea.left = iStart;
sArea.bottom = sArea.top + 1;
ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL);
sArea.top -= iHeight;
}
//************ Extraeinträge zeichnen *********************************
ExtraDraw:
uNextColor = uOutColor; // Hintergrundfarbe für die nächste Spalte
if(pData->aColumn[0].bMark) { // Ist die erste Spalte markiert
uBkColor = pData->uColors[TVC_BK];
}
for(uColumn = 1; uColumn <= pData->uColumnCount; uColumn++) { // Extra Spalten zeichnen
uExtra = pData->aColumnPos[uColumn ];
uNext = pData->aColumnPos[uColumn + 1];
if(pData->aColumn[uExtra].sReal == 0) // Ist die Spalte sichtbar
if(uColumn < pData->uColumnCount) {
continue;
}
if(uColMark != pData->aColumn[uExtra].bMark) // Ist die Spalte anderst markiert
if(!(pEntry->uState & TVIS_SELECTED) || !(uStyleEx & TVS_EX_FULLROWMARK)) {
if(uPos & 1) {
uColMark = pData->aColumn[uExtra].bMark;
uOutColor = (uColMark) ? uOcColor : uOdColor;
} else {
uColMark = pData->aColumn[uExtra].bMark;
uOutColor = (uColMark) ? uEcColor : uEvColor;
}
}
GetRgnBox(aRgn[uExtra], &sButton);
if(iRnType[uExtra] == NULLREGION)
continue;
SelectObject(hDc, aRgn[uExtra]);
sArea.left = pData->aColumnXpos[uExtra];
sArea.left += iXscroll;
if(uStyleEx & TVS_EX_ITEMLINES) { // Linie um den Eintrag zeichnen
SetBkColor(hDc, uFrColor);
// Linke Linie
sArea.right = sArea.left + 1;
sArea.bottom += 1;
ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL);
sArea.left += 1; // Untere Linie
sArea.top += iHeight;
sArea.bottom = sArea.top + 1;
sArea.right = pData->aColumnXpos[uNext];
if(uColumn < pData->uColumnCount)
sArea.right += iXscroll;
ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL);
sArea.top -= iHeight;
sArea.bottom -= 1;
iAdd = 1;
}
if(sArea.left > (int)pData->uSizeX)
break; // Noch im sichtbaren Bereich
sArea.right = pData->aColumnXpos[uNext];
if(uColumn < pData->uColumnCount) { // Ist es die letze Spalte ?
sArea.right += iXscroll;
pExtra = pData->pExtraItems[uExtra - 1][uItem];
if(!pExtra)
uNextColor = uOutColor;
} else {
pExtra = 0;
}
if(!pExtra) { // Leeres Feld zeichnen
SetBkColor(hDc, uNextColor);
ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL);
} else {
iSize = pData->iSubImgXsize;
hImgList = pData->hSubImg;
iImage = pExtra->iImage;
pText = pExtra->pText;
uTextSize = pExtra->uTextSize;
uBits = (pExtra->uState & 0xFFFF);
uBits |= (pExtra->bFlags << 16);
uBits |= pEntry->uState & TVIS_BASEFLAGS;
uBits &= uMark;
if(uExtra != pData->uSelectedSub) {
uBits &= ~TVIS_SELECTED;
}
if(pExtra->bCallback) { // Text über Callback holen
CallbackExtra(pData, pEntry, pExtra, uItem, uExtra, pExtra->bCallback, &iImage, &uTextSize, &pText);
pExtra = pData->pExtraItems[uExtra - 1][uItem];
if(!pExtra)
break;
}
uNextColor = (uBits & TVIS_BKCOLOR) ? pExtra->uColorBk : uOutColor;
SetBkColor(hDc, uNextColor);
if(pData->aColumn[uExtra].bEdit >= TVAX_CHECK) { // Checkboxen statt Icons
hImgList = pData->hChecks;
iSize = pData->iChecksXsize;
iImage = (pExtra->uState & TVIS_STATEIMAGEMASK) >> 12;
uBits &= ~TVIS_OVERLAYMASK;
if(iImage & 8)
if(pData->aColumn[uExtra].bFlags & TVAE_STATEENABLE) {
iImage &= 7;
}
}
if(hImgList && iImage > TV_NOIMAGE) { // Icon zeichnen
sArea.right = sArea.left + iSize + 2;
iYpos = sArea.top + (iHeight - iSize) / 2;
pExtra->bFlags |= TVIX_HASIMAGE;
SelectObject(hDc, (uBits & TVIS_BOLD) ? pData->hFontB : pData->hFontN);
ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL);
if(hImgList == THEMEIMGLIST) { // Mit Thema zeichnen
if(pData->uStyleEx & TVS_EX_BITCHECKBOX)
iImage++;
if(iImage == 1 || iImage == 2) {
uState = (iImage == 1) ? CBS_UNCHECKEDNORMAL : CBS_CHECKEDNORMAL;
pDrawThemeBackg(pData->hThemeBt, hDc, BP_CHECKBOX, uState, &sArea, 0);
}
} else {
ImageList_Draw(hImgList, iImage, hDc, sArea.left + 1, iYpos, ILD_TRANSPARENT | (uBits & (TVIS_OVERLAYMASK | LVIS_CUT)));
}
sArea.left += iSize + 1;
sArea.right = pData->aColumnXpos[uNext];
sArea.right += iXscroll;
} else {
pExtra->bFlags &= ~TVIX_HASIMAGE;
}
iYpos = sArea.top + (iHeight - pData->iFontHeight) / 2;
SelectObject(hDc, (uBits & TVIS_BOLD) ? pData->hFontB : pData->hFontN);
if(uBits & (TVIS_SELECTED | TVIS_DROPHILITED | TVIS_UNDERLINE | TVIS_TRACKED | TVIS_TEXTCOLOR | TVIS_FOCUSED)) {
// Text ausgeben in spezilem Format
TCHAR *pPtr = (TCHAR *)new(TCHAR, uTextSize + 4);
INT *pPos = (INT *)new(INT, uTextSize + 4);
ExtTextOut(hDc, 0, 0, ETO_OPAQUE | ETO_CLIPPED, &sArea, NULL, 0, NULL);
sButton.top = iYpos;
sButton.left = sArea.left + 4;
sButton.right = sArea.right;
sButton.bottom = iYpos + pData->iFontHeight + 2;
if(!uTextSize) {
sButton.left--;
sButton.right -= 2;
sButton.bottom--;
pExtra->iTextPixels = 0;
} else {
if(pData->uStyleEx & TVS_EX_FULLROWMARK)
if(pData->cHasFocus == 0 || uItem != pData->uSelectedItem)
if((uBits & TVIS_SELECTED) && !(uBits & (TVIS_DROPHILITED | TVIS_FOCUSED))) {
sButton.left -= 2;
}
DrawText(hDc, pText, uTextSize, &sButton, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX | DT_CALCRECT);
pExtra->iTextPixels = sButton.right - sButton.left;
}
// Passt der Text in die Spalte
if(sButton.left + pExtra->iTextPixels >= sArea.right) {
if(uTextSize > 253)
uTextSize = 253;
iSize = sArea.right - sButton.left - 2;
iSize -= (uBits & TVIS_BOLD) ? pData->uTrippleB : pData->uTrippleN;
if(iSize < 3) {
iCount = 0;
} else {
GetTextExtentExPoint(hDc, pText, uTextSize, iSize, &iCount, pPos, &sSize);
}
memcpy(pPtr , pText, iCount * sizeof(TCHAR));
memcpy(pPtr + iCount, _T("..."), 4 * sizeof(TCHAR));
pText = pPtr;
uTextSize = iCount + 3;
sButton.right = sArea.right - 2;
}
switch(pData->aColumn[uExtra].bAlign) { // Textausrichtung ausgleichen
case DT_CENTER:
iDelta = sArea .right - sArea .left;
iDelta -= sButton.right - sButton.left;
iDelta -= 6;
iDelta /= 2;
sButton.right += iDelta;
sButton.left += iDelta;
break;
case DT_RIGHT:
iDelta = sArea .right - sArea .left;
iDelta -= sButton.right - sButton.left;
iDelta -= 6;
sButton.right += iDelta;
sButton.left += iDelta;
break;
}
// Ist das Feld ohne Focus ausgewählt
if((uBits & TVIS_SELECTED) && pData->cHasFocus && uItem == pData->uSelectedItem) {
uTempColor = GetSysColor(COLOR_HIGHLIGHTTEXT);
SelectObject(hDc, GetSysColorBrush(COLOR_HIGHLIGHT));
Rectangle(hDc, sButton.left - 2, sButton.top - 1, sButton.right + 2, sButton.bottom + 1);
} else {
if(uBits & TVIS_DROPHILITED) {
uTempColor = GetSysColor(COLOR_HIGHLIGHTTEXT);
SelectObject(hDc, GetSysColorBrush(COLOR_HIGHLIGHT));
SelectObject(hDc, GetStockObject(NULL_PEN));
Rectangle(hDc, sButton.left - 2, sButton.top - 1, sButton.right + 2, sButton.bottom + 1);
} else
if(uBits & TVIS_SELECTED && uItem == pData->uSelectedItem) {
if(uBits & TVIS_TEXTCOLOR)
uTempColor = pEntry->uColorText;
else
uTempColor = pData->uColors[TVC_TEXT];
if(pData->uStyleEx & TVS_EX_FULLROWMARK)
SelectObject(hDc, GetStockObject(NULL_BRUSH));
else
SelectObject(hDc, GetSysColorBrush(COLOR_3DFACE));
SelectObject(hDc, GetStockObject(NULL_PEN));
Rectangle(hDc, sButton.left - 2, sButton.top - 1, sButton.right + 2, sButton.bottom + 1);
} else {
if(uBits & TVIS_TRACKED)
uTempColor = pData ->uColors[TVC_TRACK];
else
if(uBits & TVIS_TEXTCOLOR)
uTempColor = pExtra->uColorText;
else
uTempColor = pData ->uColors[TVC_TEXT ];
sButton.right--;
sButton.left --;
}
SelectObject(hDc, hPatternPen);
if(uBits & TVIS_FOCUSED) { // Punktierten Rahmen um Text zeichnen
SelectObject(hDc, GetStockObject(NULL_BRUSH));
Rectangle(hDc, sButton.left - 2, sButton.top - 1, sButton.right + 2, sButton.bottom + 1);
}
}
SetTextColor(hDc, uTempColor);
sButton.left += pData->iFontOff;
DrawText(hDc, pText, uTextSize, &sButton, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX);
if(uBits & (TVIS_UNDERLINE | TVIS_TRACKED)) // Text unterstreichen
if(pText && *pText) {
sButton.left -= pData->iFontOff;
sButton.right -= pData->iFontOff + 1;
sButton.top += pData->iFontLine;
sButton.bottom = sButton.top + 1;
uOldColor = SetBkColor(hDc, uTempColor);
ExtTextOut(hDc, 0, 0, ETO_OPAQUE | ETO_CLIPPED, &sButton, NULL, 0, NULL);
SetBkColor(hDc, uOldColor);
}
SetTextColor(hDc, pData->uColors[TVC_TEXT]);
delete(pPos);
delete(pPtr);
} else { // Den Text ganz normal ausgeben
if(!pExtra->iTextPixels) {
sButton.top = iYpos;
sButton.left = sArea.left + 4;
sButton.right = sArea.right;
sButton.bottom = iYpos + pData->iFontHeight + 2;
if(uTextSize) { // Textbreite berechen
DrawText(hDc, pText, uTextSize, &sButton, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX | DT_CALCRECT);
pExtra->iTextPixels = sButton.right - sButton.left;
} else {
pExtra->iTextPixels = 0;
}
}
// Ist der Text größer als die Spalte
if(sArea.left + pExtra->iTextPixels >= sArea.right) {
INT *pPos = (INT *)new(INT, uTextSize);
iSize = sArea.right - sArea.left;
iSize -= (uBits & TVIS_BOLD) ? pData->uTrippleB : pData->uTrippleN;
if(iSize < 3) {
iCount = 0;
} else {
GetTextExtentExPoint(hDc, pText, uTextSize, iSize, &iCount, pPos, &sSize);
}
if(iCount > 0) { // Passen noch Buchstaben vor "..."
sButton.right = sArea.right;
sArea.right = sArea.left + 2 + pPos[iCount - 1];
ExtTextOut(hDc, sArea.left + 2, iYpos, ETO_OPAQUE | ETO_CLIPPED, &sArea, pText, iCount, NULL);
sArea.left = sArea.right;
sArea.right = sButton.right;
ExtTextOut(hDc, sArea.left , iYpos, ETO_OPAQUE | ETO_CLIPPED, &sArea, _T("..."), 3, NULL);
} else {
ExtTextOut(hDc, sArea.left + 2, iYpos, ETO_OPAQUE | ETO_CLIPPED, &sArea, _T("..."), 3, NULL);
}
delete(pPos);
} else {
switch(pData->aColumn[uExtra].bAlign) { // Textausrichtung
case DT_CENTER:
SetTextAlign(hDc, TA_CENTER | TA_TOP);
ExtTextOut(hDc, (sArea.right + sArea.left) / 2, iYpos, ETO_OPAQUE | ETO_CLIPPED, &sArea, pText, uTextSize, NULL);
SetTextAlign(hDc, TA_LEFT | TA_TOP);
break;
case DT_RIGHT:
SetTextAlign(hDc, TA_RIGHT | TA_TOP);
ExtTextOut(hDc, sArea.right - 2, iYpos, ETO_OPAQUE | ETO_CLIPPED, &sArea, pText, uTextSize, NULL);
SetTextAlign(hDc, TA_LEFT | TA_TOP);
break;
default
:
ExtTextOut(hDc, sArea.left + 2, iYpos, ETO_OPAQUE | ETO_CLIPPED, &sArea, pText, uTextSize, NULL);
break;
}
}
}
}
}
sArea.top += pData->iRowHeight;
}
if(sArea.top < sRect.bottom) { // Untere Fläche ohne Einträge füllen
SelectObject(hDc, hRgnMain);
// Gibt es markierte Spalten
if(pData->uMarkedCols > 0 && (pData->cIsEnabled || !(pData->uStyleEx & TVS_EX_GRAYEDDISABLE))) {
sRect.right = 0 - pData->uScrollX;
for(uColumn = 0; uColumn < pData->uColumnCount; uColumn++) {
uExtra = pData->aColumnPos[uColumn ];
uNext = pData->aColumnPos[uColumn + 1];
uMark = pData->aColumn[uExtra].bMark;
sRect.top = sArea.top;
sRect.left = sRect.right;
sRect.right = pData->aColumnXpos[uNext] + uMark;
sRect.right -= pData->uScrollX;
if(sRect.right == sRect.left + 1)
uMark = 0;
SetBkColor(hDc, (uMark) ? pData->uColors[TVC_COLBK] : uBkColor);
ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sRect, NULL, 0, NULL);
}
sRect.top = sArea.top;
sRect.left = sRect.right;
sRect.right = pData->uSizeX;
SetBkColor(hDc, uBkColor);
ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sRect, NULL, 0, NULL);
} else { // Keine markierten Spalten
sRect.top = sArea.top;
SetBkColor(hDc, uBkColor);
ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sRect, NULL, 0, NULL);
}
}
if(pData->iMaxSizeX != iMaxX) { // X-Scrollbar aktuallisieren
pData->iMaxSizeX = iMaxX;
if(pData->uColumnCount == 0)
if(pData->iMaxSizeX != (int)pData->uOldXCount) {
UpdateScrollX(pData);
}
}
UNLOCK(pData);
DeleteObject(hRgnMain);
for(uPos = 0; uPos < uRgnCount; uPos++) {
DeleteObject(aRgn[uPos]);
}
}