New action 'cut_lines_with_string'.
[mp-5.x.git] / mp_clipboard.mpsl
blobe2340d857a3a71fd279fd70e38223c4bcc823f63
1 /*
3     Minimum Profit 5.x
4     A Programmer's Text Editor
6     Clipboard routines.
8     Copyright (C) 1991-2011 Angel Ortega <angel@triptico.com>
10     This program is free software; you can redistribute it and/or
11     modify it under the terms of the GNU General Public License
12     as published by the Free Software Foundation; either version 2
13     of the License, or (at your option) any later version.
15     This program is distributed in the hope that it will be useful,
16     but WITHOUT ANY WARRANTY; without even the implied warranty of
17     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18     GNU General Public License for more details.
20     You should have received a copy of the GNU General Public License
21     along with this program; if not, write to the Free Software
22     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24     http://www.triptico.com
28 /** editor actions **/
30 mp.actions['unmark']        = sub (d) { mp.unmark(d); };
31 mp.actions['mark']          = sub (d) { mp.mark(d); };
32 mp.actions['mark_vertical'] = sub (d) { mp.mark_vertical(d); };
34 mp.actions['copy_mark'] = sub (d) {
35                                         mp.busy(1);
36                                         mp.copy(d);
37                                         mp.unmark(d);
38                                         mp.busy(0);
39                                 };
41 mp.actions['paste_mark'] = sub (d) {
42                                         mp.busy(1);
43                                         mp.store_undo(d);
44                                         mp.paste(d);
45                                         mp.busy(0);
46                                 };
48 mp.actions['cut_mark']  = sub (d) {
49                                         mp.busy(1);
50                                         mp.store_undo(d);
51                                         mp.cut(d);
52                                         mp.busy(0);
53                                 };
55 mp.actions['delete_mark'] = sub (d) {
56                                         mp.busy(1);
57                                         mp.store_undo(d);
58                                         mp.delete_mark(d);
59                                         mp.busy(0);
60                                 };
62 mp.actions['mouse_drag_mark'] = sub (d) {
64         /* no selection yet? move to initial click and mark */
65         if (d.txt.mark == NULL)
66                 mp.mark(d);
68         /* move to drag position */
69         mp.move_to_coords_xy(d, mp.mouse_to_x, mp.mouse_to_y);
71         /* and mark */
72         mp.mark(d);
75 mp.actions['mark_all'] = sub (d) {
76     mp.move_bof(d);
77     mp.move_bol(d);
78     mp.mark(d);
80     mp.move_eof(d);
81     mp.move_eol(d);
82     mp.mark(d);
85 mp.actions['cut_lines_with_string'] = sub (d) {
86     local r = mp.form([
87         { 'label'   => L("Cut lines containing:"),
88           'type'    => 'text',
89           'history' => 'cut_lines_with_string' }
90         ]);
92     if (r != NULL) {
93         mp.busy(1);
94         mp.store_undo(d);
95         mp.cut_lines_with_string(d, r[0]);
96         mp.busy(0);
97     }
101 /** default key bindings **/
103 mp.keycodes['f8']           = "unmark";
104 mp.keycodes['f9']           = "mark";
105 mp.keycodes['ctrl-b']       = "mark_vertical";
106 mp.keycodes['ctrl-c']       = "copy_mark";
107 mp.keycodes['ctrl-v']       = "paste_mark";
108 mp.keycodes['ctrl-x']       = "cut_mark";
109 mp.keycodes['mouse-drag']   = "mouse_drag_mark";
111 /** action descriptions **/
113 mp.actdesc['unmark']                = LL("Unmark block");
114 mp.actdesc['mark']                  = LL("Mark beginning/end of block");
115 mp.actdesc['mark_vertical']         = LL("Mark vertical block");
116 mp.actdesc['copy_mark']             = LL("Copy block");
117 mp.actdesc['paste_mark']            = LL("Paste block");
118 mp.actdesc['cut_mark']              = LL("Cut block");
119 mp.actdesc['delete_mark']           = LL("Delete block");
120 mp.actdesc['mouse_drag_mark']       = LL("Mark using mouse dragging");
121 mp.actdesc['mark_all']              = LL("Mark all");
122 mp.actdesc['cut_lines_with_string'] = LL("Cut lines containing a string...");
125 /** code **/
127 sub mp.unmark(doc)
128 /* unmarks the block */
130         /* just destroy the mark */
131         doc.txt.mark = NULL;
133         return doc;
137 sub mp.mark(doc)
138 /* marks the start or end of the block */
140         local txt = doc.txt;
142         if (txt.mark == NULL) {
143                 /* no mark; create one */
144                 txt.mark = {};
145                 txt.mark.ax = txt.mark.bx = txt.mark.ex = txt.x;
146                 txt.mark.ay = txt.mark.by = txt.mark.ey = txt.y;
147                 txt.mark.vertical = 0;
148                 txt.mark.incomplete = 1;
149         }
150         else {
151                 /* mark exists; extend current one */
152                 if (txt.mark.vertical == 0) {
153                         /* normal selection */
154                         if (txt.y < txt.mark.ay ||
155                                 (txt.y == txt.mark.ay && txt.x < txt.mark.ax)) {
156                                 /* move the beginning of the block */
157                                 txt.mark.bx = txt.x;
158                                 txt.mark.by = txt.y;
159                                 txt.mark.ex = txt.mark.ax;
160                                 txt.mark.ey = txt.mark.ay;
161                         }
162                         else {
163                                 /* move the end of the block */
164                                 txt.mark.ex = txt.x;
165                                 txt.mark.ey = txt.y;
166                                 txt.mark.bx = txt.mark.ax;
167                                 txt.mark.by = txt.mark.ay;
168                         }
169                 }
170                 else {
171                         /* vertical selection */
172                         txt.mark.by = txt.mark.ay;
173                         txt.mark.ey = txt.y;
174                         if (txt.y < txt.mark.ay) {
175                                 txt.mark.by = txt.y;
176                                 txt.mark.ey = txt.mark.ay;
177                         }
179                         txt.mark.bx = txt.mark.ax;
180                         txt.mark.ex = txt.x;
181                         if (txt.x < txt.mark.ax) {
182                                 txt.mark.bx = txt.x;
183                                 txt.mark.ex = txt.mark.ax;
184                         }
185                 }
187                 txt.mark.incomplete = 0;
188         }
190         return doc;
194 sub mp.mark_vertical(doc)
195 /* start vertical block selection */
197         mp.mark(doc);
198         doc.txt.mark.vertical = 1;
200         return doc;
204 sub mp.get_active_area(doc)
205 /* returns the active area: the selection or the full document */
207         local m;
209         if ((m = doc.txt.mark) == NULL)
210                 return doc.txt.lines;
211         else
212                 return mp.get_range(doc, m.bx, m.by, m.ex, m.ey, m.vertical);
217  * mp.copy - Copies the selected block or a string to the clipboard
218  * @doc: the source of the copy
220  * If @doc is a document, it copies to the clipboard the content of the
221  * selected block, if one exists. If @doc is an array or scalar, it copies
222  * that data directly into it.
223  */
224 sub mp.copy(doc)
226         if (is_hash(doc)) {
227                 if (doc.txt.mark) {
228                         mp.clipboard = mp.get_active_area(doc);
229                         mp.clipboard_vertical = doc.txt.mark.vertical;
231                         mp.drv.clip_to_sys();
232                 }
233         }
234         else {
235                 if (!is_array(doc))
236                         doc = split("\n", doc);
238                 mp.clipboard = doc;
239                 mp.clipboard_vertical = 0;
240                 mp.drv.clip_to_sys();
241         }
243         return doc;
247 sub mp.delete_mark(doc)
248 /* deletes current selection */
250         local txt = doc.txt;
252         /* no mark? done */
253         if (txt.mark != NULL) {
254                 /* deletes the range */
255         if (txt.mark.bx != txt.mark.ex || txt.mark.by != txt.mark.ey)
256                 mp.delete_range(doc, txt.mark.bx, txt.mark.by,
257                                         txt.mark.ex, txt.mark.ey, txt.mark.vertical);
259                 mp.unmark(doc);
260         }
262         return doc;
266 sub mp.cut(doc)
267 /* cut (copy + delete) selected mark */
269         mp.copy(doc);
270         mp.delete_mark(doc);
271         mp.drv.clip_to_sys();
273         return doc;
278  * mp.paste - Pastes from the clipboard into a text or as a value
279  * @doc: the destination of the copy
281  * If @doc is NULL, returns the content of the clipboard as a
282  * scalar string; if it's not, is assumed to be a document and
283  * pastes the content of the clipboard into the cursor position.
284  */
285 sub mp.paste(doc)
287         mp.drv.sys_to_clip();
289         if (doc == NULL)
290                 return join("\n", mp.clipboard);
292         if (size(mp.clipboard) == 0)
293                 return doc;
295         local t = mp.config.auto_indent;
296         mp.config.auto_indent = 0;
298         /* is there a block? replace it */
299         if (doc.txt.mark != NULL) {
300                 /* move there */
301                 doc.txt.x = doc.txt.mark.bx;
302                 doc.txt.y = doc.txt.mark.by;
304                 /* and delete the block */
305                 mp.delete_mark(doc);
306         }
308         if (mp.clipboard_vertical == 0) {
309                 /* normal selection in clipboard */
310                 mp.insert(doc, mp.clipboard);
311         }
312         else {
313                 /* vertical selection in clipboard */
314                 local txt = doc.txt;
315                 local s = size(mp.clipboard);
316                 local i = 0;
317                 local w;
318                 local e;
319                 while (i < s) {
320                         /* pad out to current x position */
321                         e = txt.x - size(txt.lines[txt.y]);
323                         while(e-- > 0)
324                                 txt.lines[txt.y] = txt.lines[txt.y] ~ " ";
325                         
326                         /* insert this line of the clipboard */
327                         w = splice(txt.lines[txt.y], mp.clipboard[i++], txt.x, 0);
328                         txt.lines[txt.y++] = w[0];
329                 }
330                 txt.y--;
331                 txt.mod++;
332         }
334         mp.config.auto_indent = t;
336         return doc;
341  * mp.cut_lines_with_string - Cuts all lines matching a string
342  * @doc: the document
343  * @str: the string to be matched
345  * Cuts all lines from the document that matches @str, that is
346  * a regular expression. The deleted lines are left in the clipboard.
347  * If a block is selected, only lines inside it are cut.
348  */
349 sub mp.cut_lines_with_string(doc, str)
351     local r = [];
352     local p;
354     if (str == NULL || str eq '')
355         str = '^$';
357     str = '/' ~ str ~ '/';
359     if (doc.txt.mark) {
360         mp.cut(doc);
362         /* create a temporary work document */
363         p = mp.create('<wrk>', mp.clipboard);
364     }
365     else
366         p = doc;
368     mp.move_bof(p);
369     mp.move_bol(p);
371     while (p.txt.y < size(p.txt.lines) - 1) {
372         local l = p.txt.lines[p.txt.y];
374         if (regex(str, l) != NULL) {
375             push(r, l);
376             mp.delete_line(p);
377         }
378         else
379             mp.move_down(p);
380     }
382     /* if p is the working document, move content back to doc */
383     if (p.name eq '<wrk>')
384         mp.insert(doc, p.txt.lines);
386     mp.clipboard = r;
387     mp.drv.clip_to_sys();