1005 lines
29 KiB
C++
1005 lines
29 KiB
C++
///////////////////////////////////////////////////////////////////////////////
|
|
// Name: src/msw/textentry.cpp
|
|
// Purpose: wxTextEntry implementation for wxMSW
|
|
// Author: Vadim Zeitlin
|
|
// Created: 2007-09-26
|
|
// 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 "wx/msw/private.h"
|
|
|
|
#if wxUSE_UXTHEME
|
|
#include "wx/msw/uxtheme.h"
|
|
#endif
|
|
|
|
#include "wx/msw/wrapwin.h"
|
|
#include <shlwapi.h>
|
|
|
|
#define GetEditHwnd() ((HWND)(GetEditHWND()))
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Classes used by auto-completion implementation.
|
|
// ----------------------------------------------------------------------------
|
|
|
|
#if wxUSE_OLE
|
|
#define HAS_AUTOCOMPLETE
|
|
#endif
|
|
|
|
#ifdef HAS_AUTOCOMPLETE
|
|
|
|
#include "wx/msw/ole/oleutils.h"
|
|
#include <shldisp.h>
|
|
|
|
#if defined(__MINGW32__) || 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
|
|
|
|
// This must be the last header included to only affect the DEFINE_GUID()
|
|
// occurrences below but not any GUIDs declared in the standard files included
|
|
// above.
|
|
#include <initguid.h>
|
|
|
|
// 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;
|
|
};
|
|
|
|
namespace
|
|
{
|
|
|
|
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);
|
|
|
|
#ifndef ACDD_VISIBLE
|
|
#define ACDD_VISIBLE 0x0001
|
|
#endif
|
|
|
|
// 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
|
|
{
|
|
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();
|
|
}
|
|
|
|
m_win->Bind(wxEVT_CHAR_HOOK, &wxTextAutoCompleteData::OnCharHook, this);
|
|
}
|
|
|
|
~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.
|
|
m_win->Bind(wxEVT_AFTER_CHAR,
|
|
&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();
|
|
}
|
|
|
|
void OnCharHook(wxKeyEvent& event)
|
|
{
|
|
// If the autocomplete drop-down list is currently displayed when the
|
|
// user presses Escape, we need to dismiss it manually from here as
|
|
// Escape could be eaten by something else (e.g. EVT_CHAR_HOOK in the
|
|
// dialog that this control is found in) otherwise.
|
|
if ( event.GetKeyCode() == WXK_ESCAPE )
|
|
{
|
|
DWORD dwFlags = 0;
|
|
if ( SUCCEEDED(m_autoCompleteDropDown->GetDropDownStatus(&dwFlags,
|
|
NULL))
|
|
&& dwFlags == ACDD_VISIBLE )
|
|
{
|
|
::SendMessage(GetHwndOf(m_win), WM_KEYDOWN, WXK_ESCAPE, 0);
|
|
|
|
// Do not skip the event in this case, we've already handled it.
|
|
return;
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
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 = ::SHAutoComplete(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);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// input restrictions
|
|
// ----------------------------------------------------------------------------
|
|
|
|
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);
|
|
}
|
|
|
|
void wxTextEntry::ForceUpper()
|
|
{
|
|
ConvertToUpperCase();
|
|
|
|
const HWND hwnd = GetEditHwnd();
|
|
const LONG styleOld = ::GetWindowLong(hwnd, GWL_STYLE);
|
|
::SetWindowLong(hwnd, GWL_STYLE, styleOld | ES_UPPERCASE);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// hints
|
|
// ----------------------------------------------------------------------------
|
|
|
|
#if wxUSE_UXTHEME
|
|
|
|
#ifndef EM_SETCUEBANNER
|
|
#define EM_SETCUEBANNER 0x1501
|
|
#define EM_GETCUEBANNER 0x1502
|
|
#endif
|
|
|
|
bool wxTextEntry::SetHint(const wxString& hint)
|
|
{
|
|
if ( wxGetWinVersion() >= wxWinVersion_Vista && 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)
|
|
{
|
|
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;
|
|
}
|
|
|
|
wxPoint wxTextEntry::DoGetMargins() const
|
|
{
|
|
LRESULT lResult = ::SendMessage(GetEditHwnd(), EM_GETMARGINS,
|
|
0, 0);
|
|
int left = LOWORD(lResult);
|
|
int top = -1;
|
|
return wxPoint(left, top);
|
|
}
|
|
|
|
#endif // wxUSE_TEXTCTRL || wxUSE_COMBOBOX
|