]> git.saurik.com Git - wxWidgets.git/blob - src/msw/textentry.cpp
implement AutoComplete for wxMSW too
[wxWidgets.git] / src / msw / textentry.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/textentry.cpp
3 // Purpose: wxTextEntry implementation for wxMSW
4 // Author: Vadim Zeitlin
5 // Created: 2007-09-26
6 // RCS-ID: $Id$
7 // Copyright: (c) 2007 Vadim Zeitlin <vadim@wxwindows.org>
8 // Licence: wxWindows licence
9 ///////////////////////////////////////////////////////////////////////////////
10
11 // ============================================================================
12 // declarations
13 // ============================================================================
14
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18
19 // for compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
21
22 #ifdef __BORLANDC__
23 #pragma hdrstop
24 #endif
25
26 #ifndef WX_PRECOMP
27 #include "wx/string.h"
28 #endif // WX_PRECOMP
29
30 #if wxUSE_TEXTCTRL || wxUSE_COMBOBOX
31
32 #include "wx/textentry.h"
33 #include "wx/dynlib.h"
34
35 #include "wx/msw/private.h"
36
37 #ifndef SHACF_FILESYS_ONLY
38 #define SHACF_FILESYS_ONLY 0x00000010
39 #endif
40
41 #define GetEditHwnd() ((HWND)(GetEditHWND()))
42
43 // ----------------------------------------------------------------------------
44 // wxIEnumString implements IEnumString interface
45 // ----------------------------------------------------------------------------
46
47 #if wxUSE_OLE
48
49 #include "wx/msw/ole/oleutils.h"
50 #include <shldisp.h>
51
52 DEFINE_GUID(CLSID_AutoComplete,
53 0x00bb2763, 0x6a77, 0x11d0, 0xa5, 0x35, 0x00, 0xc0, 0x4f, 0xd7, 0xd0, 0x62);
54
55 class wxIEnumString : public IEnumString
56 {
57 public:
58 wxIEnumString(const wxArrayString& strings) : m_strings(strings)
59 {
60 m_index = 0;
61 }
62
63 DECLARE_IUNKNOWN_METHODS;
64
65 virtual HRESULT STDMETHODCALLTYPE Next(ULONG celt,
66 LPOLESTR *rgelt,
67 ULONG *pceltFetched)
68 {
69 if ( !rgelt || (!pceltFetched && celt > 1) )
70 return E_POINTER;
71
72 ULONG pceltFetchedDummy;
73 if ( !pceltFetched )
74 pceltFetched = &pceltFetchedDummy;
75
76 *pceltFetched = 0;
77
78 for ( const unsigned count = m_strings.size(); celt--; ++m_index )
79 {
80 if ( m_index == count )
81 return S_FALSE;
82
83 const wxWX2WCbuf wcbuf(m_strings[m_index].wc_str());
84 const size_t size = (wcslen(wcbuf) + 1)*sizeof(wchar_t);
85 void *olestr = CoTaskMemAlloc(size);
86 if ( !olestr )
87 return E_OUTOFMEMORY;
88
89 memcpy(olestr, wcbuf, size);
90
91 *rgelt++ = wx_static_cast(LPOLESTR, olestr);
92
93 ++(*pceltFetched);
94 }
95
96 return S_OK;
97 }
98
99 virtual HRESULT STDMETHODCALLTYPE Skip(ULONG celt)
100 {
101 m_index += celt;
102 if ( m_index > m_strings.size() )
103 {
104 m_index = m_strings.size();
105 return S_FALSE;
106 }
107
108 return S_OK;
109 }
110
111 virtual HRESULT STDMETHODCALLTYPE Reset()
112 {
113 m_index = 0;
114
115 return S_OK;
116 }
117
118 virtual HRESULT STDMETHODCALLTYPE Clone(IEnumString **ppEnum)
119 {
120 if ( !ppEnum )
121 return E_POINTER;
122
123 wxIEnumString *e = new wxIEnumString(m_strings);
124 e->m_index = m_index;
125
126 e->AddRef();
127 *ppEnum = e;
128
129 return S_OK;
130 }
131
132 private:
133 const wxArrayString m_strings;
134 unsigned m_index;
135
136 DECLARE_NO_COPY_CLASS(wxIEnumString)
137 };
138
139 BEGIN_IID_TABLE(wxIEnumString)
140 ADD_IID(Unknown)
141 ADD_IID(EnumString)
142 END_IID_TABLE;
143
144 IMPLEMENT_IUNKNOWN_METHODS(wxIEnumString)
145
146 #endif // wxUSE_OLE
147
148 // ============================================================================
149 // wxTextEntry implementation
150 // ============================================================================
151
152 // ----------------------------------------------------------------------------
153 // operations on text
154 // ----------------------------------------------------------------------------
155
156 void wxTextEntry::WriteText(const wxString& text)
157 {
158 ::SendMessage(GetEditHwnd(), EM_REPLACESEL, 0, (LPARAM)text.wx_str());
159 }
160
161 wxString wxTextEntry::GetValue() const
162 {
163 return wxGetWindowText(GetEditHWND());
164 }
165
166 void wxTextEntry::Remove(long from, long to)
167 {
168 DoSetSelection(from, to, SetSel_NoScroll);
169 WriteText(wxString());
170 }
171
172 // ----------------------------------------------------------------------------
173 // clipboard operations
174 // ----------------------------------------------------------------------------
175
176 void wxTextEntry::Copy()
177 {
178 ::SendMessage(GetEditHwnd(), WM_COPY, 0, 0);
179 }
180
181 void wxTextEntry::Cut()
182 {
183 ::SendMessage(GetEditHwnd(), WM_CUT, 0, 0);
184 }
185
186 void wxTextEntry::Paste()
187 {
188 ::SendMessage(GetEditHwnd(), WM_PASTE, 0, 0);
189 }
190
191 // ----------------------------------------------------------------------------
192 // undo/redo
193 // ----------------------------------------------------------------------------
194
195 void wxTextEntry::Undo()
196 {
197 ::SendMessage(GetEditHwnd(), EM_UNDO, 0, 0);
198 }
199
200 void wxTextEntry::Redo()
201 {
202 // same as Undo, since Undo undoes the undo
203 Undo();
204 return;
205 }
206
207 bool wxTextEntry::CanUndo() const
208 {
209 return ::SendMessage(GetEditHwnd(), EM_CANUNDO, 0, 0) != 0;
210 }
211
212 bool wxTextEntry::CanRedo() const
213 {
214 // see comment in Redo()
215 return CanUndo();
216 }
217
218 // ----------------------------------------------------------------------------
219 // insertion point and selection
220 // ----------------------------------------------------------------------------
221
222 void wxTextEntry::SetInsertionPoint(long pos)
223 {
224 // be careful to call DoSetSelection() which is overridden in wxTextCtrl
225 // and not just SetSelection() here
226 DoSetSelection(pos, pos);
227 }
228
229 long wxTextEntry::GetInsertionPoint() const
230 {
231 long from;
232 GetSelection(&from, NULL);
233 return from;
234 }
235
236 long wxTextEntry::GetLastPosition() const
237 {
238 return ::SendMessage(GetEditHwnd(), EM_LINELENGTH, 0, 0);
239 }
240
241 void wxTextEntry::DoSetSelection(long from, long to, int WXUNUSED(flags))
242 {
243 // if from and to are both -1, it means (in wxWidgets) that all text should
244 // be selected, translate this into Windows convention
245 if ( (from == -1) && (to == -1) )
246 {
247 from = 0;
248 }
249
250 ::SendMessage(GetEditHwnd(), EM_SETSEL, from, to);
251 }
252
253 void wxTextEntry::GetSelection(long *from, long *to) const
254 {
255 DWORD dwStart, dwEnd;
256 ::SendMessage(GetEditHwnd(), EM_GETSEL, (WPARAM)&dwStart, (LPARAM)&dwEnd);
257
258 if ( from )
259 *from = dwStart;
260 if ( to )
261 *to = dwEnd;
262 }
263
264 // ----------------------------------------------------------------------------
265 // auto-completion
266 // ----------------------------------------------------------------------------
267
268 #if wxUSE_OLE
269
270 bool wxTextEntry::AutoCompleteFileNames()
271 {
272 typedef HRESULT (WINAPI *SHAutoComplete_t)(HWND, DWORD);
273 static SHAutoComplete_t s_pfnSHAutoComplete = (SHAutoComplete_t)-1;
274 static wxDynamicLibrary s_dllShlwapi;
275 if ( s_pfnSHAutoComplete == (SHAutoComplete_t)-1 )
276 {
277 wxLogNull noLog;
278
279 if ( !s_dllShlwapi.Load(_T("shlwapi.dll"), wxDL_VERBATIM) )
280 {
281 s_pfnSHAutoComplete = NULL;
282 }
283 else
284 {
285 wxDL_INIT_FUNC(s_pfn, SHAutoComplete, s_dllShlwapi);
286 }
287 }
288
289 if ( !s_pfnSHAutoComplete )
290 return false;
291
292 HRESULT hr = (*s_pfnSHAutoComplete)(GetEditHwnd(), SHACF_FILESYS_ONLY);
293 if ( FAILED(hr) )
294 {
295 wxLogApiError(_T("SHAutoComplete()"), hr);
296
297 return false;
298 }
299
300 return true;
301 }
302
303 bool wxTextEntry::AutoComplete(const wxArrayString& choices)
304 {
305 // create an object exposing IAutoComplete interface (don't go for
306 // IAutoComplete2 immediately as, presumably, it might be not available on
307 // older systems as otherwise why do we have both -- although in practice I
308 // don't know when can this happen)
309 IAutoComplete *pAutoComplete = NULL;
310 HRESULT hr = CoCreateInstance
311 (
312 CLSID_AutoComplete,
313 NULL,
314 CLSCTX_INPROC_SERVER,
315 IID_IAutoComplete,
316 wx_reinterpret_cast(void **, &pAutoComplete)
317 );
318 if ( FAILED(hr) )
319 {
320 wxLogApiError(_T("CoCreateInstance(CLSID_AutoComplete)"), hr);
321 return false;
322 }
323
324 // associate it with our strings
325 wxIEnumString *pEnumString = new wxIEnumString(choices);
326 pEnumString->AddRef();
327 hr = pAutoComplete->Init(GetEditHwnd(), pEnumString, NULL, NULL);
328 pEnumString->Release();
329 if ( FAILED(hr) )
330 {
331 wxLogApiError(_T("IAutoComplete::Init"), hr);
332 return false;
333 }
334
335 // if IAutoComplete2 is available, set more user-friendly options
336 IAutoComplete2 *pAutoComplete2 = NULL;
337 hr = pAutoComplete->QueryInterface
338 (
339 IID_IAutoComplete2,
340 wx_reinterpret_cast(void **, &pAutoComplete2)
341 );
342 if ( SUCCEEDED(hr) )
343 {
344 pAutoComplete2->SetOptions(ACO_AUTOSUGGEST | ACO_UPDOWNKEYDROPSLIST);
345 pAutoComplete2->Release();
346 }
347
348 // the docs are unclear about when can we release it but it seems safe to
349 // do it immediately, presumably the edit control itself keeps a reference
350 // to the auto completer object
351 pAutoComplete->Release();
352
353 return true;
354 }
355
356 #endif // wxUSE_OLE
357
358 // ----------------------------------------------------------------------------
359 // editable state
360 // ----------------------------------------------------------------------------
361
362 bool wxTextEntry::IsEditable() const
363 {
364 return !(::GetWindowLong(GetEditHwnd(), GWL_STYLE) & ES_READONLY);
365 }
366
367 void wxTextEntry::SetEditable(bool editable)
368 {
369 ::SendMessage(GetEditHwnd(), EM_SETREADONLY, !editable, 0);
370 }
371
372 // ----------------------------------------------------------------------------
373 // max length
374 // ----------------------------------------------------------------------------
375
376 void wxTextEntry::SetMaxLength(unsigned long len)
377 {
378 if ( len >= 0xffff )
379 {
380 // this will set it to a platform-dependent maximum (much more
381 // than 64Kb under NT)
382 len = 0;
383 }
384
385 ::SendMessage(GetEditHwnd(), EM_LIMITTEXT, len, 0);
386 }
387
388 #endif // wxUSE_TEXTCTRL || wxUSE_COMBOBOX