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
28
29 #ifdef HAVE_CONFIG_H
30 #include "../config.h"
31 #endif
32
33 #include "textDrag.h"
34 #include "textBuf.h"
35 #include "textDisp.h"
36 #include "textP.h"
37 #include "../util/nedit_malloc.h"
38
39 #include <limits.h>
40
41 #include <X11/Intrinsic.h>
42 #include <X11/IntrinsicP.h>
43 #include <Xm/Xm.h>
44 #include <Xm/XmP.h>
45 #if XmVersion >=
1002
46 #include <Xm/PrimitiveP.h>
47 #endif
48
49 #ifdef HAVE_DEBUG_H
50 #include "../debug.h"
51 #endif
52
53 static void trackModifyRange(
int *rangeStart,
int *modRangeEnd,
54 int *unmodRangeEnd,
int modPos,
int nInserted,
int nDeleted);
55 static void findTextMargins(textBuffer *buf,
int start,
int end,
int *leftMargin,
56 int *rightMargin);
57 static int findRelativeLineStart(textBuffer *buf,
int referencePos,
58 int referenceLineNum,
int newLineNum);
59 static int min3(
int i1,
int i2,
int i3);
60 static int max3(
int i1,
int i2,
int i3);
61 static int max(
int i1,
int i2);
62
63
64
65
66
67
68 void BeginBlockDrag(TextWidget tw)
69 {
70 textDisp *textD = tw->text.textD;
71 textBuffer *buf = textD->buffer;
72 XftFont *font = FontDefault(textD->font);
73 int fontHeight = font->ascent + font->descent;
74 int fontWidth = font->max_advance_width;
75 selection *sel = &buf->primary;
76 int nLines, mousePos, lineStart;
77 int x, y, lineEnd;
78
79 char *text;
80
81
82
83 tw->text.dragOrigBuf = BufCreate();
84 BufSetTabDistance(tw->text.dragOrigBuf, buf->tabDist);
85 tw->text.dragOrigBuf->useTabs = buf->useTabs;
86 text = BufGetAll(buf);
87 BufSetAll(tw->text.dragOrigBuf, text);
88 NEditFree(text);
89 if (sel->rectangular)
90 BufRectSelect(tw->text.dragOrigBuf, sel->start, sel->end, sel->rectStart,
91 sel->rectEnd);
92 else
93 BufSelect(tw->text.dragOrigBuf, sel->start, sel->end);
94
95
96
97
98 if (sel->rectangular) {
99 tw->text.dragXOffset = tw->text.btnDownX + textD->horizOffset -
100 textD->left - sel->rectStart * fontWidth;
101 }
else {
102 if (!TextDPositionToXY(textD, sel->start, &x, &y))
103 x = BufCountDispChars(buf, TextDStartOfLine(textD, sel->start),
104 sel->start) * fontWidth + textD->left -
105 textD->horizOffset;
106 tw->text.dragXOffset = tw->text.btnDownX - x;
107 }
108 mousePos = TextDXYToPosition(textD, tw->text.btnDownX, tw->text.btnDownY);
109 nLines = BufCountLines(buf, sel->start, mousePos);
110 tw->text.dragYOffset = nLines * fontHeight + (((tw->text.btnDownY -
111 tw->text.marginHeight) % fontHeight) - fontHeight/
2);
112 tw->text.dragNLines = BufCountLines(buf, sel->start, sel->end);
113
114
115
116 tw->text.dragInsertPos = sel->start;
117 tw->text.dragInserted = sel->end - sel->start;
118 if (sel->rectangular) {
119 textBuffer *testBuf = BufCreate();
120 char *testText = BufGetRange(buf, sel->start, sel->end);
121 BufSetTabDistance(testBuf, buf->tabDist);
122 testBuf->useTabs = buf->useTabs;
123 BufSetAll(testBuf, testText);
124 NEditFree(testText);
125 BufRemoveRect(testBuf,
0, sel->end - sel->start, sel->rectStart,
126 sel->rectEnd);
127 tw->text.dragDeleted = testBuf->length;
128 BufFree(testBuf);
129 tw->text.dragRectStart = sel->rectStart;
130 }
else {
131 tw->text.dragDeleted =
0;
132 tw->text.dragRectStart =
0;
133 }
134 tw->text.dragType =
DRAG_MOVE;
135 tw->text.dragSourceDeletePos = sel->start;
136 tw->text.dragSourceInserted = tw->text.dragDeleted;
137 tw->text.dragSourceDeleted = tw->text.dragInserted;
138
139
140
141 if (!sel->rectangular) {
142 lineStart = BufStartOfLine(buf, sel->start);
143 if (tw->text.dragNLines ==
0) {
144 tw->text.dragOrigBuf->primary.rectStart =
145 BufCountDispChars(buf, lineStart, sel->start);
146 tw->text.dragOrigBuf->primary.rectEnd =
147 BufCountDispChars(buf, lineStart, sel->end);
148 }
else {
149 lineEnd = BufGetCharacter(buf, sel->end -
1) ==
'\n' ?
150 sel->end -
1 : sel->end;
151 findTextMargins(buf, lineStart, lineEnd,
152 &tw->text.dragOrigBuf->primary.rectStart,
153 &tw->text.dragOrigBuf->primary.rectEnd);
154 }
155 }
156
157
158 tw->text.dragState =
PRIMARY_BLOCK_DRAG;
159
160
161 XtCallCallbacks((Widget)tw, textNdragStartCallback, (XtPointer)
NULL);
162 }
163
164
165
166
167
168 void BlockDragSelection(TextWidget tw,
int x,
int y,
int dragType)
169 {
170 textDisp *textD = tw->text.textD;
171 textBuffer *buf = textD->buffer;
172 XftFont *font = FontDefault(textD->font);
173 int fontHeight = font->ascent + font->descent;
174 int fontWidth = font->max_advance_width;
175 textBuffer *origBuf = tw->text.dragOrigBuf;
176 int dragXOffset = tw->text.dragXOffset;
177 textBuffer *tempBuf;
178 selection *origSel = &origBuf->primary;
179 int rectangular = origSel->rectangular;
180 int overlay, oldDragType = tw->text.dragType;
181 int nLines = tw->text.dragNLines;
182 int insLineNum, insLineStart, insRectStart, insRectEnd, insStart;
183 char *repText, *text, *insText;
184 int modRangeStart = -
1, tempModRangeEnd = -
1, bufModRangeEnd = -
1;
185 int referenceLine, referencePos, tempStart, tempEnd, origSelLen;
186 int insertInserted, insertDeleted, row, column;
187 int origSelLineStart, origSelLineEnd;
188 int sourceInserted, sourceDeleted, sourceDeletePos;
189
190 if (tw->text.dragState !=
PRIMARY_BLOCK_DRAG)
191 return;
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215 tempBuf = BufCreate();
216 tempBuf->tabDist = buf->tabDist;
217 tempBuf->useTabs = buf->useTabs;
218 tempStart = min3(tw->text.dragInsertPos, origSel->start,
219 BufCountBackwardNLines(buf, textD->firstChar, nLines+
2));
220 tempEnd = BufCountForwardNLines(buf, max3(tw->text.dragInsertPos,
221 origSel->start, textD->lastChar), nLines+
2) +
222 origSel->end - origSel->start;
223 text = BufGetRange(origBuf, tempStart, tempEnd);
224 BufSetAll(tempBuf, text);
225 NEditFree(text);
226
227
228 if (dragType ==
USE_LAST)
229 dragType = tw->text.dragType;
230 overlay = dragType ==
DRAG_OVERLAY_MOVE || dragType ==
DRAG_OVERLAY_COPY;
231
232
233
234
235
236 origSelLineStart = BufStartOfLine(origBuf, origSel->start);
237 if (!rectangular && BufGetCharacter(origBuf, origSel->end -
1) ==
'\n')
238 origSelLineEnd = origSel->end -
1;
239 else
240 origSelLineEnd = BufEndOfLine(origBuf, origSel->end);
241 if (!rectangular && overlay && nLines !=
0)
242 dragXOffset -= fontWidth * (origSel->rectStart -
243 (origSel->start - origSelLineStart));
244
245
246
247
248 if (dragType != oldDragType && tw->text.dragSourceDeleted !=
0)
249 trackModifyRange(&modRangeStart, &bufModRangeEnd, &tempModRangeEnd,
250 tw->text.dragSourceDeletePos, tw->text.dragSourceInserted,
251 tw->text.dragSourceDeleted);
252
253
254
255
256
257
258 if (dragType ==
DRAG_MOVE || dragType ==
DRAG_OVERLAY_MOVE) {
259 if (rectangular || overlay) {
260 int prevLen = tempBuf->length;
261 origSelLen = origSelLineEnd - origSelLineStart;
262 if (overlay)
263 BufClearRect(tempBuf, origSelLineStart-tempStart,
264 origSelLineEnd-tempStart, origSel->rectStart,
265 origSel->rectEnd);
266 else
267 BufRemoveRect(tempBuf, origSelLineStart-tempStart,
268 origSelLineEnd-tempStart, origSel->rectStart,
269 origSel->rectEnd);
270 sourceDeletePos = origSelLineStart;
271 sourceInserted = origSelLen - prevLen + tempBuf->length;
272 sourceDeleted = origSelLen;
273 }
else {
274 BufRemove(tempBuf, origSel->start - tempStart,
275 origSel->end - tempStart);
276 sourceDeletePos = origSel->start;
277 sourceInserted =
0;
278 sourceDeleted = origSel->end - origSel->start;
279 }
280 if (dragType != oldDragType)
281 trackModifyRange(&modRangeStart, &tempModRangeEnd, &bufModRangeEnd,
282 sourceDeletePos, sourceInserted, sourceDeleted);
283 }
else {
284 sourceDeletePos =
0;
285 sourceInserted =
0;
286 sourceDeleted =
0;
287 }
288
289
290
291 trackModifyRange(&modRangeStart, &bufModRangeEnd, &tempModRangeEnd,
292 tw->text.dragInsertPos, tw->text.dragInserted, tw->text.dragDeleted);
293
294
295
296
297 TextDXYToUnconstrainedPosition(textD, max(
0, x - dragXOffset),
298 max(
0, y - (tw->text.dragYOffset % fontHeight)), &row, &column);
299 column = TextDOffsetWrappedColumn(textD, row, column);
300 row = TextDOffsetWrappedRow(textD, row);
301 insLineNum = row + textD->topLineNum - tw->text.dragYOffset / fontHeight;
302
303
304
305 if (textD->firstChar > modRangeStart) {
306 referenceLine = textD->topLineNum -
307 BufCountLines(buf, modRangeStart, textD->firstChar);
308 referencePos = modRangeStart;
309 }
else {
310 referencePos = textD->firstChar;
311 referenceLine = textD->topLineNum;
312 }
313
314
315
316 insLineStart = findRelativeLineStart(tempBuf, referencePos - tempStart,
317 referenceLine, insLineNum) + tempStart;
318 if (insLineStart - tempStart == tempBuf->length)
319 insLineStart = BufStartOfLine(tempBuf, insLineStart - tempStart) +
320 tempStart;
321
322
323 if (rectangular || overlay) {
324 insStart = insLineStart;
325 insRectStart = column;
326 }
else {
327 insStart = BufCountForwardDispChars(tempBuf, insLineStart - tempStart,
328 column) + tempStart;
329 insRectStart =
0;
330 }
331
332
333
334 if (insStart == tw->text.dragInsertPos &&
335 insRectStart == tw->text.dragRectStart && dragType == oldDragType) {
336 BufFree(tempBuf);
337 return;
338 }
339
340
341 if (rectangular || overlay) {
342 insText = BufGetTextInRect(origBuf, origSelLineStart, origSelLineEnd,
343 origSel->rectStart, origSel->rectEnd);
344 if (overlay)
345 BufOverlayRect(tempBuf, insStart - tempStart, insRectStart,
346 insRectStart + origSel->rectEnd - origSel->rectStart,
347 insText, &insertInserted, &insertDeleted);
348 else
349 BufInsertCol(tempBuf, insRectStart, insStart - tempStart, insText,
350 &insertInserted, &insertDeleted);
351 trackModifyRange(&modRangeStart, &tempModRangeEnd, &bufModRangeEnd,
352 insStart, insertInserted, insertDeleted);
353 NEditFree(insText);
354 }
else {
355 insText = BufGetSelectionText(origBuf);
356 BufInsert(tempBuf, insStart - tempStart, insText);
357 trackModifyRange(&modRangeStart, &tempModRangeEnd, &bufModRangeEnd,
358 insStart, origSel->end - origSel->start,
0);
359 insertInserted = origSel->end - origSel->start;
360 insertDeleted =
0;
361 NEditFree(insText);
362 }
363
364
365 repText = BufGetRange(tempBuf, modRangeStart - tempStart,
366 tempModRangeEnd - tempStart);
367 BufFree(tempBuf);
368 TextDBlankCursor(textD);
369 BufReplace(buf, modRangeStart, bufModRangeEnd, repText);
370 NEditFree(repText);
371
372
373 tw->text.dragInsertPos = insStart;
374 tw->text.dragRectStart = insRectStart;
375 tw->text.dragInserted = insertInserted;
376 tw->text.dragDeleted = insertDeleted;
377 tw->text.dragSourceDeletePos = sourceDeletePos;
378 tw->text.dragSourceInserted = sourceInserted;
379 tw->text.dragSourceDeleted = sourceDeleted;
380 tw->text.dragType = dragType;
381
382
383 if (rectangular || overlay) {
384 insRectEnd = insRectStart + origSel->rectEnd - origSel->rectStart;
385 BufRectSelect(buf, insStart, insStart + insertInserted, insRectStart,
386 insRectEnd);
387 TextDSetInsertPosition(textD, BufCountForwardDispChars(buf,
388 BufCountForwardNLines(buf, insStart, tw->text.dragNLines),
389 insRectEnd));
390 }
else {
391 BufSelect(buf, insStart, insStart + origSel->end - origSel->start);
392 TextDSetInsertPosition(textD, insStart + origSel->end - origSel->start);
393 }
394 TextDUnblankCursor(textD);
395 XtCallCallbacks((Widget)tw, textNcursorMovementCallback, (XtPointer)
NULL);
396 tw->text.emTabsBeforeCursor =
0;
397 }
398
399
400
401
402 void FinishBlockDrag(TextWidget tw)
403 {
404 dragEndCBStruct endStruct;
405 int modRangeStart = -
1, origModRangeEnd, bufModRangeEnd;
406 char *deletedText;
407
408
409
410
411 trackModifyRange(&modRangeStart, &bufModRangeEnd, &origModRangeEnd,
412 tw->text.dragSourceDeletePos, tw->text.dragSourceInserted,
413 tw->text.dragSourceDeleted);
414 trackModifyRange(&modRangeStart, &bufModRangeEnd, &origModRangeEnd,
415 tw->text.dragInsertPos, tw->text.dragInserted,
416 tw->text.dragDeleted);
417
418
419 deletedText = BufGetRange(tw->text.dragOrigBuf, modRangeStart,
420 origModRangeEnd);
421
422
423 BufFree(tw->text.dragOrigBuf);
424
425
426 tw->text.dragState =
NOT_CLICKED;
427
428
429 endStruct.startPos = modRangeStart;
430 endStruct.nCharsDeleted = origModRangeEnd - modRangeStart;
431 endStruct.nCharsInserted = bufModRangeEnd - modRangeStart;
432 endStruct.deletedText = deletedText;
433 XtCallCallbacks((Widget)tw, textNdragEndCallback, (XtPointer)&endStruct);
434 NEditFree(deletedText);
435 }
436
437
438
439
440 void CancelBlockDrag(TextWidget tw)
441 {
442 textBuffer *buf = tw->text.textD->buffer;
443 textBuffer *origBuf = tw->text.dragOrigBuf;
444 selection *origSel = &origBuf->primary;
445 int modRangeStart = -
1, origModRangeEnd, bufModRangeEnd;
446 char *repText;
447 dragEndCBStruct endStruct;
448
449
450
451 if (tw->text.dragSourceDeleted !=
0)
452 trackModifyRange(&modRangeStart, &bufModRangeEnd, &origModRangeEnd,
453 tw->text.dragSourceDeletePos, tw->text.dragSourceInserted,
454 tw->text.dragSourceDeleted);
455
456
457
458 trackModifyRange(&modRangeStart, &bufModRangeEnd, &origModRangeEnd,
459 tw->text.dragInsertPos, tw->text.dragInserted, tw->text.dragDeleted);
460
461
462 repText = BufGetRange(origBuf, modRangeStart, origModRangeEnd);
463 BufReplace(buf, modRangeStart, bufModRangeEnd, repText);
464 NEditFree(repText);
465
466
467 if (origSel->rectangular)
468 BufRectSelect(buf, origSel->start, origSel->end, origSel->rectStart,
469 origSel->rectEnd);
470 else
471 BufSelect(buf, origSel->start, origSel->end);
472 TextDSetInsertPosition(tw->text.textD, buf->cursorPosHint);
473 XtCallCallbacks((Widget)tw, textNcursorMovementCallback,
NULL);
474 tw->text.emTabsBeforeCursor =
0;
475
476
477 BufFree(origBuf);
478
479
480 tw->text.dragState =
DRAG_CANCELED;
481
482
483 endStruct.startPos =
0;
484 endStruct.nCharsDeleted =
0;
485 endStruct.nCharsInserted =
0;
486 endStruct.deletedText =
NULL;
487 XtCallCallbacks((Widget)tw, textNdragEndCallback, (XtPointer)&endStruct);
488 }
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504 static void trackModifyRange(
int *rangeStart,
int *modRangeEnd,
505 int *unmodRangeEnd,
int modPos,
int nInserted,
int nDeleted)
506 {
507 if (*rangeStart == -
1) {
508 *rangeStart = modPos;
509 *modRangeEnd = modPos + nInserted;
510 *unmodRangeEnd = modPos + nDeleted;
511 }
else {
512 if (modPos < *rangeStart)
513 *rangeStart = modPos;
514 if (modPos + nDeleted > *modRangeEnd) {
515 *unmodRangeEnd += modPos + nDeleted - *modRangeEnd;
516 *modRangeEnd = modPos + nInserted;
517 }
else
518 *modRangeEnd += nInserted - nDeleted;
519 }
520 }
521
522
523
524
525
526 static void findTextMargins(textBuffer *buf,
int start,
int end,
int *leftMargin,
527 int *rightMargin)
528 {
529 char c;
530 int pos, width =
0, maxWidth =
0, minWhite =
INT_MAX, inWhite = True;
531
532 for (pos=start; pos<end; pos++) {
533 c = BufGetCharacter(buf, pos);
534 if (inWhite && c !=
' ' && c !=
'\t') {
535 inWhite = False;
536 if (width < minWhite)
537 minWhite = width;
538 }
539 if (c ==
'\n') {
540 if (width > maxWidth)
541 maxWidth = width;
542 width =
0;
543 inWhite = True;
544 }
else
545 width += BufCharWidth(c, width, buf->tabDist, buf->nullSubsChar);
546 }
547 if (width > maxWidth)
548 maxWidth = width;
549 *leftMargin = minWhite ==
INT_MAX ?
0 : minWhite;
550 *rightMargin = maxWidth;
551 }
552
553
554
555
556
557 static int findRelativeLineStart(textBuffer *buf,
int referencePos,
558 int referenceLineNum,
int newLineNum)
559 {
560 if (newLineNum < referenceLineNum)
561 return BufCountBackwardNLines(buf, referencePos,
562 referenceLineNum - newLineNum);
563 else if (newLineNum > referenceLineNum)
564 return BufCountForwardNLines(buf, referencePos,
565 newLineNum - referenceLineNum);
566 return BufStartOfLine(buf, referencePos);
567 }
568
569 static int min3(
int i1,
int i2,
int i3)
570 {
571 if (i1 <= i2 && i1 <= i3)
572 return i1;
573 return i2 <= i3 ? i2 : i3;
574 }
575
576 static int max3(
int i1,
int i2,
int i3)
577 {
578 if (i1 >= i2 && i1 >= i3)
579 return i1;
580 return i2 >= i3 ? i2 : i3;
581 }
582
583 static int max(
int i1,
int i2)
584 {
585 return i1 >= i2 ? i1 : i2;
586 }
587