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