Updated TODO.
[mp-5.x.git] / mp_tui.mpsl
blobc4a81fa136df3f52e255d9143a0e9cc08434d8da
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 */
29 mp.tui = {};
31 /* colors */
33 mp.colors.menu = { 'text' => [ 'white', 'blue' ], 'flags' => [ 'bright' ] };
35 /* code */
37 sub mp.tui.prompt(prompt)
38 /* draw a prompt on screen */
40         /* delete all possible newlines */
41         prompt = sregex("/\n/g", prompt, ' ');
43         mp.tui.attr(mp.colors.normal.attr);
44         mp.tui.move(0, mp.window.ty - 1, 1);
45         mp.tui.addstr(prompt);
46         mp.tui.refresh();
50 sub mp.tui.readline(prompt, history, default, flags)
51 /* the readline function, with special functionality in 'flags' */
53         local c, r, h, i, v;
55         mp.tui.prompt(prompt ~ ' ');
56         c = mp.tui.getxy();
57         r = default || '';
59         /* get the history stack */
60         h = mp.get_history(history);
62         /* create the clipping regular expression */
63         v = '/.{1,' ~ (mp.window.tx - c[0] - 1) ~ '}$/';
65         i = 0;
67         while(1)
68         {
69                 local k, s;
71                 /* builds the string */
72                 s = regex(v, r) || '';
74                 /* if it's a password, change everything to asterisks */
75                 if(flags.password)
76                         s = sregex('/./g', s, flags.password);
78                 /* draws the string */
79                 mp.tui.move(c[0], c[1], 1);
80                 mp.tui.addstr(s);
82                 k = mp.tui.getkey();
84                 if(k eq 'enter') break;
85                 else
86                 if(k eq 'escape') { r = NULL; break; }
87                 else
88                 if(k eq 'backspace') r = sregex('/.$/', r);
89                 else
90                 if(k eq 'ctrl-u') r = '';
91                 else
92                 if(k eq 'space') r = r ~ ' ';
93                 else
94                 if(k eq 'cursor-up' && size(h)) { i--; r = h[i % size(h)]; }
95                 else
96                 if(k eq 'cursor-down' && size(h)) { i++; r = h[i % size(h)]; }
97                 else
98                 if(size(k) == 1)
99                         r = r ~ k;
100         }
102         /* if a string was accepted, store in the history */
103         if(h != NULL && size(r) && h[-1] ne r)
104                 push(h, r);
106         return(r);
110 sub mp.tui.list(prompt, data, pos)
111 /* select from a list */
113         local vy, ty, r;
115         mp.tui.move(0, 0, 1);
116         mp.tui.addstr(prompt);
118         vy = 0;
119         ty = mp.window.ty - 1;
121         /* clipping regex */
122         r = '/^.{1,' ~ (mp.window.tx) ~ '}/';
124         if(pos == NULL) pos = 0;
126         while(1)
127         {
128                 local k, n;
130                 /* limits for pos */
131                 if(pos < 0) pos = 0;
132                 if(pos >= size(data)) pos = size(data) - 1;
134                 /* limits for vy */
135                 if(pos < vy) vy = pos;
136                 if(vy + ty <= pos) vy = pos - ty + 1;
138                 /* draw all the lines */
139                 n = 0;
140                 while(n < ty)
141                 {
142                         local l = data[n + vy];
144                         /* no more data? */
145                         if(l == NULL) break;
147                         mp.tui.move(0, n + 1, 1);
149                         if(n + vy == pos)
150                                 mp.tui.attr(mp.colors.cursor.attr);
151                         else
152                                 mp.tui.attr(mp.colors.normal.attr);
154                         mp.tui.addstr(regex(r,
155                                 sprintf("%-" ~ mp.window.tx ~ "s", l)));
157                         n++;
158                 }
160                 /* clean the rest of lines */
161                 while(n < ty)
162                 {
163                         mp.tui.move(0, n + 1, 1);
164                         n++;
165                 }
167                 k = mp.tui.getkey();
169                 if(k eq 'cursor-up') pos--;
170                 else
171                 if(k eq 'cursor-down') pos++;
172                 else
173                 if(k eq 'page-up') pos -= ty;
174                 else
175                 if(k eq 'page-down') pos += ty;
176                 else
177                 if(k eq 'home') pos = 0;
178                 else
179                 if(k eq 'end') pos = size(data) - 1;
180                 else
181                 if(k eq 'enter') break;
182                 else
183                 if(k eq 'escape') { pos = NULL; break; }
184         }
186         return(pos);
190 /* interface */
192 sub mp.drv.alert(msg)
194         mp.tui.prompt(msg ~ L(" [ENTER]"));
196         while(mp.tui.getkey() ne 'enter');
200 sub mp.drv.confirm(msg, def)
202         local y, n;
203         local ret = NULL;
205         /* get the initials for localized 'Yes' and 'No' */
206         y = regex('/^./', L("Yes"));
207         n = regex('/^./', L("No"));
209         /* add options */
210         msg = msg ~ ' (' ~ y ~ '/' ~ n ~ ')';
212         if (def != NULL) {
213                 /* a default option? add to prompt */
214                 msg = msg ~ ' [' ~ (def && y || n) ~ ']';
215         }
217         mp.tui.prompt(msg);
219         while(ret == NULL)
220         {
221                 local k = mp.tui.getkey();
223                 if (regex('/^' ~ y ~ '$/i', k)) ret = 1;
224                 if (regex('/^' ~ n ~ '$/i', k)) ret = 2;
225                 if (k eq 'escape') ret = 0;
226                 if (k eq 'enter') ret = (def && 1 || 2);
227         }
229         return(ret);
232 sub mp.drv.openfile(prompt)
234         mp.tui.readline(prompt, 'openfile');
238 sub mp.drv.savefile(prompt)
240         mp.tui.readline(prompt, 'savefile');
244 sub mp.drv.form(widgets)
246         local r = [];
248         foreach(local w, widgets)
249         {
250                 local r1 = NULL;
252                 if(w.type eq 'text')
253                         r1 = mp.tui.readline(w.label, w.history, w.value);
254                 else
255                 if(w.type eq 'password')
256                         r1 = mp.tui.readline(w.label, NULL, NULL,
257                                 { 'password' => '*' });
258                 else
259                 if(w.type eq 'checkbox')
260                 {
261                         /* return value conversion */
262                         local c = [ NULL, 1, 0 ];
264                         r1 = c[mp.drv.confirm(w.label, w.value)];
265                 }
266                 else
267                 if(w.type eq 'list')
268                         r1 = mp.tui.list(w.label, w.list, w.value);
270                 /* cancellation? */
271                 if(r1 == NULL)
272                 {
273                         r = NULL;
274                         break;
275                 }
277                 /* store value */
278                 push(r, r1);
279         }
281         return(r);
285 sub mp.drv.menu()
287         local mx = 0;
288         local action = NULL;
289         local key = NULL;
291         while(action == NULL && key ne 'escape')
292         {
293                 local pos, mo, my;
294                 local n = 0;
296                 /* wrap */
297                 if(mx < 0) mx = size(mp.menu) - 1;
298                 if(mx >= size(mp.menu)) mx = 0;
300                 /* draw the menu bar */
301                 mp.tui.attr(mp.colors.menu.attr);
302                 mp.tui.move(0, 0, 1);
304                 while(n < size(mp.menu))
305                 {
306                         /* get the label */
307                         local l = L(mp.menu[n][0]);
309                         /* strip (by now) the & */
310                         l = sregex('/&/g', l, NULL);
312                         mp.tui.attr(mp.colors.menu.attr);
313                         mp.tui.addstr('   ');
315                         if(n == mx)
316                         {
317                                 pos = mp.tui.getxy();
318                                 mp.tui.attr(mp.colors.cursor.attr);
319                         }
321                         mp.tui.addstr(l);
323                         n++;
324                 }
326                 /* get the menu options */
327                 mo = mp.menu[mx][1];
329                 /* calculate panel optimal dimensions */
330                 pos[2] = 0;
332                 foreach(n, mo)
333                 {
334                         local l = mp.menu_label(n);
336                         if(size(l) > pos[2])
337                                 pos[2] = size(l);
338                 }
340                 /* if the panel will surpass the right margin,
341                    move to the left */
342                 if(pos[0] + pos[2] > mp.window.tx)
343                         pos[0] = pos[2] - mp.window.tx;
345                 mp.tui.refresh();
346                 mp.tui.attr(mp.colors.menu.attr);
347                 mp.tui.openpanel(pos[0], 1, pos[2] + 2, size(mo) + 2);
349                 my = 0;
351                 while(key ne 'escape')
352                 {
353                         /* draw the options */
354                         n = 0;
355                         while(n < size(mo))
356                         {
357                                 local l = mp.menu_label(mo[n]);
359                                 /* set color */
360                                 if(n == my)
361                                         mp.tui.attr(mp.colors.cursor.attr);
362                                 else
363                                         mp.tui.attr(mp.colors.menu.attr);
365                                 if(l != NULL)
366                                 {
367                                         mp.tui.move(1, 1 + n);
368                                         mp.tui.addstr(sprintf("%-" ~ pos[2] ~ "s", l));
369                                 }
371                                 n++;
372                         }
374                         /* move the hw cursor to the selected option */
375                         mp.tui.move(1, 1 + my);
377                         mp.tui.refresh();
379                         key = mp.tui.getkey();
381                         if(key eq 'cursor-up')
382                         {
383                                 /* move up avoiding separators */
384                                 while(1)
385                                 {
386                                         if(--my < 0)
387                                                 my = size(mo) - 1;
389                                         if(mo[my] ne '-') break;
390                                 }
391                         }
392                         else
393                         if(key eq 'cursor-down')
394                         {
395                                 /* move down avoiding separators */
396                                 while(1)
397                                 {
398                                         if(++my >= size(mo))
399                                                 my = 0;
401                                         if(mo[my] ne '-') break;
402                                 }
403                         }
404                         else
405                         if(key eq 'cursor-right') { mx++; break; }
406                         else
407                         if(key eq 'cursor-left') { mx--; break; }
408                         else
409                         if(key eq 'enter') { action = mo[my]; break; }
410                 }
412                 mp.tui.closepanel();
413         }
415         mp.tui.attr(mp.color.normal.attr);
417         if(action != NULL)
418                 mp.process_action(action);
420         return(NULL);
424 sub mp.drv.busy(onoff)
426         mp.tui.prompt(onoff && L("Please, wait...") || '');
430 /* returns the main namespace */
431 mp.tui;