Fix crash in new on_close handler.
[mp-5.x.git] / mp_move.mpsl
blobdc49c5bf1768014abc3a5f5b4bfb4c31ddcf92f7
1 /*
3     Minimum Profit 5.x
4     A Programmer's Text Editor
6     Movement.
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['move_left']         = sub (d) { mp.move(d, mp.move_left); };
31 mp.actions['move_right']        = sub (d) { mp.move(d, mp.move_right); };
32 mp.actions['move_up']           = sub (d) { mp.move(d, mp.move_up); };
33 mp.actions['move_down']         = sub (d) { mp.move(d, mp.move_down); };
34 mp.actions['move_pgup']         = sub (d) { mp.move(d, mp.move_pgup); };
35 mp.actions['move_pgdn']         = sub (d) { mp.move(d, mp.move_pgdn); };
36 mp.actions['move_bol']          = sub (d) { 
37     if (mp.config.smart_bol)
38         mp.move(d, mp.move_bol_smart); 
39     else
40         mp.move(d, mp.move_bol); 
42 mp.actions['move_eol']          = sub (d) { mp.move(d, mp.move_eol); };
43 mp.actions['move_bof']          = sub (d) { mp.move(d, mp.move_bof); };
44 mp.actions['move_eof']          = sub (d) { mp.move(d, mp.move_eof); };
45 mp.actions['move_word_left']    = sub (d) { mp.move(d, mp.move_word_left); };
46 mp.actions['move_word_right']   = sub (d) { mp.move(d, mp.move_word_right); };
48 mp.actions['goto']              = sub (d, line) {
49     if (line == NULL) {
50         local t = mp.form(
51             [
52                 {
53                     'label'     => L("Line to go to:"),
54                     'type'      => 'text',
55                     'history'   => 'goto'
56                 }
57             ]
58         );
60         if (t != NULL && t[0] >= 1)
61             line = t[0];
62     }
64     if (line != NULL) {
65         d.txt.x = 0;
66         mp.set_y(d, line - 1);
67     }
69     return d;
72 mp.actions['move_to_mouse_position']    = sub (d) {
73     /* move the cursor there */
74     mp.move_to_coords_xy(d, mp.mouse_x, mp.mouse_y);
76     /* mouse click always unmarks */
77     mp.unmark(d);
79     return d;
82 mp.actions['move_mouse_wheel_up']       = sub (d) {
83     mp.move(d, sub (d) {
84         mp.move_up(d);
85         mp.move_up(d);
86         mp.move_up(d);
87         mp.move_up(d);
88     });
91 mp.actions['move_mouse_wheel_down']     = sub (d) {
92     mp.move(d, sub(d) {
93         mp.move_down(d);
94         mp.move_down(d);
95         mp.move_down(d);
96         mp.move_down(d);
97     });
100 mp.actions['document_list'] = sub (d) {
101     local r = mp.form(
102         [
103             {
104                 'label' => L("Document list"),
105                 'type'  => 'list',
106                 'list'  => mp.get_doc_names(60),
107                 'value' => mp.active_i
108             }
109         ]
110     );
112     if (r[0] != NULL) {
113         mp.active_i = r[0];
114         d = mp.active();
115     }
117     return d;
120 /** default key bindings **/
122 mp.keycodes['cursor-left']                      = "move_left";
123 mp.keycodes['cursor-right']                     = "move_right";
124 mp.keycodes['cursor-up']                        = "move_up";
125 mp.keycodes['cursor-down']                      = "move_down";
126 mp.keycodes['page-up']                          = "move_pgup";
127 mp.keycodes['page-down']                        = "move_pgdn";
128 mp.keycodes['home']                                     = "move_bol";
129 mp.keycodes['end']                                      = "move_eol";
130 mp.keycodes['ctrl-home']                        = "move_bof";
131 mp.keycodes['ctrl-end']                         = "move_eof";
132 mp.keycodes['ctrl-cursor-left']         = "move_word_left";
133 mp.keycodes['ctrl-cursor-right']        = "move_word_right";
134 mp.keycodes['alt-home']                         = "move_bof";
135 mp.keycodes['alt-end']                          = "move_eof";
136 mp.keycodes['ctrl-g']                           = "goto";
137 mp.keycodes['mouse-left-button']        = "move_to_mouse_position";
138 mp.keycodes['mouse-right-button']       = "move_to_mouse_position";
139 mp.keycodes['mouse-middle-button']      = "move_to_mouse_position";
140 mp.keycodes['mouse-wheel-up']           = "move_mouse_wheel_up";
141 mp.keycodes['mouse-wheel-down']         = "move_mouse_wheel_down";
143 /** action descriptions **/
145 mp.actdesc['move_left']                 = LL("Character left");
146 mp.actdesc['move_right']                = LL("Character right");
147 mp.actdesc['move_up']                   = LL("Line up");
148 mp.actdesc['move_down']                 = LL("Line down");
149 mp.actdesc['move_pgup']                 = LL("Page up");
150 mp.actdesc['move_pgdn']                 = LL("Page down");
151 mp.actdesc['move_bol']                  = LL("Beginning of line");
152 mp.actdesc['move_eol']                  = LL("End of line");
153 mp.actdesc['move_bof']                  = LL("Beginning of document");
154 mp.actdesc['move_eof']                  = LL("End of document");
155 mp.actdesc['move_word_left']            = LL("Word left");
156 mp.actdesc['move_word_right']           = LL("Word right");
157 mp.actdesc['goto']                      = LL("Go to line...");
158 mp.actdesc['move_to_mouse_position']    = LL("Move cursor to mouse click");
159 mp.actdesc['move_mouse_wheel_down']     = LL("Mouse wheel up");
160 mp.actdesc['move_mouse_wheel_up']       = LL("Mouse wheel down");
161 mp.actdesc['document_list']             = LL("Document list");
163 /** code **/
165 sub mp.move(doc, func)
166 /* wrapper for movement functions, with possible shift selection */
168         if (func != NULL) {
169                 if (mp.shift_pressed) {
170                         /* shift pressed? move selecting */
171                         if (doc.txt.mark == NULL)
172                                 mp.mark(doc);
174                         func(doc);
175                         mp.mark(doc);
176                 }
177                 else
178                         func(doc);
179         }
181         return doc;
185 sub mp.split_by_words(s, r)
186 /* splits a string by words */
188         /* if no special-purpose regex set, take global one */
189         if (r == NULL)
190                 r = mp.word_regex;
192         return regex(s, r ~ 'g');
196 sub mp.split_line_by_words(doc, r)
197 /* splits current line by words and returns a three element array containing
198    the list of words, the list of offsets and the current position */
200         local txt, l, w, c, ol, oc, p;
202         txt = doc.txt;
203         l = txt.lines[txt.y];
204         ol = [];
205         oc = [];
206         p = -1;
208         /* if no special-purpose regex set, take global one */
209         if (r == NULL)
210                 r = mp.word_regex;
212         while ((w = regex(l, r, c[0] + c[1])) != NULL) {
213                 /* store the word */
214                 push(ol, w);
216                 /* get coordinates */
217                 c = regex();
219                 /* push the starting column */
220                 push(oc, c[0]);
222                 /* if matching coords are between the cursor, store it */
223                 if (c[0] <= txt.x && c[0] + c[1] >= txt.x)
224                         p = size(ol) - 1;
225         }
227         /* it txt.x is still further than the last match, it means
228            that the 'current' position is beyond the last word */
229         if (txt.x > c[0] + c[1])
230                 p = size(ol);
232         /* return the list of words, the list of
233            coordinates and the current one */
234         return [ ol, oc, p ];
238 sub mp.get_word(doc, r)
239 /* returns the word under the cursor */
241         local l = mp.split_line_by_words(doc, r);
243         /* no word over cursor? */
244         if (l[2] == -1)
245                 return NULL;
247         return l[0][l[2]];
251 sub mp.get_range(doc, bx, by, ex, ey, v)
252 /* gets a range or characters from a document */
254         local txt = doc.txt;
256         local r = [];
258         if (by == ey) {
259                 local w;
261                 /* block is just one line; take the inside
262                    part and push it onto the clipboard */
264                 w = splice(txt.lines[by], NULL, bx, ex - bx);
266                 push(r, w[1]);
267         }
268         else {
269                 local w, n;
271                 /* block has more than one line */
273                 n = by;
274                 if (v == 0) {
275                         /* use normal selection block */
277                         /* take from the beginning to the end of the first line */
278                         w = splice(txt.lines[n], NULL, bx, -1);
280                         push(r, w[1] || '');
281                         n++;
283                         /* take the central lines */
284                         while (n < ey)
285                                 push(r, txt.lines[n++]);
287                         /* take the last line */
288                         w = splice(txt.lines[n], NULL, 0, ex);
289                         push(r, w[1] || '');
290                 }
291                 else {
292                         /* use vertical selection block */
293                         while (n <= ey) {
294                                 w = splice(txt.lines[n++], NULL, bx, ex - bx + 1);
295                                 local l = w[1];
296                                 local p = ex - bx - size(l);
298                                 /* pad out to end of block line */
299                                 while (p-- >= 0)
300                                         l = l ~ " ";
302                                 push(r, l);
303                         }
304                 }
305         }
307         return r;
311 sub mp.set_x(doc, x)
312 /* sets the x position */
314         local txt = doc.txt;
316         if (x < 0) {
317                 /* cursor moved left of the bol; effective cursor up + eol */
318                 if (txt.y > 0) {
319                         /* one line up */
320                         txt.y--;
322                         /* set x to the end of the line */
323                         txt.x = size(txt.lines[txt.y]);
324                 }
325         }
326         else {
327                 /* test if moved beyond end of line */
328                 if (x > size(txt.lines[txt.y])) {
329                         if (txt.y < size(txt.lines) - 1) {
330                                 /* cursor moved right of eol;
331                                    effective cursor down + bol */
332                                 txt.x = 0;
333                                 txt.y++;
334                         }
335                 }
336                 else
337                         txt.x = x;
338         }
340         return doc;
344 sub mp.set_y(doc, y)
345 /* sets the y position */
347         local txt = doc.txt;
348         local vx;
350         /* get current visual x position */
351         vx = mp.x2vx(txt.lines[txt.y], txt.x);
353         /* set boundaries */
354         if (y < 0)
355                 y = 0;
356         if (y >= size(txt.lines))
357                 y = size(txt.lines) - 1;
359         /* move there */
360         txt.y = y;
362         /* adjust new x to match previously one */
363         txt.x = mp.vx2x(txt.lines[txt.y], vx);
365         return doc;
369 sub mp.move_up(doc)
370 /* moves one line up */
372         mp.set_y(doc, doc.txt.y - 1);
376 sub mp.move_down(doc)
377 /* moves one line down */
379         mp.set_y(doc, doc.txt.y + 1);
383 sub mp.move_pgup(doc)
384 /* moves one page up */
386         mp.set_y(doc, doc.txt.y - mp.window.ty);
390 sub mp.move_pgdn(doc)
391 /* moves one page down */
393         mp.set_y(doc, doc.txt.y + mp.window.ty);
397 sub mp.move_left(doc)
398 /* moves one char left */
400         if (doc.txt.x + doc.txt.y)
401                 mp.set_x(doc, doc.txt.x - 1);
403         return doc;
407 sub mp.move_right(doc)
408 /* moves one char right */
410         mp.set_x(doc, doc.txt.x + 1);
414 sub mp.move_bol(doc)
415 /* moves to the beginning of the line */
417         doc.txt.x = 0;
418         return doc;
421 sub mp.move_bol_smart(doc)
422 /* moves to the first non-whitespace or if already there the beginning of the line */
424         local non_white = 0;
425         local l = split(doc.txt.lines[doc.txt.y]);
427         while (non_white < size(l) && 
428                         (
429                                 cmp(l[non_white], " ") == 0 || 
430                                 cmp(l[non_white], "\t") == 0)
431                         ) 
432         {
433                 non_white++;
434         }
435         
436         if (doc.txt.x == non_white) {
437                 doc.txt.x = 0;
438         } else {
439                 doc.txt.x = non_white;
440         }
443 sub mp.move_eol(doc)
444 /* moves to the end of the line */
446         doc.txt.x = size(doc.txt.lines[doc.txt.y]);
447         return doc;
451 sub mp.move_bof(doc)
452 /* moves to the beginning of the file */
454         doc.txt.x = 0;
455         doc.txt.y = 0;
456         return doc;
460 sub mp.move_eof(doc)
461 /* moves to the end of the file */
463         doc.txt.y = size(doc.txt.lines) - 1;
464         mp.move_eol(doc);
468 sub mp.move_word_left(doc)
469 /* moves a word to the left */
471         local txt = doc.txt;
473         while (1) {
474                 /* split by words */
475                 local l = mp.split_line_by_words(doc);
477                 /* get current word */
478                 local i = l[2];
480                 if (i >= 0) {
481                         /* if it's not at the beginning of a word,
482                            move there and go */
483                         if (i < size(l[1]) && txt.x != l[1][i]) {
484                                 txt.x = l[1][i];
485                                 break;
486                         }
488                         /* go to previous word */
489                         i = l[2] - 1;
491                         /* if that position exists, move there */
492                         if (i >= 0) {
493                                 txt.x = l[1][i];
494                                 break;
495                         }
496                 }
498                 /* no lines up? exit */
499                 if (txt.y == 0)
500                         break;
502                 txt.y--;
503                 txt.x = size(txt.lines[txt.y]);
504         }
506         return doc;
510 sub mp.move_word_right(doc)
511 /* moves a word to the right */
513         local txt = doc.txt;
515         while (txt.y < size(txt.lines) - 1) {
516                 /* split by words */
517                 local l = mp.split_line_by_words(doc);
519                 /* get next position */
520                 local i = l[2] + 1;
522                 /* if that position exists, move there */
523                 if (i < size(l[1])) {
524                         txt.x = l[1][i];
525                         break;
526                 }
528                 /* try next line */
529                 txt.y++;
530                 txt.x = 0;
531         }
533         return doc;
537 sub mp.move_to_coords_xy(doc, x, y)
538 /* move the cursor to the character on the visual coords x and y */
540         /* set y */
541         mp.set_y(doc, doc.txt.vy + y);
543         /* calculate the real position in that line
544            where the mouse click seem to be */
545         x = mp.vx2x(doc.txt.lines[doc.txt.y], doc.txt.vx + x);
547         /* move there */
548         mp.set_x(doc, x);