- Change help version to 5.4DEV
[nedit.git] / source / text.c
blobb72f034a2e5cbbe206efd5f9ba60107ee9559e70
1 static const char CVSID[] = "$Id: text.c,v 1.36 2002/10/14 18:41:04 n8gray 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. *
12 * *
13 * This software is distributed in the hope that it will be useful, but WITHOUT *
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
16 * for more details. *
17 * *
18 * You should have received a copy of the GNU General Public License along with *
19 * software; if not, write to the Free Software Foundation, Inc., 59 Temple *
20 * Place, Suite 330, Boston, MA 02111-1307 USA *
21 * *
22 * Nirvana Text Editor *
23 * June 15, 1995 *
24 * *
25 *******************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 #include "../config.h"
29 #endif
31 #include "text.h"
32 #include "textP.h"
33 #include "textBuf.h"
34 #include "textDisp.h"
35 #include "textSel.h"
36 #include "textDrag.h"
37 #include "nedit.h"
38 #include "preferences.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(TextWidget w, XtPointer unused,
84 XEvent *event, Boolean *continue_to_dispatch);
85 static void handleShowPointer(TextWidget 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 /* Backspace */
255 "Ctrl<KeyPress>osfBackSpace: delete_previous_word()\n"
256 "<KeyPress>osfBackSpace: delete_previous_character()\n"
258 /* Delete */
259 "Alt Shift Ctrl<KeyPress>osfDelete: cut_primary(\"rect\")\n"
260 "Meta Shift Ctrl<KeyPress>osfDelete: cut_primary(\"rect\")\n"
261 "Shift Ctrl<KeyPress>osfDelete: cut_primary()\n"
262 "Ctrl<KeyPress>osfDelete: delete_to_end_of_line()\n"
263 "Shift<KeyPress>osfDelete: cut_clipboard()\n"
264 "<KeyPress>osfDelete: delete_next_character()\n"
266 /* Insert */
267 "Alt Shift Ctrl<KeyPress>osfInsert: copy_primary(\"rect\")\n"
268 "Meta Shift Ctrl<KeyPress>osfInsert: copy_primary(\"rect\")\n"
269 "Shift Ctrl<KeyPress>osfInsert: copy_primary()\n"
270 "Shift<KeyPress>osfInsert: paste_clipboard()\n"
271 "Ctrl<KeyPress>osfInsert: copy_clipboard()\n"
272 "~Shift ~Ctrl<KeyPress>osfInsert: toggle_overstrike()\n"
274 /* Cut/Copy/Paste */
275 "Shift Ctrl<KeyPress>osfCut: cut_primary()\n"
276 "<KeyPress>osfCut: cut_clipboard()\n"
277 "<KeyPress>osfCopy: copy_clipboard()\n"
278 "<KeyPress>osfPaste: paste_clipboard()\n"
279 "<KeyPress>osfPrimaryPaste: copy_primary()\n"
281 /* BeginLine */
282 "Alt Shift Ctrl<KeyPress>osfBeginLine: beginning_of_file(\"extend\", \"rect\")\n"
283 "Meta Shift Ctrl<KeyPress>osfBeginLine: beginning_of_file(\"extend\" \"rect\")\n"
284 "Alt Shift<KeyPress>osfBeginLine: beginning_of_line(\"extend\", \"rect\")\n"
285 "Meta Shift<KeyPress>osfBeginLine: beginning_of_line(\"extend\", \"rect\")\n"
286 "Shift Ctrl<KeyPress>osfBeginLine: beginning_of_file(\"extend\")\n"
287 "Ctrl<KeyPress>osfBeginLine: beginning_of_file()\n"
288 "Shift<KeyPress>osfBeginLine: beginning_of_line(\"extend\")\n"
289 "~Alt~Shift~Ctrl~Meta<KeyPress>osfBeginLine: beginning_of_line()\n"
291 /* EndLine */
292 "Alt Shift Ctrl<KeyPress>osfEndLine: end_of_file(\"extend\", \"rect\")\n"
293 "Meta Shift Ctrl<KeyPress>osfEndLine: end_of_file(\"extend\", \"rect\")\n"
294 "Alt Shift<KeyPress>osfEndLine: end_of_line(\"extend\", \"rect\")\n"
295 "Meta Shift<KeyPress>osfEndLine: end_of_line(\"extend\", \"rect\")\n"
296 "Shift Ctrl<KeyPress>osfEndLine: end_of_file(\"extend\")\n"
297 "Ctrl<KeyPress>osfEndLine: end_of_file()\n"
298 "Shift<KeyPress>osfEndLine: end_of_line(\"extend\")\n"
299 "~Alt~Shift~Ctrl~Meta<KeyPress>osfEndLine: end_of_line()\n"
301 /* Left */
302 "Alt Shift Ctrl<KeyPress>osfLeft: backward_word(\"extend\", \"rect\")\n"
303 "Meta Shift Ctrl<KeyPress>osfLeft: backward_word(\"extend\", \"rect\")\n"
304 "Alt Shift<KeyPress>osfLeft: key_select(\"left\", \"rect\")\n"
305 "Meta Shift<KeyPress>osfLeft: key_select(\"left\", \"rect\")\n"
306 "Shift Ctrl<KeyPress>osfLeft: backward_word(\"extend\")\n"
307 "Ctrl<KeyPress>osfLeft: backward_word()\n"
308 "Shift<KeyPress>osfLeft: key_select(\"left\")\n"
309 "~Alt~Shift~Ctrl~Meta<KeyPress>osfLeft: backward_character()\n"
311 /* Right */
312 "Alt Shift Ctrl<KeyPress>osfRight: forward_word(\"extend\", \"rect\")\n"
313 "Meta Shift Ctrl<KeyPress>osfRight: forward_word(\"extend\", \"rect\")\n"
314 "Alt Shift<KeyPress>osfRight: key_select(\"right\", \"rect\")\n"
315 "Meta Shift<KeyPress>osfRight: key_select(\"right\", \"rect\")\n"
316 "Shift Ctrl<KeyPress>osfRight: forward_word(\"extend\")\n"
317 "Ctrl<KeyPress>osfRight: forward_word()\n"
318 "Shift<KeyPress>osfRight: key_select(\"right\")\n"
319 "~Alt~Shift~Ctrl~Meta<KeyPress>osfRight: forward_character()\n"
321 /* Up */
322 "Alt Shift Ctrl<KeyPress>osfUp: backward_paragraph(\"extend\", \"rect\")\n"
323 "Meta Shift Ctrl<KeyPress>osfUp: backward_paragraph(\"extend\", \"rect\")\n"
324 "Alt Shift<KeyPress>osfUp: process_shift_up(\"rect\")\n"
325 "Meta Shift<KeyPress>osfUp: process_shift_up(\"rect\")\n"
326 "Shift Ctrl<KeyPress>osfUp: backward_paragraph(\"extend\")\n"
327 "Ctrl<KeyPress>osfUp: backward_paragraph()\n"
328 "Shift<KeyPress>osfUp: process_shift_up()\n"
329 "~Alt~Shift~Ctrl~Meta<KeyPress>osfUp: process_up()\n"
331 /* Down */
332 "Alt Shift Ctrl<KeyPress>osfDown: forward_paragraph(\"extend\", \"rect\")\n"
333 "Meta Shift Ctrl<KeyPress>osfDown: forward_paragraph(\"extend\", \"rect\")\n"
334 "Alt Shift<KeyPress>osfDown: process_shift_down(\"rect\")\n"
335 "Meta Shift<KeyPress>osfDown: process_shift_down(\"rect\")\n"
336 "Shift Ctrl<KeyPress>osfDown: forward_paragraph(\"extend\")\n"
337 "Ctrl<KeyPress>osfDown: forward_paragraph()\n"
338 "Shift<KeyPress>osfDown: process_shift_down()\n"
339 "~Alt~Shift~Ctrl~Meta<KeyPress>osfDown: process_down()\n"
341 /* PageUp */
342 "Alt Shift Ctrl<KeyPress>osfPageUp: page_left(\"extend\", \"rect\")\n"
343 "Meta Shift Ctrl<KeyPress>osfPageUp: page_left(\"extend\", \"rect\")\n"
344 "Alt Shift<KeyPress>osfPageUp: previous_page(\"extend\", \"rect\")\n"
345 "Meta Shift<KeyPress>osfPageUp: previous_page(\"extend\", \"rect\")\n"
346 "Shift Ctrl<KeyPress>osfPageUp: page_left(\"extend\")\n"
347 "Ctrl<KeyPress>osfPageUp: page_left()\n"
348 "Shift<KeyPress>osfPageUp: previous_page(\"extend\")\n"
349 "~Alt ~Shift ~Ctrl ~Meta<KeyPress>osfPageUp: previous_page()\n"
351 /* PageDown */
352 "Alt Shift Ctrl<KeyPress>osfPageDown: page_right(\"extend\", \"rect\")\n"
353 "Meta Shift Ctrl<KeyPress>osfPageDown: page_right(\"extend\", \"rect\")\n"
354 "Alt Shift<KeyPress>osfPageDown: next_page(\"extend\", \"rect\")\n"
355 "Meta Shift<KeyPress>osfPageDown: next_page(\"extend\", \"rect\")\n"
356 "Shift Ctrl<KeyPress>osfPageDown: page_right(\"extend\")\n"
357 "Ctrl<KeyPress>osfPageDown: page_right()\n"
358 "Shift<KeyPress>osfPageDown: next_page(\"extend\")\n"
359 "~Alt ~Shift ~Ctrl ~Meta<KeyPress>osfPageDown: next_page()\n"
361 /* PageLeft and PageRight are placed later than the PageUp/PageDown
362 bindings. Some systems map osfPageLeft to Ctrl-PageUp.
363 Overloading this single key gives problems, and we want to give
364 priority to the normal version. */
366 /* PageLeft */
367 "Alt Shift<KeyPress>osfPageLeft: page_left(\"extend\", \"rect\")\n"
368 "Meta Shift<KeyPress>osfPageLeft: page_left(\"extend\", \"rect\")\n"
369 "Shift<KeyPress>osfPageLeft: page_left(\"extend\")\n"
370 "~Alt ~Shift ~Ctrl ~Meta<KeyPress>osfPageLeft: page_left()\n"
372 /* PageRight */
373 "Alt Shift<KeyPress>osfPageRight: page_right(\"extend\", \"rect\")\n"
374 "Meta Shift<KeyPress>osfPageRight: page_right(\"extend\", \"rect\")\n"
375 "Shift<KeyPress>osfPageRight: page_right(\"extend\")\n"
376 "~Alt ~Shift ~Ctrl ~Meta<KeyPress>osfPageRight: page_right()\n"
378 "Shift<KeyPress>osfSelect: key_select()\n"
379 "<KeyPress>osfCancel: process_cancel()\n"
380 "Ctrl~Alt~Meta<KeyPress>v: paste_clipboard()\n"
381 "Ctrl~Alt~Meta<KeyPress>c: copy_clipboard()\n"
382 "Ctrl~Alt~Meta<KeyPress>x: cut_clipboard()\n"
383 "Ctrl~Alt~Meta<KeyPress>u: delete_to_start_of_line()\n"
384 "Ctrl<KeyPress>Return: newline_and_indent()\n"
385 "Shift<KeyPress>Return: newline_no_indent()\n"
386 "<KeyPress>Return: newline()\n"
387 "Ctrl<KeyPress>Tab: self_insert()\n"
388 "<KeyPress>Tab: process_tab()\n"
389 "Alt Shift Ctrl<KeyPress>space: key_select(\"rect\")\n"
390 "Meta Shift Ctrl<KeyPress>space: key_select(\"rect\")\n"
391 "Shift Ctrl~Meta~Alt<KeyPress>space: key_select()\n"
392 "Ctrl~Meta~Alt<KeyPress>slash: select_all()\n"
393 "Ctrl~Meta~Alt<KeyPress>backslash: deselect_all()\n"
394 "<KeyPress>: self_insert()\n"
395 "Alt Ctrl<Btn1Down>: move_destination()\n"
396 "Meta Ctrl<Btn1Down>: move_destination()\n"
397 "Shift Ctrl<Btn1Down>: extend_start(\"rect\")\n"
398 "Shift<Btn1Down>: extend_start()\n"
399 "<Btn1Down>: grab_focus()\n"
400 "Button1 Ctrl<MotionNotify>: extend_adjust(\"rect\")\n"
401 "Button1~Ctrl<MotionNotify>: extend_adjust()\n"
402 "<Btn1Up>: extend_end()\n"
403 "<Btn2Down>: secondary_or_drag_start()\n"
404 "Shift Ctrl Button2<MotionNotify>: secondary_or_drag_adjust(\"rect\", \"copy\", \"overlay\")\n"
405 "Shift Button2<MotionNotify>: secondary_or_drag_adjust(\"copy\")\n"
406 "Ctrl Button2<MotionNotify>: secondary_or_drag_adjust(\"rect\", \"overlay\")\n"
407 "Button2<MotionNotify>: secondary_or_drag_adjust()\n"
408 "Shift Ctrl<Btn2Up>: move_to_or_end_drag(\"copy\", \"overlay\")\n"
409 "Shift <Btn2Up>: move_to_or_end_drag(\"copy\")\n"
410 "Alt<Btn2Up>: exchange()\n"
411 "Meta<Btn2Up>: exchange()\n"
412 "Ctrl<Btn2Up>: copy_to_or_end_drag(\"overlay\")\n"
413 "<Btn2Up>: copy_to_or_end_drag()\n"
414 "Ctrl~Meta~Alt<Btn3Down>: mouse_pan()\n"
415 "Ctrl~Meta~Alt Button3<MotionNotify>: mouse_pan()\n"
416 "<Btn3Up>: end_drag()\n"
417 "<FocusIn>: focusIn()\n"
418 "<FocusOut>: focusOut()\n"
419 /* Support for mouse wheel in XFree86 */
420 "Shift<Btn4Down>,<Btn4Up>: scroll_up(1)\n"
421 "Shift<Btn5Down>,<Btn5Up>: scroll_down(1)\n"
422 "Ctrl<Btn4Down>,<Btn4Up>: previous_page()\n"
423 "Ctrl<Btn5Down>,<Btn5Up>: next_page(1)\n"
424 "<Btn4Down>,<Btn4Up>: scroll_up(5)\n"
425 "<Btn5Down>,<Btn5Up>: scroll_down(5)\n";
426 /* some of the translations from the Motif text widget were not picked up:
427 :<KeyPress>osfSelect: set-anchor()\n\
428 :<KeyPress>osfActivate: activate()\n\
429 ~Shift Ctrl~Meta~Alt<KeyPress>Return: activate()\n\
430 ~Shift Ctrl~Meta~Alt<KeyPress>space: set-anchor()\n\
431 :<KeyPress>osfClear: clear-selection()\n\
432 ~Shift~Ctrl~Meta~Alt<KeyPress>Return: process-return()\n\
433 Shift~Meta~Alt<KeyPress>Tab: prev-tab-group()\n\
434 Ctrl~Meta~Alt<KeyPress>Tab: next-tab-group()\n\
435 <UnmapNotify>: unmap()\n\
436 <EnterNotify>: enter()\n\
437 <LeaveNotify>: leave()\n
441 static XtActionsRec actionsList[] = {
442 {"self-insert", selfInsertAP},
443 {"self_insert", selfInsertAP},
444 {"grab-focus", grabFocusAP},
445 {"grab_focus", grabFocusAP},
446 {"extend-adjust", extendAdjustAP},
447 {"extend_adjust", extendAdjustAP},
448 {"extend-start", extendStartAP},
449 {"extend_start", extendStartAP},
450 {"extend-end", extendEndAP},
451 {"extend_end", extendEndAP},
452 {"secondary-adjust", secondaryAdjustAP},
453 {"secondary_adjust", secondaryAdjustAP},
454 {"secondary-or-drag-adjust", secondaryOrDragAdjustAP},
455 {"secondary_or_drag_adjust", secondaryOrDragAdjustAP},
456 {"secondary-start", secondaryStartAP},
457 {"secondary_start", secondaryStartAP},
458 {"secondary-or-drag-start", secondaryOrDragStartAP},
459 {"secondary_or_drag_start", secondaryOrDragStartAP},
460 {"process-bdrag", secondaryOrDragStartAP},
461 {"process_bdrag", secondaryOrDragStartAP},
462 {"move-destination", moveDestinationAP},
463 {"move_destination", moveDestinationAP},
464 {"move-to", moveToAP},
465 {"move_to", moveToAP},
466 {"move-to-or-end-drag", moveToOrEndDragAP},
467 {"move_to_or_end_drag", moveToOrEndDragAP},
468 {"end_drag", endDragAP},
469 {"copy-to", copyToAP},
470 {"copy_to", copyToAP},
471 {"copy-to-or-end-drag", copyToOrEndDragAP},
472 {"copy_to_or_end_drag", copyToOrEndDragAP},
473 {"exchange", exchangeAP},
474 {"process-cancel", processCancelAP},
475 {"process_cancel", processCancelAP},
476 {"paste-clipboard", pasteClipboardAP},
477 {"paste_clipboard", pasteClipboardAP},
478 {"copy-clipboard", copyClipboardAP},
479 {"copy_clipboard", copyClipboardAP},
480 {"cut-clipboard", cutClipboardAP},
481 {"cut_clipboard", cutClipboardAP},
482 {"copy-primary", copyPrimaryAP},
483 {"copy_primary", copyPrimaryAP},
484 {"cut-primary", cutPrimaryAP},
485 {"cut_primary", cutPrimaryAP},
486 {"newline", newlineAP},
487 {"newline-and-indent", newlineAndIndentAP},
488 {"newline_and_indent", newlineAndIndentAP},
489 {"newline-no-indent", newlineNoIndentAP},
490 {"newline_no_indent", newlineNoIndentAP},
491 {"delete-selection", deleteSelectionAP},
492 {"delete_selection", deleteSelectionAP},
493 {"delete-previous-character", deletePreviousCharacterAP},
494 {"delete_previous_character", deletePreviousCharacterAP},
495 {"delete-next-character", deleteNextCharacterAP},
496 {"delete_next_character", deleteNextCharacterAP},
497 {"delete-previous-word", deletePreviousWordAP},
498 {"delete_previous_word", deletePreviousWordAP},
499 {"delete-next-word", deleteNextWordAP},
500 {"delete_next_word", deleteNextWordAP},
501 {"delete-to-start-of-line", deleteToStartOfLineAP},
502 {"delete_to_start_of_line", deleteToStartOfLineAP},
503 {"delete-to-end-of-line", deleteToEndOfLineAP},
504 {"delete_to_end_of_line", deleteToEndOfLineAP},
505 {"forward-character", forwardCharacterAP},
506 {"forward_character", forwardCharacterAP},
507 {"backward-character", backwardCharacterAP},
508 {"backward_character", backwardCharacterAP},
509 {"key-select", keySelectAP},
510 {"key_select", keySelectAP},
511 {"process-up", processUpAP},
512 {"process_up", processUpAP},
513 {"process-down", processDownAP},
514 {"process_down", processDownAP},
515 {"process-shift-up", processShiftUpAP},
516 {"process_shift_up", processShiftUpAP},
517 {"process-shift-down", processShiftDownAP},
518 {"process_shift_down", processShiftDownAP},
519 {"process-home", beginningOfLineAP},
520 {"process_home", beginningOfLineAP},
521 {"forward-word", forwardWordAP},
522 {"forward_word", forwardWordAP},
523 {"backward-word", backwardWordAP},
524 {"backward_word", backwardWordAP},
525 {"forward-paragraph", forwardParagraphAP},
526 {"forward_paragraph", forwardParagraphAP},
527 {"backward-paragraph", backwardParagraphAP},
528 {"backward_paragraph", backwardParagraphAP},
529 {"beginning-of-line", beginningOfLineAP},
530 {"beginning_of_line", beginningOfLineAP},
531 {"end-of-line", endOfLineAP},
532 {"end_of_line", endOfLineAP},
533 {"beginning-of-file", beginningOfFileAP},
534 {"beginning_of_file", beginningOfFileAP},
535 {"end-of-file", endOfFileAP},
536 {"end_of_file", endOfFileAP},
537 {"next-page", nextPageAP},
538 {"next_page", nextPageAP},
539 {"previous-page", previousPageAP},
540 {"previous_page", previousPageAP},
541 {"page-left", pageLeftAP},
542 {"page_left", pageLeftAP},
543 {"page-right", pageRightAP},
544 {"page_right", pageRightAP},
545 {"toggle-overstrike", toggleOverstrikeAP},
546 {"toggle_overstrike", toggleOverstrikeAP},
547 {"scroll-up", scrollUpAP},
548 {"scroll_up", scrollUpAP},
549 {"scroll-down", scrollDownAP},
550 {"scroll_down", scrollDownAP},
551 {"scroll_left", scrollLeftAP},
552 {"scroll_right", scrollRightAP},
553 {"scroll-to-line", scrollToLineAP},
554 {"scroll_to_line", scrollToLineAP},
555 {"select-all", selectAllAP},
556 {"select_all", selectAllAP},
557 {"deselect-all", deselectAllAP},
558 {"deselect_all", deselectAllAP},
559 {"focusIn", focusInAP},
560 {"focusOut", focusOutAP},
561 {"process-return", selfInsertAP},
562 {"process_return", selfInsertAP},
563 {"process-tab", processTabAP},
564 {"process_tab", processTabAP},
565 {"insert-string", insertStringAP},
566 {"insert_string", insertStringAP},
567 {"mouse_pan", mousePanAP},
570 /* The motif text widget defined a bunch of actions which the nedit text
571 widget as-of-yet does not support:
573 Actions which were not bound to keys (for emacs emulation, some of
574 them should probably be supported:
576 kill-next-character()
577 kill-next-word()
578 kill-previous-character()
579 kill-previous-word()
580 kill-selection()
581 kill-to-end-of-line()
582 kill-to-start-of-line()
583 unkill()
584 next-line()
585 newline-and-backup()
586 beep()
587 redraw-display()
588 scroll-one-line-down()
589 scroll-one-line-up()
590 set-insertion-point()
592 Actions which are not particularly useful:
594 set-anchor()
595 activate()
596 clear-selection() -> this is a wierd one
597 do-quick-action() -> don't think this ever worked
598 Help()
599 next-tab-group()
600 select-adjust()
601 select-start()
602 select-end()
605 static XtResource resources[] = {
606 {XmNhighlightThickness, XmCHighlightThickness, XmRDimension,
607 sizeof(Dimension), XtOffset(TextWidget, primitive.highlight_thickness),
608 XmRInt, 0},
609 {XmNshadowThickness, XmCShadowThickness, XmRDimension, sizeof(Dimension),
610 XtOffset(TextWidget, primitive.shadow_thickness), XmRInt, 0},
611 {textNfont, textCFont, XmRFontStruct, sizeof(XFontStruct *),
612 XtOffset(TextWidget, text.fontStruct), XmRString, "fixed"},
613 {textNselectForeground, textCSelectForeground, XmRPixel, sizeof(Pixel),
614 XtOffset(TextWidget, text.selectFGPixel), XmRString, "black"},
615 {textNselectBackground, textCSelectBackground, XmRPixel, sizeof(Pixel),
616 XtOffset(TextWidget, text.selectBGPixel), XmRString, "#cccccc"},
617 {textNhighlightForeground, textCHighlightForeground, XmRPixel,sizeof(Pixel),
618 XtOffset(TextWidget, text.highlightFGPixel), XmRString, "white"},
619 {textNhighlightBackground, textCHighlightBackground, XmRPixel,sizeof(Pixel),
620 XtOffset(TextWidget, text.highlightBGPixel), XmRString, "red"},
621 {textNcursorForeground, textCCursorForeground, XmRPixel,sizeof(Pixel),
622 XtOffset(TextWidget, text.cursorFGPixel), XmRString, "black"},
623 {textNbacklightCharTypes,textCBacklightCharTypes,XmRString,sizeof(XmString),
624 XtOffset(TextWidget, text.backlightCharTypes), XmRString, NULL},
625 {textNlineNumForeground, textCLineNumForeground, XmRPixel,sizeof(Pixel),
626 XtOffset(TextWidget, text.lineNumFGPixel), XmRString, "black"},
627 {textNrows, textCRows, XmRInt,sizeof(int),
628 XtOffset(TextWidget, text.rows), XmRString, "24"},
629 {textNcolumns, textCColumns, XmRInt, sizeof(int),
630 XtOffset(TextWidget, text.columns), XmRString, "80"},
631 {textNmarginWidth, textCMarginWidth, XmRInt, sizeof(int),
632 XtOffset(TextWidget, text.marginWidth), XmRString, "5"},
633 {textNmarginHeight, textCMarginHeight, XmRInt, sizeof(int),
634 XtOffset(TextWidget, text.marginHeight), XmRString, "5"},
635 {textNpendingDelete, textCPendingDelete, XmRBoolean, sizeof(Boolean),
636 XtOffset(TextWidget, text.pendingDelete), XmRString, "True"},
637 {textNautoWrap, textCAutoWrap, XmRBoolean, sizeof(Boolean),
638 XtOffset(TextWidget, text.autoWrap), XmRString, "True"},
639 {textNcontinuousWrap, textCContinuousWrap, XmRBoolean, sizeof(Boolean),
640 XtOffset(TextWidget, text.continuousWrap), XmRString, "True"},
641 {textNautoIndent, textCAutoIndent, XmRBoolean, sizeof(Boolean),
642 XtOffset(TextWidget, text.autoIndent), XmRString, "True"},
643 {textNsmartIndent, textCSmartIndent, XmRBoolean, sizeof(Boolean),
644 XtOffset(TextWidget, text.smartIndent), XmRString, "False"},
645 {textNoverstrike, textCOverstrike, XmRBoolean, sizeof(Boolean),
646 XtOffset(TextWidget, text.overstrike), XmRString, "False"},
647 {textNheavyCursor, textCHeavyCursor, XmRBoolean, sizeof(Boolean),
648 XtOffset(TextWidget, text.heavyCursor), XmRString, "False"},
649 {textNreadOnly, textCReadOnly, XmRBoolean, sizeof(Boolean),
650 XtOffset(TextWidget, text.readOnly), XmRString, "False"},
651 {textNwrapMargin, textCWrapMargin, XmRInt, sizeof(int),
652 XtOffset(TextWidget, text.wrapMargin), XmRString, "0"},
653 {textNhScrollBar, textCHScrollBar, XmRWidget, sizeof(Widget),
654 XtOffset(TextWidget, text.hScrollBar), XmRString, ""},
655 {textNvScrollBar, textCVScrollBar, XmRWidget, sizeof(Widget),
656 XtOffset(TextWidget, text.vScrollBar), XmRString, ""},
657 {textNlineNumCols, textCLineNumCols, XmRInt, sizeof(int),
658 XtOffset(TextWidget, text.lineNumCols), XmRString, "0"},
659 {textNautoShowInsertPos, textCAutoShowInsertPos, XmRBoolean,
660 sizeof(Boolean), XtOffset(TextWidget, text.autoShowInsertPos),
661 XmRString, "True"},
662 {textNautoWrapPastedText, textCAutoWrapPastedText, XmRBoolean,
663 sizeof(Boolean), XtOffset(TextWidget, text.autoWrapPastedText),
664 XmRString, "False"},
665 {textNwordDelimiters, textCWordDelimiters, XmRString, sizeof(char *),
666 XtOffset(TextWidget, text.delimiters), XmRString,
667 ".,/\\`'!@#%^&*()-=+{}[]\":;<>?"},
668 {textNblinkRate, textCBlinkRate, XmRInt, sizeof(int),
669 XtOffset(TextWidget, text.cursorBlinkRate), XmRString, "500"},
670 {textNemulateTabs, textCEmulateTabs, XmRInt, sizeof(int),
671 XtOffset(TextWidget, text.emulateTabs), XmRString, "0"},
672 {textNfocusCallback, textCFocusCallback, XmRCallback, sizeof(caddr_t),
673 XtOffset(TextWidget, text.focusInCB), XtRCallback, NULL},
674 {textNlosingFocusCallback, textCLosingFocusCallback, XmRCallback,
675 sizeof(caddr_t), XtOffset(TextWidget, text.focusOutCB), XtRCallback,NULL},
676 {textNcursorMovementCallback, textCCursorMovementCallback, XmRCallback,
677 sizeof(caddr_t), XtOffset(TextWidget, text.cursorCB), XtRCallback, NULL},
678 {textNdragStartCallback, textCDragStartCallback, XmRCallback,
679 sizeof(caddr_t), XtOffset(TextWidget, text.dragStartCB), XtRCallback,
680 NULL},
681 {textNdragEndCallback, textCDragEndCallback, XmRCallback,
682 sizeof(caddr_t), XtOffset(TextWidget, text.dragEndCB), XtRCallback, NULL},
683 {textNsmartIndentCallback, textCSmartIndentCallback, XmRCallback,
684 sizeof(caddr_t), XtOffset(TextWidget, text.smartIndentCB), XtRCallback,
685 NULL},
686 {textNcursorVPadding, textCCursorVPadding, XtRCardinal, sizeof(Cardinal),
687 XtOffset(TextWidget, text.cursorVPadding), XmRString, "0"},
690 static TextClassRec textClassRec = {
691 /* CoreClassPart */
693 (WidgetClass) &xmPrimitiveClassRec, /* superclass */
694 "Text", /* class_name */
695 sizeof(TextRec), /* widget_size */
696 NULL, /* class_initialize */
697 NULL, /* class_part_initialize */
698 FALSE, /* class_inited */
699 (XtInitProc)initialize, /* initialize */
700 NULL, /* initialize_hook */
701 realize, /* realize */
702 actionsList, /* actions */
703 XtNumber(actionsList), /* num_actions */
704 resources, /* resources */
705 XtNumber(resources), /* num_resources */
706 NULLQUARK, /* xrm_class */
707 TRUE, /* compress_motion */
708 TRUE, /* compress_exposure */
709 TRUE, /* compress_enterleave */
710 FALSE, /* visible_interest */
711 (XtWidgetProc)destroy, /* destroy */
712 (XtWidgetProc)resize, /* resize */
713 (XtExposeProc)redisplay, /* expose */
714 (XtSetValuesFunc)setValues, /* set_values */
715 NULL, /* set_values_hook */
716 XtInheritSetValuesAlmost, /* set_values_almost */
717 NULL, /* get_values_hook */
718 NULL, /* accept_focus */
719 XtVersion, /* version */
720 NULL, /* callback private */
721 defaultTranslations, /* tm_table */
722 queryGeometry, /* query_geometry */
723 NULL, /* display_accelerator */
724 NULL, /* extension */
726 /* Motif primitive class fields */
728 (XtWidgetProc)_XtInherit, /* Primitive border_highlight */
729 (XtWidgetProc)_XtInherit, /* Primitive border_unhighlight */
730 NULL, /*XtInheritTranslations,*/ /* translations */
731 NULL, /* arm_and_activate */
732 NULL, /* get resources */
733 0, /* num get_resources */
734 NULL, /* extension */
736 /* Text class part */
738 0, /* ignored */
742 WidgetClass textWidgetClass = (WidgetClass)&textClassRec;
743 #define NEDIT_HIDE_CURSOR_MASK (KeyPressMask)
744 #define NEDIT_SHOW_CURSOR_MASK (FocusChangeMask | PointerMotionMask | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask)
745 static char empty_bits[] = {0x00, 0x00, 0x00, 0x00};
746 static Cursor empty_cursor = 0;
749 ** Widget initialize method
751 static void initialize(TextWidget request, TextWidget new)
753 XFontStruct *fs = new->text.fontStruct;
754 char *delimiters;
755 textBuffer *buf;
756 Pixel white, black;
757 int textLeft;
758 int charWidth = fs->max_bounds.width;
759 int marginWidth = new->text.marginWidth;
760 int lineNumCols = new->text.lineNumCols;
761 Pixmap empty_pixmap;
762 XColor black_color;
763 Display *theDisplay;
765 /* Set the initial window size based on the rows and columns resources */
766 if (request->core.width == 0)
767 new->core.width = charWidth * new->text.columns + marginWidth*2 +
768 (lineNumCols == 0 ? 0 : marginWidth + charWidth * lineNumCols);
769 if (request->core.height == 0)
770 new->core.height = (fs->ascent + fs->descent) * new->text.rows +
771 new->text.marginHeight * 2;
773 /* The default colors work for B&W as well as color, except for
774 selectFGPixel and selectBGPixel, where color highlighting looks
775 much better without reverse video, so if we get here, and the
776 selection is totally unreadable because of the bad default colors,
777 swap the colors and make the selection reverse video */
778 white = WhitePixelOfScreen(XtScreen((Widget)new));
779 black = BlackPixelOfScreen(XtScreen((Widget)new));
780 if ( new->text.selectBGPixel == white &&
781 new->core.background_pixel == white &&
782 new->text.selectFGPixel == black &&
783 new->primitive.foreground == black) {
784 new->text.selectBGPixel = black;
785 new->text.selectFGPixel = white;
788 /* Create the initial text buffer for the widget to display (which can
789 be replaced later with TextSetBuffer) */
790 buf = BufCreate();
792 /* Create and initialize the text-display part of the widget */
793 textLeft = new->text.marginWidth +
794 (lineNumCols == 0 ? 0 : marginWidth + charWidth * lineNumCols);
795 new->text.textD = TextDCreate((Widget)new, new->text.hScrollBar,
796 new->text.vScrollBar, textLeft, new->text.marginHeight,
797 new->core.width - marginWidth - textLeft,
798 new->core.height - new->text.marginHeight * 2,
799 lineNumCols == 0 ? 0 : marginWidth,
800 lineNumCols == 0 ? 0 : lineNumCols * charWidth,
801 buf, new->text.fontStruct, new->core.background_pixel,
802 new->primitive.foreground, new->text.selectFGPixel,
803 new->text.selectBGPixel, new->text.highlightFGPixel,
804 new->text.highlightBGPixel, new->text.cursorFGPixel,
805 new->text.lineNumFGPixel,
806 new->text.continuousWrap, new->text.wrapMargin,
807 new->text.backlightCharTypes);
809 /* Add mandatory delimiters blank, tab, and newline to the list of
810 delimiters. The memory use scheme here is that new values are
811 always copied, and can therefore be safely freed on subsequent
812 set-values calls or destroy */
813 delimiters = XtMalloc(strlen(new->text.delimiters) + 4);
814 sprintf(delimiters, "%s%s", " \t\n", new->text.delimiters);
815 new->text.delimiters = delimiters;
817 /* Start with the cursor blanked (widgets don't have focus on creation,
818 the initial FocusIn event will unblank it and get blinking started) */
819 new->text.textD->cursorOn = False;
821 /* Initialize the widget variables */
822 new->text.autoScrollProcID = 0;
823 new->text.cursorBlinkProcID = 0;
824 new->text.dragState = NOT_CLICKED;
825 new->text.multiClickState = NO_CLICKS;
826 new->text.lastBtnDown = 0;
827 new->text.selectionOwner = False;
828 new->text.motifDestOwner = False;
829 new->text.emTabsBeforeCursor = 0;
831 #ifndef NO_XMIM
832 /* Register the widget to the input manager */
833 XmImRegister((Widget)new, 0);
834 /* In case some Resources for the IC need to be set, add them below */
835 XmImVaSetValues((Widget)new, NULL);
836 #endif
838 XtAddEventHandler((Widget)new, GraphicsExpose, True,
839 (XtEventHandler)redisplayGE, (Opaque)NULL);
841 if (GetPrefTypingHidesPointer()) {
842 /* Set up the empty Cursor */
843 if (empty_cursor == 0) {
844 theDisplay = XtDisplay((Widget)new);
845 empty_pixmap = XCreateBitmapFromData(theDisplay,
846 RootWindowOfScreen(XtScreen((Widget)new)), empty_bits, 1, 1);
847 XParseColor(theDisplay, DefaultColormapOfScreen(XtScreen((Widget)new)),
848 "black", &black_color);
849 empty_cursor = XCreatePixmapCursor(theDisplay, empty_pixmap,
850 empty_pixmap, &black_color, &black_color, 0, 0);
853 /* Add event handler to hide the pointer on keypresses */
854 XtAddEventHandler((Widget)new, NEDIT_HIDE_CURSOR_MASK, False,
855 (XtEventHandler)handleHidePointer, (Opaque)NULL);
859 /* Hide the pointer while the user is typing */
860 static void handleHidePointer(TextWidget w, XtPointer unused,
861 XEvent *event, Boolean *continue_to_dispatch) {
862 ShowHidePointer(w, True);
865 /* Restore the pointer if the mouse moves or focus changes */
866 static void handleShowPointer(TextWidget w, XtPointer unused,
867 XEvent *event, Boolean *continue_to_dispatch) {
868 ShowHidePointer(w, False);
871 void ShowHidePointer(TextWidget w, Boolean hidePointer)
873 if (GetPrefTypingHidesPointer()) {
874 if (hidePointer != w->text.textD->pointerHidden) {
875 if (hidePointer) {
876 /* Don't listen for keypresses any more */
877 XtRemoveEventHandler((Widget)w, NEDIT_HIDE_CURSOR_MASK, False,
878 (XtEventHandler)handleHidePointer, (Opaque)NULL);
879 /* Switch to empty cursor */
880 XDefineCursor(XtDisplay(w), XtWindow(w), empty_cursor);
882 w->text.textD->pointerHidden = True;
884 /* Listen to mouse movement, focus change, and button presses */
885 XtAddEventHandler((Widget)w, NEDIT_SHOW_CURSOR_MASK,
886 False, (XtEventHandler)handleShowPointer, (Opaque)NULL);
888 else {
889 /* Don't listen to mouse/focus events any more */
890 XtRemoveEventHandler((Widget)w, NEDIT_SHOW_CURSOR_MASK,
891 False, (XtEventHandler)handleShowPointer, (Opaque)NULL);
892 /* Switch to regular cursor */
893 XUndefineCursor(XtDisplay(w), XtWindow(w));
895 w->text.textD->pointerHidden = False;
897 /* Listen for keypresses now */
898 XtAddEventHandler((Widget)w, NEDIT_HIDE_CURSOR_MASK, False,
899 (XtEventHandler)handleHidePointer, (Opaque)NULL);
906 ** Widget destroy method
908 static void destroy(TextWidget w)
910 textBuffer *buf;
912 /* Free the text display and possibly the attached buffer. The buffer
913 is freed only if after removing all of the modify procs (by calling
914 StopHandlingXSelections and TextDFree) there are no modify procs
915 left */
916 StopHandlingXSelections((Widget)w);
917 buf = w->text.textD->buffer;
918 TextDFree(w->text.textD);
919 if (buf->nModifyProcs == 0)
920 BufFree(buf);
922 if (w->text.cursorBlinkProcID != 0)
923 XtRemoveTimeOut(w->text.cursorBlinkProcID);
924 XtFree(w->text.delimiters);
925 XtRemoveAllCallbacks((Widget)w, textNfocusCallback);
926 XtRemoveAllCallbacks((Widget)w, textNlosingFocusCallback);
927 XtRemoveAllCallbacks((Widget)w, textNcursorMovementCallback);
928 XtRemoveAllCallbacks((Widget)w, textNdragStartCallback);
929 XtRemoveAllCallbacks((Widget)w, textNdragEndCallback);
931 #ifndef NO_XMIM
932 /* Unregister the widget from the input manager */
933 XmImUnregister((Widget)w);
934 #endif
938 ** Widget resize method. Called when the size of the widget changes
940 static void resize(TextWidget w)
942 XFontStruct *fs = w->text.fontStruct;
943 int height = w->core.height, width = w->core.width;
944 int marginWidth = w->text.marginWidth, marginHeight = w->text.marginHeight;
945 int lineNumAreaWidth = w->text.lineNumCols == 0 ? 0 : w->text.marginWidth +
946 fs->max_bounds.width * w->text.lineNumCols;
948 w->text.columns = (width - marginWidth*2 - lineNumAreaWidth) /
949 fs->max_bounds.width;
950 w->text.rows = (height - marginHeight*2) / (fs->ascent + fs->descent);
952 /* Reject widths and heights less than a character, which the text
953 display can't tolerate. This is not strictly legal, but I've seen
954 it done in other widgets and it seems to do no serious harm. NEdit
955 prevents panes from getting smaller than one line, but sometimes
956 splitting windows on Linux 2.0 systems (same Motif, why the change in
957 behavior?), causes one or two resize calls with < 1 line of height.
958 Fixing it here is 100x easier than re-designing textDisp.c */
959 if (w->text.columns < 1) {
960 w->text.columns = 1;
961 w->core.width = width = fs->max_bounds.width + marginWidth*2 +
962 lineNumAreaWidth;
964 if (w->text.rows < 1) {
965 w->text.rows = 1;
966 w->core.height = height = fs->ascent + fs->descent + marginHeight*2;
969 /* Resize the text display that the widget uses to render text */
970 TextDResize(w->text.textD, width - marginWidth*2 - lineNumAreaWidth,
971 height - marginHeight*2);
973 /* if the window became shorter or narrower, there may be text left
974 in the bottom or right margin area, which must be cleaned up */
975 if (XtIsRealized((Widget)w)) {
976 XClearArea(XtDisplay(w), XtWindow(w), 0, height-marginHeight,
977 width, marginHeight, False);
978 XClearArea(XtDisplay(w), XtWindow(w),width-marginWidth,
979 0, marginWidth, height, False);
984 ** Widget redisplay method
986 static void redisplay(TextWidget w, XEvent *event, Region region)
988 XExposeEvent *e = &event->xexpose;
990 TextDRedisplayRect(w->text.textD, e->x, e->y, e->width, e->height);
993 static Bool findGraphicsExposeOrNoExposeEvent(Display *theDisplay, XEvent *event, XPointer arg)
995 if ((theDisplay == event->xany.display) &&
996 (event->type == GraphicsExpose || event->type == NoExpose) &&
997 ((Widget)arg == XtWindowToWidget(event->xany.display, event->xany.window))) {
998 return(True);
1000 else {
1001 return(False);
1005 static void adjustRectForGraphicsExposeOrNoExposeEvent(TextWidget w, XEvent *event,
1006 Boolean *first, int *left, int *top, int *width, int *height)
1008 Boolean removeQueueEntry = False;
1010 if (event->type == GraphicsExpose) {
1011 XGraphicsExposeEvent *e = &event->xgraphicsexpose;
1012 int x = e->x, y = e->y;
1014 TextDImposeGraphicsExposeTranslation(w->text.textD, &x, &y);
1015 if (*first) {
1016 *left = x;
1017 *top = y;
1018 *width = e->width;
1019 *height = e->height;
1021 *first = False;
1023 else {
1024 int prev_left = *left;
1025 int prev_top = *top;
1027 *left = min(*left, x);
1028 *top = min(*top, y);
1029 *width = max(prev_left + *width, x + e->width) - *left;
1030 *height = max(prev_top + *height, y + e->height) - *top;
1032 if (e->count == 0) {
1033 removeQueueEntry = True;
1036 else if (event->type == NoExpose) {
1037 removeQueueEntry = True;
1039 if (removeQueueEntry) {
1040 TextDPopGraphicExposeQueueEntry(w->text.textD);
1044 static void redisplayGE(TextWidget w, XtPointer client_data,
1045 XEvent *event, Boolean *continue_to_dispatch_return)
1047 if (event->type == GraphicsExpose || event->type == NoExpose) {
1048 HandleAllPendingGraphicsExposeNoExposeEvents(w, event);
1052 void HandleAllPendingGraphicsExposeNoExposeEvents(TextWidget w, XEvent *event)
1054 XEvent foundEvent;
1055 int left;
1056 int top;
1057 int width;
1058 int height;
1059 Boolean invalidRect = True;
1061 if (event) {
1062 adjustRectForGraphicsExposeOrNoExposeEvent(w, event, &invalidRect, &left, &top, &width, &height);
1064 while (XCheckIfEvent(XtDisplay(w), &foundEvent, findGraphicsExposeOrNoExposeEvent, (XPointer)w)) {
1065 adjustRectForGraphicsExposeOrNoExposeEvent(w, &foundEvent, &invalidRect, &left, &top, &width, &height);
1067 if (!invalidRect) {
1068 TextDRedisplayRect(w->text.textD, left, top, width, height);
1073 ** Widget setValues method
1075 static Boolean setValues(TextWidget current, TextWidget request,
1076 TextWidget new)
1078 Boolean redraw = False, reconfigure = False;
1080 if (new->text.overstrike != current->text.overstrike) {
1081 if (current->text.textD->cursorStyle == BLOCK_CURSOR)
1082 TextDSetCursorStyle(current->text.textD,
1083 current->text.heavyCursor ? HEAVY_CURSOR : NORMAL_CURSOR);
1084 else if (current->text.textD->cursorStyle == NORMAL_CURSOR ||
1085 current->text.textD->cursorStyle == HEAVY_CURSOR)
1086 TextDSetCursorStyle(current->text.textD, BLOCK_CURSOR);
1089 if (new->text.fontStruct != current->text.fontStruct) {
1090 if (new->text.lineNumCols != 0)
1091 reconfigure = True;
1092 TextDSetFont(current->text.textD, new->text.fontStruct);
1095 if (new->text.wrapMargin != current->text.wrapMargin ||
1096 new->text.continuousWrap != current->text.continuousWrap)
1097 TextDSetWrapMode(current->text.textD, new->text.continuousWrap,
1098 new->text.wrapMargin);
1100 /* When delimiters are changed, copy the memory, so that the caller
1101 doesn't have to manage it, and add mandatory delimiters blank,
1102 tab, and newline to the list */
1103 if (new->text.delimiters != current->text.delimiters) {
1104 char *delimiters = XtMalloc(strlen(new->text.delimiters) + 4);
1105 XtFree(current->text.delimiters);
1106 sprintf(delimiters, "%s%s", " \t\n", new->text.delimiters);
1107 new->text.delimiters = delimiters;
1110 /* Setting the lineNumCols resource tells the text widget to hide or
1111 show, or change the number of columns of the line number display,
1112 which requires re-organizing the x coordinates of both the line
1113 number display and the main text display */
1114 if (new->text.lineNumCols != current->text.lineNumCols || reconfigure)
1116 int marginWidth = new->text.marginWidth;
1117 int charWidth = new->text.fontStruct->max_bounds.width;
1118 int lineNumCols = new->text.lineNumCols;
1119 if (lineNumCols == 0)
1121 TextDSetLineNumberArea(new->text.textD, 0, 0, marginWidth);
1122 new->text.columns = (new->core.width - marginWidth*2) / charWidth;
1123 } else
1125 TextDSetLineNumberArea(new->text.textD, marginWidth,
1126 charWidth * lineNumCols,
1127 2*marginWidth + charWidth * lineNumCols);
1128 new->text.columns = (new->core.width - marginWidth*3 - charWidth
1129 * lineNumCols) / charWidth;
1133 if (new->text.backlightCharTypes != current->text.backlightCharTypes)
1135 TextDSetupBGClasses((Widget)new, new->text.backlightCharTypes,
1136 &new->text.textD->bgClassPixel, &new->text.textD->bgClass,
1137 new->text.textD->bgPixel);
1138 redraw = True;
1141 return redraw;
1145 ** Widget realize method
1147 static void realize(Widget w, XtValueMask *valueMask,
1148 XSetWindowAttributes *attributes)
1150 /* Set bit gravity window attribute. This saves a full blank and redraw
1151 on window resizing */
1152 *valueMask |= CWBitGravity;
1153 attributes->bit_gravity = NorthWestGravity;
1155 /* Continue with realize method from superclass */
1156 (xmPrimitiveClassRec.core_class.realize)(w, valueMask, attributes);
1160 ** Widget query geometry method ... experimental, not fully tested, does
1161 ** nothing in NEdit since paned window ignores children's suggested geometry
1163 static XtGeometryResult queryGeometry(Widget w, XtWidgetGeometry *proposed,
1164 XtWidgetGeometry *answer)
1166 int curHeight = ((TextWidget)w)->core.height;
1167 int curWidth = ((TextWidget)w)->core.width;
1168 XFontStruct *fs = ((TextWidget)w)->text.textD->fontStruct;
1169 int fontWidth = fs->max_bounds.width;
1170 int fontHeight = fs->ascent + fs->descent;
1171 int marginHeight = ((TextWidget)w)->text.marginHeight;
1172 int propWidth = (proposed->request_mode & CWWidth) ? proposed->width : 0;
1173 int propHeight = (proposed->request_mode & CWHeight) ? proposed->height : 0;
1175 /* suggest a height that is an exact multiple of the line height
1176 and at least one line high and 10 chars wide */
1177 answer->request_mode = CWHeight | CWWidth;
1178 answer->width = max(fontWidth * 10, propWidth);
1179 answer->height = max(1, ((propHeight - 2*marginHeight) / fontHeight)) *
1180 fontHeight + 2*marginHeight;
1181 /* printf("propWidth %d, propHeight %d, ansWidth %d, ansHeight %d\n",
1182 propWidth, propHeight, answer->width, answer->height); */
1183 if (propWidth == answer->width && propHeight == answer->height)
1184 return XtGeometryYes;
1185 else if (answer->width == curWidth && answer->height == curHeight)
1186 return XtGeometryNo;
1187 else
1188 return XtGeometryAlmost;
1192 ** Set the text buffer which this widget will display and interact with.
1193 ** The currently attached buffer is automatically freed, ONLY if it has
1194 ** no additional modify procs attached (as it would if it were being
1195 ** displayed by another text widget).
1197 void TextSetBuffer(Widget w, textBuffer *buffer)
1199 textBuffer *oldBuf = ((TextWidget)w)->text.textD->buffer;
1201 StopHandlingXSelections(w);
1202 TextDSetBuffer(((TextWidget)w)->text.textD, buffer);
1203 if (oldBuf->nModifyProcs == 0)
1204 BufFree(oldBuf);
1208 ** Get the buffer associated with this text widget. Note that attaching
1209 ** additional modify callbacks to the buffer will prevent it from being
1210 ** automatically freed when the widget is destroyed.
1212 textBuffer *TextGetBuffer(Widget w)
1214 return ((TextWidget)w)->text.textD->buffer;
1218 ** Translate a line number and column into a position
1220 int TextLineAndColToPos(Widget w, int lineNum, int column)
1222 return TextDLineAndColToPos(((TextWidget)w)->text.textD, lineNum, column );
1226 ** Translate a position into a line number (if the position is visible,
1227 ** if it's not, return False
1229 int TextPosToLineAndCol(Widget w, int pos, int *lineNum, int *column)
1231 return TextDPosToLineAndCol(((TextWidget)w)->text.textD, pos, lineNum,
1232 column);
1236 ** Translate a buffer text position to the XY location where the center
1237 ** of the cursor would be positioned to point to that character. Returns
1238 ** False if the position is not displayed because it is VERTICALLY out
1239 ** of view. If the position is horizontally out of view, returns the
1240 ** x coordinate where the position would be if it were visible.
1242 int TextPosToXY(Widget w, int pos, int *x, int *y)
1244 return TextDPositionToXY(((TextWidget)w)->text.textD, pos, x, y);
1248 ** Return the cursor position
1250 int TextGetCursorPos(Widget w)
1252 return TextDGetInsertPosition(((TextWidget)w)->text.textD);
1256 ** Set the cursor position
1258 void TextSetCursorPos(Widget w, int pos)
1260 TextDSetInsertPosition(((TextWidget)w)->text.textD, pos);
1261 checkAutoShowInsertPos(w);
1262 callCursorMovementCBs(w, NULL);
1267 ** Return the horizontal and vertical scroll positions of the widget
1269 void TextGetScroll(Widget w, int *topLineNum, int *horizOffset)
1271 TextDGetScroll(((TextWidget)w)->text.textD, topLineNum, horizOffset);
1275 ** Set the horizontal and vertical scroll positions of the widget
1277 void TextSetScroll(Widget w, int topLineNum, int horizOffset)
1279 TextDSetScroll(((TextWidget)w)->text.textD, topLineNum, horizOffset);
1282 int TextGetMinFontWidth(Widget w, Boolean considerStyles)
1284 return(TextDMinFontWidth(((TextWidget)w)->text.textD, considerStyles));
1287 int TextGetMaxFontWidth(Widget w, Boolean considerStyles)
1289 return(TextDMaxFontWidth(((TextWidget)w)->text.textD, considerStyles));
1293 ** Set this widget to be the owner of selections made in it's attached
1294 ** buffer (text buffers may be shared among several text widgets).
1296 void TextHandleXSelections(Widget w)
1298 HandleXSelections(w);
1301 void TextStopHandlingSelections(Widget w)
1303 StopHandlingXSelections(w);
1306 void TextPasteClipboard(Widget w, Time time)
1308 cancelDrag(w);
1309 if (checkReadOnly(w))
1310 return;
1311 TakeMotifDestination(w, time);
1312 InsertClipboard(w, time, False);
1313 callCursorMovementCBs(w, NULL);
1316 void TextColPasteClipboard(Widget w, Time time)
1318 cancelDrag(w);
1319 if (checkReadOnly(w))
1320 return;
1321 TakeMotifDestination(w, time);
1322 InsertClipboard(w, time, True);
1323 callCursorMovementCBs(w, NULL);
1326 void TextCopyClipboard(Widget w, Time time)
1328 cancelDrag(w);
1329 if (!((TextWidget)w)->text.textD->buffer->primary.selected) {
1330 XBell(XtDisplay(w), 0);
1331 return;
1333 CopyToClipboard(w, time);
1336 void TextCutClipboard(Widget w, Time time)
1338 textDisp *textD = ((TextWidget)w)->text.textD;
1340 cancelDrag(w);
1341 if (checkReadOnly(w))
1342 return;
1343 if (!textD->buffer->primary.selected) {
1344 XBell(XtDisplay(w), 0);
1345 return;
1347 TakeMotifDestination(w, time);
1348 CopyToClipboard (w, time);
1349 BufRemoveSelected(textD->buffer);
1350 TextDSetInsertPosition(textD, textD->buffer->cursorPosHint);
1351 checkAutoShowInsertPos(w);
1354 int TextFirstVisibleLine(Widget w)
1356 return(((TextWidget)w)->text.textD->topLineNum);
1359 int TextNumVisibleLines(Widget w)
1361 return(((TextWidget)w)->text.textD->nVisibleLines);
1364 int TextVisibleWidth(Widget w)
1366 return(((TextWidget)w)->text.textD->width);
1369 int TextFirstVisiblePos(Widget w)
1371 return ((TextWidget)w)->text.textD->firstChar;
1374 int TextLastVisiblePos(Widget w)
1376 return ((TextWidget)w)->text.textD->lastChar;
1380 ** Insert text "chars" at the cursor position, respecting pending delete
1381 ** selections, overstrike, and handling cursor repositioning as if the text
1382 ** had been typed. If autoWrap is on wraps the text to fit within the wrap
1383 ** margin, auto-indenting where the line was wrapped (but nowhere else).
1384 ** "allowPendingDelete" controls whether primary selections in the widget are
1385 ** treated as pending delete selections (True), or ignored (False). "event"
1386 ** is optional and is just passed on to the cursor movement callbacks.
1388 void TextInsertAtCursor(Widget w, char *chars, XEvent *event,
1389 int allowPendingDelete, int allowWrap)
1391 int wrapMargin, colNum, lineStartPos, cursorPos;
1392 char *c, *lineStartText, *wrappedText;
1393 TextWidget tw = (TextWidget)w;
1394 textDisp *textD = tw->text.textD;
1395 textBuffer *buf = textD->buffer;
1396 int fontWidth = textD->fontStruct->max_bounds.width;
1397 int replaceSel, singleLine, breakAt = 0;
1399 /* Don't wrap if auto-wrap is off or suppressed, or it's just a newline */
1400 if (!allowWrap || !tw->text.autoWrap ||
1401 (chars[0] == '\n' && chars[1] == '\0')) {
1402 simpleInsertAtCursor(w, chars, event, allowPendingDelete);
1403 return;
1406 /* If this is going to be a pending delete operation, the real insert
1407 position is the start of the selection. This will make rectangular
1408 selections wrap strangely, but this routine should rarely be used for
1409 them, and even more rarely when they need to be wrapped. */
1410 replaceSel = allowPendingDelete && pendingSelection(w);
1411 cursorPos = replaceSel ? buf->primary.start : TextDGetInsertPosition(textD);
1413 /* If the text is only one line and doesn't need to be wrapped, just insert
1414 it and be done (for efficiency only, this routine is called for each
1415 character typed). (Of course, it may not be significantly more efficient
1416 than the more general code below it, so it may be a waste of time!) */
1417 wrapMargin = tw->text.wrapMargin != 0 ? tw->text.wrapMargin :
1418 textD->width / fontWidth;
1419 lineStartPos = BufStartOfLine(buf, cursorPos);
1420 colNum = BufCountDispChars(buf, lineStartPos, cursorPos);
1421 for (c=chars; *c!='\0' && *c!='\n'; c++)
1422 colNum += BufCharWidth(*c, colNum, buf->tabDist, buf->nullSubsChar);
1423 singleLine = *c == '\0';
1424 if (colNum < wrapMargin && singleLine) {
1425 simpleInsertAtCursor(w, chars, event, True);
1426 return;
1429 /* Wrap the text */
1430 lineStartText = BufGetRange(buf, lineStartPos, cursorPos);
1431 wrappedText = wrapText(tw, lineStartText, chars, lineStartPos, wrapMargin,
1432 replaceSel ? NULL : &breakAt);
1433 XtFree(lineStartText);
1435 /* Insert the text. Where possible, use TextDInsert which is optimized
1436 for less redraw. */
1437 if (replaceSel) {
1438 BufReplaceSelected(buf, wrappedText);
1439 TextDSetInsertPosition(textD, buf->cursorPosHint);
1440 } else if (tw->text.overstrike) {
1441 if (breakAt == 0 && singleLine)
1442 TextDOverstrike(textD, wrappedText);
1443 else {
1444 BufReplace(buf, cursorPos-breakAt, cursorPos, wrappedText);
1445 TextDSetInsertPosition(textD, buf->cursorPosHint);
1447 } else {
1448 if (breakAt == 0) {
1449 TextDInsert(textD, wrappedText);
1450 } else {
1451 BufReplace(buf, cursorPos-breakAt, cursorPos, wrappedText);
1452 TextDSetInsertPosition(textD, buf->cursorPosHint);
1455 XtFree(wrappedText);
1456 checkAutoShowInsertPos(w);
1457 callCursorMovementCBs(w, event);
1461 ** Fetch text from the widget's buffer, adding wrapping newlines to emulate
1462 ** effect acheived by wrapping in the text display in continuous wrap mode.
1464 char *TextGetWrapped(Widget w, int startPos, int endPos, int *outLen)
1466 textDisp *textD = ((TextWidget)w)->text.textD;
1467 textBuffer *buf = textD->buffer;
1468 textBuffer *outBuf;
1469 int fromPos, toPos, outPos;
1470 char c, *outString;
1472 if (!((TextWidget)w)->text.continuousWrap || startPos == endPos) {
1473 *outLen = endPos - startPos;
1474 return BufGetRange(buf, startPos, endPos);
1477 /* Create a text buffer with a good estimate of the size that adding
1478 newlines will expand it to. Since it's a text buffer, if we guess
1479 wrong, it will fail softly, and simply expand the size */
1480 outBuf = BufCreatePreallocated((endPos-startPos) + (endPos-startPos)/5);
1481 outPos = 0;
1483 /* Go (displayed) line by line through the buffer, adding newlines where
1484 the text is wrapped at some character other than an existing newline */
1485 fromPos = startPos;
1486 toPos = TextDCountForwardNLines(textD, startPos, 1, False);
1487 while (toPos < endPos) {
1488 BufCopyFromBuf(buf, outBuf, fromPos, toPos, outPos);
1489 outPos += toPos - fromPos;
1490 c = BufGetCharacter(outBuf, outPos-1);
1491 if (c == ' ' || c == '\t')
1492 BufReplace(outBuf, outPos-1, outPos, "\n");
1493 else if (c != '\n') {
1494 BufInsert(outBuf, outPos, "\n");
1495 outPos++;
1497 fromPos = toPos;
1498 toPos = TextDCountForwardNLines(textD, fromPos, 1, True);
1500 BufCopyFromBuf(buf, outBuf, fromPos, endPos, outPos);
1502 /* return the contents of the output buffer as a string */
1503 outString = BufGetAll(outBuf);
1504 *outLen = outBuf->length;
1505 BufFree(outBuf);
1506 return outString;
1510 ** Return the (statically allocated) action table for menu item actions.
1512 ** Warning: This routine can only be used before the first text widget is
1513 ** created! After that, apparently, Xt takes over the table and overwrites
1514 ** it with its own version. XtGetActionList is preferable, but is not
1515 ** available before X11R5.
1517 XtActionsRec *TextGetActions(int *nActions)
1519 *nActions = XtNumber(actionsList);
1520 return actionsList;
1523 static void grabFocusAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
1525 XButtonEvent *e = &event->xbutton;
1526 TextWidget tw = (TextWidget)w;
1527 textDisp *textD = tw->text.textD;
1528 Time lastBtnDown = tw->text.lastBtnDown;
1529 int row, column;
1531 /* Indicate state for future events, PRIMARY_CLICKED indicates that
1532 the proper initialization has been done for primary dragging and/or
1533 multi-clicking. Also record the timestamp for multi-click processing */
1534 tw->text.dragState = PRIMARY_CLICKED;
1535 tw->text.lastBtnDown = e->time;
1537 /* Become owner of the MOTIF_DESTINATION selection, making this widget
1538 the designated recipient of secondary quick actions in Motif XmText
1539 widgets and in other NEdit text widgets */
1540 TakeMotifDestination(w, e->time);
1542 /* Check for possible multi-click sequence in progress */
1543 if (tw->text.multiClickState != NO_CLICKS) {
1544 if (e->time < lastBtnDown + XtGetMultiClickTime(XtDisplay(w))) {
1545 if (tw->text.multiClickState == ONE_CLICK) {
1546 selectWord(w, e->x);
1547 callCursorMovementCBs(w, event);
1548 return;
1549 } else if (tw->text.multiClickState == TWO_CLICKS) {
1550 selectLine(w);
1551 callCursorMovementCBs(w, event);
1552 return;
1553 } else if (tw->text.multiClickState == THREE_CLICKS) {
1554 BufSelect(textD->buffer, 0, textD->buffer->length);
1555 return;
1556 } else if (tw->text.multiClickState > THREE_CLICKS)
1557 tw->text.multiClickState = NO_CLICKS;
1558 } else
1559 tw->text.multiClickState = NO_CLICKS;
1562 /* Clear any existing selections */
1563 BufUnselect(textD->buffer);
1565 /* Move the cursor to the pointer location */
1566 moveDestinationAP(w, event, args, nArgs);
1568 /* Record the site of the initial button press and the initial character
1569 position so subsequent motion events and clicking can decide when and
1570 where to begin a primary selection */
1571 tw->text.btnDownX = e->x;
1572 tw->text.btnDownY = e->y;
1573 tw->text.anchor = TextDGetInsertPosition(textD);
1574 TextDXYToUnconstrainedPosition(textD, e->x, e->y, &row, &column);
1575 column = TextDOffsetWrappedColumn(textD, row, column);
1576 tw->text.rectAnchor = column;
1579 static void moveDestinationAP(Widget w, XEvent *event, String *args,
1580 Cardinal *nArgs)
1582 XButtonEvent *e = &event->xbutton;
1583 textDisp *textD = ((TextWidget)w)->text.textD;
1585 /* Get input focus */
1586 XmProcessTraversal(w, XmTRAVERSE_CURRENT);
1588 /* Move the cursor */
1589 TextDSetInsertPosition(textD, TextDXYToPosition(textD, e->x, e->y));
1590 checkAutoShowInsertPos(w);
1591 callCursorMovementCBs(w, event);
1594 static void extendAdjustAP(Widget w, XEvent *event, String *args,
1595 Cardinal *nArgs)
1597 TextWidget tw = (TextWidget)w;
1598 XMotionEvent *e = &event->xmotion;
1599 int dragState = tw->text.dragState;
1600 int rectDrag = hasKey("rect", args, nArgs);
1602 /* Make sure the proper initialization was done on mouse down */
1603 if (dragState != PRIMARY_DRAG && dragState != PRIMARY_CLICKED &&
1604 dragState != PRIMARY_RECT_DRAG)
1605 return;
1607 /* If the selection hasn't begun, decide whether the mouse has moved
1608 far enough from the initial mouse down to be considered a drag */
1609 if (tw->text.dragState == PRIMARY_CLICKED) {
1610 if (abs(e->x - tw->text.btnDownX) > SELECT_THRESHOLD ||
1611 abs(e->y - tw->text.btnDownY) > SELECT_THRESHOLD)
1612 tw->text.dragState = rectDrag ? PRIMARY_RECT_DRAG : PRIMARY_DRAG;
1613 else
1614 return;
1617 /* If "rect" argument has appeared or disappeared, keep dragState up
1618 to date about which type of drag this is */
1619 tw->text.dragState = rectDrag ? PRIMARY_RECT_DRAG : PRIMARY_DRAG;
1621 /* Record the new position for the autoscrolling timer routine, and
1622 engage or disengage the timer if the mouse is in/out of the window */
1623 checkAutoScroll(tw, e->x, e->y);
1625 /* Adjust the selection and move the cursor */
1626 adjustSelection(tw, e->x, e->y);
1629 static void extendStartAP(Widget w, XEvent *event, String *args,
1630 Cardinal *nArgs)
1632 XMotionEvent *e = &event->xmotion;
1633 textDisp *textD = ((TextWidget)w)->text.textD;
1634 textBuffer *buf = textD->buffer;
1635 selection *sel = &buf->primary;
1636 int anchor, rectAnchor, anchorLineStart, newPos, row, column;
1638 /* Find the new anchor point for the rest of this drag operation */
1639 newPos = TextDXYToPosition(textD, e->x, e->y);
1640 TextDXYToUnconstrainedPosition(textD, e->x, e->y, &row, &column);
1641 column = TextDOffsetWrappedColumn(textD, row, column);
1642 if (sel->selected) {
1643 if (sel->rectangular) {
1644 rectAnchor = column < (sel->rectEnd + sel->rectStart) / 2 ?
1645 sel->rectEnd : sel->rectStart;
1646 anchorLineStart = BufStartOfLine(buf, newPos <
1647 (sel->end + sel->start) / 2 ? sel->end : sel->start);
1648 anchor = BufCountForwardDispChars(buf, anchorLineStart, rectAnchor);
1649 } else {
1650 if (abs(newPos - sel->start) < abs(newPos - sel->end))
1651 anchor = sel->end;
1652 else
1653 anchor = sel->start;
1654 anchorLineStart = BufStartOfLine(buf, anchor);
1655 rectAnchor = BufCountDispChars(buf, anchorLineStart, anchor);
1657 } else {
1658 anchor = TextDGetInsertPosition(textD);
1659 anchorLineStart = BufStartOfLine(buf, anchor);
1660 rectAnchor = BufCountDispChars(buf, anchorLineStart, anchor);
1662 ((TextWidget)w)->text.anchor = anchor;
1663 ((TextWidget)w)->text.rectAnchor = rectAnchor;
1665 /* Make the new selection */
1666 if (hasKey("rect", args, nArgs))
1667 BufRectSelect(buf, BufStartOfLine(buf, min(anchor, newPos)),
1668 BufEndOfLine(buf, max(anchor, newPos)),
1669 min(rectAnchor, column), max(rectAnchor, column));
1670 else
1671 BufSelect(buf, min(anchor, newPos), max(anchor, newPos));
1673 /* Never mind the motion threshold, go right to dragging since
1674 extend-start is unambiguously the start of a selection */
1675 ((TextWidget)w)->text.dragState = PRIMARY_DRAG;
1677 /* Don't do by-word or by-line adjustment, just by character */
1678 ((TextWidget)w)->text.multiClickState = NO_CLICKS;
1680 /* Move the cursor */
1681 TextDSetInsertPosition(textD, newPos);
1682 callCursorMovementCBs(w, event);
1685 static void extendEndAP(Widget w, XEvent *event, String *args,
1686 Cardinal *nArgs)
1688 XButtonEvent *e = &event->xbutton;
1689 TextWidget tw = (TextWidget)w;
1691 if (tw->text.dragState == PRIMARY_CLICKED &&
1692 tw->text.lastBtnDown <= e->time + XtGetMultiClickTime(XtDisplay(w)))
1693 tw->text.multiClickState++;
1694 endDrag(w);
1697 static void processCancelAP(Widget w, XEvent *event, String *args,
1698 Cardinal *nArgs)
1700 int dragState = ((TextWidget)w)->text.dragState;
1701 textBuffer *buf = ((TextWidget)w)->text.textD->buffer;
1702 textDisp *textD = ((TextWidget)w)->text.textD;
1704 /* If there's a calltip displayed, kill it. */
1705 TextDKillCalltip(textD, 0);
1707 if (dragState == PRIMARY_DRAG || dragState == PRIMARY_RECT_DRAG)
1708 BufUnselect(buf);
1709 cancelDrag(w);
1712 static void secondaryStartAP(Widget w, XEvent *event, String *args,
1713 Cardinal *nArgs)
1715 XMotionEvent *e = &event->xmotion;
1716 textDisp *textD = ((TextWidget)w)->text.textD;
1717 textBuffer *buf = textD->buffer;
1718 selection *sel = &buf->secondary;
1719 int anchor, pos, row, column;
1721 /* Find the new anchor point and make the new selection */
1722 pos = TextDXYToPosition(textD, e->x, e->y);
1723 if (sel->selected) {
1724 if (abs(pos - sel->start) < abs(pos - sel->end))
1725 anchor = sel->end;
1726 else
1727 anchor = sel->start;
1728 BufSecondarySelect(buf, anchor, pos);
1729 } else
1730 anchor = pos;
1732 /* Record the site of the initial button press and the initial character
1733 position so subsequent motion events can decide when to begin a
1734 selection, (and where the selection began) */
1735 ((TextWidget)w)->text.btnDownX = e->x;
1736 ((TextWidget)w)->text.btnDownY = e->y;
1737 ((TextWidget)w)->text.anchor = pos;
1738 TextDXYToUnconstrainedPosition(textD, e->x, e->y, &row, &column);
1739 column = TextDOffsetWrappedColumn(textD, row, column);
1740 ((TextWidget)w)->text.rectAnchor = column;
1741 ((TextWidget)w)->text.dragState = SECONDARY_CLICKED;
1744 static void secondaryOrDragStartAP(Widget w, XEvent *event, String *args,
1745 Cardinal *nArgs)
1747 XMotionEvent *e = &event->xmotion;
1748 textDisp *textD = ((TextWidget)w)->text.textD;
1749 textBuffer *buf = textD->buffer;
1751 /* If the click was outside of the primary selection, this is not
1752 a drag, start a secondary selection */
1753 if (!buf->primary.selected || !TextDInSelection(textD, e->x, e->y)) {
1754 secondaryStartAP(w, event, args, nArgs);
1755 return;
1758 if (checkReadOnly(w))
1759 return;
1761 /* Record the site of the initial button press and the initial character
1762 position so subsequent motion events can decide when to begin a
1763 drag, and where to drag to */
1764 ((TextWidget)w)->text.btnDownX = e->x;
1765 ((TextWidget)w)->text.btnDownY = e->y;
1766 ((TextWidget)w)->text.dragState = CLICKED_IN_SELECTION;
1769 static void secondaryAdjustAP(Widget w, XEvent *event, String *args,
1770 Cardinal *nArgs)
1772 TextWidget tw = (TextWidget)w;
1773 XMotionEvent *e = &event->xmotion;
1774 int dragState = tw->text.dragState;
1775 int rectDrag = hasKey("rect", args, nArgs);
1777 /* Make sure the proper initialization was done on mouse down */
1778 if (dragState != SECONDARY_DRAG && dragState != SECONDARY_RECT_DRAG &&
1779 dragState != SECONDARY_CLICKED)
1780 return;
1782 /* If the selection hasn't begun, decide whether the mouse has moved
1783 far enough from the initial mouse down to be considered a drag */
1784 if (tw->text.dragState == SECONDARY_CLICKED) {
1785 if (abs(e->x - tw->text.btnDownX) > SELECT_THRESHOLD ||
1786 abs(e->y - tw->text.btnDownY) > SELECT_THRESHOLD)
1787 tw->text.dragState = rectDrag ? SECONDARY_RECT_DRAG: SECONDARY_DRAG;
1788 else
1789 return;
1792 /* If "rect" argument has appeared or disappeared, keep dragState up
1793 to date about which type of drag this is */
1794 tw->text.dragState = rectDrag ? SECONDARY_RECT_DRAG : SECONDARY_DRAG;
1796 /* Record the new position for the autoscrolling timer routine, and
1797 engage or disengage the timer if the mouse is in/out of the window */
1798 checkAutoScroll(tw, e->x, e->y);
1800 /* Adjust the selection */
1801 adjustSecondarySelection(tw, e->x, e->y);
1804 static void secondaryOrDragAdjustAP(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;
1811 /* Only dragging of blocks of text is handled in this action proc.
1812 Otherwise, defer to secondaryAdjust to handle the rest */
1813 if (dragState != CLICKED_IN_SELECTION && dragState != PRIMARY_BLOCK_DRAG) {
1814 secondaryAdjustAP(w, event, args, nArgs);
1815 return;
1818 /* Decide whether the mouse has moved far enough from the
1819 initial mouse down to be considered a drag */
1820 if (tw->text.dragState == CLICKED_IN_SELECTION) {
1821 if (abs(e->x - tw->text.btnDownX) > SELECT_THRESHOLD ||
1822 abs(e->y - tw->text.btnDownY) > SELECT_THRESHOLD)
1823 BeginBlockDrag(tw);
1824 else
1825 return;
1828 /* Record the new position for the autoscrolling timer routine, and
1829 engage or disengage the timer if the mouse is in/out of the window */
1830 checkAutoScroll(tw, e->x, e->y);
1832 /* Adjust the selection */
1833 BlockDragSelection(tw, e->x, e->y, hasKey("overlay", args, nArgs) ?
1834 (hasKey("copy", args, nArgs) ? DRAG_OVERLAY_COPY : DRAG_OVERLAY_MOVE) :
1835 (hasKey("copy", args, nArgs) ? DRAG_COPY : DRAG_MOVE));
1838 static void copyToAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
1840 XButtonEvent *e = &event->xbutton;
1841 TextWidget tw = (TextWidget)w;
1842 textDisp *textD = tw->text.textD;
1843 int dragState = tw->text.dragState;
1844 textBuffer *buf = textD->buffer;
1845 selection *secondary = &buf->secondary, *primary = &buf->primary;
1846 int rectangular = secondary->rectangular;
1847 char *textToCopy;
1848 int insertPos, lineStart, column;
1850 endDrag(w);
1851 if (!((dragState == SECONDARY_DRAG && secondary->selected) ||
1852 (dragState == SECONDARY_RECT_DRAG && secondary->selected) ||
1853 dragState == SECONDARY_CLICKED || dragState == NOT_CLICKED))
1854 return;
1855 if (!(secondary->selected && !((TextWidget)w)->text.motifDestOwner)) {
1856 if (checkReadOnly(w)) {
1857 BufSecondaryUnselect(buf);
1858 return;
1861 if (secondary->selected) {
1862 if (tw->text.motifDestOwner) {
1863 TextDBlankCursor(textD);
1864 textToCopy = BufGetSecSelectText(buf);
1865 if (primary->selected && rectangular) {
1866 insertPos = TextDGetInsertPosition(textD);
1867 BufReplaceSelected(buf, textToCopy);
1868 TextDSetInsertPosition(textD, buf->cursorPosHint);
1869 } else if (rectangular) {
1870 insertPos = TextDGetInsertPosition(textD);
1871 lineStart = BufStartOfLine(buf, insertPos);
1872 column = BufCountDispChars(buf, lineStart, insertPos);
1873 BufInsertCol(buf, column, lineStart, textToCopy, NULL, NULL);
1874 TextDSetInsertPosition(textD, buf->cursorPosHint);
1875 } else
1876 TextInsertAtCursor(w, textToCopy, event, True,
1877 tw->text.autoWrapPastedText);
1878 XtFree(textToCopy);
1879 BufSecondaryUnselect(buf);
1880 TextDUnblankCursor(textD);
1881 } else
1882 SendSecondarySelection(w, e->time, False);
1883 } else if (primary->selected) {
1884 textToCopy = BufGetSelectionText(buf);
1885 TextDSetInsertPosition(textD, TextDXYToPosition(textD, e->x, e->y));
1886 TextInsertAtCursor(w, textToCopy, event, False,
1887 tw->text.autoWrapPastedText);
1888 XtFree(textToCopy);
1889 } else {
1890 TextDSetInsertPosition(textD, TextDXYToPosition(textD, e->x, e->y));
1891 InsertPrimarySelection(w, e->time, False);
1895 static void copyToOrEndDragAP(Widget w, XEvent *event, String *args,
1896 Cardinal *nArgs)
1898 int dragState = ((TextWidget)w)->text.dragState;
1900 if (dragState != PRIMARY_BLOCK_DRAG) {
1901 copyToAP(w, event, args, nArgs);
1902 return;
1905 FinishBlockDrag((TextWidget)w);
1908 static void moveToAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
1910 XButtonEvent *e = &event->xbutton;
1911 textDisp *textD = ((TextWidget)w)->text.textD;
1912 int dragState = ((TextWidget)w)->text.dragState;
1913 textBuffer *buf = textD->buffer;
1914 selection *secondary = &buf->secondary, *primary = &buf->primary;
1915 int insertPos, rectangular = secondary->rectangular;
1916 int column, lineStart;
1917 char *textToCopy;
1919 endDrag(w);
1920 if (!((dragState == SECONDARY_DRAG && secondary->selected) ||
1921 (dragState == SECONDARY_RECT_DRAG && secondary->selected) ||
1922 dragState == SECONDARY_CLICKED || dragState == NOT_CLICKED))
1923 return;
1924 if (checkReadOnly(w)) {
1925 BufSecondaryUnselect(buf);
1926 return;
1929 if (secondary->selected) {
1930 if (((TextWidget)w)->text.motifDestOwner) {
1931 textToCopy = BufGetSecSelectText(buf);
1932 if (primary->selected && rectangular) {
1933 insertPos = TextDGetInsertPosition(textD);
1934 BufReplaceSelected(buf, textToCopy);
1935 TextDSetInsertPosition(textD, buf->cursorPosHint);
1936 } else if (rectangular) {
1937 insertPos = TextDGetInsertPosition(textD);
1938 lineStart = BufStartOfLine(buf, insertPos);
1939 column = BufCountDispChars(buf, lineStart, insertPos);
1940 BufInsertCol(buf, column, lineStart, textToCopy, NULL, NULL);
1941 TextDSetInsertPosition(textD, buf->cursorPosHint);
1942 } else
1943 TextInsertAtCursor(w, textToCopy, event, True,
1944 ((TextWidget)w)->text.autoWrapPastedText);
1945 XtFree(textToCopy);
1946 BufRemoveSecSelect(buf);
1947 BufSecondaryUnselect(buf);
1948 } else
1949 SendSecondarySelection(w, e->time, True);
1950 } else if (primary->selected) {
1951 textToCopy = BufGetRange(buf, primary->start, primary->end);
1952 TextDSetInsertPosition(textD, TextDXYToPosition(textD, e->x, e->y));
1953 TextInsertAtCursor(w, textToCopy, event, False,
1954 ((TextWidget)w)->text.autoWrapPastedText);
1955 XtFree(textToCopy);
1956 BufRemoveSelected(buf);
1957 BufUnselect(buf);
1958 } else {
1959 TextDSetInsertPosition(textD, TextDXYToPosition(textD, e->x, e->y));
1960 MovePrimarySelection(w, e->time, False);
1964 static void moveToOrEndDragAP(Widget w, XEvent *event, String *args,
1965 Cardinal *nArgs)
1967 int dragState = ((TextWidget)w)->text.dragState;
1969 if (dragState != PRIMARY_BLOCK_DRAG) {
1970 moveToAP(w, event, args, nArgs);
1971 return;
1974 FinishBlockDrag((TextWidget)w);
1977 static void endDragAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
1979 if (((TextWidget)w)->text.dragState == PRIMARY_BLOCK_DRAG)
1980 FinishBlockDrag((TextWidget)w);
1981 else
1982 endDrag(w);
1985 static void exchangeAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
1987 XButtonEvent *e = &event->xbutton;
1988 textDisp *textD = ((TextWidget)w)->text.textD;
1989 textBuffer *buf = textD->buffer;
1990 selection *sec = &buf->secondary, *primary = &buf->primary;
1991 char *primaryText, *secText;
1992 int newPrimaryStart, newPrimaryEnd, secWasRect;
1993 int dragState = ((TextWidget)w)->text.dragState; /* save before endDrag */
1994 int silent = hasKey("nobell", args, nArgs);
1996 endDrag(w);
1997 if (checkReadOnly(w))
1998 return;
2000 /* If there's no secondary selection here, or the primary and secondary
2001 selection overlap, just beep and return */
2002 if (!sec->selected || (primary->selected &&
2003 ((primary->start <= sec->start && primary->end > sec->start) ||
2004 (sec->start <= primary->start && sec->end > primary->start))))
2006 BufSecondaryUnselect(buf);
2007 ringIfNecessary(silent, w);
2008 /* If there's no secondary selection, but the primary selection is
2009 being dragged, we must not forget to finish the dragging.
2010 Otherwise, modifications aren't recorded. */
2011 if (dragState == PRIMARY_BLOCK_DRAG)
2012 FinishBlockDrag((TextWidget)w);
2013 return;
2016 /* if the primary selection is in another widget, use selection routines */
2017 if (!primary->selected) {
2018 ExchangeSelections(w, e->time);
2019 return;
2022 /* Both primary and secondary are in this widget, do the exchange here */
2023 primaryText = BufGetSelectionText(buf);
2024 secText = BufGetSecSelectText(buf);
2025 secWasRect = sec->rectangular;
2026 BufReplaceSecSelect(buf, primaryText);
2027 newPrimaryStart = primary->start;
2028 BufReplaceSelected(buf, secText);
2029 newPrimaryEnd = newPrimaryStart + strlen(secText);
2030 XtFree(primaryText);
2031 XtFree(secText);
2032 BufSecondaryUnselect(buf);
2033 if (secWasRect) {
2034 TextDSetInsertPosition(textD, buf->cursorPosHint);
2035 } else {
2036 BufSelect(buf, newPrimaryStart, newPrimaryEnd);
2037 TextDSetInsertPosition(textD, newPrimaryEnd);
2039 checkAutoShowInsertPos(w);
2042 static void copyPrimaryAP(Widget w, XEvent *event, String *args,
2043 Cardinal *nArgs)
2045 XKeyEvent *e = &event->xkey;
2046 TextWidget tw = (TextWidget)w;
2047 textDisp *textD = tw->text.textD;
2048 textBuffer *buf = textD->buffer;
2049 selection *primary = &buf->primary;
2050 int rectangular = hasKey("rect", args, nArgs);
2051 char *textToCopy;
2052 int insertPos, col;
2054 cancelDrag(w);
2055 if (checkReadOnly(w))
2056 return;
2057 if (primary->selected && rectangular) {
2058 textToCopy = BufGetSelectionText(buf);
2059 insertPos = TextDGetInsertPosition(textD);
2060 col = BufCountDispChars(buf, BufStartOfLine(buf, insertPos), insertPos);
2061 BufInsertCol(buf, col, insertPos, textToCopy, NULL, NULL);
2062 TextDSetInsertPosition(textD, buf->cursorPosHint);
2063 XtFree(textToCopy);
2064 checkAutoShowInsertPos(w);
2065 } else if (primary->selected) {
2066 textToCopy = BufGetSelectionText(buf);
2067 insertPos = TextDGetInsertPosition(textD);
2068 BufInsert(buf, insertPos, textToCopy);
2069 TextDSetInsertPosition(textD, insertPos + strlen(textToCopy));
2070 XtFree(textToCopy);
2071 checkAutoShowInsertPos(w);
2072 } else if (rectangular) {
2073 if (!TextDPositionToXY(textD, TextDGetInsertPosition(textD),
2074 &tw->text.btnDownX, &tw->text.btnDownY))
2075 return; /* shouldn't happen */
2076 InsertPrimarySelection(w, e->time, True);
2077 } else
2078 InsertPrimarySelection(w, e->time, False);
2081 static void cutPrimaryAP(Widget w, XEvent *event, String *args,
2082 Cardinal *nArgs)
2084 XKeyEvent *e = &event->xkey;
2085 textDisp *textD = ((TextWidget)w)->text.textD;
2086 textBuffer *buf = textD->buffer;
2087 selection *primary = &buf->primary;
2088 char *textToCopy;
2089 int rectangular = hasKey("rect", args, nArgs);
2090 int insertPos, col;
2092 cancelDrag(w);
2093 if (checkReadOnly(w))
2094 return;
2095 if (primary->selected && rectangular) {
2096 textToCopy = BufGetSelectionText(buf);
2097 insertPos = TextDGetInsertPosition(textD);
2098 col = BufCountDispChars(buf, BufStartOfLine(buf, insertPos), insertPos);
2099 BufInsertCol(buf, col, insertPos, textToCopy, NULL, NULL);
2100 TextDSetInsertPosition(textD, buf->cursorPosHint);
2101 XtFree(textToCopy);
2102 BufRemoveSelected(buf);
2103 checkAutoShowInsertPos(w);
2104 } else if (primary->selected) {
2105 textToCopy = BufGetSelectionText(buf);
2106 insertPos = TextDGetInsertPosition(textD);
2107 BufInsert(buf, insertPos, textToCopy);
2108 TextDSetInsertPosition(textD, insertPos + strlen(textToCopy));
2109 XtFree(textToCopy);
2110 BufRemoveSelected(buf);
2111 checkAutoShowInsertPos(w);
2112 } else if (rectangular) {
2113 if (!TextDPositionToXY(textD, TextDGetInsertPosition(textD),
2114 &((TextWidget)w)->text.btnDownX,
2115 &((TextWidget)w)->text.btnDownY))
2116 return; /* shouldn't happen */
2117 MovePrimarySelection(w, e->time, True);
2118 } else {
2119 MovePrimarySelection(w, e->time, False);
2123 static void mousePanAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2125 XButtonEvent *e = &event->xbutton;
2126 TextWidget tw = (TextWidget)w;
2127 textDisp *textD = tw->text.textD;
2128 int lineHeight = textD->ascent + textD->descent;
2129 int topLineNum, horizOffset;
2130 static Cursor panCursor = 0;
2132 if (tw->text.dragState == MOUSE_PAN) {
2133 TextDSetScroll(textD,
2134 (tw->text.btnDownY - e->y + lineHeight/2) / lineHeight,
2135 tw->text.btnDownX - e->x);
2136 } else if (tw->text.dragState == NOT_CLICKED) {
2137 TextDGetScroll(textD, &topLineNum, &horizOffset);
2138 tw->text.btnDownX = e->x + horizOffset;
2139 tw->text.btnDownY = e->y + topLineNum * lineHeight;
2140 tw->text.dragState = MOUSE_PAN;
2141 if (!panCursor)
2142 panCursor = XCreateFontCursor(XtDisplay(w), XC_fleur);
2143 XGrabPointer(XtDisplay(w), XtWindow(w), False,
2144 ButtonMotionMask | ButtonReleaseMask, GrabModeAsync,
2145 GrabModeAsync, None, panCursor, CurrentTime);
2146 } else
2147 cancelDrag(w);
2150 static void pasteClipboardAP(Widget w, XEvent *event, String *args,
2151 Cardinal *nArgs)
2153 if (hasKey("rect", args, nArgs))
2154 TextColPasteClipboard(w, event->xkey.time);
2155 else
2156 TextPasteClipboard(w, event->xkey.time);
2159 static void copyClipboardAP(Widget w, XEvent *event, String *args,
2160 Cardinal *nArgs)
2162 TextCopyClipboard(w, event->xkey.time);
2165 static void cutClipboardAP(Widget w, XEvent *event, String *args,
2166 Cardinal *nArgs)
2168 TextCutClipboard(w, event->xkey.time);
2171 static void insertStringAP(Widget w, XEvent *event, String *args,
2172 Cardinal *nArgs)
2174 smartIndentCBStruct smartIndent;
2175 textDisp *textD = ((TextWidget)w)->text.textD;
2177 if (*nArgs == 0)
2178 return;
2179 cancelDrag(w);
2180 if (checkReadOnly(w))
2181 return;
2182 if (((TextWidget)w)->text.smartIndent) {
2183 smartIndent.reason = CHAR_TYPED;
2184 smartIndent.pos = TextDGetInsertPosition(textD);
2185 smartIndent.indentRequest = 0;
2186 smartIndent.charsTyped = args[0];
2187 XtCallCallbacks(w, textNsmartIndentCallback, (XtPointer)&smartIndent);
2189 TextInsertAtCursor(w, args[0], event, True, True);
2190 BufUnselect((((TextWidget)w)->text.textD)->buffer);
2193 static void selfInsertAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2195 #ifdef NO_XMIM
2196 static XComposeStatus compose = {NULL, 0};
2197 #else
2198 int status;
2199 #endif
2200 XKeyEvent *e = &event->xkey;
2201 char chars[20];
2202 KeySym keysym;
2203 int nChars;
2204 smartIndentCBStruct smartIndent;
2205 textDisp *textD = ((TextWidget)w)->text.textD;
2207 #ifdef NO_XMIM
2208 nChars = XLookupString(&event->xkey, chars, 19, &keysym, &compose);
2209 if (nChars == 0)
2210 return;
2211 #else
2212 nChars = XmImMbLookupString(w, &event->xkey, chars, 19, &keysym,
2213 &status);
2214 if (nChars == 0 || status == XLookupNone ||
2215 status == XLookupKeySym || status == XBufferOverflow)
2216 return;
2217 #endif
2218 cancelDrag(w);
2219 if (checkReadOnly(w))
2220 return;
2221 TakeMotifDestination(w, e->time);
2222 chars[nChars] = '\0';
2224 /* If smart indent is on, call the smart indent callback to check the
2225 inserted character */
2226 if (((TextWidget)w)->text.smartIndent) {
2227 smartIndent.reason = CHAR_TYPED;
2228 smartIndent.pos = TextDGetInsertPosition(textD);
2229 smartIndent.indentRequest = 0;
2230 smartIndent.charsTyped = chars;
2231 XtCallCallbacks(w, textNsmartIndentCallback, (XtPointer)&smartIndent);
2233 TextInsertAtCursor(w, chars, event, True, True);
2234 BufUnselect(textD->buffer);
2237 static void newlineAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2239 if (((TextWidget)w)->text.autoIndent || ((TextWidget)w)->text.smartIndent)
2240 newlineAndIndentAP(w, event, args, nArgs);
2241 else
2242 newlineNoIndentAP(w, event, args, nArgs);
2245 static void newlineNoIndentAP(Widget w, XEvent *event, String *args,
2246 Cardinal *nArgs)
2248 XKeyEvent *e = &event->xkey;
2250 cancelDrag(w);
2251 if (checkReadOnly(w))
2252 return;
2253 TakeMotifDestination(w, e->time);
2254 simpleInsertAtCursor(w, "\n", event, True);
2255 BufUnselect((((TextWidget)w)->text.textD)->buffer);
2258 static void newlineAndIndentAP(Widget w, XEvent *event, String *args,
2259 Cardinal *nArgs)
2261 XKeyEvent *e = &event->xkey;
2262 TextWidget tw = (TextWidget)w;
2263 textDisp *textD = tw->text.textD;
2264 textBuffer *buf = textD->buffer;
2265 char *indentStr;
2266 int cursorPos, lineStartPos, column;
2268 if (checkReadOnly(w))
2269 return;
2270 cancelDrag(w);
2271 TakeMotifDestination(w, e->time);
2273 /* Create a string containing a newline followed by auto or smart
2274 indent string */
2275 cursorPos = TextDGetInsertPosition(textD);
2276 lineStartPos = BufStartOfLine(buf, cursorPos);
2277 indentStr = createIndentString(tw, buf, 0, lineStartPos,
2278 cursorPos, NULL, &column);
2280 /* Insert it at the cursor */
2281 simpleInsertAtCursor(w, indentStr, event, True);
2282 XtFree(indentStr);
2284 /* If emulated tabs are on, make the inserted indent deletable by tab */
2285 if (tw->text.emulateTabs)
2286 tw->text.emTabsBeforeCursor = column / tw->text.emulateTabs;
2288 BufUnselect(buf);
2291 static void processTabAP(Widget w, XEvent *event, String *args,
2292 Cardinal *nArgs)
2294 textDisp *textD = ((TextWidget)w)->text.textD;
2295 textBuffer *buf = textD->buffer;
2296 selection *sel = &buf->primary;
2297 int emTabDist = ((TextWidget)w)->text.emulateTabs;
2298 int emTabsBeforeCursor = ((TextWidget)w)->text.emTabsBeforeCursor;
2299 int insertPos, indent, startIndent, toIndent, lineStart, tabWidth;
2300 char *outStr, *outPtr;
2302 if (checkReadOnly(w))
2303 return;
2304 cancelDrag(w);
2305 TakeMotifDestination(w, event->xkey.time);
2307 /* If emulated tabs are off, just insert a tab */
2308 if (emTabDist <= 0) {
2309 TextInsertAtCursor(w, "\t", event, True, True);
2310 return;
2313 /* Find the starting and ending indentation. If the tab is to
2314 replace an existing selection, use the start of the selection
2315 instead of the cursor position as the indent. When replacing
2316 rectangular selections, tabs are automatically recalculated as
2317 if the inserted text began at the start of the line */
2318 insertPos = pendingSelection(w) ?
2319 sel->start : TextDGetInsertPosition(textD);
2320 lineStart = BufStartOfLine(buf, insertPos);
2321 if (pendingSelection(w) && sel->rectangular)
2322 insertPos = BufCountForwardDispChars(buf, lineStart, sel->rectStart);
2323 startIndent = BufCountDispChars(buf, lineStart, insertPos);
2324 toIndent = startIndent + emTabDist - (startIndent % emTabDist);
2325 if (pendingSelection(w) && sel->rectangular) {
2326 toIndent -= startIndent;
2327 startIndent = 0;
2330 /* Allocate a buffer assuming all the inserted characters will be spaces */
2331 outStr = XtMalloc(toIndent - startIndent + 1);
2333 /* Add spaces and tabs to outStr until it reaches toIndent */
2334 outPtr = outStr;
2335 indent = startIndent;
2336 while (indent < toIndent) {
2337 tabWidth = BufCharWidth('\t', indent, buf->tabDist, buf->nullSubsChar);
2338 if (buf->useTabs && tabWidth > 1 && indent + tabWidth <= toIndent) {
2339 *outPtr++ = '\t';
2340 indent += tabWidth;
2341 } else {
2342 *outPtr++ = ' ';
2343 indent++;
2346 *outPtr = '\0';
2348 /* Insert the emulated tab */
2349 TextInsertAtCursor(w, outStr, event, True, True);
2350 XtFree(outStr);
2352 /* Restore and ++ emTabsBeforeCursor cleared by TextInsertAtCursor */
2353 ((TextWidget)w)->text.emTabsBeforeCursor = emTabsBeforeCursor + 1;
2355 BufUnselect(buf);
2358 static void deleteSelectionAP(Widget w, XEvent *event, String *args,
2359 Cardinal *nArgs)
2361 XKeyEvent *e = &event->xkey;
2363 cancelDrag(w);
2364 if (checkReadOnly(w))
2365 return;
2366 TakeMotifDestination(w, e->time);
2367 deletePendingSelection(w, event);
2370 static void deletePreviousCharacterAP(Widget w, XEvent *event, String *args,
2371 Cardinal *nArgs)
2373 XKeyEvent *e = &event->xkey;
2374 textDisp *textD = ((TextWidget)w)->text.textD;
2375 int insertPos = TextDGetInsertPosition(textD);
2376 char c;
2377 int silent = hasKey("nobell", args, nArgs);
2379 cancelDrag(w);
2380 if (checkReadOnly(w))
2381 return;
2382 TakeMotifDestination(w, e->time);
2383 if (deletePendingSelection(w, event))
2384 return;
2385 if (insertPos == 0) {
2386 ringIfNecessary(silent, w);
2387 return;
2389 if (deleteEmulatedTab(w, event))
2390 return;
2391 if (((TextWidget)w)->text.overstrike) {
2392 c = BufGetCharacter(textD->buffer, insertPos - 1);
2393 if (c == '\n')
2394 BufRemove(textD->buffer, insertPos - 1, insertPos);
2395 else if (c != '\t')
2396 BufReplace(textD->buffer, insertPos - 1, insertPos, " ");
2397 } else {
2398 BufRemove(textD->buffer, insertPos - 1, insertPos);
2400 TextDSetInsertPosition(textD, insertPos - 1);
2401 checkAutoShowInsertPos(w);
2402 callCursorMovementCBs(w, event);
2405 static void deleteNextCharacterAP(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 int silent = hasKey("nobell", args, nArgs);
2413 cancelDrag(w);
2414 if (checkReadOnly(w))
2415 return;
2416 TakeMotifDestination(w, e->time);
2417 if (deletePendingSelection(w, event))
2418 return;
2419 if (insertPos == textD->buffer->length) {
2420 ringIfNecessary(silent, w);
2421 return;
2423 BufRemove(textD->buffer, insertPos , insertPos + 1);
2424 checkAutoShowInsertPos(w);
2425 callCursorMovementCBs(w, event);
2428 static void deletePreviousWordAP(Widget w, XEvent *event, String *args,
2429 Cardinal *nArgs)
2431 XKeyEvent *e = &event->xkey;
2432 textDisp *textD = ((TextWidget)w)->text.textD;
2433 int insertPos = TextDGetInsertPosition(textD);
2434 int pos, lineStart = BufStartOfLine(textD->buffer, insertPos);
2435 char *delimiters = ((TextWidget)w)->text.delimiters;
2436 int silent = hasKey("nobell", args, nArgs);
2438 cancelDrag(w);
2439 if (checkReadOnly(w))
2440 return;
2441 TakeMotifDestination(w, e->time);
2442 if (deletePendingSelection(w, event))
2443 return;
2444 if (insertPos == lineStart) {
2445 ringIfNecessary(silent, w);
2446 return;
2448 pos = max(insertPos - 1, 0);
2449 while (strchr(delimiters, BufGetCharacter(textD->buffer, pos)) != NULL &&
2450 pos != lineStart)
2451 pos--;
2452 pos = startOfWord((TextWidget)w, pos);
2453 BufRemove(textD->buffer, pos, insertPos);
2454 checkAutoShowInsertPos(w);
2455 callCursorMovementCBs(w, event);
2458 static void deleteNextWordAP(Widget w, XEvent *event, String *args,
2459 Cardinal *nArgs)
2461 XKeyEvent *e = &event->xkey;
2462 textDisp *textD = ((TextWidget)w)->text.textD;
2463 int insertPos = TextDGetInsertPosition(textD);
2464 int pos, lineEnd = BufEndOfLine(textD->buffer, insertPos);
2465 char *delimiters = ((TextWidget)w)->text.delimiters;
2466 int silent = hasKey("nobell", args, nArgs);
2468 cancelDrag(w);
2469 if (checkReadOnly(w))
2470 return;
2471 TakeMotifDestination(w, e->time);
2472 if (deletePendingSelection(w, event))
2473 return;
2474 if (insertPos == lineEnd) {
2475 ringIfNecessary(silent, w);
2476 return;
2478 pos = insertPos;
2479 while (strchr(delimiters, BufGetCharacter(textD->buffer, pos)) != NULL &&
2480 pos != lineEnd)
2481 pos++;
2482 pos = endOfWord((TextWidget)w, pos);
2483 BufRemove(textD->buffer, insertPos, pos);
2484 checkAutoShowInsertPos(w);
2485 callCursorMovementCBs(w, event);
2488 static void deleteToEndOfLineAP(Widget w, XEvent *event, String *args,
2489 Cardinal *nArgs)
2491 XKeyEvent *e = &event->xkey;
2492 textDisp *textD = ((TextWidget)w)->text.textD;
2493 int insertPos = TextDGetInsertPosition(textD);
2494 int endOfLine;
2495 int silent = 0;
2497 silent = hasKey("nobell", args, nArgs);
2498 if (hasKey("absolute", args, nArgs))
2499 endOfLine = BufEndOfLine(textD->buffer, insertPos);
2500 else
2501 endOfLine = TextDEndOfLine(textD, insertPos, False);
2502 cancelDrag(w);
2503 if (checkReadOnly(w))
2504 return;
2505 TakeMotifDestination(w, e->time);
2506 if (deletePendingSelection(w, event))
2507 return;
2508 if (insertPos == endOfLine) {
2509 ringIfNecessary(silent, w);
2510 return;
2512 BufRemove(textD->buffer, insertPos, endOfLine);
2513 checkAutoShowInsertPos(w);
2514 callCursorMovementCBs(w, event);
2517 static void deleteToStartOfLineAP(Widget w, XEvent *event, String *args,
2518 Cardinal *nArgs)
2520 XKeyEvent *e = &event->xkey;
2521 textDisp *textD = ((TextWidget)w)->text.textD;
2522 int insertPos = TextDGetInsertPosition(textD);
2523 int startOfLine;
2524 int silent = 0;
2526 silent = hasKey("nobell", args, nArgs);
2527 if (hasKey("wrap", args, nArgs))
2528 startOfLine = TextDStartOfLine(textD, insertPos);
2529 else
2530 startOfLine = BufStartOfLine(textD->buffer, insertPos);
2531 cancelDrag(w);
2532 if (checkReadOnly(w))
2533 return;
2534 TakeMotifDestination(w, e->time);
2535 if (deletePendingSelection(w, event))
2536 return;
2537 if (insertPos == startOfLine) {
2538 ringIfNecessary(silent, w);
2539 return;
2541 BufRemove(textD->buffer, startOfLine, insertPos);
2542 checkAutoShowInsertPos(w);
2543 callCursorMovementCBs(w, event);
2546 static void forwardCharacterAP(Widget w, XEvent *event, String *args,
2547 Cardinal *nArgs)
2549 int insertPos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
2550 int silent = hasKey("nobell", args, nArgs);
2552 cancelDrag(w);
2553 if (!TextDMoveRight(((TextWidget)w)->text.textD))
2554 ringIfNecessary(silent, w);
2555 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2556 checkAutoShowInsertPos(w);
2557 callCursorMovementCBs(w, event);
2560 static void backwardCharacterAP(Widget w, XEvent *event, String *args,
2561 Cardinal *nArgs)
2563 int insertPos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
2564 int silent = hasKey("nobell", args, nArgs);
2566 cancelDrag(w);
2567 if (!TextDMoveLeft(((TextWidget)w)->text.textD))
2568 ringIfNecessary(silent, w);
2569 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2570 checkAutoShowInsertPos(w);
2571 callCursorMovementCBs(w, event);
2574 static void forwardWordAP(Widget w, XEvent *event, String *args,
2575 Cardinal *nArgs)
2577 textDisp *textD = ((TextWidget)w)->text.textD;
2578 textBuffer *buf = textD->buffer;
2579 int pos, insertPos = TextDGetInsertPosition(textD);
2580 char *delimiters = ((TextWidget)w)->text.delimiters;
2581 int silent = hasKey("nobell", args, nArgs);
2583 cancelDrag(w);
2584 if (insertPos == buf->length) {
2585 ringIfNecessary(silent, w);
2586 return;
2588 pos = insertPos;
2589 if (hasKey("tail", args, nArgs)) {
2590 for (; pos<buf->length; pos++) {
2591 if (strchr(delimiters, BufGetCharacter(buf, pos)) == NULL)
2592 break;
2594 if (strchr(delimiters, BufGetCharacter(buf, pos)) == NULL)
2595 pos = endOfWord((TextWidget)w, pos);
2597 else {
2598 if (strchr(delimiters, BufGetCharacter(buf, pos)) == NULL)
2599 pos = endOfWord((TextWidget)w, pos);
2600 for (; pos<buf->length; pos++) {
2601 if (strchr(delimiters, BufGetCharacter(buf, pos)) == NULL)
2602 break;
2605 TextDSetInsertPosition(textD, pos);
2606 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2607 checkAutoShowInsertPos(w);
2608 callCursorMovementCBs(w, event);
2611 static void backwardWordAP(Widget w, XEvent *event, String *args,
2612 Cardinal *nArgs)
2614 textDisp *textD = ((TextWidget)w)->text.textD;
2615 textBuffer *buf = textD->buffer;
2616 int pos, insertPos = TextDGetInsertPosition(textD);
2617 char *delimiters = ((TextWidget)w)->text.delimiters;
2618 int silent = hasKey("nobell", args, nArgs);
2620 cancelDrag(w);
2621 if (insertPos == 0) {
2622 ringIfNecessary(silent, w);
2623 return;
2625 pos = max(insertPos - 1, 0);
2626 while (strchr(delimiters, BufGetCharacter(buf, pos)) != NULL && pos > 0)
2627 pos--;
2628 pos = startOfWord((TextWidget)w, pos);
2630 TextDSetInsertPosition(textD, pos);
2631 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2632 checkAutoShowInsertPos(w);
2633 callCursorMovementCBs(w, event);
2636 static void forwardParagraphAP(Widget w, XEvent *event, String *args,
2637 Cardinal *nArgs)
2639 textDisp *textD = ((TextWidget)w)->text.textD;
2640 int pos, insertPos = TextDGetInsertPosition(textD);
2641 textBuffer *buf = textD->buffer;
2642 char c;
2643 static char whiteChars[] = " \t";
2644 int silent = hasKey("nobell", args, nArgs);
2646 cancelDrag(w);
2647 if (insertPos == buf->length) {
2648 ringIfNecessary(silent, w);
2649 return;
2651 pos = min(BufEndOfLine(buf, insertPos)+1, buf->length);
2652 while (pos < buf->length) {
2653 c = BufGetCharacter(buf, pos);
2654 if (c == '\n')
2655 break;
2656 if (strchr(whiteChars, c) != NULL)
2657 pos++;
2658 else
2659 pos = min(BufEndOfLine(buf, pos)+1, buf->length);
2661 TextDSetInsertPosition(textD, min(pos+1, buf->length));
2662 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2663 checkAutoShowInsertPos(w);
2664 callCursorMovementCBs(w, event);
2667 static void backwardParagraphAP(Widget w, XEvent *event, String *args,
2668 Cardinal *nArgs)
2670 textDisp *textD = ((TextWidget)w)->text.textD;
2671 int parStart, pos, insertPos = TextDGetInsertPosition(textD);
2672 textBuffer *buf = textD->buffer;
2673 char c;
2674 static char whiteChars[] = " \t";
2675 int silent = hasKey("nobell", args, nArgs);
2677 cancelDrag(w);
2678 if (insertPos == 0) {
2679 ringIfNecessary(silent, w);
2680 return;
2682 parStart = BufStartOfLine(buf, max(insertPos-1, 0));
2683 pos = max(parStart - 2, 0);
2684 while (pos > 0) {
2685 c = BufGetCharacter(buf, pos);
2686 if (c == '\n')
2687 break;
2688 if (strchr(whiteChars, c) != NULL)
2689 pos--;
2690 else {
2691 parStart = BufStartOfLine(buf, pos);
2692 pos = max(parStart - 2, 0);
2695 TextDSetInsertPosition(textD, parStart);
2696 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2697 checkAutoShowInsertPos(w);
2698 callCursorMovementCBs(w, event);
2701 static void keySelectAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2703 textDisp *textD = ((TextWidget)w)->text.textD;
2704 int stat, insertPos = TextDGetInsertPosition(textD);
2705 int silent = hasKey("nobell", args, nArgs);
2707 cancelDrag(w);
2708 if (hasKey("left", args, nArgs)) stat = TextDMoveLeft(textD);
2709 else if (hasKey("right", args, nArgs)) stat = TextDMoveRight(textD);
2710 else if (hasKey("up", args, nArgs)) stat = TextDMoveUp(textD, 0);
2711 else if (hasKey("down", args, nArgs)) stat = TextDMoveDown(textD, 0);
2712 else {
2713 keyMoveExtendSelection(w, event, insertPos, hasKey("rect", args,nArgs));
2714 return;
2716 if (!stat) {
2717 ringIfNecessary(silent, w);
2719 else {
2720 keyMoveExtendSelection(w, event, insertPos, hasKey("rect", args,nArgs));
2721 checkAutoShowInsertPos(w);
2722 callCursorMovementCBs(w, event);
2726 static void processUpAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2728 int insertPos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
2729 int silent = hasKey("nobell", args, nArgs);
2730 int abs = hasKey("absolute", args, nArgs);
2732 cancelDrag(w);
2733 if (!TextDMoveUp(((TextWidget)w)->text.textD, abs))
2734 ringIfNecessary(silent, w);
2735 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2736 checkAutoShowInsertPos(w);
2737 callCursorMovementCBs(w, event);
2740 static void processShiftUpAP(Widget w, XEvent *event, String *args,
2741 Cardinal *nArgs)
2743 int insertPos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
2744 int silent = hasKey("nobell", args, nArgs);
2745 int abs = hasKey("absolute", args, nArgs);
2747 cancelDrag(w);
2748 if (!TextDMoveUp(((TextWidget)w)->text.textD, abs))
2749 ringIfNecessary(silent, w);
2750 keyMoveExtendSelection(w, event, insertPos, hasKey("rect", args, nArgs));
2751 checkAutoShowInsertPos(w);
2752 callCursorMovementCBs(w, event);
2755 static void processDownAP(Widget w, XEvent *event, String *args,
2756 Cardinal *nArgs)
2758 int insertPos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
2759 int silent = hasKey("nobell", args, nArgs);
2760 int abs = hasKey("absolute", args, nArgs);
2762 cancelDrag(w);
2763 if (!TextDMoveDown(((TextWidget)w)->text.textD, abs))
2764 ringIfNecessary(silent, w);
2765 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2766 checkAutoShowInsertPos(w);
2767 callCursorMovementCBs(w, event);
2770 static void processShiftDownAP(Widget w, XEvent *event, String *args,
2771 Cardinal *nArgs)
2773 int insertPos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
2774 int silent = hasKey("nobell", args, nArgs);
2775 int abs = hasKey("absolute", args, nArgs);
2777 cancelDrag(w);
2778 if (!TextDMoveDown(((TextWidget)w)->text.textD, abs))
2779 ringIfNecessary(silent, w);
2780 keyMoveExtendSelection(w, event, insertPos, hasKey("rect", args, nArgs));
2781 checkAutoShowInsertPos(w);
2782 callCursorMovementCBs(w, event);
2785 static void beginningOfLineAP(Widget w, XEvent *event, String *args,
2786 Cardinal *nArgs)
2788 textDisp *textD = ((TextWidget)w)->text.textD;
2789 int insertPos = TextDGetInsertPosition(textD);
2791 cancelDrag(w);
2792 if (hasKey("absolute", args, nArgs))
2793 TextDSetInsertPosition(textD, BufStartOfLine(textD->buffer, insertPos));
2794 else
2795 TextDSetInsertPosition(textD, TextDStartOfLine(textD, insertPos));
2796 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2797 checkAutoShowInsertPos(w);
2798 callCursorMovementCBs(w, event);
2799 textD->cursorPreferredCol = 0;
2802 static void endOfLineAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2804 textDisp *textD = ((TextWidget)w)->text.textD;
2805 int insertPos = TextDGetInsertPosition(textD);
2807 cancelDrag(w);
2808 if (hasKey("absolute", args, nArgs))
2809 TextDSetInsertPosition(textD, BufEndOfLine(textD->buffer, insertPos));
2810 else
2811 TextDSetInsertPosition(textD, TextDEndOfLine(textD, insertPos, False));
2812 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2813 checkAutoShowInsertPos(w);
2814 callCursorMovementCBs(w, event);
2815 textD->cursorPreferredCol = -1;
2818 static void beginningOfFileAP(Widget w, XEvent *event, String *args,
2819 Cardinal *nArgs)
2821 int insertPos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
2822 textDisp *textD = ((TextWidget)w)->text.textD;
2824 cancelDrag(w);
2825 if (hasKey("scrollbar", args, nArgs)) {
2826 if (textD->topLineNum != 1) {
2827 TextDSetScroll(textD, 1, textD->horizOffset);
2830 else {
2831 TextDSetInsertPosition(((TextWidget)w)->text.textD, 0);
2832 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2833 checkAutoShowInsertPos(w);
2834 callCursorMovementCBs(w, event);
2838 static void endOfFileAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2840 textDisp *textD = ((TextWidget)w)->text.textD;
2841 int insertPos = TextDGetInsertPosition(textD);
2842 int lastTopLine;
2844 cancelDrag(w);
2845 if (hasKey("scrollbar", args, nArgs)) {
2846 lastTopLine = max(1,
2847 textD->nBufferLines - (textD->nVisibleLines - 2) +
2848 ((TextWidget)w)->text.cursorVPadding);
2849 if (lastTopLine != textD->topLineNum) {
2850 TextDSetScroll(textD, lastTopLine, textD->horizOffset);
2853 else {
2854 TextDSetInsertPosition(textD, textD->buffer->length);
2855 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2856 checkAutoShowInsertPos(w);
2857 callCursorMovementCBs(w, event);
2861 static void nextPageAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2863 textDisp *textD = ((TextWidget)w)->text.textD;
2864 textBuffer *buf = textD->buffer;
2865 int lastTopLine = max(1,
2866 textD->nBufferLines - (textD->nVisibleLines - 2) +
2867 ((TextWidget)w)->text.cursorVPadding );
2868 int insertPos = TextDGetInsertPosition(textD);
2869 int column = 0, visLineNum, lineStartPos;
2870 int pos, targetLine;
2871 int pageForwardCount = max(1, textD->nVisibleLines - 1);
2872 int maintainColumn = 0;
2873 int silent = hasKey("nobell", args, nArgs);
2875 maintainColumn = hasKey("column", args, nArgs);
2876 cancelDrag(w);
2877 if (hasKey("scrollbar", args, nArgs)) { /* scrollbar only */
2878 targetLine = min(textD->topLineNum + pageForwardCount, lastTopLine);
2880 if (targetLine == textD->topLineNum) {
2881 ringIfNecessary(silent, w);
2882 return;
2884 TextDSetScroll(textD, targetLine, textD->horizOffset);
2886 else if (hasKey("stutter", args, nArgs)) { /* Mac style */
2887 /* move to bottom line of visible area */
2888 /* if already there, page down maintaining preferrred column */
2889 targetLine = max(min(textD->nVisibleLines - 1, textD->nBufferLines), 0);
2890 column = TextDPreferredColumn(textD, &visLineNum, &lineStartPos);
2891 if (lineStartPos == textD->lineStarts[targetLine]) {
2892 if (insertPos >= buf->length || textD->topLineNum == lastTopLine) {
2893 ringIfNecessary(silent, w);
2894 return;
2896 targetLine = min(textD->topLineNum + pageForwardCount, lastTopLine);
2897 pos = TextDCountForwardNLines(textD, insertPos, pageForwardCount, False);
2898 if (maintainColumn) {
2899 pos = TextDPosOfPreferredCol(textD, column, pos);
2901 TextDSetInsertPosition(textD, pos);
2902 TextDSetScroll(textD, targetLine, textD->horizOffset);
2904 else {
2905 pos = textD->lineStarts[targetLine];
2906 while (targetLine > 0 && pos == -1) {
2907 --targetLine;
2908 pos = textD->lineStarts[targetLine];
2910 if (lineStartPos == pos) {
2911 ringIfNecessary(silent, w);
2912 return;
2914 if (maintainColumn) {
2915 pos = TextDPosOfPreferredCol(textD, column, pos);
2917 TextDSetInsertPosition(textD, pos);
2919 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2920 checkAutoShowInsertPos(w);
2921 callCursorMovementCBs(w, event);
2922 if (maintainColumn) {
2923 textD->cursorPreferredCol = column;
2925 else {
2926 textD->cursorPreferredCol = -1;
2929 else { /* "standard" */
2930 if (insertPos >= buf->length && textD->topLineNum == lastTopLine) {
2931 ringIfNecessary(silent, w);
2932 return;
2934 if (maintainColumn) {
2935 column = TextDPreferredColumn(textD, &visLineNum, &lineStartPos);
2937 targetLine = textD->topLineNum + textD->nVisibleLines - 1;
2938 if (targetLine < 1) targetLine = 1;
2939 if (targetLine > lastTopLine) targetLine = lastTopLine;
2940 pos = TextDCountForwardNLines(textD, insertPos, textD->nVisibleLines-1, False);
2941 if (maintainColumn) {
2942 pos = TextDPosOfPreferredCol(textD, column, pos);
2944 TextDSetInsertPosition(textD, pos);
2945 TextDSetScroll(textD, targetLine, textD->horizOffset);
2946 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2947 checkAutoShowInsertPos(w);
2948 callCursorMovementCBs(w, event);
2949 if (maintainColumn) {
2950 textD->cursorPreferredCol = column;
2952 else {
2953 textD->cursorPreferredCol = -1;
2958 static void previousPageAP(Widget w, XEvent *event, String *args,
2959 Cardinal *nArgs)
2961 textDisp *textD = ((TextWidget)w)->text.textD;
2962 int insertPos = TextDGetInsertPosition(textD);
2963 int column = 0, visLineNum, lineStartPos;
2964 int pos, targetLine;
2965 int pageBackwardCount = max(1, textD->nVisibleLines - 1);
2966 int maintainColumn = 0;
2967 int silent = hasKey("nobell", args, nArgs);
2969 maintainColumn = hasKey("column", args, nArgs);
2970 cancelDrag(w);
2971 if (hasKey("scrollbar", args, nArgs)) { /* scrollbar only */
2972 targetLine = max(textD->topLineNum - pageBackwardCount, 1);
2974 if (targetLine == textD->topLineNum) {
2975 ringIfNecessary(silent, w);
2976 return;
2978 TextDSetScroll(textD, targetLine, textD->horizOffset);
2980 else if (hasKey("stutter", args, nArgs)) { /* Mac style */
2981 /* move to top line of visible area */
2982 /* if already there, page up maintaining preferrred column if required */
2983 targetLine = 0;
2984 column = TextDPreferredColumn(textD, &visLineNum, &lineStartPos);
2985 if (lineStartPos == textD->lineStarts[targetLine]) {
2986 if (textD->topLineNum == 1 && (maintainColumn || column == 0)) {
2987 ringIfNecessary(silent, w);
2988 return;
2990 targetLine = max(textD->topLineNum - pageBackwardCount, 1);
2991 pos = TextDCountBackwardNLines(textD, insertPos, pageBackwardCount);
2992 if (maintainColumn) {
2993 pos = TextDPosOfPreferredCol(textD, column, pos);
2995 TextDSetInsertPosition(textD, pos);
2996 TextDSetScroll(textD, targetLine, textD->horizOffset);
2998 else {
2999 pos = textD->lineStarts[targetLine];
3000 if (maintainColumn) {
3001 pos = TextDPosOfPreferredCol(textD, column, pos);
3003 TextDSetInsertPosition(textD, pos);
3005 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
3006 checkAutoShowInsertPos(w);
3007 callCursorMovementCBs(w, event);
3008 if (maintainColumn) {
3009 textD->cursorPreferredCol = column;
3011 else {
3012 textD->cursorPreferredCol = -1;
3015 else { /* "standard" */
3016 if (insertPos <= 0 && textD->topLineNum == 1) {
3017 ringIfNecessary(silent, w);
3018 return;
3020 if (maintainColumn) {
3021 column = TextDPreferredColumn(textD, &visLineNum, &lineStartPos);
3023 targetLine = textD->topLineNum - (textD->nVisibleLines - 1);
3024 if (targetLine < 1) targetLine = 1;
3025 pos = TextDCountBackwardNLines(textD, insertPos, textD->nVisibleLines-1);
3026 if (maintainColumn) {
3027 pos = TextDPosOfPreferredCol(textD, column, pos);
3029 TextDSetInsertPosition(textD, pos);
3030 TextDSetScroll(textD, targetLine, textD->horizOffset);
3031 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
3032 checkAutoShowInsertPos(w);
3033 callCursorMovementCBs(w, event);
3034 if (maintainColumn) {
3035 textD->cursorPreferredCol = column;
3037 else {
3038 textD->cursorPreferredCol = -1;
3043 static void pageLeftAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
3045 textDisp *textD = ((TextWidget)w)->text.textD;
3046 textBuffer *buf = textD->buffer;
3047 int insertPos = TextDGetInsertPosition(textD);
3048 int maxCharWidth = textD->fontStruct->max_bounds.width;
3049 int lineStartPos, indent, pos;
3050 int horizOffset;
3051 int silent = hasKey("nobell", args, nArgs);
3053 cancelDrag(w);
3054 if (hasKey("scrollbar", args, nArgs)) {
3055 if (textD->horizOffset == 0) {
3056 ringIfNecessary(silent, w);
3057 return;
3059 horizOffset = max(0, textD->horizOffset - textD->width);
3060 TextDSetScroll(textD, textD->topLineNum, horizOffset);
3062 else {
3063 lineStartPos = BufStartOfLine(buf, insertPos);
3064 if (insertPos == lineStartPos && textD->horizOffset == 0) {
3065 ringIfNecessary(silent, w);
3066 return;
3068 indent = BufCountDispChars(buf, lineStartPos, insertPos);
3069 pos = BufCountForwardDispChars(buf, lineStartPos,
3070 max(0, indent - textD->width / maxCharWidth));
3071 TextDSetInsertPosition(textD, pos);
3072 TextDSetScroll(textD, textD->topLineNum,
3073 max(0, textD->horizOffset - textD->width));
3074 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
3075 checkAutoShowInsertPos(w);
3076 callCursorMovementCBs(w, event);
3080 static void pageRightAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
3082 textDisp *textD = ((TextWidget)w)->text.textD;
3083 textBuffer *buf = textD->buffer;
3084 int insertPos = TextDGetInsertPosition(textD);
3085 int maxCharWidth = textD->fontStruct->max_bounds.width;
3086 int oldHorizOffset = textD->horizOffset;
3087 int lineStartPos, indent, pos;
3088 int horizOffset, sliderSize, sliderMax;
3089 int silent = hasKey("nobell", args, nArgs);
3091 cancelDrag(w);
3092 if (hasKey("scrollbar", args, nArgs)) {
3093 XtVaGetValues(textD->hScrollBar, XmNmaximum, &sliderMax,
3094 XmNsliderSize, &sliderSize, 0);
3095 horizOffset = min(textD->horizOffset + textD->width, sliderMax - sliderSize);
3096 if (textD->horizOffset == horizOffset) {
3097 ringIfNecessary(silent, w);
3098 return;
3100 TextDSetScroll(textD, textD->topLineNum, horizOffset);
3102 else {
3103 lineStartPos = BufStartOfLine(buf, insertPos);
3104 indent = BufCountDispChars(buf, lineStartPos, insertPos);
3105 pos = BufCountForwardDispChars(buf, lineStartPos,
3106 indent + textD->width / maxCharWidth);
3107 TextDSetInsertPosition(textD, pos);
3108 TextDSetScroll(textD, textD->topLineNum, textD->horizOffset + textD->width);
3109 if (textD->horizOffset == oldHorizOffset && insertPos == pos)
3110 ringIfNecessary(silent, w);
3111 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
3112 checkAutoShowInsertPos(w);
3113 callCursorMovementCBs(w, event);
3117 static void toggleOverstrikeAP(Widget w, XEvent *event, String *args,
3118 Cardinal *nArgs)
3120 TextWidget tw = (TextWidget)w;
3122 if (tw->text.overstrike) {
3123 tw->text.overstrike = False;
3124 TextDSetCursorStyle(tw->text.textD,
3125 tw->text.heavyCursor ? HEAVY_CURSOR : NORMAL_CURSOR);
3126 } else {
3127 tw->text.overstrike = True;
3128 if ( tw->text.textD->cursorStyle == NORMAL_CURSOR ||
3129 tw->text.textD->cursorStyle == HEAVY_CURSOR)
3130 TextDSetCursorStyle(tw->text.textD, BLOCK_CURSOR);
3134 static void scrollUpAP(Widget w, XEvent *event, String *args,
3135 Cardinal *nArgs)
3137 textDisp *textD = ((TextWidget)w)->text.textD;
3138 int topLineNum, horizOffset, nLines;
3140 if (*nArgs == 0 || sscanf(args[0], "%d", &nLines) != 1)
3141 return;
3142 TextDGetScroll(textD, &topLineNum, &horizOffset);
3143 TextDSetScroll(textD, topLineNum-nLines, horizOffset);
3146 static void scrollDownAP(Widget w, XEvent *event, String *args,
3147 Cardinal *nArgs)
3149 textDisp *textD = ((TextWidget)w)->text.textD;
3150 int topLineNum, horizOffset, nLines;
3152 if (*nArgs == 0 || sscanf(args[0], "%d", &nLines) != 1)
3153 return;
3154 TextDGetScroll(textD, &topLineNum, &horizOffset);
3155 TextDSetScroll(textD, topLineNum+nLines, horizOffset);
3158 static void scrollLeftAP(Widget w, XEvent *event, String *args,
3159 Cardinal *nArgs)
3161 textDisp *textD = ((TextWidget)w)->text.textD;
3162 int horizOffset, nPixels;
3163 int sliderMax, sliderSize;
3165 if (*nArgs == 0 || sscanf(args[0], "%d", &nPixels) != 1)
3166 return;
3167 XtVaGetValues(textD->hScrollBar, XmNmaximum, &sliderMax,
3168 XmNsliderSize, &sliderSize, 0);
3169 horizOffset = min(max(0, textD->horizOffset - nPixels), sliderMax - sliderSize);
3170 if (textD->horizOffset != horizOffset) {
3171 TextDSetScroll(textD, textD->topLineNum, horizOffset);
3175 static void scrollRightAP(Widget w, XEvent *event, String *args,
3176 Cardinal *nArgs)
3178 textDisp *textD = ((TextWidget)w)->text.textD;
3179 int horizOffset, nPixels;
3180 int sliderMax, sliderSize;
3182 if (*nArgs == 0 || sscanf(args[0], "%d", &nPixels) != 1)
3183 return;
3184 XtVaGetValues(textD->hScrollBar, XmNmaximum, &sliderMax,
3185 XmNsliderSize, &sliderSize, 0);
3186 horizOffset = min(max(0, textD->horizOffset + nPixels), sliderMax - sliderSize);
3187 if (textD->horizOffset != horizOffset) {
3188 TextDSetScroll(textD, textD->topLineNum, horizOffset);
3192 static void scrollToLineAP(Widget w, XEvent *event, String *args,
3193 Cardinal *nArgs)
3195 textDisp *textD = ((TextWidget)w)->text.textD;
3196 int topLineNum, horizOffset, lineNum;
3198 if (*nArgs == 0 || sscanf(args[0], "%d", &lineNum) != 1)
3199 return;
3200 TextDGetScroll(textD, &topLineNum, &horizOffset);
3201 TextDSetScroll(textD, lineNum, horizOffset);
3204 static void selectAllAP(Widget w, XEvent *event, String *args,
3205 Cardinal *nArgs)
3207 textBuffer *buf = ((TextWidget)w)->text.textD->buffer;
3209 cancelDrag(w);
3210 BufSelect(buf, 0, buf->length);
3213 static void deselectAllAP(Widget w, XEvent *event, String *args,
3214 Cardinal *nArgs)
3216 cancelDrag(w);
3217 BufUnselect(((TextWidget)w)->text.textD->buffer);
3220 static void focusInAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
3222 TextWidget tw = (TextWidget)w;
3223 textDisp *textD = tw->text.textD;
3225 /* I don't entirely understand the traversal mechanism in Motif widgets,
3226 particularly, what leads to this widget getting a focus-in event when
3227 it does not actually have the input focus. The temporary solution is
3228 to do the comparison below, and not show the cursor when Motif says
3229 we don't have focus, but keep looking for the real answer */
3230 #if XmVersion >= 1002
3231 if (w != XmGetFocusWidget(w))
3232 return;
3233 #endif
3235 /* If the timer is not already started, start it */
3236 if (tw->text.cursorBlinkRate != 0 && tw->text.cursorBlinkProcID == 0) {
3237 tw->text.cursorBlinkProcID = XtAppAddTimeOut(
3238 XtWidgetToApplicationContext((Widget)w),
3239 tw->text.cursorBlinkRate, cursorBlinkTimerProc, w);
3242 /* Change the cursor to active style */
3243 if (((TextWidget)w)->text.overstrike)
3244 TextDSetCursorStyle(textD, BLOCK_CURSOR);
3245 else
3246 TextDSetCursorStyle(textD, ((TextWidget)w)->text.heavyCursor ?
3247 HEAVY_CURSOR : NORMAL_CURSOR);
3248 TextDUnblankCursor(textD);
3250 #ifndef NO_XMIM
3251 /* Notify Motif input manager that widget has focus */
3252 XmImVaSetFocusValues(w,NULL);
3253 #endif
3255 /* Call any registered focus-in callbacks */
3256 XtCallCallbacks((Widget)w, textNfocusCallback, (XtPointer)event);
3259 static void focusOutAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
3261 textDisp *textD = ((TextWidget)w)->text.textD;
3263 /* Remove the cursor blinking timer procedure */
3264 if (((TextWidget)w)->text.cursorBlinkProcID != 0)
3265 XtRemoveTimeOut(((TextWidget)w)->text.cursorBlinkProcID);
3266 ((TextWidget)w)->text.cursorBlinkProcID = 0;
3268 /* Leave a dim or destination cursor */
3269 TextDSetCursorStyle(textD, ((TextWidget)w)->text.motifDestOwner ?
3270 CARET_CURSOR : DIM_CURSOR);
3271 TextDUnblankCursor(textD);
3273 /* If there's a calltip displayed, kill it. */
3274 TextDKillCalltip(textD, 0);
3276 /* Call any registered focus-out callbacks */
3277 XtCallCallbacks((Widget)w, textNlosingFocusCallback, (XtPointer)event);
3281 ** For actions involving cursor movement, "extend" keyword means incorporate
3282 ** the new cursor position in the selection, and lack of an "extend" keyword
3283 ** means cancel the existing selection
3285 static void checkMoveSelectionChange(Widget w, XEvent *event, int startPos,
3286 String *args, Cardinal *nArgs)
3288 if (hasKey("extend", args, nArgs))
3289 keyMoveExtendSelection(w, event, startPos, hasKey("rect", args, nArgs));
3290 else
3291 BufUnselect((((TextWidget)w)->text.textD)->buffer);
3295 ** If a selection change was requested via a keyboard command for moving
3296 ** the insertion cursor (usually with the "extend" keyword), adjust the
3297 ** selection to include the new cursor position, or begin a new selection
3298 ** between startPos and the new cursor position with anchor at startPos.
3300 static void keyMoveExtendSelection(Widget w, XEvent *event, int origPos,
3301 int rectangular)
3303 XKeyEvent *e = &event->xkey;
3304 TextWidget tw = (TextWidget)w;
3305 textDisp *textD = tw->text.textD;
3306 textBuffer *buf = textD->buffer;
3307 selection *sel = &buf->primary;
3308 int newPos = TextDGetInsertPosition(textD);
3309 int startPos, endPos, startCol, endCol, newCol, origCol;
3310 int anchor, rectAnchor, anchorLineStart;
3312 /* Moving the cursor does not take the Motif destination, but as soon as
3313 the user selects something, grab it (I'm not sure if this distinction
3314 actually makes sense, but it's what Motif was doing, back when their
3315 secondary selections actually worked correctly) */
3316 TakeMotifDestination(w, e->time);
3318 if ((sel->selected || sel->zeroWidth) && sel->rectangular && rectangular) {
3319 /* rect -> rect */
3320 newCol = BufCountDispChars(buf, BufStartOfLine(buf, newPos), newPos);
3321 startCol = min(tw->text.rectAnchor, newCol);
3322 endCol = max(tw->text.rectAnchor, newCol);
3323 startPos = BufStartOfLine(buf, min(tw->text.anchor, newPos));
3324 endPos = BufEndOfLine(buf, max(tw->text.anchor, newPos));
3325 BufRectSelect(buf, startPos, endPos, startCol, endCol);
3326 } else if (sel->selected && rectangular) { /* plain -> rect */
3327 newCol = BufCountDispChars(buf, BufStartOfLine(buf, newPos), newPos);
3328 if (abs(newPos - sel->start) < abs(newPos - sel->end))
3329 anchor = sel->end;
3330 else
3331 anchor = sel->start;
3332 anchorLineStart = BufStartOfLine(buf, anchor);
3333 rectAnchor = BufCountDispChars(buf, anchorLineStart, anchor);
3334 tw->text.anchor = anchor;
3335 tw->text.rectAnchor = rectAnchor;
3336 BufRectSelect(buf, BufStartOfLine(buf, min(anchor, newPos)),
3337 BufEndOfLine(buf, max(anchor, newPos)),
3338 min(rectAnchor, newCol), max(rectAnchor, newCol));
3339 } else if (sel->selected && sel->rectangular) { /* rect -> plain */
3340 startPos = BufCountForwardDispChars(buf,
3341 BufStartOfLine(buf, sel->start), sel->rectStart);
3342 endPos = BufCountForwardDispChars(buf,
3343 BufStartOfLine(buf, sel->end), sel->rectEnd);
3344 if (abs(origPos - startPos) < abs(origPos - endPos))
3345 anchor = endPos;
3346 else
3347 anchor = startPos;
3348 BufSelect(buf, anchor, newPos);
3349 } else if (sel->selected) { /* plain -> plain */
3350 if (abs(origPos - sel->start) < abs(origPos - sel->end))
3351 anchor = sel->end;
3352 else
3353 anchor = sel->start;
3354 BufSelect(buf, anchor, newPos);
3355 } else if (rectangular) { /* no sel -> rect */
3356 origCol = BufCountDispChars(buf, BufStartOfLine(buf, origPos), origPos);
3357 newCol = BufCountDispChars(buf, BufStartOfLine(buf, newPos), newPos);
3358 startCol = min(newCol, origCol);
3359 endCol = max(newCol, origCol);
3360 startPos = BufStartOfLine(buf, min(origPos, newPos));
3361 endPos = BufEndOfLine(buf, max(origPos, newPos));
3362 tw->text.anchor = origPos;
3363 tw->text.rectAnchor = origCol;
3364 BufRectSelect(buf, startPos, endPos, startCol, endCol);
3365 } else { /* no sel -> plain */
3366 tw->text.anchor = origPos;
3367 tw->text.rectAnchor = BufCountDispChars(buf,
3368 BufStartOfLine(buf, origPos), origPos);
3369 BufSelect(buf, tw->text.anchor, newPos);
3373 static void checkAutoShowInsertPos(Widget w)
3375 if (((TextWidget)w)->text.autoShowInsertPos)
3376 TextDMakeInsertPosVisible(((TextWidget)w)->text.textD);
3379 static int checkReadOnly(Widget w)
3381 if (((TextWidget)w)->text.readOnly) {
3382 XBell(XtDisplay(w), 0);
3383 return True;
3385 return False;
3389 ** Insert text "chars" at the cursor position, as if the text had been
3390 ** typed. Same as TextInsertAtCursor, but without the complicated auto-wrap
3391 ** scanning and re-formatting.
3393 static void simpleInsertAtCursor(Widget w, char *chars, XEvent *event,
3394 int allowPendingDelete)
3396 textDisp *textD = ((TextWidget)w)->text.textD;
3397 textBuffer *buf = textD->buffer;
3398 char *c;
3400 if (allowPendingDelete && pendingSelection(w)) {
3401 BufReplaceSelected(buf, chars);
3402 TextDSetInsertPosition(textD, buf->cursorPosHint);
3403 } else if (((TextWidget)w)->text.overstrike) {
3404 for (c=chars; *c!='\0' && *c!='\n'; c++);
3405 if (*c == '\n')
3406 TextDInsert(textD, chars);
3407 else
3408 TextDOverstrike(textD, chars);
3409 } else
3410 TextDInsert(textD, chars);
3411 checkAutoShowInsertPos(w);
3412 callCursorMovementCBs(w, event);
3416 ** If there's a selection, delete it and position the cursor where the
3417 ** selection was deleted. (Called by routines which do deletion to check
3418 ** first for and do possible selection delete)
3420 static int deletePendingSelection(Widget w, XEvent *event)
3422 textDisp *textD = ((TextWidget)w)->text.textD;
3423 textBuffer *buf = textD->buffer;
3425 if (((TextWidget)w)->text.textD->buffer->primary.selected) {
3426 BufRemoveSelected(buf);
3427 TextDSetInsertPosition(textD, buf->cursorPosHint);
3428 checkAutoShowInsertPos(w);
3429 callCursorMovementCBs(w, event);
3430 return True;
3431 } else
3432 return False;
3436 ** Return true if pending delete is on and there's a selection contiguous
3437 ** with the cursor ready to be deleted. These criteria are used to decide
3438 ** if typing a character or inserting something should delete the selection
3439 ** first.
3441 static int pendingSelection(Widget w)
3443 selection *sel = &((TextWidget)w)->text.textD->buffer->primary;
3444 int pos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
3446 return ((TextWidget)w)->text.pendingDelete && sel->selected &&
3447 pos >= sel->start && pos <= sel->end;
3451 ** Check if tab emulation is on and if there are emulated tabs before the
3452 ** cursor, and if so, delete an emulated tab as a unit. Also finishes up
3453 ** by calling checkAutoShowInsertPos and callCursorMovementCBs, so the
3454 ** calling action proc can just return (this is necessary to preserve
3455 ** emTabsBeforeCursor which is otherwise cleared by callCursorMovementCBs).
3457 static int deleteEmulatedTab(Widget w, XEvent *event)
3459 textDisp *textD = ((TextWidget)w)->text.textD;
3460 textBuffer *buf = ((TextWidget)w)->text.textD->buffer;
3461 int emTabDist = ((TextWidget)w)->text.emulateTabs;
3462 int emTabsBeforeCursor = ((TextWidget)w)->text.emTabsBeforeCursor;
3463 int startIndent, toIndent, insertPos, startPos, lineStart;
3464 int pos, indent, startPosIndent;
3465 char c, *spaceString;
3467 if (emTabDist <= 0 || emTabsBeforeCursor <= 0)
3468 return False;
3470 /* Find the position of the previous tab stop */
3471 insertPos = TextDGetInsertPosition(textD);
3472 lineStart = BufStartOfLine(buf, insertPos);
3473 startIndent = BufCountDispChars(buf, lineStart, insertPos);
3474 toIndent = (startIndent-1) - ((startIndent-1) % emTabDist);
3476 /* Find the position at which to begin deleting (stop at non-whitespace
3477 characters) */
3478 startPosIndent = indent = 0;
3479 startPos = lineStart;
3480 for (pos=lineStart; pos < insertPos; pos++) {
3481 c = BufGetCharacter(buf, pos);
3482 indent += BufCharWidth(c, indent, buf->tabDist, buf->nullSubsChar);
3483 if (indent > toIndent)
3484 break;
3485 startPosIndent = indent;
3486 startPos = pos + 1;
3489 /* Just to make sure, check that we're not deleting any non-white chars */
3490 for (pos=insertPos-1; pos>=startPos; pos--) {
3491 c = BufGetCharacter(buf, pos);
3492 if (c != ' ' && c != '\t') {
3493 startPos = pos + 1;
3494 break;
3498 /* Do the text replacement and reposition the cursor. If any spaces need
3499 to be inserted to make up for a deleted tab, do a BufReplace, otherwise,
3500 do a BufRemove. */
3501 if (startPosIndent < toIndent) {
3502 spaceString = XtMalloc(toIndent - startPosIndent + 1);
3503 memset(spaceString, ' ', toIndent-startPosIndent);
3504 spaceString[toIndent - startPosIndent] = '\0';
3505 BufReplace(buf, startPos, insertPos, spaceString);
3506 TextDSetInsertPosition(textD, startPos + toIndent - startPosIndent);
3507 XtFree(spaceString);
3508 } else {
3509 BufRemove(buf, startPos, insertPos);
3510 TextDSetInsertPosition(textD, startPos);
3513 /* The normal cursor movement stuff would usually be called by the action
3514 routine, but this wraps around it to restore emTabsBeforeCursor */
3515 checkAutoShowInsertPos(w);
3516 callCursorMovementCBs(w, event);
3518 /* Decrement and restore the marker for consecutive emulated tabs, which
3519 would otherwise have been zeroed by callCursorMovementCBs */
3520 ((TextWidget)w)->text.emTabsBeforeCursor = emTabsBeforeCursor - 1;
3521 return True;
3525 ** Select the word or whitespace adjacent to the cursor, and move the cursor
3526 ** to its end. pointerX is used as a tie-breaker, when the cursor is at the
3527 ** boundary between a word and some white-space. If the cursor is on the
3528 ** left, the word or space on the left is used. If it's on the right, that
3529 ** is used instead.
3531 static void selectWord(Widget w, int pointerX)
3533 TextWidget tw = (TextWidget)w;
3534 textBuffer *buf = tw->text.textD->buffer;
3535 int x, y, insertPos = TextDGetInsertPosition(tw->text.textD);
3537 TextPosToXY(w, insertPos, &x, &y);
3538 if (pointerX < x && insertPos > 0 && BufGetCharacter(buf, insertPos-1) != '\n')
3539 insertPos--;
3540 BufSelect(buf, startOfWord(tw, insertPos), endOfWord(tw, insertPos));
3543 static int startOfWord(TextWidget w, int pos)
3545 int startPos;
3546 textBuffer *buf = w->text.textD->buffer;
3547 char *delimiters=w->text.delimiters;
3548 char c = BufGetCharacter(buf, pos);
3550 if (c == ' ' || c== '\t') {
3551 if (!spanBackward(buf, pos, " \t", False, &startPos))
3552 return 0;
3553 } else if (strchr(delimiters, c)) {
3554 if (!spanBackward(buf, pos, delimiters, True, &startPos))
3555 return 0;
3556 } else {
3557 if (!BufSearchBackward(buf, pos, delimiters, &startPos))
3558 return 0;
3560 return min(pos, startPos+1);
3564 static int endOfWord(TextWidget w, int pos)
3566 int endPos;
3567 textBuffer *buf = w->text.textD->buffer;
3568 char *delimiters=w->text.delimiters;
3569 char c = BufGetCharacter(buf, pos);
3571 if (c == ' ' || c== '\t') {
3572 if (!spanForward(buf, pos, " \t", False, &endPos))
3573 return buf->length;
3574 } else if (strchr(delimiters, c)) {
3575 if (!spanForward(buf, pos, delimiters, True, &endPos))
3576 return buf->length;
3577 } else {
3578 if (!BufSearchForward(buf, pos, delimiters, &endPos))
3579 return buf->length;
3581 return endPos;
3585 ** Search forwards in buffer "buf" for the first character NOT in
3586 ** "searchChars", starting with the character "startPos", and returning the
3587 ** result in "foundPos" returns True if found, False if not. If ignoreSpace
3588 ** is set, then Space, Tab, and Newlines are ignored in searchChars.
3590 static int spanForward(textBuffer *buf, int startPos, char *searchChars,
3591 int ignoreSpace, int *foundPos)
3593 int pos;
3594 char *c;
3596 pos = startPos;
3597 while (pos < buf->length) {
3598 for (c=searchChars; *c!='\0'; c++)
3599 if(!(ignoreSpace && (*c==' ' || *c=='\t' || *c=='\n')))
3600 if (BufGetCharacter(buf, pos) == *c)
3601 break;
3602 if(*c == 0) {
3603 *foundPos = pos;
3604 return True;
3606 pos++;
3608 *foundPos = buf->length;
3609 return False;
3613 ** Search backwards in buffer "buf" for the first character NOT in
3614 ** "searchChars", starting with the character BEFORE "startPos", returning the
3615 ** result in "foundPos" returns True if found, False if not. If ignoreSpace is
3616 ** set, then Space, Tab, and Newlines are ignored in searchChars.
3618 static int spanBackward(textBuffer *buf, int startPos, char *searchChars, int
3619 ignoreSpace, int *foundPos)
3621 int pos;
3622 char *c;
3624 if (startPos == 0) {
3625 *foundPos = 0;
3626 return False;
3628 pos = startPos == 0 ? 0 : startPos - 1;
3629 while (pos >= 0) {
3630 for (c=searchChars; *c!='\0'; c++)
3631 if(!(ignoreSpace && (*c==' ' || *c=='\t' || *c=='\n')))
3632 if (BufGetCharacter(buf, pos) == *c)
3633 break;
3634 if(*c == 0) {
3635 *foundPos = pos;
3636 return True;
3638 pos--;
3640 *foundPos = 0;
3641 return False;
3645 ** Select the line containing the cursor, including the terminating newline,
3646 ** and move the cursor to its end.
3648 static void selectLine(Widget w)
3650 textDisp *textD = ((TextWidget)w)->text.textD;
3651 int insertPos = TextDGetInsertPosition(textD);
3652 int endPos, startPos;
3654 endPos = BufEndOfLine(textD->buffer, insertPos);
3655 startPos = BufStartOfLine(textD->buffer, insertPos);
3656 BufSelect(textD->buffer, startPos, min(endPos + 1, textD->buffer->length));
3657 TextDSetInsertPosition(textD, endPos);
3661 ** Given a new mouse pointer location, pass the position on to the
3662 ** autoscroll timer routine, and make sure the timer is on when it's
3663 ** needed and off when it's not.
3665 static void checkAutoScroll(TextWidget w, int x, int y)
3667 int inWindow;
3669 /* Is the pointer in or out of the window? */
3670 inWindow = x >= w->text.textD->left &&
3671 x < w->core.width - w->text.marginWidth &&
3672 y >= w->text.marginHeight &&
3673 y < w->core.height - w->text.marginHeight;
3675 /* If it's in the window, cancel the timer procedure */
3676 if (inWindow) {
3677 if (w->text.autoScrollProcID != 0)
3678 XtRemoveTimeOut(w->text.autoScrollProcID);;
3679 w->text.autoScrollProcID = 0;
3680 return;
3683 /* If the timer is not already started, start it */
3684 if (w->text.autoScrollProcID == 0) {
3685 w->text.autoScrollProcID = XtAppAddTimeOut(
3686 XtWidgetToApplicationContext((Widget)w),
3687 0, autoScrollTimerProc, w);
3690 /* Pass on the newest mouse location to the autoscroll routine */
3691 w->text.mouseX = x;
3692 w->text.mouseY = y;
3696 ** Reset drag state and cancel the auto-scroll timer
3698 static void endDrag(Widget w)
3700 if (((TextWidget)w)->text.autoScrollProcID != 0)
3701 XtRemoveTimeOut(((TextWidget)w)->text.autoScrollProcID);
3702 ((TextWidget)w)->text.autoScrollProcID = 0;
3703 if (((TextWidget)w)->text.dragState == MOUSE_PAN)
3704 XUngrabPointer(XtDisplay(w), CurrentTime);
3705 ((TextWidget)w)->text.dragState = NOT_CLICKED;
3709 ** Cancel any drag operation that might be in progress. Should be included
3710 ** in nearly every key event to cleanly end any dragging before edits are made
3711 ** which might change the insert position or the content of the buffer during
3712 ** a drag operation)
3714 static void cancelDrag(Widget w)
3716 int dragState = ((TextWidget)w)->text.dragState;
3718 if (((TextWidget)w)->text.autoScrollProcID != 0)
3719 XtRemoveTimeOut(((TextWidget)w)->text.autoScrollProcID);
3720 if (dragState == SECONDARY_DRAG || dragState == SECONDARY_RECT_DRAG)
3721 BufSecondaryUnselect(((TextWidget)w)->text.textD->buffer);
3722 if (dragState == PRIMARY_BLOCK_DRAG)
3723 CancelBlockDrag((TextWidget)w);
3724 if (dragState == MOUSE_PAN)
3725 XUngrabPointer(XtDisplay(w), CurrentTime);
3726 if (dragState != NOT_CLICKED)
3727 ((TextWidget)w)->text.dragState = DRAG_CANCELED;
3731 ** Do operations triggered by cursor movement: Call cursor movement callback
3732 ** procedure(s), and cancel marker indicating that the cursor is after one or
3733 ** more just-entered emulated tabs (spaces to be deleted as a unit).
3735 static void callCursorMovementCBs(Widget w, XEvent *event)
3737 ((TextWidget)w)->text.emTabsBeforeCursor = 0;
3738 XtCallCallbacks((Widget)w, textNcursorMovementCallback, (XtPointer)event);
3742 ** Adjust the selection as the mouse is dragged to position: (x, y).
3744 static void adjustSelection(TextWidget tw, int x, int y)
3746 textDisp *textD = tw->text.textD;
3747 textBuffer *buf = textD->buffer;
3748 int row, col, startCol, endCol, startPos, endPos;
3749 int newPos = TextDXYToPosition(textD, x, y);
3751 /* Adjust the selection */
3752 if (tw->text.dragState == PRIMARY_RECT_DRAG) {
3753 TextDXYToUnconstrainedPosition(textD, x, y, &row, &col);
3754 col = TextDOffsetWrappedColumn(textD, row, col);
3755 startCol = min(tw->text.rectAnchor, col);
3756 endCol = max(tw->text.rectAnchor, col);
3757 startPos = BufStartOfLine(buf, min(tw->text.anchor, newPos));
3758 endPos = BufEndOfLine(buf, max(tw->text.anchor, newPos));
3759 BufRectSelect(buf, startPos, endPos, startCol, endCol);
3760 } else if (tw->text.multiClickState == ONE_CLICK) {
3761 startPos = startOfWord(tw, min(tw->text.anchor, newPos));
3762 endPos = endOfWord(tw, max(tw->text.anchor, newPos));
3763 BufSelect(buf, startPos, endPos);
3764 newPos = newPos < tw->text.anchor ? startPos : endPos;
3765 } else if (tw->text.multiClickState == TWO_CLICKS) {
3766 startPos = BufStartOfLine(buf, min(tw->text.anchor, newPos));
3767 endPos = BufEndOfLine(buf, max(tw->text.anchor, newPos));
3768 BufSelect(buf, startPos, min(endPos+1, buf->length));
3769 newPos = newPos < tw->text.anchor ? startPos : endPos;
3770 } else
3771 BufSelect(buf, tw->text.anchor, newPos);
3773 /* Move the cursor */
3774 TextDSetInsertPosition(textD, newPos);
3775 callCursorMovementCBs((Widget)tw, NULL);
3778 static void adjustSecondarySelection(TextWidget tw, int x, int y)
3780 textDisp *textD = tw->text.textD;
3781 textBuffer *buf = textD->buffer;
3782 int row, col, startCol, endCol, startPos, endPos;
3783 int newPos = TextDXYToPosition(textD, x, y);
3785 if (tw->text.dragState == SECONDARY_RECT_DRAG) {
3786 TextDXYToUnconstrainedPosition(textD, x, y, &row, &col);
3787 col = TextDOffsetWrappedColumn(textD, row, col);
3788 startCol = min(tw->text.rectAnchor, col);
3789 endCol = max(tw->text.rectAnchor, col);
3790 startPos = BufStartOfLine(buf, min(tw->text.anchor, newPos));
3791 endPos = BufEndOfLine(buf, max(tw->text.anchor, newPos));
3792 BufSecRectSelect(buf, startPos, endPos, startCol, endCol);
3793 } else
3794 BufSecondarySelect(textD->buffer, tw->text.anchor, newPos);
3798 ** Wrap multi-line text in argument "text" to be inserted at the end of the
3799 ** text on line "startLine" and return the result. If "breakBefore" is
3800 ** non-NULL, allow wrapping to extend back into "startLine", in which case
3801 ** the returned text will include the wrapped part of "startLine", and
3802 ** "breakBefore" will return the number of characters at the end of
3803 ** "startLine" that were absorbed into the returned string. "breakBefore"
3804 ** will return zero if no characters were absorbed into the returned string.
3805 ** The buffer offset of text in the widget's text buffer is needed so that
3806 ** smart indent (which can be triggered by wrapping) can search back farther
3807 ** in the buffer than just the text in startLine.
3809 static char *wrapText(TextWidget tw, char *startLine, char *text, int bufOffset,
3810 int wrapMargin, int *breakBefore)
3812 textBuffer *wrapBuf, *buf = tw->text.textD->buffer;
3813 int startLineLen = strlen(startLine);
3814 int colNum, pos, lineStartPos, limitPos, breakAt, charsAdded;
3815 int firstBreak = -1, tabDist = buf->tabDist;
3816 char c, *wrappedText;
3818 /* Create a temporary text buffer and load it with the strings */
3819 wrapBuf = BufCreate();
3820 BufInsert(wrapBuf, 0, startLine);
3821 BufInsert(wrapBuf, wrapBuf->length, text);
3823 /* Scan the buffer for long lines and apply wrapLine when wrapMargin is
3824 exceeded. limitPos enforces no breaks in the "startLine" part of the
3825 string (if requested), and prevents re-scanning of long unbreakable
3826 lines for each character beyond the margin */
3827 colNum = 0;
3828 pos = 0;
3829 lineStartPos = 0;
3830 limitPos = breakBefore == NULL ? startLineLen : 0;
3831 while (pos < wrapBuf->length) {
3832 c = BufGetCharacter(wrapBuf, pos);
3833 if (c == '\n') {
3834 lineStartPos = limitPos = pos + 1;
3835 colNum = 0;
3836 } else {
3837 colNum += BufCharWidth(c, colNum, tabDist, buf->nullSubsChar);
3838 if (colNum > wrapMargin) {
3839 if (!wrapLine(tw, wrapBuf, bufOffset, lineStartPos, pos,
3840 limitPos, &breakAt, &charsAdded)) {
3841 limitPos = max(pos, limitPos);
3842 } else {
3843 lineStartPos = limitPos = breakAt+1;
3844 pos += charsAdded;
3845 colNum = BufCountDispChars(wrapBuf, lineStartPos, pos+1);
3846 if (firstBreak == -1)
3847 firstBreak = breakAt;
3851 pos++;
3854 /* Return the wrapped text, possibly including part of startLine */
3855 if (breakBefore == NULL)
3856 wrappedText = BufGetRange(wrapBuf, startLineLen, wrapBuf->length);
3857 else {
3858 *breakBefore = firstBreak != -1 && firstBreak < startLineLen ?
3859 startLineLen - firstBreak : 0;
3860 wrappedText = BufGetRange(wrapBuf, startLineLen - *breakBefore,
3861 wrapBuf->length);
3863 BufFree(wrapBuf);
3864 return wrappedText;
3868 ** Wraps the end of a line beginning at lineStartPos and ending at lineEndPos
3869 ** in "buf", at the last white-space on the line >= limitPos. (The implicit
3870 ** assumption is that just the last character of the line exceeds the wrap
3871 ** margin, and anywhere on the line we can wrap is correct). Returns False if
3872 ** unable to wrap the line. "breakAt", returns the character position at
3873 ** which the line was broken,
3875 ** Auto-wrapping can also trigger auto-indent. The additional parameter
3876 ** bufOffset is needed when auto-indent is set to smart indent and the smart
3877 ** indent routines need to scan far back in the buffer. "charsAdded" returns
3878 ** the number of characters added to acheive the auto-indent. wrapMargin is
3879 ** used to decide whether auto-indent should be skipped because the indent
3880 ** string itself would exceed the wrap margin.
3882 static int wrapLine(TextWidget tw, textBuffer *buf, int bufOffset,
3883 int lineStartPos, int lineEndPos, int limitPos, int *breakAt,
3884 int *charsAdded)
3886 int p, length, column;
3887 char c, *indentStr;
3889 /* Scan backward for whitespace or BOL. If BOL, return False, no
3890 whitespace in line at which to wrap */
3891 for (p=lineEndPos; ; p--) {
3892 if (p < lineStartPos || p < limitPos)
3893 return False;
3894 c = BufGetCharacter(buf, p);
3895 if (c == '\t' || c == ' ')
3896 break;
3899 /* Create an auto-indent string to insert to do wrap. If the auto
3900 indent string reaches the wrap position, slice the auto-indent
3901 back off and return to the left margin */
3902 if (tw->text.autoIndent || tw->text.smartIndent) {
3903 indentStr = createIndentString(tw, buf, bufOffset, lineStartPos,
3904 lineEndPos, &length, &column);
3905 if (column >= p-lineStartPos)
3906 indentStr[1] = '\0';
3907 } else {
3908 indentStr = "\n";
3909 length = 1;
3912 /* Replace the whitespace character with the auto-indent string
3913 and return the stats */
3914 BufReplace(buf, p, p+1, indentStr);
3915 if (tw->text.autoIndent || tw->text.smartIndent)
3916 XtFree(indentStr);
3917 *breakAt = p;
3918 *charsAdded = length-1;
3919 return True;
3923 ** Create and return an auto-indent string to add a newline at lineEndPos to a
3924 ** line starting at lineStartPos in buf. "buf" may or may not be the real
3925 ** text buffer for the widget. If it is not the widget's text buffer it's
3926 ** offset position from the real buffer must be specified in "bufOffset" to
3927 ** allow the smart-indent routines to scan back as far as necessary. The
3928 ** string length is returned in "length" (or "length" can be passed as NULL,
3929 ** and the indent column is returned in "column" (if non NULL).
3931 static char *createIndentString(TextWidget tw, textBuffer *buf, int bufOffset,
3932 int lineStartPos, int lineEndPos, int *length, int *column)
3934 textDisp *textD = tw->text.textD;
3935 int pos, indent = -1, tabDist = textD->buffer->tabDist;
3936 int i, useTabs = textD->buffer->useTabs;
3937 char *indentPtr, *indentStr, c;
3938 smartIndentCBStruct smartIndent;
3940 /* If smart indent is on, call the smart indent callback. It is not
3941 called when multi-line changes are being made (lineStartPos != 0),
3942 because smart indent needs to search back an indeterminate distance
3943 through the buffer, and reconciling that with wrapping changes made,
3944 but not yet committed in the buffer, would make programming smart
3945 indent more difficult for users and make everything more complicated */
3946 if (tw->text.smartIndent && (lineStartPos == 0 || buf == textD->buffer)) {
3947 smartIndent.reason = NEWLINE_INDENT_NEEDED;
3948 smartIndent.pos = lineEndPos + bufOffset;
3949 smartIndent.indentRequest = 0;
3950 smartIndent.charsTyped = NULL;
3951 XtCallCallbacks((Widget)tw, textNsmartIndentCallback,
3952 (XtPointer)&smartIndent);
3953 indent = smartIndent.indentRequest;
3956 /* If smart indent wasn't used, measure the indent distance of the line */
3957 if (indent == -1) {
3958 indent = 0;
3959 for (pos=lineStartPos; pos<lineEndPos; pos++) {
3960 c = BufGetCharacter(buf, pos);
3961 if (c != ' ' && c != '\t')
3962 break;
3963 if (c == '\t')
3964 indent += tabDist - (indent % tabDist);
3965 else
3966 indent++;
3970 /* Allocate and create a string of tabs and spaces to achieve the indent */
3971 indentPtr = indentStr = XtMalloc(indent + 2);
3972 *indentPtr++ = '\n';
3973 if (useTabs) {
3974 for (i=0; i < indent / tabDist; i++)
3975 *indentPtr++ = '\t';
3976 for (i=0; i < indent % tabDist; i++)
3977 *indentPtr++ = ' ';
3978 } else {
3979 for (i=0; i < indent; i++)
3980 *indentPtr++ = ' ';
3982 *indentPtr = '\0';
3984 /* Return any requested stats */
3985 if (length != NULL)
3986 *length = indentPtr - indentStr;
3987 if (column != NULL)
3988 *column = indent;
3990 return indentStr;
3994 ** Xt timer procedure for autoscrolling
3996 static void autoScrollTimerProc(XtPointer clientData, XtIntervalId *id)
3998 TextWidget w = (TextWidget)clientData;
3999 textDisp *textD = w->text.textD;
4000 int topLineNum, horizOffset, newPos, cursorX, y;
4001 int fontWidth = textD->fontStruct->max_bounds.width;
4002 int fontHeight = textD->fontStruct->ascent + textD->fontStruct->descent;
4004 /* For vertical autoscrolling just dragging the mouse outside of the top
4005 or bottom of the window is sufficient, for horizontal (non-rectangular)
4006 scrolling, see if the position where the CURSOR would go is outside */
4007 newPos = TextDXYToPosition(textD, w->text.mouseX, w->text.mouseY);
4008 if (w->text.dragState == PRIMARY_RECT_DRAG)
4009 cursorX = w->text.mouseX;
4010 else if (!TextDPositionToXY(textD, newPos, &cursorX, &y))
4011 cursorX = w->text.mouseX;
4013 /* Scroll away from the pointer, 1 character (horizontal), or 1 character
4014 for each fontHeight distance from the mouse to the text (vertical) */
4015 TextDGetScroll(textD, &topLineNum, &horizOffset);
4016 if (cursorX >= (int)w->core.width - w->text.marginWidth)
4017 horizOffset += fontWidth;
4018 else if (w->text.mouseX < textD->left)
4019 horizOffset -= fontWidth;
4020 if (w->text.mouseY >= (int)w->core.height - w->text.marginHeight)
4021 topLineNum += 1 + ((w->text.mouseY - (int)w->core.height -
4022 w->text.marginHeight) / fontHeight) + 1;
4023 else if (w->text.mouseY < w->text.marginHeight)
4024 topLineNum -= 1 + ((w->text.marginHeight-w->text.mouseY) / fontHeight);
4025 TextDSetScroll(textD, topLineNum, horizOffset);
4027 /* Continue the drag operation in progress. If none is in progress
4028 (safety check) don't continue to re-establish the timer proc */
4029 if (w->text.dragState == PRIMARY_DRAG) {
4030 adjustSelection(w, w->text.mouseX, w->text.mouseY);
4031 } else if (w->text.dragState == PRIMARY_RECT_DRAG) {
4032 adjustSelection(w, w->text.mouseX, w->text.mouseY);
4033 } else if (w->text.dragState == SECONDARY_DRAG) {
4034 adjustSecondarySelection(w, w->text.mouseX, w->text.mouseY);
4035 } else if (w->text.dragState == SECONDARY_RECT_DRAG) {
4036 adjustSecondarySelection(w, w->text.mouseX, w->text.mouseY);
4037 } else if (w->text.dragState == PRIMARY_BLOCK_DRAG) {
4038 BlockDragSelection(w, w->text.mouseX, w->text.mouseY, USE_LAST);
4039 } else {
4040 w->text.autoScrollProcID = 0;
4041 return;
4044 /* re-establish the timer proc (this routine) to continue processing */
4045 w->text.autoScrollProcID = XtAppAddTimeOut(
4046 XtWidgetToApplicationContext((Widget)w),
4047 w->text.mouseY >= w->text.marginHeight &&
4048 w->text.mouseY < w->core.height - w->text.marginHeight ?
4049 (VERTICAL_SCROLL_DELAY*fontWidth) / fontHeight :
4050 VERTICAL_SCROLL_DELAY, autoScrollTimerProc, w);
4054 ** Xt timer procedure for cursor blinking
4056 static void cursorBlinkTimerProc(XtPointer clientData, XtIntervalId *id)
4058 TextWidget w = (TextWidget)clientData;
4059 textDisp *textD = w->text.textD;
4061 /* Blink the cursor */
4062 if (textD->cursorOn)
4063 TextDBlankCursor(textD);
4064 else
4065 TextDUnblankCursor(textD);
4067 /* re-establish the timer proc (this routine) to continue processing */
4068 w->text.cursorBlinkProcID = XtAppAddTimeOut(
4069 XtWidgetToApplicationContext((Widget)w),
4070 w->text.cursorBlinkRate, cursorBlinkTimerProc, w);
4074 ** look at an action procedure's arguments to see if argument "key" has been
4075 ** specified in the argument list
4077 static int hasKey(const char *key, const String *args, const Cardinal *nArgs)
4079 int i;
4081 for (i=0; i<(int)*nArgs; i++)
4082 if (!strCaseCmp(args[i], key))
4083 return True;
4084 return False;
4087 static int max(int i1, int i2)
4089 return i1 >= i2 ? i1 : i2;
4092 static int min(int i1, int i2)
4094 return i1 <= i2 ? i1 : i2;
4097 const char *GetDefaultTranslations(void)
4099 return defaultTranslations;
4102 ** strCaseCmp compares its arguments and returns 0 if the two strings
4103 ** are equal IGNORING case differences. Otherwise returns 1.
4106 static int strCaseCmp(const char *str1, const char *str2)
4108 unsigned const char *c1 = (unsigned const char*) str1;
4109 unsigned const char *c2 = (unsigned const char*) str2;
4111 for (; *c1!='\0' && *c2!='\0'; c1++, c2++)
4112 if (toupper(*c1) != toupper(*c2))
4113 return 1;
4114 if (*c1 == *c2) {
4115 return(0);
4117 else {
4118 return(1);
4122 static void ringIfNecessary(Boolean silent, Widget w)
4124 if (!silent)
4125 XBell(XtDisplay(w), 0);