1 /***************************************************************************
3 TextEditor.mcc - Textediting MUI Custom Class
4 Copyright (C) 1997-2000 Allan Odgaard
5 Copyright (C) 2005-2014 TextEditor.mcc Open Source Team
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Lesser General Public
9 License as published by the Free Software Foundation; either
10 version 2.1 of the License, or (at your option) any later version.
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 TextEditor class Support Site: http://www.sf.net/projects/texteditor-mcc
21 ***************************************************************************/
23 #include <clib/alib_protos.h>
24 #include <proto/graphics.h>
25 #include <proto/intuition.h>
26 #include <proto/locale.h>
34 LONG
FlowSpace(struct InstData
*data
, UWORD flow
, STRPTR text
)
40 if(flow
!= MUIV_TextEditor_Flow_Left
)
42 flowspace
= (_mwidth(data
->object
)-TextLengthNew(&data
->tmprp
, text
, LineCharsWidth(data
, text
)-1, data
->TabSizePixels
));
43 flowspace
-= (data
->CursorWidth
== 6) ? TextLength(&data
->tmprp
, " ", 1) : data
->CursorWidth
;
44 if(flow
== MUIV_TextEditor_Flow_Center
)
56 static LONG
CursorOffset(struct InstData
*data
)
58 struct line_node
*line
= data
->actualline
;
59 STRPTR text
= &line
->line
.Contents
[data
->CPos_X
];
65 // call TextFitNew() to find out how many chars would fit.
66 if((lineCharsWidth
= LineCharsWidth(data
, text
)) > 0)
68 struct TextExtent tExtend
;
69 LONG offset
= data
->pixel_x
-FlowSpace(data
, line
->line
.Flow
, text
);
74 res
= TextFitNew(&data
->tmprp
, text
, lineCharsWidth
, &tExtend
, NULL
, 1, offset
, data
->font
->tf_YSize
, data
->TabSizePixels
);
76 // in case of a hard-wrapped line we have to deal with
77 // the possibility that the user tries to
78 // select the last char in a line which should normally by a white space
79 // due to soft-wrapping. So in this case we have to lower res by one so
80 // that it is not possible to select that last white space. However, for
81 // a hard wrapped line it still have to be possible to select that last char.
82 if(lineCharsWidth
-res
== 0 && text
[lineCharsWidth
-1] <= ' ')
92 /*---------------------------------------*
93 * Return the number of pixels to cursor *
94 *---------------------------------------*/
95 static LONG
GetPosInPixels(struct InstData
*data
, LONG bytes
, LONG x
)
101 pos
= TextLengthNew(&data
->tmprp
, &data
->actualline
->line
.Contents
[bytes
], x
, data
->TabSizePixels
);
103 if(data
->actualline
->line
.Contents
[data
->CPos_X
] == '\n')
104 pos
+= TextLength(&data
->tmprp
, " ", 1)/2;
106 pos
+= TextLengthNew(&data
->tmprp
, &data
->actualline
->line
.Contents
[data
->CPos_X
], 1, data
->TabSizePixels
)/2;
108 pos
+= FlowSpace(data
, data
->actualline
->line
.Flow
, &data
->actualline
->line
.Contents
[bytes
]);
116 void SetBookmark(struct InstData
*data
, ULONG nr
)
120 if(nr
< ARRAY_SIZE(data
->bookmarks
))
122 data
->bookmarks
[nr
].line
= data
->actualline
;
123 data
->bookmarks
[nr
].x
= data
->CPos_X
;
131 void GotoBookmark(struct InstData
*data
, ULONG nr
)
135 if(nr
< ARRAY_SIZE(data
->bookmarks
))
137 if(data
->bookmarks
[nr
].line
)
139 struct line_node
*actual
= GetFirstLine(&data
->linelist
);
141 while(actual
!= NULL
)
143 if(actual
== data
->bookmarks
[nr
].line
)
145 data
->actualline
= actual
;
146 data
->CPos_X
= data
->bookmarks
[nr
].x
;
147 if(data
->CPos_X
>= actual
->line
.Length
)
148 data
->CPos_X
= actual
->line
.Length
-1;
151 actual
= GetNextLine(actual
);
155 DoMethod(data
->object
, MUIM_TextEditor_HandleError
, Error_BookmarkHasBeenLost
);
160 DoMethod(data
->object
, MUIM_TextEditor_HandleError
, Error_NoBookmarkInstalled
);
169 /*---- CursorUp ---- */
170 void GoTop(struct InstData
*data
)
174 data
->actualline
= GetFirstLine(&data
->linelist
);
182 void GoPreviousLine(struct InstData
*data
)
186 if(data
->CPos_X
== 0 && HasPrevLine(data
->actualline
) == TRUE
)
187 data
->actualline
= GetPrevLine(data
->actualline
);
195 void GoPreviousPage(struct InstData
*data
)
199 if(data
->shown
== TRUE
)
201 OffsetToLines(data
, data
->CPos_X
, data
->actualline
, &pos
);
202 if(data
->pixel_x
== 0)
203 data
->pixel_x
= GetPosInPixels(data
, pos
.bytes
, pos
.x
);
204 if(!(HasPrevLine(data
->actualline
) == FALSE
&& pos
.lines
== 1))
207 LONG linemove
= data
->maxlines
;
208 struct line_node
*prev
;
210 lineplacement
= LineToVisual(data
, data
->actualline
)+pos
.lines
-1;
211 if(lineplacement
!= 1)
212 linemove
= lineplacement
-1;
213 linemove
-= pos
.lines
-1;
215 while(linemove
!= 0 && (prev
= GetPrevLine(data
->actualline
)) != NULL
&& prev
->visual
<= linemove
)
217 data
->actualline
= prev
;
218 linemove
-= data
->actualline
->visual
;
221 if(linemove
!= 0 && HasPrevLine(data
->actualline
) == TRUE
)
225 data
->actualline
= GetPrevLine(data
->actualline
);
226 linemove
= data
->actualline
->visual
-linemove
;
230 linemove
= -linemove
;
234 data
->CPos_X
+= LineCharsWidth(data
, &data
->actualline
->line
.Contents
[data
->CPos_X
]);
237 data
->CPos_X
+= CursorOffset(data
);
250 void GoUp(struct InstData
*data
)
254 if(data
->shown
== TRUE
)
256 OffsetToLines(data
, data
->CPos_X
, data
->actualline
, &pos
);
257 if(data
->pixel_x
== 0)
259 data
->pixel_x
= GetPosInPixels(data
, pos
.bytes
, pos
.x
);
263 if(HasPrevLine(data
->actualline
) == TRUE
)
265 data
->actualline
= GetPrevLine(data
->actualline
);
266 pos
.lines
= data
->actualline
->visual
+1;
280 data
->CPos_X
+= LineCharsWidth(data
, &data
->actualline
->line
.Contents
[data
->CPos_X
]);
282 data
->CPos_X
+= CursorOffset(data
);
293 /*---- CursorDown ---- */
294 void GoBottom(struct InstData
*data
)
298 data
->actualline
= GetLastLine(&data
->linelist
);
299 data
->CPos_X
= data
->actualline
->line
.Length
-1;
306 void GoNextLine(struct InstData
*data
)
311 if(HasNextLine(data
->actualline
) == TRUE
)
312 data
->actualline
= GetNextLine(data
->actualline
);
314 data
->CPos_X
= data
->actualline
->line
.Length
-1;
321 void GoNextPage(struct InstData
*data
)
325 if(data
->shown
== TRUE
)
327 OffsetToLines(data
, data
->CPos_X
, data
->actualline
, &pos
);
328 if(data
->pixel_x
== 0)
329 data
->pixel_x
= GetPosInPixels(data
, pos
.bytes
, pos
.x
);
330 if(!(HasNextLine(data
->actualline
) == FALSE
&& pos
.lines
== data
->actualline
->visual
))
333 LONG linemove
= data
->maxlines
;
335 lineplacement
= LineToVisual(data
, data
->actualline
)+pos
.lines
-1;
336 if(lineplacement
!= data
->maxlines
)
337 linemove
= data
->maxlines
- lineplacement
;
339 linemove
+= pos
.lines
-1;
340 while(linemove
!= 0 && HasNextLine(data
->actualline
) == TRUE
&& data
->actualline
->visual
<= linemove
)
342 linemove
-= data
->actualline
->visual
;
345 if(data
->actualline
->visual
<= linemove
)
346 linemove
= data
->actualline
->visual
-1;
350 OffsetToLines(data
, data
->CPos_X
, data
->actualline
, &pos
);
351 data
->CPos_X
= pos
.extra
;
353 data
->CPos_X
+= CursorOffset(data
);
366 void GoDown (struct InstData
*data
)
370 if(data
->shown
== TRUE
)
372 OffsetToLines(data
, data
->CPos_X
, data
->actualline
, &pos
);
373 if(data
->pixel_x
== 0)
375 data
->pixel_x
= GetPosInPixels(data
, pos
.bytes
, pos
.x
);
378 if(pos
.lines
== data
->actualline
->visual
)
380 struct line_node
*next
= GetNextLine(data
->actualline
);
385 data
->actualline
= next
;
387 data
->CPos_X
+= CursorOffset(data
);
391 // data->CPos_X = data->actualline->line.Length-1;
396 data
->CPos_X
= pos
.extra
;
397 data
->CPos_X
+= CursorOffset(data
);
406 /*---- CursorRight ---- */
407 void GoEndOfLine(struct InstData
*data
)
411 if(data
->shown
== TRUE
)
415 OffsetToLines(data
, data
->CPos_X
, data
->actualline
, &pos
);
417 if((c
= LineCharsWidth(data
, &data
->actualline
->line
.Contents
[pos
.bytes
])) > 0)
418 data
->CPos_X
= pos
.bytes
+ c
- 1;
420 data
->CPos_X
= pos
.bytes
;
428 void GoNextWord(struct InstData
*data
)
432 while(data
->CPos_X
< data
->actualline
->line
.Length
&& CheckSep(data
, data
->actualline
->line
.Contents
[data
->CPos_X
]) == FALSE
)
437 while(data
->CPos_X
< data
->actualline
->line
.Length
&& CheckSep(data
, data
->actualline
->line
.Contents
[data
->CPos_X
]) == TRUE
)
440 if(data
->CPos_X
== data
->actualline
->line
.Length
)
442 struct line_node
*next
= GetNextLine(data
->actualline
);
446 data
->actualline
= next
;
459 void GoNextSentence(struct InstData
*data
)
463 while(data
->CPos_X
< data
->actualline
->line
.Length
&& CheckSent(data
, data
->actualline
->line
.Contents
[data
->CPos_X
]) == FALSE
)
465 while(data
->CPos_X
< data
->actualline
->line
.Length
&& CheckSent(data
, data
->actualline
->line
.Contents
[data
->CPos_X
]) == TRUE
)
467 if(data
->CPos_X
>= data
->actualline
->line
.Length
-1)
471 while(data
->CPos_X
< data
->actualline
->line
.Length
&& data
->actualline
->line
.Contents
[data
->CPos_X
] == ' ')
473 if(data
->CPos_X
== data
->actualline
->line
.Length
-1)
482 void GoRight(struct InstData
*data
)
486 if(data
->CPos_X
+1 < data
->actualline
->line
.Length
)
496 void GoStartOfLine(struct InstData
*data
)
500 if(data
->shown
== TRUE
)
502 OffsetToLines(data
, data
->CPos_X
, data
->actualline
, &pos
);
503 data
->CPos_X
= pos
.bytes
;
511 void GoPreviousWord(struct InstData
*data
)
514 struct line_node
*prev
;
520 if(data
->CPos_X
> 0 && CheckSep(data
, data
->actualline
->line
.Contents
[data
->CPos_X
-1]) == TRUE
)
525 while(data
->CPos_X
> 0 && CheckSep(data
, data
->actualline
->line
.Contents
[data
->CPos_X
]) == TRUE
)
530 prev
= GetPrevLine(data
->actualline
);
531 if(data
->CPos_X
== 0 && prev
!= NULL
&& (moved
== FALSE
|| CheckSep(data
, data
->actualline
->line
.Contents
[0]) == TRUE
))
533 data
->actualline
= prev
;
534 data
->CPos_X
= data
->actualline
->line
.Length
-1;
538 while(data
->CPos_X
> 0 && CheckSep(data
, data
->actualline
->line
.Contents
[data
->CPos_X
-1]) == FALSE
)
545 /// GoPreviousSentence()
546 void GoPreviousSentence(struct InstData
*data
)
548 struct line_node
*prev
;
552 prev
= GetPrevLine(data
->actualline
);
553 while(data
->CPos_X
== 0 && prev
!= NULL
)
555 data
->actualline
= prev
;
556 data
->CPos_X
= data
->actualline
->line
.Length
-1;
557 prev
= GetPrevLine(prev
);
559 if(data
->CPos_X
!= 0)
562 while(data
->CPos_X
> 0 && data
->actualline
->line
.Contents
[data
->CPos_X
] == ' ')
564 while(data
->CPos_X
> 0 && CheckSent(data
, data
->actualline
->line
.Contents
[data
->CPos_X
]) == TRUE
)
566 while(data
->CPos_X
> 0 && CheckSent(data
, data
->actualline
->line
.Contents
[data
->CPos_X
]) == FALSE
)
571 while(data
->CPos_X
< data
->actualline
->line
.Length
&& data
->actualline
->line
.Contents
[data
->CPos_X
] == ' ')
581 void GoLeft(struct InstData
*data
)
589 struct line_node
*prev
= GetPrevLine(data
->actualline
);
593 data
->actualline
= prev
;
594 data
->CPos_X
= data
->actualline
->line
.Length
-1;
603 /*-----------------------------------------*
604 * Check if given char is a word-seperator *
605 *-----------------------------------------*/
606 BOOL
CheckSep(struct InstData
*data
, char character
)
612 if(!IsAlNum(data
->mylocale
, (long)character
))
621 /*-----------------------------------------*
622 * Check if given char is a sentence ender *
623 *-----------------------------------------*/
624 BOOL
CheckSent(UNUSED
struct InstData
*data
, char character
)
630 if(character
== '.' || character
== '!' || character
== '?')
639 /*-----------------------------------*
640 * Move cursor to start of next line *
641 *-----------------------------------*/
642 void NextLine(struct InstData
*data
)
644 struct line_node
*next
;
648 next
= GetNextLine(data
->actualline
);
651 data
->actualline
= next
;
656 data
->CPos_X
= data
->actualline
->line
.Length
-1;
664 /*-----------------------------------------*
665 * Place the cursor, based on an X Y coord *
666 *-----------------------------------------*/
667 void PosFromCursor(struct InstData
*data
, LONG MouseX
, LONG MouseY
)
670 LONG limit
= data
->ypos
;
674 if(data
->maxlines
< data
->totallines
-data
->visual_y
+1)
675 limit
+= (data
->maxlines
* data
->fontheight
);
677 limit
+= (data
->totallines
-data
->visual_y
+1)*data
->fontheight
;
682 GetLine(data
, ((MouseY
- data
->ypos
)/data
->fontheight
) + data
->visual_y
, &pos
);
684 data
->actualline
= pos
.line
;
686 data
->pixel_x
= MouseX
-_mleft(data
->object
)+1;
688 if(data
->pixel_x
< 1)
691 data
->CPos_X
= pos
.x
;
692 data
->CPos_X
+= CursorOffset(data
);