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