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