4 A Programmer's Text Editor
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 /* main TUI namespace */
35 text: [ 'white', 'blue' ],
41 sub mp.tui.prompt(prompt, y)
42 /* draw a prompt on screen */
44 /* no y? default to last line */
48 /* delete all possible newlines */
49 prompt = sregex(prompt, "/\n/g", ' ');
51 mp.tui.attr(mp.colors.menu.attr);
53 mp.tui.addstr(prompt);
54 mp.tui.attr(mp.colors.normal.attr);
59 sub mp.tui.readline(prompt, history, default, flags)
60 /* the readline function, with special functionality in 'flags' */
64 mp.tui.prompt(prompt ~ ' ', flags.y);
68 /* get the history stack */
69 h = mp.get_history(history);
74 /* store in c[2] the usable size */
75 push(c, mp.window.tx - c[0] - 1);
81 /* is the string bigger than the usable size? */
82 if (size(r) >= c[2]) {
83 /* if x is beyond the usable size,
88 s = splice(s, NULL, v, c[2]);
92 /* if it's a password, change everything to asterisks */
94 s = sregex(s, '/./g', flags.password);
96 /* draws the string */
97 mp.tui.move(c[0], c[1], 1);
99 mp.tui.move(c[0] + x - v, c[1]);
101 local k = mp.tui.getkey();
114 if (k eq 'backspace' && x > 0) {
116 r = splice(r, NULL, x, 1);
121 r = splice(r, NULL, x, 1);
131 r = splice(r, NULL, x, -1);
135 if (k eq 'cursor-up' && size(h)) {
141 if (k eq 'cursor-down' && size(h)) {
147 if (k eq 'cursor-left' && x > 0) {
151 if (k eq 'cursor-right' && x < size(r)) {
163 if (k eq 'tab' && flags.file) {
167 local l = glob(r ~ '*');
173 local p = mp.tui.list(prompt, l, 0);
182 /* if it's not a directory, retry */
183 if (regex(r, '@/$@') == NULL)
196 r = splice(r, k, x, 0);
202 /* if a string was accepted, store in the history */
203 if (h != NULL && size(r) && h[-1] ne r)
210 sub mp.tui.list(prompt, data, pos)
211 /* select from a list */
215 mp.tui.attr(mp.colors.menu.attr);
216 mp.tui.move(0, 0, 1);
217 mp.tui.addstr(prompt);
218 mp.tui.attr(mp.colors.normal.attr);
221 ty = mp.window.ty - 1;
224 r = '/^.{1,' ~ (mp.window.tx) ~ '}/';
235 if (pos >= size(data))
236 pos = size(data) - 1;
244 /* draw all the lines */
247 local l = data[n + vy];
253 mp.tui.move(0, n + 1, 1);
256 mp.tui.attr(mp.colors.cursor.attr);
258 mp.tui.attr(mp.colors.normal.attr);
260 mp.tui.addstr(regex(sprintf("%-" ~ mp.window.tx ~ "s", l), r));
265 /* clean the rest of lines */
266 mp.tui.attr(mp.colors.normal.attr);
268 mp.tui.move(0, n + 1, 1);
274 if (k eq 'cursor-up')
277 if (k eq 'cursor-down')
283 if (k eq 'page-down')
290 pos = size(data) - 1;
300 if ((ord(k) >= ord('a') && ord(k) <= ord('z')) ||
301 (ord(k) >= ord('A') && ord(k) <= ord('Z'))) {
302 /* search the first item >= k */
305 while (pos < size(data) - 1) {
306 local c = regex(data[pos], '/^./');
308 if (ord(c) >= ord(k))
320 sub mp.tui.confirm(msg, def, ypos)
325 /* get the initials for localized 'Yes' and 'No' */
326 y = regex(L("Yes"), '/^./');
327 n = regex(L("No"), '/^./');
330 msg = msg ~ ' (' ~ y ~ '/' ~ n ~ ')';
333 /* a default option? add to prompt */
334 msg = msg ~ ' [' ~ (def && y || n) ~ ']';
337 mp.tui.prompt(msg, ypos);
339 while (ret == NULL) {
340 local k = mp.tui.getkey();
342 if (regex(k, '/^' ~ y ~ '$/i'))
344 if (regex(k, '/^' ~ n ~ '$/i'))
349 ret = (def && 1 || 2);
358 /* draw the document part */
359 mp.tui.doc_draw(doc);
361 /* draw the status line */
362 mp.tui.attr(mp.colors.normal.attr);
363 mp.tui.move(0, mp.window.ty - 1, 1);
364 mp.tui.addstr(mp.build_status_line());
366 /* draw the 'menu' hint */
367 local t = "ctrl-a: " ~ L("Menu");
368 mp.tui.move(mp.window.tx - size(t), mp.window.ty - 1);
371 /* if a hardware cursor is desired, set it */
372 if (mp.config.hw_cursor) {
374 mp.x2vx(doc.txt.lines[doc.txt.y],
375 doc.txt.x - doc.txt.vx),
376 doc.txt.y - doc.txt.vy
386 sub mp.drv.alert(msg)
388 mp.tui.prompt(msg ~ L(" [ENTER]"));
390 while (mp.tui.getkey() ne 'enter');
394 sub mp.drv.openfile(prompt)
396 mp.tui.readline(prompt, 'openfile', NULL, { 'file' => 1 } );
400 sub mp.drv.savefile(prompt)
402 mp.tui.readline(prompt, 'savefile', NULL, { 'file' => 1 } );
406 sub mp.drv.confirm(msg, def)
408 mp.tui.confirm(msg, def);
412 sub mp.drv.form(widgets)
415 local pos = mp.window.ty - size(widgets);
418 /* print first all prompts */
419 foreach (w, widgets) {
420 if (w.type ne 'list')
421 mp.tui.prompt(w.label, y++);
426 /* now iterate widgets */
427 foreach (w, widgets) {
430 if (w.type eq 'text')
431 r1 = mp.tui.readline(w.label, w.history, w.value,
434 if (w.type eq 'password')
435 r1 = mp.tui.readline(w.label, NULL, NULL,
436 { 'password' => '*', 'y' => y });
438 if (w.type eq 'checkbox') {
439 /* return value conversion */
440 local c = [ NULL, 1, 0 ];
442 r1 = c[mp.tui.confirm(w.label, w.value, y)];
445 if (w.type eq 'list')
446 r1 = mp.tui.list(w.label, w.list, w.value);
469 while (action == NULL && key ne 'escape') {
470 local pos, mo, my, vy;
475 mx = size(mp.menu) - 1;
476 if (mx >= size(mp.menu))
479 /* draw the menu bar */
480 mp.tui.attr(mp.colors.menu.attr);
481 mp.tui.move(0, 0, 1);
483 while (n < size(mp.menu)) {
485 local l = L(mp.menu[n][0]);
487 /* strip (by now) the & */
488 l = sregex(l, '/&/g', NULL);
490 mp.tui.attr(mp.colors.menu.attr);
494 pos = mp.tui.getxy();
495 mp.tui.attr(mp.colors.cursor.attr);
503 /* get the menu options */
506 /* calculate panel optimal dimensions */
510 local l = mp.menu_label(i);
512 if (size(l) > pos[2])
516 /* if the panel will surpass the right margin,
518 if (pos[0] + pos[2] > mp.window.tx - 2)
519 pos[0] = mp.window.tx - pos[2] - 2;
521 local pty = size(mo);
523 if (pty > mp.window.ty - 3)
524 pty = mp.window.ty - 3;
527 mp.tui.attr(mp.colors.menu.attr);
528 mp.tui.openpanel(pos[0], 1, pos[2] + 2, pty + 2);
533 while (key ne 'escape') {
534 /* draw the options */
535 if (my < vy) vy = my;
536 if (my > vy + (pty - 1)) vy = my - (pty - 1);
541 local l = mp.menu_label(mo[n + vy]);
545 mp.tui.attr(mp.colors.cursor.attr);
547 mp.tui.attr(mp.colors.menu.attr);
549 mp.tui.move(1, 1 + n);
550 mp.tui.addstr(sprintf("%-" ~ pos[2] ~ "s", l || ''));
555 /* add visual cues that the menu is bigger */
556 if (size(mo) > pty) {
557 mp.tui.attr(mp.colors.menu.attr);
560 mp.tui.move(pos[2], 1);
564 if (pty + vy < size(mo)) {
565 mp.tui.move(pos[2], pty);
570 /* move the hw cursor to the selected option */
571 mp.tui.move(1, 1 + my - vy);
575 key = mp.tui.getkey();
577 if (key eq 'cursor-up') {
578 /* move up avoiding separators */
588 if (key eq 'cursor-down') {
589 /* move down avoiding separators */
591 if (++my >= size(mo))
599 if (key eq 'cursor-right') {
604 if (key eq 'cursor-left') {
609 if (key eq 'enter') {
618 mp.tui.attr(mp.color.normal.attr);
621 mp.process_action(action);
627 sub mp.drv.busy(onoff)
629 mp.tui.prompt(onoff && L("Please, wait...") || '');
633 sub mp.drv.main_loop()
635 while (!mp.exit_requested()) {
636 mp.tui.draw(mp.active());
638 mp.process_event(mp.tui.getkey());
643 /* returns the main namespace */