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
43LRESULT CALLBACK wxFindReplaceWindowProc(HWND hwnd, WXUINT nMsg,
44                                         WPARAM wParam, LPARAM lParam);
45
46UINT_PTR CALLBACK wxFindReplaceDialogHookProc(HWND hwnd,
47                                              UINT uiMsg,
48                                              WPARAM wParam,
49                                              LPARAM lParam);
50
51// ----------------------------------------------------------------------------
52// wxWin macros
53// ----------------------------------------------------------------------------
54
55IMPLEMENT_DYNAMIC_CLASS(wxFindReplaceDialog, wxDialog)
56
57// ----------------------------------------------------------------------------
58// wxFindReplaceDialogImpl: the internals of wxFindReplaceDialog
59// ----------------------------------------------------------------------------
60
61class WXDLLEXPORT wxFindReplaceDialogImpl
62{
63public:
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
72    static UINT GetFindDialogMessage() { return ms_msgFindDialog; }
73
74    // only for passing to ::FindText or ::ReplaceText
75    FINDREPLACE *GetPtrFindReplace() { return &m_findReplace; }
76
77    // set/query the "closed by user" flag
78    void SetClosedByUser() { m_wasClosedByUser = true; }
79    bool WasClosedByUser() const { return m_wasClosedByUser; }
80
81private:
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
102UINT wxFindReplaceDialogImpl::ms_msgFindDialog = 0;
103
104// ============================================================================
105// implementation
106// ============================================================================
107
108// ----------------------------------------------------------------------------
109// wxFindReplaceDialogImpl
110// ----------------------------------------------------------------------------
111
112wxFindReplaceDialogImpl::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
162void 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
177void wxFindReplaceDialogImpl::InitFindWhat(const wxString& str)
178{
179    InitString(str, &m_findReplace.lpstrFindWhat, &m_findReplace.wFindWhatLen);
180}
181
182void wxFindReplaceDialogImpl::InitReplaceWith(const wxString& str)
183{
184    InitString(str,
185               &m_findReplace.lpstrReplaceWith,
186               &m_findReplace.wReplaceWithLen);
187}
188
189void 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
205wxFindReplaceDialogImpl::~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
221LRESULT 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
334UINT_PTR CALLBACK
335wxFindReplaceDialogHookProc(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
362void 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
371wxFindReplaceDialog::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
382wxFindReplaceDialog::~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
409bool 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
428bool 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
504void wxFindReplaceDialog::SetTitle( const wxString& title)
505{
506    m_title = title;
507}
508
509wxString wxFindReplaceDialog::GetTitle() const
510{
511    return m_title;
512}
513
514// ----------------------------------------------------------------------------
515// wxFindReplaceDialog position/size
516// ----------------------------------------------------------------------------
517
518void 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
528void 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
537void 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