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 */
51 #ifndef PORT_HAS_SET_IDLE
52 # define x_set_idle(d,x)
55 #ifndef PORT_HAS_DIALOG_STOP
56 # define x_dialog_stop(d)
60 void widget_erase (Widget
*w
)
64 void dlg_erase (Dlg_head
*h
)
68 static void slow_box (Dlg_head
*h
, int y
, int x
, int ys
, int xs
)
70 move (h
->y
+y
, h
->x
+x
);
73 move (h
->y
+y
, h
->x
+x
+xs
-1);
75 move (h
->y
+y
+ys
-1, h
->x
+x
);
79 /* draw box in window */
80 void draw_box (Dlg_head
*h
, int y
, int x
, int ys
, int xs
)
82 extern int slow_terminal
;
85 slow_box (h
, y
, x
, ys
, xs
);
90 waddc (h
, y
, x
, ACS_ULCORNER
);
91 hline (ACS_HLINE
, xs
- 2);
92 waddc (h
, y
+ ys
- 1, x
, ACS_LLCORNER
);
93 hline (ACS_HLINE
, xs
- 2);
95 waddc (h
, y
, x
+ xs
- 1, ACS_URCORNER
);
96 waddc (h
, y
+ ys
- 1, x
+ xs
- 1, ACS_LRCORNER
);
98 move (h
->y
+y
+1, h
->x
+x
);
99 vline (ACS_VLINE
, ys
- 2);
100 move (h
->y
+y
+1, h
->x
+x
+xs
-1);
101 vline (ACS_VLINE
, ys
- 2);
103 SLsmg_draw_box (h
->y
+y
, h
->x
+x
, ys
, xs
);
107 /* draw box in window */
108 void draw_double_box (Dlg_head
*h
, int y
, int x
, int ys
, int xs
)
111 draw_box (h
, y
, x
, ys
, xs
);
113 SLsmg_draw_double_box (h
->y
+y
, h
->x
+x
, ys
, xs
);
117 void widget_erase (Widget
*w
)
121 for (y
= 0; y
< w
->lines
; y
++){
122 widget_move (w
, y
, 0);
123 for (x
= 0; x
< w
->cols
; x
++)
128 void dlg_erase (Dlg_head
*h
)
132 for (y
= 0; y
< h
->lines
; y
++){
133 move (y
+h
->y
, h
->x
); /* FIXME: should test if ERR */
134 for (x
= 0; x
< h
->cols
; x
++){
141 void init_widget (Widget
*w
, int y
, int x
, int lines
, int cols
,
142 int (*callback
)(Dlg_head
*, void *, int, int),
143 destroy_fn destroy
, mouse_h mouse_handler
, char *tkname
)
150 w
->callback
= callback
;
151 w
->destroy
= destroy
;
152 w
->mouse
= mouse_handler
;
159 if (tkname
&& *tkname
== 0){
160 fprintf (stderr
, "Got a null string for the tkname\n");
163 /* Almost all widgets want to put the cursor in a suitable place */
164 w
->options
= W_WANT_CURSOR
;
167 int default_proc (Dlg_head
*h
, int Msg
, int Par
)
171 case WIDGET_HOTKEY
: /* Didn't use the key */
174 case WIDGET_INIT
: /* We could tell if something went wrong */
178 return 0; /* Didn't use the key */
180 case WIDGET_FOCUS
: /* We accept FOCUSes */
182 x_focus_widget (h
->current
);
185 case WIDGET_UNFOCUS
: /* We accept loose FOCUSes */
187 x_unfocus_widget (h
->current
);
197 /* Move the cursor to the default widget position */
203 printf ("Internal error: unhandled message: %d\n", Msg
);
207 int default_dlg_callback (Dlg_head
*h
, int id
, int msg
)
209 if (msg
== DLG_IDLE
){
210 dlg_broadcast_msg_to (h
, WIDGET_IDLE
, 0, W_WANT_IDLE
);
216 int midnight_callback (struct Dlg_head
*h
, int id
, int msg
);
218 Dlg_head
*create_dlg (int y1
, int x1
, int lines
, int cols
,
220 int (*callback
) (struct Dlg_head
*, int, int),
221 char *help_ctx
, char *name
,
227 if (flags
& DLG_CENTER
){
228 y1
= (LINES
-lines
)/2;
233 if ((flags
& DLG_TRYUP
) && (y1
> 3))
236 new_d
= g_new (Dlg_head
, 1);
237 new_d
->current
= NULL
;
239 new_d
->direction
= DIR_FORWARD
;
240 new_d
->color
= color_set
;
241 new_d
->help_ctx
= help_ctx
;
242 new_d
->callback
= callback
? callback
: default_dlg_callback
;
243 new_d
->send_idle_msg
= 0;
248 new_d
->lines
= lines
;
249 new_d
->refresh_pushed
= 0;
250 new_d
->has_menubar
= 0;
254 new_d
->initfocus
= NULL
;
257 if (callback
!= midnight_callback
)
258 new_d
->wdata
= xtoolkit_create_dialog (new_d
, flags
);
260 new_d
->wdata
= xtoolkit_get_main_dialog (new_d
);
265 void set_idle_proc (Dlg_head
*d
, int state
)
267 d
->send_idle_msg
= state
;
268 x_set_idle (d
, state
);
271 /* add component to dialog buffer */
272 int add_widget (Dlg_head
*where
, void *what
)
275 Widget
*widget
= (Widget
*) what
;
277 /* Only used by Tk */
278 widget
->frame
= the_frame
;
280 /* Don't accept 0 widgets, this could be from widgets that could not */
281 /* initialize properly */
285 widget
->x
+= where
->x
;
286 widget
->y
+= where
->y
;
289 Widget_Item
*point
= where
->current
;
291 where
->current
= g_new (Widget_Item
, 1);
294 where
->current
->next
= point
->next
;
295 where
->current
->prev
= point
;
296 point
->next
->prev
= where
->current
;
297 point
->next
= where
->current
;
299 where
->current
->next
= where
->current
;
300 where
->first
= where
->current
;
301 where
->current
->prev
= where
->first
;
302 where
->last
= where
->current
;
303 where
->first
->next
= where
->last
;
306 back
= where
->current
;
307 where
->current
= g_new (Widget_Item
, 1);
309 back
->prev
= where
->current
;
310 where
->current
->next
= back
;
312 where
->current
->next
= where
->current
;
313 where
->first
= where
->current
;
316 where
->current
->prev
= where
->first
;
317 where
->last
= where
->current
;
318 where
->first
->next
= where
->last
;
321 where
->current
->dlg_id
= where
->count
;
322 where
->current
->widget
= what
;
323 where
->current
->widget
->parent
= where
;
327 /* If the widget is inserted in a running dialog */
329 send_message (where
, widget
, WIDGET_INIT
, 0);
330 send_message (where
, widget
, WIDGET_DRAW
, 0);
332 x_add_widget (where
, where
->current
);
335 return (where
->count
- 1);
338 int remove_widget (Dlg_head
*h
, void *what
)
340 Widget_Item
*first
, *p
;
345 first
= p
= h
->current
;
348 if (p
->widget
== what
){
349 /* Remove links to this Widget_Item */
350 p
->prev
->next
= p
->next
;
351 p
->next
->prev
= p
->prev
;
353 /* Make sure h->current is always valid */
354 if (p
== h
->current
){
355 h
->current
= h
->current
->next
;
364 } while (p
!= first
);
368 int destroy_widget (Widget
*w
)
370 send_message (w
->parent
, w
, WIDGET_DESTROY
, 0);
377 int send_message (Dlg_head
*h
, Widget
*w
, int msg
, int par
)
379 return (*(w
->callback
))(h
, w
, msg
, par
);
382 /* broadcast a message to all the widgets in a dialog that have
383 * the options set to flags.
385 void dlg_broadcast_msg_to (Dlg_head
*h
, int message
, int reverse
, int flags
)
387 Widget_Item
*p
, *first
, *wi
;
396 first
= p
= h
->current
->prev
;
398 /* FIXME: On XView the layout for the widget->next widget is
399 invoked, and we should change the buttons order on query_dialog
400 in order to use the HAVE_X part of the statement */
402 first
= p
= h
->current
;
404 first
= p
= h
->current
->next
;
416 /* if (p->widget->options & flags) */
418 if (is_a_panel (wi
->widget
))
421 send_message (h
, wi
->widget
, message
, 0);
422 } while (first
!= p
);
424 if (was_panel
&& message
== WIDGET_INIT
)
425 h
->current
= h
->current
->prev
;
429 /* broadcast a message to all the widgets in a dialog */
430 void dlg_broadcast_msg (Dlg_head
*h
, int message
, int reverse
)
432 dlg_broadcast_msg_to (h
, message
, reverse
, ~0);
435 int dlg_focus (Dlg_head
*h
)
440 if (send_message (h
, h
->current
->widget
, WIDGET_FOCUS
, 0)){
441 (*h
->callback
) (h
, h
->current
->dlg_id
, DLG_FOCUS
);
447 int dlg_unfocus (Dlg_head
*h
)
452 if (send_message (h
, h
->current
->widget
, WIDGET_UNFOCUS
, 0)){
453 (*h
->callback
) (h
, h
->current
->dlg_id
, DLG_UNFOCUS
);
459 static void select_a_widget (Dlg_head
*h
, int down
)
461 int direction
= h
->direction
;
467 direction
= !direction
;
471 h
->current
= h
->current
->next
;
473 h
->current
= h
->current
->prev
;
475 (*h
->callback
) (h
, h
->current
->dlg_id
, DLG_ONE_DOWN
);
476 } while (!dlg_focus (h
));
479 /* Return true if the windows overlap */
480 int dlg_overlap (Widget
*a
, Widget
*b
)
482 if ((b
->x
>= a
->x
+ a
->cols
)
483 || (a
->x
>= b
->x
+ b
->cols
)
484 || (b
->y
>= a
->y
+ a
->lines
)
485 || (a
->y
>= b
->y
+ b
->lines
))
491 /* Searches a widget, uses the callback as a signature in the dialog h */
492 Widget
*find_widget_type (Dlg_head
*h
, callback_fn signature
)
504 for (i
= 0, item
= h
->current
; i
< h
->count
; i
++, item
= item
->next
){
505 if (item
->widget
->callback
== signature
){
513 void dlg_one_up (Dlg_head
*h
)
522 /* If it accepts unFOCUSion */
526 select_a_widget (h
, 0);
527 if (dlg_overlap (old
->widget
, h
->current
->widget
)){
528 send_message (h
, h
->current
->widget
, WIDGET_DRAW
, 0);
529 send_message (h
, h
->current
->widget
, WIDGET_FOCUS
, 0);
533 void dlg_one_down (Dlg_head
*h
)
541 if (!dlg_unfocus (h
))
544 select_a_widget (h
, 1);
545 if (dlg_overlap (old
->widget
, h
->current
->widget
)){
546 send_message (h
, h
->current
->widget
, WIDGET_DRAW
, 0);
547 send_message (h
, h
->current
->widget
, WIDGET_FOCUS
, 0);
551 int dlg_select_widget (Dlg_head
*h
, void *w
)
556 if (dlg_unfocus (h
)){
557 while (h
->current
->widget
!= w
)
558 h
->current
= h
->current
->next
;
559 while (!dlg_focus (h
))
560 h
->current
= h
->current
->next
;
567 int send_message_to (Dlg_head
*h
, Widget
*w
, int msg
, int par
)
569 Widget_Item
*p
= h
->current
;
576 for (i
= 0; i
< h
->count
; i
++){
577 if (w
== (void *) p
->widget
){
578 v
= send_message (h
, p
->widget
, msg
, par
);
586 #define callback(h) (h->current->widget->callback)
588 void update_cursor (Dlg_head
*h
)
592 if (h
->current
->widget
->options
& W_WANT_CURSOR
)
593 send_message (h
, h
->current
->widget
, WIDGET_CURSOR
, 0);
595 Widget_Item
*p
= h
->current
;
598 if (p
->widget
->options
& W_WANT_CURSOR
)
599 if ((*p
->widget
->callback
)(h
, p
->widget
, WIDGET_CURSOR
, 0)){
604 } while (h
->current
!= p
);
608 /* Redraw the widgets in reverse order, leaving the current widget
611 void dlg_redraw (Dlg_head
*h
)
613 (h
->callback
)(h
, 0, DLG_DRAW
);
615 dlg_broadcast_msg (h
, WIDGET_DRAW
, 1);
620 void dlg_refresh (void *parameter
)
622 dlg_redraw ((Dlg_head
*) parameter
);
625 void dlg_stop (Dlg_head
*h
)
631 static INLINE
void dialog_handle_key (Dlg_head
*h
, int d_key
)
647 hlpfile
= concat_dir_and_file (mc_home
, _("mc.hlp"));
648 interactive_display (hlpfile
, h
->help_ctx
);
660 /* Use this if the refreshes fail */
665 #endif /* HAVE_SLANG */
673 h
->ret_value
= B_ENTER
;
682 h
->ret_value
= B_CANCEL
;
688 static int dlg_try_hotkey (Dlg_head
*h
, int d_key
)
690 Widget_Item
*hot_cur
;
691 Widget_Item
*previous
;
698 * Explanation: we don't send letter hotkeys to other widgets if
699 * the currently selected widget is an input line
702 if (h
->current
->widget
->options
& W_IS_INPUT
){
703 if(d_key
< 255 && isalpha(d_key
))
707 /* If it's an alt key, send the message */
709 if (d_key
& ALT(0) && c
< 255 && isalpha(c
))
713 /* .ado: fix problem with file_permission under Win95 */
714 if (d_key
== 0) return 0;
718 if (h
->current
->widget
->options
& W_WANT_HOTKEY
)
719 handled
= callback (h
) (h
, h
->current
->widget
, WIDGET_HOTKEY
, d_key
);
721 /* If not used, send hotkey to other widgets */
725 hot_cur
= h
->current
;
727 /* send it to all widgets */
729 if (hot_cur
->widget
->options
& W_WANT_HOTKEY
)
730 handled
|= (*hot_cur
->widget
->callback
)
731 (h
, hot_cur
->widget
, WIDGET_HOTKEY
, d_key
);
734 hot_cur
= hot_cur
->next
;
735 } while (h
->current
!= hot_cur
&& !handled
);
740 (*h
->callback
) (h
, 0, DLG_HOTKEY_HANDLED
);
741 previous
= h
->current
;
742 if (!dlg_unfocus (h
))
745 h
->current
= hot_cur
;
747 h
->current
= previous
;
753 int dlg_key_event (Dlg_head
*h
, int d_key
)
760 /* TAB used to cycle */
761 if (!h
->raw
&& (d_key
== '\t' || d_key
== KEY_BTAB
))
768 /* first can dlg_callback handle the key */
769 handled
= (*h
->callback
) (h
, d_key
, DLG_KEY
);
771 /* next try the hotkey */
773 handled
= dlg_try_hotkey (h
, d_key
);
775 /* not used - then try widget_callback */
777 handled
|= callback (h
)(h
, h
->current
->widget
, WIDGET_KEY
, d_key
);
779 /* not used- try to use the unhandled case */
781 handled
|= (*h
->callback
) (h
, d_key
, DLG_UNHANDLED_KEY
);
784 dialog_handle_key (h
, d_key
);
785 (*h
->callback
) (h
, d_key
, DLG_POST_KEY
);
792 static INLINE
int dlg_mouse_event (Dlg_head
*h
, Gpm_Event
*event
)
795 Widget_Item
*starting_widget
= h
->current
;
801 /* kludge for the menubar: start at h->first, not current */
802 /* Must be carefull in the insertion order to the dlg list */
803 if (y
== 1 && h
->has_menubar
)
804 starting_widget
= h
->first
;
806 item
= starting_widget
;
808 Widget
*widget
= item
->widget
;
812 if (!((x
> widget
->x
) && (x
<= widget
->x
+widget
->cols
)
813 && (y
> widget
->y
) && (y
<= widget
->y
+widget
->lines
)))
817 new_event
.x
-= widget
->x
;
818 new_event
.y
-= widget
->y
;
820 ret_value
= widget
->mouse
? (*widget
->mouse
) (&new_event
, widget
) :
824 } while (item
!= starting_widget
);
828 /* Run dialog routines */
830 /* Init the process */
831 void init_dlg (Dlg_head
*h
)
835 /* Initialize dialog manager and widgets */
836 (*h
->callback
) (h
, 0, DLG_INIT
);
837 dlg_broadcast_msg (h
, WIDGET_INIT
, 0);
840 refresh_mode
= REFRESH_COVERS_PART
;
842 if (h
->x
== 0 && h
->y
== 0 && h
->cols
== COLS
&& h
->lines
== LINES
)
843 refresh_mode
= REFRESH_COVERS_ALL
;
845 refresh_mode
= REFRESH_COVERS_PART
;
848 push_refresh (dlg_refresh
, h
, refresh_mode
);
849 h
->refresh_pushed
= 1;
851 /* Initialize direction */
853 h
->current
= h
->first
;
855 if (h
->initfocus
!= NULL
)
856 h
->current
= h
->initfocus
;
858 h
->previous_dialog
= current_dlg
;
861 /* Initialize the mouse status */
864 /* Redraw the screen */
867 while (!dlg_focus (h
) && h
->current
)
868 h
->current
= h
->current
->next
;
875 /* Shutdown the run_dlg */
876 void dlg_run_done (Dlg_head
*h
)
879 (*h
->callback
) (h
, h
->current
->dlg_id
, DLG_END
);
881 current_dlg
= (Dlg_head
*) h
->previous_dialog
;
885 * Special case for the GNOME desktop:
886 * The desktop will not have any widgets
888 if (current_dlg
->current
)
889 x_focus_widget (current_dlg
->current
);
893 void dlg_process_event (Dlg_head
*h
, int key
, Gpm_Event
*event
)
896 if (got_interrupt ())
903 h
->mouse_status
= dlg_mouse_event (h
, event
);
905 dlg_key_event (h
, key
);
908 #ifndef PORT_HAS_FRONTEND_RUN_DLG
910 frontend_run_dlg (Dlg_head
*h
)
917 #if defined(HAVE_SLANG) || NCURSES_VERSION_MAJOR >= 4
918 /* It does not work with ncurses before 1.9.9g, it will break */
920 change_screen_size ();
924 execute_hooks (idle_hook
);
926 while (h
->send_idle_msg
&& is_idle ()){
927 (*h
->callback
) (h
, 0, DLG_IDLE
);
932 (*h
->callback
)(h
, 0, DLG_PRE_EVENT
);
934 /* Clear interrupt flag */
936 d_key
= get_event (&event
, h
->mouse_status
== MOU_REPEAT
, 1);
938 dlg_process_event (h
, d_key
, &event
);
941 #endif /* PORT_HAS_FRONTEND_RUN_DLG */
943 /* Standard run dialog routine
944 * We have to keep this routine small so that we can duplicate it's
945 * behavior on complex routines like the file routines, this way,
946 * they can call the dlg_process_event without rewriting all the code
948 void run_dlg (Dlg_head
*h
)
951 frontend_run_dlg (h
);
956 destroy_dlg (Dlg_head
*h
)
961 if (h
->refresh_pushed
)
964 x_destroy_dlg_start (h
);
965 dlg_broadcast_msg (h
, WIDGET_DESTROY
, 0);
967 for (i
= 0; i
< h
->count
; i
++){
968 if (c
->widget
->destroy
)
969 c
->widget
->destroy (c
->widget
);
972 g_free (h
->current
->widget
);
988 int std_callback (Dlg_head
*h
, int Msg
, int Par
)
993 void widget_set_size (Widget
*widget
, int y
, int x
, int lines
, int cols
)
998 widget
->lines
= lines
;
1001 /* Replace widget old for widget new in the h dialog */
1002 void dlg_replace_widget (Dlg_head
*h
, Widget
*old
, Widget
*new)
1004 Widget_Item
*p
= h
->current
;
1005 int should_focus
= 0;
1011 if (p
->widget
== old
){
1013 if (old
== h
->current
->widget
)
1016 /* We found the widget */
1017 /* First kill the widget */
1018 new->focused
= old
->focused
;
1020 send_message_to (h
, old
, WIDGET_DESTROY
, 0);
1021 (*old
->destroy
) (old
);
1023 /* We insert the new widget */
1025 send_message_to (h
, new, WIDGET_INIT
, 0);
1027 if (dlg_focus (h
) == 0)
1028 select_a_widget (h
, 1);
1030 send_message_to (h
, new, WIDGET_DRAW
, 0);
1034 } while (p
!= h
->current
);
1037 void widget_redraw (Dlg_head
*h
, Widget_Item
*w
)
1039 Widget_Item
*save
= h
->current
;
1045 (*w
->widget
->callback
)(h
, h
->current
->widget
, WIDGET_DRAW
, 0);
1049 /* Returns the index of h->current from h->first */
1050 int dlg_item_number (Dlg_head
*h
)
1058 if (p
== h
->current
)
1062 } while (p
!= h
->first
);
1063 fprintf (stderr
, "Internal error: current not in dialog list\n\r");
1067 int dlg_select_nth_widget (Dlg_head
*h
, int n
)
1073 for (i
= 0; i
< n
; i
++)
1076 return dlg_select_widget (h
, w
->widget
);
1079 #ifndef PORT_HAS_DIALOG_TITLE
1081 x_set_dialog_title (Dlg_head
*h
, char *title
)
1083 h
->title
= g_strdup (title
);