root/lib/widget/quick.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. quick_create_input
  2. quick_create_labeled_input
  3. quick_dialog_skip

   1 /*
   2    Widget based utility functions.
   3 
   4    Copyright (C) 1994-2024
   5    Free Software Foundation, Inc.
   6 
   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-2022
  13 
  14    This file is part of the Midnight Commander.
  15 
  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.
  20 
  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.
  25 
  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/>.
  28  */
  29 
  30 /** \file quick.c
  31  *  \brief Source: quick dialog engine
  32  */
  33 
  34 #include <config.h>
  35 
  36 #include <stdlib.h>
  37 #include <stdio.h>              /* fprintf() */
  38 
  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"
  43 
  44 /*** global variables ****************************************************************************/
  45 
  46 /*** file scope macro definitions ****************************************************************/
  47 
  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
  53 
  54 /*** file scope type declarations ****************************************************************/
  55 
  56 typedef struct
  57 {
  58     Widget *widget;
  59     quick_widget_t *quick_widget;
  60 } quick_widget_item_t;
  61 
  62 /*** forward declarations (file scope functions) *************************************************/
  63 
  64 /*** file scope variables ************************************************************************/
  65 
  66 /* --------------------------------------------------------------------------------------------- */
  67 /*** file scope functions ************************************************************************/
  68 /* --------------------------------------------------------------------------------------------- */
  69 
  70 static WInput *
  71 quick_create_input (int y, int x, const quick_widget_t * qw)
     /* [previous][next][first][last][top][bottom][index][help]  */
  72 {
  73     WInput *in;
  74 
  75     in = input_new (y, x, input_colors, 8, qw->u.input.text, qw->u.input.histname,
  76                     qw->u.input.completion_flags);
  77 
  78     in->is_password = qw->u.input.is_passwd;
  79     in->strip_password = qw->u.input.strip_passwd;
  80 
  81     return in;
  82 }
  83 
  84 /* --------------------------------------------------------------------------------------------- */
  85 
  86 static void
  87 quick_create_labeled_input (GArray * widgets, int *y, int x, quick_widget_t * quick_widget,
     /* [previous][next][first][last][top][bottom][index][help]  */
  88                             int *width)
  89 {
  90     quick_widget_item_t in, label;
  91 
  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;
  98 
  99     switch (quick_widget->u.input.label_location)
 100     {
 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);
 105 
 106         in.widget = WIDGET (quick_create_input (++(*y), x, quick_widget));
 107         in.quick_widget = quick_widget;
 108         g_array_append_val (widgets, in);
 109 
 110         *width = MAX (label.widget->rect.cols, in.widget->rect.cols);
 111         break;
 112 
 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);
 116 
 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);
 120 
 121         *width = label.widget->rect.cols + in.widget->rect.cols + 1;
 122         break;
 123 
 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);
 128 
 129         label.widget =
 130             WIDGET (label_new
 131                     (*y, x + in.widget->rect.cols + 1, I18N (quick_widget->u.input.label_text)));
 132         g_array_append_val (widgets, label);
 133 
 134         *width = label.widget->rect.cols + in.widget->rect.cols + 1;
 135         break;
 136 
 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);
 141 
 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);
 145 
 146         *width = MAX (label.widget->rect.cols, in.widget->rect.cols);
 147         break;
 148 
 149     default:
 150         return;
 151     }
 152 
 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;
 157 }
 158 
 159 /* --------------------------------------------------------------------------------------------- */
 160 /*** public functions ****************************************************************************/
 161 /* --------------------------------------------------------------------------------------------- */
 162 
 163 int
 164 quick_dialog_skip (quick_dialog_t * quick_dlg, int nskip)
     /* [previous][next][first][last][top][bottom][index][help]  */
 165 {
 166     int len;
 167     int blen = 0;
 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;
 176 
 177     /* x position of 1st column is 3 */
 178     const int x1 = 3;
 179     /* x position of 2nd column is 4 and it will be fixed later, after creation of all widgets */
 180     int x2 = 4;
 181 
 182     GArray *widgets;
 183     size_t i;
 184     quick_widget_t *quick_widget;
 185     WGroupbox *g = NULL;
 186     WDialog *dd;
 187     GList *input_labels = NULL; /* Widgets not directly requested by the user. */
 188     int return_val;
 189 
 190     len = str_term_width1 (I18N (quick_dlg->title)) + 6;
 191     quick_dlg->rect.cols = MAX (quick_dlg->rect.cols, len);
 192 
 193     y = 1;
 194     x = x1;
 195 
 196     /* create widgets */
 197     widgets = g_array_sized_new (FALSE, FALSE, sizeof (quick_widget_item_t), 8);
 198 
 199     for (quick_widget = quick_dlg->widgets; quick_widget->widget_type != quick_end; quick_widget++)
 200     {
 201         quick_widget_item_t item = { NULL, quick_widget };
 202         int width = 0;
 203 
 204         switch (quick_widget->widget_type)
 205         {
 206         case quick_checkbox:
 207             item.widget =
 208                 WIDGET (check_new
 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;
 213             if (g != NULL)
 214                 width += 2;
 215             if (two_columns)
 216                 width2 = MAX (width2, width);
 217             else
 218                 width1 = MAX (width1, width);
 219             break;
 220 
 221         case quick_button:
 222             /* single button */
 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;
 230             if (g != NULL)
 231                 width += 2;
 232             if (two_columns)
 233                 width2 = MAX (width2, width);
 234             else
 235                 width1 = MAX (width1, width);
 236             break;
 237 
 238         case quick_input:
 239             *quick_widget->u.input.result = NULL;
 240             y++;
 241             if (quick_widget->u.input.label_location != input_label_none)
 242             {
 243                 quick_create_labeled_input (widgets, &y, x, quick_widget, &width);
 244                 input_labels = g_list_prepend (input_labels, quick_widget->u.input.label);
 245             }
 246             else
 247             {
 248                 item.widget = WIDGET (quick_create_input (y, x, quick_widget));
 249                 g_array_append_val (widgets, item);
 250                 width = item.widget->rect.cols;
 251             }
 252             if (g != NULL)
 253                 width += 2;
 254             if (two_columns)
 255                 width2 = MAX (width2, width);
 256             else
 257                 width1 = MAX (width1, width);
 258             break;
 259 
 260         case quick_label:
 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;
 265             if (g != NULL)
 266                 width += 2;
 267             if (two_columns)
 268                 width2 = MAX (width2, width);
 269             else
 270                 width1 = MAX (width1, width);
 271             break;
 272 
 273         case quick_radio:
 274             {
 275                 WRadio *r;
 276                 char **items = NULL;
 277 
 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]));
 282                 items[i] = NULL;
 283 
 284                 r = radio_new (++y, x, quick_widget->u.radio.count, (const char **) items);
 285                 r->pos = r->sel = *quick_widget->u.radio.value;
 286                 g_strfreev (items);
 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;
 291                 if (g != NULL)
 292                     width += 2;
 293                 if (two_columns)
 294                     width2 = MAX (width2, width);
 295                 else
 296                     width1 = MAX (width1, width);
 297             }
 298             break;
 299 
 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;
 307             break;
 308 
 309         case quick_stop_groupbox:
 310             if (g != NULL)
 311             {
 312                 Widget *w = WIDGET (g);
 313 
 314                 y++;
 315                 w->rect.lines = y + 1 - w->rect.y;
 316                 g = NULL;
 317 
 318                 g_array_append_val (widgets, item);
 319             }
 320             break;
 321 
 322         case quick_separator:
 323             y++;
 324             if (quick_widget->u.separator.line)
 325             {
 326                 item.widget = WIDGET (hline_new (y, x, 1));
 327                 g_array_append_val (widgets, item);
 328             }
 329             break;
 330 
 331         case quick_start_columns:
 332             y2 = y;
 333             g_array_append_val (widgets, item);
 334             two_columns = TRUE;
 335             break;
 336 
 337         case quick_next_column:
 338             x = x2;
 339             y1 = y;
 340             y = y2;
 341             break;
 342 
 343         case quick_stop_columns:
 344             x = x1;
 345             y = MAX (y1, y);
 346             g_array_append_val (widgets, item);
 347             two_columns = FALSE;
 348             break;
 349 
 350         case quick_buttons:
 351             /* start put several buttons in bottom line */
 352             if (quick_widget->u.separator.space)
 353             {
 354                 y++;
 355 
 356                 if (quick_widget->u.separator.line)
 357                     item.widget = WIDGET (hline_new (y, 1, -1));
 358             }
 359 
 360             g_array_append_val (widgets, item);
 361 
 362             /* several buttons in bottom line */
 363             y++;
 364             quick_widget++;
 365             for (; quick_widget->widget_type == quick_button; quick_widget++)
 366             {
 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;
 375             }
 376 
 377             /* stop dialog build here */
 378             blen--;
 379             quick_widget->widget_type = quick_end;
 380             quick_widget--;
 381             break;
 382 
 383         default:
 384             break;
 385         }
 386     }
 387 
 388     /* adjust dialog width */
 389     quick_dlg->rect.cols = MAX (quick_dlg->rect.cols, blen + 6);
 390     if (have_groupbox)
 391     {
 392         if (width1 != 0)
 393             width1 += 2;
 394         if (width2 != 0)
 395             width2 += 2;
 396     }
 397     if (width2 == 0)
 398         len = width1 + 6;
 399     else
 400     {
 401         len = width2 * 2 + 7;
 402         if (width1 != 0)
 403             len = MAX (len, width1 + 6);
 404     }
 405 
 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;
 409 
 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);
 414     else
 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);
 418 
 419     /* add widgets into the dialog */
 420     x2 = x1 + width2 + 1;
 421     g = NULL;
 422     two_columns = FALSE;
 423     x = (WIDGET (dd)->rect.cols - blen) / 2;
 424 
 425     for (i = 0; i < widgets->len; i++)
 426     {
 427         quick_widget_item_t *item;
 428         int column_width;
 429         WRect *r;
 430 
 431         item = &g_array_index (widgets, quick_widget_item_t, i);
 432         r = &item->widget->rect;
 433         column_width = two_columns ? width2 : width1;
 434 
 435         /* adjust widget width and x position */
 436         switch (item->quick_widget->widget_type)
 437         {
 438         case quick_label:
 439             {
 440                 quick_widget_t *input = item->quick_widget->u.label.input;
 441 
 442                 if (input != NULL && input->u.input.label_location == input_label_right)
 443                 {
 444                     /* location of this label will be adjusted later */
 445                     break;
 446                 }
 447             }
 448             MC_FALLTHROUGH;
 449         case quick_checkbox:
 450         case quick_radio:
 451             if (r->x != x1)
 452                 r->x = x2;
 453             if (g != NULL)
 454                 r->x += 2;
 455             break;
 456 
 457         case quick_button:
 458             if (!put_buttons)
 459             {
 460                 if (r->x != x1)
 461                     r->x = x2;
 462                 if (g != NULL)
 463                     r->x += 2;
 464             }
 465             else
 466             {
 467                 r->x = x;
 468                 x += r->cols + 1;
 469             }
 470             break;
 471 
 472         case quick_input:
 473             {
 474                 Widget *label = WIDGET (INPUT (item->widget)->label);
 475                 int width = column_width;
 476 
 477                 if (g != NULL)
 478                     width -= 4;
 479 
 480                 switch (item->quick_widget->u.input.label_location)
 481                 {
 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;
 486                     break;
 487 
 488                 case input_label_right:
 489                     if (r->x != x1)
 490                         r->x = x2;
 491                     if (g != NULL)
 492                         r->x += 2;
 493                     r->cols = width - label->rect.cols - 1;
 494                     label->rect.x = r->x + r->cols + 1;
 495                     break;
 496 
 497                 default:
 498                     if (r->x != x1)
 499                         r->x = x2;
 500                     if (g != NULL)
 501                         r->x += 2;
 502                     r->cols = width;
 503                     break;
 504                 }
 505 
 506                 /* forced update internal variables of input line */
 507                 r->lines = 1;
 508                 widget_set_size_rect (item->widget, r);
 509             }
 510             break;
 511 
 512         case quick_start_groupbox:
 513             g = GROUPBOX (item->widget);
 514             if (r->x != x1)
 515                 r->x = x2;
 516             r->cols = column_width;
 517             break;
 518 
 519         case quick_stop_groupbox:
 520             g = NULL;
 521             break;
 522 
 523         case quick_separator:
 524             if (item->widget != NULL)
 525             {
 526                 if (g != NULL)
 527                 {
 528                     Widget *wg = WIDGET (g);
 529 
 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;
 533                 }
 534                 else if (two_columns)
 535                 {
 536                     HLINE (item->widget)->auto_adjust_cols = FALSE;
 537                     if (r->x != x1)
 538                         r->x = x2;
 539                     r->x--;
 540                     r->cols = column_width + 2;
 541                 }
 542                 else
 543                     HLINE (item->widget)->auto_adjust_cols = TRUE;
 544             }
 545             break;
 546 
 547         case quick_start_columns:
 548             two_columns = TRUE;
 549             break;
 550 
 551         case quick_stop_columns:
 552             two_columns = FALSE;
 553             break;
 554 
 555         case quick_buttons:
 556             /* several buttons in bottom line */
 557             put_buttons = TRUE;
 558             break;
 559 
 560         default:
 561             break;
 562         }
 563 
 564         if (item->widget != NULL)
 565         {
 566             unsigned long id;
 567 
 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,
 572                                            NULL);
 573             if (item->quick_widget->id != NULL)
 574                 *item->quick_widget->id = id;
 575         }
 576     }
 577 
 578     /* skip frame widget */
 579     if (dd->bg != NULL)
 580         nskip++;
 581 
 582     while (nskip-- != 0)
 583         group_set_current_widget_next (GROUP (dd));
 584 
 585     return_val = dlg_run (dd);
 586 
 587     /* Get the data if we found something interesting */
 588     if (return_val != B_CANCEL)
 589         for (i = 0; i < widgets->len; i++)
 590         {
 591             quick_widget_item_t *item;
 592 
 593             item = &g_array_index (widgets, quick_widget_item_t, i);
 594 
 595             switch (item->quick_widget->widget_type)
 596             {
 597             case quick_checkbox:
 598                 *item->quick_widget->u.checkbox.state = CHECK (item->widget)->state;
 599                 break;
 600 
 601             case quick_input:
 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)));
 605                 else
 606                     *item->quick_widget->u.input.result = input_get_text (INPUT (item->widget));
 607                 break;
 608 
 609             case quick_radio:
 610                 *item->quick_widget->u.radio.value = RADIO (item->widget)->sel;
 611                 break;
 612 
 613             default:
 614                 break;
 615             }
 616         }
 617 
 618     widget_destroy (WIDGET (dd));
 619 
 620     g_list_free_full (input_labels, g_free);    /* destroy input labels created before */
 621     g_array_free (widgets, TRUE);
 622 
 623     return return_val;
 624 }
 625 
 626 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */