Propogate colors to new windows on opening (744294)
[nedit.git] / source / text.c
blobc1f321c47c1475c58613d993baa85f62f49a441d
1 static const char CVSID[] = "$Id: text.c,v 1.43 2003/05/16 16:47:20 slobasso 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 "Ctrl<KeyPress>Tab: self_insert()\n"
387 "<KeyPress>Tab: process_tab()\n"
388 "Alt Shift Ctrl<KeyPress>space: key_select(\"rect\")\n"
389 "Meta Shift Ctrl<KeyPress>space: key_select(\"rect\")\n"
390 "Shift Ctrl~Meta~Alt<KeyPress>space: key_select()\n"
391 "Ctrl~Meta~Alt<KeyPress>slash: select_all()\n"
392 "Ctrl~Meta~Alt<KeyPress>backslash: deselect_all()\n"
393 "<KeyPress>: self_insert()\n"
394 "Alt Ctrl<Btn1Down>: move_destination()\n"
395 "Meta Ctrl<Btn1Down>: move_destination()\n"
396 "Shift Ctrl<Btn1Down>: extend_start(\"rect\")\n"
397 "Shift<Btn1Down>: extend_start()\n"
398 "<Btn1Down>: grab_focus()\n"
399 "Button1 Ctrl<MotionNotify>: extend_adjust(\"rect\")\n"
400 "Button1~Ctrl<MotionNotify>: extend_adjust()\n"
401 "<Btn1Up>: extend_end()\n"
402 "<Btn2Down>: secondary_or_drag_start()\n"
403 "Shift Ctrl Button2<MotionNotify>: secondary_or_drag_adjust(\"rect\", \"copy\", \"overlay\")\n"
404 "Shift Button2<MotionNotify>: secondary_or_drag_adjust(\"copy\")\n"
405 "Ctrl Button2<MotionNotify>: secondary_or_drag_adjust(\"rect\", \"overlay\")\n"
406 "Button2<MotionNotify>: secondary_or_drag_adjust()\n"
407 "Shift Ctrl<Btn2Up>: move_to_or_end_drag(\"copy\", \"overlay\")\n"
408 "Shift <Btn2Up>: move_to_or_end_drag(\"copy\")\n"
409 "Alt<Btn2Up>: exchange()\n"
410 "Meta<Btn2Up>: exchange()\n"
411 "Ctrl<Btn2Up>: copy_to_or_end_drag(\"overlay\")\n"
412 "<Btn2Up>: copy_to_or_end_drag()\n"
413 "Ctrl~Meta~Alt<Btn3Down>: mouse_pan()\n"
414 "Ctrl~Meta~Alt Button3<MotionNotify>: mouse_pan()\n"
415 "<Btn3Up>: end_drag()\n"
416 "<FocusIn>: focusIn()\n"
417 "<FocusOut>: focusOut()\n"
418 /* Support for mouse wheel in XFree86 */
419 "Shift<Btn4Down>,<Btn4Up>: scroll_up(1)\n"
420 "Shift<Btn5Down>,<Btn5Up>: scroll_down(1)\n"
421 "Ctrl<Btn4Down>,<Btn4Up>: previous_page()\n"
422 "Ctrl<Btn5Down>,<Btn5Up>: next_page(1)\n"
423 "<Btn4Down>,<Btn4Up>: scroll_up(5)\n"
424 "<Btn5Down>,<Btn5Up>: scroll_down(5)\n";
425 /* some of the translations from the Motif text widget were not picked up:
426 :<KeyPress>osfSelect: set-anchor()\n\
427 :<KeyPress>osfActivate: activate()\n\
428 ~Shift Ctrl~Meta~Alt<KeyPress>Return: activate()\n\
429 ~Shift Ctrl~Meta~Alt<KeyPress>space: set-anchor()\n\
430 :<KeyPress>osfClear: clear-selection()\n\
431 ~Shift~Ctrl~Meta~Alt<KeyPress>Return: process-return()\n\
432 Shift~Meta~Alt<KeyPress>Tab: prev-tab-group()\n\
433 Ctrl~Meta~Alt<KeyPress>Tab: next-tab-group()\n\
434 <UnmapNotify>: unmap()\n\
435 <EnterNotify>: enter()\n\
436 <LeaveNotify>: leave()\n
440 static XtActionsRec actionsList[] = {
441 {"self-insert", selfInsertAP},
442 {"self_insert", selfInsertAP},
443 {"grab-focus", grabFocusAP},
444 {"grab_focus", grabFocusAP},
445 {"extend-adjust", extendAdjustAP},
446 {"extend_adjust", extendAdjustAP},
447 {"extend-start", extendStartAP},
448 {"extend_start", extendStartAP},
449 {"extend-end", extendEndAP},
450 {"extend_end", extendEndAP},
451 {"secondary-adjust", secondaryAdjustAP},
452 {"secondary_adjust", secondaryAdjustAP},
453 {"secondary-or-drag-adjust", secondaryOrDragAdjustAP},
454 {"secondary_or_drag_adjust", secondaryOrDragAdjustAP},
455 {"secondary-start", secondaryStartAP},
456 {"secondary_start", secondaryStartAP},
457 {"secondary-or-drag-start", secondaryOrDragStartAP},
458 {"secondary_or_drag_start", secondaryOrDragStartAP},
459 {"process-bdrag", secondaryOrDragStartAP},
460 {"process_bdrag", secondaryOrDragStartAP},
461 {"move-destination", moveDestinationAP},
462 {"move_destination", moveDestinationAP},
463 {"move-to", moveToAP},
464 {"move_to", moveToAP},
465 {"move-to-or-end-drag", moveToOrEndDragAP},
466 {"move_to_or_end_drag", moveToOrEndDragAP},
467 {"end_drag", endDragAP},
468 {"copy-to", copyToAP},
469 {"copy_to", copyToAP},
470 {"copy-to-or-end-drag", copyToOrEndDragAP},
471 {"copy_to_or_end_drag", copyToOrEndDragAP},
472 {"exchange", exchangeAP},
473 {"process-cancel", processCancelAP},
474 {"process_cancel", processCancelAP},
475 {"paste-clipboard", pasteClipboardAP},
476 {"paste_clipboard", pasteClipboardAP},
477 {"copy-clipboard", copyClipboardAP},
478 {"copy_clipboard", copyClipboardAP},
479 {"cut-clipboard", cutClipboardAP},
480 {"cut_clipboard", cutClipboardAP},
481 {"copy-primary", copyPrimaryAP},
482 {"copy_primary", copyPrimaryAP},
483 {"cut-primary", cutPrimaryAP},
484 {"cut_primary", cutPrimaryAP},
485 {"newline", newlineAP},
486 {"newline-and-indent", newlineAndIndentAP},
487 {"newline_and_indent", newlineAndIndentAP},
488 {"newline-no-indent", newlineNoIndentAP},
489 {"newline_no_indent", newlineNoIndentAP},
490 {"delete-selection", deleteSelectionAP},
491 {"delete_selection", deleteSelectionAP},
492 {"delete-previous-character", deletePreviousCharacterAP},
493 {"delete_previous_character", deletePreviousCharacterAP},
494 {"delete-next-character", deleteNextCharacterAP},
495 {"delete_next_character", deleteNextCharacterAP},
496 {"delete-previous-word", deletePreviousWordAP},
497 {"delete_previous_word", deletePreviousWordAP},
498 {"delete-next-word", deleteNextWordAP},
499 {"delete_next_word", deleteNextWordAP},
500 {"delete-to-start-of-line", deleteToStartOfLineAP},
501 {"delete_to_start_of_line", deleteToStartOfLineAP},
502 {"delete-to-end-of-line", deleteToEndOfLineAP},
503 {"delete_to_end_of_line", deleteToEndOfLineAP},
504 {"forward-character", forwardCharacterAP},
505 {"forward_character", forwardCharacterAP},
506 {"backward-character", backwardCharacterAP},
507 {"backward_character", backwardCharacterAP},
508 {"key-select", keySelectAP},
509 {"key_select", keySelectAP},
510 {"process-up", processUpAP},
511 {"process_up", processUpAP},
512 {"process-down", processDownAP},
513 {"process_down", processDownAP},
514 {"process-shift-up", processShiftUpAP},
515 {"process_shift_up", processShiftUpAP},
516 {"process-shift-down", processShiftDownAP},
517 {"process_shift_down", processShiftDownAP},
518 {"process-home", beginningOfLineAP},
519 {"process_home", beginningOfLineAP},
520 {"forward-word", forwardWordAP},
521 {"forward_word", forwardWordAP},
522 {"backward-word", backwardWordAP},
523 {"backward_word", backwardWordAP},
524 {"forward-paragraph", forwardParagraphAP},
525 {"forward_paragraph", forwardParagraphAP},
526 {"backward-paragraph", backwardParagraphAP},
527 {"backward_paragraph", backwardParagraphAP},
528 {"beginning-of-line", beginningOfLineAP},
529 {"beginning_of_line", beginningOfLineAP},
530 {"end-of-line", endOfLineAP},
531 {"end_of_line", endOfLineAP},
532 {"beginning-of-file", beginningOfFileAP},
533 {"beginning_of_file", beginningOfFileAP},
534 {"end-of-file", endOfFileAP},
535 {"end_of_file", endOfFileAP},
536 {"next-page", nextPageAP},
537 {"next_page", nextPageAP},
538 {"previous-page", previousPageAP},
539 {"previous_page", previousPageAP},
540 {"page-left", pageLeftAP},
541 {"page_left", pageLeftAP},
542 {"page-right", pageRightAP},
543 {"page_right", pageRightAP},
544 {"toggle-overstrike", toggleOverstrikeAP},
545 {"toggle_overstrike", toggleOverstrikeAP},
546 {"scroll-up", scrollUpAP},
547 {"scroll_up", scrollUpAP},
548 {"scroll-down", scrollDownAP},
549 {"scroll_down", scrollDownAP},
550 {"scroll_left", scrollLeftAP},
551 {"scroll_right", scrollRightAP},
552 {"scroll-to-line", scrollToLineAP},
553 {"scroll_to_line", scrollToLineAP},
554 {"select-all", selectAllAP},
555 {"select_all", selectAllAP},
556 {"deselect-all", deselectAllAP},
557 {"deselect_all", deselectAllAP},
558 {"focusIn", focusInAP},
559 {"focusOut", focusOutAP},
560 {"process-return", selfInsertAP},
561 {"process_return", selfInsertAP},
562 {"process-tab", processTabAP},
563 {"process_tab", processTabAP},
564 {"insert-string", insertStringAP},
565 {"insert_string", insertStringAP},
566 {"mouse_pan", mousePanAP},
569 /* The motif text widget defined a bunch of actions which the nedit text
570 widget as-of-yet does not support:
572 Actions which were not bound to keys (for emacs emulation, some of
573 them should probably be supported:
575 kill-next-character()
576 kill-next-word()
577 kill-previous-character()
578 kill-previous-word()
579 kill-selection()
580 kill-to-end-of-line()
581 kill-to-start-of-line()
582 unkill()
583 next-line()
584 newline-and-backup()
585 beep()
586 redraw-display()
587 scroll-one-line-down()
588 scroll-one-line-up()
589 set-insertion-point()
591 Actions which are not particularly useful:
593 set-anchor()
594 activate()
595 clear-selection() -> this is a wierd one
596 do-quick-action() -> don't think this ever worked
597 Help()
598 next-tab-group()
599 select-adjust()
600 select-start()
601 select-end()
604 static XtResource resources[] = {
605 {XmNhighlightThickness, XmCHighlightThickness, XmRDimension,
606 sizeof(Dimension), XtOffset(TextWidget, primitive.highlight_thickness),
607 XmRInt, 0},
608 {XmNshadowThickness, XmCShadowThickness, XmRDimension, sizeof(Dimension),
609 XtOffset(TextWidget, primitive.shadow_thickness), XmRInt, 0},
610 {textNfont, textCFont, XmRFontStruct, sizeof(XFontStruct *),
611 XtOffset(TextWidget, text.fontStruct), XmRString, "fixed"},
612 {textNselectForeground, textCSelectForeground, XmRPixel, sizeof(Pixel),
613 XtOffset(TextWidget, text.selectFGPixel), XmRString,
614 NEDIT_DEFAULT_SEL_FG},
615 {textNselectBackground, textCSelectBackground, XmRPixel, sizeof(Pixel),
616 XtOffset(TextWidget, text.selectBGPixel), XmRString,
617 NEDIT_DEFAULT_SEL_BG},
618 {textNhighlightForeground, textCHighlightForeground, XmRPixel,sizeof(Pixel),
619 XtOffset(TextWidget, text.highlightFGPixel), XmRString,
620 NEDIT_DEFAULT_HI_FG},
621 {textNhighlightBackground, textCHighlightBackground, XmRPixel,sizeof(Pixel),
622 XtOffset(TextWidget, text.highlightBGPixel), XmRString,
623 NEDIT_DEFAULT_HI_BG},
624 {textNlineNumForeground, textCLineNumForeground, XmRPixel,sizeof(Pixel),
625 XtOffset(TextWidget, text.lineNumFGPixel), XmRString,
626 NEDIT_DEFAULT_LINENO_FG},
627 {textNcursorForeground, textCCursorForeground, XmRPixel,sizeof(Pixel),
628 XtOffset(TextWidget, text.cursorFGPixel), XmRString,
629 NEDIT_DEFAULT_CURSOR_FG},
630 {textNcalltipForeground, textCcalltipForeground, XmRPixel,sizeof(Pixel),
631 XtOffset(TextWidget, text.calltipFGPixel), XmRString,
632 NEDIT_DEFAULT_CALLTIP_FG},
633 {textNcalltipBackground, textCcalltipBackground, XmRPixel,sizeof(Pixel),
634 XtOffset(TextWidget, text.calltipBGPixel), XmRString,
635 NEDIT_DEFAULT_CALLTIP_BG},
636 {textNbacklightCharTypes,textCBacklightCharTypes,XmRString,sizeof(XmString),
637 XtOffset(TextWidget, text.backlightCharTypes), XmRString, NULL},
638 {textNrows, textCRows, XmRInt,sizeof(int),
639 XtOffset(TextWidget, text.rows), XmRString, "24"},
640 {textNcolumns, textCColumns, XmRInt, sizeof(int),
641 XtOffset(TextWidget, text.columns), XmRString, "80"},
642 {textNmarginWidth, textCMarginWidth, XmRInt, sizeof(int),
643 XtOffset(TextWidget, text.marginWidth), XmRString, "5"},
644 {textNmarginHeight, textCMarginHeight, XmRInt, sizeof(int),
645 XtOffset(TextWidget, text.marginHeight), XmRString, "5"},
646 {textNpendingDelete, textCPendingDelete, XmRBoolean, sizeof(Boolean),
647 XtOffset(TextWidget, text.pendingDelete), XmRString, "True"},
648 {textNautoWrap, textCAutoWrap, XmRBoolean, sizeof(Boolean),
649 XtOffset(TextWidget, text.autoWrap), XmRString, "True"},
650 {textNcontinuousWrap, textCContinuousWrap, XmRBoolean, sizeof(Boolean),
651 XtOffset(TextWidget, text.continuousWrap), XmRString, "True"},
652 {textNautoIndent, textCAutoIndent, XmRBoolean, sizeof(Boolean),
653 XtOffset(TextWidget, text.autoIndent), XmRString, "True"},
654 {textNsmartIndent, textCSmartIndent, XmRBoolean, sizeof(Boolean),
655 XtOffset(TextWidget, text.smartIndent), XmRString, "False"},
656 {textNoverstrike, textCOverstrike, XmRBoolean, sizeof(Boolean),
657 XtOffset(TextWidget, text.overstrike), XmRString, "False"},
658 {textNheavyCursor, textCHeavyCursor, XmRBoolean, sizeof(Boolean),
659 XtOffset(TextWidget, text.heavyCursor), XmRString, "False"},
660 {textNreadOnly, textCReadOnly, XmRBoolean, sizeof(Boolean),
661 XtOffset(TextWidget, text.readOnly), XmRString, "False"},
662 {textNhidePointer, textCHidePointer, XmRBoolean, sizeof(Boolean),
663 XtOffset(TextWidget, text.hidePointer), XmRString, "False"},
664 {textNwrapMargin, textCWrapMargin, XmRInt, sizeof(int),
665 XtOffset(TextWidget, text.wrapMargin), XmRString, "0"},
666 {textNhScrollBar, textCHScrollBar, XmRWidget, sizeof(Widget),
667 XtOffset(TextWidget, text.hScrollBar), XmRString, ""},
668 {textNvScrollBar, textCVScrollBar, XmRWidget, sizeof(Widget),
669 XtOffset(TextWidget, text.vScrollBar), XmRString, ""},
670 {textNlineNumCols, textCLineNumCols, XmRInt, sizeof(int),
671 XtOffset(TextWidget, text.lineNumCols), XmRString, "0"},
672 {textNautoShowInsertPos, textCAutoShowInsertPos, XmRBoolean,
673 sizeof(Boolean), XtOffset(TextWidget, text.autoShowInsertPos),
674 XmRString, "True"},
675 {textNautoWrapPastedText, textCAutoWrapPastedText, XmRBoolean,
676 sizeof(Boolean), XtOffset(TextWidget, text.autoWrapPastedText),
677 XmRString, "False"},
678 {textNwordDelimiters, textCWordDelimiters, XmRString, sizeof(char *),
679 XtOffset(TextWidget, text.delimiters), XmRString,
680 ".,/\\`'!@#%^&*()-=+{}[]\":;<>?"},
681 {textNblinkRate, textCBlinkRate, XmRInt, sizeof(int),
682 XtOffset(TextWidget, text.cursorBlinkRate), XmRString, "500"},
683 {textNemulateTabs, textCEmulateTabs, XmRInt, sizeof(int),
684 XtOffset(TextWidget, text.emulateTabs), XmRString, "0"},
685 {textNfocusCallback, textCFocusCallback, XmRCallback, sizeof(caddr_t),
686 XtOffset(TextWidget, text.focusInCB), XtRCallback, NULL},
687 {textNlosingFocusCallback, textCLosingFocusCallback, XmRCallback,
688 sizeof(caddr_t), XtOffset(TextWidget, text.focusOutCB), XtRCallback,NULL},
689 {textNcursorMovementCallback, textCCursorMovementCallback, XmRCallback,
690 sizeof(caddr_t), XtOffset(TextWidget, text.cursorCB), XtRCallback, NULL},
691 {textNdragStartCallback, textCDragStartCallback, XmRCallback,
692 sizeof(caddr_t), XtOffset(TextWidget, text.dragStartCB), XtRCallback,
693 NULL},
694 {textNdragEndCallback, textCDragEndCallback, XmRCallback,
695 sizeof(caddr_t), XtOffset(TextWidget, text.dragEndCB), XtRCallback, NULL},
696 {textNsmartIndentCallback, textCSmartIndentCallback, XmRCallback,
697 sizeof(caddr_t), XtOffset(TextWidget, text.smartIndentCB), XtRCallback,
698 NULL},
699 {textNcursorVPadding, textCCursorVPadding, XtRCardinal, sizeof(Cardinal),
700 XtOffset(TextWidget, text.cursorVPadding), XmRString, "0"}
703 static TextClassRec textClassRec = {
704 /* CoreClassPart */
706 (WidgetClass) &xmPrimitiveClassRec, /* superclass */
707 "Text", /* class_name */
708 sizeof(TextRec), /* widget_size */
709 NULL, /* class_initialize */
710 NULL, /* class_part_initialize */
711 FALSE, /* class_inited */
712 (XtInitProc)initialize, /* initialize */
713 NULL, /* initialize_hook */
714 realize, /* realize */
715 actionsList, /* actions */
716 XtNumber(actionsList), /* num_actions */
717 resources, /* resources */
718 XtNumber(resources), /* num_resources */
719 NULLQUARK, /* xrm_class */
720 TRUE, /* compress_motion */
721 TRUE, /* compress_exposure */
722 TRUE, /* compress_enterleave */
723 FALSE, /* visible_interest */
724 (XtWidgetProc)destroy, /* destroy */
725 (XtWidgetProc)resize, /* resize */
726 (XtExposeProc)redisplay, /* expose */
727 (XtSetValuesFunc)setValues, /* set_values */
728 NULL, /* set_values_hook */
729 XtInheritSetValuesAlmost, /* set_values_almost */
730 NULL, /* get_values_hook */
731 NULL, /* accept_focus */
732 XtVersion, /* version */
733 NULL, /* callback private */
734 defaultTranslations, /* tm_table */
735 queryGeometry, /* query_geometry */
736 NULL, /* display_accelerator */
737 NULL, /* extension */
739 /* Motif primitive class fields */
741 (XtWidgetProc)_XtInherit, /* Primitive border_highlight */
742 (XtWidgetProc)_XtInherit, /* Primitive border_unhighlight */
743 NULL, /*XtInheritTranslations,*/ /* translations */
744 NULL, /* arm_and_activate */
745 NULL, /* get resources */
746 0, /* num get_resources */
747 NULL, /* extension */
749 /* Text class part */
751 0, /* ignored */
755 WidgetClass textWidgetClass = (WidgetClass)&textClassRec;
756 #define NEDIT_HIDE_CURSOR_MASK (KeyPressMask)
757 #define NEDIT_SHOW_CURSOR_MASK (FocusChangeMask | PointerMotionMask | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask)
758 static char empty_bits[] = {0x00, 0x00, 0x00, 0x00};
759 static Cursor empty_cursor = 0;
762 ** Widget initialize method
764 static void initialize(TextWidget request, TextWidget new)
766 XFontStruct *fs = new->text.fontStruct;
767 char *delimiters;
768 textBuffer *buf;
769 Pixel white, black;
770 int textLeft;
771 int charWidth = fs->max_bounds.width;
772 int marginWidth = new->text.marginWidth;
773 int lineNumCols = new->text.lineNumCols;
775 /* Set the initial window size based on the rows and columns resources */
776 if (request->core.width == 0)
777 new->core.width = charWidth * new->text.columns + marginWidth*2 +
778 (lineNumCols == 0 ? 0 : marginWidth + charWidth * lineNumCols);
779 if (request->core.height == 0)
780 new->core.height = (fs->ascent + fs->descent) * new->text.rows +
781 new->text.marginHeight * 2;
783 /* The default colors work for B&W as well as color, except for
784 selectFGPixel and selectBGPixel, where color highlighting looks
785 much better without reverse video, so if we get here, and the
786 selection is totally unreadable because of the bad default colors,
787 swap the colors and make the selection reverse video */
788 white = WhitePixelOfScreen(XtScreen((Widget)new));
789 black = BlackPixelOfScreen(XtScreen((Widget)new));
790 if ( new->text.selectBGPixel == white &&
791 new->core.background_pixel == white &&
792 new->text.selectFGPixel == black &&
793 new->primitive.foreground == black) {
794 new->text.selectBGPixel = black;
795 new->text.selectFGPixel = white;
798 /* Create the initial text buffer for the widget to display (which can
799 be replaced later with TextSetBuffer) */
800 buf = BufCreate();
802 /* Create and initialize the text-display part of the widget */
803 textLeft = new->text.marginWidth +
804 (lineNumCols == 0 ? 0 : marginWidth + charWidth * lineNumCols);
805 new->text.textD = TextDCreate((Widget)new, new->text.hScrollBar,
806 new->text.vScrollBar, textLeft, new->text.marginHeight,
807 new->core.width - marginWidth - textLeft,
808 new->core.height - new->text.marginHeight * 2,
809 lineNumCols == 0 ? 0 : marginWidth,
810 lineNumCols == 0 ? 0 : lineNumCols * charWidth,
811 buf, new->text.fontStruct, new->core.background_pixel,
812 new->primitive.foreground, new->text.selectFGPixel,
813 new->text.selectBGPixel, new->text.highlightFGPixel,
814 new->text.highlightBGPixel, new->text.cursorFGPixel,
815 new->text.lineNumFGPixel,
816 new->text.continuousWrap, new->text.wrapMargin,
817 new->text.backlightCharTypes, new->text.calltipFGPixel,
818 new->text.calltipBGPixel);
820 /* Add mandatory delimiters blank, tab, and newline to the list of
821 delimiters. The memory use scheme here is that new values are
822 always copied, and can therefore be safely freed on subsequent
823 set-values calls or destroy */
824 delimiters = XtMalloc(strlen(new->text.delimiters) + 4);
825 sprintf(delimiters, "%s%s", " \t\n", new->text.delimiters);
826 new->text.delimiters = delimiters;
828 /* Start with the cursor blanked (widgets don't have focus on creation,
829 the initial FocusIn event will unblank it and get blinking started) */
830 new->text.textD->cursorOn = False;
832 /* Initialize the widget variables */
833 new->text.autoScrollProcID = 0;
834 new->text.cursorBlinkProcID = 0;
835 new->text.dragState = NOT_CLICKED;
836 new->text.multiClickState = NO_CLICKS;
837 new->text.lastBtnDown = 0;
838 new->text.selectionOwner = False;
839 new->text.motifDestOwner = False;
840 new->text.emTabsBeforeCursor = 0;
842 #ifndef NO_XMIM
843 /* Register the widget to the input manager */
844 XmImRegister((Widget)new, 0);
845 /* In case some Resources for the IC need to be set, add them below */
846 XmImVaSetValues((Widget)new, NULL);
847 #endif
849 XtAddEventHandler((Widget)new, GraphicsExpose, True,
850 (XtEventHandler)redisplayGE, (Opaque)NULL);
852 if (new->text.hidePointer) {
853 Display *theDisplay;
854 Pixmap empty_pixmap;
855 XColor black_color;
856 /* Set up the empty Cursor */
857 if (empty_cursor == 0) {
858 theDisplay = XtDisplay((Widget)new);
859 empty_pixmap = XCreateBitmapFromData(theDisplay,
860 RootWindowOfScreen(XtScreen((Widget)new)), empty_bits, 1, 1);
861 XParseColor(theDisplay, DefaultColormapOfScreen(XtScreen((Widget)new)),
862 "black", &black_color);
863 empty_cursor = XCreatePixmapCursor(theDisplay, empty_pixmap,
864 empty_pixmap, &black_color, &black_color, 0, 0);
867 /* Add event handler to hide the pointer on keypresses */
868 XtAddEventHandler((Widget)new, NEDIT_HIDE_CURSOR_MASK, False,
869 handleHidePointer, (Opaque)NULL);
873 /* Hide the pointer while the user is typing */
874 static void handleHidePointer(Widget w, XtPointer unused,
875 XEvent *event, Boolean *continue_to_dispatch) {
876 TextWidget tw = (TextWidget) w;
877 ShowHidePointer(tw, True);
880 /* Restore the pointer if the mouse moves or focus changes */
881 static void handleShowPointer(Widget w, XtPointer unused,
882 XEvent *event, Boolean *continue_to_dispatch) {
883 TextWidget tw = (TextWidget) w;
884 ShowHidePointer(tw, False);
887 void ShowHidePointer(TextWidget w, Boolean hidePointer)
889 if (w->text.hidePointer) {
890 if (hidePointer != w->text.textD->pointerHidden) {
891 if (hidePointer) {
892 /* Don't listen for keypresses any more */
893 XtRemoveEventHandler((Widget)w, NEDIT_HIDE_CURSOR_MASK, False,
894 handleHidePointer, (Opaque)NULL);
895 /* Switch to empty cursor */
896 XDefineCursor(XtDisplay(w), XtWindow(w), empty_cursor);
898 w->text.textD->pointerHidden = True;
900 /* Listen to mouse movement, focus change, and button presses */
901 XtAddEventHandler((Widget)w, NEDIT_SHOW_CURSOR_MASK,
902 False, handleShowPointer, (Opaque)NULL);
904 else {
905 /* Don't listen to mouse/focus events any more */
906 XtRemoveEventHandler((Widget)w, NEDIT_SHOW_CURSOR_MASK,
907 False, handleShowPointer, (Opaque)NULL);
908 /* Switch to regular cursor */
909 XUndefineCursor(XtDisplay(w), XtWindow(w));
911 w->text.textD->pointerHidden = False;
913 /* Listen for keypresses now */
914 XtAddEventHandler((Widget)w, NEDIT_HIDE_CURSOR_MASK, False,
915 handleHidePointer, (Opaque)NULL);
922 ** Widget destroy method
924 static void destroy(TextWidget w)
926 textBuffer *buf;
928 /* Free the text display and possibly the attached buffer. The buffer
929 is freed only if after removing all of the modify procs (by calling
930 StopHandlingXSelections and TextDFree) there are no modify procs
931 left */
932 StopHandlingXSelections((Widget)w);
933 buf = w->text.textD->buffer;
934 TextDFree(w->text.textD);
935 if (buf->nModifyProcs == 0)
936 BufFree(buf);
938 if (w->text.cursorBlinkProcID != 0)
939 XtRemoveTimeOut(w->text.cursorBlinkProcID);
940 XtFree(w->text.delimiters);
941 XtRemoveAllCallbacks((Widget)w, textNfocusCallback);
942 XtRemoveAllCallbacks((Widget)w, textNlosingFocusCallback);
943 XtRemoveAllCallbacks((Widget)w, textNcursorMovementCallback);
944 XtRemoveAllCallbacks((Widget)w, textNdragStartCallback);
945 XtRemoveAllCallbacks((Widget)w, textNdragEndCallback);
947 #ifndef NO_XMIM
948 /* Unregister the widget from the input manager */
949 XmImUnregister((Widget)w);
950 #endif
954 ** Widget resize method. Called when the size of the widget changes
956 static void resize(TextWidget w)
958 XFontStruct *fs = w->text.fontStruct;
959 int height = w->core.height, width = w->core.width;
960 int marginWidth = w->text.marginWidth, marginHeight = w->text.marginHeight;
961 int lineNumAreaWidth = w->text.lineNumCols == 0 ? 0 : w->text.marginWidth +
962 fs->max_bounds.width * w->text.lineNumCols;
964 w->text.columns = (width - marginWidth*2 - lineNumAreaWidth) /
965 fs->max_bounds.width;
966 w->text.rows = (height - marginHeight*2) / (fs->ascent + fs->descent);
968 /* Reject widths and heights less than a character, which the text
969 display can't tolerate. This is not strictly legal, but I've seen
970 it done in other widgets and it seems to do no serious harm. NEdit
971 prevents panes from getting smaller than one line, but sometimes
972 splitting windows on Linux 2.0 systems (same Motif, why the change in
973 behavior?), causes one or two resize calls with < 1 line of height.
974 Fixing it here is 100x easier than re-designing textDisp.c */
975 if (w->text.columns < 1) {
976 w->text.columns = 1;
977 w->core.width = width = fs->max_bounds.width + marginWidth*2 +
978 lineNumAreaWidth;
980 if (w->text.rows < 1) {
981 w->text.rows = 1;
982 w->core.height = height = fs->ascent + fs->descent + marginHeight*2;
985 /* Resize the text display that the widget uses to render text */
986 TextDResize(w->text.textD, width - marginWidth*2 - lineNumAreaWidth,
987 height - marginHeight*2);
989 /* if the window became shorter or narrower, there may be text left
990 in the bottom or right margin area, which must be cleaned up */
991 if (XtIsRealized((Widget)w)) {
992 XClearArea(XtDisplay(w), XtWindow(w), 0, height-marginHeight,
993 width, marginHeight, False);
994 XClearArea(XtDisplay(w), XtWindow(w),width-marginWidth,
995 0, marginWidth, height, False);
1000 ** Widget redisplay method
1002 static void redisplay(TextWidget w, XEvent *event, Region region)
1004 XExposeEvent *e = &event->xexpose;
1006 TextDRedisplayRect(w->text.textD, e->x, e->y, e->width, e->height);
1009 static Bool findGraphicsExposeOrNoExposeEvent(Display *theDisplay, XEvent *event, XPointer arg)
1011 if ((theDisplay == event->xany.display) &&
1012 (event->type == GraphicsExpose || event->type == NoExpose) &&
1013 ((Widget)arg == XtWindowToWidget(event->xany.display, event->xany.window))) {
1014 return(True);
1016 else {
1017 return(False);
1021 static void adjustRectForGraphicsExposeOrNoExposeEvent(TextWidget w, XEvent *event,
1022 Boolean *first, int *left, int *top, int *width, int *height)
1024 Boolean removeQueueEntry = False;
1026 if (event->type == GraphicsExpose) {
1027 XGraphicsExposeEvent *e = &event->xgraphicsexpose;
1028 int x = e->x, y = e->y;
1030 TextDImposeGraphicsExposeTranslation(w->text.textD, &x, &y);
1031 if (*first) {
1032 *left = x;
1033 *top = y;
1034 *width = e->width;
1035 *height = e->height;
1037 *first = False;
1039 else {
1040 int prev_left = *left;
1041 int prev_top = *top;
1043 *left = min(*left, x);
1044 *top = min(*top, y);
1045 *width = max(prev_left + *width, x + e->width) - *left;
1046 *height = max(prev_top + *height, y + e->height) - *top;
1048 if (e->count == 0) {
1049 removeQueueEntry = True;
1052 else if (event->type == NoExpose) {
1053 removeQueueEntry = True;
1055 if (removeQueueEntry) {
1056 TextDPopGraphicExposeQueueEntry(w->text.textD);
1060 static void redisplayGE(TextWidget w, XtPointer client_data,
1061 XEvent *event, Boolean *continue_to_dispatch_return)
1063 if (event->type == GraphicsExpose || event->type == NoExpose) {
1064 HandleAllPendingGraphicsExposeNoExposeEvents(w, event);
1068 void HandleAllPendingGraphicsExposeNoExposeEvents(TextWidget w, XEvent *event)
1070 XEvent foundEvent;
1071 int left;
1072 int top;
1073 int width;
1074 int height;
1075 Boolean invalidRect = True;
1077 if (event) {
1078 adjustRectForGraphicsExposeOrNoExposeEvent(w, event, &invalidRect, &left, &top, &width, &height);
1080 while (XCheckIfEvent(XtDisplay(w), &foundEvent, findGraphicsExposeOrNoExposeEvent, (XPointer)w)) {
1081 adjustRectForGraphicsExposeOrNoExposeEvent(w, &foundEvent, &invalidRect, &left, &top, &width, &height);
1083 if (!invalidRect) {
1084 TextDRedisplayRect(w->text.textD, left, top, width, height);
1089 ** Widget setValues method
1091 static Boolean setValues(TextWidget current, TextWidget request,
1092 TextWidget new)
1094 Boolean redraw = False, reconfigure = False;
1096 if (new->text.overstrike != current->text.overstrike) {
1097 if (current->text.textD->cursorStyle == BLOCK_CURSOR)
1098 TextDSetCursorStyle(current->text.textD,
1099 current->text.heavyCursor ? HEAVY_CURSOR : NORMAL_CURSOR);
1100 else if (current->text.textD->cursorStyle == NORMAL_CURSOR ||
1101 current->text.textD->cursorStyle == HEAVY_CURSOR)
1102 TextDSetCursorStyle(current->text.textD, BLOCK_CURSOR);
1105 if (new->text.fontStruct != current->text.fontStruct) {
1106 if (new->text.lineNumCols != 0)
1107 reconfigure = True;
1108 TextDSetFont(current->text.textD, new->text.fontStruct);
1111 if (new->text.wrapMargin != current->text.wrapMargin ||
1112 new->text.continuousWrap != current->text.continuousWrap)
1113 TextDSetWrapMode(current->text.textD, new->text.continuousWrap,
1114 new->text.wrapMargin);
1116 /* When delimiters are changed, copy the memory, so that the caller
1117 doesn't have to manage it, and add mandatory delimiters blank,
1118 tab, and newline to the list */
1119 if (new->text.delimiters != current->text.delimiters) {
1120 char *delimiters = XtMalloc(strlen(new->text.delimiters) + 4);
1121 XtFree(current->text.delimiters);
1122 sprintf(delimiters, "%s%s", " \t\n", new->text.delimiters);
1123 new->text.delimiters = delimiters;
1126 /* Setting the lineNumCols resource tells the text widget to hide or
1127 show, or change the number of columns of the line number display,
1128 which requires re-organizing the x coordinates of both the line
1129 number display and the main text display */
1130 if (new->text.lineNumCols != current->text.lineNumCols || reconfigure)
1132 int marginWidth = new->text.marginWidth;
1133 int charWidth = new->text.fontStruct->max_bounds.width;
1134 int lineNumCols = new->text.lineNumCols;
1135 if (lineNumCols == 0)
1137 TextDSetLineNumberArea(new->text.textD, 0, 0, marginWidth);
1138 new->text.columns = (new->core.width - marginWidth*2) / charWidth;
1139 } else
1141 TextDSetLineNumberArea(new->text.textD, marginWidth,
1142 charWidth * lineNumCols,
1143 2*marginWidth + charWidth * lineNumCols);
1144 new->text.columns = (new->core.width - marginWidth*3 - charWidth
1145 * lineNumCols) / charWidth;
1149 if (new->text.backlightCharTypes != current->text.backlightCharTypes)
1151 TextDSetupBGClasses((Widget)new, new->text.backlightCharTypes,
1152 &new->text.textD->bgClassPixel, &new->text.textD->bgClass,
1153 new->text.textD->bgPixel);
1154 redraw = True;
1157 return redraw;
1161 ** Widget realize method
1163 static void realize(Widget w, XtValueMask *valueMask,
1164 XSetWindowAttributes *attributes)
1166 /* Set bit gravity window attribute. This saves a full blank and redraw
1167 on window resizing */
1168 *valueMask |= CWBitGravity;
1169 attributes->bit_gravity = NorthWestGravity;
1171 /* Continue with realize method from superclass */
1172 (xmPrimitiveClassRec.core_class.realize)(w, valueMask, attributes);
1176 ** Widget query geometry method ... unless asked to negotiate a different size simply return current size.
1178 static XtGeometryResult queryGeometry(Widget w, XtWidgetGeometry *proposed,
1179 XtWidgetGeometry *answer)
1181 TextWidget tw = (TextWidget)w;
1183 int curHeight = tw->core.height;
1184 int curWidth = tw->core.width;
1185 XFontStruct *fs = tw->text.textD->fontStruct;
1186 int fontWidth = fs->max_bounds.width;
1187 int fontHeight = fs->ascent + fs->descent;
1188 int marginHeight = tw->text.marginHeight;
1189 int propWidth = (proposed->request_mode & CWWidth) ? proposed->width : 0;
1190 int propHeight = (proposed->request_mode & CWHeight) ? proposed->height : 0;
1192 answer->request_mode = CWHeight | CWWidth;
1194 if(proposed->request_mode & CWWidth)
1195 /* Accept a width no smaller than 10 chars */
1196 answer->width = max(fontWidth * 10, proposed->width);
1197 else
1198 answer->width = curWidth;
1200 if(proposed->request_mode & CWHeight)
1201 /* Accept a height no smaller than an exact multiple of the line height
1202 and at least one line high */
1203 answer->height = max(1, ((propHeight - 2*marginHeight) / fontHeight)) *
1204 fontHeight + 2*marginHeight;
1205 else
1206 answer->height = curHeight;
1208 /*printf("propWidth %d, propHeight %d, ansWidth %d, ansHeight %d\n",
1209 propWidth, propHeight, answer->width, answer->height);*/
1210 if (propWidth == answer->width && propHeight == answer->height)
1211 return XtGeometryYes;
1212 else if (answer->width == curWidth && answer->height == curHeight)
1213 return XtGeometryNo;
1214 else
1215 return XtGeometryAlmost;
1219 ** Set the text buffer which this widget will display and interact with.
1220 ** The currently attached buffer is automatically freed, ONLY if it has
1221 ** no additional modify procs attached (as it would if it were being
1222 ** displayed by another text widget).
1224 void TextSetBuffer(Widget w, textBuffer *buffer)
1226 textBuffer *oldBuf = ((TextWidget)w)->text.textD->buffer;
1228 StopHandlingXSelections(w);
1229 TextDSetBuffer(((TextWidget)w)->text.textD, buffer);
1230 if (oldBuf->nModifyProcs == 0)
1231 BufFree(oldBuf);
1235 ** Get the buffer associated with this text widget. Note that attaching
1236 ** additional modify callbacks to the buffer will prevent it from being
1237 ** automatically freed when the widget is destroyed.
1239 textBuffer *TextGetBuffer(Widget w)
1241 return ((TextWidget)w)->text.textD->buffer;
1245 ** Translate a line number and column into a position
1247 int TextLineAndColToPos(Widget w, int lineNum, int column)
1249 return TextDLineAndColToPos(((TextWidget)w)->text.textD, lineNum, column );
1253 ** Translate a position into a line number (if the position is visible,
1254 ** if it's not, return False
1256 int TextPosToLineAndCol(Widget w, int pos, int *lineNum, int *column)
1258 return TextDPosToLineAndCol(((TextWidget)w)->text.textD, pos, lineNum,
1259 column);
1263 ** Translate a buffer text position to the XY location where the center
1264 ** of the cursor would be positioned to point to that character. Returns
1265 ** False if the position is not displayed because it is VERTICALLY out
1266 ** of view. If the position is horizontally out of view, returns the
1267 ** x coordinate where the position would be if it were visible.
1269 int TextPosToXY(Widget w, int pos, int *x, int *y)
1271 return TextDPositionToXY(((TextWidget)w)->text.textD, pos, x, y);
1275 ** Return the cursor position
1277 int TextGetCursorPos(Widget w)
1279 return TextDGetInsertPosition(((TextWidget)w)->text.textD);
1283 ** Set the cursor position
1285 void TextSetCursorPos(Widget w, int pos)
1287 TextDSetInsertPosition(((TextWidget)w)->text.textD, pos);
1288 checkAutoShowInsertPos(w);
1289 callCursorMovementCBs(w, NULL);
1294 ** Return the horizontal and vertical scroll positions of the widget
1296 void TextGetScroll(Widget w, int *topLineNum, int *horizOffset)
1298 TextDGetScroll(((TextWidget)w)->text.textD, topLineNum, horizOffset);
1302 ** Set the horizontal and vertical scroll positions of the widget
1304 void TextSetScroll(Widget w, int topLineNum, int horizOffset)
1306 TextDSetScroll(((TextWidget)w)->text.textD, topLineNum, horizOffset);
1309 int TextGetMinFontWidth(Widget w, Boolean considerStyles)
1311 return(TextDMinFontWidth(((TextWidget)w)->text.textD, considerStyles));
1314 int TextGetMaxFontWidth(Widget w, Boolean considerStyles)
1316 return(TextDMaxFontWidth(((TextWidget)w)->text.textD, considerStyles));
1320 ** Set this widget to be the owner of selections made in it's attached
1321 ** buffer (text buffers may be shared among several text widgets).
1323 void TextHandleXSelections(Widget w)
1325 HandleXSelections(w);
1328 void TextStopHandlingSelections(Widget w)
1330 StopHandlingXSelections(w);
1333 void TextPasteClipboard(Widget w, Time time)
1335 cancelDrag(w);
1336 if (checkReadOnly(w))
1337 return;
1338 TakeMotifDestination(w, time);
1339 InsertClipboard(w, False);
1340 callCursorMovementCBs(w, NULL);
1343 void TextColPasteClipboard(Widget w, Time time)
1345 cancelDrag(w);
1346 if (checkReadOnly(w))
1347 return;
1348 TakeMotifDestination(w, time);
1349 InsertClipboard(w, True);
1350 callCursorMovementCBs(w, NULL);
1353 void TextCopyClipboard(Widget w, Time time)
1355 cancelDrag(w);
1356 if (!((TextWidget)w)->text.textD->buffer->primary.selected) {
1357 XBell(XtDisplay(w), 0);
1358 return;
1360 CopyToClipboard(w, time);
1363 void TextCutClipboard(Widget w, Time time)
1365 textDisp *textD = ((TextWidget)w)->text.textD;
1367 cancelDrag(w);
1368 if (checkReadOnly(w))
1369 return;
1370 if (!textD->buffer->primary.selected) {
1371 XBell(XtDisplay(w), 0);
1372 return;
1374 TakeMotifDestination(w, time);
1375 CopyToClipboard (w, time);
1376 BufRemoveSelected(textD->buffer);
1377 TextDSetInsertPosition(textD, textD->buffer->cursorPosHint);
1378 checkAutoShowInsertPos(w);
1381 int TextFirstVisibleLine(Widget w)
1383 return(((TextWidget)w)->text.textD->topLineNum);
1386 int TextNumVisibleLines(Widget w)
1388 return(((TextWidget)w)->text.textD->nVisibleLines);
1391 int TextVisibleWidth(Widget w)
1393 return(((TextWidget)w)->text.textD->width);
1396 int TextFirstVisiblePos(Widget w)
1398 return ((TextWidget)w)->text.textD->firstChar;
1401 int TextLastVisiblePos(Widget w)
1403 return ((TextWidget)w)->text.textD->lastChar;
1407 ** Insert text "chars" at the cursor position, respecting pending delete
1408 ** selections, overstrike, and handling cursor repositioning as if the text
1409 ** had been typed. If autoWrap is on wraps the text to fit within the wrap
1410 ** margin, auto-indenting where the line was wrapped (but nowhere else).
1411 ** "allowPendingDelete" controls whether primary selections in the widget are
1412 ** treated as pending delete selections (True), or ignored (False). "event"
1413 ** is optional and is just passed on to the cursor movement callbacks.
1415 void TextInsertAtCursor(Widget w, char *chars, XEvent *event,
1416 int allowPendingDelete, int allowWrap)
1418 int wrapMargin, colNum, lineStartPos, cursorPos;
1419 char *c, *lineStartText, *wrappedText;
1420 TextWidget tw = (TextWidget)w;
1421 textDisp *textD = tw->text.textD;
1422 textBuffer *buf = textD->buffer;
1423 int fontWidth = textD->fontStruct->max_bounds.width;
1424 int replaceSel, singleLine, breakAt = 0;
1426 /* Don't wrap if auto-wrap is off or suppressed, or it's just a newline */
1427 if (!allowWrap || !tw->text.autoWrap ||
1428 (chars[0] == '\n' && chars[1] == '\0')) {
1429 simpleInsertAtCursor(w, chars, event, allowPendingDelete);
1430 return;
1433 /* If this is going to be a pending delete operation, the real insert
1434 position is the start of the selection. This will make rectangular
1435 selections wrap strangely, but this routine should rarely be used for
1436 them, and even more rarely when they need to be wrapped. */
1437 replaceSel = allowPendingDelete && pendingSelection(w);
1438 cursorPos = replaceSel ? buf->primary.start : TextDGetInsertPosition(textD);
1440 /* If the text is only one line and doesn't need to be wrapped, just insert
1441 it and be done (for efficiency only, this routine is called for each
1442 character typed). (Of course, it may not be significantly more efficient
1443 than the more general code below it, so it may be a waste of time!) */
1444 wrapMargin = tw->text.wrapMargin != 0 ? tw->text.wrapMargin :
1445 textD->width / fontWidth;
1446 lineStartPos = BufStartOfLine(buf, cursorPos);
1447 colNum = BufCountDispChars(buf, lineStartPos, cursorPos);
1448 for (c=chars; *c!='\0' && *c!='\n'; c++)
1449 colNum += BufCharWidth(*c, colNum, buf->tabDist, buf->nullSubsChar);
1450 singleLine = *c == '\0';
1451 if (colNum < wrapMargin && singleLine) {
1452 simpleInsertAtCursor(w, chars, event, True);
1453 return;
1456 /* Wrap the text */
1457 lineStartText = BufGetRange(buf, lineStartPos, cursorPos);
1458 wrappedText = wrapText(tw, lineStartText, chars, lineStartPos, wrapMargin,
1459 replaceSel ? NULL : &breakAt);
1460 XtFree(lineStartText);
1462 /* Insert the text. Where possible, use TextDInsert which is optimized
1463 for less redraw. */
1464 if (replaceSel) {
1465 BufReplaceSelected(buf, wrappedText);
1466 TextDSetInsertPosition(textD, buf->cursorPosHint);
1467 } else if (tw->text.overstrike) {
1468 if (breakAt == 0 && singleLine)
1469 TextDOverstrike(textD, wrappedText);
1470 else {
1471 BufReplace(buf, cursorPos-breakAt, cursorPos, wrappedText);
1472 TextDSetInsertPosition(textD, buf->cursorPosHint);
1474 } else {
1475 if (breakAt == 0) {
1476 TextDInsert(textD, wrappedText);
1477 } else {
1478 BufReplace(buf, cursorPos-breakAt, cursorPos, wrappedText);
1479 TextDSetInsertPosition(textD, buf->cursorPosHint);
1482 XtFree(wrappedText);
1483 checkAutoShowInsertPos(w);
1484 callCursorMovementCBs(w, event);
1488 ** Fetch text from the widget's buffer, adding wrapping newlines to emulate
1489 ** effect acheived by wrapping in the text display in continuous wrap mode.
1491 char *TextGetWrapped(Widget w, int startPos, int endPos, int *outLen)
1493 textDisp *textD = ((TextWidget)w)->text.textD;
1494 textBuffer *buf = textD->buffer;
1495 textBuffer *outBuf;
1496 int fromPos, toPos, outPos;
1497 char c, *outString;
1499 if (!((TextWidget)w)->text.continuousWrap || startPos == endPos) {
1500 *outLen = endPos - startPos;
1501 return BufGetRange(buf, startPos, endPos);
1504 /* Create a text buffer with a good estimate of the size that adding
1505 newlines will expand it to. Since it's a text buffer, if we guess
1506 wrong, it will fail softly, and simply expand the size */
1507 outBuf = BufCreatePreallocated((endPos-startPos) + (endPos-startPos)/5);
1508 outPos = 0;
1510 /* Go (displayed) line by line through the buffer, adding newlines where
1511 the text is wrapped at some character other than an existing newline */
1512 fromPos = startPos;
1513 toPos = TextDCountForwardNLines(textD, startPos, 1, False);
1514 while (toPos < endPos) {
1515 BufCopyFromBuf(buf, outBuf, fromPos, toPos, outPos);
1516 outPos += toPos - fromPos;
1517 c = BufGetCharacter(outBuf, outPos-1);
1518 if (c == ' ' || c == '\t')
1519 BufReplace(outBuf, outPos-1, outPos, "\n");
1520 else if (c != '\n') {
1521 BufInsert(outBuf, outPos, "\n");
1522 outPos++;
1524 fromPos = toPos;
1525 toPos = TextDCountForwardNLines(textD, fromPos, 1, True);
1527 BufCopyFromBuf(buf, outBuf, fromPos, endPos, outPos);
1529 /* return the contents of the output buffer as a string */
1530 outString = BufGetAll(outBuf);
1531 *outLen = outBuf->length;
1532 BufFree(outBuf);
1533 return outString;
1537 ** Return the (statically allocated) action table for menu item actions.
1539 ** Warning: This routine can only be used before the first text widget is
1540 ** created! After that, apparently, Xt takes over the table and overwrites
1541 ** it with its own version. XtGetActionList is preferable, but is not
1542 ** available before X11R5.
1544 XtActionsRec *TextGetActions(int *nActions)
1546 *nActions = XtNumber(actionsList);
1547 return actionsList;
1550 static void grabFocusAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
1552 XButtonEvent *e = &event->xbutton;
1553 TextWidget tw = (TextWidget)w;
1554 textDisp *textD = tw->text.textD;
1555 Time lastBtnDown = tw->text.lastBtnDown;
1556 int row, column;
1558 /* Indicate state for future events, PRIMARY_CLICKED indicates that
1559 the proper initialization has been done for primary dragging and/or
1560 multi-clicking. Also record the timestamp for multi-click processing */
1561 tw->text.dragState = PRIMARY_CLICKED;
1562 tw->text.lastBtnDown = e->time;
1564 /* Become owner of the MOTIF_DESTINATION selection, making this widget
1565 the designated recipient of secondary quick actions in Motif XmText
1566 widgets and in other NEdit text widgets */
1567 TakeMotifDestination(w, e->time);
1569 /* Check for possible multi-click sequence in progress */
1570 if (tw->text.multiClickState != NO_CLICKS) {
1571 if (e->time < lastBtnDown + XtGetMultiClickTime(XtDisplay(w))) {
1572 if (tw->text.multiClickState == ONE_CLICK) {
1573 selectWord(w, e->x);
1574 callCursorMovementCBs(w, event);
1575 return;
1576 } else if (tw->text.multiClickState == TWO_CLICKS) {
1577 selectLine(w);
1578 callCursorMovementCBs(w, event);
1579 return;
1580 } else if (tw->text.multiClickState == THREE_CLICKS) {
1581 BufSelect(textD->buffer, 0, textD->buffer->length);
1582 return;
1583 } else if (tw->text.multiClickState > THREE_CLICKS)
1584 tw->text.multiClickState = NO_CLICKS;
1585 } else
1586 tw->text.multiClickState = NO_CLICKS;
1589 /* Clear any existing selections */
1590 BufUnselect(textD->buffer);
1592 /* Move the cursor to the pointer location */
1593 moveDestinationAP(w, event, args, nArgs);
1595 /* Record the site of the initial button press and the initial character
1596 position so subsequent motion events and clicking can decide when and
1597 where to begin a primary selection */
1598 tw->text.btnDownX = e->x;
1599 tw->text.btnDownY = e->y;
1600 tw->text.anchor = TextDGetInsertPosition(textD);
1601 TextDXYToUnconstrainedPosition(textD, e->x, e->y, &row, &column);
1602 column = TextDOffsetWrappedColumn(textD, row, column);
1603 tw->text.rectAnchor = column;
1606 static void moveDestinationAP(Widget w, XEvent *event, String *args,
1607 Cardinal *nArgs)
1609 XButtonEvent *e = &event->xbutton;
1610 textDisp *textD = ((TextWidget)w)->text.textD;
1612 /* Get input focus */
1613 XmProcessTraversal(w, XmTRAVERSE_CURRENT);
1615 /* Move the cursor */
1616 TextDSetInsertPosition(textD, TextDXYToPosition(textD, e->x, e->y));
1617 checkAutoShowInsertPos(w);
1618 callCursorMovementCBs(w, event);
1621 static void extendAdjustAP(Widget w, XEvent *event, String *args,
1622 Cardinal *nArgs)
1624 TextWidget tw = (TextWidget)w;
1625 XMotionEvent *e = &event->xmotion;
1626 int dragState = tw->text.dragState;
1627 int rectDrag = hasKey("rect", args, nArgs);
1629 /* Make sure the proper initialization was done on mouse down */
1630 if (dragState != PRIMARY_DRAG && dragState != PRIMARY_CLICKED &&
1631 dragState != PRIMARY_RECT_DRAG)
1632 return;
1634 /* If the selection hasn't begun, decide whether the mouse has moved
1635 far enough from the initial mouse down to be considered a drag */
1636 if (tw->text.dragState == PRIMARY_CLICKED) {
1637 if (abs(e->x - tw->text.btnDownX) > SELECT_THRESHOLD ||
1638 abs(e->y - tw->text.btnDownY) > SELECT_THRESHOLD)
1639 tw->text.dragState = rectDrag ? PRIMARY_RECT_DRAG : PRIMARY_DRAG;
1640 else
1641 return;
1644 /* If "rect" argument has appeared or disappeared, keep dragState up
1645 to date about which type of drag this is */
1646 tw->text.dragState = rectDrag ? PRIMARY_RECT_DRAG : PRIMARY_DRAG;
1648 /* Record the new position for the autoscrolling timer routine, and
1649 engage or disengage the timer if the mouse is in/out of the window */
1650 checkAutoScroll(tw, e->x, e->y);
1652 /* Adjust the selection and move the cursor */
1653 adjustSelection(tw, e->x, e->y);
1656 static void extendStartAP(Widget w, XEvent *event, String *args,
1657 Cardinal *nArgs)
1659 XMotionEvent *e = &event->xmotion;
1660 textDisp *textD = ((TextWidget)w)->text.textD;
1661 textBuffer *buf = textD->buffer;
1662 selection *sel = &buf->primary;
1663 int anchor, rectAnchor, anchorLineStart, newPos, row, column;
1665 /* Find the new anchor point for the rest of this drag operation */
1666 newPos = TextDXYToPosition(textD, e->x, e->y);
1667 TextDXYToUnconstrainedPosition(textD, e->x, e->y, &row, &column);
1668 column = TextDOffsetWrappedColumn(textD, row, column);
1669 if (sel->selected) {
1670 if (sel->rectangular) {
1671 rectAnchor = column < (sel->rectEnd + sel->rectStart) / 2 ?
1672 sel->rectEnd : sel->rectStart;
1673 anchorLineStart = BufStartOfLine(buf, newPos <
1674 (sel->end + sel->start) / 2 ? sel->end : sel->start);
1675 anchor = BufCountForwardDispChars(buf, anchorLineStart, rectAnchor);
1676 } else {
1677 if (abs(newPos - sel->start) < abs(newPos - sel->end))
1678 anchor = sel->end;
1679 else
1680 anchor = sel->start;
1681 anchorLineStart = BufStartOfLine(buf, anchor);
1682 rectAnchor = BufCountDispChars(buf, anchorLineStart, anchor);
1684 } else {
1685 anchor = TextDGetInsertPosition(textD);
1686 anchorLineStart = BufStartOfLine(buf, anchor);
1687 rectAnchor = BufCountDispChars(buf, anchorLineStart, anchor);
1689 ((TextWidget)w)->text.anchor = anchor;
1690 ((TextWidget)w)->text.rectAnchor = rectAnchor;
1692 /* Make the new selection */
1693 if (hasKey("rect", args, nArgs))
1694 BufRectSelect(buf, BufStartOfLine(buf, min(anchor, newPos)),
1695 BufEndOfLine(buf, max(anchor, newPos)),
1696 min(rectAnchor, column), max(rectAnchor, column));
1697 else
1698 BufSelect(buf, min(anchor, newPos), max(anchor, newPos));
1700 /* Never mind the motion threshold, go right to dragging since
1701 extend-start is unambiguously the start of a selection */
1702 ((TextWidget)w)->text.dragState = PRIMARY_DRAG;
1704 /* Don't do by-word or by-line adjustment, just by character */
1705 ((TextWidget)w)->text.multiClickState = NO_CLICKS;
1707 /* Move the cursor */
1708 TextDSetInsertPosition(textD, newPos);
1709 callCursorMovementCBs(w, event);
1712 static void extendEndAP(Widget w, XEvent *event, String *args,
1713 Cardinal *nArgs)
1715 XButtonEvent *e = &event->xbutton;
1716 TextWidget tw = (TextWidget)w;
1718 if (tw->text.dragState == PRIMARY_CLICKED &&
1719 tw->text.lastBtnDown <= e->time + XtGetMultiClickTime(XtDisplay(w)))
1720 tw->text.multiClickState++;
1721 endDrag(w);
1724 static void processCancelAP(Widget w, XEvent *event, String *args,
1725 Cardinal *nArgs)
1727 int dragState = ((TextWidget)w)->text.dragState;
1728 textBuffer *buf = ((TextWidget)w)->text.textD->buffer;
1729 textDisp *textD = ((TextWidget)w)->text.textD;
1731 /* If there's a calltip displayed, kill it. */
1732 TextDKillCalltip(textD, 0);
1734 if (dragState == PRIMARY_DRAG || dragState == PRIMARY_RECT_DRAG)
1735 BufUnselect(buf);
1736 cancelDrag(w);
1739 static void secondaryStartAP(Widget w, XEvent *event, String *args,
1740 Cardinal *nArgs)
1742 XMotionEvent *e = &event->xmotion;
1743 textDisp *textD = ((TextWidget)w)->text.textD;
1744 textBuffer *buf = textD->buffer;
1745 selection *sel = &buf->secondary;
1746 int anchor, pos, row, column;
1748 /* Find the new anchor point and make the new selection */
1749 pos = TextDXYToPosition(textD, e->x, e->y);
1750 if (sel->selected) {
1751 if (abs(pos - sel->start) < abs(pos - sel->end))
1752 anchor = sel->end;
1753 else
1754 anchor = sel->start;
1755 BufSecondarySelect(buf, anchor, pos);
1756 } else
1757 anchor = pos;
1759 /* Record the site of the initial button press and the initial character
1760 position so subsequent motion events can decide when to begin a
1761 selection, (and where the selection began) */
1762 ((TextWidget)w)->text.btnDownX = e->x;
1763 ((TextWidget)w)->text.btnDownY = e->y;
1764 ((TextWidget)w)->text.anchor = pos;
1765 TextDXYToUnconstrainedPosition(textD, e->x, e->y, &row, &column);
1766 column = TextDOffsetWrappedColumn(textD, row, column);
1767 ((TextWidget)w)->text.rectAnchor = column;
1768 ((TextWidget)w)->text.dragState = SECONDARY_CLICKED;
1771 static void secondaryOrDragStartAP(Widget w, XEvent *event, String *args,
1772 Cardinal *nArgs)
1774 XMotionEvent *e = &event->xmotion;
1775 textDisp *textD = ((TextWidget)w)->text.textD;
1776 textBuffer *buf = textD->buffer;
1778 /* If the click was outside of the primary selection, this is not
1779 a drag, start a secondary selection */
1780 if (!buf->primary.selected || !TextDInSelection(textD, e->x, e->y)) {
1781 secondaryStartAP(w, event, args, nArgs);
1782 return;
1785 if (checkReadOnly(w))
1786 return;
1788 /* Record the site of the initial button press and the initial character
1789 position so subsequent motion events can decide when to begin a
1790 drag, and where to drag to */
1791 ((TextWidget)w)->text.btnDownX = e->x;
1792 ((TextWidget)w)->text.btnDownY = e->y;
1793 ((TextWidget)w)->text.dragState = CLICKED_IN_SELECTION;
1796 static void secondaryAdjustAP(Widget w, XEvent *event, String *args,
1797 Cardinal *nArgs)
1799 TextWidget tw = (TextWidget)w;
1800 XMotionEvent *e = &event->xmotion;
1801 int dragState = tw->text.dragState;
1802 int rectDrag = hasKey("rect", args, nArgs);
1804 /* Make sure the proper initialization was done on mouse down */
1805 if (dragState != SECONDARY_DRAG && dragState != SECONDARY_RECT_DRAG &&
1806 dragState != SECONDARY_CLICKED)
1807 return;
1809 /* If the selection hasn't begun, decide whether the mouse has moved
1810 far enough from the initial mouse down to be considered a drag */
1811 if (tw->text.dragState == SECONDARY_CLICKED) {
1812 if (abs(e->x - tw->text.btnDownX) > SELECT_THRESHOLD ||
1813 abs(e->y - tw->text.btnDownY) > SELECT_THRESHOLD)
1814 tw->text.dragState = rectDrag ? SECONDARY_RECT_DRAG: SECONDARY_DRAG;
1815 else
1816 return;
1819 /* If "rect" argument has appeared or disappeared, keep dragState up
1820 to date about which type of drag this is */
1821 tw->text.dragState = rectDrag ? SECONDARY_RECT_DRAG : SECONDARY_DRAG;
1823 /* Record the new position for the autoscrolling timer routine, and
1824 engage or disengage the timer if the mouse is in/out of the window */
1825 checkAutoScroll(tw, e->x, e->y);
1827 /* Adjust the selection */
1828 adjustSecondarySelection(tw, e->x, e->y);
1831 static void secondaryOrDragAdjustAP(Widget w, XEvent *event, String *args,
1832 Cardinal *nArgs)
1834 TextWidget tw = (TextWidget)w;
1835 XMotionEvent *e = &event->xmotion;
1836 int dragState = tw->text.dragState;
1838 /* Only dragging of blocks of text is handled in this action proc.
1839 Otherwise, defer to secondaryAdjust to handle the rest */
1840 if (dragState != CLICKED_IN_SELECTION && dragState != PRIMARY_BLOCK_DRAG) {
1841 secondaryAdjustAP(w, event, args, nArgs);
1842 return;
1845 /* Decide whether the mouse has moved far enough from the
1846 initial mouse down to be considered a drag */
1847 if (tw->text.dragState == CLICKED_IN_SELECTION) {
1848 if (abs(e->x - tw->text.btnDownX) > SELECT_THRESHOLD ||
1849 abs(e->y - tw->text.btnDownY) > SELECT_THRESHOLD)
1850 BeginBlockDrag(tw);
1851 else
1852 return;
1855 /* Record the new position for the autoscrolling timer routine, and
1856 engage or disengage the timer if the mouse is in/out of the window */
1857 checkAutoScroll(tw, e->x, e->y);
1859 /* Adjust the selection */
1860 BlockDragSelection(tw, e->x, e->y, hasKey("overlay", args, nArgs) ?
1861 (hasKey("copy", args, nArgs) ? DRAG_OVERLAY_COPY : DRAG_OVERLAY_MOVE) :
1862 (hasKey("copy", args, nArgs) ? DRAG_COPY : DRAG_MOVE));
1865 static void copyToAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
1867 XButtonEvent *e = &event->xbutton;
1868 TextWidget tw = (TextWidget)w;
1869 textDisp *textD = tw->text.textD;
1870 int dragState = tw->text.dragState;
1871 textBuffer *buf = textD->buffer;
1872 selection *secondary = &buf->secondary, *primary = &buf->primary;
1873 int rectangular = secondary->rectangular;
1874 char *textToCopy;
1875 int insertPos, lineStart, column;
1877 endDrag(w);
1878 if (!((dragState == SECONDARY_DRAG && secondary->selected) ||
1879 (dragState == SECONDARY_RECT_DRAG && secondary->selected) ||
1880 dragState == SECONDARY_CLICKED || dragState == NOT_CLICKED))
1881 return;
1882 if (!(secondary->selected && !((TextWidget)w)->text.motifDestOwner)) {
1883 if (checkReadOnly(w)) {
1884 BufSecondaryUnselect(buf);
1885 return;
1888 if (secondary->selected) {
1889 if (tw->text.motifDestOwner) {
1890 TextDBlankCursor(textD);
1891 textToCopy = BufGetSecSelectText(buf);
1892 if (primary->selected && rectangular) {
1893 insertPos = TextDGetInsertPosition(textD);
1894 BufReplaceSelected(buf, textToCopy);
1895 TextDSetInsertPosition(textD, buf->cursorPosHint);
1896 } else if (rectangular) {
1897 insertPos = TextDGetInsertPosition(textD);
1898 lineStart = BufStartOfLine(buf, insertPos);
1899 column = BufCountDispChars(buf, lineStart, insertPos);
1900 BufInsertCol(buf, column, lineStart, textToCopy, NULL, NULL);
1901 TextDSetInsertPosition(textD, buf->cursorPosHint);
1902 } else
1903 TextInsertAtCursor(w, textToCopy, event, True,
1904 tw->text.autoWrapPastedText);
1905 XtFree(textToCopy);
1906 BufSecondaryUnselect(buf);
1907 TextDUnblankCursor(textD);
1908 } else
1909 SendSecondarySelection(w, e->time, False);
1910 } else if (primary->selected) {
1911 textToCopy = BufGetSelectionText(buf);
1912 TextDSetInsertPosition(textD, TextDXYToPosition(textD, e->x, e->y));
1913 TextInsertAtCursor(w, textToCopy, event, False,
1914 tw->text.autoWrapPastedText);
1915 XtFree(textToCopy);
1916 } else {
1917 TextDSetInsertPosition(textD, TextDXYToPosition(textD, e->x, e->y));
1918 InsertPrimarySelection(w, e->time, False);
1922 static void copyToOrEndDragAP(Widget w, XEvent *event, String *args,
1923 Cardinal *nArgs)
1925 int dragState = ((TextWidget)w)->text.dragState;
1927 if (dragState != PRIMARY_BLOCK_DRAG) {
1928 copyToAP(w, event, args, nArgs);
1929 return;
1932 FinishBlockDrag((TextWidget)w);
1935 static void moveToAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
1937 XButtonEvent *e = &event->xbutton;
1938 textDisp *textD = ((TextWidget)w)->text.textD;
1939 int dragState = ((TextWidget)w)->text.dragState;
1940 textBuffer *buf = textD->buffer;
1941 selection *secondary = &buf->secondary, *primary = &buf->primary;
1942 int insertPos, rectangular = secondary->rectangular;
1943 int column, lineStart;
1944 char *textToCopy;
1946 endDrag(w);
1947 if (!((dragState == SECONDARY_DRAG && secondary->selected) ||
1948 (dragState == SECONDARY_RECT_DRAG && secondary->selected) ||
1949 dragState == SECONDARY_CLICKED || dragState == NOT_CLICKED))
1950 return;
1951 if (checkReadOnly(w)) {
1952 BufSecondaryUnselect(buf);
1953 return;
1956 if (secondary->selected) {
1957 if (((TextWidget)w)->text.motifDestOwner) {
1958 textToCopy = BufGetSecSelectText(buf);
1959 if (primary->selected && rectangular) {
1960 insertPos = TextDGetInsertPosition(textD);
1961 BufReplaceSelected(buf, textToCopy);
1962 TextDSetInsertPosition(textD, buf->cursorPosHint);
1963 } else if (rectangular) {
1964 insertPos = TextDGetInsertPosition(textD);
1965 lineStart = BufStartOfLine(buf, insertPos);
1966 column = BufCountDispChars(buf, lineStart, insertPos);
1967 BufInsertCol(buf, column, lineStart, textToCopy, NULL, NULL);
1968 TextDSetInsertPosition(textD, buf->cursorPosHint);
1969 } else
1970 TextInsertAtCursor(w, textToCopy, event, True,
1971 ((TextWidget)w)->text.autoWrapPastedText);
1972 XtFree(textToCopy);
1973 BufRemoveSecSelect(buf);
1974 BufSecondaryUnselect(buf);
1975 } else
1976 SendSecondarySelection(w, e->time, True);
1977 } else if (primary->selected) {
1978 textToCopy = BufGetRange(buf, primary->start, primary->end);
1979 TextDSetInsertPosition(textD, TextDXYToPosition(textD, e->x, e->y));
1980 TextInsertAtCursor(w, textToCopy, event, False,
1981 ((TextWidget)w)->text.autoWrapPastedText);
1982 XtFree(textToCopy);
1983 BufRemoveSelected(buf);
1984 BufUnselect(buf);
1985 } else {
1986 TextDSetInsertPosition(textD, TextDXYToPosition(textD, e->x, e->y));
1987 MovePrimarySelection(w, e->time, False);
1991 static void moveToOrEndDragAP(Widget w, XEvent *event, String *args,
1992 Cardinal *nArgs)
1994 int dragState = ((TextWidget)w)->text.dragState;
1996 if (dragState != PRIMARY_BLOCK_DRAG) {
1997 moveToAP(w, event, args, nArgs);
1998 return;
2001 FinishBlockDrag((TextWidget)w);
2004 static void endDragAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2006 if (((TextWidget)w)->text.dragState == PRIMARY_BLOCK_DRAG)
2007 FinishBlockDrag((TextWidget)w);
2008 else
2009 endDrag(w);
2012 static void exchangeAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2014 XButtonEvent *e = &event->xbutton;
2015 textDisp *textD = ((TextWidget)w)->text.textD;
2016 textBuffer *buf = textD->buffer;
2017 selection *sec = &buf->secondary, *primary = &buf->primary;
2018 char *primaryText, *secText;
2019 int newPrimaryStart, newPrimaryEnd, secWasRect;
2020 int dragState = ((TextWidget)w)->text.dragState; /* save before endDrag */
2021 int silent = hasKey("nobell", args, nArgs);
2023 endDrag(w);
2024 if (checkReadOnly(w))
2025 return;
2027 /* If there's no secondary selection here, or the primary and secondary
2028 selection overlap, just beep and return */
2029 if (!sec->selected || (primary->selected &&
2030 ((primary->start <= sec->start && primary->end > sec->start) ||
2031 (sec->start <= primary->start && sec->end > primary->start))))
2033 BufSecondaryUnselect(buf);
2034 ringIfNecessary(silent, w);
2035 /* If there's no secondary selection, but the primary selection is
2036 being dragged, we must not forget to finish the dragging.
2037 Otherwise, modifications aren't recorded. */
2038 if (dragState == PRIMARY_BLOCK_DRAG)
2039 FinishBlockDrag((TextWidget)w);
2040 return;
2043 /* if the primary selection is in another widget, use selection routines */
2044 if (!primary->selected) {
2045 ExchangeSelections(w, e->time);
2046 return;
2049 /* Both primary and secondary are in this widget, do the exchange here */
2050 primaryText = BufGetSelectionText(buf);
2051 secText = BufGetSecSelectText(buf);
2052 secWasRect = sec->rectangular;
2053 BufReplaceSecSelect(buf, primaryText);
2054 newPrimaryStart = primary->start;
2055 BufReplaceSelected(buf, secText);
2056 newPrimaryEnd = newPrimaryStart + strlen(secText);
2057 XtFree(primaryText);
2058 XtFree(secText);
2059 BufSecondaryUnselect(buf);
2060 if (secWasRect) {
2061 TextDSetInsertPosition(textD, buf->cursorPosHint);
2062 } else {
2063 BufSelect(buf, newPrimaryStart, newPrimaryEnd);
2064 TextDSetInsertPosition(textD, newPrimaryEnd);
2066 checkAutoShowInsertPos(w);
2069 static void copyPrimaryAP(Widget w, XEvent *event, String *args,
2070 Cardinal *nArgs)
2072 XKeyEvent *e = &event->xkey;
2073 TextWidget tw = (TextWidget)w;
2074 textDisp *textD = tw->text.textD;
2075 textBuffer *buf = textD->buffer;
2076 selection *primary = &buf->primary;
2077 int rectangular = hasKey("rect", args, nArgs);
2078 char *textToCopy;
2079 int insertPos, col;
2081 cancelDrag(w);
2082 if (checkReadOnly(w))
2083 return;
2084 if (primary->selected && rectangular) {
2085 textToCopy = BufGetSelectionText(buf);
2086 insertPos = TextDGetInsertPosition(textD);
2087 col = BufCountDispChars(buf, BufStartOfLine(buf, insertPos), insertPos);
2088 BufInsertCol(buf, col, insertPos, textToCopy, NULL, NULL);
2089 TextDSetInsertPosition(textD, buf->cursorPosHint);
2090 XtFree(textToCopy);
2091 checkAutoShowInsertPos(w);
2092 } else if (primary->selected) {
2093 textToCopy = BufGetSelectionText(buf);
2094 insertPos = TextDGetInsertPosition(textD);
2095 BufInsert(buf, insertPos, textToCopy);
2096 TextDSetInsertPosition(textD, insertPos + strlen(textToCopy));
2097 XtFree(textToCopy);
2098 checkAutoShowInsertPos(w);
2099 } else if (rectangular) {
2100 if (!TextDPositionToXY(textD, TextDGetInsertPosition(textD),
2101 &tw->text.btnDownX, &tw->text.btnDownY))
2102 return; /* shouldn't happen */
2103 InsertPrimarySelection(w, e->time, True);
2104 } else
2105 InsertPrimarySelection(w, e->time, False);
2108 static void cutPrimaryAP(Widget w, XEvent *event, String *args,
2109 Cardinal *nArgs)
2111 XKeyEvent *e = &event->xkey;
2112 textDisp *textD = ((TextWidget)w)->text.textD;
2113 textBuffer *buf = textD->buffer;
2114 selection *primary = &buf->primary;
2115 char *textToCopy;
2116 int rectangular = hasKey("rect", args, nArgs);
2117 int insertPos, col;
2119 cancelDrag(w);
2120 if (checkReadOnly(w))
2121 return;
2122 if (primary->selected && rectangular) {
2123 textToCopy = BufGetSelectionText(buf);
2124 insertPos = TextDGetInsertPosition(textD);
2125 col = BufCountDispChars(buf, BufStartOfLine(buf, insertPos), insertPos);
2126 BufInsertCol(buf, col, insertPos, textToCopy, NULL, NULL);
2127 TextDSetInsertPosition(textD, buf->cursorPosHint);
2128 XtFree(textToCopy);
2129 BufRemoveSelected(buf);
2130 checkAutoShowInsertPos(w);
2131 } else if (primary->selected) {
2132 textToCopy = BufGetSelectionText(buf);
2133 insertPos = TextDGetInsertPosition(textD);
2134 BufInsert(buf, insertPos, textToCopy);
2135 TextDSetInsertPosition(textD, insertPos + strlen(textToCopy));
2136 XtFree(textToCopy);
2137 BufRemoveSelected(buf);
2138 checkAutoShowInsertPos(w);
2139 } else if (rectangular) {
2140 if (!TextDPositionToXY(textD, TextDGetInsertPosition(textD),
2141 &((TextWidget)w)->text.btnDownX,
2142 &((TextWidget)w)->text.btnDownY))
2143 return; /* shouldn't happen */
2144 MovePrimarySelection(w, e->time, True);
2145 } else {
2146 MovePrimarySelection(w, e->time, False);
2150 static void mousePanAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2152 XButtonEvent *e = &event->xbutton;
2153 TextWidget tw = (TextWidget)w;
2154 textDisp *textD = tw->text.textD;
2155 int lineHeight = textD->ascent + textD->descent;
2156 int topLineNum, horizOffset;
2157 static Cursor panCursor = 0;
2159 if (tw->text.dragState == MOUSE_PAN) {
2160 TextDSetScroll(textD,
2161 (tw->text.btnDownY - e->y + lineHeight/2) / lineHeight,
2162 tw->text.btnDownX - e->x);
2163 } else if (tw->text.dragState == NOT_CLICKED) {
2164 TextDGetScroll(textD, &topLineNum, &horizOffset);
2165 tw->text.btnDownX = e->x + horizOffset;
2166 tw->text.btnDownY = e->y + topLineNum * lineHeight;
2167 tw->text.dragState = MOUSE_PAN;
2168 if (!panCursor)
2169 panCursor = XCreateFontCursor(XtDisplay(w), XC_fleur);
2170 XGrabPointer(XtDisplay(w), XtWindow(w), False,
2171 ButtonMotionMask | ButtonReleaseMask, GrabModeAsync,
2172 GrabModeAsync, None, panCursor, CurrentTime);
2173 } else
2174 cancelDrag(w);
2177 static void pasteClipboardAP(Widget w, XEvent *event, String *args,
2178 Cardinal *nArgs)
2180 if (hasKey("rect", args, nArgs))
2181 TextColPasteClipboard(w, event->xkey.time);
2182 else
2183 TextPasteClipboard(w, event->xkey.time);
2186 static void copyClipboardAP(Widget w, XEvent *event, String *args,
2187 Cardinal *nArgs)
2189 TextCopyClipboard(w, event->xkey.time);
2192 static void cutClipboardAP(Widget w, XEvent *event, String *args,
2193 Cardinal *nArgs)
2195 TextCutClipboard(w, event->xkey.time);
2198 static void insertStringAP(Widget w, XEvent *event, String *args,
2199 Cardinal *nArgs)
2201 smartIndentCBStruct smartIndent;
2202 textDisp *textD = ((TextWidget)w)->text.textD;
2204 if (*nArgs == 0)
2205 return;
2206 cancelDrag(w);
2207 if (checkReadOnly(w))
2208 return;
2209 if (((TextWidget)w)->text.smartIndent) {
2210 smartIndent.reason = CHAR_TYPED;
2211 smartIndent.pos = TextDGetInsertPosition(textD);
2212 smartIndent.indentRequest = 0;
2213 smartIndent.charsTyped = args[0];
2214 XtCallCallbacks(w, textNsmartIndentCallback, (XtPointer)&smartIndent);
2216 TextInsertAtCursor(w, args[0], event, True, True);
2217 BufUnselect((((TextWidget)w)->text.textD)->buffer);
2220 static void selfInsertAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2222 #ifdef NO_XMIM
2223 static XComposeStatus compose = {NULL, 0};
2224 #else
2225 int status;
2226 #endif
2227 XKeyEvent *e = &event->xkey;
2228 char chars[20];
2229 KeySym keysym;
2230 int nChars;
2231 smartIndentCBStruct smartIndent;
2232 textDisp *textD = ((TextWidget)w)->text.textD;
2234 #ifdef NO_XMIM
2235 nChars = XLookupString(&event->xkey, chars, 19, &keysym, &compose);
2236 if (nChars == 0)
2237 return;
2238 #else
2239 nChars = XmImMbLookupString(w, &event->xkey, chars, 19, &keysym,
2240 &status);
2241 if (nChars == 0 || status == XLookupNone ||
2242 status == XLookupKeySym || status == XBufferOverflow)
2243 return;
2244 #endif
2245 cancelDrag(w);
2246 if (checkReadOnly(w))
2247 return;
2248 TakeMotifDestination(w, e->time);
2249 chars[nChars] = '\0';
2251 /* If smart indent is on, call the smart indent callback to check the
2252 inserted character */
2253 if (((TextWidget)w)->text.smartIndent) {
2254 smartIndent.reason = CHAR_TYPED;
2255 smartIndent.pos = TextDGetInsertPosition(textD);
2256 smartIndent.indentRequest = 0;
2257 smartIndent.charsTyped = chars;
2258 XtCallCallbacks(w, textNsmartIndentCallback, (XtPointer)&smartIndent);
2260 TextInsertAtCursor(w, chars, event, True, True);
2261 BufUnselect(textD->buffer);
2264 static void newlineAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2266 if (((TextWidget)w)->text.autoIndent || ((TextWidget)w)->text.smartIndent)
2267 newlineAndIndentAP(w, event, args, nArgs);
2268 else
2269 newlineNoIndentAP(w, event, args, nArgs);
2272 static void newlineNoIndentAP(Widget w, XEvent *event, String *args,
2273 Cardinal *nArgs)
2275 XKeyEvent *e = &event->xkey;
2277 cancelDrag(w);
2278 if (checkReadOnly(w))
2279 return;
2280 TakeMotifDestination(w, e->time);
2281 simpleInsertAtCursor(w, "\n", event, True);
2282 BufUnselect((((TextWidget)w)->text.textD)->buffer);
2285 static void newlineAndIndentAP(Widget w, XEvent *event, String *args,
2286 Cardinal *nArgs)
2288 XKeyEvent *e = &event->xkey;
2289 TextWidget tw = (TextWidget)w;
2290 textDisp *textD = tw->text.textD;
2291 textBuffer *buf = textD->buffer;
2292 char *indentStr;
2293 int cursorPos, lineStartPos, column;
2295 if (checkReadOnly(w))
2296 return;
2297 cancelDrag(w);
2298 TakeMotifDestination(w, e->time);
2300 /* Create a string containing a newline followed by auto or smart
2301 indent string */
2302 cursorPos = TextDGetInsertPosition(textD);
2303 lineStartPos = BufStartOfLine(buf, cursorPos);
2304 indentStr = createIndentString(tw, buf, 0, lineStartPos,
2305 cursorPos, NULL, &column);
2307 /* Insert it at the cursor */
2308 simpleInsertAtCursor(w, indentStr, event, True);
2309 XtFree(indentStr);
2311 /* If emulated tabs are on, make the inserted indent deletable by tab */
2312 if (tw->text.emulateTabs)
2313 tw->text.emTabsBeforeCursor = column / tw->text.emulateTabs;
2315 BufUnselect(buf);
2318 static void processTabAP(Widget w, XEvent *event, String *args,
2319 Cardinal *nArgs)
2321 textDisp *textD = ((TextWidget)w)->text.textD;
2322 textBuffer *buf = textD->buffer;
2323 selection *sel = &buf->primary;
2324 int emTabDist = ((TextWidget)w)->text.emulateTabs;
2325 int emTabsBeforeCursor = ((TextWidget)w)->text.emTabsBeforeCursor;
2326 int insertPos, indent, startIndent, toIndent, lineStart, tabWidth;
2327 char *outStr, *outPtr;
2329 if (checkReadOnly(w))
2330 return;
2331 cancelDrag(w);
2332 TakeMotifDestination(w, event->xkey.time);
2334 /* If emulated tabs are off, just insert a tab */
2335 if (emTabDist <= 0) {
2336 TextInsertAtCursor(w, "\t", event, True, True);
2337 return;
2340 /* Find the starting and ending indentation. If the tab is to
2341 replace an existing selection, use the start of the selection
2342 instead of the cursor position as the indent. When replacing
2343 rectangular selections, tabs are automatically recalculated as
2344 if the inserted text began at the start of the line */
2345 insertPos = pendingSelection(w) ?
2346 sel->start : TextDGetInsertPosition(textD);
2347 lineStart = BufStartOfLine(buf, insertPos);
2348 if (pendingSelection(w) && sel->rectangular)
2349 insertPos = BufCountForwardDispChars(buf, lineStart, sel->rectStart);
2350 startIndent = BufCountDispChars(buf, lineStart, insertPos);
2351 toIndent = startIndent + emTabDist - (startIndent % emTabDist);
2352 if (pendingSelection(w) && sel->rectangular) {
2353 toIndent -= startIndent;
2354 startIndent = 0;
2357 /* Allocate a buffer assuming all the inserted characters will be spaces */
2358 outStr = XtMalloc(toIndent - startIndent + 1);
2360 /* Add spaces and tabs to outStr until it reaches toIndent */
2361 outPtr = outStr;
2362 indent = startIndent;
2363 while (indent < toIndent) {
2364 tabWidth = BufCharWidth('\t', indent, buf->tabDist, buf->nullSubsChar);
2365 if (buf->useTabs && tabWidth > 1 && indent + tabWidth <= toIndent) {
2366 *outPtr++ = '\t';
2367 indent += tabWidth;
2368 } else {
2369 *outPtr++ = ' ';
2370 indent++;
2373 *outPtr = '\0';
2375 /* Insert the emulated tab */
2376 TextInsertAtCursor(w, outStr, event, True, True);
2377 XtFree(outStr);
2379 /* Restore and ++ emTabsBeforeCursor cleared by TextInsertAtCursor */
2380 ((TextWidget)w)->text.emTabsBeforeCursor = emTabsBeforeCursor + 1;
2382 BufUnselect(buf);
2385 static void deleteSelectionAP(Widget w, XEvent *event, String *args,
2386 Cardinal *nArgs)
2388 XKeyEvent *e = &event->xkey;
2390 cancelDrag(w);
2391 if (checkReadOnly(w))
2392 return;
2393 TakeMotifDestination(w, e->time);
2394 deletePendingSelection(w, event);
2397 static void deletePreviousCharacterAP(Widget w, XEvent *event, String *args,
2398 Cardinal *nArgs)
2400 XKeyEvent *e = &event->xkey;
2401 textDisp *textD = ((TextWidget)w)->text.textD;
2402 int insertPos = TextDGetInsertPosition(textD);
2403 char c;
2404 int silent = hasKey("nobell", args, nArgs);
2406 cancelDrag(w);
2407 if (checkReadOnly(w))
2408 return;
2409 TakeMotifDestination(w, e->time);
2410 if (deletePendingSelection(w, event))
2411 return;
2412 if (insertPos == 0) {
2413 ringIfNecessary(silent, w);
2414 return;
2416 if (deleteEmulatedTab(w, event))
2417 return;
2418 if (((TextWidget)w)->text.overstrike) {
2419 c = BufGetCharacter(textD->buffer, insertPos - 1);
2420 if (c == '\n')
2421 BufRemove(textD->buffer, insertPos - 1, insertPos);
2422 else if (c != '\t')
2423 BufReplace(textD->buffer, insertPos - 1, insertPos, " ");
2424 } else {
2425 BufRemove(textD->buffer, insertPos - 1, insertPos);
2427 TextDSetInsertPosition(textD, insertPos - 1);
2428 checkAutoShowInsertPos(w);
2429 callCursorMovementCBs(w, event);
2432 static void deleteNextCharacterAP(Widget w, XEvent *event, String *args,
2433 Cardinal *nArgs)
2435 XKeyEvent *e = &event->xkey;
2436 textDisp *textD = ((TextWidget)w)->text.textD;
2437 int insertPos = TextDGetInsertPosition(textD);
2438 int silent = hasKey("nobell", args, nArgs);
2440 cancelDrag(w);
2441 if (checkReadOnly(w))
2442 return;
2443 TakeMotifDestination(w, e->time);
2444 if (deletePendingSelection(w, event))
2445 return;
2446 if (insertPos == textD->buffer->length) {
2447 ringIfNecessary(silent, w);
2448 return;
2450 BufRemove(textD->buffer, insertPos , insertPos + 1);
2451 checkAutoShowInsertPos(w);
2452 callCursorMovementCBs(w, event);
2455 static void deletePreviousWordAP(Widget w, XEvent *event, String *args,
2456 Cardinal *nArgs)
2458 XKeyEvent *e = &event->xkey;
2459 textDisp *textD = ((TextWidget)w)->text.textD;
2460 int insertPos = TextDGetInsertPosition(textD);
2461 int pos, lineStart = BufStartOfLine(textD->buffer, insertPos);
2462 char *delimiters = ((TextWidget)w)->text.delimiters;
2463 int silent = hasKey("nobell", args, nArgs);
2465 cancelDrag(w);
2466 if (checkReadOnly(w))
2467 return;
2468 TakeMotifDestination(w, e->time);
2469 if (deletePendingSelection(w, event))
2470 return;
2471 if (insertPos == lineStart) {
2472 ringIfNecessary(silent, w);
2473 return;
2475 pos = max(insertPos - 1, 0);
2476 while (strchr(delimiters, BufGetCharacter(textD->buffer, pos)) != NULL &&
2477 pos != lineStart)
2478 pos--;
2479 pos = startOfWord((TextWidget)w, pos);
2480 BufRemove(textD->buffer, pos, insertPos);
2481 checkAutoShowInsertPos(w);
2482 callCursorMovementCBs(w, event);
2485 static void deleteNextWordAP(Widget w, XEvent *event, String *args,
2486 Cardinal *nArgs)
2488 XKeyEvent *e = &event->xkey;
2489 textDisp *textD = ((TextWidget)w)->text.textD;
2490 int insertPos = TextDGetInsertPosition(textD);
2491 int pos, lineEnd = BufEndOfLine(textD->buffer, insertPos);
2492 char *delimiters = ((TextWidget)w)->text.delimiters;
2493 int silent = hasKey("nobell", args, nArgs);
2495 cancelDrag(w);
2496 if (checkReadOnly(w))
2497 return;
2498 TakeMotifDestination(w, e->time);
2499 if (deletePendingSelection(w, event))
2500 return;
2501 if (insertPos == lineEnd) {
2502 ringIfNecessary(silent, w);
2503 return;
2505 pos = insertPos;
2506 while (strchr(delimiters, BufGetCharacter(textD->buffer, pos)) != NULL &&
2507 pos != lineEnd)
2508 pos++;
2509 pos = endOfWord((TextWidget)w, pos);
2510 BufRemove(textD->buffer, insertPos, pos);
2511 checkAutoShowInsertPos(w);
2512 callCursorMovementCBs(w, event);
2515 static void deleteToEndOfLineAP(Widget w, XEvent *event, String *args,
2516 Cardinal *nArgs)
2518 XKeyEvent *e = &event->xkey;
2519 textDisp *textD = ((TextWidget)w)->text.textD;
2520 int insertPos = TextDGetInsertPosition(textD);
2521 int endOfLine;
2522 int silent = 0;
2524 silent = hasKey("nobell", args, nArgs);
2525 if (hasKey("absolute", args, nArgs))
2526 endOfLine = BufEndOfLine(textD->buffer, insertPos);
2527 else
2528 endOfLine = TextDEndOfLine(textD, insertPos, False);
2529 cancelDrag(w);
2530 if (checkReadOnly(w))
2531 return;
2532 TakeMotifDestination(w, e->time);
2533 if (deletePendingSelection(w, event))
2534 return;
2535 if (insertPos == endOfLine) {
2536 ringIfNecessary(silent, w);
2537 return;
2539 BufRemove(textD->buffer, insertPos, endOfLine);
2540 checkAutoShowInsertPos(w);
2541 callCursorMovementCBs(w, event);
2544 static void deleteToStartOfLineAP(Widget w, XEvent *event, String *args,
2545 Cardinal *nArgs)
2547 XKeyEvent *e = &event->xkey;
2548 textDisp *textD = ((TextWidget)w)->text.textD;
2549 int insertPos = TextDGetInsertPosition(textD);
2550 int startOfLine;
2551 int silent = 0;
2553 silent = hasKey("nobell", args, nArgs);
2554 if (hasKey("wrap", args, nArgs))
2555 startOfLine = TextDStartOfLine(textD, insertPos);
2556 else
2557 startOfLine = BufStartOfLine(textD->buffer, insertPos);
2558 cancelDrag(w);
2559 if (checkReadOnly(w))
2560 return;
2561 TakeMotifDestination(w, e->time);
2562 if (deletePendingSelection(w, event))
2563 return;
2564 if (insertPos == startOfLine) {
2565 ringIfNecessary(silent, w);
2566 return;
2568 BufRemove(textD->buffer, startOfLine, insertPos);
2569 checkAutoShowInsertPos(w);
2570 callCursorMovementCBs(w, event);
2573 static void forwardCharacterAP(Widget w, XEvent *event, String *args,
2574 Cardinal *nArgs)
2576 int insertPos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
2577 int silent = hasKey("nobell", args, nArgs);
2579 cancelDrag(w);
2580 if (!TextDMoveRight(((TextWidget)w)->text.textD))
2581 ringIfNecessary(silent, w);
2582 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2583 checkAutoShowInsertPos(w);
2584 callCursorMovementCBs(w, event);
2587 static void backwardCharacterAP(Widget w, XEvent *event, String *args,
2588 Cardinal *nArgs)
2590 int insertPos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
2591 int silent = hasKey("nobell", args, nArgs);
2593 cancelDrag(w);
2594 if (!TextDMoveLeft(((TextWidget)w)->text.textD))
2595 ringIfNecessary(silent, w);
2596 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2597 checkAutoShowInsertPos(w);
2598 callCursorMovementCBs(w, event);
2601 static void forwardWordAP(Widget w, XEvent *event, String *args,
2602 Cardinal *nArgs)
2604 textDisp *textD = ((TextWidget)w)->text.textD;
2605 textBuffer *buf = textD->buffer;
2606 int pos, insertPos = TextDGetInsertPosition(textD);
2607 char *delimiters = ((TextWidget)w)->text.delimiters;
2608 int silent = hasKey("nobell", args, nArgs);
2610 cancelDrag(w);
2611 if (insertPos == buf->length) {
2612 ringIfNecessary(silent, w);
2613 return;
2615 pos = insertPos;
2616 if (hasKey("tail", args, nArgs)) {
2617 for (; pos<buf->length; pos++) {
2618 if (strchr(delimiters, BufGetCharacter(buf, pos)) == NULL)
2619 break;
2621 if (strchr(delimiters, BufGetCharacter(buf, pos)) == NULL)
2622 pos = endOfWord((TextWidget)w, pos);
2624 else {
2625 if (strchr(delimiters, BufGetCharacter(buf, pos)) == NULL)
2626 pos = endOfWord((TextWidget)w, pos);
2627 for (; pos<buf->length; pos++) {
2628 if (strchr(delimiters, BufGetCharacter(buf, pos)) == NULL)
2629 break;
2632 TextDSetInsertPosition(textD, pos);
2633 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2634 checkAutoShowInsertPos(w);
2635 callCursorMovementCBs(w, event);
2638 static void backwardWordAP(Widget w, XEvent *event, String *args,
2639 Cardinal *nArgs)
2641 textDisp *textD = ((TextWidget)w)->text.textD;
2642 textBuffer *buf = textD->buffer;
2643 int pos, insertPos = TextDGetInsertPosition(textD);
2644 char *delimiters = ((TextWidget)w)->text.delimiters;
2645 int silent = hasKey("nobell", args, nArgs);
2647 cancelDrag(w);
2648 if (insertPos == 0) {
2649 ringIfNecessary(silent, w);
2650 return;
2652 pos = max(insertPos - 1, 0);
2653 while (strchr(delimiters, BufGetCharacter(buf, pos)) != NULL && pos > 0)
2654 pos--;
2655 pos = startOfWord((TextWidget)w, pos);
2657 TextDSetInsertPosition(textD, pos);
2658 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2659 checkAutoShowInsertPos(w);
2660 callCursorMovementCBs(w, event);
2663 static void forwardParagraphAP(Widget w, XEvent *event, String *args,
2664 Cardinal *nArgs)
2666 textDisp *textD = ((TextWidget)w)->text.textD;
2667 int pos, insertPos = TextDGetInsertPosition(textD);
2668 textBuffer *buf = textD->buffer;
2669 char c;
2670 static char whiteChars[] = " \t";
2671 int silent = hasKey("nobell", args, nArgs);
2673 cancelDrag(w);
2674 if (insertPos == buf->length) {
2675 ringIfNecessary(silent, w);
2676 return;
2678 pos = min(BufEndOfLine(buf, insertPos)+1, buf->length);
2679 while (pos < buf->length) {
2680 c = BufGetCharacter(buf, pos);
2681 if (c == '\n')
2682 break;
2683 if (strchr(whiteChars, c) != NULL)
2684 pos++;
2685 else
2686 pos = min(BufEndOfLine(buf, pos)+1, buf->length);
2688 TextDSetInsertPosition(textD, min(pos+1, buf->length));
2689 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2690 checkAutoShowInsertPos(w);
2691 callCursorMovementCBs(w, event);
2694 static void backwardParagraphAP(Widget w, XEvent *event, String *args,
2695 Cardinal *nArgs)
2697 textDisp *textD = ((TextWidget)w)->text.textD;
2698 int parStart, pos, insertPos = TextDGetInsertPosition(textD);
2699 textBuffer *buf = textD->buffer;
2700 char c;
2701 static char whiteChars[] = " \t";
2702 int silent = hasKey("nobell", args, nArgs);
2704 cancelDrag(w);
2705 if (insertPos == 0) {
2706 ringIfNecessary(silent, w);
2707 return;
2709 parStart = BufStartOfLine(buf, max(insertPos-1, 0));
2710 pos = max(parStart - 2, 0);
2711 while (pos > 0) {
2712 c = BufGetCharacter(buf, pos);
2713 if (c == '\n')
2714 break;
2715 if (strchr(whiteChars, c) != NULL)
2716 pos--;
2717 else {
2718 parStart = BufStartOfLine(buf, pos);
2719 pos = max(parStart - 2, 0);
2722 TextDSetInsertPosition(textD, parStart);
2723 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2724 checkAutoShowInsertPos(w);
2725 callCursorMovementCBs(w, event);
2728 static void keySelectAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2730 textDisp *textD = ((TextWidget)w)->text.textD;
2731 int stat, insertPos = TextDGetInsertPosition(textD);
2732 int silent = hasKey("nobell", args, nArgs);
2734 cancelDrag(w);
2735 if (hasKey("left", args, nArgs)) stat = TextDMoveLeft(textD);
2736 else if (hasKey("right", args, nArgs)) stat = TextDMoveRight(textD);
2737 else if (hasKey("up", args, nArgs)) stat = TextDMoveUp(textD, 0);
2738 else if (hasKey("down", args, nArgs)) stat = TextDMoveDown(textD, 0);
2739 else {
2740 keyMoveExtendSelection(w, event, insertPos, hasKey("rect", args,nArgs));
2741 return;
2743 if (!stat) {
2744 ringIfNecessary(silent, w);
2746 else {
2747 keyMoveExtendSelection(w, event, insertPos, hasKey("rect", args,nArgs));
2748 checkAutoShowInsertPos(w);
2749 callCursorMovementCBs(w, event);
2753 static void processUpAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2755 int insertPos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
2756 int silent = hasKey("nobell", args, nArgs);
2757 int abs = hasKey("absolute", args, nArgs);
2759 cancelDrag(w);
2760 if (!TextDMoveUp(((TextWidget)w)->text.textD, abs))
2761 ringIfNecessary(silent, w);
2762 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2763 checkAutoShowInsertPos(w);
2764 callCursorMovementCBs(w, event);
2767 static void processShiftUpAP(Widget w, XEvent *event, String *args,
2768 Cardinal *nArgs)
2770 int insertPos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
2771 int silent = hasKey("nobell", args, nArgs);
2772 int abs = hasKey("absolute", args, nArgs);
2774 cancelDrag(w);
2775 if (!TextDMoveUp(((TextWidget)w)->text.textD, abs))
2776 ringIfNecessary(silent, w);
2777 keyMoveExtendSelection(w, event, insertPos, hasKey("rect", args, nArgs));
2778 checkAutoShowInsertPos(w);
2779 callCursorMovementCBs(w, event);
2782 static void processDownAP(Widget w, XEvent *event, String *args,
2783 Cardinal *nArgs)
2785 int insertPos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
2786 int silent = hasKey("nobell", args, nArgs);
2787 int abs = hasKey("absolute", args, nArgs);
2789 cancelDrag(w);
2790 if (!TextDMoveDown(((TextWidget)w)->text.textD, abs))
2791 ringIfNecessary(silent, w);
2792 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2793 checkAutoShowInsertPos(w);
2794 callCursorMovementCBs(w, event);
2797 static void processShiftDownAP(Widget w, XEvent *event, String *args,
2798 Cardinal *nArgs)
2800 int insertPos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
2801 int silent = hasKey("nobell", args, nArgs);
2802 int abs = hasKey("absolute", args, nArgs);
2804 cancelDrag(w);
2805 if (!TextDMoveDown(((TextWidget)w)->text.textD, abs))
2806 ringIfNecessary(silent, w);
2807 keyMoveExtendSelection(w, event, insertPos, hasKey("rect", args, nArgs));
2808 checkAutoShowInsertPos(w);
2809 callCursorMovementCBs(w, event);
2812 static void beginningOfLineAP(Widget w, XEvent *event, String *args,
2813 Cardinal *nArgs)
2815 textDisp *textD = ((TextWidget)w)->text.textD;
2816 int insertPos = TextDGetInsertPosition(textD);
2818 cancelDrag(w);
2819 if (hasKey("absolute", args, nArgs))
2820 TextDSetInsertPosition(textD, BufStartOfLine(textD->buffer, insertPos));
2821 else
2822 TextDSetInsertPosition(textD, TextDStartOfLine(textD, insertPos));
2823 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2824 checkAutoShowInsertPos(w);
2825 callCursorMovementCBs(w, event);
2826 textD->cursorPreferredCol = 0;
2829 static void endOfLineAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2831 textDisp *textD = ((TextWidget)w)->text.textD;
2832 int insertPos = TextDGetInsertPosition(textD);
2834 cancelDrag(w);
2835 if (hasKey("absolute", args, nArgs))
2836 TextDSetInsertPosition(textD, BufEndOfLine(textD->buffer, insertPos));
2837 else
2838 TextDSetInsertPosition(textD, TextDEndOfLine(textD, insertPos, False));
2839 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2840 checkAutoShowInsertPos(w);
2841 callCursorMovementCBs(w, event);
2842 textD->cursorPreferredCol = -1;
2845 static void beginningOfFileAP(Widget w, XEvent *event, String *args,
2846 Cardinal *nArgs)
2848 int insertPos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
2849 textDisp *textD = ((TextWidget)w)->text.textD;
2851 cancelDrag(w);
2852 if (hasKey("scrollbar", args, nArgs)) {
2853 if (textD->topLineNum != 1) {
2854 TextDSetScroll(textD, 1, textD->horizOffset);
2857 else {
2858 TextDSetInsertPosition(((TextWidget)w)->text.textD, 0);
2859 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2860 checkAutoShowInsertPos(w);
2861 callCursorMovementCBs(w, event);
2865 static void endOfFileAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2867 textDisp *textD = ((TextWidget)w)->text.textD;
2868 int insertPos = TextDGetInsertPosition(textD);
2869 int lastTopLine;
2871 cancelDrag(w);
2872 if (hasKey("scrollbar", args, nArgs)) {
2873 lastTopLine = max(1,
2874 textD->nBufferLines - (textD->nVisibleLines - 2) +
2875 ((TextWidget)w)->text.cursorVPadding);
2876 if (lastTopLine != textD->topLineNum) {
2877 TextDSetScroll(textD, lastTopLine, textD->horizOffset);
2880 else {
2881 TextDSetInsertPosition(textD, textD->buffer->length);
2882 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2883 checkAutoShowInsertPos(w);
2884 callCursorMovementCBs(w, event);
2888 static void nextPageAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2890 textDisp *textD = ((TextWidget)w)->text.textD;
2891 textBuffer *buf = textD->buffer;
2892 int lastTopLine = max(1,
2893 textD->nBufferLines - (textD->nVisibleLines - 2) +
2894 ((TextWidget)w)->text.cursorVPadding );
2895 int insertPos = TextDGetInsertPosition(textD);
2896 int column = 0, visLineNum, lineStartPos;
2897 int pos, targetLine;
2898 int pageForwardCount = max(1, textD->nVisibleLines - 1);
2899 int maintainColumn = 0;
2900 int silent = hasKey("nobell", args, nArgs);
2902 maintainColumn = hasKey("column", args, nArgs);
2903 cancelDrag(w);
2904 if (hasKey("scrollbar", args, nArgs)) { /* scrollbar only */
2905 targetLine = min(textD->topLineNum + pageForwardCount, lastTopLine);
2907 if (targetLine == textD->topLineNum) {
2908 ringIfNecessary(silent, w);
2909 return;
2911 TextDSetScroll(textD, targetLine, textD->horizOffset);
2913 else if (hasKey("stutter", args, nArgs)) { /* Mac style */
2914 /* move to bottom line of visible area */
2915 /* if already there, page down maintaining preferrred column */
2916 targetLine = max(min(textD->nVisibleLines - 1, textD->nBufferLines), 0);
2917 column = TextDPreferredColumn(textD, &visLineNum, &lineStartPos);
2918 if (lineStartPos == textD->lineStarts[targetLine]) {
2919 if (insertPos >= buf->length || textD->topLineNum == lastTopLine) {
2920 ringIfNecessary(silent, w);
2921 return;
2923 targetLine = min(textD->topLineNum + pageForwardCount, lastTopLine);
2924 pos = TextDCountForwardNLines(textD, insertPos, pageForwardCount, False);
2925 if (maintainColumn) {
2926 pos = TextDPosOfPreferredCol(textD, column, pos);
2928 TextDSetInsertPosition(textD, pos);
2929 TextDSetScroll(textD, targetLine, textD->horizOffset);
2931 else {
2932 pos = textD->lineStarts[targetLine];
2933 while (targetLine > 0 && pos == -1) {
2934 --targetLine;
2935 pos = textD->lineStarts[targetLine];
2937 if (lineStartPos == pos) {
2938 ringIfNecessary(silent, w);
2939 return;
2941 if (maintainColumn) {
2942 pos = TextDPosOfPreferredCol(textD, column, pos);
2944 TextDSetInsertPosition(textD, pos);
2946 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2947 checkAutoShowInsertPos(w);
2948 callCursorMovementCBs(w, event);
2949 if (maintainColumn) {
2950 textD->cursorPreferredCol = column;
2952 else {
2953 textD->cursorPreferredCol = -1;
2956 else { /* "standard" */
2957 if (insertPos >= buf->length && textD->topLineNum == lastTopLine) {
2958 ringIfNecessary(silent, w);
2959 return;
2961 if (maintainColumn) {
2962 column = TextDPreferredColumn(textD, &visLineNum, &lineStartPos);
2964 targetLine = textD->topLineNum + textD->nVisibleLines - 1;
2965 if (targetLine < 1) targetLine = 1;
2966 if (targetLine > lastTopLine) targetLine = lastTopLine;
2967 pos = TextDCountForwardNLines(textD, insertPos, textD->nVisibleLines-1, False);
2968 if (maintainColumn) {
2969 pos = TextDPosOfPreferredCol(textD, column, pos);
2971 TextDSetInsertPosition(textD, pos);
2972 TextDSetScroll(textD, targetLine, textD->horizOffset);
2973 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2974 checkAutoShowInsertPos(w);
2975 callCursorMovementCBs(w, event);
2976 if (maintainColumn) {
2977 textD->cursorPreferredCol = column;
2979 else {
2980 textD->cursorPreferredCol = -1;
2985 static void previousPageAP(Widget w, XEvent *event, String *args,
2986 Cardinal *nArgs)
2988 textDisp *textD = ((TextWidget)w)->text.textD;
2989 int insertPos = TextDGetInsertPosition(textD);
2990 int column = 0, visLineNum, lineStartPos;
2991 int pos, targetLine;
2992 int pageBackwardCount = max(1, textD->nVisibleLines - 1);
2993 int maintainColumn = 0;
2994 int silent = hasKey("nobell", args, nArgs);
2996 maintainColumn = hasKey("column", args, nArgs);
2997 cancelDrag(w);
2998 if (hasKey("scrollbar", args, nArgs)) { /* scrollbar only */
2999 targetLine = max(textD->topLineNum - pageBackwardCount, 1);
3001 if (targetLine == textD->topLineNum) {
3002 ringIfNecessary(silent, w);
3003 return;
3005 TextDSetScroll(textD, targetLine, textD->horizOffset);
3007 else if (hasKey("stutter", args, nArgs)) { /* Mac style */
3008 /* move to top line of visible area */
3009 /* if already there, page up maintaining preferrred column if required */
3010 targetLine = 0;
3011 column = TextDPreferredColumn(textD, &visLineNum, &lineStartPos);
3012 if (lineStartPos == textD->lineStarts[targetLine]) {
3013 if (textD->topLineNum == 1 && (maintainColumn || column == 0)) {
3014 ringIfNecessary(silent, w);
3015 return;
3017 targetLine = max(textD->topLineNum - pageBackwardCount, 1);
3018 pos = TextDCountBackwardNLines(textD, insertPos, pageBackwardCount);
3019 if (maintainColumn) {
3020 pos = TextDPosOfPreferredCol(textD, column, pos);
3022 TextDSetInsertPosition(textD, pos);
3023 TextDSetScroll(textD, targetLine, textD->horizOffset);
3025 else {
3026 pos = textD->lineStarts[targetLine];
3027 if (maintainColumn) {
3028 pos = TextDPosOfPreferredCol(textD, column, pos);
3030 TextDSetInsertPosition(textD, pos);
3032 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
3033 checkAutoShowInsertPos(w);
3034 callCursorMovementCBs(w, event);
3035 if (maintainColumn) {
3036 textD->cursorPreferredCol = column;
3038 else {
3039 textD->cursorPreferredCol = -1;
3042 else { /* "standard" */
3043 if (insertPos <= 0 && textD->topLineNum == 1) {
3044 ringIfNecessary(silent, w);
3045 return;
3047 if (maintainColumn) {
3048 column = TextDPreferredColumn(textD, &visLineNum, &lineStartPos);
3050 targetLine = textD->topLineNum - (textD->nVisibleLines - 1);
3051 if (targetLine < 1) targetLine = 1;
3052 pos = TextDCountBackwardNLines(textD, insertPos, textD->nVisibleLines-1);
3053 if (maintainColumn) {
3054 pos = TextDPosOfPreferredCol(textD, column, pos);
3056 TextDSetInsertPosition(textD, pos);
3057 TextDSetScroll(textD, targetLine, textD->horizOffset);
3058 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
3059 checkAutoShowInsertPos(w);
3060 callCursorMovementCBs(w, event);
3061 if (maintainColumn) {
3062 textD->cursorPreferredCol = column;
3064 else {
3065 textD->cursorPreferredCol = -1;
3070 static void pageLeftAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
3072 textDisp *textD = ((TextWidget)w)->text.textD;
3073 textBuffer *buf = textD->buffer;
3074 int insertPos = TextDGetInsertPosition(textD);
3075 int maxCharWidth = textD->fontStruct->max_bounds.width;
3076 int lineStartPos, indent, pos;
3077 int horizOffset;
3078 int silent = hasKey("nobell", args, nArgs);
3080 cancelDrag(w);
3081 if (hasKey("scrollbar", args, nArgs)) {
3082 if (textD->horizOffset == 0) {
3083 ringIfNecessary(silent, w);
3084 return;
3086 horizOffset = max(0, textD->horizOffset - textD->width);
3087 TextDSetScroll(textD, textD->topLineNum, horizOffset);
3089 else {
3090 lineStartPos = BufStartOfLine(buf, insertPos);
3091 if (insertPos == lineStartPos && textD->horizOffset == 0) {
3092 ringIfNecessary(silent, w);
3093 return;
3095 indent = BufCountDispChars(buf, lineStartPos, insertPos);
3096 pos = BufCountForwardDispChars(buf, lineStartPos,
3097 max(0, indent - textD->width / maxCharWidth));
3098 TextDSetInsertPosition(textD, pos);
3099 TextDSetScroll(textD, textD->topLineNum,
3100 max(0, textD->horizOffset - textD->width));
3101 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
3102 checkAutoShowInsertPos(w);
3103 callCursorMovementCBs(w, event);
3107 static void pageRightAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
3109 textDisp *textD = ((TextWidget)w)->text.textD;
3110 textBuffer *buf = textD->buffer;
3111 int insertPos = TextDGetInsertPosition(textD);
3112 int maxCharWidth = textD->fontStruct->max_bounds.width;
3113 int oldHorizOffset = textD->horizOffset;
3114 int lineStartPos, indent, pos;
3115 int horizOffset, sliderSize, sliderMax;
3116 int silent = hasKey("nobell", args, nArgs);
3118 cancelDrag(w);
3119 if (hasKey("scrollbar", args, nArgs)) {
3120 XtVaGetValues(textD->hScrollBar, XmNmaximum, &sliderMax,
3121 XmNsliderSize, &sliderSize, 0);
3122 horizOffset = min(textD->horizOffset + textD->width, sliderMax - sliderSize);
3123 if (textD->horizOffset == horizOffset) {
3124 ringIfNecessary(silent, w);
3125 return;
3127 TextDSetScroll(textD, textD->topLineNum, horizOffset);
3129 else {
3130 lineStartPos = BufStartOfLine(buf, insertPos);
3131 indent = BufCountDispChars(buf, lineStartPos, insertPos);
3132 pos = BufCountForwardDispChars(buf, lineStartPos,
3133 indent + textD->width / maxCharWidth);
3134 TextDSetInsertPosition(textD, pos);
3135 TextDSetScroll(textD, textD->topLineNum, textD->horizOffset + textD->width);
3136 if (textD->horizOffset == oldHorizOffset && insertPos == pos)
3137 ringIfNecessary(silent, w);
3138 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
3139 checkAutoShowInsertPos(w);
3140 callCursorMovementCBs(w, event);
3144 static void toggleOverstrikeAP(Widget w, XEvent *event, String *args,
3145 Cardinal *nArgs)
3147 TextWidget tw = (TextWidget)w;
3149 if (tw->text.overstrike) {
3150 tw->text.overstrike = False;
3151 TextDSetCursorStyle(tw->text.textD,
3152 tw->text.heavyCursor ? HEAVY_CURSOR : NORMAL_CURSOR);
3153 } else {
3154 tw->text.overstrike = True;
3155 if ( tw->text.textD->cursorStyle == NORMAL_CURSOR ||
3156 tw->text.textD->cursorStyle == HEAVY_CURSOR)
3157 TextDSetCursorStyle(tw->text.textD, BLOCK_CURSOR);
3161 static void scrollUpAP(Widget w, XEvent *event, String *args,
3162 Cardinal *nArgs)
3164 textDisp *textD = ((TextWidget)w)->text.textD;
3165 int topLineNum, horizOffset, nLines;
3167 if (*nArgs == 0 || sscanf(args[0], "%d", &nLines) != 1)
3168 return;
3169 TextDGetScroll(textD, &topLineNum, &horizOffset);
3170 TextDSetScroll(textD, topLineNum-nLines, horizOffset);
3173 static void scrollDownAP(Widget w, XEvent *event, String *args,
3174 Cardinal *nArgs)
3176 textDisp *textD = ((TextWidget)w)->text.textD;
3177 int topLineNum, horizOffset, nLines;
3179 if (*nArgs == 0 || sscanf(args[0], "%d", &nLines) != 1)
3180 return;
3181 TextDGetScroll(textD, &topLineNum, &horizOffset);
3182 TextDSetScroll(textD, topLineNum+nLines, horizOffset);
3185 static void scrollLeftAP(Widget w, XEvent *event, String *args,
3186 Cardinal *nArgs)
3188 textDisp *textD = ((TextWidget)w)->text.textD;
3189 int horizOffset, nPixels;
3190 int sliderMax, sliderSize;
3192 if (*nArgs == 0 || sscanf(args[0], "%d", &nPixels) != 1)
3193 return;
3194 XtVaGetValues(textD->hScrollBar, XmNmaximum, &sliderMax,
3195 XmNsliderSize, &sliderSize, 0);
3196 horizOffset = min(max(0, textD->horizOffset - nPixels), sliderMax - sliderSize);
3197 if (textD->horizOffset != horizOffset) {
3198 TextDSetScroll(textD, textD->topLineNum, horizOffset);
3202 static void scrollRightAP(Widget w, XEvent *event, String *args,
3203 Cardinal *nArgs)
3205 textDisp *textD = ((TextWidget)w)->text.textD;
3206 int horizOffset, nPixels;
3207 int sliderMax, sliderSize;
3209 if (*nArgs == 0 || sscanf(args[0], "%d", &nPixels) != 1)
3210 return;
3211 XtVaGetValues(textD->hScrollBar, XmNmaximum, &sliderMax,
3212 XmNsliderSize, &sliderSize, 0);
3213 horizOffset = min(max(0, textD->horizOffset + nPixels), sliderMax - sliderSize);
3214 if (textD->horizOffset != horizOffset) {
3215 TextDSetScroll(textD, textD->topLineNum, horizOffset);
3219 static void scrollToLineAP(Widget w, XEvent *event, String *args,
3220 Cardinal *nArgs)
3222 textDisp *textD = ((TextWidget)w)->text.textD;
3223 int topLineNum, horizOffset, lineNum;
3225 if (*nArgs == 0 || sscanf(args[0], "%d", &lineNum) != 1)
3226 return;
3227 TextDGetScroll(textD, &topLineNum, &horizOffset);
3228 TextDSetScroll(textD, lineNum, horizOffset);
3231 static void selectAllAP(Widget w, XEvent *event, String *args,
3232 Cardinal *nArgs)
3234 textBuffer *buf = ((TextWidget)w)->text.textD->buffer;
3236 cancelDrag(w);
3237 BufSelect(buf, 0, buf->length);
3240 static void deselectAllAP(Widget w, XEvent *event, String *args,
3241 Cardinal *nArgs)
3243 cancelDrag(w);
3244 BufUnselect(((TextWidget)w)->text.textD->buffer);
3247 static void focusInAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
3249 TextWidget tw = (TextWidget)w;
3250 textDisp *textD = tw->text.textD;
3252 /* I don't entirely understand the traversal mechanism in Motif widgets,
3253 particularly, what leads to this widget getting a focus-in event when
3254 it does not actually have the input focus. The temporary solution is
3255 to do the comparison below, and not show the cursor when Motif says
3256 we don't have focus, but keep looking for the real answer */
3257 #if XmVersion >= 1002
3258 if (w != XmGetFocusWidget(w))
3259 return;
3260 #endif
3262 /* If the timer is not already started, start it */
3263 if (tw->text.cursorBlinkRate != 0 && tw->text.cursorBlinkProcID == 0) {
3264 tw->text.cursorBlinkProcID = XtAppAddTimeOut(
3265 XtWidgetToApplicationContext((Widget)w),
3266 tw->text.cursorBlinkRate, cursorBlinkTimerProc, w);
3269 /* Change the cursor to active style */
3270 if (((TextWidget)w)->text.overstrike)
3271 TextDSetCursorStyle(textD, BLOCK_CURSOR);
3272 else
3273 TextDSetCursorStyle(textD, ((TextWidget)w)->text.heavyCursor ?
3274 HEAVY_CURSOR : NORMAL_CURSOR);
3275 TextDUnblankCursor(textD);
3277 #ifndef NO_XMIM
3278 /* Notify Motif input manager that widget has focus */
3279 XmImVaSetFocusValues(w,NULL);
3280 #endif
3282 /* Call any registered focus-in callbacks */
3283 XtCallCallbacks((Widget)w, textNfocusCallback, (XtPointer)event);
3286 static void focusOutAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
3288 textDisp *textD = ((TextWidget)w)->text.textD;
3290 /* Remove the cursor blinking timer procedure */
3291 if (((TextWidget)w)->text.cursorBlinkProcID != 0)
3292 XtRemoveTimeOut(((TextWidget)w)->text.cursorBlinkProcID);
3293 ((TextWidget)w)->text.cursorBlinkProcID = 0;
3295 /* Leave a dim or destination cursor */
3296 TextDSetCursorStyle(textD, ((TextWidget)w)->text.motifDestOwner ?
3297 CARET_CURSOR : DIM_CURSOR);
3298 TextDUnblankCursor(textD);
3300 /* If there's a calltip displayed, kill it. */
3301 TextDKillCalltip(textD, 0);
3303 /* Call any registered focus-out callbacks */
3304 XtCallCallbacks((Widget)w, textNlosingFocusCallback, (XtPointer)event);
3308 ** For actions involving cursor movement, "extend" keyword means incorporate
3309 ** the new cursor position in the selection, and lack of an "extend" keyword
3310 ** means cancel the existing selection
3312 static void checkMoveSelectionChange(Widget w, XEvent *event, int startPos,
3313 String *args, Cardinal *nArgs)
3315 if (hasKey("extend", args, nArgs))
3316 keyMoveExtendSelection(w, event, startPos, hasKey("rect", args, nArgs));
3317 else
3318 BufUnselect((((TextWidget)w)->text.textD)->buffer);
3322 ** If a selection change was requested via a keyboard command for moving
3323 ** the insertion cursor (usually with the "extend" keyword), adjust the
3324 ** selection to include the new cursor position, or begin a new selection
3325 ** between startPos and the new cursor position with anchor at startPos.
3327 static void keyMoveExtendSelection(Widget w, XEvent *event, int origPos,
3328 int rectangular)
3330 XKeyEvent *e = &event->xkey;
3331 TextWidget tw = (TextWidget)w;
3332 textDisp *textD = tw->text.textD;
3333 textBuffer *buf = textD->buffer;
3334 selection *sel = &buf->primary;
3335 int newPos = TextDGetInsertPosition(textD);
3336 int startPos, endPos, startCol, endCol, newCol, origCol;
3337 int anchor, rectAnchor, anchorLineStart;
3339 /* Moving the cursor does not take the Motif destination, but as soon as
3340 the user selects something, grab it (I'm not sure if this distinction
3341 actually makes sense, but it's what Motif was doing, back when their
3342 secondary selections actually worked correctly) */
3343 TakeMotifDestination(w, e->time);
3345 if ((sel->selected || sel->zeroWidth) && sel->rectangular && rectangular) {
3346 /* rect -> rect */
3347 newCol = BufCountDispChars(buf, BufStartOfLine(buf, newPos), newPos);
3348 startCol = min(tw->text.rectAnchor, newCol);
3349 endCol = max(tw->text.rectAnchor, newCol);
3350 startPos = BufStartOfLine(buf, min(tw->text.anchor, newPos));
3351 endPos = BufEndOfLine(buf, max(tw->text.anchor, newPos));
3352 BufRectSelect(buf, startPos, endPos, startCol, endCol);
3353 } else if (sel->selected && rectangular) { /* plain -> rect */
3354 newCol = BufCountDispChars(buf, BufStartOfLine(buf, newPos), newPos);
3355 if (abs(newPos - sel->start) < abs(newPos - sel->end))
3356 anchor = sel->end;
3357 else
3358 anchor = sel->start;
3359 anchorLineStart = BufStartOfLine(buf, anchor);
3360 rectAnchor = BufCountDispChars(buf, anchorLineStart, anchor);
3361 tw->text.anchor = anchor;
3362 tw->text.rectAnchor = rectAnchor;
3363 BufRectSelect(buf, BufStartOfLine(buf, min(anchor, newPos)),
3364 BufEndOfLine(buf, max(anchor, newPos)),
3365 min(rectAnchor, newCol), max(rectAnchor, newCol));
3366 } else if (sel->selected && sel->rectangular) { /* rect -> plain */
3367 startPos = BufCountForwardDispChars(buf,
3368 BufStartOfLine(buf, sel->start), sel->rectStart);
3369 endPos = BufCountForwardDispChars(buf,
3370 BufStartOfLine(buf, sel->end), sel->rectEnd);
3371 if (abs(origPos - startPos) < abs(origPos - endPos))
3372 anchor = endPos;
3373 else
3374 anchor = startPos;
3375 BufSelect(buf, anchor, newPos);
3376 } else if (sel->selected) { /* plain -> plain */
3377 if (abs(origPos - sel->start) < abs(origPos - sel->end))
3378 anchor = sel->end;
3379 else
3380 anchor = sel->start;
3381 BufSelect(buf, anchor, newPos);
3382 } else if (rectangular) { /* no sel -> rect */
3383 origCol = BufCountDispChars(buf, BufStartOfLine(buf, origPos), origPos);
3384 newCol = BufCountDispChars(buf, BufStartOfLine(buf, newPos), newPos);
3385 startCol = min(newCol, origCol);
3386 endCol = max(newCol, origCol);
3387 startPos = BufStartOfLine(buf, min(origPos, newPos));
3388 endPos = BufEndOfLine(buf, max(origPos, newPos));
3389 tw->text.anchor = origPos;
3390 tw->text.rectAnchor = origCol;
3391 BufRectSelect(buf, startPos, endPos, startCol, endCol);
3392 } else { /* no sel -> plain */
3393 tw->text.anchor = origPos;
3394 tw->text.rectAnchor = BufCountDispChars(buf,
3395 BufStartOfLine(buf, origPos), origPos);
3396 BufSelect(buf, tw->text.anchor, newPos);
3400 static void checkAutoShowInsertPos(Widget w)
3402 if (((TextWidget)w)->text.autoShowInsertPos)
3403 TextDMakeInsertPosVisible(((TextWidget)w)->text.textD);
3406 static int checkReadOnly(Widget w)
3408 if (((TextWidget)w)->text.readOnly) {
3409 XBell(XtDisplay(w), 0);
3410 return True;
3412 return False;
3416 ** Insert text "chars" at the cursor position, as if the text had been
3417 ** typed. Same as TextInsertAtCursor, but without the complicated auto-wrap
3418 ** scanning and re-formatting.
3420 static void simpleInsertAtCursor(Widget w, char *chars, XEvent *event,
3421 int allowPendingDelete)
3423 textDisp *textD = ((TextWidget)w)->text.textD;
3424 textBuffer *buf = textD->buffer;
3425 char *c;
3427 if (allowPendingDelete && pendingSelection(w)) {
3428 BufReplaceSelected(buf, chars);
3429 TextDSetInsertPosition(textD, buf->cursorPosHint);
3430 } else if (((TextWidget)w)->text.overstrike) {
3431 for (c=chars; *c!='\0' && *c!='\n'; c++);
3432 if (*c == '\n')
3433 TextDInsert(textD, chars);
3434 else
3435 TextDOverstrike(textD, chars);
3436 } else
3437 TextDInsert(textD, chars);
3438 checkAutoShowInsertPos(w);
3439 callCursorMovementCBs(w, event);
3443 ** If there's a selection, delete it and position the cursor where the
3444 ** selection was deleted. (Called by routines which do deletion to check
3445 ** first for and do possible selection delete)
3447 static int deletePendingSelection(Widget w, XEvent *event)
3449 textDisp *textD = ((TextWidget)w)->text.textD;
3450 textBuffer *buf = textD->buffer;
3452 if (((TextWidget)w)->text.textD->buffer->primary.selected) {
3453 BufRemoveSelected(buf);
3454 TextDSetInsertPosition(textD, buf->cursorPosHint);
3455 checkAutoShowInsertPos(w);
3456 callCursorMovementCBs(w, event);
3457 return True;
3458 } else
3459 return False;
3463 ** Return true if pending delete is on and there's a selection contiguous
3464 ** with the cursor ready to be deleted. These criteria are used to decide
3465 ** if typing a character or inserting something should delete the selection
3466 ** first.
3468 static int pendingSelection(Widget w)
3470 selection *sel = &((TextWidget)w)->text.textD->buffer->primary;
3471 int pos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
3473 return ((TextWidget)w)->text.pendingDelete && sel->selected &&
3474 pos >= sel->start && pos <= sel->end;
3478 ** Check if tab emulation is on and if there are emulated tabs before the
3479 ** cursor, and if so, delete an emulated tab as a unit. Also finishes up
3480 ** by calling checkAutoShowInsertPos and callCursorMovementCBs, so the
3481 ** calling action proc can just return (this is necessary to preserve
3482 ** emTabsBeforeCursor which is otherwise cleared by callCursorMovementCBs).
3484 static int deleteEmulatedTab(Widget w, XEvent *event)
3486 textDisp *textD = ((TextWidget)w)->text.textD;
3487 textBuffer *buf = ((TextWidget)w)->text.textD->buffer;
3488 int emTabDist = ((TextWidget)w)->text.emulateTabs;
3489 int emTabsBeforeCursor = ((TextWidget)w)->text.emTabsBeforeCursor;
3490 int startIndent, toIndent, insertPos, startPos, lineStart;
3491 int pos, indent, startPosIndent;
3492 char c, *spaceString;
3494 if (emTabDist <= 0 || emTabsBeforeCursor <= 0)
3495 return False;
3497 /* Find the position of the previous tab stop */
3498 insertPos = TextDGetInsertPosition(textD);
3499 lineStart = BufStartOfLine(buf, insertPos);
3500 startIndent = BufCountDispChars(buf, lineStart, insertPos);
3501 toIndent = (startIndent-1) - ((startIndent-1) % emTabDist);
3503 /* Find the position at which to begin deleting (stop at non-whitespace
3504 characters) */
3505 startPosIndent = indent = 0;
3506 startPos = lineStart;
3507 for (pos=lineStart; pos < insertPos; pos++) {
3508 c = BufGetCharacter(buf, pos);
3509 indent += BufCharWidth(c, indent, buf->tabDist, buf->nullSubsChar);
3510 if (indent > toIndent)
3511 break;
3512 startPosIndent = indent;
3513 startPos = pos + 1;
3516 /* Just to make sure, check that we're not deleting any non-white chars */
3517 for (pos=insertPos-1; pos>=startPos; pos--) {
3518 c = BufGetCharacter(buf, pos);
3519 if (c != ' ' && c != '\t') {
3520 startPos = pos + 1;
3521 break;
3525 /* Do the text replacement and reposition the cursor. If any spaces need
3526 to be inserted to make up for a deleted tab, do a BufReplace, otherwise,
3527 do a BufRemove. */
3528 if (startPosIndent < toIndent) {
3529 spaceString = XtMalloc(toIndent - startPosIndent + 1);
3530 memset(spaceString, ' ', toIndent-startPosIndent);
3531 spaceString[toIndent - startPosIndent] = '\0';
3532 BufReplace(buf, startPos, insertPos, spaceString);
3533 TextDSetInsertPosition(textD, startPos + toIndent - startPosIndent);
3534 XtFree(spaceString);
3535 } else {
3536 BufRemove(buf, startPos, insertPos);
3537 TextDSetInsertPosition(textD, startPos);
3540 /* The normal cursor movement stuff would usually be called by the action
3541 routine, but this wraps around it to restore emTabsBeforeCursor */
3542 checkAutoShowInsertPos(w);
3543 callCursorMovementCBs(w, event);
3545 /* Decrement and restore the marker for consecutive emulated tabs, which
3546 would otherwise have been zeroed by callCursorMovementCBs */
3547 ((TextWidget)w)->text.emTabsBeforeCursor = emTabsBeforeCursor - 1;
3548 return True;
3552 ** Select the word or whitespace adjacent to the cursor, and move the cursor
3553 ** to its end. pointerX is used as a tie-breaker, when the cursor is at the
3554 ** boundary between a word and some white-space. If the cursor is on the
3555 ** left, the word or space on the left is used. If it's on the right, that
3556 ** is used instead.
3558 static void selectWord(Widget w, int pointerX)
3560 TextWidget tw = (TextWidget)w;
3561 textBuffer *buf = tw->text.textD->buffer;
3562 int x, y, insertPos = TextDGetInsertPosition(tw->text.textD);
3564 TextPosToXY(w, insertPos, &x, &y);
3565 if (pointerX < x && insertPos > 0 && BufGetCharacter(buf, insertPos-1) != '\n')
3566 insertPos--;
3567 BufSelect(buf, startOfWord(tw, insertPos), endOfWord(tw, insertPos));
3570 static int startOfWord(TextWidget w, int pos)
3572 int startPos;
3573 textBuffer *buf = w->text.textD->buffer;
3574 char *delimiters=w->text.delimiters;
3575 char c = BufGetCharacter(buf, pos);
3577 if (c == ' ' || c== '\t') {
3578 if (!spanBackward(buf, pos, " \t", False, &startPos))
3579 return 0;
3580 } else if (strchr(delimiters, c)) {
3581 if (!spanBackward(buf, pos, delimiters, True, &startPos))
3582 return 0;
3583 } else {
3584 if (!BufSearchBackward(buf, pos, delimiters, &startPos))
3585 return 0;
3587 return min(pos, startPos+1);
3591 static int endOfWord(TextWidget w, int pos)
3593 int endPos;
3594 textBuffer *buf = w->text.textD->buffer;
3595 char *delimiters=w->text.delimiters;
3596 char c = BufGetCharacter(buf, pos);
3598 if (c == ' ' || c== '\t') {
3599 if (!spanForward(buf, pos, " \t", False, &endPos))
3600 return buf->length;
3601 } else if (strchr(delimiters, c)) {
3602 if (!spanForward(buf, pos, delimiters, True, &endPos))
3603 return buf->length;
3604 } else {
3605 if (!BufSearchForward(buf, pos, delimiters, &endPos))
3606 return buf->length;
3608 return endPos;
3612 ** Search forwards in buffer "buf" for the first character NOT in
3613 ** "searchChars", starting with the character "startPos", and returning the
3614 ** result in "foundPos" returns True if found, False if not. If ignoreSpace
3615 ** is set, then Space, Tab, and Newlines are ignored in searchChars.
3617 static int spanForward(textBuffer *buf, int startPos, char *searchChars,
3618 int ignoreSpace, int *foundPos)
3620 int pos;
3621 char *c;
3623 pos = startPos;
3624 while (pos < buf->length) {
3625 for (c=searchChars; *c!='\0'; c++)
3626 if(!(ignoreSpace && (*c==' ' || *c=='\t' || *c=='\n')))
3627 if (BufGetCharacter(buf, pos) == *c)
3628 break;
3629 if(*c == 0) {
3630 *foundPos = pos;
3631 return True;
3633 pos++;
3635 *foundPos = buf->length;
3636 return False;
3640 ** Search backwards in buffer "buf" for the first character NOT in
3641 ** "searchChars", starting with the character BEFORE "startPos", returning the
3642 ** result in "foundPos" returns True if found, False if not. If ignoreSpace is
3643 ** set, then Space, Tab, and Newlines are ignored in searchChars.
3645 static int spanBackward(textBuffer *buf, int startPos, char *searchChars, int
3646 ignoreSpace, int *foundPos)
3648 int pos;
3649 char *c;
3651 if (startPos == 0) {
3652 *foundPos = 0;
3653 return False;
3655 pos = startPos == 0 ? 0 : startPos - 1;
3656 while (pos >= 0) {
3657 for (c=searchChars; *c!='\0'; c++)
3658 if(!(ignoreSpace && (*c==' ' || *c=='\t' || *c=='\n')))
3659 if (BufGetCharacter(buf, pos) == *c)
3660 break;
3661 if(*c == 0) {
3662 *foundPos = pos;
3663 return True;
3665 pos--;
3667 *foundPos = 0;
3668 return False;
3672 ** Select the line containing the cursor, including the terminating newline,
3673 ** and move the cursor to its end.
3675 static void selectLine(Widget w)
3677 textDisp *textD = ((TextWidget)w)->text.textD;
3678 int insertPos = TextDGetInsertPosition(textD);
3679 int endPos, startPos;
3681 endPos = BufEndOfLine(textD->buffer, insertPos);
3682 startPos = BufStartOfLine(textD->buffer, insertPos);
3683 BufSelect(textD->buffer, startPos, min(endPos + 1, textD->buffer->length));
3684 TextDSetInsertPosition(textD, endPos);
3688 ** Given a new mouse pointer location, pass the position on to the
3689 ** autoscroll timer routine, and make sure the timer is on when it's
3690 ** needed and off when it's not.
3692 static void checkAutoScroll(TextWidget w, int x, int y)
3694 int inWindow;
3696 /* Is the pointer in or out of the window? */
3697 inWindow = x >= w->text.textD->left &&
3698 x < w->core.width - w->text.marginWidth &&
3699 y >= w->text.marginHeight &&
3700 y < w->core.height - w->text.marginHeight;
3702 /* If it's in the window, cancel the timer procedure */
3703 if (inWindow) {
3704 if (w->text.autoScrollProcID != 0)
3705 XtRemoveTimeOut(w->text.autoScrollProcID);;
3706 w->text.autoScrollProcID = 0;
3707 return;
3710 /* If the timer is not already started, start it */
3711 if (w->text.autoScrollProcID == 0) {
3712 w->text.autoScrollProcID = XtAppAddTimeOut(
3713 XtWidgetToApplicationContext((Widget)w),
3714 0, autoScrollTimerProc, w);
3717 /* Pass on the newest mouse location to the autoscroll routine */
3718 w->text.mouseX = x;
3719 w->text.mouseY = y;
3723 ** Reset drag state and cancel the auto-scroll timer
3725 static void endDrag(Widget w)
3727 if (((TextWidget)w)->text.autoScrollProcID != 0)
3728 XtRemoveTimeOut(((TextWidget)w)->text.autoScrollProcID);
3729 ((TextWidget)w)->text.autoScrollProcID = 0;
3730 if (((TextWidget)w)->text.dragState == MOUSE_PAN)
3731 XUngrabPointer(XtDisplay(w), CurrentTime);
3732 ((TextWidget)w)->text.dragState = NOT_CLICKED;
3736 ** Cancel any drag operation that might be in progress. Should be included
3737 ** in nearly every key event to cleanly end any dragging before edits are made
3738 ** which might change the insert position or the content of the buffer during
3739 ** a drag operation)
3741 static void cancelDrag(Widget w)
3743 int dragState = ((TextWidget)w)->text.dragState;
3745 if (((TextWidget)w)->text.autoScrollProcID != 0)
3746 XtRemoveTimeOut(((TextWidget)w)->text.autoScrollProcID);
3747 if (dragState == SECONDARY_DRAG || dragState == SECONDARY_RECT_DRAG)
3748 BufSecondaryUnselect(((TextWidget)w)->text.textD->buffer);
3749 if (dragState == PRIMARY_BLOCK_DRAG)
3750 CancelBlockDrag((TextWidget)w);
3751 if (dragState == MOUSE_PAN)
3752 XUngrabPointer(XtDisplay(w), CurrentTime);
3753 if (dragState != NOT_CLICKED)
3754 ((TextWidget)w)->text.dragState = DRAG_CANCELED;
3758 ** Do operations triggered by cursor movement: Call cursor movement callback
3759 ** procedure(s), and cancel marker indicating that the cursor is after one or
3760 ** more just-entered emulated tabs (spaces to be deleted as a unit).
3762 static void callCursorMovementCBs(Widget w, XEvent *event)
3764 ((TextWidget)w)->text.emTabsBeforeCursor = 0;
3765 XtCallCallbacks((Widget)w, textNcursorMovementCallback, (XtPointer)event);
3769 ** Adjust the selection as the mouse is dragged to position: (x, y).
3771 static void adjustSelection(TextWidget tw, int x, int y)
3773 textDisp *textD = tw->text.textD;
3774 textBuffer *buf = textD->buffer;
3775 int row, col, startCol, endCol, startPos, endPos;
3776 int newPos = TextDXYToPosition(textD, x, y);
3778 /* Adjust the selection */
3779 if (tw->text.dragState == PRIMARY_RECT_DRAG) {
3780 TextDXYToUnconstrainedPosition(textD, x, y, &row, &col);
3781 col = TextDOffsetWrappedColumn(textD, row, col);
3782 startCol = min(tw->text.rectAnchor, col);
3783 endCol = max(tw->text.rectAnchor, col);
3784 startPos = BufStartOfLine(buf, min(tw->text.anchor, newPos));
3785 endPos = BufEndOfLine(buf, max(tw->text.anchor, newPos));
3786 BufRectSelect(buf, startPos, endPos, startCol, endCol);
3787 } else if (tw->text.multiClickState == ONE_CLICK) {
3788 startPos = startOfWord(tw, min(tw->text.anchor, newPos));
3789 endPos = endOfWord(tw, max(tw->text.anchor, newPos));
3790 BufSelect(buf, startPos, endPos);
3791 newPos = newPos < tw->text.anchor ? startPos : endPos;
3792 } else if (tw->text.multiClickState == TWO_CLICKS) {
3793 startPos = BufStartOfLine(buf, min(tw->text.anchor, newPos));
3794 endPos = BufEndOfLine(buf, max(tw->text.anchor, newPos));
3795 BufSelect(buf, startPos, min(endPos+1, buf->length));
3796 newPos = newPos < tw->text.anchor ? startPos : endPos;
3797 } else
3798 BufSelect(buf, tw->text.anchor, newPos);
3800 /* Move the cursor */
3801 TextDSetInsertPosition(textD, newPos);
3802 callCursorMovementCBs((Widget)tw, NULL);
3805 static void adjustSecondarySelection(TextWidget tw, int x, int y)
3807 textDisp *textD = tw->text.textD;
3808 textBuffer *buf = textD->buffer;
3809 int row, col, startCol, endCol, startPos, endPos;
3810 int newPos = TextDXYToPosition(textD, x, y);
3812 if (tw->text.dragState == SECONDARY_RECT_DRAG) {
3813 TextDXYToUnconstrainedPosition(textD, x, y, &row, &col);
3814 col = TextDOffsetWrappedColumn(textD, row, col);
3815 startCol = min(tw->text.rectAnchor, col);
3816 endCol = max(tw->text.rectAnchor, col);
3817 startPos = BufStartOfLine(buf, min(tw->text.anchor, newPos));
3818 endPos = BufEndOfLine(buf, max(tw->text.anchor, newPos));
3819 BufSecRectSelect(buf, startPos, endPos, startCol, endCol);
3820 } else
3821 BufSecondarySelect(textD->buffer, tw->text.anchor, newPos);
3825 ** Wrap multi-line text in argument "text" to be inserted at the end of the
3826 ** text on line "startLine" and return the result. If "breakBefore" is
3827 ** non-NULL, allow wrapping to extend back into "startLine", in which case
3828 ** the returned text will include the wrapped part of "startLine", and
3829 ** "breakBefore" will return the number of characters at the end of
3830 ** "startLine" that were absorbed into the returned string. "breakBefore"
3831 ** will return zero if no characters were absorbed into the returned string.
3832 ** The buffer offset of text in the widget's text buffer is needed so that
3833 ** smart indent (which can be triggered by wrapping) can search back farther
3834 ** in the buffer than just the text in startLine.
3836 static char *wrapText(TextWidget tw, char *startLine, char *text, int bufOffset,
3837 int wrapMargin, int *breakBefore)
3839 textBuffer *wrapBuf, *buf = tw->text.textD->buffer;
3840 int startLineLen = strlen(startLine);
3841 int colNum, pos, lineStartPos, limitPos, breakAt, charsAdded;
3842 int firstBreak = -1, tabDist = buf->tabDist;
3843 char c, *wrappedText;
3845 /* Create a temporary text buffer and load it with the strings */
3846 wrapBuf = BufCreate();
3847 BufInsert(wrapBuf, 0, startLine);
3848 BufInsert(wrapBuf, wrapBuf->length, text);
3850 /* Scan the buffer for long lines and apply wrapLine when wrapMargin is
3851 exceeded. limitPos enforces no breaks in the "startLine" part of the
3852 string (if requested), and prevents re-scanning of long unbreakable
3853 lines for each character beyond the margin */
3854 colNum = 0;
3855 pos = 0;
3856 lineStartPos = 0;
3857 limitPos = breakBefore == NULL ? startLineLen : 0;
3858 while (pos < wrapBuf->length) {
3859 c = BufGetCharacter(wrapBuf, pos);
3860 if (c == '\n') {
3861 lineStartPos = limitPos = pos + 1;
3862 colNum = 0;
3863 } else {
3864 colNum += BufCharWidth(c, colNum, tabDist, buf->nullSubsChar);
3865 if (colNum > wrapMargin) {
3866 if (!wrapLine(tw, wrapBuf, bufOffset, lineStartPos, pos,
3867 limitPos, &breakAt, &charsAdded)) {
3868 limitPos = max(pos, limitPos);
3869 } else {
3870 lineStartPos = limitPos = breakAt+1;
3871 pos += charsAdded;
3872 colNum = BufCountDispChars(wrapBuf, lineStartPos, pos+1);
3873 if (firstBreak == -1)
3874 firstBreak = breakAt;
3878 pos++;
3881 /* Return the wrapped text, possibly including part of startLine */
3882 if (breakBefore == NULL)
3883 wrappedText = BufGetRange(wrapBuf, startLineLen, wrapBuf->length);
3884 else {
3885 *breakBefore = firstBreak != -1 && firstBreak < startLineLen ?
3886 startLineLen - firstBreak : 0;
3887 wrappedText = BufGetRange(wrapBuf, startLineLen - *breakBefore,
3888 wrapBuf->length);
3890 BufFree(wrapBuf);
3891 return wrappedText;
3895 ** Wraps the end of a line beginning at lineStartPos and ending at lineEndPos
3896 ** in "buf", at the last white-space on the line >= limitPos. (The implicit
3897 ** assumption is that just the last character of the line exceeds the wrap
3898 ** margin, and anywhere on the line we can wrap is correct). Returns False if
3899 ** unable to wrap the line. "breakAt", returns the character position at
3900 ** which the line was broken,
3902 ** Auto-wrapping can also trigger auto-indent. The additional parameter
3903 ** bufOffset is needed when auto-indent is set to smart indent and the smart
3904 ** indent routines need to scan far back in the buffer. "charsAdded" returns
3905 ** the number of characters added to acheive the auto-indent. wrapMargin is
3906 ** used to decide whether auto-indent should be skipped because the indent
3907 ** string itself would exceed the wrap margin.
3909 static int wrapLine(TextWidget tw, textBuffer *buf, int bufOffset,
3910 int lineStartPos, int lineEndPos, int limitPos, int *breakAt,
3911 int *charsAdded)
3913 int p, length, column;
3914 char c, *indentStr;
3916 /* Scan backward for whitespace or BOL. If BOL, return False, no
3917 whitespace in line at which to wrap */
3918 for (p=lineEndPos; ; p--) {
3919 if (p < lineStartPos || p < limitPos)
3920 return False;
3921 c = BufGetCharacter(buf, p);
3922 if (c == '\t' || c == ' ')
3923 break;
3926 /* Create an auto-indent string to insert to do wrap. If the auto
3927 indent string reaches the wrap position, slice the auto-indent
3928 back off and return to the left margin */
3929 if (tw->text.autoIndent || tw->text.smartIndent) {
3930 indentStr = createIndentString(tw, buf, bufOffset, lineStartPos,
3931 lineEndPos, &length, &column);
3932 if (column >= p-lineStartPos)
3933 indentStr[1] = '\0';
3934 } else {
3935 indentStr = "\n";
3936 length = 1;
3939 /* Replace the whitespace character with the auto-indent string
3940 and return the stats */
3941 BufReplace(buf, p, p+1, indentStr);
3942 if (tw->text.autoIndent || tw->text.smartIndent)
3943 XtFree(indentStr);
3944 *breakAt = p;
3945 *charsAdded = length-1;
3946 return True;
3950 ** Create and return an auto-indent string to add a newline at lineEndPos to a
3951 ** line starting at lineStartPos in buf. "buf" may or may not be the real
3952 ** text buffer for the widget. If it is not the widget's text buffer it's
3953 ** offset position from the real buffer must be specified in "bufOffset" to
3954 ** allow the smart-indent routines to scan back as far as necessary. The
3955 ** string length is returned in "length" (or "length" can be passed as NULL,
3956 ** and the indent column is returned in "column" (if non NULL).
3958 static char *createIndentString(TextWidget tw, textBuffer *buf, int bufOffset,
3959 int lineStartPos, int lineEndPos, int *length, int *column)
3961 textDisp *textD = tw->text.textD;
3962 int pos, indent = -1, tabDist = textD->buffer->tabDist;
3963 int i, useTabs = textD->buffer->useTabs;
3964 char *indentPtr, *indentStr, c;
3965 smartIndentCBStruct smartIndent;
3967 /* If smart indent is on, call the smart indent callback. It is not
3968 called when multi-line changes are being made (lineStartPos != 0),
3969 because smart indent needs to search back an indeterminate distance
3970 through the buffer, and reconciling that with wrapping changes made,
3971 but not yet committed in the buffer, would make programming smart
3972 indent more difficult for users and make everything more complicated */
3973 if (tw->text.smartIndent && (lineStartPos == 0 || buf == textD->buffer)) {
3974 smartIndent.reason = NEWLINE_INDENT_NEEDED;
3975 smartIndent.pos = lineEndPos + bufOffset;
3976 smartIndent.indentRequest = 0;
3977 smartIndent.charsTyped = NULL;
3978 XtCallCallbacks((Widget)tw, textNsmartIndentCallback,
3979 (XtPointer)&smartIndent);
3980 indent = smartIndent.indentRequest;
3983 /* If smart indent wasn't used, measure the indent distance of the line */
3984 if (indent == -1) {
3985 indent = 0;
3986 for (pos=lineStartPos; pos<lineEndPos; pos++) {
3987 c = BufGetCharacter(buf, pos);
3988 if (c != ' ' && c != '\t')
3989 break;
3990 if (c == '\t')
3991 indent += tabDist - (indent % tabDist);
3992 else
3993 indent++;
3997 /* Allocate and create a string of tabs and spaces to achieve the indent */
3998 indentPtr = indentStr = XtMalloc(indent + 2);
3999 *indentPtr++ = '\n';
4000 if (useTabs) {
4001 for (i=0; i < indent / tabDist; i++)
4002 *indentPtr++ = '\t';
4003 for (i=0; i < indent % tabDist; i++)
4004 *indentPtr++ = ' ';
4005 } else {
4006 for (i=0; i < indent; i++)
4007 *indentPtr++ = ' ';
4009 *indentPtr = '\0';
4011 /* Return any requested stats */
4012 if (length != NULL)
4013 *length = indentPtr - indentStr;
4014 if (column != NULL)
4015 *column = indent;
4017 return indentStr;
4021 ** Xt timer procedure for autoscrolling
4023 static void autoScrollTimerProc(XtPointer clientData, XtIntervalId *id)
4025 TextWidget w = (TextWidget)clientData;
4026 textDisp *textD = w->text.textD;
4027 int topLineNum, horizOffset, newPos, cursorX, y;
4028 int fontWidth = textD->fontStruct->max_bounds.width;
4029 int fontHeight = textD->fontStruct->ascent + textD->fontStruct->descent;
4031 /* For vertical autoscrolling just dragging the mouse outside of the top
4032 or bottom of the window is sufficient, for horizontal (non-rectangular)
4033 scrolling, see if the position where the CURSOR would go is outside */
4034 newPos = TextDXYToPosition(textD, w->text.mouseX, w->text.mouseY);
4035 if (w->text.dragState == PRIMARY_RECT_DRAG)
4036 cursorX = w->text.mouseX;
4037 else if (!TextDPositionToXY(textD, newPos, &cursorX, &y))
4038 cursorX = w->text.mouseX;
4040 /* Scroll away from the pointer, 1 character (horizontal), or 1 character
4041 for each fontHeight distance from the mouse to the text (vertical) */
4042 TextDGetScroll(textD, &topLineNum, &horizOffset);
4043 if (cursorX >= (int)w->core.width - w->text.marginWidth)
4044 horizOffset += fontWidth;
4045 else if (w->text.mouseX < textD->left)
4046 horizOffset -= fontWidth;
4047 if (w->text.mouseY >= (int)w->core.height - w->text.marginHeight)
4048 topLineNum += 1 + ((w->text.mouseY - (int)w->core.height -
4049 w->text.marginHeight) / fontHeight) + 1;
4050 else if (w->text.mouseY < w->text.marginHeight)
4051 topLineNum -= 1 + ((w->text.marginHeight-w->text.mouseY) / fontHeight);
4052 TextDSetScroll(textD, topLineNum, horizOffset);
4054 /* Continue the drag operation in progress. If none is in progress
4055 (safety check) don't continue to re-establish the timer proc */
4056 if (w->text.dragState == PRIMARY_DRAG) {
4057 adjustSelection(w, w->text.mouseX, w->text.mouseY);
4058 } else if (w->text.dragState == PRIMARY_RECT_DRAG) {
4059 adjustSelection(w, w->text.mouseX, w->text.mouseY);
4060 } else if (w->text.dragState == SECONDARY_DRAG) {
4061 adjustSecondarySelection(w, w->text.mouseX, w->text.mouseY);
4062 } else if (w->text.dragState == SECONDARY_RECT_DRAG) {
4063 adjustSecondarySelection(w, w->text.mouseX, w->text.mouseY);
4064 } else if (w->text.dragState == PRIMARY_BLOCK_DRAG) {
4065 BlockDragSelection(w, w->text.mouseX, w->text.mouseY, USE_LAST);
4066 } else {
4067 w->text.autoScrollProcID = 0;
4068 return;
4071 /* re-establish the timer proc (this routine) to continue processing */
4072 w->text.autoScrollProcID = XtAppAddTimeOut(
4073 XtWidgetToApplicationContext((Widget)w),
4074 w->text.mouseY >= w->text.marginHeight &&
4075 w->text.mouseY < w->core.height - w->text.marginHeight ?
4076 (VERTICAL_SCROLL_DELAY*fontWidth) / fontHeight :
4077 VERTICAL_SCROLL_DELAY, autoScrollTimerProc, w);
4081 ** Xt timer procedure for cursor blinking
4083 static void cursorBlinkTimerProc(XtPointer clientData, XtIntervalId *id)
4085 TextWidget w = (TextWidget)clientData;
4086 textDisp *textD = w->text.textD;
4088 /* Blink the cursor */
4089 if (textD->cursorOn)
4090 TextDBlankCursor(textD);
4091 else
4092 TextDUnblankCursor(textD);
4094 /* re-establish the timer proc (this routine) to continue processing */
4095 w->text.cursorBlinkProcID = XtAppAddTimeOut(
4096 XtWidgetToApplicationContext((Widget)w),
4097 w->text.cursorBlinkRate, cursorBlinkTimerProc, w);
4101 ** look at an action procedure's arguments to see if argument "key" has been
4102 ** specified in the argument list
4104 static int hasKey(const char *key, const String *args, const Cardinal *nArgs)
4106 int i;
4108 for (i=0; i<(int)*nArgs; i++)
4109 if (!strCaseCmp(args[i], key))
4110 return True;
4111 return False;
4114 static int max(int i1, int i2)
4116 return i1 >= i2 ? i1 : i2;
4119 static int min(int i1, int i2)
4121 return i1 <= i2 ? i1 : i2;
4124 const char *GetDefaultTranslations(void)
4126 return defaultTranslations;
4129 ** strCaseCmp compares its arguments and returns 0 if the two strings
4130 ** are equal IGNORING case differences. Otherwise returns 1.
4133 static int strCaseCmp(const char *str1, const char *str2)
4135 unsigned const char *c1 = (unsigned const char*) str1;
4136 unsigned const char *c2 = (unsigned const char*) str2;
4138 for (; *c1!='\0' && *c2!='\0'; c1++, c2++)
4139 if (toupper(*c1) != toupper(*c2))
4140 return 1;
4141 if (*c1 == *c2) {
4142 return(0);
4144 else {
4145 return(1);
4149 static void ringIfNecessary(Boolean silent, Widget w)
4151 if (!silent)
4152 XBell(XtDisplay(w), 0);