Bugfix for: [ 1942506 ] JanoEditor - Random characters in search field
[cake.git] / workbench / tools / Edit / Search.c
blob705bb6ea3dd06ebe7bbf19add1ffb51dc59be7c3
1 /*********************************************************
2 ** Search.c: Implementation of `Search' menu commands. **
3 ** (Search/replace/goto/bracket match) **
4 ** Written by T.Pierron, aug 5, 2000 **
5 *********************************************************/
7 #include <intuition/intuition.h>
8 #include <intuition/sghooks.h>
9 #include <libraries/gadtools.h>
10 #include <utility/hooks.h>
11 #include "Project.h"
12 #include "Gui.h"
13 #include "Jed.h"
14 #include "Search.h"
15 #include "Edit.h"
16 #include "Utility.h"
17 #include "Events.h"
18 #include "ProtoTypes.h"
20 #define CATCOMP_NUMBERS
21 #include "strings.h"
23 static struct Window *swin = NULL; /* Main search/replace window */
24 static struct Gadget *sgads,*sg; /* Gadtool's gadgets */
25 static struct Gadget *rep=NULL,*search; /* Specific gadtools gadgets */
26 extern struct IntuiMessage *msg,msgbuf; /* For collecting events */
27 extern UBYTE TypeChar[]; /* Characters type */
28 ULONG swinsig = 0; /* Signal bit */
29 struct NewWindow NewSWin = /* Main window information */
31 0,0,0,0, 1,1, /* LE,TE,Wid,Hei, Pens */
32 IDCMP_MENUPICK | IDCMP_CLOSEWINDOW | IDCMP_GADGETUP | IDCMP_NEWSIZE | IDCMP_VANILLAKEY,
33 WFLG_CLOSEGADGET | WFLG_NEWLOOKMENUS | WFLG_DEPTHGADGET | WFLG_ACTIVATE | WFLG_SMART_REFRESH |
34 WFLG_DRAGBAR,
35 NULL, /* FirstGadget */
36 NULL, /* CheckMark */
37 NULL, /* Title */
38 NULL, /* Screen */
39 NULL, /* BitMap */
40 100, 50, 0xffff, 0xffff, /* Min & Max size */
41 CUSTOMSCREEN
44 WORD SWinZoom[4];
46 UBYTE SearchStr[60], SLen=0; /* Saved search and replace string */
47 UBYTE ReplaceStr[60],RLen=0;
48 UBYTE CharClass[256];
50 char HilitePattern(Project, STRPTR, BYTE, BYTE quiet);
52 /** Values for `quiet' parameter **/
53 #define VERBOSE 0 /* Move cursor and show warnings */
54 #define VERB_BUT_NO_HIDE_CURS 1 /* Cursor has been already moved */
55 #define FULLY_QUIET 2 /* Just internally move cursor */
56 #define QUIET_BUT_WARN 3 /* Warn if pattern not found */
58 /*** Init search table for faster text scan ***/
59 void init_searchtable( void )
61 static UBYTE UpClassEqv[] =
62 "aÀÁÂÃÄÅàáâãäå" "eÈÉÊËèéêë" "iÌÍÎÏìíîï" "nñÑ"
63 "oÒÓÔÕÖòóôõöøØ" "uÙÚÛÜùúûü" "yýÿ" "cçÇ";
64 STRPTR p;
65 UWORD i;
67 /* Init character class equivalence table */
68 for (p=CharClass, i=0; i<256; *p++ = i++);
69 for (p=CharClass + (i='A'); i<='Z'; *p++ = i+('a'-'A'), i++);
70 for (p=UpClassEqv; *p; p++)
71 if(*p < 128) i = *p; else CharClass[ *p ] = i;
74 #ifdef __GNUC__
75 /** Special hook executed while editing a line **/
76 ULONG CheckConfirm(struct Hook *hook, struct SGWork *sgw, ULONG *msg)
78 if(*msg == SGH_KEY)
80 if( sgw->Code == 27 )
81 sgw->Code = 0, sgw->Actions |= SGA_END;
82 else if( sgw->EditOp == EO_ENTER )
83 sgw->Code = (sgw->Actions & (SGA_NEXTACTIVE | SGA_PREVACTIVE) ? 0 : 1);
85 else return 0;
86 return ~0;
88 static struct Hook hook;
89 #endif
91 /*** Open an asynchronous GUI for searching/replacing string ***/
92 BYTE setup_winsearch(Project p, UBYTE replace)
94 static struct NewGadget NG;
95 static ULONG SGadTags[] = {
96 GTCB_Checked, 0,
97 GTST_MaxChars, sizeof(SearchStr)-1,
98 GT_Underscore, '_',
99 TAG_DONE
101 WORD Wid[6];
102 WORD innerWidth, innerHeight;
104 /** If window is already opened, but isn't to the required type **/
105 if( (rep ? 0:1) == replace )
106 close_searchwnd(TRUE);
107 else
109 /** Window already opened? **/
110 if( swin ) {
111 ActivateWindow(swin);
112 WindowToFront(swin);
113 /* Uniconified it, if needed */
114 if(swin->Height<=swin->BorderTop) ZipWindow(swin);
115 /* Copy selected part of text in string gadget */
116 if(p->ccp.select)
117 GetSI(search)->BufferPos = copy_mark_to_buf(p,GetSI(search)->Buffer,GetSI(search)->MaxChars),
118 unmark_all(p,FALSE), inv_curs(p,TRUE);
119 ActivateGadget(search, swin, NULL);
120 return TRUE;
123 /** Compute optimal window width **/
125 WORD len,sum,*wid;
126 UBYTE **str;
127 for(str=SWinTxt+(replace ? 6:8), sum=0, wid=Wid; *str; str++)
129 len=TextLength(&RPT,*str,strlen(*str))+20;
130 if( replace ) *wid++ = len, sum += len;
131 else if( sum < len ) sum = len;
133 /* Minimal width of 320 pixels */
134 if(replace == 0) {
135 if(sum < (320-50)/4) sum = (320-50)/4;
136 len = sum*4+50;
137 Wid[0] = sum;
138 } else len = sum + 70;
139 innerWidth = len;
141 /* Window too large ? */
142 if(innerWidth > Scr->Width) innerWidth = Scr->Width;
143 innerHeight = (replace ?
144 prefs.scrfont->tf_YSize * 4 + 40 :
145 prefs.scrfont->tf_YSize * 3 + 32);
146 NewSWin.LeftEdge = (Scr->Width - innerWidth) / 2;
147 NewSWin.Screen = Scr;
148 NewSWin.Title = SWinTxt[replace ? 1 : 0];
150 /** Compute iconified window dimensions **/
151 SWinZoom[2] = TextLength(&RPT,NewSWin.Title,strlen(NewSWin.Title))+100;
152 SWinZoom[0] = Scr->Width-SWinZoom[2]-gui.depthwid;
153 SWinZoom[3] = Scr->BarHeight+1;
154 SWinZoom[1] = 0;
156 /** Let the cursor visible **/
157 if( (NewSWin.TopEdge = Wnd->TopEdge + p->ycurs + 10)+innerHeight > Scr->Height )
158 NewSWin.TopEdge = 50;
160 /** Setting up GUI **/
161 if(NULL != (swin = (APTR) OpenWindowTags(&NewSWin,
162 WA_Zoom, SWinZoom,
163 WA_NewLookMenus,TRUE,
164 WA_InnerWidth, innerWidth,
165 WA_InnerHeight, innerHeight,
166 TAG_DONE)))
168 UWORD I;
169 rep = search = sgads = NULL;
170 sg = (void *) CreateContext(&sgads);
171 swin->UserData = NewSWin.Title;
173 #ifdef __GNUC__
174 /* Init strgad hook (v37+ only) */
175 hook.h_Entry = HookEntry;
176 hook.h_SubEntry = CheckConfirm;
177 #endif
179 /** `Search' string gadget **/
180 if( (NG.ng_LeftEdge = TextLength(&RPT,SWinTxt[2],strlen(SWinTxt[2]))) <
181 (I = TextLength(&RPT,SWinTxt[3],strlen(SWinTxt[3]))) )
182 NG.ng_LeftEdge = I;
183 NG.ng_TopEdge = swin->BorderTop+5;
184 NG.ng_Width = swin->Width-swin->BorderLeft-swin->BorderRight-(NG.ng_LeftEdge += 20);
185 NG.ng_Height = prefs.scrfont->tf_YSize+6;
186 NG.ng_Flags = PLACETEXT_LEFT;
187 NG.ng_TextAttr = &prefs.attrscr;
188 NG.ng_VisualInfo = Vi;
189 NG.ng_GadgetText = SWinTxt[2];
190 NG.ng_GadgetID = 0;
191 search = sg = (void *)
192 #ifdef __GNUC__
193 CreateGadget(STRING_KIND, sg, &NG, GTST_EditHook, (ULONG)&hook, TAG_MORE, (ULONG)(SGadTags+2));
194 #else
195 CreateGadgetA(STRING_KIND, sg, &NG, (ULONG)(SGadTags+2));
196 #endif
197 /** If user has selected part of text, copy it in the `Search' string gadget **/
198 if(p->ccp.select)
199 GetSI(sg)->BufferPos = copy_mark_to_buf(p,GetSI(sg)->Buffer,GetSI(sg)->MaxChars),
200 unmark_all(p,FALSE), inv_curs(p,TRUE);
201 else
202 /* Copy the previous search string */
203 CopyMem(SearchStr, GetSI(search)->Buffer, (GetSI(search)->BufferPos = SLen) + 1);
205 /** `Replace' string gadget **/
206 if( replace )
208 NG.ng_TopEdge += NG.ng_Height+2;
209 NG.ng_GadgetText = SWinTxt[3];
210 NG.ng_GadgetID = 1;
211 rep = sg = (void *)
212 #ifdef __GNUC__
213 CreateGadget(STRING_KIND, sg, &NG, GTST_EditHook, (ULONG)&hook, TAG_MORE, (ULONG)(SGadTags+2));
214 #else
215 CreateGadgetA(STRING_KIND, sg, &NG, (ULONG)(SGadTags+2));
216 #endif
217 /* Copy last replace string */
218 CopyMem(ReplaceStr, GetSI(rep)->Buffer, (GetSI(rep)->BufferPos = RLen) + 1);
221 /** Check box gadgets **/
222 NG.ng_TopEdge += NG.ng_Height+4;
223 for(NG.ng_LeftEdge = swin->BorderLeft + 10, I=4; I<6; I++)
225 NG.ng_Flags = (I&1 ? PLACETEXT_LEFT : PLACETEXT_RIGHT);
226 NG.ng_GadgetText = SWinTxt[I];
227 NG.ng_GadgetID = I;
228 SGadTags[1] = (&prefs.matchcase)[I-4];
229 sg = (void *) CreateGadgetA(CHECKBOX_KIND, sg, &NG, (struct TagItem *)SGadTags);
230 sg->LeftEdge = (I&1 ? swin->Width-swin->BorderRight-10-sg->Width : swin->BorderLeft + 10);
231 if(I&1) NG.ng_TopEdge += NG.ng_Height;
234 /** Bottom row of gadget ([Replace/Replace All/]Search/Previous/Use/Cancel) **/
235 NG.ng_TopEdge += 3;
236 NG.ng_Flags = PLACETEXT_IN;
237 NG.ng_Height -= 2;
238 if(replace == 0) I+=2;
239 for(NG.ng_LeftEdge = swin->BorderLeft + 10; I<12; I++)
241 NG.ng_Width = (replace ? Wid[I-6] : Wid[0]);
242 NG.ng_GadgetText = SWinTxt[I];
243 NG.ng_GadgetID = I;
244 sg = (void *) CreateGadgetA(BUTTON_KIND, sg, &NG, (struct TagItem *)(SGadTags+4));
245 NG.ng_LeftEdge += NG.ng_Width+10;
248 /** Finally, add gadgets into the window **/
249 AddGList(swin, sgads, -1, -1, NULL);
250 RefreshGList(sgads, swin, NULL, -1);
251 SetMenuStrip(swin, Menu);
252 ActivateGadget(search, swin, NULL);
254 /** Register window's signal bit **/
255 swinsig = 1 << swin->UserPort->mp_SigBit;
256 recalc_sigbits();
257 return TRUE;
259 } else ThrowError(Wnd, ErrMsg(ERR_NOMEM));
260 return FALSE;
263 /*** Free all allocated ressources ***/
264 void close_searchwnd(BYTE really)
266 if(swin)
268 if(really) {
269 CopyMem(GetSI(search)->Buffer, SearchStr, (SLen=GetSI(search)->NumChars)+1);
270 if(rep) CopyMem(GetSI(rep)->Buffer, ReplaceStr, (RLen=GetSI(rep)->NumChars)+1);
273 ClearMenuStrip(swin);
274 CloseWindow(swin);
275 swinsig=0; swin=NULL; rep=NULL;
276 recalc_sigbits();
277 if(sgads) FreeGadgets(sgads);
281 /*** Handle messages coming from gadgets ***/
282 BYTE handle_swingadget(struct Gadget *G)
284 extern Project edit;
285 switch( G->GadgetID )
287 case 0:
288 #ifdef __GNUC__
289 if( msgbuf.Code != 1 ) break;
290 #endif
291 case 8:
292 if( GetSI(search)->Buffer[0] )
293 HilitePattern(edit, GetSI(search)->Buffer, 1, VERBOSE);
294 /* Activate replace gadget if it exists */
295 if( G->GadgetID == 0 && rep != NULL )
296 ActivateGadget(rep, swin, NULL);
297 break;
298 case 4: case 5:
299 (&prefs.matchcase)[G->GadgetID-4] = ((G->Flags & GFLG_SELECTED) == GFLG_SELECTED);
300 break;
301 case 6:
302 if( GetSI(search)->Buffer[0] ) ReplacePattern( edit );
303 break;
304 case 7:
305 if( GetSI(search)->Buffer[0] ) ReplaceAllPat( edit );
306 break;
307 case 9:
308 if( GetSI(search)->Buffer[0] )
309 HilitePattern(edit, GetSI(search)->Buffer, -1, VERBOSE);
310 break;
311 case 10: close_searchwnd(TRUE); return TRUE;
312 case 11: close_searchwnd(FALSE); return TRUE;
314 return FALSE;
317 /*** Keyboard handler ***/
318 BYTE handle_swinkey( UBYTE Key )
320 extern Project edit;
321 switch( Key )
323 case 'q': case 'Q': case 'c': case 'C':
324 close_searchwnd(FALSE); return TRUE;
325 case 27: case 'u': case 'U':
326 close_searchwnd(TRUE); return TRUE;
327 case 'n': case 'N': Key = 3; goto case_all;
328 case 'p': case 'P': Key = 1; goto case_all;
329 case 'r': case 'R': Key = 2; if(rep == NULL) break;
330 case 'A': case_all:
331 if( GetSI(search)->Buffer[0] == 0 ) break;
332 switch( Key )
334 case 3:
335 case 1: HilitePattern(edit, GetSI(search)->Buffer, Key-2, VERBOSE); break;
336 case 2: ReplacePattern(edit); break;
337 default:ReplaceAllPat(edit);
339 break;
340 case '/': case 'f': case 'F':
341 ActivateGadget(search, swin, NULL); break;
342 case 's': case 'S':
343 if(rep) ActivateGadget(rep, swin, NULL);
345 return FALSE;
348 /*** Handle events coming from search window ***/
349 void handle_search()
351 extern ULONG err_time;
352 /* Collect messages posted to the window port */
353 while( ( msg=GT_GetIMsg(swin->UserPort) ) )
355 /* Copy the entire message into the buffer */
356 CopyMemQuick(msg, &msgbuf, sizeof(msgbuf));
357 GT_ReplyIMsg( msg );
359 switch( msgbuf.Class )
361 case IDCMP_CLOSEWINDOW: close_searchwnd(FALSE); return;
362 case IDCMP_MENUPICK: handle_menu((ULONG)GTMENUITEM_USERDATA(ItemAddress(Menu,msgbuf.Code))); break;
363 case IDCMP_VANILLAKEY: if( handle_swinkey( msgbuf.Code ) ) return; break;
364 case IDCMP_NEWSIZE: if( swin->Height <= swin->BorderTop ) ActivateWindow( Wnd ); return;
365 case IDCMP_GADGETUP:
366 if( handle_swingadget((struct Gadget *)msgbuf.IAddress) ) return;
367 else break;
368 case IDCMP_INTUITICKS:
369 /* An error message which needs to be removed? */
370 if(err_time == 0) err_time = msgbuf.Seconds;
371 if(err_time && msgbuf.Seconds-err_time>4) StopError(swin);
372 break;
377 /*** Find next matching keyword ***/
378 void FindPattern( Project p, BYTE dir )
380 if( swin )
382 /* Window overrides SearchStr buffer */
383 if( GetSI(search)->Buffer[0] )
385 HilitePattern(p, GetSI(search)->Buffer, dir, VERBOSE);
386 return;
388 } else
389 if( *SearchStr )
391 HilitePattern(p, SearchStr, dir, VERBOSE);
392 return;
395 /* No pattern entered! Avoid short-circuit evaluation */
396 if( !p->ccp.select & setup_winsearch(p, 0) )
397 ThrowError(swin, ErrMsg(ERR_NOSEARCHTEXT));
400 /*** Search directly word under cursor ***/
401 void FindWord( Project p, BYTE dir )
403 LINE *ln = p->edited;
404 if( p->nbc < ln->size && TypeChar[ ln->stream[ p->nbc ] ] != SPACE )
406 /* Get the word under the cursor */
407 ULONG start, nbc;
408 SLen = forward_word(ln, p->nbc);
409 while( TypeChar[ ln->stream[ --SLen ] ] == SPACE );
410 SLen -= (start = (p->nbc > 0 ? backward_word(ln, p->nbc) : 0))-1;
411 if(SLen >= sizeof(SearchStr)) SLen = sizeof(SearchStr)-1;
412 CopyMem(ln->stream + start, SearchStr, SLen);
413 SearchStr[ SLen ] = 0;
414 /* Move cursor to start of word, thus this occurency won't be found */
415 nbc = p->nbc; inv_curs(p, FALSE); if(dir < 0) p->nbc = start;
416 if( HilitePattern(p, SearchStr, dir, VERB_BUT_NO_HIDE_CURS) == 0 )
417 p->nbc = nbc, inv_curs(p, TRUE); /* Nothing found */
421 /*** Replace next pattern ***/
422 void ReplacePattern( Project p )
424 STRPTR str; WORD len;
425 if( swin )
427 /* Window overrides SearchStr buffer */
428 if( rep != NULL && (str = GetSI(search)->Buffer)[0] )
430 if( HilitePattern(p, str, 0, VERBOSE) == 1)
432 str = GetSI(rep)->Buffer;
433 len = GetSI(rep)->NumChars;
434 goto replace;
435 } else return;
438 else if( *SearchStr )
440 if(HilitePattern(p, SearchStr, 0, VERBOSE) == 1)
442 str = ReplaceStr;
443 len = RLen;
444 replace: inv_curs(p, FALSE);
445 if(*str) reg_group_by( &p->undo );
446 rem_chars(&p->undo,p->edited,p->nbc,p->nbc+(rep ? GetSI(search)->NumChars : SLen)-1);
447 /* Something to replace with? */
448 if( *str ) {
449 LONG Buf[3];
450 add_string(&p->undo, p->edited, p->nbc, str, len, Buf);
451 reg_group_by( &p->undo );
452 /* Disable recusive replacement */
453 p->nbc += len; p->xcurs += len*XSIZE;
455 REDRAW_CURLINE( p );
456 /* Hilight next pattern */
457 if( !HilitePattern(p, rep ? GetSI(search)->Buffer : SearchStr, 0, VERBOSE) )
458 inv_curs(p, TRUE);
461 else /* No pattern given yet! */
462 if( !p->ccp.select & setup_winsearch(p, 1) )
463 ThrowError(swin, ErrMsg(ERR_NOSEARCHTEXT));
466 /*** Replace all pattern ***/
467 void ReplaceAllPat( Project p )
469 struct { /* This vars must appear in this order for RawDoFmt */
470 ULONG nbrep;
471 STRPTR occur;
472 STRPTR _find_, _replace_;
473 } args;
474 WORD replen;
475 LINE *edited = p->edited;
476 LONG nbl = p->nbl;
477 # define find args._find_
478 # define replace args._replace_
480 if( swin )
482 /* Window overrides SearchStr buffer */
483 if( rep != NULL && (find = GetSI(search)->Buffer)[0] ) {
484 replace = GetSI(rep)->Buffer;
485 replen = GetSI(rep)->NumChars;
486 goto replace_all;
489 else if( *SearchStr )
491 find = SearchStr;
492 replace = ReplaceStr;
493 replen = RLen;
494 replace_all: inv_curs(p, FALSE);
495 /* Replace all pattern */
496 p->edited = p->the_line;
497 p->nbc = p->nbl = 0;
498 if( HilitePattern(p, find, 0, QUIET_BUT_WARN) != 0 )
500 LINE *lastline = p->edited;
501 LONG nblast = p->nbl;
502 char retcode;
503 reg_group_by( &p->undo ); args.nbrep = 0;
504 do {
505 args.nbrep ++;
506 rem_chars(&p->undo,p->edited,p->nbc,p->nbc+(rep ? GetSI(search)->NumChars : SLen)-1);
507 /* Something to replace with? */
508 if( *replace ) {
509 LONG Buf[3];
510 add_string(&p->undo, p->edited, p->nbc, replace, replen, Buf);
511 /* Disable recursive replacement if search string **
512 ** can be found in this string */
513 p->nbc += replen;
515 retcode = HilitePattern(p, find, 0, FULLY_QUIET);
516 /* Show modifications to the line */
517 if((lastline != p->edited || retcode == 0) &&
518 p->top_line <= nblast && nblast < p->top_line+gui.nbline)
520 Move(RP,gui.left,(nblast-p->top_line)*YSIZE+gui.topcurs);
521 write_text(p, lastline);
522 lastline = p->edited;
523 nblast = p->nbl;
525 } while( retcode > 0 );
526 reg_group_by( &p->undo );
528 /* Show the number of pattern replaced */
529 args.occur = (args.nbrep > 1 ? SWinTxt[15] : SWinTxt[14]);
530 ThrowError(swin ? swin : Wnd, my_SPrintf(SWinTxt[13], &args));
532 /* Reset original cursor position */
533 p->edited = edited;
534 p->xcurs = gui.left + XSIZE * ((
535 p->nbrc = adjust_rc(edited, p->nbrc, &p->nbc, 0)) - p->left_pos);
536 p->nbl = nbl;
538 ULONG newleft;
539 if(p->left_pos != (newleft = center_horiz(p)))
540 scroll_xy(p, newleft, p->top_line, 0);
542 draw_info(p);
543 inv_curs(p, TRUE);
544 return;
547 /* No pattern entered! */
548 if( !p->ccp.select & setup_winsearch(p, 1) )
549 ThrowError(swin, ErrMsg(ERR_NOSEARCHTEXT));
552 /*** Hightlight next pattern ***/
553 char HilitePattern(Project p, STRPTR pattern, BYTE direction, BYTE quiet)
555 LINE *ln; register STRPTR search, pat;
556 WORD len; register LONG nb, nbl;
558 ln = p->edited;
559 search = ln->stream + p->nbc + direction;
560 pat = pattern;
561 len = strlen( pat );
562 nb = (direction>=0 ? ln->size - p->nbc - len + 1 : p->nbc);
563 nbl = p->nbl;
565 register BYTE dir = direction;
566 if(dir == 0) dir=1;
567 for(;;) {
568 finish_it:if(prefs.matchcase)
569 /* Matching case algorithm is a little bit **
570 ** simpler and therefore can be optimized */
571 for( ; nb>0; nb--, search+=dir)
573 /* This single instruction is executed thousand of times !! */
574 if( *search == *pat ) {
575 do {
576 pat++; search++;
577 } while( *pat && *pat==*search );
578 search -= pat-pattern;
579 if(*pat == 0) break;
580 pat = pattern;
583 else /* Non matching case algorithm */
584 for( ; nb>0; nb--, search+=dir)
586 if( CharClass[*search] == CharClass[*pat] ) {
587 do {
588 pat++; search++;
589 } while( *pat && CharClass[*pat] == CharClass[*search] );
590 search -= pat - pattern;
591 if(*pat == 0) break;
592 pat = pattern;
595 /* Something found ? */
596 if(nb > 0) {
597 if((prefs.wholewords == 0 || (TypeChar[ search[len] ] != ALPHA &&
598 (search == ln->stream || (TypeChar[ search[-1] ] != ALPHA)) )))
599 goto jump_cursor;
600 pat=pattern; nb--; search+=dir; goto finish_it;
603 /* Next line */
604 ln = (dir<0 ? ln->prev : ln->next); nbl+=dir;
605 if (ln) search = ln->stream, nb = ln->size-len+1;
606 else break;
607 if(dir < 0) search += ln->size-len;
610 if(quiet != FULLY_QUIET)
611 ThrowError(swin ? swin:Wnd, my_SPrintf(ErrMsg(ERR_NOT_FOUND), &pattern));
612 return 0;
614 jump_cursor:
615 /* Has cursor moved? */
616 len = (search == p->edited->stream + p->nbc ? 1:2);
618 if(quiet < FULLY_QUIET)
620 /* A pattern has been found, move cursor at beginning */
621 p->nbrwc = x2pos(ln,search - ln->stream);
623 /* Does the keyword remain on visible area? */
624 search = (UBYTE *)p->nbl; p->nbl = nbl;
625 nb = center_vert( p ); p->nbl = (LONG)search;
627 if(quiet != VERB_BUT_NO_HIDE_CURS) inv_curs(p,FALSE);
628 set_cursor_line(p, nbl, nb);
630 scroll_xy(p, center_horiz(p), nb, TRUE);
632 if(p->ccp.select) move_selection(p,p->nbrc,nbl);
633 inv_curs(p,TRUE);
635 /* Does the window recover the word? */
636 if( swin && swin->TopEdge-Wnd->TopEdge < p->ycurs-gui.basel+YSIZE &&
637 p->ycurs-gui.basel < swin->TopEdge+swin->Height-Wnd->TopEdge )
639 WORD newy;
640 /* Move it under the word found, if there is enough place */
641 if( Scr->Height - (newy = Wnd->TopEdge + p->ycurs + 10) >= swin->Height )
642 MoveWindow(swin, 0, newy-swin->TopEdge);
643 else
644 /* Otherwise, place it above the word found */
645 MoveWindow(swin, 0, newy-swin->Height-10-gui.ysize-swin->TopEdge);
647 } else {
648 p->edited = ln;
649 p->nbc = search - ln->stream;
650 p->nbl = nbl;
652 return (char)len;
655 /*** Ask the user for a line number to jump to ***/
656 void goto_line( Project p )
658 static LONG line_num;
660 if( get_number( p, GetMenuText(306), &line_num ) )
662 if(line_num < 0) line_num += p->max_lines;
663 else line_num --;
664 if(line_num < 0) line_num = 0;
665 if(line_num >= p->max_lines) line_num = p->max_lines-1;
667 move_to_line(p, line_num, LINE_AS_IS);
671 UBYTE Brackets[]="[{(<>)}] ";
673 #define NbBrak (sizeof(Brackets)-2)
675 /*** Search for the corresponding bracket under cursor ***/
676 void match_bracket( Project p )
678 BYTE i; UBYTE ch;
679 /* Look if character under cursor is registered */
680 for(i=NbBrak,ch=p->edited->stream[p->nbc]; i && Brackets[i-1] != ch; i--);
682 if(i==0) ThrowError(Wnd, ErrMsg(ERR_NOBRACKET));
683 else {
684 LINE *ln; register STRPTR search, cch;
685 WORD nest; register LONG nb, nbl;
687 /* Can't use HilitePattern because of nested char */
688 ln = p->edited;
689 cch = Brackets[ NbBrak-i ];
690 i = (i <= (NbBrak>>1) ? 1 : -1);
691 search = ln->stream + p->nbc + i;
692 nb = (i>0 ? ln->size - p->nbc - 1 : p->nbc);
693 nbl = p->nbl;
694 nest = 0;
696 for(;;) {
697 for( ; nb>0; nb--, search+=i)
699 if( *search == cch && !(nest--) ) goto jump_cursor;
700 if( *search == ch ) nest++;
703 /* Go next line */
704 ln = (i<0 ? ln->prev : ln->next); nbl+=i;
705 if (ln) search = ln->stream, nb = ln->size;
706 else break;
707 if(i < 0) search += ln->size-1;
709 { STRPTR Char = Brackets + NbBrak; *Char = cch;
710 ThrowError(Wnd, my_SPrintf(ErrMsg(ERR_NOT_FOUND), &Char));
711 } return;
713 jump_cursor:
714 /* A matching bracket has been found, move cursor to */
715 p->nbrwc = x2pos(ln,search - ln->stream);
717 /* Does the bracket remain on visible area? */
718 search = (UBYTE *)p->nbl; p->nbl = nbl;
719 nb = center_vert( p ); p->nbl = (LONG)search;
721 inv_curs(p,FALSE);
722 set_cursor_line(p, nbl, nb);
723 nbl = center_horiz(p);
724 if( nb != p->top_line || nbl != p->left_pos )
725 scroll_xy(p, nbl, nb, TRUE);
726 if(p->ccp.select) move_selection(p,p->nbrc,p->nbl);
727 inv_curs(p,TRUE);