latest SDK
This commit is contained in:
77
libPPUI/AutoComplete.cpp
Normal file
77
libPPUI/AutoComplete.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
#include "stdafx.h"
|
||||
#include "AutoComplete.h"
|
||||
#include "pp-COM-macros.h"
|
||||
|
||||
#include <ShlGuid.h> // CLSID_AutoComplete
|
||||
|
||||
#include "CEnumString.h"
|
||||
using PP::CEnumString;
|
||||
|
||||
HRESULT InitializeSimpleAC(HWND edit, IUnknown * vals, DWORD opts) {
|
||||
pfc::com_ptr_t<IAutoComplete> ac;
|
||||
HRESULT hr;
|
||||
hr = CoCreateInstance(CLSID_AutoComplete, NULL, CLSCTX_ALL, IID_IAutoComplete, (void**)ac.receive_ptr());
|
||||
if (FAILED(hr)) {
|
||||
PFC_ASSERT(!"Should not get here - CoCreateInstance/IAutoComplete fail!"); return hr;
|
||||
}
|
||||
hr = ac->Init(edit, vals, NULL, NULL);
|
||||
if (FAILED(hr)) {
|
||||
PFC_ASSERT(!"Should not get here - ac->Init fail!"); return hr;
|
||||
}
|
||||
|
||||
pfc::com_ptr_t<IAutoComplete2> ac2;
|
||||
hr = ac->QueryInterface(ac2.receive_ptr());
|
||||
if (FAILED(hr)) {
|
||||
PFC_ASSERT(!"Should not get here - ac->QueryInterface fail!"); return hr;
|
||||
}
|
||||
hr = ac2->SetOptions(opts);
|
||||
if (FAILED(hr)) {
|
||||
PFC_ASSERT(!"Should not get here - ac2->SetOptions fail!"); return hr;
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
pfc::com_ptr_t<IUnknown> CreateACList(pfc::const_iterator<pfc::string8> valueEnum) {
|
||||
pfc::com_ptr_t<CEnumString> acl = new CEnumString::TImpl();
|
||||
while (valueEnum.is_valid()) {
|
||||
acl->AddStringU(*valueEnum); ++valueEnum;
|
||||
}
|
||||
return acl;
|
||||
}
|
||||
pfc::com_ptr_t<IUnknown> CreateACList(pfc::const_iterator<const char *> valueEnum) {
|
||||
pfc::com_ptr_t<CEnumString> acl = new CEnumString::TImpl();
|
||||
while (valueEnum.is_valid()) {
|
||||
acl->AddStringU(*valueEnum); ++valueEnum;
|
||||
}
|
||||
return acl;
|
||||
}
|
||||
pfc::com_ptr_t<IUnknown> CreateACList(pfc::const_iterator<pfc::string> valueEnum) {
|
||||
pfc::com_ptr_t<CEnumString> acl = new CEnumString::TImpl();
|
||||
while (valueEnum.is_valid()) {
|
||||
acl->AddStringU(valueEnum->ptr()); ++valueEnum;
|
||||
}
|
||||
return acl;
|
||||
}
|
||||
|
||||
pfc::com_ptr_t<IUnknown> CreateACList() {
|
||||
return new CEnumString::TImpl();
|
||||
}
|
||||
|
||||
void CreateACList_AddItem(IUnknown * theList, const char * item) {
|
||||
static_cast<CEnumString*>(theList)->AddStringU(item);
|
||||
}
|
||||
|
||||
HRESULT InitializeEditAC(HWND edit, pfc::const_iterator<pfc::string8> valueEnum, DWORD opts) {
|
||||
pfc::com_ptr_t<IUnknown> acl = CreateACList(valueEnum);
|
||||
return InitializeSimpleAC(edit, acl.get_ptr(), opts);
|
||||
}
|
||||
HRESULT InitializeEditAC(HWND edit, const char * values, DWORD opts) {
|
||||
pfc::com_ptr_t<CEnumString> acl = new CEnumString::TImpl();
|
||||
for (const char * walk = values;;) {
|
||||
const char * next = strchr(walk, '\n');
|
||||
if (next == NULL) { acl->AddStringU(walk, ~0); break; }
|
||||
acl->AddStringU(walk, next - walk);
|
||||
walk = next + 1;
|
||||
}
|
||||
return InitializeSimpleAC(edit, acl.get_ptr(), opts);
|
||||
}
|
||||
12
libPPUI/AutoComplete.h
Normal file
12
libPPUI/AutoComplete.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <ShlDisp.h>
|
||||
|
||||
HRESULT InitializeEditAC(HWND edit, pfc::const_iterator<pfc::string8> valueEnum, DWORD opts = ACO_AUTOAPPEND | ACO_AUTOSUGGEST);
|
||||
HRESULT InitializeEditAC(HWND edit, const char * values, DWORD opts = ACO_AUTOAPPEND | ACO_AUTOSUGGEST);
|
||||
HRESULT InitializeSimpleAC(HWND edit, IUnknown * vals, DWORD opts);
|
||||
pfc::com_ptr_t<IUnknown> CreateACList(pfc::const_iterator<pfc::string8> valueEnum);
|
||||
pfc::com_ptr_t<IUnknown> CreateACList(pfc::const_iterator<const char *> valueEnum);
|
||||
pfc::com_ptr_t<IUnknown> CreateACList(pfc::const_iterator<pfc::string> valueEnum);
|
||||
pfc::com_ptr_t<IUnknown> CreateACList();
|
||||
void CreateACList_AddItem(IUnknown * theList, const char * item);
|
||||
272
libPPUI/CButtonLite.h
Normal file
272
libPPUI/CButtonLite.h
Normal file
@@ -0,0 +1,272 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <vsstyle.h>
|
||||
#include "WTL-PP.h"
|
||||
#include "win32_op.h"
|
||||
|
||||
typedef CWinTraits<WS_CHILD|WS_TABSTOP,0> CButtonLiteTraits;
|
||||
|
||||
class CButtonLite : public CWindowImpl<CButtonLite, CWindow, CButtonLiteTraits > {
|
||||
public:
|
||||
BEGIN_MSG_MAP_EX(CButtonLite)
|
||||
MESSAGE_RANGE_HANDLER_EX(WM_MOUSEFIRST, WM_MOUSELAST, MousePassThru);
|
||||
MSG_WM_SETTEXT(OnSetText)
|
||||
MSG_WM_PAINT( OnPaint )
|
||||
MSG_WM_MOUSEMOVE(OnMouseMove)
|
||||
MSG_WM_LBUTTONDOWN(OnLButtonDown)
|
||||
MSG_WM_SETFOCUS(OnSetFocus)
|
||||
MSG_WM_KILLFOCUS(OnKillFocus)
|
||||
MSG_WM_KEYDOWN(OnKeyDown)
|
||||
MSG_WM_KEYUP(OnKeyUp)
|
||||
MSG_WM_CHAR(OnChar)
|
||||
MSG_WM_ENABLE(OnEnable)
|
||||
MESSAGE_HANDLER_EX(WM_GETDLGCODE, OnGetDlgCode)
|
||||
MSG_WM_SETFONT(OnSetFont)
|
||||
MSG_WM_GETFONT(OnGetFont)
|
||||
MSG_WM_CREATE(OnCreate)
|
||||
END_MSG_MAP()
|
||||
std::function<void () > ClickHandler;
|
||||
|
||||
unsigned Measure() {
|
||||
auto font = myGetFont();
|
||||
LOGFONT lf;
|
||||
WIN32_OP_D(font.GetLogFont(lf));
|
||||
MakeBoldFont( lf );
|
||||
CFont bold;
|
||||
WIN32_OP_D(bold.CreateFontIndirect(&lf));
|
||||
CWindowDC dc(*this);
|
||||
auto oldFont = dc.SelectFont( bold );
|
||||
CSize size (0,0);
|
||||
|
||||
{
|
||||
CString measure;
|
||||
measure = L"#";
|
||||
measure += m_textDrawMe;
|
||||
WIN32_OP_D(dc.GetTextExtent(measure, measure.GetLength(), &size));
|
||||
}
|
||||
|
||||
dc.SelectFont( oldFont );
|
||||
|
||||
return size.cx;
|
||||
}
|
||||
std::function< void (HWND) > TabCycleHandler;
|
||||
std::function< HBRUSH (CDCHandle) > CtlColorHandler;
|
||||
std::function< bool (HWND) > WantTabCheck;
|
||||
CWindow WndCtlColorTarget;
|
||||
|
||||
// Rationale: sometimes you want a different text to be presented to accessibility APIs than actually drawn
|
||||
// For an example, a clear button looks best with a multiplication sign, but the narrator should say "clear" or so when focused
|
||||
void DrawAlternateText( const wchar_t * textDrawMe ) {
|
||||
m_textDrawMe = textDrawMe;
|
||||
}
|
||||
|
||||
protected:
|
||||
CFontHandle m_font;
|
||||
void OnSetFont(HFONT font, BOOL bRedraw) {
|
||||
m_font = font; if (bRedraw) Invalidate();
|
||||
}
|
||||
HFONT OnGetFont() {
|
||||
return m_font;
|
||||
}
|
||||
LRESULT OnGetDlgCode(UINT, WPARAM wp, LPARAM lp) {
|
||||
if ( wp == VK_TAB && TabCycleHandler != NULL) {
|
||||
if ( WantTabCheck == NULL || WantTabCheck(m_hWnd) ) {
|
||||
TabCycleHandler( m_hWnd );
|
||||
return DLGC_WANTTAB;
|
||||
}
|
||||
}
|
||||
SetMsgHandled(FALSE); return 0;
|
||||
}
|
||||
void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) {
|
||||
}
|
||||
void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) {
|
||||
switch(nChar) {
|
||||
case VK_SPACE:
|
||||
case VK_RETURN:
|
||||
TogglePressed(true); break;
|
||||
}
|
||||
}
|
||||
void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) {
|
||||
switch(nChar) {
|
||||
case VK_SPACE:
|
||||
case VK_RETURN:
|
||||
TogglePressed(false);
|
||||
OnClicked();
|
||||
break;
|
||||
}
|
||||
}
|
||||
void OnSetFocus(CWindow wndOld) {
|
||||
m_focused = true; Invalidate();
|
||||
}
|
||||
void OnKillFocus(CWindow wndFocus) {
|
||||
m_focused = false; Invalidate();
|
||||
}
|
||||
CFontHandle myGetFont() {
|
||||
auto f = GetFont();
|
||||
if ( f == NULL ) f = (HFONT) GetStockObject(DEFAULT_GUI_FONT);
|
||||
return f;
|
||||
}
|
||||
static void MakeBoldFont(LOGFONT & lf ) {
|
||||
lf.lfWeight += 300;
|
||||
if (lf.lfWeight > 1000 ) lf.lfWeight = 1000;
|
||||
}
|
||||
|
||||
virtual void DrawBackground( CDCHandle dc, CRect rcClient ) {
|
||||
HBRUSH brush = NULL;
|
||||
if ( IsPressed() ) {
|
||||
CTheme theme;
|
||||
if (theme.OpenThemeData(*this, L"BUTTON" )) {
|
||||
DrawThemeBackground(theme, dc, BP_PUSHBUTTON, PBS_PRESSED, rcClient, rcClient );
|
||||
} else {
|
||||
DrawFrameControl( dc, rcClient, DFC_BUTTON, DFCS_PUSHED );
|
||||
}
|
||||
} else if (m_hot) {
|
||||
CTheme theme;
|
||||
if (theme.OpenThemeData(*this, L"BUTTON")) {
|
||||
DrawThemeBackground(theme, dc, BP_PUSHBUTTON, PBS_HOT, rcClient, rcClient);
|
||||
} else {
|
||||
DrawFrameControl(dc, rcClient, DFC_BUTTON, DFCS_HOT);
|
||||
}
|
||||
} else if ( CtlColorHandler ) {
|
||||
brush = CtlColorHandler( dc );
|
||||
} else {
|
||||
CWindow target = WndCtlColorTarget;
|
||||
if ( target == NULL ) target = GetParent();
|
||||
brush = (HBRUSH) target.SendMessage(WM_CTLCOLORBTN, (WPARAM) dc.m_hDC, (LPARAM) this->m_hWnd );
|
||||
}
|
||||
if ( brush != NULL ) {
|
||||
dc.FillRect( rcClient, brush );
|
||||
dc.SetBkMode( TRANSPARENT );
|
||||
}
|
||||
}
|
||||
|
||||
virtual void OnPaint(CDCHandle) {
|
||||
CPaintDC pdc(*this);
|
||||
|
||||
CRect rcClient;
|
||||
if (! GetClientRect( &rcClient ) ) return;
|
||||
|
||||
auto font = myGetFont();
|
||||
/*
|
||||
CFont fontOverride;
|
||||
if ( IsPressed() ) {
|
||||
LOGFONT lf;
|
||||
font.GetLogFont( lf );
|
||||
MakeBoldFont( lf );
|
||||
fontOverride.CreateFontIndirect( & lf );
|
||||
font = fontOverride;
|
||||
}
|
||||
*/
|
||||
HFONT oldFont = pdc.SelectFont( font );
|
||||
|
||||
DrawBackground( pdc.m_hDC, rcClient );
|
||||
|
||||
pdc.SetBkMode( TRANSPARENT );
|
||||
if ( !IsWindowEnabled() ) {
|
||||
pdc.SetTextColor( ::GetSysColor(COLOR_GRAYTEXT) );
|
||||
} else if ( m_focused ) {
|
||||
pdc.SetTextColor( ::GetSysColor(COLOR_HIGHLIGHT) );
|
||||
}
|
||||
pdc.DrawText( m_textDrawMe, m_textDrawMe.GetLength(), &rcClient, DT_VCENTER | DT_CENTER | DT_SINGLELINE | DT_NOPREFIX );
|
||||
|
||||
pdc.SelectFont( oldFont );
|
||||
}
|
||||
virtual void OnClicked() {
|
||||
if ( ClickHandler ) {
|
||||
ClickHandler();
|
||||
} else {
|
||||
GetParent().PostMessage( WM_COMMAND, MAKEWPARAM( this->GetDlgCtrlID(), BN_CLICKED ), (LPARAM) m_hWnd );
|
||||
}
|
||||
}
|
||||
bool IsPressed() {return m_pressed; }
|
||||
private:
|
||||
int OnCreate(LPCREATESTRUCT lpCreateStruct) {
|
||||
if ( lpCreateStruct->lpszName != nullptr ) this->m_textDrawMe = lpCreateStruct->lpszName;
|
||||
SetMsgHandled(FALSE); return 0;
|
||||
}
|
||||
void OnEnable(BOOL bEnable) {
|
||||
Invalidate(); SetMsgHandled(FALSE);
|
||||
}
|
||||
void ToggleHot( bool bHot ) {
|
||||
if ( bHot != m_hot ) {
|
||||
m_hot = bHot; Invalidate();
|
||||
}
|
||||
}
|
||||
void OnMouseMove(UINT nFlags, CPoint point) {
|
||||
const DWORD maskButtons = MK_LBUTTON | MK_RBUTTON | MK_MBUTTON | MK_XBUTTON1 | MK_XBUTTON2;
|
||||
if ((nFlags & maskButtons) != 0) return;
|
||||
CRect rcClient;
|
||||
if (!GetClientRect(rcClient)) return;
|
||||
if (!rcClient.PtInRect( point ) ) return;
|
||||
ToggleHot( true );
|
||||
SetCaptureEx([=](UINT cMsg, DWORD cFlags, CPoint cPoint) {
|
||||
CRect rcClient;
|
||||
if (!GetClientRect(rcClient)) return false;
|
||||
if ( cMsg == WM_MOUSEWHEEL || cMsg == WM_MOUSEHWHEEL || (cFlags & maskButtons) != 0 || !rcClient.PtInRect(cPoint) ) {
|
||||
ToggleHot(false);
|
||||
SetMsgHandled( FALSE );
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} );
|
||||
}
|
||||
void OnLButtonDown(UINT nFlags, CPoint point) {
|
||||
const DWORD maskButtons = MK_LBUTTON | MK_RBUTTON | MK_MBUTTON | MK_XBUTTON1 | MK_XBUTTON2;
|
||||
if ( ( nFlags & maskButtons ) != MK_LBUTTON ) return;
|
||||
TogglePressed( true );
|
||||
SetCaptureEx([=] (UINT cMsg, DWORD cFlags, CPoint cPoint) {
|
||||
if (cMsg == WM_MOUSEWHEEL || cMsg == WM_MOUSEHWHEEL) {
|
||||
TogglePressed(false);
|
||||
SetMsgHandled(FALSE);
|
||||
return false;
|
||||
}
|
||||
if ( cMsg == WM_LBUTTONUP ) {
|
||||
bool wasPressed = m_pressed;
|
||||
TogglePressed(false);
|
||||
if ( wasPressed ) OnClicked();
|
||||
return false;
|
||||
}
|
||||
CRect rcClient;
|
||||
if (!GetClientRect( rcClient )) return false;
|
||||
if ( (cFlags & maskButtons) != (nFlags & maskButtons ) || ! rcClient.PtInRect( cPoint ) ) {
|
||||
TogglePressed(false);
|
||||
SetMsgHandled( FALSE ); return false;
|
||||
}
|
||||
return true;
|
||||
} );
|
||||
}
|
||||
void TogglePressed( bool bPressed ) {
|
||||
if ( bPressed != m_pressed ) {
|
||||
m_pressed = bPressed; Invalidate();
|
||||
}
|
||||
}
|
||||
int OnSetText(LPCTSTR lpstrText) {
|
||||
m_textDrawMe = lpstrText;
|
||||
Invalidate(); SetMsgHandled(FALSE);
|
||||
return 0;
|
||||
}
|
||||
typedef std::function< bool(UINT, DWORD, CPoint) > CaptureProc_t;
|
||||
void SetCaptureEx( CaptureProc_t proc ) {
|
||||
m_captureProc = proc; SetCapture();
|
||||
}
|
||||
LRESULT MousePassThru(UINT msg, WPARAM wp, LPARAM lp) {
|
||||
auto p = m_captureProc; // create local ref in case something in mid-captureproc clears it
|
||||
if (p) {
|
||||
CPoint pt(lp);
|
||||
if (!p(msg, (DWORD)wp, pt)) {
|
||||
::ReleaseCapture();
|
||||
m_captureProc = nullptr;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
SetMsgHandled(FALSE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
CaptureProc_t m_captureProc;
|
||||
|
||||
bool m_pressed = false, m_focused = false, m_hot = false;
|
||||
CString m_textDrawMe;
|
||||
};
|
||||
194
libPPUI/CDialogResizeHelper.cpp
Normal file
194
libPPUI/CDialogResizeHelper.cpp
Normal file
@@ -0,0 +1,194 @@
|
||||
#include "stdafx.h"
|
||||
#include "CDialogResizeHelper.h"
|
||||
|
||||
#include "win32_op.h"
|
||||
|
||||
static BOOL GetChildWindowRect2(HWND wnd, HWND wndChild, RECT* child) {
|
||||
RECT temp;
|
||||
if (wndChild == NULL) return FALSE;
|
||||
if (!GetWindowRect(wndChild, &temp)) return FALSE;
|
||||
if (!MapWindowPoints(NULL, wnd, (POINT*)&temp, 2)) return FALSE;
|
||||
*child = temp;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL GetChildWindowRect(HWND wnd, UINT id, RECT* child) {
|
||||
return GetChildWindowRect2(wnd, GetDlgItem(wnd, id), child);
|
||||
}
|
||||
|
||||
|
||||
bool CDialogResizeHelper::EvalRect(UINT id, CRect & out) const {
|
||||
for( auto iter = m_runtime.begin(); iter != m_runtime.end(); ++ iter ) {
|
||||
if ( iter->initData.id == id ) {
|
||||
out = _EvalRect(*iter, this->CurrentSize() );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
CRect CDialogResizeHelper::_EvalRect(runtime_t const & rt, CSize wndSize) const {
|
||||
CRect rc = rt.origRect;
|
||||
int delta_x = wndSize.cx - rt.origWindowSize.cx,
|
||||
delta_y = wndSize.cy - rt.origWindowSize.cy;
|
||||
|
||||
const Param & e = rt.initData;
|
||||
rc.left += pfc::rint32(e.snapLeft * delta_x);
|
||||
rc.right += pfc::rint32(e.snapRight * delta_x);
|
||||
rc.top += pfc::rint32(e.snapTop * delta_y);
|
||||
rc.bottom += pfc::rint32(e.snapBottom * delta_y);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
CWindow CDialogResizeHelper::ResolveWnd(runtime_t const & rt) const {
|
||||
if ( rt.userHWND != NULL ) return rt.userHWND;
|
||||
return m_thisWnd.GetDlgItem( rt.initData.id );
|
||||
}
|
||||
|
||||
CSize CDialogResizeHelper::CurrentSize() const {
|
||||
CRect rc;
|
||||
WIN32_OP_D( m_thisWnd.GetClientRect(rc) );
|
||||
return rc.Size();
|
||||
}
|
||||
|
||||
void CDialogResizeHelper::OnSize(UINT, CSize newSize)
|
||||
{
|
||||
if (m_thisWnd != NULL) {
|
||||
HDWP hWinPosInfo = BeginDeferWindowPos((int)(m_runtime.size() + (m_sizeGrip != NULL ? 1 : 0)));
|
||||
for (auto iter = m_runtime.begin(); iter != m_runtime.end(); ++ iter) {
|
||||
CRect rc = _EvalRect(*iter, newSize);
|
||||
hWinPosInfo = DeferWindowPos(hWinPosInfo, ResolveWnd(*iter), 0, rc.left, rc.top, rc.Width(), rc.Height(), SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS);
|
||||
}
|
||||
if (m_sizeGrip != NULL)
|
||||
{
|
||||
RECT rc, rc_grip;
|
||||
if (m_thisWnd.GetClientRect(&rc) && m_sizeGrip.GetWindowRect(&rc_grip)) {
|
||||
DWORD flags = SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOCOPYBITS;
|
||||
if (IsZoomed(m_thisWnd)) flags |= SWP_HIDEWINDOW;
|
||||
else flags |= SWP_SHOWWINDOW;
|
||||
hWinPosInfo = DeferWindowPos(hWinPosInfo, m_sizeGrip, NULL, rc.right - (rc_grip.right - rc_grip.left), rc.bottom - (rc_grip.bottom - rc_grip.top), 0, 0, flags);
|
||||
}
|
||||
}
|
||||
EndDeferWindowPos(hWinPosInfo);
|
||||
//RedrawWindow(m_thisWnd, NULL, NULL, RDW_UPDATENOW | RDW_ALLCHILDREN);
|
||||
}
|
||||
SetMsgHandled(FALSE);
|
||||
}
|
||||
|
||||
void CDialogResizeHelper::OnGetMinMaxInfo(LPMINMAXINFO info) const {
|
||||
CRect r;
|
||||
const DWORD dwStyle = m_thisWnd.GetWindowLong(GWL_STYLE);
|
||||
const DWORD dwExStyle = m_thisWnd.GetWindowLong(GWL_EXSTYLE);
|
||||
if (max_x && max_y)
|
||||
{
|
||||
r.left = 0; r.right = max_x;
|
||||
r.top = 0; r.bottom = max_y;
|
||||
MapDialogRect(m_thisWnd, &r);
|
||||
AdjustWindowRectEx(&r, dwStyle, FALSE, dwExStyle);
|
||||
info->ptMaxTrackSize.x = r.right - r.left;
|
||||
info->ptMaxTrackSize.y = r.bottom - r.top;
|
||||
}
|
||||
if (min_x && min_y)
|
||||
{
|
||||
r.left = 0; r.right = min_x;
|
||||
r.top = 0; r.bottom = min_y;
|
||||
MapDialogRect(m_thisWnd, &r);
|
||||
AdjustWindowRectEx(&r, dwStyle, FALSE, dwExStyle);
|
||||
info->ptMinTrackSize.x = r.right - r.left;
|
||||
info->ptMinTrackSize.y = r.bottom - r.top;
|
||||
}
|
||||
}
|
||||
|
||||
void CDialogResizeHelper::OnInitDialog(CWindow thisWnd) {
|
||||
|
||||
m_thisWnd = thisWnd;
|
||||
const auto origSize = CurrentSize();
|
||||
for( auto initIter = m_initData.begin(); initIter != m_initData.end(); ++ initIter ) {
|
||||
CRect rc;
|
||||
if (GetChildWindowRect(m_thisWnd, initIter->id, &rc)) {
|
||||
runtime_t rt;
|
||||
rt.origRect = rc;
|
||||
rt.initData = * initIter;
|
||||
rt.origWindowSize = origSize;
|
||||
m_runtime.push_back( std::move(rt) );
|
||||
}
|
||||
}
|
||||
AddSizeGrip();
|
||||
SetMsgHandled(FALSE);
|
||||
}
|
||||
void CDialogResizeHelper::OnDestroy() {
|
||||
m_runtime.clear();
|
||||
m_sizeGrip = NULL; m_thisWnd = NULL;
|
||||
SetMsgHandled(FALSE);
|
||||
}
|
||||
|
||||
void CDialogResizeHelper::AddSizeGrip()
|
||||
{
|
||||
if (m_thisWnd != NULL && m_sizeGrip == NULL)
|
||||
{
|
||||
if (m_thisWnd.GetWindowLong(GWL_STYLE) & WS_POPUP) {
|
||||
m_sizeGrip = CreateWindowEx(0, WC_SCROLLBAR, _T(""), WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | SBS_SIZEGRIP | SBS_SIZEBOXBOTTOMRIGHTALIGN,
|
||||
0, 0, CW_USEDEFAULT, CW_USEDEFAULT,
|
||||
m_thisWnd, (HMENU)0, NULL, NULL);
|
||||
if (m_sizeGrip != NULL)
|
||||
{
|
||||
RECT rc, rc_grip;
|
||||
if (m_thisWnd.GetClientRect(&rc) && m_sizeGrip.GetWindowRect(&rc_grip)) {
|
||||
m_sizeGrip.SetWindowPos(NULL, rc.right - (rc_grip.right - rc_grip.left), rc.bottom - (rc_grip.bottom - rc_grip.top), 0, 0, SWP_NOZORDER | SWP_NOSIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CDialogResizeHelper::InitTable(const Param* table, size_t tableSize) {
|
||||
m_initData.assign( table, table+tableSize );
|
||||
}
|
||||
void CDialogResizeHelper::InitTable(const ParamOld * table, size_t tableSize) {
|
||||
m_initData.resize(tableSize);
|
||||
for (size_t walk = 0; walk < tableSize; ++walk) {
|
||||
const ParamOld in = table[walk];
|
||||
Param entry = {};
|
||||
entry.id = table[walk].id;
|
||||
if (in.flags & CDialogResizeHelperCompat::X_MOVE) entry.snapLeft = entry.snapRight = 1;
|
||||
else if (in.flags & CDialogResizeHelperCompat::X_SIZE) entry.snapRight = 1;
|
||||
if (in.flags & CDialogResizeHelperCompat::Y_MOVE) entry.snapTop = entry.snapBottom = 1;
|
||||
else if (in.flags & CDialogResizeHelperCompat::Y_SIZE) entry.snapBottom = 1;
|
||||
m_initData[walk] = entry;
|
||||
}
|
||||
}
|
||||
void CDialogResizeHelper::InitMinMax(const CRect & range) {
|
||||
min_x = range.left; min_y = range.top; max_x = range.right; max_y = range.bottom;
|
||||
}
|
||||
|
||||
void CDialogResizeHelper::AddControl(Param const & initData, CWindow wnd) {
|
||||
CRect rc;
|
||||
if ( wnd == NULL ) {
|
||||
PFC_ASSERT( initData.id != 0 );
|
||||
WIN32_OP_D(GetChildWindowRect(m_thisWnd, initData.id, rc));
|
||||
} else {
|
||||
WIN32_OP_D(GetChildWindowRect2(m_thisWnd, wnd, rc));
|
||||
}
|
||||
|
||||
runtime_t rt;
|
||||
rt.initData = initData;
|
||||
rt.userHWND = wnd;
|
||||
rt.origRect = rc;
|
||||
rt.origWindowSize = this->CurrentSize();
|
||||
m_runtime.push_back( std::move(rt) );
|
||||
}
|
||||
|
||||
bool CDialogResizeHelper::RemoveControl(CWindow wnd) {
|
||||
return pfc::remove_if_t( m_runtime, [wnd] (runtime_t const & rt) {
|
||||
return rt.userHWND == wnd;
|
||||
} ) > 0;
|
||||
}
|
||||
|
||||
bool CDialogResizeHelper::HaveControl(CWindow wnd) const {
|
||||
for( auto i = m_runtime.begin(); i != m_runtime.end(); ++ i ) {
|
||||
if ( i->userHWND == wnd ) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
67
libPPUI/CDialogResizeHelper.h
Normal file
67
libPPUI/CDialogResizeHelper.h
Normal file
@@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
|
||||
#include "CDialogResizeHelperCompat.h"
|
||||
#include <vector>
|
||||
|
||||
class CDialogResizeHelper : public CMessageMap {
|
||||
public:
|
||||
|
||||
typedef CDialogResizeHelperCompat::param ParamOld;
|
||||
|
||||
struct Param {
|
||||
uint32_t id;
|
||||
float snapLeft, snapTop, snapRight, snapBottom;
|
||||
};
|
||||
private:
|
||||
void AddSizeGrip();
|
||||
public:
|
||||
inline void set_min_size(unsigned x, unsigned y) { min_x = x; min_y = y; }
|
||||
inline void set_max_size(unsigned x, unsigned y) { max_x = x; max_y = y; }
|
||||
|
||||
|
||||
BEGIN_MSG_MAP_EX(CDialogResizeHelper)
|
||||
if (uMsg == WM_INITDIALOG) OnInitDialog(hWnd);
|
||||
MSG_WM_SIZE(OnSize)
|
||||
MSG_WM_DESTROY(OnDestroy)
|
||||
MSG_WM_GETMINMAXINFO(OnGetMinMaxInfo)
|
||||
END_MSG_MAP()
|
||||
|
||||
template<typename TParam, size_t paramCount> CDialogResizeHelper(const TParam(&src)[paramCount], CRect const& minMaxRange = CRect(0, 0, 0, 0)) {
|
||||
InitTable(src, paramCount);
|
||||
InitMinMax(minMaxRange);
|
||||
}
|
||||
|
||||
void InitTable(const Param* table, size_t tableSize);
|
||||
void InitTable(const ParamOld * table, size_t tableSize);
|
||||
void InitMinMax(const CRect & range);
|
||||
|
||||
bool EvalRect(UINT id, CRect & out) const;
|
||||
|
||||
//! initData.id may be null, if so specify a non null window handle.
|
||||
void AddControl( Param const & initData, CWindow wnd = NULL );
|
||||
bool RemoveControl( CWindow wnd );
|
||||
bool HaveControl(CWindow wnd) const;
|
||||
private:
|
||||
struct runtime_t {
|
||||
HWND userHWND = 0;
|
||||
CRect origRect;
|
||||
CSize origWindowSize;
|
||||
Param initData = {};
|
||||
};
|
||||
|
||||
CSize CurrentSize() const;
|
||||
CWindow ResolveWnd( runtime_t const & rt ) const;
|
||||
|
||||
CRect _EvalRect(runtime_t const & rt, CSize wndSize) const;
|
||||
void OnGetMinMaxInfo(LPMINMAXINFO lpMMI) const;
|
||||
void OnSize(UINT nType, CSize size);
|
||||
void OnInitDialog(CWindow thisWnd);
|
||||
void OnDestroy();
|
||||
|
||||
|
||||
std::vector<runtime_t> m_runtime;
|
||||
std::vector<Param> m_initData;
|
||||
|
||||
CWindow m_thisWnd, m_sizeGrip;
|
||||
unsigned min_x, min_y, max_x, max_y;
|
||||
};
|
||||
16
libPPUI/CDialogResizeHelperCompat.h
Normal file
16
libPPUI/CDialogResizeHelperCompat.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
class CDialogResizeHelperCompat {
|
||||
public:
|
||||
struct param {
|
||||
unsigned short id;
|
||||
unsigned short flags;
|
||||
};
|
||||
|
||||
enum {
|
||||
X_MOVE = 1, X_SIZE = 2, Y_MOVE = 4, Y_SIZE = 8,
|
||||
XY_MOVE = X_MOVE | Y_MOVE, XY_SIZE = X_SIZE | Y_SIZE,
|
||||
X_MOVE_Y_SIZE = X_MOVE | Y_SIZE, X_SIZE_Y_MOVE = X_SIZE | Y_MOVE,
|
||||
};
|
||||
|
||||
};
|
||||
178
libPPUI/CEditWithButtons.cpp
Normal file
178
libPPUI/CEditWithButtons.cpp
Normal file
@@ -0,0 +1,178 @@
|
||||
#include "stdafx.h"
|
||||
#include "CEditWithButtons.h"
|
||||
|
||||
|
||||
void CEditWithButtons::AddMoreButton(std::function<void()> f) {
|
||||
AddButton(L"more", f, nullptr, L"\x2026");
|
||||
}
|
||||
void CEditWithButtons::AddClearButton(const wchar_t * clearVal, bool bHandleEsc) {
|
||||
std::wstring clearValCopy(clearVal);
|
||||
auto handler = [this, clearValCopy] {
|
||||
this->SetWindowText(clearValCopy.c_str());
|
||||
};
|
||||
auto condition = [clearValCopy](const wchar_t * txt) -> bool {
|
||||
return clearValCopy != txt;
|
||||
};
|
||||
// Present "clear" to accessibility APIs but actually draw a multiplication x sign
|
||||
AddButton(L"clear", handler, condition, L"\x00D7");
|
||||
|
||||
if (bHandleEsc) {
|
||||
this->onEscKey = handler;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void CEditWithButtons::AddButton(const wchar_t * str, handler_t handler, condition_t condition, const wchar_t * drawAlternateText) {
|
||||
Button_t btn;
|
||||
btn.handler = handler;
|
||||
btn.title = str;
|
||||
btn.condition = condition;
|
||||
btn.visible = EvalCondition(btn, nullptr);
|
||||
|
||||
if (drawAlternateText != nullptr) {
|
||||
btn.titleDraw = drawAlternateText;
|
||||
}
|
||||
|
||||
m_buttons.push_back(std::move(btn));
|
||||
RefreshButtons();
|
||||
}
|
||||
|
||||
CRect CEditWithButtons::RectOfButton(const wchar_t * text) {
|
||||
for (auto i = m_buttons.begin(); i != m_buttons.end(); ++i) {
|
||||
if (i->title == text && i->wnd != NULL) {
|
||||
CRect rc;
|
||||
if (i->wnd.GetWindowRect(rc)) return rc;
|
||||
}
|
||||
}
|
||||
return CRect();
|
||||
}
|
||||
|
||||
void CEditWithButtons::TabCycleButtons(HWND wnd) {
|
||||
for (auto i = m_buttons.begin(); i != m_buttons.end(); ++i) {
|
||||
if (i->wnd == wnd) {
|
||||
if (IsShiftPressed()) {
|
||||
// back
|
||||
for (;; ) {
|
||||
if (i == m_buttons.begin()) {
|
||||
TabFocusThis(m_hWnd); break;
|
||||
} else {
|
||||
--i;
|
||||
if (i->visible) {
|
||||
TabFocusThis(i->wnd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// forward
|
||||
for (;; ) {
|
||||
++i;
|
||||
if (i == m_buttons.end()) {
|
||||
TabFocusThis(m_hWnd);
|
||||
TabFocusPrevNext(false);
|
||||
break;
|
||||
} else {
|
||||
if (i->visible) {
|
||||
TabFocusThis(i->wnd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool CEditWithButtons::ButtonWantTab(HWND wnd) {
|
||||
if (IsShiftPressed()) return true;
|
||||
if (m_buttons.size() == 0) return false; // should not be possible
|
||||
auto last = m_buttons.rbegin();
|
||||
if (wnd == last->wnd) return false; // not for last button
|
||||
return true;
|
||||
}
|
||||
bool CEditWithButtons::EvalCondition(Button_t & btn, const wchar_t * newText) {
|
||||
if (!btn.condition) return true;
|
||||
if (newText != nullptr) return btn.condition(newText);
|
||||
TCHAR text[256] = {};
|
||||
GetWindowText(text, 256);
|
||||
text[255] = 0;
|
||||
return btn.condition(text);
|
||||
}
|
||||
void CEditWithButtons::RefreshConditions(const wchar_t * newText) {
|
||||
bool changed = false;
|
||||
for (auto i = m_buttons.begin(); i != m_buttons.end(); ++i) {
|
||||
bool status = EvalCondition(*i, newText);
|
||||
if (status != i->visible) {
|
||||
i->visible = status; changed = true;
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
Layout();
|
||||
}
|
||||
}
|
||||
|
||||
void CEditWithButtons::Layout(CSize size, CFontHandle fontSetMe) {
|
||||
if (m_buttons.size() == 0) return;
|
||||
|
||||
int walk = size.cx;
|
||||
|
||||
HDWP dwp = BeginDeferWindowPos((int)m_buttons.size());
|
||||
for (auto iter = m_buttons.rbegin(); iter != m_buttons.rend(); ++iter) {
|
||||
if (!iter->visible) {
|
||||
if (::GetFocus() == iter->wnd) {
|
||||
this->SetFocus();
|
||||
}
|
||||
::DeferWindowPos(dwp, iter->wnd, NULL, 0, 0, 0, 0, SWP_NOZORDER | SWP_HIDEWINDOW | SWP_NOMOVE | SWP_NOSIZE | SWP_NOCOPYBITS);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (iter->wnd == NULL) {
|
||||
auto b = std::make_shared< CButtonLite >();
|
||||
iter->buttonImpl = b;
|
||||
b->Create(*this, NULL, iter->title.c_str());
|
||||
if (iter->titleDraw.length() > 0) b->DrawAlternateText(iter->titleDraw.c_str());
|
||||
CFontHandle font = fontSetMe;
|
||||
if (font == NULL) font = GetFont();
|
||||
b->SetFont(font);
|
||||
b->ClickHandler = iter->handler;
|
||||
b->CtlColorHandler = [=](CDCHandle dc) -> HBRUSH {
|
||||
return this->OnColorBtn(dc, NULL);
|
||||
};
|
||||
b->TabCycleHandler = [=](HWND wnd) {
|
||||
TabCycleButtons(wnd);
|
||||
};
|
||||
b->WantTabCheck = [=](HWND wnd) -> bool {
|
||||
return ButtonWantTab(wnd);
|
||||
};
|
||||
if (!IsWindowEnabled()) b->EnableWindow(FALSE);
|
||||
iter->wnd = *b;
|
||||
} else if (fontSetMe) {
|
||||
iter->wnd.SetFont(fontSetMe);
|
||||
}
|
||||
|
||||
unsigned delta = MeasureButton(*iter);
|
||||
int left = walk - delta;
|
||||
|
||||
if (iter->wnd != NULL) {
|
||||
CRect rc;
|
||||
rc.top = 0;
|
||||
rc.bottom = size.cy;
|
||||
rc.left = left;
|
||||
rc.right = walk;
|
||||
::DeferWindowPos(dwp, iter->wnd, NULL, rc.left, rc.top, rc.Width(), rc.Height(), SWP_NOZORDER | SWP_SHOWWINDOW | SWP_NOCOPYBITS);
|
||||
}
|
||||
|
||||
walk = left;
|
||||
}
|
||||
EndDeferWindowPos(dwp);
|
||||
this->SetMargins(0, size.cx - walk, EC_RIGHTMARGIN);
|
||||
}
|
||||
|
||||
unsigned CEditWithButtons::MeasureButton(Button_t const & button) {
|
||||
if (m_fixedWidth != 0) return m_fixedWidth;
|
||||
|
||||
return button.buttonImpl->Measure();
|
||||
}
|
||||
213
libPPUI/CEditWithButtons.h
Normal file
213
libPPUI/CEditWithButtons.h
Normal file
@@ -0,0 +1,213 @@
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include "WTL-PP.h"
|
||||
|
||||
#include "CButtonLite.h"
|
||||
|
||||
class CEditWithButtons : public CEditPPHooks {
|
||||
public:
|
||||
CEditWithButtons(::ATL::CMessageMap * hookMM = nullptr, int hookMMID = 0) : CEditPPHooks(hookMM, hookMMID), m_fixedWidth() {}
|
||||
|
||||
enum {
|
||||
MSG_CHECKCONDITIONS = WM_USER+13,
|
||||
MSG_CHECKCONDITIONS_MAGIC1 = 0xaec66f0c,
|
||||
MSG_CHECKCONDITIONS_MAGIC2 = 0x180c2f35,
|
||||
|
||||
};
|
||||
|
||||
BEGIN_MSG_MAP_EX(CEditWithButtons)
|
||||
MSG_WM_SETFONT(OnSetFont)
|
||||
MSG_WM_WINDOWPOSCHANGED(OnPosChanged)
|
||||
MSG_WM_CTLCOLORBTN(OnColorBtn)
|
||||
MESSAGE_HANDLER_EX(WM_GETDLGCODE, OnGetDlgCode)
|
||||
MSG_WM_KEYDOWN(OnKeyDown)
|
||||
MSG_WM_CHAR(OnChar)
|
||||
// MSG_WM_SETFOCUS( OnSetFocus )
|
||||
// MSG_WM_KILLFOCUS( OnKillFocus )
|
||||
MSG_WM_ENABLE(OnEnable)
|
||||
MSG_WM_SETTEXT( OnSetText )
|
||||
MESSAGE_HANDLER_EX(WM_PAINT, CheckConditionsTrigger)
|
||||
MESSAGE_HANDLER_EX(WM_CUT, CheckConditionsTrigger)
|
||||
MESSAGE_HANDLER_EX(WM_PASTE, CheckConditionsTrigger)
|
||||
MESSAGE_HANDLER_EX(MSG_CHECKCONDITIONS, OnCheckConditions)
|
||||
CHAIN_MSG_MAP(CEditPPHooks)
|
||||
END_MSG_MAP()
|
||||
|
||||
void SubclassWindow( HWND wnd ) {
|
||||
CEditPPHooks::SubclassWindow( wnd );
|
||||
this->ModifyStyle(0, WS_CLIPCHILDREN);
|
||||
RefreshButtons();
|
||||
}
|
||||
typedef std::function<void () > handler_t;
|
||||
typedef std::function<bool (const wchar_t*) > condition_t;
|
||||
|
||||
void AddMoreButton( std::function<void ()> f );
|
||||
void AddClearButton( const wchar_t * clearVal = L"", bool bHandleEsc = false);
|
||||
void AddButton( const wchar_t * str, handler_t handler, condition_t condition = nullptr, const wchar_t * drawAlternateText = nullptr );
|
||||
|
||||
static unsigned DefaultFixedWidth() {return GetSystemMetrics(SM_CXVSCROLL) * 3 / 4;}
|
||||
void SetFixedWidth(unsigned fw = DefaultFixedWidth() ) {
|
||||
m_fixedWidth = fw;
|
||||
RefreshButtons();
|
||||
}
|
||||
CRect RectOfButton( const wchar_t * text );
|
||||
|
||||
void Invalidate() {
|
||||
__super::Invalidate();
|
||||
for( auto i = m_buttons.begin(); i != m_buttons.end(); ++i ) {
|
||||
if (i->wnd != NULL) i->wnd.Invalidate();
|
||||
}
|
||||
}
|
||||
void SetShellFolderAutoComplete() {
|
||||
SetShellAutoComplete(SHACF_FILESYS_DIRS);
|
||||
}
|
||||
void SetShellFileAutoComplete() {
|
||||
SetShellAutoComplete(SHACF_FILESYS_ONLY);
|
||||
}
|
||||
void SetShellAutoComplete(DWORD flags) {
|
||||
SHAutoComplete(*this, flags);
|
||||
SetHasAutoComplete();
|
||||
}
|
||||
void SetHasAutoComplete(bool bValue = true) {
|
||||
m_hasAutoComplete = bValue;
|
||||
}
|
||||
void RefreshConditions(const wchar_t * newText = nullptr);
|
||||
private:
|
||||
LRESULT OnCheckConditions( UINT msg, WPARAM wp, LPARAM lp ) {
|
||||
if ( msg == MSG_CHECKCONDITIONS && wp == MSG_CHECKCONDITIONS_MAGIC1 && lp == MSG_CHECKCONDITIONS_MAGIC2 ) {
|
||||
this->RefreshConditions();
|
||||
} else {
|
||||
SetMsgHandled(FALSE);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
bool HaveConditions() {
|
||||
for( auto i = m_buttons.begin(); i != m_buttons.end(); ++i ) {
|
||||
if ( i->condition ) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void PostCheckConditions() {
|
||||
if ( HaveConditions() ) {
|
||||
PostMessage( MSG_CHECKCONDITIONS, MSG_CHECKCONDITIONS_MAGIC1, MSG_CHECKCONDITIONS_MAGIC2 );
|
||||
}
|
||||
}
|
||||
LRESULT CheckConditionsTrigger( UINT, WPARAM, LPARAM ) {
|
||||
PostCheckConditions();
|
||||
SetMsgHandled(FALSE);
|
||||
return 0;
|
||||
}
|
||||
int OnSetText(LPCTSTR lpstrText) {
|
||||
PostCheckConditions();
|
||||
SetMsgHandled(FALSE);
|
||||
return 0;
|
||||
}
|
||||
void OnEnable(BOOL bEnable) {
|
||||
for( auto i = m_buttons.begin(); i != m_buttons.end(); ++ i ) {
|
||||
if ( i->wnd != NULL ) {
|
||||
i->wnd.EnableWindow( bEnable );
|
||||
}
|
||||
}
|
||||
SetMsgHandled(FALSE);
|
||||
}
|
||||
void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) {
|
||||
if (nChar == VK_TAB ) {
|
||||
return;
|
||||
}
|
||||
PostCheckConditions();
|
||||
SetMsgHandled(FALSE);
|
||||
}
|
||||
void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) {
|
||||
if ( nChar == VK_TAB ) {
|
||||
return;
|
||||
}
|
||||
SetMsgHandled(FALSE);
|
||||
}
|
||||
bool canStealTab() {
|
||||
if (m_hasAutoComplete) return false;
|
||||
if (IsShiftPressed()) return false;
|
||||
if (m_buttons.size() == 0) return false;
|
||||
return true;
|
||||
}
|
||||
UINT OnGetDlgCode(UINT, WPARAM wp, LPARAM lp) {
|
||||
if ( wp == VK_TAB && canStealTab() ) {
|
||||
for (auto i = m_buttons.begin(); i != m_buttons.end(); ++ i ) {
|
||||
if ( i->visible ) {
|
||||
TabFocusThis(i->wnd);
|
||||
return DLGC_WANTTAB;
|
||||
}
|
||||
}
|
||||
}
|
||||
SetMsgHandled(FALSE); return 0;
|
||||
}
|
||||
void OnSetFocus(HWND) {
|
||||
this->ModifyStyleEx(0, WS_EX_CONTROLPARENT ); SetMsgHandled(FALSE);
|
||||
}
|
||||
void OnKillFocus(HWND) {
|
||||
this->ModifyStyleEx(WS_EX_CONTROLPARENT, 0 ); SetMsgHandled(FALSE);
|
||||
}
|
||||
HBRUSH OnColorBtn(CDCHandle dc, CButton button) {
|
||||
if ( (this->GetStyle() & ES_READONLY) != 0 || !this->IsWindowEnabled() ) {
|
||||
return (HBRUSH) GetParent().SendMessage( WM_CTLCOLORSTATIC, (WPARAM) dc.m_hDC, (LPARAM) m_hWnd );
|
||||
} else {
|
||||
return (HBRUSH) GetParent().SendMessage( WM_CTLCOLOREDIT, (WPARAM) dc.m_hDC, (LPARAM) m_hWnd );
|
||||
}
|
||||
}
|
||||
void OnPosChanged(LPWINDOWPOS lpWndPos) {
|
||||
Layout(); SetMsgHandled(FALSE);
|
||||
}
|
||||
|
||||
struct Button_t {
|
||||
std::wstring title, titleDraw;
|
||||
handler_t handler;
|
||||
std::shared_ptr<CButtonLite> buttonImpl;
|
||||
CWindow wnd;
|
||||
bool visible;
|
||||
condition_t condition;
|
||||
};
|
||||
void OnSetFont(CFontHandle font, BOOL bRedraw) {
|
||||
CRect rc;
|
||||
if (GetClientRect(&rc)) {
|
||||
Layout(rc.Size(), font);
|
||||
}
|
||||
SetMsgHandled(FALSE);
|
||||
}
|
||||
|
||||
void RefreshButtons() {
|
||||
if ( m_hWnd != NULL && m_buttons.size() > 0 ) {
|
||||
Layout();
|
||||
}
|
||||
}
|
||||
void Layout( ) {
|
||||
CRect rc;
|
||||
if (GetClientRect(&rc)) {
|
||||
Layout(rc.Size(), NULL);
|
||||
}
|
||||
}
|
||||
static bool IsShiftPressed() {
|
||||
return (GetKeyState(VK_SHIFT) & 0x8000) ? true : false;
|
||||
}
|
||||
CWindow FindDialog() {
|
||||
return GetParent();
|
||||
}
|
||||
void TabFocusThis(HWND wnd) {
|
||||
FindDialog().PostMessage(WM_NEXTDLGCTL, (WPARAM) wnd, TRUE );
|
||||
}
|
||||
void TabFocusPrevNext(bool bPrev) {
|
||||
FindDialog().PostMessage(WM_NEXTDLGCTL, bPrev ? TRUE : FALSE, FALSE);
|
||||
}
|
||||
void TabCycleButtons(HWND wnd);
|
||||
|
||||
bool ButtonWantTab( HWND wnd );
|
||||
bool EvalCondition( Button_t & btn, const wchar_t * newText );
|
||||
void Layout(CSize size, CFontHandle fontSetMe);
|
||||
unsigned MeasureButton(Button_t const & button );
|
||||
|
||||
unsigned m_fixedWidth;
|
||||
std::list< Button_t > m_buttons;
|
||||
bool m_hasAutoComplete = false;
|
||||
};
|
||||
99
libPPUI/CEnumString.h
Normal file
99
libPPUI/CEnumString.h
Normal file
@@ -0,0 +1,99 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include "pp-COM-macros.h"
|
||||
|
||||
namespace PP {
|
||||
|
||||
class CEnumString : public IEnumString {
|
||||
public:
|
||||
typedef pfc::chain_list_v2_t<pfc::array_t<TCHAR> > t_data;
|
||||
typedef std::shared_ptr<t_data> shared_t;
|
||||
|
||||
CEnumString(t_data && in) {
|
||||
m_shared = std::make_shared<t_data>(std::move(in));
|
||||
Reset();
|
||||
}
|
||||
CEnumString(const t_data & in) {
|
||||
m_shared = std::make_shared<t_data>(in);
|
||||
Reset();
|
||||
}
|
||||
CEnumString() {
|
||||
m_shared = std::make_shared< t_data >();
|
||||
}
|
||||
|
||||
void SetStrings(t_data && data) {
|
||||
*m_shared = std::move(data);
|
||||
Reset();
|
||||
}
|
||||
|
||||
static pfc::array_t<TCHAR> stringToBuffer(const char * in) {
|
||||
pfc::array_t<TCHAR> arr;
|
||||
arr.set_size(pfc::stringcvt::estimate_utf8_to_wide(in));
|
||||
pfc::stringcvt::convert_utf8_to_wide_unchecked(arr.get_ptr(), in);
|
||||
return arr;
|
||||
}
|
||||
|
||||
void AddString(const TCHAR * in) {
|
||||
m_shared->insert_last()->set_data_fromptr(in, _tcslen(in) + 1);
|
||||
Reset();
|
||||
}
|
||||
void AddStringU(const char * in, t_size len) {
|
||||
pfc::array_t<TCHAR> & arr = *m_shared->insert_last();
|
||||
arr.set_size(pfc::stringcvt::estimate_utf8_to_wide(in, len));
|
||||
pfc::stringcvt::convert_utf8_to_wide(arr.get_ptr(), arr.get_size(), in, len);
|
||||
Reset();
|
||||
}
|
||||
void AddStringU(const char * in) {
|
||||
*m_shared->insert_last() = stringToBuffer(in);
|
||||
Reset();
|
||||
}
|
||||
void ResetStrings() {
|
||||
m_shared->remove_all();
|
||||
Reset();
|
||||
}
|
||||
|
||||
typedef ImplementCOMRefCounter<CEnumString> TImpl;
|
||||
COM_QI_BEGIN()
|
||||
COM_QI_ENTRY(IUnknown)
|
||||
COM_QI_ENTRY(IEnumString)
|
||||
COM_QI_END()
|
||||
|
||||
HRESULT STDMETHODCALLTYPE Next(ULONG celt, LPOLESTR *rgelt, ULONG *pceltFetched) {
|
||||
if (rgelt == NULL) return E_INVALIDARG;
|
||||
ULONG done = 0;
|
||||
while (done < celt && m_walk.is_valid()) {
|
||||
rgelt[done] = CoStrDup(m_walk->get_ptr());
|
||||
++m_walk; ++done;
|
||||
}
|
||||
if (pceltFetched != NULL) *pceltFetched = done;
|
||||
return done == celt ? S_OK : S_FALSE;
|
||||
}
|
||||
|
||||
static TCHAR * CoStrDup(const TCHAR * in) {
|
||||
const size_t lenBytes = (_tcslen(in) + 1) * sizeof(TCHAR);
|
||||
TCHAR * out = reinterpret_cast<TCHAR*>(CoTaskMemAlloc(lenBytes));
|
||||
if (out) memcpy(out, in, lenBytes);
|
||||
return out;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE Skip(ULONG celt) {
|
||||
while (celt > 0) {
|
||||
if (m_walk.is_empty()) return S_FALSE;
|
||||
--celt; ++m_walk;
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE Reset() {
|
||||
m_walk = m_shared->first();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE Clone(IEnumString **ppenum) {
|
||||
*ppenum = new TImpl(*this); return S_OK;
|
||||
}
|
||||
private:
|
||||
shared_t m_shared;
|
||||
t_data::const_iterator m_walk;
|
||||
};
|
||||
}
|
||||
69
libPPUI/CFlashWindow.h
Normal file
69
libPPUI/CFlashWindow.h
Normal file
@@ -0,0 +1,69 @@
|
||||
#pragma once
|
||||
|
||||
#include "win32_op.h"
|
||||
|
||||
typedef CWinTraits<WS_POPUP,WS_EX_TRANSPARENT|WS_EX_LAYERED|WS_EX_TOPMOST|WS_EX_TOOLWINDOW> CFlashWindowTraits;
|
||||
|
||||
class CFlashWindow : public CWindowImpl<CFlashWindow,CWindow,CFlashWindowTraits> {
|
||||
public:
|
||||
void Activate(CWindow parent) {
|
||||
ShowAbove(parent);
|
||||
m_tickCount = 0;
|
||||
SetTimer(KTimerID, 500);
|
||||
}
|
||||
void Deactivate() throw() {
|
||||
ShowWindow(SW_HIDE); KillTimer(KTimerID);
|
||||
}
|
||||
|
||||
void ShowAbove(CWindow parent) {
|
||||
if (m_hWnd == NULL) {
|
||||
WIN32_OP( Create(NULL) != NULL );
|
||||
}
|
||||
CRect rect;
|
||||
WIN32_OP_D( parent.GetWindowRect(rect) );
|
||||
WIN32_OP_D( SetWindowPos(NULL,rect,SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW) );
|
||||
m_parent = parent;
|
||||
}
|
||||
|
||||
void CleanUp() throw() {
|
||||
if (m_hWnd != NULL) DestroyWindow();
|
||||
}
|
||||
|
||||
BEGIN_MSG_MAP_EX(CFlashWindow)
|
||||
MSG_WM_CREATE(OnCreate)
|
||||
MSG_WM_TIMER(OnTimer)
|
||||
MSG_WM_DESTROY(OnDestroy)
|
||||
END_MSG_MAP()
|
||||
|
||||
DECLARE_WND_CLASS_EX(TEXT("{2E124D52-131F-4004-A569-2316615BE63F}"),0,COLOR_HIGHLIGHT);
|
||||
private:
|
||||
void OnDestroy() throw() {
|
||||
KillTimer(KTimerID);
|
||||
}
|
||||
enum {
|
||||
KTimerID = 0x47f42dd0
|
||||
};
|
||||
void OnTimer(WPARAM id) {
|
||||
if (id == KTimerID) {
|
||||
switch(++m_tickCount) {
|
||||
case 1:
|
||||
ShowWindow(SW_HIDE);
|
||||
break;
|
||||
case 2:
|
||||
ShowAbove(m_parent);
|
||||
break;
|
||||
case 3:
|
||||
ShowWindow(SW_HIDE);
|
||||
KillTimer(KTimerID);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
LRESULT OnCreate(LPCREATESTRUCT) throw() {
|
||||
SetLayeredWindowAttributes(*this,0,128,LWA_ALPHA);
|
||||
return 0;
|
||||
}
|
||||
CWindow m_parent;
|
||||
uint32_t m_tickCount;
|
||||
};
|
||||
|
||||
15
libPPUI/CHeaderCtrlEx.h
Normal file
15
libPPUI/CHeaderCtrlEx.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
class CHeaderCtrlEx : public CHeaderCtrl {
|
||||
public:
|
||||
CHeaderCtrlEx(HWND wnd = NULL) : CHeaderCtrl(wnd) {}
|
||||
CHeaderCtrlEx const & operator=(HWND wnd) { m_hWnd = wnd; return *this; }
|
||||
|
||||
// Column sort marker operations
|
||||
// If they appear to have no effect, you're probably missing Common Controls 6 manifest, see link-CommonControls6.h
|
||||
DWORD GetItemFormat(int iItem);
|
||||
void SetItemFormat(int iItem, DWORD flags);
|
||||
void SetItemSort(int iItem, int direction);
|
||||
void SetSingleItemSort(int iItem, int direction);
|
||||
void ClearSort();
|
||||
};
|
||||
47
libPPUI/CIconOverlayWindow.h
Normal file
47
libPPUI/CIconOverlayWindow.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include "win32_op.h"
|
||||
|
||||
typedef CWinTraits<WS_POPUP,WS_EX_LAYERED> _COverlayWindowTraits;
|
||||
|
||||
class CIconOverlayWindow : public CWindowImpl<CIconOverlayWindow,CWindow,_COverlayWindowTraits> {
|
||||
public:
|
||||
DECLARE_WND_CLASS_EX(TEXT("{384298D0-4370-4f9b-9C36-49FC1A396DC7}"),0,(-1));
|
||||
|
||||
void AttachIcon(HICON p_icon) {m_icon = p_icon;}
|
||||
bool HaveIcon() const {return m_icon != NULL;}
|
||||
|
||||
enum {
|
||||
ColorKey = 0xc0ffee
|
||||
};
|
||||
|
||||
BEGIN_MSG_MAP_EX(CIconOverlayWindow)
|
||||
MESSAGE_HANDLER(WM_CREATE,OnCreate);
|
||||
MESSAGE_HANDLER(WM_PAINT,OnPaint);
|
||||
MESSAGE_HANDLER(WM_ERASEBKGND,OnEraseBkgnd);
|
||||
END_MSG_MAP()
|
||||
private:
|
||||
LRESULT OnCreate(UINT,WPARAM,LPARAM,BOOL&) {
|
||||
::SetLayeredWindowAttributes(*this,ColorKey,0,LWA_COLORKEY);
|
||||
return 0;
|
||||
}
|
||||
LRESULT OnEraseBkgnd(UINT,WPARAM p_wp,LPARAM,BOOL& bHandled) {
|
||||
CRect rcClient;
|
||||
WIN32_OP_D( GetClientRect(rcClient) );
|
||||
CDCHandle((HDC)p_wp).FillSolidRect(rcClient,ColorKey);
|
||||
return 1;
|
||||
}
|
||||
LRESULT OnPaint(UINT,WPARAM,LPARAM,BOOL& bHandled) {
|
||||
if (m_icon != NULL) {
|
||||
CPaintDC dc(*this);
|
||||
CRect client;
|
||||
WIN32_OP_D( GetClientRect(&client) );
|
||||
dc.DrawIconEx(0,0,m_icon,client.right,client.bottom);
|
||||
//CDCHandle(ps.hdc).DrawIcon(0,0,m_icon);
|
||||
} else {
|
||||
bHandled = FALSE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
CIcon m_icon;
|
||||
};
|
||||
744
libPPUI/CListAccessible.cpp
Normal file
744
libPPUI/CListAccessible.cpp
Normal file
@@ -0,0 +1,744 @@
|
||||
#include "stdafx.h"
|
||||
#include "CListAccessible.h"
|
||||
|
||||
#include "pp-COM-macros.h"
|
||||
|
||||
static size_t IndexFromChildId(LONG id) {return (size_t)(id-1);}
|
||||
static LONG ChildIdFromIndex(size_t index) {return (LONG)(index+1);}
|
||||
|
||||
class IEnumVARIANT_selection : public ImplementCOMRefCounter<IEnumVARIANT> {
|
||||
public:
|
||||
IEnumVARIANT_selection(const LONG * data,size_t dataCount, ULONG pos = 0) : m_pos(pos) {
|
||||
m_data.set_data_fromptr(data,dataCount);
|
||||
}
|
||||
|
||||
COM_QI_BEGIN()
|
||||
COM_QI_ENTRY(IEnumVARIANT)
|
||||
COM_QI_ENTRY(IUnknown)
|
||||
COM_QI_END()
|
||||
|
||||
// IEnumVARIANT methods
|
||||
STDMETHOD(Next)(ULONG celt, VARIANT *rgVar, ULONG *pCeltFetched);
|
||||
STDMETHOD(Skip)(ULONG celt);
|
||||
STDMETHOD(Reset)();
|
||||
STDMETHOD(Clone)(IEnumVARIANT **ppEnum);
|
||||
private:
|
||||
pfc::array_t<LONG> m_data;
|
||||
ULONG m_pos;
|
||||
};
|
||||
|
||||
|
||||
HRESULT IEnumVARIANT_selection::Next(ULONG celt, VARIANT *rgVar, ULONG *pceltFetched) {
|
||||
if (rgVar == NULL) return E_INVALIDARG;
|
||||
|
||||
if (pceltFetched) *pceltFetched = 0;
|
||||
|
||||
ULONG n;
|
||||
for (n = 0; n < celt; n++) VariantInit(&rgVar[n]);
|
||||
|
||||
for (n = 0; n < celt && m_pos+n < m_data.get_size(); n++) {
|
||||
rgVar[n].vt = VT_I4;
|
||||
rgVar[n].lVal = m_data[m_pos+n];
|
||||
}
|
||||
|
||||
if (pceltFetched) *pceltFetched = n;
|
||||
|
||||
m_pos += n;
|
||||
|
||||
return n == celt ? S_OK : S_FALSE;
|
||||
}
|
||||
|
||||
HRESULT IEnumVARIANT_selection::Skip(ULONG celt) {
|
||||
m_pos += celt;
|
||||
if (m_pos > m_data.get_size()) {
|
||||
m_pos = (ULONG)m_data.get_size();
|
||||
return S_FALSE;
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT IEnumVARIANT_selection::Reset() {
|
||||
m_pos = 0;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT IEnumVARIANT_selection::Clone(IEnumVARIANT **ppEnum)
|
||||
{
|
||||
if (ppEnum == NULL)
|
||||
return E_INVALIDARG;
|
||||
|
||||
IEnumVARIANT * var;
|
||||
try {
|
||||
var = new IEnumVARIANT_selection(m_data.get_ptr(), m_data.get_size(), m_pos);
|
||||
} catch(std::bad_alloc) {return E_OUTOFMEMORY;}
|
||||
|
||||
var->AddRef();
|
||||
*ppEnum = var;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
namespace {
|
||||
class WeakRef {
|
||||
public:
|
||||
WeakRef( CListAccessible * ptr_, std::shared_ptr<bool> ks_ ) : ptr(ptr_), ks(ks_) {}
|
||||
|
||||
CListAccessible * operator->() const { return ptr; }
|
||||
|
||||
bool IsEmpty() const {
|
||||
return * ks;
|
||||
}
|
||||
private:
|
||||
CListAccessible * ptr;
|
||||
std::shared_ptr<bool> ks;
|
||||
};
|
||||
}
|
||||
|
||||
class IAccessible_CListControl : public ImplementCOMRefCounter<IAccessible> {
|
||||
public:
|
||||
IAccessible_CListControl(WeakRef owner) : m_owner(owner) {}
|
||||
COM_QI_BEGIN()
|
||||
COM_QI_ENTRY(IUnknown)
|
||||
COM_QI_ENTRY(IDispatch)
|
||||
COM_QI_ENTRY(IAccessible)
|
||||
COM_QI_END()
|
||||
|
||||
//IDispatch
|
||||
STDMETHOD(GetTypeInfoCount)(UINT * pcTInfo);
|
||||
STDMETHOD(GetTypeInfo)(UINT iTInfo, LCID lcid, ITypeInfo ** ppTInfo);
|
||||
STDMETHOD(GetIDsOfNames)(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId);
|
||||
STDMETHOD(Invoke)(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS * pDispParams, VARIANT * pVarResult, EXCEPINFO * pExcepInfo, UINT * puArgErr);
|
||||
//IAccessible
|
||||
STDMETHOD(get_accParent)(IDispatch **ppdispParent); // required
|
||||
STDMETHOD(get_accChildCount)(long *pcountChildren);
|
||||
STDMETHOD(get_accChild)(VARIANT varChild, IDispatch **ppdispChild);
|
||||
STDMETHOD(get_accName)(VARIANT varChild, BSTR *pszName); // required
|
||||
STDMETHOD(get_accValue)(VARIANT varChild, BSTR *pszValue);
|
||||
STDMETHOD(get_accDescription)(VARIANT varChild, BSTR *pszDescription);
|
||||
STDMETHOD(get_accRole)(VARIANT varChild, VARIANT *pvarRole); // required
|
||||
STDMETHOD(get_accState)(VARIANT varChild, VARIANT *pvarState); // required
|
||||
STDMETHOD(get_accHelp)(VARIANT varChild, BSTR *pszHelp);
|
||||
STDMETHOD(get_accHelpTopic)(BSTR *pszHelpFile, VARIANT varChild, long *pidTopic);
|
||||
STDMETHOD(get_accKeyboardShortcut)(VARIANT varChild, BSTR *pszKeyboardShortcut);
|
||||
STDMETHOD(get_accFocus)(VARIANT *pvarChild);
|
||||
STDMETHOD(get_accSelection)(VARIANT *pvarChildren);
|
||||
STDMETHOD(get_accDefaultAction)(VARIANT varChild, BSTR *pszDefaultAction);
|
||||
STDMETHOD(accSelect)(long flagsSelect, VARIANT varChild);
|
||||
STDMETHOD(accLocation)(long *pxLeft, long *pyTop, long *pcxWidth, long *pcyHeight, VARIANT varChild); // required
|
||||
STDMETHOD(accNavigate)(long navDir, VARIANT varStart, VARIANT *pvarEndUpAt);
|
||||
STDMETHOD(accHitTest)(long xLeft, long yTop, VARIANT *pvarChild);
|
||||
STDMETHOD(accDoDefaultAction)(VARIANT varChild);
|
||||
STDMETHOD(put_accName)(VARIANT varChild, BSTR szName);
|
||||
STDMETHOD(put_accValue)(VARIANT varChild, BSTR szValue);
|
||||
private:
|
||||
const WeakRef m_owner;
|
||||
};
|
||||
|
||||
void CListAccessible::AccInitialize(CWindow wnd) {
|
||||
m_wnd = wnd;
|
||||
}
|
||||
void CListAccessible::AccCleanup() {
|
||||
if (m_interface.is_valid()) {
|
||||
NotifyWinEvent(EVENT_OBJECT_DESTROY, m_wnd, OBJID_CLIENT, CHILDID_SELF);
|
||||
m_interface.release();
|
||||
}
|
||||
m_wnd = NULL;
|
||||
}
|
||||
|
||||
LRESULT CListAccessible::AccGetObject(WPARAM wp,LPARAM lp) {
|
||||
const WPARAM dwFlags = wp;
|
||||
const LPARAM dwObjId = lp;
|
||||
|
||||
if (dwObjId == OBJID_CLIENT)
|
||||
{
|
||||
if (m_interface.is_empty())
|
||||
{
|
||||
try {
|
||||
m_interface = new IAccessible_CListControl( WeakRef(this, m_killSwitch) );
|
||||
} catch(...) {
|
||||
//bah
|
||||
return DefWindowProc(m_wnd,WM_GETOBJECT,wp,lp);
|
||||
}
|
||||
NotifyWinEvent(EVENT_OBJECT_CREATE, m_wnd, OBJID_CLIENT, CHILDID_SELF);
|
||||
}
|
||||
return LresultFromObject(IID_IAccessible, dwFlags, m_interface.get_ptr());
|
||||
}
|
||||
else return DefWindowProc(m_wnd,WM_GETOBJECT,wp,lp);
|
||||
}
|
||||
|
||||
void CListAccessible::AccItemNamesChanged(pfc::bit_array const & mask) {
|
||||
this->AccRefreshItems(mask, EVENT_OBJECT_NAMECHANGE);
|
||||
}
|
||||
void CListAccessible::AccReloadItems(pfc::bit_array const & mask) {
|
||||
this->AccRefreshItems(mask, EVENT_OBJECT_NAMECHANGE);
|
||||
}
|
||||
void CListAccessible::AccStateChange(pfc::bit_array const & mask) {
|
||||
this->AccRefreshItems(mask, EVENT_OBJECT_STATECHANGE);
|
||||
}
|
||||
void CListAccessible::AccStateChange(size_t index) {
|
||||
this->AccStateChange(pfc::bit_array_one(index));
|
||||
}
|
||||
void CListAccessible::AccRefreshItems(pfc::bit_array const & affected, UINT what) {
|
||||
if (m_wnd == NULL || m_interface.is_empty()) return;
|
||||
//if (GetFocus() != m_hWnd) return;
|
||||
const size_t total = AccGetItemCount();
|
||||
for (size_t walk = affected.find_first(true, 0, total); walk < total; walk = affected.find_next(true, walk, total)) {
|
||||
NotifyWinEvent(what, m_wnd, OBJID_CLIENT, ChildIdFromIndex(walk));
|
||||
}
|
||||
}
|
||||
void CListAccessible::AccItemLayoutChanged() {
|
||||
if (m_wnd == NULL || m_interface.is_empty()) return;
|
||||
NotifyWinEvent(EVENT_OBJECT_REORDER, m_wnd, OBJID_CLIENT, CHILDID_SELF);
|
||||
}
|
||||
void CListAccessible::AccFocusItemChanged(size_t index) {
|
||||
if (m_wnd == NULL || m_interface.is_empty()) return;
|
||||
if (GetFocus() != m_wnd) return;
|
||||
NotifyWinEvent(EVENT_OBJECT_FOCUS, m_wnd, OBJID_CLIENT, index != SIZE_MAX ? ChildIdFromIndex(index) : CHILDID_SELF);
|
||||
}
|
||||
void CListAccessible::AccFocusOtherChanged(size_t index) {
|
||||
if (m_wnd == NULL || m_interface.is_empty()) return;
|
||||
if (GetFocus() != m_wnd) return;
|
||||
const size_t itemCount = this->AccGetItemCount();
|
||||
index += itemCount;
|
||||
NotifyWinEvent(EVENT_OBJECT_FOCUS, m_wnd, OBJID_CLIENT, index != SIZE_MAX ? ChildIdFromIndex(index) : CHILDID_SELF);
|
||||
}
|
||||
void CListAccessible::AccSelectionChanged(const pfc::bit_array & affected, const pfc::bit_array & state) {
|
||||
if (m_wnd == NULL || m_interface.is_empty()) return;
|
||||
//if (GetFocus() != m_wnd) return;
|
||||
const size_t itemCount = this->AccGetItemCount();
|
||||
|
||||
const size_t limit = 20;
|
||||
if (affected.calc_count(true, 0, itemCount, limit) == limit) {
|
||||
NotifyWinEvent(EVENT_OBJECT_SELECTIONWITHIN, m_wnd, OBJID_CLIENT, CHILDID_SELF);
|
||||
} else for(size_t walk = affected.find_first(true,0,itemCount); walk < itemCount; walk = affected.find_next(true,walk,itemCount)) {
|
||||
NotifyWinEvent(state[walk] ? EVENT_OBJECT_SELECTIONADD : EVENT_OBJECT_SELECTIONREMOVE, m_wnd, OBJID_CLIENT, ChildIdFromIndex(walk));
|
||||
}
|
||||
}
|
||||
void CListAccessible::AccLocationChange() {
|
||||
if (m_wnd == NULL || m_interface.is_empty()) return;
|
||||
NotifyWinEvent(EVENT_OBJECT_LOCATIONCHANGE,m_wnd,OBJID_CLIENT, CHILDID_SELF);
|
||||
}
|
||||
|
||||
HRESULT IAccessible_CListControl::GetTypeInfoCount(UINT * pcTInfo) {
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
HRESULT IAccessible_CListControl::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo ** ppTInfo) {
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
HRESULT IAccessible_CListControl::GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) {
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
HRESULT IAccessible_CListControl::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS * pDispParams, VARIANT * pVarResult, EXCEPINFO * pExcepInfo, UINT * puArgErr) {
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
HRESULT IAccessible_CListControl::get_accParent(IDispatch **ppdispParent) // required
|
||||
{
|
||||
if (ppdispParent == NULL) return E_INVALIDARG;
|
||||
if (m_owner.IsEmpty()) return E_FAIL;
|
||||
|
||||
*ppdispParent = NULL;
|
||||
HRESULT hResult = AccessibleObjectFromWindow(m_owner->AccGetWnd(), OBJID_WINDOW, IID_IDispatch, (void**)ppdispParent);
|
||||
return (hResult == S_OK) ? S_OK : S_FALSE;
|
||||
}
|
||||
|
||||
HRESULT IAccessible_CListControl::get_accChildCount(long *pcountChildren) {
|
||||
if (pcountChildren == NULL) return E_INVALIDARG;
|
||||
if (m_owner.IsEmpty()) return E_FAIL;
|
||||
*pcountChildren = (long)( m_owner->AccGetItemCount() + m_owner->AccGetOtherCount() );
|
||||
return S_OK;
|
||||
}
|
||||
HRESULT IAccessible_CListControl::get_accChild(VARIANT varChild, IDispatch **ppdispChild) {
|
||||
if (varChild.vt != VT_I4) return E_INVALIDARG;
|
||||
if (ppdispChild == NULL) return E_INVALIDARG;
|
||||
if (varChild.lVal == CHILDID_SELF) {
|
||||
*ppdispChild = this; AddRef();
|
||||
return S_OK;
|
||||
} else {
|
||||
if (m_owner.IsEmpty()) return E_FAIL;
|
||||
const size_t index = IndexFromChildId(varChild.lVal);
|
||||
const size_t itemCount = m_owner->AccGetItemCount();
|
||||
if (index < itemCount) {
|
||||
return S_FALSE;
|
||||
} else if (index < itemCount + m_owner->AccGetOtherCount()) {
|
||||
CWindow wnd = m_owner->AccGetOtherChildWnd(index - itemCount);
|
||||
if (wnd == NULL) return S_FALSE;
|
||||
if (AccessibleObjectFromWindow(wnd, OBJID_WINDOW, IID_IDispatch, (void**)ppdispChild) != S_OK) return S_FALSE;
|
||||
return S_OK;
|
||||
} else {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
HRESULT IAccessible_CListControl::get_accName(VARIANT varChild, BSTR *pszName) // required
|
||||
{
|
||||
if (varChild.vt != VT_I4) return E_INVALIDARG;
|
||||
if (pszName == NULL) return E_INVALIDARG;
|
||||
if (m_owner.IsEmpty()) return E_FAIL;
|
||||
pfc::string8_fastalloc name;
|
||||
if (varChild.lVal == CHILDID_SELF) {
|
||||
m_owner->AccGetName(name);
|
||||
} else {
|
||||
const size_t index = IndexFromChildId(varChild.lVal);
|
||||
const size_t itemCount = m_owner->AccGetItemCount();
|
||||
if (index < itemCount) {
|
||||
m_owner->AccGetItemName(index,name);
|
||||
} else if (index < itemCount + m_owner->AccGetOtherCount()) {
|
||||
m_owner->AccGetOtherName(index - itemCount, name);
|
||||
} else {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
}
|
||||
*pszName = SysAllocString(pfc::stringcvt::string_wide_from_utf8(name));
|
||||
return S_OK;
|
||||
}
|
||||
HRESULT IAccessible_CListControl::get_accValue(VARIANT varChild, BSTR *pszValue) {
|
||||
if (varChild.vt != VT_I4)
|
||||
return E_INVALIDARG;
|
||||
if (pszValue == NULL)
|
||||
return E_INVALIDARG;
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT IAccessible_CListControl::get_accDescription(VARIANT varChild, BSTR *pszDescription) {
|
||||
if (varChild.vt != VT_I4) return E_INVALIDARG;
|
||||
if (pszDescription == NULL) return E_INVALIDARG;
|
||||
if (varChild.lVal == CHILDID_SELF) return S_FALSE;
|
||||
if (m_owner.IsEmpty()) return E_FAIL;
|
||||
|
||||
*pszDescription = NULL;
|
||||
|
||||
const size_t itemCount = m_owner->AccGetItemCount();
|
||||
const size_t index = IndexFromChildId(varChild.lVal);
|
||||
|
||||
pfc::string_formatter temp;
|
||||
if (index < itemCount) {
|
||||
if (!m_owner->AccGetItemDescription(index, temp)) return S_FALSE;
|
||||
} else if (index < itemCount + m_owner->AccGetOtherCount()) {
|
||||
if (!m_owner->AccGetOtherDescription(index - itemCount, temp)) return S_FALSE;
|
||||
} else {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
*pszDescription = SysAllocString(pfc::stringcvt::string_os_from_utf8(temp));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT IAccessible_CListControl::get_accRole(VARIANT varChild, VARIANT *pvarRole) // required
|
||||
{
|
||||
if (varChild.vt != VT_I4) return E_INVALIDARG;
|
||||
if (pvarRole == NULL) return E_INVALIDARG;
|
||||
if (m_owner.IsEmpty()) return E_FAIL;
|
||||
VariantClear(pvarRole);
|
||||
pvarRole->vt = VT_I4;
|
||||
if (varChild.lVal == CHILDID_SELF) {
|
||||
pvarRole->lVal = ROLE_SYSTEM_LIST;
|
||||
} else {
|
||||
const size_t itemCount = m_owner->AccGetItemCount();
|
||||
const size_t index = IndexFromChildId(varChild.lVal);
|
||||
if (index < itemCount) {
|
||||
pvarRole->lVal = m_owner->AccGetItemRole( index );
|
||||
} else if (index < itemCount + m_owner->AccGetOtherCount()) {
|
||||
pvarRole->lVal = m_owner->AccGetOtherRole(index - itemCount);
|
||||
} else {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT IAccessible_CListControl::get_accState(VARIANT varChild, VARIANT *pvarState) // required
|
||||
{
|
||||
if (varChild.vt != VT_I4) return E_INVALIDARG;
|
||||
if (pvarState == NULL) return E_INVALIDARG;
|
||||
if (m_owner.IsEmpty()) return E_FAIL;
|
||||
|
||||
VariantClear(pvarState);
|
||||
pvarState->vt = VT_I4;
|
||||
|
||||
if (varChild.lVal == CHILDID_SELF) {
|
||||
pvarState->lVal = /*STATE_SYSTEM_NORMAL*/ 0;
|
||||
if (GetFocus() == m_owner->AccGetWnd())
|
||||
pvarState->lVal |= STATE_SYSTEM_FOCUSED;
|
||||
}
|
||||
else
|
||||
{
|
||||
const size_t index = IndexFromChildId(varChild.lVal);
|
||||
const size_t itemCount = m_owner->AccGetItemCount();
|
||||
if (index < itemCount) {
|
||||
pvarState->lVal = STATE_SYSTEM_MULTISELECTABLE | STATE_SYSTEM_SELECTABLE;
|
||||
if (GetFocus() == m_owner->AccGetWnd()) {
|
||||
pvarState->lVal |= STATE_SYSTEM_FOCUSABLE;
|
||||
if (m_owner->AccGetFocusItem() == index) pvarState->lVal |= STATE_SYSTEM_FOCUSED;
|
||||
}
|
||||
if (m_owner->AccIsItemSelected(index)) pvarState->lVal |= STATE_SYSTEM_SELECTED;
|
||||
if (!m_owner->AccIsItemVisible(index)) pvarState->lVal |= STATE_SYSTEM_OFFSCREEN | STATE_SYSTEM_INVISIBLE;
|
||||
if (m_owner->AccIsItemChecked(index)) pvarState->lVal |= STATE_SYSTEM_CHECKED;
|
||||
} else if (index < itemCount + m_owner->AccGetOtherCount()) {
|
||||
const size_t indexO = index - itemCount;
|
||||
pvarState->lVal = 0;
|
||||
if (m_owner->AccIsOtherFocusable(indexO) && GetFocus() == m_owner->AccGetWnd()) {
|
||||
pvarState->lVal |= STATE_SYSTEM_FOCUSABLE;
|
||||
if (m_owner->AccGetFocusOther() == indexO) pvarState->lVal |= STATE_SYSTEM_FOCUSED;
|
||||
}
|
||||
if (!m_owner->AccIsOtherVisible(indexO)) pvarState->lVal |= STATE_SYSTEM_OFFSCREEN | STATE_SYSTEM_INVISIBLE;
|
||||
} else {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
HRESULT IAccessible_CListControl::get_accHelp(VARIANT varChild, BSTR *pszHelp)
|
||||
{
|
||||
if (varChild.vt != VT_I4)
|
||||
return E_INVALIDARG;
|
||||
if (pszHelp == NULL)
|
||||
return E_INVALIDARG;
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT IAccessible_CListControl::get_accHelpTopic(BSTR *pszHelpFile, VARIANT varChild, long *pidTopic)
|
||||
{
|
||||
if (varChild.vt != VT_I4)
|
||||
return E_INVALIDARG;
|
||||
if (pszHelpFile == NULL)
|
||||
return E_INVALIDARG;
|
||||
if (pidTopic == NULL)
|
||||
return E_INVALIDARG;
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT IAccessible_CListControl::get_accKeyboardShortcut(VARIANT varChild, BSTR *pszKeyboardShortcut)
|
||||
{
|
||||
if (pszKeyboardShortcut == NULL)
|
||||
return E_INVALIDARG;
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT IAccessible_CListControl::get_accFocus(VARIANT *pvarChild)
|
||||
{
|
||||
if (pvarChild == NULL) return E_INVALIDARG;
|
||||
if (m_owner.IsEmpty()) return E_FAIL;
|
||||
VariantClear(pvarChild);
|
||||
if (GetFocus() != m_owner->AccGetWnd())
|
||||
pvarChild->vt = VT_EMPTY;
|
||||
else {
|
||||
pvarChild->vt = VT_I4;
|
||||
size_t index = m_owner->AccGetFocusItem();
|
||||
if (index != ~0) {
|
||||
pvarChild->lVal = ChildIdFromIndex(index);
|
||||
} else {
|
||||
index = m_owner->AccGetFocusOther();
|
||||
if (index != ~0) {
|
||||
pvarChild->lVal = ChildIdFromIndex(index + m_owner->AccGetItemCount());
|
||||
} else {
|
||||
pvarChild->lVal = CHILDID_SELF;
|
||||
}
|
||||
}
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
HRESULT IAccessible_CListControl::get_accSelection(VARIANT *pvarChildren)
|
||||
{
|
||||
if (pvarChildren == NULL) return E_INVALIDARG;
|
||||
if (m_owner.IsEmpty()) return E_FAIL;
|
||||
VariantClear(pvarChildren);
|
||||
try {
|
||||
|
||||
const size_t itemCount = m_owner->AccGetItemCount();
|
||||
size_t selCount = 0;
|
||||
pfc::bit_array_bittable mask(itemCount);
|
||||
for(size_t walk = 0; walk < itemCount; ++walk) {
|
||||
bool state = m_owner->AccIsItemSelected(walk);
|
||||
mask.set(walk,state);
|
||||
if (state) selCount++;
|
||||
}
|
||||
|
||||
if (selCount == 0) {
|
||||
pvarChildren->vt = VT_EMPTY;
|
||||
} else if (selCount == 1) {
|
||||
pvarChildren->vt = VT_I4;
|
||||
pvarChildren->lVal = ChildIdFromIndex(mask.find_first(true, 0, itemCount));
|
||||
} else {
|
||||
pfc::array_t<LONG> data; data.set_size(selCount); size_t dataWalk = 0;
|
||||
for(size_t walk = mask.find_first(true,0,itemCount); walk < itemCount; walk = mask.find_next(true,walk,itemCount)) {
|
||||
data[dataWalk++] = ChildIdFromIndex(walk);
|
||||
}
|
||||
IEnumVARIANT * ptr = new IEnumVARIANT_selection(data.get_ptr(),data.get_size());
|
||||
ptr->AddRef();
|
||||
pvarChildren->vt = VT_UNKNOWN;
|
||||
pvarChildren->punkVal = ptr;
|
||||
}
|
||||
} catch(std::bad_alloc) {
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
HRESULT IAccessible_CListControl::get_accDefaultAction(VARIANT varChild, BSTR *pszDefaultAction)
|
||||
{
|
||||
if (varChild.vt != VT_I4) return E_INVALIDARG;
|
||||
if (pszDefaultAction == NULL) return E_INVALIDARG;
|
||||
if (m_owner.IsEmpty()) return E_FAIL;
|
||||
*pszDefaultAction = NULL;
|
||||
if (varChild.lVal == CHILDID_SELF) {
|
||||
return S_FALSE;
|
||||
} else {
|
||||
pfc::string_formatter temp;
|
||||
const size_t index = IndexFromChildId(varChild.lVal);
|
||||
const size_t itemCount = m_owner->AccGetItemCount();
|
||||
if (index < itemCount) {
|
||||
if (!m_owner->AccGetItemDefaultAction(temp)) return S_FALSE;
|
||||
} else if (index < itemCount + m_owner->AccGetOtherCount()) {
|
||||
if (!m_owner->AccGetOtherDefaultAction(index - itemCount, temp)) return false;
|
||||
} else {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
*pszDefaultAction = SysAllocString(pfc::stringcvt::string_os_from_utf8(temp));
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
HRESULT IAccessible_CListControl::accSelect(long flagsSelect, VARIANT varChild)
|
||||
{
|
||||
if (varChild.vt != VT_EMPTY && varChild.vt != VT_I4) return E_INVALIDARG;
|
||||
if (m_owner.IsEmpty()) return E_FAIL;
|
||||
if (varChild.vt == VT_EMPTY || varChild.lVal == CHILDID_SELF)
|
||||
{
|
||||
switch (flagsSelect)
|
||||
{
|
||||
case SELFLAG_TAKEFOCUS:
|
||||
m_owner->AccGetWnd().SetFocus();
|
||||
return S_OK;
|
||||
default:
|
||||
return DISP_E_MEMBERNOTFOUND;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const size_t count = m_owner->AccGetItemCount();
|
||||
const size_t index = IndexFromChildId(varChild.lVal);
|
||||
if (index < count) {
|
||||
if (flagsSelect & SELFLAG_TAKESELECTION)
|
||||
{
|
||||
if (flagsSelect & (SELFLAG_ADDSELECTION | SELFLAG_REMOVESELECTION | SELFLAG_EXTENDSELECTION))
|
||||
return E_INVALIDARG;
|
||||
m_owner->AccSetSelection(pfc::bit_array_true(), pfc::bit_array_one(index));
|
||||
}
|
||||
else if (flagsSelect & SELFLAG_EXTENDSELECTION)
|
||||
{
|
||||
if (flagsSelect & SELFLAG_TAKESELECTION)
|
||||
return E_INVALIDARG;
|
||||
size_t focus = m_owner->AccGetFocusItem();
|
||||
if (focus == ~0) return S_FALSE;
|
||||
bool state;
|
||||
if (flagsSelect & SELFLAG_ADDSELECTION)
|
||||
{
|
||||
if (flagsSelect & SELFLAG_REMOVESELECTION)
|
||||
return E_INVALIDARG;
|
||||
state = true;
|
||||
}
|
||||
else if (flagsSelect & SELFLAG_REMOVESELECTION)
|
||||
{
|
||||
if (flagsSelect & SELFLAG_ADDSELECTION)
|
||||
return E_INVALIDARG;
|
||||
state = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = m_owner->AccIsItemSelected(focus);
|
||||
}
|
||||
m_owner->AccSetSelection(pfc::bit_array_range(min(index, focus), max(index, focus)-min(index, focus)+1), pfc::bit_array_val(state));
|
||||
}
|
||||
else if (flagsSelect & SELFLAG_ADDSELECTION)
|
||||
{
|
||||
if (flagsSelect & (SELFLAG_REMOVESELECTION | SELFLAG_EXTENDSELECTION))
|
||||
return E_INVALIDARG;
|
||||
m_owner->AccSetSelection(pfc::bit_array_one(index), pfc::bit_array_true());
|
||||
}
|
||||
else if (flagsSelect & SELFLAG_REMOVESELECTION)
|
||||
{
|
||||
if (flagsSelect & (SELFLAG_ADDSELECTION | SELFLAG_EXTENDSELECTION))
|
||||
return E_INVALIDARG;
|
||||
m_owner->AccSetSelection(pfc::bit_array_one(index), pfc::bit_array_false());
|
||||
}
|
||||
|
||||
if (flagsSelect & SELFLAG_TAKEFOCUS) {
|
||||
m_owner->AccSetFocusItem(index);
|
||||
}
|
||||
} else if (index < count + m_owner->AccGetOtherCount()) {
|
||||
const size_t indexO = index - count;
|
||||
if (flagsSelect & SELFLAG_TAKEFOCUS) {
|
||||
m_owner->AccSetFocusOther(indexO);
|
||||
}
|
||||
} else {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
HRESULT IAccessible_CListControl::accLocation(long *pxLeft, long *pyTop, long *pcxWidth, long *pcyHeight, VARIANT varChild) // required
|
||||
{
|
||||
if (varChild.vt != VT_I4) return E_INVALIDARG;
|
||||
if (pxLeft == NULL || pyTop == NULL || pcxWidth == NULL || pcyHeight == NULL) return E_INVALIDARG;
|
||||
if (m_owner.IsEmpty()) return E_FAIL;
|
||||
CRect rc;
|
||||
const CWindow ownerWnd = m_owner->AccGetWnd();
|
||||
if (varChild.lVal == CHILDID_SELF) {
|
||||
if (!ownerWnd.GetClientRect(&rc)) return E_FAIL;
|
||||
} else {
|
||||
const size_t index = IndexFromChildId(varChild.lVal);
|
||||
const size_t itemCount = m_owner->AccGetItemCount();
|
||||
if (index < itemCount) {
|
||||
if (!m_owner->AccGetItemRect(index,rc)) return S_FALSE;
|
||||
} else if (index < itemCount + m_owner->AccGetOtherCount()) {
|
||||
if (!m_owner->AccGetOtherRect(index - itemCount, rc)) return S_FALSE;
|
||||
} else {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ownerWnd.ClientToScreen(rc)) return E_FAIL;
|
||||
*pxLeft = rc.left;
|
||||
*pyTop = rc.top;
|
||||
*pcxWidth = rc.right-rc.left;
|
||||
*pcyHeight = rc.bottom-rc.top;
|
||||
return S_OK;
|
||||
}
|
||||
HRESULT IAccessible_CListControl::accNavigate(long navDir, VARIANT varStart, VARIANT *pvarEndUpAt)
|
||||
{
|
||||
if (varStart.vt != VT_I4 && varStart.vt != VT_EMPTY) return E_INVALIDARG;
|
||||
if (pvarEndUpAt == NULL) return E_INVALIDARG;
|
||||
if (m_owner.IsEmpty()) return E_FAIL;
|
||||
VariantClear(pvarEndUpAt);
|
||||
pvarEndUpAt->vt = VT_EMPTY;
|
||||
if (varStart.vt == VT_EMPTY || varStart.lVal == CHILDID_SELF)
|
||||
{
|
||||
switch (navDir)
|
||||
{
|
||||
case NAVDIR_LEFT:
|
||||
case NAVDIR_RIGHT:
|
||||
case NAVDIR_UP:
|
||||
case NAVDIR_DOWN:
|
||||
case NAVDIR_PREVIOUS:
|
||||
case NAVDIR_NEXT:
|
||||
// leave empty
|
||||
break;
|
||||
case NAVDIR_FIRSTCHILD:
|
||||
if (m_owner->AccGetItemCount() > 0)
|
||||
{
|
||||
pvarEndUpAt->vt = VT_I4;
|
||||
pvarEndUpAt->lVal = ChildIdFromIndex(0);
|
||||
}
|
||||
break;
|
||||
case NAVDIR_LASTCHILD:
|
||||
if (m_owner->AccGetItemCount() > 0)
|
||||
{
|
||||
pvarEndUpAt->vt = VT_I4;
|
||||
pvarEndUpAt->lVal = ChildIdFromIndex(m_owner->AccGetItemCount()-1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const size_t index = IndexFromChildId(varStart.lVal);
|
||||
const size_t itemCount = m_owner->AccGetItemCount();
|
||||
if (index >= itemCount) {
|
||||
if (index < itemCount + m_owner->AccGetOtherCount()) return S_FALSE;
|
||||
else return E_INVALIDARG;
|
||||
}
|
||||
switch (navDir)
|
||||
{
|
||||
case NAVDIR_LEFT:
|
||||
case NAVDIR_RIGHT:
|
||||
case NAVDIR_FIRSTCHILD:
|
||||
case NAVDIR_LASTCHILD:
|
||||
// leave empty
|
||||
break;
|
||||
case NAVDIR_UP:
|
||||
case NAVDIR_PREVIOUS:
|
||||
if (index > 0)
|
||||
{
|
||||
pvarEndUpAt->vt = VT_I4;
|
||||
pvarEndUpAt->lVal = ChildIdFromIndex(index-1);
|
||||
}
|
||||
break;
|
||||
case NAVDIR_DOWN:
|
||||
case NAVDIR_NEXT:
|
||||
if (index+1 < itemCount)
|
||||
{
|
||||
pvarEndUpAt->vt = VT_I4;
|
||||
pvarEndUpAt->lVal = ChildIdFromIndex(index+1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (pvarEndUpAt->vt != VT_EMPTY) ? S_OK : S_FALSE;
|
||||
}
|
||||
HRESULT IAccessible_CListControl::accHitTest(long xLeft, long yTop, VARIANT *pvarChild)
|
||||
{
|
||||
if (pvarChild == NULL) return E_INVALIDARG;
|
||||
if (m_owner.IsEmpty()) return E_FAIL;
|
||||
VariantClear(pvarChild);
|
||||
CPoint pt (xLeft, yTop);
|
||||
const CWindow ownerWnd = m_owner->AccGetWnd();
|
||||
if (!ownerWnd.ScreenToClient(&pt)) return E_FAIL;
|
||||
CRect rcClient;
|
||||
if (!ownerWnd.GetClientRect(&rcClient)) return E_FAIL;
|
||||
if (PtInRect(&rcClient, pt)) {
|
||||
size_t index = m_owner->AccItemHitTest(pt);
|
||||
if (index != ~0) {
|
||||
pvarChild->vt = VT_I4;
|
||||
pvarChild->lVal = ChildIdFromIndex(index);
|
||||
} else {
|
||||
index = m_owner->AccOtherHitTest(pt);
|
||||
if (index != ~0) {
|
||||
pvarChild->vt = VT_I4;
|
||||
pvarChild->lVal = ChildIdFromIndex(m_owner->AccGetItemCount() + index);
|
||||
CWindow wnd = m_owner->AccGetOtherChildWnd(index);
|
||||
if (wnd != NULL) {
|
||||
IDispatch * obj;
|
||||
if (AccessibleObjectFromWindow(wnd,OBJID_WINDOW, IID_IDispatch, (void**)&obj) == S_OK) {
|
||||
pvarChild->vt = VT_DISPATCH;
|
||||
pvarChild->pdispVal = obj;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
pvarChild->vt = VT_I4;
|
||||
pvarChild->lVal = CHILDID_SELF;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
pvarChild->vt = VT_EMPTY;
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
HRESULT IAccessible_CListControl::accDoDefaultAction(VARIANT varChild)
|
||||
{
|
||||
if (varChild.vt != VT_I4) return E_INVALIDARG;
|
||||
if (m_owner.IsEmpty()) return E_FAIL;
|
||||
if (varChild.lVal == CHILDID_SELF) return S_FALSE;
|
||||
const size_t index = IndexFromChildId(varChild.lVal);
|
||||
const size_t itemCount = m_owner->AccGetItemCount();
|
||||
if (index < itemCount) {
|
||||
if (!m_owner->AccExecuteItemDefaultAction(index)) return S_FALSE;
|
||||
} else if (index < itemCount + m_owner->AccGetOtherCount()) {
|
||||
if (!m_owner->AccExecuteOtherDefaultAction(index - itemCount)) return S_FALSE;
|
||||
} else {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
HRESULT IAccessible_CListControl::put_accName(VARIANT varChild, BSTR szName) {
|
||||
return DISP_E_MEMBERNOTFOUND;
|
||||
}
|
||||
HRESULT IAccessible_CListControl::put_accValue(VARIANT varChild, BSTR szValue) {
|
||||
return DISP_E_MEMBERNOTFOUND;
|
||||
}
|
||||
|
||||
void CListAccessible::AccGetName(pfc::string_base & out) const {
|
||||
auto str = pfc::getWindowText(m_wnd);
|
||||
if ( str.length() > 0 ) out = str;
|
||||
else out = "List Control";
|
||||
}
|
||||
292
libPPUI/CListAccessible.h
Normal file
292
libPPUI/CListAccessible.h
Normal file
@@ -0,0 +1,292 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#pragma comment(lib, "oleacc.lib")
|
||||
|
||||
#include "CListControl-Cell.h"
|
||||
#include "CListControlWithSelection.h"
|
||||
|
||||
//! Internal class interfacing with Windows accessibility APIs. \n
|
||||
//! This class is not tied to any specific control and requires most of its methods to be overridden. \n
|
||||
//! With CListControl you want to use CListControlAccImpl<> template instead of using CListAccessible directly.
|
||||
class CListAccessible {
|
||||
public:
|
||||
void AccInitialize(CWindow wnd);
|
||||
void AccCleanup();
|
||||
LRESULT AccGetObject(WPARAM wp,LPARAM lp);
|
||||
CWindow AccGetWnd() const {return m_wnd;}
|
||||
virtual size_t AccGetItemCount() const {return 0;}
|
||||
virtual LONG AccGetItemRole( size_t index ) const {return ROLE_SYSTEM_LISTITEM;}
|
||||
virtual void AccGetItemName(size_t index, pfc::string_base & out) const {out = "";}
|
||||
virtual void AccGetName(pfc::string_base & out) const;
|
||||
virtual size_t AccGetFocusItem() const {return ~0;}
|
||||
virtual bool AccIsItemSelected(size_t index) const {return false;}
|
||||
virtual bool AccIsItemChecked( size_t index ) const { return false; }
|
||||
virtual bool AccIsItemVisible(size_t index) const {return false;}
|
||||
virtual bool AccGetItemDefaultAction(pfc::string_base & out) const {return false;}
|
||||
virtual bool AccExecuteItemDefaultAction(size_t index) {return false;}
|
||||
virtual void AccSetSelection(pfc::bit_array const & affected, pfc::bit_array const & state) {}
|
||||
virtual void AccSetFocusItem(size_t index) {}
|
||||
virtual bool AccGetItemRect(size_t index, CRect & out) const {return false;}
|
||||
virtual bool AccGetItemDescription(size_t index, pfc::string_base & out) const {return false;}
|
||||
virtual size_t AccItemHitTest(CPoint const & pt) const {return ~0;}
|
||||
|
||||
virtual DWORD AccGetOtherRole(size_t index) {return 0;}
|
||||
virtual size_t AccGetOtherCount() const {return 0;}
|
||||
virtual void AccGetOtherName(size_t index, pfc::string_base & out) const {out = "";}
|
||||
virtual size_t AccGetFocusOther() const {return ~0;}
|
||||
virtual void AccSetFocusOther(size_t index) {}
|
||||
virtual bool AccIsOtherVisible(size_t index) const {return false;}
|
||||
virtual bool AccGetOtherDescription(size_t index, pfc::string_base & out) const {return false;}
|
||||
virtual size_t AccOtherHitTest(CPoint const & pt) const {return ~0;}
|
||||
virtual bool AccIsOtherFocusable(size_t index) const {return false;}
|
||||
virtual bool AccGetOtherDefaultAction(size_t index, pfc::string_base & out) const {return false;}
|
||||
virtual bool AccExecuteOtherDefaultAction(size_t index) {return false;}
|
||||
virtual bool AccGetOtherRect(size_t index, CRect & out) const {return false;}
|
||||
virtual CWindow AccGetOtherChildWnd(size_t index) const {return NULL;}
|
||||
|
||||
void AccItemNamesChanged( pfc::bit_array const & mask);
|
||||
void AccReloadItems(pfc::bit_array const & mask );
|
||||
void AccRefreshItems(pfc::bit_array const & mask, UINT what);
|
||||
void AccStateChange(pfc::bit_array const & mask);
|
||||
void AccStateChange(size_t index);
|
||||
void AccItemLayoutChanged();
|
||||
void AccFocusItemChanged(size_t index);
|
||||
void AccFocusOtherChanged(size_t index);
|
||||
void AccSelectionChanged(const pfc::bit_array & affected, const pfc::bit_array & status);
|
||||
void AccLocationChange();
|
||||
protected:
|
||||
~CListAccessible() { * m_killSwitch = true; }
|
||||
private:
|
||||
CWindow m_wnd;
|
||||
std::shared_ptr<bool> m_killSwitch = std::make_shared<bool>();
|
||||
pfc::com_ptr_t<IAccessible> m_interface;
|
||||
};
|
||||
|
||||
//! Basic wrapper implementing CListAccessible methods on top of CListControl. Leaves many methods to be overridden by calle
|
||||
template<typename TBaseClass> class CListControlAccImpl : public TBaseClass, protected CListAccessible {
|
||||
public:
|
||||
template<typename ... arg_t> CListControlAccImpl( arg_t && ... arg ) : TBaseClass(std::forward<arg_t>(arg) ... ) {}
|
||||
|
||||
BEGIN_MSG_MAP_EX(CListControlAccImpl)
|
||||
MESSAGE_HANDLER(WM_GETOBJECT,OnGetObject)
|
||||
MESSAGE_HANDLER(WM_DESTROY,OnDestroyPassThru)
|
||||
MESSAGE_HANDLER(WM_CREATE,OnCreatePassThru)
|
||||
CHAIN_MSG_MAP(TBaseClass)
|
||||
END_MSG_MAP()
|
||||
public:
|
||||
void ReloadData() {
|
||||
TBaseClass::ReloadData();
|
||||
AccItemLayoutChanged();
|
||||
}
|
||||
void ReloadItems(pfc::bit_array const & mask) {
|
||||
TBaseClass::ReloadItems(mask);
|
||||
AccReloadItems(mask);
|
||||
}
|
||||
protected:
|
||||
size_t AccGetItemCount() const {return this->GetItemCount();}
|
||||
size_t AccGetFocusItem() const {return this->GetFocusItem();}
|
||||
bool AccIsItemSelected(size_t index) const {return this->IsItemSelected(index);}
|
||||
bool AccIsItemVisible(size_t index) const {
|
||||
if (index >= AccGetItemCount()) return false;
|
||||
return this->IsRectVisible(this->GetItemRect(index));
|
||||
}
|
||||
bool AccExecuteItemDefaultAction(size_t index) {if (index < AccGetItemCount()) {this->ExecuteDefaultAction(index);return true;} else return false;}
|
||||
bool AccGetItemRect(size_t index, CRect & out) const {
|
||||
if (index >= AccGetItemCount()) return false;
|
||||
out = this->GetItemRect(index);
|
||||
return true;
|
||||
}
|
||||
size_t AccItemHitTest(CPoint const & pt) const {
|
||||
size_t item;
|
||||
if (!this->ItemFromPoint(pt,item)) return ~0;
|
||||
return item;
|
||||
}
|
||||
|
||||
void OnViewOriginChange(CPoint p_delta) override {
|
||||
TBaseClass::OnViewOriginChange(p_delta);
|
||||
AccLocationChange();
|
||||
}
|
||||
|
||||
/* overrideme, optional
|
||||
void AccGetName(pfc::string_base & out) const;
|
||||
bool AccGetItemDefaultAction(pfc::string_base & out) const;
|
||||
*/
|
||||
|
||||
LONG AccRoleAt(size_t idx, size_t sub) const {
|
||||
auto cell = this->GetCellType(idx, sub);
|
||||
if (cell == nullptr) return 0;
|
||||
return cell->AccRole();
|
||||
}
|
||||
bool isCellText(size_t idx, size_t sub) const {
|
||||
switch (AccRoleAt(idx, sub)) {
|
||||
case ROLE_SYSTEM_TEXT:
|
||||
case ROLE_SYSTEM_STATICTEXT:
|
||||
case ROLE_SYSTEM_LISTITEM:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool useCellForDescription(size_t idx, size_t sub) const {
|
||||
return sub > 0 && isCellText(idx, sub);
|
||||
}
|
||||
bool AccGetItemDescription(size_t index, pfc::string_base& out) const {
|
||||
pfc::string_formatter ret, temp, temp2;
|
||||
const size_t total = this->GetColumnCount();
|
||||
for (size_t walk = 0; walk < total; ) {
|
||||
if (useCellForDescription(index, walk) && this->GetSubItemText(index, walk, temp) && temp.length() > 0) {
|
||||
if (ret.length() > 0) ret << "; ";
|
||||
this->GetColumnText(walk, temp2);
|
||||
if (temp2.length() > 0) ret << temp2 << ": ";
|
||||
ret << temp;
|
||||
}
|
||||
size_t d = this->GetSubItemSpan(index, walk);
|
||||
if (d < 1) d = 1;
|
||||
walk += d;
|
||||
}
|
||||
bool rv = (ret.length() > 0);
|
||||
if (rv) out = ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void AccGetItemNameAlt(size_t index, pfc::string_base & out) const {
|
||||
pfc::string_formatter ret, temp;
|
||||
const size_t total = this->GetColumnCount();
|
||||
for (size_t walk = 0; walk < total; ) {
|
||||
if (this->isCellText(index, walk) && this->GetSubItemText(index, walk, temp) && temp.length() > 0) {
|
||||
if (ret.length() > 0) ret << "; ";
|
||||
ret << temp;
|
||||
}
|
||||
size_t d = this->GetSubItemSpan(index, walk);
|
||||
if (d < 1) d = 1;
|
||||
walk += d;
|
||||
}
|
||||
out = ret;
|
||||
}
|
||||
|
||||
// Item name by default taken from column 0, override this if you supply another
|
||||
void AccGetItemName(size_t index, pfc::string_base & out) const {
|
||||
this->GetSubItemText(index, 0, out);
|
||||
}
|
||||
|
||||
void AccSetSelection(pfc::bit_array const & affected, pfc::bit_array const & state) override {
|
||||
this->SetSelection(affected, state);
|
||||
}
|
||||
void AccSetFocusItem(size_t index) override {
|
||||
this->SetFocusItem(index);
|
||||
}
|
||||
|
||||
void OnFocusChangedGroup(int iGroup) override {
|
||||
TBaseClass::OnFocusChangedGroup(iGroup);
|
||||
AccFocusOtherChanged((size_t)iGroup);
|
||||
}
|
||||
void OnFocusChanged(size_t f) override {
|
||||
TBaseClass::OnFocusChanged(f);
|
||||
AccFocusItemChanged(f);
|
||||
}
|
||||
|
||||
void OnSelectionChanged(pfc::bit_array const & affected, pfc::bit_array const & status) override {
|
||||
TBaseClass::OnSelectionChanged(affected, status);
|
||||
AccSelectionChanged(affected, status);
|
||||
}
|
||||
|
||||
CWindow AccGetOtherChildWnd(size_t index) const {return index == 0 ? CWindow(this->GetHeaderCtrl()) : CWindow(NULL) ;}
|
||||
virtual DWORD AccGetOtherRole(size_t index) {return index > 0 ? ROLE_SYSTEM_GROUPING : ROLE_SYSTEM_WINDOW;}//FIXME?
|
||||
virtual bool AccGetOtherDescription(size_t index, pfc::string_base & out) const {return false;}//FIXME??
|
||||
|
||||
size_t AccGetOtherCount() const {return 1 + this->GetGroupCount();}
|
||||
void AccGetOtherName(size_t index, pfc::string_base & out) const override {
|
||||
if (index == 0) out = "Columns Header";
|
||||
else if (!this->GetGroupHeaderText((int)index, out)) out = "";
|
||||
}
|
||||
size_t AccGetFocusOther() const override {
|
||||
int focus = this->GetGroupFocus();
|
||||
if (focus > 0) return (size_t) focus;
|
||||
else return SIZE_MAX;
|
||||
}
|
||||
void AccSetFocusOther(size_t index) override {
|
||||
if (index > 0) this->SetGroupFocus((int)index);
|
||||
}
|
||||
bool AccIsOtherVisible(size_t index) const override {
|
||||
if (index == 0) return true;
|
||||
CRect rc;
|
||||
if (!this->GetGroupHeaderRect((int)index,rc)) return false;
|
||||
return IsRectVisible(rc);
|
||||
}
|
||||
|
||||
size_t AccOtherHitTest(CPoint const & pt) const {
|
||||
{
|
||||
CPoint s(pt);
|
||||
if (this->ClientToScreen(&s)) {
|
||||
CRect rc;
|
||||
auto hdr = this->GetHeaderCtrl();
|
||||
if (hdr != NULL && hdr.GetWindowRect(rc)) {
|
||||
if (rc.PtInRect(s)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
int group;
|
||||
if (this->GroupHeaderFromPoint(pt,group)) return (size_t) group;
|
||||
return ~0;
|
||||
}
|
||||
bool AccIsOtherFocusable(size_t index) const {return index > 0;}
|
||||
bool AccGetOtherDefaultAction(size_t index, pfc::string_base & out) const {return false;}
|
||||
bool AccExecuteOtherDefaultAction(size_t index) {return false;}
|
||||
bool AccGetOtherRect(size_t index, CRect & out) const {
|
||||
if (index == 0) {
|
||||
CRect rc, client;
|
||||
|
||||
auto hdr = this->GetHeaderCtrl();
|
||||
if ( hdr == NULL ) return false;
|
||||
if (!hdr.GetWindowRect(rc)) return false;
|
||||
if (!this->ScreenToClient(rc)) return false;
|
||||
if (!this->GetClientRect(client)) return false;
|
||||
return !! out.IntersectRect(rc, client);
|
||||
} else {
|
||||
return this->GetGroupHeaderRect((int)index, out);
|
||||
}
|
||||
}
|
||||
LONG AccGetItemRole( size_t index ) const override {
|
||||
auto type = this->GetCellType( index, 0 );
|
||||
if ( type != nullptr ) {
|
||||
return type->AccRole();
|
||||
}
|
||||
return ROLE_SYSTEM_LISTITEM;
|
||||
}
|
||||
void SetCellCheckState(size_t item, size_t subItem, bool value) override {
|
||||
__super::SetCellCheckState(item, subItem, value);
|
||||
this->AccStateChange(item);
|
||||
}
|
||||
bool AccIsItemChecked( size_t index ) const override {
|
||||
auto type = this->GetCellType( index, 0 );
|
||||
if ( type != nullptr && type->IsToggle() ) {
|
||||
return this->GetCellCheckState( index, 0 );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
private:
|
||||
bool IsRectVisible(CRect const & rc) const {
|
||||
return !!CRect().IntersectRect(this->GetClientRectHook(),rc);
|
||||
}
|
||||
LRESULT OnCreatePassThru(UINT,WPARAM,LPARAM,BOOL& bHandled) {
|
||||
bHandled = FALSE;
|
||||
try { AccInitialize(*this); } catch(...) {AccCleanup();}
|
||||
return 0;
|
||||
}
|
||||
LRESULT OnDestroyPassThru(UINT,WPARAM,LPARAM,BOOL& bHandled) {
|
||||
bHandled = FALSE;
|
||||
AccCleanup();
|
||||
return 0;
|
||||
}
|
||||
LRESULT OnGetObject(UINT,WPARAM wp, LPARAM lp, BOOL & ) {
|
||||
return this->AccGetObject(wp,lp);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class CListControlWithSelectionImpl;
|
||||
typedef CListControlAccImpl< CListControlWithSelectionImpl > CListControlWithSelectionAccImpl;
|
||||
39
libPPUI/CListControl-Cell.h
Normal file
39
libPPUI/CListControl-Cell.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
#include <uxtheme.h> // HTHEME
|
||||
|
||||
class CListCell {
|
||||
public:
|
||||
typedef uint32_t cellState_t;
|
||||
enum {
|
||||
cellState_none = 0,
|
||||
cellState_hot = 1 << 0,
|
||||
cellState_pressed = 1 << 1,
|
||||
cellState_disabled = 1 << 2,
|
||||
};
|
||||
|
||||
struct DrawContentArg_t {
|
||||
DWORD hdrFormat;
|
||||
cellState_t cellState = 0;
|
||||
CRect subItemRect;
|
||||
CDCHandle dc;
|
||||
const wchar_t * text = nullptr;
|
||||
bool allowColors = true;
|
||||
CRect rcHot;
|
||||
CRect rcText;
|
||||
HTHEME theme = NULL;
|
||||
uint32_t colorHighlight = 0;
|
||||
CWindow thisWnd;
|
||||
};
|
||||
virtual void DrawContent( DrawContentArg_t const & arg ) = 0;
|
||||
virtual const char * Theme() { return nullptr; }
|
||||
virtual bool ApplyTextStyle( LOGFONT & font, double scale, uint32_t state );
|
||||
virtual bool IsInteractive() { return false; }
|
||||
virtual bool SuppressRowSelect() { return false; }
|
||||
virtual bool IsToggle() { return false; }
|
||||
virtual bool IsRadioToggle() { return false; }
|
||||
virtual bool AllowTypeFind() { return true; }
|
||||
virtual CRect HotRect( CRect rc ) { return rc; }
|
||||
virtual HCURSOR HotCursor() { return NULL; }
|
||||
virtual bool AllowDrawThemeText() { return false; }
|
||||
virtual LONG AccRole();
|
||||
};
|
||||
13
libPPUI/CListControl-Cells-Compat.h
Normal file
13
libPPUI/CListControl-Cells-Compat.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include "CListControl-Cells.h"
|
||||
|
||||
// Wrapper for old code using cell type enum constants
|
||||
|
||||
#define cell_text &PFC_SINGLETON(CListCell_Text)
|
||||
#define cell_multitext &PFC_SINGLETON(CListCell_MultiText)
|
||||
#define cell_hyperlink &PFC_SINGLETON(CListCell_Hyperlink)
|
||||
#define cell_button &PFC_SINGLETON(CListCell_Button)
|
||||
#define cell_button_lite &PFC_SINGLETON(CListCell_ButtonLite)
|
||||
#define cell_button_glyph &PFC_SINGLETON(CListCell_ButtonGlyph)
|
||||
#define cell_checkbox &PFC_SINGLETON(CListCell_Checkbox)
|
||||
#define cell_radiocheckbox &PFC_SINGLETON(CListCell_RadioCheckbox)
|
||||
298
libPPUI/CListControl-Cells.cpp
Normal file
298
libPPUI/CListControl-Cells.cpp
Normal file
@@ -0,0 +1,298 @@
|
||||
#include "stdafx.h"
|
||||
#include "CListControl.h"
|
||||
#include "CListControlHeaderImpl.h"
|
||||
#include "CListControl-Cells.h"
|
||||
#include "PaintUtils.h"
|
||||
#include "GDIUtils.h"
|
||||
#include <vsstyle.h>
|
||||
|
||||
LONG CListCell::AccRole() {
|
||||
return ROLE_SYSTEM_LISTITEM;
|
||||
}
|
||||
|
||||
void RenderCheckbox( HTHEME theme, CDCHandle dc, CRect rcCheckBox, unsigned stateFlags, bool bRadio ) {
|
||||
|
||||
const int part = bRadio ? BP_RADIOBUTTON : BP_CHECKBOX;
|
||||
|
||||
const bool bDisabled = (stateFlags & CListCell::cellState_disabled) != 0;
|
||||
const bool bPressed = (stateFlags & CListCell::cellState_pressed ) != 0;
|
||||
const bool bHot = ( stateFlags & CListCell::cellState_hot ) != 0;
|
||||
|
||||
if (theme != NULL && IsThemePartDefined(theme, part, 0)) {
|
||||
int state = 0;
|
||||
if (bDisabled) {
|
||||
state = bPressed ? CBS_CHECKEDDISABLED : CBS_DISABLED;
|
||||
} else if ( bHot ) {
|
||||
state = bPressed ? CBS_CHECKEDHOT : CBS_HOT;
|
||||
} else {
|
||||
state = bPressed ? CBS_CHECKEDNORMAL : CBS_NORMAL;
|
||||
}
|
||||
|
||||
CSize size;
|
||||
if (SUCCEEDED(GetThemePartSize(theme, dc, part, state, rcCheckBox, TS_TRUE, &size))) {
|
||||
if (size.cx <= rcCheckBox.Width() && size.cy <= rcCheckBox.Height()) {
|
||||
CRect rc = rcCheckBox;
|
||||
rc.left += ( rc.Width() - size.cx ) / 2;
|
||||
rc.top += ( rc.Height() - size.cy ) / 2;
|
||||
rc.right = rc.left + size.cx;
|
||||
rc.bottom = rc.top + size.cy;
|
||||
DrawThemeBackground(theme, dc, part, state, rc, &rc);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
int stateEx = bRadio ? DFCS_BUTTONRADIO : DFCS_BUTTONCHECK;
|
||||
if ( bPressed ) stateEx |= DFCS_CHECKED;
|
||||
if ( bDisabled ) stateEx |= DFCS_INACTIVE;
|
||||
else if ( bHot ) stateEx |= DFCS_HOT;
|
||||
DrawFrameControl(dc, rcCheckBox, DFC_BUTTON, stateEx);
|
||||
}
|
||||
|
||||
void RenderButton( HTHEME theme, CDCHandle dc, CRect rcButton, CRect rcUpdate, uint32_t cellState ) {
|
||||
|
||||
const int part = BP_PUSHBUTTON;
|
||||
|
||||
enum {
|
||||
stNormal = PBS_NORMAL,
|
||||
stHot = PBS_HOT,
|
||||
stDisabled = PBS_DISABLED,
|
||||
stPressed = PBS_PRESSED,
|
||||
};
|
||||
|
||||
int state = 0;
|
||||
if (cellState & CListCell::cellState_disabled) state = stDisabled;
|
||||
if ( cellState & CListCell::cellState_pressed ) state = stPressed;
|
||||
else if ( cellState & CListCell::cellState_hot ) state = stHot;
|
||||
else state = stNormal;
|
||||
|
||||
CRect rcClient = rcButton;
|
||||
|
||||
if (theme != NULL && IsThemePartDefined(theme, part, 0)) {
|
||||
DrawThemeBackground(theme, dc, part, state, rcClient, &rcUpdate);
|
||||
} else {
|
||||
int stateEx = DFCS_BUTTONPUSH;
|
||||
switch (state) {
|
||||
case stPressed: stateEx |= DFCS_PUSHED; break;
|
||||
case stDisabled: stateEx |= DFCS_INACTIVE; break;
|
||||
}
|
||||
DrawFrameControl(dc, rcClient, DFC_BUTTON, stateEx);
|
||||
}
|
||||
}
|
||||
|
||||
bool CListCell::ApplyTextStyle( LOGFONT & font, double scale, uint32_t ) {
|
||||
if ( scale != 1.0 ) {
|
||||
font.lfHeight = pfc::rint32( font.lfHeight * scale );
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void CListCell_Text::DrawContent( DrawContentArg_t const & arg ) {
|
||||
const auto fgWas = arg.dc.GetTextColor();
|
||||
CDCHandle dc = arg.dc;
|
||||
if (arg.cellState & cellState_disabled) {
|
||||
dc.SetTextColor(GetSysColor(COLOR_GRAYTEXT));
|
||||
}
|
||||
|
||||
|
||||
CRect clip = arg.rcText;
|
||||
|
||||
const t_uint32 format = PaintUtils::DrawText_TranslateHeaderAlignment(arg.hdrFormat);
|
||||
dc.DrawText( arg.text, (int)wcslen(arg.text), clip, format | DT_NOPREFIX | DT_END_ELLIPSIS | DT_SINGLELINE | DT_VCENTER );
|
||||
|
||||
dc.SetTextColor(fgWas);
|
||||
}
|
||||
|
||||
void CListCell_TextColors::DrawContent( DrawContentArg_t const & arg ) {
|
||||
CDCHandle dc = arg.dc;
|
||||
|
||||
CRect clip = arg.rcText;
|
||||
|
||||
const uint32_t fgWas = dc.GetTextColor();
|
||||
|
||||
const t_uint32 format = PaintUtils::DrawText_TranslateHeaderAlignment(arg.hdrFormat);
|
||||
const t_uint32 bk = dc.GetBkColor();
|
||||
const t_uint32 fg = fgWas;
|
||||
const t_uint32 hl = (arg.allowColors ? arg.colorHighlight : fg);
|
||||
const t_uint32 colors[3] = { PaintUtils::BlendColor(bk, fg, 33), fg, hl };
|
||||
|
||||
PaintUtils::TextOutColorsEx(dc, arg.text, clip, format, colors);
|
||||
|
||||
dc.SetTextColor(fgWas);
|
||||
}
|
||||
|
||||
void CListCell_MultiText::DrawContent( DrawContentArg_t const & arg ) {
|
||||
CDCHandle dc = arg.dc;
|
||||
|
||||
const int textLen = (int) wcslen( arg.text );
|
||||
|
||||
CRect clip = arg.rcText;
|
||||
|
||||
const t_uint32 format = PaintUtils::DrawText_TranslateHeaderAlignment(arg.hdrFormat) | DT_NOPREFIX | DT_VCENTER ;
|
||||
|
||||
CRect rcDraw = clip;
|
||||
dc.DrawText(arg.text, textLen, rcDraw, format | DT_CALCRECT);
|
||||
auto txSize = rcDraw.Size();
|
||||
rcDraw = clip;
|
||||
if ( txSize.cy < rcDraw.Height() ) {
|
||||
int sub = rcDraw.Height() - txSize.cy;
|
||||
rcDraw.top += sub/2;
|
||||
rcDraw.bottom = rcDraw.top + txSize.cy;
|
||||
}
|
||||
dc.DrawText(arg.text, textLen, rcDraw, format);
|
||||
}
|
||||
|
||||
bool CListCell_Hyperlink::ApplyTextStyle( LOGFONT & font, double scale, uint32_t state ) {
|
||||
bool rv = __super::ApplyTextStyle(font, scale, state);
|
||||
|
||||
if ( state & cellState_hot ) {
|
||||
font.lfUnderline = TRUE;
|
||||
rv = true;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
HCURSOR CListCell_Hyperlink::HotCursor() {
|
||||
return LoadCursor(NULL, IDC_HAND);
|
||||
}
|
||||
|
||||
LONG CListCell_Hyperlink::AccRole() {
|
||||
return ROLE_SYSTEM_LINK;
|
||||
}
|
||||
|
||||
void CListCell_Hyperlink::DrawContent( DrawContentArg_t const & arg ) {
|
||||
|
||||
CDCHandle dc = arg.dc;
|
||||
|
||||
const uint32_t fgWas = dc.GetTextColor();
|
||||
|
||||
const t_uint32 format = PaintUtils::DrawText_TranslateHeaderAlignment(arg.hdrFormat);
|
||||
if (arg.allowColors) dc.SetTextColor( arg.colorHighlight );
|
||||
const t_uint32 bk = dc.GetBkColor();
|
||||
|
||||
CRect rc = arg.rcText;
|
||||
dc.DrawText(arg.text, (int) wcslen(arg.text), rc, format | DT_NOPREFIX | DT_END_ELLIPSIS | DT_SINGLELINE | DT_VCENTER );
|
||||
|
||||
dc.SetTextColor(fgWas);
|
||||
}
|
||||
|
||||
LONG CListCell_Button::AccRole() {
|
||||
return ROLE_SYSTEM_PUSHBUTTON;
|
||||
}
|
||||
|
||||
void CListCell_Button::DrawContent( DrawContentArg_t const & arg ) {
|
||||
|
||||
CDCHandle dc = arg.dc;
|
||||
|
||||
const bool bPressed = (arg.cellState & cellState_pressed) != 0;
|
||||
const bool bHot = (arg.cellState & cellState_hot) != 0;
|
||||
|
||||
|
||||
if ( !m_lite || bHot || bPressed ) {
|
||||
RenderButton( arg.theme, dc, arg.rcHot, arg.rcHot, arg.cellState );
|
||||
}
|
||||
|
||||
CRect clip = arg.rcText;
|
||||
|
||||
dc.DrawText(arg.text, (int) wcslen(arg.text), clip, DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER | DT_CENTER);
|
||||
}
|
||||
|
||||
bool CListCell_ButtonGlyph::ApplyTextStyle( LOGFONT & font, double scale, uint32_t state ) {
|
||||
return __super::ApplyTextStyle(font, scale * 1.3, state);
|
||||
}
|
||||
|
||||
static CRect CheckBoxRect(CRect rc) {
|
||||
if (rc.Width() > rc.Height()) {
|
||||
rc.right = rc.left + rc.Height();
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
LONG CListCell_Checkbox::AccRole() {
|
||||
return m_radio ? ROLE_SYSTEM_RADIOBUTTON : ROLE_SYSTEM_CHECKBUTTON;
|
||||
}
|
||||
|
||||
CRect CListCell_Checkbox::HotRect( CRect rc ) {
|
||||
return CheckBoxRect( rc );
|
||||
}
|
||||
|
||||
void CListCell_Checkbox::DrawContent( DrawContentArg_t const & arg ) {
|
||||
|
||||
CDCHandle dc = arg.dc;
|
||||
|
||||
const bool bPressed = (arg.cellState & cellState_pressed) != 0;
|
||||
const bool bHot = (arg.cellState & cellState_hot) != 0;
|
||||
|
||||
|
||||
CRect clip = arg.rcText;
|
||||
|
||||
const uint32_t fgWas = dc.GetTextColor();
|
||||
|
||||
if (arg.subItemRect.Width() > arg.subItemRect.Height() ) {
|
||||
CRect rcCheckbox = arg.subItemRect;
|
||||
rcCheckbox.right = rcCheckbox.left + rcCheckbox.Height();
|
||||
RenderCheckbox(arg.theme, dc, rcCheckbox, arg.cellState, m_radio );
|
||||
CRect rcText = arg.subItemRect;
|
||||
rcText.left = rcCheckbox.right;
|
||||
if (arg.cellState & cellState_disabled) {
|
||||
dc.SetTextColor(GetSysColor(COLOR_GRAYTEXT));
|
||||
}
|
||||
dc.DrawText(arg.text, (int) wcslen(arg.text), rcText, DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER | DT_LEFT);
|
||||
} else {
|
||||
RenderCheckbox(arg.theme, dc, arg.subItemRect, arg.cellState, m_radio );
|
||||
}
|
||||
|
||||
dc.SetTextColor(fgWas);
|
||||
}
|
||||
|
||||
void CListCell_Text_FixedColor::DrawContent(DrawContentArg_t const & arg) {
|
||||
if (arg.allowColors) {
|
||||
SetTextColorScope scope(arg.dc, m_col);
|
||||
__super::DrawContent(arg);
|
||||
} else {
|
||||
__super::DrawContent(arg);
|
||||
}
|
||||
}
|
||||
|
||||
void CListCell_Combo::DrawContent(DrawContentArg_t const & arg) {
|
||||
CDCHandle dc = arg.dc;
|
||||
|
||||
const bool bDisabled = (arg.cellState & CListCell::cellState_disabled) != 0;
|
||||
const bool bPressed = (arg.cellState & cellState_pressed) != 0;
|
||||
const bool bHot = (arg.cellState & cellState_hot) != 0;
|
||||
|
||||
const int part = CP_DROPDOWNBUTTONRIGHT;
|
||||
|
||||
const HTHEME theme = arg.theme;
|
||||
|
||||
if (theme != NULL && IsThemePartDefined(theme, part, 0)) {
|
||||
int state = CBXSR_NORMAL;
|
||||
if (bDisabled) {
|
||||
state = CBXSR_DISABLED;
|
||||
} else if (bPressed) {
|
||||
state = CBXSR_PRESSED;
|
||||
} else if (bHot) {
|
||||
state = CBXSR_HOT;
|
||||
}
|
||||
|
||||
CSize size;
|
||||
CRect rcCombo = arg.subItemRect;
|
||||
CRect rcText = arg.rcText;
|
||||
int w = rcCombo.Height()*3/4;
|
||||
if (w < rcCombo.Width()) {
|
||||
rcCombo.left = rcCombo.right - w;
|
||||
DrawThemeBackground(theme, dc, part, state, rcCombo, &rcCombo);
|
||||
if (rcCombo.left < rcText.right ) rcText.right = rcCombo.left;
|
||||
}
|
||||
|
||||
DrawContentArg_t arg2 = arg;
|
||||
arg2.rcText = rcText;
|
||||
PFC_SINGLETON(CListCell_Text).DrawContent(arg2);
|
||||
}
|
||||
}
|
||||
|
||||
LONG CListCell_Combo::AccRole() {
|
||||
return ROLE_SYSTEM_DROPLIST;
|
||||
}
|
||||
94
libPPUI/CListControl-Cells.h
Normal file
94
libPPUI/CListControl-Cells.h
Normal file
@@ -0,0 +1,94 @@
|
||||
#pragma once
|
||||
|
||||
#include "CListControl-Cell.h"
|
||||
|
||||
|
||||
class CListCell_Interactive : public CListCell {
|
||||
public:
|
||||
bool IsInteractive() override { return true; }
|
||||
};
|
||||
|
||||
class CListCell_Text : public CListCell {
|
||||
public:
|
||||
void DrawContent( DrawContentArg_t const & ) override;
|
||||
bool AllowDrawThemeText() override { return true; }
|
||||
};
|
||||
|
||||
class CListCell_TextColors : public CListCell_Text {
|
||||
public:
|
||||
void DrawContent( DrawContentArg_t const & ) override;
|
||||
};
|
||||
|
||||
class CListCell_MultiText : public CListCell {
|
||||
public:
|
||||
void DrawContent( DrawContentArg_t const & ) override;
|
||||
};
|
||||
|
||||
class CListCell_Hyperlink : public CListCell_Interactive {
|
||||
public:
|
||||
void DrawContent( DrawContentArg_t const & ) override;
|
||||
bool ApplyTextStyle( LOGFONT & font, double scale, uint32_t state ) override;
|
||||
HCURSOR HotCursor() override;
|
||||
LONG AccRole() override;
|
||||
bool SuppressRowSelect() override { return true; }
|
||||
};
|
||||
|
||||
class CListCell_Button : public CListCell_Interactive {
|
||||
public:
|
||||
void DrawContent( DrawContentArg_t const & ) override;
|
||||
const char * Theme() override { return "BUTTON"; }
|
||||
bool AllowTypeFind() override { return false; }
|
||||
LONG AccRole() override;
|
||||
bool SuppressRowSelect() override { return true; }
|
||||
|
||||
protected:
|
||||
bool m_lite = false;
|
||||
};
|
||||
|
||||
class CListCell_ButtonLite : public CListCell_Button {
|
||||
public:
|
||||
CListCell_ButtonLite() { m_lite = true; }
|
||||
};
|
||||
|
||||
class CListCell_ButtonGlyph : public CListCell_ButtonLite {
|
||||
public:
|
||||
bool ApplyTextStyle( LOGFONT & font, double scale, uint32_t state ) override;
|
||||
};
|
||||
|
||||
class CListCell_Checkbox : public CListCell_Interactive {
|
||||
public:
|
||||
void DrawContent( DrawContentArg_t const & ) override;
|
||||
const char * Theme() override { return "BUTTON"; }
|
||||
bool IsToggle() override { return true; }
|
||||
CRect HotRect( CRect rc ) override;
|
||||
bool IsRadioToggle() override { return m_radio; }
|
||||
LONG AccRole() override;
|
||||
|
||||
protected:
|
||||
bool m_radio = false;
|
||||
};
|
||||
|
||||
class CListCell_RadioCheckbox : public CListCell_Checkbox {
|
||||
public:
|
||||
CListCell_RadioCheckbox() { m_radio = true; }
|
||||
|
||||
static CListCell_RadioCheckbox instance;
|
||||
};
|
||||
|
||||
class CListCell_Combo : public CListCell_Interactive {
|
||||
public:
|
||||
void DrawContent(DrawContentArg_t const &) override;
|
||||
const char * Theme() override { return "COMBOBOX"; }
|
||||
LONG AccRole() override;
|
||||
};
|
||||
|
||||
void RenderButton( HTHEME theme, CDCHandle dc, CRect rcButton, CRect rcUpdate, uint32_t cellState );
|
||||
void RenderCheckbox( HTHEME theme, CDCHandle dc, CRect rcCheckBox, unsigned stateFlags, bool bRadio );
|
||||
|
||||
|
||||
class CListCell_Text_FixedColor : public CListCell_Text {
|
||||
const COLORREF m_col;
|
||||
public:
|
||||
CListCell_Text_FixedColor(COLORREF col) : m_col(col) {}
|
||||
void DrawContent(DrawContentArg_t const & arg) override;
|
||||
};
|
||||
989
libPPUI/CListControl.cpp
Normal file
989
libPPUI/CListControl.cpp
Normal file
@@ -0,0 +1,989 @@
|
||||
#include "stdafx.h"
|
||||
#include "CListControl.h"
|
||||
#include "PaintUtils.h"
|
||||
#include "CListControlUserOptions.h"
|
||||
#include "GDIUtils.h"
|
||||
|
||||
CListControlUserOptions * CListControlUserOptions::instance = nullptr;
|
||||
|
||||
CRect CListControlImpl::GetClientRectHook() const {
|
||||
CRect temp; if (!GetClientRect(temp)) temp.SetRectEmpty(); return temp;
|
||||
}
|
||||
|
||||
bool CListControlImpl::UserEnabledSmoothScroll() const {
|
||||
auto i = CListControlUserOptions::instance;
|
||||
if ( i != nullptr ) return i->useSmoothScroll();
|
||||
return false;
|
||||
}
|
||||
|
||||
LRESULT CListControlImpl::SetFocusPassThru(UINT,WPARAM,LPARAM,BOOL& bHandled) {
|
||||
SetFocus();
|
||||
bHandled = FALSE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CListControlImpl::EnsureVisibleRectAbs(const CRect & p_rect) {
|
||||
const CRect rcView = GetVisibleRectAbs();
|
||||
const CRect rcItem = p_rect;
|
||||
int deltaX = 0, deltaY = 0;
|
||||
|
||||
const bool centerOnItem = m_ensureVisibleUser;
|
||||
|
||||
if (rcItem.top < rcView.top || rcItem.bottom > rcView.bottom) {
|
||||
if (rcItem.Height() > rcView.Height()) {
|
||||
deltaY = rcItem.top - rcView.top;
|
||||
} else {
|
||||
if (centerOnItem) {
|
||||
deltaY = rcItem.CenterPoint().y - rcView.CenterPoint().y;
|
||||
} else {
|
||||
if (rcItem.bottom > rcView.bottom) deltaY = rcItem.bottom - rcView.bottom;
|
||||
else deltaY = rcItem.top - rcView.top;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
if (rcItem.left < rcView.left || rcItem.right > rcView.right) {
|
||||
if (rcItem.Width() > rcView.Width()) {
|
||||
if (rcItem.left > rcView.left || rcItem.right < rcView.right) deltaX = rcItem.left - rcView.left;
|
||||
} else {
|
||||
if (centerOnItem) {
|
||||
deltaX = rcItem.CenterPoint().x - rcView.CenterPoint().x;
|
||||
} else {
|
||||
if (rcItem.right > rcView.right) deltaX = rcItem.right - rcView.right;
|
||||
else deltaX = rcItem.left - rcView.left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (deltaX != 0 || deltaY != 0) {
|
||||
MoveViewOriginDelta(CPoint(deltaX,deltaY));
|
||||
}
|
||||
}
|
||||
void CListControlImpl::EnsureItemVisible(t_size p_item, bool bUser) {
|
||||
m_ensureVisibleUser = bUser;
|
||||
EnsureVisibleRectAbs(GetItemRectAbs(p_item));
|
||||
m_ensureVisibleUser = false;
|
||||
}
|
||||
void CListControlImpl::EnsureHeaderVisible(int p_group) {
|
||||
CRect rect;
|
||||
if (GetGroupHeaderRectAbs(p_group,rect)) EnsureVisibleRectAbs(rect);
|
||||
}
|
||||
|
||||
void CListControlImpl::RefreshSlider(bool p_vertical) {
|
||||
const CRect viewArea = GetViewAreaRectAbs();
|
||||
const CRect rcVisible = GetVisibleRectAbs();
|
||||
SCROLLINFO si = {};
|
||||
si.cbSize = sizeof(si);
|
||||
si.fMask = SIF_PAGE|SIF_RANGE|SIF_POS;
|
||||
|
||||
|
||||
if (AllowScrollbar(p_vertical)) {
|
||||
if (p_vertical) {
|
||||
si.nPage = rcVisible.Height();
|
||||
si.nMin = viewArea.top;
|
||||
si.nMax = viewArea.bottom - 1;
|
||||
si.nPos = rcVisible.top;
|
||||
} else {
|
||||
si.nPage = rcVisible.Width();
|
||||
si.nMin = viewArea.left;
|
||||
si.nMax = viewArea.right - 1;
|
||||
si.nPos = rcVisible.left;
|
||||
}
|
||||
}
|
||||
|
||||
SetScrollInfo(p_vertical ? SB_VERT : SB_HORZ, &si);
|
||||
}
|
||||
|
||||
void CListControlImpl::RefreshSliders() {
|
||||
//PROBLEM: while lots of data can be reused across those, it has to be recalculated inbetween because view area etc may change when scroll info changes
|
||||
RefreshSlider(false); RefreshSlider(true);
|
||||
}
|
||||
|
||||
int CListControlImpl::GetScrollThumbPos(int which) {
|
||||
SCROLLINFO si = {};
|
||||
si.cbSize = sizeof(si);
|
||||
si.fMask = SIF_TRACKPOS;
|
||||
GetScrollInfo(which,&si);
|
||||
return si.nTrackPos;
|
||||
}
|
||||
|
||||
namespace {
|
||||
class ResolveGroupHelper {
|
||||
public:
|
||||
ResolveGroupHelper(const CListControlImpl & p_control) : m_control(p_control) {}
|
||||
int operator[](t_size p_index) const {return m_control.GetItemGroup(p_index);}
|
||||
private:
|
||||
const CListControlImpl & m_control;
|
||||
};
|
||||
}
|
||||
|
||||
bool CListControlImpl::ResolveGroupRange(int p_id,t_size & p_base,t_size & p_count) const {
|
||||
|
||||
return pfc::binarySearch<>::runGroup(ResolveGroupHelper(*this),0,GetItemCount(),p_id,p_base,p_count);
|
||||
|
||||
|
||||
//return pfc::bsearch_range_t(GetItemCount(),ResolveGroupHelper(*this),pfc::compare_t<int,int>,p_id,p_base,p_count);
|
||||
}
|
||||
|
||||
static int HandleScroll(WORD p_code,int p_offset,int p_page, int p_line, int p_bottom, int p_thumbpos) {
|
||||
switch(p_code) {
|
||||
case SB_LINEUP:
|
||||
return p_offset - p_line;
|
||||
case SB_LINEDOWN:
|
||||
return p_offset + p_line;
|
||||
case SB_BOTTOM:
|
||||
return p_bottom - p_page;
|
||||
case SB_TOP:
|
||||
return 0;
|
||||
case SB_PAGEUP:
|
||||
return p_offset - p_page;
|
||||
case SB_PAGEDOWN:
|
||||
return p_offset + p_page;
|
||||
case SB_THUMBPOSITION:
|
||||
return p_thumbpos;
|
||||
case SB_THUMBTRACK:
|
||||
return p_thumbpos;
|
||||
default:
|
||||
return p_offset;
|
||||
}
|
||||
}
|
||||
|
||||
static CPoint ClipPointToRect(CPoint const & p_pt,CRect const & p_rect) {
|
||||
return CPoint(pfc::clip_t(p_pt.x,p_rect.left,p_rect.right),pfc::clip_t(p_pt.y,p_rect.top,p_rect.bottom));
|
||||
}
|
||||
|
||||
void CListControlImpl::MoveViewOriginNoClip(CPoint p_target) {
|
||||
UpdateWindow();
|
||||
const CPoint old = m_viewOrigin;
|
||||
m_viewOrigin = p_target;
|
||||
|
||||
if (m_viewOrigin != old) {
|
||||
if (m_viewOrigin.x != old.x) SetScrollPos(SB_HORZ,m_viewOrigin.x);
|
||||
if (m_viewOrigin.y != old.y) SetScrollPos(SB_VERT,m_viewOrigin.y);
|
||||
|
||||
const CPoint delta = old - m_viewOrigin;
|
||||
if (FixedOverlayPresent()) Invalidate();
|
||||
else {
|
||||
DWORD flags = SW_INVALIDATE | SW_ERASE;
|
||||
const DWORD smoothScrollMS = 50;
|
||||
if (this->UserEnabledSmoothScroll() && this->CanSmoothScroll()) {
|
||||
flags |= SW_SMOOTHSCROLL | (smoothScrollMS << 16);
|
||||
}
|
||||
|
||||
ScrollWindowEx(delta.x,delta.y,GetClientRectHook(),NULL,0,0,flags );
|
||||
}
|
||||
|
||||
OnViewOriginChange(m_viewOrigin - old);
|
||||
}
|
||||
}
|
||||
|
||||
CPoint CListControlImpl::ClipViewOrigin(CPoint p_origin) const {
|
||||
return ClipPointToRect(p_origin,GetValidViewOriginArea());
|
||||
}
|
||||
void CListControlImpl::MoveViewOrigin(CPoint p_target) {
|
||||
MoveViewOriginNoClip(ClipViewOrigin(p_target));
|
||||
}
|
||||
|
||||
#ifndef SPI_GETWHEELSCROLLCHARS
|
||||
#define SPI_GETWHEELSCROLLCHARS 0x006C
|
||||
#endif
|
||||
int CListControlImpl::HandleWheel(int & p_accum,int p_delta, bool bHoriz) {
|
||||
if ( m_suppressMouseWheel ) return 0;
|
||||
UINT scrollLines = 1;
|
||||
SystemParametersInfo(bHoriz ? SPI_GETWHEELSCROLLCHARS : SPI_GETWHEELSCROLLLINES,0,&scrollLines,0);
|
||||
if (scrollLines == ~0) {
|
||||
p_accum = 0;
|
||||
int rv = -pfc::sgn_t(p_delta);
|
||||
CRect client = GetClientRectHook();
|
||||
if (bHoriz) rv *= client.Width();
|
||||
else rv *= client.Height();
|
||||
return rv;
|
||||
}
|
||||
|
||||
const int itemHeight = GetItemHeight();
|
||||
const int extraScale = 10000;
|
||||
|
||||
p_accum += p_delta * extraScale;
|
||||
if ((int)scrollLines < 1) scrollLines = 1;
|
||||
int multiplier = (WHEEL_DELTA * extraScale) / (scrollLines * itemHeight);
|
||||
if (multiplier<1) multiplier = 1;
|
||||
|
||||
int delta = pfc::rint32( (double) p_accum / (double) multiplier );
|
||||
p_accum -= delta * multiplier;
|
||||
return -delta;
|
||||
|
||||
/*
|
||||
if (p_accum<=-multiplier || p_accum>=multiplier) {
|
||||
int direction;
|
||||
int ov = p_accum;
|
||||
if (ov<0) {
|
||||
direction = -1;
|
||||
ov = -ov;
|
||||
p_accum = - ((-p_accum)%multiplier);
|
||||
} else {
|
||||
p_accum %= multiplier;
|
||||
direction = 1;
|
||||
}
|
||||
|
||||
return - (direction * (ov + multiplier - 1) ) / multiplier;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
LRESULT CListControlImpl::OnVWheel(UINT,WPARAM p_wp,LPARAM p_lp,BOOL&) {
|
||||
const CRect client = GetClientRectHook(), view = this->GetViewAreaRectAbs();
|
||||
int deltaPixels = HandleWheel(m_wheelAccumY,(short)HIWORD(p_wp), false);
|
||||
|
||||
const bool canVScroll = client.Height() < view.Height();
|
||||
const bool canHScroll = client.Width() < view.Width();
|
||||
|
||||
CPoint ptDelta;
|
||||
if ( canVScroll && canHScroll && GetHotkeyModifierFlags() == MOD_SHIFT) {
|
||||
ptDelta = CPoint(deltaPixels, 0); // default to horizontal scroll if shift is pressed
|
||||
} else if (canVScroll) {
|
||||
ptDelta = CPoint(0,deltaPixels);
|
||||
} else if (canHScroll) {
|
||||
ptDelta = CPoint(deltaPixels,0);
|
||||
}
|
||||
|
||||
if ( ptDelta != CPoint(0,0) ) {
|
||||
MoveViewOriginDelta(ptDelta);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
LRESULT CListControlImpl::OnHWheel(UINT,WPARAM p_wp,LPARAM p_lp,BOOL&) {
|
||||
const CRect client = GetClientRectHook();
|
||||
int deltaPixels = HandleWheel(m_wheelAccumX,(short)HIWORD(p_wp), true);
|
||||
MoveViewOriginDelta(CPoint(-deltaPixels,0));
|
||||
return 0;
|
||||
}
|
||||
|
||||
LRESULT CListControlImpl::OnVScroll(UINT,WPARAM p_wp,LPARAM,BOOL&) {
|
||||
int target = HandleScroll(LOWORD(p_wp),m_viewOrigin.y,GetVisibleRectAbs().Height(),GetItemHeight(),GetViewAreaRectAbs().bottom,GetScrollThumbPos(SB_VERT));
|
||||
MoveViewOrigin(CPoint(m_viewOrigin.x,target));
|
||||
return 0;
|
||||
}
|
||||
LRESULT CListControlImpl::OnHScroll(UINT,WPARAM p_wp,LPARAM,BOOL&) {
|
||||
int target = HandleScroll(LOWORD(p_wp),m_viewOrigin.x,GetVisibleRectAbs().Width(),GetItemHeight() /*fixme*/,GetViewAreaRectAbs().right,GetScrollThumbPos(SB_HORZ));
|
||||
MoveViewOrigin(CPoint(target,m_viewOrigin.y));
|
||||
return 0;
|
||||
}
|
||||
|
||||
LRESULT CListControlImpl::OnGesture(UINT,WPARAM,LPARAM lParam,BOOL& bHandled) {
|
||||
if (!this->m_gestureAPI.IsAvailable()) {
|
||||
bHandled = FALSE;
|
||||
return 0;
|
||||
}
|
||||
HGESTUREINFO hGesture = (HGESTUREINFO) lParam;
|
||||
GESTUREINFO gestureInfo = {sizeof(gestureInfo)};
|
||||
if (m_gestureAPI.GetGestureInfo(hGesture, &gestureInfo)) {
|
||||
//console::formatter() << "WM_GESTURE " << pfc::format_hex( gestureInfo.dwFlags ) << " " << (int)gestureInfo.dwID << " X:" << gestureInfo.ptsLocation.x << " Y:" << gestureInfo.ptsLocation.y << " arg:" << (__int64) gestureInfo.ullArguments;
|
||||
CPoint pt( gestureInfo.ptsLocation.x, gestureInfo.ptsLocation.y );
|
||||
switch(gestureInfo.dwID) {
|
||||
case GID_BEGIN:
|
||||
m_gesturePoint = pt;
|
||||
break;
|
||||
case GID_END:
|
||||
break;
|
||||
case GID_PAN:
|
||||
MoveViewOriginDelta( this->m_gesturePoint - pt);
|
||||
m_gesturePoint = pt;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_gestureAPI.CloseGestureInfoHandle(hGesture);
|
||||
bHandled = TRUE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
LRESULT CListControlImpl::OnSize(UINT,WPARAM,LPARAM p_lp,BOOL&) {
|
||||
OnSizeAsync_Trigger();
|
||||
RefreshSliders();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void CListControlImpl::RenderBackground( CDCHandle dc, CRect const & rc ) {
|
||||
PaintUtils::FillRectSimple(dc,rc,GetSysColorHook(colorBackground));
|
||||
}
|
||||
|
||||
void CListControlImpl::PaintContent(CRect rcPaint, HDC dc) {
|
||||
CDCHandle renderDC(dc);
|
||||
|
||||
CMemoryDC bufferDC(renderDC,rcPaint);
|
||||
renderDC = bufferDC;
|
||||
this->RenderBackground(renderDC, rcPaint);
|
||||
|
||||
{
|
||||
const CPoint pt = GetViewOffset();
|
||||
OffsetWindowOrgScope offsetScope(renderDC, pt);
|
||||
CRect renderRect = rcPaint; renderRect.OffsetRect(pt);
|
||||
RenderRect(renderRect,renderDC);
|
||||
}
|
||||
}
|
||||
|
||||
void CListControlImpl::OnPrintClient(HDC dc, UINT uFlags) {
|
||||
CRect rcClient; this->GetClientRect( rcClient );
|
||||
PaintContent( rcClient, dc );
|
||||
}
|
||||
|
||||
LRESULT CListControlImpl::OnPaint(UINT,WPARAM,LPARAM,BOOL&) {
|
||||
CPaintDC paintDC(*this);
|
||||
|
||||
PaintContent( paintDC.m_ps.rcPaint, paintDC.m_hDC );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace {
|
||||
class comparator_rect {
|
||||
public:
|
||||
static int compare(const CRect & p_rect1,const CRect & p_rect2) {
|
||||
if (p_rect1.bottom <= p_rect2.top) return -1;
|
||||
else if (p_rect1.top >= p_rect2.bottom) return 1;
|
||||
else return 0;
|
||||
}
|
||||
};
|
||||
|
||||
static int RectPointCompare(const CRect & p_item1,const int p_y) {
|
||||
if (p_item1.bottom <= p_y) return -1;
|
||||
else if (p_item1.top > p_y) return 1;
|
||||
else return 0;
|
||||
}
|
||||
|
||||
class RectSearchHelper_Items {
|
||||
public:
|
||||
RectSearchHelper_Items(const CListControlImpl & p_control) : m_control(p_control) {}
|
||||
CRect operator[](t_size p_index) const {
|
||||
return m_control.GetItemRectAbs(p_index);
|
||||
}
|
||||
private:
|
||||
const CListControlImpl & m_control;
|
||||
};
|
||||
class RectSearchHelper_Groups {
|
||||
public:
|
||||
RectSearchHelper_Groups(const CListControlImpl & p_control) : m_control(p_control) {}
|
||||
CRect operator[](t_size p_index) const {
|
||||
CRect rect;
|
||||
if (!m_control.GetGroupHeaderRectAbs((int)(p_index + 1),rect)) rect.SetRectEmpty();
|
||||
return rect;
|
||||
}
|
||||
private:
|
||||
const CListControlImpl & m_control;
|
||||
};
|
||||
}
|
||||
|
||||
bool CListControlImpl::GetItemRange(const CRect & p_rect,t_size & p_base,t_size & p_count) const {
|
||||
CRect temp(p_rect); temp.OffsetRect( GetViewOffset() );
|
||||
return GetItemRangeAbs(temp, p_base, p_count);
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool CListControlImpl::GetItemRangeAbsInclHeaders(const CRect & p_rect,t_size & p_base,t_size & p_count) const {
|
||||
CRect temp(p_rect);
|
||||
temp.bottom += this->GetGroupHeaderHeight();
|
||||
return GetItemRangeAbs(temp, p_base, p_count);
|
||||
}
|
||||
|
||||
bool CListControlImpl::GetItemRangeAbs(const CRect & p_rect,t_size & p_base,t_size & p_count) const {
|
||||
if (p_rect.right < 0 || p_rect.left >= GetItemWidth()) return false;
|
||||
|
||||
return pfc::binarySearch<comparator_rect>::runGroup(RectSearchHelper_Items(*this),0,GetItemCount(),p_rect,p_base,p_count);
|
||||
}
|
||||
|
||||
void CListControlImpl::RenderRect(const CRect & p_rect,CDCHandle p_dc) {
|
||||
const CRect rectAbs = p_rect;
|
||||
|
||||
t_size base, count;
|
||||
if (GetItemRangeAbs(rectAbs,base,count)) {
|
||||
for(t_size walk = 0; walk < count; ++walk) {
|
||||
CRect rcUpdate, rcItem = GetItemRectAbs(base+walk);
|
||||
if (rcUpdate.IntersectRect(rcItem,p_rect)) {
|
||||
DCStateScope dcState(p_dc);
|
||||
if (p_dc.IntersectClipRect(rcUpdate) != NULLREGION) {
|
||||
try {
|
||||
RenderItem(base+walk,rcItem,rcUpdate,p_dc);
|
||||
} catch(std::exception const & e) {
|
||||
(void) e;
|
||||
// console::complain("List Control: Item rendering failure", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pfc::binarySearch<comparator_rect>::runGroup(RectSearchHelper_Groups(*this),0,GetGroupCount(),rectAbs,base,count)) {
|
||||
for(t_size walk = 0; walk < count; ++walk) {
|
||||
CRect rcHeader, rcUpdate;
|
||||
const int id = (int)(base+walk+1);
|
||||
if (GetGroupHeaderRectAbs(id,rcHeader) && rcUpdate.IntersectRect(rcHeader,p_rect)) {
|
||||
DCStateScope dcState(p_dc);
|
||||
if (p_dc.IntersectClipRect(rcUpdate) != NULLREGION) {
|
||||
try {
|
||||
RenderGroupHeader(id,rcHeader,rcUpdate,p_dc);
|
||||
} catch(std::exception const & e) {
|
||||
(void) e;
|
||||
// console::complain("List Control: Group header rendering failure", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RenderOverlay(p_rect,p_dc);
|
||||
}
|
||||
|
||||
CRect CListControlImpl::GetItemRect(t_size p_item) const {
|
||||
CRect rcItem = GetItemRectAbs(p_item);
|
||||
rcItem.OffsetRect( - GetViewOffset() );
|
||||
return rcItem;
|
||||
}
|
||||
bool CListControlImpl::GetGroupHeaderRect(int p_group,CRect & p_rect) const {
|
||||
if (!GetGroupHeaderRectAbs(p_group,p_rect)) return false;
|
||||
p_rect.OffsetRect( - GetViewOffset() );
|
||||
return true;
|
||||
}
|
||||
|
||||
int CListControlImpl::GetViewAreaHeight() const {
|
||||
const t_size itemCount = GetItemCount();
|
||||
int subAreaBase = 0;
|
||||
if (itemCount > 0) {
|
||||
subAreaBase = GetItemRectAbs(itemCount - 1).bottom;
|
||||
}
|
||||
return subAreaBase;
|
||||
}
|
||||
|
||||
CRect CListControlImpl::GetItemRectAbs(t_size p_item) const {
|
||||
CRect rcItem;
|
||||
const int itemHeight = GetItemHeight(), itemWidth = GetItemWidth(), groupHeight = GetGroupHeaderHeight(), itemGroup = GetItemGroup(p_item);
|
||||
rcItem.top = (int)p_item * itemHeight + groupHeight * itemGroup;
|
||||
rcItem.bottom = rcItem.top + itemHeight;
|
||||
rcItem.left = 0;
|
||||
rcItem.right = rcItem.left + itemWidth;
|
||||
return rcItem;
|
||||
}
|
||||
bool CListControlImpl::GetGroupHeaderRectAbs(int p_group,CRect & p_rect) const {
|
||||
if (p_group == 0) return false;
|
||||
t_size itemBase, itemCount;
|
||||
if (!ResolveGroupRange(p_group,itemBase,itemCount)) return false;
|
||||
const int itemHeight = GetItemHeight(), itemWidth = GetItemWidth(), groupHeight = GetGroupHeaderHeight();
|
||||
p_rect.bottom = (int) itemBase * itemHeight + groupHeight * p_group;
|
||||
p_rect.top = p_rect.bottom - groupHeight;
|
||||
p_rect.left = 0;
|
||||
p_rect.right = p_rect.left + itemWidth;
|
||||
return true;
|
||||
}
|
||||
|
||||
CRect CListControlImpl::GetViewAreaRectAbs() const {
|
||||
return CRect(0,0,GetViewAreaWidth(),GetViewAreaHeight());
|
||||
}
|
||||
|
||||
CRect CListControlImpl::GetViewAreaRect() const {
|
||||
CRect rc = GetViewAreaRectAbs();
|
||||
rc.OffsetRect( - GetViewOffset() );
|
||||
CRect ret; ret.IntersectRect(rc,GetClientRectHook());
|
||||
return ret;
|
||||
}
|
||||
|
||||
t_size CListControlImpl::GetGroupCount() const {
|
||||
const t_size itemCount = GetItemCount();
|
||||
if (itemCount > 0) {
|
||||
return (t_size) GetItemGroup(itemCount-1);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void CListControlImpl::UpdateGroupHeader(int p_id) {
|
||||
CRect rect;
|
||||
if (GetGroupHeaderRect(p_id,rect)) {
|
||||
InvalidateRect(rect);
|
||||
}
|
||||
}
|
||||
static void AddUpdateRect(HRGN p_rgn,CRect const & p_rect) {
|
||||
CRgn temp; temp.CreateRectRgnIndirect(p_rect);
|
||||
CRgnHandle(p_rgn).CombineRgn(temp,RGN_OR);
|
||||
}
|
||||
|
||||
void CListControlImpl::OnItemsReordered( const size_t * order, size_t count ) {
|
||||
PFC_ASSERT( count == GetItemCount() );
|
||||
ReloadItems( pfc::bit_array_order_changed(order) );
|
||||
}
|
||||
void CListControlImpl::UpdateItems(const pfc::bit_array & p_mask) {
|
||||
t_size base,count;
|
||||
if (GetItemRangeAbs(GetVisibleRectAbs(),base,count)) {
|
||||
const t_size max = base+count;
|
||||
CRgn updateRgn; updateRgn.CreateRectRgn(0,0,0,0);
|
||||
bool found = false;
|
||||
for(t_size walk = p_mask.find_first(true,base,max); walk < max; walk = p_mask.find_next(true,walk,max)) {
|
||||
found = true;
|
||||
AddUpdateRect(updateRgn,GetItemRect(walk));
|
||||
}
|
||||
if (found) {
|
||||
InvalidateRgn(updateRgn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CListControlImpl::UpdateItemsAndHeaders(const pfc::bit_array & p_mask) {
|
||||
t_size base,count;
|
||||
int groupWalk = 0;
|
||||
if (GetItemRangeAbsInclHeaders(GetVisibleRectAbs(),base,count)) {
|
||||
const t_size max = base+count;
|
||||
CRgn updateRgn; updateRgn.CreateRectRgn(0,0,0,0);
|
||||
bool found = false;
|
||||
for(t_size walk = p_mask.find_first(true,base,max); walk < max; walk = p_mask.find_next(true,walk,max)) {
|
||||
found = true;
|
||||
const int groupId = GetItemGroup(walk);
|
||||
if (groupId != groupWalk) {
|
||||
if (groupId > 0) {
|
||||
CRect rect;
|
||||
if (GetGroupHeaderRect(groupId,rect)) {
|
||||
AddUpdateRect(updateRgn,rect);
|
||||
}
|
||||
}
|
||||
groupWalk = groupId;
|
||||
}
|
||||
AddUpdateRect(updateRgn,GetItemRect(walk));
|
||||
}
|
||||
if (found) {
|
||||
InvalidateRgn(updateRgn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CRect CListControlImpl::GetValidViewOriginArea() const {
|
||||
const CRect rcView = GetViewAreaRectAbs();
|
||||
const CRect rcClient = GetClientRectHook();
|
||||
CRect rcArea = rcView;
|
||||
rcArea.right -= pfc::min_t(rcView.Width(),rcClient.Width());
|
||||
rcArea.bottom -= pfc::min_t(rcView.Height(),rcClient.Height());
|
||||
return rcArea;
|
||||
}
|
||||
|
||||
void CListControlImpl::OnViewAreaChanged(CPoint p_originOverride) {
|
||||
const CPoint oldViewOrigin = m_viewOrigin;
|
||||
m_viewOrigin = ClipPointToRect(p_originOverride,GetValidViewOriginArea());
|
||||
|
||||
RefreshSliders();
|
||||
|
||||
Invalidate();
|
||||
|
||||
if (oldViewOrigin != m_viewOrigin) {
|
||||
OnViewOriginChange(m_viewOrigin - oldViewOrigin);
|
||||
}
|
||||
}
|
||||
|
||||
bool CListControlImpl::ItemFromPointAbs(CPoint const & p_pt,t_size & p_item) const {
|
||||
if (p_pt.x < 0 || p_pt.x >= GetItemWidth()) return false;
|
||||
t_size dummy;
|
||||
return GetItemRangeAbs(CRect(p_pt,p_pt + CPoint(1,1)),p_item,dummy);
|
||||
}
|
||||
|
||||
bool CListControlImpl::GroupHeaderFromPointAbs(CPoint const & p_pt,int & p_group) const {
|
||||
if (p_pt.x < 0 || p_pt.x >= GetItemWidth()) return false;
|
||||
t_size result;
|
||||
|
||||
if (!pfc::binarySearch<comparator_rect>::run(RectSearchHelper_Groups(*this),0,GetGroupCount(),CRect(p_pt,p_pt + CSize(1,1)),result)) return false;
|
||||
|
||||
|
||||
//if (!pfc::bsearch_t(GetGroupCount(),RectSearchHelper_Groups(*this),RectCompare,CRect(p_pt,p_pt),result)) return false;
|
||||
p_group = (int) (result + 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
void CListControlImpl::OnThemeChanged() {
|
||||
m_themeCache.remove_all();
|
||||
}
|
||||
|
||||
CTheme & CListControlImpl::themeFor(const char * what) {
|
||||
bool bNew;
|
||||
auto & ret = this->m_themeCache.find_or_add_ex( what, bNew );
|
||||
if (bNew) ret.OpenThemeData(*this, pfc::stringcvt::string_wide_from_utf8(what));
|
||||
return ret;
|
||||
}
|
||||
|
||||
LRESULT CListControlImpl::OnCreatePassThru(UINT,WPARAM,LPARAM,BOOL& bHandled) {
|
||||
::SetWindowTheme(*this, _T("explorer"), NULL);
|
||||
OnViewAreaChanged();
|
||||
|
||||
if (m_gestureAPI.IsAvailable()) {
|
||||
GESTURECONFIG config = {GID_PAN, GC_PAN_WITH_SINGLE_FINGER_VERTICALLY|GC_PAN_WITH_INERTIA, GC_PAN_WITH_SINGLE_FINGER_HORIZONTALLY | GC_PAN_WITH_GUTTER};
|
||||
m_gestureAPI.SetGestureConfig( *this, 0, 1, &config, sizeof(GESTURECONFIG));
|
||||
}
|
||||
|
||||
bHandled = FALSE;
|
||||
return 0;
|
||||
}
|
||||
bool CListControlImpl::IsSameItemOrHeaderAbs(const CPoint & p_point1, const CPoint & p_point2) const {
|
||||
t_size item1, item2; int group1, group2;
|
||||
if (ItemFromPointAbs(p_point1, item1)) {
|
||||
if (ItemFromPointAbs(p_point2,item2)) {
|
||||
return item1 == item2;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (GroupHeaderFromPointAbs(p_point1, group1)) {
|
||||
if (GroupHeaderFromPointAbs(p_point2, group2)) {
|
||||
return group1 == group2;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CListControlImpl::OnSizeAsync_Trigger() {
|
||||
if (!m_sizeAsyncPending) {
|
||||
if (PostMessage(MSG_SIZE_ASYNC,0,0)) {
|
||||
m_sizeAsyncPending = true;
|
||||
} else {
|
||||
PFC_ASSERT(!"Shouldn't get here!");
|
||||
//should not happen
|
||||
ListHandleResize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CListControlImpl::ListHandleResize() {
|
||||
MoveViewOriginDelta(CPoint(0,0));
|
||||
m_sizeAsyncPending = false;
|
||||
}
|
||||
|
||||
void CListControlImpl::AddGroupHeaderToUpdateRgn(HRGN p_rgn, int id) const {
|
||||
if (id > 0) {
|
||||
CRect rcHeader;
|
||||
if (GetGroupHeaderRect(id,rcHeader)) AddUpdateRect(p_rgn,rcHeader);
|
||||
}
|
||||
}
|
||||
void CListControlImpl::AddItemToUpdateRgn(HRGN p_rgn, t_size p_index) const {
|
||||
if (p_index < this->GetItemCount()) {
|
||||
AddUpdateRect(p_rgn,GetItemRect(p_index));
|
||||
}
|
||||
}
|
||||
|
||||
COLORREF CListControlImpl::GetSysColorHook(int colorIndex) const {
|
||||
return GetSysColor(colorIndex);
|
||||
}
|
||||
|
||||
LRESULT CListControlImpl::OnEraseBkgnd(UINT,WPARAM wp,LPARAM,BOOL&) {
|
||||
#ifndef CListControl_ScrollWindowFix
|
||||
const CRect rcClient = GetClientRectHook();
|
||||
PaintUtils::FillRectSimple((HDC)wp,rcClient,GetColor(ui_color_background));
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
t_size CListControlImpl::InsertIndexFromPointEx(const CPoint & pt, bool & bInside) const {
|
||||
bInside = false;
|
||||
t_size insertMark = ~0;
|
||||
t_size itemIdx; int groupId;
|
||||
CPoint test(pt); test += GetViewOffset();
|
||||
test.x = GetItemWidth() / 2;
|
||||
if (test.y >= GetViewAreaHeight()) {
|
||||
return GetItemCount();
|
||||
} else if (ItemFromPointAbs(test,itemIdx)) {
|
||||
const CRect rc = GetItemRectAbs(itemIdx);
|
||||
if (test.y > rc.top + MulDiv(rc.Height(),2,3)) itemIdx++;
|
||||
else if (test.y >= rc.top + MulDiv(rc.Height(),1,3)) bInside = true;
|
||||
return itemIdx;
|
||||
} else if (GroupHeaderFromPointAbs(test,groupId)) {
|
||||
t_size base,count;
|
||||
if (ResolveGroupRange(groupId,base,count)) {
|
||||
return base;
|
||||
}
|
||||
}
|
||||
return ~0;
|
||||
}
|
||||
t_size CListControlImpl::InsertIndexFromPoint(const CPoint & pt) const {
|
||||
bool dummy; return InsertIndexFromPointEx(pt,dummy);
|
||||
}
|
||||
|
||||
COLORREF CListControlImpl::BlendGridColor( COLORREF bk ) {
|
||||
return BlendGridColor( bk, PaintUtils::DetermineTextColor( bk ) );
|
||||
}
|
||||
|
||||
COLORREF CListControlImpl::BlendGridColor( COLORREF bk, COLORREF tx ) {
|
||||
return PaintUtils::BlendColor(bk, tx, 10);
|
||||
}
|
||||
|
||||
COLORREF CListControlImpl::GridColor() {
|
||||
return BlendGridColor( GetSysColorHook(colorBackground), GetSysColorHook(colorText) );
|
||||
}
|
||||
|
||||
void CListControlImpl::RenderItemBackground(CDCHandle p_dc,const CRect & p_itemRect,size_t p_item, uint32_t bkColor) {
|
||||
switch( this->m_rowStyle ) {
|
||||
case rowStylePlaylistDelimited:
|
||||
PaintUtils::RenderItemBackground(p_dc,p_itemRect,p_item+GetItemGroup(p_item),bkColor);
|
||||
{
|
||||
auto blend = BlendGridColor(bkColor);
|
||||
CDCPen pen(p_dc, blend);
|
||||
SelectObjectScope scope(p_dc, pen);
|
||||
|
||||
p_dc.MoveTo( p_itemRect.right-1, p_itemRect.top );
|
||||
p_dc.LineTo( p_itemRect.right-1, p_itemRect.bottom );
|
||||
}
|
||||
break;
|
||||
case rowStylePlaylist:
|
||||
PaintUtils::RenderItemBackground(p_dc,p_itemRect,p_item+GetItemGroup(p_item),bkColor);
|
||||
break;
|
||||
case rowStyleGrid:
|
||||
PaintUtils::FillRectSimple(p_dc, p_itemRect, bkColor );
|
||||
{
|
||||
auto blend = BlendGridColor(bkColor);
|
||||
CDCBrush brush(p_dc, blend);
|
||||
p_dc.FrameRect(&p_itemRect, brush);
|
||||
|
||||
}
|
||||
break;
|
||||
case rowStyleFlat:
|
||||
PaintUtils::FillRectSimple(p_dc, p_itemRect, bkColor );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CListControlImpl::RenderGroupHeaderBackground(CDCHandle p_dc,const CRect & p_headerRect,int p_group) {
|
||||
const t_uint32 bkColor = GetSysColorHook(colorBackground);
|
||||
t_size index = 0;
|
||||
t_size base, count;
|
||||
if (p_group > 0 && ResolveGroupRange(p_group,base,count)) {
|
||||
index = base + (t_size) p_group - 1;
|
||||
}
|
||||
switch( this->m_rowStyle ) {
|
||||
default:
|
||||
PaintUtils::FillRectSimple( p_dc, p_headerRect, bkColor );
|
||||
break;
|
||||
case rowStylePlaylistDelimited:
|
||||
case rowStylePlaylist:
|
||||
PaintUtils::RenderItemBackground(p_dc,p_headerRect,index,bkColor);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CListControlImpl::RenderItem(t_size p_item,const CRect & p_itemRect,const CRect & p_updateRect,CDCHandle p_dc) {
|
||||
this->RenderItemBackground(p_dc, p_itemRect, p_item, GetSysColorHook(colorBackground) );
|
||||
|
||||
DCStateScope backup(p_dc);
|
||||
p_dc.SetBkMode(TRANSPARENT);
|
||||
p_dc.SetBkColor(GetSysColorHook(colorBackground));
|
||||
p_dc.SetTextColor(GetSysColorHook(colorText));
|
||||
|
||||
RenderItemText(p_item,p_itemRect,p_updateRect,p_dc, true);
|
||||
}
|
||||
|
||||
void CListControlImpl::RenderGroupHeader(int p_group,const CRect & p_headerRect,const CRect & p_updateRect,CDCHandle p_dc) {
|
||||
this->RenderGroupHeaderBackground(p_dc, p_headerRect, p_group );
|
||||
|
||||
DCStateScope backup(p_dc);
|
||||
p_dc.SetBkMode(TRANSPARENT);
|
||||
p_dc.SetBkColor(GetSysColorHook(colorBackground));
|
||||
p_dc.SetTextColor(GetSysColorHook(colorHighlight));
|
||||
|
||||
RenderGroupHeaderText(p_group,p_headerRect,p_updateRect,p_dc);
|
||||
}
|
||||
|
||||
|
||||
CListControlFontOps::CListControlFontOps() : m_font((HFONT)::GetStockObject(DEFAULT_GUI_FONT)), m_itemHeight(), m_groupHeaderHeight() {
|
||||
UpdateGroupHeaderFont();
|
||||
CalculateHeights();
|
||||
}
|
||||
|
||||
void CListControlFontOps::UpdateGroupHeaderFont() {
|
||||
try {
|
||||
m_groupHeaderFont = NULL;
|
||||
LOGFONT lf = {};
|
||||
WIN32_OP_D( m_font.GetLogFont(lf) );
|
||||
lf.lfHeight = pfc::rint32( (double) lf.lfHeight * GroupHeaderFontScale() );
|
||||
lf.lfWeight = GroupHeaderFontWeight(lf.lfWeight);
|
||||
WIN32_OP_D( m_groupHeaderFont.CreateFontIndirect(&lf) != NULL );
|
||||
} catch(std::exception const & e) {
|
||||
(void) e;
|
||||
// console::print(e.what());
|
||||
m_groupHeaderFont = (HFONT)::GetStockObject(DEFAULT_GUI_FONT);
|
||||
}
|
||||
}
|
||||
|
||||
void CListControlFontOps::CalculateHeights() {
|
||||
const t_uint32 spacing = MulDiv(4, m_dpi.cy, 96);
|
||||
m_itemHeight = GetFontHeight( m_font ) + spacing;
|
||||
m_groupHeaderHeight = GetFontHeight( m_groupHeaderFont ) + spacing;
|
||||
}
|
||||
|
||||
void CListControlFontOps::SetFont(HFONT font,bool bUpdateView) {
|
||||
m_font = font;
|
||||
UpdateGroupHeaderFont(); CalculateHeights();
|
||||
OnSetFont(bUpdateView);
|
||||
if (bUpdateView && m_hWnd != NULL) OnViewAreaChanged();
|
||||
|
||||
}
|
||||
|
||||
LRESULT CListControlFontOps::OnSetFont(UINT,WPARAM wp,LPARAM,BOOL&) {
|
||||
SetFont((HFONT)wp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
LRESULT CListControlFontOps::OnGetFont(UINT,WPARAM,LPARAM,BOOL&) {
|
||||
return (LRESULT)(HFONT)m_font;
|
||||
}
|
||||
|
||||
void CListControlImpl::SetCaptureEx(CaptureProc_t proc) {
|
||||
this->m_captureProc = proc; SetCapture();
|
||||
}
|
||||
|
||||
LRESULT CListControlImpl::MousePassThru(UINT msg, WPARAM wp, LPARAM lp) {
|
||||
auto p = m_captureProc; // create local ref in case something in mid-captureproc clears it
|
||||
if ( p ) {
|
||||
CPoint pt(lp);
|
||||
if (!p(msg, (DWORD) wp, pt ) ) {
|
||||
ReleaseCapture();
|
||||
m_captureProc = nullptr;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
SetMsgHandled(FALSE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
LRESULT CListControlImpl::OnGetDlgCode(UINT, WPARAM wp, LPARAM) {
|
||||
switch(wp) {
|
||||
case VK_RETURN:
|
||||
return m_dlgWantEnter ? DLGC_WANTMESSAGE : 0;
|
||||
default:
|
||||
SetMsgHandled(FALSE);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void CListControlImpl::CreateInDialog(CWindow wndDialog, UINT replaceControlID ) {
|
||||
CWindow lstReplace = wndDialog.GetDlgItem(replaceControlID);
|
||||
PFC_ASSERT( lstReplace != NULL );
|
||||
auto status = lstReplace.SendMessage(WM_GETDLGCODE, VK_RETURN );
|
||||
m_dlgWantEnter = (status & DLGC_WANTMESSAGE);
|
||||
CRect rc;
|
||||
CWindow wndPrev = wndDialog.GetNextDlgTabItem(lstReplace, TRUE);
|
||||
WIN32_OP_D( lstReplace.GetWindowRect(&rc) );
|
||||
WIN32_OP_D( wndDialog.ScreenToClient(rc) );
|
||||
WIN32_OP_D( lstReplace.DestroyWindow() );
|
||||
WIN32_OP_D( this->Create(wndDialog, &rc, 0, 0, WS_EX_STATICEDGE, replaceControlID) );
|
||||
if (wndPrev != NULL ) this->SetWindowPos(wndPrev, 0,0,0,0, SWP_NOSIZE | SWP_NOMOVE );
|
||||
this->SetFont(wndDialog.GetFont());
|
||||
}
|
||||
|
||||
|
||||
void CListControlImpl::defer(std::function<void() > f) {
|
||||
m_deferred.push_back( f );
|
||||
if (!m_defferredMsgPending) {
|
||||
if ( PostMessage(MSG_EXEC_DEFERRED) ) m_defferredMsgPending = true;
|
||||
}
|
||||
}
|
||||
|
||||
LRESULT CListControlImpl::OnExecDeferred(UINT, WPARAM, LPARAM) {
|
||||
|
||||
for ( ;; ) {
|
||||
auto i = m_deferred.begin();
|
||||
if ( i == m_deferred.end() ) break;
|
||||
auto op = std::move(*i);
|
||||
m_deferred.erase(i); // erase first, execute later - avoid erratic behavior if op alters the list
|
||||
op();
|
||||
}
|
||||
|
||||
m_defferredMsgPending = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ========================================================================================
|
||||
// Mouse wheel vs drag&drop hacks
|
||||
// Install MouseHookProc for the duration of DoDragDrop and handle the input from there
|
||||
// ========================================================================================
|
||||
static HHOOK g_hook = NULL;
|
||||
static CListControlImpl * g_dragDropInstance = nullptr;
|
||||
LRESULT CALLBACK CListControlImpl::MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam) {
|
||||
if (nCode == HC_ACTION && g_dragDropInstance != nullptr) {
|
||||
switch (wParam) {
|
||||
case WM_MOUSEWHEEL:
|
||||
case WM_MOUSEHWHEEL:
|
||||
g_dragDropInstance->MouseWheelFromHook((UINT)wParam, lParam);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return CallNextHookEx(g_hook, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
bool CListControlImpl::MouseWheelFromHook(UINT msg, LPARAM data) {
|
||||
MOUSEHOOKSTRUCTEX const * mhs = reinterpret_cast<MOUSEHOOKSTRUCTEX const *> ( data );
|
||||
if ( ::WindowFromPoint(mhs->pt) != m_hWnd ) return false;
|
||||
LRESULT dummyResult = 0;
|
||||
WPARAM wp = mhs->mouseData;
|
||||
LPARAM lp = MAKELPARAM( mhs->pt.x, mhs->pt.y );
|
||||
// If we get here, m_suppressMouseWheel should be true per our DoDragDrop()
|
||||
pfc::vartoggle_t<bool> scope(m_suppressMouseWheel, false);
|
||||
this->ProcessWindowMessage( m_hWnd, msg, wp, lp, dummyResult );
|
||||
return true;
|
||||
}
|
||||
|
||||
HRESULT CListControlImpl::DoDragDrop(LPDATAOBJECT pDataObj, LPDROPSOURCE pDropSource, DWORD dwOKEffects, LPDWORD pdwEffect) {
|
||||
HRESULT ret = E_FAIL;
|
||||
// Should not get here with non null g_dragDropInstance - means we have a recursive call
|
||||
PFC_ASSERT(g_dragDropInstance == nullptr);
|
||||
if ( g_dragDropInstance == nullptr ) {
|
||||
// futureproofing: kill mouse wheel message processing if we get them delivered the regular way while this is in progress
|
||||
pfc::vartoggle_t<bool> scope(m_suppressMouseWheel, true);
|
||||
g_dragDropInstance = this;
|
||||
g_hook = SetWindowsHookEx(WH_MOUSE, MouseHookProc, NULL, GetCurrentThreadId());
|
||||
try {
|
||||
ret = ::DoDragDrop(pDataObj, pDropSource, dwOKEffects, pdwEffect);
|
||||
} catch (...) {
|
||||
}
|
||||
g_dragDropInstance = nullptr;
|
||||
UnhookWindowsHookEx(pfc::replace_null_t(g_hook));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void CListControlImpl::OnKillFocus(CWindow) {
|
||||
if (m_captureProc) {
|
||||
ReleaseCapture();
|
||||
m_captureProc = nullptr;
|
||||
}
|
||||
SetMsgHandled(FALSE);
|
||||
}
|
||||
|
||||
void CListControlImpl::OnWindowPosChanged(LPWINDOWPOS arg) {
|
||||
if ( arg->flags & SWP_HIDEWINDOW ) {
|
||||
if (m_captureProc) {
|
||||
ReleaseCapture();
|
||||
m_captureProc = nullptr;
|
||||
}
|
||||
}
|
||||
SetMsgHandled(FALSE);
|
||||
}
|
||||
|
||||
void CListControlHeaderImpl::RenderItemBackground(CDCHandle p_dc,const CRect & p_itemRect,size_t item, uint32_t bkColor) {
|
||||
if ( ! this->DelimitColumns() ) {
|
||||
__super::RenderItemBackground(p_dc, p_itemRect, item, bkColor);
|
||||
} else {
|
||||
auto cnt = this->GetColumnCount();
|
||||
uint32_t x = 0;
|
||||
for( size_t walk = 0; walk < cnt; ) {
|
||||
auto span = this->GetSubItemSpan( item, walk );
|
||||
PFC_ASSERT( span > 0 );
|
||||
uint32_t width = 0;
|
||||
for( size_t walk2 = 0; walk2 < span; ++ walk2 ) {
|
||||
width += this->GetSubItemWidth( walk + walk2 );
|
||||
}
|
||||
CRect rc = p_itemRect;
|
||||
rc.left = x;
|
||||
x += width;
|
||||
rc.right = x;
|
||||
__super::RenderItemBackground(p_dc, rc, item, bkColor);
|
||||
walk += span;
|
||||
}
|
||||
}
|
||||
}
|
||||
328
libPPUI/CListControl.h
Normal file
328
libPPUI/CListControl.h
Normal file
@@ -0,0 +1,328 @@
|
||||
#pragma once
|
||||
|
||||
// ================================================================================
|
||||
// Main CListControl implementation
|
||||
//
|
||||
// For ready-to-use CListControl specializations,
|
||||
// see CListControlSimple.h and CListControlOwnerData.h
|
||||
// ================================================================================
|
||||
|
||||
|
||||
#pragma comment(lib, "uxtheme.lib")
|
||||
|
||||
#include <functional>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include "CMiddleDragImpl.h"
|
||||
#include "wtl-pp.h"
|
||||
#include "gesture.h"
|
||||
#include "gdiplus_helpers.h"
|
||||
|
||||
#define CListControl_ScrollWindowFix
|
||||
|
||||
#ifdef CListControl_ScrollWindowFix
|
||||
#define WS_EX_COMPOSITED_CListControl 0
|
||||
#else
|
||||
#define WS_EX_COMPOSITED_CListControl WS_EX_COMPOSITED
|
||||
#endif
|
||||
|
||||
|
||||
typedef std::function< bool ( UINT, DWORD, CPoint ) > CaptureProc_t;
|
||||
|
||||
typedef CWinTraits<WS_VSCROLL | WS_HSCROLL | WS_TABSTOP | WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, WS_EX_COMPOSITED_CListControl> CListControlTraits;
|
||||
|
||||
class CListControlImpl : public CWindowImpl<CListControlImpl,CWindow,CListControlTraits> {
|
||||
public:
|
||||
CListControlImpl() {}
|
||||
|
||||
DECLARE_WND_CLASS_EX(TEXT("{4B94B650-C2D8-40de-A0AD-E8FADF62D56C}"),CS_DBLCLKS,COLOR_WINDOW);
|
||||
|
||||
// Wrapper around CWindowImpl::Create().
|
||||
// Creates CListControl replacing another dialog control with the specified ID.
|
||||
// Note that m_dlgWantEnter is set to false by this method, as it's typically unwanted in dialogs.
|
||||
void CreateInDialog( CWindow wndDialog, UINT replaceControlID );
|
||||
|
||||
enum {
|
||||
MSG_SIZE_ASYNC = WM_USER + 13,
|
||||
MSG_EXEC_DEFERRED,
|
||||
UserMsgBase
|
||||
};
|
||||
|
||||
BEGIN_MSG_MAP_EX(CListControlImpl)
|
||||
MESSAGE_RANGE_HANDLER_EX(WM_MOUSEFIRST, WM_MOUSELAST, MousePassThru);
|
||||
MESSAGE_HANDLER_EX(MSG_EXEC_DEFERRED, OnExecDeferred);
|
||||
MESSAGE_HANDLER(WM_PAINT,OnPaint);
|
||||
MSG_WM_PRINTCLIENT(OnPrintClient);
|
||||
MESSAGE_HANDLER(WM_VSCROLL,OnVScroll);
|
||||
MESSAGE_HANDLER(WM_HSCROLL,OnHScroll);
|
||||
MESSAGE_HANDLER(WM_SIZE,OnSize);
|
||||
MESSAGE_HANDLER(WM_MOUSEHWHEEL,OnHWheel);
|
||||
MESSAGE_HANDLER(WM_MOUSEWHEEL,OnVWheel);
|
||||
MESSAGE_HANDLER(WM_LBUTTONDOWN,SetFocusPassThru);
|
||||
MESSAGE_HANDLER(WM_RBUTTONDOWN,SetFocusPassThru);
|
||||
MESSAGE_HANDLER(WM_MBUTTONDOWN,SetFocusPassThru);
|
||||
MESSAGE_HANDLER(WM_LBUTTONDBLCLK,SetFocusPassThru);
|
||||
MESSAGE_HANDLER(WM_RBUTTONDBLCLK,SetFocusPassThru);
|
||||
MESSAGE_HANDLER(WM_MBUTTONDBLCLK,SetFocusPassThru);
|
||||
MESSAGE_HANDLER(WM_CREATE,OnCreatePassThru);
|
||||
MESSAGE_HANDLER(WM_ERASEBKGND,OnEraseBkgnd);
|
||||
MESSAGE_HANDLER(MSG_SIZE_ASYNC,OnSizeAsync);
|
||||
MESSAGE_HANDLER(WM_GESTURE, OnGesture)
|
||||
MSG_WM_THEMECHANGED(OnThemeChanged)
|
||||
MSG_WM_KILLFOCUS(OnKillFocus)
|
||||
MSG_WM_WINDOWPOSCHANGED(OnWindowPosChanged)
|
||||
MESSAGE_HANDLER_EX( WM_GETDLGCODE, OnGetDlgCode )
|
||||
END_MSG_MAP()
|
||||
|
||||
virtual void ReloadData() { OnViewAreaChanged(); }
|
||||
virtual void ReloadItems( pfc::bit_array const & mask ) { UpdateItems( mask); }
|
||||
|
||||
//! Hookable function called in response to reordering of items. Redraws the view and updates internal data to reflect the change.
|
||||
virtual void OnItemsReordered( const size_t * order, size_t count );
|
||||
//! Hookable function called in response to removal of items. Redraws the view and updates internal data to reflect the change.
|
||||
virtual void OnItemsRemoved( pfc::bit_array const & mask, size_t oldCount ) { ReloadData(); }
|
||||
//! Hookable function called in response to insertion of items. Redraws the view and updates internal data to reflect the change.
|
||||
virtual void OnItemsInserted( size_t at, size_t count, bool bSelect ) { ReloadData(); }
|
||||
|
||||
void ReloadItem(size_t i) { ReloadItems( pfc::bit_array_one(i) ); }
|
||||
void OnViewAreaChanged() {OnViewAreaChanged(GetViewOrigin());}
|
||||
void OnViewAreaChanged(CPoint p_originOverride);
|
||||
void UpdateGroupHeader(int p_id);
|
||||
void UpdateItems(const pfc::bit_array & p_mask);
|
||||
void UpdateItemsAndHeaders(const pfc::bit_array & p_mask);
|
||||
void UpdateItem(t_size p_item) {UpdateItems(pfc::bit_array_one(p_item));}
|
||||
void UpdateItemsAll() {Invalidate();}
|
||||
void EnsureItemVisible(t_size p_item, bool bUser = false);
|
||||
void EnsureHeaderVisible(int p_group);
|
||||
virtual void EnsureVisibleRectAbs(const CRect & p_rect);
|
||||
CRect GetItemRect(t_size p_item) const;
|
||||
bool GetGroupHeaderRect(int p_group,CRect & p_rect) const;
|
||||
CRect GetItemRectAbs(t_size p_item) const;
|
||||
bool GetGroupHeaderRectAbs(int p_group,CRect & p_rect) const;
|
||||
CPoint GetViewOrigin() const {return m_viewOrigin;}
|
||||
CPoint GetViewOffset() const {return GetViewOrigin() - GetClientOrigin();}
|
||||
int GetViewAreaWidth() const {return GetItemWidth();}
|
||||
int GetViewAreaHeight() const;
|
||||
CRect GetViewAreaRectAbs() const;
|
||||
CRect GetViewAreaRect() const;
|
||||
CRect GetValidViewOriginArea() const;
|
||||
t_size GetGroupCount() const;
|
||||
bool GetItemRangeAbs(const CRect & p_rect,t_size & p_base,t_size & p_count) const;
|
||||
bool GetItemRangeAbsInclHeaders(const CRect & p_rect,t_size & p_base,t_size & p_count) const;
|
||||
bool GetItemRange(const CRect & p_rect,t_size & p_base,t_size & p_count) const;
|
||||
void MoveViewOriginNoClip(CPoint p_target);
|
||||
void MoveViewOrigin(CPoint p_target);
|
||||
CPoint ClipViewOrigin(CPoint p_origin) const;
|
||||
void MoveViewOriginDelta(CPoint p_delta) {MoveViewOrigin( GetViewOrigin() + p_delta );}
|
||||
void MoveViewOriginDeltaNoClip(CPoint p_delta) {MoveViewOriginNoClip( GetViewOrigin() + p_delta );}
|
||||
bool ItemFromPoint(CPoint const & p_pt,t_size & p_item) const {return ItemFromPointAbs(p_pt + GetViewOffset(),p_item);}
|
||||
bool GroupHeaderFromPoint(CPoint const & p_pt,int & p_group) const {return GroupHeaderFromPointAbs(p_pt + GetViewOffset(),p_group);}
|
||||
bool ItemFromPointAbs(CPoint const & p_pt,t_size & p_item) const;
|
||||
bool GroupHeaderFromPointAbs(CPoint const & p_pt,int & p_group) const;
|
||||
|
||||
bool ResolveGroupRange(int p_id,t_size & p_base,t_size & p_count) const;
|
||||
|
||||
virtual int GetGroupHeaderHeight() const {return 0;}
|
||||
virtual int GetItemHeight() const {return 0;}
|
||||
virtual int GetItemWidth() const {return 0;}
|
||||
virtual t_size GetItemCount() const {return 0;}
|
||||
virtual int GetItemGroup(t_size p_item) const {return 0;}
|
||||
//override optionally
|
||||
virtual void RenderItem(t_size p_item,const CRect & p_itemRect,const CRect & p_updateRect,CDCHandle p_dc);
|
||||
//override optionally
|
||||
virtual void RenderGroupHeader(int p_group,const CRect & p_headerRect,const CRect & p_updateRect,CDCHandle p_dc);
|
||||
|
||||
//called by default RenderItem implementation
|
||||
virtual void RenderItemText(t_size p_item,const CRect & p_itemRect,const CRect & p_updateRect,CDCHandle p_dc, bool allowColors) {}
|
||||
//called by default RenderItem implementation
|
||||
virtual void RenderGroupHeaderText(int p_group,const CRect & p_headerRect,const CRect & p_updateRect,CDCHandle p_dc) {}
|
||||
|
||||
virtual void RenderItemBackground(CDCHandle p_dc,const CRect & p_itemRect,size_t item, uint32_t bkColor);
|
||||
virtual void RenderGroupHeaderBackground(CDCHandle p_dc,const CRect & p_headerRect,int iGroup);
|
||||
|
||||
virtual void RenderBackground( CDCHandle dc, CRect const & rc );
|
||||
|
||||
virtual void OnViewOriginChange(CPoint p_delta) {}
|
||||
virtual void RenderOverlay(const CRect & p_updaterect,CDCHandle p_dc) {}
|
||||
virtual bool FixedOverlayPresent() {return false;}
|
||||
|
||||
virtual CRect GetClientRectHook() const;
|
||||
|
||||
enum {
|
||||
colorText = COLOR_WINDOWTEXT,
|
||||
colorBackground = COLOR_WINDOW,
|
||||
colorHighlight = COLOR_HOTLIGHT,
|
||||
colorSelection = COLOR_HIGHLIGHT,
|
||||
};
|
||||
|
||||
virtual COLORREF GetSysColorHook( int colorIndex ) const;
|
||||
|
||||
//! Called by CListControlWithSelectionBase.
|
||||
virtual void OnItemClicked(t_size item, CPoint pt) {}
|
||||
//! Called by CListControlWithSelectionBase.
|
||||
virtual void OnGroupHeaderClicked(int groupId, CPoint pt) {}
|
||||
|
||||
//! Return true to indicate that some area of the control has a special purpose and clicks there should not trigger changes in focus/selection.
|
||||
virtual bool OnClickedSpecialHitTest(CPoint pt) { return false; }
|
||||
virtual bool OnClickedSpecial(DWORD status, CPoint pt) {return false;}
|
||||
|
||||
virtual bool AllowScrollbar(bool vertical) const {return true;}
|
||||
|
||||
CPoint GetClientOrigin() const {return GetClientRectHook().TopLeft();}
|
||||
CRect GetVisibleRectAbs() const {
|
||||
CRect view = GetClientRectHook();
|
||||
view.OffsetRect( GetViewOrigin() - view.TopLeft() );
|
||||
return view;
|
||||
}
|
||||
|
||||
bool IsSameItemOrHeaderAbs(const CPoint & p_point1, const CPoint & p_point2) const;
|
||||
|
||||
void AddItemToUpdateRgn(HRGN p_rgn, t_size p_index) const;
|
||||
void AddGroupHeaderToUpdateRgn(HRGN p_rgn, int id) const;
|
||||
|
||||
t_size InsertIndexFromPoint(const CPoint & p_pt) const;
|
||||
//! Translate point to insert location for drag and drop. \n
|
||||
//! Made virtual so it can be specialized to allow only specific drop locations.
|
||||
virtual t_size InsertIndexFromPointEx(const CPoint & pt, bool & bInside) const;
|
||||
|
||||
virtual void ListHandleResize();
|
||||
|
||||
//! Can smooth-scroll *now* ? Used to suppress smooth scroll on temporary basis due to specific user operations in progress
|
||||
virtual bool CanSmoothScroll() const { return true; }
|
||||
//! Is smooth scroll enabled by user?
|
||||
virtual bool UserEnabledSmoothScroll() const;
|
||||
virtual bool ToggleSelectedItemsHook(pfc::bit_array const & mask) { return false; }
|
||||
|
||||
void SetCaptureEx(CaptureProc_t proc);
|
||||
void SetCaptureMsgHandled(BOOL v) { this->SetMsgHandled(v); }
|
||||
|
||||
SIZE GetDPI() const { return this->m_dpi;}
|
||||
|
||||
// Should this control take enter key in dialogs or not?
|
||||
// Default to true for compatibility with existing code - but when used in dialogs, you'll want it set to false to hit [OK] with enter.
|
||||
// Note that CreateInDialog() sets this to false. Change it later if you want enter key presses to reach this control in a dialog.
|
||||
bool m_dlgWantEnter = true;
|
||||
|
||||
bool WantReturn() const { return m_dlgWantEnter; }
|
||||
void SetWantReturn(bool v) { m_dlgWantEnter = v; }
|
||||
|
||||
enum {
|
||||
rowStyleGrid = 0,
|
||||
rowStyleFlat,
|
||||
rowStylePlaylist,
|
||||
rowStylePlaylistDelimited,
|
||||
};
|
||||
void SetPlaylistStyle() {SetRowStyle(rowStylePlaylist);}
|
||||
void SetRowStyle(unsigned v) { this->m_rowStyle = v; if (m_hWnd) Invalidate(); }
|
||||
void SetFlatStyle() {SetRowStyle(rowStyleFlat);}
|
||||
unsigned m_rowStyle = rowStylePlaylistDelimited;
|
||||
bool DelimitColumns() const { return m_rowStyle == rowStyleGrid || m_rowStyle == rowStylePlaylistDelimited; }
|
||||
|
||||
static COLORREF BlendGridColor( COLORREF bk, COLORREF tx );
|
||||
static COLORREF BlendGridColor( COLORREF bk );
|
||||
COLORREF GridColor();
|
||||
|
||||
private:
|
||||
void RenderRect(const CRect & p_rect,CDCHandle p_dc);
|
||||
int HandleWheel(int & p_accum,int p_delta, bool bHoriz);
|
||||
|
||||
void OnKillFocus(CWindow);
|
||||
void OnWindowPosChanged(LPWINDOWPOS);
|
||||
void PaintContent(CRect rcPaint, HDC dc);
|
||||
void OnPrintClient(HDC dc, UINT uFlags);
|
||||
LRESULT OnPaint(UINT,WPARAM,LPARAM,BOOL&);
|
||||
LRESULT OnVScroll(UINT,WPARAM,LPARAM,BOOL&);
|
||||
LRESULT OnHScroll(UINT,WPARAM,LPARAM,BOOL&);
|
||||
LRESULT OnSize(UINT,WPARAM,LPARAM,BOOL&);
|
||||
LRESULT OnSizeAsync(UINT,WPARAM,LPARAM,BOOL&) {ListHandleResize();return 0;}
|
||||
LRESULT OnVWheel(UINT,WPARAM,LPARAM,BOOL&);
|
||||
LRESULT OnHWheel(UINT,WPARAM,LPARAM,BOOL&);
|
||||
LRESULT OnGesture(UINT,WPARAM,LPARAM,BOOL&);
|
||||
LRESULT SetFocusPassThru(UINT,WPARAM,LPARAM,BOOL&);
|
||||
LRESULT OnCreatePassThru(UINT,WPARAM,LPARAM,BOOL&);
|
||||
LRESULT OnEraseBkgnd(UINT,WPARAM,LPARAM,BOOL&);
|
||||
LRESULT MousePassThru(UINT, WPARAM, LPARAM);
|
||||
LRESULT OnGetDlgCode(UINT, WPARAM, LPARAM);
|
||||
|
||||
void OnThemeChanged();
|
||||
int GetScrollThumbPos(int which);
|
||||
void RefreshSliders();
|
||||
void RefreshSlider(bool p_vertical);
|
||||
|
||||
void OnSizeAsync_Trigger();
|
||||
static LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam);
|
||||
bool MouseWheelFromHook(UINT msg, LPARAM data);
|
||||
|
||||
bool m_suppressMouseWheel = false;
|
||||
int m_wheelAccumX = 0, m_wheelAccumY = 0;
|
||||
CPoint m_viewOrigin = CPoint(0,0);
|
||||
bool m_sizeAsyncPending = false;
|
||||
CPoint m_gesturePoint;
|
||||
|
||||
protected:
|
||||
pfc::map_t<pfc::string8, CTheme, pfc::comparator_strcmp> m_themeCache;
|
||||
CTheme & themeFor( const char * what );
|
||||
CTheme & theme() { return themeFor("LISTVIEW");}
|
||||
|
||||
const SIZE m_dpi = QueryScreenDPIEx();
|
||||
CGestureAPI m_gestureAPI;
|
||||
bool m_ensureVisibleUser = false;
|
||||
CaptureProc_t m_captureProc;
|
||||
void defer( std::function<void () > f );
|
||||
LRESULT OnExecDeferred(UINT, WPARAM, LPARAM);
|
||||
|
||||
|
||||
// Overlays our stuff on top of generic DoDragDrop call.
|
||||
// Currently catches mouse wheel messages in mid-drag&drop and handles them in our view.
|
||||
HRESULT DoDragDrop(LPDATAOBJECT pDataObj, LPDROPSOURCE pDropSource, DWORD dwOKEffects, LPDWORD pdwEffect);
|
||||
|
||||
private:
|
||||
bool m_defferredMsgPending = false;
|
||||
std::list<std::function<void ()> > m_deferred;
|
||||
};
|
||||
|
||||
class CListControlFontOps : public CListControlImpl {
|
||||
private:
|
||||
typedef CListControlImpl TParent;
|
||||
public:
|
||||
CListControlFontOps();
|
||||
BEGIN_MSG_MAP_EX(CListControlFontOps)
|
||||
MESSAGE_HANDLER(WM_GETFONT,OnGetFont);
|
||||
MESSAGE_HANDLER(WM_SETFONT,OnSetFont);
|
||||
CHAIN_MSG_MAP(TParent);
|
||||
END_MSG_MAP()
|
||||
CFontHandle GetFont() const { return m_font; }
|
||||
void SetFont(HFONT font, bool bUpdateView = true);
|
||||
protected:
|
||||
CFontHandle GetGroupHeaderFont() const {return (HFONT)m_groupHeaderFont;}
|
||||
virtual double GroupHeaderFontScale() const { return 1.25; }
|
||||
virtual int GroupHeaderFontWeight(int origVal) const {
|
||||
//return pfc::min_t<int>(FW_BLACK, origVal + 200);
|
||||
return origVal;
|
||||
}
|
||||
|
||||
//! Overridden implementations should always forward the call to the base class.
|
||||
virtual void OnSetFont(bool bUpdatingView) {}
|
||||
|
||||
int GetGroupHeaderHeight() const {return m_groupHeaderHeight;}
|
||||
int GetItemHeight() const {return m_itemHeight;}
|
||||
|
||||
private:
|
||||
LRESULT OnSetFont(UINT,WPARAM,LPARAM,BOOL&);
|
||||
LRESULT OnGetFont(UINT,WPARAM,LPARAM,BOOL&);
|
||||
void UpdateGroupHeaderFont();
|
||||
void CalculateHeights();
|
||||
int m_itemHeight, m_groupHeaderHeight;
|
||||
CFontHandle m_font;
|
||||
CFont m_groupHeaderFont;
|
||||
};
|
||||
|
||||
#include "CListControlHeaderImpl.h"
|
||||
#include "CListControlTruncationTooltipImpl.h"
|
||||
|
||||
|
||||
|
||||
typedef CMiddleDragImpl<CListControlTruncationTooltipImpl> CListControl;
|
||||
|
||||
22
libPPUI/CListControlComplete.h
Normal file
22
libPPUI/CListControlComplete.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
// ================================================================================
|
||||
// CListControlComplete
|
||||
// ================================================================================
|
||||
// Simplified declaration of the base class that most CListControl users will need.
|
||||
// The other base classes are used directly mainly by old code predating libPPUI.
|
||||
// ================================================================================
|
||||
|
||||
#include "CListControlWithSelection.h"
|
||||
#include "CListControl_EditImpl.h"
|
||||
#include "CListAccessible.h"
|
||||
|
||||
// ================================================================================
|
||||
// CListControlWithSelectionImpl = list control with selection/focus
|
||||
// CListControl_EditImpl = inplace editbox implementation
|
||||
// CListControlAccImpl = accessibility API implementation (screen reader interop)
|
||||
// ================================================================================
|
||||
typedef CListControlAccImpl<CListControl_EditImpl<CListControlWithSelectionImpl> > CListControlComplete;
|
||||
|
||||
// CListControlReadOnly : no inplace edit functionality (CListControl_EditImpl)
|
||||
typedef CListControlAccImpl<CListControlWithSelectionImpl> CListControlReadOnly;
|
||||
1078
libPPUI/CListControlHeaderImpl.cpp
Normal file
1078
libPPUI/CListControlHeaderImpl.cpp
Normal file
File diff suppressed because it is too large
Load Diff
224
libPPUI/CListControlHeaderImpl.h
Normal file
224
libPPUI/CListControlHeaderImpl.h
Normal file
@@ -0,0 +1,224 @@
|
||||
#pragma once
|
||||
|
||||
class CListCell;
|
||||
|
||||
class CListControlHeaderImpl : public CListControlFontOps {
|
||||
private:
|
||||
typedef CListControlFontOps TParent;
|
||||
public:
|
||||
CListControlHeaderImpl() {}
|
||||
|
||||
BEGIN_MSG_MAP_EX(CListControlHeaderImpl)
|
||||
MSG_WM_CTLCOLORSTATIC(OnCtlColorStatic);
|
||||
MESSAGE_HANDLER(WM_KEYDOWN,OnKeyDown);
|
||||
MESSAGE_HANDLER(WM_SYSKEYDOWN,OnKeyDown);
|
||||
MESSAGE_HANDLER_EX(WM_SIZE,OnSizePassThru);
|
||||
NOTIFY_CODE_HANDLER(HDN_ITEMCHANGED,OnHeaderItemChanged);
|
||||
NOTIFY_CODE_HANDLER(HDN_ENDDRAG,OnHeaderEndDrag);
|
||||
NOTIFY_CODE_HANDLER(HDN_ITEMCLICK,OnHeaderItemClick);
|
||||
NOTIFY_CODE_HANDLER(HDN_DIVIDERDBLCLICK,OnDividerDoubleClick);
|
||||
MSG_WM_SETCURSOR(OnSetCursor);
|
||||
MSG_WM_MOUSEMOVE(OnMouseMove)
|
||||
MSG_WM_DESTROY(OnDestroy)
|
||||
MSG_WM_ENABLE(OnEnable)
|
||||
CHAIN_MSG_MAP(TParent)
|
||||
END_MSG_MAP()
|
||||
|
||||
typedef uint32_t cellState_t;
|
||||
typedef CListCell * cellType_t;
|
||||
|
||||
int GetHeaderItemWidth( int which );
|
||||
void InitializeHeaderCtrl(DWORD flags = HDS_FULLDRAG);
|
||||
void InitializeHeaderCtrlSortable() {InitializeHeaderCtrl(HDS_FULLDRAG | HDS_BUTTONS);}
|
||||
CHeaderCtrl GetHeaderCtrl() const {return m_header;}
|
||||
void SetSortIndicator( size_t whichColumn, bool isUp );
|
||||
void ClearSortIndicator();
|
||||
|
||||
bool IsHeaderEnabled() const {return m_header.m_hWnd != NULL;}
|
||||
void ResetColumns(bool update = true);
|
||||
|
||||
enum {
|
||||
columnWidthMax = (uint32_t)INT32_MAX,
|
||||
columnWidthAuto = UINT32_MAX,
|
||||
columnWidthAutoUseContent = UINT32_MAX-1,
|
||||
};
|
||||
void AddColumn(const char * label, uint32_t widthPixels, DWORD fmtFlags = HDF_LEFT,bool update = true);
|
||||
//! Extended AddColumn, specifies width in pixels @ 96DPI instead of screen-specific pixels
|
||||
void AddColumnEx( const char * label, uint32_t widthPixelsAt96DPI, DWORD fmtFlags = HDF_LEFT, bool update = true );
|
||||
//! Extended AddColumn, specifies width in Dialog Length Units (DLU), assumes parent of this list to be a dialog window.
|
||||
void AddColumnDLU( const char * label, uint32_t widthDLU, DWORD fmtFlags = HDF_LEFT, bool update = true );
|
||||
//! Extended AddColumn, specifies width as floating-point value of pixels at 96DPI. \n
|
||||
//! For DPI-safe storage of user's column widths.
|
||||
void AddColumnF( const char * label, float widthF, DWORD fmtFlags = HDF_LEFT, bool update = true );
|
||||
void AddColumnAutoWidth( const char * label, DWORD fmtFlags = HDF_LEFT, bool bUpdate = true) { AddColumn(label, columnWidthAuto, fmtFlags, bUpdate); }
|
||||
bool DeleteColumn(size_t index, bool updateView = true);
|
||||
void DeleteColumns( pfc::bit_array const & mask, bool updateView = true);
|
||||
void ResizeColumn(t_size index, t_uint32 widthPixels, bool updateView = true);
|
||||
void SetColumn( size_t which, const char * title, DWORD fmtFlags = HDF_LEFT, bool updateView = true);
|
||||
void GetColumnText(size_t which, pfc::string_base & out) const;
|
||||
|
||||
uint32_t GetOptimalColumnWidth( size_t index ) const;
|
||||
uint32_t GetOptimalColumnWidthFixed( const char * fixedText) const;
|
||||
uint32_t GetColumnsBlankWidth( size_t colExclude = SIZE_MAX ) const;
|
||||
void SizeColumnToContent( size_t which, uint32_t minWidth );
|
||||
void SizeColumnToContentFillBlank( size_t which );
|
||||
|
||||
//! If creating a custom headerless multi column scheme, override these to manipulate your columns
|
||||
virtual size_t GetColumnCount() const;
|
||||
virtual uint32_t GetSubItemWidth(size_t subItem) const;
|
||||
//! Returns column width as a floating-point value of pixels at 96DPI. \n
|
||||
//! For DPI-safe storage of user's column widths.
|
||||
float GetColumnWidthF( size_t subItem ) const;
|
||||
//! Indicate how many columns a specific row/column cell spans\n
|
||||
//! This makes sense only if the columns can't be user-reordered
|
||||
virtual size_t GetSubItemSpan(size_t row, size_t column) const;
|
||||
|
||||
t_size GetSubItemOrder(t_size subItem) const;
|
||||
int GetItemWidth() const override;
|
||||
protected:
|
||||
CRect GetClientRectHook() const override;
|
||||
void RenderBackground(CDCHandle dc, CRect const& rc) override;
|
||||
|
||||
struct GetOptimalWidth_Cache {
|
||||
//! For temporary use.
|
||||
pfc::string8_fastalloc m_stringTemp, m_stringTempUnfuckAmpersands;
|
||||
//! For temporary use.
|
||||
pfc::stringcvt::string_wide_from_utf8_t<pfc::alloc_fast_aggressive> m_convertTemp;
|
||||
//! Our DC for measuring text. Correct font pre-selected.
|
||||
CDCHandle m_dc;
|
||||
|
||||
t_uint32 GetStringTempWidth();
|
||||
};
|
||||
|
||||
void UpdateHeaderLayout();
|
||||
void OnViewOriginChange(CPoint p_delta);
|
||||
void SetHeaderFont(HFONT font);
|
||||
void RenderItemText(t_size item,const CRect & itemRect,const CRect & updateRect,CDCHandle dc, bool allowColors);
|
||||
void RenderGroupHeaderText(int id,const CRect & headerRect,const CRect & updateRect,CDCHandle dc);
|
||||
|
||||
//! Converts an item/subitem rect to a rect in which the text should be rendered, removing spacing to the left/right of the text.
|
||||
virtual CRect GetItemTextRectHook(size_t item, size_t subItem, CRect const & itemRect) const;
|
||||
CRect GetItemTextRect(CRect const & itemRect) const;
|
||||
//! Override for custom spacing to the left/right of the text in each column.
|
||||
virtual t_uint32 GetColumnSpacing() const {return MulDiv(4,m_dpi.cx,96);}
|
||||
//! Override for column-header-click sorting.
|
||||
virtual void OnColumnHeaderClick(t_size index) {}
|
||||
//! Override to supply item labels.
|
||||
virtual bool GetSubItemText(t_size item, t_size subItem, pfc::string_base & out) const {return false;}
|
||||
//! Override if you support groups.
|
||||
virtual bool GetGroupHeaderText(int id, pfc::string_base & out) const {return false;}
|
||||
//! Override optionally.
|
||||
virtual void RenderSubItemText(t_size item, t_size subItem,const CRect & subItemRect,const CRect & updateRect,CDCHandle dc, bool allowColors);
|
||||
|
||||
virtual void OnColumnsChanged();
|
||||
|
||||
virtual t_uint32 GetOptimalSubItemWidth(t_size item, t_size subItem, GetOptimalWidth_Cache & cache) const;
|
||||
|
||||
virtual t_uint32 GetOptimalGroupHeaderWidth(int which) const;
|
||||
|
||||
|
||||
bool GetItemAtPointAbsEx( CPoint pt, size_t & outItem, size_t & outSubItem ) const;
|
||||
cellType_t GetCellTypeAtPointAbs( CPoint pt ) const;
|
||||
virtual cellType_t GetCellType( size_t item, size_t subItem ) const;
|
||||
virtual bool AllowTypeFindInCell( size_t item, size_t subItem ) const;
|
||||
virtual bool GetCellTypeSupported() const { return false; } // optimization hint, some expensive checks can be suppressed if cell types are not used for this view
|
||||
virtual bool GetCellCheckState( size_t item, size_t subItem ) const { return false; }
|
||||
virtual void SetCellCheckState(size_t item, size_t subItem, bool value);
|
||||
virtual bool ToggleSelectedItemsHook(const pfc::bit_array & mask);
|
||||
|
||||
void RenderSubItemTextInternal(size_t subItem, const CRect & subItemRect, CDCHandle dc, const char * text, bool allowColors);
|
||||
|
||||
t_uint32 GetOptimalColumnWidth(t_size which, GetOptimalWidth_Cache & cache) const;
|
||||
t_uint32 GetOptimalSubItemWidthSimple(t_size item, t_size subItem) const;
|
||||
|
||||
void AutoColumnWidths(const pfc::bit_array & mask,bool expandLast = false);
|
||||
void AutoColumnWidths() {AutoColumnWidths(pfc::bit_array_true());}
|
||||
void AutoColumnWidth(t_size which) {AutoColumnWidths(pfc::bit_array_one(which));}
|
||||
|
||||
virtual bool OnColumnHeaderDrag(t_size index, t_size newOrder);
|
||||
|
||||
void OnItemClicked(t_size item, CPoint pt) override;
|
||||
virtual void OnSubItemClicked(t_size item, t_size subItem,CPoint pt);
|
||||
bool OnClickedSpecialHitTest(CPoint pt) override;
|
||||
bool OnClickedSpecial(DWORD status, CPoint pt) override;
|
||||
static bool CellTypeUsesSpecialHitTests( cellType_t ct );
|
||||
|
||||
|
||||
CRect GetSubItemRectAbs(t_size item,t_size subItem) const;
|
||||
CRect GetSubItemRect(t_size item,t_size subItem) const;
|
||||
|
||||
t_size SubItemFromPointAbs(CPoint pt) const;
|
||||
|
||||
static bool CellTypeReactsToMouseOver( cellType_t ct );
|
||||
virtual CRect CellHotRect( size_t item, size_t subItem, cellType_t ct, CRect rcCell );
|
||||
CRect CellHotRect( size_t item, size_t subItem, cellType_t ct );
|
||||
virtual double CellTextScale(size_t item, size_t subItem) { return 1; }
|
||||
virtual bool IsSubItemGrayed(size_t item, size_t subItem) { return !this->IsWindowEnabled(); }
|
||||
|
||||
// HDF_* constants for this column, override when not using list header control. Used to control text alignment.
|
||||
virtual DWORD GetColumnFormat(t_size which) const;
|
||||
void SetColumnFormat(t_size which,DWORD format);
|
||||
void SetColumnSort(t_size which, bool isUp);
|
||||
|
||||
std::vector<int> GetColumnOrderArray() const;
|
||||
|
||||
bool AllowScrollbar(bool vertical) const override;
|
||||
|
||||
void RenderItemBackground(CDCHandle p_dc,const CRect & p_itemRect,size_t item, uint32_t bkColor) override;
|
||||
|
||||
void ColumnWidthFix(); // Call RecalcItemWidth() / ProcessAutoWidth()
|
||||
|
||||
void ReloadData() override;
|
||||
private:
|
||||
void OnEnable(BOOL) { Invalidate(); }
|
||||
HBRUSH OnCtlColorStatic(CDCHandle dc, CStatic wndStatic);
|
||||
void ProcessColumnsChange() { OnColumnsChanged();}
|
||||
LRESULT OnSizePassThru(UINT,WPARAM,LPARAM);
|
||||
LRESULT OnHeaderItemClick(int,LPNMHDR,BOOL&);
|
||||
LRESULT OnDividerDoubleClick(int,LPNMHDR,BOOL&);
|
||||
LRESULT OnHeaderItemChanged(int,LPNMHDR,BOOL&);
|
||||
LRESULT OnHeaderEndDrag(int,LPNMHDR,BOOL&);
|
||||
LRESULT OnKeyDown(UINT,WPARAM,LPARAM,BOOL&);
|
||||
void OnDestroy();
|
||||
BOOL OnSetCursor(CWindow wnd, UINT nHitTest, UINT message);
|
||||
void OnMouseMove(UINT nFlags, CPoint point);
|
||||
|
||||
void RecalcItemWidth(); // FIXED width math
|
||||
void ProcessAutoWidth(); // DYNAMIC width math
|
||||
|
||||
bool m_ownColumnsChange = false;
|
||||
|
||||
int m_itemWidth = 0;
|
||||
int m_clientWidth = 0;
|
||||
CHeaderCtrl m_header;
|
||||
CStatic m_headerLine;
|
||||
|
||||
bool HaveAutoWidthColumns() const;
|
||||
bool HaveAutoWidthContentColumns() const;
|
||||
|
||||
struct colRuntime_t {
|
||||
bool m_autoWidth = false;
|
||||
bool m_autoWidthContent = false;
|
||||
int m_widthPixels = 0;
|
||||
uint32_t m_userWidth = 0;
|
||||
std::string m_text;
|
||||
|
||||
bool autoWidth() const { return m_userWidth > columnWidthMax; }
|
||||
bool autoWidthContent() const { return m_userWidth == columnWidthAutoUseContent; }
|
||||
bool autoWidthPlain() const { return m_userWidth == columnWidthAuto; }
|
||||
};
|
||||
|
||||
std::vector<colRuntime_t> m_colRuntime;
|
||||
|
||||
|
||||
//for group headers
|
||||
GdiplusScope m_gdiPlusScope;
|
||||
|
||||
void SetPressedItem(size_t row, size_t column);
|
||||
void ClearPressedItem() {SetPressedItem(SIZE_MAX, SIZE_MAX);}
|
||||
void SetHotItem( size_t row, size_t column );
|
||||
void ClearHotItem() { SetHotItem(SIZE_MAX, SIZE_MAX); }
|
||||
|
||||
size_t m_pressedItem = SIZE_MAX, m_pressedSubItem = SIZE_MAX;
|
||||
size_t m_hotItem = SIZE_MAX, m_hotSubItem = SIZE_MAX;
|
||||
};
|
||||
206
libPPUI/CListControlOwnerData.h
Normal file
206
libPPUI/CListControlOwnerData.h
Normal file
@@ -0,0 +1,206 @@
|
||||
#pragma once
|
||||
|
||||
// ================================================================================
|
||||
// CListControlOwnerData
|
||||
// ================================================================================
|
||||
// Forwards all list data retrieval and manipulation calls to a host object.
|
||||
// Not intended for subclassing, just implement IListControlOwnerDataSource methods
|
||||
// in your class.
|
||||
// ================================================================================
|
||||
|
||||
#include "CListControlComplete.h"
|
||||
|
||||
class CListControlOwnerData;
|
||||
|
||||
class IListControlOwnerDataSource {
|
||||
public:
|
||||
typedef const CListControlOwnerData * ctx_t;
|
||||
|
||||
|
||||
virtual size_t listGetItemCount( ctx_t ) = 0;
|
||||
virtual pfc::string8 listGetSubItemText( ctx_t, size_t item, size_t subItem ) = 0;
|
||||
virtual bool listCanReorderItems( ctx_t ) { return false; }
|
||||
virtual bool listReorderItems( ctx_t, const size_t*, size_t) {return false;}
|
||||
virtual bool listRemoveItems( ctx_t, pfc::bit_array const & ) {return false;}
|
||||
virtual void listItemAction(ctx_t, size_t) {}
|
||||
virtual void listSubItemClicked( ctx_t, size_t, size_t ) {}
|
||||
virtual bool listCanSelectItem( ctx_t, size_t ) { return true; }
|
||||
virtual pfc::string8 listGetEditField(ctx_t ctx, size_t item, size_t subItem, size_t & lineCount) {
|
||||
lineCount = 1; return listGetSubItemText( ctx, item, subItem);
|
||||
}
|
||||
virtual void listSetEditField(ctx_t ctx, size_t item, size_t subItem, const char * val) {}
|
||||
virtual uint32_t listGetEditFlags(ctx_t ctx, size_t item, size_t subItem) {return 0;}
|
||||
typedef InPlaceEdit::CTableEditHelperV2::autoComplete_t autoComplete_t;
|
||||
typedef InPlaceEdit::CTableEditHelperV2::combo_t combo_t;
|
||||
virtual autoComplete_t listGetAutoComplete(ctx_t, size_t item, size_t subItem) {return autoComplete_t();}
|
||||
virtual combo_t listGetCombo(ctx_t, size_t item, size_t subItem) { return combo_t(); }
|
||||
|
||||
virtual bool listIsColumnEditable( ctx_t, size_t ) { return false; }
|
||||
virtual bool listKeyDown(ctx_t, UINT nChar, UINT nRepCnt, UINT nFlags) { return false; }
|
||||
virtual bool listKeyUp(ctx_t, UINT nChar, UINT nRepCnt, UINT nFlags) { return false; }
|
||||
|
||||
// Allow type-find in this view?
|
||||
// Called prior to a typefind pass attempt, you can either deny entirely, or prepare any necessary data and allow it.
|
||||
virtual bool listAllowTypeFind( ctx_t ) { return true; }
|
||||
// Allow type-find in a specific item/column?
|
||||
virtual bool listAllowTypeFindHere( ctx_t, size_t item, size_t subItem ) { return true ;}
|
||||
|
||||
virtual void listColumnHeaderClick(ctx_t, size_t subItem) {}
|
||||
|
||||
virtual void listBeforeDrawItemText( ctx_t, size_t item, CDCHandle dc ) {}
|
||||
virtual bool listIsSubItemGrayed(ctx_t, size_t item, size_t subItem) { return false; }
|
||||
|
||||
virtual void listSelChanged(ctx_t) {}
|
||||
virtual void listFocusChanged(ctx_t) {}
|
||||
|
||||
virtual void listColumnsChanged(ctx_t) {}
|
||||
|
||||
virtual bool listEditCanAdvanceHere(ctx_t, size_t item, size_t subItem, uint32_t whatHappened) {(void) item; (void) subItem, (void) whatHappened; return true;}
|
||||
};
|
||||
|
||||
class IListControlOwnerDataCells {
|
||||
public:
|
||||
typedef const CListControlOwnerData * cellsCtx_t;
|
||||
virtual CListControl::cellType_t listCellType( cellsCtx_t, size_t item, size_t subItem ) = 0;
|
||||
virtual size_t listCellSpan( cellsCtx_t, size_t item, size_t subItem ) {return 1;}
|
||||
virtual bool listCellCheckState( cellsCtx_t, size_t item, size_t subItem ) {return false; }
|
||||
virtual void listCellSetCheckState( cellsCtx_t, size_t item, size_t subItem, bool state ) {}
|
||||
};
|
||||
|
||||
class CListControlOwnerData : public CListControlComplete {
|
||||
IListControlOwnerDataSource * const m_host;
|
||||
public:
|
||||
|
||||
CListControlOwnerData( IListControlOwnerDataSource * h) : m_host(h) {}
|
||||
|
||||
BEGIN_MSG_MAP_EX(CListControlOwnerData)
|
||||
MSG_WM_KEYDOWN(OnKeyDown)
|
||||
MSG_WM_KEYUP(OnKeyUp)
|
||||
MSG_WM_SYSKEYDOWN(OnKeyDown)
|
||||
MSG_WM_SYSKEYUP(OnKeyUp)
|
||||
CHAIN_MSG_MAP(CListControlComplete)
|
||||
END_MSG_MAP()
|
||||
|
||||
using CListControl_EditImpl::TableEdit_Abort;
|
||||
using CListControl_EditImpl::TableEdit_Start;
|
||||
using CListControl_EditImpl::TableEdit_IsActive;
|
||||
|
||||
bool CanSelectItem( size_t idx ) const override {
|
||||
return m_host->listCanSelectItem( this, idx );
|
||||
}
|
||||
size_t GetItemCount() const override {
|
||||
return m_host->listGetItemCount( this );
|
||||
}
|
||||
bool GetSubItemText(size_t item, size_t subItem, pfc::string_base & out) const override {
|
||||
out = m_host->listGetSubItemText( this, item, subItem );
|
||||
return true;
|
||||
}
|
||||
void OnSubItemClicked( size_t item, size_t subItem, CPoint pt ) override {
|
||||
__super::OnSubItemClicked(item, subItem, pt); // needed to toggle checkboxes etc
|
||||
m_host->listSubItemClicked( this, item, subItem );
|
||||
}
|
||||
|
||||
uint32_t QueryDragDropTypes() const override {
|
||||
return (m_host->listCanReorderItems(this)) ? dragDrop_reorder : 0;
|
||||
}
|
||||
|
||||
void RequestReorder( const size_t * order, size_t count) override {
|
||||
if ( ! m_host->listReorderItems( this, order, count )) return;
|
||||
this->OnItemsReordered( order, count );
|
||||
}
|
||||
void RequestRemoveSelection() override {
|
||||
auto mask = this->GetSelectionMask();
|
||||
size_t oldCount = GetItemCount();
|
||||
if ( ! m_host->listRemoveItems( this, mask ) ) return;
|
||||
this->OnItemsRemoved( mask, oldCount );
|
||||
}
|
||||
void ExecuteDefaultAction( size_t idx ) override {
|
||||
m_host->listItemAction( this, idx );
|
||||
}
|
||||
size_t EvalTypeFind() override {
|
||||
if (! m_host->listAllowTypeFind(this) ) return SIZE_MAX;
|
||||
return __super::EvalTypeFind();
|
||||
}
|
||||
bool AllowTypeFindInCell( size_t item, size_t subItem ) const {
|
||||
return __super::AllowTypeFindInCell( item, subItem ) && m_host->listAllowTypeFindHere( this, item, subItem );
|
||||
}
|
||||
|
||||
protected:
|
||||
void OnFocusChanged(size_t newFocus) override {
|
||||
__super::OnFocusChanged(newFocus);
|
||||
m_host->listFocusChanged(this);
|
||||
}
|
||||
void OnSelectionChanged(pfc::bit_array const & affected, pfc::bit_array const & status) {
|
||||
__super::OnSelectionChanged(affected, status);
|
||||
m_host->listSelChanged(this);
|
||||
}
|
||||
|
||||
void RenderItemText(t_size p_item,const CRect & p_itemRect,const CRect & p_updateRect,CDCHandle p_dc, bool allowColors) {
|
||||
m_host->listBeforeDrawItemText(this, p_item, p_dc );
|
||||
__super::RenderItemText(p_item, p_itemRect, p_updateRect, p_dc, allowColors);
|
||||
}
|
||||
void TableEdit_SetField(t_size item, t_size subItem, const char * value) override {
|
||||
m_host->listSetEditField(this, item, subItem, value);
|
||||
}
|
||||
void TableEdit_GetField(t_size item, t_size subItem, pfc::string_base & out, t_size & lineCount) override {
|
||||
lineCount = 1;
|
||||
out = m_host->listGetEditField(this, item, subItem, lineCount);
|
||||
}
|
||||
|
||||
t_uint32 TableEdit_GetEditFlags(t_size item, t_size subItem) const override {
|
||||
return m_host->listGetEditFlags( this, item, subItem );
|
||||
}
|
||||
|
||||
combo_t TableEdit_GetCombo(size_t item, size_t subItem) override {
|
||||
return m_host->listGetCombo(this, item, subItem);
|
||||
}
|
||||
autoComplete_t TableEdit_GetAutoCompleteEx(t_size item, t_size subItem) override {
|
||||
return m_host->listGetAutoComplete( this, item, subItem );
|
||||
}
|
||||
bool TableEdit_IsColumnEditable(t_size subItem) const override {
|
||||
return m_host->listIsColumnEditable( this, subItem );
|
||||
}
|
||||
void OnColumnHeaderClick(t_size index) override {
|
||||
m_host->listColumnHeaderClick(this, index);
|
||||
}
|
||||
void OnColumnsChanged() override {
|
||||
__super::OnColumnsChanged();
|
||||
m_host->listColumnsChanged(this);
|
||||
}
|
||||
bool IsSubItemGrayed(size_t item, size_t subItem) override {
|
||||
return __super::IsSubItemGrayed(item, subItem) || m_host->listIsSubItemGrayed(this, item, subItem);
|
||||
}
|
||||
private:
|
||||
bool TableEdit_CanAdvanceHere( size_t item, size_t subItem, uint32_t whatHappened ) const override {
|
||||
return m_host->listEditCanAdvanceHere(this, item, subItem, whatHappened);
|
||||
}
|
||||
void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) {
|
||||
bool handled = m_host->listKeyDown(this, nChar, nRepCnt, nFlags);
|
||||
SetMsgHandled( !! handled );
|
||||
}
|
||||
void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) {
|
||||
bool handled = m_host->listKeyUp(this, nChar, nRepCnt, nFlags);
|
||||
SetMsgHandled( !! handled );
|
||||
}
|
||||
};
|
||||
|
||||
class CListControlOwnerDataCells : public CListControlOwnerData {
|
||||
IListControlOwnerDataCells * const m_cells;
|
||||
public:
|
||||
CListControlOwnerDataCells( IListControlOwnerDataSource * source, IListControlOwnerDataCells * cells ) : CListControlOwnerData(source), m_cells(cells) {}
|
||||
|
||||
bool GetCellTypeSupported() const override {return true; }
|
||||
bool GetCellCheckState( size_t item, size_t subItem ) const override {
|
||||
return m_cells->listCellCheckState( this, item, subItem );
|
||||
}
|
||||
void SetCellCheckState( size_t item, size_t subItem, bool value ) override {
|
||||
m_cells->listCellSetCheckState( this, item, subItem, value );
|
||||
__super::SetCellCheckState(item, subItem, value);
|
||||
}
|
||||
cellType_t GetCellType( size_t item, size_t subItem ) const override {
|
||||
return m_cells->listCellType( this, item, subItem );
|
||||
}
|
||||
size_t GetSubItemSpan(size_t row, size_t column) const override {
|
||||
return m_cells->listCellSpan( this, row, column );
|
||||
}
|
||||
};
|
||||
137
libPPUI/CListControlSimple.h
Normal file
137
libPPUI/CListControlSimple.h
Normal file
@@ -0,0 +1,137 @@
|
||||
#pragma once
|
||||
|
||||
// ================================================================================
|
||||
// CListControlSimple
|
||||
// Simplified CListControl interface; a ready-to-use class that can be instantiated
|
||||
// without subclassing.
|
||||
// Use when you don't need advanced features such as buttons or editing.
|
||||
// Maintains its own data.
|
||||
// ================================================================================
|
||||
|
||||
#include "CListControlComplete.h"
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
|
||||
class CListControlSimple : public CListControlReadOnly {
|
||||
public:
|
||||
// Events
|
||||
std::function<void()> onReordered; // if not set, list reordering is disabled
|
||||
std::function<void()> onRemoved; // if not set, list item removal is disabled
|
||||
std::function<void(size_t)> onItemAction;
|
||||
std::function<void()> onSelChange;
|
||||
|
||||
size_t GetItemCount() const override {
|
||||
return m_lines.size();
|
||||
}
|
||||
void SetItemCount( size_t count ) {
|
||||
m_lines.resize( count );
|
||||
ReloadData();
|
||||
}
|
||||
void SetItemText(size_t item, size_t subItem, const char * text, bool bRedraw = true) {
|
||||
if ( item < m_lines.size() ) {
|
||||
m_lines[item].text[subItem] = text;
|
||||
if ( bRedraw ) ReloadItem( item );
|
||||
}
|
||||
}
|
||||
bool GetSubItemText(size_t item, size_t subItem, pfc::string_base & out) const override {
|
||||
if ( item < m_lines.size() ) {
|
||||
auto & l = m_lines[item].text;
|
||||
auto iter = l.find( subItem );
|
||||
if ( iter != l.end() ) {
|
||||
out = iter->second.c_str();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t QueryDragDropTypes() const override {
|
||||
return (onReordered != nullptr) ? dragDrop_reorder : 0;
|
||||
}
|
||||
|
||||
void RequestReorder( const size_t * order, size_t count) override {
|
||||
if ( onReordered == nullptr ) return;
|
||||
pfc::reorder_t( m_lines, order, count );
|
||||
this->OnItemsReordered( order, count );
|
||||
onReordered();
|
||||
}
|
||||
void RequestRemoveSelection() override {
|
||||
if (onRemoved == nullptr) return;
|
||||
auto mask = this->GetSelectionMask();
|
||||
size_t oldCount = m_lines.size();
|
||||
pfc::remove_mask_t( m_lines, mask );
|
||||
this->OnItemsRemoved( mask, oldCount );
|
||||
onRemoved();
|
||||
}
|
||||
void ExecuteDefaultAction( size_t idx ) override {
|
||||
if (onItemAction != nullptr) onItemAction(idx);
|
||||
}
|
||||
|
||||
void SetItemUserData( size_t item, size_t user ) {
|
||||
if ( item < m_lines.size() ) {
|
||||
m_lines[item].user = user;
|
||||
}
|
||||
}
|
||||
size_t GetItemUserData( size_t item ) const {
|
||||
size_t ret = 0;
|
||||
if ( item < m_lines.size() ) {
|
||||
ret = m_lines[item].user;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
void RemoveAllItems() {
|
||||
RemoveItems(pfc::bit_array_true());
|
||||
}
|
||||
void RemoveItems( pfc::bit_array const & mask ) {
|
||||
const auto oldCount = m_lines.size();
|
||||
pfc::remove_mask_t( m_lines, mask );
|
||||
this->OnItemsRemoved( mask, oldCount );
|
||||
}
|
||||
void RemoveItem( size_t which ) {
|
||||
RemoveItems( pfc::bit_array_one( which ) );
|
||||
}
|
||||
|
||||
size_t InsertItem( size_t insertAt, const char * textCol0 = nullptr ) {
|
||||
if ( insertAt > m_lines.size() ) {
|
||||
insertAt = m_lines.size();
|
||||
}
|
||||
{
|
||||
line_t data;
|
||||
if ( textCol0 != nullptr ) data.text[0] = textCol0;
|
||||
m_lines.insert( m_lines.begin() + insertAt, std::move(data) );
|
||||
}
|
||||
this->OnItemsInserted( insertAt, 1, false );
|
||||
return insertAt;
|
||||
}
|
||||
size_t AddItem( const char * textCol0 = nullptr ) {
|
||||
return InsertItem( SIZE_MAX, textCol0 );
|
||||
}
|
||||
size_t InsertItems( size_t insertAt, size_t count ) {
|
||||
if ( insertAt > m_lines.size() ) {
|
||||
insertAt = m_lines.size();
|
||||
}
|
||||
|
||||
{
|
||||
line_t val;
|
||||
m_lines.insert( m_lines.begin(), count, val );
|
||||
}
|
||||
|
||||
this->OnItemsInserted( insertAt, count, false );
|
||||
return insertAt;
|
||||
}
|
||||
protected:
|
||||
void OnSelectionChanged(pfc::bit_array const & affected, pfc::bit_array const & status) {
|
||||
__super::OnSelectionChanged(affected, status);
|
||||
if ( onSelChange ) onSelChange();
|
||||
}
|
||||
private:
|
||||
struct line_t {
|
||||
std::map<size_t, std::string> text;
|
||||
size_t user = 0;
|
||||
};
|
||||
std::vector<line_t> m_lines;
|
||||
};
|
||||
209
libPPUI/CListControlTruncationTooltipImpl.cpp
Normal file
209
libPPUI/CListControlTruncationTooltipImpl.cpp
Normal file
@@ -0,0 +1,209 @@
|
||||
#include "stdafx.h"
|
||||
#include "CListControl.h"
|
||||
#include "PaintUtils.h"
|
||||
|
||||
LRESULT CListControlTruncationTooltipImpl::OnTTShow(int,LPNMHDR,BOOL&) {
|
||||
SetTimer(KTooltipTimer,KTooltipTimerDelay);
|
||||
return 0;
|
||||
}
|
||||
LRESULT CListControlTruncationTooltipImpl::OnTTPop(int,LPNMHDR,BOOL&) {
|
||||
KillTimer(KTooltipTimer);
|
||||
return 0;
|
||||
}
|
||||
LRESULT CListControlTruncationTooltipImpl::OnTTGetDispInfo(int,LPNMHDR p_hdr,BOOL&) {
|
||||
LPNMTTDISPINFO info = (LPNMTTDISPINFO)p_hdr;
|
||||
|
||||
info->lpszText = const_cast<TCHAR*>(this->m_tooltipText.get_ptr());
|
||||
info->hinst = 0;
|
||||
info->uFlags = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LRESULT CListControlTruncationTooltipImpl::OnDestroyPassThru(UINT,WPARAM,LPARAM,BOOL& bHandled) {
|
||||
if (m_tooltip.m_hWnd != NULL) m_tooltip.DestroyWindow();
|
||||
KillTimer(KTooltipTimer);
|
||||
bHandled = FALSE; return 0;
|
||||
}
|
||||
|
||||
CListControlTruncationTooltipImpl::CListControlTruncationTooltipImpl()
|
||||
: m_toolinfo()
|
||||
, m_tooltipRect(0,0,0,0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CListControlTruncationTooltipImpl::TooltipRemove() {
|
||||
m_tooltipRect = CRect(0,0,0,0);
|
||||
if (m_tooltip.m_hWnd != NULL) {
|
||||
m_tooltip.TrackActivate(&m_toolinfo,FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
void CListControlTruncationTooltipImpl::TooltipRemoveCheck() {
|
||||
CPoint pt = GetCursorPos();
|
||||
if (ScreenToClient(&pt)) {
|
||||
TooltipRemoveCheck( MAKELPARAM( pt.x, pt.y ) );
|
||||
}
|
||||
}
|
||||
void CListControlTruncationTooltipImpl::TooltipRemoveCheck(LPARAM pos) {
|
||||
if (!m_tooltipRect.IsRectEmpty()) {
|
||||
CPoint pt(pos);
|
||||
if (!GetClientRectHook().PtInRect(pt)) {
|
||||
TooltipRemove();
|
||||
} else {
|
||||
ClientToScreen(&pt);
|
||||
if (!m_tooltipRect.PtInRect(pt)) {
|
||||
TooltipRemove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LRESULT CListControlTruncationTooltipImpl::OnTimer(UINT,WPARAM wp,LPARAM,BOOL& bHandled) {
|
||||
switch(wp) {
|
||||
case KTooltipTimer:
|
||||
TooltipRemoveCheck();
|
||||
return 0;
|
||||
default:
|
||||
bHandled = FALSE;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
LRESULT CListControlTruncationTooltipImpl::OnMouseMovePassThru(UINT,WPARAM,LPARAM lp,BOOL& bHandled) {
|
||||
TooltipRemoveCheck(lp);
|
||||
{
|
||||
TRACKMOUSEEVENT ev = {sizeof(ev)};
|
||||
ev.dwFlags = TME_HOVER;
|
||||
ev.hwndTrack = *this;
|
||||
ev.dwHoverTime = HOVER_DEFAULT;
|
||||
TrackMouseEvent(&ev);
|
||||
}
|
||||
bHandled = FALSE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
bool CListControlTruncationTooltipImpl::IsRectPartiallyObscuredAbs(CRect const & r) const {
|
||||
CRect cl = this->GetClientRectHook(); cl.OffsetRect( this->GetViewOffset() );
|
||||
return r.right > cl.right || r.top < cl.top || r.bottom > cl.bottom;
|
||||
}
|
||||
|
||||
bool CListControlTruncationTooltipImpl::IsRectFullyVisibleAbs(CRect const & r) {
|
||||
CRect cl = this->GetClientRectHook(); cl.OffsetRect( this->GetViewOffset() );
|
||||
return r.left >= cl.left && r.right <= cl.right && r.top >= cl.top && r.bottom <= cl.bottom;
|
||||
}
|
||||
|
||||
bool CListControlTruncationTooltipImpl::GetTooltipData(CPoint pt, pfc::string_base & outText, CRect & outRC, CFontHandle & outFont) const {
|
||||
t_size item; int group;
|
||||
if (ItemFromPointAbs(pt, item)) {
|
||||
const CRect itemRectAbs = this->GetItemRectAbs(item);
|
||||
/*if (this->IsHeaderEnabled()) */{
|
||||
t_uint32 cbase = 0;
|
||||
auto orderArray = this->GetColumnOrderArray();
|
||||
for (t_size _cwalk = 0; _cwalk < orderArray.size(); ++_cwalk) {
|
||||
const t_size cwalk = orderArray[_cwalk];
|
||||
//const TColumnRuntime & col = m_columns[cwalk];
|
||||
|
||||
const t_uint32 width = GetSubItemWidth(cwalk);
|
||||
if ((t_uint32)pt.x < cbase + width) {
|
||||
t_uint32 estWidth = GetOptimalSubItemWidthSimple(item, cwalk);
|
||||
CRect rc = itemRectAbs; rc.left = cbase; rc.right = cbase + estWidth;
|
||||
if (estWidth > width || (IsRectPartiallyObscuredAbs(rc) && rc.PtInRect(pt))) {
|
||||
pfc::string_formatter label, ccTemp;
|
||||
if (GetSubItemText(item, cwalk, label)) {
|
||||
PaintUtils::TextOutColors_StripCodes(ccTemp, label);
|
||||
outFont = GetFont(); outRC = rc; outText = ccTemp;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
cbase += width;
|
||||
}
|
||||
}
|
||||
} else if (GroupHeaderFromPointAbs(pt, group)) {
|
||||
CRect rc;
|
||||
if (GetGroupHeaderRectAbs(group, rc) && rc.PtInRect(pt)) {
|
||||
const t_uint32 estWidth = GetOptimalGroupHeaderWidth(group);
|
||||
CRect rcText = rc; rcText.right = rcText.left + estWidth;
|
||||
if (estWidth > (t_uint32)rc.Width() || (IsRectPartiallyObscuredAbs(rcText) && rcText.PtInRect(pt))) {
|
||||
pfc::string_formatter label;
|
||||
if (GetGroupHeaderText(group, label)) {
|
||||
outFont = GetGroupHeaderFont(); outRC = rc; outText = label;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
LRESULT CListControlTruncationTooltipImpl::OnHover(UINT,WPARAM wp,LPARAM lp,BOOL&) {
|
||||
if (!m_tooltipRect.IsRectEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
if (wp & (MK_LBUTTON | MK_RBUTTON | MK_MBUTTON)) return 0;
|
||||
const CPoint viewOffset = GetViewOffset();
|
||||
CPoint pt ( lp ); pt += viewOffset;
|
||||
|
||||
CFontHandle font;
|
||||
CRect rc;
|
||||
pfc::string8 text;
|
||||
if ( this->GetTooltipData(pt, text, rc, font) ) {
|
||||
this->m_tooltipFont = font;
|
||||
// Gets stuck if the text is very long!
|
||||
if (text.length() < 4096) {
|
||||
TooltipActivateAbs(text, rc);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CListControlTruncationTooltipImpl::TooltipActivateAbs(const char * label, const CRect & rect) {
|
||||
CRect temp(rect);
|
||||
temp.OffsetRect( - GetViewOffset() );
|
||||
ClientToScreen(temp);
|
||||
TooltipActivate(label,temp);
|
||||
}
|
||||
void CListControlTruncationTooltipImpl::TooltipActivate(const char * label, const CRect & rect) {
|
||||
if (rect.IsRectEmpty()) return;
|
||||
if (m_tooltip.m_hWnd == NULL) {
|
||||
try {
|
||||
InitTooltip();
|
||||
} catch(std::exception const & e) {
|
||||
(void) e;
|
||||
// console::complain("Tooltip initialization failure", e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_tooltipText.convert( EscapeTooltipText( label ) );
|
||||
|
||||
m_tooltipRect = rect;
|
||||
|
||||
TooltipUpdateFont();
|
||||
m_tooltip.TrackPosition(rect.left,rect.top);
|
||||
m_tooltip.TrackActivate(&m_toolinfo,TRUE);
|
||||
}
|
||||
|
||||
void CListControlTruncationTooltipImpl::TooltipUpdateFont() {
|
||||
if (m_tooltip.m_hWnd != NULL) {
|
||||
if (m_tooltipFont) {
|
||||
m_tooltip.SetFont(m_tooltipFont);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CListControlTruncationTooltipImpl::InitTooltip() {
|
||||
m_tooltipRect = CRect(0,0,0,0);
|
||||
WIN32_OP( m_tooltip.Create(NULL,NULL,NULL,WS_POPUP,WS_EX_TRANSPARENT) );
|
||||
m_toolinfo.cbSize = sizeof(m_toolinfo);
|
||||
m_toolinfo.uFlags = TTF_TRACK | TTF_IDISHWND | TTF_ABSOLUTE | TTF_TRANSPARENT;
|
||||
m_toolinfo.hwnd = *this;
|
||||
m_toolinfo.uId = 0;
|
||||
m_toolinfo.lpszText = LPSTR_TEXTCALLBACK;
|
||||
m_toolinfo.hinst = GetThisModuleHandle();
|
||||
WIN32_OP_D( m_tooltip.AddTool(&m_toolinfo) );
|
||||
}
|
||||
51
libPPUI/CListControlTruncationTooltipImpl.h
Normal file
51
libPPUI/CListControlTruncationTooltipImpl.h
Normal file
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
class CListControlTruncationTooltipImpl : public CListControlHeaderImpl {
|
||||
private:
|
||||
typedef CListControlHeaderImpl TParent;
|
||||
public:
|
||||
CListControlTruncationTooltipImpl();
|
||||
|
||||
BEGIN_MSG_MAP_EX(CListControlTruncationTooltipImpl)
|
||||
MESSAGE_HANDLER(WM_MOUSEHOVER,OnHover);
|
||||
MESSAGE_HANDLER(WM_MOUSEMOVE,OnMouseMovePassThru);
|
||||
MESSAGE_HANDLER(WM_TIMER,OnTimer);
|
||||
MESSAGE_HANDLER(WM_DESTROY,OnDestroyPassThru);
|
||||
CHAIN_MSG_MAP(TParent)
|
||||
NOTIFY_CODE_HANDLER(TTN_GETDISPINFO,OnTTGetDispInfo);
|
||||
NOTIFY_CODE_HANDLER(TTN_POP,OnTTPop);
|
||||
NOTIFY_CODE_HANDLER(TTN_SHOW,OnTTShow);
|
||||
END_MSG_MAP()
|
||||
|
||||
void OnViewOriginChange(CPoint p_delta) {TParent::OnViewOriginChange(p_delta);TooltipRemove();}
|
||||
void TooltipRemove();
|
||||
protected:
|
||||
virtual bool GetTooltipData( CPoint ptAbs, pfc::string_base & text, CRect & rc, CFontHandle & font) const;
|
||||
private:
|
||||
enum {
|
||||
KTooltipTimer = 0x51dbee9e,
|
||||
KTooltipTimerDelay = 50,
|
||||
};
|
||||
LRESULT OnHover(UINT,WPARAM,LPARAM,BOOL&);
|
||||
LRESULT OnMouseMovePassThru(UINT,WPARAM,LPARAM,BOOL&);
|
||||
LRESULT OnTimer(UINT,WPARAM,LPARAM,BOOL&);
|
||||
LRESULT OnTTGetDispInfo(int,LPNMHDR,BOOL&);
|
||||
LRESULT OnTTShow(int,LPNMHDR,BOOL&);
|
||||
LRESULT OnTTPop(int,LPNMHDR,BOOL&);
|
||||
LRESULT OnDestroyPassThru(UINT,WPARAM,LPARAM,BOOL&);
|
||||
|
||||
void InitTooltip();
|
||||
void TooltipActivateAbs(const char * label, const CRect & rect);
|
||||
void TooltipActivate(const char * label, const CRect & rect);
|
||||
void TooltipRemoveCheck(LPARAM pos);
|
||||
void TooltipRemoveCheck();
|
||||
void TooltipUpdateFont();
|
||||
void OnSetFont(bool) {TooltipUpdateFont();}
|
||||
bool IsRectFullyVisibleAbs(CRect const & r);
|
||||
bool IsRectPartiallyObscuredAbs(CRect const & r) const;
|
||||
CRect m_tooltipRect;
|
||||
CToolTipCtrl m_tooltip;
|
||||
TOOLINFO m_toolinfo;
|
||||
pfc::stringcvt::string_os_from_utf8 m_tooltipText;
|
||||
CFontHandle m_tooltipFont;
|
||||
};
|
||||
9
libPPUI/CListControlUserOptions.h
Normal file
9
libPPUI/CListControlUserOptions.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
class CListControlUserOptions {
|
||||
public:
|
||||
CListControlUserOptions() { instance = this; }
|
||||
virtual bool useSmoothScroll() = 0;
|
||||
|
||||
static CListControlUserOptions * instance;
|
||||
};
|
||||
1587
libPPUI/CListControlWithSelection.cpp
Normal file
1587
libPPUI/CListControlWithSelection.cpp
Normal file
File diff suppressed because it is too large
Load Diff
265
libPPUI/CListControlWithSelection.h
Normal file
265
libPPUI/CListControlWithSelection.h
Normal file
@@ -0,0 +1,265 @@
|
||||
#pragma once
|
||||
|
||||
#include "CListControl.h"
|
||||
|
||||
//! Implementation of focus/selection handling. Leaves maintaining focus/selection info to the derived class. \n
|
||||
//! Most classes should derive from CListControlWithSelectionImpl instead.
|
||||
class CListControlWithSelectionBase : public CListControl {
|
||||
public:
|
||||
typedef CListControl TParent;
|
||||
CListControlWithSelectionBase() : m_selectDragMode(), m_prepareDragDropMode(), m_prepareDragDropModeRightClick(), m_ownDDActive(), m_noEnsureVisible(), m_drawThemeText(), m_typeFindTS() {}
|
||||
BEGIN_MSG_MAP_EX(CListControlWithSelectionBase)
|
||||
MSG_WM_CREATE(OnCreatePassThru);
|
||||
MSG_WM_DESTROY(OnDestroyPassThru);
|
||||
CHAIN_MSG_MAP(TParent)
|
||||
MESSAGE_HANDLER(WM_LBUTTONDBLCLK,OnLButtonDblClk)
|
||||
MESSAGE_HANDLER(WM_LBUTTONDOWN,OnButtonDown)
|
||||
MESSAGE_HANDLER(WM_RBUTTONDOWN,OnButtonDown)
|
||||
MESSAGE_HANDLER(WM_RBUTTONDBLCLK,OnButtonDown)
|
||||
MESSAGE_HANDLER(WM_RBUTTONUP,OnRButtonUp)
|
||||
MESSAGE_HANDLER(WM_MOUSEMOVE,OnMouseMove)
|
||||
MESSAGE_HANDLER(WM_LBUTTONUP,OnLButtonUp)
|
||||
MESSAGE_HANDLER(WM_KEYDOWN,OnKeyDown);
|
||||
MESSAGE_HANDLER(WM_SYSKEYDOWN,OnKeyDown);
|
||||
MESSAGE_HANDLER(WM_SETFOCUS,OnFocus);
|
||||
MESSAGE_HANDLER(WM_KILLFOCUS,OnFocus);
|
||||
MESSAGE_HANDLER(WM_TIMER,OnTimer);
|
||||
MESSAGE_HANDLER(WM_CAPTURECHANGED,OnCaptureChanged);
|
||||
MESSAGE_HANDLER(WM_GETDLGCODE,OnGetDlgCode);
|
||||
MSG_WM_CHAR(OnChar)
|
||||
END_MSG_MAP()
|
||||
|
||||
virtual void SetFocusItem(t_size index) = 0;
|
||||
virtual t_size GetFocusItem() const = 0;
|
||||
virtual void SetGroupFocus(int group) = 0;
|
||||
virtual void SetGroupFocusByItem(t_size item) = 0;
|
||||
virtual int GetGroupFocus() const = 0;
|
||||
virtual bool IsItemSelected(t_size index) const = 0;
|
||||
virtual void SetSelection(pfc::bit_array const & affected,pfc::bit_array const & status) = 0;
|
||||
void SelectSingle(size_t which);
|
||||
virtual bool SelectAll();
|
||||
void SelectNone();
|
||||
virtual void RequestMoveSelection(int delta);
|
||||
bool MoveSelectionProbe(int delta);
|
||||
virtual void RequestReorder( size_t const * order, size_t count ) = 0;
|
||||
virtual void RequestRemoveSelection() = 0;
|
||||
virtual void ExecuteDefaultAction(t_size index) = 0;
|
||||
virtual void ExecuteDefaultActionGroup(t_size base, t_size count) {}
|
||||
virtual bool ExecuteCanvasDefaultAction(CPoint pt) { return false; }
|
||||
|
||||
virtual t_size GetSelectionStart() const = 0;
|
||||
virtual void SetSelectionStart(t_size val) = 0;
|
||||
//! Full hook for drag-drop loop
|
||||
virtual void RunDragDrop(const CPoint & p_origin,bool p_isRightClick);
|
||||
|
||||
//! Should RunDragDrop() be called at all?
|
||||
virtual bool IsDragDropSupported() {return QueryDragDropTypes() != 0;}
|
||||
|
||||
//! Notification, mandatory to call by SetFocusItem() implementation. \n
|
||||
//! If overridden by subclass, must call parent.
|
||||
virtual void OnFocusChanged(size_t newFocus) {}
|
||||
virtual void OnFocusChangedGroup(int inGroup) {}
|
||||
//! Notification, mandatory to call by SetSelection() implementation. \n
|
||||
//! If overridden by subclass, must call parent.
|
||||
virtual void OnSelectionChanged(pfc::bit_array const & affected, pfc::bit_array const & status) {}
|
||||
|
||||
enum {
|
||||
dragDrop_reorder = 1 << 0,
|
||||
dragDrop_external = 1 << 1,
|
||||
};
|
||||
|
||||
virtual uint32_t QueryDragDropTypes() const { return 0; }
|
||||
virtual DWORD DragDropAccept(IDataObject * obj, bool & showDropMark) { return DROPEFFECT_NONE; }
|
||||
virtual pfc::com_ptr_t<IDataObject> MakeDataObject();
|
||||
virtual void OnDrop(IDataObject * obj, CPoint pt ) {}
|
||||
virtual DWORD DragDropSourceEffects() { return DROPEFFECT_MOVE | DROPEFFECT_COPY;}
|
||||
virtual void DragDropSourceSucceeded( DWORD effect ) {}
|
||||
|
||||
bool GroupFocusActive() const {return GetGroupFocus() >= 0;}
|
||||
|
||||
void RenderOverlay(const CRect & p_updaterect,CDCHandle p_dc);
|
||||
|
||||
bool IsItemFocused(t_size index) const {return GetFocusItem() == index;}
|
||||
bool IsGroupHeaderFocused(int p_id) const {return GetGroupFocus() == p_id;}
|
||||
void ToggleSelection(pfc::bit_array const & mask);
|
||||
|
||||
size_t GetSelectedCount(pfc::bit_array const & mask,size_t max = SIZE_MAX) const;
|
||||
size_t GetSelectedCount() const {return GetSelectedCount(pfc::bit_array_true());}
|
||||
size_t GetSingleSel() const;
|
||||
size_t GetFirstSelected() const;
|
||||
size_t GetLastSelected() const;
|
||||
|
||||
//! Execute default action per focus or selection depending on what's focused/selected
|
||||
virtual void ExecuteDefaultActionByFocus();
|
||||
|
||||
void FocusToUpdateRgn(HRGN rgn);
|
||||
|
||||
|
||||
//! Self-contained minimal drag and drop implementation for reordering list items only; dummy IDataObject presented. \n
|
||||
//! Call from your override of RunDragDrop(), if p_isRightClick is false / left button clicked, never with right button clicked. \n
|
||||
//! On success, use MakeDropReorderPermutation() to fetch the permutation to apply to your content.
|
||||
bool RunReorderDragDrop(CPoint ptOrigin, CPoint & ptDrop);
|
||||
|
||||
bool MakeDropReorderPermutation(pfc::array_t<t_size> & out, CPoint ptDrop) const;
|
||||
|
||||
size_t GetPasteTarget( const CPoint * ptPaste = nullptr ) const;
|
||||
|
||||
CPoint GetContextMenuPoint(LPARAM lp);
|
||||
CPoint GetContextMenuPoint(CPoint ptGot);
|
||||
|
||||
protected:
|
||||
void ToggleDDScroll(bool p_state);
|
||||
void AbortSelectDragMode() {AbortSelectDragMode(false);}
|
||||
void RenderDropMarkerByOffset(int offset,CDCHandle p_dc);
|
||||
void RenderDropMarker(CDCHandle dc, t_size item, bool bInside);
|
||||
bool RenderDropMarkerClipped(CDCHandle dc, const CRect & update, t_size item, bool bInside);
|
||||
CRect DropMarkerRect(int offset) const;
|
||||
int DropMarkerOffset(t_size marker) const;
|
||||
void AddDropMarkToUpdateRgn(HRGN p_rgn, t_size p_index, bool bInside = false) const;
|
||||
CRect DropMarkerUpdateRect(t_size index,bool bInside) const;
|
||||
bool GetFocusRect(CRect & p_rect);
|
||||
bool GetFocusRectAbs(CRect & p_rect);
|
||||
bool IsOwnDDActive() const {return m_ownDDActive;}
|
||||
|
||||
SIZE DropMarkerMargin() const;
|
||||
void MakeDropMarkerPen(CPen & out) const;
|
||||
|
||||
virtual void EnsureVisibleRectAbs(const CRect & p_rect);
|
||||
virtual size_t EvalTypeFind();
|
||||
|
||||
virtual bool AllowRangeSelect() const { return true; }
|
||||
|
||||
CRect GetWholeSelectionRectAbs() const;
|
||||
|
||||
size_t GetDropMark( ) const { return m_dropMark; }
|
||||
bool IsDropMarkInside( ) const { return m_dropMarkInside; }
|
||||
void SetDropMark( size_t idx, bool bInside );
|
||||
void ClearDropMark() { SetDropMark(pfc_infinite, false); }
|
||||
private:
|
||||
int OnCreatePassThru(LPCREATESTRUCT lpCreateStruct);
|
||||
void OnDestroyPassThru();
|
||||
|
||||
struct TDDScrollControl {
|
||||
bool m_timerActive = false;
|
||||
|
||||
enum {KTimerID = 0x35bb25af,KTimerPeriod = 25};
|
||||
};
|
||||
|
||||
enum {
|
||||
KSelectionTimerID = 0xad8abd04,
|
||||
KSelectionTimerPeriod = 50,
|
||||
};
|
||||
|
||||
LRESULT OnFocus(UINT,WPARAM,LPARAM,BOOL&);
|
||||
LRESULT OnKeyDown(UINT,WPARAM,LPARAM,BOOL&);
|
||||
LRESULT OnLButtonDblClk(UINT,WPARAM,LPARAM,BOOL&);
|
||||
LRESULT OnButtonDown(UINT,WPARAM,LPARAM,BOOL&);
|
||||
LRESULT OnRButtonUp(UINT,WPARAM,LPARAM,BOOL&);
|
||||
LRESULT OnMouseMove(UINT,WPARAM,LPARAM,BOOL&);
|
||||
LRESULT OnLButtonUp(UINT,WPARAM,LPARAM,BOOL&);
|
||||
LRESULT OnTimer(UINT,WPARAM,LPARAM,BOOL&);
|
||||
LRESULT OnCaptureChanged(UINT,WPARAM,LPARAM,BOOL&);
|
||||
LRESULT OnGetDlgCode(UINT,WPARAM,LPARAM,BOOL&);
|
||||
void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags);
|
||||
void RunTypeFind();
|
||||
|
||||
void OnKeyDown_HomeEndHelper(bool isEnd, int p_keys);
|
||||
void OnKeyDown_SetIndexHelper(int p_index, int p_keys);
|
||||
void OnKeyDown_SetIndexDeltaHelper(int p_delta, int p_keys);
|
||||
void OnKeyDown_SetIndexDeltaLineHelper(int p_delta, int p_keys);
|
||||
void OnKeyDown_SetIndexDeltaPageHelper(int p_delta, int p_keys);
|
||||
void SelectGroupHelper(int p_group,int p_keys);
|
||||
void HandleDragSel(const CPoint & p_pt);
|
||||
void AbortSelectDragMode(bool p_lostCapture);
|
||||
void InitSelectDragMode(const CPoint & p_pt,bool p_rightClick = false);
|
||||
|
||||
void ToggleRangeSelection(pfc::bit_array const & mask);
|
||||
void ToggleGroupSelection(int p_group);
|
||||
|
||||
void HandleDDScroll();
|
||||
|
||||
void PrepareDragDrop(const CPoint & p_point,bool p_isRightClick);
|
||||
void AbortPrepareDragDropMode(bool p_lostCapture = false);
|
||||
|
||||
bool TypeFindCheck(DWORD ts = GetTickCount()) const;
|
||||
|
||||
|
||||
protected:
|
||||
// Spacebar handler
|
||||
void ToggleSelectedItems();
|
||||
|
||||
void RenderItem(t_size p_item,const CRect & p_itemRect,const CRect & p_updateRect,CDCHandle p_dc);
|
||||
void RenderGroupHeader(int p_group,const CRect & p_headerRect,const CRect & p_updateRect,CDCHandle p_dc);
|
||||
void RenderSubItemText(t_size item, t_size subItem,const CRect & subItemRect,const CRect & updateRect,CDCHandle dc, bool allowColors);
|
||||
private:
|
||||
bool m_selectDragMode;
|
||||
CPoint m_selectDragOriginAbs, m_selectDragCurrentAbs;
|
||||
bool m_selectDragChanged, m_selectDragMoved;
|
||||
TDDScrollControl m_ddScroll;
|
||||
|
||||
bool m_prepareDragDropMode, m_prepareDragDropModeRightClick;
|
||||
bool m_noEnsureVisible;
|
||||
CPoint m_prepareDragDropOrigin;
|
||||
|
||||
bool m_ownDDActive;
|
||||
bool m_drawThemeText;
|
||||
pfc::string8 m_typeFind; DWORD m_typeFindTS;
|
||||
|
||||
size_t m_dropMark = SIZE_MAX; bool m_dropMarkInside = false;
|
||||
};
|
||||
|
||||
//! CListControlWithSelectionImpl implements virtual methods of CListControlWithSelectionBase,
|
||||
//! maintaining focus/selection info for you.
|
||||
class CListControlWithSelectionImpl : public CListControlWithSelectionBase {
|
||||
public:
|
||||
|
||||
enum { selectionSupportNone = 0, selectionSupportSingle, selectionSupportMulti };
|
||||
unsigned m_selectionSupport = selectionSupportMulti;
|
||||
|
||||
void SetSelectionModeNone() { m_selectionSupport = selectionSupportNone; }
|
||||
void SetSelectionModeSingle() { m_selectionSupport = selectionSupportSingle; }
|
||||
void SetSelectionModeMulti() { m_selectionSupport = selectionSupportMulti; }
|
||||
|
||||
|
||||
CListControlWithSelectionImpl() {}
|
||||
void SetFocusItem(t_size index);
|
||||
t_size GetFocusItem() const {return m_groupFocus ? SIZE_MAX : m_focus;}
|
||||
void SetGroupFocus(int group);
|
||||
void SetGroupFocusByItem(t_size item);
|
||||
int GetGroupFocus() const;
|
||||
bool IsItemSelected(t_size index) const {return index < m_selection.get_size() ? m_selection[index] : false;}
|
||||
void SetSelection(pfc::bit_array const & affected,pfc::bit_array const & status);
|
||||
virtual bool CanSelectItem( size_t index ) const { return true; }
|
||||
t_size GetSelectionStart() const {return m_selectionStart;}
|
||||
void SetSelectionStart(t_size val) {m_selectionStart = val;}
|
||||
|
||||
void SelHandleReorder(const t_size * order, t_size count);
|
||||
void SelHandleRemoval(const pfc::bit_array & mask, t_size oldCount);
|
||||
void SelHandleInsertion(t_size base, t_size count, bool select);
|
||||
void SelHandleReset();
|
||||
|
||||
void ReloadData() override;
|
||||
size_t _DebugGetItemCountSel() const { return m_selection.get_size(); }
|
||||
|
||||
virtual void OnItemsReordered( const size_t* order, size_t count ) override;
|
||||
virtual void OnItemsRemoved( pfc::bit_array const & mask, size_t oldCount ) override;
|
||||
virtual void OnItemsInserted( size_t at, size_t count, bool bSelect ) override;
|
||||
|
||||
pfc::bit_array_bittable GetSelectionMask(); // returns an standalone object holding a copy of the state
|
||||
|
||||
bool SelectAll() override;
|
||||
|
||||
protected:
|
||||
const bool * GetSelectionArray() {RefreshSelectionSize();return m_selection.get_ptr();}
|
||||
pfc::bit_array_table GetSelectionMaskRef(); // returns a *temporary* object referencing internal data structures
|
||||
|
||||
bool AllowRangeSelect() const override { return m_selectionSupport == selectionSupportMulti; }
|
||||
|
||||
private:
|
||||
void SetSelectionImpl(pfc::bit_array const & affected,pfc::bit_array const & status);
|
||||
void RefreshSelectionSize();
|
||||
void RefreshSelectionSize(t_size size);
|
||||
pfc::array_t<bool> m_selection;
|
||||
size_t m_focus = SIZE_MAX, m_selectionStart = SIZE_MAX;
|
||||
bool m_groupFocus = false;
|
||||
};
|
||||
62
libPPUI/CListControl_EditImpl.h
Normal file
62
libPPUI/CListControl_EditImpl.h
Normal file
@@ -0,0 +1,62 @@
|
||||
#pragma once
|
||||
|
||||
#include "InPlaceEditTable.h"
|
||||
#include "GDIUtils.h" // MakeTempBrush()
|
||||
|
||||
//! Implements inplace-edit functionality on top of TParent class. Must derive from CListControlHeaderImpl.
|
||||
template<typename T_CListControl_EditImpl_Parent> class CListControl_EditImpl : public T_CListControl_EditImpl_Parent, protected InPlaceEdit::CTableEditHelperV2 {
|
||||
public:
|
||||
BEGIN_MSG_MAP_EX(CListControl_EditImpl)
|
||||
CHAIN_MSG_MAP(T_CListControl_EditImpl_Parent)
|
||||
MESSAGE_HANDLER(WM_CTLCOLOREDIT,OnCtlColor);
|
||||
MESSAGE_HANDLER(WM_CTLCOLORSTATIC,OnCtlColor);
|
||||
END_MSG_MAP()
|
||||
|
||||
// IMPLEMENT ME
|
||||
// virtual void TableEdit_SetField(t_size item, t_size subItem, const char * value) = 0;
|
||||
protected:
|
||||
RECT TableEdit_GetItemRect(t_size item, t_size subItem) const override {
|
||||
return this->GetSubItemRect(item,subItem);
|
||||
}
|
||||
void TableEdit_GetField(t_size item, t_size subItem, pfc::string_base & out, t_size & lineCount) override {
|
||||
lineCount = 1;
|
||||
if (!this->GetSubItemText(item,subItem,out)) {
|
||||
PFC_ASSERT(!"Should not get here."); out = "";
|
||||
}
|
||||
}
|
||||
HWND TableEdit_GetParentWnd() const override {return this->m_hWnd;}
|
||||
t_size TableEdit_GetItemCount() const override {return this->GetItemCount();}
|
||||
t_size TableEdit_GetColumnCount() const override {return this->GetColumnCount();}
|
||||
void TableEdit_SetItemFocus(t_size item, t_size subItem) override {
|
||||
this->TooltipRemove();
|
||||
this->SetFocusItem(item); this->SetSelection(pfc::bit_array_true(), pfc::bit_array_one(item));
|
||||
auto rcView = this->GetVisibleRectAbs();
|
||||
auto rcEdit = this->GetSubItemRectAbs(item,subItem);
|
||||
CRect rcTest;
|
||||
if (!rcTest.IntersectRect(rcView, rcEdit)) {
|
||||
// Only scroll to subitem if entirely invisible
|
||||
this->EnsureVisibleRectAbs( rcEdit );
|
||||
}
|
||||
}
|
||||
void TableEdit_GetColumnOrder(t_size * out, t_size count) const override {
|
||||
PFC_ASSERT( count == this->GetColumnCount() );
|
||||
if ( this->IsHeaderEnabled() ) {
|
||||
pfc::array_t<int> temp; temp.set_size(count);
|
||||
WIN32_OP_D( this->GetHeaderCtrl().GetOrderArray(temp.get_size(), temp.get_ptr()) );
|
||||
for(t_size walk = 0; walk < count; ++walk) out[walk] = (t_size) temp[walk];
|
||||
} else {
|
||||
for(t_size walk = 0; walk < count; ++walk) out[walk] = walk;
|
||||
}
|
||||
}
|
||||
|
||||
void TableEdit_OnColorsChanged() {}
|
||||
private:
|
||||
LRESULT OnCtlColor(UINT,WPARAM wp,LPARAM lp,BOOL&) {
|
||||
CDCHandle dc((HDC)wp);
|
||||
const COLORREF bkgnd = this->GetSysColorHook(CListControlImpl::colorBackground);
|
||||
dc.SetTextColor(this->GetSysColorHook(CListControlImpl::colorText));
|
||||
dc.SetBkColor(bkgnd);
|
||||
|
||||
return (LRESULT) MakeTempBrush(dc, bkgnd);
|
||||
}
|
||||
};
|
||||
34
libPPUI/CListViewCtrlEx.h
Normal file
34
libPPUI/CListViewCtrlEx.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
class CListViewCtrlEx : public CListViewCtrl {
|
||||
public:
|
||||
CListViewCtrlEx( HWND wnd = NULL ) : CListViewCtrl(wnd) {}
|
||||
CListViewCtrlEx const & operator=( HWND wnd ) { m_hWnd = wnd; return *this; }
|
||||
unsigned InsertColumnEx(unsigned index, const wchar_t * name, unsigned widthDLU);
|
||||
unsigned AddColumnEx( const wchar_t * name, unsigned widthDLU );
|
||||
void FixContextMenuPoint( CPoint & pt );
|
||||
unsigned GetColunnCount();
|
||||
|
||||
unsigned InsertString( unsigned index, const wchar_t * str );
|
||||
unsigned InsertString8( unsigned index, const char * str );
|
||||
unsigned AddString( const wchar_t * str );
|
||||
unsigned AddString8(const char * str);
|
||||
void SetItemText(unsigned item, unsigned subItem, const wchar_t * str );
|
||||
void SetItemText8(unsigned item, unsigned subItem, const char * str );
|
||||
|
||||
void AutoSizeColumn( int iCol ) { SetColumnWidth(iCol, LVSCW_AUTOSIZE) ;}
|
||||
int AddGroup(int iGroupID, const wchar_t * header);
|
||||
};
|
||||
|
||||
// BOOL HandleLVKeyDownMod()
|
||||
#define LVN_KEYDOWN_MOD_HANDLER(id, key, mod, func) \
|
||||
if (uMsg == WM_NOTIFY && LVN_KEYDOWN == ((LPNMHDR)lParam)->code && id == ((LPNMHDR)lParam)->idFrom && ((LPNMLVKEYDOWN)lParam)->wVKey == (key) && GetHotkeyModifierFlags() == (mod)) \
|
||||
{ \
|
||||
SetMsgHandled(TRUE); \
|
||||
lResult = func()?1:0; \
|
||||
if(IsMsgHandled()) \
|
||||
return TRUE; \
|
||||
}
|
||||
|
||||
// BOOL HandleLVCopy()
|
||||
#define LVN_COPY_HANDLER(id, func) LVN_KEYDOWN_MOD_HANDLER(id, 'C', MOD_CONTROL, func)
|
||||
40
libPPUI/CMiddleDragImpl.cpp
Normal file
40
libPPUI/CMiddleDragImpl.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
#include "stdafx.h"
|
||||
#include "CMiddleDragImpl.h"
|
||||
|
||||
double CMiddleDragCommon::myPow(double p_val, double p_exp) {
|
||||
if (p_val < 0) {
|
||||
return -pow(-p_val, p_exp);
|
||||
} else {
|
||||
return pow(p_val, p_exp);
|
||||
}
|
||||
}
|
||||
|
||||
double CMiddleDragCommon::ProcessMiddleDragDeltaInternal(double p_delta) {
|
||||
p_delta *= (double)KTimerPeriod / 25; /*originally calculated for 25ms timer interval*/
|
||||
return myPow(p_delta * 0.05, 2.0);
|
||||
}
|
||||
|
||||
double CMiddleDragCommon::radiusHelper(double p_x, double p_y) {
|
||||
return sqrt(p_x * p_x + p_y * p_y);
|
||||
}
|
||||
int CMiddleDragCommon::mySGN(LONG v) {
|
||||
if (v > 0) return 1;
|
||||
else if (v < 0) return -1;
|
||||
else return 0;
|
||||
}
|
||||
|
||||
int32_t CMiddleDragCommon::Round(double val, double & acc) {
|
||||
val += acc;
|
||||
int32_t ret = (int32_t)floor(val + 0.5);
|
||||
acc = val - ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
LONG CMiddleDragCommon::LineToPixelsHelper(LONG & p_overflow, LONG p_pixels, LONG p_dpi, LONG p_lineWidth) {
|
||||
const int lineWidth = MulDiv(p_lineWidth, p_dpi, 96);
|
||||
if (lineWidth == 0) return 0;
|
||||
p_overflow += p_pixels;
|
||||
LONG ret = p_overflow / lineWidth;
|
||||
p_overflow -= ret * lineWidth;
|
||||
return ret;
|
||||
}
|
||||
280
libPPUI/CMiddleDragImpl.h
Normal file
280
libPPUI/CMiddleDragImpl.h
Normal file
@@ -0,0 +1,280 @@
|
||||
#pragma once
|
||||
|
||||
#include "CIconOverlayWindow.h"
|
||||
#include <utility>
|
||||
#include "ppresources.h"
|
||||
#include "win32_utility.h"
|
||||
|
||||
class CMiddleDragCommon {
|
||||
public:
|
||||
enum {
|
||||
KTimerID = 0x389675f8,
|
||||
KTimerPeriod = 15,
|
||||
};
|
||||
static double myPow(double p_val, double p_exp);
|
||||
static double ProcessMiddleDragDeltaInternal(double p_delta);
|
||||
static double radiusHelper(double p_x, double p_y);
|
||||
static int mySGN(LONG v);
|
||||
static int32_t Round(double val, double & acc);
|
||||
static LONG LineToPixelsHelper(LONG & p_overflow, LONG p_pixels, LONG p_dpi, LONG p_lineWidth);
|
||||
};
|
||||
|
||||
template<typename TBase>
|
||||
class CMiddleDragImpl : public TBase, protected CMiddleDragCommon {
|
||||
private:
|
||||
typedef CMiddleDragImpl<TBase> TSelf;
|
||||
public:
|
||||
template<typename ... arg_t> CMiddleDragImpl( arg_t && ... arg ) : TBase(std::forward<arg_t>(arg) ... ) {}
|
||||
|
||||
BEGIN_MSG_MAP_EX(TSelf)
|
||||
MESSAGE_HANDLER(WM_TIMER,OnTimer);
|
||||
MESSAGE_HANDLER(WM_CAPTURECHANGED,OnCaptureChanged);
|
||||
MESSAGE_HANDLER(WM_MBUTTONDOWN,OnMButtonDown);
|
||||
MESSAGE_HANDLER(WM_MBUTTONDBLCLK,OnMButtonDown);
|
||||
MESSAGE_HANDLER(WM_MBUTTONUP,OnMButtonUp);
|
||||
MESSAGE_HANDLER(WM_LBUTTONDOWN,OnMouseAction);
|
||||
MESSAGE_HANDLER(WM_RBUTTONDOWN,OnMouseAction);
|
||||
MESSAGE_HANDLER(WM_LBUTTONDBLCLK,OnMouseAction);
|
||||
MESSAGE_HANDLER(WM_RBUTTONDBLCLK,OnMouseAction);
|
||||
MESSAGE_HANDLER(WM_LBUTTONUP,OnMouseAction);
|
||||
MESSAGE_HANDLER(WM_RBUTTONUP,OnMouseAction);
|
||||
MESSAGE_HANDLER(WM_XBUTTONDOWN,OnMouseAction);
|
||||
MESSAGE_HANDLER(WM_XBUTTONDBLCLK,OnMouseAction);
|
||||
MESSAGE_HANDLER(WM_XBUTTONUP,OnMouseAction);
|
||||
MESSAGE_HANDLER(WM_MOUSEMOVE,OnMouseMove);
|
||||
MESSAGE_HANDLER(WM_DESTROY,OnDestroyPassThru);
|
||||
CHAIN_MSG_MAP(TBase);
|
||||
END_MSG_MAP()
|
||||
protected:
|
||||
bool CanSmoothScroll() const {
|
||||
return !m_active;
|
||||
}
|
||||
private:
|
||||
bool m_active = false, m_dragged = false;
|
||||
CPoint m_base;
|
||||
CIconOverlayWindow m_overlay;
|
||||
double m_accX = 0, m_accY = 0;
|
||||
|
||||
LRESULT OnDestroyPassThru(UINT,WPARAM,LPARAM,BOOL& bHandled) {
|
||||
if (m_overlay != NULL) m_overlay.DestroyWindow();
|
||||
bHandled = FALSE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
LRESULT OnMButtonUp(UINT,WPARAM,LPARAM p_lp,BOOL& bHandled) {
|
||||
if (m_active /*&& m_dragged*/) {
|
||||
EndDrag();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
LRESULT OnMButtonDown(UINT,WPARAM,LPARAM p_lp,BOOL& bHandled) {
|
||||
if (m_active) {
|
||||
EndDrag();
|
||||
return 0;
|
||||
}
|
||||
EndDrag();
|
||||
this->SetFocus();
|
||||
CPoint pt(p_lp);
|
||||
WIN32_OP_D( this->ClientToScreen(&pt) );
|
||||
StartDrag(pt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
LRESULT OnMouseMove(UINT,WPARAM,LPARAM p_lp,BOOL& bHandled) {
|
||||
if (m_active) {
|
||||
if (!m_dragged) {
|
||||
CPoint pt(p_lp);
|
||||
WIN32_OP_D( this->ClientToScreen(&pt) );
|
||||
if (pt != m_base) {
|
||||
m_dragged = true;
|
||||
}
|
||||
}
|
||||
bHandled = TRUE;
|
||||
} else {
|
||||
bHandled = FALSE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
LRESULT OnMouseAction(UINT,WPARAM,LPARAM,BOOL& bHandled) {
|
||||
EndDrag();
|
||||
bHandled = FALSE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void StartDrag(CPoint const & p_point) {
|
||||
::SetCapture(NULL);
|
||||
|
||||
if (m_overlay == NULL) {
|
||||
PFC_ASSERT( this->m_hWnd != NULL );
|
||||
if (m_overlay.Create(*this) == NULL) {PFC_ASSERT(!"Should not get here!"); return;}
|
||||
HANDLE temp;
|
||||
WIN32_OP_D( (temp = LoadImage(GetThisModuleHandle(),MAKEINTRESOURCE(CPPUIResources::get_IDI_SCROLL()),IMAGE_ICON,32,32,LR_DEFAULTCOLOR)) != NULL );
|
||||
m_overlay.AttachIcon((HICON) temp);
|
||||
}
|
||||
|
||||
//todo sanity checks - don't drag when the entire content is visible, perhaps use a different icon when only vertical or horizontal drag is possible
|
||||
SetCursor(::LoadCursor(NULL,IDC_SIZEALL));
|
||||
m_active = true;
|
||||
m_dragged = false;
|
||||
m_base = p_point;
|
||||
m_accX = m_accY = 0;
|
||||
this->SetCapture();
|
||||
this->SetTimer(KTimerID,KTimerPeriod);
|
||||
|
||||
{
|
||||
CSize radius(16,16);
|
||||
CPoint center (p_point);
|
||||
CRect rect(center - radius, center + radius);
|
||||
m_overlay.SetWindowPos(HWND_TOPMOST,rect,SWP_SHOWWINDOW | SWP_NOACTIVATE);
|
||||
}
|
||||
}
|
||||
|
||||
void EndDrag() {
|
||||
if (m_active) ::SetCapture(NULL);
|
||||
}
|
||||
|
||||
void HandleEndDrag() {
|
||||
if (m_active) {
|
||||
m_active = false;
|
||||
this->KillTimer(KTimerID);
|
||||
if (m_overlay != NULL) m_overlay.ShowWindow(SW_HIDE);
|
||||
}
|
||||
}
|
||||
|
||||
LRESULT OnCaptureChanged(UINT,WPARAM,LPARAM,BOOL& bHandled) {
|
||||
HandleEndDrag();
|
||||
bHandled = FALSE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
LRESULT OnTimer(UINT,WPARAM p_wp,LPARAM,BOOL& bHandled) {
|
||||
switch(p_wp) {
|
||||
case KTimerID:
|
||||
this->MoveViewOriginDelta(ProcessMiddleDragDelta(CPoint(GetCursorPos()) - m_base));
|
||||
return 0;
|
||||
default:
|
||||
bHandled = FALSE;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
CPoint ProcessMiddleDragDelta(CPoint p_delta) {
|
||||
const CSize dpi = QueryScreenDPIEx();
|
||||
if (dpi.cx <= 0 || dpi.cy <= 0 || p_delta == CPoint(0, 0)) return CPoint(0, 0);
|
||||
const double dpiMulX = 96.0 / (double)dpi.cx;
|
||||
const double dpiMulY = 96.0 / (double)dpi.cy;
|
||||
|
||||
const double deltaTotal = ProcessMiddleDragDeltaInternal(radiusHelper((double)p_delta.x * dpiMulX, (double)p_delta.y * dpiMulY));
|
||||
|
||||
double xVal = 0, yVal = 0;
|
||||
|
||||
if (p_delta.x == 0) {
|
||||
yVal = deltaTotal;
|
||||
} else if (p_delta.y == 0) {
|
||||
xVal = deltaTotal;
|
||||
} else {
|
||||
const double ratio = (double)p_delta.x / (double)p_delta.y;
|
||||
yVal = sqrt((deltaTotal*deltaTotal) / (1.0 + (ratio*ratio)));
|
||||
xVal = yVal * ratio;
|
||||
}
|
||||
|
||||
xVal = mySGN(p_delta.x) * fabs(xVal);
|
||||
yVal = mySGN(p_delta.y) * fabs(yVal);
|
||||
|
||||
|
||||
return CPoint(Round(xVal / dpiMulX, m_accX), Round(yVal / dpiMulY, m_accY));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template<typename TBase>
|
||||
class CMiddleDragWrapper : public TBase {
|
||||
private:
|
||||
typedef CMiddleDragWrapper<TBase> TSelf;
|
||||
public:
|
||||
template<typename ... arg_t> CMiddleDragWrapper(arg_t && ... arg ) : TBase(std::forward<arg_t>(arg) ... ) { m_overflow = CPoint(0, 0); m_lineWidth = CSize(4, 16); }
|
||||
|
||||
BEGIN_MSG_MAP_EX(TSelf)
|
||||
MESSAGE_HANDLER(WM_CAPTURECHANGED,OnCaptureChanged);
|
||||
CHAIN_MSG_MAP(TBase)
|
||||
END_MSG_MAP()
|
||||
|
||||
void MoveViewOriginDelta(CPoint p_delta) {
|
||||
MoveViewOriginDeltaLines( MiddleDrag_PixelsToLines( m_overflow, p_delta ) );
|
||||
}
|
||||
void MoveViewOriginDeltaLines(CPoint p_delta) {
|
||||
FireScrollMessage(WM_HSCROLL,p_delta.x);
|
||||
FireScrollMessage(WM_VSCROLL,p_delta.y);
|
||||
}
|
||||
void SetLineWidth(CSize p_width) {m_lineWidth = p_width;}
|
||||
|
||||
private:
|
||||
void FireScrollMessage(UINT p_msg,int p_delta) {
|
||||
UINT count = (UINT)(p_delta<0?-p_delta:p_delta);
|
||||
const UINT which = (p_msg == WM_HSCROLL ? SB_HORZ : SB_VERT);
|
||||
SCROLLINFO si = {}; si.cbSize = sizeof(si); si.fMask = SIF_PAGE | SIF_RANGE;
|
||||
if (!this->GetScrollInfo(which, &si) || si.nPage <= 0) return;
|
||||
while(count >= si.nPage) {
|
||||
const WPARAM code = p_delta < 0 ? SB_PAGEUP : SB_PAGEDOWN;
|
||||
this->SendMessage(p_msg, code, 0);
|
||||
count -= si.nPage;
|
||||
}
|
||||
const WPARAM code = p_delta < 0 ? SB_LINEUP : SB_LINEDOWN;
|
||||
for(UINT walk = 0; walk < count; ++walk) this->SendMessage(p_msg, code, 0);
|
||||
}
|
||||
LRESULT OnCaptureChanged(UINT,WPARAM,LPARAM,BOOL& bHandled) {
|
||||
m_overflow = CPoint(0,0);
|
||||
bHandled = FALSE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
CPoint MiddleDrag_PixelsToLines(CPoint & p_overflow,CPoint p_pixels) {
|
||||
const CSize dpi = QueryScreenDPIEx();
|
||||
if (dpi.cx <= 0 || dpi.cy <= 0) return CPoint(0,0);
|
||||
CPoint pt;
|
||||
pt.x = CMiddleDragCommon::LineToPixelsHelper(p_overflow.x,p_pixels.x,dpi.cx,m_lineWidth.cx);
|
||||
pt.y = CMiddleDragCommon::LineToPixelsHelper(p_overflow.y,p_pixels.y,dpi.cy,m_lineWidth.cy);
|
||||
return pt;
|
||||
}
|
||||
|
||||
CSize m_lineWidth;
|
||||
|
||||
CPoint m_overflow;
|
||||
};
|
||||
|
||||
template<typename TBase = CWindow>
|
||||
class CWindow_DummyMsgMap : public TBase , public CMessageMap {
|
||||
public:
|
||||
BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam,
|
||||
LRESULT& lResult, DWORD dwMsgMapID = 0) {return FALSE;}
|
||||
};
|
||||
|
||||
typedef CMiddleDragImpl<CMiddleDragWrapper<CWindow_DummyMsgMap<> > > CMiddleDragImplSimple;
|
||||
|
||||
template<typename TBase = CWindow>
|
||||
class CContainedWindow_MsgMap : public CContainedWindowT<CWindow_DummyMsgMap<TBase> > {
|
||||
protected:
|
||||
CContainedWindow_MsgMap() : CContainedWindowT<CWindow_DummyMsgMap<TBase> >(msgMapCast(this)) {}
|
||||
private:
|
||||
template<typename t> static CMessageMap * msgMapCast(t* arg) { return arg; }
|
||||
};
|
||||
|
||||
template<typename TBase>
|
||||
class CMiddleDragImplCtrlHook : public CMiddleDragImpl<CMiddleDragWrapper<CContainedWindow_MsgMap<TBase> > > {
|
||||
};
|
||||
|
||||
template<typename TBase> class CMiddleDragImplCtrlHookEx : public CMiddleDragImplCtrlHook<TBase> {
|
||||
public:
|
||||
CMiddleDragImplCtrlHookEx(CMessageMap * hook, DWORD hookMapID = 0) : m_hook(*hook), m_hookMapID(hookMapID) {}
|
||||
|
||||
BEGIN_MSG_MAP_EX(CMiddleDragImplCtrlHookEx)
|
||||
CHAIN_MSG_MAP(CMiddleDragImplCtrlHook<TBase>)
|
||||
CHAIN_MSG_MAP_ALT_MEMBER(m_hook,m_hookMapID);
|
||||
END_MSG_MAP()
|
||||
private:
|
||||
DWORD const m_hookMapID;
|
||||
CMessageMap & m_hook;
|
||||
};
|
||||
101
libPPUI/CPopupTooltipMessage.h
Normal file
101
libPPUI/CPopupTooltipMessage.h
Normal file
@@ -0,0 +1,101 @@
|
||||
#pragma once
|
||||
|
||||
#include "win32_op.h"
|
||||
#include "wtl-pp.h"
|
||||
|
||||
class CPopupTooltipMessage {
|
||||
public:
|
||||
CPopupTooltipMessage(DWORD style = TTS_BALLOON | TTS_NOPREFIX) : m_style(style | WS_POPUP), m_toolinfo(), m_shutDown() {}
|
||||
void ShowFocus(const TCHAR * message, CWindow wndParent) {
|
||||
Show(message, wndParent); wndParent.SetFocus();
|
||||
}
|
||||
void Show(const TCHAR * message, CWindow wndParent) {
|
||||
if (m_shutDown || (message == NULL && m_tooltip.m_hWnd == NULL)) return;
|
||||
Initialize();
|
||||
Hide();
|
||||
|
||||
if (message != NULL) {
|
||||
CRect rect;
|
||||
WIN32_OP_D(wndParent.GetWindowRect(rect));
|
||||
ShowInternal(message, wndParent, rect);
|
||||
}
|
||||
}
|
||||
void ShowEx(const TCHAR * message, CWindow wndParent, CRect rect) {
|
||||
if (m_shutDown) return;
|
||||
Initialize();
|
||||
Hide();
|
||||
ShowInternal(message, wndParent, rect);
|
||||
}
|
||||
void Hide() {
|
||||
if (m_tooltip.m_hWnd != NULL && m_tooltip.GetToolCount() > 0) {
|
||||
m_tooltip.TrackActivate(&m_toolinfo, FALSE);
|
||||
m_tooltip.DelTool(&m_toolinfo);
|
||||
}
|
||||
}
|
||||
|
||||
void CleanUp() {
|
||||
if (m_tooltip.m_hWnd != NULL) {
|
||||
m_tooltip.DestroyWindow();
|
||||
}
|
||||
}
|
||||
void ShutDown() {
|
||||
m_shutDown = true; CleanUp();
|
||||
}
|
||||
private:
|
||||
void ShowInternal(const TCHAR * message, CWindow wndParent, CRect rect) {
|
||||
|
||||
PFC_ASSERT(!m_shutDown);
|
||||
PFC_ASSERT(message != NULL);
|
||||
PFC_ASSERT(wndParent != NULL);
|
||||
|
||||
if (_tcschr(message, '\n') != nullptr) {
|
||||
m_tooltip.SetMaxTipWidth(rect.Width());
|
||||
}
|
||||
m_toolinfo.cbSize = sizeof(m_toolinfo);
|
||||
m_toolinfo.uFlags = TTF_TRACK | TTF_IDISHWND | TTF_ABSOLUTE | TTF_TRANSPARENT | TTF_CENTERTIP;
|
||||
m_toolinfo.hwnd = wndParent;
|
||||
m_toolinfo.uId = 0;
|
||||
m_toolinfo.lpszText = const_cast<TCHAR*>(message);
|
||||
m_toolinfo.hinst = NULL; //core_api::get_my_instance();
|
||||
if (m_tooltip.AddTool(&m_toolinfo)) {
|
||||
m_tooltip.TrackPosition(rect.CenterPoint().x, rect.bottom);
|
||||
m_tooltip.TrackActivate(&m_toolinfo, TRUE);
|
||||
}
|
||||
}
|
||||
void Initialize() {
|
||||
if (m_tooltip.m_hWnd == NULL) {
|
||||
WIN32_OP(m_tooltip.Create(NULL, NULL, NULL, m_style));
|
||||
}
|
||||
}
|
||||
CContainedWindowSimpleT<CToolTipCtrl> m_tooltip;
|
||||
TOOLINFO m_toolinfo;
|
||||
const DWORD m_style;
|
||||
bool m_shutDown;
|
||||
};
|
||||
|
||||
|
||||
template<typename T> class CDialogWithTooltip : public CDialogImpl<T> {
|
||||
public:
|
||||
BEGIN_MSG_MAP_EX(CDialogWithTooltip)
|
||||
MSG_WM_DESTROY(OnDestroy)
|
||||
END_MSG_MAP()
|
||||
|
||||
void ShowTip(UINT id, const TCHAR * label) {
|
||||
m_tip.Show(label, this->GetDlgItem(id));
|
||||
}
|
||||
void ShowTip(HWND child, const TCHAR * label) {
|
||||
m_tip.Show(label, child);
|
||||
}
|
||||
|
||||
void ShowTipF(UINT id, const TCHAR * label) {
|
||||
m_tip.ShowFocus(label, this->GetDlgItem(id));
|
||||
}
|
||||
void ShowTipF(HWND child, const TCHAR * label) {
|
||||
m_tip.ShowFocus(label, child);
|
||||
}
|
||||
void HideTip() { m_tip.Hide(); }
|
||||
private:
|
||||
void OnDestroy() { m_tip.ShutDown(); this->SetMsgHandled(FALSE); }
|
||||
CPopupTooltipMessage m_tip;
|
||||
};
|
||||
|
||||
91
libPPUI/CPowerRequest.cpp
Normal file
91
libPPUI/CPowerRequest.cpp
Normal file
@@ -0,0 +1,91 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "CPowerRequest.h"
|
||||
|
||||
#ifdef CPowerRequestAPI_Avail
|
||||
|
||||
|
||||
// win32 API declaration duplicate - not always defined on some of the Windows versions we target
|
||||
namespace winapi_substitute {
|
||||
|
||||
typedef struct _REASON_CONTEXT {
|
||||
ULONG Version;
|
||||
DWORD Flags;
|
||||
union {
|
||||
struct {
|
||||
HMODULE LocalizedReasonModule;
|
||||
ULONG LocalizedReasonId;
|
||||
ULONG ReasonStringCount;
|
||||
LPWSTR *ReasonStrings;
|
||||
|
||||
} Detailed;
|
||||
|
||||
LPWSTR SimpleReasonString;
|
||||
} Reason;
|
||||
} REASON_CONTEXT, *PREASON_CONTEXT;
|
||||
|
||||
//
|
||||
// Power Request APIs
|
||||
//
|
||||
|
||||
typedef REASON_CONTEXT POWER_REQUEST_CONTEXT, *PPOWER_REQUEST_CONTEXT, *LPPOWER_REQUEST_CONTEXT;
|
||||
|
||||
}
|
||||
|
||||
HANDLE CPowerRequestAPI::PowerCreateRequestNamed( const wchar_t * str ) {
|
||||
winapi_substitute::REASON_CONTEXT ctx = {POWER_REQUEST_CONTEXT_VERSION, POWER_REQUEST_CONTEXT_SIMPLE_STRING};
|
||||
ctx.Reason.SimpleReasonString = const_cast<wchar_t*>(str);
|
||||
return this->PowerCreateRequest(&ctx);
|
||||
}
|
||||
|
||||
CPowerRequest::CPowerRequest(const wchar_t * Reason) : m_Request(INVALID_HANDLE_VALUE), m_bSystem(), m_bDisplay() {
|
||||
if (m_API.IsValid()) {
|
||||
winapi_substitute::REASON_CONTEXT ctx = {POWER_REQUEST_CONTEXT_VERSION, POWER_REQUEST_CONTEXT_SIMPLE_STRING};
|
||||
ctx.Reason.SimpleReasonString = const_cast<wchar_t*>(Reason);
|
||||
m_Request = m_API.PowerCreateRequest(&ctx);
|
||||
}
|
||||
}
|
||||
|
||||
void CPowerRequest::SetSystem(bool bSystem) {
|
||||
if (bSystem == m_bSystem) return;
|
||||
m_bSystem = bSystem;
|
||||
if (m_Request != INVALID_HANDLE_VALUE) {
|
||||
m_API.ToggleSystem( m_Request, bSystem );
|
||||
} else {
|
||||
_UpdateTES();
|
||||
}
|
||||
}
|
||||
|
||||
void CPowerRequest::SetExecution(bool bExecution) {
|
||||
if (bExecution == m_bSystem) return;
|
||||
m_bSystem = bExecution;
|
||||
if (m_Request != INVALID_HANDLE_VALUE) {
|
||||
m_API.ToggleExecution( m_Request, bExecution );
|
||||
} else {
|
||||
_UpdateTES();
|
||||
}
|
||||
}
|
||||
|
||||
void CPowerRequest::SetDisplay(bool bDisplay) {
|
||||
if (bDisplay == m_bDisplay) return;
|
||||
m_bDisplay = bDisplay;
|
||||
if (m_Request != INVALID_HANDLE_VALUE) {
|
||||
m_API.ToggleDisplay(m_Request, bDisplay);
|
||||
} else {
|
||||
_UpdateTES();
|
||||
}
|
||||
}
|
||||
|
||||
CPowerRequest::~CPowerRequest() {
|
||||
if (m_Request != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(m_Request);
|
||||
} else {
|
||||
if (m_bDisplay || m_bSystem) SetThreadExecutionState(ES_CONTINUOUS);
|
||||
}
|
||||
}
|
||||
|
||||
void CPowerRequest::_UpdateTES() {
|
||||
SetThreadExecutionState(ES_CONTINUOUS | (m_bSystem ? ES_SYSTEM_REQUIRED : 0 ) | (m_bDisplay ? ES_DISPLAY_REQUIRED : 0) );
|
||||
}
|
||||
#endif // _WIN32
|
||||
|
||||
115
libPPUI/CPowerRequest.h
Normal file
115
libPPUI/CPowerRequest.h
Normal file
@@ -0,0 +1,115 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#ifdef WINAPI_FAMILY_PARTITION
|
||||
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||
#define CPowerRequestAPI_Avail
|
||||
#endif
|
||||
#else // no WINAPI_FAMILY_PARTITION, desktop SDK
|
||||
#define CPowerRequestAPI_Avail
|
||||
#endif
|
||||
|
||||
#endif // _WIN32
|
||||
|
||||
#ifdef CPowerRequestAPI_Avail
|
||||
|
||||
typedef HANDLE (WINAPI * pPowerCreateRequest_t) (
|
||||
__in void* Context
|
||||
);
|
||||
|
||||
typedef BOOL (WINAPI * pPowerSetRequest_t) (
|
||||
__in HANDLE PowerRequest,
|
||||
__in POWER_REQUEST_TYPE RequestType
|
||||
);
|
||||
|
||||
typedef BOOL (WINAPI * pPowerClearRequest_t) (
|
||||
__in HANDLE PowerRequest,
|
||||
__in POWER_REQUEST_TYPE RequestType
|
||||
);
|
||||
|
||||
class CPowerRequestAPI {
|
||||
public:
|
||||
CPowerRequestAPI() : PowerCreateRequest(), PowerSetRequest(), PowerClearRequest() {
|
||||
Bind();
|
||||
}
|
||||
bool Bind() {
|
||||
HMODULE kernel32 = GetModuleHandle(_T("kernel32.dll"));
|
||||
return Bind(PowerCreateRequest, kernel32, "PowerCreateRequest")
|
||||
&& Bind(PowerSetRequest, kernel32, "PowerSetRequest")
|
||||
&& Bind(PowerClearRequest, kernel32, "PowerClearRequest") ;
|
||||
}
|
||||
bool IsValid() {return PowerCreateRequest != NULL;}
|
||||
|
||||
void ToggleSystem(HANDLE hRequest, bool bSystem) {
|
||||
Toggle(hRequest, bSystem, PowerRequestSystemRequired);
|
||||
}
|
||||
|
||||
void ToggleExecution(HANDLE hRequest, bool bSystem) {
|
||||
const POWER_REQUEST_TYPE _PowerRequestExecutionRequired = (POWER_REQUEST_TYPE)3;
|
||||
const POWER_REQUEST_TYPE RequestType = IsWin8() ? _PowerRequestExecutionRequired : PowerRequestSystemRequired;
|
||||
Toggle(hRequest, bSystem, RequestType);
|
||||
}
|
||||
|
||||
void ToggleDisplay(HANDLE hRequest, bool bDisplay) {
|
||||
Toggle(hRequest, bDisplay, PowerRequestDisplayRequired);
|
||||
}
|
||||
|
||||
void Toggle(HANDLE hRequest, bool bToggle, POWER_REQUEST_TYPE what) {
|
||||
if (bToggle) {
|
||||
PowerSetRequest(hRequest, what);
|
||||
} else {
|
||||
PowerClearRequest(hRequest, what);
|
||||
}
|
||||
|
||||
}
|
||||
HANDLE PowerCreateRequestNamed( const wchar_t * str );
|
||||
|
||||
static bool IsWin8() {
|
||||
auto ver = myGetOSVersion();
|
||||
return ver >= 0x602;
|
||||
}
|
||||
static WORD myGetOSVersion() {
|
||||
const DWORD ver = GetVersion();
|
||||
return (WORD)HIBYTE(LOWORD(ver)) | ((WORD)LOBYTE(LOWORD(ver)) << 8);
|
||||
}
|
||||
|
||||
pPowerCreateRequest_t PowerCreateRequest;
|
||||
pPowerSetRequest_t PowerSetRequest;
|
||||
pPowerClearRequest_t PowerClearRequest;
|
||||
private:
|
||||
template<typename func_t> static bool Bind(func_t & f, HMODULE dll, const char * name) {
|
||||
f = reinterpret_cast<func_t>(GetProcAddress(dll, name));
|
||||
return f != NULL;
|
||||
}
|
||||
};
|
||||
|
||||
class CPowerRequest {
|
||||
public:
|
||||
CPowerRequest(const wchar_t * Reason);
|
||||
void SetSystem(bool bSystem);
|
||||
void SetExecution(bool bExecution);
|
||||
void SetDisplay(bool bDisplay);
|
||||
~CPowerRequest();
|
||||
private:
|
||||
void _UpdateTES();
|
||||
HANDLE m_Request;
|
||||
bool m_bSystem, m_bDisplay;
|
||||
CPowerRequestAPI m_API;
|
||||
CPowerRequest(const CPowerRequest&);
|
||||
void operator=(const CPowerRequest&);
|
||||
};
|
||||
#else
|
||||
|
||||
class CPowerRequest {
|
||||
public:
|
||||
CPowerRequest(const wchar_t * Reason) {}
|
||||
void SetSystem(bool bSystem) {}
|
||||
void SetExecution(bool bExecution) {}
|
||||
void SetDisplay(bool bDisplay) {}
|
||||
private:
|
||||
CPowerRequest(const CPowerRequest&);
|
||||
void operator=(const CPowerRequest&);
|
||||
};
|
||||
|
||||
#endif // CPowerRequestAPI_Avail
|
||||
54
libPPUI/CPropVariant.h
Normal file
54
libPPUI/CPropVariant.h
Normal file
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
class CPropVariant : public PROPVARIANT {
|
||||
public:
|
||||
CPropVariant() {init();}
|
||||
~CPropVariant() {clear();}
|
||||
CPropVariant( const CPropVariant & other ) {
|
||||
init();
|
||||
PropVariantCopy( this, &other );
|
||||
}
|
||||
const CPropVariant& operator=( const CPropVariant & other ) {
|
||||
clear();
|
||||
PropVariantCopy(this, &other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool toInt64(int64_t & out) const {
|
||||
switch( vt ) {
|
||||
case VT_I1: out = (int64_t) cVal; return true;
|
||||
case VT_I2: out = (int64_t) iVal; return true;
|
||||
case VT_I4: out = (int64_t) lVal; return true;
|
||||
case VT_I8: out = (int64_t) hVal.QuadPart; return true;
|
||||
case VT_INT: out = (int64_t) intVal; return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
bool toUint64(uint64_t & out) const {
|
||||
switch( vt ) {
|
||||
case VT_UI1: out = (uint64_t) bVal; return true;
|
||||
case VT_UI2: out = (uint64_t) uiVal; return true;
|
||||
case VT_UI4: out = (uint64_t) ulVal; return true;
|
||||
case VT_UI8: out = (uint64_t) uhVal.QuadPart; return true;
|
||||
case VT_UINT: out = (uint64_t) uintVal; return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
bool toString( pfc::string_base & out ) const {
|
||||
switch( vt ) {
|
||||
case VT_LPSTR:
|
||||
out = pfc::stringcvt::string_utf8_from_ansi( pszVal ); return true;
|
||||
case VT_LPWSTR:
|
||||
out = pfc::stringcvt::string_utf8_from_wide( pwszVal ); return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
private:
|
||||
void clear() {
|
||||
PropVariantClear( this );
|
||||
}
|
||||
void init() {
|
||||
PROPVARIANT * pv = this;
|
||||
PropVariantInit( pv );
|
||||
}
|
||||
};
|
||||
11
libPPUI/CWindowCreateAndDelete.h
Normal file
11
libPPUI/CWindowCreateAndDelete.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "win32_op.h"
|
||||
|
||||
template<typename TClass>
|
||||
class CWindowCreateAndDelete : public TClass {
|
||||
public:
|
||||
template<typename ... arg_t> CWindowCreateAndDelete(HWND parent, arg_t && ... arg) : TClass(std::forward<arg_t>(arg) ...) { WIN32_OP(this->Create(parent) != NULL); }
|
||||
private:
|
||||
void OnFinalMessage(HWND wnd) { PFC_ASSERT_NO_EXCEPTION(TClass::OnFinalMessage(wnd)); PFC_ASSERT_NO_EXCEPTION(delete this); }
|
||||
};
|
||||
50
libPPUI/Controls.cpp
Normal file
50
libPPUI/Controls.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
#include "stdafx.h"
|
||||
#include <vsstyle.h>
|
||||
#include "Controls.h"
|
||||
#include "PaintUtils.h"
|
||||
|
||||
void CStaticSeparator::OnPaint(CDCHandle) {
|
||||
PaintUtils::PaintSeparatorControl(*this);
|
||||
}
|
||||
|
||||
void CSeparator::OnPaint(CDCHandle dc) {
|
||||
PaintUtils::PaintSeparatorControl(*this);
|
||||
}
|
||||
|
||||
CStaticMainInstruction::CStaticMainInstruction() {
|
||||
SetThemePart(TEXT_MAININSTRUCTION);
|
||||
}
|
||||
|
||||
void CStaticThemed::OnPaint(CDCHandle) {
|
||||
if (m_fallback) {
|
||||
SetMsgHandled(FALSE); return;
|
||||
}
|
||||
if (m_theme == NULL) {
|
||||
m_theme.OpenThemeData(*this, L"TextStyle");
|
||||
if (m_theme == NULL) {
|
||||
m_fallback = true; SetMsgHandled(FALSE); return;
|
||||
}
|
||||
}
|
||||
CPaintDC dc(*this);
|
||||
TCHAR buffer[512] = {};
|
||||
GetWindowText(buffer, _countof(buffer));
|
||||
const int txLen = (int) pfc::strlen_max_t(buffer, _countof(buffer));
|
||||
CRect contentRect;
|
||||
WIN32_OP_D(GetClientRect(contentRect));
|
||||
SelectObjectScope scopeFont(dc, GetFont());
|
||||
dc.SetTextColor(GetSysColor(COLOR_WINDOWTEXT));
|
||||
dc.SetBkMode(TRANSPARENT);
|
||||
|
||||
if (txLen > 0) {
|
||||
CRect rcText(contentRect);
|
||||
DWORD flags = 0;
|
||||
DWORD style = GetStyle();
|
||||
if (style & SS_LEFT) flags |= DT_LEFT;
|
||||
else if (style & SS_RIGHT) flags |= DT_RIGHT;
|
||||
else if (style & SS_CENTER) flags |= DT_CENTER;
|
||||
if (style & SS_ENDELLIPSIS) flags |= DT_END_ELLIPSIS;
|
||||
|
||||
HRESULT retval = DrawThemeText(m_theme, dc, m_id, 0, buffer, txLen, flags, 0, rcText);
|
||||
PFC_ASSERT(SUCCEEDED(retval));
|
||||
}
|
||||
}
|
||||
101
libPPUI/Controls.h
Normal file
101
libPPUI/Controls.h
Normal file
@@ -0,0 +1,101 @@
|
||||
#pragma once
|
||||
|
||||
#pragma comment(lib, "uxtheme.lib")
|
||||
|
||||
#include "wtl-pp.h"
|
||||
#include "win32_op.h"
|
||||
|
||||
// Separator-in-dialog tool: subclass a static control on init
|
||||
class CStaticSeparator : public CContainedWindowT<CStatic>, private CMessageMap {
|
||||
public:
|
||||
CStaticSeparator() : CContainedWindowT<CStatic>(this, 0) {}
|
||||
BEGIN_MSG_MAP_EX(CSeparator)
|
||||
MSG_WM_PAINT(OnPaint)
|
||||
MSG_WM_SETTEXT(OnSetText)
|
||||
END_MSG_MAP()
|
||||
private:
|
||||
int OnSetText(LPCTSTR lpstrText) {
|
||||
Invalidate();
|
||||
SetMsgHandled(FALSE);
|
||||
return 0;
|
||||
}
|
||||
void OnPaint(CDCHandle);
|
||||
};
|
||||
|
||||
// CWindowRegistered with font & text functionality, for creating custom text label classes
|
||||
template<typename TClass>
|
||||
class CTextControl : public CWindowRegisteredT<TClass> {
|
||||
public:
|
||||
BEGIN_MSG_MAP_EX(CTextControl)
|
||||
MSG_WM_SETFONT(OnSetFont)
|
||||
MSG_WM_GETFONT(OnGetFont)
|
||||
MSG_WM_SETTEXT(OnSetText)
|
||||
CHAIN_MSG_MAP(__super)
|
||||
END_MSG_MAP()
|
||||
private:
|
||||
HFONT OnGetFont() {
|
||||
return m_font;
|
||||
}
|
||||
void OnSetFont(HFONT font, BOOL bRedraw) {
|
||||
m_font = font;
|
||||
if (bRedraw) this->Invalidate();
|
||||
}
|
||||
int OnSetText(LPCTSTR lpstrText) {
|
||||
this->Invalidate();this->SetMsgHandled(FALSE); return 0;
|
||||
}
|
||||
CFontHandle m_font;
|
||||
};
|
||||
|
||||
|
||||
// Static control subclass with override for theme part used for rendering
|
||||
class CStaticThemed : public CContainedWindowT<CStatic>, private CMessageMap {
|
||||
public:
|
||||
CStaticThemed() : CContainedWindowT<CStatic>(this, 0), m_id(), m_fallback() {}
|
||||
BEGIN_MSG_MAP_EX(CStaticThemed)
|
||||
MSG_WM_PAINT(OnPaint)
|
||||
MSG_WM_THEMECHANGED(OnThemeChanged)
|
||||
MSG_WM_SETTEXT(OnSetText)
|
||||
END_MSG_MAP()
|
||||
|
||||
void SetThemePart(int id) {m_id = id; if (m_hWnd != NULL) Invalidate();}
|
||||
private:
|
||||
int OnSetText(LPCTSTR lpstrText) {
|
||||
Invalidate();
|
||||
SetMsgHandled(FALSE);
|
||||
return 0;
|
||||
}
|
||||
void OnThemeChanged() {
|
||||
m_theme.Release();
|
||||
m_fallback = false;
|
||||
}
|
||||
void OnPaint(CDCHandle);
|
||||
int m_id;
|
||||
CTheme m_theme;
|
||||
bool m_fallback;
|
||||
};
|
||||
|
||||
class CStaticMainInstruction : public CStaticThemed {
|
||||
public:
|
||||
CStaticMainInstruction();
|
||||
};
|
||||
|
||||
|
||||
|
||||
class CSeparator : public CTextControl<CSeparator> {
|
||||
public:
|
||||
BEGIN_MSG_MAP_EX(CSeparator)
|
||||
MSG_WM_PAINT(OnPaint)
|
||||
MSG_WM_ENABLE(OnEnable)
|
||||
CHAIN_MSG_MAP(__super)
|
||||
END_MSG_MAP()
|
||||
|
||||
static const TCHAR * GetClassName() {
|
||||
return _T("foobar2000:separator");
|
||||
}
|
||||
private:
|
||||
void OnEnable(BOOL bEnable) {
|
||||
Invalidate();
|
||||
}
|
||||
void OnPaint(CDCHandle dc);
|
||||
};
|
||||
|
||||
261
libPPUI/GDIUtils.h
Normal file
261
libPPUI/GDIUtils.h
Normal file
@@ -0,0 +1,261 @@
|
||||
#pragma once
|
||||
#include "win32_op.h"
|
||||
|
||||
static HBITMAP CreateDIB24(CSize size) {
|
||||
struct {
|
||||
BITMAPINFOHEADER bmi;
|
||||
} bi = {};
|
||||
bi.bmi.biSize = sizeof(bi.bmi);
|
||||
bi.bmi.biWidth = size.cx;
|
||||
bi.bmi.biHeight = size.cy;
|
||||
bi.bmi.biPlanes = 1;
|
||||
bi.bmi.biBitCount = 24;
|
||||
bi.bmi.biCompression = BI_RGB;
|
||||
void * bitsPtr;
|
||||
return CreateDIBSection(NULL, reinterpret_cast<const BITMAPINFO*>(&bi), DIB_RGB_COLORS,&bitsPtr,0,0);
|
||||
}
|
||||
|
||||
static HBITMAP CreateDIB16(CSize size) {
|
||||
struct {
|
||||
BITMAPINFOHEADER bmi;
|
||||
} bi = {};
|
||||
bi.bmi.biSize = sizeof(bi.bmi);
|
||||
bi.bmi.biWidth = size.cx;
|
||||
bi.bmi.biHeight = size.cy;
|
||||
bi.bmi.biPlanes = 1;
|
||||
bi.bmi.biBitCount = 16;
|
||||
bi.bmi.biCompression = BI_RGB;
|
||||
void * bitsPtr;
|
||||
return CreateDIBSection(NULL, reinterpret_cast<const BITMAPINFO*>(&bi), DIB_RGB_COLORS,&bitsPtr,0,0);
|
||||
}
|
||||
|
||||
static HBITMAP CreateDIB8(CSize size, const COLORREF palette[256]) {
|
||||
struct {
|
||||
BITMAPINFOHEADER bmi;
|
||||
COLORREF colors[256];
|
||||
} bi = { };
|
||||
for(int c = 0; c < 256; ++c ) bi.colors[c] = palette[c];
|
||||
bi.bmi.biSize = sizeof(bi.bmi);
|
||||
bi.bmi.biWidth = size.cx;
|
||||
bi.bmi.biHeight = size.cy;
|
||||
bi.bmi.biPlanes = 1;
|
||||
bi.bmi.biBitCount = 8;
|
||||
bi.bmi.biCompression = BI_RGB;
|
||||
bi.bmi.biClrUsed = 256;
|
||||
void * bitsPtr;
|
||||
return CreateDIBSection(NULL, reinterpret_cast<const BITMAPINFO*>(&bi), DIB_RGB_COLORS,&bitsPtr,0,0);
|
||||
}
|
||||
|
||||
static void CreateScaledFont(CFont & out, CFontHandle in, double scale) {
|
||||
LOGFONT lf;
|
||||
WIN32_OP_D( in.GetLogFont(lf) );
|
||||
int temp = pfc::rint32(scale * lf.lfHeight);
|
||||
if (temp == 0) temp = pfc::sgn_t(lf.lfHeight);
|
||||
lf.lfHeight = temp;
|
||||
WIN32_OP_D( out.CreateFontIndirect(&lf) != NULL );
|
||||
}
|
||||
|
||||
static void CreateScaledFontEx(CFont & out, CFontHandle in, double scale, int weight) {
|
||||
LOGFONT lf;
|
||||
WIN32_OP_D( in.GetLogFont(lf) );
|
||||
int temp = pfc::rint32(scale * lf.lfHeight);
|
||||
if (temp == 0) temp = pfc::sgn_t(lf.lfHeight);
|
||||
lf.lfHeight = temp;
|
||||
lf.lfWeight = weight;
|
||||
WIN32_OP_D( out.CreateFontIndirect(&lf) != NULL );
|
||||
}
|
||||
|
||||
static void CreatePreferencesHeaderFont(CFont & out, CWindow source) {
|
||||
CreateScaledFontEx(out, source.GetFont(), 1.3, FW_BOLD);
|
||||
}
|
||||
|
||||
static void CreatePreferencesHeaderFont2(CFont & out, CWindow source) {
|
||||
CreateScaledFontEx(out, source.GetFont(), 1.1, FW_BOLD);
|
||||
}
|
||||
|
||||
template<typename TCtrl>
|
||||
class CAltFontCtrl : public TCtrl {
|
||||
public:
|
||||
void Initialize(CWindow wnd, double scale, int weight) {
|
||||
CreateScaledFontEx(m_font, wnd.GetFont(), scale, weight);
|
||||
_initWnd(wnd);
|
||||
}
|
||||
void MakeHeader(CWindow wnd) {
|
||||
CreatePreferencesHeaderFont(m_font, wnd);
|
||||
_initWnd(wnd);
|
||||
}
|
||||
void MakeHeader2(CWindow wnd) {
|
||||
CreatePreferencesHeaderFont2(m_font, wnd);
|
||||
_initWnd(wnd);
|
||||
}
|
||||
private:
|
||||
void _initWnd(CWindow wnd) {
|
||||
this->SubclassWindow(wnd); this->SetFont(m_font);
|
||||
}
|
||||
CFont m_font;
|
||||
};
|
||||
|
||||
class CFontScaled : public CFont {
|
||||
public:
|
||||
CFontScaled(HFONT _in, double scale) {
|
||||
CreateScaledFont(*this, _in, scale);
|
||||
}
|
||||
};
|
||||
|
||||
class DCClipRgnScope {
|
||||
public:
|
||||
DCClipRgnScope(HDC dc) : m_dc(dc) {
|
||||
m_dc.GetClipRgn(m_rgn);
|
||||
}
|
||||
~DCClipRgnScope() {
|
||||
m_dc.SelectClipRgn(m_rgn);
|
||||
}
|
||||
|
||||
HRGN OldVal() const throw() {return m_rgn;}
|
||||
|
||||
PFC_CLASS_NOT_COPYABLE_EX(DCClipRgnScope)
|
||||
private:
|
||||
CDCHandle m_dc;
|
||||
CRgn m_rgn;
|
||||
};
|
||||
|
||||
|
||||
static HBRUSH MakeTempBrush(HDC dc, COLORREF color) throw() {
|
||||
SetDCBrushColor(dc, color); return (HBRUSH) GetStockObject(DC_BRUSH);
|
||||
}
|
||||
|
||||
class CDCBrush : public CBrushHandle {
|
||||
public:
|
||||
CDCBrush(HDC dc, COLORREF color) throw() {
|
||||
m_dc = dc;
|
||||
m_oldCol = m_dc.SetDCBrushColor(color);
|
||||
m_hBrush = (HBRUSH) GetStockObject(DC_BRUSH);
|
||||
}
|
||||
~CDCBrush() throw() {
|
||||
m_dc.SetDCBrushColor(m_oldCol);
|
||||
}
|
||||
PFC_CLASS_NOT_COPYABLE_EX(CDCBrush)
|
||||
private:
|
||||
CDCHandle m_dc;
|
||||
COLORREF m_oldCol;
|
||||
};
|
||||
|
||||
class CDCPen : public CPenHandle {
|
||||
public:
|
||||
CDCPen(HDC dc, COLORREF color) throw() {
|
||||
m_dc = dc;
|
||||
m_oldCol = m_dc.SetDCPenColor(color);
|
||||
m_hPen = (HPEN) GetStockObject(DC_PEN);
|
||||
}
|
||||
~CDCPen() throw() {
|
||||
m_dc.SetDCPenColor(m_oldCol);
|
||||
}
|
||||
private:
|
||||
CDCHandle m_dc;
|
||||
COLORREF m_oldCol;
|
||||
};
|
||||
|
||||
|
||||
class CBackBuffer : public CDC {
|
||||
public:
|
||||
CBackBuffer() : m_bitmapOld(NULL), m_curSize(0,0) {
|
||||
CreateCompatibleDC(NULL);
|
||||
ATLASSERT(m_hDC != NULL);
|
||||
}
|
||||
~CBackBuffer() {
|
||||
Dispose();
|
||||
}
|
||||
void Attach(HBITMAP bmp, CSize size) {
|
||||
Dispose();
|
||||
m_bitmap.Attach(bmp); m_curSize = size;
|
||||
m_bitmapOld = SelectBitmap(m_bitmap);
|
||||
}
|
||||
void Attach(HBITMAP bmp) {
|
||||
CSize size;
|
||||
bool state = CBitmapHandle(bmp).GetSize(size);
|
||||
ATLASSERT(state);
|
||||
Attach(bmp, size);
|
||||
}
|
||||
BOOL Allocate(CSize size, HDC dcCompatible = NULL) {
|
||||
if (m_hDC == NULL) return FALSE;
|
||||
if (m_curSize == size) return TRUE;
|
||||
Dispose();
|
||||
HBITMAP temp;
|
||||
if (dcCompatible == NULL) {
|
||||
temp = CreateDIB24(size);
|
||||
} else {
|
||||
temp = CreateCompatibleBitmap(dcCompatible, size.cx, size.cy);
|
||||
}
|
||||
if (temp == NULL) return FALSE;
|
||||
Attach(temp);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void Dispose() {
|
||||
if (m_bitmap != NULL) {
|
||||
SelectBitmap(m_bitmapOld); m_bitmapOld = NULL;
|
||||
m_bitmap.DeleteObject();
|
||||
}
|
||||
m_curSize = CSize(0,0);
|
||||
}
|
||||
BOOL GetBitmapPtr(t_uint8 * & ptr, t_ssize & lineWidth) {
|
||||
if (m_bitmap == NULL) return FALSE;
|
||||
BITMAP bmp = {};
|
||||
if (!m_bitmap.GetBitmap(bmp)) return FALSE;
|
||||
lineWidth = bmp.bmWidthBytes;
|
||||
ptr = reinterpret_cast<t_uint8*>(bmp.bmBits);
|
||||
return TRUE;
|
||||
}
|
||||
CSize GetSize() const { return m_curSize; }
|
||||
|
||||
PFC_CLASS_NOT_COPYABLE_EX(CBackBuffer)
|
||||
private:
|
||||
CSize m_curSize;
|
||||
CBitmap m_bitmap;
|
||||
HBITMAP m_bitmapOld;
|
||||
};
|
||||
|
||||
class CBackBufferScope : public CDCHandle {
|
||||
public:
|
||||
CBackBufferScope(HDC hDC, HDC hDCBB, const CRect & rcPaint) : CDCHandle(hDCBB), m_dcOrig(hDC), m_rcPaint(rcPaint)
|
||||
{
|
||||
GetClipRgn(m_clipRgnOld);
|
||||
CRgn temp;
|
||||
if (m_dcOrig.GetClipRgn(temp) == 1) {
|
||||
if (m_clipRgnOld != NULL) temp.CombineRgn(m_clipRgnOld,RGN_AND);
|
||||
SelectClipRgn(temp);
|
||||
}
|
||||
IntersectClipRect(rcPaint);
|
||||
}
|
||||
|
||||
~CBackBufferScope()
|
||||
{
|
||||
m_dcOrig.BitBlt(m_rcPaint.left,m_rcPaint.top,m_rcPaint.Width(),m_rcPaint.Height(),m_hDC,m_rcPaint.left,m_rcPaint.top,SRCCOPY);
|
||||
SelectClipRgn(m_clipRgnOld);
|
||||
}
|
||||
private:
|
||||
const CRect m_rcPaint;
|
||||
CDCHandle m_dcOrig;
|
||||
CRgn m_clipRgnOld;
|
||||
};
|
||||
|
||||
class SetTextColorScope {
|
||||
public:
|
||||
SetTextColorScope(HDC dc, COLORREF col) throw() : m_dc(dc) {
|
||||
m_oldCol = SetTextColor(dc, col);
|
||||
}
|
||||
~SetTextColorScope() throw() {
|
||||
SetTextColor(m_dc, m_oldCol);
|
||||
}
|
||||
PFC_CLASS_NOT_COPYABLE_EX(SetTextColorScope)
|
||||
private:
|
||||
HDC m_dc;
|
||||
COLORREF m_oldCol;
|
||||
};
|
||||
|
||||
static CSize GetBitmapSize( HBITMAP bmp ) {
|
||||
CBitmapHandle h ( bmp );
|
||||
BITMAP bm = {};
|
||||
WIN32_OP_D( h.GetBitmap(bm) );
|
||||
return CSize(bm.bmWidth, bm.bmHeight);
|
||||
}
|
||||
BIN
libPPUI/IDI_SCROLL.ico
Normal file
BIN
libPPUI/IDI_SCROLL.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 766 B |
3
libPPUI/IDI_SCROLL.txt
Normal file
3
libPPUI/IDI_SCROLL.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
Icon needed by CMiddleDragImpl and things that use it - such as CListControl.
|
||||
Include IDI_SCROLL.ico in your resources, with identifier set to IDI_SCROLL.
|
||||
To get libPPUI to recognize your resource, include your resource.h before libPPUI headers in at least one of your source files.
|
||||
187
libPPUI/IDataObjectUtils.cpp
Normal file
187
libPPUI/IDataObjectUtils.cpp
Normal file
@@ -0,0 +1,187 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "IDataObjectUtils.h"
|
||||
|
||||
HRESULT IDataObjectUtils::DataBlockToSTGMEDIUM(const void * blockPtr, t_size blockSize, STGMEDIUM * medium, DWORD tymed, bool bHere) throw() {
|
||||
try {
|
||||
if (bHere) {
|
||||
switch(tymed) {
|
||||
case TYMED_ISTREAM:
|
||||
{
|
||||
if (medium->pstm == NULL) return E_INVALIDARG;
|
||||
ULONG written = 0;
|
||||
HRESULT state;
|
||||
state = medium->pstm->Write(blockPtr, pfc::downcast_guarded<ULONG>(blockSize),&written);
|
||||
if (FAILED(state)) return state;
|
||||
if (written != blockSize) return STG_E_MEDIUMFULL;
|
||||
return S_OK;
|
||||
}
|
||||
default:
|
||||
return DV_E_TYMED;
|
||||
}
|
||||
} else {
|
||||
if (tymed & TYMED_HGLOBAL) {
|
||||
HGLOBAL hMem = HGlobalFromMemblock(blockPtr, blockSize);
|
||||
if (hMem == NULL) return E_OUTOFMEMORY;
|
||||
medium->tymed = TYMED_HGLOBAL;
|
||||
medium->hGlobal = hMem;
|
||||
medium->pUnkForRelease = NULL;
|
||||
return S_OK;
|
||||
}
|
||||
if (tymed & TYMED_ISTREAM) {
|
||||
HRESULT state;
|
||||
HGLOBAL hMem = HGlobalFromMemblock(blockPtr, blockSize);
|
||||
if (hMem == NULL) return E_OUTOFMEMORY;
|
||||
medium->tymed = TYMED_ISTREAM;
|
||||
pfc::com_ptr_t<IStream> stream;
|
||||
if (FAILED( state = CreateStreamOnHGlobal(hMem,TRUE,stream.receive_ptr()) ) ) {
|
||||
GlobalFree(hMem);
|
||||
return state;
|
||||
}
|
||||
{
|
||||
LARGE_INTEGER wtf = {};
|
||||
if (FAILED( state = stream->Seek(wtf,STREAM_SEEK_END,NULL) ) ) {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
medium->pstm = stream.detach();
|
||||
medium->pUnkForRelease = NULL;
|
||||
return S_OK;
|
||||
}
|
||||
return DV_E_TYMED;
|
||||
}
|
||||
} catch(pfc::exception_not_implemented) {
|
||||
return E_NOTIMPL;
|
||||
} catch(std::bad_alloc) {
|
||||
return E_OUTOFMEMORY;
|
||||
} catch(...) {
|
||||
return E_UNEXPECTED;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
HGLOBAL IDataObjectUtils::HGlobalFromMemblock(const void * ptr,t_size size) {
|
||||
HGLOBAL handle = GlobalAlloc(GMEM_MOVEABLE,size);
|
||||
if (handle != NULL) {
|
||||
void * destptr = GlobalLock(handle);
|
||||
if (destptr == NULL) {
|
||||
GlobalFree(handle);
|
||||
handle = NULL;
|
||||
} else {
|
||||
memcpy(destptr,ptr,size);
|
||||
GlobalUnlock(handle);
|
||||
}
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
|
||||
HRESULT IDataObjectUtils::ExtractDataObjectContent(pfc::com_ptr_t<IDataObject> obj, UINT format, DWORD aspect, LONG index, pfc::array_t<t_uint8> & out) {
|
||||
FORMATETC fmt = {};
|
||||
fmt.cfFormat = format; fmt.dwAspect = aspect; fmt.lindex = index;
|
||||
fmt.tymed = TYMED_HGLOBAL /* | TYMED_ISTREAM*/;
|
||||
|
||||
STGMEDIUM med = {};
|
||||
HRESULT state;
|
||||
if (FAILED( state = obj->GetData(&fmt,&med) ) ) return state;
|
||||
ReleaseStgMediumScope relScope(&med);
|
||||
return STGMEDIUMToDataBlock(med, out);
|
||||
}
|
||||
|
||||
HRESULT IDataObjectUtils::STGMEDIUMToDataBlock(const STGMEDIUM & med, pfc::array_t<t_uint8> & out) {
|
||||
switch(med.tymed) {
|
||||
case TYMED_HGLOBAL:
|
||||
{
|
||||
CGlobalLockScope lock(med.hGlobal);
|
||||
out.set_data_fromptr( (const t_uint8*) lock.GetPtr(), lock.GetSize() );
|
||||
}
|
||||
return S_OK;
|
||||
case TYMED_ISTREAM:
|
||||
{
|
||||
HRESULT state;
|
||||
IStream * stream = med.pstm;
|
||||
LARGE_INTEGER offset = {};
|
||||
STATSTG stats = {};
|
||||
if (FAILED( state = stream->Stat(&stats,STATFLAG_NONAME ) ) ) return state;
|
||||
t_size toRead = pfc::downcast_guarded<t_size>(stats.cbSize.QuadPart);
|
||||
out.set_size(toRead);
|
||||
if (FAILED( state = stream->Seek(offset,STREAM_SEEK_SET,NULL) ) ) return state;
|
||||
ULONG cbRead = 0;
|
||||
if (FAILED( state = stream->Read(out.get_ptr(), pfc::downcast_guarded<ULONG>(toRead), &cbRead) ) ) return state;
|
||||
if (cbRead != toRead) return E_UNEXPECTED;
|
||||
}
|
||||
return S_OK;
|
||||
default:
|
||||
return DV_E_TYMED;
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT IDataObjectUtils::ExtractDataObjectContent(pfc::com_ptr_t<IDataObject> obj, UINT format, pfc::array_t<t_uint8> & out) {
|
||||
return ExtractDataObjectContent(obj, format, DVASPECT_CONTENT, -1, out);
|
||||
}
|
||||
|
||||
HRESULT IDataObjectUtils::ExtractDataObjectContentTest(pfc::com_ptr_t<IDataObject> obj, UINT format, DWORD aspect, LONG index) {
|
||||
FORMATETC fmt = {};
|
||||
fmt.cfFormat = format; fmt.dwAspect = aspect; fmt.lindex = index;
|
||||
for(t_uint32 walk = 0; walk < 32; ++walk) {
|
||||
const DWORD tymed = 1 << walk;
|
||||
if ((ExtractDataObjectContent_SupportedTymeds & tymed) != 0) {
|
||||
fmt.tymed = tymed;
|
||||
HRESULT state = obj->QueryGetData(&fmt);
|
||||
if (SUCCEEDED(state)) {
|
||||
if (state == S_OK) return S_OK;
|
||||
} else {
|
||||
if (state != DV_E_TYMED) return state;
|
||||
}
|
||||
}
|
||||
}
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
HRESULT IDataObjectUtils::ExtractDataObjectContentTest(pfc::com_ptr_t<IDataObject> obj, UINT format) {
|
||||
return ExtractDataObjectContentTest(obj,format,DVASPECT_CONTENT,-1);
|
||||
}
|
||||
|
||||
HRESULT IDataObjectUtils::ExtractDataObjectString(pfc::com_ptr_t<IDataObject> obj, pfc::string_base & out) {
|
||||
pfc::array_t<t_uint8> data;
|
||||
HRESULT state;
|
||||
state = ExtractDataObjectContent(obj,CF_UNICODETEXT,data);
|
||||
if (SUCCEEDED(state)) {
|
||||
out = pfc::stringcvt::string_utf8_from_os_ex( (const wchar_t*) data.get_ptr(), data.get_size() / sizeof(wchar_t) );
|
||||
return S_OK;
|
||||
}
|
||||
state = ExtractDataObjectContent(obj,CF_TEXT,data);
|
||||
if (SUCCEEDED(state)) {
|
||||
out = pfc::stringcvt::string_utf8_from_os_ex( (const char*) data.get_ptr(), data.get_size() / sizeof(char) );
|
||||
return S_OK;
|
||||
}
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
HRESULT IDataObjectUtils::SetDataObjectContent(pfc::com_ptr_t<IDataObject> obj, UINT format, DWORD aspect, LONG index, const void * data, t_size dataSize) {
|
||||
STGMEDIUM med = {};
|
||||
FORMATETC fmt = {};
|
||||
fmt.cfFormat = format; fmt.dwAspect = aspect; fmt.lindex = index; fmt.tymed = TYMED_HGLOBAL;
|
||||
HRESULT state;
|
||||
if (FAILED(state = DataBlockToSTGMEDIUM(data,dataSize,&med,TYMED_HGLOBAL,false))) return state;
|
||||
return obj->SetData(&fmt,&med,TRUE);
|
||||
}
|
||||
|
||||
HRESULT IDataObjectUtils::SetDataObjectString(pfc::com_ptr_t<IDataObject> obj, const char * str) {
|
||||
pfc::stringcvt::string_wide_from_utf8 s(str);
|
||||
return SetDataObjectContent(obj,CF_UNICODETEXT,DVASPECT_CONTENT,-1,s.get_ptr(), (s.length()+1) * sizeof(s[0]));
|
||||
}
|
||||
|
||||
HRESULT IDataObjectUtils::ExtractDataObjectDWORD(pfc::com_ptr_t<IDataObject> obj, UINT format, DWORD & val) {
|
||||
HRESULT state;
|
||||
pfc::array_t<t_uint8> buffer;
|
||||
if (FAILED( state = ExtractDataObjectContent(obj, format, DVASPECT_CONTENT, -1, buffer) ) ) return state;
|
||||
if (buffer.get_size() < sizeof(val)) return E_UNEXPECTED;
|
||||
val = *(DWORD*) buffer.get_ptr();
|
||||
return S_OK;
|
||||
}
|
||||
HRESULT IDataObjectUtils::SetDataObjectDWORD(pfc::com_ptr_t<IDataObject> obj, UINT format, DWORD val) {
|
||||
return SetDataObjectContent(obj,format,DVASPECT_CONTENT,-1,&val,sizeof(val));
|
||||
}
|
||||
HRESULT IDataObjectUtils::PasteSucceeded(pfc::com_ptr_t<IDataObject> obj, DWORD effect) {
|
||||
return SetDataObjectDWORD(obj, RegisterClipboardFormat(CFSTR_PASTESUCCEEDED), effect);
|
||||
}
|
||||
228
libPPUI/IDataObjectUtils.h
Normal file
228
libPPUI/IDataObjectUtils.h
Normal file
@@ -0,0 +1,228 @@
|
||||
#pragma once
|
||||
|
||||
#include <shlobj.h>
|
||||
#include <ShlDisp.h> // IAsyncOperation
|
||||
#include "pp-COM-macros.h"
|
||||
|
||||
namespace IDataObjectUtils {
|
||||
|
||||
class ReleaseStgMediumScope {
|
||||
public:
|
||||
ReleaseStgMediumScope(STGMEDIUM * medium) : m_medium(medium) {}
|
||||
~ReleaseStgMediumScope() {if (m_medium != NULL) ReleaseStgMedium(m_medium);}
|
||||
private:
|
||||
STGMEDIUM * m_medium;
|
||||
|
||||
PFC_CLASS_NOT_COPYABLE_EX(ReleaseStgMediumScope)
|
||||
};
|
||||
|
||||
static const DWORD DataBlockToSTGMEDIUM_SupportedTymeds = TYMED_ISTREAM | TYMED_HGLOBAL;
|
||||
static const DWORD ExtractDataObjectContent_SupportedTymeds = TYMED_ISTREAM | TYMED_HGLOBAL;
|
||||
|
||||
HRESULT DataBlockToSTGMEDIUM(const void * blockPtr, t_size blockSize, STGMEDIUM * medium, DWORD tymed, bool bHere) throw();
|
||||
|
||||
HGLOBAL HGlobalFromMemblock(const void * ptr,t_size size);
|
||||
|
||||
HRESULT ExtractDataObjectContent(pfc::com_ptr_t<IDataObject> obj, UINT format, DWORD aspect, LONG index, pfc::array_t<t_uint8> & out);
|
||||
HRESULT ExtractDataObjectContent(pfc::com_ptr_t<IDataObject> obj, UINT format, pfc::array_t<t_uint8> & out);
|
||||
|
||||
HRESULT ExtractDataObjectContentTest(pfc::com_ptr_t<IDataObject> obj, UINT format, DWORD aspect, LONG index);
|
||||
HRESULT ExtractDataObjectContentTest(pfc::com_ptr_t<IDataObject> obj, UINT format);
|
||||
|
||||
HRESULT ExtractDataObjectString(pfc::com_ptr_t<IDataObject> obj, pfc::string_base & out);
|
||||
HRESULT SetDataObjectString(pfc::com_ptr_t<IDataObject> obj, const char * str);
|
||||
|
||||
HRESULT SetDataObjectContent(pfc::com_ptr_t<IDataObject> obj, UINT format, DWORD aspect, LONG index, const void * data, t_size dataSize);
|
||||
|
||||
HRESULT STGMEDIUMToDataBlock(const STGMEDIUM & med, pfc::array_t<t_uint8> & out);
|
||||
|
||||
HRESULT ExtractDataObjectDWORD(pfc::com_ptr_t<IDataObject> obj, UINT format, DWORD & val);
|
||||
HRESULT SetDataObjectDWORD(pfc::com_ptr_t<IDataObject> obj, UINT format, DWORD val);
|
||||
|
||||
HRESULT PasteSucceeded(pfc::com_ptr_t<IDataObject> obj, DWORD effect);
|
||||
|
||||
class comparator_FORMATETC {
|
||||
public:
|
||||
static int compare(const FORMATETC & v1, const FORMATETC & v2) {
|
||||
int val;
|
||||
val = pfc::compare_t(v1.cfFormat,v2.cfFormat); if (val != 0) return val;
|
||||
val = pfc::compare_t(v1.dwAspect,v2.dwAspect); if (val != 0) return val;
|
||||
val = pfc::compare_t(v1.lindex, v2.lindex ); if (val != 0) return val;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
class CDataObjectBase : public IDataObject {
|
||||
public:
|
||||
COM_QI_SIMPLE(IDataObject)
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetData(FORMATETC * formatetc, STGMEDIUM * medium) override {
|
||||
return GetData_internal(formatetc,medium,false);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetDataHere(FORMATETC * formatetc, STGMEDIUM * medium) override {
|
||||
return GetData_internal(formatetc,medium,true);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE QueryGetData(FORMATETC * formatetc) override {
|
||||
if (formatetc == NULL) return E_INVALIDARG;
|
||||
|
||||
if ((DataBlockToSTGMEDIUM_SupportedTymeds & formatetc->tymed) == 0) return DV_E_TYMED;
|
||||
|
||||
try {
|
||||
return RenderDataTest(formatetc->cfFormat,formatetc->dwAspect,formatetc->lindex);
|
||||
} PP_COM_CATCH;
|
||||
}
|
||||
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetCanonicalFormatEtc(FORMATETC * in, FORMATETC * out) override {
|
||||
//check this again
|
||||
if (in == NULL || out == NULL)
|
||||
return E_INVALIDARG;
|
||||
*out = *in;
|
||||
return DATA_S_SAMEFORMATETC;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE EnumFormatEtc(DWORD dwDirection,IEnumFORMATETC ** ppenumFormatetc) override {
|
||||
if (dwDirection == DATADIR_GET) {
|
||||
if (ppenumFormatetc == NULL) return E_INVALIDARG;
|
||||
return CreateIEnumFORMATETC(ppenumFormatetc);
|
||||
} else if (dwDirection == DATADIR_SET) {
|
||||
return E_NOTIMPL;
|
||||
} else {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE SetData(FORMATETC * pFormatetc, STGMEDIUM * pmedium, BOOL fRelease) override {
|
||||
try {
|
||||
ReleaseStgMediumScope relScope(fRelease ? pmedium : NULL);
|
||||
if (pFormatetc == NULL || pmedium == NULL) return E_INVALIDARG;
|
||||
|
||||
/*TCHAR buf[256];
|
||||
if (GetClipboardFormatName(pFormatetc->cfFormat,buf,PFC_TABSIZE(buf)) > 0) {
|
||||
buf[PFC_TABSIZE(buf)-1] = 0;
|
||||
OutputDebugString(TEXT("SetData: ")); OutputDebugString(buf); OutputDebugString(TEXT("\n"));
|
||||
} else {
|
||||
OutputDebugString(TEXT("SetData: unknown clipboard format.\n"));
|
||||
}*/
|
||||
|
||||
pfc::array_t<t_uint8> temp;
|
||||
HRESULT state = STGMEDIUMToDataBlock(*pmedium,temp);
|
||||
if (FAILED(state)) return state;
|
||||
m_entries.set(*pFormatetc,temp);
|
||||
return S_OK;
|
||||
} PP_COM_CATCH;
|
||||
}
|
||||
HRESULT STDMETHODCALLTYPE DAdvise(FORMATETC * pFormatetc, DWORD advf, IAdviseSink * pAdvSink, DWORD * pdwConnection) override {return OLE_E_ADVISENOTSUPPORTED;}
|
||||
HRESULT STDMETHODCALLTYPE DUnadvise(DWORD dwConnection) override {return OLE_E_ADVISENOTSUPPORTED;}
|
||||
HRESULT STDMETHODCALLTYPE EnumDAdvise(IEnumSTATDATA ** ppenumAdvise) override {return OLE_E_ADVISENOTSUPPORTED;}
|
||||
protected:
|
||||
typedef pfc::array_t<uint8_t> data_t;
|
||||
virtual HRESULT RenderData(UINT format,DWORD aspect,LONG dataIndex, data_t & out) const {
|
||||
FORMATETC fmt = {};
|
||||
fmt.cfFormat = format; fmt.dwAspect = aspect; fmt.lindex = dataIndex;
|
||||
const pfc::array_t<t_uint8> * entry = m_entries.query_ptr(fmt);
|
||||
if (entry != NULL) {
|
||||
out = * entry;
|
||||
return S_OK;
|
||||
}
|
||||
return DV_E_FORMATETC;
|
||||
}
|
||||
virtual HRESULT RenderDataTest(UINT format,DWORD aspect,LONG dataIndex) const {
|
||||
FORMATETC fmt = {};
|
||||
fmt.cfFormat = format; fmt.dwAspect = aspect; fmt.lindex = dataIndex;
|
||||
if (m_entries.have_item(fmt)) return S_OK;
|
||||
return DV_E_FORMATETC;
|
||||
}
|
||||
typedef pfc::list_base_t<FORMATETC> TFormatList;
|
||||
|
||||
static void AddFormat(TFormatList & out,UINT code) {
|
||||
FORMATETC fmt = {};
|
||||
fmt.dwAspect = DVASPECT_CONTENT;
|
||||
fmt.lindex = -1;
|
||||
fmt.cfFormat = code;
|
||||
for(t_size medWalk = 0; medWalk < 32; ++medWalk) {
|
||||
const DWORD med = 1 << medWalk;
|
||||
if ((DataBlockToSTGMEDIUM_SupportedTymeds & med) != 0) {
|
||||
fmt.tymed = med;
|
||||
out.add_item(fmt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void EnumFormats(TFormatList & out) const {
|
||||
pfc::avltree_t<UINT> formats;
|
||||
for(auto walk = m_entries.cfirst(); walk.is_valid(); ++walk) {
|
||||
formats.add_item( walk->m_key.cfFormat );
|
||||
}
|
||||
for(auto walk = formats.cfirst(); walk.is_valid(); ++walk) {
|
||||
AddFormat(out, *walk);
|
||||
}
|
||||
}
|
||||
HRESULT CreateIEnumFORMATETC(IEnumFORMATETC ** outptr) const throw() {
|
||||
try {
|
||||
pfc::list_t<FORMATETC> out;
|
||||
EnumFormats(out);
|
||||
return SHCreateStdEnumFmtEtc((UINT)out.get_count(), out.get_ptr(), outptr);
|
||||
} PP_COM_CATCH;
|
||||
}
|
||||
private:
|
||||
HRESULT GetData_internal(FORMATETC * formatetc, STGMEDIUM * medium,bool bHere) {
|
||||
if (formatetc == NULL || medium == NULL) return E_INVALIDARG;
|
||||
|
||||
try {
|
||||
data_t out;
|
||||
HRESULT hr = RenderData(formatetc->cfFormat,formatetc->dwAspect,formatetc->lindex,out);
|
||||
if (FAILED(hr)) return hr;
|
||||
return DataBlockToSTGMEDIUM(out.get_ptr(),out.get_size(),medium,formatetc->tymed,bHere);
|
||||
} PP_COM_CATCH;
|
||||
}
|
||||
|
||||
typedef pfc::map_t<FORMATETC, pfc::array_t<t_uint8>, comparator_FORMATETC> t_entries;
|
||||
t_entries m_entries;
|
||||
};
|
||||
|
||||
#ifdef __IDataObjectAsyncCapability_INTERFACE_DEFINED__
|
||||
typedef IDataObjectAsyncCapability IDataObjectAsyncCapability_t;
|
||||
#else
|
||||
typedef IAsyncOperation IDataObjectAsyncCapability_t;
|
||||
#endif
|
||||
|
||||
class CAsyncDataObjectBase : public CDataObjectBase, public IDataObjectAsyncCapability_t {
|
||||
BOOL m_inOperation = FALSE;
|
||||
BOOL m_asyncMode = TRUE;
|
||||
protected:
|
||||
COM_QI_BEGIN()
|
||||
COM_QI_CHAIN(CDataObjectBase)
|
||||
COM_QI_ENTRY(IDataObjectAsyncCapability_t)
|
||||
COM_QI_END()
|
||||
public:
|
||||
HRESULT STDMETHODCALLTYPE SetAsyncMode(BOOL fDoOpAsync) override {
|
||||
m_asyncMode = fDoOpAsync;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetAsyncMode(BOOL *pfIsOpAsync) override {
|
||||
if ( pfIsOpAsync == nullptr ) return E_INVALIDARG;
|
||||
*pfIsOpAsync = m_asyncMode;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE StartOperation(IBindCtx *pbcReserved) override {
|
||||
m_inOperation = TRUE;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE InOperation(BOOL *pfInAsyncOp) override {
|
||||
if ( pfInAsyncOp == nullptr ) return E_INVALIDARG;
|
||||
*pfInAsyncOp = m_inOperation;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE EndOperation(HRESULT hResult,IBindCtx *pbcReserved,DWORD dwEffects) override {
|
||||
m_inOperation = FALSE;
|
||||
return S_OK;
|
||||
}
|
||||
};
|
||||
}
|
||||
349
libPPUI/InPlaceCombo.cpp
Normal file
349
libPPUI/InPlaceCombo.cpp
Normal file
@@ -0,0 +1,349 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "InPlaceEdit.h"
|
||||
#include "wtl-pp.h"
|
||||
#include "win32_op.h"
|
||||
|
||||
#include "AutoComplete.h"
|
||||
#include "CWindowCreateAndDelete.h"
|
||||
#include "win32_utility.h"
|
||||
#include "listview_helper.h" // ListView_GetColumnCount
|
||||
#include "clipboard.h"
|
||||
|
||||
#include <forward_list>
|
||||
|
||||
#ifndef WM_MOUSEHWHEEL
|
||||
#define WM_MOUSEHWHEEL 0x20E
|
||||
#endif
|
||||
|
||||
using namespace InPlaceEdit;
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
enum {
|
||||
MSG_COMPLETION = WM_USER,
|
||||
MSG_DISABLE_EDITING
|
||||
};
|
||||
|
||||
|
||||
// Rationale: more than one HWND on the list is extremely uncommon, hence forward_list
|
||||
static std::forward_list<HWND> g_editboxes;
|
||||
static HHOOK g_hook = NULL /*, g_keyHook = NULL*/;
|
||||
|
||||
static void GAbortEditing(HWND edit, t_uint32 code) {
|
||||
CWindow parent = ::GetParent(edit);
|
||||
parent.SendMessage(MSG_DISABLE_EDITING);
|
||||
parent.PostMessage(MSG_COMPLETION, code, 0);
|
||||
}
|
||||
|
||||
static void GAbortEditing(t_uint32 code) {
|
||||
for (auto walk = g_editboxes.begin(); walk != g_editboxes.end(); ++walk) {
|
||||
GAbortEditing(*walk, code);
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsSamePopup(CWindow wnd1, CWindow wnd2) {
|
||||
return pfc::findOwningPopup(wnd1) == pfc::findOwningPopup(wnd2);
|
||||
}
|
||||
|
||||
static void MouseEventTest(HWND target, CPoint pt, bool isWheel) {
|
||||
for (auto walk = g_editboxes.begin(); walk != g_editboxes.end(); ++walk) {
|
||||
CWindow edit(*walk);
|
||||
bool cancel = false;
|
||||
if (target != edit && IsSamePopup(target, edit)) {
|
||||
cancel = true;
|
||||
} else if (isWheel) {
|
||||
CWindow target2 = WindowFromPoint(pt);
|
||||
if (target2 != edit && IsSamePopup(target2, edit)) {
|
||||
cancel = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (cancel) GAbortEditing(edit, KEditLostFocus);
|
||||
}
|
||||
}
|
||||
|
||||
static LRESULT CALLBACK GMouseProc(int nCode, WPARAM wParam, LPARAM lParam) {
|
||||
if (nCode == HC_ACTION) {
|
||||
const MOUSEHOOKSTRUCT * mhs = reinterpret_cast<const MOUSEHOOKSTRUCT *>(lParam);
|
||||
switch (wParam) {
|
||||
case WM_NCLBUTTONDOWN:
|
||||
case WM_NCRBUTTONDOWN:
|
||||
case WM_NCMBUTTONDOWN:
|
||||
case WM_NCXBUTTONDOWN:
|
||||
case WM_LBUTTONDOWN:
|
||||
case WM_RBUTTONDOWN:
|
||||
case WM_MBUTTONDOWN:
|
||||
case WM_XBUTTONDOWN:
|
||||
MouseEventTest(mhs->hwnd, mhs->pt, false);
|
||||
break;
|
||||
case WM_MOUSEWHEEL:
|
||||
case WM_MOUSEHWHEEL:
|
||||
MouseEventTest(mhs->hwnd, mhs->pt, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return CallNextHookEx(g_hook, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
static void on_editbox_creation(HWND p_editbox) {
|
||||
// PFC_ASSERT(core_api::is_main_thread());
|
||||
g_editboxes.push_front(p_editbox);
|
||||
if (g_hook == NULL) {
|
||||
g_hook = SetWindowsHookEx(WH_MOUSE, GMouseProc, NULL, GetCurrentThreadId());
|
||||
}
|
||||
/*if (g_keyHook == NULL) {
|
||||
g_keyHook = SetWindowsHookEx(WH_KEYBOARD, GKeyboardProc, NULL, GetCurrentThreadId());
|
||||
}*/
|
||||
}
|
||||
static void UnhookHelper(HHOOK & hook) {
|
||||
HHOOK v = pfc::replace_null_t(hook);
|
||||
if (v != NULL) UnhookWindowsHookEx(v);
|
||||
}
|
||||
static void on_editbox_destruction(HWND p_editbox) {
|
||||
// PFC_ASSERT(core_api::is_main_thread());
|
||||
g_editboxes.remove(p_editbox);
|
||||
if (g_editboxes.empty()) {
|
||||
UnhookHelper(g_hook); /*UnhookHelper(g_keyHook);*/
|
||||
}
|
||||
}
|
||||
|
||||
class CInPlaceComboBox : public CContainedWindowSimpleT<CComboBox> {
|
||||
public:
|
||||
BEGIN_MSG_MAP_EX(CInPlaceComboBox)
|
||||
//MSG_WM_CREATE(OnCreate)
|
||||
MSG_WM_DESTROY(OnDestroy)
|
||||
MSG_WM_GETDLGCODE(OnGetDlgCode)
|
||||
// MSG_WM_KILLFOCUS(OnKillFocus)
|
||||
MSG_WM_KEYDOWN(OnKeyDown)
|
||||
END_MSG_MAP()
|
||||
|
||||
void OnCreation() {
|
||||
on_editbox_creation(m_hWnd);
|
||||
}
|
||||
private:
|
||||
void OnDestroy() {
|
||||
m_selfDestruct = true;
|
||||
on_editbox_destruction(m_hWnd);
|
||||
SetMsgHandled(FALSE);
|
||||
}
|
||||
int OnCreate(LPCREATESTRUCT lpCreateStruct) {
|
||||
OnCreation();
|
||||
SetMsgHandled(FALSE);
|
||||
return 0;
|
||||
}
|
||||
UINT OnGetDlgCode(LPMSG lpMsg) {
|
||||
if (lpMsg == NULL) {
|
||||
SetMsgHandled(FALSE); return 0;
|
||||
} else {
|
||||
switch (lpMsg->message) {
|
||||
case WM_KEYDOWN:
|
||||
case WM_SYSKEYDOWN:
|
||||
switch (lpMsg->wParam) {
|
||||
case VK_TAB:
|
||||
case VK_ESCAPE:
|
||||
case VK_RETURN:
|
||||
return DLGC_WANTMESSAGE;
|
||||
default:
|
||||
SetMsgHandled(FALSE); return 0;
|
||||
}
|
||||
default:
|
||||
SetMsgHandled(FALSE); return 0;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
void OnKillFocus(CWindow wndFocus) {
|
||||
if ( wndFocus != NULL ) ForwardCompletion(KEditLostFocus);
|
||||
SetMsgHandled(FALSE);
|
||||
}
|
||||
|
||||
void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) {
|
||||
m_suppressChar = nFlags & 0xFF;
|
||||
switch (nChar) {
|
||||
case VK_TAB:
|
||||
ForwardCompletion(IsKeyPressed(VK_SHIFT) ? KEditShiftTab : KEditTab);
|
||||
return;
|
||||
case VK_ESCAPE:
|
||||
ForwardCompletion(KEditAborted);
|
||||
return;
|
||||
}
|
||||
m_suppressChar = 0;
|
||||
SetMsgHandled(FALSE);
|
||||
}
|
||||
|
||||
void ForwardCompletion(t_uint32 code) {
|
||||
if (IsWindowEnabled()) {
|
||||
CWindow owner = GetParent();
|
||||
owner.SendMessage(MSG_DISABLE_EDITING);
|
||||
owner.PostMessage(MSG_COMPLETION, code, 0);
|
||||
EnableWindow(FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
bool m_selfDestruct = false;
|
||||
UINT m_suppressChar = 0;
|
||||
};
|
||||
|
||||
class InPlaceComboContainer : public CWindowImpl<InPlaceComboContainer> {
|
||||
public:
|
||||
DECLARE_WND_CLASS_EX(_T("{18D85006-0CDB-49AB-A563-6A42014309A3}"), 0, -1);
|
||||
|
||||
const pfc::string_list_const * m_initData;
|
||||
const unsigned m_iDefault;
|
||||
|
||||
HWND Create(CWindow parent) {
|
||||
|
||||
RECT rect_cropped;
|
||||
{
|
||||
RECT client;
|
||||
WIN32_OP_D(parent.GetClientRect(&client));
|
||||
IntersectRect(&rect_cropped, &client, &m_initRect);
|
||||
}
|
||||
const DWORD containerStyle = WS_BORDER | WS_CHILD;
|
||||
AdjustWindowRect(&rect_cropped, containerStyle, FALSE);
|
||||
|
||||
|
||||
|
||||
WIN32_OP(__super::Create(parent, rect_cropped, NULL, containerStyle) != NULL);
|
||||
|
||||
try {
|
||||
CRect rcClient;
|
||||
WIN32_OP_D(GetClientRect(rcClient));
|
||||
|
||||
|
||||
DWORD style = WS_CHILD | WS_VISIBLE;//parent is invisible now
|
||||
|
||||
style |= CBS_DROPDOWNLIST;
|
||||
|
||||
|
||||
CComboBox edit;
|
||||
|
||||
WIN32_OP(edit.Create(*this, rcClient, NULL, style, 0, ID_MYEDIT) != NULL);
|
||||
edit.SetFont(parent.GetFont());
|
||||
|
||||
m_edit.SubclassWindow(edit);
|
||||
m_edit.OnCreation();
|
||||
|
||||
|
||||
#if 0 // doesn't quite work
|
||||
if (m_flags & (KFlagAlignCenter | KFlagAlignRight)) {
|
||||
COMBOBOXINFO info = {sizeof(info)};
|
||||
if (m_edit.GetComboBoxInfo(&info)) {
|
||||
CEdit edit2 = info.hwndList;
|
||||
if (edit2) {
|
||||
if (m_flags & KFlagAlignCenter) edit2.ModifyStyle(0, ES_CENTER);
|
||||
else if (m_flags & KFlagAlignRight) edit2.ModifyStyle(0, ES_RIGHT);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
if (m_initData != nullptr) {
|
||||
const size_t count = m_initData->get_count();
|
||||
for (size_t walk = 0; walk < count; ++walk) {
|
||||
m_edit.AddString(pfc::stringcvt::string_os_from_utf8(m_initData->get_item(walk)));
|
||||
}
|
||||
if (m_iDefault < count) m_edit.SetCurSel(m_iDefault);
|
||||
}
|
||||
} catch (...) {
|
||||
PostMessage(MSG_COMPLETION, InPlaceEdit::KEditAborted, 0);
|
||||
return m_hWnd;
|
||||
}
|
||||
|
||||
ShowWindow(SW_SHOW);
|
||||
m_edit.SetFocus();
|
||||
|
||||
m_initialized = true;
|
||||
|
||||
m_edit.ShowDropDown();
|
||||
|
||||
PFC_ASSERT(m_hWnd != NULL);
|
||||
|
||||
return m_hWnd;
|
||||
}
|
||||
|
||||
InPlaceComboContainer(const RECT & p_rect, unsigned p_flags, pfc::string_list_const * initData, unsigned iDefault, comboReply_t p_notify) : m_notify(p_notify), m_initData(initData), m_iDefault(iDefault), m_initRect(p_rect), m_flags(p_flags) { }
|
||||
|
||||
enum { ID_MYEDIT = 666 };
|
||||
|
||||
BEGIN_MSG_MAP_EX(InPlaceEditContainer)
|
||||
MESSAGE_HANDLER_EX(WM_CTLCOLOREDIT, MsgForwardToParent)
|
||||
MESSAGE_HANDLER_EX(WM_CTLCOLORSTATIC, MsgForwardToParent)
|
||||
MESSAGE_HANDLER_EX(WM_MOUSEWHEEL, MsgLostFocus)
|
||||
MESSAGE_HANDLER_EX(WM_MOUSEHWHEEL, MsgLostFocus)
|
||||
MESSAGE_HANDLER_SIMPLE(MSG_DISABLE_EDITING, OnMsgDisableEditing)
|
||||
MESSAGE_HANDLER_EX(MSG_COMPLETION, OnMsgCompletion)
|
||||
COMMAND_ID_HANDLER_EX(ID_MYEDIT, OnComboMsg)
|
||||
MSG_WM_DESTROY(OnDestroy)
|
||||
END_MSG_MAP()
|
||||
|
||||
HWND GetEditBox() const { return m_edit; }
|
||||
|
||||
private:
|
||||
void OnDestroy() { m_selfDestruct = true; }
|
||||
|
||||
LRESULT MsgForwardToParent(UINT msg, WPARAM wParam, LPARAM lParam) {
|
||||
return GetParent().SendMessage(msg, wParam, lParam);
|
||||
}
|
||||
LRESULT MsgLostFocus(UINT, WPARAM, LPARAM) {
|
||||
PostMessage(MSG_COMPLETION, InPlaceEdit::KEditLostFocus, 0);
|
||||
return 0;
|
||||
}
|
||||
void OnMsgDisableEditing() {
|
||||
ShowWindow(SW_HIDE);
|
||||
GetParent().UpdateWindow();
|
||||
}
|
||||
LRESULT OnMsgCompletion(UINT, WPARAM wParam, LPARAM lParam) {
|
||||
PFC_ASSERT(m_initialized);
|
||||
if ((wParam & KEditMaskReason) != KEditLostFocus) {
|
||||
GetParent().SetFocus();
|
||||
}
|
||||
OnCompletion((unsigned) wParam );
|
||||
if (!m_selfDestruct) {
|
||||
m_selfDestruct = true;
|
||||
DestroyWindow();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
void OnComboMsg(UINT code, int, CWindow source) {
|
||||
if (m_initialized && (code == CBN_SELENDOK || code == CBN_SELENDCANCEL) ) {
|
||||
PostMessage(MSG_COMPLETION, InPlaceEdit::KEditLostFocus, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void OnCompletion(unsigned p_status) {
|
||||
if (!m_completed) {
|
||||
m_completed = true;
|
||||
if (m_notify) m_notify( p_status, m_edit.GetCurSel() );
|
||||
}
|
||||
}
|
||||
|
||||
const comboReply_t m_notify;
|
||||
bool m_completed = false;
|
||||
bool m_initialized = false;
|
||||
bool m_selfDestruct = false;
|
||||
const CRect m_initRect;
|
||||
CInPlaceComboBox m_edit;
|
||||
const unsigned m_flags;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
static void fail(comboReply_t p_notify) {
|
||||
p_notify(KEditAborted, UINT_MAX);
|
||||
}
|
||||
|
||||
HWND InPlaceEdit::StartCombo(HWND p_parentwnd, const RECT & p_rect, unsigned p_flags, pfc::string_list_const & data, unsigned iDefault, comboReply_t p_notify) {
|
||||
try {
|
||||
PFC_ASSERT((CWindow(p_parentwnd).GetWindowLong(GWL_STYLE) & WS_CLIPCHILDREN) != 0);
|
||||
return (new CWindowCreateAndDelete<InPlaceComboContainer>(p_parentwnd, p_rect, p_flags, &data, iDefault, p_notify))->GetEditBox();
|
||||
} catch (...) {
|
||||
fail(p_notify);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
520
libPPUI/InPlaceEdit.cpp
Normal file
520
libPPUI/InPlaceEdit.cpp
Normal file
@@ -0,0 +1,520 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "InPlaceEdit.h"
|
||||
#include "wtl-pp.h"
|
||||
#include "win32_op.h"
|
||||
|
||||
#include "AutoComplete.h"
|
||||
#include "CWindowCreateAndDelete.h"
|
||||
#include "win32_utility.h"
|
||||
#include "listview_helper.h" // ListView_GetColumnCount
|
||||
#include "clipboard.h"
|
||||
|
||||
#include <forward_list>
|
||||
|
||||
#ifndef WM_MOUSEHWHEEL
|
||||
#define WM_MOUSEHWHEEL 0x20E
|
||||
#endif
|
||||
|
||||
using namespace InPlaceEdit;
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
enum {
|
||||
MSG_COMPLETION = WM_USER,
|
||||
MSG_DISABLE_EDITING
|
||||
};
|
||||
|
||||
|
||||
// Rationale: more than one HWND on the list is extremely uncommon, hence forward_list
|
||||
static std::forward_list<HWND> g_editboxes;
|
||||
static HHOOK g_hook = NULL /*, g_keyHook = NULL*/;
|
||||
|
||||
static void GAbortEditing(HWND edit, t_uint32 code) {
|
||||
CWindow parent = ::GetParent(edit);
|
||||
parent.SendMessage(MSG_DISABLE_EDITING);
|
||||
parent.PostMessage(MSG_COMPLETION, code, 0);
|
||||
}
|
||||
|
||||
static void GAbortEditing(t_uint32 code) {
|
||||
for (auto walk = g_editboxes.begin(); walk != g_editboxes.end(); ++walk) {
|
||||
GAbortEditing(*walk, code);
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsSamePopup(CWindow wnd1, CWindow wnd2) {
|
||||
return pfc::findOwningPopup(wnd1) == pfc::findOwningPopup(wnd2);
|
||||
}
|
||||
|
||||
static void MouseEventTest(HWND target, CPoint pt, bool isWheel) {
|
||||
for (auto walk = g_editboxes.begin(); walk != g_editboxes.end(); ++walk) {
|
||||
CWindow edit(*walk);
|
||||
bool cancel = false;
|
||||
if (target != edit && IsSamePopup(target, edit)) {
|
||||
cancel = true;
|
||||
} else if (isWheel) {
|
||||
CWindow target2 = WindowFromPoint(pt);
|
||||
if (target2 != edit && IsSamePopup(target2, edit)) {
|
||||
cancel = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (cancel) GAbortEditing(edit, KEditLostFocus);
|
||||
}
|
||||
}
|
||||
|
||||
static LRESULT CALLBACK GMouseProc(int nCode, WPARAM wParam, LPARAM lParam) {
|
||||
if (nCode == HC_ACTION) {
|
||||
const MOUSEHOOKSTRUCT * mhs = reinterpret_cast<const MOUSEHOOKSTRUCT *>(lParam);
|
||||
switch (wParam) {
|
||||
case WM_NCLBUTTONDOWN:
|
||||
case WM_NCRBUTTONDOWN:
|
||||
case WM_NCMBUTTONDOWN:
|
||||
case WM_NCXBUTTONDOWN:
|
||||
case WM_LBUTTONDOWN:
|
||||
case WM_RBUTTONDOWN:
|
||||
case WM_MBUTTONDOWN:
|
||||
case WM_XBUTTONDOWN:
|
||||
MouseEventTest(mhs->hwnd, mhs->pt, false);
|
||||
break;
|
||||
case WM_MOUSEWHEEL:
|
||||
case WM_MOUSEHWHEEL:
|
||||
MouseEventTest(mhs->hwnd, mhs->pt, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return CallNextHookEx(g_hook, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
static void on_editbox_creation(HWND p_editbox) {
|
||||
// PFC_ASSERT(core_api::is_main_thread());
|
||||
g_editboxes.push_front(p_editbox);
|
||||
if (g_hook == NULL) {
|
||||
g_hook = SetWindowsHookEx(WH_MOUSE, GMouseProc, NULL, GetCurrentThreadId());
|
||||
}
|
||||
/*if (g_keyHook == NULL) {
|
||||
g_keyHook = SetWindowsHookEx(WH_KEYBOARD, GKeyboardProc, NULL, GetCurrentThreadId());
|
||||
}*/
|
||||
}
|
||||
static void UnhookHelper(HHOOK & hook) {
|
||||
HHOOK v = pfc::replace_null_t(hook);
|
||||
if (v != NULL) UnhookWindowsHookEx(v);
|
||||
}
|
||||
static void on_editbox_destruction(HWND p_editbox) {
|
||||
// PFC_ASSERT(core_api::is_main_thread());
|
||||
g_editboxes.remove(p_editbox);
|
||||
if (g_editboxes.empty()) {
|
||||
UnhookHelper(g_hook); /*UnhookHelper(g_keyHook);*/
|
||||
}
|
||||
}
|
||||
|
||||
class CInPlaceEditBox : public CContainedWindowSimpleT<CEdit> {
|
||||
public:
|
||||
CInPlaceEditBox(uint32_t flags) : m_flags(flags) {}
|
||||
BEGIN_MSG_MAP_EX(CInPlaceEditBox)
|
||||
//MSG_WM_CREATE(OnCreate)
|
||||
MSG_WM_DESTROY(OnDestroy)
|
||||
MSG_WM_GETDLGCODE(OnGetDlgCode)
|
||||
MSG_WM_KILLFOCUS(OnKillFocus)
|
||||
MSG_WM_CHAR(OnChar)
|
||||
MSG_WM_KEYDOWN(OnKeyDown)
|
||||
MSG_WM_PASTE(OnPaste)
|
||||
END_MSG_MAP()
|
||||
|
||||
void OnCreation() {
|
||||
on_editbox_creation(m_hWnd);
|
||||
}
|
||||
private:
|
||||
void OnDestroy() {
|
||||
m_selfDestruct = true;
|
||||
on_editbox_destruction(m_hWnd);
|
||||
SetMsgHandled(FALSE);
|
||||
}
|
||||
int OnCreate(LPCREATESTRUCT lpCreateStruct) {
|
||||
OnCreation();
|
||||
SetMsgHandled(FALSE);
|
||||
return 0;
|
||||
}
|
||||
UINT OnGetDlgCode(LPMSG lpMsg) {
|
||||
if (lpMsg == NULL) {
|
||||
SetMsgHandled(FALSE); return 0;
|
||||
} else {
|
||||
switch (lpMsg->message) {
|
||||
case WM_KEYDOWN:
|
||||
case WM_SYSKEYDOWN:
|
||||
switch (lpMsg->wParam) {
|
||||
case VK_TAB:
|
||||
case VK_ESCAPE:
|
||||
case VK_RETURN:
|
||||
return DLGC_WANTMESSAGE;
|
||||
default:
|
||||
SetMsgHandled(FALSE); return 0;
|
||||
}
|
||||
default:
|
||||
SetMsgHandled(FALSE); return 0;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
void OnKillFocus(CWindow wndFocus) {
|
||||
if ( wndFocus != NULL ) ForwardCompletion(KEditLostFocus);
|
||||
SetMsgHandled(FALSE);
|
||||
}
|
||||
|
||||
bool testPaste(const char* str) {
|
||||
if (m_flags & InPlaceEdit::KFlagNumberSigned) {
|
||||
if (pfc::string_is_numeric(str)) return true;
|
||||
if (str[0] == '-' && pfc::string_is_numeric(str + 1) && GetWindowTextLength() == 0) return true;
|
||||
return false;
|
||||
}
|
||||
if (m_flags & InPlaceEdit::KFlagNumber) {
|
||||
return pfc::string_is_numeric(str);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void OnPaste() {
|
||||
if (m_flags & (InPlaceEdit::KFlagNumber | InPlaceEdit::KFlagNumberSigned)) {
|
||||
pfc::string8 temp;
|
||||
ClipboardHelper::OpenScope scope; scope.Open(m_hWnd);
|
||||
if (ClipboardHelper::GetString(temp)) {
|
||||
if (!testPaste(temp)) return;
|
||||
}
|
||||
}
|
||||
// Let edit box handle it
|
||||
SetMsgHandled(FALSE);
|
||||
}
|
||||
bool testChar(UINT nChar) {
|
||||
// Allow various non text characters
|
||||
if (nChar < ' ') return true;
|
||||
|
||||
if (m_flags & InPlaceEdit::KFlagNumberSigned) {
|
||||
if (pfc::char_is_numeric(nChar)) return true;
|
||||
if (nChar == '-') {
|
||||
return GetWindowTextLength() == 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (m_flags & InPlaceEdit::KFlagNumber) {
|
||||
return pfc::char_is_numeric(nChar);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) {
|
||||
if (m_suppressChar != 0) {
|
||||
UINT code = nFlags & 0xFF;
|
||||
if (code == m_suppressChar) return;
|
||||
}
|
||||
|
||||
if (!testChar(nChar)) {
|
||||
MessageBeep(0);
|
||||
return;
|
||||
}
|
||||
|
||||
SetMsgHandled(FALSE);
|
||||
}
|
||||
void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) {
|
||||
m_suppressChar = nFlags & 0xFF;
|
||||
switch (nChar) {
|
||||
case VK_BACK:
|
||||
if (GetHotkeyModifierFlags() == MOD_CONTROL) {
|
||||
CEditPPHooks::DeleteLastWord(*this);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 'A':
|
||||
if (GetHotkeyModifierFlags() == MOD_CONTROL) {
|
||||
this->SetSelAll(); return;
|
||||
}
|
||||
break;
|
||||
case VK_RETURN:
|
||||
if (!IsKeyPressed(VK_LCONTROL) && !IsKeyPressed(VK_RCONTROL)) {
|
||||
ForwardCompletion(KEditEnter);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case VK_TAB:
|
||||
ForwardCompletion(IsKeyPressed(VK_SHIFT) ? KEditShiftTab : KEditTab);
|
||||
return;
|
||||
case VK_ESCAPE:
|
||||
ForwardCompletion(KEditAborted);
|
||||
return;
|
||||
}
|
||||
m_suppressChar = 0;
|
||||
SetMsgHandled(FALSE);
|
||||
}
|
||||
|
||||
void ForwardCompletion(t_uint32 code) {
|
||||
if (IsWindowEnabled()) {
|
||||
CWindow owner = GetParent();
|
||||
owner.SendMessage(MSG_DISABLE_EDITING);
|
||||
owner.PostMessage(MSG_COMPLETION, code, 0);
|
||||
EnableWindow(FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
const uint32_t m_flags;
|
||||
bool m_selfDestruct = false;
|
||||
UINT m_suppressChar = 0;
|
||||
};
|
||||
|
||||
class InPlaceEditContainer : public CWindowImpl<InPlaceEditContainer> {
|
||||
public:
|
||||
DECLARE_WND_CLASS_EX(_T("{54340C80-248C-4b8e-8CD4-D624A8E9377B}"), 0, -1);
|
||||
|
||||
|
||||
HWND Create(CWindow parent) {
|
||||
|
||||
RECT rect_cropped;
|
||||
{
|
||||
RECT client;
|
||||
WIN32_OP_D(parent.GetClientRect(&client));
|
||||
IntersectRect(&rect_cropped, &client, &m_initRect);
|
||||
}
|
||||
const DWORD containerStyle = WS_BORDER | WS_CHILD;
|
||||
AdjustWindowRect(&rect_cropped, containerStyle, FALSE);
|
||||
|
||||
|
||||
|
||||
WIN32_OP(__super::Create(parent, rect_cropped, NULL, containerStyle) != NULL);
|
||||
|
||||
try {
|
||||
CRect rcClient;
|
||||
WIN32_OP_D(GetClientRect(rcClient));
|
||||
|
||||
|
||||
DWORD style = WS_CHILD | WS_VISIBLE;//parent is invisible now
|
||||
if (m_flags & KFlagMultiLine) style |= WS_VSCROLL | ES_MULTILINE;
|
||||
else style |= ES_AUTOHSCROLL;
|
||||
if (m_flags & KFlagReadOnly) style |= ES_READONLY;
|
||||
if (m_flags & KFlagAlignCenter) style |= ES_CENTER;
|
||||
else if (m_flags & KFlagAlignRight) style |= ES_RIGHT;
|
||||
else style |= ES_LEFT;
|
||||
|
||||
// ES_NUMBER is buggy in many ways (repaint glitches after balloon popup) and does not allow signed numbers.
|
||||
// We implement number handling by filtering WM_CHAR instead.
|
||||
// if (m_flags & KFlagNumber) style |= ES_NUMBER;
|
||||
|
||||
|
||||
CEdit edit;
|
||||
|
||||
WIN32_OP(edit.Create(*this, rcClient, NULL, style, 0, ID_MYEDIT) != NULL);
|
||||
edit.SetFont(parent.GetFont());
|
||||
|
||||
if (m_ACData.is_valid()) InitializeSimpleAC(edit, m_ACData.get_ptr(), m_ACOpts);
|
||||
m_edit.SubclassWindow(edit);
|
||||
m_edit.OnCreation();
|
||||
|
||||
pfc::setWindowText(m_edit, *m_content);
|
||||
m_edit.SetSelAll();
|
||||
} catch (...) {
|
||||
PostMessage(MSG_COMPLETION, InPlaceEdit::KEditAborted, 0);
|
||||
return m_hWnd;
|
||||
}
|
||||
|
||||
ShowWindow(SW_SHOW);
|
||||
m_edit.SetFocus();
|
||||
|
||||
m_initialized = true;
|
||||
|
||||
PFC_ASSERT(m_hWnd != NULL);
|
||||
|
||||
return m_hWnd;
|
||||
}
|
||||
|
||||
InPlaceEditContainer(const RECT & p_rect, t_uint32 p_flags, pfc::rcptr_t<pfc::string_base> p_content, reply_t p_notify, IUnknown * ACData, DWORD ACOpts)
|
||||
: m_content(p_content), m_notify(p_notify), m_completed(false), m_initialized(false), m_changed(false), m_disable_editing(false), m_initRect(p_rect),
|
||||
m_flags(p_flags), m_selfDestruct(), m_ACData(ACData), m_ACOpts(ACOpts),
|
||||
m_edit(p_flags)
|
||||
{
|
||||
}
|
||||
|
||||
enum { ID_MYEDIT = 666 };
|
||||
|
||||
BEGIN_MSG_MAP_EX(InPlaceEditContainer)
|
||||
MESSAGE_HANDLER_EX(WM_CTLCOLOREDIT, MsgForwardToParent)
|
||||
MESSAGE_HANDLER_EX(WM_CTLCOLORSTATIC, MsgForwardToParent)
|
||||
MESSAGE_HANDLER_EX(WM_MOUSEWHEEL, MsgLostFocus)
|
||||
MESSAGE_HANDLER_EX(WM_MOUSEHWHEEL, MsgLostFocus)
|
||||
MESSAGE_HANDLER_SIMPLE(MSG_DISABLE_EDITING, OnMsgDisableEditing)
|
||||
MESSAGE_HANDLER_EX(MSG_COMPLETION, OnMsgCompletion)
|
||||
COMMAND_HANDLER_EX(ID_MYEDIT, EN_CHANGE, OnEditChange)
|
||||
MSG_WM_DESTROY(OnDestroy)
|
||||
END_MSG_MAP()
|
||||
|
||||
HWND GetEditBox() const { return m_edit; }
|
||||
|
||||
private:
|
||||
void OnDestroy() { m_selfDestruct = true; }
|
||||
|
||||
LRESULT MsgForwardToParent(UINT msg, WPARAM wParam, LPARAM lParam) {
|
||||
return GetParent().SendMessage(msg, wParam, lParam);
|
||||
}
|
||||
LRESULT MsgLostFocus(UINT, WPARAM, LPARAM) {
|
||||
PostMessage(MSG_COMPLETION, InPlaceEdit::KEditLostFocus, 0);
|
||||
return 0;
|
||||
}
|
||||
void OnMsgDisableEditing() {
|
||||
ShowWindow(SW_HIDE);
|
||||
GetParent().UpdateWindow();
|
||||
m_disable_editing = true;
|
||||
}
|
||||
LRESULT OnMsgCompletion(UINT, WPARAM wParam, LPARAM lParam) {
|
||||
PFC_ASSERT(m_initialized);
|
||||
if ((wParam & KEditMaskReason) != KEditLostFocus) {
|
||||
GetParent().SetFocus();
|
||||
}
|
||||
if ( m_changed && m_edit != NULL ) {
|
||||
*m_content = pfc::getWindowText(m_edit);
|
||||
}
|
||||
OnCompletion((unsigned)wParam);
|
||||
if (!m_selfDestruct) {
|
||||
m_selfDestruct = true;
|
||||
DestroyWindow();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
void OnEditChange(UINT, int, CWindow source) {
|
||||
if (m_initialized && !m_disable_editing) {
|
||||
*m_content = pfc::getWindowText(source);
|
||||
m_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void OnCompletion(unsigned p_status) {
|
||||
if (!m_completed) {
|
||||
m_completed = true;
|
||||
p_status &= KEditMaskReason;
|
||||
unsigned code = p_status;
|
||||
if (m_changed && p_status != KEditAborted) code |= KEditFlagContentChanged;
|
||||
if (m_notify) m_notify(code);
|
||||
}
|
||||
}
|
||||
|
||||
const pfc::rcptr_t<pfc::string_base> m_content;
|
||||
const reply_t m_notify;
|
||||
bool m_completed;
|
||||
bool m_initialized, m_changed;
|
||||
bool m_disable_editing;
|
||||
bool m_selfDestruct;
|
||||
const CRect m_initRect;
|
||||
const t_uint32 m_flags;
|
||||
CInPlaceEditBox m_edit;
|
||||
|
||||
const pfc::com_ptr_t<IUnknown> m_ACData;
|
||||
const DWORD m_ACOpts;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
static void fail(reply_t p_notify) {
|
||||
p_notify(KEditAborted);
|
||||
// completion_notify::g_signal_completion_async(p_notify, KEditAborted);
|
||||
}
|
||||
|
||||
HWND InPlaceEdit::Start(HWND p_parentwnd, const RECT & p_rect, bool p_multiline, pfc::rcptr_t<pfc::string_base> p_content, reply_t p_notify) {
|
||||
return StartEx(p_parentwnd, p_rect, p_multiline ? KFlagMultiLine : 0, p_content, p_notify);
|
||||
}
|
||||
|
||||
void InPlaceEdit::Start_FromListView(HWND p_listview, unsigned p_item, unsigned p_subitem, unsigned p_linecount, pfc::rcptr_t<pfc::string_base> p_content, reply_t p_notify) {
|
||||
Start_FromListViewEx(p_listview, p_item, p_subitem, p_linecount, 0, p_content, p_notify);
|
||||
}
|
||||
|
||||
bool InPlaceEdit::TableEditAdvance_ListView(HWND p_listview, unsigned p_column_base, unsigned & p_item, unsigned & p_column, unsigned p_item_count, unsigned p_column_count, unsigned p_whathappened) {
|
||||
if (p_column >= p_column_count) return false;
|
||||
|
||||
|
||||
pfc::array_t<t_size> orderRev;
|
||||
{
|
||||
pfc::array_t<unsigned> order;
|
||||
const unsigned orderExCount = /*p_column_base + p_column_count*/ ListView_GetColumnCount(p_listview);
|
||||
PFC_ASSERT(orderExCount >= p_column_base + p_column_count);
|
||||
pfc::array_t<int> orderEx; orderEx.set_size(orderExCount);
|
||||
if (!ListView_GetColumnOrderArray(p_listview, orderExCount, orderEx.get_ptr())) {
|
||||
PFC_ASSERT(!"Should not get here - probably mis-calculated column count");
|
||||
return false;
|
||||
}
|
||||
order.set_size(p_column_count);
|
||||
for (unsigned walk = 0; walk < p_column_count; ++walk) order[walk] = orderEx[p_column_base + walk];
|
||||
|
||||
orderRev.set_size(p_column_count); order_helper::g_fill(orderRev);
|
||||
pfc::sort_get_permutation_t(order, pfc::compare_t<unsigned, unsigned>, p_column_count, orderRev.get_ptr());
|
||||
}
|
||||
|
||||
unsigned columnVisible = (unsigned)orderRev[p_column];
|
||||
|
||||
|
||||
if (!TableEditAdvance(p_item, columnVisible, p_item_count, p_column_count, p_whathappened)) return false;
|
||||
|
||||
p_column = (unsigned)order_helper::g_find_reverse(orderRev.get_ptr(), columnVisible);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InPlaceEdit::TableEditAdvance(unsigned & p_item, unsigned & p_column, unsigned p_item_count, unsigned p_column_count, unsigned p_whathappened) {
|
||||
if (p_item >= p_item_count || p_column >= p_column_count) return false;
|
||||
int delta = 0;
|
||||
|
||||
switch (p_whathappened & KEditMaskReason) {
|
||||
case KEditEnter:
|
||||
delta = (int)p_column_count;
|
||||
break;
|
||||
case KEditTab:
|
||||
delta = 1;
|
||||
break;
|
||||
case KEditShiftTab:
|
||||
delta = -1;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
while (delta > 0) {
|
||||
p_column++;
|
||||
if (p_column >= p_column_count) {
|
||||
p_column = 0;
|
||||
p_item++;
|
||||
if (p_item >= p_item_count) return false;
|
||||
}
|
||||
delta--;
|
||||
}
|
||||
while (delta < 0) {
|
||||
if (p_column == 0) {
|
||||
if (p_item == 0) return false;
|
||||
p_item--;
|
||||
p_column = p_column_count;
|
||||
}
|
||||
p_column--;
|
||||
delta++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
HWND InPlaceEdit::StartEx(HWND p_parentwnd, const RECT & p_rect, unsigned p_flags, pfc::rcptr_t<pfc::string_base> p_content, reply_t p_notify, IUnknown * ACData, DWORD ACOpts) {
|
||||
try {
|
||||
PFC_ASSERT((CWindow(p_parentwnd).GetWindowLong(GWL_STYLE) & WS_CLIPCHILDREN) != 0);
|
||||
return (new CWindowCreateAndDelete<InPlaceEditContainer>(p_parentwnd, p_rect, p_flags, p_content, p_notify, ACData, ACOpts))->GetEditBox();
|
||||
} catch (...) {
|
||||
fail(p_notify);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void InPlaceEdit::Start_FromListViewEx(HWND p_listview, unsigned p_item, unsigned p_subitem, unsigned p_linecount, unsigned p_flags, pfc::rcptr_t<pfc::string_base> p_content, reply_t p_notify) {
|
||||
try {
|
||||
ListView_EnsureVisible(p_listview, p_item, FALSE);
|
||||
RECT itemrect;
|
||||
WIN32_OP_D(ListView_GetSubItemRect(p_listview, p_item, p_subitem, LVIR_LABEL, &itemrect));
|
||||
|
||||
const bool multiline = p_linecount > 1;
|
||||
if (multiline) {
|
||||
itemrect.bottom = itemrect.top + (itemrect.bottom - itemrect.top) * p_linecount;
|
||||
}
|
||||
|
||||
StartEx(p_listview, itemrect, p_flags | (multiline ? KFlagMultiLine : 0), p_content, p_notify);
|
||||
} catch (...) {
|
||||
fail(p_notify);
|
||||
}
|
||||
}
|
||||
44
libPPUI/InPlaceEdit.h
Normal file
44
libPPUI/InPlaceEdit.h
Normal file
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace InPlaceEdit {
|
||||
|
||||
enum {
|
||||
KEditAborted = 0,
|
||||
KEditTab,
|
||||
KEditShiftTab,
|
||||
KEditEnter,
|
||||
KEditLostFocus,
|
||||
|
||||
KEditMaskReason = 0xFF,
|
||||
KEditFlagContentChanged = 0x100,
|
||||
|
||||
KFlagReadOnly = 1 << 0,
|
||||
KFlagMultiLine = 1 << 1,
|
||||
KFlagAlignCenter = 1 << 2,
|
||||
KFlagAlignRight = 1 << 3,
|
||||
KFlagNumber = 1 << 4,
|
||||
KFlagNumberSigned = 1 << 5,
|
||||
|
||||
KFlagCombo = 1 << 8, // FOR INTERNAL USE
|
||||
};
|
||||
|
||||
typedef std::function< void (unsigned) > reply_t;
|
||||
|
||||
typedef std::function< void(unsigned, unsigned) > comboReply_t; // status, index (UINT_MAX if n/a)
|
||||
|
||||
HWND Start(HWND p_parentwnd, const RECT & p_rect, bool p_multiline, pfc::rcptr_t<pfc::string_base> p_content, reply_t p_notify);
|
||||
|
||||
HWND StartEx(HWND p_parentwnd, const RECT & p_rect, unsigned p_flags, pfc::rcptr_t<pfc::string_base> p_content, reply_t p_notify, IUnknown * ACData = NULL, DWORD ACOpts = 0);
|
||||
|
||||
|
||||
HWND StartCombo(HWND p_parentwnd, const RECT & p_rect, unsigned p_flags, pfc::string_list_const & data, unsigned iDefault, comboReply_t p_notify);
|
||||
|
||||
void Start_FromListView(HWND p_listview, unsigned p_item, unsigned p_subitem, unsigned p_linecount, pfc::rcptr_t<pfc::string_base> p_content, reply_t p_notify);
|
||||
void Start_FromListViewEx(HWND p_listview, unsigned p_item, unsigned p_subitem, unsigned p_linecount, unsigned p_flags, pfc::rcptr_t<pfc::string_base> p_content, reply_t p_notify);
|
||||
|
||||
bool TableEditAdvance(unsigned & p_item, unsigned & p_column, unsigned p_item_count, unsigned p_column_count, unsigned p_whathappened);
|
||||
bool TableEditAdvance_ListView(HWND p_listview, unsigned p_column_base, unsigned & p_item, unsigned & p_column, unsigned p_item_count, unsigned p_column_count, unsigned p_whathappened);
|
||||
|
||||
}
|
||||
221
libPPUI/InPlaceEditTable.cpp
Normal file
221
libPPUI/InPlaceEditTable.cpp
Normal file
@@ -0,0 +1,221 @@
|
||||
#include "stdafx.h"
|
||||
#include "InPlaceEditTable.h"
|
||||
|
||||
#include "win32_op.h"
|
||||
#include "win32_utility.h"
|
||||
#include "AutoComplete.h"
|
||||
|
||||
#include "listview_helper.h"
|
||||
|
||||
namespace InPlaceEdit {
|
||||
|
||||
bool CTableEditHelperV2::tableEdit_cancel_task() {
|
||||
bool rv = false;
|
||||
if (m_taskKill) {
|
||||
*m_taskKill = true; m_taskKill = nullptr;
|
||||
rv = true;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
reply_t CTableEditHelperV2::tableEdit_create_task() {
|
||||
tableEdit_cancel_task();
|
||||
auto ks = std::make_shared<bool>(false);
|
||||
m_taskKill = ks;
|
||||
return [ks,this](unsigned code) {
|
||||
if ( ! * ks ) {
|
||||
this->tableEdit_on_task_completion( code );
|
||||
}
|
||||
};
|
||||
}
|
||||
t_size CTableEditHelperV2::ColumnToPosition(t_size col) const {
|
||||
PFC_ASSERT(TableEdit_IsColumnEditable(col));
|
||||
pfc::array_t<t_size> colOrder; GrabColumnOrder(colOrder);
|
||||
t_size skipped = 0;
|
||||
for (t_size walk = 0; walk < colOrder.get_size(); ++walk) {
|
||||
const t_size curCol = colOrder[walk];
|
||||
if (TableEdit_IsColumnEditable(curCol)) {
|
||||
if (curCol == col) return skipped;
|
||||
++skipped;
|
||||
}
|
||||
}
|
||||
PFC_ASSERT(!"Should not get here.");
|
||||
return ~0;
|
||||
}
|
||||
t_size CTableEditHelperV2::PositionToColumn(t_size pos) const {
|
||||
pfc::array_t<t_size> colOrder; GrabColumnOrder(colOrder);
|
||||
t_size skipped = 0;
|
||||
for (t_size walk = 0; walk < colOrder.get_size(); ++walk) {
|
||||
const t_size curCol = colOrder[walk];
|
||||
if (TableEdit_IsColumnEditable(curCol)) {
|
||||
if (skipped == pos) return curCol;
|
||||
++skipped;
|
||||
}
|
||||
}
|
||||
PFC_ASSERT(!"Should not get here.");
|
||||
return ~0;
|
||||
}
|
||||
t_size CTableEditHelperV2::EditableColumnCount() const {
|
||||
const t_size total = TableEdit_GetColumnCount();
|
||||
t_size found = 0;
|
||||
for (t_size walk = 0; walk < total; ++walk) {
|
||||
if (TableEdit_IsColumnEditable(walk)) found++;
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
bool CTableEditHelperV2::TableEdit_Advance(t_size & p_item, t_size & p_subItem, t_uint32 whathappened) {
|
||||
size_t guardItem = SIZE_MAX, guardSubItem = SIZE_MAX; // infinite loop guard
|
||||
size_t item = p_item, subItem = p_subItem;
|
||||
for ( ;; ) {
|
||||
unsigned _item((unsigned)item), _subItem((unsigned)ColumnToPosition(subItem));
|
||||
if (!InPlaceEdit::TableEditAdvance(_item, _subItem, (unsigned)TableEdit_GetItemCount(), (unsigned)EditableColumnCount(), whathappened)) return false;
|
||||
item = _item; subItem = PositionToColumn(_subItem);
|
||||
|
||||
if ( guardItem == SIZE_MAX ) {
|
||||
guardItem = item; guardSubItem = subItem;
|
||||
} else {
|
||||
// infinite loop guard
|
||||
if ( item == guardItem && subItem == guardSubItem ) return false;
|
||||
}
|
||||
|
||||
if (TableEdit_CanAdvanceHere(item, subItem, whathappened)) break;
|
||||
}
|
||||
p_item = item;
|
||||
p_subItem = subItem;
|
||||
return true;
|
||||
}
|
||||
|
||||
void CTableEditHelperV2::TableEdit_Abort(bool forwardContent) {
|
||||
if (tableEdit_cancel_task()) {
|
||||
if (forwardContent && (m_editFlags & KFlagReadOnly) == 0) {
|
||||
if (m_editData.is_valid()) {
|
||||
pfc::string8 temp(*m_editData);
|
||||
TableEdit_SetField(m_editItem, m_editSubItem, temp);
|
||||
}
|
||||
}
|
||||
m_editData.release();
|
||||
m_editDataCombo.reset();
|
||||
::SetFocus(TableEdit_GetParentWnd());
|
||||
TableEdit_Finished();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void CTableEditHelperV2::TableEdit_Start(t_size item, t_size subItem) {
|
||||
PFC_ASSERT(TableEdit_IsColumnEditable(subItem));
|
||||
m_editItem = item; m_editSubItem = subItem;
|
||||
_ReStart();
|
||||
}
|
||||
|
||||
void CTableEditHelperV2::_ReStart() {
|
||||
PFC_ASSERT(m_editItem < TableEdit_GetItemCount());
|
||||
PFC_ASSERT(m_editSubItem < TableEdit_GetColumnCount());
|
||||
|
||||
TableEdit_SetItemFocus(m_editItem, m_editSubItem);
|
||||
|
||||
m_editFlags = TableEdit_GetEditFlags(m_editItem, m_editSubItem);
|
||||
|
||||
m_editData.release();
|
||||
m_editDataCombo.reset();
|
||||
|
||||
if (m_editFlags & InPlaceEdit::KFlagCombo) {
|
||||
auto combo = TableEdit_GetCombo(m_editItem, m_editSubItem);
|
||||
RECT rc = TableEdit_GetItemRect(m_editItem, m_editSubItem);
|
||||
|
||||
auto data = std::make_shared< combo_t >(combo);
|
||||
m_editDataCombo = data;
|
||||
|
||||
auto task = tableEdit_create_task();
|
||||
auto comboTask = [data, task](unsigned status, unsigned sel) {
|
||||
data->iDefault = sel;
|
||||
task(status);
|
||||
};
|
||||
|
||||
InPlaceEdit::StartCombo(TableEdit_GetParentWnd(), rc, m_editFlags, combo.strings, combo.iDefault, comboTask );
|
||||
return;
|
||||
}
|
||||
|
||||
m_editData.new_t();
|
||||
t_size lineCount = 1;
|
||||
TableEdit_GetField(m_editItem, m_editSubItem, *m_editData, lineCount);
|
||||
|
||||
RECT rc = TableEdit_GetItemRect(m_editItem, m_editSubItem);
|
||||
if (lineCount > 1) {
|
||||
rc.bottom = (LONG)( rc.top + (rc.bottom - rc.top) * lineCount );
|
||||
m_editFlags |= KFlagMultiLine;
|
||||
}
|
||||
auto ac = this->TableEdit_GetAutoCompleteEx(m_editItem, m_editSubItem );
|
||||
InPlaceEdit::StartEx(TableEdit_GetParentWnd(), rc, m_editFlags, m_editData, tableEdit_create_task(), ac.data.get_ptr(), ac.options);
|
||||
}
|
||||
|
||||
CTableEditHelperV2::combo_t CTableEditHelperV2::TableEdit_GetCombo(size_t item, size_t sub) {
|
||||
return combo_t();
|
||||
}
|
||||
CTableEditHelperV2::autoComplete_t CTableEditHelperV2::TableEdit_GetAutoCompleteEx( size_t item, size_t sub ) {
|
||||
autoComplete_t ret;
|
||||
if ( this->TableEdit_GetAutoComplete( item, sub, ret.data ) ) ret.options = ret.optsDefault;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void CTableEditHelperV2::tableEdit_on_task_completion(unsigned status) {
|
||||
tableEdit_cancel_task();
|
||||
if (m_editData.is_valid()) {
|
||||
if (status & InPlaceEdit::KEditFlagContentChanged) {
|
||||
TableEdit_SetField(m_editItem, m_editSubItem, *m_editData);
|
||||
}
|
||||
m_editData.release();
|
||||
}
|
||||
if (m_editDataCombo != nullptr) {
|
||||
unsigned idx = m_editDataCombo->iDefault;
|
||||
if ( idx < m_editDataCombo->strings.get_count()) {
|
||||
const char * text = m_editDataCombo->strings.get_item(idx);
|
||||
TableEdit_SetField(m_editItem, m_editSubItem, text);
|
||||
}
|
||||
|
||||
m_editDataCombo.reset();
|
||||
}
|
||||
|
||||
if (TableEdit_Advance(m_editItem, m_editSubItem, status)) {
|
||||
_ReStart();
|
||||
} else {
|
||||
TableEdit_Finished();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void CTableEditHelperV2_ListView::TableEdit_GetColumnOrder(t_size * out, t_size count) const {
|
||||
pfc::array_t<int> temp; temp.set_size(count);
|
||||
WIN32_OP_D(ListView_GetColumnOrderArray(TableEdit_GetParentWnd(), count, temp.get_ptr()));
|
||||
for (t_size walk = 0; walk < count; ++walk) out[walk] = temp[walk];
|
||||
}
|
||||
|
||||
RECT CTableEditHelperV2_ListView::TableEdit_GetItemRect(t_size item, t_size subItem) const {
|
||||
RECT rc;
|
||||
WIN32_OP_D(ListView_GetSubItemRect(TableEdit_GetParentWnd(), (int)item, (int)subItem, LVIR_LABEL, &rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
void CTableEditHelperV2_ListView::TableEdit_GetField(t_size item, t_size subItem, pfc::string_base & out, t_size & lineCount) {
|
||||
listview_helper::get_item_text(TableEdit_GetParentWnd(), (int)item, (int)subItem, out);
|
||||
lineCount = pfc::is_multiline(out) ? 5 : 1;
|
||||
}
|
||||
void CTableEditHelperV2_ListView::TableEdit_SetField(t_size item, t_size subItem, const char * value) {
|
||||
WIN32_OP_D(listview_helper::set_item_text(TableEdit_GetParentWnd(), (int)item, (int)subItem, value));
|
||||
}
|
||||
t_size CTableEditHelperV2_ListView::TableEdit_GetItemCount() const {
|
||||
LRESULT temp;
|
||||
WIN32_OP_D((temp = ListView_GetItemCount(TableEdit_GetParentWnd())) >= 0);
|
||||
return (t_size)temp;
|
||||
}
|
||||
void CTableEditHelperV2_ListView::TableEdit_SetItemFocus(t_size item, t_size subItem) {
|
||||
WIN32_OP_D(listview_helper::select_single_item(TableEdit_GetParentWnd(), (int) item));
|
||||
}
|
||||
|
||||
t_size CTableEditHelperV2_ListView::TableEdit_GetColumnCount() const {
|
||||
return (t_size)ListView_GetColumnCount(TableEdit_GetParentWnd());
|
||||
}
|
||||
|
||||
}
|
||||
87
libPPUI/InPlaceEditTable.h
Normal file
87
libPPUI/InPlaceEditTable.h
Normal file
@@ -0,0 +1,87 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "InPlaceEdit.h"
|
||||
|
||||
namespace InPlaceEdit {
|
||||
class NOVTABLE CTableEditHelperV2 {
|
||||
public:
|
||||
virtual RECT TableEdit_GetItemRect(t_size item, t_size subItem) const = 0;
|
||||
virtual void TableEdit_GetField(t_size item, t_size subItem, pfc::string_base & out, t_size & lineCount) = 0;
|
||||
virtual void TableEdit_SetField(t_size item, t_size subItem, const char * value) = 0;
|
||||
virtual HWND TableEdit_GetParentWnd() const = 0;
|
||||
virtual bool TableEdit_Advance(t_size & item, t_size & subItem, t_uint32 whathappened);
|
||||
virtual bool TableEdit_CanAdvanceHere( size_t item, size_t subItem, uint32_t whatHappened ) const { return true; }
|
||||
virtual void TableEdit_Finished() {}
|
||||
virtual t_size TableEdit_GetItemCount() const = 0;
|
||||
virtual t_size TableEdit_GetColumnCount() const = 0;
|
||||
virtual void TableEdit_SetItemFocus(t_size item, t_size subItem) = 0;
|
||||
virtual bool TableEdit_IsColumnEditable(t_size subItem) const { return true; }
|
||||
virtual void TableEdit_GetColumnOrder(t_size * out, t_size count) const { order_helper::g_fill(out, count); }
|
||||
virtual t_uint32 TableEdit_GetEditFlags(t_size item, t_size subItem) const { return 0; }
|
||||
virtual bool TableEdit_GetAutoComplete(t_size item, t_size subItem, pfc::com_ptr_t<IUnknown> & out) { return false; }
|
||||
|
||||
struct autoComplete_t {
|
||||
pfc::com_ptr_t<IUnknown> data;
|
||||
enum { // ACO_* equivalents
|
||||
optsNone = 0,
|
||||
optsDefault = 1,
|
||||
optsAutoSuggest = 1,
|
||||
optsAutoAppend = 3,
|
||||
};
|
||||
DWORD options = optsNone;
|
||||
};
|
||||
virtual autoComplete_t TableEdit_GetAutoCompleteEx( size_t item, size_t sub );
|
||||
|
||||
struct combo_t {
|
||||
unsigned iDefault = 0;
|
||||
pfc::string_list_impl strings;
|
||||
};
|
||||
|
||||
virtual combo_t TableEdit_GetCombo(size_t item, size_t sub);
|
||||
|
||||
void TableEdit_Start(t_size item, t_size subItem);
|
||||
void TableEdit_Abort(bool forwardContent);
|
||||
bool TableEdit_IsActive() const { return !!m_taskKill; }
|
||||
protected:
|
||||
~CTableEditHelperV2() { tableEdit_cancel_task(); }
|
||||
CTableEditHelperV2() {}
|
||||
private:
|
||||
void tableEdit_on_task_completion(unsigned p_status);
|
||||
reply_t tableEdit_create_task();
|
||||
bool tableEdit_cancel_task();
|
||||
|
||||
t_size ColumnToPosition(t_size col) const;
|
||||
t_size PositionToColumn(t_size pos) const;
|
||||
t_size EditableColumnCount() const;
|
||||
void GrabColumnOrder(pfc::array_t<t_size> & buffer) const { buffer.set_size(TableEdit_GetColumnCount()); TableEdit_GetColumnOrder(buffer.get_ptr(), buffer.get_size()); }
|
||||
void _ReStart();
|
||||
|
||||
t_size m_editItem, m_editSubItem;
|
||||
t_uint32 m_editFlags;
|
||||
pfc::rcptr_t<pfc::string8> m_editData;
|
||||
std::shared_ptr< combo_t > m_editDataCombo;
|
||||
|
||||
std::shared_ptr<bool> m_taskKill;
|
||||
|
||||
CTableEditHelperV2( const CTableEditHelperV2 & ) = delete;
|
||||
void operator=( const CTableEditHelperV2 & ) = delete;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
class NOVTABLE CTableEditHelperV2_ListView : public CTableEditHelperV2 {
|
||||
public:
|
||||
RECT TableEdit_GetItemRect(t_size item, t_size subItem) const override;
|
||||
void TableEdit_GetField(t_size item, t_size subItem, pfc::string_base & out, t_size & lineCount) override;
|
||||
void TableEdit_SetField(t_size item, t_size subItem, const char * value) override;
|
||||
|
||||
t_size TableEdit_GetColumnCount() const override;
|
||||
|
||||
t_size TableEdit_GetItemCount() const override;
|
||||
void TableEdit_SetItemFocus(t_size item, t_size subItem) override;
|
||||
|
||||
void TableEdit_GetColumnOrder(t_size * out, t_size count) const override;
|
||||
};
|
||||
}
|
||||
502
libPPUI/PaintUtils.cpp
Normal file
502
libPPUI/PaintUtils.cpp
Normal file
@@ -0,0 +1,502 @@
|
||||
#include "stdafx.h"
|
||||
#include <vsstyle.h>
|
||||
|
||||
#include "PaintUtils.h"
|
||||
#include "gdiplus_helpers.h"
|
||||
// #include <helpers/win32_misc.h>
|
||||
// #include <ATLHelpers/GDIUtils.h>
|
||||
|
||||
#include "GDIUtils.h"
|
||||
#include "win32_op.h"
|
||||
#include "wtl-pp.h"
|
||||
|
||||
namespace PaintUtils {
|
||||
static t_uint16 extractChannel16(t_uint32 p_color,int p_which) throw() {
|
||||
return (t_uint16)( ((p_color >> (p_which * 8)) & 0xFF) << 8 );
|
||||
}
|
||||
|
||||
static t_uint8 extractbyte(t_uint32 p_val,t_size p_which) throw() {
|
||||
return (t_uint8) ( (p_val >> (p_which * 8)) & 0xFF );
|
||||
}
|
||||
|
||||
t_uint32 BlendColorEx(t_uint32 p_color1, t_uint32 p_color2, double mix) throw() {
|
||||
PFC_ASSERT(mix >= 0 && mix <= 1);
|
||||
t_uint32 ret = 0;
|
||||
for(t_size walk = 0; walk < 3; ++walk) {
|
||||
int val1 = extractbyte(p_color1,walk), val2 = extractbyte(p_color2,walk);
|
||||
int val = val1 + pfc::rint32((val2 - val1) * mix);
|
||||
ret |= (t_uint32)val << (walk * 8);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
t_uint32 BlendColor(t_uint32 p_color1, t_uint32 p_color2, int p_percentage) throw() {
|
||||
PFC_ASSERT(p_percentage <= 100);
|
||||
t_uint32 ret = 0;
|
||||
for(t_size walk = 0; walk < 3; ++walk) {
|
||||
int val1 = extractbyte(p_color1,walk), val2 = extractbyte(p_color2,walk);
|
||||
int val = val1 + MulDiv(val2 - val1,p_percentage,100);
|
||||
ret |= (t_uint32)val << (walk * 8);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
t_uint32 DriftColor(t_uint32 p_color,unsigned p_delta,bool p_direction) throw() {
|
||||
t_uint32 ret = 0;
|
||||
for(t_size walk = 0; walk < 3; ++walk) {
|
||||
unsigned val = extractbyte(p_color,walk);
|
||||
if (p_direction) val = 0xFF - val;
|
||||
if (val < p_delta) val = p_delta;
|
||||
val += p_delta;
|
||||
if (val > 0xFF) val = 0xFF;
|
||||
if (p_direction) val = 0xFF - val;
|
||||
ret |= (t_uint32)val << (walk * 8);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void FillVertexColor(TRIVERTEX & p_vertex,t_uint32 p_color,t_uint16 p_alpha) throw() {
|
||||
p_vertex.Red = extractChannel16(p_color,0);
|
||||
p_vertex.Green = extractChannel16(p_color,1);
|
||||
p_vertex.Blue = extractChannel16(p_color,2);
|
||||
p_vertex.Alpha = p_alpha;
|
||||
}
|
||||
|
||||
void FillRectSimple(CDCHandle p_dc,const CRect & p_rect,t_uint32 p_color) throw() {
|
||||
p_dc.FillSolidRect(p_rect, p_color);
|
||||
}
|
||||
|
||||
void GradientFillRect(CDCHandle p_dc,const CRect & p_rect,t_uint32 p_color1, t_uint32 p_color2, bool p_horizontal) throw() {
|
||||
TRIVERTEX verticies[2];
|
||||
GRADIENT_RECT element = {0,1};
|
||||
FillVertexColor(verticies[0],p_color1);
|
||||
FillVertexColor(verticies[1],p_color2);
|
||||
verticies[0].x = p_rect.left; verticies[0].y = p_rect.top;
|
||||
verticies[1].x = p_rect.right; verticies[1].y = p_rect.bottom;
|
||||
WIN32_OP_D( p_dc.GradientFill(verticies,tabsize(verticies),&element,1,p_horizontal ? GRADIENT_FILL_RECT_H : GRADIENT_FILL_RECT_V) );
|
||||
}
|
||||
|
||||
void GradientSplitRect(CDCHandle p_dc,const CRect & p_rect,t_uint32 p_bkColor, t_uint32 p_gradientColor,int p_splitPercent) throw() {
|
||||
const long split = p_rect.top + MulDiv(p_rect.Height(),p_splitPercent,100);
|
||||
CRect rcTemp;
|
||||
rcTemp = p_rect;
|
||||
rcTemp.bottom = split;
|
||||
GradientFillRect(p_dc,rcTemp,p_bkColor,p_gradientColor,false);
|
||||
rcTemp = p_rect;
|
||||
rcTemp.top = split;
|
||||
GradientFillRect(p_dc,rcTemp,p_gradientColor,p_bkColor,false);
|
||||
}
|
||||
|
||||
void GradientBar(CDCHandle p_dc,const CRect & p_rect,t_uint32 p_exterior, t_uint32 p_interior, int p_percentage) throw() {
|
||||
const int gradientPix = MulDiv(p_rect.Height(),p_percentage,100);
|
||||
CRect rcTemp;
|
||||
|
||||
rcTemp = p_rect;
|
||||
rcTemp.bottom = rcTemp.top + gradientPix;
|
||||
GradientFillRect(p_dc,rcTemp,p_exterior,p_interior,false);
|
||||
|
||||
rcTemp = p_rect;
|
||||
rcTemp.top += gradientPix; rcTemp.bottom -= gradientPix;
|
||||
FillRectSimple(p_dc,rcTemp,p_interior);
|
||||
|
||||
rcTemp = p_rect;
|
||||
rcTemp.top = rcTemp.bottom - gradientPix;
|
||||
GradientFillRect(p_dc,rcTemp,p_interior,p_exterior,false);
|
||||
}
|
||||
|
||||
void RenderItemBackground(CDCHandle p_dc,const CRect & p_itemRect,t_size p_item,t_uint32 p_color) throw() {
|
||||
const DWORD bkColor_base = p_color;
|
||||
const DWORD bkColor = DriftColor(bkColor_base,3, (p_item&1) != 0);
|
||||
|
||||
//GradientSplitRect(p_dc,p_itemRect,bkColor,BlendColor(bkColor,textColor,7),80);
|
||||
GradientBar(p_dc,p_itemRect,bkColor_base,bkColor,10);
|
||||
}
|
||||
|
||||
double Luminance(t_uint32 color) throw() {
|
||||
double r = extractbyte(color,0), g = extractbyte(color,1), b = extractbyte(color,2);
|
||||
return (0.2126 * r + 0.7152 * g + 0.0722 * b) / 255.0;
|
||||
//return (r * 0.3 + g * 0.59 + b * 0.11) / 255.0;
|
||||
}
|
||||
t_uint32 DetermineTextColor(t_uint32 bk) throw() {
|
||||
double l = Luminance(bk);
|
||||
if ( l > 0.6 ) {
|
||||
return 0; // black
|
||||
} else {
|
||||
return 0xFFFFFF; // white
|
||||
}
|
||||
}
|
||||
|
||||
void AddRectToRgn(HRGN p_rgn,CRect const & p_rect) throw() {
|
||||
CRgn temp;
|
||||
WIN32_OP_D( temp.CreateRectRgnIndirect(p_rect) != NULL );
|
||||
CRgnHandle(p_rgn).CombineRgn(temp,RGN_OR);
|
||||
}
|
||||
|
||||
void FocusRect2(CDCHandle dc, CRect const & rect, COLORREF bkColor) throw() {
|
||||
COLORREF txColor = DetermineTextColor( bkColor );
|
||||
COLORREF useColor = BlendColor(bkColor, txColor, 50);
|
||||
CDCBrush brush(dc, useColor);
|
||||
WIN32_OP_D( dc.FrameRect(rect,brush) );
|
||||
}
|
||||
void FocusRect(CDCHandle dc, CRect const & rect) throw() {
|
||||
CDCBrush brush(dc, 0x7F7F7F);
|
||||
WIN32_OP_D( dc.FrameRect(rect,brush) );
|
||||
//dc.DrawFocusRect(rect);
|
||||
}
|
||||
|
||||
namespace TrackBar {
|
||||
void DrawThumb(HTHEME theme,HDC dc,int state,const RECT * rcThumb, const RECT * rcUpdate) {
|
||||
if (theme == NULL) {
|
||||
RECT blah = *rcThumb;
|
||||
int flags = DFCS_BUTTONPUSH;
|
||||
switch(state) {
|
||||
case TUS_NORMAL:
|
||||
break;
|
||||
case TUS_DISABLED:
|
||||
flags |= DFCS_INACTIVE;
|
||||
break;
|
||||
case TUS_PRESSED:
|
||||
flags |= DFCS_PUSHED;
|
||||
break;
|
||||
}
|
||||
DrawFrameControl(dc,&blah,DFC_BUTTON,flags);
|
||||
} else {
|
||||
DrawThemeBackground(theme,dc,TKP_THUMB,state,rcThumb,rcUpdate);
|
||||
}
|
||||
}
|
||||
void DrawTrack(HTHEME theme,HDC dc,const RECT * rcTrack, const RECT * rcUpdate) {
|
||||
if (theme == NULL) {
|
||||
RECT blah = *rcTrack;
|
||||
DrawFrameControl(dc,&blah,DFC_BUTTON,DFCS_BUTTONPUSH|DFCS_PUSHED);
|
||||
} else {
|
||||
DrawThemeBackground(theme,dc,TKP_TRACK,TKS_NORMAL,rcTrack,rcUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawTrackVolume(HTHEME theme,HDC p_dc,const RECT * rcTrack, const RECT * rcUpdate) {
|
||||
CMemoryDC dc(p_dc,CRect(rcUpdate));
|
||||
CRect rc(*rcTrack);
|
||||
CRect update(*rcUpdate);
|
||||
|
||||
WIN32_OP_D( dc.BitBlt(update.left,update.top,update.Width(),update.Height(),p_dc,update.left,update.top,SRCCOPY) );
|
||||
|
||||
|
||||
|
||||
/*CDCHandle dc(p_dc);
|
||||
CPen pen; pen.CreatePen(PS_SOLID,1, GetSysColor(COLOR_GRAYTEXT));
|
||||
SelectObjectScope scope(dc, pen);
|
||||
dc.MoveTo(rc.left,rc.bottom);
|
||||
dc.LineTo(rc.right,rc.bottom);
|
||||
dc.LineTo(rc.right,rc.top);
|
||||
dc.LineTo(rc.left,rc.bottom);*/
|
||||
|
||||
//DrawTrack(theme,dc,rcTrack,rcUpdate);
|
||||
|
||||
try {
|
||||
Gdiplus::Point points[] = { Gdiplus::Point(rc.left, rc.bottom), Gdiplus::Point(rc.right, rc.bottom), Gdiplus::Point(rc.right, rc.top) } ;
|
||||
GdiplusErrorHandler eh;
|
||||
Gdiplus::Graphics graphics(dc);
|
||||
eh << graphics.GetLastStatus();
|
||||
Gdiplus::Color c;
|
||||
c.SetFromCOLORREF( GetSysColor(COLOR_BTNHIGHLIGHT) );
|
||||
Gdiplus::Pen penHL(c);
|
||||
c.SetFromCOLORREF( GetSysColor(COLOR_BTNSHADOW) );
|
||||
Gdiplus::Pen penSH(c);
|
||||
eh << graphics.SetSmoothingMode(Gdiplus::SmoothingModeHighQuality);
|
||||
//graphics.DrawPolygon(&pen,points,tabsize(points));
|
||||
eh << graphics.DrawLine(&penSH, points[0], points[0] + Gdiplus::Point(0,-1));
|
||||
eh << graphics.DrawLine(&penHL, points[0], points[1]);
|
||||
eh << graphics.DrawLine(&penHL, points[1], points[2]);
|
||||
eh << graphics.DrawLine(&penSH, points[2], points[0] + Gdiplus::Point(0,-1));
|
||||
} catch(std::exception const & e) {
|
||||
(void) e;
|
||||
// console::print(e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawSmoothedLine(HDC dc, CPoint pt1, CPoint pt2, COLORREF col, double width) {
|
||||
try {
|
||||
Gdiplus::Point points[] = { Gdiplus::Point(pt1.x,pt1.y), Gdiplus::Point(pt2.x,pt2.y) };
|
||||
GdiplusErrorHandler eh;
|
||||
Gdiplus::Graphics graphics(dc);
|
||||
eh << graphics.GetLastStatus();
|
||||
Gdiplus::Color c;
|
||||
c.SetFromCOLORREF( col );
|
||||
Gdiplus::Pen pen(c, (Gdiplus::REAL)( width ));
|
||||
eh << graphics.SetSmoothingMode(Gdiplus::SmoothingModeHighQuality);
|
||||
//graphics.DrawPolygon(&pen,points,tabsize(points));
|
||||
eh << graphics.DrawLine(&pen, points[0], points[1]);
|
||||
} catch(std::exception const & e) {
|
||||
(void) e;
|
||||
// console::print(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int get_text_width(HDC dc,const TCHAR * src,int len) {
|
||||
if (len<=0) return 0;
|
||||
else {
|
||||
SIZE goatse;
|
||||
GetTextExtentPoint32(dc,src,len,&goatse);
|
||||
return goatse.cx;
|
||||
}
|
||||
}
|
||||
|
||||
static t_uint32 TextOutColors_TranslateColor(const t_uint32 colors[3], int offset) {
|
||||
const double v = (double)offset / 3.0;
|
||||
if (v <= -1) return colors[0];
|
||||
else if (v < 0) return BlendColorEx(colors[0], colors[1], v + 1);
|
||||
else if (v == 0) return colors[1];
|
||||
else if (v < 1) return BlendColorEx(colors[1], colors[2], v);
|
||||
else return colors[2];
|
||||
}
|
||||
|
||||
void TextOutColors_StripCodesAppend(pfc::string_formatter & out, const char * in) {
|
||||
t_size done = 0, walk = 0;
|
||||
for(;;) {
|
||||
if (in[walk] == 0) {
|
||||
if (walk > done) out.add_string_nc(in + done, walk - done);
|
||||
return;
|
||||
}
|
||||
if ((unsigned)in[walk] < 32) {
|
||||
if (walk > done) {out.add_string_nc(in + done, walk - done);}
|
||||
done = walk + 1;
|
||||
}
|
||||
++walk;
|
||||
}
|
||||
}
|
||||
void TextOutColors_StripCodes(pfc::string_formatter & out, const char * in) {
|
||||
out.reset(); TextOutColors_StripCodesAppend(out, in);
|
||||
}
|
||||
|
||||
static bool IsControlChar(TCHAR c) {
|
||||
return (unsigned)c < 32;
|
||||
}
|
||||
static int MatchTruncat(HDC dc, int & pixels, const TCHAR * text, int textLen) {
|
||||
int min = 0, max = textLen;
|
||||
int minWidth = 0;
|
||||
while(min + 1 < max) {
|
||||
const int probe = (min + max) / 2;
|
||||
CSize size;
|
||||
WIN32_OP( GetTextExtentPoint32(dc, text, probe, &size) );
|
||||
if (size.cx <= pixels) {min = probe; minWidth = size.cx;}
|
||||
else max = probe;
|
||||
}
|
||||
pixels = minWidth;
|
||||
return min;
|
||||
}
|
||||
static int TruncatHeadroom(HDC dc) {
|
||||
CSize size;
|
||||
WIN32_OP( GetTextExtentPoint32(dc, _T("\x2026"), 1, &size) );
|
||||
return size.cx;
|
||||
}
|
||||
static void ExtTextOut_Truncat(HDC dc, int x, int y, CRect const & clip, const TCHAR * text, int textLen) {
|
||||
int width = pfc::max_t<int>(0, clip.right - x - TruncatHeadroom(dc));
|
||||
int truncat = MatchTruncat(dc, width, text, textLen);
|
||||
WIN32_OP( ExtTextOut(dc, x, y, ETO_CLIPPED, &clip, text, truncat, NULL) );
|
||||
WIN32_OP( ExtTextOut(dc, x + width, y, ETO_CLIPPED, &clip, _T("\x2026"), 1, NULL) );
|
||||
|
||||
|
||||
}
|
||||
bool TextContainsCodes(const TCHAR * src) {
|
||||
for(;;) {
|
||||
if (*src == 0) return false;
|
||||
if ((unsigned)*src < 32) return true;
|
||||
++src;
|
||||
}
|
||||
}
|
||||
void TextOutColorsEx(HDC dc,const TCHAR * src,const CRect & target,DWORD flags,const t_uint32 colors[3]) {
|
||||
if (!TextContainsCodes(src)) {
|
||||
SetTextColorScope cs(dc, colors[1]);
|
||||
CRect rc(target);
|
||||
CDCHandle(dc).DrawText(src,(int)_tcslen(src),rc,DT_NOPREFIX | DT_END_ELLIPSIS | DT_SINGLELINE | DT_VCENTER | flags);
|
||||
} else {
|
||||
const CSize textSize = PaintUtils::TextOutColors_CalcSize(dc, src);
|
||||
CPoint origin = target.TopLeft();
|
||||
origin.y = (target.top + target.bottom - textSize.cy) / 2;
|
||||
switch(flags & (DT_LEFT | DT_RIGHT | DT_CENTER)) {
|
||||
case DT_LEFT:
|
||||
break;
|
||||
case DT_RIGHT:
|
||||
if (textSize.cx < target.Width()) origin.x = target.right - textSize.cx;
|
||||
break;
|
||||
case DT_CENTER:
|
||||
if (textSize.cx < target.Width()) origin.x = (target.right + target.left - textSize.cx) / 2;
|
||||
break;
|
||||
}
|
||||
TextOutColors(dc, src, (int)_tcslen(src), origin, target, colors);
|
||||
}
|
||||
}
|
||||
void TextOutColors(HDC dc,const TCHAR * src,int len,CPoint offset,const CRect & clip,const t_uint32 colors[3], int tabWidthTotal, int tabWidthDiv) {
|
||||
SetTextAlign(dc,TA_LEFT);
|
||||
SetBkMode(dc,TRANSPARENT);
|
||||
|
||||
|
||||
int walk = 0;
|
||||
int position = offset.x;
|
||||
int colorOffset = 0;
|
||||
int tabs = 0;
|
||||
int positionTabDelta = 0;
|
||||
|
||||
for(;;) {
|
||||
int base = walk;
|
||||
while(walk < len && !IsControlChar(src[walk])) ++walk;
|
||||
if (walk>base) {
|
||||
SetTextColor(dc,TextOutColors_TranslateColor(colors, colorOffset));
|
||||
int width = get_text_width(dc,src+base,walk-base);
|
||||
if (position + width > clip.right) {
|
||||
ExtTextOut_Truncat(dc, position, offset.y, clip, src + base, walk - base);
|
||||
return;
|
||||
}
|
||||
WIN32_OP( ExtTextOut(dc,position,offset.y,ETO_CLIPPED,&clip,src+base,walk-base,0) );
|
||||
position += width;
|
||||
}
|
||||
if (walk>=len) break;
|
||||
|
||||
while(walk < len && IsControlChar(src[walk])) {
|
||||
if (src[walk] == TextOutColors_Dim) --colorOffset;
|
||||
else if (src[walk] == TextOutColors_Highlight) ++colorOffset;
|
||||
else if (src[walk] == '\t') {
|
||||
int newDelta = MulDiv(++tabs, tabWidthTotal, tabWidthDiv);
|
||||
position += newDelta - positionTabDelta;
|
||||
positionTabDelta = newDelta;
|
||||
}
|
||||
walk++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CSize TextOutColors_CalcSize(HDC dc, const TCHAR * src) {
|
||||
CSize acc(0,0);
|
||||
for(int walk = 0;;) {
|
||||
const int done = walk;
|
||||
while(!IsControlChar(src[walk])) ++walk;
|
||||
if (walk > done) {
|
||||
CSize temp;
|
||||
WIN32_OP( GetTextExtentPoint32(dc,src + done, walk - done, &temp) );
|
||||
acc.cx += temp.cx; pfc::max_acc(acc.cy, temp.cy);
|
||||
}
|
||||
if (src[walk] == 0) return acc;
|
||||
while(src[walk] != 0 && IsControlChar(src[walk])) ++walk;
|
||||
}
|
||||
}
|
||||
t_uint32 TextOutColors_CalcWidth(HDC dc, const TCHAR * src) {
|
||||
t_uint32 acc = 0;
|
||||
for(int walk = 0;;) {
|
||||
const int done = walk;
|
||||
while(!IsControlChar(src[walk])) ++walk;
|
||||
acc += get_text_width(dc, src + done, walk - done);
|
||||
if (src[walk] == 0) return acc;
|
||||
while(src[walk] != 0 && IsControlChar(src[walk])) ++walk;
|
||||
}
|
||||
}
|
||||
static const unsigned Marker_Dim = '<', Marker_Highlight = '>';
|
||||
|
||||
pfc::string TextOutColors_ImportScript(pfc::string script) {
|
||||
pfc::string_formatter temp; TextOutColors_ImportScript(temp, script.ptr()); return temp.get_ptr();
|
||||
}
|
||||
void TextOutColors_ImportScript(pfc::string_base & out, const char * in) {
|
||||
out.reset();
|
||||
for(;;) {
|
||||
t_size delta; t_uint32 c;
|
||||
delta = pfc::utf8_decode_char(in, c);
|
||||
if (delta == 0) break;
|
||||
switch(c) {
|
||||
case '>':
|
||||
c = PaintUtils::TextOutColors_Highlight;
|
||||
break;
|
||||
case '<':
|
||||
c = PaintUtils::TextOutColors_Dim;
|
||||
break;
|
||||
}
|
||||
out.add_char(c);
|
||||
in += delta;
|
||||
}
|
||||
}
|
||||
t_uint32 DrawText_TranslateHeaderAlignment(t_uint32 val) {
|
||||
switch(val & HDF_JUSTIFYMASK) {
|
||||
case HDF_LEFT:
|
||||
default:
|
||||
return DT_LEFT;
|
||||
case HDF_RIGHT:
|
||||
return DT_RIGHT;
|
||||
case HDF_CENTER:
|
||||
return DT_CENTER;
|
||||
}
|
||||
}
|
||||
|
||||
void RenderButton(HWND wnd_, HDC dc_, CRect rcUpdate, bool bPressed) {
|
||||
CDCHandle dc(dc_); CWindow wnd(wnd_);
|
||||
CTheme theme; theme.OpenThemeData(wnd, L"BUTTON");
|
||||
|
||||
RelayEraseBkgnd(wnd, wnd.GetParent(), dc);
|
||||
|
||||
const int part = BP_PUSHBUTTON;
|
||||
|
||||
enum {
|
||||
stNormal = PBS_NORMAL,
|
||||
stHot = PBS_HOT,
|
||||
stDisabled = PBS_DISABLED,
|
||||
stPressed = PBS_PRESSED,
|
||||
};
|
||||
|
||||
int state = 0;
|
||||
if (!wnd.IsWindowEnabled()) state = stDisabled;
|
||||
else if (bPressed) state = stPressed;
|
||||
else state = stNormal;
|
||||
|
||||
CRect rcClient; WIN32_OP_D( wnd.GetClientRect(rcClient) );
|
||||
|
||||
if (theme != NULL && IsThemePartDefined(theme, part, 0)) {
|
||||
DrawThemeBackground(theme, dc, part, state, rcClient, &rcUpdate);
|
||||
} else {
|
||||
int stateEx = DFCS_BUTTONPUSH;
|
||||
switch(state) {
|
||||
case stPressed: stateEx |= DFCS_PUSHED; break;
|
||||
case stDisabled: stateEx |= DFCS_INACTIVE; break;
|
||||
}
|
||||
DrawFrameControl(dc, rcClient, DFC_BUTTON, stateEx);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PaintSeparatorControl(HWND wnd_) {
|
||||
CWindow wnd(wnd_);
|
||||
CPaintDC dc(wnd);
|
||||
TCHAR buffer[512] = {};
|
||||
wnd.GetWindowText(buffer, _countof(buffer));
|
||||
const int txLen = (int) pfc::strlen_max_t(buffer, _countof(buffer));
|
||||
CRect contentRect;
|
||||
WIN32_OP_D(wnd.GetClientRect(contentRect));
|
||||
|
||||
dc.SetTextColor(GetSysColor(COLOR_WINDOWTEXT));
|
||||
dc.SetBkMode(TRANSPARENT);
|
||||
|
||||
{
|
||||
CBrushHandle brush = (HBRUSH)wnd.GetParent().SendMessage(WM_CTLCOLORSTATIC, (WPARAM)(HDC)dc, (LPARAM)wnd.m_hWnd);
|
||||
if (brush != NULL) dc.FillRect(contentRect, brush);
|
||||
}
|
||||
SelectObjectScope scopeFont(dc, wnd.GetFont());
|
||||
|
||||
if (txLen > 0) {
|
||||
CRect rcText(contentRect);
|
||||
if (!wnd.IsWindowEnabled()) {
|
||||
dc.SetTextColor(GetSysColor(COLOR_GRAYTEXT));
|
||||
}
|
||||
WIN32_OP_D(dc.DrawText(buffer, txLen, rcText, DT_NOPREFIX | DT_END_ELLIPSIS | DT_SINGLELINE | DT_VCENTER | DT_LEFT) > 0);
|
||||
// WIN32_OP_D( dc.GrayString(NULL, NULL, (LPARAM) buffer, txLen, rcText.left, rcText.top, rcText.Width(), rcText.Height() ) );
|
||||
}
|
||||
|
||||
SIZE txSize, probeSize;
|
||||
const TCHAR probe[] = _T("#");
|
||||
if (dc.GetTextExtent(buffer, txLen, &txSize) && dc.GetTextExtent(probe, _countof(probe), &probeSize)) {
|
||||
int spacing = txSize.cx > 0 ? (probeSize.cx / 4) : 0;
|
||||
if (txSize.cx + spacing < contentRect.Width()) {
|
||||
const CPoint center = contentRect.CenterPoint();
|
||||
CRect rcEdge(contentRect.left + txSize.cx + spacing, center.y, contentRect.right, contentRect.bottom);
|
||||
WIN32_OP_D(dc.DrawEdge(rcEdge, EDGE_ETCHED, BF_TOP));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
53
libPPUI/PaintUtils.h
Normal file
53
libPPUI/PaintUtils.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
|
||||
#include <Uxtheme.h>
|
||||
|
||||
namespace PaintUtils {
|
||||
t_uint32 BlendColor(t_uint32 p_color1, t_uint32 p_color2, int p_percentage = 50) throw();
|
||||
t_uint32 BlendColorEx(t_uint32 p_color1, t_uint32 p_color2, double mix = 0.5) throw();
|
||||
t_uint32 DriftColor(t_uint32 p_color,unsigned p_delta,bool p_direction) throw();
|
||||
void FillVertexColor(TRIVERTEX & p_vertex,t_uint32 p_color,t_uint16 p_alpha = 0) throw();
|
||||
void FillRectSimple(CDCHandle p_dc,const CRect & p_rect,t_uint32 p_color) throw();
|
||||
void GradientFillRect(CDCHandle p_dc,const CRect & p_rect,t_uint32 p_color1, t_uint32 p_color2, bool p_horizontal) throw();
|
||||
void GradientSplitRect(CDCHandle p_dc,const CRect & p_rect,t_uint32 p_bkColor, t_uint32 p_gradientColor,int p_splitPercent) throw();
|
||||
void GradientBar(CDCHandle p_dc,const CRect & p_rect,t_uint32 p_exterior, t_uint32 p_interior, int p_percentage) throw();
|
||||
void RenderItemBackground(CDCHandle p_dc,const CRect & p_itemRect,t_size p_item,t_uint32 p_color) throw();
|
||||
|
||||
t_uint32 DetermineTextColor(t_uint32 background) throw();
|
||||
double Luminance(t_uint32 color) throw();
|
||||
|
||||
void AddRectToRgn(HRGN rgn, CRect const & rect) throw();
|
||||
|
||||
void FocusRect(CDCHandle dc, CRect const & rect) throw();
|
||||
void FocusRect2(CDCHandle dc, CRect const & rect, COLORREF bkColor) throw();
|
||||
|
||||
namespace TrackBar {
|
||||
void DrawThumb(HTHEME theme,HDC dc,int state,const RECT * rcThumb, const RECT * rcUpdate);
|
||||
void DrawTrack(HTHEME theme,HDC dc,const RECT * rcTrack, const RECT * rcUpdate);
|
||||
void DrawTrackVolume(HTHEME theme,HDC dc,const RECT * rcTrack, const RECT * rcUpdate);
|
||||
};
|
||||
|
||||
void DrawSmoothedLine(HDC dc, CPoint p1, CPoint p2, COLORREF col, double width);
|
||||
|
||||
enum {
|
||||
TextOutColors_Dim = 21,
|
||||
TextOutColors_Highlight = 22,
|
||||
};
|
||||
void TextOutColors(HDC dc,const TCHAR * src,int len,CPoint offset,const CRect & clip,const t_uint32 colors[3], int tabWidthTotal = 0, int tabWidthDiv = 1);
|
||||
void TextOutColorsEx(HDC dc,const TCHAR * src,const CRect & target,DWORD flags,const t_uint32 colors[3]);
|
||||
void TextOutColors_StripCodesAppend(pfc::string_formatter & out, const char * in);
|
||||
void TextOutColors_StripCodes(pfc::string_formatter & out, const char * in);
|
||||
|
||||
t_uint32 TextOutColors_CalcWidth(HDC dc, const TCHAR * src);
|
||||
CSize TextOutColors_CalcSize(HDC dc, const TCHAR * src);
|
||||
|
||||
pfc::string TextOutColors_ImportScript(pfc::string script);
|
||||
void TextOutColors_ImportScript(pfc::string_base & out, const char * in);
|
||||
|
||||
bool TextContainsCodes(const TCHAR * src);
|
||||
|
||||
t_uint32 DrawText_TranslateHeaderAlignment(t_uint32 val);
|
||||
|
||||
void RenderButton(HWND wnd_, HDC dc_, CRect rcUpdate, bool bPressed);
|
||||
void PaintSeparatorControl(HWND wnd_);
|
||||
}
|
||||
195
libPPUI/SmartStrStr.cpp
Normal file
195
libPPUI/SmartStrStr.cpp
Normal file
@@ -0,0 +1,195 @@
|
||||
#include "StdAfx.h"
|
||||
#include "SmartStrStr.h"
|
||||
|
||||
SmartStrStr::SmartStrStr() {
|
||||
for (uint32_t walk = 128; walk < 0x10000; ++walk) {
|
||||
uint32_t c = Transform(walk);
|
||||
if (c != walk) {
|
||||
substituions[walk].insert(c);
|
||||
}
|
||||
}
|
||||
for (uint32_t walk = 32; walk < 0x10000; ++walk) {
|
||||
auto lo = ToLower(walk);
|
||||
if (lo != walk) {
|
||||
auto & s = substituions[walk]; s.insert(lo);
|
||||
|
||||
auto iter = substituions.find(lo);
|
||||
if (iter != substituions.end()) {
|
||||
s.insert(iter->second.begin(), iter->second.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InitTwoCharMappings();
|
||||
}
|
||||
|
||||
|
||||
const wchar_t * SmartStrStr::matchHereW(const wchar_t * pString, const wchar_t * pUserString) const {
|
||||
auto walkData = pString;
|
||||
auto walkUser = pUserString;
|
||||
for (;; ) {
|
||||
if (*walkUser == 0) return walkData;
|
||||
|
||||
uint32_t cData, cUser;
|
||||
size_t dData = pfc::utf16_decode_char(walkData, &cData);
|
||||
size_t dUser = pfc::utf16_decode_char(walkUser, &cUser);
|
||||
if (dData == 0 || dUser == 0) return nullptr;
|
||||
|
||||
if (cData != cUser) {
|
||||
bool gotMulti = false;
|
||||
{
|
||||
auto multi = twoCharMappings.find(cData);
|
||||
if (multi != twoCharMappings.end()) {
|
||||
const char * cDataSubst = multi->second;
|
||||
PFC_ASSERT(strlen(cDataSubst) == 2);
|
||||
if (matchOneChar(cUser, (uint32_t)cDataSubst[0])) {
|
||||
auto walkUser2 = walkUser + dUser;
|
||||
uint32_t cUser2;
|
||||
auto dUser2 = pfc::utf16_decode_char(walkUser2, &cUser2);
|
||||
if (matchOneChar(cUser2, (uint32_t)cDataSubst[1])) {
|
||||
gotMulti = true;
|
||||
dUser += dUser2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!gotMulti) {
|
||||
if (!matchOneChar(cUser, cData)) return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
walkData += dData;
|
||||
walkUser += dUser;
|
||||
}
|
||||
}
|
||||
|
||||
bool SmartStrStr::equals(const char * pString, const char * pUserString) const {
|
||||
auto p = matchHere(pString, pUserString);
|
||||
if ( p == nullptr ) return false;
|
||||
return *p == 0;
|
||||
}
|
||||
|
||||
const char * SmartStrStr::matchHere(const char * pString, const char * pUserString) const {
|
||||
const char * walkData = pString;
|
||||
const char * walkUser = pUserString;
|
||||
for (;; ) {
|
||||
if (*walkUser == 0) return walkData;
|
||||
|
||||
uint32_t cData, cUser;
|
||||
size_t dData = pfc::utf8_decode_char(walkData, cData);
|
||||
size_t dUser = pfc::utf8_decode_char(walkUser, cUser);
|
||||
if (dData == 0 || dUser == 0) return nullptr;
|
||||
|
||||
if (cData != cUser) {
|
||||
bool gotMulti = false;
|
||||
{
|
||||
auto multi = twoCharMappings.find(cData);
|
||||
if (multi != twoCharMappings.end()) {
|
||||
const char * cDataSubst = multi->second;
|
||||
PFC_ASSERT(strlen(cDataSubst) == 2);
|
||||
if (matchOneChar(cUser, (uint32_t)cDataSubst[0])) {
|
||||
auto walkUser2 = walkUser + dUser;
|
||||
uint32_t cUser2;
|
||||
auto dUser2 = pfc::utf8_decode_char(walkUser2, cUser2);
|
||||
if (matchOneChar(cUser2, (uint32_t)cDataSubst[1])) {
|
||||
gotMulti = true;
|
||||
dUser += dUser2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!gotMulti) {
|
||||
if (!matchOneChar(cUser, cData)) return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
walkData += dData;
|
||||
walkUser += dUser;
|
||||
}
|
||||
}
|
||||
|
||||
const char * SmartStrStr::strStrEnd(const char * pString, const char * pSubString, size_t * outFoundAt) const {
|
||||
size_t walk = 0;
|
||||
for (;; ) {
|
||||
if (pString[walk] == 0) return nullptr;
|
||||
auto end = matchHere(pString+walk, pSubString);
|
||||
if (end != nullptr) {
|
||||
if ( outFoundAt != nullptr ) * outFoundAt = walk;
|
||||
return end;
|
||||
}
|
||||
|
||||
size_t delta = pfc::utf8_char_len( pString + walk );
|
||||
if ( delta == 0 ) return nullptr;
|
||||
walk += delta;
|
||||
}
|
||||
}
|
||||
|
||||
const wchar_t * SmartStrStr::strStrEndW(const wchar_t * pString, const wchar_t * pSubString, size_t * outFoundAt) const {
|
||||
size_t walk = 0;
|
||||
for (;; ) {
|
||||
if (pString[walk] == 0) return nullptr;
|
||||
auto end = matchHereW(pString + walk, pSubString);
|
||||
if (end != nullptr) {
|
||||
if (outFoundAt != nullptr) * outFoundAt = walk;
|
||||
return end;
|
||||
}
|
||||
|
||||
uint32_t dontcare;
|
||||
size_t delta = pfc::utf16_decode_char(pString + walk, & dontcare);
|
||||
if (delta == 0) return nullptr;
|
||||
walk += delta;
|
||||
}
|
||||
}
|
||||
|
||||
bool SmartStrStr::matchOneChar(uint32_t cInput, uint32_t cData) const {
|
||||
if (cInput == cData) return true;
|
||||
auto iter = substituions.find(cData);
|
||||
if (iter == substituions.end()) return false;
|
||||
auto & s = iter->second;
|
||||
return s.find(cInput) != s.end();
|
||||
}
|
||||
|
||||
uint32_t SmartStrStr::Transform(uint32_t c) {
|
||||
wchar_t wide[2] = {}; char out[4] = {};
|
||||
pfc::utf16_encode_char(c, wide);
|
||||
BOOL fail = FALSE;
|
||||
if (WideCharToMultiByte(pfc::stringcvt::codepage_ascii, 0, wide, 2, out, 4, "?", &fail) > 0) {
|
||||
if (!fail) {
|
||||
if (out[0] > 0 && out[1] == 0) {
|
||||
c = out[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
uint32_t SmartStrStr::ToLower(uint32_t c) {
|
||||
return pfc::charLower(c);
|
||||
}
|
||||
|
||||
void SmartStrStr::ImportTwoCharMappings(const wchar_t * list, const char * replacement) {
|
||||
PFC_ASSERT(strlen(replacement) == 2);
|
||||
for (const wchar_t* ptr = list; ; ) {
|
||||
wchar_t c = *ptr++;
|
||||
if (c == 0) break;
|
||||
twoCharMappings[(uint32_t)c] = replacement;
|
||||
}
|
||||
}
|
||||
|
||||
void SmartStrStr::InitTwoCharMappings() {
|
||||
ImportTwoCharMappings(L"ÆǢǼ", "AE");
|
||||
ImportTwoCharMappings(L"æǣǽ", "ae");
|
||||
ImportTwoCharMappings(L"Œ", "OE");
|
||||
ImportTwoCharMappings(L"œɶ", "oe");
|
||||
ImportTwoCharMappings(L"DŽDZ", "DZ");
|
||||
ImportTwoCharMappings(L"dždzʣʥ", "dz");
|
||||
ImportTwoCharMappings(L"ß", "ss");
|
||||
ImportTwoCharMappings(L"LJ", "LJ");
|
||||
ImportTwoCharMappings(L"Lj", "Lj");
|
||||
ImportTwoCharMappings(L"lj", "lj");
|
||||
ImportTwoCharMappings(L"NJ", "NJ");
|
||||
ImportTwoCharMappings(L"Nj", "Nj");
|
||||
ImportTwoCharMappings(L"nj", "nj");
|
||||
ImportTwoCharMappings(L"IJ", "IJ");
|
||||
ImportTwoCharMappings(L"ij", "ij");
|
||||
}
|
||||
42
libPPUI/SmartStrStr.h
Normal file
42
libPPUI/SmartStrStr.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
//! Implementation of string matching for search purposes, such as media library search or typefind in list views. \n
|
||||
//! Inspired by Unicode asymetic search, but not strictly implementing the Unicode asymetric search specifications. \n
|
||||
//! Bootstraps its character mapping data from various Win32 API methods, requires no externally provided character mapping data. \n
|
||||
//! Windows-only code. \n
|
||||
//! \n
|
||||
//! Keeping a global instance of it is recommended, due to one time init overhead. \n
|
||||
//! Thread safety: safe to call concurrently once constructed.
|
||||
|
||||
class SmartStrStr {
|
||||
public:
|
||||
SmartStrStr();
|
||||
|
||||
//! Returns ptr to the end of the string if positive (for continuing search), nullptr if negative.
|
||||
const char * strStrEnd(const char * pString, const char * pSubString, size_t * outFoundAt = nullptr) const;
|
||||
const wchar_t * strStrEndW(const wchar_t * pString, const wchar_t * pSubString, size_t * outFoundAt = nullptr) const;
|
||||
//! Returns ptr to the end of the string if positive (for continuing search), nullptr if negative.
|
||||
const char * matchHere(const char * pString, const char * pUserString) const;
|
||||
const wchar_t * matchHereW( const wchar_t * pString, const wchar_t * pUserString) const;
|
||||
|
||||
//! String-equals tool, compares strings rather than searching for occurance
|
||||
bool equals( const char * pString, const char * pUserString) const;
|
||||
|
||||
//! One-char match. Doesn't use twoCharMappings, use only if you have to operate on char by char basis rather than call the other methods.
|
||||
bool matchOneChar(uint32_t cInput, uint32_t cData) const;
|
||||
private:
|
||||
|
||||
static uint32_t Transform(uint32_t c);
|
||||
static uint32_t ToLower(uint32_t c);
|
||||
|
||||
void ImportTwoCharMappings(const wchar_t * list, const char * replacement);
|
||||
void InitTwoCharMappings();
|
||||
|
||||
std::map<uint32_t, std::set<uint32_t> > substituions;
|
||||
std::map<uint32_t, const char* > twoCharMappings;
|
||||
};
|
||||
397
libPPUI/TreeMultiSel.h
Normal file
397
libPPUI/TreeMultiSel.h
Normal file
@@ -0,0 +1,397 @@
|
||||
#pragma once
|
||||
|
||||
// ================================================================================
|
||||
// CTreeMultiSel
|
||||
// Implementation of multi-selection in a tree view ctrl
|
||||
// Instantiate with dialog ID of your treeview,
|
||||
// plug into your dialog's message map.
|
||||
// Doesn't work correctly with explorer-themed tree controls (glitches happen).
|
||||
// ================================================================================
|
||||
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
class CTreeMultiSel : public CMessageMap {
|
||||
public:
|
||||
typedef std::set<HTREEITEM> selection_t;
|
||||
typedef std::vector<HTREEITEM> selectionOrdered_t;
|
||||
|
||||
CTreeMultiSel(unsigned ID) : m_ID(ID) {}
|
||||
|
||||
BEGIN_MSG_MAP_EX(CTreeMultiSel)
|
||||
NOTIFY_HANDLER_EX(m_ID, TVN_ITEMEXPANDED, OnItemExpanded)
|
||||
NOTIFY_HANDLER_EX(m_ID, NM_CLICK, OnClick)
|
||||
NOTIFY_HANDLER_EX(m_ID, TVN_DELETEITEM, OnItemDeleted)
|
||||
NOTIFY_HANDLER_EX(m_ID, TVN_SELCHANGING, OnSelChanging)
|
||||
NOTIFY_HANDLER_EX(m_ID, TVN_SELCHANGED, OnSelChangedFilter)
|
||||
NOTIFY_HANDLER_EX(m_ID, NM_SETFOCUS, OnFocus)
|
||||
NOTIFY_HANDLER_EX(m_ID, NM_KILLFOCUS, OnFocus)
|
||||
END_MSG_MAP()
|
||||
|
||||
const unsigned m_ID;
|
||||
|
||||
// Retrieves selected items - on order of appearance in the view
|
||||
selectionOrdered_t GetSelectionOrdered(CTreeViewCtrl tree) const {
|
||||
HTREEITEM first = tree.GetRootItem();
|
||||
selectionOrdered_t ret; ret.reserve( m_selection.size() );
|
||||
for(HTREEITEM walk = first; walk != NULL; walk = tree.GetNextVisibleItem(walk)) {
|
||||
if (m_selection.count(walk) > 0) ret.push_back( walk );
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
//! Undefined order! Use only when order of selected items is not relevant.
|
||||
selection_t GetSelection() const { return m_selection; }
|
||||
selection_t const & GetSelectionRef() const { return m_selection; }
|
||||
bool IsItemSelected(HTREEITEM item) const {return m_selection.count(item) > 0;}
|
||||
size_t GetSelCount() const {return m_selection.size();}
|
||||
//! Retrieves a single-selection item. Null if nothing or more than one item is selected.
|
||||
HTREEITEM GetSingleSel() const {
|
||||
if (m_selection.size() != 1) return NULL;
|
||||
return *m_selection.begin();
|
||||
}
|
||||
|
||||
void OnContextMenu_FixSelection(CTreeViewCtrl tree, CPoint pt) {
|
||||
if (pt != CPoint(-1, -1)) {
|
||||
WIN32_OP_D(tree.ScreenToClient(&pt));
|
||||
UINT flags = 0;
|
||||
const HTREEITEM item = tree.HitTest(pt, &flags);
|
||||
if (item != NULL && (flags & TVHT_ONITEM) != 0) {
|
||||
if (!IsItemSelected(item)) {
|
||||
SelectSingleItem(tree, item);
|
||||
}
|
||||
CallSelectItem(tree, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnLButtonDown(CTreeViewCtrl tree, WPARAM wp, LPARAM lp) {
|
||||
if (!IsKeyPressed(VK_CONTROL)) {
|
||||
UINT flags = 0;
|
||||
HTREEITEM item = tree.HitTest(CPoint(lp), &flags);
|
||||
if (item != NULL && (flags & TVHT_ONITEM) != 0) {
|
||||
if (!IsItemSelected(item)) tree.SelectItem(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
static bool IsNavKey(UINT vk) {
|
||||
switch(vk) {
|
||||
case VK_UP:
|
||||
case VK_DOWN:
|
||||
case VK_RIGHT:
|
||||
case VK_LEFT:
|
||||
case VK_PRIOR:
|
||||
case VK_NEXT:
|
||||
case VK_HOME:
|
||||
case VK_END:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
BOOL OnChar(CTreeViewCtrl tree, WPARAM code) {
|
||||
switch(code) {
|
||||
case ' ':
|
||||
if (IsKeyPressed(VK_CONTROL) || !IsTypingInProgress()) {
|
||||
HTREEITEM item = tree.GetSelectedItem();
|
||||
if (item != NULL) SelectToggleItem(tree, item);
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
m_lastTypingTime = GetTickCount(); m_lastTypingTimeValid = true;
|
||||
return FALSE;
|
||||
}
|
||||
BOOL OnKeyDown(CTreeViewCtrl tree, UINT vKey) {
|
||||
if (IsNavKey(vKey)) m_lastTypingTimeValid = false;
|
||||
switch(vKey) {
|
||||
case VK_UP:
|
||||
if (IsKeyPressed(VK_CONTROL)) {
|
||||
HTREEITEM item = tree.GetSelectedItem();
|
||||
if (item != NULL) {
|
||||
HTREEITEM prev = tree.GetPrevVisibleItem(item);
|
||||
if (prev != NULL) {
|
||||
CallSelectItem(tree, prev);
|
||||
if (IsKeyPressed(VK_SHIFT)) {
|
||||
if (m_selStart == NULL) m_selStart = item;
|
||||
SelectItemRange(tree, prev);
|
||||
}
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
case VK_DOWN:
|
||||
if (IsKeyPressed(VK_CONTROL)) {
|
||||
HTREEITEM item = tree.GetSelectedItem();
|
||||
if (item != NULL) {
|
||||
HTREEITEM next = tree.GetNextVisibleItem(item);
|
||||
if (next != NULL) {
|
||||
CallSelectItem(tree, next);
|
||||
if (IsKeyPressed(VK_SHIFT)) {
|
||||
if (m_selStart == NULL) m_selStart = item;
|
||||
SelectItemRange(tree, next);
|
||||
}
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
/*case VK_LEFT:
|
||||
if (IsKeyPressed(VK_CONTROL)) {
|
||||
tree.SendMessage(WM_HSCROLL, SB_LINEUP, 0);
|
||||
}
|
||||
break;
|
||||
case VK_RIGHT:
|
||||
if (IsKeyPressed(VK_CONTROL)) {
|
||||
tree.SendMessage(WM_HSCROLL, SB_LINEDOWN, 0);
|
||||
}
|
||||
break;*/
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
private:
|
||||
LRESULT OnFocus(LPNMHDR hdr) {
|
||||
if ( m_selection.size() > 100 ) {
|
||||
CTreeViewCtrl tree(hdr->hwndFrom);
|
||||
tree.RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
|
||||
} else if (m_selection.size() > 0) {
|
||||
CTreeViewCtrl tree(hdr->hwndFrom);
|
||||
CRgn rgn; rgn.CreateRectRgn(0,0,0,0);
|
||||
for(auto walk = m_selection.begin(); walk != m_selection.end(); ++walk) {
|
||||
CRect rc;
|
||||
if (tree.GetItemRect(*walk, rc, TRUE)) {
|
||||
CRgn temp; temp.CreateRectRgnIndirect(rc);
|
||||
rgn.CombineRgn(temp, RGN_OR);
|
||||
}
|
||||
}
|
||||
tree.RedrawWindow(NULL, rgn, RDW_INVALIDATE | RDW_ERASE);
|
||||
}
|
||||
SetMsgHandled(FALSE);
|
||||
return 0;
|
||||
}
|
||||
void CallSelectItem(CTreeViewCtrl tree, HTREEITEM item) {
|
||||
const bool was = m_ownSelChange; m_ownSelChange = true;
|
||||
tree.SelectItem(item);
|
||||
m_ownSelChange = was;
|
||||
}
|
||||
LRESULT OnSelChangedFilter(LPNMHDR) {
|
||||
if (m_ownSelChangeNotify) SetMsgHandled(FALSE);
|
||||
return 0;
|
||||
}
|
||||
LRESULT OnItemDeleted(LPNMHDR pnmh) {
|
||||
const HTREEITEM item = reinterpret_cast<NMTREEVIEW*>(pnmh)->itemOld.hItem;
|
||||
m_selection.erase( item );
|
||||
if (m_selStart == item) m_selStart = NULL;
|
||||
SetMsgHandled(FALSE);
|
||||
return 0;
|
||||
}
|
||||
LRESULT OnItemExpanded(LPNMHDR pnmh) {
|
||||
NMTREEVIEW * info = reinterpret_cast<NMTREEVIEW *>(pnmh);
|
||||
CTreeViewCtrl tree ( pnmh->hwndFrom );
|
||||
if ((info->itemNew.state & TVIS_EXPANDED) == 0) {
|
||||
if (DeselectChildren( tree, info->itemNew.hItem )) {
|
||||
SendOnSelChanged(tree);
|
||||
}
|
||||
}
|
||||
SetMsgHandled(FALSE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
BOOL HandleClick(CTreeViewCtrl tree, CPoint pt) {
|
||||
UINT htFlags = 0;
|
||||
HTREEITEM item = tree.HitTest(pt, &htFlags);
|
||||
if (item != NULL && (htFlags & TVHT_ONITEM) != 0) {
|
||||
if (IsKeyPressed(VK_CONTROL)) {
|
||||
SelectToggleItem(tree, item);
|
||||
return TRUE;
|
||||
} else if (item == tree.GetSelectedItem() && !IsItemSelected(item)) {
|
||||
SelectToggleItem(tree, item);
|
||||
return TRUE;
|
||||
} else {
|
||||
//tree.SelectItem(item);
|
||||
return FALSE;
|
||||
}
|
||||
} else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
LRESULT OnClick(LPNMHDR pnmh) {
|
||||
CPoint pt(GetMessagePos());
|
||||
CTreeViewCtrl tree ( pnmh->hwndFrom );
|
||||
WIN32_OP_D ( tree.ScreenToClient( &pt ) );
|
||||
return HandleClick(tree, pt) ? 1 : 0;
|
||||
}
|
||||
|
||||
LRESULT OnSelChanging(LPNMHDR pnmh) {
|
||||
if (!m_ownSelChange) {
|
||||
//console::formatter() << "OnSelChanging";
|
||||
NMTREEVIEW * info = reinterpret_cast<NMTREEVIEW *>(pnmh);
|
||||
CTreeViewCtrl tree ( pnmh->hwndFrom );
|
||||
const HTREEITEM item = info->itemNew.hItem;
|
||||
|
||||
if (IsTypingInProgress()) {
|
||||
SelectSingleItem(tree, item);
|
||||
} else if (IsKeyPressed(VK_SHIFT)) {
|
||||
SelectItemRange(tree, item);
|
||||
} else if (IsKeyPressed(VK_CONTROL)) {
|
||||
SelectToggleItem(tree, item);
|
||||
} else {
|
||||
SelectSingleItem(tree, item);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SelectItemRange(CTreeViewCtrl tree, HTREEITEM item) {
|
||||
if (m_selStart == NULL || m_selStart == item) {
|
||||
SelectSingleItem(tree, item);
|
||||
return;
|
||||
}
|
||||
|
||||
selection_t newSel = GrabRange(tree, m_selStart, item );
|
||||
ApplySelection(tree, newSel);
|
||||
}
|
||||
static selection_t GrabRange(CTreeViewCtrl tree, HTREEITEM item1, HTREEITEM item2) {
|
||||
selection_t range1, range2;
|
||||
HTREEITEM walk1 = item1, walk2 = item2;
|
||||
for(;;) {
|
||||
if (walk1 != NULL) {
|
||||
range1.insert( walk1 );
|
||||
if (walk1 == item2) {
|
||||
return range1;
|
||||
}
|
||||
walk1 = tree.GetNextVisibleItem(walk1);
|
||||
}
|
||||
if (walk2 != NULL) {
|
||||
range2.insert( walk2 );
|
||||
if (walk2 == item1) {
|
||||
return range2;
|
||||
}
|
||||
walk2 = tree.GetNextVisibleItem(walk2);
|
||||
}
|
||||
if (walk1 == NULL && walk2 == NULL) {
|
||||
// should not get here
|
||||
return selection_t();
|
||||
}
|
||||
}
|
||||
}
|
||||
void SelectToggleItem(CTreeViewCtrl tree, HTREEITEM item) {
|
||||
m_selStart = item;
|
||||
if ( IsItemSelected( item ) ) {
|
||||
m_selection.erase( item );
|
||||
} else {
|
||||
m_selection.insert( item );
|
||||
}
|
||||
UpdateItem(tree, item);
|
||||
}
|
||||
public:
|
||||
void SelectSingleItem(CTreeViewCtrl tree, HTREEITEM item) {
|
||||
m_selStart = item;
|
||||
if (m_selection.size() == 1 && *m_selection.begin() == item) return;
|
||||
DeselectAll(tree); SelectItem(tree, item);
|
||||
}
|
||||
|
||||
void ApplySelection(CTreeViewCtrl tree, selection_t const & newSel) {
|
||||
CRgn updateRgn;
|
||||
bool changed = false;
|
||||
if (newSel.size() != m_selection.size() && newSel.size() + m_selection.size() > 100) {
|
||||
// don't bother with regions
|
||||
changed = true;
|
||||
} else {
|
||||
WIN32_OP_D(updateRgn.CreateRectRgn(0, 0, 0, 0) != NULL);
|
||||
for (auto walk = m_selection.begin(); walk != m_selection.end(); ++walk) {
|
||||
if (newSel.count(*walk) == 0) {
|
||||
changed = true;
|
||||
CRect rc;
|
||||
if (tree.GetItemRect(*walk, rc, TRUE)) {
|
||||
CRgn temp; WIN32_OP_D(temp.CreateRectRgnIndirect(rc));
|
||||
WIN32_OP_D(updateRgn.CombineRgn(temp, RGN_OR) != ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto walk = newSel.begin(); walk != newSel.end(); ++walk) {
|
||||
if (m_selection.count(*walk) == 0) {
|
||||
changed = true;
|
||||
CRect rc;
|
||||
if (tree.GetItemRect(*walk, rc, TRUE)) {
|
||||
CRgn temp; WIN32_OP_D(temp.CreateRectRgnIndirect(rc));
|
||||
WIN32_OP_D(updateRgn.CombineRgn(temp, RGN_OR) != ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
m_selection = newSel;
|
||||
tree.RedrawWindow(NULL, updateRgn);
|
||||
SendOnSelChanged(tree);
|
||||
}
|
||||
}
|
||||
|
||||
void DeselectItem(CTreeViewCtrl tree, HTREEITEM item) {
|
||||
if (IsItemSelected(item)) {
|
||||
m_selection.erase(item); UpdateItem(tree, item);
|
||||
}
|
||||
}
|
||||
void SelectItem(CTreeViewCtrl tree, HTREEITEM item) {
|
||||
if (!IsItemSelected(item)) {
|
||||
m_selection.insert(item); UpdateItem(tree, item);
|
||||
}
|
||||
}
|
||||
|
||||
void DeselectAll(CTreeViewCtrl tree) {
|
||||
if (m_selection.size() == 0) return;
|
||||
CRgn updateRgn;
|
||||
if (m_selection.size() <= 100) {
|
||||
WIN32_OP_D(updateRgn.CreateRectRgn(0, 0, 0, 0) != NULL);
|
||||
for (auto walk = m_selection.begin(); walk != m_selection.end(); ++walk) {
|
||||
CRect rc;
|
||||
if (tree.GetItemRect(*walk, rc, TRUE)) {
|
||||
CRgn temp; WIN32_OP_D(temp.CreateRectRgnIndirect(rc));
|
||||
WIN32_OP_D(updateRgn.CombineRgn(temp, RGN_OR) != ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
m_selection.clear();
|
||||
tree.RedrawWindow(NULL, updateRgn);
|
||||
}
|
||||
private:
|
||||
void UpdateItem(CTreeViewCtrl tree, HTREEITEM item) {
|
||||
CRect rc;
|
||||
if (tree.GetItemRect(item, rc, TRUE) ) {
|
||||
tree.RedrawWindow(rc);
|
||||
}
|
||||
SendOnSelChanged(tree);
|
||||
}
|
||||
void SendOnSelChanged(CTreeViewCtrl tree) {
|
||||
NMHDR hdr = {};
|
||||
hdr.code = TVN_SELCHANGED;
|
||||
hdr.hwndFrom = tree;
|
||||
hdr.idFrom = m_ID;
|
||||
const bool was = m_ownSelChangeNotify; m_ownSelChangeNotify = true;
|
||||
tree.GetParent().SendMessage(WM_NOTIFY, m_ID, (LPARAM) &hdr );
|
||||
m_ownSelChangeNotify = was;
|
||||
}
|
||||
|
||||
bool DeselectChildren( CTreeViewCtrl tree, HTREEITEM item ) {
|
||||
bool state = false;
|
||||
for(HTREEITEM walk = tree.GetChildItem( item ); walk != NULL; walk = tree.GetNextSiblingItem( walk ) ) {
|
||||
if (m_selection.erase(walk) > 0) state = true;
|
||||
if (m_selStart == walk) m_selStart = NULL;
|
||||
if (tree.GetItemState( walk, TVIS_EXPANDED ) ) {
|
||||
if (DeselectChildren( tree, walk )) state = true;
|
||||
}
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
bool IsTypingInProgress() const {
|
||||
return m_lastTypingTimeValid && (GetTickCount() - m_lastTypingTime < 500);
|
||||
}
|
||||
|
||||
selection_t m_selection;
|
||||
HTREEITEM m_selStart = NULL;
|
||||
bool m_ownSelChangeNotify = false, m_ownSelChange = false;
|
||||
DWORD m_lastTypingTime = 0; bool m_lastTypingTimeValid = false;
|
||||
};
|
||||
53
libPPUI/TypeFind.cpp
Normal file
53
libPPUI/TypeFind.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
#include "stdafx.h"
|
||||
#include "TypeFind.h"
|
||||
#include "SmartStrStr.h"
|
||||
|
||||
static size_t _ItemCount(HWND wnd) {
|
||||
return ListView_GetItemCount(wnd);
|
||||
}
|
||||
|
||||
static const wchar_t * _ItemText(wchar_t * buffer, int bufSize, HWND wnd, size_t index, int subItem) {
|
||||
NMLVDISPINFO info = {};
|
||||
info.hdr.code = LVN_GETDISPINFO;
|
||||
info.hdr.idFrom = GetDlgCtrlID(wnd);
|
||||
info.hdr.hwndFrom = wnd;
|
||||
info.item.iItem = (int) index;
|
||||
info.item.iSubItem = subItem;
|
||||
info.item.mask = LVIF_TEXT;
|
||||
info.item.pszText = buffer;
|
||||
info.item.cchTextMax = bufSize;
|
||||
::SendMessage(::GetParent(wnd), WM_NOTIFY, info.hdr.idFrom, reinterpret_cast<LPARAM>(&info));
|
||||
if (info.item.pszText == NULL) return L"";
|
||||
if ( bufSize > 0 && info.item.pszText == buffer ) buffer[ bufSize - 1 ] = 0;
|
||||
return info.item.pszText;
|
||||
}
|
||||
|
||||
LRESULT TypeFind::Handler(NMHDR* hdr, int subItemFrom, int subItemCnt) {
|
||||
NMLVFINDITEM * info = reinterpret_cast<NMLVFINDITEM*>(hdr);
|
||||
const HWND wnd = hdr->hwndFrom;
|
||||
if (info->lvfi.flags & LVFI_NEARESTXY) return -1;
|
||||
const size_t count = _ItemCount(wnd);
|
||||
if (count == 0) return -1;
|
||||
const size_t base = (size_t)info->iStart % count;
|
||||
|
||||
static SmartStrStr tool;
|
||||
|
||||
enum {
|
||||
bufSize = 1024,
|
||||
};
|
||||
wchar_t textBuf[bufSize];
|
||||
|
||||
for (size_t walk = 0; walk < count; ++walk) {
|
||||
const size_t index = (walk + base) % count;
|
||||
for (int subItem = subItemFrom; subItem < subItemFrom + subItemCnt; ++subItem) {
|
||||
if (tool.matchHereW(_ItemText(textBuf, bufSize, wnd, index, subItem), info->lvfi.psz)) return (LRESULT)index;
|
||||
}
|
||||
}
|
||||
for (size_t walk = 0; walk < count; ++walk) {
|
||||
const size_t index = (walk + base) % count;
|
||||
for (int subItem = subItemFrom; subItem < subItemFrom + subItemCnt; ++subItem) {
|
||||
if (tool.strStrEndW(_ItemText(textBuf, bufSize, wnd, index, subItem), info->lvfi.psz)) return (LRESULT)index;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
6
libPPUI/TypeFind.h
Normal file
6
libPPUI/TypeFind.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
class TypeFind {
|
||||
public:
|
||||
static LRESULT Handler(NMHDR* hdr, int subItemFrom = 0, int subItemCnt = 1);
|
||||
};
|
||||
51
libPPUI/clipboard.cpp
Normal file
51
libPPUI/clipboard.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "clipboard.h"
|
||||
#include "win32_op.h"
|
||||
|
||||
#ifdef UNICODE
|
||||
#define CF_TCHAR CF_UNICODETEXT
|
||||
#else
|
||||
#define CF_TCHAR CF_TEXT
|
||||
#endif
|
||||
|
||||
namespace ClipboardHelper {
|
||||
void OpenScope::Open(HWND p_owner) {
|
||||
Close();
|
||||
WIN32_OP(OpenClipboard(p_owner));
|
||||
m_open = true;
|
||||
}
|
||||
void OpenScope::Close() {
|
||||
if (m_open) {
|
||||
m_open = false;
|
||||
CloseClipboard();
|
||||
}
|
||||
}
|
||||
void SetRaw(UINT format,const void * data, t_size size) {
|
||||
HANDLE buffer = GlobalAlloc(GMEM_DDESHARE,size);
|
||||
if (buffer == NULL) throw std::bad_alloc();
|
||||
try {
|
||||
CGlobalLockScope lock(buffer);
|
||||
PFC_ASSERT(lock.GetSize() == size);
|
||||
memcpy(lock.GetPtr(),data,size);
|
||||
} catch(...) {
|
||||
GlobalFree(buffer); throw;
|
||||
}
|
||||
|
||||
WIN32_OP(SetClipboardData(format,buffer) != NULL);
|
||||
}
|
||||
void SetString(const char * in) {
|
||||
pfc::stringcvt::string_os_from_utf8 temp(in);
|
||||
SetRaw(CF_TCHAR,temp.get_ptr(),(temp.length() + 1) * sizeof(TCHAR));
|
||||
}
|
||||
|
||||
bool GetString(pfc::string_base & out) {
|
||||
pfc::array_t<t_uint8> temp;
|
||||
if (!GetRaw(CF_TCHAR,temp)) return false;
|
||||
out = pfc::stringcvt::string_utf8_from_os(reinterpret_cast<const TCHAR*>(temp.get_ptr()),temp.get_size() / sizeof(TCHAR));
|
||||
return true;
|
||||
}
|
||||
bool IsTextAvailable() {
|
||||
return IsClipboardFormatAvailable(CF_TCHAR) == TRUE;
|
||||
}
|
||||
}
|
||||
33
libPPUI/clipboard.h
Normal file
33
libPPUI/clipboard.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
namespace ClipboardHelper {
|
||||
|
||||
class OpenScope {
|
||||
public:
|
||||
OpenScope() : m_open(false) {}
|
||||
~OpenScope() {Close();}
|
||||
void Open(HWND p_owner);
|
||||
void Close();
|
||||
private:
|
||||
bool m_open;
|
||||
|
||||
PFC_CLASS_NOT_COPYABLE_EX(OpenScope)
|
||||
};
|
||||
|
||||
void SetRaw(UINT format,const void * buffer, t_size size);
|
||||
void SetString(const char * in);
|
||||
|
||||
bool GetString(pfc::string_base & out);
|
||||
|
||||
template<typename TArray>
|
||||
bool GetRaw(UINT format,TArray & out) {
|
||||
pfc::assert_byte_type<typename TArray::t_item>();
|
||||
HANDLE data = GetClipboardData(format);
|
||||
if (data == NULL) return false;
|
||||
CGlobalLockScope lock(data);
|
||||
out.set_size( lock.GetSize() );
|
||||
memcpy(out.get_ptr(), lock.GetPtr(), lock.GetSize() );
|
||||
return true;
|
||||
}
|
||||
bool IsTextAvailable();
|
||||
};
|
||||
65
libPPUI/commandline_parser.cpp
Normal file
65
libPPUI/commandline_parser.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
#include "stdafx.h"
|
||||
#include "commandline_parser.h"
|
||||
|
||||
commandline_parser::commandline_parser() {
|
||||
init(pfc::stringcvt::string_utf8_from_os(GetCommandLine()));
|
||||
}
|
||||
|
||||
void commandline_parser::init(const char * cmd)
|
||||
{
|
||||
pfc::string8_fastalloc temp;
|
||||
pfc::chain_list_v2_t<pfc::string8> out;
|
||||
while(*cmd)
|
||||
{
|
||||
temp.reset();
|
||||
while(*cmd && *cmd!=' ')
|
||||
{
|
||||
if (*cmd=='\"')
|
||||
{
|
||||
cmd++;
|
||||
while(*cmd && *cmd!='\"') temp.add_byte(*(cmd++));
|
||||
if (*cmd == '\"') cmd++;
|
||||
}
|
||||
else temp.add_byte(*(cmd++));
|
||||
}
|
||||
out.insert_last(temp);
|
||||
while(*cmd==' ') cmd++;
|
||||
}
|
||||
pfc::list_to_array(m_data,out);
|
||||
}
|
||||
|
||||
size_t commandline_parser::find_param(const char * ptr) const {
|
||||
for(size_t n=1;n<m_data.get_size();n++)
|
||||
{
|
||||
const char * cmd = m_data[n];
|
||||
if (cmd[0]=='/') {
|
||||
if (!strcmp(cmd+1,ptr)) return n;
|
||||
}
|
||||
}
|
||||
return SIZE_MAX;
|
||||
}
|
||||
bool commandline_parser::check_param(const char * ptr) const
|
||||
{
|
||||
return find_param(ptr) != SIZE_MAX;
|
||||
}
|
||||
|
||||
void commandline_parser::build_string(pfc::string_base & out)
|
||||
{
|
||||
out.reset();
|
||||
unsigned n;
|
||||
for(n=0;n<m_data.get_size();n++)
|
||||
{
|
||||
const char * cmd = m_data[n];
|
||||
if (!out.is_empty()) out += " ";
|
||||
if (strchr(cmd,' '))
|
||||
{
|
||||
out += "\"";
|
||||
out += cmd;
|
||||
out += "\"";
|
||||
}
|
||||
else
|
||||
{
|
||||
out += cmd;
|
||||
}
|
||||
}
|
||||
}
|
||||
18
libPPUI/commandline_parser.h
Normal file
18
libPPUI/commandline_parser.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
class commandline_parser
|
||||
{
|
||||
public:
|
||||
commandline_parser(const char * p_cmd) { init(p_cmd); }
|
||||
commandline_parser();
|
||||
|
||||
inline size_t get_count() const {return m_data.get_size();}
|
||||
inline const char * get_item(size_t n) const {return m_data[n];}
|
||||
inline const char * operator[](size_t n) const {return m_data[n];}
|
||||
bool check_param(const char * p_ptr) const;
|
||||
void build_string(pfc::string_base & p_out);
|
||||
size_t find_param(const char * ptr) const;
|
||||
private:
|
||||
void init(const char * cmd);
|
||||
pfc::array_t<pfc::string8> m_data;
|
||||
};
|
||||
46
libPPUI/gdiplus-helpers-webp.h
Normal file
46
libPPUI/gdiplus-helpers-webp.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include "gdiplus_helpers.h"
|
||||
|
||||
// Presumes prior #include of webp/decode.h
|
||||
|
||||
static bool IsImageWebP(const void * ptr, size_t bytes) {
|
||||
if (bytes < 12) return false;
|
||||
return memcmp(ptr, "RIFF", 4) == 0 && memcmp((const char*)ptr + 8, "WEBP", 4) == 0;
|
||||
}
|
||||
|
||||
|
||||
// WebP-aware GdiplusImageFromMem
|
||||
static Gdiplus::Image * GdiplusImageFromMem2(const void * ptr, size_t bytes) {
|
||||
GdiplusErrorHandler EH;
|
||||
using namespace Gdiplus;
|
||||
if (IsImageWebP(ptr, bytes)) {
|
||||
WebPBitstreamFeatures bs;
|
||||
if (WebPGetFeatures((const uint8_t*)ptr, bytes, &bs) != VP8_STATUS_OK) {
|
||||
throw std::runtime_error("WebP decoding failure");
|
||||
}
|
||||
const Gdiplus::PixelFormat pf = bs.has_alpha ? PixelFormat32bppARGB : PixelFormat32bppRGB;
|
||||
const int pfBytes = 4; // Gdiplus RGB is 4 bytes
|
||||
int w = 0, h = 0;
|
||||
// ALWAYS decode BGRA, Gdiplus will disregard alpha if was not originally present
|
||||
uint8_t * decodedData = WebPDecodeBGRA((const uint8_t*)ptr, bytes, &w, &h);
|
||||
pfc::onLeaving scope([decodedData] {WebPFree(decodedData); });
|
||||
if (decodedData == nullptr || w <= 0 || h <= 0) throw std::runtime_error("WebP decoding failure");
|
||||
|
||||
pfc::ptrholder_t<Bitmap> ret = new Gdiplus::Bitmap(w, h, pf);
|
||||
EH << ret->GetLastStatus();
|
||||
Rect rc(0, 0, w, h);
|
||||
Gdiplus::BitmapData bitmapData;
|
||||
EH << ret->LockBits(&rc, 0, pf, &bitmapData);
|
||||
uint8_t * target = (uint8_t*)bitmapData.Scan0;
|
||||
const uint8_t * source = decodedData;
|
||||
for (int y = 0; y < h; ++y) {
|
||||
memcpy(target, source, w * pfBytes);
|
||||
target += bitmapData.Stride;
|
||||
source += pfBytes * w;
|
||||
}
|
||||
EH << ret->UnlockBits(&bitmapData);
|
||||
return ret.detach();
|
||||
}
|
||||
return GdiplusImageFromMem(ptr, bytes);
|
||||
}
|
||||
233
libPPUI/gdiplus_helpers.h
Normal file
233
libPPUI/gdiplus_helpers.h
Normal file
@@ -0,0 +1,233 @@
|
||||
#pragma once
|
||||
#include <GdiPlus.h>
|
||||
|
||||
#include "win32_op.h"
|
||||
#include "win32_utility.h"
|
||||
|
||||
class GdiplusErrorHandler {
|
||||
public:
|
||||
void operator<<(Gdiplus::Status p_code) {
|
||||
if (p_code != Gdiplus::Ok) {
|
||||
throw pfc::exception(PFC_string_formatter() << "Gdiplus error (" << (unsigned) p_code << ")");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class GdiplusScope {
|
||||
public:
|
||||
GdiplusScope() {
|
||||
Gdiplus::GdiplusStartupInput input;
|
||||
Gdiplus::GdiplusStartupOutput output;
|
||||
GdiplusErrorHandler() << Gdiplus::GdiplusStartup(&m_token,&input,&output);
|
||||
}
|
||||
~GdiplusScope() {
|
||||
Gdiplus::GdiplusShutdown(m_token);
|
||||
}
|
||||
static void Once() {
|
||||
static bool done = _Once();
|
||||
}
|
||||
private:
|
||||
static bool _Once() {
|
||||
ULONG_PTR token = 0;
|
||||
Gdiplus::GdiplusStartupInput input;
|
||||
Gdiplus::GdiplusStartupOutput output;
|
||||
GdiplusErrorHandler() << Gdiplus::GdiplusStartup(&token, &input, &output);
|
||||
return true;
|
||||
}
|
||||
GdiplusScope( const GdiplusScope & ) = delete;
|
||||
void operator=( const GdiplusScope & ) = delete;
|
||||
|
||||
ULONG_PTR m_token = 0;
|
||||
};
|
||||
|
||||
static HBITMAP GdiplusLoadBitmap(UINT id, const TCHAR * resType, CSize size) {
|
||||
using namespace Gdiplus;
|
||||
try {
|
||||
auto stream = WinLoadResourceAsStream(GetThisModuleHandle(), MAKEINTRESOURCE(id), resType );
|
||||
|
||||
GdiplusErrorHandler EH;
|
||||
pfc::ptrholder_t<Image> source = new Image(stream);
|
||||
|
||||
EH << source->GetLastStatus();
|
||||
pfc::ptrholder_t<Bitmap> resized = new Bitmap(size.cx, size.cy, PixelFormat32bppARGB);
|
||||
EH << resized->GetLastStatus();
|
||||
|
||||
{
|
||||
pfc::ptrholder_t<Graphics> target = new Graphics(resized.get_ptr());
|
||||
EH << target->GetLastStatus();
|
||||
EH << target->SetInterpolationMode(InterpolationModeHighQuality);
|
||||
EH << target->Clear(Color(0,0,0,0));
|
||||
EH << target->DrawImage(source.get_ptr(), Rect(0,0,size.cx,size.cy));
|
||||
}
|
||||
|
||||
HBITMAP bmp = NULL;
|
||||
EH << resized->GetHBITMAP(Gdiplus::Color::White, & bmp );
|
||||
return bmp;
|
||||
} catch(...) {
|
||||
PFC_ASSERT( !"Should not get here");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static Gdiplus::Image * GdiplusImageFromMem(const void * ptr, size_t bytes) {
|
||||
using namespace Gdiplus;
|
||||
GdiplusErrorHandler EH;
|
||||
pfc::ptrholder_t<Image> source;
|
||||
|
||||
{
|
||||
pfc::com_ptr_t<IStream> stream;
|
||||
stream.attach( SHCreateMemStream((const BYTE*)ptr, pfc::downcast_guarded<UINT>(bytes)) );
|
||||
if (stream.is_empty()) throw std::bad_alloc();
|
||||
source = new Image(stream.get_ptr());
|
||||
}
|
||||
|
||||
EH << source->GetLastStatus();
|
||||
return source.detach();
|
||||
}
|
||||
|
||||
static Gdiplus::Bitmap * GdiplusResizeImage( Gdiplus::Image * source, CSize size ) {
|
||||
using namespace Gdiplus;
|
||||
GdiplusErrorHandler EH;
|
||||
pfc::ptrholder_t<Bitmap> resized = new Bitmap(size.cx, size.cy, PixelFormat32bppARGB);
|
||||
EH << resized->GetLastStatus();
|
||||
pfc::ptrholder_t<Graphics> target = new Graphics(resized.get_ptr());
|
||||
EH << target->GetLastStatus();
|
||||
EH << target->SetInterpolationMode(InterpolationModeHighQuality);
|
||||
EH << target->Clear(Color(0, 0, 0, 0));
|
||||
EH << target->DrawImage(source, Rect(0, 0, size.cx, size.cy));
|
||||
return resized.detach();
|
||||
}
|
||||
|
||||
static HICON GdiplusLoadIconFromMem( const void * ptr, size_t bytes, CSize size ) {
|
||||
using namespace Gdiplus;
|
||||
try {
|
||||
|
||||
GdiplusErrorHandler EH;
|
||||
pfc::ptrholder_t<Image> source = GdiplusImageFromMem(ptr, bytes);
|
||||
pfc::ptrholder_t<Bitmap> resized = GdiplusResizeImage( source.get_ptr(), size );
|
||||
HICON icon = NULL;
|
||||
EH << resized->GetHICON(&icon);
|
||||
return icon;
|
||||
} catch (...) {
|
||||
PFC_ASSERT(!"Should not get here");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static HICON GdiplusLoadIcon(UINT id, const TCHAR * resType, CSize size) {
|
||||
using namespace Gdiplus;
|
||||
try {
|
||||
auto stream = WinLoadResourceAsStream(GetThisModuleHandle(), MAKEINTRESOURCE(id), resType);
|
||||
|
||||
GdiplusErrorHandler EH;
|
||||
pfc::ptrholder_t<Image> source = new Image(stream);
|
||||
|
||||
EH << source->GetLastStatus();
|
||||
pfc::ptrholder_t<Bitmap> resized = new Bitmap(size.cx, size.cy, PixelFormat32bppARGB);
|
||||
EH << resized->GetLastStatus();
|
||||
|
||||
{
|
||||
pfc::ptrholder_t<Graphics> target = new Graphics(resized.get_ptr());
|
||||
EH << target->GetLastStatus();
|
||||
EH << target->SetInterpolationMode(InterpolationModeHighQuality);
|
||||
EH << target->Clear(Color(0,0,0,0));
|
||||
EH << target->DrawImage(source.get_ptr(), Rect(0,0,size.cx,size.cy));
|
||||
}
|
||||
|
||||
HICON icon = NULL;
|
||||
EH << resized->GetHICON(&icon);
|
||||
return icon;
|
||||
} catch(...) {
|
||||
PFC_ASSERT( !"Should not get here");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
static HICON GdiplusLoadPNGIcon(UINT id, CSize size) {return GdiplusLoadIcon(id, _T("PNG"), size);}
|
||||
|
||||
static HICON LoadPNGIcon(UINT id, CSize size) {
|
||||
GdiplusScope scope;
|
||||
return GdiplusLoadPNGIcon(id, size);
|
||||
}
|
||||
|
||||
static void GdiplusLoadButtonPNG(CIcon & icon, HWND btn_, UINT image) {
|
||||
CButton btn(btn_);
|
||||
if (icon == NULL) {
|
||||
CRect client; WIN32_OP_D( btn.GetClientRect(client) );
|
||||
CSize size = client.Size();
|
||||
int v = MulDiv(pfc::min_t<int>(size.cx, size.cy), 3, 4);
|
||||
if (v < 8) v = 8;
|
||||
icon = GdiplusLoadPNGIcon(image, CSize(v,v));
|
||||
}
|
||||
btn.SetIcon(icon);
|
||||
}
|
||||
|
||||
static Gdiplus::Bitmap * GdiplusLoadResource(UINT id, const TCHAR * resType) {
|
||||
using namespace Gdiplus;
|
||||
GdiplusErrorHandler EH;
|
||||
|
||||
auto stream = WinLoadResourceAsStream(GetThisModuleHandle(), MAKEINTRESOURCE(id), resType);
|
||||
pfc::ptrholder_t<Bitmap> img = new Bitmap(stream);
|
||||
EH << img->GetLastStatus();
|
||||
return img.detach();
|
||||
}
|
||||
|
||||
static Gdiplus::Bitmap * GdiplusLoadResourceAsSize(UINT id, const TCHAR * resType, CSize size) {
|
||||
using namespace Gdiplus;
|
||||
GdiplusErrorHandler EH;
|
||||
pfc::ptrholder_t<Bitmap> source = GdiplusLoadResource(id, resType);
|
||||
|
||||
pfc::ptrholder_t<Bitmap> resized = new Bitmap(size.cx, size.cy, PixelFormat32bppARGB);
|
||||
EH << resized->GetLastStatus();
|
||||
{
|
||||
pfc::ptrholder_t<Graphics> target = new Graphics(resized.get_ptr());
|
||||
EH << target->GetLastStatus();
|
||||
EH << target->SetInterpolationMode(InterpolationModeHighQuality);
|
||||
EH << target->DrawImage(source.get_ptr(), Rect(0, 0, size.cx, size.cy));
|
||||
}
|
||||
return resized.detach();
|
||||
}
|
||||
|
||||
static void GdiplusDimImage(Gdiplus::Bitmap * bmp) {
|
||||
using namespace Gdiplus;
|
||||
GdiplusErrorHandler EH;
|
||||
Gdiplus::Rect r(0, 0, bmp->GetWidth(), bmp->GetHeight());
|
||||
BitmapData data = {};
|
||||
EH << bmp->LockBits(&r, ImageLockModeRead | ImageLockModeWrite, PixelFormat32bppARGB, &data);
|
||||
|
||||
BYTE * scan = (BYTE*)data.Scan0;
|
||||
for (UINT y = 0; y < data.Height; ++y) {
|
||||
BYTE * px = scan;
|
||||
for (UINT x = 0; x < data.Width; ++x) {
|
||||
//px[0] = _dimPixel(px[0]);
|
||||
//px[1] = _dimPixel(px[1]);
|
||||
//px[2] = _dimPixel(px[2]);
|
||||
px[3] /= 3;
|
||||
px += 4;
|
||||
}
|
||||
scan += data.Stride;
|
||||
}
|
||||
|
||||
EH << bmp->UnlockBits(&data);
|
||||
}
|
||||
static void GdiplusInvertImage(Gdiplus::Bitmap * bmp) {
|
||||
using namespace Gdiplus;
|
||||
GdiplusErrorHandler EH;
|
||||
Gdiplus::Rect r(0, 0, bmp->GetWidth(), bmp->GetHeight());
|
||||
BitmapData data = {};
|
||||
EH << bmp->LockBits(&r, ImageLockModeRead | ImageLockModeWrite, PixelFormat32bppARGB, &data);
|
||||
|
||||
BYTE * scan = (BYTE*)data.Scan0;
|
||||
for (UINT y = 0; y < data.Height; ++y) {
|
||||
BYTE * px = scan;
|
||||
for (UINT x = 0; x < data.Width; ++x) {
|
||||
unsigned v = (unsigned)px[0] + (unsigned)px[1] + (unsigned)px[2];
|
||||
v /= 3;
|
||||
px[0] = px[1] = px[2] = 255 - v;
|
||||
px += 4;
|
||||
}
|
||||
scan += data.Stride;
|
||||
}
|
||||
|
||||
EH << bmp->UnlockBits(&data);
|
||||
}
|
||||
#pragma comment(lib, "gdiplus.lib")
|
||||
264
libPPUI/gesture.h
Normal file
264
libPPUI/gesture.h
Normal file
@@ -0,0 +1,264 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef WM_GESTURE
|
||||
|
||||
#define WM_GESTURE 0x0119
|
||||
#define WM_GESTURENOTIFY 0x011A
|
||||
|
||||
|
||||
DECLARE_HANDLE(HGESTUREINFO);
|
||||
|
||||
|
||||
/*
|
||||
* Gesture flags - GESTUREINFO.dwFlags
|
||||
*/
|
||||
#define GF_BEGIN 0x00000001
|
||||
#define GF_INERTIA 0x00000002
|
||||
#define GF_END 0x00000004
|
||||
|
||||
/*
|
||||
* Gesture IDs
|
||||
*/
|
||||
#define GID_BEGIN 1
|
||||
#define GID_END 2
|
||||
#define GID_ZOOM 3
|
||||
#define GID_PAN 4
|
||||
#define GID_ROTATE 5
|
||||
#define GID_TWOFINGERTAP 6
|
||||
#define GID_PRESSANDTAP 7
|
||||
#define GID_ROLLOVER GID_PRESSANDTAP
|
||||
|
||||
/*
|
||||
* Gesture information structure
|
||||
* - Pass the HGESTUREINFO received in the WM_GESTURE message lParam into the
|
||||
* GetGestureInfo function to retrieve this information.
|
||||
* - If cbExtraArgs is non-zero, pass the HGESTUREINFO received in the WM_GESTURE
|
||||
* message lParam into the GetGestureExtraArgs function to retrieve extended
|
||||
* argument information.
|
||||
*/
|
||||
typedef struct tagGESTUREINFO {
|
||||
UINT cbSize; // size, in bytes, of this structure (including variable length Args field)
|
||||
DWORD dwFlags; // see GF_* flags
|
||||
DWORD dwID; // gesture ID, see GID_* defines
|
||||
HWND hwndTarget; // handle to window targeted by this gesture
|
||||
POINTS ptsLocation; // current location of this gesture
|
||||
DWORD dwInstanceID; // internally used
|
||||
DWORD dwSequenceID; // internally used
|
||||
ULONGLONG ullArguments; // arguments for gestures whose arguments fit in 8 BYTES
|
||||
UINT cbExtraArgs; // size, in bytes, of extra arguments, if any, that accompany this gesture
|
||||
} GESTUREINFO, *PGESTUREINFO;
|
||||
typedef GESTUREINFO const * PCGESTUREINFO;
|
||||
|
||||
|
||||
/*
|
||||
* Gesture notification structure
|
||||
* - The WM_GESTURENOTIFY message lParam contains a pointer to this structure.
|
||||
* - The WM_GESTURENOTIFY message notifies a window that gesture recognition is
|
||||
* in progress and a gesture will be generated if one is recognized under the
|
||||
* current gesture settings.
|
||||
*/
|
||||
typedef struct tagGESTURENOTIFYSTRUCT {
|
||||
UINT cbSize; // size, in bytes, of this structure
|
||||
DWORD dwFlags; // unused
|
||||
HWND hwndTarget; // handle to window targeted by the gesture
|
||||
POINTS ptsLocation; // starting location
|
||||
DWORD dwInstanceID; // internally used
|
||||
} GESTURENOTIFYSTRUCT, *PGESTURENOTIFYSTRUCT;
|
||||
|
||||
/*
|
||||
* Gesture argument helpers
|
||||
* - Angle should be a double in the range of -2pi to +2pi
|
||||
* - Argument should be an unsigned 16-bit value
|
||||
*/
|
||||
#define GID_ROTATE_ANGLE_TO_ARGUMENT(_arg_) ((USHORT)((((_arg_) + 2.0 * 3.14159265) / (4.0 * 3.14159265)) * 65535.0))
|
||||
#define GID_ROTATE_ANGLE_FROM_ARGUMENT(_arg_) ((((double)(_arg_) / 65535.0) * 4.0 * 3.14159265) - 2.0 * 3.14159265)
|
||||
|
||||
/*
|
||||
* Gesture information retrieval
|
||||
* - HGESTUREINFO is received by a window in the lParam of a WM_GESTURE message.
|
||||
*/
|
||||
WINUSERAPI
|
||||
BOOL
|
||||
WINAPI
|
||||
GetGestureInfo(
|
||||
__in HGESTUREINFO hGestureInfo,
|
||||
__out PGESTUREINFO pGestureInfo);
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Gesture extra arguments retrieval
|
||||
* - HGESTUREINFO is received by a window in the lParam of a WM_GESTURE message.
|
||||
* - Size, in bytes, of the extra argument data is available in the cbExtraArgs
|
||||
* field of the GESTUREINFO structure retrieved using the GetGestureInfo function.
|
||||
*/
|
||||
WINUSERAPI
|
||||
BOOL
|
||||
WINAPI
|
||||
GetGestureExtraArgs(
|
||||
__in HGESTUREINFO hGestureInfo,
|
||||
__in UINT cbExtraArgs,
|
||||
__out_bcount(cbExtraArgs) PBYTE pExtraArgs);
|
||||
|
||||
/*
|
||||
* Gesture information handle management
|
||||
* - If an application processes the WM_GESTURE message, then once it is done
|
||||
* with the associated HGESTUREINFO, the application is responsible for
|
||||
* closing the handle using this function. Failure to do so may result in
|
||||
* process memory leaks.
|
||||
* - If the message is instead passed to DefWindowProc, or is forwarded using
|
||||
* one of the PostMessage or SendMessage class of API functions, the handle
|
||||
* is transfered with the message and need not be closed by the application.
|
||||
*/
|
||||
WINUSERAPI
|
||||
BOOL
|
||||
WINAPI
|
||||
CloseGestureInfoHandle(
|
||||
__in HGESTUREINFO hGestureInfo);
|
||||
|
||||
|
||||
/*
|
||||
* Gesture configuration structure
|
||||
* - Used in SetGestureConfig and GetGestureConfig
|
||||
* - Note that any setting not included in either GESTURECONFIG.dwWant or
|
||||
* GESTURECONFIG.dwBlock will use the parent window's preferences or
|
||||
* system defaults.
|
||||
*/
|
||||
typedef struct tagGESTURECONFIG {
|
||||
DWORD dwID; // gesture ID
|
||||
DWORD dwWant; // settings related to gesture ID that are to be turned on
|
||||
DWORD dwBlock; // settings related to gesture ID that are to be turned off
|
||||
} GESTURECONFIG, *PGESTURECONFIG;
|
||||
|
||||
/*
|
||||
* Gesture configuration flags - GESTURECONFIG.dwWant or GESTURECONFIG.dwBlock
|
||||
*/
|
||||
|
||||
/*
|
||||
* Common gesture configuration flags - set GESTURECONFIG.dwID to zero
|
||||
*/
|
||||
#define GC_ALLGESTURES 0x00000001
|
||||
|
||||
/*
|
||||
* Zoom gesture configuration flags - set GESTURECONFIG.dwID to GID_ZOOM
|
||||
*/
|
||||
#define GC_ZOOM 0x00000001
|
||||
|
||||
/*
|
||||
* Pan gesture configuration flags - set GESTURECONFIG.dwID to GID_PAN
|
||||
*/
|
||||
#define GC_PAN 0x00000001
|
||||
#define GC_PAN_WITH_SINGLE_FINGER_VERTICALLY 0x00000002
|
||||
#define GC_PAN_WITH_SINGLE_FINGER_HORIZONTALLY 0x00000004
|
||||
#define GC_PAN_WITH_GUTTER 0x00000008
|
||||
#define GC_PAN_WITH_INERTIA 0x00000010
|
||||
|
||||
/*
|
||||
* Rotate gesture configuration flags - set GESTURECONFIG.dwID to GID_ROTATE
|
||||
*/
|
||||
#define GC_ROTATE 0x00000001
|
||||
|
||||
/*
|
||||
* Two finger tap gesture configuration flags - set GESTURECONFIG.dwID to GID_TWOFINGERTAP
|
||||
*/
|
||||
#define GC_TWOFINGERTAP 0x00000001
|
||||
|
||||
/*
|
||||
* PressAndTap gesture configuration flags - set GESTURECONFIG.dwID to GID_PRESSANDTAP
|
||||
*/
|
||||
#define GC_PRESSANDTAP 0x00000001
|
||||
#define GC_ROLLOVER GC_PRESSANDTAP
|
||||
|
||||
#define GESTURECONFIGMAXCOUNT 256 // Maximum number of gestures that can be included
|
||||
// in a single call to SetGestureConfig / GetGestureConfig
|
||||
|
||||
WINUSERAPI
|
||||
BOOL
|
||||
WINAPI
|
||||
SetGestureConfig(
|
||||
__in HWND hwnd, // window for which configuration is specified
|
||||
__in DWORD dwReserved, // reserved, must be 0
|
||||
__in UINT cIDs, // count of GESTURECONFIG structures
|
||||
__in_ecount(cIDs) PGESTURECONFIG pGestureConfig, // array of GESTURECONFIG structures, dwIDs will be processed in the
|
||||
// order specified and repeated occurances will overwrite previous ones
|
||||
__in UINT cbSize); // sizeof(GESTURECONFIG)
|
||||
|
||||
|
||||
|
||||
|
||||
#define GCF_INCLUDE_ANCESTORS 0x00000001 // If specified, GetGestureConfig returns consolidated configuration
|
||||
// for the specified window and it's parent window chain
|
||||
|
||||
WINUSERAPI
|
||||
BOOL
|
||||
WINAPI
|
||||
GetGestureConfig(
|
||||
__in HWND hwnd, // window for which configuration is required
|
||||
__in DWORD dwReserved, // reserved, must be 0
|
||||
__in DWORD dwFlags, // see GCF_* flags
|
||||
__in PUINT pcIDs, // *pcIDs contains the size, in number of GESTURECONFIG structures,
|
||||
// of the buffer pointed to by pGestureConfig
|
||||
__inout_ecount(*pcIDs) PGESTURECONFIG pGestureConfig,
|
||||
// pointer to buffer to receive the returned array of GESTURECONFIG structures
|
||||
__in UINT cbSize); // sizeof(GESTURECONFIG)
|
||||
|
||||
|
||||
|
||||
typedef BOOL
|
||||
(WINAPI *pGetGestureInfo_t)(
|
||||
__in HGESTUREINFO hGestureInfo,
|
||||
__out PGESTUREINFO pGestureInfo);
|
||||
|
||||
typedef BOOL
|
||||
(WINAPI * pCloseGestureInfoHandle_t)(
|
||||
__in HGESTUREINFO hGestureInfo);
|
||||
|
||||
typedef BOOL
|
||||
(WINAPI * pSetGestureConfig_t) (
|
||||
__in HWND hwnd, // window for which configuration is specified
|
||||
__in DWORD dwReserved, // reserved, must be 0
|
||||
__in UINT cIDs, // count of GESTURECONFIG structures
|
||||
__in_ecount(cIDs) PGESTURECONFIG pGestureConfig, // array of GESTURECONFIG structures, dwIDs will be processed in the
|
||||
// order specified and repeated occurances will overwrite previous ones
|
||||
__in UINT cbSize); // sizeof(GESTURECONFIG)
|
||||
|
||||
class CGestureAPI {
|
||||
public:
|
||||
CGestureAPI() {
|
||||
HMODULE dll = GetModuleHandle(_T("user32.dll"));
|
||||
|
||||
Bind(GetGestureInfo, dll, "GetGestureInfo");
|
||||
Bind(CloseGestureInfoHandle, dll, "CloseGestureInfoHandle");
|
||||
Bind(SetGestureConfig, dll, "SetGestureConfig");
|
||||
}
|
||||
|
||||
bool IsAvailable() {
|
||||
return this->GetGestureInfo != NULL;
|
||||
}
|
||||
|
||||
pGetGestureInfo_t GetGestureInfo;
|
||||
pCloseGestureInfoHandle_t CloseGestureInfoHandle;
|
||||
pSetGestureConfig_t SetGestureConfig;
|
||||
private:
|
||||
template<typename func_t> static void Bind(func_t & f, HMODULE dll, const char * name) {
|
||||
f = reinterpret_cast<func_t>(GetProcAddress(dll, name));
|
||||
}
|
||||
};
|
||||
#else
|
||||
|
||||
class CGestureAPI {
|
||||
public:
|
||||
inline static bool IsAvailable() { return true; }
|
||||
inline static BOOL GetGestureInfo(HGESTUREINFO hGestureInfo, PGESTUREINFO pGestureInfo) {
|
||||
return ::GetGestureInfo(hGestureInfo, pGestureInfo);
|
||||
}
|
||||
inline static BOOL CloseGestureInfoHandle(HGESTUREINFO hGestureInfo) {
|
||||
return ::CloseGestureInfoHandle(hGestureInfo);
|
||||
}
|
||||
|
||||
inline static BOOL SetGestureConfig(HWND hwnd, DWORD dwReserved, UINT cIDs, PGESTURECONFIG pGestureConfig, UINT cbSize) {
|
||||
return ::SetGestureConfig(hwnd, dwReserved, cIDs, pGestureConfig, cbSize);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
17
libPPUI/libPPUI-license.txt
Normal file
17
libPPUI/libPPUI-license.txt
Normal file
@@ -0,0 +1,17 @@
|
||||
Copyright (C) 2002-2021 Peter Pawlowski
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
3
libPPUI/libPPUI-readme.txt
Normal file
3
libPPUI/libPPUI-readme.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
libPPUI
|
||||
|
||||
A library of user interface classes used by foobar2000 codebase; freely available for anyone to use in their programming projects.
|
||||
260
libPPUI/libPPUI.vcxproj
Normal file
260
libPPUI/libPPUI.vcxproj
Normal file
@@ -0,0 +1,260 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>15.0</VCProjectVersion>
|
||||
<ProjectGuid>{7729EB82-4069-4414-964B-AD399091A03F}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>libPPUI</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<AdditionalIncludeDirectories>..</AdditionalIncludeDirectories>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<TreatSpecificWarningsAsErrors>4715</TreatSpecificWarningsAsErrors>
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<FloatingPointModel>Fast</FloatingPointModel>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<AdditionalIncludeDirectories>..</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MinSpace</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<AdditionalIncludeDirectories>..</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<TreatSpecificWarningsAsErrors>4715</TreatSpecificWarningsAsErrors>
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<FloatingPointModel>Fast</FloatingPointModel>
|
||||
<OmitFramePointers>true</OmitFramePointers>
|
||||
<AdditionalOptions>/d2notypeopt %(AdditionalOptions)</AdditionalOptions>
|
||||
<PreprocessorDefinitions>NDEBUG;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<FloatingPointModel>Fast</FloatingPointModel>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<AdditionalIncludeDirectories>..</AdditionalIncludeDirectories>
|
||||
<AdditionalOptions>/d2notypeopt %(AdditionalOptions)</AdditionalOptions>
|
||||
<PreprocessorDefinitions>NDEBUG;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="AutoComplete.h" />
|
||||
<ClInclude Include="CButtonLite.h" />
|
||||
<ClInclude Include="CDialogResizeHelperCompat.h" />
|
||||
<ClInclude Include="CEditWithButtons.h" />
|
||||
<ClInclude Include="CEnumString.h" />
|
||||
<ClInclude Include="CFlashWindow.h" />
|
||||
<ClInclude Include="CHeaderCtrlEx.h" />
|
||||
<ClInclude Include="CIconOverlayWindow.h" />
|
||||
<ClInclude Include="clipboard.h" />
|
||||
<ClInclude Include="CListAccessible.h" />
|
||||
<ClInclude Include="CListControl-Cell.h" />
|
||||
<ClInclude Include="CListControl-Cells-Compat.h" />
|
||||
<ClInclude Include="CListControl-Cells.h" />
|
||||
<ClInclude Include="CListControl.h" />
|
||||
<ClInclude Include="CListControlComplete.h" />
|
||||
<ClInclude Include="CListControlHeaderImpl.h" />
|
||||
<ClInclude Include="CListControlOwnerData.h" />
|
||||
<ClInclude Include="CListControlSimple.h" />
|
||||
<ClInclude Include="CListControlTruncationTooltipImpl.h" />
|
||||
<ClInclude Include="CListControlUserOptions.h" />
|
||||
<ClInclude Include="CListControlWithSelection.h" />
|
||||
<ClInclude Include="CListControl_EditImpl.h" />
|
||||
<ClInclude Include="CListViewCtrlEx.h" />
|
||||
<ClInclude Include="CMiddleDragImpl.h" />
|
||||
<ClInclude Include="commandline_parser.h" />
|
||||
<ClInclude Include="Controls.h" />
|
||||
<ClInclude Include="CPopupTooltipMessage.h" />
|
||||
<ClInclude Include="CPowerRequest.h" />
|
||||
<ClInclude Include="CPropVariant.h" />
|
||||
<ClInclude Include="CWindowCreateAndDelete.h" />
|
||||
<ClInclude Include="CDialogResizeHelper.h" />
|
||||
<ClInclude Include="gdiplus-helpers-webp.h" />
|
||||
<ClInclude Include="gdiplus_helpers.h" />
|
||||
<ClInclude Include="GDIUtils.h" />
|
||||
<ClInclude Include="gesture.h" />
|
||||
<ClInclude Include="IDataObjectUtils.h" />
|
||||
<ClInclude Include="InPlaceEdit.h" />
|
||||
<ClInclude Include="InPlaceEditTable.h" />
|
||||
<ClInclude Include="link-CommonControls6.h" />
|
||||
<ClInclude Include="listview_helper.h" />
|
||||
<ClInclude Include="PaintUtils.h" />
|
||||
<ClInclude Include="pp-COM-macros.h" />
|
||||
<ClInclude Include="ppresources.h" />
|
||||
<ClInclude Include="SmartStrStr.h" />
|
||||
<ClInclude Include="stdafx.h" />
|
||||
<ClInclude Include="targetver.h" />
|
||||
<ClInclude Include="TreeMultiSel.h" />
|
||||
<ClInclude Include="TypeFind.h" />
|
||||
<ClInclude Include="win32_op.h" />
|
||||
<ClInclude Include="win32_utility.h" />
|
||||
<ClInclude Include="wtl-pp.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="AutoComplete.cpp" />
|
||||
<ClCompile Include="CEditWithButtons.cpp" />
|
||||
<ClCompile Include="clipboard.cpp" />
|
||||
<ClCompile Include="CListAccessible.cpp" />
|
||||
<ClCompile Include="CListControl-Cells.cpp" />
|
||||
<ClCompile Include="CListControl.cpp" />
|
||||
<ClCompile Include="CListControlHeaderImpl.cpp" />
|
||||
<ClCompile Include="CListControlTruncationTooltipImpl.cpp" />
|
||||
<ClCompile Include="CListControlWithSelection.cpp" />
|
||||
<ClCompile Include="CMiddleDragImpl.cpp" />
|
||||
<ClCompile Include="commandline_parser.cpp" />
|
||||
<ClCompile Include="Controls.cpp" />
|
||||
<ClCompile Include="CDialogResizeHelper.cpp" />
|
||||
<ClCompile Include="CPowerRequest.cpp" />
|
||||
<ClCompile Include="IDataObjectUtils.cpp" />
|
||||
<ClCompile Include="InPlaceCombo.cpp" />
|
||||
<ClCompile Include="InPlaceEdit.cpp" />
|
||||
<ClCompile Include="InPlaceEditTable.cpp" />
|
||||
<ClCompile Include="listview_helper.cpp" />
|
||||
<ClCompile Include="PaintUtils.cpp" />
|
||||
<ClCompile Include="SmartStrStr.cpp" />
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TypeFind.cpp" />
|
||||
<ClCompile Include="win32_op.cpp" />
|
||||
<ClCompile Include="win32_utility.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Text Include="IDI_SCROLL.txt" />
|
||||
<Text Include="libPPUI-license.txt" />
|
||||
<Text Include="libPPUI-readme.txt" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Image Include="IDI_SCROLL.ico" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
260
libPPUI/libPPUI.vcxproj.filters
Normal file
260
libPPUI/libPPUI.vcxproj.filters
Normal file
@@ -0,0 +1,260 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resources">
|
||||
<UniqueIdentifier>{b5bb0d58-0b1e-442d-a84d-09288c375185}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="stdafx.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="targetver.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CEditWithButtons.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CFlashWindow.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CIconOverlayWindow.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CMiddleDragImpl.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CButtonLite.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ppresources.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="win32_op.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="win32_utility.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="wtl-pp.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CListAccessible.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CListControl.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CListControl_EditImpl.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CListControlWithSelection.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="gesture.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="PaintUtils.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="pp-COM-macros.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="gdiplus_helpers.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GDIUtils.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="IDataObjectUtils.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SmartStrStr.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CListControlUserOptions.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="InPlaceEdit.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="InPlaceEditTable.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="AutoComplete.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CWindowCreateAndDelete.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CEnumString.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="listview_helper.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Controls.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CDialogResizeHelper.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CDialogResizeHelperCompat.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="clipboard.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CPropVariant.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CPowerRequest.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TypeFind.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CPopupTooltipMessage.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CListViewCtrlEx.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CHeaderCtrlEx.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="link-CommonControls6.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CListControlComplete.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CListControlSimple.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CListControlOwnerData.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TreeMultiSel.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CListControlHeaderImpl.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CListControlTruncationTooltipImpl.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CListControl-Cells.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CListControl-Cells-Compat.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CListControl-Cell.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="gdiplus-helpers-webp.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="commandline_parser.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CEditWithButtons.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CMiddleDragImpl.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="win32_op.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="win32_utility.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CListAccessible.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CListControl.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CListControlHeaderImpl.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CListControlTruncationTooltipImpl.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CListControlWithSelection.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="PaintUtils.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="IDataObjectUtils.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SmartStrStr.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="InPlaceEdit.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="InPlaceEditTable.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="AutoComplete.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="listview_helper.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Controls.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CDialogResizeHelper.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="clipboard.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CPowerRequest.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TypeFind.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CListControl-Cells.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="commandline_parser.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="InPlaceCombo.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Text Include="libPPUI-license.txt" />
|
||||
<Text Include="libPPUI-readme.txt" />
|
||||
<Text Include="IDI_SCROLL.txt">
|
||||
<Filter>Resources</Filter>
|
||||
</Text>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Image Include="IDI_SCROLL.ico">
|
||||
<Filter>Resources</Filter>
|
||||
</Image>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
3
libPPUI/link-CommonControls6.h
Normal file
3
libPPUI/link-CommonControls6.h
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' processorArchitecture='*'\"")
|
||||
284
libPPUI/listview_helper.cpp
Normal file
284
libPPUI/listview_helper.cpp
Normal file
@@ -0,0 +1,284 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "win32_utility.h"
|
||||
#include "win32_op.h"
|
||||
#include "listview_helper.h"
|
||||
#include "CListViewCtrlEx.h"
|
||||
#include "CHeaderCtrlEx.h"
|
||||
|
||||
namespace listview_helper {
|
||||
|
||||
unsigned insert_item(HWND p_listview,unsigned p_index,const char * p_name,LPARAM p_param)
|
||||
{
|
||||
if (p_index == ~0) p_index = ListView_GetItemCount(p_listview);
|
||||
LVITEM item = {};
|
||||
|
||||
pfc::stringcvt::string_os_from_utf8 os_string_temp(p_name);
|
||||
|
||||
item.mask = LVIF_TEXT | LVIF_PARAM;
|
||||
item.iItem = p_index;
|
||||
item.lParam = p_param;
|
||||
item.pszText = const_cast<TCHAR*>(os_string_temp.get_ptr());
|
||||
|
||||
LRESULT ret = SendMessage(p_listview,LVM_INSERTITEM,0,(LPARAM)&item);
|
||||
if (ret < 0) return ~0;
|
||||
else return (unsigned) ret;
|
||||
}
|
||||
|
||||
unsigned insert_item2(HWND p_listview, unsigned p_index, const char * col0, const char * col1, LPARAM p_param) {
|
||||
unsigned i = insert_item( p_listview, p_index, col0, p_param );
|
||||
if (i != ~0) {
|
||||
set_item_text( p_listview, i, 1, col1 );
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
unsigned insert_item3(HWND p_listview, unsigned p_index, const char * col0, const char * col1, const char * col2, LPARAM p_param) {
|
||||
unsigned i = insert_item( p_listview, p_index, col0, p_param );
|
||||
if (i != ~0) {
|
||||
set_item_text( p_listview, i, 1, col1 );
|
||||
set_item_text( p_listview, i, 2, col2 );
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
unsigned insert_column(HWND p_listview,unsigned p_index,const char * p_name,unsigned p_width_dlu)
|
||||
{
|
||||
pfc::stringcvt::string_os_from_utf8 os_string_temp(p_name);
|
||||
|
||||
RECT rect = {0,0,(LONG)p_width_dlu,0};
|
||||
MapDialogRect(GetParent(p_listview),&rect);
|
||||
|
||||
LVCOLUMN data = {};
|
||||
data.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_FMT;
|
||||
data.fmt = LVCFMT_LEFT;
|
||||
data.cx = rect.right;
|
||||
data.pszText = const_cast<TCHAR*>(os_string_temp.get_ptr());
|
||||
|
||||
LRESULT ret = SendMessage(p_listview,LVM_INSERTCOLUMN,p_index,(LPARAM)&data);
|
||||
if (ret < 0) return UINT_MAX;
|
||||
else return (unsigned) ret;
|
||||
}
|
||||
|
||||
void get_item_text(HWND p_listview,unsigned p_index,unsigned p_column,pfc::string_base & p_out) {
|
||||
enum {buffer_length = 1024*64};
|
||||
pfc::array_t<TCHAR> buffer; buffer.set_size(buffer_length);
|
||||
ListView_GetItemText(p_listview,p_index,p_column,buffer.get_ptr(),buffer_length);
|
||||
p_out = pfc::stringcvt::string_utf8_from_os(buffer.get_ptr(),buffer_length);
|
||||
}
|
||||
|
||||
bool set_item_text(HWND p_listview,unsigned p_index,unsigned p_column,const char * p_name)
|
||||
{
|
||||
LVITEM item = {};
|
||||
|
||||
pfc::stringcvt::string_os_from_utf8 os_string_temp(p_name);
|
||||
|
||||
item.mask = LVIF_TEXT;
|
||||
item.iItem = p_index;
|
||||
item.iSubItem = p_column;
|
||||
item.pszText = const_cast<TCHAR*>(os_string_temp.get_ptr());
|
||||
return SendMessage(p_listview,LVM_SETITEM,0,(LPARAM)&item) ? true : false;
|
||||
}
|
||||
|
||||
bool is_item_selected(HWND p_listview,unsigned p_index)
|
||||
{
|
||||
LVITEM item = {};
|
||||
item.mask = LVIF_STATE;
|
||||
item.iItem = p_index;
|
||||
item.stateMask = LVIS_SELECTED;
|
||||
if (!SendMessage(p_listview,LVM_GETITEM,0,(LPARAM)&item)) return false;
|
||||
return (item.state & LVIS_SELECTED) ? true : false;
|
||||
}
|
||||
|
||||
void set_item_selection(HWND p_listview,unsigned p_index,bool p_state)
|
||||
{
|
||||
PFC_ASSERT( ::IsWindow(p_listview) );
|
||||
LVITEM item = {};
|
||||
item.stateMask = LVIS_SELECTED;
|
||||
item.state = p_state ? LVIS_SELECTED : 0;
|
||||
WIN32_OP_D( SendMessage(p_listview,LVM_SETITEMSTATE,(WPARAM)p_index,(LPARAM)&item) );
|
||||
}
|
||||
|
||||
bool select_single_item(HWND p_listview,unsigned p_index)
|
||||
{
|
||||
LRESULT temp = SendMessage(p_listview,LVM_GETITEMCOUNT,0,0);
|
||||
if (temp < 0) return false;
|
||||
ListView_SetSelectionMark(p_listview,p_index);
|
||||
unsigned n; const unsigned m = pfc::downcast_guarded<unsigned>(temp);
|
||||
for(n=0;n<m;n++) {
|
||||
enum {mask = LVIS_FOCUSED | LVIS_SELECTED};
|
||||
ListView_SetItemState(p_listview,n,n == p_index ? mask : 0, mask);
|
||||
}
|
||||
return ensure_visible(p_listview,p_index);
|
||||
}
|
||||
|
||||
bool ensure_visible(HWND p_listview,unsigned p_index)
|
||||
{
|
||||
return SendMessage(p_listview,LVM_ENSUREVISIBLE,p_index,FALSE) ? true : false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ListView_GetContextMenuPoint(HWND p_list,LPARAM p_coords,POINT & p_point,int & p_selection) {
|
||||
POINT pt = {(short)LOWORD(p_coords),(short)HIWORD(p_coords)};
|
||||
ListView_GetContextMenuPoint(p_list, pt, p_point, p_selection);
|
||||
}
|
||||
|
||||
void ListView_GetContextMenuPoint(HWND p_list,POINT p_coords,POINT & p_point,int & p_selection) {
|
||||
if (p_coords.x == -1 && p_coords.y == -1) {
|
||||
int firstsel = ListView_GetFirstSelection(p_list);
|
||||
if (firstsel >= 0) {
|
||||
ListView_EnsureVisible(p_list, firstsel, FALSE);
|
||||
RECT rect;
|
||||
WIN32_OP_D( ListView_GetItemRect(p_list,firstsel,&rect,LVIR_BOUNDS) );
|
||||
p_point.x = (rect.left + rect.right) / 2;
|
||||
p_point.y = (rect.top + rect.bottom) / 2;
|
||||
WIN32_OP_D( ClientToScreen(p_list,&p_point) );
|
||||
} else {
|
||||
RECT rect;
|
||||
WIN32_OP_D(GetClientRect(p_list,&rect));
|
||||
p_point.x = (rect.left + rect.right) / 2;
|
||||
p_point.y = (rect.top + rect.bottom) / 2;
|
||||
WIN32_OP_D(ClientToScreen(p_list,&p_point));
|
||||
}
|
||||
p_selection = firstsel;
|
||||
} else {
|
||||
POINT pt = p_coords; // {(short)LOWORD(p_coords),(short)HIWORD(p_coords)};
|
||||
p_point = pt;
|
||||
POINT client = pt;
|
||||
WIN32_OP_D( ScreenToClient(p_list,&client) );
|
||||
LVHITTESTINFO info = {};
|
||||
info.pt = client;
|
||||
p_selection = ListView_HitTest(p_list,&info);
|
||||
}
|
||||
}
|
||||
|
||||
int ListView_GetColumnCount(HWND listView) {
|
||||
HWND header = ListView_GetHeader(listView);
|
||||
PFC_ASSERT(header != NULL);
|
||||
return Header_GetItemCount(header);
|
||||
}
|
||||
|
||||
void ListView_FixContextMenuPoint(CListViewCtrl list, CPoint & coords) {
|
||||
if (coords == CPoint(-1, -1)) {
|
||||
int selWalk = -1;
|
||||
CRect rcClient; WIN32_OP_D(list.GetClientRect(rcClient));
|
||||
for (;;) {
|
||||
selWalk = list.GetNextItem(selWalk, LVNI_SELECTED);
|
||||
if (selWalk < 0) {
|
||||
CRect rc;
|
||||
WIN32_OP_D(list.GetWindowRect(&rc));
|
||||
coords = rc.CenterPoint();
|
||||
return;
|
||||
}
|
||||
CRect rcItem, rcVisible;
|
||||
WIN32_OP_D(list.GetItemRect(selWalk, &rcItem, LVIR_BOUNDS));
|
||||
if (rcVisible.IntersectRect(rcItem, rcClient)) {
|
||||
coords = rcVisible.CenterPoint();
|
||||
WIN32_OP_D(list.ClientToScreen(&coords));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned CListViewCtrlEx::GetColunnCount() {
|
||||
return (unsigned) ListView_GetColumnCount( *this );
|
||||
}
|
||||
unsigned CListViewCtrlEx::AddColumnEx(const wchar_t * name, unsigned widthDLU) {
|
||||
return InsertColumnEx( GetColunnCount(), name, widthDLU );
|
||||
}
|
||||
unsigned CListViewCtrlEx::InsertColumnEx(unsigned index, const wchar_t * name, unsigned widthDLU) {
|
||||
|
||||
RECT rect = { 0,0,(LONG)widthDLU,0 };
|
||||
MapDialogRect(GetParent(), &rect);
|
||||
|
||||
LVCOLUMN data = {};
|
||||
data.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_FMT;
|
||||
data.fmt = LVCFMT_LEFT;
|
||||
data.cx = rect.right;
|
||||
data.pszText = const_cast<wchar_t*>(name);
|
||||
|
||||
auto ret = this->InsertColumn(index, & data );
|
||||
if (ret < 0) return UINT_MAX;
|
||||
else return (unsigned)ret;
|
||||
}
|
||||
|
||||
void CListViewCtrlEx::FixContextMenuPoint(CPoint & pt) {
|
||||
ListView_FixContextMenuPoint(*this, pt);
|
||||
}
|
||||
|
||||
|
||||
|
||||
unsigned CListViewCtrlEx::InsertString(unsigned index, const wchar_t * str) {
|
||||
LVITEM item = {};
|
||||
|
||||
item.mask = LVIF_TEXT;
|
||||
item.iItem = index;
|
||||
item.pszText = const_cast<wchar_t*>(str);
|
||||
|
||||
auto ret = InsertItem(&item);
|
||||
if ( ret < 0 ) return UINT_MAX;
|
||||
else return (unsigned) ret;
|
||||
}
|
||||
unsigned CListViewCtrlEx::InsertString8(unsigned index, const char * str) {
|
||||
return InsertString(index, pfc::stringcvt::string_os_from_utf8( str ) );
|
||||
}
|
||||
unsigned CListViewCtrlEx::AddString(const wchar_t * str) {
|
||||
return InsertString(GetItemCount(), str);
|
||||
}
|
||||
unsigned CListViewCtrlEx::AddString8(const char * str) {
|
||||
return AddString(pfc::stringcvt::string_os_from_utf8( str ) );
|
||||
}
|
||||
void CListViewCtrlEx::SetItemText(unsigned iItem, unsigned iSubItem, const wchar_t * str) {
|
||||
LVITEM item = {};
|
||||
item.mask = LVIF_TEXT;
|
||||
item.iItem = iItem;
|
||||
item.iSubItem = iSubItem;
|
||||
item.pszText = const_cast<wchar_t*>(str);
|
||||
SetItem(&item);
|
||||
}
|
||||
void CListViewCtrlEx::SetItemText8(unsigned item, unsigned subItem, const char * str) {
|
||||
SetItemText( item, subItem, pfc::stringcvt::string_os_from_utf8( str ) );
|
||||
}
|
||||
|
||||
|
||||
DWORD CHeaderCtrlEx::GetItemFormat(int iItem) {
|
||||
HDITEM item = {};
|
||||
item.mask = HDI_FORMAT;
|
||||
if (!this->GetItem(iItem, &item)) return 0;
|
||||
return item.fmt;
|
||||
}
|
||||
void CHeaderCtrlEx::SetItemFormat(int iItem, DWORD flags) {
|
||||
HDITEM item = {};
|
||||
item.mask = HDI_FORMAT;
|
||||
item.fmt = flags;
|
||||
SetItem(iItem, &item);
|
||||
}
|
||||
|
||||
void CHeaderCtrlEx::SetItemSort(int iItem, int direction) {
|
||||
DWORD fmtWas = GetItemFormat(iItem);
|
||||
DWORD fmt = fmtWas & ~(HDF_SORTDOWN | HDF_SORTUP);
|
||||
if (direction > 0) fmt |= HDF_SORTDOWN;
|
||||
else if (direction < 0) fmt |= HDF_SORTUP;
|
||||
if (fmt != fmtWas) SetItemFormat(iItem, fmt);
|
||||
}
|
||||
|
||||
void CHeaderCtrlEx::SetSingleItemSort(int iItem, int direction) {
|
||||
const int total = GetItemCount();
|
||||
for (int walk = 0; walk < total; ++walk) {
|
||||
SetItemSort(walk, walk == iItem ? direction : 0);
|
||||
}
|
||||
}
|
||||
|
||||
void CHeaderCtrlEx::ClearSort() {
|
||||
SetSingleItemSort(-1,0);
|
||||
}
|
||||
|
||||
int CListViewCtrlEx::AddGroup(int iGroupID, const wchar_t * header) {
|
||||
LVGROUP g = { sizeof(g) };
|
||||
g.mask = LVGF_HEADER | LVGF_GROUPID;
|
||||
g.pszHeader = const_cast<wchar_t*>( header );
|
||||
g.iGroupId = iGroupID;
|
||||
return __super::AddGroup(&g);
|
||||
}
|
||||
49
libPPUI/listview_helper.h
Normal file
49
libPPUI/listview_helper.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
namespace listview_helper
|
||||
{
|
||||
unsigned insert_item(HWND p_listview,unsigned p_index,const char * p_name,LPARAM p_param);//returns index of new item on success, infinite on failure
|
||||
|
||||
unsigned insert_column(HWND p_listview,unsigned p_index,const char * p_name,unsigned p_width_dlu);//returns index of new item on success, infinite on failure
|
||||
|
||||
bool set_item_text(HWND p_listview,unsigned p_index,unsigned p_column,const char * p_name);
|
||||
|
||||
bool is_item_selected(HWND p_listview,unsigned p_index);
|
||||
|
||||
void set_item_selection(HWND p_listview,unsigned p_index,bool p_state);
|
||||
|
||||
bool select_single_item(HWND p_listview,unsigned p_index);
|
||||
|
||||
bool ensure_visible(HWND p_listview,unsigned p_index);
|
||||
|
||||
void get_item_text(HWND p_listview,unsigned p_index,unsigned p_column,pfc::string_base & p_out);
|
||||
|
||||
unsigned insert_item2(HWND p_listview, unsigned p_index, const char * col0, const char * col1, LPARAM p_param = 0);
|
||||
unsigned insert_item3(HWND p_listview, unsigned p_index, const char * col0, const char * col1, const char * col2, LPARAM p_param = 0);
|
||||
|
||||
|
||||
};
|
||||
|
||||
static int ListView_GetFirstSelection(HWND p_listview) {
|
||||
return ListView_GetNextItem(p_listview,-1,LVNI_SELECTED);
|
||||
}
|
||||
|
||||
static int ListView_GetSingleSelection(HWND p_listview) {
|
||||
if (ListView_GetSelectedCount(p_listview) != 1) return -1;
|
||||
return ListView_GetFirstSelection(p_listview);
|
||||
}
|
||||
|
||||
static int ListView_GetFocusItem(HWND p_listview) {
|
||||
return ListView_GetNextItem(p_listview,-1,LVNI_FOCUSED);
|
||||
}
|
||||
|
||||
static bool ListView_IsItemSelected(HWND p_listview,int p_index) {
|
||||
return ListView_GetItemState(p_listview,p_index,LVIS_SELECTED) != 0;
|
||||
}
|
||||
|
||||
void ListView_GetContextMenuPoint(HWND p_list,LPARAM p_coords,POINT & p_point,int & p_selection);
|
||||
void ListView_GetContextMenuPoint(HWND p_list,POINT p_coords,POINT & p_point,int & p_selection);
|
||||
|
||||
int ListView_GetColumnCount(HWND listView);
|
||||
|
||||
void ListView_FixContextMenuPoint(CListViewCtrl list, CPoint & coords);
|
||||
13
libPPUI/pp-COM-macros.h
Normal file
13
libPPUI/pp-COM-macros.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#define COM_QI_BEGIN() HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid,void ** ppvObject) { if (ppvObject == NULL) return E_INVALIDARG;
|
||||
#define COM_QI_ENTRY(IWhat) { if (iid == __uuidof(IWhat)) {IWhat * temp = this; temp->AddRef(); * ppvObject = temp; return S_OK;} }
|
||||
#define COM_QI_ENTRY_(IWhat, IID) { if (iid == IID) {IWhat * temp = this; temp->AddRef(); * ppvObject = temp; return S_OK;} }
|
||||
#define COM_QI_END() * ppvObject = NULL; return E_NOINTERFACE; }
|
||||
|
||||
#define COM_QI_CHAIN(Parent) { HRESULT status = Parent::QueryInterface(iid, ppvObject); if (SUCCEEDED(status)) return status; }
|
||||
|
||||
#define COM_QI_SIMPLE(IWhat) COM_QI_BEGIN() COM_QI_ENTRY(IUnknown) COM_QI_ENTRY(IWhat) COM_QI_END()
|
||||
|
||||
|
||||
#define PP_COM_CATCH catch(exception_com const & e) {return e.get_code();} catch(std::bad_alloc) {return E_OUTOFMEMORY;} catch(pfc::exception_invalid_params) {return E_INVALIDARG;} catch(...) {return E_UNEXPECTED;}
|
||||
10
libPPUI/ppresources.h
Normal file
10
libPPUI/ppresources.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
class CPPUIResources {
|
||||
public:
|
||||
static UINT get_IDI_SCROLL()
|
||||
#ifdef IDI_SCROLL
|
||||
{ return IDI_SCROLL; }
|
||||
#endif
|
||||
;
|
||||
};
|
||||
1
libPPUI/stdafx.cpp
Normal file
1
libPPUI/stdafx.cpp
Normal file
@@ -0,0 +1 @@
|
||||
#include "stdafx.h"
|
||||
21
libPPUI/stdafx.h
Normal file
21
libPPUI/stdafx.h
Normal file
@@ -0,0 +1,21 @@
|
||||
// stdafx.h : include file for standard system include files,
|
||||
// or project specific include files that are used frequently, but
|
||||
// are changed infrequently
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "targetver.h"
|
||||
|
||||
#define _SECURE_ATL 1
|
||||
|
||||
#include <atlbase.h>
|
||||
#include <atltypes.h>
|
||||
#include <atlstr.h>
|
||||
#include <atlapp.h>
|
||||
#include <atlctrls.h>
|
||||
#include <atlwin.h>
|
||||
#include <atlcom.h>
|
||||
#include <atlcrack.h>
|
||||
|
||||
#include <pfc/pfc.h>
|
||||
6
libPPUI/targetver.h
Normal file
6
libPPUI/targetver.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef _WIN32_WINNT
|
||||
#define _WIN32_WINNT 0x0601
|
||||
#include <SDKDDKVer.h>
|
||||
#endif
|
||||
36
libPPUI/win32_op.cpp
Normal file
36
libPPUI/win32_op.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#include "stdafx.h"
|
||||
#include "win32_op.h"
|
||||
#include <assert.h>
|
||||
|
||||
PFC_NORETURN PFC_NOINLINE void WIN32_OP_FAIL() {
|
||||
const DWORD code = GetLastError();
|
||||
PFC_ASSERT(code != NO_ERROR);
|
||||
throw exception_win32(code);
|
||||
}
|
||||
|
||||
PFC_NORETURN PFC_NOINLINE void WIN32_OP_FAIL_CRITICAL(const char * what) {
|
||||
#if PFC_DEBUG
|
||||
const DWORD code = GetLastError();
|
||||
PFC_ASSERT(code != NO_ERROR);
|
||||
#endif
|
||||
pfc::crash();
|
||||
#if 0
|
||||
pfc::string_formatter msg; msg << what << " failure #" << (uint32_t)code;
|
||||
TRACK_CODE(msg.get_ptr(), uBugCheck());
|
||||
#endif
|
||||
}
|
||||
|
||||
#if PFC_DEBUG
|
||||
void WIN32_OP_D_FAIL(const wchar_t * _Message, const wchar_t *_File, unsigned _Line) {
|
||||
const DWORD code = GetLastError();
|
||||
pfc::array_t<wchar_t> msgFormatted; msgFormatted.set_size(pfc::strlen_t(_Message) + 64);
|
||||
wsprintfW(msgFormatted.get_ptr(), L"%s (code: %u)", _Message, code);
|
||||
if (IsDebuggerPresent()) {
|
||||
OutputDebugString(TEXT("WIN32_OP_D() failure:\n"));
|
||||
OutputDebugString(msgFormatted.get_ptr());
|
||||
OutputDebugString(TEXT("\n"));
|
||||
pfc::crash();
|
||||
}
|
||||
_wassert(msgFormatted.get_ptr(), _File, _Line);
|
||||
}
|
||||
#endif
|
||||
36
libPPUI/win32_op.h
Normal file
36
libPPUI/win32_op.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
PFC_NORETURN PFC_NOINLINE void WIN32_OP_FAIL();
|
||||
PFC_NORETURN PFC_NOINLINE void WIN32_OP_FAIL_CRITICAL(const char * what);
|
||||
|
||||
#ifdef _DEBUG
|
||||
void WIN32_OP_D_FAIL(const wchar_t * _Message, const wchar_t *_File, unsigned _Line);
|
||||
#endif
|
||||
|
||||
//Throws an exception when (OP) evaluates to false/zero.
|
||||
#define WIN32_OP(OP) \
|
||||
{ \
|
||||
SetLastError(NO_ERROR); \
|
||||
if (!(OP)) WIN32_OP_FAIL(); \
|
||||
}
|
||||
|
||||
// Kills the application with appropriate debug info when (OP) evaluates to false/zero.
|
||||
#define WIN32_OP_CRITICAL(WHAT, OP) \
|
||||
{ \
|
||||
SetLastError(NO_ERROR); \
|
||||
if (!(OP)) WIN32_OP_FAIL_CRITICAL(WHAT); \
|
||||
}
|
||||
|
||||
//WIN32_OP_D() acts like an assert specialized for win32 operations in debug build, ignores the return value / error codes in release build.
|
||||
//Use WIN32_OP_D() instead of WIN32_OP() on operations that are extremely unlikely to fail, so failure condition checks are performed in the debug build only, to avoid bloating release code with pointless error checks.
|
||||
#ifdef _DEBUG
|
||||
#define WIN32_OP_D(OP) \
|
||||
{ \
|
||||
SetLastError(NO_ERROR); \
|
||||
if (!(OP)) WIN32_OP_D_FAIL(PFC_WIDESTRING(#OP), PFC_WIDESTRING(__FILE__), __LINE__); \
|
||||
}
|
||||
|
||||
#else
|
||||
#define WIN32_OP_D(OP) (void)( (OP), 0);
|
||||
#endif
|
||||
|
||||
218
libPPUI/win32_utility.cpp
Normal file
218
libPPUI/win32_utility.cpp
Normal file
@@ -0,0 +1,218 @@
|
||||
#include "stdafx.h"
|
||||
#include "win32_utility.h"
|
||||
#include "win32_op.h"
|
||||
|
||||
unsigned QueryScreenDPI(HWND wnd) {
|
||||
HDC dc = GetDC(wnd);
|
||||
unsigned ret = GetDeviceCaps(dc, LOGPIXELSY);
|
||||
ReleaseDC(wnd, dc);
|
||||
return ret;
|
||||
}
|
||||
unsigned QueryScreenDPI_X(HWND wnd) {
|
||||
HDC dc = GetDC(wnd);
|
||||
unsigned ret = GetDeviceCaps(dc, LOGPIXELSX);
|
||||
ReleaseDC(wnd, dc);
|
||||
return ret;
|
||||
}
|
||||
unsigned QueryScreenDPI_Y(HWND wnd) {
|
||||
HDC dc = GetDC(wnd);
|
||||
unsigned ret = GetDeviceCaps(dc, LOGPIXELSY);
|
||||
ReleaseDC(wnd, dc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
SIZE QueryScreenDPIEx(HWND wnd) {
|
||||
HDC dc = GetDC(wnd);
|
||||
SIZE ret = { GetDeviceCaps(dc,LOGPIXELSX), GetDeviceCaps(dc,LOGPIXELSY) };
|
||||
ReleaseDC(wnd, dc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void HeaderControl_SetSortIndicator(HWND header_, int column, bool isUp) {
|
||||
CHeaderCtrl header(header_);
|
||||
const int total = header.GetItemCount();
|
||||
for (int walk = 0; walk < total; ++walk) {
|
||||
HDITEM item = {}; item.mask = HDI_FORMAT;
|
||||
if (header.GetItem(walk, &item)) {
|
||||
DWORD newFormat = item.fmt;
|
||||
newFormat &= ~(HDF_SORTUP | HDF_SORTDOWN);
|
||||
if (walk == column) {
|
||||
newFormat |= isUp ? HDF_SORTUP : HDF_SORTDOWN;
|
||||
}
|
||||
if (newFormat != item.fmt) {
|
||||
item.fmt = newFormat;
|
||||
header.SetItem(walk, &item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HINSTANCE GetThisModuleHandle() {
|
||||
return (HINSTANCE)_AtlBaseModule.m_hInst;
|
||||
}
|
||||
|
||||
WinResourceRef_t WinLoadResource(HMODULE hMod, const TCHAR * name, const TCHAR * type, WORD wLang) {
|
||||
SetLastError(0);
|
||||
HRSRC res = wLang ? FindResourceEx(hMod, type, name, wLang) : FindResource(hMod, name, type);
|
||||
if ( res == NULL ) WIN32_OP_FAIL();
|
||||
SetLastError(0);
|
||||
HGLOBAL hglob = LoadResource(hMod, res);
|
||||
if ( hglob == NULL ) WIN32_OP_FAIL();
|
||||
SetLastError(0);
|
||||
void * ptr = LockResource(hglob);
|
||||
if ( ptr == nullptr ) WIN32_OP_FAIL();
|
||||
WinResourceRef_t ref;
|
||||
ref.ptr = ptr;
|
||||
ref.bytes = SizeofResource(hMod, res);
|
||||
return ref;
|
||||
}
|
||||
|
||||
CComPtr<IStream> WinLoadResourceAsStream(HMODULE hMod, const TCHAR * name, const TCHAR * type, WORD wLang) {
|
||||
auto res = WinLoadResource(hMod, name, type, wLang );
|
||||
auto str = SHCreateMemStream( (const BYTE*) res.ptr, (UINT) res.bytes );
|
||||
if ( str == nullptr ) throw std::bad_alloc();
|
||||
CComPtr<IStream> ret;
|
||||
ret.Attach( str );
|
||||
return ret;
|
||||
}
|
||||
|
||||
UINT GetFontHeight(HFONT font)
|
||||
{
|
||||
UINT ret;
|
||||
HDC dc = CreateCompatibleDC(0);
|
||||
SelectObject(dc, font);
|
||||
ret = GetTextHeight(dc);
|
||||
DeleteDC(dc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
UINT GetTextHeight(HDC dc)
|
||||
{
|
||||
TEXTMETRIC tm;
|
||||
POINT pt[2];
|
||||
GetTextMetrics(dc, &tm);
|
||||
pt[0].x = 0;
|
||||
pt[0].y = tm.tmHeight;
|
||||
pt[1].x = 0;
|
||||
pt[1].y = 0;
|
||||
LPtoDP(dc, pt, 2);
|
||||
|
||||
int ret = pt[0].y - pt[1].y;
|
||||
return ret > 1 ? (unsigned)ret : 1;
|
||||
}
|
||||
|
||||
|
||||
LRESULT RelayEraseBkgnd(HWND p_from, HWND p_to, HDC p_dc) {
|
||||
LRESULT status;
|
||||
POINT pt = { 0, 0 }, pt_old = { 0,0 };
|
||||
MapWindowPoints(p_from, p_to, &pt, 1);
|
||||
OffsetWindowOrgEx(p_dc, pt.x, pt.y, &pt_old);
|
||||
status = SendMessage(p_to, WM_ERASEBKGND, (WPARAM)p_dc, 0);
|
||||
SetWindowOrgEx(p_dc, pt_old.x, pt_old.y, 0);
|
||||
return status;
|
||||
}
|
||||
|
||||
pfc::string8 EscapeTooltipText(const char * src)
|
||||
{
|
||||
pfc::string8 out;
|
||||
while (*src)
|
||||
{
|
||||
if (*src == '&')
|
||||
{
|
||||
out.add_string("&&&");
|
||||
src++;
|
||||
while (*src == '&')
|
||||
{
|
||||
out.add_string("&&");
|
||||
src++;
|
||||
}
|
||||
} else out.add_byte(*(src++));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
bool IsMenuNonEmpty(HMENU menu) {
|
||||
unsigned n, m = GetMenuItemCount(menu);
|
||||
for (n = 0; n < m; n++) {
|
||||
if (GetSubMenu(menu, n)) return true;
|
||||
if (!(GetMenuState(menu, n, MF_BYPOSITION)&MF_SEPARATOR)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SetDefaultMenuItem(HMENU p_menu, unsigned p_id) {
|
||||
MENUITEMINFO info = { sizeof(info) };
|
||||
info.fMask = MIIM_STATE;
|
||||
GetMenuItemInfo(p_menu, p_id, FALSE, &info);
|
||||
info.fState |= MFS_DEFAULT;
|
||||
SetMenuItemInfo(p_menu, p_id, FALSE, &info);
|
||||
}
|
||||
|
||||
static bool running_under_wine(void) {
|
||||
HMODULE module = GetModuleHandle(_T("ntdll.dll"));
|
||||
if (!module) return false;
|
||||
return GetProcAddress(module, "wine_server_call") != NULL;
|
||||
}
|
||||
static bool FetchWineInfoAppend(pfc::string_base & out) {
|
||||
typedef const char *(__cdecl *t_wine_get_build_id)(void);
|
||||
typedef void(__cdecl *t_wine_get_host_version)(const char **sysname, const char **release);
|
||||
const HMODULE ntdll = GetModuleHandle(_T("ntdll.dll"));
|
||||
if (ntdll == NULL) return false;
|
||||
t_wine_get_build_id wine_get_build_id;
|
||||
t_wine_get_host_version wine_get_host_version;
|
||||
wine_get_build_id = (t_wine_get_build_id)GetProcAddress(ntdll, "wine_get_build_id");
|
||||
wine_get_host_version = (t_wine_get_host_version)GetProcAddress(ntdll, "wine_get_host_version");
|
||||
if (wine_get_build_id == NULL || wine_get_host_version == NULL) {
|
||||
if (GetProcAddress(ntdll, "wine_server_call") != NULL) {
|
||||
out << "wine (unknown version)";
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
const char * sysname = NULL; const char * release = NULL;
|
||||
wine_get_host_version(&sysname, &release);
|
||||
out << wine_get_build_id() << ", on: " << sysname << " / " << release;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void GetOSVersionStringAppend(pfc::string_base & out) {
|
||||
|
||||
if (FetchWineInfoAppend(out)) return;
|
||||
|
||||
OSVERSIONINFO ver = {}; ver.dwOSVersionInfoSize = sizeof(ver);
|
||||
WIN32_OP(GetVersionEx(&ver));
|
||||
SYSTEM_INFO info = {};
|
||||
GetNativeSystemInfo(&info);
|
||||
|
||||
out << "Windows " << (int)ver.dwMajorVersion << "." << (int)ver.dwMinorVersion << "." << (int)ver.dwBuildNumber;
|
||||
if (ver.szCSDVersion[0] != 0) out << " " << pfc::stringcvt::string_utf8_from_os(ver.szCSDVersion, PFC_TABSIZE(ver.szCSDVersion));
|
||||
|
||||
switch (info.wProcessorArchitecture) {
|
||||
case PROCESSOR_ARCHITECTURE_AMD64:
|
||||
out << " x64"; break;
|
||||
case PROCESSOR_ARCHITECTURE_IA64:
|
||||
out << " IA64"; break;
|
||||
case PROCESSOR_ARCHITECTURE_INTEL:
|
||||
out << " x86"; break;
|
||||
}
|
||||
}
|
||||
|
||||
void GetOSVersionString(pfc::string_base & out) {
|
||||
out.reset(); GetOSVersionStringAppend(out);
|
||||
}
|
||||
WORD GetOSVersionCode() {
|
||||
OSVERSIONINFO ver = {sizeof(ver)};
|
||||
WIN32_OP_D(GetVersionEx(&ver));
|
||||
|
||||
DWORD ret = ver.dwMinorVersion;
|
||||
ret += ver.dwMajorVersion << 8;
|
||||
|
||||
return (WORD)ret;
|
||||
}
|
||||
|
||||
|
||||
POINT GetCursorPos() {
|
||||
POINT pt;
|
||||
WIN32_OP_D( GetCursorPos(&pt) );
|
||||
return pt;
|
||||
}
|
||||
48
libPPUI/win32_utility.h
Normal file
48
libPPUI/win32_utility.h
Normal file
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include <atlcomcli.h> // CComPtr
|
||||
|
||||
unsigned QueryScreenDPI(HWND wnd = NULL);
|
||||
unsigned QueryScreenDPI_X(HWND wnd = NULL);
|
||||
unsigned QueryScreenDPI_Y(HWND wnd = NULL);
|
||||
|
||||
SIZE QueryScreenDPIEx(HWND wnd = NULL);
|
||||
|
||||
void HeaderControl_SetSortIndicator(HWND header, int column, bool isUp);
|
||||
|
||||
POINT GetCursorPos();
|
||||
|
||||
HINSTANCE GetThisModuleHandle();
|
||||
|
||||
struct WinResourceRef_t {
|
||||
const void * ptr;
|
||||
size_t bytes;
|
||||
} ;
|
||||
|
||||
WinResourceRef_t WinLoadResource(HMODULE hMod, const TCHAR * name, const TCHAR * type, WORD wLang = 0);
|
||||
CComPtr<IStream> WinLoadResourceAsStream(HMODULE hMod, const TCHAR * name, const TCHAR * type, WORD wLang = 0);
|
||||
|
||||
UINT GetFontHeight(HFONT font);
|
||||
UINT GetTextHeight(HDC dc);
|
||||
|
||||
LRESULT RelayEraseBkgnd(HWND p_from, HWND p_to, HDC p_dc);
|
||||
|
||||
pfc::string8 EscapeTooltipText(const char * text);
|
||||
|
||||
class CloseHandleScope {
|
||||
public:
|
||||
CloseHandleScope(HANDLE handle) throw() : m_handle(handle) {}
|
||||
~CloseHandleScope() throw() { CloseHandle(m_handle); }
|
||||
HANDLE Detach() throw() { return pfc::replace_t(m_handle, INVALID_HANDLE_VALUE); }
|
||||
HANDLE Get() const throw() { return m_handle; }
|
||||
void Close() throw() { CloseHandle(Detach()); }
|
||||
PFC_CLASS_NOT_COPYABLE_EX(CloseHandleScope)
|
||||
private:
|
||||
HANDLE m_handle;
|
||||
};
|
||||
|
||||
bool IsMenuNonEmpty(HMENU menu);
|
||||
void SetDefaultMenuItem(HMENU p_menu, unsigned p_id);
|
||||
|
||||
void GetOSVersionString(pfc::string_base & out);
|
||||
WORD GetOSVersionCode();
|
||||
474
libPPUI/wtl-pp.h
Normal file
474
libPPUI/wtl-pp.h
Normal file
@@ -0,0 +1,474 @@
|
||||
#pragma once
|
||||
// Various WTL extensions that are not fb2k specific and can be reused in other WTL based software
|
||||
|
||||
#include <Uxtheme.h>
|
||||
#include <functional>
|
||||
|
||||
#define ATLASSERT_SUCCESS(X) {auto RetVal = (X); ATLASSERT( RetVal ); }
|
||||
|
||||
class NoRedrawScope {
|
||||
public:
|
||||
NoRedrawScope(HWND p_wnd) throw() : m_wnd(p_wnd) {
|
||||
m_wnd.SetRedraw(FALSE);
|
||||
}
|
||||
~NoRedrawScope() throw() {
|
||||
m_wnd.SetRedraw(TRUE);
|
||||
}
|
||||
private:
|
||||
CWindow m_wnd;
|
||||
};
|
||||
|
||||
class NoRedrawScopeEx {
|
||||
public:
|
||||
NoRedrawScopeEx(HWND p_wnd) throw() : m_wnd(p_wnd), m_active() {
|
||||
if (m_wnd.IsWindowVisible()) {
|
||||
m_active = true;
|
||||
m_wnd.SetRedraw(FALSE);
|
||||
}
|
||||
}
|
||||
~NoRedrawScopeEx() throw() {
|
||||
if (m_active) {
|
||||
m_wnd.SetRedraw(TRUE);
|
||||
m_wnd.RedrawWindow(NULL,NULL,RDW_INVALIDATE|RDW_ERASE|RDW_ALLCHILDREN);
|
||||
}
|
||||
}
|
||||
private:
|
||||
bool m_active;
|
||||
CWindow m_wnd;
|
||||
};
|
||||
|
||||
|
||||
#define MSG_WM_TIMER_EX(timerId, func) \
|
||||
if (uMsg == WM_TIMER && (UINT_PTR)wParam == timerId) \
|
||||
{ \
|
||||
SetMsgHandled(TRUE); \
|
||||
func(); \
|
||||
lResult = 0; \
|
||||
if(IsMsgHandled()) \
|
||||
return TRUE; \
|
||||
}
|
||||
|
||||
#define MESSAGE_HANDLER_SIMPLE(msg, func) \
|
||||
if(uMsg == msg) \
|
||||
{ \
|
||||
SetMsgHandled(TRUE); \
|
||||
func(); \
|
||||
lResult = 0; \
|
||||
if(IsMsgHandled()) \
|
||||
return TRUE; \
|
||||
}
|
||||
|
||||
// void OnSysCommandHelp()
|
||||
#define MSG_WM_SYSCOMMAND_HELP(func) \
|
||||
if (uMsg == WM_SYSCOMMAND && wParam == SC_CONTEXTHELP) \
|
||||
{ \
|
||||
SetMsgHandled(TRUE); \
|
||||
func(); \
|
||||
lResult = 0; \
|
||||
if(IsMsgHandled()) \
|
||||
return TRUE; \
|
||||
}
|
||||
|
||||
//BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult, DWORD dwMsgMapID)
|
||||
#define END_MSG_MAP_HOOK() \
|
||||
break; \
|
||||
default: \
|
||||
return __super::ProcessWindowMessage(hWnd, uMsg, wParam, lParam, lResult, dwMsgMapID); \
|
||||
} \
|
||||
return FALSE; \
|
||||
}
|
||||
|
||||
|
||||
|
||||
class CImageListContainer : public CImageList {
|
||||
public:
|
||||
CImageListContainer() {}
|
||||
~CImageListContainer() {Destroy();}
|
||||
private:
|
||||
const CImageListContainer & operator=(const CImageListContainer&);
|
||||
CImageListContainer(const CImageListContainer&);
|
||||
};
|
||||
|
||||
|
||||
template<bool managed> class CThemeT {
|
||||
public:
|
||||
CThemeT(HTHEME source = NULL) : m_theme(source) {}
|
||||
|
||||
~CThemeT() {
|
||||
Release();
|
||||
}
|
||||
|
||||
HTHEME OpenThemeData(HWND wnd,LPCWSTR classList) {
|
||||
Release();
|
||||
return m_theme = ::OpenThemeData(wnd, classList);
|
||||
}
|
||||
|
||||
void Release() {
|
||||
HTHEME releaseme = pfc::replace_null_t(m_theme);
|
||||
if (managed && releaseme != NULL) CloseThemeData(releaseme);
|
||||
}
|
||||
|
||||
operator HTHEME() const {return m_theme;}
|
||||
HTHEME m_theme;
|
||||
};
|
||||
typedef CThemeT<false> CThemeHandle;
|
||||
typedef CThemeT<true> CTheme;
|
||||
|
||||
|
||||
class CCheckBox : public CButton {
|
||||
public:
|
||||
void ToggleCheck(bool state) {SetCheck(state ? BST_CHECKED : BST_UNCHECKED);}
|
||||
bool IsChecked() const {return GetCheck() == BST_CHECKED;}
|
||||
|
||||
CCheckBox(HWND hWnd = NULL) : CButton(hWnd) { }
|
||||
CCheckBox & operator=(HWND wnd) {m_hWnd = wnd; return *this; }
|
||||
};
|
||||
|
||||
class CEditPPHooks : public CContainedWindowT<CEdit>, private CMessageMap {
|
||||
public:
|
||||
bool HandleCtrlA = true, NoEscSteal = false, NoEnterSteal = false;
|
||||
|
||||
std::function<void ()> onEnterKey;
|
||||
std::function<void ()> onEscKey;
|
||||
|
||||
CEditPPHooks(CMessageMap * hookMM = nullptr, int hookMMID = 0) : CContainedWindowT<CEdit>(this, 0), m_hookMM(hookMM), m_hookMMID(hookMMID) {}
|
||||
|
||||
BEGIN_MSG_MAP_EX(CEditPPHooks)
|
||||
MSG_WM_KEYDOWN(OnKeyDown)
|
||||
MSG_WM_CHAR(OnChar)
|
||||
MSG_WM_GETDLGCODE(OnEditGetDlgCode)
|
||||
|
||||
if ( m_hookMM != nullptr ) {
|
||||
|
||||
CHAIN_MSG_MAP_ALT_MEMBER( ( * m_hookMM ), m_hookMMID );
|
||||
|
||||
}
|
||||
|
||||
END_MSG_MAP()
|
||||
|
||||
static void DeleteLastWord( CEdit wnd ) {
|
||||
if ( wnd.GetWindowLong(GWL_STYLE) & ES_READONLY ) return;
|
||||
int len = wnd.GetWindowTextLength();
|
||||
if ( len <= 0 ) return;
|
||||
TCHAR * buffer = new TCHAR [ len + 1 ];
|
||||
if ( wnd.GetWindowText( buffer, len + 1 ) <= 0 ) {
|
||||
delete[] buffer;
|
||||
return;
|
||||
}
|
||||
buffer[len] = 0;
|
||||
int selStart = len, selEnd = len;
|
||||
wnd.GetSel(selStart, selEnd);
|
||||
if ( selStart < 0 || selStart > len ) selStart = len; // sanity
|
||||
if ( selEnd < selStart ) selEnd = selStart; // sanity
|
||||
int work = selStart;
|
||||
if ( work == selEnd ) {
|
||||
// Only do our stuff if there is nothing yet selected. Otherwise first delete selection.
|
||||
while( work > 0 && isWordDelimiter(buffer[work-1]) ) --work;
|
||||
while( work > 0 && !isWordDelimiter(buffer[work-1] ) ) --work;
|
||||
}
|
||||
delete[] buffer;
|
||||
if ( selEnd > work ) {
|
||||
wnd.SetSel(work, selEnd, TRUE );
|
||||
wnd.ReplaceSel( TEXT(""), TRUE );
|
||||
}
|
||||
}
|
||||
private:
|
||||
static bool isWordDelimiter( TCHAR c ) {
|
||||
return (unsigned) c <= ' ' || c == ',' || c == '.' || c == ';' || c == ':';
|
||||
}
|
||||
void OnChar(UINT nChar, UINT, UINT nFlags) {
|
||||
if (m_suppressChar != 0) {
|
||||
if (nChar == m_suppressChar) return;
|
||||
}
|
||||
if (m_suppressScanCode != 0) {
|
||||
UINT code = nFlags & 0xFF;
|
||||
if (code == m_suppressScanCode) return;
|
||||
}
|
||||
SetMsgHandled(FALSE);
|
||||
}
|
||||
void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) {
|
||||
m_suppressChar = 0;
|
||||
m_suppressScanCode = 0;
|
||||
if (HandleCtrlA) {
|
||||
if (nChar == 'A') {
|
||||
if (GetHotkeyModifierFlags() == MOD_CONTROL) {
|
||||
m_suppressScanCode = nFlags & 0xFF;
|
||||
this->SetSelAll(); return;
|
||||
}
|
||||
}
|
||||
if ( nChar == VK_BACK ) {
|
||||
if (GetHotkeyModifierFlags() == MOD_CONTROL) {
|
||||
m_suppressScanCode = nFlags & 0xFF;
|
||||
DeleteLastWord( *this ) ; return;
|
||||
}
|
||||
}
|
||||
if ( nChar == VK_RETURN && onEnterKey ) {
|
||||
m_suppressChar = nChar;
|
||||
onEnterKey(); return;
|
||||
}
|
||||
if ( nChar == VK_ESCAPE && onEscKey ) {
|
||||
m_suppressChar = nChar;
|
||||
onEscKey(); return;
|
||||
}
|
||||
}
|
||||
SetMsgHandled(FALSE);
|
||||
}
|
||||
UINT OnEditGetDlgCode(LPMSG lpMsg) {
|
||||
if (lpMsg == NULL) {
|
||||
SetMsgHandled(FALSE); return 0;
|
||||
} else {
|
||||
switch(lpMsg->message) {
|
||||
case WM_KEYDOWN:
|
||||
case WM_SYSKEYDOWN:
|
||||
switch(lpMsg->wParam) {
|
||||
case VK_ESCAPE:
|
||||
if (onEscKey) {
|
||||
return DLGC_WANTMESSAGE;
|
||||
}
|
||||
SetMsgHandled(!!NoEscSteal);
|
||||
return 0;
|
||||
case VK_RETURN:
|
||||
if (onEnterKey) {
|
||||
return DLGC_WANTMESSAGE;
|
||||
}
|
||||
SetMsgHandled(!!NoEnterSteal);
|
||||
return 0;
|
||||
default:
|
||||
SetMsgHandled(FALSE); return 0;
|
||||
}
|
||||
default:
|
||||
SetMsgHandled(FALSE); return 0;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
UINT m_suppressChar = 0, m_suppressScanCode = 0;
|
||||
CMessageMap * const m_hookMM;
|
||||
const int m_hookMMID;
|
||||
};
|
||||
|
||||
|
||||
class CEditNoEscSteal : public CContainedWindowT<CEdit>, private CMessageMap {
|
||||
public:
|
||||
CEditNoEscSteal() : CContainedWindowT<CEdit>(this, 0) {}
|
||||
BEGIN_MSG_MAP_EX(CEditNoEscSteal)
|
||||
MSG_WM_GETDLGCODE(OnEditGetDlgCode)
|
||||
END_MSG_MAP()
|
||||
private:
|
||||
UINT OnEditGetDlgCode(LPMSG lpMsg) {
|
||||
if (lpMsg == NULL) {
|
||||
SetMsgHandled(FALSE); return 0;
|
||||
} else {
|
||||
switch(lpMsg->message) {
|
||||
case WM_KEYDOWN:
|
||||
case WM_SYSKEYDOWN:
|
||||
switch(lpMsg->wParam) {
|
||||
case VK_ESCAPE:
|
||||
return 0;
|
||||
default:
|
||||
SetMsgHandled(FALSE); return 0;
|
||||
}
|
||||
default:
|
||||
SetMsgHandled(FALSE); return 0;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class CEditNoEnterEscSteal : public CContainedWindowT<CEdit>, private CMessageMap {
|
||||
public:
|
||||
CEditNoEnterEscSteal() : CContainedWindowT<CEdit>(this, 0) {}
|
||||
BEGIN_MSG_MAP_EX(CEditNoEscSteal)
|
||||
MSG_WM_GETDLGCODE(OnEditGetDlgCode)
|
||||
END_MSG_MAP()
|
||||
private:
|
||||
UINT OnEditGetDlgCode(LPMSG lpMsg) {
|
||||
if (lpMsg == NULL) {
|
||||
SetMsgHandled(FALSE); return 0;
|
||||
} else {
|
||||
switch(lpMsg->message) {
|
||||
case WM_KEYDOWN:
|
||||
case WM_SYSKEYDOWN:
|
||||
switch(lpMsg->wParam) {
|
||||
case VK_ESCAPE:
|
||||
case VK_RETURN:
|
||||
return 0;
|
||||
default:
|
||||
SetMsgHandled(FALSE); return 0;
|
||||
}
|
||||
default:
|
||||
SetMsgHandled(FALSE); return 0;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
class CWindowClassUnregisterScope {
|
||||
public:
|
||||
CWindowClassUnregisterScope() : name() {}
|
||||
const TCHAR * name;
|
||||
void Set(const TCHAR * n) {ATLASSERT( name == NULL ); name = n; }
|
||||
bool IsActive() const {return name != NULL;}
|
||||
void CleanUp() {
|
||||
const TCHAR * n = name; name = NULL;
|
||||
if (n != NULL) ATLASSERT_SUCCESS( UnregisterClass(n, (HINSTANCE)&__ImageBase) );
|
||||
}
|
||||
~CWindowClassUnregisterScope() {CleanUp();}
|
||||
};
|
||||
|
||||
|
||||
// CWindowRegisteredT
|
||||
// Minimalistic wrapper for registering own window classes that can be created by class name, included in dialogs and such.
|
||||
// Usage:
|
||||
// class myClass : public CWindowRegisteredT<myClass> {...};
|
||||
// Call myClass::Register() before first use
|
||||
template<typename TClass, typename TBaseClass = CWindow>
|
||||
class CWindowRegisteredT : public TBaseClass, public CMessageMap {
|
||||
public:
|
||||
static UINT GetClassStyle() {
|
||||
return CS_VREDRAW | CS_HREDRAW;
|
||||
}
|
||||
static HCURSOR GetCursor() {
|
||||
return ::LoadCursor(NULL, IDC_ARROW);
|
||||
}
|
||||
|
||||
BEGIN_MSG_MAP_EX(CWindowRegisteredT)
|
||||
END_MSG_MAP()
|
||||
|
||||
|
||||
static void Register() {
|
||||
static CWindowClassUnregisterScope scope;
|
||||
if (!scope.IsActive()) {
|
||||
WNDCLASS wc = {};
|
||||
wc.style = TClass::GetClassStyle();
|
||||
wc.cbWndExtra = sizeof(void*);
|
||||
wc.lpszClassName = TClass::GetClassName();
|
||||
wc.lpfnWndProc = myWindowProc;
|
||||
wc.hInstance = (HINSTANCE)&__ImageBase;
|
||||
wc.hCursor = TClass::GetCursor();
|
||||
ATLASSERT_SUCCESS( RegisterClass(&wc) != 0 );
|
||||
scope.Set(wc.lpszClassName);
|
||||
}
|
||||
}
|
||||
protected:
|
||||
virtual ~CWindowRegisteredT() {}
|
||||
private:
|
||||
static LRESULT CALLBACK myWindowProc(HWND wnd, UINT msg, WPARAM wp, LPARAM lp) {
|
||||
TClass * i = NULL;
|
||||
if (msg == WM_NCCREATE) {
|
||||
i = new TClass;
|
||||
i->Attach(wnd);
|
||||
::SetWindowLongPtr(wnd, 0, reinterpret_cast<LONG_PTR>(i));
|
||||
} else {
|
||||
i = reinterpret_cast<TClass*>( ::GetWindowLongPtr(wnd, 0) );
|
||||
}
|
||||
LRESULT r;
|
||||
if (i == NULL || !i->ProcessWindowMessage(wnd, msg, wp, lp, r)) r = ::DefWindowProc(wnd, msg, wp, lp);
|
||||
if (msg == WM_NCDESTROY) {
|
||||
::SetWindowLongPtr(wnd, 0, 0);
|
||||
delete i;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
class CSRWlock {
|
||||
public:
|
||||
CSRWlock() : theLock() {
|
||||
#if _WIN32_WINNT < 0x600
|
||||
auto dll = GetModuleHandle(_T("kernel32"));
|
||||
Bind(AcquireSRWLockExclusive, dll, "AcquireSRWLockExclusive");
|
||||
Bind(AcquireSRWLockShared, dll, "AcquireSRWLockShared");
|
||||
Bind(ReleaseSRWLockExclusive, dll, "ReleaseSRWLockExclusive");
|
||||
Bind(ReleaseSRWLockShared, dll, "ReleaseSRWLockShared");
|
||||
#endif
|
||||
}
|
||||
|
||||
bool HaveAPI() {
|
||||
#if _WIN32_WINNT < 0x600
|
||||
return AcquireSRWLockExclusive != NULL;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void EnterShared() {
|
||||
AcquireSRWLockShared( & theLock );
|
||||
}
|
||||
void EnterExclusive() {
|
||||
AcquireSRWLockExclusive( & theLock );
|
||||
}
|
||||
void LeaveShared() {
|
||||
ReleaseSRWLockShared( & theLock );
|
||||
}
|
||||
void LeaveExclusive() {
|
||||
ReleaseSRWLockExclusive( &theLock );
|
||||
}
|
||||
|
||||
private:
|
||||
CSRWlock(const CSRWlock&) = delete;
|
||||
void operator=(const CSRWlock&) = delete;
|
||||
|
||||
SRWLOCK theLock;
|
||||
#if _WIN32_WINNT < 0x600
|
||||
template<typename func_t> static void Bind(func_t & func, HMODULE dll, const char * name) {
|
||||
func = reinterpret_cast<func_t>(GetProcAddress( dll, name ) );
|
||||
}
|
||||
|
||||
VOID (WINAPI * AcquireSRWLockExclusive)(PSRWLOCK SRWLock);
|
||||
VOID (WINAPI * AcquireSRWLockShared)(PSRWLOCK SRWLock);
|
||||
VOID (WINAPI * ReleaseSRWLockExclusive)(PSRWLOCK SRWLock);
|
||||
VOID (WINAPI * ReleaseSRWLockShared)(PSRWLOCK SRWLock);
|
||||
#endif
|
||||
};
|
||||
|
||||
#if _WIN32_WINNT < 0x600
|
||||
class CSRWorCS {
|
||||
public:
|
||||
CSRWorCS() : cs() {
|
||||
if (!srw.HaveAPI()) InitializeCriticalSection(&cs);
|
||||
}
|
||||
~CSRWorCS() {
|
||||
if (!srw.HaveAPI()) DeleteCriticalSection(& cs );
|
||||
}
|
||||
void EnterShared() {
|
||||
if (srw.HaveAPI()) srw.EnterShared();
|
||||
else EnterCriticalSection(&cs);
|
||||
}
|
||||
void EnterExclusive() {
|
||||
if (srw.HaveAPI()) srw.EnterExclusive();
|
||||
else EnterCriticalSection(&cs);
|
||||
}
|
||||
void LeaveShared() {
|
||||
if (srw.HaveAPI()) srw.LeaveShared();
|
||||
else LeaveCriticalSection(&cs);
|
||||
}
|
||||
void LeaveExclusive() {
|
||||
if (srw.HaveAPI()) srw.LeaveExclusive();
|
||||
else LeaveCriticalSection(&cs);
|
||||
}
|
||||
private:
|
||||
CSRWorCS(const CSRWorCS&) = delete;
|
||||
void operator=(const CSRWorCS&) = delete;
|
||||
|
||||
CSRWlock srw;
|
||||
CRITICAL_SECTION cs;
|
||||
};
|
||||
#else
|
||||
typedef CSRWlock CSRWorCS;
|
||||
#endif
|
||||
|
||||
|
||||
template<typename TBase> class CContainedWindowSimpleT : public CContainedWindowT<TBase>, public CMessageMap {
|
||||
public:
|
||||
CContainedWindowSimpleT() : CContainedWindowT<TBase>(this) {}
|
||||
BEGIN_MSG_MAP(CContainedWindowSimpleT)
|
||||
END_MSG_MAP()
|
||||
};
|
||||
Reference in New Issue
Block a user