#ifdef HAVE_CONFIG_H
#include "../config.h"
#endif
#include "textDrag.h"
#include "textBuf.h"
#include "textDisp.h"
#include "textP.h"
#include "../util/nedit_malloc.h"
#include <limits.h>
#include <X11/Intrinsic.h>
#include <X11/IntrinsicP.h>
#include <Xm/Xm.h>
#include <Xm/XmP.h>
#if XmVersion >=
1002
#include <Xm/PrimitiveP.h>
#endif
#ifdef HAVE_DEBUG_H
#include "../debug.h"
#endif
static void trackModifyRange(
int *rangeStart,
int *modRangeEnd,
int *unmodRangeEnd,
int modPos,
int nInserted,
int nDeleted);
static void findTextMargins(textBuffer *buf,
int start,
int end,
int *leftMargin,
int *rightMargin);
static int findRelativeLineStart(textBuffer *buf,
int referencePos,
int referenceLineNum,
int newLineNum);
static int min3(
int i1,
int i2,
int i3);
static int max3(
int i1,
int i2,
int i3);
static int max(
int i1,
int i2);
void BeginBlockDrag(TextWidget tw)
{
textDisp *textD = tw->text.textD;
textBuffer *buf = textD->buffer;
XftFont *font = FontDefault(textD->font);
int fontHeight = font->ascent + font->descent;
int fontWidth = font->max_advance_width;
selection *sel = &buf->primary;
int nLines, mousePos, lineStart;
int x, y, lineEnd;
char *text;
tw->text.dragOrigBuf = BufCreate();
BufSetTabDistance(tw->text.dragOrigBuf, buf->tabDist);
tw->text.dragOrigBuf->useTabs = buf->useTabs;
text = BufGetAll(buf);
BufSetAll(tw->text.dragOrigBuf, text);
NEditFree(text);
if (sel->rectangular)
BufRectSelect(tw->text.dragOrigBuf, sel->start, sel->end, sel->rectStart,
sel->rectEnd);
else
BufSelect(tw->text.dragOrigBuf, sel->start, sel->end);
if (sel->rectangular) {
tw->text.dragXOffset = tw->text.btnDownX + textD->horizOffset -
textD->left - sel->rectStart * fontWidth;
}
else {
if (!TextDPositionToXY(textD, sel->start, &x, &y))
x = BufCountDispChars(buf, TextDStartOfLine(textD, sel->start),
sel->start) * fontWidth + textD->left -
textD->horizOffset;
tw->text.dragXOffset = tw->text.btnDownX - x;
}
mousePos = TextDXYToPosition(textD, tw->text.btnDownX, tw->text.btnDownY);
nLines = BufCountLines(buf, sel->start, mousePos);
tw->text.dragYOffset = nLines * fontHeight + (((tw->text.btnDownY -
tw->text.marginHeight) % fontHeight) - fontHeight/
2);
tw->text.dragNLines = BufCountLines(buf, sel->start, sel->end);
tw->text.dragInsertPos = sel->start;
tw->text.dragInserted = sel->end - sel->start;
if (sel->rectangular) {
textBuffer *testBuf = BufCreate();
char *testText = BufGetRange(buf, sel->start, sel->end);
BufSetTabDistance(testBuf, buf->tabDist);
testBuf->useTabs = buf->useTabs;
BufSetAll(testBuf, testText);
NEditFree(testText);
BufRemoveRect(testBuf,
0, sel->end - sel->start, sel->rectStart,
sel->rectEnd);
tw->text.dragDeleted = testBuf->length;
BufFree(testBuf);
tw->text.dragRectStart = sel->rectStart;
}
else {
tw->text.dragDeleted =
0;
tw->text.dragRectStart =
0;
}
tw->text.dragType =
DRAG_MOVE;
tw->text.dragSourceDeletePos = sel->start;
tw->text.dragSourceInserted = tw->text.dragDeleted;
tw->text.dragSourceDeleted = tw->text.dragInserted;
if (!sel->rectangular) {
lineStart = BufStartOfLine(buf, sel->start);
if (tw->text.dragNLines ==
0) {
tw->text.dragOrigBuf->primary.rectStart =
BufCountDispChars(buf, lineStart, sel->start);
tw->text.dragOrigBuf->primary.rectEnd =
BufCountDispChars(buf, lineStart, sel->end);
}
else {
lineEnd = BufGetCharacter(buf, sel->end -
1) ==
'\n' ?
sel->end -
1 : sel->end;
findTextMargins(buf, lineStart, lineEnd,
&tw->text.dragOrigBuf->primary.rectStart,
&tw->text.dragOrigBuf->primary.rectEnd);
}
}
tw->text.dragState =
PRIMARY_BLOCK_DRAG;
XtCallCallbacks((Widget)tw, textNdragStartCallback, (XtPointer)
NULL);
}
void BlockDragSelection(TextWidget tw,
int x,
int y,
int dragType)
{
textDisp *textD = tw->text.textD;
textBuffer *buf = textD->buffer;
XftFont *font = FontDefault(textD->font);
int fontHeight = font->ascent + font->descent;
int fontWidth = font->max_advance_width;
textBuffer *origBuf = tw->text.dragOrigBuf;
int dragXOffset = tw->text.dragXOffset;
textBuffer *tempBuf;
selection *origSel = &origBuf->primary;
int rectangular = origSel->rectangular;
int overlay, oldDragType = tw->text.dragType;
int nLines = tw->text.dragNLines;
int insLineNum, insLineStart, insRectStart, insRectEnd, insStart;
char *repText, *text, *insText;
int modRangeStart = -
1, tempModRangeEnd = -
1, bufModRangeEnd = -
1;
int referenceLine, referencePos, tempStart, tempEnd, origSelLen;
int insertInserted, insertDeleted, row, column;
int origSelLineStart, origSelLineEnd;
int sourceInserted, sourceDeleted, sourceDeletePos;
if (tw->text.dragState !=
PRIMARY_BLOCK_DRAG)
return;
tempBuf = BufCreate();
tempBuf->tabDist = buf->tabDist;
tempBuf->useTabs = buf->useTabs;
tempStart = min3(tw->text.dragInsertPos, origSel->start,
BufCountBackwardNLines(buf, textD->firstChar, nLines+
2));
tempEnd = BufCountForwardNLines(buf, max3(tw->text.dragInsertPos,
origSel->start, textD->lastChar), nLines+
2) +
origSel->end - origSel->start;
text = BufGetRange(origBuf, tempStart, tempEnd);
BufSetAll(tempBuf, text);
NEditFree(text);
if (dragType ==
USE_LAST)
dragType = tw->text.dragType;
overlay = dragType ==
DRAG_OVERLAY_MOVE || dragType ==
DRAG_OVERLAY_COPY;
origSelLineStart = BufStartOfLine(origBuf, origSel->start);
if (!rectangular && BufGetCharacter(origBuf, origSel->end -
1) ==
'\n')
origSelLineEnd = origSel->end -
1;
else
origSelLineEnd = BufEndOfLine(origBuf, origSel->end);
if (!rectangular && overlay && nLines !=
0)
dragXOffset -= fontWidth * (origSel->rectStart -
(origSel->start - origSelLineStart));
if (dragType != oldDragType && tw->text.dragSourceDeleted !=
0)
trackModifyRange(&modRangeStart, &bufModRangeEnd, &tempModRangeEnd,
tw->text.dragSourceDeletePos, tw->text.dragSourceInserted,
tw->text.dragSourceDeleted);
if (dragType ==
DRAG_MOVE || dragType ==
DRAG_OVERLAY_MOVE) {
if (rectangular || overlay) {
int prevLen = tempBuf->length;
origSelLen = origSelLineEnd - origSelLineStart;
if (overlay)
BufClearRect(tempBuf, origSelLineStart-tempStart,
origSelLineEnd-tempStart, origSel->rectStart,
origSel->rectEnd);
else
BufRemoveRect(tempBuf, origSelLineStart-tempStart,
origSelLineEnd-tempStart, origSel->rectStart,
origSel->rectEnd);
sourceDeletePos = origSelLineStart;
sourceInserted = origSelLen - prevLen + tempBuf->length;
sourceDeleted = origSelLen;
}
else {
BufRemove(tempBuf, origSel->start - tempStart,
origSel->end - tempStart);
sourceDeletePos = origSel->start;
sourceInserted =
0;
sourceDeleted = origSel->end - origSel->start;
}
if (dragType != oldDragType)
trackModifyRange(&modRangeStart, &tempModRangeEnd, &bufModRangeEnd,
sourceDeletePos, sourceInserted, sourceDeleted);
}
else {
sourceDeletePos =
0;
sourceInserted =
0;
sourceDeleted =
0;
}
trackModifyRange(&modRangeStart, &bufModRangeEnd, &tempModRangeEnd,
tw->text.dragInsertPos, tw->text.dragInserted, tw->text.dragDeleted);
TextDXYToUnconstrainedPosition(textD, max(
0, x - dragXOffset),
max(
0, y - (tw->text.dragYOffset % fontHeight)), &row, &column);
column = TextDOffsetWrappedColumn(textD, row, column);
row = TextDOffsetWrappedRow(textD, row);
insLineNum = row + textD->topLineNum - tw->text.dragYOffset / fontHeight;
if (textD->firstChar > modRangeStart) {
referenceLine = textD->topLineNum -
BufCountLines(buf, modRangeStart, textD->firstChar);
referencePos = modRangeStart;
}
else {
referencePos = textD->firstChar;
referenceLine = textD->topLineNum;
}
insLineStart = findRelativeLineStart(tempBuf, referencePos - tempStart,
referenceLine, insLineNum) + tempStart;
if (insLineStart - tempStart == tempBuf->length)
insLineStart = BufStartOfLine(tempBuf, insLineStart - tempStart) +
tempStart;
if (rectangular || overlay) {
insStart = insLineStart;
insRectStart = column;
}
else {
insStart = BufCountForwardDispChars(tempBuf, insLineStart - tempStart,
column) + tempStart;
insRectStart =
0;
}
if (insStart == tw->text.dragInsertPos &&
insRectStart == tw->text.dragRectStart && dragType == oldDragType) {
BufFree(tempBuf);
return;
}
if (rectangular || overlay) {
insText = BufGetTextInRect(origBuf, origSelLineStart, origSelLineEnd,
origSel->rectStart, origSel->rectEnd);
if (overlay)
BufOverlayRect(tempBuf, insStart - tempStart, insRectStart,
insRectStart + origSel->rectEnd - origSel->rectStart,
insText, &insertInserted, &insertDeleted);
else
BufInsertCol(tempBuf, insRectStart, insStart - tempStart, insText,
&insertInserted, &insertDeleted);
trackModifyRange(&modRangeStart, &tempModRangeEnd, &bufModRangeEnd,
insStart, insertInserted, insertDeleted);
NEditFree(insText);
}
else {
insText = BufGetSelectionText(origBuf);
BufInsert(tempBuf, insStart - tempStart, insText);
trackModifyRange(&modRangeStart, &tempModRangeEnd, &bufModRangeEnd,
insStart, origSel->end - origSel->start,
0);
insertInserted = origSel->end - origSel->start;
insertDeleted =
0;
NEditFree(insText);
}
repText = BufGetRange(tempBuf, modRangeStart - tempStart,
tempModRangeEnd - tempStart);
BufFree(tempBuf);
TextDBlankCursor(textD);
BufReplace(buf, modRangeStart, bufModRangeEnd, repText);
NEditFree(repText);
tw->text.dragInsertPos = insStart;
tw->text.dragRectStart = insRectStart;
tw->text.dragInserted = insertInserted;
tw->text.dragDeleted = insertDeleted;
tw->text.dragSourceDeletePos = sourceDeletePos;
tw->text.dragSourceInserted = sourceInserted;
tw->text.dragSourceDeleted = sourceDeleted;
tw->text.dragType = dragType;
if (rectangular || overlay) {
insRectEnd = insRectStart + origSel->rectEnd - origSel->rectStart;
BufRectSelect(buf, insStart, insStart + insertInserted, insRectStart,
insRectEnd);
TextDSetInsertPosition(textD, BufCountForwardDispChars(buf,
BufCountForwardNLines(buf, insStart, tw->text.dragNLines),
insRectEnd));
}
else {
BufSelect(buf, insStart, insStart + origSel->end - origSel->start);
TextDSetInsertPosition(textD, insStart + origSel->end - origSel->start);
}
TextDUnblankCursor(textD);
XtCallCallbacks((Widget)tw, textNcursorMovementCallback, (XtPointer)
NULL);
tw->text.emTabsBeforeCursor =
0;
}
void FinishBlockDrag(TextWidget tw)
{
dragEndCBStruct endStruct;
int modRangeStart = -
1, origModRangeEnd, bufModRangeEnd;
char *deletedText;
trackModifyRange(&modRangeStart, &bufModRangeEnd, &origModRangeEnd,
tw->text.dragSourceDeletePos, tw->text.dragSourceInserted,
tw->text.dragSourceDeleted);
trackModifyRange(&modRangeStart, &bufModRangeEnd, &origModRangeEnd,
tw->text.dragInsertPos, tw->text.dragInserted,
tw->text.dragDeleted);
deletedText = BufGetRange(tw->text.dragOrigBuf, modRangeStart,
origModRangeEnd);
BufFree(tw->text.dragOrigBuf);
tw->text.dragState =
NOT_CLICKED;
endStruct.startPos = modRangeStart;
endStruct.nCharsDeleted = origModRangeEnd - modRangeStart;
endStruct.nCharsInserted = bufModRangeEnd - modRangeStart;
endStruct.deletedText = deletedText;
XtCallCallbacks((Widget)tw, textNdragEndCallback, (XtPointer)&endStruct);
NEditFree(deletedText);
}
void CancelBlockDrag(TextWidget tw)
{
textBuffer *buf = tw->text.textD->buffer;
textBuffer *origBuf = tw->text.dragOrigBuf;
selection *origSel = &origBuf->primary;
int modRangeStart = -
1, origModRangeEnd, bufModRangeEnd;
char *repText;
dragEndCBStruct endStruct;
if (tw->text.dragSourceDeleted !=
0)
trackModifyRange(&modRangeStart, &bufModRangeEnd, &origModRangeEnd,
tw->text.dragSourceDeletePos, tw->text.dragSourceInserted,
tw->text.dragSourceDeleted);
trackModifyRange(&modRangeStart, &bufModRangeEnd, &origModRangeEnd,
tw->text.dragInsertPos, tw->text.dragInserted, tw->text.dragDeleted);
repText = BufGetRange(origBuf, modRangeStart, origModRangeEnd);
BufReplace(buf, modRangeStart, bufModRangeEnd, repText);
NEditFree(repText);
if (origSel->rectangular)
BufRectSelect(buf, origSel->start, origSel->end, origSel->rectStart,
origSel->rectEnd);
else
BufSelect(buf, origSel->start, origSel->end);
TextDSetInsertPosition(tw->text.textD, buf->cursorPosHint);
XtCallCallbacks((Widget)tw, textNcursorMovementCallback,
NULL);
tw->text.emTabsBeforeCursor =
0;
BufFree(origBuf);
tw->text.dragState =
DRAG_CANCELED;
endStruct.startPos =
0;
endStruct.nCharsDeleted =
0;
endStruct.nCharsInserted =
0;
endStruct.deletedText =
NULL;
XtCallCallbacks((Widget)tw, textNdragEndCallback, (XtPointer)&endStruct);
}
static void trackModifyRange(
int *rangeStart,
int *modRangeEnd,
int *unmodRangeEnd,
int modPos,
int nInserted,
int nDeleted)
{
if (*rangeStart == -
1) {
*rangeStart = modPos;
*modRangeEnd = modPos + nInserted;
*unmodRangeEnd = modPos + nDeleted;
}
else {
if (modPos < *rangeStart)
*rangeStart = modPos;
if (modPos + nDeleted > *modRangeEnd) {
*unmodRangeEnd += modPos + nDeleted - *modRangeEnd;
*modRangeEnd = modPos + nInserted;
}
else
*modRangeEnd += nInserted - nDeleted;
}
}
static void findTextMargins(textBuffer *buf,
int start,
int end,
int *leftMargin,
int *rightMargin)
{
char c;
int pos, width =
0, maxWidth =
0, minWhite =
INT_MAX, inWhite = True;
for (pos=start; pos<end; pos++) {
c = BufGetCharacter(buf, pos);
if (inWhite && c !=
' ' && c !=
'\t') {
inWhite = False;
if (width < minWhite)
minWhite = width;
}
if (c ==
'\n') {
if (width > maxWidth)
maxWidth = width;
width =
0;
inWhite = True;
}
else
width += BufCharWidth(c, width, buf->tabDist, buf->nullSubsChar);
}
if (width > maxWidth)
maxWidth = width;
*leftMargin = minWhite ==
INT_MAX ?
0 : minWhite;
*rightMargin = maxWidth;
}
static int findRelativeLineStart(textBuffer *buf,
int referencePos,
int referenceLineNum,
int newLineNum)
{
if (newLineNum < referenceLineNum)
return BufCountBackwardNLines(buf, referencePos,
referenceLineNum - newLineNum);
else if (newLineNum > referenceLineNum)
return BufCountForwardNLines(buf, referencePos,
newLineNum - referenceLineNum);
return BufStartOfLine(buf, referencePos);
}
static int min3(
int i1,
int i2,
int i3)
{
if (i1 <= i2 && i1 <= i3)
return i1;
return i2 <= i3 ? i2 : i3;
}
static int max3(
int i1,
int i2,
int i3)
{
if (i1 >= i2 && i1 >= i3)
return i1;
return i2 >= i3 ? i2 : i3;
}
static int max(
int i1,
int i2)
{
return i1 >= i2 ? i1 : i2;
}