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.
32 #include "key.h" /* For mi_getch() */
34 #include "dialog.h" /* For push_refresh() and pop_refresh() */
38 #define waddc(w,y1,x1,c) move (w->y+y1, w->x+x1); addch (c)
40 /* Primitive way to check if the the current dialog is our dialog */
41 /* This is needed by async routines like load_prompt */
42 Dlg_head
*current_dlg
= 0;
44 /* A hook list for idle events */
47 static void dlg_broadcast_msg_to (Dlg_head
*h
, int message
, int reverse
, int flags
);
49 static void slow_box (Dlg_head
*h
, int y
, int x
, int ys
, int xs
)
51 move (h
->y
+y
, h
->x
+x
);
54 move (h
->y
+y
, h
->x
+x
+xs
-1);
56 move (h
->y
+y
+ys
-1, h
->x
+x
);
60 /* draw box in window */
61 void draw_box (Dlg_head
*h
, int y
, int x
, int ys
, int xs
)
63 extern int slow_terminal
;
66 slow_box (h
, y
, x
, ys
, xs
);
71 waddc (h
, y
, x
, ACS_ULCORNER
);
72 hline (ACS_HLINE
, xs
- 2);
73 waddc (h
, y
+ ys
- 1, x
, ACS_LLCORNER
);
74 hline (ACS_HLINE
, xs
- 2);
76 waddc (h
, y
, x
+ xs
- 1, ACS_URCORNER
);
77 waddc (h
, y
+ ys
- 1, x
+ xs
- 1, ACS_LRCORNER
);
79 move (h
->y
+y
+1, h
->x
+x
);
80 vline (ACS_VLINE
, ys
- 2);
81 move (h
->y
+y
+1, h
->x
+x
+xs
-1);
82 vline (ACS_VLINE
, ys
- 2);
84 SLsmg_draw_box (h
->y
+y
, h
->x
+x
, ys
, xs
);
85 #endif /* HAVE_SLANG */
88 /* draw box in window */
89 void draw_double_box (Dlg_head
*h
, int y
, int x
, int ys
, int xs
)
92 draw_box (h
, y
, x
, ys
, xs
);
94 SLsmg_draw_double_box (h
->y
+y
, h
->x
+x
, ys
, xs
);
95 #endif /* HAVE_SLANG */
98 void widget_erase (Widget
*w
)
102 for (y
= 0; y
< w
->lines
; y
++){
103 widget_move (w
, y
, 0);
104 for (x
= 0; x
< w
->cols
; x
++)
109 void dlg_erase (Dlg_head
*h
)
113 for (y
= 0; y
< h
->lines
; y
++){
114 move (y
+h
->y
, h
->x
); /* FIXME: should test if ERR */
115 for (x
= 0; x
< h
->cols
; x
++){
121 void init_widget (Widget
*w
, int y
, int x
, int lines
, int cols
,
122 int (*callback
)(Dlg_head
*, void *, int, int),
123 destroy_fn destroy
, mouse_h mouse_handler
, char *tkname
)
129 w
->callback
= callback
;
130 w
->destroy
= destroy
;
131 w
->mouse
= mouse_handler
;
135 if (tkname
&& *tkname
== 0){
136 fprintf (stderr
, "Got a null string for the tkname\n");
139 /* Almost all widgets want to put the cursor in a suitable place */
140 w
->options
= W_WANT_CURSOR
;
143 /* Default callback for widgets */
144 int default_proc (Dlg_head
*h
, int Msg
, int Par
)
148 case WIDGET_HOTKEY
: /* Didn't use the key */
151 case WIDGET_INIT
: /* We could tell if something went wrong */
155 return 0; /* Didn't use the key */
157 case WIDGET_FOCUS
: /* We accept FOCUSes */
160 case WIDGET_UNFOCUS
: /* We accept loose FOCUSes */
170 /* Move the cursor to the default widget position */
176 printf ("Internal error: unhandled message: %d\n", Msg
);
180 /* Clean the dialog area, draw the frame and the title */
182 common_dialog_repaint (struct Dlg_head
*h
)
186 space
= (h
->flags
& DLG_COMPACT
) ? 0 : 1;
190 draw_box (h
, space
, space
, h
->lines
- 2 * space
, h
->cols
- 2 * space
);
193 attrset (HOT_NORMALC
);
194 dlg_move (h
, space
, (h
->cols
- strlen (h
->title
)) / 2);
199 /* Default dialog callback */
200 int default_dlg_callback (Dlg_head
*h
, int id
, int msg
)
202 if (msg
== DLG_DRAW
&& h
->color
) {
203 common_dialog_repaint (h
);
206 if (msg
== DLG_IDLE
){
207 dlg_broadcast_msg_to (h
, WIDGET_IDLE
, 0, W_WANT_IDLE
);
210 return MSG_NOT_HANDLED
;
213 Dlg_head
*create_dlg (int y1
, int x1
, int lines
, int cols
,
214 const int *color_set
,
215 int (*callback
) (struct Dlg_head
*, int, int),
216 char *help_ctx
, const char *title
, int flags
)
220 if (flags
& DLG_CENTER
){
221 y1
= (LINES
-lines
)/2;
225 if ((flags
& DLG_TRYUP
) && (y1
> 3))
228 new_d
= g_new0 (Dlg_head
, 1);
229 new_d
->color
= color_set
;
230 new_d
->help_ctx
= help_ctx
;
231 new_d
->callback
= callback
? callback
: default_dlg_callback
;
235 new_d
->lines
= lines
;
236 new_d
->flags
= flags
;
238 /* Strip existing spaces, add one space before and after the title */
241 t
= g_strstrip (g_strdup (title
));
242 new_d
->title
= g_strconcat (" ", t
, " ", NULL
);
249 void set_idle_proc (Dlg_head
*d
, int state
)
251 d
->send_idle_msg
= state
;
254 /* add component to dialog buffer */
255 int add_widget (Dlg_head
*where
, void *what
)
258 Widget
*widget
= (Widget
*) what
;
260 /* Don't accept 0 widgets, this could be from widgets that could not */
261 /* initialize properly */
265 widget
->x
+= where
->x
;
266 widget
->y
+= where
->y
;
269 Widget_Item
*point
= where
->current
;
271 where
->current
= g_new (Widget_Item
, 1);
274 where
->current
->next
= point
->next
;
275 where
->current
->prev
= point
;
276 point
->next
->prev
= where
->current
;
277 point
->next
= where
->current
;
279 where
->current
->next
= where
->current
;
280 where
->first
= where
->current
;
281 where
->current
->prev
= where
->first
;
282 where
->last
= where
->current
;
283 where
->first
->next
= where
->last
;
286 back
= where
->current
;
287 where
->current
= g_new (Widget_Item
, 1);
289 back
->prev
= where
->current
;
290 where
->current
->next
= back
;
292 where
->current
->next
= where
->current
;
293 where
->first
= where
->current
;
296 where
->current
->prev
= where
->first
;
297 where
->last
= where
->current
;
298 where
->first
->next
= where
->last
;
301 where
->current
->dlg_id
= where
->count
;
302 where
->current
->widget
= what
;
303 where
->current
->widget
->parent
= where
;
307 /* If the widget is inserted in a running dialog */
309 send_message (where
, widget
, WIDGET_INIT
, 0);
310 send_message (where
, widget
, WIDGET_DRAW
, 0);
312 return (where
->count
- 1);
315 int send_message (Dlg_head
*h
, Widget
*w
, int msg
, int par
)
317 return (*(w
->callback
))(h
, w
, msg
, par
);
320 /* broadcast a message to all the widgets in a dialog that have
321 * the options set to flags.
324 dlg_broadcast_msg_to (Dlg_head
*h
, int message
, int reverse
, int flags
)
326 Widget_Item
*p
, *first
, *wi
;
332 first
= p
= h
->current
->prev
;
334 first
= p
= h
->current
->next
;
342 send_message (h
, wi
->widget
, message
, 0);
343 } while (first
!= p
);
346 /* broadcast a message to all the widgets in a dialog */
347 void dlg_broadcast_msg (Dlg_head
*h
, int message
, int reverse
)
349 dlg_broadcast_msg_to (h
, message
, reverse
, ~0);
352 int dlg_focus (Dlg_head
*h
)
357 if (send_message (h
, h
->current
->widget
, WIDGET_FOCUS
, 0)){
358 (*h
->callback
) (h
, h
->current
->dlg_id
, DLG_FOCUS
);
365 dlg_unfocus (Dlg_head
*h
)
370 if (send_message (h
, h
->current
->widget
, WIDGET_UNFOCUS
, 0)){
371 (*h
->callback
) (h
, h
->current
->dlg_id
, DLG_UNFOCUS
);
377 static void select_a_widget (Dlg_head
*h
, int down
)
379 int dir_forward
= !(h
->flags
& DLG_BACKWARD
);
385 dir_forward
= !dir_forward
;
389 h
->current
= h
->current
->next
;
391 h
->current
= h
->current
->prev
;
393 (*h
->callback
) (h
, h
->current
->dlg_id
, DLG_ONE_DOWN
);
394 } while (!dlg_focus (h
));
397 /* Return true if the windows overlap */
398 int dlg_overlap (Widget
*a
, Widget
*b
)
400 if ((b
->x
>= a
->x
+ a
->cols
)
401 || (a
->x
>= b
->x
+ b
->cols
)
402 || (b
->y
>= a
->y
+ a
->lines
)
403 || (a
->y
>= b
->y
+ b
->lines
))
409 /* Searches a widget, uses the callback as a signature in the dialog h */
410 Widget
*find_widget_type (Dlg_head
*h
, callback_fn signature
)
422 for (i
= 0, item
= h
->current
; i
< h
->count
; i
++, item
= item
->next
){
423 if (item
->widget
->callback
== signature
){
431 void dlg_one_up (Dlg_head
*h
)
440 /* If it accepts unFOCUSion */
444 select_a_widget (h
, 0);
445 if (dlg_overlap (old
->widget
, h
->current
->widget
)){
446 send_message (h
, h
->current
->widget
, WIDGET_DRAW
, 0);
447 send_message (h
, h
->current
->widget
, WIDGET_FOCUS
, 0);
451 void dlg_one_down (Dlg_head
*h
)
459 if (!dlg_unfocus (h
))
462 select_a_widget (h
, 1);
463 if (dlg_overlap (old
->widget
, h
->current
->widget
)){
464 send_message (h
, h
->current
->widget
, WIDGET_DRAW
, 0);
465 send_message (h
, h
->current
->widget
, WIDGET_FOCUS
, 0);
469 int dlg_select_widget (Dlg_head
*h
, void *w
)
474 if (dlg_unfocus (h
)){
475 while (h
->current
->widget
!= w
)
476 h
->current
= h
->current
->next
;
477 while (!dlg_focus (h
))
478 h
->current
= h
->current
->next
;
485 int send_message_to (Dlg_head
*h
, Widget
*w
, int msg
, int par
)
487 Widget_Item
*p
= h
->current
;
494 for (i
= 0; i
< h
->count
; i
++){
495 if (w
== (void *) p
->widget
){
496 v
= send_message (h
, p
->widget
, msg
, par
);
504 #define callback(h) (h->current->widget->callback)
506 void update_cursor (Dlg_head
*h
)
510 if (h
->current
->widget
->options
& W_WANT_CURSOR
)
511 send_message (h
, h
->current
->widget
, WIDGET_CURSOR
, 0);
513 Widget_Item
*p
= h
->current
;
516 if (p
->widget
->options
& W_WANT_CURSOR
)
517 if ((*p
->widget
->callback
)(h
, p
->widget
, WIDGET_CURSOR
, 0)){
521 } while (h
->current
!= p
);
525 /* Redraw the widgets in reverse order, leaving the current widget
528 void dlg_redraw (Dlg_head
*h
)
530 (h
->callback
)(h
, 0, DLG_DRAW
);
532 dlg_broadcast_msg (h
, WIDGET_DRAW
, 1);
538 dlg_refresh (void *parameter
)
540 dlg_redraw ((Dlg_head
*) parameter
);
543 void dlg_stop (Dlg_head
*h
)
548 static inline void dialog_handle_key (Dlg_head
*h
, int d_key
)
562 interactive_display (NULL
, h
->help_ctx
);
572 /* Use this if the refreshes fail */
577 #endif /* HAVE_SLANG */
584 h
->ret_value
= B_ENTER
;
592 h
->ret_value
= B_CANCEL
;
598 static int dlg_try_hotkey (Dlg_head
*h
, int d_key
)
600 Widget_Item
*hot_cur
;
601 Widget_Item
*previous
;
608 * Explanation: we don't send letter hotkeys to other widgets if
609 * the currently selected widget is an input line
612 if (h
->current
->widget
->options
& W_IS_INPUT
){
613 if(d_key
< 255 && isalpha(d_key
))
617 /* If it's an alt key, send the message */
619 if (d_key
& ALT(0) && c
< 255 && isalpha(c
))
623 /* .ado: fix problem with file_permission under Win95 */
624 if (d_key
== 0) return 0;
628 if (h
->current
->widget
->options
& W_WANT_HOTKEY
)
629 handled
= callback (h
) (h
, h
->current
->widget
, WIDGET_HOTKEY
, d_key
);
631 /* If not used, send hotkey to other widgets */
635 hot_cur
= h
->current
;
637 /* send it to all widgets */
639 if (hot_cur
->widget
->options
& W_WANT_HOTKEY
)
640 handled
|= (*hot_cur
->widget
->callback
)
641 (h
, hot_cur
->widget
, WIDGET_HOTKEY
, d_key
);
644 hot_cur
= hot_cur
->next
;
645 } while (h
->current
!= hot_cur
&& !handled
);
650 (*h
->callback
) (h
, 0, DLG_HOTKEY_HANDLED
);
651 previous
= h
->current
;
652 if (!dlg_unfocus (h
))
655 h
->current
= hot_cur
;
657 h
->current
= previous
;
664 dlg_key_event (Dlg_head
* h
, int d_key
)
671 /* TAB used to cycle */
672 if (!(h
->flags
& DLG_WANT_TAB
)
673 && (d_key
== '\t' || d_key
== KEY_BTAB
)) {
680 /* first can dlg_callback handle the key */
681 handled
= (*h
->callback
) (h
, d_key
, DLG_KEY
);
683 /* next try the hotkey */
685 handled
= dlg_try_hotkey (h
, d_key
);
687 /* not used - then try widget_callback */
690 callback (h
) (h
, h
->current
->widget
, WIDGET_KEY
, d_key
);
692 /* not used- try to use the unhandled case */
694 handled
|= (*h
->callback
) (h
, d_key
, DLG_UNHANDLED_KEY
);
697 dialog_handle_key (h
, d_key
);
698 (*h
->callback
) (h
, d_key
, DLG_POST_KEY
);
706 dlg_mouse_event (Dlg_head
* h
, Gpm_Event
* event
)
709 Widget_Item
*starting_widget
= h
->current
;
714 /* kludge for the menubar: start at h->first, not current */
715 /* Must be careful in the insertion order to the dlg list */
716 if (y
== 1 && (h
->flags
& DLG_HAS_MENUBAR
))
717 starting_widget
= h
->first
;
719 item
= starting_widget
;
721 Widget
*widget
= item
->widget
;
725 if (!((x
> widget
->x
) && (x
<= widget
->x
+ widget
->cols
)
726 && (y
> widget
->y
) && (y
<= widget
->y
+ widget
->lines
)))
730 new_event
.x
-= widget
->x
;
731 new_event
.y
-= widget
->y
;
736 return (*widget
->mouse
) (&new_event
, widget
);
737 } while (item
!= starting_widget
);
742 /* Run dialog routines */
744 /* Init the process */
745 void init_dlg (Dlg_head
*h
)
749 /* Initialize dialog manager and widgets */
750 (*h
->callback
) (h
, 0, DLG_INIT
);
751 dlg_broadcast_msg (h
, WIDGET_INIT
, 0);
753 if (h
->x
== 0 && h
->y
== 0 && h
->cols
== COLS
&& h
->lines
== LINES
)
754 refresh_mode
= REFRESH_COVERS_ALL
;
756 refresh_mode
= REFRESH_COVERS_PART
;
758 push_refresh (dlg_refresh
, h
, refresh_mode
);
759 h
->refresh_pushed
= 1;
761 /* Initialize direction */
762 if (h
->flags
& DLG_BACKWARD
)
763 h
->current
= h
->first
;
765 if (h
->initfocus
!= NULL
)
766 h
->current
= h
->initfocus
;
768 h
->previous_dialog
= current_dlg
;
771 /* Initialize the mouse status */
772 h
->mouse_status
= MOU_NORMAL
;
774 /* Redraw the screen */
777 while (!dlg_focus (h
) && h
->current
)
778 h
->current
= h
->current
->next
;
784 /* Shutdown the run_dlg */
785 void dlg_run_done (Dlg_head
*h
)
788 (*h
->callback
) (h
, h
->current
->dlg_id
, DLG_END
);
790 current_dlg
= (Dlg_head
*) h
->previous_dialog
;
793 void dlg_process_event (Dlg_head
*h
, int key
, Gpm_Event
*event
)
796 if (got_interrupt ())
803 h
->mouse_status
= dlg_mouse_event (h
, event
);
805 dlg_key_event (h
, key
);
809 frontend_run_dlg (Dlg_head
*h
)
816 #if defined(HAVE_SLANG) || NCURSES_VERSION_MAJOR >= 4
817 /* It does not work with ncurses before 1.9.9g, it will break */
819 change_screen_size ();
823 execute_hooks (idle_hook
);
825 while (h
->send_idle_msg
&& is_idle ()){
826 (*h
->callback
) (h
, 0, DLG_IDLE
);
831 (*h
->callback
)(h
, 0, DLG_PRE_EVENT
);
833 /* Clear interrupt flag */
835 d_key
= get_event (&event
, h
->mouse_status
== MOU_REPEAT
, 1);
837 dlg_process_event (h
, d_key
, &event
);
841 /* Standard run dialog routine
842 * We have to keep this routine small so that we can duplicate it's
843 * behavior on complex routines like the file routines, this way,
844 * they can call the dlg_process_event without rewriting all the code
846 int run_dlg (Dlg_head
*h
)
849 frontend_run_dlg (h
);
855 destroy_dlg (Dlg_head
*h
)
860 if (h
->refresh_pushed
)
863 dlg_broadcast_msg (h
, WIDGET_DESTROY
, 0);
865 for (i
= 0; i
< h
->count
; i
++){
866 if (c
->widget
->destroy
)
867 c
->widget
->destroy (c
->widget
);
870 g_free (h
->current
->widget
);
882 void widget_set_size (Widget
*widget
, int y
, int x
, int lines
, int cols
)
887 widget
->lines
= lines
;
890 /* Replace widget old for widget new in the h dialog */
891 void dlg_replace_widget (Dlg_head
*h
, Widget
*old
, Widget
*new)
893 Widget_Item
*p
= h
->current
;
894 int should_focus
= 0;
900 if (p
->widget
== old
){
902 if (old
== h
->current
->widget
)
905 /* We found the widget */
906 /* First kill the widget */
908 send_message_to (h
, old
, WIDGET_DESTROY
, 0);
909 (*old
->destroy
) (old
);
911 /* We insert the new widget */
913 send_message_to (h
, new, WIDGET_INIT
, 0);
915 if (dlg_focus (h
) == 0)
916 select_a_widget (h
, 1);
918 send_message_to (h
, new, WIDGET_DRAW
, 0);
922 } while (p
!= h
->current
);
925 void widget_redraw (Dlg_head
*h
, Widget_Item
*w
)
927 Widget_Item
*save
= h
->current
;
933 (*w
->widget
->callback
)(h
, h
->current
->widget
, WIDGET_DRAW
, 0);
937 /* Returns the index of h->current from h->first */
938 int dlg_item_number (Dlg_head
*h
)
950 } while (p
!= h
->first
);
951 fprintf (stderr
, "Internal error: current not in dialog list\n\r");
955 int dlg_select_nth_widget (Dlg_head
*h
, int n
)
961 for (i
= 0; i
< n
; i
++)
964 return dlg_select_widget (h
, w
->widget
);