📄 shift.c
字号:
static const char CVSID[] = "$Id: shift.c,v 1.14 2003/04/07 22:51:41 yooden Exp $";/******************************************************************************** ** shift.c -- Nirvana Editor built-in filter commands ** ** Copyright (C) 1999 Mark Edel ** ** This is free software; you can redistribute it and/or modify it under the ** terms of the GNU General Public License as published by the Free Software ** Foundation; either version 2 of the License, or (at your option) any later ** version. ** ** This software is distributed in the hope that it will be useful, but WITHOUT ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ** for more details. ** ** You should have received a copy of the GNU General Public License along with ** software; if not, write to the Free Software Foundation, Inc., 59 Temple ** Place, Suite 330, Boston, MA 02111-1307 USA ** ** Nirvana Text Editor ** June 18, 1991 ** ** Written by Mark Edel ** ********************************************************************************/#ifdef HAVE_CONFIG_H#include "../config.h"#endif#include "shift.h"#include "textBuf.h"#include "text.h"#include "nedit.h"#include "window.h"#include <string.h>#include <limits.h>#include <ctype.h>#ifdef VMS#include "../util/VMSparam.h"#else#ifndef __MVS__#include <sys/param.h>#endif#endif /*VMS*/#include <Xm/Xm.h>#ifdef HAVE_DEBUG_H#include "../debug.h"#endifstatic 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);/*** Shift the selection left or right by a single character, or by one tab stop** if "byTab" is true. (The length of a tab stop is the size of an emulated** tab if emulated tabs are turned on, or a hardware tab if not).*/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; /* get selection, if no text selected, use current insert position */ 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); } /* shift the text by the appropriate distance */ 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); XtFree(text); BufReplaceSelected(buf, shiftedText); XtFree(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; /* Make sure selStart and SelEnd refer to whole lines */ selStart = BufStartOfLine(buf, selStart); selEnd = BufEndOfLine(buf, selEnd); /* Calculate the the left/right offset for the new rectangle */ 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; /* Create a temporary buffer for the lines containing the selection, to hide the intermediate steps from the display update routines */ tempBuf = BufCreate(); tempBuf->tabDist = buf->tabDist; tempBuf->useTabs = buf->useTabs; text = BufGetRange(buf, selStart, selEnd); BufSetAll(tempBuf, text); XtFree(text); /* Do the shift in the temporary buffer */ text = BufGetTextInRect(buf, selStart, selEnd, rectStart, rectEnd); BufRemoveRect(tempBuf, 0, selEnd-selStart, rectStart, rectEnd); BufInsertCol(tempBuf, rectStart+offset, 0, text, NULL, NULL); XtFree(text); /* Make the change in the real buffer */ text = BufGetAll(tempBuf); BufReplace(buf, selStart, selEnd, text); XtFree(text); 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);}/*** Capitalize or lowercase the contents of the selection (or of the character** before the cursor if there is no selection). If "makeUpper" is true,** change to upper case, otherwise, change to lower case.*/static void changeCase(WindowInfo *window, int makeUpper){ textBuffer *buf = window->buffer; char *text, *c; int cursorPos, start, end, isRect, rectStart, rectEnd; /* Get the selection. Use character before cursor if no selection */ if (!BufGetSelectionPos(buf, &start, &end, &isRect, &rectStart, &rectEnd)) { char bufChar[2] = " "; cursorPos = TextGetCursorPos(window->lastFocus); if (cursorPos == 0) { XBell(TheDisplay, 0); return; } *bufChar = BufGetCharacter(buf, cursorPos-1); *bufChar = makeUpper ? toupper((unsigned char)*bufChar) : tolower((unsigned char)*bufChar); BufReplace(buf, cursorPos-1, cursorPos, bufChar); } else { text = BufGetSelectionText(buf); for (c=text; *c!='\0'; c++) *c = makeUpper ? toupper((unsigned char)*c) : tolower((unsigned char)*c); BufReplaceSelected(buf, text); XtFree(text); if (isRect) BufRectSelect(buf, start, end, rectStart, rectEnd); else BufSelect(buf, start, end); }}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; /* Find the range of characters and get the text to fill. If there is a selection, use it but extend non-rectangular selections to encompass whole lines. If there is no selection, find the paragraph containing the insertion cursor */ 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); } /* Find right margin either as specified in the rectangular selection, or by measuring the text and querying the window's wrap margin (or width) */ if (hasSelection && isRect) { rightMargin = rectEnd - rectStart; } else { XtVaGetValues(window->textArea, textNcolumns, &nCols, textNwrapMargin, &wrapMargin, NULL); rightMargin = (wrapMargin == 0 ? nCols : wrapMargin) - 1; } /* Fill the text */ filledText = fillParagraphs(text, rightMargin, buf->tabDist, buf->useTabs, buf->nullSubsChar, &len, False); XtFree(text); /* Replace the text in the window */ 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); } XtFree(filledText); /* Find a reasonable cursor position. Usually insertPos is best, but if the text was indented, positions can shift */ if (hasSelection && isRect) TextSetCursorPos(window->lastFocus, buf->cursorPosHint); else TextSetCursorPos(window->lastFocus, insertPos < left ? left : (insertPos > left + len ? left + len : insertPos));}/*** shift lines left and right in a multi-line text string. Returns the** shifted text in memory that must be freed by the caller with XtFree.*/char *ShiftText(char *text, int direction, int tabsAllowed, int tabDist, int nChars, int *newLen){ char *shiftedText, *shiftedLine; char *textPtr, *lineStartPtr, *shiftedPtr; int bufLen; /* ** Allocate memory for shifted string. Shift left adds a maximum of ** tabDist-2 characters per line (remove one tab, add tabDist-1 spaces). ** Shift right adds a maximum of nChars character per line. */ if (direction == SHIFT_RIGHT) bufLen = strlen(text) + countLines(text) * nChars; else bufLen = strlen(text) + countLines(text) * tabDist; shiftedText = (char *)XtMalloc(bufLen + 1); /* ** break into lines and call shiftLine(Left/Right) on each */ 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); XtFree(shiftedLine); if (*textPtr == '\0') { /* terminate string & exit loop at end of text */ *shiftedPtr = '\0'; break; } else { /* move the newline from text to shifted text */ *shiftedPtr++ = *textPtr++; } /* start line over */ 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 = XtMalloc(lineLen + nChars + 1); lineOutPtr = lineOut; whiteWidth = 0; while (TRUE) { if (*lineInPtr == '\0' || (lineInPtr - line) >= lineLen) { /* nothing on line, wipe it out */ *lineOut = '\0'; return lineOut; } else if (*lineInPtr == ' ') { /* white space continues with tab, advance to next tab stop */ whiteWidth++; *lineOutPtr++ = *lineInPtr++; } else if (*lineInPtr == '\t') { /* white space continues with tab, advance to next tab stop */ whiteWidth = nextTab(whiteWidth, tabDist); *lineOutPtr++ = *lineInPtr++; } else { /* end of white space, add nChars of space */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -