Use the same short names as are used by the event table macros for the event type constants themselves. This makes them much more comfortable to use, e.g. Bind(wxEVT_BUTTON) compared to Bind(wxEVT_COMMAND_BUTTON_CLICKED). The old long names are still kept for backwards compatibility and shouldn't be removed as it doesn't really cost anything to continue providing them, but all new event types should only use the short versions. Closes #10661. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@73850 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
992 lines
28 KiB
C++
992 lines
28 KiB
C++
///////////////////////////////////////////////////////////////////////////////
|
|
// Name: src/msw/textentry.cpp
|
|
// Purpose: wxTextEntry implementation for wxMSW
|
|
// Author: Vadim Zeitlin
|
|
// Created: 2007-09-26
|
|
// RCS-ID: $Id$
|
|
// Copyright: (c) 2007 Vadim Zeitlin <vadim@wxwindows.org>
|
|
// Licence: wxWindows licence
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// ============================================================================
|
|
// declarations
|
|
// ============================================================================
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// headers
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// for compilers that support precompilation, includes "wx.h".
|
|
#include "wx/wxprec.h"
|
|
|
|
#ifdef __BORLANDC__
|
|
#pragma hdrstop
|
|
#endif
|
|
|
|
#ifndef WX_PRECOMP
|
|
#include "wx/arrstr.h"
|
|
#include "wx/string.h"
|
|
#endif // WX_PRECOMP
|
|
|
|
#if wxUSE_TEXTCTRL || wxUSE_COMBOBOX
|
|
|
|
#include "wx/textentry.h"
|
|
#include "wx/textcompleter.h"
|
|
#include "wx/dynlib.h"
|
|
|
|
#include <initguid.h>
|
|
|
|
#include "wx/msw/private.h"
|
|
|
|
#if wxUSE_UXTHEME
|
|
#include "wx/msw/uxtheme.h"
|
|
#endif
|
|
|
|
#define GetEditHwnd() ((HWND)(GetEditHWND()))
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Classes used by auto-completion implementation.
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// standard VC6 SDK (WINVER == 0x0400) does not know about IAutoComplete
|
|
#if wxUSE_OLE && (WINVER >= 0x0500)
|
|
#define HAS_AUTOCOMPLETE
|
|
#endif
|
|
|
|
#ifdef HAS_AUTOCOMPLETE
|
|
|
|
#include "wx/msw/ole/oleutils.h"
|
|
#include <shldisp.h>
|
|
|
|
#if defined(__MINGW32__) || defined (__WATCOMC__) || defined(__CYGWIN__)
|
|
// needed for IID_IAutoComplete, IID_IAutoComplete2 and ACO_AUTOSUGGEST
|
|
#include <shlguid.h>
|
|
|
|
#ifndef ACO_AUTOAPPEND
|
|
#define ACO_AUTOAPPEND 0x02
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef ACO_UPDOWNKEYDROPSLIST
|
|
#define ACO_UPDOWNKEYDROPSLIST 0x20
|
|
#endif
|
|
|
|
#ifndef SHACF_FILESYS_ONLY
|
|
#define SHACF_FILESYS_ONLY 0x00000010
|
|
#endif
|
|
|
|
#ifndef SHACF_FILESYS_DIRS
|
|
#define SHACF_FILESYS_DIRS 0x00000020
|
|
#endif
|
|
|
|
namespace
|
|
{
|
|
|
|
// Normally this interface and its IID are defined in shobjidl.h header file
|
|
// included in the platform SDK but MinGW and Cygwin don't have it so redefine
|
|
// the interface ourselves and, as long as we do it all, do it for all
|
|
// compilers to ensure we have the same behaviour for all of them and to avoid
|
|
// the need to check for concrete compilers and maybe even their versions.
|
|
class IAutoCompleteDropDown : public IUnknown
|
|
{
|
|
public:
|
|
virtual HRESULT wxSTDCALL GetDropDownStatus(DWORD *, LPWSTR *) = 0;
|
|
virtual HRESULT wxSTDCALL ResetEnumerator() = 0;
|
|
};
|
|
|
|
DEFINE_GUID(wxIID_IAutoCompleteDropDown,
|
|
0x3cd141f4, 0x3c6a, 0x11d2, 0xbc, 0xaa, 0x00, 0xc0, 0x4f, 0xd9, 0x29, 0xdb);
|
|
|
|
DEFINE_GUID(wxCLSID_AutoComplete,
|
|
0x00bb2763, 0x6a77, 0x11d0, 0xa5, 0x35, 0x00, 0xc0, 0x4f, 0xd7, 0xd0, 0x62);
|
|
|
|
// Small helper class which can be used to ensure thread safety even when
|
|
// wxUSE_THREADS==0 (and hence wxCriticalSection does nothing).
|
|
class CSLock
|
|
{
|
|
public:
|
|
CSLock(CRITICAL_SECTION& cs) : m_cs(&cs)
|
|
{
|
|
::EnterCriticalSection(m_cs);
|
|
}
|
|
|
|
~CSLock()
|
|
{
|
|
::LeaveCriticalSection(m_cs);
|
|
}
|
|
|
|
private:
|
|
CRITICAL_SECTION * const m_cs;
|
|
|
|
wxDECLARE_NO_COPY_CLASS(CSLock);
|
|
};
|
|
|
|
} // anonymity namespace
|
|
|
|
// Implementation of string enumerator used by wxTextAutoCompleteData. This
|
|
// class simply forwards to wxTextCompleter associated with it.
|
|
//
|
|
// Notice that Next() method of this class is called by IAutoComplete
|
|
// background thread and so we must care about thread safety here.
|
|
class wxIEnumString : public IEnumString
|
|
{
|
|
public:
|
|
wxIEnumString()
|
|
{
|
|
Init();
|
|
}
|
|
|
|
void ChangeCompleter(wxTextCompleter *completer)
|
|
{
|
|
// Indicate to Next() that it should bail out as soon as possible.
|
|
{
|
|
CSLock lock(m_csRestart);
|
|
|
|
m_restart = TRUE;
|
|
}
|
|
|
|
// Now try to enter this critical section to ensure that Next() doesn't
|
|
// use the old pointer any more before changing it (this is vital as
|
|
// the old pointer will be destroyed after we return).
|
|
CSLock lock(m_csCompleter);
|
|
|
|
m_completer = completer;
|
|
}
|
|
|
|
void UpdatePrefix(const wxString& prefix)
|
|
{
|
|
CSLock lock(m_csRestart);
|
|
|
|
// We simply store the prefix here and will really update during the
|
|
// next call to our Next() method as we want to call Start() from the
|
|
// worker thread to prevent the main UI thread from blocking while the
|
|
// completions are generated.
|
|
m_prefix = prefix;
|
|
m_restart = TRUE;
|
|
}
|
|
|
|
virtual HRESULT STDMETHODCALLTYPE Next(ULONG celt,
|
|
LPOLESTR *rgelt,
|
|
ULONG *pceltFetched)
|
|
{
|
|
if ( !rgelt || (!pceltFetched && celt > 1) )
|
|
return E_POINTER;
|
|
|
|
ULONG pceltFetchedDummy;
|
|
if ( !pceltFetched )
|
|
pceltFetched = &pceltFetchedDummy;
|
|
|
|
*pceltFetched = 0;
|
|
|
|
CSLock lock(m_csCompleter);
|
|
|
|
if ( !RestartIfNeeded() )
|
|
return S_FALSE;
|
|
|
|
while ( celt-- )
|
|
{
|
|
// Stop iterating if we need to update completions anyhow.
|
|
if ( m_restart )
|
|
return S_FALSE;
|
|
|
|
const wxString s = m_completer->GetNext();
|
|
if ( s.empty() )
|
|
return S_FALSE;
|
|
|
|
const wxWX2WCbuf wcbuf = s.wc_str();
|
|
const size_t size = (wcslen(wcbuf) + 1)*sizeof(wchar_t);
|
|
void *olestr = CoTaskMemAlloc(size);
|
|
if ( !olestr )
|
|
return E_OUTOFMEMORY;
|
|
|
|
memcpy(olestr, wcbuf, size);
|
|
|
|
*rgelt++ = static_cast<LPOLESTR>(olestr);
|
|
|
|
++(*pceltFetched);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
virtual HRESULT STDMETHODCALLTYPE Skip(ULONG celt)
|
|
{
|
|
if ( !celt )
|
|
return E_INVALIDARG;
|
|
|
|
CSLock lock(m_csCompleter);
|
|
|
|
if ( !RestartIfNeeded() )
|
|
return S_FALSE;
|
|
|
|
while ( celt-- )
|
|
{
|
|
if ( m_restart )
|
|
return S_FALSE;
|
|
|
|
if ( m_completer->GetNext().empty() )
|
|
return S_FALSE;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
virtual HRESULT STDMETHODCALLTYPE Reset()
|
|
{
|
|
CSLock lock(m_csRestart);
|
|
|
|
m_restart = TRUE;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
virtual HRESULT STDMETHODCALLTYPE Clone(IEnumString **ppEnum)
|
|
{
|
|
if ( !ppEnum )
|
|
return E_POINTER;
|
|
|
|
CSLock lock(m_csCompleter);
|
|
|
|
wxIEnumString * const e = new wxIEnumString;
|
|
e->AddRef();
|
|
|
|
e->ChangeCompleter(m_completer);
|
|
|
|
*ppEnum = e;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
DECLARE_IUNKNOWN_METHODS;
|
|
|
|
private:
|
|
// dtor doesn't have to be virtual as we're only ever deleted from our own
|
|
// Release() and are not meant to be derived form anyhow, but making it
|
|
// virtual silences gcc warnings; making it private makes it impossible to
|
|
// (mistakenly) delete us directly instead of calling Release()
|
|
virtual ~wxIEnumString()
|
|
{
|
|
::DeleteCriticalSection(&m_csRestart);
|
|
::DeleteCriticalSection(&m_csCompleter);
|
|
}
|
|
|
|
// Common part of all ctors.
|
|
void Init()
|
|
{
|
|
::InitializeCriticalSection(&m_csCompleter);
|
|
::InitializeCriticalSection(&m_csRestart);
|
|
|
|
m_completer = NULL;
|
|
m_restart = FALSE;
|
|
}
|
|
|
|
// Restart completions generation if needed. Should be only called from
|
|
// inside m_csCompleter.
|
|
//
|
|
// If false is returned, it means that there are no completions and that
|
|
// wxTextCompleter::GetNext() shouldn't be called at all.
|
|
bool RestartIfNeeded()
|
|
{
|
|
bool rc = true;
|
|
for ( ;; )
|
|
{
|
|
wxString prefix;
|
|
LONG restart;
|
|
{
|
|
CSLock lock(m_csRestart);
|
|
|
|
prefix = m_prefix;
|
|
restart = m_restart;
|
|
|
|
m_restart = FALSE;
|
|
} // Release m_csRestart before calling Start() to avoid blocking
|
|
// the main thread in UpdatePrefix() during its execution.
|
|
|
|
if ( !restart )
|
|
break;
|
|
|
|
rc = m_completer->Start(prefix);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
// Critical section protecting m_completer itself. It must be entered when
|
|
// using the pointer to ensure that we don't continue using a dangling one
|
|
// after it is destroyed.
|
|
CRITICAL_SECTION m_csCompleter;
|
|
|
|
// The completer we delegate to for the completions generation. It is never
|
|
// NULL after the initial ChangeCompleter() call.
|
|
wxTextCompleter *m_completer;
|
|
|
|
|
|
// Critical section m_prefix and m_restart. It should be only entered for
|
|
// short periods of time, i.e. we shouldn't call any wxTextCompleter
|
|
// methods from inside, to prevent the main thread from blocking on it in
|
|
// UpdatePrefix().
|
|
CRITICAL_SECTION m_csRestart;
|
|
|
|
// If m_restart is true, we need to call wxTextCompleter::Start() with the
|
|
// given prefix to restart generating the completions.
|
|
wxString m_prefix;
|
|
|
|
// Notice that we use LONG and not bool here to ensure that reading this
|
|
// value is atomic (32 bit reads are atomic operations under all Windows
|
|
// versions but reading bool isn't necessarily).
|
|
LONG m_restart;
|
|
|
|
|
|
wxDECLARE_NO_COPY_CLASS(wxIEnumString);
|
|
};
|
|
|
|
BEGIN_IID_TABLE(wxIEnumString)
|
|
ADD_IID(Unknown)
|
|
ADD_IID(EnumString)
|
|
END_IID_TABLE;
|
|
|
|
IMPLEMENT_IUNKNOWN_METHODS(wxIEnumString)
|
|
|
|
|
|
// This class gathers the all auto-complete-related stuff we use. It is
|
|
// allocated on demand by wxTextEntry when AutoComplete() is called.
|
|
class wxTextAutoCompleteData wxBIND_OR_CONNECT_HACK_ONLY_BASE_CLASS
|
|
{
|
|
public:
|
|
// The constructor associates us with the given text entry.
|
|
wxTextAutoCompleteData(wxTextEntry *entry)
|
|
: m_entry(entry),
|
|
m_win(entry->GetEditableWindow())
|
|
{
|
|
m_autoComplete = NULL;
|
|
m_autoCompleteDropDown = NULL;
|
|
m_enumStrings = NULL;
|
|
|
|
m_fixedCompleter = NULL;
|
|
m_customCompleter = NULL;
|
|
|
|
m_connectedCharEvent = false;
|
|
|
|
// Create an object exposing IAutoComplete interface which we'll later
|
|
// use to get IAutoComplete2 as the latter can't be created directly,
|
|
// apparently.
|
|
HRESULT hr = CoCreateInstance
|
|
(
|
|
wxCLSID_AutoComplete,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_IAutoComplete,
|
|
reinterpret_cast<void **>(&m_autoComplete)
|
|
);
|
|
if ( FAILED(hr) )
|
|
{
|
|
wxLogApiError(wxT("CoCreateInstance(CLSID_AutoComplete)"), hr);
|
|
return;
|
|
}
|
|
|
|
// Create a string enumerator and initialize the completer with it.
|
|
m_enumStrings = new wxIEnumString;
|
|
m_enumStrings->AddRef();
|
|
hr = m_autoComplete->Init(m_entry->GetEditHWND(), m_enumStrings,
|
|
NULL, NULL);
|
|
if ( FAILED(hr) )
|
|
{
|
|
wxLogApiError(wxT("IAutoComplete::Init"), hr);
|
|
|
|
m_enumStrings->Release();
|
|
m_enumStrings = NULL;
|
|
|
|
return;
|
|
}
|
|
|
|
// As explained in DoRefresh(), we need to call IAutoCompleteDropDown::
|
|
// ResetEnumerator() if we want to be able to change the completions on
|
|
// the fly. In principle we could live without it, i.e. return true
|
|
// from IsOk() even if this QueryInterface() fails, but it doesn't look
|
|
// like this is ever going to have in practice anyhow as the shell-
|
|
// provided IAutoComplete always implements IAutoCompleteDropDown too.
|
|
hr = m_autoComplete->QueryInterface
|
|
(
|
|
wxIID_IAutoCompleteDropDown,
|
|
reinterpret_cast<void **>(&m_autoCompleteDropDown)
|
|
);
|
|
if ( FAILED(hr) )
|
|
{
|
|
wxLogApiError(wxT("IAutoComplete::QI(IAutoCompleteDropDown)"), hr);
|
|
return;
|
|
}
|
|
|
|
// Finally set the completion options using IAutoComplete2.
|
|
IAutoComplete2 *pAutoComplete2 = NULL;
|
|
hr = m_autoComplete->QueryInterface
|
|
(
|
|
IID_IAutoComplete2,
|
|
reinterpret_cast<void **>(&pAutoComplete2)
|
|
);
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
pAutoComplete2->SetOptions(ACO_AUTOSUGGEST |
|
|
ACO_AUTOAPPEND |
|
|
ACO_UPDOWNKEYDROPSLIST);
|
|
pAutoComplete2->Release();
|
|
}
|
|
}
|
|
|
|
~wxTextAutoCompleteData()
|
|
{
|
|
delete m_customCompleter;
|
|
delete m_fixedCompleter;
|
|
|
|
if ( m_enumStrings )
|
|
m_enumStrings->Release();
|
|
if ( m_autoCompleteDropDown )
|
|
m_autoCompleteDropDown->Release();
|
|
if ( m_autoComplete )
|
|
m_autoComplete->Release();
|
|
}
|
|
|
|
// Must be called after creating this object to verify if initializing it
|
|
// succeeded.
|
|
bool IsOk() const
|
|
{
|
|
return m_autoComplete && m_autoCompleteDropDown && m_enumStrings;
|
|
}
|
|
|
|
void ChangeStrings(const wxArrayString& strings)
|
|
{
|
|
if ( !m_fixedCompleter )
|
|
m_fixedCompleter = new wxTextCompleterFixed;
|
|
|
|
m_fixedCompleter->SetCompletions(strings);
|
|
|
|
m_enumStrings->ChangeCompleter(m_fixedCompleter);
|
|
|
|
DoRefresh();
|
|
}
|
|
|
|
// Takes ownership of the pointer if it is non-NULL.
|
|
bool ChangeCustomCompleter(wxTextCompleter *completer)
|
|
{
|
|
// Ensure that the old completer is not used any more before deleting
|
|
// it.
|
|
m_enumStrings->ChangeCompleter(completer);
|
|
|
|
delete m_customCompleter;
|
|
m_customCompleter = completer;
|
|
|
|
if ( m_customCompleter )
|
|
{
|
|
// We postpone connecting to this event until we really need to do
|
|
// it (however we don't disconnect from it when we don't need it
|
|
// any more because we don't have wxUNBIND_OR_DISCONNECT_HACK...).
|
|
if ( !m_connectedCharEvent )
|
|
{
|
|
m_connectedCharEvent = true;
|
|
|
|
// Use the special wxEVT_AFTER_CHAR and not the usual
|
|
// wxEVT_CHAR here because we need to have the updated value of
|
|
// the text control in this handler in order to provide
|
|
// completions for the correct prefix and unfortunately we
|
|
// don't have any way to let DefWindowProc() run from our
|
|
// wxEVT_CHAR handler (as we must also let the other handlers
|
|
// defined at wx level run first).
|
|
//
|
|
// Notice that we can't use wxEVT_TEXT here
|
|
// neither as, due to our use of ACO_AUTOAPPEND, we get
|
|
// EN_CHANGE notifications from the control every time
|
|
// IAutoComplete auto-appends something to it.
|
|
wxBIND_OR_CONNECT_HACK(m_win, wxEVT_AFTER_CHAR,
|
|
wxKeyEventHandler,
|
|
wxTextAutoCompleteData::OnAfterChar,
|
|
this);
|
|
}
|
|
|
|
UpdateStringsFromCustomCompleter();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void DisableCompletion()
|
|
{
|
|
// We currently simply reset the list of possible strings as this seems
|
|
// to effectively disable auto-completion just fine. We could (and
|
|
// probably should) use IAutoComplete::Enable(FALSE) for this too but
|
|
// then we'd need to call Enable(TRUE) to turn it on back again later.
|
|
ChangeStrings(wxArrayString());
|
|
}
|
|
|
|
private:
|
|
// Must be called after changing the values to be returned by wxIEnumString
|
|
// to really make the changes stick.
|
|
void DoRefresh()
|
|
{
|
|
m_enumStrings->Reset();
|
|
|
|
// This is completely and utterly not documented and in fact the
|
|
// current MSDN seems to try to discourage us from using it by saying
|
|
// that "there is no reason to use this method unless the drop-down
|
|
// list is currently visible" but actually we absolutely must call it
|
|
// to force the auto-completer (and not just its drop-down!) to refresh
|
|
// the list of completions which could have changed now. Without this
|
|
// call the new choices returned by GetCompletions() that hadn't been
|
|
// returned by it before are simply silently ignored.
|
|
m_autoCompleteDropDown->ResetEnumerator();
|
|
}
|
|
|
|
// Update the strings returned by our string enumerator to correspond to
|
|
// the currently valid choices according to the custom completer.
|
|
void UpdateStringsFromCustomCompleter()
|
|
{
|
|
// As we use ACO_AUTOAPPEND, the selected part of the text is usually
|
|
// the one appended by us so don't consider it as part of the
|
|
// user-entered prefix.
|
|
long from, to;
|
|
m_entry->GetSelection(&from, &to);
|
|
|
|
if ( to == from )
|
|
from = m_entry->GetLastPosition(); // Take all if no selection.
|
|
|
|
const wxString prefix = m_entry->GetRange(0, from);
|
|
|
|
m_enumStrings->UpdatePrefix(prefix);
|
|
|
|
DoRefresh();
|
|
}
|
|
|
|
void OnAfterChar(wxKeyEvent& event)
|
|
{
|
|
// Notice that we must not refresh the completions when the user
|
|
// presses Backspace as this would result in adding back the just
|
|
// erased character(s) because of ACO_AUTOAPPEND option we use.
|
|
if ( m_customCompleter && event.GetKeyCode() != WXK_BACK )
|
|
UpdateStringsFromCustomCompleter();
|
|
|
|
event.Skip();
|
|
}
|
|
|
|
|
|
// The text entry we're associated with.
|
|
wxTextEntry * const m_entry;
|
|
|
|
// The window of this text entry.
|
|
wxWindow * const m_win;
|
|
|
|
// The auto-completer object itself.
|
|
IAutoComplete *m_autoComplete;
|
|
|
|
// Its IAutoCompleteDropDown interface needed for ResetEnumerator() call.
|
|
IAutoCompleteDropDown *m_autoCompleteDropDown;
|
|
|
|
// Enumerator for strings currently used for auto-completion.
|
|
wxIEnumString *m_enumStrings;
|
|
|
|
// Fixed string completer or NULL if none.
|
|
wxTextCompleterFixed *m_fixedCompleter;
|
|
|
|
// Custom completer or NULL if none.
|
|
wxTextCompleter *m_customCompleter;
|
|
|
|
// Initially false, set to true after connecting OnTextChanged() handler.
|
|
bool m_connectedCharEvent;
|
|
|
|
|
|
wxDECLARE_NO_COPY_CLASS(wxTextAutoCompleteData);
|
|
};
|
|
|
|
#endif // HAS_AUTOCOMPLETE
|
|
|
|
// ============================================================================
|
|
// wxTextEntry implementation
|
|
// ============================================================================
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// initialization and destruction
|
|
// ----------------------------------------------------------------------------
|
|
|
|
wxTextEntry::wxTextEntry()
|
|
{
|
|
#ifdef HAS_AUTOCOMPLETE
|
|
m_autoCompleteData = NULL;
|
|
#endif // HAS_AUTOCOMPLETE
|
|
}
|
|
|
|
wxTextEntry::~wxTextEntry()
|
|
{
|
|
#ifdef HAS_AUTOCOMPLETE
|
|
delete m_autoCompleteData;
|
|
#endif // HAS_AUTOCOMPLETE
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// operations on text
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void wxTextEntry::WriteText(const wxString& text)
|
|
{
|
|
::SendMessage(GetEditHwnd(), EM_REPLACESEL, 0, wxMSW_CONV_LPARAM(text));
|
|
}
|
|
|
|
wxString wxTextEntry::DoGetValue() const
|
|
{
|
|
return wxGetWindowText(GetEditHWND());
|
|
}
|
|
|
|
void wxTextEntry::Remove(long from, long to)
|
|
{
|
|
DoSetSelection(from, to, SetSel_NoScroll);
|
|
WriteText(wxString());
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// clipboard operations
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void wxTextEntry::Copy()
|
|
{
|
|
::SendMessage(GetEditHwnd(), WM_COPY, 0, 0);
|
|
}
|
|
|
|
void wxTextEntry::Cut()
|
|
{
|
|
::SendMessage(GetEditHwnd(), WM_CUT, 0, 0);
|
|
}
|
|
|
|
void wxTextEntry::Paste()
|
|
{
|
|
::SendMessage(GetEditHwnd(), WM_PASTE, 0, 0);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// undo/redo
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void wxTextEntry::Undo()
|
|
{
|
|
::SendMessage(GetEditHwnd(), EM_UNDO, 0, 0);
|
|
}
|
|
|
|
void wxTextEntry::Redo()
|
|
{
|
|
// same as Undo, since Undo undoes the undo
|
|
Undo();
|
|
return;
|
|
}
|
|
|
|
bool wxTextEntry::CanUndo() const
|
|
{
|
|
return ::SendMessage(GetEditHwnd(), EM_CANUNDO, 0, 0) != 0;
|
|
}
|
|
|
|
bool wxTextEntry::CanRedo() const
|
|
{
|
|
// see comment in Redo()
|
|
return CanUndo();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// insertion point and selection
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void wxTextEntry::SetInsertionPoint(long pos)
|
|
{
|
|
// calling DoSetSelection(-1, -1) would select everything which is not what
|
|
// we want here
|
|
if ( pos == -1 )
|
|
pos = GetLastPosition();
|
|
|
|
// be careful to call DoSetSelection() which is overridden in wxTextCtrl
|
|
// and not just SetSelection() here
|
|
DoSetSelection(pos, pos);
|
|
}
|
|
|
|
long wxTextEntry::GetInsertionPoint() const
|
|
{
|
|
long from;
|
|
GetSelection(&from, NULL);
|
|
return from;
|
|
}
|
|
|
|
long wxTextEntry::GetLastPosition() const
|
|
{
|
|
return ::SendMessage(GetEditHwnd(), EM_LINELENGTH, 0, 0);
|
|
}
|
|
|
|
void wxTextEntry::DoSetSelection(long from, long to, int WXUNUSED(flags))
|
|
{
|
|
// if from and to are both -1, it means (in wxWidgets) that all text should
|
|
// be selected, translate this into Windows convention
|
|
if ( (from == -1) && (to == -1) )
|
|
{
|
|
from = 0;
|
|
}
|
|
|
|
::SendMessage(GetEditHwnd(), EM_SETSEL, from, to);
|
|
}
|
|
|
|
void wxTextEntry::GetSelection(long *from, long *to) const
|
|
{
|
|
DWORD dwStart, dwEnd;
|
|
::SendMessage(GetEditHwnd(), EM_GETSEL, (WPARAM)&dwStart, (LPARAM)&dwEnd);
|
|
|
|
if ( from )
|
|
*from = dwStart;
|
|
if ( to )
|
|
*to = dwEnd;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// auto-completion
|
|
// ----------------------------------------------------------------------------
|
|
|
|
#if wxUSE_OLE
|
|
|
|
#ifdef HAS_AUTOCOMPLETE
|
|
|
|
#if wxUSE_DYNLIB_CLASS
|
|
|
|
bool wxTextEntry::DoAutoCompleteFileNames(int flags)
|
|
{
|
|
typedef HRESULT (WINAPI *SHAutoComplete_t)(HWND, DWORD);
|
|
static SHAutoComplete_t s_pfnSHAutoComplete = (SHAutoComplete_t)-1;
|
|
static wxDynamicLibrary s_dllShlwapi;
|
|
if ( s_pfnSHAutoComplete == (SHAutoComplete_t)-1 )
|
|
{
|
|
if ( !s_dllShlwapi.Load(wxT("shlwapi.dll"), wxDL_VERBATIM | wxDL_QUIET) )
|
|
{
|
|
s_pfnSHAutoComplete = NULL;
|
|
}
|
|
else
|
|
{
|
|
wxDL_INIT_FUNC(s_pfn, SHAutoComplete, s_dllShlwapi);
|
|
}
|
|
}
|
|
|
|
if ( !s_pfnSHAutoComplete )
|
|
return false;
|
|
|
|
DWORD dwFlags = 0;
|
|
if ( flags & wxFILE )
|
|
dwFlags |= SHACF_FILESYS_ONLY;
|
|
else if ( flags & wxDIR )
|
|
dwFlags |= SHACF_FILESYS_DIRS;
|
|
else
|
|
{
|
|
wxFAIL_MSG(wxS("No flags for file name auto completion?"));
|
|
return false;
|
|
}
|
|
|
|
HRESULT hr = (*s_pfnSHAutoComplete)(GetEditHwnd(), dwFlags);
|
|
if ( FAILED(hr) )
|
|
{
|
|
wxLogApiError(wxT("SHAutoComplete()"), hr);
|
|
|
|
return false;
|
|
}
|
|
|
|
// Disable the other kinds of completion now that we use the built-in file
|
|
// names completion.
|
|
if ( m_autoCompleteData )
|
|
m_autoCompleteData->DisableCompletion();
|
|
|
|
return true;
|
|
}
|
|
|
|
#endif // wxUSE_DYNLIB_CLASS
|
|
|
|
wxTextAutoCompleteData *wxTextEntry::GetOrCreateCompleter()
|
|
{
|
|
if ( !m_autoCompleteData )
|
|
{
|
|
wxTextAutoCompleteData * const ac = new wxTextAutoCompleteData(this);
|
|
if ( ac->IsOk() )
|
|
m_autoCompleteData = ac;
|
|
else
|
|
delete ac;
|
|
}
|
|
|
|
return m_autoCompleteData;
|
|
}
|
|
|
|
bool wxTextEntry::DoAutoCompleteStrings(const wxArrayString& choices)
|
|
{
|
|
wxTextAutoCompleteData * const ac = GetOrCreateCompleter();
|
|
if ( !ac )
|
|
return false;
|
|
|
|
ac->ChangeStrings(choices);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool wxTextEntry::DoAutoCompleteCustom(wxTextCompleter *completer)
|
|
{
|
|
// First deal with the case when we just want to disable auto-completion.
|
|
if ( !completer )
|
|
{
|
|
if ( m_autoCompleteData )
|
|
m_autoCompleteData->DisableCompletion();
|
|
//else: Nothing to do, we hadn't used auto-completion even before.
|
|
}
|
|
else // Have a valid completer.
|
|
{
|
|
wxTextAutoCompleteData * const ac = GetOrCreateCompleter();
|
|
if ( !ac )
|
|
{
|
|
// Delete the custom completer for consistency with the case when
|
|
// we succeed to avoid memory leaks in user code.
|
|
delete completer;
|
|
return false;
|
|
}
|
|
|
|
// This gives ownership of the custom completer to m_autoCompleteData.
|
|
if ( !ac->ChangeCustomCompleter(completer) )
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#else // !HAS_AUTOCOMPLETE
|
|
|
|
// We still need to define stubs as we declared these overrides in the header.
|
|
|
|
bool wxTextEntry::DoAutoCompleteFileNames(int flags)
|
|
{
|
|
return wxTextEntryBase::DoAutoCompleteFileNames(flags);
|
|
}
|
|
|
|
bool wxTextEntry::DoAutoCompleteStrings(const wxArrayString& choices)
|
|
{
|
|
return wxTextEntryBase::DoAutoCompleteStrings(choices);
|
|
}
|
|
|
|
bool wxTextEntry::DoAutoCompleteCustom(wxTextCompleter *completer)
|
|
{
|
|
return wxTextEntryBase::DoAutoCompleteCustom(completer);
|
|
}
|
|
|
|
#endif // HAS_AUTOCOMPLETE/!HAS_AUTOCOMPLETE
|
|
|
|
#endif // wxUSE_OLE
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// editable state
|
|
// ----------------------------------------------------------------------------
|
|
|
|
bool wxTextEntry::IsEditable() const
|
|
{
|
|
return !(::GetWindowLong(GetEditHwnd(), GWL_STYLE) & ES_READONLY);
|
|
}
|
|
|
|
void wxTextEntry::SetEditable(bool editable)
|
|
{
|
|
::SendMessage(GetEditHwnd(), EM_SETREADONLY, !editable, 0);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// max length
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void wxTextEntry::SetMaxLength(unsigned long len)
|
|
{
|
|
if ( len >= 0xffff )
|
|
{
|
|
// this will set it to a platform-dependent maximum (much more
|
|
// than 64Kb under NT)
|
|
len = 0;
|
|
}
|
|
|
|
::SendMessage(GetEditHwnd(), EM_LIMITTEXT, len, 0);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// hints
|
|
// ----------------------------------------------------------------------------
|
|
|
|
#if wxUSE_UXTHEME
|
|
|
|
#ifndef EM_SETCUEBANNER
|
|
#define EM_SETCUEBANNER 0x1501
|
|
#define EM_GETCUEBANNER 0x1502
|
|
#endif
|
|
|
|
bool wxTextEntry::SetHint(const wxString& hint)
|
|
{
|
|
if ( wxUxThemeEngine::GetIfActive() )
|
|
{
|
|
// notice that this message always works with Unicode strings
|
|
//
|
|
// we always use TRUE for wParam to show the hint even when the window
|
|
// has focus, otherwise there would be no way to show the hint for the
|
|
// initially focused window
|
|
if ( ::SendMessage(GetEditHwnd(), EM_SETCUEBANNER,
|
|
TRUE, (LPARAM)(const wchar_t *)hint.wc_str()) )
|
|
return true;
|
|
}
|
|
|
|
return wxTextEntryBase::SetHint(hint);
|
|
}
|
|
|
|
wxString wxTextEntry::GetHint() const
|
|
{
|
|
if ( wxUxThemeEngine::GetIfActive() )
|
|
{
|
|
wchar_t buf[256];
|
|
if ( ::SendMessage(GetEditHwnd(), EM_GETCUEBANNER,
|
|
(WPARAM)buf, WXSIZEOF(buf)) )
|
|
return wxString(buf);
|
|
}
|
|
|
|
return wxTextEntryBase::GetHint();
|
|
}
|
|
|
|
|
|
#endif // wxUSE_UXTHEME
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// margins support
|
|
// ----------------------------------------------------------------------------
|
|
|
|
bool wxTextEntry::DoSetMargins(const wxPoint& margins)
|
|
{
|
|
#if !defined(__WXWINCE__)
|
|
bool res = true;
|
|
|
|
if ( margins.x != -1 )
|
|
{
|
|
// Set both horizontal margins to the given value, we don't distinguish
|
|
// between left and right margin at wx API level and it seems to be
|
|
// better to change both of them than only left one.
|
|
::SendMessage(GetEditHwnd(), EM_SETMARGINS,
|
|
EC_LEFTMARGIN | EC_RIGHTMARGIN,
|
|
MAKELONG(margins.x, margins.x));
|
|
}
|
|
|
|
if ( margins.y != -1 )
|
|
{
|
|
res = false;
|
|
}
|
|
|
|
return res;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
wxPoint wxTextEntry::DoGetMargins() const
|
|
{
|
|
#if !defined(__WXWINCE__)
|
|
LRESULT lResult = ::SendMessage(GetEditHwnd(), EM_GETMARGINS,
|
|
0, 0);
|
|
int left = LOWORD(lResult);
|
|
int top = -1;
|
|
return wxPoint(left, top);
|
|
#else
|
|
return wxPoint(-1, -1);
|
|
#endif
|
|
}
|
|
|
|
#endif // wxUSE_TEXTCTRL || wxUSE_COMBOBOX
|