#ifdef HAVE_CONFIG_H
#include "../config.h"
#endif
#include "shift.h"
#include "textBuf.h"
#include "text.h"
#include "nedit.h"
#include "window.h"
#include "../util/nedit_malloc.h"
#include <string.h>
#include <limits.h>
#include <ctype.h>
#include <locale.h>
#include <wctype.h>
#include <wchar.h>
#include <sys/param.h>
#include <Xm/Xm.h>
#ifdef HAVE_DEBUG_H
#include "../debug.h"
#endif
static void shiftRect(WindowInfo *window,
int direction,
int byTab,
int selStart,
int selEnd,
int rectStart,
int rectEnd);
static void changeCase(WindowInfo *window,
int makeUpper);
static char *shiftLineRight(
char *line,
int lineLen,
int tabsAllowed,
int tabDist,
int nChars);
static char *shiftLineLeft(
char *line,
int lineLen,
int tabDist,
int nChars);
static int findLeftMargin(
char *text,
int length,
int tabDist);
static char *fillParagraphs(
char *text,
int rightMargin,
int tabDist,
int useTabs,
char nullSubsChar,
int *filledLen,
int alignWithFirst);
static char *fillParagraph(
char *text,
int leftMargin,
int firstLineIndent,
int rightMargin,
int tabDist,
int allowTabs,
char nullSubsChar,
int *filledLen);
static char *makeIndentString(
int indent,
int tabDist,
int allowTabs,
int *nChars);
static int atTabStop(
int pos,
int tabDist);
static int nextTab(
int pos,
int tabDist);
static int countLines(
const char *text);
static int findParagraphStart(textBuffer *buf,
int startPos);
static int findParagraphEnd(textBuffer *buf,
int startPos);
void ShiftSelection(WindowInfo *window,
int direction,
int byTab)
{
int selStart, selEnd, isRect, rectStart, rectEnd;
int shiftedLen, newEndPos, cursorPos, origLength, emTabDist, shiftDist;
char *text, *shiftedText;
textBuffer *buf = window->buffer;
if (!BufGetSelectionPos(buf, &selStart, &selEnd, &isRect,
&rectStart, &rectEnd)) {
cursorPos = TextGetCursorPos(window->lastFocus);
selStart = BufStartOfLine(buf, cursorPos);
selEnd = BufEndOfLine(buf, cursorPos);
if (selEnd < buf->length)
selEnd++;
BufSelect(buf, selStart, selEnd);
isRect = False;
text = BufGetRange(buf, selStart, selEnd);
}
else if (isRect) {
cursorPos = TextGetCursorPos(window->lastFocus);
origLength = buf->length;
shiftRect(window, direction, byTab, selStart, selEnd, rectStart,
rectEnd);
TextSetCursorPos(window->lastFocus, (cursorPos < (selEnd+selStart)/
2) ?
selStart : cursorPos + (buf->length - origLength));
return;
}
else {
selStart = BufStartOfLine(buf, selStart);
if (selEnd !=
0 && BufGetCharacter(buf, selEnd-
1) !=
'\n') {
selEnd = BufEndOfLine(buf, selEnd);
if (selEnd < buf->length)
selEnd++;
}
BufSelect(buf, selStart, selEnd);
text = BufGetRange(buf, selStart, selEnd);
}
if (byTab) {
XtVaGetValues(window->textArea, textNemulateTabs, &emTabDist,
NULL);
shiftDist = emTabDist ==
0 ? buf->tabDist : emTabDist;
}
else
shiftDist =
1;
shiftedText = ShiftText(text, direction, buf->useTabs, buf->tabDist,
shiftDist, &shiftedLen);
NEditFree(text);
BufReplaceSelected(buf, shiftedText);
NEditFree(shiftedText);
newEndPos = selStart + shiftedLen;
BufSelect(buf, selStart, newEndPos);
}
static void shiftRect(WindowInfo *window,
int direction,
int byTab,
int selStart,
int selEnd,
int rectStart,
int rectEnd)
{
int offset, emTabDist;
textBuffer *tempBuf, *buf = window->buffer;
char *text;
selStart = BufStartOfLine(buf, selStart);
selEnd = BufEndOfLine(buf, selEnd);
if (byTab) {
XtVaGetValues(window->textArea, textNemulateTabs, &emTabDist,
NULL);
offset = emTabDist ==
0 ? buf->tabDist : emTabDist;
}
else
offset =
1;
offset *= direction ==
SHIFT_LEFT ? -
1 :
1;
if (rectStart + offset <
0)
offset = -rectStart;
tempBuf = BufCreate();
tempBuf->tabDist = buf->tabDist;
tempBuf->useTabs = buf->useTabs;
text = BufGetRange(buf, selStart, selEnd);
BufSetAll(tempBuf, text);
NEditFree(text);
text = BufGetTextInRect(buf, selStart, selEnd, rectStart, rectEnd);
BufRemoveRect(tempBuf,
0, selEnd-selStart, rectStart, rectEnd);
BufInsertCol(tempBuf, rectStart+offset,
0, text,
NULL,
NULL);
NEditFree(text);
BufReplace(buf, selStart, selEnd, BufAsString(tempBuf));
BufRectSelect(buf, selStart, selStart + tempBuf->length,
rectStart+offset, rectEnd+offset);
BufFree(tempBuf);
}
void UpcaseSelection(WindowInfo *window)
{
changeCase(window, True);
}
void DowncaseSelection(WindowInfo *window)
{
changeCase(window, False);
}
static void changeCase(WindowInfo *window,
int makeUpper)
{
textBuffer *buf = window->buffer;
char *text, *c;
int cursorPos, start, end, isRect, rectStart, rectEnd;
char *bak_locale =
NULL;
if(!XNEditDefaultCharsetIsUTF8()) {
bak_locale = setlocale(
LC_CTYPE,
NULL);
setlocale(
LC_CTYPE,
"C.UTF-8");
}
if (!BufGetSelectionPos(buf, &start, &end, &isRect, &rectStart, &rectEnd)) {
cursorPos = TextGetCursorPos(window->lastFocus);
if (cursorPos ==
0) {
XBell(TheDisplay,
0);
if(bak_locale) setlocale(
LC_CTYPE, bak_locale);
return;
}
int leftPos = BufLeftPos(buf, cursorPos);
wchar_t w = BufGetCharacterW(buf, leftPos);
wchar_t wc = makeUpper ? towupper(w) : towlower(w);
if(wc ==
0) wc = w;
char bufChar[
8];
int clen = wctomb(bufChar, wc);
bufChar[clen] =
0;
BufReplace(buf, leftPos, cursorPos, bufChar);
}
else {
Boolean modified = False;
text = BufGetSelectionText(buf);
size_t textlen = strlen(text);
size_t conv_alloc = textlen +
8;
size_t conv_len =
0;
char *converted = NEditMalloc(conv_alloc);
mbstate_t state;
memset(&state,
0,
sizeof(
mbstate_t));
int inc =
1;
size_t i =
0;
size_t cpos =
0;
char utf8[
8];
int append_len =
0;
for(c = text; *c !=
'\0'; c += inc) {
inc = Utf8CharLen((
unsigned char*)c);
if(i + inc > textlen) {
inc = textlen - i;
}
wchar_t w;
size_t nconverted = mbrtowc(&w, c, inc, &state);
if(nconverted >
0) {
wchar_t wc = makeUpper ? towupper(w) : towlower(w);
append_len = wctomb(utf8, wc);
if(wc != w) {
modified = True;
}
inc = nconverted;
}
else {
append_len = inc;
}
if(conv_alloc - cpos -
1 < append_len) {
conv_alloc +=
32;
converted = NEditRealloc(converted, conv_alloc);
}
memcpy(converted + cpos, utf8, append_len);
conv_len += append_len;
cpos += append_len;
i += inc;
}
converted[conv_len] =
'\0';
if (modified) {
BufReplaceSelected(buf, converted);
}
NEditFree(converted);
NEditFree(text);
if (isRect)
BufRectSelect(buf, start, end, rectStart, rectEnd);
else
BufSelect(buf, start, end);
}
if(bak_locale) setlocale(
LC_CTYPE, bak_locale);
}
void FillSelection(WindowInfo *window)
{
textBuffer *buf = window->buffer;
char *text, *filledText;
int left, right, nCols, len, isRect, rectStart, rectEnd;
int rightMargin, wrapMargin;
int insertPos = TextGetCursorPos(window->lastFocus);
int hasSelection = window->buffer->primary.selected;
if (!BufGetSelectionPos(buf, &left, &right, &isRect, &rectStart, &rectEnd)) {
left = findParagraphStart(buf, insertPos);
right = findParagraphEnd(buf, insertPos);
if (left == right) {
XBell(TheDisplay,
0);
return;
}
text = BufGetRange(buf, left, right);
}
else if (isRect) {
left = BufStartOfLine(buf, left);
right = BufEndOfLine(buf, right);
text = BufGetTextInRect(buf, left, right, rectStart,
INT_MAX);
}
else {
left = BufStartOfLine(buf, left);
if (right !=
0 && BufGetCharacter(buf, right-
1) !=
'\n') {
right = BufEndOfLine(buf, right);
if (right < buf->length)
right++;
}
BufSelect(buf, left, right);
text = BufGetRange(buf, left, right);
}
if (hasSelection && isRect) {
rightMargin = rectEnd - rectStart;
}
else
{
XtVaGetValues(window->textArea,
textNcolumns, &nCols,
textNwrapMargin, &wrapMargin,
NULL);
rightMargin = (wrapMargin ==
0 ? nCols : wrapMargin);
}
filledText = fillParagraphs(text, rightMargin, buf->tabDist, buf->useTabs,
buf->nullSubsChar, &len, False);
NEditFree(text);
if (hasSelection && isRect) {
BufReplaceRect(buf, left, right, rectStart,
INT_MAX, filledText);
BufRectSelect(buf, left,
BufEndOfLine(buf, BufCountForwardNLines(buf, left,
countLines(filledText)-
1)), rectStart, rectEnd);
}
else {
BufReplace(buf, left, right, filledText);
if (hasSelection)
BufSelect(buf, left, left + len);
}
NEditFree(filledText);
if (hasSelection && isRect)
TextSetCursorPos(window->lastFocus, buf->cursorPosHint);
else
TextSetCursorPos(window->lastFocus, insertPos < left ? left :
(insertPos > left + len ? left + len : insertPos));
}
char *ShiftText(
char *text,
int direction,
int tabsAllowed,
int tabDist,
int nChars,
int *newLen)
{
char *shiftedText, *shiftedLine;
char *textPtr, *lineStartPtr, *shiftedPtr;
int bufLen;
if (direction ==
SHIFT_RIGHT)
bufLen = strlen(text) + countLines(text) * nChars;
else
bufLen = strlen(text) + countLines(text) * tabDist;
shiftedText = (
char*)NEditMalloc(bufLen +
1);
lineStartPtr = text;
textPtr = text;
shiftedPtr = shiftedText;
while (
TRUE) {
if (*textPtr==
'\n' || *textPtr==
'\0') {
shiftedLine = (direction ==
SHIFT_RIGHT) ?
shiftLineRight(lineStartPtr, textPtr-lineStartPtr,
tabsAllowed, tabDist, nChars) :
shiftLineLeft(lineStartPtr, textPtr-lineStartPtr, tabDist,
nChars);
strcpy(shiftedPtr, shiftedLine);
shiftedPtr += strlen(shiftedLine);
NEditFree(shiftedLine);
if (*textPtr ==
'\0') {
*shiftedPtr =
'\0';
break;
}
else {
*shiftedPtr++ = *textPtr++;
}
lineStartPtr = textPtr;
}
else
textPtr++;
}
*newLen = shiftedPtr - shiftedText;
return shiftedText;
}
static char *shiftLineRight(
char *line,
int lineLen,
int tabsAllowed,
int tabDist,
int nChars)
{
char *lineOut;
char *lineInPtr, *lineOutPtr;
int whiteWidth, i;
lineInPtr = line;
lineOut = (
char*)NEditMalloc(lineLen + nChars +
1);
lineOutPtr = lineOut;
whiteWidth =
0;
while (
TRUE) {
if (*lineInPtr ==
'\0' || (lineInPtr - line) >= lineLen) {
*lineOut =
'\0';
return lineOut;
}
else if (*lineInPtr ==
' ') {
whiteWidth++;
*lineOutPtr++ = *lineInPtr++;
}
else if (*lineInPtr ==
'\t') {
whiteWidth = nextTab(whiteWidth, tabDist);
*lineOutPtr++ = *lineInPtr++;
}
else {
for (i=
0; i<nChars; i++) {
*lineOutPtr++ =
' ';
whiteWidth++;
if (tabsAllowed && atTabStop(whiteWidth, tabDist)) {
lineOutPtr -= tabDist;
*lineOutPtr++ =
'\t';
}
}
while (*lineInPtr!=
'\0' && (lineInPtr - line) < lineLen)
*lineOutPtr++ = *lineInPtr++;
*lineOutPtr =
'\0';
return lineOut;
}
}
}
static char *shiftLineLeft(
char *line,
int lineLen,
int tabDist,
int nChars)
{
char *lineOut;
int i, whiteWidth, lastWhiteWidth, whiteGoal;
char *lineInPtr, *lineOutPtr;
lineInPtr = line;
lineOut = (
char*)NEditMalloc(lineLen + tabDist +
1);
lineOutPtr = lineOut;
whiteWidth =
0;
lastWhiteWidth =
0;
while (
TRUE) {
if (*lineInPtr ==
'\0' || (lineInPtr - line) >= lineLen) {
*lineOut =
'\0';
return lineOut;
}
else if (*lineInPtr ==
' ') {
whiteWidth++;
*lineOutPtr++ = *lineInPtr++;
}
else if (*lineInPtr ==
'\t') {
lastWhiteWidth = whiteWidth;
whiteWidth = nextTab(whiteWidth, tabDist);
*lineOutPtr++ = *lineInPtr++;
}
else {
for (i=
1; i<=nChars; i++) {
if (lineOutPtr > lineOut) {
if (*(lineOutPtr-
1) ==
' ') {
lineOutPtr--;
}
else {
lineOutPtr--;
whiteGoal = whiteWidth - i;
whiteWidth = lastWhiteWidth;
while (whiteWidth < whiteGoal) {
*lineOutPtr++ =
' ';
whiteWidth++;
}
}
}
}
while (*lineInPtr!=
'\0' && (lineInPtr - line) < lineLen)
*lineOutPtr++ = *lineInPtr++;
*lineOutPtr =
'\0';
return lineOut;
}
}
}
static int atTabStop(
int pos,
int tabDist)
{
return (pos%tabDist ==
0);
}
static int nextTab(
int pos,
int tabDist)
{
return (pos/tabDist)*tabDist + tabDist;
}
static int countLines(
const char *text)
{
int count =
1;
while(*text !=
'\0') {
if (*text++ ==
'\n') {
count++;
}
}
return count;
}
static int findLeftMargin(
char *text,
int length,
int tabDist)
{
char *c;
int col =
0, leftMargin =
INT_MAX;
int inMargin = True;
for (c=text; *c!=
'\0' && c-text<length; c++) {
if (*c ==
'\t') {
col += BufCharWidth(
'\t', col, tabDist,
'\0');
}
else if (*c ==
' ') {
col++;
}
else if (*c ==
'\n') {
col =
0;
inMargin = True;
}
else {
if (col < leftMargin && inMargin)
leftMargin = col;
inMargin = False;
}
}
if (leftMargin ==
INT_MAX)
return 0;
return leftMargin;
}
static char *fillParagraphs(
char *text,
int rightMargin,
int tabDist,
int useTabs,
char nullSubsChar,
int *filledLen,
int alignWithFirst)
{
int paraStart, paraEnd, fillEnd;
char *c, ch, *secondLineStart, *paraText, *filledText;
int firstLineLen, firstLineIndent, leftMargin, len;
textBuffer *buf;
buf = BufCreate();
BufSetAll(buf, text);
paraStart =
0;
for (;;) {
while (paraStart < buf->length) {
ch = BufGetCharacter(buf, paraStart);
if (ch !=
' ' && ch !=
'\t' && ch !=
'\n')
break;
paraStart++;
}
if (paraStart >= buf->length)
break;
paraStart = BufStartOfLine(buf, paraStart);
paraEnd = findParagraphEnd(buf, paraStart);
fillEnd = alignWithFirst ? buf->length : paraEnd;
paraText = BufGetRange(buf, paraStart, fillEnd);
for (c=paraText ; *c!=
'\0' && *c!=
'\n'; c++);
firstLineLen = c - paraText;
secondLineStart = *c ==
'\0' ? paraText : c +
1;
firstLineIndent = findLeftMargin(paraText, firstLineLen, tabDist);
leftMargin = findLeftMargin(secondLineStart, paraEnd - paraStart -
(secondLineStart - paraText), tabDist);
filledText = fillParagraph(paraText, leftMargin, firstLineIndent,
rightMargin, tabDist, useTabs, nullSubsChar, &len);
NEditFree(paraText);
BufReplace(buf, paraStart, fillEnd, filledText);
NEditFree(filledText);
paraStart += len;
}
filledText = BufGetAll(buf);
*filledLen = buf->length;
BufFree(buf);
return filledText;
}
static char *fillParagraph(
char *text,
int leftMargin,
int firstLineIndent,
int rightMargin,
int tabDist,
int allowTabs,
char nullSubsChar,
int *filledLen)
{
char *cleanedText, *outText, *indentString, *leadIndentStr, *outPtr, *c, *b;
int col, cleanedLen, indentLen, leadIndentLen, nLines =
1;
int inWhitespace, inMargin;
cleanedText = (
char*)NEditMalloc(strlen(text)+
1);
outPtr = cleanedText;
inMargin = True;
for (c=text; *c!=
'\0'; c++) {
if (*c ==
'\t' || *c ==
' ') {
if (!inMargin)
*outPtr++ = *c;
}
else if (*c ==
'\n') {
if (inMargin) {
if (outPtr > cleanedText && *(outPtr-
1) ==
' ')
*(outPtr-
1) =
'\n';
*outPtr++ =
'\n';
nLines +=
2;
}
else
*outPtr++ =
' ';
inMargin = True;
}
else {
*outPtr++ = *c;
inMargin = False;
}
}
cleanedLen = outPtr - cleanedText;
*outPtr =
'\0';
col = firstLineIndent;
int inc =
1;
for (c=cleanedText; *c!=
'\0'; c+=inc) {
if (*c ==
'\n') {
col = leftMargin;
inc =
1;
}
else {
col += BufCharWidth(*c, col, tabDist, nullSubsChar);
inc = Utf8CharLen((
unsigned char*)c);
}
if (col-
1 > rightMargin) {
inWhitespace = True;
for (b=c; b>=cleanedText && *b!=
'\n'; b--) {
if (*b ==
'\t' || *b ==
' ') {
if (!inWhitespace) {
*b =
'\n';
c = b;
col = leftMargin;
nLines++;
break;
}
}
else
inWhitespace = False;
}
}
}
nLines++;
leadIndentStr = makeIndentString(firstLineIndent, tabDist,
allowTabs, &leadIndentLen);
indentString = makeIndentString(leftMargin, tabDist, allowTabs, &indentLen);
outText = (
char*)NEditMalloc(
sizeof(
char) * (cleanedLen + leadIndentLen +
indentLen * (nLines-
1) +
1));
outPtr = outText;
strncpy(outPtr, leadIndentStr, leadIndentLen);
outPtr += leadIndentLen;
for (c=cleanedText; *c!=
'\0'; c++) {
*outPtr++ = *c;
if (*c ==
'\n') {
strncpy(outPtr, indentString, indentLen);
outPtr += indentLen;
}
}
if (*(outPtr-
1) ==
' ')
*(outPtr-
1) =
'\n';
*outPtr =
'\0';
NEditFree(cleanedText);
NEditFree(leadIndentStr);
NEditFree(indentString);
*filledLen = outPtr - outText;
return outText;
}
static char *makeIndentString(
int indent,
int tabDist,
int allowTabs,
int *nChars)
{
char *indentString, *outPtr;
int i;
outPtr = indentString = (
char*)NEditMalloc(
sizeof(
char) * indent +
1);
if (allowTabs) {
for (i=
0; i<indent/tabDist; i++)
*outPtr++ =
'\t';
for (i=
0; i<indent%tabDist; i++)
*outPtr++ =
' ';
}
else {
for (i=
0; i<indent; i++)
*outPtr++ =
' ';
}
*outPtr =
'\0';
*nChars = outPtr - indentString;
return indentString;
}
static int findParagraphEnd(textBuffer *buf,
int startPos)
{
char c;
int pos;
static char whiteChars[] =
" \t";
pos = BufEndOfLine(buf, startPos)+
1;
while (pos < buf->length) {
c = BufGetCharacter(buf, pos);
if (c ==
'\n')
break;
if (strchr(whiteChars, c) !=
NULL)
pos++;
else
pos = BufEndOfLine(buf, pos)+
1;
}
return pos < buf->length ? pos : buf->length;
}
static int findParagraphStart(textBuffer *buf,
int startPos)
{
char c;
int pos, parStart;
static char whiteChars[] =
" \t";
if (startPos ==
0)
return 0;
parStart = BufStartOfLine(buf, startPos);
pos = parStart -
2;
while (pos >
0) {
c = BufGetCharacter(buf, pos);
if (c ==
'\n')
break;
if (strchr(whiteChars, c) !=
NULL)
pos--;
else {
parStart = BufStartOfLine(buf, pos);
pos = parStart -
2;
}
}
return parStart >
0 ? parStart :
0;
}