1 /********************************************************************************
2 *                                                                               *
3 *                   M u l t i - L i n e   T e x t   W i d g e t                 *
4 *                                                                               *
5 *********************************************************************************
6 * Copyright (C) 1998,2021 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 "fxascii.h"
27 #include "fxunicode.h"
28 #include "FXColors.h"
29 #include "FXArray.h"
30 #include "FXHash.h"
31 #include "FXMutex.h"
32 #include "FXStream.h"
33 #include "FXString.h"
34 #include "FXElement.h"
35 #include "FXException.h"
36 #include "FXRex.h"
37 #include "FXSize.h"
38 #include "FXPoint.h"
39 #include "FXRectangle.h"
40 #include "FXObject.h"
41 #include "FXStringDictionary.h"
42 #include "FXSettings.h"
43 #include "FXRegistry.h"
44 #include "FXAccelTable.h"
45 #include "FXFont.h"
46 #include "FXEvent.h"
47 #include "FXWindow.h"
48 #include "FXDCWindow.h"
49 #include "FXApp.h"
50 #include "FXGIFIcon.h"
51 #include "FXScrollBar.h"
52 #include "FXText.h"
53 #include "FXComposeContext.h"
54 #include "icons.h"
55 
56 
57 
58 /*
59   Notes:
60   - Generally, assume the following definitions in terms of how things work:
61 
62       position  Character position in the buffer; should avoid pointing to
63                 places other than the start of a UTF8 character.
64       indent    logical character-index (not byte index) from the start of
65                 a line.
66       line      A newline terminated sequence of characters. A line may be wrapped
67                 to multiple rows on the screen.
68       row       Sequence of characters wrapped at the wrap-margin, therefore not
69                 necessarily ending at a newline
70       column    Logical column from start of the line.
71 
72   - Line start array is one longer than number of visible lines; therefore,
73     the length of each visible line is just visrows[i+1]-visrows[i].
74   - Control characters in the buffer are allowed, and are represented as sequences
75     like '^L'.
76   - Wrapped lines contain at least 1 character; this is necessary in order to ensure
77     the text widget has finite number of rows.
78   - Viewport definition is established with virtual functions; overloadinging them
79     in subclasses allows for custom items in subclassed FXText widgets.
80 
81       +------------------------------------------------+<-- 0
82       |                                                |
83       +----+--------------------------------------+----+<-- getVisibleY()
84       |    |                                      |    |
85       |    |           T e x t                    |    |
86       |    |                                      |    |
87       |    |                                      |    |
88       +----+--------------------------------------+----+<-- getVisibleHeight()
89       |                                                |
90       +------------------------------------------------+<-- height
91       ^    ^                                      ^    ^
92       |    |                                      |    |
93       0    |                                      |    width
94          getVisibleX()             getVisibleWidth()
95 
96   - Buffer layout:
97 
98       Content  :  A  B  C  .  .  .  .  .  .  .  .  D  E  F  G
99       Position :  0  1  2                          3  4  5  6    length=7
100       Addresss :  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14    buffersize=7+11-3=15
101                            ^                       ^
102                            |                       |
103                            gapstart=3              gapend=11     gaplen=11-3=8
104 
105     The gap is moved around the buffer so newly added text can be entered into the gap;
106     when the gap becomes too small, the buffer is resized.
107     This gapped-buffer technique minimizes the number of resizes of the buffer, and
108     minimizes the number of block moves.
109 
110     The tail end of the visrows array will look like:
111 
112       visrow[0]= 0: "Q R S T U V W \n"
113       visrow[1]= 8: "X Y Z"
114       visrow[2]=11: <no text>
115       visrow[3]=11: <no text>            length = 11
116 
117     The last legal position is length = 11.
118 
119   - While resizing window, we keep track of a position which should remain visible at the
120     top of the visible buffer (keeppos).  Due to wrapping, the exact value of toppos may
121     change but its always the case that keeppos is visible.
122   - When changing text, try keep the top part of the visible buffer stationary, to avoid
123     jumping text while typing.
124   - If there is a style table, the style buffer is used as index into the style table,
125     allowing for up to 255 styles (style index==0 is the default style).
126     The style member in the FXHiliteStyle struct is used for underlining, strikeouts,
127     and other effects.
128   - Italic fonts are bit problematic on border between selected/unselected text
129     due to kerning.
130   - Possibly split off buffer management into separate text buffer class (allows for
131     multiple views).
132   - Maybe put all keyboard bindings into accelerator table.
133   - Perhaps change text and style buffer to FXString for further complexity reduction.
134   - When in overstrike mode and having a selection, entering a character should
135     replace the selection, not delete the selection and then overstrike the character
136     after the selection.
137   - When pasting or dropping whole lines, insert at begin of line instead of at cursor;
138     question:- how to know we're pasting whole lines?
139   - Inserting lots of stuff should show cursor.
140   - For now, right, top, and bottom bars are zero; subclasses may override
141     and add space for text annotations.
142   - Possible (minor) improvement to wrap(): don't break after space unless
143     at least non-space was seen before that space.  This will cause a line
144     to have at least some non-blank characters on it.
145 */
146 
147 #define TOPIC_KEYBOARD  1009
148 #define TOPIC_TEXT      1012
149 #define TOPIC_LAYOUT    1013
150 
151 #define MINSIZE         100             // Minimum gap size
152 #define MAXSIZE         4000            // Minimum gap size
153 #define NVISROWS        20              // Initial visible rows
154 #define MAXTABCOLUMNS   32              // Maximum tab column setting
155 
156 #define TEXT_MASK       (TEXT_FIXEDWRAP|TEXT_WORDWRAP|TEXT_OVERSTRIKE|TEXT_READONLY|TEXT_NO_TABS|TEXT_AUTOINDENT|TEXT_SHOWACTIVE|TEXT_SHOWMATCH)
157 
158 #define CC(x,in)        (((x)=='\t')?tabcolumns-in%tabcolumns:1)        // Count Columns
159 
160 using namespace FX;
161 
162 /*******************************************************************************/
163 
164 namespace FX {
165 
166 
167 // Furnish our own version
168 extern FXAPI FXint __snprintf(FXchar* string,FXint length,const FXchar* format,...);
169 
170 
171 // Map
172 FXDEFMAP(FXText) FXTextMap[]={
173   FXMAPFUNC(SEL_PAINT,0,FXText::onPaint),
174   FXMAPFUNC(SEL_MOTION,0,FXText::onMotion),
175   FXMAPFUNC(SEL_DRAGGED,0,FXText::onDragged),
176   FXMAPFUNC(SEL_ENTER,0,FXText::onEnter),
177   FXMAPFUNC(SEL_LEAVE,0,FXText::onLeave),
178   FXMAPFUNC(SEL_TIMEOUT,FXText::ID_BLINK,FXText::onBlink),
179   FXMAPFUNC(SEL_TIMEOUT,FXText::ID_FLASH,FXText::onFlash),
180   FXMAPFUNC(SEL_TIMEOUT,FXText::ID_TIPTIMER,FXText::onTipTimer),
181   FXMAPFUNC(SEL_TIMEOUT,FXText::ID_AUTOSCROLL,FXText::onAutoScroll),
182   FXMAPFUNC(SEL_FOCUSIN,0,FXText::onFocusIn),
183   FXMAPFUNC(SEL_FOCUSOUT,0,FXText::onFocusOut),
184   FXMAPFUNC(SEL_BEGINDRAG,0,FXText::onBeginDrag),
185   FXMAPFUNC(SEL_ENDDRAG,0,FXText::onEndDrag),
186   FXMAPFUNC(SEL_LEFTBUTTONPRESS,0,FXText::onLeftBtnPress),
187   FXMAPFUNC(SEL_LEFTBUTTONRELEASE,0,FXText::onLeftBtnRelease),
188   FXMAPFUNC(SEL_MIDDLEBUTTONPRESS,0,FXText::onMiddleBtnPress),
189   FXMAPFUNC(SEL_MIDDLEBUTTONRELEASE,0,FXText::onMiddleBtnRelease),
190   FXMAPFUNC(SEL_RIGHTBUTTONPRESS,0,FXText::onRightBtnPress),
191   FXMAPFUNC(SEL_RIGHTBUTTONRELEASE,0,FXText::onRightBtnRelease),
192   FXMAPFUNC(SEL_UNGRABBED,0,FXText::onUngrabbed),
193   FXMAPFUNC(SEL_DND_ENTER,0,FXText::onDNDEnter),
194   FXMAPFUNC(SEL_DND_LEAVE,0,FXText::onDNDLeave),
195   FXMAPFUNC(SEL_DND_DROP,0,FXText::onDNDDrop),
196   FXMAPFUNC(SEL_DND_MOTION,0,FXText::onDNDMotion),
197   FXMAPFUNC(SEL_DND_REQUEST,0,FXText::onDNDRequest),
198   FXMAPFUNC(SEL_SELECTION_LOST,0,FXText::onSelectionLost),
199   FXMAPFUNC(SEL_SELECTION_GAINED,0,FXText::onSelectionGained),
200   FXMAPFUNC(SEL_SELECTION_REQUEST,0,FXText::onSelectionRequest),
201   FXMAPFUNC(SEL_CLIPBOARD_LOST,0,FXText::onClipboardLost),
202   FXMAPFUNC(SEL_CLIPBOARD_GAINED,0,FXText::onClipboardGained),
203   FXMAPFUNC(SEL_CLIPBOARD_REQUEST,0,FXText::onClipboardRequest),
204   FXMAPFUNC(SEL_KEYPRESS,0,FXText::onKeyPress),
205   FXMAPFUNC(SEL_KEYRELEASE,0,FXText::onKeyRelease),
206   FXMAPFUNC(SEL_QUERY_TIP,0,FXText::onQueryTip),
207   FXMAPFUNC(SEL_QUERY_HELP,0,FXText::onQueryHelp),
208   FXMAPFUNC(SEL_IME_START,0,FXText::onIMEStart),
209   FXMAPFUNC(SEL_UPDATE,FXText::ID_TOGGLE_EDITABLE,FXText::onUpdToggleEditable),
210   FXMAPFUNC(SEL_UPDATE,FXText::ID_TOGGLE_OVERSTRIKE,FXText::onUpdToggleOverstrike),
211   FXMAPFUNC(SEL_UPDATE,FXText::ID_CURSOR_ROW,FXText::onUpdCursorRow),
212   FXMAPFUNC(SEL_UPDATE,FXText::ID_CURSOR_COLUMN,FXText::onUpdCursorColumn),
213   FXMAPFUNC(SEL_UPDATE,FXText::ID_CUT_SEL,FXText::onUpdHaveEditableSelection),
214   FXMAPFUNC(SEL_UPDATE,FXText::ID_COPY_SEL,FXText::onUpdHaveSelection),
215   FXMAPFUNC(SEL_UPDATE,FXText::ID_PASTE_SEL,FXText::onUpdIsEditable),
216   FXMAPFUNC(SEL_UPDATE,FXText::ID_DELETE_SEL,FXText::onUpdHaveEditableSelection),
217   FXMAPFUNC(SEL_UPDATE,FXText::ID_CLEAN_INDENT,FXText::onUpdHaveEditableSelection),
218   FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_TOP,FXText::onCmdCursorTop),
219   FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_BOTTOM,FXText::onCmdCursorBottom),
220   FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_HOME,FXText::onCmdCursorHome),
221   FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_END,FXText::onCmdCursorEnd),
222   FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_UP,FXText::onCmdCursorUp),
223   FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_DOWN,FXText::onCmdCursorDown),
224   FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_LEFT,FXText::onCmdCursorLeft),
225   FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_RIGHT,FXText::onCmdCursorRight),
226   FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_PAGEUP,FXText::onCmdCursorPageUp),
227   FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_PAGEDOWN,FXText::onCmdCursorPageDown),
228   FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_WORD_LEFT,FXText::onCmdCursorWordLeft),
229   FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_WORD_RIGHT,FXText::onCmdCursorWordRight),
230   FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_SHIFT_TOP,FXText::onCmdCursorShiftTop),
231   FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_SHIFT_BOTTOM,FXText::onCmdCursorShiftBottom),
232   FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_SHIFT_HOME,FXText::onCmdCursorShiftHome),
233   FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_SHIFT_END,FXText::onCmdCursorShiftEnd),
234   FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_SHIFT_UP,FXText::onCmdCursorShiftUp),
235   FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_SHIFT_DOWN,FXText::onCmdCursorShiftDown),
236   FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_SHIFT_LEFT,FXText::onCmdCursorShiftLeft),
237   FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_SHIFT_RIGHT,FXText::onCmdCursorShiftRight),
238   FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_SHIFT_PAGEUP,FXText::onCmdCursorShiftPageUp),
239   FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_SHIFT_PAGEDOWN,FXText::onCmdCursorShiftPageDown),
240   FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_SHIFT_WORD_LEFT,FXText::onCmdCursorShiftWordLeft),
241   FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_SHIFT_WORD_RIGHT,FXText::onCmdCursorShiftWordRight),
242   FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_ALT_UP,FXText::onCmdCursorAltUp),
243   FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_ALT_DOWN,FXText::onCmdCursorAltDown),
244   FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_ALT_LEFT,FXText::onCmdCursorAltLeft),
245   FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_ALT_RIGHT,FXText::onCmdCursorAltRight),
246   FXMAPFUNC(SEL_COMMAND,FXText::ID_SCROLL_UP,FXText::onCmdScrollUp),
247   FXMAPFUNC(SEL_COMMAND,FXText::ID_SCROLL_DOWN,FXText::onCmdScrollDown),
248   FXMAPFUNC(SEL_COMMAND,FXText::ID_SCROLL_TOP,FXText::onCmdScrollTop),
249   FXMAPFUNC(SEL_COMMAND,FXText::ID_SCROLL_BOTTOM,FXText::onCmdScrollBottom),
250   FXMAPFUNC(SEL_COMMAND,FXText::ID_SCROLL_CENTER,FXText::onCmdScrollCenter),
251   FXMAPFUNC(SEL_COMMAND,FXText::ID_INSERT_STRING,FXText::onCmdInsertString),
252   FXMAPFUNC(SEL_COMMAND,FXText::ID_INSERT_NEWLINE,FXText::onCmdInsertNewline),
253   FXMAPFUNC(SEL_COMMAND,FXText::ID_INSERT_NEWLINE_ONLY,FXText::onCmdInsertNewlineOnly),
254   FXMAPFUNC(SEL_COMMAND,FXText::ID_INSERT_NEWLINE_INDENT,FXText::onCmdInsertNewlineIndent),
255   FXMAPFUNC(SEL_COMMAND,FXText::ID_INSERT_TAB,FXText::onCmdInsertTab),
256   FXMAPFUNC(SEL_COMMAND,FXText::ID_INSERT_HARDTAB,FXText::onCmdInsertHardTab),
257   FXMAPFUNC(SEL_COMMAND,FXText::ID_INSERT_SOFTTAB,FXText::onCmdInsertSoftTab),
258   FXMAPFUNC(SEL_COMMAND,FXText::ID_CUT_SEL,FXText::onCmdCutSel),
259   FXMAPFUNC(SEL_COMMAND,FXText::ID_COPY_SEL,FXText::onCmdCopySel),
260   FXMAPFUNC(SEL_COMMAND,FXText::ID_DELETE_SEL,FXText::onCmdDeleteSel),
261   FXMAPFUNC(SEL_COMMAND,FXText::ID_PASTE_SEL,FXText::onCmdPasteSel),
262   FXMAPFUNC(SEL_COMMAND,FXText::ID_PASTE_MIDDLE,FXText::onCmdPasteMiddle),
263   FXMAPFUNC(SEL_COMMAND,FXText::ID_SELECT_CHAR,FXText::onCmdSelectChar),
264   FXMAPFUNC(SEL_COMMAND,FXText::ID_SELECT_WORD,FXText::onCmdSelectWord),
265   FXMAPFUNC(SEL_COMMAND,FXText::ID_SELECT_LINE,FXText::onCmdSelectLine),
266   FXMAPFUNC(SEL_COMMAND,FXText::ID_SELECT_ALL,FXText::onCmdSelectAll),
267   FXMAPFUNC(SEL_COMMAND,FXText::ID_DESELECT_ALL,FXText::onCmdDeselectAll),
268   FXMAPFUNC(SEL_COMMAND,FXText::ID_BACKSPACE_CHAR,FXText::onCmdBackspaceChar),
269   FXMAPFUNC(SEL_COMMAND,FXText::ID_BACKSPACE_WORD,FXText::onCmdBackspaceWord),
270   FXMAPFUNC(SEL_COMMAND,FXText::ID_BACKSPACE_BOL,FXText::onCmdBackspaceBol),
271   FXMAPFUNC(SEL_COMMAND,FXText::ID_DELETE_CHAR,FXText::onCmdDeleteChar),
272   FXMAPFUNC(SEL_COMMAND,FXText::ID_DELETE_WORD,FXText::onCmdDeleteWord),
273   FXMAPFUNC(SEL_COMMAND,FXText::ID_DELETE_EOL,FXText::onCmdDeleteEol),
274   FXMAPFUNC(SEL_COMMAND,FXText::ID_DELETE_ALL,FXText::onCmdDeleteAll),
275   FXMAPFUNC(SEL_COMMAND,FXText::ID_DELETE_LINE,FXText::onCmdDeleteLine),
276   FXMAPFUNC(SEL_COMMAND,FXText::ID_TOGGLE_EDITABLE,FXText::onCmdToggleEditable),
277   FXMAPFUNC(SEL_COMMAND,FXText::ID_TOGGLE_OVERSTRIKE,FXText::onCmdToggleOverstrike),
278   FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_ROW,FXText::onCmdCursorRow),
279   FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_COLUMN,FXText::onCmdCursorColumn),
280   FXMAPFUNC(SEL_COMMAND,FXText::ID_SETSTRINGVALUE,FXText::onCmdSetStringValue),
281   FXMAPFUNC(SEL_COMMAND,FXText::ID_GETSTRINGVALUE,FXText::onCmdGetStringValue),
282   FXMAPFUNC(SEL_COMMAND,FXText::ID_UPPER_CASE,FXText::onCmdChangeCase),
283   FXMAPFUNC(SEL_COMMAND,FXText::ID_LOWER_CASE,FXText::onCmdChangeCase),
284   FXMAPFUNC(SEL_COMMAND,FXText::ID_JOIN_LINES,FXText::onCmdJoinLines),
285   FXMAPFUNC(SEL_COMMAND,FXText::ID_GOTO_MATCHING,FXText::onCmdGotoMatching),
286   FXMAPFUNC(SEL_COMMAND,FXText::ID_SELECT_MATCHING,FXText::onCmdSelectMatching),
287   FXMAPFUNCS(SEL_COMMAND,FXText::ID_SELECT_BRACE,FXText::ID_SELECT_ANG,FXText::onCmdSelectEnclosing),
288   FXMAPFUNCS(SEL_COMMAND,FXText::ID_LEFT_BRACE,FXText::ID_LEFT_ANG,FXText::onCmdBlockBeg),
289   FXMAPFUNCS(SEL_COMMAND,FXText::ID_RIGHT_BRACE,FXText::ID_RIGHT_ANG,FXText::onCmdBlockEnd),
290   FXMAPFUNCS(SEL_COMMAND,FXText::ID_SHIFT_LEFT,FXText::ID_SHIFT_TABRIGHT,FXText::onCmdShiftText),
291   FXMAPFUNC(SEL_COMMAND,FXText::ID_COPY_LINE,FXText::onCmdCopyLine),
292   FXMAPFUNC(SEL_COMMAND,FXText::ID_MOVE_LINE_UP,FXText::onCmdMoveLineUp),
293   FXMAPFUNC(SEL_COMMAND,FXText::ID_MOVE_LINE_DOWN,FXText::onCmdMoveLineDown),
294   FXMAPFUNC(SEL_COMMAND,FXText::ID_CLEAN_INDENT,FXText::onCmdShiftText),
295   FXMAPFUNC(SEL_COMMAND,FXText::ID_SETHELPSTRING,FXText::onCmdSetHelp),
296   FXMAPFUNC(SEL_COMMAND,FXText::ID_GETHELPSTRING,FXText::onCmdGetHelp),
297   FXMAPFUNC(SEL_COMMAND,FXText::ID_SETTIPSTRING,FXText::onCmdSetTip),
298   FXMAPFUNC(SEL_COMMAND,FXText::ID_GETTIPSTRING,FXText::onCmdGetTip),
299   };
300 
301 
302 // Object implementation
303 FXIMPLEMENT(FXText,FXScrollArea,FXTextMap,ARRAYNUMBER(FXTextMap))
304 
305 
306 // Delimiters
307 const FXchar FXText::textDelimiters[]="~.,/\\`'!@#$%^&*()-=+{}|[]\":;<>?";
308 
309 // Matching things
310 static const FXchar lefthand[]="{[(<";
311 static const FXchar righthand[]="}])>";
312 
313 // Spaces, lots of spaces
314 static const FXchar spaces[MAXTABCOLUMNS+1]="                                ";
315 
316 /*******************************************************************************/
317 
318 
319 // For deserialization
FXText()320 FXText::FXText(){
321   flags|=FLAG_ENABLED|FLAG_DROPTARGET;
322   buffer=NULL;
323   sbuffer=NULL;
324   visrows=NULL;
325   length=0;
326   nvisrows=0;
327   nrows=1;
328   gapstart=0;
329   gapend=0;
330   toppos=0;
331   toprow=0;
332   keeppos=0;
333   select.startpos=0;
334   select.endpos=-1;
335   select.startcol=0;
336   select.endcol=-1;
337   hilite.startpos=0;
338   hilite.endpos=-1;
339   hilite.startcol=0;
340   hilite.endcol=-1;
341   anchorpos=0;
342   anchorrow=0;
343   anchorcol=0;
344   anchorvcol=0;
345   cursorpos=0;
346   cursorrow=0;
347   cursorcol=0;
348   cursorvcol=0;
349   prefcol=-1;
350   margintop=0;
351   marginbottom=0;
352   marginleft=0;
353   marginright=0;
354   wrapwidth=80;
355   wrapcolumns=80;
356   tabwidth=8;
357   tabcolumns=8;
358   barwidth=0;
359   barcolumns=0;
360   font=NULL;
361   textColor=0;
362   selbackColor=0;
363   seltextColor=0;
364   hilitebackColor=0;
365   hilitetextColor=0;
366   activebackColor=0;
367   numberColor=0;
368   cursorColor=0;
369   barColor=0;
370   textWidth=0;
371   textHeight=0;
372   delimiters=textDelimiters;
373   vrows=0;
374   vcols=0;
375   hilitestyles=NULL;
376   blink=FLAG_CARET;
377   matchtime=0;
378   grabx=0;
379   graby=0;
380   mode=MOUSE_NONE;
381   modified=false;
382   }
383 
384 
385 // Text widget
FXText(FXComposite * p,FXObject * tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h,FXint pl,FXint pr,FXint pt,FXint pb)386 FXText::FXText(FXComposite *p,FXObject* tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h,FXint pl,FXint pr,FXint pt,FXint pb):FXScrollArea(p,opts,x,y,w,h){
387   flags|=FLAG_ENABLED|FLAG_DROPTARGET;
388   target=tgt;
389   message=sel;
390   callocElms(buffer,MINSIZE);
391   sbuffer=NULL;
392   callocElms(visrows,NVISROWS+1);
393   length=0;
394   nrows=1;
395   nvisrows=NVISROWS;
396   gapstart=0;
397   gapend=MINSIZE;
398   toppos=0;
399   toprow=0;
400   keeppos=0;
401   select.startpos=0;
402   select.endpos=-1;
403   select.startcol=0;
404   select.endcol=-1;
405   hilite.startpos=0;
406   hilite.endpos=-1;
407   hilite.startcol=0;
408   hilite.endcol=-1;
409   anchorpos=0;
410   anchorrow=0;
411   anchorcol=0;
412   anchorvcol=0;
413   cursorpos=0;
414   cursorrow=0;
415   cursorcol=0;
416   cursorvcol=0;
417   prefcol=-1;
418   margintop=pt;
419   marginbottom=pb;
420   marginleft=pl;
421   marginright=pr;
422   wrapwidth=80;
423   wrapcolumns=80;
424   tabwidth=8;
425   tabcolumns=8;
426   barwidth=0;
427   barcolumns=0;
428   font=getApp()->getNormalFont();
429   hilitestyles=NULL;
430   blink=FLAG_CARET;
431   defaultCursor=getApp()->getDefaultCursor(DEF_TEXT_CURSOR);
432   dragCursor=getApp()->getDefaultCursor(DEF_TEXT_CURSOR);
433   textColor=getApp()->getForeColor();
434   selbackColor=getApp()->getSelbackColor();
435   seltextColor=getApp()->getSelforeColor();
436   hilitebackColor=FXRGB(255,128,128);
437   hilitetextColor=getApp()->getForeColor();
438   activebackColor=backColor;
439   numberColor=textColor;
440   cursorColor=getApp()->getForeColor();
441   barColor=backColor;
442   textWidth=0;
443   textHeight=0;
444   delimiters=textDelimiters;
445   vrows=0;
446   vcols=0;
447   matchtime=0;
448   grabx=0;
449   graby=0;
450   mode=MOUSE_NONE;
451   modified=false;
452   }
453 
454 
455 // Create window
create()456 void FXText::create(){
457   FXScrollArea::create();
458   font->create();
459   tabwidth=tabcolumns*font->getTextWidth(" ",1);
460   barwidth=barcolumns*font->getTextWidth("8",1);
461   recalc();
462   }
463 
464 
465 // Detach window
detach()466 void FXText::detach(){
467   FXScrollArea::detach();
468   font->detach();
469   }
470 
471 
472 // If window can have focus
canFocus() const473 FXbool FXText::canFocus() const {
474   return true;
475   }
476 
477 
478 // Into focus chain
setFocus()479 void FXText::setFocus(){
480   FXScrollArea::setFocus();
481   setDefault(true);
482   flags&=~FLAG_UPDATE;
483   if(getApp()->hasInputMethod()){
484     createComposeContext();
485     getComposeContext()->setFont(font);
486     getComposeContext()->focusIn();
487     }
488   }
489 
490 
491 // Enable the window
enable()492 void FXText::enable(){
493   if(!(flags&FLAG_ENABLED)){
494     FXScrollArea::enable();
495     update();
496     }
497   }
498 
499 
500 // Disable the window
disable()501 void FXText::disable(){
502   if(flags&FLAG_ENABLED){
503     FXScrollArea::disable();
504     update();
505     }
506   }
507 
508 
509 // Out of focus chain
killFocus()510 void FXText::killFocus(){
511   FXScrollArea::killFocus();
512   setDefault(maybe);
513   flags|=FLAG_UPDATE;
514   if(getApp()->hasInputMethod()){
515     destroyComposeContext();
516     }
517   }
518 
519 
520 // Return true if editable
isEditable() const521 FXbool FXText::isEditable() const {
522   return (options&TEXT_READONLY)==0;
523   }
524 
525 
526 // Set widget is editable or not
setEditable(FXbool edit)527 void FXText::setEditable(FXbool edit){
528   options^=((edit-1)^options)&TEXT_READONLY;
529   }
530 
531 
532 // Return true if text is in overstrike mode
isOverstrike() const533 FXbool FXText::isOverstrike() const {
534   return (options&TEXT_OVERSTRIKE)!=0;
535   }
536 
537 
538 // Set overstrike mode
setOverstrike(FXbool over)539 void FXText::setOverstrike(FXbool over){
540   options^=((0-over)^options)&TEXT_OVERSTRIKE;
541   }
542 
543 
544 // Check if w is delimiter
isdelimiter(FXwchar w) const545 FXbool FXText::isdelimiter(FXwchar w) const {
546   FXchar wcs[5]={'\0','\0','\0','\0','\0'};
547   if(__unlikely(128<=w)){
548     wc2utf(wcs,w);
549     return (strstr(delimiters,wcs)!=NULL);
550     }
551   return (strchr(delimiters,w)!=NULL);
552   }
553 
554 /*******************************************************************************/
555 
556 // Move the gap; gap is never moved inside utf character.
557 // Afterward, new characters may be inserted at position simply
558 // by dropping them at the start of the gap, thereby shrinking
559 // the gap as each character is inserted.  No big block moves
560 // are required until the gap has become too small for the next
561 // insert operation.
movegap(FXint pos)562 void FXText::movegap(FXint pos){
563   FXint gaplen=gapend-gapstart;
564   FXASSERT(0<=pos && pos<=length);
565   FXASSERT(0<=gapstart && gapstart<=length);
566   if(gapstart<pos){
567     moveElms(&buffer[gapstart],&buffer[gapend],pos-gapstart);
568     if(sbuffer){moveElms(&sbuffer[gapstart],&sbuffer[gapend],pos-gapstart);}
569     gapend=pos+gaplen;
570     gapstart=pos;
571     }
572   else if(pos<gapstart){
573     moveElms(&buffer[pos+gaplen],&buffer[pos],gapstart-pos);
574     if(sbuffer){moveElms(&sbuffer[pos+gaplen],&sbuffer[pos],gapstart-pos);}
575     gapend=pos+gaplen;
576     gapstart=pos;
577     }
578   }
579 
580 
581 // Grow or shrink the gap size, keeping gap start at the same position.
sizegap(FXint sz)582 void FXText::sizegap(FXint sz){
583   FXint gaplen=gapend-gapstart;
584   FXASSERT(0<=sz);
585   FXASSERT(0<=gapstart && gapstart<=length);
586   if(sz>gaplen){
587     if(!resizeElms(buffer,length+sz)){ fxerror("%s::sizegap: out of memory.\n",getClassName()); }
588     moveElms(&buffer[gapstart+sz],&buffer[gapend],length-gapstart);
589     if(sbuffer){
590       if(!resizeElms(sbuffer,length+sz)){ fxerror("%s::sizegap: out of memory.\n",getClassName()); }
591       moveElms(&sbuffer[gapstart+sz],&sbuffer[gapend],length-gapstart);
592       }
593     gapend=gapstart+sz;
594     }
595   else if(sz<gaplen){
596     moveElms(&buffer[gapstart+sz],&buffer[gapend],length-gapstart);
597     if(!resizeElms(buffer,length+sz)){ fxerror("%s::sizegap: out of memory.\n",getClassName()); }
598     if(sbuffer){
599       moveElms(&sbuffer[gapstart+sz],&sbuffer[gapend],length-gapstart);
600       if(!resizeElms(sbuffer,length+sz)){ fxerror("%s::sizegap: out of memory.\n",getClassName()); }
601       }
602     gapend=gapstart+sz;
603     }
604   }
605 
606 /*******************************************************************************/
607 
608 // Get byte
getByte(FXint pos) const609 FXint FXText::getByte(FXint pos) const {
610   FXASSERT(0<=pos && pos<=length);
611   return (FXuchar)buffer[pos+((gapend-gapstart)&((~pos+gapstart)>>31))];
612   }
613 
614 
615 // Get character, assuming that gap never inside utf8 encoding
getChar(FXint pos) const616 FXwchar FXText::getChar(FXint pos) const {
617   FXASSERT(0<=pos && pos<=length);
618   const FXuchar* ptr=(FXuchar*)&buffer[pos+((gapend-gapstart)&((~pos+gapstart)>>31))];
619   FXwchar w=ptr[0];
620   if(__unlikely(0xC0<=w)){ w=(w<<6)^ptr[1]^0x3080;
621   if(__unlikely(0x800<=w)){ w=(w<<6)^ptr[2]^0x20080;
622   if(__unlikely(0x10000<=w)){ w=(w<<6)^ptr[3]^0x400080; }}}
623   return w;
624   }
625 
626 
627 // Get length of wide character at position pos
getCharLen(FXint pos) const628 FXint FXText::getCharLen(FXint pos) const {
629   FXASSERT(0<=pos && pos<=length);
630   return FXUTF8LEN(buffer[pos+((gapend-gapstart)&((~pos+gapstart)>>31))]);
631   }
632 
633 
634 // Get style
getStyle(FXint pos) const635 FXint FXText::getStyle(FXint pos) const {
636   FXASSERT(0<=pos && pos<=length);
637   return (FXuchar)sbuffer[pos+((gapend-gapstart)&((~pos+gapstart)>>31))];
638   }
639 
640 /*******************************************************************************/
641 
642 // Decrement; a wide character does not cross the gap, so if pos is at
643 // or below below the gap, we read from the segment below the gap
dec(FXint pos) const644 FXint FXText::dec(FXint pos) const {
645   const FXchar* ptr=&buffer[(gapend-gapstart)&((gapstart-pos)>>31)];
646   return (--pos<=0 || FXISUTF8(ptr[pos]) || --pos<=0 || FXISUTF8(ptr[pos]) || --pos<=0 || FXISUTF8(ptr[pos]) || --pos), pos;
647   }
648 
649 
650 // Increment; since a wide character does not cross the gap, if we
651 // start under the gap the last character accessed is below the gap
inc(FXint pos) const652 FXint FXText::inc(FXint pos) const {
653   const FXchar* ptr=&buffer[(gapend-gapstart)&((~pos+gapstart)>>31)];
654   return (++pos>=length || FXISUTF8(ptr[pos]) || ++pos>=length || FXISUTF8(ptr[pos]) || ++pos>=length || FXISUTF8(ptr[pos]) || ++pos), pos;
655   }
656 
657 /*******************************************************************************/
658 
659 // Its a little bit more complex than this:
660 // We need to deal with diacritics, i.e. non-spacing stuff.  When wrapping, scan till
661 // the next starter-character [the one with charCombining(c)==0].  Then measure the
662 // string from that point on. This means FXFont::getCharWidth() is really quite useless.
663 // Next, we also have the issue of ligatures [fi, AE] and kerning-pairs [VA].
664 // With possible kerning pairs, we should really measure stuff from the start of the
665 // line [but this is *very* expensive!!].  We may want to just back up a few characters;
666 // perhaps to the start of the word, or just the previous character, if not a space.
667 // Need to investigate this some more; for now assume Normalization Form C.
668 
669 // Character width
charWidth(FXwchar ch,FXint indent) const670 FXint FXText::charWidth(FXwchar ch,FXint indent) const {
671   if(__likely(' '<=ch)) return font->getCharWidth(ch);
672   if(__likely(ch=='\t')) return (tabwidth-indent%tabwidth);
673   return font->getCharWidth('^')+font->getCharWidth(ch|0x40);
674   }
675 
676 
677 // Calculate X offset from line start to pos
xoffset(FXint start,FXint pos) const678 FXint FXText::xoffset(FXint start,FXint pos) const {
679   FXint w=0;
680   FXASSERT(0<=start && start<=pos && pos<=length);
681   while(start<pos){
682     w+=charWidth(getChar(start),w);
683     start+=getCharLen(start);
684     }
685   return w;
686   }
687 
688 
689 // Start of next wrapped line
690 // Position returned is start of next line, i.e. after newline
691 // or after space where line got broken during line wrapping.
wrap(FXint start) const692 FXint FXText::wrap(FXint start) const {
693   FXint lw,cw,p,s;
694   FXwchar ch;
695   FXASSERT(0<=start && start<=length);
696   lw=0;
697   p=s=start;
698   while(p<length){
699     ch=getChar(p);
700     if(ch=='\n') return p+1;            // Newline always breaks
701     cw=charWidth(ch,lw);
702     if(lw+cw>wrapwidth){                // Technically, a tab-before-wrap should be as wide as space!
703       if(s>start) return s;             // We remembered the last space we encountered; break there!
704       if(p>start) return p;             // Got at least one character, so return that
705       return p+getCharLen(p);           // Otherwise, advance one extra character
706       }
707     lw+=cw;
708     p+=getCharLen(p);
709     if(Unicode::isSpace(ch)) s=p;       // Remember potential break point!
710     }
711   return length;
712   }
713 
714 /*******************************************************************************/
715 
716 // Find row number from position
717 // If position falls in visible area, scan visrows for the proper row;
718 // otherwise, count rows from start of row containing position to the
719 // first visible line, or from the last visible line to the position.
rowFromPos(FXint pos) const720 FXint FXText::rowFromPos(FXint pos) const {
721   FXint row=0;
722   if(pos<visrows[0]){                                                   // Above visible buffer
723     if(pos<=0) return 0;
724     return toprow-countRows(rowStart(pos),visrows[0]);
725     }
726   if(visrows[nvisrows]<pos){                                            // Below visible buffer
727     if(pos>=length) return nrows-1;
728     return toprow+nvisrows-1+countRows(visrows[nvisrows-1],rowStart(pos));
729     }
730   while(row+1<nvisrows && visrows[row+1]<=pos && visrows[row]<visrows[row+1]) row++;
731   FXASSERT(0<=row && row<nvisrows);
732   FXASSERT(visrows[row]<=pos && pos<=visrows[row+1]);
733   return toprow+row;
734   }
735 
736 
737 // Find row start position from row number
738 // If row falls in visible area, we can directly return the row start position;
739 // otherwise, we scan backward from first visible line, or forward from last
740 // visible line, checking for start or end of buffer of course.
posFromRow(FXint row) const741 FXint FXText::posFromRow(FXint row) const {
742   if(row<toprow){
743     if(row<0) return 0;
744     return prevRow(visrows[0],toprow-row);
745     }
746   if(row>=toprow+nvisrows){
747     if(row>=nrows) return length;
748     return nextRow(visrows[nvisrows-1],row-toprow-nvisrows+1);
749     }
750   return visrows[row-toprow];
751   }
752 
753 
754 // Determine logical indent of position pos relative to line start.
755 // Stop at the end of the line (not row).
columnFromPos(FXint start,FXint pos) const756 FXint FXText::columnFromPos(FXint start,FXint pos) const {
757   FXint column=0; FXuchar c;
758   FXASSERT(0<=start && pos<=length);
759   while(start<pos && (c=getByte(start))!='\n'){
760     column+=CC(c,column);
761     start+=getCharLen(start);
762     }
763   return column;
764   }
765 
766 
767 // Determine position of logical indent relative to line start.
768 // Stop at the end of the line (not row).
posFromColumn(FXint start,FXint col) const769 FXint FXText::posFromColumn(FXint start,FXint col) const {
770   FXint column=0; FXuchar c;
771   FXASSERT(0<=start && start<=length);
772   while(start<length && (c=getByte(start))!='\n'){
773     column+=CC(c,column);
774     if(col<column) break;
775     start+=getCharLen(start);
776     }
777   return start;
778   }
779 
780 /*******************************************************************************/
781 
782 // Return position of begin of paragraph
lineStart(FXint pos) const783 FXint FXText::lineStart(FXint pos) const {
784   FXASSERT(0<=pos && pos<=length);
785   while(0<pos && getByte(pos-1)!='\n'){
786     pos--;
787     }
788   FXASSERT(0<=pos && pos<=length);
789   return pos;
790   }
791 
792 
793 // Return position of end of paragraph
lineEnd(FXint pos) const794 FXint FXText::lineEnd(FXint pos) const {
795   FXASSERT(0<=pos && pos<=length);
796   while(pos<length && getByte(pos)!='\n'){
797     pos++;
798     }
799   FXASSERT(0<=pos && pos<=length);
800   return pos;
801   }
802 
803 
804 // Return start of next line
nextLine(FXint pos,FXint nl) const805 FXint FXText::nextLine(FXint pos,FXint nl) const {
806   FXASSERT(0<=pos && pos<=length);
807   if(0<nl){
808     while(pos<length){
809       if(getByte(pos++)=='\n' && --nl<=0) break;
810       }
811     }
812   FXASSERT(0<=pos && pos<=length);
813   return pos;
814   }
815 
816 
817 // Return start of previous line
prevLine(FXint pos,FXint nl) const818 FXint FXText::prevLine(FXint pos,FXint nl) const {
819   FXASSERT(0<=pos && pos<=length);
820   if(0<nl){
821     while(0<pos){
822       if(getByte(pos-1)=='\n' && --nl<0) break;
823       pos--;
824       }
825     }
826   FXASSERT(0<=pos && pos<=length);
827   return pos;
828   }
829 
830 /*******************************************************************************/
831 
832 // Return row start
rowStart(FXint pos) const833 FXint FXText::rowStart(FXint pos) const {
834   FXint p,t;
835   FXASSERT(0<=pos && pos<=length);
836   if(options&TEXT_WORDWRAP){
837     p=pos;
838     while(0<pos && getByte(pos-1)!='\n'){               // Find line start first
839       pos--;
840       }
841     while(pos<p && (t=wrap(pos))<=p && t<length){       // Find row containing position, except if last row
842       pos=t;
843       }
844     }
845   else{
846     while(0<pos && getByte(pos-1)!='\n'){               // Find line start
847       pos--;
848       }
849     }
850   FXASSERT(0<=pos && pos<=length);
851   return pos;
852   }
853 
854 
855 // Return row end
rowEnd(FXint pos) const856 FXint FXText::rowEnd(FXint pos) const {
857   FXint p,t;
858   FXASSERT(0<=pos && pos<=length);
859   if(options&TEXT_WORDWRAP){
860     p=pos;
861     while(0<pos && getByte(pos-1)!='\n'){               // Find line start first
862       pos--;
863       }
864     while(pos<=p && pos<length){                        // Find row past position
865       pos=wrap(pos);
866       }
867     if(p<pos){                                          // Back off if line broke at space
868       t=dec(pos);
869       if(Unicode::isSpace(getChar(t))) pos=t;
870       }
871     }
872   else{
873     while(pos<length && getByte(pos)!='\n'){            // Hunt for end of line
874       pos++;
875       }
876     }
877   FXASSERT(0<=pos && pos<=length);
878   return pos;
879   }
880 
881 
882 // Move to next row given start of line
nextRow(FXint pos,FXint nr) const883 FXint FXText::nextRow(FXint pos,FXint nr) const {
884   FXint p,t;
885   FXASSERT(0<=pos && pos<=length);
886   if(0<nr){
887     if(options&TEXT_WORDWRAP){
888       p=pos;
889       while(0<pos && getByte(pos-1)!='\n'){             // Find line start first
890         pos--;
891         }
892       while(pos<p && (t=wrap(pos))<=p && t<length){     // Find row containing pos
893         pos=t;
894         }
895       while(pos<length){                                // Then wrap until nth row after
896         pos=wrap(pos);
897         if(--nr<=0) break;
898         }
899       }
900     else{
901       while(pos<length){                                // Hunt for begin of nth next line
902         if(getByte(pos++)=='\n' && --nr<=0) break;
903         }
904       }
905     }
906   FXASSERT(0<=pos && pos<=length);
907   return pos;
908   }
909 
910 
911 // Move to previous row given start of line
prevRow(FXint pos,FXint nr) const912 FXint FXText::prevRow(FXint pos,FXint nr) const {
913   FXint p,q,t;
914   FXASSERT(0<=pos && pos<=length);
915   if(0<nr){
916     if(options&TEXT_WORDWRAP){
917       while(0<pos){
918         p=pos;
919         while(0<pos && getByte(pos-1)!='\n'){           // Find line start first
920           pos--;
921           }
922         FXASSERT(0<=pos);
923         q=pos;
924         while(q<p && (t=wrap(q))<=p && t<length){       // Decrement number of rows to this point
925           nr--;
926           q=t;
927           }
928         while(nr<0){                                    // Went too far forward; try again from pos
929           pos=wrap(pos);
930           nr++;
931           }
932         FXASSERT(0<=nr);
933         if(nr==0) break;
934         if(pos==0) break;
935         pos--;                                          // Skip over newline
936         nr--;                                           // Which also counts as a row
937         }
938       }
939     else{
940       while(0<pos){                                     // Find previous line start
941         if(getByte(pos-1)=='\n' && --nr<0) break;
942         pos--;
943         }
944       }
945     }
946   FXASSERT(0<=pos && pos<=length);
947   return pos;
948   }
949 
950 /*******************************************************************************/
951 
952 // Find end of previous word
leftWord(FXint pos) const953 FXint FXText::leftWord(FXint pos) const {
954   FXwchar ch;
955   FXASSERT(0<=pos && pos<=length);
956   if(0<pos){
957     pos=dec(pos);
958     ch=getChar(pos);
959     if(isdelimiter(ch)){
960       while(0<pos){
961         ch=getChar(dec(pos));
962         if(Unicode::isSpace(ch) || !isdelimiter(ch)) return pos;
963         pos=dec(pos);
964         }
965       }
966     else if(!Unicode::isSpace(ch)){
967       while(0<pos){
968         ch=getChar(dec(pos));
969         if(Unicode::isSpace(ch) || isdelimiter(ch)) return pos;
970         pos=dec(pos);
971         }
972       }
973     while(0<pos){
974       ch=getChar(dec(pos));
975       if(!Unicode::isBlank(ch)) return pos;
976       pos=dec(pos);
977       }
978     }
979   return pos;
980   }
981 
982 
983 // Find begin of next word
rightWord(FXint pos) const984 FXint FXText::rightWord(FXint pos) const {
985   FXwchar ch;
986   FXASSERT(0<=pos && pos<=length);
987   if(pos<length){
988     ch=getChar(pos);
989     pos=inc(pos);
990     if(isdelimiter(ch)){
991       while(pos<length){
992         ch=getChar(pos);
993         if(Unicode::isSpace(ch) || !isdelimiter(ch)) return pos;
994         pos=inc(pos);
995         }
996       }
997     else if(!Unicode::isSpace(ch)){
998       while(pos<length){
999         ch=getChar(pos);
1000         if(Unicode::isSpace(ch) || isdelimiter(ch)) return pos;
1001         pos=inc(pos);
1002         }
1003       }
1004     while(pos<length){
1005       ch=getChar(pos);
1006       if(!Unicode::isBlank(ch)) return pos;
1007       pos=inc(pos);
1008       }
1009     }
1010   return pos;
1011   }
1012 
1013 
1014 // Find begin of a word
wordStart(FXint pos) const1015 FXint FXText::wordStart(FXint pos) const {
1016   FXwchar ch;
1017   FXASSERT(0<=pos && pos<=length);
1018   if(0<pos){
1019     ch=(pos<length)?getChar(pos):' ';
1020     if(ch=='\n') return pos;
1021     if(Unicode::isBlank(ch)){
1022       while(0<pos){
1023         ch=getChar(dec(pos));
1024         if(!Unicode::isBlank(ch)) return pos;
1025         pos=dec(pos);
1026         }
1027       }
1028     else if(isdelimiter(ch)){
1029       while(0<pos){
1030         ch=getChar(dec(pos));
1031         if(!isdelimiter(ch)) return pos;
1032         pos=dec(pos);
1033         }
1034       }
1035     else{
1036       while(0<pos){
1037         ch=getChar(dec(pos));
1038         if(isdelimiter(ch) || Unicode::isSpace(ch)) return pos;
1039         pos=dec(pos);
1040         }
1041       }
1042     }
1043   return pos;
1044   }
1045 
1046 
1047 // Find end of word
wordEnd(FXint pos) const1048 FXint FXText::wordEnd(FXint pos) const {
1049   FXwchar ch;
1050   FXASSERT(0<=pos && pos<=length);
1051   if(pos<length){
1052     ch=getChar(pos);
1053     if(ch=='\n') return pos+1;
1054     if(Unicode::isBlank(ch)){
1055       while(pos<length){
1056         ch=getChar(pos);
1057         if(!Unicode::isBlank(ch)) return pos;
1058         pos=inc(pos);
1059         }
1060       }
1061     else if(isdelimiter(ch)){
1062       while(pos<length){
1063         ch=getChar(pos);
1064         if(!isdelimiter(ch)) return pos;
1065         pos=inc(pos);
1066         }
1067       }
1068     else{
1069       while(pos<length){
1070         ch=getChar(pos);
1071         if(isdelimiter(ch) || Unicode::isSpace(ch)) return pos;
1072         pos=inc(pos);
1073         }
1074       }
1075     }
1076   return pos;
1077   }
1078 
1079 
1080 // Make a valid position, at the start of a wide character
validPos(FXint pos) const1081 FXint FXText::validPos(FXint pos) const {
1082   const FXchar* ptr=&buffer[(gapend-gapstart)&((~pos+gapstart)>>31)];
1083   if(pos<=0) return 0;
1084   if(pos>=length) return length;
1085   return (FXISUTF8(ptr[pos]) || --pos<=0 || FXISUTF8(ptr[pos]) || --pos<=0 || FXISUTF8(ptr[pos]) || --pos), pos;
1086   }
1087 
1088 /*******************************************************************************/
1089 
1090 // Count number of columns; start should be on a row start
countCols(FXint start,FXint end) const1091 FXint FXText::countCols(FXint start,FXint end) const {
1092   FXint result=0;
1093   FXint in=0;
1094   FXwchar c;
1095   FXASSERT(0<=start && start<=end && end<=length);
1096   while(start<end){
1097     c=getChar(start);
1098     if(c=='\n'){ start++; result=FXMAX(result,in); in=0; continue; }
1099     if(c=='\t'){ start++; in+=(tabcolumns-in%tabcolumns); continue; }
1100     start+=getCharLen(start);
1101     in++;
1102     }
1103   result=FXMAX(result,in);
1104   return result;
1105   }
1106 
1107 
1108 // Count number of rows; start and end should be on a row start
countRows(FXint start,FXint end) const1109 FXint FXText::countRows(FXint start,FXint end) const {
1110   FXint result=0;
1111   if(options&TEXT_WORDWRAP){
1112     while(start<end){
1113       start=wrap(start);
1114       result++;
1115       }
1116     }
1117   else{
1118     while(start<end){
1119       if(getByte(start++)=='\n'){
1120         result++;
1121         }
1122       }
1123     }
1124   return result;
1125   }
1126 
1127 
1128 // Count number of newlines
countLines(FXint start,FXint end) const1129 FXint FXText::countLines(FXint start,FXint end) const {
1130   FXint result=0;
1131   FXASSERT(0<=start && start<=end && end<=length);
1132   while(start<end){
1133     if(getByte(start++)=='\n'){
1134       result++;
1135       }
1136     }
1137   return result;
1138   }
1139 
1140 /*******************************************************************************/
1141 
1142 // Measure lines; start and end should be on a row start
measureText(FXint start,FXint end,FXint & wmax,FXint & hmax) const1143 FXint FXText::measureText(FXint start,FXint end,FXint& wmax,FXint& hmax) const {
1144   FXint result=0;
1145   FXint s=start;
1146   FXint b=start;
1147   FXint w=0;
1148   FXint cw;
1149   FXwchar c;
1150   FXASSERT(0<=start && start<=end && end<=length);
1151   if(options&TEXT_WORDWRAP){
1152     wmax=wrapwidth;
1153     while(start<end){
1154       c=getChar(start);
1155       if(c=='\n'){                      // Break at newline
1156         result++;
1157         start++;
1158         s=b=start;
1159         w=0;
1160         continue;
1161         }
1162       cw=charWidth(c,w);
1163       if(w+cw>wrapwidth){               // Break due to wrap
1164         result++;
1165         w=0;
1166         if(s<b){                        // Break past last space seen
1167           s=start=b;
1168           continue;
1169           }
1170         if(start==s){                   // Always at least one character on each line!
1171           start+=getCharLen(start);
1172           }
1173         s=b=start;
1174         continue;
1175         }
1176       w+=cw;
1177       start+=getCharLen(start);
1178       if(Unicode::isSpace(c)) b=start;  // Remember potential break point!
1179       }
1180     }
1181   else{
1182     wmax=0;
1183     while(start<end){
1184       c=getChar(start);
1185       if(c=='\n'){                      // Break at newline
1186         wmax=FXMAX(wmax,w);
1187         result++;
1188         start++;
1189         w=0;
1190         continue;
1191         }
1192       w+=charWidth(c,w);
1193       start+=getCharLen(start);
1194       }
1195     wmax=FXMAX(wmax,w);
1196     }
1197   hmax=result*font->getFontHeight();
1198   return result;
1199   }
1200 
1201 
1202 // Recalculate line starts, by re-wrapping affected
1203 // lines within the visible range of the text buffer.
calcVisRows(FXint startline,FXint endline)1204 void FXText::calcVisRows(FXint startline,FXint endline){
1205   FXASSERT(0<nvisrows);
1206   if(startline<1){
1207     visrows[0]=toppos;
1208     startline=1;
1209     }
1210   if(endline>nvisrows){
1211     endline=nvisrows;
1212     }
1213   if(startline<=endline){
1214     FXint pos=visrows[startline-1];
1215     if(options&TEXT_WORDWRAP){
1216       while(startline<=endline && pos<length){
1217         pos=wrap(pos);
1218         FXASSERT(0<=pos && pos<=length);
1219         visrows[startline++]=pos;
1220         }
1221       }
1222     else{
1223       while(startline<=endline && pos<length){
1224         pos=nextLine(pos);
1225         FXASSERT(0<=pos && pos<=length);
1226         visrows[startline++]=pos;
1227         }
1228       }
1229     while(startline<=endline){
1230       visrows[startline++]=length;
1231       }
1232     }
1233   }
1234 
1235 
1236 // Recompute the text dimensions; this is based on font, margins, wrapping
1237 // and line numbers, so if any of these things change it has to be redone.
recompute()1238 void FXText::recompute(){
1239   FXint hh=font->getFontHeight();
1240   FXint botrow,ww1,hh1,ww2,hh2;
1241 
1242   // The keep position is where we want to have the top of the buffer be;
1243   // make sure this is still inside the text buffer!
1244   keeppos=FXCLAMP(0,keeppos,length);
1245 
1246   // Due to wrapping, toppos which USED to point to a row start may no
1247   // longer do so.  We back off till the nearest row start.  If we resize
1248   // the window repeatedly, toppos will not wander away indiscriminately.
1249   toppos=rowStart(keeppos);
1250 
1251   // Remeasure the text; first, the part above the visible buffer, then
1252   // the rest.  This avoids measuring the entire text twice, which is
1253   // quite expensive.
1254   toprow=measureText(0,toppos,ww1,hh1);
1255 
1256   FXTRACE((TOPIC_LAYOUT,"measureText(%d,%d,%d,%d) = %d\n",0,toppos,ww1,hh1,toprow));
1257 
1258   botrow=measureText(toppos,length,ww2,hh2);
1259 
1260   FXTRACE((TOPIC_LAYOUT,"measureText(%d,%d,%d,%d) = %d\n",toppos,length,ww2,hh2,botrow));
1261 
1262   // Update text dimensions in terms of pixels and rows; note one extra
1263   // row always added, as there is always at least one row, even though
1264   // it may be empty of any characters.
1265   textWidth=FXMAX(ww1,ww2);
1266   textHeight=hh1+hh2+hh;
1267   nrows=toprow+botrow+1;
1268 
1269   // Adjust position, keeping same fractional position. Do this AFTER having
1270   // determined toprow, which may have changed due to wrapping changes.
1271   pos_y=-toprow*hh-(-pos_y%hh);
1272 
1273   FXTRACE((TOPIC_LAYOUT,"recompute: textWidth=%d textHeight=%d nrows=%d\n",textWidth,textHeight,nrows));
1274 
1275   // All is clean
1276   flags&=~FLAG_RECALC;
1277   }
1278 
1279 /*******************************************************************************/
1280 
1281 // Determine content width of scroll area
getContentWidth()1282 FXint FXText::getContentWidth(){
1283   if(flags&FLAG_RECALC) recompute();
1284   return marginleft+marginright+textWidth;
1285   }
1286 
1287 
1288 // Determine content height of scroll area
getContentHeight()1289 FXint FXText::getContentHeight(){
1290   if(flags&FLAG_RECALC) recompute();
1291   return margintop+marginbottom+textHeight;
1292   }
1293 
1294 
1295 // Return visible scroll-area x position
getVisibleX() const1296 FXint FXText::getVisibleX() const {
1297   return barwidth;
1298   }
1299 
1300 
1301 // Return visible scroll-area y position
getVisibleY() const1302 FXint FXText::getVisibleY() const {
1303   return 0;
1304   }
1305 
1306 
1307 // Return visible scroll-area width
getVisibleWidth() const1308 FXint FXText::getVisibleWidth() const {
1309   return width-vertical->getWidth()-barwidth;
1310   }
1311 
1312 
1313 // Return visible scroll-area height
getVisibleHeight() const1314 FXint FXText::getVisibleHeight() const {
1315   return height-horizontal->getHeight();
1316   }
1317 
1318 
1319 // Get default width
getDefaultWidth()1320 FXint FXText::getDefaultWidth(){
1321   return 0<vcols ? marginleft+marginright+vcols*font->getTextWidth("8",1)+barwidth : FXScrollArea::getDefaultWidth()+barwidth;
1322   }
1323 
1324 
1325 // Get default height
getDefaultHeight()1326 FXint FXText::getDefaultHeight(){
1327   return 0<vrows ? margintop+marginbottom+vrows*font->getFontHeight() : FXScrollArea::getDefaultHeight();
1328   }
1329 
1330 /*******************************************************************************/
1331 
1332 // Recalculate layout
layout()1333 void FXText::layout(){
1334   FXint fh=font->getFontHeight();
1335   FXint fw=font->getFontWidth();
1336   FXint oww=wrapwidth;
1337   FXint cursorstartpos;
1338   FXint anchorstartpos;
1339 
1340   // Compute new wrap width, which is either based on the wrap columns or on the
1341   // width of the window.  If a vertical scroll bar MAY be visible, assume it IS
1342   // so we don't get sudden surprises.
1343   // For mono-spaced fonts, wrapwidth is a integral multiple of font width.
1344   if(options&TEXT_FIXEDWRAP){
1345     wrapwidth=wrapcolumns*font->getTextWidth("x",1);
1346     }
1347   else{
1348     wrapwidth=width-barwidth-marginleft-marginright;
1349     if(!(options&VSCROLLER_NEVER)) wrapwidth-=vertical->getDefaultWidth();
1350     if(font->isFontMono()) wrapwidth=fw*(wrapwidth/fw);
1351     }
1352 
1353   // If we're wrapping, and wrap width changed, we may need to reflow the text.
1354   if((options&TEXT_WORDWRAP) && (wrapwidth!=oww)) flags|=FLAG_RECALC;
1355 
1356   // Adjust scrollbars; if necessary, remeasure reflowed text
1357   // This places the scrollbars, and thus sets the visible area.
1358   placeScrollBars(width-barwidth,height);
1359 
1360   // Number of visible lines depends on viewport height
1361   nvisrows=(getVisibleHeight()-margintop-marginbottom+fh+fh-1)/fh;
1362   if(nvisrows<1) nvisrows=1;
1363 
1364   // Resize line start array; the plus 1 is to keep track of the start
1365   // of the next line just beyond the last visible one; this ensures
1366   // we know how long the last visible line is.
1367   resizeElms(visrows,nvisrows+1);
1368 
1369   // Recompute line start array
1370   calcVisRows(0,nvisrows);
1371 
1372   // Scroll bar line/column sizes are based on font; set these now
1373   vertical->setLine(fh);
1374   horizontal->setLine(fw);
1375 
1376   // Hopefully, all is still in range
1377   FXASSERT(0<=toprow && toprow<=nrows);
1378   FXASSERT(0<=toppos && toppos<=length);
1379 
1380   // Update cursor location parameters
1381   cursorstartpos=rowStart(cursorpos);
1382   cursorrow=rowFromPos(cursorstartpos);
1383   cursorcol=columnFromPos(cursorstartpos,cursorpos);
1384   cursorvcol=cursorcol;
1385 
1386   // Update anchor location parameters
1387   anchorstartpos=rowStart(anchorpos);
1388   anchorrow=rowFromPos(anchorstartpos);
1389   anchorcol=columnFromPos(anchorstartpos,anchorpos);
1390   anchorvcol=anchorcol;
1391 
1392   // Force repaint
1393   update();
1394 
1395   // Done
1396   flags&=~FLAG_DIRTY;
1397   }
1398 
1399 
1400 // Propagate size change
recalc()1401 void FXText::recalc(){
1402   FXScrollArea::recalc();
1403   flags|=FLAG_RECALC;
1404   }
1405 
1406 /*******************************************************************************/
1407 
1408 // Search forward for match
matchForward(FXint pos,FXint end,FXwchar l,FXwchar r,FXint level) const1409 FXint FXText::matchForward(FXint pos,FXint end,FXwchar l,FXwchar r,FXint level) const {
1410   FXwchar ch;
1411   FXASSERT(0<=end && end<=length);
1412   FXASSERT(0<=pos && pos<=length);
1413   while(pos<end){
1414     ch=getChar(pos);
1415     if(ch==r){
1416       level--;
1417       if(level<=0) return pos;
1418       }
1419     else if(ch==l){
1420       level++;
1421       }
1422     pos=inc(pos);
1423     }
1424   return -1;
1425   }
1426 
1427 
1428 // Search backward for match
matchBackward(FXint pos,FXint beg,FXwchar l,FXwchar r,FXint level) const1429 FXint FXText::matchBackward(FXint pos,FXint beg,FXwchar l,FXwchar r,FXint level) const {
1430   FXwchar ch;
1431   FXASSERT(0<=beg && beg<=length);
1432   FXASSERT(0<=pos && pos<=length);
1433   while(beg<=pos){
1434     ch=getChar(pos);
1435     if(ch==l){
1436       level--;
1437       if(level<=0) return pos;
1438       }
1439     else if(ch==r){
1440       level++;
1441       }
1442     pos=dec(pos);
1443     }
1444   return -1;
1445   }
1446 
1447 
1448 // Search for matching character
findMatching(FXint pos,FXint beg,FXint end,FXwchar ch,FXint level) const1449 FXint FXText::findMatching(FXint pos,FXint beg,FXint end,FXwchar ch,FXint level) const {
1450   FXASSERT(0<=level);
1451   FXASSERT(0<=pos && pos<=length);
1452   switch(ch){
1453     case '{': return matchForward(pos+1,end,'{','}',level);
1454     case '}': return matchBackward(pos-1,beg,'{','}',level);
1455     case '[': return matchForward(pos+1,end,'[',']',level);
1456     case ']': return matchBackward(pos-1,beg,'[',']',level);
1457     case '(': return matchForward(pos+1,end,'(',')',level);
1458     case ')': return matchBackward(pos-1,beg,'(',')',level);
1459     }
1460   return -1;
1461   }
1462 
1463 
1464 // Flash matching braces or parentheses
1465 // If flashing briefly, highlight only if visible; otherwise, highlight always
flashMatching()1466 void FXText::flashMatching(){
1467   killHighlight();
1468   getApp()->removeTimeout(this,ID_FLASH);
1469   if((options&TEXT_SHOWMATCH) && 0<cursorpos){
1470     FXint beg=(matchtime<forever) ? visrows[0] : 0;
1471     FXint end=(matchtime<forever) ? visrows[nvisrows] : length;
1472     FXint matchpos=findMatching(cursorpos-1,beg,end,getByte(cursorpos-1),1);
1473     if(0<=matchpos){
1474       setHighlight(matchpos,1);
1475       if(0<matchtime && matchtime<forever){
1476         getApp()->addTimeout(this,ID_FLASH,matchtime);
1477         }
1478       }
1479     }
1480   }
1481 
1482 /*******************************************************************************/
1483 
1484 // Search for text
findText(const FXString & string,FXint * beg,FXint * end,FXint start,FXuint flgs,FXint npar)1485 FXbool FXText::findText(const FXString& string,FXint* beg,FXint* end,FXint start,FXuint flgs,FXint npar){
1486   FXint rexmode=FXRex::Normal;
1487   FXRex rex;
1488 
1489   // Check arguments
1490   if(npar<1 || !beg || !end){ fxerror("%s::findText: bad argument.\n",getClassName()); }
1491 
1492   // Tweak parse flags a bit
1493   if(1<npar) rexmode|=FXRex::Capture;                           // Capturing parentheses
1494   if(flgs&SEARCH_IGNORECASE) rexmode|=FXRex::IgnoreCase;        // Case insensitivity
1495   if(!(flgs&SEARCH_REGEX)) rexmode|=FXRex::Verbatim;            // Verbatim match
1496 
1497   // Try parse the regex
1498   if(rex.parse(string,rexmode)==FXRex::ErrOK){
1499 
1500     // Make all characters contiguous in the buffer
1501     movegap(length);
1502 
1503     // Search forward
1504     if(flgs&SEARCH_FORWARD){
1505       if(start<=length){
1506         if(rex.search(buffer,length,FXMAX(start,0),length,FXRex::Normal,beg,end,npar)>=0) return true;
1507         }
1508       if((flgs&SEARCH_WRAP) && (start>0)){
1509         if(rex.search(buffer,length,0,FXMIN(start,length),FXRex::Normal,beg,end,npar)>=0) return true;
1510         }
1511       return false;
1512       }
1513 
1514     // Search backward
1515     if(flgs&SEARCH_BACKWARD){
1516       if(0<=start){
1517         if(rex.search(buffer,length,FXMIN(start,length),0,FXRex::Normal,beg,end,npar)>=0) return true;
1518         }
1519       if((flgs&SEARCH_WRAP) && (start<length)){
1520         if(rex.search(buffer,length,length,FXMAX(start,0),FXRex::Normal,beg,end,npar)>=0) return true;
1521         }
1522       return false;
1523       }
1524 
1525     // Anchored match
1526     return rex.amatch(buffer,length,start,FXRex::Normal,beg,end,npar);
1527     }
1528   return false;
1529   }
1530 
1531 /*******************************************************************************/
1532 
1533 // Localize position at x,y
getPosAt(FXint x,FXint y) const1534 FXint FXText::getPosAt(FXint x,FXint y) const {
1535   FXint linebeg,lineend,row,cx=0,cw,p;
1536   FXwchar c;
1537   x=x-pos_x-marginleft-getVisibleX();
1538   y=y-pos_y-margintop-getVisibleY();
1539   row=y/font->getFontHeight();
1540   if(row<toprow){                       // Above visible area
1541     if(row<0) return 0;                 // Before first row
1542     linebeg=prevRow(visrows[0],toprow-row);
1543     lineend=nextRow(linebeg);
1544     }
1545   else if(row>=toprow+nvisrows){        // Below visible area
1546     if(row>=nrows) return length;       // Below last row
1547     linebeg=nextRow(visrows[nvisrows-1],row-toprow-nvisrows+1);
1548     lineend=nextRow(linebeg);
1549     }
1550   else{                                 // Inside visible area
1551     FXASSERT(row-toprow<nvisrows);
1552     linebeg=visrows[row-toprow];
1553     lineend=visrows[row-toprow+1];
1554     }
1555   if(linebeg<lineend){                  // Backup past line-break character, space or newline
1556     p=dec(lineend);
1557     if(Unicode::isSpace(getChar(p))) lineend=p;
1558     }
1559   FXASSERT(0<=linebeg);
1560   FXASSERT(linebeg<=lineend);
1561   FXASSERT(lineend<=length);
1562   while(linebeg<lineend){
1563     c=getChar(linebeg);
1564     cw=charWidth(c,cx);
1565     if(x<=(cx+(cw>>1))) return linebeg; // Before middle of character
1566     linebeg+=getCharLen(linebeg);
1567     cx+=cw;
1568     }
1569   return lineend;
1570   }
1571 
1572 
1573 // Return text position containing x, y coordinate
getPosContaining(FXint x,FXint y) const1574 FXint FXText::getPosContaining(FXint x,FXint y) const {
1575   FXint linebeg,lineend,row,cx=0,cw,p;
1576   FXwchar c;
1577   x=x-pos_x-marginleft-getVisibleX();
1578   y=y-pos_y-margintop-getVisibleY();
1579   row=y/font->getFontHeight();
1580   if(row<toprow){                       // Above visible area
1581     if(row<0) return 0;                 // Before first row
1582     linebeg=prevRow(visrows[0],toprow-row);
1583     lineend=nextRow(linebeg);
1584     }
1585   else if(row>=toprow+nvisrows){        // Below visible area
1586     if(row>=nrows) return length;       // Below last row
1587     linebeg=nextRow(visrows[nvisrows-1],row-toprow-nvisrows+1);
1588     lineend=nextRow(linebeg);
1589     }
1590   else{                                 // Inside visible area
1591     FXASSERT(row-toprow<nvisrows);
1592     linebeg=visrows[row-toprow];
1593     lineend=visrows[row-toprow+1];
1594     }
1595   if(linebeg<lineend){                  // Backup past line-break character, space or newline
1596     p=dec(lineend);
1597     if(Unicode::isSpace(getChar(p))) lineend=p;
1598     }
1599   FXASSERT(0<=linebeg);
1600   FXASSERT(linebeg<=lineend);
1601   FXASSERT(lineend<=length);
1602   while(linebeg<lineend){
1603     c=getChar(linebeg);
1604     cw=charWidth(c,cx);
1605     if(x<cx+cw) return linebeg;         // Character contains x
1606     linebeg+=getCharLen(linebeg);
1607     cx+=cw;
1608     }
1609   return lineend;
1610   }
1611 
1612 
1613 // Return closest position and (row,col) of given x,y coordinate.
1614 // Computing the logical column inside of a tab, things can get tricky when
1615 // the font is not a fixed-pitch.  Our solution is to stretch spaces to
1616 // subdivide the tab into as many columns as needed, regardless of whether
1617 // the space is a whole multiple of the regular space width.
1618 // Also, control-characters are problematic as they're rendered as ^A,
1619 // thus, take up two columns even for fixed-pitch fonts.
getRowColumnAt(FXint x,FXint y,FXint & row,FXint & col) const1620 FXint FXText::getRowColumnAt(FXint x,FXint y,FXint& row,FXint& col) const {
1621   FXint spacew=font->getCharWidth(' ');
1622   FXint caretw=font->getCharWidth('^');
1623   FXint linebeg,lineend,cx=0,cw,cc,p;
1624   FXwchar c;
1625   x=x-pos_x-marginleft-getVisibleX();
1626   y=y-pos_y-margintop-getVisibleY();
1627   row=y/font->getFontHeight();          // Row is easy to find
1628   col=0;                                // Find column later
1629   if(row<toprow){                       // Above visible area
1630     linebeg=prevRow(visrows[0],toprow-row);
1631     lineend=nextRow(linebeg);
1632     }
1633   else if(row>=toprow+nvisrows){        // Below visible area
1634     linebeg=nextRow(visrows[nvisrows-1],row-toprow-nvisrows+1);
1635     lineend=nextRow(linebeg);
1636     }
1637   else{                                 // Inside visible area
1638     FXASSERT(row-toprow<nvisrows);
1639     linebeg=visrows[row-toprow];
1640     lineend=visrows[row-toprow+1];
1641     }
1642   if(linebeg<lineend){                  // Backup past line-break character, space or newline
1643     p=dec(lineend);
1644     if(Unicode::isSpace(getChar(p))) lineend=p;
1645     }
1646   FXASSERT(0<=linebeg);
1647   FXASSERT(linebeg<=lineend);
1648   FXASSERT(lineend<=length);
1649   while(linebeg<lineend){
1650     c=getChar(linebeg);
1651     if(' '<=c){                         // Normal character
1652       cw=font->getCharWidth(c);
1653       if((cx+(cw>>1))<x){
1654         linebeg+=getCharLen(linebeg);   // Advance over utf8 character
1655         col+=1;
1656         cx+=cw;
1657         continue;
1658         }
1659       return linebeg;
1660       }
1661     else if(c=='\t'){                   // Tab is really complex
1662       cw=tabwidth-cx%tabwidth;
1663       cc=tabcolumns-col%tabcolumns;
1664       if(cx+cw<=x){                     // Advance over entire tab
1665         linebeg+=1;
1666         col+=cc;
1667         cx+=cw;
1668         continue;
1669         }
1670       if(cx<x){                         // Calculate column inside tab
1671         col+=(cc*(x-cx)+(cw>>1))/cw;
1672         linebeg+=(x>=cx+(cw>>1));       // Round to nearest position
1673         }
1674       return linebeg;
1675       }
1676     else{                               // Control characters
1677       cw=caretw+font->getCharWidth(c|0x40);
1678       if((cx+(cw>>1))<x){
1679         linebeg+=1;
1680         col+=1;
1681         cx+=cw;
1682         continue;
1683         }
1684       return linebeg;
1685       }
1686     }
1687   if(cx<x){                             // Calculate column beyond end of line
1688     col+=(x+(spacew>>1)-cx)/spacew;
1689     }
1690   return linebeg;
1691   }
1692 
1693 
1694 // Calculate X position of pos
getXOfPos(FXint pos) const1695 FXint FXText::getXOfPos(FXint pos) const {
1696   FXint base=rowStart(pos);
1697   return getVisibleX()+marginleft+pos_x+xoffset(base,pos);
1698   }
1699 
1700 
1701 // Determine Y from position pos
getYOfPos(FXint pos) const1702 FXint FXText::getYOfPos(FXint pos) const {
1703   FXint h=font->getFontHeight();
1704   return getVisibleY()+margintop+pos_y+rowFromPos(pos)*h;
1705   }
1706 
1707 
1708 // Return screen x-coordinate of row and column
getXOfRowColumn(FXint row,FXint col) const1709 FXint FXText::getXOfRowColumn(FXint row,FXint col) const {
1710   FXint spacew=font->getCharWidth(' ');
1711   FXint caretw=font->getCharWidth('^');
1712   FXint linebeg,lineend,tcol=0,twid=0,tadj=0,cx=0,cc=0,cw,p;
1713   FXwchar c;
1714   if(row<toprow){                       // Above visible area
1715     linebeg=prevRow(visrows[0],toprow-row);
1716     lineend=nextRow(linebeg);
1717     }
1718   else if(row>=toprow+nvisrows){        // Below visible area
1719     linebeg=nextRow(visrows[nvisrows-1],row-toprow-nvisrows+1);
1720     lineend=nextRow(linebeg);
1721     }
1722   else{                                 // Inside visible area
1723     linebeg=visrows[row-toprow];
1724     lineend=visrows[row-toprow+1];
1725     }
1726   if(linebeg<lineend){                  // Backup past line-break character, space or newline
1727     p=dec(lineend);
1728     if(Unicode::isSpace(getChar(p))) lineend=p;
1729     }
1730   FXASSERT(0<=linebeg);
1731   FXASSERT(linebeg<=lineend);
1732   FXASSERT(lineend<=length);
1733   while(cc<col){
1734     if(linebeg>=lineend){               // Column past end of line
1735       cx+=spacew*(col-cc);              // Add left-over columns and we're done
1736       break;
1737       }
1738     c=getChar(linebeg);
1739     if(' '<=c){                         // Normal character
1740       cx+=font->getCharWidth(c);
1741       cc+=1;
1742       linebeg+=getCharLen(linebeg);     // Advance over utf8 character
1743       continue;
1744       }
1745     if(c!='\t'){                        // Control character
1746       cx+=caretw+font->getCharWidth(c|0x40);
1747       cc+=1;
1748       linebeg+=1;
1749       continue;
1750       }
1751     if(tcol==0){                        // Tab character
1752       cw=tabwidth-cx%tabwidth;
1753       tcol=tabcolumns-cc%tabcolumns;
1754       twid=cw/tcol;
1755       tadj=cw-twid*tcol;
1756       }
1757     cx+=twid+(tadj>0);                  // Mete out bits of tab character
1758     tcol-=1;
1759     tadj-=1;
1760     cc+=1;
1761     linebeg+=(tcol==0);
1762     }
1763   return getVisibleX()+marginleft+pos_x+cx;
1764   }
1765 
1766 
1767 // Return screen y-coordinate of row and column
getYOfRowColumn(FXint row,FXint) const1768 FXint FXText::getYOfRowColumn(FXint row,FXint) const {
1769   return getVisibleY()+margintop+pos_y+row*font->getFontHeight();
1770   }
1771 
1772 /*******************************************************************************/
1773 
1774 // Make line containing pos the top visible line
setTopLine(FXint pos)1775 void FXText::setTopLine(FXint pos){
1776   setPosition(pos_x,-rowFromPos(pos)*font->getFontHeight());
1777   }
1778 
1779 
1780 // Get top line
getTopLine() const1781 FXint FXText::getTopLine() const {
1782   return visrows[0];
1783   }
1784 
1785 
1786 // Make line containing pos the bottom visible line
setBottomLine(FXint pos)1787 void FXText::setBottomLine(FXint pos){
1788   setPosition(pos_x,getVisibleHeight()-marginbottom-margintop-font->getFontHeight()-rowFromPos(pos)*font->getFontHeight());
1789   }
1790 
1791 
1792 // Get bottom line
getBottomLine() const1793 FXint FXText::getBottomLine() const {
1794   return visrows[nvisrows-1];
1795   }
1796 
1797 
1798 // Center line containing pos to center of the screen
setCenterLine(FXint pos)1799 void FXText::setCenterLine(FXint pos){
1800   setPosition(pos_x,((getVisibleHeight()-marginbottom-margintop)/2)-rowFromPos(pos)*font->getFontHeight());
1801   }
1802 
1803 
1804 // Return true if line containing position is fully visible
isPosVisible(FXint pos) const1805 FXbool FXText::isPosVisible(FXint pos) const {
1806   if(visrows[0]<=pos && pos<=visrows[nvisrows]){
1807     FXint vy=getVisibleY();
1808     FXint vh=getVisibleHeight();
1809     FXint y=getYOfPos(pos);
1810     return vy+margintop<=y && y<=vy+vh-marginbottom-font->getFontHeight();
1811     }
1812   return false;
1813   }
1814 
1815 
1816 // Force position to become fully visible
makePositionVisible(FXint pos)1817 void FXText::makePositionVisible(FXint pos){
1818   FXint vx=getVisibleX();
1819   FXint vy=getVisibleY();
1820   FXint vw=getVisibleWidth();
1821   FXint vh=getVisibleHeight();
1822   FXint x=getXOfPos(pos);
1823   FXint y=getYOfPos(pos);
1824   FXint h=font->getFontHeight();
1825   FXint ny=pos_y;
1826   FXint nx=pos_x;
1827 
1828   // Check vertical visibility
1829   if(y<vy+margintop){
1830     ny=pos_y+vy+margintop-y;
1831     nx=0;
1832     }
1833   else if(y>vy+vh-marginbottom-h){
1834     ny=pos_y+vy+vh-marginbottom-h-y;
1835     nx=0;
1836     }
1837 
1838   // Check horizontal visibility
1839   if(x<vx+marginleft){
1840     nx=pos_x+vx+marginleft-x;
1841     }
1842   else if(x>vx+vw-marginright){
1843     nx=pos_x+vx+vw-marginright-x;
1844     }
1845 
1846   // If needed, scroll
1847   if(nx!=pos_x || ny!=pos_y){
1848     setPosition(nx,ny);
1849     }
1850   }
1851 
1852 
1853 // Move content
moveContents(FXint x,FXint y)1854 void FXText::moveContents(FXint x,FXint y){
1855   FXint delta=-y/font->getFontHeight()-toprow;
1856   FXint vx=getVisibleX();
1857   FXint vy=getVisibleY();
1858   FXint vw=getVisibleWidth();
1859   FXint vh=getVisibleHeight();
1860   FXint dx=x-pos_x;
1861   FXint dy=y-pos_y;
1862   FXint i;
1863 
1864   // Erase fragments of cursor overhanging margins
1865   eraseCursorOverhang();
1866 
1867   // Scrolled up one or more lines
1868   if(delta<0){
1869     if(toprow+delta<=0){
1870       toppos=0;
1871       toprow=0;
1872       }
1873     else{
1874       toppos=prevRow(toppos,-delta);
1875       toprow=toprow+delta;
1876       }
1877     if(-delta<nvisrows){
1878       for(i=nvisrows; i>=-delta; i--) visrows[i]=visrows[delta+i];
1879       calcVisRows(0,-delta);
1880       }
1881     else{
1882       calcVisRows(0,nvisrows);
1883       }
1884     }
1885 
1886   // Scrolled down one or more lines
1887   else if(delta>0){
1888     if(toprow+delta>=nrows-1){
1889       toppos=rowStart(length);
1890       toprow=nrows-1;
1891       }
1892     else{
1893       toppos=nextRow(toppos,delta);
1894       toprow=toprow+delta;
1895       }
1896     if(delta<nvisrows){
1897       for(i=0; i<=nvisrows-delta; i++) visrows[i]=visrows[delta+i];
1898       calcVisRows(nvisrows-delta,nvisrows);
1899       }
1900     else{
1901       calcVisRows(0,nvisrows);
1902       }
1903     }
1904 
1905   // This is now the new keep position
1906   keeppos=toppos;
1907 
1908   // Hopefully, all is still in range
1909   FXASSERT(0<=toprow && toprow<=nrows);
1910   FXASSERT(0<=toppos && toppos<=length);
1911 
1912   // Scroll stuff in the bar only vertically
1913   scroll(0,vy+margintop,vx,vh-margintop-marginbottom,0,dy);
1914 
1915   // Scroll the text
1916   scroll(vx+marginleft,vy+margintop,vw-marginleft-marginright,vh-margintop-marginbottom,dx,dy);
1917 
1918   pos_x=x;
1919   pos_y=y;
1920   }
1921 
1922 /*******************************************************************************/
1923 
1924 // Move the cursor
setCursorPos(FXint pos,FXbool notify)1925 void FXText::setCursorPos(FXint pos,FXbool notify){
1926   pos=validPos(pos);
1927   if(cursorpos!=pos){
1928     if(isEditable()) drawCursor(0);
1929     if(options&TEXT_SHOWACTIVE){ updateRow(cursorrow); }
1930     FXint cursorstartpos=rowStart(pos);
1931     cursorrow=rowFromPos(cursorstartpos);
1932     cursorcol=columnFromPos(cursorstartpos,pos);
1933     cursorvcol=cursorcol;
1934     cursorpos=pos;
1935     prefcol=-1;
1936     if(options&TEXT_SHOWACTIVE){ updateRow(cursorrow); }
1937     if(isEditable()) drawCursor(FLAG_CARET);
1938     if(target && notify){
1939       target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)cursorpos);
1940       }
1941     }
1942   blink=FLAG_CARET;
1943   }
1944 
1945 
1946 // Set cursor row, column
setCursorRowColumn(FXint row,FXint col,FXbool notify)1947 void FXText::setCursorRowColumn(FXint row,FXint col,FXbool notify){
1948   row=FXCLAMP(0,row,nrows-1);
1949   col=FXMAX(col,0);
1950   if((row!=cursorrow) || (col!=cursorvcol)){
1951     FXint newstart=posFromRow(row);             // Row start of new row
1952     FXint newpos=posFromColumn(newstart,col);   // Position of column on that row
1953     setCursorPos(newpos,notify);
1954     cursorvcol=col;
1955     }
1956   }
1957 
1958 
1959 // Set cursor row
setCursorRow(FXint row,FXbool notify)1960 void FXText::setCursorRow(FXint row,FXbool notify){
1961   setCursorRowColumn(row,(0<=prefcol)?prefcol:cursorcol,notify);
1962   }
1963 
1964 
1965 // Set cursor column
setCursorColumn(FXint col,FXbool notify)1966 void FXText::setCursorColumn(FXint col,FXbool notify){
1967   setCursorRowColumn(cursorrow,col,notify);
1968   }
1969 
1970 
1971 // Move cursor
moveCursor(FXint pos,FXbool notify)1972 void FXText::moveCursor(FXint pos,FXbool notify){
1973   setCursorPos(pos,notify);
1974   setAnchorPos(pos);
1975   makePositionVisible(cursorpos);
1976   killSelection(notify);
1977   flashMatching();
1978   }
1979 
1980 
1981 // Move cursor to row and column, and scroll into view
moveCursorRowColumn(FXint row,FXint col,FXbool notify)1982 void FXText::moveCursorRowColumn(FXint row,FXint col,FXbool notify){
1983   setCursorRowColumn(row,col,notify);
1984   setAnchorRowColumn(row,col);
1985   makePositionVisible(cursorpos);
1986   killSelection(notify);
1987   flashMatching();
1988   }
1989 
1990 
1991 // Move cursor and select
moveCursorAndSelect(FXint pos,FXuint sel,FXbool notify)1992 void FXText::moveCursorAndSelect(FXint pos,FXuint sel,FXbool notify){
1993   killHighlight();
1994   setCursorPos(pos,notify);
1995   makePositionVisible(cursorpos);
1996   extendSelection(cursorpos,sel,notify);
1997   }
1998 
1999 
2000 // Move cursor to row and column, and extend the block selection to this point
moveCursorRowColumnAndSelect(FXint row,FXint col,FXbool notify)2001 void FXText::moveCursorRowColumnAndSelect(FXint row,FXint col,FXbool notify){
2002   killHighlight();
2003   setCursorRowColumn(row,col,notify);
2004   makePositionVisible(cursorpos);
2005   extendBlockSelection(row,col,notify);
2006   }
2007 
2008 
2009 // Set anchor position
setAnchorPos(FXint pos)2010 void FXText::setAnchorPos(FXint pos){
2011   pos=validPos(pos);
2012   if(anchorpos!=pos){
2013     FXint anchorstartpos=rowStart(pos);
2014     anchorrow=rowFromPos(anchorstartpos);
2015     anchorcol=columnFromPos(anchorstartpos,pos);
2016     anchorpos=pos;
2017     anchorvcol=anchorcol;
2018     }
2019   }
2020 
2021 
2022 // Set anchor row and column
setAnchorRowColumn(FXint row,FXint col)2023 void FXText::setAnchorRowColumn(FXint row,FXint col){
2024   row=FXCLAMP(0,row,nrows-1);
2025   col=FXMAX(col,0);
2026   if((row!=anchorrow) || (col!=anchorvcol)){
2027     FXint newstart=posFromRow(row);             // Row start of new row
2028     FXint newpos=posFromColumn(newstart,col);   // Position of column on that row
2029     setAnchorPos(newpos);
2030     anchorvcol=col;
2031     }
2032   }
2033 
2034 /*******************************************************************************/
2035 
2036 // At position pos, ncdel old characters have been replaced by ncins new ones,
2037 // and nrdel old rows have been replaced with nrins new rows. Recalculate the
2038 // visrows[] array and ancillary buffer positioning information.
mutation(FXint pos,FXint ncins,FXint ncdel,FXint nrins,FXint nrdel)2039 void FXText::mutation(FXint pos,FXint ncins,FXint ncdel,FXint nrins,FXint nrdel){
2040   FXint th=font->getFontHeight();
2041   FXint vx=getVisibleX();
2042   FXint vy=getVisibleY();
2043   FXint vw=getVisibleWidth();
2044   FXint vh=getVisibleHeight();
2045   FXint ncdelta=ncins-ncdel;
2046   FXint nrdelta=nrins-nrdel;
2047   FXint line,i,y;
2048 
2049   FXTRACE((TOPIC_LAYOUT,"BEFORE: pos=%d ncins=%d ncdel=%d nrins=%d nrdel=%d toppos=%d toprow=%d nrows=%d nvisrows=%d length=%d\n",pos,ncins,ncdel,nrins,nrdel,toppos,toprow,nrows,nvisrows,length));
2050 
2051   FXASSERT(0<=ncins && 0<=ncdel);
2052   FXASSERT(0<=nrins && 0<=nrdel);
2053   FXASSERT(0<=pos && pos<=length);
2054 
2055   // Changed text begins below first visible line
2056   if(visrows[0]<=pos){
2057 
2058     // Changed text begins above last visible line
2059     if(pos<=visrows[nvisrows]){
2060 
2061       // Scan to find line containing start of change
2062       for(line=0; line+1<nvisrows && visrows[line+1]<=pos && visrows[line]<visrows[line+1]; line++){ }
2063       FXASSERT(0<=line && line<nvisrows);
2064 
2065       // More lines
2066       if(nrdelta>0){
2067         for(i=nvisrows; i>=line+nrins; i--) visrows[i]=visrows[i-nrdelta]+ncdelta;
2068         calcVisRows(line,line+nrins);
2069         y=vy+pos_y+margintop+(toprow+line)*th;
2070         update(vx,y,vw,vh-y);                   // Repaint bottom part
2071         FXASSERT(0<=visrows[0]);
2072         FXASSERT(visrows[nvisrows]<=length);
2073         }
2074 
2075       // Fewer lines
2076       else if(nrdelta<0){
2077         for(i=line+nrdel; i<=nvisrows; i++) visrows[i+nrdelta]=visrows[i]+ncdelta;
2078         calcVisRows(line,line+nrins);
2079         calcVisRows(nvisrows+nrdelta,nvisrows);
2080         y=vy+pos_y+margintop+(toprow+line)*th;
2081         update(vx,y,vw,vh-y);                   // Repaint bottom part
2082         FXASSERT(0<=visrows[0]);
2083         FXASSERT(visrows[nvisrows]<=length);
2084         }
2085 
2086       // Same lines
2087       else{
2088         for(i=line+nrdel; i<=nvisrows; i++) visrows[i]=visrows[i]+ncdelta;
2089         calcVisRows(line,line+nrins);
2090         if(nrins==0){
2091           y=vy+pos_y+margintop+(toprow+line)*th;
2092           update(vx,y,vw,th);                  // Repaint one line
2093           }
2094         else{
2095           y=vy+pos_y+margintop+(toprow+line)*th;
2096           update(vx,y,vw,nrins*th);             // Repaint nrins lines
2097           }
2098         FXASSERT(0<=visrows[0]);
2099         FXASSERT(visrows[nvisrows]<=length);
2100         }
2101       }
2102     }
2103 
2104   // Changed text ends above last visible line
2105   else if(pos+ncdel<visrows[nvisrows]){
2106 
2107     // Changed text ends below first visible line
2108     if(visrows[0]<pos+ncdel){
2109 
2110       // Scan to find line containing end of change
2111       for(line=nvisrows; pos+ncdel<visrows[line]; line--){ }
2112       FXASSERT(0<=line && line<nvisrows);
2113 
2114       // Enough text to keep bottom part of buffer
2115       if(line<=toprow+nrdelta){
2116         toprow+=nrdelta;
2117         toppos=prevRow(visrows[line]+ncdelta,line);
2118         keeppos=toppos;
2119         FXASSERT(0<=toprow);
2120         //FXASSERT(nextRow(0,toprow)==toppos);
2121         pos_y-=nrdelta*th;
2122         for(i=line; i<=nvisrows; i++) visrows[i]=visrows[i]+ncdelta;
2123         calcVisRows(0,line);
2124         update(vx,vy,vw,pos_y+margintop+(toprow+line)*th);
2125         if(nrdelta) update(0,vy,vx,vh);         // Repaint line numbers
2126         }
2127 
2128       // To few lines left, scroll to top of document
2129       else{
2130         toprow=0;
2131         toppos=0;
2132         keeppos=0;
2133         pos_y=0;
2134         calcVisRows(0,nvisrows);
2135         update();                               // Repaint all
2136         }
2137       }
2138 
2139     // Changed text ends above first visible line
2140     else{
2141       toprow+=nrdelta;
2142       toppos+=ncdelta;
2143       keeppos=toppos;
2144       FXASSERT(0<=toprow);
2145       //FXASSERT(nextRow(0,toprow)==toppos);
2146       for(i=0; i<=nvisrows; i++) visrows[i]+=ncdelta;
2147       FXASSERT(0<=visrows[0]);
2148       FXASSERT(visrows[nvisrows]<=length);
2149       pos_y-=nrdelta*th;
2150       if(nrdelta) update(0,vy,vx,vh);           // Repaint only line numbers
2151       }
2152     }
2153 
2154   // Changed text begins above first and ends below last visible line
2155   else{
2156     toprow=FXMAX(0,FXMIN(toprow,nrows-nvisrows));
2157     toppos=nextRow(0,toprow);
2158     keeppos=toppos;
2159     pos_y=-toprow*th;
2160     calcVisRows(0,nvisrows);
2161     update();                                   // Repaint all
2162     }
2163   FXTRACE((TOPIC_LAYOUT,"AFTER : pos=%d ncins=%d ncdel=%d nrins=%d nrdel=%d toppos=%d toprow=%d nrows=%d nvisrows=%d length=%d\n",pos,ncins,ncdel,nrins,nrdel,toppos,toprow,nrows,nvisrows,length));
2164   }
2165 
2166 
2167 // Adjust selection for change in text, if there is a selection
adjustSelection(FXTextSelection & sel,FXint pos,FXint ndel,FXint nins)2168 static void adjustSelection(FXTextSelection& sel,FXint pos,FXint ndel,FXint nins){
2169 //#define SELECTION_SNIPPED 1
2170   if(sel.startpos<=sel.endpos){
2171     if(pos+ndel<=sel.startpos){         // No overlap with change, just adjust positions
2172       sel.startpos+=nins-ndel;
2173       sel.endpos+=nins-ndel;
2174       }
2175     else if(pos<=sel.startpos){
2176       if(pos+ndel<=sel.endpos){         // First part of selection inside change
2177         sel.endpos+=nins-ndel;
2178 #ifdef SELECTION_SNIPPED
2179         sel.startpos=pos+nins;          // Snip selection
2180 #else
2181         sel.startpos=pos;               // Expand selection
2182 #endif
2183         }
2184       else{                             // Whole of selection inside change
2185 #ifdef SELECTION_SNIPPED
2186         sel.startpos=0;                 // Snip selection to empty
2187         sel.endpos=-1;
2188         sel.startcol=0;
2189         sel.endcol=-1;
2190 #else
2191         sel.startpos=pos;               // Expand selection to change
2192         sel.endpos=pos+nins;
2193 #endif
2194         }
2195       }
2196     else if(pos<sel.endpos){
2197       if(sel.endpos<=pos+ndel){         // Last part of selection inside change
2198 #ifdef SELECTION_SNIPPED
2199         sel.endpos=pos;                 // Snip selection
2200 #else
2201         sel.endpos=pos+nins;            // Expand selection
2202 #endif
2203         }
2204       else{                             // Whole of range inside selection
2205         sel.endpos+=nins-ndel;
2206         }
2207       }
2208     }
2209   }
2210 
2211 
2212 // Backs up to the begin of the line preceding the line containing pos, or the
2213 // start of the line containing pos if the preceding line terminated in a newline.
changeBeg(FXint pos) const2214 FXint FXText::changeBeg(FXint pos) const {
2215   FXint p1,p2,t;
2216   FXASSERT(0<=pos && pos<=length);
2217   p1=p2=lineStart(pos);
2218   if(options&TEXT_WORDWRAP){
2219     while(p2<pos && (t=wrap(p2))<=pos){
2220       p1=p2;
2221       p2=t;
2222       }
2223     }
2224   FXASSERT(0<=p1 && p1<=length);
2225   return p1;
2226   }
2227 
2228 
2229 // Scan forward to the end of affected area, which is the start of the next
2230 // paragraph; a change can cause the rest of the paragraph to reflow.
changeEnd(FXint pos) const2231 FXint FXText::changeEnd(FXint pos) const {
2232   FXASSERT(0<=pos && pos<=length);
2233   while(pos<length){
2234     if(getByte(pos)=='\n') return pos+1;
2235     pos++;
2236     }
2237   return length;
2238   }
2239 
2240 
2241 // Replace m characters at pos by n characters
replace(FXint pos,FXint del,const FXchar * text,FXint ins,FXint style)2242 void FXText::replace(FXint pos,FXint del,const FXchar *text,FXint ins,FXint style){
2243   FXint nrdel,nrins,ncdel,ncins,wbeg,wend,diff,wdel,hdel,wins,hins,cursorstartpos,anchorstartpos;
2244 
2245   FXTRACE((TOPIC_TEXT,"pos=%d del=%d ins=%d\n",pos,del,ins));
2246 
2247   // Delta in characters
2248   diff=ins-del;
2249 
2250   // Bracket potentially affected character range for wrapping purposes
2251   wbeg=changeBeg(pos);
2252   wend=changeEnd(pos+del);
2253 
2254   // Measure stuff before change
2255   nrdel=measureText(wbeg,wend,wdel,hdel);
2256   ncdel=wend-wbeg;
2257 
2258   FXTRACE((TOPIC_TEXT,"wbeg=%d wend=%d nrdel=%d ncdel=%d length=%d nrows=%d wdel=%d hdel=%d\n",wbeg,wend,nrdel,ncdel,length,nrows,wdel,hdel));
2259 
2260   // Move the gap to current position
2261   movegap(pos);
2262 
2263   // Grow the gap if needed
2264   if(diff>(gapend-gapstart)){ sizegap(diff+MINSIZE); }
2265 
2266   // Modify the buffer
2267   copyElms(&buffer[pos],text,ins);
2268   if(sbuffer){fillElms(&sbuffer[pos],style,ins);}
2269   gapstart+=ins;
2270   gapend+=del;
2271   length+=diff;
2272 
2273   // Shrink the gap to avoid getting too large
2274   if(MAXSIZE<(gapend-gapstart)){ sizegap(MAXSIZE); }
2275 
2276   // Measure stuff after change
2277   nrins=measureText(wbeg,wend+diff,wins,hins);
2278   ncins=wend+diff-wbeg;
2279 
2280   // Adjust number of rows now
2281   nrows+=nrins-nrdel;
2282 
2283   FXTRACE((TOPIC_TEXT,"wbeg=%d wend+diff=%d nrins=%d ncins=%d length=%d nrows=%d wins=%d hins=%d\n",wbeg,wend+diff,nrins,ncins,length,nrows,wins,hins));
2284 
2285   // Update visrows array and other stuff
2286   mutation(wbeg,ncins,ncdel,nrins,nrdel);
2287 
2288   // Fix text metrics
2289   textHeight=textHeight+hins-hdel;
2290   textWidth=FXMAX(textWidth,wins);
2291 
2292   // Reconcile scrollbars
2293   placeScrollBars(width-barwidth,height);
2294 
2295   // Fix selection ranges
2296   adjustSelection(select,pos,del,ins);
2297   adjustSelection(hilite,pos,del,ins);
2298 
2299   // Keep anchorpos at same place relative to its surrounding text.
2300   // When inside the changed region, move it to the end of the change.
2301   if(wbeg<=anchorpos){
2302     if(anchorpos<=wend){
2303       if(pos+del<=anchorpos) anchorpos+=diff;           // Beyond changed text
2304       else if(pos<=anchorpos) anchorpos=pos+ins;        // To end of changed text
2305       FXASSERT(0<=anchorpos && anchorpos<=length);
2306       anchorstartpos=rowStart(anchorpos);
2307       FXASSERT(0<=anchorstartpos && anchorstartpos<=length);
2308       anchorrow=rowFromPos(anchorstartpos);
2309       anchorcol=columnFromPos(anchorstartpos,anchorpos);
2310       anchorvcol=anchorcol;
2311       }
2312     else{
2313       anchorpos+=diff;                                  // Adjust position
2314       anchorrow+=nrins-nrdel;                           // Adjust row
2315       }
2316     }
2317 
2318   // Keep cursorpos at same place relative to its surrounding text.
2319   // When inside the changed region, move it to the end of the change.
2320   if(wbeg<=cursorpos){
2321     if(cursorpos<=wend){
2322       if(pos+del<=cursorpos) cursorpos+=diff;           // Beyond changed text
2323       else if(pos<=cursorpos) cursorpos=pos+ins;        // To end of changed text
2324       FXASSERT(0<=cursorpos && cursorpos<=length);
2325       cursorstartpos=rowStart(cursorpos);
2326       FXASSERT(0<=cursorstartpos && cursorstartpos<=length);
2327       cursorrow=rowFromPos(cursorstartpos);
2328       cursorcol=columnFromPos(cursorstartpos,cursorpos);
2329       cursorvcol=cursorcol;
2330       }
2331     else{
2332       cursorpos+=diff;                                  // Adjust position
2333       cursorrow+=nrins-nrdel;                           // Adjust row
2334       }
2335     }
2336 
2337   // Hopefully it all still makes sense
2338   FXASSERT(0<=anchorpos && anchorpos<=length);
2339   FXASSERT(0<=cursorpos && cursorpos<=length);
2340 
2341   // Forget preferred column
2342   prefcol=-1;
2343 
2344   // Text was changed
2345   modified=true;
2346   }
2347 
2348 /*******************************************************************************/
2349 
2350 // Change the text in the buffer to new text
setText(const FXchar * text,FXint num,FXbool notify)2351 FXint FXText::setText(const FXchar* text,FXint num,FXbool notify){
2352   return setStyledText(text,num,0,notify);
2353   }
2354 
2355 
2356 // Change all of the text
setText(const FXString & text,FXbool notify)2357 FXint FXText::setText(const FXString& text,FXbool notify){
2358   return setStyledText(text.text(),text.length(),0,notify);
2359   }
2360 
2361 
2362 // Change the text in the buffer to new text
setStyledText(const FXchar * text,FXint num,FXint style,FXbool notify)2363 FXint FXText::setStyledText(const FXchar* text,FXint num,FXint style,FXbool notify){
2364   FXTextChange textchange;
2365   if(num<0){ fxerror("%s::setStyledText: bad argument.\n",getClassName()); }
2366   if(!resizeElms(buffer,num+MINSIZE)){
2367     fxerror("%s::setStyledText: out of memory.\n",getClassName());
2368     }
2369   copyElms(buffer,text,num);
2370   if(sbuffer){
2371     if(!resizeElms(sbuffer,num+MINSIZE)){
2372       fxerror("%s::setStyledText: out of memory.\n",getClassName());
2373       }
2374     fillElms(sbuffer,style,num);
2375     }
2376   gapstart=num;
2377   gapend=gapstart+MINSIZE;
2378   length=num;
2379   toppos=0;
2380   toprow=0;
2381   keeppos=0;
2382   select.startpos=0;
2383   select.endpos=-1;
2384   select.startcol=0;
2385   select.endcol=-1;
2386   hilite.startpos=0;
2387   hilite.endpos=-1;
2388   hilite.startcol=0;
2389   hilite.endcol=-1;
2390   anchorpos=0;
2391   anchorrow=0;
2392   anchorcol=0;
2393   anchorvcol=0;
2394   cursorpos=0;
2395   cursorrow=0;
2396   cursorcol=0;
2397   cursorvcol=0;
2398   prefcol=-1;
2399   pos_x=0;
2400   pos_y=0;
2401   modified=false;
2402   textchange.pos=0;
2403   textchange.ndel=0;
2404   textchange.nins=num;
2405   textchange.ins=(FXchar*)text;
2406   textchange.del=(FXchar*)"";
2407   if(notify && target){
2408     target->tryHandle(this,FXSEL(SEL_INSERTED,message),(void*)&textchange);
2409     target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)cursorpos);
2410     }
2411   recalc();
2412   layout();
2413   update();
2414   return num;
2415   }
2416 
2417 
2418 // Change all of the text
setStyledText(const FXString & text,FXint style,FXbool notify)2419 FXint FXText::setStyledText(const FXString& text,FXint style,FXbool notify){
2420   return setStyledText(text.text(),text.length(),style,notify);
2421   }
2422 
2423 /*******************************************************************************/
2424 
2425 // Replace text by other text
replaceText(FXint pos,FXint del,const FXchar * text,FXint ins,FXbool notify)2426 FXint FXText::replaceText(FXint pos,FXint del,const FXchar *text,FXint ins,FXbool notify){
2427   return replaceStyledText(pos,del,text,ins,0,notify);
2428   }
2429 
2430 
2431 // Replace text by other text
replaceText(FXint pos,FXint del,const FXString & text,FXbool notify)2432 FXint FXText::replaceText(FXint pos,FXint del,const FXString& text,FXbool notify){
2433   return replaceStyledText(pos,del,text.text(),text.length(),0,notify);
2434   }
2435 
2436 
2437 // Replace m characters at pos by n characters
replaceStyledText(FXint pos,FXint del,const FXchar * text,FXint ins,FXint style,FXbool notify)2438 FXint FXText::replaceStyledText(FXint pos,FXint del,const FXchar *text,FXint ins,FXint style,FXbool notify){
2439   if(0<=pos && 0<=del && 0<=ins && pos+del<=length && text){
2440     FXTextChange textchange;
2441     textchange.pos=pos;
2442     textchange.ndel=del;
2443     textchange.nins=ins;
2444     textchange.ins=(FXchar*)text;
2445     allocElms(textchange.del,del);
2446     extractText(textchange.del,pos,del);
2447     replace(pos,del,text,ins,style);
2448     if(notify && target){
2449       target->tryHandle(this,FXSEL(SEL_REPLACED,message),(void*)&textchange);
2450       target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)cursorpos);
2451       }
2452     freeElms(textchange.del);
2453     return ins;
2454     }
2455   return 0;
2456   }
2457 
2458 
2459 // Replace m characters at pos by n characters
replaceStyledText(FXint pos,FXint del,const FXString & text,FXint style,FXbool notify)2460 FXint FXText::replaceStyledText(FXint pos,FXint del,const FXString& text,FXint style,FXbool notify){
2461   return replaceStyledText(pos,del,text.text(),text.length(),style,notify);
2462   }
2463 
2464 /*******************************************************************************/
2465 
2466 // Count number of characters equal to ch
countchars(const FXchar * beg,const FXchar * end,FXchar ch)2467 static FXint countchars(const FXchar* beg,const FXchar* end,FXchar ch){
2468   FXint result=0;
2469   while(beg<end){
2470     result+=(*beg++==ch);
2471     }
2472   return result;
2473   }
2474 
2475 
2476 // Maximum number of columns in a string
maxcolumns(const FXchar * beg,const FXchar * end,FXint tabcols)2477 static FXint maxcolumns(const FXchar* beg,const FXchar* end,FXint tabcols){
2478   FXint result=0,cols=0; FXuchar c;
2479   while(beg<end){
2480     c=*beg++;
2481     if(c=='\n'){                                // End of the line; keep track of the longest
2482       result=FXMAX(result,cols);
2483       cols=0;
2484       continue;
2485       }
2486     if(c=='\t'){                                // Advance by number of tab columns
2487       cols+=tabcols-cols%tabcols;
2488       continue;
2489       }
2490     cols++;
2491     if(c<0xC0) continue;
2492     beg++;
2493     if(c<0xE0) continue;
2494     beg++;
2495     if(c<0xF0) continue;
2496     beg++;
2497     }
2498   result=FXMAX(result,cols);                    // In case of unterminated last line
2499   return result;
2500   }
2501 
2502 
2503 // Expand tabs to spaces
2504 // Parse a character at a time, replacing tabs with the equivalent amount of spaces.
detab(FXchar * dst,FXchar * dstend,const FXchar * src,const FXchar * srcend,FXint tabcols=8)2505 static FXchar *detab(FXchar* dst,FXchar* dstend,const FXchar* src,const FXchar* srcend,FXint tabcols=8){
2506   FXint ie=0; FXuchar c;
2507   while(src<srcend && dst<dstend){
2508     c=*dst++=*src++;
2509     if(c=='\t'){                                // Generate spaces till tab-column
2510       dst--;
2511       do{*dst++=' ';}while(++ie%tabcols);
2512       continue;
2513       }
2514     if(c=='\n'){                                // Reset columns
2515       ie=0;
2516       continue;
2517       }
2518     ie++;                                       // One UTF8 character
2519     if(c<0xC0) continue;
2520     *dst++=*src++;
2521     if(c<0xE0) continue;
2522     *dst++=*src++;
2523     if(c<0xF0) continue;
2524     *dst++=*src++;
2525     }
2526   FXASSERT(dst<=dstend);
2527   FXASSERT(src<=srcend);
2528   return dst;
2529   }
2530 
2531 
2532 // Compress spaces to tabs
2533 // Parse a character at a time, replacing runs of more than 2 spaces with tabs.
entab(FXchar * dst,FXchar * dstend,const FXchar * src,const FXchar * srcend,FXint tabcols=8)2534 static FXchar* entab(FXchar* dst,FXchar* dstend,const FXchar* src,const FXchar* srcend,FXint tabcols=8){
2535   FXint is=0,ie=0,ts; FXuchar c;
2536   while(src<srcend && dst<dstend){
2537     c=*dst++=*src++;
2538     if(c==' '){                                 // Accumulate spaces
2539       ie+=1;
2540       if((ie-is)<3) continue;                   // Run of less than 3
2541       ts=is+tabcols-is%tabcols;
2542       if(ie<ts) continue;                       // Not crossing tabstop
2543       dst-=(ie-is);                             // Back up to first space of run
2544       *dst++='\t';                              // Write a tab there
2545       is=ts;                                    // Advance to tabstop
2546       dst+=(ie-is);                             // Skip over spaces
2547       continue;
2548       }
2549     if(c=='\t'){                                // Keep the tab
2550       dst-=(ie-is+1);                           // Back up to first space of the run
2551       *dst++='\t';                              // Replace by tab
2552       ie+=tabcols-ie%tabcols;                   // New tab-column
2553       is+=tabcols-is%tabcols;
2554       if(is==ie) continue;                      // Reached tabstop
2555       *dst++='\t';
2556       is+=tabcols-is%tabcols;
2557       continue;
2558       }
2559     if(c=='\n'){                                // Reset columns
2560       is=ie=0;
2561       continue;
2562       }
2563     is=++ie;                                    // One UTF8 character
2564     if(c<0xC0) continue;
2565     *dst++=*src++;
2566     if(c<0xE0) continue;
2567     *dst++=*src++;
2568     if(c<0xF0) continue;
2569     *dst++=*src++;
2570     }
2571   FXASSERT(dst<=dstend);
2572   FXASSERT(src<=srcend);
2573   return dst;
2574   }
2575 
2576 
2577 // Copy one utf8 character
wccopy(FXchar * & dst,const FXchar * & src)2578 static inline void wccopy(FXchar*& dst,const FXchar*& src){
2579   FXuchar c=*dst++=*src++;
2580   if(c>=0xC0){
2581     *dst++=*src++;
2582     if(c>=0xE0){
2583       *dst++=*src++;
2584       if(c>=0xF0){
2585         *dst++=*src++;
2586         }
2587       }
2588     }
2589   }
2590 
2591 
2592 // Skip one utf8 character
wcskip(const FXchar * & src)2593 static inline void wcskip(const FXchar*& src){
2594   FXuchar c=*src++;
2595   if(c>=0xC0){
2596     src++;
2597     if(c>=0xE0){
2598       src++;
2599       if(c>=0xF0){
2600         src++;
2601         }
2602       }
2603     }
2604   }
2605 
2606 
2607 // Copy columns up from col to endcol
copycols(FXchar * & dst,FXchar * dstend,const FXchar * & src,const FXchar * srcend,FXint ncols=2147483647)2608 static FXint copycols(FXchar*& dst,FXchar* dstend,const FXchar*& src,const FXchar* srcend,FXint ncols=2147483647){
2609   FXint nc=ncols;
2610   while(dst<dstend && 0<nc && src<srcend && *src!='\n'){
2611     wccopy(dst,src);
2612     nc--;
2613     }
2614   return ncols-nc;
2615   }
2616 
2617 
2618 // Skip columns from col to endcol
skipcols(const FXchar * & src,const FXchar * srcend,FXint ncols=2147483647)2619 static FXint skipcols(const FXchar*& src,const FXchar* srcend,FXint ncols=2147483647){
2620   FXint nc=ncols;
2621   while(0<nc && src<srcend && *src!='\n'){
2622     wcskip(src);
2623     nc--;
2624     }
2625   return ncols-nc;
2626   }
2627 
2628 
2629 // Padd output until endcol
padcols(FXchar * & dst,FXchar * dstend,FXint ncols=0)2630 static FXint padcols(FXchar*& dst,FXchar* dstend,FXint ncols=0){
2631   FXint nc=ncols;
2632   while(dst<dstend && 0<nc){
2633     *dst++=' ';
2634     nc--;
2635     }
2636   return ncols-nc;
2637   }
2638 
2639 
2640 // Remove columns startcol up to endcol from src; assume input has been detabbed.
2641 // For each line, copy up to startcol; then skip characters up to endcol,
2642 // and copy the remainder of the line, up to and including newline, if any.
removecolumns(FXchar * dst,FXchar * dstend,const FXchar * src,const FXchar * srcend,FXint startcol,FXint endcol)2643 static FXchar* removecolumns(FXchar* dst,FXchar* dstend,const FXchar* src,const FXchar* srcend,FXint startcol,FXint endcol){
2644   while(dst<dstend && src<srcend){
2645     copycols(dst,dstend,src,srcend,startcol);                   // Copy up to startcol
2646     skipcols(src,srcend,endcol-startcol);                       // Skip to endcol
2647     copycols(dst,dstend,src,srcend);                            // Copy to line end
2648     if(dst<dstend && src<srcend && *src=='\n'){                 // Copy newline
2649       *dst++=*src++;
2650       }
2651     }
2652   FXASSERT(src<=srcend);
2653   FXASSERT(dst<=dstend);
2654   return dst;
2655   }
2656 
2657 
2658 // Replicate text at src n times to dst
replicatecolumns(FXchar * dst,FXchar * dstend,const FXchar * src,const FXchar * srcend,FXint nr)2659 static FXchar* replicatecolumns(FXchar* dst,FXchar* dstend,const FXchar* src,const FXchar* srcend,FXint nr){
2660   while(dst<dstend && 0<nr){
2661     const FXchar *ptr=src;
2662     while(dst<dstend && ptr<srcend && *ptr!='\n'){
2663       wccopy(dst,ptr);
2664       }
2665     if(dst<dstend && --nr>0){
2666       *dst++='\n';
2667       }
2668     }
2669   FXASSERT(dst<=dstend);
2670   return dst;
2671   }
2672 
2673 
2674 // Extract block of columns of text from input; assume input has been detabbed.
2675 // For each line, scan to startcol, then copy characters up to endcol to the
2676 // destination. If there are fewer than startcol columns on the line, just
2677 // copy a newline to indicate an empty column on that particular line.
extractcolumns(FXchar * dst,FXchar * dstend,const FXchar * src,const FXchar * srcend,FXint startcol,FXint endcol)2678 static FXchar* extractcolumns(FXchar* dst,FXchar* dstend,const FXchar* src,const FXchar* srcend,FXint startcol,FXint endcol){
2679   while(dst<dstend && src<srcend){
2680     skipcols(src,srcend,startcol);                              // Skip to startcol
2681     copycols(dst,dstend,src,srcend,endcol-startcol);            // Copy up to endcol
2682     skipcols(src,srcend);                                       // Skip to line end
2683     if(dst<dstend && src<srcend && *src=='\n'){                 // Copy newline
2684       *dst++=*src++;
2685       }
2686     }
2687   FXASSERT(src<=srcend);
2688   FXASSERT(dst<=dstend);
2689   return dst;
2690   }
2691 
2692 
2693 // Insert same text at given column on each line.
insertcolumns(FXchar * dst,FXchar * dstend,const FXchar * src,const FXchar * srcend,const FXchar * ins,const FXchar * insend,FXint startcol,FXint inscols)2694 static FXchar* insertcolumns(FXchar* dst,FXchar* dstend,const FXchar* src,const FXchar* srcend,const FXchar *ins,const FXchar* insend,FXint startcol,FXint inscols){
2695   FXint sc;
2696   while(dst<dstend && src<srcend){
2697     sc=copycols(dst,dstend,src,srcend,startcol);                // Copy to startcol
2698     if(ins<insend && *ins!='\n'){                               // Inserted block non-empty
2699       sc+=padcols(dst,dstend,startcol-sc);                      // Pad up to startcol where insert starts
2700       sc+=copycols(dst,dstend,ins,insend,inscols);              // Insert string, up to newline
2701       }
2702     if(src<srcend && *src!='\n'){                               // Stuff past endcol
2703       padcols(dst,dstend,startcol+inscols-sc);                  // Pad to startcol+ninscols
2704       copycols(dst,dstend,src,srcend);                          // Copy the rest
2705       }
2706     if(dst<dstend && src<srcend && *src=='\n'){                 // Copy newline
2707       *dst++=*src++;
2708       }
2709     }
2710   FXASSERT(src<=srcend);
2711   FXASSERT(ins<=insend);
2712   FXASSERT(dst<=dstend);
2713   return dst;
2714   }
2715 
2716 
2717 // Replace block of columns of text with new ones; assume both source text and inserted text has been detabbed.
2718 // Copies up to inscols of new text into the destination column
replacecolumns(FXchar * dst,FXchar * dstend,const FXchar * src,const FXchar * srcend,const FXchar * ins,const FXchar * insend,FXint startcol,FXint endcol,FXint inscols)2719 static FXchar* replacecolumns(FXchar* dst,FXchar* dstend,const FXchar* src,const FXchar* srcend,const FXchar *ins,const FXchar* insend,FXint startcol,FXint endcol,FXint inscols){
2720   FXint sc,c;
2721   while(dst<dstend && (src<srcend || ins<insend)){
2722     sc=copycols(dst,dstend,src,srcend,startcol);                // Copy to startcol
2723     skipcols(src,srcend,endcol-startcol);                       // Skip to endcol
2724     if(ins<insend && *ins!='\n'){                               // Inserted block non-empty
2725       sc+=padcols(dst,dstend,startcol-sc);                      // Pad up to startcol
2726       sc+=copycols(dst,dstend,ins,insend,inscols);              // Copy inserted block, up to inscols
2727       }
2728     if(src<srcend && *src!='\n'){                               // Stuff past endcol
2729       padcols(dst,dstend,startcol+inscols-sc);                  // Pad to startcol+ninscols
2730       copycols(dst,dstend,src,srcend);                          // Copy the rest
2731       }
2732     c=0;
2733     if(dst<dstend && ins<insend && *ins=='\n'){                 // Advance over line end
2734       *dst=*ins++; c=1;
2735       }
2736     if(dst<dstend && src<srcend && *src=='\n'){
2737       *dst=*src++; c=1;
2738       }
2739     dst+=c;
2740     }
2741   FXASSERT(src<=srcend);
2742   FXASSERT(ins<=insend);
2743   FXASSERT(dst<=dstend);
2744   return dst;
2745   }
2746 
2747 
2748 // Overstrike columns starting at startcol with new text; assume inputs have been detabbed.
overstrikecolumns(FXchar * dst,FXchar * dstend,const FXchar * src,const FXchar * srcend,const FXchar * ovr,const FXchar * ovrend,FXint startcol)2749 static FXchar* overstrikecolumns(FXchar* dst,FXchar* dstend,const FXchar* src,const FXchar* srcend,const FXchar* ovr,const FXchar* ovrend,FXint startcol){
2750   FXint sc,ec; FXuchar c;
2751   while(dst<dstend && src<srcend){
2752     sc=ec=copycols(dst,dstend,src,srcend,startcol);     // Copy up to startcol
2753     if(ovr<ovrend && *ovr!='\n'){                       // Overstrike block is non-empty
2754       ec+=padcols(dst,dstend,startcol-ec);              // Pad up to column where overstrike starts
2755       ec+=copycols(dst,dstend,ovr,ovrend);              // Copy new overstruck block
2756       }
2757     if(src<srcend && *src!='\n'){                       // More stuff past startcol
2758       sc+=skipcols(src,srcend,ec-sc);                   // Skip past overstruck text
2759       copycols(dst,dstend,src,srcend);                  // Copy the rest
2760       }
2761     c=0;
2762     if(dst<dstend && src<srcend && *src=='\n'){         // Advance over line end
2763       *dst=*src++; c=1;
2764       }
2765     if(dst<dstend && ovr<ovrend && *ovr=='\n'){
2766       *dst=*ovr++; c=1;
2767       }
2768     dst+=c;
2769     }
2770   FXASSERT(src<=srcend);
2771   FXASSERT(ovr<=ovrend);
2772   FXASSERT(dst<=dstend);
2773   return dst;
2774   }
2775 
2776 /*******************************************************************************/
2777 
2778 // Replace block of columns with text
replaceTextBlock(FXint startpos,FXint endpos,FXint startcol,FXint endcol,const FXchar * text,FXint num,FXbool notify)2779 FXint FXText::replaceTextBlock(FXint startpos,FXint endpos,FXint startcol,FXint endcol,const FXchar *text,FXint num,FXbool notify){
2780   return replaceStyledTextBlock(startpos,endpos,startcol,endcol,text,num,0,notify);
2781   }
2782 
2783 
2784 // Replace block of columns with text
replaceTextBlock(FXint startpos,FXint endpos,FXint startcol,FXint endcol,const FXString & text,FXbool notify)2785 FXint FXText::replaceTextBlock(FXint startpos,FXint endpos,FXint startcol,FXint endcol,const FXString& text,FXbool notify){
2786   return replaceStyledTextBlock(startpos,endpos,startcol,endcol,text.text(),text.length(),0,notify);
2787   }
2788 
2789 
2790 // Replace block of columns with text
2791 // Calculating the size of the scratch array to assemble the replacing text is a bit
2792 // complicated; it is best understood graphically:
2793 //
2794 //             col-0          startcol
2795 //             |              |
2796 //             |              | endcol
2797 //             |              | |
2798 //             V              v v
2799 // startpos--->X--------------+-+----+-------------+  ^          ^
2800 //             |              |      |             |  |          |
2801 //             | A       A'   | R R' |  B          |  |norgrows  |
2802 //             |              |      |             |  |          |
2803 //             |              |      |             |  |          |
2804 //             +--------------+      +-------------X  v          |
2805 //             | C            |      |             ^             |
2806 //             |              |      |             |             |ninsrows
2807 // endpos- - - |- - - - - - - |- - - |- - - - - - -+             |
2808 //             |              |      |                           |
2809 //             |              |      |                           |
2810 //             +--------------+-+----+                           v
2811 //
2812 //                            <------>
2813 //                            ninscols
2814 //
2815 // Here A, B are the parts of the original text, A being the part before the selected
2816 // block and B the part after (or inside) the selected block.  R is the newly added
2817 // text, which may be more or fewer lines than the selected block.  C is any additional
2818 // lines added in case the newly added text includes more lines than the selection.
2819 // Note that A, B, and R may have lines of varying lengths [some lines may have no
2820 // part in section B, for example].
2821 //
2822 // The total amount of allocated space should account for:
2823 //
2824 //   1) Original text (A + B), plus possibly expanded tabs,
2825 //   2) Inserted text (R), plus possibly expanded tabs,
2826 //   3) Extra padding (A') after some lines in (A), up to startcol,
2827 //   4) Padding of empty lines (C), if any, up to startcol,
2828 //   5) Padding of (R), (R') up to startcol+ninscols.
2829 //   6) The block being removed
2830 //
2831 // Some lines in A, B, and R are longer than others. Rather than calculating the exact
2832 // amount of padding needed, its simpler just to over-estimate in a way which is guaranteed
2833 // to be enough; this is done by just addding the whole rectangle; so we just add an extra
2834 // (startcol+ninscols)*max(ninsrows,norgrows) as total padding for A,C, and R.
2835 //
replaceStyledTextBlock(FXint startpos,FXint endpos,FXint startcol,FXint endcol,const FXchar * text,FXint num,FXint style,FXbool notify)2836 FXint FXText::replaceStyledTextBlock(FXint startpos,FXint endpos,FXint startcol,FXint endcol,const FXchar *text,FXint num,FXint style,FXbool notify){
2837   if(0<=startpos && startpos<=endpos && endpos<=length && 0<=startcol && startcol<=endcol){
2838     FXint norgrows,norgtabs,ninsrows,ninstabs,ninscols;
2839     FXchar *insend,*orgend,*repend;
2840     FXString org,rep,ins;
2841     extractText(rep,startpos,endpos-startpos);
2842     norgrows=countchars(&rep[0],&rep[rep.length()],'\n')+1;
2843     norgtabs=countchars(&rep[0],&rep[rep.length()],'\t');
2844     ninsrows=countchars(text,text+num,'\n')+1;
2845     ninstabs=countchars(text,text+num,'\t');
2846     ninscols=maxcolumns(text,text+num,tabcolumns);
2847     ins.length(num+ninstabs*tabcolumns);
2848     org.length(endpos-startpos+norgtabs*tabcolumns);
2849     insend=detab(&ins[0],&ins[ins.length()],text,text+num,tabcolumns);
2850     orgend=detab(&org[0],&org[org.length()],&rep[0],&rep[rep.length()],tabcolumns);
2851 
2852 //    (orgend-&org[0]) + (startcol+ninscols+1)*Math::imax(ninsrows,norgrows) + (insend-&ins[0])
2853 
2854     rep.length(endpos-startpos+num+(norgtabs+ninstabs)*tabcolumns+(startcol+ninscols+1)*FXMAX(ninsrows,norgrows));
2855     repend=replacecolumns(&rep[0],&rep[rep.length()],&org[0],orgend,&ins[0],insend,startcol,endcol,ninscols);
2856     if(!(options&TEXT_NO_TABS)){
2857       repend=entab(&rep[0],repend,&rep[0],repend,tabcolumns);
2858       }
2859     return replaceStyledText(startpos,endpos-startpos,&rep[0],repend-&rep[0],style,notify);
2860     }
2861   return 0;
2862   }
2863 
2864 
2865 // Replace block of columns with text
replaceStyledTextBlock(FXint startpos,FXint endpos,FXint startcol,FXint endcol,const FXString & text,FXint style,FXbool notify)2866 FXint FXText::replaceStyledTextBlock(FXint startpos,FXint endpos,FXint startcol,FXint endcol,const FXString& text,FXint style,FXbool notify){
2867   return replaceStyledTextBlock(startpos,endpos,startcol,endcol,text.text(),text.length(),style,notify);
2868   }
2869 
2870 /*******************************************************************************/
2871 
2872 // Overstrike text block
overstrikeTextBlock(FXint startpos,FXint endpos,FXint startcol,FXint endcol,const FXchar * text,FXint n,FXbool notify)2873 FXint FXText::overstrikeTextBlock(FXint startpos,FXint endpos,FXint startcol,FXint endcol,const FXchar *text,FXint n,FXbool notify){
2874   return overstrikeStyledTextBlock(startpos,endpos,startcol,endcol,text,n,0,notify);
2875   }
2876 
2877 
2878 // Overstrike text block
overstrikeTextBlock(FXint startpos,FXint endpos,FXint startcol,FXint endcol,const FXString & text,FXbool notify)2879 FXint FXText::overstrikeTextBlock(FXint startpos,FXint endpos,FXint startcol,FXint endcol,const FXString& text,FXbool notify){
2880   return overstrikeStyledTextBlock(startpos,endpos,startcol,endcol,text.text(),text.length(),0,notify);
2881   }
2882 
2883 
2884 // Overstrike styled text block
overstrikeStyledTextBlock(FXint startpos,FXint endpos,FXint startcol,FXint endcol,const FXchar * text,FXint n,FXint style,FXbool notify)2885 FXint FXText::overstrikeStyledTextBlock(FXint startpos,FXint endpos,FXint startcol,FXint endcol,const FXchar *text,FXint n,FXint style,FXbool notify){
2886   if(0<=startpos && startpos<=endpos && endpos<=length && 0<=startcol && startcol<=endcol){
2887     FXint norgrows,norgtabs,novrrows,novrtabs,novrcols;
2888     FXchar *ovrend,*orgend,*repend;
2889     FXString org,rep,ovr;
2890 
2891     extractText(rep,startpos,endpos-startpos);
2892 
2893     norgrows=countchars(&rep[0],&rep[rep.length()],'\n')+1;
2894     norgtabs=countchars(&rep[0],&rep[rep.length()],'\t');
2895 
2896     novrrows=countchars(text,text+n,'\n')+1;
2897     novrtabs=countchars(text,text+n,'\t');
2898     novrcols=maxcolumns(text,text+n,tabcolumns);
2899 
2900     ovr.length(n+novrtabs*tabcolumns);
2901     org.length(endpos-startpos+norgtabs*tabcolumns);
2902 
2903     ovrend=detab(&ovr[0],&ovr[ovr.length()],text,text+n,tabcolumns);
2904     orgend=detab(&org[0],&org[org.length()],&rep[0],&rep[rep.length()],tabcolumns);
2905 
2906 //    Math::imax((orgend-&org[0]),(startcol+novrcols+1)*Math::imax(novrrows,norgrows)+(ovrend-&ovr[0]))
2907 
2908     // Estimate sux
2909     rep.length(endpos-startpos+n+(norgtabs+novrtabs)*tabcolumns+(startcol+novrcols+1)*FXMAX(novrrows,norgrows));
2910 
2911     repend=overstrikecolumns(&rep[0],&rep[rep.length()],&org[0],orgend,&ovr[0],ovrend,startcol);
2912 
2913     if(!(options&TEXT_NO_TABS)){
2914       repend=entab(&rep[0],repend,&rep[0],repend,tabcolumns);
2915       }
2916     return replaceStyledText(startpos,endpos-startpos,&rep[0],repend-&rep[0],style,notify);
2917     }
2918   return 0;
2919   }
2920 
2921 
2922 // Overstrike styled text block
overstrikeStyledTextBlock(FXint startpos,FXint endpos,FXint startcol,FXint endcol,const FXString & text,FXint style,FXbool notify)2923 FXint FXText::overstrikeStyledTextBlock(FXint startpos,FXint endpos,FXint startcol,FXint endcol,const FXString& text,FXint style,FXbool notify){
2924   return overstrikeStyledTextBlock(startpos,endpos,startcol,endcol,text.text(),text.length(),style,notify);
2925   }
2926 
2927 
2928 /*******************************************************************************/
2929 
2930 // Add text at the end
appendText(const FXchar * text,FXint num,FXbool notify)2931 FXint FXText::appendText(const FXchar *text,FXint num,FXbool notify){
2932   return appendStyledText(text,num,0,notify);
2933   }
2934 
2935 
2936 // Add text at the end
appendText(const FXString & text,FXbool notify)2937 FXint FXText::appendText(const FXString& text,FXbool notify){
2938   return appendStyledText(text.text(),text.length(),0,notify);
2939   }
2940 
2941 
2942 // Add text at the end
appendStyledText(const FXchar * text,FXint num,FXint style,FXbool notify)2943 FXint FXText::appendStyledText(const FXchar *text,FXint num,FXint style,FXbool notify){
2944   FXTextChange textchange;
2945   if(num<0){ fxerror("%s::appendStyledText: bad argument.\n",getClassName()); }
2946   textchange.pos=length;
2947   textchange.ndel=0;
2948   textchange.nins=num;
2949   textchange.ins=(FXchar*)text;
2950   textchange.del=(FXchar*)"";
2951   replace(length,0,text,num,style);
2952   if(notify && target){
2953     target->tryHandle(this,FXSEL(SEL_INSERTED,message),(void*)&textchange);
2954     target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)cursorpos);
2955     }
2956   return num;
2957   }
2958 
2959 
2960 // Add text at the end
appendStyledText(const FXString & text,FXint style,FXbool notify)2961 FXint FXText::appendStyledText(const FXString& text,FXint style,FXbool notify){
2962   return appendStyledText(text.text(),text.length(),style,notify);
2963   }
2964 
2965 /*******************************************************************************/
2966 
2967 // Insert some text at pos
insertText(FXint pos,const FXchar * text,FXint num,FXbool notify)2968 FXint FXText::insertText(FXint pos,const FXchar *text,FXint num,FXbool notify){
2969   return insertStyledText(pos,text,num,0,notify);
2970   }
2971 
2972 
2973 // Insert some text at pos
insertText(FXint pos,const FXString & text,FXbool notify)2974 FXint FXText::insertText(FXint pos,const FXString& text,FXbool notify){
2975   return insertStyledText(pos,text.text(),text.length(),0,notify);
2976   }
2977 
2978 
2979 // Insert some text at pos
insertStyledText(FXint pos,const FXchar * text,FXint num,FXint style,FXbool notify)2980 FXint FXText::insertStyledText(FXint pos,const FXchar *text,FXint num,FXint style,FXbool notify){
2981   if(0<=pos && pos<=length && 0<=num && text){
2982     FXTextChange textchange;
2983     textchange.pos=pos;
2984     textchange.ndel=0;
2985     textchange.nins=num;
2986     textchange.ins=(FXchar*)text;
2987     textchange.del=(FXchar*)"";
2988     replace(pos,0,text,num,style);
2989     if(notify && target){
2990       target->tryHandle(this,FXSEL(SEL_INSERTED,message),(void*)&textchange);
2991       target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)cursorpos);
2992       }
2993     return num;
2994     }
2995   return 0;
2996   }
2997 
2998 
2999 // Insert some text at pos
insertStyledText(FXint pos,const FXString & text,FXint style,FXbool notify)3000 FXint FXText::insertStyledText(FXint pos,const FXString& text,FXint style,FXbool notify){
3001   return insertStyledText(pos,text.text(),text.length(),style,notify);
3002   }
3003 
3004 /*******************************************************************************/
3005 
3006 // Insert text columns at startcol in line starting at startpos to endpos
insertTextBlock(FXint startpos,FXint endpos,FXint startcol,const FXchar * text,FXint num,FXbool notify)3007 FXint FXText::insertTextBlock(FXint startpos,FXint endpos,FXint startcol,const FXchar *text,FXint num,FXbool notify){
3008   return insertStyledTextBlock(startpos,endpos,startcol,text,num,0,notify);
3009   }
3010 
3011 
3012 // Insert text columns at startcol in line starting at startpos to endpos
insertTextBlock(FXint startpos,FXint endpos,FXint startcol,const FXString & text,FXbool notify)3013 FXint FXText::insertTextBlock(FXint startpos,FXint endpos,FXint startcol,const FXString& text,FXbool notify){
3014   return insertStyledTextBlock(startpos,endpos,startcol,text.text(),text.length(),0,notify);
3015   }
3016 
3017 
3018 // Insert text columns at startcol in line starting at startpos to endpos with given style
insertStyledTextBlock(FXint startpos,FXint endpos,FXint startcol,const FXchar * text,FXint num,FXint style,FXbool notify)3019 FXint FXText::insertStyledTextBlock(FXint startpos,FXint endpos,FXint startcol,const FXchar *text,FXint num,FXint style,FXbool notify){
3020   if(0<=startpos && startpos<=endpos && endpos<=length && 0<=startcol){
3021     FXint norgrows,norgtabs,ninstabs,ninscols;
3022     FXchar *orgend,*repend;
3023     FXString org,rep;
3024     extractText(rep,startpos,endpos-startpos);
3025     norgrows=countchars(&rep[0],&rep[rep.length()],'\n')+1;
3026     norgtabs=countchars(&rep[0],&rep[rep.length()],'\t');
3027     org.length(endpos-startpos+norgtabs*tabcolumns);
3028     orgend=detab(&org[0],&org[org.length()],&rep[0],&rep[rep.length()],tabcolumns);
3029     ninstabs=countchars(text,text+num,'\t');
3030     ninscols=maxcolumns(text,text+num,tabcolumns);
3031     rep.length(orgend-&org[0]+norgrows*(num+ninstabs*tabcolumns));
3032     repend=insertcolumns(&rep[0],&rep[rep.length()],&org[0],orgend,text,text+num,startcol,ninscols);
3033     if(!(options&TEXT_NO_TABS)){
3034       repend=entab(&rep[0],repend,&rep[0],repend,tabcolumns);
3035       }
3036     return replaceStyledText(startpos,endpos-startpos,&rep[0],repend-&rep[0],style,notify);
3037     }
3038   return 0;
3039   }
3040 
3041 
3042 // Insert text columns at startcol in line starting at startpos to endpos with given style
insertStyledTextBlock(FXint startpos,FXint endpos,FXint startcol,const FXString & text,FXint style,FXbool notify)3043 FXint FXText::insertStyledTextBlock(FXint startpos,FXint endpos,FXint startcol,const FXString& text,FXint style,FXbool notify){
3044   return insertStyledTextBlock(startpos,endpos,startcol,text.text(),text.length(),style,notify);
3045   }
3046 
3047 /*******************************************************************************/
3048 
3049 // Change style of text range
changeStyle(FXint pos,FXint num,FXint style)3050 FXint FXText::changeStyle(FXint pos,FXint num,FXint style){
3051   if(0<=pos && 0<=num && pos+num<=length){
3052     if(sbuffer){
3053       if(pos+num<=gapstart){
3054         fillElms(sbuffer+pos,style,num);
3055         }
3056       else if(gapstart<=pos){
3057         fillElms(sbuffer+pos-gapstart+gapend,style,num);
3058         }
3059       else{
3060         fillElms(sbuffer+pos,style,gapstart-pos);
3061         fillElms(sbuffer+gapend,style,pos+num-gapstart);
3062         }
3063       updateRange(pos,pos+num);
3064       }
3065     return num;
3066     }
3067   return 0;
3068   }
3069 
3070 
3071 // Change style of text range from style-array
changeStyle(FXint pos,const FXchar * style,FXint num)3072 FXint FXText::changeStyle(FXint pos,const FXchar* style,FXint num){
3073   if(0<=pos && 0<=num && pos+num<=length){
3074     if(sbuffer && style){
3075       if(pos+num<=gapstart){
3076         copyElms(sbuffer+pos,style,num);
3077         }
3078       else if(gapstart<=pos){
3079         copyElms(sbuffer+gapend-gapstart+pos,style,num);
3080         }
3081       else{
3082         copyElms(sbuffer+pos,style,gapstart-pos);
3083         copyElms(sbuffer+gapend,style+gapstart-pos,pos+num-gapstart);
3084         }
3085       updateRange(pos,pos+num);
3086       }
3087     return num;
3088     }
3089   return 0;
3090   }
3091 
3092 
3093 // Change style of text range from style-array
changeStyle(FXint pos,const FXString & style)3094 FXint FXText::changeStyle(FXint pos,const FXString& style){
3095   return changeStyle(pos,style.text(),style.length());
3096   }
3097 
3098 /*******************************************************************************/
3099 
3100 // Remove some text at pos
removeText(FXint pos,FXint num,FXbool notify)3101 FXint FXText::removeText(FXint pos,FXint num,FXbool notify){
3102   if(0<=pos && 0<=num && pos+num<=length){
3103     FXTextChange textchange;
3104     textchange.pos=pos;
3105     textchange.ndel=num;
3106     textchange.nins=0;
3107     textchange.ins=(FXchar*)"";
3108     allocElms(textchange.del,num);
3109     extractText(textchange.del,pos,num);
3110     replace(pos,num,NULL,0,0);
3111     if(notify && target){
3112       target->tryHandle(this,FXSEL(SEL_DELETED,message),(void*)&textchange);
3113       target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)cursorpos);
3114       }
3115     freeElms(textchange.del);
3116     return num;
3117     }
3118   return 0;
3119   }
3120 
3121 
3122 // Remove columns startcol to endcol from lines starting at startpos to endpos
removeTextBlock(FXint startpos,FXint endpos,FXint startcol,FXint endcol,FXbool notify)3123 FXint FXText::removeTextBlock(FXint startpos,FXint endpos,FXint startcol,FXint endcol,FXbool notify){
3124   if(0<=startpos && startpos<=endpos && endpos<=length && 0<=startcol && startcol<=endcol){
3125     FXchar *orgend,*repend;
3126     FXString src,org,rep;
3127     FXint nsrctabs;
3128     extractText(src,startpos,endpos-startpos);
3129     nsrctabs=countchars(&src[0],&src[src.length()],'\t');
3130     org.length(endpos-startpos+nsrctabs*tabcolumns);
3131     orgend=detab(&org[0],&org[org.length()],&src[0],&src[src.length()],tabcolumns);
3132     rep.length(orgend-&org[0]);
3133     repend=removecolumns(&rep[0],&rep[rep.length()],&org[0],orgend,startcol,endcol);
3134     if(!(options&TEXT_NO_TABS)){
3135       repend=entab(&rep[0],repend,&rep[0],repend,tabcolumns);
3136       }
3137     return replaceStyledText(startpos,endpos-startpos,&rep[0],repend-&rep[0],0,notify);
3138     }
3139   return 0;
3140   }
3141 
3142 /*******************************************************************************/
3143 
3144 // Remove all text from the buffer
clearText(FXbool notify)3145 FXint FXText::clearText(FXbool notify){
3146   return removeText(0,length,notify);
3147   }
3148 
3149 /*******************************************************************************/
3150 
3151 // Grab range of text
extractText(FXchar * text,FXint pos,FXint num) const3152 void FXText::extractText(FXchar *text,FXint pos,FXint num) const {
3153   if(0<=pos && 0<=num && pos+num<=length && text){
3154     if(pos+num<=gapstart){
3155       copyElms(text,buffer+pos,num);
3156       }
3157     else if(gapstart<=pos){
3158       copyElms(text,buffer+gapend-gapstart+pos,num);
3159       }
3160     else{
3161       copyElms(text,buffer+pos,gapstart-pos);
3162       copyElms(text+gapstart-pos,buffer+gapend,pos+num-gapstart);
3163       }
3164     }
3165   }
3166 
3167 
3168 // Return n bytes of contents of text buffer from position pos
extractText(FXint pos,FXint num) const3169 FXString FXText::extractText(FXint pos,FXint num) const {
3170   FXString result;
3171   if(0<=pos && 0<=num && pos+num<=length && result.length(num)){
3172     if(pos+num<=gapstart){
3173       copyElms(&result[0],buffer+pos,num);
3174       }
3175     else if(gapstart<=pos){
3176       copyElms(&result[0],buffer+gapend-gapstart+pos,num);
3177       }
3178     else{
3179       copyElms(&result[0],buffer+pos,gapstart-pos);
3180       copyElms(&result[gapstart-pos],buffer+gapend,pos+num-gapstart);
3181       }
3182     }
3183   return result;
3184   }
3185 
3186 
3187 // Grab range of style
extractText(FXString & text,FXint pos,FXint num) const3188 void FXText::extractText(FXString& text,FXint pos,FXint num) const {
3189   if(0<=pos && 0<=num && pos+num<=length && text.length(num)){
3190     if(pos+num<=gapstart){
3191       copyElms(&text[0],buffer+pos,num);
3192       }
3193     else if(gapstart<=pos){
3194       copyElms(&text[0],buffer+gapend-gapstart+pos,num);
3195       }
3196     else{
3197       copyElms(&text[0],buffer+pos,gapstart-pos);
3198       copyElms(&text[gapstart-pos],buffer+gapend,pos+num-gapstart);
3199       }
3200     }
3201   }
3202 
3203 
3204 // Grab range of style
extractStyle(FXchar * style,FXint pos,FXint num) const3205 void FXText::extractStyle(FXchar *style,FXint pos,FXint num) const {
3206   if(0<=pos && 0<=num && pos+num<=length && style && sbuffer){
3207     if(pos+num<=gapstart){
3208       copyElms(style,sbuffer+pos,num);
3209       }
3210     else if(gapstart<=pos){
3211       copyElms(style,sbuffer+gapend-gapstart+pos,num);
3212       }
3213     else{
3214       copyElms(style,sbuffer+pos,gapstart-pos);
3215       copyElms(style+gapstart-pos,sbuffer+gapend,pos+num-gapstart);
3216       }
3217     }
3218   }
3219 
3220 
3221 // Return n bytes of style info from buffer from position pos
extractStyle(FXint pos,FXint num) const3222 FXString FXText::extractStyle(FXint pos,FXint num) const {
3223   FXString result;
3224   if(0<=pos && 0<=num && pos+num<=length && sbuffer && result.length(num)){
3225     if(pos+num<=gapstart){
3226       copyElms(&result[0],sbuffer+pos,num);
3227       }
3228     else if(gapstart<=pos){
3229       copyElms(&result[0],sbuffer+gapend-gapstart+pos,num);
3230       }
3231     else{
3232       copyElms(&result[0],sbuffer+pos,gapstart-pos);
3233       copyElms(&result[gapstart-pos],sbuffer+gapend,pos+num-gapstart);
3234       }
3235     }
3236   return result;
3237   }
3238 
3239 
3240 // Grab range of style
extractStyle(FXString & style,FXint pos,FXint num) const3241 void FXText::extractStyle(FXString& style,FXint pos,FXint num) const {
3242   if(0<=pos && 0<=num && pos+num<=length && sbuffer && style.length(num)){
3243     if(pos+num<=gapstart){
3244       copyElms(&style[0],sbuffer+pos,num);
3245       }
3246     else if(gapstart<=pos){
3247       copyElms(&style[0],sbuffer+gapend-gapstart+pos,num);
3248       }
3249     else{
3250       copyElms(&style[0],sbuffer+pos,gapstart-pos);
3251       copyElms(&style[gapstart-pos],sbuffer+gapend,pos+num-gapstart);
3252       }
3253     }
3254   }
3255 
3256 
3257 // Extract block of columns
extractTextBlock(FXString & text,FXint startpos,FXint endpos,FXint startcol,FXint endcol) const3258 void FXText::extractTextBlock(FXString& text,FXint startpos,FXint endpos,FXint startcol,FXint endcol) const {
3259   if(startpos<endpos && startcol<=endcol){
3260     FXchar *textend;
3261     FXString src;
3262     FXint ntabs;
3263     extractText(src,startpos,endpos-startpos);
3264     ntabs=countchars(&src[0],&src[endpos-startpos],'\t');
3265     text.length(endpos-startpos+ntabs*tabcolumns);
3266     textend=detab(&text[0],&text[text.length()],&src[0],&src[endpos-startpos],tabcolumns);
3267     textend=extractcolumns(&text[0],textend,&text[0],textend,startcol,endcol);
3268     if(!(options&TEXT_NO_TABS)){
3269       textend=entab(&text[0],textend,&text[0],textend,tabcolumns);
3270       }
3271     text.trunc(textend-&text[0]);
3272     }
3273   else{
3274     text.clear();
3275     }
3276   }
3277 
3278 
3279 // Extract block of columns
extractTextBlock(FXint startpos,FXint endpos,FXint startcol,FXint endcol) const3280 FXString FXText::extractTextBlock(FXint startpos,FXint endpos,FXint startcol,FXint endcol) const {
3281   FXString text;
3282   extractTextBlock(text,startpos,endpos,startcol,endcol);
3283   return text;
3284   }
3285 
3286 /*******************************************************************************/
3287 
3288 // Retrieve text into buffer
getText(FXchar * text,FXint num) const3289 void FXText::getText(FXchar* text,FXint num) const {
3290   extractText(text,0,num);
3291   }
3292 
3293 
3294 // Retrieve text into buffer
getText(FXString & text) const3295 void FXText::getText(FXString& text) const {
3296   extractText(text,0,getLength());
3297   }
3298 
3299 
3300 // We return a constant copy of the buffer
getText() const3301 FXString FXText::getText() const {
3302   return extractText(0,getLength());
3303   }
3304 
3305 /*******************************************************************************/
3306 
3307 // End of overstruck character range
overstruck(FXint start,FXint end,const FXchar * text,FXint num)3308 FXint FXText::overstruck(FXint start,FXint end,const FXchar *text,FXint num){
3309   if(!memchr(text,'\n',num)){
3310     FXint sindent,nindent,oindent,p;
3311     const FXchar *ptr;
3312     FXwchar ch;
3313 
3314     // Measure indent at pos
3315     sindent=columnFromPos(lineStart(start),start);
3316 
3317     // Measure indent at end of (first line of the) new text
3318     for(ptr=text,nindent=sindent; ptr<text+num; ptr=wcinc(ptr)){
3319       nindent+=CC(*ptr,nindent);
3320       }
3321 
3322     // Now figure out how much text to replace
3323     for(p=start,oindent=sindent; p<length; p+=getCharLen(p)){
3324       ch=getChar(p);
3325       if(ch=='\n') break;                // Stuff past the newline just gets inserted
3326       oindent+=CC(ch,oindent);
3327       if(oindent>=nindent){              // Replace string fits inside here
3328         if(oindent==nindent) p+=getCharLen(p);
3329         break;
3330         }
3331       }
3332     end=p;
3333     }
3334   return end;
3335   }
3336 
3337 /*******************************************************************************/
3338 
3339 // Shift block of lines from position start up to end by given indent
shiftText(FXint startpos,FXint endpos,FXint shift,FXbool notify)3340 FXint FXText::shiftText(FXint startpos,FXint endpos,FXint shift,FXbool notify){
3341   if(0<=startpos && startpos<endpos && endpos<=length){
3342     FXString org=extractText(startpos,endpos-startpos);
3343     FXString rep=FXString::tabbify(org,tabcolumns,0,0,shift,!(options&TEXT_NO_TABS));
3344     return replaceStyledText(startpos,endpos-startpos,rep,0,notify);
3345     }
3346   return 0;
3347   }
3348 
3349 /*******************************************************************************/
3350 
3351 // Shift case of text
caseShift(FXint startpos,FXint endpos,FXint upper,FXbool notify)3352 FXint FXText::caseShift(FXint startpos,FXint endpos,FXint upper,FXbool notify){
3353   if(startpos<endpos){
3354     FXString text;
3355     extractText(text,startpos,endpos-startpos);
3356     switch(upper){
3357       case 1: text.upper(); break;
3358       case 0: text.lower(); break;
3359       }
3360     replaceText(startpos,endpos-startpos,text,notify);
3361     return text.length();
3362     }
3363   return 0;
3364   }
3365 
3366 /*******************************************************************************/
3367 
3368 // Select all text
selectAll(FXbool notify)3369 FXbool FXText::selectAll(FXbool notify){
3370   return setSelection(0,length,notify);
3371   }
3372 
3373 
3374 // Select range of len characters starting at given position pos
setSelection(FXint pos,FXint len,FXbool notify)3375 FXbool FXText::setSelection(FXint pos,FXint len,FXbool notify){
3376   FXDragType types[4]={stringType,textType,utf8Type,utf16Type};
3377   FXint spos=validPos(pos);
3378   FXint epos=validPos(pos+len);
3379   if(select.startpos!=spos || select.endpos!=epos || select.startcol<=select.endcol){
3380     FXint what[4];
3381 
3382     // Now a range select
3383     if(select.startcol<=select.endcol){
3384       updateRange(select.startpos,select.endpos);
3385       select.startcol=0;
3386       select.endcol=-1;
3387       }
3388 
3389     // Update affected areas
3390     if((epos<=select.startpos) || (select.endpos<=spos)){
3391       updateRange(select.startpos,select.endpos);
3392       updateRange(spos,epos);
3393       }
3394     else{
3395       updateRange(select.startpos,spos);
3396       updateRange(select.endpos,epos);
3397       }
3398 
3399     // Release selection
3400     if(spos>=epos){
3401       if(hasSelection()) releaseSelection();
3402       if(notify && target){
3403         what[0]=select.startpos;
3404         what[1]=select.endpos-select.startpos;
3405         what[2]=select.startcol;
3406         what[3]=select.endcol-select.startcol;
3407         target->tryHandle(this,FXSEL(SEL_DESELECTED,message),(void*)what);
3408         }
3409       select.startpos=0;
3410       select.startcol=0;
3411       }
3412 
3413     // Acquire selection
3414     else{
3415       if(!hasSelection()) acquireSelection(types,ARRAYNUMBER(types));
3416       if(notify && target){
3417         what[0]=select.startpos;
3418         what[1]=select.endpos-select.startpos;
3419         what[2]=select.startcol;
3420         what[3]=select.endcol-select.startcol;
3421         target->tryHandle(this,FXSEL(SEL_SELECTED,message),(void*)what);
3422         }
3423       select.startpos=spos;
3424       select.endpos=epos;
3425       }
3426     return true;
3427     }
3428   return false;
3429   }
3430 
3431 
3432 // Extend the primary selection from the anchor to the given position
extendSelection(FXint pos,FXuint sel,FXbool notify)3433 FXbool FXText::extendSelection(FXint pos,FXuint sel,FXbool notify){
3434   FXint p=validPos(pos),ss=0,se=0;
3435   switch(sel){
3436     case SelectChars:                   // Selecting characters
3437       if(p<=anchorpos){
3438         ss=p;
3439         se=anchorpos;
3440         }
3441       else{
3442         ss=anchorpos;
3443         se=p;
3444         }
3445       break;
3446     case SelectWords:                   // Selecting words
3447       if(p<=anchorpos){
3448         ss=wordStart(p);
3449         se=wordEnd(anchorpos);
3450         }
3451       else{
3452         ss=wordStart(anchorpos);
3453         se=wordEnd(p);
3454         }
3455       break;
3456     case SelectRows:                    // Selecting rows
3457       if(p<=anchorpos){
3458         ss=rowStart(p);
3459         se=nextRow(anchorpos);
3460         }
3461       else{
3462         ss=rowStart(anchorpos);
3463         se=nextRow(p);
3464         }
3465       break;
3466     case SelectLines:                   // Selecting lines
3467       if(p<=anchorpos){
3468         ss=lineStart(p);
3469         se=nextLine(anchorpos);
3470         }
3471       else{
3472         ss=lineStart(anchorpos);
3473         se=nextLine(p);
3474         }
3475       break;
3476     }
3477   return setSelection(ss,se-ss,notify);
3478   }
3479 
3480 
3481 // Select block of characters within given box
setBlockSelection(FXint trow,FXint lcol,FXint brow,FXint rcol,FXbool notify)3482 FXbool FXText::setBlockSelection(FXint trow,FXint lcol,FXint brow,FXint rcol,FXbool notify){
3483   FXDragType types[4]={stringType,textType,utf8Type,utf16Type};
3484   FXint spos=lineStart(posFromRow(trow));
3485   FXint epos=lineEnd(posFromRow(brow));
3486   if(select.startpos!=spos || select.endpos!=epos || select.startcol!=lcol || select.endcol!=rcol){
3487     FXint what[4];
3488 
3489     // Update affected areas
3490     updateLines(select.startpos,select.endpos);
3491     updateLines(spos,epos);
3492 
3493     // Release selection
3494     if(spos>epos || lcol>rcol){
3495       if(hasSelection()) releaseSelection();
3496       if(notify && target){
3497         what[0]=select.startpos;
3498         what[1]=select.endpos-select.startpos;
3499         what[2]=select.startcol;
3500         what[3]=select.endcol-select.startcol;
3501         target->tryHandle(this,FXSEL(SEL_DESELECTED,message),(void*)what);
3502         }
3503       select.startpos=0;
3504       select.endpos=-1;
3505       select.startcol=0;
3506       select.endcol=-1;
3507       }
3508 
3509     // Acquire selection
3510     else{
3511       if(!hasSelection()) acquireSelection(types,ARRAYNUMBER(types));
3512       if(notify && target){
3513         what[0]=select.startpos;
3514         what[1]=select.endpos-select.startpos;
3515         what[2]=select.startcol;
3516         what[3]=select.endcol-select.startcol;
3517         target->tryHandle(this,FXSEL(SEL_SELECTED,message),(void*)what);
3518         }
3519       select.startpos=spos;
3520       select.endpos=epos;
3521       select.startcol=lcol;
3522       select.endcol=rcol;
3523       }
3524     FXTRACE((TOPIC_TEXT,"select: startpos=%d endpos=%d startcol=%d endcol=%d\n",select.startpos,select.endpos,select.startcol,select.endcol));
3525     return true;
3526     }
3527   return false;
3528   }
3529 
3530 
3531 // Extend primary selection from anchor to given row, column
extendBlockSelection(FXint row,FXint col,FXbool notify)3532 FXbool FXText::extendBlockSelection(FXint row,FXint col,FXbool notify){
3533   FXint trow,brow,lcol,rcol;
3534   FXMINMAX(trow,brow,anchorrow,row);
3535   FXMINMAX(lcol,rcol,anchorvcol,col);
3536   return setBlockSelection(trow,lcol,brow,rcol,notify);
3537   }
3538 
3539 
3540 // Kill the selection
killSelection(FXbool notify)3541 FXbool FXText::killSelection(FXbool notify){
3542   if(select.startpos<=select.endpos){
3543     FXint what[4];
3544     if(hasSelection()) releaseSelection();
3545     if(notify && target){
3546       what[0]=select.startpos;
3547       what[1]=select.endpos-select.startpos;
3548       what[2]=select.startcol;
3549       what[3]=select.endcol-select.startcol;
3550       target->tryHandle(this,FXSEL(SEL_DESELECTED,message),(void*)what);
3551       }
3552     updateRange(select.startpos,select.endpos);
3553     select.startpos=0;
3554     select.endpos=-1;
3555     select.startcol=0;
3556     select.endcol=-1;
3557     return true;
3558     }
3559   return false;
3560   }
3561 
3562 
3563 // Position is selected if inside character range AND character range non-empty AND NOT column-selection,
3564 // OR inside character range AND inside column range AND non-empty column-range.
isPosSelected(FXint pos,FXint col) const3565 FXbool FXText::isPosSelected(FXint pos,FXint col) const {
3566   return select.startpos<=pos && pos<=select.endpos && ((select.startpos<select.endpos && select.startcol>select.endcol) || (select.startcol<=col && col<=select.endcol && select.startcol<select.endcol));
3567   }
3568 
3569 
3570 // Position is range-selected if inside character range AND character range non-empty AND NOT column-selection
isPosSelected(FXint pos) const3571 FXbool FXText::isPosSelected(FXint pos) const {
3572   return select.startpos<=pos && pos<=select.endpos && select.startpos<select.endpos && select.startcol>select.endcol;
3573   }
3574 
3575 
3576 // Get selected text
getSelectedText() const3577 FXString FXText::getSelectedText() const {
3578   if((select.startcol<select.endcol) && (select.startpos<=select.endpos)){
3579     return extractTextBlock(select.startpos,select.endpos,select.startcol,select.endcol);
3580     }
3581   if((select.startcol>select.endcol) && (select.startpos<select.endpos)){
3582     return extractText(select.startpos,select.endpos-select.startpos);
3583     }
3584   return FXString::null;
3585   }
3586 
3587 /*******************************************************************************/
3588 
3589 // Copy selection to clipboard
copySelection()3590 FXbool FXText::copySelection(){
3591   FXDragType types[4]={stringType,textType,utf8Type,utf16Type};
3592   if(select.startpos<=select.endpos){
3593     if(acquireClipboard(types,ARRAYNUMBER(types))){
3594       clipped=getSelectedText();
3595       return true;
3596       }
3597     }
3598   return false;
3599   }
3600 
3601 
3602 // Copy selection to clipboard and delete it
cutSelection(FXbool notify)3603 FXbool FXText::cutSelection(FXbool notify){
3604   if(copySelection()){
3605     return deleteSelection(notify);
3606     }
3607   return false;
3608   }
3609 
3610 
3611 // Replace selection by other text
replaceSelection(const FXString & text,FXbool notify)3612 FXbool FXText::replaceSelection(const FXString& text,FXbool notify){
3613   if(select.startpos<=select.endpos){
3614     FXint pos,cols,ins;
3615     if(select.startcol<=select.endcol){
3616       cols=maxcolumns(text.text(),text.text()+text.length(),tabcolumns);
3617       ins=replaceTextBlock(select.startpos,select.endpos,select.startcol,select.endcol,text,notify);
3618       pos=posFromColumn(lineStart(select.startpos+ins),select.startcol+cols);
3619       moveCursor(pos,notify);
3620       return true;
3621       }
3622     if(select.startpos<select.endpos){
3623       ins=replaceText(select.startpos,select.endpos-select.startpos,text,notify);
3624       pos=select.startpos+ins;
3625       moveCursor(pos,notify);
3626       return true;
3627       }
3628     }
3629   return false;
3630   }
3631 
3632 
3633 // Delete selection
deleteSelection(FXbool notify)3634 FXbool FXText::deleteSelection(FXbool notify){
3635   if(select.startpos<=select.endpos){
3636     FXint pos,ins;
3637     if(select.startcol<select.endcol){
3638       ins=removeTextBlock(select.startpos,select.endpos,select.startcol,select.endcol,notify);
3639       pos=posFromColumn(lineStart(select.startpos+ins),select.startcol);
3640       moveCursor(pos,notify);
3641       return true;
3642       }
3643     if(select.startpos<select.endpos){
3644       removeText(select.startpos,select.endpos-select.startpos,notify);
3645       moveCursor(select.startpos,notify);
3646       return true;
3647       }
3648     }
3649   return false;
3650   }
3651 
3652 
3653 // Delete pending selection
deletePendingSelection(FXbool notify)3654 FXbool FXText::deletePendingSelection(FXbool notify){
3655   return isPosSelected(cursorpos,cursorvcol) && deleteSelection(notify);
3656   }
3657 
3658 
3659 // Paste primary ("middle-mouse") selection
pasteSelection(FXbool notify)3660 FXbool FXText::pasteSelection(FXbool notify){
3661 
3662   // Don't paste inside selection
3663   if((select.startpos>select.endpos) || (cursorpos<=select.startpos) || (select.endpos<=cursorpos)){
3664     FXString string;
3665 
3666     // Try UTF-8, then UTF-16, then 8859-1
3667     if(getDNDData(FROM_SELECTION,utf8Type,string) || getDNDData(FROM_SELECTION,utf16Type,string) || getDNDData(FROM_SELECTION,stringType,string)){
3668       FXint pos=cursorpos;
3669       FXint ins;
3670 
3671       // Overstrike mode, extent
3672       if(isOverstrike()){       // FIXME should overstrike always be block-mode?
3673         ins=overstruck(pos,pos,string.text(),string.length());
3674         ins=replaceText(pos,ins-pos,string,notify);
3675         makePositionVisible(pos+ins);
3676         setCursorPos(pos+ins,notify);
3677         setAnchorPos(pos+ins);
3678         flashMatching();
3679         return true;
3680         }
3681 
3682       // Replace text and move cursor
3683       ins=insertText(pos,string,notify);
3684       makePositionVisible(pos+ins);
3685       setCursorPos(pos+ins,notify);
3686       setAnchorPos(pos+ins);
3687       flashMatching();
3688       return true;
3689       }
3690     }
3691   return false;
3692   }
3693 
3694 
3695 // Paste clipboard
pasteClipboard(FXbool notify)3696 FXbool FXText::pasteClipboard(FXbool notify){
3697   FXString string;
3698 
3699   // Try UTF-8, then UTF-16, then 8859-1
3700   if(getDNDData(FROM_CLIPBOARD,utf8Type,string) || getDNDData(FROM_CLIPBOARD,utf16Type,string) || getDNDData(FROM_CLIPBOARD,stringType,string)){
3701     FXint pos=cursorpos;
3702     FXint ins;
3703 
3704     // De-DOS
3705 #ifdef WIN32
3706     dosToUnix(string);
3707 #endif
3708 
3709     // If there's a selection, replace it
3710     if(replaceSelection(string,notify)){
3711       return true;
3712       }
3713 
3714     // Overstrike
3715     if(isOverstrike()){         // FIXME should overstrike always be block-mode?
3716       ins=overstruck(pos,pos,string.text(),string.length());
3717       ins=replaceText(pos,ins-pos,string,notify);
3718       moveCursor(pos+ins,notify);
3719       return true;
3720       }
3721 
3722     // Default is insert
3723     ins=insertText(pos,string,notify);
3724     moveCursor(pos+ins,notify);
3725     return true;
3726     }
3727   return false;
3728   }
3729 
3730 /*******************************************************************************/
3731 
3732 // Set highlight
setHighlight(FXint pos,FXint len)3733 FXbool FXText::setHighlight(FXint pos,FXint len){
3734   FXint spos=validPos(pos);
3735   FXint epos=validPos(pos+len);
3736   if(spos!=hilite.startpos || epos!=hilite.endpos){
3737     if(epos<=hilite.startpos || hilite.endpos<=spos){
3738       updateRange(hilite.startpos,hilite.endpos);
3739       updateRange(spos,epos);
3740       }
3741     else{
3742       updateRange(hilite.startpos,spos);
3743       updateRange(hilite.endpos,epos);
3744       }
3745     hilite.startpos=spos;
3746     hilite.endpos=epos;
3747     hilite.startcol=0;
3748     hilite.endcol=-1;
3749     return true;
3750     }
3751   return false;
3752   }
3753 
3754 
3755 // Unhighlight the text
killHighlight()3756 FXbool FXText::killHighlight(){
3757   if(hilite.startpos<=hilite.endpos){
3758     updateRange(hilite.startpos,hilite.endpos);
3759     hilite.startpos=0;
3760     hilite.endpos=-1;
3761     hilite.startcol=0;
3762     hilite.endcol=-1;
3763     return true;
3764     }
3765   return false;
3766   }
3767 
3768 /*******************************************************************************/
3769 
3770 // Draw the cursor
drawCursor(FXuint state)3771 void FXText::drawCursor(FXuint state){
3772   if((state^flags)&FLAG_CARET){
3773     if(xid){
3774       FXDCWindow dc(this);
3775       if(state&FLAG_CARET)
3776         paintCursor(dc);
3777       else
3778         eraseCursor(dc);
3779       }
3780     flags^=FLAG_CARET;
3781     }
3782   }
3783 
3784 
3785 // Paint cursor glyph
paintCursor(FXDCWindow & dc) const3786 void FXText::paintCursor(FXDCWindow& dc) const {
3787   FXint th,tw,cursorx,cursory;
3788   FXwchar c;
3789   th=font->getFontHeight();
3790   cursory=getVisibleY()+margintop+pos_y+cursorrow*th;
3791   if(getVisibleY()+margintop<cursory+th && cursory<=getVisibleY()+getVisibleHeight()-marginbottom){
3792     FXASSERT(toprow<=cursorrow && cursorrow<toprow+nvisrows);
3793     FXASSERT(0<=visrows[cursorrow-toprow] && visrows[cursorrow-toprow]<=cursorpos && cursorpos<=length);
3794     tw=font->getCharWidth((cursorpos<length) && ((c=getChar(cursorpos))>=' ')?c:' ');
3795     cursorx=getVisibleX()+marginleft+pos_x+xoffset(visrows[cursorrow-toprow],cursorpos)-1;
3796     if(getVisibleX()<=cursorx+tw+2 && cursorx-2<=getVisibleX()+getVisibleWidth()){
3797       dc.setClipRectangle(getVisibleX(),getVisibleY(),getVisibleWidth(),getVisibleHeight());
3798       if(0<dc.getClipWidth() && 0<dc.getClipHeight()){
3799         dc.setForeground(cursorColor);
3800         if(options&TEXT_OVERSTRIKE){
3801           dc.drawRectangle(cursorx,cursory,tw,th-1);
3802           }
3803         else{
3804           dc.fillRectangle(cursorx,cursory,2,th);
3805           dc.fillRectangle(cursorx-2,cursory,6,1);
3806           dc.fillRectangle(cursorx-2,cursory+th-1,6,1);
3807           }
3808         }
3809       }
3810     }
3811   }
3812 
3813 
3814 // Erase cursor glyph
eraseCursor(FXDCWindow & dc) const3815 void FXText::eraseCursor(FXDCWindow& dc) const {
3816   FXint th,tw,cursorx,cursory,cx,cy,ch,cw;
3817   FXwchar c;
3818   th=font->getFontHeight();
3819   cursory=getVisibleY()+margintop+pos_y+cursorrow*th;
3820   if(getVisibleY()+margintop<cursory+th && cursory<=getVisibleY()+getVisibleHeight()-marginbottom){
3821     FXASSERT(toprow<=cursorrow && cursorrow<toprow+nvisrows);
3822     FXASSERT(0<=visrows[cursorrow-toprow] && visrows[cursorrow-toprow]<=cursorpos && cursorpos<=length);
3823     tw=font->getCharWidth((cursorpos<length) && ((c=getChar(cursorpos))>=' ')?c:' ');
3824     cursorx=getVisibleX()+marginleft+pos_x+xoffset(visrows[cursorrow-toprow],cursorpos)-1;
3825     if(getVisibleX()<=cursorx+tw+2 && cursorx-2<=getVisibleX()+getVisibleWidth()){
3826       dc.setClipRectangle(getVisibleX(),getVisibleY(),getVisibleWidth(),getVisibleHeight());
3827       if(0<dc.getClipWidth() && 0<dc.getClipHeight()){
3828         dc.setFont(font);
3829         dc.setForeground(backColor);
3830         dc.fillRectangle(cursorx-2,cursory,tw+4,th);
3831         cx=FXMAX(cursorx-2,getVisibleX()+marginleft);
3832         cy=getVisibleY()+margintop;
3833         cw=FXMIN(cursorx+tw+2,getVisibleX()+getVisibleWidth()-marginright)-cx;
3834         ch=getVisibleHeight()-margintop-marginbottom;
3835         dc.setClipRectangle(cx,cy,cw,ch);
3836         FXASSERT(toprow<=cursorrow && cursorrow<toprow+nvisrows);
3837         drawTextRow(dc,cursorrow);
3838         }
3839       }
3840     }
3841   }
3842 
3843 
3844 // Erase cursor overhang outside of margins
eraseCursorOverhang()3845 void FXText::eraseCursorOverhang(){
3846   FXint th,tw,cursorx,cursory;
3847   FXwchar c;
3848   th=font->getFontHeight();
3849   cursory=getVisibleY()+margintop+pos_y+cursorrow*th;
3850   if(getVisibleY()+margintop<cursory+th && cursory<=getVisibleY()+getVisibleHeight()-marginbottom){
3851     FXASSERT(0<=cursorrow-toprow && cursorrow-toprow<nvisrows);
3852     tw=font->getCharWidth((cursorpos<length) && ((c=getChar(cursorpos))>=' ')?c:' ');
3853     cursorx=getVisibleX()+marginleft+pos_x+xoffset(visrows[cursorrow-toprow],cursorpos)-1;
3854     if(getVisibleX()<=cursorx+tw+2 && cursorx-2<=getVisibleX()+getVisibleWidth()){
3855       FXDCWindow dc(this);
3856       if(cursorx-2<=getVisibleX()+marginleft && getVisibleX()<=cursorx+tw+2){
3857         dc.setForeground(backColor);
3858         dc.fillRectangle(getVisibleX(),cursory,marginleft,th);
3859         }
3860       if(getVisibleX()+getVisibleWidth()-marginright<=cursorx+tw+2 && cursorx-2<=getVisibleX()+getVisibleWidth()){
3861         dc.setForeground(backColor);
3862         dc.fillRectangle(getVisibleX()+getVisibleWidth()-marginright,cursory,marginright,th);
3863         }
3864       if(cursory<=getVisibleY()+margintop && getVisibleY()<=cursory+th){
3865         dc.setForeground(backColor);
3866         dc.fillRectangle(cursorx-2,getVisibleY(),tw+4,margintop);
3867         }
3868       if(getVisibleY()+getVisibleHeight()-marginbottom<=cursory+th && cursory<getVisibleY()+getVisibleHeight()){
3869         dc.setForeground(backColor);
3870         dc.fillRectangle(cursorx-2,getVisibleY()+getVisibleHeight()-marginbottom,tw+4,marginbottom);
3871         }
3872       }
3873     }
3874   }
3875 
3876 /*******************************************************************************/
3877 
3878 // Draw fragment of text in given style
drawBufferText(FXDCWindow & dc,FXint x,FXint y,FXint,FXint,FXint pos,FXint n,FXuint style) const3879 void FXText::drawBufferText(FXDCWindow& dc,FXint x,FXint y,FXint,FXint,FXint pos,FXint n,FXuint style) const {
3880   FXuint index=(style&STYLE_MASK);
3881   FXuint usedstyle=style;                                              // Style flags from style buffer
3882   FXColor color;
3883   FXchar str[2];
3884   color=0;
3885   if(hilitestyles && index){                                                    // Get colors from style table
3886     usedstyle=hilitestyles[index-1].style;                                      // Style flags now from style table
3887     if(style&STYLE_SELECTED) color=hilitestyles[index-1].selectForeColor;
3888     else if(style&STYLE_HILITE) color=hilitestyles[index-1].hiliteForeColor;
3889     if(color==0) color=hilitestyles[index-1].normalForeColor;                   // Fall back on normal foreground color
3890     }
3891   if(color==0){                                                                 // Fall back to default style
3892     if(style&STYLE_SELECTED) color=seltextColor;
3893     else if(style&STYLE_HILITE) color=hilitetextColor;
3894     if(color==0) color=textColor;                                               // Fall back to normal text color
3895     }
3896   dc.setForeground(color);
3897   if(style&STYLE_CONTROL){
3898     y+=font->getFontAscent();
3899     str[0]='^';
3900     while(pos<gapstart && 0<n){
3901       str[1]=buffer[pos]|0x40;
3902       dc.drawText(x,y,str,2);
3903       if(usedstyle&STYLE_BOLD) dc.drawText(x+1,y,str,2);
3904       x+=font->getTextWidth(str,2);
3905       pos++;
3906       n--;
3907       }
3908     while(0<n){
3909       str[1]=buffer[pos-gapstart+gapend]|0x40;
3910       dc.drawText(x,y,str,2);
3911       if(usedstyle&STYLE_BOLD) dc.drawText(x+1,y,str,2);
3912       x+=font->getTextWidth(str,2);
3913       pos++;
3914       n--;
3915       }
3916     }
3917   else{
3918     y+=font->getFontAscent();
3919     if(pos+n<=gapstart){
3920       dc.drawText(x,y,&buffer[pos],n);
3921       if(usedstyle&STYLE_BOLD) dc.drawText(x+1,y,&buffer[pos],n);
3922       }
3923     else if(pos>=gapstart){
3924       dc.drawText(x,y,&buffer[pos-gapstart+gapend],n);
3925       if(usedstyle&STYLE_BOLD) dc.drawText(x+1,y,&buffer[pos-gapstart+gapend],n);
3926       }
3927     else{
3928       dc.drawText(x,y,&buffer[pos],gapstart-pos);
3929       if(usedstyle&STYLE_BOLD) dc.drawText(x+1,y,&buffer[pos],gapstart-pos);
3930       x+=font->getTextWidth(&buffer[pos],gapstart-pos);
3931       dc.drawText(x,y,&buffer[gapend],pos+n-gapstart);
3932       if(usedstyle&STYLE_BOLD) dc.drawText(x+1,y,&buffer[gapend],pos+n-gapstart);
3933       }
3934     }
3935   }
3936 
3937 
3938 // Fill fragment of background in given style
fillBufferRect(FXDCWindow & dc,FXint x,FXint y,FXint w,FXint h,FXuint style) const3939 void FXText::fillBufferRect(FXDCWindow& dc,FXint x,FXint y,FXint w,FXint h,FXuint style) const {
3940   FXuint index=(style&STYLE_MASK);
3941   FXuint usedstyle=style;                              // Style flags from style buffer
3942   FXColor bgcolor,fgcolor;
3943   bgcolor=fgcolor=0;
3944   if(hilitestyles && index){                                    // Get colors from style table
3945     usedstyle=hilitestyles[index-1].style;                      // Style flags now from style table
3946     if(style&STYLE_SELECTED){
3947       bgcolor=hilitestyles[index-1].selectBackColor;
3948       fgcolor=hilitestyles[index-1].selectForeColor;
3949       }
3950     else if(style&STYLE_HILITE){
3951       bgcolor=hilitestyles[index-1].hiliteBackColor;
3952       fgcolor=hilitestyles[index-1].hiliteForeColor;
3953       }
3954     else if(style&STYLE_ACTIVE){
3955       bgcolor=hilitestyles[index-1].activeBackColor;
3956       }
3957     else{
3958       bgcolor=hilitestyles[index-1].normalBackColor;
3959       }
3960     if(fgcolor==0){                                             // Fall back to normal foreground color
3961       fgcolor=hilitestyles[index-1].normalForeColor;
3962       }
3963     }
3964   if(bgcolor==0){                                               // Fall back to default background colors
3965     if(style&STYLE_SELECTED) bgcolor=selbackColor;
3966     else if(style&STYLE_HILITE) bgcolor=hilitebackColor;
3967     else if(style&STYLE_ACTIVE) bgcolor=activebackColor;
3968     else bgcolor=backColor;
3969     }
3970   if(fgcolor==0){                                               // Fall back to default foreground colors
3971     if(style&STYLE_SELECTED) fgcolor=seltextColor;
3972     else if(style&STYLE_HILITE) fgcolor=hilitetextColor;
3973     if(fgcolor==0) fgcolor=textColor;                           // Fall back to text color
3974     }
3975   dc.setForeground(bgcolor);
3976   dc.fillRectangle(x,y,w,h);
3977   if(style&STYLE_INSERT){                                       // Vertical insertion point
3978     dc.setForeground(cursorColor);                              // Use cursor color for now
3979     dc.fillRectangle(x,y,1,h);
3980     }
3981   if(usedstyle&STYLE_UNDERLINE){
3982     dc.setForeground(fgcolor);
3983     dc.fillRectangle(x,y+font->getFontAscent()+1,w,1);
3984     }
3985   if(usedstyle&STYLE_STRIKEOUT){
3986     dc.setForeground(fgcolor);
3987     dc.fillRectangle(x,y+font->getFontAscent()/2,w,1);
3988     }
3989   }
3990 
3991 
3992 // Obtain text style given line range, row, column, and position
3993 // Note that for block selections, the column may be outside the text, but need
3994 // to be on non-empty lines.
styleOf(FXint beg,FXint end,FXint row,FXint col,FXint pos) const3995 FXuint FXText::styleOf(FXint beg,FXint end,FXint row,FXint col,FXint pos) const {
3996   FXuint style=0;
3997 
3998   // Current active line
3999   if((row==cursorrow) && (options&TEXT_SHOWACTIVE)) style|=STYLE_ACTIVE;
4000 
4001   // Need non-empty line
4002   if(beg<end){
4003 
4004     // Selected range or block
4005     if(select.startpos<=pos){
4006       if(select.startcol>select.endcol){
4007         if(pos<select.endpos) style|=STYLE_SELECTED;
4008         }
4009       else if(pos<=select.endpos){
4010         if(select.startcol<=col && col<select.endcol) style|=STYLE_SELECTED;
4011         if(select.startcol==col && select.endcol==col) style|=STYLE_INSERT;
4012         }
4013       }
4014 
4015     // Highlighted range or block
4016     if(hilite.startpos<=pos){
4017       if(hilite.startcol>hilite.endcol){
4018         if(pos<hilite.endpos) style|=STYLE_HILITE;
4019         }
4020       else if(pos<=hilite.endpos){
4021         if(hilite.startcol<=col && col<hilite.endcol) style|=STYLE_HILITE;
4022         }
4023       }
4024 
4025     // Inside text
4026     if(pos<end){
4027 
4028       // Get character
4029       FXuchar c=getByte(pos);
4030 
4031       // Get value from style buffer
4032       if(sbuffer) style|=getStyle(pos);
4033 
4034       // Tab or whitespace
4035       if(c=='\t') return style;
4036       if(c==' ') return style;
4037 
4038       // Control codes
4039       if(c<' ') style|=STYLE_CONTROL;
4040 
4041       // Normal character
4042       style|=STYLE_TEXT;
4043       }
4044     }
4045 
4046   return style;
4047   }
4048 
4049 
4050 // Draw line of text from the buffer, skipping over the parts outside
4051 // of the current clip rectangle.
drawTextRow(FXDCWindow & dc,FXint row) const4052 void FXText::drawTextRow(FXDCWindow& dc,FXint row) const {
4053   FXint spacew=font->getCharWidth(' ');
4054   FXint caretw=font->getCharWidth('^');
4055   FXint th=font->getFontHeight();
4056   FXint tx=getVisibleX()+marginleft+pos_x;
4057   FXint ty=getVisibleY()+margintop+pos_y+row*th;
4058   FXint leftclip=dc.getClipX();
4059   FXint riteclip=dc.getClipX()+dc.getClipWidth();
4060   FXint linebeg=visrows[row-toprow];
4061   FXint lineend=visrows[row-toprow+1];
4062   FXint linebreak=lineend;
4063   FXint tcol=0,twid=0,tadj=0;
4064   FXint cw,cc,pc,cx,px,cp,pp;
4065   FXuint curstyle,newstyle;
4066   FXwchar c;
4067 
4068   FXASSERT(toprow<=row && row<toprow+nvisrows);
4069   FXASSERT(0<=linebeg && lineend<=length);
4070 
4071   // If last character on a line is newline or space, back off by one
4072   // character position, and interpret all subsequent columns as spaces.
4073   if(linebeg<lineend){
4074     linebreak=dec(lineend);
4075     FXASSERT(linebeg<=linebreak);
4076     if(!Unicode::isSpace(getChar(linebreak))){
4077       linebreak=lineend;
4078       }
4079     }
4080 
4081   // Reset running variables
4082   cc=0;
4083   cx=tx;
4084   cp=linebeg;
4085 
4086   // Scan forward to get past left edge
4087   do{
4088     px=cx;
4089     pc=cc;
4090     pp=cp;
4091     if(cp>=linebreak){                          // Character past end of line
4092       cx+=spacew;
4093       cc+=1;
4094       continue;
4095       }
4096     c=getChar(cp);
4097     if(' '<=c){                                 // Normal character
4098       cx+=font->getCharWidth(c);
4099       cc+=1;
4100       cp+=getCharLen(cp);
4101       continue;
4102       }
4103     if(c=='\t'){                                // Tab character
4104       cx+=tabwidth-(cx-tx)%tabwidth;
4105       cc+=tabcolumns-cc%tabcolumns;
4106       cp+=1;
4107       continue;
4108       }
4109     cx+=caretw+font->getCharWidth(c|0x40);      // Control character
4110     cc+=1;
4111     cp+=1;
4112     }
4113   while(cx<leftclip);
4114 
4115   // Roll back to just before edge
4116   cx=px;
4117   cc=pc;
4118   cp=pp;
4119 
4120   // First style to display
4121   curstyle=styleOf(linebeg,lineend,row,cc,cp);
4122 
4123   // Draw segments of uniformly styled text
4124   do{
4125     newstyle=styleOf(linebeg,lineend,row,cc,cp);
4126     if(newstyle!=curstyle){                     // Found a style change!
4127       fillBufferRect(dc,px,ty,cx-px,th,curstyle);
4128       if(curstyle&STYLE_TEXT) drawBufferText(dc,px,ty,cx-px,th,pp,cp-pp,curstyle);
4129       curstyle=newstyle;
4130       pp=cp;
4131       pc=cc;
4132       px=cx;
4133       }
4134     if(cp>=linebreak){                          // Character past end of line
4135       cx+=spacew;
4136       cc+=1;
4137       continue;
4138       }
4139     c=getChar(cp);
4140     if(' '<=c){                                 // Normal character
4141       cx+=font->getCharWidth(c);
4142       cc+=1;
4143       cp+=getCharLen(cp);
4144       continue;
4145       }
4146     if(c=='\t'){                                // Tab character
4147       if(tcol==0){
4148         cw=tabwidth-(cx-tx)%tabwidth;
4149         tcol=tabcolumns-cc%tabcolumns;
4150         twid=cw/tcol;
4151         tadj=cw-twid*tcol;
4152         }
4153       cx+=twid+(tadj>0);                        // Mete out columns comprising the tab character
4154       tcol-=1;
4155       tadj-=1;
4156       cc+=1;
4157       cp+=(tcol==0);
4158       continue;
4159       }
4160     cx+=caretw+font->getCharWidth(c|0x40);      // Control character
4161     cc+=1;
4162     cp+=1;
4163     }
4164   while(cx<riteclip);
4165 
4166   // Draw unfinished fragment
4167   fillBufferRect(dc,px,ty,cx-px,th,curstyle);
4168   if(curstyle&STYLE_TEXT) drawBufferText(dc,px,ty,cx-px,th,pp,cp-pp,curstyle);
4169   }
4170 
4171 
4172 // Repaint lines of text
4173 // Erase margins, then draw text one line at a time to reduce flicker.
4174 // Only draw if intersection of bar area and dirty rectangle is non-empty
drawContents(FXDCWindow & dc) const4175 void FXText::drawContents(FXDCWindow& dc) const {
4176   FXint vx=getVisibleX();
4177   FXint vy=getVisibleY();
4178   FXint vw=getVisibleWidth();
4179   FXint vh=getVisibleHeight();
4180   dc.setClipRectangle(vx,vy,vw,vh);
4181   if(0<dc.getClipWidth() && 0<dc.getClipHeight()){
4182     FXint th,row,trow,brow;
4183     dc.setForeground(backColor);
4184     if(dc.getClipY()<=vy+margintop){
4185       dc.fillRectangle(vx,vy,vw,margintop);
4186       }
4187     if(dc.getClipY()+dc.getClipHeight()>=vy+vh-marginbottom){
4188       dc.fillRectangle(vx,vy+vh-marginbottom,vw,marginbottom);
4189       }
4190     if(dc.getClipX()<vx+marginleft){
4191       dc.fillRectangle(vx,vy+margintop,marginleft,vh-margintop-marginbottom);
4192       }
4193     if(dc.getClipX()+dc.getClipWidth()>=vx+vw-marginright){
4194       dc.fillRectangle(vx+vw-marginright,vy+margintop,marginright,vh-margintop-marginbottom);
4195       }
4196     th=font->getFontHeight();
4197     trow=(dc.getClipY()-pos_y-vy-margintop)/th;
4198     brow=(dc.getClipY()+dc.getClipHeight()-pos_y-vy-margintop)/th;
4199     if(trow<=toprow) trow=toprow;
4200     if(brow>=toprow+nvisrows) brow=toprow+nvisrows-1;
4201     dc.setClipRectangle(vx+marginleft,vy+margintop,vw-marginright-marginleft,vh-margintop-marginbottom);
4202     for(row=trow; row<=brow; row++){
4203       drawTextRow(dc,row);
4204       }
4205     }
4206   }
4207 
4208 
4209 // Repaint line numbers
4210 // Erase and redraw number one at a time, instead of erasing all background
4211 // and then drawing numbers on top; this leads to less flicker.
4212 // Only draw if intersection of bar area and dirty rectangle is non-empty
drawNumbers(FXDCWindow & dc) const4213 void FXText::drawNumbers(FXDCWindow& dc) const {
4214   FXint vx=getVisibleX();
4215   FXint vy=getVisibleY();
4216   FXint vh=getVisibleHeight();
4217   dc.setClipRectangle(0,vy,vx,vh);
4218   if(0<dc.getClipWidth() && 0<dc.getClipHeight()){
4219     FXint tw,th,trow,brow,row,n;
4220     FXchar number[20];
4221     dc.setForeground(barColor);
4222     if(dc.getClipY()<=vy+margintop){
4223       dc.fillRectangle(0,vy,vx,margintop);
4224       }
4225     if(dc.getClipY()+dc.getClipHeight()>=vy+vh-marginbottom){
4226       dc.fillRectangle(0,vy+vh-marginbottom,vx,marginbottom);
4227       }
4228     th=font->getFontHeight();
4229     trow=(dc.getClipY()-pos_y-vy-margintop)/th;
4230     brow=(dc.getClipY()+dc.getClipHeight()-pos_y-vy-margintop)/th;
4231     if(trow<=toprow) trow=toprow;
4232     if(brow>=toprow+nvisrows) brow=toprow+nvisrows;
4233     dc.setClipRectangle(0,vy+margintop,vx,vh-margintop-marginbottom);
4234     for(row=trow; row<=brow; row++){
4235       n=__snprintf(number,sizeof(number),"%d",row+1);
4236       tw=font->getTextWidth(number,n);
4237       dc.setForeground(barColor);
4238       dc.fillRectangle(0,pos_y+vy+margintop+row*th,vx,th);
4239       dc.setForeground(numberColor);
4240       dc.drawText(vx-tw,pos_y+vy+margintop+row*th+font->getFontAscent(),number,n);
4241       }
4242     }
4243   }
4244 
4245 
4246 // Repaint the row
updateRow(FXint row) const4247 void FXText::updateRow(FXint row) const {
4248   if(toprow<=row && row<=toprow+nvisrows){
4249     update(getVisibleX(),getVisibleY()+margintop+pos_y+row*font->getFontHeight(),getVisibleWidth(),font->getFontHeight());
4250     }
4251   }
4252 
4253 
4254 // Update whole lines
updateLines(FXint startpos,FXint endpos) const4255 void FXText::updateLines(FXint startpos,FXint endpos) const {
4256   FXint b,e,tr,br,ty,by;
4257   FXMINMAX(b,e,startpos,endpos);
4258   if(b<=visrows[nvisrows] && visrows[0]<e){
4259     if(b<visrows[0]) b=visrows[0];
4260     if(e>visrows[nvisrows-1]) e=visrows[nvisrows-1];
4261     tr=rowFromPos(b);
4262     br=rowFromPos(e);
4263     ty=getVisibleY()+margintop+pos_y+tr*font->getFontHeight();
4264     by=getVisibleY()+margintop+pos_y+br*font->getFontHeight()+font->getFontHeight();
4265     update(getVisibleX(),ty,getVisibleWidth(),by-ty);
4266     }
4267   }
4268 
4269 
4270 // Repaint text range
updateRange(FXint startpos,FXint endpos) const4271 void FXText::updateRange(FXint startpos,FXint endpos) const {
4272   FXint b,e,vx,vy,vw,tr,br,lx,rx,ty,by;
4273   FXMINMAX(b,e,startpos,endpos);
4274   if(b<=visrows[nvisrows] && visrows[0]<e){
4275     if(b<visrows[0]) b=visrows[0];
4276     if(e>visrows[nvisrows-1]) e=visrows[nvisrows-1];
4277     vx=getVisibleX();
4278     vy=getVisibleY();
4279     vw=getVisibleWidth();
4280     tr=rowFromPos(b);
4281     br=rowFromPos(e);
4282     if(tr==br){
4283       ty=pos_y+vy+margintop+tr*font->getFontHeight();
4284       by=ty+font->getFontHeight();
4285       lx=vx+pos_x+marginleft+xoffset(visrows[tr-toprow],b);
4286       if(e<=(visrows[tr-toprow+1]-1))
4287         rx=vx+pos_x+marginleft+xoffset(visrows[tr-toprow],e);
4288       else
4289         rx=vx+vw;
4290       }
4291     else{
4292       ty=vy+pos_y+margintop+tr*font->getFontHeight();
4293       by=vy+pos_y+margintop+br*font->getFontHeight()+font->getFontHeight();
4294       lx=vx;
4295       rx=lx+vw;
4296       }
4297     update(lx,ty,rx-lx,by-ty);
4298     }
4299   }
4300 
4301 
4302 // Draw the text
onPaint(FXObject *,FXSelector,void * ptr)4303 long FXText::onPaint(FXObject*,FXSelector,void* ptr){
4304   FXDCWindow dc(this,(FXEvent*)ptr);
4305 
4306   // Set font
4307   dc.setFont(font);
4308 
4309 //dc.setForeground(FXRGB(255,0,0));
4310 //dc.fillRectangle(0,0,width,height);
4311 
4312   // Paint text
4313   drawContents(dc);
4314 
4315   // Paint line numbers if turned on
4316   if(barwidth){
4317     drawNumbers(dc);
4318     }
4319 
4320   // Paint cursor
4321   if(flags&FLAG_CARET){
4322     paintCursor(dc);
4323     }
4324   return 1;
4325   }
4326 
4327 /*******************************************************************************/
4328 
4329 // Blink the cursor
onBlink(FXObject *,FXSelector,void *)4330 long FXText::onBlink(FXObject*,FXSelector,void*){
4331   drawCursor(blink);
4332   blink^=FLAG_CARET;
4333   getApp()->addTimeout(this,ID_BLINK,getApp()->getBlinkSpeed());
4334   return 0;
4335   }
4336 
4337 
4338 // Flash matching brace
onFlash(FXObject *,FXSelector,void *)4339 long FXText::onFlash(FXObject*,FXSelector,void*){
4340   killHighlight();
4341   return 0;
4342   }
4343 
4344 
4345 // Start motion timer while in this window
onEnter(FXObject * sender,FXSelector sel,void * ptr)4346 long FXText::onEnter(FXObject* sender,FXSelector sel,void* ptr){
4347   FXScrollArea::onEnter(sender,sel,ptr);
4348   getApp()->addTimeout(this,ID_TIPTIMER,getApp()->getMenuPause());
4349   return 1;
4350   }
4351 
4352 
4353 // Stop motion timer when leaving window
onLeave(FXObject * sender,FXSelector sel,void * ptr)4354 long FXText::onLeave(FXObject* sender,FXSelector sel,void* ptr){
4355   FXScrollArea::onLeave(sender,sel,ptr);
4356   getApp()->removeTimeout(this,ID_TIPTIMER);
4357   return 1;
4358   }
4359 
4360 
4361 // Gained focus
onFocusIn(FXObject * sender,FXSelector sel,void * ptr)4362 long FXText::onFocusIn(FXObject* sender,FXSelector sel,void* ptr){
4363   FXScrollArea::onFocusIn(sender,sel,ptr);
4364   if(isEditable()){
4365     getApp()->addTimeout(this,ID_BLINK,getApp()->getBlinkSpeed());
4366     drawCursor(FLAG_CARET);
4367     }
4368   return 1;
4369   }
4370 
4371 
4372 // Lost focus
onFocusOut(FXObject * sender,FXSelector sel,void * ptr)4373 long FXText::onFocusOut(FXObject* sender,FXSelector sel,void* ptr){
4374   FXScrollArea::onFocusOut(sender,sel,ptr);
4375   if(isEditable()){
4376     getApp()->removeTimeout(this,ID_BLINK);
4377     drawCursor(0);
4378     }
4379   flags|=FLAG_UPDATE;
4380   return 1;
4381   }
4382 
4383 /*******************************************************************************/
4384 
4385 // Update value from a message
onCmdSetStringValue(FXObject *,FXSelector,void * ptr)4386 long FXText::onCmdSetStringValue(FXObject*,FXSelector,void* ptr){
4387   setText(*((FXString*)ptr));
4388   return 1;
4389   }
4390 
4391 
4392 // Obtain value from text
onCmdGetStringValue(FXObject *,FXSelector,void * ptr)4393 long FXText::onCmdGetStringValue(FXObject*,FXSelector,void* ptr){
4394   getText(*((FXString*)ptr));
4395   return 1;
4396   }
4397 
4398 /*******************************************************************************/
4399 
4400 // Set tip using a message
onCmdSetTip(FXObject *,FXSelector,void * ptr)4401 long FXText::onCmdSetTip(FXObject*,FXSelector,void* ptr){
4402   setTipText(*((FXString*)ptr));
4403   return 1;
4404   }
4405 
4406 
4407 // Get tip using a message
onCmdGetTip(FXObject *,FXSelector,void * ptr)4408 long FXText::onCmdGetTip(FXObject*,FXSelector,void* ptr){
4409   *((FXString*)ptr)=getTipText();
4410   return 1;
4411   }
4412 
4413 
4414 // Set help using a message
onCmdSetHelp(FXObject *,FXSelector,void * ptr)4415 long FXText::onCmdSetHelp(FXObject*,FXSelector,void* ptr){
4416   setHelpText(*((FXString*)ptr));
4417   return 1;
4418   }
4419 
4420 
4421 // Get help using a message
onCmdGetHelp(FXObject *,FXSelector,void * ptr)4422 long FXText::onCmdGetHelp(FXObject*,FXSelector,void* ptr){
4423   *((FXString*)ptr)=getHelpText();
4424   return 1;
4425   }
4426 
4427 
4428 // We were asked about tip text
onQueryTip(FXObject * sender,FXSelector sel,void * ptr)4429 long FXText::onQueryTip(FXObject* sender,FXSelector sel,void* ptr){
4430   if(FXScrollArea::onQueryTip(sender,sel,ptr)) return 1;
4431   if((flags&FLAG_TIP) && !tip.empty()){
4432     sender->handle(this,FXSEL(SEL_COMMAND,ID_SETSTRINGVALUE),(void*)&tip);
4433     return 1;
4434     }
4435   return 0;
4436   }
4437 
4438 
4439 // We were asked about status text
onQueryHelp(FXObject * sender,FXSelector sel,void * ptr)4440 long FXText::onQueryHelp(FXObject* sender,FXSelector sel,void* ptr){
4441   if(FXScrollArea::onQueryHelp(sender,sel,ptr)) return 1;
4442   if((flags&FLAG_HELP) && !help.empty()){
4443     sender->handle(this,FXSEL(SEL_COMMAND,ID_SETSTRINGVALUE),(void*)&help);
4444     return 1;
4445     }
4446   return 0;
4447   }
4448 
4449 
4450 // Update somebody who wants to change the text
onUpdIsEditable(FXObject * sender,FXSelector,void *)4451 long FXText::onUpdIsEditable(FXObject* sender,FXSelector,void*){
4452   sender->handle(this,isEditable()?FXSEL(SEL_COMMAND,ID_ENABLE):FXSEL(SEL_COMMAND,ID_DISABLE),NULL);
4453   return 1;
4454   }
4455 
4456 
4457 // Update somebody who works on the selection
onUpdHaveSelection(FXObject * sender,FXSelector,void *)4458 long FXText::onUpdHaveSelection(FXObject* sender,FXSelector,void*){
4459   sender->handle(this,(select.startpos<=select.endpos)?FXSEL(SEL_COMMAND,ID_ENABLE):FXSEL(SEL_COMMAND,ID_DISABLE),NULL);
4460   return 1;
4461   }
4462 
4463 
4464 // Update somebody who works on the selection and change the text
onUpdHaveEditableSelection(FXObject * sender,FXSelector,void *)4465 long FXText::onUpdHaveEditableSelection(FXObject* sender,FXSelector,void*){
4466   sender->handle(this,isEditable() && (select.startpos<=select.endpos)?FXSEL(SEL_COMMAND,ID_ENABLE):FXSEL(SEL_COMMAND,ID_DISABLE),NULL);
4467   return 1;
4468   }
4469 
4470 
4471 // Start input method editor
onIMEStart(FXObject *,FXSelector,void *)4472 long FXText::onIMEStart(FXObject*,FXSelector,void*){
4473   if(isEditable()){
4474     if(getComposeContext()){
4475       FXint th=font->getFontHeight();
4476       FXint cursory=getVisibleY()+margintop+pos_y+(cursorrow*th)+th;
4477       if(getVisibleY()<=cursory+th && cursory<=getVisibleY()+getVisibleHeight()){
4478         FXASSERT(0<=cursorrow-toprow && cursorrow-toprow<nvisrows);
4479         FXint cursorstart=visrows[cursorrow-toprow];
4480         FXint cursorx=getVisibleX()+marginleft+pos_x+xoffset(cursorstart,cursorpos)-1;
4481         getComposeContext()->setSpot(cursorx,cursory);
4482         }
4483       }
4484     return 1;
4485     }
4486   return 0;
4487   }
4488 
4489 /*******************************************************************************/
4490 
4491 // Start a drag operation
onBeginDrag(FXObject * sender,FXSelector sel,void * ptr)4492 long FXText::onBeginDrag(FXObject* sender,FXSelector sel,void* ptr){
4493   FXDragType types[4]={stringType,textType,utf8Type,utf16Type};
4494   if(!FXScrollArea::onBeginDrag(sender,sel,ptr)){
4495     beginDrag(types,ARRAYNUMBER(types));
4496     setDragCursor(getApp()->getDefaultCursor(DEF_DNDSTOP_CURSOR));
4497     }
4498   return 1;
4499   }
4500 
4501 
4502 // End drag operation
onEndDrag(FXObject * sender,FXSelector sel,void * ptr)4503 long FXText::onEndDrag(FXObject* sender,FXSelector sel,void* ptr){
4504   if(!FXScrollArea::onEndDrag(sender,sel,ptr)){
4505     endDrag((didAccept()!=DRAG_REJECT));
4506     setDragCursor(getApp()->getDefaultCursor(DEF_TEXT_CURSOR));
4507     }
4508   return 1;
4509   }
4510 
4511 
4512 // Dragged stuff around
onDragged(FXObject * sender,FXSelector sel,void * ptr)4513 long FXText::onDragged(FXObject* sender,FXSelector sel,void* ptr){
4514   if(!FXScrollArea::onDragged(sender,sel,ptr)){
4515     FXDragAction action=DRAG_COPY;
4516     if(isEditable()){
4517       if(isDropTarget()) action=DRAG_MOVE;
4518       if(((FXEvent*)ptr)->state&CONTROLMASK) action=DRAG_COPY;
4519       if(((FXEvent*)ptr)->state&SHIFTMASK) action=DRAG_MOVE;
4520       }
4521     handleDrag(((FXEvent*)ptr)->root_x,((FXEvent*)ptr)->root_y,action);
4522     action=didAccept();
4523     switch(action){
4524       case DRAG_MOVE:
4525         setDragCursor(getApp()->getDefaultCursor(DEF_DNDMOVE_CURSOR));
4526         break;
4527       case DRAG_COPY:
4528         setDragCursor(getApp()->getDefaultCursor(DEF_DNDCOPY_CURSOR));
4529         break;
4530       default:
4531         setDragCursor(getApp()->getDefaultCursor(DEF_DNDSTOP_CURSOR));
4532         break;
4533       }
4534     }
4535   return 1;
4536   }
4537 
4538 
4539 // Handle drag-and-drop enter
onDNDEnter(FXObject * sender,FXSelector sel,void * ptr)4540 long FXText::onDNDEnter(FXObject* sender,FXSelector sel,void* ptr){
4541   FXScrollArea::onDNDEnter(sender,sel,ptr);
4542   if(isEditable()){
4543     drawCursor(FLAG_CARET);
4544     }
4545   return 1;
4546   }
4547 
4548 
4549 // Handle drag-and-drop leave
onDNDLeave(FXObject * sender,FXSelector sel,void * ptr)4550 long FXText::onDNDLeave(FXObject* sender,FXSelector sel,void* ptr){
4551   FXScrollArea::onDNDLeave(sender,sel,ptr);
4552   stopAutoScroll();
4553   if(isEditable()){
4554     drawCursor(0);
4555     }
4556   return 1;
4557   }
4558 
4559 
4560 // Handle drag-and-drop motion
onDNDMotion(FXObject * sender,FXSelector sel,void * ptr)4561 long FXText::onDNDMotion(FXObject* sender,FXSelector sel,void* ptr){
4562   FXEvent* event=(FXEvent*)ptr;
4563 
4564   // Scroll into view
4565   if(startAutoScroll(event,true)) return 1;
4566 
4567   // Handled elsewhere
4568   if(FXScrollArea::onDNDMotion(sender,sel,ptr)) return 1;
4569 
4570   // Correct drop type
4571   if(offeredDNDType(FROM_DRAGNDROP,textType) || offeredDNDType(FROM_DRAGNDROP,stringType) || offeredDNDType(FROM_DRAGNDROP,utf8Type) || offeredDNDType(FROM_DRAGNDROP,utf16Type)){
4572 
4573     // Is target editable?
4574     if(isEditable()){
4575       FXDragAction action=inquireDNDAction();
4576 
4577       // Check for legal DND action
4578       if(action==DRAG_COPY || action==DRAG_MOVE){
4579         FXint pos,row,col;
4580 
4581         // Get the suggested drop position
4582         pos=getRowColumnAt(event->win_x,event->win_y,row,col);
4583 
4584         // Move cursor to new position
4585         setCursorPos(pos,true);
4586 
4587         // We don't accept a drop on the selection
4588         if(!isPosSelected(pos,col)){
4589           acceptDrop(DRAG_ACCEPT);
4590           }
4591         }
4592       }
4593     return 1;
4594     }
4595 
4596   // Didn't handle it here
4597   return 0;
4598   }
4599 
4600 
4601 // Handle drag-and-drop drop
onDNDDrop(FXObject * sender,FXSelector sel,void * ptr)4602 long FXText::onDNDDrop(FXObject* sender,FXSelector sel,void* ptr){
4603 
4604   // Stop scrolling
4605   stopAutoScroll();
4606   drawCursor(0);
4607 
4608   // Try handling it in base class first
4609   if(FXScrollArea::onDNDDrop(sender,sel,ptr)) return 1;
4610 
4611   // Should really not have gotten this if non-editable
4612   if(isEditable()){
4613     FXString string;
4614     FXString junk;
4615 
4616     // First, try UTF-8
4617     if(getDNDData(FROM_DRAGNDROP,utf8Type,string)){
4618       if(inquireDNDAction()==DRAG_MOVE){
4619         getDNDData(FROM_DRAGNDROP,deleteType,junk);
4620         }
4621       replaceText(cursorpos,0,string,true);
4622       setCursorPos(cursorpos,true);
4623       return 1;
4624       }
4625 
4626     // Next, try UTF-16
4627     if(getDNDData(FROM_DRAGNDROP,utf16Type,string)){
4628       if(inquireDNDAction()==DRAG_MOVE){
4629         getDNDData(FROM_DRAGNDROP,deleteType,junk);
4630         }
4631       replaceText(cursorpos,0,string,true);
4632       setCursorPos(cursorpos,true);
4633       return 1;
4634       }
4635 
4636     // Next, try good old Latin-1
4637     if(getDNDData(FROM_DRAGNDROP,textType,string)){
4638       if(inquireDNDAction()==DRAG_MOVE){
4639         getDNDData(FROM_DRAGNDROP,deleteType,junk);
4640         }
4641       replaceText(cursorpos,0,string,true);
4642       setCursorPos(cursorpos,true);
4643       return 1;
4644       }
4645     return 1;
4646     }
4647   return 0;
4648   }
4649 
4650 
4651 // Service requested DND data
onDNDRequest(FXObject * sender,FXSelector sel,void * ptr)4652 long FXText::onDNDRequest(FXObject* sender,FXSelector sel,void* ptr){
4653   FXEvent *event=(FXEvent*)ptr;
4654 
4655   // Perhaps the target wants to supply its own data
4656   if(FXScrollArea::onDNDRequest(sender,sel,ptr)) return 1;
4657 
4658   // Recognize the request?
4659   if(event->target==stringType || event->target==textType || event->target==utf8Type || event->target==utf16Type){
4660     FXString string;
4661 
4662     // Get selected fragment
4663     string=getSelectedText();
4664 
4665     // Return text of the selection as UTF-8
4666     if(event->target==utf8Type){
4667       setDNDData(FROM_DRAGNDROP,event->target,string);
4668       return 1;
4669       }
4670 
4671     // Return text of the selection translated to 8859-1
4672     if(event->target==stringType || event->target==textType){
4673       setDNDData(FROM_DRAGNDROP,event->target,string);
4674       return 1;
4675       }
4676 
4677     // Return text of the selection translated to UTF-16
4678     if(event->target==utf16Type){
4679       setDNDData(FROM_DRAGNDROP,event->target,string);
4680       return 1;
4681       }
4682     }
4683 
4684   // Delete dragged text, if editable
4685   if(event->target==deleteType){
4686     if(isEditable()){
4687       if(select.startcol<=select.endcol){
4688         removeTextBlock(select.startpos,select.endpos,select.startcol,select.endcol,true);
4689         }
4690       else{
4691         removeText(select.startpos,select.endpos-select.startpos,true);
4692         }
4693       }
4694     return 1;
4695     }
4696 
4697   return 0;
4698   }
4699 
4700 /*******************************************************************************/
4701 
4702 // We now really do have the selection
onSelectionGained(FXObject * sender,FXSelector sel,void * ptr)4703 long FXText::onSelectionGained(FXObject* sender,FXSelector sel,void* ptr){
4704   FXScrollArea::onSelectionGained(sender,sel,ptr);
4705   return 1;
4706   }
4707 
4708 
4709 // We lost the selection somehow
onSelectionLost(FXObject * sender,FXSelector sel,void * ptr)4710 long FXText::onSelectionLost(FXObject* sender,FXSelector sel,void* ptr){
4711   FXScrollArea::onSelectionLost(sender,sel,ptr);
4712   if(target){
4713     FXint what[4];
4714     what[0]=select.startpos;
4715     what[1]=select.endpos-select.startpos;
4716     what[2]=select.startcol;
4717     what[3]=select.endcol-select.startcol;
4718     target->tryHandle(this,FXSEL(SEL_DESELECTED,message),(void*)what);
4719     }
4720   updateRange(select.startpos,select.endpos);
4721   select.startpos=0;
4722   select.endpos=-1;
4723   select.startcol=0;
4724   select.endcol=-1;
4725   return 1;
4726   }
4727 
4728 
4729 // Somebody wants our selection
onSelectionRequest(FXObject * sender,FXSelector sel,void * ptr)4730 long FXText::onSelectionRequest(FXObject* sender,FXSelector sel,void* ptr){
4731   FXEvent *event=(FXEvent*)ptr;
4732 
4733   // Perhaps the target wants to supply its own data for the selection
4734   if(FXScrollArea::onSelectionRequest(sender,sel,ptr)) return 1;
4735 
4736   // Recognize the request?
4737   if(event->target==stringType || event->target==textType || event->target==utf8Type || event->target==utf16Type){
4738 
4739     // Get selected fragment
4740     FXString string=getSelectedText();
4741 
4742     // Return text of the selection as UTF-8
4743     if(event->target==utf8Type){
4744       setDNDData(FROM_SELECTION,event->target,string);
4745       return 1;
4746       }
4747 
4748     // Return text of the selection translated to 8859-1
4749     if(event->target==stringType || event->target==textType){
4750       setDNDData(FROM_SELECTION,event->target,string);
4751       return 1;
4752       }
4753 
4754     // Return text of the selection translated to UTF-16
4755     if(event->target==utf16Type){
4756       setDNDData(FROM_SELECTION,event->target,string);
4757       return 1;
4758       }
4759     }
4760   return 0;
4761   }
4762 
4763 /*******************************************************************************/
4764 
4765 // We now really do have the selection
onClipboardGained(FXObject * sender,FXSelector sel,void * ptr)4766 long FXText::onClipboardGained(FXObject* sender,FXSelector sel,void* ptr){
4767   FXScrollArea::onClipboardGained(sender,sel,ptr);
4768   return 1;
4769   }
4770 
4771 
4772 // We lost the selection somehow
onClipboardLost(FXObject * sender,FXSelector sel,void * ptr)4773 long FXText::onClipboardLost(FXObject* sender,FXSelector sel,void* ptr){
4774   FXScrollArea::onClipboardLost(sender,sel,ptr);
4775   clipped.clear();
4776   return 1;
4777   }
4778 
4779 
4780 // Somebody wants our selection
onClipboardRequest(FXObject * sender,FXSelector sel,void * ptr)4781 long FXText::onClipboardRequest(FXObject* sender,FXSelector sel,void* ptr){
4782   FXEvent *event=(FXEvent*)ptr;
4783   FXString string=clipped;
4784 
4785   // Try handling it in base class first
4786   if(FXScrollArea::onClipboardRequest(sender,sel,ptr)) return 1;
4787 
4788   // Requested data from clipboard
4789   if(event->target==stringType || event->target==textType || event->target==utf8Type || event->target==utf16Type){
4790 
4791     // Expand newlines to CRLF on Windows
4792 #ifdef WIN32
4793     unixToDos(string);
4794 #endif
4795 
4796     // Return clipped text as as UTF-8
4797     if(event->target==utf8Type){
4798       setDNDData(FROM_CLIPBOARD,event->target,string);
4799       return 1;
4800       }
4801 
4802     // Return clipped text translated to 8859-1
4803     if(event->target==stringType || event->target==textType){
4804       setDNDData(FROM_CLIPBOARD,event->target,string);
4805       return 1;
4806       }
4807 
4808     // Return text of the selection translated to UTF-16
4809     if(event->target==utf16Type){
4810       setDNDData(FROM_CLIPBOARD,event->target,string);
4811       return 1;
4812       }
4813     }
4814   return 0;
4815   }
4816 
4817 /*******************************************************************************/
4818 
4819 // Pressed left button
onLeftBtnPress(FXObject *,FXSelector,void * ptr)4820 long FXText::onLeftBtnPress(FXObject*,FXSelector,void* ptr){
4821   FXEvent* event=(FXEvent*)ptr;
4822   FXint pos,row,col;
4823   flags&=~FLAG_TIP;
4824   handle(this,FXSEL(SEL_FOCUS_SELF,0),ptr);
4825   if(isEnabled()){
4826     grab();
4827     if(target && target->tryHandle(this,FXSEL(SEL_LEFTBUTTONPRESS,message),ptr)) return 1;
4828     grabx=event->win_x-pos_x;
4829     graby=event->win_y-pos_y;
4830     if(event->click_count==1){
4831       pos=getRowColumnAt(event->win_x,event->win_y,row,col);
4832       if((event->state&CONTROLMASK) && !(options&TEXT_WORDWRAP)){
4833         if(event->state&SHIFTMASK){                     // Shift-select block
4834           moveCursorRowColumnAndSelect(row,col,true);
4835           }
4836         else{                                           // Drag select block
4837           moveCursorRowColumn(row,col,true);
4838           }
4839         mode=MOUSE_BLOCK;
4840         }
4841       else{
4842         if(event->state&SHIFTMASK){                     // Shift-select range
4843           moveCursorAndSelect(pos,SelectChars,true);
4844           }
4845         else{                                           // Drag select range
4846           moveCursor(pos,true);
4847           }
4848         mode=MOUSE_CHARS;
4849         }
4850       }
4851     else if(event->click_count==2){     // Drag select words
4852       pos=getPosContaining(event->win_x,event->win_y);
4853       setAnchorPos(pos);
4854       moveCursorAndSelect(pos,SelectWords,true);
4855       mode=MOUSE_WORDS;
4856       }
4857     else{                               // Drag select lines
4858       pos=getPosAt(event->win_x,event->win_y);
4859       moveCursorAndSelect(pos,SelectLines,true);
4860       mode=MOUSE_LINES;
4861       }
4862     flags&=~FLAG_UPDATE;
4863     return 1;
4864     }
4865   return 0;
4866   }
4867 
4868 
4869 // Released left button
onLeftBtnRelease(FXObject *,FXSelector,void * ptr)4870 long FXText::onLeftBtnRelease(FXObject*,FXSelector,void* ptr){
4871   if(isEnabled()){
4872     ungrab();
4873     mode=MOUSE_NONE;
4874     stopAutoScroll();
4875     if(target && target->tryHandle(this,FXSEL(SEL_LEFTBUTTONRELEASE,message),ptr)) return 1;
4876     return 1;
4877     }
4878   return 0;
4879   }
4880 
4881 
4882 // Pressed middle button
onMiddleBtnPress(FXObject *,FXSelector,void * ptr)4883 long FXText::onMiddleBtnPress(FXObject*,FXSelector,void* ptr){
4884   FXEvent* event=(FXEvent*)ptr;
4885   FXint pos,row,col;
4886   flags&=~FLAG_TIP;
4887   handle(this,FXSEL(SEL_FOCUS_SELF,0),ptr);
4888   if(isEnabled()){
4889     grab();
4890     if(target && target->tryHandle(this,FXSEL(SEL_MIDDLEBUTTONPRESS,message),ptr)) return 1;
4891     pos=getRowColumnAt(event->win_x,event->win_y,row,col);
4892     setCursorPos(pos,true);
4893     setAnchorPos(cursorpos);
4894     if(isPosSelected(cursorpos,col)){
4895       mode=MOUSE_TRYDRAG;
4896       }
4897     flags&=~FLAG_UPDATE;
4898     return 1;
4899     }
4900   return 0;
4901   }
4902 
4903 
4904 // Released middle button
onMiddleBtnRelease(FXObject *,FXSelector,void * ptr)4905 long FXText::onMiddleBtnRelease(FXObject*,FXSelector,void* ptr){
4906   FXuint md=mode;
4907   if(isEnabled()){
4908     ungrab();
4909     stopAutoScroll();
4910     mode=MOUSE_NONE;
4911     if(target && target->tryHandle(this,FXSEL(SEL_MIDDLEBUTTONRELEASE,message),ptr)) return 1;
4912     if(md==MOUSE_DRAG){
4913       handle(this,FXSEL(SEL_ENDDRAG,0),ptr);
4914       }
4915     else{
4916       handle(this,FXSEL(SEL_COMMAND,ID_PASTE_MIDDLE),NULL);
4917       }
4918     return 1;
4919     }
4920   return 0;
4921   }
4922 
4923 
4924 // Pressed right button
onRightBtnPress(FXObject *,FXSelector,void * ptr)4925 long FXText::onRightBtnPress(FXObject*,FXSelector,void* ptr){
4926   FXEvent* event=(FXEvent*)ptr;
4927   flags&=~FLAG_TIP;
4928   handle(this,FXSEL(SEL_FOCUS_SELF,0),ptr);
4929   if(isEnabled()){
4930     grab();
4931     if(target && target->tryHandle(this,FXSEL(SEL_RIGHTBUTTONPRESS,message),ptr)) return 1;
4932     grabx=event->win_x-pos_x;
4933     graby=event->win_y-pos_y;
4934     mode=MOUSE_SCROLL;
4935     flags&=~FLAG_UPDATE;
4936     return 1;
4937     }
4938   return 0;
4939   }
4940 
4941 
4942 // Released right button
onRightBtnRelease(FXObject *,FXSelector,void * ptr)4943 long FXText::onRightBtnRelease(FXObject*,FXSelector,void* ptr){
4944   if(isEnabled()){
4945     ungrab();
4946     mode=MOUSE_NONE;
4947     if(target && target->tryHandle(this,FXSEL(SEL_RIGHTBUTTONRELEASE,message),ptr)) return 1;
4948     return 1;
4949     }
4950   return 0;
4951   }
4952 
4953 
4954 // Handle real or simulated mouse motion
onMotion(FXObject *,FXSelector,void * ptr)4955 long FXText::onMotion(FXObject*,FXSelector,void* ptr){
4956   FXEvent* event=(FXEvent*)ptr;
4957   FXint pos,row,col;
4958   flags&=~FLAG_TIP;
4959   getApp()->removeTimeout(this,ID_TIPTIMER);
4960   switch(mode){
4961     case MOUSE_NONE:
4962       getApp()->addTimeout(this,ID_TIPTIMER,getApp()->getMenuPause());
4963       return 1;
4964     case MOUSE_CHARS:
4965       if(startAutoScroll(event,false)) return 1;
4966       if((Math::iabs(event->win_x-grabx-pos_x)>getApp()->getDragDelta())||(Math::iabs(event->win_y-graby-pos_y)>getApp()->getDragDelta())){
4967         killHighlight();
4968         pos=getPosAt(event->win_x,event->win_y);
4969         setCursorPos(pos,true);
4970         extendSelection(cursorpos,SelectChars,true);
4971         }
4972       return 1;
4973     case MOUSE_WORDS:
4974       if(startAutoScroll(event,false)) return 1;
4975       if((Math::iabs(event->win_x-grabx-pos_x)>getApp()->getDragDelta())||(Math::iabs(event->win_y-graby-pos_y)>getApp()->getDragDelta())){
4976         killHighlight();
4977         pos=getPosContaining(event->win_x,event->win_y);
4978         setCursorPos(pos,true);
4979         extendSelection(cursorpos,SelectWords,true);
4980         }
4981       return 1;
4982     case MOUSE_LINES:
4983       if(startAutoScroll(event,false)) return 1;
4984       if((Math::iabs(event->win_x-grabx-pos_x)>getApp()->getDragDelta())||(Math::iabs(event->win_y-graby-pos_y)>getApp()->getDragDelta())){
4985         killHighlight();
4986         pos=getPosAt(event->win_x,event->win_y);
4987         setCursorPos(pos,true);
4988         extendSelection(cursorpos,SelectLines,true);
4989         }
4990       return 1;
4991     case MOUSE_BLOCK:
4992       if(startAutoScroll(event,false)) return 1;
4993       if((Math::iabs(event->win_x-grabx-pos_x)>getApp()->getDragDelta())||(Math::iabs(event->win_y-graby-pos_y)>getApp()->getDragDelta())){
4994         killHighlight();
4995         getRowColumnAt(event->win_x,event->win_y,row,col);
4996         setCursorRowColumn(row,col,true);
4997         extendBlockSelection(row,col,true);
4998         }
4999       return 1;
5000     case MOUSE_SCROLL:
5001       setPosition(event->win_x-grabx,event->win_y-graby);
5002       return 1;
5003     case MOUSE_DRAG:
5004       handle(this,FXSEL(SEL_DRAGGED,0),ptr);
5005       return 1;
5006     case MOUSE_TRYDRAG:
5007       if(event->moved){
5008         mode=MOUSE_NONE;
5009         if(handle(this,FXSEL(SEL_BEGINDRAG,0),ptr)){
5010           mode=MOUSE_DRAG;
5011           }
5012         }
5013       return 1;
5014     }
5015   return 0;
5016   }
5017 
5018 
5019 // Autoscroll timer fired; autoscrolling hysteresis is based on movement
5020 // relative to the original document position of the click, in case the
5021 // click-position is close to the autoscrolling fudge-border.
onAutoScroll(FXObject * sender,FXSelector sel,void * ptr)5022 long FXText::onAutoScroll(FXObject* sender,FXSelector sel,void* ptr){
5023   FXEvent* event=(FXEvent*)ptr;
5024   FXint pos,row,col;
5025   FXScrollArea::onAutoScroll(sender,sel,ptr);
5026   switch(mode){
5027     case MOUSE_CHARS:
5028       if((Math::iabs(event->win_x-grabx-pos_x)>getApp()->getDragDelta())||(Math::iabs(event->win_y-graby-pos_y)>getApp()->getDragDelta())){
5029         killHighlight();
5030         pos=getPosAt(event->win_x,event->win_y);
5031         extendSelection(pos,SelectChars,true);
5032         setCursorPos(pos,true);
5033         }
5034       return 1;
5035     case MOUSE_WORDS:
5036       if((Math::iabs(event->win_x-grabx-pos_x)>getApp()->getDragDelta())||(Math::iabs(event->win_y-graby-pos_y)>getApp()->getDragDelta())){
5037         killHighlight();
5038         pos=getPosContaining(event->win_x,event->win_y);
5039         extendSelection(pos,SelectWords,true);
5040         setCursorPos(pos,true);
5041         }
5042       return 1;
5043     case MOUSE_LINES:
5044       if((Math::iabs(event->win_x-grabx-pos_x)>getApp()->getDragDelta())||(Math::iabs(event->win_y-graby-pos_y)>getApp()->getDragDelta())){
5045         killHighlight();
5046         pos=getPosAt(event->win_x,event->win_y);
5047         extendSelection(pos,SelectLines,true);
5048         setCursorPos(pos,true);
5049         }
5050       return 1;
5051     case MOUSE_BLOCK:
5052       if((Math::iabs(event->win_x-grabx-pos_x)>getApp()->getDragDelta())||(Math::iabs(event->win_y-graby-pos_y)>getApp()->getDragDelta())){
5053         killHighlight();
5054         getRowColumnAt(event->win_x,event->win_y,row,col);
5055         extendBlockSelection(row,col,true);
5056         setCursorRowColumn(row,col,true);
5057         }
5058       return 1;
5059     }
5060   return 0;
5061   }
5062 
5063 
5064 // The widget lost the grab for some reason
onUngrabbed(FXObject * sender,FXSelector sel,void * ptr)5065 long FXText::onUngrabbed(FXObject* sender,FXSelector sel,void* ptr){
5066   FXScrollArea::onUngrabbed(sender,sel,ptr);
5067   mode=MOUSE_NONE;
5068   flags|=FLAG_UPDATE;
5069   stopAutoScroll();
5070   return 1;
5071   }
5072 
5073 
5074 // Mouse hovered a while
onTipTimer(FXObject *,FXSelector,void *)5075 long FXText::onTipTimer(FXObject*,FXSelector,void*){
5076   FXTRACE((250,"%s::onTipTimer %p\n",getClassName(),this));
5077   flags|=FLAG_TIP;
5078   return 1;
5079   }
5080 
5081 /*******************************************************************************/
5082 
5083 // Keyboard press
onKeyPress(FXObject *,FXSelector,void * ptr)5084 long FXText::onKeyPress(FXObject*,FXSelector,void* ptr){
5085   flags&=~FLAG_TIP;
5086   if(isEnabled()){
5087     FXEvent* event=(FXEvent*)ptr;
5088     FXTRACE((TOPIC_KEYBOARD,"%s::onKeyPress keysym=0x%04x state=%04x\n",getClassName(),event->code,event->state));
5089     if(target && target->tryHandle(this,FXSEL(SEL_KEYPRESS,message),ptr)) return 1;
5090     flags&=~FLAG_UPDATE;
5091     switch(event->code){
5092       case KEY_Shift_L:
5093       case KEY_Shift_R:
5094       case KEY_Control_L:
5095       case KEY_Control_R:
5096         if(mode==MOUSE_DRAG){handle(this,FXSEL(SEL_DRAGGED,0),ptr);}
5097         return 1;
5098       case KEY_Up:
5099       case KEY_KP_Up:
5100         if(event->state&CONTROLMASK){
5101           handle(this,FXSEL(SEL_COMMAND,ID_SCROLL_UP),NULL);
5102           }
5103         else if(event->state&SHIFTMASK){
5104           handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_SHIFT_UP),NULL);
5105           }
5106         else if(event->state&ALTMASK){
5107           handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_ALT_UP),NULL);
5108           }
5109         else{
5110           handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_UP),NULL);
5111           }
5112         break;
5113       case KEY_Down:
5114       case KEY_KP_Down:
5115         if(event->state&CONTROLMASK){
5116           handle(this,FXSEL(SEL_COMMAND,ID_SCROLL_DOWN),NULL);
5117           }
5118         else if(event->state&SHIFTMASK){
5119           handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_SHIFT_DOWN),NULL);
5120           }
5121         else if(event->state&ALTMASK){
5122           handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_ALT_DOWN),NULL);
5123           }
5124         else{
5125           handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_DOWN),NULL);
5126           }
5127         break;
5128       case KEY_Left:
5129       case KEY_KP_Left:
5130         if(event->state&CONTROLMASK){
5131           if(event->state&SHIFTMASK){
5132             handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_SHIFT_WORD_LEFT),NULL);
5133             }
5134           else{
5135             handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_WORD_LEFT),NULL);
5136             }
5137           }
5138         else{
5139           if(event->state&SHIFTMASK){
5140             handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_SHIFT_LEFT),NULL);
5141             }
5142           else if(event->state&ALTMASK){
5143             handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_ALT_LEFT),NULL);
5144             }
5145           else{
5146             handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_LEFT),NULL);
5147             }
5148           }
5149         break;
5150       case KEY_Right:
5151       case KEY_KP_Right:
5152         if(event->state&CONTROLMASK){
5153           if(event->state&SHIFTMASK){
5154             handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_SHIFT_WORD_RIGHT),NULL);
5155             }
5156           else{
5157             handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_WORD_RIGHT),NULL);
5158             }
5159           }
5160         else{
5161           if(event->state&SHIFTMASK){
5162             handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_SHIFT_RIGHT),NULL);
5163             }
5164           else if(event->state&ALTMASK){
5165             handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_ALT_RIGHT),NULL);
5166             }
5167           else{
5168             handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_RIGHT),NULL);
5169             }
5170           }
5171         break;
5172       case KEY_Home:
5173       case KEY_KP_Home:
5174         if(event->state&CONTROLMASK){
5175           if(event->state&SHIFTMASK){
5176             handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_SHIFT_TOP),NULL);
5177             }
5178           else{
5179             handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_TOP),NULL);
5180             }
5181           }
5182         else{
5183           if(event->state&SHIFTMASK){
5184             handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_SHIFT_HOME),NULL);
5185             }
5186           else{
5187             handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_HOME),NULL);
5188             }
5189           }
5190         break;
5191       case KEY_End:
5192       case KEY_KP_End:
5193         if(event->state&CONTROLMASK){
5194           if(event->state&SHIFTMASK){
5195             handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_SHIFT_BOTTOM),NULL);
5196             }
5197           else{
5198             handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_BOTTOM),NULL);
5199             }
5200           }
5201         else{
5202           if(event->state&SHIFTMASK){
5203             handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_SHIFT_END),NULL);
5204             }
5205           else{
5206             handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_END),NULL);
5207             }
5208           }
5209         break;
5210       case KEY_Page_Up:
5211       case KEY_KP_Page_Up:
5212         if(event->state&SHIFTMASK){
5213           handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_SHIFT_PAGEUP),NULL);
5214           }
5215         else{
5216           handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_PAGEUP),NULL);
5217           }
5218         break;
5219       case KEY_Page_Down:
5220       case KEY_KP_Page_Down:
5221         if(event->state&SHIFTMASK){
5222           handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_SHIFT_PAGEDOWN),NULL);
5223           }
5224         else{
5225           handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_PAGEDOWN),NULL);
5226           }
5227         break;
5228       case KEY_Insert:
5229       case KEY_KP_Insert:
5230         if(event->state&CONTROLMASK){
5231           handle(this,FXSEL(SEL_COMMAND,ID_COPY_SEL),NULL);
5232           }
5233         else if(event->state&SHIFTMASK){
5234           handle(this,FXSEL(SEL_COMMAND,ID_PASTE_SEL),NULL);
5235           }
5236         else{
5237           handle(this,FXSEL(SEL_COMMAND,ID_TOGGLE_OVERSTRIKE),NULL);
5238           }
5239         break;
5240       case KEY_Delete:
5241       case KEY_KP_Delete:
5242         if(event->state&CONTROLMASK){
5243           handle(this,FXSEL(SEL_COMMAND,ID_DELETE_WORD),NULL);
5244           }
5245         else if(event->state&SHIFTMASK){
5246           handle(this,FXSEL(SEL_COMMAND,ID_DELETE_EOL),NULL);
5247           }
5248         else{
5249           handle(this,FXSEL(SEL_COMMAND,ID_DELETE_CHAR),NULL);
5250           }
5251         break;
5252       case KEY_BackSpace:
5253         if(event->state&CONTROLMASK){
5254           handle(this,FXSEL(SEL_COMMAND,ID_BACKSPACE_WORD),NULL);
5255           }
5256         else if(event->state&SHIFTMASK){
5257           handle(this,FXSEL(SEL_COMMAND,ID_BACKSPACE_BOL),NULL);
5258           }
5259         else{
5260           handle(this,FXSEL(SEL_COMMAND,ID_BACKSPACE_CHAR),NULL);
5261           }
5262         break;
5263       case KEY_Return:
5264       case KEY_KP_Enter:
5265         if(event->state&CONTROLMASK){
5266           handle(this,FXSEL(SEL_COMMAND,ID_INSERT_NEWLINE_ONLY),NULL);
5267           }
5268         else if(event->state&SHIFTMASK){
5269           handle(this,FXSEL(SEL_COMMAND,ID_INSERT_NEWLINE_INDENT),NULL);
5270           }
5271         else{
5272           handle(this,FXSEL(SEL_COMMAND,ID_INSERT_NEWLINE),NULL);
5273           }
5274         break;
5275       case KEY_Tab:
5276       case KEY_KP_Tab:
5277         if(event->state&CONTROLMASK){
5278           handle(this,FXSEL(SEL_COMMAND,ID_INSERT_HARDTAB),NULL);
5279           }
5280         else{
5281           handle(this,FXSEL(SEL_COMMAND,ID_INSERT_TAB),NULL);
5282           }
5283         break;
5284       case KEY_a:
5285         if(!(event->state&CONTROLMASK)) goto ins;
5286         handle(this,FXSEL(SEL_COMMAND,ID_SELECT_ALL),NULL);
5287         break;
5288       case KEY_x:
5289         if(!(event->state&CONTROLMASK)) goto ins;
5290       case KEY_F20:                               // Sun Cut key
5291         handle(this,FXSEL(SEL_COMMAND,ID_CUT_SEL),NULL);
5292         break;
5293       case KEY_c:
5294         if(!(event->state&CONTROLMASK)) goto ins;
5295       case KEY_F16:                               // Sun Copy key
5296         handle(this,FXSEL(SEL_COMMAND,ID_COPY_SEL),NULL);
5297         break;
5298       case KEY_v:
5299         if(!(event->state&CONTROLMASK)) goto ins;
5300       case KEY_F18:                               // Sun Paste key
5301         handle(this,FXSEL(SEL_COMMAND,ID_PASTE_SEL),NULL);
5302         break;
5303       case KEY_k:
5304         if(!(event->state&CONTROLMASK)) goto ins;
5305         handle(this,FXSEL(SEL_COMMAND,ID_DELETE_LINE),NULL);
5306         break;
5307       case KEY_j:
5308         if(!(event->state&CONTROLMASK)) goto ins;
5309         handle(this,FXSEL(SEL_COMMAND,ID_JOIN_LINES),NULL);
5310         break;
5311       default:
5312 ins:    if((event->state&(CONTROLMASK|ALTMASK)) || ((FXuchar)event->text[0]<32)) return 0;
5313         handle(this,FXSEL(SEL_COMMAND,ID_INSERT_STRING),(void*)event->text.text());
5314         break;
5315       }
5316     return 1;
5317     }
5318   return 0;
5319   }
5320 
5321 
5322 // Keyboard release
onKeyRelease(FXObject *,FXSelector,void * ptr)5323 long FXText::onKeyRelease(FXObject*,FXSelector,void* ptr){
5324   if(isEnabled()){
5325     FXEvent* event=(FXEvent*)ptr;
5326     FXTRACE((TOPIC_KEYBOARD,"%s::onKeyRelease keysym=0x%04x state=%04x\n",getClassName(),event->code,event->state));
5327     if(target && target->tryHandle(this,FXSEL(SEL_KEYRELEASE,message),ptr)) return 1;
5328     switch(event->code){
5329       case KEY_Shift_L:
5330       case KEY_Shift_R:
5331       case KEY_Control_L:
5332       case KEY_Control_R:
5333         if(mode==MOUSE_DRAG){handle(this,FXSEL(SEL_DRAGGED,0),ptr);}
5334         return 1;
5335       }
5336     }
5337   return 0;
5338   }
5339 
5340 /*******************************************************************************/
5341 
5342 // Move cursor to top of buffer
onCmdCursorTop(FXObject *,FXSelector,void *)5343 long FXText::onCmdCursorTop(FXObject*,FXSelector,void*){
5344   moveCursor(0,true);
5345   return 1;
5346   }
5347 
5348 
5349 // Move cursor to bottom of buffer
onCmdCursorBottom(FXObject *,FXSelector,void *)5350 long FXText::onCmdCursorBottom(FXObject*,FXSelector,void*){
5351   moveCursor(length,true);
5352   return 1;
5353   }
5354 
5355 
5356 // Move cursor to begin of line
onCmdCursorHome(FXObject *,FXSelector,void *)5357 long FXText::onCmdCursorHome(FXObject*,FXSelector,void*){
5358   moveCursor(lineStart(cursorpos),true);
5359   return 1;
5360   }
5361 
5362 
5363 // Move cursor to end of line
onCmdCursorEnd(FXObject *,FXSelector,void *)5364 long FXText::onCmdCursorEnd(FXObject*,FXSelector,void*){
5365   moveCursor(lineEnd(cursorpos),true);
5366   return 1;
5367   }
5368 
5369 
5370 // Process cursor right
onCmdCursorRight(FXObject *,FXSelector,void *)5371 long FXText::onCmdCursorRight(FXObject*,FXSelector,void*){
5372   moveCursor(cursorpos<length?inc(cursorpos):length,true);
5373   return 1;
5374   }
5375 
5376 
5377 // Process cursor left
onCmdCursorLeft(FXObject *,FXSelector,void *)5378 long FXText::onCmdCursorLeft(FXObject*,FXSelector,void*){
5379   moveCursor(0<cursorpos?dec(cursorpos):0,true);
5380   return 1;
5381   }
5382 
5383 
5384 // Process cursor up
onCmdCursorUp(FXObject *,FXSelector,void *)5385 long FXText::onCmdCursorUp(FXObject*,FXSelector,void*){
5386   FXint col=(0<=prefcol) ? prefcol : cursorcol;
5387   moveCursor(posFromColumn(prevRow(cursorpos),col),true);
5388   prefcol=col;
5389   return 1;
5390   }
5391 
5392 
5393 // Process cursor down
onCmdCursorDown(FXObject *,FXSelector,void *)5394 long FXText::onCmdCursorDown(FXObject*,FXSelector,void*){
5395   FXint col=(0<=prefcol) ? prefcol : cursorcol;
5396   moveCursor(posFromColumn(nextRow(cursorpos),col),true);
5397   prefcol=col;
5398   return 1;
5399   }
5400 
5401 
5402 // Page up
onCmdCursorPageUp(FXObject *,FXSelector,void *)5403 long FXText::onCmdCursorPageUp(FXObject*,FXSelector,void*){
5404   FXint col=(0<=prefcol) ? prefcol : cursorcol;
5405   FXint lines=FXMAX(nvisrows-2,1);
5406   setTopLine(prevRow(toppos,lines));
5407   moveCursor(posFromColumn(prevRow(cursorpos,lines),col),true);
5408   prefcol=col;
5409   return 1;
5410   }
5411 
5412 
5413 // Page down
onCmdCursorPageDown(FXObject *,FXSelector,void *)5414 long FXText::onCmdCursorPageDown(FXObject*,FXSelector,void*){
5415   FXint col=(0<=prefcol) ? prefcol : cursorcol;
5416   FXint lines=FXMAX(nvisrows-2,1);
5417   setTopLine(nextRow(toppos,lines));
5418   moveCursor(posFromColumn(nextRow(cursorpos,lines),col),true);
5419   prefcol=col;
5420   return 1;
5421   }
5422 
5423 
5424 // Process cursor word left
onCmdCursorWordLeft(FXObject *,FXSelector,void *)5425 long FXText::onCmdCursorWordLeft(FXObject*,FXSelector,void*){
5426   moveCursor(leftWord(cursorpos),true);
5427   return 1;
5428   }
5429 
5430 
5431 // Process cursor word right
onCmdCursorWordRight(FXObject *,FXSelector,void *)5432 long FXText::onCmdCursorWordRight(FXObject*,FXSelector,void*){
5433   moveCursor(rightWord(cursorpos),true);
5434   return 1;
5435   }
5436 
5437 
5438 // Process cursor shift+top
onCmdCursorShiftTop(FXObject *,FXSelector,void *)5439 long FXText::onCmdCursorShiftTop(FXObject*,FXSelector,void*){
5440   moveCursorAndSelect(0,SelectChars,true);
5441   return 1;
5442   }
5443 
5444 
5445 // Process cursor shift+bottom
onCmdCursorShiftBottom(FXObject *,FXSelector,void *)5446 long FXText::onCmdCursorShiftBottom(FXObject*,FXSelector,void*){
5447   moveCursorAndSelect(length,SelectChars,true);
5448   return 1;
5449   }
5450 
5451 
5452 // Process cursor shift+home
onCmdCursorShiftHome(FXObject *,FXSelector,void *)5453 long FXText::onCmdCursorShiftHome(FXObject*,FXSelector,void*){
5454   moveCursorAndSelect(lineStart(cursorpos),SelectChars,true);
5455   return 1;
5456   }
5457 
5458 
5459 // Process cursor shift+end
onCmdCursorShiftEnd(FXObject *,FXSelector,void *)5460 long FXText::onCmdCursorShiftEnd(FXObject*,FXSelector,void*){
5461   moveCursorAndSelect(lineEnd(cursorpos),SelectChars,true);
5462   return 1;
5463   }
5464 
5465 
5466 // Process cursor shift+right
onCmdCursorShiftRight(FXObject *,FXSelector,void *)5467 long FXText::onCmdCursorShiftRight(FXObject*,FXSelector,void*){
5468   moveCursorAndSelect(cursorpos<length?inc(cursorpos):length,SelectChars,true);
5469   return 1;
5470   }
5471 
5472 
5473 // Process cursor shift+left
onCmdCursorShiftLeft(FXObject *,FXSelector,void *)5474 long FXText::onCmdCursorShiftLeft(FXObject*,FXSelector,void*){
5475   moveCursorAndSelect(0<cursorpos?dec(cursorpos):0,SelectChars,true);
5476   return 1;
5477   }
5478 
5479 
5480 // Process cursor shift+up
onCmdCursorShiftUp(FXObject *,FXSelector,void *)5481 long FXText::onCmdCursorShiftUp(FXObject*,FXSelector,void*){
5482   FXint col=(0<=prefcol) ? prefcol : cursorcol;
5483   moveCursorAndSelect(posFromColumn(prevRow(cursorpos),col),SelectChars,true);
5484   prefcol=col;
5485   return 1;
5486   }
5487 
5488 
5489 // Process cursor shift+down
onCmdCursorShiftDown(FXObject *,FXSelector,void *)5490 long FXText::onCmdCursorShiftDown(FXObject*,FXSelector,void*){
5491   FXint col=(0<=prefcol) ? prefcol : cursorcol;
5492   moveCursorAndSelect(posFromColumn(nextRow(cursorpos),col),SelectChars,true);
5493   prefcol=col;
5494   return 1;
5495   }
5496 
5497 
5498 // Process cursor shift+page up
onCmdCursorShiftPageUp(FXObject *,FXSelector,void *)5499 long FXText::onCmdCursorShiftPageUp(FXObject*,FXSelector,void*){
5500   FXint col=(0<=prefcol) ? prefcol : cursorcol;
5501   FXint lines=FXMAX(nvisrows-2,1);
5502   setTopLine(prevRow(toppos,lines));
5503   moveCursorAndSelect(posFromColumn(prevRow(cursorpos,lines),col),SelectChars,true);
5504   prefcol=col;
5505   return 1;
5506   }
5507 
5508 
5509 // Process cursor shift+page down
onCmdCursorShiftPageDown(FXObject *,FXSelector,void *)5510 long FXText::onCmdCursorShiftPageDown(FXObject*,FXSelector,void*){
5511   FXint col=(0<=prefcol) ? prefcol : cursorcol;
5512   FXint lines=FXMAX(nvisrows-2,1);
5513   setTopLine(nextRow(toppos,lines));
5514   moveCursorAndSelect(posFromColumn(nextRow(cursorpos,lines),col),SelectChars,true);
5515   prefcol=col;
5516   return 1;
5517   }
5518 
5519 
5520 // Process cursor shift+word left
onCmdCursorShiftWordLeft(FXObject *,FXSelector,void *)5521 long FXText::onCmdCursorShiftWordLeft(FXObject*,FXSelector,void*){
5522   moveCursorAndSelect(leftWord(cursorpos),SelectChars,true);
5523   return 1;
5524   }
5525 
5526 
5527 // Process cursor shift+word right
onCmdCursorShiftWordRight(FXObject *,FXSelector,void *)5528 long FXText::onCmdCursorShiftWordRight(FXObject*,FXSelector,void*){
5529   moveCursorAndSelect(rightWord(cursorpos),SelectChars,true);
5530   return 1;
5531   }
5532 
5533 
5534 // Process cursor alt-up
onCmdCursorAltUp(FXObject *,FXSelector,void *)5535 long FXText::onCmdCursorAltUp(FXObject*,FXSelector,void*){      // FIXME
5536   FXint col=(0<=prefcol) ? prefcol : cursorvcol;
5537   moveCursorRowColumnAndSelect(cursorrow-1,col,true);
5538   prefcol=col;
5539   return 1;
5540   }
5541 
5542 
5543 // Process cursor alt-down
onCmdCursorAltDown(FXObject *,FXSelector,void *)5544 long FXText::onCmdCursorAltDown(FXObject*,FXSelector,void*){
5545   FXint col=(0<=prefcol) ? prefcol : cursorvcol;
5546   moveCursorRowColumnAndSelect(cursorrow+1,col,true);
5547   prefcol=col;
5548   return 1;
5549   }
5550 
5551 
5552 // Process cursor alt-left
onCmdCursorAltLeft(FXObject *,FXSelector,void *)5553 long FXText::onCmdCursorAltLeft(FXObject*,FXSelector,void*){
5554   moveCursorRowColumnAndSelect(cursorrow,cursorvcol-1,true);
5555   return 1;
5556   }
5557 
5558 
5559 // Process cursor alt-right
onCmdCursorAltRight(FXObject *,FXSelector,void *)5560 long FXText::onCmdCursorAltRight(FXObject*,FXSelector,void*){
5561   moveCursorRowColumnAndSelect(cursorrow,cursorvcol+1,true);
5562   return 1;
5563   }
5564 
5565 
5566 // Scroll up one line
onCmdScrollUp(FXObject *,FXSelector,void *)5567 long FXText::onCmdScrollUp(FXObject*,FXSelector,void*){
5568   setTopLine(prevRow(toppos));
5569   return 1;
5570   }
5571 
5572 
5573 // Scroll down one line
onCmdScrollDown(FXObject *,FXSelector,void *)5574 long FXText::onCmdScrollDown(FXObject*,FXSelector,void*){
5575   setTopLine(nextRow(toppos));
5576   return 1;
5577   }
5578 
5579 
5580 // Scroll to move cursor to top of screen
onCmdScrollTop(FXObject *,FXSelector,void *)5581 long FXText::onCmdScrollTop(FXObject*,FXSelector,void*){
5582   setTopLine(cursorpos);
5583   return 1;
5584   }
5585 
5586 
5587 // Scroll to move cursor to bottom of screen
onCmdScrollBottom(FXObject *,FXSelector,void *)5588 long FXText::onCmdScrollBottom(FXObject*,FXSelector,void*){
5589   setBottomLine(cursorpos);
5590   return 1;
5591   }
5592 
5593 
5594 // Scroll to move cursor to center of screen
onCmdScrollCenter(FXObject *,FXSelector,void *)5595 long FXText::onCmdScrollCenter(FXObject*,FXSelector,void*){
5596   setCenterLine(cursorpos);
5597   return 1;
5598   }
5599 
5600 
5601 // Insert a string as if typed
onCmdInsertString(FXObject *,FXSelector,void * ptr)5602 long FXText::onCmdInsertString(FXObject*,FXSelector,void* ptr){
5603   if(isEditable()){
5604     FXchar* txt=(FXchar*)ptr;
5605     FXint len=strlen(txt);
5606     FXint beg=cursorpos;
5607     FXint end=cursorpos;
5608     FXint rows,cols,ins;
5609     FXString rep;
5610 
5611     // Position is selected
5612     if(isPosSelected(cursorpos,cursorvcol)){
5613       beg=select.startpos;
5614       end=select.endpos;
5615 
5616       // Block selection
5617       if(select.startcol<select.endcol){
5618         rows=countLines(beg,end);
5619         cols=maxcolumns(txt,txt+len,tabcolumns);
5620         for(FXint i=0; i<rows; ++i){
5621           rep.append(txt,len);
5622           rep.append('\n');
5623           }
5624         rep.append(txt,len);
5625 //        ins=replaceTextBlock(select.startpos,select.endpos,select.startcol,select.endcol,txt,len,true);
5626         ins=replaceTextBlock(select.startpos,select.endpos,select.startcol,select.endcol,rep,true);
5627         beg=posFromColumn(lineStart(beg+ins),select.startcol+cols);
5628         moveCursor(beg,true);
5629         return 1;
5630         }
5631 
5632       // Range selection
5633       if(select.startpos<select.endpos){
5634         ins=replaceText(beg,end-beg,txt,len,true);
5635         moveCursor(beg+ins,true);
5636         return 1;
5637         }
5638       }
5639 
5640     // Overstrike
5641     if(isOverstrike()){         // FIXME should overstrike always be block-mode?
5642       ins=overstruck(beg,beg,txt,len);
5643       ins=replaceText(beg,ins-beg,txt,len,true);
5644       moveCursor(beg+ins,true);
5645       return 1;
5646       }
5647 
5648     // Default is insert
5649     ins=insertText(beg,txt,len,true);
5650     moveCursor(beg+ins,true);
5651     return 1;
5652     }
5653 
5654   getApp()->beep();
5655   return 1;
5656   }
5657 
5658 
5659 // Insert newline with optional autoindent
onCmdInsertNewline(FXObject *,FXSelector,void *)5660 long FXText::onCmdInsertNewline(FXObject*,FXSelector,void*){
5661   if(options&TEXT_AUTOINDENT){
5662     return onCmdInsertNewlineIndent(this,FXSEL(SEL_COMMAND,ID_INSERT_NEWLINE_INDENT),NULL);
5663     }
5664   return onCmdInsertNewlineOnly(this,FXSEL(SEL_COMMAND,ID_INSERT_NEWLINE_ONLY),NULL);
5665   }
5666 
5667 
5668 // Insert newline only
onCmdInsertNewlineOnly(FXObject *,FXSelector,void *)5669 long FXText::onCmdInsertNewlineOnly(FXObject*,FXSelector,void*){
5670   return onCmdInsertString(this,FXSEL(SEL_COMMAND,ID_INSERT_STRING),(void*)"\n");
5671   }
5672 
5673 
5674 // Insert a character
5675 // For block-select, just delete the block
onCmdInsertNewlineIndent(FXObject *,FXSelector,void *)5676 long FXText::onCmdInsertNewlineIndent(FXObject*,FXSelector,void*){
5677   FXString string;
5678   FXint start,n;
5679   if(isPosSelected(cursorpos,cursorvcol)){
5680     if(select.startcol>=select.endcol){
5681       start=lineStart(select.startpos);
5682       string="\n"+extractText(start,select.startpos-start);
5683       n=string.find_first_not_of(" \t\v\n");
5684       if(0<=n) string.trunc(n);
5685       }
5686     }
5687   else{
5688     start=lineStart(cursorpos);
5689     string="\n"+extractText(start,cursorpos-start);
5690     n=string.find_first_not_of(" \t\v\n");
5691     if(0<=n) string.trunc(n);
5692     }
5693   return onCmdInsertString(this,FXSEL(SEL_COMMAND,ID_INSERT_STRING),(void*)string.text());
5694   }
5695 
5696 
5697 // Insert optional soft-tab
onCmdInsertTab(FXObject *,FXSelector,void *)5698 long FXText::onCmdInsertTab(FXObject*,FXSelector,void*){
5699   if(options&TEXT_NO_TABS){
5700     return onCmdInsertSoftTab(this,FXSEL(SEL_COMMAND,ID_INSERT_SOFTTAB),NULL);
5701     }
5702   return onCmdInsertHardTab(this,FXSEL(SEL_COMMAND,ID_INSERT_HARDTAB),NULL);
5703   }
5704 
5705 
5706 // Insert hard-tab
onCmdInsertHardTab(FXObject *,FXSelector,void *)5707 long FXText::onCmdInsertHardTab(FXObject*,FXSelector,void*){
5708   return onCmdInsertString(this,FXSEL(SEL_COMMAND,ID_INSERT_STRING),(void*)"\t");
5709   }
5710 
5711 
5712 // Insert soft-tab
5713 // The number of spaces to insert depends on the indentation level.
5714 //   No selection:      use indent at the cursor insert-position
5715 //   Range-selection:   use indent at the start of the selection
5716 //   Block-selection:   use the start column of the selection
onCmdInsertSoftTab(FXObject *,FXSelector,void *)5717 long FXText::onCmdInsertSoftTab(FXObject*,FXSelector,void*){
5718   FXint indent;
5719   if(isPosSelected(cursorpos,cursorvcol)){
5720     if(select.startcol<select.endcol){
5721       indent=select.startcol;
5722       }
5723     else{
5724       indent=columnFromPos(lineStart(select.startpos),select.startpos);
5725       }
5726     }
5727   else{
5728     indent=columnFromPos(lineStart(cursorpos),cursorpos);
5729     }
5730   FXASSERT(0<tabcolumns && tabcolumns<MAXTABCOLUMNS);
5731   return onCmdInsertString(this,FXSEL(SEL_COMMAND,ID_INSERT_STRING),(void*)(spaces+MAXTABCOLUMNS+indent%tabcolumns-tabcolumns));
5732   }
5733 
5734 /*******************************************************************************/
5735 
5736 // Cut
onCmdCutSel(FXObject *,FXSelector,void *)5737 long FXText::onCmdCutSel(FXObject*,FXSelector,void*){
5738   if(isEditable() && cutSelection(true)) return 1;
5739   getApp()->beep();
5740   return 1;
5741   }
5742 
5743 
5744 // Copy
onCmdCopySel(FXObject *,FXSelector,void *)5745 long FXText::onCmdCopySel(FXObject*,FXSelector,void*){
5746   copySelection();
5747   return 1;
5748   }
5749 
5750 
5751 // Paste clipboard
onCmdPasteSel(FXObject *,FXSelector,void *)5752 long FXText::onCmdPasteSel(FXObject*,FXSelector,void*){
5753   if(isEditable() && pasteClipboard(true)) return 1;
5754   getApp()->beep();
5755   return 1;
5756   }
5757 
5758 
5759 // Paste selection
onCmdPasteMiddle(FXObject *,FXSelector,void *)5760 long FXText::onCmdPasteMiddle(FXObject*,FXSelector,void*){
5761   if(isEditable() && pasteSelection(true)) return 1;
5762   getApp()->beep();
5763   return 1;
5764   }
5765 
5766 
5767 // Delete selection
onCmdDeleteSel(FXObject *,FXSelector,void *)5768 long FXText::onCmdDeleteSel(FXObject*,FXSelector,void*){
5769   if(isEditable() && deleteSelection(true)) return 1;
5770   getApp()->beep();
5771   return 1;
5772   }
5773 
5774 
5775 // Select character
onCmdSelectChar(FXObject *,FXSelector,void *)5776 long FXText::onCmdSelectChar(FXObject*,FXSelector,void*){
5777   setAnchorPos(cursorpos);
5778   extendSelection(inc(cursorpos),SelectChars,true);
5779   return 1;
5780   }
5781 
5782 
5783 // Select Word
onCmdSelectWord(FXObject *,FXSelector,void *)5784 long FXText::onCmdSelectWord(FXObject*,FXSelector,void*){
5785   setAnchorPos(cursorpos);
5786   extendSelection(cursorpos,SelectWords,true);
5787   return 1;
5788   }
5789 
5790 
5791 // Select Line
onCmdSelectLine(FXObject *,FXSelector,void *)5792 long FXText::onCmdSelectLine(FXObject*,FXSelector,void*){
5793   setAnchorPos(cursorpos);
5794   extendSelection(cursorpos,SelectLines,true);
5795   return 1;
5796   }
5797 
5798 
5799 // Select text till matching character
onCmdSelectMatching(FXObject *,FXSelector,void *)5800 long FXText::onCmdSelectMatching(FXObject*,FXSelector,void*){
5801   if(0<cursorpos){
5802     FXchar ch=getByte(cursorpos-1);
5803     FXint pos=findMatching(cursorpos-1,0,length,ch,1);
5804     if(0<=pos){
5805       if(cursorpos<=pos){
5806         setSelection(cursorpos-1,pos-cursorpos+2,true);
5807         setAnchorPos(cursorpos-1);
5808         setCursorPos(pos+1,true);
5809         }
5810       else{
5811         setSelection(pos,cursorpos-pos,true);
5812         setAnchorPos(cursorpos);
5813         setCursorPos(pos+1,true);
5814         }
5815       makePositionVisible(cursorpos);
5816       flashMatching();
5817       return 1;
5818       }
5819     }
5820   getApp()->beep();
5821   return 1;
5822   }
5823 
5824 
5825 // Select entire enclosing block
onCmdSelectEnclosing(FXObject *,FXSelector sel,void *)5826 long FXText::onCmdSelectEnclosing(FXObject*,FXSelector sel,void*){
5827   FXint what=FXSELID(sel)-ID_SELECT_BRACE;
5828   FXint level=1;
5829   FXint beg,end;
5830   while(1){
5831     beg=matchBackward(cursorpos-1,0,lefthand[what],righthand[what],level);
5832     end=matchForward(cursorpos,length,lefthand[what],righthand[what],level);
5833     if(0<=beg && beg<end){
5834       if(isPosSelected(beg) && isPosSelected(end+1)){ level++; continue; }
5835       setAnchorPos(beg);
5836       extendSelection(end+1,SelectChars,true);
5837       return 1;
5838       }
5839     getApp()->beep();
5840     break;
5841     }
5842   return 1;
5843   }
5844 
5845 
5846 // Select All
onCmdSelectAll(FXObject *,FXSelector,void *)5847 long FXText::onCmdSelectAll(FXObject*,FXSelector,void*){
5848   setAnchorPos(0);
5849   extendSelection(length,SelectChars,true);
5850   return 1;
5851   }
5852 
5853 
5854 // Deselect All
onCmdDeselectAll(FXObject *,FXSelector,void *)5855 long FXText::onCmdDeselectAll(FXObject*,FXSelector,void*){
5856   killSelection(true);
5857   return 1;
5858   }
5859 
5860 /*******************************************************************************/
5861 
5862 // Backspace character
onCmdBackspaceChar(FXObject *,FXSelector,void *)5863 long FXText::onCmdBackspaceChar(FXObject*,FXSelector,void*){
5864   if(isEditable()){
5865     if(deletePendingSelection(true)) return 1;
5866     if(0<cursorpos){
5867       FXint pos=dec(cursorpos);
5868       removeText(pos,cursorpos-pos,true);
5869       moveCursor(pos,true);
5870       return 1;
5871       }
5872     }
5873   getApp()->beep();
5874   return 1;
5875   }
5876 
5877 
5878 // Backspace word
onCmdBackspaceWord(FXObject *,FXSelector,void *)5879 long FXText::onCmdBackspaceWord(FXObject*,FXSelector,void*){
5880   if(isEditable()){
5881     if(deletePendingSelection(true)) return 1;
5882     FXint pos=leftWord(cursorpos);
5883     if(pos<cursorpos){
5884       removeText(pos,cursorpos-pos,true);
5885       moveCursor(pos,true);
5886       return 1;
5887       }
5888     }
5889   getApp()->beep();
5890   return 1;
5891   }
5892 
5893 
5894 // Backspace bol
onCmdBackspaceBol(FXObject *,FXSelector,void *)5895 long FXText::onCmdBackspaceBol(FXObject*,FXSelector,void*){
5896   if(isEditable()){
5897     if(deletePendingSelection(true)) return 1;
5898     FXint pos=lineStart(cursorpos);
5899     if(pos<cursorpos){
5900       removeText(pos,cursorpos-pos,true);
5901       moveCursor(pos,true);
5902       return 1;
5903       }
5904     }
5905   getApp()->beep();
5906   return 1;
5907   }
5908 
5909 
5910 // Delete character
onCmdDeleteChar(FXObject *,FXSelector,void *)5911 long FXText::onCmdDeleteChar(FXObject*,FXSelector,void*){
5912   if(isEditable()){
5913     if(deletePendingSelection(true)) return 1;
5914     if(cursorpos<length){
5915       FXint pos=inc(cursorpos);
5916       removeText(cursorpos,pos-cursorpos,true);
5917       moveCursor(cursorpos,true);
5918       return 1;
5919       }
5920     }
5921   getApp()->beep();
5922   return 1;
5923   }
5924 
5925 
5926 // Delete word
onCmdDeleteWord(FXObject *,FXSelector,void *)5927 long FXText::onCmdDeleteWord(FXObject*,FXSelector,void*){
5928   if(isEditable()){
5929     if(deletePendingSelection(true)) return 1;
5930     FXint pos=rightWord(cursorpos);
5931     if(pos<length){
5932       removeText(cursorpos,pos-cursorpos,true);
5933       moveCursor(cursorpos,true);
5934       return 1;
5935       }
5936     }
5937   getApp()->beep();
5938   return 1;
5939   }
5940 
5941 
5942 // Delete to end of line
onCmdDeleteEol(FXObject *,FXSelector,void *)5943 long FXText::onCmdDeleteEol(FXObject*,FXSelector,void*){
5944   if(isEditable()){
5945     if(deletePendingSelection(true)) return 1;
5946     FXint pos=lineEnd(cursorpos);
5947     if(pos<length){
5948       removeText(cursorpos,pos-cursorpos,true);
5949       moveCursor(cursorpos,true);
5950       return 1;
5951       }
5952     }
5953   getApp()->beep();
5954   return 1;
5955   }
5956 
5957 
5958 // Delete line
onCmdDeleteLine(FXObject *,FXSelector,void *)5959 long FXText::onCmdDeleteLine(FXObject*,FXSelector,void*){
5960   if(isEditable()){
5961     if(deletePendingSelection(true)) return 1;
5962     FXint beg=lineStart(cursorpos);
5963     FXint end=nextLine(cursorpos);
5964     if(beg<end){
5965       removeText(beg,end-beg,true);
5966       moveCursor(beg,true);
5967       return 1;
5968       }
5969     }
5970   getApp()->beep();
5971   return 1;
5972   }
5973 
5974 
5975 // Delete all text
onCmdDeleteAll(FXObject *,FXSelector,void *)5976 long FXText::onCmdDeleteAll(FXObject*,FXSelector,void*){
5977   if(isEditable()){
5978     if(0<length){
5979       removeText(0,length,true);
5980       moveCursor(0,true);
5981       return 1;
5982       }
5983     }
5984   getApp()->beep();
5985   return 1;
5986   }
5987 
5988 /*******************************************************************************/
5989 
5990 // Shift selected lines left or right, or clean indent
5991 // Try keep the cursor on same row and (adjusted) column as before
onCmdShiftText(FXObject *,FXSelector sel,void *)5992 long FXText::onCmdShiftText(FXObject*,FXSelector sel,void*){
5993   if(isEditable()){
5994     FXint startpos,endpos,len;
5995     FXint curc=getCursorColumn();
5996     FXint curr=getCursorRow();
5997     FXint indent=0;
5998     switch(FXSELID(sel)){
5999       case ID_SHIFT_LEFT: indent=-1; break;
6000       case ID_SHIFT_RIGHT: indent=1; break;
6001       case ID_SHIFT_TABLEFT: indent=-tabcolumns; break;
6002       case ID_SHIFT_TABRIGHT: indent=tabcolumns; break;
6003       }
6004     if(select.startpos<=select.endpos){
6005       startpos=lineStart(select.startpos);
6006       endpos=nextLine(select.endpos-1);
6007       }
6008     else{
6009       startpos=lineStart(cursorpos);
6010       endpos=lineEnd(cursorpos);
6011       if(endpos<length) endpos++;
6012       }
6013     len=shiftText(startpos,endpos,indent,true);
6014     setSelection(startpos,len,true);
6015     setAnchorRowColumn(curr,FXMAX(curc+indent,0));
6016     setCursorRowColumn(curr,FXMAX(curc+indent,0),true);
6017     }
6018   else{
6019     getApp()->beep();
6020     }
6021   return 1;
6022   }
6023 
6024 /*******************************************************************************/
6025 
6026 // Make selected text upper case
onCmdChangeCase(FXObject *,FXSelector sel,void *)6027 long FXText::onCmdChangeCase(FXObject*,FXSelector sel,void*){
6028   if(isEditable()){
6029     FXint startpos,endpos,len;
6030     FXint upper=(FXSELID(sel)==ID_UPPER_CASE);
6031     FXint curc=getCursorColumn();
6032     FXint curr=getCursorRow();
6033     if(select.startpos<=select.endpos){
6034       startpos=select.startpos;
6035       endpos=select.endpos;
6036       }
6037     else{
6038       startpos=cursorpos;
6039       endpos=inc(cursorpos);
6040       }
6041     len=caseShift(startpos,endpos,upper,true);
6042     setSelection(startpos,len,true);
6043     setAnchorRowColumn(curr,curc);
6044     setCursorRowColumn(curr,curc,true);
6045     }
6046   else{
6047     getApp()->beep();
6048     }
6049   return 1;
6050   }
6051 
6052 /*******************************************************************************/
6053 
6054 // Copy current line to the line below; leave it selected with cursor at the end
onCmdCopyLine(FXObject *,FXSelector,void *)6055 long FXText::onCmdCopyLine(FXObject*,FXSelector,void*){
6056   if(isEditable()){
6057     FXString text;
6058     FXint start,end;
6059     if(select.startpos<=select.endpos){
6060       start=lineStart(select.startpos);
6061       end=lineEnd(select.endpos-1);
6062       }
6063     else{
6064       start=lineStart(cursorpos);
6065       end=lineEnd(cursorpos);
6066       }
6067     text=extractText(start,end-start);
6068     text+='\n';
6069     insertText(start,text,true);
6070     setSelection(start+text.length(),text.length(),true);
6071     setAnchorPos(cursorpos);
6072     makePositionVisible(cursorpos);
6073     return 1;
6074     }
6075   getApp()->beep();
6076   return 1;
6077   }
6078 
6079 /*******************************************************************************/
6080 
6081 // Move the current line up, if there is a line above it.
6082 // More tricky than it looks; current line may be non-terminated by a newline.
6083 // However, previous line *is* newline terminated by definition.
6084 // Solution is to snip the lines without the newline, and then place the
6085 // newline at the appropriate spot.
onCmdMoveLineUp(FXObject *,FXSelector,void *)6086 long FXText::onCmdMoveLineUp(FXObject*,FXSelector,void*){
6087   if(isEditable()){
6088     FXint curbeg,curend,prvbeg,pos;
6089     if(select.startpos<=select.endpos){
6090       curbeg=lineStart(select.startpos);
6091       curend=lineEnd(select.endpos-1);
6092       }
6093     else{
6094       curbeg=lineStart(cursorpos);
6095       curend=lineEnd(cursorpos);
6096       }
6097     FXASSERT(curbeg<=curend);
6098     prvbeg=prevLine(curbeg);
6099     if(0<curbeg){
6100       FXString text('\0',curend-prvbeg);
6101       pos=prvbeg+cursorpos-curbeg;
6102       extractText(&text[0],curbeg,curend-curbeg);
6103       text[curend-curbeg]='\n';
6104       extractText(&text[curend-curbeg+1],prvbeg,curbeg-prvbeg-1);
6105       replaceText(prvbeg,curend-prvbeg,text,true);
6106       setSelection(prvbeg,curend-curbeg+1,true);
6107       setAnchorPos(prvbeg);
6108       setCursorPos(pos,true);
6109       makePositionVisible(cursorpos);
6110       return 1;
6111       }
6112     }
6113   getApp()->beep();
6114   return 1;
6115   }
6116 
6117 
6118 // Move current line down, if there is a line below it.
6119 // Similar logic as above; the line to be moved up may be non-terminated by a newline.
6120 // The current line *is* newline terminated, by definition.
6121 // Thus we snip the lines w/o including the newline, and place the missing
6122 // newline at the proper place in the middle.
onCmdMoveLineDown(FXObject *,FXSelector,void *)6123 long FXText::onCmdMoveLineDown(FXObject*,FXSelector,void*){
6124   if(isEditable()){
6125     FXint curbeg,curend,nxtend,pos;
6126     if(select.startpos<=select.endpos){
6127       curbeg=lineStart(select.startpos);
6128       curend=nextLine(select.endpos-1);
6129       }
6130     else{
6131       curbeg=lineStart(cursorpos);
6132       curend=nextLine(cursorpos);
6133       }
6134     nxtend=lineEnd(curend);
6135     if(curend<length){
6136       FXString text('\0',nxtend-curbeg);
6137       pos=nxtend-curend+cursorpos;
6138       extractText(&text[0],curend,nxtend-curend);
6139       text[nxtend-curend]='\n';
6140       extractText(&text[nxtend-curend+1],curbeg,curend-curbeg-1);
6141       replaceText(curbeg,nxtend-curbeg,text,true);
6142       setSelection(curbeg+nxtend-curend+1,curend-curbeg,true);
6143       setAnchorPos(curbeg+nxtend-curend+1);
6144       setCursorPos(pos,true);
6145       makePositionVisible(cursorpos);
6146       return 1;
6147       }
6148     }
6149   getApp()->beep();
6150   return 1;
6151   }
6152 
6153 /*******************************************************************************/
6154 
6155 // Join lines
onCmdJoinLines(FXObject *,FXSelector,void *)6156 long FXText::onCmdJoinLines(FXObject*,FXSelector,void*){
6157   if(isEditable()){
6158     FXint pos=lineEnd(cursorpos);
6159     if(pos<length){
6160       removeText(pos,1,true);
6161       return 1;
6162       }
6163     }
6164   else{
6165     getApp()->beep();
6166     }
6167   return 1;
6168   }
6169 
6170 
6171 // Goto start of enclosing block
onCmdBlockBeg(FXObject *,FXSelector sel,void *)6172 long FXText::onCmdBlockBeg(FXObject*,FXSelector sel,void*){
6173   FXint what=FXSELID(sel)-ID_LEFT_BRACE;
6174   FXint beg=cursorpos-1;
6175   if(0<beg){
6176     if(getByte(beg)==lefthand[what]) beg--;
6177     FXint pos=matchBackward(beg,0,lefthand[what],righthand[what],1);
6178     if(0<=pos){
6179       moveCursor(pos+1,true);
6180       return 1;
6181       }
6182     }
6183   getApp()->beep();
6184   return 1;
6185   }
6186 
6187 
6188 // Goto end of enclosing block
onCmdBlockEnd(FXObject *,FXSelector sel,void *)6189 long FXText::onCmdBlockEnd(FXObject*,FXSelector sel,void*){
6190   FXint what=FXSELID(sel)-ID_RIGHT_BRACE;
6191   FXint start=cursorpos;
6192   if(start<length){
6193     if(getByte(start)==righthand[what]) start++;
6194     FXint pos=matchForward(start,length,lefthand[what],righthand[what],1);
6195     if(0<=pos){
6196       moveCursor(pos,true);
6197       return 1;
6198       }
6199     }
6200   getApp()->beep();
6201   return 1;
6202   }
6203 
6204 
6205 // Goto matching character
onCmdGotoMatching(FXObject *,FXSelector,void *)6206 long FXText::onCmdGotoMatching(FXObject*,FXSelector,void*){
6207   if(0<cursorpos){
6208     FXchar ch=getByte(cursorpos-1);
6209     FXint pos=findMatching(cursorpos-1,0,length,ch,1);
6210     if(0<=pos){
6211       moveCursor(pos+1,true);
6212       return 1;
6213       }
6214     }
6215   getApp()->beep();
6216   return 1;
6217   }
6218 
6219 
6220 // Move cursor to indicated row
onCmdCursorRow(FXObject * sender,FXSelector,void *)6221 long FXText::onCmdCursorRow(FXObject* sender,FXSelector,void*){
6222   FXint row=cursorrow+1;
6223   sender->handle(this,FXSEL(SEL_COMMAND,ID_GETINTVALUE),(void*)&row);
6224   setCursorRow(row-1,true);
6225   return 1;
6226   }
6227 
6228 
6229 // Being asked about current row number
onUpdCursorRow(FXObject * sender,FXSelector,void *)6230 long FXText::onUpdCursorRow(FXObject* sender,FXSelector,void*){
6231   FXint row=cursorrow+1;
6232   sender->handle(this,FXSEL(SEL_COMMAND,ID_SETINTVALUE),(void*)&row);
6233   return 1;
6234   }
6235 
6236 
6237 // Move cursor to indicated column
onCmdCursorColumn(FXObject * sender,FXSelector,void *)6238 long FXText::onCmdCursorColumn(FXObject* sender,FXSelector,void*){
6239   FXint col=cursorcol;
6240   sender->handle(this,FXSEL(SEL_COMMAND,ID_GETINTVALUE),(void*)&col);
6241   setCursorColumn(col,true);
6242   return 1;
6243   }
6244 
6245 
6246 // Being asked about current column
onUpdCursorColumn(FXObject * sender,FXSelector,void *)6247 long FXText::onUpdCursorColumn(FXObject* sender,FXSelector,void*){
6248   sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_SETINTVALUE),(void*)&cursorcol);
6249   return 1;
6250   }
6251 
6252 
6253 // Editable toggle
onCmdToggleEditable(FXObject *,FXSelector,void *)6254 long FXText::onCmdToggleEditable(FXObject*,FXSelector,void*){
6255   setEditable(!isEditable());
6256   return 1;
6257   }
6258 
6259 
6260 // Update editable toggle
onUpdToggleEditable(FXObject * sender,FXSelector,void *)6261 long FXText::onUpdToggleEditable(FXObject* sender,FXSelector,void*){
6262   sender->handle(this,isEditable()?FXSEL(SEL_COMMAND,ID_CHECK):FXSEL(SEL_COMMAND,ID_UNCHECK),NULL);
6263   sender->handle(this,FXSEL(SEL_COMMAND,ID_SHOW),NULL);
6264   sender->handle(this,FXSEL(SEL_COMMAND,ID_ENABLE),NULL);
6265   return 1;
6266   }
6267 
6268 
6269 // Overstrike toggle
onCmdToggleOverstrike(FXObject *,FXSelector,void *)6270 long FXText::onCmdToggleOverstrike(FXObject*,FXSelector,void*){
6271   setOverstrike(!isOverstrike());
6272   return 1;
6273   }
6274 
6275 
6276 // Update overstrike toggle
onUpdToggleOverstrike(FXObject * sender,FXSelector,void *)6277 long FXText::onUpdToggleOverstrike(FXObject* sender,FXSelector,void*){
6278   sender->handle(this,isOverstrike()?FXSEL(SEL_COMMAND,ID_CHECK):FXSEL(SEL_COMMAND,ID_UNCHECK),NULL);
6279   sender->handle(this,FXSEL(SEL_COMMAND,ID_SHOW),NULL);
6280   sender->handle(this,FXSEL(SEL_COMMAND,ID_ENABLE),NULL);
6281   return 1;
6282   }
6283 
6284 /*******************************************************************************/
6285 
6286 // Change text style
setTextStyle(FXuint style)6287 void FXText::setTextStyle(FXuint style){
6288   FXuint opts=((style^options)&TEXT_MASK)^options;
6289   if(options!=opts){
6290     options=opts;
6291     recalc();
6292     update();
6293     }
6294   }
6295 
6296 
6297 // Get text style
getTextStyle() const6298 FXuint FXText::getTextStyle() const {
6299   return (options&TEXT_MASK);
6300   }
6301 
6302 
6303 // Change the font
setFont(FXFont * fnt)6304 void FXText::setFont(FXFont* fnt){
6305   if(!fnt){ fxerror("%s::setFont: NULL font specified.\n",getClassName()); }
6306   if(font!=fnt){
6307     font=fnt;
6308     tabwidth=tabcolumns*font->getTextWidth(" ",1);
6309     barwidth=barcolumns*font->getTextWidth("8",1);
6310     if(getComposeContext()) getComposeContext()->setFont(font);
6311     recalc();
6312     update();
6313     }
6314   }
6315 
6316 
6317 // Change number of visible rows
setVisibleRows(FXint rows)6318 void FXText::setVisibleRows(FXint rows){
6319   if(rows<0) rows=0;
6320   if(vrows!=rows){
6321     vrows=rows;
6322     recalc();
6323     }
6324   }
6325 
6326 
6327 // Change number of visible columns
setVisibleColumns(FXint cols)6328 void FXText::setVisibleColumns(FXint cols){
6329   if(cols<0) cols=0;
6330   if(vcols!=cols){
6331     vcols=cols;
6332     recalc();
6333     }
6334   }
6335 
6336 
6337 // Change top margin
setMarginTop(FXint mt)6338 void FXText::setMarginTop(FXint mt){
6339   if(margintop!=mt){
6340     margintop=mt;
6341     recalc();
6342     update();
6343     }
6344   }
6345 
6346 
6347 // Change bottom margin
setMarginBottom(FXint mb)6348 void FXText::setMarginBottom(FXint mb){
6349   if(marginbottom!=mb){
6350     marginbottom=mb;
6351     recalc();
6352     update();
6353     }
6354   }
6355 
6356 
6357 // Change left margin
setMarginLeft(FXint ml)6358 void FXText::setMarginLeft(FXint ml){
6359   if(marginleft!=ml){
6360     marginleft=ml;
6361     recalc();
6362     update();
6363     }
6364   }
6365 
6366 
6367 // Change right margin
setMarginRight(FXint mr)6368 void FXText::setMarginRight(FXint mr){
6369   if(marginright!=mr){
6370     marginright=mr;
6371     recalc();
6372     update();
6373     }
6374   }
6375 
6376 
6377 // Change number of columns used for line numbers
setBarColumns(FXint cols)6378 void FXText::setBarColumns(FXint cols){
6379   if(cols<=0) cols=0;
6380   if(cols!=barcolumns){
6381     barcolumns=cols;
6382     barwidth=barcolumns*font->getTextWidth("8",1);
6383     recalc();
6384     update();
6385     }
6386   }
6387 
6388 
6389 // Set wrap columns
setWrapColumns(FXint cols)6390 void FXText::setWrapColumns(FXint cols){
6391   if(cols<=0) cols=1;
6392   if(cols!=wrapcolumns){
6393     wrapcolumns=cols;
6394     recalc();
6395     update();
6396     }
6397   }
6398 
6399 
6400 // Set tab columns
setTabColumns(FXint cols)6401 void FXText::setTabColumns(FXint cols){
6402   cols=FXCLAMP(1,cols,MAXTABCOLUMNS);
6403   if(cols!=tabcolumns){
6404     tabcolumns=cols;
6405     tabwidth=tabcolumns*font->getTextWidth(" ",1);
6406     recalc();
6407     update();
6408     }
6409   }
6410 
6411 
6412 // Set text color
setTextColor(FXColor clr)6413 void FXText::setTextColor(FXColor clr){
6414   if(clr!=textColor){
6415     textColor=clr;
6416     update();
6417     }
6418   }
6419 
6420 
6421 // Set select background color
setSelBackColor(FXColor clr)6422 void FXText::setSelBackColor(FXColor clr){
6423   if(clr!=selbackColor){
6424     selbackColor=clr;
6425     update();
6426     }
6427   }
6428 
6429 
6430 // Set selected text color
setSelTextColor(FXColor clr)6431 void FXText::setSelTextColor(FXColor clr){
6432   if(clr!=seltextColor){
6433     seltextColor=clr;
6434     update();
6435     }
6436   }
6437 
6438 
6439 // Change highlighted text color
setHiliteTextColor(FXColor clr)6440 void FXText::setHiliteTextColor(FXColor clr){
6441   if(clr!=hilitetextColor){
6442     hilitetextColor=clr;
6443     update();
6444     }
6445   }
6446 
6447 
6448 // Change highlighted background color
setHiliteBackColor(FXColor clr)6449 void FXText::setHiliteBackColor(FXColor clr){
6450   if(clr!=hilitebackColor){
6451     hilitebackColor=clr;
6452     update();
6453     }
6454   }
6455 
6456 
6457 // Change active background color
setActiveBackColor(FXColor clr)6458 void FXText::setActiveBackColor(FXColor clr){
6459   if(clr!=activebackColor){
6460     activebackColor=clr;
6461     update();
6462     }
6463   }
6464 
6465 // Set cursor color
setCursorColor(FXColor clr)6466 void FXText::setCursorColor(FXColor clr){
6467   if(clr!=cursorColor){
6468     cursorColor=clr;
6469     update();
6470     }
6471   }
6472 
6473 
6474 // Change line number color
setNumberColor(FXColor clr)6475 void FXText::setNumberColor(FXColor clr){
6476   if(clr!=numberColor){
6477     numberColor=clr;
6478     update();
6479     }
6480   }
6481 
6482 // Change bar color
setBarColor(FXColor clr)6483 void FXText::setBarColor(FXColor clr){
6484   if(clr!=barColor){
6485     barColor=clr;
6486     update();
6487     }
6488   }
6489 
6490 
6491 // Set styled text mode
setStyled(FXbool styled)6492 FXbool FXText::setStyled(FXbool styled){
6493   if(styled && !sbuffer){
6494     if(!callocElms(sbuffer,length+gapend-gapstart)) return false;
6495     update();
6496     }
6497   if(!styled && sbuffer){
6498     freeElms(sbuffer);
6499     update();
6500     }
6501   return true;
6502   }
6503 
6504 
6505 // Set highlight styles
setHiliteStyles(FXHiliteStyle * styles)6506 void FXText::setHiliteStyles(FXHiliteStyle* styles){
6507   hilitestyles=styles;
6508   update();
6509   }
6510 
6511 
6512 // Save object to stream
save(FXStream & store) const6513 void FXText::save(FXStream& store) const {
6514   FXScrollArea::save(store);
6515   store << length;
6516   store.save(buffer,gapstart);
6517   store.save(buffer+gapend,length-gapstart);
6518   store << nvisrows;
6519   store.save(visrows,nvisrows+1);
6520   store << margintop;
6521   store << marginbottom;
6522   store << marginleft;
6523   store << marginright;
6524   store << wrapcolumns;
6525   store << tabcolumns;
6526   store << barcolumns;
6527   store << font;
6528   store << textColor;
6529   store << selbackColor;
6530   store << seltextColor;
6531   store << hilitebackColor;
6532   store << hilitetextColor;
6533   store << activebackColor;
6534   store << numberColor;
6535   store << cursorColor;
6536   store << barColor;
6537   store << vrows;
6538   store << vcols;
6539   store << help;
6540   store << tip;
6541   store << matchtime;
6542   }
6543 
6544 
6545 // Load object from stream
load(FXStream & store)6546 void FXText::load(FXStream& store){
6547   FXScrollArea::load(store);
6548   store >> length;
6549   allocElms(buffer,length+MINSIZE);
6550   store.load(buffer,length);
6551   gapstart=length;
6552   gapend=length+MINSIZE;
6553   store >> nvisrows;
6554   allocElms(visrows,nvisrows+1);
6555   store.load(visrows,nvisrows+1);
6556   store >> margintop;
6557   store >> marginbottom;
6558   store >> marginleft;
6559   store >> marginright;
6560   store >> wrapcolumns;
6561   store >> tabcolumns;
6562   store >> barcolumns;
6563   store >> font;
6564   store >> textColor;
6565   store >> selbackColor;
6566   store >> seltextColor;
6567   store >> hilitebackColor;
6568   store >> hilitetextColor;
6569   store >> activebackColor;
6570   store >> numberColor;
6571   store >> cursorColor;
6572   store >> barColor;
6573   store >> vrows;
6574   store >> vcols;
6575   store >> help;
6576   store >> tip;
6577   store >> matchtime;
6578   }
6579 
6580 
6581 // Clean up
~FXText()6582 FXText::~FXText(){
6583   getApp()->removeTimeout(this,ID_BLINK);
6584   getApp()->removeTimeout(this,ID_FLASH);
6585   getApp()->removeTimeout(this,ID_TIPTIMER);
6586   freeElms(buffer);
6587   freeElms(sbuffer);
6588   freeElms(visrows);
6589   buffer=(FXchar*)-1L;
6590   sbuffer=(FXchar*)-1L;
6591   visrows=(FXint*)-1L;
6592   font=(FXFont*)-1L;
6593   delimiters=(const FXchar*)-1L;
6594   hilitestyles=(FXHiliteStyle*)-1L;
6595   }
6596 
6597 }
6598