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