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., 675 Mass Ave, Cambridge, MA 02139, USA.
36 #include "key.h" /* For mi_getch() */
38 #include "dialog.h" /* For push_refresh() and pop_refresh() */
42 /* This is the current frame, used to group Tk packings */
45 #define waddc(w,y1,x1,c) move (w->y+y1, w->x+x1); addch (c)
47 /* Primitive way to check if the the current dialog is our dialog */
48 /* This is needed by async routines like load_prompt */
49 Dlg_head
*current_dlg
= 0;
51 /* A hook list for idle events */
54 #ifndef PORT_HAS_SET_IDLE
55 # define x_set_idle(d,x)
58 #ifndef PORT_HAS_DIALOG_STOP
59 # define x_dialog_stop(d)
62 static void slow_box (Dlg_head
*h
, int y
, int x
, int ys
, int xs
)
64 move (h
->y
+y
, h
->x
+x
);
67 move (h
->y
+y
, h
->x
+x
+xs
-1);
69 move (h
->y
+y
+ys
-1, h
->x
+x
);
73 /* draw box in window */
74 void draw_box (Dlg_head
*h
, int y
, int x
, int ys
, int xs
)
76 extern int slow_terminal
;
79 slow_box (h
, y
, x
, ys
, xs
);
84 waddc (h
, y
, x
, ACS_ULCORNER
);
85 hline (ACS_HLINE
, xs
- 2);
86 waddc (h
, y
+ ys
- 1, x
, ACS_LLCORNER
);
87 hline (ACS_HLINE
, xs
- 2);
89 waddc (h
, y
, x
+ xs
- 1, ACS_URCORNER
);
90 waddc (h
, y
+ ys
- 1, x
+ xs
- 1, ACS_LRCORNER
);
92 move (h
->y
+y
+1, h
->x
+x
);
93 vline (ACS_VLINE
, ys
- 2);
94 move (h
->y
+y
+1, h
->x
+x
+xs
-1);
95 vline (ACS_VLINE
, ys
- 2);
97 SLsmg_draw_box (h
->y
+y
, h
->x
+x
, ys
, xs
);
101 /* draw box in window */
102 void draw_double_box (Dlg_head
*h
, int y
, int x
, int ys
, int xs
)
105 draw_box (h
, y
, x
, ys
, xs
);
107 SLsmg_draw_double_box (h
->y
+y
, h
->x
+x
, ys
, xs
);
111 void widget_erase (Widget
*w
)
115 for (y
= 0; y
< w
->lines
; y
++){
116 widget_move (w
, y
, 0);
117 for (x
= 0; x
< w
->cols
; x
++)
122 void dlg_erase (Dlg_head
*h
)
126 for (y
= 0; y
< h
->lines
; y
++){
127 move (y
+h
->y
, h
->x
); /* FIXME: should test if ERR */
128 for (x
= 0; x
< h
->cols
; x
++){
134 void init_widget (Widget
*w
, int y
, int x
, int lines
, int cols
,
135 int (*callback
)(Dlg_head
*, void *, int, int),
136 destroy_fn destroy
, mouse_h mouse_handler
, char *tkname
)
143 w
->callback
= callback
;
144 w
->destroy
= destroy
;
145 w
->mouse
= mouse_handler
;
152 if (tkname
&& *tkname
== 0){
153 fprintf (stderr
, "Got a null string for the tkname\n");
156 /* Almost all widgets want to put the cursor in a suitable place */
157 w
->options
= W_WANT_CURSOR
;
160 int default_proc (Dlg_head
*h
, int Msg
, int Par
)
164 case WIDGET_HOTKEY
: /* Didn't use the key */
167 case WIDGET_INIT
: /* We could tell if something went wrong */
171 return 0; /* Didn't use the key */
173 case WIDGET_FOCUS
: /* We accept FOCUSes */
175 x_focus_widget (h
->current
);
178 case WIDGET_UNFOCUS
: /* We accept loose FOCUSes */
180 x_unfocus_widget (h
->current
);
190 /* Move the cursor to the default widget position */
196 printf ("Internal error: unhandled message: %d\n", Msg
);
200 int default_dlg_callback (Dlg_head
*h
, int id
, int msg
)
202 if (msg
== DLG_IDLE
){
203 dlg_broadcast_msg_to (h
, WIDGET_IDLE
, 0, W_WANT_IDLE
);
209 int midnight_callback (struct Dlg_head
*h
, int id
, int msg
);
211 Dlg_head
*create_dlg (int y1
, int x1
, int lines
, int cols
,
213 int (*callback
) (struct Dlg_head
*, int, int),
214 char *help_ctx
, char *name
,
219 if (flags
& DLG_CENTER
){
220 y1
= (LINES
-lines
)/2;
223 if ((flags
& DLG_TRYUP
) && (y1
> 3))
226 new_d
= (Dlg_head
*) malloc (sizeof (Dlg_head
));
227 new_d
->current
= NULL
;
229 new_d
->direction
= DIR_FORWARD
;
230 new_d
->color
= color_set
;
231 new_d
->help_ctx
= help_ctx
;
232 new_d
->callback
= callback
? callback
: default_dlg_callback
;
233 new_d
->send_idle_msg
= 0;
238 new_d
->lines
= lines
;
239 new_d
->refresh_pushed
= 0;
240 new_d
->has_menubar
= 0;
244 new_d
->initfocus
= NULL
;
247 if (callback
!= midnight_callback
)
248 new_d
->wdata
= xtoolkit_create_dialog (new_d
, flags
);
250 new_d
->wdata
= xtoolkit_get_main_dialog (new_d
);
255 void set_idle_proc (Dlg_head
*d
, int state
)
257 d
->send_idle_msg
= state
;
258 x_set_idle (d
, state
);
261 /* add component to dialog buffer */
262 int add_widgetl (Dlg_head
*where
, void *what
, WLay layout
)
265 Widget
*widget
= (Widget
*) what
;
267 /* Only used by Tk */
268 widget
->frame
= the_frame
;
270 widget
->layout
= layout
;
271 /* Don't accept 0 widgets, this could be from widgets that could not */
272 /* initialize properly */
276 widget
->x
+= where
->x
;
277 widget
->y
+= where
->y
;
280 Widget_Item
*point
= where
->current
;
282 where
->current
= (Widget_Item
*) malloc (sizeof (Widget_Item
));
285 where
->current
->next
= point
->next
;
286 where
->current
->prev
= point
;
287 point
->next
->prev
= where
->current
;
288 point
->next
= where
->current
;
290 where
->current
->next
= where
->current
;
291 where
->first
= where
->current
;
292 where
->current
->prev
= where
->first
;
293 where
->last
= where
->current
;
294 where
->first
->next
= where
->last
;
297 back
= where
->current
;
298 where
->current
= (Widget_Item
*) malloc (sizeof (Widget_Item
));
300 back
->prev
= where
->current
;
301 where
->current
->next
= back
;
303 where
->current
->next
= where
->current
;
304 where
->first
= where
->current
;
307 where
->current
->prev
= where
->first
;
308 where
->last
= where
->current
;
309 where
->first
->next
= where
->last
;
312 where
->current
->dlg_id
= where
->count
;
313 where
->current
->widget
= what
;
314 where
->current
->widget
->parent
= where
;
318 /* If the widget is inserted in a running dialog */
320 send_message (where
, widget
, WIDGET_INIT
, 0);
321 send_message (where
, widget
, WIDGET_DRAW
, 0);
323 x_add_widget (where
, where
->current
);
326 return (where
->count
- 1);
329 int remove_widget (Dlg_head
*h
, void *what
)
331 Widget_Item
*first
, *p
;
333 first
= p
= h
->current
;
336 if (p
->widget
== what
){
337 /* Remove links to this Widget_Item */
338 p
->prev
->next
= p
->next
;
339 p
->next
->prev
= p
->prev
;
341 /* Make sure h->current is always valid */
342 if (p
== h
->current
){
343 h
->current
= h
->current
->next
;
352 } while (p
!= first
);
356 int destroy_widget (Widget
*w
)
358 send_message (w
->parent
, w
, WIDGET_DESTROY
, 0);
365 int add_widget (Dlg_head
*where
, void *what
)
367 return add_widgetl (where
, what
, XV_WLAY_DONTCARE
);
370 int send_message (Dlg_head
*h
, Widget
*w
, int msg
, int par
)
372 return (*(w
->callback
))(h
, w
, msg
, par
);
375 /* broadcast a message to all the widgets in a dialog that have
376 * the options set to flags.
378 void dlg_broadcast_msg_to (Dlg_head
*h
, int message
, int reverse
, int flags
)
380 Widget_Item
*p
, *first
;
386 first
= p
= h
->current
->prev
;
388 /* FIXME: On XView the layout for the widget->next widget is
389 invoked, and we should change the buttons order on query_dialog
390 in order to use the HAVE_X part of the statement */
392 first
= p
= h
->current
;
394 first
= p
= h
->current
->next
;
397 /* if (p->widget->options & flags) */
398 send_message (h
, p
->widget
, message
, 0);
404 } while (first
!= p
);
407 /* broadcast a message to all the widgets in a dialog */
408 void dlg_broadcast_msg (Dlg_head
*h
, int message
, int reverse
)
410 dlg_broadcast_msg_to (h
, message
, reverse
, ~0);
413 int dlg_focus (Dlg_head
*h
)
415 if (send_message (h
, h
->current
->widget
, WIDGET_FOCUS
, 0)){
416 (*h
->callback
) (h
, h
->current
->dlg_id
, DLG_FOCUS
);
422 int dlg_unfocus (Dlg_head
*h
)
424 if (send_message (h
, h
->current
->widget
, WIDGET_UNFOCUS
, 0)){
425 (*h
->callback
) (h
, h
->current
->dlg_id
, DLG_UNFOCUS
);
431 static void select_a_widget (Dlg_head
*h
, int down
)
433 int direction
= h
->direction
;
436 direction
= !direction
;
440 h
->current
= h
->current
->next
;
442 h
->current
= h
->current
->prev
;
444 (*h
->callback
) (h
, h
->current
->dlg_id
, DLG_ONE_DOWN
);
445 } while (!dlg_focus (h
));
448 /* Return true if the windows overlap */
449 int dlg_overlap (Widget
*a
, Widget
*b
)
451 if ((b
->x
>= a
->x
+ a
->cols
)
452 || (a
->x
>= b
->x
+ b
->cols
)
453 || (b
->y
>= a
->y
+ a
->lines
)
454 || (a
->y
>= b
->y
+ b
->lines
))
460 /* Searches a widget, uses the callback as a signature in the dialog h */
461 Widget
*find_widget_type (Dlg_head
*h
, callback_fn signature
)
471 for (i
= 0, item
= h
->current
; i
< h
->count
; i
++, item
= item
->next
){
472 if (item
->widget
->callback
== signature
){
480 void dlg_one_up (Dlg_head
*h
)
485 /* If it accepts unFOCUSion */
489 select_a_widget (h
, 0);
490 if (dlg_overlap (old
->widget
, h
->current
->widget
)){
491 send_message (h
, h
->current
->widget
, WIDGET_DRAW
, 0);
492 send_message (h
, h
->current
->widget
, WIDGET_FOCUS
, 0);
496 void dlg_one_down (Dlg_head
*h
)
501 if (!dlg_unfocus (h
))
504 select_a_widget (h
, 1);
505 if (dlg_overlap (old
->widget
, h
->current
->widget
)){
506 send_message (h
, h
->current
->widget
, WIDGET_DRAW
, 0);
507 send_message (h
, h
->current
->widget
, WIDGET_FOCUS
, 0);
511 int dlg_select_widget (Dlg_head
*h
, void *w
)
513 if (dlg_unfocus (h
)){
514 while (h
->current
->widget
!= w
)
515 h
->current
= h
->current
->next
;
516 while (!dlg_focus (h
))
517 h
->current
= h
->current
->next
;
524 int send_message_to (Dlg_head
*h
, Widget
*w
, int msg
, int par
)
526 Widget_Item
*p
= h
->current
;
530 for (i
= 0; i
< h
->count
; i
++){
531 if (w
== (void *) p
->widget
){
532 v
= send_message (h
, p
->widget
, msg
, par
);
540 #define callback(h) (h->current->widget->callback)
542 void update_cursor (Dlg_head
*h
)
546 if (h
->current
->widget
->options
& W_WANT_CURSOR
)
547 send_message (h
, h
->current
->widget
, WIDGET_CURSOR
, 0);
549 Widget_Item
*p
= h
->current
;
552 if (p
->widget
->options
& W_WANT_CURSOR
)
553 if ((*p
->widget
->callback
)(h
, p
->widget
, WIDGET_CURSOR
, 0)){
558 } while (h
->current
!= p
);
562 /* Redraw the widgets in reverse order, leaving the current widget
565 void dlg_redraw (Dlg_head
*h
)
567 (h
->callback
)(h
, 0, DLG_DRAW
);
569 dlg_broadcast_msg (h
, WIDGET_DRAW
, 1);
574 void dlg_refresh (void *parameter
)
576 dlg_redraw ((Dlg_head
*) parameter
);
579 void dlg_stop (Dlg_head
*h
)
585 static INLINE
void dialog_handle_key (Dlg_head
*h
, int d_key
)
601 hlpfile
= concat_dir_and_file (mc_home
, "mc.hlp");
602 interactive_display (hlpfile
, h
->help_ctx
);
613 /* Use this if the refreshes fail */
625 h
->ret_value
= B_ENTER
;
634 h
->ret_value
= B_CANCEL
;
640 static int dlg_try_hotkey (Dlg_head
*h
, int d_key
)
642 Widget_Item
*hot_cur
;
643 Widget_Item
*previous
;
645 extern input_event ();
648 * Explanation: we don't send letter hotkeys to other widgets if
649 * the currently selected widget is an input line
652 if (h
->current
->widget
->options
& W_IS_INPUT
){
653 if(d_key
< 255 && isalpha(d_key
))
657 /* If it's an alt key, send the message */
659 if (d_key
& ALT(0) && c
< 255 && isalpha(c
))
663 /* .ado: fix problem with file_permission under Win95 */
664 if (d_key
== 0) return 0;
668 if (h
->current
->widget
->options
& W_WANT_HOTKEY
)
669 handled
= callback (h
) (h
, h
->current
->widget
, WIDGET_HOTKEY
, d_key
);
671 /* If not used, send hotkey to other widgets */
675 hot_cur
= h
->current
;
677 /* send it to all widgets */
679 if (hot_cur
->widget
->options
& W_WANT_HOTKEY
)
680 handled
|= (*hot_cur
->widget
->callback
)
681 (h
, hot_cur
->widget
, WIDGET_HOTKEY
, d_key
);
684 hot_cur
= hot_cur
->next
;
685 } while (h
->current
!= hot_cur
&& !handled
);
690 (*h
->callback
) (h
, 0, DLG_HOTKEY_HANDLED
);
691 previous
= h
->current
;
692 if (!dlg_unfocus (h
))
695 h
->current
= hot_cur
;
697 h
->current
= previous
;
703 void dlg_key_event (Dlg_head
*h
, int d_key
)
707 /* TAB used to cycle */
708 if (!h
->raw
&& (d_key
== '\t' || d_key
== KEY_BTAB
))
715 /* first can dlg_callback handle the key */
716 handled
= (*h
->callback
) (h
, d_key
, DLG_KEY
);
718 /* next try the hotkey */
720 handled
= dlg_try_hotkey (h
, d_key
);
722 /* not used - then try widget_callback */
724 handled
|= callback (h
)(h
, h
->current
->widget
, WIDGET_KEY
, d_key
);
726 /* not used- try to use the unhandled case */
728 handled
|= (*h
->callback
) (h
, d_key
, DLG_UNHANDLED_KEY
);
731 dialog_handle_key (h
, d_key
);
732 (*h
->callback
) (h
, d_key
, DLG_POST_KEY
);
736 static INLINE
int dlg_mouse_event (Dlg_head
*h
, Gpm_Event
*event
)
739 Widget_Item
*starting_widget
= h
->current
;
745 /* kludge for the menubar: start at h->first, not current */
746 /* Must be carefull in the insertion order to the dlg list */
747 if (y
== 1 && h
->has_menubar
)
748 starting_widget
= h
->first
;
750 item
= starting_widget
;
752 Widget
*widget
= item
->widget
;
756 if (!((x
> widget
->x
) && (x
<= widget
->x
+widget
->cols
)
757 && (y
> widget
->y
) && (y
<= widget
->y
+widget
->lines
)))
761 new_event
.x
-= widget
->x
;
762 new_event
.y
-= widget
->y
;
764 ret_value
= widget
->mouse
? (*widget
->mouse
) (&new_event
, widget
) :
768 } while (item
!= starting_widget
);
772 /* Run dialog routines */
774 /* Init the process */
775 void init_dlg (Dlg_head
*h
)
781 /* Initialize dialog manager and widgets */
782 (*h
->callback
) (h
, 0, DLG_INIT
);
783 dlg_broadcast_msg (h
, WIDGET_INIT
, 0);
785 if (h
->x
== 0 && h
->y
== 0 && h
->cols
== COLS
&& h
->lines
== LINES
)
786 refresh_mode
= REFRESH_COVERS_ALL
;
788 refresh_mode
= REFRESH_COVERS_PART
;
789 push_refresh (dlg_refresh
, h
, refresh_mode
);
790 h
->refresh_pushed
= 1;
792 /* Initialize direction */
794 h
->current
= h
->first
;
796 if (h
->initfocus
!= NULL
)
797 h
->current
= h
->initfocus
;
799 h
->previous_dialog
= current_dlg
;
802 /* Initialize the mouse status */
805 /* Redraw the screen */
808 while (!dlg_focus (h
))
809 h
->current
= h
->current
->next
;
816 /* Shutdown the run_dlg */
817 void dlg_run_done (Dlg_head
*h
)
819 (*h
->callback
) (h
, h
->current
->dlg_id
, DLG_END
);
820 current_dlg
= (Dlg_head
*) h
->previous_dialog
;
822 x_focus_widget (current_dlg
->current
);
825 void dlg_process_event (Dlg_head
*h
, int key
, Gpm_Event
*event
)
828 if (got_interrupt ())
835 h
->mouse_status
= dlg_mouse_event (h
, event
);
837 dlg_key_event (h
, key
);
840 #ifndef PORT_HAS_FRONTEND_RUN_DLG
842 frontend_run_dlg (Dlg_head
*h
)
849 #if defined(HAVE_SLANG) || NCURSES_VERSION_MAJOR >= 4
850 /* It does not work with ncurses before 1.9.9g, it will break */
852 change_screen_size ();
856 execute_hooks (idle_hook
);
858 while (h
->send_idle_msg
&& is_idle ()){
859 (*h
->callback
) (h
, 0, DLG_IDLE
);
864 (*h
->callback
)(h
, 0, DLG_PRE_EVENT
);
866 /* Clear interrupt flag */
868 d_key
= get_event (&event
, h
->mouse_status
== MOU_REPEAT
, 1);
870 dlg_process_event (h
, d_key
, &event
);
873 #endif /* PORT_HAS_FRONTEND_RUN_DLG */
875 /* Standard run dialog routine
876 * We have to keep this routine small so that we can duplicate it's
877 * behavior on complex routines like the file routines, this way,
878 * they can call the dlg_process_event without rewriting all the code
880 void run_dlg (Dlg_head
*h
)
883 frontend_run_dlg (h
);
888 destroy_dlg (Dlg_head
*h
)
893 if (h
->refresh_pushed
)
896 x_destroy_dlg_start (h
);
897 dlg_broadcast_msg (h
, WIDGET_DESTROY
, 0);
899 for (i
= 0; i
< h
->count
; i
++){
900 if (c
->widget
->destroy
)
901 c
->widget
->destroy (c
->widget
);
903 free (h
->current
->widget
);
915 int std_callback (Dlg_head
*h
, int Msg
, int Par
)
920 void widget_set_size (Widget
*widget
, int y
, int x
, int lines
, int cols
)
925 widget
->lines
= lines
;
928 /* Replace widget old for widget new in the h dialog */
929 void dlg_replace_widget (Dlg_head
*h
, Widget
*old
, Widget
*new)
931 Widget_Item
*p
= h
->current
;
932 int should_focus
= 0;
935 if (p
->widget
== old
){
937 if (old
== h
->current
->widget
)
940 /* We found the widget */
941 /* First kill the widget */
942 new->focused
= old
->focused
;
944 send_message_to (h
, old
, WIDGET_DESTROY
, 0);
945 (*old
->destroy
) (old
);
947 /* We insert the new widget */
949 send_message_to (h
, new, WIDGET_INIT
, 0);
951 if (dlg_focus (h
) == 0)
952 select_a_widget (h
, 1);
954 send_message_to (h
, new, WIDGET_DRAW
, 0);
958 } while (p
!= h
->current
);
961 void widget_redraw (Dlg_head
*h
, Widget_Item
*w
)
963 Widget_Item
*save
= h
->current
;
966 (*w
->widget
->callback
)(h
, h
->current
->widget
, WIDGET_DRAW
, 0);
970 /* Returns the index of h->current from h->first */
971 int dlg_item_number (Dlg_head
*h
)
983 } while (p
!= h
->first
);
984 fprintf (stderr
, "Internal error: current not in dialog list\n\r");
988 int dlg_select_nth_widget (Dlg_head
*h
, int n
)
994 for (i
= 0; i
< n
; i
++)
997 return dlg_select_widget (h
, w
->widget
);
1001 /* Frames must include a trailing dot */
1002 static void tk_frame_proc (Dlg_head
*h
, char *frame
, int new_frame
)
1004 char *s
= strdup (frame
);
1006 if (frame
[strlen (frame
)-1] != '.'){
1007 fprintf (stderr
, "Invalid frame name\n");
1010 s
[strlen (frame
)-1] = 0;
1014 tk_evalf ("frame %s.%s", (char *)h
->wdata
, s
);
1017 /* If passed a null string, it returns */
1018 void tk_new_frame (Dlg_head
*h
, char *frame
)
1022 tk_frame_proc (h
, frame
, 1);
1025 void tk_frame (Dlg_head
*h
, char *frame
)
1027 tk_frame_proc (h
, frame
, 0);
1030 void tk_end_frame ()
1035 void tk_new_frame (Dlg_head
*h
, char *x
)
1039 void tk_frame (Dlg_head
*h
, char *x
)
1043 void tk_end_frame (void)
1048 #ifndef PORT_HAS_DIALOG_TITLE
1050 x_set_dialog_title (Dlg_head
*h
, char *title
)
1052 h
->title
= strdup(title
);