added support for gcc precompiled headers
[wxWidgets.git] / src / msw / fdrepdlg.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/fdrepdlg.cpp
3 // Purpose: wxFindReplaceDialog class
4 // Author: Markus Greither and Vadim Zeitlin
5 // Modified by:
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 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
21 #pragma implementation "mswfdrepdlg.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(__WXWINCE__)
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 // ----------------------------------------------------------------------------
65 // wxFindReplaceDialogImpl: the internals of wxFindReplaceDialog
66 // ----------------------------------------------------------------------------
67
68 class WXDLLEXPORT wxFindReplaceDialogImpl
69 {
70 public:
71 wxFindReplaceDialogImpl(wxFindReplaceDialog *dialog, int flagsWX);
72 ~wxFindReplaceDialogImpl();
73
74 void InitFindWhat(const wxString& str);
75 void InitReplaceWith(const wxString& str);
76
77 void SubclassDialog(HWND hwnd);
78
79 static UINT GetFindDialogMessage() { return ms_msgFindDialog; }
80
81 // only for passing to ::FindText or ::ReplaceText
82 FINDREPLACE *GetPtrFindReplace() { return &m_findReplace; }
83
84 // set/query the "closed by user" flag
85 void SetClosedByUser() { m_wasClosedByUser = TRUE; }
86 bool WasClosedByUser() const { return m_wasClosedByUser; }
87
88 private:
89 void InitString(const wxString& str, LPTSTR *ppStr, WORD *pLen);
90
91 // the owner of the dialog
92 HWND m_hwndOwner;
93
94 // the previous window proc of our owner
95 WNDPROC m_oldParentWndProc;
96
97 // the find replace data used by the dialog
98 FINDREPLACE m_findReplace;
99
100 // TRUE if the user closed us, FALSE otherwise
101 bool m_wasClosedByUser;
102
103 // registered Message for Dialog
104 static UINT ms_msgFindDialog;
105
106 DECLARE_NO_COPY_CLASS(wxFindReplaceDialogImpl)
107 };
108
109 UINT wxFindReplaceDialogImpl::ms_msgFindDialog = 0;
110
111 // ============================================================================
112 // implementation
113 // ============================================================================
114
115 // ----------------------------------------------------------------------------
116 // wxFindReplaceDialogImpl
117 // ----------------------------------------------------------------------------
118
119 wxFindReplaceDialogImpl::wxFindReplaceDialogImpl(wxFindReplaceDialog *dialog,
120 int flagsWX)
121 {
122 // get the identifier for the find dialog message if we don't have it yet
123 if ( !ms_msgFindDialog )
124 {
125 ms_msgFindDialog = ::RegisterWindowMessage(FINDMSGSTRING);
126
127 if ( !ms_msgFindDialog )
128 {
129 wxLogLastError(_T("RegisterWindowMessage(FINDMSGSTRING)"));
130 }
131 }
132
133 m_hwndOwner = NULL;
134 m_oldParentWndProc = NULL;
135
136 m_wasClosedByUser = FALSE;
137
138 wxZeroMemory(m_findReplace);
139
140 // translate the flags: first the dialog creation flags
141
142 // always set this to be able to set the title
143 int flags = FR_ENABLEHOOK;
144
145 int flagsDialog = dialog->GetWindowStyle();
146 if ( flagsDialog & wxFR_NOMATCHCASE)
147 flags |= FR_NOMATCHCASE;
148 if ( flagsDialog & wxFR_NOWHOLEWORD)
149 flags |= FR_NOWHOLEWORD;
150 if ( flagsDialog & wxFR_NOUPDOWN)
151 flags |= FR_NOUPDOWN;
152
153 // and now the flags governing the initial values of the dialogs controls
154 if ( flagsWX & wxFR_DOWN)
155 flags |= FR_DOWN;
156 if ( flagsWX & wxFR_MATCHCASE)
157 flags |= FR_MATCHCASE;
158 if ( flagsWX & wxFR_WHOLEWORD )
159 flags |= FR_WHOLEWORD;
160
161 m_findReplace.lStructSize = sizeof(FINDREPLACE);
162 m_findReplace.hwndOwner = GetHwndOf(dialog->GetParent());
163 m_findReplace.Flags = flags;
164
165 m_findReplace.lCustData = (LPARAM)dialog;
166 m_findReplace.lpfnHook = wxFindReplaceDialogHookProc;
167 }
168
169 void wxFindReplaceDialogImpl::InitString(const wxString& str,
170 LPTSTR *ppStr, WORD *pLen)
171 {
172 size_t len = str.length() + 1;
173 if ( len < 80 )
174 {
175 // MSDN docs say that the buffer must be at least 80 chars
176 len = 80;
177 }
178
179 *ppStr = new wxChar[len];
180 wxStrcpy(*ppStr, str);
181 *pLen = len;
182 }
183
184 void wxFindReplaceDialogImpl::InitFindWhat(const wxString& str)
185 {
186 InitString(str, &m_findReplace.lpstrFindWhat, &m_findReplace.wFindWhatLen);
187 }
188
189 void wxFindReplaceDialogImpl::InitReplaceWith(const wxString& str)
190 {
191 InitString(str,
192 &m_findReplace.lpstrReplaceWith,
193 &m_findReplace.wReplaceWithLen);
194 }
195
196 void wxFindReplaceDialogImpl::SubclassDialog(HWND hwnd)
197 {
198 m_hwndOwner = hwnd;
199
200 // check that we don't subclass the parent twice: this would be a bad idea
201 // as then we'd have infinite recursion in wxFindReplaceWindowProc
202 if ( !wxCheckWindowWndProc((WXHWND)hwnd, (WXFARPROC)wxFindReplaceWindowProc) )
203 {
204 WNDPROC oldParentWndProc = (WNDPROC)::GetWindowLong(hwnd, GWL_WNDPROC);
205 // save old wnd proc elsewhere to access it from
206 // wxFindReplaceWindowProc
207 m_oldParentWndProc = oldParentWndProc;
208 (void)::SetWindowLong(hwnd, GWL_USERDATA, (LONG)oldParentWndProc);
209
210 // and set the new one
211 (void)::SetWindowLong(hwnd, GWL_WNDPROC, (LONG)wxFindReplaceWindowProc);
212 }
213 }
214
215 wxFindReplaceDialogImpl::~wxFindReplaceDialogImpl()
216 {
217 delete [] m_findReplace.lpstrFindWhat;
218 delete [] m_findReplace.lpstrReplaceWith;
219
220 if ( m_hwndOwner )
221 {
222 ::SetWindowLong(m_hwndOwner, GWL_WNDPROC, (LONG)m_oldParentWndProc);
223 }
224 }
225
226 // ----------------------------------------------------------------------------
227 // Window Proc for handling RegisterWindowMessage(FINDMSGSTRING)
228 // ----------------------------------------------------------------------------
229
230 LRESULT APIENTRY wxFindReplaceWindowProc(HWND hwnd, WXUINT nMsg,
231 WPARAM wParam, LPARAM lParam)
232 {
233 #if wxUSE_UNICODE_MSLU
234 static unsigned long s_lastMsgFlags = 0;
235
236 // This flag helps us to identify the bogus ANSI message
237 // sent by UNICOWS.DLL (see below)
238 // while we're sending our message to the dialog
239 // we ignore possible messages sent in between
240 static bool s_blockMsg = false;
241 #endif // wxUSE_UNICODE_MSLU
242
243 if ( nMsg == wxFindReplaceDialogImpl::GetFindDialogMessage() )
244 {
245 FINDREPLACE *pFR = (FINDREPLACE *)lParam;
246
247 #if wxUSE_UNICODE_MSLU
248 // This is a hack for a MSLU problem: Versions up to 1.0.4011
249 // of UNICOWS.DLL send the correct UNICODE item after button press
250 // and a bogus ANSI mode item right after this, so lets ignore
251 // the second bogus message
252 if ( s_lastMsgFlags == pFR->Flags )
253 {
254 s_lastMsgFlags = 0;
255 return 0;
256 }
257 s_lastMsgFlags = pFR->Flags;
258 #endif // wxUSE_UNICODE_MSLU
259
260 wxFindReplaceDialog *dialog = (wxFindReplaceDialog *)pFR->lCustData;
261
262 // map flags from Windows
263 wxEventType evtType;
264
265 bool replace = FALSE;
266 if ( pFR->Flags & FR_DIALOGTERM )
267 {
268 // we have to notify the dialog that it's being closed by user and
269 // not deleted programmatically as it behaves differently in these
270 // 2 cases
271 dialog->GetImpl()->SetClosedByUser();
272
273 evtType = wxEVT_COMMAND_FIND_CLOSE;
274 }
275 else if ( pFR->Flags & FR_FINDNEXT )
276 {
277 evtType = wxEVT_COMMAND_FIND_NEXT;
278 }
279 else if ( pFR->Flags & FR_REPLACE )
280 {
281 evtType = wxEVT_COMMAND_FIND_REPLACE;
282
283 replace = TRUE;
284 }
285 else if ( pFR->Flags & FR_REPLACEALL )
286 {
287 evtType = wxEVT_COMMAND_FIND_REPLACE_ALL;
288
289 replace = TRUE;
290 }
291 else
292 {
293 wxFAIL_MSG( _T("unknown find dialog event") );
294
295 return 0;
296 }
297
298 wxUint32 flags = 0;
299 if ( pFR->Flags & FR_DOWN )
300 flags |= wxFR_DOWN;
301 if ( pFR->Flags & FR_WHOLEWORD )
302 flags |= wxFR_WHOLEWORD;
303 if ( pFR->Flags & FR_MATCHCASE )
304 flags |= wxFR_MATCHCASE;
305
306 wxFindDialogEvent event(evtType, dialog->GetId());
307 event.SetEventObject(dialog);
308 event.SetFlags(flags);
309 event.SetFindString(pFR->lpstrFindWhat);
310 if ( replace )
311 {
312 event.SetReplaceString(pFR->lpstrReplaceWith);
313 }
314
315 #if wxUSE_UNICODE_MSLU
316 s_blockMsg = true;
317 #endif // wxUSE_UNICODE_MSLU
318
319 dialog->Send(event);
320
321 #if wxUSE_UNICODE_MSLU
322 s_blockMsg = false;
323 #endif // wxUSE_UNICODE_MSLU
324 }
325 #if wxUSE_UNICODE_MSLU
326 else if ( !s_blockMsg )
327 s_lastMsgFlags = 0;
328 #endif // wxUSE_UNICODE_MSLU
329
330 WNDPROC wndProc = (WNDPROC)::GetWindowLong(hwnd, GWL_USERDATA);
331
332 // sanity check
333 wxASSERT_MSG( wndProc != wxFindReplaceWindowProc,
334 _T("infinite recursion detected") );
335
336 return ::CallWindowProc(wndProc, hwnd, nMsg, wParam, lParam);
337 }
338
339 // ----------------------------------------------------------------------------
340 // Find/replace dialog hook proc
341 // ----------------------------------------------------------------------------
342
343 UINT CALLBACK wxFindReplaceDialogHookProc(HWND hwnd,
344 UINT uiMsg,
345 WPARAM WXUNUSED(wParam),
346 LPARAM lParam)
347 {
348 if ( uiMsg == WM_INITDIALOG )
349 {
350 FINDREPLACE *pFR = (FINDREPLACE *)lParam;
351 wxFindReplaceDialog *dialog = (wxFindReplaceDialog *)pFR->lCustData;
352
353 ::SetWindowText(hwnd, dialog->GetTitle());
354
355 // don't return FALSE from here or the dialog won't be shown
356 return TRUE;
357 }
358
359 return 0;
360 }
361
362 // ============================================================================
363 // wxFindReplaceDialog implementation
364 // ============================================================================
365
366 // ----------------------------------------------------------------------------
367 // wxFindReplaceDialog ctors/dtor
368 // ----------------------------------------------------------------------------
369
370 void wxFindReplaceDialog::Init()
371 {
372 m_impl = NULL;
373 m_FindReplaceData = NULL;
374
375 // as we're created in the hidden state, bring the internal flag in sync
376 m_isShown = FALSE;
377 }
378
379 wxFindReplaceDialog::wxFindReplaceDialog(wxWindow *parent,
380 wxFindReplaceData *data,
381 const wxString &title,
382 int flags)
383 : wxFindReplaceDialogBase(parent, data, title, flags)
384 {
385 Init();
386
387 (void)Create(parent, data, title, flags);
388 }
389
390 wxFindReplaceDialog::~wxFindReplaceDialog()
391 {
392 // the dialog might have been already deleted if the user closed it
393 // manually but in this case we should have got a notification about it and
394 // the flagmust have been set
395 if ( !m_impl->WasClosedByUser() )
396 {
397 // if it wasn't, delete the dialog ourselves
398 if ( !::DestroyWindow(GetHwnd()) )
399 {
400 wxLogLastError(_T("DestroyWindow(find dialog)"));
401 }
402 }
403
404 // unsubclass the parent
405 delete m_impl;
406
407 // prevent the base class dtor from trying to hide us!
408 m_isShown = FALSE;
409
410 // and from destroying our window [again]
411 m_hWnd = (WXHWND)NULL;
412 }
413
414 bool wxFindReplaceDialog::Create(wxWindow *parent,
415 wxFindReplaceData *data,
416 const wxString &title,
417 int flags)
418 {
419 m_windowStyle = flags;
420 m_FindReplaceData = data;
421 m_parent = parent;
422
423 SetTitle(title);
424
425 // we must have a parent as it will get the messages from us
426 return parent != NULL;
427 }
428
429 // ----------------------------------------------------------------------------
430 // wxFindReplaceData show/hide
431 // ----------------------------------------------------------------------------
432
433 bool wxFindReplaceDialog::Show(bool show)
434 {
435 if ( !wxWindowBase::Show(show) )
436 {
437 // visibility status didn't change
438 return FALSE;
439 }
440
441 // do we already have the dialog window?
442 if ( m_hWnd )
443 {
444 // yes, just use it
445 (void)::ShowWindow(GetHwnd(), show ? SW_SHOW : SW_HIDE);
446
447 return TRUE;
448 }
449
450 if ( !show )
451 {
452 // well, it doesn't exist which is as good as being hidden
453 return TRUE;
454 }
455
456 wxCHECK_MSG( m_FindReplaceData, FALSE, _T("call Create() first!") );
457
458 wxASSERT_MSG( !m_impl, _T("why don't we have the window then?") );
459
460 m_impl = new wxFindReplaceDialogImpl(this, m_FindReplaceData->GetFlags());
461
462 m_impl->InitFindWhat(m_FindReplaceData->GetFindString());
463
464 bool replace = HasFlag(wxFR_REPLACEDIALOG);
465 if ( replace )
466 {
467 m_impl->InitReplaceWith(m_FindReplaceData->GetReplaceString());
468 }
469
470 // call the right function to show the dialog which does what we want
471 FINDREPLACE *pFR = m_impl->GetPtrFindReplace();
472 HWND hwnd;
473 if ( replace )
474 hwnd = ::ReplaceText(pFR);
475 else
476 hwnd = ::FindText(pFR);
477
478 if ( !hwnd )
479 {
480 wxLogError(_("Failed to create the standard find/replace dialog (error code %d)"),
481 ::CommDlgExtendedError());
482
483 delete m_impl;
484 m_impl = NULL;
485
486 return FALSE;
487 }
488
489 // subclass parent window in order to get FINDMSGSTRING message
490 m_impl->SubclassDialog(GetHwndOf(m_parent));
491
492 if ( !::ShowWindow(hwnd, SW_SHOW) )
493 {
494 wxLogLastError(_T("ShowWindow(find dialog)"));
495 }
496
497 m_hWnd = (WXHWND)hwnd;
498
499 return TRUE;
500 }
501
502 // ----------------------------------------------------------------------------
503 // wxFindReplaceDialog title handling
504 // ----------------------------------------------------------------------------
505
506 // we set the title of this dialog in our jook proc but for now don't crash in
507 // the base class version because of m_hWnd == 0
508
509 void wxFindReplaceDialog::SetTitle( const wxString& title)
510 {
511 m_title = title;
512 }
513
514 wxString wxFindReplaceDialog::GetTitle() const
515 {
516 return m_title;
517 }
518
519 // ----------------------------------------------------------------------------
520 // wxFindReplaceDialog position/size
521 // ----------------------------------------------------------------------------
522
523 void wxFindReplaceDialog::DoSetSize(int WXUNUSED(x), int WXUNUSED(y),
524 int WXUNUSED(width), int WXUNUSED(height),
525 int WXUNUSED(sizeFlags))
526 {
527 // ignore - we can't change the size of this standard dialog
528 return;
529 }
530
531 // NB: of course, both of these functions are completely bogus, but it's better
532 // than nothing
533 void wxFindReplaceDialog::DoGetSize(int *width, int *height) const
534 {
535 // the standard dialog size
536 if ( width )
537 *width = 225;
538 if ( height )
539 *height = 324;
540 }
541
542 void wxFindReplaceDialog::DoGetClientSize(int *width, int *height) const
543 {
544 // the standard dialog size
545 if ( width )
546 *width = 219;
547 if ( height )
548 *height = 299;
549 }
550
551 #endif // wxUSE_FINDREPLDLG
552