Manual pages: mcmcdiffmceditmcview

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-2025
   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 <https://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 /*** file scope type declarations ****************************************************************/
  49 
  50 typedef struct
  51 {
  52     Widget *widget;
  53     quick_widget_t *quick_widget;
  54 } quick_widget_item_t;
  55 
  56 /*** forward declarations (file scope functions) *************************************************/
  57 
  58 /*** file scope variables ************************************************************************/
  59 
  60 /* --------------------------------------------------------------------------------------------- */
  61 /*** file scope functions ************************************************************************/
  62 /* --------------------------------------------------------------------------------------------- */
  63 
  64 static WInput *
  65 quick_create_input (int y, int x, const quick_widget_t *qw)
     /* [previous][next][first][last][top][bottom][index][help]  */
  66 {
  67     WInput *in;
  68 
  69     in = input_new (y, x, input_colors, 8, qw->u.input.text, qw->u.input.histname,
  70                     qw->u.input.completion_flags);
  71 
  72     in->is_password = qw->u.input.is_passwd;
  73     in->strip_password = qw->u.input.strip_passwd;
  74 
  75     return in;
  76 }
  77 
  78 /* --------------------------------------------------------------------------------------------- */
  79 
  80 static void
  81 quick_create_labeled_input (GArray *widgets, int *y, int x, quick_widget_t *quick_widget,
     /* [previous][next][first][last][top][bottom][index][help]  */
  82                             int *width)
  83 {
  84     quick_widget_item_t in, label;
  85 
  86     label.quick_widget = g_new0 (quick_widget_t, 1);
  87     label.quick_widget->widget_type = quick_label;
  88     label.quick_widget->options = quick_widget->options;
  89     label.quick_widget->state = quick_widget->state;
  90     // FIXME: this should be turned in depend of label_location
  91     label.quick_widget->pos_flags = quick_widget->pos_flags;
  92 
  93     switch (quick_widget->u.input.label_location)
  94     {
  95     case input_label_above:
  96         label.widget = WIDGET (label_new (*y, x, quick_widget->u.input.label_text));
  97         *y += label.widget->rect.lines - 1;
  98         g_array_append_val (widgets, label);
  99 
 100         in.widget = WIDGET (quick_create_input (++(*y), x, quick_widget));
 101         in.quick_widget = quick_widget;
 102         g_array_append_val (widgets, in);
 103 
 104         *width = MAX (label.widget->rect.cols, in.widget->rect.cols);
 105         break;
 106 
 107     case input_label_left:
 108         label.widget = WIDGET (label_new (*y, x, quick_widget->u.input.label_text));
 109         g_array_append_val (widgets, label);
 110 
 111         in.widget = WIDGET (quick_create_input (*y, x + label.widget->rect.cols + 1, quick_widget));
 112         in.quick_widget = quick_widget;
 113         g_array_append_val (widgets, in);
 114 
 115         *width = label.widget->rect.cols + in.widget->rect.cols + 1;
 116         break;
 117 
 118     case input_label_right:
 119         in.widget = WIDGET (quick_create_input (*y, x, quick_widget));
 120         in.quick_widget = quick_widget;
 121         g_array_append_val (widgets, in);
 122 
 123         label.widget =
 124             WIDGET (label_new (*y, x + in.widget->rect.cols + 1, quick_widget->u.input.label_text));
 125         g_array_append_val (widgets, label);
 126 
 127         *width = label.widget->rect.cols + in.widget->rect.cols + 1;
 128         break;
 129 
 130     case input_label_below:
 131         in.widget = WIDGET (quick_create_input (*y, x, quick_widget));
 132         in.quick_widget = quick_widget;
 133         g_array_append_val (widgets, in);
 134 
 135         label.widget = WIDGET (label_new (++(*y), x, quick_widget->u.input.label_text));
 136         *y += label.widget->rect.lines - 1;
 137         g_array_append_val (widgets, label);
 138 
 139         *width = MAX (label.widget->rect.cols, in.widget->rect.cols);
 140         break;
 141 
 142     default:
 143         g_free (label.quick_widget);
 144         return;
 145     }
 146 
 147     INPUT (in.widget)->label = LABEL (label.widget);
 148     // cross references
 149     label.quick_widget->u.label.input = in.quick_widget;
 150     in.quick_widget->u.input.label = label.quick_widget;
 151 }
 152 
 153 /* --------------------------------------------------------------------------------------------- */
 154 /*** public functions ****************************************************************************/
 155 /* --------------------------------------------------------------------------------------------- */
 156 
 157 int
 158 quick_dialog_skip (quick_dialog_t *quick_dlg, int nskip)
     /* [previous][next][first][last][top][bottom][index][help]  */
 159 {
 160     int len;
 161     int blen = 0;
 162     int x, y;        // current positions
 163     int y1 = 0;      // bottom of 1st column in case of two columns
 164     int y2 = -1;     // start of two columns
 165     int width1 = 0;  // width of single column
 166     int width2 = 0;  // width of each of two columns
 167     gboolean have_groupbox = FALSE;
 168     gboolean two_columns = FALSE;
 169     gboolean put_buttons = FALSE;
 170 
 171     // x position of 1st column is 3
 172     const int x1 = 3;
 173     // x position of 2nd column is 4 and it will be fixed later, after creation of all widgets
 174     int x2 = 4;
 175 
 176     GArray *widgets;
 177     size_t i;
 178     quick_widget_t *quick_widget;
 179     WGroupbox *g = NULL;
 180     WDialog *dd;
 181     GList *input_labels = NULL;  // Widgets not directly requested by the user.
 182     int return_val;
 183 
 184     len = str_term_width1 (quick_dlg->title) + 6;
 185     quick_dlg->rect.cols = MAX (quick_dlg->rect.cols, len);
 186 
 187     y = 1;
 188     x = x1;
 189 
 190     // create widgets
 191     widgets = g_array_sized_new (FALSE, FALSE, sizeof (quick_widget_item_t), 8);
 192 
 193     for (quick_widget = quick_dlg->widgets; quick_widget->widget_type != quick_end; quick_widget++)
 194     {
 195         quick_widget_item_t item = { NULL, quick_widget };
 196         int width = 0;
 197 
 198         switch (quick_widget->widget_type)
 199         {
 200         case quick_checkbox:
 201             item.widget = WIDGET (
 202                 check_new (++y, x, *quick_widget->u.checkbox.state, quick_widget->u.checkbox.text));
 203             g_array_append_val (widgets, item);
 204             width = item.widget->rect.cols;
 205             if (g != NULL)
 206                 width += 2;
 207             if (two_columns)
 208                 width2 = MAX (width2, width);
 209             else
 210                 width1 = MAX (width1, width);
 211             break;
 212 
 213         case quick_button:
 214             // single button
 215             item.widget = WIDGET (button_new (
 216                 ++y, x, quick_widget->u.button.action,
 217                 quick_widget->u.button.action == B_ENTER ? DEFPUSH_BUTTON : NORMAL_BUTTON,
 218                 quick_widget->u.button.text, quick_widget->u.button.callback));
 219             g_array_append_val (widgets, item);
 220             width = item.widget->rect.cols;
 221             if (g != NULL)
 222                 width += 2;
 223             if (two_columns)
 224                 width2 = MAX (width2, width);
 225             else
 226                 width1 = MAX (width1, width);
 227             break;
 228 
 229         case quick_input:
 230             *quick_widget->u.input.result = NULL;
 231             y++;
 232             if (quick_widget->u.input.label_location != input_label_none)
 233             {
 234                 quick_create_labeled_input (widgets, &y, x, quick_widget, &width);
 235                 input_labels = g_list_prepend (input_labels, quick_widget->u.input.label);
 236             }
 237             else
 238             {
 239                 item.widget = WIDGET (quick_create_input (y, x, quick_widget));
 240                 g_array_append_val (widgets, item);
 241                 width = item.widget->rect.cols;
 242             }
 243             if (g != NULL)
 244                 width += 2;
 245             if (two_columns)
 246                 width2 = MAX (width2, width);
 247             else
 248                 width1 = MAX (width1, width);
 249             break;
 250 
 251         case quick_label:
 252             item.widget = WIDGET (label_new (++y, x, quick_widget->u.label.text));
 253             g_array_append_val (widgets, item);
 254             y += item.widget->rect.lines - 1;
 255             width = item.widget->rect.cols;
 256             if (g != NULL)
 257                 width += 2;
 258             if (two_columns)
 259                 width2 = MAX (width2, width);
 260             else
 261                 width1 = MAX (width1, width);
 262             break;
 263 
 264         case quick_radio:
 265         {
 266             WRadio *r;
 267             char **items = NULL;
 268 
 269             // create the copy of radio_items to avoid memory leak
 270             items = g_new (char *, quick_widget->u.radio.count + 1);
 271             for (i = 0; i < (size_t) quick_widget->u.radio.count; i++)
 272                 items[i] = g_strdup (quick_widget->u.radio.items[i]);
 273             items[i] = NULL;
 274 
 275             r = radio_new (++y, x, quick_widget->u.radio.count, (const char **) items);
 276             r->pos = r->sel = *quick_widget->u.radio.value;
 277             g_strfreev (items);
 278             item.widget = WIDGET (r);
 279             g_array_append_val (widgets, item);
 280             y += item.widget->rect.lines - 1;
 281             width = item.widget->rect.cols;
 282             if (g != NULL)
 283                 width += 2;
 284             if (two_columns)
 285                 width2 = MAX (width2, width);
 286             else
 287                 width1 = MAX (width1, width);
 288         }
 289         break;
 290 
 291         case quick_start_groupbox:
 292             len = str_term_width1 (quick_widget->u.groupbox.title);
 293             g = groupbox_new (++y, x, 1, len + 4, quick_widget->u.groupbox.title);
 294             item.widget = WIDGET (g);
 295             g_array_append_val (widgets, item);
 296             have_groupbox = TRUE;
 297             break;
 298 
 299         case quick_stop_groupbox:
 300             if (g != NULL)
 301             {
 302                 Widget *w = WIDGET (g);
 303 
 304                 y++;
 305                 w->rect.lines = y + 1 - w->rect.y;
 306                 g = NULL;
 307 
 308                 g_array_append_val (widgets, item);
 309             }
 310             break;
 311 
 312         case quick_separator:
 313             y++;
 314             if (quick_widget->u.separator.line)
 315             {
 316                 item.widget = WIDGET (hline_new (y, x, 1));
 317                 g_array_append_val (widgets, item);
 318             }
 319             break;
 320 
 321         case quick_start_columns:
 322             y2 = y;
 323             g_array_append_val (widgets, item);
 324             two_columns = TRUE;
 325             break;
 326 
 327         case quick_next_column:
 328             x = x2;
 329             y1 = y;
 330             y = y2;
 331             break;
 332 
 333         case quick_stop_columns:
 334             x = x1;
 335             y = MAX (y1, y);
 336             g_array_append_val (widgets, item);
 337             two_columns = FALSE;
 338             break;
 339 
 340         case quick_buttons:
 341             // start put several buttons in bottom line
 342             if (quick_widget->u.separator.space)
 343             {
 344                 y++;
 345 
 346                 if (quick_widget->u.separator.line)
 347                     item.widget = WIDGET (hline_new (y, 1, -1));
 348             }
 349 
 350             g_array_append_val (widgets, item);
 351 
 352             // several buttons in bottom line
 353             y++;
 354             quick_widget++;
 355             for (; quick_widget->widget_type == quick_button; quick_widget++)
 356             {
 357                 item.widget = WIDGET (button_new (
 358                     y, x++, quick_widget->u.button.action,
 359                     quick_widget->u.button.action == B_ENTER ? DEFPUSH_BUTTON : NORMAL_BUTTON,
 360                     quick_widget->u.button.text, quick_widget->u.button.callback));
 361                 item.quick_widget = quick_widget;
 362                 g_array_append_val (widgets, item);
 363                 blen += item.widget->rect.cols + 1;
 364             }
 365 
 366             // stop dialog build here
 367             blen--;
 368             quick_widget->widget_type = quick_end;
 369             quick_widget--;
 370             break;
 371 
 372         default:
 373             break;
 374         }
 375     }
 376 
 377     // adjust dialog width
 378     quick_dlg->rect.cols = MAX (quick_dlg->rect.cols, blen + 6);
 379     if (have_groupbox)
 380     {
 381         if (width1 != 0)
 382             width1 += 2;
 383         if (width2 != 0)
 384             width2 += 2;
 385     }
 386     if (width2 == 0)
 387         len = width1 + 6;
 388     else
 389     {
 390         len = width2 * 2 + 7;
 391         if (width1 != 0)
 392             len = MAX (len, width1 + 6);
 393     }
 394 
 395     quick_dlg->rect.cols = MAX (quick_dlg->rect.cols, len);
 396     width1 = quick_dlg->rect.cols - 6;
 397     width2 = (quick_dlg->rect.cols - 7) / 2;
 398 
 399     if (quick_dlg->rect.x == -1 || quick_dlg->rect.y == -1)
 400         dd = dlg_create (TRUE, 0, 0, y + 3, quick_dlg->rect.cols, WPOS_CENTER | WPOS_TRYUP, FALSE,
 401                          dialog_colors, quick_dlg->callback, quick_dlg->mouse_callback,
 402                          quick_dlg->help, quick_dlg->title);
 403     else
 404         dd = dlg_create (TRUE, quick_dlg->rect.y, quick_dlg->rect.x, y + 3, quick_dlg->rect.cols,
 405                          WPOS_KEEP_DEFAULT, FALSE, dialog_colors, quick_dlg->callback,
 406                          quick_dlg->mouse_callback, quick_dlg->help, quick_dlg->title);
 407 
 408     // add widgets into the dialog
 409     x2 = x1 + width2 + 1;
 410     g = NULL;
 411     two_columns = FALSE;
 412     x = (WIDGET (dd)->rect.cols - blen) / 2;
 413 
 414     for (i = 0; i < widgets->len; i++)
 415     {
 416         quick_widget_item_t *item;
 417         int column_width;
 418         WRect *r;
 419 
 420         item = &g_array_index (widgets, quick_widget_item_t, i);
 421         column_width = two_columns ? width2 : width1;
 422 
 423         // adjust widget width and x position
 424         switch (item->quick_widget->widget_type)
 425         {
 426         case quick_label:
 427         {
 428             quick_widget_t *input = item->quick_widget->u.label.input;
 429 
 430             if (input != NULL && input->u.input.label_location == input_label_right)
 431             {
 432                 // location of this label will be adjusted later
 433                 break;
 434             }
 435         }
 436             MC_FALLTHROUGH;
 437         case quick_checkbox:
 438         case quick_radio:
 439             r = &item->widget->rect;
 440             if (r->x != x1)
 441                 r->x = x2;
 442             if (g != NULL)
 443                 r->x += 2;
 444             break;
 445 
 446         case quick_button:
 447             r = &item->widget->rect;
 448             if (!put_buttons)
 449             {
 450                 if (r->x != x1)
 451                     r->x = x2;
 452                 if (g != NULL)
 453                     r->x += 2;
 454             }
 455             else
 456             {
 457                 r->x = x;
 458                 x += r->cols + 1;
 459             }
 460             break;
 461 
 462         case quick_input:
 463         {
 464             Widget *label = WIDGET (INPUT (item->widget)->label);
 465             int width = column_width;
 466 
 467             if (g != NULL)
 468                 width -= 4;
 469 
 470             r = &item->widget->rect;
 471 
 472             switch (item->quick_widget->u.input.label_location)
 473             {
 474             case input_label_left:
 475                 // label was adjusted before; adjust input line
 476                 r->x = label->rect.x + label->rect.cols + 1 - WIDGET (label->owner)->rect.x;
 477                 r->cols = width - label->rect.cols - 1;
 478                 break;
 479 
 480             case input_label_right:
 481                 if (r->x != x1)
 482                     r->x = x2;
 483                 if (g != NULL)
 484                     r->x += 2;
 485                 r->cols = width - label->rect.cols - 1;
 486                 label->rect.x = r->x + r->cols + 1;
 487                 break;
 488 
 489             default:
 490                 if (r->x != x1)
 491                     r->x = x2;
 492                 if (g != NULL)
 493                     r->x += 2;
 494                 r->cols = width;
 495                 break;
 496             }
 497 
 498             // forced update internal variables of input line
 499             r->lines = 1;
 500             widget_set_size_rect (item->widget, r);
 501         }
 502         break;
 503 
 504         case quick_start_groupbox:
 505             g = GROUPBOX (item->widget);
 506             r = &item->widget->rect;
 507             if (r->x != x1)
 508                 r->x = x2;
 509             r->cols = column_width;
 510             break;
 511 
 512         case quick_stop_groupbox:
 513             g = NULL;
 514             break;
 515 
 516         case quick_separator:
 517             if (item->widget != NULL)
 518             {
 519                 r = &item->widget->rect;
 520 
 521                 if (g != NULL)
 522                 {
 523                     Widget *wg = WIDGET (g);
 524 
 525                     HLINE (item->widget)->auto_adjust_cols = FALSE;
 526                     r->x = wg->rect.x + 1 - WIDGET (wg->owner)->rect.x;
 527                     r->cols = wg->rect.cols;
 528                 }
 529                 else if (two_columns)
 530                 {
 531                     HLINE (item->widget)->auto_adjust_cols = FALSE;
 532                     if (r->x != x1)
 533                         r->x = x2;
 534                     r->x--;
 535                     r->cols = column_width + 2;
 536                 }
 537                 else
 538                     HLINE (item->widget)->auto_adjust_cols = TRUE;
 539             }
 540             break;
 541 
 542         case quick_start_columns:
 543             two_columns = TRUE;
 544             break;
 545 
 546         case quick_stop_columns:
 547             two_columns = FALSE;
 548             break;
 549 
 550         case quick_buttons:
 551             // several buttons in bottom line
 552             put_buttons = TRUE;
 553             break;
 554 
 555         default:
 556             break;
 557         }
 558 
 559         if (item->widget != NULL)
 560         {
 561             unsigned long id;
 562 
 563             // add widget into dialog
 564             item->widget->options |=
 565                 item->quick_widget->options;  // FIXME: cannot reset flags, setup only
 566             item->widget->state |=
 567                 item->quick_widget->state;  // FIXME: cannot reset flags, setup only
 568             id = group_add_widget_autopos (GROUP (dd), item->widget, item->quick_widget->pos_flags,
 569                                            NULL);
 570             if (item->quick_widget->id != NULL)
 571                 *item->quick_widget->id = id;
 572         }
 573     }
 574 
 575     // skip frame widget
 576     if (dd->bg != NULL)
 577         nskip++;
 578 
 579     while (nskip-- != 0)
 580         group_set_current_widget_next (GROUP (dd));
 581 
 582     return_val = dlg_run (dd);
 583 
 584     // Get the data if we found something interesting
 585     if (return_val != B_CANCEL)
 586         for (i = 0; i < widgets->len; i++)
 587         {
 588             quick_widget_item_t *item;
 589 
 590             item = &g_array_index (widgets, quick_widget_item_t, i);
 591 
 592             switch (item->quick_widget->widget_type)
 593             {
 594             case quick_checkbox:
 595                 *item->quick_widget->u.checkbox.state = CHECK (item->widget)->state;
 596                 break;
 597 
 598             case quick_input:
 599                 if ((item->quick_widget->u.input.completion_flags & INPUT_COMPLETE_CD) != 0)
 600                     *item->quick_widget->u.input.result =
 601                         tilde_expand (input_get_ctext (INPUT (item->widget)));
 602                 else
 603                     *item->quick_widget->u.input.result = input_get_text (INPUT (item->widget));
 604                 break;
 605 
 606             case quick_radio:
 607                 *item->quick_widget->u.radio.value = RADIO (item->widget)->sel;
 608                 break;
 609 
 610             default:
 611                 break;
 612             }
 613         }
 614 
 615     widget_destroy (WIDGET (dd));
 616 
 617     g_list_free_full (input_labels, g_free);  // destroy input labels created before
 618     g_array_free (widgets, TRUE);
 619 
 620     return return_val;
 621 }
 622 
 623 /* --------------------------------------------------------------------------------------------- */

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