Ticket #3723: fix positioning of skin selector dialog.
[midnight-commander.git] / lib / widget / dialog.c
blob480813ce2149105a258ef76028956bd3403ab177
1 /*
2 Dialog box features module for the Midnight Commander
4 Copyright (C) 1994-2014
5 Free Software Foundation, Inc.
7 This file is part of the Midnight Commander.
9 The Midnight Commander is free software: you can redistribute it
10 and/or modify it under the terms of the GNU General Public License as
11 published by the Free Software Foundation, either version 3 of the License,
12 or (at your option) any later version.
14 The Midnight Commander is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 /** \file dialog.c
24 * \brief Source: dialog box features module
27 #include <config.h>
29 #include <ctype.h>
30 #include <errno.h>
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
37 #include "lib/global.h"
39 #include "lib/tty/tty.h"
40 #include "lib/skin.h"
41 #include "lib/tty/key.h"
42 #include "lib/strutil.h"
43 #include "lib/fileloc.h" /* MC_HISTORY_FILE */
44 #include "lib/event.h" /* mc_event_raise() */
46 #include "lib/widget.h"
47 #include "lib/widget/mouse.h"
49 /*** global variables ****************************************************************************/
51 /* Color styles for normal and error dialogs */
52 dlg_colors_t dialog_colors;
53 dlg_colors_t alarm_colors;
54 dlg_colors_t listbox_colors;
56 /* Primitive way to check if the the current dialog is our dialog */
57 /* This is needed by async routines like load_prompt */
58 GList *top_dlg = NULL;
60 /* A hook list for idle events */
61 hook_t *idle_hook = NULL;
63 /* If set then dialogs just clean the screen when refreshing, else */
64 /* they do a complete refresh, refreshing all the parts of the program */
65 int fast_refresh = 0;
67 /* left click outside of dialog closes it */
68 int mouse_close_dialog = 0;
70 const global_keymap_t *dialog_map = NULL;
72 /*** file scope macro definitions ****************************************************************/
74 /*** file scope type declarations ****************************************************************/
76 /* Control widget positions in dialog */
77 typedef struct
79 int shift_x;
80 int scale_x;
81 int shift_y;
82 int scale_y;
83 } widget_shift_scale_t;
85 /*** file scope variables ************************************************************************/
87 /* --------------------------------------------------------------------------------------------- */
88 /*** file scope functions ************************************************************************/
89 /* --------------------------------------------------------------------------------------------- */
91 static GList *
92 dlg_get_next_or_prev_of (const GList * list, gboolean next)
94 GList *l = NULL;
96 if (list != NULL && list->data != NULL)
98 const WDialog *owner = CONST_WIDGET (list->data)->owner;
100 if (owner != NULL)
102 if (next)
104 l = g_list_next (list);
105 if (l == NULL)
106 l = owner->widgets;
108 else
110 l = g_list_previous (list);
111 if (l == NULL)
112 l = g_list_last (owner->widgets);
117 return l;
120 /* --------------------------------------------------------------------------------------------- */
122 static void
123 dlg_select_next_or_prev (WDialog * h, gboolean next)
125 if (h->widgets != NULL && h->current != NULL)
127 GList *l = h->current;
128 Widget *w;
132 l = dlg_get_next_or_prev_of (l, next);
133 w = WIDGET (l->data);
135 while ((widget_get_state (w, WST_DISABLED) || !widget_get_options (w, WOP_SELECTABLE))
136 && l != h->current);
138 widget_select (l->data);
142 /* --------------------------------------------------------------------------------------------- */
144 * broadcast a message to all the widgets in a dialog that have
145 * the options set to flags. If flags is zero, the message is sent
146 * to all widgets.
149 static void
150 dlg_broadcast_msg_to (WDialog * h, widget_msg_t msg, gboolean reverse, widget_options_t flags)
152 GList *p, *first;
154 if (h->widgets == NULL)
155 return;
157 if (h->current == NULL)
158 h->current = h->widgets;
160 p = dlg_get_next_or_prev_of (h->current, !reverse);
161 first = p;
165 Widget *w = WIDGET (p->data);
167 p = dlg_get_next_or_prev_of (p, !reverse);
169 if ((flags == 0) || ((flags & w->options) != 0))
170 send_message (w, NULL, msg, 0, NULL);
172 while (first != p);
175 /* --------------------------------------------------------------------------------------------- */
178 * Read histories from the ${XDG_CACHE_HOME}/mc/history file
180 static void
181 dlg_read_history (WDialog * h)
183 char *profile;
184 ev_history_load_save_t event_data;
186 if (num_history_items_recorded == 0) /* this is how to disable */
187 return;
189 profile = mc_config_get_full_path (MC_HISTORY_FILE);
190 event_data.cfg = mc_config_init (profile, TRUE);
191 event_data.receiver = NULL;
193 /* create all histories in dialog */
194 mc_event_raise (h->event_group, MCEVENT_HISTORY_LOAD, &event_data);
196 mc_config_deinit (event_data.cfg);
197 g_free (profile);
200 /* --------------------------------------------------------------------------------------------- */
202 static int
203 dlg_find_widget_callback (const void *a, const void *b)
205 const Widget *w = CONST_WIDGET (a);
206 const widget_cb_fn f = b;
208 return (w->callback == f) ? 0 : 1;
211 /* --------------------------------------------------------------------------------------------- */
213 static void
214 refresh_cmd (void)
216 #ifdef HAVE_SLANG
217 tty_touch_screen ();
218 mc_refresh ();
219 #else
220 /* Use this if the refreshes fail */
221 clr_scr ();
222 repaint_screen ();
223 #endif /* HAVE_SLANG */
226 /* --------------------------------------------------------------------------------------------- */
228 static cb_ret_t
229 dlg_execute_cmd (WDialog * h, long command)
231 cb_ret_t ret = MSG_HANDLED;
232 switch (command)
234 case CK_Ok:
235 h->ret_value = B_ENTER;
236 dlg_stop (h);
237 break;
238 case CK_Cancel:
239 h->ret_value = B_CANCEL;
240 dlg_stop (h);
241 break;
243 case CK_Up:
244 case CK_Left:
245 dlg_select_prev_widget (h);
246 break;
247 case CK_Down:
248 case CK_Right:
249 dlg_select_next_widget (h);
250 break;
252 case CK_Help:
254 ev_help_t event_data = { NULL, h->help_ctx };
255 mc_event_raise (MCEVENT_GROUP_CORE, "help", &event_data);
257 break;
259 case CK_Suspend:
260 mc_event_raise (MCEVENT_GROUP_CORE, "suspend", NULL);
261 refresh_cmd ();
262 break;
263 case CK_Refresh:
264 refresh_cmd ();
265 break;
267 case CK_ScreenList:
268 if (!widget_get_state (WIDGET (h), WST_MODAL))
269 dialog_switch_list ();
270 else
271 ret = MSG_NOT_HANDLED;
272 break;
273 case CK_ScreenNext:
274 if (!widget_get_state (WIDGET (h), WST_MODAL))
275 dialog_switch_next ();
276 else
277 ret = MSG_NOT_HANDLED;
278 break;
279 case CK_ScreenPrev:
280 if (!widget_get_state (WIDGET (h), WST_MODAL))
281 dialog_switch_prev ();
282 else
283 ret = MSG_NOT_HANDLED;
284 break;
286 default:
287 ret = MSG_NOT_HANDLED;
290 return ret;
293 /* --------------------------------------------------------------------------------------------- */
295 static cb_ret_t
296 dlg_handle_key (WDialog * h, int d_key)
298 long command;
300 command = keybind_lookup_keymap_command (dialog_map, d_key);
302 if (command == CK_IgnoreKey)
303 return MSG_NOT_HANDLED;
305 if (send_message (h, NULL, MSG_ACTION, command, NULL) == MSG_HANDLED
306 || dlg_execute_cmd (h, command) == MSG_HANDLED)
307 return MSG_HANDLED;
309 return MSG_NOT_HANDLED;
312 /* --------------------------------------------------------------------------------------------- */
314 * This is the low-level mouse handler.
315 * It receives a Gpm_Event event and translates it into a higher level protocol.
317 static int
318 dlg_mouse_translator (Gpm_Event * event, Widget * w)
320 mouse_event_t me;
322 me = mouse_translate_event (w, event);
324 return mouse_process_event (w, &me);
327 /* --------------------------------------------------------------------------------------------- */
329 static int
330 dlg_mouse_event (WDialog * h, Gpm_Event * event)
332 Widget *wh = WIDGET (h);
334 GList *p;
336 /* close the dialog by mouse left click out of dialog area */
337 if (mouse_close_dialog && (wh->pos_flags & WPOS_FULLSCREEN) == 0
338 && ((event->buttons & GPM_B_LEFT) != 0) && ((event->type & GPM_DOWN) != 0)
339 && !mouse_global_in_widget (event, wh))
341 h->ret_value = B_CANCEL;
342 dlg_stop (h);
343 return MOU_NORMAL;
346 if (wh->mouse_callback != NULL)
348 int mou;
350 mou = dlg_mouse_translator (event, wh);
351 if (mou != MOU_UNHANDLED)
352 return mou;
355 /* send the event to widgets in reverse Z-order */
356 p = g_list_last (h->widgets);
359 Widget *w = WIDGET (p->data);
361 if (!widget_get_state (w, WST_DISABLED) && w->mouse_callback != NULL)
363 /* put global cursor position to the widget */
364 int ret;
366 ret = dlg_mouse_translator (event, w);
367 if (ret != MOU_UNHANDLED)
368 return ret;
371 p = g_list_previous (p);
373 while (p != NULL);
375 return MOU_UNHANDLED;
378 /* --------------------------------------------------------------------------------------------- */
380 static cb_ret_t
381 dlg_try_hotkey (WDialog * h, int d_key)
383 GList *hot_cur;
384 Widget *current;
385 cb_ret_t handled;
386 int c;
388 if (h->widgets == NULL)
389 return MSG_NOT_HANDLED;
391 if (h->current == NULL)
392 h->current = h->widgets;
395 * Explanation: we don't send letter hotkeys to other widgets if
396 * the currently selected widget is an input line
399 current = WIDGET (h->current->data);
401 if (widget_get_state (current, WST_DISABLED))
402 return MSG_NOT_HANDLED;
404 if (widget_get_options (current, WOP_IS_INPUT))
406 /* skip ascii control characters, anything else can valid character in
407 * some encoding */
408 if (d_key >= 32 && d_key < 256)
409 return MSG_NOT_HANDLED;
412 /* If it's an alt key, send the message */
413 c = d_key & ~ALT (0);
414 if (d_key & ALT (0) && g_ascii_isalpha (c))
415 d_key = g_ascii_tolower (c);
417 handled = MSG_NOT_HANDLED;
418 if (widget_get_options (current, WOP_WANT_HOTKEY))
419 handled = send_message (current, NULL, MSG_HOTKEY, d_key, NULL);
421 /* If not used, send hotkey to other widgets */
422 if (handled == MSG_HANDLED)
423 return MSG_HANDLED;
425 hot_cur = dlg_get_widget_next_of (h->current);
427 /* send it to all widgets */
428 while (h->current != hot_cur && handled == MSG_NOT_HANDLED)
430 current = WIDGET (hot_cur->data);
432 if (widget_get_options (current, WOP_WANT_HOTKEY)
433 && !widget_get_state (current, WST_DISABLED))
434 handled = send_message (current, NULL, MSG_HOTKEY, d_key, NULL);
436 if (handled == MSG_NOT_HANDLED)
437 hot_cur = dlg_get_widget_next_of (hot_cur);
440 if (handled == MSG_HANDLED)
441 widget_select (WIDGET (hot_cur->data));
443 return handled;
446 /* --------------------------------------------------------------------------------------------- */
448 static void
449 dlg_key_event (WDialog * h, int d_key)
451 cb_ret_t handled;
453 if (h->widgets == NULL)
454 return;
456 if (h->current == NULL)
457 h->current = h->widgets;
459 /* TAB used to cycle */
460 if (!widget_get_options (WIDGET (h), WOP_WANT_TAB))
462 if (d_key == '\t')
464 dlg_select_next_widget (h);
465 return;
467 else if ((d_key & ~(KEY_M_SHIFT | KEY_M_CTRL)) == '\t')
469 dlg_select_prev_widget (h);
470 return;
474 /* first can dlg_callback handle the key */
475 handled = send_message (h, NULL, MSG_KEY, d_key, NULL);
477 /* next try the hotkey */
478 if (handled == MSG_NOT_HANDLED)
479 handled = dlg_try_hotkey (h, d_key);
481 if (handled == MSG_HANDLED)
482 send_message (h, NULL, MSG_HOTKEY_HANDLED, 0, NULL);
483 else
484 /* not used - then try widget_callback */
485 handled = send_message (h->current->data, NULL, MSG_KEY, d_key, NULL);
487 /* not used- try to use the unhandled case */
488 if (handled == MSG_NOT_HANDLED)
489 handled = send_message (h, NULL, MSG_UNHANDLED_KEY, d_key, NULL);
491 if (handled == MSG_NOT_HANDLED)
492 handled = dlg_handle_key (h, d_key);
494 (void) handled;
495 send_message (h, NULL, MSG_POST_KEY, d_key, NULL);
498 /* --------------------------------------------------------------------------------------------- */
500 static void
501 frontend_dlg_run (WDialog * h)
503 Widget *wh = WIDGET (h);
504 Gpm_Event event;
506 event.x = -1;
508 /* close opened editors, viewers, etc */
509 if (!widget_get_state (wh, WST_MODAL) && mc_global.midnight_shutdown)
511 send_message (h, NULL, MSG_VALIDATE, 0, NULL);
512 return;
515 while (widget_get_state (wh, WST_ACTIVE))
517 int d_key;
519 if (mc_global.tty.winch_flag != 0)
520 dialog_change_screen_size ();
522 if (is_idle ())
524 if (idle_hook)
525 execute_hooks (idle_hook);
527 while (widget_get_state (wh, WST_IDLE) && is_idle ())
528 send_message (wh, NULL, MSG_IDLE, 0, NULL);
530 /* Allow terminating the dialog from the idle handler */
531 if (!widget_get_state (wh, WST_ACTIVE))
532 break;
535 update_cursor (h);
537 /* Clear interrupt flag */
538 tty_got_interrupt ();
539 d_key = tty_get_event (&event, h->mouse_status == MOU_REPEAT, TRUE);
541 dlg_process_event (h, d_key, &event);
543 if (widget_get_state (wh, WST_CLOSED))
544 send_message (h, NULL, MSG_VALIDATE, 0, NULL);
548 /* --------------------------------------------------------------------------------------------- */
550 static int
551 dlg_find_widget_by_id (gconstpointer a, gconstpointer b)
553 const Widget *w = CONST_WIDGET (a);
554 unsigned long id = GPOINTER_TO_UINT (b);
556 return w->id == id ? 0 : 1;
559 /* --------------------------------------------------------------------------------------------- */
560 static void
561 dlg_widget_set_position (gpointer data, gpointer user_data)
563 /* there are, mainly, 2 generally possible situations:
564 * 1. control sticks to one side - it should be moved
565 * 2. control sticks to two sides of one direction - it should be sized
568 Widget *c = WIDGET (data);
569 Widget *wh = WIDGET (c->owner);
570 const widget_shift_scale_t *wss = (const widget_shift_scale_t *) user_data;
571 int x = c->x;
572 int y = c->y;
573 int cols = c->cols;
574 int lines = c->lines;
576 if ((c->pos_flags & WPOS_CENTER_HORZ) != 0)
577 x = wh->x + (wh->cols - c->cols) / 2;
578 else if ((c->pos_flags & WPOS_KEEP_LEFT) != 0 && (c->pos_flags & WPOS_KEEP_RIGHT) != 0)
580 x += wss->shift_x;
581 cols += wss->scale_x;
583 else if ((c->pos_flags & WPOS_KEEP_LEFT) != 0)
584 x += wss->shift_x;
585 else if ((c->pos_flags & WPOS_KEEP_RIGHT) != 0)
586 x += wss->shift_x + wss->scale_x;
588 if ((c->pos_flags & WPOS_CENTER_VERT) != 0)
589 y = wh->y + (wh->lines - c->lines) / 2;
590 else if ((c->pos_flags & WPOS_KEEP_TOP) != 0 && (c->pos_flags & WPOS_KEEP_BOTTOM) != 0)
592 y += wss->shift_y;
593 lines += wss->scale_y;
595 else if ((c->pos_flags & WPOS_KEEP_TOP) != 0)
596 y += wss->shift_y;
597 else if ((c->pos_flags & WPOS_KEEP_BOTTOM) != 0)
598 y += wss->shift_y + wss->scale_y;
600 widget_set_size (c, y, x, lines, cols);
603 /* --------------------------------------------------------------------------------------------- */
605 static void
606 dlg_adjust_position (widget_pos_flags_t pos_flags, int *y, int *x, int *lines, int *cols)
608 if ((pos_flags & WPOS_FULLSCREEN) != 0)
610 *y = 0;
611 *x = 0;
612 *lines = LINES;
613 *cols = COLS;
615 else
617 if ((pos_flags & WPOS_CENTER_HORZ) != 0)
618 *x = (COLS - *cols) / 2;
620 if ((pos_flags & WPOS_CENTER_VERT) != 0)
621 *y = (LINES - *lines) / 2;
623 if ((pos_flags & WPOS_TRYUP) != 0)
625 if (*y > 3)
626 *y -= 2;
627 else if (*y == 3)
628 *y = 2;
633 /* --------------------------------------------------------------------------------------------- */
634 /*** public functions ****************************************************************************/
635 /* --------------------------------------------------------------------------------------------- */
637 /** Clean the dialog area, draw the frame and the title */
638 void
639 dlg_default_repaint (WDialog * h)
641 Widget *wh = WIDGET (h);
643 int space;
645 if (!widget_get_state (wh, WST_ACTIVE))
646 return;
648 space = h->compact ? 0 : 1;
650 tty_setcolor (h->color[DLG_COLOR_NORMAL]);
651 dlg_erase (h);
652 tty_draw_box (wh->y + space, wh->x + space, wh->lines - 2 * space, wh->cols - 2 * space, FALSE);
654 if (h->title != NULL)
656 tty_setcolor (h->color[DLG_COLOR_TITLE]);
657 widget_move (h, space, (wh->cols - str_term_width1 (h->title)) / 2);
658 tty_print_string (h->title);
662 /* --------------------------------------------------------------------------------------------- */
663 /** this function allows to set dialog position */
665 void
666 dlg_set_position (WDialog * h, int y, int x, int lines, int cols)
668 Widget *wh = WIDGET (h);
669 widget_shift_scale_t wss;
671 /* save old positions, will be used to reposition childs */
672 int ox, oy, oc, ol;
674 /* save old positions, will be used to reposition childs */
675 ox = wh->x;
676 oy = wh->y;
677 oc = wh->cols;
678 ol = wh->lines;
680 wh->x = x;
681 wh->y = y;
682 wh->lines = lines;
683 wh->cols = cols;
685 /* dialog is empty */
686 if (h->widgets == NULL)
687 return;
689 if (h->current == NULL)
690 h->current = h->widgets;
692 /* values by which controls should be moved */
693 wss.shift_x = wh->x - ox;
694 wss.scale_x = wh->cols - oc;
695 wss.shift_y = wh->y - oy;
696 wss.scale_y = wh->lines - ol;
698 if (wss.shift_x != 0 || wss.shift_y != 0 || wss.scale_x != 0 || wss.scale_y != 0)
699 g_list_foreach (h->widgets, dlg_widget_set_position, &wss);
702 /* --------------------------------------------------------------------------------------------- */
703 /** Set dialog size and position */
705 void
706 dlg_set_size (WDialog * h, int lines, int cols)
708 int x = 0, y = 0;
710 dlg_adjust_position (WIDGET (h)->pos_flags, &y, &x, &lines, &cols);
711 dlg_set_position (h, y, x, lines, cols);
714 /* --------------------------------------------------------------------------------------------- */
715 /** Default dialog callback */
717 cb_ret_t
718 dlg_default_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
720 WDialog *h = DIALOG (w);
722 (void) sender;
723 (void) parm;
724 (void) data;
726 switch (msg)
728 case MSG_DRAW:
729 if (h->color != NULL)
731 dlg_default_repaint (h);
732 return MSG_HANDLED;
734 return MSG_NOT_HANDLED;
736 case MSG_IDLE:
737 /* we don't want endless loop */
738 widget_idle (w, FALSE);
739 return MSG_HANDLED;
741 case MSG_RESIZE:
742 /* this is default resizing mechanism */
743 /* the main idea of this code is to resize dialog
744 according to flags (if any of flags require automatic
745 resizing, like WPOS_CENTER, end after that reposition
746 controls in dialog according to flags of widget) */
747 dlg_set_size (h, w->lines, w->cols);
748 return MSG_HANDLED;
750 default:
751 break;
754 return MSG_NOT_HANDLED;
757 /* --------------------------------------------------------------------------------------------- */
759 WDialog *
760 dlg_create (gboolean modal, int y1, int x1, int lines, int cols, widget_pos_flags_t pos_flags,
761 gboolean compact, const int *colors, widget_cb_fn callback,
762 widget_mouse_cb_fn mouse_callback, const char *help_ctx, const char *title)
764 WDialog *new_d;
765 Widget *w;
767 new_d = g_new0 (WDialog, 1);
768 w = WIDGET (new_d);
769 dlg_adjust_position (pos_flags, &y1, &x1, &lines, &cols);
770 widget_init (w, y1, x1, lines, cols, (callback != NULL) ? callback : dlg_default_callback,
771 mouse_callback);
772 w->pos_flags = pos_flags;
773 w->options |= WOP_SELECTABLE | WOP_TOP_SELECT;
775 w->state |= WST_CONSTRUCT | WST_FOCUSED;
776 if (modal)
777 w->state |= WST_MODAL;
779 new_d->color = colors;
780 new_d->help_ctx = help_ctx;
781 new_d->compact = compact;
782 new_d->data = NULL;
784 new_d->mouse_status = MOU_UNHANDLED;
786 /* Strip existing spaces, add one space before and after the title */
787 if (title != NULL && *title != '\0')
789 char *t;
791 t = g_strstrip (g_strdup (title));
792 if (*t != '\0')
793 new_d->title = g_strdup_printf (" %s ", t);
794 g_free (t);
797 /* unique name of event group for this dialog */
798 new_d->event_group = g_strdup_printf ("%s_%p", MCEVENT_GROUP_DIALOG, (void *) new_d);
800 return new_d;
803 /* --------------------------------------------------------------------------------------------- */
805 void
806 dlg_set_default_colors (void)
808 dialog_colors[DLG_COLOR_NORMAL] = COLOR_NORMAL;
809 dialog_colors[DLG_COLOR_FOCUS] = COLOR_FOCUS;
810 dialog_colors[DLG_COLOR_HOT_NORMAL] = COLOR_HOT_NORMAL;
811 dialog_colors[DLG_COLOR_HOT_FOCUS] = COLOR_HOT_FOCUS;
812 dialog_colors[DLG_COLOR_TITLE] = COLOR_TITLE;
814 alarm_colors[DLG_COLOR_NORMAL] = ERROR_COLOR;
815 alarm_colors[DLG_COLOR_FOCUS] = ERROR_FOCUS;
816 alarm_colors[DLG_COLOR_HOT_NORMAL] = ERROR_HOT_NORMAL;
817 alarm_colors[DLG_COLOR_HOT_FOCUS] = ERROR_HOT_FOCUS;
818 alarm_colors[DLG_COLOR_TITLE] = ERROR_TITLE;
820 listbox_colors[DLG_COLOR_NORMAL] = PMENU_ENTRY_COLOR;
821 listbox_colors[DLG_COLOR_FOCUS] = PMENU_SELECTED_COLOR;
822 listbox_colors[DLG_COLOR_HOT_NORMAL] = PMENU_ENTRY_COLOR;
823 listbox_colors[DLG_COLOR_HOT_FOCUS] = PMENU_SELECTED_COLOR;
824 listbox_colors[DLG_COLOR_TITLE] = PMENU_TITLE_COLOR;
827 /* --------------------------------------------------------------------------------------------- */
829 void
830 dlg_erase (WDialog * h)
832 Widget *wh = WIDGET (h);
834 if (wh != NULL && widget_get_state (wh, WST_ACTIVE))
835 tty_fill_region (wh->y, wh->x, wh->lines, wh->cols, ' ');
838 /* --------------------------------------------------------------------------------------------- */
840 * Insert widget to dialog before requested widget. Make the widget current. Return widget ID.
843 unsigned long
844 add_widget_autopos (WDialog * h, void *w, widget_pos_flags_t pos_flags, const void *before)
846 Widget *wh = WIDGET (h);
847 Widget *widget;
849 /* Don't accept 0 widgets */
850 if (w == NULL)
851 abort ();
853 widget = WIDGET (w);
855 if ((pos_flags & WPOS_CENTER_HORZ) != 0)
856 widget->x = (wh->cols - widget->cols) / 2;
857 widget->x += wh->x;
859 if ((pos_flags & WPOS_CENTER_VERT) != 0)
860 widget->y = (wh->lines - widget->lines) / 2;
861 widget->y += wh->y;
863 widget->owner = h;
864 widget->pos_flags = pos_flags;
865 widget->id = h->widget_id++;
867 if (h->widgets == NULL || before == NULL)
869 h->widgets = g_list_append (h->widgets, widget);
870 h->current = g_list_last (h->widgets);
872 else
874 GList *b;
876 b = g_list_find (h->widgets, before);
878 /* don't accept widget not from dialog. This shouldn't happen */
879 if (b == NULL)
880 abort ();
882 b = g_list_next (b);
883 h->widgets = g_list_insert_before (h->widgets, b, widget);
884 if (b != NULL)
885 h->current = g_list_previous (b);
886 else
887 h->current = g_list_last (h->widgets);
890 /* widget has been added in runtime */
891 if (widget_get_state (wh, WST_ACTIVE))
893 send_message (widget, NULL, MSG_INIT, 0, NULL);
894 widget_set_state (widget, WST_FOCUSED, TRUE);
897 return widget->id;
900 /* --------------------------------------------------------------------------------------------- */
901 /** wrapper to simply add lefttop positioned controls */
903 unsigned long
904 add_widget (WDialog * h, void *w)
906 return add_widget_autopos (h, w, WPOS_KEEP_DEFAULT,
907 h->current != NULL ? h->current->data : NULL);
910 /* --------------------------------------------------------------------------------------------- */
912 unsigned long
913 add_widget_before (WDialog * h, void *w, void *before)
915 return add_widget_autopos (h, w, WPOS_KEEP_DEFAULT, before);
918 /* --------------------------------------------------------------------------------------------- */
920 /** delete widget from dialog */
921 void
922 del_widget (void *w)
924 WDialog *h;
925 GList *d;
927 /* Don't accept NULL widget. This shouldn't happen */
928 if (w == NULL)
929 abort ();
931 h = WIDGET (w)->owner;
933 d = g_list_find (h->widgets, w);
934 if (d == h->current)
935 dlg_set_current_widget_next (h);
937 h->widgets = g_list_remove_link (h->widgets, d);
938 if (h->widgets == NULL)
939 h->current = NULL;
940 send_message (d->data, NULL, MSG_DESTROY, 0, NULL);
941 g_free (d->data);
942 g_list_free_1 (d);
944 /* widget has been deleted in runtime */
945 if (widget_get_state (WIDGET (h), WST_ACTIVE))
947 dlg_redraw (h);
948 dlg_select_current_widget (h);
952 /* --------------------------------------------------------------------------------------------- */
954 void
955 do_refresh (void)
957 GList *d = top_dlg;
959 if (fast_refresh)
961 if ((d != NULL) && (d->data != NULL))
962 dlg_redraw (DIALOG (d->data));
964 else
966 /* Search first fullscreen dialog */
967 for (; d != NULL; d = g_list_next (d))
968 if (d->data != NULL && (WIDGET (d->data)->pos_flags & WPOS_FULLSCREEN) != 0)
969 break;
970 /* back to top dialog */
971 for (; d != NULL; d = g_list_previous (d))
972 if (d->data != NULL)
973 dlg_redraw (DIALOG (d->data));
977 /* --------------------------------------------------------------------------------------------- */
978 /** broadcast a message to all the widgets in a dialog */
980 void
981 dlg_broadcast_msg (WDialog * h, widget_msg_t msg)
983 dlg_broadcast_msg_to (h, msg, FALSE, 0);
986 /* --------------------------------------------------------------------------------------------- */
987 /** Find the widget with the given callback in the dialog h */
989 Widget *
990 find_widget_type (const WDialog * h, widget_cb_fn callback)
992 GList *w;
994 w = g_list_find_custom (h->widgets, (gconstpointer) callback, dlg_find_widget_callback);
996 return (w == NULL) ? NULL : WIDGET (w->data);
999 /* --------------------------------------------------------------------------------------------- */
1001 GList *
1002 dlg_find (const WDialog * h, const Widget * w)
1004 return (w->owner == NULL || w->owner != h) ? NULL : g_list_find (h->widgets, w);
1007 /* --------------------------------------------------------------------------------------------- */
1008 /** Find the widget with the given id */
1010 Widget *
1011 dlg_find_by_id (const WDialog * h, unsigned long id)
1013 GList *w;
1015 w = g_list_find_custom (h->widgets, GUINT_TO_POINTER (id), dlg_find_widget_by_id);
1016 return w != NULL ? WIDGET (w->data) : NULL;
1019 /* --------------------------------------------------------------------------------------------- */
1020 /** Find the widget with the given id in the dialog h and select it */
1022 void
1023 dlg_select_by_id (const WDialog * h, unsigned long id)
1025 Widget *w;
1027 w = dlg_find_by_id (h, id);
1028 if (w != NULL)
1029 widget_select (w);
1032 /* --------------------------------------------------------------------------------------------- */
1033 /** Try to select previous widget in the tab order */
1035 void
1036 dlg_select_prev_widget (WDialog * h)
1038 dlg_select_next_or_prev (h, FALSE);
1041 /* --------------------------------------------------------------------------------------------- */
1042 /** Try to select next widget in the tab order */
1044 void
1045 dlg_select_next_widget (WDialog * h)
1047 dlg_select_next_or_prev (h, TRUE);
1050 /* --------------------------------------------------------------------------------------------- */
1052 void
1053 update_cursor (WDialog * h)
1055 GList *p = h->current;
1057 if (p != NULL && widget_get_state (WIDGET (h), WST_ACTIVE))
1059 Widget *w;
1061 w = WIDGET (p->data);
1063 if (!widget_get_state (w, WST_DISABLED) && widget_get_options (w, WOP_WANT_CURSOR))
1064 send_message (w, NULL, MSG_CURSOR, 0, NULL);
1065 else
1068 p = dlg_get_widget_next_of (p);
1069 if (p == h->current)
1070 break;
1072 w = WIDGET (p->data);
1074 if (!widget_get_state (w, WST_DISABLED) && widget_get_options (w, WOP_WANT_CURSOR)
1075 && send_message (w, NULL, MSG_CURSOR, 0, NULL) == MSG_HANDLED)
1076 break;
1078 while (TRUE);
1082 /* --------------------------------------------------------------------------------------------- */
1084 * Redraw the widgets in reverse order, leaving the current widget
1085 * as the last one
1088 void
1089 dlg_redraw (WDialog * h)
1091 if (!widget_get_state (WIDGET (h), WST_ACTIVE))
1092 return;
1094 if (h->winch_pending)
1096 h->winch_pending = FALSE;
1097 send_message (h, NULL, MSG_RESIZE, 0, NULL);
1100 send_message (h, NULL, MSG_DRAW, 0, NULL);
1101 dlg_broadcast_msg (h, MSG_DRAW);
1102 update_cursor (h);
1105 /* --------------------------------------------------------------------------------------------- */
1107 void
1108 dlg_stop (WDialog * h)
1110 widget_set_state (WIDGET (h), WST_CLOSED, TRUE);
1113 /* --------------------------------------------------------------------------------------------- */
1114 /** Init the process */
1116 void
1117 dlg_init (WDialog * h)
1119 Widget *wh = WIDGET (h);
1121 if (top_dlg != NULL && widget_get_state (WIDGET (top_dlg->data), WST_MODAL))
1122 widget_set_state (wh, WST_MODAL, TRUE);
1124 /* add dialog to the stack */
1125 top_dlg = g_list_prepend (top_dlg, h);
1127 /* Initialize dialog manager and widgets */
1128 if (widget_get_state (wh, WST_CONSTRUCT))
1130 if (!widget_get_state (wh, WST_MODAL))
1131 dialog_switch_add (h);
1133 send_message (h, NULL, MSG_INIT, 0, NULL);
1134 dlg_broadcast_msg (h, MSG_INIT);
1135 dlg_read_history (h);
1138 /* Select the first widget that takes focus */
1139 while (h->current != NULL && !widget_get_options (WIDGET (h->current->data), WOP_SELECTABLE)
1140 && !widget_get_state (WIDGET (h->current->data), WST_DISABLED))
1141 dlg_set_current_widget_next (h);
1143 widget_set_state (wh, WST_ACTIVE, TRUE);
1144 dlg_redraw (h);
1145 /* focus found widget */
1146 widget_set_state (WIDGET (h->current->data), WST_FOCUSED, TRUE);
1148 h->ret_value = 0;
1151 /* --------------------------------------------------------------------------------------------- */
1153 void
1154 dlg_process_event (WDialog * h, int key, Gpm_Event * event)
1156 if (key == EV_NONE)
1158 if (tty_got_interrupt ())
1159 if (send_message (h, NULL, MSG_ACTION, CK_Cancel, NULL) != MSG_HANDLED)
1160 dlg_execute_cmd (h, CK_Cancel);
1162 else if (key == EV_MOUSE)
1163 h->mouse_status = dlg_mouse_event (h, event);
1164 else
1165 dlg_key_event (h, key);
1168 /* --------------------------------------------------------------------------------------------- */
1169 /** Shutdown the dlg_run */
1171 void
1172 dlg_run_done (WDialog * h)
1174 top_dlg = g_list_remove (top_dlg, h);
1176 if (widget_get_state (WIDGET (h), WST_CLOSED))
1178 send_message (h, h->current->data, MSG_END, 0, NULL);
1179 if (!widget_get_state (WIDGET (h), WST_MODAL))
1180 dialog_switch_remove (h);
1184 /* --------------------------------------------------------------------------------------------- */
1186 * Standard run dialog routine
1187 * We have to keep this routine small so that we can duplicate it's
1188 * behavior on complex routines like the file routines, this way,
1189 * they can call the dlg_process_event without rewriting all the code
1193 dlg_run (WDialog * h)
1195 dlg_init (h);
1196 frontend_dlg_run (h);
1197 dlg_run_done (h);
1198 return h->ret_value;
1201 /* --------------------------------------------------------------------------------------------- */
1203 void
1204 dlg_destroy (WDialog * h)
1206 /* if some widgets have history, save all history at one moment here */
1207 dlg_save_history (h);
1208 dlg_broadcast_msg (h, MSG_DESTROY);
1209 g_list_free_full (h->widgets, g_free);
1210 mc_event_group_del (h->event_group);
1211 g_free (h->event_group);
1212 g_free (h->title);
1213 g_free (h);
1215 do_refresh ();
1218 /* --------------------------------------------------------------------------------------------- */
1221 * Write history to the ${XDG_CACHE_HOME}/mc/history file
1223 void
1224 dlg_save_history (WDialog * h)
1226 char *profile;
1227 int i;
1229 if (num_history_items_recorded == 0) /* this is how to disable */
1230 return;
1232 profile = mc_config_get_full_path (MC_HISTORY_FILE);
1233 i = open (profile, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
1234 if (i != -1)
1235 close (i);
1237 /* Make sure the history is only readable by the user */
1238 if (chmod (profile, S_IRUSR | S_IWUSR) != -1 || errno == ENOENT)
1240 ev_history_load_save_t event_data;
1242 event_data.cfg = mc_config_init (profile, FALSE);
1243 event_data.receiver = NULL;
1245 /* get all histories in dialog */
1246 mc_event_raise (h->event_group, MCEVENT_HISTORY_SAVE, &event_data);
1248 mc_config_save_file (event_data.cfg, NULL);
1249 mc_config_deinit (event_data.cfg);
1252 g_free (profile);
1255 /* --------------------------------------------------------------------------------------------- */
1257 char *
1258 dlg_get_title (const WDialog * h, size_t len)
1260 char *t;
1262 if (h == NULL)
1263 abort ();
1265 if (h->get_title != NULL)
1266 t = h->get_title (h, len);
1267 else
1268 t = g_strdup ("");
1270 return t;
1273 /* --------------------------------------------------------------------------------------------- */
1276 * Switch current widget to widget after current in dialog.
1278 * @param h WDialog widget
1281 void
1282 dlg_set_current_widget_next (WDialog * h)
1284 h->current = dlg_get_next_or_prev_of (h->current, TRUE);
1287 /* --------------------------------------------------------------------------------------------- */
1290 * Switch current widget to widget before current in dialog.
1292 * @param h WDialog widget
1295 void
1296 dlg_set_current_widget_prev (WDialog * h)
1298 h->current = dlg_get_next_or_prev_of (h->current, FALSE);
1301 /* --------------------------------------------------------------------------------------------- */
1304 * Get widget that is after specified widget in dialog.
1306 * @param w widget holder
1308 * @return widget that is after "w" or NULL if "w" is NULL or widget doesn't have owner
1311 GList *
1312 dlg_get_widget_next_of (GList * w)
1314 return dlg_get_next_or_prev_of (w, TRUE);
1317 /* --------------------------------------------------------------------------------------------- */
1320 * Get widget that is before specified widget in dialog.
1322 * @param w widget holder
1324 * @return widget that is before "w" or NULL if "w" is NULL or widget doesn't have owner
1327 GList *
1328 dlg_get_widget_prev_of (GList * w)
1330 return dlg_get_next_or_prev_of (w, FALSE);
1333 /* --------------------------------------------------------------------------------------------- */