Vain attempts to make kbd navigation work inside find/replace dialog - it
[wxWidgets.git] / src / msw / fdrepdlg.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/fdrepdlg.cpp
3 // Purpose: wxFindReplaceDialog class
4 // Author: Markus Greither
5 // Modified by: 31.07.01: VZ: integrated into wxWindows
6 // Created: 23/03/2001
7 // RCS-ID:
8 // Copyright: (c) Markus Greither
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #ifdef __GNUG__
21 #pragma implementation "fdrepdlg.h"
22 #endif
23
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
26
27 #ifdef __BORLANDC__
28 #pragma hdrstop
29 #endif
30
31 #if wxUSE_FINDREPLDLG
32
33 #ifndef WX_PRECOMP
34 #include "wx/intl.h"
35 #include "wx/log.h"
36 #endif
37
38 #include "wx/msw/private.h"
39
40 #if !defined(__WIN32__) || defined(__SALFORDC__) || defined(__WXWINE__)
41 #include <commdlg.h>
42 #endif
43
44 #include "wx/fdrepdlg.h"
45
46 // ----------------------------------------------------------------------------
47 // functions prototypes
48 // ----------------------------------------------------------------------------
49
50 LRESULT APIENTRY wxFindReplaceWindowProc(HWND hwnd, WXUINT nMsg,
51 WPARAM wParam, LPARAM lParam);
52
53 UINT CALLBACK wxFindReplaceDialogHookProc(HWND hwnd,
54 UINT uiMsg,
55 WPARAM wParam,
56 LPARAM lParam);
57
58 // ----------------------------------------------------------------------------
59 // wxWin macros
60 // ----------------------------------------------------------------------------
61
62 IMPLEMENT_DYNAMIC_CLASS(wxFindReplaceDialog, wxDialog)
63
64 IMPLEMENT_DYNAMIC_CLASS(wxFindDialogEvent, wxCommandEvent)
65
66 DEFINE_EVENT_TYPE(wxEVT_COMMAND_FIND)
67 DEFINE_EVENT_TYPE(wxEVT_COMMAND_FIND_NEXT)
68 DEFINE_EVENT_TYPE(wxEVT_COMMAND_FIND_REPLACE)
69 DEFINE_EVENT_TYPE(wxEVT_COMMAND_FIND_REPLACE_ALL)
70 DEFINE_EVENT_TYPE(wxEVT_COMMAND_FIND_CLOSE)
71
72 // ----------------------------------------------------------------------------
73 // wxFindReplaceDialogImpl: the internals of wxFindReplaceDialog
74 // ----------------------------------------------------------------------------
75
76 class WXDLLEXPORT wxFindReplaceDialogImpl
77 {
78 public:
79 wxFindReplaceDialogImpl(wxFindReplaceDialog *dialog, int flagsWX);
80 ~wxFindReplaceDialogImpl();
81
82 void InitFindWhat(const wxString& str);
83 void InitReplaceWith(const wxString& str);
84
85 void SubclassDialog(HWND hwnd);
86
87 static UINT GetFindDialogMessage() { return ms_msgFindDialog; }
88
89 // only for passing to ::FindText or ::ReplaceText
90 FINDREPLACE *GetPtrFindReplace() { return &m_findReplace; }
91
92 private:
93 void InitString(const wxString& str, LPTSTR *ppStr, WORD *pLen);
94
95 // the owner of the dialog
96 HWND m_hwndOwner;
97
98 // the previous window proc of our owner
99 WNDPROC m_oldParentWndProc;
100
101 // the find replace data used by the dialog
102 FINDREPLACE m_findReplace;
103
104 // registered Message for Dialog
105 static UINT ms_msgFindDialog;
106 };
107
108 UINT wxFindReplaceDialogImpl::ms_msgFindDialog = 0;
109
110 // ============================================================================
111 // implementation
112 // ============================================================================
113
114 // ----------------------------------------------------------------------------
115 // wxFindReplaceDialogImpl
116 // ----------------------------------------------------------------------------
117
118 wxFindReplaceDialogImpl::wxFindReplaceDialogImpl(wxFindReplaceDialog *dialog,
119 int flagsWX)
120 {
121 // get the identifier for the find dialog message if we don't have it yet
122 if ( !ms_msgFindDialog )
123 {
124 ms_msgFindDialog = ::RegisterWindowMessage(FINDMSGSTRING);
125
126 if ( !ms_msgFindDialog )
127 {
128 wxLogLastError(_T("RegisterWindowMessage(FINDMSGSTRING)"));
129 }
130 }
131
132 m_hwndOwner = NULL;
133 m_oldParentWndProc = NULL;
134
135 wxZeroMemory(m_findReplace);
136
137 // translate the flags: first the dialog creation flags
138
139 // always set this to be able to set the title
140 int flags = FR_ENABLEHOOK;
141
142 int flagsDialog = dialog->GetWindowStyle();
143 if ( flagsDialog & wxFR_NOMATCHCASE)
144 flags |= FR_NOMATCHCASE;
145 if ( flagsDialog & wxFR_NOWHOLEWORD)
146 flags |= FR_NOWHOLEWORD;
147 if ( flagsDialog & wxFR_NOUPDOWN)
148 flags |= FR_NOUPDOWN;
149
150 // and now the flags governing the initial values of the dialogs controls
151 if ( flagsWX & wxFR_DOWN)
152 flags |= FR_DOWN;
153 if ( flagsWX & wxFR_MATCHCASE)
154 flags |= FR_MATCHCASE;
155 if ( flagsWX & wxFR_WHOLEWORD )
156 flags |= FR_WHOLEWORD;
157
158 m_findReplace.lStructSize = sizeof(FINDREPLACE);
159 m_findReplace.hwndOwner = GetHwndOf(dialog->GetParent());
160 m_findReplace.Flags = flags;
161
162 m_findReplace.lCustData = (LPARAM)dialog;
163 m_findReplace.lpfnHook = wxFindReplaceDialogHookProc;
164 }
165
166 void wxFindReplaceDialogImpl::InitString(const wxString& str,
167 LPTSTR *ppStr, WORD *pLen)
168 {
169 size_t len = str.length() + 1;
170 if ( len < 80 )
171 {
172 // MSDN docs say that the buffer must be at least 80 chars
173 len = 80;
174 }
175
176 *ppStr = new wxChar[len];
177 wxStrcpy(*ppStr, str);
178 *pLen = len;
179 }
180
181 void wxFindReplaceDialogImpl::InitFindWhat(const wxString& str)
182 {
183 InitString(str, &m_findReplace.lpstrFindWhat, &m_findReplace.wFindWhatLen);
184 }
185
186 void wxFindReplaceDialogImpl::InitReplaceWith(const wxString& str)
187 {
188 InitString(str,
189 &m_findReplace.lpstrReplaceWith,
190 &m_findReplace.wReplaceWithLen);
191 }
192
193 void wxFindReplaceDialogImpl::SubclassDialog(HWND hwnd)
194 {
195 m_hwndOwner = hwnd;
196
197 // check that we don't subclass the parent twice: this would be a bad idea
198 // as then we'd have infinite recursion in wxFindReplaceWindowProc
199 WNDPROC oldParentWndProc = (WNDPROC)::GetWindowLong(hwnd, GWL_WNDPROC);
200
201 if ( oldParentWndProc != wxFindReplaceWindowProc )
202 {
203 // save old wnd proc elsewhere to access it from
204 // wxFindReplaceWindowProc
205 m_oldParentWndProc = oldParentWndProc;
206 (void)::SetWindowLong(hwnd, GWL_USERDATA, (LONG)oldParentWndProc);
207
208 // and set the new one
209 (void)::SetWindowLong(hwnd, GWL_WNDPROC, (LONG)wxFindReplaceWindowProc);
210 }
211 }
212
213 wxFindReplaceDialogImpl::~wxFindReplaceDialogImpl()
214 {
215 delete [] m_findReplace.lpstrFindWhat;
216 delete [] m_findReplace.lpstrReplaceWith;
217
218 if ( m_hwndOwner )
219 {
220 ::SetWindowLong(m_hwndOwner, GWL_WNDPROC, (LONG)m_oldParentWndProc);
221 }
222 }
223
224 // ----------------------------------------------------------------------------
225 // Window Proc for handling RegisterWindowMessage(FINDMSGSTRING)
226 // ----------------------------------------------------------------------------
227
228 LRESULT APIENTRY wxFindReplaceWindowProc(HWND hwnd, WXUINT nMsg,
229 WPARAM wParam, LPARAM lParam)
230 {
231 if ( nMsg == wxFindReplaceDialogImpl::GetFindDialogMessage() )
232 {
233 FINDREPLACE *pFR = (FINDREPLACE *)lParam;
234 wxFindReplaceDialog *dialog = (wxFindReplaceDialog *)pFR->lCustData;
235
236 // map flags from Windows
237 wxEventType evtType;
238
239 bool replace = FALSE;
240 if ( pFR->Flags & FR_DIALOGTERM )
241 {
242 evtType = wxEVT_COMMAND_FIND_CLOSE;
243 }
244 else if ( pFR->Flags & FR_FINDNEXT )
245 {
246 evtType = wxEVT_COMMAND_FIND_NEXT;
247 }
248 else if ( pFR->Flags & FR_REPLACE )
249 {
250 evtType = wxEVT_COMMAND_FIND_REPLACE;
251
252 replace = TRUE;
253 }
254 else if ( pFR->Flags & FR_REPLACEALL )
255 {
256 evtType = wxEVT_COMMAND_FIND_REPLACE_ALL;
257
258 replace = TRUE;
259 }
260 else
261 {
262 wxFAIL_MSG( _T("unknown find dialog event") );
263
264 return 0;
265 }
266
267 wxUint32 flags = 0;
268 if ( pFR->Flags & FR_DOWN )
269 flags |= wxFR_DOWN;
270 if ( pFR->Flags & FR_WHOLEWORD )
271 flags |= wxFR_WHOLEWORD;
272 if ( pFR->Flags & FR_MATCHCASE )
273 flags |= wxFR_MATCHCASE;
274
275 wxFindDialogEvent event(evtType, dialog->GetId());
276 event.SetEventObject(dialog);
277 event.SetFlags(flags);
278 event.SetFindString(pFR->lpstrFindWhat);
279 if ( replace )
280 {
281 event.SetReplaceString(pFR->lpstrReplaceWith);
282 }
283
284 // TODO: should we copy the strings to dialog->GetData() as well?
285
286 if ( !dialog->GetEventHandler()->ProcessEvent(event) )
287 {
288 // the event is not propagated upwards to the parent automatically
289 // because the dialog is a top level window, so do it manually as
290 // in 9 cases of 10 the message must be processed by the dialog
291 // owner and not the dialog itself
292 (void)dialog->GetParent()->GetEventHandler()->ProcessEvent(event);
293 }
294 }
295
296 WNDPROC wndProc = (WNDPROC)::GetWindowLong(hwnd, GWL_USERDATA);
297
298 // sanity check
299 wxASSERT_MSG( wndProc != wxFindReplaceWindowProc,
300 _T("infinite recursion detected") );
301
302 return ::CallWindowProc(wndProc, hwnd, nMsg, wParam, lParam);
303 }
304
305 // ----------------------------------------------------------------------------
306 // Find/replace dialog hook proc
307 // ----------------------------------------------------------------------------
308
309 UINT CALLBACK wxFindReplaceDialogHookProc(HWND hwnd,
310 UINT uiMsg,
311 WPARAM WXUNUSED(wParam),
312 LPARAM lParam)
313 {
314 if ( uiMsg == WM_INITDIALOG )
315 {
316 FINDREPLACE *pFR = (FINDREPLACE *)lParam;
317 wxFindReplaceDialog *dialog = (wxFindReplaceDialog *)pFR->lCustData;
318
319 ::SetWindowText(hwnd, dialog->GetTitle());
320
321 // don't return FALSE from here or the dialog won't be shown
322 return TRUE;
323 }
324
325 return 0;
326 }
327
328 // ----------------------------------------------------------------------------
329 // wxFindReplaceData
330 // ----------------------------------------------------------------------------
331
332 void wxFindReplaceData::Init()
333 {
334 m_Flags = 0;
335 }
336
337 // ============================================================================
338 // wxFindReplaceDialog implementation
339 // ============================================================================
340
341 // ----------------------------------------------------------------------------
342 // wxFindReplaceDialog ctors/dtor
343 // ----------------------------------------------------------------------------
344
345 void wxFindReplaceDialog::Init()
346 {
347 m_impl = NULL;
348 m_FindReplaceData = NULL;
349
350 // as we're created in the hidden state, bring the internal flag in sync
351 m_isShown = FALSE;
352 }
353
354 wxFindReplaceDialog::wxFindReplaceDialog(wxWindow *parent,
355 wxFindReplaceData *data,
356 const wxString &title,
357 int flags)
358 : m_FindReplaceData(data)
359 {
360 Init();
361
362 (void)Create(parent, data, title, flags);
363 }
364
365 wxFindReplaceDialog::~wxFindReplaceDialog()
366 {
367 // unsubclass the parent
368 delete m_impl;
369
370 // prevent the base class dtor from trying to hide us!
371 m_isShown = FALSE;
372
373 // and from destroying our window
374 m_hWnd = NULL;
375 }
376
377 bool wxFindReplaceDialog::Create(wxWindow *parent,
378 wxFindReplaceData *data,
379 const wxString &title,
380 int flags)
381 {
382 m_windowStyle = flags;
383 m_FindReplaceData = data;
384 m_parent = parent;
385
386 SetTitle(title);
387
388 // we must have a parent as it will get the messages from us
389 return parent != NULL;
390 }
391
392 // ----------------------------------------------------------------------------
393 // wxFindReplaceDialog data access
394 // ----------------------------------------------------------------------------
395
396 void wxFindReplaceDialog::SetData(wxFindReplaceData *data)
397 {
398 delete m_FindReplaceData;
399 m_FindReplaceData = data;
400 }
401
402 // ----------------------------------------------------------------------------
403 // wxFindReplaceData show/hide
404 // ----------------------------------------------------------------------------
405
406 bool wxFindReplaceDialog::Show(bool show)
407 {
408 if ( !wxWindowBase::Show(show) )
409 {
410 // visibility status didn't change
411 return FALSE;
412 }
413
414 // do we already have the dialog window?
415 if ( m_hWnd )
416 {
417 // yes, just use it
418 (void)::ShowWindow(GetHwnd(), show ? SW_SHOW : SW_HIDE);
419
420 return TRUE;
421 }
422
423 if ( !show )
424 {
425 // well, it doesn't exist which is as good as being hidden
426 return TRUE;
427 }
428
429 wxCHECK_MSG( m_FindReplaceData, FALSE, _T("call Create() first!") );
430
431 wxASSERT_MSG( !m_impl, _T("why don't we have the window then?") );
432
433 m_impl = new wxFindReplaceDialogImpl(this, m_FindReplaceData->GetFlags());
434
435 m_impl->InitFindWhat(m_FindReplaceData->GetFindString());
436
437 bool replace = HasFlag(wxFR_REPLACEDIALOG);
438 if ( replace )
439 {
440 m_impl->InitReplaceWith(m_FindReplaceData->GetReplaceString());
441 }
442
443 // call the right function to show the dialog which does what we want
444 FINDREPLACE *pFR = m_impl->GetPtrFindReplace();
445 HWND hwnd;
446 if ( replace )
447 hwnd = ::ReplaceText(pFR);
448 else
449 hwnd = ::FindText(pFR);
450
451 if ( !hwnd )
452 {
453 wxLogError(_("Failed to create the standard find/replace dialog (error code %d)"),
454 ::CommDlgExtendedError());
455
456 delete m_impl;
457 m_impl = NULL;
458
459 return FALSE;
460 }
461
462 // subclass parent window in order to get FINDMSGSTRING message
463 m_impl->SubclassDialog(GetHwndOf(m_parent));
464
465 if ( !::ShowWindow(hwnd, SW_SHOW) )
466 {
467 wxLogLastError(_T("ShowWindow(find dialog)"));
468 }
469
470 m_hWnd = (WXHWND)hwnd;
471
472 return TRUE;
473 }
474
475 // ----------------------------------------------------------------------------
476 // wxFindReplaceDialog title handling
477 // ----------------------------------------------------------------------------
478
479 // we set the title of this dialog in our jook proc but for now don't crash in
480 // the base class version because of m_hWnd == 0
481
482 void wxFindReplaceDialog::SetTitle( const wxString& title)
483 {
484 m_title = title;
485 }
486
487 wxString wxFindReplaceDialog::GetTitle() const
488 {
489 return m_title;
490 }
491
492 // ----------------------------------------------------------------------------
493 // wxFindReplaceDialog position/size
494 // ----------------------------------------------------------------------------
495
496 void wxFindReplaceDialog::DoSetSize(int WXUNUSED(x), int WXUNUSED(y),
497 int WXUNUSED(width), int WXUNUSED(height),
498 int WXUNUSED(sizeFlags))
499 {
500 // ignore - we can't change the size of this standard dialog
501 return;
502 }
503
504 // NB: of course, both of these functions are completely bogus, but it's better
505 // than nothing
506 void wxFindReplaceDialog::DoGetSize(int *width, int *height) const
507 {
508 // the standard dialog size
509 if ( width )
510 *width = 225;
511 if ( height )
512 *height = 324;
513 }
514
515 void wxFindReplaceDialog::DoGetClientSize(int *width, int *height) const
516 {
517 // the standard dialog size
518 if ( width )
519 *width = 219;
520 if ( height )
521 *height = 299;
522 }
523
524 #endif // wxUSE_FINDREPLDLG
525