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.
31 #include "key.h" /* For mi_getch() */
33 #include "dialog.h" /* For push_refresh() and pop_refresh() */
37 #define waddc(w,y1,x1,c) move (w->y+y1, w->x+x1); addch (c)
39 /* Primitive way to check if the the current dialog is our dialog */
40 /* This is needed by async routines like load_prompt */
41 Dlg_head
*current_dlg
= 0;
43 /* A hook list for idle events */
46 static void dlg_broadcast_msg_to (Dlg_head
*h
, int message
, int reverse
, int flags
);
48 static void slow_box (Dlg_head
*h
, int y
, int x
, int ys
, int xs
)
50 move (h
->y
+y
, h
->x
+x
);
53 move (h
->y
+y
, h
->x
+x
+xs
-1);
55 move (h
->y
+y
+ys
-1, h
->x
+x
);
59 /* draw box in window */
60 void draw_box (Dlg_head
*h
, int y
, int x
, int ys
, int xs
)
63 slow_box (h
, y
, x
, ys
, xs
);
68 waddc (h
, y
, x
, ACS_ULCORNER
);
69 hline (ACS_HLINE
, xs
- 2);
70 waddc (h
, y
+ ys
- 1, x
, ACS_LLCORNER
);
71 hline (ACS_HLINE
, xs
- 2);
73 waddc (h
, y
, x
+ xs
- 1, ACS_URCORNER
);
74 waddc (h
, y
+ ys
- 1, x
+ xs
- 1, ACS_LRCORNER
);
76 move (h
->y
+y
+1, h
->x
+x
);
77 vline (ACS_VLINE
, ys
- 2);
78 move (h
->y
+y
+1, h
->x
+x
+xs
-1);
79 vline (ACS_VLINE
, ys
- 2);
81 SLsmg_draw_box (h
->y
+y
, h
->x
+x
, ys
, xs
);
82 #endif /* HAVE_SLANG */
85 /* draw box in window */
86 void draw_double_box (Dlg_head
*h
, int y
, int x
, int ys
, int xs
)
89 draw_box (h
, y
, x
, ys
, xs
);
91 SLsmg_draw_double_box (h
->y
+y
, h
->x
+x
, ys
, xs
);
92 #endif /* HAVE_SLANG */
95 void widget_erase (Widget
*w
)
99 for (y
= 0; y
< w
->lines
; y
++){
100 widget_move (w
, y
, 0);
101 for (x
= 0; x
< w
->cols
; x
++)
106 void dlg_erase (Dlg_head
*h
)
110 for (y
= 0; y
< h
->lines
; y
++){
111 move (y
+h
->y
, h
->x
); /* FIXME: should test if ERR */
112 for (x
= 0; x
< h
->cols
; x
++){
119 init_widget (Widget
*w
, int y
, int x
, int lines
, int cols
,
120 callback_fn callback
, destroy_fn destroy
,
121 mouse_h mouse_handler
, char *tkname
)
127 w
->callback
= callback
;
128 w
->destroy
= destroy
;
129 w
->mouse
= mouse_handler
;
133 if (tkname
&& *tkname
== 0) {
134 fprintf (stderr
, "Got a null string for the tkname\n");
137 /* Almost all widgets want to put the cursor in a suitable place */
138 w
->options
= W_WANT_CURSOR
;
141 /* Default callback for widgets */
142 int default_proc (int Msg
, int Par
)
146 case WIDGET_HOTKEY
: /* Didn't use the key */
149 case WIDGET_INIT
: /* We could tell if something went wrong */
153 return 0; /* Didn't use the key */
155 case WIDGET_FOCUS
: /* We accept FOCUSes */
158 case WIDGET_UNFOCUS
: /* We accept loose FOCUSes */
168 /* Move the cursor to the default widget position */
174 printf ("Internal error: unhandled message: %d\n", Msg
);
178 /* Clean the dialog area, draw the frame and the title */
180 common_dialog_repaint (struct Dlg_head
*h
)
184 space
= (h
->flags
& DLG_COMPACT
) ? 0 : 1;
188 draw_box (h
, space
, space
, h
->lines
- 2 * space
, h
->cols
- 2 * space
);
191 attrset (HOT_NORMALC
);
192 dlg_move (h
, space
, (h
->cols
- strlen (h
->title
)) / 2);
197 /* Default dialog callback */
198 int default_dlg_callback (Dlg_head
*h
, int id
, int msg
)
200 if (msg
== DLG_DRAW
&& h
->color
) {
201 common_dialog_repaint (h
);
204 if (msg
== DLG_IDLE
){
205 dlg_broadcast_msg_to (h
, WIDGET_IDLE
, 0, W_WANT_IDLE
);
208 return MSG_NOT_HANDLED
;
212 create_dlg (int y1
, int x1
, int lines
, int cols
, const int *color_set
,
213 dlg_cb_fn callback
, char *help_ctx
, const char *title
,
218 if (flags
& DLG_CENTER
) {
219 y1
= (LINES
- lines
) / 2;
220 x1
= (COLS
- cols
) / 2;
223 if ((flags
& DLG_TRYUP
) && (y1
> 3))
226 new_d
= g_new0 (Dlg_head
, 1);
227 new_d
->color
= color_set
;
228 new_d
->help_ctx
= help_ctx
;
229 new_d
->callback
= callback
? callback
: default_dlg_callback
;
233 new_d
->lines
= lines
;
234 new_d
->flags
= flags
;
236 /* Strip existing spaces, add one space before and after the title */
239 t
= g_strstrip (g_strdup (title
));
240 new_d
->title
= g_strconcat (" ", t
, " ", NULL
);
247 void set_idle_proc (Dlg_head
*d
, int state
)
249 d
->send_idle_msg
= state
;
252 /* add component to dialog buffer */
253 int add_widget (Dlg_head
*where
, void *what
)
256 Widget
*widget
= (Widget
*) what
;
258 /* Don't accept 0 widgets, this could be from widgets that could not */
259 /* initialize properly */
263 widget
->x
+= where
->x
;
264 widget
->y
+= where
->y
;
267 Widget_Item
*point
= where
->current
;
269 where
->current
= g_new (Widget_Item
, 1);
272 where
->current
->next
= point
->next
;
273 where
->current
->prev
= point
;
274 point
->next
->prev
= where
->current
;
275 point
->next
= where
->current
;
277 where
->current
->next
= where
->current
;
278 where
->first
= where
->current
;
279 where
->current
->prev
= where
->first
;
280 where
->last
= where
->current
;
281 where
->first
->next
= where
->last
;
284 back
= where
->current
;
285 where
->current
= g_new (Widget_Item
, 1);
287 back
->prev
= where
->current
;
288 where
->current
->next
= back
;
290 where
->current
->next
= where
->current
;
291 where
->first
= where
->current
;
294 where
->current
->prev
= where
->first
;
295 where
->last
= where
->current
;
296 where
->first
->next
= where
->last
;
299 where
->current
->dlg_id
= where
->count
;
300 where
->current
->widget
= what
;
301 where
->current
->widget
->parent
= where
;
305 /* If the widget is inserted in a running dialog */
306 if (where
->running
) {
307 send_message (widget
, WIDGET_INIT
, 0);
308 send_message (widget
, WIDGET_DRAW
, 0);
310 return (where
->count
- 1);
313 /* broadcast a message to all the widgets in a dialog that have
314 * the options set to flags.
317 dlg_broadcast_msg_to (Dlg_head
*h
, int message
, int reverse
, int flags
)
319 Widget_Item
*p
, *first
, *wi
;
325 first
= p
= h
->current
->prev
;
327 first
= p
= h
->current
->next
;
335 send_message (wi
->widget
, message
, 0);
336 } while (first
!= p
);
339 /* broadcast a message to all the widgets in a dialog */
340 void dlg_broadcast_msg (Dlg_head
*h
, int message
, int reverse
)
342 dlg_broadcast_msg_to (h
, message
, reverse
, ~0);
345 int dlg_focus (Dlg_head
*h
)
350 if (send_message (h
->current
->widget
, WIDGET_FOCUS
, 0)){
351 (*h
->callback
) (h
, h
->current
->dlg_id
, DLG_FOCUS
);
358 dlg_unfocus (Dlg_head
*h
)
363 if (send_message (h
->current
->widget
, WIDGET_UNFOCUS
, 0)){
364 (*h
->callback
) (h
, h
->current
->dlg_id
, DLG_UNFOCUS
);
370 static void select_a_widget (Dlg_head
*h
, int down
)
372 int dir_forward
= !(h
->flags
& DLG_BACKWARD
);
378 dir_forward
= !dir_forward
;
382 h
->current
= h
->current
->next
;
384 h
->current
= h
->current
->prev
;
385 } while (!dlg_focus (h
));
388 /* Return true if the windows overlap */
389 int dlg_overlap (Widget
*a
, Widget
*b
)
391 if ((b
->x
>= a
->x
+ a
->cols
)
392 || (a
->x
>= b
->x
+ b
->cols
)
393 || (b
->y
>= a
->y
+ a
->lines
)
394 || (a
->y
>= b
->y
+ b
->lines
))
400 /* Find the widget with the given callback in the dialog h */
402 find_widget_type (Dlg_head
*h
, callback_fn signature
)
414 for (i
= 0, item
= h
->current
; i
< h
->count
; i
++, item
= item
->next
) {
415 if (item
->widget
->callback
== signature
) {
423 void dlg_one_up (Dlg_head
*h
)
432 /* If it accepts unFOCUSion */
436 select_a_widget (h
, 0);
437 if (dlg_overlap (old
->widget
, h
->current
->widget
)){
438 send_message (h
->current
->widget
, WIDGET_DRAW
, 0);
439 send_message (h
->current
->widget
, WIDGET_FOCUS
, 0);
443 void dlg_one_down (Dlg_head
*h
)
451 if (!dlg_unfocus (h
))
454 select_a_widget (h
, 1);
455 if (dlg_overlap (old
->widget
, h
->current
->widget
)){
456 send_message (h
->current
->widget
, WIDGET_DRAW
, 0);
457 send_message (h
->current
->widget
, WIDGET_FOCUS
, 0);
461 int dlg_select_widget (Dlg_head
*h
, void *w
)
466 if (dlg_unfocus (h
)){
467 while (h
->current
->widget
!= w
)
468 h
->current
= h
->current
->next
;
469 while (!dlg_focus (h
))
470 h
->current
= h
->current
->next
;
477 #define callback(h) (h->current->widget->callback)
479 void update_cursor (Dlg_head
*h
)
483 if (h
->current
->widget
->options
& W_WANT_CURSOR
)
484 send_message (h
->current
->widget
, WIDGET_CURSOR
, 0);
486 Widget_Item
*p
= h
->current
;
489 if (p
->widget
->options
& W_WANT_CURSOR
)
490 if ((*p
->widget
->callback
)(p
->widget
, WIDGET_CURSOR
, 0)){
494 } while (h
->current
!= p
);
498 /* Redraw the widgets in reverse order, leaving the current widget
501 void dlg_redraw (Dlg_head
*h
)
503 (h
->callback
)(h
, 0, DLG_DRAW
);
505 dlg_broadcast_msg (h
, WIDGET_DRAW
, 1);
511 dlg_refresh (void *parameter
)
513 dlg_redraw ((Dlg_head
*) parameter
);
516 void dlg_stop (Dlg_head
*h
)
521 static inline void dialog_handle_key (Dlg_head
*h
, int d_key
)
535 interactive_display (NULL
, h
->help_ctx
);
545 /* Use this if the refreshes fail */
550 #endif /* HAVE_SLANG */
557 h
->ret_value
= B_ENTER
;
565 h
->ret_value
= B_CANCEL
;
571 static int dlg_try_hotkey (Dlg_head
*h
, int d_key
)
573 Widget_Item
*hot_cur
;
574 Widget_Item
*previous
;
581 * Explanation: we don't send letter hotkeys to other widgets if
582 * the currently selected widget is an input line
585 if (h
->current
->widget
->options
& W_IS_INPUT
){
586 if(d_key
< 255 && isalpha(d_key
))
590 /* If it's an alt key, send the message */
592 if (d_key
& ALT(0) && c
< 255 && isalpha(c
))
596 /* .ado: fix problem with file_permission under Win95 */
597 if (d_key
== 0) return 0;
601 if (h
->current
->widget
->options
& W_WANT_HOTKEY
)
602 handled
= callback (h
) (h
->current
->widget
, WIDGET_HOTKEY
, d_key
);
604 /* If not used, send hotkey to other widgets */
608 hot_cur
= h
->current
;
610 /* send it to all widgets */
612 if (hot_cur
->widget
->options
& W_WANT_HOTKEY
)
613 handled
|= (*hot_cur
->widget
->callback
)
614 (hot_cur
->widget
, WIDGET_HOTKEY
, d_key
);
617 hot_cur
= hot_cur
->next
;
618 } while (h
->current
!= hot_cur
&& !handled
);
623 (*h
->callback
) (h
, 0, DLG_HOTKEY_HANDLED
);
624 previous
= h
->current
;
625 if (!dlg_unfocus (h
))
628 h
->current
= hot_cur
;
630 h
->current
= previous
;
637 dlg_key_event (Dlg_head
* h
, int d_key
)
644 /* TAB used to cycle */
645 if (!(h
->flags
& DLG_WANT_TAB
)
646 && (d_key
== '\t' || d_key
== KEY_BTAB
)) {
653 /* first can dlg_callback handle the key */
654 handled
= (*h
->callback
) (h
, d_key
, DLG_KEY
);
656 /* next try the hotkey */
658 handled
= dlg_try_hotkey (h
, d_key
);
660 /* not used - then try widget_callback */
663 callback (h
) (h
->current
->widget
, WIDGET_KEY
, d_key
);
665 /* not used- try to use the unhandled case */
667 handled
|= (*h
->callback
) (h
, d_key
, DLG_UNHANDLED_KEY
);
670 dialog_handle_key (h
, d_key
);
671 (*h
->callback
) (h
, d_key
, DLG_POST_KEY
);
679 dlg_mouse_event (Dlg_head
* h
, Gpm_Event
* event
)
682 Widget_Item
*starting_widget
= h
->current
;
687 /* kludge for the menubar: start at h->first, not current */
688 /* Must be careful in the insertion order to the dlg list */
689 if (y
== 1 && (h
->flags
& DLG_HAS_MENUBAR
))
690 starting_widget
= h
->first
;
692 item
= starting_widget
;
694 Widget
*widget
= item
->widget
;
698 if (!((x
> widget
->x
) && (x
<= widget
->x
+ widget
->cols
)
699 && (y
> widget
->y
) && (y
<= widget
->y
+ widget
->lines
)))
703 new_event
.x
-= widget
->x
;
704 new_event
.y
-= widget
->y
;
709 return (*widget
->mouse
) (&new_event
, widget
);
710 } while (item
!= starting_widget
);
715 /* Run dialog routines */
717 /* Init the process */
718 void init_dlg (Dlg_head
*h
)
722 /* Initialize dialog manager and widgets */
723 (*h
->callback
) (h
, 0, DLG_INIT
);
724 dlg_broadcast_msg (h
, WIDGET_INIT
, 0);
726 if (h
->x
== 0 && h
->y
== 0 && h
->cols
== COLS
&& h
->lines
== LINES
)
727 refresh_mode
= REFRESH_COVERS_ALL
;
729 refresh_mode
= REFRESH_COVERS_PART
;
731 push_refresh (dlg_refresh
, h
, refresh_mode
);
732 h
->refresh_pushed
= 1;
734 /* Initialize direction */
735 if (h
->flags
& DLG_BACKWARD
)
736 h
->current
= h
->first
;
738 if (h
->initfocus
!= NULL
)
739 h
->current
= h
->initfocus
;
741 h
->previous_dialog
= current_dlg
;
744 /* Initialize the mouse status */
745 h
->mouse_status
= MOU_NORMAL
;
747 /* Select the first widget that takes focus */
748 while (!dlg_focus (h
) && h
->current
)
749 h
->current
= h
->current
->next
;
751 /* Redraw the screen */
758 /* Shutdown the run_dlg */
759 void dlg_run_done (Dlg_head
*h
)
762 (*h
->callback
) (h
, h
->current
->dlg_id
, DLG_END
);
764 current_dlg
= (Dlg_head
*) h
->previous_dialog
;
767 void dlg_process_event (Dlg_head
*h
, int key
, Gpm_Event
*event
)
770 if (got_interrupt ())
777 h
->mouse_status
= dlg_mouse_event (h
, event
);
779 dlg_key_event (h
, key
);
783 frontend_run_dlg (Dlg_head
*h
)
791 change_screen_size ();
795 execute_hooks (idle_hook
);
797 while (h
->send_idle_msg
&& is_idle ()) {
798 (*h
->callback
) (h
, 0, DLG_IDLE
);
801 /* Allow terminating the dialog from the idle handler */
807 (*h
->callback
) (h
, 0, DLG_PRE_EVENT
);
809 /* Clear interrupt flag */
811 d_key
= get_event (&event
, h
->mouse_status
== MOU_REPEAT
, 1);
813 dlg_process_event (h
, d_key
, &event
);
816 (*h
->callback
) (h
, 0, DLG_VALIDATE
);
820 /* Standard run dialog routine
821 * We have to keep this routine small so that we can duplicate it's
822 * behavior on complex routines like the file routines, this way,
823 * they can call the dlg_process_event without rewriting all the code
825 int run_dlg (Dlg_head
*h
)
828 frontend_run_dlg (h
);
834 destroy_dlg (Dlg_head
*h
)
839 if (h
->refresh_pushed
)
842 dlg_broadcast_msg (h
, WIDGET_DESTROY
, 0);
844 for (i
= 0; i
< h
->count
; i
++){
845 if (c
->widget
->destroy
)
846 c
->widget
->destroy (c
->widget
);
849 g_free (h
->current
->widget
);
860 void widget_set_size (Widget
*widget
, int y
, int x
, int lines
, int cols
)
865 widget
->lines
= lines
;
868 /* Replace widget old for widget new in the h dialog */
869 void dlg_replace_widget (Dlg_head
*h
, Widget
*old
, Widget
*new)
871 Widget_Item
*p
= h
->current
;
872 int should_focus
= 0;
878 if (p
->widget
== old
){
880 if (old
== h
->current
->widget
)
883 /* We found the widget */
884 /* First kill the widget */
886 send_message (old
, WIDGET_DESTROY
, 0);
887 (*old
->destroy
) (old
);
889 /* We insert the new widget */
891 send_message (new, WIDGET_INIT
, 0);
893 if (dlg_focus (h
) == 0)
894 select_a_widget (h
, 1);
896 send_message (new, WIDGET_DRAW
, 0);
900 } while (p
!= h
->current
);
903 /* Returns the index of h->current from h->first */
904 int dlg_item_number (Dlg_head
*h
)
916 } while (p
!= h
->first
);
917 fprintf (stderr
, "Internal error: current not in dialog list\n\r");
921 int dlg_select_nth_widget (Dlg_head
*h
, int n
)
927 for (i
= 0; i
< n
; i
++)
930 return dlg_select_widget (h
, w
->widget
);