1 /*******************************************************************************
2 *									       *
3 * text.c - Display text from a text buffer				       *
4 *									       *
5 * Copyright (C) 1999 Mark Edel						       *
6 *									       *
7 * This is free software; you can redistribute it and/or modify it under the    *
8 * terms of the GNU General Public License as published by the Free Software    *
9 * Foundation; either version 2 of the License, or (at your option) any later   *
10 * version. In addition, you may distribute version of this program linked to   *
11 * Motif or Open Motif. See README for details.                                 *
12 * 									       *
13 * This software is distributed in the hope that it will be useful, but WITHOUT *
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or        *
15 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License        *
16 * for more details.							       *
17 * 									       *
18 * You should have received a copy of the GNU General Public License along with *
19 * software; if not, write to the Free Software Foundation, Inc., 59 Temple     *
20 * Place, Suite 330, Boston, MA  02111-1307 USA		                       *
21 *									       *
22 * Nirvana Text Editor	    						       *
23 * June 15, 1995								       *
24 *									       *
25 *******************************************************************************/
26 
27 #ifdef HAVE_CONFIG_H
28 #include "../config.h"
29 #endif
30 
31 #include "text.h"
32 #include "textP.h"
33 #include "textBuf.h"
34 #include "textDisp.h"
35 #include "textSel.h"
36 #include "textDrag.h"
37 #include "nedit.h"
38 #include "calltips.h"
39 #include "../util/DialogF.h"
40 #include "window.h"
41 #include "../util/nedit_malloc.h"
42 
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <limits.h>
46 #include <string.h>
47 #include <ctype.h>
48 #ifdef VMS
49 #include "../util/VMSparam.h"
50 #else
51 #ifndef __MVS__
52 #include <sys/param.h>
53 #endif
54 #endif /*VMS*/
55 #include <limits.h>
56 
57 #include <X11/Intrinsic.h>
58 #include <X11/IntrinsicP.h>
59 #include <X11/StringDefs.h>
60 #include <X11/cursorfont.h>
61 #include <Xm/Xm.h>
62 #include <Xm/XmP.h>
63 #if XmVersion >= 1002
64 #include <Xm/PrimitiveP.h>
65 #endif
66 
67 #ifdef HAVE_DEBUG_H
68 #include "../debug.h"
69 #endif
70 
71 
72 #ifdef UNICOS
73 #define XtOffset(p_type,field) ((size_t)__INTADDR__(&(((p_type)0)->field)))
74 #endif
75 
76 /* Number of pixels of motion from the initial (grab-focus) button press
77    required to begin recognizing a mouse drag for the purpose of making a
78    selection */
79 #define SELECT_THRESHOLD 5
80 
81 /* Length of delay in milliseconds for vertical autoscrolling */
82 #define VERTICAL_SCROLL_DELAY 50
83 
84 static void initialize(TextWidget request, TextWidget new);
85 static void handleHidePointer(Widget w, XtPointer unused,
86         XEvent *event, Boolean *continue_to_dispatch);
87 static void handleShowPointer(Widget w, XtPointer unused,
88         XEvent *event, Boolean *continue_to_dispatch);
89 static void redisplay(TextWidget w, XEvent *event, Region region);
90 static void redisplayGE(TextWidget w, XtPointer client_data,
91                     XEvent *event, Boolean *continue_to_dispatch_return);
92 static void destroy(TextWidget w);
93 static void resize(TextWidget w);
94 static Boolean setValues(TextWidget current, TextWidget request,
95 	TextWidget new);
96 static void realize(Widget w, XtValueMask *valueMask,
97 	XSetWindowAttributes *attributes);
98 static XtGeometryResult queryGeometry(Widget w, XtWidgetGeometry *proposed,
99 	XtWidgetGeometry *answer);
100 static void grabFocusAP(Widget w, XEvent *event, String *args,
101 	Cardinal *n_args);
102 static void moveDestinationAP(Widget w, XEvent *event, String *args,
103 	Cardinal *nArgs);
104 static void extendAdjustAP(Widget w, XEvent *event, String *args,
105 	Cardinal *nArgs);
106 static void extendStartAP(Widget w, XEvent *event, String *args,
107 	Cardinal *nArgs);
108 static void extendEndAP(Widget w, XEvent *event, String *args,
109 	Cardinal *nArgs);
110 static void processCancelAP(Widget w, XEvent *event, String *args,
111 	Cardinal *nArgs);
112 static void secondaryStartAP(Widget w, XEvent *event, String *args,
113 	Cardinal *nArgs);
114 static void secondaryOrDragStartAP(Widget w, XEvent *event, String *args,
115 	Cardinal *nArgs);
116 static void secondaryAdjustAP(Widget w, XEvent *event, String *args,
117 	Cardinal *nArgs);
118 static void secondaryOrDragAdjustAP(Widget w, XEvent *event, String *args,
119 	Cardinal *nArgs);
120 static void copyToAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
121 static void copyToOrEndDragAP(Widget w, XEvent *event, String *args,
122     	Cardinal *nArgs);
123 static void copyPrimaryAP(Widget w, XEvent *event, String *args,
124 	Cardinal *nArgs);
125 static void cutPrimaryAP(Widget w, XEvent *event, String *args,
126 	Cardinal *nArgs);
127 static void moveToAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
128 static void moveToOrEndDragAP(Widget w, XEvent *event, String *args,
129     	Cardinal *nArgs);
130 static void endDragAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
131 static void exchangeAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
132 static void mousePanAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
133 static void pasteClipboardAP(Widget w, XEvent *event, String *args,
134 	Cardinal *nArgs);
135 static void copyClipboardAP(Widget w, XEvent *event, String *args,
136 	Cardinal *nArgs);
137 static void cutClipboardAP(Widget w, XEvent *event, String *args,
138 	Cardinal *nArgs);
139 static void insertStringAP(Widget w, XEvent *event, String *args,
140 	Cardinal *nArgs);
141 static void selfInsertAP(Widget w, XEvent *event, String *args,
142 	Cardinal *n_args);
143 static void newlineAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
144 static void newlineAndIndentAP(Widget w, XEvent *event, String *args,
145 	Cardinal *nArgs);
146 static void newlineNoIndentAP(Widget w, XEvent *event, String *args,
147 	Cardinal *nArgs);
148 static void processTabAP(Widget w, XEvent *event, String *args,
149 	Cardinal *nArgs);
150 static void endOfLineAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
151 static void beginningOfLineAP(Widget w, XEvent *event, String *args,
152 	Cardinal *nArgs);
153 static void deleteSelectionAP(Widget w, XEvent *event, String *args,
154 	Cardinal *nArgs);
155 static void deletePreviousCharacterAP(Widget w, XEvent *event, String *args,
156 	Cardinal *nArgs);
157 static void deleteNextCharacterAP(Widget w, XEvent *event, String *args,
158 	Cardinal *nArgs);
159 static void deletePreviousWordAP(Widget w, XEvent *event, String *args,
160 	Cardinal *nArgs);
161 static void deleteNextWordAP(Widget w, XEvent *event, String *args,
162 	Cardinal *nArgs);
163 static void deleteToStartOfLineAP(Widget w, XEvent *event, String *args,
164 	Cardinal *nArgs);
165 static void deleteToEndOfLineAP(Widget w, XEvent *event, String *args,
166 	Cardinal *nArgs);
167 static void forwardCharacterAP(Widget w, XEvent *event, String *args,
168 	Cardinal *nArgs);
169 static void backwardCharacterAP(Widget w, XEvent *event, String *args,
170 	Cardinal *nArgs);
171 static void forwardWordAP(Widget w, XEvent *event, String *args,
172 	Cardinal *nArgs);
173 static void backwardWordAP(Widget w, XEvent *event, String *args,
174 	Cardinal *nArgs);
175 static void forwardParagraphAP(Widget w, XEvent *event, String *args,
176 	Cardinal *nArgs);
177 static void backwardParagraphAP(Widget w, XEvent *event, String *args,
178 	Cardinal *nArgs);
179 static void keySelectAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
180 static void processUpAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
181 static void processShiftUpAP(Widget w, XEvent *event, String *args,
182 	Cardinal *nArgs);
183 static void processDownAP(Widget w, XEvent *event, String *args,
184 	Cardinal *nArgs);
185 static void processShiftDownAP(Widget w, XEvent *event, String *args,
186 	Cardinal *nArgs);
187 static void beginningOfFileAP(Widget w, XEvent *event, String *args,
188 	Cardinal *nArgs);
189 static void endOfFileAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
190 static void nextPageAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
191 static void previousPageAP(Widget w, XEvent *event, String *args,
192 	Cardinal *nArgs);
193 static void pageLeftAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
194 static void pageRightAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
195 static void toggleOverstrikeAP(Widget w, XEvent *event, String *args,
196     	Cardinal *nArgs);
197 static void scrollUpAP(Widget w, XEvent *event, String *args,
198     	Cardinal *nArgs);
199 static void scrollDownAP(Widget w, XEvent *event, String *args,
200     	Cardinal *nArgs);
201 static void scrollLeftAP(Widget w, XEvent *event, String *args,
202     	Cardinal *nArgs);
203 static void scrollRightAP(Widget w, XEvent *event, String *args,
204     	Cardinal *nArgs);
205 static void scrollToLineAP(Widget w, XEvent *event, String *args,
206     	Cardinal *nArgs);
207 static void selectAllAP(Widget w, XEvent *event, String *args,
208 	Cardinal *nArgs);
209 static void deselectAllAP(Widget w, XEvent *event, String *args,
210 	Cardinal *nArgs);
211 static void focusInAP(Widget w, XEvent *event, String *args,
212 	Cardinal *nArgs);
213 static void focusOutAP(Widget w, XEvent *event, String *args,
214 	Cardinal *nArgs);
215 static void checkMoveSelectionChange(Widget w, XEvent *event, int startPos,
216 	String *args, Cardinal *nArgs);
217 static void keyMoveExtendSelection(Widget w, XEvent *event, int startPos,
218 	int rectangular);
219 static void checkAutoShowInsertPos(Widget w);
220 static int checkReadOnly(Widget w);
221 static void simpleInsertAtCursor(Widget w, char *chars, XEvent *event,
222     	int allowPendingDelete);
223 static int pendingSelection(Widget w);
224 static int deletePendingSelection(Widget w, XEvent *event);
225 static int deleteEmulatedTab(Widget w, XEvent *event);
226 static void selectWord(Widget w, int pointerX);
227 static int spanForward(textBuffer *buf, int startPos, char *searchChars,
228 	int ignoreSpace, int *foundPos);
229 static int spanBackward(textBuffer *buf, int startPos, char *searchChars, int
230     	ignoreSpace, int *foundPos);
231 static void selectLine(Widget w);
232 static int startOfWord(TextWidget w, int pos);
233 static int endOfWord(TextWidget w, int pos);
234 static void checkAutoScroll(TextWidget w, int x, int y);
235 static void endDrag(Widget w);
236 static void cancelDrag(Widget w);
237 static void callCursorMovementCBs(Widget w, XEvent *event);
238 static void adjustSelection(TextWidget tw, int x, int y);
239 static void adjustSecondarySelection(TextWidget tw, int x, int y);
240 static void autoScrollTimerProc(XtPointer clientData, XtIntervalId *id);
241 static char *wrapText(TextWidget tw, char *startLine, char *text, int bufOffset,
242     	int wrapMargin, int *breakBefore);
243 static int wrapLine(TextWidget tw, textBuffer *buf, int bufOffset,
244     	int lineStartPos, int lineEndPos, int limitPos, int *breakAt,
245 	int *charsAdded);
246 static char *createIndentString(TextWidget tw, textBuffer *buf, int bufOffset,
247     	int lineStartPos, int lineEndPos, int *length, int *column);
248 static void cursorBlinkTimerProc(XtPointer clientData, XtIntervalId *id);
249 static int hasKey(const char *key, const String *args, const Cardinal *nArgs);
250 static int max(int i1, int i2);
251 static int min(int i1, int i2);
252 static int strCaseCmp(const char *str1, const char *str2);
253 static void ringIfNecessary(Boolean silent, Widget w);
254 
255 static char defaultTranslations[] =
256     /* Home */
257     "~Shift ~Ctrl Alt<Key>osfBeginLine: last_document()\n"
258 
259     /* Backspace */
260     "Ctrl<KeyPress>osfBackSpace: delete_previous_word()\n"
261     "<KeyPress>osfBackSpace: delete_previous_character()\n"
262 
263     /* Delete */
264     "Alt Shift Ctrl<KeyPress>osfDelete: cut_primary(\"rect\")\n"
265     "Meta Shift Ctrl<KeyPress>osfDelete: cut_primary(\"rect\")\n"
266     "Shift Ctrl<KeyPress>osfDelete: cut_primary()\n"
267     "Ctrl<KeyPress>osfDelete: delete_to_end_of_line()\n"
268     "Shift<KeyPress>osfDelete: cut_clipboard()\n"
269     "<KeyPress>osfDelete: delete_next_character()\n"
270 
271     /* Insert */
272     "Alt Shift Ctrl<KeyPress>osfInsert: copy_primary(\"rect\")\n"
273     "Meta Shift Ctrl<KeyPress>osfInsert: copy_primary(\"rect\")\n"
274     "Shift Ctrl<KeyPress>osfInsert: copy_primary()\n"
275     "Shift<KeyPress>osfInsert: paste_clipboard()\n"
276     "Ctrl<KeyPress>osfInsert: copy_clipboard()\n"
277     "~Shift ~Ctrl<KeyPress>osfInsert: set_overtype_mode()\n"
278 
279     /* Cut/Copy/Paste */
280     "Shift Ctrl<KeyPress>osfCut: cut_primary()\n"
281     "<KeyPress>osfCut: cut_clipboard()\n"
282     "<KeyPress>osfCopy: copy_clipboard()\n"
283     "<KeyPress>osfPaste: paste_clipboard()\n"
284     "<KeyPress>osfPrimaryPaste: copy_primary()\n"
285 
286     /* BeginLine */
287     "Alt Shift Ctrl<KeyPress>osfBeginLine: beginning_of_file(\"extend\", \"rect\")\n"
288     "Meta Shift Ctrl<KeyPress>osfBeginLine: beginning_of_file(\"extend\" \"rect\")\n"
289     "Alt Shift<KeyPress>osfBeginLine: beginning_of_line(\"extend\", \"rect\")\n"
290     "Meta Shift<KeyPress>osfBeginLine: beginning_of_line(\"extend\", \"rect\")\n"
291     "Shift Ctrl<KeyPress>osfBeginLine: beginning_of_file(\"extend\")\n"
292     "Ctrl<KeyPress>osfBeginLine: beginning_of_file()\n"
293     "Shift<KeyPress>osfBeginLine: beginning_of_line(\"extend\")\n"
294     "~Alt~Shift~Ctrl~Meta<KeyPress>osfBeginLine: beginning_of_line()\n"
295 
296     /* EndLine */
297     "Alt Shift Ctrl<KeyPress>osfEndLine: end_of_file(\"extend\", \"rect\")\n"
298     "Meta Shift Ctrl<KeyPress>osfEndLine: end_of_file(\"extend\", \"rect\")\n"
299     "Alt Shift<KeyPress>osfEndLine: end_of_line(\"extend\", \"rect\")\n"
300     "Meta Shift<KeyPress>osfEndLine: end_of_line(\"extend\", \"rect\")\n"
301     "Shift Ctrl<KeyPress>osfEndLine: end_of_file(\"extend\")\n"
302     "Ctrl<KeyPress>osfEndLine: end_of_file()\n"
303     "Shift<KeyPress>osfEndLine: end_of_line(\"extend\")\n"
304     "~Alt~Shift~Ctrl~Meta<KeyPress>osfEndLine: end_of_line()\n"
305 
306     /* Left */
307     "Alt Shift Ctrl<KeyPress>osfLeft: backward_word(\"extend\", \"rect\")\n"
308     "Meta Shift Ctrl<KeyPress>osfLeft: backward_word(\"extend\", \"rect\")\n"
309     "Alt Shift<KeyPress>osfLeft: key_select(\"left\", \"rect\")\n"
310     "Meta Shift<KeyPress>osfLeft: key_select(\"left\", \"rect\")\n"
311     "Shift Ctrl<KeyPress>osfLeft: backward_word(\"extend\")\n"
312     "Ctrl<KeyPress>osfLeft: backward_word()\n"
313     "Shift<KeyPress>osfLeft: key_select(\"left\")\n"
314     "~Alt~Shift~Ctrl~Meta<KeyPress>osfLeft: backward_character()\n"
315 
316     /* Right */
317     "Alt Shift Ctrl<KeyPress>osfRight: forward_word(\"extend\", \"rect\")\n"
318     "Meta Shift Ctrl<KeyPress>osfRight: forward_word(\"extend\", \"rect\")\n"
319     "Alt Shift<KeyPress>osfRight: key_select(\"right\", \"rect\")\n"
320     "Meta Shift<KeyPress>osfRight: key_select(\"right\", \"rect\")\n"
321     "Shift Ctrl<KeyPress>osfRight: forward_word(\"extend\")\n"
322     "Ctrl<KeyPress>osfRight: forward_word()\n"
323     "Shift<KeyPress>osfRight: key_select(\"right\")\n"
324     "~Alt~Shift~Ctrl~Meta<KeyPress>osfRight: forward_character()\n"
325 
326     /* Up */
327     "Alt Shift Ctrl<KeyPress>osfUp: backward_paragraph(\"extend\", \"rect\")\n"
328     "Meta Shift Ctrl<KeyPress>osfUp: backward_paragraph(\"extend\", \"rect\")\n"
329     "Alt Shift<KeyPress>osfUp: process_shift_up(\"rect\")\n"
330     "Meta Shift<KeyPress>osfUp: process_shift_up(\"rect\")\n"
331     "Shift Ctrl<KeyPress>osfUp: backward_paragraph(\"extend\")\n"
332     "Ctrl<KeyPress>osfUp: backward_paragraph()\n"
333     "Shift<KeyPress>osfUp: process_shift_up()\n"
334     "~Alt~Shift~Ctrl~Meta<KeyPress>osfUp: process_up()\n"
335 
336     /* Down */
337     "Alt Shift Ctrl<KeyPress>osfDown: forward_paragraph(\"extend\", \"rect\")\n"
338     "Meta Shift Ctrl<KeyPress>osfDown: forward_paragraph(\"extend\", \"rect\")\n"
339     "Alt Shift<KeyPress>osfDown: process_shift_down(\"rect\")\n"
340     "Meta Shift<KeyPress>osfDown: process_shift_down(\"rect\")\n"
341     "Shift Ctrl<KeyPress>osfDown: forward_paragraph(\"extend\")\n"
342     "Ctrl<KeyPress>osfDown: forward_paragraph()\n"
343     "Shift<KeyPress>osfDown: process_shift_down()\n"
344     "~Alt~Shift~Ctrl~Meta<KeyPress>osfDown: process_down()\n"
345 
346     /* PageUp */
347     "Alt Shift Ctrl<KeyPress>osfPageUp: page_left(\"extend\", \"rect\")\n"
348     "Meta Shift Ctrl<KeyPress>osfPageUp: page_left(\"extend\", \"rect\")\n"
349     "Alt Shift<KeyPress>osfPageUp: previous_page(\"extend\", \"rect\")\n"
350     "Meta Shift<KeyPress>osfPageUp: previous_page(\"extend\", \"rect\")\n"
351     "Shift Ctrl<KeyPress>osfPageUp: page_left(\"extend\")\n"
352     "Ctrl<KeyPress>osfPageUp: previous_document()\n"
353     "Shift<KeyPress>osfPageUp: previous_page(\"extend\")\n"
354     "~Alt ~Shift ~Ctrl ~Meta<KeyPress>osfPageUp: previous_page()\n"
355 
356     /* PageDown */
357     "Alt Shift Ctrl<KeyPress>osfPageDown: page_right(\"extend\", \"rect\")\n"
358     "Meta Shift Ctrl<KeyPress>osfPageDown: page_right(\"extend\", \"rect\")\n"
359     "Alt Shift<KeyPress>osfPageDown: next_page(\"extend\", \"rect\")\n"
360     "Meta Shift<KeyPress>osfPageDown: next_page(\"extend\", \"rect\")\n"
361     "Shift Ctrl<KeyPress>osfPageDown: page_right(\"extend\")\n"
362     "Ctrl<KeyPress>osfPageDown: next_document()\n"
363     "Shift<KeyPress>osfPageDown: next_page(\"extend\")\n"
364     "~Alt ~Shift ~Ctrl ~Meta<KeyPress>osfPageDown: next_page()\n"
365 
366     /* PageLeft and PageRight are placed later than the PageUp/PageDown
367        bindings.  Some systems map osfPageLeft to Ctrl-PageUp.
368        Overloading this single key gives problems, and we want to give
369        priority to the normal version. */
370 
371     /* PageLeft */
372     "Alt Shift<KeyPress>osfPageLeft: page_left(\"extend\", \"rect\")\n"
373     "Meta Shift<KeyPress>osfPageLeft: page_left(\"extend\", \"rect\")\n"
374     "Shift<KeyPress>osfPageLeft: page_left(\"extend\")\n"
375     "~Alt ~Shift ~Ctrl ~Meta<KeyPress>osfPageLeft: page_left()\n"
376 
377     /* PageRight */
378     "Alt Shift<KeyPress>osfPageRight: page_right(\"extend\", \"rect\")\n"
379     "Meta Shift<KeyPress>osfPageRight: page_right(\"extend\", \"rect\")\n"
380     "Shift<KeyPress>osfPageRight: page_right(\"extend\")\n"
381     "~Alt ~Shift ~Ctrl ~Meta<KeyPress>osfPageRight: page_right()\n"
382 
383     "Shift<KeyPress>osfSelect: key_select()\n"
384     "<KeyPress>osfCancel: process_cancel()\n"
385     "Ctrl~Alt~Meta<KeyPress>v: paste_clipboard()\n"
386     "Ctrl~Alt~Meta<KeyPress>c: copy_clipboard()\n"
387     "Ctrl~Alt~Meta<KeyPress>x: cut_clipboard()\n"
388     "Ctrl~Alt~Meta<KeyPress>u: delete_to_start_of_line()\n"
389     "Ctrl<KeyPress>Return: newline_and_indent()\n"
390     "Shift<KeyPress>Return: newline_no_indent()\n"
391     "<KeyPress>Return: newline()\n"
392     /* KP_Enter = osfActivate
393        Note: Ctrl+KP_Enter is already bound to Execute Command Line... */
394     "Shift<KeyPress>osfActivate: newline_no_indent()\n"
395     "<KeyPress>osfActivate: newline()\n"
396     "Ctrl<KeyPress>Tab: self_insert()\n"
397     "<KeyPress>Tab: process_tab()\n"
398     "Alt Shift Ctrl<KeyPress>space: key_select(\"rect\")\n"
399     "Meta Shift Ctrl<KeyPress>space: key_select(\"rect\")\n"
400     "Shift Ctrl~Meta~Alt<KeyPress>space: key_select()\n"
401     "Ctrl~Meta~Alt<KeyPress>slash: select_all()\n"
402     "Ctrl~Meta~Alt<KeyPress>backslash: deselect_all()\n"
403     "<KeyPress>: self_insert()\n"
404     "Alt Ctrl<Btn1Down>: move_destination()\n"
405     "Meta Ctrl<Btn1Down>: move_destination()\n"
406     "Shift Ctrl<Btn1Down>: extend_start(\"rect\")\n"
407     "Shift<Btn1Down>: extend_start()\n"
408     "<Btn1Down>: grab_focus()\n"
409     "Button1 Ctrl<MotionNotify>: extend_adjust(\"rect\")\n"
410     "Button1~Ctrl<MotionNotify>: extend_adjust()\n"
411     "<Btn1Up>: extend_end()\n"
412     "<Btn2Down>: secondary_or_drag_start()\n"
413     "Shift Ctrl Button2<MotionNotify>: secondary_or_drag_adjust(\"rect\", \"copy\", \"overlay\")\n"
414     "Shift Button2<MotionNotify>: secondary_or_drag_adjust(\"copy\")\n"
415     "Ctrl Button2<MotionNotify>: secondary_or_drag_adjust(\"rect\", \"overlay\")\n"
416     "Button2<MotionNotify>: secondary_or_drag_adjust()\n"
417     "Shift Ctrl<Btn2Up>: move_to_or_end_drag(\"copy\", \"overlay\")\n"
418     "Shift <Btn2Up>: move_to_or_end_drag(\"copy\")\n"
419     "Alt<Btn2Up>: exchange()\n"
420     "Meta<Btn2Up>: exchange()\n"
421     "Ctrl<Btn2Up>: copy_to_or_end_drag(\"overlay\")\n"
422     "<Btn2Up>: copy_to_or_end_drag()\n"
423     "Ctrl~Meta~Alt<Btn3Down>: mouse_pan()\n"
424     "Ctrl~Meta~Alt Button3<MotionNotify>: mouse_pan()\n"
425     "<Btn3Up>: end_drag()\n"
426     "<FocusIn>: focusIn()\n"
427     "<FocusOut>: focusOut()\n"
428     /* Support for mouse wheel in XFree86 */
429     "Shift<Btn4Down>,<Btn4Up>: scroll_up(1)\n"
430     "Shift<Btn5Down>,<Btn5Up>: scroll_down(1)\n"
431     "Ctrl<Btn4Down>,<Btn4Up>: scroll_up(1, pages)\n"
432     "Ctrl<Btn5Down>,<Btn5Up>: scroll_down(1, pages)\n"
433     "<Btn4Down>,<Btn4Up>: scroll_up(5)\n"
434     "<Btn5Down>,<Btn5Up>: scroll_down(5)\n";
435      /* some of the translations from the Motif text widget were not picked up:
436 	  :<KeyPress>osfSelect: set-anchor()\n\
437 	  :<KeyPress>osfActivate: activate()\n\
438 	 ~Shift Ctrl~Meta~Alt<KeyPress>Return: activate()\n\
439 	  ~Shift Ctrl~Meta~Alt<KeyPress>space: set-anchor()\n\
440 	   :<KeyPress>osfClear: clear-selection()\n\
441 	  ~Shift~Ctrl~Meta~Alt<KeyPress>Return: process-return()\n\
442 	  Shift~Meta~Alt<KeyPress>Tab: prev-tab-group()\n\
443 	  Ctrl~Meta~Alt<KeyPress>Tab: next-tab-group()\n\
444 	  <UnmapNotify>: unmap()\n\
445 	  <EnterNotify>: enter()\n\
446 	  <LeaveNotify>: leave()\n
447      */
448 
449 
450 static XtActionsRec actionsList[] = {
451     {"self-insert", selfInsertAP},
452     {"self_insert", selfInsertAP},
453     {"grab-focus", grabFocusAP},
454     {"grab_focus", grabFocusAP},
455     {"extend-adjust", extendAdjustAP},
456     {"extend_adjust", extendAdjustAP},
457     {"extend-start", extendStartAP},
458     {"extend_start", extendStartAP},
459     {"extend-end", extendEndAP},
460     {"extend_end", extendEndAP},
461     {"secondary-adjust", secondaryAdjustAP},
462     {"secondary_adjust", secondaryAdjustAP},
463     {"secondary-or-drag-adjust", secondaryOrDragAdjustAP},
464     {"secondary_or_drag_adjust", secondaryOrDragAdjustAP},
465     {"secondary-start", secondaryStartAP},
466     {"secondary_start", secondaryStartAP},
467     {"secondary-or-drag-start", secondaryOrDragStartAP},
468     {"secondary_or_drag_start", secondaryOrDragStartAP},
469     {"process-bdrag", secondaryOrDragStartAP},
470     {"process_bdrag", secondaryOrDragStartAP},
471     {"move-destination", moveDestinationAP},
472     {"move_destination", moveDestinationAP},
473     {"move-to", moveToAP},
474     {"move_to", moveToAP},
475     {"move-to-or-end-drag", moveToOrEndDragAP},
476     {"move_to_or_end_drag", moveToOrEndDragAP},
477     {"end_drag", endDragAP},
478     {"copy-to", copyToAP},
479     {"copy_to", copyToAP},
480     {"copy-to-or-end-drag", copyToOrEndDragAP},
481     {"copy_to_or_end_drag", copyToOrEndDragAP},
482     {"exchange", exchangeAP},
483     {"process-cancel", processCancelAP},
484     {"process_cancel", processCancelAP},
485     {"paste-clipboard", pasteClipboardAP},
486     {"paste_clipboard", pasteClipboardAP},
487     {"copy-clipboard", copyClipboardAP},
488     {"copy_clipboard", copyClipboardAP},
489     {"cut-clipboard", cutClipboardAP},
490     {"cut_clipboard", cutClipboardAP},
491     {"copy-primary", copyPrimaryAP},
492     {"copy_primary", copyPrimaryAP},
493     {"cut-primary", cutPrimaryAP},
494     {"cut_primary", cutPrimaryAP},
495     {"newline", newlineAP},
496     {"newline-and-indent", newlineAndIndentAP},
497     {"newline_and_indent", newlineAndIndentAP},
498     {"newline-no-indent", newlineNoIndentAP},
499     {"newline_no_indent", newlineNoIndentAP},
500     {"delete-selection", deleteSelectionAP},
501     {"delete_selection", deleteSelectionAP},
502     {"delete-previous-character", deletePreviousCharacterAP},
503     {"delete_previous_character", deletePreviousCharacterAP},
504     {"delete-next-character", deleteNextCharacterAP},
505     {"delete_next_character", deleteNextCharacterAP},
506     {"delete-previous-word", deletePreviousWordAP},
507     {"delete_previous_word", deletePreviousWordAP},
508     {"delete-next-word", deleteNextWordAP},
509     {"delete_next_word", deleteNextWordAP},
510     {"delete-to-start-of-line", deleteToStartOfLineAP},
511     {"delete_to_start_of_line", deleteToStartOfLineAP},
512     {"delete-to-end-of-line", deleteToEndOfLineAP},
513     {"delete_to_end_of_line", deleteToEndOfLineAP},
514     {"forward-character", forwardCharacterAP},
515     {"forward_character", forwardCharacterAP},
516     {"backward-character", backwardCharacterAP},
517     {"backward_character", backwardCharacterAP},
518     {"key-select", keySelectAP},
519     {"key_select", keySelectAP},
520     {"process-up", processUpAP},
521     {"process_up", processUpAP},
522     {"process-down", processDownAP},
523     {"process_down", processDownAP},
524     {"process-shift-up", processShiftUpAP},
525     {"process_shift_up", processShiftUpAP},
526     {"process-shift-down", processShiftDownAP},
527     {"process_shift_down", processShiftDownAP},
528     {"process-home", beginningOfLineAP},
529     {"process_home", beginningOfLineAP},
530     {"forward-word", forwardWordAP},
531     {"forward_word", forwardWordAP},
532     {"backward-word", backwardWordAP},
533     {"backward_word", backwardWordAP},
534     {"forward-paragraph", forwardParagraphAP},
535     {"forward_paragraph", forwardParagraphAP},
536     {"backward-paragraph", backwardParagraphAP},
537     {"backward_paragraph", backwardParagraphAP},
538     {"beginning-of-line", beginningOfLineAP},
539     {"beginning_of_line", beginningOfLineAP},
540     {"end-of-line", endOfLineAP},
541     {"end_of_line", endOfLineAP},
542     {"beginning-of-file", beginningOfFileAP},
543     {"beginning_of_file", beginningOfFileAP},
544     {"end-of-file", endOfFileAP},
545     {"end_of_file", endOfFileAP},
546     {"next-page", nextPageAP},
547     {"next_page", nextPageAP},
548     {"previous-page", previousPageAP},
549     {"previous_page", previousPageAP},
550     {"page-left", pageLeftAP},
551     {"page_left", pageLeftAP},
552     {"page-right", pageRightAP},
553     {"page_right", pageRightAP},
554     {"toggle-overstrike", toggleOverstrikeAP},
555     {"toggle_overstrike", toggleOverstrikeAP},
556     {"scroll-up", scrollUpAP},
557     {"scroll_up", scrollUpAP},
558     {"scroll-down", scrollDownAP},
559     {"scroll_down", scrollDownAP},
560     {"scroll_left", scrollLeftAP},
561     {"scroll_right", scrollRightAP},
562     {"scroll-to-line", scrollToLineAP},
563     {"scroll_to_line", scrollToLineAP},
564     {"select-all", selectAllAP},
565     {"select_all", selectAllAP},
566     {"deselect-all", deselectAllAP},
567     {"deselect_all", deselectAllAP},
568     {"focusIn", focusInAP},
569     {"focusOut", focusOutAP},
570     {"process-return", selfInsertAP},
571     {"process_return", selfInsertAP},
572     {"process-tab", processTabAP},
573     {"process_tab", processTabAP},
574     {"insert-string", insertStringAP},
575     {"insert_string", insertStringAP},
576     {"mouse_pan", mousePanAP},
577 };
578 
579 /* The motif text widget defined a bunch of actions which the nedit text
580    widget as-of-yet does not support:
581 
582      Actions which were not bound to keys (for emacs emulation, some of
583      them should probably be supported:
584 
585 	kill-next-character()
586 	kill-next-word()
587 	kill-previous-character()
588 	kill-previous-word()
589 	kill-selection()
590 	kill-to-end-of-line()
591 	kill-to-start-of-line()
592 	unkill()
593 	next-line()
594 	newline-and-backup()
595 	beep()
596 	redraw-display()
597 	scroll-one-line-down()
598 	scroll-one-line-up()
599 	set-insertion-point()
600 
601     Actions which are not particularly useful:
602 
603 	set-anchor()
604 	activate()
605 	clear-selection() -> this is a wierd one
606 	do-quick-action() -> don't think this ever worked
607 	Help()
608 	next-tab-group()
609 	select-adjust()
610 	select-start()
611 	select-end()
612 */
613 
614 static XtResource resources[] = {
615     {XmNhighlightThickness, XmCHighlightThickness, XmRDimension,
616       sizeof(Dimension), XtOffset(TextWidget, primitive.highlight_thickness),
617       XmRInt, 0},
618     {XmNshadowThickness, XmCShadowThickness, XmRDimension, sizeof(Dimension),
619       XtOffset(TextWidget, primitive.shadow_thickness), XmRInt, 0},
620     {textNfont, textCFont, XmRFontStruct, sizeof(XFontStruct *),
621       XtOffset(TextWidget, text.fontStruct), XmRString, "fixed"},
622     {textNselectForeground, textCSelectForeground, XmRPixel, sizeof(Pixel),
623       XtOffset(TextWidget, text.selectFGPixel), XmRString,
624       NEDIT_DEFAULT_SEL_FG},
625     {textNselectBackground, textCSelectBackground, XmRPixel, sizeof(Pixel),
626       XtOffset(TextWidget, text.selectBGPixel), XmRString,
627       NEDIT_DEFAULT_SEL_BG},
628     {textNhighlightForeground, textCHighlightForeground, XmRPixel,sizeof(Pixel),
629       XtOffset(TextWidget, text.highlightFGPixel), XmRString,
630       NEDIT_DEFAULT_HI_FG},
631     {textNhighlightBackground, textCHighlightBackground, XmRPixel,sizeof(Pixel),
632       XtOffset(TextWidget, text.highlightBGPixel), XmRString,
633       NEDIT_DEFAULT_HI_BG},
634     {textNlineNumForeground, textCLineNumForeground, XmRPixel,sizeof(Pixel),
635       XtOffset(TextWidget, text.lineNumFGPixel), XmRString,
636       NEDIT_DEFAULT_LINENO_FG},
637     {textNcursorForeground, textCCursorForeground, XmRPixel,sizeof(Pixel),
638       XtOffset(TextWidget, text.cursorFGPixel), XmRString,
639       NEDIT_DEFAULT_CURSOR_FG},
640     {textNcalltipForeground, textCcalltipForeground, XmRPixel,sizeof(Pixel),
641       XtOffset(TextWidget, text.calltipFGPixel), XmRString,
642       NEDIT_DEFAULT_CALLTIP_FG},
643     {textNcalltipBackground, textCcalltipBackground, XmRPixel,sizeof(Pixel),
644       XtOffset(TextWidget, text.calltipBGPixel), XmRString,
645       NEDIT_DEFAULT_CALLTIP_BG},
646     {textNbacklightCharTypes,textCBacklightCharTypes,XmRString,sizeof(XmString),
647       XtOffset(TextWidget, text.backlightCharTypes), XmRString, NULL},
648     {textNrows, textCRows, XmRInt,sizeof(int),
649       XtOffset(TextWidget, text.rows), XmRString, "24"},
650     {textNcolumns, textCColumns, XmRInt, sizeof(int),
651       XtOffset(TextWidget, text.columns), XmRString, "80"},
652     {textNmarginWidth, textCMarginWidth, XmRInt, sizeof(int),
653       XtOffset(TextWidget, text.marginWidth), XmRString, "5"},
654     {textNmarginHeight, textCMarginHeight, XmRInt, sizeof(int),
655       XtOffset(TextWidget, text.marginHeight), XmRString, "5"},
656     {textNpendingDelete, textCPendingDelete, XmRBoolean, sizeof(Boolean),
657       XtOffset(TextWidget, text.pendingDelete), XmRString, "True"},
658     {textNautoWrap, textCAutoWrap, XmRBoolean, sizeof(Boolean),
659       XtOffset(TextWidget, text.autoWrap), XmRString, "True"},
660     {textNcontinuousWrap, textCContinuousWrap, XmRBoolean, sizeof(Boolean),
661       XtOffset(TextWidget, text.continuousWrap), XmRString, "True"},
662     {textNautoIndent, textCAutoIndent, XmRBoolean, sizeof(Boolean),
663       XtOffset(TextWidget, text.autoIndent), XmRString, "True"},
664     {textNsmartIndent, textCSmartIndent, XmRBoolean, sizeof(Boolean),
665       XtOffset(TextWidget, text.smartIndent), XmRString, "False"},
666     {textNoverstrike, textCOverstrike, XmRBoolean, sizeof(Boolean),
667       XtOffset(TextWidget, text.overstrike), XmRString, "False"},
668     {textNheavyCursor, textCHeavyCursor, XmRBoolean, sizeof(Boolean),
669       XtOffset(TextWidget, text.heavyCursor), XmRString, "False"},
670     {textNreadOnly, textCReadOnly, XmRBoolean, sizeof(Boolean),
671       XtOffset(TextWidget, text.readOnly), XmRString, "False"},
672     {textNhidePointer, textCHidePointer, XmRBoolean, sizeof(Boolean),
673       XtOffset(TextWidget, text.hidePointer), XmRString, "False"},
674     {textNwrapMargin, textCWrapMargin, XmRInt, sizeof(int),
675       XtOffset(TextWidget, text.wrapMargin), XmRString, "0"},
676     {textNhScrollBar, textCHScrollBar, XmRWidget, sizeof(Widget),
677       XtOffset(TextWidget, text.hScrollBar), XmRString, ""},
678     {textNvScrollBar, textCVScrollBar, XmRWidget, sizeof(Widget),
679       XtOffset(TextWidget, text.vScrollBar), XmRString, ""},
680     {textNlineNumCols, textCLineNumCols, XmRInt, sizeof(int),
681       XtOffset(TextWidget, text.lineNumCols), XmRString, "0"},
682     {textNautoShowInsertPos, textCAutoShowInsertPos, XmRBoolean,
683       sizeof(Boolean), XtOffset(TextWidget, text.autoShowInsertPos),
684       XmRString, "True"},
685     {textNautoWrapPastedText, textCAutoWrapPastedText, XmRBoolean,
686       sizeof(Boolean), XtOffset(TextWidget, text.autoWrapPastedText),
687       XmRString, "False"},
688     {textNwordDelimiters, textCWordDelimiters, XmRString, sizeof(char *),
689       XtOffset(TextWidget, text.delimiters), XmRString,
690       ".,/\\`'!@#%^&*()-=+{}[]\":;<>?"},
691     {textNblinkRate, textCBlinkRate, XmRInt, sizeof(int),
692       XtOffset(TextWidget, text.cursorBlinkRate), XmRString, "500"},
693     {textNemulateTabs, textCEmulateTabs, XmRInt, sizeof(int),
694       XtOffset(TextWidget, text.emulateTabs), XmRString, "0"},
695     {textNfocusCallback, textCFocusCallback, XmRCallback, sizeof(caddr_t),
696       XtOffset(TextWidget, text.focusInCB), XtRCallback, NULL},
697     {textNlosingFocusCallback, textCLosingFocusCallback, XmRCallback,
698       sizeof(caddr_t), XtOffset(TextWidget, text.focusOutCB), XtRCallback,NULL},
699     {textNcursorMovementCallback, textCCursorMovementCallback, XmRCallback,
700       sizeof(caddr_t), XtOffset(TextWidget, text.cursorCB), XtRCallback, NULL},
701     {textNdragStartCallback, textCDragStartCallback, XmRCallback,
702       sizeof(caddr_t), XtOffset(TextWidget, text.dragStartCB), XtRCallback,
703       NULL},
704     {textNdragEndCallback, textCDragEndCallback, XmRCallback,
705       sizeof(caddr_t), XtOffset(TextWidget, text.dragEndCB), XtRCallback, NULL},
706     {textNsmartIndentCallback, textCSmartIndentCallback, XmRCallback,
707       sizeof(caddr_t), XtOffset(TextWidget, text.smartIndentCB), XtRCallback,
708       NULL},
709     {textNcursorVPadding, textCCursorVPadding, XtRCardinal, sizeof(Cardinal),
710       XtOffset(TextWidget, text.cursorVPadding), XmRString, "0"}
711 };
712 
713 static TextClassRec textClassRec = {
714      /* CoreClassPart */
715   {
716     (WidgetClass) &xmPrimitiveClassRec,  /* superclass       */
717     "Text",                         /* class_name            */
718     sizeof(TextRec),                /* widget_size           */
719     NULL,                           /* class_initialize      */
720     NULL,                           /* class_part_initialize */
721     FALSE,                          /* class_inited          */
722     (XtInitProc)initialize,         /* initialize            */
723     NULL,                           /* initialize_hook       */
724     realize,   		            /* realize               */
725     actionsList,                    /* actions               */
726     XtNumber(actionsList),          /* num_actions           */
727     resources,                      /* resources             */
728     XtNumber(resources),            /* num_resources         */
729     NULLQUARK,                      /* xrm_class             */
730     TRUE,                           /* compress_motion       */
731     TRUE,                           /* compress_exposure     */
732     TRUE,                           /* compress_enterleave   */
733     FALSE,                          /* visible_interest      */
734     (XtWidgetProc)destroy,          /* destroy               */
735     (XtWidgetProc)resize,           /* resize                */
736     (XtExposeProc)redisplay,        /* expose                */
737     (XtSetValuesFunc)setValues,     /* set_values            */
738     NULL,                           /* set_values_hook       */
739     XtInheritSetValuesAlmost,       /* set_values_almost     */
740     NULL,                           /* get_values_hook       */
741     NULL,                           /* accept_focus          */
742     XtVersion,                      /* version               */
743     NULL,                           /* callback private      */
744     defaultTranslations,            /* tm_table              */
745     queryGeometry,                  /* query_geometry        */
746     NULL,                           /* display_accelerator   */
747     NULL,                           /* extension             */
748   },
749   /* Motif primitive class fields */
750   {
751      (XtWidgetProc)_XtInherit,   	/* Primitive border_highlight   */
752      (XtWidgetProc)_XtInherit,   	/* Primitive border_unhighlight */
753      NULL, /*XtInheritTranslations,*/	/* translations                 */
754      NULL,				/* arm_and_activate             */
755      NULL,				/* get resources      		*/
756      0,					/* num get_resources  		*/
757      NULL,         			/* extension                    */
758   },
759   /* Text class part */
760   {
761     0,                              	/* ignored	                */
762   }
763 };
764 
765 WidgetClass textWidgetClass = (WidgetClass)&textClassRec;
766 #define NEDIT_HIDE_CURSOR_MASK (KeyPressMask)
767 #define NEDIT_SHOW_CURSOR_MASK (FocusChangeMask | PointerMotionMask | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask)
768 static char empty_bits[] = {0x00, 0x00, 0x00, 0x00};
769 static Cursor empty_cursor = 0;
770 
771 /*
772 ** Widget initialize method
773 */
initialize(TextWidget request,TextWidget new)774 static void initialize(TextWidget request, TextWidget new)
775 {
776     XFontStruct *fs = new->text.fontStruct;
777     char *delimiters;
778     textBuffer *buf;
779     Pixel white, black;
780     int textLeft;
781     int charWidth;
782     int marginWidth;
783     int lineNumCols;
784 
785     charWidth = fs->max_bounds.width;
786     marginWidth = new->text.marginWidth;
787     lineNumCols = new->text.lineNumCols;
788 
789     /* Set the initial window size based on the rows and columns resources */
790     if (request->core.width == 0)
791     	new->core.width = charWidth * new->text.columns + marginWidth*2 +
792 	       (lineNumCols == 0 ? 0 : marginWidth + charWidth * lineNumCols);
793     if (request->core.height == 0)
794    	new->core.height = (fs->ascent + fs->descent) * new->text.rows +
795    		new->text.marginHeight * 2;
796 
797     /* The default colors work for B&W as well as color, except for
798        selectFGPixel and selectBGPixel, where color highlighting looks
799        much better without reverse video, so if we get here, and the
800        selection is totally unreadable because of the bad default colors,
801        swap the colors and make the selection reverse video */
802     white = WhitePixelOfScreen(XtScreen((Widget)new));
803     black = BlackPixelOfScreen(XtScreen((Widget)new));
804     if (    new->text.selectBGPixel == white &&
805     	    new->core.background_pixel == white &&
806     	    new->text.selectFGPixel == black &&
807     	    new->primitive.foreground == black) {
808     	new->text.selectBGPixel = black;
809     	new->text.selectFGPixel = white;
810     }
811 
812     /* Create the initial text buffer for the widget to display (which can
813        be replaced later with TextSetBuffer) */
814     buf = BufCreate();
815 
816     /* Create and initialize the text-display part of the widget */
817     textLeft = new->text.marginWidth +
818 	    (lineNumCols == 0 ? 0 : marginWidth + charWidth * lineNumCols);
819     new->text.textD = TextDCreate((Widget)new, new->text.hScrollBar,
820 	    new->text.vScrollBar, textLeft, new->text.marginHeight,
821 	    new->core.width - marginWidth - textLeft,
822 	    new->core.height - new->text.marginHeight * 2,
823 	    lineNumCols == 0 ? 0 : marginWidth,
824 	    lineNumCols == 0 ? 0 : lineNumCols * charWidth,
825 	    buf, new->text.fontStruct, new->core.background_pixel,
826 	    new->primitive.foreground, new->text.selectFGPixel,
827 	    new->text.selectBGPixel, new->text.highlightFGPixel,
828 	    new->text.highlightBGPixel, new->text.cursorFGPixel,
829 	    new->text.lineNumFGPixel,
830           new->text.continuousWrap, new->text.wrapMargin,
831           new->text.backlightCharTypes, new->text.calltipFGPixel,
832           new->text.calltipBGPixel);
833 
834     /* Add mandatory delimiters blank, tab, and newline to the list of
835        delimiters.  The memory use scheme here is that new values are
836        always copied, and can therefore be safely freed on subsequent
837        set-values calls or destroy */
838     delimiters = (char*)NEditMalloc(strlen(new->text.delimiters) + 4);
839     sprintf(delimiters, "%s%s", " \t\n", new->text.delimiters);
840     new->text.delimiters = delimiters;
841 
842     /* Start with the cursor blanked (widgets don't have focus on creation,
843        the initial FocusIn event will unblank it and get blinking started) */
844     new->text.textD->cursorOn = False;
845 
846     /* Initialize the widget variables */
847     new->text.autoScrollProcID = 0;
848     new->text.cursorBlinkProcID = 0;
849     new->text.dragState = NOT_CLICKED;
850     new->text.multiClickState = NO_CLICKS;
851     new->text.lastBtnDown = 0;
852     new->text.selectionOwner = False;
853     new->text.motifDestOwner = False;
854     new->text.emTabsBeforeCursor = 0;
855 
856 #ifndef NO_XMIM
857     /* Register the widget to the input manager */
858     XmImRegister((Widget)new, 0);
859     /* In case some Resources for the IC need to be set, add them below */
860     XmImVaSetValues((Widget)new, NULL);
861 #endif
862 
863     XtAddEventHandler((Widget)new, GraphicsExpose, True,
864             (XtEventHandler)redisplayGE, (Opaque)NULL);
865 
866     if (new->text.hidePointer) {
867         Display *theDisplay;
868         Pixmap empty_pixmap;
869         XColor black_color;
870         /* Set up the empty Cursor */
871         if (empty_cursor == 0) {
872             theDisplay = XtDisplay((Widget)new);
873             empty_pixmap = XCreateBitmapFromData(theDisplay,
874                     RootWindowOfScreen(XtScreen((Widget)new)), empty_bits, 1, 1);
875             XParseColor(theDisplay, DefaultColormapOfScreen(XtScreen((Widget)new)),
876                     "black", &black_color);
877             empty_cursor = XCreatePixmapCursor(theDisplay, empty_pixmap,
878                     empty_pixmap, &black_color, &black_color, 0, 0);
879         }
880 
881         /* Add event handler to hide the pointer on keypresses */
882         XtAddEventHandler((Widget)new, NEDIT_HIDE_CURSOR_MASK, False,
883                 handleHidePointer, (Opaque)NULL);
884     }
885 }
886 
887 /* Hide the pointer while the user is typing */
handleHidePointer(Widget w,XtPointer unused,XEvent * event,Boolean * continue_to_dispatch)888 static void handleHidePointer(Widget w, XtPointer unused,
889         XEvent *event, Boolean *continue_to_dispatch) {
890     TextWidget tw = (TextWidget) w;
891     ShowHidePointer(tw, True);
892 }
893 
894 /* Restore the pointer if the mouse moves or focus changes */
handleShowPointer(Widget w,XtPointer unused,XEvent * event,Boolean * continue_to_dispatch)895 static void handleShowPointer(Widget w, XtPointer unused,
896         XEvent *event, Boolean *continue_to_dispatch) {
897     TextWidget tw = (TextWidget) w;
898     ShowHidePointer(tw, False);
899 }
900 
ShowHidePointer(TextWidget w,Boolean hidePointer)901 void ShowHidePointer(TextWidget w, Boolean hidePointer)
902 {
903     if (w->text.hidePointer) {
904         if (hidePointer != w->text.textD->pointerHidden) {
905             if (hidePointer) {
906                 /* Don't listen for keypresses any more */
907                 XtRemoveEventHandler((Widget)w, NEDIT_HIDE_CURSOR_MASK, False,
908                         handleHidePointer, (Opaque)NULL);
909                 /* Switch to empty cursor */
910                 XDefineCursor(XtDisplay(w), XtWindow(w), empty_cursor);
911 
912                 w->text.textD->pointerHidden = True;
913 
914                 /* Listen to mouse movement, focus change, and button presses */
915                 XtAddEventHandler((Widget)w, NEDIT_SHOW_CURSOR_MASK,
916                         False, handleShowPointer, (Opaque)NULL);
917             }
918             else {
919                 /* Don't listen to mouse/focus events any more */
920                 XtRemoveEventHandler((Widget)w, NEDIT_SHOW_CURSOR_MASK,
921                         False, handleShowPointer, (Opaque)NULL);
922                 /* Switch to regular cursor */
923                 XUndefineCursor(XtDisplay(w), XtWindow(w));
924 
925                 w->text.textD->pointerHidden = False;
926 
927                 /* Listen for keypresses now */
928                 XtAddEventHandler((Widget)w, NEDIT_HIDE_CURSOR_MASK, False,
929                         handleHidePointer, (Opaque)NULL);
930             }
931         }
932     }
933 }
934 
935 /*
936 ** Widget destroy method
937 */
destroy(TextWidget w)938 static void destroy(TextWidget w)
939 {
940     textBuffer *buf;
941 
942     /* Free the text display and possibly the attached buffer.  The buffer
943        is freed only if after removing all of the modify procs (by calling
944        StopHandlingXSelections and TextDFree) there are no modify procs
945        left */
946     StopHandlingXSelections((Widget)w);
947     buf = w->text.textD->buffer;
948     TextDFree(w->text.textD);
949     if (buf->nModifyProcs == 0)
950     	BufFree(buf);
951 
952     if (w->text.cursorBlinkProcID != 0)
953     	XtRemoveTimeOut(w->text.cursorBlinkProcID);
954     NEditFree(w->text.delimiters);
955     XtRemoveAllCallbacks((Widget)w, textNfocusCallback);
956     XtRemoveAllCallbacks((Widget)w, textNlosingFocusCallback);
957     XtRemoveAllCallbacks((Widget)w, textNcursorMovementCallback);
958     XtRemoveAllCallbacks((Widget)w, textNdragStartCallback);
959     XtRemoveAllCallbacks((Widget)w, textNdragEndCallback);
960 
961 #ifndef NO_XMIM
962     /* Unregister the widget from the input manager */
963     XmImUnregister((Widget)w);
964 #endif
965 }
966 
967 /*
968 ** Widget resize method.  Called when the size of the widget changes
969 */
resize(TextWidget w)970 static void resize(TextWidget w)
971 {
972     XFontStruct *fs = w->text.fontStruct;
973     int height = w->core.height, width = w->core.width;
974     int marginWidth = w->text.marginWidth, marginHeight = w->text.marginHeight;
975     int lineNumAreaWidth = w->text.lineNumCols == 0 ? 0 : w->text.marginWidth +
976 		fs->max_bounds.width * w->text.lineNumCols;
977 
978     w->text.columns = (width - marginWidth*2 - lineNumAreaWidth) /
979     	    fs->max_bounds.width;
980     w->text.rows = (height - marginHeight*2) / (fs->ascent + fs->descent);
981 
982     /* Reject widths and heights less than a character, which the text
983        display can't tolerate.  This is not strictly legal, but I've seen
984        it done in other widgets and it seems to do no serious harm.  NEdit
985        prevents panes from getting smaller than one line, but sometimes
986        splitting windows on Linux 2.0 systems (same Motif, why the change in
987        behavior?), causes one or two resize calls with < 1 line of height.
988        Fixing it here is 100x easier than re-designing textDisp.c */
989     if (w->text.columns < 1) {
990     	w->text.columns = 1;
991     	w->core.width = width = fs->max_bounds.width + marginWidth*2 +
992 		lineNumAreaWidth;
993     }
994     if (w->text.rows < 1) {
995     	w->text.rows = 1;
996     	w->core.height = height = fs->ascent + fs->descent + marginHeight*2;
997     }
998 
999     /* Resize the text display that the widget uses to render text */
1000     TextDResize(w->text.textD, width - marginWidth*2 - lineNumAreaWidth,
1001 	    height - marginHeight*2);
1002 
1003     /* if the window became shorter or narrower, there may be text left
1004        in the bottom or right margin area, which must be cleaned up */
1005     if (XtIsRealized((Widget)w)) {
1006 	XClearArea(XtDisplay(w), XtWindow(w), 0, height-marginHeight,
1007     		width, marginHeight, False);
1008 	XClearArea(XtDisplay(w), XtWindow(w),width-marginWidth,
1009 		0, marginWidth, height, False);
1010     }
1011 }
1012 
1013 /*
1014 ** Widget redisplay method
1015 */
redisplay(TextWidget w,XEvent * event,Region region)1016 static void redisplay(TextWidget w, XEvent *event, Region region)
1017 {
1018     XExposeEvent *e = &event->xexpose;
1019 
1020     TextDRedisplayRect(w->text.textD, e->x, e->y, e->width, e->height);
1021 }
1022 
findGraphicsExposeOrNoExposeEvent(Display * theDisplay,XEvent * event,XPointer arg)1023 static Bool findGraphicsExposeOrNoExposeEvent(Display *theDisplay, XEvent *event, XPointer arg)
1024 {
1025     if ((theDisplay == event->xany.display) &&
1026         (event->type == GraphicsExpose || event->type == NoExpose) &&
1027         ((Widget)arg == XtWindowToWidget(event->xany.display, event->xany.window))) {
1028         return(True);
1029     }
1030     else {
1031         return(False);
1032     }
1033 }
1034 
adjustRectForGraphicsExposeOrNoExposeEvent(TextWidget w,XEvent * event,Boolean * first,int * left,int * top,int * width,int * height)1035 static void adjustRectForGraphicsExposeOrNoExposeEvent(TextWidget w, XEvent *event,
1036                             Boolean *first, int *left, int *top, int *width, int *height)
1037 {
1038     Boolean removeQueueEntry = False;
1039 
1040     if (event->type == GraphicsExpose) {
1041         XGraphicsExposeEvent *e = &event->xgraphicsexpose;
1042         int x = e->x, y = e->y;
1043 
1044         TextDImposeGraphicsExposeTranslation(w->text.textD, &x, &y);
1045         if (*first) {
1046             *left = x;
1047             *top = y;
1048             *width = e->width;
1049             *height = e->height;
1050 
1051             *first = False;
1052         }
1053         else {
1054             int prev_left = *left;
1055             int prev_top = *top;
1056 
1057             *left = min(*left, x);
1058             *top = min(*top, y);
1059             *width = max(prev_left + *width, x + e->width) - *left;
1060             *height = max(prev_top + *height, y + e->height) - *top;
1061         }
1062         if (e->count == 0) {
1063             removeQueueEntry = True;
1064         }
1065     }
1066     else if (event->type == NoExpose) {
1067         removeQueueEntry = True;
1068     }
1069     if (removeQueueEntry) {
1070         TextDPopGraphicExposeQueueEntry(w->text.textD);
1071     }
1072 }
1073 
redisplayGE(TextWidget w,XtPointer client_data,XEvent * event,Boolean * continue_to_dispatch_return)1074 static void redisplayGE(TextWidget w, XtPointer client_data,
1075                     XEvent *event, Boolean *continue_to_dispatch_return)
1076 {
1077     if (event->type == GraphicsExpose || event->type == NoExpose) {
1078         HandleAllPendingGraphicsExposeNoExposeEvents(w, event);
1079     }
1080 }
1081 
HandleAllPendingGraphicsExposeNoExposeEvents(TextWidget w,XEvent * event)1082 void HandleAllPendingGraphicsExposeNoExposeEvents(TextWidget w, XEvent *event)
1083 {
1084     XEvent foundEvent;
1085     int left;
1086     int top;
1087     int width;
1088     int height;
1089     Boolean invalidRect = True;
1090 
1091     if (event) {
1092         adjustRectForGraphicsExposeOrNoExposeEvent(w, event, &invalidRect, &left, &top, &width, &height);
1093     }
1094     while (XCheckIfEvent(XtDisplay(w), &foundEvent, findGraphicsExposeOrNoExposeEvent, (XPointer)w)) {
1095         adjustRectForGraphicsExposeOrNoExposeEvent(w, &foundEvent, &invalidRect, &left, &top, &width, &height);
1096     }
1097     if (!invalidRect) {
1098         TextDRedisplayRect(w->text.textD, left, top, width, height);
1099     }
1100 }
1101 
1102 /*
1103 ** Widget setValues method
1104 */
setValues(TextWidget current,TextWidget request,TextWidget new)1105 static Boolean setValues(TextWidget current, TextWidget request,
1106 	TextWidget new)
1107 {
1108     Boolean redraw = False, reconfigure = False;
1109 
1110     if (new->text.overstrike != current->text.overstrike) {
1111     	if (current->text.textD->cursorStyle == BLOCK_CURSOR)
1112     	    TextDSetCursorStyle(current->text.textD,
1113     	    	    current->text.heavyCursor ? HEAVY_CURSOR : NORMAL_CURSOR);
1114     	else if (current->text.textD->cursorStyle == NORMAL_CURSOR ||
1115     		current->text.textD->cursorStyle == HEAVY_CURSOR)
1116     	    TextDSetCursorStyle(current->text.textD, BLOCK_CURSOR);
1117     }
1118 
1119     if (new->text.fontStruct != current->text.fontStruct) {
1120 	if (new->text.lineNumCols != 0)
1121 	    reconfigure = True;
1122     	TextDSetFont(current->text.textD, new->text.fontStruct);
1123     }
1124 
1125     if (new->text.wrapMargin != current->text.wrapMargin ||
1126     	    new->text.continuousWrap != current->text.continuousWrap)
1127     	TextDSetWrapMode(current->text.textD, new->text.continuousWrap,
1128     	    	new->text.wrapMargin);
1129 
1130     /* When delimiters are changed, copy the memory, so that the caller
1131        doesn't have to manage it, and add mandatory delimiters blank,
1132        tab, and newline to the list */
1133     if (new->text.delimiters != current->text.delimiters) {
1134 	char *delimiters = (char*)NEditMalloc(strlen(new->text.delimiters) + 4);
1135     	NEditFree(current->text.delimiters);
1136 	sprintf(delimiters, "%s%s", " \t\n", new->text.delimiters);
1137 	new->text.delimiters = delimiters;
1138     }
1139 
1140     /* Setting the lineNumCols resource tells the text widget to hide or
1141        show, or change the number of columns of the line number display,
1142        which requires re-organizing the x coordinates of both the line
1143        number display and the main text display */
1144     if (new->text.lineNumCols != current->text.lineNumCols || reconfigure)
1145     {
1146         int marginWidth = new->text.marginWidth;
1147         int charWidth = new->text.fontStruct->max_bounds.width;
1148         int lineNumCols = new->text.lineNumCols;
1149         if (lineNumCols == 0)
1150         {
1151             TextDSetLineNumberArea(new->text.textD, 0, 0, marginWidth);
1152             new->text.columns = (new->core.width - marginWidth*2) / charWidth;
1153         } else
1154         {
1155             TextDSetLineNumberArea(new->text.textD, marginWidth,
1156                     charWidth * lineNumCols,
1157                     2*marginWidth + charWidth * lineNumCols);
1158             new->text.columns = (new->core.width - marginWidth*3 - charWidth
1159                     * lineNumCols) / charWidth;
1160         }
1161     }
1162 
1163     if (new->text.backlightCharTypes != current->text.backlightCharTypes)
1164     {
1165         TextDSetupBGClasses((Widget)new, new->text.backlightCharTypes,
1166                 &new->text.textD->bgClassPixel, &new->text.textD->bgClass,
1167                 new->text.textD->bgPixel);
1168         redraw = True;
1169     }
1170 
1171     return redraw;
1172 }
1173 
1174 /*
1175 ** Widget realize method
1176 */
realize(Widget w,XtValueMask * valueMask,XSetWindowAttributes * attributes)1177 static void realize(Widget w, XtValueMask *valueMask,
1178 	XSetWindowAttributes *attributes)
1179 {
1180     /* Set bit gravity window attribute.  This saves a full blank and redraw
1181        on window resizing */
1182     *valueMask |= CWBitGravity;
1183     attributes->bit_gravity = NorthWestGravity;
1184 
1185     /* Continue with realize method from superclass */
1186     (xmPrimitiveClassRec.core_class.realize)(w, valueMask, attributes);
1187 }
1188 
1189 /*
1190 ** Widget query geometry method ... unless asked to negotiate a different size simply return current size.
1191 */
queryGeometry(Widget w,XtWidgetGeometry * proposed,XtWidgetGeometry * answer)1192 static XtGeometryResult queryGeometry(Widget w, XtWidgetGeometry *proposed,
1193 	XtWidgetGeometry *answer)
1194 {
1195     TextWidget tw = (TextWidget)w;
1196 
1197     int curHeight = tw->core.height;
1198     int curWidth = tw->core.width;
1199     XFontStruct *fs = tw->text.textD->fontStruct;
1200     int fontWidth = fs->max_bounds.width;
1201     int fontHeight = fs->ascent + fs->descent;
1202     int marginHeight = tw->text.marginHeight;
1203     int propWidth = (proposed->request_mode & CWWidth) ? proposed->width : 0;
1204     int propHeight = (proposed->request_mode & CWHeight) ? proposed->height : 0;
1205 
1206     answer->request_mode = CWHeight | CWWidth;
1207 
1208     if(proposed->request_mode & CWWidth)
1209        /* Accept a width no smaller than 10 chars */
1210        answer->width = max(fontWidth * 10, proposed->width);
1211     else
1212        answer->width = curWidth;
1213 
1214     if(proposed->request_mode & CWHeight)
1215        /* Accept a height no smaller than an exact multiple of the line height
1216           and at least one line high */
1217        answer->height = max(1, ((propHeight - 2*marginHeight) / fontHeight)) *
1218     	    fontHeight + 2*marginHeight;
1219     else
1220        answer->height = curHeight;
1221 
1222     /*printf("propWidth %d, propHeight %d, ansWidth %d, ansHeight %d\n",
1223     	    propWidth, propHeight, answer->width, answer->height);*/
1224     if (propWidth == answer->width && propHeight == answer->height)
1225     	return XtGeometryYes;
1226     else if (answer->width == curWidth && answer->height == curHeight)
1227     	return XtGeometryNo;
1228     else
1229     	return XtGeometryAlmost;
1230 }
1231 
1232 /*
1233 ** Set the text buffer which this widget will display and interact with.
1234 ** The currently attached buffer is automatically freed, ONLY if it has
1235 ** no additional modify procs attached (as it would if it were being
1236 ** displayed by another text widget).
1237 */
TextSetBuffer(Widget w,textBuffer * buffer)1238 void TextSetBuffer(Widget w, textBuffer *buffer)
1239 {
1240     textBuffer *oldBuf = ((TextWidget)w)->text.textD->buffer;
1241 
1242     StopHandlingXSelections(w);
1243     TextDSetBuffer(((TextWidget)w)->text.textD, buffer);
1244     if (oldBuf->nModifyProcs == 0)
1245     	BufFree(oldBuf);
1246 }
1247 
1248 /*
1249 ** Get the buffer associated with this text widget.  Note that attaching
1250 ** additional modify callbacks to the buffer will prevent it from being
1251 ** automatically freed when the widget is destroyed.
1252 */
TextGetBuffer(Widget w)1253 textBuffer *TextGetBuffer(Widget w)
1254 {
1255     return ((TextWidget)w)->text.textD->buffer;
1256 }
1257 
1258 /*
1259 ** Translate a line number and column into a position
1260 */
TextLineAndColToPos(Widget w,int lineNum,int column)1261 int TextLineAndColToPos(Widget w, int lineNum, int column)
1262 {
1263     return TextDLineAndColToPos(((TextWidget)w)->text.textD, lineNum, column );
1264 }
1265 
1266 /*
1267 ** Translate a position into a line number (if the position is visible,
1268 ** if it's not, return False
1269 */
TextPosToLineAndCol(Widget w,int pos,int * lineNum,int * column)1270 int TextPosToLineAndCol(Widget w, int pos, int *lineNum, int *column)
1271 {
1272     return TextDPosToLineAndCol(((TextWidget)w)->text.textD, pos, lineNum,
1273     	    column);
1274 }
1275 
1276 /*
1277 ** Translate a buffer text position to the XY location where the center
1278 ** of the cursor would be positioned to point to that character.  Returns
1279 ** False if the position is not displayed because it is VERTICALLY out
1280 ** of view.  If the position is horizontally out of view, returns the
1281 ** x coordinate where the position would be if it were visible.
1282 */
TextPosToXY(Widget w,int pos,int * x,int * y)1283 int TextPosToXY(Widget w, int pos, int *x, int *y)
1284 {
1285     return TextDPositionToXY(((TextWidget)w)->text.textD, pos, x, y);
1286 }
1287 
1288 /*
1289 ** Return the cursor position
1290 */
TextGetCursorPos(Widget w)1291 int TextGetCursorPos(Widget w)
1292 {
1293     return TextDGetInsertPosition(((TextWidget)w)->text.textD);
1294 }
1295 
1296 /*
1297 ** Set the cursor position
1298 */
TextSetCursorPos(Widget w,int pos)1299 void TextSetCursorPos(Widget w, int pos)
1300 {
1301     TextDSetInsertPosition(((TextWidget)w)->text.textD, pos);
1302     checkAutoShowInsertPos(w);
1303     callCursorMovementCBs(w, NULL);
1304 
1305 }
1306 
1307 /*
1308 ** Return the horizontal and vertical scroll positions of the widget
1309 */
TextGetScroll(Widget w,int * topLineNum,int * horizOffset)1310 void TextGetScroll(Widget w, int *topLineNum, int *horizOffset)
1311 {
1312     TextDGetScroll(((TextWidget)w)->text.textD, topLineNum, horizOffset);
1313 }
1314 
1315 /*
1316 ** Set the horizontal and vertical scroll positions of the widget
1317 */
TextSetScroll(Widget w,int topLineNum,int horizOffset)1318 void TextSetScroll(Widget w, int topLineNum, int horizOffset)
1319 {
1320     TextDSetScroll(((TextWidget)w)->text.textD, topLineNum, horizOffset);
1321 }
1322 
TextGetMinFontWidth(Widget w,Boolean considerStyles)1323 int TextGetMinFontWidth(Widget w, Boolean considerStyles)
1324 {
1325     return(TextDMinFontWidth(((TextWidget)w)->text.textD, considerStyles));
1326 }
1327 
TextGetMaxFontWidth(Widget w,Boolean considerStyles)1328 int TextGetMaxFontWidth(Widget w, Boolean considerStyles)
1329 {
1330     return(TextDMaxFontWidth(((TextWidget)w)->text.textD, considerStyles));
1331 }
1332 
1333 /*
1334 ** Set this widget to be the owner of selections made in it's attached
1335 ** buffer (text buffers may be shared among several text widgets).
1336 */
TextHandleXSelections(Widget w)1337 void TextHandleXSelections(Widget w)
1338 {
1339     HandleXSelections(w);
1340 }
1341 
TextPasteClipboard(Widget w,Time time)1342 void TextPasteClipboard(Widget w, Time time)
1343 {
1344     cancelDrag(w);
1345     if (checkReadOnly(w))
1346 	return;
1347     TakeMotifDestination(w, time);
1348     InsertClipboard(w, False);
1349     callCursorMovementCBs(w, NULL);
1350 }
1351 
TextColPasteClipboard(Widget w,Time time)1352 void TextColPasteClipboard(Widget w, Time time)
1353 {
1354     cancelDrag(w);
1355     if (checkReadOnly(w))
1356 	return;
1357     TakeMotifDestination(w, time);
1358     InsertClipboard(w, True);
1359     callCursorMovementCBs(w, NULL);
1360 }
1361 
TextCopyClipboard(Widget w,Time time)1362 void TextCopyClipboard(Widget w, Time time)
1363 {
1364     cancelDrag(w);
1365     if (!((TextWidget)w)->text.textD->buffer->primary.selected) {
1366     	XBell(XtDisplay(w), 0);
1367     	return;
1368     }
1369     CopyToClipboard(w, time);
1370 }
1371 
TextCutClipboard(Widget w,Time time)1372 void TextCutClipboard(Widget w, Time time)
1373 {
1374     textDisp *textD = ((TextWidget)w)->text.textD;
1375 
1376     cancelDrag(w);
1377     if (checkReadOnly(w))
1378 	return;
1379     if (!textD->buffer->primary.selected) {
1380     	XBell(XtDisplay(w), 0);
1381     	return;
1382     }
1383     TakeMotifDestination(w, time);
1384     CopyToClipboard (w, time);
1385     BufRemoveSelected(textD->buffer);
1386     TextDSetInsertPosition(textD, textD->buffer->cursorPosHint);
1387     checkAutoShowInsertPos(w);
1388 }
1389 
TextFirstVisibleLine(Widget w)1390 int TextFirstVisibleLine(Widget w)
1391 {
1392     return(((TextWidget)w)->text.textD->topLineNum);
1393 }
1394 
TextNumVisibleLines(Widget w)1395 int TextNumVisibleLines(Widget w)
1396 {
1397     return(((TextWidget)w)->text.textD->nVisibleLines);
1398 }
1399 
TextVisibleWidth(Widget w)1400 int TextVisibleWidth(Widget w)
1401 {
1402     return(((TextWidget)w)->text.textD->width);
1403 }
1404 
TextFirstVisiblePos(Widget w)1405 int TextFirstVisiblePos(Widget w)
1406 {
1407     return ((TextWidget)w)->text.textD->firstChar;
1408 }
1409 
TextLastVisiblePos(Widget w)1410 int TextLastVisiblePos(Widget w)
1411 {
1412     return ((TextWidget)w)->text.textD->lastChar;
1413 }
1414 
1415 /*
1416 ** Insert text "chars" at the cursor position, respecting pending delete
1417 ** selections, overstrike, and handling cursor repositioning as if the text
1418 ** had been typed.  If autoWrap is on wraps the text to fit within the wrap
1419 ** margin, auto-indenting where the line was wrapped (but nowhere else).
1420 ** "allowPendingDelete" controls whether primary selections in the widget are
1421 ** treated as pending delete selections (True), or ignored (False). "event"
1422 ** is optional and is just passed on to the cursor movement callbacks.
1423 */
TextInsertAtCursor(Widget w,char * chars,XEvent * event,int allowPendingDelete,int allowWrap)1424 void TextInsertAtCursor(Widget w, char *chars, XEvent *event,
1425     	int allowPendingDelete, int allowWrap)
1426 {
1427     int wrapMargin, colNum, lineStartPos, cursorPos;
1428     char *c, *lineStartText, *wrappedText;
1429     TextWidget tw = (TextWidget)w;
1430     textDisp *textD = tw->text.textD;
1431     textBuffer *buf = textD->buffer;
1432     int fontWidth = textD->fontStruct->max_bounds.width;
1433     int replaceSel, singleLine, breakAt = 0;
1434 
1435     /* Don't wrap if auto-wrap is off or suppressed, or it's just a newline */
1436     if (!allowWrap || !tw->text.autoWrap ||
1437 	    (chars[0] == '\n' && chars[1] == '\0')) {
1438     	simpleInsertAtCursor(w, chars, event, allowPendingDelete);
1439         return;
1440     }
1441 
1442     /* If this is going to be a pending delete operation, the real insert
1443        position is the start of the selection.  This will make rectangular
1444        selections wrap strangely, but this routine should rarely be used for
1445        them, and even more rarely when they need to be wrapped. */
1446     replaceSel = allowPendingDelete && pendingSelection(w);
1447     cursorPos = replaceSel ? buf->primary.start : TextDGetInsertPosition(textD);
1448 
1449     /* If the text is only one line and doesn't need to be wrapped, just insert
1450        it and be done (for efficiency only, this routine is called for each
1451        character typed). (Of course, it may not be significantly more efficient
1452        than the more general code below it, so it may be a waste of time!) */
1453     wrapMargin = tw->text.wrapMargin != 0 ? tw->text.wrapMargin :
1454             textD->width / fontWidth;
1455     lineStartPos = BufStartOfLine(buf, cursorPos);
1456     colNum = BufCountDispChars(buf, lineStartPos, cursorPos);
1457     for (c=chars; *c!='\0' && *c!='\n'; c++)
1458         colNum += BufCharWidth(*c, colNum, buf->tabDist, buf->nullSubsChar);
1459     singleLine = *c == '\0';
1460     if (colNum < wrapMargin && singleLine) {
1461     	simpleInsertAtCursor(w, chars, event, True);
1462         return;
1463     }
1464 
1465     /* Wrap the text */
1466     lineStartText = BufGetRange(buf, lineStartPos, cursorPos);
1467     wrappedText = wrapText(tw, lineStartText, chars, lineStartPos, wrapMargin,
1468     	    replaceSel ? NULL : &breakAt);
1469     NEditFree(lineStartText);
1470 
1471     /* Insert the text.  Where possible, use TextDInsert which is optimized
1472        for less redraw. */
1473     if (replaceSel) {
1474     	BufReplaceSelected(buf, wrappedText);
1475    	TextDSetInsertPosition(textD, buf->cursorPosHint);
1476     } else if (tw->text.overstrike) {
1477     	if (breakAt == 0 && singleLine)
1478     	    TextDOverstrike(textD, wrappedText);
1479     	else {
1480 	    BufReplace(buf, cursorPos-breakAt, cursorPos, wrappedText);
1481 	    TextDSetInsertPosition(textD, buf->cursorPosHint);
1482     	}
1483     } else {
1484     	if (breakAt == 0) {
1485     	    TextDInsert(textD, wrappedText);
1486     	} else {
1487     	    BufReplace(buf, cursorPos-breakAt, cursorPos, wrappedText);
1488     	    TextDSetInsertPosition(textD, buf->cursorPosHint);
1489     	}
1490     }
1491     NEditFree(wrappedText);
1492     checkAutoShowInsertPos(w);
1493     callCursorMovementCBs(w, event);
1494 }
1495 
1496 /*
1497 ** Fetch text from the widget's buffer, adding wrapping newlines to emulate
1498 ** effect acheived by wrapping in the text display in continuous wrap mode.
1499 */
TextGetWrapped(Widget w,int startPos,int endPos,int * outLen)1500 char *TextGetWrapped(Widget w, int startPos, int endPos, int *outLen)
1501 {
1502     textDisp *textD = ((TextWidget)w)->text.textD;
1503     textBuffer *buf = textD->buffer;
1504     textBuffer *outBuf;
1505     int fromPos, toPos, outPos;
1506     char c, *outString;
1507 
1508     if (!((TextWidget)w)->text.continuousWrap || startPos == endPos) {
1509     	*outLen = endPos - startPos;
1510     	return BufGetRange(buf, startPos, endPos);
1511     }
1512 
1513     /* Create a text buffer with a good estimate of the size that adding
1514        newlines will expand it to.  Since it's a text buffer, if we guess
1515        wrong, it will fail softly, and simply expand the size */
1516     outBuf = BufCreatePreallocated((endPos-startPos) + (endPos-startPos)/5);
1517     outPos = 0;
1518 
1519     /* Go (displayed) line by line through the buffer, adding newlines where
1520        the text is wrapped at some character other than an existing newline */
1521     fromPos = startPos;
1522     toPos = TextDCountForwardNLines(textD, startPos, 1, False);
1523     while (toPos < endPos) {
1524     	BufCopyFromBuf(buf, outBuf, fromPos, toPos, outPos);
1525     	outPos += toPos - fromPos;
1526     	c = BufGetCharacter(outBuf, outPos-1);
1527     	if (c == ' ' || c == '\t')
1528     	    BufReplace(outBuf, outPos-1, outPos, "\n");
1529     	else if (c != '\n') {
1530     	    BufInsert(outBuf, outPos, "\n");
1531     	    outPos++;
1532     	}
1533     	fromPos = toPos;
1534     	toPos = TextDCountForwardNLines(textD, fromPos, 1, True);
1535     }
1536     BufCopyFromBuf(buf, outBuf, fromPos, endPos, outPos);
1537 
1538     /* return the contents of the output buffer as a string */
1539     outString = BufGetAll(outBuf);
1540     *outLen = outBuf->length;
1541     BufFree(outBuf);
1542     return outString;
1543 }
1544 
1545 /*
1546 ** Return the (statically allocated) action table for menu item actions.
1547 **
1548 ** Warning: This routine can only be used before the first text widget is
1549 ** created!  After that, apparently, Xt takes over the table and overwrites
1550 ** it with its own version.  XtGetActionList is preferable, but is not
1551 ** available before X11R5.
1552 */
TextGetActions(int * nActions)1553 XtActionsRec *TextGetActions(int *nActions)
1554 {
1555     *nActions = XtNumber(actionsList);
1556     return actionsList;
1557 }
1558 
grabFocusAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)1559 static void grabFocusAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
1560 {
1561     XButtonEvent *e = &event->xbutton;
1562     TextWidget tw = (TextWidget)w;
1563     textDisp *textD = tw->text.textD;
1564     Time lastBtnDown = tw->text.lastBtnDown;
1565     int row, column;
1566 
1567     /* Indicate state for future events, PRIMARY_CLICKED indicates that
1568        the proper initialization has been done for primary dragging and/or
1569        multi-clicking.  Also record the timestamp for multi-click processing */
1570     tw->text.dragState = PRIMARY_CLICKED;
1571     tw->text.lastBtnDown = e->time;
1572 
1573     /* Become owner of the MOTIF_DESTINATION selection, making this widget
1574        the designated recipient of secondary quick actions in Motif XmText
1575        widgets and in other NEdit text widgets */
1576     TakeMotifDestination(w, e->time);
1577 
1578     /* Check for possible multi-click sequence in progress */
1579     if (tw->text.multiClickState != NO_CLICKS) {
1580 	if (e->time < lastBtnDown + XtGetMultiClickTime(XtDisplay(w))) {
1581     	    if (tw->text.multiClickState == ONE_CLICK) {
1582     		selectWord(w, e->x);
1583     		callCursorMovementCBs(w, event);
1584 		return;
1585     	    } else if (tw->text.multiClickState == TWO_CLICKS) {
1586     		selectLine(w);
1587     		callCursorMovementCBs(w, event);
1588     		return;
1589     	    } else if (tw->text.multiClickState == THREE_CLICKS) {
1590     		BufSelect(textD->buffer, 0, textD->buffer->length);
1591     		return;
1592     	    } else if (tw->text.multiClickState > THREE_CLICKS)
1593     		tw->text.multiClickState = NO_CLICKS;
1594 	} else
1595     	    tw->text.multiClickState = NO_CLICKS;
1596     }
1597 
1598     /* Clear any existing selections */
1599     BufUnselect(textD->buffer);
1600 
1601     /* Move the cursor to the pointer location */
1602     moveDestinationAP(w, event, args, nArgs);
1603 
1604     /* Record the site of the initial button press and the initial character
1605        position so subsequent motion events and clicking can decide when and
1606        where to begin a primary selection */
1607     tw->text.btnDownX = e->x;
1608     tw->text.btnDownY = e->y;
1609     tw->text.anchor = TextDGetInsertPosition(textD);
1610     TextDXYToUnconstrainedPosition(textD, e->x, e->y, &row, &column);
1611     column = TextDOffsetWrappedColumn(textD, row, column);
1612     tw->text.rectAnchor = column;
1613 }
1614 
moveDestinationAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)1615 static void moveDestinationAP(Widget w, XEvent *event, String *args,
1616 	Cardinal *nArgs)
1617 {
1618     XButtonEvent *e = &event->xbutton;
1619     textDisp *textD = ((TextWidget)w)->text.textD;
1620 
1621     /* Get input focus */
1622     XmProcessTraversal(w, XmTRAVERSE_CURRENT);
1623 
1624     /* Move the cursor */
1625     TextDSetInsertPosition(textD, TextDXYToPosition(textD, e->x, e->y));
1626     checkAutoShowInsertPos(w);
1627     callCursorMovementCBs(w, event);
1628 }
1629 
extendAdjustAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)1630 static void extendAdjustAP(Widget w, XEvent *event, String *args,
1631 	Cardinal *nArgs)
1632 {
1633     TextWidget tw = (TextWidget)w;
1634     XMotionEvent *e = &event->xmotion;
1635     int dragState = tw->text.dragState;
1636     int rectDrag = hasKey("rect", args, nArgs);
1637 
1638     /* Make sure the proper initialization was done on mouse down */
1639     if (dragState != PRIMARY_DRAG && dragState != PRIMARY_CLICKED &&
1640     	    dragState != PRIMARY_RECT_DRAG)
1641     	return;
1642 
1643     /* If the selection hasn't begun, decide whether the mouse has moved
1644        far enough from the initial mouse down to be considered a drag */
1645     if (tw->text.dragState == PRIMARY_CLICKED) {
1646     	if (abs(e->x - tw->text.btnDownX) > SELECT_THRESHOLD ||
1647     	    	abs(e->y - tw->text.btnDownY) > SELECT_THRESHOLD)
1648     	    tw->text.dragState = rectDrag ? PRIMARY_RECT_DRAG : PRIMARY_DRAG;
1649     	else
1650     	    return;
1651     }
1652 
1653     /* If "rect" argument has appeared or disappeared, keep dragState up
1654        to date about which type of drag this is */
1655     tw->text.dragState = rectDrag ? PRIMARY_RECT_DRAG : PRIMARY_DRAG;
1656 
1657     /* Record the new position for the autoscrolling timer routine, and
1658        engage or disengage the timer if the mouse is in/out of the window */
1659     checkAutoScroll(tw, e->x, e->y);
1660 
1661     /* Adjust the selection and move the cursor */
1662     adjustSelection(tw, e->x, e->y);
1663 }
1664 
extendStartAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)1665 static void extendStartAP(Widget w, XEvent *event, String *args,
1666 	Cardinal *nArgs)
1667 {
1668     XMotionEvent *e = &event->xmotion;
1669     textDisp *textD = ((TextWidget)w)->text.textD;
1670     textBuffer *buf = textD->buffer;
1671     selection *sel = &buf->primary;
1672     int anchor, rectAnchor, anchorLineStart, newPos, row, column;
1673 
1674     /* Find the new anchor point for the rest of this drag operation */
1675     newPos = TextDXYToPosition(textD, e->x, e->y);
1676     TextDXYToUnconstrainedPosition(textD, e->x, e->y, &row, &column);
1677     column = TextDOffsetWrappedColumn(textD, row, column);
1678     if (sel->selected) {
1679     	if (sel->rectangular) {
1680     	    rectAnchor = column < (sel->rectEnd + sel->rectStart) / 2 ?
1681     	    	    sel->rectEnd : sel->rectStart;
1682     	    anchorLineStart = BufStartOfLine(buf, newPos <
1683     	    	    (sel->end + sel->start) / 2 ? sel->end : sel->start);
1684     	    anchor = BufCountForwardDispChars(buf, anchorLineStart, rectAnchor);
1685     	} else {
1686     	    if (abs(newPos - sel->start) < abs(newPos - sel->end))
1687     		anchor = sel->end;
1688     	    else
1689     		anchor = sel->start;
1690     	    anchorLineStart = BufStartOfLine(buf, anchor);
1691     	    rectAnchor = BufCountDispChars(buf, anchorLineStart, anchor);
1692     	}
1693     } else {
1694     	anchor = TextDGetInsertPosition(textD);
1695     	anchorLineStart = BufStartOfLine(buf, anchor);
1696     	rectAnchor = BufCountDispChars(buf, anchorLineStart, anchor);
1697     }
1698     ((TextWidget)w)->text.anchor = anchor;
1699     ((TextWidget)w)->text.rectAnchor = rectAnchor;
1700 
1701     /* Make the new selection */
1702     if (hasKey("rect", args, nArgs))
1703 	BufRectSelect(buf, BufStartOfLine(buf, min(anchor, newPos)),
1704 		BufEndOfLine(buf, max(anchor, newPos)),
1705 		min(rectAnchor, column), max(rectAnchor, column));
1706     else
1707     	BufSelect(buf, min(anchor, newPos), max(anchor, newPos));
1708 
1709     /* Never mind the motion threshold, go right to dragging since
1710        extend-start is unambiguously the start of a selection */
1711     ((TextWidget)w)->text.dragState = PRIMARY_DRAG;
1712 
1713     /* Don't do by-word or by-line adjustment, just by character */
1714     ((TextWidget)w)->text.multiClickState = NO_CLICKS;
1715 
1716     /* Move the cursor */
1717     TextDSetInsertPosition(textD, newPos);
1718     callCursorMovementCBs(w, event);
1719 }
1720 
extendEndAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)1721 static void extendEndAP(Widget w, XEvent *event, String *args,
1722 	Cardinal *nArgs)
1723 {
1724     XButtonEvent *e = &event->xbutton;
1725     TextWidget tw = (TextWidget)w;
1726 
1727     if (tw->text.dragState == PRIMARY_CLICKED &&
1728     	    tw->text.lastBtnDown <= e->time + XtGetMultiClickTime(XtDisplay(w)))
1729     	tw->text.multiClickState++;
1730     endDrag(w);
1731 }
1732 
processCancelAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)1733 static void processCancelAP(Widget w, XEvent *event, String *args,
1734 	Cardinal *nArgs)
1735 {
1736     int dragState = ((TextWidget)w)->text.dragState;
1737     textBuffer *buf = ((TextWidget)w)->text.textD->buffer;
1738     textDisp *textD = ((TextWidget)w)->text.textD;
1739 
1740     /* If there's a calltip displayed, kill it. */
1741     TextDKillCalltip(textD, 0);
1742 
1743     if (dragState == PRIMARY_DRAG || dragState == PRIMARY_RECT_DRAG)
1744     	BufUnselect(buf);
1745     cancelDrag(w);
1746 }
1747 
secondaryStartAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)1748 static void secondaryStartAP(Widget w, XEvent *event, String *args,
1749 	Cardinal *nArgs)
1750 {
1751     XMotionEvent *e = &event->xmotion;
1752     textDisp *textD = ((TextWidget)w)->text.textD;
1753     textBuffer *buf = textD->buffer;
1754     selection *sel = &buf->secondary;
1755     int anchor, pos, row, column;
1756 
1757     /* Find the new anchor point and make the new selection */
1758     pos = TextDXYToPosition(textD, e->x, e->y);
1759     if (sel->selected) {
1760     	if (abs(pos - sel->start) < abs(pos - sel->end))
1761     	    anchor = sel->end;
1762     	else
1763     	    anchor = sel->start;
1764     	BufSecondarySelect(buf, anchor, pos);
1765     } else
1766     	anchor = pos;
1767 
1768     /* Record the site of the initial button press and the initial character
1769        position so subsequent motion events can decide when to begin a
1770        selection, (and where the selection began) */
1771     ((TextWidget)w)->text.btnDownX = e->x;
1772     ((TextWidget)w)->text.btnDownY = e->y;
1773     ((TextWidget)w)->text.anchor = pos;
1774     TextDXYToUnconstrainedPosition(textD, e->x, e->y, &row, &column);
1775     column = TextDOffsetWrappedColumn(textD, row, column);
1776     ((TextWidget)w)->text.rectAnchor = column;
1777     ((TextWidget)w)->text.dragState = SECONDARY_CLICKED;
1778 }
1779 
secondaryOrDragStartAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)1780 static void secondaryOrDragStartAP(Widget w, XEvent *event, String *args,
1781 	Cardinal *nArgs)
1782 {
1783     XMotionEvent *e = &event->xmotion;
1784     textDisp *textD = ((TextWidget)w)->text.textD;
1785     textBuffer *buf = textD->buffer;
1786 
1787     /* If the click was outside of the primary selection, this is not
1788        a drag, start a secondary selection */
1789     if (!buf->primary.selected || !TextDInSelection(textD, e->x, e->y)) {
1790     	secondaryStartAP(w, event, args, nArgs);
1791     	return;
1792     }
1793 
1794     if (checkReadOnly(w))
1795 	return;
1796 
1797     /* Record the site of the initial button press and the initial character
1798        position so subsequent motion events can decide when to begin a
1799        drag, and where to drag to */
1800     ((TextWidget)w)->text.btnDownX = e->x;
1801     ((TextWidget)w)->text.btnDownY = e->y;
1802     ((TextWidget)w)->text.dragState = CLICKED_IN_SELECTION;
1803 }
1804 
secondaryAdjustAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)1805 static void secondaryAdjustAP(Widget w, XEvent *event, String *args,
1806 	Cardinal *nArgs)
1807 {
1808     TextWidget tw = (TextWidget)w;
1809     XMotionEvent *e = &event->xmotion;
1810     int dragState = tw->text.dragState;
1811     int rectDrag = hasKey("rect", args, nArgs);
1812 
1813     /* Make sure the proper initialization was done on mouse down */
1814     if (dragState != SECONDARY_DRAG && dragState != SECONDARY_RECT_DRAG &&
1815     	    dragState != SECONDARY_CLICKED)
1816     	return;
1817 
1818     /* If the selection hasn't begun, decide whether the mouse has moved
1819        far enough from the initial mouse down to be considered a drag */
1820     if (tw->text.dragState == SECONDARY_CLICKED) {
1821     	if (abs(e->x - tw->text.btnDownX) > SELECT_THRESHOLD ||
1822     	    	abs(e->y - tw->text.btnDownY) > SELECT_THRESHOLD)
1823     	    tw->text.dragState = rectDrag ? SECONDARY_RECT_DRAG: SECONDARY_DRAG;
1824     	else
1825     	    return;
1826     }
1827 
1828     /* If "rect" argument has appeared or disappeared, keep dragState up
1829        to date about which type of drag this is */
1830     tw->text.dragState = rectDrag ? SECONDARY_RECT_DRAG : SECONDARY_DRAG;
1831 
1832     /* Record the new position for the autoscrolling timer routine, and
1833        engage or disengage the timer if the mouse is in/out of the window */
1834     checkAutoScroll(tw, e->x, e->y);
1835 
1836     /* Adjust the selection */
1837     adjustSecondarySelection(tw, e->x, e->y);
1838 }
1839 
secondaryOrDragAdjustAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)1840 static void secondaryOrDragAdjustAP(Widget w, XEvent *event, String *args,
1841 	Cardinal *nArgs)
1842 {
1843     TextWidget tw = (TextWidget)w;
1844     XMotionEvent *e = &event->xmotion;
1845     int dragState = tw->text.dragState;
1846 
1847     /* Only dragging of blocks of text is handled in this action proc.
1848        Otherwise, defer to secondaryAdjust to handle the rest */
1849     if (dragState != CLICKED_IN_SELECTION && dragState != PRIMARY_BLOCK_DRAG) {
1850      	secondaryAdjustAP(w, event, args, nArgs);
1851     	return;
1852     }
1853 
1854     /* Decide whether the mouse has moved far enough from the
1855        initial mouse down to be considered a drag */
1856     if (tw->text.dragState == CLICKED_IN_SELECTION) {
1857     	if (abs(e->x - tw->text.btnDownX) > SELECT_THRESHOLD ||
1858     	    	abs(e->y - tw->text.btnDownY) > SELECT_THRESHOLD)
1859     	    BeginBlockDrag(tw);
1860     	else
1861     	    return;
1862     }
1863 
1864     /* Record the new position for the autoscrolling timer routine, and
1865        engage or disengage the timer if the mouse is in/out of the window */
1866     checkAutoScroll(tw, e->x, e->y);
1867 
1868     /* Adjust the selection */
1869     BlockDragSelection(tw, e->x, e->y, hasKey("overlay", args, nArgs) ?
1870     	(hasKey("copy", args, nArgs) ? DRAG_OVERLAY_COPY : DRAG_OVERLAY_MOVE) :
1871     	(hasKey("copy", args, nArgs) ? DRAG_COPY : DRAG_MOVE));
1872 }
1873 
copyToAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)1874 static void copyToAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
1875 {
1876     XButtonEvent *e = &event->xbutton;
1877     TextWidget tw = (TextWidget)w;
1878     textDisp *textD = tw->text.textD;
1879     int dragState = tw->text.dragState;
1880     textBuffer *buf = textD->buffer;
1881     selection *secondary = &buf->secondary, *primary = &buf->primary;
1882     int rectangular = secondary->rectangular;
1883     char *textToCopy;
1884     int insertPos, lineStart, column;
1885 
1886     endDrag(w);
1887     if (!((dragState == SECONDARY_DRAG && secondary->selected) ||
1888     	    (dragState == SECONDARY_RECT_DRAG && secondary->selected) ||
1889     	    dragState == SECONDARY_CLICKED || dragState == NOT_CLICKED))
1890         return;
1891     if (!(secondary->selected && !((TextWidget)w)->text.motifDestOwner)) {
1892 	if (checkReadOnly(w)) {
1893 	    BufSecondaryUnselect(buf);
1894 	    return;
1895 	}
1896     }
1897     if (secondary->selected) {
1898     	if (tw->text.motifDestOwner) {
1899 	    TextDBlankCursor(textD);
1900 	    textToCopy = BufGetSecSelectText(buf);
1901 	    if (primary->selected && rectangular) {
1902     		insertPos = TextDGetInsertPosition(textD);
1903     		BufReplaceSelected(buf, textToCopy);
1904     		TextDSetInsertPosition(textD, buf->cursorPosHint);
1905 	    } else if (rectangular) {
1906     		insertPos = TextDGetInsertPosition(textD);
1907     		lineStart = BufStartOfLine(buf, insertPos);
1908     		column = BufCountDispChars(buf, lineStart, insertPos);
1909     		BufInsertCol(buf, column, lineStart, textToCopy, NULL, NULL);
1910     		TextDSetInsertPosition(textD, buf->cursorPosHint);
1911 	    } else
1912 	    	TextInsertAtCursor(w, textToCopy, event, True,
1913 			tw->text.autoWrapPastedText);
1914 	    NEditFree(textToCopy);
1915 	    BufSecondaryUnselect(buf);
1916 	    TextDUnblankCursor(textD);
1917 	} else
1918 	    SendSecondarySelection(w, e->time, False);
1919     } else if (primary->selected) {
1920 	textToCopy = BufGetSelectionText(buf);
1921 	TextDSetInsertPosition(textD, TextDXYToPosition(textD, e->x, e->y));
1922 	TextInsertAtCursor(w, textToCopy, event, False,
1923 			tw->text.autoWrapPastedText);
1924 	NEditFree(textToCopy);
1925     } else {
1926     	TextDSetInsertPosition(textD, TextDXYToPosition(textD, e->x, e->y));
1927     	InsertPrimarySelection(w, e->time, False);
1928     }
1929 }
1930 
copyToOrEndDragAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)1931 static void copyToOrEndDragAP(Widget w, XEvent *event, String *args,
1932     	Cardinal *nArgs)
1933 {
1934     int dragState = ((TextWidget)w)->text.dragState;
1935 
1936     if (dragState != PRIMARY_BLOCK_DRAG) {
1937     	copyToAP(w, event, args, nArgs);
1938     	return;
1939     }
1940 
1941     FinishBlockDrag((TextWidget)w);
1942 }
1943 
moveToAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)1944 static void moveToAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
1945 {
1946     XButtonEvent *e = &event->xbutton;
1947     textDisp *textD = ((TextWidget)w)->text.textD;
1948     int dragState = ((TextWidget)w)->text.dragState;
1949     textBuffer *buf = textD->buffer;
1950     selection *secondary = &buf->secondary, *primary = &buf->primary;
1951     int insertPos, rectangular = secondary->rectangular;
1952     int column, lineStart;
1953     char *textToCopy;
1954 
1955     endDrag(w);
1956     if (!((dragState == SECONDARY_DRAG && secondary->selected) ||
1957     	    (dragState == SECONDARY_RECT_DRAG && secondary->selected) ||
1958     	    dragState == SECONDARY_CLICKED || dragState == NOT_CLICKED))
1959         return;
1960     if (checkReadOnly(w)) {
1961 	BufSecondaryUnselect(buf);
1962 	return;
1963     }
1964 
1965     if (secondary->selected) {
1966 	if (((TextWidget)w)->text.motifDestOwner) {
1967 	    textToCopy = BufGetSecSelectText(buf);
1968 	    if (primary->selected && rectangular) {
1969     		insertPos = TextDGetInsertPosition(textD);
1970     		BufReplaceSelected(buf, textToCopy);
1971     		TextDSetInsertPosition(textD, buf->cursorPosHint);
1972 	    } else if (rectangular) {
1973     		insertPos = TextDGetInsertPosition(textD);
1974     		lineStart = BufStartOfLine(buf, insertPos);
1975     		column = BufCountDispChars(buf, lineStart, insertPos);
1976     		BufInsertCol(buf, column, lineStart, textToCopy, NULL, NULL);
1977     		TextDSetInsertPosition(textD, buf->cursorPosHint);
1978 	    } else
1979 	    	TextInsertAtCursor(w, textToCopy, event, True,
1980 			((TextWidget)w)->text.autoWrapPastedText);
1981 	    NEditFree(textToCopy);
1982 	    BufRemoveSecSelect(buf);
1983 	    BufSecondaryUnselect(buf);
1984 	} else
1985 	    SendSecondarySelection(w, e->time, True);
1986     } else if (primary->selected) {
1987         textToCopy = BufGetRange(buf, primary->start, primary->end);
1988 	TextDSetInsertPosition(textD, TextDXYToPosition(textD, e->x, e->y));
1989 	TextInsertAtCursor(w, textToCopy, event, False,
1990 			((TextWidget)w)->text.autoWrapPastedText);
1991 	NEditFree(textToCopy);
1992 	BufRemoveSelected(buf);
1993 	BufUnselect(buf);
1994     } else {
1995     	TextDSetInsertPosition(textD, TextDXYToPosition(textD, e->x, e->y));
1996     	MovePrimarySelection(w, e->time, False);
1997     }
1998 }
1999 
moveToOrEndDragAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)2000 static void moveToOrEndDragAP(Widget w, XEvent *event, String *args,
2001     	Cardinal *nArgs)
2002 {
2003     int dragState = ((TextWidget)w)->text.dragState;
2004 
2005     if (dragState != PRIMARY_BLOCK_DRAG) {
2006     	moveToAP(w, event, args, nArgs);
2007     	return;
2008     }
2009 
2010     FinishBlockDrag((TextWidget)w);
2011 }
2012 
endDragAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)2013 static void endDragAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2014 {
2015     if (((TextWidget)w)->text.dragState == PRIMARY_BLOCK_DRAG)
2016     	FinishBlockDrag((TextWidget)w);
2017     else
2018 	endDrag(w);
2019 }
2020 
exchangeAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)2021 static void exchangeAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2022 {
2023     XButtonEvent *e = &event->xbutton;
2024     textDisp *textD = ((TextWidget)w)->text.textD;
2025     textBuffer *buf = textD->buffer;
2026     selection *sec = &buf->secondary, *primary = &buf->primary;
2027     char *primaryText, *secText;
2028     int newPrimaryStart, newPrimaryEnd, secWasRect;
2029     int dragState = ((TextWidget)w)->text.dragState; /* save before endDrag */
2030     int silent = hasKey("nobell", args, nArgs);
2031 
2032     endDrag(w);
2033     if (checkReadOnly(w))
2034 	return;
2035 
2036     /* If there's no secondary selection here, or the primary and secondary
2037        selection overlap, just beep and return */
2038     if (!sec->selected || (primary->selected &&
2039     	    ((primary->start <= sec->start && primary->end > sec->start) ||
2040     	     (sec->start <= primary->start && sec->end > primary->start))))
2041     {
2042     	BufSecondaryUnselect(buf);
2043         ringIfNecessary(silent, w);
2044 	/* If there's no secondary selection, but the primary selection is
2045 	   being dragged, we must not forget to finish the dragging.
2046 	   Otherwise, modifications aren't recorded. */
2047 	if (dragState == PRIMARY_BLOCK_DRAG)
2048 	    FinishBlockDrag((TextWidget)w);
2049     	return;
2050     }
2051 
2052     /* if the primary selection is in another widget, use selection routines */
2053     if (!primary->selected) {
2054     	ExchangeSelections(w, e->time);
2055     	return;
2056     }
2057 
2058     /* Both primary and secondary are in this widget, do the exchange here */
2059     primaryText = BufGetSelectionText(buf);
2060     secText = BufGetSecSelectText(buf);
2061     secWasRect = sec->rectangular;
2062     BufReplaceSecSelect(buf, primaryText);
2063     newPrimaryStart = primary->start;
2064     BufReplaceSelected(buf, secText);
2065     newPrimaryEnd = newPrimaryStart + strlen(secText);
2066     NEditFree(primaryText);
2067     NEditFree(secText);
2068     BufSecondaryUnselect(buf);
2069     if (secWasRect) {
2070     	TextDSetInsertPosition(textD, buf->cursorPosHint);
2071     } else {
2072 	BufSelect(buf, newPrimaryStart, newPrimaryEnd);
2073 	TextDSetInsertPosition(textD, newPrimaryEnd);
2074     }
2075     checkAutoShowInsertPos(w);
2076 }
2077 
copyPrimaryAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)2078 static void copyPrimaryAP(Widget w, XEvent *event, String *args,
2079 	Cardinal *nArgs)
2080 {
2081     XKeyEvent *e = &event->xkey;
2082     TextWidget tw = (TextWidget)w;
2083     textDisp *textD = tw->text.textD;
2084     textBuffer *buf = textD->buffer;
2085     selection *primary = &buf->primary;
2086     int rectangular = hasKey("rect", args, nArgs);
2087     char *textToCopy;
2088     int insertPos, col;
2089 
2090     cancelDrag(w);
2091     if (checkReadOnly(w))
2092 	return;
2093     if (primary->selected && rectangular) {
2094 	textToCopy = BufGetSelectionText(buf);
2095 	insertPos = TextDGetInsertPosition(textD);
2096 	col = BufCountDispChars(buf, BufStartOfLine(buf, insertPos), insertPos);
2097 	BufInsertCol(buf, col, insertPos, textToCopy, NULL, NULL);
2098 	TextDSetInsertPosition(textD, buf->cursorPosHint);
2099 	NEditFree(textToCopy);
2100 	checkAutoShowInsertPos(w);
2101     } else if (primary->selected) {
2102 	textToCopy = BufGetSelectionText(buf);
2103 	insertPos = TextDGetInsertPosition(textD);
2104 	BufInsert(buf, insertPos, textToCopy);
2105 	TextDSetInsertPosition(textD, insertPos + strlen(textToCopy));
2106 	NEditFree(textToCopy);
2107 	checkAutoShowInsertPos(w);
2108     } else if (rectangular) {
2109     	if (!TextDPositionToXY(textD, TextDGetInsertPosition(textD),
2110     		&tw->text.btnDownX, &tw->text.btnDownY))
2111     	    return; /* shouldn't happen */
2112     	InsertPrimarySelection(w, e->time, True);
2113     } else
2114     	InsertPrimarySelection(w, e->time, False);
2115 }
2116 
cutPrimaryAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)2117 static void cutPrimaryAP(Widget w, XEvent *event, String *args,
2118 	Cardinal *nArgs)
2119 {
2120     XKeyEvent *e = &event->xkey;
2121     textDisp *textD = ((TextWidget)w)->text.textD;
2122     textBuffer *buf = textD->buffer;
2123     selection *primary = &buf->primary;
2124     char *textToCopy;
2125     int rectangular = hasKey("rect", args, nArgs);
2126     int insertPos, col;
2127 
2128     cancelDrag(w);
2129     if (checkReadOnly(w))
2130 	return;
2131     if (primary->selected && rectangular) {
2132 	textToCopy = BufGetSelectionText(buf);
2133 	insertPos = TextDGetInsertPosition(textD);
2134 	col = BufCountDispChars(buf, BufStartOfLine(buf, insertPos), insertPos);
2135 	BufInsertCol(buf, col, insertPos, textToCopy, NULL, NULL);
2136 	TextDSetInsertPosition(textD, buf->cursorPosHint);
2137 	NEditFree(textToCopy);
2138 	BufRemoveSelected(buf);
2139 	checkAutoShowInsertPos(w);
2140     } else if (primary->selected) {
2141 	textToCopy = BufGetSelectionText(buf);
2142 	insertPos = TextDGetInsertPosition(textD);
2143 	BufInsert(buf, insertPos, textToCopy);
2144 	TextDSetInsertPosition(textD, insertPos + strlen(textToCopy));
2145 	NEditFree(textToCopy);
2146 	BufRemoveSelected(buf);
2147 	checkAutoShowInsertPos(w);
2148     } else if (rectangular) {
2149     	if (!TextDPositionToXY(textD, TextDGetInsertPosition(textD),
2150     	    	&((TextWidget)w)->text.btnDownX,
2151     	    	&((TextWidget)w)->text.btnDownY))
2152     	    return; /* shouldn't happen */
2153     	MovePrimarySelection(w, e->time, True);
2154     } else {
2155     	MovePrimarySelection(w, e->time, False);
2156     }
2157 }
2158 
mousePanAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)2159 static void mousePanAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2160 {
2161     XButtonEvent *e = &event->xbutton;
2162     TextWidget tw = (TextWidget)w;
2163     textDisp *textD = tw->text.textD;
2164     int lineHeight = textD->ascent + textD->descent;
2165     int topLineNum, horizOffset;
2166     static Cursor panCursor = 0;
2167 
2168     if (tw->text.dragState == MOUSE_PAN) {
2169     	TextDSetScroll(textD,
2170     		(tw->text.btnDownY - e->y + lineHeight/2) / lineHeight,
2171     		tw->text.btnDownX - e->x);
2172     } else if (tw->text.dragState == NOT_CLICKED) {
2173     	TextDGetScroll(textD, &topLineNum, &horizOffset);
2174     	tw->text.btnDownX = e->x + horizOffset;
2175     	tw->text.btnDownY = e->y + topLineNum * lineHeight;
2176     	tw->text.dragState = MOUSE_PAN;
2177     	if (!panCursor)
2178 	    panCursor = XCreateFontCursor(XtDisplay(w), XC_fleur);
2179 	XGrabPointer(XtDisplay(w), XtWindow(w), False,
2180 	    	ButtonMotionMask | ButtonReleaseMask, GrabModeAsync,
2181 	    	GrabModeAsync, None, panCursor, CurrentTime);
2182     } else
2183     	cancelDrag(w);
2184 }
2185 
pasteClipboardAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)2186 static void pasteClipboardAP(Widget w, XEvent *event, String *args,
2187 	Cardinal *nArgs)
2188 {
2189     if (hasKey("rect", args, nArgs))
2190     	TextColPasteClipboard(w, event->xkey.time);
2191     else
2192 	TextPasteClipboard(w, event->xkey.time);
2193 }
2194 
copyClipboardAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)2195 static void copyClipboardAP(Widget w, XEvent *event, String *args,
2196 	Cardinal *nArgs)
2197 {
2198     TextCopyClipboard(w, event->xkey.time);
2199 }
2200 
cutClipboardAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)2201 static void cutClipboardAP(Widget w, XEvent *event, String *args,
2202 	Cardinal *nArgs)
2203 {
2204     TextCutClipboard(w, event->xkey.time);
2205 }
2206 
insertStringAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)2207 static void insertStringAP(Widget w, XEvent *event, String *args,
2208 	Cardinal *nArgs)
2209 {
2210     smartIndentCBStruct smartIndent;
2211     textDisp *textD = ((TextWidget)w)->text.textD;
2212 
2213     if (*nArgs == 0)
2214     	return;
2215     cancelDrag(w);
2216     if (checkReadOnly(w))
2217 	return;
2218     if (((TextWidget)w)->text.smartIndent) {
2219     	smartIndent.reason = CHAR_TYPED;
2220 	smartIndent.pos = TextDGetInsertPosition(textD);
2221     	smartIndent.indentRequest = 0;
2222 	smartIndent.charsTyped = args[0];
2223     	XtCallCallbacks(w, textNsmartIndentCallback, (XtPointer)&smartIndent);
2224     }
2225     TextInsertAtCursor(w, args[0], event, True, True);
2226     BufUnselect((((TextWidget)w)->text.textD)->buffer);
2227 }
2228 
selfInsertAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)2229 static void selfInsertAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2230 {
2231     WindowInfo* window = WidgetToWindow(w);
2232 
2233 #ifdef NO_XMIM
2234     static XComposeStatus compose = {NULL, 0};
2235 #else
2236     int status;
2237 #endif
2238     XKeyEvent *e = &event->xkey;
2239     char chars[20];
2240     KeySym keysym;
2241     int nChars;
2242     smartIndentCBStruct smartIndent;
2243     textDisp *textD = ((TextWidget)w)->text.textD;
2244 
2245 #ifdef NO_XMIM
2246     nChars = XLookupString(&event->xkey, chars, 19, &keysym, &compose);
2247     if (nChars == 0)
2248     	return;
2249 #else
2250     nChars = XmImMbLookupString(w, &event->xkey, chars, 19, &keysym,
2251      	   &status);
2252     if (nChars == 0 || status == XLookupNone ||
2253      	   status == XLookupKeySym || status == XBufferOverflow)
2254     	return;
2255 #endif
2256     cancelDrag(w);
2257     if (checkReadOnly(w))
2258 	return;
2259     TakeMotifDestination(w, e->time);
2260     chars[nChars] = '\0';
2261 
2262     if (!BufSubstituteNullChars(chars, nChars, window->buffer)) {
2263         DialogF(DF_ERR, window->shell, 1, "Error", "Too much binary data", "OK");
2264         return;
2265     }
2266 
2267     /* If smart indent is on, call the smart indent callback to check the
2268        inserted character */
2269     if (((TextWidget)w)->text.smartIndent) {
2270     	smartIndent.reason = CHAR_TYPED;
2271 	smartIndent.pos = TextDGetInsertPosition(textD);
2272     	smartIndent.indentRequest = 0;
2273 	smartIndent.charsTyped = chars;
2274     	XtCallCallbacks(w, textNsmartIndentCallback, (XtPointer)&smartIndent);
2275     }
2276     TextInsertAtCursor(w, chars, event, True, True);
2277     BufUnselect(textD->buffer);
2278 }
2279 
newlineAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)2280 static void newlineAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2281 {
2282     if (((TextWidget)w)->text.autoIndent || ((TextWidget)w)->text.smartIndent)
2283     	newlineAndIndentAP(w, event, args, nArgs);
2284     else
2285     	newlineNoIndentAP(w, event, args, nArgs);
2286 }
2287 
newlineNoIndentAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)2288 static void newlineNoIndentAP(Widget w, XEvent *event, String *args,
2289 	Cardinal *nArgs)
2290 {
2291     XKeyEvent *e = &event->xkey;
2292 
2293     cancelDrag(w);
2294     if (checkReadOnly(w))
2295 	return;
2296     TakeMotifDestination(w, e->time);
2297     simpleInsertAtCursor(w, "\n", event, True);
2298     BufUnselect((((TextWidget)w)->text.textD)->buffer);
2299 }
2300 
newlineAndIndentAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)2301 static void newlineAndIndentAP(Widget w, XEvent *event, String *args,
2302 	Cardinal *nArgs)
2303 {
2304     XKeyEvent *e = &event->xkey;
2305     TextWidget tw = (TextWidget)w;
2306     textDisp *textD = tw->text.textD;
2307     textBuffer *buf = textD->buffer;
2308     char *indentStr;
2309     int cursorPos, lineStartPos, column;
2310 
2311     if (checkReadOnly(w))
2312 	return;
2313     cancelDrag(w);
2314     TakeMotifDestination(w, e->time);
2315 
2316     /* Create a string containing a newline followed by auto or smart
2317        indent string */
2318     cursorPos = TextDGetInsertPosition(textD);
2319     lineStartPos = BufStartOfLine(buf, cursorPos);
2320     indentStr = createIndentString(tw, buf, 0, lineStartPos,
2321     	    cursorPos, NULL, &column);
2322 
2323     /* Insert it at the cursor */
2324     simpleInsertAtCursor(w, indentStr, event, True);
2325     NEditFree(indentStr);
2326 
2327     if (tw->text.emulateTabs > 0) {
2328         /*  If emulated tabs are on, make the inserted indent deletable by
2329             tab. Round this up by faking the column a bit to the right to
2330             let the user delete half-tabs with one keypress.  */
2331         column += tw->text.emulateTabs - 1;
2332         tw->text.emTabsBeforeCursor = column / tw->text.emulateTabs;
2333     }
2334 
2335     BufUnselect(buf);
2336 }
2337 
processTabAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)2338 static void processTabAP(Widget w, XEvent *event, String *args,
2339 	Cardinal *nArgs)
2340 {
2341     textDisp *textD = ((TextWidget)w)->text.textD;
2342     textBuffer *buf = textD->buffer;
2343     selection *sel = &buf->primary;
2344     int emTabDist = ((TextWidget)w)->text.emulateTabs;
2345     int emTabsBeforeCursor = ((TextWidget)w)->text.emTabsBeforeCursor;
2346     int insertPos, indent, startIndent, toIndent, lineStart, tabWidth;
2347     char *outStr, *outPtr;
2348 
2349     if (checkReadOnly(w))
2350 	return;
2351     cancelDrag(w);
2352     TakeMotifDestination(w, event->xkey.time);
2353 
2354     /* If emulated tabs are off, just insert a tab */
2355     if (emTabDist <= 0) {
2356 	TextInsertAtCursor(w, "\t", event, True, True);
2357     	return;
2358     }
2359 
2360     /* Find the starting and ending indentation.  If the tab is to
2361        replace an existing selection, use the start of the selection
2362        instead of the cursor position as the indent.  When replacing
2363        rectangular selections, tabs are automatically recalculated as
2364        if the inserted text began at the start of the line */
2365     insertPos = pendingSelection(w) ?
2366     	    sel->start : TextDGetInsertPosition(textD);
2367     lineStart = BufStartOfLine(buf, insertPos);
2368     if (pendingSelection(w) && sel->rectangular)
2369     	insertPos = BufCountForwardDispChars(buf, lineStart, sel->rectStart);
2370     startIndent = BufCountDispChars(buf, lineStart, insertPos);
2371     toIndent = startIndent + emTabDist - (startIndent % emTabDist);
2372     if (pendingSelection(w) && sel->rectangular) {
2373     	toIndent -= startIndent;
2374     	startIndent = 0;
2375     }
2376 
2377     /* Allocate a buffer assuming all the inserted characters will be spaces */
2378     outStr = (char*)NEditMalloc(toIndent - startIndent + 1);
2379 
2380     /* Add spaces and tabs to outStr until it reaches toIndent */
2381     outPtr = outStr;
2382     indent = startIndent;
2383     while (indent < toIndent) {
2384     	tabWidth = BufCharWidth('\t', indent, buf->tabDist, buf->nullSubsChar);
2385     	if (buf->useTabs && tabWidth > 1 && indent + tabWidth <= toIndent) {
2386     	    *outPtr++ = '\t';
2387     	    indent += tabWidth;
2388     	} else {
2389     	    *outPtr++ = ' ';
2390     	    indent++;
2391     	}
2392     }
2393     *outPtr = '\0';
2394 
2395     /* Insert the emulated tab */
2396     TextInsertAtCursor(w, outStr, event, True, True);
2397     NEditFree(outStr);
2398 
2399     /* Restore and ++ emTabsBeforeCursor cleared by TextInsertAtCursor */
2400     ((TextWidget)w)->text.emTabsBeforeCursor = emTabsBeforeCursor + 1;
2401 
2402     BufUnselect(buf);
2403 }
2404 
deleteSelectionAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)2405 static void deleteSelectionAP(Widget w, XEvent *event, String *args,
2406 	Cardinal *nArgs)
2407 {
2408     XKeyEvent *e = &event->xkey;
2409 
2410     cancelDrag(w);
2411     if (checkReadOnly(w))
2412 	return;
2413     TakeMotifDestination(w, e->time);
2414     deletePendingSelection(w, event);
2415 }
2416 
deletePreviousCharacterAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)2417 static void deletePreviousCharacterAP(Widget w, XEvent *event, String *args,
2418 	Cardinal *nArgs)
2419 {
2420     XKeyEvent *e = &event->xkey;
2421     textDisp *textD = ((TextWidget)w)->text.textD;
2422     int insertPos = TextDGetInsertPosition(textD);
2423     char c;
2424     int silent = hasKey("nobell", args, nArgs);
2425 
2426     cancelDrag(w);
2427     if (checkReadOnly(w))
2428 	return;
2429 
2430     TakeMotifDestination(w, e->time);
2431     if (deletePendingSelection(w, event))
2432     	return;
2433 
2434     if (insertPos == 0) {
2435         ringIfNecessary(silent, w);
2436     	return;
2437     }
2438 
2439     if (deleteEmulatedTab(w, event))
2440     	return;
2441 
2442     if (((TextWidget)w)->text.overstrike) {
2443     	c = BufGetCharacter(textD->buffer, insertPos - 1);
2444     	if (c == '\n')
2445     	    BufRemove(textD->buffer, insertPos - 1, insertPos);
2446     	else if (c != '\t')
2447     	    BufReplace(textD->buffer, insertPos - 1, insertPos, " ");
2448     } else {
2449     	BufRemove(textD->buffer, insertPos - 1, insertPos);
2450     }
2451 
2452     TextDSetInsertPosition(textD, insertPos - 1);
2453     checkAutoShowInsertPos(w);
2454     callCursorMovementCBs(w, event);
2455 }
2456 
deleteNextCharacterAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)2457 static void deleteNextCharacterAP(Widget w, XEvent *event, String *args,
2458 	Cardinal *nArgs)
2459 {
2460     XKeyEvent *e = &event->xkey;
2461     textDisp *textD = ((TextWidget)w)->text.textD;
2462     int insertPos = TextDGetInsertPosition(textD);
2463     int silent = hasKey("nobell", args, nArgs);
2464 
2465     cancelDrag(w);
2466     if (checkReadOnly(w))
2467 	return;
2468     TakeMotifDestination(w, e->time);
2469     if (deletePendingSelection(w, event))
2470     	return;
2471     if (insertPos == textD->buffer->length) {
2472         ringIfNecessary(silent, w);
2473     	return;
2474     }
2475     BufRemove(textD->buffer, insertPos , insertPos + 1);
2476     checkAutoShowInsertPos(w);
2477     callCursorMovementCBs(w, event);
2478 }
2479 
deletePreviousWordAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)2480 static void deletePreviousWordAP(Widget w, XEvent *event, String *args,
2481 	Cardinal *nArgs)
2482 {
2483     XKeyEvent *e = &event->xkey;
2484     textDisp *textD = ((TextWidget)w)->text.textD;
2485     int insertPos = TextDGetInsertPosition(textD);
2486     int pos, lineStart = BufStartOfLine(textD->buffer, insertPos);
2487     char *delimiters = ((TextWidget)w)->text.delimiters;
2488     int silent = hasKey("nobell", args, nArgs);
2489 
2490     cancelDrag(w);
2491     if (checkReadOnly(w)) {
2492         return;
2493     }
2494 
2495     TakeMotifDestination(w, e->time);
2496     if (deletePendingSelection(w, event)) {
2497         return;
2498     }
2499 
2500     if (insertPos == lineStart) {
2501         ringIfNecessary(silent, w);
2502         return;
2503     }
2504 
2505     pos = max(insertPos - 1, 0);
2506     while (strchr(delimiters, BufGetCharacter(textD->buffer, pos)) != NULL &&
2507             pos != lineStart) {
2508         pos--;
2509     }
2510 
2511     pos = startOfWord((TextWidget)w, pos);
2512     BufRemove(textD->buffer, pos, insertPos);
2513     checkAutoShowInsertPos(w);
2514     callCursorMovementCBs(w, event);
2515 }
2516 
deleteNextWordAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)2517 static void deleteNextWordAP(Widget w, XEvent *event, String *args,
2518 	Cardinal *nArgs)
2519 {
2520     XKeyEvent *e = &event->xkey;
2521     textDisp *textD = ((TextWidget)w)->text.textD;
2522     int insertPos = TextDGetInsertPosition(textD);
2523     int pos, lineEnd = BufEndOfLine(textD->buffer, insertPos);
2524     char *delimiters = ((TextWidget)w)->text.delimiters;
2525     int silent = hasKey("nobell", args, nArgs);
2526 
2527     cancelDrag(w);
2528     if (checkReadOnly(w)) {
2529         return;
2530     }
2531 
2532     TakeMotifDestination(w, e->time);
2533     if (deletePendingSelection(w, event)) {
2534         return;
2535     }
2536 
2537     if (insertPos == lineEnd) {
2538         ringIfNecessary(silent, w);
2539         return;
2540     }
2541 
2542     pos = insertPos;
2543     while (strchr(delimiters, BufGetCharacter(textD->buffer, pos)) != NULL &&
2544             pos != lineEnd) {
2545         pos++;
2546     }
2547 
2548     pos = endOfWord((TextWidget)w, pos);
2549     BufRemove(textD->buffer, insertPos, pos);
2550     checkAutoShowInsertPos(w);
2551     callCursorMovementCBs(w, event);
2552 }
2553 
deleteToEndOfLineAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)2554 static void deleteToEndOfLineAP(Widget w, XEvent *event, String *args,
2555     Cardinal *nArgs)
2556 {
2557     XKeyEvent *e = &event->xkey;
2558     textDisp *textD = ((TextWidget)w)->text.textD;
2559     int insertPos = TextDGetInsertPosition(textD);
2560     int endOfLine;
2561     int silent = 0;
2562 
2563     silent = hasKey("nobell", args, nArgs);
2564     if (hasKey("absolute", args, nArgs))
2565         endOfLine = BufEndOfLine(textD->buffer, insertPos);
2566     else
2567         endOfLine = TextDEndOfLine(textD, insertPos, False);
2568     cancelDrag(w);
2569     if (checkReadOnly(w))
2570         return;
2571     TakeMotifDestination(w, e->time);
2572     if (deletePendingSelection(w, event))
2573         return;
2574     if (insertPos == endOfLine) {
2575         ringIfNecessary(silent, w);
2576     	return;
2577     }
2578     BufRemove(textD->buffer, insertPos, endOfLine);
2579     checkAutoShowInsertPos(w);
2580     callCursorMovementCBs(w, event);
2581 }
2582 
deleteToStartOfLineAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)2583 static void deleteToStartOfLineAP(Widget w, XEvent *event, String *args,
2584     Cardinal *nArgs)
2585 {
2586     XKeyEvent *e = &event->xkey;
2587     textDisp *textD = ((TextWidget)w)->text.textD;
2588     int insertPos = TextDGetInsertPosition(textD);
2589     int startOfLine;
2590     int silent = 0;
2591 
2592     silent = hasKey("nobell", args, nArgs);
2593     if (hasKey("wrap", args, nArgs))
2594         startOfLine = TextDStartOfLine(textD, insertPos);
2595     else
2596         startOfLine = BufStartOfLine(textD->buffer, insertPos);
2597     cancelDrag(w);
2598     if (checkReadOnly(w))
2599         return;
2600     TakeMotifDestination(w, e->time);
2601     if (deletePendingSelection(w, event))
2602         return;
2603     if (insertPos == startOfLine) {
2604         ringIfNecessary(silent, w);
2605     	return;
2606     }
2607     BufRemove(textD->buffer, startOfLine, insertPos);
2608     checkAutoShowInsertPos(w);
2609     callCursorMovementCBs(w, event);
2610 }
2611 
forwardCharacterAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)2612 static void forwardCharacterAP(Widget w, XEvent *event, String *args,
2613 	Cardinal *nArgs)
2614 {
2615     int insertPos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
2616     int silent = hasKey("nobell", args, nArgs);
2617 
2618     cancelDrag(w);
2619     if (!TextDMoveRight(((TextWidget)w)->text.textD)) {
2620         ringIfNecessary(silent, w);
2621     }
2622     checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2623     checkAutoShowInsertPos(w);
2624     callCursorMovementCBs(w, event);
2625 }
2626 
backwardCharacterAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)2627 static void backwardCharacterAP(Widget w, XEvent *event, String *args,
2628 	Cardinal *nArgs)
2629 {
2630     int insertPos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
2631     int silent = hasKey("nobell", args, nArgs);
2632 
2633     cancelDrag(w);
2634     if (!TextDMoveLeft(((TextWidget)w)->text.textD)) {
2635         ringIfNecessary(silent, w);
2636     }
2637     checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2638     checkAutoShowInsertPos(w);
2639     callCursorMovementCBs(w, event);
2640 }
2641 
forwardWordAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)2642 static void forwardWordAP(Widget w, XEvent *event, String *args,
2643 	Cardinal *nArgs)
2644 {
2645     textDisp *textD = ((TextWidget)w)->text.textD;
2646     textBuffer *buf = textD->buffer;
2647     int pos, insertPos = TextDGetInsertPosition(textD);
2648     char *delimiters = ((TextWidget)w)->text.delimiters;
2649     int silent = hasKey("nobell", args, nArgs);
2650 
2651     cancelDrag(w);
2652     if (insertPos == buf->length) {
2653         ringIfNecessary(silent, w);
2654     	return;
2655     }
2656     pos = insertPos;
2657 
2658     if (hasKey("tail", args, nArgs)) {
2659         for (; pos<buf->length; pos++) {
2660             if (NULL == strchr(delimiters, BufGetCharacter(buf, pos))) {
2661                 break;
2662             }
2663         }
2664         if (NULL == strchr(delimiters, BufGetCharacter(buf, pos))) {
2665             pos = endOfWord((TextWidget)w, pos);
2666         }
2667     } else {
2668         if (NULL == strchr(delimiters, BufGetCharacter(buf, pos))) {
2669             pos = endOfWord((TextWidget)w, pos);
2670         }
2671         for (; pos<buf->length; pos++) {
2672             if (NULL == strchr(delimiters, BufGetCharacter(buf, pos))) {
2673                 break;
2674             }
2675         }
2676     }
2677 
2678     TextDSetInsertPosition(textD, pos);
2679     checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2680     checkAutoShowInsertPos(w);
2681     callCursorMovementCBs(w, event);
2682 }
2683 
backwardWordAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)2684 static void backwardWordAP(Widget w, XEvent *event, String *args,
2685 	Cardinal *nArgs)
2686 {
2687     textDisp *textD = ((TextWidget)w)->text.textD;
2688     textBuffer *buf = textD->buffer;
2689     int pos, insertPos = TextDGetInsertPosition(textD);
2690     char *delimiters = ((TextWidget)w)->text.delimiters;
2691     int silent = hasKey("nobell", args, nArgs);
2692 
2693     cancelDrag(w);
2694     if (insertPos == 0) {
2695         ringIfNecessary(silent, w);
2696     	return;
2697     }
2698     pos = max(insertPos - 1, 0);
2699     while (strchr(delimiters, BufGetCharacter(buf, pos)) != NULL && pos > 0)
2700     	pos--;
2701     pos = startOfWord((TextWidget)w, pos);
2702 
2703     TextDSetInsertPosition(textD, pos);
2704     checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2705     checkAutoShowInsertPos(w);
2706     callCursorMovementCBs(w, event);
2707 }
2708 
forwardParagraphAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)2709 static void forwardParagraphAP(Widget w, XEvent *event, String *args,
2710 	Cardinal *nArgs)
2711 {
2712     textDisp *textD = ((TextWidget)w)->text.textD;
2713     int pos, insertPos = TextDGetInsertPosition(textD);
2714     textBuffer *buf = textD->buffer;
2715     char c;
2716     static char whiteChars[] = " \t";
2717     int silent = hasKey("nobell", args, nArgs);
2718 
2719     cancelDrag(w);
2720     if (insertPos == buf->length) {
2721         ringIfNecessary(silent, w);
2722     	return;
2723     }
2724     pos = min(BufEndOfLine(buf, insertPos)+1, buf->length);
2725     while (pos < buf->length) {
2726     	c = BufGetCharacter(buf, pos);
2727     	if (c == '\n')
2728     	    break;
2729     	if (strchr(whiteChars, c) != NULL)
2730     	    pos++;
2731     	else
2732     	    pos = min(BufEndOfLine(buf, pos)+1, buf->length);
2733     }
2734     TextDSetInsertPosition(textD, min(pos+1, buf->length));
2735     checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2736     checkAutoShowInsertPos(w);
2737     callCursorMovementCBs(w, event);
2738 }
2739 
backwardParagraphAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)2740 static void backwardParagraphAP(Widget w, XEvent *event, String *args,
2741 	Cardinal *nArgs)
2742 {
2743     textDisp *textD = ((TextWidget)w)->text.textD;
2744     int parStart, pos, insertPos = TextDGetInsertPosition(textD);
2745     textBuffer *buf = textD->buffer;
2746     char c;
2747     static char whiteChars[] = " \t";
2748     int silent = hasKey("nobell", args, nArgs);
2749 
2750     cancelDrag(w);
2751     if (insertPos == 0) {
2752         ringIfNecessary(silent, w);
2753     	return;
2754     }
2755     parStart = BufStartOfLine(buf, max(insertPos-1, 0));
2756     pos = max(parStart - 2, 0);
2757     while (pos > 0) {
2758     	c = BufGetCharacter(buf, pos);
2759     	if (c == '\n')
2760     	    break;
2761     	if (strchr(whiteChars, c) != NULL)
2762     	    pos--;
2763     	else {
2764     	    parStart = BufStartOfLine(buf, pos);
2765     	    pos = max(parStart - 2, 0);
2766     	}
2767     }
2768     TextDSetInsertPosition(textD, parStart);
2769     checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2770     checkAutoShowInsertPos(w);
2771     callCursorMovementCBs(w, event);
2772 }
2773 
keySelectAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)2774 static void keySelectAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2775 {
2776     textDisp *textD = ((TextWidget)w)->text.textD;
2777     int stat, insertPos = TextDGetInsertPosition(textD);
2778     int silent = hasKey("nobell", args, nArgs);
2779 
2780     cancelDrag(w);
2781     if (hasKey("left", args, nArgs)) stat = TextDMoveLeft(textD);
2782     else if (hasKey("right", args, nArgs)) stat = TextDMoveRight(textD);
2783     else if (hasKey("up", args, nArgs)) stat = TextDMoveUp(textD, 0);
2784     else if (hasKey("down", args, nArgs)) stat = TextDMoveDown(textD, 0);
2785     else {
2786     	keyMoveExtendSelection(w, event, insertPos, hasKey("rect", args,nArgs));
2787     	return;
2788     }
2789     if (!stat) {
2790         ringIfNecessary(silent, w);
2791     }
2792     else {
2793 	keyMoveExtendSelection(w, event, insertPos, hasKey("rect", args,nArgs));
2794 	checkAutoShowInsertPos(w);
2795     	callCursorMovementCBs(w, event);
2796     }
2797 }
2798 
processUpAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)2799 static void processUpAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2800 {
2801     int insertPos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
2802     int silent = hasKey("nobell", args, nArgs);
2803     int abs = hasKey("absolute", args, nArgs);
2804 
2805     cancelDrag(w);
2806     if (!TextDMoveUp(((TextWidget)w)->text.textD, abs))
2807         ringIfNecessary(silent, w);
2808     checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2809     checkAutoShowInsertPos(w);
2810     callCursorMovementCBs(w, event);
2811 }
2812 
processShiftUpAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)2813 static void processShiftUpAP(Widget w, XEvent *event, String *args,
2814     Cardinal *nArgs)
2815 {
2816     int insertPos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
2817     int silent = hasKey("nobell", args, nArgs);
2818     int abs = hasKey("absolute", args, nArgs);
2819 
2820     cancelDrag(w);
2821     if (!TextDMoveUp(((TextWidget)w)->text.textD, abs))
2822         ringIfNecessary(silent, w);
2823     keyMoveExtendSelection(w, event, insertPos, hasKey("rect", args, nArgs));
2824     checkAutoShowInsertPos(w);
2825     callCursorMovementCBs(w, event);
2826 }
2827 
processDownAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)2828 static void processDownAP(Widget w, XEvent *event, String *args,
2829     Cardinal *nArgs)
2830 {
2831     int insertPos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
2832     int silent = hasKey("nobell", args, nArgs);
2833     int abs = hasKey("absolute", args, nArgs);
2834 
2835     cancelDrag(w);
2836     if (!TextDMoveDown(((TextWidget)w)->text.textD, abs))
2837         ringIfNecessary(silent, w);
2838     checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2839     checkAutoShowInsertPos(w);
2840     callCursorMovementCBs(w, event);
2841 }
2842 
processShiftDownAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)2843 static void processShiftDownAP(Widget w, XEvent *event, String *args,
2844     Cardinal *nArgs)
2845 {
2846     int insertPos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
2847     int silent = hasKey("nobell", args, nArgs);
2848     int abs = hasKey("absolute", args, nArgs);
2849 
2850     cancelDrag(w);
2851     if (!TextDMoveDown(((TextWidget)w)->text.textD, abs))
2852         ringIfNecessary(silent, w);
2853     keyMoveExtendSelection(w, event, insertPos, hasKey("rect", args, nArgs));
2854     checkAutoShowInsertPos(w);
2855     callCursorMovementCBs(w, event);
2856 }
2857 
beginningOfLineAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)2858 static void beginningOfLineAP(Widget w, XEvent *event, String *args,
2859 	Cardinal *nArgs)
2860 {
2861     textDisp *textD = ((TextWidget)w)->text.textD;
2862     int insertPos = TextDGetInsertPosition(textD);
2863 
2864     cancelDrag(w);
2865     if (hasKey("absolute", args, nArgs))
2866         TextDSetInsertPosition(textD, BufStartOfLine(textD->buffer, insertPos));
2867     else
2868         TextDSetInsertPosition(textD, TextDStartOfLine(textD, insertPos));
2869     checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2870     checkAutoShowInsertPos(w);
2871     callCursorMovementCBs(w, event);
2872     textD->cursorPreferredCol = 0;
2873 }
2874 
endOfLineAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)2875 static void endOfLineAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2876 {
2877     textDisp *textD = ((TextWidget)w)->text.textD;
2878     int insertPos = TextDGetInsertPosition(textD);
2879 
2880     cancelDrag(w);
2881     if (hasKey("absolute", args, nArgs))
2882         TextDSetInsertPosition(textD, BufEndOfLine(textD->buffer, insertPos));
2883     else
2884         TextDSetInsertPosition(textD, TextDEndOfLine(textD, insertPos, False));
2885     checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2886     checkAutoShowInsertPos(w);
2887     callCursorMovementCBs(w, event);
2888     textD->cursorPreferredCol = -1;
2889 }
2890 
beginningOfFileAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)2891 static void beginningOfFileAP(Widget w, XEvent *event, String *args,
2892 	Cardinal *nArgs)
2893 {
2894     int insertPos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
2895 	textDisp *textD = ((TextWidget)w)->text.textD;
2896 
2897     cancelDrag(w);
2898     if (hasKey("scrollbar", args, nArgs)) {
2899 	if (textD->topLineNum != 1) {
2900 	    TextDSetScroll(textD, 1, textD->horizOffset);
2901 	}
2902     }
2903     else {
2904         TextDSetInsertPosition(((TextWidget)w)->text.textD, 0);
2905         checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2906         checkAutoShowInsertPos(w);
2907         callCursorMovementCBs(w, event);
2908     }
2909 }
2910 
endOfFileAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)2911 static void endOfFileAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2912 {
2913     textDisp *textD = ((TextWidget)w)->text.textD;
2914     int insertPos = TextDGetInsertPosition(textD);
2915     int lastTopLine;
2916 
2917     cancelDrag(w);
2918     if (hasKey("scrollbar", args, nArgs)) {
2919         lastTopLine = max(1,
2920                 textD->nBufferLines - (textD->nVisibleLines - 2) +
2921                 ((TextWidget)w)->text.cursorVPadding);
2922         if (lastTopLine != textD->topLineNum) {
2923             TextDSetScroll(textD, lastTopLine, textD->horizOffset);
2924         }
2925     }
2926     else {
2927         TextDSetInsertPosition(textD, textD->buffer->length);
2928         checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2929         checkAutoShowInsertPos(w);
2930         callCursorMovementCBs(w, event);
2931     }
2932 }
2933 
nextPageAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)2934 static void nextPageAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2935 {
2936     textDisp *textD = ((TextWidget)w)->text.textD;
2937     textBuffer *buf = textD->buffer;
2938     int lastTopLine = max(1,
2939             textD->nBufferLines - (textD->nVisibleLines - 2) +
2940             ((TextWidget)w)->text.cursorVPadding );
2941     int insertPos = TextDGetInsertPosition(textD);
2942     int column = 0, visLineNum, lineStartPos;
2943     int pos, targetLine;
2944     int pageForwardCount = max(1, textD->nVisibleLines - 1);
2945     int maintainColumn = 0;
2946     int silent = hasKey("nobell", args, nArgs);
2947 
2948     maintainColumn = hasKey("column", args, nArgs);
2949     cancelDrag(w);
2950     if (hasKey("scrollbar", args, nArgs)) { /* scrollbar only */
2951         targetLine = min(textD->topLineNum + pageForwardCount, lastTopLine);
2952 
2953         if (targetLine == textD->topLineNum) {
2954             ringIfNecessary(silent, w);
2955             return;
2956         }
2957         TextDSetScroll(textD, targetLine, textD->horizOffset);
2958     }
2959     else if (hasKey("stutter", args, nArgs)) { /* Mac style */
2960         /* move to bottom line of visible area */
2961         /* if already there, page down maintaining preferrred column */
2962         targetLine = max(min(textD->nVisibleLines - 1, textD->nBufferLines), 0);
2963         column = TextDPreferredColumn(textD, &visLineNum, &lineStartPos);
2964         if (lineStartPos == textD->lineStarts[targetLine]) {
2965             if (insertPos >= buf->length || textD->topLineNum == lastTopLine) {
2966                 ringIfNecessary(silent, w);
2967                 return;
2968             }
2969             targetLine = min(textD->topLineNum + pageForwardCount, lastTopLine);
2970             pos = TextDCountForwardNLines(textD, insertPos, pageForwardCount, False);
2971             if (maintainColumn) {
2972                 pos = TextDPosOfPreferredCol(textD, column, pos);
2973             }
2974             TextDSetInsertPosition(textD, pos);
2975             TextDSetScroll(textD, targetLine, textD->horizOffset);
2976         }
2977         else {
2978             pos = textD->lineStarts[targetLine];
2979             while (targetLine > 0 && pos == -1) {
2980                 --targetLine;
2981                 pos = textD->lineStarts[targetLine];
2982             }
2983             if (lineStartPos == pos) {
2984                 ringIfNecessary(silent, w);
2985                 return;
2986             }
2987             if (maintainColumn) {
2988                 pos = TextDPosOfPreferredCol(textD, column, pos);
2989             }
2990             TextDSetInsertPosition(textD, pos);
2991         }
2992         checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2993         checkAutoShowInsertPos(w);
2994         callCursorMovementCBs(w, event);
2995         if (maintainColumn) {
2996             textD->cursorPreferredCol = column;
2997         }
2998         else {
2999             textD->cursorPreferredCol = -1;
3000         }
3001     }
3002     else { /* "standard" */
3003         if (insertPos >= buf->length && textD->topLineNum == lastTopLine) {
3004             ringIfNecessary(silent, w);
3005             return;
3006         }
3007         if (maintainColumn) {
3008             column = TextDPreferredColumn(textD, &visLineNum, &lineStartPos);
3009         }
3010         targetLine = textD->topLineNum + textD->nVisibleLines - 1;
3011         if (targetLine < 1) targetLine = 1;
3012         if (targetLine > lastTopLine) targetLine = lastTopLine;
3013         pos = TextDCountForwardNLines(textD, insertPos, textD->nVisibleLines-1, False);
3014         if (maintainColumn) {
3015             pos = TextDPosOfPreferredCol(textD, column, pos);
3016         }
3017         TextDSetInsertPosition(textD, pos);
3018         TextDSetScroll(textD, targetLine, textD->horizOffset);
3019         checkMoveSelectionChange(w, event, insertPos, args, nArgs);
3020         checkAutoShowInsertPos(w);
3021         callCursorMovementCBs(w, event);
3022         if (maintainColumn) {
3023             textD->cursorPreferredCol = column;
3024         }
3025         else {
3026             textD->cursorPreferredCol = -1;
3027         }
3028     }
3029 }
3030 
previousPageAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)3031 static void previousPageAP(Widget w, XEvent *event, String *args,
3032 	Cardinal *nArgs)
3033 {
3034     textDisp *textD = ((TextWidget)w)->text.textD;
3035     int insertPos = TextDGetInsertPosition(textD);
3036     int column = 0, visLineNum, lineStartPos;
3037     int pos, targetLine;
3038     int pageBackwardCount = max(1, textD->nVisibleLines - 1);
3039     int maintainColumn = 0;
3040     int silent = hasKey("nobell", args, nArgs);
3041 
3042     maintainColumn = hasKey("column", args, nArgs);
3043     cancelDrag(w);
3044     if (hasKey("scrollbar", args, nArgs)) { /* scrollbar only */
3045         targetLine = max(textD->topLineNum - pageBackwardCount, 1);
3046 
3047         if (targetLine == textD->topLineNum) {
3048             ringIfNecessary(silent, w);
3049             return;
3050         }
3051         TextDSetScroll(textD, targetLine, textD->horizOffset);
3052     }
3053     else if (hasKey("stutter", args, nArgs)) { /* Mac style */
3054         /* move to top line of visible area */
3055         /* if already there, page up maintaining preferrred column if required */
3056         targetLine = 0;
3057         column = TextDPreferredColumn(textD, &visLineNum, &lineStartPos);
3058         if (lineStartPos == textD->lineStarts[targetLine]) {
3059             if (textD->topLineNum == 1 && (maintainColumn || column == 0)) {
3060                 ringIfNecessary(silent, w);
3061                 return;
3062             }
3063             targetLine = max(textD->topLineNum - pageBackwardCount, 1);
3064             pos = TextDCountBackwardNLines(textD, insertPos, pageBackwardCount);
3065             if (maintainColumn) {
3066                 pos = TextDPosOfPreferredCol(textD, column, pos);
3067             }
3068             TextDSetInsertPosition(textD, pos);
3069             TextDSetScroll(textD, targetLine, textD->horizOffset);
3070         }
3071         else {
3072             pos = textD->lineStarts[targetLine];
3073             if (maintainColumn) {
3074                 pos = TextDPosOfPreferredCol(textD, column, pos);
3075             }
3076             TextDSetInsertPosition(textD, pos);
3077         }
3078         checkMoveSelectionChange(w, event, insertPos, args, nArgs);
3079         checkAutoShowInsertPos(w);
3080         callCursorMovementCBs(w, event);
3081         if (maintainColumn) {
3082             textD->cursorPreferredCol = column;
3083         }
3084         else {
3085             textD->cursorPreferredCol = -1;
3086         }
3087     }
3088     else { /* "standard" */
3089         if (insertPos <= 0 && textD->topLineNum == 1) {
3090             ringIfNecessary(silent, w);
3091             return;
3092         }
3093         if (maintainColumn) {
3094             column = TextDPreferredColumn(textD, &visLineNum, &lineStartPos);
3095         }
3096         targetLine = textD->topLineNum - (textD->nVisibleLines - 1);
3097         if (targetLine < 1) targetLine = 1;
3098         pos = TextDCountBackwardNLines(textD, insertPos, textD->nVisibleLines-1);
3099         if (maintainColumn) {
3100             pos = TextDPosOfPreferredCol(textD, column, pos);
3101         }
3102         TextDSetInsertPosition(textD, pos);
3103         TextDSetScroll(textD, targetLine, textD->horizOffset);
3104         checkMoveSelectionChange(w, event, insertPos, args, nArgs);
3105         checkAutoShowInsertPos(w);
3106         callCursorMovementCBs(w, event);
3107         if (maintainColumn) {
3108             textD->cursorPreferredCol = column;
3109         }
3110         else {
3111             textD->cursorPreferredCol = -1;
3112         }
3113     }
3114 }
3115 
pageLeftAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)3116 static void pageLeftAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
3117 {
3118     textDisp *textD = ((TextWidget)w)->text.textD;
3119     textBuffer *buf = textD->buffer;
3120     int insertPos = TextDGetInsertPosition(textD);
3121     int maxCharWidth = textD->fontStruct->max_bounds.width;
3122     int lineStartPos, indent, pos;
3123     int horizOffset;
3124     int silent = hasKey("nobell", args, nArgs);
3125 
3126     cancelDrag(w);
3127     if (hasKey("scrollbar", args, nArgs)) {
3128 	if (textD->horizOffset == 0) {
3129             ringIfNecessary(silent, w);
3130     	    return;
3131 	}
3132 	horizOffset = max(0, textD->horizOffset - textD->width);
3133 	TextDSetScroll(textD, textD->topLineNum, horizOffset);
3134     }
3135     else {
3136         lineStartPos = BufStartOfLine(buf, insertPos);
3137         if (insertPos == lineStartPos && textD->horizOffset == 0) {
3138             ringIfNecessary(silent, w);
3139     	    return;
3140         }
3141         indent = BufCountDispChars(buf, lineStartPos, insertPos);
3142         pos = BufCountForwardDispChars(buf, lineStartPos,
3143     	        max(0, indent - textD->width / maxCharWidth));
3144         TextDSetInsertPosition(textD, pos);
3145         TextDSetScroll(textD, textD->topLineNum,
3146     	        max(0, textD->horizOffset - textD->width));
3147         checkMoveSelectionChange(w, event, insertPos, args, nArgs);
3148         checkAutoShowInsertPos(w);
3149         callCursorMovementCBs(w, event);
3150     }
3151 }
3152 
pageRightAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)3153 static void pageRightAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
3154 {
3155     textDisp *textD = ((TextWidget)w)->text.textD;
3156     textBuffer *buf = textD->buffer;
3157     int insertPos = TextDGetInsertPosition(textD);
3158     int maxCharWidth = textD->fontStruct->max_bounds.width;
3159     int oldHorizOffset = textD->horizOffset;
3160     int lineStartPos, indent, pos;
3161     int horizOffset, sliderSize, sliderMax;
3162     int silent = hasKey("nobell", args, nArgs);
3163 
3164     cancelDrag(w);
3165     if (hasKey("scrollbar", args, nArgs)) {
3166         XtVaGetValues(textD->hScrollBar, XmNmaximum, &sliderMax,
3167     	    XmNsliderSize, &sliderSize, NULL);
3168 	horizOffset = min(textD->horizOffset + textD->width, sliderMax - sliderSize);
3169 	if (textD->horizOffset == horizOffset) {
3170             ringIfNecessary(silent, w);
3171     	    return;
3172 	}
3173 	TextDSetScroll(textD, textD->topLineNum, horizOffset);
3174     }
3175     else {
3176         lineStartPos = BufStartOfLine(buf, insertPos);
3177         indent = BufCountDispChars(buf, lineStartPos, insertPos);
3178         pos = BufCountForwardDispChars(buf, lineStartPos,
3179     	        indent + textD->width / maxCharWidth);
3180         TextDSetInsertPosition(textD, pos);
3181         TextDSetScroll(textD, textD->topLineNum, textD->horizOffset + textD->width);
3182         if (textD->horizOffset == oldHorizOffset && insertPos == pos)
3183             ringIfNecessary(silent, w);
3184         checkMoveSelectionChange(w, event, insertPos, args, nArgs);
3185         checkAutoShowInsertPos(w);
3186         callCursorMovementCBs(w, event);
3187     }
3188 }
3189 
toggleOverstrikeAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)3190 static void toggleOverstrikeAP(Widget w, XEvent *event, String *args,
3191     	Cardinal *nArgs)
3192 {
3193     TextWidget tw = (TextWidget)w;
3194 
3195     if (tw->text.overstrike) {
3196     	tw->text.overstrike = False;
3197     	TextDSetCursorStyle(tw->text.textD,
3198     	    	tw->text.heavyCursor ? HEAVY_CURSOR : NORMAL_CURSOR);
3199     } else {
3200     	tw->text.overstrike = True;
3201     	if (    tw->text.textD->cursorStyle == NORMAL_CURSOR ||
3202     		tw->text.textD->cursorStyle == HEAVY_CURSOR)
3203     	    TextDSetCursorStyle(tw->text.textD, BLOCK_CURSOR);
3204     }
3205 }
3206 
scrollUpAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)3207 static void scrollUpAP(Widget w, XEvent *event, String *args,
3208     	Cardinal *nArgs)
3209 {
3210     textDisp *textD = ((TextWidget)w)->text.textD;
3211     int topLineNum, horizOffset, nLines;
3212 
3213     if (*nArgs == 0 || sscanf(args[0], "%d", &nLines) != 1)
3214     	return;
3215     if (*nArgs == 2) {
3216         /* Allow both 'page' and 'pages' */
3217         if (strncmp(args[1], "page", 4) == 0)
3218             nLines *= textD->nVisibleLines;
3219 
3220         /* 'line' or 'lines' is the only other valid possibility */
3221         else if (strncmp(args[1], "line", 4) != 0)
3222             return;
3223     }
3224     TextDGetScroll(textD, &topLineNum, &horizOffset);
3225     TextDSetScroll(textD, topLineNum-nLines, horizOffset);
3226 }
3227 
scrollDownAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)3228 static void scrollDownAP(Widget w, XEvent *event, String *args,
3229     	Cardinal *nArgs)
3230 {
3231     textDisp *textD = ((TextWidget)w)->text.textD;
3232     int topLineNum, horizOffset, nLines;
3233 
3234     if (*nArgs == 0 || sscanf(args[0], "%d", &nLines) != 1)
3235     	return;
3236     if (*nArgs == 2) {
3237         /* Allow both 'page' and 'pages' */
3238         if (strncmp(args[1], "page", 4) == 0)
3239             nLines *= textD->nVisibleLines;
3240 
3241         /* 'line' or 'lines' is the only other valid possibility */
3242         else if (strncmp(args[1], "line", 4) != 0)
3243             return;
3244     }
3245     TextDGetScroll(textD, &topLineNum, &horizOffset);
3246     TextDSetScroll(textD, topLineNum+nLines, horizOffset);
3247 }
3248 
scrollLeftAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)3249 static void scrollLeftAP(Widget w, XEvent *event, String *args,
3250     	Cardinal *nArgs)
3251 {
3252     textDisp *textD = ((TextWidget)w)->text.textD;
3253     int horizOffset, nPixels;
3254     int sliderMax, sliderSize;
3255 
3256     if (*nArgs == 0 || sscanf(args[0], "%d", &nPixels) != 1)
3257     	return;
3258     XtVaGetValues(textD->hScrollBar, XmNmaximum, &sliderMax,
3259    	XmNsliderSize, &sliderSize, NULL);
3260     horizOffset = min(max(0, textD->horizOffset - nPixels), sliderMax - sliderSize);
3261     if (textD->horizOffset != horizOffset) {
3262 	TextDSetScroll(textD, textD->topLineNum, horizOffset);
3263     }
3264 }
3265 
scrollRightAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)3266 static void scrollRightAP(Widget w, XEvent *event, String *args,
3267     	Cardinal *nArgs)
3268 {
3269     textDisp *textD = ((TextWidget)w)->text.textD;
3270     int horizOffset, nPixels;
3271     int sliderMax, sliderSize;
3272 
3273     if (*nArgs == 0 || sscanf(args[0], "%d", &nPixels) != 1)
3274     	return;
3275     XtVaGetValues(textD->hScrollBar, XmNmaximum, &sliderMax,
3276     	    XmNsliderSize, &sliderSize, NULL);
3277     horizOffset = min(max(0, textD->horizOffset + nPixels), sliderMax - sliderSize);
3278     if (textD->horizOffset != horizOffset) {
3279 	TextDSetScroll(textD, textD->topLineNum, horizOffset);
3280     }
3281 }
3282 
scrollToLineAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)3283 static void scrollToLineAP(Widget w, XEvent *event, String *args,
3284     	Cardinal *nArgs)
3285 {
3286     textDisp *textD = ((TextWidget)w)->text.textD;
3287     int topLineNum, horizOffset, lineNum;
3288 
3289     if (*nArgs == 0 || sscanf(args[0], "%d", &lineNum) != 1)
3290     	return;
3291     TextDGetScroll(textD, &topLineNum, &horizOffset);
3292     TextDSetScroll(textD, lineNum, horizOffset);
3293 }
3294 
selectAllAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)3295 static void selectAllAP(Widget w, XEvent *event, String *args,
3296 	Cardinal *nArgs)
3297 {
3298     textBuffer *buf = ((TextWidget)w)->text.textD->buffer;
3299 
3300     cancelDrag(w);
3301     BufSelect(buf, 0, buf->length);
3302 }
3303 
deselectAllAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)3304 static void deselectAllAP(Widget w, XEvent *event, String *args,
3305 	Cardinal *nArgs)
3306 {
3307     cancelDrag(w);
3308     BufUnselect(((TextWidget)w)->text.textD->buffer);
3309 }
3310 
3311 /*
3312 **  Called on the Intrinsic FocusIn event.
3313 **
3314 **  Note that the widget has no internal state about the focus, ie. it does
3315 **  not know whether it has the focus or not.
3316 */
focusInAP(Widget widget,XEvent * event,String * unused1,Cardinal * unused2)3317 static void focusInAP(Widget widget, XEvent* event, String* unused1,
3318         Cardinal* unused2)
3319 {
3320     TextWidget textwidget = (TextWidget) widget;
3321     textDisp* textD = textwidget->text.textD;
3322 
3323     /* I don't entirely understand the traversal mechanism in Motif widgets,
3324        particularly, what leads to this widget getting a focus-in event when
3325        it does not actually have the input focus.  The temporary solution is
3326        to do the comparison below, and not show the cursor when Motif says
3327        we don't have focus, but keep looking for the real answer */
3328 #if XmVersion >= 1002
3329     if (widget != XmGetFocusWidget(widget))
3330     	return;
3331 #endif
3332 
3333     /* If the timer is not already started, start it */
3334     if (textwidget->text.cursorBlinkRate != 0
3335             && textwidget->text.cursorBlinkProcID == 0) {
3336         textwidget->text.cursorBlinkProcID
3337                 = XtAppAddTimeOut(XtWidgetToApplicationContext(widget),
3338                     textwidget->text.cursorBlinkRate, cursorBlinkTimerProc,
3339                     widget);
3340     }
3341 
3342     /* Change the cursor to active style */
3343     if (textwidget->text.overstrike)
3344     	TextDSetCursorStyle(textD, BLOCK_CURSOR);
3345     else
3346         TextDSetCursorStyle(textD, (textwidget->text.heavyCursor
3347                 ? HEAVY_CURSOR
3348                 : NORMAL_CURSOR));
3349     TextDUnblankCursor(textD);
3350 
3351 #ifndef NO_XMIM
3352     /* Notify Motif input manager that widget has focus */
3353     XmImVaSetFocusValues(widget, NULL);
3354 #endif
3355 
3356     /* Call any registered focus-in callbacks */
3357     XtCallCallbacks((Widget) widget, textNfocusCallback, (XtPointer) event);
3358 }
3359 
focusOutAP(Widget w,XEvent * event,String * args,Cardinal * nArgs)3360 static void focusOutAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
3361 {
3362     textDisp *textD = ((TextWidget)w)->text.textD;
3363 
3364     /* Remove the cursor blinking timer procedure */
3365     if (((TextWidget)w)->text.cursorBlinkProcID != 0)
3366     	XtRemoveTimeOut(((TextWidget)w)->text.cursorBlinkProcID);
3367     ((TextWidget)w)->text.cursorBlinkProcID = 0;
3368 
3369     /* Leave a dim or destination cursor */
3370     TextDSetCursorStyle(textD, ((TextWidget)w)->text.motifDestOwner ?
3371     	    CARET_CURSOR : DIM_CURSOR);
3372     TextDUnblankCursor(textD);
3373 
3374     /* If there's a calltip displayed, kill it. */
3375     TextDKillCalltip(textD, 0);
3376 
3377     /* Call any registered focus-out callbacks */
3378     XtCallCallbacks((Widget)w, textNlosingFocusCallback, (XtPointer)event);
3379 }
3380 
3381 /*
3382 ** For actions involving cursor movement, "extend" keyword means incorporate
3383 ** the new cursor position in the selection, and lack of an "extend" keyword
3384 ** means cancel the existing selection
3385 */
checkMoveSelectionChange(Widget w,XEvent * event,int startPos,String * args,Cardinal * nArgs)3386 static void checkMoveSelectionChange(Widget w, XEvent *event, int startPos,
3387 	String *args, Cardinal *nArgs)
3388 {
3389     if (hasKey("extend", args, nArgs))
3390     	keyMoveExtendSelection(w, event, startPos, hasKey("rect", args, nArgs));
3391     else
3392     	BufUnselect((((TextWidget)w)->text.textD)->buffer);
3393 }
3394 
3395 /*
3396 ** If a selection change was requested via a keyboard command for moving
3397 ** the insertion cursor (usually with the "extend" keyword), adjust the
3398 ** selection to include the new cursor position, or begin a new selection
3399 ** between startPos and the new cursor position with anchor at startPos.
3400 */
keyMoveExtendSelection(Widget w,XEvent * event,int origPos,int rectangular)3401 static void keyMoveExtendSelection(Widget w, XEvent *event, int origPos,
3402 	int rectangular)
3403 {
3404     XKeyEvent *e = &event->xkey;
3405     TextWidget tw = (TextWidget)w;
3406     textDisp *textD = tw->text.textD;
3407     textBuffer *buf = textD->buffer;
3408     selection *sel = &buf->primary;
3409     int newPos = TextDGetInsertPosition(textD);
3410     int startPos, endPos, startCol, endCol, newCol, origCol;
3411     int anchor, rectAnchor, anchorLineStart;
3412 
3413     /* Moving the cursor does not take the Motif destination, but as soon as
3414        the user selects something, grab it (I'm not sure if this distinction
3415        actually makes sense, but it's what Motif was doing, back when their
3416        secondary selections actually worked correctly) */
3417     TakeMotifDestination(w, e->time);
3418 
3419     if ((sel->selected || sel->zeroWidth) && sel->rectangular && rectangular) {
3420         /* rect -> rect */
3421         newCol = BufCountDispChars(buf, BufStartOfLine(buf, newPos), newPos);
3422         startCol = min(tw->text.rectAnchor, newCol);
3423         endCol   = max(tw->text.rectAnchor, newCol);
3424         startPos = BufStartOfLine(buf, min(tw->text.anchor, newPos));
3425         endPos = BufEndOfLine(buf, max(tw->text.anchor, newPos));
3426 	BufRectSelect(buf, startPos, endPos, startCol, endCol);
3427     } else if (sel->selected && rectangular) { /* plain -> rect */
3428         newCol = BufCountDispChars(buf, BufStartOfLine(buf, newPos), newPos);
3429         if (abs(newPos - sel->start) < abs(newPos - sel->end))
3430             anchor = sel->end;
3431         else
3432             anchor = sel->start;
3433         anchorLineStart = BufStartOfLine(buf, anchor);
3434         rectAnchor = BufCountDispChars(buf, anchorLineStart, anchor);
3435         tw->text.anchor = anchor;
3436         tw->text.rectAnchor = rectAnchor;
3437         BufRectSelect(buf, BufStartOfLine(buf, min(anchor, newPos)),
3438                 BufEndOfLine(buf, max(anchor, newPos)),
3439                 min(rectAnchor, newCol), max(rectAnchor, newCol));
3440     } else if (sel->selected && sel->rectangular) { /* rect -> plain */
3441     	startPos = BufCountForwardDispChars(buf,
3442     		BufStartOfLine(buf, sel->start), sel->rectStart);
3443     	endPos = BufCountForwardDispChars(buf,
3444     		BufStartOfLine(buf, sel->end), sel->rectEnd);
3445     	if (abs(origPos - startPos) < abs(origPos - endPos))
3446     	    anchor = endPos;
3447     	else
3448     	    anchor = startPos;
3449     	BufSelect(buf, anchor, newPos);
3450     } else if (sel->selected) { /* plain -> plain */
3451         if (abs(origPos - sel->start) < abs(origPos - sel->end))
3452     	    anchor = sel->end;
3453     	else
3454     	    anchor = sel->start;
3455      	BufSelect(buf, anchor, newPos);
3456     } else if (rectangular) { /* no sel -> rect */
3457 	origCol = BufCountDispChars(buf, BufStartOfLine(buf, origPos), origPos);
3458 	newCol = BufCountDispChars(buf, BufStartOfLine(buf, newPos), newPos);
3459 	startCol = min(newCol, origCol);
3460 	endCol = max(newCol, origCol);
3461 	startPos = BufStartOfLine(buf, min(origPos, newPos));
3462 	endPos = BufEndOfLine(buf, max(origPos, newPos));
3463         tw->text.anchor = origPos;
3464         tw->text.rectAnchor = origCol;
3465 	BufRectSelect(buf, startPos, endPos, startCol, endCol);
3466     } else { /* no sel -> plain */
3467         tw->text.anchor = origPos;
3468         tw->text.rectAnchor = BufCountDispChars(buf,
3469                 BufStartOfLine(buf, origPos), origPos);
3470         BufSelect(buf, tw->text.anchor, newPos);
3471     }
3472 }
3473 
checkAutoShowInsertPos(Widget w)3474 static void checkAutoShowInsertPos(Widget w)
3475 {
3476     if (((TextWidget)w)->text.autoShowInsertPos)
3477     	TextDMakeInsertPosVisible(((TextWidget)w)->text.textD);
3478 }
3479 
checkReadOnly(Widget w)3480 static int checkReadOnly(Widget w)
3481 {
3482     if (((TextWidget)w)->text.readOnly) {
3483     	XBell(XtDisplay(w), 0);
3484 	return True;
3485     }
3486     return False;
3487 }
3488 
3489 /*
3490 ** Insert text "chars" at the cursor position, as if the text had been
3491 ** typed.  Same as TextInsertAtCursor, but without the complicated auto-wrap
3492 ** scanning and re-formatting.
3493 */
simpleInsertAtCursor(Widget w,char * chars,XEvent * event,int allowPendingDelete)3494 static void simpleInsertAtCursor(Widget w, char *chars, XEvent *event,
3495     	int allowPendingDelete)
3496 {
3497     textDisp *textD = ((TextWidget)w)->text.textD;
3498     textBuffer *buf = textD->buffer;
3499     char *c;
3500 
3501     if (allowPendingDelete && pendingSelection(w)) {
3502     	BufReplaceSelected(buf, chars);
3503     	TextDSetInsertPosition(textD, buf->cursorPosHint);
3504     } else if (((TextWidget)w)->text.overstrike) {
3505     	for (c=chars; *c!='\0' && *c!='\n'; c++);
3506     	if (*c == '\n')
3507     	    TextDInsert(textD, chars);
3508     	else
3509     	    TextDOverstrike(textD, chars);
3510     } else
3511     	TextDInsert(textD, chars);
3512     checkAutoShowInsertPos(w);
3513     callCursorMovementCBs(w, event);
3514 }
3515 
3516 /*
3517 ** If there's a selection, delete it and position the cursor where the
3518 ** selection was deleted.  (Called by routines which do deletion to check
3519 ** first for and do possible selection delete)
3520 */
deletePendingSelection(Widget w,XEvent * event)3521 static int deletePendingSelection(Widget w, XEvent *event)
3522 {
3523     textDisp *textD = ((TextWidget)w)->text.textD;
3524     textBuffer *buf = textD->buffer;
3525 
3526     if (((TextWidget)w)->text.textD->buffer->primary.selected) {
3527 	BufRemoveSelected(buf);
3528 	TextDSetInsertPosition(textD, buf->cursorPosHint);
3529     	checkAutoShowInsertPos(w);
3530     	callCursorMovementCBs(w, event);
3531 	return True;
3532     } else
3533 	return False;
3534 }
3535 
3536 /*
3537 ** Return true if pending delete is on and there's a selection contiguous
3538 ** with the cursor ready to be deleted.  These criteria are used to decide
3539 ** if typing a character or inserting something should delete the selection
3540 ** first.
3541 */
pendingSelection(Widget w)3542 static int pendingSelection(Widget w)
3543 {
3544     selection *sel = &((TextWidget)w)->text.textD->buffer->primary;
3545     int pos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
3546 
3547     return ((TextWidget)w)->text.pendingDelete && sel->selected &&
3548     	    pos >= sel->start && pos <= sel->end;
3549 }
3550 
3551 /*
3552 ** Check if tab emulation is on and if there are emulated tabs before the
3553 ** cursor, and if so, delete an emulated tab as a unit.  Also finishes up
3554 ** by calling checkAutoShowInsertPos and callCursorMovementCBs, so the
3555 ** calling action proc can just return (this is necessary to preserve
3556 ** emTabsBeforeCursor which is otherwise cleared by callCursorMovementCBs).
3557 */
deleteEmulatedTab(Widget w,XEvent * event)3558 static int deleteEmulatedTab(Widget w, XEvent *event)
3559 {
3560     textDisp *textD = ((TextWidget)w)->text.textD;
3561     textBuffer *buf = ((TextWidget)w)->text.textD->buffer;
3562     int emTabDist = ((TextWidget)w)->text.emulateTabs;
3563     int emTabsBeforeCursor = ((TextWidget)w)->text.emTabsBeforeCursor;
3564     int startIndent, toIndent, insertPos, startPos, lineStart;
3565     int pos, indent, startPosIndent;
3566     char c, *spaceString;
3567 
3568     if (emTabDist <= 0 || emTabsBeforeCursor <= 0)
3569     	return False;
3570 
3571     /* Find the position of the previous tab stop */
3572     insertPos = TextDGetInsertPosition(textD);
3573     lineStart = BufStartOfLine(buf, insertPos);
3574     startIndent = BufCountDispChars(buf, lineStart, insertPos);
3575     toIndent = (startIndent-1) - ((startIndent-1) % emTabDist);
3576 
3577     /* Find the position at which to begin deleting (stop at non-whitespace
3578        characters) */
3579     startPosIndent = indent = 0;
3580     startPos = lineStart;
3581     for (pos=lineStart; pos < insertPos; pos++) {
3582     	c = BufGetCharacter(buf, pos);
3583     	indent += BufCharWidth(c, indent, buf->tabDist, buf->nullSubsChar);
3584     	if (indent > toIndent)
3585     	    break;
3586     	startPosIndent = indent;
3587     	startPos = pos + 1;
3588     }
3589 
3590     /* Just to make sure, check that we're not deleting any non-white chars */
3591     for (pos=insertPos-1; pos>=startPos; pos--) {
3592     	c = BufGetCharacter(buf, pos);
3593     	if (c != ' ' && c != '\t') {
3594     	    startPos = pos + 1;
3595     	    break;
3596     	}
3597     }
3598 
3599     /* Do the text replacement and reposition the cursor.  If any spaces need
3600        to be inserted to make up for a deleted tab, do a BufReplace, otherwise,
3601        do a BufRemove. */
3602     if (startPosIndent < toIndent) {
3603     	spaceString = (char*)NEditMalloc(toIndent - startPosIndent + 1);
3604     	memset(spaceString, ' ', toIndent-startPosIndent);
3605     	spaceString[toIndent - startPosIndent] = '\0';
3606     	BufReplace(buf, startPos, insertPos, spaceString);
3607     	TextDSetInsertPosition(textD, startPos + toIndent - startPosIndent);
3608     	NEditFree(spaceString);
3609     } else {
3610 	BufRemove(buf, startPos, insertPos);
3611 	TextDSetInsertPosition(textD, startPos);
3612     }
3613 
3614     /* The normal cursor movement stuff would usually be called by the action
3615        routine, but this wraps around it to restore emTabsBeforeCursor */
3616     checkAutoShowInsertPos(w);
3617     callCursorMovementCBs(w, event);
3618 
3619     /* Decrement and restore the marker for consecutive emulated tabs, which
3620        would otherwise have been zeroed by callCursorMovementCBs */
3621     ((TextWidget)w)->text.emTabsBeforeCursor = emTabsBeforeCursor - 1;
3622     return True;
3623 }
3624 
3625 /*
3626 ** Select the word or whitespace adjacent to the cursor, and move the cursor
3627 ** to its end.  pointerX is used as a tie-breaker, when the cursor is at the
3628 ** boundary between a word and some white-space.  If the cursor is on the
3629 ** left, the word or space on the left is used.  If it's on the right, that
3630 ** is used instead.
3631 */
selectWord(Widget w,int pointerX)3632 static void selectWord(Widget w, int pointerX)
3633 {
3634     TextWidget tw = (TextWidget)w;
3635     textBuffer *buf = tw->text.textD->buffer;
3636     int x, y, insertPos = TextDGetInsertPosition(tw->text.textD);
3637 
3638     TextPosToXY(w, insertPos, &x, &y);
3639     if (pointerX < x && insertPos > 0 && BufGetCharacter(buf, insertPos-1) != '\n')
3640 	insertPos--;
3641     BufSelect(buf, startOfWord(tw, insertPos), endOfWord(tw, insertPos));
3642 }
3643 
startOfWord(TextWidget w,int pos)3644 static int startOfWord(TextWidget w, int pos)
3645 {
3646     int startPos;
3647     textBuffer *buf = w->text.textD->buffer;
3648     char *delimiters=w->text.delimiters;
3649     char c = BufGetCharacter(buf, pos);
3650 
3651     if (c == ' ' || c== '\t') {
3652         if (!spanBackward(buf, pos, " \t", False, &startPos))
3653 	    return 0;
3654     } else if (strchr(delimiters, c)) {
3655         if (!spanBackward(buf, pos, delimiters, True, &startPos))
3656 	    return 0;
3657     } else {
3658         if (!BufSearchBackward(buf, pos, delimiters, &startPos))
3659 	    return 0;
3660     }
3661     return min(pos, startPos+1);
3662 
3663 }
3664 
endOfWord(TextWidget w,int pos)3665 static int endOfWord(TextWidget w, int pos)
3666 {
3667     int endPos;
3668     textBuffer *buf = w->text.textD->buffer;
3669     char *delimiters=w->text.delimiters;
3670     char c = BufGetCharacter(buf, pos);
3671 
3672     if (c == ' ' || c== '\t') {
3673         if (!spanForward(buf, pos, " \t", False, &endPos))
3674 	    return buf->length;
3675     } else if (strchr(delimiters, c)) {
3676         if (!spanForward(buf, pos, delimiters, True, &endPos))
3677 	    return buf->length;
3678     } else {
3679         if (!BufSearchForward(buf, pos, delimiters, &endPos))
3680 	    return buf->length;
3681     }
3682     return endPos;
3683 }
3684 
3685 /*
3686 ** Search forwards in buffer "buf" for the first character NOT in
3687 ** "searchChars",  starting with the character "startPos", and returning the
3688 ** result in "foundPos" returns True if found, False if not. If ignoreSpace
3689 ** is set, then Space, Tab, and Newlines are ignored in searchChars.
3690 */
spanForward(textBuffer * buf,int startPos,char * searchChars,int ignoreSpace,int * foundPos)3691 static int spanForward(textBuffer *buf, int startPos, char *searchChars,
3692 	int ignoreSpace, int *foundPos)
3693 {
3694     int pos;
3695     char *c;
3696 
3697     pos = startPos;
3698     while (pos < buf->length) {
3699 	for (c=searchChars; *c!='\0'; c++)
3700             if(!(ignoreSpace && (*c==' ' || *c=='\t' || *c=='\n')))
3701                 if (BufGetCharacter(buf, pos) == *c)
3702 		    break;
3703         if(*c == 0) {
3704             *foundPos = pos;
3705             return True;
3706         }
3707         pos++;
3708     }
3709     *foundPos = buf->length;
3710     return False;
3711 }
3712 
3713 /*
3714 ** Search backwards in buffer "buf" for the first character NOT in
3715 ** "searchChars",  starting with the character BEFORE "startPos", returning the
3716 ** result in "foundPos" returns True if found, False if not. If ignoreSpace is
3717 ** set, then Space, Tab, and Newlines are ignored in searchChars.
3718 */
spanBackward(textBuffer * buf,int startPos,char * searchChars,int ignoreSpace,int * foundPos)3719 static int spanBackward(textBuffer *buf, int startPos, char *searchChars, int
3720     	ignoreSpace, int *foundPos)
3721 {
3722     int pos;
3723     char *c;
3724 
3725     if (startPos == 0) {
3726         *foundPos = 0;
3727         return False;
3728     }
3729     pos = startPos == 0 ? 0 : startPos - 1;
3730     while (pos >= 0) {
3731         for (c=searchChars; *c!='\0'; c++)
3732             if(!(ignoreSpace && (*c==' ' || *c=='\t' || *c=='\n')))
3733                 if (BufGetCharacter(buf, pos) == *c)
3734 		    break;
3735         if(*c == 0) {
3736             *foundPos = pos;
3737             return True;
3738         }
3739         pos--;
3740     }
3741     *foundPos = 0;
3742     return False;
3743 }
3744 
3745 /*
3746 ** Select the line containing the cursor, including the terminating newline,
3747 ** and move the cursor to its end.
3748 */
selectLine(Widget w)3749 static void selectLine(Widget w)
3750 {
3751     textDisp *textD = ((TextWidget)w)->text.textD;
3752     int insertPos = TextDGetInsertPosition(textD);
3753     int endPos, startPos;
3754 
3755     endPos = BufEndOfLine(textD->buffer, insertPos);
3756     startPos = BufStartOfLine(textD->buffer, insertPos);
3757     BufSelect(textD->buffer, startPos, min(endPos + 1, textD->buffer->length));
3758     TextDSetInsertPosition(textD, endPos);
3759 }
3760 
3761 /*
3762 ** Given a new mouse pointer location, pass the position on to the
3763 ** autoscroll timer routine, and make sure the timer is on when it's
3764 ** needed and off when it's not.
3765 */
checkAutoScroll(TextWidget w,int x,int y)3766 static void checkAutoScroll(TextWidget w, int x, int y)
3767 {
3768     int inWindow;
3769 
3770     /* Is the pointer in or out of the window? */
3771     inWindow = x >= w->text.textD->left &&
3772     	    x < w->core.width - w->text.marginWidth &&
3773     	    y >= w->text.marginHeight &&
3774     	    y < w->core.height - w->text.marginHeight;
3775 
3776     /* If it's in the window, cancel the timer procedure */
3777     if (inWindow) {
3778     	if (w->text.autoScrollProcID != 0)
3779     	    XtRemoveTimeOut(w->text.autoScrollProcID);;
3780     	w->text.autoScrollProcID = 0;
3781     	return;
3782     }
3783 
3784     /* If the timer is not already started, start it */
3785     if (w->text.autoScrollProcID == 0) {
3786     	w->text.autoScrollProcID = XtAppAddTimeOut(
3787     	    	XtWidgetToApplicationContext((Widget)w),
3788     		0, autoScrollTimerProc, w);
3789     }
3790 
3791     /* Pass on the newest mouse location to the autoscroll routine */
3792     w->text.mouseX = x;
3793     w->text.mouseY = y;
3794 }
3795 
3796 /*
3797 ** Reset drag state and cancel the auto-scroll timer
3798 */
endDrag(Widget w)3799 static void endDrag(Widget w)
3800 {
3801     if (((TextWidget)w)->text.autoScrollProcID != 0)
3802     	XtRemoveTimeOut(((TextWidget)w)->text.autoScrollProcID);
3803     ((TextWidget)w)->text.autoScrollProcID = 0;
3804     if (((TextWidget)w)->text.dragState == MOUSE_PAN)
3805     	XUngrabPointer(XtDisplay(w), CurrentTime);
3806     ((TextWidget)w)->text.dragState = NOT_CLICKED;
3807 }
3808 
3809 /*
3810 ** Cancel any drag operation that might be in progress.  Should be included
3811 ** in nearly every key event to cleanly end any dragging before edits are made
3812 ** which might change the insert position or the content of the buffer during
3813 ** a drag operation)
3814 */
cancelDrag(Widget w)3815 static void cancelDrag(Widget w)
3816 {
3817     int dragState = ((TextWidget)w)->text.dragState;
3818 
3819     if (((TextWidget)w)->text.autoScrollProcID != 0)
3820     	XtRemoveTimeOut(((TextWidget)w)->text.autoScrollProcID);
3821     if (dragState == SECONDARY_DRAG || dragState == SECONDARY_RECT_DRAG)
3822     	BufSecondaryUnselect(((TextWidget)w)->text.textD->buffer);
3823     if (dragState == PRIMARY_BLOCK_DRAG)
3824     	CancelBlockDrag((TextWidget)w);
3825     if (dragState == MOUSE_PAN)
3826     	XUngrabPointer(XtDisplay(w), CurrentTime);
3827     if (dragState != NOT_CLICKED)
3828     	((TextWidget)w)->text.dragState = DRAG_CANCELED;
3829 }
3830 
3831 /*
3832 ** Do operations triggered by cursor movement: Call cursor movement callback
3833 ** procedure(s), and cancel marker indicating that the cursor is after one or
3834 ** more just-entered emulated tabs (spaces to be deleted as a unit).
3835 */
callCursorMovementCBs(Widget w,XEvent * event)3836 static void callCursorMovementCBs(Widget w, XEvent *event)
3837 {
3838     ((TextWidget)w)->text.emTabsBeforeCursor = 0;
3839     XtCallCallbacks((Widget)w, textNcursorMovementCallback, (XtPointer)event);
3840 }
3841 
3842 /*
3843 ** Adjust the selection as the mouse is dragged to position: (x, y).
3844 */
adjustSelection(TextWidget tw,int x,int y)3845 static void adjustSelection(TextWidget tw, int x, int y)
3846 {
3847     textDisp *textD = tw->text.textD;
3848     textBuffer *buf = textD->buffer;
3849     int row, col, startCol, endCol, startPos, endPos;
3850     int newPos = TextDXYToPosition(textD, x, y);
3851 
3852     /* Adjust the selection */
3853     if (tw->text.dragState == PRIMARY_RECT_DRAG) {
3854 	TextDXYToUnconstrainedPosition(textD, x, y, &row, &col);
3855     	col = TextDOffsetWrappedColumn(textD, row, col);
3856 	startCol = min(tw->text.rectAnchor, col);
3857 	endCol = max(tw->text.rectAnchor, col);
3858 	startPos = BufStartOfLine(buf, min(tw->text.anchor, newPos));
3859 	endPos = BufEndOfLine(buf, max(tw->text.anchor, newPos));
3860 	BufRectSelect(buf, startPos, endPos, startCol, endCol);
3861     } else if (tw->text.multiClickState == ONE_CLICK) {
3862     	startPos = startOfWord(tw, min(tw->text.anchor, newPos));
3863     	endPos = endOfWord(tw, max(tw->text.anchor, newPos));
3864     	BufSelect(buf, startPos, endPos);
3865     	newPos = newPos < tw->text.anchor ? startPos : endPos;
3866     } else if (tw->text.multiClickState == TWO_CLICKS) {
3867         startPos = BufStartOfLine(buf, min(tw->text.anchor, newPos));
3868         endPos = BufEndOfLine(buf, max(tw->text.anchor, newPos));
3869         BufSelect(buf, startPos, min(endPos+1, buf->length));
3870         newPos = newPos < tw->text.anchor ? startPos : endPos;
3871     } else
3872     	BufSelect(buf, tw->text.anchor, newPos);
3873 
3874     /* Move the cursor */
3875     TextDSetInsertPosition(textD, newPos);
3876     callCursorMovementCBs((Widget)tw, NULL);
3877 }
3878 
adjustSecondarySelection(TextWidget tw,int x,int y)3879 static void adjustSecondarySelection(TextWidget tw, int x, int y)
3880 {
3881     textDisp *textD = tw->text.textD;
3882     textBuffer *buf = textD->buffer;
3883     int row, col, startCol, endCol, startPos, endPos;
3884     int newPos = TextDXYToPosition(textD, x, y);
3885 
3886     if (tw->text.dragState == SECONDARY_RECT_DRAG) {
3887 	TextDXYToUnconstrainedPosition(textD, x, y, &row, &col);
3888     	col = TextDOffsetWrappedColumn(textD, row, col);
3889 	startCol = min(tw->text.rectAnchor, col);
3890 	endCol = max(tw->text.rectAnchor, col);
3891 	startPos = BufStartOfLine(buf, min(tw->text.anchor, newPos));
3892 	endPos = BufEndOfLine(buf, max(tw->text.anchor, newPos));
3893 	BufSecRectSelect(buf, startPos, endPos, startCol, endCol);
3894     } else
3895     	BufSecondarySelect(textD->buffer, tw->text.anchor, newPos);
3896 }
3897 
3898 /*
3899 ** Wrap multi-line text in argument "text" to be inserted at the end of the
3900 ** text on line "startLine" and return the result.  If "breakBefore" is
3901 ** non-NULL, allow wrapping to extend back into "startLine", in which case
3902 ** the returned text will include the wrapped part of "startLine", and
3903 ** "breakBefore" will return the number of characters at the end of
3904 ** "startLine" that were absorbed into the returned string.  "breakBefore"
3905 ** will return zero if no characters were absorbed into the returned string.
3906 ** The buffer offset of text in the widget's text buffer is needed so that
3907 ** smart indent (which can be triggered by wrapping) can search back farther
3908 ** in the buffer than just the text in startLine.
3909 */
wrapText(TextWidget tw,char * startLine,char * text,int bufOffset,int wrapMargin,int * breakBefore)3910 static char *wrapText(TextWidget tw, char *startLine, char *text, int bufOffset,
3911     	int wrapMargin, int *breakBefore)
3912 {
3913     textBuffer *wrapBuf, *buf = tw->text.textD->buffer;
3914     int startLineLen = strlen(startLine);
3915     int colNum, pos, lineStartPos, limitPos, breakAt, charsAdded;
3916     int firstBreak = -1, tabDist = buf->tabDist;
3917     char c, *wrappedText;
3918 
3919     /* Create a temporary text buffer and load it with the strings */
3920     wrapBuf = BufCreate();
3921     BufInsert(wrapBuf, 0, startLine);
3922     BufInsert(wrapBuf, wrapBuf->length, text);
3923 
3924     /* Scan the buffer for long lines and apply wrapLine when wrapMargin is
3925        exceeded.  limitPos enforces no breaks in the "startLine" part of the
3926        string (if requested), and prevents re-scanning of long unbreakable
3927        lines for each character beyond the margin */
3928     colNum = 0;
3929     pos = 0;
3930     lineStartPos = 0;
3931     limitPos = breakBefore == NULL ? startLineLen : 0;
3932     while (pos < wrapBuf->length) {
3933     	c = BufGetCharacter(wrapBuf, pos);
3934     	if (c == '\n') {
3935     	    lineStartPos = limitPos = pos + 1;
3936     	    colNum = 0;
3937     	} else {
3938     	    colNum += BufCharWidth(c, colNum, tabDist, buf->nullSubsChar);
3939     	    if (colNum > wrapMargin) {
3940     		if (!wrapLine(tw, wrapBuf, bufOffset, lineStartPos, pos,
3941     		    	limitPos, &breakAt, &charsAdded)) {
3942     	    	    limitPos = max(pos, limitPos);
3943     		} else {
3944     	    	    lineStartPos = limitPos = breakAt+1;
3945     	    	    pos += charsAdded;
3946     	    	    colNum = BufCountDispChars(wrapBuf, lineStartPos, pos+1);
3947     	    	    if (firstBreak == -1)
3948     	    		firstBreak = breakAt;
3949     		}
3950     	    }
3951     	}
3952     	pos++;
3953     }
3954 
3955     /* Return the wrapped text, possibly including part of startLine */
3956     if (breakBefore == NULL)
3957     	wrappedText = BufGetRange(wrapBuf, startLineLen, wrapBuf->length);
3958     else {
3959     	*breakBefore = firstBreak != -1 && firstBreak < startLineLen ?
3960     	    	startLineLen - firstBreak : 0;
3961     	wrappedText = BufGetRange(wrapBuf, startLineLen - *breakBefore,
3962     	    	wrapBuf->length);
3963     }
3964     BufFree(wrapBuf);
3965     return wrappedText;
3966 }
3967 
3968 /*
3969 ** Wraps the end of a line beginning at lineStartPos and ending at lineEndPos
3970 ** in "buf", at the last white-space on the line >= limitPos.  (The implicit
3971 ** assumption is that just the last character of the line exceeds the wrap
3972 ** margin, and anywhere on the line we can wrap is correct).  Returns False if
3973 ** unable to wrap the line.  "breakAt", returns the character position at
3974 ** which the line was broken,
3975 **
3976 ** Auto-wrapping can also trigger auto-indent.  The additional parameter
3977 ** bufOffset is needed when auto-indent is set to smart indent and the smart
3978 ** indent routines need to scan far back in the buffer.  "charsAdded" returns
3979 ** the number of characters added to acheive the auto-indent.  wrapMargin is
3980 ** used to decide whether auto-indent should be skipped because the indent
3981 ** string itself would exceed the wrap margin.
3982 */
wrapLine(TextWidget tw,textBuffer * buf,int bufOffset,int lineStartPos,int lineEndPos,int limitPos,int * breakAt,int * charsAdded)3983 static int wrapLine(TextWidget tw, textBuffer *buf, int bufOffset,
3984     	int lineStartPos, int lineEndPos, int limitPos, int *breakAt,
3985 	int *charsAdded)
3986 {
3987     int p, length, column;
3988     char c, *indentStr;
3989 
3990     /* Scan backward for whitespace or BOL.  If BOL, return False, no
3991        whitespace in line at which to wrap */
3992     for (p=lineEndPos; ; p--) {
3993 	if (p < lineStartPos || p < limitPos)
3994      	    return False;
3995      	c = BufGetCharacter(buf, p);
3996      	if (c == '\t' || c == ' ')
3997      	    break;
3998     }
3999 
4000     /* Create an auto-indent string to insert to do wrap.  If the auto
4001        indent string reaches the wrap position, slice the auto-indent
4002        back off and return to the left margin */
4003     if (tw->text.autoIndent || tw->text.smartIndent) {
4004 	indentStr = createIndentString(tw, buf, bufOffset, lineStartPos,
4005 	    	lineEndPos, &length, &column);
4006 	if (column >= p-lineStartPos)
4007 	    indentStr[1] = '\0';
4008     } else {
4009     	indentStr = "\n";
4010     	length = 1;
4011     }
4012 
4013     /* Replace the whitespace character with the auto-indent string
4014        and return the stats */
4015     BufReplace(buf, p, p+1, indentStr);
4016     if (tw->text.autoIndent || tw->text.smartIndent)
4017     	NEditFree(indentStr);
4018     *breakAt = p;
4019     *charsAdded = length-1;
4020     return True;
4021 }
4022 
4023 /*
4024 ** Create and return an auto-indent string to add a newline at lineEndPos to a
4025 ** line starting at lineStartPos in buf.  "buf" may or may not be the real
4026 ** text buffer for the widget.  If it is not the widget's text buffer it's
4027 ** offset position from the real buffer must be specified in "bufOffset" to
4028 ** allow the smart-indent routines to scan back as far as necessary. The
4029 ** string length is returned in "length" (or "length" can be passed as NULL,
4030 ** and the indent column is returned in "column" (if non NULL).
4031 */
createIndentString(TextWidget tw,textBuffer * buf,int bufOffset,int lineStartPos,int lineEndPos,int * length,int * column)4032 static char *createIndentString(TextWidget tw, textBuffer *buf, int bufOffset,
4033     	int lineStartPos, int lineEndPos, int *length, int *column)
4034 {
4035     textDisp *textD = tw->text.textD;
4036     int pos, indent = -1, tabDist = textD->buffer->tabDist;
4037     int i, useTabs = textD->buffer->useTabs;
4038     char *indentPtr, *indentStr, c;
4039     smartIndentCBStruct smartIndent;
4040 
4041     /* If smart indent is on, call the smart indent callback.  It is not
4042        called when multi-line changes are being made (lineStartPos != 0),
4043        because smart indent needs to search back an indeterminate distance
4044        through the buffer, and reconciling that with wrapping changes made,
4045        but not yet committed in the buffer, would make programming smart
4046        indent more difficult for users and make everything more complicated */
4047     if (tw->text.smartIndent && (lineStartPos == 0 || buf == textD->buffer)) {
4048     	smartIndent.reason = NEWLINE_INDENT_NEEDED;
4049 	smartIndent.pos = lineEndPos + bufOffset;
4050     	smartIndent.indentRequest = 0;
4051 	smartIndent.charsTyped = NULL;
4052     	XtCallCallbacks((Widget)tw, textNsmartIndentCallback,
4053     	    	(XtPointer)&smartIndent);
4054     	indent = smartIndent.indentRequest;
4055     }
4056 
4057     /* If smart indent wasn't used, measure the indent distance of the line */
4058     if (indent == -1) {
4059 	indent = 0;
4060 	for (pos=lineStartPos; pos<lineEndPos; pos++) {
4061 	    c =  BufGetCharacter(buf, pos);
4062 	    if (c != ' ' && c != '\t')
4063 		break;
4064 	    if (c == '\t')
4065 		indent += tabDist - (indent % tabDist);
4066 	    else
4067 		indent++;
4068 	}
4069     }
4070 
4071     /* Allocate and create a string of tabs and spaces to achieve the indent */
4072     indentPtr = indentStr = (char*)NEditMalloc(indent + 2);
4073     *indentPtr++ = '\n';
4074     if (useTabs) {
4075 	for (i=0; i < indent / tabDist; i++)
4076 	    *indentPtr++ = '\t';
4077 	for (i=0; i < indent % tabDist; i++)
4078 	    *indentPtr++ = ' ';
4079     } else {
4080 	for (i=0; i < indent; i++)
4081 	    *indentPtr++ = ' ';
4082     }
4083     *indentPtr = '\0';
4084 
4085     /* Return any requested stats */
4086     if (length != NULL)
4087     	*length = indentPtr - indentStr;
4088     if (column != NULL)
4089     	*column = indent;
4090 
4091     return indentStr;
4092 }
4093 
4094 /*
4095 ** Xt timer procedure for autoscrolling
4096 */
autoScrollTimerProc(XtPointer clientData,XtIntervalId * id)4097 static void autoScrollTimerProc(XtPointer clientData, XtIntervalId *id)
4098 {
4099     TextWidget w = (TextWidget)clientData;
4100     textDisp *textD = w->text.textD;
4101     int topLineNum, horizOffset, newPos, cursorX, y;
4102     int fontWidth = textD->fontStruct->max_bounds.width;
4103     int fontHeight = textD->fontStruct->ascent + textD->fontStruct->descent;
4104 
4105     /* For vertical autoscrolling just dragging the mouse outside of the top
4106        or bottom of the window is sufficient, for horizontal (non-rectangular)
4107        scrolling, see if the position where the CURSOR would go is outside */
4108     newPos = TextDXYToPosition(textD, w->text.mouseX, w->text.mouseY);
4109     if (w->text.dragState == PRIMARY_RECT_DRAG)
4110     	cursorX = w->text.mouseX;
4111     else if (!TextDPositionToXY(textD, newPos, &cursorX, &y))
4112     	cursorX = w->text.mouseX;
4113 
4114     /* Scroll away from the pointer, 1 character (horizontal), or 1 character
4115        for each fontHeight distance from the mouse to the text (vertical) */
4116     TextDGetScroll(textD, &topLineNum, &horizOffset);
4117     if (cursorX >= (int)w->core.width - w->text.marginWidth)
4118     	horizOffset += fontWidth;
4119     else if (w->text.mouseX < textD->left)
4120     	horizOffset -= fontWidth;
4121     if (w->text.mouseY >= (int)w->core.height - w->text.marginHeight)
4122     	topLineNum += 1 + ((w->text.mouseY - (int)w->core.height -
4123     	    	w->text.marginHeight) / fontHeight) + 1;
4124     else if (w->text.mouseY < w->text.marginHeight)
4125     	topLineNum -= 1 + ((w->text.marginHeight-w->text.mouseY) / fontHeight);
4126     TextDSetScroll(textD, topLineNum, horizOffset);
4127 
4128     /* Continue the drag operation in progress.  If none is in progress
4129        (safety check) don't continue to re-establish the timer proc */
4130     if (w->text.dragState == PRIMARY_DRAG) {
4131 	adjustSelection(w, w->text.mouseX, w->text.mouseY);
4132     } else if (w->text.dragState == PRIMARY_RECT_DRAG) {
4133 	adjustSelection(w, w->text.mouseX, w->text.mouseY);
4134     } else if (w->text.dragState == SECONDARY_DRAG) {
4135 	adjustSecondarySelection(w, w->text.mouseX, w->text.mouseY);
4136     } else if (w->text.dragState == SECONDARY_RECT_DRAG) {
4137 	adjustSecondarySelection(w, w->text.mouseX, w->text.mouseY);
4138     } else if (w->text.dragState == PRIMARY_BLOCK_DRAG) {
4139     	BlockDragSelection(w, w->text.mouseX, w->text.mouseY, USE_LAST);
4140     } else {
4141     	w->text.autoScrollProcID = 0;
4142     	return;
4143     }
4144 
4145     /* re-establish the timer proc (this routine) to continue processing */
4146     w->text.autoScrollProcID = XtAppAddTimeOut(
4147     	    XtWidgetToApplicationContext((Widget)w),
4148     	    w->text.mouseY >= w->text.marginHeight &&
4149     	    w->text.mouseY < w->core.height - w->text.marginHeight ?
4150     	    (VERTICAL_SCROLL_DELAY*fontWidth) / fontHeight :
4151     	    VERTICAL_SCROLL_DELAY, autoScrollTimerProc, w);
4152 }
4153 
4154 /*
4155 ** Xt timer procedure for cursor blinking
4156 */
cursorBlinkTimerProc(XtPointer clientData,XtIntervalId * id)4157 static void cursorBlinkTimerProc(XtPointer clientData, XtIntervalId *id)
4158 {
4159     TextWidget w = (TextWidget)clientData;
4160     textDisp *textD = w->text.textD;
4161 
4162     /* Blink the cursor */
4163     if (textD->cursorOn)
4164     	TextDBlankCursor(textD);
4165     else
4166     	TextDUnblankCursor(textD);
4167 
4168     /* re-establish the timer proc (this routine) to continue processing */
4169     w->text.cursorBlinkProcID = XtAppAddTimeOut(
4170     	    XtWidgetToApplicationContext((Widget)w),
4171     	    w->text.cursorBlinkRate, cursorBlinkTimerProc, w);
4172 }
4173 
4174 /*
4175 **  Sets the caret to on or off and restart the caret blink timer.
4176 **  This could be used by other modules to modify the caret's blinking.
4177 */
ResetCursorBlink(TextWidget textWidget,Boolean startsBlanked)4178 void ResetCursorBlink(TextWidget textWidget, Boolean startsBlanked)
4179 {
4180     if (0 != textWidget->text.cursorBlinkRate)
4181     {
4182         if (0 != textWidget->text.cursorBlinkProcID)
4183         {
4184             XtRemoveTimeOut(textWidget->text.cursorBlinkProcID);
4185         }
4186 
4187         if (startsBlanked)
4188         {
4189             TextDBlankCursor(textWidget->text.textD);
4190         } else
4191         {
4192             TextDUnblankCursor(textWidget->text.textD);
4193         }
4194 
4195         textWidget->text.cursorBlinkProcID
4196                 = XtAppAddTimeOut(XtWidgetToApplicationContext((Widget) textWidget),
4197                     textWidget->text.cursorBlinkRate, cursorBlinkTimerProc,
4198                     textWidget);
4199     }
4200 }
4201 
4202 /*
4203 ** look at an action procedure's arguments to see if argument "key" has been
4204 ** specified in the argument list
4205 */
hasKey(const char * key,const String * args,const Cardinal * nArgs)4206 static int hasKey(const char *key, const String *args, const Cardinal *nArgs)
4207 {
4208     int i;
4209 
4210     for (i=0; i<(int)*nArgs; i++)
4211     	if (!strCaseCmp(args[i], key))
4212     	    return True;
4213     return False;
4214 }
4215 
max(int i1,int i2)4216 static int max(int i1, int i2)
4217 {
4218     return i1 >= i2 ? i1 : i2;
4219 }
4220 
min(int i1,int i2)4221 static int min(int i1, int i2)
4222 {
4223     return i1 <= i2 ? i1 : i2;
4224 }
4225 
4226 /*
4227 ** strCaseCmp compares its arguments and returns 0 if the two strings
4228 ** are equal IGNORING case differences.  Otherwise returns 1.
4229 */
4230 
strCaseCmp(const char * str1,const char * str2)4231 static int strCaseCmp(const char *str1, const char *str2)
4232 {
4233     unsigned const char *c1 = (unsigned const char*) str1;
4234     unsigned const char *c2 = (unsigned const char*) str2;
4235 
4236     for (; *c1!='\0' && *c2!='\0'; c1++, c2++)
4237     	if (toupper(*c1) != toupper(*c2))
4238     	    return 1;
4239     if (*c1 == *c2) {
4240         return(0);
4241     }
4242     else {
4243         return(1);
4244     }
4245 }
4246 
ringIfNecessary(Boolean silent,Widget w)4247 static void ringIfNecessary(Boolean silent, Widget w)
4248 {
4249     if (!silent)
4250         XBell(XtDisplay(w), 0);
4251 }
4252