The repeated word configuration interface is accepted as working (Closes: #1274).
[mp-5.x.git] / mp_tui.mpsl
blob1455fa81f5263da11b942b650ce80402f19bf18a
1 /*
3     Minimum Profit 5.x
4     A Programmer's Text Editor
6     Text User Interface.
8     Copyright (C) 1991-2007 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 */
30 mp.tui = {};
32 /** colors **/
34 mp.colors.menu = { 'text' => [ 'white', 'blue' ], 'flags' => [ 'bright' ] };
36 /** code **/
38 sub mp.tui.prompt(prompt, y)
39 /* draw a prompt on screen */
41         /* no y? default to last line */
42         if (y == NULL)
43                 y = mp.window.ty - 1;
45         /* delete all possible newlines */
46         prompt = sregex("/\n/g", prompt, ' ');
48         mp.tui.attr(mp.colors.menu.attr);
49         mp.tui.move(0, y, 1);
50         mp.tui.addstr(prompt);
51         mp.tui.attr(mp.colors.normal.attr);
52         mp.tui.refresh();
56 sub mp.tui.readline(prompt, history, default, flags)
57 /* the readline function, with special functionality in 'flags' */
59         local c, r, h, i, x;
61         mp.tui.prompt(prompt ~ ' ', flags.y);
62         c = mp.tui.getxy();
63         r = default || '';
65         /* get the history stack */
66         h = mp.get_history(history);
68         i = 0;
69         x = size(r);
71         /* store in c[2] the usable size */
72         push(c, mp.window.tx - c[0] - 1);
74         while (1) {
75                 local s = r;
76                 local v = 0;
78                 /* is the string bigger than the usable size? */
79                 if (size(r) >= c[2]) {
80                         /* if x is beyond the usable size,
81                            cut from the start */
82                         if (x >= c[2])
83                                 v = x - c[2];
85                         s = splice(s, NULL, v, c[2]);
86                         s = s[1];
87                 }
89                 /* if it's a password, change everything to asterisks */
90                 if (flags.password)
91                         s = sregex('/./g', s, flags.password);
93                 /* draws the string */
94                 mp.tui.move(c[0], c[1], 1);
95                 mp.tui.addstr(s);
96                 mp.tui.move(c[0] + x - v, c[1]);
98                 local k = mp.tui.getkey();
100                 if (k eq 'space')
101                         k = ' ';
103                 if (k eq 'enter')
104                         break;
105                 else
106                 if (k eq 'escape') {
107                         r = NULL;
108                         break;
109                 }
110                 else
111                 if (k eq 'backspace' && x > 0) {
112                         x--;
113                         r = splice(r, NULL, x, 1);
114                         r = r[0];
115                 }
116                 else
117                 if(k eq 'delete') {
118                         r = splice(r, NULL, x, 1);
119                         r = r[0];
120                 }
121                 else
122                 if (k eq 'ctrl-u') {
123                         x = 0;
124                         r = '';
125                 }
126                 else
127                 if (k eq 'ctrl-k') {
128                         r = splice(r, NULL, x, -1);
129                         r = r[0];
130                 }
131                 else
132                 if (k eq 'cursor-up' && size(h)) {
133                         i--;
134                         r = h[i % size(h)];
135                         x = size(r);
136                 }
137                 else
138                 if (k eq 'cursor-down' && size(h)) {
139                         i++;
140                         r = h[i % size(h)];
141                         x = size(r);
142                 }
143                 else
144                 if (k eq 'cursor-left' && x > 0) {
145                         x--;
146                 }
147                 else
148                 if (k eq 'cursor-right' && x < size(r)) {
149                         x++;
150                 }
151                 else
152                 if (k eq 'home') {
153                         x = 0;
154                 }
155                 else
156                 if (k eq 'end') {
157                         x = size(r);
158                 }
159                 else
160                 if (k eq 'tab' && flags.file) {
161                         local l = glob(r ~ '*');
163                         if (size(l)) {
164                                 local p = mp.tui.list(prompt, l, 0);
166                                 if (p == NULL)
167                                         r = NULL;
168                                 else
169                                         r = l[p];
171                                 break;
172                         }
173                 }
174                 else
175                 if (size(k) == 1) {
176                         r = splice(r, k, x, 0);
177                         r = r[0];
178                         x++;
179                 }
180         }
182         /* if a string was accepted, store in the history */
183         if (h != NULL && size(r) && h[-1] ne r)
184                 push(h, r);
186         return r;
190 sub mp.tui.list(prompt, data, pos)
191 /* select from a list */
193         local vy, ty, r;
195         mp.tui.attr(mp.colors.menu.attr);
196         mp.tui.move(0, 0, 1);
197         mp.tui.addstr(prompt);
198         mp.tui.attr(mp.colors.normal.attr);
200         vy = 0;
201         ty = mp.window.ty - 1;
203         /* clipping regex */
204         r = '/^.{1,' ~ (mp.window.tx) ~ '}/';
206         if (pos == NULL)
207                 pos = 0;
209         while (1) {
210                 local k, n;
212                 /* limits for pos */
213                 if (pos < 0)
214                         pos = 0;
215                 if (pos >= size(data))
216                         pos = size(data) - 1;
218                 /* limits for vy */
219                 if (pos < vy)
220                         vy = pos;
221                 if (vy + ty <= pos)
222                         vy = pos - ty + 1;
224                 /* draw all the lines */
225                 n = 0;
226                 while (n < ty) {
227                         local l = data[n + vy];
229                         /* no more data? */
230                         if (l == NULL)
231                                 break;
233                         mp.tui.move(0, n + 1, 1);
235                         if (n + vy == pos)
236                                 mp.tui.attr(mp.colors.cursor.attr);
237                         else
238                                 mp.tui.attr(mp.colors.normal.attr);
240                         mp.tui.addstr(regex(r,
241                                 sprintf("%-" ~ mp.window.tx ~ "s", l)));
243                         n++;
244                 }
246                 /* clean the rest of lines */
247                 mp.tui.attr(mp.colors.normal.attr);
248                 while (n < ty) {
249                         mp.tui.move(0, n + 1, 1);
250                         n++;
251                 }
253                 k = mp.tui.getkey();
255                 if (k eq 'cursor-up')
256                         pos--;
257                 else
258                 if (k eq 'cursor-down')
259                         pos++;
260                 else
261                 if (k eq 'page-up')
262                         pos -= ty;
263                 else
264                 if (k eq 'page-down')
265                         pos += ty;
266                 else
267                 if (k eq 'home')
268                         pos = 0;
269                 else
270                 if (k eq 'end')
271                         pos = size(data) - 1;
272                 else
273                 if (k eq 'enter')
274                         break;
275                 else
276                 if (k eq 'escape') {
277                         pos = NULL;
278                         break;
279                 }
280         }
282         return pos;
286 sub mp.tui.confirm(msg, def, ypos)
288         local y, n;
289         local ret = NULL;
291         /* get the initials for localized 'Yes' and 'No' */
292         y = regex('/^./', L("Yes"));
293         n = regex('/^./', L("No"));
295         /* add options */
296         msg = msg ~ ' (' ~ y ~ '/' ~ n ~ ')';
298         if (def != NULL) {
299                 /* a default option? add to prompt */
300                 msg = msg ~ ' [' ~ (def && y || n) ~ ']';
301         }
303         mp.tui.prompt(msg, ypos);
305         while (ret == NULL) {
306                 local k = mp.tui.getkey();
308                 if (regex('/^' ~ y ~ '$/i', k))
309                         ret = 1;
310                 if (regex('/^' ~ n ~ '$/i', k))
311                         ret = 2;
312                 if (k eq 'escape')
313                         ret = 0;
314                 if (k eq 'enter')
315                         ret = (def && 1 || 2);
316         }
318         return ret;
321 /** interface **/
323 sub mp.drv.alert(msg)
325         mp.tui.prompt(msg ~ L(" [ENTER]"));
327         while (mp.tui.getkey() ne 'enter');
331 sub mp.drv.openfile(prompt)
333         mp.tui.readline(prompt, 'openfile', NULL, { 'file' => 1 } );
337 sub mp.drv.savefile(prompt)
339         mp.tui.readline(prompt, 'savefile', NULL, { 'file' => 1 } );
343 sub mp.drv.confirm(msg, def)
345         mp.tui.confirm(msg, def);
349 sub mp.drv.form(widgets)
351         local r = [];
352         local pos = mp.window.ty - size(widgets);
353         local y = pos;
355         /* print first all prompts */
356         foreach (w, widgets) {
357                 if (w.type ne 'list')
358                         mp.tui.prompt(w.label, y++);
359         }
361         y = pos;
363         /* now iterate widgets */
364         foreach (w, widgets) {
365                 local r1 = NULL;
367                 if (w.type eq 'text')
368                         r1 = mp.tui.readline(w.label, w.history, w.value,
369                                 { 'y' => y } );
370                 else
371                 if (w.type eq 'password')
372                         r1 = mp.tui.readline(w.label, NULL, NULL,
373                                 { 'password' => '*', 'y' => y });
374                 else
375                 if (w.type eq 'checkbox') {
376                         /* return value conversion */
377                         local c = [ NULL, 1, 0 ];
379                         r1 = c[mp.tui.confirm(w.label, w.value, y)];
380                 }
381                 else
382                 if (w.type eq 'list')
383                         r1 = mp.tui.list(w.label, w.list, w.value);
385                 /* cancellation? */
386                 if (r1 == NULL) {
387                         r = NULL;
388                         break;
389                 }
391                 /* store value */
392                 push(r, r1);
393                 y++;
394         }
396         return r;
400 sub mp.drv.menu()
402         local mx = 0;
403         local action = NULL;
404         local key = NULL;
406         while (action == NULL && key ne 'escape') {
407                 local pos, mo, my;
408                 local n = 0;
410                 /* wrap */
411                 if (mx < 0)
412                         mx = size(mp.menu) - 1;
413                 if (mx >= size(mp.menu))
414                         mx = 0;
416                 /* draw the menu bar */
417                 mp.tui.attr(mp.colors.menu.attr);
418                 mp.tui.move(0, 0, 1);
420                 while (n < size(mp.menu)) {
421                         /* get the label */
422                         local l = L(mp.menu[n][0]);
424                         /* strip (by now) the & */
425                         l = sregex('/&/g', l, NULL);
427                         mp.tui.attr(mp.colors.menu.attr);
428                         mp.tui.addstr('   ');
430                         if (n == mx) {
431                                 pos = mp.tui.getxy();
432                                 mp.tui.attr(mp.colors.cursor.attr);
433                         }
435                         mp.tui.addstr(l);
437                         n++;
438                 }
440                 /* get the menu options */
441                 mo = mp.menu[mx][1];
443                 /* calculate panel optimal dimensions */
444                 pos[2] = 0;
446                 foreach (i, mo) {
447                         local l = mp.menu_label(i);
449                         if (size(l) > pos[2])
450                                 pos[2] = size(l);
451                 }
453                 /* if the panel will surpass the right margin,
454                    move to the left */
455                 if (pos[0] + pos[2] > mp.window.tx - 2)
456                         pos[0] = mp.window.tx - pos[2] - 2;
458                 mp.tui.refresh();
459                 mp.tui.attr(mp.colors.menu.attr);
460                 mp.tui.openpanel(pos[0], 1, pos[2] + 2, size(mo) + 2);
462                 my = 0;
464                 while (key ne 'escape') {
465                         /* draw the options */
466                         n = 0;
467                         while (n < size(mo)) {
468                                 local l = mp.menu_label(mo[n]);
470                                 /* set color */
471                                 if (n == my)
472                                         mp.tui.attr(mp.colors.cursor.attr);
473                                 else
474                                         mp.tui.attr(mp.colors.menu.attr);
476                                 if (l != NULL) {
477                                         mp.tui.move(1, 1 + n);
478                                         mp.tui.addstr(sprintf("%-" ~ pos[2] ~ "s", l));
479                                 }
481                                 n++;
482                         }
484                         /* move the hw cursor to the selected option */
485                         mp.tui.move(1, 1 + my);
487                         mp.tui.refresh();
489                         key = mp.tui.getkey();
491                         if (key eq 'cursor-up') {
492                                 /* move up avoiding separators */
493                                 while (1) {
494                                         if (--my < 0)
495                                                 my = size(mo) - 1;
497                                         if (mo[my] ne '-')
498                                                 break;
499                                 }
500                         }
501                         else
502                         if (key eq 'cursor-down') {
503                                 /* move down avoiding separators */
504                                 while (1) {
505                                         if (++my >= size(mo))
506                                                 my = 0;
508                                         if (mo[my] ne '-')
509                                                 break;
510                                 }
511                         }
512                         else
513                         if (key eq 'cursor-right') {
514                                 mx++;
515                                 break;
516                         }
517                         else
518                         if (key eq 'cursor-left') {
519                                 mx--;
520                                 break;
521                         }
522                         else
523                         if (key eq 'enter') {
524                                 action = mo[my];
525                                 break;
526                         }
527                 }
529                 mp.tui.closepanel();
530         }
532         mp.tui.attr(mp.color.normal.attr);
534         if (action != NULL)
535                 mp.process_action(action);
537         return NULL;
541 sub mp.drv.busy(onoff)
543         mp.tui.prompt(onoff && L("Please, wait...") || '');
547 /* returns the main namespace */
548 mp.tui;