2 Widget based utility functions.
4 Copyright (C) 1994-2024
5 Free Software Foundation, Inc.
8 Miguel de Icaza, 1994, 1995, 1996
9 Radek Doulik, 1994, 1995
11 Andrej Borsenkow, 1995
12 Andrew Borodin <aborodin@vmail.ru>, 2009-2022
14 This file is part of the Midnight Commander.
16 The Midnight Commander is free software: you can redistribute it
17 and/or modify it under the terms of the GNU General Public License as
18 published by the Free Software Foundation, either version 3 of the License,
19 or (at your option) any later version.
21 The Midnight Commander is distributed in the hope that it will be useful,
22 but WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 GNU General Public License for more details.
26 You should have received a copy of the GNU General Public License
27 along with this program. If not, see <http://www.gnu.org/licenses/>.
31 * \brief Source: quick dialog engine
37 #include <stdio.h> /* fprintf() */
39 #include "lib/global.h"
40 #include "lib/strutil.h" /* str_term_width1() */
41 #include "lib/util.h" /* tilde_expand() */
42 #include "lib/widget.h"
44 /*** global variables ****************************************************************************/
46 /*** file scope macro definitions ****************************************************************/
49 #define I18N(x) (x = x != NULL && *x != '\0' ? _(x) : x)
51 #define I18N(x) (x = x)
54 /*** file scope type declarations ****************************************************************/
59 quick_widget_t
*quick_widget
;
60 } quick_widget_item_t
;
62 /*** forward declarations (file scope functions) *************************************************/
64 /*** file scope variables ************************************************************************/
66 /* --------------------------------------------------------------------------------------------- */
67 /*** file scope functions ************************************************************************/
68 /* --------------------------------------------------------------------------------------------- */
71 quick_create_input (int y
, int x
, const quick_widget_t
*qw
)
75 in
= input_new (y
, x
, input_colors
, 8, qw
->u
.input
.text
, qw
->u
.input
.histname
,
76 qw
->u
.input
.completion_flags
);
78 in
->is_password
= qw
->u
.input
.is_passwd
;
79 in
->strip_password
= qw
->u
.input
.strip_passwd
;
84 /* --------------------------------------------------------------------------------------------- */
87 quick_create_labeled_input (GArray
*widgets
, int *y
, int x
, quick_widget_t
*quick_widget
,
90 quick_widget_item_t in
, label
;
92 label
.quick_widget
= g_new0 (quick_widget_t
, 1);
93 label
.quick_widget
->widget_type
= quick_label
;
94 label
.quick_widget
->options
= quick_widget
->options
;
95 label
.quick_widget
->state
= quick_widget
->state
;
96 /* FIXME: this should be turned in depend of label_location */
97 label
.quick_widget
->pos_flags
= quick_widget
->pos_flags
;
99 switch (quick_widget
->u
.input
.label_location
)
101 case input_label_above
:
102 label
.widget
= WIDGET (label_new (*y
, x
, I18N (quick_widget
->u
.input
.label_text
)));
103 *y
+= label
.widget
->rect
.lines
- 1;
104 g_array_append_val (widgets
, label
);
106 in
.widget
= WIDGET (quick_create_input (++(*y
), x
, quick_widget
));
107 in
.quick_widget
= quick_widget
;
108 g_array_append_val (widgets
, in
);
110 *width
= MAX (label
.widget
->rect
.cols
, in
.widget
->rect
.cols
);
113 case input_label_left
:
114 label
.widget
= WIDGET (label_new (*y
, x
, I18N (quick_widget
->u
.input
.label_text
)));
115 g_array_append_val (widgets
, label
);
117 in
.widget
= WIDGET (quick_create_input (*y
, x
+ label
.widget
->rect
.cols
+ 1, quick_widget
));
118 in
.quick_widget
= quick_widget
;
119 g_array_append_val (widgets
, in
);
121 *width
= label
.widget
->rect
.cols
+ in
.widget
->rect
.cols
+ 1;
124 case input_label_right
:
125 in
.widget
= WIDGET (quick_create_input (*y
, x
, quick_widget
));
126 in
.quick_widget
= quick_widget
;
127 g_array_append_val (widgets
, in
);
131 (*y
, x
+ in
.widget
->rect
.cols
+ 1, I18N (quick_widget
->u
.input
.label_text
)));
132 g_array_append_val (widgets
, label
);
134 *width
= label
.widget
->rect
.cols
+ in
.widget
->rect
.cols
+ 1;
137 case input_label_below
:
138 in
.widget
= WIDGET (quick_create_input (*y
, x
, quick_widget
));
139 in
.quick_widget
= quick_widget
;
140 g_array_append_val (widgets
, in
);
142 label
.widget
= WIDGET (label_new (++(*y
), x
, I18N (quick_widget
->u
.input
.label_text
)));
143 *y
+= label
.widget
->rect
.lines
- 1;
144 g_array_append_val (widgets
, label
);
146 *width
= MAX (label
.widget
->rect
.cols
, in
.widget
->rect
.cols
);
153 INPUT (in
.widget
)->label
= LABEL (label
.widget
);
154 /* cross references */
155 label
.quick_widget
->u
.label
.input
= in
.quick_widget
;
156 in
.quick_widget
->u
.input
.label
= label
.quick_widget
;
159 /* --------------------------------------------------------------------------------------------- */
160 /*** public functions ****************************************************************************/
161 /* --------------------------------------------------------------------------------------------- */
164 quick_dialog_skip (quick_dialog_t
*quick_dlg
, int nskip
)
168 int x
, y
; /* current positions */
169 int y1
= 0; /* bottom of 1st column in case of two columns */
170 int y2
= -1; /* start of two columns */
171 int width1
= 0; /* width of single column */
172 int width2
= 0; /* width of each of two columns */
173 gboolean have_groupbox
= FALSE
;
174 gboolean two_columns
= FALSE
;
175 gboolean put_buttons
= FALSE
;
177 /* x position of 1st column is 3 */
179 /* x position of 2nd column is 4 and it will be fixed later, after creation of all widgets */
184 quick_widget_t
*quick_widget
;
187 GList
*input_labels
= NULL
; /* Widgets not directly requested by the user. */
190 len
= str_term_width1 (I18N (quick_dlg
->title
)) + 6;
191 quick_dlg
->rect
.cols
= MAX (quick_dlg
->rect
.cols
, len
);
197 widgets
= g_array_sized_new (FALSE
, FALSE
, sizeof (quick_widget_item_t
), 8);
199 for (quick_widget
= quick_dlg
->widgets
; quick_widget
->widget_type
!= quick_end
; quick_widget
++)
201 quick_widget_item_t item
= { NULL
, quick_widget
};
204 switch (quick_widget
->widget_type
)
209 (++y
, x
, *quick_widget
->u
.checkbox
.state
,
210 I18N (quick_widget
->u
.checkbox
.text
)));
211 g_array_append_val (widgets
, item
);
212 width
= item
.widget
->rect
.cols
;
216 width2
= MAX (width2
, width
);
218 width1
= MAX (width1
, width
);
223 item
.widget
= WIDGET (button_new (++y
, x
, quick_widget
->u
.button
.action
,
224 quick_widget
->u
.button
.action
== B_ENTER
?
225 DEFPUSH_BUTTON
: NORMAL_BUTTON
,
226 I18N (quick_widget
->u
.button
.text
),
227 quick_widget
->u
.button
.callback
));
228 g_array_append_val (widgets
, item
);
229 width
= item
.widget
->rect
.cols
;
233 width2
= MAX (width2
, width
);
235 width1
= MAX (width1
, width
);
239 *quick_widget
->u
.input
.result
= NULL
;
241 if (quick_widget
->u
.input
.label_location
!= input_label_none
)
243 quick_create_labeled_input (widgets
, &y
, x
, quick_widget
, &width
);
244 input_labels
= g_list_prepend (input_labels
, quick_widget
->u
.input
.label
);
248 item
.widget
= WIDGET (quick_create_input (y
, x
, quick_widget
));
249 g_array_append_val (widgets
, item
);
250 width
= item
.widget
->rect
.cols
;
255 width2
= MAX (width2
, width
);
257 width1
= MAX (width1
, width
);
261 item
.widget
= WIDGET (label_new (++y
, x
, I18N (quick_widget
->u
.label
.text
)));
262 g_array_append_val (widgets
, item
);
263 y
+= item
.widget
->rect
.lines
- 1;
264 width
= item
.widget
->rect
.cols
;
268 width2
= MAX (width2
, width
);
270 width1
= MAX (width1
, width
);
278 /* create the copy of radio_items to avoid mwmory leak */
279 items
= g_new (char *, quick_widget
->u
.radio
.count
+ 1);
280 for (i
= 0; i
< (size_t) quick_widget
->u
.radio
.count
; i
++)
281 items
[i
] = g_strdup (_(quick_widget
->u
.radio
.items
[i
]));
284 r
= radio_new (++y
, x
, quick_widget
->u
.radio
.count
, (const char **) items
);
285 r
->pos
= r
->sel
= *quick_widget
->u
.radio
.value
;
287 item
.widget
= WIDGET (r
);
288 g_array_append_val (widgets
, item
);
289 y
+= item
.widget
->rect
.lines
- 1;
290 width
= item
.widget
->rect
.cols
;
294 width2
= MAX (width2
, width
);
296 width1
= MAX (width1
, width
);
300 case quick_start_groupbox
:
301 I18N (quick_widget
->u
.groupbox
.title
);
302 len
= str_term_width1 (quick_widget
->u
.groupbox
.title
);
303 g
= groupbox_new (++y
, x
, 1, len
+ 4, quick_widget
->u
.groupbox
.title
);
304 item
.widget
= WIDGET (g
);
305 g_array_append_val (widgets
, item
);
306 have_groupbox
= TRUE
;
309 case quick_stop_groupbox
:
312 Widget
*w
= WIDGET (g
);
315 w
->rect
.lines
= y
+ 1 - w
->rect
.y
;
318 g_array_append_val (widgets
, item
);
322 case quick_separator
:
324 if (quick_widget
->u
.separator
.line
)
326 item
.widget
= WIDGET (hline_new (y
, x
, 1));
327 g_array_append_val (widgets
, item
);
331 case quick_start_columns
:
333 g_array_append_val (widgets
, item
);
337 case quick_next_column
:
343 case quick_stop_columns
:
346 g_array_append_val (widgets
, item
);
351 /* start put several buttons in bottom line */
352 if (quick_widget
->u
.separator
.space
)
356 if (quick_widget
->u
.separator
.line
)
357 item
.widget
= WIDGET (hline_new (y
, 1, -1));
360 g_array_append_val (widgets
, item
);
362 /* several buttons in bottom line */
365 for (; quick_widget
->widget_type
== quick_button
; quick_widget
++)
367 item
.widget
= WIDGET (button_new (y
, x
++, quick_widget
->u
.button
.action
,
368 quick_widget
->u
.button
.action
== B_ENTER
?
369 DEFPUSH_BUTTON
: NORMAL_BUTTON
,
370 I18N (quick_widget
->u
.button
.text
),
371 quick_widget
->u
.button
.callback
));
372 item
.quick_widget
= quick_widget
;
373 g_array_append_val (widgets
, item
);
374 blen
+= item
.widget
->rect
.cols
+ 1;
377 /* stop dialog build here */
379 quick_widget
->widget_type
= quick_end
;
388 /* adjust dialog width */
389 quick_dlg
->rect
.cols
= MAX (quick_dlg
->rect
.cols
, blen
+ 6);
401 len
= width2
* 2 + 7;
403 len
= MAX (len
, width1
+ 6);
406 quick_dlg
->rect
.cols
= MAX (quick_dlg
->rect
.cols
, len
);
407 width1
= quick_dlg
->rect
.cols
- 6;
408 width2
= (quick_dlg
->rect
.cols
- 7) / 2;
410 if (quick_dlg
->rect
.x
== -1 || quick_dlg
->rect
.y
== -1)
411 dd
= dlg_create (TRUE
, 0, 0, y
+ 3, quick_dlg
->rect
.cols
, WPOS_CENTER
| WPOS_TRYUP
, FALSE
,
412 dialog_colors
, quick_dlg
->callback
, quick_dlg
->mouse_callback
,
413 quick_dlg
->help
, quick_dlg
->title
);
415 dd
= dlg_create (TRUE
, quick_dlg
->rect
.y
, quick_dlg
->rect
.x
, y
+ 3, quick_dlg
->rect
.cols
,
416 WPOS_KEEP_DEFAULT
, FALSE
, dialog_colors
, quick_dlg
->callback
,
417 quick_dlg
->mouse_callback
, quick_dlg
->help
, quick_dlg
->title
);
419 /* add widgets into the dialog */
420 x2
= x1
+ width2
+ 1;
423 x
= (WIDGET (dd
)->rect
.cols
- blen
) / 2;
425 for (i
= 0; i
< widgets
->len
; i
++)
427 quick_widget_item_t
*item
;
431 item
= &g_array_index (widgets
, quick_widget_item_t
, i
);
432 r
= &item
->widget
->rect
;
433 column_width
= two_columns
? width2
: width1
;
435 /* adjust widget width and x position */
436 switch (item
->quick_widget
->widget_type
)
440 quick_widget_t
*input
= item
->quick_widget
->u
.label
.input
;
442 if (input
!= NULL
&& input
->u
.input
.label_location
== input_label_right
)
444 /* location of this label will be adjusted later */
474 Widget
*label
= WIDGET (INPUT (item
->widget
)->label
);
475 int width
= column_width
;
480 switch (item
->quick_widget
->u
.input
.label_location
)
482 case input_label_left
:
483 /* label was adjusted before; adjust input line */
484 r
->x
= label
->rect
.x
+ label
->rect
.cols
+ 1 - WIDGET (label
->owner
)->rect
.x
;
485 r
->cols
= width
- label
->rect
.cols
- 1;
488 case input_label_right
:
493 r
->cols
= width
- label
->rect
.cols
- 1;
494 label
->rect
.x
= r
->x
+ r
->cols
+ 1;
506 /* forced update internal variables of input line */
508 widget_set_size_rect (item
->widget
, r
);
512 case quick_start_groupbox
:
513 g
= GROUPBOX (item
->widget
);
516 r
->cols
= column_width
;
519 case quick_stop_groupbox
:
523 case quick_separator
:
524 if (item
->widget
!= NULL
)
528 Widget
*wg
= WIDGET (g
);
530 HLINE (item
->widget
)->auto_adjust_cols
= FALSE
;
531 r
->x
= wg
->rect
.x
+ 1 - WIDGET (wg
->owner
)->rect
.x
;
532 r
->cols
= wg
->rect
.cols
;
534 else if (two_columns
)
536 HLINE (item
->widget
)->auto_adjust_cols
= FALSE
;
540 r
->cols
= column_width
+ 2;
543 HLINE (item
->widget
)->auto_adjust_cols
= TRUE
;
547 case quick_start_columns
:
551 case quick_stop_columns
:
556 /* several buttons in bottom line */
564 if (item
->widget
!= NULL
)
568 /* add widget into dialog */
569 item
->widget
->options
|= item
->quick_widget
->options
; /* FIXME: cannot reset flags, setup only */
570 item
->widget
->state
|= item
->quick_widget
->state
; /* FIXME: cannot reset flags, setup only */
571 id
= group_add_widget_autopos (GROUP (dd
), item
->widget
, item
->quick_widget
->pos_flags
,
573 if (item
->quick_widget
->id
!= NULL
)
574 *item
->quick_widget
->id
= id
;
578 /* skip frame widget */
583 group_set_current_widget_next (GROUP (dd
));
585 return_val
= dlg_run (dd
);
587 /* Get the data if we found something interesting */
588 if (return_val
!= B_CANCEL
)
589 for (i
= 0; i
< widgets
->len
; i
++)
591 quick_widget_item_t
*item
;
593 item
= &g_array_index (widgets
, quick_widget_item_t
, i
);
595 switch (item
->quick_widget
->widget_type
)
598 *item
->quick_widget
->u
.checkbox
.state
= CHECK (item
->widget
)->state
;
602 if ((item
->quick_widget
->u
.input
.completion_flags
& INPUT_COMPLETE_CD
) != 0)
603 *item
->quick_widget
->u
.input
.result
=
604 tilde_expand (input_get_ctext (INPUT (item
->widget
)));
606 *item
->quick_widget
->u
.input
.result
= input_get_text (INPUT (item
->widget
));
610 *item
->quick_widget
->u
.radio
.value
= RADIO (item
->widget
)->sel
;
618 widget_destroy (WIDGET (dd
));
620 g_list_free_full (input_labels
, g_free
); /* destroy input labels created before */
621 g_array_free (widgets
, TRUE
);
626 /* --------------------------------------------------------------------------------------------- */