Fix crash in new on_close handler.
[mp-5.x.git] / mp_clipboard.mpsl
blobe569d498dd4b341da4a1915898c95be1bfdc3d12
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);
40     return d;
43 mp.actions['paste_mark'] = sub (d) {
44     mp.busy(1);
45     mp.store_undo(d);
46     mp.paste(d);
47     mp.busy(0);
49     return d;
52 mp.actions['cut_mark']  = sub (d) {
53     mp.busy(1);
54     mp.store_undo(d);
55     mp.cut(d);
56     mp.busy(0);
58     return d;
61 mp.actions['delete_mark'] = sub (d) {
62     mp.busy(1);
63     mp.store_undo(d);
64     mp.delete_mark(d);
65     mp.busy(0);
67     return d;
70 mp.actions['mouse_drag_mark'] = sub (d) {
71     /* no selection yet? move to initial click and mark */
72     if (d.txt.mark == NULL)
73         mp.mark(d);
75     /* move to drag position */
76     mp.move_to_coords_xy(d, mp.mouse_to_x, mp.mouse_to_y);
78     /* and mark */
79     mp.mark(d);
81     return d;
84 mp.actions['mark_all'] = sub (d) {
85     mp.move_bof(d);
86     mp.move_bol(d);
87     mp.mark(d);
89     mp.move_eof(d);
90     mp.move_eol(d);
91     mp.mark(d);
93     return d;
96 mp.actions['cut_lines_with_string'] = sub (d, str) {
97     if (str == NULL) {
98         local r = mp.form(
99             [
100                 {
101                     'label'   => L("Cut lines containing:"),
102                     'type'    => 'text',
103                     'history' => 'cut_lines_with_string'
104                 }
105             ]
106         );
108         if (r != NULL)
109             str = r[0];
110     }
112     if (str != NULL) {
113         mp.busy(1);
114         mp.store_undo(d);
115         mp.cut_lines_with_string(d, str);
116         mp.busy(0);
117     }
119     return d;
123 /** default key bindings **/
125 mp.keycodes['f8']           = "unmark";
126 mp.keycodes['f9']           = "mark";
127 mp.keycodes['ctrl-b']       = "mark_vertical";
128 mp.keycodes['ctrl-c']       = "copy_mark";
129 mp.keycodes['ctrl-v']       = "paste_mark";
130 mp.keycodes['ctrl-x']       = "cut_mark";
131 mp.keycodes['mouse-drag']   = "mouse_drag_mark";
133 /** action descriptions **/
135 mp.actdesc['unmark']                = LL("Unmark block");
136 mp.actdesc['mark']                  = LL("Mark beginning/end of block");
137 mp.actdesc['mark_vertical']         = LL("Mark vertical block");
138 mp.actdesc['copy_mark']             = LL("Copy block");
139 mp.actdesc['paste_mark']            = LL("Paste block");
140 mp.actdesc['cut_mark']              = LL("Cut block");
141 mp.actdesc['delete_mark']           = LL("Delete block");
142 mp.actdesc['mouse_drag_mark']       = LL("Mark using mouse dragging");
143 mp.actdesc['mark_all']              = LL("Mark all");
144 mp.actdesc['cut_lines_with_string'] = LL("Cut lines containing a string...");
147 /** code **/
149 sub mp.unmark(doc)
150 /* unmarks the block */
152         /* just destroy the mark */
153         doc.txt.mark = NULL;
155         return doc;
159 sub mp.mark(doc)
160 /* marks the start or end of the block */
162         local txt = doc.txt;
164         if (txt.mark == NULL) {
165                 /* no mark; create one */
166                 txt.mark = {};
167                 txt.mark.ax = txt.mark.bx = txt.mark.ex = txt.x;
168                 txt.mark.ay = txt.mark.by = txt.mark.ey = txt.y;
169                 txt.mark.vertical = 0;
170                 txt.mark.incomplete = 1;
171         }
172         else {
173                 /* mark exists; extend current one */
174                 if (txt.mark.vertical == 0) {
175                         /* normal selection */
176                         if (txt.y < txt.mark.ay ||
177                                 (txt.y == txt.mark.ay && txt.x < txt.mark.ax)) {
178                                 /* move the beginning of the block */
179                                 txt.mark.bx = txt.x;
180                                 txt.mark.by = txt.y;
181                                 txt.mark.ex = txt.mark.ax;
182                                 txt.mark.ey = txt.mark.ay;
183                         }
184                         else {
185                                 /* move the end of the block */
186                                 txt.mark.ex = txt.x;
187                                 txt.mark.ey = txt.y;
188                                 txt.mark.bx = txt.mark.ax;
189                                 txt.mark.by = txt.mark.ay;
190                         }
191                 }
192                 else {
193                         /* vertical selection */
194                         txt.mark.by = txt.mark.ay;
195                         txt.mark.ey = txt.y;
196                         if (txt.y < txt.mark.ay) {
197                                 txt.mark.by = txt.y;
198                                 txt.mark.ey = txt.mark.ay;
199                         }
201                         txt.mark.bx = txt.mark.ax;
202                         txt.mark.ex = txt.x;
203                         if (txt.x < txt.mark.ax) {
204                                 txt.mark.bx = txt.x;
205                                 txt.mark.ex = txt.mark.ax;
206                         }
207                 }
209                 txt.mark.incomplete = 0;
210         }
212         return doc;
216 sub mp.mark_vertical(doc)
217 /* start vertical block selection */
219         mp.mark(doc);
220         doc.txt.mark.vertical = 1;
222         return doc;
226 sub mp.get_active_area(doc)
227 /* returns the active area: the selection or the full document */
229         local m;
231         if ((m = doc.txt.mark) == NULL)
232                 return doc.txt.lines;
233         else
234                 return mp.get_range(doc, m.bx, m.by, m.ex, m.ey, m.vertical);
239  * mp.copy - Copies the selected block or a string to the clipboard
240  * @doc: the source of the copy
242  * If @doc is a document, it copies to the clipboard the content of the
243  * selected block, if one exists. If @doc is an array or scalar, it copies
244  * that data directly into it.
245  */
246 sub mp.copy(doc)
248         if (is_hash(doc)) {
249                 if (doc.txt.mark) {
250                         mp.clipboard = mp.get_active_area(doc);
251                         mp.clipboard_vertical = doc.txt.mark.vertical;
253                         mp.drv.clip_to_sys();
254                 }
255         }
256         else {
257                 if (!is_array(doc))
258                         doc = split(doc, "\n");
260                 mp.clipboard = doc;
261                 mp.clipboard_vertical = 0;
262                 mp.drv.clip_to_sys();
263         }
265         return doc;
269 sub mp.delete_mark(doc)
270 /* deletes current selection */
272         local txt = doc.txt;
274         /* no mark? done */
275         if (txt.mark != NULL) {
276                 /* deletes the range */
277         if (txt.mark.bx != txt.mark.ex || txt.mark.by != txt.mark.ey)
278                 mp.delete_range(doc, txt.mark.bx, txt.mark.by,
279                                         txt.mark.ex, txt.mark.ey, txt.mark.vertical);
281                 mp.unmark(doc);
282         }
284         return doc;
288 sub mp.cut(doc)
289 /* cut (copy + delete) selected mark */
291         mp.copy(doc);
292         mp.delete_mark(doc);
293         mp.drv.clip_to_sys();
295         return doc;
300  * mp.paste - Pastes from the clipboard into a text or as a value
301  * @doc: the destination of the copy
303  * If @doc is NULL, returns the content of the clipboard as a
304  * scalar string; if it's not, is assumed to be a document and
305  * pastes the content of the clipboard into the cursor position.
306  */
307 sub mp.paste(doc)
309         mp.drv.sys_to_clip();
311         if (doc == NULL)
312                 return join(mp.clipboard, "\n");
314         if (size(mp.clipboard) == 0)
315                 return doc;
317         local t = mp.config.auto_indent;
318         mp.config.auto_indent = 0;
320         /* is there a block? replace it */
321         if (doc.txt.mark != NULL) {
322                 /* move there */
323                 doc.txt.x = doc.txt.mark.bx;
324                 doc.txt.y = doc.txt.mark.by;
326                 /* and delete the block */
327                 mp.delete_mark(doc);
328         }
330         if (mp.clipboard_vertical == 0) {
331                 /* normal selection in clipboard */
332                 mp.insert(doc, mp.clipboard);
333         }
334         else {
335                 /* vertical selection in clipboard */
336                 local txt = doc.txt;
337                 local s = size(mp.clipboard);
338                 local i = 0;
339                 local w;
340                 local e;
341                 while (i < s) {
342                         /* pad out to current x position */
343                         e = txt.x - size(txt.lines[txt.y]);
345                         while(e-- > 0)
346                                 txt.lines[txt.y] = txt.lines[txt.y] ~ " ";
347                         
348                         /* insert this line of the clipboard */
349                         w = splice(txt.lines[txt.y], mp.clipboard[i++], txt.x, 0);
350                         txt.lines[txt.y++] = w[0];
351                 }
352                 txt.y--;
353                 txt.mod++;
354         }
356         mp.config.auto_indent = t;
358         return doc;
363  * mp.cut_lines_with_string - Cuts all lines matching a string
364  * @doc: the document
365  * @str: the string to be matched
367  * Cuts all lines from the document that matches @str, that is
368  * a regular expression. The deleted lines are left in the clipboard.
369  * If a block is selected, only lines inside it are cut.
370  */
371 sub mp.cut_lines_with_string(doc, str)
373     local r = [];
374     local p;
376     if (str == NULL || str eq '')
377         str = '^$';
379     str = '/' ~ str ~ '/';
381     if (doc.txt.mark) {
382         mp.cut(doc);
384         /* create a temporary work document */
385         p = mp.create('<wrk>', mp.clipboard);
386     }
387     else
388         p = doc;
390     mp.move_bof(p);
391     mp.move_bol(p);
393     while (p.txt.y < size(p.txt.lines) - 1) {
394         local l = p.txt.lines[p.txt.y];
396         if (regex(l, str) != NULL) {
397             push(r, l);
398             mp.delete_line(p);
399         }
400         else
401             mp.move_down(p);
402     }
404     /* if p is the working document, move content back to doc */
405     if (p.name eq '<wrk>')
406         mp.insert(doc, p.txt.lines);
408     mp.clipboard = r;
409     mp.drv.clip_to_sys();