@@ -146,3 +146,232 @@ void DoFind(void) {
146146 g_hwndMain , NULL , g_hInst , NULL );
147147 ShowWindow (g_hwndFindDlg , SW_SHOW );
148148}
149+
150+ /*============================================================================
151+ ** Find and Replace (Ctrl+H) - Query view only
152+ **============================================================================*/
153+
154+ static HWND g_hwndReplaceDlg = NULL ;
155+ static HWND g_hwndReplFind = NULL ;
156+ static HWND g_hwndReplWith = NULL ;
157+ static wchar_t g_replaceText [128 ] = L"" ;
158+
159+ #define ID_REPL_FIND 201
160+ #define ID_REPL_REPLACE 202
161+ #define ID_REPL_ALL 203
162+
163+ static void DoReplaceOne (void ) {
164+ DWORD selStart , selEnd ;
165+
166+ if (!g_findText [0 ]) return ;
167+ if (g_viewMode != 0 ) return ; /* Query view only */
168+
169+ SendMessage (g_hwndQuery , EM_GETSEL , (WPARAM )& selStart , (LPARAM )& selEnd );
170+
171+ /* If we have a selection matching find text, replace it */
172+ if (selEnd > selStart ) {
173+ int selLen = selEnd - selStart ;
174+ int findLen = lstrlenW (g_findText );
175+ if (selLen == findLen ) {
176+ int len = GetWindowTextLengthW (g_hwndQuery );
177+ wchar_t * buf = (wchar_t * )LocalAlloc (LMEM_FIXED , (len + 1 ) * sizeof (wchar_t ));
178+ if (buf ) {
179+ int i , match = 1 ;
180+ GetWindowTextW (g_hwndQuery , buf , len + 1 );
181+ /* Case-insensitive compare of selection */
182+ for (i = 0 ; i < selLen && match ; i ++ ) {
183+ wchar_t c1 = buf [selStart + i ], c2 = g_findText [i ];
184+ if (c1 >= 'A' && c1 <= 'Z' ) c1 += 32 ;
185+ if (c2 >= 'A' && c2 <= 'Z' ) c2 += 32 ;
186+ if (c1 != c2 ) match = 0 ;
187+ }
188+ LocalFree (buf );
189+ if (match ) {
190+ SendMessage (g_hwndQuery , EM_REPLACESEL , TRUE, (LPARAM )g_replaceText );
191+ g_queryDirty = 1 ;
192+ }
193+ }
194+ }
195+ }
196+ /* Find next */
197+ DoFindNext ();
198+ }
199+
200+ static int DoReplaceAll (void ) {
201+ int len , findLen , replLen , count = 0 , i , j ;
202+ wchar_t * buf , * newBuf , * p ;
203+
204+ if (!g_findText [0 ]) return 0 ;
205+ if (g_viewMode != 0 ) return 0 ;
206+
207+ findLen = lstrlenW (g_findText );
208+ replLen = lstrlenW (g_replaceText );
209+ len = GetWindowTextLengthW (g_hwndQuery );
210+ if (len == 0 ) return 0 ;
211+
212+ buf = (wchar_t * )LocalAlloc (LMEM_FIXED , (len + 1 ) * sizeof (wchar_t ));
213+ if (!buf ) return 0 ;
214+ GetWindowTextW (g_hwndQuery , buf , len + 1 );
215+
216+ /* Count matches first */
217+ for (i = 0 ; i <= len - findLen ; i ++ ) {
218+ for (j = 0 ; j < findLen ; j ++ ) {
219+ wchar_t c1 = buf [i + j ], c2 = g_findText [j ];
220+ if (c1 >= 'A' && c1 <= 'Z' ) c1 += 32 ;
221+ if (c2 >= 'A' && c2 <= 'Z' ) c2 += 32 ;
222+ if (c1 != c2 ) break ;
223+ }
224+ if (j == findLen ) count ++ ;
225+ }
226+
227+ if (count == 0 ) {
228+ LocalFree (buf );
229+ return 0 ;
230+ }
231+
232+ /* Build new string */
233+ newBuf = (wchar_t * )LocalAlloc (LMEM_FIXED ,
234+ (len + count * (replLen - findLen ) + 1 ) * sizeof (wchar_t ));
235+ if (!newBuf ) {
236+ LocalFree (buf );
237+ return 0 ;
238+ }
239+
240+ p = newBuf ;
241+ for (i = 0 ; i <= len ; i ++ ) {
242+ if (i <= len - findLen ) {
243+ for (j = 0 ; j < findLen ; j ++ ) {
244+ wchar_t c1 = buf [i + j ], c2 = g_findText [j ];
245+ if (c1 >= 'A' && c1 <= 'Z' ) c1 += 32 ;
246+ if (c2 >= 'A' && c2 <= 'Z' ) c2 += 32 ;
247+ if (c1 != c2 ) break ;
248+ }
249+ if (j == findLen ) {
250+ for (j = 0 ; j < replLen ; j ++ ) * p ++ = g_replaceText [j ];
251+ i += findLen - 1 ;
252+ continue ;
253+ }
254+ }
255+ * p ++ = buf [i ];
256+ }
257+
258+ SetWindowTextW (g_hwndQuery , newBuf );
259+ g_queryDirty = 1 ;
260+
261+ LocalFree (newBuf );
262+ LocalFree (buf );
263+ return count ;
264+ }
265+
266+ static WNDPROC g_pfnReplFindProc , g_pfnReplWithProc ;
267+
268+ static LRESULT CALLBACK ReplEditProc (HWND hwnd , UINT msg , WPARAM wParam , LPARAM lParam ) {
269+ WNDPROC origProc = (hwnd == g_hwndReplFind ) ? g_pfnReplFindProc : g_pfnReplWithProc ;
270+ if (msg == WM_KEYDOWN ) {
271+ if (wParam == VK_TAB ) {
272+ SetFocus ((hwnd == g_hwndReplFind ) ? g_hwndReplWith : g_hwndReplFind );
273+ return 0 ;
274+ }
275+ if (wParam == VK_RETURN ) {
276+ SendMessage (GetParent (hwnd ), WM_COMMAND , ID_REPL_FIND , 0 );
277+ return 0 ;
278+ }
279+ if (wParam == VK_ESCAPE ) {
280+ SendMessage (GetParent (hwnd ), WM_CLOSE , 0 , 0 );
281+ return 0 ;
282+ }
283+ }
284+ if (msg == WM_CHAR && wParam == 27 ) { /* Escape as WM_CHAR */
285+ SendMessage (GetParent (hwnd ), WM_CLOSE , 0 , 0 );
286+ return 0 ;
287+ }
288+ return CallWindowProc (origProc , hwnd , msg , wParam , lParam );
289+ }
290+
291+ static LRESULT CALLBACK ReplaceWndProc (HWND hwnd , UINT msg , WPARAM wParam , LPARAM lParam ) {
292+ switch (msg ) {
293+ case WM_CREATE : {
294+ CreateWindowW (L"STATIC" , L"Find:" ,
295+ WS_CHILD | WS_VISIBLE , 5 , 7 , 45 , 16 , hwnd , NULL , g_hInst , NULL );
296+ g_hwndReplFind = CreateWindowW (L"EDIT" , g_findText ,
297+ WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP ,
298+ 55 , 5 , 175 , 20 , hwnd , (HMENU )101 , g_hInst , NULL );
299+ CreateWindowW (L"STATIC" , L"Replace:" ,
300+ WS_CHILD | WS_VISIBLE , 5 , 32 , 50 , 16 , hwnd , NULL , g_hInst , NULL );
301+ g_hwndReplWith = CreateWindowW (L"EDIT" , g_replaceText ,
302+ WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP ,
303+ 55 , 30 , 175 , 20 , hwnd , (HMENU )102 , g_hInst , NULL );
304+ CreateWindowW (L"BUTTON" , L"Find" ,
305+ WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_DEFPUSHBUTTON ,
306+ 5 , 58 , 55 , 22 , hwnd , (HMENU )ID_REPL_FIND , g_hInst , NULL );
307+ CreateWindowW (L"BUTTON" , L"Replace" ,
308+ WS_CHILD | WS_VISIBLE | WS_TABSTOP ,
309+ 65 , 58 , 60 , 22 , hwnd , (HMENU )ID_REPL_REPLACE , g_hInst , NULL );
310+ CreateWindowW (L"BUTTON" , L"All" ,
311+ WS_CHILD | WS_VISIBLE | WS_TABSTOP ,
312+ 175 , 58 , 55 , 22 , hwnd , (HMENU )ID_REPL_ALL , g_hInst , NULL );
313+ g_pfnReplFindProc = (WNDPROC )SetWindowLong (g_hwndReplFind , GWL_WNDPROC , (LONG )ReplEditProc );
314+ g_pfnReplWithProc = (WNDPROC )SetWindowLong (g_hwndReplWith , GWL_WNDPROC , (LONG )ReplEditProc );
315+ SetFocus (g_hwndReplFind );
316+ SendMessage (g_hwndReplFind , EM_SETSEL , 0 , -1 );
317+ return 0 ;
318+ }
319+ case WM_COMMAND :
320+ GetWindowTextW (g_hwndReplFind , g_findText , 128 );
321+ GetWindowTextW (g_hwndReplWith , g_replaceText , 128 );
322+ if (LOWORD (wParam ) == ID_REPL_FIND ) {
323+ if (g_findText [0 ]) DoFindNext ();
324+ } else if (LOWORD (wParam ) == ID_REPL_REPLACE ) {
325+ if (g_findText [0 ]) DoReplaceOne ();
326+ } else if (LOWORD (wParam ) == ID_REPL_ALL ) {
327+ if (g_findText [0 ]) {
328+ int n = DoReplaceAll ();
329+ wchar_t buf [64 ];
330+ wsprintfW (buf , L"%d replacement%s made." , n , n == 1 ? L"" : L"s" );
331+ MessageBoxW (hwnd , buf , L"Replace All" , MB_OK );
332+ }
333+ }
334+ return 0 ;
335+ case WM_CLOSE :
336+ DestroyWindow (hwnd );
337+ g_hwndReplaceDlg = NULL ;
338+ SetFocus (g_hwndQuery );
339+ return 0 ;
340+ }
341+ return DefWindowProc (hwnd , msg , wParam , lParam );
342+ }
343+
344+ void DoReplace (void ) {
345+ WNDCLASSW wc = {0 };
346+ RECT rc ;
347+
348+ /* Only available in Query view */
349+ if (g_viewMode != 0 ) {
350+ MessageBoxW (g_hwndMain , L"Replace is only available in Query view." , L"Replace" , MB_OK );
351+ return ;
352+ }
353+
354+ if (g_hwndReplaceDlg ) {
355+ SetFocus (g_hwndReplFind );
356+ return ;
357+ }
358+
359+ /* Close find dialog if open */
360+ if (g_hwndFindDlg ) {
361+ DestroyWindow (g_hwndFindDlg );
362+ g_hwndFindDlg = NULL ;
363+ }
364+
365+ wc .lpfnWndProc = ReplaceWndProc ;
366+ wc .hInstance = g_hInst ;
367+ wc .hbrBackground = (HBRUSH )(COLOR_BTNFACE + 1 );
368+ wc .lpszClassName = L"SQLiteCEReplace" ;
369+ RegisterClassW (& wc );
370+
371+ GetWindowRect (g_hwndMain , & rc );
372+ g_hwndReplaceDlg = CreateWindowExW (WS_EX_TOOLWINDOW , L"SQLiteCEReplace" , L"Replace" ,
373+ WS_POPUP | WS_CAPTION | WS_SYSMENU ,
374+ rc .left + 20 , rc .top + 50 , 240 , 108 ,
375+ g_hwndMain , NULL , g_hInst , NULL );
376+ ShowWindow (g_hwndReplaceDlg , SW_SHOW );
377+ }
0 commit comments