* extfs.c (extfs_generate_entry): Initialize inode->last_in_subdir.
[midnight-commander.git] / src / dialog.c
blobe7f98155e51e98354a36c06c04158a6e8b514104
1 /* Dialog 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 "mouse.h"
27 #include "help.h" /* interactive_display() */
28 #include "key.h" /* mi_getch() */
29 #include "dialog.h"
30 #include "layout.h" /* winch_flag */
31 #include "execute.h" /* suspend_cmd() */
32 #include "main.h" /* slow_terminal */
34 #define waddc(w,y1,x1,c) move (w->y+y1, w->x+x1); addch (c)
36 /* Primitive way to check if the the current dialog is our dialog */
37 /* This is needed by async routines like load_prompt */
38 Dlg_head *current_dlg = 0;
40 /* A hook list for idle events */
41 Hook *idle_hook = 0;
43 static void dlg_broadcast_msg_to (Dlg_head * h, widget_msg_t message,
44 int reverse, int flags);
46 static void slow_box (Dlg_head *h, int y, int x, int ys, int xs)
48 move (h->y+y, h->x+x);
49 hline (' ', xs);
50 vline (' ', ys);
51 move (h->y+y, h->x+x+xs-1);
52 vline (' ', ys);
53 move (h->y+y+ys-1, h->x+x);
54 hline (' ', xs);
57 /* draw box in window */
58 void draw_box (Dlg_head *h, int y, int x, int ys, int xs)
60 if (slow_terminal){
61 slow_box (h, y, x, ys, xs);
62 return;
65 #ifndef HAVE_SLANG
66 waddc (h, y, x, ACS_ULCORNER);
67 hline (ACS_HLINE, xs - 2);
68 waddc (h, y + ys - 1, x, ACS_LLCORNER);
69 hline (ACS_HLINE, xs - 2);
71 waddc (h, y, x + xs - 1, ACS_URCORNER);
72 waddc (h, y + ys - 1, x + xs - 1, ACS_LRCORNER);
74 move (h->y+y+1, h->x+x);
75 vline (ACS_VLINE, ys - 2);
76 move (h->y+y+1, h->x+x+xs-1);
77 vline (ACS_VLINE, ys - 2);
78 #else
79 SLsmg_draw_box (h->y+y, h->x+x, ys, xs);
80 #endif /* HAVE_SLANG */
83 /* draw box in window */
84 void draw_double_box (Dlg_head *h, int y, int x, int ys, int xs)
86 #ifndef HAVE_SLANG
87 draw_box (h, y, x, ys, xs);
88 #else
89 SLsmg_draw_double_box (h->y+y, h->x+x, ys, xs);
90 #endif /* HAVE_SLANG */
93 void widget_erase (Widget *w)
95 int x, y;
97 for (y = 0; y < w->lines; y++){
98 widget_move (w, y, 0);
99 for (x = 0; x < w->cols; x++)
100 addch (' ');
104 void dlg_erase (Dlg_head *h)
106 int x, y;
108 for (y = 0; y < h->lines; y++){
109 move (y+h->y, h->x); /* FIXME: should test if ERR */
110 for (x = 0; x < h->cols; x++){
111 addch (' ');
116 void
117 init_widget (Widget *w, int y, int x, int lines, int cols,
118 callback_fn callback, mouse_h mouse_handler)
120 w->x = x;
121 w->y = y;
122 w->cols = cols;
123 w->lines = lines;
124 w->callback = callback;
125 w->mouse = mouse_handler;
126 w->parent = 0;
128 /* Almost all widgets want to put the cursor in a suitable place */
129 w->options = W_WANT_CURSOR;
132 /* Default callback for widgets */
133 cb_ret_t
134 default_proc (widget_msg_t msg, int parm)
136 switch (msg) {
137 case WIDGET_INIT:
138 case WIDGET_FOCUS:
139 case WIDGET_UNFOCUS:
140 case WIDGET_DRAW:
141 case WIDGET_DESTROY:
142 case WIDGET_CURSOR:
143 case WIDGET_IDLE:
144 return MSG_HANDLED;
146 default:
147 return MSG_NOT_HANDLED;
151 /* Clean the dialog area, draw the frame and the title */
152 void
153 common_dialog_repaint (struct Dlg_head *h)
155 int space;
157 space = (h->flags & DLG_COMPACT) ? 0 : 1;
159 attrset (NORMALC);
160 dlg_erase (h);
161 draw_box (h, space, space, h->lines - 2 * space, h->cols - 2 * space);
163 if (h->title) {
164 attrset (HOT_NORMALC);
165 dlg_move (h, space, (h->cols - strlen (h->title)) / 2);
166 addstr (h->title);
170 /* Default dialog callback */
171 cb_ret_t default_dlg_callback (Dlg_head *h, dlg_msg_t msg, int parm)
173 if (msg == DLG_DRAW && h->color) {
174 common_dialog_repaint (h);
175 return MSG_HANDLED;
177 if (msg == DLG_IDLE){
178 dlg_broadcast_msg_to (h, WIDGET_IDLE, 0, W_WANT_IDLE);
179 return MSG_HANDLED;
181 return MSG_NOT_HANDLED;
184 Dlg_head *
185 create_dlg (int y1, int x1, int lines, int cols, const int *color_set,
186 dlg_cb_fn callback, const char *help_ctx, const char *title,
187 int flags)
189 Dlg_head *new_d;
191 if (flags & DLG_CENTER) {
192 y1 = (LINES - lines) / 2;
193 x1 = (COLS - cols) / 2;
196 if ((flags & DLG_TRYUP) && (y1 > 3))
197 y1 -= 2;
199 new_d = g_new0 (Dlg_head, 1);
200 new_d->color = color_set;
201 new_d->help_ctx = const_cast(char *, help_ctx);
202 new_d->callback = callback ? callback : default_dlg_callback;
203 new_d->x = x1;
204 new_d->y = y1;
205 new_d->cols = cols;
206 new_d->lines = lines;
207 new_d->flags = flags;
209 /* Strip existing spaces, add one space before and after the title */
210 if (title) {
211 char *t;
212 t = g_strstrip (g_strdup (title));
213 new_d->title = g_strconcat (" ", t, " ", (char *) NULL);
214 g_free (t);
217 return (new_d);
220 void
221 set_idle_proc (Dlg_head *d, int enable)
223 if (enable)
224 d->flags |= DLG_WANT_IDLE;
225 else
226 d->flags &= ~DLG_WANT_IDLE;
230 * Insert widget to dialog before current widget. For dialogs populated
231 * from the bottom, make the widget current. Return widget number.
234 add_widget (Dlg_head *h, void *w)
236 Widget *widget = (Widget *) w;
238 /* Don't accept 0 widgets, and running dialogs */
239 if (!widget || h->running)
240 abort ();
242 widget->x += h->x;
243 widget->y += h->y;
244 widget->parent = h;
245 widget->dlg_id = h->count++;
247 if (h->current) {
248 widget->next = h->current;
249 widget->prev = h->current->prev;
250 h->current->prev->next = widget;
251 h->current->prev = widget;
252 } else {
253 widget->prev = widget;
254 widget->next = widget;
257 if ((h->flags & DLG_REVERSE) || !h->current)
258 h->current = widget;
260 return widget->dlg_id;
263 enum {
264 REFRESH_COVERS_PART, /* If the refresh fn convers only a part */
265 REFRESH_COVERS_ALL /* If the refresh fn convers all the screen */
268 static void
269 do_complete_refresh (Dlg_head *dlg)
271 if (!dlg->fullscreen && dlg->parent)
272 do_complete_refresh (dlg->parent);
274 dlg_redraw (dlg);
277 void
278 do_refresh (void)
280 if (!current_dlg)
281 return;
283 if (fast_refresh)
284 dlg_redraw (current_dlg);
285 else {
286 do_complete_refresh (current_dlg);
290 /* broadcast a message to all the widgets in a dialog that have
291 * the options set to flags.
293 static void
294 dlg_broadcast_msg_to (Dlg_head *h, widget_msg_t message, int reverse,
295 int flags)
297 Widget *p, *first, *wi;
299 if (!h->current)
300 return;
302 if (reverse)
303 first = p = h->current->prev;
304 else
305 first = p = h->current->next;
307 do {
308 wi = p;
309 if (reverse)
310 p = p->prev;
311 else
312 p = p->next;
313 send_message (wi, message, 0);
314 } while (first != p);
317 /* broadcast a message to all the widgets in a dialog */
318 void
319 dlg_broadcast_msg (Dlg_head *h, widget_msg_t message, int reverse)
321 dlg_broadcast_msg_to (h, message, reverse, ~0);
324 int dlg_focus (Dlg_head *h)
326 if (!h->current)
327 return 0;
329 if (send_message (h->current, WIDGET_FOCUS, 0)){
330 (*h->callback) (h, DLG_FOCUS, 0);
331 return 1;
333 return 0;
336 static int
337 dlg_unfocus (Dlg_head *h)
339 if (!h->current)
340 return 0;
342 if (send_message (h->current, WIDGET_UNFOCUS, 0)){
343 (*h->callback) (h, DLG_UNFOCUS, 0);
344 return 1;
346 return 0;
350 /* Return true if the windows overlap */
351 int dlg_overlap (Widget *a, Widget *b)
353 if ((b->x >= a->x + a->cols)
354 || (a->x >= b->x + b->cols)
355 || (b->y >= a->y + a->lines)
356 || (a->y >= b->y + b->lines))
357 return 0;
358 return 1;
362 /* Find the widget with the given callback in the dialog h */
363 Widget *
364 find_widget_type (Dlg_head *h, callback_fn callback)
366 Widget *w;
367 Widget *item;
368 int i;
370 if (!h)
371 return 0;
372 if (!h->current)
373 return 0;
375 w = 0;
376 for (i = 0, item = h->current; i < h->count; i++, item = item->next) {
377 if (item->callback == callback) {
378 w = item;
379 break;
382 return w;
385 /* Find the widget with the given dialog id in the dialog h and select it */
386 void
387 dlg_select_by_id (Dlg_head *h, int id)
389 Widget *w, *w_found;
391 if (!h->current)
392 return;
394 w = h->current;
395 w_found = NULL;
397 do {
398 if (w->dlg_id == id) {
399 w_found = w;
400 break;
402 w = w->next;
403 } while (w != h->current);
405 if (w_found)
406 dlg_select_widget(h, w_found);
410 /* What to do if the requested widget doesn't take focus */
411 typedef enum {
412 SELECT_NEXT, /* go the the next widget */
413 SELECT_PREV, /* go the the previous widget */
414 SELECT_EXACT /* use current widget */
415 } select_dir_t;
418 * Try to select another widget. If forward is set, follow tab order.
419 * Otherwise go to the previous widget.
421 static void
422 do_select_widget (Dlg_head *h, Widget *w, select_dir_t dir)
424 Widget *w0 = h->current;
426 if (!dlg_unfocus (h))
427 return;
429 h->current = w;
430 do {
431 if (dlg_focus (h))
432 break;
434 switch (dir) {
435 case SELECT_NEXT:
436 h->current = h->current->next;
437 break;
438 case SELECT_PREV:
439 h->current = h->current->prev;
440 break;
441 case SELECT_EXACT:
442 h->current = w0;
443 dlg_focus (h);
444 return;
446 } while (h->current != w);
448 if (dlg_overlap (w0, h->current)) {
449 send_message (h->current, WIDGET_DRAW, 0);
450 send_message (h->current, WIDGET_FOCUS, 0);
456 * Try to select widget in the dialog.
458 void
459 dlg_select_widget (Dlg_head *h, void *w)
461 do_select_widget (h, w, SELECT_NEXT);
465 /* Try to select previous widget in the tab order */
466 void
467 dlg_one_up (Dlg_head *h)
469 if (h->current)
470 do_select_widget (h, h->current->prev, SELECT_PREV);
474 /* Try to select next widget in the tab order */
475 void
476 dlg_one_down (Dlg_head *h)
478 if (h->current)
479 do_select_widget (h, h->current->next, SELECT_NEXT);
483 void update_cursor (Dlg_head *h)
485 if (!h->current)
486 return;
487 if (h->current->options & W_WANT_CURSOR)
488 send_message (h->current, WIDGET_CURSOR, 0);
489 else {
490 Widget *p = h->current;
492 do {
493 if (p->options & W_WANT_CURSOR)
494 if ((*p->callback)(p, WIDGET_CURSOR, 0)){
495 break;
497 p = p->next;
498 } while (h->current != p);
502 /* Redraw the widgets in reverse order, leaving the current widget
503 * as the last one
505 void dlg_redraw (Dlg_head *h)
507 (h->callback)(h, DLG_DRAW, 0);
509 dlg_broadcast_msg (h, WIDGET_DRAW, 1);
511 update_cursor (h);
514 void dlg_stop (Dlg_head *h)
516 h->running = 0;
519 static inline void dialog_handle_key (Dlg_head *h, int d_key)
521 switch (d_key){
522 case KEY_LEFT:
523 case KEY_UP:
524 dlg_one_up (h);
525 break;
527 case KEY_RIGHT:
528 case KEY_DOWN:
529 dlg_one_down (h);
530 break;
532 case KEY_F(1):
533 interactive_display (NULL, h->help_ctx);
534 do_refresh ();
535 break;
537 case XCTRL('z'):
538 suspend_cmd ();
539 /* Fall through */
541 case XCTRL('l'):
542 #ifndef HAVE_SLANG
543 /* Use this if the refreshes fail */
544 clr_scr ();
545 do_refresh ();
546 #else
547 touchwin (stdscr);
548 #endif /* HAVE_SLANG */
549 mc_refresh ();
550 doupdate ();
551 break;
553 case '\n':
554 case KEY_ENTER:
555 h->ret_value = B_ENTER;
556 h->running = 0;
557 break;
559 case ESC_CHAR:
560 case KEY_F (10):
561 case XCTRL ('c'):
562 case XCTRL ('g'):
563 h->ret_value = B_CANCEL;
564 dlg_stop (h);
565 break;
569 static int
570 dlg_try_hotkey (Dlg_head *h, int d_key)
572 Widget *hot_cur;
573 int handled, c;
575 if (!h->current)
576 return 0;
579 * Explanation: we don't send letter hotkeys to other widgets if
580 * the currently selected widget is an input line
583 if (h->current->options & W_IS_INPUT) {
584 if (d_key < 255 && isalpha (d_key))
585 return 0;
588 /* If it's an alt key, send the message */
589 c = d_key & ~ALT (0);
590 if (d_key & ALT (0) && c < 255 && isalpha (c))
591 d_key = tolower (c);
593 handled = 0;
594 if (h->current->options & W_WANT_HOTKEY)
595 handled = h->current->callback (h->current, WIDGET_HOTKEY, d_key);
597 /* If not used, send hotkey to other widgets */
598 if (handled)
599 return handled;
601 hot_cur = h->current;
603 /* send it to all widgets */
604 do {
605 if (hot_cur->options & W_WANT_HOTKEY)
606 handled |=
607 (*hot_cur->callback) (hot_cur, WIDGET_HOTKEY, d_key);
609 if (!handled)
610 hot_cur = hot_cur->next;
611 } while (h->current != hot_cur && !handled);
613 if (!handled)
614 return 0;
616 do_select_widget (h, hot_cur, SELECT_EXACT);
617 return handled;
620 static void
621 dlg_key_event (Dlg_head *h, int d_key)
623 int handled;
625 if (!h->current)
626 return;
628 /* TAB used to cycle */
629 if (!(h->flags & DLG_WANT_TAB)) {
630 if (d_key == '\t') {
631 dlg_one_down (h);
632 return;
633 } else if (d_key == KEY_BTAB) {
634 dlg_one_up (h);
635 return;
639 /* first can dlg_callback handle the key */
640 handled = (*h->callback) (h, DLG_KEY, d_key);
642 /* next try the hotkey */
643 if (!handled)
644 handled = dlg_try_hotkey (h, d_key);
646 if (handled)
647 (*h->callback) (h, DLG_HOTKEY_HANDLED, 0);
649 /* not used - then try widget_callback */
650 if (!handled)
651 handled = h->current->callback (h->current, WIDGET_KEY, d_key);
653 /* not used- try to use the unhandled case */
654 if (!handled)
655 handled = (*h->callback) (h, DLG_UNHANDLED_KEY, d_key);
657 if (!handled)
658 dialog_handle_key (h, d_key);
660 (*h->callback) (h, DLG_POST_KEY, d_key);
663 static inline int
664 dlg_mouse_event (Dlg_head * h, Gpm_Event * event)
666 Widget *item;
667 Widget *starting_widget = h->current;
668 Gpm_Event new_event;
669 int x = event->x;
670 int y = event->y;
672 item = starting_widget;
673 do {
674 Widget *widget = item;
676 item = item->next;
678 if (!((x > widget->x) && (x <= widget->x + widget->cols)
679 && (y > widget->y) && (y <= widget->y + widget->lines)))
680 continue;
682 new_event = *event;
683 new_event.x -= widget->x;
684 new_event.y -= widget->y;
686 if (!widget->mouse)
687 return MOU_NORMAL;
689 return (*widget->mouse) (&new_event, widget);
690 } while (item != starting_widget);
692 return MOU_NORMAL;
695 /* Run dialog routines */
697 /* Init the process */
698 void init_dlg (Dlg_head *h)
700 /* Initialize dialog manager and widgets */
701 (*h->callback) (h, DLG_INIT, 0);
702 dlg_broadcast_msg (h, WIDGET_INIT, 0);
704 if (h->x == 0 && h->y == 0 && h->cols == COLS && h->lines == LINES)
705 h->fullscreen = 1;
707 h->parent = current_dlg;
708 current_dlg = h;
710 /* Initialize the mouse status */
711 h->mouse_status = MOU_NORMAL;
713 /* Select the first widget that takes focus */
714 while (!dlg_focus (h) && h->current)
715 h->current = h->current->next;
717 /* Redraw the screen */
718 dlg_redraw (h);
720 h->ret_value = 0;
721 h->running = 1;
724 /* Shutdown the run_dlg */
725 void dlg_run_done (Dlg_head *h)
727 if (h->current)
728 (*h->callback) (h, DLG_END, 0);
730 current_dlg = h->parent;
733 void dlg_process_event (Dlg_head *h, int key, Gpm_Event *event)
735 if (key == EV_NONE){
736 if (got_interrupt ())
737 key = XCTRL('g');
738 else
739 return;
742 if (key == EV_MOUSE)
743 h->mouse_status = dlg_mouse_event (h, event);
744 else
745 dlg_key_event (h, key);
748 static inline void
749 frontend_run_dlg (Dlg_head *h)
751 int d_key;
752 Gpm_Event event;
754 event.x = -1;
755 while (h->running) {
756 if (winch_flag)
757 change_screen_size ();
759 if (is_idle ()) {
760 if (idle_hook)
761 execute_hooks (idle_hook);
763 while ((h->flags & DLG_WANT_IDLE) && is_idle ())
764 (*h->callback) (h, DLG_IDLE, 0);
766 /* Allow terminating the dialog from the idle handler */
767 if (!h->running)
768 break;
771 update_cursor (h);
773 /* Clear interrupt flag */
774 got_interrupt ();
775 d_key = get_event (&event, h->mouse_status == MOU_REPEAT, 1);
777 dlg_process_event (h, d_key, &event);
779 if (!h->running)
780 (*h->callback) (h, DLG_VALIDATE, 0);
784 /* Standard run dialog routine
785 * We have to keep this routine small so that we can duplicate it's
786 * behavior on complex routines like the file routines, this way,
787 * they can call the dlg_process_event without rewriting all the code
789 int run_dlg (Dlg_head *h)
791 init_dlg (h);
792 frontend_run_dlg (h);
793 dlg_run_done (h);
794 return h->ret_value;
797 void
798 destroy_dlg (Dlg_head *h)
800 int i;
801 Widget *c;
803 dlg_broadcast_msg (h, WIDGET_DESTROY, 0);
804 c = h->current;
805 for (i = 0; i < h->count; i++) {
806 c = c->next;
807 g_free (h->current);
808 h->current = c;
810 g_free (h->title);
811 g_free (h);
813 do_refresh ();
816 void widget_set_size (Widget *widget, int y, int x, int lines, int cols)
818 widget->x = x;
819 widget->y = y;
820 widget->cols = cols;
821 widget->lines = lines;
824 /* Replace widget old_w for widget new_w in the dialog h */
825 void
826 dlg_replace_widget (Dlg_head *h, Widget *old_w, Widget *new_w)
828 int should_focus = 0;
830 if (!h->current)
831 return;
833 if (old_w == h->current)
834 should_focus = 1;
836 new_w->parent = h;
837 new_w->dlg_id = old_w->dlg_id;
839 if (old_w == old_w->next) {
840 /* just one widget */
841 new_w->prev = new_w;
842 new_w->next = new_w;
843 } else {
844 new_w->prev = old_w->prev;
845 new_w->next = old_w->next;
846 old_w->prev->next = new_w;
847 old_w->next->prev = new_w;
850 if (should_focus)
851 h->current = new_w;
853 send_message (old_w, WIDGET_DESTROY, 0);
854 send_message (new_w, WIDGET_INIT, 0);
856 if (should_focus)
857 dlg_select_widget (h, new_w);
859 send_message (new_w, WIDGET_DRAW, 0);