- Fix bad hardware tab of 4 spaces. Please set hardware tabs to 8 for NEdit!
[nedit.git] / source / text.c
blob802b09648b67ec1d803fbd5e9ea91bba9ba93cbe
1 static const char CVSID[] = "$Id: text.c,v 1.31 2002/08/10 23:58:25 tringali Exp $";
2 /*******************************************************************************
3 * *
4 * text.c - Display text from a text buffer *
5 * *
6 * Copyright (C) 1999 Mark Edel *
7 * *
8 * This is free software; you can redistribute it and/or modify it under the *
9 * terms of the GNU General Public License as published by the Free Software *
10 * Foundation; either version 2 of the License, or (at your option) any later *
11 * version. *
12 * *
13 * This software is distributed in the hope that it will be useful, but WITHOUT *
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
16 * for more details. *
17 * *
18 * You should have received a copy of the GNU General Public License along with *
19 * software; if not, write to the Free Software Foundation, Inc., 59 Temple *
20 * Place, Suite 330, Boston, MA 02111-1307 USA *
21 * *
22 * Nirvana Text Editor *
23 * June 15, 1995 *
24 * *
25 *******************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 #include "../config.h"
29 #endif
31 #include "text.h"
32 #include "textP.h"
33 #include "textBuf.h"
34 #include "textDisp.h"
35 #include "textSel.h"
36 #include "textDrag.h"
37 #include "nedit.h"
38 #include "preferences.h"
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(TextWidget w, XtPointer unused,
83 XEvent *event, Boolean *continue_to_dispatch);
84 static void handleShowPointer(TextWidget 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, "black"},
614 {textNselectBackground, textCSelectBackground, XmRPixel, sizeof(Pixel),
615 XtOffset(TextWidget, text.selectBGPixel), XmRString, "#cccccc"},
616 {textNhighlightForeground, textCHighlightForeground, XmRPixel,sizeof(Pixel),
617 XtOffset(TextWidget, text.highlightFGPixel), XmRString, "white"},
618 {textNhighlightBackground, textCHighlightBackground, XmRPixel,sizeof(Pixel),
619 XtOffset(TextWidget, text.highlightBGPixel), XmRString, "red"},
620 {textNcursorForeground, textCCursorForeground, XmRPixel,sizeof(Pixel),
621 XtOffset(TextWidget, text.cursorFGPixel), XmRString, "black"},
622 {textNlineNumForeground, textCLineNumForeground, XmRPixel,sizeof(Pixel),
623 XtOffset(TextWidget, text.lineNumFGPixel), XmRString, "black"},
624 {textNrows, textCRows, XmRInt,sizeof(int),
625 XtOffset(TextWidget, text.rows), XmRString, "24"},
626 {textNcolumns, textCColumns, XmRInt, sizeof(int),
627 XtOffset(TextWidget, text.columns), XmRString, "80"},
628 {textNmarginWidth, textCMarginWidth, XmRInt, sizeof(int),
629 XtOffset(TextWidget, text.marginWidth), XmRString, "5"},
630 {textNmarginHeight, textCMarginHeight, XmRInt, sizeof(int),
631 XtOffset(TextWidget, text.marginHeight), XmRString, "5"},
632 {textNpendingDelete, textCPendingDelete, XmRBoolean, sizeof(Boolean),
633 XtOffset(TextWidget, text.pendingDelete), XmRString, "True"},
634 {textNautoWrap, textCAutoWrap, XmRBoolean, sizeof(Boolean),
635 XtOffset(TextWidget, text.autoWrap), XmRString, "True"},
636 {textNcontinuousWrap, textCContinuousWrap, XmRBoolean, sizeof(Boolean),
637 XtOffset(TextWidget, text.continuousWrap), XmRString, "True"},
638 {textNautoIndent, textCAutoIndent, XmRBoolean, sizeof(Boolean),
639 XtOffset(TextWidget, text.autoIndent), XmRString, "True"},
640 {textNsmartIndent, textCSmartIndent, XmRBoolean, sizeof(Boolean),
641 XtOffset(TextWidget, text.smartIndent), XmRString, "False"},
642 {textNoverstrike, textCOverstrike, XmRBoolean, sizeof(Boolean),
643 XtOffset(TextWidget, text.overstrike), XmRString, "False"},
644 {textNheavyCursor, textCHeavyCursor, XmRBoolean, sizeof(Boolean),
645 XtOffset(TextWidget, text.heavyCursor), XmRString, "False"},
646 {textNreadOnly, textCReadOnly, XmRBoolean, sizeof(Boolean),
647 XtOffset(TextWidget, text.readOnly), XmRString, "False"},
648 {textNwrapMargin, textCWrapMargin, XmRInt, sizeof(int),
649 XtOffset(TextWidget, text.wrapMargin), XmRString, "0"},
650 {textNhScrollBar, textCHScrollBar, XmRWidget, sizeof(Widget),
651 XtOffset(TextWidget, text.hScrollBar), XmRString, ""},
652 {textNvScrollBar, textCVScrollBar, XmRWidget, sizeof(Widget),
653 XtOffset(TextWidget, text.vScrollBar), XmRString, ""},
654 {textNlineNumCols, textCLineNumCols, XmRInt, sizeof(int),
655 XtOffset(TextWidget, text.lineNumCols), XmRString, "0"},
656 {textNautoShowInsertPos, textCAutoShowInsertPos, XmRBoolean,
657 sizeof(Boolean), XtOffset(TextWidget, text.autoShowInsertPos),
658 XmRString, "True"},
659 {textNautoWrapPastedText, textCAutoWrapPastedText, XmRBoolean,
660 sizeof(Boolean), XtOffset(TextWidget, text.autoWrapPastedText),
661 XmRString, "False"},
662 {textNwordDelimiters, textCWordDelimiters, XmRString, sizeof(char *),
663 XtOffset(TextWidget, text.delimiters), XmRString,
664 ".,/\\`'!@#%^&*()-=+{}[]\":;<>?"},
665 {textNblinkRate, textCBlinkRate, XmRInt, sizeof(int),
666 XtOffset(TextWidget, text.cursorBlinkRate), XmRString, "500"},
667 {textNemulateTabs, textCEmulateTabs, XmRInt, sizeof(int),
668 XtOffset(TextWidget, text.emulateTabs), XmRString, "0"},
669 {textNfocusCallback, textCFocusCallback, XmRCallback, sizeof(caddr_t),
670 XtOffset(TextWidget, text.focusInCB), XtRCallback, NULL},
671 {textNlosingFocusCallback, textCLosingFocusCallback, XmRCallback,
672 sizeof(caddr_t), XtOffset(TextWidget, text.focusOutCB), XtRCallback,NULL},
673 {textNcursorMovementCallback, textCCursorMovementCallback, XmRCallback,
674 sizeof(caddr_t), XtOffset(TextWidget, text.cursorCB), XtRCallback, NULL},
675 {textNdragStartCallback, textCDragStartCallback, XmRCallback,
676 sizeof(caddr_t), XtOffset(TextWidget, text.dragStartCB), XtRCallback,
677 NULL},
678 {textNdragEndCallback, textCDragEndCallback, XmRCallback,
679 sizeof(caddr_t), XtOffset(TextWidget, text.dragEndCB), XtRCallback, NULL},
680 {textNsmartIndentCallback, textCSmartIndentCallback, XmRCallback,
681 sizeof(caddr_t), XtOffset(TextWidget, text.smartIndentCB), XtRCallback,
682 NULL},
685 static TextClassRec textClassRec = {
686 /* CoreClassPart */
688 (WidgetClass) &xmPrimitiveClassRec, /* superclass */
689 "Text", /* class_name */
690 sizeof(TextRec), /* widget_size */
691 NULL, /* class_initialize */
692 NULL, /* class_part_initialize */
693 FALSE, /* class_inited */
694 (XtInitProc)initialize, /* initialize */
695 NULL, /* initialize_hook */
696 realize, /* realize */
697 actionsList, /* actions */
698 XtNumber(actionsList), /* num_actions */
699 resources, /* resources */
700 XtNumber(resources), /* num_resources */
701 NULLQUARK, /* xrm_class */
702 TRUE, /* compress_motion */
703 TRUE, /* compress_exposure */
704 TRUE, /* compress_enterleave */
705 FALSE, /* visible_interest */
706 (XtWidgetProc)destroy, /* destroy */
707 (XtWidgetProc)resize, /* resize */
708 (XtExposeProc)redisplay, /* expose */
709 (XtSetValuesFunc)setValues, /* set_values */
710 NULL, /* set_values_hook */
711 XtInheritSetValuesAlmost, /* set_values_almost */
712 NULL, /* get_values_hook */
713 NULL, /* accept_focus */
714 XtVersion, /* version */
715 NULL, /* callback private */
716 defaultTranslations, /* tm_table */
717 queryGeometry, /* query_geometry */
718 NULL, /* display_accelerator */
719 NULL, /* extension */
721 /* Motif primitive class fields */
723 (XtWidgetProc)_XtInherit, /* Primitive border_highlight */
724 (XtWidgetProc)_XtInherit, /* Primitive border_unhighlight */
725 NULL, /*XtInheritTranslations,*/ /* translations */
726 NULL, /* arm_and_activate */
727 NULL, /* get resources */
728 0, /* num get_resources */
729 NULL, /* extension */
731 /* Text class part */
733 0, /* ignored */
737 WidgetClass textWidgetClass = (WidgetClass)&textClassRec;
738 #define NEDIT_HIDE_CURSOR_MASK (KeyPressMask)
739 #define NEDIT_SHOW_CURSOR_MASK (FocusChangeMask | PointerMotionMask | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask)
740 static char empty_bits[] = {0x00, 0x00, 0x00, 0x00};
741 static Cursor empty_cursor = 0;
744 ** Widget initialize method
746 static void initialize(TextWidget request, TextWidget new)
748 XFontStruct *fs = new->text.fontStruct;
749 char *delimiters;
750 textBuffer *buf;
751 Pixel white, black;
752 int textLeft;
753 int charWidth = fs->max_bounds.width;
754 int marginWidth = new->text.marginWidth;
755 int lineNumCols = new->text.lineNumCols;
756 Pixmap empty_pixmap;
757 XColor black_color;
758 Display *theDisplay;
760 /* Set the initial window size based on the rows and columns resources */
761 if (request->core.width == 0)
762 new->core.width = charWidth * new->text.columns + marginWidth*2 +
763 (lineNumCols == 0 ? 0 : marginWidth + charWidth * lineNumCols);
764 if (request->core.height == 0)
765 new->core.height = (fs->ascent + fs->descent) * new->text.rows +
766 new->text.marginHeight * 2;
768 /* The default colors work for B&W as well as color, except for
769 selectFGPixel and selectBGPixel, where color highlighting looks
770 much better without reverse video, so if we get here, and the
771 selection is totally unreadable because of the bad default colors,
772 swap the colors and make the selection reverse video */
773 white = WhitePixelOfScreen(XtScreen((Widget)new));
774 black = BlackPixelOfScreen(XtScreen((Widget)new));
775 if ( new->text.selectBGPixel == white &&
776 new->core.background_pixel == white &&
777 new->text.selectFGPixel == black &&
778 new->primitive.foreground == black) {
779 new->text.selectBGPixel = black;
780 new->text.selectFGPixel = white;
783 /* Create the initial text buffer for the widget to display (which can
784 be replaced later with TextSetBuffer) */
785 buf = BufCreate();
787 /* Create and initialize the text-display part of the widget */
788 textLeft = new->text.marginWidth +
789 (lineNumCols == 0 ? 0 : marginWidth + charWidth * lineNumCols);
790 new->text.textD = TextDCreate((Widget)new, new->text.hScrollBar,
791 new->text.vScrollBar, textLeft, new->text.marginHeight,
792 new->core.width - marginWidth - textLeft,
793 new->core.height - new->text.marginHeight * 2,
794 lineNumCols == 0 ? 0 : marginWidth,
795 lineNumCols == 0 ? 0 : lineNumCols * charWidth,
796 buf, new->text.fontStruct, new->core.background_pixel,
797 new->primitive.foreground, new->text.selectFGPixel,
798 new->text.selectBGPixel, new->text.highlightFGPixel,
799 new->text.highlightBGPixel, new->text.cursorFGPixel,
800 new->text.lineNumFGPixel,
801 new->text.continuousWrap, new->text.wrapMargin);
803 /* Add mandatory delimiters blank, tab, and newline to the list of
804 delimiters. The memory use scheme here is that new values are
805 always copied, and can therefore be safely freed on subsequent
806 set-values calls or destroy */
807 delimiters = XtMalloc(strlen(new->text.delimiters) + 4);
808 sprintf(delimiters, "%s%s", " \t\n", new->text.delimiters);
809 new->text.delimiters = delimiters;
811 /* Start with the cursor blanked (widgets don't have focus on creation,
812 the initial FocusIn event will unblank it and get blinking started) */
813 new->text.textD->cursorOn = False;
815 /* Initialize the widget variables */
816 new->text.autoScrollProcID = 0;
817 new->text.cursorBlinkProcID = 0;
818 new->text.dragState = NOT_CLICKED;
819 new->text.multiClickState = NO_CLICKS;
820 new->text.lastBtnDown = 0;
821 new->text.selectionOwner = False;
822 new->text.motifDestOwner = False;
823 new->text.emTabsBeforeCursor = 0;
825 #ifndef NO_XMIM
826 /* Register the widget to the input manager */
827 XmImRegister((Widget)new, 0);
828 /* In case some Resources for the IC need to be set, add them below */
829 XmImVaSetValues((Widget)new, NULL);
830 #endif
832 XtAddEventHandler((Widget)new, GraphicsExpose, True,
833 (XtEventHandler)redisplayGE, (Opaque)NULL);
835 if (GetPrefTypingHidesPointer()) {
836 /* Set up the empty Cursor */
837 if (empty_cursor == 0) {
838 theDisplay = XtDisplay((Widget)new);
839 empty_pixmap = XCreateBitmapFromData(theDisplay,
840 RootWindowOfScreen(XtScreen((Widget)new)), empty_bits, 1, 1);
841 XParseColor(theDisplay, DefaultColormapOfScreen(XtScreen((Widget)new)),
842 "black", &black_color);
843 empty_cursor = XCreatePixmapCursor(theDisplay, empty_pixmap,
844 empty_pixmap, &black_color, &black_color, 0, 0);
847 /* Add event handler to hide the pointer on keypresses */
848 XtAddEventHandler((Widget)new, NEDIT_HIDE_CURSOR_MASK, False,
849 (XtEventHandler)handleHidePointer, (Opaque)NULL);
853 /* Hide the pointer while the user is typing */
854 static void handleHidePointer(TextWidget w, XtPointer unused,
855 XEvent *event, Boolean *continue_to_dispatch) {
856 ShowHidePointer(w, True);
859 /* Restore the pointer if the mouse moves or focus changes */
860 static void handleShowPointer(TextWidget w, XtPointer unused,
861 XEvent *event, Boolean *continue_to_dispatch) {
862 ShowHidePointer(w, False);
865 void ShowHidePointer(TextWidget w, Boolean hidePointer)
867 if (GetPrefTypingHidesPointer()) {
868 if (hidePointer != w->text.textD->pointerHidden) {
869 if (hidePointer) {
870 /* Don't listen for keypresses any more */
871 XtRemoveEventHandler((Widget)w, NEDIT_HIDE_CURSOR_MASK, False,
872 (XtEventHandler)handleHidePointer, (Opaque)NULL);
873 /* Switch to empty cursor */
874 XDefineCursor(XtDisplay(w), XtWindow(w), empty_cursor);
876 w->text.textD->pointerHidden = True;
878 /* Listen to mouse movement, focus change, and button presses */
879 XtAddEventHandler((Widget)w, NEDIT_SHOW_CURSOR_MASK,
880 False, (XtEventHandler)handleShowPointer, (Opaque)NULL);
882 else {
883 /* Don't listen to mouse/focus events any more */
884 XtRemoveEventHandler((Widget)w, NEDIT_SHOW_CURSOR_MASK,
885 False, (XtEventHandler)handleShowPointer, (Opaque)NULL);
886 /* Switch to regular cursor */
887 XUndefineCursor(XtDisplay(w), XtWindow(w));
889 w->text.textD->pointerHidden = False;
891 /* Listen for keypresses now */
892 XtAddEventHandler((Widget)w, NEDIT_HIDE_CURSOR_MASK, False,
893 (XtEventHandler)handleHidePointer, (Opaque)NULL);
900 ** Widget destroy method
902 static void destroy(TextWidget w)
904 textBuffer *buf;
906 /* Free the text display and possibly the attached buffer. The buffer
907 is freed only if after removing all of the modify procs (by calling
908 StopHandlingXSelections and TextDFree) there are no modify procs
909 left */
910 StopHandlingXSelections((Widget)w);
911 buf = w->text.textD->buffer;
912 TextDFree(w->text.textD);
913 if (buf->nModifyProcs == 0)
914 BufFree(buf);
916 if (w->text.cursorBlinkProcID != 0)
917 XtRemoveTimeOut(w->text.cursorBlinkProcID);
918 XtFree(w->text.delimiters);
919 XtRemoveAllCallbacks((Widget)w, textNfocusCallback);
920 XtRemoveAllCallbacks((Widget)w, textNlosingFocusCallback);
921 XtRemoveAllCallbacks((Widget)w, textNcursorMovementCallback);
922 XtRemoveAllCallbacks((Widget)w, textNdragStartCallback);
923 XtRemoveAllCallbacks((Widget)w, textNdragEndCallback);
925 #ifndef NO_XMIM
926 /* Unregister the widget from the input manager */
927 XmImUnregister((Widget)w);
928 #endif
932 ** Widget resize method. Called when the size of the widget changes
934 static void resize(TextWidget w)
936 XFontStruct *fs = w->text.fontStruct;
937 int height = w->core.height, width = w->core.width;
938 int marginWidth = w->text.marginWidth, marginHeight = w->text.marginHeight;
939 int lineNumAreaWidth = w->text.lineNumCols == 0 ? 0 : w->text.marginWidth +
940 fs->max_bounds.width * w->text.lineNumCols;
942 w->text.columns = (width - marginWidth*2 - lineNumAreaWidth) /
943 fs->max_bounds.width;
944 w->text.rows = (height - marginHeight*2) / (fs->ascent + fs->descent);
946 /* Reject widths and heights less than a character, which the text
947 display can't tolerate. This is not strictly legal, but I've seen
948 it done in other widgets and it seems to do no serious harm. NEdit
949 prevents panes from getting smaller than one line, but sometimes
950 splitting windows on Linux 2.0 systems (same Motif, why the change in
951 behavior?), causes one or two resize calls with < 1 line of height.
952 Fixing it here is 100x easier than re-designing textDisp.c */
953 if (w->text.columns < 1) {
954 w->text.columns = 1;
955 w->core.width = width = fs->max_bounds.width + marginWidth*2 +
956 lineNumAreaWidth;
958 if (w->text.rows < 1) {
959 w->text.rows = 1;
960 w->core.height = height = fs->ascent + fs->descent + marginHeight*2;
963 /* Resize the text display that the widget uses to render text */
964 TextDResize(w->text.textD, width - marginWidth*2 - lineNumAreaWidth,
965 height - marginHeight*2);
967 /* if the window became shorter or narrower, there may be text left
968 in the bottom or right margin area, which must be cleaned up */
969 if (XtIsRealized((Widget)w)) {
970 XClearArea(XtDisplay(w), XtWindow(w), 0, height-marginHeight,
971 width, marginHeight, False);
972 XClearArea(XtDisplay(w), XtWindow(w),width-marginWidth,
973 0, marginWidth, height, False);
978 ** Widget redisplay method
980 static void redisplay(TextWidget w, XEvent *event, Region region)
982 XExposeEvent *e = &event->xexpose;
984 TextDRedisplayRect(w->text.textD, e->x, e->y, e->width, e->height);
987 static Bool findGraphicsExposeOrNoExposeEvent(Display *theDisplay, XEvent *event, XPointer arg)
989 if ((theDisplay == event->xany.display) &&
990 (event->type == GraphicsExpose || event->type == NoExpose) &&
991 ((Widget)arg == XtWindowToWidget(event->xany.display, event->xany.window))) {
992 return(True);
994 else {
995 return(False);
999 static void adjustRectForGraphicsExposeOrNoExposeEvent(TextWidget w, XEvent *event,
1000 Boolean *first, int *left, int *top, int *width, int *height)
1002 Boolean removeQueueEntry = False;
1004 if (event->type == GraphicsExpose) {
1005 XGraphicsExposeEvent *e = &event->xgraphicsexpose;
1006 int x = e->x, y = e->y;
1008 TextDImposeGraphicsExposeTranslation(w->text.textD, &x, &y);
1009 if (*first) {
1010 *left = x;
1011 *top = y;
1012 *width = e->width;
1013 *height = e->height;
1015 *first = False;
1017 else {
1018 int prev_left = *left;
1019 int prev_top = *top;
1021 *left = min(*left, x);
1022 *top = min(*top, y);
1023 *width = max(prev_left + *width, x + e->width) - *left;
1024 *height = max(prev_top + *height, y + e->height) - *top;
1026 if (e->count == 0) {
1027 removeQueueEntry = True;
1030 else if (event->type == NoExpose) {
1031 removeQueueEntry = True;
1033 if (removeQueueEntry) {
1034 TextDPopGraphicExposeQueueEntry(w->text.textD);
1038 static void redisplayGE(TextWidget w, XtPointer client_data,
1039 XEvent *event, Boolean *continue_to_dispatch_return)
1041 if (event->type == GraphicsExpose || event->type == NoExpose) {
1042 HandleAllPendingGraphicsExposeNoExposeEvents(w, event);
1046 void HandleAllPendingGraphicsExposeNoExposeEvents(TextWidget w, XEvent *event)
1048 XEvent foundEvent;
1049 int left;
1050 int top;
1051 int width;
1052 int height;
1053 Boolean invalidRect = True;
1055 if (event) {
1056 adjustRectForGraphicsExposeOrNoExposeEvent(w, event, &invalidRect, &left, &top, &width, &height);
1058 while (XCheckIfEvent(XtDisplay(w), &foundEvent, findGraphicsExposeOrNoExposeEvent, (XPointer)w)) {
1059 adjustRectForGraphicsExposeOrNoExposeEvent(w, &foundEvent, &invalidRect, &left, &top, &width, &height);
1061 if (!invalidRect) {
1062 TextDRedisplayRect(w->text.textD, left, top, width, height);
1067 ** Widget setValues method
1069 static Boolean setValues(TextWidget current, TextWidget request,
1070 TextWidget new)
1072 Boolean redraw = False, reconfigure = False;
1074 if (new->text.overstrike != current->text.overstrike) {
1075 if (current->text.textD->cursorStyle == BLOCK_CURSOR)
1076 TextDSetCursorStyle(current->text.textD,
1077 current->text.heavyCursor ? HEAVY_CURSOR : NORMAL_CURSOR);
1078 else if (current->text.textD->cursorStyle == NORMAL_CURSOR ||
1079 current->text.textD->cursorStyle == HEAVY_CURSOR)
1080 TextDSetCursorStyle(current->text.textD, BLOCK_CURSOR);
1083 if (new->text.fontStruct != current->text.fontStruct) {
1084 if (new->text.lineNumCols != 0)
1085 reconfigure = True;
1086 TextDSetFont(current->text.textD, new->text.fontStruct);
1089 if (new->text.wrapMargin != current->text.wrapMargin ||
1090 new->text.continuousWrap != current->text.continuousWrap)
1091 TextDSetWrapMode(current->text.textD, new->text.continuousWrap,
1092 new->text.wrapMargin);
1094 /* When delimiters are changed, copy the memory, so that the caller
1095 doesn't have to manage it, and add mandatory delimiters blank,
1096 tab, and newline to the list */
1097 if (new->text.delimiters != current->text.delimiters) {
1098 char *delimiters = XtMalloc(strlen(new->text.delimiters) + 4);
1099 XtFree(current->text.delimiters);
1100 sprintf(delimiters, "%s%s", " \t\n", new->text.delimiters);
1101 new->text.delimiters = delimiters;
1104 /* Setting the lineNumCols resource tells the text widget to hide or
1105 show, or change the number of columns of the line number display,
1106 which requires re-organizing the x coordinates of both the line
1107 number display and the main text display */
1108 if (new->text.lineNumCols != current->text.lineNumCols || reconfigure) {
1109 int marginWidth = new->text.marginWidth;
1110 int charWidth = new->text.fontStruct->max_bounds.width;
1111 int lineNumCols = new->text.lineNumCols;
1112 if (lineNumCols == 0) {
1113 TextDSetLineNumberArea(new->text.textD, 0, 0, marginWidth);
1114 new->text.columns = (new->core.width - marginWidth*2) / charWidth;
1115 } else {
1116 TextDSetLineNumberArea(new->text.textD, marginWidth, charWidth *
1117 lineNumCols, 2*marginWidth + charWidth * lineNumCols);
1118 new->text.columns = (new->core.width - marginWidth*3 -
1119 charWidth * lineNumCols) / charWidth;
1123 return redraw;
1127 ** Widget realize method
1129 static void realize(Widget w, XtValueMask *valueMask,
1130 XSetWindowAttributes *attributes)
1132 /* Set bit gravity window attribute. This saves a full blank and redraw
1133 on window resizing */
1134 *valueMask |= CWBitGravity;
1135 attributes->bit_gravity = NorthWestGravity;
1137 /* Continue with realize method from superclass */
1138 (xmPrimitiveClassRec.core_class.realize)(w, valueMask, attributes);
1142 ** Widget query geometry method ... experimental, not fully tested, does
1143 ** nothing in NEdit since paned window ignores children's suggested geometry
1145 static XtGeometryResult queryGeometry(Widget w, XtWidgetGeometry *proposed,
1146 XtWidgetGeometry *answer)
1148 int curHeight = ((TextWidget)w)->core.height;
1149 int curWidth = ((TextWidget)w)->core.width;
1150 XFontStruct *fs = ((TextWidget)w)->text.textD->fontStruct;
1151 int fontWidth = fs->max_bounds.width;
1152 int fontHeight = fs->ascent + fs->descent;
1153 int marginHeight = ((TextWidget)w)->text.marginHeight;
1154 int propWidth = (proposed->request_mode & CWWidth) ? proposed->width : 0;
1155 int propHeight = (proposed->request_mode & CWHeight) ? proposed->height : 0;
1157 /* suggest a height that is an exact multiple of the line height
1158 and at least one line high and 10 chars wide */
1159 answer->request_mode = CWHeight | CWWidth;
1160 answer->width = max(fontWidth * 10, propWidth);
1161 answer->height = max(1, ((propHeight - 2*marginHeight) / fontHeight)) *
1162 fontHeight + 2*marginHeight;
1163 /* printf("propWidth %d, propHeight %d, ansWidth %d, ansHeight %d\n",
1164 propWidth, propHeight, answer->width, answer->height); */
1165 if (propWidth == answer->width && propHeight == answer->height)
1166 return XtGeometryYes;
1167 else if (answer->width == curWidth && answer->height == curHeight)
1168 return XtGeometryNo;
1169 else
1170 return XtGeometryAlmost;
1174 ** Set the text buffer which this widget will display and interact with.
1175 ** The currently attached buffer is automatically freed, ONLY if it has
1176 ** no additional modify procs attached (as it would if it were being
1177 ** displayed by another text widget).
1179 void TextSetBuffer(Widget w, textBuffer *buffer)
1181 textBuffer *oldBuf = ((TextWidget)w)->text.textD->buffer;
1183 StopHandlingXSelections(w);
1184 TextDSetBuffer(((TextWidget)w)->text.textD, buffer);
1185 if (oldBuf->nModifyProcs == 0)
1186 BufFree(oldBuf);
1190 ** Get the buffer associated with this text widget. Note that attaching
1191 ** additional modify callbacks to the buffer will prevent it from being
1192 ** automatically freed when the widget is destroyed.
1194 textBuffer *TextGetBuffer(Widget w)
1196 return ((TextWidget)w)->text.textD->buffer;
1200 ** Translate a line number and column into a position
1202 int TextLineAndColToPos(Widget w, int lineNum, int column)
1204 return TextDLineAndColToPos(((TextWidget)w)->text.textD, lineNum, column );
1208 ** Translate a position into a line number (if the position is visible,
1209 ** if it's not, return False
1211 int TextPosToLineAndCol(Widget w, int pos, int *lineNum, int *column)
1213 return TextDPosToLineAndCol(((TextWidget)w)->text.textD, pos, lineNum,
1214 column);
1218 ** Translate a buffer text position to the XY location where the center
1219 ** of the cursor would be positioned to point to that character. Returns
1220 ** False if the position is not displayed because it is VERTICALLY out
1221 ** of view. If the position is horizontally out of view, returns the
1222 ** x coordinate where the position would be if it were visible.
1224 int TextPosToXY(Widget w, int pos, int *x, int *y)
1226 return TextDPositionToXY(((TextWidget)w)->text.textD, pos, x, y);
1230 ** Return the cursor position
1232 int TextGetCursorPos(Widget w)
1234 return TextDGetInsertPosition(((TextWidget)w)->text.textD);
1238 ** Set the cursor position
1240 void TextSetCursorPos(Widget w, int pos)
1242 TextDSetInsertPosition(((TextWidget)w)->text.textD, pos);
1243 checkAutoShowInsertPos(w);
1244 callCursorMovementCBs(w, NULL);
1249 ** Return the horizontal and vertical scroll positions of the widget
1251 void TextGetScroll(Widget w, int *topLineNum, int *horizOffset)
1253 TextDGetScroll(((TextWidget)w)->text.textD, topLineNum, horizOffset);
1257 ** Set the horizontal and vertical scroll positions of the widget
1259 void TextSetScroll(Widget w, int topLineNum, int horizOffset)
1261 TextDSetScroll(((TextWidget)w)->text.textD, topLineNum, horizOffset);
1264 int TextGetMinFontWidth(Widget w, Boolean considerStyles)
1266 return(TextDMinFontWidth(((TextWidget)w)->text.textD, considerStyles));
1269 int TextGetMaxFontWidth(Widget w, Boolean considerStyles)
1271 return(TextDMaxFontWidth(((TextWidget)w)->text.textD, considerStyles));
1275 ** Set this widget to be the owner of selections made in it's attached
1276 ** buffer (text buffers may be shared among several text widgets).
1278 void TextHandleXSelections(Widget w)
1280 HandleXSelections(w);
1283 void TextStopHandlingSelections(Widget w)
1285 StopHandlingXSelections(w);
1288 void TextPasteClipboard(Widget w, Time time)
1290 cancelDrag(w);
1291 if (checkReadOnly(w))
1292 return;
1293 TakeMotifDestination(w, time);
1294 InsertClipboard(w, time, False);
1295 callCursorMovementCBs(w, NULL);
1298 void TextColPasteClipboard(Widget w, Time time)
1300 cancelDrag(w);
1301 if (checkReadOnly(w))
1302 return;
1303 TakeMotifDestination(w, time);
1304 InsertClipboard(w, time, True);
1305 callCursorMovementCBs(w, NULL);
1308 void TextCopyClipboard(Widget w, Time time)
1310 cancelDrag(w);
1311 if (!((TextWidget)w)->text.textD->buffer->primary.selected) {
1312 XBell(XtDisplay(w), 0);
1313 return;
1315 CopyToClipboard(w, time);
1318 void TextCutClipboard(Widget w, Time time)
1320 textDisp *textD = ((TextWidget)w)->text.textD;
1322 cancelDrag(w);
1323 if (checkReadOnly(w))
1324 return;
1325 if (!textD->buffer->primary.selected) {
1326 XBell(XtDisplay(w), 0);
1327 return;
1329 TakeMotifDestination(w, time);
1330 CopyToClipboard (w, time);
1331 BufRemoveSelected(textD->buffer);
1332 TextDSetInsertPosition(textD, textD->buffer->cursorPosHint);
1333 checkAutoShowInsertPos(w);
1336 int TextFirstVisibleLine(Widget w)
1338 return(((TextWidget)w)->text.textD->topLineNum);
1341 int TextNumVisibleLines(Widget w)
1343 return(((TextWidget)w)->text.textD->nVisibleLines);
1346 int TextVisibleWidth(Widget w)
1348 return(((TextWidget)w)->text.textD->width);
1351 int TextFirstVisiblePos(Widget w)
1353 return ((TextWidget)w)->text.textD->firstChar;
1356 int TextLastVisiblePos(Widget w)
1358 return ((TextWidget)w)->text.textD->lastChar;
1362 ** Insert text "chars" at the cursor position, respecting pending delete
1363 ** selections, overstrike, and handling cursor repositioning as if the text
1364 ** had been typed. If autoWrap is on wraps the text to fit within the wrap
1365 ** margin, auto-indenting where the line was wrapped (but nowhere else).
1366 ** "allowPendingDelete" controls whether primary selections in the widget are
1367 ** treated as pending delete selections (True), or ignored (False). "event"
1368 ** is optional and is just passed on to the cursor movement callbacks.
1370 void TextInsertAtCursor(Widget w, char *chars, XEvent *event,
1371 int allowPendingDelete, int allowWrap)
1373 int wrapMargin, colNum, lineStartPos, cursorPos;
1374 char *c, *lineStartText, *wrappedText;
1375 TextWidget tw = (TextWidget)w;
1376 textDisp *textD = tw->text.textD;
1377 textBuffer *buf = textD->buffer;
1378 int fontWidth = textD->fontStruct->max_bounds.width;
1379 int replaceSel, singleLine, breakAt = 0;
1381 /* Don't wrap if auto-wrap is off or suppressed, or it's just a newline */
1382 if (!allowWrap || !tw->text.autoWrap ||
1383 (chars[0] == '\n' && chars[1] == '\0')) {
1384 simpleInsertAtCursor(w, chars, event, allowPendingDelete);
1385 return;
1388 /* If this is going to be a pending delete operation, the real insert
1389 position is the start of the selection. This will make rectangular
1390 selections wrap strangely, but this routine should rarely be used for
1391 them, and even more rarely when they need to be wrapped. */
1392 replaceSel = allowPendingDelete && pendingSelection(w);
1393 cursorPos = replaceSel ? buf->primary.start : TextDGetInsertPosition(textD);
1395 /* If the text is only one line and doesn't need to be wrapped, just insert
1396 it and be done (for efficiency only, this routine is called for each
1397 character typed). (Of course, it may not be significantly more efficient
1398 than the more general code below it, so it may be a waste of time!) */
1399 wrapMargin = tw->text.wrapMargin != 0 ? tw->text.wrapMargin :
1400 textD->width / fontWidth;
1401 lineStartPos = BufStartOfLine(buf, cursorPos);
1402 colNum = BufCountDispChars(buf, lineStartPos, cursorPos);
1403 for (c=chars; *c!='\0' && *c!='\n'; c++)
1404 colNum += BufCharWidth(*c, colNum, buf->tabDist, buf->nullSubsChar);
1405 singleLine = *c == '\0';
1406 if (colNum < wrapMargin && singleLine) {
1407 simpleInsertAtCursor(w, chars, event, True);
1408 return;
1411 /* Wrap the text */
1412 lineStartText = BufGetRange(buf, lineStartPos, cursorPos);
1413 wrappedText = wrapText(tw, lineStartText, chars, lineStartPos, wrapMargin,
1414 replaceSel ? NULL : &breakAt);
1415 XtFree(lineStartText);
1417 /* Insert the text. Where possible, use TextDInsert which is optimized
1418 for less redraw. */
1419 if (replaceSel) {
1420 BufReplaceSelected(buf, wrappedText);
1421 TextDSetInsertPosition(textD, buf->cursorPosHint);
1422 } else if (tw->text.overstrike) {
1423 if (breakAt == 0 && singleLine)
1424 TextDOverstrike(textD, wrappedText);
1425 else {
1426 BufReplace(buf, cursorPos-breakAt, cursorPos, wrappedText);
1427 TextDSetInsertPosition(textD, buf->cursorPosHint);
1429 } else {
1430 if (breakAt == 0) {
1431 TextDInsert(textD, wrappedText);
1432 } else {
1433 BufReplace(buf, cursorPos-breakAt, cursorPos, wrappedText);
1434 TextDSetInsertPosition(textD, buf->cursorPosHint);
1437 XtFree(wrappedText);
1438 checkAutoShowInsertPos(w);
1439 callCursorMovementCBs(w, event);
1443 ** Fetch text from the widget's buffer, adding wrapping newlines to emulate
1444 ** effect acheived by wrapping in the text display in continuous wrap mode.
1446 char *TextGetWrapped(Widget w, int startPos, int endPos, int *outLen)
1448 textDisp *textD = ((TextWidget)w)->text.textD;
1449 textBuffer *buf = textD->buffer;
1450 textBuffer *outBuf;
1451 int fromPos, toPos, outPos;
1452 char c, *outString;
1454 if (!((TextWidget)w)->text.continuousWrap || startPos == endPos) {
1455 *outLen = endPos - startPos;
1456 return BufGetRange(buf, startPos, endPos);
1459 /* Create a text buffer with a good estimate of the size that adding
1460 newlines will expand it to. Since it's a text buffer, if we guess
1461 wrong, it will fail softly, and simply expand the size */
1462 outBuf = BufCreatePreallocated((endPos-startPos) + (endPos-startPos)/5);
1463 outPos = 0;
1465 /* Go (displayed) line by line through the buffer, adding newlines where
1466 the text is wrapped at some character other than an existing newline */
1467 fromPos = startPos;
1468 toPos = TextDCountForwardNLines(textD, startPos, 1, False);
1469 while (toPos < endPos) {
1470 BufCopyFromBuf(buf, outBuf, fromPos, toPos, outPos);
1471 outPos += toPos - fromPos;
1472 c = BufGetCharacter(outBuf, outPos-1);
1473 if (c == ' ' || c == '\t')
1474 BufReplace(outBuf, outPos-1, outPos, "\n");
1475 else if (c != '\n') {
1476 BufInsert(outBuf, outPos, "\n");
1477 outPos++;
1479 fromPos = toPos;
1480 toPos = TextDCountForwardNLines(textD, fromPos, 1, True);
1482 BufCopyFromBuf(buf, outBuf, fromPos, endPos, outPos);
1484 /* return the contents of the output buffer as a string */
1485 outString = BufGetAll(outBuf);
1486 *outLen = outBuf->length;
1487 BufFree(outBuf);
1488 return outString;
1492 ** Return the (statically allocated) action table for menu item actions.
1494 ** Warning: This routine can only be used before the first text widget is
1495 ** created! After that, apparently, Xt takes over the table and overwrites
1496 ** it with its own version. XtGetActionList is preferable, but is not
1497 ** available before X11R5.
1499 XtActionsRec *TextGetActions(int *nActions)
1501 *nActions = XtNumber(actionsList);
1502 return actionsList;
1505 static void grabFocusAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
1507 XButtonEvent *e = &event->xbutton;
1508 TextWidget tw = (TextWidget)w;
1509 textDisp *textD = tw->text.textD;
1510 Time lastBtnDown = tw->text.lastBtnDown;
1511 int row, column;
1513 /* Indicate state for future events, PRIMARY_CLICKED indicates that
1514 the proper initialization has been done for primary dragging and/or
1515 multi-clicking. Also record the timestamp for multi-click processing */
1516 tw->text.dragState = PRIMARY_CLICKED;
1517 tw->text.lastBtnDown = e->time;
1519 /* Become owner of the MOTIF_DESTINATION selection, making this widget
1520 the designated recipient of secondary quick actions in Motif XmText
1521 widgets and in other NEdit text widgets */
1522 TakeMotifDestination(w, e->time);
1524 /* Check for possible multi-click sequence in progress */
1525 if (tw->text.multiClickState != NO_CLICKS) {
1526 if (e->time < lastBtnDown + XtGetMultiClickTime(XtDisplay(w))) {
1527 if (tw->text.multiClickState == ONE_CLICK) {
1528 selectWord(w, e->x);
1529 callCursorMovementCBs(w, event);
1530 return;
1531 } else if (tw->text.multiClickState == TWO_CLICKS) {
1532 selectLine(w);
1533 callCursorMovementCBs(w, event);
1534 return;
1535 } else if (tw->text.multiClickState == THREE_CLICKS) {
1536 BufSelect(textD->buffer, 0, textD->buffer->length);
1537 return;
1538 } else if (tw->text.multiClickState > THREE_CLICKS)
1539 tw->text.multiClickState = NO_CLICKS;
1540 } else
1541 tw->text.multiClickState = NO_CLICKS;
1544 /* Clear any existing selections */
1545 BufUnselect(textD->buffer);
1547 /* Move the cursor to the pointer location */
1548 moveDestinationAP(w, event, args, nArgs);
1550 /* Record the site of the initial button press and the initial character
1551 position so subsequent motion events and clicking can decide when and
1552 where to begin a primary selection */
1553 tw->text.btnDownX = e->x;
1554 tw->text.btnDownY = e->y;
1555 tw->text.anchor = TextDGetInsertPosition(textD);
1556 TextDXYToUnconstrainedPosition(textD, e->x, e->y, &row, &column);
1557 column = TextDOffsetWrappedColumn(textD, row, column);
1558 tw->text.rectAnchor = column;
1561 static void moveDestinationAP(Widget w, XEvent *event, String *args,
1562 Cardinal *nArgs)
1564 XButtonEvent *e = &event->xbutton;
1565 textDisp *textD = ((TextWidget)w)->text.textD;
1567 /* Get input focus */
1568 XmProcessTraversal(w, XmTRAVERSE_CURRENT);
1570 /* Move the cursor */
1571 TextDSetInsertPosition(textD, TextDXYToPosition(textD, e->x, e->y));
1572 checkAutoShowInsertPos(w);
1573 callCursorMovementCBs(w, event);
1576 static void extendAdjustAP(Widget w, XEvent *event, String *args,
1577 Cardinal *nArgs)
1579 TextWidget tw = (TextWidget)w;
1580 XMotionEvent *e = &event->xmotion;
1581 int dragState = tw->text.dragState;
1582 int rectDrag = hasKey("rect", args, nArgs);
1584 /* Make sure the proper initialization was done on mouse down */
1585 if (dragState != PRIMARY_DRAG && dragState != PRIMARY_CLICKED &&
1586 dragState != PRIMARY_RECT_DRAG)
1587 return;
1589 /* If the selection hasn't begun, decide whether the mouse has moved
1590 far enough from the initial mouse down to be considered a drag */
1591 if (tw->text.dragState == PRIMARY_CLICKED) {
1592 if (abs(e->x - tw->text.btnDownX) > SELECT_THRESHOLD ||
1593 abs(e->y - tw->text.btnDownY) > SELECT_THRESHOLD)
1594 tw->text.dragState = rectDrag ? PRIMARY_RECT_DRAG : PRIMARY_DRAG;
1595 else
1596 return;
1599 /* If "rect" argument has appeared or disappeared, keep dragState up
1600 to date about which type of drag this is */
1601 tw->text.dragState = rectDrag ? PRIMARY_RECT_DRAG : PRIMARY_DRAG;
1603 /* Record the new position for the autoscrolling timer routine, and
1604 engage or disengage the timer if the mouse is in/out of the window */
1605 checkAutoScroll(tw, e->x, e->y);
1607 /* Adjust the selection and move the cursor */
1608 adjustSelection(tw, e->x, e->y);
1611 static void extendStartAP(Widget w, XEvent *event, String *args,
1612 Cardinal *nArgs)
1614 XMotionEvent *e = &event->xmotion;
1615 textDisp *textD = ((TextWidget)w)->text.textD;
1616 textBuffer *buf = textD->buffer;
1617 selection *sel = &buf->primary;
1618 int anchor, rectAnchor, anchorLineStart, newPos, row, column;
1620 /* Find the new anchor point for the rest of this drag operation */
1621 newPos = TextDXYToPosition(textD, e->x, e->y);
1622 TextDXYToUnconstrainedPosition(textD, e->x, e->y, &row, &column);
1623 column = TextDOffsetWrappedColumn(textD, row, column);
1624 if (sel->selected) {
1625 if (sel->rectangular) {
1626 rectAnchor = column < (sel->rectEnd + sel->rectStart) / 2 ?
1627 sel->rectEnd : sel->rectStart;
1628 anchorLineStart = BufStartOfLine(buf, newPos <
1629 (sel->end + sel->start) / 2 ? sel->end : sel->start);
1630 anchor = BufCountForwardDispChars(buf, anchorLineStart, rectAnchor);
1631 } else {
1632 if (abs(newPos - sel->start) < abs(newPos - sel->end))
1633 anchor = sel->end;
1634 else
1635 anchor = sel->start;
1636 anchorLineStart = BufStartOfLine(buf, anchor);
1637 rectAnchor = BufCountDispChars(buf, anchorLineStart, anchor);
1639 } else {
1640 anchor = TextDGetInsertPosition(textD);
1641 anchorLineStart = BufStartOfLine(buf, anchor);
1642 rectAnchor = BufCountDispChars(buf, anchorLineStart, anchor);
1644 ((TextWidget)w)->text.anchor = anchor;
1645 ((TextWidget)w)->text.rectAnchor = rectAnchor;
1647 /* Make the new selection */
1648 if (hasKey("rect", args, nArgs))
1649 BufRectSelect(buf, BufStartOfLine(buf, min(anchor, newPos)),
1650 BufEndOfLine(buf, max(anchor, newPos)),
1651 min(rectAnchor, column), max(rectAnchor, column));
1652 else
1653 BufSelect(buf, min(anchor, newPos), max(anchor, newPos));
1655 /* Never mind the motion threshold, go right to dragging since
1656 extend-start is unambiguously the start of a selection */
1657 ((TextWidget)w)->text.dragState = PRIMARY_DRAG;
1659 /* Don't do by-word or by-line adjustment, just by character */
1660 ((TextWidget)w)->text.multiClickState = NO_CLICKS;
1662 /* Move the cursor */
1663 TextDSetInsertPosition(textD, newPos);
1664 callCursorMovementCBs(w, event);
1667 static void extendEndAP(Widget w, XEvent *event, String *args,
1668 Cardinal *nArgs)
1670 XButtonEvent *e = &event->xbutton;
1671 TextWidget tw = (TextWidget)w;
1673 if (tw->text.dragState == PRIMARY_CLICKED &&
1674 tw->text.lastBtnDown <= e->time + XtGetMultiClickTime(XtDisplay(w)))
1675 tw->text.multiClickState++;
1676 endDrag(w);
1679 static void processCancelAP(Widget w, XEvent *event, String *args,
1680 Cardinal *nArgs)
1682 int dragState = ((TextWidget)w)->text.dragState;
1683 textBuffer *buf = ((TextWidget)w)->text.textD->buffer;
1684 textDisp *textD = ((TextWidget)w)->text.textD;
1686 /* If there's a calltip displayed, kill it. */
1687 TextDKillCalltip(textD, 0);
1689 if (dragState == PRIMARY_DRAG || dragState == PRIMARY_RECT_DRAG)
1690 BufUnselect(buf);
1691 cancelDrag(w);
1694 static void secondaryStartAP(Widget w, XEvent *event, String *args,
1695 Cardinal *nArgs)
1697 XMotionEvent *e = &event->xmotion;
1698 textDisp *textD = ((TextWidget)w)->text.textD;
1699 textBuffer *buf = textD->buffer;
1700 selection *sel = &buf->secondary;
1701 int anchor, pos, row, column;
1703 /* Find the new anchor point and make the new selection */
1704 pos = TextDXYToPosition(textD, e->x, e->y);
1705 if (sel->selected) {
1706 if (abs(pos - sel->start) < abs(pos - sel->end))
1707 anchor = sel->end;
1708 else
1709 anchor = sel->start;
1710 BufSecondarySelect(buf, anchor, pos);
1711 } else
1712 anchor = pos;
1714 /* Record the site of the initial button press and the initial character
1715 position so subsequent motion events can decide when to begin a
1716 selection, (and where the selection began) */
1717 ((TextWidget)w)->text.btnDownX = e->x;
1718 ((TextWidget)w)->text.btnDownY = e->y;
1719 ((TextWidget)w)->text.anchor = pos;
1720 TextDXYToUnconstrainedPosition(textD, e->x, e->y, &row, &column);
1721 column = TextDOffsetWrappedColumn(textD, row, column);
1722 ((TextWidget)w)->text.rectAnchor = column;
1723 ((TextWidget)w)->text.dragState = SECONDARY_CLICKED;
1726 static void secondaryOrDragStartAP(Widget w, XEvent *event, String *args,
1727 Cardinal *nArgs)
1729 XMotionEvent *e = &event->xmotion;
1730 textDisp *textD = ((TextWidget)w)->text.textD;
1731 textBuffer *buf = textD->buffer;
1733 /* If the click was outside of the primary selection, this is not
1734 a drag, start a secondary selection */
1735 if (!buf->primary.selected || !TextDInSelection(textD, e->x, e->y)) {
1736 secondaryStartAP(w, event, args, nArgs);
1737 return;
1740 if (checkReadOnly(w))
1741 return;
1743 /* Record the site of the initial button press and the initial character
1744 position so subsequent motion events can decide when to begin a
1745 drag, and where to drag to */
1746 ((TextWidget)w)->text.btnDownX = e->x;
1747 ((TextWidget)w)->text.btnDownY = e->y;
1748 ((TextWidget)w)->text.dragState = CLICKED_IN_SELECTION;
1751 static void secondaryAdjustAP(Widget w, XEvent *event, String *args,
1752 Cardinal *nArgs)
1754 TextWidget tw = (TextWidget)w;
1755 XMotionEvent *e = &event->xmotion;
1756 int dragState = tw->text.dragState;
1757 int rectDrag = hasKey("rect", args, nArgs);
1759 /* Make sure the proper initialization was done on mouse down */
1760 if (dragState != SECONDARY_DRAG && dragState != SECONDARY_RECT_DRAG &&
1761 dragState != SECONDARY_CLICKED)
1762 return;
1764 /* If the selection hasn't begun, decide whether the mouse has moved
1765 far enough from the initial mouse down to be considered a drag */
1766 if (tw->text.dragState == SECONDARY_CLICKED) {
1767 if (abs(e->x - tw->text.btnDownX) > SELECT_THRESHOLD ||
1768 abs(e->y - tw->text.btnDownY) > SELECT_THRESHOLD)
1769 tw->text.dragState = rectDrag ? SECONDARY_RECT_DRAG: SECONDARY_DRAG;
1770 else
1771 return;
1774 /* If "rect" argument has appeared or disappeared, keep dragState up
1775 to date about which type of drag this is */
1776 tw->text.dragState = rectDrag ? SECONDARY_RECT_DRAG : SECONDARY_DRAG;
1778 /* Record the new position for the autoscrolling timer routine, and
1779 engage or disengage the timer if the mouse is in/out of the window */
1780 checkAutoScroll(tw, e->x, e->y);
1782 /* Adjust the selection */
1783 adjustSecondarySelection(tw, e->x, e->y);
1786 static void secondaryOrDragAdjustAP(Widget w, XEvent *event, String *args,
1787 Cardinal *nArgs)
1789 TextWidget tw = (TextWidget)w;
1790 XMotionEvent *e = &event->xmotion;
1791 int dragState = tw->text.dragState;
1793 /* Only dragging of blocks of text is handled in this action proc.
1794 Otherwise, defer to secondaryAdjust to handle the rest */
1795 if (dragState != CLICKED_IN_SELECTION && dragState != PRIMARY_BLOCK_DRAG) {
1796 secondaryAdjustAP(w, event, args, nArgs);
1797 return;
1800 /* Decide whether the mouse has moved far enough from the
1801 initial mouse down to be considered a drag */
1802 if (tw->text.dragState == CLICKED_IN_SELECTION) {
1803 if (abs(e->x - tw->text.btnDownX) > SELECT_THRESHOLD ||
1804 abs(e->y - tw->text.btnDownY) > SELECT_THRESHOLD)
1805 BeginBlockDrag(tw);
1806 else
1807 return;
1810 /* Record the new position for the autoscrolling timer routine, and
1811 engage or disengage the timer if the mouse is in/out of the window */
1812 checkAutoScroll(tw, e->x, e->y);
1814 /* Adjust the selection */
1815 BlockDragSelection(tw, e->x, e->y, hasKey("overlay", args, nArgs) ?
1816 (hasKey("copy", args, nArgs) ? DRAG_OVERLAY_COPY : DRAG_OVERLAY_MOVE) :
1817 (hasKey("copy", args, nArgs) ? DRAG_COPY : DRAG_MOVE));
1820 static void copyToAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
1822 XButtonEvent *e = &event->xbutton;
1823 TextWidget tw = (TextWidget)w;
1824 textDisp *textD = tw->text.textD;
1825 int dragState = tw->text.dragState;
1826 textBuffer *buf = textD->buffer;
1827 selection *secondary = &buf->secondary, *primary = &buf->primary;
1828 int rectangular = secondary->rectangular;
1829 char *textToCopy;
1830 int insertPos, lineStart, column;
1832 endDrag(w);
1833 if (!((dragState == SECONDARY_DRAG && secondary->selected) ||
1834 (dragState == SECONDARY_RECT_DRAG && secondary->selected) ||
1835 dragState == SECONDARY_CLICKED || dragState == NOT_CLICKED))
1836 return;
1837 if (!(secondary->selected && !((TextWidget)w)->text.motifDestOwner)) {
1838 if (checkReadOnly(w)) {
1839 BufSecondaryUnselect(buf);
1840 return;
1843 if (secondary->selected) {
1844 if (tw->text.motifDestOwner) {
1845 TextDBlankCursor(textD);
1846 textToCopy = BufGetSecSelectText(buf);
1847 if (primary->selected && rectangular) {
1848 insertPos = TextDGetInsertPosition(textD);
1849 BufReplaceSelected(buf, textToCopy);
1850 TextDSetInsertPosition(textD, buf->cursorPosHint);
1851 } else if (rectangular) {
1852 insertPos = TextDGetInsertPosition(textD);
1853 lineStart = BufStartOfLine(buf, insertPos);
1854 column = BufCountDispChars(buf, lineStart, insertPos);
1855 BufInsertCol(buf, column, lineStart, textToCopy, NULL, NULL);
1856 TextDSetInsertPosition(textD, buf->cursorPosHint);
1857 } else
1858 TextInsertAtCursor(w, textToCopy, event, True,
1859 tw->text.autoWrapPastedText);
1860 XtFree(textToCopy);
1861 BufSecondaryUnselect(buf);
1862 TextDUnblankCursor(textD);
1863 } else
1864 SendSecondarySelection(w, e->time, False);
1865 } else if (primary->selected) {
1866 textToCopy = BufGetSelectionText(buf);
1867 TextDSetInsertPosition(textD, TextDXYToPosition(textD, e->x, e->y));
1868 TextInsertAtCursor(w, textToCopy, event, False,
1869 tw->text.autoWrapPastedText);
1870 XtFree(textToCopy);
1871 } else {
1872 TextDSetInsertPosition(textD, TextDXYToPosition(textD, e->x, e->y));
1873 InsertPrimarySelection(w, e->time, False);
1877 static void copyToOrEndDragAP(Widget w, XEvent *event, String *args,
1878 Cardinal *nArgs)
1880 int dragState = ((TextWidget)w)->text.dragState;
1882 if (dragState != PRIMARY_BLOCK_DRAG) {
1883 copyToAP(w, event, args, nArgs);
1884 return;
1887 FinishBlockDrag((TextWidget)w);
1890 static void moveToAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
1892 XButtonEvent *e = &event->xbutton;
1893 textDisp *textD = ((TextWidget)w)->text.textD;
1894 int dragState = ((TextWidget)w)->text.dragState;
1895 textBuffer *buf = textD->buffer;
1896 selection *secondary = &buf->secondary, *primary = &buf->primary;
1897 int insertPos, rectangular = secondary->rectangular;
1898 int column, lineStart;
1899 char *textToCopy;
1901 endDrag(w);
1902 if (!((dragState == SECONDARY_DRAG && secondary->selected) ||
1903 (dragState == SECONDARY_RECT_DRAG && secondary->selected) ||
1904 dragState == SECONDARY_CLICKED || dragState == NOT_CLICKED))
1905 return;
1906 if (checkReadOnly(w)) {
1907 BufSecondaryUnselect(buf);
1908 return;
1911 if (secondary->selected) {
1912 if (((TextWidget)w)->text.motifDestOwner) {
1913 textToCopy = BufGetSecSelectText(buf);
1914 if (primary->selected && rectangular) {
1915 insertPos = TextDGetInsertPosition(textD);
1916 BufReplaceSelected(buf, textToCopy);
1917 TextDSetInsertPosition(textD, buf->cursorPosHint);
1918 } else if (rectangular) {
1919 insertPos = TextDGetInsertPosition(textD);
1920 lineStart = BufStartOfLine(buf, insertPos);
1921 column = BufCountDispChars(buf, lineStart, insertPos);
1922 BufInsertCol(buf, column, lineStart, textToCopy, NULL, NULL);
1923 TextDSetInsertPosition(textD, buf->cursorPosHint);
1924 } else
1925 TextInsertAtCursor(w, textToCopy, event, True,
1926 ((TextWidget)w)->text.autoWrapPastedText);
1927 XtFree(textToCopy);
1928 BufRemoveSecSelect(buf);
1929 BufSecondaryUnselect(buf);
1930 } else
1931 SendSecondarySelection(w, e->time, True);
1932 } else if (primary->selected) {
1933 textToCopy = BufGetRange(buf, primary->start, primary->end);
1934 TextDSetInsertPosition(textD, TextDXYToPosition(textD, e->x, e->y));
1935 TextInsertAtCursor(w, textToCopy, event, False,
1936 ((TextWidget)w)->text.autoWrapPastedText);
1937 XtFree(textToCopy);
1938 BufRemoveSelected(buf);
1939 BufUnselect(buf);
1940 } else {
1941 TextDSetInsertPosition(textD, TextDXYToPosition(textD, e->x, e->y));
1942 MovePrimarySelection(w, e->time, False);
1946 static void moveToOrEndDragAP(Widget w, XEvent *event, String *args,
1947 Cardinal *nArgs)
1949 int dragState = ((TextWidget)w)->text.dragState;
1951 if (dragState != PRIMARY_BLOCK_DRAG) {
1952 moveToAP(w, event, args, nArgs);
1953 return;
1956 FinishBlockDrag((TextWidget)w);
1959 static void endDragAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
1961 if (((TextWidget)w)->text.dragState == PRIMARY_BLOCK_DRAG)
1962 FinishBlockDrag((TextWidget)w);
1963 else
1964 endDrag(w);
1967 static void exchangeAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
1969 XButtonEvent *e = &event->xbutton;
1970 textDisp *textD = ((TextWidget)w)->text.textD;
1971 textBuffer *buf = textD->buffer;
1972 selection *sec = &buf->secondary, *primary = &buf->primary;
1973 char *primaryText, *secText;
1974 int newPrimaryStart, newPrimaryEnd, secWasRect;
1975 int dragState = ((TextWidget)w)->text.dragState; /* save before endDrag */
1976 int silent = hasKey("nobell", args, nArgs);
1978 endDrag(w);
1979 if (checkReadOnly(w))
1980 return;
1982 /* If there's no secondary selection here, or the primary and secondary
1983 selection overlap, just beep and return */
1984 if (!sec->selected || (primary->selected &&
1985 ((primary->start <= sec->start && primary->end > sec->start) ||
1986 (sec->start <= primary->start && sec->end > primary->start))))
1988 BufSecondaryUnselect(buf);
1989 ringIfNecessary(silent, w);
1990 /* If there's no secondary selection, but the primary selection is
1991 being dragged, we must not forget to finish the dragging.
1992 Otherwise, modifications aren't recorded. */
1993 if (dragState == PRIMARY_BLOCK_DRAG)
1994 FinishBlockDrag((TextWidget)w);
1995 return;
1998 /* if the primary selection is in another widget, use selection routines */
1999 if (!primary->selected) {
2000 ExchangeSelections(w, e->time);
2001 return;
2004 /* Both primary and secondary are in this widget, do the exchange here */
2005 primaryText = BufGetSelectionText(buf);
2006 secText = BufGetSecSelectText(buf);
2007 secWasRect = sec->rectangular;
2008 BufReplaceSecSelect(buf, primaryText);
2009 newPrimaryStart = primary->start;
2010 BufReplaceSelected(buf, secText);
2011 newPrimaryEnd = newPrimaryStart + strlen(secText);
2012 XtFree(primaryText);
2013 XtFree(secText);
2014 BufSecondaryUnselect(buf);
2015 if (secWasRect) {
2016 TextDSetInsertPosition(textD, buf->cursorPosHint);
2017 } else {
2018 BufSelect(buf, newPrimaryStart, newPrimaryEnd);
2019 TextDSetInsertPosition(textD, newPrimaryEnd);
2021 checkAutoShowInsertPos(w);
2024 static void copyPrimaryAP(Widget w, XEvent *event, String *args,
2025 Cardinal *nArgs)
2027 XKeyEvent *e = &event->xkey;
2028 TextWidget tw = (TextWidget)w;
2029 textDisp *textD = tw->text.textD;
2030 textBuffer *buf = textD->buffer;
2031 selection *primary = &buf->primary;
2032 int rectangular = hasKey("rect", args, nArgs);
2033 char *textToCopy;
2034 int insertPos, col;
2036 cancelDrag(w);
2037 if (checkReadOnly(w))
2038 return;
2039 if (primary->selected && rectangular) {
2040 textToCopy = BufGetSelectionText(buf);
2041 insertPos = TextDGetInsertPosition(textD);
2042 col = BufCountDispChars(buf, BufStartOfLine(buf, insertPos), insertPos);
2043 BufInsertCol(buf, col, insertPos, textToCopy, NULL, NULL);
2044 TextDSetInsertPosition(textD, buf->cursorPosHint);
2045 XtFree(textToCopy);
2046 checkAutoShowInsertPos(w);
2047 } else if (primary->selected) {
2048 textToCopy = BufGetSelectionText(buf);
2049 insertPos = TextDGetInsertPosition(textD);
2050 BufInsert(buf, insertPos, textToCopy);
2051 TextDSetInsertPosition(textD, insertPos + strlen(textToCopy));
2052 XtFree(textToCopy);
2053 checkAutoShowInsertPos(w);
2054 } else if (rectangular) {
2055 if (!TextDPositionToXY(textD, TextDGetInsertPosition(textD),
2056 &tw->text.btnDownX, &tw->text.btnDownY))
2057 return; /* shouldn't happen */
2058 InsertPrimarySelection(w, e->time, True);
2059 } else
2060 InsertPrimarySelection(w, e->time, False);
2063 static void cutPrimaryAP(Widget w, XEvent *event, String *args,
2064 Cardinal *nArgs)
2066 XKeyEvent *e = &event->xkey;
2067 textDisp *textD = ((TextWidget)w)->text.textD;
2068 textBuffer *buf = textD->buffer;
2069 selection *primary = &buf->primary;
2070 char *textToCopy;
2071 int rectangular = hasKey("rect", args, nArgs);
2072 int insertPos, col;
2074 cancelDrag(w);
2075 if (checkReadOnly(w))
2076 return;
2077 if (primary->selected && rectangular) {
2078 textToCopy = BufGetSelectionText(buf);
2079 insertPos = TextDGetInsertPosition(textD);
2080 col = BufCountDispChars(buf, BufStartOfLine(buf, insertPos), insertPos);
2081 BufInsertCol(buf, col, insertPos, textToCopy, NULL, NULL);
2082 TextDSetInsertPosition(textD, buf->cursorPosHint);
2083 XtFree(textToCopy);
2084 BufRemoveSelected(buf);
2085 checkAutoShowInsertPos(w);
2086 } else if (primary->selected) {
2087 textToCopy = BufGetSelectionText(buf);
2088 insertPos = TextDGetInsertPosition(textD);
2089 BufInsert(buf, insertPos, textToCopy);
2090 TextDSetInsertPosition(textD, insertPos + strlen(textToCopy));
2091 XtFree(textToCopy);
2092 BufRemoveSelected(buf);
2093 checkAutoShowInsertPos(w);
2094 } else if (rectangular) {
2095 if (!TextDPositionToXY(textD, TextDGetInsertPosition(textD),
2096 &((TextWidget)w)->text.btnDownX,
2097 &((TextWidget)w)->text.btnDownY))
2098 return; /* shouldn't happen */
2099 MovePrimarySelection(w, e->time, True);
2100 } else {
2101 MovePrimarySelection(w, e->time, False);
2105 static void mousePanAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2107 XButtonEvent *e = &event->xbutton;
2108 TextWidget tw = (TextWidget)w;
2109 textDisp *textD = tw->text.textD;
2110 int lineHeight = textD->ascent + textD->descent;
2111 int topLineNum, horizOffset;
2112 static Cursor panCursor = 0;
2114 if (tw->text.dragState == MOUSE_PAN) {
2115 TextDSetScroll(textD,
2116 (tw->text.btnDownY - e->y + lineHeight/2) / lineHeight,
2117 tw->text.btnDownX - e->x);
2118 } else if (tw->text.dragState == NOT_CLICKED) {
2119 TextDGetScroll(textD, &topLineNum, &horizOffset);
2120 tw->text.btnDownX = e->x + horizOffset;
2121 tw->text.btnDownY = e->y + topLineNum * lineHeight;
2122 tw->text.dragState = MOUSE_PAN;
2123 if (!panCursor)
2124 panCursor = XCreateFontCursor(XtDisplay(w), XC_fleur);
2125 XGrabPointer(XtDisplay(w), XtWindow(w), False,
2126 ButtonMotionMask | ButtonReleaseMask, GrabModeAsync,
2127 GrabModeAsync, None, panCursor, CurrentTime);
2128 } else
2129 cancelDrag(w);
2132 static void pasteClipboardAP(Widget w, XEvent *event, String *args,
2133 Cardinal *nArgs)
2135 if (hasKey("rect", args, nArgs))
2136 TextColPasteClipboard(w, event->xkey.time);
2137 else
2138 TextPasteClipboard(w, event->xkey.time);
2141 static void copyClipboardAP(Widget w, XEvent *event, String *args,
2142 Cardinal *nArgs)
2144 TextCopyClipboard(w, event->xkey.time);
2147 static void cutClipboardAP(Widget w, XEvent *event, String *args,
2148 Cardinal *nArgs)
2150 TextCutClipboard(w, event->xkey.time);
2153 static void insertStringAP(Widget w, XEvent *event, String *args,
2154 Cardinal *nArgs)
2156 smartIndentCBStruct smartIndent;
2157 textDisp *textD = ((TextWidget)w)->text.textD;
2159 if (*nArgs == 0)
2160 return;
2161 cancelDrag(w);
2162 if (checkReadOnly(w))
2163 return;
2164 if (((TextWidget)w)->text.smartIndent) {
2165 smartIndent.reason = CHAR_TYPED;
2166 smartIndent.pos = TextDGetInsertPosition(textD);
2167 smartIndent.indentRequest = 0;
2168 smartIndent.charsTyped = args[0];
2169 XtCallCallbacks(w, textNsmartIndentCallback, (XtPointer)&smartIndent);
2171 TextInsertAtCursor(w, args[0], event, True, True);
2172 BufUnselect((((TextWidget)w)->text.textD)->buffer);
2175 static void selfInsertAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2177 #ifdef NO_XMIM
2178 static XComposeStatus compose = {NULL, 0};
2179 #else
2180 int status;
2181 #endif
2182 XKeyEvent *e = &event->xkey;
2183 char chars[20];
2184 KeySym keysym;
2185 int nChars;
2186 smartIndentCBStruct smartIndent;
2187 textDisp *textD = ((TextWidget)w)->text.textD;
2189 #ifdef NO_XMIM
2190 nChars = XLookupString(&event->xkey, chars, 19, &keysym, &compose);
2191 if (nChars == 0)
2192 return;
2193 #else
2194 nChars = XmImMbLookupString(w, &event->xkey, chars, 19, &keysym,
2195 &status);
2196 if (nChars == 0 || status == XLookupNone ||
2197 status == XLookupKeySym || status == XBufferOverflow)
2198 return;
2199 #endif
2200 cancelDrag(w);
2201 if (checkReadOnly(w))
2202 return;
2203 TakeMotifDestination(w, e->time);
2204 chars[nChars] = '\0';
2206 /* If smart indent is on, call the smart indent callback to check the
2207 inserted character */
2208 if (((TextWidget)w)->text.smartIndent) {
2209 smartIndent.reason = CHAR_TYPED;
2210 smartIndent.pos = TextDGetInsertPosition(textD);
2211 smartIndent.indentRequest = 0;
2212 smartIndent.charsTyped = chars;
2213 XtCallCallbacks(w, textNsmartIndentCallback, (XtPointer)&smartIndent);
2215 TextInsertAtCursor(w, chars, event, True, True);
2216 BufUnselect(textD->buffer);
2219 static void newlineAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2221 if (((TextWidget)w)->text.autoIndent || ((TextWidget)w)->text.smartIndent)
2222 newlineAndIndentAP(w, event, args, nArgs);
2223 else
2224 newlineNoIndentAP(w, event, args, nArgs);
2227 static void newlineNoIndentAP(Widget w, XEvent *event, String *args,
2228 Cardinal *nArgs)
2230 XKeyEvent *e = &event->xkey;
2232 cancelDrag(w);
2233 if (checkReadOnly(w))
2234 return;
2235 TakeMotifDestination(w, e->time);
2236 simpleInsertAtCursor(w, "\n", event, True);
2237 BufUnselect((((TextWidget)w)->text.textD)->buffer);
2240 static void newlineAndIndentAP(Widget w, XEvent *event, String *args,
2241 Cardinal *nArgs)
2243 XKeyEvent *e = &event->xkey;
2244 TextWidget tw = (TextWidget)w;
2245 textDisp *textD = tw->text.textD;
2246 textBuffer *buf = textD->buffer;
2247 char *indentStr;
2248 int cursorPos, lineStartPos, column;
2250 if (checkReadOnly(w))
2251 return;
2252 cancelDrag(w);
2253 TakeMotifDestination(w, e->time);
2255 /* Create a string containing a newline followed by auto or smart
2256 indent string */
2257 cursorPos = TextDGetInsertPosition(textD);
2258 lineStartPos = BufStartOfLine(buf, cursorPos);
2259 indentStr = createIndentString(tw, buf, 0, lineStartPos,
2260 cursorPos, NULL, &column);
2262 /* Insert it at the cursor */
2263 simpleInsertAtCursor(w, indentStr, event, True);
2264 XtFree(indentStr);
2266 /* If emulated tabs are on, make the inserted indent deletable by tab */
2267 if (tw->text.emulateTabs)
2268 tw->text.emTabsBeforeCursor = column / tw->text.emulateTabs;
2270 BufUnselect(buf);
2273 static void processTabAP(Widget w, XEvent *event, String *args,
2274 Cardinal *nArgs)
2276 textDisp *textD = ((TextWidget)w)->text.textD;
2277 textBuffer *buf = textD->buffer;
2278 selection *sel = &buf->primary;
2279 int emTabDist = ((TextWidget)w)->text.emulateTabs;
2280 int emTabsBeforeCursor = ((TextWidget)w)->text.emTabsBeforeCursor;
2281 int insertPos, indent, startIndent, toIndent, lineStart, tabWidth;
2282 char *outStr, *outPtr;
2284 if (checkReadOnly(w))
2285 return;
2286 cancelDrag(w);
2287 TakeMotifDestination(w, event->xkey.time);
2289 /* If emulated tabs are off, just insert a tab */
2290 if (emTabDist <= 0) {
2291 TextInsertAtCursor(w, "\t", event, True, True);
2292 return;
2295 /* Find the starting and ending indentation. If the tab is to
2296 replace an existing selection, use the start of the selection
2297 instead of the cursor position as the indent. When replacing
2298 rectangular selections, tabs are automatically recalculated as
2299 if the inserted text began at the start of the line */
2300 insertPos = pendingSelection(w) ?
2301 sel->start : TextDGetInsertPosition(textD);
2302 lineStart = BufStartOfLine(buf, insertPos);
2303 if (pendingSelection(w) && sel->rectangular)
2304 insertPos = BufCountForwardDispChars(buf, lineStart, sel->rectStart);
2305 startIndent = BufCountDispChars(buf, lineStart, insertPos);
2306 toIndent = startIndent + emTabDist - (startIndent % emTabDist);
2307 if (pendingSelection(w) && sel->rectangular) {
2308 toIndent -= startIndent;
2309 startIndent = 0;
2312 /* Allocate a buffer assuming all the inserted characters will be spaces */
2313 outStr = XtMalloc(toIndent - startIndent + 1);
2315 /* Add spaces and tabs to outStr until it reaches toIndent */
2316 outPtr = outStr;
2317 indent = startIndent;
2318 while (indent < toIndent) {
2319 tabWidth = BufCharWidth('\t', indent, buf->tabDist, buf->nullSubsChar);
2320 if (buf->useTabs && tabWidth > 1 && indent + tabWidth <= toIndent) {
2321 *outPtr++ = '\t';
2322 indent += tabWidth;
2323 } else {
2324 *outPtr++ = ' ';
2325 indent++;
2328 *outPtr = '\0';
2330 /* Insert the emulated tab */
2331 TextInsertAtCursor(w, outStr, event, True, True);
2332 XtFree(outStr);
2334 /* Restore and ++ emTabsBeforeCursor cleared by TextInsertAtCursor */
2335 ((TextWidget)w)->text.emTabsBeforeCursor = emTabsBeforeCursor + 1;
2337 BufUnselect(buf);
2340 static void deleteSelectionAP(Widget w, XEvent *event, String *args,
2341 Cardinal *nArgs)
2343 XKeyEvent *e = &event->xkey;
2345 cancelDrag(w);
2346 if (checkReadOnly(w))
2347 return;
2348 TakeMotifDestination(w, e->time);
2349 deletePendingSelection(w, event);
2352 static void deletePreviousCharacterAP(Widget w, XEvent *event, String *args,
2353 Cardinal *nArgs)
2355 XKeyEvent *e = &event->xkey;
2356 textDisp *textD = ((TextWidget)w)->text.textD;
2357 int insertPos = TextDGetInsertPosition(textD);
2358 char c;
2359 int silent = hasKey("nobell", args, nArgs);
2361 cancelDrag(w);
2362 if (checkReadOnly(w))
2363 return;
2364 TakeMotifDestination(w, e->time);
2365 if (deletePendingSelection(w, event))
2366 return;
2367 if (insertPos == 0) {
2368 ringIfNecessary(silent, w);
2369 return;
2371 if (deleteEmulatedTab(w, event))
2372 return;
2373 if (((TextWidget)w)->text.overstrike) {
2374 c = BufGetCharacter(textD->buffer, insertPos - 1);
2375 if (c == '\n')
2376 BufRemove(textD->buffer, insertPos - 1, insertPos);
2377 else if (c != '\t')
2378 BufReplace(textD->buffer, insertPos - 1, insertPos, " ");
2379 } else {
2380 BufRemove(textD->buffer, insertPos - 1, insertPos);
2382 TextDSetInsertPosition(textD, insertPos - 1);
2383 checkAutoShowInsertPos(w);
2384 callCursorMovementCBs(w, event);
2387 static void deleteNextCharacterAP(Widget w, XEvent *event, String *args,
2388 Cardinal *nArgs)
2390 XKeyEvent *e = &event->xkey;
2391 textDisp *textD = ((TextWidget)w)->text.textD;
2392 int insertPos = TextDGetInsertPosition(textD);
2393 int silent = hasKey("nobell", args, nArgs);
2395 cancelDrag(w);
2396 if (checkReadOnly(w))
2397 return;
2398 TakeMotifDestination(w, e->time);
2399 if (deletePendingSelection(w, event))
2400 return;
2401 if (insertPos == textD->buffer->length) {
2402 ringIfNecessary(silent, w);
2403 return;
2405 BufRemove(textD->buffer, insertPos , insertPos + 1);
2406 checkAutoShowInsertPos(w);
2407 callCursorMovementCBs(w, event);
2410 static void deletePreviousWordAP(Widget w, XEvent *event, String *args,
2411 Cardinal *nArgs)
2413 XKeyEvent *e = &event->xkey;
2414 textDisp *textD = ((TextWidget)w)->text.textD;
2415 int insertPos = TextDGetInsertPosition(textD);
2416 int pos, lineStart = BufStartOfLine(textD->buffer, insertPos);
2417 char *delimiters = ((TextWidget)w)->text.delimiters;
2418 int silent = hasKey("nobell", args, nArgs);
2420 cancelDrag(w);
2421 if (checkReadOnly(w))
2422 return;
2423 TakeMotifDestination(w, e->time);
2424 if (deletePendingSelection(w, event))
2425 return;
2426 if (insertPos == lineStart) {
2427 ringIfNecessary(silent, w);
2428 return;
2430 pos = max(insertPos - 1, 0);
2431 while (strchr(delimiters, BufGetCharacter(textD->buffer, pos)) != NULL &&
2432 pos != lineStart)
2433 pos--;
2434 pos = startOfWord((TextWidget)w, pos);
2435 BufRemove(textD->buffer, pos, insertPos);
2436 checkAutoShowInsertPos(w);
2437 callCursorMovementCBs(w, event);
2440 static void deleteNextWordAP(Widget w, XEvent *event, String *args,
2441 Cardinal *nArgs)
2443 XKeyEvent *e = &event->xkey;
2444 textDisp *textD = ((TextWidget)w)->text.textD;
2445 int insertPos = TextDGetInsertPosition(textD);
2446 int pos, lineEnd = BufEndOfLine(textD->buffer, insertPos);
2447 char *delimiters = ((TextWidget)w)->text.delimiters;
2448 int silent = hasKey("nobell", args, nArgs);
2450 cancelDrag(w);
2451 if (checkReadOnly(w))
2452 return;
2453 TakeMotifDestination(w, e->time);
2454 if (deletePendingSelection(w, event))
2455 return;
2456 if (insertPos == lineEnd) {
2457 ringIfNecessary(silent, w);
2458 return;
2460 pos = insertPos;
2461 while (strchr(delimiters, BufGetCharacter(textD->buffer, pos)) != NULL &&
2462 pos != lineEnd)
2463 pos++;
2464 pos = endOfWord((TextWidget)w, pos);
2465 BufRemove(textD->buffer, insertPos, pos);
2466 checkAutoShowInsertPos(w);
2467 callCursorMovementCBs(w, event);
2470 static void deleteToEndOfLineAP(Widget w, XEvent *event, String *args,
2471 Cardinal *nArgs)
2473 XKeyEvent *e = &event->xkey;
2474 textDisp *textD = ((TextWidget)w)->text.textD;
2475 int insertPos = TextDGetInsertPosition(textD);
2476 int endOfLine = TextDEndOfLine(textD, insertPos, False);
2477 int silent = hasKey("nobell", args, nArgs);
2479 cancelDrag(w);
2480 if (checkReadOnly(w))
2481 return;
2482 TakeMotifDestination(w, e->time);
2483 if (deletePendingSelection(w, event))
2484 return;
2485 if (insertPos == endOfLine) {
2486 ringIfNecessary(silent, w);
2487 return;
2489 BufRemove(textD->buffer, insertPos, endOfLine);
2490 checkAutoShowInsertPos(w);
2491 callCursorMovementCBs(w, event);
2494 static void deleteToStartOfLineAP(Widget w, XEvent *event, String *args,
2495 Cardinal *nArgs)
2497 XKeyEvent *e = &event->xkey;
2498 textDisp *textD = ((TextWidget)w)->text.textD;
2499 int insertPos = TextDGetInsertPosition(textD);
2500 int startOfLine = BufStartOfLine(textD->buffer, insertPos);
2501 int silent = hasKey("nobell", args, nArgs);
2503 cancelDrag(w);
2504 if (checkReadOnly(w))
2505 return;
2506 TakeMotifDestination(w, e->time);
2507 if (deletePendingSelection(w, event))
2508 return;
2509 if (insertPos == startOfLine) {
2510 ringIfNecessary(silent, w);
2511 return;
2513 BufRemove(textD->buffer, startOfLine, insertPos);
2514 checkAutoShowInsertPos(w);
2515 callCursorMovementCBs(w, event);
2518 static void forwardCharacterAP(Widget w, XEvent *event, String *args,
2519 Cardinal *nArgs)
2521 int insertPos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
2522 int silent = hasKey("nobell", args, nArgs);
2524 cancelDrag(w);
2525 if (!TextDMoveRight(((TextWidget)w)->text.textD))
2526 ringIfNecessary(silent, w);
2527 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2528 checkAutoShowInsertPos(w);
2529 callCursorMovementCBs(w, event);
2532 static void backwardCharacterAP(Widget w, XEvent *event, String *args,
2533 Cardinal *nArgs)
2535 int insertPos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
2536 int silent = hasKey("nobell", args, nArgs);
2538 cancelDrag(w);
2539 if (!TextDMoveLeft(((TextWidget)w)->text.textD))
2540 ringIfNecessary(silent, w);
2541 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2542 checkAutoShowInsertPos(w);
2543 callCursorMovementCBs(w, event);
2546 static void forwardWordAP(Widget w, XEvent *event, String *args,
2547 Cardinal *nArgs)
2549 textDisp *textD = ((TextWidget)w)->text.textD;
2550 textBuffer *buf = textD->buffer;
2551 int pos, insertPos = TextDGetInsertPosition(textD);
2552 char *delimiters = ((TextWidget)w)->text.delimiters;
2553 int silent = hasKey("nobell", args, nArgs);
2555 cancelDrag(w);
2556 if (insertPos == buf->length) {
2557 ringIfNecessary(silent, w);
2558 return;
2560 pos = insertPos;
2561 if (hasKey("tail", args, nArgs)) {
2562 for (; pos<buf->length; pos++) {
2563 if (strchr(delimiters, BufGetCharacter(buf, pos)) == NULL)
2564 break;
2566 if (strchr(delimiters, BufGetCharacter(buf, pos)) == NULL)
2567 pos = endOfWord((TextWidget)w, pos);
2569 else {
2570 if (strchr(delimiters, BufGetCharacter(buf, pos)) == NULL)
2571 pos = endOfWord((TextWidget)w, pos);
2572 for (; pos<buf->length; pos++) {
2573 if (strchr(delimiters, BufGetCharacter(buf, pos)) == NULL)
2574 break;
2577 TextDSetInsertPosition(textD, pos);
2578 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2579 checkAutoShowInsertPos(w);
2580 callCursorMovementCBs(w, event);
2583 static void backwardWordAP(Widget w, XEvent *event, String *args,
2584 Cardinal *nArgs)
2586 textDisp *textD = ((TextWidget)w)->text.textD;
2587 textBuffer *buf = textD->buffer;
2588 int pos, insertPos = TextDGetInsertPosition(textD);
2589 char *delimiters = ((TextWidget)w)->text.delimiters;
2590 int silent = hasKey("nobell", args, nArgs);
2592 cancelDrag(w);
2593 if (insertPos == 0) {
2594 ringIfNecessary(silent, w);
2595 return;
2597 pos = max(insertPos - 1, 0);
2598 while (strchr(delimiters, BufGetCharacter(buf, pos)) != NULL && pos > 0)
2599 pos--;
2600 pos = startOfWord((TextWidget)w, pos);
2602 TextDSetInsertPosition(textD, pos);
2603 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2604 checkAutoShowInsertPos(w);
2605 callCursorMovementCBs(w, event);
2608 static void forwardParagraphAP(Widget w, XEvent *event, String *args,
2609 Cardinal *nArgs)
2611 textDisp *textD = ((TextWidget)w)->text.textD;
2612 int pos, insertPos = TextDGetInsertPosition(textD);
2613 textBuffer *buf = textD->buffer;
2614 char c;
2615 static char whiteChars[] = " \t";
2616 int silent = hasKey("nobell", args, nArgs);
2618 cancelDrag(w);
2619 if (insertPos == buf->length) {
2620 ringIfNecessary(silent, w);
2621 return;
2623 pos = min(BufEndOfLine(buf, insertPos)+1, buf->length);
2624 while (pos < buf->length) {
2625 c = BufGetCharacter(buf, pos);
2626 if (c == '\n')
2627 break;
2628 if (strchr(whiteChars, c) != NULL)
2629 pos++;
2630 else
2631 pos = min(BufEndOfLine(buf, pos)+1, buf->length);
2633 TextDSetInsertPosition(textD, min(pos+1, buf->length));
2634 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2635 checkAutoShowInsertPos(w);
2636 callCursorMovementCBs(w, event);
2639 static void backwardParagraphAP(Widget w, XEvent *event, String *args,
2640 Cardinal *nArgs)
2642 textDisp *textD = ((TextWidget)w)->text.textD;
2643 int parStart, pos, insertPos = TextDGetInsertPosition(textD);
2644 textBuffer *buf = textD->buffer;
2645 char c;
2646 static char whiteChars[] = " \t";
2647 int silent = hasKey("nobell", args, nArgs);
2649 cancelDrag(w);
2650 if (insertPos == 0) {
2651 ringIfNecessary(silent, w);
2652 return;
2654 parStart = BufStartOfLine(buf, max(insertPos-1, 0));
2655 pos = max(parStart - 2, 0);
2656 while (pos > 0) {
2657 c = BufGetCharacter(buf, pos);
2658 if (c == '\n')
2659 break;
2660 if (strchr(whiteChars, c) != NULL)
2661 pos--;
2662 else {
2663 parStart = BufStartOfLine(buf, pos);
2664 pos = max(parStart - 2, 0);
2667 TextDSetInsertPosition(textD, parStart);
2668 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2669 checkAutoShowInsertPos(w);
2670 callCursorMovementCBs(w, event);
2673 static void keySelectAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2675 textDisp *textD = ((TextWidget)w)->text.textD;
2676 int stat, insertPos = TextDGetInsertPosition(textD);
2677 int silent = hasKey("nobell", args, nArgs);
2679 cancelDrag(w);
2680 if (hasKey("left", args, nArgs)) stat = TextDMoveLeft(textD);
2681 else if (hasKey("right", args, nArgs)) stat = TextDMoveRight(textD);
2682 else if (hasKey("up", args, nArgs)) stat = TextDMoveUp(textD);
2683 else if (hasKey("down", args, nArgs)) stat = TextDMoveDown(textD);
2684 else {
2685 keyMoveExtendSelection(w, event, insertPos, hasKey("rect", args,nArgs));
2686 return;
2688 if (!stat) {
2689 ringIfNecessary(silent, w);
2691 else {
2692 keyMoveExtendSelection(w, event, insertPos, hasKey("rect", args,nArgs));
2693 checkAutoShowInsertPos(w);
2694 callCursorMovementCBs(w, event);
2698 static void processUpAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2700 int insertPos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
2701 int silent = hasKey("nobell", args, nArgs);
2703 cancelDrag(w);
2704 if (!TextDMoveUp(((TextWidget)w)->text.textD))
2705 ringIfNecessary(silent, w);
2706 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2707 checkAutoShowInsertPos(w);
2708 callCursorMovementCBs(w, event);
2711 static void processShiftUpAP(Widget w, XEvent *event, String *args,
2712 Cardinal *nArgs)
2714 int insertPos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
2715 int silent = hasKey("nobell", args, nArgs);
2717 cancelDrag(w);
2718 if (!TextDMoveUp(((TextWidget)w)->text.textD))
2719 ringIfNecessary(silent, w);
2720 keyMoveExtendSelection(w, event, insertPos, hasKey("rect", args, nArgs));
2721 checkAutoShowInsertPos(w);
2722 callCursorMovementCBs(w, event);
2725 static void processDownAP(Widget w, XEvent *event, String *args,
2726 Cardinal *nArgs)
2728 int insertPos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
2729 int silent = hasKey("nobell", args, nArgs);
2731 cancelDrag(w);
2732 if (!TextDMoveDown(((TextWidget)w)->text.textD))
2733 ringIfNecessary(silent, w);
2734 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2735 checkAutoShowInsertPos(w);
2736 callCursorMovementCBs(w, event);
2739 static void processShiftDownAP(Widget w, XEvent *event, String *args,
2740 Cardinal *nArgs)
2742 int insertPos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
2743 int silent = hasKey("nobell", args, nArgs);
2745 cancelDrag(w);
2746 if (!TextDMoveDown(((TextWidget)w)->text.textD))
2747 ringIfNecessary(silent, w);
2748 keyMoveExtendSelection(w, event, insertPos, hasKey("rect", args, nArgs));
2749 checkAutoShowInsertPos(w);
2750 callCursorMovementCBs(w, event);
2753 static void beginningOfLineAP(Widget w, XEvent *event, String *args,
2754 Cardinal *nArgs)
2756 textDisp *textD = ((TextWidget)w)->text.textD;
2757 int insertPos = TextDGetInsertPosition(textD);
2759 cancelDrag(w);
2760 TextDSetInsertPosition(textD, TextDStartOfLine(textD, insertPos));
2761 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2762 checkAutoShowInsertPos(w);
2763 callCursorMovementCBs(w, event);
2764 textD->cursorPreferredCol = 0;
2767 static void endOfLineAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2769 textDisp *textD = ((TextWidget)w)->text.textD;
2770 int insertPos = TextDGetInsertPosition(textD);
2772 cancelDrag(w);
2773 TextDSetInsertPosition(textD, TextDEndOfLine(textD, insertPos, False));
2774 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2775 checkAutoShowInsertPos(w);
2776 callCursorMovementCBs(w, event);
2777 textD->cursorPreferredCol = -1;
2780 static void beginningOfFileAP(Widget w, XEvent *event, String *args,
2781 Cardinal *nArgs)
2783 int insertPos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
2784 textDisp *textD = ((TextWidget)w)->text.textD;
2786 cancelDrag(w);
2787 if (hasKey("scrollbar", args, nArgs)) {
2788 if (textD->topLineNum != 1) {
2789 TextDSetScroll(textD, 1, textD->horizOffset);
2792 else {
2793 TextDSetInsertPosition(((TextWidget)w)->text.textD, 0);
2794 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2795 checkAutoShowInsertPos(w);
2796 callCursorMovementCBs(w, event);
2800 static void endOfFileAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2802 textDisp *textD = ((TextWidget)w)->text.textD;
2803 int insertPos = TextDGetInsertPosition(textD);
2804 int lastTopLine;
2806 cancelDrag(w);
2807 if (hasKey("scrollbar", args, nArgs)) {
2808 lastTopLine = max(1, textD->nBufferLines - (textD->nVisibleLines - 2));
2809 if (lastTopLine != textD->topLineNum) {
2810 TextDSetScroll(textD, lastTopLine, textD->horizOffset);
2813 else {
2814 TextDSetInsertPosition(textD, textD->buffer->length);
2815 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2816 checkAutoShowInsertPos(w);
2817 callCursorMovementCBs(w, event);
2821 static void nextPageAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2823 textDisp *textD = ((TextWidget)w)->text.textD;
2824 textBuffer *buf = textD->buffer;
2825 /*int lastTopLine; = textD->nBufferLines - (textD->nVisibleLines - 2);*/
2826 int lastTopLine = max(1, textD->nBufferLines - (textD->nVisibleLines - 2));
2827 int insertPos = TextDGetInsertPosition(textD);
2828 int column = 0, visLineNum, lineStartPos;
2829 int pos, targetLine;
2830 int pageForwardCount = max(1, textD->nVisibleLines - 1);
2831 int maintainColumn = 0;
2832 int silent = hasKey("nobell", args, nArgs);
2834 maintainColumn = hasKey("column", args, nArgs);
2835 cancelDrag(w);
2836 if (hasKey("scrollbar", args, nArgs)) { /* scrollbar only */
2837 targetLine = min(textD->topLineNum + pageForwardCount, lastTopLine);
2839 if (targetLine == textD->topLineNum) {
2840 ringIfNecessary(silent, w);
2841 return;
2843 TextDSetScroll(textD, targetLine, textD->horizOffset);
2845 else if (hasKey("stutter", args, nArgs)) { /* Mac style */
2846 /* move to bottom line of visible area */
2847 /* if already there, page down maintaining preferrred column */
2848 targetLine = max(min(textD->nVisibleLines - 1, textD->nBufferLines), 0);
2849 column = TextDPreferredColumn(textD, &visLineNum, &lineStartPos);
2850 if (lineStartPos == textD->lineStarts[targetLine]) {
2851 if (insertPos >= buf->length || textD->topLineNum == lastTopLine) {
2852 ringIfNecessary(silent, w);
2853 return;
2855 targetLine = min(textD->topLineNum + pageForwardCount, lastTopLine);
2856 pos = TextDCountForwardNLines(textD, insertPos, pageForwardCount, False);
2857 if (maintainColumn) {
2858 pos = TextDPosOfPreferredCol(textD, column, pos);
2860 TextDSetInsertPosition(textD, pos);
2861 TextDSetScroll(textD, targetLine, textD->horizOffset);
2863 else {
2864 pos = textD->lineStarts[targetLine];
2865 while (targetLine > 0 && pos == -1) {
2866 --targetLine;
2867 pos = textD->lineStarts[targetLine];
2869 if (lineStartPos == pos) {
2870 ringIfNecessary(silent, w);
2871 return;
2873 if (maintainColumn) {
2874 pos = TextDPosOfPreferredCol(textD, column, pos);
2876 TextDSetInsertPosition(textD, pos);
2878 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2879 checkAutoShowInsertPos(w);
2880 callCursorMovementCBs(w, event);
2881 if (maintainColumn) {
2882 textD->cursorPreferredCol = column;
2884 else {
2885 textD->cursorPreferredCol = -1;
2888 else { /* "standard" */
2889 if (insertPos >= buf->length && textD->topLineNum == lastTopLine) {
2890 ringIfNecessary(silent, w);
2891 return;
2893 if (maintainColumn) {
2894 column = TextDPreferredColumn(textD, &visLineNum, &lineStartPos);
2896 targetLine = textD->topLineNum + textD->nVisibleLines - 1;
2897 if (targetLine < 1) targetLine = 1;
2898 if (targetLine > lastTopLine) targetLine = lastTopLine;
2899 pos = TextDCountForwardNLines(textD, insertPos, textD->nVisibleLines-1, False);
2900 if (maintainColumn) {
2901 pos = TextDPosOfPreferredCol(textD, column, pos);
2903 TextDSetInsertPosition(textD, pos);
2904 TextDSetScroll(textD, targetLine, textD->horizOffset);
2905 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2906 checkAutoShowInsertPos(w);
2907 callCursorMovementCBs(w, event);
2908 if (maintainColumn) {
2909 textD->cursorPreferredCol = column;
2911 else {
2912 textD->cursorPreferredCol = -1;
2917 static void previousPageAP(Widget w, XEvent *event, String *args,
2918 Cardinal *nArgs)
2920 textDisp *textD = ((TextWidget)w)->text.textD;
2921 int insertPos = TextDGetInsertPosition(textD);
2922 int column = 0, visLineNum, lineStartPos;
2923 int pos, targetLine;
2924 int pageBackwardCount = max(1, textD->nVisibleLines - 1);
2925 int maintainColumn = 0;
2926 int silent = hasKey("nobell", args, nArgs);
2928 maintainColumn = hasKey("column", args, nArgs);
2929 cancelDrag(w);
2930 if (hasKey("scrollbar", args, nArgs)) { /* scrollbar only */
2931 targetLine = max(textD->topLineNum - pageBackwardCount, 1);
2933 if (targetLine == textD->topLineNum) {
2934 ringIfNecessary(silent, w);
2935 return;
2937 TextDSetScroll(textD, targetLine, textD->horizOffset);
2939 else if (hasKey("stutter", args, nArgs)) { /* Mac style */
2940 /* move to top line of visible area */
2941 /* if already there, page up maintaining preferrred column if required */
2942 targetLine = 0;
2943 column = TextDPreferredColumn(textD, &visLineNum, &lineStartPos);
2944 if (lineStartPos == textD->lineStarts[targetLine]) {
2945 if (textD->topLineNum == 1 && (maintainColumn || column == 0)) {
2946 ringIfNecessary(silent, w);
2947 return;
2949 targetLine = max(textD->topLineNum - pageBackwardCount, 1);
2950 pos = TextDCountBackwardNLines(textD, insertPos, pageBackwardCount);
2951 if (maintainColumn) {
2952 pos = TextDPosOfPreferredCol(textD, column, pos);
2954 TextDSetInsertPosition(textD, pos);
2955 TextDSetScroll(textD, targetLine, textD->horizOffset);
2957 else {
2958 pos = textD->lineStarts[targetLine];
2959 if (maintainColumn) {
2960 pos = TextDPosOfPreferredCol(textD, column, pos);
2962 TextDSetInsertPosition(textD, pos);
2964 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2965 checkAutoShowInsertPos(w);
2966 callCursorMovementCBs(w, event);
2967 if (maintainColumn) {
2968 textD->cursorPreferredCol = column;
2970 else {
2971 textD->cursorPreferredCol = -1;
2974 else { /* "standard" */
2975 if (insertPos <= 0 && textD->topLineNum == 1) {
2976 ringIfNecessary(silent, w);
2977 return;
2979 if (maintainColumn) {
2980 column = TextDPreferredColumn(textD, &visLineNum, &lineStartPos);
2982 targetLine = textD->topLineNum - (textD->nVisibleLines - 1);
2983 if (targetLine < 1) targetLine = 1;
2984 pos = TextDCountBackwardNLines(textD, insertPos, textD->nVisibleLines-1);
2985 if (maintainColumn) {
2986 pos = TextDPosOfPreferredCol(textD, column, pos);
2988 TextDSetInsertPosition(textD, pos);
2989 TextDSetScroll(textD, targetLine, textD->horizOffset);
2990 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2991 checkAutoShowInsertPos(w);
2992 callCursorMovementCBs(w, event);
2993 if (maintainColumn) {
2994 textD->cursorPreferredCol = column;
2996 else {
2997 textD->cursorPreferredCol = -1;
3002 static void pageLeftAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
3004 textDisp *textD = ((TextWidget)w)->text.textD;
3005 textBuffer *buf = textD->buffer;
3006 int insertPos = TextDGetInsertPosition(textD);
3007 int maxCharWidth = textD->fontStruct->max_bounds.width;
3008 int lineStartPos, indent, pos;
3009 int horizOffset;
3010 int silent = hasKey("nobell", args, nArgs);
3012 cancelDrag(w);
3013 if (hasKey("scrollbar", args, nArgs)) {
3014 if (textD->horizOffset == 0) {
3015 ringIfNecessary(silent, w);
3016 return;
3018 horizOffset = max(0, textD->horizOffset - textD->width);
3019 TextDSetScroll(textD, textD->topLineNum, horizOffset);
3021 else {
3022 lineStartPos = BufStartOfLine(buf, insertPos);
3023 if (insertPos == lineStartPos && textD->horizOffset == 0) {
3024 ringIfNecessary(silent, w);
3025 return;
3027 indent = BufCountDispChars(buf, lineStartPos, insertPos);
3028 pos = BufCountForwardDispChars(buf, lineStartPos,
3029 max(0, indent - textD->width / maxCharWidth));
3030 TextDSetInsertPosition(textD, pos);
3031 TextDSetScroll(textD, textD->topLineNum,
3032 max(0, textD->horizOffset - textD->width));
3033 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
3034 checkAutoShowInsertPos(w);
3035 callCursorMovementCBs(w, event);
3039 static void pageRightAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
3041 textDisp *textD = ((TextWidget)w)->text.textD;
3042 textBuffer *buf = textD->buffer;
3043 int insertPos = TextDGetInsertPosition(textD);
3044 int maxCharWidth = textD->fontStruct->max_bounds.width;
3045 int oldHorizOffset = textD->horizOffset;
3046 int lineStartPos, indent, pos;
3047 int horizOffset, sliderSize, sliderMax;
3048 int silent = hasKey("nobell", args, nArgs);
3050 cancelDrag(w);
3051 if (hasKey("scrollbar", args, nArgs)) {
3052 XtVaGetValues(textD->hScrollBar, XmNmaximum, &sliderMax,
3053 XmNsliderSize, &sliderSize, 0);
3054 horizOffset = min(textD->horizOffset + textD->width, sliderMax - sliderSize);
3055 if (textD->horizOffset == horizOffset) {
3056 ringIfNecessary(silent, w);
3057 return;
3059 TextDSetScroll(textD, textD->topLineNum, horizOffset);
3061 else {
3062 lineStartPos = BufStartOfLine(buf, insertPos);
3063 indent = BufCountDispChars(buf, lineStartPos, insertPos);
3064 pos = BufCountForwardDispChars(buf, lineStartPos,
3065 indent + textD->width / maxCharWidth);
3066 TextDSetInsertPosition(textD, pos);
3067 TextDSetScroll(textD, textD->topLineNum, textD->horizOffset + textD->width);
3068 if (textD->horizOffset == oldHorizOffset && insertPos == pos)
3069 ringIfNecessary(silent, w);
3070 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
3071 checkAutoShowInsertPos(w);
3072 callCursorMovementCBs(w, event);
3076 static void toggleOverstrikeAP(Widget w, XEvent *event, String *args,
3077 Cardinal *nArgs)
3079 TextWidget tw = (TextWidget)w;
3081 if (tw->text.overstrike) {
3082 tw->text.overstrike = False;
3083 TextDSetCursorStyle(tw->text.textD,
3084 tw->text.heavyCursor ? HEAVY_CURSOR : NORMAL_CURSOR);
3085 } else {
3086 tw->text.overstrike = True;
3087 if ( tw->text.textD->cursorStyle == NORMAL_CURSOR ||
3088 tw->text.textD->cursorStyle == HEAVY_CURSOR)
3089 TextDSetCursorStyle(tw->text.textD, BLOCK_CURSOR);
3093 static void scrollUpAP(Widget w, XEvent *event, String *args,
3094 Cardinal *nArgs)
3096 textDisp *textD = ((TextWidget)w)->text.textD;
3097 int topLineNum, horizOffset, nLines;
3099 if (*nArgs == 0 || sscanf(args[0], "%d", &nLines) != 1)
3100 return;
3101 TextDGetScroll(textD, &topLineNum, &horizOffset);
3102 TextDSetScroll(textD, topLineNum-nLines, horizOffset);
3105 static void scrollDownAP(Widget w, XEvent *event, String *args,
3106 Cardinal *nArgs)
3108 textDisp *textD = ((TextWidget)w)->text.textD;
3109 int topLineNum, horizOffset, nLines;
3111 if (*nArgs == 0 || sscanf(args[0], "%d", &nLines) != 1)
3112 return;
3113 TextDGetScroll(textD, &topLineNum, &horizOffset);
3114 TextDSetScroll(textD, topLineNum+nLines, horizOffset);
3117 static void scrollLeftAP(Widget w, XEvent *event, String *args,
3118 Cardinal *nArgs)
3120 textDisp *textD = ((TextWidget)w)->text.textD;
3121 int horizOffset, nPixels;
3122 int sliderMax, sliderSize;
3124 if (*nArgs == 0 || sscanf(args[0], "%d", &nPixels) != 1)
3125 return;
3126 XtVaGetValues(textD->hScrollBar, XmNmaximum, &sliderMax,
3127 XmNsliderSize, &sliderSize, 0);
3128 horizOffset = min(max(0, textD->horizOffset - nPixels), sliderMax - sliderSize);
3129 if (textD->horizOffset != horizOffset) {
3130 TextDSetScroll(textD, textD->topLineNum, horizOffset);
3134 static void scrollRightAP(Widget w, XEvent *event, String *args,
3135 Cardinal *nArgs)
3137 textDisp *textD = ((TextWidget)w)->text.textD;
3138 int horizOffset, nPixels;
3139 int sliderMax, sliderSize;
3141 if (*nArgs == 0 || sscanf(args[0], "%d", &nPixels) != 1)
3142 return;
3143 XtVaGetValues(textD->hScrollBar, XmNmaximum, &sliderMax,
3144 XmNsliderSize, &sliderSize, 0);
3145 horizOffset = min(max(0, textD->horizOffset + nPixels), sliderMax - sliderSize);
3146 if (textD->horizOffset != horizOffset) {
3147 TextDSetScroll(textD, textD->topLineNum, horizOffset);
3151 static void scrollToLineAP(Widget w, XEvent *event, String *args,
3152 Cardinal *nArgs)
3154 textDisp *textD = ((TextWidget)w)->text.textD;
3155 int topLineNum, horizOffset, lineNum;
3157 if (*nArgs == 0 || sscanf(args[0], "%d", &lineNum) != 1)
3158 return;
3159 TextDGetScroll(textD, &topLineNum, &horizOffset);
3160 TextDSetScroll(textD, lineNum, horizOffset);
3163 static void selectAllAP(Widget w, XEvent *event, String *args,
3164 Cardinal *nArgs)
3166 textBuffer *buf = ((TextWidget)w)->text.textD->buffer;
3168 cancelDrag(w);
3169 BufSelect(buf, 0, buf->length);
3172 static void deselectAllAP(Widget w, XEvent *event, String *args,
3173 Cardinal *nArgs)
3175 cancelDrag(w);
3176 BufUnselect(((TextWidget)w)->text.textD->buffer);
3179 static void focusInAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
3181 TextWidget tw = (TextWidget)w;
3182 textDisp *textD = tw->text.textD;
3184 /* I don't entirely understand the traversal mechanism in Motif widgets,
3185 particularly, what leads to this widget getting a focus-in event when
3186 it does not actually have the input focus. The temporary solution is
3187 to do the comparison below, and not show the cursor when Motif says
3188 we don't have focus, but keep looking for the real answer */
3189 #if XmVersion >= 1002
3190 if (w != XmGetFocusWidget(w))
3191 return;
3192 #endif
3194 /* If the timer is not already started, start it */
3195 if (tw->text.cursorBlinkRate != 0 && tw->text.cursorBlinkProcID == 0) {
3196 tw->text.cursorBlinkProcID = XtAppAddTimeOut(
3197 XtWidgetToApplicationContext((Widget)w),
3198 tw->text.cursorBlinkRate, cursorBlinkTimerProc, w);
3201 /* Change the cursor to active style */
3202 if (((TextWidget)w)->text.overstrike)
3203 TextDSetCursorStyle(textD, BLOCK_CURSOR);
3204 else
3205 TextDSetCursorStyle(textD, ((TextWidget)w)->text.heavyCursor ?
3206 HEAVY_CURSOR : NORMAL_CURSOR);
3207 TextDUnblankCursor(textD);
3209 #ifndef NO_XMIM
3210 /* Notify Motif input manager that widget has focus */
3211 XmImVaSetFocusValues(w,NULL);
3212 #endif
3214 /* Call any registered focus-in callbacks */
3215 XtCallCallbacks((Widget)w, textNfocusCallback, (XtPointer)event);
3218 static void focusOutAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
3220 textDisp *textD = ((TextWidget)w)->text.textD;
3222 /* Remove the cursor blinking timer procedure */
3223 if (((TextWidget)w)->text.cursorBlinkProcID != 0)
3224 XtRemoveTimeOut(((TextWidget)w)->text.cursorBlinkProcID);
3225 ((TextWidget)w)->text.cursorBlinkProcID = 0;
3227 /* Leave a dim or destination cursor */
3228 TextDSetCursorStyle(textD, ((TextWidget)w)->text.motifDestOwner ?
3229 CARET_CURSOR : DIM_CURSOR);
3230 TextDUnblankCursor(textD);
3232 /* If there's a calltip displayed, kill it. */
3233 TextDKillCalltip(textD, 0);
3235 /* Call any registered focus-out callbacks */
3236 XtCallCallbacks((Widget)w, textNlosingFocusCallback, (XtPointer)event);
3240 ** For actions involving cursor movement, "extend" keyword means incorporate
3241 ** the new cursor position in the selection, and lack of an "extend" keyword
3242 ** means cancel the existing selection
3244 static void checkMoveSelectionChange(Widget w, XEvent *event, int startPos,
3245 String *args, Cardinal *nArgs)
3247 if (hasKey("extend", args, nArgs))
3248 keyMoveExtendSelection(w, event, startPos, hasKey("rect", args, nArgs));
3249 else
3250 BufUnselect((((TextWidget)w)->text.textD)->buffer);
3254 ** If a selection change was requested via a keyboard command for moving
3255 ** the insertion cursor (usually with the "extend" keyword), adjust the
3256 ** selection to include the new cursor position, or begin a new selection
3257 ** between startPos and the new cursor position with anchor at startPos.
3259 static void keyMoveExtendSelection(Widget w, XEvent *event, int origPos,
3260 int rectangular)
3262 XKeyEvent *e = &event->xkey;
3263 textDisp *textD = ((TextWidget)w)->text.textD;
3264 textBuffer *buf = textD->buffer;
3265 selection *sel = &buf->primary;
3266 int newPos = TextDGetInsertPosition(textD);
3267 int startPos, endPos, col, startCol, endCol, newCol, origCol;
3268 int anchor, rectAnchor, anchorLineStart;
3270 /* Moving the cursor does not take the Motif destination, but as soon as
3271 the user selects something, grab it (I'm not sure if this distinction
3272 actually makes sense, but it's what Motif was doing, back when their
3273 secondary selections actually worked correctly) */
3274 TakeMotifDestination(w, e->time);
3276 /* Extend the selection based on original and new selection type */
3277 if (sel->selected && sel->rectangular && rectangular) { /* rect -> rect */
3278 col = BufCountDispChars(buf, BufStartOfLine(buf, newPos), newPos);
3279 rectAnchor = col < (sel->rectEnd + sel->rectStart) / 2 ?
3280 sel->rectEnd : sel->rectStart;
3281 anchorLineStart = BufStartOfLine(buf, newPos <
3282 (sel->end + sel->start) / 2 ? sel->end : sel->start);
3283 anchor = BufCountForwardDispChars(buf, anchorLineStart, rectAnchor);
3284 startCol = min(rectAnchor, col);
3285 endCol = max(rectAnchor, col);
3286 startPos = BufStartOfLine(buf, min(anchor, newPos));
3287 endPos = BufEndOfLine(buf, max(anchor, newPos));
3288 BufRectSelect(buf, startPos, endPos, startCol, endCol);
3289 } else if (sel->selected && sel->rectangular) { /* rect -> plain */
3290 startPos = BufCountForwardDispChars(buf,
3291 BufStartOfLine(buf, sel->start), sel->rectStart);
3292 endPos = BufCountForwardDispChars(buf,
3293 BufStartOfLine(buf, sel->end), sel->rectEnd);
3294 if (abs(origPos - startPos) < abs(origPos - endPos))
3295 anchor = endPos;
3296 else
3297 anchor = startPos;
3298 BufSelect(buf, anchor, newPos);
3299 } else if (sel->selected && rectangular) { /* plain -> rect */
3300 startCol = BufCountDispChars(buf,
3301 BufStartOfLine(buf, sel->start), sel->start);
3302 endCol = BufCountDispChars(buf,
3303 BufStartOfLine(buf, sel->end), sel->end);
3304 newCol = BufCountDispChars(buf, BufStartOfLine(buf, newPos), newPos);
3305 startPos = BufStartOfLine(buf, min(sel->start, newPos));
3306 endPos = BufEndOfLine(buf, max(sel->end, newPos));
3307 BufRectSelect(buf, startPos, endPos, startCol, endCol);
3308 } else if (sel->selected) { /* plain -> plain */
3309 if (abs(origPos - sel->start) < abs(origPos - sel->end))
3310 anchor = sel->end;
3311 else
3312 anchor = sel->start;
3313 BufSelect(buf, anchor, newPos);
3314 } else if (rectangular) { /* no sel -> rect */
3315 origCol = BufCountDispChars(buf, BufStartOfLine(buf, origPos), origPos);
3316 newCol = BufCountDispChars(buf, BufStartOfLine(buf, newPos), newPos);
3317 startCol = min(newCol, origCol);
3318 endCol = max(newCol, origCol);
3319 startPos = BufStartOfLine(buf, min(origPos, newPos));
3320 endPos = BufEndOfLine(buf, max(origPos, newPos));
3321 BufRectSelect(buf, startPos, endPos, startCol, endCol);
3322 } else { /* no sel -> plain */
3323 anchor = origPos;
3324 BufSelect(buf, anchor, newPos);
3328 static void checkAutoShowInsertPos(Widget w)
3330 if (((TextWidget)w)->text.autoShowInsertPos)
3331 TextDMakeInsertPosVisible(((TextWidget)w)->text.textD);
3334 static int checkReadOnly(Widget w)
3336 if (((TextWidget)w)->text.readOnly) {
3337 XBell(XtDisplay(w), 0);
3338 return True;
3340 return False;
3344 ** Insert text "chars" at the cursor position, as if the text had been
3345 ** typed. Same as TextInsertAtCursor, but without the complicated auto-wrap
3346 ** scanning and re-formatting.
3348 static void simpleInsertAtCursor(Widget w, char *chars, XEvent *event,
3349 int allowPendingDelete)
3351 textDisp *textD = ((TextWidget)w)->text.textD;
3352 textBuffer *buf = textD->buffer;
3353 char *c;
3355 if (allowPendingDelete && pendingSelection(w)) {
3356 BufReplaceSelected(buf, chars);
3357 TextDSetInsertPosition(textD, buf->cursorPosHint);
3358 } else if (((TextWidget)w)->text.overstrike) {
3359 for (c=chars; *c!='\0' && *c!='\n'; c++);
3360 if (*c == '\n')
3361 TextDInsert(textD, chars);
3362 else
3363 TextDOverstrike(textD, chars);
3364 } else
3365 TextDInsert(textD, chars);
3366 checkAutoShowInsertPos(w);
3367 callCursorMovementCBs(w, event);
3371 ** If there's a selection, delete it and position the cursor where the
3372 ** selection was deleted. (Called by routines which do deletion to check
3373 ** first for and do possible selection delete)
3375 static int deletePendingSelection(Widget w, XEvent *event)
3377 textDisp *textD = ((TextWidget)w)->text.textD;
3378 textBuffer *buf = textD->buffer;
3380 if (((TextWidget)w)->text.textD->buffer->primary.selected) {
3381 BufRemoveSelected(buf);
3382 TextDSetInsertPosition(textD, buf->cursorPosHint);
3383 checkAutoShowInsertPos(w);
3384 callCursorMovementCBs(w, event);
3385 return True;
3386 } else
3387 return False;
3391 ** Return true if pending delete is on and there's a selection contiguous
3392 ** with the cursor ready to be deleted. These criteria are used to decide
3393 ** if typing a character or inserting something should delete the selection
3394 ** first.
3396 static int pendingSelection(Widget w)
3398 selection *sel = &((TextWidget)w)->text.textD->buffer->primary;
3399 int pos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
3401 return ((TextWidget)w)->text.pendingDelete && sel->selected &&
3402 pos >= sel->start && pos <= sel->end;
3406 ** Check if tab emulation is on and if there are emulated tabs before the
3407 ** cursor, and if so, delete an emulated tab as a unit. Also finishes up
3408 ** by calling checkAutoShowInsertPos and callCursorMovementCBs, so the
3409 ** calling action proc can just return (this is necessary to preserve
3410 ** emTabsBeforeCursor which is otherwise cleared by callCursorMovementCBs).
3412 static int deleteEmulatedTab(Widget w, XEvent *event)
3414 textDisp *textD = ((TextWidget)w)->text.textD;
3415 textBuffer *buf = ((TextWidget)w)->text.textD->buffer;
3416 int emTabDist = ((TextWidget)w)->text.emulateTabs;
3417 int emTabsBeforeCursor = ((TextWidget)w)->text.emTabsBeforeCursor;
3418 int startIndent, toIndent, insertPos, startPos, lineStart;
3419 int pos, indent, startPosIndent;
3420 char c, *spaceString;
3422 if (emTabDist <= 0 || emTabsBeforeCursor <= 0)
3423 return False;
3425 /* Find the position of the previous tab stop */
3426 insertPos = TextDGetInsertPosition(textD);
3427 lineStart = BufStartOfLine(buf, insertPos);
3428 startIndent = BufCountDispChars(buf, lineStart, insertPos);
3429 toIndent = (startIndent-1) - ((startIndent-1) % emTabDist);
3431 /* Find the position at which to begin deleting (stop at non-whitespace
3432 characters) */
3433 startPosIndent = indent = 0;
3434 startPos = lineStart;
3435 for (pos=lineStart; pos < insertPos; pos++) {
3436 c = BufGetCharacter(buf, pos);
3437 indent += BufCharWidth(c, indent, buf->tabDist, buf->nullSubsChar);
3438 if (indent > toIndent)
3439 break;
3440 startPosIndent = indent;
3441 startPos = pos + 1;
3444 /* Just to make sure, check that we're not deleting any non-white chars */
3445 for (pos=insertPos-1; pos>=startPos; pos--) {
3446 c = BufGetCharacter(buf, pos);
3447 if (c != ' ' && c != '\t') {
3448 startPos = pos + 1;
3449 break;
3453 /* Do the text replacement and reposition the cursor. If any spaces need
3454 to be inserted to make up for a deleted tab, do a BufReplace, otherwise,
3455 do a BufRemove. */
3456 if (startPosIndent < toIndent) {
3457 spaceString = XtMalloc(toIndent - startPosIndent + 1);
3458 memset(spaceString, ' ', toIndent-startPosIndent);
3459 spaceString[toIndent - startPosIndent] = '\0';
3460 BufReplace(buf, startPos, insertPos, spaceString);
3461 TextDSetInsertPosition(textD, startPos + toIndent - startPosIndent);
3462 XtFree(spaceString);
3463 } else {
3464 BufRemove(buf, startPos, insertPos);
3465 TextDSetInsertPosition(textD, startPos);
3468 /* The normal cursor movement stuff would usually be called by the action
3469 routine, but this wraps around it to restore emTabsBeforeCursor */
3470 checkAutoShowInsertPos(w);
3471 callCursorMovementCBs(w, event);
3473 /* Decrement and restore the marker for consecutive emulated tabs, which
3474 would otherwise have been zeroed by callCursorMovementCBs */
3475 ((TextWidget)w)->text.emTabsBeforeCursor = emTabsBeforeCursor - 1;
3476 return True;
3480 ** Select the word or whitespace adjacent to the cursor, and move the cursor
3481 ** to its end. pointerX is used as a tie-breaker, when the cursor is at the
3482 ** boundary between a word and some white-space. If the cursor is on the
3483 ** left, the word or space on the left is used. If it's on the right, that
3484 ** is used instead.
3486 static void selectWord(Widget w, int pointerX)
3488 TextWidget tw = (TextWidget)w;
3489 textBuffer *buf = tw->text.textD->buffer;
3490 int x, y, insertPos = TextDGetInsertPosition(tw->text.textD);
3492 TextPosToXY(w, insertPos, &x, &y);
3493 if (pointerX < x && insertPos > 0 && BufGetCharacter(buf, insertPos-1) != '\n')
3494 insertPos--;
3495 BufSelect(buf, startOfWord(tw, insertPos), endOfWord(tw, insertPos));
3498 static int startOfWord(TextWidget w, int pos)
3500 int startPos;
3501 textBuffer *buf = w->text.textD->buffer;
3502 char *delimiters=w->text.delimiters;
3503 char c = BufGetCharacter(buf, pos);
3505 if (c == ' ' || c== '\t') {
3506 if (!spanBackward(buf, pos, " \t", False, &startPos))
3507 return 0;
3508 } else if (strchr(delimiters, c)) {
3509 if (!spanBackward(buf, pos, delimiters, True, &startPos))
3510 return 0;
3511 } else {
3512 if (!BufSearchBackward(buf, pos, delimiters, &startPos))
3513 return 0;
3515 return min(pos, startPos+1);
3519 static int endOfWord(TextWidget w, int pos)
3521 int endPos;
3522 textBuffer *buf = w->text.textD->buffer;
3523 char *delimiters=w->text.delimiters;
3524 char c = BufGetCharacter(buf, pos);
3526 if (c == ' ' || c== '\t') {
3527 if (!spanForward(buf, pos, " \t", False, &endPos))
3528 return buf->length;
3529 } else if (strchr(delimiters, c)) {
3530 if (!spanForward(buf, pos, delimiters, True, &endPos))
3531 return buf->length;
3532 } else {
3533 if (!BufSearchForward(buf, pos, delimiters, &endPos))
3534 return buf->length;
3536 return endPos;
3540 ** Search forwards in buffer "buf" for the first character NOT in
3541 ** "searchChars", starting with the character "startPos", and returning the
3542 ** result in "foundPos" returns True if found, False if not. If ignoreSpace
3543 ** is set, then Space, Tab, and Newlines are ignored in searchChars.
3545 static int spanForward(textBuffer *buf, int startPos, char *searchChars,
3546 int ignoreSpace, int *foundPos)
3548 int pos;
3549 char *c;
3551 pos = startPos;
3552 while (pos < buf->length) {
3553 for (c=searchChars; *c!='\0'; c++)
3554 if(!(ignoreSpace && (*c==' ' || *c=='\t' || *c=='\n')))
3555 if (BufGetCharacter(buf, pos) == *c)
3556 break;
3557 if(*c == 0) {
3558 *foundPos = pos;
3559 return True;
3561 pos++;
3563 *foundPos = buf->length;
3564 return False;
3568 ** Search backwards in buffer "buf" for the first character NOT in
3569 ** "searchChars", starting with the character BEFORE "startPos", returning the
3570 ** result in "foundPos" returns True if found, False if not. If ignoreSpace is
3571 ** set, then Space, Tab, and Newlines are ignored in searchChars.
3573 static int spanBackward(textBuffer *buf, int startPos, char *searchChars, int
3574 ignoreSpace, int *foundPos)
3576 int pos;
3577 char *c;
3579 if (startPos == 0) {
3580 *foundPos = 0;
3581 return False;
3583 pos = startPos == 0 ? 0 : startPos - 1;
3584 while (pos >= 0) {
3585 for (c=searchChars; *c!='\0'; c++)
3586 if(!(ignoreSpace && (*c==' ' || *c=='\t' || *c=='\n')))
3587 if (BufGetCharacter(buf, pos) == *c)
3588 break;
3589 if(*c == 0) {
3590 *foundPos = pos;
3591 return True;
3593 pos--;
3595 *foundPos = 0;
3596 return False;
3600 ** Select the line containing the cursor, including the terminating newline,
3601 ** and move the cursor to its end.
3603 static void selectLine(Widget w)
3605 textDisp *textD = ((TextWidget)w)->text.textD;
3606 int insertPos = TextDGetInsertPosition(textD);
3607 int endPos, startPos;
3609 endPos = BufEndOfLine(textD->buffer, insertPos);
3610 startPos = BufStartOfLine(textD->buffer, insertPos);
3611 BufSelect(textD->buffer, startPos, min(endPos + 1, textD->buffer->length));
3612 TextDSetInsertPosition(textD, endPos);
3616 ** Given a new mouse pointer location, pass the position on to the
3617 ** autoscroll timer routine, and make sure the timer is on when it's
3618 ** needed and off when it's not.
3620 static void checkAutoScroll(TextWidget w, int x, int y)
3622 int inWindow;
3624 /* Is the pointer in or out of the window? */
3625 inWindow = x >= w->text.textD->left &&
3626 x < w->core.width - w->text.marginWidth &&
3627 y >= w->text.marginHeight &&
3628 y < w->core.height - w->text.marginHeight;
3630 /* If it's in the window, cancel the timer procedure */
3631 if (inWindow) {
3632 if (w->text.autoScrollProcID != 0)
3633 XtRemoveTimeOut(w->text.autoScrollProcID);;
3634 w->text.autoScrollProcID = 0;
3635 return;
3638 /* If the timer is not already started, start it */
3639 if (w->text.autoScrollProcID == 0) {
3640 w->text.autoScrollProcID = XtAppAddTimeOut(
3641 XtWidgetToApplicationContext((Widget)w),
3642 0, autoScrollTimerProc, w);
3645 /* Pass on the newest mouse location to the autoscroll routine */
3646 w->text.mouseX = x;
3647 w->text.mouseY = y;
3651 ** Reset drag state and cancel the auto-scroll timer
3653 static void endDrag(Widget w)
3655 if (((TextWidget)w)->text.autoScrollProcID != 0)
3656 XtRemoveTimeOut(((TextWidget)w)->text.autoScrollProcID);
3657 ((TextWidget)w)->text.autoScrollProcID = 0;
3658 if (((TextWidget)w)->text.dragState == MOUSE_PAN)
3659 XUngrabPointer(XtDisplay(w), CurrentTime);
3660 ((TextWidget)w)->text.dragState = NOT_CLICKED;
3664 ** Cancel any drag operation that might be in progress. Should be included
3665 ** in nearly every key event to cleanly end any dragging before edits are made
3666 ** which might change the insert position or the content of the buffer during
3667 ** a drag operation)
3669 static void cancelDrag(Widget w)
3671 int dragState = ((TextWidget)w)->text.dragState;
3673 if (((TextWidget)w)->text.autoScrollProcID != 0)
3674 XtRemoveTimeOut(((TextWidget)w)->text.autoScrollProcID);
3675 if (dragState == SECONDARY_DRAG || dragState == SECONDARY_RECT_DRAG)
3676 BufSecondaryUnselect(((TextWidget)w)->text.textD->buffer);
3677 if (dragState == PRIMARY_BLOCK_DRAG)
3678 CancelBlockDrag((TextWidget)w);
3679 if (dragState == MOUSE_PAN)
3680 XUngrabPointer(XtDisplay(w), CurrentTime);
3681 if (dragState != NOT_CLICKED)
3682 ((TextWidget)w)->text.dragState = DRAG_CANCELED;
3686 ** Do operations triggered by cursor movement: Call cursor movement callback
3687 ** procedure(s), and cancel marker indicating that the cursor is after one or
3688 ** more just-entered emulated tabs (spaces to be deleted as a unit).
3690 static void callCursorMovementCBs(Widget w, XEvent *event)
3692 ((TextWidget)w)->text.emTabsBeforeCursor = 0;
3693 XtCallCallbacks((Widget)w, textNcursorMovementCallback, (XtPointer)event);
3697 ** Adjust the selection as the mouse is dragged to position: (x, y).
3699 static void adjustSelection(TextWidget tw, int x, int y)
3701 textDisp *textD = tw->text.textD;
3702 textBuffer *buf = textD->buffer;
3703 int row, col, startCol, endCol, startPos, endPos;
3704 int newPos = TextDXYToPosition(textD, x, y);
3706 /* Adjust the selection */
3707 if (tw->text.dragState == PRIMARY_RECT_DRAG) {
3708 TextDXYToUnconstrainedPosition(textD, x, y, &row, &col);
3709 col = TextDOffsetWrappedColumn(textD, row, col);
3710 startCol = min(tw->text.rectAnchor, col);
3711 endCol = max(tw->text.rectAnchor, col);
3712 startPos = BufStartOfLine(buf, min(tw->text.anchor, newPos));
3713 endPos = BufEndOfLine(buf, max(tw->text.anchor, newPos));
3714 BufRectSelect(buf, startPos, endPos, startCol, endCol);
3715 } else if (tw->text.multiClickState == ONE_CLICK) {
3716 startPos = startOfWord(tw, min(tw->text.anchor, newPos));
3717 endPos = endOfWord(tw, max(tw->text.anchor, newPos));
3718 BufSelect(buf, startPos, endPos);
3719 newPos = newPos < tw->text.anchor ? startPos : endPos;
3720 } else if (tw->text.multiClickState == TWO_CLICKS) {
3721 startPos = BufStartOfLine(buf, min(tw->text.anchor, newPos));
3722 endPos = BufEndOfLine(buf, max(tw->text.anchor, newPos));
3723 BufSelect(buf, startPos, min(endPos+1, buf->length));
3724 newPos = newPos < tw->text.anchor ? startPos : endPos;
3725 } else
3726 BufSelect(buf, tw->text.anchor, newPos);
3728 /* Move the cursor */
3729 TextDSetInsertPosition(textD, newPos);
3730 callCursorMovementCBs((Widget)tw, NULL);
3733 static void adjustSecondarySelection(TextWidget tw, int x, int y)
3735 textDisp *textD = tw->text.textD;
3736 textBuffer *buf = textD->buffer;
3737 int row, col, startCol, endCol, startPos, endPos;
3738 int newPos = TextDXYToPosition(textD, x, y);
3740 if (tw->text.dragState == SECONDARY_RECT_DRAG) {
3741 TextDXYToUnconstrainedPosition(textD, x, y, &row, &col);
3742 col = TextDOffsetWrappedColumn(textD, row, col);
3743 startCol = min(tw->text.rectAnchor, col);
3744 endCol = max(tw->text.rectAnchor, col);
3745 startPos = BufStartOfLine(buf, min(tw->text.anchor, newPos));
3746 endPos = BufEndOfLine(buf, max(tw->text.anchor, newPos));
3747 BufSecRectSelect(buf, startPos, endPos, startCol, endCol);
3748 } else
3749 BufSecondarySelect(textD->buffer, tw->text.anchor, newPos);
3753 ** Wrap multi-line text in argument "text" to be inserted at the end of the
3754 ** text on line "startLine" and return the result. If "breakBefore" is
3755 ** non-NULL, allow wrapping to extend back into "startLine", in which case
3756 ** the returned text will include the wrapped part of "startLine", and
3757 ** "breakBefore" will return the number of characters at the end of
3758 ** "startLine" that were absorbed into the returned string. "breakBefore"
3759 ** will return zero if no characters were absorbed into the returned string.
3760 ** The buffer offset of text in the widget's text buffer is needed so that
3761 ** smart indent (which can be triggered by wrapping) can search back farther
3762 ** in the buffer than just the text in startLine.
3764 static char *wrapText(TextWidget tw, char *startLine, char *text, int bufOffset,
3765 int wrapMargin, int *breakBefore)
3767 textBuffer *wrapBuf, *buf = tw->text.textD->buffer;
3768 int startLineLen = strlen(startLine);
3769 int colNum, pos, lineStartPos, limitPos, breakAt, charsAdded;
3770 int firstBreak = -1, tabDist = buf->tabDist;
3771 char c, *wrappedText;
3773 /* Create a temporary text buffer and load it with the strings */
3774 wrapBuf = BufCreate();
3775 BufInsert(wrapBuf, 0, startLine);
3776 BufInsert(wrapBuf, wrapBuf->length, text);
3778 /* Scan the buffer for long lines and apply wrapLine when wrapMargin is
3779 exceeded. limitPos enforces no breaks in the "startLine" part of the
3780 string (if requested), and prevents re-scanning of long unbreakable
3781 lines for each character beyond the margin */
3782 colNum = 0;
3783 pos = 0;
3784 lineStartPos = 0;
3785 limitPos = breakBefore == NULL ? startLineLen : 0;
3786 while (pos < wrapBuf->length) {
3787 c = BufGetCharacter(wrapBuf, pos);
3788 if (c == '\n') {
3789 lineStartPos = limitPos = pos + 1;
3790 colNum = 0;
3791 } else {
3792 colNum += BufCharWidth(c, colNum, tabDist, buf->nullSubsChar);
3793 if (colNum > wrapMargin) {
3794 if (!wrapLine(tw, wrapBuf, bufOffset, lineStartPos, pos,
3795 limitPos, &breakAt, &charsAdded)) {
3796 limitPos = max(pos, limitPos);
3797 } else {
3798 lineStartPos = limitPos = breakAt+1;
3799 pos += charsAdded;
3800 colNum = BufCountDispChars(wrapBuf, lineStartPos, pos+1);
3801 if (firstBreak == -1)
3802 firstBreak = breakAt;
3806 pos++;
3809 /* Return the wrapped text, possibly including part of startLine */
3810 if (breakBefore == NULL)
3811 wrappedText = BufGetRange(wrapBuf, startLineLen, wrapBuf->length);
3812 else {
3813 *breakBefore = firstBreak != -1 && firstBreak < startLineLen ?
3814 startLineLen - firstBreak : 0;
3815 wrappedText = BufGetRange(wrapBuf, startLineLen - *breakBefore,
3816 wrapBuf->length);
3818 BufFree(wrapBuf);
3819 return wrappedText;
3823 ** Wraps the end of a line beginning at lineStartPos and ending at lineEndPos
3824 ** in "buf", at the last white-space on the line >= limitPos. (The implicit
3825 ** assumption is that just the last character of the line exceeds the wrap
3826 ** margin, and anywhere on the line we can wrap is correct). Returns False if
3827 ** unable to wrap the line. "breakAt", returns the character position at
3828 ** which the line was broken,
3830 ** Auto-wrapping can also trigger auto-indent. The additional parameter
3831 ** bufOffset is needed when auto-indent is set to smart indent and the smart
3832 ** indent routines need to scan far back in the buffer. "charsAdded" returns
3833 ** the number of characters added to acheive the auto-indent. wrapMargin is
3834 ** used to decide whether auto-indent should be skipped because the indent
3835 ** string itself would exceed the wrap margin.
3837 static int wrapLine(TextWidget tw, textBuffer *buf, int bufOffset,
3838 int lineStartPos, int lineEndPos, int limitPos, int *breakAt,
3839 int *charsAdded)
3841 int p, length, column;
3842 char c, *indentStr;
3844 /* Scan backward for whitespace or BOL. If BOL, return False, no
3845 whitespace in line at which to wrap */
3846 for (p=lineEndPos; ; p--) {
3847 if (p < lineStartPos || p < limitPos)
3848 return False;
3849 c = BufGetCharacter(buf, p);
3850 if (c == '\t' || c == ' ')
3851 break;
3854 /* Create an auto-indent string to insert to do wrap. If the auto
3855 indent string reaches the wrap position, slice the auto-indent
3856 back off and return to the left margin */
3857 if (tw->text.autoIndent || tw->text.smartIndent) {
3858 indentStr = createIndentString(tw, buf, bufOffset, lineStartPos,
3859 lineEndPos, &length, &column);
3860 if (column >= p-lineStartPos)
3861 indentStr[1] = '\0';
3862 } else {
3863 indentStr = "\n";
3864 length = 1;
3867 /* Replace the whitespace character with the auto-indent string
3868 and return the stats */
3869 BufReplace(buf, p, p+1, indentStr);
3870 if (tw->text.autoIndent || tw->text.smartIndent)
3871 XtFree(indentStr);
3872 *breakAt = p;
3873 *charsAdded = length-1;
3874 return True;
3878 ** Create and return an auto-indent string to add a newline at lineEndPos to a
3879 ** line starting at lineStartPos in buf. "buf" may or may not be the real
3880 ** text buffer for the widget. If it is not the widget's text buffer it's
3881 ** offset position from the real buffer must be specified in "bufOffset" to
3882 ** allow the smart-indent routines to scan back as far as necessary. The
3883 ** string length is returned in "length" (or "length" can be passed as NULL,
3884 ** and the indent column is returned in "column" (if non NULL).
3886 static char *createIndentString(TextWidget tw, textBuffer *buf, int bufOffset,
3887 int lineStartPos, int lineEndPos, int *length, int *column)
3889 textDisp *textD = tw->text.textD;
3890 int pos, indent = -1, tabDist = textD->buffer->tabDist;
3891 int i, useTabs = textD->buffer->useTabs;
3892 char *indentPtr, *indentStr, c;
3893 smartIndentCBStruct smartIndent;
3895 /* If smart indent is on, call the smart indent callback. It is not
3896 called when multi-line changes are being made (lineStartPos != 0),
3897 because smart indent needs to search back an indeterminate distance
3898 through the buffer, and reconciling that with wrapping changes made,
3899 but not yet committed in the buffer, would make programming smart
3900 indent more difficult for users and make everything more complicated */
3901 if (tw->text.smartIndent && (lineStartPos == 0 || buf == textD->buffer)) {
3902 smartIndent.reason = NEWLINE_INDENT_NEEDED;
3903 smartIndent.pos = lineEndPos + bufOffset;
3904 smartIndent.indentRequest = 0;
3905 smartIndent.charsTyped = NULL;
3906 XtCallCallbacks((Widget)tw, textNsmartIndentCallback,
3907 (XtPointer)&smartIndent);
3908 indent = smartIndent.indentRequest;
3911 /* If smart indent wasn't used, measure the indent distance of the line */
3912 if (indent == -1) {
3913 indent = 0;
3914 for (pos=lineStartPos; pos<lineEndPos; pos++) {
3915 c = BufGetCharacter(buf, pos);
3916 if (c != ' ' && c != '\t')
3917 break;
3918 if (c == '\t')
3919 indent += tabDist - (indent % tabDist);
3920 else
3921 indent++;
3925 /* Allocate and create a string of tabs and spaces to achieve the indent */
3926 indentPtr = indentStr = XtMalloc(indent + 2);
3927 *indentPtr++ = '\n';
3928 if (useTabs) {
3929 for (i=0; i < indent / tabDist; i++)
3930 *indentPtr++ = '\t';
3931 for (i=0; i < indent % tabDist; i++)
3932 *indentPtr++ = ' ';
3933 } else {
3934 for (i=0; i < indent; i++)
3935 *indentPtr++ = ' ';
3937 *indentPtr = '\0';
3939 /* Return any requested stats */
3940 if (length != NULL)
3941 *length = indentPtr - indentStr;
3942 if (column != NULL)
3943 *column = indent;
3945 return indentStr;
3949 ** Xt timer procedure for autoscrolling
3951 static void autoScrollTimerProc(XtPointer clientData, XtIntervalId *id)
3953 TextWidget w = (TextWidget)clientData;
3954 textDisp *textD = w->text.textD;
3955 int topLineNum, horizOffset, newPos, cursorX, y;
3956 int fontWidth = textD->fontStruct->max_bounds.width;
3957 int fontHeight = textD->fontStruct->ascent + textD->fontStruct->descent;
3959 /* For vertical autoscrolling just dragging the mouse outside of the top
3960 or bottom of the window is sufficient, for horizontal (non-rectangular)
3961 scrolling, see if the position where the CURSOR would go is outside */
3962 newPos = TextDXYToPosition(textD, w->text.mouseX, w->text.mouseY);
3963 if (w->text.dragState == PRIMARY_RECT_DRAG)
3964 cursorX = w->text.mouseX;
3965 else if (!TextDPositionToXY(textD, newPos, &cursorX, &y))
3966 cursorX = w->text.mouseX;
3968 /* Scroll away from the pointer, 1 character (horizontal), or 1 character
3969 for each fontHeight distance from the mouse to the text (vertical) */
3970 TextDGetScroll(textD, &topLineNum, &horizOffset);
3971 if (cursorX >= (int)w->core.width - w->text.marginWidth)
3972 horizOffset += fontWidth;
3973 else if (w->text.mouseX < textD->left)
3974 horizOffset -= fontWidth;
3975 if (w->text.mouseY >= (int)w->core.height - w->text.marginHeight)
3976 topLineNum += 1 + ((w->text.mouseY - (int)w->core.height -
3977 w->text.marginHeight) / fontHeight) + 1;
3978 else if (w->text.mouseY < w->text.marginHeight)
3979 topLineNum -= 1 + ((w->text.marginHeight-w->text.mouseY) / fontHeight);
3980 TextDSetScroll(textD, topLineNum, horizOffset);
3982 /* Continue the drag operation in progress. If none is in progress
3983 (safety check) don't continue to re-establish the timer proc */
3984 if (w->text.dragState == PRIMARY_DRAG) {
3985 adjustSelection(w, w->text.mouseX, w->text.mouseY);
3986 } else if (w->text.dragState == PRIMARY_RECT_DRAG) {
3987 adjustSelection(w, w->text.mouseX, w->text.mouseY);
3988 } else if (w->text.dragState == SECONDARY_DRAG) {
3989 adjustSecondarySelection(w, w->text.mouseX, w->text.mouseY);
3990 } else if (w->text.dragState == SECONDARY_RECT_DRAG) {
3991 adjustSecondarySelection(w, w->text.mouseX, w->text.mouseY);
3992 } else if (w->text.dragState == PRIMARY_BLOCK_DRAG) {
3993 BlockDragSelection(w, w->text.mouseX, w->text.mouseY, USE_LAST);
3994 } else {
3995 w->text.autoScrollProcID = 0;
3996 return;
3999 /* re-establish the timer proc (this routine) to continue processing */
4000 w->text.autoScrollProcID = XtAppAddTimeOut(
4001 XtWidgetToApplicationContext((Widget)w),
4002 w->text.mouseY >= w->text.marginHeight &&
4003 w->text.mouseY < w->core.height - w->text.marginHeight ?
4004 (VERTICAL_SCROLL_DELAY*fontWidth) / fontHeight :
4005 VERTICAL_SCROLL_DELAY, autoScrollTimerProc, w);
4009 ** Xt timer procedure for cursor blinking
4011 static void cursorBlinkTimerProc(XtPointer clientData, XtIntervalId *id)
4013 TextWidget w = (TextWidget)clientData;
4014 textDisp *textD = w->text.textD;
4016 /* Blink the cursor */
4017 if (textD->cursorOn)
4018 TextDBlankCursor(textD);
4019 else
4020 TextDUnblankCursor(textD);
4022 /* re-establish the timer proc (this routine) to continue processing */
4023 w->text.cursorBlinkProcID = XtAppAddTimeOut(
4024 XtWidgetToApplicationContext((Widget)w),
4025 w->text.cursorBlinkRate, cursorBlinkTimerProc, w);
4029 ** look at an action procedure's arguments to see if argument "key" has been
4030 ** specified in the argument list
4032 static int hasKey(const char *key, const String *args, const Cardinal *nArgs)
4034 int i;
4036 for (i=0; i<(int)*nArgs; i++)
4037 if (!strCaseCmp(args[i], key))
4038 return True;
4039 return False;
4042 static int max(int i1, int i2)
4044 return i1 >= i2 ? i1 : i2;
4047 static int min(int i1, int i2)
4049 return i1 <= i2 ? i1 : i2;
4052 const char *GetDefaultTranslations(void)
4054 return defaultTranslations;
4057 ** strCaseCmp compares its arguments and returns 0 if the two strings
4058 ** are equal IGNORING case differences. Otherwise returns 1.
4061 static int strCaseCmp(const char *str1, const char *str2)
4063 unsigned const char *c1 = (unsigned const char*) str1;
4064 unsigned const char *c2 = (unsigned const char*) str2;
4066 for (; *c1!='\0' && *c2!='\0'; c1++, c2++)
4067 if (toupper(*c1) != toupper(*c2))
4068 return 1;
4069 if (*c1 == *c2) {
4070 return(0);
4072 else {
4073 return(1);
4077 static void ringIfNecessary(Boolean silent, Widget w)
4079 if (!silent)
4080 XBell(XtDisplay(w), 0);