Move widget add/del API from WDialog to WGroup.
[midnight-commander.git] / lib / widget / quick.c
blobee2837bdc92d44c9c44918336952935fef9876ac
1 /*
2 Widget based utility functions.
4 Copyright (C) 1994-2020
5 Free Software Foundation, Inc.
7 Authors:
8 Miguel de Icaza, 1994, 1995, 1996
9 Radek Doulik, 1994, 1995
10 Jakub Jelinek, 1995
11 Andrej Borsenkow, 1995
12 Andrew Borodin <aborodin@vmail.ru>, 2009, 2010, 2011, 2012, 2013
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/>.
30 /** \file quick.c
31 * \brief Source: quick dialog engine
34 #include <config.h>
36 #include <stdlib.h>
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 ****************************************************************/
48 #ifdef ENABLE_NLS
49 #define I18N(x) (x = x != NULL && *x != '\0' ? _(x) : x)
50 #else
51 #define I18N(x) (x = x)
52 #endif
54 /*** file scope type declarations ****************************************************************/
56 typedef struct
58 Widget *widget;
59 quick_widget_t *quick_widget;
60 } quick_widget_item_t;
62 /*** file scope variables ************************************************************************/
64 /* --------------------------------------------------------------------------------------------- */
65 /*** file scope functions ************************************************************************/
66 /* --------------------------------------------------------------------------------------------- */
68 static WInput *
69 quick_create_input (int y, int x, const quick_widget_t * qw)
71 WInput *in;
73 in = input_new (y, x, input_colors, 8, qw->u.input.text, qw->u.input.histname,
74 qw->u.input.completion_flags);
76 in->is_password = qw->u.input.is_passwd;
77 in->strip_password = qw->u.input.strip_passwd;
79 return in;
82 /* --------------------------------------------------------------------------------------------- */
84 static void
85 quick_create_labeled_input (GArray * widgets, int *y, int x, quick_widget_t * quick_widget,
86 int *width)
88 quick_widget_item_t in, label;
90 label.quick_widget = g_new0 (quick_widget_t, 1);
91 label.quick_widget->widget_type = quick_label;
92 label.quick_widget->options = quick_widget->options;
93 label.quick_widget->state = quick_widget->state;
94 /* FIXME: this should be turned in depend of label_location */
95 label.quick_widget->pos_flags = quick_widget->pos_flags;
97 switch (quick_widget->u.input.label_location)
99 case input_label_above:
100 label.widget = WIDGET (label_new (*y, x, I18N (quick_widget->u.input.label_text)));
101 *y += label.widget->lines - 1;
102 g_array_append_val (widgets, label);
104 in.widget = WIDGET (quick_create_input (++(*y), x, quick_widget));
105 in.quick_widget = quick_widget;
106 g_array_append_val (widgets, in);
108 *width = MAX (label.widget->cols, in.widget->cols);
109 break;
111 case input_label_left:
112 label.widget = WIDGET (label_new (*y, x, I18N (quick_widget->u.input.label_text)));
113 g_array_append_val (widgets, label);
115 in.widget = WIDGET (quick_create_input (*y, x + label.widget->cols + 1, quick_widget));
116 in.quick_widget = quick_widget;
117 g_array_append_val (widgets, in);
119 *width = label.widget->cols + in.widget->cols + 1;
120 break;
122 case input_label_right:
123 in.widget = WIDGET (quick_create_input (*y, x, quick_widget));
124 in.quick_widget = quick_widget;
125 g_array_append_val (widgets, in);
127 label.widget =
128 WIDGET (label_new
129 (*y, x + in.widget->cols + 1, I18N (quick_widget->u.input.label_text)));
130 g_array_append_val (widgets, label);
132 *width = label.widget->cols + in.widget->cols + 1;
133 break;
135 case input_label_below:
136 in.widget = WIDGET (quick_create_input (*y, x, quick_widget));
137 in.quick_widget = quick_widget;
138 g_array_append_val (widgets, in);
140 label.widget = WIDGET (label_new (++(*y), x, I18N (quick_widget->u.input.label_text)));
141 *y += label.widget->lines - 1;
142 g_array_append_val (widgets, label);
144 *width = MAX (label.widget->cols, in.widget->cols);
145 break;
147 default:
148 return;
151 INPUT (in.widget)->label = LABEL (label.widget);
152 /* cross references */
153 label.quick_widget->u.label.input = in.quick_widget;
154 in.quick_widget->u.input.label = label.quick_widget;
157 /* --------------------------------------------------------------------------------------------- */
158 /*** public functions ****************************************************************************/
159 /* --------------------------------------------------------------------------------------------- */
162 quick_dialog_skip (quick_dialog_t * quick_dlg, int nskip)
164 int len;
165 int blen = 0;
166 int x, y; /* current positions */
167 int y1 = 0; /* bottom of 1st column in case of two columns */
168 int y2 = -1; /* start of two columns */
169 int width1 = 0; /* width of single column */
170 int width2 = 0; /* width of each of two columns */
171 gboolean have_groupbox = FALSE;
172 gboolean two_columns = FALSE;
173 gboolean put_buttons = FALSE;
175 /* x position of 1st column is 3 */
176 const int x1 = 3;
177 /* x position of 2nd column is 4 and it will be fixed later, after creation of all widgets */
178 int x2 = 4;
180 GArray *widgets;
181 size_t i;
182 quick_widget_t *quick_widget;
183 WGroupbox *g = NULL;
184 WDialog *dd;
185 GList *input_labels = NULL; /* Widgets not directly requested by the user. */
186 int return_val;
188 len = str_term_width1 (I18N (quick_dlg->title)) + 6;
189 quick_dlg->cols = MAX (quick_dlg->cols, len);
191 y = 1;
192 x = x1;
194 /* create widgets */
195 widgets = g_array_sized_new (FALSE, FALSE, sizeof (quick_widget_item_t), 8);
197 for (quick_widget = quick_dlg->widgets; quick_widget->widget_type != quick_end; quick_widget++)
199 quick_widget_item_t item = { NULL, quick_widget };
200 int width = 0;
202 switch (quick_widget->widget_type)
204 case quick_checkbox:
205 item.widget =
206 WIDGET (check_new
207 (++y, x, *quick_widget->u.checkbox.state,
208 I18N (quick_widget->u.checkbox.text)));
209 g_array_append_val (widgets, item);
210 width = item.widget->cols;
211 if (g != NULL)
212 width += 2;
213 if (two_columns)
214 width2 = MAX (width2, width);
215 else
216 width1 = MAX (width1, width);
217 break;
219 case quick_button:
220 /* single button */
221 item.widget = WIDGET (button_new (++y, x, quick_widget->u.button.action,
222 quick_widget->u.button.action == B_ENTER ?
223 DEFPUSH_BUTTON : NORMAL_BUTTON,
224 I18N (quick_widget->u.button.text),
225 quick_widget->u.button.callback));
226 g_array_append_val (widgets, item);
227 width = item.widget->cols;
228 if (g != NULL)
229 width += 2;
230 if (two_columns)
231 width2 = MAX (width2, width);
232 else
233 width1 = MAX (width1, width);
234 break;
236 case quick_input:
237 *quick_widget->u.input.result = NULL;
238 y++;
239 if (quick_widget->u.input.label_location != input_label_none)
241 quick_create_labeled_input (widgets, &y, x, quick_widget, &width);
242 input_labels = g_list_prepend (input_labels, quick_widget->u.input.label);
244 else
246 item.widget = WIDGET (quick_create_input (y, x, quick_widget));
247 g_array_append_val (widgets, item);
248 width = item.widget->cols;
250 if (g != NULL)
251 width += 2;
252 if (two_columns)
253 width2 = MAX (width2, width);
254 else
255 width1 = MAX (width1, width);
256 break;
258 case quick_label:
259 item.widget = WIDGET (label_new (++y, x, I18N (quick_widget->u.label.text)));
260 g_array_append_val (widgets, item);
261 y += item.widget->lines - 1;
262 width = item.widget->cols;
263 if (g != NULL)
264 width += 2;
265 if (two_columns)
266 width2 = MAX (width2, width);
267 else
268 width1 = MAX (width1, width);
269 break;
271 case quick_radio:
273 WRadio *r;
274 char **items = NULL;
276 /* create the copy of radio_items to avoid mwmory leak */
277 items = g_new (char *, quick_widget->u.radio.count + 1);
278 for (i = 0; i < (size_t) quick_widget->u.radio.count; i++)
279 items[i] = g_strdup (_(quick_widget->u.radio.items[i]));
280 items[i] = NULL;
282 r = radio_new (++y, x, quick_widget->u.radio.count, (const char **) items);
283 r->pos = r->sel = *quick_widget->u.radio.value;
284 g_strfreev (items);
285 item.widget = WIDGET (r);
286 g_array_append_val (widgets, item);
287 y += item.widget->lines - 1;
288 width = item.widget->cols;
289 if (g != NULL)
290 width += 2;
291 if (two_columns)
292 width2 = MAX (width2, width);
293 else
294 width1 = MAX (width1, width);
296 break;
298 case quick_start_groupbox:
299 I18N (quick_widget->u.groupbox.title);
300 len = str_term_width1 (quick_widget->u.groupbox.title);
301 g = groupbox_new (++y, x, 1, len + 4, quick_widget->u.groupbox.title);
302 item.widget = WIDGET (g);
303 g_array_append_val (widgets, item);
304 have_groupbox = TRUE;
305 break;
307 case quick_stop_groupbox:
308 if (g != NULL)
310 Widget *w = WIDGET (g);
312 y++;
313 w->lines = y + 1 - w->y;
314 g = NULL;
316 g_array_append_val (widgets, item);
318 break;
320 case quick_separator:
321 y++;
322 if (quick_widget->u.separator.line)
324 item.widget = WIDGET (hline_new (y, x, 1));
325 g_array_append_val (widgets, item);
327 break;
329 case quick_start_columns:
330 y2 = y;
331 g_array_append_val (widgets, item);
332 two_columns = TRUE;
333 break;
335 case quick_next_column:
336 x = x2;
337 y1 = y;
338 y = y2;
339 break;
341 case quick_stop_columns:
342 x = x1;
343 y = MAX (y1, y);
344 g_array_append_val (widgets, item);
345 two_columns = FALSE;
346 break;
348 case quick_buttons:
349 /* start put several buttons in bottom line */
350 if (quick_widget->u.separator.space)
352 y++;
354 if (quick_widget->u.separator.line)
355 item.widget = WIDGET (hline_new (y, 1, -1));
358 g_array_append_val (widgets, item);
360 /* several buttons in bottom line */
361 y++;
362 quick_widget++;
363 for (; quick_widget->widget_type == quick_button; quick_widget++)
365 item.widget = WIDGET (button_new (y, x++, quick_widget->u.button.action,
366 quick_widget->u.button.action == B_ENTER ?
367 DEFPUSH_BUTTON : NORMAL_BUTTON,
368 I18N (quick_widget->u.button.text),
369 quick_widget->u.button.callback));
370 item.quick_widget = quick_widget;
371 g_array_append_val (widgets, item);
372 blen += item.widget->cols + 1;
375 /* stop dialog build here */
376 blen--;
377 quick_widget->widget_type = quick_end;
378 quick_widget--;
379 break;
381 default:
382 break;
386 /* adjust dialog width */
387 quick_dlg->cols = MAX (quick_dlg->cols, blen + 6);
388 if (have_groupbox)
390 if (width1 != 0)
391 width1 += 2;
392 if (width2 != 0)
393 width2 += 2;
395 if (width2 == 0)
396 len = width1 + 6;
397 else
399 len = width2 * 2 + 7;
400 if (width1 != 0)
401 len = MAX (len, width1 + 6);
404 quick_dlg->cols = MAX (quick_dlg->cols, len);
405 width1 = quick_dlg->cols - 6;
406 width2 = (quick_dlg->cols - 7) / 2;
408 if (quick_dlg->x == -1 || quick_dlg->y == -1)
409 dd = dlg_create (TRUE, 0, 0, y + 3, quick_dlg->cols, WPOS_CENTER | WPOS_TRYUP, FALSE,
410 dialog_colors, quick_dlg->callback, quick_dlg->mouse_callback,
411 quick_dlg->help, quick_dlg->title);
412 else
413 dd = dlg_create (TRUE, quick_dlg->y, quick_dlg->x, y + 3, quick_dlg->cols,
414 WPOS_KEEP_DEFAULT, FALSE, dialog_colors, quick_dlg->callback,
415 quick_dlg->mouse_callback, quick_dlg->help, quick_dlg->title);
417 /* add widgets into the dialog */
418 x2 = x1 + width2 + 1;
419 g = NULL;
420 two_columns = FALSE;
421 x = (WIDGET (dd)->cols - blen) / 2;
423 for (i = 0; i < widgets->len; i++)
425 quick_widget_item_t *item;
426 int column_width;
428 item = &g_array_index (widgets, quick_widget_item_t, i);
429 column_width = two_columns ? width2 : width1;
431 /* adjust widget width and x position */
432 switch (item->quick_widget->widget_type)
434 case quick_label:
436 quick_widget_t *input = item->quick_widget->u.label.input;
438 if (input != NULL && input->u.input.label_location == input_label_right)
440 /* location of this label will be adjusted later */
441 break;
444 MC_FALLTHROUGH;
445 case quick_checkbox:
446 case quick_radio:
447 if (item->widget->x != x1)
448 item->widget->x = x2;
449 if (g != NULL)
450 item->widget->x += 2;
451 break;
453 case quick_button:
454 if (!put_buttons)
456 if (item->widget->x != x1)
457 item->widget->x = x2;
458 if (g != NULL)
459 item->widget->x += 2;
461 else
463 item->widget->x = x;
464 x += item->widget->cols + 1;
466 break;
468 case quick_input:
470 Widget *label = WIDGET (INPUT (item->widget)->label);
471 int width = column_width;
473 if (g != NULL)
474 width -= 4;
476 switch (item->quick_widget->u.input.label_location)
478 case input_label_left:
479 /* label was adjusted before; adjust input line */
480 item->widget->x = label->x + label->cols + 1 - WIDGET (label->owner)->x;
481 item->widget->cols = width - label->cols - 1;
482 break;
484 case input_label_right:
485 if (item->widget->x != x1)
486 item->widget->x = x2;
487 if (g != NULL)
488 item->widget->x += 2;
489 item->widget->cols = width - label->cols - 1;
490 label->x = item->widget->x + item->widget->cols + 1;
491 break;
493 default:
494 if (item->widget->x != x1)
495 item->widget->x = x2;
496 if (g != NULL)
497 item->widget->x += 2;
498 item->widget->cols = width;
499 break;
502 /* forced update internal variables of inpuit line */
503 widget_set_size (item->widget, item->widget->y, item->widget->x, 1,
504 item->widget->cols);
506 break;
508 case quick_start_groupbox:
509 g = GROUPBOX (item->widget);
510 if (item->widget->x != x1)
511 item->widget->x = x2;
512 item->widget->cols = column_width;
513 break;
515 case quick_stop_groupbox:
516 g = NULL;
517 break;
519 case quick_separator:
520 if (item->widget != NULL)
522 if (g != NULL)
524 Widget *wg = WIDGET (g);
526 HLINE (item->widget)->auto_adjust_cols = FALSE;
527 item->widget->x = wg->x + 1 - WIDGET (wg->owner)->x;
528 item->widget->cols = wg->cols;
530 else if (two_columns)
532 HLINE (item->widget)->auto_adjust_cols = FALSE;
533 if (item->widget->x != x1)
534 item->widget->x = x2;
535 item->widget->x--;
536 item->widget->cols = column_width + 2;
538 else
539 HLINE (item->widget)->auto_adjust_cols = TRUE;
541 break;
543 case quick_start_columns:
544 two_columns = TRUE;
545 break;
547 case quick_stop_columns:
548 two_columns = FALSE;
549 break;
551 case quick_buttons:
552 /* several buttons in bottom line */
553 put_buttons = TRUE;
554 break;
556 default:
557 break;
560 if (item->widget != NULL)
562 unsigned long id;
564 /* add widget into dialog */
565 item->widget->options |= item->quick_widget->options; /* FIXME: cannot reset flags, setup only */
566 item->widget->state |= item->quick_widget->state; /* FIXME: cannot reset flags, setup only */
567 id = group_add_widget_autopos (GROUP (dd), item->widget, item->quick_widget->pos_flags,
568 NULL);
569 if (item->quick_widget->id != NULL)
570 *item->quick_widget->id = id;
574 while (nskip-- != 0)
575 group_set_current_widget_next (GROUP (dd));
577 return_val = dlg_run (dd);
579 /* Get the data if we found something interesting */
580 if (return_val != B_CANCEL)
581 for (i = 0; i < widgets->len; i++)
583 quick_widget_item_t *item;
585 item = &g_array_index (widgets, quick_widget_item_t, i);
587 switch (item->quick_widget->widget_type)
589 case quick_checkbox:
590 *item->quick_widget->u.checkbox.state = CHECK (item->widget)->state;
591 break;
593 case quick_input:
594 if ((item->quick_widget->u.input.completion_flags & INPUT_COMPLETE_CD) != 0)
595 *item->quick_widget->u.input.result =
596 tilde_expand (INPUT (item->widget)->buffer);
597 else
598 *item->quick_widget->u.input.result = g_strdup (INPUT (item->widget)->buffer);
599 break;
601 case quick_radio:
602 *item->quick_widget->u.radio.value = RADIO (item->widget)->sel;
603 break;
605 default:
606 break;
610 dlg_destroy (dd);
612 g_list_free_full (input_labels, g_free); /* destroy input labels created before */
613 g_array_free (widgets, TRUE);
615 return return_val;
618 /* --------------------------------------------------------------------------------------------- */