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