1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 #ifdef HAVE_CONFIG_H
28 #include "../config.h"
29 #endif
30
31 #include "selection.h"
32 #include "textBuf.h"
33 #include "text.h"
34 #include "nedit.h"
35 #include "file.h"
36 #include "window.h"
37 #include "menu.h"
38 #include "preferences.h"
39 #include "server.h"
40 #include "../util/DialogF.h"
41 #include "../util/fileUtils.h"
42 #include "../util/nedit_malloc.h"
43
44 #include <stdlib.h>
45 #include <stdio.h>
46 #include <ctype.h>
47 #include <string.h>
48 #include <limits.h>
49 #include <sys/param.h>
50
51 #if !defined(
DONT_HAVE_GLOB) && !defined(
USE_MOTIF_GLOB) && !defined(
VMS)
52 #include <glob.h>
53 #endif
54
55 #include <Xm/Xm.h>
56 #include <X11/Xatom.h>
57
58 #ifdef HAVE_DEBUG_H
59 #include "../debug.h"
60 #endif
61
62
63 static void gotoCB(Widget widget, XtPointer window, Atom *sel,
64 Atom *type, XtPointer value,
unsigned long *length,
int *format);
65 static void fileCB(Widget widget, XtPointer window, Atom *sel,
66 Atom *type, XtPointer value,
unsigned long *length,
int *format);
67 static void getAnySelectionCB(Widget widget, XtPointer result, Atom *sel,
68 Atom *type, XtPointer value,
unsigned long *length,
int *format);
69 static void processMarkEvent(Widget w, XtPointer clientData, XEvent *event,
70 Boolean *continueDispatch,
char *action,
int extend);
71 static void markTimeoutProc(XtPointer clientData, XtIntervalId *id);
72 static void markKeyCB(Widget w, XtPointer clientData, XEvent *event,
73 Boolean *continueDispatch);
74 static void gotoMarkKeyCB(Widget w, XtPointer clientData, XEvent *event,
75 Boolean *continueDispatch);
76 static void gotoMarkExtendKeyCB(Widget w, XtPointer clientData, XEvent *event,
77 Boolean *continueDispatch);
78 static void maintainSelection(selection *sel,
int pos,
int nInserted,
79 int nDeleted);
80 static void maintainPosition(
int *position,
int modPos,
int nInserted,
81 int nDeleted);
82
83
84
85
86
87
88 int StringToLineAndCol(
const char *text,
int *lineNum,
int *column) {
89 char *endptr;
90 long tempNum;
91 int textLen;
92
93
94 tempNum = strtol( text, &endptr,
10 );
95
96
97 if ( endptr == text ) { *lineNum = -
1; }
98 else if ( tempNum >=
INT_MAX ) { *lineNum =
INT_MAX; }
99 else if ( tempNum <
0 ) { *lineNum =
0; }
100 else { *lineNum = tempNum; }
101
102
103 for ( textLen = strlen(endptr); textLen >
0; endptr++, textLen-- ) {
104 if (isdigit((
unsigned char)*endptr) ||
105 *endptr ==
'-' || *endptr ==
'+') {
106 break;
107 }
108 }
109
110
111 if ( *endptr !=
'\0' ) {
112 tempNum = strtol( endptr,
NULL,
10 );
113 if ( tempNum >=
INT_MAX ) { *column =
INT_MAX; }
114 else if ( tempNum <
0 ) { *column =
0; }
115 else { *column = tempNum; }
116 }
117 else { *column = -
1; }
118
119 return *lineNum == -
1 && *column == -
1 ? -
1 :
0;
120 }
121
122 void GotoLineNumber(WindowInfo *window)
123 {
124 char lineNumText[
DF_MAX_PROMPT_LENGTH], *params[
1];
125 int lineNum, column, response;
126
127 response = DialogF(
DF_PROMPT, window->shell,
2,
"Goto Line Number",
128 "Goto Line (and/or Column) Number:", lineNumText,
"OK",
"Cancel");
129 if (response ==
2)
130 return;
131
132 if (StringToLineAndCol(lineNumText, &lineNum, &column) == -
1) {
133 XBell(TheDisplay,
0);
134 return;
135 }
136 params[
0] = lineNumText;
137 XtCallActionProc(window->lastFocus,
"goto_line_number",
NULL, params,
1);
138 }
139
140 void GotoSelectedLineNumber(WindowInfo *window, Time time)
141 {
142 XtGetSelectionValue(window->textArea,
XA_PRIMARY,
XA_STRING,
143 gotoCB, window, time);
144 }
145
146 void OpenSelectedFile(WindowInfo *window, Time time)
147 {
148 XtGetSelectionValue(window->textArea,
XA_PRIMARY,
XA_STRING,
149 fileCB, window, time);
150 }
151
152
153
154
155
156
157 char *GetAnySelection(WindowInfo *window)
158 {
159 static char waitingMarker[
1] =
"";
160 char *selText = waitingMarker;
161 XEvent nextEvent;
162
163
164
165 if (window->buffer->primary.selected) {
166 selText = BufGetSelectionText(window->buffer);
167 BufUnsubstituteNullChars(selText, window->buffer);
168 return selText;
169 }
170
171
172 XtGetSelectionValue(window->textArea,
XA_PRIMARY,
XA_STRING,
173 getAnySelectionCB, &selText,
174 XtLastTimestampProcessed(XtDisplay(window->textArea)));
175
176
177 while (selText == waitingMarker) {
178 XtAppNextEvent(XtWidgetToApplicationContext(window->textArea),
179 &nextEvent);
180 ServerDispatchEvent(&nextEvent);
181 }
182 return selText;
183 }
184
185 static void gotoCB(Widget widget, XtPointer wi, Atom *sel,
186 Atom *type, XtPointer v,
unsigned long *length,
int *format)
187 {
188 WindowInfo *window = wi;
189 char *value = v;
190
191
192 char lineText[(
TYPE_INT_STR_SIZE(
int) *
2) +
5];
193 int rc, lineNum, column, position, curCol;
194
195
196 if (*type ==
XT_CONVERT_FAIL || value ==
NULL) {
197 XBell(TheDisplay,
0);
198 return;
199 }
200 if (((
size_t) *length) >
sizeof(lineText) -
1) {
201 XBell(TheDisplay,
0);
202 NEditFree(value);
203 return;
204 }
205
206 if (*format !=
8) {
207 fprintf(stderr,
"XNEdit: Can''t handle non 8-bit text\n");
208 XBell(TheDisplay,
0);
209 NEditFree(value);
210 return;
211 }
212 strncpy(lineText, value,
sizeof(lineText));
213 lineText[
sizeof(lineText) -
1] =
'\0';
214
215 rc = StringToLineAndCol(lineText, &lineNum, &column);
216 NEditFree(value);
217 if (rc == -
1) {
218 XBell(TheDisplay,
0);
219 return;
220 }
221
222
223 if ( lineNum == -
1 ) {
224 position = TextGetCursorPos(widget);
225 if (TextPosToLineAndCol(widget, position, &lineNum, &curCol) == False) {
226 XBell(TheDisplay,
0);
227 return;
228 }
229 }
230
231 else if ( column == -
1 ) {
232 SelectNumberedLine(window, lineNum);
233 return;
234 }
235
236 position = TextLineAndColToPos(widget, lineNum, column );
237 if ( position == -
1 ) {
238 XBell(TheDisplay,
0);
239 return;
240 }
241 TextSetCursorPos(widget, position);
242 }
243
244 static void fileCB(Widget widget, XtPointer wi, Atom *sel,
245 Atom *type, XtPointer v,
unsigned long *length,
int *format)
246 {
247 WindowInfo *window = wi;
248 char *value = v;
249
250 char nameText[
MAXPATHLEN], includeName[
MAXPATHLEN];
251 char filename[
MAXPATHLEN], pathname[
MAXPATHLEN];
252 nameText[
MAXPATHLEN-
1] =
0;
253 char *inPtr, *outPtr;
254 static char includeDir[] =
"/usr/include/";
255
256
257
258 if (*type ==
XT_CONVERT_FAIL || value ==
NULL) {
259 XBell(TheDisplay,
0);
260 return;
261 }
262 if (*length +
2 >
MAXPATHLEN || *length ==
0) {
263 XBell(TheDisplay,
0);
264 NEditFree(value);
265 return;
266 }
267
268 if (*format !=
8) {
269 fprintf(stderr,
"XNEdit: Can''t handle non 8-bit text\n");
270 XBell(TheDisplay,
0);
271 NEditFree(value);
272 return;
273 }
274 strncpy(nameText, value, *length);
275 NEditFree(value);
276 nameText[*length] =
'\0';
277
278
279 if (sscanf(nameText,
"#include \"%[^\"]\"", includeName) ==
1)
280 strcpy(nameText, includeName);
281 else if (sscanf(nameText,
"#include <%[^<>]>", includeName) ==
1)
282 snprintf(nameText,
MAXPATHLEN-
1,
"%s%s", includeDir, includeName);
283
284
285 for (inPtr=nameText, outPtr=nameText; *inPtr!=
'\0'; inPtr++)
286 if (*inPtr !=
' ' && *inPtr !=
'\t' && *inPtr !=
'\n')
287 *outPtr++ = *inPtr;
288 *outPtr =
'\0';
289
290
291 ExpandTilde(nameText);
292
293
294 if (nameText[
0] !=
'/') {
295 strcpy(filename, window->path);
296 strcat(filename, nameText);
297 strcpy(nameText, filename);
298 }
299
300
301
302
303
304
305 #if defined(
DONT_HAVE_GLOB) || defined(
VMS)
306
307 if (ParseFilename(nameText, filename, pathname) !=
0) {
308 XBell(TheDisplay,
0);
309 return;
310 }
311 EditExistingFile(window, filename,
312 pathname,
0,
NULL, False,
NULL, GetPrefOpenInTab(), False);
313 #elif defined(
USE_MOTIF_GLOB)
314 {
char **nameList =
NULL;
315 int i, nFiles =
0, maxFiles =
30;
316
317 if (ParseFilename(nameText, filename, pathname) !=
0) {
318 XBell(TheDisplay,
0);
319 return;
320 }
321 _XmOSGetDirEntries(pathname, filename, XmFILE_ANY_TYPE, False, True,
322 &nameList, &nFiles, &maxFiles);
323 for (i=
0; i<nFiles; i++) {
324 if (ParseFilename(nameList[i], filename, pathname) !=
0) {
325 XBell(TheDisplay,
0);
326 }
327 else {
328 EditExistingFile(window, filename, pathname,
0,
329 NULL, False,
NULL, GetPrefOpenInTab(), False);
330 }
331 }
332 for (i=
0; i<nFiles; i++) {
333 NEditFree(nameList[i]);
334 }
335 NEditFree(nameList);
336 }
337 #else
338 {
glob_t globbuf;
339 int i;
340
341 glob(nameText,
GLOB_NOCHECK,
NULL, &globbuf);
342 for (i=
0; i<(
int)globbuf.gl_pathc; i++) {
343 if (ParseFilename(globbuf.gl_pathv[i], filename, pathname) !=
0)
344 XBell(TheDisplay,
0);
345 else
346 EditExistingFile(GetPrefOpenInTab()? window :
NULL,
347 filename, pathname,
NULL,
NULL,
0,
NULL, False,
NULL,
348 GetPrefOpenInTab(), False);
349 }
350 globfree(&globbuf);
351 }
352 #endif
353 CheckCloseDim();
354 }
355
356 static void getAnySelectionCB(Widget widget, XtPointer voidresult, Atom *sel,
357 Atom *type, XtPointer voidvalue,
unsigned long *length,
int *format)
358 {
359 char **result = voidresult;
360 char *value = voidvalue;
361
362
363 if (*type !=
XA_STRING || *format !=
8) {
364 XBell(TheDisplay,
0);
365 NEditFree(value);
366 *result =
NULL;
367 return;
368 }
369
370
371 *result = (
char*)NEditMalloc(*length +
1);
372 strncpy(*result, value, *length);
373 NEditFree(value);
374 (*result)[*length] =
'\0';
375 }
376
377 void SelectNumberedLine(WindowInfo *window,
int lineNum)
378 {
379 int i, lineStart =
0, lineEnd;
380
381
382 if (lineNum <
1)
383 lineNum =
1;
384 lineEnd = -
1;
385 for (i=
1; i<=lineNum && lineEnd<window->buffer->length; i++) {
386 lineStart = lineEnd +
1;
387 lineEnd = BufEndOfLine(window->buffer, lineStart);
388 }
389
390
391 if (i>lineNum) {
392
393 if (lineEnd < window->buffer->length) {
394 BufSelect(window->buffer, lineStart, lineEnd+
1);
395 }
else {
396
397 BufSelect(window->buffer, lineStart, window->buffer->length);
398 }
399 }
else {
400
401
402 lineStart = window->buffer->length;
403 BufSelect(window->buffer, lineStart, lineStart);
404 XBell(TheDisplay,
0);
405 }
406 MakeSelectionVisible(window, window->lastFocus);
407 TextSetCursorPos(window->lastFocus, lineStart);
408 }
409
410 void MarkDialog(WindowInfo *window)
411 {
412 char letterText[
DF_MAX_PROMPT_LENGTH], *params[
1];
413 int response;
414
415 response = DialogF(
DF_PROMPT, window->shell,
2,
"Mark",
416 "Enter a single letter label to use for recalling\n"
417 "the current selection and cursor position.\n\n"
418 "(To skip this dialog, use the accelerator key,\n"
419 "followed immediately by a letter key (a-z))", letterText,
"OK",
420 "Cancel");
421 if (response ==
2)
422 return;
423 if (strlen(letterText) !=
1 || !isalpha((
unsigned char)letterText[
0])) {
424 XBell(TheDisplay,
0);
425 return;
426 }
427 params[
0] = letterText;
428 XtCallActionProc(window->lastFocus,
"mark",
NULL, params,
1);
429 }
430
431 void GotoMarkDialog(WindowInfo *window,
int extend)
432 {
433 char letterText[
DF_MAX_PROMPT_LENGTH], *params[
2];
434 int response;
435
436 response = DialogF(
DF_PROMPT, window->shell,
2,
"Goto Mark",
437 "Enter the single letter label used to mark\n"
438 "the selection and/or cursor position.\n\n"
439 "(To skip this dialog, use the accelerator\n"
440 "key, followed immediately by the letter)", letterText,
"OK",
441 "Cancel");
442 if (response ==
2)
443 return;
444 if (strlen(letterText) !=
1 || !isalpha((
unsigned char)letterText[
0])) {
445 XBell(TheDisplay,
0);
446 return;
447 }
448 params[
0] = letterText;
449 params[
1] =
"extend";
450 XtCallActionProc(window->lastFocus,
"goto_mark",
NULL, params,
451 extend ?
2 :
1);
452 }
453
454
455
456
457
458
459
460 void BeginMarkCommand(WindowInfo *window)
461 {
462 XtInsertEventHandler(window->lastFocus, KeyPressMask, False,
463 markKeyCB, window, XtListHead);
464 window->markTimeoutID = XtAppAddTimeOut(
465 XtWidgetToApplicationContext(window->shell),
4000,
466 markTimeoutProc, window->lastFocus);
467 }
468
469
470
471
472
473
474
475 void BeginGotoMarkCommand(WindowInfo *window,
int extend)
476 {
477 XtInsertEventHandler(window->lastFocus, KeyPressMask, False,
478 extend ? gotoMarkExtendKeyCB : gotoMarkKeyCB, window, XtListHead);
479 window->markTimeoutID = XtAppAddTimeOut(
480 XtWidgetToApplicationContext(window->shell),
4000,
481 markTimeoutProc, window->lastFocus);
482 }
483
484
485
486
487
488 static void markTimeoutProc(XtPointer clientData, XtIntervalId *id)
489 {
490 Widget w = (Widget)clientData;
491 WindowInfo *window = WidgetToWindow(w);
492
493 XtRemoveEventHandler(w, KeyPressMask, False, markKeyCB, window);
494 XtRemoveEventHandler(w, KeyPressMask, False, gotoMarkKeyCB, window);
495 XtRemoveEventHandler(w, KeyPressMask, False, gotoMarkExtendKeyCB, window);
496 window->markTimeoutID =
0;
497 }
498
499
500
501
502
503
504
505 static void processMarkEvent(Widget w, XtPointer clientData, XEvent *event,
506 Boolean *continueDispatch,
char *action,
int extend)
507 {
508 XKeyEvent *e = (XKeyEvent *)event;
509 WindowInfo *window = WidgetToWindow(w);
510 Modifiers modifiers;
511 KeySym keysym;
512 char *params[
2], string[
2];
513
514 XtTranslateKeycode(TheDisplay, e->keycode, e->state, &modifiers,
515 &keysym);
516 if ((keysym >=
'A' && keysym <=
'Z') || (keysym >=
'a' && keysym <=
'z')) {
517 string[
0] = toupper(keysym);
518 string[
1] =
'\0';
519 params[
0] = string;
520 params[
1] =
"extend";
521 XtCallActionProc(window->lastFocus, action, event, params,
522 extend ?
2 :
1);
523 *continueDispatch = False;
524 }
525 XtRemoveEventHandler(w, KeyPressMask, False, markKeyCB, window);
526 XtRemoveEventHandler(w, KeyPressMask, False, gotoMarkKeyCB, window);
527 XtRemoveEventHandler(w, KeyPressMask, False, gotoMarkExtendKeyCB, window);
528 XtRemoveTimeOut(window->markTimeoutID);
529 }
530 static void markKeyCB(Widget w, XtPointer clientData, XEvent *event,
531 Boolean *continueDispatch)
532 {
533 processMarkEvent(w, clientData, event, continueDispatch,
"mark", False);
534 }
535 static void gotoMarkKeyCB(Widget w, XtPointer clientData, XEvent *event,
536 Boolean *continueDispatch)
537 {
538 processMarkEvent(w, clientData, event, continueDispatch,
"goto_mark",False);
539 }
540 static void gotoMarkExtendKeyCB(Widget w, XtPointer clientData, XEvent *event,
541 Boolean *continueDispatch)
542 {
543 processMarkEvent(w, clientData, event, continueDispatch,
"goto_mark", True);
544 }
545
546 void AddMark(WindowInfo *window, Widget widget,
char label)
547 {
548 int index;
549
550
551
552 label = toupper(label);
553 for (index=
0; index<window->nMarks; index++) {
554 if (window->markTable[index].label == label)
555 break;
556 }
557 if (index >=
MAX_MARKS) {
558 fprintf(stderr,
"no more marks allowed\n");
559 return;
560 }
561 if (index == window->nMarks)
562 window->nMarks++;
563
564
565 window->markTable[index].label = label;
566 memcpy(&window->markTable[index].sel, &window->buffer->primary,
567 sizeof(selection));
568 window->markTable[index].cursorPos = TextGetCursorPos(widget);
569 }
570
571 void GotoMark(WindowInfo *window, Widget w,
char label,
int extendSel)
572 {
573 int index, oldStart, newStart, oldEnd, newEnd, cursorPos;
574 selection *sel, *oldSel;
575
576
577 label = toupper(label);
578 for (index=
0; index<window->nMarks; index++) {
579 if (window->markTable[index].label == label)
580 break;
581 }
582 if (index == window->nMarks) {
583 XBell(TheDisplay,
0);
584 return;
585 }
586
587
588 sel = &window->markTable[index].sel;
589 oldSel = &window->buffer->primary;
590 cursorPos = window->markTable[index].cursorPos;
591 if (extendSel) {
592 oldStart = oldSel->selected ? oldSel->start : TextGetCursorPos(w);
593 oldEnd = oldSel->selected ? oldSel->end : TextGetCursorPos(w);
594 newStart = sel->selected ? sel->start : cursorPos;
595 newEnd = sel->selected ? sel->end : cursorPos;
596 BufSelect(window->buffer, oldStart < newStart ? oldStart : newStart,
597 oldEnd > newEnd ? oldEnd : newEnd);
598 }
else {
599 if (sel->selected) {
600 if (sel->rectangular)
601 BufRectSelect(window->buffer, sel->start, sel->end,
602 sel->rectStart, sel->rectEnd);
603 else
604 BufSelect(window->buffer, sel->start, sel->end);
605 }
else
606 BufUnselect(window->buffer);
607 }
608
609
610
611
612
613
614
615 XtVaSetValues(w, textNautoShowInsertPos, False,
NULL);
616 TextSetCursorPos(w, cursorPos);
617 MakeSelectionVisible(window, window->lastFocus);
618 XtVaSetValues(w, textNautoShowInsertPos, True,
NULL);
619 }
620
621
622
623
624
625 void UpdateMarkTable(WindowInfo *window,
int pos,
int nInserted,
626 int nDeleted)
627 {
628 int i;
629
630 for (i=
0; i<window->nMarks; i++) {
631 maintainSelection(&window->markTable[i].sel, pos, nInserted,
632 nDeleted);
633 maintainPosition(&window->markTable[i].cursorPos, pos, nInserted,
634 nDeleted);
635 }
636 }
637
638
639
640
641
642 static void maintainSelection(selection *sel,
int pos,
int nInserted,
643 int nDeleted)
644 {
645 if (!sel->selected || pos > sel->end)
646 return;
647 maintainPosition(&sel->start, pos, nInserted, nDeleted);
648 maintainPosition(&sel->end, pos, nInserted, nDeleted);
649 if (sel->end <= sel->start)
650 sel->selected = False;
651 }
652
653
654
655
656
657 static void maintainPosition(
int *position,
int modPos,
int nInserted,
658 int nDeleted)
659 {
660 if (modPos > *position)
661 return;
662 if (modPos+nDeleted <= *position)
663 *position += nInserted - nDeleted;
664 else
665 *position = modPos;
666 }
667