Just a little correction at the it.po file.
[midnight-commander.git] / src / dlg.c
blob0f70bfd755a20e029056c9c5cfd25217412c715d
1 /* Dlg box features module for the Midnight Commander
2 Copyright (C) 1994, 1995 Radek Doulik, Miguel de Icaza
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 #include <config.h>
20 #include <string.h>
21 #include <stdio.h>
22 #include <ctype.h>
23 #include <stdarg.h>
24 #include "global.h"
25 #include "tty.h"
26 #include "menu.h"
27 #include "win.h"
28 #include "color.h"
29 #include "mouse.h"
30 #include "help.h"
31 #include "key.h" /* For mi_getch() */
32 #include "dlg.h"
33 #include "dialog.h" /* For push_refresh() and pop_refresh() */
34 #include "layout.h"
35 #include "main.h"
37 #define waddc(w,y1,x1,c) move (w->y+y1, w->x+x1); addch (c)
39 /* Primitive way to check if the the current dialog is our dialog */
40 /* This is needed by async routines like load_prompt */
41 Dlg_head *current_dlg = 0;
43 /* A hook list for idle events */
44 Hook *idle_hook = 0;
46 static void dlg_broadcast_msg_to (Dlg_head *h, int message, int reverse, int flags);
48 static void slow_box (Dlg_head *h, int y, int x, int ys, int xs)
50 move (h->y+y, h->x+x);
51 hline (' ', xs);
52 vline (' ', ys);
53 move (h->y+y, h->x+x+xs-1);
54 vline (' ', ys);
55 move (h->y+y+ys-1, h->x+x);
56 hline (' ', xs);
59 /* draw box in window */
60 void draw_box (Dlg_head *h, int y, int x, int ys, int xs)
62 if (slow_terminal){
63 slow_box (h, y, x, ys, xs);
64 return;
67 #ifndef HAVE_SLANG
68 waddc (h, y, x, ACS_ULCORNER);
69 hline (ACS_HLINE, xs - 2);
70 waddc (h, y + ys - 1, x, ACS_LLCORNER);
71 hline (ACS_HLINE, xs - 2);
73 waddc (h, y, x + xs - 1, ACS_URCORNER);
74 waddc (h, y + ys - 1, x + xs - 1, ACS_LRCORNER);
76 move (h->y+y+1, h->x+x);
77 vline (ACS_VLINE, ys - 2);
78 move (h->y+y+1, h->x+x+xs-1);
79 vline (ACS_VLINE, ys - 2);
80 #else
81 SLsmg_draw_box (h->y+y, h->x+x, ys, xs);
82 #endif /* HAVE_SLANG */
85 /* draw box in window */
86 void draw_double_box (Dlg_head *h, int y, int x, int ys, int xs)
88 #ifndef HAVE_SLANG
89 draw_box (h, y, x, ys, xs);
90 #else
91 SLsmg_draw_double_box (h->y+y, h->x+x, ys, xs);
92 #endif /* HAVE_SLANG */
95 void widget_erase (Widget *w)
97 int x, y;
99 for (y = 0; y < w->lines; y++){
100 widget_move (w, y, 0);
101 for (x = 0; x < w->cols; x++)
102 addch (' ');
106 void dlg_erase (Dlg_head *h)
108 int x, y;
110 for (y = 0; y < h->lines; y++){
111 move (y+h->y, h->x); /* FIXME: should test if ERR */
112 for (x = 0; x < h->cols; x++){
113 addch (' ');
118 void
119 init_widget (Widget *w, int y, int x, int lines, int cols,
120 callback_fn callback, destroy_fn destroy,
121 mouse_h mouse_handler, char *tkname)
123 w->x = x;
124 w->y = y;
125 w->cols = cols;
126 w->lines = lines;
127 w->callback = callback;
128 w->destroy = destroy;
129 w->mouse = mouse_handler;
130 w->parent = 0;
131 w->tkname = tkname;
133 if (tkname && *tkname == 0) {
134 fprintf (stderr, "Got a null string for the tkname\n");
135 abort ();
137 /* Almost all widgets want to put the cursor in a suitable place */
138 w->options = W_WANT_CURSOR;
141 /* Default callback for widgets */
142 int default_proc (int Msg, int Par)
144 switch (Msg){
146 case WIDGET_HOTKEY: /* Didn't use the key */
147 return 0;
149 case WIDGET_INIT: /* We could tell if something went wrong */
150 return 1;
152 case WIDGET_KEY:
153 return 0; /* Didn't use the key */
155 case WIDGET_FOCUS: /* We accept FOCUSes */
156 return 1;
158 case WIDGET_UNFOCUS: /* We accept loose FOCUSes */
159 return 1;
161 case WIDGET_DRAW:
162 return 1;
164 case WIDGET_DESTROY:
165 return 1;
167 case WIDGET_CURSOR:
168 /* Move the cursor to the default widget position */
169 return 1;
171 case WIDGET_IDLE:
172 return 1;
174 printf ("Internal error: unhandled message: %d\n", Msg);
175 return 1;
178 /* Clean the dialog area, draw the frame and the title */
179 void
180 common_dialog_repaint (struct Dlg_head *h)
182 int space;
184 space = (h->flags & DLG_COMPACT) ? 0 : 1;
186 attrset (NORMALC);
187 dlg_erase (h);
188 draw_box (h, space, space, h->lines - 2 * space, h->cols - 2 * space);
190 if (h->title) {
191 attrset (HOT_NORMALC);
192 dlg_move (h, space, (h->cols - strlen (h->title)) / 2);
193 addstr (h->title);
197 /* Default dialog callback */
198 int default_dlg_callback (Dlg_head *h, int id, int msg)
200 if (msg == DLG_DRAW && h->color) {
201 common_dialog_repaint (h);
202 return MSG_HANDLED;
204 if (msg == DLG_IDLE){
205 dlg_broadcast_msg_to (h, WIDGET_IDLE, 0, W_WANT_IDLE);
206 return MSG_HANDLED;
208 return MSG_NOT_HANDLED;
211 Dlg_head *
212 create_dlg (int y1, int x1, int lines, int cols, const int *color_set,
213 dlg_cb_fn callback, char *help_ctx, const char *title,
214 int flags)
216 Dlg_head *new_d;
218 if (flags & DLG_CENTER) {
219 y1 = (LINES - lines) / 2;
220 x1 = (COLS - cols) / 2;
223 if ((flags & DLG_TRYUP) && (y1 > 3))
224 y1 -= 2;
226 new_d = g_new0 (Dlg_head, 1);
227 new_d->color = color_set;
228 new_d->help_ctx = help_ctx;
229 new_d->callback = callback ? callback : default_dlg_callback;
230 new_d->x = x1;
231 new_d->y = y1;
232 new_d->cols = cols;
233 new_d->lines = lines;
234 new_d->flags = flags;
236 /* Strip existing spaces, add one space before and after the title */
237 if (title) {
238 char *t;
239 t = g_strstrip (g_strdup (title));
240 new_d->title = g_strconcat (" ", t, " ", NULL);
241 g_free (t);
244 return (new_d);
247 void set_idle_proc (Dlg_head *d, int state)
249 d->send_idle_msg = state;
252 /* add component to dialog buffer */
253 int add_widget (Dlg_head *where, void *what)
255 Widget_Item *back;
256 Widget *widget = (Widget *) what;
258 /* Don't accept 0 widgets, this could be from widgets that could not */
259 /* initialize properly */
260 if (!what)
261 return 0;
263 widget->x += where->x;
264 widget->y += where->y;
266 if (where->running){
267 Widget_Item *point = where->current;
269 where->current = g_new (Widget_Item, 1);
271 if (point){
272 where->current->next = point->next;
273 where->current->prev = point;
274 point->next->prev = where->current;
275 point->next = where->current;
276 } else {
277 where->current->next = where->current;
278 where->first = where->current;
279 where->current->prev = where->first;
280 where->last = where->current;
281 where->first->next = where->last;
283 } else {
284 back = where->current;
285 where->current = g_new (Widget_Item, 1);
286 if (back){
287 back->prev = where->current;
288 where->current->next = back;
289 } else {
290 where->current->next = where->current;
291 where->first = where->current;
294 where->current->prev = where->first;
295 where->last = where->current;
296 where->first->next = where->last;
299 where->current->dlg_id = where->count;
300 where->current->widget = what;
301 where->current->widget->parent = where;
303 where->count++;
305 /* If the widget is inserted in a running dialog */
306 if (where->running) {
307 send_message (widget, WIDGET_INIT, 0);
308 send_message (widget, WIDGET_DRAW, 0);
310 return (where->count - 1);
313 /* broadcast a message to all the widgets in a dialog that have
314 * the options set to flags.
316 static void
317 dlg_broadcast_msg_to (Dlg_head *h, int message, int reverse, int flags)
319 Widget_Item *p, *first, *wi;
321 if (!h->current)
322 return;
324 if (reverse)
325 first = p = h->current->prev;
326 else
327 first = p = h->current->next;
329 do {
330 wi = p;
331 if (reverse)
332 p = p->prev;
333 else
334 p = p->next;
335 send_message (wi->widget, message, 0);
336 } while (first != p);
339 /* broadcast a message to all the widgets in a dialog */
340 void dlg_broadcast_msg (Dlg_head *h, int message, int reverse)
342 dlg_broadcast_msg_to (h, message, reverse, ~0);
345 int dlg_focus (Dlg_head *h)
347 if (!h->current)
348 return 0;
350 if (send_message (h->current->widget, WIDGET_FOCUS, 0)){
351 (*h->callback) (h, h->current->dlg_id, DLG_FOCUS);
352 return 1;
354 return 0;
357 static int
358 dlg_unfocus (Dlg_head *h)
360 if (!h->current)
361 return 0;
363 if (send_message (h->current->widget, WIDGET_UNFOCUS, 0)){
364 (*h->callback) (h, h->current->dlg_id, DLG_UNFOCUS);
365 return 1;
367 return 0;
370 static void select_a_widget (Dlg_head *h, int down)
372 int dir_forward = !(h->flags & DLG_BACKWARD);
374 if (!h->current)
375 return;
377 if (!down)
378 dir_forward = !dir_forward;
380 do {
381 if (dir_forward)
382 h->current = h->current->next;
383 else
384 h->current = h->current->prev;
385 } while (!dlg_focus (h));
388 /* Return true if the windows overlap */
389 int dlg_overlap (Widget *a, Widget *b)
391 if ((b->x >= a->x + a->cols)
392 || (a->x >= b->x + b->cols)
393 || (b->y >= a->y + a->lines)
394 || (a->y >= b->y + b->lines))
395 return 0;
396 return 1;
400 /* Find the widget with the given callback in the dialog h */
401 Widget *
402 find_widget_type (Dlg_head *h, callback_fn signature)
404 Widget *w;
405 Widget_Item *item;
406 int i;
408 if (!h)
409 return 0;
410 if (!h->current)
411 return 0;
413 w = 0;
414 for (i = 0, item = h->current; i < h->count; i++, item = item->next) {
415 if (item->widget->callback == signature) {
416 w = item->widget;
417 break;
420 return w;
423 void dlg_one_up (Dlg_head *h)
425 Widget_Item *old;
427 old = h->current;
429 if (!old)
430 return;
432 /* If it accepts unFOCUSion */
433 if (!dlg_unfocus(h))
434 return;
436 select_a_widget (h, 0);
437 if (dlg_overlap (old->widget, h->current->widget)){
438 send_message (h->current->widget, WIDGET_DRAW, 0);
439 send_message (h->current->widget, WIDGET_FOCUS, 0);
443 void dlg_one_down (Dlg_head *h)
445 Widget_Item *old;
447 old = h->current;
448 if (!old)
449 return;
451 if (!dlg_unfocus (h))
452 return;
454 select_a_widget (h, 1);
455 if (dlg_overlap (old->widget, h->current->widget)){
456 send_message (h->current->widget, WIDGET_DRAW, 0);
457 send_message (h->current->widget, WIDGET_FOCUS, 0);
461 int dlg_select_widget (Dlg_head *h, void *w)
463 if (!h->current)
464 return 0;
466 if (dlg_unfocus (h)){
467 while (h->current->widget != w)
468 h->current = h->current->next;
469 while (!dlg_focus (h))
470 h->current = h->current->next;
472 return 1;
474 return 0;
477 #define callback(h) (h->current->widget->callback)
479 void update_cursor (Dlg_head *h)
481 if (!h->current)
482 return;
483 if (h->current->widget->options & W_WANT_CURSOR)
484 send_message (h->current->widget, WIDGET_CURSOR, 0);
485 else {
486 Widget_Item *p = h->current;
488 do {
489 if (p->widget->options & W_WANT_CURSOR)
490 if ((*p->widget->callback)(p->widget, WIDGET_CURSOR, 0)){
491 break;
493 p = p->next;
494 } while (h->current != p);
498 /* Redraw the widgets in reverse order, leaving the current widget
499 * as the last one
501 void dlg_redraw (Dlg_head *h)
503 (h->callback)(h, 0, DLG_DRAW);
505 dlg_broadcast_msg (h, WIDGET_DRAW, 1);
507 update_cursor (h);
510 static void
511 dlg_refresh (void *parameter)
513 dlg_redraw ((Dlg_head *) parameter);
516 void dlg_stop (Dlg_head *h)
518 h->running = 0;
521 static inline void dialog_handle_key (Dlg_head *h, int d_key)
523 switch (d_key){
524 case KEY_LEFT:
525 case KEY_UP:
526 dlg_one_up (h);
527 break;
529 case KEY_RIGHT:
530 case KEY_DOWN:
531 dlg_one_down (h);
532 break;
534 case KEY_F(1):
535 interactive_display (NULL, h->help_ctx);
536 do_refresh ();
537 break;
539 case XCTRL('z'):
540 suspend_cmd ();
541 /* Fall through */
543 case XCTRL('l'):
544 #ifndef HAVE_SLANG
545 /* Use this if the refreshes fail */
546 clr_scr ();
547 do_refresh ();
548 #else
549 touchwin (stdscr);
550 #endif /* HAVE_SLANG */
551 mc_refresh ();
552 doupdate ();
553 break;
555 case '\n':
556 case KEY_ENTER:
557 h->ret_value = B_ENTER;
558 h->running = 0;
559 break;
561 case ESC_CHAR:
562 case KEY_F (10):
563 case XCTRL ('c'):
564 case XCTRL ('g'):
565 h->ret_value = B_CANCEL;
566 dlg_stop (h);
567 break;
571 static int dlg_try_hotkey (Dlg_head *h, int d_key)
573 Widget_Item *hot_cur;
574 Widget_Item *previous;
575 int handled, c;
577 if (!h->current)
578 return 0;
581 * Explanation: we don't send letter hotkeys to other widgets if
582 * the currently selected widget is an input line
585 if (h->current->widget->options & W_IS_INPUT){
586 if(d_key < 255 && isalpha(d_key))
587 return 0;
590 /* If it's an alt key, send the message */
591 c = d_key & ~ALT(0);
592 if (d_key & ALT(0) && c < 255 && isalpha(c))
593 d_key = tolower(c);
595 #ifdef NATIVE_WIN32
596 /* .ado: fix problem with file_permission under Win95 */
597 if (d_key == 0) return 0;
598 #endif
600 handled = 0;
601 if (h->current->widget->options & W_WANT_HOTKEY)
602 handled = callback (h) (h->current->widget, WIDGET_HOTKEY, d_key);
604 /* If not used, send hotkey to other widgets */
605 if (handled)
606 return handled;
608 hot_cur = h->current;
610 /* send it to all widgets */
611 do {
612 if (hot_cur->widget->options & W_WANT_HOTKEY)
613 handled |= (*hot_cur->widget->callback)
614 (hot_cur->widget, WIDGET_HOTKEY, d_key);
616 if (!handled)
617 hot_cur = hot_cur->next;
618 } while (h->current != hot_cur && !handled);
620 if (!handled)
621 return 0;
623 (*h->callback) (h, 0, DLG_HOTKEY_HANDLED);
624 previous = h->current;
625 if (!dlg_unfocus (h))
626 return handled;
628 h->current = hot_cur;
629 if (!dlg_focus (h)){
630 h->current = previous;
631 dlg_focus (h);
633 return handled;
636 static int
637 dlg_key_event (Dlg_head * h, int d_key)
639 int handled;
641 if (!h->current)
642 return 0;
644 /* TAB used to cycle */
645 if (!(h->flags & DLG_WANT_TAB)
646 && (d_key == '\t' || d_key == KEY_BTAB)) {
647 if (d_key == '\t')
648 dlg_one_down (h);
649 else
650 dlg_one_up (h);
651 } else {
653 /* first can dlg_callback handle the key */
654 handled = (*h->callback) (h, d_key, DLG_KEY);
656 /* next try the hotkey */
657 if (!handled)
658 handled = dlg_try_hotkey (h, d_key);
660 /* not used - then try widget_callback */
661 if (!handled)
662 handled |=
663 callback (h) (h->current->widget, WIDGET_KEY, d_key);
665 /* not used- try to use the unhandled case */
666 if (!handled)
667 handled |= (*h->callback) (h, d_key, DLG_UNHANDLED_KEY);
669 if (!handled)
670 dialog_handle_key (h, d_key);
671 (*h->callback) (h, d_key, DLG_POST_KEY);
673 return handled;
675 return 1;
678 static inline int
679 dlg_mouse_event (Dlg_head * h, Gpm_Event * event)
681 Widget_Item *item;
682 Widget_Item *starting_widget = h->current;
683 Gpm_Event new_event;
684 int x = event->x;
685 int y = event->y;
687 /* kludge for the menubar: start at h->first, not current */
688 /* Must be careful in the insertion order to the dlg list */
689 if (y == 1 && (h->flags & DLG_HAS_MENUBAR))
690 starting_widget = h->first;
692 item = starting_widget;
693 do {
694 Widget *widget = item->widget;
696 item = item->next;
698 if (!((x > widget->x) && (x <= widget->x + widget->cols)
699 && (y > widget->y) && (y <= widget->y + widget->lines)))
700 continue;
702 new_event = *event;
703 new_event.x -= widget->x;
704 new_event.y -= widget->y;
706 if (!widget->mouse)
707 return MOU_NORMAL;
709 return (*widget->mouse) (&new_event, widget);
710 } while (item != starting_widget);
712 return MOU_NORMAL;
715 /* Run dialog routines */
717 /* Init the process */
718 void init_dlg (Dlg_head *h)
720 int refresh_mode;
722 /* Initialize dialog manager and widgets */
723 (*h->callback) (h, 0, DLG_INIT);
724 dlg_broadcast_msg (h, WIDGET_INIT, 0);
726 if (h->x == 0 && h->y == 0 && h->cols == COLS && h->lines == LINES)
727 refresh_mode = REFRESH_COVERS_ALL;
728 else
729 refresh_mode = REFRESH_COVERS_PART;
731 push_refresh (dlg_refresh, h, refresh_mode);
732 h->refresh_pushed = 1;
734 /* Initialize direction */
735 if (h->flags & DLG_BACKWARD)
736 h->current = h->first;
738 if (h->initfocus != NULL)
739 h->current = h->initfocus;
741 h->previous_dialog = current_dlg;
742 current_dlg = h;
744 /* Initialize the mouse status */
745 h->mouse_status = MOU_NORMAL;
747 /* Select the first widget that takes focus */
748 while (!dlg_focus (h) && h->current)
749 h->current = h->current->next;
751 /* Redraw the screen */
752 dlg_redraw (h);
754 h->ret_value = 0;
755 h->running = 1;
758 /* Shutdown the run_dlg */
759 void dlg_run_done (Dlg_head *h)
761 if (h->current)
762 (*h->callback) (h, h->current->dlg_id, DLG_END);
764 current_dlg = (Dlg_head *) h->previous_dialog;
767 void dlg_process_event (Dlg_head *h, int key, Gpm_Event *event)
769 if (key == EV_NONE){
770 if (got_interrupt ())
771 key = XCTRL('g');
772 else
773 return;
776 if (key == EV_MOUSE)
777 h->mouse_status = dlg_mouse_event (h, event);
778 else
779 dlg_key_event (h, key);
782 static inline void
783 frontend_run_dlg (Dlg_head *h)
785 int d_key;
786 Gpm_Event event;
788 event.x = -1;
789 while (h->running) {
790 if (winch_flag)
791 change_screen_size ();
793 if (is_idle ()) {
794 if (idle_hook)
795 execute_hooks (idle_hook);
797 while (h->send_idle_msg && is_idle ()) {
798 (*h->callback) (h, 0, DLG_IDLE);
801 /* Allow terminating the dialog from the idle handler */
802 if (!h->running)
803 break;
806 update_cursor (h);
807 (*h->callback) (h, 0, DLG_PRE_EVENT);
809 /* Clear interrupt flag */
810 got_interrupt ();
811 d_key = get_event (&event, h->mouse_status == MOU_REPEAT, 1);
813 dlg_process_event (h, d_key, &event);
815 if (!h->running)
816 (*h->callback) (h, 0, DLG_VALIDATE);
820 /* Standard run dialog routine
821 * We have to keep this routine small so that we can duplicate it's
822 * behavior on complex routines like the file routines, this way,
823 * they can call the dlg_process_event without rewriting all the code
825 int run_dlg (Dlg_head *h)
827 init_dlg (h);
828 frontend_run_dlg (h);
829 dlg_run_done (h);
830 return h->ret_value;
833 void
834 destroy_dlg (Dlg_head *h)
836 int i;
837 Widget_Item *c;
839 if (h->refresh_pushed)
840 pop_refresh ();
842 dlg_broadcast_msg (h, WIDGET_DESTROY, 0);
843 c = h->current;
844 for (i = 0; i < h->count; i++){
845 if (c->widget->destroy)
846 c->widget->destroy (c->widget);
847 c = c->next;
848 if (h->current){
849 g_free (h->current->widget);
850 g_free (h->current);
852 h->current = c;
854 g_free (h->title);
855 g_free (h);
857 do_refresh ();
860 void widget_set_size (Widget *widget, int y, int x, int lines, int cols)
862 widget->x = x;
863 widget->y = y;
864 widget->cols = cols;
865 widget->lines = lines;
868 /* Replace widget old for widget new in the h dialog */
869 void dlg_replace_widget (Dlg_head *h, Widget *old, Widget *new)
871 Widget_Item *p = h->current;
872 int should_focus = 0;
874 if (!h->current)
875 return;
877 do {
878 if (p->widget == old){
880 if (old == h->current->widget)
881 should_focus = 1;
883 /* We found the widget */
884 /* First kill the widget */
885 new->parent = h;
886 send_message (old, WIDGET_DESTROY, 0);
887 (*old->destroy) (old);
889 /* We insert the new widget */
890 p->widget = new;
891 send_message (new, WIDGET_INIT, 0);
892 if (should_focus){
893 if (dlg_focus (h) == 0)
894 select_a_widget (h, 1);
896 send_message (new, WIDGET_DRAW, 0);
897 break;
899 p = p->next;
900 } while (p != h->current);
903 /* Returns the index of h->current from h->first */
904 int dlg_item_number (Dlg_head *h)
906 Widget_Item *p;
907 int i = 0;
909 p = h->first;
911 do {
912 if (p == h->current)
913 return i;
914 i++;
915 p = p->next;
916 } while (p != h->first);
917 fprintf (stderr, "Internal error: current not in dialog list\n\r");
918 exit (1);
921 int dlg_select_nth_widget (Dlg_head *h, int n)
923 Widget_Item *w;
924 int i;
926 w = h->first;
927 for (i = 0; i < n; i++)
928 w = w->next;
930 return dlg_select_widget (h, w->widget);