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