Files
envi-code/SourceCode/Code2026/CoreCommand/TreeListWnd.cpp
T
gjm 164968b62e chore
把非utf8-bom编码的cpp/h文件改为 utf8 bom 编码, msvc识别utf8编码时,如果不是bom格式的,会使用当前cp_oem来解码.
2026-10-04 00:04:20 +08:00

10755 lines
245 KiB
C++

//*****************************************************************************
//*
//*
//* TreeListWnd.cpp
//*
//*
//*****************************************************************************
//
// Dieser Code erstellt ein Tree-Fenster mit einer Liste
//
//
// Copyright (C) Anton Zechner 2007
//
// 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鰂fendlicht werden.
// Komerzielle Nutzer m黶sen ihren Code ebenfalls ver鰂fentlichen, oder
// eine Nutzungsvereinbarung mit mir treffen.
//
// az_software@inode.at
//
//
#include "stdafx.h"
#include <windows.h>
#include <string.h>
#include <tchar.h>
#include "TreeListWnd.h"
#include "CoreTools.h"
#ifdef _MSC_VER
#pragma warning( disable :4996 )
#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 wcslen
#define str_cmp wcscmp
#define str_ncpy wcsncpy
#define str_ncmp wcsncmp
#define str_icmp wcsicmp
#else
#define str_len 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 GWL_WNDPROC
#define GWL_WNDPROC (-4)
#define GWL_HINSTANCE (-6)
#define GWL_HWNDPARENT (-8)
#define GWL_STYLE (-16)
#define GWL_EXSTYLE (-20)
#define GWL_USERDATA (-21)
#define GWL_ID (-12)
#endif
#define MAX_COLUMNS 32
#define MAX_COLORS 11
#define EN_RETURN 0x1578
#define EN_ESCAPE 0x1579
#define ID_TOOLTIPCHECK 0x3912
#define SORT_NOUPDATE 1234567
#define FIRST_LINE 0xFFFFFFFE
#define I_CCB I_CHILDRENCALLBACK
#define U(h) ((unsigned)(h))
#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_BASEFLAGS (TVIS_EXPANDED|TVIS_EXPANDEDONCE|TVIS_EXPANDPARTIAL|TVIS_SELECTED)
#define TVIS_TRACKED (TVIX_TRACKED<<16)
#define TVIS_BKCOLOR (TVIX_BKCOLOR<<16)
#define TVIS_FOCUSED (TVIX_FOCUSED<<16)
#define TVIS_TEXTCOLOR (TVIX_TEXTCOLOR<<16)
#define TVC_DESELECT 0x8000
#define DEFAULT_IDENT 19
#define DEFAULT_SHIFT 7
#define TVIX_VARBUTTON 0x01 // Schaltfl鋍hen sind nicht fix eingestellt
#define TVIX_HASBUTTON 0x02 // Der Eintrag hat eine Schaltfl鋍he
#define TVIX_HASIMAGE 0x04 // Der Eintrag hat ein Icon
#define TVIX_TRACKED 0x08 // Der Eintrag unter dem Cursor
#define TVIX_TEXTCOLOR 0x10 // Der Eintrag hat eine eigene Textfarbe
#define TVIX_BKCOLOR 0x20 // Der Eintrag hat eine eigene Hintergrundfarbe
#define TVIX_FOCUSED 0x40 // Der Eintrag hat einen Eingabe-Focus
namespace
{
int roundToInt(double value, double threshold)
{
if (threshold <= 0.0) threshold = 0.0;
if (threshold >= 1.0) threshold = 1.0;
double intPart;
double frac = modf(value, &intPart);
if (value >= 0.0)
{
if (frac >= threshold)
return (int)(intPart + 1);
else
return (int)(intPart);
}
else
{
if (-frac >= threshold)
return (int)(intPart - 1);
else
return (int)(intPart);
}
}
double getDPIScale()
{
return CoreTools::GetDPIScale();
}
bool DrawScaledImage(HIMAGELIST hImgList, int iImage, HDC hDC, int x, int y, UINT ustyle)
{
return CoreTools::drawIcon(hImgList, iImage, hDC, CPoint(x, y), ustyle);
}
}
typedef struct
{
LPARAM lParam; // Ist der LPARAM Wert f黵 den Eintrag
LPTSTR pText; // Zeiger auf Tree-Text
UINT uState; // Zustand des Eintrages
int iImage; // Ist die Nummer des an zu zeigenden Icons
int iSelectedImage; // Ist die Nummer des an zu zeigenden Icons (wenn ausgew鋒lt)
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 letzen Kind-Eintrages (0=keines)
unsigned uPrevItem; // Ist die Nummer des vorherigen Eintrages (0=keines)
unsigned uNextItem; // Ist die Nummer des n鋍hsten 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鋘ge des Textes in Zeichen
BYTE bCallback; // Sind Bits f黵 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鋘ge des Textes in Zeichen
BYTE bCallback; // Sind Bits f黵 Callbacks
BYTE bFlags; // Diverse Flags
COLORREF uColorText; // Spezielle Textfarbe
COLORREF uColorBk; // Spezielle Hintergrundfarbe
}ExtraItem;
typedef struct
{
HWND hWnd; // Fenster Handle
HANDLE hSem; // Zugriffssemaphore
HIMAGELIST hStates; // Handle der Icon-Liste f黵 States und Overlay
HIMAGELIST hImages; // Handle der Icon-Liste
HFONT hFontN; // Ist der normale Zeichensatz
HFONT hFontB; // Ist der fette Zeichensatz
HFONT hFontL; // Der letzte zugewiesene Font
HFONT hFontT; // Ist der Font f黵 das Tooltip
HWND hEdit; // Handle des Edit-Fensters
HWND hHeader; // Handle des Header Fensters
HWND hToolTip; // Handle des Tooltip-Fensters
COLORREF uColors[MAX_COLORS]; // 0=Hintergrundfarbe 1=Abwechselnte Farbe 2=Farbe f黵 Trennlinien 3=Textfarbe
int iFontHeight; // Ist die H鰄e des Fonts
int iFontLine; // Ist die Position der Linie beim unterstreichen
int iFontOff; // Ist die Position um der ein Text horizontal verschoben wird
int iStateCheck; // Die hState Image-Liste wurde f黵 die Scheckboxen erzeugt
int iStatesXsize; // Breite der States und Overlay Icons
int iStatesYsize; // H鰄e der States und Overlay Icons
int iImagesXsize; // Breite der Icons
int iImagesYsize; // H鰄e der Icons
int iRowHeight; // Ist die H鰄e einer
int iIndent; // Einr點kung der Kindereint鋑e
int iShift; // Einr點kung der vertikalen Linien
int iMaxSizeX; // Die Gr鲞e des breitesten sichtbaren Eintrages
unsigned uItemPosCount; // Anzahl der sichtbaren Eintr鋑e
unsigned *pItemPos; // Liste mit den Offsets der sichtbaren Eintr鋑e
BaseItem **pTreeItems; // Zeiger auf Item Zeiger
ExtraItem **pExtraItems[MAX_COLUMNS-1]; // Zeiger auf die Spalteneintr鋑e
unsigned uTreeItemsMax; // Gr鲞e der Liste mit den vorhanden Eintr鋑e (alociert um 1 gr鲞er)
unsigned uTreeItemsCount; // Anzahl der vorhanden Eintr鋑e
unsigned uNextSeachPos; // N鋍hste Position zum suchen von freien Eintr鋑en
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 letzen Kind-Eintrages (0=keines)
unsigned uScrollY; // Aktuelle X-Scroll-Position
unsigned uScrollX; // 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-Koortinate bei der der erste Eintrag beginnt
unsigned uMaxEnties; // Anzahl der sichtbaren Eintr鋑e (inkl. halbsichtbare)
unsigned uPageEnties; // Anzahl der sichtbaren Eintr鋑e (ohne halbsichtbare)
unsigned uColumnCount; // Anzahl der Spalten
unsigned uColumnCountVar; // Anzahl der variabeln Spalten
unsigned uSelectedCount; // Anzahl der ausgew鋒lten Eintr鋑e
unsigned uSelectedBase; // Ist der Eintrag ab dem gew鋒lt wurde
unsigned uSelectedItem; // Ist der Eintrag der gerade gew鋒lt ist
unsigned uSelectedSub; // Ist die Spalte die gerade gew鋒lt 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鋒lt ist
unsigned uToolTipShow; // Ist die Zeitverz鰃erung in 500 ms Schritten f黵 das Tooltip
unsigned uToolTipSub; // Ist die ToolTip-Spalte die gerade gew鋒lt 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黵 X-Scroll-Bar
unsigned uOldXCount; // *
unsigned uOldYPage; // Alte Werte f黵 Y-Scroll-Bar
unsigned uOldYCount; // *
unsigned uTrippleB; // Bereite des "..." Strings f黵 den fetten Fonts
unsigned uTrippleN; // Bereite des "..." Strings f黵 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黦emarke
unsigned uDragFlags; // Welche Maustasten sind an
unsigned uDragItem; // Eintrag f黵 Dragoperation
unsigned uDragSub; // Untereintrag f黵 Dragoperation
TCHAR cTempText1[260]; // Erster Textpuffer f黵 Callbacks
TCHAR cTempText2[260]; // Zeiter Textpuffer f黵 Callbacks
TCHAR cToolTipText[260]; // Textspeicher f黵 Tooltip
BYTE bColumnEdit[MAX_COLUMNS]; // Automaisches Editiern einer Spalte (siehe TVAE_???)
BYTE bColumnCbSize[MAX_COLUMNS]; // Maximale Anzahl der Eintr鋑e in der Datenliste
BYTE bColumnCbChar[MAX_COLUMNS]; // Trennzeichen f黵 die Datenliste
INT iColumnCbIcon[MAX_COLUMNS]; // Startoffset f黵 in Iconliste f黵 Autoedit
void *pColumnCbData[MAX_COLUMNS]; // Daten f黵 das Autoeditieren
int iColumnXpos[MAX_COLUMNS+2]; // Array mit den Positionen der Spalten
char cColumnVar[MAX_COLUMNS]; // Markiert jene Spalten die eine variable Breite haben
char cColumnAlign[MAX_COLUMNS]; // Ist die Ausrichtung einer Spalte
char cColorChanged[MAX_COLORS]; // Welche Farbe wurden ver鋘dert
char cFixedHeight; // Ist eine fixe H鰄e eingestellt
char cLockChanges; // Sperren von Fenster鋘derungen
char cHasRootRow; // Wird gesetz wenn eine Root-Spalte eingef黦t wird
char cHasFocus; // Hat das Fenster den Focus
char cIsEnabled; // Ist das Fenster freigegeben
char cEditCb; // Muss das Edit-Fenster einen Callback aufrufen
char cClickFlag; // Merker f黵 LBUTTON-DOWN bei Multiselct
char cReSelect; // Soll die Auswahl neu Selektiert werden
}TreeListData;
static LONG lWindowCount = -1;
static HPEN hPatternPen = NULL;
static HFONT hDefaultFontN = NULL;
static HFONT hDefaultFontB = NULL;
static RECT sToolRect = { -2,0,2,64 };
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 TreeListStartAutoEdit(TreeListData *pData, unsigned uColumn, WPARAM wParam, LPARAM lParam);
static int TreeListEndLabelEdit(TreeListData *pData, int iCancel);
//*****************************************************************************
//*
//* TreeListRegister
//*
//*****************************************************************************
// Registiert das TreeList Fenster.
// Ergibt 1 wenn das Fenster erfolgreich registiert wurde.
int TreeListRegister()
{
static int iIsRegistered = FALSE;
WNDCLASSEX sClass;
if (iIsRegistered)return TRUE;
memset(&sClass, 0, sizeof(sClass));
sClass.cbSize = sizeof(sClass);
sClass.style = CS_DBLCLKS;
sClass.lpfnWndProc = TreeListProc;
sClass.cbClsExtra = 0;
sClass.cbWndExtra = sizeof(TreeListData *);
sClass.hInstance = NULL;
sClass.hIcon = NULL;
sClass.hCursor = LoadCursor(NULL, IDC_ARROW);
sClass.hbrBackground = NULL;
sClass.lpszMenuName = NULL;
sClass.lpszClassName = _T("TreeList");
sClass.hIconSm = NULL;
if (!RegisterClassEx(&sClass))return 0;
iIsRegistered = TRUE;
return TRUE;
}
//*****************************************************************************
//*
//* 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));
}
}
//*****************************************************************************
//*
//* 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;
}
}
//*****************************************************************************
//*
//* SendNotify
//*
//*****************************************************************************
// Sendet eine WM_NOTIFY Nachricht and das Elternfenster
// pData : Zeiger auf die Fensterdaten
// pNotify : Zeiger auf die Notify-Daten
// Ergibt den R點kgabewert 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點kgabewert der WM_NOTIFY Nachrich
static void CallbackEntry(TreeListData *pData, BaseItem *pEntry, unsigned uItem, unsigned uFlags, int &iImage, unsigned &uTextSize, LPCTSTR &pText)
{
NMTVDISPINFO sInfo;
sInfo.item.mask = TVIF_HANDLE|TVIF_PARAM|TVIF_STATE|uFlags;
sInfo.item.lParam = pEntry->lParam;
sInfo.item.hItem = (HTREEITEM)uItem;
sInfo.item.state = pEntry->uState;
sInfo.item.stateMask = 0xFFFFFFFF;
sInfo.item.iImage = I_IMAGECALLBACK;
sInfo.item.iSelectedImage = I_IMAGECALLBACK;
sInfo.item.cChildren = I_CHILDRENCALLBACK;
if (uFlags&TVIF_TEXT)
{
if (uTextSize)
{
pData->cTempText2[sizeof(pData->cTempText2)/sizeof(TCHAR)-1] = 0;
pData->cTempText2[0] = 0;
sInfo.item.pszText = pData->cTempText2;
sInfo.item.cchTextMax = sizeof(pData->cTempText2)/sizeof(TCHAR)-1;
}
else {
pData->cTempText1[sizeof(pData->cTempText1)/sizeof(TCHAR)-1] = 0;
pData->cTempText1[0] = 0;
sInfo.item.pszText = pData->cTempText1;
sInfo.item.cchTextMax = sizeof(pData->cTempText1)/sizeof(TCHAR)-1;
}
}
else {
sInfo.item.pszText = 0;
sInfo.item.cchTextMax = 0;
}
sInfo.hdr.hwndFrom = pData->hWnd;
sInfo.hdr.idFrom = GetWindowLong(pData->hWnd, GWL_ID);
sInfo.hdr.code = TVN_GETDISPINFO;
UNLOCK(pData);
SendMessage(GetParent(sInfo.hdr.hwndFrom), WM_NOTIFY, sInfo.hdr.idFrom, (LPARAM)&sInfo);
LOCK(pData);
if (uFlags&TVIF_IMAGE)
{
if (!(pEntry->uState&TVIS_SELECTED))iImage = sInfo.item.iImage;
}
else if (uFlags&TVIF_SELECTEDIMAGE)
{
if (pEntry->uState&TVIS_SELECTED)iImage = sInfo.item.iSelectedImage;
}
if (uFlags&TVIF_CHILDREN)
{
switch (sInfo.item.cChildren)
{
case 0: pEntry->bFlags &= ~TVIX_HASBUTTON;
pEntry->bFlags &= TVIX_VARBUTTON;
break;
case 1: pEntry->bFlags &= TVIX_VARBUTTON;
pEntry->bFlags |= TVIX_HASBUTTON;
break;
default: pEntry->bFlags |= TVIX_VARBUTTON;
if (pEntry->uFirstChild)
pEntry->bFlags |= TVIX_HASBUTTON;
else pEntry->bFlags &= ~TVIX_HASBUTTON;
}
}
if (uFlags&TVIF_TEXT)
{
pText = sInfo.item.pszText;
uTextSize = str_len(sInfo.item.pszText);
pEntry->iTextPixels = 0;
}
}
//*****************************************************************************
//*
//* CallbackExtra
//*
//*****************************************************************************
// Sendet eine WM_NOTIFY Nachricht and das Elternfenster um Daten zuholen
// pData : Zeiger auf die Fensterdaten
// pEntry : Zeiger auf den Eintrag
// uItem : Nummer des Eintrages
// uFlags : Welche Daten sollen abgefragt werden
// Ergibt den R點kgabewert der WM_NOTIFY Nachrich
static void CallbackExtra(TreeListData *pData, BaseItem *pEntry, ExtraItem *pExtra, unsigned uItem, unsigned uSub, unsigned uFlags, int &iImage, unsigned &uTextSize, LPCTSTR &pText)
{
NMTVDISPINFO sInfo;
sInfo.item.mask = TVIF_HANDLE|TVIF_PARAM|TVIF_STATE|TVIF_SUBITEM|uFlags;
sInfo.item.lParam = pEntry->lParam;
sInfo.item.hItem = (HTREEITEM)uItem;
sInfo.item.state = pExtra->uState;
sInfo.item.state |= (pEntry->uState&TVIS_BASEFLAGS);
sInfo.item.stateMask = 0xFFFFFFFF;
sInfo.item.iImage = I_IMAGECALLBACK;
sInfo.item.iSelectedImage = I_IMAGECALLBACK;
sInfo.item.cChildren = uSub;
if (uFlags&TVIF_TEXT)
{
pData->cTempText1[sizeof(pData->cTempText1)/sizeof(TCHAR)-1] = 0;
pData->cTempText1[0] = 0;
sInfo.item.pszText = pData->cTempText1;
sInfo.item.cchTextMax = sizeof(pData->cTempText1)/sizeof(TCHAR)-1;
}
else {
sInfo.item.pszText = 0;
sInfo.item.cchTextMax = 0;
}
sInfo.hdr.hwndFrom = pData->hWnd;
sInfo.hdr.idFrom = GetWindowLong(pData->hWnd, GWL_ID);
sInfo.hdr.code = TVN_GETDISPINFO;
UNLOCK(pData);
SendMessage(GetParent(sInfo.hdr.hwndFrom), WM_NOTIFY, sInfo.hdr.idFrom, (LPARAM)&sInfo);
LOCK(pData);
if (uFlags&TVIF_IMAGE)iImage = sInfo.item.iImage;
if (uFlags&TVIF_TEXT)
{
pText = sInfo.item.pszText;
uTextSize = str_len(sInfo.item.pszText);
}
}
//*****************************************************************************
//*
//* EditProc
//*
//*****************************************************************************
// Ist die Fensterfunktion f黵 das Edit Fenster
static LRESULT CALLBACK EditProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
WNDPROC pProc;
int iDelta;
LRESULT iState;
LRESULT iPos;
pProc = (WNDPROC)GetWindowLongPtr(hWnd, GWL_USERDATA);
if (uMsg==WM_KEYDOWN)
{
if (wParam==VK_RETURN)
{
SendMessage(GetParent(hWnd), WM_COMMAND, MAKELONG(3, EN_RETURN), (LPARAM)hWnd);
return 0;
}
if (wParam==VK_ESCAPE)
{
SendMessage(GetParent(hWnd), WM_COMMAND, MAKELONG(3, EN_ESCAPE), (LPARAM)hWnd);
return 0;
}
}
else if (uMsg==WM_COMMAND)
{
if (wParam==MAKELONG(3, EN_ESCAPE) || wParam==MAKELONG(3, EN_RETURN))
{
SendMessage(GetParent(hWnd), WM_COMMAND, wParam, (LPARAM)hWnd);
return 0;
}
}
else if (uMsg==WM_MOUSEWHEEL)
{
iState = CallWindowProc(pProc, hWnd, CB_GETDROPPEDSTATE, 0, 0);
if (iState)
{
iDelta = (short)HIWORD(wParam);
iDelta /= 120;
iPos = CallWindowProc(pProc, hWnd, CB_GETTOPINDEX, 0, 0);
iPos -= iDelta;
CallWindowProc(pProc, hWnd, CB_SETTOPINDEX, iPos, 0);
return 0;
}
}
else {
if (uMsg==WM_GETDLGCODE) // Welsche Tasten werden im Dialog benutzt
{
return DLGC_WANTALLKEYS;
}
}
return CallWindowProc(pProc, hWnd, uMsg, wParam, lParam);
}
//*****************************************************************************
//*
//* CrateToolTip
//*
//*****************************************************************************
// Erzeugt ein ToolTip Fenster
// pData : Zeiger auf die Fensterdaten
static void CrateToolTip(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);
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);
}
//*****************************************************************************
//*
//* CreateStateImageList
//*
//*****************************************************************************
// Erzeugt eine Image-Liste mit zwei Checkboxen
// pData : Zeiger auf die Fensterdaten
static void CreateStateImageList(TreeListData *pData)
{
BITMAPINFO sInfo;
BYTE aMem[0x1000];
HDC hDcSrc;
HDC hDc;
HBITMAP hBmp;
HBITMAP hBmpNew;
RECT sRect;
int iBits;
if (pData->hStates)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);
pData->hStates = ImageList_Create(16, 16, ILC_COLORDDB|ILC_MASK, 3, 14);
pData->iStatesXsize = 16;
pData->iStatesYsize = 16;
pData->iStateCheck = 1;
ImageList_AddMasked(pData->hStates, 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黵 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黵 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黵 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黵 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;
}
//*****************************************************************************
//*
//* UpdateColors
//*
//*****************************************************************************
// Aktualisiert alle Farben
// pData : Zeiger auf die Fensterdaten
static void UpdateColors(TreeListData *pData)
{
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_ODD])
{
iDiff = ((pData->uColors[TVC_ODD])&0xFF)-((pData->uColors[TVC_EVEN])&0xFF); iSum = iDiff*iDiff;
iDiff = ((pData->uColors[TVC_ODD]>> 8)&0xFF)-((pData->uColors[TVC_EVEN]>> 8)&0xFF); iSum += iDiff*iDiff;
iDiff = ((pData->uColors[TVC_ODD]>>16)&0xFF)-((pData->uColors[TVC_EVEN]>>16)&0xFF); iSum += iDiff*iDiff;
if (iSum<64) // Ist die alternierente Farbe fast gleich ?
{
pData->uColors[TVC_ODD] = pData->uColors[TVC_EVEN]&0x0000FFFF;
pData->uColors[TVC_ODD] |= ((pData->uColors[TVC_EVEN]&0x00FF0000)-0x00080000)&0x00FF0000;
}
}
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
//*
//*****************************************************************************
// Pr黤t ob die Zeilenh鰄e sich ge鋘tert hat
// pData : Zeiger auf die Fensterdaten
// Ergibt 1 bei 膎derungen sonst 0
static int UpdateHeight(TreeListData *pData)
{
int iHeight;
RECT sRect;
if (pData->cFixedHeight)return 0;
iHeight = 10;
if (pData->hStates)
if (iHeight<pData->iStatesYsize+2)iHeight = pData->iStatesYsize+2;
if (iHeight<pData->iImagesXsize+2)iHeight = pData->iImagesXsize+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;
pData->uMaxEnties = pData->uSizeY;
pData->uMaxEnties -= pData->uStartPixel;
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;
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 Fenstersichtbar
sRect.left = pData->iColumnXpos[uSub];
sRect.left -= pData->uScrollX;
sRect.right = pData->iColumnXpos[uSub+1];
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;
}
//*****************************************************************************
//*
//* UpdateRow
//*
//*****************************************************************************
// Zeichnet einen Eintrag 黚er 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 Fenstersichtbar
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
//*
//*****************************************************************************
// Zeichnet das ganze Fenster neu
// 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);
}
//*****************************************************************************
//*
//* UpdateScrollX
//*
//*****************************************************************************
// Aktualisiert die X-Scroolbar
// pData : Zeiger auf die Fensterdaten
// Ergibt 1 wenn der sich Einstellungen ver鋘dert haben
static void UpdateScrollX(TreeListData *pData)
{
SCROLLINFO sInfo;
unsigned uSize;
unsigned uCols;
uCols = pData->uColumnCount;
if (uCols)
uSize = pData->iColumnXpos[uCols]-1;
else uSize = pData->iMaxSizeX;
if (pData->uOldXCount==uSize)
if (pData->uOldXPage ==pData->uSizeX)
{
return;
}
pData->uOldYPage = pData->uSizeX;
pData->uOldYCount = 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;
}
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-1, 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鋘dert 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
//*
//*****************************************************************************
// Ist die Fensterfunktion f黵 das TreeList Fenster
// pData : Zeiger auf Fensterdaten
// uItem : Item auf den der Mauszeiger zeigt
// uFlags : Flags vom HitTest
static void UpdateToolTip(TreeListData *pData, unsigned uItem, unsigned uFlags)
{
HWND hToolTip;
TOOLINFO sInfo;
ExtraItem *pExtra;
BaseItem *pEntry;
LPCTSTR pText;
RECT sRect;
unsigned uSize;
unsigned uCol;
LRESULT lRet;
int iTemp;
if (!uItem)
{
if (pData->uToolTipItem)goto ExitTip;
return;
}
pEntry = pData->pTreeItems[uItem];
if (uFlags&TVHT_ONITEM)
{
if (pData->uToolTipItem!=uItem || pData->uToolTipSub!=0)
{
if (!pData->pTreeItems[pData->uToolTipItem]) // Existiert der Eintag noch ?
{
goto ExitTip;
}
TreeListGetItemRect(pData, uItem, TVIR_GETCOLUMN|TVIR_TEXT, &sRect);
if (sRect.right>(int)pData->uSizeX)
{
sRect.right = pData->uSizeX;
}
if (pData->uStyleEx&TVS_EX_TOOLTIPNOTIFY) // Tooltip-Daten via Notify holen
{
NMTREEVIEW sNotify;
sNotify.hdr.code = TVN_ITEMTOOLTIP;
sNotify.action = 0;
sNotify.itemNew.mask = TVIF_HANDLE|TVIF_PARAM|TVIF_STATE|TVIF_SUBITEM;
sNotify.itemNew.hItem = (HTREEITEM)uItem;
sNotify.itemNew.stateMask = 0xFFFFFFFF;
sNotify.itemNew.state = pEntry->uState;
sNotify.itemNew.lParam = pEntry->lParam;
sNotify.itemNew.pszText = pEntry->pText;
sNotify.itemNew.cchTextMax = pEntry->uTextSize;
sNotify.itemNew.cChildren = 0;
sNotify.itemOld.mask = 0;
sNotify.ptDrag.x = sRect.left;
sNotify.ptDrag.y = sRect.top;
UNLOCK(pData);
lRet = SendNotify(pData, &sNotify.hdr);
LOCK(pData);
if (lRet) // Soll ein User-Tooltip angezeigt werden
{
pText = sNotify.itemNew.pszText;
uSize = sNotify.itemNew.cchTextMax;
sRect.left = sNotify.ptDrag.x;
sRect.top = sNotify.ptDrag.y;
if (*pText==0)goto ExitTip;
str_ncpy(pData->cToolTipText, pText, sizeof(pData->cToolTipText)/sizeof(TCHAR)-1);
pData->hFontT = (pEntry->uState&TVIS_BOLD) ? pData->hFontB : pData->hFontN;
ClientToScreen(pData->hWnd, &sNotify.ptDrag);
pData->sToolTipPos = sNotify.ptDrag;
hToolTip = pData->hToolTip;
if (sNotify.itemNew.mask&TVIF_TOOLTIPTIME && sNotify.itemNew.lParam>0)
{
pData->uToolTipShow = (unsigned)(sNotify.itemNew.lParam+499)/500;
pData->uToolTipItem = uItem;
pData->uToolTipSub = 0;
SetTimer(pData->hWnd, ID_TOOLTIPCHECK, 500, NULL);
}
else {
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(sNotify.ptDrag.x, sNotify.ptDrag.y));
SendMessage(pData->hToolTip, TTM_TRACKACTIVATE, 1, (LPARAM)&sInfo);
LOCK(pData);
pData->uToolTipShow = 0;
pData->uToolTipItem = uItem;
pData->uToolTipSub = 0;
SetTimer(pData->hWnd, ID_TOOLTIPCHECK, 1500, NULL);
}
return;
}
}
// 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==0)goto ExitTip;
str_ncpy(pData->cToolTipText, pText, sizeof(pData->cToolTipText)/sizeof(TCHAR)-1);
pData->hFontT = (pEntry->uState&TVIS_BOLD) ? pData->hFontB : pData->hFontN;
hToolTip = pData->hToolTip;
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)
{
uCol = TVHT_SUBTOCOL(uFlags);
pExtra = pData->pExtraItems[uCol-1][uItem];
if (pData->uStyleEx&TVS_EX_TOOLTIPNOTIFY) // Tooltip-Daten via Notify holen
{
NMTREEVIEW sNotify;
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 = const_cast<LPTSTR>(_T(""));
}
sNotify.hdr.code = TVN_ITEMTOOLTIP;
sNotify.action = 0;
sNotify.itemNew.lParam = pEntry->lParam;
sNotify.itemNew.mask = TVIF_HANDLE|TVIF_PARAM|TVIF_STATE|TVIF_SUBITEM;
sNotify.itemNew.hItem = (HTREEITEM)uItem;
sNotify.itemNew.stateMask = 0xFFFFFFFF;
sNotify.itemNew.cChildren = uCol;
sNotify.itemOld.mask = 0;
sNotify.ptDrag.x = sRect.left;
sNotify.ptDrag.y = sRect.top;
UNLOCK(pData);
lRet = SendNotify(pData, &sNotify.hdr);
LOCK(pData);
if (lRet) // Soll ein User-Tooltip angezeigt werden
{
pText = sNotify.itemNew.pszText;
uSize = sNotify.itemNew.cchTextMax;
sRect.left = sNotify.ptDrag.x;
sRect.top = sNotify.ptDrag.y;
if (*pText==0)goto ExitTip;
str_ncpy(pData->cToolTipText, pText, sizeof(pData->cToolTipText)/sizeof(TCHAR)-1);
pData->hFontT = (pExtra && pExtra->uState&TVIS_BOLD) ? pData->hFontB : pData->hFontN;
ClientToScreen(pData->hWnd, &sNotify.ptDrag);
pData->sToolTipPos = sNotify.ptDrag;
hToolTip = pData->hToolTip;
if (sNotify.itemNew.mask&TVIF_TOOLTIPTIME && sNotify.itemNew.lParam>0)
{
pData->uToolTipShow = (unsigned)(sNotify.itemNew.lParam+499)/500;
pData->uToolTipItem = uItem;
pData->uToolTipSub = uCol;
SetTimer(pData->hWnd, ID_TOOLTIPCHECK, 500, NULL);
}
else {
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(sNotify.ptDrag.x, sNotify.ptDrag.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);
}
return;
}
}
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==0)goto ExitTip;
str_ncpy(pData->cToolTipText, pText, sizeof(pData->cToolTipText)/sizeof(TCHAR)-1);
pData->hFontT = (pExtra->uState&TVIS_BOLD) ? pData->hFontB : pData->hFontN;
pData->sToolTipPos.x = sRect.left;
pData->sToolTipPos.y = sRect.top;
hToolTip = pData->hToolTip;
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);
}
}
//*****************************************************************************
//*
//* UpdateFont
//*
//*****************************************************************************
// Erzeugt einen den fetten Font f黵 das Fenster
// pData : Zeiger auf die Fensterdaten
// iRedraw : Soll das Fenster neugezeichnet werden
// Ergibt 1 wenn der Font ver鋘dert 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->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點ksetzen
{
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;
}
hDc = GetDC(NULL); // Breite der "..." Texte
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黵 die sichtbaren Eintr鋑e
// 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鋑e zur點ksetzen
{
uTemp = pLines[uNum];
if (!uTemp)continue;
pLines[uNum] = 0;
pTemp = pItems[uTemp];
if (!pTemp)continue;
pTemp->uShowPos = 0;
}
memset(pLines, 0, sizeof(unsigned)*uOld);
return;
}
for (uNum = 0; uNum<uOld; uNum++) // Die alten Eintr鋑e zur點ksetzen
{
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鋑e zur點ksetzen
{
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->iRowHeight*uStart;
if (sRect.top<=sRect.bottom)
{
InvalidateRect(pData->hWnd, &sRect, FALSE);
}
if (uOld!=uPos)UpdateScrollY(pData);
}
//*****************************************************************************
//*
//* UpdateCoumns
//*
//*****************************************************************************
// Pr黤t ob es Ver鋘derungen in Spaltenbreiten gab
// pData : Zeiger auf die Fensterdaten
// Ergibt die Breite ab der die Spaltenver鋘dert wurden oder 0x10000
static int UpdateCoumns(TreeListData *pData)
{
HWND hHeader;
RECT sRect;
unsigned uPos;
int iNum;
int iRet = 0x10000;
hHeader = pData->hHeader;
pData->iColumnXpos[0] = 0;
for (uPos = 0; uPos<pData->uColumnCount;)
{
Header_GetItemRect(hHeader, uPos, &sRect);
uPos++;
sRect.right--;
iNum = pData->iColumnXpos[uPos];
if (iNum==sRect.right)continue;
if (iNum==0)iNum = sRect.left;
if (iNum>=sRect.right)
{
iRet = sRect.left;
}
else {
iRet = sRect.left;
if (pData->uSelectedItem) // Problem bei ausgew鋒lten leeren Eintr鋑en
if (pData->uSelectedSub+1==uPos)
{
UpdateRect(pData, pData->uSelectedItem, pData->uSelectedSub);
}
if (pData->uTrackedItem)
if (pData->uTrackedSub+1==uPos)
{
UpdateRect(pData, pData->uTrackedItem, pData->uTrackedSub);
}
}
pData->iColumnXpos[uPos] = sRect.right;
break;
}
for (; uPos<pData->uColumnCount;)
{
Header_GetItemRect(hHeader, uPos, &sRect);
uPos++;
sRect.right--;
pData->iColumnXpos[uPos] = sRect.right;
}
pData->iColumnXpos[pData->uColumnCount+1] = pData->uSizeX;
return iRet;
}
//*****************************************************************************
//*
//* 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黦t werden sollen
// Ergibt -1 = Fehler
// 0 = Ausgef黨rt
// 1 = Abbruch
static int TreeListToggleItem(TreeListData *pData, unsigned uItem, unsigned uAddFlags)
{
NMTREEVIEW sNotify;
BaseItem **pList;
BaseItem *pEntry;
BaseItem *pTemp;
unsigned uLevel;
unsigned uMask;
unsigned uBits;
unsigned uNext;
LRESULT lRet;
if (uItem>pData->uTreeItemsMax)return 0;
pList = pData->pTreeItems;
pEntry = pList[uItem];
if (!pEntry)return -1;
UNLOCK(pData);
uBits = pEntry->uState&TVIS_EXPANDEDONCE;
uMask = (pEntry->uState&TVIS_EXPANDED) ? ~(TVIS_EXPANDEDONCE|TVIS_EXPANDED) : ~0;
sNotify.hdr.code = TVN_ITEMEXPANDING;
sNotify.action = (pEntry->uState&TVIS_EXPANDED) ? TVE_COLLAPSE : TVE_EXPAND;
sNotify.itemNew.mask = TVIF_HANDLE|TVIF_PARAM|TVIF_STATE;
sNotify.itemNew.hItem = (HTREEITEM)uItem;
sNotify.itemNew.stateMask = 0xFFFFFFFF;
sNotify.itemNew.state = (pEntry->uState^TVIS_EXPANDED)&uMask;
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;
lRet = SendNotify(pData, &sNotify.hdr);
LOCK(pData);
pEntry = pList[uItem];
if (!pEntry)return -1;
if (lRet)return 1; // User-Abbruch ?
pList = pData->pTreeItems;
pEntry->uState ^= TVIS_EXPANDED; // Teile auf/zuklappen
pEntry->uState &= ~TVIS_EXPANDPARTIAL;
pEntry->uState |= uAddFlags;
if (pEntry->uShowPos)
{
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);
}
UNLOCK(pData);
sNotify.hdr.code = TVN_ITEMEXPANDED;
sNotify.action = (pEntry->uState&TVIS_EXPANDED) ? TVE_COLLAPSE : TVE_EXPAND;
sNotify.itemNew.mask = TVIF_HANDLE|TVIF_PARAM|TVIF_STATE;
sNotify.itemNew.hItem = (HTREEITEM)uItem;
sNotify.itemNew.stateMask = 0xFFFFFFFF;
sNotify.itemNew.state = (pEntry->uState|uBits)&uMask;
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;
SendNotify(pData, &sNotify.hdr);
LOCK(pData);
pEntry = pData->pTreeItems[uItem];
if (!pEntry)return -1;
if (pData->uSelectedItem) // Ist der ausgew鋒lten 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);
}
}
return 0;
}
//*****************************************************************************
//*
//* TreeListGetItemRect
//*
//*****************************************************************************
// Zeichnet einen Eintrag neu
// 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 8.. : Spaltennummer
// Ergibt 1 wenn der Eintrag sichtbar war
static int TreeListGetItemRect(TreeListData *pData, unsigned uItem, unsigned uFlags, RECT *pRect)
{
ExtraItem *pExtra;
BaseItem *pEntry;
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 Fenstersichtbar
{
memset(pRect, 0, sizeof(RECT));
return 0;
}
pRect->top = pData->uStartPixel;
pRect->top += pData->iRowHeight*uPos;
pRect->bottom = pData->iRowHeight+pRect->top;
if ((uFlags&0xFE)==TVIR_GETCOLUMN) // Nur Spalten
{
uSub = uFlags>>24;
if (uSub>=pData->uColumnCount)uSub = 0;
pRect->left = pData->iColumnXpos[uSub];
pRect->left -= pData->uScrollX;
pRect->right = pData->iColumnXpos[uSub+1];
pRect->right -= pData->uScrollX;
}
else {
uSub = 0;
pRect->left = 0;
pRect->left -= pData->uScrollX;
pRect->right = pData->uSizeX;
}
if (uFlags&TVIR_TEXT) // Nur Text augeben
{
if (uSub>0)
{
pExtra = pData->pExtraItems[uSub-1][uItem];
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 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
{
iNum = pData->uSizeX;
iOff = pData->uScrollX;
iAnf = pData->iColumnXpos[uSub];
iEnd = pData->iColumnXpos[uSub+1];
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->iColumnXpos[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-1, pData->uStartPixel, TRUE);
}
}
return iRet;
}
//*****************************************************************************
//*
//* TreeListDeleteItem
//*
//*****************************************************************************
// L鰏cht einen Eintrag aus dem Fenster
// pData : Zeiger auf die Fensterdaten
// uItem : Ist die Nummer des Eintrages der gel鰏cht werden soll
// iUpdateList : Soll die Anzeigeliste aktualisiert werden
// Ergibt 1 wenn der Eintrag gel鰏cht wurde
static int TreeListDeleteItem(TreeListData *pData, unsigned uItem, int iUpdateList)
{
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黤e den Eintrag
{
if (uItem != U(TVI_ROOT))return 0; // Alles l鰏chen
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黤e den Eintrag
{
if (uItem!=NULL)return 0; // Alles l鰏chen
if (pData->uLastChild==0)return 0;
while (pData->uLastChild)
{
TreeListDeleteItem(pData, pData->uLastChild, 0);
}
pData->uItemPosCount = 0;
UpdateScrollY(pData);
UpdateView(pData);
return 1;
}
while (pEntry->uLastChild) // Alle Kinder l鰏chen
{
TreeListDeleteItem(pData, pEntry->uLastChild, 0);
}
if (uItem==pData->uSelectedItem) // Einen ausgew鋒lten Eintrag l鰏chen
{
sNotify.hdr.code = TVN_SELCHANGED;
sNotify.action = TVC_UNKNOWN;
sNotify.itemOld.mask = TVIF_HANDLE|TVIF_PARAM|TVIF_STATE|TVIF_SUBITEM;
sNotify.itemOld.hItem = (HTREEITEM)uItem;
sNotify.itemOld.stateMask = 0xFFFFFFFF;
sNotify.itemOld.state = pEntry->uState&~TVIS_SELECTED;
sNotify.itemOld.cChildren = 0;
sNotify.itemOld.pszText = (LPTSTR)-1;
sNotify.itemOld.cchTextMax = -1;
sNotify.itemNew.mask = TVIF_HANDLE|TVIF_PARAM|TVIF_STATE|TVIF_SUBITEM;
sNotify.itemNew.hItem = NULL;
sNotify.itemNew.stateMask = 0xFFFFFFFF;
sNotify.itemNew.state = 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); // Bekanndgeben das der Eintrag nicht mehr ausgew鋒lt 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;
sNotify.itemOld.hItem = (HTREEITEM)uItem;
sNotify.itemOld.lParam = pEntry->lParam;
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]; // Pr黤en ob der Eintrag noch existiert
if (!pEntry)return 0;
if (uItem==pData->uTrackedItem) // Einen unterstrichenen Eintrag l鰏chen
{
pData->uTrackedItem = 0;
pData->uTrackedSub = 0;
}
if (pData->uInsertMark==uItem)
{
pData->uInsertMark = 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鋑e l鰏chen
{
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鰏chen
if (pEntry->pText)
{
pEntry->uTextSize = 0;
delete pEntry->pText;
}
if (iUpdateList) // Den Eintrag neuzeichen
{
uItem = pEntry->uPrevItem;
if (!uItem)uItem = pEntry->uParent;
if (pEntry->uShowPos)
{
UpdateItems(pData, uItem);
}
}
if (pEntry->uState&TVIS_SELECTED) // Ausgew鋒lte Eintr鋑e runterz鋒len
if (pData->uSelectedCount>0)
{
pData->uSelectedCount--;
}
delete pEntry;
pData->uTreeItemsCount--;
iOff = pData->uScrollY; // Pr黤e 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鋒lt einen Eintrag ab bzw. an
// pData : Zeiger auf die Fensterdaten
// uItem : Ist die Nummer des Eintrages der ausgew鋒lt werden soll
// iMode : Ist der Grund f黵 die 膎derung
// TVC_BYKEYBOARD
// TVC_BYMOUSE
// TVC_UNKNOWN
// Ergibt 1 wenn der Eintrag ab/angew鋒lt wurde
// 0 wenn der Eintrag nicht ver鋘dert wurde
static int TreeListXorSelectItem(TreeListData *pData, unsigned uItem, int iMode)
{
NMTREEVIEW sNotify;
BaseItem *pEntry;
LRESULT uRet;
pEntry = pData->pTreeItems[uItem];
if (!pEntry)return 0;
if (uItem==pData->uSelectedItem)return 0;
sNotify.hdr.code = TVN_SELCHANGING;
sNotify.action = iMode;
sNotify.itemNew.mask = TVIF_HANDLE|TVIF_PARAM|TVIF_STATE|TVIF_SUBITEM;
sNotify.itemNew.hItem = (HTREEITEM)uItem;
sNotify.itemNew.stateMask = 0xFFFFFFFF;
sNotify.itemNew.state = pEntry->uState^TVIS_SELECTED;
sNotify.itemNew.cChildren = 0;
sNotify.itemNew.pszText = (LPTSTR)-1;
sNotify.itemNew.cchTextMax = -1;
sNotify.itemOld.mask = TVIF_HANDLE|TVIF_PARAM|TVIF_STATE|TVIF_SUBITEM;
sNotify.itemOld.hItem = NULL;
sNotify.itemOld.stateMask = 0xFFFFFFFF;
sNotify.itemOld.state = 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 = SendNotify(pData, &sNotify.hdr);
LOCK(pData);
if (uRet)return 0;
pEntry = pData->pTreeItems[uItem];
if (!pEntry)return 0;
sNotify.hdr.code = TVN_SELCHANGED;
sNotify.action = iMode;
sNotify.itemNew.mask = TVIF_HANDLE|TVIF_PARAM|TVIF_STATE|TVIF_SUBITEM;
sNotify.itemNew.hItem = (HTREEITEM)uItem;
sNotify.itemNew.stateMask = 0xFFFFFFFF;
sNotify.itemNew.state = pEntry->uState^TVIS_SELECTED;
sNotify.itemNew.cChildren = 0;
sNotify.itemNew.pszText = (LPTSTR)-1;
sNotify.itemNew.cchTextMax = -1;
sNotify.itemOld.mask = TVIF_HANDLE|TVIF_PARAM|TVIF_STATE|TVIF_SUBITEM;
sNotify.itemOld.hItem = NULL;
sNotify.itemOld.stateMask = 0xFFFFFFFF;
sNotify.itemOld.state = 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;
pEntry->uState ^= TVIS_SELECTED;
if (pEntry->uShowPos)
{
if (pData->uStyleEx&TVS_EX_FULLROWMARK)
UpdateRow(pData, uItem);
else UpdateRect(pData, uItem, 0);
}
if (pEntry->uState&TVIS_SELECTED)
pData->uSelectedCount++;
else pData->uSelectedCount--;
return 1;
}
//*****************************************************************************
//*
//* TreeListRemoveFocus
//*
//*****************************************************************************
// W鋒lt 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)return;
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;
}
//*****************************************************************************
//*
//* TreeListSelectItem
//*
//*****************************************************************************
// W鋒lt einen Eintrag aus
// pData : Zeiger auf die Fensterdaten
// uItem : Ist die Nummer des Eintrages der ausgew鋒lt werden soll
// uSubItem : Ist die Spalte die gew鋒lt werden soll
// iMode : Ist der Grund f黵 die 膎derung
// TVC_BYKEYBOARD
// TVC_BYMOUSE
// TVC_UNKNOWN
// Ergibt 2 wenn der Eintrag gew鋒lt und umgeklapt wurde
// 1 wenn der Eintrag gew鋒lt wurde
// 0 wenn der Eintrag nicht gew鋒lt wurde
static int TreeListSelectItem(TreeListData *pData, unsigned uItem, unsigned uSubItem, int iMode)
{
NMTREEVIEW sNotify;
ExtraItem *pExtra;
BaseItem *pEntry;
BaseItem *pTemp;
unsigned uState;
unsigned uStOld;
unsigned uNext;
unsigned uPos;
unsigned uOld;
unsigned uSub;
LRESULT uRet;
int iDel;
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鰃lich
{
iDel = iMode&TVC_DESELECT;
if (!iDel)
{
if (pData->uStyleEx&TVS_EX_FULLROWMARK)
UpdateRow(pData, uOld);
else UpdateRect(pData, uOld, uSub);
uOld = 0;
uSub = 0;
}
else { // Alle gew鋒lten Eintr鋑e abw鋒len
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鋑e abgew鋒lt
}
}
}
}
else {
iDel = 1;
}
iMode &= ~TVC_DESELECT;
pEntry = pData->pTreeItems[uItem];
if (!pEntry) // Neuen Statatus holen
{
if (uItem)return 0;
uState = 0;
}
else {
uState = pEntry->uState|TVIS_SELECTED;
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;
}
else {
uStOld = pTemp->uState|TVIS_SELECTED;
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;
sNotify.itemNew.hItem = (HTREEITEM)uItem;
sNotify.itemNew.stateMask = 0xFFFFFFFF;
sNotify.itemNew.state = uState;
sNotify.itemNew.cChildren = uSubItem;
sNotify.itemNew.pszText = (LPTSTR)-1;
sNotify.itemNew.cchTextMax = -1;
sNotify.itemOld.mask = TVIF_HANDLE|TVIF_PARAM|TVIF_STATE|TVIF_SUBITEM;
sNotify.itemOld.hItem = (HTREEITEM)uOld;
sNotify.itemOld.stateMask = 0xFFFFFFFF;
sNotify.itemOld.state = uStOld;
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鋒lt werden darf
{
LOCK(pData);
return 0;
}
LOCK(pData);
if (uItem) // Pr黤en 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鋒len
{
if (pTemp->uShowPos) // Den Eintrag neu zeichnen
{
if (pData->uStyleEx&TVS_EX_FULLROWMARK)
UpdateRow(pData, uOld);
else UpdateRect(pData, uOld, uSub);
}
if (pTemp->uState&TVIS_SELECTED)
{
pTemp->uState &= ~TVIS_SELECTED;
pData->uSelectedCount -= 1;
}
pData->uSelectedSub = 0;
pData->uSelectedItem = 0;
}
else {
uOld = 0;
}
if (uItem) // Den neuen Eintrag w鋒len
{
if (uSubItem && uSubItem<pData->uColumnCount)
{
pExtra = pData->pExtraItems[uSubItem-1][uItem];
if (!pExtra)
{
pExtra = new ExtraItem;
memset(pExtra, 0, sizeof(ExtraItem));
pExtra->iImage = TV_NOIMAGE;
pExtra->uState = pEntry->uState&(TVIS_BOLD|TVIS_UNTERLINE);
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)
UpdateRow(pData, uItem);
else UpdateRect(pData, uItem, uSubItem);
}
if (!(pEntry->uState&TVIS_SELECTED))
{
pEntry->uState |= TVIS_SELECTED;
pData->uSelectedCount += 1;
}
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;
sNotify.itemNew.hItem = (HTREEITEM)uItem;
sNotify.itemNew.stateMask = 0xFFFFFFFF;
sNotify.itemNew.state = uState;
sNotify.itemNew.cChildren = uSubItem;
sNotify.itemNew.pszText = (LPTSTR)-1;
sNotify.itemNew.cchTextMax = -1;
sNotify.itemOld.mask = TVIF_HANDLE|TVIF_PARAM|TVIF_STATE|TVIF_SUBITEM;
sNotify.itemOld.hItem = (HTREEITEM)uOld;
sNotify.itemOld.stateMask = 0xFFFFFFFF;
sNotify.itemOld.state = uStOld;
sNotify.itemOld.cChildren = uSub;
sNotify.itemOld.pszText = (LPTSTR)-1;
sNotify.itemOld.cchTextMax = -1;
sNotify.ptDrag.x = 0;
sNotify.ptDrag.y = 0;
UNLOCK(pData);
SendNotify(pData, &sNotify.hdr);
LOCK(pData);
if (!(pData->uStyle&TVS_SINGLEEXPAND)) // Einzelmodus aktiv
{
if (pData->uStyle&TVS_SHOWSELALWAYS)
if (pData->uSelectedItem)
{
TreeListEnsureVisible(pData, pData->uSelectedItem, pData->uSelectedSub);
}
return 1;
}
//*****************************************************************************
sNotify.hdr.code = TVN_SINGLEEXPAND;
sNotify.action = iMode;
sNotify.itemNew.mask = TVIF_HANDLE|TVIF_PARAM|TVIF_STATE;
sNotify.itemNew.hItem = (HTREEITEM)uItem;
sNotify.itemNew.stateMask = 0xFFFFFFFF;
sNotify.itemNew.state = (pEntry) ? pEntry->uState : 0;
sNotify.itemNew.cChildren = 0;
sNotify.itemNew.pszText = (LPTSTR)-1;
sNotify.itemNew.cchTextMax = -1;
sNotify.itemOld.mask = TVIF_HANDLE|TVIF_PARAM|TVIF_STATE;
sNotify.itemOld.hItem = (HTREEITEM)uOld;
sNotify.itemOld.stateMask = 0xFFFFFFFF;
sNotify.itemOld.state = (pTemp) ? pTemp->uState : 0;
sNotify.itemOld.cChildren = 0;
sNotify.itemOld.pszText = (LPTSTR)-1;
sNotify.itemOld.cchTextMax = -1;
sNotify.ptDrag.x = 0;
sNotify.ptDrag.y = 0;
UNLOCK(pData);
uRet = SendNotify(pData, &sNotify.hdr); // Anfragen ob die Zweige umgeklappt werden d黵fen
LOCK(pData);
pTemp = pData->pTreeItems[uOld]; // Zeiger neu hollen falls es 膎derungen 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;
}
//*****************************************************************************
//*
//* TreeListInsertItem
//*
//*****************************************************************************
// F黦t einen Eintrag ins Fenster ein
// pData : Zeiger auf die Fensterdaten
// pInsert : Zeiger auf die ein zu f黦enden Daten
// Ergibt die Einf黦eposition des neuen Eintrages oder 0 bei einem Fehler
static unsigned TreeListInsertItem(TreeListData *pData, TV_INSERTSTRUCT *pInsert)
{
char *pTemp;
BYTE bFlag;
LPCTSTR pText;
LPCTSTR pTextTemp;
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黤e 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鰄en
{
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
uBits = pInsert->item.mask;
if (uBits&TVIF_STATE)
{
pNew->uState = pInsert->item.state&pInsert->item.stateMask;
}
else {
if (pData->uStyle&TVS_CHECKBOXES)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鋍hen
{
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;
}
pNew->uState |= TVIS_EXPANDEDONCE;
if (uBits&TVIF_TEXT) // Text einf黦en
{
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黦en
{
pNew->uParent = 0;
uParent = 0;
bFlag = 0;
uFirst = 0xFFFFFFFF;
pFirst = &pData->uFirstChild;
pLast = &pData->uLastChild;
}
else { // Einen Tree-Eintrag einf黦en
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黦en **************************************
uAfter = U(pInsert->hInsertAfter);
switch (uAfter)
{
case U(TVI_BEFORE): // Nach einem Eintrag einf黦en
if (pParent) // Einen Root-Eintrag einf黦en
{
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黦en
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黦en
if (pFirst[0]) // Gibt es schon Eintr鋑e
{
pEntry = pItems[pFirst[0]];
pEntry->uPrevItem = uPos;
}
else {
pFirst[0] = uPos;
pLast[0] = uPos;
break;
}
pNew->uNextItem = pFirst[0]; // Eintrag einf黦en
pFirst[0] = uPos;
break;
case U(TVI_ROOT): // Als Root-Eintrag einf黦en
pNew->uParent = 0;
uParent = 0;
pFirst = &pData->uFirstChild;
pLast = &pData->uLastChild;
case U(TVI_LAST): // Am Ende einf黦en
if (pLast[0]) // Gibt es schon Eintr鋑e
{
pEntry = pItems[pLast[0]];
pEntry->uNextItem = uPos;
}
else {
pFirst[0] = uPos;
pLast[0] = uPos;
break;
}
pNew->uPrevItem = pLast[0]; // Eintrag einf黦en
pLast[0] = uPos;
break;
case U(TVI_SORT): // Alphapetisch einf黦en
uItem = pFirst[0];
if (!uItem) // Gibt es keine Kindeintr鋑e
{
pFirst[0] = uPos;
pLast[0] = uPos;
break;
}
if (pNew->bCallback&TVIF_TEXT) // Text 黚er 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鋒le die Eintr鋑e
{
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 黚er 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黦en
pFirst[0] = uPos;
}
if (uItem) // N鋍hsten Eintrag anpassen
{
pEntry = pItems[uItem];
pEntry->uPrevItem = uPos;
}
else { // Am Ende anh鋘gen
pLast[0] = uPos;
}
break;
case U(TVI_AFTER): // Nach einem Eintrag einf黦en
uAfter = uParent;
if (pParent) // Einen Root-Eintrag einf黦en
{
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黦en
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黦en
DoInsert:
uItem = pFirst[0];
if (!uItem) // Gibt es keine Kindeintr鋑e
{
pFirst[0] = uPos;
pLast[0] = uPos;
break;
}
if (uAfter>pData->uTreeItemsMax)
{
if ((uAfter&0xFFF00000)==0xFFE00000) // In einer genauen Reihe nach Patent einf黦en
{
uAfter &= 0xFFFFF;
uItem = pFirst[0];
if (!uItem) // Gibt es keine Kindeintr鋑e
{
pFirst[0] = uPos;
pLast[0] = uPos;
break;
}
if (uAfter==0) // In die erste Reihe einf黦en
{
pEntry = pItems[uItem];
pEntry->uPrevItem = uPos;
pNew->uNextItem = uItem;
pFirst[0] = uPos;
break;
}
uNum = 1;
uBefore = 0;
// Suche Einf黦ereihe
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黦en
pFirst[0] = uPos;
}
if (uItem) // N鋍hsten Eintrag anpassen
{
pEntry = pItems[uItem];
pEntry->uPrevItem = uPos;
}
else { // Am Ende anh鋘gen
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黦en
pFirst[0] = uPos;
}
if (uItem) // N鋍hsten Eintrag anpassen
{
pEntry = pItems[uItem];
pEntry->uPrevItem = uPos;
}
else { // Am Ende anh鋘gen
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鋒lten Eintrag ausw鋒len
{
TreeListSelectItem(pData, uPos, 0, TVC_UNKNOWN);
}
return uPos;
}
//*****************************************************************************
//*
//* TreeListSetItem
//*
//*****************************************************************************
// 膎dert einen Eintrag im Fenster
// pData : Zeiger auf die Fensterdaten
// pItem : Zeiger auf die ein zu 鋘dernden Daten
// Ergibt 0 wenn ok oder -1 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 -1;
pEntry = pData->pTreeItems[uItem];
if (!pEntry)return -1;
uBits = pItem->mask;
if (uBits&TVIF_SUBITEM) // Einen Extraeintrag 鋘dern
{
uSub = pItem->cChildren;
if (uSub>0)
{
if (uSub>= pData->uColumnCount)return -1;
pList = pData->pExtraItems[uSub-1];
pExtra = pList[uItem];
if (!pExtra) // Einen neuen Eintrag erzeugen
{
pExtra = new ExtraItem;
memset(pExtra, 0, sizeof(ExtraItem));
pExtra->iImage = TV_NOIMAGE;
pExtra->uState = pEntry->uState&(TVIS_BOLD|TVIS_UNTERLINE);
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 鋘dern
{
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)
{
uChange = 1;
}
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 -1;
pEntry = pData->pTreeItems[uItem];
if (!pEntry)return -1;
}
if (uBits&TVIS_SELECTED)
{
iVal = (pData->uStyleEx&TVS_EX_SUBSELECT) ? uSub : 0;
if (pItem->state&TVIS_SELECTED)
iRet = TreeListSelectItem(pData, uItem, iVal, TVC_UNKNOWN);
else iRet = TreeListSelectItem(pData, 0, 0, TVC_UNKNOWN);
pEntry = pData->pTreeItems[uItem];
if (!pEntry)return -1;
if (iRet>=2)
{
pList = pData->pExtraItems[uSub-1];
pExtra = pList[uItem];
if (!pExtra)return -1;
}
else if (iRet==1)
{
uChange = 1;
}
}
}
if (!uChange || !pEntry->uShowPos)return 0; // Neuzeichnen des Eintrages
UpdateRect(pData, uItem, uSub);
return 0;
}
uBits &= ~TVIF_CHILDREN;
}
//******************** Einen Basis Eintrag 鋘dern *****************************
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) && pData->hStates)
{
uChange = 1;
}
if (uBits&(TVIS_BOLD|TVIS_DROPHILITED))
{
pEntry->iTextPixels = 0;
uChange = 1;
}
if (uBits&TVIS_SELECTED)
{
pEntry->uState ^= TVIS_SELECTED;
if (pItem->state&TVIS_SELECTED)
iRet = TreeListSelectItem(pData, uItem, 0, TVC_UNKNOWN);
else iRet = TreeListSelectItem(pData, 0, 0, TVC_UNKNOWN);
pEntry = pData->pTreeItems[uItem];
if (!pEntry)return -1;
if (iRet==1)
{
uChange = 1;
}
}
if ((uBits&TVIS_EXPANDED) && pEntry->uFirstChild) // Sollen Teile auf/zugeklappt werden
{
pEntry->uState ^= TVIS_EXPANDED;
iRet = TreeListToggleItem(pData, uItem, 0);
if (iRet<1)return iRet;
}
}
if (uChange && pEntry->uShowPos) // Neuzeichnen des Eintrages
{
UpdateRect(pData, uItem, 0);
}
return 0;
}
//*****************************************************************************
//*
//* TreeListGetItem
//*
//*****************************************************************************
// Daten vone ienem 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;
memset(pExtra, 0, sizeof(ExtraItem));
pExtra->iImage = TV_NOIMAGE;
pExtra->uState = pEntry->uState&(TVIS_BOLD|TVIS_UNTERLINE);
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 = const_cast<LPTSTR>(_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 = (pEntry->uFirstChild) ? 1 : 0;
else pItem->cChildren = I_CHILDRENCALLBACK;
uBits &= ~TVIF_CHILDREN;
}
//******************** Einen Basis Eintrag 鋘dern *****************************
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 = (pEntry->uFirstChild) ? 1 : 0;
else pItem->cChildren = I_CHILDRENCALLBACK;
}
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鰏cht eine Spalte aus dem Header
// pData : Zeiger auf die Fensterdaten
// uCol : Ist die Nummer der Spalte die gel鰏cht werden soll
// Ergibt 1 wenn die Spalte gel鰏cht wurde
static int TreeListDeleteColumn(TreeListData *pData, unsigned uCol)
{
ExtraItem **pList;
ExtraItem *pExtra;
HDITEM sItem;
RECT sRect;
int iSize;
int iXoff;
int iNum;
int iVar = 0;
unsigned uItem;
unsigned uPos;
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, 1);
}
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);
iSize = pData->uSizeX;
for (uPos = 0; uPos<pData->uColumnCount; uPos++)
{
if (pData->cColumnVar[uPos]) { iVar++; continue; }
if (uPos==uCol)continue;
iSize -= pData->iColumnXpos[uPos+1]-pData->iColumnXpos[uPos];
}
Header_DeleteItem(pData->hHeader, uCol);
pData->uColumnCount--;
if (pData->uColumnCount>0) // Liste mit Extraeintr鋑en l鰏chen
{
iNum = uCol-1;
if (iNum<0)iNum = 0;
pList = pData->pExtraItems[iNum];
if (pList)
{
for (uItem = 0; uItem<=pData->uTreeItemsMax; uItem++)// Alle Eintr鋑e aus der Liste l鰏chen
{
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->cColumnVar[uCol])
{
pData->uColumnCountVar--;
}
memmove(pData->cColumnAlign+uCol, pData->cColumnAlign+uCol+1, MAX_COLUMNS-1-uCol);
memmove(pData->cColumnVar +uCol, pData->cColumnVar +uCol+1, MAX_COLUMNS-1-uCol);
pData->cColumnVar[pData->uColumnCount] = 0;
if (pData->uColumnCountVar>0) // Variable Breiten anpassen
{
sItem.mask = HDI_WIDTH;
iNum = pData->uColumnCountVar;
for (uPos = 0; uPos<pData->uColumnCount; uPos++)
{
if (!pData->cColumnVar[uPos])continue;
sItem.cxy = iSize/iNum;
Header_SetItem(pData->hHeader, uPos, &sItem);
iSize -= sItem.cxy;
iNum -= 1;
}
}
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鰏chen
{
DestroyWindow(pData->hHeader);
pData->hHeader = NULL;
pData->uStartPixel = 0;
pData->iRowHeight = 1;
UpdateHeight(pData);
InvalidateRect(pData->hWnd, &sRect, FALSE);
}
iXoff = UpdateCoumns(pData); // Hat sich die Spaltenbreiten ver鋘dert
if (iXoff<0x10000)
{
sRect.left = iXoff;
sRect.left -= pData->uScrollX;
sRect.top = pData->uStartPixel;
InvalidateRect(pData->hWnd, &sRect, FALSE);
}
UpdateScrollX(pData);
return 1;
}
//*****************************************************************************
//*
//* TreeListInsertColumn
//*
//*****************************************************************************
// F黦t eine neue Spalte in den Header ein
// pData : Zeiger auf die Fensterdaten
// iCol : Ist die Nummer der Spalte die eingef黦t wird
// pInsert : Zeiger auf die ein zu f黦enden Daten
// Ergibt die Einf黦eposition der neuen Spalte bzw. -1 bei eimem Fehler
static int TreeListInsertColumn(TreeListData *pData, int iCol, TV_COLUMN *pColumn)
{
ExtraItem **pList;
HDITEM sItem;
RECT sRect;
int iSize;
int iXoff;
int iYoff;
int iNum;
int iAdd = 0;
int iVar = 0;
char cAlign;
char cFlag;
unsigned uPos;
GetClientRect(pData->hWnd, &sRect);
if (!pData->hHeader) // Einen neuen Header erzeugen
{
iYoff = sRect.top+GetSystemMetrics(SM_CYHSCROLL);
pData->hHeader = CreateWindow(WC_HEADER, NULL, WS_VISIBLE|WS_CHILD|HDS_HORZ|HDS_BUTTONS, sRect.left, sRect.top, sRect.right, iYoff, pData->hWnd, NULL, NULL, NULL);
if (!pData->hHeader)return -1;
pData->uStartPixel = GetSystemMetrics(SM_CYHSCROLL);
pData->iRowHeight = 1;
UpdateHeight(pData);
InvalidateRect(pData->hWnd, &sRect, FALSE);
SendMessage(pData->hHeader, HDM_SETIMAGELIST, 0, (LPARAM)pData->hImages);
SendMessage(pData->hHeader, WM_SETFONT, (WPARAM)hDefaultFontN, 0);
SetWindowLong(pData->hHeader, GWL_ID, 1);
if (pData->uSizeX<=pData->uStartPixel)
pData->uSizeYsub = 0;
else pData->uSizeYsub = pData->uSizeX-pData->uStartPixel;
}
if (pData->uColumnCount>=MAX_COLUMNS) // Pr黤e die Anzahl der Spalten
{
return -1;
}
memset(&sItem, 0, sizeof(sItem)); // Die Spaltendaten zusammenstellen
iSize = pData->uSizeX;
for (uPos = 0; uPos<pData->uColumnCount; uPos++)
{
if (pData->cColumnVar[uPos]) { iVar++; continue; }
iSize -= pData->iColumnXpos[uPos+1]-pData->iColumnXpos[uPos];
}
if (pColumn->mask&TVCF_FMT)
{
sItem.mask |= HDI_FORMAT;
sItem.fmt = pColumn->fmt;
switch (sItem.fmt&HDF_JUSTIFYMASK)
{
case HDF_CENTER: cAlign = DT_CENTER; break;
case HDF_RIGHT: cAlign = DT_RIGHT; break;
default: cAlign = DT_LEFT; break;
}
}
else {
cAlign = DT_LEFT;
}
if (pColumn->mask&TVCF_IMAGE)
{
sItem.mask |= HDI_IMAGE;
sItem.iImage = pColumn->iImage;
}
if (pColumn->mask&TVCF_TEXT)
{
sItem.mask |= HDI_TEXT;
sItem.pszText = pColumn->pszText;
}
if (pColumn->mask&TVCF_WIDTH)
{
if (iVar)
{
iAdd = iSize%iVar;
iSize /= iVar;
}
if (iSize<16)iSize = 16;
sItem.mask |= HDI_WIDTH;
sItem.cxy = pColumn->cx;
cFlag = 0;
}
else { // Variable Breite berechen
iVar++;
iAdd = iSize%iVar;
iSize /= iVar;
if (iSize<16)iSize = 16;
sItem.mask |= HDI_WIDTH;
sItem.cxy = iSize;
cFlag = 1;
}
iCol = Header_InsertItem(pData->hHeader, iCol, &sItem);
if (iCol<0)return -1;
if (pData->uColumnCount>0) // Liste mit Extraeintr鋑en erzeugen
{
pList = new ExtraItem*[pData->uTreeItemsMax+1];
if (!pList)
{
Header_DeleteItem(pData->hHeader, iCol);
return -1;
}
memset(pList, 0, sizeof(ExtraItem *)*(pData->uTreeItemsMax+1));
iNum = iCol-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->cColumnAlign+iCol+1, pData->cColumnAlign+iCol, MAX_COLUMNS-1-iCol);
memmove(pData->cColumnVar +iCol+1, pData->cColumnVar +iCol, MAX_COLUMNS-1-iCol);
pData->cColumnAlign[iCol] = cAlign;
pData->cColumnVar[iCol] = cFlag;
pData->uColumnCountVar += cFlag;
pData->uColumnCount += 1;
if (pData->uSelectedSub>0 && pData->uSelectedSub>=(unsigned)iCol)
{
pData->uSelectedSub++;
}
if (pData->uTrackedSub>0 && pData->uTrackedSub>=(unsigned)iCol)
{
pData->uTrackedSub++;
}
if (pData->uFocusSub>0 && pData->uFocusSub>=(unsigned)iCol)
{
pData->uFocusSub++;
}
if (pData->uEditSub>0 && pData->uEditSub>=(unsigned)iCol)
{
pData->uEditSub++;
}
iNum = iVar-cFlag;
if (iNum>0) // Variable Breiten anpassen
{
sItem.mask = HDI_WIDTH;
sItem.cxy = iSize;
for (uPos = 0; uPos<pData->uColumnCount; uPos++)
{
if (!pData->cColumnVar[uPos])continue;
if (uPos==(unsigned)iCol)continue;
iNum--;
if (!iNum)sItem.cxy += iAdd;
Header_SetItem(pData->hHeader, uPos, &sItem);
}
}
iXoff = UpdateCoumns(pData); // Hat sich die Spaltenbreiten ver鋘dert
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黦en
{
TV_ITEM sSet;
ExtraItem *pExtra;
sSet.mask = TVIF_SUBITEM;
sSet.hItem = (HTREEITEM)pData->uInsertMark;
sSet.cChildren = iCol;
TreeListSetItem(pData, &sSet);
pExtra = pData->pExtraItems[iCol-1][pData->uInsertMark];
if (pExtra)
{
pExtra->uColorBk = pData->uColors[TVC_INSERT];
pExtra->bFlags |= TVIX_BKCOLOR;
}
}
return iCol;
}
//*****************************************************************************
//*
//* TreeListScanColumn
//*
//*****************************************************************************
// Berechnet die Breite der sichtbaren Eintr鋑e 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->hImages && (pExtra->bFlags&TVIX_HASIMAGE))
iPos = pData->iImagesXsize;
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黤t wo eine Koortinate im Fenster ist
// pData : Zeiger auf die Fensterdaten
// pInfo : Zeiger auf die ein zu f黦enden Daten
// Ergibt das Item auf dem die Koortinate zeigt
static unsigned TreeListHitTest(TreeListData *pData, TV_HITTESTINFO *pInfo)
{
int iXpos;
int iYpos;
int iZpos;
int iWidth;
int iIcon;
BaseItem *pEntry;
ExtraItem *pExtra;
unsigned uItem;
unsigned uSub;
double dScale = getDPIScale();
iXpos = pInfo->pt.x;
iYpos = pInfo->pt.y;
if ((unsigned)iXpos>=pData->uSizeX)
{
pInfo->hItem = NULL;
pInfo->flags = (iXpos<0) ? TVHT_TOLEFT : TVHT_TORIGHT;
return 0;
}
iYpos -= pData->uStartPixel;
if ((unsigned)iYpos>=pData->uSizeY)
{
pInfo->hItem = NULL;
pInfo->flags = (iYpos<0) ? TVHT_ABOVE : TVHT_BELOW;
return 0;
}
iZpos = iYpos/pData->iRowHeight;
iZpos += pData->uScrollY;
if ((unsigned)iZpos>=pData->uItemPosCount)
{
pInfo->hItem = NULL;
pInfo->flags = TVHT_NOWHERE;
return 0;
}
iXpos += pData->uScrollX;
uItem = pData->pItemPos[iZpos];
pEntry = pData->pTreeItems[uItem];
pInfo->hItem = (HTREEITEM)uItem;
if (!pEntry)return 0;
if (iXpos>=pData->iColumnXpos[1]) // Aus Extraeintrag
{
for (uSub = 1; uSub<pData->uColumnCount; uSub++)
{
if (iXpos>=pData->iColumnXpos[uSub+1])continue;
iXpos -= pData->iColumnXpos[uSub];
pExtra = pData->pExtraItems[uSub-1][uItem];
// Hat der Extraeintrag ein Icon
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->cColumnAlign[uSub]) // Textausrichtung
{
default: if (iXpos-iIcon<pExtra->iTextPixels+5)
{
pInfo->flags |= TVHT_ONSUBLABEL;
return uItem;
}
break;
case DT_RIGHT: iWidth = pData->iColumnXpos[uSub+1];
iWidth -= pData->iColumnXpos[uSub];
if (iXpos>=iWidth-pExtra->iTextPixels-5)
{
pInfo->flags |= TVHT_ONSUBLABEL;
return uItem;
}
break;
case DT_CENTER: iWidth = pData->iColumnXpos[uSub+1];
iWidth -= pData->iColumnXpos[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點ktem Bereich
{
int i6(roundToInt(6 * dScale, .5)), i7(roundToInt(7 * dScale, .5));
if (pData->uStyle &TVS_HASBUTTONS)
if (pEntry->bFlags&TVIX_HASBUTTON)
{
if (iXpos>=pData->iShift-i6)
if (iXpos<=pData->iShift+i7)
{
iYpos %= pData->iRowHeight;
iYpos -= pData->iRowHeight/2;
if (iYpos>=-i6 && iYpos<=i7)
{
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 * dScale;
if (pData->uStyleEx&TVS_EX_ITEMLINES)iXpos--;
if (iXpos<0)
{
pInfo->flags = TVHT_ONITEMICON;
return uItem;
}
}
if (iXpos<pEntry->iTextPixels+5) // Auf Text
{
pInfo->flags = TVHT_ONITEMLABEL;
}
else {
pInfo->flags = TVHT_ONITEMRIGHT;
}
return uItem;
}
//*****************************************************************************
//*
//* TreeListSetTrackItem
//*
//*****************************************************************************
// Setzt das den Einf黦eeintrag
// pData : Zeiger auf die Fensterdaten
// uItem : Ist die Nummer des Eintrages bei dem eingef黦t werden soll
// iMode : 0=davor einf黦en 1=nachher einf黦en
// Ergibt 1 wenn der Eintrag eingef黦t wurde
static unsigned TreeListSetInsertMark(TreeListData *pData, unsigned uItem, int iMode)
{
TV_INSERTSTRUCT sInsert;
ExtraItem *pExtra;
BaseItem *pEntry;
unsigned uSub;
int iRet;
if (pData->uInsertMark)
{
iRet = TreeListDeleteItem(pData, pData->uInsertMark, 1);
pData->uInsertMark = 0;
}
else {
iRet = 0;
}
if (uItem==0)return iRet;
if (uItem>pData->uTreeItemsMax)return 0;
pEntry = pData->pTreeItems[uItem];
if (!pEntry)return 0;
if (iMode)
{
uItem = pEntry->uPrevItem;
if (!uItem)uItem = U(TVI_FIRST);
}
sInsert.hParent = (HTREEITEM)pEntry->uParent;
sInsert.hInsertAfter = (HTREEITEM)uItem;
sInsert.item.mask = TVIF_SELECTEDIMAGE|TVIF_IMAGE;
sInsert.item.iImage = TV_NOIMAGE;
sInsert.item.iSelectedImage = TV_NOIMAGE;
uItem = TreeListInsertItem(pData, &sInsert);
if (!uItem)return 0;
pEntry = pData->pTreeItems[uItem];
pEntry->uColorBk = pData->uColors[TVC_INSERT];
pEntry->bFlags |= TVIX_BKCOLOR;
sInsert.item.mask |= TVIF_SUBITEM;
sInsert.item.hItem = (HTREEITEM)uItem;
for (uSub = 1; uSub<pData->uColumnCount; uSub++)
{
sInsert.item.cChildren = uSub;
TreeListSetItem(pData, &sInsert.item);
pExtra = pData->pExtraItems[uSub-1][uItem];
pExtra->uColorBk = pData->uColors[TVC_INSERT];
pExtra->bFlags |= TVIX_BKCOLOR;
}
pData->uInsertMark = uItem;
return uItem;
}
//*****************************************************************************
//*
//* TreeListGetItemColor
//*
//*****************************************************************************
// Holt die Farbe eins Feldes
// pData : Zeiger auf die Fensterdaten
// uItem : Ist die Nummer des Eintrages
// uSub : Ist die Spalte
// iMode : 0=Hintergrundfarbe 1=Textfarbe abfragen
// Ergibt die alte Farbe oder TV_NOCOLOR wenn keine Farbe eingestellt war
static COLORREF 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 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) // Extra-Eintrag ver鋘dern
{
if (uSub>=pData->uColumnCount)return TV_NOCOLOR;
pExtra = pData->pExtraItems[uSub-1][uItem];
if (!pExtra) // Extra-Eintrag erzeugen
{
sSet.mask = TVIF_SUBITEM;
sSet.hItem = (HTREEITEM)uItem;
sSet.cChildren = uSub;
if (TreeListSetItem(pData, &sSet))return TV_NOCOLOR;
pExtra = pData->pExtraItems[uSub-1][uItem];
}
if (iMode) // Textfarbe
{
uOld = (pExtra->bFlags&TVIX_TEXTCOLOR) ? pExtra->uColorText : TV_NOCOLOR;
if (uColor==TV_NOCOLOR)
{
pExtra->bFlags &= ~TVIX_TEXTCOLOR;
}
else {
pExtra->bFlags |= TVIX_TEXTCOLOR;
pExtra->uColorText = uColor;
}
}
else { // Hintergrund
uOld = (pExtra->bFlags&TVIX_BKCOLOR) ? pExtra->uColorBk : TV_NOCOLOR;
if (uColor==TV_NOCOLOR)
{
pExtra->bFlags &= ~TVIX_BKCOLOR;
}
else {
pExtra->bFlags |= TVIX_BKCOLOR;
pExtra->uColorBk = uColor;
}
}
}
else {
if (iMode) // Textfarbe
{
uOld = (pEntry->bFlags&TVIX_TEXTCOLOR) ? pEntry->uColorText : TV_NOCOLOR;
if (uColor==TV_NOCOLOR)
{
pEntry->bFlags &= ~TVIX_TEXTCOLOR;
}
else {
pEntry->bFlags |= TVIX_TEXTCOLOR;
pEntry->uColorText = uColor;
}
}
else { // Hintergrund
uOld = (pEntry->bFlags&TVIX_BKCOLOR) ? pEntry->uColorBk : TV_NOCOLOR;
if (uColor==TV_NOCOLOR)
{
pEntry->bFlags &= ~TVIX_BKCOLOR;
}
else {
pEntry->bFlags |= TVIX_BKCOLOR;
pEntry->uColorBk = uColor;
}
}
}
if (uColor!=uOld) // Neu zeichnen
{
UpdateRect(pData, uItem, uSub);
}
return uOld;
}
//*****************************************************************************
//*
//* TreeListSetTrackItem
//*
//*****************************************************************************
// Setzt das unterstrichene Item
// pData : Zeiger auf die Fensterdaten
// uItem : Ist die Nummer des Eintrages
// uSub : Ist die Spalte
// Ergibt 1 wenn der Eintrag unterstrichen wurde
static int TreeListSetTrackItem(TreeListData *pData, unsigned uItem, unsigned uSub)
{
ExtraItem *pExtra;
BaseItem *pEntry;
int iRet = 1;
if (!(pData->uStyleEx&TVS_EX_SUBSELECT))
{
uSub = 0;
}
else {
if (uSub>=pData->uColumnCount)
{
uItem = 0;
uSub = 0;
iRet = 0;
}
}
if (uItem>pData->uTreeItemsMax)
{
uItem = 0;
uSub = 0;
iRet = 0;
}
else {
if (uItem==pData->uTrackedItem)
if (uSub ==pData->uTrackedSub)
{
return iRet;
}
}
if (pData->uTrackedItem) // Den alten Eintrag zur點ksetzen
{
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;
}
}
}
return iRet;
}
//*****************************************************************************
//*
//* TreeListGetNextItem
//*
//*****************************************************************************
// Setzt sucht den n鋍hsten 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_FIRSTVISIBLE: if (pData->uItemPosCount<=0)return 0;
if (pData->uItemPosCount<=pData->uScrollY)return 0;
return pData->pItemPos[pData->uScrollY];
case TVGN_NEXTVISIBLE: if (uItem>pData->uTreeItemsMax)
{
if (uItem==U(TVI_ROOT))return pData->uFirstChild;
return 0;
}
pEntry = pData->pTreeItems[uItem];
if (!pEntry)return 0;
uPos = pEntry->uShowPos;
if (uPos< pData->uScrollY)return 0;
if (uPos>=pData->uScrollY+pData->uPageEnties-1)return 0;
return pData->pItemPos[uPos+1];
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_PREVIOUSVISIBLE: if (uItem>pData->uTreeItemsMax)
{
if (uItem==U(TVI_ROOT))return pData->uFirstChild;
return 0;
}
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;
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;
}
//*****************************************************************************
//*
//* TreeListNextSelUntil
//*
//*****************************************************************************
// Sucht rekursiv ausgew鋒lte Eintr鋑e
// 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 1 wenn der Eintrag unterstrichen 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鋒lte Eintr鋑e
// 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 1 wenn der Eintrag unterstrichen 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;
}
//*****************************************************************************
//*
//* TreeListMouseClick
//*
//*****************************************************************************
// Soll ein Mausklick ausgef黨rt werden. ACHTUNG UNLOCK wird ausgef黨rt
// 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 uMaskItem;
unsigned uMaskSub;
unsigned uMsgOld;
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
{
UNLOCK(pData);
SetFocus(pData->hWnd);
LOCK(pData);
}
sInfo.flags = (UINT)wParam;
sInfo.pt.x = LOWORD(lParam);
sInfo.pt.y = HIWORD(lParam);
uItem = TreeListHitTest(pData, &sInfo);
uMsgOld = uMsg;
if (uItem) // Wurde auf einen Eintrag getr點kt
{
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))
{
pEntry->uState &= ~TVIS_EXPANDPARTIAL;
UpdateRect(pData, uItem, 0);
}
else {
TreeListToggleItem(pData, uItem, 0);
}
}
}
else if (sInfo.flags&TVHT_ONITEMSTATEICON) // Checkbox umschalten
{
if (pData->uStyle&TVS_CHECKBOXES)
if (uMsg==WM_LBUTTONDOWN || uMsg==WM_LBUTTONDBLCLK)
{
TV_ITEM sItem;
NMTREEVIEW sNotify;
sItem.hItem = (HTREEITEM)uItem;
sItem.mask = TVIF_STATE;
sItem.stateMask = TVIS_STATEIMAGEMASK;
if (pData->uStyleEx&TVS_EX_BITCHECKBOX)
sItem.state = (pEntry->uState^0x1000);
else sItem.state = (pEntry->uState&0x1000) ? 0x2000 : 0x1000;
TreeListSetItem(pData, &sItem);
sNotify.hdr.code = TVN_CBSTATECHANGED;
sNotify.action = 0;
sNotify.itemNew.mask = TVIF_HANDLE|TVIF_PARAM|TVIF_STATE|TVIF_TEXT;
sNotify.itemNew.hItem = (HTREEITEM)uItem;
sNotify.itemNew.stateMask = 0xFFFFFFFF;
sNotify.itemNew.state = pEntry->uState;
sNotify.itemNew.lParam = pEntry->lParam;
sNotify.itemNew.pszText = pEntry->pText;
sNotify.itemNew.cchTextMax = pEntry->uTextSize;
sNotify.itemNew.cChildren = 0;
sNotify.itemOld.mask = 0;
sNotify.ptDrag.x = sInfo.pt.x;
sNotify.ptDrag.y = sInfo.pt.y;
UNLOCK(pData);
SendNotify(pData, &sNotify.hdr);
LOCK(pData);
}
}
else if (sInfo.flags&uMaskItem) // Eintrag ausw鋒len
{
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鋑e aus gew鋒lt
if (pData->uSelectedCount>1)
{
pEntry = pData->pTreeItems[uItem];
if (pEntry->uState&TVIS_SELECTED)
{
goto End;
}
}
if (uMsg==WM_LBUTTONDOWN) // Spezialsteuerung f黵 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;
goto End;
}
}
if (uMsg==WM_LBUTTONUP && pData->cClickFlag)
{
wParam &= ~(MK_CONTROL|MK_SHIFT);
uMsg = WM_LBUTTONDOWN;
}
if (wParam&MK_SHIFT) // Bis zum angeklicken Ausw鋒len
{
if (uMsg!=WM_LBUTTONDOWN)
if (uMsg!=WM_LBUTTONDBLCLK)goto End;
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鋒len
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鋑e w鋒len
{
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鋒len
pTemp = pData->pTreeItems[pEntry->uFirstChild];
if (pTemp && !pTemp->uShowPos)
{
if (uSel)
{
for (uNum = uTemp;;) // Kinder ausw鋒len
{
uNum = TreeListNextUnselUntil(pData, uNum, uTemp);
if (!uNum)break;
TreeListXorSelectItem(pData, uNum, TVC_BYMOUSE);
}
}
else {
for (uNum = uTemp;;) // Kinder abw鋒len
{
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;
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鋒len
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)
{
iMode = TVC_BYMOUSE;
}
else {
iMode = TVC_BYMOUSE|TVC_DESELECT;
}
TreeListRemoveFocus(pData);
TreeListSelectItem(pData, uItem, 0, iMode);
TreeListEnsureVisible(pData, pData->uSelectedItem, pData->uSelectedSub);
pEntry = pData->pTreeItems[uItem];
if (uMsg==WM_LBUTTONDBLCLK)
{
if (pData->bColumnEdit[0]&TVAE_DBLCLICK) // Auto-Edit mit Doppelklick
{
TreeListStartAutoEdit(pData, 0, VK_RETURN, 0);
}
else if (pEntry && (pEntry->uState&(TVIS_EXPANDED|TVIS_EXPANDPARTIAL))==(TVIS_EXPANDED|TVIS_EXPANDPARTIAL))
{
pEntry->uState &= ~TVIS_EXPANDPARTIAL;
UpdateRect(pData, uItem, 0);
}
else {
TreeListToggleItem(pData, uItem, 0);
}
}
}
else if (sInfo.flags&uMaskSub) // Extra-Eintrag ausw鋒len
{
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鋒len
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->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;
}
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->bColumnEdit[uSub]&TVAE_DBLCLICK) // Auto-Edit mit Doppelklick
{
TreeListStartAutoEdit(pData, uSub, VK_RETURN, 0);
}
else {
TreeListToggleItem(pData, uItem, 0); // Eintrag auflappen
}
}
}
}
else {
pEntry = NULL;
uSub = 0;
}
End:
switch (uMsgOld)
{
case WM_LBUTTONDOWN: sNotify.hdr.code = NM_CLICK; break;
case WM_LBUTTONDBLCLK: sNotify.hdr.code = NM_DBLCLK; break;
case WM_RBUTTONDOWN: sNotify.hdr.code = NM_RCLICK; break;
case WM_RBUTTONDBLCLK: sNotify.hdr.code = NM_RDBLCLK; break;
default: UNLOCK(pData); return;
}
if (pEntry)
{
sNotify.itemNew.stateMask = 0xFFFFFFFF;
sNotify.itemNew.state = pEntry->uState;
sNotify.itemNew.lParam = pEntry->lParam;
sNotify.itemNew.cChildren = TVHT_SUBTOCOL(sInfo.flags);
}
else {
sNotify.itemNew.stateMask = 0;
sNotify.itemNew.state = 0;
sNotify.itemNew.lParam = 0;
}
sNotify.action = 0;
sNotify.itemNew.mask = TVIF_HANDLE|TVIF_PARAM|TVIF_STATE|TVIF_SUBNUMBER;
sNotify.itemNew.hItem = (HTREEITEM)uItem;
sNotify.itemNew.pszText = (LPTSTR)-1;
sNotify.itemNew.cchTextMax = -1;
sNotify.itemOld.mask = 0;
sNotify.ptDrag.x = LOWORD(lParam);
sNotify.ptDrag.y = HIWORD(lParam);
UNLOCK(pData);
SendNotify(pData, &sNotify.hdr);
}
//*****************************************************************************
//*
//* TreeListKeyDown
//*
//*****************************************************************************
// Soll ein Tastentruck ausgef黨rt werden. ACHTUNG UNLOCK wird ausgef黨rt
// 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 sNotify;
unsigned uTemp;
unsigned uItem;
unsigned uStop;
unsigned uVal;
int iLineCmp;
int iOldLine;
int iOldSub;
int iBase;
int iLine;
int iAdd;
int iPos;
int iSub;
int iMax;
int iDel;
int iScr;
int iSel;
if (!pData->cIsEnabled) // Ist das Fenster freigegeben
{
return;
}
if (!pData->cHasFocus) // Hat das Fenster den Focus
{
UNLOCK(pData);
SetFocus(pData->hWnd);
LOCK(pData);
}
if (wParam==VK_RETURN) // Wurde Enter gedr點kt
{
UNLOCK(pData);
sNotify.hdr.code = NM_RETURN;
sNotify.wVKey = (WORD)wParam;
sNotify.flags = (UINT)lParam;
SendNotify(pData, &sNotify.hdr);
iSub = pData->uSelectedSub; // Auto-Edit starten
if (pData->bColumnEdit[iSub]&7)
{
TreeListStartAutoEdit(pData, 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) // 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遝n
if (pEntry->bFlags&TVIX_HASBUTTON)
{
TreeListToggleItem(pData, pData->uSelectedItem, 0);
}
}
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)
{
pData->uScrollY = iLine;
SetScrollPos(pData->hWnd, SB_VERT, iLine, TRUE);
UpdateView(pData);
}
}
if (iSub!=(int)pData->uScrollX)
{
uVal = pData->uColumnCount;
if (uVal)iMax = pData->iColumnXpos[uVal]-pData->uSizeX/2;
else iMax = pData->iMaxSizeX;
iMax -= pData->uSizeX/2;
if (iSub>=iMax)iSub = iMax;
if (iSub<0)iSub = 0;
if (iSub!=(int)pData->uScrollX)
{
pData->uScrollX = iSub;
SetScrollPos(pData->hWnd, SB_HORZ, iSub, TRUE);
UpdateView(pData);
if (pData->hHeader)
{
MoveWindow(pData->hHeader, -iSub, 0, pData->uSizeX-1+iSub, pData->uStartPixel, TRUE);
}
}
}
}
else { // Einen anderen Eintrag ausw鋒len
iSub = pData->uSelectedSub;
if (!pEntry)
{
iLine = 0;
iSub = 0;
iOldSub = 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;
}
if (pData->uSelectedCount>1)
{
iOldSub = iSub;
iOldLine = iLine;
}
else {
TreeListRemoveFocus(pData);
}
}
}
else {
iLine = pEntry->uShowPos-1;
if (iLine<0)iLine = 0;
uTemp = pData->uSelectedItem;
iOldLine = iLine;
iOldSub = iSub;
}
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))
{
iSub--;
}
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))
{
iSub++;
}
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鰄er
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 && iSub==0 && (pData->uStyle&TVS_CHECKBOXES))
{
TV_ITEM sItem;
sItem.hItem = (HTREEITEM)pData->uSelectedItem;
sItem.mask = TVIF_STATE;
sItem.stateMask = TVIS_STATEIMAGEMASK;
sItem.state = (pEntry->uState&0x1000) ? 0x2000 : 0x1000;
TreeListSetItem(pData, &sItem);
}
}
if (iSub>=(int)pData->uColumnCount)iSub = pData->uColumnCount-1;
if (iSub<0)iSub = 0;
if (iLine>=(int)pData->uItemPosCount)iLine = pData->uItemPosCount-1;
if (iLine<0)iLine = 0;
if (!(pData->uStyleEx&TVS_EX_SUBSELECT))iSub = 0;
if (pData->uItemPosCount>0)
if (iLine!=iOldLine || iSub!=iOldSub) // Wurde ein anderer Eintrag ausgew鋒lt
{
if (pData->uSelectedCount>1)
{
TreeListRemoveFocus(pData);
}
else {
pData->uSelectedBase = uTemp;
}
if (pData->cReSelect) // Die Shift-Auswahl neu erstellen
{
iDel = TVC_DESELECT;
}
uItem = pData->pItemPos[iLine];
TreeListSelectItem(pData, uItem, iSub, TVC_BYKEYBOARD|iDel);
TreeListEnsureVisible(pData, pData->uSelectedItem, pData->uSelectedSub);
if (pData->cReSelect && pData->uSelectedBase) // Shift-Select neu ausw鋒len
{
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);
}
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--) // 躡ersprungene Eintr鋑e mit aus/abw鋒len
{
uItem = pData->pItemPos[iPos];
if (iPos!=iLine && iPos!=iBase)
{
if (!TreeListXorSelectItem(pData, uItem, TVC_BYKEYBOARD))continue;
}
pTemp = pData->pTreeItems[uItem]; // Auch unsichtbare Kinder abw鋒len
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);
}
}
}
}
}
}
}
sNotify.hdr.code = TVN_KEYDOWN;
sNotify.wVKey = (WORD)wParam;
sNotify.flags = (UINT)lParam;
UNLOCK(pData);
SendNotify(pData, &sNotify.hdr);
iSub = pData->uSelectedSub; // Auto-Edit starten
if (!iScr && (pData->bColumnEdit[iSub]&7))
{
TreeListStartAutoEdit(pData, iSub, wParam, lParam);
}
}
//*****************************************************************************
//*
//* TreeListSortItemsEx
//*
//*****************************************************************************
// Sortiert die Kindereintr鋑e 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 uParent;
unsigned uFirst;
unsigned uItem;
BaseItem *pEntry;
BaseItem *pNext;
BaseItem **pList;
uParent = U(pSortData->hParent);
if (uParent>pData->uTreeItemsMax) // Root-Eintrag sortieren
{
if (uParent!=U(TVI_ROOT))return 0;
uFirst = pData->uFirstChild;
if (uFirst==0)return 1;
if (uFirst==pData->uLastChild)
{
if (uFirst)
{
sSort.hParent = (HTREEITEM)uFirst;
sSort.lParam = pSortData->lParam;
sSort.lpfnCompare = pSortData->lpfnCompare;
TreeListSortItemsEx(pData, &sSort, iMode);
}
return 1;
}
pList = pData->pTreeItems;
uParent = 0;
}
else { // Untereintrag sortieren
pList = pData->pTreeItems;
pEntry = pList[uParent];
if (pEntry==NULL)return 0;
uFirst = pEntry->uFirstChild;
if (uFirst==0)return 1;
if (uFirst==pEntry->uLastChild)
{
if (uFirst)
{
sSort.hParent = (HTREEITEM)uFirst;
sSort.lParam = pSortData->lParam;
sSort.lpfnCompare = pSortData->lpfnCompare;
TreeListSortItemsEx(pData, &sSort, iMode);
}
return 1;
}
}
if (iMode) // Sortiere die Untereintr鋑e
{
pNext = pList[uFirst];
sSort.hParent = (HTREEITEM)uFirst;
sSort.lParam = pSortData->lParam;
sSort.lpfnCompare = pSortData->lpfnCompare;
do {
TreeListSortItemsEx(pData, &sSort, SORT_NOUPDATE);
sSort.hParent = (HTREEITEM)pNext->uNextItem;
pNext = pList[pNext->uNextItem];
} while (pNext);
}
//******************** Erzeuge Eintragsliste **********************************
unsigned *pItemList;
unsigned *pItemNew;
unsigned uEnties[128];
unsigned uPos;
unsigned uMax;
uItem = uFirst;
pItemList = uEnties;
uMax = 128;
uPos = 0;
do { // Alle Kindeintr鋑e 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;
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;
pData->cLockChanges = 1;
UNLOCK(pData);
hTreeWnd = pData->hWnd;
pCompare = pSortData->lpfnCompare;
lParamSort = pSortData->lParam;
iLast = uPos-1;
iStart = 0;
iLevel = 0;
for (;;)
{
iLower = iStart;
iMiddle = (iStart+iLast)>>1; // Mitte bereichnen
iUpper = iLast +1;
XCHANGE_MEM(iMiddle, iLower);
uItem = pItemList[iStart];
hItemTemp = (HTREEITEM)uItem;
lParamTemp = pList[uItem]->lParam;
for (;;)
{
do {
iLower++;
if (iLower>iLast)break;
uItem = pItemList[iLower];
iCmp = pCompare(hTreeWnd, (HTREEITEM)uItem, hItemTemp, pList[uItem]->lParam, lParamTemp, lParamSort);
} while (iCmp<=0);
do {
iUpper--;
if (iUpper<=iStart)break;
uItem = pItemList[iUpper];
iCmp = pCompare(hTreeWnd, (HTREEITEM)uItem, hItemTemp, pList[uItem]->lParam, lParamTemp, lParamSort);
} while (iCmp>=0);
if (iUpper<iLower)break;
XCHANGE_MEM(iUpper, iLower);
}
XCHANGE_MEM(iStart, iUpper);
if (iUpper-1-iStart >= iLast-iLower)
{
if (iStart+1 < iUpper)
{
uMemL[iLevel] = iStart;
uMemU[iLevel] = iUpper-1;
iLevel++;
}
if (iLower < iLast)
{
iStart = iLower;
continue;
}
}
else {
if (iLower<iLast)
{
uMemL[iLevel] = iLower;
uMemU[iLevel] = iLast;
iLevel++;
}
if (iStart+1 < iUpper)
{
iLast = iUpper-1;
continue;
}
}
// Eine Ebene absteigen
iLevel--;
if (iLevel>=0) // Noch Ebenen vorhanden
{
iStart = uMemL[iLevel];
iLast = uMemU[iLevel];
continue;
}
break;
}
LOCK(pData);
pData->cLockChanges = 0;
//******************** Eintr鋑e neu einsortirenen *****************************
unsigned uNum;
unsigned uLast;
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鋘gen
{
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
{
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鋑e 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 uParent;
unsigned uFirst;
unsigned uItem;
BaseItem *pEntry;
BaseItem *pNext;
BaseItem **pList;
uParent = U(pSortData->hParent);
if (uParent>pData->uTreeItemsMax) // Root-Eintrag sortieren
{
if (uParent!=U(TVI_ROOT))return 0;
uFirst = pData->uFirstChild;
if (uFirst==0)return 1;
if (uFirst==pData->uLastChild)
{
if (uFirst)
{
sSort.hParent = (HTREEITEM)uFirst;
sSort.lParam = pSortData->lParam;
sSort.lpfnCompare = pSortData->lpfnCompare;
TreeListSortItemsCb(pData, &sSort, iMode);
}
return 1;
}
pList = pData->pTreeItems;
uParent = 0;
}
else { // Untereintrag sortieren
pList = pData->pTreeItems;
pEntry = pList[uParent];
if (pEntry==NULL)return 0;
uFirst = pEntry->uFirstChild;
if (uFirst==0)return 1;
if (uFirst==pEntry->uLastChild)
{
if (uFirst)
{
sSort.hParent = (HTREEITEM)uFirst;
sSort.lParam = pSortData->lParam;
sSort.lpfnCompare = pSortData->lpfnCompare;
TreeListSortItemsCb(pData, &sSort, iMode);
}
return 1;
}
}
if (iMode) // Sortiere die Untereintr鋑e
{
pNext = pList[uFirst];
sSort.hParent = (HTREEITEM)uFirst;
sSort.lParam = pSortData->lParam;
sSort.lpfnCompare = pSortData->lpfnCompare;
do {
TreeListSortItemsCb(pData, &sSort, SORT_NOUPDATE);
sSort.hParent = (HTREEITEM)pNext->uNextItem;
pNext = pList[pNext->uNextItem];
} while (pNext);
}
//******************** Erzeuge Eintragsliste **********************************
unsigned *pItemList;
unsigned *pItemNew;
unsigned uEnties[128];
unsigned uPos;
unsigned uMax;
uItem = uFirst;
pItemList = uEnties;
uMax = 128;
uPos = 0;
do { // Alle Kindeintr鋑e 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;
PFNTVCOMPARE pCompare;
LPARAM lParamTemp;
LPARAM lParamSort;
int iLower, iUpper, iMiddle, iCmp;
int uMemL[30], uMemU[30];
int iStart, iLast;
int iLevel;
unsigned 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鋑e neu einsortirenen *****************************
unsigned uNum;
unsigned uLast;
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鋘gen
{
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
{
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鋑e 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 uItem;
unsigned uFirst;
BaseItem *pEntry;
BaseItem *pNext;
BaseItem **pList;
if (uParent>pData->uTreeItemsMax) // Root-Eintrag sortieren
{
if (uParent!=U(TVI_ROOT))return 0;
uFirst = pData->uFirstChild;
if (uFirst==0)return 1;
if (uFirst==pData->uLastChild)
{
if (uFirst)
{
TreeListSortItems(pData, uFirst, iMode);
}
return 1;
}
pList = pData->pTreeItems;
uParent = 0;
}
else { // Untereintrag sortieren
pList = pData->pTreeItems;
pEntry = pList[uParent];
if (pEntry==NULL)return 0;
uFirst = pEntry->uFirstChild;
if (uFirst==0)return 1;
if (uFirst==pEntry->uLastChild)
{
if (uFirst)
{
TreeListSortItems(pData, uFirst, iMode);
}
return 1;
}
}
if (iMode) // Sortiere die Untereintr鋑e
{
uItem = uFirst;
do {
TreeListSortItems(pData, uItem, SORT_NOUPDATE);
pNext = pList[uItem];
uItem = pNext->uNextItem;
} while (uItem);
}
//******************** Erzeuge Eintragsliste **********************************
unsigned *pItemList;
unsigned *pItemNew;
unsigned uEnties[128];
unsigned uPos;
unsigned uMax;
uItem = uFirst;
pItemList = uEnties;
uMax = 128;
uPos = 0;
do { // Alle Kindeintr鋑e 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;
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;
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鋑e neu einsortirenen *****************************
unsigned uNum;
unsigned uLast;
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鋘gen
{
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
{
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
// iCancel : Gab es einen Abbruch des Editierens
// Ergibt das Handle des Edit-Controlls oder NULL bei einem Fehler
static int TreeListEndLabelEdit(TreeListData *pData, int iCancel)
{
TCHAR cText[260];
NMTVDISPINFO sNotify;
TV_ITEM sSet;
LRESULT lRet;
unsigned uSub;
unsigned uItem;
ExtraItem *pExtra;
BaseItem *pEntry;
int iAuto;
int iIcon;
LRESULT iPos;
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 (iCancel) // Abbruch
{
sNotify.item.cchTextMax = 0;
sNotify.item.pszText = NULL;
}
else {
GetWindowText(pData->hEdit, cText, sizeof(cText)/sizeof(cText[0]));
cText[sizeof(cText)/sizeof(cText[0])-1] = 0;
sNotify.item.cchTextMax = sizeof(cText)/sizeof(cText[0])-1;
sNotify.item.pszText = cText;
}
sNotify.hdr.code = TVN_ENDLABELEDIT;
sNotify.item.mask = TVIF_HANDLE|TVIF_PARAM|TVIF_STATE|TVIF_TEXT|TVIF_SUBITEM;
sNotify.item.hItem = (HTREEITEM)uItem;
sNotify.item.stateMask = 0xFFFFFFFF;
sNotify.item.cChildren = uSub;
if (uSub)
{
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 || iCancel)return 0;
if (cCb)
{
sNotify.hdr.code = TVN_SETDISPINFO;
sNotify.item.mask = TVIF_HANDLE|TVIF_PARAM|TVIF_STATE|TVIF_TEXT|TVIF_SUBITEM;
sNotify.item.stateMask = (UINT)~TVIS_BASEFLAGS;
sNotify.item.hItem = (HTREEITEM)uItem;
sNotify.item.cchTextMax = sizeof(cText)/sizeof(cText[0])-1;
sNotify.item.pszText = cText;
sNotify.item.cChildren = uSub;
UNLOCK(pData);
SendNotify(pData, &sNotify.hdr);
LOCK(pData);
}
else {
sSet.mask = TVIF_SUBITEM|TVIF_TEXT;
sSet.cchTextMax = sizeof(cText)/sizeof(cText[0]);
sSet.hItem = (HTREEITEM)uItem;
sSet.cChildren = uSub;
sSet.pszText = cText;
iIcon = pData->iColumnCbIcon[uSub];
iAuto = pData->bColumnEdit[uSub]&7;
if (iAuto==TVAE_COMBO || iAuto==TVAE_CBLIST) // Auch ein Icon zuweisen
if (iIcon>=0)
{
iPos = SendMessage(pData->hEdit, CB_GETCURSEL, 0, 0);
sSet.mask |= TVIF_IMAGE|TVIF_SELECTEDIMAGE;
sSet.iImage = iIcon+iPos;
sSet.iSelectedImage = sSet.iImage;
}
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
// 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;
LPARAM lParam;
ExtraItem *pExtra;
BaseItem *pEntry;
NMTVDISPINFO sNotify;
WNDPROC pProc;
LPCTSTR pText;
RECT sRect;
HFONT hFont;
unsigned uCol;
unsigned uBits;
unsigned uSize;
unsigned uState;
unsigned uHeight;
int iPixels;
int iWidth;
int iTemp;
uBits = uSub>>29;
uSub &= 0x1FFFFFFF;
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鰏chen
{
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, 0, 0, 0, 0, pData->hWnd, NULL, NULL, NULL);
pData->uEditMode = 1;
break;
case 2: pData->hEdit = CreateWindow(_T("COMBOBOX"), NULL, WS_BORDER|WS_CHILD|CBS_AUTOHSCROLL|CBS_DROPDOWNLIST, 0, 0, 0, 0, pData->hWnd, NULL, NULL, NULL);
pData->uEditMode = 2;
break;
default:pData->hEdit = CreateWindow(_T("EDIT"), NULL, WS_BORDER|WS_CHILD|ES_AUTOHSCROLL|ES_LEFT, 0, 0, 0, 0, pData->hWnd, NULL, NULL, NULL);
pData->uEditMode = 0;
break;
}
if (!pData->hEdit)return NULL;
pProc = (WNDPROC)GetWindowLongPtr(pData->hEdit, GWL_WNDPROC);
SetWindowLongPtr(pData->hEdit, GWL_USERDATA, (LPARAM)pProc);
SetWindowLongPtr(pData->hEdit, GWL_WNDPROC, (LPARAM)EditProc);
SetWindowLong(pData->hEdit, GWL_ID, 3);
hWnd = GetWindow(pData->hEdit, GW_CHILD);
while (hWnd)
{
pProc = (WNDPROC)GetWindowLongPtr(hWnd, GWL_WNDPROC);
SetWindowLongPtr(hWnd, GWL_USERDATA, (LPARAM)pProc);
SetWindowLongPtr(hWnd, GWL_WNDPROC, (LPARAM)EditProc);
hWnd = GetNextWindow(hWnd, GW_HWNDNEXT);
}
if (!pData->hEdit)return NULL;
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)
{
sRect.right = pData->iColumnXpos[1];
sRect.right -= pData->uScrollX;
}
iPixels = sRect.right-sRect.left-2;
}
else {
if (pText)iPixels += str_len(pText);
}
}
else { // Extraeintrag bearbeiten
if (uBits&4)
TreeListGetItemRect(pData, uItem, TVIR_GETCOLUMN| TVIR_COLTOSUB(uSub), &sRect);
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 (pText)iPixels += str_len(pText);
switch (pData->cColumnAlign[uSub])
{
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);
if (!pText) return NULL;
sNotify.hdr.code = TVN_BEGINLABELEDIT;
sNotify.item.mask = TVIF_HANDLE|TVIF_PARAM|TVIF_STATE|TVIF_TEXT|TVIF_SUBITEM;
sNotify.item.hItem = (HTREEITEM)uItem;
sNotify.item.pszText = (LPTSTR)pText;
sNotify.item.lParam = lParam;
sNotify.item.state = uState;
sNotify.item.cchTextMax = uSize;
sNotify.item.stateMask = 0xFFFFFFFF;
sNotify.item.cChildren = uSub;
lRet = SendNotify(pData, &sNotify.hdr);
LOCK(pData);
if (lRet) // Das Editiren abbrechen
{
TreeListEndLabelEdit(pData, 1);
return NULL;
}
pEntry = pData->pTreeItems[uItem];
if (!pEntry)return NULL;
if (iPixels<32)iPixels = 32;
UNLOCK(pData);
SetFocus(pData->hEdit);
LOCK(pData);
if (pData->uEditMode)
{
sRect.top--;
uHeight = 260;
}
else {
uHeight = pData->iFontHeight+4;
}
SetWindowText(pData->hEdit, pText);
SendMessage(pData->hEdit, WM_SETFONT, (WPARAM)hFont, 0);
int nLen(0);
if (pText)
nLen = _tcslen(pText);
switch (pData->uEditMode)
{
case 0: SendMessage(pData->hEdit, EM_SETSEL, nLen, nLen); break;
case 1: SendMessage(pData->hEdit, CB_SETEDITSEL, MAKELPARAM(0, 255), uSize); break;
}
iPixels = (sRect.right - sRect.left) * .9;
SetWindowPos(pData->hEdit, HWND_TOP, sRect.left+2, sRect.top+1, iPixels, uHeight, SWP_SHOWWINDOW);
RedrawWindow(pData->hEdit, NULL, NULL, RDW_INVALIDATE|RDW_UPDATENOW|RDW_ERASE);
pData->uEditItem = uItem;
pData->uEditSub = uSub;
return pData->hEdit;
}
//*****************************************************************************
//*
//* TreeListStartAutoEdit
//*
//*****************************************************************************
// Startet die Autoeditierung f黵 eine Spalte
// pData : Zeiger auf die Fensterdaten
// uColumn : Ist die Nummer der Spalte
// wParam : Ist der W-Parameter des Tastendrucks
// lParam : Ist der L-Parameter des Tastendrucks
// Ergibt 1 das Editieren gestartet wurde, ansonsten 0
// 0 wenn der Eintrag nicht gew鋒lt wurde
static int TreeListStartAutoEdit(TreeListData *pData, unsigned uColumn, WPARAM wParam, LPARAM lParam)
{
TCHAR cBuffer[256];
LPTSTR *pList;
LPTSTR pText;
TV_ITEM sItem;
TCHAR cChar;
unsigned uMode;
unsigned uBits;
unsigned uMax;
unsigned uPos;
unsigned uLen;
unsigned uSub;
HWND hWnd;
int iSel;
int iIcon;
if (pData->uEditItem)return 0;
if (pData->uColumnCount<=uColumn)return 0;
if (!(pData->uStyle&TVS_EDITLABELS))return 0;
uBits = pData->bColumnEdit[uColumn];
uMode = uBits&7;
if (uMode==0)return 0;
if (wParam!=VK_RETURN)
{
if (uMode>=TVAE_CBLIST)return 0;
if (uBits &TVAE_ONLYRETURN)return 0;
if (wParam<'0' || wParam>'Z')return 0;
}
uMax = pData->bColumnCbSize[uColumn];
if (!uMax)uMax = 1024;
//******************** Weiterschalten mit Return ******************************
if (uMode==TVAE_STEP)
{
sItem.mask = TVIF_TEXT|TVIF_SUBITEM|TVIF_TEXTPTR;
sItem.hItem = (HTREEITEM)pData->uSelectedItem;
sItem.cChildren = uColumn;
if (!TreeListGetItem(pData, &sItem))return 0;
if (uBits&TVAE_PTRLIST) // Zeigerliste char *pTexte[]={"1","2",NULL};
{
pList = (LPTSTR *)pData->pColumnCbData[uColumn];
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)return 0;
}
else { // Textliste char *pText="1|2|3";
pText = (LPTSTR)pData->pColumnCbData[uColumn];
cChar = (TCHAR)pData->bColumnCbChar[uColumn];
sItem.cchTextMax--;
for (uPos = 0;; uPos++)
{
if (uPos>=uMax)
{
pText = (LPTSTR)pData->pColumnCbData[uColumn];
uPos = 0;
break;
}
for (uLen = 0; pText[uLen]; uLen++) // Subtextl鋘ge 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->pColumnCbData[uColumn];
uPos = 0;
break;
}
pText += uLen;
if (pText[0]==cChar)pText++;
if (pText[0]==0)
{
pText = (LPTSTR)pData->pColumnCbData[uColumn];
uPos = 0;
}
break;
}
pText += uLen;
if (pText[0]==cChar)pText++;
if (pText[0]==0)
{
pText = (LPTSTR)pData->pColumnCbData[uColumn];
uPos = 0;
break;
}
}
for (uLen = 0; pText[uLen] && uLen<256; uLen++)
{
if (pText[uLen]==cChar)break;
cBuffer[uLen] = pText[uLen];
}
cBuffer[uLen] = 0;
pText = cBuffer;
}
sItem.mask = TVIF_SUBITEM|TVIF_TEXT;
sItem.hItem = (HTREEITEM)pData->uSelectedItem;
sItem.stateMask = 0;
sItem.pszText = pText;
sItem.cchTextMax = 256;
sItem.cChildren = uColumn;
iIcon = pData->iColumnCbIcon[uColumn];
if (iIcon>=0) // Auch ein Icon zuweisen
{
sItem.mask |= TVIF_IMAGE|TVIF_SELECTEDIMAGE;
sItem.iImage = iIcon+uPos;
sItem.iSelectedImage = sItem.iImage;
}
return TreeListSetItem(pData, &sItem);
}
//******************** Edit oder Combobox *************************************
uSub = (uMode-1)<<29;
uSub |= uColumn;
if (uBits&TVAE_FULLWIDTH)
{
uSub |= 0x80000000;
}
hWnd = TreeListEditLabel(pData, pData->uSelectedItem, uSub);
if (!hWnd)return 0;
if (uMode!=TVAE_EDIT)
{
iSel = (uMode!=TVAE_CBLIST) ? -1 : 0;
sItem.mask = TVIF_TEXT|TVIF_SUBITEM|TVIF_TEXTPTR;
sItem.hItem = (HTREEITEM)pData->uSelectedItem;
sItem.cChildren = uColumn;
if (!TreeListGetItem(pData, &sItem))return 0;
if (uBits&TVAE_PTRLIST) // Zeigerliste char *pTexte[]={"1","2",NULL};
{
pList = (LPTSTR *)pData->pColumnCbData[uColumn];
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->pColumnCbData[uColumn];
cChar = (TCHAR)pData->bColumnCbChar[uColumn];
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 (uMode<TVAE_CBLIST && wParam!=VK_RETURN) // Ersten Buchstaben ans Fenster 黚ergeben
{
MSG sMsg;
sMsg.hwnd = hWnd;
sMsg.message = WM_KEYDOWN;
sMsg.wParam = wParam;
sMsg.lParam = lParam;
sMsg.time = 0;
sMsg.pt.x = 0;
sMsg.pt.y = 0;
TranslateMessage(&sMsg);
}
return 1;
}
//*****************************************************************************
//*
//* TreeListProc
//*
//*****************************************************************************
// Ist die Fensterfunktion f黵 das TreeList Fenster
static LRESULT CALLBACK TreeListProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
TreeListData *pData;
MSG *pMsg;
LPARAM lRet;
TV_HITTESTINFO sInfo;
unsigned uChange;
unsigned uFlag;
unsigned uVal;
int iPos;
int iMax;
HDC hDc;
double dScale(1.);
switch (uMsg)
{
case WM_CREATE: // Das Fenster erzeugen
pData = new TreeListData;
if (!pData)return -1;
memset(pData, 0, sizeof(TreeListData));
pData->pTreeItems = new BaseItem*[1];
if (!pData->pTreeItems)
{
delete pData;
return -1;
}
pData->pItemPos = new unsigned[1];
if (!pData->pItemPos)
{
delete pData->pTreeItems;
delete pData;
return -1;
}
GlobalInit();
pData->pItemPos[0] = 0;
pData->pTreeItems[0] = NULL;
dScale = getDPIScale();
SetWindowLongPtr(hWnd, 0, (LONG_PTR)pData);
pData->iIndent = roundToInt(DEFAULT_IDENT * dScale, .5);
pData->iShift = roundToInt(DEFAULT_SHIFT * dScale, .5);
pData->uStyle = GetWindowLong(hWnd, GWL_STYLE);
pData->hSem = CreateSemaphore(0, 1, 0x70000000, 0);
pData->hWnd = hWnd;
pData->cIsEnabled = (char)IsWindowEnabled(hWnd);
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))
{
CrateToolTip(pData);
}
UpdateFont(pData);
UpdateHeight(pData);
UpdateColors(pData);
UpdateScrollY(pData);
if (pData->uStyle&TVS_CHECKBOXES)
{
CreateStateImageList(pData);
}
return 0;
case WM_DESTROY: // Das Fenster zerst鰎en
pData = GetHandle(hWnd);
LOCK(pData);
TreeListDeleteItem(pData, U(TVI_ROOT), 0);
if (pData->hEdit)DestroyWindow(pData->hEdit);
if (pData->hHeader)DestroyWindow(pData->hHeader);
if (pData->hToolTip)DestroyWindow(pData->hToolTip);
if (pData->hStates)ImageList_Destroy(pData->hStates);
if (pData->hImages)ImageList_Destroy(pData->hImages);
for (uVal = 1; uVal<pData->uColumnCount; uVal++)
{
delete pData->pExtraItems[uVal-1];
}
delete pData->pTreeItems;
delete pData->pItemPos;
UNLOCK(pData);
CloseHandle(pData->hSem);
memset(pData, 0, sizeof(TreeListData));
delete pData;
GlobalDeinit();
return 0;
case WM_SIZE: // Die Fenstergr鲞e wurde ver鋘dert
pData = GetHandle(hWnd);
uFlag = 0;
LOCK(pData);
uVal = LOWORD(lParam);
if (uVal!=pData->uSizeX)
{
if (pData->uColumnCountVar) // Spalten mit variabler Breite nach f黨ren
{
unsigned uPos;
HDITEM sItem;
RECT sRect;
int iDelta;
int iSize;
int iOld;
int iNum;
uFlag = 0;
sItem.mask = HDI_WIDTH;
iDelta = uVal-pData->uSizeX;
iNum = pData->uColumnCountVar;
for (uPos = 0; uPos<pData->uColumnCount; uPos++)
{
if (!pData->cColumnVar[uPos])continue;
iOld = pData->iColumnXpos[uPos+1];
iOld -= pData->iColumnXpos[uPos];
iSize = iOld+iDelta/iNum;
uFlag = 1;
if (iOld<=16 && iSize<16)
{
iNum--;
continue;
}
if (uPos)iSize++;
if (iSize<16)iSize = 16;
sItem.cxy = iSize;
Header_SetItem(pData->hHeader, uPos, &sItem);
iDelta -= iSize-iOld;
iNum--;
}
iNum = UpdateCoumns(pData);
GetClientRect(hWnd, &sRect);
sRect.left = iNum;
sRect.left -= pData->uScrollX;
sRect.top = pData->uStartPixel;
InvalidateRect(hWnd, &sRect, FALSE);
}
if (uVal>pData->uSizeX) // Hat sich die Breite vergr鲞ert
{
RECT sRect;
GetClientRect(hWnd, &sRect);
sRect.right = uVal;
sRect.left = pData->uSizeX;
InvalidateRect(hWnd, &sRect, FALSE);
}
pData->uSizeX = uVal;
pData->iColumnXpos[pData->uColumnCount+1] = uVal;
MoveWindow(pData->hHeader, -(int)pData->uScrollX, 0, pData->uSizeX+pData->uScrollX-1, pData->uStartPixel, FALSE);
UpdateScrollX(pData);
}
uVal = HIWORD(lParam);
if (uVal!=pData->uSizeY)
{
if (uVal>pData->uSizeY) // Hat sich die H鰄e vergr鲞ert
{
RECT sRect;
GetClientRect(hWnd, &sRect);
sRect.bottom = uVal;
sRect.top = pData->uSizeY;
InvalidateRect(hWnd, &sRect, FALSE);
}
pData->uSizeY = uVal;
pData->uSizeYsub = (uVal<=pData->uStartPixel) ? 0 : uVal-pData->uStartPixel;
pData->uMaxEnties = pData->uSizeY;
pData->uMaxEnties -= pData->uStartPixel;
pData->uPageEnties = pData->uMaxEnties;
pData->uMaxEnties += pData->iRowHeight-1;
pData->uMaxEnties /= pData->iRowHeight;
pData->uPageEnties /= pData->iRowHeight;
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!=(char)wParam)
{
LOCK(pData);
pData->cIsEnabled = (char)wParam;
UpdateView(pData);
if (!pData->cIsEnabled)UpdateToolTip(pData, 0, 0);
UNLOCK(pData);
}
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
{
NMTREEVIEW sNotify;
BaseItem *pEntry;
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 = (pData->uDragFlags&MK_LBUTTON) ? TVN_BEGINDRAG : TVN_BEGINRDRAG;
sNotify.action = (pEntry->uState&TVIS_EXPANDED) ? TVE_COLLAPSE : TVE_EXPAND;
sNotify.itemNew.mask = TVIF_HANDLE|TVIF_PARAM|TVIF_STATE|TVIF_SUBNUMBER;
sNotify.itemNew.hItem = (HTREEITEM)pData->uDragItem;
sNotify.itemNew.stateMask = 0xFFFFFFFF;
sNotify.itemNew.state = pEntry->uState;
sNotify.itemNew.lParam = pEntry->lParam;
sNotify.itemNew.cChildren = pData->uDragSub;
sNotify.itemNew.pszText = (LPTSTR)-1;
sNotify.itemNew.cchTextMax = -1;
sNotify.itemOld.mask = 0;
sNotify.ptDrag.x = LOWORD(lParam);
sNotify.ptDrag.y = HIWORD(lParam);
pData->uDragFlags = 0;
pData->cClickFlag = 0;
UNLOCK(pData);
SendNotify(pData, &sNotify.hdr);
}
return 0;
case WM_LBUTTONUP: // Linken Mausklick aufheben
pData = GetHandle(hWnd);
pData->uDragFlags &= ~MK_LBUTTON;
if (pData->cClickFlag)
{
TreeListMouseClick(pData, uMsg, wParam, lParam);
pData->cClickFlag = 0;
}
return 0;
case WM_RBUTTONUP: // Linken Mausklick aufheben
pData = GetHandle(hWnd);
pData->uDragFlags &= ~MK_RBUTTON;
return 0;
case WM_LBUTTONDOWN: // Mausklick
case WM_LBUTTONDBLCLK:
case WM_RBUTTONDOWN:
case WM_RBUTTONDBLCLK:
pData = GetHandle(hWnd);
LOCK(pData);
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_MOUSEWHEEL: // Scrollen mit dem Mausrad
if (!(LOWORD(wParam)&(MK_CONTROL|MK_LBUTTON|MK_MBUTTON|MK_RBUTTON|MK_SHIFT)))
{
pData = GetHandle(hWnd);
iPos = pData->uScrollY;
iPos -= (short)HIWORD(wParam)/(WHEEL_DELTA/2);
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 1;
}
return 0;
case WM_HSCROLL: // Vertikalles scrollen
pData = GetHandle(hWnd);
iPos = pData->uScrollX;
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->iColumnXpos[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-1+iPos, pData->uStartPixel, TRUE);
}
}
return 0;
case WM_VSCROLL: // Vertikalles scrollen
pData = GetHandle(hWnd);
iPos = pData->uScrollY;
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: iPos = HIWORD(wParam); break;
case SB_THUMBTRACK: iPos = HIWORD(wParam); break;
}
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鋘dert
pData = GetHandle(hWnd);
LOCK(pData);
if (pData->hHeader)SendMessage(pData->hHeader, WM_SYSCOLORCHANGE, 0, 0);
UpdateColors(pData);
UNLOCK(pData);
return 0;
case WM_SETFONT: // Einen neuen Font zuweisen
pData = GetHandle(hWnd);
LOCK(pData);
pData->hFontN = (HFONT)wParam;
if (UpdateFont(pData))UpdateView(pData);
UNLOCK(pData);
return 0;
case WM_STYLECHANGED: // Hat sich der Fenstersytle ge鋘dert
if (wParam==GWL_STYLE)
{
pData = GetHandle(hWnd);
lParam = ((STYLESTRUCT *)lParam)->styleNew;
LOCK(pData);
uChange = (unsigned)lParam^pData->uStyle;
pData->uStyle = (unsigned)lParam;
if (uChange&(TVS_CHECKBOXES|TVS_NONEVENHEIGHT))
{
if (lParam&TVS_CHECKBOXES)
{
if (pData->hStates==NULL)
{
CreateStateImageList(pData);
}
}
else {
if (pData->iStateCheck && pData->hStates)
{
ImageList_Destroy(pData->hStates);
pData->hStates = NULL;
pData->iStateCheck = 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)
{
UpdateScrollX(pData);
UpdateScrollY(pData);
}
if (uChange&TVS_NOTOOLTIPS)
{
if (pData->uStyle&TVS_NOTOOLTIPS)
{
CrateToolTip(pData);
}
else {
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);
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鰃ertes einblenden
{
pData->uToolTipShow--;
if (pData->uToolTipShow)return 0;
LOCK(pData);
GetCursorPos(&sInfo.pt);
ScreenToClient(hWnd, &sInfo.pt);
TreeListHitTest(pData, &sInfo);
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);
pData->uToolTipItem = 0;
}
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_ONSUBICON|TVHT_ONSUBLABEL))
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);
pData->uToolTipItem = 0;
}
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, 0);
UNLOCK(pData);
if (pData->uEditMode)
if (wParam!=MAKELONG(3, EN_KILLFOCUS))
{
SetFocus(pData->hWnd);
}
}
}
if (wParam==MAKELONG(3, EN_ESCAPE))
{
pData = GetHandle(hWnd);
if (pData->uEditItem)
{
LOCK(pData);
TreeListEndLabelEdit(pData, 1);
UNLOCK(pData);
if (pData->uEditMode)SetFocus(pData->hWnd);
}
}
return 0;
case WM_NOTIFY: // Notify Nachrichnten
if (wParam==1)
{
NMHEADER *pHdr = (NMHEADER *)lParam;
HDITEM sItem;
RECT sRect;
int iSize;
int iCol;
if (pHdr->hdr.code==HDN_DIVIDERDBLCLICK) // Doppelcklick auf Spaltentrenner
{
int iSize;
pData = GetHandle(hWnd);
LOCK(pData);
iSize = TreeListScanColumn(pData, pHdr->iItem);
if (iSize)
{
sItem.cxy = iSize;
pHdr->hdr.code = HDN_ENDTRACK;
pHdr->pitem = &sItem;
}
UNLOCK(pData);
}
// Wurde die Spaltenbreite ver鋘dert
if (pHdr->hdr.code==HDN_TRACK || pHdr->hdr.code==HDN_ENDTRACK)
{
pData = GetHandle(hWnd);
LOCK(pData);
iCol = pHdr->iItem;
sItem.mask = HDI_WIDTH;
sItem.cxy = pHdr->pitem->cxy;
Header_SetItem(pData->hHeader, iCol, &sItem);
iSize = UpdateCoumns(pData);
if (iSize<0x10000)
{
GetClientRect(hWnd, &sRect);
sRect.left = iSize;
sRect.left -= pData->uScrollX;
InvalidateRect(hWnd, &sRect, FALSE);
}
UpdateScrollX(pData);
UNLOCK(pData);
if (!pData->cHasFocus && pHdr->hdr.code==HDN_ENDTRACK)
{
SetFocus(pData->hWnd);
}
}
if (pHdr->hdr.code==HDN_ITEMCLICK || pHdr->hdr.code==HDN_ITEMDBLCLICK)
{
pData = GetHandle(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->cToolTipText;
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 TVM_SETIMAGELIST: // Die Image-Liste einstellen
pData = GetHandle(hWnd);
LOCK(pData);
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)SendMessage(pData->hHeader, HDM_SETIMAGELIST, 0, (LPARAM)pData->hImages);
}
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->iStateCheck)
{
ImageList_Destroy(pData->hStates);
}
pData->hStates = (HIMAGELIST)lParam;
if (!pData->hStates)
{
pData->iStatesXsize = 0;
pData->iStatesYsize = 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;
}
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);
switch ((int)wParam)
{
case TVSIL_NORMAL: lRet = (LPARAM)pData->hImages; break;
case TVSIL_STATE: lRet = (LPARAM)pData->hImages; break;
default: lRet = 0;
}
return lRet;
case TVM_GETITEMSTATE: // Die Statusbits eines Eintrags abfragen
pData = GetHandle(hWnd);
LOCK(pData);
if ((unsigned)wParam<=pData->uTreeItemsMax)
{
BaseItem *pEntry;
pEntry = pData->pTreeItems[(unsigned)wParam];
if (!pEntry)lRet = 0;
{
lRet = pEntry->uState;
}
}
else {
lRet = 0;
}
UNLOCK(pData);
return lRet;
case TVM_GETITEM: // Eintr鋑e abfragen
pData = GetHandle(hWnd);
LOCK(pData);
lRet = TreeListGetItem(pData, (TV_ITEM *)lParam);
UNLOCK(pData);
return lRet;
case TVM_SETITEM: // Eintr鋑e einf黦en
pData = GetHandle(hWnd);
LOCK(pData);
lRet = TreeListSetItem(pData, (TV_ITEM *)lParam);
UNLOCK(pData);
return lRet;
case TVM_INSERTITEM: // Eintr鋑e einf黦en
pData = GetHandle(hWnd);
LOCK(pData);
lRet = TreeListInsertItem(pData, (TV_INSERTSTRUCT *)lParam);
UNLOCK(pData);
return lRet;
case TVM_DELETEITEM: // Eintr鋑e l鰏chen
pData = GetHandle(hWnd);
LOCK(pData);
lRet = TreeListDeleteItem(pData, (unsigned)lParam, 1);
UNLOCK(pData);
return lRet;
case TVM_DELETECOLUMN: // L鰏chen einer Salte im Header
pData = GetHandle(hWnd);
LOCK(pData);
lRet = TreeListDeleteColumn(pData, (unsigned)wParam);
UNLOCK(pData);
return lRet;
case TVM_INSERTCOLUMN: // Einf黦en 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 &~(unsigned)wParam;
uVal |= (unsigned)lParam& (unsigned)wParam;
if (pData->uStyleEx!=uVal) // Gab es 膎derungen
{
LOCK(pData);
uChange = pData->uStyleEx^uVal;
pData->uStyleEx = uVal;
if (uChange&TVS_EX_AUTOHSCROLL)
{
UpdateScrollX(pData);
}
if (uChange&TVS_EX_ITEMLINES)
{
UpdateHeight(pData);
UpdateScrollY(pData);
}
if (uChange&(TVS_EX_ALTERNATECOLOR|TVS_EX_SUBSELECT|TVS_EX_MULTISELECT|TVS_EX_FULLROWMARK))
{
UpdateView(pData);
}
if ((uChange&~lParam)&TVS_EX_SUBSELECT)
{
TreeListSelectItem(pData, pData->uSelectedItem, 0, TVC_UNKNOWN);
}
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[(unsigned)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[(unsigned)wParam];
if ((COLORREF)lParam==TV_NOCOLOR) // Standartfarbe einstellen
{
if (pData->cColorChanged[(unsigned)wParam])
{
pData->cColorChanged[(unsigned)wParam] = 0;
UpdateColors(pData);
UpdateView(pData);
}
}
else {
pData->cColorChanged[(unsigned)wParam] = 1;
pData->uColors[(unsigned)wParam] = (COLORREF)lParam;
if (lRet!=lParam)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_SELECTSUBITEM: // Einen (Sub)Eintrag ausw鋒len
pData = GetHandle(hWnd);
LOCK(pData);
if (!(pData->uStyleEx&TVS_EX_SUBSELECT))wParam = 0;
lRet = TreeListSelectItem(pData, (unsigned)lParam, (unsigned)wParam, TVC_UNKNOWN);
if (lRet>1)lRet = 1;
UNLOCK(pData);
return lRet;
case TVM_SELECTDROP: // Den unterstrichenen Eintrags ausw鋒len
pData = GetHandle(hWnd);
LOCK(pData);
lRet = TreeListSetTrackItem(pData, (unsigned)lParam, (unsigned)wParam);
UNLOCK(pData);
return lRet;
case TVM_SELECTITEM: // Einen Eintrag ausw鋒len
pData = GetHandle(hWnd);
LOCK(pData);
switch (wParam)
{
case TVGN_CARET: lRet = TreeListSelectItem(pData, (unsigned)lParam, 0, TVC_UNKNOWN);
if (lRet>1)lRet = 1;
break;
case TVGN_DROPHILITE: lRet = TreeListSetTrackItem(pData, (unsigned)lParam, 0);
break;
case TVGN_FIRSTVISIBLE: lRet = TreeListEnsureVisible(pData, (unsigned)lParam, FIRST_LINE);
lRet = (lRet<0) ? 0 : 1;
default: lRet = 0;
}
UNLOCK(pData);
return lRet;
case TVM_GETCOUNT: // Anzahl der Eintr鋑e abfragen
pData = GetHandle(hWnd);
return pData->uTreeItemsCount;
case TVM_GETINDENT: // Einr點kung abfragen
pData = GetHandle(hWnd);
return pData->iIndent;
case TVM_SETINDENT: // Einr點kung 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鰄e abfragen
pData = GetHandle(hWnd);
return pData->iRowHeight;
case TVM_SETITEMHEIGHT: // Zeilenh鰄e 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->uSizeY-pData->uStartPixel)/pData->iRowHeight;
case TVM_ENSUREVISIBLE: // Einen Eintrag ins Sichtfenster legen
pData = GetHandle(hWnd);
LOCK(pData);
lRet = TreeListEnsureVisible(pData, (unsigned)lParam, (int)wParam);
UNLOCK(pData);
return lRet;
case TVM_GETNEXTITEM: // Einen Eintrag suchen
pData = GetHandle(hWnd);
LOCK(pData);
lRet = TreeListGetNextItem(pData, (unsigned)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, (unsigned)wParam, (RECT *)lParam);
UNLOCK(pData);
return lRet;
case TVM_EXPAND: // Einen Eintrag umklappen
pData = GetHandle(hWnd);
LOCK(pData);
lRet = 0;
if ((unsigned)lParam<=pData->uTreeItemsMax)
{
BaseItem *pEntry;
POINT sPoint;
pEntry = pData->pTreeItems[(unsigned)lParam];
if (pEntry)
{
sPoint.x = 0;
sPoint.y = 0;
switch (wParam&0x0F)
{
case TVE_COLLAPSE: if (pEntry->uState&TVIS_EXPANDED)
{
lRet = (TreeListToggleItem(pData, lParam, 0)) ? 0 : 1;
}
else {
if (pEntry->uState&TVIS_EXPANDPARTIAL)
{
pEntry->uState &= ~TVIS_EXPANDPARTIAL;
UpdateRect(pData, (unsigned)lParam, 0);
}
lRet = 1;
}
if (wParam&TVE_COLLAPSERESET)
{
pEntry->uState &= TVIS_EXPANDEDONCE;
while (pEntry->uLastChild)
{
if (!TreeListDeleteItem(pData, pEntry->uLastChild, 1))break;
pEntry = pData->pTreeItems[(unsigned)lParam];
}
}
break;
case TVE_EXPAND: if (!(pEntry->uState&TVIS_EXPANDED))
{
uVal = (wParam&TVE_EXPANDPARTIAL) ? TVIS_EXPANDPARTIAL : 0;
lRet = (TreeListToggleItem(pData, (unsigned)lParam, uVal)) ? 0 : 1;
}
else {
if (wParam&TVE_EXPANDPARTIAL)
{
if (!(pEntry->uState&TVIS_EXPANDPARTIAL))
{
pEntry->uState |= TVIS_EXPANDPARTIAL;
UpdateRect(pData, (unsigned)lParam, 0);
}
}
else {
if (pEntry->uState&TVIS_EXPANDPARTIAL)
{
pEntry->uState &= ~TVIS_EXPANDPARTIAL;
UpdateRect(pData, (unsigned)lParam, 0);
}
}
lRet = 1;
}
break;
case TVE_TOGGLE: if (pEntry->uState&TVIS_EXPANDPARTIAL)
{
if (pEntry->uState&TVIS_EXPANDED)
{
pEntry->uState &= ~TVIS_EXPANDPARTIAL;
UpdateRect(pData, (unsigned)lParam, 0);
lRet = 1;
break;
}
}
else {
lRet = 1;
}
lRet = (TreeListToggleItem(pData, lParam, 0)) ? 0 : 1;
break;
}
}
}
UNLOCK(pData);
return lRet;
case TVM_SETINSERTMARK: // Einf黦emarke eintellen
pData = GetHandle(hWnd);
LOCK(pData);
lRet = TreeListSetInsertMark(pData, (unsigned)lParam, (int)wParam);
UNLOCK(pData);
return lRet;
case TVM_SETITEMBKCOLOR: // Hintergrundfarbe eines Eintrages 鋘dern
pData = GetHandle(hWnd);
LOCK(pData);
lRet = TreeListSetItemColor(pData, (unsigned)wParam&0xFFFFFF, wParam>>24, (COLORREF)lParam, 0);
UNLOCK(pData);
return lRet;
case TVM_SETITEMTEXTCOLOR: // Textfarbe eines Eintrages 鋘dern
pData = GetHandle(hWnd);
LOCK(pData);
lRet = TreeListSetItemColor(pData, (unsigned)wParam&0xFFFFFF, wParam>>24, (COLORREF)lParam, 1);
UNLOCK(pData);
return lRet;
case TVM_GETITEMBKCOLOR: // Hintergrundfarbe eines Eintrages abfragen
pData = GetHandle(hWnd);
LOCK(pData);
lRet = TreeListGetItemColor(pData, (unsigned)wParam, (unsigned)lParam, 0);
UNLOCK(pData);
return lRet;
case TVM_GETITEMTEXTCOLOR: // Textfarbe eines Eintrages abfragen
pData = GetHandle(hWnd);
LOCK(pData);
lRet = TreeListGetItemColor(pData, (unsigned)wParam, (unsigned)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鋑e
pData = GetHandle(hWnd);
LOCK(pData);
lRet = TreeListSortItems(pData, (unsigned)lParam, (int)wParam);
UNLOCK(pData);
return lRet;
case TVM_GETITEMOFROW: // Hole den Eintrag von einer Reihe
pData = GetHandle(hWnd);
LOCK(pData);
if ((unsigned)lParam<pData->uItemPosCount)
lRet = pData->pItemPos[(unsigned)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_GETROWOFITEM: // Suche Reihe zu einem Eintrag
pData = GetHandle(hWnd);
LOCK(pData);
if ((unsigned)lParam<=pData->uTreeItemsMax)
{
BaseItem *pEntry;
pEntry = pData->pTreeItems[(unsigned)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);
lRet = (LRESULT)TreeListEditLabel(pData, (unsigned)lParam, (unsigned)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黵 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 = 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 && (unsigned)lParam<=pData->uTreeItemsMax)
{
BaseItem *pEntry;
pEntry = pData->pTreeItems[(unsigned)lParam];
if (pEntry)return (LRESULT)(pEntry+1);
}
return 0;
case TVM_SETCOLUMN: // Einen Spalten-Header einstellen
pData = GetHandle(hWnd);
if (pData->hHeader)
{
HDITEM sItem;
TV_COLUMN *pCol = (TV_COLUMN *)lParam;
sItem.mask = 0;
if (pCol->mask&TVCF_FMT) { sItem.mask |= HDI_FORMAT; sItem.fmt = pCol->fmt; }
if (pCol->mask&TVCF_IMAGE) { sItem.mask |= HDI_IMAGE; sItem.iImage = pCol->iImage; }
if (pCol->mask&TVCF_WIDTH) { sItem.mask |= HDI_WIDTH; sItem.cxy = pCol->cx; }
if (pCol->mask&TVCF_TEXT) { sItem.mask |= HDI_TEXT; sItem.pszText = pCol->pszText; sItem.cchTextMax = pCol->cchTextMax; }
lRet = SendMessage(pData->hHeader, HDM_SETITEM, wParam, (LPARAM)&sItem);
}
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;
sItem.mask = 0;
if (pCol->mask&TVCF_FMT) { sItem.mask |= HDI_FORMAT; }
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 (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_SETCOLUMNWIDTH: // Die Spaltenbreite einstellen
pData = GetHandle(hWnd);
if (pData->hHeader)
{
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黵 eine Spalte einstellen
pData = GetHandle(hWnd);
uVal = (wParam>>8)&0xFF;
if (uVal>=pData->uColumnCount)
{
return 0;
}
LOCK(pData);
if ((wParam&7)==TVAE_NONE)
{
wParam = 0;
}
pData->bColumnEdit[uVal] = (BYTE)(wParam);
pData->bColumnCbChar[uVal] = (BYTE)(wParam>>16);
pData->bColumnCbSize[uVal] = (BYTE)(wParam>>24);
pData->pColumnCbData[uVal] = (void *)lParam;
pData->iColumnCbIcon[uVal] = -1;
UNLOCK(pData);
return 1;
case TVM_COLUMNAUTOICON: // Icons f黵 AutoEdit einstellen
pData = GetHandle(hWnd);
uVal = U(wParam);
if (uVal>=pData->uColumnCount || !(pData->bColumnEdit[uVal]&7))
{
return 0;
}
LOCK(pData);
pData->iColumnCbIcon[uVal] = (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, (unsigned)lParam, (unsigned)wParam);
UNLOCK(pData);
return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
//*****************************************************************************
//*
//* TreeListDraw
//*
//*****************************************************************************
// Zeichnet das Fenster
static void TreeListDraw(HWND hWnd, HDC hDc, RECT *pRect)
{
COLORREF uEvColor;
COLORREF uBkColor;
COLORREF uBxColor;
COLORREF uBtColor;
COLORREF uOdColor;
COLORREF uFrColor;
COLORREF uInColor;
COLORREF uOldColor;
COLORREF uOutColor;
COLORREF uTempColor;
HRGN hRgnMain;
HRGN hRgn[MAX_COLUMNS+1];
SIZE sSize;
RECT sRect;
RECT sArea;
RECT sButton;
TreeListData *pData;
BaseItem *pTemp;
BaseItem *pEntry;
ExtraItem *pExtra;
LPCTSTR pText;
unsigned uTextSize;
unsigned uRgnCount;
unsigned uStyleEx;
unsigned uStyle;
unsigned uExtra;
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->iColumnXpos;
hRgnMain = CreateRectRgn(pRect->left, pRect->top, pRect->right, pRect->bottom);
uMax = pData->uColumnCount;
if (!uMax)
{
hRgn[0] = CreateRectRgn(sRect.left, sRect.top, sRect.right, sRect.bottom);
iRnType[0] = CombineRgn(hRgn[0], hRgn[0], hRgnMain, RGN_AND);
uRgnCount = 1;
}
else {
for (uPos = 0; uPos<uMax; uPos++)
{
hRgn[uPos] = CreateRectRgn(sRect.left+pOffsets[uPos]+iXscroll, sRect.top, sRect.left+pOffsets[uPos+1]+iXscroll, sRect.bottom);
iRnType[uPos] = CombineRgn(hRgn[uPos], hRgn[uPos], hRgnMain, RGN_AND);
}
hRgn[uPos] = CreateRectRgn(sRect.left+pOffsets[uPos]+iXscroll, sRect.top, sRect.right, sRect.bottom);
iRnType[uPos] = CombineRgn(hRgn[uPos], hRgn[uPos], hRgnMain, RGN_AND);
uRgnCount = uMax+1;
}
iHeight = pData->iRowHeight;
uStyleEx = pData->uStyleEx;
uStyle = pData->uStyle;
double dScale = getDPIScale();
iIndent = pData->iIndent;
iShift = pData->iShift;
uPos = pData->uScrollY;
uMax = pData->uMaxEnties+uPos;
if (iRnType[0]==NULLREGION)
iMaxX = pData->iMaxSizeX;
else iMaxX = pData->iColumnXpos[1];
if (uStyleEx&TVS_EX_ITEMLINES)
{
iHeight--;
}
if (uMax>pData->uItemPosCount)
{
uMax = pData->uItemPosCount;
}
if (pData->cIsEnabled) // Ist das Fenster gessperrt
{
uBkColor = pData->uColors[TVC_BK];
uOdColor = pData->uColors[TVC_ODD];
uFrColor = pData->uColors[TVC_FRAME];
if (pData->uStyleEx&(TVS_EX_ITEMLINES|TVS_EX_ALTERNATECOLOR))
uEvColor = pData->uColors[TVC_EVEN];
else uEvColor = uBkColor;
}
else {
uBxColor = GetSysColor(COLOR_WINDOW);
uBkColor = GetSysColor(COLOR_SCROLLBAR);
uFrColor = ((((uBkColor)&0xFF)*2 +((uBxColor)&0xFF))/3);
uFrColor |= ((((uBkColor>> 8)&0xFF)*2 +((uBxColor>> 8)&0xFF))/3)<<8;
uFrColor |= ((((uBkColor>>16)&0xFF)*2 +((uBxColor>>16)&0xFF))/3)<<16;
uOdColor = ((((uBkColor>> 1)&0x7F7F7F)+((uFrColor>> 1)&0x7F7F7F)));
uEvColor = uBkColor;
}
uInColor = pData->uColors[TVC_LINE];
uBtColor = pData->uColors[TVC_BOX];
uBxColor = uBkColor;
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鋑e zeichnen ***************************************
for (; uPos<uMax; uPos++) // Alle Eintr鋑e ausgeben
{
uItem = pData->pItemPos[uPos];
pEntry = pData->pTreeItems[uItem];
if (!pEntry)break;
if ((uStyleEx&TVS_EX_FULLROWMARK) && (pEntry->uState&TVIS_SELECTED))
{
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;
}
else if (uStyleEx&TVS_EX_ALTERNATECOLOR) // Farbe wechselweise 鋘dern
{
uOutColor = (uPos&1) ? uOdColor : uEvColor;
uMark = 0xFFFFFFFF;
}
else {
uOutColor = uBxColor;
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)
{
uBits &= ~TVIS_SELECTED;
}
if (pEntry->bCallback)
{
CallbackEntry(pData, pEntry, uItem, pEntry->bCallback, iImage, uTextSize, pText);
pEntry = pData->pTreeItems[uItem];
if (!pEntry)break;
}
SelectObject(hDc, hRgn[0]);
SetBkColor(hDc, uBkColor);
if (pData->cHasRootRow)iLevel++;
if (iLevel<=0)goto NoRootLines;
if (uStyle&(TVS_HASBUTTONS|TVS_HASLINES))
{
iLevel--;
}
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鋍hen
{
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);
}
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鋍hen ?
{
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鋍hen
{
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);
}
if (pEntry->bFlags&TVIX_HASBUTTON) // Schaltfl鋍hen zeichnen
{
int i1(roundToInt(1 * dScale, .5));
int i2(roundToInt(2 * dScale, .5));
int i3(roundToInt(3 * dScale, .5));
int i4(roundToInt(4 * dScale, .5));
int i5(roundToInt(5 * dScale, .5));
sButton.left = iXpos - i4;
sButton.top = iYpos- i4;
sButton.right = iXpos + i5;
sButton.bottom = iYpos + i5;
SetBkColor(hDc, uBtColor);
ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sButton, NULL, 0, NULL);
sButton.left += i1;
sButton.top += i1;
sButton.right -= i1;
sButton.bottom -= i1;
SetBkColor(hDc, uBkColor);
ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sButton, NULL, 0, NULL);
sButton.left = iXpos - i2;
sButton.top = iYpos;
sButton.right = iXpos + i3;
sButton.bottom = iYpos+ i1;
SetBkColor(hDc, uInColor);
ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sButton, NULL, 0, NULL);
// '+' statt '-' Schaltfl鋝che zeichenen
if ((uBits^TVIS_EXPANDED)&(TVIS_EXPANDED|TVIS_EXPANDPARTIAL))
{
sButton.left = iXpos;
sButton.top = iYpos-i2;
sButton.right = iXpos+i1;
sButton.bottom = iYpos+i3;
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鋍hen
{
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;
}
NoRootLines:
if (uStyleEx&TVS_EX_ITEMLINES) // Linien um den Eintrag zeichnen
{
iAdd = 1;
sArea.right = sArea.left+1;
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;
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
{
sArea.right = sArea.left+pData->iImagesXsize * dScale;
iYpos = sArea.top+(iHeight - pData->iImagesYsize * dScale)/2;
pEntry->bFlags |= TVIX_HASIMAGE;
sArea.right += iAdd * dScale;
ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL);
sArea.left += iAdd * dScale;
if (!DrawScaledImage(pData->hImages
, iImage
, hDc
, sArea.left
, iYpos
, ILD_TRANSPARENT|(uBits&(TVIS_OVERLAYMASK|LVIS_CUT))))
{
ImageList_Draw(pData->hImages
, iImage
, hDc
, sArea.left
, iYpos
, ILD_TRANSPARENT|(uBits&(TVIS_OVERLAYMASK|LVIS_CUT)));
}
sArea.left += pData->iImagesXsize * dScale;
iAdd = 0;
}
else {
pEntry->bFlags &= ~TVIX_HASIMAGE;
}
sArea.right = pData->iColumnXpos[1]; // Textausgeben vom Haupteintrag
iYpos = sArea.top+(iHeight-pData->iFontHeight)/2;
if (uBits&(TVIS_SELECTED|TVIS_DROPHILITED|TVIS_UNTERLINE|TVIS_TRACKED|TVIS_TEXTCOLOR|TVIS_FOCUSED))
{ // Das Feld speziel zeichen
TCHAR cTemp[256];
INT aPos[256];
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 = sArea.bottom;
if (!uTextSize) // Felde ohne Text ?
{
sButton.right -= 2;
sButton.bottom--;
pEntry->iTextPixels = 0;
}
else {
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>=sArea.right)
{
if (uTextSize>509)uTextSize = 509;
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, aPos, &sSize);
}
// Tempor鋜en Text mit "..." erzeugen
memcpy(cTemp, pText, iCount*sizeof(TCHAR));
memcpy(cTemp+iCount, _T("..."), 3 *sizeof(TCHAR));
pText = cTemp;
uTextSize = iCount+3;
sButton.right = sArea.right-2;
}
// Das Feld selektiert zeichen
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 ausgew鋒lt
{
if (pData->cHasFocus)
{
uTempColor = GetSysColor(COLOR_HIGHLIGHTTEXT);
SelectObject(hDc, GetSysColorBrush(COLOR_HIGHLIGHT));
SelectObject(hDc, GetStockObject(NULL_PEN));
}
else {
uTempColor = pData->uColors[TVC_TEXT];
if (pData->uStyleEx&TVS_EX_FULLROWMARK)
SelectObject(hDc, GetStockObject(NULL_BRUSH));
else SelectObject(hDc, GetSysColorBrush(COLOR_MENU));
SelectObject(hDc, GetStockObject(NULL_PEN));
}
Rectangle(hDc, sButton.left-2, sButton.top-1, sButton.right+2, sButton.bottom+1);
}
else { // Das Feld normal zeichen
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 zeichen
{
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_UNTERLINE|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]);
}
else { // Das Feld normal ausgeben
if (!pEntry->iTextPixels && uTextSize)
{
sButton.top = iYpos;
sButton.left = sArea.left+4;
sButton.right = sArea.right;
sButton.bottom = sArea.bottom;
// Textbreite berechen
DrawText(hDc, pText, uTextSize, &sButton, DT_LEFT|DT_VCENTER|DT_SINGLELINE|DT_NOPREFIX|DT_CALCRECT);
pEntry->iTextPixels = sButton.right-sButton.left;
}
if (sArea.left+pEntry->iTextPixels>=sArea.right) // Ist der Text gr鲞er als die Spalte
{
INT aPos[256];
if (uTextSize>256)uTextSize = 256;
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, aPos, &sSize);
}
if (iCount>0) // Passen noch Buchstaben vor "..."
{
sButton.right = sArea.right;
sArea.right = sArea.left+2+aPos[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);
}
}
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點kt gegen黚er 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 zeichen
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鋑e zeichnen *********************************
ExtraDraw:
for (uExtra = 1; uExtra<=pData->uColumnCount; uExtra++) // Extra Spalten zeichnen
{
GetRgnBox(hRgn[uExtra], &sButton);
if (iRnType[uExtra]==NULLREGION)continue;
SelectObject(hDc, hRgn[uExtra]);
sArea.left = pData->iColumnXpos[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->iColumnXpos[uExtra+1];
if (uExtra<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->iColumnXpos[uExtra+1];
if (uExtra<pData->uColumnCount) // Ist es die letze Spalte ?
{
sArea.right += iXscroll;
pExtra = pData->pExtraItems[uExtra-1][uItem];
}
else {
pExtra = 0;
if (uExtra<pData->uColumnCount)sArea.right += iXscroll;
}
if (!pExtra) // Leeres Feld zeichen
{
SetBkColor(hDc, uOutColor);
ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL);
}
else {
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 黚er Callback holen
{
CallbackExtra(pData, pEntry, pExtra, uItem, uExtra, pExtra->bCallback, iImage, uTextSize, pText);
pExtra = pData->pExtraItems[uExtra-1][uItem];
if (!pExtra)break;
}
SetBkColor(hDc, (uBits&TVIS_BKCOLOR) ? pExtra->uColorBk : uOutColor);
if (pData->hImages && iImage>TV_NOIMAGE) // Icon zeichnen
{
sArea.right = sArea.left+pData->iImagesXsize+2;
iYpos = sArea.top+(iHeight-pData->iImagesYsize)/2;
pExtra->bFlags |= TVIX_HASIMAGE;
SelectObject(hDc, (uBits&TVIS_BOLD) ? pData->hFontB : pData->hFontN);
ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &sArea, NULL, 0, NULL);
ImageList_Draw(pData->hImages, iImage, hDc, sArea.left+1, iYpos, ILD_TRANSPARENT|(uBits&(TVIS_OVERLAYMASK|LVIS_CUT)));
sArea.left += pData->iImagesXsize+1;
sArea.right = pData->iColumnXpos[uExtra+1];
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_UNTERLINE|TVIS_TRACKED|TVIS_TEXTCOLOR|TVIS_FOCUSED))
{ // Text ausgeben in spezilem Format
TCHAR cTemp[256];
INT aPos[256];
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 = sArea.bottom;
if (!uTextSize)
{
sButton.left--;
sButton.right -= 2;
sButton.bottom--;
pExtra->iTextPixels = 0;
}
else {
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, aPos, &sSize);
}
memcpy(cTemp, pText, iCount*sizeof(TCHAR));
memcpy(cTemp+iCount, _T("..."), 3 *sizeof(TCHAR));
pText = cTemp;
uTextSize = iCount+3;
sButton.right = sArea.right-2;
}
switch (pData->cColumnAlign[uExtra]) // 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 ausgew鋒lt
if ((uBits&TVIS_SELECTED) && pData->cHasFocus && uItem==pData->uSelectedItem && uExtra==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 && uItem==pData->uSelectedItem)
{
if (pData->cHasFocus)
{
uTempColor = GetSysColor(COLOR_HIGHLIGHTTEXT);
SelectObject(hDc, GetSysColorBrush(COLOR_HIGHLIGHT));
SelectObject(hDc, GetStockObject(NULL_PEN));
}
else {
uTempColor = pData->uColors[TVC_TEXT];
if (pData->uStyleEx&TVS_EX_FULLROWMARK)
SelectObject(hDc, GetStockObject(NULL_BRUSH));
else SelectObject(hDc, GetSysColorBrush(COLOR_MENU));
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 zeichen
{
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_UNTERLINE|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]);
}
else { // Den Text ganz normal ausgeben
if (!pExtra->iTextPixels)
{
sButton.top = iYpos;
sButton.left = sArea.left+4;
sButton.right = sArea.right;
sButton.bottom = sArea.bottom;
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 aPos[256];
if (uTextSize>256)uTextSize = 256;
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, aPos, &sSize);
}
if (iCount>0) // Passen noch Buchstaben vor "..."
{
sButton.right = sArea.right;
sArea.right = sArea.left+2+aPos[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);
}
}
else {
switch (pData->cColumnAlign[uExtra]) // 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鋍he ohne Eintr鋑e f黮len
{
sRect.top = sArea.top;
SelectObject(hDc, hRgnMain);
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(hRgn[uPos]);
}
}