1 /********************************************************************************
2 *                                                                               *
3 *                      T e x t   R e p l a c e   D i a l o g                    *
4 *                                                                               *
5 *********************************************************************************
6 * Copyright (C) 2000,2020 by Jeroen van der Zijp.   All Rights Reserved.        *
7 *********************************************************************************
8 * This library is free software; you can redistribute it and/or modify          *
9 * it under the terms of the GNU Lesser General Public License as published by   *
10 * the Free Software Foundation; either version 3 of the License, or             *
11 * (at your option) any later version.                                           *
12 *                                                                               *
13 * This library is distributed in the hope that it will be useful,               *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of                *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                 *
16 * GNU Lesser General Public License for more details.                           *
17 *                                                                               *
18 * You should have received a copy of the GNU Lesser General Public License      *
19 * along with this program.  If not, see <http://www.gnu.org/licenses/>          *
20 ********************************************************************************/
21 #include "xincs.h"
22 #include "fxver.h"
23 #include "fxdefs.h"
24 #include "fxmath.h"
25 #include "fxkeys.h"
26 #include "FXArray.h"
27 #include "FXHash.h"
28 #include "FXMutex.h"
29 #include "FXStream.h"
30 #include "FXString.h"
31 #include "FXSize.h"
32 #include "FXPoint.h"
33 #include "FXRectangle.h"
34 #include "FXStringDictionary.h"
35 #include "FXSettings.h"
36 #include "FXRegistry.h"
37 #include "FXAccelTable.h"
38 #include "FXFont.h"
39 #include "FXEvent.h"
40 #include "FXWindow.h"
41 #include "FXApp.h"
42 #include "FXGIFIcon.h"
43 #include "FXWindow.h"
44 #include "FXFrame.h"
45 #include "FXArrowButton.h"
46 #include "FXSeparator.h"
47 #include "FXLabel.h"
48 #include "FXButton.h"
49 #include "FXCheckButton.h"
50 #include "FXRadioButton.h"
51 #include "FXPacker.h"
52 #include "FXScrollBar.h"
53 #include "FXTextField.h"
54 #include "FXVerticalFrame.h"
55 #include "FXHorizontalFrame.h"
56 #include "FXReplaceDialog.h"
57 
58 
59 
60 /*
61   Notes:
62 
63   - Keep history of strings previously searched, and allow up/down arrows
64     to "scroll" back to this history.
65   - Simplified: just keep dialog open until explicitly closed.  This allows
66     you to hammer return repeatedly to search and replace.
67   - We think ^N and ^P is more natural to scroll through history; then we
68     can use the arrows to search up and down in the content.
69 */
70 
71 // Padding for buttons
72 #define HORZ_PAD      12
73 #define VERT_PAD      2
74 
75 using namespace FX;
76 
77 /*******************************************************************************/
78 
79 namespace FX {
80 
81 
82 // Search and replace dialog registry section name
83 const FXchar FXReplaceDialog::sectionName[]="SearchReplace";
84 
85 // Value key names
86 static const FXchar skey[20][3]={"SA","SB","SC","SD","SE","SF","SG","SH","SI","SJ","SK","SL","SM","SN","SO","SP","SQ","SR","SS","ST"};
87 static const FXchar rkey[20][3]={"RA","RB","RC","RD","RE","RF","RG","RH","RI","RJ","RK","RL","RM","RN","RO","RP","RQ","RR","RS","RT"};
88 static const FXchar mkey[20][3]={"MA","MB","MC","MD","ME","MF","MG","MH","MI","MJ","MK","ML","MM","MN","MO","MP","MQ","MR","MS","MT"};
89 
90 
91 // Map
92 FXDEFMAP(FXReplaceDialog) FXReplaceDialogMap[]={
93   FXMAPFUNC(SEL_UPDATE,FXReplaceDialog::ID_SEARCH,FXReplaceDialog::onUpdSearch),
94   FXMAPFUNC(SEL_COMMAND,FXReplaceDialog::ID_SEARCH,FXReplaceDialog::onCmdSearch),
95   FXMAPFUNC(SEL_UPDATE,FXReplaceDialog::ID_REPLACE,FXReplaceDialog::onUpdSearch),
96   FXMAPFUNC(SEL_COMMAND,FXReplaceDialog::ID_REPLACE,FXReplaceDialog::onCmdReplace),
97   FXMAPFUNC(SEL_COMMAND,FXReplaceDialog::ID_SEARCH_TEXT,FXReplaceDialog::onCmdSearch),
98   FXMAPFUNC(SEL_KEYPRESS,FXReplaceDialog::ID_SEARCH_TEXT,FXReplaceDialog::onSearchKey),
99   FXMAPFUNC(SEL_MOUSEWHEEL,FXReplaceDialog::ID_SEARCH_TEXT,FXReplaceDialog::onWheelSearch),
100   FXMAPFUNC(SEL_COMMAND,FXReplaceDialog::ID_REPLACE_TEXT,FXReplaceDialog::onCmdReplace),
101   FXMAPFUNC(SEL_KEYPRESS,FXReplaceDialog::ID_REPLACE_TEXT,FXReplaceDialog::onReplaceKey),
102   FXMAPFUNC(SEL_MOUSEWHEEL,FXReplaceDialog::ID_REPLACE_TEXT,FXReplaceDialog::onWheelReplace),
103   FXMAPFUNC(SEL_UPDATE,FXReplaceDialog::ID_REPLACE_SEL,FXReplaceDialog::onUpdSearch),
104   FXMAPFUNC(SEL_COMMAND,FXReplaceDialog::ID_REPLACE_SEL,FXReplaceDialog::onCmdReplaceSel),
105   FXMAPFUNC(SEL_UPDATE,FXReplaceDialog::ID_REPLACE_ALL,FXReplaceDialog::onUpdSearch),
106   FXMAPFUNC(SEL_COMMAND,FXReplaceDialog::ID_REPLACE_ALL,FXReplaceDialog::onCmdReplaceAll),
107   FXMAPFUNC(SEL_UPDATE,FXReplaceDialog::ID_DIR,FXReplaceDialog::onUpdDir),
108   FXMAPFUNC(SEL_COMMAND,FXReplaceDialog::ID_DIR,FXReplaceDialog::onCmdDir),
109   FXMAPFUNC(SEL_UPDATE,FXReplaceDialog::ID_WRAP,FXReplaceDialog::onUpdWrap),
110   FXMAPFUNC(SEL_COMMAND,FXReplaceDialog::ID_WRAP,FXReplaceDialog::onCmdWrap),
111   FXMAPFUNC(SEL_UPDATE,FXReplaceDialog::ID_CASE,FXReplaceDialog::onUpdCase),
112   FXMAPFUNC(SEL_COMMAND,FXReplaceDialog::ID_CASE,FXReplaceDialog::onCmdCase),
113   FXMAPFUNC(SEL_UPDATE,FXReplaceDialog::ID_REGEX,FXReplaceDialog::onUpdRegex),
114   FXMAPFUNC(SEL_COMMAND,FXReplaceDialog::ID_REGEX,FXReplaceDialog::onCmdRegex),
115   FXMAPFUNC(SEL_COMMAND,FXReplaceDialog::ID_SEARCH_UP,FXReplaceDialog::onCmdSearchHistUp),
116   FXMAPFUNC(SEL_COMMAND,FXReplaceDialog::ID_SEARCH_DN,FXReplaceDialog::onCmdSearchHistDn),
117   FXMAPFUNC(SEL_COMMAND,FXReplaceDialog::ID_REPLACE_UP,FXReplaceDialog::onCmdReplaceHistUp),
118   FXMAPFUNC(SEL_COMMAND,FXReplaceDialog::ID_REPLACE_DN,FXReplaceDialog::onCmdReplaceHistDn),
119   };
120 
121 
122 // Object implementation
FXIMPLEMENT(FXReplaceDialog,FXDialogBox,FXReplaceDialogMap,ARRAYNUMBER (FXReplaceDialogMap))123 FXIMPLEMENT(FXReplaceDialog,FXDialogBox,FXReplaceDialogMap,ARRAYNUMBER(FXReplaceDialogMap))
124 
125 
126 // Search and Replace Dialog
127 FXReplaceDialog::FXReplaceDialog(FXWindow* own,const FXString& caption,FXIcon* icn,FXuint opts,FXint x,FXint y,FXint w,FXint h):FXDialogBox(own,caption,opts|DECOR_TITLE|DECOR_BORDER|DECOR_RESIZE,x,y,w,h,10,10,10,10, 10,10){
128   FXHorizontalFrame* buttons=new FXHorizontalFrame(this,LAYOUT_SIDE_BOTTOM|LAYOUT_RIGHT,0,0,0,0,0,0,0,0);
129   search=new FXButton(buttons,tr("&Search\tSearch\tSearch for pattern."),NULL,this,ID_SEARCH,BUTTON_INITIAL|BUTTON_DEFAULT|FRAME_RAISED|FRAME_THICK|LAYOUT_FILL_Y,0,0,0,0,HORZ_PAD,HORZ_PAD,VERT_PAD,VERT_PAD);
130   replace=new FXButton(buttons,tr("&Replace\tReplace\tSearch and replace pattern."),NULL,this,ID_REPLACE,BUTTON_DEFAULT|FRAME_RAISED|FRAME_THICK|LAYOUT_FILL_Y,0,0,0,0,HORZ_PAD,HORZ_PAD,VERT_PAD,VERT_PAD);
131   replacesel=new FXButton(buttons,tr("Replace Sel\tReplace in selection\tReplace all in selection."),NULL,this,ID_REPLACE_SEL,BUTTON_DEFAULT|FRAME_RAISED|FRAME_THICK|LAYOUT_CENTER_Y,0,0,0,0,6,6,VERT_PAD,VERT_PAD);
132   replaceall=new FXButton(buttons,tr("Replace All\tReplace all\tReplace all."),NULL,this,ID_REPLACE_ALL,BUTTON_DEFAULT|FRAME_RAISED|FRAME_THICK|LAYOUT_CENTER_Y,0,0,0,0,6,6,VERT_PAD,VERT_PAD);
133   cancel=new FXButton(buttons,tr("&Close"),NULL,this,ID_CANCEL,BUTTON_DEFAULT|FRAME_RAISED|FRAME_THICK|LAYOUT_FILL_Y,0,0,0,0,HORZ_PAD,HORZ_PAD,VERT_PAD,VERT_PAD);
134   new FXHorizontalSeparator(this,SEPARATOR_GROOVE|LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X);
135   FXHorizontalFrame* toppart=new FXHorizontalFrame(this,LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X|LAYOUT_CENTER_Y,0,0,0,0, 0,0,0,0, 10,10);
136   new FXLabel(toppart,FXString::null,icn,ICON_BEFORE_TEXT|JUSTIFY_CENTER_X|JUSTIFY_CENTER_Y|LAYOUT_FILL_Y|LAYOUT_FILL_X);
137   FXVerticalFrame* entry=new FXVerticalFrame(toppart,LAYOUT_FILL_X|LAYOUT_CENTER_Y,0,0,0,0, 0,0,0,0);
138   searchlabel=new FXLabel(entry,tr("Search &For:"),NULL,JUSTIFY_LEFT|ICON_BEFORE_TEXT|LAYOUT_TOP|LAYOUT_LEFT|LAYOUT_FILL_X);
139   searchbox=new FXHorizontalFrame(entry,FRAME_SUNKEN|FRAME_THICK|LAYOUT_FILL_X|LAYOUT_CENTER_Y,0,0,0,0, 0,0,0,0, 0,0);
140   searchtext=new FXTextField(searchbox,26,this,ID_SEARCH_TEXT,TEXTFIELD_ENTER_ONLY|LAYOUT_FILL_X|LAYOUT_FILL_Y,0,0,0,0,4,4,2,2);
141   FXVerticalFrame* searcharrows=new FXVerticalFrame(searchbox,LAYOUT_RIGHT|LAYOUT_FILL_Y,0,0,0,0, 0,0,0,0, 0,0);
142   FXArrowButton* ar1=new FXArrowButton(searcharrows,this,ID_SEARCH_UP,FRAME_RAISED|FRAME_THICK|ARROW_UP|ARROW_REPEAT|LAYOUT_FILL_Y|LAYOUT_FIX_WIDTH, 0,0,16,0, 1,1,1,1);
143   FXArrowButton* ar2=new FXArrowButton(searcharrows,this,ID_SEARCH_DN,FRAME_RAISED|FRAME_THICK|ARROW_DOWN|ARROW_REPEAT|LAYOUT_FILL_Y|LAYOUT_FIX_WIDTH, 0,0,16,0, 1,1,1,1);
144   ar1->setArrowSize(3);
145   ar2->setArrowSize(3);
146   replacelabel=new FXLabel(entry,tr("Re&place With:"),NULL,LAYOUT_LEFT);
147   replacebox=new FXHorizontalFrame(entry,FRAME_SUNKEN|FRAME_THICK|LAYOUT_FILL_X|LAYOUT_CENTER_Y,0,0,0,0, 0,0,0,0, 0,0);
148   replacetext=new FXTextField(replacebox,26,this,ID_REPLACE_TEXT,TEXTFIELD_ENTER_ONLY|LAYOUT_FILL_X|LAYOUT_FILL_Y,0,0,0,0,4,4,2,2);
149   FXVerticalFrame* replacearrows=new FXVerticalFrame(replacebox,LAYOUT_RIGHT|LAYOUT_FILL_Y,0,0,0,0, 0,0,0,0, 0,0);
150   FXArrowButton* ar3=new FXArrowButton(replacearrows,this,ID_REPLACE_UP,FRAME_RAISED|FRAME_THICK|ARROW_UP|ARROW_REPEAT|LAYOUT_FILL_Y|LAYOUT_FIX_WIDTH, 0,0,16,0, 1,1,1,1);
151   FXArrowButton* ar4=new FXArrowButton(replacearrows,this,ID_REPLACE_DN,FRAME_RAISED|FRAME_THICK|ARROW_DOWN|ARROW_REPEAT|LAYOUT_FILL_Y|LAYOUT_FIX_WIDTH, 0,0,16,0, 1,1,1,1);
152   ar3->setArrowSize(3);
153   ar4->setArrowSize(3);
154   FXHorizontalFrame* options1=new FXHorizontalFrame(entry,LAYOUT_FILL_X,0,0,0,0, 0,0,0,0);
155   new FXCheckButton(options1,tr("&Expression\tRegular Expression\tPerform regular-expression match."),this,ID_REGEX,ICON_BEFORE_TEXT|LAYOUT_CENTER_X);
156   new FXCheckButton(options1,tr("&Ignore Case\tCase Insensitive\tPerform case-insentive match."),this,ID_CASE,ICON_BEFORE_TEXT|LAYOUT_CENTER_X);
157   new FXCheckButton(options1,tr("&Wrap\tWrap Around\tWrap around when searching."),this,ID_WRAP,ICON_BEFORE_TEXT|LAYOUT_CENTER_X);
158   new FXCheckButton(options1,tr("&Backward\tSearch Direction\tChange search direction."),this,ID_DIR,ICON_BEFORE_TEXT|LAYOUT_CENTER_X);
159   replacetext->setHelpText(tr("Text to replace with."));
160   searchmode=SEARCH_EXACT|SEARCH_FORWARD|SEARCH_WRAP;
161   loadHistory();
162   }
163 
164 
165 // Set text or pattern to search for
setSearchText(const FXString & text)166 void FXReplaceDialog::setSearchText(const FXString& text){
167   searchtext->setText(text);
168   }
169 
170 
171 // Return text or pattern the user has entered
getSearchText() const172 FXString FXReplaceDialog::getSearchText() const {
173   return searchtext->getText();
174   }
175 
176 
177 // Set text or pattern to search with
setReplaceText(const FXString & text)178 void FXReplaceDialog::setReplaceText(const FXString& text){
179   replacetext->setText(text);
180   }
181 
182 
183 // Return text or pattern the user has entered
getReplaceText() const184 FXString FXReplaceDialog::getReplaceText() const {
185   return replacetext->getText();
186   }
187 
188 
189 // Change search text color
setSearchTextColor(FXColor clr)190 void FXReplaceDialog::setSearchTextColor(FXColor clr){
191   searchtext->setBackColor(clr);
192   }
193 
194 
195 // Return search text color
getSearchTextColor() const196 FXColor FXReplaceDialog::getSearchTextColor() const {
197   return searchtext->getBackColor();
198   }
199 
200 
201 // Change replace text color
setReplaceTextColor(FXColor clr)202 void FXReplaceDialog::setReplaceTextColor(FXColor clr){
203   replacetext->setBackColor(clr);
204   }
205 
206 
207 // Return replace text color
getReplaceTextColor() const208 FXColor FXReplaceDialog::getReplaceTextColor() const {
209   return replacetext->getBackColor();
210   }
211 
212 
213 // Append entry
appendHistory(const FXString & pat,const FXString & sub,FXuint opt)214 void FXReplaceDialog::appendHistory(const FXString& pat,const FXString& sub,FXuint opt){
215   if(!pat.empty()){
216     if(searchHistory[0]!=pat){
217       for(FXuval i=ARRAYNUMBER(searchHistory)-1; i>0; --i){
218         swap(searchHistory[i],searchHistory[i-1]);
219         swap(replacHistory[i],replacHistory[i-1]);
220         swap(optionHistory[i],optionHistory[i-1]);
221         }
222       }
223     searchHistory[0]=pat;
224     replacHistory[0]=sub;
225     optionHistory[0]=opt;
226     activeHistory=-1;
227     }
228   }
229 
230 
231 // Load registy
loadHistory()232 void FXReplaceDialog::loadHistory(){
233   for(FXuval i=0; i<ARRAYNUMBER(searchHistory); ++i){
234     searchHistory[i]=getApp()->reg().readStringEntry(sectionName,skey[i],FXString::null);
235     if(searchHistory[i].empty()) break;
236     replacHistory[i]=getApp()->reg().readStringEntry(sectionName,rkey[i],FXString::null);
237     optionHistory[i]=getApp()->reg().readUIntEntry(sectionName,mkey[i],SEARCH_EXACT|SEARCH_FORWARD|SEARCH_WRAP);
238     }
239   activeHistory=-1;
240   }
241 
242 
243 // Save registry
saveHistory()244 void FXReplaceDialog::saveHistory(){
245   for(FXuval i=0; i<ARRAYNUMBER(searchHistory); ++i){
246     if(!searchHistory[i].empty()){
247       getApp()->reg().writeStringEntry(sectionName,skey[i],searchHistory[i].text());
248       getApp()->reg().writeStringEntry(sectionName,rkey[i],replacHistory[i].text());
249       getApp()->reg().writeUIntEntry(sectionName,mkey[i],optionHistory[i]);
250       }
251     else{
252       getApp()->reg().deleteEntry(sectionName,skey[i]);
253       getApp()->reg().deleteEntry(sectionName,rkey[i]);
254       getApp()->reg().deleteEntry(sectionName,mkey[i]);
255       }
256     }
257   }
258 
259 
260 // Return with code for search, and close dialog
onCmdSearch(FXObject *,FXSelector,void *)261 long FXReplaceDialog::onCmdSearch(FXObject*,FXSelector,void*){
262   appendHistory(getSearchText(),getReplaceText(),getSearchMode());
263   getApp()->stopModal(this,SEARCH);
264   return 1;
265   }
266 
267 
268 // Grey out buttons if no search text
onUpdSearch(FXObject * sender,FXSelector,void *)269 long FXReplaceDialog::onUpdSearch(FXObject* sender,FXSelector,void*){
270   sender->handle(this,searchtext->getText().empty()?FXSEL(SEL_COMMAND,ID_DISABLE):FXSEL(SEL_COMMAND,ID_ENABLE),NULL);
271   return 1;
272   }
273 
274 
275 // Return with code for replace, and close dialog
onCmdReplace(FXObject *,FXSelector,void *)276 long FXReplaceDialog::onCmdReplace(FXObject*,FXSelector,void*){
277   appendHistory(getSearchText(),getReplaceText(),getSearchMode());
278   getApp()->stopModal(this,REPLACE);
279   return 1;
280   }
281 
282 
283 // Return with code for replace-in-selection, and close dialog
onCmdReplaceSel(FXObject *,FXSelector,void *)284 long FXReplaceDialog::onCmdReplaceSel(FXObject*,FXSelector,void*){
285   appendHistory(getSearchText(),getReplaceText(),getSearchMode());
286   getApp()->stopModal(this,REPLACE_SEL);
287   return 1;
288   }
289 
290 
291 // Return with code for replace-all, and close dialog
onCmdReplaceAll(FXObject *,FXSelector,void *)292 long FXReplaceDialog::onCmdReplaceAll(FXObject*,FXSelector,void*){
293   appendHistory(getSearchText(),getReplaceText(),getSearchMode());
294   getApp()->stopModal(this,REPLACE_ALL);
295   return 1;
296   }
297 
298 
299 // Change search direction
onCmdDir(FXObject *,FXSelector,void *)300 long FXReplaceDialog::onCmdDir(FXObject*,FXSelector,void*){
301   searchmode^=(SEARCH_FORWARD|SEARCH_BACKWARD);
302   return 1;
303   }
304 
305 
306 // Update search direction
onUpdDir(FXObject * sender,FXSelector,void *)307 long FXReplaceDialog::onUpdDir(FXObject* sender,FXSelector,void*){
308   sender->handle(this,(searchmode&SEARCH_BACKWARD)?FXSEL(SEL_COMMAND,ID_CHECK):FXSEL(SEL_COMMAND,ID_UNCHECK),NULL);
309   return 1;
310   }
311 
312 
313 // Change wrap mode
onCmdWrap(FXObject *,FXSelector,void *)314 long FXReplaceDialog::onCmdWrap(FXObject*,FXSelector,void*){
315   searchmode^=SEARCH_WRAP;
316   return 1;
317   }
318 
319 
320 // Update wrap mode
onUpdWrap(FXObject * sender,FXSelector,void *)321 long FXReplaceDialog::onUpdWrap(FXObject* sender,FXSelector,void*){
322   sender->handle(this,(searchmode&SEARCH_WRAP)?FXSEL(SEL_COMMAND,ID_CHECK):FXSEL(SEL_COMMAND,ID_UNCHECK),NULL);
323   return 1;
324   }
325 
326 
327 // Change case sensitive mode
onCmdCase(FXObject *,FXSelector,void *)328 long FXReplaceDialog::onCmdCase(FXObject*,FXSelector,void*){
329   searchmode^=SEARCH_IGNORECASE;
330   return 1;
331   }
332 
333 
334 // Update case sensitive mode
onUpdCase(FXObject * sender,FXSelector,void *)335 long FXReplaceDialog::onUpdCase(FXObject* sender,FXSelector,void*){
336   sender->handle(this,(searchmode&SEARCH_IGNORECASE)?FXSEL(SEL_COMMAND,ID_CHECK):FXSEL(SEL_COMMAND,ID_UNCHECK),NULL);
337   return 1;
338   }
339 
340 
341 // Change search mode
onCmdRegex(FXObject *,FXSelector,void *)342 long FXReplaceDialog::onCmdRegex(FXObject*,FXSelector,void*){
343   searchmode^=SEARCH_REGEX;
344   return 1;
345   }
346 
347 
348 // Update search mode
onUpdRegex(FXObject * sender,FXSelector,void *)349 long FXReplaceDialog::onUpdRegex(FXObject* sender,FXSelector,void*){
350   sender->handle(this,(searchmode&SEARCH_REGEX)?FXSEL(SEL_COMMAND,ID_CHECK):FXSEL(SEL_COMMAND,ID_UNCHECK),NULL);
351   return 1;
352   }
353 
354 
355 // Scroll back in search history
onCmdSearchHistUp(FXObject *,FXSelector,void *)356 long FXReplaceDialog::onCmdSearchHistUp(FXObject*,FXSelector,void*){
357   if(activeHistory+1<(FXint)ARRAYNUMBER(searchHistory) && !searchHistory[activeHistory+1].empty()){
358     activeHistory++;
359     FXASSERT(0<=activeHistory && activeHistory<(FXint)ARRAYNUMBER(searchHistory));
360     setSearchText(searchHistory[activeHistory]);
361     setReplaceText(replacHistory[activeHistory]);
362     setSearchMode(optionHistory[activeHistory]);
363     }
364   else{
365     getApp()->beep();
366     }
367   return 1;
368   }
369 
370 
371 // Scroll forward in search history
onCmdSearchHistDn(FXObject *,FXSelector,void *)372 long FXReplaceDialog::onCmdSearchHistDn(FXObject*,FXSelector,void*){
373   if(0<activeHistory){
374     activeHistory--;
375     FXASSERT(0<=activeHistory && activeHistory<(FXint)ARRAYNUMBER(searchHistory));
376     setSearchText(searchHistory[activeHistory]);
377     setReplaceText(replacHistory[activeHistory]);
378     setSearchMode(optionHistory[activeHistory]);
379     }
380   else{
381     activeHistory=-1;
382     setSearchText(FXString::null);
383     setReplaceText(FXString::null);
384     setSearchMode(SEARCH_EXACT|SEARCH_FORWARD|SEARCH_WRAP);
385     }
386   return 1;
387   }
388 
389 
390 // Scroll back in replace history
onCmdReplaceHistUp(FXObject *,FXSelector,void *)391 long FXReplaceDialog::onCmdReplaceHistUp(FXObject*,FXSelector,void*){
392   if(activeHistory+1<(FXint)ARRAYNUMBER(searchHistory) && !searchHistory[activeHistory+1].empty()){
393     activeHistory++;
394     FXASSERT(0<=activeHistory && activeHistory<(FXint)ARRAYNUMBER(searchHistory));
395     setReplaceText(replacHistory[activeHistory]);
396     }
397   else{
398     getApp()->beep();
399     }
400   return 1;
401   }
402 
403 
404 // Scroll back in replace history
onCmdReplaceHistDn(FXObject *,FXSelector,void *)405 long FXReplaceDialog::onCmdReplaceHistDn(FXObject*,FXSelector,void*){
406   if(0<activeHistory){
407     activeHistory--;
408     FXASSERT(0<=activeHistory && activeHistory<(FXint)ARRAYNUMBER(searchHistory));
409     setReplaceText(replacHistory[activeHistory]);
410     }
411   else{
412     activeHistory=-1;
413     setReplaceText(FXString::null);
414     }
415   return 1;
416   }
417 
418 
419 // Keyboard press in search text field
onSearchKey(FXObject *,FXSelector,void * ptr)420 long FXReplaceDialog::onSearchKey(FXObject*,FXSelector,void* ptr){
421   setSearchTextColor(getApp()->getBackColor());
422   setReplaceTextColor(getApp()->getBackColor());
423   switch(((FXEvent*)ptr)->code){
424     case KEY_Page_Up:
425       searchmode=(searchmode&~SEARCH_FORWARD)|SEARCH_BACKWARD;
426       return onCmdSearch(this,FXSEL(SEL_COMMAND,ID_SEARCH),NULL);
427     case KEY_Page_Down:
428       searchmode=(searchmode&~SEARCH_BACKWARD)|SEARCH_FORWARD;
429       return onCmdSearch(this,FXSEL(SEL_COMMAND,ID_SEARCH),NULL);
430     case KEY_Up:
431     case KEY_KP_Up:
432       return onCmdSearchHistUp(this,FXSEL(SEL_COMMAND,ID_SEARCH_UP),NULL);
433     case KEY_Down:
434     case KEY_KP_Down:
435       return onCmdSearchHistDn(this,FXSEL(SEL_COMMAND,ID_SEARCH_DN),NULL);
436     case KEY_i:
437       if(!(((FXEvent*)ptr)->state&CONTROLMASK)) return 0;
438       searchmode^=SEARCH_IGNORECASE;
439       return 1;
440     case KEY_e:
441       if(!(((FXEvent*)ptr)->state&CONTROLMASK)) return 0;
442       searchmode^=SEARCH_REGEX;
443       return 1;
444     case KEY_w:
445       if(!(((FXEvent*)ptr)->state&CONTROLMASK)) return 0;
446       searchmode^=SEARCH_WRAP;
447       return 1;
448     case KEY_b:
449       if(!(((FXEvent*)ptr)->state&CONTROLMASK)) return 0;
450       searchmode^=(SEARCH_FORWARD|SEARCH_BACKWARD);
451       return 1;
452     }
453   return 0;
454   }
455 
456 
457 // Keyboard press in replace text field
onReplaceKey(FXObject *,FXSelector,void * ptr)458 long FXReplaceDialog::onReplaceKey(FXObject*,FXSelector,void* ptr){
459   setReplaceTextColor(getApp()->getBackColor());
460   switch(((FXEvent*)ptr)->code){
461     case KEY_Up:
462     case KEY_KP_Up:
463       return onCmdReplaceHistUp(this,FXSEL(SEL_COMMAND,ID_REPLACE_UP),NULL);
464     case KEY_Down:
465     case KEY_KP_Down:
466       return onCmdReplaceHistDn(this,FXSEL(SEL_COMMAND,ID_REPLACE_DN),NULL);
467     }
468   return 0;
469   }
470 
471 
472 // Wheeled in search text
onWheelSearch(FXObject *,FXSelector,void * ptr)473 long FXReplaceDialog::onWheelSearch(FXObject*,FXSelector,void* ptr){
474   if(((FXEvent*)ptr)->code>0){
475     return onCmdSearchHistUp(this,FXSEL(SEL_COMMAND,ID_SEARCH_UP),NULL);
476     }
477   if(((FXEvent*)ptr)->code<0){
478     return onCmdSearchHistDn(this,FXSEL(SEL_COMMAND,ID_SEARCH_DN),NULL);
479     }
480   return 1;
481   }
482 
483 
484 // Wheeled in replace text
onWheelReplace(FXObject *,FXSelector,void * ptr)485 long FXReplaceDialog::onWheelReplace(FXObject*,FXSelector,void* ptr){
486   if(((FXEvent*)ptr)->code>0){
487     return onCmdReplaceHistUp(this,FXSEL(SEL_COMMAND,ID_REPLACE_UP),NULL);
488     }
489   if(((FXEvent*)ptr)->code<0){
490     return onCmdReplaceHistDn(this,FXSEL(SEL_COMMAND,ID_REPLACE_DN),NULL);
491     }
492   return 1;
493   }
494 
495 
496 // Force the initial text to be seleced
execute(FXuint placement)497 FXuint FXReplaceDialog::execute(FXuint placement){
498   create();
499   searchtext->setFocus();
500   show(placement);
501   activeHistory=-1;
502   return getApp()->runModalFor(this);
503   }
504 
505 
506 // Save data
save(FXStream & store) const507 void FXReplaceDialog::save(FXStream& store) const {
508   FXDialogBox::save(store);
509   store << searchlabel;
510   store << searchtext;
511   store << searchbox;
512   store << replacelabel;
513   store << replacetext;
514   store << replacebox;
515   store << search;
516   store << replace;
517   store << replacesel;
518   store << replaceall;
519   store << cancel;
520   store << searchmode;
521   }
522 
523 
524 // Load data
load(FXStream & store)525 void FXReplaceDialog::load(FXStream& store){
526   FXDialogBox::load(store);
527   store >> searchlabel;
528   store >> searchtext;
529   store >> searchbox;
530   store >> replacelabel;
531   store >> replacetext;
532   store >> replacebox;
533   store >> search;
534   store >> replace;
535   store >> replacesel;
536   store >> replaceall;
537   store >> cancel;
538   store >> searchmode;
539   }
540 
541 
542 // Cleanup
~FXReplaceDialog()543 FXReplaceDialog::~FXReplaceDialog(){
544   saveHistory();
545   searchlabel=(FXLabel*)-1L;
546   searchtext=(FXTextField*)-1L;
547   searchbox=(FXHorizontalFrame*)-1L;
548   replacelabel=(FXLabel*)-1L;
549   replacetext=(FXTextField*)-1L;
550   replacebox=(FXHorizontalFrame*)-1L;
551   search=(FXButton*)-1L;
552   replace=(FXButton*)-1L;
553   replacesel=(FXButton*)-1L;
554   replaceall=(FXButton*)-1L;
555   cancel=(FXButton*)-1L;
556   }
557 
558 }
559