From Ivan Skytte Jørgensen: remove duplicate declarations
[nedit.git] / source / text.c
blobc7e3370eb629577e86b80440a61425efdf40fa88
1 static const char CVSID[] = "$Id: text.c,v 1.55 2007/10/02 15:47:08 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. In addition, you may distribute version of this program linked to *
12 * Motif or Open Motif. See README for details. *
13 * *
14 * This software is distributed in the hope that it will be useful, but WITHOUT *
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
17 * for more details. *
18 * *
19 * You should have received a copy of the GNU General Public License along with *
20 * software; if not, write to the Free Software Foundation, Inc., 59 Temple *
21 * Place, Suite 330, Boston, MA 02111-1307 USA *
22 * *
23 * Nirvana Text Editor *
24 * June 15, 1995 *
25 * *
26 *******************************************************************************/
28 #ifdef HAVE_CONFIG_H
29 #include "../config.h"
30 #endif
32 #include "text.h"
33 #include "textP.h"
34 #include "textBuf.h"
35 #include "textDisp.h"
36 #include "textSel.h"
37 #include "textDrag.h"
38 #include "nedit.h"
39 #include "calltips.h"
40 #include "../util/DialogF.h"
41 #include "window.h"
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <limits.h>
46 #include <string.h>
47 #include <ctype.h>
48 #ifdef VMS
49 #include "../util/VMSparam.h"
50 #else
51 #ifndef __MVS__
52 #include <sys/param.h>
53 #endif
54 #endif /*VMS*/
55 #include <limits.h>
57 #include <X11/Intrinsic.h>
58 #include <X11/IntrinsicP.h>
59 #include <X11/StringDefs.h>
60 #include <X11/cursorfont.h>
61 #include <Xm/Xm.h>
62 #include <Xm/XmP.h>
63 #if XmVersion >= 1002
64 #include <Xm/PrimitiveP.h>
65 #endif
67 #ifdef HAVE_DEBUG_H
68 #include "../debug.h"
69 #endif
72 #ifdef UNICOS
73 #define XtOffset(p_type,field) ((size_t)__INTADDR__(&(((p_type)0)->field)))
74 #endif
76 /* Number of pixels of motion from the initial (grab-focus) button press
77 required to begin recognizing a mouse drag for the purpose of making a
78 selection */
79 #define SELECT_THRESHOLD 5
81 /* Length of delay in milliseconds for vertical autoscrolling */
82 #define VERTICAL_SCROLL_DELAY 50
84 static void initialize(TextWidget request, TextWidget new);
85 static void handleHidePointer(Widget w, XtPointer unused,
86 XEvent *event, Boolean *continue_to_dispatch);
87 static void handleShowPointer(Widget w, XtPointer unused,
88 XEvent *event, Boolean *continue_to_dispatch);
89 static void redisplay(TextWidget w, XEvent *event, Region region);
90 static void redisplayGE(TextWidget w, XtPointer client_data,
91 XEvent *event, Boolean *continue_to_dispatch_return);
92 static void destroy(TextWidget w);
93 static void resize(TextWidget w);
94 static Boolean setValues(TextWidget current, TextWidget request,
95 TextWidget new);
96 static void realize(Widget w, XtValueMask *valueMask,
97 XSetWindowAttributes *attributes);
98 static XtGeometryResult queryGeometry(Widget w, XtWidgetGeometry *proposed,
99 XtWidgetGeometry *answer);
100 static void grabFocusAP(Widget w, XEvent *event, String *args,
101 Cardinal *n_args);
102 static void moveDestinationAP(Widget w, XEvent *event, String *args,
103 Cardinal *nArgs);
104 static void extendAdjustAP(Widget w, XEvent *event, String *args,
105 Cardinal *nArgs);
106 static void extendStartAP(Widget w, XEvent *event, String *args,
107 Cardinal *nArgs);
108 static void extendEndAP(Widget w, XEvent *event, String *args,
109 Cardinal *nArgs);
110 static void processCancelAP(Widget w, XEvent *event, String *args,
111 Cardinal *nArgs);
112 static void secondaryStartAP(Widget w, XEvent *event, String *args,
113 Cardinal *nArgs);
114 static void secondaryOrDragStartAP(Widget w, XEvent *event, String *args,
115 Cardinal *nArgs);
116 static void secondaryAdjustAP(Widget w, XEvent *event, String *args,
117 Cardinal *nArgs);
118 static void secondaryOrDragAdjustAP(Widget w, XEvent *event, String *args,
119 Cardinal *nArgs);
120 static void copyToAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
121 static void copyToOrEndDragAP(Widget w, XEvent *event, String *args,
122 Cardinal *nArgs);
123 static void copyPrimaryAP(Widget w, XEvent *event, String *args,
124 Cardinal *nArgs);
125 static void cutPrimaryAP(Widget w, XEvent *event, String *args,
126 Cardinal *nArgs);
127 static void moveToAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
128 static void moveToOrEndDragAP(Widget w, XEvent *event, String *args,
129 Cardinal *nArgs);
130 static void endDragAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
131 static void exchangeAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
132 static void mousePanAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
133 static void pasteClipboardAP(Widget w, XEvent *event, String *args,
134 Cardinal *nArgs);
135 static void copyClipboardAP(Widget w, XEvent *event, String *args,
136 Cardinal *nArgs);
137 static void cutClipboardAP(Widget w, XEvent *event, String *args,
138 Cardinal *nArgs);
139 static void insertStringAP(Widget w, XEvent *event, String *args,
140 Cardinal *nArgs);
141 static void selfInsertAP(Widget w, XEvent *event, String *args,
142 Cardinal *n_args);
143 static void newlineAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
144 static void newlineAndIndentAP(Widget w, XEvent *event, String *args,
145 Cardinal *nArgs);
146 static void newlineNoIndentAP(Widget w, XEvent *event, String *args,
147 Cardinal *nArgs);
148 static void processTabAP(Widget w, XEvent *event, String *args,
149 Cardinal *nArgs);
150 static void endOfLineAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
151 static void beginningOfLineAP(Widget w, XEvent *event, String *args,
152 Cardinal *nArgs);
153 static void deleteSelectionAP(Widget w, XEvent *event, String *args,
154 Cardinal *nArgs);
155 static void deletePreviousCharacterAP(Widget w, XEvent *event, String *args,
156 Cardinal *nArgs);
157 static void deleteNextCharacterAP(Widget w, XEvent *event, String *args,
158 Cardinal *nArgs);
159 static void deletePreviousWordAP(Widget w, XEvent *event, String *args,
160 Cardinal *nArgs);
161 static void deleteNextWordAP(Widget w, XEvent *event, String *args,
162 Cardinal *nArgs);
163 static void deleteToStartOfLineAP(Widget w, XEvent *event, String *args,
164 Cardinal *nArgs);
165 static void deleteToEndOfLineAP(Widget w, XEvent *event, String *args,
166 Cardinal *nArgs);
167 static void forwardCharacterAP(Widget w, XEvent *event, String *args,
168 Cardinal *nArgs);
169 static void backwardCharacterAP(Widget w, XEvent *event, String *args,
170 Cardinal *nArgs);
171 static void forwardWordAP(Widget w, XEvent *event, String *args,
172 Cardinal *nArgs);
173 static void backwardWordAP(Widget w, XEvent *event, String *args,
174 Cardinal *nArgs);
175 static void forwardParagraphAP(Widget w, XEvent *event, String *args,
176 Cardinal *nArgs);
177 static void backwardParagraphAP(Widget w, XEvent *event, String *args,
178 Cardinal *nArgs);
179 static void keySelectAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
180 static void processUpAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
181 static void processShiftUpAP(Widget w, XEvent *event, String *args,
182 Cardinal *nArgs);
183 static void processDownAP(Widget w, XEvent *event, String *args,
184 Cardinal *nArgs);
185 static void processShiftDownAP(Widget w, XEvent *event, String *args,
186 Cardinal *nArgs);
187 static void beginningOfFileAP(Widget w, XEvent *event, String *args,
188 Cardinal *nArgs);
189 static void endOfFileAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
190 static void nextPageAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
191 static void previousPageAP(Widget w, XEvent *event, String *args,
192 Cardinal *nArgs);
193 static void pageLeftAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
194 static void pageRightAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
195 static void toggleOverstrikeAP(Widget w, XEvent *event, String *args,
196 Cardinal *nArgs);
197 static void scrollUpAP(Widget w, XEvent *event, String *args,
198 Cardinal *nArgs);
199 static void scrollDownAP(Widget w, XEvent *event, String *args,
200 Cardinal *nArgs);
201 static void scrollLeftAP(Widget w, XEvent *event, String *args,
202 Cardinal *nArgs);
203 static void scrollRightAP(Widget w, XEvent *event, String *args,
204 Cardinal *nArgs);
205 static void scrollToLineAP(Widget w, XEvent *event, String *args,
206 Cardinal *nArgs);
207 static void selectAllAP(Widget w, XEvent *event, String *args,
208 Cardinal *nArgs);
209 static void deselectAllAP(Widget w, XEvent *event, String *args,
210 Cardinal *nArgs);
211 static void focusInAP(Widget w, XEvent *event, String *args,
212 Cardinal *nArgs);
213 static void focusOutAP(Widget w, XEvent *event, String *args,
214 Cardinal *nArgs);
215 static void checkMoveSelectionChange(Widget w, XEvent *event, int startPos,
216 String *args, Cardinal *nArgs);
217 static void keyMoveExtendSelection(Widget w, XEvent *event, int startPos,
218 int rectangular);
219 static void checkAutoShowInsertPos(Widget w);
220 static int checkReadOnly(Widget w);
221 static void simpleInsertAtCursor(Widget w, char *chars, XEvent *event,
222 int allowPendingDelete);
223 static int pendingSelection(Widget w);
224 static int deletePendingSelection(Widget w, XEvent *event);
225 static int deleteEmulatedTab(Widget w, XEvent *event);
226 static void selectWord(Widget w, int pointerX);
227 static int spanForward(textBuffer *buf, int startPos, char *searchChars,
228 int ignoreSpace, int *foundPos);
229 static int spanBackward(textBuffer *buf, int startPos, char *searchChars, int
230 ignoreSpace, int *foundPos);
231 static void selectLine(Widget w);
232 static int startOfWord(TextWidget w, int pos);
233 static int endOfWord(TextWidget w, int pos);
234 static void checkAutoScroll(TextWidget w, int x, int y);
235 static void endDrag(Widget w);
236 static void cancelDrag(Widget w);
237 static void callCursorMovementCBs(Widget w, XEvent *event);
238 static void adjustSelection(TextWidget tw, int x, int y);
239 static void adjustSecondarySelection(TextWidget tw, int x, int y);
240 static void autoScrollTimerProc(XtPointer clientData, XtIntervalId *id);
241 static char *wrapText(TextWidget tw, char *startLine, char *text, int bufOffset,
242 int wrapMargin, int *breakBefore);
243 static int wrapLine(TextWidget tw, textBuffer *buf, int bufOffset,
244 int lineStartPos, int lineEndPos, int limitPos, int *breakAt,
245 int *charsAdded);
246 static char *createIndentString(TextWidget tw, textBuffer *buf, int bufOffset,
247 int lineStartPos, int lineEndPos, int *length, int *column);
248 static void cursorBlinkTimerProc(XtPointer clientData, XtIntervalId *id);
249 static int hasKey(const char *key, const String *args, const Cardinal *nArgs);
250 static int max(int i1, int i2);
251 static int min(int i1, int i2);
252 static int strCaseCmp(const char *str1, const char *str2);
253 static void ringIfNecessary(Boolean silent, Widget w);
255 static char defaultTranslations[] =
256 /* Home */
257 "~Shift ~Ctrl Alt<Key>osfBeginLine: last_document()\n"
259 /* Backspace */
260 "Ctrl<KeyPress>osfBackSpace: delete_previous_word()\n"
261 "<KeyPress>osfBackSpace: delete_previous_character()\n"
263 /* Delete */
264 "Alt Shift Ctrl<KeyPress>osfDelete: cut_primary(\"rect\")\n"
265 "Meta Shift Ctrl<KeyPress>osfDelete: cut_primary(\"rect\")\n"
266 "Shift Ctrl<KeyPress>osfDelete: cut_primary()\n"
267 "Ctrl<KeyPress>osfDelete: delete_to_end_of_line()\n"
268 "Shift<KeyPress>osfDelete: cut_clipboard()\n"
269 "<KeyPress>osfDelete: delete_next_character()\n"
271 /* Insert */
272 "Alt Shift Ctrl<KeyPress>osfInsert: copy_primary(\"rect\")\n"
273 "Meta Shift Ctrl<KeyPress>osfInsert: copy_primary(\"rect\")\n"
274 "Shift Ctrl<KeyPress>osfInsert: copy_primary()\n"
275 "Shift<KeyPress>osfInsert: paste_clipboard()\n"
276 "Ctrl<KeyPress>osfInsert: copy_clipboard()\n"
277 "~Shift ~Ctrl<KeyPress>osfInsert: set_overtype_mode()\n"
279 /* Cut/Copy/Paste */
280 "Shift Ctrl<KeyPress>osfCut: cut_primary()\n"
281 "<KeyPress>osfCut: cut_clipboard()\n"
282 "<KeyPress>osfCopy: copy_clipboard()\n"
283 "<KeyPress>osfPaste: paste_clipboard()\n"
284 "<KeyPress>osfPrimaryPaste: copy_primary()\n"
286 /* BeginLine */
287 "Alt Shift Ctrl<KeyPress>osfBeginLine: beginning_of_file(\"extend\", \"rect\")\n"
288 "Meta Shift Ctrl<KeyPress>osfBeginLine: beginning_of_file(\"extend\" \"rect\")\n"
289 "Alt Shift<KeyPress>osfBeginLine: beginning_of_line(\"extend\", \"rect\")\n"
290 "Meta Shift<KeyPress>osfBeginLine: beginning_of_line(\"extend\", \"rect\")\n"
291 "Shift Ctrl<KeyPress>osfBeginLine: beginning_of_file(\"extend\")\n"
292 "Ctrl<KeyPress>osfBeginLine: beginning_of_file()\n"
293 "Shift<KeyPress>osfBeginLine: beginning_of_line(\"extend\")\n"
294 "~Alt~Shift~Ctrl~Meta<KeyPress>osfBeginLine: beginning_of_line()\n"
296 /* EndLine */
297 "Alt Shift Ctrl<KeyPress>osfEndLine: end_of_file(\"extend\", \"rect\")\n"
298 "Meta Shift Ctrl<KeyPress>osfEndLine: end_of_file(\"extend\", \"rect\")\n"
299 "Alt Shift<KeyPress>osfEndLine: end_of_line(\"extend\", \"rect\")\n"
300 "Meta Shift<KeyPress>osfEndLine: end_of_line(\"extend\", \"rect\")\n"
301 "Shift Ctrl<KeyPress>osfEndLine: end_of_file(\"extend\")\n"
302 "Ctrl<KeyPress>osfEndLine: end_of_file()\n"
303 "Shift<KeyPress>osfEndLine: end_of_line(\"extend\")\n"
304 "~Alt~Shift~Ctrl~Meta<KeyPress>osfEndLine: end_of_line()\n"
306 /* Left */
307 "Alt Shift Ctrl<KeyPress>osfLeft: backward_word(\"extend\", \"rect\")\n"
308 "Meta Shift Ctrl<KeyPress>osfLeft: backward_word(\"extend\", \"rect\")\n"
309 "Alt Shift<KeyPress>osfLeft: key_select(\"left\", \"rect\")\n"
310 "Meta Shift<KeyPress>osfLeft: key_select(\"left\", \"rect\")\n"
311 "Shift Ctrl<KeyPress>osfLeft: backward_word(\"extend\")\n"
312 "Ctrl<KeyPress>osfLeft: backward_word()\n"
313 "Shift<KeyPress>osfLeft: key_select(\"left\")\n"
314 "~Alt~Shift~Ctrl~Meta<KeyPress>osfLeft: backward_character()\n"
316 /* Right */
317 "Alt Shift Ctrl<KeyPress>osfRight: forward_word(\"extend\", \"rect\")\n"
318 "Meta Shift Ctrl<KeyPress>osfRight: forward_word(\"extend\", \"rect\")\n"
319 "Alt Shift<KeyPress>osfRight: key_select(\"right\", \"rect\")\n"
320 "Meta Shift<KeyPress>osfRight: key_select(\"right\", \"rect\")\n"
321 "Shift Ctrl<KeyPress>osfRight: forward_word(\"extend\")\n"
322 "Ctrl<KeyPress>osfRight: forward_word()\n"
323 "Shift<KeyPress>osfRight: key_select(\"right\")\n"
324 "~Alt~Shift~Ctrl~Meta<KeyPress>osfRight: forward_character()\n"
326 /* Up */
327 "Alt Shift Ctrl<KeyPress>osfUp: backward_paragraph(\"extend\", \"rect\")\n"
328 "Meta Shift Ctrl<KeyPress>osfUp: backward_paragraph(\"extend\", \"rect\")\n"
329 "Alt Shift<KeyPress>osfUp: process_shift_up(\"rect\")\n"
330 "Meta Shift<KeyPress>osfUp: process_shift_up(\"rect\")\n"
331 "Shift Ctrl<KeyPress>osfUp: backward_paragraph(\"extend\")\n"
332 "Ctrl<KeyPress>osfUp: backward_paragraph()\n"
333 "Shift<KeyPress>osfUp: process_shift_up()\n"
334 "~Alt~Shift~Ctrl~Meta<KeyPress>osfUp: process_up()\n"
336 /* Down */
337 "Alt Shift Ctrl<KeyPress>osfDown: forward_paragraph(\"extend\", \"rect\")\n"
338 "Meta Shift Ctrl<KeyPress>osfDown: forward_paragraph(\"extend\", \"rect\")\n"
339 "Alt Shift<KeyPress>osfDown: process_shift_down(\"rect\")\n"
340 "Meta Shift<KeyPress>osfDown: process_shift_down(\"rect\")\n"
341 "Shift Ctrl<KeyPress>osfDown: forward_paragraph(\"extend\")\n"
342 "Ctrl<KeyPress>osfDown: forward_paragraph()\n"
343 "Shift<KeyPress>osfDown: process_shift_down()\n"
344 "~Alt~Shift~Ctrl~Meta<KeyPress>osfDown: process_down()\n"
346 /* PageUp */
347 "Alt Shift Ctrl<KeyPress>osfPageUp: page_left(\"extend\", \"rect\")\n"
348 "Meta Shift Ctrl<KeyPress>osfPageUp: page_left(\"extend\", \"rect\")\n"
349 "Alt Shift<KeyPress>osfPageUp: previous_page(\"extend\", \"rect\")\n"
350 "Meta Shift<KeyPress>osfPageUp: previous_page(\"extend\", \"rect\")\n"
351 "Shift Ctrl<KeyPress>osfPageUp: page_left(\"extend\")\n"
352 "Ctrl<KeyPress>osfPageUp: previous_document()\n"
353 "Shift<KeyPress>osfPageUp: previous_page(\"extend\")\n"
354 "~Alt ~Shift ~Ctrl ~Meta<KeyPress>osfPageUp: previous_page()\n"
356 /* PageDown */
357 "Alt Shift Ctrl<KeyPress>osfPageDown: page_right(\"extend\", \"rect\")\n"
358 "Meta Shift Ctrl<KeyPress>osfPageDown: page_right(\"extend\", \"rect\")\n"
359 "Alt Shift<KeyPress>osfPageDown: next_page(\"extend\", \"rect\")\n"
360 "Meta Shift<KeyPress>osfPageDown: next_page(\"extend\", \"rect\")\n"
361 "Shift Ctrl<KeyPress>osfPageDown: page_right(\"extend\")\n"
362 "Ctrl<KeyPress>osfPageDown: next_document()\n"
363 "Shift<KeyPress>osfPageDown: next_page(\"extend\")\n"
364 "~Alt ~Shift ~Ctrl ~Meta<KeyPress>osfPageDown: next_page()\n"
366 /* PageLeft and PageRight are placed later than the PageUp/PageDown
367 bindings. Some systems map osfPageLeft to Ctrl-PageUp.
368 Overloading this single key gives problems, and we want to give
369 priority to the normal version. */
371 /* PageLeft */
372 "Alt Shift<KeyPress>osfPageLeft: page_left(\"extend\", \"rect\")\n"
373 "Meta Shift<KeyPress>osfPageLeft: page_left(\"extend\", \"rect\")\n"
374 "Shift<KeyPress>osfPageLeft: page_left(\"extend\")\n"
375 "~Alt ~Shift ~Ctrl ~Meta<KeyPress>osfPageLeft: page_left()\n"
377 /* PageRight */
378 "Alt Shift<KeyPress>osfPageRight: page_right(\"extend\", \"rect\")\n"
379 "Meta Shift<KeyPress>osfPageRight: page_right(\"extend\", \"rect\")\n"
380 "Shift<KeyPress>osfPageRight: page_right(\"extend\")\n"
381 "~Alt ~Shift ~Ctrl ~Meta<KeyPress>osfPageRight: page_right()\n"
383 "Shift<KeyPress>osfSelect: key_select()\n"
384 "<KeyPress>osfCancel: process_cancel()\n"
385 "Ctrl~Alt~Meta<KeyPress>v: paste_clipboard()\n"
386 "Ctrl~Alt~Meta<KeyPress>c: copy_clipboard()\n"
387 "Ctrl~Alt~Meta<KeyPress>x: cut_clipboard()\n"
388 "Ctrl~Alt~Meta<KeyPress>u: delete_to_start_of_line()\n"
389 "Ctrl<KeyPress>Return: newline_and_indent()\n"
390 "Shift<KeyPress>Return: newline_no_indent()\n"
391 "<KeyPress>Return: newline()\n"
392 /* KP_Enter = osfActivate
393 Note: Ctrl+KP_Enter is already bound to Execute Command Line... */
394 "Shift<KeyPress>osfActivate: newline_no_indent()\n"
395 "<KeyPress>osfActivate: newline()\n"
396 "Ctrl<KeyPress>Tab: self_insert()\n"
397 "<KeyPress>Tab: process_tab()\n"
398 "Alt Shift Ctrl<KeyPress>space: key_select(\"rect\")\n"
399 "Meta Shift Ctrl<KeyPress>space: key_select(\"rect\")\n"
400 "Shift Ctrl~Meta~Alt<KeyPress>space: key_select()\n"
401 "Ctrl~Meta~Alt<KeyPress>slash: select_all()\n"
402 "Ctrl~Meta~Alt<KeyPress>backslash: deselect_all()\n"
403 "<KeyPress>: self_insert()\n"
404 "Alt Ctrl<Btn1Down>: move_destination()\n"
405 "Meta Ctrl<Btn1Down>: move_destination()\n"
406 "Shift Ctrl<Btn1Down>: extend_start(\"rect\")\n"
407 "Shift<Btn1Down>: extend_start()\n"
408 "<Btn1Down>: grab_focus()\n"
409 "Button1 Ctrl<MotionNotify>: extend_adjust(\"rect\")\n"
410 "Button1~Ctrl<MotionNotify>: extend_adjust()\n"
411 "<Btn1Up>: extend_end()\n"
412 "<Btn2Down>: secondary_or_drag_start()\n"
413 "Shift Ctrl Button2<MotionNotify>: secondary_or_drag_adjust(\"rect\", \"copy\", \"overlay\")\n"
414 "Shift Button2<MotionNotify>: secondary_or_drag_adjust(\"copy\")\n"
415 "Ctrl Button2<MotionNotify>: secondary_or_drag_adjust(\"rect\", \"overlay\")\n"
416 "Button2<MotionNotify>: secondary_or_drag_adjust()\n"
417 "Shift Ctrl<Btn2Up>: move_to_or_end_drag(\"copy\", \"overlay\")\n"
418 "Shift <Btn2Up>: move_to_or_end_drag(\"copy\")\n"
419 "Alt<Btn2Up>: exchange()\n"
420 "Meta<Btn2Up>: exchange()\n"
421 "Ctrl<Btn2Up>: copy_to_or_end_drag(\"overlay\")\n"
422 "<Btn2Up>: copy_to_or_end_drag()\n"
423 "Ctrl~Meta~Alt<Btn3Down>: mouse_pan()\n"
424 "Ctrl~Meta~Alt Button3<MotionNotify>: mouse_pan()\n"
425 "<Btn3Up>: end_drag()\n"
426 "<FocusIn>: focusIn()\n"
427 "<FocusOut>: focusOut()\n"
428 /* Support for mouse wheel in XFree86 */
429 "Shift<Btn4Down>,<Btn4Up>: scroll_up(1)\n"
430 "Shift<Btn5Down>,<Btn5Up>: scroll_down(1)\n"
431 "Ctrl<Btn4Down>,<Btn4Up>: scroll_up(1, pages)\n"
432 "Ctrl<Btn5Down>,<Btn5Up>: scroll_down(1, pages)\n"
433 "<Btn4Down>,<Btn4Up>: scroll_up(5)\n"
434 "<Btn5Down>,<Btn5Up>: scroll_down(5)\n";
435 /* some of the translations from the Motif text widget were not picked up:
436 :<KeyPress>osfSelect: set-anchor()\n\
437 :<KeyPress>osfActivate: activate()\n\
438 ~Shift Ctrl~Meta~Alt<KeyPress>Return: activate()\n\
439 ~Shift Ctrl~Meta~Alt<KeyPress>space: set-anchor()\n\
440 :<KeyPress>osfClear: clear-selection()\n\
441 ~Shift~Ctrl~Meta~Alt<KeyPress>Return: process-return()\n\
442 Shift~Meta~Alt<KeyPress>Tab: prev-tab-group()\n\
443 Ctrl~Meta~Alt<KeyPress>Tab: next-tab-group()\n\
444 <UnmapNotify>: unmap()\n\
445 <EnterNotify>: enter()\n\
446 <LeaveNotify>: leave()\n
450 static XtActionsRec actionsList[] = {
451 {"self-insert", selfInsertAP},
452 {"self_insert", selfInsertAP},
453 {"grab-focus", grabFocusAP},
454 {"grab_focus", grabFocusAP},
455 {"extend-adjust", extendAdjustAP},
456 {"extend_adjust", extendAdjustAP},
457 {"extend-start", extendStartAP},
458 {"extend_start", extendStartAP},
459 {"extend-end", extendEndAP},
460 {"extend_end", extendEndAP},
461 {"secondary-adjust", secondaryAdjustAP},
462 {"secondary_adjust", secondaryAdjustAP},
463 {"secondary-or-drag-adjust", secondaryOrDragAdjustAP},
464 {"secondary_or_drag_adjust", secondaryOrDragAdjustAP},
465 {"secondary-start", secondaryStartAP},
466 {"secondary_start", secondaryStartAP},
467 {"secondary-or-drag-start", secondaryOrDragStartAP},
468 {"secondary_or_drag_start", secondaryOrDragStartAP},
469 {"process-bdrag", secondaryOrDragStartAP},
470 {"process_bdrag", secondaryOrDragStartAP},
471 {"move-destination", moveDestinationAP},
472 {"move_destination", moveDestinationAP},
473 {"move-to", moveToAP},
474 {"move_to", moveToAP},
475 {"move-to-or-end-drag", moveToOrEndDragAP},
476 {"move_to_or_end_drag", moveToOrEndDragAP},
477 {"end_drag", endDragAP},
478 {"copy-to", copyToAP},
479 {"copy_to", copyToAP},
480 {"copy-to-or-end-drag", copyToOrEndDragAP},
481 {"copy_to_or_end_drag", copyToOrEndDragAP},
482 {"exchange", exchangeAP},
483 {"process-cancel", processCancelAP},
484 {"process_cancel", processCancelAP},
485 {"paste-clipboard", pasteClipboardAP},
486 {"paste_clipboard", pasteClipboardAP},
487 {"copy-clipboard", copyClipboardAP},
488 {"copy_clipboard", copyClipboardAP},
489 {"cut-clipboard", cutClipboardAP},
490 {"cut_clipboard", cutClipboardAP},
491 {"copy-primary", copyPrimaryAP},
492 {"copy_primary", copyPrimaryAP},
493 {"cut-primary", cutPrimaryAP},
494 {"cut_primary", cutPrimaryAP},
495 {"newline", newlineAP},
496 {"newline-and-indent", newlineAndIndentAP},
497 {"newline_and_indent", newlineAndIndentAP},
498 {"newline-no-indent", newlineNoIndentAP},
499 {"newline_no_indent", newlineNoIndentAP},
500 {"delete-selection", deleteSelectionAP},
501 {"delete_selection", deleteSelectionAP},
502 {"delete-previous-character", deletePreviousCharacterAP},
503 {"delete_previous_character", deletePreviousCharacterAP},
504 {"delete-next-character", deleteNextCharacterAP},
505 {"delete_next_character", deleteNextCharacterAP},
506 {"delete-previous-word", deletePreviousWordAP},
507 {"delete_previous_word", deletePreviousWordAP},
508 {"delete-next-word", deleteNextWordAP},
509 {"delete_next_word", deleteNextWordAP},
510 {"delete-to-start-of-line", deleteToStartOfLineAP},
511 {"delete_to_start_of_line", deleteToStartOfLineAP},
512 {"delete-to-end-of-line", deleteToEndOfLineAP},
513 {"delete_to_end_of_line", deleteToEndOfLineAP},
514 {"forward-character", forwardCharacterAP},
515 {"forward_character", forwardCharacterAP},
516 {"backward-character", backwardCharacterAP},
517 {"backward_character", backwardCharacterAP},
518 {"key-select", keySelectAP},
519 {"key_select", keySelectAP},
520 {"process-up", processUpAP},
521 {"process_up", processUpAP},
522 {"process-down", processDownAP},
523 {"process_down", processDownAP},
524 {"process-shift-up", processShiftUpAP},
525 {"process_shift_up", processShiftUpAP},
526 {"process-shift-down", processShiftDownAP},
527 {"process_shift_down", processShiftDownAP},
528 {"process-home", beginningOfLineAP},
529 {"process_home", beginningOfLineAP},
530 {"forward-word", forwardWordAP},
531 {"forward_word", forwardWordAP},
532 {"backward-word", backwardWordAP},
533 {"backward_word", backwardWordAP},
534 {"forward-paragraph", forwardParagraphAP},
535 {"forward_paragraph", forwardParagraphAP},
536 {"backward-paragraph", backwardParagraphAP},
537 {"backward_paragraph", backwardParagraphAP},
538 {"beginning-of-line", beginningOfLineAP},
539 {"beginning_of_line", beginningOfLineAP},
540 {"end-of-line", endOfLineAP},
541 {"end_of_line", endOfLineAP},
542 {"beginning-of-file", beginningOfFileAP},
543 {"beginning_of_file", beginningOfFileAP},
544 {"end-of-file", endOfFileAP},
545 {"end_of_file", endOfFileAP},
546 {"next-page", nextPageAP},
547 {"next_page", nextPageAP},
548 {"previous-page", previousPageAP},
549 {"previous_page", previousPageAP},
550 {"page-left", pageLeftAP},
551 {"page_left", pageLeftAP},
552 {"page-right", pageRightAP},
553 {"page_right", pageRightAP},
554 {"toggle-overstrike", toggleOverstrikeAP},
555 {"toggle_overstrike", toggleOverstrikeAP},
556 {"scroll-up", scrollUpAP},
557 {"scroll_up", scrollUpAP},
558 {"scroll-down", scrollDownAP},
559 {"scroll_down", scrollDownAP},
560 {"scroll_left", scrollLeftAP},
561 {"scroll_right", scrollRightAP},
562 {"scroll-to-line", scrollToLineAP},
563 {"scroll_to_line", scrollToLineAP},
564 {"select-all", selectAllAP},
565 {"select_all", selectAllAP},
566 {"deselect-all", deselectAllAP},
567 {"deselect_all", deselectAllAP},
568 {"focusIn", focusInAP},
569 {"focusOut", focusOutAP},
570 {"process-return", selfInsertAP},
571 {"process_return", selfInsertAP},
572 {"process-tab", processTabAP},
573 {"process_tab", processTabAP},
574 {"insert-string", insertStringAP},
575 {"insert_string", insertStringAP},
576 {"mouse_pan", mousePanAP},
579 /* The motif text widget defined a bunch of actions which the nedit text
580 widget as-of-yet does not support:
582 Actions which were not bound to keys (for emacs emulation, some of
583 them should probably be supported:
585 kill-next-character()
586 kill-next-word()
587 kill-previous-character()
588 kill-previous-word()
589 kill-selection()
590 kill-to-end-of-line()
591 kill-to-start-of-line()
592 unkill()
593 next-line()
594 newline-and-backup()
595 beep()
596 redraw-display()
597 scroll-one-line-down()
598 scroll-one-line-up()
599 set-insertion-point()
601 Actions which are not particularly useful:
603 set-anchor()
604 activate()
605 clear-selection() -> this is a wierd one
606 do-quick-action() -> don't think this ever worked
607 Help()
608 next-tab-group()
609 select-adjust()
610 select-start()
611 select-end()
614 static XtResource resources[] = {
615 {XmNhighlightThickness, XmCHighlightThickness, XmRDimension,
616 sizeof(Dimension), XtOffset(TextWidget, primitive.highlight_thickness),
617 XmRInt, 0},
618 {XmNshadowThickness, XmCShadowThickness, XmRDimension, sizeof(Dimension),
619 XtOffset(TextWidget, primitive.shadow_thickness), XmRInt, 0},
620 {textNfont, textCFont, XmRFontStruct, sizeof(XFontStruct *),
621 XtOffset(TextWidget, text.fontStruct), XmRString, "fixed"},
622 {textNselectForeground, textCSelectForeground, XmRPixel, sizeof(Pixel),
623 XtOffset(TextWidget, text.selectFGPixel), XmRString,
624 NEDIT_DEFAULT_SEL_FG},
625 {textNselectBackground, textCSelectBackground, XmRPixel, sizeof(Pixel),
626 XtOffset(TextWidget, text.selectBGPixel), XmRString,
627 NEDIT_DEFAULT_SEL_BG},
628 {textNhighlightForeground, textCHighlightForeground, XmRPixel,sizeof(Pixel),
629 XtOffset(TextWidget, text.highlightFGPixel), XmRString,
630 NEDIT_DEFAULT_HI_FG},
631 {textNhighlightBackground, textCHighlightBackground, XmRPixel,sizeof(Pixel),
632 XtOffset(TextWidget, text.highlightBGPixel), XmRString,
633 NEDIT_DEFAULT_HI_BG},
634 {textNlineNumForeground, textCLineNumForeground, XmRPixel,sizeof(Pixel),
635 XtOffset(TextWidget, text.lineNumFGPixel), XmRString,
636 NEDIT_DEFAULT_LINENO_FG},
637 {textNcursorForeground, textCCursorForeground, XmRPixel,sizeof(Pixel),
638 XtOffset(TextWidget, text.cursorFGPixel), XmRString,
639 NEDIT_DEFAULT_CURSOR_FG},
640 {textNcalltipForeground, textCcalltipForeground, XmRPixel,sizeof(Pixel),
641 XtOffset(TextWidget, text.calltipFGPixel), XmRString,
642 NEDIT_DEFAULT_CALLTIP_FG},
643 {textNcalltipBackground, textCcalltipBackground, XmRPixel,sizeof(Pixel),
644 XtOffset(TextWidget, text.calltipBGPixel), XmRString,
645 NEDIT_DEFAULT_CALLTIP_BG},
646 {textNbacklightCharTypes,textCBacklightCharTypes,XmRString,sizeof(XmString),
647 XtOffset(TextWidget, text.backlightCharTypes), XmRString, NULL},
648 {textNrows, textCRows, XmRInt,sizeof(int),
649 XtOffset(TextWidget, text.rows), XmRString, "24"},
650 {textNcolumns, textCColumns, XmRInt, sizeof(int),
651 XtOffset(TextWidget, text.columns), XmRString, "80"},
652 {textNmarginWidth, textCMarginWidth, XmRInt, sizeof(int),
653 XtOffset(TextWidget, text.marginWidth), XmRString, "5"},
654 {textNmarginHeight, textCMarginHeight, XmRInt, sizeof(int),
655 XtOffset(TextWidget, text.marginHeight), XmRString, "5"},
656 {textNpendingDelete, textCPendingDelete, XmRBoolean, sizeof(Boolean),
657 XtOffset(TextWidget, text.pendingDelete), XmRString, "True"},
658 {textNautoWrap, textCAutoWrap, XmRBoolean, sizeof(Boolean),
659 XtOffset(TextWidget, text.autoWrap), XmRString, "True"},
660 {textNcontinuousWrap, textCContinuousWrap, XmRBoolean, sizeof(Boolean),
661 XtOffset(TextWidget, text.continuousWrap), XmRString, "True"},
662 {textNautoIndent, textCAutoIndent, XmRBoolean, sizeof(Boolean),
663 XtOffset(TextWidget, text.autoIndent), XmRString, "True"},
664 {textNsmartIndent, textCSmartIndent, XmRBoolean, sizeof(Boolean),
665 XtOffset(TextWidget, text.smartIndent), XmRString, "False"},
666 {textNoverstrike, textCOverstrike, XmRBoolean, sizeof(Boolean),
667 XtOffset(TextWidget, text.overstrike), XmRString, "False"},
668 {textNheavyCursor, textCHeavyCursor, XmRBoolean, sizeof(Boolean),
669 XtOffset(TextWidget, text.heavyCursor), XmRString, "False"},
670 {textNreadOnly, textCReadOnly, XmRBoolean, sizeof(Boolean),
671 XtOffset(TextWidget, text.readOnly), XmRString, "False"},
672 {textNhidePointer, textCHidePointer, XmRBoolean, sizeof(Boolean),
673 XtOffset(TextWidget, text.hidePointer), XmRString, "False"},
674 {textNwrapMargin, textCWrapMargin, XmRInt, sizeof(int),
675 XtOffset(TextWidget, text.wrapMargin), XmRString, "0"},
676 {textNhScrollBar, textCHScrollBar, XmRWidget, sizeof(Widget),
677 XtOffset(TextWidget, text.hScrollBar), XmRString, ""},
678 {textNvScrollBar, textCVScrollBar, XmRWidget, sizeof(Widget),
679 XtOffset(TextWidget, text.vScrollBar), XmRString, ""},
680 {textNlineNumCols, textCLineNumCols, XmRInt, sizeof(int),
681 XtOffset(TextWidget, text.lineNumCols), XmRString, "0"},
682 {textNautoShowInsertPos, textCAutoShowInsertPos, XmRBoolean,
683 sizeof(Boolean), XtOffset(TextWidget, text.autoShowInsertPos),
684 XmRString, "True"},
685 {textNautoWrapPastedText, textCAutoWrapPastedText, XmRBoolean,
686 sizeof(Boolean), XtOffset(TextWidget, text.autoWrapPastedText),
687 XmRString, "False"},
688 {textNwordDelimiters, textCWordDelimiters, XmRString, sizeof(char *),
689 XtOffset(TextWidget, text.delimiters), XmRString,
690 ".,/\\`'!@#%^&*()-=+{}[]\":;<>?"},
691 {textNblinkRate, textCBlinkRate, XmRInt, sizeof(int),
692 XtOffset(TextWidget, text.cursorBlinkRate), XmRString, "500"},
693 {textNemulateTabs, textCEmulateTabs, XmRInt, sizeof(int),
694 XtOffset(TextWidget, text.emulateTabs), XmRString, "0"},
695 {textNfocusCallback, textCFocusCallback, XmRCallback, sizeof(caddr_t),
696 XtOffset(TextWidget, text.focusInCB), XtRCallback, NULL},
697 {textNlosingFocusCallback, textCLosingFocusCallback, XmRCallback,
698 sizeof(caddr_t), XtOffset(TextWidget, text.focusOutCB), XtRCallback,NULL},
699 {textNcursorMovementCallback, textCCursorMovementCallback, XmRCallback,
700 sizeof(caddr_t), XtOffset(TextWidget, text.cursorCB), XtRCallback, NULL},
701 {textNdragStartCallback, textCDragStartCallback, XmRCallback,
702 sizeof(caddr_t), XtOffset(TextWidget, text.dragStartCB), XtRCallback,
703 NULL},
704 {textNdragEndCallback, textCDragEndCallback, XmRCallback,
705 sizeof(caddr_t), XtOffset(TextWidget, text.dragEndCB), XtRCallback, NULL},
706 {textNsmartIndentCallback, textCSmartIndentCallback, XmRCallback,
707 sizeof(caddr_t), XtOffset(TextWidget, text.smartIndentCB), XtRCallback,
708 NULL},
709 {textNcursorVPadding, textCCursorVPadding, XtRCardinal, sizeof(Cardinal),
710 XtOffset(TextWidget, text.cursorVPadding), XmRString, "0"}
713 static TextClassRec textClassRec = {
714 /* CoreClassPart */
716 (WidgetClass) &xmPrimitiveClassRec, /* superclass */
717 "Text", /* class_name */
718 sizeof(TextRec), /* widget_size */
719 NULL, /* class_initialize */
720 NULL, /* class_part_initialize */
721 FALSE, /* class_inited */
722 (XtInitProc)initialize, /* initialize */
723 NULL, /* initialize_hook */
724 realize, /* realize */
725 actionsList, /* actions */
726 XtNumber(actionsList), /* num_actions */
727 resources, /* resources */
728 XtNumber(resources), /* num_resources */
729 NULLQUARK, /* xrm_class */
730 TRUE, /* compress_motion */
731 TRUE, /* compress_exposure */
732 TRUE, /* compress_enterleave */
733 FALSE, /* visible_interest */
734 (XtWidgetProc)destroy, /* destroy */
735 (XtWidgetProc)resize, /* resize */
736 (XtExposeProc)redisplay, /* expose */
737 (XtSetValuesFunc)setValues, /* set_values */
738 NULL, /* set_values_hook */
739 XtInheritSetValuesAlmost, /* set_values_almost */
740 NULL, /* get_values_hook */
741 NULL, /* accept_focus */
742 XtVersion, /* version */
743 NULL, /* callback private */
744 defaultTranslations, /* tm_table */
745 queryGeometry, /* query_geometry */
746 NULL, /* display_accelerator */
747 NULL, /* extension */
749 /* Motif primitive class fields */
751 (XtWidgetProc)_XtInherit, /* Primitive border_highlight */
752 (XtWidgetProc)_XtInherit, /* Primitive border_unhighlight */
753 NULL, /*XtInheritTranslations,*/ /* translations */
754 NULL, /* arm_and_activate */
755 NULL, /* get resources */
756 0, /* num get_resources */
757 NULL, /* extension */
759 /* Text class part */
761 0, /* ignored */
765 WidgetClass textWidgetClass = (WidgetClass)&textClassRec;
766 #define NEDIT_HIDE_CURSOR_MASK (KeyPressMask)
767 #define NEDIT_SHOW_CURSOR_MASK (FocusChangeMask | PointerMotionMask | ButtonMotionMask | ButtonPressMask | ButtonReleaseMask)
768 static char empty_bits[] = {0x00, 0x00, 0x00, 0x00};
769 static Cursor empty_cursor = 0;
772 ** Widget initialize method
774 static void initialize(TextWidget request, TextWidget new)
776 XFontStruct *fs = new->text.fontStruct;
777 char *delimiters;
778 textBuffer *buf;
779 Pixel white, black;
780 int textLeft;
781 int charWidth = fs->max_bounds.width;
782 int marginWidth = new->text.marginWidth;
783 int lineNumCols = new->text.lineNumCols;
785 /* Set the initial window size based on the rows and columns resources */
786 if (request->core.width == 0)
787 new->core.width = charWidth * new->text.columns + marginWidth*2 +
788 (lineNumCols == 0 ? 0 : marginWidth + charWidth * lineNumCols);
789 if (request->core.height == 0)
790 new->core.height = (fs->ascent + fs->descent) * new->text.rows +
791 new->text.marginHeight * 2;
793 /* The default colors work for B&W as well as color, except for
794 selectFGPixel and selectBGPixel, where color highlighting looks
795 much better without reverse video, so if we get here, and the
796 selection is totally unreadable because of the bad default colors,
797 swap the colors and make the selection reverse video */
798 white = WhitePixelOfScreen(XtScreen((Widget)new));
799 black = BlackPixelOfScreen(XtScreen((Widget)new));
800 if ( new->text.selectBGPixel == white &&
801 new->core.background_pixel == white &&
802 new->text.selectFGPixel == black &&
803 new->primitive.foreground == black) {
804 new->text.selectBGPixel = black;
805 new->text.selectFGPixel = white;
808 /* Create the initial text buffer for the widget to display (which can
809 be replaced later with TextSetBuffer) */
810 buf = BufCreate();
812 /* Create and initialize the text-display part of the widget */
813 textLeft = new->text.marginWidth +
814 (lineNumCols == 0 ? 0 : marginWidth + charWidth * lineNumCols);
815 new->text.textD = TextDCreate((Widget)new, new->text.hScrollBar,
816 new->text.vScrollBar, textLeft, new->text.marginHeight,
817 new->core.width - marginWidth - textLeft,
818 new->core.height - new->text.marginHeight * 2,
819 lineNumCols == 0 ? 0 : marginWidth,
820 lineNumCols == 0 ? 0 : lineNumCols * charWidth,
821 buf, new->text.fontStruct, new->core.background_pixel,
822 new->primitive.foreground, new->text.selectFGPixel,
823 new->text.selectBGPixel, new->text.highlightFGPixel,
824 new->text.highlightBGPixel, new->text.cursorFGPixel,
825 new->text.lineNumFGPixel,
826 new->text.continuousWrap, new->text.wrapMargin,
827 new->text.backlightCharTypes, new->text.calltipFGPixel,
828 new->text.calltipBGPixel);
830 /* Add mandatory delimiters blank, tab, and newline to the list of
831 delimiters. The memory use scheme here is that new values are
832 always copied, and can therefore be safely freed on subsequent
833 set-values calls or destroy */
834 delimiters = XtMalloc(strlen(new->text.delimiters) + 4);
835 sprintf(delimiters, "%s%s", " \t\n", new->text.delimiters);
836 new->text.delimiters = delimiters;
838 /* Start with the cursor blanked (widgets don't have focus on creation,
839 the initial FocusIn event will unblank it and get blinking started) */
840 new->text.textD->cursorOn = False;
842 /* Initialize the widget variables */
843 new->text.autoScrollProcID = 0;
844 new->text.cursorBlinkProcID = 0;
845 new->text.dragState = NOT_CLICKED;
846 new->text.multiClickState = NO_CLICKS;
847 new->text.lastBtnDown = 0;
848 new->text.selectionOwner = False;
849 new->text.motifDestOwner = False;
850 new->text.emTabsBeforeCursor = 0;
852 #ifndef NO_XMIM
853 /* Register the widget to the input manager */
854 XmImRegister((Widget)new, 0);
855 /* In case some Resources for the IC need to be set, add them below */
856 XmImVaSetValues((Widget)new, NULL);
857 #endif
859 XtAddEventHandler((Widget)new, GraphicsExpose, True,
860 (XtEventHandler)redisplayGE, (Opaque)NULL);
862 if (new->text.hidePointer) {
863 Display *theDisplay;
864 Pixmap empty_pixmap;
865 XColor black_color;
866 /* Set up the empty Cursor */
867 if (empty_cursor == 0) {
868 theDisplay = XtDisplay((Widget)new);
869 empty_pixmap = XCreateBitmapFromData(theDisplay,
870 RootWindowOfScreen(XtScreen((Widget)new)), empty_bits, 1, 1);
871 XParseColor(theDisplay, DefaultColormapOfScreen(XtScreen((Widget)new)),
872 "black", &black_color);
873 empty_cursor = XCreatePixmapCursor(theDisplay, empty_pixmap,
874 empty_pixmap, &black_color, &black_color, 0, 0);
877 /* Add event handler to hide the pointer on keypresses */
878 XtAddEventHandler((Widget)new, NEDIT_HIDE_CURSOR_MASK, False,
879 handleHidePointer, (Opaque)NULL);
883 /* Hide the pointer while the user is typing */
884 static void handleHidePointer(Widget w, XtPointer unused,
885 XEvent *event, Boolean *continue_to_dispatch) {
886 TextWidget tw = (TextWidget) w;
887 ShowHidePointer(tw, True);
890 /* Restore the pointer if the mouse moves or focus changes */
891 static void handleShowPointer(Widget w, XtPointer unused,
892 XEvent *event, Boolean *continue_to_dispatch) {
893 TextWidget tw = (TextWidget) w;
894 ShowHidePointer(tw, False);
897 void ShowHidePointer(TextWidget w, Boolean hidePointer)
899 if (w->text.hidePointer) {
900 if (hidePointer != w->text.textD->pointerHidden) {
901 if (hidePointer) {
902 /* Don't listen for keypresses any more */
903 XtRemoveEventHandler((Widget)w, NEDIT_HIDE_CURSOR_MASK, False,
904 handleHidePointer, (Opaque)NULL);
905 /* Switch to empty cursor */
906 XDefineCursor(XtDisplay(w), XtWindow(w), empty_cursor);
908 w->text.textD->pointerHidden = True;
910 /* Listen to mouse movement, focus change, and button presses */
911 XtAddEventHandler((Widget)w, NEDIT_SHOW_CURSOR_MASK,
912 False, handleShowPointer, (Opaque)NULL);
914 else {
915 /* Don't listen to mouse/focus events any more */
916 XtRemoveEventHandler((Widget)w, NEDIT_SHOW_CURSOR_MASK,
917 False, handleShowPointer, (Opaque)NULL);
918 /* Switch to regular cursor */
919 XUndefineCursor(XtDisplay(w), XtWindow(w));
921 w->text.textD->pointerHidden = False;
923 /* Listen for keypresses now */
924 XtAddEventHandler((Widget)w, NEDIT_HIDE_CURSOR_MASK, False,
925 handleHidePointer, (Opaque)NULL);
932 ** Widget destroy method
934 static void destroy(TextWidget w)
936 textBuffer *buf;
938 /* Free the text display and possibly the attached buffer. The buffer
939 is freed only if after removing all of the modify procs (by calling
940 StopHandlingXSelections and TextDFree) there are no modify procs
941 left */
942 StopHandlingXSelections((Widget)w);
943 buf = w->text.textD->buffer;
944 TextDFree(w->text.textD);
945 if (buf->nModifyProcs == 0)
946 BufFree(buf);
948 if (w->text.cursorBlinkProcID != 0)
949 XtRemoveTimeOut(w->text.cursorBlinkProcID);
950 XtFree(w->text.delimiters);
951 XtRemoveAllCallbacks((Widget)w, textNfocusCallback);
952 XtRemoveAllCallbacks((Widget)w, textNlosingFocusCallback);
953 XtRemoveAllCallbacks((Widget)w, textNcursorMovementCallback);
954 XtRemoveAllCallbacks((Widget)w, textNdragStartCallback);
955 XtRemoveAllCallbacks((Widget)w, textNdragEndCallback);
957 #ifndef NO_XMIM
958 /* Unregister the widget from the input manager */
959 XmImUnregister((Widget)w);
960 #endif
964 ** Widget resize method. Called when the size of the widget changes
966 static void resize(TextWidget w)
968 XFontStruct *fs = w->text.fontStruct;
969 int height = w->core.height, width = w->core.width;
970 int marginWidth = w->text.marginWidth, marginHeight = w->text.marginHeight;
971 int lineNumAreaWidth = w->text.lineNumCols == 0 ? 0 : w->text.marginWidth +
972 fs->max_bounds.width * w->text.lineNumCols;
974 w->text.columns = (width - marginWidth*2 - lineNumAreaWidth) /
975 fs->max_bounds.width;
976 w->text.rows = (height - marginHeight*2) / (fs->ascent + fs->descent);
978 /* Reject widths and heights less than a character, which the text
979 display can't tolerate. This is not strictly legal, but I've seen
980 it done in other widgets and it seems to do no serious harm. NEdit
981 prevents panes from getting smaller than one line, but sometimes
982 splitting windows on Linux 2.0 systems (same Motif, why the change in
983 behavior?), causes one or two resize calls with < 1 line of height.
984 Fixing it here is 100x easier than re-designing textDisp.c */
985 if (w->text.columns < 1) {
986 w->text.columns = 1;
987 w->core.width = width = fs->max_bounds.width + marginWidth*2 +
988 lineNumAreaWidth;
990 if (w->text.rows < 1) {
991 w->text.rows = 1;
992 w->core.height = height = fs->ascent + fs->descent + marginHeight*2;
995 /* Resize the text display that the widget uses to render text */
996 TextDResize(w->text.textD, width - marginWidth*2 - lineNumAreaWidth,
997 height - marginHeight*2);
999 /* if the window became shorter or narrower, there may be text left
1000 in the bottom or right margin area, which must be cleaned up */
1001 if (XtIsRealized((Widget)w)) {
1002 XClearArea(XtDisplay(w), XtWindow(w), 0, height-marginHeight,
1003 width, marginHeight, False);
1004 XClearArea(XtDisplay(w), XtWindow(w),width-marginWidth,
1005 0, marginWidth, height, False);
1010 ** Widget redisplay method
1012 static void redisplay(TextWidget w, XEvent *event, Region region)
1014 XExposeEvent *e = &event->xexpose;
1016 TextDRedisplayRect(w->text.textD, e->x, e->y, e->width, e->height);
1019 static Bool findGraphicsExposeOrNoExposeEvent(Display *theDisplay, XEvent *event, XPointer arg)
1021 if ((theDisplay == event->xany.display) &&
1022 (event->type == GraphicsExpose || event->type == NoExpose) &&
1023 ((Widget)arg == XtWindowToWidget(event->xany.display, event->xany.window))) {
1024 return(True);
1026 else {
1027 return(False);
1031 static void adjustRectForGraphicsExposeOrNoExposeEvent(TextWidget w, XEvent *event,
1032 Boolean *first, int *left, int *top, int *width, int *height)
1034 Boolean removeQueueEntry = False;
1036 if (event->type == GraphicsExpose) {
1037 XGraphicsExposeEvent *e = &event->xgraphicsexpose;
1038 int x = e->x, y = e->y;
1040 TextDImposeGraphicsExposeTranslation(w->text.textD, &x, &y);
1041 if (*first) {
1042 *left = x;
1043 *top = y;
1044 *width = e->width;
1045 *height = e->height;
1047 *first = False;
1049 else {
1050 int prev_left = *left;
1051 int prev_top = *top;
1053 *left = min(*left, x);
1054 *top = min(*top, y);
1055 *width = max(prev_left + *width, x + e->width) - *left;
1056 *height = max(prev_top + *height, y + e->height) - *top;
1058 if (e->count == 0) {
1059 removeQueueEntry = True;
1062 else if (event->type == NoExpose) {
1063 removeQueueEntry = True;
1065 if (removeQueueEntry) {
1066 TextDPopGraphicExposeQueueEntry(w->text.textD);
1070 static void redisplayGE(TextWidget w, XtPointer client_data,
1071 XEvent *event, Boolean *continue_to_dispatch_return)
1073 if (event->type == GraphicsExpose || event->type == NoExpose) {
1074 HandleAllPendingGraphicsExposeNoExposeEvents(w, event);
1078 void HandleAllPendingGraphicsExposeNoExposeEvents(TextWidget w, XEvent *event)
1080 XEvent foundEvent;
1081 int left;
1082 int top;
1083 int width;
1084 int height;
1085 Boolean invalidRect = True;
1087 if (event) {
1088 adjustRectForGraphicsExposeOrNoExposeEvent(w, event, &invalidRect, &left, &top, &width, &height);
1090 while (XCheckIfEvent(XtDisplay(w), &foundEvent, findGraphicsExposeOrNoExposeEvent, (XPointer)w)) {
1091 adjustRectForGraphicsExposeOrNoExposeEvent(w, &foundEvent, &invalidRect, &left, &top, &width, &height);
1093 if (!invalidRect) {
1094 TextDRedisplayRect(w->text.textD, left, top, width, height);
1099 ** Widget setValues method
1101 static Boolean setValues(TextWidget current, TextWidget request,
1102 TextWidget new)
1104 Boolean redraw = False, reconfigure = False;
1106 if (new->text.overstrike != current->text.overstrike) {
1107 if (current->text.textD->cursorStyle == BLOCK_CURSOR)
1108 TextDSetCursorStyle(current->text.textD,
1109 current->text.heavyCursor ? HEAVY_CURSOR : NORMAL_CURSOR);
1110 else if (current->text.textD->cursorStyle == NORMAL_CURSOR ||
1111 current->text.textD->cursorStyle == HEAVY_CURSOR)
1112 TextDSetCursorStyle(current->text.textD, BLOCK_CURSOR);
1115 if (new->text.fontStruct != current->text.fontStruct) {
1116 if (new->text.lineNumCols != 0)
1117 reconfigure = True;
1118 TextDSetFont(current->text.textD, new->text.fontStruct);
1121 if (new->text.wrapMargin != current->text.wrapMargin ||
1122 new->text.continuousWrap != current->text.continuousWrap)
1123 TextDSetWrapMode(current->text.textD, new->text.continuousWrap,
1124 new->text.wrapMargin);
1126 /* When delimiters are changed, copy the memory, so that the caller
1127 doesn't have to manage it, and add mandatory delimiters blank,
1128 tab, and newline to the list */
1129 if (new->text.delimiters != current->text.delimiters) {
1130 char *delimiters = XtMalloc(strlen(new->text.delimiters) + 4);
1131 XtFree(current->text.delimiters);
1132 sprintf(delimiters, "%s%s", " \t\n", new->text.delimiters);
1133 new->text.delimiters = delimiters;
1136 /* Setting the lineNumCols resource tells the text widget to hide or
1137 show, or change the number of columns of the line number display,
1138 which requires re-organizing the x coordinates of both the line
1139 number display and the main text display */
1140 if (new->text.lineNumCols != current->text.lineNumCols || reconfigure)
1142 int marginWidth = new->text.marginWidth;
1143 int charWidth = new->text.fontStruct->max_bounds.width;
1144 int lineNumCols = new->text.lineNumCols;
1145 if (lineNumCols == 0)
1147 TextDSetLineNumberArea(new->text.textD, 0, 0, marginWidth);
1148 new->text.columns = (new->core.width - marginWidth*2) / charWidth;
1149 } else
1151 TextDSetLineNumberArea(new->text.textD, marginWidth,
1152 charWidth * lineNumCols,
1153 2*marginWidth + charWidth * lineNumCols);
1154 new->text.columns = (new->core.width - marginWidth*3 - charWidth
1155 * lineNumCols) / charWidth;
1159 if (new->text.backlightCharTypes != current->text.backlightCharTypes)
1161 TextDSetupBGClasses((Widget)new, new->text.backlightCharTypes,
1162 &new->text.textD->bgClassPixel, &new->text.textD->bgClass,
1163 new->text.textD->bgPixel);
1164 redraw = True;
1167 return redraw;
1171 ** Widget realize method
1173 static void realize(Widget w, XtValueMask *valueMask,
1174 XSetWindowAttributes *attributes)
1176 /* Set bit gravity window attribute. This saves a full blank and redraw
1177 on window resizing */
1178 *valueMask |= CWBitGravity;
1179 attributes->bit_gravity = NorthWestGravity;
1181 /* Continue with realize method from superclass */
1182 (xmPrimitiveClassRec.core_class.realize)(w, valueMask, attributes);
1186 ** Widget query geometry method ... unless asked to negotiate a different size simply return current size.
1188 static XtGeometryResult queryGeometry(Widget w, XtWidgetGeometry *proposed,
1189 XtWidgetGeometry *answer)
1191 TextWidget tw = (TextWidget)w;
1193 int curHeight = tw->core.height;
1194 int curWidth = tw->core.width;
1195 XFontStruct *fs = tw->text.textD->fontStruct;
1196 int fontWidth = fs->max_bounds.width;
1197 int fontHeight = fs->ascent + fs->descent;
1198 int marginHeight = tw->text.marginHeight;
1199 int propWidth = (proposed->request_mode & CWWidth) ? proposed->width : 0;
1200 int propHeight = (proposed->request_mode & CWHeight) ? proposed->height : 0;
1202 answer->request_mode = CWHeight | CWWidth;
1204 if(proposed->request_mode & CWWidth)
1205 /* Accept a width no smaller than 10 chars */
1206 answer->width = max(fontWidth * 10, proposed->width);
1207 else
1208 answer->width = curWidth;
1210 if(proposed->request_mode & CWHeight)
1211 /* Accept a height no smaller than an exact multiple of the line height
1212 and at least one line high */
1213 answer->height = max(1, ((propHeight - 2*marginHeight) / fontHeight)) *
1214 fontHeight + 2*marginHeight;
1215 else
1216 answer->height = curHeight;
1218 /*printf("propWidth %d, propHeight %d, ansWidth %d, ansHeight %d\n",
1219 propWidth, propHeight, answer->width, answer->height);*/
1220 if (propWidth == answer->width && propHeight == answer->height)
1221 return XtGeometryYes;
1222 else if (answer->width == curWidth && answer->height == curHeight)
1223 return XtGeometryNo;
1224 else
1225 return XtGeometryAlmost;
1229 ** Set the text buffer which this widget will display and interact with.
1230 ** The currently attached buffer is automatically freed, ONLY if it has
1231 ** no additional modify procs attached (as it would if it were being
1232 ** displayed by another text widget).
1234 void TextSetBuffer(Widget w, textBuffer *buffer)
1236 textBuffer *oldBuf = ((TextWidget)w)->text.textD->buffer;
1238 StopHandlingXSelections(w);
1239 TextDSetBuffer(((TextWidget)w)->text.textD, buffer);
1240 if (oldBuf->nModifyProcs == 0)
1241 BufFree(oldBuf);
1245 ** Get the buffer associated with this text widget. Note that attaching
1246 ** additional modify callbacks to the buffer will prevent it from being
1247 ** automatically freed when the widget is destroyed.
1249 textBuffer *TextGetBuffer(Widget w)
1251 return ((TextWidget)w)->text.textD->buffer;
1255 ** Translate a line number and column into a position
1257 int TextLineAndColToPos(Widget w, int lineNum, int column)
1259 return TextDLineAndColToPos(((TextWidget)w)->text.textD, lineNum, column );
1263 ** Translate a position into a line number (if the position is visible,
1264 ** if it's not, return False
1266 int TextPosToLineAndCol(Widget w, int pos, int *lineNum, int *column)
1268 return TextDPosToLineAndCol(((TextWidget)w)->text.textD, pos, lineNum,
1269 column);
1273 ** Translate a buffer text position to the XY location where the center
1274 ** of the cursor would be positioned to point to that character. Returns
1275 ** False if the position is not displayed because it is VERTICALLY out
1276 ** of view. If the position is horizontally out of view, returns the
1277 ** x coordinate where the position would be if it were visible.
1279 int TextPosToXY(Widget w, int pos, int *x, int *y)
1281 return TextDPositionToXY(((TextWidget)w)->text.textD, pos, x, y);
1285 ** Return the cursor position
1287 int TextGetCursorPos(Widget w)
1289 return TextDGetInsertPosition(((TextWidget)w)->text.textD);
1293 ** Set the cursor position
1295 void TextSetCursorPos(Widget w, int pos)
1297 TextDSetInsertPosition(((TextWidget)w)->text.textD, pos);
1298 checkAutoShowInsertPos(w);
1299 callCursorMovementCBs(w, NULL);
1304 ** Return the horizontal and vertical scroll positions of the widget
1306 void TextGetScroll(Widget w, int *topLineNum, int *horizOffset)
1308 TextDGetScroll(((TextWidget)w)->text.textD, topLineNum, horizOffset);
1312 ** Set the horizontal and vertical scroll positions of the widget
1314 void TextSetScroll(Widget w, int topLineNum, int horizOffset)
1316 TextDSetScroll(((TextWidget)w)->text.textD, topLineNum, horizOffset);
1319 int TextGetMinFontWidth(Widget w, Boolean considerStyles)
1321 return(TextDMinFontWidth(((TextWidget)w)->text.textD, considerStyles));
1324 int TextGetMaxFontWidth(Widget w, Boolean considerStyles)
1326 return(TextDMaxFontWidth(((TextWidget)w)->text.textD, considerStyles));
1330 ** Set this widget to be the owner of selections made in it's attached
1331 ** buffer (text buffers may be shared among several text widgets).
1333 void TextHandleXSelections(Widget w)
1335 HandleXSelections(w);
1338 void TextStopHandlingSelections(Widget w)
1340 StopHandlingXSelections(w);
1343 void TextPasteClipboard(Widget w, Time time)
1345 cancelDrag(w);
1346 if (checkReadOnly(w))
1347 return;
1348 TakeMotifDestination(w, time);
1349 InsertClipboard(w, False);
1350 callCursorMovementCBs(w, NULL);
1353 void TextColPasteClipboard(Widget w, Time time)
1355 cancelDrag(w);
1356 if (checkReadOnly(w))
1357 return;
1358 TakeMotifDestination(w, time);
1359 InsertClipboard(w, True);
1360 callCursorMovementCBs(w, NULL);
1363 void TextCopyClipboard(Widget w, Time time)
1365 cancelDrag(w);
1366 if (!((TextWidget)w)->text.textD->buffer->primary.selected) {
1367 XBell(XtDisplay(w), 0);
1368 return;
1370 CopyToClipboard(w, time);
1373 void TextCutClipboard(Widget w, Time time)
1375 textDisp *textD = ((TextWidget)w)->text.textD;
1377 cancelDrag(w);
1378 if (checkReadOnly(w))
1379 return;
1380 if (!textD->buffer->primary.selected) {
1381 XBell(XtDisplay(w), 0);
1382 return;
1384 TakeMotifDestination(w, time);
1385 CopyToClipboard (w, time);
1386 BufRemoveSelected(textD->buffer);
1387 TextDSetInsertPosition(textD, textD->buffer->cursorPosHint);
1388 checkAutoShowInsertPos(w);
1391 int TextFirstVisibleLine(Widget w)
1393 return(((TextWidget)w)->text.textD->topLineNum);
1396 int TextNumVisibleLines(Widget w)
1398 return(((TextWidget)w)->text.textD->nVisibleLines);
1401 int TextVisibleWidth(Widget w)
1403 return(((TextWidget)w)->text.textD->width);
1406 int TextFirstVisiblePos(Widget w)
1408 return ((TextWidget)w)->text.textD->firstChar;
1411 int TextLastVisiblePos(Widget w)
1413 return ((TextWidget)w)->text.textD->lastChar;
1417 ** Insert text "chars" at the cursor position, respecting pending delete
1418 ** selections, overstrike, and handling cursor repositioning as if the text
1419 ** had been typed. If autoWrap is on wraps the text to fit within the wrap
1420 ** margin, auto-indenting where the line was wrapped (but nowhere else).
1421 ** "allowPendingDelete" controls whether primary selections in the widget are
1422 ** treated as pending delete selections (True), or ignored (False). "event"
1423 ** is optional and is just passed on to the cursor movement callbacks.
1425 void TextInsertAtCursor(Widget w, char *chars, XEvent *event,
1426 int allowPendingDelete, int allowWrap)
1428 int wrapMargin, colNum, lineStartPos, cursorPos;
1429 char *c, *lineStartText, *wrappedText;
1430 TextWidget tw = (TextWidget)w;
1431 textDisp *textD = tw->text.textD;
1432 textBuffer *buf = textD->buffer;
1433 int fontWidth = textD->fontStruct->max_bounds.width;
1434 int replaceSel, singleLine, breakAt = 0;
1436 /* Don't wrap if auto-wrap is off or suppressed, or it's just a newline */
1437 if (!allowWrap || !tw->text.autoWrap ||
1438 (chars[0] == '\n' && chars[1] == '\0')) {
1439 simpleInsertAtCursor(w, chars, event, allowPendingDelete);
1440 return;
1443 /* If this is going to be a pending delete operation, the real insert
1444 position is the start of the selection. This will make rectangular
1445 selections wrap strangely, but this routine should rarely be used for
1446 them, and even more rarely when they need to be wrapped. */
1447 replaceSel = allowPendingDelete && pendingSelection(w);
1448 cursorPos = replaceSel ? buf->primary.start : TextDGetInsertPosition(textD);
1450 /* If the text is only one line and doesn't need to be wrapped, just insert
1451 it and be done (for efficiency only, this routine is called for each
1452 character typed). (Of course, it may not be significantly more efficient
1453 than the more general code below it, so it may be a waste of time!) */
1454 wrapMargin = tw->text.wrapMargin != 0 ? tw->text.wrapMargin :
1455 textD->width / fontWidth;
1456 lineStartPos = BufStartOfLine(buf, cursorPos);
1457 colNum = BufCountDispChars(buf, lineStartPos, cursorPos);
1458 for (c=chars; *c!='\0' && *c!='\n'; c++)
1459 colNum += BufCharWidth(*c, colNum, buf->tabDist, buf->nullSubsChar);
1460 singleLine = *c == '\0';
1461 if (colNum < wrapMargin && singleLine) {
1462 simpleInsertAtCursor(w, chars, event, True);
1463 return;
1466 /* Wrap the text */
1467 lineStartText = BufGetRange(buf, lineStartPos, cursorPos);
1468 wrappedText = wrapText(tw, lineStartText, chars, lineStartPos, wrapMargin,
1469 replaceSel ? NULL : &breakAt);
1470 XtFree(lineStartText);
1472 /* Insert the text. Where possible, use TextDInsert which is optimized
1473 for less redraw. */
1474 if (replaceSel) {
1475 BufReplaceSelected(buf, wrappedText);
1476 TextDSetInsertPosition(textD, buf->cursorPosHint);
1477 } else if (tw->text.overstrike) {
1478 if (breakAt == 0 && singleLine)
1479 TextDOverstrike(textD, wrappedText);
1480 else {
1481 BufReplace(buf, cursorPos-breakAt, cursorPos, wrappedText);
1482 TextDSetInsertPosition(textD, buf->cursorPosHint);
1484 } else {
1485 if (breakAt == 0) {
1486 TextDInsert(textD, wrappedText);
1487 } else {
1488 BufReplace(buf, cursorPos-breakAt, cursorPos, wrappedText);
1489 TextDSetInsertPosition(textD, buf->cursorPosHint);
1492 XtFree(wrappedText);
1493 checkAutoShowInsertPos(w);
1494 callCursorMovementCBs(w, event);
1498 ** Fetch text from the widget's buffer, adding wrapping newlines to emulate
1499 ** effect acheived by wrapping in the text display in continuous wrap mode.
1501 char *TextGetWrapped(Widget w, int startPos, int endPos, int *outLen)
1503 textDisp *textD = ((TextWidget)w)->text.textD;
1504 textBuffer *buf = textD->buffer;
1505 textBuffer *outBuf;
1506 int fromPos, toPos, outPos;
1507 char c, *outString;
1509 if (!((TextWidget)w)->text.continuousWrap || startPos == endPos) {
1510 *outLen = endPos - startPos;
1511 return BufGetRange(buf, startPos, endPos);
1514 /* Create a text buffer with a good estimate of the size that adding
1515 newlines will expand it to. Since it's a text buffer, if we guess
1516 wrong, it will fail softly, and simply expand the size */
1517 outBuf = BufCreatePreallocated((endPos-startPos) + (endPos-startPos)/5);
1518 outPos = 0;
1520 /* Go (displayed) line by line through the buffer, adding newlines where
1521 the text is wrapped at some character other than an existing newline */
1522 fromPos = startPos;
1523 toPos = TextDCountForwardNLines(textD, startPos, 1, False);
1524 while (toPos < endPos) {
1525 BufCopyFromBuf(buf, outBuf, fromPos, toPos, outPos);
1526 outPos += toPos - fromPos;
1527 c = BufGetCharacter(outBuf, outPos-1);
1528 if (c == ' ' || c == '\t')
1529 BufReplace(outBuf, outPos-1, outPos, "\n");
1530 else if (c != '\n') {
1531 BufInsert(outBuf, outPos, "\n");
1532 outPos++;
1534 fromPos = toPos;
1535 toPos = TextDCountForwardNLines(textD, fromPos, 1, True);
1537 BufCopyFromBuf(buf, outBuf, fromPos, endPos, outPos);
1539 /* return the contents of the output buffer as a string */
1540 outString = BufGetAll(outBuf);
1541 *outLen = outBuf->length;
1542 BufFree(outBuf);
1543 return outString;
1547 ** Return the (statically allocated) action table for menu item actions.
1549 ** Warning: This routine can only be used before the first text widget is
1550 ** created! After that, apparently, Xt takes over the table and overwrites
1551 ** it with its own version. XtGetActionList is preferable, but is not
1552 ** available before X11R5.
1554 XtActionsRec *TextGetActions(int *nActions)
1556 *nActions = XtNumber(actionsList);
1557 return actionsList;
1560 static void grabFocusAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
1562 XButtonEvent *e = &event->xbutton;
1563 TextWidget tw = (TextWidget)w;
1564 textDisp *textD = tw->text.textD;
1565 Time lastBtnDown = tw->text.lastBtnDown;
1566 int row, column;
1568 /* Indicate state for future events, PRIMARY_CLICKED indicates that
1569 the proper initialization has been done for primary dragging and/or
1570 multi-clicking. Also record the timestamp for multi-click processing */
1571 tw->text.dragState = PRIMARY_CLICKED;
1572 tw->text.lastBtnDown = e->time;
1574 /* Become owner of the MOTIF_DESTINATION selection, making this widget
1575 the designated recipient of secondary quick actions in Motif XmText
1576 widgets and in other NEdit text widgets */
1577 TakeMotifDestination(w, e->time);
1579 /* Check for possible multi-click sequence in progress */
1580 if (tw->text.multiClickState != NO_CLICKS) {
1581 if (e->time < lastBtnDown + XtGetMultiClickTime(XtDisplay(w))) {
1582 if (tw->text.multiClickState == ONE_CLICK) {
1583 selectWord(w, e->x);
1584 callCursorMovementCBs(w, event);
1585 return;
1586 } else if (tw->text.multiClickState == TWO_CLICKS) {
1587 selectLine(w);
1588 callCursorMovementCBs(w, event);
1589 return;
1590 } else if (tw->text.multiClickState == THREE_CLICKS) {
1591 BufSelect(textD->buffer, 0, textD->buffer->length);
1592 return;
1593 } else if (tw->text.multiClickState > THREE_CLICKS)
1594 tw->text.multiClickState = NO_CLICKS;
1595 } else
1596 tw->text.multiClickState = NO_CLICKS;
1599 /* Clear any existing selections */
1600 BufUnselect(textD->buffer);
1602 /* Move the cursor to the pointer location */
1603 moveDestinationAP(w, event, args, nArgs);
1605 /* Record the site of the initial button press and the initial character
1606 position so subsequent motion events and clicking can decide when and
1607 where to begin a primary selection */
1608 tw->text.btnDownX = e->x;
1609 tw->text.btnDownY = e->y;
1610 tw->text.anchor = TextDGetInsertPosition(textD);
1611 TextDXYToUnconstrainedPosition(textD, e->x, e->y, &row, &column);
1612 column = TextDOffsetWrappedColumn(textD, row, column);
1613 tw->text.rectAnchor = column;
1616 static void moveDestinationAP(Widget w, XEvent *event, String *args,
1617 Cardinal *nArgs)
1619 XButtonEvent *e = &event->xbutton;
1620 textDisp *textD = ((TextWidget)w)->text.textD;
1622 /* Get input focus */
1623 XmProcessTraversal(w, XmTRAVERSE_CURRENT);
1625 /* Move the cursor */
1626 TextDSetInsertPosition(textD, TextDXYToPosition(textD, e->x, e->y));
1627 checkAutoShowInsertPos(w);
1628 callCursorMovementCBs(w, event);
1631 static void extendAdjustAP(Widget w, XEvent *event, String *args,
1632 Cardinal *nArgs)
1634 TextWidget tw = (TextWidget)w;
1635 XMotionEvent *e = &event->xmotion;
1636 int dragState = tw->text.dragState;
1637 int rectDrag = hasKey("rect", args, nArgs);
1639 /* Make sure the proper initialization was done on mouse down */
1640 if (dragState != PRIMARY_DRAG && dragState != PRIMARY_CLICKED &&
1641 dragState != PRIMARY_RECT_DRAG)
1642 return;
1644 /* If the selection hasn't begun, decide whether the mouse has moved
1645 far enough from the initial mouse down to be considered a drag */
1646 if (tw->text.dragState == PRIMARY_CLICKED) {
1647 if (abs(e->x - tw->text.btnDownX) > SELECT_THRESHOLD ||
1648 abs(e->y - tw->text.btnDownY) > SELECT_THRESHOLD)
1649 tw->text.dragState = rectDrag ? PRIMARY_RECT_DRAG : PRIMARY_DRAG;
1650 else
1651 return;
1654 /* If "rect" argument has appeared or disappeared, keep dragState up
1655 to date about which type of drag this is */
1656 tw->text.dragState = rectDrag ? PRIMARY_RECT_DRAG : PRIMARY_DRAG;
1658 /* Record the new position for the autoscrolling timer routine, and
1659 engage or disengage the timer if the mouse is in/out of the window */
1660 checkAutoScroll(tw, e->x, e->y);
1662 /* Adjust the selection and move the cursor */
1663 adjustSelection(tw, e->x, e->y);
1666 static void extendStartAP(Widget w, XEvent *event, String *args,
1667 Cardinal *nArgs)
1669 XMotionEvent *e = &event->xmotion;
1670 textDisp *textD = ((TextWidget)w)->text.textD;
1671 textBuffer *buf = textD->buffer;
1672 selection *sel = &buf->primary;
1673 int anchor, rectAnchor, anchorLineStart, newPos, row, column;
1675 /* Find the new anchor point for the rest of this drag operation */
1676 newPos = TextDXYToPosition(textD, e->x, e->y);
1677 TextDXYToUnconstrainedPosition(textD, e->x, e->y, &row, &column);
1678 column = TextDOffsetWrappedColumn(textD, row, column);
1679 if (sel->selected) {
1680 if (sel->rectangular) {
1681 rectAnchor = column < (sel->rectEnd + sel->rectStart) / 2 ?
1682 sel->rectEnd : sel->rectStart;
1683 anchorLineStart = BufStartOfLine(buf, newPos <
1684 (sel->end + sel->start) / 2 ? sel->end : sel->start);
1685 anchor = BufCountForwardDispChars(buf, anchorLineStart, rectAnchor);
1686 } else {
1687 if (abs(newPos - sel->start) < abs(newPos - sel->end))
1688 anchor = sel->end;
1689 else
1690 anchor = sel->start;
1691 anchorLineStart = BufStartOfLine(buf, anchor);
1692 rectAnchor = BufCountDispChars(buf, anchorLineStart, anchor);
1694 } else {
1695 anchor = TextDGetInsertPosition(textD);
1696 anchorLineStart = BufStartOfLine(buf, anchor);
1697 rectAnchor = BufCountDispChars(buf, anchorLineStart, anchor);
1699 ((TextWidget)w)->text.anchor = anchor;
1700 ((TextWidget)w)->text.rectAnchor = rectAnchor;
1702 /* Make the new selection */
1703 if (hasKey("rect", args, nArgs))
1704 BufRectSelect(buf, BufStartOfLine(buf, min(anchor, newPos)),
1705 BufEndOfLine(buf, max(anchor, newPos)),
1706 min(rectAnchor, column), max(rectAnchor, column));
1707 else
1708 BufSelect(buf, min(anchor, newPos), max(anchor, newPos));
1710 /* Never mind the motion threshold, go right to dragging since
1711 extend-start is unambiguously the start of a selection */
1712 ((TextWidget)w)->text.dragState = PRIMARY_DRAG;
1714 /* Don't do by-word or by-line adjustment, just by character */
1715 ((TextWidget)w)->text.multiClickState = NO_CLICKS;
1717 /* Move the cursor */
1718 TextDSetInsertPosition(textD, newPos);
1719 callCursorMovementCBs(w, event);
1722 static void extendEndAP(Widget w, XEvent *event, String *args,
1723 Cardinal *nArgs)
1725 XButtonEvent *e = &event->xbutton;
1726 TextWidget tw = (TextWidget)w;
1728 if (tw->text.dragState == PRIMARY_CLICKED &&
1729 tw->text.lastBtnDown <= e->time + XtGetMultiClickTime(XtDisplay(w)))
1730 tw->text.multiClickState++;
1731 endDrag(w);
1734 static void processCancelAP(Widget w, XEvent *event, String *args,
1735 Cardinal *nArgs)
1737 int dragState = ((TextWidget)w)->text.dragState;
1738 textBuffer *buf = ((TextWidget)w)->text.textD->buffer;
1739 textDisp *textD = ((TextWidget)w)->text.textD;
1741 /* If there's a calltip displayed, kill it. */
1742 TextDKillCalltip(textD, 0);
1744 if (dragState == PRIMARY_DRAG || dragState == PRIMARY_RECT_DRAG)
1745 BufUnselect(buf);
1746 cancelDrag(w);
1749 static void secondaryStartAP(Widget w, XEvent *event, String *args,
1750 Cardinal *nArgs)
1752 XMotionEvent *e = &event->xmotion;
1753 textDisp *textD = ((TextWidget)w)->text.textD;
1754 textBuffer *buf = textD->buffer;
1755 selection *sel = &buf->secondary;
1756 int anchor, pos, row, column;
1758 /* Find the new anchor point and make the new selection */
1759 pos = TextDXYToPosition(textD, e->x, e->y);
1760 if (sel->selected) {
1761 if (abs(pos - sel->start) < abs(pos - sel->end))
1762 anchor = sel->end;
1763 else
1764 anchor = sel->start;
1765 BufSecondarySelect(buf, anchor, pos);
1766 } else
1767 anchor = pos;
1769 /* Record the site of the initial button press and the initial character
1770 position so subsequent motion events can decide when to begin a
1771 selection, (and where the selection began) */
1772 ((TextWidget)w)->text.btnDownX = e->x;
1773 ((TextWidget)w)->text.btnDownY = e->y;
1774 ((TextWidget)w)->text.anchor = pos;
1775 TextDXYToUnconstrainedPosition(textD, e->x, e->y, &row, &column);
1776 column = TextDOffsetWrappedColumn(textD, row, column);
1777 ((TextWidget)w)->text.rectAnchor = column;
1778 ((TextWidget)w)->text.dragState = SECONDARY_CLICKED;
1781 static void secondaryOrDragStartAP(Widget w, XEvent *event, String *args,
1782 Cardinal *nArgs)
1784 XMotionEvent *e = &event->xmotion;
1785 textDisp *textD = ((TextWidget)w)->text.textD;
1786 textBuffer *buf = textD->buffer;
1788 /* If the click was outside of the primary selection, this is not
1789 a drag, start a secondary selection */
1790 if (!buf->primary.selected || !TextDInSelection(textD, e->x, e->y)) {
1791 secondaryStartAP(w, event, args, nArgs);
1792 return;
1795 if (checkReadOnly(w))
1796 return;
1798 /* Record the site of the initial button press and the initial character
1799 position so subsequent motion events can decide when to begin a
1800 drag, and where to drag to */
1801 ((TextWidget)w)->text.btnDownX = e->x;
1802 ((TextWidget)w)->text.btnDownY = e->y;
1803 ((TextWidget)w)->text.dragState = CLICKED_IN_SELECTION;
1806 static void secondaryAdjustAP(Widget w, XEvent *event, String *args,
1807 Cardinal *nArgs)
1809 TextWidget tw = (TextWidget)w;
1810 XMotionEvent *e = &event->xmotion;
1811 int dragState = tw->text.dragState;
1812 int rectDrag = hasKey("rect", args, nArgs);
1814 /* Make sure the proper initialization was done on mouse down */
1815 if (dragState != SECONDARY_DRAG && dragState != SECONDARY_RECT_DRAG &&
1816 dragState != SECONDARY_CLICKED)
1817 return;
1819 /* If the selection hasn't begun, decide whether the mouse has moved
1820 far enough from the initial mouse down to be considered a drag */
1821 if (tw->text.dragState == SECONDARY_CLICKED) {
1822 if (abs(e->x - tw->text.btnDownX) > SELECT_THRESHOLD ||
1823 abs(e->y - tw->text.btnDownY) > SELECT_THRESHOLD)
1824 tw->text.dragState = rectDrag ? SECONDARY_RECT_DRAG: SECONDARY_DRAG;
1825 else
1826 return;
1829 /* If "rect" argument has appeared or disappeared, keep dragState up
1830 to date about which type of drag this is */
1831 tw->text.dragState = rectDrag ? SECONDARY_RECT_DRAG : SECONDARY_DRAG;
1833 /* Record the new position for the autoscrolling timer routine, and
1834 engage or disengage the timer if the mouse is in/out of the window */
1835 checkAutoScroll(tw, e->x, e->y);
1837 /* Adjust the selection */
1838 adjustSecondarySelection(tw, e->x, e->y);
1841 static void secondaryOrDragAdjustAP(Widget w, XEvent *event, String *args,
1842 Cardinal *nArgs)
1844 TextWidget tw = (TextWidget)w;
1845 XMotionEvent *e = &event->xmotion;
1846 int dragState = tw->text.dragState;
1848 /* Only dragging of blocks of text is handled in this action proc.
1849 Otherwise, defer to secondaryAdjust to handle the rest */
1850 if (dragState != CLICKED_IN_SELECTION && dragState != PRIMARY_BLOCK_DRAG) {
1851 secondaryAdjustAP(w, event, args, nArgs);
1852 return;
1855 /* Decide whether the mouse has moved far enough from the
1856 initial mouse down to be considered a drag */
1857 if (tw->text.dragState == CLICKED_IN_SELECTION) {
1858 if (abs(e->x - tw->text.btnDownX) > SELECT_THRESHOLD ||
1859 abs(e->y - tw->text.btnDownY) > SELECT_THRESHOLD)
1860 BeginBlockDrag(tw);
1861 else
1862 return;
1865 /* Record the new position for the autoscrolling timer routine, and
1866 engage or disengage the timer if the mouse is in/out of the window */
1867 checkAutoScroll(tw, e->x, e->y);
1869 /* Adjust the selection */
1870 BlockDragSelection(tw, e->x, e->y, hasKey("overlay", args, nArgs) ?
1871 (hasKey("copy", args, nArgs) ? DRAG_OVERLAY_COPY : DRAG_OVERLAY_MOVE) :
1872 (hasKey("copy", args, nArgs) ? DRAG_COPY : DRAG_MOVE));
1875 static void copyToAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
1877 XButtonEvent *e = &event->xbutton;
1878 TextWidget tw = (TextWidget)w;
1879 textDisp *textD = tw->text.textD;
1880 int dragState = tw->text.dragState;
1881 textBuffer *buf = textD->buffer;
1882 selection *secondary = &buf->secondary, *primary = &buf->primary;
1883 int rectangular = secondary->rectangular;
1884 char *textToCopy;
1885 int insertPos, lineStart, column;
1887 endDrag(w);
1888 if (!((dragState == SECONDARY_DRAG && secondary->selected) ||
1889 (dragState == SECONDARY_RECT_DRAG && secondary->selected) ||
1890 dragState == SECONDARY_CLICKED || dragState == NOT_CLICKED))
1891 return;
1892 if (!(secondary->selected && !((TextWidget)w)->text.motifDestOwner)) {
1893 if (checkReadOnly(w)) {
1894 BufSecondaryUnselect(buf);
1895 return;
1898 if (secondary->selected) {
1899 if (tw->text.motifDestOwner) {
1900 TextDBlankCursor(textD);
1901 textToCopy = BufGetSecSelectText(buf);
1902 if (primary->selected && rectangular) {
1903 insertPos = TextDGetInsertPosition(textD);
1904 BufReplaceSelected(buf, textToCopy);
1905 TextDSetInsertPosition(textD, buf->cursorPosHint);
1906 } else if (rectangular) {
1907 insertPos = TextDGetInsertPosition(textD);
1908 lineStart = BufStartOfLine(buf, insertPos);
1909 column = BufCountDispChars(buf, lineStart, insertPos);
1910 BufInsertCol(buf, column, lineStart, textToCopy, NULL, NULL);
1911 TextDSetInsertPosition(textD, buf->cursorPosHint);
1912 } else
1913 TextInsertAtCursor(w, textToCopy, event, True,
1914 tw->text.autoWrapPastedText);
1915 XtFree(textToCopy);
1916 BufSecondaryUnselect(buf);
1917 TextDUnblankCursor(textD);
1918 } else
1919 SendSecondarySelection(w, e->time, False);
1920 } else if (primary->selected) {
1921 textToCopy = BufGetSelectionText(buf);
1922 TextDSetInsertPosition(textD, TextDXYToPosition(textD, e->x, e->y));
1923 TextInsertAtCursor(w, textToCopy, event, False,
1924 tw->text.autoWrapPastedText);
1925 XtFree(textToCopy);
1926 } else {
1927 TextDSetInsertPosition(textD, TextDXYToPosition(textD, e->x, e->y));
1928 InsertPrimarySelection(w, e->time, False);
1932 static void copyToOrEndDragAP(Widget w, XEvent *event, String *args,
1933 Cardinal *nArgs)
1935 int dragState = ((TextWidget)w)->text.dragState;
1937 if (dragState != PRIMARY_BLOCK_DRAG) {
1938 copyToAP(w, event, args, nArgs);
1939 return;
1942 FinishBlockDrag((TextWidget)w);
1945 static void moveToAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
1947 XButtonEvent *e = &event->xbutton;
1948 textDisp *textD = ((TextWidget)w)->text.textD;
1949 int dragState = ((TextWidget)w)->text.dragState;
1950 textBuffer *buf = textD->buffer;
1951 selection *secondary = &buf->secondary, *primary = &buf->primary;
1952 int insertPos, rectangular = secondary->rectangular;
1953 int column, lineStart;
1954 char *textToCopy;
1956 endDrag(w);
1957 if (!((dragState == SECONDARY_DRAG && secondary->selected) ||
1958 (dragState == SECONDARY_RECT_DRAG && secondary->selected) ||
1959 dragState == SECONDARY_CLICKED || dragState == NOT_CLICKED))
1960 return;
1961 if (checkReadOnly(w)) {
1962 BufSecondaryUnselect(buf);
1963 return;
1966 if (secondary->selected) {
1967 if (((TextWidget)w)->text.motifDestOwner) {
1968 textToCopy = BufGetSecSelectText(buf);
1969 if (primary->selected && rectangular) {
1970 insertPos = TextDGetInsertPosition(textD);
1971 BufReplaceSelected(buf, textToCopy);
1972 TextDSetInsertPosition(textD, buf->cursorPosHint);
1973 } else if (rectangular) {
1974 insertPos = TextDGetInsertPosition(textD);
1975 lineStart = BufStartOfLine(buf, insertPos);
1976 column = BufCountDispChars(buf, lineStart, insertPos);
1977 BufInsertCol(buf, column, lineStart, textToCopy, NULL, NULL);
1978 TextDSetInsertPosition(textD, buf->cursorPosHint);
1979 } else
1980 TextInsertAtCursor(w, textToCopy, event, True,
1981 ((TextWidget)w)->text.autoWrapPastedText);
1982 XtFree(textToCopy);
1983 BufRemoveSecSelect(buf);
1984 BufSecondaryUnselect(buf);
1985 } else
1986 SendSecondarySelection(w, e->time, True);
1987 } else if (primary->selected) {
1988 textToCopy = BufGetRange(buf, primary->start, primary->end);
1989 TextDSetInsertPosition(textD, TextDXYToPosition(textD, e->x, e->y));
1990 TextInsertAtCursor(w, textToCopy, event, False,
1991 ((TextWidget)w)->text.autoWrapPastedText);
1992 XtFree(textToCopy);
1993 BufRemoveSelected(buf);
1994 BufUnselect(buf);
1995 } else {
1996 TextDSetInsertPosition(textD, TextDXYToPosition(textD, e->x, e->y));
1997 MovePrimarySelection(w, e->time, False);
2001 static void moveToOrEndDragAP(Widget w, XEvent *event, String *args,
2002 Cardinal *nArgs)
2004 int dragState = ((TextWidget)w)->text.dragState;
2006 if (dragState != PRIMARY_BLOCK_DRAG) {
2007 moveToAP(w, event, args, nArgs);
2008 return;
2011 FinishBlockDrag((TextWidget)w);
2014 static void endDragAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2016 if (((TextWidget)w)->text.dragState == PRIMARY_BLOCK_DRAG)
2017 FinishBlockDrag((TextWidget)w);
2018 else
2019 endDrag(w);
2022 static void exchangeAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2024 XButtonEvent *e = &event->xbutton;
2025 textDisp *textD = ((TextWidget)w)->text.textD;
2026 textBuffer *buf = textD->buffer;
2027 selection *sec = &buf->secondary, *primary = &buf->primary;
2028 char *primaryText, *secText;
2029 int newPrimaryStart, newPrimaryEnd, secWasRect;
2030 int dragState = ((TextWidget)w)->text.dragState; /* save before endDrag */
2031 int silent = hasKey("nobell", args, nArgs);
2033 endDrag(w);
2034 if (checkReadOnly(w))
2035 return;
2037 /* If there's no secondary selection here, or the primary and secondary
2038 selection overlap, just beep and return */
2039 if (!sec->selected || (primary->selected &&
2040 ((primary->start <= sec->start && primary->end > sec->start) ||
2041 (sec->start <= primary->start && sec->end > primary->start))))
2043 BufSecondaryUnselect(buf);
2044 ringIfNecessary(silent, w);
2045 /* If there's no secondary selection, but the primary selection is
2046 being dragged, we must not forget to finish the dragging.
2047 Otherwise, modifications aren't recorded. */
2048 if (dragState == PRIMARY_BLOCK_DRAG)
2049 FinishBlockDrag((TextWidget)w);
2050 return;
2053 /* if the primary selection is in another widget, use selection routines */
2054 if (!primary->selected) {
2055 ExchangeSelections(w, e->time);
2056 return;
2059 /* Both primary and secondary are in this widget, do the exchange here */
2060 primaryText = BufGetSelectionText(buf);
2061 secText = BufGetSecSelectText(buf);
2062 secWasRect = sec->rectangular;
2063 BufReplaceSecSelect(buf, primaryText);
2064 newPrimaryStart = primary->start;
2065 BufReplaceSelected(buf, secText);
2066 newPrimaryEnd = newPrimaryStart + strlen(secText);
2067 XtFree(primaryText);
2068 XtFree(secText);
2069 BufSecondaryUnselect(buf);
2070 if (secWasRect) {
2071 TextDSetInsertPosition(textD, buf->cursorPosHint);
2072 } else {
2073 BufSelect(buf, newPrimaryStart, newPrimaryEnd);
2074 TextDSetInsertPosition(textD, newPrimaryEnd);
2076 checkAutoShowInsertPos(w);
2079 static void copyPrimaryAP(Widget w, XEvent *event, String *args,
2080 Cardinal *nArgs)
2082 XKeyEvent *e = &event->xkey;
2083 TextWidget tw = (TextWidget)w;
2084 textDisp *textD = tw->text.textD;
2085 textBuffer *buf = textD->buffer;
2086 selection *primary = &buf->primary;
2087 int rectangular = hasKey("rect", args, nArgs);
2088 char *textToCopy;
2089 int insertPos, col;
2091 cancelDrag(w);
2092 if (checkReadOnly(w))
2093 return;
2094 if (primary->selected && rectangular) {
2095 textToCopy = BufGetSelectionText(buf);
2096 insertPos = TextDGetInsertPosition(textD);
2097 col = BufCountDispChars(buf, BufStartOfLine(buf, insertPos), insertPos);
2098 BufInsertCol(buf, col, insertPos, textToCopy, NULL, NULL);
2099 TextDSetInsertPosition(textD, buf->cursorPosHint);
2100 XtFree(textToCopy);
2101 checkAutoShowInsertPos(w);
2102 } else if (primary->selected) {
2103 textToCopy = BufGetSelectionText(buf);
2104 insertPos = TextDGetInsertPosition(textD);
2105 BufInsert(buf, insertPos, textToCopy);
2106 TextDSetInsertPosition(textD, insertPos + strlen(textToCopy));
2107 XtFree(textToCopy);
2108 checkAutoShowInsertPos(w);
2109 } else if (rectangular) {
2110 if (!TextDPositionToXY(textD, TextDGetInsertPosition(textD),
2111 &tw->text.btnDownX, &tw->text.btnDownY))
2112 return; /* shouldn't happen */
2113 InsertPrimarySelection(w, e->time, True);
2114 } else
2115 InsertPrimarySelection(w, e->time, False);
2118 static void cutPrimaryAP(Widget w, XEvent *event, String *args,
2119 Cardinal *nArgs)
2121 XKeyEvent *e = &event->xkey;
2122 textDisp *textD = ((TextWidget)w)->text.textD;
2123 textBuffer *buf = textD->buffer;
2124 selection *primary = &buf->primary;
2125 char *textToCopy;
2126 int rectangular = hasKey("rect", args, nArgs);
2127 int insertPos, col;
2129 cancelDrag(w);
2130 if (checkReadOnly(w))
2131 return;
2132 if (primary->selected && rectangular) {
2133 textToCopy = BufGetSelectionText(buf);
2134 insertPos = TextDGetInsertPosition(textD);
2135 col = BufCountDispChars(buf, BufStartOfLine(buf, insertPos), insertPos);
2136 BufInsertCol(buf, col, insertPos, textToCopy, NULL, NULL);
2137 TextDSetInsertPosition(textD, buf->cursorPosHint);
2138 XtFree(textToCopy);
2139 BufRemoveSelected(buf);
2140 checkAutoShowInsertPos(w);
2141 } else if (primary->selected) {
2142 textToCopy = BufGetSelectionText(buf);
2143 insertPos = TextDGetInsertPosition(textD);
2144 BufInsert(buf, insertPos, textToCopy);
2145 TextDSetInsertPosition(textD, insertPos + strlen(textToCopy));
2146 XtFree(textToCopy);
2147 BufRemoveSelected(buf);
2148 checkAutoShowInsertPos(w);
2149 } else if (rectangular) {
2150 if (!TextDPositionToXY(textD, TextDGetInsertPosition(textD),
2151 &((TextWidget)w)->text.btnDownX,
2152 &((TextWidget)w)->text.btnDownY))
2153 return; /* shouldn't happen */
2154 MovePrimarySelection(w, e->time, True);
2155 } else {
2156 MovePrimarySelection(w, e->time, False);
2160 static void mousePanAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2162 XButtonEvent *e = &event->xbutton;
2163 TextWidget tw = (TextWidget)w;
2164 textDisp *textD = tw->text.textD;
2165 int lineHeight = textD->ascent + textD->descent;
2166 int topLineNum, horizOffset;
2167 static Cursor panCursor = 0;
2169 if (tw->text.dragState == MOUSE_PAN) {
2170 TextDSetScroll(textD,
2171 (tw->text.btnDownY - e->y + lineHeight/2) / lineHeight,
2172 tw->text.btnDownX - e->x);
2173 } else if (tw->text.dragState == NOT_CLICKED) {
2174 TextDGetScroll(textD, &topLineNum, &horizOffset);
2175 tw->text.btnDownX = e->x + horizOffset;
2176 tw->text.btnDownY = e->y + topLineNum * lineHeight;
2177 tw->text.dragState = MOUSE_PAN;
2178 if (!panCursor)
2179 panCursor = XCreateFontCursor(XtDisplay(w), XC_fleur);
2180 XGrabPointer(XtDisplay(w), XtWindow(w), False,
2181 ButtonMotionMask | ButtonReleaseMask, GrabModeAsync,
2182 GrabModeAsync, None, panCursor, CurrentTime);
2183 } else
2184 cancelDrag(w);
2187 static void pasteClipboardAP(Widget w, XEvent *event, String *args,
2188 Cardinal *nArgs)
2190 if (hasKey("rect", args, nArgs))
2191 TextColPasteClipboard(w, event->xkey.time);
2192 else
2193 TextPasteClipboard(w, event->xkey.time);
2196 static void copyClipboardAP(Widget w, XEvent *event, String *args,
2197 Cardinal *nArgs)
2199 TextCopyClipboard(w, event->xkey.time);
2202 static void cutClipboardAP(Widget w, XEvent *event, String *args,
2203 Cardinal *nArgs)
2205 TextCutClipboard(w, event->xkey.time);
2208 static void insertStringAP(Widget w, XEvent *event, String *args,
2209 Cardinal *nArgs)
2211 smartIndentCBStruct smartIndent;
2212 textDisp *textD = ((TextWidget)w)->text.textD;
2214 if (*nArgs == 0)
2215 return;
2216 cancelDrag(w);
2217 if (checkReadOnly(w))
2218 return;
2219 if (((TextWidget)w)->text.smartIndent) {
2220 smartIndent.reason = CHAR_TYPED;
2221 smartIndent.pos = TextDGetInsertPosition(textD);
2222 smartIndent.indentRequest = 0;
2223 smartIndent.charsTyped = args[0];
2224 XtCallCallbacks(w, textNsmartIndentCallback, (XtPointer)&smartIndent);
2226 TextInsertAtCursor(w, args[0], event, True, True);
2227 BufUnselect((((TextWidget)w)->text.textD)->buffer);
2230 static void selfInsertAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2232 WindowInfo* window = WidgetToWindow(w);
2234 #ifdef NO_XMIM
2235 static XComposeStatus compose = {NULL, 0};
2236 #else
2237 int status;
2238 #endif
2239 XKeyEvent *e = &event->xkey;
2240 char chars[20];
2241 KeySym keysym;
2242 int nChars;
2243 smartIndentCBStruct smartIndent;
2244 textDisp *textD = ((TextWidget)w)->text.textD;
2246 #ifdef NO_XMIM
2247 nChars = XLookupString(&event->xkey, chars, 19, &keysym, &compose);
2248 if (nChars == 0)
2249 return;
2250 #else
2251 nChars = XmImMbLookupString(w, &event->xkey, chars, 19, &keysym,
2252 &status);
2253 if (nChars == 0 || status == XLookupNone ||
2254 status == XLookupKeySym || status == XBufferOverflow)
2255 return;
2256 #endif
2257 cancelDrag(w);
2258 if (checkReadOnly(w))
2259 return;
2260 TakeMotifDestination(w, e->time);
2261 chars[nChars] = '\0';
2263 if (!BufSubstituteNullChars(chars, nChars, window->buffer)) {
2264 DialogF(DF_ERR, window->shell, 1, "Error", "Too much binary data", "OK");
2265 return;
2268 /* If smart indent is on, call the smart indent callback to check the
2269 inserted character */
2270 if (((TextWidget)w)->text.smartIndent) {
2271 smartIndent.reason = CHAR_TYPED;
2272 smartIndent.pos = TextDGetInsertPosition(textD);
2273 smartIndent.indentRequest = 0;
2274 smartIndent.charsTyped = chars;
2275 XtCallCallbacks(w, textNsmartIndentCallback, (XtPointer)&smartIndent);
2277 TextInsertAtCursor(w, chars, event, True, True);
2278 BufUnselect(textD->buffer);
2281 static void newlineAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2283 if (((TextWidget)w)->text.autoIndent || ((TextWidget)w)->text.smartIndent)
2284 newlineAndIndentAP(w, event, args, nArgs);
2285 else
2286 newlineNoIndentAP(w, event, args, nArgs);
2289 static void newlineNoIndentAP(Widget w, XEvent *event, String *args,
2290 Cardinal *nArgs)
2292 XKeyEvent *e = &event->xkey;
2294 cancelDrag(w);
2295 if (checkReadOnly(w))
2296 return;
2297 TakeMotifDestination(w, e->time);
2298 simpleInsertAtCursor(w, "\n", event, True);
2299 BufUnselect((((TextWidget)w)->text.textD)->buffer);
2302 static void newlineAndIndentAP(Widget w, XEvent *event, String *args,
2303 Cardinal *nArgs)
2305 XKeyEvent *e = &event->xkey;
2306 TextWidget tw = (TextWidget)w;
2307 textDisp *textD = tw->text.textD;
2308 textBuffer *buf = textD->buffer;
2309 char *indentStr;
2310 int cursorPos, lineStartPos, column;
2312 if (checkReadOnly(w))
2313 return;
2314 cancelDrag(w);
2315 TakeMotifDestination(w, e->time);
2317 /* Create a string containing a newline followed by auto or smart
2318 indent string */
2319 cursorPos = TextDGetInsertPosition(textD);
2320 lineStartPos = BufStartOfLine(buf, cursorPos);
2321 indentStr = createIndentString(tw, buf, 0, lineStartPos,
2322 cursorPos, NULL, &column);
2324 /* Insert it at the cursor */
2325 simpleInsertAtCursor(w, indentStr, event, True);
2326 XtFree(indentStr);
2328 if (tw->text.emulateTabs > 0) {
2329 /* If emulated tabs are on, make the inserted indent deletable by
2330 tab. Round this up by faking the column a bit to the right to
2331 let the user delete half-tabs with one keypress. */
2332 column += tw->text.emulateTabs - 1;
2333 tw->text.emTabsBeforeCursor = column / tw->text.emulateTabs;
2336 BufUnselect(buf);
2339 static void processTabAP(Widget w, XEvent *event, String *args,
2340 Cardinal *nArgs)
2342 textDisp *textD = ((TextWidget)w)->text.textD;
2343 textBuffer *buf = textD->buffer;
2344 selection *sel = &buf->primary;
2345 int emTabDist = ((TextWidget)w)->text.emulateTabs;
2346 int emTabsBeforeCursor = ((TextWidget)w)->text.emTabsBeforeCursor;
2347 int insertPos, indent, startIndent, toIndent, lineStart, tabWidth;
2348 char *outStr, *outPtr;
2350 if (checkReadOnly(w))
2351 return;
2352 cancelDrag(w);
2353 TakeMotifDestination(w, event->xkey.time);
2355 /* If emulated tabs are off, just insert a tab */
2356 if (emTabDist <= 0) {
2357 TextInsertAtCursor(w, "\t", event, True, True);
2358 return;
2361 /* Find the starting and ending indentation. If the tab is to
2362 replace an existing selection, use the start of the selection
2363 instead of the cursor position as the indent. When replacing
2364 rectangular selections, tabs are automatically recalculated as
2365 if the inserted text began at the start of the line */
2366 insertPos = pendingSelection(w) ?
2367 sel->start : TextDGetInsertPosition(textD);
2368 lineStart = BufStartOfLine(buf, insertPos);
2369 if (pendingSelection(w) && sel->rectangular)
2370 insertPos = BufCountForwardDispChars(buf, lineStart, sel->rectStart);
2371 startIndent = BufCountDispChars(buf, lineStart, insertPos);
2372 toIndent = startIndent + emTabDist - (startIndent % emTabDist);
2373 if (pendingSelection(w) && sel->rectangular) {
2374 toIndent -= startIndent;
2375 startIndent = 0;
2378 /* Allocate a buffer assuming all the inserted characters will be spaces */
2379 outStr = XtMalloc(toIndent - startIndent + 1);
2381 /* Add spaces and tabs to outStr until it reaches toIndent */
2382 outPtr = outStr;
2383 indent = startIndent;
2384 while (indent < toIndent) {
2385 tabWidth = BufCharWidth('\t', indent, buf->tabDist, buf->nullSubsChar);
2386 if (buf->useTabs && tabWidth > 1 && indent + tabWidth <= toIndent) {
2387 *outPtr++ = '\t';
2388 indent += tabWidth;
2389 } else {
2390 *outPtr++ = ' ';
2391 indent++;
2394 *outPtr = '\0';
2396 /* Insert the emulated tab */
2397 TextInsertAtCursor(w, outStr, event, True, True);
2398 XtFree(outStr);
2400 /* Restore and ++ emTabsBeforeCursor cleared by TextInsertAtCursor */
2401 ((TextWidget)w)->text.emTabsBeforeCursor = emTabsBeforeCursor + 1;
2403 BufUnselect(buf);
2406 static void deleteSelectionAP(Widget w, XEvent *event, String *args,
2407 Cardinal *nArgs)
2409 XKeyEvent *e = &event->xkey;
2411 cancelDrag(w);
2412 if (checkReadOnly(w))
2413 return;
2414 TakeMotifDestination(w, e->time);
2415 deletePendingSelection(w, event);
2418 static void deletePreviousCharacterAP(Widget w, XEvent *event, String *args,
2419 Cardinal *nArgs)
2421 XKeyEvent *e = &event->xkey;
2422 textDisp *textD = ((TextWidget)w)->text.textD;
2423 int insertPos = TextDGetInsertPosition(textD);
2424 char c;
2425 int silent = hasKey("nobell", args, nArgs);
2427 cancelDrag(w);
2428 if (checkReadOnly(w))
2429 return;
2431 TakeMotifDestination(w, e->time);
2432 if (deletePendingSelection(w, event))
2433 return;
2435 if (insertPos == 0) {
2436 ringIfNecessary(silent, w);
2437 return;
2440 if (deleteEmulatedTab(w, event))
2441 return;
2443 if (((TextWidget)w)->text.overstrike) {
2444 c = BufGetCharacter(textD->buffer, insertPos - 1);
2445 if (c == '\n')
2446 BufRemove(textD->buffer, insertPos - 1, insertPos);
2447 else if (c != '\t')
2448 BufReplace(textD->buffer, insertPos - 1, insertPos, " ");
2449 } else {
2450 BufRemove(textD->buffer, insertPos - 1, insertPos);
2453 TextDSetInsertPosition(textD, insertPos - 1);
2454 checkAutoShowInsertPos(w);
2455 callCursorMovementCBs(w, event);
2458 static void deleteNextCharacterAP(Widget w, XEvent *event, String *args,
2459 Cardinal *nArgs)
2461 XKeyEvent *e = &event->xkey;
2462 textDisp *textD = ((TextWidget)w)->text.textD;
2463 int insertPos = TextDGetInsertPosition(textD);
2464 int silent = hasKey("nobell", args, nArgs);
2466 cancelDrag(w);
2467 if (checkReadOnly(w))
2468 return;
2469 TakeMotifDestination(w, e->time);
2470 if (deletePendingSelection(w, event))
2471 return;
2472 if (insertPos == textD->buffer->length) {
2473 ringIfNecessary(silent, w);
2474 return;
2476 BufRemove(textD->buffer, insertPos , insertPos + 1);
2477 checkAutoShowInsertPos(w);
2478 callCursorMovementCBs(w, event);
2481 static void deletePreviousWordAP(Widget w, XEvent *event, String *args,
2482 Cardinal *nArgs)
2484 XKeyEvent *e = &event->xkey;
2485 textDisp *textD = ((TextWidget)w)->text.textD;
2486 int insertPos = TextDGetInsertPosition(textD);
2487 int pos, lineStart = BufStartOfLine(textD->buffer, insertPos);
2488 char *delimiters = ((TextWidget)w)->text.delimiters;
2489 int silent = hasKey("nobell", args, nArgs);
2491 cancelDrag(w);
2492 if (checkReadOnly(w))
2493 return;
2494 TakeMotifDestination(w, e->time);
2495 if (deletePendingSelection(w, event))
2496 return;
2497 if (insertPos == lineStart) {
2498 ringIfNecessary(silent, w);
2499 return;
2501 pos = max(insertPos - 1, 0);
2502 while (strchr(delimiters, BufGetCharacter(textD->buffer, pos)) != NULL &&
2503 pos != lineStart)
2504 pos--;
2505 pos = startOfWord((TextWidget)w, pos);
2506 BufRemove(textD->buffer, pos, insertPos);
2507 checkAutoShowInsertPos(w);
2508 callCursorMovementCBs(w, event);
2511 static void deleteNextWordAP(Widget w, XEvent *event, String *args,
2512 Cardinal *nArgs)
2514 XKeyEvent *e = &event->xkey;
2515 textDisp *textD = ((TextWidget)w)->text.textD;
2516 int insertPos = TextDGetInsertPosition(textD);
2517 int pos, lineEnd = BufEndOfLine(textD->buffer, insertPos);
2518 char *delimiters = ((TextWidget)w)->text.delimiters;
2519 int silent = hasKey("nobell", args, nArgs);
2521 cancelDrag(w);
2522 if (checkReadOnly(w))
2523 return;
2524 TakeMotifDestination(w, e->time);
2525 if (deletePendingSelection(w, event))
2526 return;
2527 if (insertPos == lineEnd) {
2528 ringIfNecessary(silent, w);
2529 return;
2531 pos = insertPos;
2532 while (strchr(delimiters, BufGetCharacter(textD->buffer, pos)) != NULL &&
2533 pos != lineEnd)
2534 pos++;
2535 pos = endOfWord((TextWidget)w, pos);
2536 BufRemove(textD->buffer, insertPos, pos);
2537 checkAutoShowInsertPos(w);
2538 callCursorMovementCBs(w, event);
2541 static void deleteToEndOfLineAP(Widget w, XEvent *event, String *args,
2542 Cardinal *nArgs)
2544 XKeyEvent *e = &event->xkey;
2545 textDisp *textD = ((TextWidget)w)->text.textD;
2546 int insertPos = TextDGetInsertPosition(textD);
2547 int endOfLine;
2548 int silent = 0;
2550 silent = hasKey("nobell", args, nArgs);
2551 if (hasKey("absolute", args, nArgs))
2552 endOfLine = BufEndOfLine(textD->buffer, insertPos);
2553 else
2554 endOfLine = TextDEndOfLine(textD, insertPos, False);
2555 cancelDrag(w);
2556 if (checkReadOnly(w))
2557 return;
2558 TakeMotifDestination(w, e->time);
2559 if (deletePendingSelection(w, event))
2560 return;
2561 if (insertPos == endOfLine) {
2562 ringIfNecessary(silent, w);
2563 return;
2565 BufRemove(textD->buffer, insertPos, endOfLine);
2566 checkAutoShowInsertPos(w);
2567 callCursorMovementCBs(w, event);
2570 static void deleteToStartOfLineAP(Widget w, XEvent *event, String *args,
2571 Cardinal *nArgs)
2573 XKeyEvent *e = &event->xkey;
2574 textDisp *textD = ((TextWidget)w)->text.textD;
2575 int insertPos = TextDGetInsertPosition(textD);
2576 int startOfLine;
2577 int silent = 0;
2579 silent = hasKey("nobell", args, nArgs);
2580 if (hasKey("wrap", args, nArgs))
2581 startOfLine = TextDStartOfLine(textD, insertPos);
2582 else
2583 startOfLine = BufStartOfLine(textD->buffer, insertPos);
2584 cancelDrag(w);
2585 if (checkReadOnly(w))
2586 return;
2587 TakeMotifDestination(w, e->time);
2588 if (deletePendingSelection(w, event))
2589 return;
2590 if (insertPos == startOfLine) {
2591 ringIfNecessary(silent, w);
2592 return;
2594 BufRemove(textD->buffer, startOfLine, insertPos);
2595 checkAutoShowInsertPos(w);
2596 callCursorMovementCBs(w, event);
2599 static void forwardCharacterAP(Widget w, XEvent *event, String *args,
2600 Cardinal *nArgs)
2602 int insertPos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
2603 int silent = hasKey("nobell", args, nArgs);
2605 cancelDrag(w);
2606 if (!TextDMoveRight(((TextWidget)w)->text.textD))
2607 ringIfNecessary(silent, w);
2608 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2609 checkAutoShowInsertPos(w);
2610 callCursorMovementCBs(w, event);
2613 static void backwardCharacterAP(Widget w, XEvent *event, String *args,
2614 Cardinal *nArgs)
2616 int insertPos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
2617 int silent = hasKey("nobell", args, nArgs);
2619 cancelDrag(w);
2620 if (!TextDMoveLeft(((TextWidget)w)->text.textD))
2621 ringIfNecessary(silent, w);
2622 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2623 checkAutoShowInsertPos(w);
2624 callCursorMovementCBs(w, event);
2627 static void forwardWordAP(Widget w, XEvent *event, String *args,
2628 Cardinal *nArgs)
2630 textDisp *textD = ((TextWidget)w)->text.textD;
2631 textBuffer *buf = textD->buffer;
2632 int pos, insertPos = TextDGetInsertPosition(textD);
2633 char *delimiters = ((TextWidget)w)->text.delimiters;
2634 int silent = hasKey("nobell", args, nArgs);
2636 cancelDrag(w);
2637 if (insertPos == buf->length) {
2638 ringIfNecessary(silent, w);
2639 return;
2641 pos = insertPos;
2642 if (hasKey("tail", args, nArgs)) {
2643 for (; pos<buf->length; pos++) {
2644 if (strchr(delimiters, BufGetCharacter(buf, pos)) == NULL)
2645 break;
2647 if (strchr(delimiters, BufGetCharacter(buf, pos)) == NULL)
2648 pos = endOfWord((TextWidget)w, pos);
2650 else {
2651 if (strchr(delimiters, BufGetCharacter(buf, pos)) == NULL)
2652 pos = endOfWord((TextWidget)w, pos);
2653 for (; pos<buf->length; pos++) {
2654 if (strchr(delimiters, BufGetCharacter(buf, pos)) == NULL)
2655 break;
2658 TextDSetInsertPosition(textD, pos);
2659 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2660 checkAutoShowInsertPos(w);
2661 callCursorMovementCBs(w, event);
2664 static void backwardWordAP(Widget w, XEvent *event, String *args,
2665 Cardinal *nArgs)
2667 textDisp *textD = ((TextWidget)w)->text.textD;
2668 textBuffer *buf = textD->buffer;
2669 int pos, insertPos = TextDGetInsertPosition(textD);
2670 char *delimiters = ((TextWidget)w)->text.delimiters;
2671 int silent = hasKey("nobell", args, nArgs);
2673 cancelDrag(w);
2674 if (insertPos == 0) {
2675 ringIfNecessary(silent, w);
2676 return;
2678 pos = max(insertPos - 1, 0);
2679 while (strchr(delimiters, BufGetCharacter(buf, pos)) != NULL && pos > 0)
2680 pos--;
2681 pos = startOfWord((TextWidget)w, pos);
2683 TextDSetInsertPosition(textD, pos);
2684 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2685 checkAutoShowInsertPos(w);
2686 callCursorMovementCBs(w, event);
2689 static void forwardParagraphAP(Widget w, XEvent *event, String *args,
2690 Cardinal *nArgs)
2692 textDisp *textD = ((TextWidget)w)->text.textD;
2693 int pos, insertPos = TextDGetInsertPosition(textD);
2694 textBuffer *buf = textD->buffer;
2695 char c;
2696 static char whiteChars[] = " \t";
2697 int silent = hasKey("nobell", args, nArgs);
2699 cancelDrag(w);
2700 if (insertPos == buf->length) {
2701 ringIfNecessary(silent, w);
2702 return;
2704 pos = min(BufEndOfLine(buf, insertPos)+1, buf->length);
2705 while (pos < buf->length) {
2706 c = BufGetCharacter(buf, pos);
2707 if (c == '\n')
2708 break;
2709 if (strchr(whiteChars, c) != NULL)
2710 pos++;
2711 else
2712 pos = min(BufEndOfLine(buf, pos)+1, buf->length);
2714 TextDSetInsertPosition(textD, min(pos+1, buf->length));
2715 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2716 checkAutoShowInsertPos(w);
2717 callCursorMovementCBs(w, event);
2720 static void backwardParagraphAP(Widget w, XEvent *event, String *args,
2721 Cardinal *nArgs)
2723 textDisp *textD = ((TextWidget)w)->text.textD;
2724 int parStart, pos, insertPos = TextDGetInsertPosition(textD);
2725 textBuffer *buf = textD->buffer;
2726 char c;
2727 static char whiteChars[] = " \t";
2728 int silent = hasKey("nobell", args, nArgs);
2730 cancelDrag(w);
2731 if (insertPos == 0) {
2732 ringIfNecessary(silent, w);
2733 return;
2735 parStart = BufStartOfLine(buf, max(insertPos-1, 0));
2736 pos = max(parStart - 2, 0);
2737 while (pos > 0) {
2738 c = BufGetCharacter(buf, pos);
2739 if (c == '\n')
2740 break;
2741 if (strchr(whiteChars, c) != NULL)
2742 pos--;
2743 else {
2744 parStart = BufStartOfLine(buf, pos);
2745 pos = max(parStart - 2, 0);
2748 TextDSetInsertPosition(textD, parStart);
2749 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2750 checkAutoShowInsertPos(w);
2751 callCursorMovementCBs(w, event);
2754 static void keySelectAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2756 textDisp *textD = ((TextWidget)w)->text.textD;
2757 int stat, insertPos = TextDGetInsertPosition(textD);
2758 int silent = hasKey("nobell", args, nArgs);
2760 cancelDrag(w);
2761 if (hasKey("left", args, nArgs)) stat = TextDMoveLeft(textD);
2762 else if (hasKey("right", args, nArgs)) stat = TextDMoveRight(textD);
2763 else if (hasKey("up", args, nArgs)) stat = TextDMoveUp(textD, 0);
2764 else if (hasKey("down", args, nArgs)) stat = TextDMoveDown(textD, 0);
2765 else {
2766 keyMoveExtendSelection(w, event, insertPos, hasKey("rect", args,nArgs));
2767 return;
2769 if (!stat) {
2770 ringIfNecessary(silent, w);
2772 else {
2773 keyMoveExtendSelection(w, event, insertPos, hasKey("rect", args,nArgs));
2774 checkAutoShowInsertPos(w);
2775 callCursorMovementCBs(w, event);
2779 static void processUpAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2781 int insertPos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
2782 int silent = hasKey("nobell", args, nArgs);
2783 int abs = hasKey("absolute", args, nArgs);
2785 cancelDrag(w);
2786 if (!TextDMoveUp(((TextWidget)w)->text.textD, abs))
2787 ringIfNecessary(silent, w);
2788 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2789 checkAutoShowInsertPos(w);
2790 callCursorMovementCBs(w, event);
2793 static void processShiftUpAP(Widget w, XEvent *event, String *args,
2794 Cardinal *nArgs)
2796 int insertPos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
2797 int silent = hasKey("nobell", args, nArgs);
2798 int abs = hasKey("absolute", args, nArgs);
2800 cancelDrag(w);
2801 if (!TextDMoveUp(((TextWidget)w)->text.textD, abs))
2802 ringIfNecessary(silent, w);
2803 keyMoveExtendSelection(w, event, insertPos, hasKey("rect", args, nArgs));
2804 checkAutoShowInsertPos(w);
2805 callCursorMovementCBs(w, event);
2808 static void processDownAP(Widget w, XEvent *event, String *args,
2809 Cardinal *nArgs)
2811 int insertPos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
2812 int silent = hasKey("nobell", args, nArgs);
2813 int abs = hasKey("absolute", args, nArgs);
2815 cancelDrag(w);
2816 if (!TextDMoveDown(((TextWidget)w)->text.textD, abs))
2817 ringIfNecessary(silent, w);
2818 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2819 checkAutoShowInsertPos(w);
2820 callCursorMovementCBs(w, event);
2823 static void processShiftDownAP(Widget w, XEvent *event, String *args,
2824 Cardinal *nArgs)
2826 int insertPos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
2827 int silent = hasKey("nobell", args, nArgs);
2828 int abs = hasKey("absolute", args, nArgs);
2830 cancelDrag(w);
2831 if (!TextDMoveDown(((TextWidget)w)->text.textD, abs))
2832 ringIfNecessary(silent, w);
2833 keyMoveExtendSelection(w, event, insertPos, hasKey("rect", args, nArgs));
2834 checkAutoShowInsertPos(w);
2835 callCursorMovementCBs(w, event);
2838 static void beginningOfLineAP(Widget w, XEvent *event, String *args,
2839 Cardinal *nArgs)
2841 textDisp *textD = ((TextWidget)w)->text.textD;
2842 int insertPos = TextDGetInsertPosition(textD);
2844 cancelDrag(w);
2845 if (hasKey("absolute", args, nArgs))
2846 TextDSetInsertPosition(textD, BufStartOfLine(textD->buffer, insertPos));
2847 else
2848 TextDSetInsertPosition(textD, TextDStartOfLine(textD, insertPos));
2849 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2850 checkAutoShowInsertPos(w);
2851 callCursorMovementCBs(w, event);
2852 textD->cursorPreferredCol = 0;
2855 static void endOfLineAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2857 textDisp *textD = ((TextWidget)w)->text.textD;
2858 int insertPos = TextDGetInsertPosition(textD);
2860 cancelDrag(w);
2861 if (hasKey("absolute", args, nArgs))
2862 TextDSetInsertPosition(textD, BufEndOfLine(textD->buffer, insertPos));
2863 else
2864 TextDSetInsertPosition(textD, TextDEndOfLine(textD, insertPos, False));
2865 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2866 checkAutoShowInsertPos(w);
2867 callCursorMovementCBs(w, event);
2868 textD->cursorPreferredCol = -1;
2871 static void beginningOfFileAP(Widget w, XEvent *event, String *args,
2872 Cardinal *nArgs)
2874 int insertPos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
2875 textDisp *textD = ((TextWidget)w)->text.textD;
2877 cancelDrag(w);
2878 if (hasKey("scrollbar", args, nArgs)) {
2879 if (textD->topLineNum != 1) {
2880 TextDSetScroll(textD, 1, textD->horizOffset);
2883 else {
2884 TextDSetInsertPosition(((TextWidget)w)->text.textD, 0);
2885 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2886 checkAutoShowInsertPos(w);
2887 callCursorMovementCBs(w, event);
2891 static void endOfFileAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2893 textDisp *textD = ((TextWidget)w)->text.textD;
2894 int insertPos = TextDGetInsertPosition(textD);
2895 int lastTopLine;
2897 cancelDrag(w);
2898 if (hasKey("scrollbar", args, nArgs)) {
2899 lastTopLine = max(1,
2900 textD->nBufferLines - (textD->nVisibleLines - 2) +
2901 ((TextWidget)w)->text.cursorVPadding);
2902 if (lastTopLine != textD->topLineNum) {
2903 TextDSetScroll(textD, lastTopLine, textD->horizOffset);
2906 else {
2907 TextDSetInsertPosition(textD, textD->buffer->length);
2908 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2909 checkAutoShowInsertPos(w);
2910 callCursorMovementCBs(w, event);
2914 static void nextPageAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2916 textDisp *textD = ((TextWidget)w)->text.textD;
2917 textBuffer *buf = textD->buffer;
2918 int lastTopLine = max(1,
2919 textD->nBufferLines - (textD->nVisibleLines - 2) +
2920 ((TextWidget)w)->text.cursorVPadding );
2921 int insertPos = TextDGetInsertPosition(textD);
2922 int column = 0, visLineNum, lineStartPos;
2923 int pos, targetLine;
2924 int pageForwardCount = 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 = min(textD->topLineNum + pageForwardCount, lastTopLine);
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 bottom line of visible area */
2941 /* if already there, page down maintaining preferrred column */
2942 targetLine = max(min(textD->nVisibleLines - 1, textD->nBufferLines), 0);
2943 column = TextDPreferredColumn(textD, &visLineNum, &lineStartPos);
2944 if (lineStartPos == textD->lineStarts[targetLine]) {
2945 if (insertPos >= buf->length || textD->topLineNum == lastTopLine) {
2946 ringIfNecessary(silent, w);
2947 return;
2949 targetLine = min(textD->topLineNum + pageForwardCount, lastTopLine);
2950 pos = TextDCountForwardNLines(textD, insertPos, pageForwardCount, False);
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 while (targetLine > 0 && pos == -1) {
2960 --targetLine;
2961 pos = textD->lineStarts[targetLine];
2963 if (lineStartPos == pos) {
2964 ringIfNecessary(silent, w);
2965 return;
2967 if (maintainColumn) {
2968 pos = TextDPosOfPreferredCol(textD, column, pos);
2970 TextDSetInsertPosition(textD, pos);
2972 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2973 checkAutoShowInsertPos(w);
2974 callCursorMovementCBs(w, event);
2975 if (maintainColumn) {
2976 textD->cursorPreferredCol = column;
2978 else {
2979 textD->cursorPreferredCol = -1;
2982 else { /* "standard" */
2983 if (insertPos >= buf->length && textD->topLineNum == lastTopLine) {
2984 ringIfNecessary(silent, w);
2985 return;
2987 if (maintainColumn) {
2988 column = TextDPreferredColumn(textD, &visLineNum, &lineStartPos);
2990 targetLine = textD->topLineNum + textD->nVisibleLines - 1;
2991 if (targetLine < 1) targetLine = 1;
2992 if (targetLine > lastTopLine) targetLine = lastTopLine;
2993 pos = TextDCountForwardNLines(textD, insertPos, textD->nVisibleLines-1, False);
2994 if (maintainColumn) {
2995 pos = TextDPosOfPreferredCol(textD, column, pos);
2997 TextDSetInsertPosition(textD, pos);
2998 TextDSetScroll(textD, targetLine, textD->horizOffset);
2999 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
3000 checkAutoShowInsertPos(w);
3001 callCursorMovementCBs(w, event);
3002 if (maintainColumn) {
3003 textD->cursorPreferredCol = column;
3005 else {
3006 textD->cursorPreferredCol = -1;
3011 static void previousPageAP(Widget w, XEvent *event, String *args,
3012 Cardinal *nArgs)
3014 textDisp *textD = ((TextWidget)w)->text.textD;
3015 int insertPos = TextDGetInsertPosition(textD);
3016 int column = 0, visLineNum, lineStartPos;
3017 int pos, targetLine;
3018 int pageBackwardCount = max(1, textD->nVisibleLines - 1);
3019 int maintainColumn = 0;
3020 int silent = hasKey("nobell", args, nArgs);
3022 maintainColumn = hasKey("column", args, nArgs);
3023 cancelDrag(w);
3024 if (hasKey("scrollbar", args, nArgs)) { /* scrollbar only */
3025 targetLine = max(textD->topLineNum - pageBackwardCount, 1);
3027 if (targetLine == textD->topLineNum) {
3028 ringIfNecessary(silent, w);
3029 return;
3031 TextDSetScroll(textD, targetLine, textD->horizOffset);
3033 else if (hasKey("stutter", args, nArgs)) { /* Mac style */
3034 /* move to top line of visible area */
3035 /* if already there, page up maintaining preferrred column if required */
3036 targetLine = 0;
3037 column = TextDPreferredColumn(textD, &visLineNum, &lineStartPos);
3038 if (lineStartPos == textD->lineStarts[targetLine]) {
3039 if (textD->topLineNum == 1 && (maintainColumn || column == 0)) {
3040 ringIfNecessary(silent, w);
3041 return;
3043 targetLine = max(textD->topLineNum - pageBackwardCount, 1);
3044 pos = TextDCountBackwardNLines(textD, insertPos, pageBackwardCount);
3045 if (maintainColumn) {
3046 pos = TextDPosOfPreferredCol(textD, column, pos);
3048 TextDSetInsertPosition(textD, pos);
3049 TextDSetScroll(textD, targetLine, textD->horizOffset);
3051 else {
3052 pos = textD->lineStarts[targetLine];
3053 if (maintainColumn) {
3054 pos = TextDPosOfPreferredCol(textD, column, pos);
3056 TextDSetInsertPosition(textD, pos);
3058 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
3059 checkAutoShowInsertPos(w);
3060 callCursorMovementCBs(w, event);
3061 if (maintainColumn) {
3062 textD->cursorPreferredCol = column;
3064 else {
3065 textD->cursorPreferredCol = -1;
3068 else { /* "standard" */
3069 if (insertPos <= 0 && textD->topLineNum == 1) {
3070 ringIfNecessary(silent, w);
3071 return;
3073 if (maintainColumn) {
3074 column = TextDPreferredColumn(textD, &visLineNum, &lineStartPos);
3076 targetLine = textD->topLineNum - (textD->nVisibleLines - 1);
3077 if (targetLine < 1) targetLine = 1;
3078 pos = TextDCountBackwardNLines(textD, insertPos, textD->nVisibleLines-1);
3079 if (maintainColumn) {
3080 pos = TextDPosOfPreferredCol(textD, column, pos);
3082 TextDSetInsertPosition(textD, pos);
3083 TextDSetScroll(textD, targetLine, textD->horizOffset);
3084 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
3085 checkAutoShowInsertPos(w);
3086 callCursorMovementCBs(w, event);
3087 if (maintainColumn) {
3088 textD->cursorPreferredCol = column;
3090 else {
3091 textD->cursorPreferredCol = -1;
3096 static void pageLeftAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
3098 textDisp *textD = ((TextWidget)w)->text.textD;
3099 textBuffer *buf = textD->buffer;
3100 int insertPos = TextDGetInsertPosition(textD);
3101 int maxCharWidth = textD->fontStruct->max_bounds.width;
3102 int lineStartPos, indent, pos;
3103 int horizOffset;
3104 int silent = hasKey("nobell", args, nArgs);
3106 cancelDrag(w);
3107 if (hasKey("scrollbar", args, nArgs)) {
3108 if (textD->horizOffset == 0) {
3109 ringIfNecessary(silent, w);
3110 return;
3112 horizOffset = max(0, textD->horizOffset - textD->width);
3113 TextDSetScroll(textD, textD->topLineNum, horizOffset);
3115 else {
3116 lineStartPos = BufStartOfLine(buf, insertPos);
3117 if (insertPos == lineStartPos && textD->horizOffset == 0) {
3118 ringIfNecessary(silent, w);
3119 return;
3121 indent = BufCountDispChars(buf, lineStartPos, insertPos);
3122 pos = BufCountForwardDispChars(buf, lineStartPos,
3123 max(0, indent - textD->width / maxCharWidth));
3124 TextDSetInsertPosition(textD, pos);
3125 TextDSetScroll(textD, textD->topLineNum,
3126 max(0, textD->horizOffset - textD->width));
3127 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
3128 checkAutoShowInsertPos(w);
3129 callCursorMovementCBs(w, event);
3133 static void pageRightAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
3135 textDisp *textD = ((TextWidget)w)->text.textD;
3136 textBuffer *buf = textD->buffer;
3137 int insertPos = TextDGetInsertPosition(textD);
3138 int maxCharWidth = textD->fontStruct->max_bounds.width;
3139 int oldHorizOffset = textD->horizOffset;
3140 int lineStartPos, indent, pos;
3141 int horizOffset, sliderSize, sliderMax;
3142 int silent = hasKey("nobell", args, nArgs);
3144 cancelDrag(w);
3145 if (hasKey("scrollbar", args, nArgs)) {
3146 XtVaGetValues(textD->hScrollBar, XmNmaximum, &sliderMax,
3147 XmNsliderSize, &sliderSize, NULL);
3148 horizOffset = min(textD->horizOffset + textD->width, sliderMax - sliderSize);
3149 if (textD->horizOffset == horizOffset) {
3150 ringIfNecessary(silent, w);
3151 return;
3153 TextDSetScroll(textD, textD->topLineNum, horizOffset);
3155 else {
3156 lineStartPos = BufStartOfLine(buf, insertPos);
3157 indent = BufCountDispChars(buf, lineStartPos, insertPos);
3158 pos = BufCountForwardDispChars(buf, lineStartPos,
3159 indent + textD->width / maxCharWidth);
3160 TextDSetInsertPosition(textD, pos);
3161 TextDSetScroll(textD, textD->topLineNum, textD->horizOffset + textD->width);
3162 if (textD->horizOffset == oldHorizOffset && insertPos == pos)
3163 ringIfNecessary(silent, w);
3164 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
3165 checkAutoShowInsertPos(w);
3166 callCursorMovementCBs(w, event);
3170 static void toggleOverstrikeAP(Widget w, XEvent *event, String *args,
3171 Cardinal *nArgs)
3173 TextWidget tw = (TextWidget)w;
3175 if (tw->text.overstrike) {
3176 tw->text.overstrike = False;
3177 TextDSetCursorStyle(tw->text.textD,
3178 tw->text.heavyCursor ? HEAVY_CURSOR : NORMAL_CURSOR);
3179 } else {
3180 tw->text.overstrike = True;
3181 if ( tw->text.textD->cursorStyle == NORMAL_CURSOR ||
3182 tw->text.textD->cursorStyle == HEAVY_CURSOR)
3183 TextDSetCursorStyle(tw->text.textD, BLOCK_CURSOR);
3187 static void scrollUpAP(Widget w, XEvent *event, String *args,
3188 Cardinal *nArgs)
3190 textDisp *textD = ((TextWidget)w)->text.textD;
3191 int topLineNum, horizOffset, nLines;
3193 if (*nArgs == 0 || sscanf(args[0], "%d", &nLines) != 1)
3194 return;
3195 if (*nArgs == 2) {
3196 /* Allow both 'page' and 'pages' */
3197 if (strncmp(args[1], "page", 4) == 0)
3198 nLines *= textD->nVisibleLines;
3200 /* 'line' or 'lines' is the only other valid possibility */
3201 else if (strncmp(args[1], "line", 4) != 0)
3202 return;
3204 TextDGetScroll(textD, &topLineNum, &horizOffset);
3205 TextDSetScroll(textD, topLineNum-nLines, horizOffset);
3208 static void scrollDownAP(Widget w, XEvent *event, String *args,
3209 Cardinal *nArgs)
3211 textDisp *textD = ((TextWidget)w)->text.textD;
3212 int topLineNum, horizOffset, nLines;
3214 if (*nArgs == 0 || sscanf(args[0], "%d", &nLines) != 1)
3215 return;
3216 if (*nArgs == 2) {
3217 /* Allow both 'page' and 'pages' */
3218 if (strncmp(args[1], "page", 4) == 0)
3219 nLines *= textD->nVisibleLines;
3221 /* 'line' or 'lines' is the only other valid possibility */
3222 else if (strncmp(args[1], "line", 4) != 0)
3223 return;
3225 TextDGetScroll(textD, &topLineNum, &horizOffset);
3226 TextDSetScroll(textD, topLineNum+nLines, horizOffset);
3229 static void scrollLeftAP(Widget w, XEvent *event, String *args,
3230 Cardinal *nArgs)
3232 textDisp *textD = ((TextWidget)w)->text.textD;
3233 int horizOffset, nPixels;
3234 int sliderMax, sliderSize;
3236 if (*nArgs == 0 || sscanf(args[0], "%d", &nPixels) != 1)
3237 return;
3238 XtVaGetValues(textD->hScrollBar, XmNmaximum, &sliderMax,
3239 XmNsliderSize, &sliderSize, NULL);
3240 horizOffset = min(max(0, textD->horizOffset - nPixels), sliderMax - sliderSize);
3241 if (textD->horizOffset != horizOffset) {
3242 TextDSetScroll(textD, textD->topLineNum, horizOffset);
3246 static void scrollRightAP(Widget w, XEvent *event, String *args,
3247 Cardinal *nArgs)
3249 textDisp *textD = ((TextWidget)w)->text.textD;
3250 int horizOffset, nPixels;
3251 int sliderMax, sliderSize;
3253 if (*nArgs == 0 || sscanf(args[0], "%d", &nPixels) != 1)
3254 return;
3255 XtVaGetValues(textD->hScrollBar, XmNmaximum, &sliderMax,
3256 XmNsliderSize, &sliderSize, NULL);
3257 horizOffset = min(max(0, textD->horizOffset + nPixels), sliderMax - sliderSize);
3258 if (textD->horizOffset != horizOffset) {
3259 TextDSetScroll(textD, textD->topLineNum, horizOffset);
3263 static void scrollToLineAP(Widget w, XEvent *event, String *args,
3264 Cardinal *nArgs)
3266 textDisp *textD = ((TextWidget)w)->text.textD;
3267 int topLineNum, horizOffset, lineNum;
3269 if (*nArgs == 0 || sscanf(args[0], "%d", &lineNum) != 1)
3270 return;
3271 TextDGetScroll(textD, &topLineNum, &horizOffset);
3272 TextDSetScroll(textD, lineNum, horizOffset);
3275 static void selectAllAP(Widget w, XEvent *event, String *args,
3276 Cardinal *nArgs)
3278 textBuffer *buf = ((TextWidget)w)->text.textD->buffer;
3280 cancelDrag(w);
3281 BufSelect(buf, 0, buf->length);
3284 static void deselectAllAP(Widget w, XEvent *event, String *args,
3285 Cardinal *nArgs)
3287 cancelDrag(w);
3288 BufUnselect(((TextWidget)w)->text.textD->buffer);
3292 ** Called on the Intrinsic FocusIn event.
3294 ** Note that the widget has no internal state about the focus, ie. it does
3295 ** not know whether it has the focus or not.
3297 static void focusInAP(Widget widget, XEvent* event, String* unused1,
3298 Cardinal* unused2)
3300 TextWidget textwidget = (TextWidget) widget;
3301 textDisp* textD = textwidget->text.textD;
3303 /* I don't entirely understand the traversal mechanism in Motif widgets,
3304 particularly, what leads to this widget getting a focus-in event when
3305 it does not actually have the input focus. The temporary solution is
3306 to do the comparison below, and not show the cursor when Motif says
3307 we don't have focus, but keep looking for the real answer */
3308 #if XmVersion >= 1002
3309 if (widget != XmGetFocusWidget(widget))
3310 return;
3311 #endif
3313 /* If the timer is not already started, start it */
3314 if (textwidget->text.cursorBlinkRate != 0
3315 && textwidget->text.cursorBlinkProcID == 0) {
3316 textwidget->text.cursorBlinkProcID
3317 = XtAppAddTimeOut(XtWidgetToApplicationContext(widget),
3318 textwidget->text.cursorBlinkRate, cursorBlinkTimerProc,
3319 widget);
3322 /* Change the cursor to active style */
3323 if (textwidget->text.overstrike)
3324 TextDSetCursorStyle(textD, BLOCK_CURSOR);
3325 else
3326 TextDSetCursorStyle(textD, (textwidget->text.heavyCursor
3327 ? HEAVY_CURSOR
3328 : NORMAL_CURSOR));
3329 TextDUnblankCursor(textD);
3331 #ifndef NO_XMIM
3332 /* Notify Motif input manager that widget has focus */
3333 XmImVaSetFocusValues(widget, NULL);
3334 #endif
3336 /* Call any registered focus-in callbacks */
3337 XtCallCallbacks((Widget) widget, textNfocusCallback, (XtPointer) event);
3340 static void focusOutAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
3342 textDisp *textD = ((TextWidget)w)->text.textD;
3344 /* Remove the cursor blinking timer procedure */
3345 if (((TextWidget)w)->text.cursorBlinkProcID != 0)
3346 XtRemoveTimeOut(((TextWidget)w)->text.cursorBlinkProcID);
3347 ((TextWidget)w)->text.cursorBlinkProcID = 0;
3349 /* Leave a dim or destination cursor */
3350 TextDSetCursorStyle(textD, ((TextWidget)w)->text.motifDestOwner ?
3351 CARET_CURSOR : DIM_CURSOR);
3352 TextDUnblankCursor(textD);
3354 /* If there's a calltip displayed, kill it. */
3355 TextDKillCalltip(textD, 0);
3357 /* Call any registered focus-out callbacks */
3358 XtCallCallbacks((Widget)w, textNlosingFocusCallback, (XtPointer)event);
3362 ** For actions involving cursor movement, "extend" keyword means incorporate
3363 ** the new cursor position in the selection, and lack of an "extend" keyword
3364 ** means cancel the existing selection
3366 static void checkMoveSelectionChange(Widget w, XEvent *event, int startPos,
3367 String *args, Cardinal *nArgs)
3369 if (hasKey("extend", args, nArgs))
3370 keyMoveExtendSelection(w, event, startPos, hasKey("rect", args, nArgs));
3371 else
3372 BufUnselect((((TextWidget)w)->text.textD)->buffer);
3376 ** If a selection change was requested via a keyboard command for moving
3377 ** the insertion cursor (usually with the "extend" keyword), adjust the
3378 ** selection to include the new cursor position, or begin a new selection
3379 ** between startPos and the new cursor position with anchor at startPos.
3381 static void keyMoveExtendSelection(Widget w, XEvent *event, int origPos,
3382 int rectangular)
3384 XKeyEvent *e = &event->xkey;
3385 TextWidget tw = (TextWidget)w;
3386 textDisp *textD = tw->text.textD;
3387 textBuffer *buf = textD->buffer;
3388 selection *sel = &buf->primary;
3389 int newPos = TextDGetInsertPosition(textD);
3390 int startPos, endPos, startCol, endCol, newCol, origCol;
3391 int anchor, rectAnchor, anchorLineStart;
3393 /* Moving the cursor does not take the Motif destination, but as soon as
3394 the user selects something, grab it (I'm not sure if this distinction
3395 actually makes sense, but it's what Motif was doing, back when their
3396 secondary selections actually worked correctly) */
3397 TakeMotifDestination(w, e->time);
3399 if ((sel->selected || sel->zeroWidth) && sel->rectangular && rectangular) {
3400 /* rect -> rect */
3401 newCol = BufCountDispChars(buf, BufStartOfLine(buf, newPos), newPos);
3402 startCol = min(tw->text.rectAnchor, newCol);
3403 endCol = max(tw->text.rectAnchor, newCol);
3404 startPos = BufStartOfLine(buf, min(tw->text.anchor, newPos));
3405 endPos = BufEndOfLine(buf, max(tw->text.anchor, newPos));
3406 BufRectSelect(buf, startPos, endPos, startCol, endCol);
3407 } else if (sel->selected && rectangular) { /* plain -> rect */
3408 newCol = BufCountDispChars(buf, BufStartOfLine(buf, newPos), newPos);
3409 if (abs(newPos - sel->start) < abs(newPos - sel->end))
3410 anchor = sel->end;
3411 else
3412 anchor = sel->start;
3413 anchorLineStart = BufStartOfLine(buf, anchor);
3414 rectAnchor = BufCountDispChars(buf, anchorLineStart, anchor);
3415 tw->text.anchor = anchor;
3416 tw->text.rectAnchor = rectAnchor;
3417 BufRectSelect(buf, BufStartOfLine(buf, min(anchor, newPos)),
3418 BufEndOfLine(buf, max(anchor, newPos)),
3419 min(rectAnchor, newCol), max(rectAnchor, newCol));
3420 } else if (sel->selected && sel->rectangular) { /* rect -> plain */
3421 startPos = BufCountForwardDispChars(buf,
3422 BufStartOfLine(buf, sel->start), sel->rectStart);
3423 endPos = BufCountForwardDispChars(buf,
3424 BufStartOfLine(buf, sel->end), sel->rectEnd);
3425 if (abs(origPos - startPos) < abs(origPos - endPos))
3426 anchor = endPos;
3427 else
3428 anchor = startPos;
3429 BufSelect(buf, anchor, newPos);
3430 } else if (sel->selected) { /* plain -> plain */
3431 if (abs(origPos - sel->start) < abs(origPos - sel->end))
3432 anchor = sel->end;
3433 else
3434 anchor = sel->start;
3435 BufSelect(buf, anchor, newPos);
3436 } else if (rectangular) { /* no sel -> rect */
3437 origCol = BufCountDispChars(buf, BufStartOfLine(buf, origPos), origPos);
3438 newCol = BufCountDispChars(buf, BufStartOfLine(buf, newPos), newPos);
3439 startCol = min(newCol, origCol);
3440 endCol = max(newCol, origCol);
3441 startPos = BufStartOfLine(buf, min(origPos, newPos));
3442 endPos = BufEndOfLine(buf, max(origPos, newPos));
3443 tw->text.anchor = origPos;
3444 tw->text.rectAnchor = origCol;
3445 BufRectSelect(buf, startPos, endPos, startCol, endCol);
3446 } else { /* no sel -> plain */
3447 tw->text.anchor = origPos;
3448 tw->text.rectAnchor = BufCountDispChars(buf,
3449 BufStartOfLine(buf, origPos), origPos);
3450 BufSelect(buf, tw->text.anchor, newPos);
3454 static void checkAutoShowInsertPos(Widget w)
3456 if (((TextWidget)w)->text.autoShowInsertPos)
3457 TextDMakeInsertPosVisible(((TextWidget)w)->text.textD);
3460 static int checkReadOnly(Widget w)
3462 if (((TextWidget)w)->text.readOnly) {
3463 XBell(XtDisplay(w), 0);
3464 return True;
3466 return False;
3470 ** Insert text "chars" at the cursor position, as if the text had been
3471 ** typed. Same as TextInsertAtCursor, but without the complicated auto-wrap
3472 ** scanning and re-formatting.
3474 static void simpleInsertAtCursor(Widget w, char *chars, XEvent *event,
3475 int allowPendingDelete)
3477 textDisp *textD = ((TextWidget)w)->text.textD;
3478 textBuffer *buf = textD->buffer;
3479 char *c;
3481 if (allowPendingDelete && pendingSelection(w)) {
3482 BufReplaceSelected(buf, chars);
3483 TextDSetInsertPosition(textD, buf->cursorPosHint);
3484 } else if (((TextWidget)w)->text.overstrike) {
3485 for (c=chars; *c!='\0' && *c!='\n'; c++);
3486 if (*c == '\n')
3487 TextDInsert(textD, chars);
3488 else
3489 TextDOverstrike(textD, chars);
3490 } else
3491 TextDInsert(textD, chars);
3492 checkAutoShowInsertPos(w);
3493 callCursorMovementCBs(w, event);
3497 ** If there's a selection, delete it and position the cursor where the
3498 ** selection was deleted. (Called by routines which do deletion to check
3499 ** first for and do possible selection delete)
3501 static int deletePendingSelection(Widget w, XEvent *event)
3503 textDisp *textD = ((TextWidget)w)->text.textD;
3504 textBuffer *buf = textD->buffer;
3506 if (((TextWidget)w)->text.textD->buffer->primary.selected) {
3507 BufRemoveSelected(buf);
3508 TextDSetInsertPosition(textD, buf->cursorPosHint);
3509 checkAutoShowInsertPos(w);
3510 callCursorMovementCBs(w, event);
3511 return True;
3512 } else
3513 return False;
3517 ** Return true if pending delete is on and there's a selection contiguous
3518 ** with the cursor ready to be deleted. These criteria are used to decide
3519 ** if typing a character or inserting something should delete the selection
3520 ** first.
3522 static int pendingSelection(Widget w)
3524 selection *sel = &((TextWidget)w)->text.textD->buffer->primary;
3525 int pos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
3527 return ((TextWidget)w)->text.pendingDelete && sel->selected &&
3528 pos >= sel->start && pos <= sel->end;
3532 ** Check if tab emulation is on and if there are emulated tabs before the
3533 ** cursor, and if so, delete an emulated tab as a unit. Also finishes up
3534 ** by calling checkAutoShowInsertPos and callCursorMovementCBs, so the
3535 ** calling action proc can just return (this is necessary to preserve
3536 ** emTabsBeforeCursor which is otherwise cleared by callCursorMovementCBs).
3538 static int deleteEmulatedTab(Widget w, XEvent *event)
3540 textDisp *textD = ((TextWidget)w)->text.textD;
3541 textBuffer *buf = ((TextWidget)w)->text.textD->buffer;
3542 int emTabDist = ((TextWidget)w)->text.emulateTabs;
3543 int emTabsBeforeCursor = ((TextWidget)w)->text.emTabsBeforeCursor;
3544 int startIndent, toIndent, insertPos, startPos, lineStart;
3545 int pos, indent, startPosIndent;
3546 char c, *spaceString;
3548 if (emTabDist <= 0 || emTabsBeforeCursor <= 0)
3549 return False;
3551 /* Find the position of the previous tab stop */
3552 insertPos = TextDGetInsertPosition(textD);
3553 lineStart = BufStartOfLine(buf, insertPos);
3554 startIndent = BufCountDispChars(buf, lineStart, insertPos);
3555 toIndent = (startIndent-1) - ((startIndent-1) % emTabDist);
3557 /* Find the position at which to begin deleting (stop at non-whitespace
3558 characters) */
3559 startPosIndent = indent = 0;
3560 startPos = lineStart;
3561 for (pos=lineStart; pos < insertPos; pos++) {
3562 c = BufGetCharacter(buf, pos);
3563 indent += BufCharWidth(c, indent, buf->tabDist, buf->nullSubsChar);
3564 if (indent > toIndent)
3565 break;
3566 startPosIndent = indent;
3567 startPos = pos + 1;
3570 /* Just to make sure, check that we're not deleting any non-white chars */
3571 for (pos=insertPos-1; pos>=startPos; pos--) {
3572 c = BufGetCharacter(buf, pos);
3573 if (c != ' ' && c != '\t') {
3574 startPos = pos + 1;
3575 break;
3579 /* Do the text replacement and reposition the cursor. If any spaces need
3580 to be inserted to make up for a deleted tab, do a BufReplace, otherwise,
3581 do a BufRemove. */
3582 if (startPosIndent < toIndent) {
3583 spaceString = XtMalloc(toIndent - startPosIndent + 1);
3584 memset(spaceString, ' ', toIndent-startPosIndent);
3585 spaceString[toIndent - startPosIndent] = '\0';
3586 BufReplace(buf, startPos, insertPos, spaceString);
3587 TextDSetInsertPosition(textD, startPos + toIndent - startPosIndent);
3588 XtFree(spaceString);
3589 } else {
3590 BufRemove(buf, startPos, insertPos);
3591 TextDSetInsertPosition(textD, startPos);
3594 /* The normal cursor movement stuff would usually be called by the action
3595 routine, but this wraps around it to restore emTabsBeforeCursor */
3596 checkAutoShowInsertPos(w);
3597 callCursorMovementCBs(w, event);
3599 /* Decrement and restore the marker for consecutive emulated tabs, which
3600 would otherwise have been zeroed by callCursorMovementCBs */
3601 ((TextWidget)w)->text.emTabsBeforeCursor = emTabsBeforeCursor - 1;
3602 return True;
3606 ** Select the word or whitespace adjacent to the cursor, and move the cursor
3607 ** to its end. pointerX is used as a tie-breaker, when the cursor is at the
3608 ** boundary between a word and some white-space. If the cursor is on the
3609 ** left, the word or space on the left is used. If it's on the right, that
3610 ** is used instead.
3612 static void selectWord(Widget w, int pointerX)
3614 TextWidget tw = (TextWidget)w;
3615 textBuffer *buf = tw->text.textD->buffer;
3616 int x, y, insertPos = TextDGetInsertPosition(tw->text.textD);
3618 TextPosToXY(w, insertPos, &x, &y);
3619 if (pointerX < x && insertPos > 0 && BufGetCharacter(buf, insertPos-1) != '\n')
3620 insertPos--;
3621 BufSelect(buf, startOfWord(tw, insertPos), endOfWord(tw, insertPos));
3624 static int startOfWord(TextWidget w, int pos)
3626 int startPos;
3627 textBuffer *buf = w->text.textD->buffer;
3628 char *delimiters=w->text.delimiters;
3629 char c = BufGetCharacter(buf, pos);
3631 if (c == ' ' || c== '\t') {
3632 if (!spanBackward(buf, pos, " \t", False, &startPos))
3633 return 0;
3634 } else if (strchr(delimiters, c)) {
3635 if (!spanBackward(buf, pos, delimiters, True, &startPos))
3636 return 0;
3637 } else {
3638 if (!BufSearchBackward(buf, pos, delimiters, &startPos))
3639 return 0;
3641 return min(pos, startPos+1);
3645 static int endOfWord(TextWidget w, int pos)
3647 int endPos;
3648 textBuffer *buf = w->text.textD->buffer;
3649 char *delimiters=w->text.delimiters;
3650 char c = BufGetCharacter(buf, pos);
3652 if (c == ' ' || c== '\t') {
3653 if (!spanForward(buf, pos, " \t", False, &endPos))
3654 return buf->length;
3655 } else if (strchr(delimiters, c)) {
3656 if (!spanForward(buf, pos, delimiters, True, &endPos))
3657 return buf->length;
3658 } else {
3659 if (!BufSearchForward(buf, pos, delimiters, &endPos))
3660 return buf->length;
3662 return endPos;
3666 ** Search forwards in buffer "buf" for the first character NOT in
3667 ** "searchChars", starting with the character "startPos", and returning the
3668 ** result in "foundPos" returns True if found, False if not. If ignoreSpace
3669 ** is set, then Space, Tab, and Newlines are ignored in searchChars.
3671 static int spanForward(textBuffer *buf, int startPos, char *searchChars,
3672 int ignoreSpace, int *foundPos)
3674 int pos;
3675 char *c;
3677 pos = startPos;
3678 while (pos < buf->length) {
3679 for (c=searchChars; *c!='\0'; c++)
3680 if(!(ignoreSpace && (*c==' ' || *c=='\t' || *c=='\n')))
3681 if (BufGetCharacter(buf, pos) == *c)
3682 break;
3683 if(*c == 0) {
3684 *foundPos = pos;
3685 return True;
3687 pos++;
3689 *foundPos = buf->length;
3690 return False;
3694 ** Search backwards in buffer "buf" for the first character NOT in
3695 ** "searchChars", starting with the character BEFORE "startPos", returning the
3696 ** result in "foundPos" returns True if found, False if not. If ignoreSpace is
3697 ** set, then Space, Tab, and Newlines are ignored in searchChars.
3699 static int spanBackward(textBuffer *buf, int startPos, char *searchChars, int
3700 ignoreSpace, int *foundPos)
3702 int pos;
3703 char *c;
3705 if (startPos == 0) {
3706 *foundPos = 0;
3707 return False;
3709 pos = startPos == 0 ? 0 : startPos - 1;
3710 while (pos >= 0) {
3711 for (c=searchChars; *c!='\0'; c++)
3712 if(!(ignoreSpace && (*c==' ' || *c=='\t' || *c=='\n')))
3713 if (BufGetCharacter(buf, pos) == *c)
3714 break;
3715 if(*c == 0) {
3716 *foundPos = pos;
3717 return True;
3719 pos--;
3721 *foundPos = 0;
3722 return False;
3726 ** Select the line containing the cursor, including the terminating newline,
3727 ** and move the cursor to its end.
3729 static void selectLine(Widget w)
3731 textDisp *textD = ((TextWidget)w)->text.textD;
3732 int insertPos = TextDGetInsertPosition(textD);
3733 int endPos, startPos;
3735 endPos = BufEndOfLine(textD->buffer, insertPos);
3736 startPos = BufStartOfLine(textD->buffer, insertPos);
3737 BufSelect(textD->buffer, startPos, min(endPos + 1, textD->buffer->length));
3738 TextDSetInsertPosition(textD, endPos);
3742 ** Given a new mouse pointer location, pass the position on to the
3743 ** autoscroll timer routine, and make sure the timer is on when it's
3744 ** needed and off when it's not.
3746 static void checkAutoScroll(TextWidget w, int x, int y)
3748 int inWindow;
3750 /* Is the pointer in or out of the window? */
3751 inWindow = x >= w->text.textD->left &&
3752 x < w->core.width - w->text.marginWidth &&
3753 y >= w->text.marginHeight &&
3754 y < w->core.height - w->text.marginHeight;
3756 /* If it's in the window, cancel the timer procedure */
3757 if (inWindow) {
3758 if (w->text.autoScrollProcID != 0)
3759 XtRemoveTimeOut(w->text.autoScrollProcID);;
3760 w->text.autoScrollProcID = 0;
3761 return;
3764 /* If the timer is not already started, start it */
3765 if (w->text.autoScrollProcID == 0) {
3766 w->text.autoScrollProcID = XtAppAddTimeOut(
3767 XtWidgetToApplicationContext((Widget)w),
3768 0, autoScrollTimerProc, w);
3771 /* Pass on the newest mouse location to the autoscroll routine */
3772 w->text.mouseX = x;
3773 w->text.mouseY = y;
3777 ** Reset drag state and cancel the auto-scroll timer
3779 static void endDrag(Widget w)
3781 if (((TextWidget)w)->text.autoScrollProcID != 0)
3782 XtRemoveTimeOut(((TextWidget)w)->text.autoScrollProcID);
3783 ((TextWidget)w)->text.autoScrollProcID = 0;
3784 if (((TextWidget)w)->text.dragState == MOUSE_PAN)
3785 XUngrabPointer(XtDisplay(w), CurrentTime);
3786 ((TextWidget)w)->text.dragState = NOT_CLICKED;
3790 ** Cancel any drag operation that might be in progress. Should be included
3791 ** in nearly every key event to cleanly end any dragging before edits are made
3792 ** which might change the insert position or the content of the buffer during
3793 ** a drag operation)
3795 static void cancelDrag(Widget w)
3797 int dragState = ((TextWidget)w)->text.dragState;
3799 if (((TextWidget)w)->text.autoScrollProcID != 0)
3800 XtRemoveTimeOut(((TextWidget)w)->text.autoScrollProcID);
3801 if (dragState == SECONDARY_DRAG || dragState == SECONDARY_RECT_DRAG)
3802 BufSecondaryUnselect(((TextWidget)w)->text.textD->buffer);
3803 if (dragState == PRIMARY_BLOCK_DRAG)
3804 CancelBlockDrag((TextWidget)w);
3805 if (dragState == MOUSE_PAN)
3806 XUngrabPointer(XtDisplay(w), CurrentTime);
3807 if (dragState != NOT_CLICKED)
3808 ((TextWidget)w)->text.dragState = DRAG_CANCELED;
3812 ** Do operations triggered by cursor movement: Call cursor movement callback
3813 ** procedure(s), and cancel marker indicating that the cursor is after one or
3814 ** more just-entered emulated tabs (spaces to be deleted as a unit).
3816 static void callCursorMovementCBs(Widget w, XEvent *event)
3818 ((TextWidget)w)->text.emTabsBeforeCursor = 0;
3819 XtCallCallbacks((Widget)w, textNcursorMovementCallback, (XtPointer)event);
3823 ** Adjust the selection as the mouse is dragged to position: (x, y).
3825 static void adjustSelection(TextWidget tw, int x, int y)
3827 textDisp *textD = tw->text.textD;
3828 textBuffer *buf = textD->buffer;
3829 int row, col, startCol, endCol, startPos, endPos;
3830 int newPos = TextDXYToPosition(textD, x, y);
3832 /* Adjust the selection */
3833 if (tw->text.dragState == PRIMARY_RECT_DRAG) {
3834 TextDXYToUnconstrainedPosition(textD, x, y, &row, &col);
3835 col = TextDOffsetWrappedColumn(textD, row, col);
3836 startCol = min(tw->text.rectAnchor, col);
3837 endCol = max(tw->text.rectAnchor, col);
3838 startPos = BufStartOfLine(buf, min(tw->text.anchor, newPos));
3839 endPos = BufEndOfLine(buf, max(tw->text.anchor, newPos));
3840 BufRectSelect(buf, startPos, endPos, startCol, endCol);
3841 } else if (tw->text.multiClickState == ONE_CLICK) {
3842 startPos = startOfWord(tw, min(tw->text.anchor, newPos));
3843 endPos = endOfWord(tw, max(tw->text.anchor, newPos));
3844 BufSelect(buf, startPos, endPos);
3845 newPos = newPos < tw->text.anchor ? startPos : endPos;
3846 } else if (tw->text.multiClickState == TWO_CLICKS) {
3847 startPos = BufStartOfLine(buf, min(tw->text.anchor, newPos));
3848 endPos = BufEndOfLine(buf, max(tw->text.anchor, newPos));
3849 BufSelect(buf, startPos, min(endPos+1, buf->length));
3850 newPos = newPos < tw->text.anchor ? startPos : endPos;
3851 } else
3852 BufSelect(buf, tw->text.anchor, newPos);
3854 /* Move the cursor */
3855 TextDSetInsertPosition(textD, newPos);
3856 callCursorMovementCBs((Widget)tw, NULL);
3859 static void adjustSecondarySelection(TextWidget tw, int x, int y)
3861 textDisp *textD = tw->text.textD;
3862 textBuffer *buf = textD->buffer;
3863 int row, col, startCol, endCol, startPos, endPos;
3864 int newPos = TextDXYToPosition(textD, x, y);
3866 if (tw->text.dragState == SECONDARY_RECT_DRAG) {
3867 TextDXYToUnconstrainedPosition(textD, x, y, &row, &col);
3868 col = TextDOffsetWrappedColumn(textD, row, col);
3869 startCol = min(tw->text.rectAnchor, col);
3870 endCol = max(tw->text.rectAnchor, col);
3871 startPos = BufStartOfLine(buf, min(tw->text.anchor, newPos));
3872 endPos = BufEndOfLine(buf, max(tw->text.anchor, newPos));
3873 BufSecRectSelect(buf, startPos, endPos, startCol, endCol);
3874 } else
3875 BufSecondarySelect(textD->buffer, tw->text.anchor, newPos);
3879 ** Wrap multi-line text in argument "text" to be inserted at the end of the
3880 ** text on line "startLine" and return the result. If "breakBefore" is
3881 ** non-NULL, allow wrapping to extend back into "startLine", in which case
3882 ** the returned text will include the wrapped part of "startLine", and
3883 ** "breakBefore" will return the number of characters at the end of
3884 ** "startLine" that were absorbed into the returned string. "breakBefore"
3885 ** will return zero if no characters were absorbed into the returned string.
3886 ** The buffer offset of text in the widget's text buffer is needed so that
3887 ** smart indent (which can be triggered by wrapping) can search back farther
3888 ** in the buffer than just the text in startLine.
3890 static char *wrapText(TextWidget tw, char *startLine, char *text, int bufOffset,
3891 int wrapMargin, int *breakBefore)
3893 textBuffer *wrapBuf, *buf = tw->text.textD->buffer;
3894 int startLineLen = strlen(startLine);
3895 int colNum, pos, lineStartPos, limitPos, breakAt, charsAdded;
3896 int firstBreak = -1, tabDist = buf->tabDist;
3897 char c, *wrappedText;
3899 /* Create a temporary text buffer and load it with the strings */
3900 wrapBuf = BufCreate();
3901 BufInsert(wrapBuf, 0, startLine);
3902 BufInsert(wrapBuf, wrapBuf->length, text);
3904 /* Scan the buffer for long lines and apply wrapLine when wrapMargin is
3905 exceeded. limitPos enforces no breaks in the "startLine" part of the
3906 string (if requested), and prevents re-scanning of long unbreakable
3907 lines for each character beyond the margin */
3908 colNum = 0;
3909 pos = 0;
3910 lineStartPos = 0;
3911 limitPos = breakBefore == NULL ? startLineLen : 0;
3912 while (pos < wrapBuf->length) {
3913 c = BufGetCharacter(wrapBuf, pos);
3914 if (c == '\n') {
3915 lineStartPos = limitPos = pos + 1;
3916 colNum = 0;
3917 } else {
3918 colNum += BufCharWidth(c, colNum, tabDist, buf->nullSubsChar);
3919 if (colNum > wrapMargin) {
3920 if (!wrapLine(tw, wrapBuf, bufOffset, lineStartPos, pos,
3921 limitPos, &breakAt, &charsAdded)) {
3922 limitPos = max(pos, limitPos);
3923 } else {
3924 lineStartPos = limitPos = breakAt+1;
3925 pos += charsAdded;
3926 colNum = BufCountDispChars(wrapBuf, lineStartPos, pos+1);
3927 if (firstBreak == -1)
3928 firstBreak = breakAt;
3932 pos++;
3935 /* Return the wrapped text, possibly including part of startLine */
3936 if (breakBefore == NULL)
3937 wrappedText = BufGetRange(wrapBuf, startLineLen, wrapBuf->length);
3938 else {
3939 *breakBefore = firstBreak != -1 && firstBreak < startLineLen ?
3940 startLineLen - firstBreak : 0;
3941 wrappedText = BufGetRange(wrapBuf, startLineLen - *breakBefore,
3942 wrapBuf->length);
3944 BufFree(wrapBuf);
3945 return wrappedText;
3949 ** Wraps the end of a line beginning at lineStartPos and ending at lineEndPos
3950 ** in "buf", at the last white-space on the line >= limitPos. (The implicit
3951 ** assumption is that just the last character of the line exceeds the wrap
3952 ** margin, and anywhere on the line we can wrap is correct). Returns False if
3953 ** unable to wrap the line. "breakAt", returns the character position at
3954 ** which the line was broken,
3956 ** Auto-wrapping can also trigger auto-indent. The additional parameter
3957 ** bufOffset is needed when auto-indent is set to smart indent and the smart
3958 ** indent routines need to scan far back in the buffer. "charsAdded" returns
3959 ** the number of characters added to acheive the auto-indent. wrapMargin is
3960 ** used to decide whether auto-indent should be skipped because the indent
3961 ** string itself would exceed the wrap margin.
3963 static int wrapLine(TextWidget tw, textBuffer *buf, int bufOffset,
3964 int lineStartPos, int lineEndPos, int limitPos, int *breakAt,
3965 int *charsAdded)
3967 int p, length, column;
3968 char c, *indentStr;
3970 /* Scan backward for whitespace or BOL. If BOL, return False, no
3971 whitespace in line at which to wrap */
3972 for (p=lineEndPos; ; p--) {
3973 if (p < lineStartPos || p < limitPos)
3974 return False;
3975 c = BufGetCharacter(buf, p);
3976 if (c == '\t' || c == ' ')
3977 break;
3980 /* Create an auto-indent string to insert to do wrap. If the auto
3981 indent string reaches the wrap position, slice the auto-indent
3982 back off and return to the left margin */
3983 if (tw->text.autoIndent || tw->text.smartIndent) {
3984 indentStr = createIndentString(tw, buf, bufOffset, lineStartPos,
3985 lineEndPos, &length, &column);
3986 if (column >= p-lineStartPos)
3987 indentStr[1] = '\0';
3988 } else {
3989 indentStr = "\n";
3990 length = 1;
3993 /* Replace the whitespace character with the auto-indent string
3994 and return the stats */
3995 BufReplace(buf, p, p+1, indentStr);
3996 if (tw->text.autoIndent || tw->text.smartIndent)
3997 XtFree(indentStr);
3998 *breakAt = p;
3999 *charsAdded = length-1;
4000 return True;
4004 ** Create and return an auto-indent string to add a newline at lineEndPos to a
4005 ** line starting at lineStartPos in buf. "buf" may or may not be the real
4006 ** text buffer for the widget. If it is not the widget's text buffer it's
4007 ** offset position from the real buffer must be specified in "bufOffset" to
4008 ** allow the smart-indent routines to scan back as far as necessary. The
4009 ** string length is returned in "length" (or "length" can be passed as NULL,
4010 ** and the indent column is returned in "column" (if non NULL).
4012 static char *createIndentString(TextWidget tw, textBuffer *buf, int bufOffset,
4013 int lineStartPos, int lineEndPos, int *length, int *column)
4015 textDisp *textD = tw->text.textD;
4016 int pos, indent = -1, tabDist = textD->buffer->tabDist;
4017 int i, useTabs = textD->buffer->useTabs;
4018 char *indentPtr, *indentStr, c;
4019 smartIndentCBStruct smartIndent;
4021 /* If smart indent is on, call the smart indent callback. It is not
4022 called when multi-line changes are being made (lineStartPos != 0),
4023 because smart indent needs to search back an indeterminate distance
4024 through the buffer, and reconciling that with wrapping changes made,
4025 but not yet committed in the buffer, would make programming smart
4026 indent more difficult for users and make everything more complicated */
4027 if (tw->text.smartIndent && (lineStartPos == 0 || buf == textD->buffer)) {
4028 smartIndent.reason = NEWLINE_INDENT_NEEDED;
4029 smartIndent.pos = lineEndPos + bufOffset;
4030 smartIndent.indentRequest = 0;
4031 smartIndent.charsTyped = NULL;
4032 XtCallCallbacks((Widget)tw, textNsmartIndentCallback,
4033 (XtPointer)&smartIndent);
4034 indent = smartIndent.indentRequest;
4037 /* If smart indent wasn't used, measure the indent distance of the line */
4038 if (indent == -1) {
4039 indent = 0;
4040 for (pos=lineStartPos; pos<lineEndPos; pos++) {
4041 c = BufGetCharacter(buf, pos);
4042 if (c != ' ' && c != '\t')
4043 break;
4044 if (c == '\t')
4045 indent += tabDist - (indent % tabDist);
4046 else
4047 indent++;
4051 /* Allocate and create a string of tabs and spaces to achieve the indent */
4052 indentPtr = indentStr = XtMalloc(indent + 2);
4053 *indentPtr++ = '\n';
4054 if (useTabs) {
4055 for (i=0; i < indent / tabDist; i++)
4056 *indentPtr++ = '\t';
4057 for (i=0; i < indent % tabDist; i++)
4058 *indentPtr++ = ' ';
4059 } else {
4060 for (i=0; i < indent; i++)
4061 *indentPtr++ = ' ';
4063 *indentPtr = '\0';
4065 /* Return any requested stats */
4066 if (length != NULL)
4067 *length = indentPtr - indentStr;
4068 if (column != NULL)
4069 *column = indent;
4071 return indentStr;
4075 ** Xt timer procedure for autoscrolling
4077 static void autoScrollTimerProc(XtPointer clientData, XtIntervalId *id)
4079 TextWidget w = (TextWidget)clientData;
4080 textDisp *textD = w->text.textD;
4081 int topLineNum, horizOffset, newPos, cursorX, y;
4082 int fontWidth = textD->fontStruct->max_bounds.width;
4083 int fontHeight = textD->fontStruct->ascent + textD->fontStruct->descent;
4085 /* For vertical autoscrolling just dragging the mouse outside of the top
4086 or bottom of the window is sufficient, for horizontal (non-rectangular)
4087 scrolling, see if the position where the CURSOR would go is outside */
4088 newPos = TextDXYToPosition(textD, w->text.mouseX, w->text.mouseY);
4089 if (w->text.dragState == PRIMARY_RECT_DRAG)
4090 cursorX = w->text.mouseX;
4091 else if (!TextDPositionToXY(textD, newPos, &cursorX, &y))
4092 cursorX = w->text.mouseX;
4094 /* Scroll away from the pointer, 1 character (horizontal), or 1 character
4095 for each fontHeight distance from the mouse to the text (vertical) */
4096 TextDGetScroll(textD, &topLineNum, &horizOffset);
4097 if (cursorX >= (int)w->core.width - w->text.marginWidth)
4098 horizOffset += fontWidth;
4099 else if (w->text.mouseX < textD->left)
4100 horizOffset -= fontWidth;
4101 if (w->text.mouseY >= (int)w->core.height - w->text.marginHeight)
4102 topLineNum += 1 + ((w->text.mouseY - (int)w->core.height -
4103 w->text.marginHeight) / fontHeight) + 1;
4104 else if (w->text.mouseY < w->text.marginHeight)
4105 topLineNum -= 1 + ((w->text.marginHeight-w->text.mouseY) / fontHeight);
4106 TextDSetScroll(textD, topLineNum, horizOffset);
4108 /* Continue the drag operation in progress. If none is in progress
4109 (safety check) don't continue to re-establish the timer proc */
4110 if (w->text.dragState == PRIMARY_DRAG) {
4111 adjustSelection(w, w->text.mouseX, w->text.mouseY);
4112 } else if (w->text.dragState == PRIMARY_RECT_DRAG) {
4113 adjustSelection(w, w->text.mouseX, w->text.mouseY);
4114 } else if (w->text.dragState == SECONDARY_DRAG) {
4115 adjustSecondarySelection(w, w->text.mouseX, w->text.mouseY);
4116 } else if (w->text.dragState == SECONDARY_RECT_DRAG) {
4117 adjustSecondarySelection(w, w->text.mouseX, w->text.mouseY);
4118 } else if (w->text.dragState == PRIMARY_BLOCK_DRAG) {
4119 BlockDragSelection(w, w->text.mouseX, w->text.mouseY, USE_LAST);
4120 } else {
4121 w->text.autoScrollProcID = 0;
4122 return;
4125 /* re-establish the timer proc (this routine) to continue processing */
4126 w->text.autoScrollProcID = XtAppAddTimeOut(
4127 XtWidgetToApplicationContext((Widget)w),
4128 w->text.mouseY >= w->text.marginHeight &&
4129 w->text.mouseY < w->core.height - w->text.marginHeight ?
4130 (VERTICAL_SCROLL_DELAY*fontWidth) / fontHeight :
4131 VERTICAL_SCROLL_DELAY, autoScrollTimerProc, w);
4135 ** Xt timer procedure for cursor blinking
4137 static void cursorBlinkTimerProc(XtPointer clientData, XtIntervalId *id)
4139 TextWidget w = (TextWidget)clientData;
4140 textDisp *textD = w->text.textD;
4142 /* Blink the cursor */
4143 if (textD->cursorOn)
4144 TextDBlankCursor(textD);
4145 else
4146 TextDUnblankCursor(textD);
4148 /* re-establish the timer proc (this routine) to continue processing */
4149 w->text.cursorBlinkProcID = XtAppAddTimeOut(
4150 XtWidgetToApplicationContext((Widget)w),
4151 w->text.cursorBlinkRate, cursorBlinkTimerProc, w);
4155 ** Sets the caret to on or off and restart the caret blink timer.
4156 ** This could be used by other modules to modify the caret's blinking.
4158 void ResetCursorBlink(TextWidget textWidget, Boolean startsBlanked)
4160 if (0 != textWidget->text.cursorBlinkRate)
4162 if (0 != textWidget->text.cursorBlinkProcID)
4164 XtRemoveTimeOut(textWidget->text.cursorBlinkProcID);
4167 if (startsBlanked)
4169 TextDBlankCursor(textWidget->text.textD);
4170 } else
4172 TextDUnblankCursor(textWidget->text.textD);
4175 textWidget->text.cursorBlinkProcID
4176 = XtAppAddTimeOut(XtWidgetToApplicationContext((Widget) textWidget),
4177 textWidget->text.cursorBlinkRate, cursorBlinkTimerProc,
4178 textWidget);
4183 ** look at an action procedure's arguments to see if argument "key" has been
4184 ** specified in the argument list
4186 static int hasKey(const char *key, const String *args, const Cardinal *nArgs)
4188 int i;
4190 for (i=0; i<(int)*nArgs; i++)
4191 if (!strCaseCmp(args[i], key))
4192 return True;
4193 return False;
4196 static int max(int i1, int i2)
4198 return i1 >= i2 ? i1 : i2;
4201 static int min(int i1, int i2)
4203 return i1 <= i2 ? i1 : i2;
4206 const char *GetDefaultTranslations(void)
4208 return defaultTranslations;
4211 ** strCaseCmp compares its arguments and returns 0 if the two strings
4212 ** are equal IGNORING case differences. Otherwise returns 1.
4215 static int strCaseCmp(const char *str1, const char *str2)
4217 unsigned const char *c1 = (unsigned const char*) str1;
4218 unsigned const char *c2 = (unsigned const char*) str2;
4220 for (; *c1!='\0' && *c2!='\0'; c1++, c2++)
4221 if (toupper(*c1) != toupper(*c2))
4222 return 1;
4223 if (*c1 == *c2) {
4224 return(0);
4226 else {
4227 return(1);
4231 static void ringIfNecessary(Boolean silent, Widget w)
4233 if (!silent)
4234 XBell(XtDisplay(w), 0);