#include "osgui_win.inl"
#include "oscontrol_win.inl"
#include "ospanel_win.inl"
#include "osscroll_win.inl"
#include "oswindow_win.inl"
#include "oslistener.inl"
#include "osstyleXP.inl"
#include "../osview.h"
#include "../osview.inl"
#include "../osgui.inl"
#include "../osscrolls.inl"
#include <draw2d/color.h>
#include <draw2d/draw.h>
#include <draw2d/dctxh.h>
#include <core/event.h>
#include <core/heap.h>
#include <osbs/osbs.h>
#include <sewer/cassert.h>
#include <sewer/ptr.h>
#include <sewer/bstd.h>
#if !defined(__WINDOWS__)
#error This file is only for Windows
#endif
#include <sewer/nowarn.hxx>
#include <gdiplus.h>
#include <Commctrl.h>
#include <sewer/warn.hxx>
struct _osview_t
{
OSControl control;
OSPanel *parent_panel;
OSScrolls *scroll;
DCtx *ctx;
uint32_t flags;
bool_t focused;
bool_t allow_tab;
HBITMAP dbuffer;
LONG dbuffer_width;
LONG dbuffer_height;
RECT border;
ViewListeners listeners;
OSDraw osdraw;
Listener *OnOverlay;
Listener *OnFocus;
Listener *OnResignFocus;
Listener *OnAcceptFocus;
};
#define i_color(c) \
Gdiplus::Color((BYTE)(c), (BYTE)((c) >> 8), (BYTE)((c) >> 16))
static LRESULT CALLBACK i_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
OSView *view = cast(GetWindowLongPtr(hwnd, GWLP_USERDATA), OSView);
cassert_no_null(view);
cassert(view->control.type == ekGUI_TYPE_CUSTOMVIEW);
cassert(view->control.hwnd == hwnd);
switch (uMsg)
{
case WM_ERASEBKGND:
return 1;
case WM_PRINTCLIENT:
cassert(FALSE);
_oslistener_draw(cast(view, OSControl), NULL, 0, 0, 0, 0, 0, 0, &view->listeners);
return 0;
case WM_NCCALCSIZE:
if (view->flags & ekVIEW_BORDER)
{
_osgui_nccalcsize(hwnd, wParam, lParam, FALSE, 0, &view->border);
return CallWindowProc(view->control.def_wnd_proc, hwnd, uMsg, wParam, lParam);
}
break;
case WM_NCPAINT:
if (view->flags & ekVIEW_BORDER)
{
CallWindowProc(view->control.def_wnd_proc, hwnd, uMsg, wParam, lParam);
return _osgui_ncpaint(hwnd, view->focused, &view->border, NULL);
}
break;
case WM_PAINT:
{
LONG_PTR edata;
edata = GetWindowLongPtr(hwnd, 0);
if (edata == 0)
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(view->control.hwnd, &ps);
HDC memHdc = CreateCompatibleDC(hdc);
if (view->ctx == NULL)
{
view->ctx = dctx_create();
dctx_data(view->ctx, &view->osdraw, NULL, OSDraw);
}
if (__TRUE_EXPECTED(view->dbuffer == NULL))
view->dbuffer = CreateCompatibleBitmap(hdc, view->dbuffer_width, view->dbuffer_height);
SelectObject(memHdc, view->dbuffer);
uint32_t background;
if (view->parent_panel != NULL)
{
cassert(cast(view->parent_panel, OSControl)->type == ekGUI_TYPE_PANEL);
background = (uint32_t)_ospanel_background_color(view->parent_panel, cast(view, OSControl));
}
else
{
background = (uint32_t)GetSysColor(COLOR_3DFACE);
}
Gdiplus::Graphics *graphics = new Gdiplus::Graphics(memHdc);
if ((view->flags & ekVIEW_NOERASE) == 0)
graphics->Clear(i_color(background));
uint32_t vx = 0;
uint32_t vy = 0;
uint32_t vwidth = (uint32_t)view->dbuffer_width;
uint32_t vheight = (uint32_t)view->dbuffer_height;
uint32_t twidth = vwidth;
uint32_t theight = vheight;
if (view->scroll != NULL)
_osscrolls_visible_area(view->scroll, &vx, &vy, &vwidth, &vheight, &twidth, &theight);
void *ctx[2];
ctx[0] = graphics;
ctx[1] = memHdc;
dctx_set_gcontext(view->ctx, ctx, (uint32_t)vwidth, (uint32_t)vheight, -(real32_t)vx, -(real32_t)vy, background, TRUE );
_oslistener_draw(cast(view, OSControl), view->ctx, (real32_t)twidth, (real32_t)theight, (real32_t)vx, (real32_t)vy, (real32_t)vwidth, (real32_t)vheight, &view->listeners);
dctx_unset_gcontext(view->ctx);
if (view->OnOverlay != NULL)
{
EvDraw params;
params.ctx = view->ctx;
params.x = 0;
params.y = 0;
params.width = (real32_t)vwidth;
params.height = (real32_t)vheight;
dctx_set_gcontext(view->ctx, ctx, (uint32_t)vwidth, (uint32_t)vheight, 0, 0, 0, TRUE);
listener_event(view->OnOverlay, ekGUI_EVENT_OVERLAY, cast(view, OSControl), ¶ms, NULL, OSControl, EvDraw, void);
dctx_unset_gcontext(view->ctx);
}
delete graphics;
graphics = NULL;
BitBlt(hdc, 0, 0, view->dbuffer_width, view->dbuffer_height, memHdc, 0, 0, SRCCOPY);
BOOL ok = DeleteDC(memHdc);
cassert_unref(ok != 0, ok);
EndPaint(hwnd, &ps);
}
else
{
_oslistener_draw(cast(view, OSControl), NULL, (real32_t)view->dbuffer_width, (real32_t)view->dbuffer_height, 0, 0, (real32_t)view->dbuffer_width, (real32_t)view->dbuffer_height, &view->listeners);
}
return 0;
}
case WM_MOUSELEAVE:
_oslistener_mouse_exit(cast(view, OSControl), &view->listeners);
return 0;
case WM_MOUSEMOVE:
{
POINTS point = MAKEPOINTS(lParam);
_oslistener_mouse_moved(cast(view, OSControl), wParam, (real32_t)point.x, (real32_t)point.y, view->scroll, &view->listeners);
return 0;
}
case WM_LBUTTONDOWN:
if (_oswindow_mouse_down(cast(view, OSControl)) == TRUE)
{
POINTS point = MAKEPOINTS(lParam);
_oslistener_mouse_down(cast(view, OSControl), ekGUI_MOUSE_LEFT, (real32_t)point.x, (real32_t)point.y, view->scroll, &view->listeners);
}
return 0;
case WM_LBUTTONDBLCLK:
if (_oswindow_mouse_down(cast(view, OSControl)) == TRUE)
break;
return 0;
case WM_RBUTTONDOWN:
{
POINTS point = MAKEPOINTS(lParam);
_oslistener_mouse_down(cast(view, OSControl), ekGUI_MOUSE_RIGHT, (real32_t)point.x, (real32_t)point.y, view->scroll, &view->listeners);
return 0;
}
case WM_MBUTTONDOWN:
{
POINTS point = MAKEPOINTS(lParam);
_oslistener_mouse_down(cast(view, OSControl), ekGUI_MOUSE_MIDDLE, (real32_t)point.x, (real32_t)point.y, view->scroll, &view->listeners);
return 0;
}
case WM_LBUTTONUP:
{
POINTS point = MAKEPOINTS(lParam);
_oslistener_mouse_up(cast(view, OSControl), ekGUI_MOUSE_LEFT, (real32_t)point.x, (real32_t)point.y, view->scroll, &view->listeners);
return 0;
}
case WM_RBUTTONUP:
{
POINTS point = MAKEPOINTS(lParam);
_oslistener_mouse_up(cast(view, OSControl), ekGUI_MOUSE_RIGHT, (real32_t)point.x, (real32_t)point.y, view->scroll, &view->listeners);
return 0;
}
case WM_MBUTTONUP:
{
POINTS point = MAKEPOINTS(lParam);
_oslistener_mouse_up(cast(view, OSControl), ekGUI_MOUSE_MIDDLE, (real32_t)point.x, (real32_t)point.y, view->scroll, &view->listeners);
return 0;
}
case WM_MOUSEWHEEL:
if (view->scroll != NULL)
{
gui_scroll_t event = ekGUI_SCROLL_STEP_LEFT;
if (GET_WHEEL_DELTA_WPARAM(wParam) < 0)
event = ekGUI_SCROLL_STEP_RIGHT;
if (_osscrolls_event(view->scroll, ekGUI_VERTICAL, event, FALSE) == TRUE)
InvalidateRect(view->control.hwnd, NULL, FALSE);
}
_oslistener_whell(cast(view, OSControl), wParam, lParam, view->scroll, &view->listeners);
break;
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
if (_oslistener_key_down(cast(view, OSControl), wParam, lParam, &view->listeners) == TRUE)
return 0;
break;
case WM_KEYUP:
case WM_SYSKEYUP:
if (_oslistener_key_up(cast(view, OSControl), wParam, lParam, &view->listeners) == TRUE)
return 0;
break;
case WM_HSCROLL:
case WM_VSCROLL:
{
gui_scroll_t event = _osscroll_event(wParam);
if (event != ENUM_MAX(gui_scroll_t))
{
gui_orient_t orient = uMsg == WM_HSCROLL ? ekGUI_HORIZONTAL : ekGUI_VERTICAL;
if (_osscrolls_event(view->scroll, orient, event, FALSE) == TRUE)
InvalidateRect(view->control.hwnd, NULL, FALSE);
}
return 0;
}
}
return CallWindowProc(view->control.def_wnd_proc, hwnd, uMsg, wParam, lParam);
}
OSView *osview_create(const uint32_t flags)
{
OSView *view = heap_new0(OSView);
DWORD dwStyle = WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
if (flags & ekVIEW_HSCROLL)
dwStyle |= WS_HSCROLL;
if (flags & ekVIEW_VSCROLL)
dwStyle |= WS_VSCROLL;
_oscontrol_init(cast(view, OSControl), PARAM(dwExStyle, WS_EX_NOPARENTNOTIFY), dwStyle, kVIEW_CLASS, 16, 16, i_WndProc, kDEFAULT_PARENT_WINDOW);
view->control.type = ekGUI_TYPE_CUSTOMVIEW;
view->flags = flags;
view->focused = FALSE;
view->allow_tab = FALSE;
SetWindowLongPtr(view->control.hwnd, 0, 0);
if ((flags & ekVIEW_HSCROLL) || (flags & ekVIEW_VSCROLL))
view->scroll = _osscrolls_create(cast(view, OSControl), (bool_t)(flags & ekVIEW_HSCROLL) != 0, (bool_t)(flags & ekVIEW_VSCROLL) != 0);
if (flags & ekVIEW_CONTROL)
{
view->osdraw.button_theme = _osstyleXP_OpenTheme(view->control.hwnd, L"BUTTON");
view->osdraw.list_theme = _osstyleXP_OpenTheme(view->control.hwnd, L"Explorer::ListView");
view->osdraw.header_theme = _osstyleXP_OpenTheme(view->control.hwnd, L"HEADER");
view->osdraw.sort_size.cx = -1;
view->osdraw.sort_size.cy = -1;
if (view->osdraw.list_theme == NULL)
view->osdraw.list_theme = _osstyleXP_OpenTheme(view->control.hwnd, L"ListView");
}
_oslistener_init(&view->listeners);
return view;
}
void osview_destroy(OSView **view)
{
cassert_no_null(view);
cassert_no_null(*view);
_oslistener_remove(&(*view)->listeners);
listener_destroy(&(*view)->OnOverlay);
listener_destroy(&(*view)->OnFocus);
listener_destroy(&(*view)->OnResignFocus);
listener_destroy(&(*view)->OnAcceptFocus);
if ((*view)->dbuffer != NULL)
{
BOOL ok = DeleteObject((*view)->dbuffer);
cassert_unref(ok != 0, ok);
}
if ((*view)->ctx != NULL)
dctx_destroy(&(*view)->ctx);
if ((*view)->scroll != NULL)
_osscrolls_destroy(&(*view)->scroll);
if ((*view)->osdraw.button_theme != NULL)
{
_osstyleXP_CloseTheme((*view)->osdraw.button_theme);
(*view)->osdraw.button_theme = NULL;
}
if ((*view)->osdraw.list_theme != NULL)
{
_osstyleXP_CloseTheme((*view)->osdraw.list_theme);
(*view)->osdraw.list_theme = NULL;
}
if ((*view)->osdraw.header_theme != NULL)
{
_osstyleXP_CloseTheme((*view)->osdraw.header_theme);
(*view)->osdraw.header_theme = NULL;
}
_oscontrol_destroy(&(*view)->control);
heap_delete(view, OSView);
}
void osview_OnDraw(OSView *view, Listener *listener)
{
cassert_no_null(view);
listener_update(&view->listeners.OnDraw, listener);
}
void osview_OnOverlay(OSView *view, Listener *listener)
{
cassert_no_null(view);
listener_update(&view->OnOverlay, listener);
}
void osview_OnEnter(OSView *view, Listener *listener)
{
cassert_no_null(view);
listener_update(&view->listeners.OnEnter, listener);
view->listeners.is_mouse_inside = FALSE;
}
void osview_OnExit(OSView *view, Listener *listener)
{
cassert_no_null(view);
listener_update(&view->listeners.OnExit, listener);
view->listeners.is_mouse_inside = FALSE;
}
void osview_OnMoved(OSView *view, Listener *listener)
{
cassert_no_null(view);
listener_update(&view->listeners.OnMoved, listener);
}
void osview_OnDown(OSView *view, Listener *listener)
{
cassert_no_null(view);
listener_update(&view->listeners.OnDown, listener);
}
void osview_OnUp(OSView *view, Listener *listener)
{
cassert_no_null(view);
listener_update(&view->listeners.OnUp, listener);
}
void osview_OnClick(OSView *view, Listener *listener)
{
cassert_no_null(view);
listener_update(&view->listeners.OnClick, listener);
}
void osview_OnDrag(OSView *view, Listener *listener)
{
cassert_no_null(view);
listener_update(&view->listeners.OnDrag, listener);
}
void osview_OnWheel(OSView *view, Listener *listener)
{
cassert_no_null(view);
listener_update(&view->listeners.OnWheel, listener);
}
void osview_OnKeyDown(OSView *view, Listener *listener)
{
cassert_no_null(view);
listener_update(&view->listeners.OnKeyDown, listener);
}
void osview_OnKeyUp(OSView *view, Listener *listener)
{
cassert_no_null(view);
listener_update(&view->listeners.OnKeyUp, listener);
}
void osview_OnFocus(OSView *view, Listener *listener)
{
cassert_no_null(view);
listener_update(&view->OnFocus, listener);
}
void osview_OnResignFocus(OSView *view, Listener *listener)
{
cassert_no_null(view);
listener_update(&view->OnResignFocus, listener);
}
void osview_OnAcceptFocus(OSView *view, Listener *listener)
{
cassert_no_null(view);
listener_update(&view->OnAcceptFocus, listener);
}
void osview_OnScroll(OSView *view, Listener *listener)
{
cassert_no_null(view);
_osscrolls_OnScroll(view->scroll, listener);
}
void osview_allow_key(OSView *view, const vkey_t key, const uint32_t value)
{
cassert_no_null(view);
cassert_unref(key == ekKEY_TAB, key);
cassert(value == 0 || value == 1);
view->allow_tab = (bool_t)value;
}
void osview_scroll(OSView *view, const real32_t x, const real32_t y)
{
cassert_no_null(view);
_osscrolls_set(view->scroll, x >= 0 ? (uint32_t)x : UINT32_MAX, y >= 0 ? (uint32_t)y : UINT32_MAX, FALSE);
}
void osview_scroll_get(const OSView *view, real32_t *x, real32_t *y)
{
cassert_no_null(view);
if (x != NULL)
*x = (real32_t)_osscrolls_x_pos(view->scroll);
if (y != NULL)
*y = (real32_t)_osscrolls_y_pos(view->scroll);
}
void osview_scroller_size(const OSView *view, real32_t *width, real32_t *height)
{
cassert_no_null(view);
if (width != NULL)
*width = (real32_t)_osscrolls_bar_width(view->scroll, TRUE);
if (height != NULL)
*height = (real32_t)_osscrolls_bar_height(view->scroll, TRUE);
}
void osview_scroller_visible(OSView *view, const bool_t horizontal, const bool_t vertical)
{
cassert_no_null(view);
_osscrolls_visible(view->scroll, horizontal, vertical);
}
void osview_content_size(OSView *view, const real32_t width, const real32_t height, const real32_t line_width, const real32_t line_height)
{
cassert_no_null(view);
_osscrolls_content_size(view->scroll, (uint32_t)width, (uint32_t)height, (uint32_t)line_width, (uint32_t)line_height);
}
real32_t osview_scale_factor(const OSView *view)
{
unref(view);
return 1;
}
void osview_set_need_display(OSView *view)
{
cassert_no_null(view);
InvalidateRect(view->control.hwnd, NULL, FALSE);
}
void *osview_get_native_view(const OSView *view)
{
cassert_no_null(view);
return cast(view->control.hwnd, void);
}
void osview_attach(OSView *view, OSPanel *panel)
{
cassert(view->parent_panel == NULL);
view->parent_panel = panel;
_ospanel_attach_control(panel, cast(view, OSControl));
}
void osview_detach(OSView *view, OSPanel *panel)
{
cassert(view->parent_panel == panel);
view->parent_panel = NULL;
_ospanel_detach_control(panel, cast(view, OSControl));
}
void osview_visible(OSView *view, const bool_t visible)
{
_oscontrol_set_visible(cast(view, OSControl), visible);
}
void osview_enabled(OSView *view, const bool_t enabled)
{
_oscontrol_set_enabled(cast(view, OSControl), enabled);
}
void osview_size(const OSView *view, real32_t *width, real32_t *height)
{
_oscontrol_get_size(cast_const(view, OSControl), width, height);
}
void osview_origin(const OSView *view, real32_t *x, real32_t *y)
{
_oscontrol_get_origin(cast_const(view, OSControl), x, y);
}
void osview_frame(OSView *view, const real32_t x, const real32_t y, const real32_t width, const real32_t height)
{
cassert_no_null(view);
_oscontrol_set_frame(cast(view, OSControl), x, y, width, height);
view->dbuffer_width = (LONG)width;
view->dbuffer_height = (LONG)height;
if (view->dbuffer != NULL)
{
DeleteObject(view->dbuffer);
view->dbuffer = NULL;
}
if (view->scroll != NULL)
_osscrolls_control_size(view->scroll, (uint32_t)width, (uint32_t)height);
}
bool_t _osview_resign_focus(const OSView *view)
{
bool_t resign = TRUE;
cassert_no_null(view);
if (view->OnResignFocus != NULL)
listener_event(view->OnResignFocus, ekGUI_EVENT_FOCUS_RESIGN, view, NULL, &resign, OSView, void, bool_t);
return resign;
}
bool_t _osview_accept_focus(const OSView *view)
{
bool_t accept = TRUE;
cassert_no_null(view);
if (view->OnAcceptFocus != NULL)
listener_event(view->OnAcceptFocus, ekGUI_EVENT_FOCUS_ACCEPT, view, NULL, &accept, OSView, void, bool_t);
return accept;
}
void _osview_focus(OSView *view, const bool_t focus)
{
cassert_no_null(view);
view->focused = focus;
if (view->OnFocus != NULL)
{
bool_t params = focus;
listener_event(view->OnFocus, ekGUI_EVENT_FOCUS, view, ¶ms, NULL, OSView, bool_t, void);
}
if (view->flags & ekVIEW_BORDER)
RedrawWindow(view->control.hwnd, NULL, NULL, RDW_FRAME | RDW_INVALIDATE);
}
bool_t _osview_capture_tab(const OSView *view)
{
cassert_no_null(view);
if (view->listeners.OnKeyDown == NULL)
return FALSE;
return view->allow_tab;
}