winamp/Src/Plugins/Library/ml_devices/embeddedEditor.cpp
2024-09-24 14:54:57 +02:00

1240 lines
32 KiB
C++

#include "main.h"
#include "./embeddedEditor.h"
#include <vector>
#define EMBEDDEDEDITOR_FRAME_LEFT 1
#define EMBEDDEDEDITOR_FRAME_TOP 1
#define EMBEDDEDEDITOR_FRAME_RIGHT 1
#define EMBEDDEDEDITOR_FRAME_BOTTOM 1
#define EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_LEFT 1
#define EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_TOP 1
#define EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_RIGHT 1
#define EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_BOTTOM 1
#define EMBEDDEDEDITOR_FRAME_INNER_SPACE_LEFT 0
#define EMBEDDEDEDITOR_FRAME_INNER_SPACE_TOP 0
#define EMBEDDEDEDITOR_FRAME_INNER_SPACE_RIGHT 0
#define EMBEDDEDEDITOR_FRAME_INNER_SPACE_BOTTOM 0
#define EMBEDDEDEDITOR_MARGIN_LEFT 4
#define EMBEDDEDEDITOR_MARGIN_TOP 1
#define EMBEDDEDEDITOR_MARGIN_RIGHT 4
#define EMBEDDEDEDITOR_MARGIN_BOTTOM 1
#define EMBEDDEDEDITOR_BORDER_LEFT (EMBEDDEDEDITOR_FRAME_LEFT + \
EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_LEFT + \
EMBEDDEDEDITOR_FRAME_INNER_SPACE_LEFT)
#define EMBEDDEDEDITOR_BORDER_TOP (EMBEDDEDEDITOR_FRAME_TOP + \
EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_TOP + \
EMBEDDEDEDITOR_FRAME_INNER_SPACE_TOP)
#define EMBEDDEDEDITOR_BORDER_RIGHT (EMBEDDEDEDITOR_FRAME_RIGHT + \
EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_RIGHT + \
EMBEDDEDEDITOR_FRAME_INNER_SPACE_RIGHT)
#define EMBEDDEDEDITOR_BORDER_BOTTOM (EMBEDDEDEDITOR_FRAME_BOTTOM + \
EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_BOTTOM + \
EMBEDDEDEDITOR_FRAME_INNER_SPACE_BOTTOM)
typedef struct EmbeddedEditor
{
WNDPROC originalProc;
COLORREF textColor;
COLORREF backColor;
COLORREF borderColor;
HBRUSH backBrush;
HBRUSH borderBrush;
EmbeddedEditorFinishCb callback;
void *user;
POINT anchorPoint;
SIZE maximumSize;
wchar_t *buffer;
size_t bufferSize;
long spacing;
long lineHeight;
} EmbeddedEditor;
typedef std::vector<HWND> WindowList;
typedef struct EmbeddedEditorParent
{
WNDPROC originalProc;
WindowList editorList;
} EmbeddedEditorParent;
typedef struct EmbeddedEditorThread
{
HHOOK hook;
HWND window;
} EmbeddedEditorThread;
static size_t editorTls = ((size_t)-1);
static ATOM EMBEDDEDEDITOR_PROP = 0;
#define EMBEDDEDEDITOR(_hwnd) ((EmbeddedEditor*)GetPropW((_hwnd), MAKEINTATOM(EMBEDDEDEDITOR_PROP)))
#define EMBEDDEDEDITOR_RET_VOID(_self, _hwnd) { (_self) = EMBEDDEDEDITOR((_hwnd)); if (NULL == (_self)) return; }
#define EMBEDDEDEDITOR_RET_VAL(_self, _hwnd, _error) { (_self) = EMBEDDEDEDITOR((_hwnd)); if (NULL == (_self)) return (_error); }
#define EMBEDDEDEDITOR_PARENT(_hwnd) ((EmbeddedEditorParent*)GetPropW((_hwnd), MAKEINTATOM(EMBEDDEDEDITOR_PROP)))
static LRESULT WINAPI
EmbeddedEditor_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
static LRESULT WINAPI
EmbeddedEditorParent_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
static LRESULT CALLBACK
EmbeddidEditorThread_MouseProc(int code, unsigned int messageId, MOUSEHOOKSTRUCT *mouse);
static BOOL
EmbeddidEditorThread_BeginMouseMonitor(HWND editorWindow)
{
EmbeddedEditorThread *threadData;
if (NULL == WASABI_API_APP ||
NULL == editorWindow)
{
return FALSE;
}
if ((size_t)-1 == editorTls)
{
editorTls = WASABI_API_APP->AllocateThreadStorage();
if ((size_t)-1 == editorTls)
return FALSE;
threadData = NULL;
}
else
{
threadData = (EmbeddedEditorThread*)WASABI_API_APP->GetThreadStorage(editorTls);
WASABI_API_APP->SetThreadStorage(editorTls, NULL);
}
if (NULL != threadData)
{
if (NULL != threadData->window)
DestroyWindow(threadData->window);
}
else
{
threadData = (EmbeddedEditorThread*)malloc(sizeof(EmbeddedEditorThread));
if (NULL == threadData)
return FALSE;
threadData->hook = SetWindowsHookEx(WH_MOUSE, (HOOKPROC)EmbeddidEditorThread_MouseProc, NULL, GetCurrentThreadId());
if (NULL == threadData->hook)
{
free(threadData);
return FALSE;
}
}
threadData->window = editorWindow;
WASABI_API_APP->SetThreadStorage(editorTls, threadData);
return TRUE;
}
static void
EmbeddidEditorThread_EndMouseMonitor(HWND editorWindow)
{
EmbeddedEditorThread *threadData;
if (NULL == WASABI_API_APP ||
(size_t)-1 == editorTls ||
NULL == editorWindow)
{
return;
}
threadData = (EmbeddedEditorThread*)WASABI_API_APP->GetThreadStorage(editorTls);
WASABI_API_APP->SetThreadStorage(editorTls, NULL);
if (NULL != threadData)
{
if (NULL != threadData->hook)
UnhookWindowsHookEx(threadData->hook);
free(threadData);
}
}
static BOOL
EmbeddedEditorParent_AddChild(HWND hwnd, HWND child)
{
EmbeddedEditorParent *self;
if (NULL == hwnd || NULL == child)
return FALSE;
self = EMBEDDEDEDITOR_PARENT(hwnd);
if (NULL == self)
{
self = new EmbeddedEditorParent();
if (NULL == self)
return FALSE;
self->originalProc = (WNDPROC)(LONG_PTR)SetWindowLongPtr(hwnd, GWLP_WNDPROC,
(LONGX86)(LONG_PTR)EmbeddedEditorParent_WindowProc);
if (NULL != self->originalProc &&
FALSE == SetProp(hwnd, MAKEINTATOM(EMBEDDEDEDITOR_PROP), self))
{
SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONGX86)(LONG_PTR)self->originalProc);
delete self;
return FALSE;
}
}
else
{
size_t index;
index = self->editorList.size();
while(index--)
{
if (self->editorList[index] == child)
return FALSE;
}
}
self->editorList.push_back(child);
return TRUE;
}
static BOOL
EmbeddedEditorParent_RemoveChild(HWND hwnd, HWND child)
{
size_t index;
EmbeddedEditorParent *self;
if (NULL == hwnd || NULL == child)
return FALSE;
self = EMBEDDEDEDITOR_PARENT(hwnd);
if (NULL == self)
return FALSE;
index = self->editorList.size();
while(index--)
{
if (self->editorList[index] == child)
break;
}
if (((size_t)-1) == index)
return FALSE;
self->editorList.erase(self->editorList.begin() + index);
if (0 == self->editorList.size())
{
RemoveProp(hwnd, MAKEINTATOM(EMBEDDEDEDITOR_PROP));
if (NULL != self->originalProc)
SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONGX86)(LONG_PTR)self->originalProc);
delete self;
}
return TRUE;
}
BOOL
EmbeddedEditor_Attach(HWND hwnd, EmbeddedEditorFinishCb callback, void *user)
{
HWND parent;
EmbeddedEditor *self;
if (NULL == hwnd)
return FALSE;
if (0 == EMBEDDEDEDITOR_PROP)
{
EMBEDDEDEDITOR_PROP = GlobalAddAtom(TEXT("EmdeddedEditorProp"));
if (0 == EMBEDDEDEDITOR_PROP)
return FALSE;
}
self = (EmbeddedEditor*)malloc(sizeof(EmbeddedEditor));
if (NULL == self)
return FALSE;
memset(self, 0, sizeof(EmbeddedEditor));
self->originalProc = (WNDPROC)(LONG_PTR)SetWindowLongPtr(hwnd, GWLP_WNDPROC,
(LONGX86)(LONG_PTR)EmbeddedEditor_WindowProc);
if (NULL != self->originalProc &&
FALSE == SetProp(hwnd, MAKEINTATOM(EMBEDDEDEDITOR_PROP), self))
{
SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONGX86)(LONG_PTR)self->originalProc);
free(self);
return FALSE;
}
self->callback = callback;
self->user = user;
self->backColor = RGB(0, 0, 255); //GetSysColor(COLOR_WINDOW);
self->backBrush = NULL;
self->textColor = RGB(255, 255, 0); //GetSysColor(COLOR_WINDOWTEXT);
self->borderColor = RGB(255, 0, 0); //GetSysColor(COLOR_WINDOWFRAME);
self->borderBrush = NULL;
self->spacing = -1;
self->lineHeight = -1;
parent = GetAncestor(hwnd, GA_PARENT);
if(NULL != parent)
EmbeddedEditorParent_AddChild(parent, hwnd);
EmbeddidEditorThread_BeginMouseMonitor(hwnd);
RECT rect, formatRect;
GetWindowRect(hwnd, &rect);
SetRect(&formatRect, 0, 0, RECTWIDTH(rect) - 6, RECTHEIGHT(rect) - 6);
SendMessage(hwnd, EM_SETRECTNP, 0, (LPARAM)&formatRect);
SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE | SWP_NOMOVE | SWP_FRAMECHANGED);
return TRUE;
}
BOOL
EmbeddedEditor_AdjustWindowRectEx(RECT *rect, unsigned long styleEx, unsigned long style)
{
if (NULL == rect)
return FALSE;
if (0 != ((WS_EX_STATICEDGE | WS_EX_CLIENTEDGE) & styleEx) ||
0 != (WS_BORDER & style))
{
rect->left -= (EMBEDDEDEDITOR_BORDER_LEFT + EMBEDDEDEDITOR_MARGIN_LEFT);
rect->top -= (EMBEDDEDEDITOR_BORDER_TOP + EMBEDDEDEDITOR_MARGIN_TOP);
rect->right += (EMBEDDEDEDITOR_BORDER_RIGHT + EMBEDDEDEDITOR_MARGIN_RIGHT);
rect->bottom += (EMBEDDEDEDITOR_BORDER_BOTTOM + EMBEDDEDEDITOR_MARGIN_BOTTOM);
}
return TRUE;
}
static HBRUSH
EmbeddedEditor_GetBackBrush(EmbeddedEditor *self)
{
if (NULL == self->backBrush)
self->backBrush = CreateSolidBrush(self->backColor);
return self->backBrush;
}
static HBRUSH
EmbeddedEditor_GetBorderBrush(EmbeddedEditor *self)
{
if (NULL == self->borderBrush)
self->borderBrush = CreateSolidBrush(self->borderColor);
return self->borderBrush;
}
static BOOL
EmbdeddedEditor_GetBorderEnabled(HWND hwnd)
{
unsigned long style;
style = GetWindowStyleEx(hwnd);
if (0 != ((WS_EX_STATICEDGE | WS_EX_CLIENTEDGE) & style))
return TRUE;
style = GetWindowStyle(hwnd);
if (0 != (WS_BORDER & style))
return TRUE;
return FALSE;
}
static BOOL
EmbdeddedEditor_DrawBorder(EmbeddedEditor *self, HWND hwnd, HDC hdc)
{
RECT windowRect, clientRect;
HRGN borderRgn;
POINT polygons[16] = {0};
const int polygonsPointCount[] = {4, 4, 4, 4};
if (FALSE == GetWindowRect(hwnd, &windowRect) ||
FALSE == GetClientRect(hwnd, &clientRect))
{
return FALSE;
}
MapWindowPoints(hwnd, HWND_DESKTOP, (POINT*)&clientRect, 2);
OffsetRect(&clientRect, -windowRect.left, -windowRect.top);
OffsetRect(&windowRect, -windowRect.left, -windowRect.top);
MakeRectPolygon(&polygons[0], windowRect.left, windowRect.top, windowRect.right, clientRect.top);
MakeRectPolygon(&polygons[4], clientRect.right, clientRect.top, windowRect.right, clientRect.bottom);
MakeRectPolygon(&polygons[8], windowRect.left, clientRect.bottom, windowRect.right, windowRect.bottom);
MakeRectPolygon(&polygons[12], windowRect.left, clientRect.top, clientRect.left, clientRect.bottom);
borderRgn = CreatePolyPolygonRgn(polygons,
polygonsPointCount,
ARRAYSIZE(polygonsPointCount),
WINDING);
if (NULL != borderRgn)
{
MakeRectPolygon(&polygons[0],
windowRect.left + EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_LEFT,
windowRect.top + EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_TOP,
windowRect.right - EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_RIGHT,
windowRect.top + (EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_TOP + EMBEDDEDEDITOR_FRAME_TOP));
MakeRectPolygon(&polygons[4],
windowRect.right - (EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_RIGHT + EMBEDDEDEDITOR_FRAME_RIGHT),
windowRect.top + (EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_TOP + EMBEDDEDEDITOR_FRAME_TOP),
windowRect.right - EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_RIGHT,
windowRect.bottom - (EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_BOTTOM + EMBEDDEDEDITOR_FRAME_BOTTOM));
MakeRectPolygon(&polygons[8],
windowRect.left + EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_LEFT,
windowRect.bottom - (EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_BOTTOM + EMBEDDEDEDITOR_FRAME_BOTTOM),
windowRect.right - EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_RIGHT,
windowRect.bottom - EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_BOTTOM);
MakeRectPolygon(&polygons[12],
windowRect.left + EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_LEFT,
windowRect.top + (EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_TOP + EMBEDDEDEDITOR_FRAME_TOP),
windowRect.left + (EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_LEFT + EMBEDDEDEDITOR_FRAME_LEFT),
windowRect.bottom - (EMBEDDEDEDITOR_FRAME_OUTTER_SPACE_BOTTOM + EMBEDDEDEDITOR_FRAME_BOTTOM));
HRGN frameRgn = CreatePolyPolygonRgn(polygons,
polygonsPointCount,
ARRAYSIZE(polygonsPointCount),
WINDING);
if (NULL != frameRgn)
{
int combineResult = CombineRgn(frameRgn, borderRgn, frameRgn, RGN_AND);
if (NULLREGION != combineResult && ERROR != combineResult)
{
FillRgn(hdc, frameRgn, EmbeddedEditor_GetBorderBrush(self));
combineResult = CombineRgn(borderRgn, borderRgn, frameRgn, RGN_DIFF);
}
else
combineResult = COMPLEXREGION;
if (NULLREGION != combineResult && ERROR != combineResult)
FillRgn(hdc, borderRgn, EmbeddedEditor_GetBackBrush(self));
DeleteObject(frameRgn);
}
DeleteObject(borderRgn);
}
return TRUE;
}
static BOOL
EmbeddedEditor_InvokeCallback(EmbeddedEditor *self, HWND hwnd, BOOL cancelMode, BOOL removeCallback)
{
wchar_t *text;
unsigned int length;
EmbeddedEditorFinishCb callback;
if (NULL == self || NULL == self->callback)
return FALSE;
length = (unsigned int)SendMessage(hwnd, WM_GETTEXTLENGTH, 0, 0L);
text = String_Malloc(++length);
if (NULL == text)
return FALSE;
SendMessage(hwnd, WM_GETTEXT, (WPARAM)length, (LPARAM)text);
callback = self->callback;
if (FALSE != removeCallback)
self->callback = NULL;
callback(hwnd, cancelMode, text, self->user);
return TRUE;
}
static const wchar_t *
EmbeddedEditor_GetWindowText(EmbeddedEditor *self, HWND hwnd, size_t *lengthOut)
{
size_t length;
length = (size_t)SendMessage(hwnd, WM_GETTEXTLENGTH, 0, 0L);
if (length >= self->bufferSize)
{
size_t size;
size = (((length + 1)/128) + 1) * 128;
String_Free(self->buffer);
self->buffer = String_Malloc(size);
if (NULL == self->buffer)
return FALSE;
self->bufferSize = size;
}
if (0 != length)
length = SendMessage(hwnd, WM_GETTEXT, (WPARAM)self->bufferSize, (LPARAM)self->buffer);
else
self->buffer[0] = L'\0';
if (NULL != lengthOut)
*lengthOut = length;
return self->buffer;
}
static BOOL
EmbeddedEditor_Resize(EmbeddedEditor *self, HWND hwnd, BOOL redraw)
{
size_t textLength;
const wchar_t *text;
HDC hdc;
SIZE maximumSize;
BOOL borderEnabled;
HWND parentWindow;
RECT rect, parentRect, marginRect;
SIZE parentSize;
BOOL result;
unsigned int windowStyle;
HFONT font, prevFont;
parentWindow = GetAncestor(hwnd, GA_PARENT);
if (NULL == parentWindow ||
FALSE == GetClientRect(parentWindow, &parentRect))
{
return FALSE;
}
parentSize.cx = RECTWIDTH(parentRect);
parentSize.cy = RECTHEIGHT(parentRect);
windowStyle = GetWindowStyle(hwnd);
borderEnabled = EmbdeddedEditor_GetBorderEnabled(hwnd);
text = EmbeddedEditor_GetWindowText(self, hwnd, &textLength);
if (NULL == text)
return FALSE;
hdc = GetDCEx(hwnd, NULL, DCX_CACHE | DCX_NORESETATTRS);
if (NULL == hdc)
return FALSE;
result = FALSE;
font = (HFONT)SendMessage(hwnd, WM_GETFONT, 0, 0L);
prevFont = SelectFont(hdc, font);
if (-1 == self->spacing ||
-1 == self->lineHeight)
{
TEXTMETRIC textMetrics;
if (FALSE != GetTextMetrics(hdc, &textMetrics))
{
self->spacing = textMetrics.tmAveCharWidth;
self->lineHeight = textMetrics.tmHeight;
}
}
if (0 == (ES_MULTILINE & windowStyle))
{
unsigned long margins;
margins = (unsigned long)SendMessage(hwnd, EM_GETMARGINS, 0, 0L);
SetRect(&marginRect, (short)LOWORD(margins), 0, (short)HIWORD(margins), 0);
}
else
{
RECT clientRect;
if (FALSE == GetClientRect(hwnd, &clientRect))
SetRectEmpty(&clientRect);
if (clientRect.right < clientRect.left)
clientRect.right = clientRect.left;
if (clientRect.bottom < (clientRect.top + self->lineHeight))
clientRect.bottom = clientRect.top + self->lineHeight;
SendMessage(hwnd, EM_GETRECT, 0, (LPARAM)&marginRect);
marginRect.left = (marginRect.left > clientRect.left) ?
(marginRect.left - clientRect.left) : 1;
marginRect.top = (marginRect.top > clientRect.top) ?
(marginRect.top - clientRect.top) : 1;
marginRect.right = (marginRect.right < clientRect.right) ?
(clientRect.right - marginRect.right) : 1;
marginRect.bottom = (marginRect.bottom < clientRect.bottom) ?
(clientRect.bottom - marginRect.bottom) : 1;
if (SendMessage(hwnd, EM_GETLINECOUNT, 0, 0L) > 1)
{
if (marginRect.bottom >= (self->lineHeight - 1))
marginRect.bottom -= (self->lineHeight - 2);
}
SetRect(&marginRect, EMBEDDEDEDITOR_MARGIN_LEFT, EMBEDDEDEDITOR_MARGIN_TOP,
EMBEDDEDEDITOR_MARGIN_RIGHT, EMBEDDEDEDITOR_MARGIN_BOTTOM);
}
maximumSize.cx = (self->maximumSize.cx > 0) ? MIN(parentSize.cx, self->maximumSize.cx) : parentSize.cx;
maximumSize.cx -= (marginRect.left + marginRect.right);
if (FALSE != borderEnabled)
maximumSize.cx -= (EMBEDDEDEDITOR_BORDER_LEFT + EMBEDDEDEDITOR_BORDER_RIGHT);
if (maximumSize.cx < 0)
maximumSize.cx = 0;
maximumSize.cy = (self->maximumSize.cy > 0) ? MIN(parentSize.cy, self->maximumSize.cy) : parentSize.cy;
maximumSize.cy -= (marginRect.top + marginRect.bottom);
if (FALSE != borderEnabled)
maximumSize.cy -= (EMBEDDEDEDITOR_BORDER_TOP + EMBEDDEDEDITOR_BORDER_BOTTOM);
if (maximumSize.cy < 0)
maximumSize.cy = 0;
if (0 == textLength)
{
SetRectEmpty(&rect);
result = TRUE;
}
else
{
SetRect(&rect, 0, 0, maximumSize.cx, maximumSize.cy);
result = DrawText(hdc, text, (int)textLength, &rect,
DT_CALCRECT | DT_LEFT | DT_TOP | DT_WORDBREAK |
DT_EDITCONTROL | DT_NOPREFIX);
}
SelectFont(hdc, prevFont);
ReleaseDC(hwnd, hdc);
if (FALSE != result)
{
unsigned int flags;
rect.right += 2 * self->spacing;
if (RECTHEIGHT(rect) < self->lineHeight)
rect.bottom = rect.top + self->lineHeight;
else if (RECTHEIGHT(rect) > self->lineHeight)
rect.right = rect.left + maximumSize.cx;
rect.right += (marginRect.left + marginRect.right);
rect.bottom += (marginRect.top + marginRect.bottom);
if (FALSE != borderEnabled)
{
rect.right += (EMBEDDEDEDITOR_BORDER_LEFT + EMBEDDEDEDITOR_BORDER_RIGHT);
rect.bottom += (EMBEDDEDEDITOR_BORDER_TOP + EMBEDDEDEDITOR_BORDER_BOTTOM);
}
flags = SWP_NOACTIVATE | SWP_NOZORDER;
if (FALSE == redraw)
flags |= SWP_NOREDRAW;
OffsetRect(&rect, self->anchorPoint.x, self->anchorPoint.y);
long offsetX = 0, offsetY = 0;
if (0 != (ES_CENTER & windowStyle))
{
if (self->maximumSize.cx > 0)
{
offsetX += (self->maximumSize.cx - RECTWIDTH(rect))/2;
}
}
if (rect.right > parentSize.cx)
offsetX -= (rect.right - parentSize.cx);
if (rect.bottom > parentSize.cy)
offsetY -= (rect.bottom - parentSize.cy);
OffsetRect(&rect, offsetX, offsetY);
result = SetWindowPos(hwnd, NULL, rect.left, rect.top, RECTWIDTH(rect), RECTHEIGHT(rect), flags);
}
return result;
}
static void
EmbdeddedEditor_OnDestroy(EmbeddedEditor *self, HWND hwnd)
{
HWND parent;
if (NULL != self &&
NULL != self->callback)
{
EmbeddedEditor_InvokeCallback(self, hwnd, TRUE, TRUE);
}
RemoveProp(hwnd, MAKEINTATOM(EMBEDDEDEDITOR_PROP));
if (NULL == self)
return;
if (NULL != self->originalProc)
{
SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONGX86)(LONG_PTR)self->originalProc);
CallWindowProc(self->originalProc, hwnd, WM_DESTROY, 0, 0L);
}
if(NULL != self->backBrush)
DeleteObject(self->backBrush);
if (NULL != self->borderBrush)
DeleteObject(self->borderBrush);
String_Free(self->buffer);
free(self);
parent = GetAncestor(hwnd, GA_PARENT);
if(NULL != parent)
EmbeddedEditorParent_RemoveChild(parent, hwnd);
EmbeddidEditorThread_EndMouseMonitor(hwnd);
}
static BOOL
EmbdeddedEditor_OnNcCalcSize(EmbeddedEditor *self, HWND hwnd, BOOL calcValidRects,
NCCALCSIZE_PARAMS *params, LRESULT *result)
{
if (FALSE != EmbdeddedEditor_GetBorderEnabled(hwnd))
{
if (FALSE != calcValidRects)
{
SetRect(&params->rgrc[0],
params->lppos->x + EMBEDDEDEDITOR_BORDER_LEFT,
params->lppos->y + EMBEDDEDEDITOR_BORDER_TOP,
params->lppos->x + params->lppos->cx - EMBEDDEDEDITOR_BORDER_RIGHT,
params->lppos->y + params->lppos->cy - EMBEDDEDEDITOR_BORDER_BOTTOM);
*result = WVR_ALIGNTOP | WVR_ALIGNLEFT;
}
else
{
if (FALSE != GetWindowRect(hwnd, &params->rgrc[0]))
{
params->rgrc[0].left += EMBEDDEDEDITOR_BORDER_LEFT;
params->rgrc[0].top += EMBEDDEDEDITOR_BORDER_TOP;
params->rgrc[0].right -= EMBEDDEDEDITOR_BORDER_RIGHT;
params->rgrc[0].bottom -= EMBEDDEDEDITOR_BORDER_BOTTOM;
}
}
}
return TRUE;
}
static BOOL
EmbdeddedEditor_OnNcPaint(EmbeddedEditor *self, HWND hwnd, HRGN updateRegion)
{
BOOL result;
if (FALSE != EmbdeddedEditor_GetBorderEnabled(hwnd))
{
HDC hdc;
unsigned int flags;
flags = DCX_PARENTCLIP | DCX_CACHE | DCX_WINDOW | DCX_CLIPSIBLINGS |
DCX_INTERSECTUPDATE | DCX_VALIDATE;
hdc = GetDCEx(hwnd, ((HRGN)NULLREGION != updateRegion) ? updateRegion : NULL, flags);
if (NULL != hdc)
{
result = EmbdeddedEditor_DrawBorder(self, hwnd, hdc);
ReleaseDC(hwnd, hdc);
}
else
result = FALSE;
}
else
result = TRUE;
return result;
}
static BOOL
EmbeddedEditor_OnSetCursor(EmbeddedEditor *self, HWND hwnd, HWND cursorWindow, int hitTest, int mouseMessage, LRESULT *result)
{
HCURSOR cursor;
if (cursorWindow != hwnd ||
HTCLIENT != hitTest)
{
return FALSE;
}
cursor = LoadCursor(NULL, IDC_IBEAM);
if (NULL == cursor)
return FALSE;
SetCursor(cursor);
*result = TRUE;
return TRUE;
}
static BOOL
EmbeddedEditor_OnGetDlgCode(EmbeddedEditor *self, HWND hwnd, unsigned int vKey, MSG *message, LRESULT *result)
{
if (NULL != message)
{
switch(vKey)
{
case VK_TAB:
case VK_ESCAPE:
EmbeddedEditor_InvokeCallback(self, hwnd, TRUE, TRUE);
DestroyWindow(hwnd);
*result = DLGC_WANTMESSAGE;
return TRUE;
case VK_RETURN:
EmbeddedEditor_InvokeCallback(self, hwnd, FALSE, TRUE);
DestroyWindow(hwnd);
*result = DLGC_WANTMESSAGE;
return TRUE;
}
}
if (NULL != self->originalProc)
*result = CallWindowProc(self->originalProc, hwnd, WM_GETDLGCODE, (WPARAM)vKey, (LPARAM)message);
else
*result = 0;
if (NULL == message)
*result |= DLGC_WANTMESSAGE;
return TRUE;
}
static BOOL
EmbeddedEditor_OnKillFocus(EmbeddedEditor *self, HWND hwnd, HWND focusedWindow)
{
EmbeddedEditor_InvokeCallback(self, hwnd, FALSE, TRUE);
DestroyWindow(hwnd);
return TRUE;
}
static BOOL
EmbeddedEditor_OnMouseWheel(EmbeddedEditor *self, HWND hwnd, int virtualKeys, int distance, long pointer_s)
{
HWND parentWindow;
parentWindow = GetAncestor(hwnd, GA_PARENT);
EmbeddedEditor_InvokeCallback(self, hwnd, TRUE, TRUE);
DestroyWindow(hwnd);
if (NULL != parentWindow)
{
SendMessage(parentWindow, WM_MOUSEWHEEL,
MAKEWPARAM(virtualKeys, distance), (LPARAM)pointer_s);
}
return TRUE;
}
static void
EmbeddedEditor_OnCommand(EmbeddedEditor *self, HWND hwnd, int eventId)
{
switch(eventId)
{
case EN_UPDATE:
EmbeddedEditor_Resize(self, hwnd, TRUE);
break;
}
}
static BOOL
EmbeddedEditor_OnSetFont(EmbeddedEditor *self, HWND hwnd, HFONT font, BOOL redraw)
{
if (NULL != self->originalProc)
CallWindowProc(self->originalProc, hwnd, WM_SETFONT, (WPARAM)font, MAKELPARAM(redraw, 0));
self->spacing = -1;
self->lineHeight = -1;
EmbeddedEditor_Resize(self, hwnd, redraw);
return TRUE;
}
static BOOL
EmbeddedEditor_OnWindowPosChanged(EmbeddedEditor *self, HWND hwnd, WINDOWPOS *pwp)
{
if (SWP_NOSIZE != ((SWP_NOSIZE | SWP_FRAMECHANGED) & pwp->flags))
{
RECT formatRect;
if (FALSE != GetClientRect(hwnd, &formatRect))
{
formatRect.left += EMBEDDEDEDITOR_MARGIN_LEFT;
formatRect.top += EMBEDDEDEDITOR_MARGIN_TOP;
formatRect.right -= EMBEDDEDEDITOR_MARGIN_RIGHT;
formatRect.bottom -= EMBEDDEDEDITOR_MARGIN_BOTTOM;
if (formatRect.right < formatRect.left)
formatRect.right = formatRect.left;
if (formatRect.bottom < formatRect.top)
formatRect.bottom = formatRect.top;
SendMessage(hwnd, EM_SETRECTNP, 0, (LPARAM)&formatRect);
}
}
if (NULL != self->originalProc)
CallWindowProc(self->originalProc, hwnd, WM_WINDOWPOSCHANGED, 0, (LPARAM)pwp);
return TRUE;
}
static void
EmbeddedEditor_OnSetTextColor(EmbeddedEditor *self, HWND hwnd, COLORREF color, LRESULT *result)
{
*result = (LRESULT)self->textColor;
if (self->textColor != color)
self->textColor = color;
}
static void
EmbeddedEditor_OnGetTextColor(EmbeddedEditor *self, HWND hwnd, LRESULT *result)
{
*result = (LRESULT)self->textColor;
}
static void
EmbeddedEditor_OnSetBackColor(EmbeddedEditor *self, HWND hwnd, COLORREF color, LRESULT *result)
{
*result = (LRESULT)self->backColor;
if (self->backColor != color)
{
self->backColor = color;
if (NULL != self->backBrush)
{
DeleteObject(self->backBrush);
self->backBrush = NULL;
}
}
}
static void
EmbeddedEditor_OnGetBackColor(EmbeddedEditor *self, HWND hwnd, LRESULT *result)
{
*result = (LRESULT)self->backColor;
}
static void
EmbeddedEditor_OnSetBorderColor(EmbeddedEditor *self, HWND hwnd, COLORREF color, LRESULT *result)
{
*result = (LRESULT)self->borderColor;
if (self->borderColor != color)
{
self->borderColor = color;
if (NULL != self->borderBrush)
{
DeleteObject(self->borderBrush);
self->borderBrush = NULL;
}
}
}
static void
EmbeddedEditor_OnGetBorderColor(EmbeddedEditor *self, HWND hwnd, LRESULT *result)
{
*result = (LRESULT)self->borderColor;
}
static void
EmbeddedEditor_OnSetUserData(EmbeddedEditor *self, HWND hwnd, void *user, LRESULT *result)
{
*result = (LRESULT)self->user;
self->user = user;
}
static void
EmbeddedEditor_OnGetUserData(EmbeddedEditor *self, HWND hwnd, LRESULT *result)
{
*result = (LRESULT)self->user;
}
static void
EmbeddedEditor_OnSetAnchorPoint(EmbeddedEditor *self, HWND hwnd, long x, long y, LRESULT *result)
{
self->anchorPoint.x = x;
self->anchorPoint.y = y;
*result = TRUE;
}
static void
EmbeddedEditor_OnGetAnchorPoint(EmbeddedEditor *self, HWND hwnd, long *x, long *y, LRESULT *result)
{
if (NULL != x)
*x = self->anchorPoint.x;
if (NULL != y)
*y = self->anchorPoint.y;
*result = TRUE;
}
static void
EmbeddedEditor_OnSetMaxSize(EmbeddedEditor *self, HWND hwnd, long width, long height, LRESULT *result)
{
self->maximumSize.cx = width;
self->maximumSize.cy = height;
*result = TRUE;
}
static void
EmbeddedEditor_OnGetMaxSize(EmbeddedEditor *self, HWND hwnd, long *width, long *height, LRESULT *result)
{
if (NULL != width)
*width = self->maximumSize.cx;
if (NULL != height)
*height = self->maximumSize.cy;
*result = TRUE;
}
static void
EmbeddedEditor_OnEndEditing(EmbeddedEditor *self, HWND hwnd, BOOL cancel)
{
EmbeddedEditor_InvokeCallback(self, hwnd, cancel, TRUE);
DestroyWindow(hwnd);
}
static BOOL
EmbeddedEditor_MessageProc(EmbeddedEditor *self, HWND hwnd, UINT uMsg,
WPARAM wParam, LPARAM lParam, LRESULT *result)
{
switch(uMsg)
{
case WM_DESTROY:
EmbdeddedEditor_OnDestroy(self, hwnd);
return TRUE;
case WM_NCCALCSIZE:
return EmbdeddedEditor_OnNcCalcSize(self, hwnd, (BOOL)wParam, (NCCALCSIZE_PARAMS*)lParam, result);
case WM_NCPAINT:
return EmbdeddedEditor_OnNcPaint(self, hwnd, (HRGN)wParam);
case WM_SETCURSOR:
return EmbeddedEditor_OnSetCursor(self, hwnd, (HWND)wParam,
LOWORD(lParam), HIWORD(lParam), result);
case WM_GETDLGCODE:
return EmbeddedEditor_OnGetDlgCode(self, hwnd, (unsigned int)wParam, (MSG*)lParam, result);
case WM_KILLFOCUS:
EmbeddedEditor_OnKillFocus(self, hwnd, (HWND)wParam);
return 0;
case WM_MOUSEWHEEL:
return EmbeddedEditor_OnMouseWheel(self, hwnd, LOWORD(wParam), (short)HIWORD(wParam), (LONG)lParam);
case WM_SETFONT:
return EmbeddedEditor_OnSetFont(self, hwnd, (HFONT)wParam, (BOOL)LOWORD(lParam));
case WM_WINDOWPOSCHANGED:
return EmbeddedEditor_OnWindowPosChanged(self, hwnd, (WINDOWPOS*)lParam);
case EMBEDDEDEDITOR_WM_SET_TEXT_COLOR: EmbeddedEditor_OnSetTextColor(self, hwnd, (COLORREF)lParam, result); return TRUE;
case EMBEDDEDEDITOR_WM_GET_TEXT_COLOR: EmbeddedEditor_OnGetTextColor(self, hwnd, result); return TRUE;
case EMBEDDEDEDITOR_WM_SET_BACK_COLOR: EmbeddedEditor_OnSetBackColor(self, hwnd, (COLORREF)lParam, result); return TRUE;
case EMBEDDEDEDITOR_WM_GET_BACK_COLOR: EmbeddedEditor_OnGetBackColor(self, hwnd, result); return TRUE;
case EMBEDDEDEDITOR_WM_SET_BORDER_COLOR: EmbeddedEditor_OnSetBorderColor(self, hwnd, (COLORREF)lParam, result); return TRUE;
case EMBEDDEDEDITOR_WM_GET_BORDER_COLOR: EmbeddedEditor_OnGetBorderColor(self, hwnd, result); return TRUE;
case EMBEDDEDEDITOR_WM_SET_USER_DATA: EmbeddedEditor_OnSetUserData(self, hwnd, (void*)lParam, result); return TRUE;
case EMBEDDEDEDITOR_WM_GET_USER_DATA: EmbeddedEditor_OnGetUserData(self, hwnd, result); return TRUE;
case EMBEDDEDEDITOR_WM_SET_ANCHOR_POINT: EmbeddedEditor_OnSetAnchorPoint(self, hwnd, (long)wParam, (long)lParam, result); return TRUE;
case EMBEDDEDEDITOR_WM_GET_ANCHOR_POINT: EmbeddedEditor_OnGetAnchorPoint(self, hwnd, (long*)wParam, (long*)lParam, result); return TRUE;
case EMBEDDEDEDITOR_WM_SET_MAX_SIZE: EmbeddedEditor_OnSetMaxSize(self, hwnd, (long)wParam, (long)lParam, result); return TRUE;
case EMBEDDEDEDITOR_WM_GET_MAX_SIZE: EmbeddedEditor_OnGetMaxSize(self, hwnd, (long*)wParam, (long*)lParam, result); return TRUE;
case EMBEDDEDEDITOR_WM_END_EDITING: EmbeddedEditor_OnEndEditing(self, hwnd, (BOOL)wParam); return TRUE;
}
return FALSE;
}
static LRESULT WINAPI
EmbeddedEditor_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
EmbeddedEditor *self;
LRESULT result;
self = EMBEDDEDEDITOR(hwnd);
if (NULL == self ||
NULL == self->originalProc)
{
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
result = 0;
if (FALSE != EmbeddedEditor_MessageProc(self, hwnd, uMsg, wParam, lParam, &result))
{
return result;
}
return CallWindowProc(self->originalProc, hwnd, uMsg, wParam, lParam);
}
static void
EmbdeddedEditorParent_OnDestroy(EmbeddedEditorParent *self, HWND hwnd)
{
RemoveProp(hwnd, MAKEINTATOM(EMBEDDEDEDITOR_PROP));
if (NULL == self)
return;
if (NULL != self->originalProc)
{
SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONGX86)(LONG_PTR)self->originalProc);
CallWindowProc(self->originalProc, hwnd, WM_DESTROY, 0, 0L);
}
delete self;
}
static BOOL
EmbdeddedEditorParent_OnGetEditColors(EmbeddedEditorParent *self, HWND hwnd, HDC hdc, HWND control, LRESULT *result)
{
size_t index;
index = self->editorList.size();
while(index--)
{
if (control == self->editorList[index])
{
EmbeddedEditor *editor = EMBEDDEDEDITOR(control);
if (NULL != editor)
{
SetTextColor(hdc, editor->textColor);
SetBkColor(hdc, editor->backColor);
*result = (LRESULT)EmbeddedEditor_GetBackBrush(editor);
return TRUE;
}
}
}
return FALSE;
}
static void
EmbdeddedEditorParent_OnCommand(EmbeddedEditorParent *self, HWND hwnd, int commandId, int eventId, HWND control)
{
size_t index;
if (NULL == control)
return;
index = self->editorList.size();
while(index--)
{
if (control == self->editorList[index])
{
EmbeddedEditor *editor = EMBEDDEDEDITOR(control);
if (NULL != editor)
{
EmbeddedEditor_OnCommand(editor, control, eventId);
}
}
}
}
static BOOL
EmbeddedEditorParent_MessageProc(EmbeddedEditorParent *self, HWND hwnd, UINT uMsg,
WPARAM wParam, LPARAM lParam, LRESULT *result)
{
switch(uMsg)
{
case WM_DESTROY:
EmbdeddedEditorParent_OnDestroy(self, hwnd);
return TRUE;
case WM_CTLCOLOREDIT:
return EmbdeddedEditorParent_OnGetEditColors(self, hwnd, (HDC)wParam, (HWND)lParam, result);
case WM_COMMAND:
EmbdeddedEditorParent_OnCommand(self, hwnd, LOWORD(wParam), HIWORD(wParam), (HWND)lParam);
break;
}
return FALSE;
}
static LRESULT WINAPI
EmbeddedEditorParent_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
EmbeddedEditorParent *self;
LRESULT result;
self = EMBEDDEDEDITOR_PARENT(hwnd);
if (NULL == self ||
NULL == self->originalProc)
{
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
result = 0;
if (FALSE != EmbeddedEditorParent_MessageProc(self, hwnd, uMsg, wParam, lParam, &result))
{
return result;
}
return CallWindowProc(self->originalProc, hwnd, uMsg, wParam, lParam);
}
static LRESULT CALLBACK
EmbeddidEditorThread_MouseProc(int code, unsigned int messageId, MOUSEHOOKSTRUCT *mouse)
{
if ((size_t)-1 != editorTls)
{
EmbeddedEditorThread *threadData;
threadData = (EmbeddedEditorThread*)WASABI_API_APP->GetThreadStorage(editorTls);
if (NULL != threadData)
{
LRESULT result;
if (NULL != threadData->hook)
result = CallNextHookEx(threadData->hook, code, (WPARAM)messageId, (LPARAM)mouse);
else
result = 0;
if (code >= 0)
{
if ((messageId >= WM_LBUTTONDOWN && messageId <= 0x20E && mouse->hwnd != threadData->window) ||
(messageId >= WM_NCLBUTTONDOWN && messageId <= 0x00AD))
{
HWND editorWindow;
editorWindow = threadData->window;
if (NULL != editorWindow)
PostMessage(editorWindow, EMBEDDEDEDITOR_WM_END_EDITING, FALSE, 0L);
}
}
return result;
}
}
return 0;
}