Fix our use of $em_tab_dist after it was changed to 0 for 'turned of'.
[nedit.git] / source / text.c
blob86e436b445efadcdb92ca66bf4add8d4a9f0216f
1 static const char CVSID[] = "$Id: text.c,v 1.57 2008/01/04 22:11:04 yooden 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 TextPasteClipboard(Widget w, Time time)
1340 cancelDrag(w);
1341 if (checkReadOnly(w))
1342 return;
1343 TakeMotifDestination(w, time);
1344 InsertClipboard(w, False);
1345 callCursorMovementCBs(w, NULL);
1348 void TextColPasteClipboard(Widget w, Time time)
1350 cancelDrag(w);
1351 if (checkReadOnly(w))
1352 return;
1353 TakeMotifDestination(w, time);
1354 InsertClipboard(w, True);
1355 callCursorMovementCBs(w, NULL);
1358 void TextCopyClipboard(Widget w, Time time)
1360 cancelDrag(w);
1361 if (!((TextWidget)w)->text.textD->buffer->primary.selected) {
1362 XBell(XtDisplay(w), 0);
1363 return;
1365 CopyToClipboard(w, time);
1368 void TextCutClipboard(Widget w, Time time)
1370 textDisp *textD = ((TextWidget)w)->text.textD;
1372 cancelDrag(w);
1373 if (checkReadOnly(w))
1374 return;
1375 if (!textD->buffer->primary.selected) {
1376 XBell(XtDisplay(w), 0);
1377 return;
1379 TakeMotifDestination(w, time);
1380 CopyToClipboard (w, time);
1381 BufRemoveSelected(textD->buffer);
1382 TextDSetInsertPosition(textD, textD->buffer->cursorPosHint);
1383 checkAutoShowInsertPos(w);
1386 int TextFirstVisibleLine(Widget w)
1388 return(((TextWidget)w)->text.textD->topLineNum);
1391 int TextNumVisibleLines(Widget w)
1393 return(((TextWidget)w)->text.textD->nVisibleLines);
1396 int TextVisibleWidth(Widget w)
1398 return(((TextWidget)w)->text.textD->width);
1401 int TextFirstVisiblePos(Widget w)
1403 return ((TextWidget)w)->text.textD->firstChar;
1406 int TextLastVisiblePos(Widget w)
1408 return ((TextWidget)w)->text.textD->lastChar;
1412 ** Insert text "chars" at the cursor position, respecting pending delete
1413 ** selections, overstrike, and handling cursor repositioning as if the text
1414 ** had been typed. If autoWrap is on wraps the text to fit within the wrap
1415 ** margin, auto-indenting where the line was wrapped (but nowhere else).
1416 ** "allowPendingDelete" controls whether primary selections in the widget are
1417 ** treated as pending delete selections (True), or ignored (False). "event"
1418 ** is optional and is just passed on to the cursor movement callbacks.
1420 void TextInsertAtCursor(Widget w, char *chars, XEvent *event,
1421 int allowPendingDelete, int allowWrap)
1423 int wrapMargin, colNum, lineStartPos, cursorPos;
1424 char *c, *lineStartText, *wrappedText;
1425 TextWidget tw = (TextWidget)w;
1426 textDisp *textD = tw->text.textD;
1427 textBuffer *buf = textD->buffer;
1428 int fontWidth = textD->fontStruct->max_bounds.width;
1429 int replaceSel, singleLine, breakAt = 0;
1431 /* Don't wrap if auto-wrap is off or suppressed, or it's just a newline */
1432 if (!allowWrap || !tw->text.autoWrap ||
1433 (chars[0] == '\n' && chars[1] == '\0')) {
1434 simpleInsertAtCursor(w, chars, event, allowPendingDelete);
1435 return;
1438 /* If this is going to be a pending delete operation, the real insert
1439 position is the start of the selection. This will make rectangular
1440 selections wrap strangely, but this routine should rarely be used for
1441 them, and even more rarely when they need to be wrapped. */
1442 replaceSel = allowPendingDelete && pendingSelection(w);
1443 cursorPos = replaceSel ? buf->primary.start : TextDGetInsertPosition(textD);
1445 /* If the text is only one line and doesn't need to be wrapped, just insert
1446 it and be done (for efficiency only, this routine is called for each
1447 character typed). (Of course, it may not be significantly more efficient
1448 than the more general code below it, so it may be a waste of time!) */
1449 wrapMargin = tw->text.wrapMargin != 0 ? tw->text.wrapMargin :
1450 textD->width / fontWidth;
1451 lineStartPos = BufStartOfLine(buf, cursorPos);
1452 colNum = BufCountDispChars(buf, lineStartPos, cursorPos);
1453 for (c=chars; *c!='\0' && *c!='\n'; c++)
1454 colNum += BufCharWidth(*c, colNum, buf->tabDist, buf->nullSubsChar);
1455 singleLine = *c == '\0';
1456 if (colNum < wrapMargin && singleLine) {
1457 simpleInsertAtCursor(w, chars, event, True);
1458 return;
1461 /* Wrap the text */
1462 lineStartText = BufGetRange(buf, lineStartPos, cursorPos);
1463 wrappedText = wrapText(tw, lineStartText, chars, lineStartPos, wrapMargin,
1464 replaceSel ? NULL : &breakAt);
1465 XtFree(lineStartText);
1467 /* Insert the text. Where possible, use TextDInsert which is optimized
1468 for less redraw. */
1469 if (replaceSel) {
1470 BufReplaceSelected(buf, wrappedText);
1471 TextDSetInsertPosition(textD, buf->cursorPosHint);
1472 } else if (tw->text.overstrike) {
1473 if (breakAt == 0 && singleLine)
1474 TextDOverstrike(textD, wrappedText);
1475 else {
1476 BufReplace(buf, cursorPos-breakAt, cursorPos, wrappedText);
1477 TextDSetInsertPosition(textD, buf->cursorPosHint);
1479 } else {
1480 if (breakAt == 0) {
1481 TextDInsert(textD, wrappedText);
1482 } else {
1483 BufReplace(buf, cursorPos-breakAt, cursorPos, wrappedText);
1484 TextDSetInsertPosition(textD, buf->cursorPosHint);
1487 XtFree(wrappedText);
1488 checkAutoShowInsertPos(w);
1489 callCursorMovementCBs(w, event);
1493 ** Fetch text from the widget's buffer, adding wrapping newlines to emulate
1494 ** effect acheived by wrapping in the text display in continuous wrap mode.
1496 char *TextGetWrapped(Widget w, int startPos, int endPos, int *outLen)
1498 textDisp *textD = ((TextWidget)w)->text.textD;
1499 textBuffer *buf = textD->buffer;
1500 textBuffer *outBuf;
1501 int fromPos, toPos, outPos;
1502 char c, *outString;
1504 if (!((TextWidget)w)->text.continuousWrap || startPos == endPos) {
1505 *outLen = endPos - startPos;
1506 return BufGetRange(buf, startPos, endPos);
1509 /* Create a text buffer with a good estimate of the size that adding
1510 newlines will expand it to. Since it's a text buffer, if we guess
1511 wrong, it will fail softly, and simply expand the size */
1512 outBuf = BufCreatePreallocated((endPos-startPos) + (endPos-startPos)/5);
1513 outPos = 0;
1515 /* Go (displayed) line by line through the buffer, adding newlines where
1516 the text is wrapped at some character other than an existing newline */
1517 fromPos = startPos;
1518 toPos = TextDCountForwardNLines(textD, startPos, 1, False);
1519 while (toPos < endPos) {
1520 BufCopyFromBuf(buf, outBuf, fromPos, toPos, outPos);
1521 outPos += toPos - fromPos;
1522 c = BufGetCharacter(outBuf, outPos-1);
1523 if (c == ' ' || c == '\t')
1524 BufReplace(outBuf, outPos-1, outPos, "\n");
1525 else if (c != '\n') {
1526 BufInsert(outBuf, outPos, "\n");
1527 outPos++;
1529 fromPos = toPos;
1530 toPos = TextDCountForwardNLines(textD, fromPos, 1, True);
1532 BufCopyFromBuf(buf, outBuf, fromPos, endPos, outPos);
1534 /* return the contents of the output buffer as a string */
1535 outString = BufGetAll(outBuf);
1536 *outLen = outBuf->length;
1537 BufFree(outBuf);
1538 return outString;
1542 ** Return the (statically allocated) action table for menu item actions.
1544 ** Warning: This routine can only be used before the first text widget is
1545 ** created! After that, apparently, Xt takes over the table and overwrites
1546 ** it with its own version. XtGetActionList is preferable, but is not
1547 ** available before X11R5.
1549 XtActionsRec *TextGetActions(int *nActions)
1551 *nActions = XtNumber(actionsList);
1552 return actionsList;
1555 static void grabFocusAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
1557 XButtonEvent *e = &event->xbutton;
1558 TextWidget tw = (TextWidget)w;
1559 textDisp *textD = tw->text.textD;
1560 Time lastBtnDown = tw->text.lastBtnDown;
1561 int row, column;
1563 /* Indicate state for future events, PRIMARY_CLICKED indicates that
1564 the proper initialization has been done for primary dragging and/or
1565 multi-clicking. Also record the timestamp for multi-click processing */
1566 tw->text.dragState = PRIMARY_CLICKED;
1567 tw->text.lastBtnDown = e->time;
1569 /* Become owner of the MOTIF_DESTINATION selection, making this widget
1570 the designated recipient of secondary quick actions in Motif XmText
1571 widgets and in other NEdit text widgets */
1572 TakeMotifDestination(w, e->time);
1574 /* Check for possible multi-click sequence in progress */
1575 if (tw->text.multiClickState != NO_CLICKS) {
1576 if (e->time < lastBtnDown + XtGetMultiClickTime(XtDisplay(w))) {
1577 if (tw->text.multiClickState == ONE_CLICK) {
1578 selectWord(w, e->x);
1579 callCursorMovementCBs(w, event);
1580 return;
1581 } else if (tw->text.multiClickState == TWO_CLICKS) {
1582 selectLine(w);
1583 callCursorMovementCBs(w, event);
1584 return;
1585 } else if (tw->text.multiClickState == THREE_CLICKS) {
1586 BufSelect(textD->buffer, 0, textD->buffer->length);
1587 return;
1588 } else if (tw->text.multiClickState > THREE_CLICKS)
1589 tw->text.multiClickState = NO_CLICKS;
1590 } else
1591 tw->text.multiClickState = NO_CLICKS;
1594 /* Clear any existing selections */
1595 BufUnselect(textD->buffer);
1597 /* Move the cursor to the pointer location */
1598 moveDestinationAP(w, event, args, nArgs);
1600 /* Record the site of the initial button press and the initial character
1601 position so subsequent motion events and clicking can decide when and
1602 where to begin a primary selection */
1603 tw->text.btnDownX = e->x;
1604 tw->text.btnDownY = e->y;
1605 tw->text.anchor = TextDGetInsertPosition(textD);
1606 TextDXYToUnconstrainedPosition(textD, e->x, e->y, &row, &column);
1607 column = TextDOffsetWrappedColumn(textD, row, column);
1608 tw->text.rectAnchor = column;
1611 static void moveDestinationAP(Widget w, XEvent *event, String *args,
1612 Cardinal *nArgs)
1614 XButtonEvent *e = &event->xbutton;
1615 textDisp *textD = ((TextWidget)w)->text.textD;
1617 /* Get input focus */
1618 XmProcessTraversal(w, XmTRAVERSE_CURRENT);
1620 /* Move the cursor */
1621 TextDSetInsertPosition(textD, TextDXYToPosition(textD, e->x, e->y));
1622 checkAutoShowInsertPos(w);
1623 callCursorMovementCBs(w, event);
1626 static void extendAdjustAP(Widget w, XEvent *event, String *args,
1627 Cardinal *nArgs)
1629 TextWidget tw = (TextWidget)w;
1630 XMotionEvent *e = &event->xmotion;
1631 int dragState = tw->text.dragState;
1632 int rectDrag = hasKey("rect", args, nArgs);
1634 /* Make sure the proper initialization was done on mouse down */
1635 if (dragState != PRIMARY_DRAG && dragState != PRIMARY_CLICKED &&
1636 dragState != PRIMARY_RECT_DRAG)
1637 return;
1639 /* If the selection hasn't begun, decide whether the mouse has moved
1640 far enough from the initial mouse down to be considered a drag */
1641 if (tw->text.dragState == PRIMARY_CLICKED) {
1642 if (abs(e->x - tw->text.btnDownX) > SELECT_THRESHOLD ||
1643 abs(e->y - tw->text.btnDownY) > SELECT_THRESHOLD)
1644 tw->text.dragState = rectDrag ? PRIMARY_RECT_DRAG : PRIMARY_DRAG;
1645 else
1646 return;
1649 /* If "rect" argument has appeared or disappeared, keep dragState up
1650 to date about which type of drag this is */
1651 tw->text.dragState = rectDrag ? PRIMARY_RECT_DRAG : PRIMARY_DRAG;
1653 /* Record the new position for the autoscrolling timer routine, and
1654 engage or disengage the timer if the mouse is in/out of the window */
1655 checkAutoScroll(tw, e->x, e->y);
1657 /* Adjust the selection and move the cursor */
1658 adjustSelection(tw, e->x, e->y);
1661 static void extendStartAP(Widget w, XEvent *event, String *args,
1662 Cardinal *nArgs)
1664 XMotionEvent *e = &event->xmotion;
1665 textDisp *textD = ((TextWidget)w)->text.textD;
1666 textBuffer *buf = textD->buffer;
1667 selection *sel = &buf->primary;
1668 int anchor, rectAnchor, anchorLineStart, newPos, row, column;
1670 /* Find the new anchor point for the rest of this drag operation */
1671 newPos = TextDXYToPosition(textD, e->x, e->y);
1672 TextDXYToUnconstrainedPosition(textD, e->x, e->y, &row, &column);
1673 column = TextDOffsetWrappedColumn(textD, row, column);
1674 if (sel->selected) {
1675 if (sel->rectangular) {
1676 rectAnchor = column < (sel->rectEnd + sel->rectStart) / 2 ?
1677 sel->rectEnd : sel->rectStart;
1678 anchorLineStart = BufStartOfLine(buf, newPos <
1679 (sel->end + sel->start) / 2 ? sel->end : sel->start);
1680 anchor = BufCountForwardDispChars(buf, anchorLineStart, rectAnchor);
1681 } else {
1682 if (abs(newPos - sel->start) < abs(newPos - sel->end))
1683 anchor = sel->end;
1684 else
1685 anchor = sel->start;
1686 anchorLineStart = BufStartOfLine(buf, anchor);
1687 rectAnchor = BufCountDispChars(buf, anchorLineStart, anchor);
1689 } else {
1690 anchor = TextDGetInsertPosition(textD);
1691 anchorLineStart = BufStartOfLine(buf, anchor);
1692 rectAnchor = BufCountDispChars(buf, anchorLineStart, anchor);
1694 ((TextWidget)w)->text.anchor = anchor;
1695 ((TextWidget)w)->text.rectAnchor = rectAnchor;
1697 /* Make the new selection */
1698 if (hasKey("rect", args, nArgs))
1699 BufRectSelect(buf, BufStartOfLine(buf, min(anchor, newPos)),
1700 BufEndOfLine(buf, max(anchor, newPos)),
1701 min(rectAnchor, column), max(rectAnchor, column));
1702 else
1703 BufSelect(buf, min(anchor, newPos), max(anchor, newPos));
1705 /* Never mind the motion threshold, go right to dragging since
1706 extend-start is unambiguously the start of a selection */
1707 ((TextWidget)w)->text.dragState = PRIMARY_DRAG;
1709 /* Don't do by-word or by-line adjustment, just by character */
1710 ((TextWidget)w)->text.multiClickState = NO_CLICKS;
1712 /* Move the cursor */
1713 TextDSetInsertPosition(textD, newPos);
1714 callCursorMovementCBs(w, event);
1717 static void extendEndAP(Widget w, XEvent *event, String *args,
1718 Cardinal *nArgs)
1720 XButtonEvent *e = &event->xbutton;
1721 TextWidget tw = (TextWidget)w;
1723 if (tw->text.dragState == PRIMARY_CLICKED &&
1724 tw->text.lastBtnDown <= e->time + XtGetMultiClickTime(XtDisplay(w)))
1725 tw->text.multiClickState++;
1726 endDrag(w);
1729 static void processCancelAP(Widget w, XEvent *event, String *args,
1730 Cardinal *nArgs)
1732 int dragState = ((TextWidget)w)->text.dragState;
1733 textBuffer *buf = ((TextWidget)w)->text.textD->buffer;
1734 textDisp *textD = ((TextWidget)w)->text.textD;
1736 /* If there's a calltip displayed, kill it. */
1737 TextDKillCalltip(textD, 0);
1739 if (dragState == PRIMARY_DRAG || dragState == PRIMARY_RECT_DRAG)
1740 BufUnselect(buf);
1741 cancelDrag(w);
1744 static void secondaryStartAP(Widget w, XEvent *event, String *args,
1745 Cardinal *nArgs)
1747 XMotionEvent *e = &event->xmotion;
1748 textDisp *textD = ((TextWidget)w)->text.textD;
1749 textBuffer *buf = textD->buffer;
1750 selection *sel = &buf->secondary;
1751 int anchor, pos, row, column;
1753 /* Find the new anchor point and make the new selection */
1754 pos = TextDXYToPosition(textD, e->x, e->y);
1755 if (sel->selected) {
1756 if (abs(pos - sel->start) < abs(pos - sel->end))
1757 anchor = sel->end;
1758 else
1759 anchor = sel->start;
1760 BufSecondarySelect(buf, anchor, pos);
1761 } else
1762 anchor = pos;
1764 /* Record the site of the initial button press and the initial character
1765 position so subsequent motion events can decide when to begin a
1766 selection, (and where the selection began) */
1767 ((TextWidget)w)->text.btnDownX = e->x;
1768 ((TextWidget)w)->text.btnDownY = e->y;
1769 ((TextWidget)w)->text.anchor = pos;
1770 TextDXYToUnconstrainedPosition(textD, e->x, e->y, &row, &column);
1771 column = TextDOffsetWrappedColumn(textD, row, column);
1772 ((TextWidget)w)->text.rectAnchor = column;
1773 ((TextWidget)w)->text.dragState = SECONDARY_CLICKED;
1776 static void secondaryOrDragStartAP(Widget w, XEvent *event, String *args,
1777 Cardinal *nArgs)
1779 XMotionEvent *e = &event->xmotion;
1780 textDisp *textD = ((TextWidget)w)->text.textD;
1781 textBuffer *buf = textD->buffer;
1783 /* If the click was outside of the primary selection, this is not
1784 a drag, start a secondary selection */
1785 if (!buf->primary.selected || !TextDInSelection(textD, e->x, e->y)) {
1786 secondaryStartAP(w, event, args, nArgs);
1787 return;
1790 if (checkReadOnly(w))
1791 return;
1793 /* Record the site of the initial button press and the initial character
1794 position so subsequent motion events can decide when to begin a
1795 drag, and where to drag to */
1796 ((TextWidget)w)->text.btnDownX = e->x;
1797 ((TextWidget)w)->text.btnDownY = e->y;
1798 ((TextWidget)w)->text.dragState = CLICKED_IN_SELECTION;
1801 static void secondaryAdjustAP(Widget w, XEvent *event, String *args,
1802 Cardinal *nArgs)
1804 TextWidget tw = (TextWidget)w;
1805 XMotionEvent *e = &event->xmotion;
1806 int dragState = tw->text.dragState;
1807 int rectDrag = hasKey("rect", args, nArgs);
1809 /* Make sure the proper initialization was done on mouse down */
1810 if (dragState != SECONDARY_DRAG && dragState != SECONDARY_RECT_DRAG &&
1811 dragState != SECONDARY_CLICKED)
1812 return;
1814 /* If the selection hasn't begun, decide whether the mouse has moved
1815 far enough from the initial mouse down to be considered a drag */
1816 if (tw->text.dragState == SECONDARY_CLICKED) {
1817 if (abs(e->x - tw->text.btnDownX) > SELECT_THRESHOLD ||
1818 abs(e->y - tw->text.btnDownY) > SELECT_THRESHOLD)
1819 tw->text.dragState = rectDrag ? SECONDARY_RECT_DRAG: SECONDARY_DRAG;
1820 else
1821 return;
1824 /* If "rect" argument has appeared or disappeared, keep dragState up
1825 to date about which type of drag this is */
1826 tw->text.dragState = rectDrag ? SECONDARY_RECT_DRAG : SECONDARY_DRAG;
1828 /* Record the new position for the autoscrolling timer routine, and
1829 engage or disengage the timer if the mouse is in/out of the window */
1830 checkAutoScroll(tw, e->x, e->y);
1832 /* Adjust the selection */
1833 adjustSecondarySelection(tw, e->x, e->y);
1836 static void secondaryOrDragAdjustAP(Widget w, XEvent *event, String *args,
1837 Cardinal *nArgs)
1839 TextWidget tw = (TextWidget)w;
1840 XMotionEvent *e = &event->xmotion;
1841 int dragState = tw->text.dragState;
1843 /* Only dragging of blocks of text is handled in this action proc.
1844 Otherwise, defer to secondaryAdjust to handle the rest */
1845 if (dragState != CLICKED_IN_SELECTION && dragState != PRIMARY_BLOCK_DRAG) {
1846 secondaryAdjustAP(w, event, args, nArgs);
1847 return;
1850 /* Decide whether the mouse has moved far enough from the
1851 initial mouse down to be considered a drag */
1852 if (tw->text.dragState == CLICKED_IN_SELECTION) {
1853 if (abs(e->x - tw->text.btnDownX) > SELECT_THRESHOLD ||
1854 abs(e->y - tw->text.btnDownY) > SELECT_THRESHOLD)
1855 BeginBlockDrag(tw);
1856 else
1857 return;
1860 /* Record the new position for the autoscrolling timer routine, and
1861 engage or disengage the timer if the mouse is in/out of the window */
1862 checkAutoScroll(tw, e->x, e->y);
1864 /* Adjust the selection */
1865 BlockDragSelection(tw, e->x, e->y, hasKey("overlay", args, nArgs) ?
1866 (hasKey("copy", args, nArgs) ? DRAG_OVERLAY_COPY : DRAG_OVERLAY_MOVE) :
1867 (hasKey("copy", args, nArgs) ? DRAG_COPY : DRAG_MOVE));
1870 static void copyToAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
1872 XButtonEvent *e = &event->xbutton;
1873 TextWidget tw = (TextWidget)w;
1874 textDisp *textD = tw->text.textD;
1875 int dragState = tw->text.dragState;
1876 textBuffer *buf = textD->buffer;
1877 selection *secondary = &buf->secondary, *primary = &buf->primary;
1878 int rectangular = secondary->rectangular;
1879 char *textToCopy;
1880 int insertPos, lineStart, column;
1882 endDrag(w);
1883 if (!((dragState == SECONDARY_DRAG && secondary->selected) ||
1884 (dragState == SECONDARY_RECT_DRAG && secondary->selected) ||
1885 dragState == SECONDARY_CLICKED || dragState == NOT_CLICKED))
1886 return;
1887 if (!(secondary->selected && !((TextWidget)w)->text.motifDestOwner)) {
1888 if (checkReadOnly(w)) {
1889 BufSecondaryUnselect(buf);
1890 return;
1893 if (secondary->selected) {
1894 if (tw->text.motifDestOwner) {
1895 TextDBlankCursor(textD);
1896 textToCopy = BufGetSecSelectText(buf);
1897 if (primary->selected && rectangular) {
1898 insertPos = TextDGetInsertPosition(textD);
1899 BufReplaceSelected(buf, textToCopy);
1900 TextDSetInsertPosition(textD, buf->cursorPosHint);
1901 } else if (rectangular) {
1902 insertPos = TextDGetInsertPosition(textD);
1903 lineStart = BufStartOfLine(buf, insertPos);
1904 column = BufCountDispChars(buf, lineStart, insertPos);
1905 BufInsertCol(buf, column, lineStart, textToCopy, NULL, NULL);
1906 TextDSetInsertPosition(textD, buf->cursorPosHint);
1907 } else
1908 TextInsertAtCursor(w, textToCopy, event, True,
1909 tw->text.autoWrapPastedText);
1910 XtFree(textToCopy);
1911 BufSecondaryUnselect(buf);
1912 TextDUnblankCursor(textD);
1913 } else
1914 SendSecondarySelection(w, e->time, False);
1915 } else if (primary->selected) {
1916 textToCopy = BufGetSelectionText(buf);
1917 TextDSetInsertPosition(textD, TextDXYToPosition(textD, e->x, e->y));
1918 TextInsertAtCursor(w, textToCopy, event, False,
1919 tw->text.autoWrapPastedText);
1920 XtFree(textToCopy);
1921 } else {
1922 TextDSetInsertPosition(textD, TextDXYToPosition(textD, e->x, e->y));
1923 InsertPrimarySelection(w, e->time, False);
1927 static void copyToOrEndDragAP(Widget w, XEvent *event, String *args,
1928 Cardinal *nArgs)
1930 int dragState = ((TextWidget)w)->text.dragState;
1932 if (dragState != PRIMARY_BLOCK_DRAG) {
1933 copyToAP(w, event, args, nArgs);
1934 return;
1937 FinishBlockDrag((TextWidget)w);
1940 static void moveToAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
1942 XButtonEvent *e = &event->xbutton;
1943 textDisp *textD = ((TextWidget)w)->text.textD;
1944 int dragState = ((TextWidget)w)->text.dragState;
1945 textBuffer *buf = textD->buffer;
1946 selection *secondary = &buf->secondary, *primary = &buf->primary;
1947 int insertPos, rectangular = secondary->rectangular;
1948 int column, lineStart;
1949 char *textToCopy;
1951 endDrag(w);
1952 if (!((dragState == SECONDARY_DRAG && secondary->selected) ||
1953 (dragState == SECONDARY_RECT_DRAG && secondary->selected) ||
1954 dragState == SECONDARY_CLICKED || dragState == NOT_CLICKED))
1955 return;
1956 if (checkReadOnly(w)) {
1957 BufSecondaryUnselect(buf);
1958 return;
1961 if (secondary->selected) {
1962 if (((TextWidget)w)->text.motifDestOwner) {
1963 textToCopy = BufGetSecSelectText(buf);
1964 if (primary->selected && rectangular) {
1965 insertPos = TextDGetInsertPosition(textD);
1966 BufReplaceSelected(buf, textToCopy);
1967 TextDSetInsertPosition(textD, buf->cursorPosHint);
1968 } else if (rectangular) {
1969 insertPos = TextDGetInsertPosition(textD);
1970 lineStart = BufStartOfLine(buf, insertPos);
1971 column = BufCountDispChars(buf, lineStart, insertPos);
1972 BufInsertCol(buf, column, lineStart, textToCopy, NULL, NULL);
1973 TextDSetInsertPosition(textD, buf->cursorPosHint);
1974 } else
1975 TextInsertAtCursor(w, textToCopy, event, True,
1976 ((TextWidget)w)->text.autoWrapPastedText);
1977 XtFree(textToCopy);
1978 BufRemoveSecSelect(buf);
1979 BufSecondaryUnselect(buf);
1980 } else
1981 SendSecondarySelection(w, e->time, True);
1982 } else if (primary->selected) {
1983 textToCopy = BufGetRange(buf, primary->start, primary->end);
1984 TextDSetInsertPosition(textD, TextDXYToPosition(textD, e->x, e->y));
1985 TextInsertAtCursor(w, textToCopy, event, False,
1986 ((TextWidget)w)->text.autoWrapPastedText);
1987 XtFree(textToCopy);
1988 BufRemoveSelected(buf);
1989 BufUnselect(buf);
1990 } else {
1991 TextDSetInsertPosition(textD, TextDXYToPosition(textD, e->x, e->y));
1992 MovePrimarySelection(w, e->time, False);
1996 static void moveToOrEndDragAP(Widget w, XEvent *event, String *args,
1997 Cardinal *nArgs)
1999 int dragState = ((TextWidget)w)->text.dragState;
2001 if (dragState != PRIMARY_BLOCK_DRAG) {
2002 moveToAP(w, event, args, nArgs);
2003 return;
2006 FinishBlockDrag((TextWidget)w);
2009 static void endDragAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2011 if (((TextWidget)w)->text.dragState == PRIMARY_BLOCK_DRAG)
2012 FinishBlockDrag((TextWidget)w);
2013 else
2014 endDrag(w);
2017 static void exchangeAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2019 XButtonEvent *e = &event->xbutton;
2020 textDisp *textD = ((TextWidget)w)->text.textD;
2021 textBuffer *buf = textD->buffer;
2022 selection *sec = &buf->secondary, *primary = &buf->primary;
2023 char *primaryText, *secText;
2024 int newPrimaryStart, newPrimaryEnd, secWasRect;
2025 int dragState = ((TextWidget)w)->text.dragState; /* save before endDrag */
2026 int silent = hasKey("nobell", args, nArgs);
2028 endDrag(w);
2029 if (checkReadOnly(w))
2030 return;
2032 /* If there's no secondary selection here, or the primary and secondary
2033 selection overlap, just beep and return */
2034 if (!sec->selected || (primary->selected &&
2035 ((primary->start <= sec->start && primary->end > sec->start) ||
2036 (sec->start <= primary->start && sec->end > primary->start))))
2038 BufSecondaryUnselect(buf);
2039 ringIfNecessary(silent, w);
2040 /* If there's no secondary selection, but the primary selection is
2041 being dragged, we must not forget to finish the dragging.
2042 Otherwise, modifications aren't recorded. */
2043 if (dragState == PRIMARY_BLOCK_DRAG)
2044 FinishBlockDrag((TextWidget)w);
2045 return;
2048 /* if the primary selection is in another widget, use selection routines */
2049 if (!primary->selected) {
2050 ExchangeSelections(w, e->time);
2051 return;
2054 /* Both primary and secondary are in this widget, do the exchange here */
2055 primaryText = BufGetSelectionText(buf);
2056 secText = BufGetSecSelectText(buf);
2057 secWasRect = sec->rectangular;
2058 BufReplaceSecSelect(buf, primaryText);
2059 newPrimaryStart = primary->start;
2060 BufReplaceSelected(buf, secText);
2061 newPrimaryEnd = newPrimaryStart + strlen(secText);
2062 XtFree(primaryText);
2063 XtFree(secText);
2064 BufSecondaryUnselect(buf);
2065 if (secWasRect) {
2066 TextDSetInsertPosition(textD, buf->cursorPosHint);
2067 } else {
2068 BufSelect(buf, newPrimaryStart, newPrimaryEnd);
2069 TextDSetInsertPosition(textD, newPrimaryEnd);
2071 checkAutoShowInsertPos(w);
2074 static void copyPrimaryAP(Widget w, XEvent *event, String *args,
2075 Cardinal *nArgs)
2077 XKeyEvent *e = &event->xkey;
2078 TextWidget tw = (TextWidget)w;
2079 textDisp *textD = tw->text.textD;
2080 textBuffer *buf = textD->buffer;
2081 selection *primary = &buf->primary;
2082 int rectangular = hasKey("rect", args, nArgs);
2083 char *textToCopy;
2084 int insertPos, col;
2086 cancelDrag(w);
2087 if (checkReadOnly(w))
2088 return;
2089 if (primary->selected && rectangular) {
2090 textToCopy = BufGetSelectionText(buf);
2091 insertPos = TextDGetInsertPosition(textD);
2092 col = BufCountDispChars(buf, BufStartOfLine(buf, insertPos), insertPos);
2093 BufInsertCol(buf, col, insertPos, textToCopy, NULL, NULL);
2094 TextDSetInsertPosition(textD, buf->cursorPosHint);
2095 XtFree(textToCopy);
2096 checkAutoShowInsertPos(w);
2097 } else if (primary->selected) {
2098 textToCopy = BufGetSelectionText(buf);
2099 insertPos = TextDGetInsertPosition(textD);
2100 BufInsert(buf, insertPos, textToCopy);
2101 TextDSetInsertPosition(textD, insertPos + strlen(textToCopy));
2102 XtFree(textToCopy);
2103 checkAutoShowInsertPos(w);
2104 } else if (rectangular) {
2105 if (!TextDPositionToXY(textD, TextDGetInsertPosition(textD),
2106 &tw->text.btnDownX, &tw->text.btnDownY))
2107 return; /* shouldn't happen */
2108 InsertPrimarySelection(w, e->time, True);
2109 } else
2110 InsertPrimarySelection(w, e->time, False);
2113 static void cutPrimaryAP(Widget w, XEvent *event, String *args,
2114 Cardinal *nArgs)
2116 XKeyEvent *e = &event->xkey;
2117 textDisp *textD = ((TextWidget)w)->text.textD;
2118 textBuffer *buf = textD->buffer;
2119 selection *primary = &buf->primary;
2120 char *textToCopy;
2121 int rectangular = hasKey("rect", args, nArgs);
2122 int insertPos, col;
2124 cancelDrag(w);
2125 if (checkReadOnly(w))
2126 return;
2127 if (primary->selected && rectangular) {
2128 textToCopy = BufGetSelectionText(buf);
2129 insertPos = TextDGetInsertPosition(textD);
2130 col = BufCountDispChars(buf, BufStartOfLine(buf, insertPos), insertPos);
2131 BufInsertCol(buf, col, insertPos, textToCopy, NULL, NULL);
2132 TextDSetInsertPosition(textD, buf->cursorPosHint);
2133 XtFree(textToCopy);
2134 BufRemoveSelected(buf);
2135 checkAutoShowInsertPos(w);
2136 } else if (primary->selected) {
2137 textToCopy = BufGetSelectionText(buf);
2138 insertPos = TextDGetInsertPosition(textD);
2139 BufInsert(buf, insertPos, textToCopy);
2140 TextDSetInsertPosition(textD, insertPos + strlen(textToCopy));
2141 XtFree(textToCopy);
2142 BufRemoveSelected(buf);
2143 checkAutoShowInsertPos(w);
2144 } else if (rectangular) {
2145 if (!TextDPositionToXY(textD, TextDGetInsertPosition(textD),
2146 &((TextWidget)w)->text.btnDownX,
2147 &((TextWidget)w)->text.btnDownY))
2148 return; /* shouldn't happen */
2149 MovePrimarySelection(w, e->time, True);
2150 } else {
2151 MovePrimarySelection(w, e->time, False);
2155 static void mousePanAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2157 XButtonEvent *e = &event->xbutton;
2158 TextWidget tw = (TextWidget)w;
2159 textDisp *textD = tw->text.textD;
2160 int lineHeight = textD->ascent + textD->descent;
2161 int topLineNum, horizOffset;
2162 static Cursor panCursor = 0;
2164 if (tw->text.dragState == MOUSE_PAN) {
2165 TextDSetScroll(textD,
2166 (tw->text.btnDownY - e->y + lineHeight/2) / lineHeight,
2167 tw->text.btnDownX - e->x);
2168 } else if (tw->text.dragState == NOT_CLICKED) {
2169 TextDGetScroll(textD, &topLineNum, &horizOffset);
2170 tw->text.btnDownX = e->x + horizOffset;
2171 tw->text.btnDownY = e->y + topLineNum * lineHeight;
2172 tw->text.dragState = MOUSE_PAN;
2173 if (!panCursor)
2174 panCursor = XCreateFontCursor(XtDisplay(w), XC_fleur);
2175 XGrabPointer(XtDisplay(w), XtWindow(w), False,
2176 ButtonMotionMask | ButtonReleaseMask, GrabModeAsync,
2177 GrabModeAsync, None, panCursor, CurrentTime);
2178 } else
2179 cancelDrag(w);
2182 static void pasteClipboardAP(Widget w, XEvent *event, String *args,
2183 Cardinal *nArgs)
2185 if (hasKey("rect", args, nArgs))
2186 TextColPasteClipboard(w, event->xkey.time);
2187 else
2188 TextPasteClipboard(w, event->xkey.time);
2191 static void copyClipboardAP(Widget w, XEvent *event, String *args,
2192 Cardinal *nArgs)
2194 TextCopyClipboard(w, event->xkey.time);
2197 static void cutClipboardAP(Widget w, XEvent *event, String *args,
2198 Cardinal *nArgs)
2200 TextCutClipboard(w, event->xkey.time);
2203 static void insertStringAP(Widget w, XEvent *event, String *args,
2204 Cardinal *nArgs)
2206 smartIndentCBStruct smartIndent;
2207 textDisp *textD = ((TextWidget)w)->text.textD;
2209 if (*nArgs == 0)
2210 return;
2211 cancelDrag(w);
2212 if (checkReadOnly(w))
2213 return;
2214 if (((TextWidget)w)->text.smartIndent) {
2215 smartIndent.reason = CHAR_TYPED;
2216 smartIndent.pos = TextDGetInsertPosition(textD);
2217 smartIndent.indentRequest = 0;
2218 smartIndent.charsTyped = args[0];
2219 XtCallCallbacks(w, textNsmartIndentCallback, (XtPointer)&smartIndent);
2221 TextInsertAtCursor(w, args[0], event, True, True);
2222 BufUnselect((((TextWidget)w)->text.textD)->buffer);
2225 static void selfInsertAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2227 WindowInfo* window = WidgetToWindow(w);
2229 #ifdef NO_XMIM
2230 static XComposeStatus compose = {NULL, 0};
2231 #else
2232 int status;
2233 #endif
2234 XKeyEvent *e = &event->xkey;
2235 char chars[20];
2236 KeySym keysym;
2237 int nChars;
2238 smartIndentCBStruct smartIndent;
2239 textDisp *textD = ((TextWidget)w)->text.textD;
2241 #ifdef NO_XMIM
2242 nChars = XLookupString(&event->xkey, chars, 19, &keysym, &compose);
2243 if (nChars == 0)
2244 return;
2245 #else
2246 nChars = XmImMbLookupString(w, &event->xkey, chars, 19, &keysym,
2247 &status);
2248 if (nChars == 0 || status == XLookupNone ||
2249 status == XLookupKeySym || status == XBufferOverflow)
2250 return;
2251 #endif
2252 cancelDrag(w);
2253 if (checkReadOnly(w))
2254 return;
2255 TakeMotifDestination(w, e->time);
2256 chars[nChars] = '\0';
2258 if (!BufSubstituteNullChars(chars, nChars, window->buffer)) {
2259 DialogF(DF_ERR, window->shell, 1, "Error", "Too much binary data", "OK");
2260 return;
2263 /* If smart indent is on, call the smart indent callback to check the
2264 inserted character */
2265 if (((TextWidget)w)->text.smartIndent) {
2266 smartIndent.reason = CHAR_TYPED;
2267 smartIndent.pos = TextDGetInsertPosition(textD);
2268 smartIndent.indentRequest = 0;
2269 smartIndent.charsTyped = chars;
2270 XtCallCallbacks(w, textNsmartIndentCallback, (XtPointer)&smartIndent);
2272 TextInsertAtCursor(w, chars, event, True, True);
2273 BufUnselect(textD->buffer);
2276 static void newlineAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2278 if (((TextWidget)w)->text.autoIndent || ((TextWidget)w)->text.smartIndent)
2279 newlineAndIndentAP(w, event, args, nArgs);
2280 else
2281 newlineNoIndentAP(w, event, args, nArgs);
2284 static void newlineNoIndentAP(Widget w, XEvent *event, String *args,
2285 Cardinal *nArgs)
2287 XKeyEvent *e = &event->xkey;
2289 cancelDrag(w);
2290 if (checkReadOnly(w))
2291 return;
2292 TakeMotifDestination(w, e->time);
2293 simpleInsertAtCursor(w, "\n", event, True);
2294 BufUnselect((((TextWidget)w)->text.textD)->buffer);
2297 static void newlineAndIndentAP(Widget w, XEvent *event, String *args,
2298 Cardinal *nArgs)
2300 XKeyEvent *e = &event->xkey;
2301 TextWidget tw = (TextWidget)w;
2302 textDisp *textD = tw->text.textD;
2303 textBuffer *buf = textD->buffer;
2304 char *indentStr;
2305 int cursorPos, lineStartPos, column;
2307 if (checkReadOnly(w))
2308 return;
2309 cancelDrag(w);
2310 TakeMotifDestination(w, e->time);
2312 /* Create a string containing a newline followed by auto or smart
2313 indent string */
2314 cursorPos = TextDGetInsertPosition(textD);
2315 lineStartPos = BufStartOfLine(buf, cursorPos);
2316 indentStr = createIndentString(tw, buf, 0, lineStartPos,
2317 cursorPos, NULL, &column);
2319 /* Insert it at the cursor */
2320 simpleInsertAtCursor(w, indentStr, event, True);
2321 XtFree(indentStr);
2323 if (tw->text.emulateTabs > 0) {
2324 /* If emulated tabs are on, make the inserted indent deletable by
2325 tab. Round this up by faking the column a bit to the right to
2326 let the user delete half-tabs with one keypress. */
2327 column += tw->text.emulateTabs - 1;
2328 tw->text.emTabsBeforeCursor = column / tw->text.emulateTabs;
2331 BufUnselect(buf);
2334 static void processTabAP(Widget w, XEvent *event, String *args,
2335 Cardinal *nArgs)
2337 textDisp *textD = ((TextWidget)w)->text.textD;
2338 textBuffer *buf = textD->buffer;
2339 selection *sel = &buf->primary;
2340 int emTabDist = ((TextWidget)w)->text.emulateTabs;
2341 int emTabsBeforeCursor = ((TextWidget)w)->text.emTabsBeforeCursor;
2342 int insertPos, indent, startIndent, toIndent, lineStart, tabWidth;
2343 char *outStr, *outPtr;
2345 if (checkReadOnly(w))
2346 return;
2347 cancelDrag(w);
2348 TakeMotifDestination(w, event->xkey.time);
2350 /* If emulated tabs are off, just insert a tab */
2351 if (emTabDist <= 0) {
2352 TextInsertAtCursor(w, "\t", event, True, True);
2353 return;
2356 /* Find the starting and ending indentation. If the tab is to
2357 replace an existing selection, use the start of the selection
2358 instead of the cursor position as the indent. When replacing
2359 rectangular selections, tabs are automatically recalculated as
2360 if the inserted text began at the start of the line */
2361 insertPos = pendingSelection(w) ?
2362 sel->start : TextDGetInsertPosition(textD);
2363 lineStart = BufStartOfLine(buf, insertPos);
2364 if (pendingSelection(w) && sel->rectangular)
2365 insertPos = BufCountForwardDispChars(buf, lineStart, sel->rectStart);
2366 startIndent = BufCountDispChars(buf, lineStart, insertPos);
2367 toIndent = startIndent + emTabDist - (startIndent % emTabDist);
2368 if (pendingSelection(w) && sel->rectangular) {
2369 toIndent -= startIndent;
2370 startIndent = 0;
2373 /* Allocate a buffer assuming all the inserted characters will be spaces */
2374 outStr = XtMalloc(toIndent - startIndent + 1);
2376 /* Add spaces and tabs to outStr until it reaches toIndent */
2377 outPtr = outStr;
2378 indent = startIndent;
2379 while (indent < toIndent) {
2380 tabWidth = BufCharWidth('\t', indent, buf->tabDist, buf->nullSubsChar);
2381 if (buf->useTabs && tabWidth > 1 && indent + tabWidth <= toIndent) {
2382 *outPtr++ = '\t';
2383 indent += tabWidth;
2384 } else {
2385 *outPtr++ = ' ';
2386 indent++;
2389 *outPtr = '\0';
2391 /* Insert the emulated tab */
2392 TextInsertAtCursor(w, outStr, event, True, True);
2393 XtFree(outStr);
2395 /* Restore and ++ emTabsBeforeCursor cleared by TextInsertAtCursor */
2396 ((TextWidget)w)->text.emTabsBeforeCursor = emTabsBeforeCursor + 1;
2398 BufUnselect(buf);
2401 static void deleteSelectionAP(Widget w, XEvent *event, String *args,
2402 Cardinal *nArgs)
2404 XKeyEvent *e = &event->xkey;
2406 cancelDrag(w);
2407 if (checkReadOnly(w))
2408 return;
2409 TakeMotifDestination(w, e->time);
2410 deletePendingSelection(w, event);
2413 static void deletePreviousCharacterAP(Widget w, XEvent *event, String *args,
2414 Cardinal *nArgs)
2416 XKeyEvent *e = &event->xkey;
2417 textDisp *textD = ((TextWidget)w)->text.textD;
2418 int insertPos = TextDGetInsertPosition(textD);
2419 char c;
2420 int silent = hasKey("nobell", args, nArgs);
2422 cancelDrag(w);
2423 if (checkReadOnly(w))
2424 return;
2426 TakeMotifDestination(w, e->time);
2427 if (deletePendingSelection(w, event))
2428 return;
2430 if (insertPos == 0) {
2431 ringIfNecessary(silent, w);
2432 return;
2435 if (deleteEmulatedTab(w, event))
2436 return;
2438 if (((TextWidget)w)->text.overstrike) {
2439 c = BufGetCharacter(textD->buffer, insertPos - 1);
2440 if (c == '\n')
2441 BufRemove(textD->buffer, insertPos - 1, insertPos);
2442 else if (c != '\t')
2443 BufReplace(textD->buffer, insertPos - 1, insertPos, " ");
2444 } else {
2445 BufRemove(textD->buffer, insertPos - 1, insertPos);
2448 TextDSetInsertPosition(textD, insertPos - 1);
2449 checkAutoShowInsertPos(w);
2450 callCursorMovementCBs(w, event);
2453 static void deleteNextCharacterAP(Widget w, XEvent *event, String *args,
2454 Cardinal *nArgs)
2456 XKeyEvent *e = &event->xkey;
2457 textDisp *textD = ((TextWidget)w)->text.textD;
2458 int insertPos = TextDGetInsertPosition(textD);
2459 int silent = hasKey("nobell", args, nArgs);
2461 cancelDrag(w);
2462 if (checkReadOnly(w))
2463 return;
2464 TakeMotifDestination(w, e->time);
2465 if (deletePendingSelection(w, event))
2466 return;
2467 if (insertPos == textD->buffer->length) {
2468 ringIfNecessary(silent, w);
2469 return;
2471 BufRemove(textD->buffer, insertPos , insertPos + 1);
2472 checkAutoShowInsertPos(w);
2473 callCursorMovementCBs(w, event);
2476 static void deletePreviousWordAP(Widget w, XEvent *event, String *args,
2477 Cardinal *nArgs)
2479 XKeyEvent *e = &event->xkey;
2480 textDisp *textD = ((TextWidget)w)->text.textD;
2481 int insertPos = TextDGetInsertPosition(textD);
2482 int pos, lineStart = BufStartOfLine(textD->buffer, insertPos);
2483 char *delimiters = ((TextWidget)w)->text.delimiters;
2484 int silent = hasKey("nobell", args, nArgs);
2486 cancelDrag(w);
2487 if (checkReadOnly(w)) {
2488 return;
2491 TakeMotifDestination(w, e->time);
2492 if (deletePendingSelection(w, event)) {
2493 return;
2496 if (insertPos == lineStart) {
2497 ringIfNecessary(silent, w);
2498 return;
2501 pos = max(insertPos - 1, 0);
2502 while (strchr(delimiters, BufGetCharacter(textD->buffer, pos)) != NULL &&
2503 pos != lineStart) {
2504 pos--;
2507 pos = startOfWord((TextWidget)w, pos);
2508 BufRemove(textD->buffer, pos, insertPos);
2509 checkAutoShowInsertPos(w);
2510 callCursorMovementCBs(w, event);
2513 static void deleteNextWordAP(Widget w, XEvent *event, String *args,
2514 Cardinal *nArgs)
2516 XKeyEvent *e = &event->xkey;
2517 textDisp *textD = ((TextWidget)w)->text.textD;
2518 int insertPos = TextDGetInsertPosition(textD);
2519 int pos, lineEnd = BufEndOfLine(textD->buffer, insertPos);
2520 char *delimiters = ((TextWidget)w)->text.delimiters;
2521 int silent = hasKey("nobell", args, nArgs);
2523 cancelDrag(w);
2524 if (checkReadOnly(w)) {
2525 return;
2528 TakeMotifDestination(w, e->time);
2529 if (deletePendingSelection(w, event)) {
2530 return;
2533 if (insertPos == lineEnd) {
2534 ringIfNecessary(silent, w);
2535 return;
2538 pos = insertPos;
2539 while (strchr(delimiters, BufGetCharacter(textD->buffer, pos)) != NULL &&
2540 pos != lineEnd) {
2541 pos++;
2544 pos = endOfWord((TextWidget)w, pos);
2545 BufRemove(textD->buffer, insertPos, pos);
2546 checkAutoShowInsertPos(w);
2547 callCursorMovementCBs(w, event);
2550 static void deleteToEndOfLineAP(Widget w, XEvent *event, String *args,
2551 Cardinal *nArgs)
2553 XKeyEvent *e = &event->xkey;
2554 textDisp *textD = ((TextWidget)w)->text.textD;
2555 int insertPos = TextDGetInsertPosition(textD);
2556 int endOfLine;
2557 int silent = 0;
2559 silent = hasKey("nobell", args, nArgs);
2560 if (hasKey("absolute", args, nArgs))
2561 endOfLine = BufEndOfLine(textD->buffer, insertPos);
2562 else
2563 endOfLine = TextDEndOfLine(textD, insertPos, False);
2564 cancelDrag(w);
2565 if (checkReadOnly(w))
2566 return;
2567 TakeMotifDestination(w, e->time);
2568 if (deletePendingSelection(w, event))
2569 return;
2570 if (insertPos == endOfLine) {
2571 ringIfNecessary(silent, w);
2572 return;
2574 BufRemove(textD->buffer, insertPos, endOfLine);
2575 checkAutoShowInsertPos(w);
2576 callCursorMovementCBs(w, event);
2579 static void deleteToStartOfLineAP(Widget w, XEvent *event, String *args,
2580 Cardinal *nArgs)
2582 XKeyEvent *e = &event->xkey;
2583 textDisp *textD = ((TextWidget)w)->text.textD;
2584 int insertPos = TextDGetInsertPosition(textD);
2585 int startOfLine;
2586 int silent = 0;
2588 silent = hasKey("nobell", args, nArgs);
2589 if (hasKey("wrap", args, nArgs))
2590 startOfLine = TextDStartOfLine(textD, insertPos);
2591 else
2592 startOfLine = BufStartOfLine(textD->buffer, insertPos);
2593 cancelDrag(w);
2594 if (checkReadOnly(w))
2595 return;
2596 TakeMotifDestination(w, e->time);
2597 if (deletePendingSelection(w, event))
2598 return;
2599 if (insertPos == startOfLine) {
2600 ringIfNecessary(silent, w);
2601 return;
2603 BufRemove(textD->buffer, startOfLine, insertPos);
2604 checkAutoShowInsertPos(w);
2605 callCursorMovementCBs(w, event);
2608 static void forwardCharacterAP(Widget w, XEvent *event, String *args,
2609 Cardinal *nArgs)
2611 int insertPos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
2612 int silent = hasKey("nobell", args, nArgs);
2614 cancelDrag(w);
2615 if (!TextDMoveRight(((TextWidget)w)->text.textD)) {
2616 ringIfNecessary(silent, w);
2618 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2619 checkAutoShowInsertPos(w);
2620 callCursorMovementCBs(w, event);
2623 static void backwardCharacterAP(Widget w, XEvent *event, String *args,
2624 Cardinal *nArgs)
2626 int insertPos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
2627 int silent = hasKey("nobell", args, nArgs);
2629 cancelDrag(w);
2630 if (!TextDMoveLeft(((TextWidget)w)->text.textD)) {
2631 ringIfNecessary(silent, w);
2633 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2634 checkAutoShowInsertPos(w);
2635 callCursorMovementCBs(w, event);
2638 static void forwardWordAP(Widget w, XEvent *event, String *args,
2639 Cardinal *nArgs)
2641 textDisp *textD = ((TextWidget)w)->text.textD;
2642 textBuffer *buf = textD->buffer;
2643 int pos, insertPos = TextDGetInsertPosition(textD);
2644 char *delimiters = ((TextWidget)w)->text.delimiters;
2645 int silent = hasKey("nobell", args, nArgs);
2647 cancelDrag(w);
2648 if (insertPos == buf->length) {
2649 ringIfNecessary(silent, w);
2650 return;
2652 pos = insertPos;
2654 if (hasKey("tail", args, nArgs)) {
2655 for (; pos<buf->length; pos++) {
2656 if (NULL == strchr(delimiters, BufGetCharacter(buf, pos))) {
2657 break;
2660 if (NULL == strchr(delimiters, BufGetCharacter(buf, pos))) {
2661 pos = endOfWord((TextWidget)w, pos);
2663 } else {
2664 if (NULL == strchr(delimiters, BufGetCharacter(buf, pos))) {
2665 pos = endOfWord((TextWidget)w, pos);
2667 for (; pos<buf->length; pos++) {
2668 if (NULL == strchr(delimiters, BufGetCharacter(buf, pos))) {
2669 break;
2674 TextDSetInsertPosition(textD, pos);
2675 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2676 checkAutoShowInsertPos(w);
2677 callCursorMovementCBs(w, event);
2680 static void backwardWordAP(Widget w, XEvent *event, String *args,
2681 Cardinal *nArgs)
2683 textDisp *textD = ((TextWidget)w)->text.textD;
2684 textBuffer *buf = textD->buffer;
2685 int pos, insertPos = TextDGetInsertPosition(textD);
2686 char *delimiters = ((TextWidget)w)->text.delimiters;
2687 int silent = hasKey("nobell", args, nArgs);
2689 cancelDrag(w);
2690 if (insertPos == 0) {
2691 ringIfNecessary(silent, w);
2692 return;
2694 pos = max(insertPos - 1, 0);
2695 while (strchr(delimiters, BufGetCharacter(buf, pos)) != NULL && pos > 0)
2696 pos--;
2697 pos = startOfWord((TextWidget)w, pos);
2699 TextDSetInsertPosition(textD, pos);
2700 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2701 checkAutoShowInsertPos(w);
2702 callCursorMovementCBs(w, event);
2705 static void forwardParagraphAP(Widget w, XEvent *event, String *args,
2706 Cardinal *nArgs)
2708 textDisp *textD = ((TextWidget)w)->text.textD;
2709 int pos, insertPos = TextDGetInsertPosition(textD);
2710 textBuffer *buf = textD->buffer;
2711 char c;
2712 static char whiteChars[] = " \t";
2713 int silent = hasKey("nobell", args, nArgs);
2715 cancelDrag(w);
2716 if (insertPos == buf->length) {
2717 ringIfNecessary(silent, w);
2718 return;
2720 pos = min(BufEndOfLine(buf, insertPos)+1, buf->length);
2721 while (pos < buf->length) {
2722 c = BufGetCharacter(buf, pos);
2723 if (c == '\n')
2724 break;
2725 if (strchr(whiteChars, c) != NULL)
2726 pos++;
2727 else
2728 pos = min(BufEndOfLine(buf, pos)+1, buf->length);
2730 TextDSetInsertPosition(textD, min(pos+1, buf->length));
2731 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2732 checkAutoShowInsertPos(w);
2733 callCursorMovementCBs(w, event);
2736 static void backwardParagraphAP(Widget w, XEvent *event, String *args,
2737 Cardinal *nArgs)
2739 textDisp *textD = ((TextWidget)w)->text.textD;
2740 int parStart, pos, insertPos = TextDGetInsertPosition(textD);
2741 textBuffer *buf = textD->buffer;
2742 char c;
2743 static char whiteChars[] = " \t";
2744 int silent = hasKey("nobell", args, nArgs);
2746 cancelDrag(w);
2747 if (insertPos == 0) {
2748 ringIfNecessary(silent, w);
2749 return;
2751 parStart = BufStartOfLine(buf, max(insertPos-1, 0));
2752 pos = max(parStart - 2, 0);
2753 while (pos > 0) {
2754 c = BufGetCharacter(buf, pos);
2755 if (c == '\n')
2756 break;
2757 if (strchr(whiteChars, c) != NULL)
2758 pos--;
2759 else {
2760 parStart = BufStartOfLine(buf, pos);
2761 pos = max(parStart - 2, 0);
2764 TextDSetInsertPosition(textD, parStart);
2765 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2766 checkAutoShowInsertPos(w);
2767 callCursorMovementCBs(w, event);
2770 static void keySelectAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2772 textDisp *textD = ((TextWidget)w)->text.textD;
2773 int stat, insertPos = TextDGetInsertPosition(textD);
2774 int silent = hasKey("nobell", args, nArgs);
2776 cancelDrag(w);
2777 if (hasKey("left", args, nArgs)) stat = TextDMoveLeft(textD);
2778 else if (hasKey("right", args, nArgs)) stat = TextDMoveRight(textD);
2779 else if (hasKey("up", args, nArgs)) stat = TextDMoveUp(textD, 0);
2780 else if (hasKey("down", args, nArgs)) stat = TextDMoveDown(textD, 0);
2781 else {
2782 keyMoveExtendSelection(w, event, insertPos, hasKey("rect", args,nArgs));
2783 return;
2785 if (!stat) {
2786 ringIfNecessary(silent, w);
2788 else {
2789 keyMoveExtendSelection(w, event, insertPos, hasKey("rect", args,nArgs));
2790 checkAutoShowInsertPos(w);
2791 callCursorMovementCBs(w, event);
2795 static void processUpAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2797 int insertPos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
2798 int silent = hasKey("nobell", args, nArgs);
2799 int abs = hasKey("absolute", args, nArgs);
2801 cancelDrag(w);
2802 if (!TextDMoveUp(((TextWidget)w)->text.textD, abs))
2803 ringIfNecessary(silent, w);
2804 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2805 checkAutoShowInsertPos(w);
2806 callCursorMovementCBs(w, event);
2809 static void processShiftUpAP(Widget w, XEvent *event, String *args,
2810 Cardinal *nArgs)
2812 int insertPos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
2813 int silent = hasKey("nobell", args, nArgs);
2814 int abs = hasKey("absolute", args, nArgs);
2816 cancelDrag(w);
2817 if (!TextDMoveUp(((TextWidget)w)->text.textD, abs))
2818 ringIfNecessary(silent, w);
2819 keyMoveExtendSelection(w, event, insertPos, hasKey("rect", args, nArgs));
2820 checkAutoShowInsertPos(w);
2821 callCursorMovementCBs(w, event);
2824 static void processDownAP(Widget w, XEvent *event, String *args,
2825 Cardinal *nArgs)
2827 int insertPos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
2828 int silent = hasKey("nobell", args, nArgs);
2829 int abs = hasKey("absolute", args, nArgs);
2831 cancelDrag(w);
2832 if (!TextDMoveDown(((TextWidget)w)->text.textD, abs))
2833 ringIfNecessary(silent, w);
2834 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2835 checkAutoShowInsertPos(w);
2836 callCursorMovementCBs(w, event);
2839 static void processShiftDownAP(Widget w, XEvent *event, String *args,
2840 Cardinal *nArgs)
2842 int insertPos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
2843 int silent = hasKey("nobell", args, nArgs);
2844 int abs = hasKey("absolute", args, nArgs);
2846 cancelDrag(w);
2847 if (!TextDMoveDown(((TextWidget)w)->text.textD, abs))
2848 ringIfNecessary(silent, w);
2849 keyMoveExtendSelection(w, event, insertPos, hasKey("rect", args, nArgs));
2850 checkAutoShowInsertPos(w);
2851 callCursorMovementCBs(w, event);
2854 static void beginningOfLineAP(Widget w, XEvent *event, String *args,
2855 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, BufStartOfLine(textD->buffer, insertPos));
2863 else
2864 TextDSetInsertPosition(textD, TextDStartOfLine(textD, insertPos));
2865 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2866 checkAutoShowInsertPos(w);
2867 callCursorMovementCBs(w, event);
2868 textD->cursorPreferredCol = 0;
2871 static void endOfLineAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2873 textDisp *textD = ((TextWidget)w)->text.textD;
2874 int insertPos = TextDGetInsertPosition(textD);
2876 cancelDrag(w);
2877 if (hasKey("absolute", args, nArgs))
2878 TextDSetInsertPosition(textD, BufEndOfLine(textD->buffer, insertPos));
2879 else
2880 TextDSetInsertPosition(textD, TextDEndOfLine(textD, insertPos, False));
2881 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2882 checkAutoShowInsertPos(w);
2883 callCursorMovementCBs(w, event);
2884 textD->cursorPreferredCol = -1;
2887 static void beginningOfFileAP(Widget w, XEvent *event, String *args,
2888 Cardinal *nArgs)
2890 int insertPos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
2891 textDisp *textD = ((TextWidget)w)->text.textD;
2893 cancelDrag(w);
2894 if (hasKey("scrollbar", args, nArgs)) {
2895 if (textD->topLineNum != 1) {
2896 TextDSetScroll(textD, 1, textD->horizOffset);
2899 else {
2900 TextDSetInsertPosition(((TextWidget)w)->text.textD, 0);
2901 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2902 checkAutoShowInsertPos(w);
2903 callCursorMovementCBs(w, event);
2907 static void endOfFileAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2909 textDisp *textD = ((TextWidget)w)->text.textD;
2910 int insertPos = TextDGetInsertPosition(textD);
2911 int lastTopLine;
2913 cancelDrag(w);
2914 if (hasKey("scrollbar", args, nArgs)) {
2915 lastTopLine = max(1,
2916 textD->nBufferLines - (textD->nVisibleLines - 2) +
2917 ((TextWidget)w)->text.cursorVPadding);
2918 if (lastTopLine != textD->topLineNum) {
2919 TextDSetScroll(textD, lastTopLine, textD->horizOffset);
2922 else {
2923 TextDSetInsertPosition(textD, textD->buffer->length);
2924 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2925 checkAutoShowInsertPos(w);
2926 callCursorMovementCBs(w, event);
2930 static void nextPageAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
2932 textDisp *textD = ((TextWidget)w)->text.textD;
2933 textBuffer *buf = textD->buffer;
2934 int lastTopLine = max(1,
2935 textD->nBufferLines - (textD->nVisibleLines - 2) +
2936 ((TextWidget)w)->text.cursorVPadding );
2937 int insertPos = TextDGetInsertPosition(textD);
2938 int column = 0, visLineNum, lineStartPos;
2939 int pos, targetLine;
2940 int pageForwardCount = max(1, textD->nVisibleLines - 1);
2941 int maintainColumn = 0;
2942 int silent = hasKey("nobell", args, nArgs);
2944 maintainColumn = hasKey("column", args, nArgs);
2945 cancelDrag(w);
2946 if (hasKey("scrollbar", args, nArgs)) { /* scrollbar only */
2947 targetLine = min(textD->topLineNum + pageForwardCount, lastTopLine);
2949 if (targetLine == textD->topLineNum) {
2950 ringIfNecessary(silent, w);
2951 return;
2953 TextDSetScroll(textD, targetLine, textD->horizOffset);
2955 else if (hasKey("stutter", args, nArgs)) { /* Mac style */
2956 /* move to bottom line of visible area */
2957 /* if already there, page down maintaining preferrred column */
2958 targetLine = max(min(textD->nVisibleLines - 1, textD->nBufferLines), 0);
2959 column = TextDPreferredColumn(textD, &visLineNum, &lineStartPos);
2960 if (lineStartPos == textD->lineStarts[targetLine]) {
2961 if (insertPos >= buf->length || textD->topLineNum == lastTopLine) {
2962 ringIfNecessary(silent, w);
2963 return;
2965 targetLine = min(textD->topLineNum + pageForwardCount, lastTopLine);
2966 pos = TextDCountForwardNLines(textD, insertPos, pageForwardCount, False);
2967 if (maintainColumn) {
2968 pos = TextDPosOfPreferredCol(textD, column, pos);
2970 TextDSetInsertPosition(textD, pos);
2971 TextDSetScroll(textD, targetLine, textD->horizOffset);
2973 else {
2974 pos = textD->lineStarts[targetLine];
2975 while (targetLine > 0 && pos == -1) {
2976 --targetLine;
2977 pos = textD->lineStarts[targetLine];
2979 if (lineStartPos == pos) {
2980 ringIfNecessary(silent, w);
2981 return;
2983 if (maintainColumn) {
2984 pos = TextDPosOfPreferredCol(textD, column, pos);
2986 TextDSetInsertPosition(textD, pos);
2988 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
2989 checkAutoShowInsertPos(w);
2990 callCursorMovementCBs(w, event);
2991 if (maintainColumn) {
2992 textD->cursorPreferredCol = column;
2994 else {
2995 textD->cursorPreferredCol = -1;
2998 else { /* "standard" */
2999 if (insertPos >= buf->length && textD->topLineNum == lastTopLine) {
3000 ringIfNecessary(silent, w);
3001 return;
3003 if (maintainColumn) {
3004 column = TextDPreferredColumn(textD, &visLineNum, &lineStartPos);
3006 targetLine = textD->topLineNum + textD->nVisibleLines - 1;
3007 if (targetLine < 1) targetLine = 1;
3008 if (targetLine > lastTopLine) targetLine = lastTopLine;
3009 pos = TextDCountForwardNLines(textD, insertPos, textD->nVisibleLines-1, False);
3010 if (maintainColumn) {
3011 pos = TextDPosOfPreferredCol(textD, column, pos);
3013 TextDSetInsertPosition(textD, pos);
3014 TextDSetScroll(textD, targetLine, textD->horizOffset);
3015 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
3016 checkAutoShowInsertPos(w);
3017 callCursorMovementCBs(w, event);
3018 if (maintainColumn) {
3019 textD->cursorPreferredCol = column;
3021 else {
3022 textD->cursorPreferredCol = -1;
3027 static void previousPageAP(Widget w, XEvent *event, String *args,
3028 Cardinal *nArgs)
3030 textDisp *textD = ((TextWidget)w)->text.textD;
3031 int insertPos = TextDGetInsertPosition(textD);
3032 int column = 0, visLineNum, lineStartPos;
3033 int pos, targetLine;
3034 int pageBackwardCount = max(1, textD->nVisibleLines - 1);
3035 int maintainColumn = 0;
3036 int silent = hasKey("nobell", args, nArgs);
3038 maintainColumn = hasKey("column", args, nArgs);
3039 cancelDrag(w);
3040 if (hasKey("scrollbar", args, nArgs)) { /* scrollbar only */
3041 targetLine = max(textD->topLineNum - pageBackwardCount, 1);
3043 if (targetLine == textD->topLineNum) {
3044 ringIfNecessary(silent, w);
3045 return;
3047 TextDSetScroll(textD, targetLine, textD->horizOffset);
3049 else if (hasKey("stutter", args, nArgs)) { /* Mac style */
3050 /* move to top line of visible area */
3051 /* if already there, page up maintaining preferrred column if required */
3052 targetLine = 0;
3053 column = TextDPreferredColumn(textD, &visLineNum, &lineStartPos);
3054 if (lineStartPos == textD->lineStarts[targetLine]) {
3055 if (textD->topLineNum == 1 && (maintainColumn || column == 0)) {
3056 ringIfNecessary(silent, w);
3057 return;
3059 targetLine = max(textD->topLineNum - pageBackwardCount, 1);
3060 pos = TextDCountBackwardNLines(textD, insertPos, pageBackwardCount);
3061 if (maintainColumn) {
3062 pos = TextDPosOfPreferredCol(textD, column, pos);
3064 TextDSetInsertPosition(textD, pos);
3065 TextDSetScroll(textD, targetLine, textD->horizOffset);
3067 else {
3068 pos = textD->lineStarts[targetLine];
3069 if (maintainColumn) {
3070 pos = TextDPosOfPreferredCol(textD, column, pos);
3072 TextDSetInsertPosition(textD, pos);
3074 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
3075 checkAutoShowInsertPos(w);
3076 callCursorMovementCBs(w, event);
3077 if (maintainColumn) {
3078 textD->cursorPreferredCol = column;
3080 else {
3081 textD->cursorPreferredCol = -1;
3084 else { /* "standard" */
3085 if (insertPos <= 0 && textD->topLineNum == 1) {
3086 ringIfNecessary(silent, w);
3087 return;
3089 if (maintainColumn) {
3090 column = TextDPreferredColumn(textD, &visLineNum, &lineStartPos);
3092 targetLine = textD->topLineNum - (textD->nVisibleLines - 1);
3093 if (targetLine < 1) targetLine = 1;
3094 pos = TextDCountBackwardNLines(textD, insertPos, textD->nVisibleLines-1);
3095 if (maintainColumn) {
3096 pos = TextDPosOfPreferredCol(textD, column, pos);
3098 TextDSetInsertPosition(textD, pos);
3099 TextDSetScroll(textD, targetLine, textD->horizOffset);
3100 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
3101 checkAutoShowInsertPos(w);
3102 callCursorMovementCBs(w, event);
3103 if (maintainColumn) {
3104 textD->cursorPreferredCol = column;
3106 else {
3107 textD->cursorPreferredCol = -1;
3112 static void pageLeftAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
3114 textDisp *textD = ((TextWidget)w)->text.textD;
3115 textBuffer *buf = textD->buffer;
3116 int insertPos = TextDGetInsertPosition(textD);
3117 int maxCharWidth = textD->fontStruct->max_bounds.width;
3118 int lineStartPos, indent, pos;
3119 int horizOffset;
3120 int silent = hasKey("nobell", args, nArgs);
3122 cancelDrag(w);
3123 if (hasKey("scrollbar", args, nArgs)) {
3124 if (textD->horizOffset == 0) {
3125 ringIfNecessary(silent, w);
3126 return;
3128 horizOffset = max(0, textD->horizOffset - textD->width);
3129 TextDSetScroll(textD, textD->topLineNum, horizOffset);
3131 else {
3132 lineStartPos = BufStartOfLine(buf, insertPos);
3133 if (insertPos == lineStartPos && textD->horizOffset == 0) {
3134 ringIfNecessary(silent, w);
3135 return;
3137 indent = BufCountDispChars(buf, lineStartPos, insertPos);
3138 pos = BufCountForwardDispChars(buf, lineStartPos,
3139 max(0, indent - textD->width / maxCharWidth));
3140 TextDSetInsertPosition(textD, pos);
3141 TextDSetScroll(textD, textD->topLineNum,
3142 max(0, textD->horizOffset - textD->width));
3143 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
3144 checkAutoShowInsertPos(w);
3145 callCursorMovementCBs(w, event);
3149 static void pageRightAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
3151 textDisp *textD = ((TextWidget)w)->text.textD;
3152 textBuffer *buf = textD->buffer;
3153 int insertPos = TextDGetInsertPosition(textD);
3154 int maxCharWidth = textD->fontStruct->max_bounds.width;
3155 int oldHorizOffset = textD->horizOffset;
3156 int lineStartPos, indent, pos;
3157 int horizOffset, sliderSize, sliderMax;
3158 int silent = hasKey("nobell", args, nArgs);
3160 cancelDrag(w);
3161 if (hasKey("scrollbar", args, nArgs)) {
3162 XtVaGetValues(textD->hScrollBar, XmNmaximum, &sliderMax,
3163 XmNsliderSize, &sliderSize, NULL);
3164 horizOffset = min(textD->horizOffset + textD->width, sliderMax - sliderSize);
3165 if (textD->horizOffset == horizOffset) {
3166 ringIfNecessary(silent, w);
3167 return;
3169 TextDSetScroll(textD, textD->topLineNum, horizOffset);
3171 else {
3172 lineStartPos = BufStartOfLine(buf, insertPos);
3173 indent = BufCountDispChars(buf, lineStartPos, insertPos);
3174 pos = BufCountForwardDispChars(buf, lineStartPos,
3175 indent + textD->width / maxCharWidth);
3176 TextDSetInsertPosition(textD, pos);
3177 TextDSetScroll(textD, textD->topLineNum, textD->horizOffset + textD->width);
3178 if (textD->horizOffset == oldHorizOffset && insertPos == pos)
3179 ringIfNecessary(silent, w);
3180 checkMoveSelectionChange(w, event, insertPos, args, nArgs);
3181 checkAutoShowInsertPos(w);
3182 callCursorMovementCBs(w, event);
3186 static void toggleOverstrikeAP(Widget w, XEvent *event, String *args,
3187 Cardinal *nArgs)
3189 TextWidget tw = (TextWidget)w;
3191 if (tw->text.overstrike) {
3192 tw->text.overstrike = False;
3193 TextDSetCursorStyle(tw->text.textD,
3194 tw->text.heavyCursor ? HEAVY_CURSOR : NORMAL_CURSOR);
3195 } else {
3196 tw->text.overstrike = True;
3197 if ( tw->text.textD->cursorStyle == NORMAL_CURSOR ||
3198 tw->text.textD->cursorStyle == HEAVY_CURSOR)
3199 TextDSetCursorStyle(tw->text.textD, BLOCK_CURSOR);
3203 static void scrollUpAP(Widget w, XEvent *event, String *args,
3204 Cardinal *nArgs)
3206 textDisp *textD = ((TextWidget)w)->text.textD;
3207 int topLineNum, horizOffset, nLines;
3209 if (*nArgs == 0 || sscanf(args[0], "%d", &nLines) != 1)
3210 return;
3211 if (*nArgs == 2) {
3212 /* Allow both 'page' and 'pages' */
3213 if (strncmp(args[1], "page", 4) == 0)
3214 nLines *= textD->nVisibleLines;
3216 /* 'line' or 'lines' is the only other valid possibility */
3217 else if (strncmp(args[1], "line", 4) != 0)
3218 return;
3220 TextDGetScroll(textD, &topLineNum, &horizOffset);
3221 TextDSetScroll(textD, topLineNum-nLines, horizOffset);
3224 static void scrollDownAP(Widget w, XEvent *event, String *args,
3225 Cardinal *nArgs)
3227 textDisp *textD = ((TextWidget)w)->text.textD;
3228 int topLineNum, horizOffset, nLines;
3230 if (*nArgs == 0 || sscanf(args[0], "%d", &nLines) != 1)
3231 return;
3232 if (*nArgs == 2) {
3233 /* Allow both 'page' and 'pages' */
3234 if (strncmp(args[1], "page", 4) == 0)
3235 nLines *= textD->nVisibleLines;
3237 /* 'line' or 'lines' is the only other valid possibility */
3238 else if (strncmp(args[1], "line", 4) != 0)
3239 return;
3241 TextDGetScroll(textD, &topLineNum, &horizOffset);
3242 TextDSetScroll(textD, topLineNum+nLines, horizOffset);
3245 static void scrollLeftAP(Widget w, XEvent *event, String *args,
3246 Cardinal *nArgs)
3248 textDisp *textD = ((TextWidget)w)->text.textD;
3249 int horizOffset, nPixels;
3250 int sliderMax, sliderSize;
3252 if (*nArgs == 0 || sscanf(args[0], "%d", &nPixels) != 1)
3253 return;
3254 XtVaGetValues(textD->hScrollBar, XmNmaximum, &sliderMax,
3255 XmNsliderSize, &sliderSize, NULL);
3256 horizOffset = min(max(0, textD->horizOffset - nPixels), sliderMax - sliderSize);
3257 if (textD->horizOffset != horizOffset) {
3258 TextDSetScroll(textD, textD->topLineNum, horizOffset);
3262 static void scrollRightAP(Widget w, XEvent *event, String *args,
3263 Cardinal *nArgs)
3265 textDisp *textD = ((TextWidget)w)->text.textD;
3266 int horizOffset, nPixels;
3267 int sliderMax, sliderSize;
3269 if (*nArgs == 0 || sscanf(args[0], "%d", &nPixels) != 1)
3270 return;
3271 XtVaGetValues(textD->hScrollBar, XmNmaximum, &sliderMax,
3272 XmNsliderSize, &sliderSize, NULL);
3273 horizOffset = min(max(0, textD->horizOffset + nPixels), sliderMax - sliderSize);
3274 if (textD->horizOffset != horizOffset) {
3275 TextDSetScroll(textD, textD->topLineNum, horizOffset);
3279 static void scrollToLineAP(Widget w, XEvent *event, String *args,
3280 Cardinal *nArgs)
3282 textDisp *textD = ((TextWidget)w)->text.textD;
3283 int topLineNum, horizOffset, lineNum;
3285 if (*nArgs == 0 || sscanf(args[0], "%d", &lineNum) != 1)
3286 return;
3287 TextDGetScroll(textD, &topLineNum, &horizOffset);
3288 TextDSetScroll(textD, lineNum, horizOffset);
3291 static void selectAllAP(Widget w, XEvent *event, String *args,
3292 Cardinal *nArgs)
3294 textBuffer *buf = ((TextWidget)w)->text.textD->buffer;
3296 cancelDrag(w);
3297 BufSelect(buf, 0, buf->length);
3300 static void deselectAllAP(Widget w, XEvent *event, String *args,
3301 Cardinal *nArgs)
3303 cancelDrag(w);
3304 BufUnselect(((TextWidget)w)->text.textD->buffer);
3308 ** Called on the Intrinsic FocusIn event.
3310 ** Note that the widget has no internal state about the focus, ie. it does
3311 ** not know whether it has the focus or not.
3313 static void focusInAP(Widget widget, XEvent* event, String* unused1,
3314 Cardinal* unused2)
3316 TextWidget textwidget = (TextWidget) widget;
3317 textDisp* textD = textwidget->text.textD;
3319 /* I don't entirely understand the traversal mechanism in Motif widgets,
3320 particularly, what leads to this widget getting a focus-in event when
3321 it does not actually have the input focus. The temporary solution is
3322 to do the comparison below, and not show the cursor when Motif says
3323 we don't have focus, but keep looking for the real answer */
3324 #if XmVersion >= 1002
3325 if (widget != XmGetFocusWidget(widget))
3326 return;
3327 #endif
3329 /* If the timer is not already started, start it */
3330 if (textwidget->text.cursorBlinkRate != 0
3331 && textwidget->text.cursorBlinkProcID == 0) {
3332 textwidget->text.cursorBlinkProcID
3333 = XtAppAddTimeOut(XtWidgetToApplicationContext(widget),
3334 textwidget->text.cursorBlinkRate, cursorBlinkTimerProc,
3335 widget);
3338 /* Change the cursor to active style */
3339 if (textwidget->text.overstrike)
3340 TextDSetCursorStyle(textD, BLOCK_CURSOR);
3341 else
3342 TextDSetCursorStyle(textD, (textwidget->text.heavyCursor
3343 ? HEAVY_CURSOR
3344 : NORMAL_CURSOR));
3345 TextDUnblankCursor(textD);
3347 #ifndef NO_XMIM
3348 /* Notify Motif input manager that widget has focus */
3349 XmImVaSetFocusValues(widget, NULL);
3350 #endif
3352 /* Call any registered focus-in callbacks */
3353 XtCallCallbacks((Widget) widget, textNfocusCallback, (XtPointer) event);
3356 static void focusOutAP(Widget w, XEvent *event, String *args, Cardinal *nArgs)
3358 textDisp *textD = ((TextWidget)w)->text.textD;
3360 /* Remove the cursor blinking timer procedure */
3361 if (((TextWidget)w)->text.cursorBlinkProcID != 0)
3362 XtRemoveTimeOut(((TextWidget)w)->text.cursorBlinkProcID);
3363 ((TextWidget)w)->text.cursorBlinkProcID = 0;
3365 /* Leave a dim or destination cursor */
3366 TextDSetCursorStyle(textD, ((TextWidget)w)->text.motifDestOwner ?
3367 CARET_CURSOR : DIM_CURSOR);
3368 TextDUnblankCursor(textD);
3370 /* If there's a calltip displayed, kill it. */
3371 TextDKillCalltip(textD, 0);
3373 /* Call any registered focus-out callbacks */
3374 XtCallCallbacks((Widget)w, textNlosingFocusCallback, (XtPointer)event);
3378 ** For actions involving cursor movement, "extend" keyword means incorporate
3379 ** the new cursor position in the selection, and lack of an "extend" keyword
3380 ** means cancel the existing selection
3382 static void checkMoveSelectionChange(Widget w, XEvent *event, int startPos,
3383 String *args, Cardinal *nArgs)
3385 if (hasKey("extend", args, nArgs))
3386 keyMoveExtendSelection(w, event, startPos, hasKey("rect", args, nArgs));
3387 else
3388 BufUnselect((((TextWidget)w)->text.textD)->buffer);
3392 ** If a selection change was requested via a keyboard command for moving
3393 ** the insertion cursor (usually with the "extend" keyword), adjust the
3394 ** selection to include the new cursor position, or begin a new selection
3395 ** between startPos and the new cursor position with anchor at startPos.
3397 static void keyMoveExtendSelection(Widget w, XEvent *event, int origPos,
3398 int rectangular)
3400 XKeyEvent *e = &event->xkey;
3401 TextWidget tw = (TextWidget)w;
3402 textDisp *textD = tw->text.textD;
3403 textBuffer *buf = textD->buffer;
3404 selection *sel = &buf->primary;
3405 int newPos = TextDGetInsertPosition(textD);
3406 int startPos, endPos, startCol, endCol, newCol, origCol;
3407 int anchor, rectAnchor, anchorLineStart;
3409 /* Moving the cursor does not take the Motif destination, but as soon as
3410 the user selects something, grab it (I'm not sure if this distinction
3411 actually makes sense, but it's what Motif was doing, back when their
3412 secondary selections actually worked correctly) */
3413 TakeMotifDestination(w, e->time);
3415 if ((sel->selected || sel->zeroWidth) && sel->rectangular && rectangular) {
3416 /* rect -> rect */
3417 newCol = BufCountDispChars(buf, BufStartOfLine(buf, newPos), newPos);
3418 startCol = min(tw->text.rectAnchor, newCol);
3419 endCol = max(tw->text.rectAnchor, newCol);
3420 startPos = BufStartOfLine(buf, min(tw->text.anchor, newPos));
3421 endPos = BufEndOfLine(buf, max(tw->text.anchor, newPos));
3422 BufRectSelect(buf, startPos, endPos, startCol, endCol);
3423 } else if (sel->selected && rectangular) { /* plain -> rect */
3424 newCol = BufCountDispChars(buf, BufStartOfLine(buf, newPos), newPos);
3425 if (abs(newPos - sel->start) < abs(newPos - sel->end))
3426 anchor = sel->end;
3427 else
3428 anchor = sel->start;
3429 anchorLineStart = BufStartOfLine(buf, anchor);
3430 rectAnchor = BufCountDispChars(buf, anchorLineStart, anchor);
3431 tw->text.anchor = anchor;
3432 tw->text.rectAnchor = rectAnchor;
3433 BufRectSelect(buf, BufStartOfLine(buf, min(anchor, newPos)),
3434 BufEndOfLine(buf, max(anchor, newPos)),
3435 min(rectAnchor, newCol), max(rectAnchor, newCol));
3436 } else if (sel->selected && sel->rectangular) { /* rect -> plain */
3437 startPos = BufCountForwardDispChars(buf,
3438 BufStartOfLine(buf, sel->start), sel->rectStart);
3439 endPos = BufCountForwardDispChars(buf,
3440 BufStartOfLine(buf, sel->end), sel->rectEnd);
3441 if (abs(origPos - startPos) < abs(origPos - endPos))
3442 anchor = endPos;
3443 else
3444 anchor = startPos;
3445 BufSelect(buf, anchor, newPos);
3446 } else if (sel->selected) { /* plain -> plain */
3447 if (abs(origPos - sel->start) < abs(origPos - sel->end))
3448 anchor = sel->end;
3449 else
3450 anchor = sel->start;
3451 BufSelect(buf, anchor, newPos);
3452 } else if (rectangular) { /* no sel -> rect */
3453 origCol = BufCountDispChars(buf, BufStartOfLine(buf, origPos), origPos);
3454 newCol = BufCountDispChars(buf, BufStartOfLine(buf, newPos), newPos);
3455 startCol = min(newCol, origCol);
3456 endCol = max(newCol, origCol);
3457 startPos = BufStartOfLine(buf, min(origPos, newPos));
3458 endPos = BufEndOfLine(buf, max(origPos, newPos));
3459 tw->text.anchor = origPos;
3460 tw->text.rectAnchor = origCol;
3461 BufRectSelect(buf, startPos, endPos, startCol, endCol);
3462 } else { /* no sel -> plain */
3463 tw->text.anchor = origPos;
3464 tw->text.rectAnchor = BufCountDispChars(buf,
3465 BufStartOfLine(buf, origPos), origPos);
3466 BufSelect(buf, tw->text.anchor, newPos);
3470 static void checkAutoShowInsertPos(Widget w)
3472 if (((TextWidget)w)->text.autoShowInsertPos)
3473 TextDMakeInsertPosVisible(((TextWidget)w)->text.textD);
3476 static int checkReadOnly(Widget w)
3478 if (((TextWidget)w)->text.readOnly) {
3479 XBell(XtDisplay(w), 0);
3480 return True;
3482 return False;
3486 ** Insert text "chars" at the cursor position, as if the text had been
3487 ** typed. Same as TextInsertAtCursor, but without the complicated auto-wrap
3488 ** scanning and re-formatting.
3490 static void simpleInsertAtCursor(Widget w, char *chars, XEvent *event,
3491 int allowPendingDelete)
3493 textDisp *textD = ((TextWidget)w)->text.textD;
3494 textBuffer *buf = textD->buffer;
3495 char *c;
3497 if (allowPendingDelete && pendingSelection(w)) {
3498 BufReplaceSelected(buf, chars);
3499 TextDSetInsertPosition(textD, buf->cursorPosHint);
3500 } else if (((TextWidget)w)->text.overstrike) {
3501 for (c=chars; *c!='\0' && *c!='\n'; c++);
3502 if (*c == '\n')
3503 TextDInsert(textD, chars);
3504 else
3505 TextDOverstrike(textD, chars);
3506 } else
3507 TextDInsert(textD, chars);
3508 checkAutoShowInsertPos(w);
3509 callCursorMovementCBs(w, event);
3513 ** If there's a selection, delete it and position the cursor where the
3514 ** selection was deleted. (Called by routines which do deletion to check
3515 ** first for and do possible selection delete)
3517 static int deletePendingSelection(Widget w, XEvent *event)
3519 textDisp *textD = ((TextWidget)w)->text.textD;
3520 textBuffer *buf = textD->buffer;
3522 if (((TextWidget)w)->text.textD->buffer->primary.selected) {
3523 BufRemoveSelected(buf);
3524 TextDSetInsertPosition(textD, buf->cursorPosHint);
3525 checkAutoShowInsertPos(w);
3526 callCursorMovementCBs(w, event);
3527 return True;
3528 } else
3529 return False;
3533 ** Return true if pending delete is on and there's a selection contiguous
3534 ** with the cursor ready to be deleted. These criteria are used to decide
3535 ** if typing a character or inserting something should delete the selection
3536 ** first.
3538 static int pendingSelection(Widget w)
3540 selection *sel = &((TextWidget)w)->text.textD->buffer->primary;
3541 int pos = TextDGetInsertPosition(((TextWidget)w)->text.textD);
3543 return ((TextWidget)w)->text.pendingDelete && sel->selected &&
3544 pos >= sel->start && pos <= sel->end;
3548 ** Check if tab emulation is on and if there are emulated tabs before the
3549 ** cursor, and if so, delete an emulated tab as a unit. Also finishes up
3550 ** by calling checkAutoShowInsertPos and callCursorMovementCBs, so the
3551 ** calling action proc can just return (this is necessary to preserve
3552 ** emTabsBeforeCursor which is otherwise cleared by callCursorMovementCBs).
3554 static int deleteEmulatedTab(Widget w, XEvent *event)
3556 textDisp *textD = ((TextWidget)w)->text.textD;
3557 textBuffer *buf = ((TextWidget)w)->text.textD->buffer;
3558 int emTabDist = ((TextWidget)w)->text.emulateTabs;
3559 int emTabsBeforeCursor = ((TextWidget)w)->text.emTabsBeforeCursor;
3560 int startIndent, toIndent, insertPos, startPos, lineStart;
3561 int pos, indent, startPosIndent;
3562 char c, *spaceString;
3564 if (emTabDist <= 0 || emTabsBeforeCursor <= 0)
3565 return False;
3567 /* Find the position of the previous tab stop */
3568 insertPos = TextDGetInsertPosition(textD);
3569 lineStart = BufStartOfLine(buf, insertPos);
3570 startIndent = BufCountDispChars(buf, lineStart, insertPos);
3571 toIndent = (startIndent-1) - ((startIndent-1) % emTabDist);
3573 /* Find the position at which to begin deleting (stop at non-whitespace
3574 characters) */
3575 startPosIndent = indent = 0;
3576 startPos = lineStart;
3577 for (pos=lineStart; pos < insertPos; pos++) {
3578 c = BufGetCharacter(buf, pos);
3579 indent += BufCharWidth(c, indent, buf->tabDist, buf->nullSubsChar);
3580 if (indent > toIndent)
3581 break;
3582 startPosIndent = indent;
3583 startPos = pos + 1;
3586 /* Just to make sure, check that we're not deleting any non-white chars */
3587 for (pos=insertPos-1; pos>=startPos; pos--) {
3588 c = BufGetCharacter(buf, pos);
3589 if (c != ' ' && c != '\t') {
3590 startPos = pos + 1;
3591 break;
3595 /* Do the text replacement and reposition the cursor. If any spaces need
3596 to be inserted to make up for a deleted tab, do a BufReplace, otherwise,
3597 do a BufRemove. */
3598 if (startPosIndent < toIndent) {
3599 spaceString = XtMalloc(toIndent - startPosIndent + 1);
3600 memset(spaceString, ' ', toIndent-startPosIndent);
3601 spaceString[toIndent - startPosIndent] = '\0';
3602 BufReplace(buf, startPos, insertPos, spaceString);
3603 TextDSetInsertPosition(textD, startPos + toIndent - startPosIndent);
3604 XtFree(spaceString);
3605 } else {
3606 BufRemove(buf, startPos, insertPos);
3607 TextDSetInsertPosition(textD, startPos);
3610 /* The normal cursor movement stuff would usually be called by the action
3611 routine, but this wraps around it to restore emTabsBeforeCursor */
3612 checkAutoShowInsertPos(w);
3613 callCursorMovementCBs(w, event);
3615 /* Decrement and restore the marker for consecutive emulated tabs, which
3616 would otherwise have been zeroed by callCursorMovementCBs */
3617 ((TextWidget)w)->text.emTabsBeforeCursor = emTabsBeforeCursor - 1;
3618 return True;
3622 ** Select the word or whitespace adjacent to the cursor, and move the cursor
3623 ** to its end. pointerX is used as a tie-breaker, when the cursor is at the
3624 ** boundary between a word and some white-space. If the cursor is on the
3625 ** left, the word or space on the left is used. If it's on the right, that
3626 ** is used instead.
3628 static void selectWord(Widget w, int pointerX)
3630 TextWidget tw = (TextWidget)w;
3631 textBuffer *buf = tw->text.textD->buffer;
3632 int x, y, insertPos = TextDGetInsertPosition(tw->text.textD);
3634 TextPosToXY(w, insertPos, &x, &y);
3635 if (pointerX < x && insertPos > 0 && BufGetCharacter(buf, insertPos-1) != '\n')
3636 insertPos--;
3637 BufSelect(buf, startOfWord(tw, insertPos), endOfWord(tw, insertPos));
3640 static int startOfWord(TextWidget w, int pos)
3642 int startPos;
3643 textBuffer *buf = w->text.textD->buffer;
3644 char *delimiters=w->text.delimiters;
3645 char c = BufGetCharacter(buf, pos);
3647 if (c == ' ' || c== '\t') {
3648 if (!spanBackward(buf, pos, " \t", False, &startPos))
3649 return 0;
3650 } else if (strchr(delimiters, c)) {
3651 if (!spanBackward(buf, pos, delimiters, True, &startPos))
3652 return 0;
3653 } else {
3654 if (!BufSearchBackward(buf, pos, delimiters, &startPos))
3655 return 0;
3657 return min(pos, startPos+1);
3661 static int endOfWord(TextWidget w, int pos)
3663 int endPos;
3664 textBuffer *buf = w->text.textD->buffer;
3665 char *delimiters=w->text.delimiters;
3666 char c = BufGetCharacter(buf, pos);
3668 if (c == ' ' || c== '\t') {
3669 if (!spanForward(buf, pos, " \t", False, &endPos))
3670 return buf->length;
3671 } else if (strchr(delimiters, c)) {
3672 if (!spanForward(buf, pos, delimiters, True, &endPos))
3673 return buf->length;
3674 } else {
3675 if (!BufSearchForward(buf, pos, delimiters, &endPos))
3676 return buf->length;
3678 return endPos;
3682 ** Search forwards in buffer "buf" for the first character NOT in
3683 ** "searchChars", starting with the character "startPos", and returning the
3684 ** result in "foundPos" returns True if found, False if not. If ignoreSpace
3685 ** is set, then Space, Tab, and Newlines are ignored in searchChars.
3687 static int spanForward(textBuffer *buf, int startPos, char *searchChars,
3688 int ignoreSpace, int *foundPos)
3690 int pos;
3691 char *c;
3693 pos = startPos;
3694 while (pos < buf->length) {
3695 for (c=searchChars; *c!='\0'; c++)
3696 if(!(ignoreSpace && (*c==' ' || *c=='\t' || *c=='\n')))
3697 if (BufGetCharacter(buf, pos) == *c)
3698 break;
3699 if(*c == 0) {
3700 *foundPos = pos;
3701 return True;
3703 pos++;
3705 *foundPos = buf->length;
3706 return False;
3710 ** Search backwards in buffer "buf" for the first character NOT in
3711 ** "searchChars", starting with the character BEFORE "startPos", returning the
3712 ** result in "foundPos" returns True if found, False if not. If ignoreSpace is
3713 ** set, then Space, Tab, and Newlines are ignored in searchChars.
3715 static int spanBackward(textBuffer *buf, int startPos, char *searchChars, int
3716 ignoreSpace, int *foundPos)
3718 int pos;
3719 char *c;
3721 if (startPos == 0) {
3722 *foundPos = 0;
3723 return False;
3725 pos = startPos == 0 ? 0 : startPos - 1;
3726 while (pos >= 0) {
3727 for (c=searchChars; *c!='\0'; c++)
3728 if(!(ignoreSpace && (*c==' ' || *c=='\t' || *c=='\n')))
3729 if (BufGetCharacter(buf, pos) == *c)
3730 break;
3731 if(*c == 0) {
3732 *foundPos = pos;
3733 return True;
3735 pos--;
3737 *foundPos = 0;
3738 return False;
3742 ** Select the line containing the cursor, including the terminating newline,
3743 ** and move the cursor to its end.
3745 static void selectLine(Widget w)
3747 textDisp *textD = ((TextWidget)w)->text.textD;
3748 int insertPos = TextDGetInsertPosition(textD);
3749 int endPos, startPos;
3751 endPos = BufEndOfLine(textD->buffer, insertPos);
3752 startPos = BufStartOfLine(textD->buffer, insertPos);
3753 BufSelect(textD->buffer, startPos, min(endPos + 1, textD->buffer->length));
3754 TextDSetInsertPosition(textD, endPos);
3758 ** Given a new mouse pointer location, pass the position on to the
3759 ** autoscroll timer routine, and make sure the timer is on when it's
3760 ** needed and off when it's not.
3762 static void checkAutoScroll(TextWidget w, int x, int y)
3764 int inWindow;
3766 /* Is the pointer in or out of the window? */
3767 inWindow = x >= w->text.textD->left &&
3768 x < w->core.width - w->text.marginWidth &&
3769 y >= w->text.marginHeight &&
3770 y < w->core.height - w->text.marginHeight;
3772 /* If it's in the window, cancel the timer procedure */
3773 if (inWindow) {
3774 if (w->text.autoScrollProcID != 0)
3775 XtRemoveTimeOut(w->text.autoScrollProcID);;
3776 w->text.autoScrollProcID = 0;
3777 return;
3780 /* If the timer is not already started, start it */
3781 if (w->text.autoScrollProcID == 0) {
3782 w->text.autoScrollProcID = XtAppAddTimeOut(
3783 XtWidgetToApplicationContext((Widget)w),
3784 0, autoScrollTimerProc, w);
3787 /* Pass on the newest mouse location to the autoscroll routine */
3788 w->text.mouseX = x;
3789 w->text.mouseY = y;
3793 ** Reset drag state and cancel the auto-scroll timer
3795 static void endDrag(Widget w)
3797 if (((TextWidget)w)->text.autoScrollProcID != 0)
3798 XtRemoveTimeOut(((TextWidget)w)->text.autoScrollProcID);
3799 ((TextWidget)w)->text.autoScrollProcID = 0;
3800 if (((TextWidget)w)->text.dragState == MOUSE_PAN)
3801 XUngrabPointer(XtDisplay(w), CurrentTime);
3802 ((TextWidget)w)->text.dragState = NOT_CLICKED;
3806 ** Cancel any drag operation that might be in progress. Should be included
3807 ** in nearly every key event to cleanly end any dragging before edits are made
3808 ** which might change the insert position or the content of the buffer during
3809 ** a drag operation)
3811 static void cancelDrag(Widget w)
3813 int dragState = ((TextWidget)w)->text.dragState;
3815 if (((TextWidget)w)->text.autoScrollProcID != 0)
3816 XtRemoveTimeOut(((TextWidget)w)->text.autoScrollProcID);
3817 if (dragState == SECONDARY_DRAG || dragState == SECONDARY_RECT_DRAG)
3818 BufSecondaryUnselect(((TextWidget)w)->text.textD->buffer);
3819 if (dragState == PRIMARY_BLOCK_DRAG)
3820 CancelBlockDrag((TextWidget)w);
3821 if (dragState == MOUSE_PAN)
3822 XUngrabPointer(XtDisplay(w), CurrentTime);
3823 if (dragState != NOT_CLICKED)
3824 ((TextWidget)w)->text.dragState = DRAG_CANCELED;
3828 ** Do operations triggered by cursor movement: Call cursor movement callback
3829 ** procedure(s), and cancel marker indicating that the cursor is after one or
3830 ** more just-entered emulated tabs (spaces to be deleted as a unit).
3832 static void callCursorMovementCBs(Widget w, XEvent *event)
3834 ((TextWidget)w)->text.emTabsBeforeCursor = 0;
3835 XtCallCallbacks((Widget)w, textNcursorMovementCallback, (XtPointer)event);
3839 ** Adjust the selection as the mouse is dragged to position: (x, y).
3841 static void adjustSelection(TextWidget tw, int x, int y)
3843 textDisp *textD = tw->text.textD;
3844 textBuffer *buf = textD->buffer;
3845 int row, col, startCol, endCol, startPos, endPos;
3846 int newPos = TextDXYToPosition(textD, x, y);
3848 /* Adjust the selection */
3849 if (tw->text.dragState == PRIMARY_RECT_DRAG) {
3850 TextDXYToUnconstrainedPosition(textD, x, y, &row, &col);
3851 col = TextDOffsetWrappedColumn(textD, row, col);
3852 startCol = min(tw->text.rectAnchor, col);
3853 endCol = max(tw->text.rectAnchor, col);
3854 startPos = BufStartOfLine(buf, min(tw->text.anchor, newPos));
3855 endPos = BufEndOfLine(buf, max(tw->text.anchor, newPos));
3856 BufRectSelect(buf, startPos, endPos, startCol, endCol);
3857 } else if (tw->text.multiClickState == ONE_CLICK) {
3858 startPos = startOfWord(tw, min(tw->text.anchor, newPos));
3859 endPos = endOfWord(tw, max(tw->text.anchor, newPos));
3860 BufSelect(buf, startPos, endPos);
3861 newPos = newPos < tw->text.anchor ? startPos : endPos;
3862 } else if (tw->text.multiClickState == TWO_CLICKS) {
3863 startPos = BufStartOfLine(buf, min(tw->text.anchor, newPos));
3864 endPos = BufEndOfLine(buf, max(tw->text.anchor, newPos));
3865 BufSelect(buf, startPos, min(endPos+1, buf->length));
3866 newPos = newPos < tw->text.anchor ? startPos : endPos;
3867 } else
3868 BufSelect(buf, tw->text.anchor, newPos);
3870 /* Move the cursor */
3871 TextDSetInsertPosition(textD, newPos);
3872 callCursorMovementCBs((Widget)tw, NULL);
3875 static void adjustSecondarySelection(TextWidget tw, int x, int y)
3877 textDisp *textD = tw->text.textD;
3878 textBuffer *buf = textD->buffer;
3879 int row, col, startCol, endCol, startPos, endPos;
3880 int newPos = TextDXYToPosition(textD, x, y);
3882 if (tw->text.dragState == SECONDARY_RECT_DRAG) {
3883 TextDXYToUnconstrainedPosition(textD, x, y, &row, &col);
3884 col = TextDOffsetWrappedColumn(textD, row, col);
3885 startCol = min(tw->text.rectAnchor, col);
3886 endCol = max(tw->text.rectAnchor, col);
3887 startPos = BufStartOfLine(buf, min(tw->text.anchor, newPos));
3888 endPos = BufEndOfLine(buf, max(tw->text.anchor, newPos));
3889 BufSecRectSelect(buf, startPos, endPos, startCol, endCol);
3890 } else
3891 BufSecondarySelect(textD->buffer, tw->text.anchor, newPos);
3895 ** Wrap multi-line text in argument "text" to be inserted at the end of the
3896 ** text on line "startLine" and return the result. If "breakBefore" is
3897 ** non-NULL, allow wrapping to extend back into "startLine", in which case
3898 ** the returned text will include the wrapped part of "startLine", and
3899 ** "breakBefore" will return the number of characters at the end of
3900 ** "startLine" that were absorbed into the returned string. "breakBefore"
3901 ** will return zero if no characters were absorbed into the returned string.
3902 ** The buffer offset of text in the widget's text buffer is needed so that
3903 ** smart indent (which can be triggered by wrapping) can search back farther
3904 ** in the buffer than just the text in startLine.
3906 static char *wrapText(TextWidget tw, char *startLine, char *text, int bufOffset,
3907 int wrapMargin, int *breakBefore)
3909 textBuffer *wrapBuf, *buf = tw->text.textD->buffer;
3910 int startLineLen = strlen(startLine);
3911 int colNum, pos, lineStartPos, limitPos, breakAt, charsAdded;
3912 int firstBreak = -1, tabDist = buf->tabDist;
3913 char c, *wrappedText;
3915 /* Create a temporary text buffer and load it with the strings */
3916 wrapBuf = BufCreate();
3917 BufInsert(wrapBuf, 0, startLine);
3918 BufInsert(wrapBuf, wrapBuf->length, text);
3920 /* Scan the buffer for long lines and apply wrapLine when wrapMargin is
3921 exceeded. limitPos enforces no breaks in the "startLine" part of the
3922 string (if requested), and prevents re-scanning of long unbreakable
3923 lines for each character beyond the margin */
3924 colNum = 0;
3925 pos = 0;
3926 lineStartPos = 0;
3927 limitPos = breakBefore == NULL ? startLineLen : 0;
3928 while (pos < wrapBuf->length) {
3929 c = BufGetCharacter(wrapBuf, pos);
3930 if (c == '\n') {
3931 lineStartPos = limitPos = pos + 1;
3932 colNum = 0;
3933 } else {
3934 colNum += BufCharWidth(c, colNum, tabDist, buf->nullSubsChar);
3935 if (colNum > wrapMargin) {
3936 if (!wrapLine(tw, wrapBuf, bufOffset, lineStartPos, pos,
3937 limitPos, &breakAt, &charsAdded)) {
3938 limitPos = max(pos, limitPos);
3939 } else {
3940 lineStartPos = limitPos = breakAt+1;
3941 pos += charsAdded;
3942 colNum = BufCountDispChars(wrapBuf, lineStartPos, pos+1);
3943 if (firstBreak == -1)
3944 firstBreak = breakAt;
3948 pos++;
3951 /* Return the wrapped text, possibly including part of startLine */
3952 if (breakBefore == NULL)
3953 wrappedText = BufGetRange(wrapBuf, startLineLen, wrapBuf->length);
3954 else {
3955 *breakBefore = firstBreak != -1 && firstBreak < startLineLen ?
3956 startLineLen - firstBreak : 0;
3957 wrappedText = BufGetRange(wrapBuf, startLineLen - *breakBefore,
3958 wrapBuf->length);
3960 BufFree(wrapBuf);
3961 return wrappedText;
3965 ** Wraps the end of a line beginning at lineStartPos and ending at lineEndPos
3966 ** in "buf", at the last white-space on the line >= limitPos. (The implicit
3967 ** assumption is that just the last character of the line exceeds the wrap
3968 ** margin, and anywhere on the line we can wrap is correct). Returns False if
3969 ** unable to wrap the line. "breakAt", returns the character position at
3970 ** which the line was broken,
3972 ** Auto-wrapping can also trigger auto-indent. The additional parameter
3973 ** bufOffset is needed when auto-indent is set to smart indent and the smart
3974 ** indent routines need to scan far back in the buffer. "charsAdded" returns
3975 ** the number of characters added to acheive the auto-indent. wrapMargin is
3976 ** used to decide whether auto-indent should be skipped because the indent
3977 ** string itself would exceed the wrap margin.
3979 static int wrapLine(TextWidget tw, textBuffer *buf, int bufOffset,
3980 int lineStartPos, int lineEndPos, int limitPos, int *breakAt,
3981 int *charsAdded)
3983 int p, length, column;
3984 char c, *indentStr;
3986 /* Scan backward for whitespace or BOL. If BOL, return False, no
3987 whitespace in line at which to wrap */
3988 for (p=lineEndPos; ; p--) {
3989 if (p < lineStartPos || p < limitPos)
3990 return False;
3991 c = BufGetCharacter(buf, p);
3992 if (c == '\t' || c == ' ')
3993 break;
3996 /* Create an auto-indent string to insert to do wrap. If the auto
3997 indent string reaches the wrap position, slice the auto-indent
3998 back off and return to the left margin */
3999 if (tw->text.autoIndent || tw->text.smartIndent) {
4000 indentStr = createIndentString(tw, buf, bufOffset, lineStartPos,
4001 lineEndPos, &length, &column);
4002 if (column >= p-lineStartPos)
4003 indentStr[1] = '\0';
4004 } else {
4005 indentStr = "\n";
4006 length = 1;
4009 /* Replace the whitespace character with the auto-indent string
4010 and return the stats */
4011 BufReplace(buf, p, p+1, indentStr);
4012 if (tw->text.autoIndent || tw->text.smartIndent)
4013 XtFree(indentStr);
4014 *breakAt = p;
4015 *charsAdded = length-1;
4016 return True;
4020 ** Create and return an auto-indent string to add a newline at lineEndPos to a
4021 ** line starting at lineStartPos in buf. "buf" may or may not be the real
4022 ** text buffer for the widget. If it is not the widget's text buffer it's
4023 ** offset position from the real buffer must be specified in "bufOffset" to
4024 ** allow the smart-indent routines to scan back as far as necessary. The
4025 ** string length is returned in "length" (or "length" can be passed as NULL,
4026 ** and the indent column is returned in "column" (if non NULL).
4028 static char *createIndentString(TextWidget tw, textBuffer *buf, int bufOffset,
4029 int lineStartPos, int lineEndPos, int *length, int *column)
4031 textDisp *textD = tw->text.textD;
4032 int pos, indent = -1, tabDist = textD->buffer->tabDist;
4033 int i, useTabs = textD->buffer->useTabs;
4034 char *indentPtr, *indentStr, c;
4035 smartIndentCBStruct smartIndent;
4037 /* If smart indent is on, call the smart indent callback. It is not
4038 called when multi-line changes are being made (lineStartPos != 0),
4039 because smart indent needs to search back an indeterminate distance
4040 through the buffer, and reconciling that with wrapping changes made,
4041 but not yet committed in the buffer, would make programming smart
4042 indent more difficult for users and make everything more complicated */
4043 if (tw->text.smartIndent && (lineStartPos == 0 || buf == textD->buffer)) {
4044 smartIndent.reason = NEWLINE_INDENT_NEEDED;
4045 smartIndent.pos = lineEndPos + bufOffset;
4046 smartIndent.indentRequest = 0;
4047 smartIndent.charsTyped = NULL;
4048 XtCallCallbacks((Widget)tw, textNsmartIndentCallback,
4049 (XtPointer)&smartIndent);
4050 indent = smartIndent.indentRequest;
4053 /* If smart indent wasn't used, measure the indent distance of the line */
4054 if (indent == -1) {
4055 indent = 0;
4056 for (pos=lineStartPos; pos<lineEndPos; pos++) {
4057 c = BufGetCharacter(buf, pos);
4058 if (c != ' ' && c != '\t')
4059 break;
4060 if (c == '\t')
4061 indent += tabDist - (indent % tabDist);
4062 else
4063 indent++;
4067 /* Allocate and create a string of tabs and spaces to achieve the indent */
4068 indentPtr = indentStr = XtMalloc(indent + 2);
4069 *indentPtr++ = '\n';
4070 if (useTabs) {
4071 for (i=0; i < indent / tabDist; i++)
4072 *indentPtr++ = '\t';
4073 for (i=0; i < indent % tabDist; i++)
4074 *indentPtr++ = ' ';
4075 } else {
4076 for (i=0; i < indent; i++)
4077 *indentPtr++ = ' ';
4079 *indentPtr = '\0';
4081 /* Return any requested stats */
4082 if (length != NULL)
4083 *length = indentPtr - indentStr;
4084 if (column != NULL)
4085 *column = indent;
4087 return indentStr;
4091 ** Xt timer procedure for autoscrolling
4093 static void autoScrollTimerProc(XtPointer clientData, XtIntervalId *id)
4095 TextWidget w = (TextWidget)clientData;
4096 textDisp *textD = w->text.textD;
4097 int topLineNum, horizOffset, newPos, cursorX, y;
4098 int fontWidth = textD->fontStruct->max_bounds.width;
4099 int fontHeight = textD->fontStruct->ascent + textD->fontStruct->descent;
4101 /* For vertical autoscrolling just dragging the mouse outside of the top
4102 or bottom of the window is sufficient, for horizontal (non-rectangular)
4103 scrolling, see if the position where the CURSOR would go is outside */
4104 newPos = TextDXYToPosition(textD, w->text.mouseX, w->text.mouseY);
4105 if (w->text.dragState == PRIMARY_RECT_DRAG)
4106 cursorX = w->text.mouseX;
4107 else if (!TextDPositionToXY(textD, newPos, &cursorX, &y))
4108 cursorX = w->text.mouseX;
4110 /* Scroll away from the pointer, 1 character (horizontal), or 1 character
4111 for each fontHeight distance from the mouse to the text (vertical) */
4112 TextDGetScroll(textD, &topLineNum, &horizOffset);
4113 if (cursorX >= (int)w->core.width - w->text.marginWidth)
4114 horizOffset += fontWidth;
4115 else if (w->text.mouseX < textD->left)
4116 horizOffset -= fontWidth;
4117 if (w->text.mouseY >= (int)w->core.height - w->text.marginHeight)
4118 topLineNum += 1 + ((w->text.mouseY - (int)w->core.height -
4119 w->text.marginHeight) / fontHeight) + 1;
4120 else if (w->text.mouseY < w->text.marginHeight)
4121 topLineNum -= 1 + ((w->text.marginHeight-w->text.mouseY) / fontHeight);
4122 TextDSetScroll(textD, topLineNum, horizOffset);
4124 /* Continue the drag operation in progress. If none is in progress
4125 (safety check) don't continue to re-establish the timer proc */
4126 if (w->text.dragState == PRIMARY_DRAG) {
4127 adjustSelection(w, w->text.mouseX, w->text.mouseY);
4128 } else if (w->text.dragState == PRIMARY_RECT_DRAG) {
4129 adjustSelection(w, w->text.mouseX, w->text.mouseY);
4130 } else if (w->text.dragState == SECONDARY_DRAG) {
4131 adjustSecondarySelection(w, w->text.mouseX, w->text.mouseY);
4132 } else if (w->text.dragState == SECONDARY_RECT_DRAG) {
4133 adjustSecondarySelection(w, w->text.mouseX, w->text.mouseY);
4134 } else if (w->text.dragState == PRIMARY_BLOCK_DRAG) {
4135 BlockDragSelection(w, w->text.mouseX, w->text.mouseY, USE_LAST);
4136 } else {
4137 w->text.autoScrollProcID = 0;
4138 return;
4141 /* re-establish the timer proc (this routine) to continue processing */
4142 w->text.autoScrollProcID = XtAppAddTimeOut(
4143 XtWidgetToApplicationContext((Widget)w),
4144 w->text.mouseY >= w->text.marginHeight &&
4145 w->text.mouseY < w->core.height - w->text.marginHeight ?
4146 (VERTICAL_SCROLL_DELAY*fontWidth) / fontHeight :
4147 VERTICAL_SCROLL_DELAY, autoScrollTimerProc, w);
4151 ** Xt timer procedure for cursor blinking
4153 static void cursorBlinkTimerProc(XtPointer clientData, XtIntervalId *id)
4155 TextWidget w = (TextWidget)clientData;
4156 textDisp *textD = w->text.textD;
4158 /* Blink the cursor */
4159 if (textD->cursorOn)
4160 TextDBlankCursor(textD);
4161 else
4162 TextDUnblankCursor(textD);
4164 /* re-establish the timer proc (this routine) to continue processing */
4165 w->text.cursorBlinkProcID = XtAppAddTimeOut(
4166 XtWidgetToApplicationContext((Widget)w),
4167 w->text.cursorBlinkRate, cursorBlinkTimerProc, w);
4171 ** Sets the caret to on or off and restart the caret blink timer.
4172 ** This could be used by other modules to modify the caret's blinking.
4174 void ResetCursorBlink(TextWidget textWidget, Boolean startsBlanked)
4176 if (0 != textWidget->text.cursorBlinkRate)
4178 if (0 != textWidget->text.cursorBlinkProcID)
4180 XtRemoveTimeOut(textWidget->text.cursorBlinkProcID);
4183 if (startsBlanked)
4185 TextDBlankCursor(textWidget->text.textD);
4186 } else
4188 TextDUnblankCursor(textWidget->text.textD);
4191 textWidget->text.cursorBlinkProcID
4192 = XtAppAddTimeOut(XtWidgetToApplicationContext((Widget) textWidget),
4193 textWidget->text.cursorBlinkRate, cursorBlinkTimerProc,
4194 textWidget);
4199 ** look at an action procedure's arguments to see if argument "key" has been
4200 ** specified in the argument list
4202 static int hasKey(const char *key, const String *args, const Cardinal *nArgs)
4204 int i;
4206 for (i=0; i<(int)*nArgs; i++)
4207 if (!strCaseCmp(args[i], key))
4208 return True;
4209 return False;
4212 static int max(int i1, int i2)
4214 return i1 >= i2 ? i1 : i2;
4217 static int min(int i1, int i2)
4219 return i1 <= i2 ? i1 : i2;
4223 ** strCaseCmp compares its arguments and returns 0 if the two strings
4224 ** are equal IGNORING case differences. Otherwise returns 1.
4227 static int strCaseCmp(const char *str1, const char *str2)
4229 unsigned const char *c1 = (unsigned const char*) str1;
4230 unsigned const char *c2 = (unsigned const char*) str2;
4232 for (; *c1!='\0' && *c2!='\0'; c1++, c2++)
4233 if (toupper(*c1) != toupper(*c2))
4234 return 1;
4235 if (*c1 == *c2) {
4236 return(0);
4238 else {
4239 return(1);
4243 static void ringIfNecessary(Boolean silent, Widget w)
4245 if (!silent)
4246 XBell(XtDisplay(w), 0);