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