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

399 lines
10 KiB
C++

#include "main.h"
#include "./infoWidget.h"
#define INFOWIDGET_OFFSET_LEFT_DLU 4
#define INFOWIDGET_OFFSET_TOP_DLU 2
#define INFOWIDGET_OFFSET_RIGHT_DLU 4
#define INFOWIDGET_OFFSET_BOTTOM_DLU 2
#define INFOWIDGET_MIN_WIDTH_DLU (8*4)
#define INFOWIDGET_MAX_WIDTH_DLU (48*4)
#define INFOWIDGET_TITLE_OFFSET_BOTTOM_DLU 24
#define INFOWIDGET_IMAGE_OFFSET_BOTTOM_DLU 24
typedef struct InfoWidget
{
wchar_t *title;
wchar_t *text;
wchar_t *imagePath;
HBITMAP image;
RECT titleRect;
RECT textRect;
RECT imageRect;
BackBuffer backBuffer;
} InfoWidget;
typedef struct InfoWidgetParam
{
const wchar_t *title;
const wchar_t *text;
const wchar_t *imagePath;
} InfoWidgetParam;
static BOOL
InfoWidget_InitCb(HWND hwnd, void **object, void *param)
{
InfoWidget *self;
const InfoWidgetParam *createParam;
self = (InfoWidget*)malloc(sizeof(InfoWidget));
if (NULL == self)
return FALSE;
ZeroMemory(self, sizeof(InfoWidget));
createParam = (InfoWidgetParam*)param;
if (NULL != createParam)
{
wchar_t buffer[4096] = {0};
if (FALSE != IS_INTRESOURCE(createParam->title))
{
if (NULL != WASABI_API_LNG)
{
WASABI_API_LNGSTRINGW_BUF((int)(INT_PTR)createParam->title, buffer, ARRAYSIZE(buffer));
self->title = String_Duplicate(buffer);
}
else
self->title = NULL;
}
else
self->title = String_Duplicate(createParam->title);
if (FALSE != IS_INTRESOURCE(createParam->text))
{
if (NULL != WASABI_API_LNG)
{
WASABI_API_LNGSTRINGW_BUF((int)(INT_PTR)createParam->text, buffer, ARRAYSIZE(buffer));
self->text = String_Duplicate(buffer);
}
else
self->text = NULL;
}
else
self->text = String_Duplicate(createParam->text);
self->imagePath = ResourceString_Duplicate(createParam->imagePath);
}
BackBuffer_Initialize(&self->backBuffer, hwnd);
*object = self;
return TRUE;
}
static void
InfoWidget_DestroyCb(InfoWidget *self, HWND hwnd)
{
if (NULL == self)
return;
String_Free(self->title);
String_Free(self->text);
ResourceString_Free(self->imagePath);
if (NULL != self->image)
DeleteObject(self->image);
BackBuffer_Uninitialize(&self->backBuffer);
free(self);
}
static HBITMAP
InfoWidget_GetImage(InfoWidget *self, WidgetStyle *style)
{
if (NULL == self->image)
{
unsigned int flags;
flags = IMAGE_FILTER_BLEND;
if (FALSE == IS_INTRESOURCE(self->imagePath))
flags |= ISF_LOADFROMFILE;
self->image = Image_LoadSkinned(self->imagePath, SRC_TYPE_PNG, flags,
0, 0,
WIDGETSTYLE_IMAGE_BACK_COLOR(style),
WIDGETSTYLE_IMAGE_FRONT_COLOR(style),
WIDGETSTYLE_BACK_COLOR(style));
}
return self->image;
}
static BOOL
InfoWidget_GetImageSize(InfoWidget *self, WidgetStyle *style, SIZE *size)
{
HBITMAP image;
BITMAP imageInfo;
image = InfoWidget_GetImage(self, style);
if (NULL == image)
return FALSE;
if (sizeof(imageInfo) != GetObject(image, sizeof(imageInfo), &imageInfo))
return FALSE;
size->cx = imageInfo.bmWidth;
size->cy = imageInfo.bmHeight;
if (size->cy < 0)
size->cy = -size->cy;
return TRUE;
}
static BOOL
InfoWidget_GetTextSize(HDC hdc, HFONT font, const wchar_t *text, long width,
unsigned int format, SIZE *size)
{
RECT rect;
BOOL result;
HFONT prevFont;
if (FALSE != IS_STRING_EMPTY(text))
{
size->cx = 0;
size->cy = 0;
return TRUE;
}
prevFont = SelectFont(hdc, font);
SetRect(&rect, 0, 0, width, 0);
result = DrawText(hdc, text, -1, &rect, DT_CALCRECT | format);
if (FALSE != result)
{
size->cx = RECTWIDTH(rect);
size->cy = RECTHEIGHT(rect);
}
SelectFont(hdc, prevFont);
return result;
}
static long
InfoWidget_GetClientWidth(WidgetStyle *style, long viewWidth)
{
long test;
viewWidth -= (WIDGETSTYLE_DLU_TO_HORZ_PX(style, INFOWIDGET_OFFSET_LEFT_DLU) +
WIDGETSTYLE_DLU_TO_HORZ_PX(style, INFOWIDGET_OFFSET_RIGHT_DLU));
test = WIDGETSTYLE_DLU_TO_HORZ_PX(style, INFOWIDGET_MIN_WIDTH_DLU);
if (viewWidth < test)
return test;
test = WIDGETSTYLE_DLU_TO_HORZ_PX(style, INFOWIDGET_MAX_WIDTH_DLU);
if (viewWidth > test)
return test;
return viewWidth;
}
static void
InfoWidget_LayoutCb(InfoWidget *self, HWND hwnd, WidgetStyle *style,
const RECT *clientRect, SIZE *viewSize, BOOL redraw)
{
HDC windowDC;
LONG offsetX, offsetY;
SIZE widgetSize;
RECT offsetRect;
if (NULL == self || NULL == style)
return;
windowDC = GetDCEx(hwnd, NULL, DCX_CACHE | DCX_NORESETATTRS);
if (NULL == windowDC)
return;
offsetRect.left = WIDGETSTYLE_DLU_TO_HORZ_PX(style, INFOWIDGET_OFFSET_LEFT_DLU);
offsetRect.top = WIDGETSTYLE_DLU_TO_VERT_PX(style, INFOWIDGET_OFFSET_TOP_DLU);
offsetRect.right = WIDGETSTYLE_DLU_TO_HORZ_PX(style, INFOWIDGET_OFFSET_RIGHT_DLU);
offsetRect.bottom = WIDGETSTYLE_DLU_TO_VERT_PX(style, INFOWIDGET_OFFSET_BOTTOM_DLU);
widgetSize.cx = InfoWidget_GetClientWidth(style, RECTWIDTH(*clientRect));
widgetSize.cy = 0;
SetRectEmpty(&self->imageRect);
if (FALSE != InfoWidget_GetImageSize(self, style, ((SIZE*)&self->imageRect) + 1))
{
if (widgetSize.cx < self->imageRect.right)
widgetSize.cx = self->imageRect.right;
widgetSize.cy += self->imageRect.bottom;
if (0 != self->imageRect.bottom)
widgetSize.cy += WIDGETSTYLE_DLU_TO_VERT_PX(style, INFOWIDGET_IMAGE_OFFSET_BOTTOM_DLU);
}
SetRectEmpty(&self->titleRect);
if (FALSE != InfoWidget_GetTextSize(windowDC, style->titleFont, self->title, widgetSize.cx,
DT_LEFT | DT_TOP | DT_NOPREFIX | DT_WORDBREAK, ((SIZE*)&self->titleRect) + 1))
{
widgetSize.cy += self->titleRect.bottom;
if (0 != self->titleRect.bottom)
widgetSize.cy += WIDGETSTYLE_DLU_TO_VERT_PX(style, INFOWIDGET_TITLE_OFFSET_BOTTOM_DLU);
}
SetRectEmpty(&self->textRect);
if (FALSE != InfoWidget_GetTextSize(windowDC, style->textFont, self->text, widgetSize.cx,
DT_LEFT | DT_TOP | DT_NOPREFIX | DT_WORDBREAK, ((SIZE*)&self->textRect) + 1))
{
widgetSize.cy += self->textRect.bottom;
}
if ((widgetSize.cx + (offsetRect.left + offsetRect.right)) < RECTWIDTH(*clientRect))
offsetX = clientRect->left + (RECTWIDTH(*clientRect) - widgetSize.cx)/2;
else
offsetX = clientRect->left + offsetRect.left;
if ((widgetSize.cy + (offsetRect.top + offsetRect.bottom)) < RECTHEIGHT(*clientRect))
offsetY = clientRect->top + (RECTHEIGHT(*clientRect) - widgetSize.cy)/2;
else
offsetY = clientRect->top + offsetRect.top;
if (FALSE == IsRectEmpty(&self->titleRect))
{
OffsetRect(&self->titleRect, offsetX + (widgetSize.cx - self->titleRect.right)/2, offsetY);
offsetY = self->titleRect.bottom;
offsetY += WIDGETSTYLE_DLU_TO_VERT_PX(style, INFOWIDGET_TITLE_OFFSET_BOTTOM_DLU);
}
if (FALSE == IsRectEmpty(&self->imageRect))
{
OffsetRect(&self->imageRect, offsetX + (widgetSize.cx - self->imageRect.right)/2, offsetY);
offsetY = self->imageRect.bottom;
offsetY += WIDGETSTYLE_DLU_TO_VERT_PX(style, INFOWIDGET_IMAGE_OFFSET_BOTTOM_DLU);
}
if (FALSE == IsRectEmpty(&self->textRect))
{
OffsetRect(&self->textRect, offsetX + (widgetSize.cx - self->textRect.right)/2, offsetY);
}
ReleaseDC(hwnd, windowDC);
viewSize->cx = widgetSize.cx + offsetRect.left + offsetRect.right;
viewSize->cy = widgetSize.cy + offsetRect.top + offsetRect.bottom;
}
static BOOL
InfoWidget_PaintCb(InfoWidget *self, HWND hwnd, WidgetStyle *style, HDC hdc, const RECT *paintRect, BOOL erase)
{
RECT intersectRect;
FillRegion fillRegion;
FillRegion_Init(&fillRegion, paintRect);
if (FALSE == IS_STRING_EMPTY(self->title) &&
FALSE != IntersectRect(&intersectRect, &self->titleRect, paintRect))
{
if (FALSE != BackBuffer_DrawTextEx(&self->backBuffer, hdc, self->title, -1, &self->titleRect,
DT_CENTER | DT_NOPREFIX | DT_WORDBREAK,
style->titleFont, style->backColor, style->titleColor, OPAQUE))
{
FillRegion_ExcludeRect(&fillRegion, &intersectRect);
}
}
if (NULL != self->image &&
FALSE != IntersectRect(&intersectRect, &self->imageRect, paintRect))
{
HDC windowDC = GetDCEx(hwnd, NULL, DCX_CACHE | DCX_NORESETATTRS);
if (NULL != windowDC)
{
HDC sourceDC = CreateCompatibleDC(windowDC);
if (NULL != sourceDC)
{
HBITMAP prevBitmap = SelectBitmap(sourceDC, self->image);
if (FALSE != BitBlt(hdc, intersectRect.left, intersectRect.top,
RECTWIDTH(intersectRect), RECTHEIGHT(intersectRect),
sourceDC,
intersectRect.left - self->imageRect.left,
intersectRect.top - self->imageRect.top,
SRCCOPY))
{
FillRegion_ExcludeRect(&fillRegion, &intersectRect);
}
SelectBitmap(sourceDC, prevBitmap);
DeleteDC(sourceDC);
}
ReleaseDC(hwnd, windowDC);
}
}
if (FALSE == IS_STRING_EMPTY(self->text) &&
FALSE != IntersectRect(&intersectRect, &self->textRect, paintRect))
{
if (FALSE != BackBuffer_DrawTextEx(&self->backBuffer, hdc, self->text, -1, &self->textRect,
DT_CENTER | DT_NOPREFIX | DT_WORDBREAK,
style->textFont, style->backColor, style->textColor, OPAQUE))
{
FillRegion_ExcludeRect(&fillRegion, &intersectRect);
}
}
if (FALSE != erase)
FillRegion_BrushFill(&fillRegion, hdc, style->backBrush);
FillRegion_Uninit(&fillRegion);
return TRUE;
}
static void
InfoWidget_StyleColorChangedCb(InfoWidget *self, HWND hwnd, WidgetStyle *style)
{
if (NULL == self)
return;
if (NULL != self->image)
{
if (NULL != self->image)
DeleteObject(self->image);
self->image = NULL;
InfoWidget_GetImage(self, style);
}
}
HWND InfoWidget_CreateWindow(unsigned int type, const wchar_t *title, const wchar_t *text,
const wchar_t *imagePath, HWND parentWindow,
int x, int y, int width, int height, BOOL border, unsigned int controlId)
{
const static WidgetInterface infoWidgetInterface =
{
(WidgetInitCallback)InfoWidget_InitCb,
(WidgetDestroyCallback)InfoWidget_DestroyCb,
(WidgetLayoutCallback)InfoWidget_LayoutCb,
(WidgetPaintCallback)InfoWidget_PaintCb,
(WidgetStyleCallback)InfoWidget_StyleColorChangedCb,
};
InfoWidgetParam param;
param.title = title;
param.text = text;
param.imagePath = imagePath;
return Widget_CreateWindow(type,
&infoWidgetInterface,
NULL,
(FALSE != border) ? WS_EX_CLIENTEDGE : 0,
0,
x, y, width, height,
parentWindow,
controlId, &param);
}