winamp/Src/Wasabi/api/wnd/wndclass/editwnd.cpp
2024-09-24 14:54:57 +02:00

1002 lines
18 KiB
C++

#include <precomp.h>
#include "editwnd.h"
#include <tataki/canvas/canvas.h>
#include <api/wnd/notifmsg.h>
#include <bfc/assert.h>
#define ID_EDITCHILD 12
enum { IDLETIMER = 8, DELETETIMER = 10 };
#define IDLETIME 350 // comprimises suck ;)
#if UTF8
#ifdef WANT_UTF8_WARNINGS
#pragma CHAT("mig", "all", "UTF8 is enabled in editwnd.cpp -- Things might be screwy till it's all debugged?")
#endif
# include <bfc/string/encodedstr.h>
#endif
#ifdef WIN32
#include <commctrl.h>
#endif
#ifdef WIN32
static LRESULT CALLBACK static_editWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
EditWnd *editwnd = (EditWnd *)GetWindowLongPtrW(hWnd, GWLP_USERDATA);
if (editwnd == NULL) return DefWindowProcW(hWnd, uMsg, wParam, lParam);
return editwnd->editWndProc(hWnd, uMsg, wParam, lParam);
}
#endif
EditWnd::EditWnd(wchar_t *buffer, int buflen)
{
wantfocus = 1;
nextenterfaked = 0;
idleenabled = 1;
beforefirstresize = 1;
editWnd = NULL;
prevWndProc = NULL;
setBuffer(buffer, buflen);
maxlen = 0;
retcode = EDITWND_RETURN_NOTHING;
modal = 0;
autoenter = 0;
autoselect = 0;
outbuf = NULL;
// bordered = 0;
idletimelen = IDLETIME;
multiline = 0;
readonly = 0;
password = 0;
autohscroll = 1;
autovscroll = 1;
vscroll = 0;
#ifdef WIN32
oldbrush = NULL;
#endif
#ifdef LINUX
selstart = selend = 0;
cursorpos = 0;
selectmode = 0;
viewstart = 0;
#endif
#ifdef WASABI_EDITWND_LISTCOLORS
if (WASABI_API_SKIN->skin_getColorElementRef(L"wasabi.list.text"))
textcolor = L"wasabi.list.text";
else
textcolor = L"wasabi.edit.text";
if (WASABI_API_SKIN->skin_getColorElementRef(L"wasabi.list.background"))
backgroundcolor = L"wasabi.list.background";
else
textcolor.setColor(WASABI_API_SKIN->skin_getBitmapColor(L"wasabi.list.background"));
#else
backgroundcolor = "wasabi.edit.background";
textcolor = "wasabi.edit.text";
#endif
selectioncolor = L"wasabi.edit.selection";
setVirtual(0);
}
EditWnd::~EditWnd()
{
killTimer(IDLETIMER);
#ifdef WIN32
if (oldbrush != NULL)
DeleteObject(oldbrush);
oldbrush = NULL;
if (editWnd != NULL)
{
SetWindowLong(editWnd, GWLP_USERDATA, (LONG_PTR)0);
SetWindowLongPtrW(editWnd, GWLP_WNDPROC, (LONG_PTR)prevWndProc);
DestroyWindow(editWnd);
}
#endif
notifyParent(ChildNotify::RETURN_CODE, retcode);
}
int EditWnd::onInit()
{
EDITWND_PARENT::onInit();
#ifdef WIN32
RECT r = clientRect();
editWnd = CreateWindowW(L"EDIT", NULL,
WS_CHILD
| (autohscroll ? ES_AUTOHSCROLL : 0)
| (readonly ? ES_READONLY : 0)
| (multiline ? ES_MULTILINE : 0)
| (password ? ES_PASSWORD : 0)
| (autovscroll ? ES_AUTOVSCROLL : 0)
| (vscroll ? WS_VSCROLL : 0),
r.left, r.top, r.right - r.left, r.bottom - r.top,
gethWnd(), (HMENU)ID_EDITCHILD,
getOsModuleHandle(), NULL);
ASSERT(editWnd != NULL);
if ((maxlen != 0) && (outbuf != NULL))
{
setBuffer(outbuf, maxlen);
}
// stash a pointer to us
SetWindowLongPtrW(editWnd, GWLP_USERDATA, (LONG_PTR)this);
// subclass the edit control -- either by 8 or by 16
prevWndProc = (WNDPROC)SetWindowLongPtrW(editWnd, GWLP_WNDPROC, (LONG_PTR)static_editWndProc);
SendMessageW(editWnd, WM_SETFONT, (WPARAM)GetStockObject(ANSI_VAR_FONT), FALSE);
ShowWindow(editWnd, !getStartHidden() ? SW_NORMAL : SW_HIDE);
#endif
return 1;
}
void EditWnd::onSetVisible(int show)
{
EDITWND_PARENT::onSetVisible(show);
if (editWnd == NULL) return ;
#ifdef WIN32
ShowWindow(editWnd, show ? SW_NORMAL : SW_HIDE);
#endif
}
int EditWnd::onPaint(Canvas *canvas)
{
// if (!bordered) return EDITWND_PARENT::onPaint(canvas);
PaintCanvas paintcanvas;
if (canvas == NULL)
{
if (!paintcanvas.beginPaint(this)) return 0;
canvas = &paintcanvas;
}
EDITWND_PARENT::onPaint(canvas);
RECT r;
getClientRect(&r);
canvas->fillRect(&r, backgroundcolor); //SKIN
#ifdef LINUX
char *str = STRDUP((const char *)inbuf + viewstart);
canvas->setTextColor(textcolor);
canvas->setTextSize(r.bottom - r.top);
canvas->setTextOpaque(FALSE);
char save;
if (selstart != selend)
{
RECT selrect = r;
int start = MAX(MIN(selstart, selend) - viewstart, 0);
int end = MAX(MAX(selstart, selend) - viewstart, 0);
save = str[ start ];
str[start] = '\0';
selrect.left = r.left + canvas->getTextWidth(str);
str[start] = save;
save = str[ end ];
str[end] = '\0';
selrect.right = r.left + canvas->getTextWidth(str);
str[end] = save;
canvas->fillRect(&selrect, selectioncolor);
}
save = str[cursorpos - viewstart];
str[cursorpos - viewstart] = '\0';
RECT cursor = r;
cursor.left = cursor.right = r.left + canvas->getTextWidth(str);
str[cursorpos - viewstart] = save;
canvas->drawRect(&cursor, TRUE, 0xffffff);
canvas->textOut(r.left, r.top, r.right - r.left, r.bottom - r.top, str);
FREE(str);
#endif
return 1;
}
int EditWnd::onResize()
{
EDITWND_PARENT::onResize();
#ifdef WIN32
RECT r = clientRect();
if (1 /*bordered*/)
{
r.top++;
r.bottom--;
r.left++;
r.right--;
}
MoveWindow(editWnd, r.left, r.top, r.right - r.left, r.bottom - r.top, TRUE);
if (beforefirstresize)
{
ShowWindow(editWnd, SW_NORMAL); beforefirstresize = 0;
if (modal)
{
SetFocus(editWnd);
if (getAutoSelect())
SendMessageW(editWnd, EM_SETSEL, 0, -1);
}
}
#endif
return TRUE;
}
#ifdef WIN32
LRESULT EditWnd::wndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CTLCOLOREDIT:
{
HDC hdc = (HDC)wParam;
SetTextColor(hdc, textcolor);
SetBkColor(hdc, backgroundcolor);
if (oldbrush != NULL)
{
DeleteObject(oldbrush);
oldbrush = NULL;
}
oldbrush = CreateSolidBrush(backgroundcolor);
return (LRESULT)oldbrush;
}
case WM_MOUSEACTIVATE:
WASABI_API_WND->popupexit_check(this);
break;
case WM_COMMAND:
{
switch (HIWORD(wParam))
{
case EN_CHANGE:
{
if (maxlen > 0)
{
GetWindowTextW(editWnd, outbuf, maxlen);
onEditUpdate();
}
}
break;
case EN_SETFOCUS:
if (getAutoSelect())
SendMessageW(editWnd, EM_SETSEL, (WPARAM)0, (LPARAM) - 1);
break;
case EN_KILLFOCUS:
onLoseFocus();
break;
}
}
break;
}
return EDITWND_PARENT::wndProc(hWnd, uMsg, wParam, lParam);
}
#endif
void EditWnd::setBuffer(wchar_t *buffer, int len)
{
#ifdef LINUX
if (buffer == NULL || len <= 1)
{
inbuf = "";
return ;
}
#endif
if (buffer == NULL || len <= 1) return ;
ASSERT(len > 1);
ASSERT(len < 0x7ffe);
ASSERT((int)wcslen(buffer) <= len);
#ifdef WIN32
#define USE_INTERNAL_BUFFER 0
#if USE_INTERNAL_BUFFER
buffer8.setSize(len + 1);
outbuf = buffer8.getMemory();
if (len)
{
STRNCPY(outbuf, buffer, len);
}
outbuf[len] = 0;
#else
outbuf = buffer;
#endif
if (editWnd != NULL)
{
SetWindowTextW(editWnd, buffer);
// This is going to be problematic. This is where utf8 sucks.
// Just how many characters CAN we save in our buffer, eh?
// (shrug) Oh well. Can't be helped. At most this many.
SendMessageW(editWnd, EM_LIMITTEXT, (WPARAM)len - 1, (LPARAM)0);
// hooray for halcyon7
/* if (getAutoSelect()) {
SetFocus(editWnd);
SendMessageW(editWnd, EM_SETSEL, (WPARAM)0, (LPARAM)-1);
}*/
}
maxlen = len;
#else
outbuf = buffer;
maxlen = len;
inbuf = buffer;
cursorpos = len;
invalidate();
#endif
}
void EditWnd::selectAll()
{
#ifdef WIN32
PostMessage(editWnd, EM_SETSEL, 0, -1);
#else
selstart = 0; selend = inbuf.len();
#endif
}
void EditWnd::enter()
{
onEnter();
}
void EditWnd::getBuffer(wchar_t *buf, int _len)
{
if (_len > maxlen) _len = maxlen;
// SendMessageW(editWnd, WM_GETTEXT, (WPARAM)_len, (LPARAM)buf);
WCSCPYN(buf, outbuf, _len);
}
void EditWnd::setModal(int _modal)
{
modal = _modal;
}
void setBorder(int border)
{
// bordered = border;
}
int EditWnd::isEditorKey(int vk)
{
if (vk >= VK_F1) return 0;
if ((vk == VK_UP || vk == VK_DOWN) || ((Std::keyDown(VK_CONTROL) || Std::keyDown(VK_MENU)) && (vk == VK_LEFT || vk == VK_RIGHT)))
return 0;
if (vk == VK_RETURN && Std::keyDown(VK_CONTROL)) return 0;
if (vk == VK_CONTROL || vk == VK_MENU) return 0;
return 1;
}
LRESULT EditWnd::editWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_KEYDOWN:
if (!isEditorKey((int)wParam) && !onKeyDown((int)wParam))
{
#ifdef WASABI_COMPILE_WND
WASABI_API_WND->forwardOnKeyDown(this, (int) wParam, (int)lParam);
#endif
}
break;
case WM_KEYUP:
if (!isEditorKey((int)wParam) && !onKeyUp((int)wParam))
{
#ifdef WASABI_COMPILE_WND
WASABI_API_WND->forwardOnKeyUp(this, (int) wParam, (int)lParam);
#endif
}
break;
case WM_CHAR:
if (!(wParam == VK_RETURN && nextenterfaked && !autoenter))
{
notifyParent(ChildNotify::EDITWND_KEY_PRESSED, wParam);
onChar((TCHAR)wParam);
}
if (wParam == VK_RETURN)
{
if (!(nextenterfaked && !autoenter))
if (onEnter()) return 0;
nextenterfaked = 0;
return 0;
}
else if (wParam == VK_ESCAPE)
{
if (onAbort()) return 0;
}
else if (wParam == VK_TAB && multiline)
{
return 0;
}
break;
case WM_SETFOCUS:
onSetRootFocus(this);
// fall thru
case WM_KILLFOCUS:
invalidate();
break;
}
#ifdef WIN32
return CallWindowProc(prevWndProc, hWnd, uMsg, wParam, lParam);
#else
DebugString("portme -- EditWnd::editWndProc\n");
return 0;
#endif
}
void EditWnd::timerCallback(int id)
{
switch (id)
{
case IDLETIMER:
killTimer(IDLETIMER);
if (idleenabled) onIdleEditUpdate();
break;
case DELETETIMER:
killTimer(DELETETIMER);
delete this;
break;
default:
EDITWND_PARENT::timerCallback(id);
}
}
void EditWnd::onEditUpdate()
{
#ifdef LINUX
STRNCPY(outbuf, inbuf, maxlen);
outbuf[maxlen] = '\0';
RECT r;
getClientRect(&r);
SysCanvas sysc;
sysc.setTextSize(r.bottom - r.top);
sysc.getTextWidth(inbuf);
char *str = STRDUP(inbuf);
if (cursorpos < viewstart)
viewstart = cursorpos;
char save = str[cursorpos];
str[cursorpos] = '\0';
while (sysc.getTextWidth(str + viewstart) > r.right - r.left)
{
viewstart++;
}
str[cursorpos] = save;
invalidate();
#endif
killTimer(IDLETIMER);
setTimer(IDLETIMER, idletimelen);
notifyParent(ChildNotify::EDITWND_DATA_MODIFIED);
}
void EditWnd::onIdleEditUpdate()
{
notifyParent(ChildNotify::EDITWND_DATA_MODIFIED_ONIDLE);
}
int EditWnd::onEnter()
{
notifyParent(ChildNotify::EDITWND_ENTER_PRESSED);
if (modal)
{
retcode = EDITWND_RETURN_OK;
delete this;
//CUT setTimer(DELETETIMER, 1);
return 1;
}
return 0;
}
int EditWnd::onAbort()
{
notifyParent(ChildNotify::EDITWND_CANCEL_PRESSED);
if (modal)
{
retcode = EDITWND_RETURN_CANCEL;
delete this;
//CUT setTimer(DELETETIMER, 1);
return 1;
}
return 0;
}
int EditWnd::onLoseFocus()
{ // fake an onEnter()
#ifdef WIN32
if (autoenter)
{
nextenterfaked = 1;
PostMessage(editWnd, WM_CHAR, VK_RETURN, 0);
}
#else
invalidate();
selstart = selend = 0;
#endif
return 0;
}
void EditWnd::setAutoEnter(int a)
{
autoenter = a;
}
void EditWnd::setAutoSelect(int a)
{
autoselect = a;
};
void EditWnd::setIdleTimerLen(int ms)
{
if (ms < 0) ms = 0;
idletimelen = ms;
}
int EditWnd::getTextLength()
{ // TOTALLY NONPORTABLE AND TOTALLY DIRTY
#ifdef WIN32
HFONT font = (HFONT)SendMessageW(editWnd, WM_GETFONT, 0, 0);
HDC sdc = GetDC(NULL);
HDC dc = CreateCompatibleDC(sdc);
ReleaseDC(NULL, sdc);
HFONT oldfont = (HFONT)SelectObject(dc, font);
SIZE s;
GetTextExtentPoint32W(dc, outbuf, wcslen(outbuf), &s);
SelectObject(dc, oldfont);
DeleteDC(dc);
return s.cx + SendMessageW(editWnd, EM_GETMARGINS, 0, 0)*2 + 2;
#else
if (inbuf.isempty())
return 0;
RECT r;
getClientRect(&r);
SysCanvas sysc;
sysc.setTextSize(r.bottom - r.top);
return sysc.getTextWidth(inbuf);
#endif
}
HWND EditWnd::getEditWnd()
{
return editWnd;
}
#ifndef WIN32
enum {
ES_MULTILINE,
ES_WANTRETURN,
ES_AUTOHSCROLL,
ES_AUTOVSCROLL,
WS_VSCROLL,
};
#endif
void EditWnd::setMultiline(int ml)
{
multiline = ml;
setStyle(ES_MULTILINE | ES_WANTRETURN, ml);
}
void EditWnd::setReadOnly(int ro)
{
readonly = ro;
setStyle(ES_READONLY, ro);
}
void EditWnd::setPassword(int pw)
{
password = pw;
setStyle(ES_PASSWORD, pw);
}
void EditWnd::setAutoHScroll(int hs)
{
autohscroll = hs;
setStyle(ES_AUTOHSCROLL, hs);
}
void EditWnd::setAutoVScroll(int vs)
{
autovscroll = vs;
setStyle(ES_AUTOVSCROLL, vs);
}
void EditWnd::setVScroll(int vs)
{
vscroll = vs;
setStyle(WS_VSCROLL, vs);
}
void EditWnd::setStyle(LONG style, int set)
{
#ifdef WIN32
if (editWnd)
{
LONG s = GetWindowLong(editWnd, GWL_STYLE);
if (set) s |= style;
else s &= ~style;
SetWindowLong(editWnd, GWL_STYLE, s);
}
#else
DebugString("portme -- EditWnd::setStyle\n");
#endif
}
int EditWnd::onGetFocus()
{
int r = EDITWND_PARENT::onGetFocus();
#ifdef WIN32
if (editWnd != NULL)
SetFocus(editWnd);
#endif
return r;
}
int EditWnd::wantFocus()
{
return wantfocus;
}
int EditWnd::gotFocus()
{
return (GetFocus() == editWnd);
}
void EditWnd::setBackgroundColor(COLORREF c)
{
backgroundcolor.setColor(c);
}
void EditWnd::setTextColor(COLORREF c)
{
textcolor.setColor(c);
}
void EditWnd::invalidate()
{
EDITWND_PARENT::invalidate();
InvalidateRect(editWnd, NULL, TRUE);
}
#ifdef LINUX
int EditWnd::textposFromCoord(int x, int y)
{
RECT r;
getClientRect(&r);
SysCanvas canvas;
canvas.setTextColor(textcolor);
canvas.setTextSize(r.bottom - r.top);
canvas.setTextOpaque(FALSE);
x -= r.left;
int i;
char *str = STRDUP(inbuf);
if (x > canvas.getTextWidth(str + viewstart))
return inbuf.len();
for (i = viewstart + 1; str[i]; i++)
{
char save = str[i];
str[i] = '\0';
if (x < canvas.getTextWidth(str + viewstart))
{
str[i] = save;
break;
}
str[i] = save;
}
FREE(str);
return i - 1;
}
int EditWnd::onLeftButtonDown(int x, int y)
{
EDITWND_PARENT::onLeftButtonDown(x, y);
// Add check for double/triple click...
cursorpos = textposFromCoord(x, y);
selstart = selend = cursorpos;
selectmode = 1;
return 1;
}
int EditWnd::onLeftButtonUp(int x, int y)
{
EDITWND_PARENT::onLeftButtonUp(x, y);
selectmode = 0;
return 1;
}
int EditWnd::onMouseMove(int x, int y)
{
EDITWND_PARENT::onMouseMove(x, y);
switch (selectmode)
{
case 0:
// Do nothing
break;
case 1:
selend = textposFromCoord(x, y);
cursorpos = selend;
onEditUpdate();
break;
default:
DebugString("selectmode %d not available\n", selectmode);
break;
}
return selectmode;
}
int EditWnd::onKeyDown(int key)
{
EDITWND_PARENT::onKeyDown(key);
if (Std::keyDown(VK_CONTROL))
{
switch (key)
{
case 'a':
case 'A':
selectAll();
break;
default:
return 0;
}
}
else
{
switch (key)
{
case XK_Home:
if (Std::keyDown(VK_SHIFT))
{
if (selstart == selend)
{
selstart = selend = cursorpos;
}
selend = 0;
}
else
{
selstart = selend = 0;
}
cursorpos = 0;
break;
case XK_End:
if (Std::keyDown(VK_SHIFT))
{
if (selstart == selend)
{
selstart = selend = cursorpos;
}
selend = inbuf.len();
}
else
{
selstart = selend = 0;
}
cursorpos = inbuf.len();
break;
case XK_Right:
if (Std::keyDown(VK_SHIFT))
{
if (selstart == selend)
{
selstart = selend = cursorpos;
}
selend++;
if (selend > inbuf.len()) selend = inbuf.len();
}
else
{
selstart = selend = 0;
}
cursorpos++;
if (cursorpos > inbuf.len()) cursorpos = inbuf.len();
break;
case XK_Left:
if (Std::keyDown(VK_SHIFT))
{
if (selstart == selend)
{
selstart = selend = cursorpos;
}
selend--;
if (selend < 0) selend = 0;
}
else
{
selstart = selend = 0;
}
cursorpos--;
if (cursorpos < 0) cursorpos = 0;
break;
case XK_Escape:
onAbort();
break;
case XK_Return:
onEnter();
break;
case XK_Delete:
if (selstart != selend)
{
int start = MIN(selstart, selend);
int end = MAX(selstart, selend);
String add;
if (end < inbuf.len())
{
add = (const char *)inbuf + end;
}
else
{
add = "";
}
inbuf.trunc(start);
inbuf += add;
cursorpos = start;
selstart = selend = 0;
}
else
{
if (cursorpos >= 0)
{
if (cursorpos < inbuf.len() - 1)
{
String tmp = inbuf;
tmp.trunc(cursorpos);
inbuf = tmp + ((const char *)inbuf + cursorpos + 1);
}
else if (cursorpos == inbuf.len() - 1)
{
inbuf.trunc(cursorpos);
}
}
}
break;
case VK_BACK:
if (selstart != selend)
{
int start = MIN(selstart, selend);
int end = MAX(selstart, selend);
String add;
if (end < inbuf.len())
{
add = (const char *)inbuf + end;
}
else
{
add = "";
}
inbuf.trunc(start);
inbuf += add;
cursorpos = start;
selstart = selend = 0;
}
else
{
if (cursorpos > 0)
{
if (cursorpos >= inbuf.len())
{
inbuf.trunc(cursorpos - 1);
cursorpos--;
}
else
{
String tmp = inbuf;
tmp.trunc(cursorpos - 1);
inbuf = tmp + ((const char *)inbuf + cursorpos);
cursorpos--;
}
}
}
break;
default:
if (key < 0x20 || key > 0x7e)
return 0;
if (selstart != selend)
{
int start = MIN(selstart, selend);
int end = MAX(selstart, selend);
String add;
if (end < inbuf.len())
{
add = (const char *)inbuf + end;
}
else
{
add = "";
}
inbuf.trunc(start);
inbuf += add;
cursorpos = start;
selstart = selend = 0;
}
String tmp;
if (cursorpos >= inbuf.len())
{
tmp = "";
}
else
{
tmp = (const char *)inbuf + cursorpos;
}
inbuf.trunc(cursorpos);
inbuf += (char)key;
inbuf += tmp;
cursorpos++;
}
}
onEditUpdate();
return 1;
}
#endif