1 /**@file texteditor/select.c  Text editor selection, copy, and paste.
2  * $Id: select.c,v 1.3 2005/05/28 03:17:46 bitman Exp $
3  * @author Ryan Phillips
4  *
5  * Copyright (C) 2003 Ryan Phillips <bitman@users.sf.net>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  */
21 
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 
26 #include "select.h"
27 #include "register.h"
28 
29 #include <stdlib.h>
30 #include <string.h>
31 
32 selectionBounds texteditGetSelectionBounds(texteditor * editor);
33 
34 /**
35  * @relates texteditor
36  * @brief Determine the bounds of the currently selected area.
37  *
38  * @returns boundaries of the editor's selection.
39  **/
texteditGetSelectionBounds(texteditor * editor)40 selectionBounds texteditGetSelectionBounds(texteditor * editor)
41 {
42 	selectionBounds bounds;
43 	int i;
44 
45 	/* Start from the current line and move outward. */
46 	bounds.startLine = bounds.endLine = editor->curline;
47 	/* Use cursor pos by default for both bounds. */
48 	bounds.startPos  = bounds.endPos  = editor->pos;
49 
50 	/* Nothing is selected if selectflag is false. */
51 	if (!editor->selectflag)
52 		return bounds;
53 
54 	if (editor->selectlineoffset > 0) {
55 		/* The end of selection is below current line. */
56 		bounds.endPos = editor->selectpos;
57 
58 		/* Advance endLine by the selectlineoffset. */
59 		for (i = 0; i < editor->selectlineoffset; i++)
60 			if (bounds.endLine->next != NULL)
61 				bounds.endLine = bounds.endLine->next;
62 
63 	} else if (editor->selectlineoffset < 0) {
64 		/* The start of selection is above current line. */
65 		bounds.startPos = editor->selectpos;
66 
67 		/* Retreat startLine by the negative selectline offset. */
68 		for (i = 0; i > editor->selectlineoffset; i--)
69 			if (bounds.startLine->prev != NULL)
70 				bounds.startLine = bounds.startLine->prev;
71 
72 	} else {
73 		/* Selection is only on current line. */
74 		if (editor->selectpos > editor->pos) {
75 			/* selectpos comes before pos. */
76 			bounds.endPos   = editor->selectpos;
77 
78 		} else {
79 			/* pos comes before selectpos. */
80 			bounds.startPos = editor->selectpos;
81 		}
82 	}
83 
84 	return bounds;
85 }
86 
87 /**
88  * @relates texteditor
89  * @brief Handle copying of text.
90  **/
texteditHandleCopy(texteditor * editor)91 void texteditHandleCopy(texteditor * editor)
92 {
93 	selectionBounds bounds;
94 
95 	if (editor->key != DKEY_CTRL_C && editor->key != DKEY_CTRL_X)
96 		return;
97 
98 	/* Can't copy if nothing is selected */
99 	/* CONSIDER: copy the current line, maybe? */
100 	if (!editor->selectflag)
101 		return;
102 
103 	bounds = texteditGetSelectionBounds(editor);
104 
105 	regyank('\"', bounds);
106 }
107 
108 
109 /**
110  * @relates texteditor
111  * @brief Handle text selection.
112  *
113  * Start or stop selecting text depending on the shift state.
114  *
115  * Always call this function before any functions that move the cursor when
116  * selection begins, such as texteditHandleScrolling() and
117  * texteditHandleEditMovement(). Always call after any function that uses the
118  * selection and expects the selection to be cleared, such as
119  * texteditHandleCopy() and texteditHandleEditKey().
120  **/
texteditHandleSelection(texteditor * editor)121 void texteditHandleSelection(texteditor * editor)
122 {
123 	/* Start selecting text if SHIFT key is held down just now. */
124 	int oldSelectFlag = editor->selectflag;
125 
126 	/* Selection is based on whether the shift key is down */
127 	editor->selectflag = editor->d->shift();
128 
129 	/* Except that shift + ASCII key does not start selection mode */
130 	if (editor->key < 0x7F)
131 		editor->selectflag = 0;
132 
133 	/* If we just started selecting, remember current position */
134 	if (editor->selectflag && !oldSelectFlag) {
135 		editor->selectpos = editor->pos;
136 		editor->selectlineoffset = 0;
137 	}
138 
139 	/* If we just stopped selecting, the edit area needs updated */
140 	if (oldSelectFlag && !editor->selectflag) {
141 		editor->updateflags |= TUD_EDITAREA;
142 	}
143 }
144 
145 
146 /**
147  * @relates texteditor
148  * @brief Clear the selected text.
149  **/
texteditClearSelectedText(texteditor * editor)150 void texteditClearSelectedText(texteditor * editor)
151 {
152 	selectionBounds bounds;
153 
154 	bounds = texteditGetSelectionBounds(editor);
155 
156 	if (bounds.startLine == bounds.endLine) {
157 		/* Selection starts and ends on the same line. */
158 		int selectionLen = bounds.endPos - bounds.startPos;
159 		char * line = bounds.startLine->s;
160 		int i;
161 
162 		/* Check for badly formed selection: endPos is before startPos. */
163 		if (selectionLen < 0) selectionLen = -selectionLen;
164 
165 		/* Copy the end of the line onto the selected text. */
166 		for (i = bounds.endPos; i < strlen(line); i++) {
167 			line[i - selectionLen] = line[i];
168 		}
169 		line[i - selectionLen] = '\0';
170 
171 		/* Move the cursor to the starting position of the cut. */
172 		editor->pos = bounds.startPos;
173 
174 	} else {
175 		/* Multiple lines are selected. */
176 		char * endString;
177 		char * unselectedEndString;
178 
179 		/* Remove all lines between the start and end lines. */
180 		editor->text->cur = bounds.startLine->next;
181 		while (editor->text->cur != bounds.endLine)
182 			deletestring(editor->text);
183 
184 		/* Remove the unselected portion of the end line. */
185 		editor->text->cur = bounds.endLine;
186 		endString = removestring(editor->text);
187 		unselectedEndString = endString + bounds.endPos;
188 
189 		/* Truncate the start line. */
190 		bounds.startLine->s[bounds.startPos] = '\0';
191 
192 		/* Wordwrap the unselectedEndString onto the truncated startLine. */
193 		editor->text->cur = bounds.startLine;
194 		/* The -1 tells wordwrap to track the cursor position at
195 		 * the beginning of cutend. Negative tracking values should
196 		 * be used only by wordwrap for internal purposes, but
197 		 * necessity warrents in this case.     vv    */
198 		editor->pos = wordwrap(editor->text, unselectedEndString, bounds.startPos, -1, editor->wrapwidth, editor->linewidth);
199 		editor->curline = editor->text->cur;
200 
201 		free(endString);
202 	}
203 
204 	editor->updateflags |= TUD_EDITAREA;
205 }
206 
207 
208 /**
209  * @relates texteditor
210  * @brief Paste copied text into the editor.
211  */
texteditPaste(texteditor * editor)212 void texteditPaste(texteditor * editor)
213 {
214 	editor->text->cur = editor->curline;
215 
216 	editor->pos = regput('\"', editor->text, editor->pos, editor->wrapwidth, editor->linewidth);
217 
218 	editor->curline = editor->text->cur;
219 	editor->updateflags |= TUD_EDITAREA;
220 }
221 
222 
223