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>
18 #include "ProtoTypes.h"
20 #define CATCOMP_NUMBERS
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
|
35 NULL
, /* FirstGadget */
40 100, 50, 0xffff, 0xffff, /* Min & Max size */
46 UBYTE SearchStr
[60], SLen
=0; /* Saved search and replace string */
47 UBYTE ReplaceStr
[60],RLen
=0;
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çÇ";
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
;
75 /** Special hook executed while editing a line **/
76 ULONG
CheckConfirm(struct Hook
*hook
, struct SGWork
*sgw
, ULONG
*msg
)
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);
88 static struct Hook hook
;
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
[] = {
97 GTST_MaxChars
, sizeof(SearchStr
)-1,
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
);
109 /** Window already opened? **/
111 ActivateWindow(swin
);
113 /* Uniconified it, if needed */
114 if(swin
->Height
<=swin
->BorderTop
) ZipWindow(swin
);
115 /* Copy selected part of text in string gadget */
117 GetSI(search
)->BufferPos
= copy_mark_to_buf(p
,GetSI(search
)->Buffer
,GetSI(search
)->MaxChars
-1),
118 unmark_all(p
,FALSE
), inv_curs(p
,TRUE
);
119 ActivateGadget(search
, swin
, NULL
);
123 /** Compute optimal window width **/
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 */
135 if(sum
< (320-50)/4) sum
= (320-50)/4;
138 } else len
= sum
+ 70;
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;
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
,
163 WA_NewLookMenus
,TRUE
,
164 WA_InnerWidth
, innerWidth
,
165 WA_InnerHeight
, innerHeight
,
169 rep
= search
= sgads
= NULL
;
170 sg
= (void *) CreateContext(&sgads
);
171 swin
->UserData
= NewSWin
.Title
;
174 /* Init strgad hook (v37+ only) */
175 hook
.h_Entry
= HookEntry
;
176 hook
.h_SubEntry
= (HOOKFUNC
)CheckConfirm
;
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]))) )
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];
191 search
= sg
= (void *)
193 CreateGadget(STRING_KIND
, sg
, &NG
, GTST_EditHook
, (ULONG
)&hook
, TAG_MORE
, (ULONG
)(SGadTags
+2));
195 CreateGadgetA(STRING_KIND
, sg
, &NG
, (ULONG
)(SGadTags
+2));
197 /** If user has selected part of text, copy it in the `Search' string gadget **/
199 GetSI(sg
)->BufferPos
= copy_mark_to_buf(p
,GetSI(sg
)->Buffer
,GetSI(sg
)->MaxChars
-1),
200 unmark_all(p
,FALSE
), inv_curs(p
,TRUE
);
202 /* Copy the previous search string */
203 CopyMem(SearchStr
, GetSI(search
)->Buffer
, (GetSI(search
)->BufferPos
= SLen
) + 1);
205 /** `Replace' string gadget **/
208 NG
.ng_TopEdge
+= NG
.ng_Height
+2;
209 NG
.ng_GadgetText
= SWinTxt
[3];
213 CreateGadget(STRING_KIND
, sg
, &NG
, GTST_EditHook
, (ULONG
)&hook
, TAG_MORE
, (ULONG
)(SGadTags
+2));
215 CreateGadgetA(STRING_KIND
, sg
, &NG
, (ULONG
)(SGadTags
+2));
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
];
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) **/
236 NG
.ng_Flags
= PLACETEXT_IN
;
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
];
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
;
259 } else ThrowError(Wnd
, ErrMsg(ERR_NOMEM
));
263 /*** Free all allocated ressources ***/
264 void close_searchwnd(BYTE 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
);
275 swinsig
=0; swin
=NULL
; rep
=NULL
;
277 if(sgads
) FreeGadgets(sgads
);
281 /*** Handle messages coming from gadgets ***/
282 BYTE
handle_swingadget(struct Gadget
*G
)
285 switch( G
->GadgetID
)
289 if( msgbuf
.Code
!= 1 ) break;
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
);
299 (&prefs
.matchcase
)[G
->GadgetID
-4] = ((G
->Flags
& GFLG_SELECTED
) == GFLG_SELECTED
);
302 if( GetSI(search
)->Buffer
[0] ) ReplacePattern( edit
);
305 if( GetSI(search
)->Buffer
[0] ) ReplaceAllPat( edit
);
308 if( GetSI(search
)->Buffer
[0] )
309 HilitePattern(edit
, GetSI(search
)->Buffer
, -1, VERBOSE
);
311 case 10: close_searchwnd(TRUE
); return TRUE
;
312 case 11: close_searchwnd(FALSE
); return TRUE
;
317 /*** Keyboard handler ***/
318 BYTE
handle_swinkey( UBYTE 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;
331 if( GetSI(search
)->Buffer
[0] == 0 ) break;
335 case 1: HilitePattern(edit
, GetSI(search
)->Buffer
, Key
-2, VERBOSE
); break;
336 case 2: ReplacePattern(edit
); break;
337 default:ReplaceAllPat(edit
);
340 case '/': case 'f': case 'F':
341 ActivateGadget(search
, swin
, NULL
); break;
343 if(rep
) ActivateGadget(rep
, swin
, NULL
);
348 /*** Handle events coming from search window ***/
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
));
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;
366 if( handle_swingadget((struct Gadget
*)msgbuf
.IAddress
) ) return;
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
);
377 /*** Find next matching keyword ***/
378 void FindPattern( Project p
, BYTE dir
)
382 /* Window overrides SearchStr buffer */
383 if( GetSI(search
)->Buffer
[0] )
385 HilitePattern(p
, GetSI(search
)->Buffer
, dir
, VERBOSE
);
391 HilitePattern(p
, SearchStr
, dir
, VERBOSE
);
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 */
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
;
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
;
438 else if( *SearchStr
)
440 if(HilitePattern(p
, SearchStr
, 0, VERBOSE
) == 1)
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? */
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
;
456 /* Hilight next pattern */
457 if( !HilitePattern(p
, rep
? GetSI(search
)->Buffer
: SearchStr
, 0, VERBOSE
) )
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 */
472 STRPTR _find_
, _replace_
;
475 LINE
*edited
= p
->edited
;
477 # define find args._find_
478 # define replace args._replace_
482 /* Window overrides SearchStr buffer */
483 if( rep
!= NULL
&& (find
= GetSI(search
)->Buffer
)[0] ) {
484 replace
= GetSI(rep
)->Buffer
;
485 replen
= GetSI(rep
)->NumChars
;
489 else if( *SearchStr
)
492 replace
= ReplaceStr
;
494 replace_all
: inv_curs(p
, FALSE
);
495 /* Replace all pattern */
496 p
->edited
= p
->the_line
;
498 if( HilitePattern(p
, find
, 0, QUIET_BUT_WARN
) != 0 )
500 LINE
*lastline
= p
->edited
;
501 LONG nblast
= p
->nbl
;
503 reg_group_by( &p
->undo
); args
.nbrep
= 0;
506 rem_chars(&p
->undo
,p
->edited
,p
->nbc
,p
->nbc
+(rep
? GetSI(search
)->NumChars
: SLen
)-1);
507 /* Something to replace with? */
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 */
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
;
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 */
534 p
->xcurs
= gui
.left
+ XSIZE
* ((
535 p
->nbrc
= adjust_rc(edited
, p
->nbrc
, &p
->nbc
, 0)) - p
->left_pos
);
539 if(p
->left_pos
!= (newleft
= center_horiz(p
)))
540 scroll_xy(p
, newleft
, p
->top_line
, 0);
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
;
559 search
= ln
->stream
+ p
->nbc
+ direction
;
562 nb
= (direction
>=0 ? ln
->size
- p
->nbc
- len
+ 1 : p
->nbc
);
565 register BYTE dir
= direction
;
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
) {
577 } while( *pat
&& *pat
==*search
);
578 search
-= pat
-pattern
;
583 else /* Non matching case algorithm */
584 for( ; nb
>0; nb
--, search
+=dir
)
586 if( CharClass
[*search
] == CharClass
[*pat
] ) {
589 } while( *pat
&& CharClass
[*pat
] == CharClass
[*search
] );
590 search
-= pat
- pattern
;
595 /* Something found ? */
597 if((prefs
.wholewords
== 0 || (TypeChar
[ search
[len
] ] != ALPHA
&&
598 (search
== ln
->stream
|| (TypeChar
[ search
[-1] ] != ALPHA
)) )))
600 pat
=pattern
; nb
--; search
+=dir
; goto finish_it
;
604 ln
= (dir
<0 ? ln
->prev
: ln
->next
); nbl
+=dir
;
605 if (ln
) search
= ln
->stream
, nb
= ln
->size
-len
+1;
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
));
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
);
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
)
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
);
644 /* Otherwise, place it above the word found */
645 MoveWindow(swin
, 0, newy
-swin
->Height
-10-gui
.ysize
-swin
->TopEdge
);
649 p
->nbc
= search
- ln
->stream
;
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
;
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
)
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
));
684 LINE
*ln
; register STRPTR search
, cch
;
685 WORD nest
; register LONG nb
, nbl
;
687 /* Can't use HilitePattern because of nested char */
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
);
697 for( ; nb
>0; nb
--, search
+=i
)
699 if( *search
== cch
&& !(nest
--) ) goto jump_cursor
;
700 if( *search
== ch
) nest
++;
704 ln
= (i
<0 ? ln
->prev
: ln
->next
); nbl
+=i
;
705 if (ln
) search
= ln
->stream
, nb
= ln
->size
;
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
));
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
;
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
);