4 A Programmer's Text Editor
8 Copyright (C) 1991-2010 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 namspace */
34 mp.colors.menu = { 'text' => [ 'white', 'blue' ], 'flags' => [ 'bright' ] };
38 sub mp.tui.prompt(prompt, y)
39 /* draw a prompt on screen */
41 /* no y? default to last line */
45 /* delete all possible newlines */
46 prompt = sregex("/\n/g", prompt, ' ');
48 mp.tui.attr(mp.colors.menu.attr);
50 mp.tui.addstr(prompt);
51 mp.tui.attr(mp.colors.normal.attr);
56 sub mp.tui.readline(prompt, history, default, flags)
57 /* the readline function, with special functionality in 'flags' */
61 mp.tui.prompt(prompt ~ ' ', flags.y);
65 /* get the history stack */
66 h = mp.get_history(history);
71 /* store in c[2] the usable size */
72 push(c, mp.window.tx - c[0] - 1);
78 /* is the string bigger than the usable size? */
79 if (size(r) >= c[2]) {
80 /* if x is beyond the usable size,
85 s = splice(s, NULL, v, c[2]);
89 /* if it's a password, change everything to asterisks */
91 s = sregex('/./g', s, flags.password);
93 /* draws the string */
94 mp.tui.move(c[0], c[1], 1);
96 mp.tui.move(c[0] + x - v, c[1]);
98 local k = mp.tui.getkey();
111 if (k eq 'backspace' && x > 0) {
113 r = splice(r, NULL, x, 1);
118 r = splice(r, NULL, x, 1);
128 r = splice(r, NULL, x, -1);
132 if (k eq 'cursor-up' && size(h)) {
138 if (k eq 'cursor-down' && size(h)) {
144 if (k eq 'cursor-left' && x > 0) {
148 if (k eq 'cursor-right' && x < size(r)) {
160 if (k eq 'tab' && flags.file) {
164 local l = glob(r ~ '*');
169 local p = mp.tui.list(prompt, l, 0);
178 /* if it's not a directory, retry */
179 if (regex('@/$@', r) == NULL)
189 r = splice(r, k, x, 0);
195 /* if a string was accepted, store in the history */
196 if (h != NULL && size(r) && h[-1] ne r)
203 sub mp.tui.list(prompt, data, pos)
204 /* select from a list */
208 mp.tui.attr(mp.colors.menu.attr);
209 mp.tui.move(0, 0, 1);
210 mp.tui.addstr(prompt);
211 mp.tui.attr(mp.colors.normal.attr);
214 ty = mp.window.ty - 1;
217 r = '/^.{1,' ~ (mp.window.tx) ~ '}/';
228 if (pos >= size(data))
229 pos = size(data) - 1;
237 /* draw all the lines */
240 local l = data[n + vy];
246 mp.tui.move(0, n + 1, 1);
249 mp.tui.attr(mp.colors.cursor.attr);
251 mp.tui.attr(mp.colors.normal.attr);
253 mp.tui.addstr(regex(r,
254 sprintf("%-" ~ mp.window.tx ~ "s", l)));
259 /* clean the rest of lines */
260 mp.tui.attr(mp.colors.normal.attr);
262 mp.tui.move(0, n + 1, 1);
268 if (k eq 'cursor-up')
271 if (k eq 'cursor-down')
277 if (k eq 'page-down')
284 pos = size(data) - 1;
299 sub mp.tui.confirm(msg, def, ypos)
304 /* get the initials for localized 'Yes' and 'No' */
305 y = regex('/^./', L("Yes"));
306 n = regex('/^./', L("No"));
309 msg = msg ~ ' (' ~ y ~ '/' ~ n ~ ')';
312 /* a default option? add to prompt */
313 msg = msg ~ ' [' ~ (def && y || n) ~ ']';
316 mp.tui.prompt(msg, ypos);
318 while (ret == NULL) {
319 local k = mp.tui.getkey();
321 if (regex('/^' ~ y ~ '$/i', k))
323 if (regex('/^' ~ n ~ '$/i', k))
328 ret = (def && 1 || 2);
337 /* draw the document part */
338 mp.tui.doc_draw(doc);
340 /* draw the status line */
341 mp.tui.attr(mp.colors.normal.attr);
342 mp.tui.move(0, mp.window.ty - 1, 1);
343 mp.tui.addstr(mp.build_status_line());
345 /* draw the 'menu' hint */
346 local t = "ctrl-a: " ~ L("Menu");
347 mp.tui.move(mp.window.tx - size(t), mp.window.ty - 1);
350 /* if a hardware cursor is desired, set it */
351 if (mp.config.hw_cursor) {
353 mp.x2vx(doc.txt.lines[doc.txt.y],
354 doc.txt.x - doc.txt.vx),
355 doc.txt.y - doc.txt.vy
365 sub mp.drv.alert(msg)
367 mp.tui.prompt(msg ~ L(" [ENTER]"));
369 while (mp.tui.getkey() ne 'enter');
373 sub mp.drv.openfile(prompt)
375 mp.tui.readline(prompt, 'openfile', NULL, { 'file' => 1 } );
379 sub mp.drv.savefile(prompt)
381 mp.tui.readline(prompt, 'savefile', NULL, { 'file' => 1 } );
385 sub mp.drv.confirm(msg, def)
387 mp.tui.confirm(msg, def);
391 sub mp.drv.form(widgets)
394 local pos = mp.window.ty - size(widgets);
397 /* print first all prompts */
398 foreach (w, widgets) {
399 if (w.type ne 'list')
400 mp.tui.prompt(w.label, y++);
405 /* now iterate widgets */
406 foreach (w, widgets) {
409 if (w.type eq 'text')
410 r1 = mp.tui.readline(w.label, w.history, w.value,
413 if (w.type eq 'password')
414 r1 = mp.tui.readline(w.label, NULL, NULL,
415 { 'password' => '*', 'y' => y });
417 if (w.type eq 'checkbox') {
418 /* return value conversion */
419 local c = [ NULL, 1, 0 ];
421 r1 = c[mp.tui.confirm(w.label, w.value, y)];
424 if (w.type eq 'list')
425 r1 = mp.tui.list(w.label, w.list, w.value);
448 while (action == NULL && key ne 'escape') {
449 local pos, mo, my, vy;
454 mx = size(mp.menu) - 1;
455 if (mx >= size(mp.menu))
458 /* draw the menu bar */
459 mp.tui.attr(mp.colors.menu.attr);
460 mp.tui.move(0, 0, 1);
462 while (n < size(mp.menu)) {
464 local l = L(mp.menu[n][0]);
466 /* strip (by now) the & */
467 l = sregex('/&/g', l, NULL);
469 mp.tui.attr(mp.colors.menu.attr);
473 pos = mp.tui.getxy();
474 mp.tui.attr(mp.colors.cursor.attr);
482 /* get the menu options */
485 /* calculate panel optimal dimensions */
489 local l = mp.menu_label(i);
491 if (size(l) > pos[2])
495 /* if the panel will surpass the right margin,
497 if (pos[0] + pos[2] > mp.window.tx - 2)
498 pos[0] = mp.window.tx - pos[2] - 2;
500 local pty = size(mo);
502 if (pty > mp.window.ty - 3)
503 pty = mp.window.ty - 3;
506 mp.tui.attr(mp.colors.menu.attr);
507 mp.tui.openpanel(pos[0], 1, pos[2] + 2, pty + 2);
512 while (key ne 'escape') {
513 /* draw the options */
514 if (my < vy) vy = my;
515 if (my > vy + (pty - 1)) vy = my - (pty - 1);
520 local l = mp.menu_label(mo[n + vy]);
524 mp.tui.attr(mp.colors.cursor.attr);
526 mp.tui.attr(mp.colors.menu.attr);
528 mp.tui.move(1, 1 + n);
529 mp.tui.addstr(sprintf("%-" ~ pos[2] ~ "s", l || ''));
534 /* add visual cues that the menu is bigger */
535 if (size(mo) > pty) {
536 mp.tui.attr(mp.colors.menu.attr);
539 mp.tui.move(pos[2], 1);
543 if (pty + vy < size(mo)) {
544 mp.tui.move(pos[2], pty);
549 /* move the hw cursor to the selected option */
550 mp.tui.move(1, 1 + my - vy);
554 key = mp.tui.getkey();
556 if (key eq 'cursor-up') {
557 /* move up avoiding separators */
567 if (key eq 'cursor-down') {
568 /* move down avoiding separators */
570 if (++my >= size(mo))
578 if (key eq 'cursor-right') {
583 if (key eq 'cursor-left') {
588 if (key eq 'enter') {
597 mp.tui.attr(mp.color.normal.attr);
600 mp.process_action(action);
606 sub mp.drv.busy(onoff)
608 mp.tui.prompt(onoff && L("Please, wait...") || '');
612 sub mp.drv.main_loop()
614 while (!mp.exit_requested()) {
615 mp.tui.draw(mp.active());
617 mp.process_event(mp.tui.getkey());
622 /* returns the main namespace */