add last backwards-compatible version
This commit is contained in:
23
foobar2000/ATLHelpers/ATLHelpers.h
Normal file
23
foobar2000/ATLHelpers/ATLHelpers.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "ATLHelpersLean.h"
|
||||
|
||||
// #pragma message("Avoid using this header. #include ATLHelpersLean.h then individual helpers instead")
|
||||
|
||||
#include "../helpers/helpers.h"
|
||||
|
||||
#include "WTL-PP.h"
|
||||
#include "misc.h"
|
||||
#include "GDIUtils.h"
|
||||
|
||||
#include "WindowPositionUtils.h"
|
||||
#include "CDialogResizeHelper.h"
|
||||
|
||||
#include "BumpableElem.h"
|
||||
|
||||
#include "inplace_edit.h"
|
||||
#include "inplace_edit_v2.h"
|
||||
|
||||
#include "AutoComplete.h"
|
||||
|
||||
#include "Controls.h"
|
||||
16
foobar2000/ATLHelpers/ATLHelpersLean.h
Normal file
16
foobar2000/ATLHelpers/ATLHelpersLean.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "../SDK/foobar2000-winver.h"
|
||||
|
||||
#define _SECURE_ATL 1
|
||||
|
||||
#include "../SDK/foobar2000.h"
|
||||
|
||||
#include <atlbase.h>
|
||||
#include <atltypes.h>
|
||||
#include <atlstr.h>
|
||||
#include <atlapp.h>
|
||||
#include <atlctrls.h>
|
||||
#include <atlwin.h>
|
||||
#include <atlcom.h>
|
||||
#include <atlcrack.h>
|
||||
184
foobar2000/ATLHelpers/AutoComplete.cpp
Normal file
184
foobar2000/ATLHelpers/AutoComplete.cpp
Normal file
@@ -0,0 +1,184 @@
|
||||
#include "stdafx.h"
|
||||
#include <ShlGuid.h> // CLSID_AutoComplete
|
||||
#include "../helpers/COM_utils.h"
|
||||
#include "../helpers/dropdown_helper.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
class CEnumString : public IEnumString {
|
||||
public:
|
||||
typedef pfc::chain_list_v2_t<pfc::array_t<TCHAR> > t_data;
|
||||
CEnumString(const t_data & in) : m_data(in) {m_walk = m_data.first();}
|
||||
CEnumString() {}
|
||||
|
||||
void AddString(const TCHAR * in) {
|
||||
m_data.insert_last()->set_data_fromptr(in, _tcslen(in) + 1);
|
||||
m_walk = m_data.first();
|
||||
}
|
||||
void AddStringU(const char * in, t_size len) {
|
||||
pfc::array_t<TCHAR> & arr = * m_data.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);
|
||||
m_walk = m_data.first();
|
||||
}
|
||||
void AddStringU(const char * in) {
|
||||
pfc::array_t<TCHAR> & arr = * m_data.insert_last();
|
||||
arr.set_size( pfc::stringcvt::estimate_utf8_to_wide( in ) );
|
||||
pfc::stringcvt::convert_utf8_to_wide_unchecked( arr.get_ptr(), in );
|
||||
m_walk = m_data.first();
|
||||
}
|
||||
void ResetStrings() {m_walk.invalidate(); m_data.remove_all();}
|
||||
|
||||
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_data.first();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE Clone(IEnumString **ppenum) {
|
||||
*ppenum = new TImpl(*this); return S_OK;
|
||||
}
|
||||
private:
|
||||
t_data m_data;
|
||||
t_data::const_iterator m_walk;
|
||||
};
|
||||
class CACList_History : public CEnumString {
|
||||
public:
|
||||
CACList_History(cfg_dropdown_history_mt * var) : m_var(var) {Reset();}
|
||||
typedef ImplementCOMRefCounter<CACList_History> TImpl;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE Reset() {
|
||||
/*if (core_api::assert_main_thread())*/ {
|
||||
ResetStrings();
|
||||
pfc::string8 state; m_var->get_state(state);
|
||||
for(const char * walk = state;;) {
|
||||
const char * next = strchr(walk, cfg_dropdown_history_mt::separator);
|
||||
t_size len = (next != NULL) ? next - walk : ~0;
|
||||
AddStringU(walk, len);
|
||||
if (next == NULL) break;
|
||||
walk = next + 1;
|
||||
}
|
||||
}
|
||||
return __super::Reset();
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE Clone(IEnumString **ppenum) {
|
||||
*ppenum = new TImpl(*this); return S_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
cfg_dropdown_history_mt * const m_var;
|
||||
};
|
||||
}
|
||||
|
||||
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, cfg_dropdown_history_mt::separator);
|
||||
if (next == NULL) {acl->AddStringU(walk, ~0); break;}
|
||||
acl->AddStringU(walk, next - walk);
|
||||
walk = next + 1;
|
||||
}
|
||||
return InitializeSimpleAC(edit, acl.get_ptr(), opts);
|
||||
}
|
||||
|
||||
HRESULT InitializeDropdownAC(HWND comboBox, cfg_dropdown_history_mt & var, const char * initVal) {
|
||||
var.on_init(comboBox, initVal);
|
||||
COMBOBOXINFO ci = {}; ci.cbSize = sizeof(ci);
|
||||
if (!GetComboBoxInfo(comboBox, &ci)) {
|
||||
PFC_ASSERT(!"Should not get here - GetComboBoxInfo fail!");
|
||||
return E_FAIL;
|
||||
}
|
||||
pfc::com_ptr_t<IUnknown> acl = new CACList_History::TImpl(&var);
|
||||
return InitializeSimpleAC(ci.hwndItem, acl.get_ptr(), ACO_AUTOAPPEND|ACO_AUTOSUGGEST);
|
||||
}
|
||||
16
foobar2000/ATLHelpers/AutoComplete.h
Normal file
16
foobar2000/ATLHelpers/AutoComplete.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <ShlDisp.h>
|
||||
|
||||
class cfg_dropdown_history_mt;
|
||||
|
||||
HRESULT InitializeDropdownAC(HWND comboBox, cfg_dropdown_history_mt & var, const char * initVal);
|
||||
HRESULT InitializeEditAC(HWND edit, const char * values, DWORD opts = ACO_AUTOAPPEND|ACO_AUTOSUGGEST);
|
||||
HRESULT InitializeEditAC(HWND edit, pfc::const_iterator<pfc::string8> valueEnum, 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);
|
||||
|
||||
72
foobar2000/ATLHelpers/BumpableElem.h
Normal file
72
foobar2000/ATLHelpers/BumpableElem.h
Normal file
@@ -0,0 +1,72 @@
|
||||
#pragma once
|
||||
#include "CFlashWindow.h"
|
||||
#include "misc.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
template<typename TClass>
|
||||
class ImplementBumpableElem : public TClass {
|
||||
private:
|
||||
typedef ImplementBumpableElem<TClass> TSelf;
|
||||
public:
|
||||
template<typename ... arg_t> ImplementBumpableElem( arg_t && ... arg ) : TClass(std::forward<arg_t>(arg) ... ) {_init(); }
|
||||
|
||||
BEGIN_MSG_MAP_EX(ImplementBumpableElem)
|
||||
MSG_WM_DESTROY(OnDestroy)
|
||||
CHAIN_MSG_MAP(__super)
|
||||
END_MSG_MAP_HOOK()
|
||||
|
||||
void notify(const GUID & p_what, t_size p_param1, const void * p_param2, t_size p_param2size) {
|
||||
if (p_what == ui_element_notify_visibility_changed && p_param1 == 0 && m_flash.m_hWnd != NULL) m_flash.Deactivate();
|
||||
__super::notify(p_what, p_param1, p_param2, p_param2size);
|
||||
}
|
||||
|
||||
static bool Bump() {
|
||||
for(auto walk = instances.cfirst(); walk.is_valid(); ++walk) {
|
||||
if ((*walk)->_bump()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
~ImplementBumpableElem() throw() {
|
||||
PFC_ASSERT(core_api::is_main_thread());
|
||||
instances -= this;
|
||||
}
|
||||
private:
|
||||
void OnDestroy() throw() {
|
||||
m_selfDestruct = true;
|
||||
m_flash.CleanUp();
|
||||
SetMsgHandled(FALSE);
|
||||
}
|
||||
bool _bump() {
|
||||
if (m_selfDestruct || m_hWnd == NULL) return false;
|
||||
//PROBLEM: This assumes we're implementing service_base methods at this point. Explodes if called during constructors/destructors.
|
||||
if (!this->m_callback->request_activation(this)) return false;
|
||||
m_flash.Activate(*this);
|
||||
this->set_default_focus();
|
||||
return true;
|
||||
}
|
||||
void _init() {
|
||||
m_selfDestruct = false;
|
||||
PFC_ASSERT(core_api::is_main_thread());
|
||||
instances += this;
|
||||
}
|
||||
static pfc::avltree_t<TSelf*> instances;
|
||||
bool m_selfDestruct;
|
||||
CFlashWindow m_flash;
|
||||
};
|
||||
|
||||
template<typename TClass>
|
||||
pfc::avltree_t<ImplementBumpableElem<TClass> *> ImplementBumpableElem<TClass>::instances;
|
||||
|
||||
|
||||
template<typename TImpl, typename TInterface = ui_element_v2> class ui_element_impl_withpopup : public ui_element_impl<ImplementBumpableElem<TImpl>, TInterface> {
|
||||
public:
|
||||
t_uint32 get_flags() {return KFlagHavePopupCommand | KFlagSupportsBump;}
|
||||
bool bump() {return ImplementBumpableElem<TImpl>::Bump();}
|
||||
};
|
||||
|
||||
template<typename TImpl, typename TInterface = ui_element_v2> class ui_element_impl_visualisation : public ui_element_impl<ImplementBumpableElem<TImpl>, TInterface> {
|
||||
public:
|
||||
t_uint32 get_flags() {return KFlagsVisualisation | KFlagSupportsBump;}
|
||||
bool bump() {return ImplementBumpableElem<TImpl>::Bump();}
|
||||
};
|
||||
267
foobar2000/ATLHelpers/CButtonLite.h
Normal file
267
foobar2000/ATLHelpers/CButtonLite.h
Normal file
@@ -0,0 +1,267 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include "WTL-PP.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;
|
||||
font.GetLogFont( lf );
|
||||
MakeBoldFont( lf );
|
||||
CFont bold;
|
||||
bold.CreateFontIndirect(&lf);
|
||||
CWindowDC dc(*this);
|
||||
auto oldFont = dc.SelectFont( bold );
|
||||
CSize size (0,0);
|
||||
CSize sizeSpace (0,0);
|
||||
|
||||
|
||||
dc.GetTextExtent(m_textDrawMe, m_textDrawMe.GetLength(), &size);
|
||||
dc.GetTextExtent(L" ", 1, &sizeSpace);
|
||||
|
||||
dc.SelectFont( oldFont );
|
||||
|
||||
return size.cx + sizeSpace.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 ) {
|
||||
if ( m_pressed ) OnClicked();
|
||||
TogglePressed(false);
|
||||
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;
|
||||
};
|
||||
141
foobar2000/ATLHelpers/CDialogResizeHelper.cpp
Normal file
141
foobar2000/ATLHelpers/CDialogResizeHelper.cpp
Normal file
@@ -0,0 +1,141 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "../helpers/win32_misc.h"
|
||||
#include "CDialogResizeHelper.h"
|
||||
|
||||
bool CDialogResizeHelper::EvalRect(UINT id, CRect & out) const {
|
||||
for(t_size walk = 0; walk < m_table.get_size(); ++walk) {
|
||||
if (m_table[walk].id == id) {
|
||||
CRect client; WIN32_OP_D( m_thisWnd.GetClientRect(client) );
|
||||
return _EvalRect(walk, client.Size(), out);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CDialogResizeHelper::_EvalRect(t_size index, CSize wndSize, CRect & out) const {
|
||||
CRect rc;
|
||||
const Param & e = m_table[index];
|
||||
if (m_origRects.query(e.id, rc)) {
|
||||
int delta_x = wndSize.cx - m_rcOrigClient.right,
|
||||
delta_y = wndSize.cy - m_rcOrigClient.bottom;
|
||||
|
||||
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 );
|
||||
|
||||
out = rc;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CDialogResizeHelper::OnSize(UINT, CSize newSize)
|
||||
{
|
||||
if (m_thisWnd != NULL) {
|
||||
HDWP hWinPosInfo = BeginDeferWindowPos( m_table.get_size() + (m_sizeGrip != NULL ? 1 : 0) );
|
||||
for(t_size n = 0; n < m_table.get_size(); ++n) {
|
||||
CRect rc;
|
||||
if (_EvalRect(n, newSize, rc)) {
|
||||
hWinPosInfo = DeferWindowPos(hWinPosInfo, m_thisWnd.GetDlgItem(m_table[n].id), 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_origRects.remove_all();
|
||||
m_thisWnd = thisWnd;
|
||||
m_thisWnd.GetClientRect(&m_rcOrigClient);
|
||||
for(t_size n = 0; n < m_table.get_size(); n++) {
|
||||
CRect rc;
|
||||
const UINT id = m_table[n].id;
|
||||
if (GetChildWindowRect(m_thisWnd,id,&rc)) {
|
||||
m_origRects.set(id, rc);
|
||||
}
|
||||
}
|
||||
AddSizeGrip();
|
||||
SetMsgHandled(FALSE);
|
||||
}
|
||||
void CDialogResizeHelper::OnDestroy() {
|
||||
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, t_size tableSize) {
|
||||
m_table.set_data_fromptr(table, tableSize);
|
||||
}
|
||||
void CDialogResizeHelper::InitTable(const ParamOld * table, t_size tableSize) {
|
||||
m_table.set_size(tableSize);
|
||||
for(t_size walk = 0; walk < tableSize; ++walk) {
|
||||
const ParamOld in = table[walk];
|
||||
Param entry = {};
|
||||
entry.id = table[walk].id;
|
||||
if (in.flags & dialog_resize_helper::X_MOVE) entry.snapLeft = entry.snapRight = 1;
|
||||
else if (in.flags & dialog_resize_helper::X_SIZE) entry.snapRight = 1;
|
||||
if (in.flags & dialog_resize_helper::Y_MOVE) entry.snapTop = entry.snapBottom = 1;
|
||||
else if (in.flags & dialog_resize_helper::Y_SIZE) entry.snapBottom = 1;
|
||||
m_table[walk] = entry;
|
||||
}
|
||||
}
|
||||
void CDialogResizeHelper::InitMinMax(const CRect & range) {
|
||||
min_x = range.left; min_y = range.top; max_x = range.right; max_y = range.bottom;
|
||||
}
|
||||
69
foobar2000/ATLHelpers/CDialogResizeHelper.h
Normal file
69
foobar2000/ATLHelpers/CDialogResizeHelper.h
Normal file
@@ -0,0 +1,69 @@
|
||||
#pragma once
|
||||
|
||||
#include "../helpers/dialog_resize_helper.h"
|
||||
#include "WindowPositionUtils.h"
|
||||
|
||||
class CDialogResizeHelper : public CMessageMap {
|
||||
public:
|
||||
typedef dialog_resize_helper::param ParamOld;
|
||||
|
||||
struct Param {
|
||||
t_uint32 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, t_size paramCount> CDialogResizeHelper(const TParam (& src)[paramCount],CRect const& minMaxRange = CRect(0,0,0,0)) {
|
||||
InitTable(src, paramCount);
|
||||
InitMinMax(minMaxRange);
|
||||
}
|
||||
|
||||
void InitTable(const Param* table, t_size tableSize);
|
||||
void InitTable(const ParamOld * table, t_size tableSize);
|
||||
void InitMinMax(const CRect & range);
|
||||
|
||||
bool EvalRect(UINT id, CRect & out) const;
|
||||
|
||||
private:
|
||||
bool _EvalRect(t_size index, CSize wndSize, CRect & out) const;
|
||||
void OnGetMinMaxInfo(LPMINMAXINFO lpMMI) const;
|
||||
void OnSize(UINT nType, CSize size);
|
||||
void OnInitDialog(CWindow thisWnd);
|
||||
void OnDestroy();
|
||||
|
||||
pfc::array_t<Param> m_table;
|
||||
pfc::map_t<UINT, CRect> m_origRects;
|
||||
CRect m_rcOrigClient;
|
||||
CWindow m_thisWnd, m_sizeGrip;
|
||||
unsigned min_x,min_y,max_x,max_y;
|
||||
};
|
||||
|
||||
template<typename TTracker> class CDialogResizeHelperTracking : public CDialogResizeHelper {
|
||||
public:
|
||||
template<typename TParam, t_size paramCount, typename TCfgVar> CDialogResizeHelperTracking(const TParam (& src)[paramCount],CRect const& minMaxRange, TCfgVar & cfgVar) : CDialogResizeHelper(src, minMaxRange), m_tracker(cfgVar) {}
|
||||
|
||||
BEGIN_MSG_MAP_EX(CDialogResizeHelperST)
|
||||
CHAIN_MSG_MAP(CDialogResizeHelper)
|
||||
CHAIN_MSG_MAP_MEMBER(m_tracker)
|
||||
END_MSG_MAP()
|
||||
|
||||
private:
|
||||
TTracker m_tracker;
|
||||
};
|
||||
|
||||
typedef CDialogResizeHelperTracking<cfgDialogSizeTracker> CDialogResizeHelperST;
|
||||
typedef CDialogResizeHelperTracking<cfgDialogPositionTracker> CDialogResizeHelperPT;
|
||||
|
||||
#define REDRAW_DIALOG_ON_RESIZE() if (uMsg == WM_SIZE) RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN);
|
||||
3
foobar2000/ATLHelpers/CEditWithButtons.cpp
Normal file
3
foobar2000/ATLHelpers/CEditWithButtons.cpp
Normal file
@@ -0,0 +1,3 @@
|
||||
#include "stdafx.h"
|
||||
#include "CEditWithButtons.h"
|
||||
// All in the header - CPP present to make MS intellisense stop showing nonsensical errors
|
||||
366
foobar2000/ATLHelpers/CEditWithButtons.h
Normal file
366
foobar2000/ATLHelpers/CEditWithButtons.h
Normal file
@@ -0,0 +1,366 @@
|
||||
#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 ) {
|
||||
AddButton(L"\x2026", f);
|
||||
}
|
||||
void AddClearButton( const wchar_t * clearVal = L"") {
|
||||
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");
|
||||
}
|
||||
|
||||
void AddButton( const wchar_t * str, handler_t handler, condition_t condition = nullptr, const wchar_t * drawAlternateText = nullptr ) {
|
||||
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();
|
||||
}
|
||||
|
||||
static unsigned DefaultFixedWidth() {
|
||||
return GetSystemMetrics(SM_CXVSCROLL) * 3 / 4;
|
||||
}
|
||||
void SetFixedWidth(unsigned fw = DefaultFixedWidth() ) {
|
||||
m_fixedWidth = fw;
|
||||
RefreshButtons();
|
||||
}
|
||||
CRect 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 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;
|
||||
}
|
||||
private:
|
||||
LRESULT OnCheckConditions( UINT msg, WPARAM wp, LPARAM lp ) {
|
||||
if ( msg == MSG_CHECKCONDITIONS && wp == MSG_CHECKCONDITIONS_MAGIC1 && lp == MSG_CHECKCONDITIONS_MAGIC2 ) {
|
||||
this->RefreshConditions(nullptr);
|
||||
} 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) {
|
||||
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 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 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 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 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 );
|
||||
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);
|
||||
}
|
||||
|
||||
walk = left;
|
||||
}
|
||||
EndDeferWindowPos( dwp );
|
||||
this->SetMargins(0, size.cx - walk, EC_RIGHTMARGIN );
|
||||
}
|
||||
|
||||
unsigned MeasureButton(Button_t const & button ) {
|
||||
if ( m_fixedWidth != 0 ) return m_fixedWidth;
|
||||
|
||||
return button.buttonImpl->Measure();
|
||||
}
|
||||
unsigned m_fixedWidth;
|
||||
std::list< Button_t > m_buttons;
|
||||
bool m_hasAutoComplete = false;
|
||||
};
|
||||
67
foobar2000/ATLHelpers/CFlashWindow.h
Normal file
67
foobar2000/ATLHelpers/CFlashWindow.h
Normal file
@@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
|
||||
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;
|
||||
t_uint32 m_tickCount;
|
||||
};
|
||||
|
||||
47
foobar2000/ATLHelpers/CIconOverlayWindow.h
Normal file
47
foobar2000/ATLHelpers/CIconOverlayWindow.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include "../ATLHelpers/misc.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(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;
|
||||
};
|
||||
292
foobar2000/ATLHelpers/CMiddleDragImpl.h
Normal file
292
foobar2000/ATLHelpers/CMiddleDragImpl.h
Normal file
@@ -0,0 +1,292 @@
|
||||
#pragma once
|
||||
|
||||
#include "CIconOverlayWindow.h"
|
||||
#include <utility>
|
||||
|
||||
template<typename TBase>
|
||||
class CMiddleDragImpl : public TBase {
|
||||
private:
|
||||
typedef CMiddleDragImpl<TBase> TSelf;
|
||||
enum {
|
||||
KTimerID = 0x389675f8,
|
||||
KTimerPeriod = 15,
|
||||
};
|
||||
public:
|
||||
template<typename ... arg_t> CMiddleDragImpl( arg_t && ... arg ) : TBase(std::forward<arg_t>(arg) ... ) { m_active = false; m_accX = m_accY = 0; }
|
||||
|
||||
BEGIN_MSG_MAP(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, m_dragged;
|
||||
CPoint m_base;
|
||||
CIconOverlayWindow m_overlay;
|
||||
double m_accX, m_accY;
|
||||
|
||||
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();
|
||||
SetFocus();
|
||||
CPoint pt(p_lp);
|
||||
WIN32_OP_D( 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( 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( m_hWnd != NULL );
|
||||
if (m_overlay.Create(*this) == NULL) {PFC_ASSERT(!"Should not get here!"); return;}
|
||||
HANDLE temp;
|
||||
WIN32_OP_D( (temp = LoadImage(core_api::get_my_instance(),MAKEINTRESOURCE(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;
|
||||
SetCapture();
|
||||
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;
|
||||
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((LPARAM)GetMessagePos()) - m_base));
|
||||
return 0;
|
||||
default:
|
||||
bHandled = FALSE;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static double myPow(double p_val,double p_exp) {
|
||||
double ex = pow(pfc::abs_t(p_val),p_exp);
|
||||
return p_val < 0 ? -ex : ex;
|
||||
}
|
||||
|
||||
static double ProcessMiddleDragDeltaInternal(double p_delta) {
|
||||
p_delta *= (double) KTimerPeriod / 25; /*originally calculated for 25ms timer interval*/
|
||||
return myPow(p_delta * 0.05,2.0);
|
||||
}
|
||||
|
||||
static double radiusHelper(double p_x,double p_y) {
|
||||
return sqrt(p_x * p_x + p_y * p_y);
|
||||
}
|
||||
|
||||
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(pfc::sqr_t(deltaTotal) / (1.0 + pfc::sqr_t(ratio)));
|
||||
xVal = yVal * ratio;
|
||||
}
|
||||
|
||||
xVal = pfc::sgn_t( p_delta.x ) * pfc::abs_t(xVal);
|
||||
yVal = pfc::sgn_t( p_delta.y ) * pfc::abs_t(yVal);
|
||||
|
||||
return CPoint(Round(xVal / dpiMulX, m_accX), Round(yVal / dpiMulY, m_accY));
|
||||
}
|
||||
static t_int32 Round(double val, double & acc) {
|
||||
val += acc;
|
||||
t_int32 ret = pfc::rint32(val);
|
||||
acc = val - ret;
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
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(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)pfc::abs_t(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;
|
||||
}
|
||||
|
||||
static LONG 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;
|
||||
}
|
||||
|
||||
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 = LineToPixelsHelper(p_overflow.x,p_pixels.x,dpi.cx,m_lineWidth.cx);
|
||||
pt.y = 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> >(pfc::implicit_cast<CMessageMap*>(this)) {}
|
||||
};
|
||||
|
||||
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(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;
|
||||
};
|
||||
166
foobar2000/ATLHelpers/Controls.h
Normal file
166
foobar2000/ATLHelpers/Controls.h
Normal file
@@ -0,0 +1,166 @@
|
||||
#pragma once
|
||||
|
||||
#include "misc.h"
|
||||
|
||||
void PaintSeparatorControl(CWindow wnd);
|
||||
|
||||
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) {
|
||||
PaintSeparatorControl(*this);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
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) Invalidate();
|
||||
}
|
||||
int OnSetText(LPCTSTR lpstrText) {
|
||||
Invalidate();SetMsgHandled(FALSE); return 0;
|
||||
}
|
||||
CFontHandle m_font;
|
||||
};
|
||||
|
||||
#ifndef VSCLASS_TEXTSTYLE
|
||||
//
|
||||
// TEXTSTYLE class parts and states
|
||||
//
|
||||
#define VSCLASS_TEXTSTYLE L"TEXTSTYLE"
|
||||
|
||||
enum TEXTSTYLEPARTS {
|
||||
TEXT_MAININSTRUCTION = 1,
|
||||
TEXT_INSTRUCTION = 2,
|
||||
TEXT_BODYTITLE = 3,
|
||||
TEXT_BODYTEXT = 4,
|
||||
TEXT_SECONDARYTEXT = 5,
|
||||
TEXT_HYPERLINKTEXT = 6,
|
||||
TEXT_EXPANDED = 7,
|
||||
TEXT_LABEL = 8,
|
||||
TEXT_CONTROLLABEL = 9,
|
||||
};
|
||||
|
||||
enum HYPERLINKTEXTSTATES {
|
||||
TS_HYPERLINK_NORMAL = 1,
|
||||
TS_HYPERLINK_HOT = 2,
|
||||
TS_HYPERLINK_PRESSED = 3,
|
||||
TS_HYPERLINK_DISABLED = 4,
|
||||
};
|
||||
|
||||
enum CONTROLLABELSTATES {
|
||||
TS_CONTROLLABEL_NORMAL = 1,
|
||||
TS_CONTROLLABEL_DISABLED = 2,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
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) {
|
||||
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 = 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 ) );
|
||||
}
|
||||
}
|
||||
int m_id;
|
||||
CTheme m_theme;
|
||||
bool m_fallback;
|
||||
};
|
||||
class CStaticMainInstruction : public CStaticThemed {
|
||||
public:
|
||||
CStaticMainInstruction() { SetThemePart(TEXT_MAININSTRUCTION); }
|
||||
};
|
||||
|
||||
|
||||
|
||||
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) {
|
||||
PaintSeparatorControl(*this);
|
||||
}
|
||||
};
|
||||
|
||||
253
foobar2000/ATLHelpers/GDIUtils.h
Normal file
253
foobar2000/ATLHelpers/GDIUtils.h
Normal file
@@ -0,0 +1,253 @@
|
||||
#pragma once
|
||||
|
||||
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) {
|
||||
SubclassWindow(wnd); 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;
|
||||
};
|
||||
441
foobar2000/ATLHelpers/WTL-PP.h
Normal file
441
foobar2000/ATLHelpers/WTL-PP.h
Normal file
@@ -0,0 +1,441 @@
|
||||
#pragma once
|
||||
// Various WTL extensions that are not fb2k specific and can be reused in other WTL based software
|
||||
|
||||
#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, NoEscSteal, NoEnterSteal;
|
||||
|
||||
CEditPPHooks(CMessageMap * hookMM = nullptr, int hookMMID = 0) : CContainedWindowT<CEdit>(this, 0), HandleCtrlA(true), NoEscSteal(), NoEnterSteal(), m_suppressChar(), 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) {
|
||||
UINT code = nFlags & 0xFF;
|
||||
if (code == m_suppressChar) return;
|
||||
}
|
||||
SetMsgHandled(FALSE);
|
||||
}
|
||||
void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) {
|
||||
m_suppressChar = 0;
|
||||
if (HandleCtrlA) {
|
||||
if (nChar == 'A') {
|
||||
if (GetHotkeyModifierFlags() == MOD_CONTROL) {
|
||||
m_suppressChar = nFlags & 0xFF;
|
||||
this->SetSelAll(); return;
|
||||
}
|
||||
}
|
||||
if ( nChar == VK_BACK ) {
|
||||
if (GetHotkeyModifierFlags() == MOD_CONTROL) {
|
||||
m_suppressChar = nFlags & 0xFF;
|
||||
DeleteLastWord( *this ) ; 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:
|
||||
SetMsgHandled(!!NoEscSteal);
|
||||
return 0;
|
||||
case VK_RETURN:
|
||||
SetMsgHandled(!!NoEnterSteal);
|
||||
return 0;
|
||||
default:
|
||||
SetMsgHandled(FALSE); return 0;
|
||||
}
|
||||
default:
|
||||
SetMsgHandled(FALSE); return 0;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
UINT m_suppressChar;
|
||||
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&);
|
||||
void operator=(const CSRWlock&);
|
||||
|
||||
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&);
|
||||
void operator=(const CSRWorCS&);
|
||||
|
||||
CSRWlock srw;
|
||||
CRITICAL_SECTION cs;
|
||||
};
|
||||
#else
|
||||
typedef CSRWlock CSRWorCS;
|
||||
#endif
|
||||
323
foobar2000/ATLHelpers/WindowPositionUtils.h
Normal file
323
foobar2000/ATLHelpers/WindowPositionUtils.h
Normal file
@@ -0,0 +1,323 @@
|
||||
#pragma once
|
||||
|
||||
#include "../helpers/win32_misc.h"
|
||||
|
||||
static BOOL AdjustWindowRectHelper(CWindow wnd, CRect & rc) {
|
||||
const DWORD style = wnd.GetWindowLong(GWL_STYLE), exstyle = wnd.GetWindowLong(GWL_EXSTYLE);
|
||||
return AdjustWindowRectEx(&rc,style,(style & WS_POPUP) ? wnd.GetMenu() != NULL : FALSE, exstyle);
|
||||
}
|
||||
|
||||
static void AdjustRectToScreenArea(CRect & rc, CRect rcParent) {
|
||||
HMONITOR monitor = MonitorFromRect(rcParent,MONITOR_DEFAULTTONEAREST);
|
||||
MONITORINFO mi = {sizeof(MONITORINFO)};
|
||||
if (GetMonitorInfo(monitor,&mi)) {
|
||||
const CRect clip = mi.rcWork;
|
||||
if (rc.right > clip.right) rc.OffsetRect(clip.right - rc.right, 0);
|
||||
if (rc.bottom > clip.bottom) rc.OffsetRect(0, clip.bottom - rc.bottom);
|
||||
if (rc.left < clip.left) rc.OffsetRect(clip.left - rc.left, 0);
|
||||
if (rc.top < clip.top) rc.OffsetRect(0, clip.top - rc.top);
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL GetClientRectAsSC(CWindow wnd, CRect & rc) {
|
||||
CRect temp;
|
||||
if (!wnd.GetClientRect(temp)) return FALSE;
|
||||
if (temp.IsRectNull()) return FALSE;
|
||||
if (!wnd.ClientToScreen(temp)) return FALSE;
|
||||
rc = temp;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static BOOL CenterWindowGetRect(CWindow wnd, CWindow wndParent, CRect & out) {
|
||||
CRect parent, child;
|
||||
if (!wndParent.GetWindowRect(&parent) || !wnd.GetWindowRect(&child)) return FALSE;
|
||||
{
|
||||
CPoint origin = parent.CenterPoint();
|
||||
origin.Offset( - child.Width() / 2, - child.Height() / 2);
|
||||
child.OffsetRect( origin - child.TopLeft() );
|
||||
}
|
||||
AdjustRectToScreenArea(child, parent);
|
||||
out = child;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL CenterWindowAbove(CWindow wnd, CWindow wndParent) {
|
||||
CRect rc;
|
||||
if (!CenterWindowGetRect(wnd, wndParent, rc)) return FALSE;
|
||||
return wnd.SetWindowPos(NULL,rc,SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
|
||||
}
|
||||
|
||||
static BOOL ShowWindowCentered(CWindow wnd,CWindow wndParent) {
|
||||
CRect rc;
|
||||
if (!CenterWindowGetRect(wnd, wndParent, rc)) return FALSE;
|
||||
return wnd.SetWindowPos(HWND_TOP,rc,SWP_NOSIZE | SWP_SHOWWINDOW);
|
||||
}
|
||||
|
||||
class cfgWindowSize : public cfg_var {
|
||||
public:
|
||||
cfgWindowSize(const GUID & p_guid) : cfg_var(p_guid), m_width(~0), m_height(~0) {}
|
||||
void get_data_raw(stream_writer * p_stream,abort_callback & p_abort) {
|
||||
stream_writer_formatter<>(*p_stream,p_abort) << m_width << m_height;
|
||||
}
|
||||
void set_data_raw(stream_reader * p_stream,t_size p_sizehint,abort_callback & p_abort) {
|
||||
stream_reader_formatter<>(*p_stream,p_abort) >> m_width >> m_height;
|
||||
}
|
||||
|
||||
t_uint32 m_width, m_height;
|
||||
};
|
||||
|
||||
class cfgWindowSizeTracker {
|
||||
public:
|
||||
cfgWindowSizeTracker(cfgWindowSize & p_var) : m_var(p_var), m_applied(false) {}
|
||||
|
||||
bool Apply(HWND p_wnd) {
|
||||
bool retVal = false;
|
||||
m_applied = false;
|
||||
if (m_var.m_width != ~0 && m_var.m_height != ~0) {
|
||||
CRect rect (0,0,m_var.m_width,m_var.m_height);
|
||||
if (AdjustWindowRectHelper(p_wnd, rect)) {
|
||||
SetWindowPos(p_wnd,NULL,0,0,rect.right-rect.left,rect.bottom-rect.top,SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER);
|
||||
retVal = true;
|
||||
}
|
||||
}
|
||||
m_applied = true;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT & lResult) {
|
||||
if (uMsg == WM_SIZE && m_applied) {
|
||||
if (lParam != 0) {
|
||||
m_var.m_width = (short)LOWORD(lParam); m_var.m_height = (short)HIWORD(lParam);
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
private:
|
||||
cfgWindowSize & m_var;
|
||||
bool m_applied;
|
||||
};
|
||||
|
||||
class cfgDialogSizeTracker : public cfgWindowSizeTracker {
|
||||
public:
|
||||
cfgDialogSizeTracker(cfgWindowSize & p_var) : cfgWindowSizeTracker(p_var) {}
|
||||
BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT & lResult) {
|
||||
if (cfgWindowSizeTracker::ProcessWindowMessage(hWnd, uMsg, wParam, lParam, lResult)) return TRUE;
|
||||
if (uMsg == WM_INITDIALOG) Apply(hWnd);
|
||||
return FALSE;
|
||||
}
|
||||
};
|
||||
|
||||
class cfgDialogPositionData {
|
||||
public:
|
||||
cfgDialogPositionData() : m_width(sizeInvalid), m_height(sizeInvalid), m_posX(posInvalid), m_posY(posInvalid) {}
|
||||
|
||||
void OverrideDefaultSize(t_uint32 width, t_uint32 height) {
|
||||
if (m_width == sizeInvalid && m_height == sizeInvalid) {
|
||||
m_width = width; m_height = height; m_posX = m_posY = posInvalid;
|
||||
m_dpiX = m_dpiY = 96;
|
||||
}
|
||||
}
|
||||
|
||||
void AddWindow(CWindow wnd) {
|
||||
TryFetchConfig();
|
||||
m_windows += wnd;
|
||||
ApplyConfig(wnd);
|
||||
}
|
||||
void RemoveWindow(CWindow wnd) {
|
||||
if (m_windows.contains(wnd)) {
|
||||
StoreConfig(wnd); m_windows -= wnd;
|
||||
}
|
||||
}
|
||||
void FetchConfig() {TryFetchConfig();}
|
||||
|
||||
private:
|
||||
BOOL ApplyConfig(CWindow wnd) {
|
||||
ApplyDPI();
|
||||
CWindow wndParent = wnd.GetParent();
|
||||
UINT flags = SWP_NOACTIVATE | SWP_NOZORDER;
|
||||
CRect rc;
|
||||
if (!GetClientRectAsSC(wnd,rc)) return FALSE;
|
||||
if (m_width != sizeInvalid && m_height != sizeInvalid && (wnd.GetWindowLong(GWL_STYLE) & WS_SIZEBOX) != 0) {
|
||||
rc.right = rc.left + m_width;
|
||||
rc.bottom = rc.top + m_height;
|
||||
} else {
|
||||
flags |= SWP_NOSIZE;
|
||||
}
|
||||
if (wndParent != NULL) {
|
||||
CRect rcParent;
|
||||
if (GetParentWndRect(wndParent, rcParent)) {
|
||||
if (m_posX != posInvalid && m_posY != posInvalid) {
|
||||
rc.MoveToXY( rcParent.TopLeft() + CPoint(m_posX, m_posY) );
|
||||
} else {
|
||||
CPoint center = rcParent.CenterPoint();
|
||||
rc.MoveToXY( center.x - rc.Width() / 2, center.y - rc.Height() / 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!AdjustWindowRectHelper(wnd, rc)) return FALSE;
|
||||
|
||||
DeOverlap(wnd, rc);
|
||||
|
||||
{
|
||||
CRect rcAdjust(0,0,1,1);
|
||||
if (wndParent != NULL) {
|
||||
CRect temp;
|
||||
if (wndParent.GetWindowRect(temp)) rcAdjust = temp;
|
||||
}
|
||||
AdjustRectToScreenArea(rc, rcAdjust);
|
||||
}
|
||||
|
||||
|
||||
return wnd.SetWindowPos(NULL, rc, flags);
|
||||
}
|
||||
struct DeOverlapState {
|
||||
CWindow m_thisWnd;
|
||||
CPoint m_topLeft;
|
||||
bool m_match;
|
||||
};
|
||||
static BOOL CALLBACK MyEnumChildProc(HWND wnd, LPARAM param) {
|
||||
DeOverlapState * state = reinterpret_cast<DeOverlapState*>(param);
|
||||
if (wnd != state->m_thisWnd && IsWindowVisible(wnd) ) {
|
||||
CRect rc;
|
||||
if (GetWindowRect(wnd, rc)) {
|
||||
if (rc.TopLeft() == state->m_topLeft) {
|
||||
state->m_match = true; return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
static bool DeOverlapTest(CWindow wnd, CPoint topLeft) {
|
||||
DeOverlapState state = {};
|
||||
state.m_thisWnd = wnd; state.m_topLeft = topLeft; state.m_match = false;
|
||||
EnumThreadWindows(GetCurrentThreadId(), MyEnumChildProc, reinterpret_cast<LPARAM>(&state));
|
||||
return state.m_match;
|
||||
}
|
||||
static int DeOverlapDelta() {
|
||||
return pfc::max_t<int>(GetSystemMetrics(SM_CYCAPTION),1);
|
||||
}
|
||||
static void DeOverlap(CWindow wnd, CRect & rc) {
|
||||
const int delta = DeOverlapDelta();
|
||||
for(;;) {
|
||||
if (!DeOverlapTest(wnd, rc.TopLeft())) break;
|
||||
rc.OffsetRect(delta,delta);
|
||||
}
|
||||
}
|
||||
BOOL StoreConfig(CWindow wnd) {
|
||||
CRect rc;
|
||||
if (!GetClientRectAsSC(wnd, rc)) return FALSE;
|
||||
const CSize DPI = QueryScreenDPIEx();
|
||||
m_dpiX = DPI.cx; m_dpiY = DPI.cy;
|
||||
m_width = rc.Width(); m_height = rc.Height();
|
||||
m_posX = m_posY = posInvalid;
|
||||
CWindow parent = wnd.GetParent();
|
||||
if (parent != NULL) {
|
||||
CRect rcParent;
|
||||
if (GetParentWndRect(parent, rcParent)) {
|
||||
m_posX = rc.left - rcParent.left;
|
||||
m_posY = rc.top - rcParent.top;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
void TryFetchConfig() {
|
||||
for(auto walk = m_windows.cfirst(); walk.is_valid(); ++walk) {
|
||||
if (StoreConfig(*walk)) break;
|
||||
}
|
||||
}
|
||||
|
||||
void ApplyDPI() {
|
||||
const CSize screenDPI = QueryScreenDPIEx();
|
||||
if (screenDPI.cx == 0 || screenDPI.cy == 0) {
|
||||
PFC_ASSERT(!"Should not get here - something seriously wrong with the OS");
|
||||
return;
|
||||
}
|
||||
if (m_dpiX != dpiInvalid && m_dpiX != screenDPI.cx) {
|
||||
if (m_width != sizeInvalid) m_width = MulDiv(m_width, screenDPI.cx, m_dpiX);
|
||||
if (m_posX != posInvalid) m_posX = MulDiv(m_posX, screenDPI.cx, m_dpiX);
|
||||
}
|
||||
if (m_dpiY != dpiInvalid && m_dpiY != screenDPI.cy) {
|
||||
if (m_height != sizeInvalid) m_height = MulDiv(m_height, screenDPI.cy, m_dpiY);
|
||||
if (m_posY != posInvalid) m_posY = MulDiv(m_posY, screenDPI.cy, m_dpiY);
|
||||
}
|
||||
m_dpiX = screenDPI.cx;
|
||||
m_dpiY = screenDPI.cy;
|
||||
}
|
||||
CSize GrabDPI() const {
|
||||
CSize DPI(96,96);
|
||||
if (m_dpiX != dpiInvalid) DPI.cx = m_dpiX;
|
||||
if (m_dpiY != dpiInvalid) DPI.cy = m_dpiY;
|
||||
return DPI;
|
||||
}
|
||||
|
||||
static BOOL GetParentWndRect(CWindow wndParent, CRect & rc) {
|
||||
if (!wndParent.IsIconic()) {
|
||||
return wndParent.GetWindowRect(rc);
|
||||
}
|
||||
WINDOWPLACEMENT pl = {sizeof(pl)};
|
||||
if (!wndParent.GetWindowPlacement(&pl)) return FALSE;
|
||||
rc = pl.rcNormalPosition;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
pfc::avltree_t<CWindow> m_windows;
|
||||
public:
|
||||
t_uint32 m_width, m_height;
|
||||
t_int32 m_posX, m_posY;
|
||||
t_uint32 m_dpiX, m_dpiY;
|
||||
enum {
|
||||
posInvalid = 0x80000000,
|
||||
sizeInvalid = 0xFFFFFFFF,
|
||||
dpiInvalid = 0,
|
||||
};
|
||||
};
|
||||
|
||||
FB2K_STREAM_READER_OVERLOAD(cfgDialogPositionData) {
|
||||
stream >> value.m_width >> value.m_height;
|
||||
try {
|
||||
stream >> value.m_posX >> value.m_posY >> value.m_dpiX >> value.m_dpiY;
|
||||
} catch(exception_io_data) {
|
||||
value.m_posX = value.m_posY = cfgDialogPositionData::posInvalid;
|
||||
value.m_dpiX = value.m_dpiY = cfgDialogPositionData::dpiInvalid;
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
FB2K_STREAM_WRITER_OVERLOAD(cfgDialogPositionData) {
|
||||
return stream << value.m_width << value.m_height << value.m_posX << value.m_posY << value.m_dpiX << value.m_dpiY;
|
||||
}
|
||||
|
||||
class cfgDialogPosition : public cfgDialogPositionData, public cfg_var {
|
||||
public:
|
||||
cfgDialogPosition(const GUID & id) : cfg_var(id) {}
|
||||
void get_data_raw(stream_writer * p_stream,abort_callback & p_abort) {FetchConfig(); stream_writer_formatter<>(*p_stream, p_abort) << *pfc::implicit_cast<cfgDialogPositionData*>(this);}
|
||||
void set_data_raw(stream_reader * p_stream,t_size p_sizehint,abort_callback & p_abort) {stream_reader_formatter<>(*p_stream, p_abort) >> *pfc::implicit_cast<cfgDialogPositionData*>(this);}
|
||||
};
|
||||
|
||||
class cfgDialogPositionTracker {
|
||||
public:
|
||||
cfgDialogPositionTracker(cfgDialogPosition & p_var) : m_var(p_var) {}
|
||||
~cfgDialogPositionTracker() {Cleanup();}
|
||||
|
||||
BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT & lResult) {
|
||||
if (uMsg == WM_CREATE || uMsg == WM_INITDIALOG) {
|
||||
Cleanup();
|
||||
m_wnd = hWnd;
|
||||
m_var.AddWindow(m_wnd);
|
||||
} else if (uMsg == WM_DESTROY) {
|
||||
PFC_ASSERT( hWnd == m_wnd );
|
||||
Cleanup();
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
private:
|
||||
void Cleanup() {
|
||||
if (m_wnd != NULL) {
|
||||
m_var.RemoveWindow(m_wnd);
|
||||
m_wnd = NULL;
|
||||
}
|
||||
}
|
||||
cfgDialogPosition & m_var;
|
||||
CWindow m_wnd;
|
||||
};
|
||||
122
foobar2000/ATLHelpers/foobar2000_ATL_helpers.vcxproj
Normal file
122
foobar2000/ATLHelpers/foobar2000_ATL_helpers.vcxproj
Normal file
@@ -0,0 +1,122 @@
|
||||
<?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>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{622E8B19-8109-4717-BD4D-9657AA78363E}</ProjectGuid>
|
||||
<RootNamespace>foobar2000_ATL_helpers</RootNamespace>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<WindowsTargetPlatformVersion>10.0.14393.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<PlatformToolset>v141_xp</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<PlatformToolset>v141_xp</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup>
|
||||
<_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
|
||||
<OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(SolutionDir)$(Configuration)\</OutDir>
|
||||
<IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(Configuration)\</IntDir>
|
||||
<OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(SolutionDir)$(Configuration)\</OutDir>
|
||||
<IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
<TreatSpecificWarningsAsErrors>4715</TreatSpecificWarningsAsErrors>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<Optimization>MinSpace</Optimization>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<FloatingPointModel>Fast</FloatingPointModel>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
|
||||
<AdditionalOptions>/d2notypeopt %(AdditionalOptions)</AdditionalOptions>
|
||||
<TreatSpecificWarningsAsErrors>4715</TreatSpecificWarningsAsErrors>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<StringPooling>true</StringPooling>
|
||||
<OmitFramePointers>true</OmitFramePointers>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="AutoComplete.cpp" />
|
||||
<ClCompile Include="CDialogResizeHelper.cpp" />
|
||||
<ClCompile Include="CEditWithButtons.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="inplace_edit.cpp" />
|
||||
<ClCompile Include="inplace_edit_v2.cpp" />
|
||||
<ClCompile Include="misc.cpp" />
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ui_element_helpers.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="ATLHelpers.h" />
|
||||
<ClInclude Include="ATLHelpersLean.h" />
|
||||
<ClInclude Include="AutoComplete.h" />
|
||||
<ClInclude Include="BumpableElem.h" />
|
||||
<ClInclude Include="CButtonLite.h" />
|
||||
<ClInclude Include="CDialogResizeHelper.h" />
|
||||
<ClInclude Include="CEditWithButtons.h" />
|
||||
<ClInclude Include="CFlashWindow.h" />
|
||||
<ClInclude Include="CIconOverlayWindow.h" />
|
||||
<ClInclude Include="CMiddleDragImpl.h" />
|
||||
<ClInclude Include="Controls.h" />
|
||||
<ClInclude Include="GDIUtils.h" />
|
||||
<ClInclude Include="inplace_edit.h" />
|
||||
<ClInclude Include="inplace_edit_v2.h" />
|
||||
<ClInclude Include="misc.h" />
|
||||
<ClInclude Include="stdafx.h" />
|
||||
<ClInclude Include="ui_element_helpers.h" />
|
||||
<ClInclude Include="WindowPositionUtils.h" />
|
||||
<ClInclude Include="WTL-PP.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
102
foobar2000/ATLHelpers/foobar2000_ATL_helpers.vcxproj.filters
Normal file
102
foobar2000/ATLHelpers/foobar2000_ATL_helpers.vcxproj.filters
Normal file
@@ -0,0 +1,102 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<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="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="AutoComplete.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CDialogResizeHelper.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="inplace_edit.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="inplace_edit_v2.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="misc.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CEditWithButtons.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ui_element_helpers.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="ATLHelpers.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="AutoComplete.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="BumpableElem.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CDialogResizeHelper.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GDIUtils.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="inplace_edit.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="inplace_edit_v2.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="stdafx.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="WTL-PP.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ATLHelpersLean.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="WindowPositionUtils.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="misc.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CFlashWindow.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Controls.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CEditWithButtons.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CButtonLite.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="ui_element_helpers.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
477
foobar2000/ATLHelpers/inplace_edit.cpp
Normal file
477
foobar2000/ATLHelpers/inplace_edit.cpp
Normal file
@@ -0,0 +1,477 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "inplace_edit.h"
|
||||
#include "AutoComplete.h"
|
||||
#include "misc.h"
|
||||
#include "WTL-PP.h"
|
||||
|
||||
#ifndef WM_MOUSEHWHEEL
|
||||
#define WM_MOUSEHWHEEL 0x20E
|
||||
#endif
|
||||
|
||||
using namespace InPlaceEdit;
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
enum {
|
||||
MSG_COMPLETION = WM_USER,
|
||||
MSG_DISABLE_EDITING
|
||||
};
|
||||
|
||||
|
||||
static pfc::avltree_t<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.cfirst(); walk.is_valid(); ++walk ) {
|
||||
GAbortEditing(*walk, code);
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsSamePopup(CWindow wnd1, CWindow wnd2) {
|
||||
return FindOwningPopup(wnd1) == FindOwningPopup(wnd2);
|
||||
}
|
||||
|
||||
static void MouseEventTest(HWND target, CPoint pt, bool isWheel) {
|
||||
for(auto walk = g_editboxes.cfirst(); walk.is_valid(); ++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);
|
||||
}
|
||||
#if 0
|
||||
static LRESULT CALLBACK GKeyboardProc(int code, WPARAM wp, LPARAM lp) {
|
||||
if (code == HC_ACTION && (lp & (1<<31)) == 0) {
|
||||
switch(wp) {
|
||||
case VK_RETURN:
|
||||
if (!IsKeyPressed(VK_LCONTROL) && !IsKeyPressed(VK_RCONTROL)) {
|
||||
GAbortEditing(KEditEnter);
|
||||
}
|
||||
break;
|
||||
case VK_TAB:
|
||||
GAbortEditing(IsKeyPressed(VK_SHIFT) ? KEditShiftTab : KEditTab);
|
||||
break;
|
||||
case VK_ESCAPE:
|
||||
GAbortEditing(KEditAborted);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return CallNextHookEx(g_keyHook,code,wp,lp);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void on_editbox_creation(HWND p_editbox) {
|
||||
PFC_ASSERT( core_api::is_main_thread() );
|
||||
g_editboxes.add(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_item(p_editbox);
|
||||
if (g_editboxes.get_count() == 0) {
|
||||
UnhookHelper(g_hook); /*UnhookHelper(g_keyHook);*/
|
||||
}
|
||||
}
|
||||
|
||||
class CInPlaceEditBox : public CContainedWindowSimpleT<CEdit> {
|
||||
public:
|
||||
CInPlaceEditBox() : m_selfDestruct(), m_suppressChar() {}
|
||||
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)
|
||||
END_MSG_MAP()
|
||||
void OnCreation() {
|
||||
m_typableScope.Set(m_hWnd);
|
||||
on_editbox_creation(m_hWnd);
|
||||
}
|
||||
private:
|
||||
void OnDestroy() {
|
||||
m_selfDestruct = true;
|
||||
m_typableScope.Set(NULL);
|
||||
on_editbox_destruction(m_hWnd);
|
||||
SetMsgHandled(FALSE);
|
||||
}
|
||||
int OnCreate(LPCREATESTRUCT lpCreateStruct) {
|
||||
OnCreation();
|
||||
SetMsgHandled(FALSE);
|
||||
return 0;
|
||||
}
|
||||
UINT OnGetDlgCode(LPMSG lpMsg) {
|
||||
if (lpMsg == NULL) {
|
||||
return DLGC_WANTALLKEYS;
|
||||
} else {
|
||||
switch(lpMsg->message) {
|
||||
case WM_KEYDOWN:
|
||||
case WM_SYSKEYDOWN:
|
||||
switch(lpMsg->wParam) {
|
||||
case VK_TAB:
|
||||
case VK_ESCAPE:
|
||||
case VK_RETURN:
|
||||
return DLGC_WANTALLKEYS;
|
||||
default:
|
||||
SetMsgHandled(FALSE); return 0;
|
||||
}
|
||||
default:
|
||||
SetMsgHandled(FALSE); return 0;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
void OnKillFocus(CWindow wndFocus) {
|
||||
ForwardCompletion(KEditLostFocus);
|
||||
SetMsgHandled(FALSE);
|
||||
}
|
||||
|
||||
void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) {
|
||||
if (m_suppressChar != 0) {
|
||||
UINT code = nFlags & 0xFF;
|
||||
if (code == m_suppressChar) 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);
|
||||
}
|
||||
}
|
||||
|
||||
CTypableWindowScope m_typableScope;
|
||||
bool m_selfDestruct;
|
||||
UINT m_suppressChar;
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
|
||||
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();
|
||||
|
||||
uSetWindowText(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,completion_notify_ptr 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)
|
||||
{
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
OnCompletion(wParam);
|
||||
if (!m_selfDestruct) {
|
||||
m_selfDestruct = true;
|
||||
DestroyWindow();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
void OnEditChange(UINT, int, CWindow source) {
|
||||
if (m_initialized && !m_disable_editing) {
|
||||
uGetWindowText(source,*m_content);
|
||||
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.is_valid()) m_notify->on_completion(code);
|
||||
}
|
||||
}
|
||||
|
||||
const pfc::rcptr_t<pfc::string_base> m_content;
|
||||
const completion_notify_ptr 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(completion_notify_ptr p_notify) {
|
||||
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,completion_notify_ptr 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,completion_notify_ptr 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,completion_notify_ptr p_notify, IUnknown * ACData , DWORD ACOpts) {
|
||||
try {
|
||||
PFC_ASSERT( (CWindow(p_parentwnd).GetWindowLong(GWL_STYLE) & WS_CLIPCHILDREN) != 0 );
|
||||
return (new CWindowAutoLifetime<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,completion_notify_ptr 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);
|
||||
}
|
||||
}
|
||||
132
foobar2000/ATLHelpers/inplace_edit.h
Normal file
132
foobar2000/ATLHelpers/inplace_edit.h
Normal file
@@ -0,0 +1,132 @@
|
||||
#pragma once
|
||||
|
||||
#include "../helpers/listview_helper.h"
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
HWND Start(HWND p_parentwnd,const RECT & p_rect,bool p_multiline,pfc::rcptr_t<pfc::string_base> p_content,completion_notify_ptr p_notify);
|
||||
|
||||
HWND StartEx(HWND p_parentwnd,const RECT & p_rect,unsigned p_flags,pfc::rcptr_t<pfc::string_base> p_content,completion_notify_ptr p_notify, IUnknown * ACData = NULL, DWORD ACOpts = 0);
|
||||
|
||||
void Start_FromListView(HWND p_listview,unsigned p_item,unsigned p_subitem,unsigned p_linecount,pfc::rcptr_t<pfc::string_base> p_content,completion_notify_ptr 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,completion_notify_ptr 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);
|
||||
|
||||
|
||||
class CTableEditHelper {
|
||||
public:
|
||||
void TableEdit_Start(HWND p_listview,unsigned p_item,unsigned p_column,unsigned p_itemcount,unsigned p_columncount,unsigned p_basecolumn,unsigned p_flags = 0) {
|
||||
if (m_notify.is_valid() || p_columncount == 0 || p_itemcount == 0 || p_item >= p_itemcount || p_column >= p_columncount) return;
|
||||
m_listview = p_listview;
|
||||
m_item = p_item;
|
||||
m_column = p_column;
|
||||
m_itemcount = p_itemcount;
|
||||
m_columncount = p_columncount;
|
||||
m_basecolumn = p_basecolumn;
|
||||
m_flags = p_flags;
|
||||
__Start();
|
||||
}
|
||||
|
||||
void TableEdit_Abort(bool p_forwardcontent) {
|
||||
if (m_notify.is_valid()) {
|
||||
m_notify->orphan();
|
||||
m_notify.release();
|
||||
|
||||
if (p_forwardcontent && (m_flags & KFlagReadOnly) == 0) {
|
||||
if (m_content.is_valid()) {
|
||||
pfc::string8 temp(*m_content);
|
||||
m_content.release();
|
||||
TableEdit_SetItemText(m_item,m_column,temp);
|
||||
}
|
||||
} else {
|
||||
m_content.release();
|
||||
}
|
||||
SetFocus(NULL);
|
||||
TableEdit_Finished();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool TableEdit_IsActive() const {
|
||||
return m_notify.is_valid();
|
||||
}
|
||||
|
||||
virtual bool TableEdit_GetItemText(unsigned p_item,unsigned p_column,pfc::string_base & p_out,unsigned & p_linecount) {
|
||||
listview_helper::get_item_text(m_listview,p_item,p_column + m_basecolumn,p_out);
|
||||
p_linecount = pfc::is_multiline(p_out) ? 5 : 1;
|
||||
return true;
|
||||
}
|
||||
virtual void TableEdit_SetItemText(unsigned p_item,unsigned p_column,const char * p_text) {
|
||||
listview_helper::set_item_text(m_listview,p_item,p_column + m_basecolumn,p_text);
|
||||
}
|
||||
|
||||
virtual void TableEdit_Finished() {}
|
||||
|
||||
void on_task_completion(unsigned p_taskid,unsigned p_state) {
|
||||
if (p_taskid == KTaskID) {
|
||||
m_notify.release();
|
||||
if (m_content.is_valid()) {
|
||||
if (p_state & InPlaceEdit::KEditFlagContentChanged) {
|
||||
TableEdit_SetItemText(m_item,m_column,*m_content);
|
||||
}
|
||||
m_content.release();
|
||||
}
|
||||
/*if (InPlaceEdit::TableEditAdvance(m_item,m_column,m_itemcount,m_columncount,p_state))*/
|
||||
if (TableEdit_OnEditCompleted(m_item,m_column,p_state) &&
|
||||
InPlaceEdit::TableEditAdvance_ListView(m_listview,m_basecolumn,m_item,m_column,m_itemcount,m_columncount,p_state)) {
|
||||
__Start();
|
||||
} else {
|
||||
TableEdit_Finished();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~CTableEditHelper() {
|
||||
if (m_notify.is_valid()) {
|
||||
m_notify->orphan();
|
||||
m_notify.release();
|
||||
}
|
||||
}
|
||||
protected:
|
||||
HWND TableEdit_GetListView() const {return m_listview;}
|
||||
//return false to abort
|
||||
virtual bool TableEdit_OnEditCompleted(unsigned item,unsigned column,unsigned state) {return true;}
|
||||
private:
|
||||
void __Start() {
|
||||
listview_helper::select_single_item(m_listview,m_item);
|
||||
m_content.new_t();
|
||||
unsigned linecount = 1;
|
||||
if (!TableEdit_GetItemText(m_item,m_column,*m_content,linecount)) return;
|
||||
m_notify = completion_notify_create(this,KTaskID);
|
||||
InPlaceEdit::Start_FromListViewEx(m_listview,m_item,m_column+m_basecolumn,linecount,m_flags,m_content,m_notify);
|
||||
}
|
||||
enum {
|
||||
KTaskID = 0xc0ffee
|
||||
};
|
||||
HWND m_listview;
|
||||
unsigned m_item,m_column;
|
||||
unsigned m_itemcount,m_columncount,m_basecolumn;
|
||||
unsigned m_flags;
|
||||
pfc::rcptr_t<pfc::string8> m_content;
|
||||
service_ptr_t<completion_notify_orphanable> m_notify;
|
||||
};
|
||||
|
||||
}
|
||||
147
foobar2000/ATLHelpers/inplace_edit_v2.cpp
Normal file
147
foobar2000/ATLHelpers/inplace_edit_v2.cpp
Normal file
@@ -0,0 +1,147 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "../helpers/win32_misc.h"
|
||||
#include "inplace_edit.h"
|
||||
#include "inplace_edit_v2.h"
|
||||
#include "AutoComplete.h"
|
||||
|
||||
|
||||
namespace InPlaceEdit {
|
||||
|
||||
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 & item, t_size & subItem, t_uint32 whathappened) {
|
||||
//moo
|
||||
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);
|
||||
return true;
|
||||
}
|
||||
|
||||
void CTableEditHelperV2::TableEdit_Abort(bool forwardContent) {
|
||||
if (this->have_task(KTaskID)) {
|
||||
this->orphan_task(KTaskID);
|
||||
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();
|
||||
SetFocus(NULL);
|
||||
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_editData.new_t();
|
||||
t_size lineCount = 1;
|
||||
TableEdit_GetField(m_editItem, m_editSubItem, *m_editData, lineCount);
|
||||
m_editFlags = TableEdit_GetEditFlags(m_editItem, m_editSubItem);
|
||||
|
||||
RECT rc = TableEdit_GetItemRect(m_editItem, m_editSubItem);
|
||||
if (lineCount > 1) {
|
||||
rc.bottom = rc.top + (rc.bottom - rc.top) * lineCount;
|
||||
m_editFlags |= KFlagMultiLine;
|
||||
}
|
||||
pfc::com_ptr_t<IUnknown> acl;
|
||||
if (!TableEdit_GetAutoComplete(m_editItem, m_editSubItem, acl)) acl.release();
|
||||
|
||||
InPlaceEdit::StartEx(TableEdit_GetParentWnd(), rc, m_editFlags, m_editData, create_task(KTaskID), acl.get_ptr(), ACO_AUTOSUGGEST);
|
||||
}
|
||||
|
||||
void CTableEditHelperV2::on_task_completion(unsigned id, unsigned status) {
|
||||
if (id == KTaskID) {
|
||||
orphan_task(KTaskID);
|
||||
if (m_editData.is_valid()) {
|
||||
if (status & InPlaceEdit::KEditFlagContentChanged) {
|
||||
TableEdit_SetField(m_editItem,m_editSubItem,*m_editData);
|
||||
}
|
||||
m_editData.release();
|
||||
}
|
||||
|
||||
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(),item,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(), item, 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(), item, 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(), item ) );
|
||||
}
|
||||
}
|
||||
55
foobar2000/ATLHelpers/inplace_edit_v2.h
Normal file
55
foobar2000/ATLHelpers/inplace_edit_v2.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include "../helpers/listview_helper.h"
|
||||
|
||||
namespace InPlaceEdit {
|
||||
class NOVTABLE CTableEditHelperV2 : protected completion_notify_receiver {
|
||||
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 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;}
|
||||
void TableEdit_Start(t_size item, t_size subItem);
|
||||
void TableEdit_Abort(bool forwardContent);
|
||||
bool TableEdit_IsActive() const {return have_task(KTaskID);}
|
||||
protected:
|
||||
void on_task_completion(unsigned p_id,unsigned p_status);
|
||||
private:
|
||||
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;
|
||||
static const unsigned KTaskID = 0x6f0a3de6;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
class NOVTABLE CTableEditHelperV2_ListView : public CTableEditHelperV2 {
|
||||
public:
|
||||
RECT TableEdit_GetItemRect(t_size item, t_size subItem) const;
|
||||
void TableEdit_GetField(t_size item, t_size subItem, pfc::string_base & out, t_size & lineCount);
|
||||
void TableEdit_SetField(t_size item, t_size subItem, const char * value);
|
||||
|
||||
t_size TableEdit_GetColumnCount() const {return (t_size) ListView_GetColumnCount(TableEdit_GetParentWnd());}
|
||||
|
||||
t_size TableEdit_GetItemCount() const;
|
||||
void TableEdit_SetItemFocus(t_size item, t_size subItem);
|
||||
|
||||
void TableEdit_GetColumnOrder(t_size * out, t_size count) const;
|
||||
};
|
||||
}
|
||||
100
foobar2000/ATLHelpers/misc.cpp
Normal file
100
foobar2000/ATLHelpers/misc.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
#include "stdafx.h"
|
||||
#include "Controls.h"
|
||||
#include "../helpers/win32_misc.h"
|
||||
|
||||
void PaintSeparatorControl(CWindow wnd) {
|
||||
CPaintDC dc(wnd);
|
||||
TCHAR buffer[512] = {};
|
||||
wnd.GetWindowText(buffer, _countof(buffer));
|
||||
const int txLen = 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) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ui_element_instance_standard_context_menu(service_ptr_t<ui_element_instance> p_elem, LPARAM p_pt) {
|
||||
CPoint pt;
|
||||
bool fromKeyboard;
|
||||
if (p_pt == -1) {
|
||||
fromKeyboard = true;
|
||||
if (!p_elem->edit_mode_context_menu_get_focus_point(pt)) {
|
||||
CRect rc;
|
||||
WIN32_OP_D( GetWindowRect(p_elem->get_wnd(), rc) );
|
||||
pt = rc.CenterPoint();
|
||||
}
|
||||
} else {
|
||||
fromKeyboard = false;
|
||||
pt = p_pt;
|
||||
}
|
||||
if (p_elem->edit_mode_context_menu_test(pt,fromKeyboard)) {
|
||||
const unsigned idBase = 1;
|
||||
CMenu menu;
|
||||
WIN32_OP( menu.CreatePopupMenu() );
|
||||
p_elem->edit_mode_context_menu_build(pt,fromKeyboard,menu,idBase);
|
||||
|
||||
int cmd;
|
||||
{
|
||||
CMenuSelectionReceiver_UiElement receiver(p_elem,idBase);
|
||||
cmd = menu.TrackPopupMenu(TPM_RIGHTBUTTON|TPM_NONOTIFY|TPM_RETURNCMD,pt.x,pt.y,receiver);
|
||||
}
|
||||
if (cmd > 0) p_elem->edit_mode_context_menu_command(pt,fromKeyboard,cmd,idBase);
|
||||
}
|
||||
}
|
||||
void ui_element_instance_standard_context_menu_eh(service_ptr_t<ui_element_instance> p_elem, LPARAM p_pt) {
|
||||
try {
|
||||
ui_element_instance_standard_context_menu(p_elem, p_pt);
|
||||
} catch(std::exception const & e) {
|
||||
console::complain("Context menu failure", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if _WIN32_WINNT >= 0x501
|
||||
void HeaderControl_SetSortIndicator(CHeaderCtrl header, int column, bool isUp) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
440
foobar2000/ATLHelpers/misc.h
Normal file
440
foobar2000/ATLHelpers/misc.h
Normal file
@@ -0,0 +1,440 @@
|
||||
#pragma once
|
||||
|
||||
#include "../helpers/win32_misc.h"
|
||||
#include "WTL-PP.h"
|
||||
#include <utility>
|
||||
|
||||
class CMenuSelectionReceiver : public CWindowImpl<CMenuSelectionReceiver> {
|
||||
public:
|
||||
CMenuSelectionReceiver(HWND p_parent) {
|
||||
WIN32_OP( Create(p_parent) != NULL );
|
||||
}
|
||||
~CMenuSelectionReceiver() {
|
||||
if (m_hWnd != NULL) DestroyWindow();
|
||||
}
|
||||
typedef CWindowImpl<CMenuSelectionReceiver> _baseClass;
|
||||
DECLARE_WND_CLASS_EX(TEXT("{DF0087DB-E765-4283-BBAB-6AB2E8AB64A1}"),0,0);
|
||||
|
||||
BEGIN_MSG_MAP(CMenuSelectionReceiver)
|
||||
MESSAGE_HANDLER(WM_MENUSELECT,OnMenuSelect)
|
||||
END_MSG_MAP()
|
||||
protected:
|
||||
virtual bool QueryHint(unsigned p_id,pfc::string_base & p_out) {
|
||||
return false;
|
||||
}
|
||||
private:
|
||||
LRESULT OnMenuSelect(UINT,WPARAM p_wp,LPARAM p_lp,BOOL&) {
|
||||
if (p_lp != 0) {
|
||||
if (HIWORD(p_wp) & MF_POPUP) {
|
||||
m_status.release();
|
||||
} else {
|
||||
pfc::string8 msg;
|
||||
UINT cmd = LOWORD(p_wp);
|
||||
if ( cmd == 0 || !QueryHint(cmd,msg)) {
|
||||
m_status.release();
|
||||
} else {
|
||||
if (m_status.is_empty()) {
|
||||
if (!static_api_ptr_t<ui_control>()->override_status_text_create(m_status)) m_status.release();
|
||||
}
|
||||
if (m_status.is_valid()) {
|
||||
m_status->override_text(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
m_status.release();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
service_ptr_t<ui_status_text_override> m_status;
|
||||
|
||||
PFC_CLASS_NOT_COPYABLE(CMenuSelectionReceiver,CMenuSelectionReceiver);
|
||||
};
|
||||
|
||||
class CMenuDescriptionMap : public CMenuSelectionReceiver {
|
||||
public:
|
||||
CMenuDescriptionMap(HWND p_parent) : CMenuSelectionReceiver(p_parent) {}
|
||||
void Set(unsigned p_id,const char * p_description) {m_content.set(p_id,p_description);}
|
||||
protected:
|
||||
bool QueryHint(unsigned p_id,pfc::string_base & p_out) {
|
||||
return m_content.query(p_id,p_out);
|
||||
}
|
||||
private:
|
||||
pfc::map_t<unsigned,pfc::string8> m_content;
|
||||
};
|
||||
|
||||
class CMenuDescriptionHybrid : public CMenuSelectionReceiver {
|
||||
public:
|
||||
CMenuDescriptionHybrid(HWND parent) : CMenuSelectionReceiver(parent) {}
|
||||
void Set(unsigned id, const char * desc) {m_content.set(id, desc);}
|
||||
|
||||
void SetCM(contextmenu_manager::ptr mgr, unsigned base, unsigned max) {
|
||||
m_cmMgr = mgr; m_cmMgr_base = base; m_cmMgr_max = max;
|
||||
}
|
||||
protected:
|
||||
bool QueryHint(unsigned p_id,pfc::string_base & p_out) {
|
||||
if (m_cmMgr.is_valid() && p_id >= m_cmMgr_base && p_id < m_cmMgr_max) {
|
||||
return m_cmMgr->get_description_by_id(p_id - m_cmMgr_base,p_out);
|
||||
}
|
||||
return m_content.query(p_id,p_out);
|
||||
}
|
||||
private:
|
||||
pfc::map_t<unsigned,pfc::string8> m_content;
|
||||
contextmenu_manager::ptr m_cmMgr; unsigned m_cmMgr_base, m_cmMgr_max;
|
||||
};
|
||||
|
||||
inline pfc::string_base & operator<<(pfc::string_base & p_fmt,const CPoint & p_point) {
|
||||
return p_fmt << "(" << p_point.x << "," << p_point.y << ")";
|
||||
}
|
||||
|
||||
inline pfc::string_base & operator<<(pfc::string_base & p_fmt,const CRect & p_rect) {
|
||||
return p_fmt << "(" << p_rect.left << "," << p_rect.top << "," << p_rect.right << "," << p_rect.bottom << ")";
|
||||
}
|
||||
|
||||
template<typename TClass>
|
||||
class CAddDummyMessageMap : public TClass {
|
||||
public:
|
||||
BEGIN_MSG_MAP(CAddDummyMessageMap<TClass>)
|
||||
END_MSG_MAP()
|
||||
};
|
||||
|
||||
template<typename _parentClass> class CWindowFixSEH : public _parentClass { public:
|
||||
BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult, DWORD dwMsgMapID = 0) {
|
||||
__try {
|
||||
return _parentClass::ProcessWindowMessage(hWnd, uMsg, wParam, lParam, lResult, dwMsgMapID);
|
||||
} __except(uExceptFilterProc(GetExceptionInformation())) { return FALSE; /* should not get here */ }
|
||||
}
|
||||
template<typename ... arg_t> CWindowFixSEH( arg_t && ... arg ) : _parentClass( std::forward<arg_t>(arg) ... ) {}
|
||||
};
|
||||
|
||||
template<typename TClass>
|
||||
class CWindowAutoLifetime : public CWindowFixSEH<TClass> {
|
||||
public:
|
||||
typedef CWindowFixSEH<TClass> TBase;
|
||||
template<typename ... arg_t> CWindowAutoLifetime(HWND parent, arg_t && ... arg) : TBase( std::forward<arg_t>(arg) ... ) {Init(parent);}
|
||||
private:
|
||||
void Init(HWND parent) {WIN32_OP(this->Create(parent) != NULL);}
|
||||
void OnFinalMessage(HWND wnd) {PFC_ASSERT_NO_EXCEPTION( TBase::OnFinalMessage(wnd) ); PFC_ASSERT_NO_EXCEPTION(delete this);}
|
||||
};
|
||||
|
||||
template<typename TClass>
|
||||
class ImplementModelessTracking : public TClass {
|
||||
public:
|
||||
template<typename ... arg_t> ImplementModelessTracking(arg_t && ... arg ) : TClass(std::forward<arg_t>(arg) ... ) {}
|
||||
|
||||
BEGIN_MSG_MAP_EX(ImplementModelessTracking)
|
||||
MSG_WM_INITDIALOG(OnInitDialog)
|
||||
MSG_WM_DESTROY(OnDestroy)
|
||||
CHAIN_MSG_MAP(TClass)
|
||||
END_MSG_MAP_HOOK()
|
||||
private:
|
||||
BOOL OnInitDialog(CWindow, LPARAM) {m_modeless.Set( m_hWnd ); SetMsgHandled(FALSE); return FALSE; }
|
||||
void OnDestroy() {m_modeless.Set(NULL); SetMsgHandled(FALSE); }
|
||||
CModelessDialogEntry m_modeless;
|
||||
};
|
||||
|
||||
namespace fb2k {
|
||||
template<typename dialog_t, typename ... arg_t> dialog_t * newDialogEx( HWND parent, arg_t && ... arg ) {
|
||||
return new CWindowAutoLifetime<ImplementModelessTracking< dialog_t > > ( parent, std::forward<arg_t>(arg) ... );
|
||||
}
|
||||
template<typename dialog_t, typename ... arg_t> dialog_t * newDialog(arg_t && ... arg) {
|
||||
return new CWindowAutoLifetime<ImplementModelessTracking< dialog_t > > (core_api::get_main_window(), std::forward<arg_t>(arg) ...);
|
||||
}
|
||||
}
|
||||
|
||||
class CMenuSelectionReceiver_UiElement : public CMenuSelectionReceiver {
|
||||
public:
|
||||
CMenuSelectionReceiver_UiElement(service_ptr_t<ui_element_instance> p_owner,unsigned p_id_base) : CMenuSelectionReceiver(p_owner->get_wnd()), m_owner(p_owner), m_id_base(p_id_base) {}
|
||||
protected:
|
||||
bool QueryHint(unsigned p_id,pfc::string_base & p_out) {
|
||||
return m_owner->edit_mode_context_menu_get_description(p_id,m_id_base,p_out);
|
||||
}
|
||||
private:
|
||||
const unsigned m_id_base;
|
||||
const service_ptr_t<ui_element_instance> m_owner;
|
||||
};
|
||||
|
||||
void ui_element_instance_standard_context_menu(service_ptr_t<ui_element_instance> p_elem, LPARAM p_pt);
|
||||
void ui_element_instance_standard_context_menu_eh(service_ptr_t<ui_element_instance> p_elem, LPARAM p_pt);
|
||||
|
||||
#if _WIN32_WINNT >= 0x501
|
||||
void HeaderControl_SetSortIndicator(CHeaderCtrl header, int column, bool isUp);
|
||||
#endif
|
||||
|
||||
class CTypableWindowScope {
|
||||
public:
|
||||
CTypableWindowScope() : m_wnd() {}
|
||||
~CTypableWindowScope() {Set(NULL);}
|
||||
void Set(HWND wnd) {
|
||||
try {
|
||||
if (m_wnd != NULL) {
|
||||
static_api_ptr_t<ui_element_typable_window_manager>()->remove(m_wnd);
|
||||
}
|
||||
m_wnd = wnd;
|
||||
if (m_wnd != NULL) {
|
||||
static_api_ptr_t<ui_element_typable_window_manager>()->add(m_wnd);
|
||||
}
|
||||
} catch(exception_service_not_found) {
|
||||
m_wnd = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
HWND m_wnd;
|
||||
PFC_CLASS_NOT_COPYABLE_EX(CTypableWindowScope);
|
||||
};
|
||||
|
||||
|
||||
template<typename TBase> class CContainedWindowSimpleT : public CContainedWindowT<TBase>, public CMessageMap {
|
||||
public:
|
||||
CContainedWindowSimpleT() : CContainedWindowT<TBase>(this) {}
|
||||
BEGIN_MSG_MAP(CContainedWindowSimpleT)
|
||||
END_MSG_MAP()
|
||||
};
|
||||
|
||||
|
||||
static bool window_service_trait_defer_destruction(const service_base *) {return true;}
|
||||
|
||||
|
||||
//! Special service_impl_t replacement for service classes that also implement ATL/WTL windows.
|
||||
template<typename _t_base>
|
||||
class window_service_impl_t : public implement_service_query< CWindowFixSEH<_t_base> > {
|
||||
private:
|
||||
typedef window_service_impl_t<_t_base> t_self;
|
||||
typedef implement_service_query< CWindowFixSEH<_t_base> > t_base;
|
||||
public:
|
||||
BEGIN_MSG_MAP_EX(window_service_impl_t)
|
||||
MSG_WM_DESTROY(OnDestroyPassThru)
|
||||
CHAIN_MSG_MAP(__super)
|
||||
END_MSG_MAP_HOOK()
|
||||
|
||||
int FB2KAPI service_release() throw() {
|
||||
int ret = --m_counter;
|
||||
if (ret == 0) {
|
||||
if (window_service_trait_defer_destruction(this) && !InterlockedExchange(&m_delayedDestroyInProgress,1)) {
|
||||
PFC_ASSERT_NO_EXCEPTION( service_impl_helper::release_object_delayed(this); );
|
||||
} else if (m_hWnd != NULL) {
|
||||
if (!m_destroyWindowInProgress) { // don't double-destroy in weird scenarios
|
||||
PFC_ASSERT_NO_EXCEPTION( ::DestroyWindow(m_hWnd) );
|
||||
}
|
||||
} else {
|
||||
PFC_ASSERT_NO_EXCEPTION( delete this );
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
int FB2KAPI service_add_ref() throw() {return ++m_counter;}
|
||||
|
||||
template<typename ... arg_t>
|
||||
window_service_impl_t( arg_t && ... arg ) : t_base( std::forward<arg_t>(arg) ... ) {};
|
||||
private:
|
||||
void OnDestroyPassThru() {
|
||||
SetMsgHandled(FALSE); m_destroyWindowInProgress = true;
|
||||
}
|
||||
void OnFinalMessage(HWND p_wnd) {
|
||||
t_base::OnFinalMessage(p_wnd);
|
||||
service_ptr_t<service_base> bump(this);
|
||||
}
|
||||
volatile bool m_destroyWindowInProgress = false;
|
||||
volatile LONG m_delayedDestroyInProgress = 0;
|
||||
pfc::refcounter m_counter;
|
||||
};
|
||||
|
||||
|
||||
static void AppendMenuPopup(HMENU menu, UINT flags, CMenu & popup, const TCHAR * label) {
|
||||
PFC_ASSERT( flags & MF_POPUP );
|
||||
WIN32_OP( CMenuHandle(menu).AppendMenu(flags, popup, label) );
|
||||
popup.Detach();
|
||||
}
|
||||
|
||||
class CMessageMapDummy : public CMessageMap {
|
||||
public:
|
||||
BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam,
|
||||
LRESULT& lResult, DWORD dwMsgMapID) {return FALSE;}
|
||||
};
|
||||
|
||||
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(CDialogWithTooltip)
|
||||
MSG_WM_DESTROY(OnDestroy)
|
||||
END_MSG_MAP()
|
||||
|
||||
void ShowTip(UINT id, const TCHAR * label) {
|
||||
m_tip.Show(label, 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, 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(); SetMsgHandled(FALSE); }
|
||||
CPopupTooltipMessage m_tip;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<typename TDialog> class preferences_page_instance_impl : public TDialog {
|
||||
public:
|
||||
preferences_page_instance_impl(HWND parent, preferences_page_callback::ptr callback) : TDialog(callback) {WIN32_OP(this->Create(parent) != NULL);}
|
||||
HWND get_wnd() {return this->m_hWnd;}
|
||||
};
|
||||
static bool window_service_trait_defer_destruction(const preferences_page_instance *) {return false;}
|
||||
template<typename TDialog> class preferences_page_impl : public preferences_page_v3 {
|
||||
public:
|
||||
preferences_page_instance::ptr instantiate(HWND parent, preferences_page_callback::ptr callback) {
|
||||
return new window_service_impl_t<preferences_page_instance_impl<TDialog> >(parent, callback);
|
||||
}
|
||||
};
|
||||
|
||||
class CEmbeddedDialog : public CDialogImpl<CEmbeddedDialog> {
|
||||
public:
|
||||
CEmbeddedDialog(CMessageMap * owner, DWORD msgMapID, UINT dialogID) : m_owner(*owner), IDD(dialogID), m_msgMapID(msgMapID) {}
|
||||
|
||||
BEGIN_MSG_MAP(CEmbeddedDialog)
|
||||
CHAIN_MSG_MAP_ALT_MEMBER(m_owner, m_msgMapID)
|
||||
END_MSG_MAP()
|
||||
|
||||
const DWORD m_msgMapID;
|
||||
const UINT IDD;
|
||||
CMessageMap & m_owner;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
// here because of window_service_impl_t
|
||||
template<typename TImpl, typename TInterface = ui_element> class ui_element_impl : public TInterface {
|
||||
public:
|
||||
GUID get_guid() { return TImpl::g_get_guid(); }
|
||||
GUID get_subclass() { return TImpl::g_get_subclass(); }
|
||||
void get_name(pfc::string_base & out) { TImpl::g_get_name(out); }
|
||||
ui_element_instance::ptr instantiate(HWND parent, ui_element_config::ptr cfg, ui_element_instance_callback::ptr callback) {
|
||||
PFC_ASSERT(cfg->get_guid() == get_guid());
|
||||
service_nnptr_t<ui_element_instance_impl_helper> item = new window_service_impl_t<ui_element_instance_impl_helper>(cfg, callback);
|
||||
item->initialize_window(parent);
|
||||
return item;
|
||||
}
|
||||
ui_element_config::ptr get_default_configuration() { return TImpl::g_get_default_configuration(); }
|
||||
ui_element_children_enumerator_ptr enumerate_children(ui_element_config::ptr cfg) { return NULL; }
|
||||
bool get_description(pfc::string_base & out) { out = TImpl::g_get_description(); return true; }
|
||||
private:
|
||||
class ui_element_instance_impl_helper : public TImpl {
|
||||
public:
|
||||
ui_element_instance_impl_helper(ui_element_config::ptr cfg, ui_element_instance_callback::ptr callback) : TImpl(cfg, callback) {}
|
||||
|
||||
GUID get_guid() { return TImpl::g_get_guid(); }
|
||||
GUID get_subclass() { return TImpl::g_get_subclass(); }
|
||||
HWND get_wnd() { return *this; }
|
||||
};
|
||||
public:
|
||||
typedef ui_element_instance_impl_helper TInstance;
|
||||
static TInstance const & instanceGlobals() { return *reinterpret_cast<const TInstance*>(NULL); }
|
||||
};
|
||||
8
foobar2000/ATLHelpers/stdafx.cpp
Normal file
8
foobar2000/ATLHelpers/stdafx.cpp
Normal file
@@ -0,0 +1,8 @@
|
||||
// stdafx.cpp : source file that includes just the standard includes
|
||||
// foobar2000_ATL_helpers.pch will be the pre-compiled header
|
||||
// stdafx.obj will contain the pre-compiled type information
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
// TODO: reference any additional headers you need in STDAFX.H
|
||||
// and not in this file
|
||||
15
foobar2000/ATLHelpers/stdafx.h
Normal file
15
foobar2000/ATLHelpers/stdafx.h
Normal file
@@ -0,0 +1,15 @@
|
||||
// stdafx.h : include file for standard system include files,
|
||||
// or project specific include files that are used frequently, but
|
||||
// are changed infrequently
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later.
|
||||
#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows.
|
||||
#endif
|
||||
|
||||
|
||||
// TODO: reference additional headers your program requires here
|
||||
|
||||
#include "ATLHelpersLean.h"
|
||||
353
foobar2000/ATLHelpers/ui_element_helpers.cpp
Normal file
353
foobar2000/ATLHelpers/ui_element_helpers.cpp
Normal file
@@ -0,0 +1,353 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
#if FOOBAR2000_TARGET_VERSION >= 79
|
||||
|
||||
#include "ui_element_helpers.h"
|
||||
#include "../helpers/IDataObjectUtils.h"
|
||||
#include "misc.h"
|
||||
|
||||
namespace ui_element_helpers {
|
||||
|
||||
ui_element_instance_ptr instantiate_dummy(HWND p_parent,ui_element_config::ptr cfg, ui_element_instance_callback_ptr p_callback) {
|
||||
service_ptr_t<ui_element> ptr;
|
||||
if (!find(ptr,pfc::guid_null)) uBugCheck();
|
||||
return ptr->instantiate(p_parent,cfg,p_callback);
|
||||
}
|
||||
ui_element_instance_ptr instantiate(HWND p_parent,ui_element_config::ptr cfg,ui_element_instance_callback_ptr p_callback) {
|
||||
try {
|
||||
service_ptr_t<ui_element> ptr;
|
||||
if (!find(ptr,cfg->get_guid())) throw exception_io_data("UI Element Not Found");
|
||||
auto ret = ptr->instantiate(p_parent,cfg,p_callback);
|
||||
if (ret.is_empty()) throw std::runtime_error("Null UI Element returned");
|
||||
return std::move(ret);
|
||||
} catch(std::exception const & e) {
|
||||
console::complain("UI Element instantiation failure",e);
|
||||
return instantiate_dummy(p_parent,cfg,p_callback);
|
||||
}
|
||||
}
|
||||
|
||||
ui_element_instance_ptr update(ui_element_instance_ptr p_element,HWND p_parent,ui_element_config::ptr cfg,ui_element_instance_callback_ptr p_callback) {
|
||||
if (p_element.is_valid() && cfg->get_guid() == p_element->get_guid()) {
|
||||
p_element->set_configuration(cfg);
|
||||
return p_element;
|
||||
} else {
|
||||
return instantiate(p_parent,cfg,p_callback);
|
||||
}
|
||||
}
|
||||
|
||||
bool find(service_ptr_t<ui_element> & p_out,const GUID & p_guid) {
|
||||
return service_by_guid(p_out,p_guid);
|
||||
}
|
||||
|
||||
ui_element_children_enumerator_ptr enumerate_children(ui_element_config::ptr cfg) {
|
||||
service_ptr_t<ui_element> ptr;
|
||||
if (!find(ptr,cfg->get_guid())) return NULL;
|
||||
try {
|
||||
return ptr->enumerate_children(cfg);
|
||||
} catch(exception_io_data) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void ui_element_helpers::replace_with_new_element(ui_element_instance_ptr & p_item,const GUID & p_guid,HWND p_parent,ui_element_instance_callback_ptr p_callback) {
|
||||
abort_callback_dummy l_abort;
|
||||
ui_element_config::ptr cfg;
|
||||
try {
|
||||
if (p_item.is_empty()) {
|
||||
service_ptr_t<ui_element> ptr;
|
||||
if (!find(ptr,p_guid)) throw exception_io_data("UI Element Not Found");
|
||||
cfg = ptr->get_default_configuration();
|
||||
p_item = ptr->instantiate(p_parent,cfg,p_callback);
|
||||
} else if (p_item->get_guid() != p_guid) {
|
||||
service_ptr_t<ui_element> ptr;
|
||||
if (!find(ptr,p_guid)) throw exception_io_data("UI Element Not Found");
|
||||
cfg = ptr->import(p_item->get_configuration());
|
||||
//p_item.release();
|
||||
if (cfg.is_empty()) cfg = ptr->get_default_configuration();
|
||||
p_item = ptr->instantiate(p_parent,cfg,p_callback);
|
||||
}
|
||||
} catch(std::exception const & e) {
|
||||
console::complain("UI Element instantiation failure",e);
|
||||
if (cfg.is_empty()) cfg = ui_element_config::g_create_empty();
|
||||
p_item = instantiate_dummy(p_parent,cfg,p_callback);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
namespace {
|
||||
class CMyMenuSelectionReceiver : public CMenuSelectionReceiver {
|
||||
public:
|
||||
CMyMenuSelectionReceiver(HWND p_wnd,ui_element_helpers::ui_element_edit_tools * p_host,ui_element_instance_ptr p_client,unsigned p_client_id,unsigned p_host_base,unsigned p_client_base) : CMenuSelectionReceiver(p_wnd), m_host(p_host), m_client(p_client), m_client_id(p_client_id), m_host_base(p_host_base), m_client_base(p_client_base) {}
|
||||
bool QueryHint(unsigned p_id,pfc::string_base & p_out) {
|
||||
if (p_id >= m_client_base) {
|
||||
return m_client->edit_mode_context_menu_get_description(p_id,m_client_base,p_out);
|
||||
} else if (p_id >= m_host_base) {
|
||||
return m_host->host_edit_mode_context_menu_get_description(m_client_id,p_id,m_host_base,p_out);
|
||||
} else {
|
||||
const char * msg = ui_element_helpers::ui_element_edit_tools::description_from_menu_command(p_id);
|
||||
if (msg == NULL) return false;
|
||||
p_out = msg;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
private:
|
||||
ui_element_helpers::ui_element_edit_tools * const m_host;
|
||||
ui_element_instance_ptr const m_client;
|
||||
unsigned const m_client_id,m_host_base,m_client_base;
|
||||
};
|
||||
};
|
||||
|
||||
namespace HostHelperIDs {
|
||||
enum {ID_LABEL, ID_REPLACE = 1, ID_ADD_NEW, ID_CUT, ID_COPY, ID_PASTE, ID_CUSTOM_BASE};
|
||||
}
|
||||
|
||||
void ui_element_helpers::ui_element_edit_tools::standard_edit_context_menu(LPARAM p_point,ui_element_instance_ptr p_item,unsigned p_id,HWND p_parent) {
|
||||
using namespace HostHelperIDs;
|
||||
static_api_ptr_t<ui_element_common_methods> api;
|
||||
POINT pt;
|
||||
bool fromkeyboard = false;
|
||||
if (p_point == (LPARAM)(-1)) {
|
||||
fromkeyboard = true;
|
||||
if (!p_item->edit_mode_context_menu_get_focus_point(pt)) {
|
||||
CRect rect;
|
||||
WIN32_OP_D( CWindow(p_item->get_wnd()).GetWindowRect(&rect) );
|
||||
pt = rect.CenterPoint();
|
||||
}
|
||||
} else {
|
||||
pt.x = (short)LOWORD(p_point);
|
||||
pt.y = (short)HIWORD(p_point);
|
||||
}
|
||||
|
||||
CMenu menu;
|
||||
WIN32_OP( menu.CreatePopupMenu() );
|
||||
|
||||
const GUID sourceItemGuid = p_item->get_guid();
|
||||
const bool sourceItemEmpty = !!(sourceItemGuid == pfc::guid_null);
|
||||
|
||||
if (sourceItemEmpty) {
|
||||
WIN32_OP_D( menu.AppendMenu(MF_STRING,ID_ADD_NEW,TEXT(AddNewUIElementCommand)) );
|
||||
WIN32_OP_D( menu.SetMenuDefaultItem(ID_ADD_NEW) );
|
||||
} else {
|
||||
service_ptr_t<ui_element> elem;
|
||||
pfc::string8 name;
|
||||
if (find(elem,sourceItemGuid)) {
|
||||
elem->get_name(name);
|
||||
} else {
|
||||
name = "<unknown element>";
|
||||
}
|
||||
WIN32_OP_D( menu.AppendMenu(MF_STRING | MF_DISABLED,ID_LABEL,pfc::stringcvt::string_os_from_utf8(name)) );
|
||||
WIN32_OP_D( menu.AppendMenu(MF_SEPARATOR,(UINT_PTR)0,TEXT("")) );
|
||||
WIN32_OP_D( menu.AppendMenu(MF_STRING,ID_REPLACE,TEXT(ReplaceUIElementCommand)) );
|
||||
}
|
||||
WIN32_OP_D( menu.AppendMenu(MF_SEPARATOR,(UINT_PTR)0,TEXT("")) );
|
||||
|
||||
//menu.AppendMenu(MF_STRING,ID_REPLACE,TEXT(ReplaceUIElementCommand));
|
||||
WIN32_OP_D( menu.AppendMenu(MF_STRING | (sourceItemEmpty ? (MF_DISABLED|MF_GRAYED) : 0),ID_CUT,TEXT(CutUIElementCommand)) );
|
||||
WIN32_OP_D( menu.AppendMenu(MF_STRING | (sourceItemEmpty ? (MF_DISABLED|MF_GRAYED) : 0),ID_COPY,TEXT(CopyUIElementCommand)) );
|
||||
WIN32_OP_D( menu.AppendMenu(MF_STRING | (api->is_paste_available() ? 0 : (MF_DISABLED|MF_GRAYED)),ID_PASTE,TEXT(PasteUIElementCommand)) );
|
||||
|
||||
unsigned custom_walk = ID_CUSTOM_BASE;
|
||||
unsigned custom_base_host = ~0, custom_base_client = ~0;
|
||||
|
||||
if (host_edit_mode_context_menu_test(p_id,pt,fromkeyboard)) {
|
||||
menu.AppendMenu(MF_SEPARATOR,(UINT_PTR)0,TEXT(""));
|
||||
custom_base_host = custom_walk;
|
||||
host_edit_mode_context_menu_build(p_id,pt,fromkeyboard,menu,custom_walk);
|
||||
}
|
||||
|
||||
{
|
||||
if (p_item->edit_mode_context_menu_test(pt,fromkeyboard)) {
|
||||
WIN32_OP_D( menu.AppendMenu(MF_SEPARATOR,(UINT_PTR)0,TEXT("")) );
|
||||
custom_base_client = custom_walk;
|
||||
p_item->edit_mode_context_menu_build(pt,fromkeyboard,menu,custom_walk);
|
||||
}
|
||||
}
|
||||
int cmd;
|
||||
|
||||
{
|
||||
ui_element_highlight_scope s(p_item->get_wnd());
|
||||
CMyMenuSelectionReceiver receiver(p_parent,this,p_item,p_id,custom_base_host,custom_base_client);
|
||||
cmd = menu.TrackPopupMenu(TPM_RIGHTBUTTON|TPM_NONOTIFY|TPM_RETURNCMD,pt.x,pt.y,receiver);
|
||||
}
|
||||
|
||||
if (cmd > 0) {
|
||||
switch(cmd) {
|
||||
case ID_REPLACE:
|
||||
case ID_ADD_NEW:
|
||||
replace_dialog(p_item->get_wnd(),p_id,p_item->get_guid());
|
||||
break;
|
||||
case ID_COPY:
|
||||
api->copy(p_item);
|
||||
break;
|
||||
case ID_CUT:
|
||||
api->copy(p_item);
|
||||
p_item.release();
|
||||
host_replace_element(p_id,pfc::guid_null);
|
||||
break;
|
||||
case ID_PASTE:
|
||||
host_paste_element(p_id);
|
||||
break;
|
||||
default:
|
||||
if ((unsigned)cmd >= custom_base_client) {
|
||||
p_item->edit_mode_context_menu_command(pt,fromkeyboard,(unsigned)cmd,custom_base_client);
|
||||
} else if ((unsigned)cmd >= custom_base_host) {
|
||||
host_edit_mode_context_menu_command(p_id,pt,fromkeyboard,(unsigned)cmd,custom_base_host);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ui_element_helpers::ui_element_edit_tools::on_elem_replace(unsigned p_id, GUID const & newGuid) {
|
||||
m_replace_dialog.Detach();
|
||||
|
||||
if ( newGuid != pfc::guid_null ) {
|
||||
host_replace_element(p_id,newGuid);
|
||||
}
|
||||
}
|
||||
|
||||
void ui_element_helpers::ui_element_edit_tools::replace_dialog(HWND p_parent,unsigned p_id,const GUID & p_current) {
|
||||
_release_replace_dialog();
|
||||
|
||||
auto ks = m_killSwitch;
|
||||
|
||||
auto reply = ui_element_replace_dialog_notify::create( [=] ( GUID newGUID ) {
|
||||
if ( !*ks) {
|
||||
on_elem_replace(p_id, newGUID );
|
||||
}
|
||||
} );
|
||||
|
||||
HWND dlg = ui_element_common_methods_v3::get()->replace_element_dialog_start( p_parent, p_current, reply );
|
||||
|
||||
m_replace_dialog.Attach( dlg );
|
||||
}
|
||||
|
||||
const char * ui_element_helpers::ui_element_edit_tools::description_from_menu_command(unsigned p_id) {
|
||||
using namespace HostHelperIDs;
|
||||
switch(p_id) {
|
||||
case ID_REPLACE:
|
||||
return ReplaceUIElementDescription;
|
||||
case ID_CUT:
|
||||
return CutUIElementDescription;
|
||||
case ID_COPY:
|
||||
return CopyUIElementDescription;
|
||||
case ID_PASTE:
|
||||
return PasteUIElementDescription;
|
||||
case ID_ADD_NEW:
|
||||
return AddNewUIElementDescription;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void ui_element_helpers::ui_element_edit_tools::_release_replace_dialog() {
|
||||
if (m_replace_dialog.m_hWnd != NULL) {
|
||||
m_replace_dialog.DestroyWindow();
|
||||
}
|
||||
}
|
||||
|
||||
bool ui_element_helpers::enforce_min_max_info(CWindow p_window,ui_element_min_max_info const & p_info) {
|
||||
CRect rect;
|
||||
WIN32_OP_D( p_window.GetWindowRect(&rect) );
|
||||
t_uint32 width = (t_uint32) rect.Width();
|
||||
t_uint32 height = (t_uint32) rect.Height();
|
||||
bool changed = false;
|
||||
if (width < p_info.m_min_width) {changed = true; width = p_info.m_min_width;}
|
||||
if (width > p_info.m_max_width) {changed = true; width = p_info.m_max_width;}
|
||||
if (height < p_info.m_min_height) {changed = true; height = p_info.m_min_height;}
|
||||
if (height > p_info.m_max_height) {changed = true; height = p_info.m_max_height;}
|
||||
if (changed) {
|
||||
WIN32_OP_D( p_window.SetWindowPos(NULL,0,0,width,height,SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER) );
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ui_element_helpers::handle_WM_GETMINMAXINFO(LPARAM p_lp,const ui_element_min_max_info & p_myinfo) {
|
||||
MINMAXINFO * info = reinterpret_cast<MINMAXINFO *>(p_lp);
|
||||
/*console::formatter() << "handle_WM_GETMINMAXINFO";
|
||||
console::formatter() << p_myinfo.m_min_width << ", " << p_myinfo.m_min_height;
|
||||
console::formatter() << info->ptMinTrackSize << ", " << info->ptMaxTrackSize;*/
|
||||
pfc::max_acc(info->ptMinTrackSize.x,(LONG)p_myinfo.m_min_width);
|
||||
pfc::max_acc(info->ptMinTrackSize.y,(LONG)p_myinfo.m_min_height);
|
||||
if ((LONG) p_myinfo.m_max_width >= 0) pfc::min_acc(info->ptMaxTrackSize.x, (LONG) p_myinfo.m_max_width);
|
||||
if ((LONG) p_myinfo.m_max_height >= 0) pfc::min_acc(info->ptMaxTrackSize.y, (LONG) p_myinfo.m_max_height);
|
||||
//console::formatter() << info->ptMinTrackSize << ", " << info->ptMaxTrackSize;
|
||||
}
|
||||
|
||||
bool ui_element_helpers::ui_element_edit_tools::host_paste_element(unsigned p_id) {
|
||||
pfc::com_ptr_t<IDataObject> obj;
|
||||
if (SUCCEEDED(OleGetClipboard(obj.receive_ptr()))) {
|
||||
DWORD effect;
|
||||
ui_element_config::ptr cfg;
|
||||
if (static_api_ptr_t<ui_element_common_methods>()->parse_dataobject(obj,cfg,effect)) {
|
||||
host_replace_element(p_id, cfg);
|
||||
IDataObjectUtils::SetDataObjectDWORD(obj, RegisterClipboardFormat(CFSTR_PERFORMEDDROPEFFECT), effect);
|
||||
IDataObjectUtils::PasteSucceeded(obj,effect);
|
||||
if (effect == DROPEFFECT_MOVE) OleSetClipboard(NULL);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool ui_element_helpers::recurse_for_elem_config(ui_element_config::ptr root, ui_element_config::ptr & out, const GUID & toFind) {
|
||||
const GUID rootID = root->get_guid();
|
||||
if (rootID == toFind) {
|
||||
out = root; return true;
|
||||
}
|
||||
ui_element::ptr elem;
|
||||
if (!find(elem, rootID)) return false;
|
||||
ui_element_children_enumerator::ptr children;
|
||||
try {
|
||||
children = elem->enumerate_children(root);
|
||||
} catch(exception_io_data) {return false;}
|
||||
if (children.is_empty()) return false;
|
||||
const t_size childrenTotal = children->get_count();
|
||||
for(t_size walk = 0; walk < childrenTotal; ++walk) {
|
||||
if (recurse_for_elem_config(children->get_item(walk), out, toFind)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ui_element_helpers::ui_element_instance_host_base::grabTopPriorityVisibleChild(ui_element_instance_ptr & out, t_size & outWhich, double & outPriority) {
|
||||
double bestPriority = 0;
|
||||
ui_element_instance_ptr best;
|
||||
t_size bestWhich = ~0;
|
||||
const t_size count = host_get_children_count();
|
||||
for (t_size walk = 0; walk < count; ++walk) if (this->host_is_child_visible(walk) ) {
|
||||
ui_element_instance_ptr item = host_get_child(walk);
|
||||
if (item.is_valid() ) {
|
||||
const double priority = item->get_focus_priority();
|
||||
if (best.is_empty() || childPriorityCompare(walk, priority, bestPriority)) {
|
||||
best = item; bestPriority = priority; bestWhich = walk;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (best.is_empty()) return false;
|
||||
out = best; outPriority = bestPriority; outWhich = bestWhich; return true;
|
||||
}
|
||||
bool ui_element_helpers::ui_element_instance_host_base::grabTopPriorityChild(ui_element_instance_ptr & out, t_size & outWhich, double & outPriority, const GUID & subclass) {
|
||||
double bestPriority = 0;
|
||||
ui_element_instance_ptr best;
|
||||
t_size bestWhich = ~0;
|
||||
const t_size count = host_get_children_count();
|
||||
for (t_size walk = 0; walk < count; ++walk) {
|
||||
ui_element_instance_ptr item = host_get_child(walk);
|
||||
if (item.is_valid()) {
|
||||
double priority;
|
||||
if (item->get_focus_priority_subclass(priority, subclass)) {
|
||||
if (best.is_empty() || childPriorityCompare(walk, priority, bestPriority)) {
|
||||
best = item; bestPriority = priority; bestWhich = walk;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (best.is_empty()) return false;
|
||||
out = best; outPriority = bestPriority; outWhich = bestWhich; return true;
|
||||
}
|
||||
#endif // FOOBAR2000_TARGET_VERSION >= 79
|
||||
374
foobar2000/ATLHelpers/ui_element_helpers.h
Normal file
374
foobar2000/ATLHelpers/ui_element_helpers.h
Normal file
@@ -0,0 +1,374 @@
|
||||
#pragma once
|
||||
|
||||
// ====================================================================================================
|
||||
// ui_element_helpers
|
||||
// A framework for creating UI Elements that host other elements.
|
||||
// All foo_ui_std elements that host other elements - such as splitters or tabs - are based on this.
|
||||
// Note that API 79 (v1.4) or newer is required, earlier did not provide ui_element_common_methods_v3.
|
||||
// ====================================================================================================
|
||||
|
||||
#if FOOBAR2000_TARGET_VERSION >= 79
|
||||
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
|
||||
namespace ui_element_helpers {
|
||||
template<typename t_receiver> class ui_element_instance_callback_multi_impl : public ui_element_instance_callback_v3 {
|
||||
public:
|
||||
ui_element_instance_callback_multi_impl(t_size id, t_receiver * p_receiver) : m_receiver(p_receiver), m_id(id) {}
|
||||
void on_min_max_info_change() {
|
||||
if (m_receiver != NULL) m_receiver->on_min_max_info_change();
|
||||
}
|
||||
bool query_color(const GUID & p_what,t_ui_color & p_out) {
|
||||
if (m_receiver != NULL) return m_receiver->query_color(p_what,p_out);
|
||||
else return false;
|
||||
}
|
||||
|
||||
bool request_activation(service_ptr_t<class ui_element_instance> p_item) {
|
||||
if (m_receiver) return m_receiver->request_activation(m_id);
|
||||
else return false;
|
||||
}
|
||||
|
||||
bool is_edit_mode_enabled() {
|
||||
if (m_receiver) return m_receiver->is_edit_mode_enabled();
|
||||
else return false;
|
||||
}
|
||||
void request_replace(service_ptr_t<class ui_element_instance> p_item) {
|
||||
if (m_receiver) m_receiver->request_replace(m_id);
|
||||
}
|
||||
|
||||
t_ui_font query_font_ex(const GUID & p_what) {
|
||||
if (m_receiver) return m_receiver->query_font_ex(p_what);
|
||||
else return NULL;
|
||||
}
|
||||
|
||||
t_size notify(ui_element_instance * source, const GUID & what, t_size param1, const void * param2, t_size param2size) {
|
||||
if (m_receiver) return m_receiver->host_notify(source, what, param1, param2, param2size);
|
||||
else return 0;
|
||||
}
|
||||
void orphan() {m_receiver = NULL;}
|
||||
|
||||
bool is_elem_visible(service_ptr_t<class ui_element_instance> elem) {
|
||||
if (m_receiver) return m_receiver->is_elem_visible(m_id);
|
||||
else return false;
|
||||
}
|
||||
|
||||
void override_id(t_size id) {m_id = id;}
|
||||
|
||||
void on_alt_pressed(bool) {}
|
||||
private:
|
||||
t_size m_id;
|
||||
t_receiver * m_receiver;
|
||||
};
|
||||
class ui_element_instance_callback_receiver_multi {
|
||||
public:
|
||||
virtual void on_min_max_info_change() {}
|
||||
virtual bool query_color(const GUID & p_what,t_ui_color & p_out) {return false;}
|
||||
virtual bool request_activation(t_size which) {return false;}
|
||||
virtual bool is_edit_mode_enabled() {return false;}
|
||||
virtual void request_replace(t_size which) {}
|
||||
virtual t_ui_font query_font_ex(const GUID&) {return NULL;}
|
||||
virtual bool is_elem_visible(t_size which) {return true;}
|
||||
virtual t_size host_notify(ui_element_instance * source, const GUID & what, t_size param1, const void * param2, t_size param2size) {return 0;}
|
||||
|
||||
void ui_element_instance_callback_handle_remove(bit_array const & mask, t_size const oldCount) {
|
||||
t_callback_list newCallbacks;
|
||||
t_size newWalk = 0;
|
||||
for(t_size walk = 0; walk < oldCount; ++walk) {
|
||||
if (mask[walk]) {
|
||||
t_callback_ptr ptr;
|
||||
if (m_callbacks.query(walk,ptr)) {
|
||||
ptr->orphan(); m_callbacks.remove(walk);
|
||||
}
|
||||
} else {
|
||||
if (newWalk != walk) {
|
||||
t_callback_ptr ptr;
|
||||
if (m_callbacks.query(walk,ptr)) {
|
||||
m_callbacks.remove(walk);
|
||||
ptr->override_id(newWalk);
|
||||
m_callbacks.set(newWalk,ptr);
|
||||
}
|
||||
}
|
||||
++newWalk;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ui_element_instance_callback_handle_reorder(const t_size * order, t_size count) {
|
||||
t_callback_list newCallbacks;
|
||||
for(t_size walk = 0; walk < count; ++walk) {
|
||||
t_callback_ptr ptr;
|
||||
if (m_callbacks.query(order[walk],ptr)) {
|
||||
ptr->override_id(walk);
|
||||
newCallbacks.set(walk,ptr);
|
||||
m_callbacks.remove(order[walk]);
|
||||
}
|
||||
}
|
||||
|
||||
PFC_ASSERT( m_callbacks.get_count() == 0 );
|
||||
ui_element_instance_callback_release_all();
|
||||
|
||||
m_callbacks = newCallbacks;
|
||||
}
|
||||
|
||||
ui_element_instance_callback_ptr ui_element_instance_callback_get_ptr(t_size which) {
|
||||
t_callback_ptr ptr;
|
||||
if (!m_callbacks.query(which,ptr)) {
|
||||
ptr = new service_impl_t<t_callback>(which,this);
|
||||
m_callbacks.set(which,ptr);
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
ui_element_instance_callback_ptr ui_element_instance_callback_create(t_size which) {
|
||||
ui_element_instance_callback_release(which);
|
||||
t_callback_ptr ptr = new service_impl_t<t_callback>(which,this);
|
||||
m_callbacks.set(which,ptr);
|
||||
return ptr;
|
||||
}
|
||||
void ui_element_instance_callback_release_all() {
|
||||
for(t_callback_list::const_iterator walk = m_callbacks.first(); walk.is_valid(); ++walk) {
|
||||
walk->m_value->orphan();
|
||||
}
|
||||
m_callbacks.remove_all();
|
||||
}
|
||||
void ui_element_instance_callback_release(t_size which) {
|
||||
t_callback_ptr ptr;
|
||||
if (m_callbacks.query(which,ptr)) {
|
||||
ptr->orphan();
|
||||
m_callbacks.remove(which);
|
||||
}
|
||||
}
|
||||
protected:
|
||||
~ui_element_instance_callback_receiver_multi() {
|
||||
ui_element_instance_callback_release_all();
|
||||
}
|
||||
ui_element_instance_callback_receiver_multi() {}
|
||||
|
||||
private:
|
||||
typedef ui_element_instance_callback_receiver_multi t_self;
|
||||
typedef ui_element_instance_callback_multi_impl<t_self> t_callback;
|
||||
typedef service_ptr_t<t_callback> t_callback_ptr;
|
||||
typedef pfc::map_t<t_size,t_callback_ptr> t_callback_list;
|
||||
t_callback_list m_callbacks;
|
||||
};
|
||||
|
||||
|
||||
//! Parses container tree to find configuration of specified element inside a layout configuration.
|
||||
bool recurse_for_elem_config(ui_element_config::ptr root, ui_element_config::ptr & out, const GUID & toFind);
|
||||
|
||||
ui_element_instance_ptr create_root_container(HWND p_parent,ui_element_instance_callback_ptr p_callback);
|
||||
ui_element_instance_ptr instantiate(HWND p_parent,ui_element_config::ptr cfg,ui_element_instance_callback_ptr p_callback);
|
||||
ui_element_instance_ptr instantiate_dummy(HWND p_parent,ui_element_config::ptr cfg,ui_element_instance_callback_ptr p_callback);
|
||||
ui_element_instance_ptr update(ui_element_instance_ptr p_element,HWND p_parent,ui_element_config::ptr cfg,ui_element_instance_callback_ptr p_callback);
|
||||
bool find(service_ptr_t<ui_element> & p_out,const GUID & p_guid);
|
||||
ui_element_children_enumerator_ptr enumerate_children(ui_element_config::ptr cfg);
|
||||
|
||||
void replace_with_new_element(ui_element_instance_ptr & p_item,const GUID & p_guid,HWND p_parent,ui_element_instance_callback_ptr p_callback);
|
||||
|
||||
class ui_element_highlight_scope {
|
||||
public:
|
||||
ui_element_highlight_scope(HWND wndElem) {
|
||||
m_highlight = ui_element_common_methods_v3::get()->highlight_element( wndElem );
|
||||
}
|
||||
~ui_element_highlight_scope() {
|
||||
DestroyWindow(m_highlight);
|
||||
}
|
||||
private:
|
||||
ui_element_highlight_scope(const ui_element_highlight_scope&) = delete;
|
||||
void operator=(const ui_element_highlight_scope&) = delete;
|
||||
HWND m_highlight;
|
||||
};
|
||||
|
||||
//! Helper class; provides edit-mode context menu functionality and interacts with "Replace UI Element" dialog. \n
|
||||
//! Do not use directly - derive from ui_element_instance_host_base instead.
|
||||
class ui_element_edit_tools {
|
||||
public:
|
||||
//! Override me
|
||||
virtual void host_replace_element(unsigned p_id, ui_element_config::ptr cfg) {}
|
||||
//! Override me
|
||||
virtual void host_replace_element(unsigned p_id,const GUID & p_newguid) {}
|
||||
|
||||
//! Override me optionally if you customize edit mode context menu
|
||||
virtual bool host_edit_mode_context_menu_test(unsigned p_childid,const POINT & p_point,bool p_fromkeyboard) {return false;}
|
||||
//! Override me optionally if you customize edit mode context menu
|
||||
virtual void host_edit_mode_context_menu_build(unsigned p_childid,const POINT & p_point,bool p_fromkeyboard,HMENU p_menu,unsigned & p_id_base) {}
|
||||
//! Override me optionally if you customize edit mode context menu
|
||||
virtual void host_edit_mode_context_menu_command(unsigned p_childid,const POINT & p_point,bool p_fromkeyboard,unsigned p_id,unsigned p_id_base) {}
|
||||
//! Override me optionally if you customize edit mode context menu
|
||||
virtual bool host_edit_mode_context_menu_get_description(unsigned p_childid,unsigned p_id,unsigned p_id_base,pfc::string_base & p_out) {return false;}
|
||||
|
||||
//! Initiates "Replace UI Element" dialog for one of your sub-elements.
|
||||
void replace_dialog(HWND p_parent,unsigned p_id,const GUID & p_current);
|
||||
|
||||
//! Shows edit mode context menu for your element.
|
||||
void standard_edit_context_menu(LPARAM p_point,ui_element_instance_ptr p_item,unsigned p_id,HWND p_parent);
|
||||
|
||||
static const char * description_from_menu_command(unsigned p_id);
|
||||
|
||||
bool host_paste_element(unsigned p_id);
|
||||
|
||||
BEGIN_MSG_MAP(ui_element_edit_tools)
|
||||
MESSAGE_HANDLER(WM_DESTROY,OnDestroy)
|
||||
END_MSG_MAP()
|
||||
protected:
|
||||
ui_element_edit_tools() {}
|
||||
private:
|
||||
void on_elem_replace(unsigned p_id,GUID const & newElem);
|
||||
void _release_replace_dialog();
|
||||
LRESULT OnDestroy(UINT,WPARAM,LPARAM,BOOL& bHandled) {bHandled = FALSE; *m_killSwitch = true; _release_replace_dialog(); return 0;}
|
||||
|
||||
CWindow m_replace_dialog;
|
||||
std::shared_ptr<bool> m_killSwitch = std::make_shared<bool>();
|
||||
|
||||
ui_element_edit_tools( const ui_element_edit_tools & ) = delete;
|
||||
void operator=( const ui_element_edit_tools & ) = delete;
|
||||
};
|
||||
|
||||
//! Base class for ui_element_instances that host other elements.
|
||||
class ui_element_instance_host_base : public ui_element_instance, protected ui_element_instance_callback_receiver_multi, protected ui_element_edit_tools {
|
||||
protected:
|
||||
// Any derived class must pass their messages to us, by CHAIN_MSG_MAP(ui_element_instance_host_base)
|
||||
BEGIN_MSG_MAP(ui_element_instance_host_base)
|
||||
CHAIN_MSG_MAP(ui_element_edit_tools)
|
||||
MESSAGE_HANDLER(WM_SETTINGCHANGE,OnSettingChange);
|
||||
END_MSG_MAP()
|
||||
|
||||
//override me
|
||||
virtual ui_element_instance_ptr host_get_child(t_size which) = 0;
|
||||
//override me
|
||||
virtual t_size host_get_children_count() = 0;
|
||||
//override me (tabs)
|
||||
virtual void host_bring_to_front(t_size which) {}
|
||||
//override me
|
||||
virtual void on_min_max_info_change() {m_callback->on_min_max_info_change();}
|
||||
//override me
|
||||
virtual void host_replace_child(t_size which) = 0;
|
||||
|
||||
virtual bool host_is_child_visible(t_size which) {return true;}
|
||||
|
||||
void host_child_visibility_changed(t_size which, bool state) {
|
||||
if (m_callback->is_elem_visible_(this)) {
|
||||
ui_element_instance::ptr item = host_get_child(which);
|
||||
if (item.is_valid()) item->notify(ui_element_notify_visibility_changed,state ? 1 : 0,NULL,0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool is_elem_visible(t_size which) {
|
||||
if (!m_callback->is_elem_visible_(this)) return false;
|
||||
return this->host_is_child_visible(which);
|
||||
}
|
||||
|
||||
GUID get_subclass() {return ui_element_subclass_containers;}
|
||||
|
||||
double get_focus_priority() {
|
||||
ui_element_instance_ptr item; double priority; t_size which;
|
||||
if (!grabTopPriorityVisibleChild(item,which,priority)) return 0;
|
||||
return priority;
|
||||
}
|
||||
void set_default_focus() {
|
||||
ui_element_instance_ptr item; double priority; t_size which;
|
||||
if (!grabTopPriorityVisibleChild(item,which,priority)) {
|
||||
this->set_default_focus_fallback();
|
||||
} else {
|
||||
host_bring_to_front(which);
|
||||
item->set_default_focus();
|
||||
}
|
||||
}
|
||||
|
||||
bool get_focus_priority_subclass(double & p_out,const GUID & p_subclass) {
|
||||
ui_element_instance_ptr item; double priority; t_size which;
|
||||
if (!grabTopPriorityChild(item,which,priority,p_subclass)) return false;
|
||||
p_out = priority;
|
||||
return true;
|
||||
}
|
||||
bool set_default_focus_subclass(const GUID & p_subclass) {
|
||||
ui_element_instance_ptr item; double priority; t_size which;
|
||||
if (!grabTopPriorityChild(item,which,priority,p_subclass)) return false;
|
||||
host_bring_to_front(which);
|
||||
return item->set_default_focus_subclass(p_subclass);
|
||||
}
|
||||
void notify(const GUID & p_what, t_size p_param1, const void * p_param2, t_size p_param2size) {
|
||||
if (p_what == ui_element_notify_visibility_changed) {
|
||||
const t_size total = host_get_children_count();
|
||||
for(t_size walk = 0; walk < total; ++walk) {
|
||||
if (this->host_is_child_visible(walk)) {
|
||||
ui_element_instance_ptr item = host_get_child(walk);
|
||||
if (item.is_valid()) item->notify(p_what,p_param1,p_param2,p_param2size);
|
||||
}
|
||||
}
|
||||
} else if (p_what == ui_element_notify_get_element_labels) {
|
||||
handleGetLabels(p_param1, p_param2, p_param2size);
|
||||
} else {
|
||||
const t_size total = host_get_children_count();
|
||||
for(t_size walk = 0; walk < total; ++walk) {
|
||||
ui_element_instance_ptr item = host_get_child(walk);
|
||||
if (item.is_valid()) item->notify(p_what,p_param1,p_param2,p_param2size);
|
||||
}
|
||||
}
|
||||
}
|
||||
bool query_color(const GUID & p_what,t_ui_color & p_out) {return m_callback->query_color(p_what,p_out);}
|
||||
bool request_activation(t_size which) {
|
||||
if (!m_callback->request_activation(this)) return false;
|
||||
host_bring_to_front(which);
|
||||
return true;
|
||||
}
|
||||
bool is_edit_mode_enabled() {return m_callback->is_edit_mode_enabled();}
|
||||
|
||||
t_ui_font query_font_ex(const GUID& id) {return m_callback->query_font_ex(id);}
|
||||
|
||||
void request_replace(t_size which) {
|
||||
host_replace_child(which);
|
||||
}
|
||||
private:
|
||||
void handleGetLabelsChild(ui_element_instance::ptr child, t_size which, t_size param1, const void * param2, t_size param2size) {
|
||||
if (child->get_subclass() == ui_element_subclass_containers) {
|
||||
child->notify(ui_element_notify_get_element_labels, param1, param2, param2size);
|
||||
} else if (child->get_guid() != pfc::guid_null && child->get_wnd() != NULL && this->host_is_child_visible(which)) {
|
||||
FB2K_DYNAMIC_ASSERT(param2 != NULL);
|
||||
reinterpret_cast<ui_element_notify_get_element_labels_callback*>(const_cast<void*>(param2))->set_visible_element(child);
|
||||
}
|
||||
}
|
||||
void handleGetLabels(t_size param1, const void * param2, t_size param2size) {
|
||||
const t_size childrenTotal = host_get_children_count();
|
||||
for(t_size childWalk = 0; childWalk < childrenTotal; ++childWalk) {
|
||||
ui_element_instance_ptr item = host_get_child(childWalk);
|
||||
if (item.is_valid()) handleGetLabelsChild(item, childWalk, param1, param2, param2size);
|
||||
}
|
||||
}
|
||||
LRESULT OnSettingChange(UINT msg,WPARAM wp,LPARAM lp,BOOL& bHandled) {
|
||||
bHandled = FALSE;
|
||||
const t_size total = host_get_children_count();
|
||||
for(t_size walk = 0; walk < total; ++walk) {
|
||||
ui_element_instance::ptr item = host_get_child(walk);
|
||||
if (item.is_valid()) {
|
||||
::SendMessage(item->get_wnd(),msg,wp,lp);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
t_size whichChild(ui_element_instance_ptr child) {
|
||||
const t_size count = host_get_children_count();
|
||||
for(t_size walk = 0; walk < count; ++walk) {
|
||||
if (child == host_get_child(walk)) return walk;
|
||||
}
|
||||
return ~0;
|
||||
}
|
||||
bool childPriorityCompare(t_size which, double priority, double bestPriority) {
|
||||
if (host_is_child_visible(which)) return priority >= bestPriority;
|
||||
else return priority > bestPriority;
|
||||
}
|
||||
bool grabTopPriorityVisibleChild(ui_element_instance_ptr & out,t_size & outWhich,double & outPriority);
|
||||
bool grabTopPriorityChild(ui_element_instance_ptr & out,t_size & outWhich,double & outPriority,const GUID & subclass);
|
||||
protected:
|
||||
ui_element_instance_host_base(ui_element_instance_callback::ptr callback) : m_callback(callback) {}
|
||||
const ui_element_instance_callback::ptr m_callback;
|
||||
};
|
||||
|
||||
|
||||
bool enforce_min_max_info(CWindow p_window,ui_element_min_max_info const & p_info);
|
||||
|
||||
void handle_WM_GETMINMAXINFO(LPARAM p_lp,const ui_element_min_max_info & p_myinfo);
|
||||
};
|
||||
|
||||
#endif // FOOBAR2000_TARGET_VERSION >= 79
|
||||
Reference in New Issue
Block a user