1 /********************************************************************************
2 * *
3 * M u l t i - L i ne T e x t O b j e c t *
4 * *
5 *********************************************************************************
6 * Copyright (C) 1998,2006 by Jeroen van der Zijp. All Rights Reserved. *
7 *********************************************************************************
8 * This library is free software; you can redistribute it and/or *
9 * modify it under the terms of the GNU Lesser General Public *
10 * License as published by the Free Software Foundation; either *
11 * version 2.1 of the License, or (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 GNU *
16 * Lesser General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU Lesser General Public *
19 * License along with this library; if not, write to the Free Software *
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
21 *********************************************************************************
22 * $Id: FXText.cpp,v 1.348.2.3 2007/06/29 13:47:37 fox Exp $ *
23 ********************************************************************************/
24 #include "xincs.h"
25 #include "fxver.h"
26 #include "fxdefs.h"
27 #include "fxkeys.h"
28 #include "fxascii.h"
29 #include "fxunicode.h"
30 #include "FXHash.h"
31 #include "FXThread.h"
32 #include "FXStream.h"
33 #include "FXString.h"
34 #include "FXRex.h"
35 #include "FXSize.h"
36 #include "FXPoint.h"
37 #include "FXRectangle.h"
38 #include "FXObject.h"
39 #include "FXRegistry.h"
40 #include "FXAccelTable.h"
41 #include "FXApp.h"
42 #include "FXDCWindow.h"
43 #include "FXFont.h"
44 #include "FXGIFIcon.h"
45 #include "FXScrollBar.h"
46 #include "FXText.h"
47 #include "FXInputDialog.h"
48 #include "FXReplaceDialog.h"
49 #include "FXSearchDialog.h"
50 #include "FX88591Codec.h"
51 #include "FXCP1252Codec.h"
52 #include "FXUTF16Codec.h"
53 #include "FXComposeContext.h"
54 #include "icons.h"
55
56
57
58 /*
59 Notes:
60 - Line start array is one longer than number of visible lines.
61 - We want both tab translated to spaces as well as tab-stops array.
62 - Control characters in the buffer are OK (e.g. ^L)
63 - Drag cursor should be same as normal one until drag starts!
64 - Change of cursor only implies makePositionVisible() if done by user.
65 - Breaking:
66 Soft-hyphen 173 \xAD
67 No break space 240 \xF0
68 - Buffer layout:
69
70 Content : A B C . . . . . . . . D E F G
71 Position : 0 1 2 3 4 5 6 length=7
72 Addresss : 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 buffersize=7+11-3=15
73 ^ ^
74 | |
75 gapstart=3 gapend=11 gaplen=11-3=8
76
77 The gap is moved around the buffer so newly added text
78 can be entered into the gap; when the gap becomes too small,
79 the buffer is resized. This gapped-buffer technique minimizes
80 the number of resizes of the buffer, and minimizes the number
81 of block moves.
82
83 The tail end of the visrows array will look like:
84
85 visrow[0]= 0: "Q R S T U V W \n"
86 visrow[1]= 8: "X Y Z"
87 visrow[2]=11: <no text>
88 visrow[3]=11: <no text> length = 11
89
90 The last legal position is length = 11.
91
92 - While resizing window, keep track of a position which should remain visible,
93 i.e. toppos=rowStart(position). The position is changed same as toppos, except during
94 resize.
95 - When changing text, if we're looking at the tail end of the buffer, avoid jumping
96 the top lines when the content hight shrinks.
97 - Add undo capability. (First undo will turn mod flag back off).
98 - Add incremental search, search/replace, selection search.
99 - Style table stuff.
100 - Need to allow for one single routine to update style buffer same as text buffer
101 - Suggested additional bindings:
102 Ctl-F Find
103 Ctl-R Replace
104 Ctl-G Find again (Shift-Ctl-G Find again backward)
105 Ctl-T Replace again (Shift-Ctl-G Replace again backward)
106 Ctl-L Goto line number
107 Ctl-E Goto selected
108 Ctl-I Indent (shift 1 character right)
109 Ctl-U Unindent (shift 1 character left)
110 Ctl-Z undo
111 Ctl-Y redo
112 Ctl-M Goto matching (Shift-Ctl-M backward)
113 Insert toggle overstrike mode
114 Brace matching
115 - Maybe put all keyboard bindings into accelerator table.
116 - Variable cursorcol should take tabcolumns into account.
117 - Italic fonts are bit problematic on border between selected/unselected text
118 due to kerning.
119 - Tab should work as tabcolumns columns when computing a column.
120 - Need rectangular selection capability.
121 - Perhaps split off buffer management into separate text buffer class (allows for multiple views).
122 - Need to implement regex search/replace.
123 - Add better support for subclassing (syntax coloring e.g.).
124 - Add support for line numbers.
125 - Improve book keeping based on line/column numbers, not rows/characters.
126 - If there is a style table, the style buffer is used as index into the style table,
127 allowing for up to 255 styles (style index==0 is the default style).
128 The style member in the FXHiliteStyle struct is used for underlining, strikeouts,
129 and other effects.
130 If there is NO style table but there is a style buffer, the style buffer can still
131 be used for underlining, strikeouts, and other effects.
132 - Sending SEL_CHANGED is pretty useless; should only be sent AFTER text change,
133 and void* should contain some sensible info.
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 - Middle mouse paste does not paste inside selection, and does not kill selection.
138 - When pasting or dropping whole lines, insert at begin of line instead of at cursor;
139 question:- how to know we're pasting whole lines?
140 - Need block cursor when in overstrike mode.
141 - Inserting lots of stuff should show cursor.
142 - Perhaps change text and style buffer to FXString for further complexity
143 reduction.
144 */
145
146
147 #define MINSIZE 80 // Minimum gap size
148 #define NVISROWS 20 // Initial visible rows
149
150 #define TEXT_MASK (TEXT_FIXEDWRAP|TEXT_WORDWRAP|TEXT_OVERSTRIKE|TEXT_READONLY|TEXT_NO_TABS|TEXT_AUTOINDENT|TEXT_SHOWACTIVE|TEXT_AUTOSCROLL)
151
152 using namespace FX;
153
154 /*******************************************************************************/
155
156 namespace FX {
157
158
159 // Map
160 FXDEFMAP(FXText) FXTextMap[]={
161 FXMAPFUNC(SEL_PAINT,0,FXText::onPaint),
162 FXMAPFUNC(SEL_MOTION,0,FXText::onMotion),
163 FXMAPFUNC(SEL_DRAGGED,0,FXText::onDragged),
164 FXMAPFUNC(SEL_TIMEOUT,FXText::ID_BLINK,FXText::onBlink),
165 FXMAPFUNC(SEL_TIMEOUT,FXText::ID_AUTOSCROLL,FXText::onAutoScroll),
166 FXMAPFUNC(SEL_TIMEOUT,FXText::ID_FLASH,FXText::onFlash),
167 FXMAPFUNC(SEL_FOCUSIN,0,FXText::onFocusIn),
168 FXMAPFUNC(SEL_FOCUSOUT,0,FXText::onFocusOut),
169 FXMAPFUNC(SEL_BEGINDRAG,0,FXText::onBeginDrag),
170 FXMAPFUNC(SEL_ENDDRAG,0,FXText::onEndDrag),
171 FXMAPFUNC(SEL_LEFTBUTTONPRESS,0,FXText::onLeftBtnPress),
172 FXMAPFUNC(SEL_LEFTBUTTONRELEASE,0,FXText::onLeftBtnRelease),
173 FXMAPFUNC(SEL_MIDDLEBUTTONPRESS,0,FXText::onMiddleBtnPress),
174 FXMAPFUNC(SEL_MIDDLEBUTTONRELEASE,0,FXText::onMiddleBtnRelease),
175 FXMAPFUNC(SEL_RIGHTBUTTONPRESS,0,FXText::onRightBtnPress),
176 FXMAPFUNC(SEL_RIGHTBUTTONRELEASE,0,FXText::onRightBtnRelease),
177 FXMAPFUNC(SEL_UNGRABBED,0,FXText::onUngrabbed),
178 FXMAPFUNC(SEL_DND_ENTER,0,FXText::onDNDEnter),
179 FXMAPFUNC(SEL_DND_LEAVE,0,FXText::onDNDLeave),
180 FXMAPFUNC(SEL_DND_DROP,0,FXText::onDNDDrop),
181 FXMAPFUNC(SEL_DND_MOTION,0,FXText::onDNDMotion),
182 FXMAPFUNC(SEL_DND_REQUEST,0,FXText::onDNDRequest),
183 FXMAPFUNC(SEL_SELECTION_LOST,0,FXText::onSelectionLost),
184 FXMAPFUNC(SEL_SELECTION_GAINED,0,FXText::onSelectionGained),
185 FXMAPFUNC(SEL_SELECTION_REQUEST,0,FXText::onSelectionRequest),
186 FXMAPFUNC(SEL_CLIPBOARD_LOST,0,FXText::onClipboardLost),
187 FXMAPFUNC(SEL_CLIPBOARD_GAINED,0,FXText::onClipboardGained),
188 FXMAPFUNC(SEL_CLIPBOARD_REQUEST,0,FXText::onClipboardRequest),
189 FXMAPFUNC(SEL_KEYPRESS,0,FXText::onKeyPress),
190 FXMAPFUNC(SEL_KEYRELEASE,0,FXText::onKeyRelease),
191 FXMAPFUNC(SEL_QUERY_TIP,0,FXText::onQueryTip),
192 FXMAPFUNC(SEL_QUERY_HELP,0,FXText::onQueryHelp),
193 FXMAPFUNC(SEL_UPDATE,FXText::ID_TOGGLE_EDITABLE,FXText::onUpdToggleEditable),
194 FXMAPFUNC(SEL_UPDATE,FXText::ID_TOGGLE_OVERSTRIKE,FXText::onUpdToggleOverstrike),
195 FXMAPFUNC(SEL_UPDATE,FXText::ID_CURSOR_ROW,FXText::onUpdCursorRow),
196 FXMAPFUNC(SEL_UPDATE,FXText::ID_CURSOR_COLUMN,FXText::onUpdCursorColumn),
197 FXMAPFUNC(SEL_UPDATE,FXText::ID_CUT_SEL,FXText::onUpdHaveSelection),
198 FXMAPFUNC(SEL_UPDATE,FXText::ID_COPY_SEL,FXText::onUpdHaveSelection),
199 FXMAPFUNC(SEL_UPDATE,FXText::ID_PASTE_SEL,FXText::onUpdYes),
200 FXMAPFUNC(SEL_UPDATE,FXText::ID_DELETE_SEL,FXText::onUpdHaveSelection),
201 FXMAPFUNC(SEL_UPDATE,FXText::ID_SELECT_ALL,FXText::onUpdSelectAll),
202 FXMAPFUNC(SEL_UPDATE,FXText::ID_UPPER_CASE,FXText::onUpdHaveSelection),
203 FXMAPFUNC(SEL_UPDATE,FXText::ID_LOWER_CASE,FXText::onUpdHaveSelection),
204 FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_TOP,FXText::onCmdCursorTop),
205 FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_BOTTOM,FXText::onCmdCursorBottom),
206 FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_HOME,FXText::onCmdCursorHome),
207 FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_END,FXText::onCmdCursorEnd),
208 FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_RIGHT,FXText::onCmdCursorRight),
209 FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_LEFT,FXText::onCmdCursorLeft),
210 FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_UP,FXText::onCmdCursorUp),
211 FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_DOWN,FXText::onCmdCursorDown),
212 FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_WORD_LEFT,FXText::onCmdCursorWordLeft),
213 FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_WORD_RIGHT,FXText::onCmdCursorWordRight),
214 FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_WORD_START,FXText::onCmdCursorWordStart),
215 FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_WORD_END,FXText::onCmdCursorWordEnd),
216 FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_PAGEDOWN,FXText::onCmdCursorPageDown),
217 FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_PAGEUP,FXText::onCmdCursorPageUp),
218 FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_SCRNTOP,FXText::onCmdCursorScreenTop),
219 FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_SCRNBTM,FXText::onCmdCursorScreenBottom),
220 FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_SCRNCTR,FXText::onCmdCursorScreenCenter),
221 FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_PAR_HOME,FXText::onCmdCursorParHome),
222 FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_PAR_END,FXText::onCmdCursorParEnd),
223 FXMAPFUNC(SEL_COMMAND,FXText::ID_SCROLL_UP,FXText::onCmdScrollUp),
224 FXMAPFUNC(SEL_COMMAND,FXText::ID_SCROLL_DOWN,FXText::onCmdScrollDown),
225 FXMAPFUNC(SEL_COMMAND,FXText::ID_MARK,FXText::onCmdMark),
226 FXMAPFUNC(SEL_COMMAND,FXText::ID_EXTEND,FXText::onCmdExtend),
227 FXMAPFUNC(SEL_COMMAND,FXText::ID_OVERST_STRING,FXText::onCmdOverstString),
228 FXMAPFUNC(SEL_COMMAND,FXText::ID_INSERT_STRING,FXText::onCmdInsertString),
229 FXMAPFUNC(SEL_COMMAND,FXText::ID_INSERT_NEWLINE,FXText::onCmdInsertNewline),
230 FXMAPFUNC(SEL_COMMAND,FXText::ID_INSERT_TAB,FXText::onCmdInsertTab),
231 FXMAPFUNC(SEL_COMMAND,FXText::ID_CUT_SEL,FXText::onCmdCutSel),
232 FXMAPFUNC(SEL_COMMAND,FXText::ID_COPY_SEL,FXText::onCmdCopySel),
233 FXMAPFUNC(SEL_COMMAND,FXText::ID_DELETE_SEL,FXText::onCmdDeleteSel),
234 FXMAPFUNC(SEL_COMMAND,FXText::ID_PASTE_SEL,FXText::onCmdPasteSel),
235 FXMAPFUNC(SEL_COMMAND,FXText::ID_PASTE_MIDDLE,FXText::onCmdPasteMiddle),
236 FXMAPFUNC(SEL_COMMAND,FXText::ID_SELECT_CHAR,FXText::onCmdSelectChar),
237 FXMAPFUNC(SEL_COMMAND,FXText::ID_SELECT_WORD,FXText::onCmdSelectWord),
238 FXMAPFUNC(SEL_COMMAND,FXText::ID_SELECT_LINE,FXText::onCmdSelectLine),
239 FXMAPFUNC(SEL_COMMAND,FXText::ID_SELECT_ALL,FXText::onCmdSelectAll),
240 FXMAPFUNC(SEL_COMMAND,FXText::ID_DESELECT_ALL,FXText::onCmdDeselectAll),
241 FXMAPFUNC(SEL_COMMAND,FXText::ID_BACKSPACE,FXText::onCmdBackspace),
242 FXMAPFUNC(SEL_COMMAND,FXText::ID_BACKSPACE_WORD,FXText::onCmdBackspaceWord),
243 FXMAPFUNC(SEL_COMMAND,FXText::ID_BACKSPACE_BOL,FXText::onCmdBackspaceBol),
244 FXMAPFUNC(SEL_COMMAND,FXText::ID_DELETE,FXText::onCmdDelete),
245 FXMAPFUNC(SEL_COMMAND,FXText::ID_DELETE_WORD,FXText::onCmdDeleteWord),
246 FXMAPFUNC(SEL_COMMAND,FXText::ID_DELETE_EOL,FXText::onCmdDeleteEol),
247 FXMAPFUNC(SEL_COMMAND,FXText::ID_DELETE_ALL,FXText::onCmdDeleteAll),
248 FXMAPFUNC(SEL_COMMAND,FXText::ID_DELETE_LINE,FXText::onCmdDeleteLine),
249 FXMAPFUNC(SEL_COMMAND,FXText::ID_TOGGLE_EDITABLE,FXText::onCmdToggleEditable),
250 FXMAPFUNC(SEL_COMMAND,FXText::ID_TOGGLE_OVERSTRIKE,FXText::onCmdToggleOverstrike),
251 FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_ROW,FXText::onCmdCursorRow),
252 FXMAPFUNC(SEL_COMMAND,FXText::ID_CURSOR_COLUMN,FXText::onCmdCursorColumn),
253 FXMAPFUNC(SEL_COMMAND,FXText::ID_SETSTRINGVALUE,FXText::onCmdSetStringValue),
254 FXMAPFUNC(SEL_COMMAND,FXText::ID_GETSTRINGVALUE,FXText::onCmdGetStringValue),
255 FXMAPFUNC(SEL_COMMAND,FXText::ID_UPPER_CASE,FXText::onCmdChangeCase),
256 FXMAPFUNC(SEL_COMMAND,FXText::ID_LOWER_CASE,FXText::onCmdChangeCase),
257 FXMAPFUNC(SEL_COMMAND,FXText::ID_GOTO_MATCHING,FXText::onCmdGotoMatching),
258 FXMAPFUNC(SEL_COMMAND,FXText::ID_GOTO_SELECTED,FXText::onCmdGotoSelected),
259 FXMAPFUNC(SEL_COMMAND,FXText::ID_GOTO_LINE,FXText::onCmdGotoLine),
260 FXMAPFUNC(SEL_COMMAND,FXText::ID_SELECT_MATCHING,FXText::onCmdSelectMatching),
261 FXMAPFUNC(SEL_COMMAND,FXText::ID_SEARCH,FXText::onCmdSearch),
262 FXMAPFUNC(SEL_COMMAND,FXText::ID_REPLACE,FXText::onCmdReplace),
263 FXMAPFUNC(SEL_COMMAND,FXText::ID_SEARCH_FORW,FXText::onCmdSearchNext),
264 FXMAPFUNC(SEL_COMMAND,FXText::ID_SEARCH_BACK,FXText::onCmdSearchNext),
265 FXMAPFUNC(SEL_COMMAND,FXText::ID_SEARCH_FORW_SEL,FXText::onCmdSearchSel),
266 FXMAPFUNC(SEL_COMMAND,FXText::ID_SEARCH_BACK_SEL,FXText::onCmdSearchSel),
267 FXMAPFUNCS(SEL_COMMAND,FXText::ID_SELECT_BRACE,FXText::ID_SELECT_ANG,FXText::onCmdSelectBlock),
268 FXMAPFUNCS(SEL_COMMAND,FXText::ID_LEFT_BRACE,FXText::ID_LEFT_ANG,FXText::onCmdBlockBeg),
269 FXMAPFUNCS(SEL_COMMAND,FXText::ID_RIGHT_BRACE,FXText::ID_RIGHT_ANG,FXText::onCmdBlockEnd),
270 FXMAPFUNCS(SEL_COMMAND,FXText::ID_CLEAN_INDENT,FXText::ID_SHIFT_TABRIGHT,FXText::onCmdShiftText),
271 };
272
273
274 // Object implementation
275 FXIMPLEMENT(FXText,FXScrollArea,FXTextMap,ARRAYNUMBER(FXTextMap))
276
277
278 // Delimiters
279 const FXchar FXText::textDelimiters[]="~.,/\\`'!@#$%^&*()-=+{}|[]\":;<>?";
280
281 /*******************************************************************************/
282
283
284 // Absolute value
fxabs(FXint a)285 static inline FXint fxabs(FXint a){ return a<0?-a:a; }
286
287
288 // For deserialization
FXText()289 FXText::FXText(){
290 flags|=FLAG_ENABLED|FLAG_DROPTARGET;
291 buffer=NULL;
292 sbuffer=NULL;
293 visrows=NULL;
294 length=0;
295 nrows=1;
296 nvisrows=0;
297 gapstart=0;
298 gapend=0;
299 toppos=0;
300 keeppos=0;
301 toprow=0;
302 selstartpos=0;
303 selendpos=0;
304 hilitestartpos=0;
305 hiliteendpos=0;
306 anchorpos=0;
307 cursorpos=0;
308 revertpos=0;
309 cursorstart=0;
310 cursorend=0;
311 cursorrow=0;
312 cursorcol=0;
313 prefcol=-1;
314 wrapwidth=80;
315 wrapcolumns=80;
316 tabwidth=8;
317 tabcolumns=8;
318 barwidth=0;
319 barcolumns=0;
320 hilitestyles=NULL;
321 textWidth=0;
322 textHeight=0;
323 searchflags=SEARCH_EXACT;
324 delimiters=textDelimiters;
325 vrows=0;
326 vcols=0;
327 matchtime=0;
328 modified=FALSE;
329 mode=MOUSE_NONE;
330 grabx=0;
331 graby=0;
332 }
333
334
335 // 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)336 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):
337 FXScrollArea(p,opts,x,y,w,h){
338 flags|=FLAG_ENABLED|FLAG_DROPTARGET;
339 target=tgt;
340 message=sel;
341 FXCALLOC(&buffer,FXchar,MINSIZE);
342 sbuffer=NULL;
343 FXCALLOC(&visrows,FXint,NVISROWS+1);
344 length=0;
345 nrows=1;
346 nvisrows=NVISROWS;
347 gapstart=0;
348 gapend=MINSIZE;
349 toppos=0;
350 keeppos=0;
351 toprow=0;
352 selstartpos=0;
353 selendpos=0;
354 hilitestartpos=0;
355 hiliteendpos=0;
356 anchorpos=0;
357 cursorpos=0;
358 revertpos=0;
359 cursorstart=0;
360 cursorend=0;
361 cursorrow=0;
362 cursorcol=0;
363 prefcol=-1;
364 margintop=pt;
365 marginbottom=pb;
366 marginleft=pl;
367 marginright=pr;
368 wrapwidth=80;
369 wrapcolumns=80;
370 tabwidth=8;
371 tabcolumns=8;
372 barwidth=0;
373 barcolumns=0;
374 font=getApp()->getNormalFont();
375 hilitestyles=NULL;
376 defaultCursor=getApp()->getDefaultCursor(DEF_TEXT_CURSOR);
377 dragCursor=getApp()->getDefaultCursor(DEF_TEXT_CURSOR);
378 textColor=getApp()->getForeColor();
379 selbackColor=getApp()->getSelbackColor();
380 seltextColor=getApp()->getSelforeColor();
381 hilitebackColor=FXRGB(255,128,128);
382 hilitetextColor=getApp()->getForeColor();
383 activebackColor=backColor;
384 cursorColor=getApp()->getForeColor();
385 numberColor=textColor;
386 barColor=backColor;
387 textWidth=0;
388 textHeight=0;
389 searchflags=SEARCH_EXACT;
390 delimiters=textDelimiters;
391 vrows=0;
392 vcols=0;
393 matchtime=0;
394 modified=FALSE;
395 mode=MOUSE_NONE;
396 grabx=0;
397 graby=0;
398 }
399
400
401 // Create window
create()402 void FXText::create(){
403 FXScrollArea::create();
404 font->create();
405 if(!deleteType){ deleteType=getApp()->registerDragType(deleteTypeName); }
406 if(!textType){ textType=getApp()->registerDragType(textTypeName); }
407 if(!utf8Type){ utf8Type=getApp()->registerDragType(utf8TypeName); }
408 if(!utf16Type){ utf16Type=getApp()->registerDragType(utf16TypeName); }
409 // if(options&TEXT_FIXEDWRAP){ wrapwidth=wrapcolumns*font->getTextWidth("x",1); }
410 tabwidth=tabcolumns*font->getTextWidth(" ",1);
411 barwidth=barcolumns*font->getTextWidth("8",1);
412 recalc();
413 }
414
415
416 // Detach window
detach()417 void FXText::detach(){
418 FXScrollArea::detach();
419 font->detach();
420 deleteType=0;
421 textType=0;
422 utf8Type=0;
423 utf16Type=0;
424 }
425
426
427 // If window can have focus
canFocus() const428 bool FXText::canFocus() const {
429 return true;
430 }
431
432
433 // Into focus chain
setFocus()434 void FXText::setFocus(){
435 FXScrollArea::setFocus();
436 setDefault(TRUE);
437 flags&=~FLAG_UPDATE;
438 if(getApp()->hasInputMethod()){
439 createComposeContext();
440 }
441 }
442
443
444 // Out of focus chain
killFocus()445 void FXText::killFocus(){
446 FXScrollArea::killFocus();
447 setDefault(MAYBE);
448 flags|=FLAG_UPDATE;
449 if(getApp()->hasInputMethod()){
450 destroyComposeContext();
451 }
452 }
453
454
455 // Get default width
getDefaultWidth()456 FXint FXText::getDefaultWidth(){
457 if(0<vcols){ return marginleft+barwidth+marginright+vcols*font->getTextWidth("8",1); }
458 return FXScrollArea::getDefaultWidth();
459 }
460
461
462 // Get default height
getDefaultHeight()463 FXint FXText::getDefaultHeight(){
464 if(0<vrows){ return margintop+marginbottom+vrows*font->getFontHeight(); }
465 return FXScrollArea::getDefaultHeight();
466 }
467
468
469 // Enable the window
enable()470 void FXText::enable(){
471 if(!(flags&FLAG_ENABLED)){
472 FXScrollArea::enable();
473 update(0,0,viewport_w,viewport_h);
474 }
475 }
476
477
478 // Disable the window
disable()479 void FXText::disable(){
480 if(flags&FLAG_ENABLED){
481 FXScrollArea::disable();
482 update(0,0,viewport_w,viewport_h);
483 }
484 }
485
486
487 // Propagate size change
recalc()488 void FXText::recalc(){
489 FXScrollArea::recalc();
490 flags|=FLAG_RECALC;
491 }
492
493
494 /*******************************************************************************/
495
496
497 // Make a valid position, at the start of a wide character
validPos(FXint pos) const498 FXint FXText::validPos(FXint pos) const {
499 register const FXchar *ptr=pos<gapstart ? buffer : buffer-gapstart+gapend;
500 if(pos<=0) return 0;
501 if(pos>=length) return length;
502 return (FXISUTF(ptr[pos]) || --pos<=0 || FXISUTF(ptr[pos]) || --pos<=0 || FXISUTF(ptr[pos]) || --pos<=0 || FXISUTF(ptr[pos]) || --pos<=0 || FXISUTF(ptr[pos]) || --pos), pos;
503 }
504
505
506 // Decrement; a wide character does not cross the gap, so if pos is at
507 // or below below the gap, we read from the segment below the gap
dec(FXint pos) const508 FXint FXText::dec(FXint pos) const {
509 register const FXchar *ptr=pos<=gapstart ? buffer : buffer-gapstart+gapend;
510 return (--pos<=0 || FXISUTF(ptr[pos]) || --pos<=0 || FXISUTF(ptr[pos]) || --pos<=0 || FXISUTF(ptr[pos]) || --pos<=0 || FXISUTF(ptr[pos]) || --pos<=0 || FXISUTF(ptr[pos]) || --pos), pos;
511 }
512
513
514 // Increment; since a wide character does not cross the gap, if we
515 // start under the gap the last character accessed is below the gap
inc(FXint pos) const516 FXint FXText::inc(FXint pos) const {
517 register const FXchar *ptr=pos<gapstart ? buffer : buffer-gapstart+gapend;
518 return (++pos>=length || FXISUTF(ptr[pos]) || ++pos>=length || FXISUTF(ptr[pos]) || ++pos>=length || FXISUTF(ptr[pos]) || ++pos>=length || FXISUTF(ptr[pos]) || ++pos>=length || FXISUTF(ptr[pos]) || ++pos), pos;
519 }
520
521
522 // Get byte
getByte(FXint pos) const523 FXint FXText::getByte(FXint pos) const {
524 return (FXuchar)buffer[pos<gapstart ? pos : pos-gapstart+gapend];
525 }
526
527
528 // Get character, assuming that gap never inside utf8 encoding
getChar(FXint pos) const529 FXwchar FXText::getChar(FXint pos) const {
530 register const FXuchar* ptr=(pos<gapstart)?(FXuchar*)(buffer+pos):(FXuchar*)(buffer+pos-gapstart+gapend);
531 register FXwchar w=ptr[0];
532 if(0xC0<=w){ w=(w<<6)^ptr[1]^0x3080;
533 if(0x800<=w){ w=(w<<6)^ptr[2]^0x20080;
534 if(0x10000<=w){ w=(w<<6)^ptr[3]^0x400080;
535 if(0x200000<=w){ w=(w<<6)^ptr[4]^0x8000080;
536 if(0x4000000<=w){ w=(w<<6)^ptr[5]^0x80; }}}}}
537 return w;
538 }
539
540
541 // Get length of wide character at position pos
getCharLen(FXint pos) const542 FXint FXText::getCharLen(FXint pos) const {
543 return FXString::utfBytes[(FXuchar)buffer[pos<gapstart ? pos : pos-gapstart+gapend]];
544 }
545
546
547 // Get style
getStyle(FXint pos) const548 FXint FXText::getStyle(FXint pos) const {
549 return (FXuchar)sbuffer[pos<gapstart ? pos : pos-gapstart+gapend];
550 }
551
552
553 // Move the gap; gap is never moved inside utf character
movegap(FXint pos)554 void FXText::movegap(FXint pos){
555 register FXint gaplen=gapend-gapstart;
556 FXASSERT(0<=pos && pos<=length);
557 FXASSERT(0<=gapstart && gapstart<=length);
558 if(gapstart<pos){
559 memmove(&buffer[gapstart],&buffer[gapend],pos-gapstart);
560 if(sbuffer){memmove(&sbuffer[gapstart],&sbuffer[gapend],pos-gapstart);}
561 gapend=pos+gaplen;
562 gapstart=pos;
563 }
564 else if(pos<gapstart){
565 memmove(&buffer[pos+gaplen],&buffer[pos],gapstart-pos);
566 if(sbuffer){memmove(&sbuffer[pos+gaplen],&sbuffer[pos],gapstart-pos);}
567 gapend=pos+gaplen;
568 gapstart=pos;
569 }
570 }
571
572
573 // Size gap
sizegap(FXint sz)574 void FXText::sizegap(FXint sz){
575 register FXint gaplen=gapend-gapstart;
576 FXASSERT(0<=gapstart && gapstart<=length);
577 if(sz>=gaplen){
578 sz+=MINSIZE;
579 if(!FXRESIZE(&buffer,FXchar,length+sz)){
580 fxerror("%s::sizegap: out of memory.\n",getClassName());
581 }
582 memmove(&buffer[gapstart+sz],&buffer[gapend],length-gapstart);
583 if(sbuffer){
584 if(!FXRESIZE(&sbuffer,FXchar,length+sz)){
585 fxerror("%s::sizegap: out of memory.\n",getClassName());
586 }
587 memmove(&sbuffer[gapstart+sz],&sbuffer[gapend],length-gapstart);
588 }
589 gapend=gapstart+sz;
590 }
591 }
592
593
594 // Squeeze out the gap by moving it to the end of the buffer
squeezegap()595 void FXText::squeezegap(){
596 if(gapstart!=length){
597 memmove(&buffer[gapstart],&buffer[gapend],length-gapstart);
598 if(sbuffer){memmove(&sbuffer[gapstart],&sbuffer[gapend],length-gapstart);}
599 gapend=length+gapend-gapstart;
600 gapstart=length;
601 }
602 }
603
604
605 /*******************************************************************************/
606
607 // FIXME
608 // Its a little bit more complex than this:
609 // We need to deal with diacritics, i.e. non-spacing stuff. When wrapping, scan till
610 // the next starter-character [the one with charCombining(c)==0]. Then measure the
611 // string from that point on. This means FXFont::getCharWidth() is really quite useless.
612 // Next, we also have the issue of ligatures [fi, AE] and kerning-pairs [VA].
613 // With possible kerning pairs, we should really measure stuff from the start of the
614 // line [but this is *very* expensive!!]. We may want to just back up a few characters;
615 // perhaps to the start of the word, or just the previous character, if not a space.
616 // Need to investigate this some more; for now assume Normalization Form C.
617
618 // Character width
charWidth(FXwchar ch,FXint indent) const619 FXint FXText::charWidth(FXwchar ch,FXint indent) const {
620 if(ch<' '){
621 if(ch!='\t'){
622 return font->getCharWidth('^')+font->getCharWidth(ch|0x40);
623 }
624 return (tabwidth-indent%tabwidth);
625 }
626 return font->getCharWidth(ch);
627 }
628
629
630 // Start of next wrapped line
wrap(FXint start) const631 FXint FXText::wrap(FXint start) const {
632 register FXint lw,cw,p,s,c;
633 FXASSERT(0<=start && start<=length);
634 lw=0;
635 p=s=start;
636 while(p<length){
637 c=getChar(p);
638 if(c=='\n') return p+1; // Newline always breaks
639 cw=charWidth(c,lw);
640 if(lw+cw>wrapwidth){ // Technically, a tab-before-wrap should be as wide as space!
641 if(s>start) return s; // We remembered the last space we encountered; break there!
642 if(p==start) p++; // Always at least one character on each line!
643 return p;
644 }
645 lw+=cw;
646 p+=getCharLen(p);
647 if(Unicode::isSpace(c)) s=p;// Remember potential break point!
648 }
649 return length;
650 }
651
652
653 // Count number of newlines
countLines(FXint start,FXint end) const654 FXint FXText::countLines(FXint start,FXint end) const {
655 register FXint p,nl=0;
656 FXASSERT(0<=start && end<=length+1);
657 p=start;
658 while(p<end){
659 if(p>=length) return nl+1;
660 if(getByte(p)=='\n') nl++;
661 p++;
662 }
663 return nl;
664 }
665
666
667 // Count number of rows; start should be on a row start
countRows(FXint start,FXint end) const668 FXint FXText::countRows(FXint start,FXint end) const {
669 register FXint p,q,s,w=0,c,cw,nr=0;
670 FXASSERT(0<=start && end<=length+1);
671 if(options&TEXT_WORDWRAP){
672 p=q=s=start;
673 while(q<end){
674 if(p>=length) return nr+1;
675 c=getChar(p);
676 if(c=='\n'){ // Break at newline
677 nr++;
678 w=0;
679 p=q=s=p+1;
680 continue;
681 }
682 cw=charWidth(c,w);
683 if(w+cw>wrapwidth){ // Break due to wrap
684 nr++;
685 w=0;
686 if(s>q){ // Break past last space seen
687 p=q=s;
688 continue;
689 }
690 if(p==q) p+=getCharLen(p); // Break anywhere, but at least one character on each line
691 q=s=p;
692 continue;
693 }
694 w+=cw;
695 p+=getCharLen(p);
696 if(Unicode::isSpace(c)) s=p;
697 }
698 }
699 else{
700 p=start;
701 while(p<end){
702 if(p>=length) return nr+1;
703 c=getByte(p);
704 if(c=='\n') nr++;
705 p++;
706 }
707 }
708 return nr;
709 }
710
711
712 // Count number of columns; start should be on a row start
countCols(FXint start,FXint end) const713 FXint FXText::countCols(FXint start,FXint end) const {
714 register FXint nc=0,in=0,ch;
715 FXASSERT(0<=start && end<=length);
716 while(start<end){
717 ch=getChar(start);
718 if(ch=='\n'){
719 if(in>nc) nc=in;
720 in=0;
721 }
722 else if(ch=='\t'){
723 in+=(tabcolumns-nc%tabcolumns);
724 }
725 else{
726 in++;
727 }
728 start+=getCharLen(start);
729 }
730 if(in>nc) nc=in;
731 return nc;
732 }
733
734
735 // Measure lines; start and end should be on a row start
measureText(FXint start,FXint end,FXint & wmax,FXint & hmax) const736 FXint FXText::measureText(FXint start,FXint end,FXint& wmax,FXint& hmax) const {
737 register FXint nr=0,w=0,c,cw,p,q,s;
738 FXASSERT(0<=start && end<=length+1);
739 if(options&TEXT_WORDWRAP){
740 wmax=wrapwidth;
741 p=q=s=start;
742 while(q<end){
743 if(p>=length){
744 nr++;
745 break;
746 }
747 c=getChar(p);
748 if(c=='\n'){ // Break at newline
749 nr++;
750 w=0;
751 p=q=s=p+1;
752 continue;
753 }
754 cw=charWidth(c,w);
755 if(w+cw>wrapwidth){ // Break due to wrap
756 nr++;
757 w=0;
758 if(s>q){ // Break past last space seen
759 p=q=s;
760 continue;
761 }
762 if(p==q) p+=getCharLen(p); // Break anywhere, but at least one character on each line
763 q=s=p;
764 continue;
765 }
766 w+=cw;
767 p+=getCharLen(p);
768 if(Unicode::isSpace(c)) s=p;
769 }
770 }
771 else{
772 wmax=0;
773 p=start;
774 while(p<end){
775 if(p>=length){
776 if(w>wmax) wmax=w;
777 nr++;
778 break;
779 }
780 c=getChar(p);
781 if(c=='\n'){ // Break at newline
782 if(w>wmax) wmax=w;
783 nr++;
784 w=0;
785 }
786 else{
787 w+=charWidth(c,w);
788 }
789 p+=getCharLen(p);
790 }
791 }
792 hmax=nr*font->getFontHeight();
793 return nr;
794 }
795
796
797 // Check if w is delimiter
isdelimiter(const FXchar * delimiters,FXwchar w)798 static FXbool isdelimiter(const FXchar *delimiters,FXwchar w){
799 return w<128 && strchr(delimiters,w); // FIXME for w>=128
800 }
801
802
803 // Find end of previous word
leftWord(FXint pos) const804 FXint FXText::leftWord(FXint pos) const {
805 register FXint ch;
806 if(pos>length) pos=length;
807 if(0<pos){
808 ch=getChar(dec(pos));
809 if(isdelimiter(delimiters,ch)) return dec(pos);
810 }
811 while(0<pos){
812 ch=getChar(dec(pos));
813 if(isdelimiter(delimiters,ch)) return pos;
814 if(Unicode::isSpace(ch)) break;
815 pos=dec(pos);
816 }
817 while(0<pos){
818 ch=getChar(dec(pos));
819 if(!Unicode::isSpace(ch)) return pos;
820 pos=dec(pos);
821 }
822 return 0;
823 }
824
825
826 // Find begin of next word
rightWord(FXint pos) const827 FXint FXText::rightWord(FXint pos) const {
828 register FXint ch;
829 if(pos<0) pos=0;
830 if(pos<length){
831 ch=getChar(pos);
832 if(isdelimiter(delimiters,ch)) return inc(pos);
833 }
834 while(pos<length){
835 ch=getChar(pos);
836 if(isdelimiter(delimiters,ch)) return pos;
837 if(Unicode::isSpace(ch)) break;
838 pos=inc(pos);
839 }
840 while(pos<length){
841 ch=getChar(pos);
842 if(!Unicode::isSpace(ch)) return pos;
843 pos=inc(pos);
844 }
845 return length;
846 }
847
848
849 // Find begin of a word
wordStart(FXint pos) const850 FXint FXText::wordStart(FXint pos) const {
851 register FXint c=' ';
852 if(pos<=0) return 0;
853 if(pos<length) c=getChar(pos); else pos=length;
854 if(c==' ' || c=='\t'){
855 while(0<pos){
856 c=getChar(dec(pos));
857 if(c!=' ' && c!='\t') return pos;
858 pos=dec(pos);
859 }
860 }
861 else if(isdelimiter(delimiters,c)){
862 while(0<pos){
863 c=getChar(dec(pos));
864 if(!isdelimiter(delimiters,c)) return pos;
865 pos=dec(pos);
866 }
867 }
868 else{
869 while(0<pos){
870 c=getChar(dec(pos));
871 if(isdelimiter(delimiters,c) || Unicode::isSpace(c)) return pos;
872 pos=dec(pos);
873 }
874 }
875 return 0;
876 }
877
878
879 // Find end of word
wordEnd(FXint pos) const880 FXint FXText::wordEnd(FXint pos) const {
881 register FXint c=' ';
882 if(pos>=length) return length;
883 if(0<=pos) c=getChar(pos); else pos=0;
884 if(c==' ' || c=='\t'){
885 while(pos<length){
886 c=getChar(pos);
887 if(c!=' ' && c!='\t') return pos;
888 pos=inc(pos);
889 }
890 }
891 else if(isdelimiter(delimiters,c)){
892 while(pos<length){
893 c=getChar(pos);
894 if(!isdelimiter(delimiters,c)) return pos;
895 pos=inc(pos);
896 }
897 }
898 else{
899 while(pos<length){
900 c=getChar(pos);
901 if(isdelimiter(delimiters,c) || Unicode::isSpace(c)) return pos;
902 pos=inc(pos);
903 }
904 }
905 return length;
906 }
907
908
909 // Return position of begin of paragraph
lineStart(FXint pos) const910 FXint FXText::lineStart(FXint pos) const {
911 FXASSERT(0<=pos && pos<=length);
912 while(0<pos){ if(getByte(pos-1)=='\n') return pos; pos--; }
913 return 0;
914 }
915
916
917 // Return position of end of paragraph
lineEnd(FXint pos) const918 FXint FXText::lineEnd(FXint pos) const {
919 FXASSERT(0<=pos && pos<=length);
920 while(pos<length){ if(getByte(pos)=='\n') return pos; pos++; }
921 return length;
922 }
923
924
925 // Return start of next line
nextLine(FXint pos,FXint nl) const926 FXint FXText::nextLine(FXint pos,FXint nl) const {
927 FXASSERT(0<=pos && pos<=length);
928 if(nl<=0) return pos;
929 while(pos<length){
930 if(getByte(pos)=='\n' && --nl==0) return pos+1;
931 pos++;
932 }
933 return length;
934 }
935
936
937 // Return start of previous line
prevLine(FXint pos,FXint nl) const938 FXint FXText::prevLine(FXint pos,FXint nl) const {
939 FXASSERT(0<=pos && pos<=length);
940 if(nl<=0) return pos;
941 while(0<pos){
942 if(getByte(pos-1)=='\n' && nl--==0) return pos;
943 pos--;
944 }
945 return 0;
946 }
947
948
949 // Return row start
rowStart(FXint pos) const950 FXint FXText::rowStart(FXint pos) const {
951 register FXint p,t;
952 FXASSERT(0<=pos && pos<=length);
953 p=lineStart(pos);
954 if(!(options&TEXT_WORDWRAP)) return p;
955 while(p<pos && (t=wrap(p))<=pos && t<length) p=t;
956 FXASSERT(0<=p && p<=pos);
957 return p;
958 }
959
960
961 // Return row end
rowEnd(FXint pos) const962 FXint FXText::rowEnd(FXint pos) const {
963 register FXint p;
964 FXASSERT(0<=pos && pos<=length);
965 if(!(options&TEXT_WORDWRAP)) return lineEnd(pos);
966 p=lineStart(pos);
967 while(p<length && p<=pos) p=wrap(p);
968 FXASSERT(0<=p && p<=length);
969 if(pos<p && Unicode::isSpace(getChar(dec(p)))) p=dec(p);
970 FXASSERT(pos<=p && p<=length);
971 return p;
972 }
973
974
975 // Move to next row given start of line
nextRow(FXint pos,FXint nr) const976 FXint FXText::nextRow(FXint pos,FXint nr) const {
977 register FXint p;
978 FXASSERT(0<=pos && pos<=length);
979 if(!(options&TEXT_WORDWRAP)) return nextLine(pos,nr);
980 if(nr<=0) return pos;
981 p=rowStart(pos);
982 while(p<length && 0<nr){ p=wrap(p); nr--; }
983 FXASSERT(0<=p && p<=length);
984 return p;
985 }
986
987
988 // Move to previous row given start of line
prevRow(FXint pos,FXint nr) const989 FXint FXText::prevRow(FXint pos,FXint nr) const {
990 register FXint p,q,t;
991 FXASSERT(0<=pos && pos<=length);
992 if(!(options&TEXT_WORDWRAP)) return prevLine(pos,nr);
993 if(nr<=0) return pos;
994 while(0<pos){
995 p=lineStart(pos);
996 for(q=p; q<pos && (t=wrap(q))<=pos && t<length; q=t) nr--;
997 if(nr==0) return p;
998 if(nr<0){
999 do{p=wrap(p);}while(++nr);
1000 FXASSERT(0<=p && p<=length);
1001 return p;
1002 }
1003 pos=p-1;
1004 nr--;
1005 }
1006 return 0;
1007 }
1008
1009
1010 // Backs up to the begin of the line preceding the line containing pos, or the
1011 // start of the line containing pos if the preceding line terminated in a newline
changeBeg(FXint pos) const1012 FXint FXText::changeBeg(FXint pos) const {
1013 register FXint p1,p2,t;
1014 FXASSERT(0<=pos && pos<=length);
1015 p1=p2=lineStart(pos);
1016 if(!(options&TEXT_WORDWRAP)) return p1;
1017 while(p2<pos && (t=wrap(p2))<=pos){
1018 p1=p2;
1019 p2=t;
1020 }
1021 FXASSERT(0<=p1 && p1<=length);
1022 return p1;
1023 }
1024
1025
1026 // Scan forward to the end of affected area, which is the start of the next
1027 // paragraph; a change can cause the rest of the paragraph to reflow.
changeEnd(FXint pos) const1028 FXint FXText::changeEnd(FXint pos) const {
1029 FXASSERT(0<=pos && pos<=length);
1030 while(pos<length){
1031 if(getByte(pos)=='\n') return pos+1;
1032 pos++;
1033 }
1034 return length+1; // YES, one more!
1035 }
1036
1037
1038 // Calculate line width
lineWidth(FXint pos,FXint n) const1039 FXint FXText::lineWidth(FXint pos,FXint n) const {
1040 register FXint end=pos+n,w=0;
1041 FXASSERT(0<=pos && end<=length);
1042 while(pos<end){ w+=charWidth(getChar(pos),w); pos+=getCharLen(pos); }
1043 return w;
1044 }
1045
1046
1047 // Determine indent of position pos relative to start
indentFromPos(FXint start,FXint pos) const1048 FXint FXText::indentFromPos(FXint start,FXint pos) const {
1049 register FXint p=start;
1050 register FXint in=0;
1051 register FXwchar c;
1052 FXASSERT(0<=start && pos<=length);
1053 while(p<pos){
1054 c=getChar(p);
1055 if(c=='\n'){
1056 in=0;
1057 }
1058 else if(c=='\t'){
1059 in+=(tabcolumns-in%tabcolumns);
1060 }
1061 else{
1062 in+=1;
1063 }
1064 p+=getCharLen(p);
1065 }
1066 return in;
1067 }
1068
1069
1070 // Determine position of indent relative to start
posFromIndent(FXint start,FXint indent) const1071 FXint FXText::posFromIndent(FXint start,FXint indent) const {
1072 register FXint pos=start;
1073 register FXint in=0;
1074 register FXwchar c;
1075 FXASSERT(0<=start && start<=length);
1076 while(in<indent && pos<length){
1077 c=getChar(pos);
1078 if(c=='\n'){
1079 break;
1080 }
1081 else if(c=='\t'){
1082 in+=(tabcolumns-in%tabcolumns);
1083 }
1084 else{
1085 in+=1;
1086 }
1087 pos+=getCharLen(pos);
1088 }
1089 return pos;
1090 }
1091
1092
1093
1094 // Search forward for match
matchForward(FXint pos,FXint end,FXwchar l,FXwchar r,FXint level) const1095 FXint FXText::matchForward(FXint pos,FXint end,FXwchar l,FXwchar r,FXint level) const {
1096 register FXwchar c;
1097 FXASSERT(0<=end && end<=length);
1098 FXASSERT(0<=pos && pos<=length);
1099 while(pos<end){
1100 c=getChar(pos);
1101 if(c==r){
1102 level--;
1103 if(level<=0) return pos;
1104 }
1105 else if(c==l){
1106 level++;
1107 }
1108 pos=inc(pos);
1109 }
1110 return -1;
1111 }
1112
1113
1114 // Search backward for match
matchBackward(FXint pos,FXint beg,FXwchar l,FXwchar r,FXint level) const1115 FXint FXText::matchBackward(FXint pos,FXint beg,FXwchar l,FXwchar r,FXint level) const {
1116 register FXwchar c;
1117 FXASSERT(0<=beg && beg<=length);
1118 FXASSERT(0<=pos && pos<=length);
1119 while(beg<=pos){
1120 c=getChar(pos);
1121 if(c==l){
1122 level--;
1123 if(level<=0) return pos;
1124 }
1125 else if(c==r){
1126 level++;
1127 }
1128 pos=dec(pos);
1129 }
1130 return -1;
1131 }
1132
1133
1134 // Search for matching character
findMatching(FXint pos,FXint beg,FXint end,FXwchar ch,FXint level) const1135 FXint FXText::findMatching(FXint pos,FXint beg,FXint end,FXwchar ch,FXint level) const {
1136 FXASSERT(0<=level);
1137 FXASSERT(0<=pos && pos<=length);
1138 switch(ch){
1139 case '{': return matchForward(pos+1,end,'{','}',level);
1140 case '}': return matchBackward(pos-1,beg,'{','}',level);
1141 case '[': return matchForward(pos+1,end,'[',']',level);
1142 case ']': return matchBackward(pos-1,beg,'[',']',level);
1143 case '(': return matchForward(pos+1,end,'(',')',level);
1144 case ')': return matchBackward(pos-1,beg,'(',')',level);
1145 }
1146 return -1;
1147 }
1148
1149
1150 // Flash matching braces or parentheses, if within visible part of buffer
flashMatching()1151 void FXText::flashMatching(){
1152 FXint matchpos;
1153 killHighlight();
1154 getApp()->removeTimeout(this,ID_FLASH);
1155 if(matchtime && 0<cursorpos){
1156 matchpos=findMatching(cursorpos-1,visrows[0],visrows[nvisrows],getByte(cursorpos-1),1);
1157 if(0<=matchpos){
1158 getApp()->addTimeout(this,ID_FLASH,matchtime);
1159 setHighlight(matchpos,1);
1160 }
1161 }
1162 }
1163
1164
1165 // Search for text
findText(const FXString & string,FXint * beg,FXint * end,FXint start,FXuint flgs,FXint npar)1166 FXbool FXText::findText(const FXString& string,FXint* beg,FXint* end,FXint start,FXuint flgs,FXint npar){
1167 register FXint rexmode;
1168 FXRex rex;
1169
1170 // Compile flags
1171 rexmode=REX_VERBATIM;
1172 if(1<npar) rexmode|=REX_CAPTURE;
1173 if(flgs&SEARCH_REGEX) rexmode&=~REX_VERBATIM;
1174 if(flgs&SEARCH_IGNORECASE) rexmode|=REX_ICASE;
1175
1176 // Try parse the regex
1177 if(rex.parse(string,rexmode)==REGERR_OK){
1178
1179 // Make all characters contiguous in the buffer
1180 squeezegap();
1181
1182 // Search backward
1183 if(flgs&SEARCH_BACKWARD){
1184
1185 // Search from start to begin of buffer
1186 if(rex.match(buffer,length,beg,end,REX_BACKWARD,npar,0,start)) return TRUE;
1187
1188 if(!(flgs&SEARCH_WRAP)) return FALSE;
1189
1190 // Search from end of buffer backwards
1191 if(rex.match(buffer,length,beg,end,REX_BACKWARD,npar,start,length)) return TRUE;
1192 }
1193
1194 // Search forward
1195 else{
1196
1197 // Search from start to end of buffer
1198 if(rex.match(buffer,length,beg,end,REX_FORWARD,npar,start,length)) return TRUE;
1199
1200 if(!(flgs&SEARCH_WRAP)) return FALSE;
1201
1202 // Search from begin of buffer forwards
1203 if(rex.match(buffer,length,beg,end,REX_FORWARD,npar,0,start)) return TRUE;
1204 }
1205 }
1206 return FALSE;
1207 }
1208
1209
1210 /*******************************************************************************/
1211
1212
1213 // See if pos is a visible position
posVisible(FXint pos) const1214 FXbool FXText::posVisible(FXint pos) const {
1215 return visrows[0]<=pos && pos<=visrows[nvisrows];
1216 }
1217
1218
1219 // See if position is in the selection, and the selection is non-empty
isPosSelected(FXint pos) const1220 FXbool FXText::isPosSelected(FXint pos) const {
1221 return selstartpos<selendpos && selstartpos<=pos && pos<=selendpos;
1222 }
1223
1224
1225 // Find line number from visible pos
posToLine(FXint pos,FXint ln) const1226 FXint FXText::posToLine(FXint pos,FXint ln) const {
1227 FXASSERT(0<=ln && ln<nvisrows);
1228 FXASSERT(visrows[ln]<=pos && pos<=visrows[nvisrows]);
1229 while(ln<nvisrows-1 && visrows[ln+1]<=pos && visrows[ln]<visrows[ln+1]) ln++;
1230 FXASSERT(0<=ln && ln<nvisrows);
1231 FXASSERT(visrows[ln]<=pos && pos<=visrows[ln+1]);
1232 return ln;
1233 }
1234
1235
1236 // Localize position at x,y
getPosAt(FXint x,FXint y) const1237 FXint FXText::getPosAt(FXint x,FXint y) const {
1238 register FXint row,ls,le,cx,cw,ch;
1239 y=y-pos_y-margintop;
1240 row=y/font->getFontHeight();
1241 if(row<0) return 0; // Before first row
1242 if(row>=nrows) return length; // Below last row
1243 if(row<toprow){ // Above visible area
1244 ls=prevRow(toppos,toprow-row);
1245 le=nextRow(ls,1);
1246 }
1247 else if(row>=toprow+nvisrows){ // Below visible area
1248 ls=nextRow(toppos,row-toprow);
1249 le=nextRow(ls,1);
1250 }
1251 else{ // Inside visible area
1252 ls=visrows[row-toprow];
1253 le=visrows[row-toprow+1];
1254 }
1255 x=x-pos_x-marginleft-barwidth; // Before begin of line
1256 if(x<0) return ls;
1257 FXASSERT(0<=ls);
1258 FXASSERT(ls<=le);
1259 FXASSERT(le<=length);
1260 if(ls<le && (((ch=getByte(le-1))=='\n') || (le<length && Ascii::isSpace(ch)))) le--;
1261 cx=0;
1262 while(ls<le){
1263 ch=getChar(ls);
1264 cw=charWidth(ch,cx);
1265 if(x<=(cx+(cw>>1))) return ls;
1266 cx+=cw;
1267 ls+=getCharLen(ls);
1268 }
1269 return le;
1270 }
1271
1272
1273 // Determine Y from position pos
getYOfPos(FXint pos) const1274 FXint FXText::getYOfPos(FXint pos) const {
1275 register FXint h=font->getFontHeight();
1276 register FXint n,y;
1277 if(pos>length) pos=length;
1278 if(pos<0) pos=0;
1279
1280 // Above visible part of buffer
1281 if(pos<visrows[0]){
1282 n=countRows(rowStart(pos),visrows[0]);
1283 y=(toprow-n)*h;
1284 FXTRACE((150,"getYOfPos(%d < visrows[0]=%d) = %d\n",pos,visrows[0],margintop+y));
1285 }
1286
1287 // Below visible part of buffer
1288 else if(pos>=visrows[nvisrows]){
1289 n=countRows(visrows[nvisrows-1],pos);
1290 y=(toprow+nvisrows-1+n)*h;
1291 FXTRACE((150,"getYOfPos(%d > visrows[%d]=%d) = %d\n",pos,nvisrows,visrows[nvisrows],margintop+y));
1292 }
1293
1294 // In visible part of buffer
1295 else{
1296 n=posToLine(pos,0);
1297 y=(toprow+n)*h;
1298 FXTRACE((150,"getYOfPos(visrows[0]=%d <= %d <= visrows[%d]=%d) = %d\n",visrows[0],pos,nvisrows,visrows[nvisrows],margintop+y));
1299 }
1300 return margintop+y;
1301 }
1302
1303
1304 // Calculate X position of pos
getXOfPos(FXint pos) const1305 FXint FXText::getXOfPos(FXint pos) const {
1306 register FXint base=rowStart(pos);
1307 return marginleft+barwidth+lineWidth(base,pos-base);
1308 }
1309
1310
1311 // Force position to become fully visible
makePositionVisible(FXint pos)1312 void FXText::makePositionVisible(FXint pos){
1313 register FXint x,y,nx,ny;
1314
1315 // Valid position
1316 pos=validPos(pos);
1317
1318 // Get coordinates of position
1319 x=getXOfPos(pos);
1320 y=getYOfPos(pos);
1321
1322 // Old scroll position
1323 ny=pos_y;
1324 nx=pos_x;
1325
1326 // Check vertical visibility
1327 if(pos_y+y<margintop){
1328 ny=margintop-y;
1329 nx=0;
1330 }
1331 else if(pos_y+y+font->getFontHeight()>viewport_h-marginbottom){
1332 ny=viewport_h-font->getFontHeight()-marginbottom-y;
1333 nx=0;
1334 }
1335
1336 // Check Horizontal visibility
1337 if(pos_x+x<marginleft+barwidth){
1338 nx=marginleft+barwidth-x;
1339 }
1340 else if(pos_x+x>viewport_w-marginright){
1341 nx=viewport_w-marginright-x;
1342 }
1343
1344 // If needed, scroll
1345 if(nx!=pos_x || ny!=pos_y){
1346 setPosition(nx,ny);
1347 }
1348 }
1349
1350
1351 // Return TRUE if position is visible
isPosVisible(FXint pos) const1352 FXbool FXText::isPosVisible(FXint pos) const {
1353 if(visrows[0]<=pos && pos<=visrows[nvisrows]){
1354 register FXint h=font->getFontHeight();
1355 register FXint y=pos_y+margintop+(toprow+posToLine(pos,0))*h;
1356 return margintop<=y && y+h<=viewport_h-marginbottom;
1357 }
1358 return FALSE;
1359 }
1360
1361
1362 // Make line containing pos the top visible line
setTopLine(FXint pos)1363 void FXText::setTopLine(FXint pos){
1364 setPosition(pos_x,margintop-getYOfPos(pos));
1365 }
1366
1367
1368 // Make line containing pos the bottom visible line
setBottomLine(FXint pos)1369 void FXText::setBottomLine(FXint pos){
1370 setPosition(pos_x,viewport_h-font->getFontHeight()-marginbottom-getYOfPos(pos));
1371 }
1372
1373
1374 // Center line of pos in the middle of the screen
setCenterLine(FXint pos)1375 void FXText::setCenterLine(FXint pos){
1376 setPosition(pos_x,viewport_h/2+font->getFontHeight()/2-getYOfPos(pos));
1377 }
1378
1379
1380 // Get top line
getTopLine() const1381 FXint FXText::getTopLine() const {
1382 return visrows[0];
1383 }
1384
1385
1386 // Get bottom line
getBottomLine() const1387 FXint FXText::getBottomLine() const {
1388 return visrows[nvisrows-1];
1389 }
1390
1391
1392 // Move content
moveContents(FXint x,FXint y)1393 void FXText::moveContents(FXint x,FXint y){
1394 register FXint delta,i,dx,dy;
1395
1396 // Erase fragments of cursor overhanging margins
1397 eraseCursorOverhang();
1398
1399 // Number of lines scrolled
1400 delta=-y/font->getFontHeight() - toprow;
1401
1402 // Scrolled up one or more lines
1403 if(delta<0){
1404 if(toprow+delta<=0){
1405 toppos=0;
1406 toprow=0;
1407 }
1408 else{
1409 toppos=prevRow(toppos,-delta);
1410 toprow=toprow+delta;
1411 }
1412 if(-delta<nvisrows){
1413 for(i=nvisrows; i>=-delta; i--) visrows[i]=visrows[delta+i];
1414 calcVisRows(0,-delta);
1415 }
1416 else{
1417 calcVisRows(0,nvisrows);
1418 }
1419 }
1420
1421 // Scrolled down one or more lines
1422 else if(delta>0){
1423 if(toprow+delta>=nrows-1){
1424 toppos=rowStart(length);
1425 toprow=nrows-1;
1426 }
1427 else{
1428 toppos=nextRow(toppos,delta);
1429 toprow=toprow+delta;
1430 }
1431 if(delta<nvisrows){
1432 for(i=0; i<=nvisrows-delta; i++) visrows[i]=visrows[delta+i];
1433 calcVisRows(nvisrows-delta,nvisrows);
1434 }
1435 else{
1436 calcVisRows(0,nvisrows);
1437 }
1438 }
1439
1440 // This is now the new keep position
1441 keeppos=toppos; // FIXME but bottom line for log mode
1442
1443 // Hopefully, all is still in range
1444 FXASSERT(0<=toprow && toprow<=nrows-1);
1445 FXASSERT(0<=toppos && toppos<=length);
1446
1447 // Scroll the contents
1448 dx=x-pos_x;
1449 dy=y-pos_y;
1450 pos_x=x;
1451 pos_y=y;
1452
1453 // Scroll stuff in the bar only vertically
1454 scroll(0,0,barwidth,viewport_h,0,dy);
1455
1456 // Scroll the text
1457 scroll(marginleft+barwidth,margintop,viewport_w-marginleft-barwidth-marginright,viewport_h-margintop-marginbottom,dx,dy);
1458 }
1459
1460
1461 /*******************************************************************************/
1462
1463
1464 // Recalculate line starts
calcVisRows(FXint startline,FXint endline)1465 void FXText::calcVisRows(FXint startline,FXint endline){
1466 register FXint line,pos;
1467 FXASSERT(nvisrows>0);
1468 if(startline<0)
1469 startline=0;
1470 else if(startline>nvisrows)
1471 startline=nvisrows;
1472 if(endline<0)
1473 endline=0;
1474 else if(endline>nvisrows)
1475 endline=nvisrows;
1476 if(startline<=endline){
1477 if(startline==0){
1478 FXASSERT(0<=toppos && toppos<=length);
1479 visrows[0]=toppos;
1480 startline=1;
1481 }
1482 pos=visrows[startline-1];
1483 line=startline;
1484 if(options&TEXT_WORDWRAP){
1485 while(line<=endline && pos<length){
1486 pos=wrap(pos);
1487 FXASSERT(0<=pos && pos<=length);
1488 visrows[line++]=pos;
1489 }
1490 }
1491 else{
1492 while(line<=endline && pos<length){
1493 pos=nextLine(pos);
1494 FXASSERT(0<=pos && pos<=length);
1495 visrows[line++]=pos;
1496 }
1497 }
1498 while(line<=endline){
1499 visrows[line++]=length;
1500 }
1501 }
1502 }
1503
1504
1505 // FIXME
1506 // when TEXT_AUTOSCROLL is on, we need to anchor text buffer changes to the
1507 // last line of the buffer [if scrolled to the end].
1508 // This will affect mutation() and perhaps replace() functions below...
1509
1510 // There has been a mutation in the buffer
mutation(FXint pos,FXint ncins,FXint ncdel,FXint nrins,FXint nrdel)1511 void FXText::mutation(FXint pos,FXint ncins,FXint ncdel,FXint nrins,FXint nrdel){
1512 register FXint ncdelta=ncins-ncdel;
1513 register FXint nrdelta=nrins-nrdel;
1514 register FXint line,i,x,y;
1515
1516 FXTRACE((150,"BEFORE: pos=%d ncins=%d ncdel=%d nrins=%d nrdel=%d toppos=%d toprow=%d nrows=%d nvisrows=%d\n",pos,ncins,ncdel,nrins,nrdel,toppos,toprow,nrows,nvisrows));
1517
1518 // All of the change is below the last visible line
1519 if(visrows[nvisrows]<pos){
1520 FXTRACE((150,"change below visible\n"));
1521 nrows+=nrdelta;
1522 }
1523
1524 // All change above first visible line
1525 else if(pos+ncdel<=visrows[0]){
1526 FXTRACE((150,"change above visible\n"));
1527 nrows+=nrdelta;
1528 toprow+=nrdelta;
1529 toppos+=ncdelta;
1530 keeppos=toppos;
1531 for(i=0; i<=nvisrows; i++) visrows[i]+=ncdelta;
1532 pos_y-=nrdelta*font->getFontHeight();
1533 FXASSERT(0<=toppos && toppos<=length);
1534 if(nrdelta) update(0,0,barwidth,height);
1535 }
1536
1537 // Top visible part unchanged
1538 else if(visrows[0]<=pos){
1539 line=posToLine(pos,0);
1540 FXTRACE((150,"change below visible line %d\n",line));
1541
1542 // More lines means paint the bottom half
1543 if(nrdelta>0){
1544 FXTRACE((150,"inserted %d rows\n",nrdelta));
1545 nrows+=nrdelta;
1546 for(i=nvisrows; i>line+nrdelta; i--) visrows[i]=visrows[i-nrdelta]+ncdelta;
1547 calcVisRows(line+1,line+nrins);
1548 FXASSERT(0<=toppos && toppos<=length);
1549 y=pos_y+margintop+(toprow+line)*font->getFontHeight();
1550 update(barwidth,y,width-barwidth,height-y);
1551 }
1552
1553 // Less lines means paint bottom half also
1554 else if(nrdelta<0){
1555 FXTRACE((150,"deleted %d rows\n",-nrdelta));
1556 nrows+=nrdelta;
1557 for(i=line+1; i<=nvisrows+nrdelta; i++) visrows[i]=visrows[i-nrdelta]+ncdelta;
1558 calcVisRows(nvisrows+nrdelta,nvisrows);
1559 calcVisRows(line+1,line+nrins);
1560 FXASSERT(0<=toppos && toppos<=length);
1561 y=pos_y+margintop+(toprow+line)*font->getFontHeight();
1562 update(barwidth,y,width-barwidth,height-y);
1563 }
1564
1565 // Same lines means paint the changed area only
1566 else{
1567 FXTRACE((150,"same number of rows\n"));
1568 for(i=line+1; i<=nvisrows; i++) visrows[i]=visrows[i]+ncdelta;
1569 calcVisRows(line+1,line+nrins);
1570 FXASSERT(0<=toppos && toppos<=length);
1571 if(nrins==0){
1572 x=pos_x+marginleft+barwidth+lineWidth(visrows[line],pos-visrows[line]);
1573 y=pos_y+margintop+(toprow+line)*font->getFontHeight();
1574 update(x,y,width-x,font->getFontHeight());
1575 FXTRACE((150,"update(%d,%d,%d,%d)\n",x,y,width-x,font->getFontHeight()));
1576 }
1577 else{
1578 y=pos_y+margintop+(toprow+line)*font->getFontHeight();
1579 update(barwidth,y,width-barwidth,nrins*font->getFontHeight());
1580 FXTRACE((150,"update(%d,%d,%d,%d)\n",0,y,width,nrins*font->getFontHeight()));
1581 }
1582 }
1583 }
1584
1585 // Bottom visible part unchanged
1586 else if(pos+ncdel<visrows[nvisrows-1]){
1587 nrows+=nrdelta;
1588 line=1+posToLine(pos+ncdel,0);
1589 FXASSERT(0<=line && line<nvisrows);
1590 FXASSERT(pos+ncdel<=visrows[line]);
1591 FXTRACE((150,"change above visible line %d\n",line));
1592
1593 // Too few lines left to display
1594 if(toprow+nrdelta<=line){
1595 FXTRACE((150,"reset to top\n"));
1596 toprow=0;
1597 toppos=0;
1598 keeppos=0;
1599 pos_y=0;
1600 calcVisRows(0,nvisrows);
1601 FXASSERT(0<=toppos && toppos<=length);
1602 update();
1603 }
1604
1605 // Redisplay only the top
1606 else{
1607 FXTRACE((150,"redraw top %d lines\n",line));
1608 toprow+=nrdelta;
1609 toppos=prevRow(visrows[line]+ncdelta,line);
1610 keeppos=toppos;
1611 pos_y-=nrdelta*font->getFontHeight();
1612 calcVisRows(0,nvisrows);
1613 FXASSERT(0<=toppos && toppos<=length);
1614 update(barwidth,0,width-barwidth,pos_y+margintop+(toprow+line)*font->getFontHeight());
1615 if(nrdelta) update(0,0,barwidth,height);
1616 }
1617 }
1618
1619 // All visible text changed
1620 else{
1621 FXTRACE((150,"change all visible lines\n"));
1622 nrows+=nrdelta;
1623
1624 // Reset to top because too few lines left
1625 if(toprow>=nrows){
1626 FXTRACE((150,"reset to top\n"));
1627 toprow=0;
1628 toppos=0;
1629 keeppos=0;
1630 FXASSERT(0<=toppos && toppos<=length);
1631 pos_y=0;
1632 }
1633
1634 // Maintain same row as before
1635 else{
1636 FXTRACE((150,"set to same row %d\n",toprow));
1637 toppos=nextRow(0,toprow);
1638 keeppos=toppos;
1639 FXASSERT(0<=toppos && toppos<=length);
1640 }
1641 calcVisRows(0,nvisrows);
1642 update();
1643 }
1644
1645 FXTRACE((150,"AFTER : pos=%d ncins=%d ncdel=%d nrins=%d nrdel=%d toppos=%d toprow=%d nrows=%d\n",pos,ncins,ncdel,nrins,nrdel,toppos,toprow,nrows));
1646 }
1647
1648
1649
1650 // Replace m characters at pos by n characters
replace(FXint pos,FXint m,const FXchar * text,FXint n,FXint style)1651 void FXText::replace(FXint pos,FXint m,const FXchar *text,FXint n,FXint style){
1652 register FXint nrdel,nrins,ncdel,ncins,wbeg,wend,del;
1653 FXint wdel,hdel,wins,hins;
1654 drawCursor(0); // FIXME can we do without this?
1655
1656 FXTRACE((150,"pos=%d mdel=%d nins=%d\n",pos,m,n));
1657
1658 // Delta in characters
1659 del=n-m;
1660
1661 // Bracket potentially affected character range for wrapping purposes
1662 wbeg=changeBeg(pos);
1663 wend=changeEnd(pos+m);
1664
1665 // Measure stuff prior to change
1666 nrdel=measureText(wbeg,wend,wdel,hdel);
1667 ncdel=wend-wbeg;
1668
1669 FXTRACE((150,"wbeg=%d wend=%d nrdel=%d ncdel=%d length=%d wdel=%d hdel=%d\n",wbeg,wend,nrdel,ncdel,length,wdel,hdel));
1670
1671 // Modify the buffer
1672 sizegap(del);
1673 movegap(pos);
1674 memcpy(&buffer[pos],text,n);
1675 if(sbuffer){memset(&sbuffer[pos],style,n);}
1676 gapstart+=n;
1677 gapend+=m;
1678 length+=del;
1679
1680 // Measure stuff after change
1681 nrins=measureText(wbeg,wend+n-m,wins,hins);
1682 ncins=wend+n-m-wbeg;
1683
1684 FXTRACE((150,"wbeg=%d wend+n-m=%d nrins=%d ncins=%d length=%d wins=%d hins=%d\n",wbeg,wend+n-m,nrins,ncins,length,wins,hins));
1685
1686 // Update stuff
1687 mutation(wbeg,ncins,ncdel,nrins,nrdel);
1688
1689 // Fix text metrics
1690 textHeight=textHeight+hins-hdel;
1691 textWidth=FXMAX(textWidth,wins);
1692
1693 // Fix selection range
1694 FXASSERT(selstartpos<=selendpos);
1695 if(pos+m<=selstartpos){
1696 selstartpos+=del;
1697 selendpos+=del;
1698 }
1699 else if(pos<selendpos){
1700 if(selendpos<=pos+m) selendpos=pos+n; else selendpos+=del;
1701 if(pos<=selstartpos) selstartpos=pos+n;
1702 }
1703
1704 // Fix highlight range
1705 FXASSERT(hilitestartpos<=hiliteendpos);
1706 if(pos+m<=hilitestartpos){
1707 hilitestartpos+=del;
1708 hiliteendpos+=del;
1709 }
1710 else if(pos<hiliteendpos){
1711 if(hiliteendpos<=pos+m) hiliteendpos=pos+n; else hiliteendpos+=del;
1712 if(pos<=hilitestartpos) hilitestartpos=pos+n;
1713 }
1714
1715 // Fix anchor position
1716 if(pos+m<=anchorpos) anchorpos+=del;
1717 else if(pos<=anchorpos) anchorpos=pos+n;
1718
1719 // Cursor is beyond changed area, so simple update
1720 if(wend<=cursorpos){
1721 cursorpos+=del;
1722 cursorstart+=del;
1723 cursorend+=del;
1724 cursorrow+=nrins-nrdel;
1725 }
1726
1727 // Cursor inside changed area, recompute cursor data
1728 else if(wbeg<=cursorpos){
1729 if(pos+m<=cursorpos) cursorpos+=del; // Beyond changed text
1730 else if(pos<=cursorpos) cursorpos=pos+n; // Inside changed text
1731 cursorstart=rowStart(cursorpos);
1732 cursorend=nextRow(cursorstart);
1733 cursorcol=indentFromPos(cursorstart,cursorpos);
1734 if(cursorstart<toppos){
1735 cursorrow=toprow-countRows(cursorstart,toppos);
1736 }
1737 else{
1738 cursorrow=toprow+countRows(toppos,cursorstart);
1739 }
1740 }
1741
1742 // Reconcile scrollbars
1743 FXScrollArea::layout(); // FIXME:- scrollbars, but no layout
1744
1745 // Forget preferred column
1746 prefcol=-1;
1747 }
1748
1749
1750 // Replace m characters at pos by n characters
replaceStyledText(FXint pos,FXint m,const FXchar * text,FXint n,FXint style,FXbool notify)1751 void FXText::replaceStyledText(FXint pos,FXint m,const FXchar *text,FXint n,FXint style,FXbool notify){
1752 FXTextChange textchange;
1753 if(n<0 || m<0 || pos<0 || length<pos+m){ fxerror("%s::replaceStyledText: bad argument.\n",getClassName()); }
1754 FXTRACE((130,"replaceStyledText(%d,%d,text,%d)\n",pos,m,n));
1755 textchange.pos=pos;
1756 textchange.ndel=m;
1757 textchange.nins=n;
1758 textchange.ins=(FXchar*)text;
1759 FXMALLOC(&textchange.del,FXchar,m);
1760 extractText(textchange.del,pos,m);
1761 replace(pos,m,text,n,style);
1762 if(notify && target){
1763 target->tryHandle(this,FXSEL(SEL_REPLACED,message),(void*)&textchange);
1764 target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)cursorpos);
1765 }
1766 FXFREE(&textchange.del);
1767 }
1768
1769
1770 // Replace m characters at pos by n characters
replaceStyledText(FXint pos,FXint m,const FXString & text,FXint style,FXbool notify)1771 void FXText::replaceStyledText(FXint pos,FXint m,const FXString& text,FXint style,FXbool notify){
1772 replaceStyledText(pos,m,text.text(),text.length(),style,notify);
1773 }
1774
1775
1776 // Replace text by other text
replaceText(FXint pos,FXint m,const FXchar * text,FXint n,FXbool notify)1777 void FXText::replaceText(FXint pos,FXint m,const FXchar *text,FXint n,FXbool notify){
1778 replaceStyledText(pos,m,text,n,0,notify);
1779 }
1780
1781
1782 // Replace text by other text
replaceText(FXint pos,FXint m,const FXString & text,FXbool notify)1783 void FXText::replaceText(FXint pos,FXint m,const FXString& text,FXbool notify){
1784 replaceText(pos,m,text.text(),text.length(),notify);
1785 }
1786
1787
1788 // Add text at the end
appendStyledText(const FXchar * text,FXint n,FXint style,FXbool notify)1789 void FXText::appendStyledText(const FXchar *text,FXint n,FXint style,FXbool notify){
1790 FXTextChange textchange;
1791 if(n<0){ fxerror("%s::appendStyledText: bad argument.\n",getClassName()); }
1792 FXTRACE((130,"appendStyledText(text,%d)\n",n));
1793 textchange.pos=length;
1794 textchange.ndel=0;
1795 textchange.nins=n;
1796 textchange.ins=(FXchar*)text;
1797 textchange.del=(FXchar*)"";
1798 replace(length,0,text,n,style);
1799 if(notify && target){
1800 target->tryHandle(this,FXSEL(SEL_INSERTED,message),(void*)&textchange);
1801 target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)cursorpos);
1802 }
1803 }
1804
1805
1806 // Add text at the end
appendStyledText(const FXString & text,FXint style,FXbool notify)1807 void FXText::appendStyledText(const FXString& text,FXint style,FXbool notify){
1808 appendStyledText(text.text(),text.length(),style,notify);
1809 }
1810
1811
1812 // Add text at the end
appendText(const FXchar * text,FXint n,FXbool notify)1813 void FXText::appendText(const FXchar *text,FXint n,FXbool notify){
1814 appendStyledText(text,n,0,notify);
1815 }
1816
1817
1818 // Add text at the end
appendText(const FXString & text,FXbool notify)1819 void FXText::appendText(const FXString& text,FXbool notify){
1820 appendText(text.text(),text.length(),notify);
1821 }
1822
1823
1824 // Insert some text at pos
insertStyledText(FXint pos,const FXchar * text,FXint n,FXint style,FXbool notify)1825 void FXText::insertStyledText(FXint pos,const FXchar *text,FXint n,FXint style,FXbool notify){
1826 FXTextChange textchange;
1827 if(n<0 || pos<0 || length<pos){ fxerror("%s::insertStyledText: bad argument.\n",getClassName()); }
1828 FXTRACE((130,"insertStyledText(%d,text,%d)\n",pos,n));
1829 textchange.pos=pos;
1830 textchange.ndel=0;
1831 textchange.nins=n;
1832 textchange.ins=(FXchar*)text;
1833 textchange.del=(FXchar*)"";
1834 replace(pos,0,text,n,style);
1835 if(notify && target){
1836 target->tryHandle(this,FXSEL(SEL_INSERTED,message),(void*)&textchange);
1837 target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)cursorpos);
1838 }
1839 }
1840
1841
1842 // Insert some text at pos
insertStyledText(FXint pos,const FXString & text,FXint style,FXbool notify)1843 void FXText::insertStyledText(FXint pos,const FXString& text,FXint style,FXbool notify){
1844 insertStyledText(pos,text.text(),text.length(),style,notify);
1845 }
1846
1847
1848 // Insert some text at pos
insertText(FXint pos,const FXchar * text,FXint n,FXbool notify)1849 void FXText::insertText(FXint pos,const FXchar *text,FXint n,FXbool notify){
1850 insertStyledText(pos,text,n,0,notify);
1851 }
1852
1853
1854 // Insert some text at pos
insertText(FXint pos,const FXString & text,FXbool notify)1855 void FXText::insertText(FXint pos,const FXString& text,FXbool notify){
1856 insertText(pos,text.text(),text.length(),notify);
1857 }
1858
1859
1860 // Remove some text at pos
removeText(FXint pos,FXint n,FXbool notify)1861 void FXText::removeText(FXint pos,FXint n,FXbool notify){
1862 FXTextChange textchange;
1863 if(n<0 || pos<0 || length<pos+n){ fxerror("%s::removeText: bad argument.\n",getClassName()); }
1864 FXTRACE((130,"removeText(%d,%d)\n",pos,n));
1865 textchange.pos=pos;
1866 textchange.ndel=n;
1867 textchange.nins=0;
1868 textchange.ins=(FXchar*)"";
1869 FXMALLOC(&textchange.del,FXchar,n);
1870 extractText(textchange.del,pos,n);
1871 replace(pos,n,NULL,0,0);
1872 if(notify && target){
1873 target->tryHandle(this,FXSEL(SEL_DELETED,message),(void*)&textchange);
1874 target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)cursorpos);
1875 }
1876 FXFREE(&textchange.del);
1877 }
1878
1879
1880 // Grab range of text
extractText(FXchar * text,FXint pos,FXint n) const1881 void FXText::extractText(FXchar *text,FXint pos,FXint n) const {
1882 if(n<0 || pos<0 || length<pos+n){ fxerror("%s::extractText: bad argument.\n",getClassName()); }
1883 FXASSERT(0<=n && 0<=pos && pos+n<=length);
1884 if(pos+n<=gapstart){
1885 memcpy(text,&buffer[pos],n);
1886 }
1887 else if(pos>=gapstart){
1888 memcpy(text,&buffer[pos-gapstart+gapend],n);
1889 }
1890 else{
1891 memcpy(text,&buffer[pos],gapstart-pos);
1892 memcpy(&text[gapstart-pos],&buffer[gapend],pos+n-gapstart);
1893 }
1894 }
1895
1896
1897 // Grab range of text
extractText(FXString & text,FXint pos,FXint n) const1898 void FXText::extractText(FXString& text,FXint pos,FXint n) const {
1899 if(n<0 || pos<0 || length<pos+n){ fxerror("%s::extractText: bad argument.\n",getClassName()); }
1900 FXASSERT(0<=n && 0<=pos && pos+n<=length);
1901 text.length(n);
1902 if(pos+n<=gapstart){
1903 text.replace(0,n,&buffer[pos],n);
1904 }
1905 else if(pos>=gapstart){
1906 text.replace(0,n,&buffer[pos-gapstart+gapend],n);
1907 }
1908 else{
1909 text.replace(0,gapstart-pos,&buffer[pos],gapstart-pos);
1910 text.replace(gapstart-pos,pos+n-gapstart,&buffer[gapend],pos+n-gapstart);
1911 }
1912 }
1913
1914
1915 // Grab range of style
extractStyle(FXchar * style,FXint pos,FXint n) const1916 void FXText::extractStyle(FXchar *style,FXint pos,FXint n) const {
1917 if(n<0 || pos<0 || length<pos+n){ fxerror("%s::extractStyle: bad argument.\n",getClassName()); }
1918 FXASSERT(0<=n && 0<=pos && pos+n<=length);
1919 if(sbuffer){
1920 if(pos+n<=gapstart){
1921 memcpy(style,&sbuffer[pos],n);
1922 }
1923 else if(pos>=gapstart){
1924 memcpy(style,&sbuffer[pos-gapstart+gapend],n);
1925 }
1926 else{
1927 memcpy(style,&sbuffer[pos],gapstart-pos);
1928 memcpy(&style[gapstart-pos],&sbuffer[gapend],pos+n-gapstart);
1929 }
1930 }
1931 }
1932
1933
1934 // Grab range of style
extractStyle(FXString & style,FXint pos,FXint n) const1935 void FXText::extractStyle(FXString& style,FXint pos,FXint n) const {
1936 if(n<0 || pos<0 || length<pos+n){ fxerror("%s::extractStyle: bad argument.\n",getClassName()); }
1937 FXASSERT(0<=n && 0<=pos && pos+n<=length);
1938 style.assign('\0',n);
1939 if(sbuffer){
1940 if(pos+n<=gapstart){
1941 style.replace(0,n,&sbuffer[pos],n);
1942 }
1943 else if(pos>=gapstart){
1944 style.replace(0,n,&sbuffer[pos-gapstart+gapend],n);
1945 }
1946 else{
1947 style.replace(0,gapstart-pos,&sbuffer[pos],gapstart-pos);
1948 style.replace(gapstart-pos,pos+n-gapstart,&sbuffer[gapend],pos+n-gapstart);
1949 }
1950 }
1951 }
1952
1953
1954 // Change style of text range
changeStyle(FXint pos,FXint n,FXint style)1955 void FXText::changeStyle(FXint pos,FXint n,FXint style){
1956 if(n<0 || pos<0 || length<pos+n){ fxerror("%s::changeStyle: bad argument.\n",getClassName()); }
1957 if(sbuffer){
1958 if(pos+n<=gapstart){
1959 memset(&sbuffer[pos],style,n);
1960 }
1961 else if(pos>=gapstart){
1962 memset(&sbuffer[pos-gapstart+gapend],style,n);
1963 }
1964 else{
1965 memset(&sbuffer[pos],style,gapstart-pos);
1966 memset(&sbuffer[gapend],style,pos+n-gapstart);
1967 }
1968 updateRange(pos,pos+n);
1969 }
1970 }
1971
1972
1973 // Change style of text range from style-array
changeStyle(FXint pos,const FXchar * style,FXint n)1974 void FXText::changeStyle(FXint pos,const FXchar* style,FXint n){
1975 if(n<0 || pos<0 || length<pos+n){ fxerror("%s::changeStyle: bad argument.\n",getClassName()); }
1976 if(sbuffer && style){
1977 if(pos+n<=gapstart){
1978 memcpy(&sbuffer[pos],style,n);
1979 }
1980 else if(pos>=gapstart){
1981 memcpy(&sbuffer[pos-gapstart+gapend],style,n);
1982 }
1983 else{
1984 memcpy(&sbuffer[pos],style,gapstart-pos);
1985 memcpy(&sbuffer[gapend],&style[gapstart-pos],pos+n-gapstart);
1986 }
1987 updateRange(pos,pos+n);
1988 }
1989 }
1990
1991
1992 // Change style of text range from style-array
changeStyle(FXint pos,const FXString & style)1993 void FXText::changeStyle(FXint pos,const FXString& style){
1994 changeStyle(pos,style.text(),style.length());
1995 }
1996
1997
1998 // Change the text in the buffer to new text
setStyledText(const FXchar * text,FXint n,FXint style,FXbool notify)1999 void FXText::setStyledText(const FXchar* text,FXint n,FXint style,FXbool notify){
2000 FXTextChange textchange;
2001 if(n<0){ fxerror("%s::setStyledText: bad argument.\n",getClassName()); }
2002 if(!FXRESIZE(&buffer,FXchar,n+MINSIZE)){
2003 fxerror("%s::setStyledText: out of memory.\n",getClassName());
2004 }
2005 memcpy(buffer,text,n);
2006 if(sbuffer){
2007 if(!FXRESIZE(&sbuffer,FXchar,n+MINSIZE)){
2008 fxerror("%s::setStyledText: out of memory.\n",getClassName());
2009 }
2010 memset(sbuffer,style,n);
2011 }
2012 gapstart=n;
2013 gapend=gapstart+MINSIZE;
2014 length=n;
2015 toppos=0;
2016 toprow=0;
2017 keeppos=0;
2018 selstartpos=0;
2019 selendpos=0;
2020 hilitestartpos=0;
2021 hiliteendpos=0;
2022 anchorpos=0;
2023 cursorpos=0;
2024 cursorstart=0;
2025 cursorend=0;
2026 cursorrow=0;
2027 cursorcol=0;
2028 prefcol=-1;
2029 pos_x=0;
2030 pos_y=0;
2031 textchange.pos=0;
2032 textchange.ndel=0;
2033 textchange.nins=n;
2034 textchange.ins=(FXchar*)text;
2035 textchange.del=(FXchar*)"";
2036 if(notify && target){
2037 target->tryHandle(this,FXSEL(SEL_INSERTED,message),(void*)&textchange);
2038 target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)cursorpos);
2039 }
2040 recalc();
2041 layout();
2042 update();
2043 }
2044
2045
2046 // Change all of the text
setStyledText(const FXString & text,FXint style,FXbool notify)2047 void FXText::setStyledText(const FXString& text,FXint style,FXbool notify){
2048 setStyledText(text.text(),text.length(),style,notify);
2049 }
2050
2051
2052 // Change the text in the buffer to new text
setText(const FXchar * text,FXint n,FXbool notify)2053 void FXText::setText(const FXchar* text,FXint n,FXbool notify){
2054 setStyledText(text,n,0,notify);
2055 }
2056
2057
2058 // Change all of the text
setText(const FXString & text,FXbool notify)2059 void FXText::setText(const FXString& text,FXbool notify){
2060 setText(text.text(),text.length(),notify);
2061 }
2062
2063
2064 // Retrieve text into buffer
getText(FXchar * text,FXint n) const2065 void FXText::getText(FXchar* text,FXint n) const {
2066 extractText(text,0,n);
2067 }
2068
2069
2070 // Retrieve text into buffer
getText(FXString & text) const2071 void FXText::getText(FXString& text) const {
2072 extractText(text,0,getLength());
2073 }
2074
2075
2076 // We return a constant copy of the buffer
getText() const2077 FXString FXText::getText() const {
2078 FXString value;
2079 FXASSERT(0<=gapstart && gapstart<=length);
2080 value.append(buffer,gapstart);
2081 value.append(&buffer[gapend],length-gapstart);
2082 return value;
2083 }
2084
2085
2086 // Completely reflow the text, because font, wrapwidth, or all of the
2087 // text may have changed and everything needs to be recomputed
recompute()2088 void FXText::recompute(){
2089 FXint ww1,ww2,ww3,hh1,hh2,hh3,hh;
2090
2091 // Make it point somewhere sensible
2092 if(keeppos<0) keeppos=0;
2093 if(keeppos>length) keeppos=length;
2094
2095 // Make sure we're pointing to the start of a row again
2096 toppos=rowStart(keeppos); // FIXME in log mode, we may want to keep bottom line anchored [if visible]
2097
2098 // Font height
2099 hh=font->getFontHeight();
2100
2101 // Get start
2102 cursorstart=rowStart(cursorpos);
2103 cursorend=nextRow(cursorstart);
2104 cursorcol=indentFromPos(cursorstart,cursorpos);
2105
2106 // Avoid measuring huge chunks of text twice!
2107 if(cursorstart<toprow){
2108 cursorrow=measureText(0,cursorstart,ww1,hh1);
2109 toprow=cursorrow+measureText(cursorstart,toppos,ww2,hh2);
2110 nrows=toprow+measureText(toppos,length+1,ww3,hh3);
2111 }
2112 else{
2113 toprow=measureText(0,toppos,ww1,hh1);
2114 cursorrow=toprow+measureText(toppos,cursorstart,ww2,hh2);
2115 nrows=cursorrow+measureText(cursorstart,length+1,ww3,hh3);
2116 }
2117
2118 textWidth=FXMAX3(ww1,ww2,ww3);
2119 textHeight=hh1+hh2+hh3;
2120
2121 // Adjust position, keeping same fractional position
2122 pos_y=-toprow*hh-(-pos_y%hh);
2123
2124 // Number of visible lines has changed
2125 nvisrows=(height-margintop-marginbottom+hh+hh-1)/hh;
2126 if(nvisrows<1) nvisrows=1;
2127
2128 // Resize line start array
2129 FXRESIZE(&visrows,FXint,nvisrows+1);
2130
2131 // Recompute line starts
2132 calcVisRows(0,nvisrows);
2133
2134 FXTRACE((150,"recompute : toprow=%d toppos=%d nrows=%d nvisrows=%d textWidth=%d textHeight=%d length=%d cursorrow=%d cursorcol=%d\n",toprow,toppos,nrows,nvisrows,textWidth,textHeight,length,cursorrow,cursorcol));
2135
2136 // Done with that
2137 flags&=~FLAG_RECALC;
2138 }
2139
2140
2141 /*******************************************************************************/
2142
2143
2144 // Determine content width of scroll area
getContentWidth()2145 FXint FXText::getContentWidth(){
2146 if(flags&FLAG_RECALC) recompute();
2147 return marginleft+barwidth+marginright+textWidth;
2148 }
2149
2150
2151 // Determine content height of scroll area
getContentHeight()2152 FXint FXText::getContentHeight(){
2153 if(flags&FLAG_RECALC) recompute();
2154 return margintop+marginbottom+textHeight;
2155 }
2156
2157
2158 // Recalculate layout
layout()2159 void FXText::layout(){
2160 FXint fh=font->getFontHeight();
2161 FXint fw=font->getFontWidth();
2162 FXint ovv=nvisrows;
2163 FXint oww=wrapwidth;
2164
2165 // Compute new wrap width; needed to reflow text
2166 if(options&TEXT_FIXEDWRAP){
2167 wrapwidth=wrapcolumns*font->getTextWidth("x",1);
2168 }
2169 else{
2170 wrapwidth=width-marginleft-barwidth-marginright;
2171 if(!(options&VSCROLLER_NEVER)) wrapwidth-=vertical->getDefaultWidth();
2172 }
2173
2174 // Wrap width changed, so reflow; when using fixed pitch font,
2175 // we only reflow if the number of columns has changed.
2176 if((options&TEXT_WORDWRAP) && (wrapwidth!=oww)){
2177 if(!font->isFontMono() || (wrapwidth/fw!=oww/fw)) flags|=FLAG_RECALC;
2178 }
2179
2180 // Scrollbars adjusted
2181 FXScrollArea::layout();
2182
2183 // Number of visible lines may have changed
2184 nvisrows=(height-margintop-marginbottom+fh+fh-1)/fh;
2185 if(nvisrows<1) nvisrows=1;
2186
2187 // Number of visible lines changed
2188 if(nvisrows!=ovv){
2189
2190 // Resize line start array
2191 FXRESIZE(&visrows,FXint,nvisrows+1);
2192
2193 // Recompute line starts
2194 calcVisRows(0,nvisrows);
2195 }
2196
2197 // Set line size based on font
2198 vertical->setLine(fh);
2199 horizontal->setLine(fw);
2200
2201 // Force repaint
2202 update();
2203
2204 // Done
2205 flags&=~FLAG_DIRTY;
2206 }
2207
2208
2209 /*******************************************************************************/
2210
2211
2212 // Blink the cursor
onBlink(FXObject *,FXSelector,void *)2213 long FXText::onBlink(FXObject*,FXSelector,void*){
2214 drawCursor(flags^FLAG_CARET);
2215 getApp()->addTimeout(this,ID_BLINK,getApp()->getBlinkSpeed());
2216 return 0;
2217 }
2218
2219
2220 // Gained focus
onFocusIn(FXObject * sender,FXSelector sel,void * ptr)2221 long FXText::onFocusIn(FXObject* sender,FXSelector sel,void* ptr){
2222 FXScrollArea::onFocusIn(sender,sel,ptr);
2223 getApp()->addTimeout(this,ID_BLINK,getApp()->getBlinkSpeed());
2224 drawCursor(FLAG_CARET);
2225 return 1;
2226 }
2227
2228
2229 // Lost focus
onFocusOut(FXObject * sender,FXSelector sel,void * ptr)2230 long FXText::onFocusOut(FXObject* sender,FXSelector sel,void* ptr){
2231 FXScrollArea::onFocusOut(sender,sel,ptr);
2232 getApp()->removeTimeout(this,ID_BLINK);
2233 drawCursor(0);
2234 flags|=FLAG_UPDATE;
2235 return 1;
2236 }
2237
2238
2239 // We were asked about tip text
onQueryTip(FXObject * sender,FXSelector sel,void * ptr)2240 long FXText::onQueryTip(FXObject* sender,FXSelector sel,void* ptr){
2241 if(FXWindow::onQueryTip(sender,sel,ptr)) return 1;
2242 if((flags&FLAG_TIP) && !tip.empty()){
2243 sender->handle(this,FXSEL(SEL_COMMAND,ID_SETSTRINGVALUE),(void*)&tip);
2244 return 1;
2245 }
2246 return 0;
2247 }
2248
2249
2250 // We were asked about status text
onQueryHelp(FXObject * sender,FXSelector sel,void * ptr)2251 long FXText::onQueryHelp(FXObject* sender,FXSelector sel,void* ptr){
2252 if(FXWindow::onQueryHelp(sender,sel,ptr)) return 1;
2253 if((flags&FLAG_HELP) && !help.empty()){
2254 sender->handle(this,FXSEL(SEL_COMMAND,ID_SETSTRINGVALUE),(void*)&help);
2255 return 1;
2256 }
2257 return 0;
2258 }
2259
2260
2261 // Flash matching brace
onFlash(FXObject *,FXSelector,void *)2262 long FXText::onFlash(FXObject*,FXSelector,void*){
2263 killHighlight();
2264 return 0;
2265 }
2266
2267
2268 // Pressed left button
onLeftBtnPress(FXObject *,FXSelector,void * ptr)2269 long FXText::onLeftBtnPress(FXObject*,FXSelector,void* ptr){
2270 FXEvent* event=(FXEvent*)ptr;
2271 FXint pos;
2272 flags&=~FLAG_TIP;
2273 handle(this,FXSEL(SEL_FOCUS_SELF,0),ptr);
2274 if(isEnabled()){
2275 grab();
2276 if(target && target->tryHandle(this,FXSEL(SEL_LEFTBUTTONPRESS,message),ptr)) return 1;
2277 flags&=~FLAG_UPDATE;
2278
2279 // Select characters
2280 if(event->click_count==1){
2281 pos=getPosAt(event->win_x,event->win_y);
2282 FXTRACE((150,"getPosAt(%d,%d) = %d getYOfPos(%d) = %d getXOfPos(%d)=%d\n",event->win_x,event->win_y,pos,pos,getYOfPos(pos),pos,getXOfPos(pos)));
2283 setCursorPos(pos,TRUE);
2284 makePositionVisible(cursorpos);
2285 if(event->state&SHIFTMASK){
2286 extendSelection(cursorpos,SELECT_CHARS,TRUE);
2287 }
2288 else{
2289 killSelection(TRUE);
2290 setAnchorPos(cursorpos);
2291 flashMatching();
2292 }
2293 mode=MOUSE_CHARS;
2294 }
2295
2296 // Select words
2297 else if(event->click_count==2){
2298 setAnchorPos(cursorpos);
2299 extendSelection(cursorpos,SELECT_WORDS,TRUE);
2300 mode=MOUSE_WORDS;
2301 }
2302
2303 // Select lines
2304 else{
2305 setAnchorPos(cursorpos);
2306 extendSelection(cursorpos,SELECT_LINES,TRUE);
2307 mode=MOUSE_LINES;
2308 }
2309 return 1;
2310 }
2311 return 0;
2312 }
2313
2314
2315 // Released left button
onLeftBtnRelease(FXObject *,FXSelector,void * ptr)2316 long FXText::onLeftBtnRelease(FXObject*,FXSelector,void* ptr){
2317 if(isEnabled()){
2318 ungrab();
2319 mode=MOUSE_NONE;
2320 stopAutoScroll();
2321 if(target && target->tryHandle(this,FXSEL(SEL_LEFTBUTTONRELEASE,message),ptr)) return 1;
2322 return 1;
2323 }
2324 return 0;
2325 }
2326
2327
2328 // Pressed middle button
onMiddleBtnPress(FXObject *,FXSelector,void * ptr)2329 long FXText::onMiddleBtnPress(FXObject*,FXSelector,void* ptr){
2330 FXEvent* event=(FXEvent*)ptr;
2331 FXint pos;
2332 flags&=~FLAG_TIP;
2333 handle(this,FXSEL(SEL_FOCUS_SELF,0),ptr);
2334 if(isEnabled()){
2335 grab();
2336 if(target && target->tryHandle(this,FXSEL(SEL_MIDDLEBUTTONPRESS,message),ptr)) return 1;
2337 pos=getPosAt(event->win_x,event->win_y);
2338
2339 // Move over
2340 setCursorPos(pos,TRUE);
2341 makePositionVisible(cursorpos);
2342
2343 // Start text drag
2344 if(isPosSelected(cursorpos)){
2345 mode=MOUSE_TRYDRAG;
2346 }
2347 flags&=~FLAG_UPDATE;
2348 return 1;
2349 }
2350 return 0;
2351 }
2352
2353
2354 // Released middle button
onMiddleBtnRelease(FXObject *,FXSelector,void * ptr)2355 long FXText::onMiddleBtnRelease(FXObject*,FXSelector,void* ptr){
2356 FXuint md=mode;
2357 if(isEnabled()){
2358 ungrab();
2359 stopAutoScroll();
2360 mode=MOUSE_NONE;
2361 if(target && target->tryHandle(this,FXSEL(SEL_MIDDLEBUTTONRELEASE,message),ptr)) return 1;
2362
2363 // Drop text somewhere
2364 if(md==MOUSE_DRAG){
2365 handle(this,FXSEL(SEL_ENDDRAG,0),ptr);
2366 }
2367
2368 // Paste selection
2369 else{
2370 handle(this,FXSEL(SEL_COMMAND,ID_PASTE_MIDDLE),NULL);
2371 }
2372 return 1;
2373 }
2374 return 0;
2375 }
2376
2377
2378 // Pressed right button
onRightBtnPress(FXObject *,FXSelector,void * ptr)2379 long FXText::onRightBtnPress(FXObject*,FXSelector,void* ptr){
2380 FXEvent* event=(FXEvent*)ptr;
2381 flags&=~FLAG_TIP;
2382 handle(this,FXSEL(SEL_FOCUS_SELF,0),ptr);
2383 if(isEnabled()){
2384 grab();
2385 if(target && target->tryHandle(this,FXSEL(SEL_RIGHTBUTTONPRESS,message),ptr)) return 1;
2386 grabx=event->win_x-pos_x;
2387 graby=event->win_y-pos_y;
2388 mode=MOUSE_SCROLL;
2389 flags&=~FLAG_UPDATE;
2390 return 1;
2391 }
2392 return 0;
2393 }
2394
2395
2396 // Released right button
onRightBtnRelease(FXObject *,FXSelector,void * ptr)2397 long FXText::onRightBtnRelease(FXObject*,FXSelector,void* ptr){
2398 if(isEnabled()){
2399 ungrab();
2400 mode=MOUSE_NONE;
2401 if(target && target->tryHandle(this,FXSEL(SEL_RIGHTBUTTONRELEASE,message),ptr)) return 1;
2402 return 1;
2403 }
2404 return 0;
2405 }
2406
2407
2408 // The widget lost the grab for some reason
onUngrabbed(FXObject * sender,FXSelector sel,void * ptr)2409 long FXText::onUngrabbed(FXObject* sender,FXSelector sel,void* ptr){
2410 FXScrollArea::onUngrabbed(sender,sel,ptr);
2411 mode=MOUSE_NONE;
2412 flags|=FLAG_UPDATE;
2413 stopAutoScroll();
2414 return 1;
2415 }
2416
2417
2418 // Autoscroll timer fired
onAutoScroll(FXObject * sender,FXSelector sel,void * ptr)2419 long FXText::onAutoScroll(FXObject* sender,FXSelector sel,void* ptr){
2420 FXEvent* event=(FXEvent*)ptr;
2421 FXint pos;
2422 FXScrollArea::onAutoScroll(sender,sel,ptr);
2423 switch(mode){
2424 case MOUSE_CHARS:
2425 if((fxabs(event->win_x-event->click_x)>getApp()->getDragDelta())||(fxabs(event->win_y-event->click_y)>getApp()->getDragDelta())){
2426 pos=getPosAt(event->win_x,event->win_y);
2427 setCursorPos(pos,TRUE);
2428 extendSelection(cursorpos,SELECT_CHARS,TRUE);
2429 }
2430 return 1;
2431 case MOUSE_WORDS:
2432 if((fxabs(event->win_x-event->click_x)>getApp()->getDragDelta())||(fxabs(event->win_y-event->click_y)>getApp()->getDragDelta())){
2433 pos=getPosAt(event->win_x,event->win_y);
2434 setCursorPos(pos,TRUE);
2435 extendSelection(cursorpos,SELECT_WORDS,TRUE);
2436 }
2437 return 1;
2438 case MOUSE_LINES:
2439 if((fxabs(event->win_x-event->click_x)>getApp()->getDragDelta())||(fxabs(event->win_y-event->click_y)>getApp()->getDragDelta())){
2440 pos=getPosAt(event->win_x,event->win_y);
2441 setCursorPos(pos,TRUE);
2442 extendSelection(cursorpos,SELECT_LINES,TRUE);
2443 }
2444 return 1;
2445 }
2446 return 0;
2447 }
2448
2449
2450 // Handle real or simulated mouse motion
onMotion(FXObject *,FXSelector,void * ptr)2451 long FXText::onMotion(FXObject*,FXSelector,void* ptr){
2452 FXEvent* event=(FXEvent*)ptr;
2453 FXint pos;
2454 switch(mode){
2455 case MOUSE_CHARS:
2456 if(startAutoScroll(event,FALSE)) return 1;
2457 if((fxabs(event->win_x-event->click_x)>getApp()->getDragDelta())||(fxabs(event->win_y-event->click_y)>getApp()->getDragDelta())){
2458 pos=getPosAt(event->win_x,event->win_y);
2459 setCursorPos(pos,TRUE);
2460 extendSelection(cursorpos,SELECT_CHARS,TRUE);
2461 }
2462 return 1;
2463 case MOUSE_WORDS:
2464 if(startAutoScroll(event,FALSE)) return 1;
2465 if((fxabs(event->win_x-event->click_x)>getApp()->getDragDelta())||(fxabs(event->win_y-event->click_y)>getApp()->getDragDelta())){
2466 pos=getPosAt(event->win_x,event->win_y);
2467 setCursorPos(pos,TRUE);
2468 extendSelection(cursorpos,SELECT_WORDS,TRUE);
2469 }
2470 return 1;
2471 case MOUSE_LINES:
2472 if(startAutoScroll(event,FALSE)) return 1;
2473 if((fxabs(event->win_x-event->click_x)>getApp()->getDragDelta())||(fxabs(event->win_y-event->click_y)>getApp()->getDragDelta())){
2474 pos=getPosAt(event->win_x,event->win_y);
2475 setCursorPos(pos,TRUE);
2476 extendSelection(cursorpos,SELECT_LINES,TRUE);
2477 }
2478 return 1;
2479 case MOUSE_SCROLL:
2480 setPosition(event->win_x-grabx,event->win_y-graby);
2481 return 1;
2482 case MOUSE_DRAG:
2483 handle(this,FXSEL(SEL_DRAGGED,0),ptr);
2484 return 1;
2485 case MOUSE_TRYDRAG:
2486 if(event->moved){
2487 mode=MOUSE_NONE;
2488 if(handle(this,FXSEL(SEL_BEGINDRAG,0),ptr)){
2489 mode=MOUSE_DRAG;
2490 }
2491 }
2492 return 1;
2493 }
2494 return 0;
2495 }
2496
2497
2498 /*******************************************************************************/
2499
2500
2501 // Start a drag operation
onBeginDrag(FXObject * sender,FXSelector sel,void * ptr)2502 long FXText::onBeginDrag(FXObject* sender,FXSelector sel,void* ptr){
2503 FXDragType types[4];
2504 if(FXScrollArea::onBeginDrag(sender,sel,ptr)) return 1;
2505 types[0]=stringType;
2506 types[1]=textType;
2507 types[2]=utf8Type;
2508 types[3]=utf16Type;
2509 beginDrag(types,4);
2510 setDragCursor(getApp()->getDefaultCursor(DEF_DNDSTOP_CURSOR));
2511 return 1;
2512 }
2513
2514
2515 // End drag operation
onEndDrag(FXObject * sender,FXSelector sel,void * ptr)2516 long FXText::onEndDrag(FXObject* sender,FXSelector sel,void* ptr){
2517 if(FXScrollArea::onEndDrag(sender,sel,ptr)) return 1;
2518 endDrag((didAccept()!=DRAG_REJECT));
2519 setDragCursor(getApp()->getDefaultCursor(DEF_TEXT_CURSOR));
2520 return 1;
2521 }
2522
2523
2524 // Dragged stuff around
onDragged(FXObject * sender,FXSelector sel,void * ptr)2525 long FXText::onDragged(FXObject* sender,FXSelector sel,void* ptr){
2526 FXEvent* event=(FXEvent*)ptr;
2527 FXDragAction action;
2528 if(FXScrollArea::onDragged(sender,sel,ptr)) return 1;
2529 action=DRAG_COPY;
2530 if(isEditable()){
2531 if(isDropTarget()) action=DRAG_MOVE;
2532 if(event->state&CONTROLMASK) action=DRAG_COPY;
2533 if(event->state&SHIFTMASK) action=DRAG_MOVE;
2534 }
2535 handleDrag(event->root_x,event->root_y,action);
2536 if(didAccept()!=DRAG_REJECT){
2537 if(action==DRAG_MOVE)
2538 setDragCursor(getApp()->getDefaultCursor(DEF_DNDMOVE_CURSOR));
2539 else
2540 setDragCursor(getApp()->getDefaultCursor(DEF_DNDCOPY_CURSOR));
2541 }
2542 else{
2543 setDragCursor(getApp()->getDefaultCursor(DEF_DNDSTOP_CURSOR));
2544 }
2545 return 1;
2546 }
2547
2548
2549 // Handle drag-and-drop enter
onDNDEnter(FXObject * sender,FXSelector sel,void * ptr)2550 long FXText::onDNDEnter(FXObject* sender,FXSelector sel,void* ptr){
2551 FXScrollArea::onDNDEnter(sender,sel,ptr);
2552 drawCursor(FLAG_CARET);
2553 revertpos=cursorpos;
2554 return 1;
2555 }
2556
2557
2558 // Handle drag-and-drop leave
onDNDLeave(FXObject * sender,FXSelector sel,void * ptr)2559 long FXText::onDNDLeave(FXObject* sender,FXSelector sel,void* ptr){
2560 FXScrollArea::onDNDLeave(sender,sel,ptr);
2561 stopAutoScroll();
2562 drawCursor(0);
2563 setCursorPos(revertpos,TRUE);
2564 return 1;
2565 }
2566
2567
2568 // Handle drag-and-drop motion
onDNDMotion(FXObject * sender,FXSelector sel,void * ptr)2569 long FXText::onDNDMotion(FXObject* sender,FXSelector sel,void* ptr){
2570 FXEvent* event=(FXEvent*)ptr;
2571
2572 // Scroll into view
2573 if(startAutoScroll(event,TRUE)) return 1;
2574
2575 // Handled elsewhere
2576 if(FXScrollArea::onDNDMotion(sender,sel,ptr)) return 1;
2577
2578 // Correct drop type
2579 if(offeredDNDType(FROM_DRAGNDROP,textType) || offeredDNDType(FROM_DRAGNDROP,stringType) || offeredDNDType(FROM_DRAGNDROP,utf8Type) || offeredDNDType(FROM_DRAGNDROP,utf16Type)){
2580
2581 // Is target editable?
2582 if(isEditable()){
2583 FXDragAction action=inquireDNDAction();
2584
2585 // Check for legal DND action
2586 if(action==DRAG_COPY || action==DRAG_MOVE){
2587
2588 // Get the suggested drop position
2589 FXint pos=getPosAt(event->win_x,event->win_y);
2590
2591 // Move cursor to new position
2592 setCursorPos(pos,TRUE);
2593 makePositionVisible(cursorpos);
2594
2595 // We don't accept a drop on the selection
2596 if(!isPosSelected(pos)){
2597 acceptDrop(DRAG_ACCEPT);
2598 }
2599 }
2600 }
2601 return 1;
2602 }
2603
2604 // Didn't handle it here
2605 return 0;
2606 }
2607
2608
2609 // Handle drag-and-drop drop
onDNDDrop(FXObject * sender,FXSelector sel,void * ptr)2610 long FXText::onDNDDrop(FXObject* sender,FXSelector sel,void* ptr){
2611
2612 // Stop scrolling
2613 stopAutoScroll();
2614 drawCursor(0);
2615
2616 // Try handling it in base class first
2617 if(FXScrollArea::onDNDDrop(sender,sel,ptr)) return 1;
2618
2619 // Should really not have gotten this if non-editable
2620 if(isEditable()){
2621 FXString string;
2622 FXString junk;
2623
2624 // First, try UTF-8
2625 if(getDNDData(FROM_DRAGNDROP,utf8Type,string)){
2626 FXTRACE((100,"Paste UTF8\n"));
2627 if(inquireDNDAction()==DRAG_MOVE){
2628 getDNDData(FROM_DRAGNDROP,deleteType,junk);
2629 }
2630 handle(this,FXSEL(SEL_COMMAND,ID_INSERT_STRING),(void*)string.text());
2631 return 1;
2632 }
2633
2634 // Next, try UTF-16
2635 if(getDNDData(FROM_DRAGNDROP,utf16Type,string)){
2636 FXUTF16LECodec unicode; // FIXME maybe other endianness for unix
2637 FXTRACE((100,"Paste UTF16\n"));
2638 if(inquireDNDAction()==DRAG_MOVE){
2639 getDNDData(FROM_DRAGNDROP,deleteType,junk);
2640 }
2641 handle(this,FXSEL(SEL_COMMAND,ID_INSERT_STRING),(void*)unicode.mb2utf(string).text());
2642 return 1;
2643 }
2644
2645 // Next, try good old Latin-1
2646 if(getDNDData(FROM_DRAGNDROP,textType,string)){
2647 FX88591Codec ascii;
2648 FXTRACE((100,"Paste ASCII\n"));
2649 if(inquireDNDAction()==DRAG_MOVE){
2650 getDNDData(FROM_DRAGNDROP,deleteType,junk);
2651 }
2652 handle(this,FXSEL(SEL_COMMAND,ID_INSERT_STRING),(void*)ascii.mb2utf(string).text());
2653 return 1;
2654 }
2655 return 1;
2656 }
2657 return 0;
2658 }
2659
2660
2661 // Service requested DND data
onDNDRequest(FXObject * sender,FXSelector sel,void * ptr)2662 long FXText::onDNDRequest(FXObject* sender,FXSelector sel,void* ptr){
2663 FXEvent *event=(FXEvent*)ptr;
2664
2665 // Perhaps the target wants to supply its own data
2666 if(FXScrollArea::onDNDRequest(sender,sel,ptr)) return 1;
2667
2668 // Recognize the request?
2669 if(event->target==stringType || event->target==textType || event->target==utf8Type || event->target==utf16Type){
2670 FXString string;
2671
2672 // Get selected fragment
2673 extractText(string,selstartpos,selendpos-selstartpos);
2674
2675 // Return text of the selection as UTF-8
2676 if(event->target==utf8Type){
2677 FXTRACE((100,"Request UTF8\n"));
2678 setDNDData(FROM_DRAGNDROP,event->target,string);
2679 return 1;
2680 }
2681
2682 // Return text of the selection translated to 8859-1
2683 if(event->target==stringType || event->target==textType){
2684 FX88591Codec ascii;
2685 FXTRACE((100,"Request ASCII\n"));
2686 setDNDData(FROM_DRAGNDROP,event->target,ascii.utf2mb(string));
2687 return 1;
2688 }
2689
2690 // Return text of the selection translated to UTF-16
2691 if(event->target==utf16Type){
2692 FXUTF16LECodec unicode; // FIXME maybe other endianness for unix
2693 FXTRACE((100,"Request UTF16\n"));
2694 setDNDData(FROM_DRAGNDROP,event->target,unicode.utf2mb(string));
2695 return 1;
2696 }
2697 }
2698
2699 // Delete dragged text, if editable
2700 if(event->target==deleteType){
2701 if(isEditable()){
2702 handle(this,FXSEL(SEL_COMMAND,ID_DELETE_SEL),NULL);
2703 }
2704 return 1;
2705 }
2706
2707 return 0;
2708 }
2709
2710
2711 /*******************************************************************************/
2712
2713
2714 // We now really do have the selection
onSelectionGained(FXObject * sender,FXSelector sel,void * ptr)2715 long FXText::onSelectionGained(FXObject* sender,FXSelector sel,void* ptr){
2716 FXScrollArea::onSelectionGained(sender,sel,ptr);
2717 return 1;
2718 }
2719
2720
2721 // We lost the selection somehow
onSelectionLost(FXObject * sender,FXSelector sel,void * ptr)2722 long FXText::onSelectionLost(FXObject* sender,FXSelector sel,void* ptr){
2723 FXint what[2];
2724 FXScrollArea::onSelectionLost(sender,sel,ptr);
2725 if(target){
2726 what[0]=selstartpos;
2727 what[1]=selendpos-selstartpos;
2728 target->tryHandle(this,FXSEL(SEL_DESELECTED,message),(void*)what);
2729 }
2730 updateRange(selstartpos,selendpos);
2731 selstartpos=0;
2732 selendpos=0;
2733 return 1;
2734 }
2735
2736
2737 // Somebody wants our selection
onSelectionRequest(FXObject * sender,FXSelector sel,void * ptr)2738 long FXText::onSelectionRequest(FXObject* sender,FXSelector sel,void* ptr){
2739 FXEvent *event=(FXEvent*)ptr;
2740
2741 // Perhaps the target wants to supply its own data for the selection
2742 if(FXScrollArea::onSelectionRequest(sender,sel,ptr)) return 1;
2743
2744 // Recognize the request?
2745 if(event->target==stringType || event->target==textType || event->target==utf8Type || event->target==utf16Type){
2746 FXString string;
2747
2748 // Get selected fragment
2749 extractText(string,selstartpos,selendpos-selstartpos);
2750
2751 // Return text of the selection as UTF-8
2752 if(event->target==utf8Type){
2753 FXTRACE((100,"Request UTF8\n"));
2754 setDNDData(FROM_SELECTION,event->target,string);
2755 return 1;
2756 }
2757
2758 // Return text of the selection translated to 8859-1
2759 if(event->target==stringType || event->target==textType){
2760 FX88591Codec ascii;
2761 FXTRACE((100,"Request ASCII\n"));
2762 string=ascii.utf2mb(string);
2763 setDNDData(FROM_SELECTION,event->target,string);
2764 return 1;
2765 }
2766
2767 // Return text of the selection translated to UTF-16
2768 if(event->target==utf16Type){
2769 FXUTF16LECodec unicode; // FIXME maybe other endianness for unix
2770 FXTRACE((100,"Request UTF16\n"));
2771 string=unicode.utf2mb(string);
2772 setDNDData(FROM_SELECTION,event->target,string);
2773 return 1;
2774 }
2775 }
2776 return 0;
2777 }
2778
2779
2780 /*******************************************************************************/
2781
2782
2783 // We now really do have the selection
onClipboardGained(FXObject * sender,FXSelector sel,void * ptr)2784 long FXText::onClipboardGained(FXObject* sender,FXSelector sel,void* ptr){
2785 FXScrollArea::onClipboardGained(sender,sel,ptr);
2786 return 1;
2787 }
2788
2789
2790 // We lost the selection somehow
onClipboardLost(FXObject * sender,FXSelector sel,void * ptr)2791 long FXText::onClipboardLost(FXObject* sender,FXSelector sel,void* ptr){
2792 FXScrollArea::onClipboardLost(sender,sel,ptr);
2793 clipped.clear();
2794 return 1;
2795 }
2796
2797
2798 // Somebody wants our selection
onClipboardRequest(FXObject * sender,FXSelector sel,void * ptr)2799 long FXText::onClipboardRequest(FXObject* sender,FXSelector sel,void* ptr){
2800 FXEvent *event=(FXEvent*)ptr;
2801
2802 // Try handling it in base class first
2803 if(FXScrollArea::onClipboardRequest(sender,sel,ptr)) return 1;
2804
2805 // Requested data from clipboard
2806 if(event->target==stringType || event->target==textType || event->target==utf8Type || event->target==utf16Type){
2807 FXString string=clipped;
2808
2809 // Expand newlines to CRLF on Windows
2810 #ifdef WIN32
2811 unixToDos(string);
2812 #endif
2813
2814 // Return clipped text as as UTF-8
2815 if(event->target==utf8Type){
2816 FXTRACE((100,"Request UTF8\n"));
2817 setDNDData(FROM_CLIPBOARD,event->target,string);
2818 return 1;
2819 }
2820
2821 // Return clipped text translated to 8859-1
2822 if(event->target==stringType || event->target==textType){
2823 FX88591Codec ascii;
2824 FXTRACE((100,"Request ASCII\n"));
2825 setDNDData(FROM_CLIPBOARD,event->target,ascii.utf2mb(string));
2826 return 1;
2827 }
2828
2829 // Return text of the selection translated to UTF-16
2830 if(event->target==utf16Type){
2831 FXUTF16LECodec unicode; // FIXME maybe other endianness for unix
2832 FXTRACE((100,"Request UTF16\n"));
2833 setDNDData(FROM_CLIPBOARD,event->target,unicode.utf2mb(string));
2834 return 1;
2835 }
2836 }
2837 return 0;
2838 }
2839
2840
2841 /*******************************************************************************/
2842
2843 // Keyboard press
onKeyPress(FXObject *,FXSelector,void * ptr)2844 long FXText::onKeyPress(FXObject*,FXSelector,void* ptr){
2845 FXEvent* event=(FXEvent*)ptr;
2846 flags&=~FLAG_TIP;
2847 if(isEnabled()){
2848 FXTRACE((200,"%s::onKeyPress keysym=0x%04x state=%04x\n",getClassName(),event->code,event->state));
2849 if(target && target->tryHandle(this,FXSEL(SEL_KEYPRESS,message),ptr)) return 1;
2850 flags&=~FLAG_UPDATE;
2851 switch(event->code){
2852 case KEY_Shift_L:
2853 case KEY_Shift_R:
2854 case KEY_Control_L:
2855 case KEY_Control_R:
2856 if(mode==MOUSE_DRAG){handle(this,FXSEL(SEL_DRAGGED,0),ptr);}
2857 return 1;
2858 case KEY_Up:
2859 case KEY_KP_Up:
2860 if(event->state&CONTROLMASK){
2861 handle(this,FXSEL(SEL_COMMAND,ID_SCROLL_UP),NULL);
2862 }
2863 else{
2864 if(!(event->state&SHIFTMASK)){
2865 handle(this,FXSEL(SEL_COMMAND,ID_DESELECT_ALL),NULL);
2866 }
2867 handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_UP),NULL);
2868 if(event->state&SHIFTMASK){
2869 handle(this,FXSEL(SEL_COMMAND,ID_EXTEND),NULL);
2870 }
2871 else{
2872 handle(this,FXSEL(SEL_COMMAND,ID_MARK),NULL);
2873 }
2874 }
2875 return 1;
2876 case KEY_Down:
2877 case KEY_KP_Down:
2878 if(event->state&CONTROLMASK){
2879 handle(this,FXSEL(SEL_COMMAND,ID_SCROLL_DOWN),NULL);
2880 }
2881 else{
2882 if(!(event->state&SHIFTMASK)){
2883 handle(this,FXSEL(SEL_COMMAND,ID_DESELECT_ALL),NULL);
2884 }
2885 handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_DOWN),NULL);
2886 if(event->state&SHIFTMASK){
2887 handle(this,FXSEL(SEL_COMMAND,ID_EXTEND),NULL);
2888 }
2889 else{
2890 handle(this,FXSEL(SEL_COMMAND,ID_MARK),NULL);
2891 }
2892 }
2893 return 1;
2894 case KEY_Left:
2895 case KEY_KP_Left:
2896 if(!(event->state&SHIFTMASK)){
2897 handle(this,FXSEL(SEL_COMMAND,ID_DESELECT_ALL),NULL);
2898 }
2899 if(event->state&CONTROLMASK){
2900 handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_WORD_LEFT),NULL);
2901 }
2902 else{
2903 handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_LEFT),NULL);
2904 }
2905 if(event->state&SHIFTMASK){
2906 handle(this,FXSEL(SEL_COMMAND,ID_EXTEND),NULL);
2907 }
2908 else{
2909 handle(this,FXSEL(SEL_COMMAND,ID_MARK),NULL);
2910 }
2911 return 1;
2912 case KEY_Right:
2913 case KEY_KP_Right:
2914 if(!(event->state&SHIFTMASK)){
2915 handle(this,FXSEL(SEL_COMMAND,ID_DESELECT_ALL),NULL);
2916 }
2917 if(event->state&CONTROLMASK){
2918 handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_WORD_RIGHT),NULL);
2919 }
2920 else{
2921 handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_RIGHT),NULL);
2922 }
2923 if(event->state&SHIFTMASK){
2924 handle(this,FXSEL(SEL_COMMAND,ID_EXTEND),NULL);
2925 }
2926 else{
2927 handle(this,FXSEL(SEL_COMMAND,ID_MARK),NULL);
2928 }
2929 return 1;
2930 case KEY_Home:
2931 case KEY_KP_Home:
2932 if(!(event->state&SHIFTMASK)){
2933 handle(this,FXSEL(SEL_COMMAND,ID_DESELECT_ALL),NULL);
2934 }
2935 if(event->state&CONTROLMASK){
2936 handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_TOP),NULL);
2937 }
2938 else{
2939 handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_HOME),NULL);
2940 }
2941 if(event->state&SHIFTMASK){
2942 handle(this,FXSEL(SEL_COMMAND,ID_EXTEND),NULL);
2943 }
2944 else{
2945 handle(this,FXSEL(SEL_COMMAND,ID_MARK),NULL);
2946 }
2947 return 1;
2948 case KEY_End:
2949 case KEY_KP_End:
2950 if(!(event->state&SHIFTMASK)){
2951 handle(this,FXSEL(SEL_COMMAND,ID_DESELECT_ALL),NULL);
2952 }
2953 if(event->state&CONTROLMASK){
2954 handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_BOTTOM),NULL);
2955 }
2956 else{
2957 handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_END),NULL);
2958 }
2959 if(event->state&SHIFTMASK){
2960 handle(this,FXSEL(SEL_COMMAND,ID_EXTEND),NULL);
2961 }
2962 else{
2963 handle(this,FXSEL(SEL_COMMAND,ID_MARK),NULL);
2964 }
2965 return 1;
2966 case KEY_Page_Up:
2967 case KEY_KP_Page_Up:
2968 if(!(event->state&SHIFTMASK)){
2969 handle(this,FXSEL(SEL_COMMAND,ID_DESELECT_ALL),NULL);
2970 }
2971 handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_PAGEUP),NULL);
2972 if(event->state&SHIFTMASK){
2973 handle(this,FXSEL(SEL_COMMAND,ID_EXTEND),NULL);
2974 }
2975 else{
2976 handle(this,FXSEL(SEL_COMMAND,ID_MARK),NULL);
2977 }
2978 return 1;
2979 case KEY_Page_Down:
2980 case KEY_KP_Page_Down:
2981 if(!(event->state&SHIFTMASK)){
2982 handle(this,FXSEL(SEL_COMMAND,ID_DESELECT_ALL),NULL);
2983 }
2984 handle(this,FXSEL(SEL_COMMAND,ID_CURSOR_PAGEDOWN),NULL);
2985 if(event->state&SHIFTMASK){
2986 handle(this,FXSEL(SEL_COMMAND,ID_EXTEND),NULL);
2987 }
2988 else{
2989 handle(this,FXSEL(SEL_COMMAND,ID_MARK),NULL);
2990 }
2991 return 1;
2992 case KEY_Insert:
2993 case KEY_KP_Insert:
2994 if(event->state&CONTROLMASK){
2995 handle(this,FXSEL(SEL_COMMAND,ID_COPY_SEL),NULL);
2996 }
2997 else if(event->state&SHIFTMASK){
2998 handle(this,FXSEL(SEL_COMMAND,ID_PASTE_SEL),NULL);
2999 }
3000 else{
3001 handle(this,FXSEL(SEL_COMMAND,ID_TOGGLE_OVERSTRIKE),NULL);
3002 }
3003 return 1;
3004 case KEY_Delete:
3005 case KEY_KP_Delete:
3006 if(isPosSelected(cursorpos)){
3007 if(event->state&SHIFTMASK){
3008 handle(this,FXSEL(SEL_COMMAND,ID_CUT_SEL),NULL);
3009 }
3010 else{
3011 handle(this,FXSEL(SEL_COMMAND,ID_DELETE_SEL),NULL);
3012 }
3013 }
3014 else{
3015 handle(this,FXSEL(SEL_COMMAND,ID_DESELECT_ALL),NULL);
3016 if(event->state&CONTROLMASK){
3017 handle(this,FXSEL(SEL_COMMAND,ID_DELETE_WORD),NULL);
3018 }
3019 else if(event->state&SHIFTMASK){
3020 handle(this,FXSEL(SEL_COMMAND,ID_DELETE_EOL),NULL);
3021 }
3022 else{
3023 handle(this,FXSEL(SEL_COMMAND,ID_DELETE),NULL);
3024 }
3025 }
3026 return 1;
3027 case KEY_BackSpace:
3028 if(isPosSelected(cursorpos)){
3029 handle(this,FXSEL(SEL_COMMAND,ID_DELETE_SEL),NULL);
3030 }
3031 else{
3032 handle(this,FXSEL(SEL_COMMAND,ID_DESELECT_ALL),NULL);
3033 if(event->state&CONTROLMASK){
3034 handle(this,FXSEL(SEL_COMMAND,ID_BACKSPACE_WORD),NULL);
3035 }
3036 else if(event->state&SHIFTMASK){
3037 handle(this,FXSEL(SEL_COMMAND,ID_BACKSPACE_BOL),NULL);
3038 }
3039 else{
3040 handle(this,FXSEL(SEL_COMMAND,ID_BACKSPACE),NULL);
3041 }
3042 }
3043 return 1;
3044 case KEY_Return:
3045 case KEY_KP_Enter:
3046 handle(this,FXSEL(SEL_COMMAND,ID_INSERT_NEWLINE),NULL);
3047 return 1;
3048 case KEY_Tab:
3049 case KEY_KP_Tab:
3050 if(event->state&CONTROLMASK){
3051 handle(this,FXSEL(SEL_COMMAND,ID_INSERT_STRING),(void*)"\t");
3052 }
3053 else{
3054 handle(this,FXSEL(SEL_COMMAND,ID_INSERT_TAB),NULL);
3055 }
3056 return 1;
3057 case KEY_a:
3058 if(!(event->state&CONTROLMASK)) goto ins;
3059 handle(this,FXSEL(SEL_COMMAND,ID_SELECT_ALL),NULL);
3060 return 1;
3061 case KEY_x:
3062 if(!(event->state&CONTROLMASK)) goto ins;
3063 case KEY_F20: // Sun Cut key
3064 handle(this,FXSEL(SEL_COMMAND,ID_CUT_SEL),NULL);
3065 return 1;
3066 case KEY_c:
3067 if(!(event->state&CONTROLMASK)) goto ins;
3068 case KEY_F16: // Sun Copy key
3069 handle(this,FXSEL(SEL_COMMAND,ID_COPY_SEL),NULL);
3070 return 1;
3071 case KEY_v:
3072 if(!(event->state&CONTROLMASK)) goto ins;
3073 case KEY_F18: // Sun Paste key
3074 handle(this,FXSEL(SEL_COMMAND,ID_PASTE_SEL),NULL);
3075 return 1;
3076 default:
3077 ins: if((event->state&(CONTROLMASK|ALTMASK)) || ((FXuchar)event->text[0]<32)) return 0;
3078 if(isOverstrike()){
3079 handle(this,FXSEL(SEL_COMMAND,ID_OVERST_STRING),(void*)event->text.text());
3080 }
3081 else{
3082 handle(this,FXSEL(SEL_COMMAND,ID_INSERT_STRING),(void*)event->text.text());
3083 }
3084 return 1;
3085 }
3086 }
3087 return 0;
3088 }
3089
3090
3091 // Keyboard release
onKeyRelease(FXObject *,FXSelector,void * ptr)3092 long FXText::onKeyRelease(FXObject*,FXSelector,void* ptr){
3093 FXEvent* event=(FXEvent*)ptr;
3094 if(isEnabled()){
3095 FXTRACE((200,"%s::onKeyRelease keysym=0x%04x state=%04x\n",getClassName(),event->code,event->state));
3096 if(target && target->tryHandle(this,FXSEL(SEL_KEYRELEASE,message),ptr)) return 1;
3097 switch(event->code){
3098 case KEY_Shift_L:
3099 case KEY_Shift_R:
3100 case KEY_Control_L:
3101 case KEY_Control_R:
3102 if(mode==MOUSE_DRAG){handle(this,FXSEL(SEL_DRAGGED,0),ptr);}
3103 return 1;
3104 }
3105 }
3106 return 0;
3107 }
3108
3109
3110 /*******************************************************************************/
3111
3112 // Move cursor to top of buffer
onCmdCursorTop(FXObject *,FXSelector,void *)3113 long FXText::onCmdCursorTop(FXObject*,FXSelector,void*){
3114 setCursorPos(0,TRUE);
3115 makePositionVisible(cursorpos);
3116 flashMatching();
3117 return 1;
3118 }
3119
3120
3121 // Move cursor to bottom of buffer
onCmdCursorBottom(FXObject *,FXSelector,void *)3122 long FXText::onCmdCursorBottom(FXObject*,FXSelector,void*){
3123 setCursorPos(length,TRUE);
3124 makePositionVisible(cursorpos);
3125 flashMatching();
3126 return 1;
3127 }
3128
3129
3130 // Move cursor to begin of line
onCmdCursorHome(FXObject *,FXSelector,void *)3131 long FXText::onCmdCursorHome(FXObject*,FXSelector,void*){
3132 setCursorPos(rowStart(cursorpos),TRUE);
3133 makePositionVisible(cursorpos);
3134 flashMatching();
3135 return 1;
3136 }
3137
3138
3139 // Move cursor to end of line
onCmdCursorEnd(FXObject *,FXSelector,void *)3140 long FXText::onCmdCursorEnd(FXObject*,FXSelector,void*){
3141 setCursorPos(rowEnd(cursorpos),TRUE);
3142 makePositionVisible(cursorpos);
3143 flashMatching();
3144 return 1;
3145 }
3146
3147
3148 // Move cursor right
onCmdCursorRight(FXObject *,FXSelector,void *)3149 long FXText::onCmdCursorRight(FXObject*,FXSelector,void*){
3150 if(cursorpos>=length) return 1;
3151 setCursorPos(inc(cursorpos),TRUE);
3152 makePositionVisible(cursorpos);
3153 flashMatching();
3154 return 1;
3155 }
3156
3157
3158 // Move cursor left
onCmdCursorLeft(FXObject *,FXSelector,void *)3159 long FXText::onCmdCursorLeft(FXObject*,FXSelector,void*){
3160 if(cursorpos<=0) return 1;
3161 setCursorPos(dec(cursorpos),TRUE);
3162 makePositionVisible(cursorpos);
3163 flashMatching();
3164 return 1;
3165 }
3166
3167
3168 // Move cursor to previous line
onCmdCursorUp(FXObject *,FXSelector,void *)3169 long FXText::onCmdCursorUp(FXObject*,FXSelector,void*){
3170 FXint newrow,newpos,col;
3171 col=(0<=prefcol) ? prefcol : cursorcol;
3172 newrow=prevRow(cursorpos);
3173 newpos=posFromIndent(newrow,col);
3174 setCursorPos(newpos,TRUE);
3175 makePositionVisible(cursorpos);
3176 flashMatching();
3177 prefcol=col;
3178 return 1;
3179 }
3180
3181
3182 // Move cursor to next line
onCmdCursorDown(FXObject *,FXSelector,void *)3183 long FXText::onCmdCursorDown(FXObject*,FXSelector,void*){
3184 FXint newrow,newpos,col;
3185 col=(0<=prefcol) ? prefcol : cursorcol;
3186 newrow=nextRow(cursorpos);
3187 newpos=posFromIndent(newrow,col);
3188 setCursorPos(newpos,TRUE);
3189 makePositionVisible(cursorpos);
3190 flashMatching();
3191 prefcol=col;
3192 return 1;
3193 }
3194
3195
3196 // Page down
onCmdCursorPageDown(FXObject *,FXSelector,void *)3197 long FXText::onCmdCursorPageDown(FXObject*,FXSelector,void*){
3198 FXint newrow,newpos,col;
3199 col=(0<=prefcol) ? prefcol : cursorcol;
3200 newrow=nextRow(cursorpos,(viewport_h)/font->getFontHeight());
3201 newpos=posFromIndent(newrow,col);
3202 setTopLine(nextRow(toppos,viewport_h/font->getFontHeight()));
3203 setCursorPos(newpos,TRUE);
3204 makePositionVisible(cursorpos);
3205 prefcol=col;
3206 return 1;
3207 }
3208
3209
3210 // Page up
onCmdCursorPageUp(FXObject *,FXSelector,void *)3211 long FXText::onCmdCursorPageUp(FXObject*,FXSelector,void*){
3212 FXint newrow,newpos,col;
3213 col=(0<=prefcol) ? prefcol : cursorcol;
3214 newrow=prevRow(cursorpos,(viewport_h)/font->getFontHeight());
3215 newpos=posFromIndent(newrow,col);
3216 setTopLine(prevRow(toppos,viewport_h/font->getFontHeight()));
3217 setCursorPos(newpos,TRUE);
3218 makePositionVisible(cursorpos);
3219 prefcol=col;
3220 return 1;
3221 }
3222
3223
3224 // Word Left
onCmdCursorWordLeft(FXObject *,FXSelector,void *)3225 long FXText::onCmdCursorWordLeft(FXObject*,FXSelector,void*){
3226 setCursorPos(leftWord(cursorpos),TRUE);
3227 makePositionVisible(cursorpos);
3228 flashMatching();
3229 return 1;
3230 }
3231
3232
3233 // Word Right
onCmdCursorWordRight(FXObject *,FXSelector,void *)3234 long FXText::onCmdCursorWordRight(FXObject*,FXSelector,void*){
3235 setCursorPos(rightWord(cursorpos),TRUE);
3236 makePositionVisible(cursorpos);
3237 flashMatching();
3238 return 1;
3239 }
3240
3241
3242 // Word Start
onCmdCursorWordStart(FXObject *,FXSelector,void *)3243 long FXText::onCmdCursorWordStart(FXObject*,FXSelector,void*){
3244 setCursorPos(wordStart(cursorpos),TRUE);
3245 makePositionVisible(cursorpos);
3246 flashMatching();
3247 return 1;
3248 }
3249
3250
3251 // Word End
onCmdCursorWordEnd(FXObject *,FXSelector,void *)3252 long FXText::onCmdCursorWordEnd(FXObject*,FXSelector,void*){
3253 setCursorPos(wordEnd(cursorpos),TRUE);
3254 makePositionVisible(cursorpos);
3255 flashMatching();
3256 return 1;
3257 }
3258
3259
3260 // Cursor pos to top of screen
onCmdCursorScreenTop(FXObject *,FXSelector,void *)3261 long FXText::onCmdCursorScreenTop(FXObject*,FXSelector,void*){
3262 setTopLine(cursorpos);
3263 return 1;
3264 }
3265
3266
3267 // Cursor pos to bottom of screen
onCmdCursorScreenBottom(FXObject *,FXSelector,void *)3268 long FXText::onCmdCursorScreenBottom(FXObject*,FXSelector,void*){
3269 setBottomLine(cursorpos);
3270 return 1;
3271 }
3272
3273
3274 // Cursor pos to center of screen
onCmdCursorScreenCenter(FXObject *,FXSelector,void *)3275 long FXText::onCmdCursorScreenCenter(FXObject*,FXSelector,void*){
3276 setCenterLine(cursorpos);
3277 return 1;
3278 }
3279
3280
3281 // Scroll up one line, leaving cursor in place
onCmdScrollUp(FXObject *,FXSelector,void *)3282 long FXText::onCmdScrollUp(FXObject*,FXSelector,void*){
3283 setTopLine(prevRow(toppos,1));
3284 return 1;
3285 }
3286
3287
3288 // Scroll down one line, leaving cursor in place
onCmdScrollDown(FXObject *,FXSelector,void *)3289 long FXText::onCmdScrollDown(FXObject*,FXSelector,void*){
3290 setTopLine(nextRow(toppos,1));
3291 return 1;
3292 }
3293
3294
3295 // Move cursor to begin of paragraph
onCmdCursorParHome(FXObject *,FXSelector,void *)3296 long FXText::onCmdCursorParHome(FXObject*,FXSelector,void*){
3297 setCursorPos(lineStart(cursorpos),TRUE);
3298 makePositionVisible(cursorpos);
3299 return 1;
3300 }
3301
3302
3303 // Move cursor to end of paragraph
onCmdCursorParEnd(FXObject *,FXSelector,void *)3304 long FXText::onCmdCursorParEnd(FXObject*,FXSelector,void*){
3305 setCursorPos(lineEnd(cursorpos),TRUE);
3306 makePositionVisible(cursorpos);
3307 return 1;
3308 }
3309
3310
3311 // Mark
onCmdMark(FXObject *,FXSelector,void *)3312 long FXText::onCmdMark(FXObject*,FXSelector,void*){
3313 setAnchorPos(cursorpos);
3314 return 1;
3315 }
3316
3317
3318 // Extend
onCmdExtend(FXObject *,FXSelector,void *)3319 long FXText::onCmdExtend(FXObject*,FXSelector,void*){
3320 extendSelection(cursorpos,SELECT_CHARS,TRUE);
3321 return 1;
3322 }
3323
3324
3325 // Overstrike a string
onCmdOverstString(FXObject *,FXSelector,void * ptr)3326 long FXText::onCmdOverstString(FXObject*,FXSelector,void* ptr){
3327 if(isEditable()){
3328 FXint sindent,oindent,nindent,pos,ch,reppos,replen;
3329 FXchar* string=(FXchar*)ptr;
3330 FXint len=strlen(string);
3331 if(isPosSelected(cursorpos)){
3332 reppos=selstartpos;
3333 replen=selendpos-selstartpos;
3334 }
3335 else{
3336 sindent=0;
3337 pos=lineStart(cursorpos);
3338 while(pos<cursorpos){ // Measure indent of reppos
3339 if(getByte(pos)=='\t')
3340 sindent+=(tabcolumns-sindent%tabcolumns);
3341 else
3342 sindent+=1;
3343 pos++;
3344 }
3345 nindent=sindent;
3346 pos=0;
3347 while(pos<len){ // Measure indent of new string
3348 if(string[pos]=='\t')
3349 nindent+=(tabcolumns-nindent%tabcolumns);
3350 else
3351 nindent+=1;
3352 pos++;
3353 }
3354 oindent=sindent;
3355 pos=cursorpos;
3356 while(pos<length && (ch=getByte(pos))!='\n'){ // Measure indent of old string
3357 if(ch=='\t')
3358 oindent+=(tabcolumns-oindent%tabcolumns);
3359 else
3360 oindent+=1;
3361 if(oindent==nindent){ // Same indent
3362 pos++; // Include last character
3363 break;
3364 }
3365 if(oindent>nindent){ // Greater indent
3366 if(ch!='\t') pos++; // Don't include last character if it was a tab
3367 break;
3368 }
3369 pos++;
3370 }
3371 reppos=cursorpos;
3372 replen=pos-reppos;
3373 }
3374 replaceText(reppos,replen,string,len,TRUE);
3375 killSelection(TRUE);
3376 setCursorPos(reppos+len,TRUE);
3377 makePositionVisible(cursorpos);
3378 flashMatching();
3379 flags|=FLAG_CHANGED;
3380 modified=TRUE;
3381 }
3382 else{
3383 getApp()->beep();
3384 }
3385 return 1;
3386 }
3387
3388
3389 // Insert a string
onCmdInsertString(FXObject *,FXSelector,void * ptr)3390 long FXText::onCmdInsertString(FXObject*,FXSelector,void* ptr){
3391 if(isEditable()){
3392 FXchar* string=(FXchar*)ptr;
3393 FXint len=strlen(string);
3394 FXint reppos=cursorpos;
3395 FXint replen=0;
3396 if(isPosSelected(cursorpos)){
3397 reppos=selstartpos;
3398 replen=selendpos-selstartpos;
3399 }
3400 replaceText(reppos,replen,string,len,TRUE);
3401 killSelection(TRUE);
3402 setCursorPos(reppos+len,TRUE);
3403 makePositionVisible(cursorpos);
3404 flashMatching();
3405 flags|=FLAG_CHANGED;
3406 modified=TRUE;
3407 }
3408 else{
3409 getApp()->beep();
3410 }
3411 return 1;
3412 }
3413
3414
3415 // Insert a character
onCmdInsertNewline(FXObject *,FXSelector,void *)3416 long FXText::onCmdInsertNewline(FXObject*,FXSelector,void*){
3417 if(isEditable()){
3418 FXint reppos=cursorpos;
3419 FXint replen=0;
3420 FXint len=1;
3421 if(isPosSelected(cursorpos)){
3422 reppos=selstartpos;
3423 replen=selendpos-selstartpos;
3424 }
3425 if(options&TEXT_AUTOINDENT){
3426 FXint start=lineStart(reppos);
3427 FXint end=start;
3428 FXchar *string;
3429 while(end<reppos){
3430 if(!Ascii::isSpace(getByte(end))) break;
3431 end++;
3432 }
3433 len=end-start+1;
3434 FXMALLOC(&string,FXchar,len);
3435 string[0]='\n';
3436 extractText(&string[1],start,end-start);
3437 replaceText(reppos,replen,string,len,TRUE);
3438 FXFREE(&string);
3439 }
3440 else{
3441 replaceText(reppos,replen,"\n",1,TRUE);
3442 }
3443 setCursorPos(reppos+len,TRUE);
3444 makePositionVisible(cursorpos);
3445 flags|=FLAG_CHANGED;
3446 modified=TRUE;
3447 }
3448 else{
3449 getApp()->beep();
3450 }
3451 return 1;
3452 }
3453
3454
3455
3456 // Insert a character
onCmdInsertTab(FXObject *,FXSelector,void *)3457 long FXText::onCmdInsertTab(FXObject*,FXSelector,void*){
3458 if(isEditable()){
3459 FXint reppos=cursorpos;
3460 FXint replen=0;
3461 FXint len=1;
3462 if(isPosSelected(cursorpos)){
3463 reppos=selstartpos;
3464 replen=selendpos-selstartpos;
3465 }
3466 if(options&TEXT_NO_TABS){
3467 FXint start=lineStart(reppos);
3468 FXint indent=0;
3469 FXchar *string;
3470 while(start<reppos){
3471 if(getByte(start)=='\t')
3472 indent+=(tabcolumns-indent%tabcolumns);
3473 else
3474 indent+=1;
3475 start++;
3476 }
3477 len=tabcolumns-indent%tabcolumns;
3478 FXMALLOC(&string,FXchar,len);
3479 memset(string,' ',len);
3480 replaceText(reppos,replen,string,len,TRUE);
3481 FXFREE(&string);
3482 }
3483 else{
3484 replaceText(reppos,replen,"\t",1,TRUE);
3485 }
3486 setCursorPos(reppos+len,TRUE);
3487 makePositionVisible(cursorpos);
3488 flags|=FLAG_CHANGED;
3489 modified=TRUE;
3490 }
3491 else{
3492 getApp()->beep();
3493 }
3494 return 1;
3495 }
3496
3497
3498 // Cut
onCmdCutSel(FXObject *,FXSelector,void *)3499 long FXText::onCmdCutSel(FXObject*,FXSelector,void*){
3500 if(isEditable()){
3501 if(selstartpos<selendpos){
3502 FXDragType types[4];
3503 types[0]=stringType;
3504 types[1]=textType;
3505 types[2]=utf8Type;
3506 types[3]=utf16Type;
3507 if(acquireClipboard(types,4)){
3508 FXASSERT(selstartpos<=selendpos);
3509 extractText(clipped,selstartpos,selendpos-selstartpos);
3510 handle(this,FXSEL(SEL_COMMAND,ID_DELETE_SEL),NULL);
3511 }
3512 }
3513 }
3514 else{
3515 getApp()->beep();
3516 }
3517 return 1;
3518 }
3519
3520
3521 // Copy
onCmdCopySel(FXObject *,FXSelector,void *)3522 long FXText::onCmdCopySel(FXObject*,FXSelector,void*){
3523 if(selstartpos<selendpos){
3524 FXDragType types[4];
3525 types[0]=stringType;
3526 types[1]=textType;
3527 types[2]=utf8Type;
3528 types[3]=utf16Type;
3529 if(acquireClipboard(types,4)){
3530 FXASSERT(selstartpos<=selendpos);
3531 extractText(clipped,selstartpos,selendpos-selstartpos);
3532 }
3533 }
3534 return 1;
3535 }
3536
3537
3538 // Delete selection
onCmdDeleteSel(FXObject *,FXSelector,void *)3539 long FXText::onCmdDeleteSel(FXObject*,FXSelector,void*){
3540 if(isEditable()){
3541 if(selstartpos<selendpos){
3542 removeText(selstartpos,selendpos-selstartpos,TRUE);
3543 killSelection(TRUE);
3544 setCursorPos(cursorpos,TRUE);
3545 makePositionVisible(cursorpos);
3546 flags|=FLAG_CHANGED;
3547 modified=TRUE;
3548 }
3549 }
3550 else{
3551 getApp()->beep();
3552 }
3553 return 1;
3554 }
3555
3556
3557 // Paste clipboard
onCmdPasteSel(FXObject *,FXSelector,void *)3558 long FXText::onCmdPasteSel(FXObject*,FXSelector,void*){
3559 if(isEditable()){
3560 FXString string;
3561
3562 // Delete existing selection
3563 if(hasSelection()){
3564 handle(this,FXSEL(SEL_COMMAND,ID_DELETE_SEL),NULL);
3565 }
3566
3567 // First, try UTF-8
3568 if(getDNDData(FROM_CLIPBOARD,utf8Type,string)){
3569 FXTRACE((100,"Paste UTF8\n"));
3570 #ifdef WIN32
3571 dosToUnix(string);
3572 #endif
3573 handle(this,FXSEL(SEL_COMMAND,ID_INSERT_STRING),(void*)string.text());
3574 return 1;
3575 }
3576
3577 // Next, try UTF-16
3578 if(getDNDData(FROM_CLIPBOARD,utf16Type,string)){
3579 FXUTF16LECodec unicode; // FIXME maybe other endianness for unix
3580 FXTRACE((100,"Paste UTF16\n"));
3581 string=unicode.mb2utf(string);
3582 #ifdef WIN32
3583 dosToUnix(string);
3584 #endif
3585 handle(this,FXSEL(SEL_COMMAND,ID_INSERT_STRING),(void*)string.text());
3586 return 1;
3587 }
3588
3589 // Next, try good old Latin-1
3590 if(getDNDData(FROM_CLIPBOARD,stringType,string)){
3591 FX88591Codec ascii;
3592 FXTRACE((100,"Paste ASCII\n"));
3593 string=ascii.mb2utf(string);
3594 #ifdef WIN32
3595 dosToUnix(string);
3596 #endif
3597 handle(this,FXSEL(SEL_COMMAND,ID_INSERT_STRING),(void*)string.text());
3598 return 1;
3599 }
3600 }
3601 else{
3602 getApp()->beep();
3603 }
3604 return 1;
3605 }
3606
3607
3608 // Paste selection
onCmdPasteMiddle(FXObject *,FXSelector,void *)3609 long FXText::onCmdPasteMiddle(FXObject*,FXSelector,void*){
3610 if(isEditable()){
3611 if(selstartpos==selendpos || cursorpos<=selstartpos || selendpos<=cursorpos){ // Avoid paste inside selection
3612 FXString string;
3613
3614 // First, try UTF-8
3615 if(getDNDData(FROM_SELECTION,utf8Type,string)){
3616 FXTRACE((100,"Paste UTF8\n"));
3617 handle(this,FXSEL(SEL_COMMAND,ID_INSERT_STRING),(void*)string.text());
3618 return 1;
3619 }
3620
3621 // Next, try UTF-16
3622 if(getDNDData(FROM_SELECTION,utf16Type,string)){
3623 FXUTF16LECodec unicode; // FIXME maybe other endianness for unix
3624 FXTRACE((100,"Paste UTF16\n"));
3625 handle(this,FXSEL(SEL_COMMAND,ID_INSERT_STRING),(void*)unicode.mb2utf(string).text());
3626 return 1;
3627 }
3628
3629 // Finally, try good old 8859-1
3630 if(getDNDData(FROM_SELECTION,stringType,string)){
3631 FX88591Codec ascii;
3632 FXTRACE((100,"Paste ASCII\n"));
3633 handle(this,FXSEL(SEL_COMMAND,ID_INSERT_STRING),(void*)ascii.mb2utf(string).text());
3634 return 1;
3635 }
3636 }
3637 }
3638 else{
3639 getApp()->beep();
3640 }
3641 return 1;
3642 }
3643
3644
3645 // Select character
onCmdSelectChar(FXObject *,FXSelector,void *)3646 long FXText::onCmdSelectChar(FXObject*,FXSelector,void*){
3647 setAnchorPos(cursorpos);
3648 extendSelection(inc(cursorpos),SELECT_CHARS,TRUE);
3649 return 1;
3650 }
3651
3652
3653 // Select Word
onCmdSelectWord(FXObject *,FXSelector,void *)3654 long FXText::onCmdSelectWord(FXObject*,FXSelector,void*){
3655 setAnchorPos(cursorpos);
3656 extendSelection(cursorpos,SELECT_WORDS,TRUE);
3657 return 1;
3658 }
3659
3660
3661 // Select Line
onCmdSelectLine(FXObject *,FXSelector,void *)3662 long FXText::onCmdSelectLine(FXObject*,FXSelector,void*){
3663 setAnchorPos(cursorpos);
3664 extendSelection(cursorpos,SELECT_LINES,TRUE);
3665 return 1;
3666 }
3667
3668
3669 // Select All
onCmdSelectAll(FXObject *,FXSelector,void *)3670 long FXText::onCmdSelectAll(FXObject*,FXSelector,void*){
3671 setAnchorPos(0);
3672 extendSelection(length,SELECT_CHARS,TRUE);
3673 return 1;
3674 }
3675
3676
3677 // Deselect All
onCmdDeselectAll(FXObject *,FXSelector,void *)3678 long FXText::onCmdDeselectAll(FXObject*,FXSelector,void*){
3679 killSelection(TRUE);
3680 return 1;
3681 }
3682
3683
3684 // Backspace character
onCmdBackspace(FXObject *,FXSelector,void *)3685 long FXText::onCmdBackspace(FXObject*,FXSelector,void*){
3686 if(isEditable() && 0<cursorpos){
3687 FXint pos=dec(cursorpos);
3688 removeText(pos,cursorpos-pos,TRUE);
3689 setCursorPos(cursorpos,TRUE);
3690 makePositionVisible(cursorpos);
3691 flags|=FLAG_CHANGED;
3692 modified=TRUE;
3693 }
3694 else{
3695 getApp()->beep();
3696 }
3697 return 1;
3698 }
3699
3700
3701 // Backspace word
onCmdBackspaceWord(FXObject *,FXSelector,void *)3702 long FXText::onCmdBackspaceWord(FXObject*,FXSelector,void*){
3703 if(isEditable()){
3704 FXint pos=leftWord(cursorpos);
3705 removeText(pos,cursorpos-pos,TRUE);
3706 setCursorPos(cursorpos,TRUE);
3707 makePositionVisible(cursorpos);
3708 flags|=FLAG_CHANGED;
3709 modified=TRUE;
3710 }
3711 else{
3712 getApp()->beep();
3713 }
3714 return 1;
3715 }
3716
3717
3718 // Backspace bol
onCmdBackspaceBol(FXObject *,FXSelector,void *)3719 long FXText::onCmdBackspaceBol(FXObject*,FXSelector,void*){
3720 if(isEditable()){
3721 FXint pos=rowStart(cursorpos);
3722 removeText(pos,cursorpos-pos,TRUE);
3723 setCursorPos(cursorpos,TRUE);
3724 makePositionVisible(cursorpos);
3725 flags|=FLAG_CHANGED;
3726 modified=TRUE;
3727 }
3728 else{
3729 getApp()->beep();
3730 }
3731 return 1;
3732 }
3733
3734
3735 // Delete character
onCmdDelete(FXObject *,FXSelector,void *)3736 long FXText::onCmdDelete(FXObject*,FXSelector,void*){
3737 if(isEditable() && cursorpos<length){
3738 FXint pos=inc(cursorpos);
3739 removeText(cursorpos,pos-cursorpos,TRUE);
3740 setCursorPos(cursorpos,TRUE);
3741 makePositionVisible(cursorpos);
3742 flags|=FLAG_CHANGED;
3743 modified=TRUE;
3744 }
3745 else{
3746 getApp()->beep();
3747 }
3748 return 1;
3749 }
3750
3751
3752 // Delete word
onCmdDeleteWord(FXObject *,FXSelector,void *)3753 long FXText::onCmdDeleteWord(FXObject*,FXSelector,void*){
3754 if(isEditable()){
3755 FXint pos=rightWord(cursorpos);
3756 removeText(cursorpos,pos-cursorpos,TRUE);
3757 setCursorPos(cursorpos,TRUE);
3758 makePositionVisible(cursorpos);
3759 flags|=FLAG_CHANGED;
3760 modified=TRUE;
3761 }
3762 else{
3763 getApp()->beep();
3764 }
3765 return 1;
3766 }
3767
3768
3769 // Delete to end of line
onCmdDeleteEol(FXObject *,FXSelector,void *)3770 long FXText::onCmdDeleteEol(FXObject*,FXSelector,void*){
3771 if(isEditable()){
3772 FXint pos=rowEnd(cursorpos);
3773 removeText(cursorpos,pos-cursorpos,TRUE);
3774 setCursorPos(cursorpos,TRUE);
3775 makePositionVisible(cursorpos);
3776 flags|=FLAG_CHANGED;
3777 modified=TRUE;
3778 }
3779 else{
3780 getApp()->beep();
3781 }
3782 return 1;
3783 }
3784
3785
3786 // Delete line
onCmdDeleteLine(FXObject *,FXSelector,void *)3787 long FXText::onCmdDeleteLine(FXObject*,FXSelector,void*){
3788 if(isEditable()){
3789 FXint beg=rowStart(cursorpos);
3790 FXint end=nextRow(cursorpos);
3791 removeText(beg,end-beg,TRUE);
3792 setCursorPos(cursorpos,TRUE);
3793 makePositionVisible(cursorpos);
3794 flags|=FLAG_CHANGED;
3795 modified=TRUE;
3796 }
3797 else{
3798 getApp()->beep();
3799 }
3800 return 1;
3801 }
3802
3803
3804 // Delete all text
onCmdDeleteAll(FXObject *,FXSelector,void *)3805 long FXText::onCmdDeleteAll(FXObject*,FXSelector,void*){
3806 if(isEditable()){
3807 removeText(0,length,TRUE);
3808 setCursorPos(0,TRUE);
3809 makePositionVisible(0);
3810 flags|=FLAG_CHANGED;
3811 modified=TRUE;
3812 }
3813 else{
3814 getApp()->beep();
3815 }
3816 return 1;
3817 }
3818
3819
3820 // Make selected text upper case
onCmdChangeCase(FXObject *,FXSelector sel,void *)3821 long FXText::onCmdChangeCase(FXObject*,FXSelector sel,void*){
3822 if(isEditable()){
3823 FXString text;
3824 FXint pos=selstartpos;
3825 FXint num=selendpos-selstartpos;
3826 extractText(text,pos,num);
3827 if(FXSELID(sel)==ID_UPPER_CASE){
3828 text.upper();
3829 }
3830 else{
3831 text.lower();
3832 }
3833 replaceText(pos,num,text,TRUE);
3834 setSelection(pos,text.length(),TRUE);
3835 setCursorPos(cursorpos,TRUE);
3836 makePositionVisible(cursorpos);
3837 flags|=FLAG_CHANGED;
3838 modified=TRUE;
3839 }
3840 else{
3841 getApp()->beep();
3842 }
3843 return 1;
3844 }
3845
3846
3847 // Shift text by certain amount
shiftText(FXint start,FXint end,FXint amount,FXbool notify)3848 FXint FXText::shiftText(FXint start,FXint end,FXint amount,FXbool notify){
3849 FXint white,p,len,size,c;
3850 FXchar *text;
3851 if(start<0) start=0;
3852 if(end>length) end=length;
3853 FXASSERT(0<tabcolumns);
3854 if(start<end){
3855 p=start;
3856 white=0;
3857 size=0;
3858 while(p<end){
3859 c=getByte(p++);
3860 if(c==' '){
3861 white++;
3862 }
3863 else if(c=='\t'){
3864 white+=(tabcolumns-white%tabcolumns);
3865 }
3866 else if(c=='\n'){
3867 size++; white=0;
3868 }
3869 else{
3870 white+=amount;
3871 if(white<0) white=0;
3872 if(!(options&TEXT_NO_TABS)){ size+=(white/tabcolumns+white%tabcolumns); } else { size+=white; }
3873 size++;
3874 while(p<end){
3875 c=getByte(p++);
3876 size++;
3877 if(c=='\n') break;
3878 }
3879 white=0;
3880 }
3881 }
3882 FXMALLOC(&text,FXchar,size);
3883 p=start;
3884 white=0;
3885 len=0;
3886 while(p<end){
3887 c=getByte(p++);
3888 if(c==' '){
3889 white++;
3890 }
3891 else if(c=='\t'){
3892 white+=(tabcolumns-white%tabcolumns);
3893 }
3894 else if(c=='\n'){
3895 text[len++]='\n'; white=0;
3896 }
3897 else{
3898 white+=amount;
3899 if(white<0) white=0;
3900 if(!(options&TEXT_NO_TABS)){ while(white>=tabcolumns){ text[len++]='\t'; white-=tabcolumns;} }
3901 while(white>0){ text[len++]=' '; white--; }
3902 text[len++]=c;
3903 while(p<end){
3904 c=getByte(p++);
3905 text[len++]=c;
3906 if(c=='\n') break;
3907 }
3908 white=0;
3909 }
3910 }
3911 FXASSERT(len<=size);
3912 replaceText(start,end-start,text,len,notify);
3913 FXFREE(&text);
3914 return len;
3915 }
3916 return 0;
3917 }
3918
3919
3920 // Shift selected lines left or right
onCmdShiftText(FXObject *,FXSelector sel,void *)3921 long FXText::onCmdShiftText(FXObject*,FXSelector sel,void*){
3922 if(isEditable()){
3923 FXint start,end,len,amount;
3924 amount=0;
3925 switch(FXSELID(sel)){
3926 case ID_SHIFT_LEFT: amount=-1; break;
3927 case ID_SHIFT_RIGHT: amount=1; break;
3928 case ID_SHIFT_TABLEFT: amount=-tabcolumns; break;
3929 case ID_SHIFT_TABRIGHT: amount=tabcolumns; break;
3930 }
3931 if(selstartpos<selendpos){
3932 FXASSERT(0<=selstartpos && selstartpos<=length);
3933 FXASSERT(0<=selendpos && selendpos<=length);
3934 start=lineStart(selstartpos);
3935 end=selendpos;
3936 if(0<end && getByte(end-1)!='\n') end=nextLine(end);
3937 }
3938 else{
3939 start=lineStart(cursorpos);
3940 end=lineEnd(cursorpos);
3941 if(end<length) end++;
3942 }
3943 len=shiftText(start,end,amount,TRUE);
3944 setAnchorPos(start);
3945 extendSelection(start+len,SELECT_CHARS,TRUE);
3946 setCursorPos(start,TRUE);
3947 flags|=FLAG_CHANGED;
3948 modified=TRUE;
3949 }
3950 else{
3951 getApp()->beep();
3952 }
3953 return 1;
3954 }
3955
3956
3957 // Goto matching character
onCmdGotoMatching(FXObject *,FXSelector,void *)3958 long FXText::onCmdGotoMatching(FXObject*,FXSelector,void*){
3959 if(0<cursorpos){
3960 FXchar ch=getByte(cursorpos-1);
3961 FXint pos=findMatching(cursorpos-1,0,length,ch,1);
3962 if(0<=pos){
3963 setCursorPos(pos+1);
3964 makePositionVisible(cursorpos);
3965 return 1;
3966 }
3967 }
3968 getApp()->beep();
3969 return 1;
3970 }
3971
3972
3973 // Select text till matching character
onCmdSelectMatching(FXObject *,FXSelector,void *)3974 long FXText::onCmdSelectMatching(FXObject*,FXSelector,void*){
3975 if(0<cursorpos){
3976 FXchar ch=getByte(cursorpos-1);
3977 FXint pos=findMatching(cursorpos-1,0,length,ch,1);
3978 if(0<=pos){
3979 if(pos>cursorpos){
3980 setAnchorPos(cursorpos-1);
3981 extendSelection(pos+1,SELECT_CHARS,TRUE);
3982 }
3983 else{
3984 setAnchorPos(pos);
3985 extendSelection(cursorpos,SELECT_CHARS,TRUE);
3986 }
3987 return 1;
3988 }
3989 }
3990 getApp()->beep();
3991 return 1;
3992 }
3993
3994
3995 static const FXchar righthand[]="}])>";
3996 static const FXchar lefthand[]="{[(<";
3997
3998
3999 // Select entire enclosing block
onCmdSelectBlock(FXObject *,FXSelector sel,void *)4000 long FXText::onCmdSelectBlock(FXObject*,FXSelector sel,void*){
4001 FXint beg,end,what,level=1;
4002 while(1){
4003 what=FXSELID(sel)-ID_SELECT_BRACE;
4004 beg=matchBackward(cursorpos-1,0,lefthand[what],righthand[what],level);
4005 end=matchForward(cursorpos,length,lefthand[what],righthand[what],level);
4006 if(0<=beg && beg<end){
4007 if(isPosSelected(beg) && isPosSelected(end+1)){ level++; continue; }
4008 setAnchorPos(beg);
4009 extendSelection(end+1,SELECT_CHARS,TRUE);
4010 return 1;
4011 }
4012 getApp()->beep();
4013 break;
4014 }
4015 return 1;
4016 }
4017
4018
4019 // Goto start of enclosing block
onCmdBlockBeg(FXObject *,FXSelector sel,void *)4020 long FXText::onCmdBlockBeg(FXObject*,FXSelector sel,void*){
4021 FXint what=FXSELID(sel)-ID_LEFT_BRACE;
4022 FXint beg=cursorpos-1;
4023 if(0<beg){
4024 if(getByte(beg)==lefthand[what]) beg--;
4025 FXint pos=matchBackward(beg,0,lefthand[what],righthand[what],1);
4026 if(0<=pos){
4027 setCursorPos(pos+1);
4028 makePositionVisible(cursorpos);
4029 return 1;
4030 }
4031 }
4032 getApp()->beep();
4033 return 1;
4034 }
4035
4036
4037 // Goto end of enclosing block
onCmdBlockEnd(FXObject *,FXSelector sel,void *)4038 long FXText::onCmdBlockEnd(FXObject*,FXSelector sel,void*){
4039 FXint what=FXSELID(sel)-ID_RIGHT_BRACE;
4040 FXint start=cursorpos;
4041 if(start<length){
4042 if(getByte(start)==righthand[what]) start++;
4043 FXint pos=matchForward(start,length,lefthand[what],righthand[what],1);
4044 if(0<=pos){
4045 setCursorPos(pos);
4046 makePositionVisible(cursorpos);
4047 return 1;
4048 }
4049 }
4050 getApp()->beep();
4051 return 1;
4052 }
4053
4054
4055 // Search for selected text
onCmdSearchSel(FXObject *,FXSelector sel,void *)4056 long FXText::onCmdSearchSel(FXObject*,FXSelector sel,void*){
4057 FXString string;
4058 FXint pos=cursorpos;
4059 FXint beg,end;
4060
4061 // First, try UTF-8
4062 if(getDNDData(FROM_SELECTION,utf8Type,string)){
4063 FXTRACE((100,"Search UTF8\n"));
4064 searchstring=string;
4065 }
4066
4067 // Next, try UTF-16
4068 else if(getDNDData(FROM_SELECTION,utf16Type,string)){
4069 FXTRACE((100,"Search UTF16\n"));
4070 FXUTF16LECodec unicode; // FIXME maybe other endianness for unix
4071 searchstring=unicode.mb2utf(string);
4072 }
4073
4074 // Finally, try good old 8859-1
4075 else if(getDNDData(FROM_SELECTION,stringType,string)){
4076 FXTRACE((100,"Search ASCII\n"));
4077 FX88591Codec ascii;
4078 searchstring=ascii.mb2utf(string);
4079 }
4080
4081 // No dice!
4082 else{
4083 goto x;
4084 }
4085
4086 // Search direction
4087 if(FXSELID(sel)==ID_SEARCH_FORW_SEL){
4088 if(isPosSelected(pos)) pos=selendpos;
4089 searchflags=SEARCH_EXACT|SEARCH_FORWARD;
4090 }
4091 else{
4092 if(isPosSelected(pos)) pos=selstartpos-1;
4093 searchflags=SEARCH_EXACT|SEARCH_BACKWARD;
4094 }
4095
4096 // Perform search
4097 if(findText(searchstring,&beg,&end,pos,searchflags|SEARCH_WRAP)){
4098 if(beg!=selstartpos || end!=selendpos){
4099 setAnchorPos(beg);
4100 extendSelection(end,SELECT_CHARS,TRUE);
4101 setCursorPos(end);
4102 makePositionVisible(beg);
4103 makePositionVisible(end);
4104 return 1;
4105 }
4106 }
4107
4108 // Beep
4109 x:getApp()->beep();
4110 return 1;
4111 }
4112
4113
4114 // Search for next occurence
onCmdSearchNext(FXObject *,FXSelector sel,void *)4115 long FXText::onCmdSearchNext(FXObject*,FXSelector sel,void*){
4116 if(!searchstring.empty()){
4117 FXint pos=cursorpos;
4118 FXint beg[10];
4119 FXint end[10];
4120 if(FXSELID(sel)==ID_SEARCH_FORW){
4121 if(isPosSelected(pos)) pos=selendpos;
4122 searchflags&=~SEARCH_BACKWARD;
4123 }
4124 else{
4125 if(isPosSelected(pos)) pos=selstartpos-1;
4126 searchflags|=SEARCH_BACKWARD;
4127 }
4128 if(findText(searchstring,beg,end,pos,searchflags|SEARCH_WRAP,10)){
4129 if(beg[0]!=selstartpos || end[0]!=selendpos){
4130 setAnchorPos(beg[0]);
4131 extendSelection(end[0],SELECT_CHARS,TRUE);
4132 setCursorPos(end[0]);
4133 makePositionVisible(beg[0]);
4134 makePositionVisible(end[0]);
4135 return 1;
4136 }
4137 }
4138 }
4139 getApp()->beep();
4140 return 1;
4141 }
4142
4143
4144 // Search text
onCmdSearch(FXObject *,FXSelector,void *)4145 long FXText::onCmdSearch(FXObject*,FXSelector,void*){
4146 FXGIFIcon icon(getApp(),searchicon);
4147 FXSearchDialog searchdialog(this,tr("Search"),&icon);
4148 FXint beg[10];
4149 FXint end[10];
4150 FXint pos;
4151 FXuint code;
4152 do{
4153 code=searchdialog.execute();
4154 if(code==FXSearchDialog::DONE) return 1;
4155 searchstring=searchdialog.getSearchText();
4156 searchflags=searchdialog.getSearchMode();
4157 pos=isPosSelected(cursorpos) ? (searchflags&SEARCH_BACKWARD) ? selstartpos-1 : selendpos : cursorpos;
4158 if(findText(searchstring,beg,end,pos,searchflags|SEARCH_WRAP,10)){
4159 setAnchorPos(beg[0]);
4160 extendSelection(end[0],SELECT_CHARS,TRUE);
4161 setCursorPos(end[0],TRUE);
4162 makePositionVisible(beg[0]);
4163 makePositionVisible(end[0]);
4164 }
4165 else{
4166 getApp()->beep();
4167 }
4168 }
4169 while(code==FXSearchDialog::SEARCH_NEXT);
4170 return 1;
4171 }
4172
4173
4174 // Replace text; we assume that findText has called squeezegap()!
onCmdReplace(FXObject *,FXSelector,void *)4175 long FXText::onCmdReplace(FXObject*,FXSelector,void*){
4176 FXGIFIcon icon(getApp(),searchicon);
4177 FXReplaceDialog replacedialog(this,tr("Replace"),&icon);
4178 FXint beg[10],end[10],fm,to,len,pos;
4179 FXuint searchflags,code;
4180 FXString searchstring;
4181 FXString replacestring;
4182 FXString replacevalue;
4183 do{
4184 code=replacedialog.execute();
4185 if(code==FXReplaceDialog::DONE) return 1;
4186 searchflags=replacedialog.getSearchMode();
4187 searchstring=replacedialog.getSearchText();
4188 replacestring=replacedialog.getReplaceText();
4189 replacevalue=FXString::null;
4190 fm=-1;
4191 to=-1;
4192 if(code==FXReplaceDialog::REPLACE_ALL){
4193 searchflags&=~SEARCH_BACKWARD;
4194 pos=0;
4195 while(findText(searchstring,beg,end,pos,searchflags,10)){
4196 if(0<=fm) replacevalue.append(&buffer[pos],beg[0]-pos);
4197 replacevalue.append(FXRex::substitute(buffer,length,beg,end,replacestring,10));
4198 if(fm<0) fm=beg[0];
4199 to=end[0];
4200 pos=end[0];
4201 if(beg[0]==end[0]) pos++;
4202 }
4203 }
4204 else{
4205 pos=isPosSelected(cursorpos) ? (searchflags&SEARCH_BACKWARD) ? selstartpos-1 : selendpos : cursorpos;
4206 if(findText(searchstring,beg,end,pos,searchflags|SEARCH_WRAP,10)){
4207 replacevalue=FXRex::substitute(buffer,length,beg,end,replacestring,10);
4208 fm=beg[0];
4209 to=end[0];
4210 }
4211 }
4212 if(0<=fm){
4213 len=replacevalue.length();
4214 replaceText(fm,to-fm,replacevalue.text(),len,TRUE);
4215 setCursorPos(fm+len,TRUE);
4216 makePositionVisible(getCursorPos());
4217 modified=TRUE;
4218 }
4219 else{
4220 getApp()->beep();
4221 }
4222 }
4223 while(code==FXReplaceDialog::REPLACE_NEXT);
4224 return 1;
4225 }
4226
4227
4228 // Goto selected line number
onCmdGotoSelected(FXObject *,FXSelector,void *)4229 long FXText::onCmdGotoSelected(FXObject*,FXSelector,void*){
4230 FXString string;
4231 if(getDNDData(FROM_SELECTION,stringType,string)){
4232 FXint s=string.find_first_of("0123456789");
4233 if(0<=s){
4234 FXint row=0;
4235 while(Ascii::isDigit(string[s])){
4236 row=row*10+Ascii::digitValue(string[s]);
4237 s++;
4238 }
4239 if(1<=row){
4240 setCursorRow(row-1,TRUE);
4241 makePositionVisible(cursorpos);
4242 return 1;
4243 }
4244 }
4245 }
4246 getApp()->beep();
4247 return 1;
4248 }
4249
4250
4251 // Goto line number
onCmdGotoLine(FXObject *,FXSelector,void *)4252 long FXText::onCmdGotoLine(FXObject*,FXSelector,void*){
4253 FXGIFIcon icon(getApp(),gotoicon);
4254 FXint row=cursorrow+1;
4255 if(FXInputDialog::getInteger(row,this,tr("Goto Line"),tr("&Goto line number:"),&icon,1,2147483647)){
4256 update();
4257 setCursorRow(row-1,TRUE);
4258 makePositionVisible(cursorpos);
4259 }
4260 return 1;
4261 }
4262
4263
4264 /*******************************************************************************/
4265
4266 // Editable toggle
onCmdToggleEditable(FXObject *,FXSelector,void *)4267 long FXText::onCmdToggleEditable(FXObject*,FXSelector,void*){
4268 setEditable(!isEditable());
4269 return 1;
4270 }
4271
4272
4273 // Update editable toggle
onUpdToggleEditable(FXObject * sender,FXSelector,void *)4274 long FXText::onUpdToggleEditable(FXObject* sender,FXSelector,void*){
4275 sender->handle(this,isEditable()?FXSEL(SEL_COMMAND,ID_CHECK):FXSEL(SEL_COMMAND,ID_UNCHECK),NULL);
4276 sender->handle(this,FXSEL(SEL_COMMAND,ID_SHOW),NULL);
4277 sender->handle(this,FXSEL(SEL_COMMAND,ID_ENABLE),NULL);
4278 return 1;
4279 }
4280
4281
4282 // Overstrike toggle
onCmdToggleOverstrike(FXObject *,FXSelector,void *)4283 long FXText::onCmdToggleOverstrike(FXObject*,FXSelector,void*){
4284 setOverstrike(!isOverstrike());
4285 return 1;
4286 }
4287
4288
4289 // Update overstrike toggle
onUpdToggleOverstrike(FXObject * sender,FXSelector,void *)4290 long FXText::onUpdToggleOverstrike(FXObject* sender,FXSelector,void*){
4291 sender->handle(this,isOverstrike()?FXSEL(SEL_COMMAND,ID_CHECK):FXSEL(SEL_COMMAND,ID_UNCHECK),NULL);
4292 sender->handle(this,FXSEL(SEL_COMMAND,ID_SHOW),NULL);
4293 sender->handle(this,FXSEL(SEL_COMMAND,ID_ENABLE),NULL);
4294 return 1;
4295 }
4296
4297
4298 // Move cursor to indicated row
onCmdCursorRow(FXObject * sender,FXSelector,void *)4299 long FXText::onCmdCursorRow(FXObject* sender,FXSelector,void*){
4300 FXint row=cursorrow+1;
4301 sender->handle(this,FXSEL(SEL_COMMAND,ID_GETINTVALUE),(void*)&row);
4302 setCursorRow(row-1,TRUE);
4303 makePositionVisible(cursorpos);
4304 return 1;
4305 }
4306
4307
4308 // Being asked about current row number
onUpdCursorRow(FXObject * sender,FXSelector,void *)4309 long FXText::onUpdCursorRow(FXObject* sender,FXSelector,void*){
4310 FXint row=cursorrow+1;
4311 sender->handle(this,FXSEL(SEL_COMMAND,ID_SETINTVALUE),(void*)&row);
4312 return 1;
4313 }
4314
4315
4316 // Move cursor to indicated column
onCmdCursorColumn(FXObject * sender,FXSelector,void *)4317 long FXText::onCmdCursorColumn(FXObject* sender,FXSelector,void*){
4318 FXint col=cursorcol;
4319 sender->handle(this,FXSEL(SEL_COMMAND,ID_GETINTVALUE),(void*)&col);
4320 setCursorColumn(col,TRUE);
4321 makePositionVisible(cursorpos);
4322 return 1;
4323 }
4324
4325
4326 // Being asked about current column
onUpdCursorColumn(FXObject * sender,FXSelector,void *)4327 long FXText::onUpdCursorColumn(FXObject* sender,FXSelector,void*){
4328 sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_SETINTVALUE),(void*)&cursorcol);
4329 return 1;
4330 }
4331
4332
4333 // Update somebody who works on the selection
onUpdHaveSelection(FXObject * sender,FXSelector,void *)4334 long FXText::onUpdHaveSelection(FXObject* sender,FXSelector,void*){
4335 sender->handle(this,(selstartpos<selendpos)?FXSEL(SEL_COMMAND,ID_ENABLE):FXSEL(SEL_COMMAND,ID_DISABLE),NULL);
4336 return 1;
4337 }
4338
4339
4340 // Update somebody who works on the selection
onUpdSelectAll(FXObject * sender,FXSelector,void *)4341 long FXText::onUpdSelectAll(FXObject* sender,FXSelector,void*){
4342 sender->handle(this,(length==0)?FXSEL(SEL_COMMAND,ID_DISABLE):FXSEL(SEL_COMMAND,ID_ENABLE),NULL);
4343 return 1;
4344 }
4345
4346
4347 /*******************************************************************************/
4348
4349 // FIXME
4350 // Runs should preferably be whole combining sequence
4351 // Deal with non-rectangular selections.
4352
4353 // Draw fragment of text in given style
drawBufferText(FXDCWindow & dc,FXint x,FXint y,FXint,FXint,FXint pos,FXint n,FXuint style) const4354 void FXText::drawBufferText(FXDCWindow& dc,FXint x,FXint y,FXint,FXint,FXint pos,FXint n,FXuint style) const {
4355 register FXuint index=(style&STYLE_MASK);
4356 register FXuint usedstyle=style; // Style flags from style buffer
4357 register FXColor color;
4358 FXchar str[2];
4359 color=0;
4360 if(hilitestyles && index){ // Get colors from style table
4361 usedstyle=hilitestyles[index-1].style; // Style flags now from style table
4362 if(style&STYLE_SELECTED) color=hilitestyles[index-1].selectForeColor;
4363 else if(style&STYLE_HILITE) color=hilitestyles[index-1].hiliteForeColor;
4364 if(color==0) color=hilitestyles[index-1].normalForeColor; // Fall back on normal foreground color
4365 }
4366 if(color==0){ // Fall back to default style
4367 if(style&STYLE_SELECTED) color=seltextColor;
4368 else if(style&STYLE_HILITE) color=hilitetextColor;
4369 if(color==0) color=textColor; // Fall back to normal text color
4370 }
4371 dc.setForeground(color);
4372 if(style&STYLE_CONTROL){
4373 y+=font->getFontAscent();
4374 str[0]='^';
4375 while(pos<gapstart && 0<n){
4376 str[1]=buffer[pos]|0x40;
4377 dc.drawText(x,y,str,2);
4378 if(usedstyle&STYLE_BOLD) dc.drawText(x+1,y,str,2);
4379 x+=font->getTextWidth(str,2);
4380 pos++;
4381 n--;
4382 }
4383 while(0<n){
4384 str[1]=buffer[pos-gapstart+gapend]|0x40;
4385 dc.drawText(x,y,str,2);
4386 if(usedstyle&STYLE_BOLD) dc.drawText(x+1,y,str,2);
4387 x+=font->getTextWidth(str,2);
4388 pos++;
4389 n--;
4390 }
4391 }
4392 else{
4393 y+=font->getFontAscent();
4394 if(pos+n<=gapstart){
4395 dc.drawText(x,y,&buffer[pos],n);
4396 if(usedstyle&STYLE_BOLD) dc.drawText(x+1,y,&buffer[pos],n);
4397 }
4398 else if(pos>=gapstart){
4399 dc.drawText(x,y,&buffer[pos-gapstart+gapend],n);
4400 if(usedstyle&STYLE_BOLD) dc.drawText(x+1,y,&buffer[pos-gapstart+gapend],n);
4401 }
4402 else{
4403 dc.drawText(x,y,&buffer[pos],gapstart-pos);
4404 if(usedstyle&STYLE_BOLD) dc.drawText(x+1,y,&buffer[pos],gapstart-pos);
4405 x+=font->getTextWidth(&buffer[pos],gapstart-pos);
4406 dc.drawText(x,y,&buffer[gapend],pos+n-gapstart);
4407 if(usedstyle&STYLE_BOLD) dc.drawText(x+1,y,&buffer[gapend],pos+n-gapstart);
4408 }
4409 }
4410 }
4411
4412
4413 // Fill fragment of background in given style
fillBufferRect(FXDCWindow & dc,FXint x,FXint y,FXint w,FXint h,FXuint style) const4414 void FXText::fillBufferRect(FXDCWindow& dc,FXint x,FXint y,FXint w,FXint h,FXuint style) const {
4415 register FXuint index=(style&STYLE_MASK);
4416 register FXuint usedstyle=style; // Style flags from style buffer
4417 register FXColor bgcolor,fgcolor;
4418 bgcolor=fgcolor=0;
4419 if(hilitestyles && index){ // Get colors from style table
4420 usedstyle=hilitestyles[index-1].style; // Style flags now from style table
4421 if(style&STYLE_SELECTED){
4422 bgcolor=hilitestyles[index-1].selectBackColor;
4423 fgcolor=hilitestyles[index-1].selectForeColor;
4424 }
4425 else if(style&STYLE_HILITE){
4426 bgcolor=hilitestyles[index-1].hiliteBackColor;
4427 fgcolor=hilitestyles[index-1].hiliteForeColor;
4428 }
4429 else if(style&STYLE_ACTIVE){
4430 bgcolor=hilitestyles[index-1].activeBackColor;
4431 }
4432 else{
4433 bgcolor=hilitestyles[index-1].normalBackColor;
4434 }
4435 if(fgcolor==0){ // Fall back to normal foreground color
4436 fgcolor=hilitestyles[index-1].normalForeColor;
4437 }
4438 }
4439 if(bgcolor==0){ // Fall back to default background colors
4440 if(style&STYLE_SELECTED) bgcolor=selbackColor;
4441 else if(style&STYLE_HILITE) bgcolor=hilitebackColor;
4442 else if(style&STYLE_ACTIVE) bgcolor=activebackColor;
4443 else bgcolor=backColor;
4444 }
4445 if(fgcolor==0){ // Fall back to default foreground colors
4446 if(style&STYLE_SELECTED) fgcolor=seltextColor;
4447 else if(style&STYLE_HILITE) fgcolor=hilitetextColor;
4448 if(fgcolor==0) fgcolor=textColor; // Fall back to text color
4449 }
4450 dc.setForeground(bgcolor);
4451 dc.fillRectangle(x,y,w,h);
4452 if(usedstyle&STYLE_UNDERLINE){
4453 dc.setForeground(fgcolor);
4454 dc.fillRectangle(x,y+font->getFontAscent()+1,w,1);
4455 }
4456 if(usedstyle&STYLE_STRIKEOUT){
4457 dc.setForeground(fgcolor);
4458 dc.fillRectangle(x,y+font->getFontAscent()/2,w,1);
4459 }
4460 }
4461
4462
4463 // Obtain text style at position pos; note pos may be outside of text
4464 // to allow for rectangular selections!
style(FXint row,FXint,FXint end,FXint pos) const4465 FXuint FXText::style(FXint row,FXint,FXint end,FXint pos) const {
4466 register FXuint s=0;
4467 register FXchar ch;
4468
4469 // Selected part of text
4470 if(selstartpos<=pos && pos<selendpos) s|=STYLE_SELECTED;
4471
4472 // Highlighted part of text
4473 if(hilitestartpos<=pos && pos<hiliteendpos) s|=STYLE_HILITE;
4474
4475 // Current active line
4476 if((row==cursorrow)&&(options&TEXT_SHOWACTIVE)) s|=STYLE_ACTIVE;
4477
4478 // Blank part of line
4479 if(pos>=end) return s;
4480
4481 // Special style for control characters
4482 ch=getByte(pos);
4483
4484 // Get value from style buffer
4485 if(sbuffer) s|=getStyle(pos);
4486
4487 // Tabs are just fill
4488 if(ch == '\t') return s;
4489
4490 // Spaces are just fill
4491 if(ch == ' ') return s;
4492
4493 // Newlines are just fill
4494 if(ch == '\n') return s;
4495
4496 // Get special style for control codes
4497 if((FXuchar)ch < ' ') return s|STYLE_CONTROL|STYLE_TEXT;
4498
4499 return s|STYLE_TEXT;
4500 }
4501
4502
4503 // Draw partial text line with correct style
drawTextRow(FXDCWindow & dc,FXint line,FXint left,FXint right) const4504 void FXText::drawTextRow(FXDCWindow& dc,FXint line,FXint left,FXint right) const {
4505 register FXint x,y,w,h,linebeg,lineend,truelineend,cw,sp,ep,row,edge;
4506 register FXuint curstyle,newstyle;
4507 linebeg=visrows[line];
4508 lineend=truelineend=visrows[line+1];
4509 if(linebeg<lineend && Ascii::isSpace(getByte(lineend-1))) lineend--; // Back off last space
4510 x=0;
4511 w=0;
4512 h=font->getFontHeight();
4513 y=pos_y+margintop+(toprow+line)*h;
4514 edge=pos_x+marginleft+barwidth;
4515 row=toprow+line;
4516
4517 // Scan ahead till until we hit the end or the left edge
4518 for(sp=linebeg; sp<lineend; sp+=getCharLen(sp)){
4519 cw=charWidth(getChar(sp),x);
4520 if(x+edge+cw>=left) break;
4521 x+=cw;
4522 }
4523
4524 // First style to display
4525 curstyle=style(row,linebeg,lineend,sp);
4526
4527 // Draw until we hit the end or the right edge
4528 for(ep=sp; ep<lineend; ep+=getCharLen(ep)){
4529 newstyle=style(row,linebeg,truelineend,ep);
4530 if(newstyle!=curstyle){
4531 fillBufferRect(dc,edge+x,y,w,h,curstyle);
4532 if(curstyle&STYLE_TEXT) drawBufferText(dc,edge+x,y,w,h,sp,ep-sp,curstyle);
4533 curstyle=newstyle;
4534 sp=ep;
4535 x+=w;
4536 w=0;
4537 }
4538 cw=charWidth(getChar(ep),x+w);
4539 if(x+edge+w>=right) break;
4540 w+=cw;
4541 }
4542
4543 // Draw unfinished fragment
4544 fillBufferRect(dc,edge+x,y,w,h,curstyle);
4545 if(curstyle&STYLE_TEXT) drawBufferText(dc,edge+x,y,w,h,sp,ep-sp,curstyle);
4546 x+=w;
4547
4548 // Fill any left-overs outside of text
4549 if(x+edge<right){
4550 curstyle=style(row,linebeg,truelineend,ep);
4551 fillBufferRect(dc,edge+x,y,right-edge-x,h,curstyle);
4552 }
4553 }
4554
4555
4556 // Draw the cursor
drawCursor(FXuint state)4557 void FXText::drawCursor(FXuint state){
4558 register FXint xx,yt,yb,xlo,xhi,fh;
4559 if((state^flags)&FLAG_CARET){
4560 if(xid){
4561 FXASSERT(0<=cursorpos && cursorpos<=length);
4562 FXASSERT(0<=cursorrow && cursorrow<=nrows);
4563 if(toprow<=cursorrow && cursorrow<toprow+nvisrows){
4564 xx=pos_x+marginleft+barwidth+lineWidth(cursorstart,cursorpos-cursorstart)-1;
4565 if(barwidth<=xx+3 && xx-2<viewport_w){
4566 FXDCWindow dc(this);
4567 fh=font->getFontHeight();
4568 yt=pos_y+margintop+cursorrow*fh;
4569 yb=yt+fh-1;
4570
4571 // Cursor can overhang margins but not line number bar
4572 dc.setClipRectangle(barwidth,0,viewport_w-barwidth,viewport_h);
4573
4574 // Draw I beam
4575 if(state&FLAG_CARET){
4576
4577 // Draw I-beam
4578 dc.setForeground(cursorColor);
4579 dc.fillRectangle(xx,yt,2,yb-yt);
4580 dc.fillRectangle(xx-2,yt,6,1);
4581 dc.fillRectangle(xx-2,yb,6,1);
4582 }
4583
4584 // Erase I-beam
4585 else{
4586
4587 // Erase I-beam, plus the text immediately surrounding it
4588 dc.setForeground(backColor);
4589 dc.fillRectangle(xx-2,yt,6,yb-yt+1);
4590
4591 // Clip the text to the margins AND the rectangle that was
4592 // just erased. We don't want to overdraw any existing
4593 // characters, because of ClearType.
4594 xlo=FXMAX(xx-2,marginleft+barwidth);
4595 xhi=FXMIN(xx+4,viewport_w-marginright);
4596 dc.setClipRectangle(xlo,margintop,xhi-xlo,viewport_h-margintop-marginbottom);
4597
4598 // Restore text
4599 dc.setFont(font);
4600 drawTextRow(dc,cursorrow-toprow,xx-3,xx+4);
4601 }
4602 }
4603 }
4604 }
4605 flags^=FLAG_CARET;
4606 }
4607 }
4608
4609
4610 // Erase cursor overhang outside of margins
eraseCursorOverhang()4611 void FXText::eraseCursorOverhang(){
4612 register FXint xx,yt,yb,fh;
4613 FXASSERT(0<=cursorpos && cursorpos<=length);
4614 FXASSERT(0<=cursorrow && cursorrow<=nrows);
4615 if(toprow<=cursorrow && cursorrow<toprow+nvisrows){
4616 xx=pos_x+marginleft+barwidth+lineWidth(cursorstart,cursorpos-cursorstart)-1;
4617 if(barwidth<=xx+3 && xx-2<viewport_w){
4618 FXDCWindow dc(this);
4619 fh=font->getFontHeight();
4620 yt=pos_y+margintop+cursorrow*fh;
4621 yb=yt+fh-1;
4622 dc.setClipRectangle(barwidth,0,viewport_w-barwidth,viewport_h);
4623 if(xx-2<=marginleft+barwidth && barwidth<=xx+3){
4624 dc.setForeground(backColor);
4625 dc.fillRectangle(barwidth,yt,marginleft,fh);
4626 }
4627 if(viewport_w-marginright<=xx+3 && xx-2<=viewport_w){
4628 dc.setForeground(backColor);
4629 dc.fillRectangle(viewport_w-marginright,yt,marginright,fh);
4630 }
4631 if(yt<=margintop && 0<=yb){
4632 dc.setForeground(backColor);
4633 dc.fillRectangle(xx-2,0,5,margintop);
4634 }
4635 if(viewport_h-marginbottom<=yb && yt<viewport_h){
4636 dc.setForeground(backColor);
4637 dc.fillRectangle(xx-2,viewport_h-marginbottom,5,marginbottom);
4638 }
4639 }
4640 }
4641 }
4642
4643
4644 // Repaint lines of text
drawContents(FXDCWindow & dc,FXint x,FXint y,FXint w,FXint h) const4645 void FXText::drawContents(FXDCWindow& dc,FXint x,FXint y,FXint w,FXint h) const {
4646 register FXint hh=font->getFontHeight();
4647 register FXint yy=pos_y+margintop+toprow*hh;
4648 register FXint tl=(y-yy)/hh;
4649 register FXint bl=(y+h-yy)/hh;
4650 register FXint ln;
4651 if(tl<0) tl=0;
4652 if(bl>=nvisrows) bl=nvisrows-1;
4653 for(ln=tl; ln<=bl; ln++){
4654 drawTextRow(dc,ln,x,x+w);
4655 }
4656 }
4657
4658
4659 // Repaint line numbers
drawNumbers(FXDCWindow & dc,FXint x,FXint y,FXint w,FXint h) const4660 void FXText::drawNumbers(FXDCWindow& dc,FXint x,FXint y,FXint w,FXint h) const {
4661 register FXint hh=font->getFontHeight();
4662 register FXint yy=pos_y+margintop+toprow*hh;
4663 register FXint tl=(y-yy)/hh;
4664 register FXint bl=(y+h-yy)/hh;
4665 register FXint ln,n,tw;
4666 FXchar lineno[20];
4667 if(tl<0) tl=0;
4668 if(bl>=nvisrows) bl=nvisrows-1;
4669 dc.setForeground(barColor);
4670 dc.fillRectangle(x,y,w,h);
4671 dc.setForeground(numberColor);
4672 for(ln=tl; ln<=bl; ln++){
4673 n=sprintf(lineno,"%d",toprow+ln+1);
4674 tw=font->getTextWidth(lineno,n);
4675 dc.drawText(barwidth-tw,yy+ln*hh+font->getFontAscent(),lineno,n);
4676 }
4677 }
4678
4679
4680 // Repaint text range
updateRange(FXint beg,FXint end) const4681 void FXText::updateRange(FXint beg,FXint end) const {
4682 register FXint tl,bl,fc,lc,ty,by,lx,rx,t;
4683 if(beg>end){t=beg;beg=end;end=t;}
4684 if(beg<visrows[nvisrows] && visrows[0]<end && beg<end){
4685 if(beg<visrows[0]) beg=visrows[0];
4686 if(end>visrows[nvisrows]) end=visrows[nvisrows];
4687 tl=posToLine(beg,0);
4688 bl=posToLine(end,tl);
4689 if(tl==bl){
4690 fc=beg-visrows[tl];
4691 lc=end-visrows[tl];
4692 ty=pos_y+margintop+(toprow+tl)*font->getFontHeight();
4693 by=ty+font->getFontHeight();
4694 lx=pos_x+marginleft+barwidth+lineWidth(visrows[tl],fc);
4695 if(end<=(visrows[tl+1]-1)) rx=pos_x+marginleft+barwidth+lineWidth(visrows[tl],lc); else rx=width;
4696 }
4697 else{
4698 ty=pos_y+margintop+(toprow+tl)*font->getFontHeight();
4699 by=pos_y+margintop+(toprow+bl+1)*font->getFontHeight();
4700 lx=barwidth;
4701 rx=width;
4702 }
4703 update(lx,ty,rx-lx,by-ty);
4704 }
4705 }
4706
4707
4708 // Draw item list
onPaint(FXObject *,FXSelector,void * ptr)4709 long FXText::onPaint(FXObject*,FXSelector,void* ptr){
4710 FXEvent* event=(FXEvent*)ptr;
4711 FXDCWindow dc(this,event);
4712 dc.setFont(font);
4713 //dc.setForeground(FXRGB(255,0,0));
4714 //dc.fillRectangle(event->rect.x,event->rect.y,event->rect.w,event->rect.h);
4715
4716 // Paint top margin
4717 if(event->rect.y<=margintop){
4718 dc.setForeground(backColor);
4719 dc.fillRectangle(barwidth,0,viewport_w-barwidth,margintop);
4720 }
4721
4722 // Paint bottom margin
4723 if(event->rect.y+event->rect.h>=viewport_h-marginbottom){
4724 dc.setForeground(backColor);
4725 dc.fillRectangle(barwidth,viewport_h-marginbottom,viewport_w-barwidth,marginbottom);
4726 }
4727
4728 // Paint left margin
4729 if(event->rect.x<barwidth+marginleft){
4730 dc.setForeground(backColor);
4731 dc.fillRectangle(barwidth,margintop,marginleft,viewport_h-margintop-marginbottom);
4732 }
4733
4734 // Paint right margin
4735 if(event->rect.x+event->rect.w>=viewport_w-marginright){
4736 dc.setForeground(backColor);
4737 dc.fillRectangle(viewport_w-marginright,margintop,marginright,viewport_h-margintop-marginbottom);
4738 }
4739
4740 // Paint line numbers
4741 if(event->rect.x<barwidth){
4742 dc.setClipRectangle(0,0,barwidth,height);
4743 drawNumbers(dc,event->rect.x,event->rect.y,event->rect.w,event->rect.h);
4744 }
4745
4746 // Paint text
4747 dc.setClipRectangle(marginleft+barwidth,margintop,viewport_w-marginright-marginleft-barwidth,viewport_h-margintop-marginbottom);
4748 drawContents(dc,event->rect.x,event->rect.y,event->rect.w,event->rect.h);
4749
4750 // FIXME make sure its drawn if we have focus
4751 drawCursor(flags);
4752
4753 return 1;
4754 }
4755
4756
4757 /*******************************************************************************/
4758
4759
4760 // Move the cursor
setCursorPos(FXint pos,FXbool notify)4761 void FXText::setCursorPos(FXint pos,FXbool notify){
4762 register FXint cursorstartold,cursorendold;
4763 pos=validPos(pos);
4764 if(cursorpos!=pos){
4765 drawCursor(0);
4766 if(pos<cursorstart || cursorend<=pos){ // Move to other line?
4767 cursorstartold=cursorstart;
4768 cursorendold=cursorend;
4769 cursorstart=rowStart(pos);
4770 cursorend=nextRow(cursorstart);
4771 if(cursorstart<cursorstartold){
4772 cursorrow=cursorrow-countRows(cursorstart,cursorstartold);
4773 }
4774 else{
4775 cursorrow=cursorrow+countRows(cursorstartold,cursorstart);
4776 }
4777 if(options&TEXT_SHOWACTIVE){
4778 updateRange(cursorstartold,cursorendold);
4779 updateRange(cursorstart,cursorend);
4780 }
4781 }
4782 cursorcol=indentFromPos(cursorstart,pos);
4783 cursorpos=pos;
4784 drawCursor(FLAG_CARET);
4785 prefcol=-1;
4786 if(target && notify){
4787 target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)cursorpos);
4788 }
4789 }
4790 }
4791
4792
4793 // Set cursor row
setCursorRow(FXint row,FXbool notify)4794 void FXText::setCursorRow(FXint row,FXbool notify){
4795 register FXint col,newrow,newpos;
4796 if(row!=cursorrow){
4797 if(row<0) row=0;
4798 if(row>=nrows) row=nrows-1;
4799 col=(0<=prefcol) ? prefcol : cursorcol;
4800 if(row>cursorrow){
4801 newrow=nextRow(cursorpos,row-cursorrow);
4802 }
4803 else{
4804 newrow=prevRow(cursorpos,cursorrow-row);
4805 }
4806 newpos=posFromIndent(newrow,col);
4807 setCursorPos(newpos,notify);
4808 prefcol=col;
4809 }
4810 }
4811
4812
4813 // Set cursor column
setCursorColumn(FXint col,FXbool notify)4814 void FXText::setCursorColumn(FXint col,FXbool notify){
4815 register FXint newpos;
4816 if(cursorcol!=col){
4817 newpos=posFromIndent(cursorstart,col);
4818 setCursorPos(newpos,notify);
4819 }
4820 }
4821
4822
4823 // Set anchor position
setAnchorPos(FXint pos)4824 void FXText::setAnchorPos(FXint pos){
4825 anchorpos=validPos(pos);
4826 }
4827
4828
4829 // Select all text
selectAll(FXbool notify)4830 FXbool FXText::selectAll(FXbool notify){
4831 return setSelection(0,length,notify);
4832 }
4833
4834
4835 // Extend selection
extendSelection(FXint pos,FXTextSelectionMode select,FXbool notify)4836 FXbool FXText::extendSelection(FXint pos,FXTextSelectionMode select,FXbool notify){
4837 register FXint sp,ep;
4838
4839 // Validate position
4840 pos=validPos(pos);
4841
4842 // Did position change?
4843 switch(select){
4844
4845 // Selecting words
4846 case SELECT_WORDS:
4847 if(pos<=anchorpos){
4848 sp=wordStart(pos);
4849 ep=wordEnd(anchorpos);
4850 }
4851 else{
4852 sp=wordStart(anchorpos);
4853 ep=wordEnd(pos);
4854 }
4855 break;
4856
4857 // Selecting lines
4858 case SELECT_LINES:
4859 if(pos<=anchorpos){
4860 sp=rowStart(pos);
4861 ep=nextRow(anchorpos);
4862 }
4863 else{
4864 sp=rowStart(anchorpos);
4865 ep=nextRow(pos);
4866 }
4867 break;
4868
4869 // Selecting characters
4870 default:
4871 if(pos<=anchorpos){
4872 sp=pos;
4873 ep=anchorpos;
4874 }
4875 else{
4876 sp=anchorpos;
4877 ep=pos;
4878 }
4879 break;
4880 }
4881
4882 // Select the new range
4883 return setSelection(sp,ep-sp,notify);
4884 }
4885
4886
4887 // Set selection
setSelection(FXint pos,FXint len,FXbool notify)4888 FXbool FXText::setSelection(FXint pos,FXint len,FXbool notify){
4889 register FXint ep,sp;
4890 FXDragType types[4];
4891 FXint what[2];
4892
4893 // Validate positions
4894 sp=validPos(pos);
4895 ep=validPos(pos+len);
4896
4897 // Something changed?
4898 if(selstartpos!=sp || selendpos!=ep){
4899
4900 // Release selection
4901 if(sp==ep){
4902 if(notify && target){
4903 what[0]=selstartpos;
4904 what[1]=selendpos-selstartpos;
4905 target->tryHandle(this,FXSEL(SEL_DESELECTED,message),(void*)what);
4906 }
4907 if(hasSelection()) releaseSelection();
4908 }
4909
4910 // Minimally update
4911 if(ep<=selstartpos || selendpos<=sp){
4912 updateRange(selstartpos,selendpos);
4913 updateRange(sp,ep);
4914 }
4915 else{
4916 updateRange(sp,selstartpos);
4917 updateRange(selendpos,ep);
4918 }
4919
4920 selstartpos=sp;
4921 selendpos=ep;
4922
4923 // Acquire selection
4924 if(sp!=ep){
4925 types[0]=stringType;
4926 types[1]=textType;
4927 types[2]=utf8Type;
4928 types[3]=utf16Type;
4929 if(!hasSelection()){
4930 acquireSelection(types,4);
4931 }
4932 if(notify && target){
4933 what[0]=selstartpos;
4934 what[1]=selendpos-selstartpos;
4935 target->tryHandle(this,FXSEL(SEL_SELECTED,message),(void*)what);
4936 }
4937 }
4938 return TRUE;
4939 }
4940 return FALSE;
4941 }
4942
4943
4944 // Kill the selection
killSelection(FXbool notify)4945 FXbool FXText::killSelection(FXbool notify){
4946 FXint what[2];
4947 if(selstartpos<selendpos){
4948 if(notify && target){
4949 what[0]=selstartpos;
4950 what[1]=selendpos-selstartpos;
4951 target->tryHandle(this,FXSEL(SEL_DESELECTED,message),(void*)what);
4952 }
4953 if(hasSelection()) releaseSelection();
4954 updateRange(selstartpos,selendpos);
4955 selstartpos=0;
4956 selendpos=0;
4957 return TRUE;
4958 }
4959 return FALSE;
4960 }
4961
4962
4963 // Set highlight
setHighlight(FXint pos,FXint len)4964 FXbool FXText::setHighlight(FXint pos,FXint len){
4965 register FXint hs,he;
4966
4967 // Validate positions
4968 hs=validPos(pos);
4969 he=validPos(pos+len);
4970
4971 // Anything changed?
4972 if(hs!=hilitestartpos || he!=hiliteendpos){
4973
4974 // Minimally update
4975 if(he<=hilitestartpos || hiliteendpos<=hs){
4976 updateRange(hilitestartpos,hiliteendpos);
4977 updateRange(hs,he);
4978 }
4979 else{
4980 updateRange(hs,hilitestartpos);
4981 updateRange(hiliteendpos,he);
4982 }
4983
4984 // Keep new range
4985 hilitestartpos=hs;
4986 hiliteendpos=he;
4987
4988 return TRUE;
4989 }
4990 return FALSE;
4991 }
4992
4993
4994 // Unhighlight the text
killHighlight()4995 FXbool FXText::killHighlight(){
4996 if(hilitestartpos<hiliteendpos){
4997 updateRange(hilitestartpos,hiliteendpos);
4998 hilitestartpos=0;
4999 hiliteendpos=0;
5000 return TRUE;
5001 }
5002 return FALSE;
5003 }
5004
5005
5006 /*******************************************************************************/
5007
5008
5009 // Change top margin
setMarginTop(FXint mt)5010 void FXText::setMarginTop(FXint mt){
5011 if(margintop!=mt){
5012 margintop=mt;
5013 recalc();
5014 update();
5015 }
5016 }
5017
5018 // Change bottom margin
setMarginBottom(FXint mb)5019 void FXText::setMarginBottom(FXint mb){
5020 if(marginbottom!=mb){
5021 marginbottom=mb;
5022 recalc();
5023 update();
5024 }
5025 }
5026
5027
5028 // Change left margin
setMarginLeft(FXint ml)5029 void FXText::setMarginLeft(FXint ml){
5030 if(marginleft!=ml){
5031 marginleft=ml;
5032 recalc();
5033 update();
5034 }
5035 }
5036
5037
5038 // Change right margin
setMarginRight(FXint mr)5039 void FXText::setMarginRight(FXint mr){
5040 if(marginright!=mr){
5041 marginright=mr;
5042 recalc();
5043 update();
5044 }
5045 }
5046
5047
5048 // Change the font
setFont(FXFont * fnt)5049 void FXText::setFont(FXFont* fnt){
5050 if(!fnt){ fxerror("%s::setFont: NULL font specified.\n",getClassName()); }
5051 if(font!=fnt){
5052 font=fnt;
5053 recalc();
5054 tabwidth=tabcolumns*font->getTextWidth(" ",1);
5055 barwidth=barcolumns*font->getTextWidth("8",1);
5056 // if(options&TEXT_FIXEDWRAP){ wrapwidth=wrapcolumns*font->getTextWidth("x",1); }
5057 recalc();
5058 update();
5059 }
5060 }
5061
5062
5063 // Set wrap columns
setWrapColumns(FXint cols)5064 void FXText::setWrapColumns(FXint cols){
5065 if(cols<=0) cols=1;
5066 if(cols!=wrapcolumns){
5067 wrapcolumns=cols;
5068 // if(options&TEXT_FIXEDWRAP){ wrapwidth=wrapcolumns*font->getTextWidth("x",1); }
5069 recalc();
5070 update();
5071 }
5072 }
5073
5074
5075 // Set tab columns
setTabColumns(FXint cols)5076 void FXText::setTabColumns(FXint cols){
5077 if(cols<=0) cols=1;
5078 if(cols!=tabcolumns){
5079 tabcolumns=cols;
5080 tabwidth=tabcolumns*font->getTextWidth(" ",1);
5081 recalc();
5082 update();
5083 }
5084 }
5085
5086 // Change number of columns used for line numbers
setBarColumns(FXint cols)5087 void FXText::setBarColumns(FXint cols){
5088 if(cols<=0) cols=0;
5089 if(cols!=barcolumns){
5090 barcolumns=cols;
5091 barwidth=barcolumns*font->getTextWidth("8",1);
5092 recalc();
5093 update();
5094 }
5095 }
5096
5097 // Set text color
setTextColor(FXColor clr)5098 void FXText::setTextColor(FXColor clr){
5099 if(clr!=textColor){
5100 textColor=clr;
5101 update(barwidth,0,width-barwidth,height);
5102 }
5103 }
5104
5105
5106 // Set select background color
setSelBackColor(FXColor clr)5107 void FXText::setSelBackColor(FXColor clr){
5108 if(clr!=selbackColor){
5109 selbackColor=clr;
5110 updateRange(selstartpos,selendpos);
5111 }
5112 }
5113
5114
5115 // Set selected text color
setSelTextColor(FXColor clr)5116 void FXText::setSelTextColor(FXColor clr){
5117 if(clr!=seltextColor){
5118 seltextColor=clr;
5119 updateRange(selstartpos,selendpos);
5120 }
5121 }
5122
5123
5124 // Change highlighted text color
setHiliteTextColor(FXColor clr)5125 void FXText::setHiliteTextColor(FXColor clr){
5126 if(clr!=hilitetextColor){
5127 hilitetextColor=clr;
5128 updateRange(hilitestartpos,hiliteendpos);
5129 }
5130 }
5131
5132
5133 // Change highlighted background color
setHiliteBackColor(FXColor clr)5134 void FXText::setHiliteBackColor(FXColor clr){
5135 if(clr!=hilitebackColor){
5136 hilitebackColor=clr;
5137 updateRange(hilitestartpos,hiliteendpos);
5138 }
5139 }
5140
5141
5142 // Change active background color
setActiveBackColor(FXColor clr)5143 void FXText::setActiveBackColor(FXColor clr){
5144 if(clr!=activebackColor){
5145 activebackColor=clr;
5146 update(barwidth,0,width-barwidth,height);
5147 }
5148 }
5149
5150 // Change line number color
setNumberColor(FXColor clr)5151 void FXText::setNumberColor(FXColor clr){
5152 if(clr!=numberColor){
5153 numberColor=clr;
5154 update(0,0,barwidth,height);
5155 }
5156 }
5157
5158
5159 // Change bar color
setBarColor(FXColor clr)5160 void FXText::setBarColor(FXColor clr){
5161 if(clr!=barColor){
5162 barColor=clr;
5163 update(0,0,barwidth,height);
5164 }
5165 }
5166
5167
5168 // Set cursor color
setCursorColor(FXColor clr)5169 void FXText::setCursorColor(FXColor clr){
5170 if(clr!=cursorColor){
5171 cursorColor=clr;
5172 updateRange(cursorstart,cursorend);
5173 }
5174 }
5175
5176
5177 // Change text style
setTextStyle(FXuint style)5178 void FXText::setTextStyle(FXuint style){
5179 FXuint opts=(options&~TEXT_MASK) | (style&TEXT_MASK);
5180 if(options!=opts){
5181 options=opts;
5182 // if(options&TEXT_FIXEDWRAP){ wrapwidth=wrapcolumns*font->getTextWidth("x",1); }
5183 recalc();
5184 update();
5185 }
5186 }
5187
5188
5189 // Get text style
getTextStyle() const5190 FXuint FXText::getTextStyle() const {
5191 return (options&TEXT_MASK);
5192 }
5193
5194
5195 // Return true if editable
isEditable() const5196 FXbool FXText::isEditable() const {
5197 return (options&TEXT_READONLY)==0;
5198 }
5199
5200
5201 // Set widget is editable or not
setEditable(FXbool edit)5202 void FXText::setEditable(FXbool edit){
5203 if(edit) options&=~TEXT_READONLY; else options|=TEXT_READONLY;
5204 }
5205
5206
5207 // Return TRUE if text is in overstrike mode
isOverstrike() const5208 FXbool FXText::isOverstrike() const {
5209 return (options&TEXT_OVERSTRIKE)!=0;
5210 }
5211
5212
5213 // Set overstrike mode
setOverstrike(FXbool over)5214 void FXText::setOverstrike(FXbool over){
5215 if(over) options|=TEXT_OVERSTRIKE; else options&=~TEXT_OVERSTRIKE;
5216 }
5217
5218
5219 // Set styled text mode
setStyled(FXbool styled)5220 void FXText::setStyled(FXbool styled){
5221 if(styled && !sbuffer){
5222 if(!FXCALLOC(&sbuffer,FXchar,length+gapend-gapstart)){fxerror("%s::setStyled: out of memory.\n",getClassName());}
5223 update();
5224 }
5225 if(!styled && sbuffer){
5226 FXFREE(&sbuffer);
5227 update();
5228 }
5229 }
5230
5231
5232 // Set highlight styles
setHiliteStyles(const FXHiliteStyle * styles)5233 void FXText::setHiliteStyles(const FXHiliteStyle* styles){
5234 hilitestyles=styles;
5235 update();
5236 }
5237
5238
5239 // Change number of visible rows
setVisibleRows(FXint rows)5240 void FXText::setVisibleRows(FXint rows){
5241 if(rows<0) rows=0;
5242 if(vrows!=rows){
5243 vrows=rows;
5244 recalc();
5245 }
5246 }
5247
5248
5249 // Change number of visible columns
setVisibleColumns(FXint cols)5250 void FXText::setVisibleColumns(FXint cols){
5251 if(cols<0) cols=0;
5252 if(vcols!=cols){
5253 vcols=cols;
5254 recalc();
5255 }
5256 }
5257
5258
5259 /*******************************************************************************/
5260
5261
5262 // Update value from a message
onCmdSetStringValue(FXObject *,FXSelector,void * ptr)5263 long FXText::onCmdSetStringValue(FXObject*,FXSelector,void* ptr){
5264 setText(*((FXString*)ptr));
5265 return 1;
5266 }
5267
5268
5269 // Obtain value from text
onCmdGetStringValue(FXObject *,FXSelector,void * ptr)5270 long FXText::onCmdGetStringValue(FXObject*,FXSelector,void* ptr){
5271 getText(*((FXString*)ptr));
5272 return 1;
5273 }
5274
5275
5276 /*******************************************************************************/
5277
5278
5279 // Save object to stream
save(FXStream & store) const5280 void FXText::save(FXStream& store) const {
5281 FXScrollArea::save(store);
5282 store << length;
5283 store.save(buffer,gapstart);
5284 store.save(buffer+gapend,length-gapstart);
5285 store << nvisrows;
5286 store.save(visrows,nvisrows+1);
5287 store << wrapcolumns;
5288 store << tabcolumns;
5289 store << margintop;
5290 store << marginbottom;
5291 store << marginleft;
5292 store << marginright;
5293 store << font;
5294 store << textColor;
5295 store << selbackColor;
5296 store << seltextColor;
5297 store << hilitebackColor;
5298 store << hilitetextColor;
5299 store << cursorColor;
5300 store << help;
5301 store << tip;
5302 store << matchtime;
5303 }
5304
5305
5306 // Load object from stream
load(FXStream & store)5307 void FXText::load(FXStream& store){
5308 FXScrollArea::load(store);
5309 store >> length;
5310 FXMALLOC(&buffer,FXchar,length+MINSIZE); // FIXME should we save text&style?
5311 store.load(buffer,length);
5312 gapstart=length;
5313 gapend=length+MINSIZE;
5314 store >> nvisrows;
5315 FXMALLOC(&visrows,FXint,nvisrows+1);
5316 store.load(visrows,nvisrows+1);
5317 store >> wrapcolumns;
5318 store >> tabcolumns;
5319 store >> margintop;
5320 store >> marginbottom;
5321 store >> marginleft;
5322 store >> marginright;
5323 store >> font;
5324 store >> textColor;
5325 store >> selbackColor;
5326 store >> seltextColor;
5327 store >> hilitebackColor;
5328 store >> hilitetextColor;
5329 store >> cursorColor;
5330 store >> help;
5331 store >> tip;
5332 store >> matchtime;
5333 }
5334
5335
5336 // Clean up
~FXText()5337 FXText::~FXText(){
5338 getApp()->removeTimeout(this,ID_BLINK);
5339 getApp()->removeTimeout(this,ID_FLASH);
5340 FXFREE(&buffer);
5341 FXFREE(&sbuffer);
5342 FXFREE(&visrows);
5343 buffer=(FXchar*)-1L;
5344 sbuffer=(FXchar*)-1L;
5345 visrows=(FXint*)-1L;
5346 font=(FXFont*)-1L;
5347 hilitestyles=(FXHiliteStyle*)-1L;
5348 }
5349
5350 }
5351