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.
33 #include "key.h" /* For mi_getch() */
35 #include "dialog.h" /* For push_refresh() and pop_refresh() */
39 /* This is the current frame, used to group Tk packings */
42 #define waddc(w,y1,x1,c) move (w->y+y1, w->x+x1); addch (c)
44 /* Primitive way to check if the the current dialog is our dialog */
45 /* This is needed by async routines like load_prompt */
46 Dlg_head
*current_dlg
= 0;
48 /* A hook list for idle events */
52 # define x_set_idle(d,x)
53 # define x_dialog_stop(d)
57 void widget_erase (Widget
*w
)
61 void dlg_erase (Dlg_head
*h
)
65 static void slow_box (Dlg_head
*h
, int y
, int x
, int ys
, int xs
)
67 move (h
->y
+y
, h
->x
+x
);
70 move (h
->y
+y
, h
->x
+x
+xs
-1);
72 move (h
->y
+y
+ys
-1, h
->x
+x
);
76 /* draw box in window */
77 void draw_box (Dlg_head
*h
, int y
, int x
, int ys
, int xs
)
79 extern int slow_terminal
;
82 slow_box (h
, y
, x
, ys
, xs
);
87 waddc (h
, y
, x
, ACS_ULCORNER
);
88 hline (ACS_HLINE
, xs
- 2);
89 waddc (h
, y
+ ys
- 1, x
, ACS_LLCORNER
);
90 hline (ACS_HLINE
, xs
- 2);
92 waddc (h
, y
, x
+ xs
- 1, ACS_URCORNER
);
93 waddc (h
, y
+ ys
- 1, x
+ xs
- 1, ACS_LRCORNER
);
95 move (h
->y
+y
+1, h
->x
+x
);
96 vline (ACS_VLINE
, ys
- 2);
97 move (h
->y
+y
+1, h
->x
+x
+xs
-1);
98 vline (ACS_VLINE
, ys
- 2);
100 SLsmg_draw_box (h
->y
+y
, h
->x
+x
, ys
, xs
);
104 /* draw box in window */
105 void draw_double_box (Dlg_head
*h
, int y
, int x
, int ys
, int xs
)
108 draw_box (h
, y
, x
, ys
, xs
);
110 SLsmg_draw_double_box (h
->y
+y
, h
->x
+x
, ys
, xs
);
114 void widget_erase (Widget
*w
)
118 for (y
= 0; y
< w
->lines
; y
++){
119 widget_move (w
, y
, 0);
120 for (x
= 0; x
< w
->cols
; x
++)
125 void dlg_erase (Dlg_head
*h
)
129 for (y
= 0; y
< h
->lines
; y
++){
130 move (y
+h
->y
, h
->x
); /* FIXME: should test if ERR */
131 for (x
= 0; x
< h
->cols
; x
++){
138 void init_widget (Widget
*w
, int y
, int x
, int lines
, int cols
,
139 int (*callback
)(Dlg_head
*, void *, int, int),
140 destroy_fn destroy
, mouse_h mouse_handler
, char *tkname
)
147 w
->callback
= callback
;
148 w
->destroy
= destroy
;
149 w
->mouse
= mouse_handler
;
156 if (tkname
&& *tkname
== 0){
157 fprintf (stderr
, "Got a null string for the tkname\n");
160 /* Almost all widgets want to put the cursor in a suitable place */
161 w
->options
= W_WANT_CURSOR
;
164 int default_proc (Dlg_head
*h
, int Msg
, int Par
)
168 case WIDGET_HOTKEY
: /* Didn't use the key */
171 case WIDGET_INIT
: /* We could tell if something went wrong */
175 return 0; /* Didn't use the key */
177 case WIDGET_FOCUS
: /* We accept FOCUSes */
179 x_focus_widget (h
->current
);
182 case WIDGET_UNFOCUS
: /* We accept loose FOCUSes */
184 x_unfocus_widget (h
->current
);
194 /* Move the cursor to the default widget position */
200 printf ("Internal error: unhandled message: %d\n", Msg
);
204 int default_dlg_callback (Dlg_head
*h
, int id
, int msg
)
206 if (msg
== DLG_IDLE
){
207 dlg_broadcast_msg_to (h
, WIDGET_IDLE
, 0, W_WANT_IDLE
);
213 int midnight_callback (struct Dlg_head
*h
, int id
, int msg
);
215 Dlg_head
*create_dlg (int y1
, int x1
, int lines
, int cols
,
217 int (*callback
) (struct Dlg_head
*, int, int),
218 char *help_ctx
, char *name
,
224 if (flags
& DLG_CENTER
){
225 y1
= (LINES
-lines
)/2;
230 if ((flags
& DLG_TRYUP
) && (y1
> 3))
233 new_d
= g_new0 (Dlg_head
, 1);
234 new_d
->direction
= DIR_FORWARD
;
235 new_d
->color
= color_set
;
236 new_d
->help_ctx
= help_ctx
;
237 new_d
->callback
= callback
? callback
: default_dlg_callback
;
241 new_d
->lines
= lines
;
244 if (callback
!= midnight_callback
)
245 new_d
->wdata
= xtoolkit_create_dialog (new_d
, flags
);
247 new_d
->wdata
= xtoolkit_get_main_dialog (new_d
);
252 void set_idle_proc (Dlg_head
*d
, int state
)
254 d
->send_idle_msg
= state
;
255 x_set_idle (d
, state
);
258 /* add component to dialog buffer */
259 int add_widget (Dlg_head
*where
, void *what
)
262 Widget
*widget
= (Widget
*) what
;
264 /* Only used by Tk */
265 widget
->frame
= the_frame
;
267 /* Don't accept 0 widgets, this could be from widgets that could not */
268 /* initialize properly */
272 widget
->x
+= where
->x
;
273 widget
->y
+= where
->y
;
276 Widget_Item
*point
= where
->current
;
278 where
->current
= g_new (Widget_Item
, 1);
281 where
->current
->next
= point
->next
;
282 where
->current
->prev
= point
;
283 point
->next
->prev
= where
->current
;
284 point
->next
= where
->current
;
286 where
->current
->next
= where
->current
;
287 where
->first
= where
->current
;
288 where
->current
->prev
= where
->first
;
289 where
->last
= where
->current
;
290 where
->first
->next
= where
->last
;
293 back
= where
->current
;
294 where
->current
= g_new (Widget_Item
, 1);
296 back
->prev
= where
->current
;
297 where
->current
->next
= back
;
299 where
->current
->next
= where
->current
;
300 where
->first
= where
->current
;
303 where
->current
->prev
= where
->first
;
304 where
->last
= where
->current
;
305 where
->first
->next
= where
->last
;
308 where
->current
->dlg_id
= where
->count
;
309 where
->current
->widget
= what
;
310 where
->current
->widget
->parent
= where
;
314 /* If the widget is inserted in a running dialog */
316 send_message (where
, widget
, WIDGET_INIT
, 0);
317 send_message (where
, widget
, WIDGET_DRAW
, 0);
319 x_add_widget (where
, where
->current
);
322 return (where
->count
- 1);
325 int remove_widget (Dlg_head
*h
, void *what
)
327 Widget_Item
*first
, *p
;
332 first
= p
= h
->current
;
335 if (p
->widget
== what
){
336 /* Remove links to this Widget_Item */
337 p
->prev
->next
= p
->next
;
338 p
->next
->prev
= p
->prev
;
340 /* Make sure h->current is always valid */
341 if (p
== h
->current
){
342 h
->current
= h
->current
->next
;
351 } while (p
!= first
);
355 int destroy_widget (Widget
*w
)
357 send_message (w
->parent
, w
, WIDGET_DESTROY
, 0);
364 int send_message (Dlg_head
*h
, Widget
*w
, int msg
, int par
)
366 return (*(w
->callback
))(h
, w
, msg
, par
);
369 /* broadcast a message to all the widgets in a dialog that have
370 * the options set to flags.
372 void dlg_broadcast_msg_to (Dlg_head
*h
, int message
, int reverse
, int flags
)
374 Widget_Item
*p
, *first
, *wi
;
383 first
= p
= h
->current
->prev
;
385 /* FIXME: On XView the layout for the widget->next widget is
386 invoked, and we should change the buttons order on query_dialog
387 in order to use the HAVE_X part of the statement */
389 first
= p
= h
->current
;
391 first
= p
= h
->current
->next
;
403 /* if (p->widget->options & flags) */
405 if (is_a_panel (wi
->widget
))
408 send_message (h
, wi
->widget
, message
, 0);
409 } while (first
!= p
);
411 if (was_panel
&& message
== WIDGET_INIT
)
412 h
->current
= h
->current
->prev
;
416 /* broadcast a message to all the widgets in a dialog */
417 void dlg_broadcast_msg (Dlg_head
*h
, int message
, int reverse
)
419 dlg_broadcast_msg_to (h
, message
, reverse
, ~0);
422 int dlg_focus (Dlg_head
*h
)
427 if (send_message (h
, h
->current
->widget
, WIDGET_FOCUS
, 0)){
428 (*h
->callback
) (h
, h
->current
->dlg_id
, DLG_FOCUS
);
434 int dlg_unfocus (Dlg_head
*h
)
439 if (send_message (h
, h
->current
->widget
, WIDGET_UNFOCUS
, 0)){
440 (*h
->callback
) (h
, h
->current
->dlg_id
, DLG_UNFOCUS
);
446 static void select_a_widget (Dlg_head
*h
, int down
)
448 int direction
= h
->direction
;
454 direction
= !direction
;
458 h
->current
= h
->current
->next
;
460 h
->current
= h
->current
->prev
;
462 (*h
->callback
) (h
, h
->current
->dlg_id
, DLG_ONE_DOWN
);
463 } while (!dlg_focus (h
));
466 /* Return true if the windows overlap */
467 int dlg_overlap (Widget
*a
, Widget
*b
)
469 if ((b
->x
>= a
->x
+ a
->cols
)
470 || (a
->x
>= b
->x
+ b
->cols
)
471 || (b
->y
>= a
->y
+ a
->lines
)
472 || (a
->y
>= b
->y
+ b
->lines
))
478 /* Searches a widget, uses the callback as a signature in the dialog h */
479 Widget
*find_widget_type (Dlg_head
*h
, callback_fn signature
)
491 for (i
= 0, item
= h
->current
; i
< h
->count
; i
++, item
= item
->next
){
492 if (item
->widget
->callback
== signature
){
500 void dlg_one_up (Dlg_head
*h
)
509 /* If it accepts unFOCUSion */
513 select_a_widget (h
, 0);
514 if (dlg_overlap (old
->widget
, h
->current
->widget
)){
515 send_message (h
, h
->current
->widget
, WIDGET_DRAW
, 0);
516 send_message (h
, h
->current
->widget
, WIDGET_FOCUS
, 0);
520 void dlg_one_down (Dlg_head
*h
)
528 if (!dlg_unfocus (h
))
531 select_a_widget (h
, 1);
532 if (dlg_overlap (old
->widget
, h
->current
->widget
)){
533 send_message (h
, h
->current
->widget
, WIDGET_DRAW
, 0);
534 send_message (h
, h
->current
->widget
, WIDGET_FOCUS
, 0);
538 int dlg_select_widget (Dlg_head
*h
, void *w
)
543 if (dlg_unfocus (h
)){
544 while (h
->current
->widget
!= w
)
545 h
->current
= h
->current
->next
;
546 while (!dlg_focus (h
))
547 h
->current
= h
->current
->next
;
554 int send_message_to (Dlg_head
*h
, Widget
*w
, int msg
, int par
)
556 Widget_Item
*p
= h
->current
;
563 for (i
= 0; i
< h
->count
; i
++){
564 if (w
== (void *) p
->widget
){
565 v
= send_message (h
, p
->widget
, msg
, par
);
573 #define callback(h) (h->current->widget->callback)
575 void update_cursor (Dlg_head
*h
)
579 if (h
->current
->widget
->options
& W_WANT_CURSOR
)
580 send_message (h
, h
->current
->widget
, WIDGET_CURSOR
, 0);
582 Widget_Item
*p
= h
->current
;
585 if (p
->widget
->options
& W_WANT_CURSOR
)
586 if ((*p
->widget
->callback
)(h
, p
->widget
, WIDGET_CURSOR
, 0)){
591 } while (h
->current
!= p
);
595 /* Redraw the widgets in reverse order, leaving the current widget
598 void dlg_redraw (Dlg_head
*h
)
600 (h
->callback
)(h
, 0, DLG_DRAW
);
602 dlg_broadcast_msg (h
, WIDGET_DRAW
, 1);
607 void dlg_refresh (void *parameter
)
609 dlg_redraw ((Dlg_head
*) parameter
);
612 void dlg_stop (Dlg_head
*h
)
618 static INLINE
void dialog_handle_key (Dlg_head
*h
, int d_key
)
632 interactive_display (NULL
, h
->help_ctx
);
643 /* Use this if the refreshes fail */
648 #endif /* HAVE_SLANG */
656 h
->ret_value
= B_ENTER
;
665 h
->ret_value
= B_CANCEL
;
671 static int dlg_try_hotkey (Dlg_head
*h
, int d_key
)
673 Widget_Item
*hot_cur
;
674 Widget_Item
*previous
;
681 * Explanation: we don't send letter hotkeys to other widgets if
682 * the currently selected widget is an input line
685 if (h
->current
->widget
->options
& W_IS_INPUT
){
686 if(d_key
< 255 && isalpha(d_key
))
690 /* If it's an alt key, send the message */
692 if (d_key
& ALT(0) && c
< 255 && isalpha(c
))
696 /* .ado: fix problem with file_permission under Win95 */
697 if (d_key
== 0) return 0;
701 if (h
->current
->widget
->options
& W_WANT_HOTKEY
)
702 handled
= callback (h
) (h
, h
->current
->widget
, WIDGET_HOTKEY
, d_key
);
704 /* If not used, send hotkey to other widgets */
708 hot_cur
= h
->current
;
710 /* send it to all widgets */
712 if (hot_cur
->widget
->options
& W_WANT_HOTKEY
)
713 handled
|= (*hot_cur
->widget
->callback
)
714 (h
, hot_cur
->widget
, WIDGET_HOTKEY
, d_key
);
717 hot_cur
= hot_cur
->next
;
718 } while (h
->current
!= hot_cur
&& !handled
);
723 (*h
->callback
) (h
, 0, DLG_HOTKEY_HANDLED
);
724 previous
= h
->current
;
725 if (!dlg_unfocus (h
))
728 h
->current
= hot_cur
;
730 h
->current
= previous
;
736 int dlg_key_event (Dlg_head
*h
, int d_key
)
743 /* TAB used to cycle */
744 if (!h
->raw
&& (d_key
== '\t' || d_key
== KEY_BTAB
))
751 /* first can dlg_callback handle the key */
752 handled
= (*h
->callback
) (h
, d_key
, DLG_KEY
);
754 /* next try the hotkey */
756 handled
= dlg_try_hotkey (h
, d_key
);
758 /* not used - then try widget_callback */
760 handled
|= callback (h
)(h
, h
->current
->widget
, WIDGET_KEY
, d_key
);
762 /* not used- try to use the unhandled case */
764 handled
|= (*h
->callback
) (h
, d_key
, DLG_UNHANDLED_KEY
);
767 dialog_handle_key (h
, d_key
);
768 (*h
->callback
) (h
, d_key
, DLG_POST_KEY
);
775 static INLINE
int dlg_mouse_event (Dlg_head
*h
, Gpm_Event
*event
)
778 Widget_Item
*starting_widget
= h
->current
;
784 /* kludge for the menubar: start at h->first, not current */
785 /* Must be carefull in the insertion order to the dlg list */
786 if (y
== 1 && h
->has_menubar
)
787 starting_widget
= h
->first
;
789 item
= starting_widget
;
791 Widget
*widget
= item
->widget
;
795 if (!((x
> widget
->x
) && (x
<= widget
->x
+widget
->cols
)
796 && (y
> widget
->y
) && (y
<= widget
->y
+widget
->lines
)))
800 new_event
.x
-= widget
->x
;
801 new_event
.y
-= widget
->y
;
803 ret_value
= widget
->mouse
? (*widget
->mouse
) (&new_event
, widget
) :
807 } while (item
!= starting_widget
);
811 /* Run dialog routines */
813 /* Init the process */
814 void init_dlg (Dlg_head
*h
)
818 /* Initialize dialog manager and widgets */
819 (*h
->callback
) (h
, 0, DLG_INIT
);
820 dlg_broadcast_msg (h
, WIDGET_INIT
, 0);
823 refresh_mode
= REFRESH_COVERS_PART
;
825 if (h
->x
== 0 && h
->y
== 0 && h
->cols
== COLS
&& h
->lines
== LINES
)
826 refresh_mode
= REFRESH_COVERS_ALL
;
828 refresh_mode
= REFRESH_COVERS_PART
;
831 push_refresh (dlg_refresh
, h
, refresh_mode
);
832 h
->refresh_pushed
= 1;
834 /* Initialize direction */
836 h
->current
= h
->first
;
838 if (h
->initfocus
!= NULL
)
839 h
->current
= h
->initfocus
;
841 h
->previous_dialog
= current_dlg
;
844 /* Initialize the mouse status */
847 /* Redraw the screen */
850 while (!dlg_focus (h
) && h
->current
)
851 h
->current
= h
->current
->next
;
858 /* Shutdown the run_dlg */
859 void dlg_run_done (Dlg_head
*h
)
862 (*h
->callback
) (h
, h
->current
->dlg_id
, DLG_END
);
864 current_dlg
= (Dlg_head
*) h
->previous_dialog
;
868 * Special case for the GNOME desktop:
869 * The desktop will not have any widgets
871 if (current_dlg
->current
)
872 x_focus_widget (current_dlg
->current
);
876 void dlg_process_event (Dlg_head
*h
, int key
, Gpm_Event
*event
)
879 if (got_interrupt ())
886 h
->mouse_status
= dlg_mouse_event (h
, event
);
888 dlg_key_event (h
, key
);
893 frontend_run_dlg (Dlg_head
*h
)
900 #if defined(HAVE_SLANG) || NCURSES_VERSION_MAJOR >= 4
901 /* It does not work with ncurses before 1.9.9g, it will break */
903 change_screen_size ();
907 execute_hooks (idle_hook
);
909 while (h
->send_idle_msg
&& is_idle ()){
910 (*h
->callback
) (h
, 0, DLG_IDLE
);
915 (*h
->callback
)(h
, 0, DLG_PRE_EVENT
);
917 /* Clear interrupt flag */
919 d_key
= get_event (&event
, h
->mouse_status
== MOU_REPEAT
, 1);
921 dlg_process_event (h
, d_key
, &event
);
926 /* Standard run dialog routine
927 * We have to keep this routine small so that we can duplicate it's
928 * behavior on complex routines like the file routines, this way,
929 * they can call the dlg_process_event without rewriting all the code
931 void run_dlg (Dlg_head
*h
)
937 frontend_run_dlg (h
);
943 destroy_dlg (Dlg_head
*h
)
948 if (h
->refresh_pushed
)
951 x_destroy_dlg_start (h
);
952 dlg_broadcast_msg (h
, WIDGET_DESTROY
, 0);
954 for (i
= 0; i
< h
->count
; i
++){
955 if (c
->widget
->destroy
)
956 c
->widget
->destroy (c
->widget
);
959 g_free (h
->current
->widget
);
975 int std_callback (Dlg_head
*h
, int Msg
, int Par
)
980 void widget_set_size (Widget
*widget
, int y
, int x
, int lines
, int cols
)
985 widget
->lines
= lines
;
988 /* Replace widget old for widget new in the h dialog */
989 void dlg_replace_widget (Dlg_head
*h
, Widget
*old
, Widget
*new)
991 Widget_Item
*p
= h
->current
;
992 int should_focus
= 0;
998 if (p
->widget
== old
){
1000 if (old
== h
->current
->widget
)
1003 /* We found the widget */
1004 /* First kill the widget */
1005 new->focused
= old
->focused
;
1007 send_message_to (h
, old
, WIDGET_DESTROY
, 0);
1008 (*old
->destroy
) (old
);
1010 /* We insert the new widget */
1012 send_message_to (h
, new, WIDGET_INIT
, 0);
1014 if (dlg_focus (h
) == 0)
1015 select_a_widget (h
, 1);
1017 send_message_to (h
, new, WIDGET_DRAW
, 0);
1021 } while (p
!= h
->current
);
1024 void widget_redraw (Dlg_head
*h
, Widget_Item
*w
)
1026 Widget_Item
*save
= h
->current
;
1032 (*w
->widget
->callback
)(h
, h
->current
->widget
, WIDGET_DRAW
, 0);
1036 /* Returns the index of h->current from h->first */
1037 int dlg_item_number (Dlg_head
*h
)
1045 if (p
== h
->current
)
1049 } while (p
!= h
->first
);
1050 fprintf (stderr
, "Internal error: current not in dialog list\n\r");
1054 int dlg_select_nth_widget (Dlg_head
*h
, int n
)
1060 for (i
= 0; i
< n
; i
++)
1063 return dlg_select_widget (h
, w
->widget
);
1068 x_set_dialog_title (Dlg_head
*h
, const char *title
)
1070 h
->title
= g_strdup (title
);
1072 #endif /* !HAVE_X */