winamp/Src/Winamp/VideoOSD.cpp
2024-09-24 14:54:57 +02:00

538 lines
14 KiB
C++

#include "Main.h"
#include "VideoOSD.h"
#include "resource.h"
#include "draw.h"
#include "../nu/AutoChar.h"
#define OSD_TEXT_R 192
#define OSD_TEXT_G 192
#define OSD_TEXT_B 192
#define OSD_TEXT_R_HILITE 255
#define OSD_TEXT_G_HILITE 255
#define OSD_TEXT_B_HILITE 255
#define OSD_VOL_COL_R 0
#define OSD_VOL_COL_G 0
#define OSD_VOL_COL_B 192
#define OSD_VOL_BKCOL_R 0
#define OSD_VOL_BKCOL_G 0
#define OSD_VOL_BKCOL_B 64
#define CTRL_PROGRESSTEXT 0
#define CTRL_PROGRESS 1
#define CTRL_PROGRESSSPACER 2
#define CTRL_REW 3
#define CTRL_PLAY 4
#define CTRL_PAUSE 5
#define CTRL_STOP 6
#define CTRL_FFWD 7
#define CTRL_VOLSPACER 8
#define CTRL_VOLTEXT 9
#define CTRL_VOL 10
#define CTRLTYPE_SYMBOL 0
#define CTRLTYPE_TEXT 1
#define CTRLTYPE_PROGRESS 2
#define CTRLTYPE_SPACER 3
IVideoOSD *temp;
int g_ctrl_type[ NUM_WIDGETS ] =
{
CTRLTYPE_TEXT,
CTRLTYPE_PROGRESS,
CTRLTYPE_SPACER,
CTRLTYPE_SYMBOL,
CTRLTYPE_SYMBOL,
CTRLTYPE_SYMBOL,
CTRLTYPE_SYMBOL,
CTRLTYPE_SYMBOL,
CTRLTYPE_SPACER,
CTRLTYPE_TEXT,
CTRLTYPE_PROGRESS
};
#define SHOW_STREAM_TITLE_AT_TOP 1
char progStr[64] = {0}, volStr[64] = {0};
const char *g_ctrl_text[NUM_WIDGETS] =
{
progStr/*"Progress "*/,
"",
"",
"7", // rew
"4", // play
";", // pause
"<", // stop
"8", // ffwd
"",
volStr/*"Volume "*/,
""
};
int g_ctrl_force_width[NUM_WIDGETS] =
{
0,
256/*96*/, // progress bar width
32, // spacer width
0, // rew
0, // play
0, // pause
0, // stop
0, // ffwd
32, // spacer width
0,
96/*64*/ // volume bar width
};
IVideoOSD::IVideoOSD()
: ctrlrects_ready(0), parent(0)
{
temp = this;
getString(IDS_OSD_PROGRESS_TEXT,progStr,64);
getString(IDS_OSD_VOLUME_TEXT,volStr,64);
last_close_height = 0;
last_close_width = 0;
osdMemBMW = 0;
osdMemBMH = 0;
osdLastMouseX = -1;
osdLastMouseY = -1;
ignore_mousemove_count = 0;
osdLastClickItem = 0;
show_osd = false;
for (int i = 0; i < NUM_WIDGETS; i++)
SetRect(&ctrlrect[i], 0, 0, 0, 0);
}
IVideoOSD::~IVideoOSD()
{
KillTimer(parent, (UINT_PTR)this);
}
bool IVideoOSD::Showing()
{
return show_osd;
}
bool IVideoOSD::Ready()
{
return !!ctrlrects_ready;
}
int IVideoOSD::GetBarHeight()
{
return (/*show_osd && */ctrlrects_ready) ? (ctrlrect_all.bottom - ctrlrect_all.top) : 0;
}
void IVideoOSD::HitTest(int x, int y, int dragging)
{
if (!show_osd) return ;
// dragging == -1: just a mousemove (no clicking)
// dragging == 0: user clicked
// dragging == 1: user clicked before, and is now dragging/moving mouse
if (dragging < 1)
osdLastClickItem = -1;
// transform (x,y) from screen coords into coords relative to the memDC
RECT lastfsrect;
getViewport(&lastfsrect, parent, 1, NULL);
y = y - ((lastfsrect.bottom - lastfsrect.top) - (ctrlrect_all.bottom - ctrlrect_all.top));
int i0 = 0;
int i1 = NUM_WIDGETS;
if (dragging == 1)
{
i0 = osdLastClickItem;
i1 = osdLastClickItem + 1;
}
for (int i = i0; i < i1; i++)
{
if (dragging == 1 || (x >= ctrlrect[i].left && x <= ctrlrect[i].right && y >= ctrlrect[i].top && y <= ctrlrect[i].bottom))
{
float t = (x - ctrlrect[i].left) / (float)(ctrlrect[i].right - ctrlrect[i].left);
if (t < 0) t = 0;
if (t > 1) t = 1;
if (dragging < 1)
osdLastClickItem = i;
switch (i)
{
case CTRL_VOL:
if (dragging >= 0)
{
int v = (int)(t * 255);
config_volume = v;
in_setvol(v);
draw_volumebar(config_volume, 0);
}
return ;
case CTRL_PROGRESS:
if (dragging >= 0)
{
int len = in_getlength();
if (len > 0 && !PlayList_ishidden(PlayList_getPosition()))
{
if (in_seek((int)(t*len*1000)) < 0)
SendMessageW(hMainWindow, WM_WA_MPEG_EOF, 0, 0);
}
}
return ;
case CTRL_PAUSE:
if (dragging == 0)
{
PostMessageW(hMainWindow, WM_COMMAND, WINAMP_BUTTON3, 0);
}
return ;
case CTRL_PLAY:
if (dragging == 0)
{
PostMessageW(hMainWindow, WM_COMMAND, WINAMP_BUTTON2, 0);
}
return ;
case CTRL_STOP:
if (dragging == 0)
{
PostMessageW(hMainWindow, WM_COMMAND, WINAMP_BUTTON4, 0);
}
return ;
case CTRL_REW:
case CTRL_FFWD:
if (dragging == 0)
{
if (i == CTRL_REW)
PostMessageW(hMainWindow, WM_COMMAND, WINAMP_BUTTON1, 0);
else
PostMessageW(hMainWindow, WM_COMMAND, WINAMP_BUTTON5, 0);
}
return ;
default:
if (dragging < 1)
osdLastClickItem = -1;
break;
}
}
}
}
void IVideoOSD::Show()
{
if (!show_osd)
{
show_osd = true;
SetCursor(LoadCursor(NULL, IDC_ARROW));
RECT r;
GetClientRect(parent, &r);
r.bottom = r.top + GetBarHeight();
InvalidateRect(parent, &r, TRUE);
GetClientRect(parent, &r);
r.top = r.bottom - GetBarHeight();
InvalidateRect(parent, &r, TRUE);
PostMessageW(parent, WM_USER + 0x888, 0, 0);
}
KillTimer(parent, (UINT_PTR)this);
SetTimer(parent, (UINT_PTR)this, 3000, IVideoOSD::TimerCallback);
// Draw();
}
void CALLBACK IVideoOSD::TimerCallback(HWND /*hwnd*/, UINT /*uMsg*/, UINT_PTR idEvent, DWORD/* dwTime*/)
{
IVideoOSD *videoOSD = (IVideoOSD *)idEvent;
videoOSD->Hide();
}
void IVideoOSD::Hide()
{
RECT r;
GetClientRect(parent, &r);
r.bottom = r.top + GetBarHeight();
InvalidateRect(parent, &r, TRUE);
GetClientRect(parent, &r);
r.top = r.bottom - GetBarHeight();
InvalidateRect(parent, &r, TRUE);
PostMessageW(parent, WM_USER + 0x888, 0, 0);
if (show_osd)
{
//MessageBeep(0xFFFFFFFF);
show_osd = false;
KillTimer(parent, (UINT_PTR)this);
SetCursor(NULL);
}
//ctrlrects_ready = 0;
}
void IVideoOSD::Draw()
{
HDC hdc = GetDC(parent);
if (show_osd)
{
HGDIOBJ osdProgressBrushBg = CreateSolidBrush(RGB(OSD_VOL_BKCOL_R, OSD_VOL_BKCOL_G, OSD_VOL_BKCOL_B));
HGDIOBJ osdProgressBrushFg = CreateSolidBrush(RGB(OSD_VOL_COL_R, OSD_VOL_COL_G, OSD_VOL_COL_B));
HGDIOBJ osdProgressPenBg = CreatePen(PS_SOLID, 0, RGB(OSD_TEXT_R, OSD_TEXT_G, OSD_TEXT_B));
HGDIOBJ osdProgressPenFg = CreatePen(PS_NULL, 0, RGB(0, 0, 0));
HGDIOBJ osdProgressPenBgHilite = CreatePen(PS_SOLID, 0, RGB(OSD_TEXT_R_HILITE, OSD_TEXT_G_HILITE, OSD_TEXT_B_HILITE));
HGDIOBJ osdBlackBrush = CreateSolidBrush(RGB(0, 0, 0)); //OV_COL_R,OV_COL_G,OV_COL_B));
HFONT osdFontSymbol = CreateFontA(OSD_TEXT_SIZE, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, SYMBOL_CHARSET, OUT_OUTLINE_PRECIS, CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, DEFAULT_PITCH, "Webdings");
HDC osdMemDC = CreateCompatibleDC(hdc);
HBITMAP osdMemBM = 0; // memory bitmap (for memDC)
HBITMAP osdOldBM = 0; // old bitmap (from memDC)
COLORREF fg = GetTextColor(osdMemDC);
COLORREF bg = GetBkColor(osdMemDC);
SetTextColor(osdMemDC, RGB(OSD_TEXT_R, OSD_TEXT_G, OSD_TEXT_B));
SetBkColor(osdMemDC, RGB(0, 0, 0)); //OV_COL_R,OV_COL_G,OV_COL_B));
HGDIOBJ oldfont = SelectObject(osdMemDC, osdFontText);
HGDIOBJ oldbrush = SelectObject(osdMemDC, osdProgressBrushBg);
HGDIOBJ oldpen = SelectObject(osdMemDC, osdProgressPenBg);
RECT fullr;
GetClientRect(parent, &fullr);
/* ClientToScreen(parent, (LPPOINT)&fullr);
ClientToScreen(parent, ((LPPOINT)&fullr) + 1);
// transform coords from windows desktop coords (where 0,0==upper-left corner of the primary monitor)
// to the coords for the monitor we're displaying on:
fullr.top -= m_mon_y;
fullr.left -= m_mon_x;
fullr.right -= m_mon_x;
fullr.bottom -= m_mon_y;
*/
int streaming = (in_getlength() < 0) || !in_mod || !in_mod->is_seekable;
if (ctrlrects_ready != streaming + 1)
{
ctrlrects_ready = streaming + 1;
int net_width = 0;
int max_height = 0;
int i;
for (i = 0; i < NUM_WIDGETS; i++)
{
SetRect(&ctrlrect[i], 0, 0, 0, 0);
if (streaming && (i == CTRL_PROGRESS || i == CTRL_PROGRESSTEXT || i == CTRL_PROGRESSSPACER || i == CTRL_FFWD || i == CTRL_REW))
{
// disable progress bar + seek arrows when the NSV is a stream
ctrlrect[i].right = -1;
continue;
}
else if (g_ctrl_force_width[i] != 0)
{
SetRect(&ctrlrect[i], 0, 0, g_ctrl_force_width[i], 0);
}
else
{
SelectObject(osdMemDC, (g_ctrl_type[i] == CTRLTYPE_SYMBOL) ? osdFontSymbol : osdFontText);
SetRect(&ctrlrect[i], 0, 0, 256, 256);
ctrlrect[i].bottom = DrawTextA(osdMemDC, g_ctrl_text[i], -1, &ctrlrect[i], DT_SINGLELINE | DT_CALCRECT);
}
net_width += ctrlrect[i].right - ctrlrect[i].left;
max_height = max(max_height, ctrlrect[i].bottom - ctrlrect[i].top);
}
// now we know the size of all the controls; now place them.
int x = (fullr.right + fullr.left) / 2 - net_width / 2;
SetRect(&ctrlrect_all, 0, 0, 0, 0);
for (i = 0; i < NUM_WIDGETS; i++)
{
if (ctrlrect[i].right >= 0) // if control is not disabled...
{
int this_width = ctrlrect[i].right - ctrlrect[i].left;
int this_height = ctrlrect[i].bottom - ctrlrect[i].top ;
if (this_height == 0) this_height = max_height * 2 / 3; // progress bars
ctrlrect[i].top = max_height / 2 - this_height / 2;
ctrlrect[i].bottom = max_height / 2 + this_height / 2;
ctrlrect[i].left = x;
ctrlrect[i].right = x + this_width;
if (ctrlrect_all.bottom == 0)
{
ctrlrect_all.top = ctrlrect[i].top ;
ctrlrect_all.bottom = ctrlrect[i].bottom;
}
else
{
ctrlrect_all.top = min(ctrlrect_all.top , ctrlrect[i].top);
ctrlrect_all.bottom = max(ctrlrect_all.bottom, ctrlrect[i].bottom);
}
x += this_width;
}
}
}
int w = fullr.right - fullr.left;
int h = ctrlrect_all.bottom - ctrlrect_all.top;
if (!osdMemBM || osdMemBMW != w || osdMemBMH != h)
{
if (osdMemBM)
{
SelectObject(osdMemDC, osdOldBM);
DeleteObject(osdMemBM);
}
osdMemBM = CreateCompatibleBitmap(hdc, w, h);
osdOldBM = (HBITMAP)SelectObject(osdMemDC, osdMemBM);
osdMemBMW = w;
osdMemBMH = h;
}
RECT temp;
SetRect(&temp, 0, 0, w, h);
FillRect(osdMemDC, &temp, (HBRUSH)osdBlackBrush);
for (int i = 0; i < NUM_WIDGETS; i++)
{
if (g_ctrl_type[i] == CTRLTYPE_PROGRESS)
{
int progress = 0;
int max_progress = ctrlrect[i].right - ctrlrect[i].left;
switch (i)
{
case CTRL_VOL:
progress = config_volume * max_progress / 255;
break;
case CTRL_PROGRESS:
if (playing)
{
int len = in_getlength();
if (len > 0) progress = (in_getouttime() / 1000) * max_progress / len;
}
if (progress > max_progress) progress = max_progress;
break;
}
SelectObject(osdMemDC, osdProgressBrushBg);
SelectObject(osdMemDC, (i == osdLastClickItem) ? osdProgressPenBgHilite : osdProgressPenBg);
RoundRect(osdMemDC, ctrlrect[i].left, ctrlrect[i].top, ctrlrect[i].right, ctrlrect[i].bottom, 3, 3);
SelectObject(osdMemDC, osdProgressBrushFg);
SelectObject(osdMemDC, osdProgressPenFg);
Rectangle(osdMemDC, ctrlrect[i].left + 1, ctrlrect[i].top + 1, ctrlrect[i].left + progress, ctrlrect[i].bottom);
}
else if (g_ctrl_type[i] == CTRLTYPE_SYMBOL ||
g_ctrl_type[i] == CTRLTYPE_TEXT)
{
SelectObject(osdMemDC, (g_ctrl_type[i] == CTRLTYPE_SYMBOL) ? osdFontSymbol : osdFontText);
SetTextColor(osdMemDC, (i == osdLastClickItem) ? RGB(OSD_TEXT_R_HILITE, OSD_TEXT_G_HILITE, OSD_TEXT_B_HILITE) : RGB(OSD_TEXT_R, OSD_TEXT_G, OSD_TEXT_B));
DrawTextA(osdMemDC, g_ctrl_text[i], -1, &ctrlrect[i], DT_SINGLELINE);
}
}
int x0 = fullr.left;
int y0 = fullr.bottom - (ctrlrect_all.bottom - ctrlrect_all.top);
BitBlt(hdc, x0, y0, w, h, osdMemDC, 0, 0, SRCCOPY);
// display stream title @ the top:
#if (SHOW_STREAM_TITLE_AT_TOP)
if (1)
{
RECT temp;
SetRect(&temp, 0, 0, w, h);
FillRect(osdMemDC, &temp, (HBRUSH)osdBlackBrush);
SelectObject(osdMemDC, osdFontText);
SetTextColor(osdMemDC, RGB(OSD_TEXT_R, OSD_TEXT_G, OSD_TEXT_B));
AutoChar narrowTitle(FileTitle);
wchar_t buf[FILETITLE_SIZE+32] = {0};
StringCchPrintfW(buf, sizeof(buf)/sizeof(*buf), L"%s (%d %s)", FileTitle ? FileTitle : L"", g_brate, getStringW(IDS_KBPS,NULL,0));
if ((config_fixtitles&2))
{
wchar_t *p = buf;
while (p && *p)
{
if (*p == '_') // replace _ with space
*p = ' ';
p = CharNextW(p);
}
}
DrawTextW(osdMemDC, buf, -1, &temp, DT_SINGLELINE | DT_CENTER);
SelectObject(osdMemDC, osdFontSymbol);
DrawTextW(osdMemDC, L"2", -1, &temp, DT_SINGLELINE | DT_RIGHT);
RECT rr = {0, 0, w, h};
DrawTextW(osdMemDC, L"2", -1, &rr, DT_SINGLELINE | DT_RIGHT | DT_CALCRECT);
last_close_height = rr.bottom - rr.top;
last_close_width = rr.right - rr.left;
int x0 = fullr.left;
int y0 = fullr.top;
BitBlt(hdc, x0, y0, w, h, osdMemDC, 0, 0, SRCCOPY);
}
SelectObject(osdMemDC, oldpen);
SelectObject(osdMemDC, oldbrush);
SelectObject(osdMemDC, oldfont);
SetTextColor(osdMemDC, fg);
SetBkColor(osdMemDC, bg);
DeleteObject(osdProgressBrushBg);
DeleteObject(osdProgressBrushFg);
DeleteObject(osdBlackBrush);
DeleteObject(osdProgressPenBg);
DeleteObject(osdProgressPenFg);
DeleteObject(osdProgressPenBgHilite);
DeleteObject(osdFontSymbol);
if (osdMemDC)
{
SelectObject(osdMemDC, osdOldBM); // delete our doublebuffer
DeleteDC(osdMemDC);
}
if (osdMemBM) DeleteObject(osdMemBM);
}
#endif
ReleaseDC(parent, hdc);
}
bool IVideoOSD::CloseHitTest(int x, int y)
{
RECT r;
GetClientRect(parent, &r);
return (x > r.right - last_close_width && y < last_close_height);
}
bool IVideoOSD::Mouse(int x, int y, WPARAM wParam, bool moving)
{
if (wParam & MK_LBUTTON)
{
Show();
if (CloseHitTest(x, y))
return true;
ignore_mousemove_count = 2;
HitTest(x, y, moving ? 1 : 0);
}
else
{
if (((osdLastMouseX - x) || (osdLastMouseY - y)) && (ignore_mousemove_count--))
{
Show();
HitTest(x, y, moving ? -1 : 0);
ignore_mousemove_count = 2;
}
}
osdLastMouseX = x;
osdLastMouseY = y;
return false;
}