Add support for dynamic auto-completion in wxTextEntry.
Add wxTextCompleter class which allows to return the possible completions dynamically and wxTextCompleter::AutoComplete() overload using it. So far this is only implemented for wxMSW. Also fix calling wxTextEntry::AutoComplete(wxArrayString) multiple times under MSW, this didn't correctly update the list of shown completions before. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@67511 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
parent
6b30ffedb1
commit
ea98f11c2f
@ -3929,6 +3929,7 @@ COND_USE_GUI_1_ALL_GUI_HEADERS = \
|
||||
wx/statbox.h \
|
||||
wx/stattext.h \
|
||||
wx/statusbr.h \
|
||||
wx/textcompleter.h \
|
||||
wx/textctrl.h \
|
||||
wx/textdlg.h \
|
||||
wx/textentry.h \
|
||||
|
@ -905,6 +905,7 @@ IMPORTANT: please read docs/tech/tn0016.txt before modifying this file!
|
||||
wx/statbox.h
|
||||
wx/stattext.h
|
||||
wx/statusbr.h
|
||||
wx/textcompleter.h
|
||||
wx/textctrl.h
|
||||
wx/textdlg.h
|
||||
wx/textentry.h
|
||||
|
@ -6741,6 +6741,10 @@ SOURCE=..\..\include\wx\tbarbase.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\include\wx\textcompleter.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\include\wx\textctrl.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
@ -5643,6 +5643,9 @@
|
||||
RelativePath="..\..\include\wx\tbarbase.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\include\wx\textcompleter.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\include\wx\textctrl.h">
|
||||
</File>
|
||||
<File
|
||||
|
@ -7544,6 +7544,10 @@
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\include\wx\textcompleter.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\include\wx\textctrl.h"
|
||||
>
|
||||
</File>
|
||||
|
@ -7540,6 +7540,10 @@
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\include\wx\textcompleter.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\include\wx\textctrl.h"
|
||||
>
|
||||
</File>
|
||||
|
@ -466,6 +466,7 @@ All (GUI):
|
||||
- Added wxRichMessageDialog (Rickard Westerlund, GSoC 2010 project).
|
||||
- Added wxCommandLinkButton (Rickard Westerlund, GSoC 2010 project).
|
||||
- Added wxUIActionSimulator (Steven Lamerton, GSoC 2010 project).
|
||||
- Added support for dynamic auto-completion in wxTextEntry.
|
||||
- wxAUI: support auto-orientable toolbars (wsu).
|
||||
- wxAUI: add support for icons in pane title bars (triton).
|
||||
- Added wxPanel::SetBackgroundBitmap().
|
||||
|
@ -11,6 +11,8 @@
|
||||
#ifndef _WX_MSW_TEXTENTRY_H_
|
||||
#define _WX_MSW_TEXTENTRY_H_
|
||||
|
||||
class wxTextAutoCompleteData; // private class used only by wxTextEntry itself
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// wxTextEntry: common part of wxComboBox and (single line) wxTextCtrl
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -18,12 +20,8 @@
|
||||
class WXDLLIMPEXP_CORE wxTextEntry : public wxTextEntryBase
|
||||
{
|
||||
public:
|
||||
wxTextEntry()
|
||||
{
|
||||
#if wxUSE_OLE
|
||||
m_enumStrings = NULL;
|
||||
#endif // wxUSE_OLE
|
||||
}
|
||||
wxTextEntry();
|
||||
virtual ~wxTextEntry();
|
||||
|
||||
// implement wxTextEntryBase pure virtual methods
|
||||
virtual void WriteText(const wxString& text);
|
||||
@ -78,6 +76,7 @@ protected:
|
||||
#if wxUSE_OLE
|
||||
virtual bool DoAutoCompleteStrings(const wxArrayString& choices);
|
||||
virtual bool DoAutoCompleteFileNames();
|
||||
virtual bool DoAutoCompleteCustom(wxTextCompleter *completer);
|
||||
#endif // wxUSE_OLE
|
||||
|
||||
private:
|
||||
@ -85,8 +84,16 @@ private:
|
||||
virtual WXHWND GetEditHWND() const = 0;
|
||||
|
||||
#if wxUSE_OLE
|
||||
// enumerator for strings currently used for auto-completion or NULL
|
||||
class wxIEnumString *m_enumStrings;
|
||||
// Get the auto-complete object creating it if necessary. Returns NULL if
|
||||
// creating it failed.
|
||||
wxTextAutoCompleteData *GetOrCreateCompleter();
|
||||
|
||||
// Various auto-completion-related stuff, only used if any of AutoComplete()
|
||||
// methods are called. Use the function above to access it.
|
||||
wxTextAutoCompleteData *m_autoCompleteData;
|
||||
|
||||
// It needs to call our GetEditableWindow() and GetEditHWND() methods.
|
||||
friend class wxTextAutoCompleteData;
|
||||
#endif // wxUSE_OLE
|
||||
};
|
||||
|
||||
|
31
include/wx/textcompleter.h
Normal file
31
include/wx/textcompleter.h
Normal file
@ -0,0 +1,31 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Name: wx/textcompleter.h
|
||||
// Purpose: Declaration of wxTextCompleter class.
|
||||
// Author: Vadim Zeitlin
|
||||
// Created: 2011-04-13
|
||||
// RCS-ID: $Id: wxhead.h,v 1.12 2010-04-22 12:44:51 zeitlin Exp $
|
||||
// Copyright: (c) 2011 Vadim Zeitlin <vadim@wxwidgets.org>
|
||||
// Licence: wxWindows licence
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _WX_TEXTCOMPLETER_H_
|
||||
#define _WX_TEXTCOMPLETER_H_
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// wxTextCompleter: used by wxTextEnter::AutoComplete()
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
class WXDLLIMPEXP_CORE wxTextCompleter
|
||||
{
|
||||
public:
|
||||
wxTextCompleter() { }
|
||||
|
||||
virtual void GetCompletions(const wxString& prefix, wxArrayString& res) = 0;
|
||||
|
||||
virtual ~wxTextCompleter();
|
||||
|
||||
private:
|
||||
wxDECLARE_NO_COPY_CLASS(wxTextCompleter);
|
||||
};
|
||||
|
||||
#endif // _WX_TEXTCOMPLETER_H_
|
@ -16,6 +16,7 @@
|
||||
typedef long wxTextPos;
|
||||
|
||||
class WXDLLIMPEXP_FWD_BASE wxArrayString;
|
||||
class WXDLLIMPEXP_FWD_CORE wxTextCompleter;
|
||||
class WXDLLIMPEXP_FWD_CORE wxTextEntryHintData;
|
||||
class WXDLLIMPEXP_FWD_CORE wxWindow;
|
||||
|
||||
@ -106,7 +107,7 @@ public:
|
||||
|
||||
// these functions allow to auto-complete the text already entered into the
|
||||
// control using either the given fixed list of strings, the paths from the
|
||||
// file system or, in the future, an arbitrary user-defined completer
|
||||
// file system or an arbitrary user-defined completer
|
||||
//
|
||||
// they all return true if completion was enabled or false on error (most
|
||||
// commonly meaning that this functionality is not available under the
|
||||
@ -118,6 +119,12 @@ public:
|
||||
bool AutoCompleteFileNames()
|
||||
{ return DoAutoCompleteFileNames(); }
|
||||
|
||||
// notice that we take ownership of the pointer and will delete it
|
||||
//
|
||||
// if the pointer is NULL auto-completion is disabled
|
||||
bool AutoComplete(wxTextCompleter *completer)
|
||||
{ return DoAutoCompleteCustom(completer); }
|
||||
|
||||
|
||||
// status
|
||||
// ------
|
||||
@ -224,6 +231,7 @@ protected:
|
||||
virtual bool DoAutoCompleteStrings(const wxArrayString& WXUNUSED(choices))
|
||||
{ return false; }
|
||||
virtual bool DoAutoCompleteFileNames() { return false; }
|
||||
virtual bool DoAutoCompleteCustom(wxTextCompleter *completer);
|
||||
|
||||
|
||||
// class which should be used to temporarily disable text change events
|
||||
|
85
interface/wx/textcompleter.h
Normal file
85
interface/wx/textcompleter.h
Normal file
@ -0,0 +1,85 @@
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Name: wx/textcompleter.h
|
||||
// Purpose: interface of wxTextCompleter
|
||||
// Author: Vadim Zeitlin
|
||||
// Created: 2011-04-13
|
||||
// RCS-ID: $Id$
|
||||
// Copyright: (c) 2011 Vadim Zeitlin <vadim@wxwindows.org>
|
||||
// Licence: wxWindows licence
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
@class wxTextCompleter
|
||||
|
||||
Base class for custom text completer objects.
|
||||
|
||||
Custom completer objects used with wxTextEntry::AutoComplete() must derive
|
||||
from this class and implement its pure virtual method returning the
|
||||
completions. You would typically use a custom completer when the total
|
||||
number of completions is too big for performance to be acceptable if all of
|
||||
them need to be returned at once but if they can be generated
|
||||
hierarchically, i.e. only the first component initially, then the second
|
||||
one after the user finished entering the first one and so on.
|
||||
|
||||
Here is a simple example of a custom completer that completes the names of
|
||||
some chess pieces. Of course, as the total list here has only four items it
|
||||
would have been much simpler to just specify the array containing all the
|
||||
completions in this example but the same approach could be used when the
|
||||
total number of completions is much higher provided the number of
|
||||
possibilities for each word is still relatively small:
|
||||
@code
|
||||
class MyTextCompleter : public wxTextCompleter
|
||||
{
|
||||
public:
|
||||
virtual void GetCompletions(const wxString& prefix, wxArrayString& res)
|
||||
{
|
||||
const wxString firstWord = prefix.BeforeFirst(' ');
|
||||
if ( firstWord == "white" )
|
||||
{
|
||||
res.push_back("white pawn");
|
||||
res.push_back("white rook");
|
||||
}
|
||||
else if ( firstWord == "black" )
|
||||
{
|
||||
res.push_back("black king");
|
||||
res.push_back("black queen");
|
||||
}
|
||||
else
|
||||
{
|
||||
res.push_back("white");
|
||||
res.push_back("black");
|
||||
}
|
||||
}
|
||||
};
|
||||
...
|
||||
wxTextCtrl *text = ...;
|
||||
text->AutoComplete(new MyTextCompleter);
|
||||
@endcode
|
||||
|
||||
@library{wxcore}
|
||||
|
||||
@since 2.9.2
|
||||
*/
|
||||
class wxTextCompleter
|
||||
{
|
||||
public:
|
||||
/**
|
||||
Pure virtual method returning all possible completions for the given
|
||||
prefix.
|
||||
|
||||
The custom completer should examine the provided prefix and return all
|
||||
the possible completions for it in the output array @a res.
|
||||
|
||||
Please notice that the returned values should start with the prefix,
|
||||
otherwise they will be simply ignored, making adding them to the array
|
||||
in the first place useless.
|
||||
|
||||
@param prefix
|
||||
The possibly empty prefix that the user had already entered.
|
||||
@param res
|
||||
Initially empty array that should be filled with all possible
|
||||
completions (possibly none if there are no valid possibilities
|
||||
starting with the given prefix).
|
||||
*/
|
||||
virtual void GetCompletions(const wxString& prefix, wxArrayString& res) = 0;
|
||||
};
|
@ -60,6 +60,40 @@ public:
|
||||
*/
|
||||
bool AutoComplete(const wxArrayString& choices);
|
||||
|
||||
/**
|
||||
Enable auto-completion using the provided completer object.
|
||||
|
||||
This method should be used instead of AutoComplete() overload taking
|
||||
the array of possible completions if the total number of strings is too
|
||||
big as it allows to return the completions dynamically, depending on
|
||||
the text already entered by user and so is more efficient.
|
||||
|
||||
The specified @a completer object will be used to retrieve the list of
|
||||
possible completions for the already entered text and will be deleted
|
||||
by wxTextEntry itself when it's not needed any longer.
|
||||
|
||||
Notice that you need to include @c wx/textcompleter.h in order to
|
||||
define your class inheriting from wxTextCompleter.
|
||||
|
||||
Currently this method is only implemented in wxMSW port.
|
||||
|
||||
@since 2.9.2
|
||||
|
||||
@param completer
|
||||
The object to be used for generating completions if non-@NULL. If
|
||||
it is @NULL, auto-completion is disabled. The wxTextEntry object
|
||||
takes ownership of this pointer and will delete it in any case
|
||||
(i.e. even if this method returns @false).
|
||||
|
||||
@return
|
||||
@true if the auto-completion was enabled or @false if the operation
|
||||
failed, typically because auto-completion is not supported by the
|
||||
current platform.
|
||||
|
||||
@see wxTextCompleter
|
||||
*/
|
||||
bool AutoComplete(wxTextCompleter *completer);
|
||||
|
||||
/**
|
||||
Call this function to enable auto-completion of the text typed in a
|
||||
single-line text control using all valid file system paths.
|
||||
|
@ -50,6 +50,7 @@
|
||||
#include "wx/textdlg.h"
|
||||
#include "wx/imaglist.h"
|
||||
#include "wx/wupdlock.h"
|
||||
#include "wx/textcompleter.h"
|
||||
|
||||
#include "wx/persist/toplevel.h"
|
||||
#include "wx/persist/treebook.h"
|
||||
@ -98,6 +99,7 @@ enum
|
||||
TextEntry_DisableAutoComplete = TextEntry_Begin,
|
||||
TextEntry_AutoCompleteFixed,
|
||||
TextEntry_AutoCompleteFilenames,
|
||||
TextEntry_AutoCompleteCustom,
|
||||
|
||||
TextEntry_SetHint,
|
||||
TextEntry_End
|
||||
@ -172,6 +174,7 @@ protected:
|
||||
void OnDisableAutoComplete(wxCommandEvent& event);
|
||||
void OnAutoCompleteFixed(wxCommandEvent& event);
|
||||
void OnAutoCompleteFilenames(wxCommandEvent& event);
|
||||
void OnAutoCompleteCustom(wxCommandEvent& event);
|
||||
|
||||
void OnSetHint(wxCommandEvent& event);
|
||||
|
||||
@ -300,6 +303,7 @@ BEGIN_EVENT_TABLE(WidgetsFrame, wxFrame)
|
||||
EVT_MENU(TextEntry_DisableAutoComplete, WidgetsFrame::OnDisableAutoComplete)
|
||||
EVT_MENU(TextEntry_AutoCompleteFixed, WidgetsFrame::OnAutoCompleteFixed)
|
||||
EVT_MENU(TextEntry_AutoCompleteFilenames, WidgetsFrame::OnAutoCompleteFilenames)
|
||||
EVT_MENU(TextEntry_AutoCompleteCustom, WidgetsFrame::OnAutoCompleteCustom)
|
||||
|
||||
EVT_MENU(TextEntry_SetHint, WidgetsFrame::OnSetHint)
|
||||
|
||||
@ -414,6 +418,8 @@ WidgetsFrame::WidgetsFrame(const wxString& title)
|
||||
wxT("Fixed-&list auto-completion"));
|
||||
menuTextEntry->AppendRadioItem(TextEntry_AutoCompleteFilenames,
|
||||
wxT("&Files names auto-completion"));
|
||||
menuTextEntry->AppendRadioItem(TextEntry_AutoCompleteCustom,
|
||||
wxT("&Custom auto-completion"));
|
||||
menuTextEntry->AppendSeparator();
|
||||
menuTextEntry->Append(TextEntry_SetHint, "Set help &hint");
|
||||
|
||||
@ -981,7 +987,7 @@ void WidgetsFrame::OnAutoCompleteFilenames(wxCommandEvent& WXUNUSED(event))
|
||||
|
||||
if ( entry->AutoCompleteFileNames() )
|
||||
{
|
||||
wxLogMessage("Enable auto completion of file names.");
|
||||
wxLogMessage("Enabled auto completion of file names.");
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -989,6 +995,112 @@ void WidgetsFrame::OnAutoCompleteFilenames(wxCommandEvent& WXUNUSED(event))
|
||||
}
|
||||
}
|
||||
|
||||
void WidgetsFrame::OnAutoCompleteCustom(wxCommandEvent& WXUNUSED(event))
|
||||
{
|
||||
wxTextEntryBase *entry = CurrentPage()->GetTextEntry();
|
||||
wxCHECK_RET( entry, "menu item should be disabled" );
|
||||
|
||||
// This is a simple (and hence rather useless) example of a custom
|
||||
// completer class that completes the first word (only) initially and only
|
||||
// build the list of the possible second words once the first word is
|
||||
// known. This allows to avoid building the full 676000 item list of
|
||||
// possible strings all at once as the we have 1000 possibilities for the
|
||||
// first word (000..999) and 676 (aa..zz) for the second one.
|
||||
class CustomTextCompleter : public wxTextCompleter
|
||||
{
|
||||
public:
|
||||
virtual void GetCompletions(const wxString& prefix, wxArrayString& res)
|
||||
{
|
||||
// This is used for illustrative purposes only and shows how many
|
||||
// completions we return every time when we're called.
|
||||
class LogCompletions
|
||||
{
|
||||
public:
|
||||
LogCompletions(const wxString& prefix, const wxArrayString& res)
|
||||
: m_prefix(prefix),
|
||||
m_res(res)
|
||||
{
|
||||
}
|
||||
|
||||
~LogCompletions()
|
||||
{
|
||||
wxLogMessage("Returning %lu possible completions for "
|
||||
"prefix \"%s\"",
|
||||
m_res.size(), m_prefix);
|
||||
}
|
||||
|
||||
private:
|
||||
const wxString& m_prefix;
|
||||
const wxArrayString& m_res;
|
||||
} logCompletions(prefix, res);
|
||||
|
||||
|
||||
// Normally it doesn't make sense to complete empty control, there
|
||||
// are too many choices and listing them all wouldn't be helpful.
|
||||
if ( prefix.empty() )
|
||||
return;
|
||||
|
||||
// The only valid strings start with 3 digits so check for their
|
||||
// presence proposing to complete the remaining ones.
|
||||
if ( !wxIsdigit(prefix[0]) )
|
||||
return;
|
||||
|
||||
if ( prefix.length() == 1 )
|
||||
{
|
||||
for ( int i = 0; i < 10; i++ )
|
||||
for ( int j = 0; j < 10; j++ )
|
||||
res.push_back(wxString::Format("%s%02d",
|
||||
prefix, 10*i + j));
|
||||
return;
|
||||
}
|
||||
else if ( !wxIsdigit(prefix[1]) )
|
||||
return;
|
||||
|
||||
if ( prefix.length() == 2 )
|
||||
{
|
||||
for ( int i = 0; i < 10; i++ )
|
||||
res.push_back(wxString::Format("%s%d", prefix, i));
|
||||
return;
|
||||
}
|
||||
else if ( !wxIsdigit(prefix[2]) )
|
||||
return;
|
||||
|
||||
// Next we must have a space and two letters.
|
||||
wxString prefix2(prefix);
|
||||
if ( prefix.length() == 3 )
|
||||
prefix2 += ' ';
|
||||
else if ( prefix[3] != ' ' )
|
||||
return;
|
||||
|
||||
if ( prefix2.length() == 4 )
|
||||
{
|
||||
for ( char c = 'a'; c <= 'z'; c++ )
|
||||
for ( char d = 'a'; d <= 'z'; d++ )
|
||||
res.push_back(wxString::Format("%s%c%c", prefix2, c, d));
|
||||
return;
|
||||
}
|
||||
else if ( !wxIslower(prefix[4]) )
|
||||
return;
|
||||
|
||||
if ( prefix.length() == 5 )
|
||||
{
|
||||
for ( char c = 'a'; c <= 'z'; c++ )
|
||||
res.push_back(prefix + c);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if ( entry->AutoComplete(new CustomTextCompleter) )
|
||||
{
|
||||
wxLogMessage("Enabled custom auto completer for \"NNN XX\" items "
|
||||
"(where N is a digit and X is a letter).");
|
||||
}
|
||||
else
|
||||
{
|
||||
wxLogMessage("AutoComplete() failed.");
|
||||
}
|
||||
}
|
||||
|
||||
void WidgetsFrame::OnSetHint(wxCommandEvent& WXUNUSED(event))
|
||||
{
|
||||
wxTextEntryBase *entry = CurrentPage()->GetTextEntry();
|
||||
|
@ -31,6 +31,7 @@
|
||||
#endif //WX_PRECOMP
|
||||
|
||||
#include "wx/textentry.h"
|
||||
#include "wx/textcompleter.h"
|
||||
#include "wx/clipbrd.h"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -368,4 +369,22 @@ bool wxTextEntryBase::SendTextUpdatedEvent(wxWindow *win)
|
||||
return win->HandleWindowEvent(event);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// auto-completion stubs
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
wxTextCompleter::~wxTextCompleter()
|
||||
{
|
||||
}
|
||||
|
||||
bool wxTextEntryBase::DoAutoCompleteCustom(wxTextCompleter *completer)
|
||||
{
|
||||
// We don't do anything here but we still need to delete the completer for
|
||||
// consistency with the ports that do implement this method and take
|
||||
// ownership of the pointer.
|
||||
delete completer;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif // wxUSE_TEXTCTRL || wxUSE_COMBOBOX
|
||||
|
@ -31,6 +31,7 @@
|
||||
#if wxUSE_TEXTCTRL || wxUSE_COMBOBOX
|
||||
|
||||
#include "wx/textentry.h"
|
||||
#include "wx/textcompleter.h"
|
||||
#include "wx/dynlib.h"
|
||||
|
||||
#include "wx/msw/private.h"
|
||||
@ -54,6 +55,7 @@
|
||||
|
||||
#include "wx/msw/ole/oleutils.h"
|
||||
#include <shldisp.h>
|
||||
#include <shobjidl.h>
|
||||
|
||||
#if defined(__MINGW32__) || defined (__WATCOMC__) || defined(__CYGWIN__)
|
||||
// needed for IID_IAutoComplete, IID_IAutoComplete2 and ACO_AUTOSUGGEST
|
||||
@ -74,15 +76,15 @@ DEFINE_GUID(CLSID_AutoComplete,
|
||||
class wxIEnumString : public IEnumString
|
||||
{
|
||||
public:
|
||||
wxIEnumString(const wxArrayString& strings) : m_strings(strings)
|
||||
wxIEnumString()
|
||||
{
|
||||
m_index = 0;
|
||||
}
|
||||
|
||||
void ChangeStrings(const wxArrayString& strings)
|
||||
wxIEnumString(const wxIEnumString& other)
|
||||
: m_strings(other.m_strings),
|
||||
m_index(other.m_index)
|
||||
{
|
||||
m_strings = strings;
|
||||
Reset();
|
||||
}
|
||||
|
||||
DECLARE_IUNKNOWN_METHODS;
|
||||
@ -145,8 +147,7 @@ public:
|
||||
if ( !ppEnum )
|
||||
return E_POINTER;
|
||||
|
||||
wxIEnumString *e = new wxIEnumString(m_strings);
|
||||
e->m_index = m_index;
|
||||
wxIEnumString *e = new wxIEnumString(*this);
|
||||
|
||||
e->AddRef();
|
||||
*ppEnum = e;
|
||||
@ -165,7 +166,9 @@ private:
|
||||
wxArrayString m_strings;
|
||||
unsigned m_index;
|
||||
|
||||
wxDECLARE_NO_COPY_CLASS(wxIEnumString);
|
||||
friend class wxTextAutoCompleteData;
|
||||
|
||||
wxDECLARE_NO_ASSIGN_CLASS(wxIEnumString);
|
||||
};
|
||||
|
||||
BEGIN_IID_TABLE(wxIEnumString)
|
||||
@ -175,12 +178,244 @@ END_IID_TABLE;
|
||||
|
||||
IMPLEMENT_IUNKNOWN_METHODS(wxIEnumString)
|
||||
|
||||
|
||||
// This class gathers the auto-complete-related 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_customCompleter = NULL;
|
||||
|
||||
m_connectedTextChangedEvent = 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
|
||||
(
|
||||
CLSID_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
|
||||
(
|
||||
IID_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_UPDOWNKEYDROPSLIST);
|
||||
pAutoComplete2->Release();
|
||||
}
|
||||
}
|
||||
|
||||
~wxTextAutoCompleteData()
|
||||
{
|
||||
delete m_customCompleter;
|
||||
|
||||
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)
|
||||
{
|
||||
m_enumStrings->m_strings = strings;
|
||||
|
||||
DoRefresh();
|
||||
}
|
||||
|
||||
// Takes ownership of the pointer if it is non-NULL.
|
||||
bool ChangeCustomCompleter(wxTextCompleter *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_connectedTextChangedEvent )
|
||||
{
|
||||
m_connectedTextChangedEvent = true;
|
||||
|
||||
wxBIND_OR_CONNECT_HACK(m_win, wxEVT_COMMAND_TEXT_UPDATED,
|
||||
wxCommandEventHandler,
|
||||
wxTextAutoCompleteData::OnTextChanged,
|
||||
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.
|
||||
|
||||
m_enumStrings->m_strings.clear();
|
||||
m_enumStrings->Reset();
|
||||
|
||||
ChangeCustomCompleter(NULL);
|
||||
}
|
||||
|
||||
private:
|
||||
// Must be called after changing wxIEnumString::m_strings 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()
|
||||
{
|
||||
// For efficiency we access m_strings directly instead of creating
|
||||
// another wxArrayString, normally this should save us an unnecessary
|
||||
// memory allocation on the subsequent calls.
|
||||
m_enumStrings->m_strings.clear();
|
||||
m_customCompleter->GetCompletions(m_entry->GetValue(),
|
||||
m_enumStrings->m_strings);
|
||||
|
||||
DoRefresh();
|
||||
}
|
||||
|
||||
void OnTextChanged(wxCommandEvent& event)
|
||||
{
|
||||
if ( m_customCompleter )
|
||||
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;
|
||||
|
||||
// Custom completer or NULL if none.
|
||||
wxTextCompleter *m_customCompleter;
|
||||
|
||||
// Initially false, set to true after connecting OnTextChanged() handler.
|
||||
bool m_connectedTextChangedEvent;
|
||||
|
||||
|
||||
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
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -333,66 +568,65 @@ bool wxTextEntry::DoAutoCompleteFileNames()
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
// if we had an old enumerator we must reuse it as IAutoComplete doesn't
|
||||
// free it if we call Init() again (see #10968) -- and it's also simpler
|
||||
if ( m_enumStrings )
|
||||
{
|
||||
m_enumStrings->ChangeStrings(choices);
|
||||
return true;
|
||||
}
|
||||
|
||||
// create an object exposing IAutoComplete interface (don't go for
|
||||
// IAutoComplete2 immediately as, presumably, it might be not available on
|
||||
// older systems as otherwise why do we have both -- although in practice I
|
||||
// don't know when can this happen)
|
||||
IAutoComplete *pAutoComplete = NULL;
|
||||
HRESULT hr = CoCreateInstance
|
||||
(
|
||||
CLSID_AutoComplete,
|
||||
NULL,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
IID_IAutoComplete,
|
||||
reinterpret_cast<void **>(&pAutoComplete)
|
||||
);
|
||||
if ( FAILED(hr) )
|
||||
{
|
||||
wxLogApiError(wxT("CoCreateInstance(CLSID_AutoComplete)"), hr);
|
||||
wxTextAutoCompleteData * const ac = GetOrCreateCompleter();
|
||||
if ( !ac )
|
||||
return false;
|
||||
}
|
||||
|
||||
// associate it with our strings
|
||||
m_enumStrings = new wxIEnumString(choices);
|
||||
m_enumStrings->AddRef();
|
||||
hr = pAutoComplete->Init(GetEditHwnd(), m_enumStrings, NULL, NULL);
|
||||
m_enumStrings->Release();
|
||||
if ( FAILED(hr) )
|
||||
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 )
|
||||
{
|
||||
wxLogApiError(wxT("IAutoComplete::Init"), hr);
|
||||
return false;
|
||||
if ( m_autoCompleteData )
|
||||
m_autoCompleteData->DisableCompletion();
|
||||
//else: Nothing to do, we hadn't used auto-completion even before.
|
||||
}
|
||||
|
||||
// if IAutoComplete2 is available, set more user-friendly options
|
||||
IAutoComplete2 *pAutoComplete2 = NULL;
|
||||
hr = pAutoComplete->QueryInterface
|
||||
(
|
||||
IID_IAutoComplete2,
|
||||
reinterpret_cast<void **>(&pAutoComplete2)
|
||||
);
|
||||
if ( SUCCEEDED(hr) )
|
||||
else // Have a valid completer.
|
||||
{
|
||||
pAutoComplete2->SetOptions(ACO_AUTOSUGGEST | ACO_UPDOWNKEYDROPSLIST);
|
||||
pAutoComplete2->Release();
|
||||
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;
|
||||
}
|
||||
|
||||
// the docs are unclear about when can we release it but it seems safe to
|
||||
// do it immediately, presumably the edit control itself keeps a reference
|
||||
// to the auto completer object
|
||||
pAutoComplete->Release();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user