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