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