quick.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626
  1. /*
  2. Widget based utility functions.
  3. Copyright (C) 1994-2024
  4. Free Software Foundation, Inc.
  5. Authors:
  6. Miguel de Icaza, 1994, 1995, 1996
  7. Radek Doulik, 1994, 1995
  8. Jakub Jelinek, 1995
  9. Andrej Borsenkow, 1995
  10. Andrew Borodin <aborodin@vmail.ru>, 2009-2022
  11. This file is part of the Midnight Commander.
  12. The Midnight Commander is free software: you can redistribute it
  13. and/or modify it under the terms of the GNU General Public License as
  14. published by the Free Software Foundation, either version 3 of the License,
  15. or (at your option) any later version.
  16. The Midnight Commander is distributed in the hope that it will be useful,
  17. but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. GNU General Public License for more details.
  20. You should have received a copy of the GNU General Public License
  21. along with this program. If not, see <http://www.gnu.org/licenses/>.
  22. */
  23. /** \file quick.c
  24. * \brief Source: quick dialog engine
  25. */
  26. #include <config.h>
  27. #include <stdlib.h>
  28. #include <stdio.h> /* fprintf() */
  29. #include "lib/global.h"
  30. #include "lib/strutil.h" /* str_term_width1() */
  31. #include "lib/util.h" /* tilde_expand() */
  32. #include "lib/widget.h"
  33. /*** global variables ****************************************************************************/
  34. /*** file scope macro definitions ****************************************************************/
  35. #ifdef ENABLE_NLS
  36. #define I18N(x) (x = x != NULL && *x != '\0' ? _(x) : x)
  37. #else
  38. #define I18N(x) (x = x)
  39. #endif
  40. /*** file scope type declarations ****************************************************************/
  41. typedef struct
  42. {
  43. Widget *widget;
  44. quick_widget_t *quick_widget;
  45. } quick_widget_item_t;
  46. /*** forward declarations (file scope functions) *************************************************/
  47. /*** file scope variables ************************************************************************/
  48. /* --------------------------------------------------------------------------------------------- */
  49. /*** file scope functions ************************************************************************/
  50. /* --------------------------------------------------------------------------------------------- */
  51. static WInput *
  52. quick_create_input (int y, int x, const quick_widget_t *qw)
  53. {
  54. WInput *in;
  55. in = input_new (y, x, input_colors, 8, qw->u.input.text, qw->u.input.histname,
  56. qw->u.input.completion_flags);
  57. in->is_password = qw->u.input.is_passwd;
  58. in->strip_password = qw->u.input.strip_passwd;
  59. return in;
  60. }
  61. /* --------------------------------------------------------------------------------------------- */
  62. static void
  63. quick_create_labeled_input (GArray *widgets, int *y, int x, quick_widget_t *quick_widget,
  64. int *width)
  65. {
  66. quick_widget_item_t in, label;
  67. label.quick_widget = g_new0 (quick_widget_t, 1);
  68. label.quick_widget->widget_type = quick_label;
  69. label.quick_widget->options = quick_widget->options;
  70. label.quick_widget->state = quick_widget->state;
  71. /* FIXME: this should be turned in depend of label_location */
  72. label.quick_widget->pos_flags = quick_widget->pos_flags;
  73. switch (quick_widget->u.input.label_location)
  74. {
  75. case input_label_above:
  76. label.widget = WIDGET (label_new (*y, x, I18N (quick_widget->u.input.label_text)));
  77. *y += label.widget->rect.lines - 1;
  78. g_array_append_val (widgets, label);
  79. in.widget = WIDGET (quick_create_input (++(*y), x, quick_widget));
  80. in.quick_widget = quick_widget;
  81. g_array_append_val (widgets, in);
  82. *width = MAX (label.widget->rect.cols, in.widget->rect.cols);
  83. break;
  84. case input_label_left:
  85. label.widget = WIDGET (label_new (*y, x, I18N (quick_widget->u.input.label_text)));
  86. g_array_append_val (widgets, label);
  87. in.widget = WIDGET (quick_create_input (*y, x + label.widget->rect.cols + 1, quick_widget));
  88. in.quick_widget = quick_widget;
  89. g_array_append_val (widgets, in);
  90. *width = label.widget->rect.cols + in.widget->rect.cols + 1;
  91. break;
  92. case input_label_right:
  93. in.widget = WIDGET (quick_create_input (*y, x, quick_widget));
  94. in.quick_widget = quick_widget;
  95. g_array_append_val (widgets, in);
  96. label.widget =
  97. WIDGET (label_new
  98. (*y, x + in.widget->rect.cols + 1, I18N (quick_widget->u.input.label_text)));
  99. g_array_append_val (widgets, label);
  100. *width = label.widget->rect.cols + in.widget->rect.cols + 1;
  101. break;
  102. case input_label_below:
  103. in.widget = WIDGET (quick_create_input (*y, x, quick_widget));
  104. in.quick_widget = quick_widget;
  105. g_array_append_val (widgets, in);
  106. label.widget = WIDGET (label_new (++(*y), x, I18N (quick_widget->u.input.label_text)));
  107. *y += label.widget->rect.lines - 1;
  108. g_array_append_val (widgets, label);
  109. *width = MAX (label.widget->rect.cols, in.widget->rect.cols);
  110. break;
  111. default:
  112. return;
  113. }
  114. INPUT (in.widget)->label = LABEL (label.widget);
  115. /* cross references */
  116. label.quick_widget->u.label.input = in.quick_widget;
  117. in.quick_widget->u.input.label = label.quick_widget;
  118. }
  119. /* --------------------------------------------------------------------------------------------- */
  120. /*** public functions ****************************************************************************/
  121. /* --------------------------------------------------------------------------------------------- */
  122. int
  123. quick_dialog_skip (quick_dialog_t *quick_dlg, int nskip)
  124. {
  125. int len;
  126. int blen = 0;
  127. int x, y; /* current positions */
  128. int y1 = 0; /* bottom of 1st column in case of two columns */
  129. int y2 = -1; /* start of two columns */
  130. int width1 = 0; /* width of single column */
  131. int width2 = 0; /* width of each of two columns */
  132. gboolean have_groupbox = FALSE;
  133. gboolean two_columns = FALSE;
  134. gboolean put_buttons = FALSE;
  135. /* x position of 1st column is 3 */
  136. const int x1 = 3;
  137. /* x position of 2nd column is 4 and it will be fixed later, after creation of all widgets */
  138. int x2 = 4;
  139. GArray *widgets;
  140. size_t i;
  141. quick_widget_t *quick_widget;
  142. WGroupbox *g = NULL;
  143. WDialog *dd;
  144. GList *input_labels = NULL; /* Widgets not directly requested by the user. */
  145. int return_val;
  146. len = str_term_width1 (I18N (quick_dlg->title)) + 6;
  147. quick_dlg->rect.cols = MAX (quick_dlg->rect.cols, len);
  148. y = 1;
  149. x = x1;
  150. /* create widgets */
  151. widgets = g_array_sized_new (FALSE, FALSE, sizeof (quick_widget_item_t), 8);
  152. for (quick_widget = quick_dlg->widgets; quick_widget->widget_type != quick_end; quick_widget++)
  153. {
  154. quick_widget_item_t item = { NULL, quick_widget };
  155. int width = 0;
  156. switch (quick_widget->widget_type)
  157. {
  158. case quick_checkbox:
  159. item.widget =
  160. WIDGET (check_new
  161. (++y, x, *quick_widget->u.checkbox.state,
  162. I18N (quick_widget->u.checkbox.text)));
  163. g_array_append_val (widgets, item);
  164. width = item.widget->rect.cols;
  165. if (g != NULL)
  166. width += 2;
  167. if (two_columns)
  168. width2 = MAX (width2, width);
  169. else
  170. width1 = MAX (width1, width);
  171. break;
  172. case quick_button:
  173. /* single button */
  174. item.widget = WIDGET (button_new (++y, x, quick_widget->u.button.action,
  175. quick_widget->u.button.action == B_ENTER ?
  176. DEFPUSH_BUTTON : NORMAL_BUTTON,
  177. I18N (quick_widget->u.button.text),
  178. quick_widget->u.button.callback));
  179. g_array_append_val (widgets, item);
  180. width = item.widget->rect.cols;
  181. if (g != NULL)
  182. width += 2;
  183. if (two_columns)
  184. width2 = MAX (width2, width);
  185. else
  186. width1 = MAX (width1, width);
  187. break;
  188. case quick_input:
  189. *quick_widget->u.input.result = NULL;
  190. y++;
  191. if (quick_widget->u.input.label_location != input_label_none)
  192. {
  193. quick_create_labeled_input (widgets, &y, x, quick_widget, &width);
  194. input_labels = g_list_prepend (input_labels, quick_widget->u.input.label);
  195. }
  196. else
  197. {
  198. item.widget = WIDGET (quick_create_input (y, x, quick_widget));
  199. g_array_append_val (widgets, item);
  200. width = item.widget->rect.cols;
  201. }
  202. if (g != NULL)
  203. width += 2;
  204. if (two_columns)
  205. width2 = MAX (width2, width);
  206. else
  207. width1 = MAX (width1, width);
  208. break;
  209. case quick_label:
  210. item.widget = WIDGET (label_new (++y, x, I18N (quick_widget->u.label.text)));
  211. g_array_append_val (widgets, item);
  212. y += item.widget->rect.lines - 1;
  213. width = item.widget->rect.cols;
  214. if (g != NULL)
  215. width += 2;
  216. if (two_columns)
  217. width2 = MAX (width2, width);
  218. else
  219. width1 = MAX (width1, width);
  220. break;
  221. case quick_radio:
  222. {
  223. WRadio *r;
  224. char **items = NULL;
  225. /* create the copy of radio_items to avoid mwmory leak */
  226. items = g_new (char *, quick_widget->u.radio.count + 1);
  227. for (i = 0; i < (size_t) quick_widget->u.radio.count; i++)
  228. items[i] = g_strdup (_(quick_widget->u.radio.items[i]));
  229. items[i] = NULL;
  230. r = radio_new (++y, x, quick_widget->u.radio.count, (const char **) items);
  231. r->pos = r->sel = *quick_widget->u.radio.value;
  232. g_strfreev (items);
  233. item.widget = WIDGET (r);
  234. g_array_append_val (widgets, item);
  235. y += item.widget->rect.lines - 1;
  236. width = item.widget->rect.cols;
  237. if (g != NULL)
  238. width += 2;
  239. if (two_columns)
  240. width2 = MAX (width2, width);
  241. else
  242. width1 = MAX (width1, width);
  243. }
  244. break;
  245. case quick_start_groupbox:
  246. I18N (quick_widget->u.groupbox.title);
  247. len = str_term_width1 (quick_widget->u.groupbox.title);
  248. g = groupbox_new (++y, x, 1, len + 4, quick_widget->u.groupbox.title);
  249. item.widget = WIDGET (g);
  250. g_array_append_val (widgets, item);
  251. have_groupbox = TRUE;
  252. break;
  253. case quick_stop_groupbox:
  254. if (g != NULL)
  255. {
  256. Widget *w = WIDGET (g);
  257. y++;
  258. w->rect.lines = y + 1 - w->rect.y;
  259. g = NULL;
  260. g_array_append_val (widgets, item);
  261. }
  262. break;
  263. case quick_separator:
  264. y++;
  265. if (quick_widget->u.separator.line)
  266. {
  267. item.widget = WIDGET (hline_new (y, x, 1));
  268. g_array_append_val (widgets, item);
  269. }
  270. break;
  271. case quick_start_columns:
  272. y2 = y;
  273. g_array_append_val (widgets, item);
  274. two_columns = TRUE;
  275. break;
  276. case quick_next_column:
  277. x = x2;
  278. y1 = y;
  279. y = y2;
  280. break;
  281. case quick_stop_columns:
  282. x = x1;
  283. y = MAX (y1, y);
  284. g_array_append_val (widgets, item);
  285. two_columns = FALSE;
  286. break;
  287. case quick_buttons:
  288. /* start put several buttons in bottom line */
  289. if (quick_widget->u.separator.space)
  290. {
  291. y++;
  292. if (quick_widget->u.separator.line)
  293. item.widget = WIDGET (hline_new (y, 1, -1));
  294. }
  295. g_array_append_val (widgets, item);
  296. /* several buttons in bottom line */
  297. y++;
  298. quick_widget++;
  299. for (; quick_widget->widget_type == quick_button; quick_widget++)
  300. {
  301. item.widget = WIDGET (button_new (y, x++, quick_widget->u.button.action,
  302. quick_widget->u.button.action == B_ENTER ?
  303. DEFPUSH_BUTTON : NORMAL_BUTTON,
  304. I18N (quick_widget->u.button.text),
  305. quick_widget->u.button.callback));
  306. item.quick_widget = quick_widget;
  307. g_array_append_val (widgets, item);
  308. blen += item.widget->rect.cols + 1;
  309. }
  310. /* stop dialog build here */
  311. blen--;
  312. quick_widget->widget_type = quick_end;
  313. quick_widget--;
  314. break;
  315. default:
  316. break;
  317. }
  318. }
  319. /* adjust dialog width */
  320. quick_dlg->rect.cols = MAX (quick_dlg->rect.cols, blen + 6);
  321. if (have_groupbox)
  322. {
  323. if (width1 != 0)
  324. width1 += 2;
  325. if (width2 != 0)
  326. width2 += 2;
  327. }
  328. if (width2 == 0)
  329. len = width1 + 6;
  330. else
  331. {
  332. len = width2 * 2 + 7;
  333. if (width1 != 0)
  334. len = MAX (len, width1 + 6);
  335. }
  336. quick_dlg->rect.cols = MAX (quick_dlg->rect.cols, len);
  337. width1 = quick_dlg->rect.cols - 6;
  338. width2 = (quick_dlg->rect.cols - 7) / 2;
  339. if (quick_dlg->rect.x == -1 || quick_dlg->rect.y == -1)
  340. dd = dlg_create (TRUE, 0, 0, y + 3, quick_dlg->rect.cols, WPOS_CENTER | WPOS_TRYUP, FALSE,
  341. dialog_colors, quick_dlg->callback, quick_dlg->mouse_callback,
  342. quick_dlg->help, quick_dlg->title);
  343. else
  344. dd = dlg_create (TRUE, quick_dlg->rect.y, quick_dlg->rect.x, y + 3, quick_dlg->rect.cols,
  345. WPOS_KEEP_DEFAULT, FALSE, dialog_colors, quick_dlg->callback,
  346. quick_dlg->mouse_callback, quick_dlg->help, quick_dlg->title);
  347. /* add widgets into the dialog */
  348. x2 = x1 + width2 + 1;
  349. g = NULL;
  350. two_columns = FALSE;
  351. x = (WIDGET (dd)->rect.cols - blen) / 2;
  352. for (i = 0; i < widgets->len; i++)
  353. {
  354. quick_widget_item_t *item;
  355. int column_width;
  356. WRect *r;
  357. item = &g_array_index (widgets, quick_widget_item_t, i);
  358. r = &item->widget->rect;
  359. column_width = two_columns ? width2 : width1;
  360. /* adjust widget width and x position */
  361. switch (item->quick_widget->widget_type)
  362. {
  363. case quick_label:
  364. {
  365. quick_widget_t *input = item->quick_widget->u.label.input;
  366. if (input != NULL && input->u.input.label_location == input_label_right)
  367. {
  368. /* location of this label will be adjusted later */
  369. break;
  370. }
  371. }
  372. MC_FALLTHROUGH;
  373. case quick_checkbox:
  374. case quick_radio:
  375. if (r->x != x1)
  376. r->x = x2;
  377. if (g != NULL)
  378. r->x += 2;
  379. break;
  380. case quick_button:
  381. if (!put_buttons)
  382. {
  383. if (r->x != x1)
  384. r->x = x2;
  385. if (g != NULL)
  386. r->x += 2;
  387. }
  388. else
  389. {
  390. r->x = x;
  391. x += r->cols + 1;
  392. }
  393. break;
  394. case quick_input:
  395. {
  396. Widget *label = WIDGET (INPUT (item->widget)->label);
  397. int width = column_width;
  398. if (g != NULL)
  399. width -= 4;
  400. switch (item->quick_widget->u.input.label_location)
  401. {
  402. case input_label_left:
  403. /* label was adjusted before; adjust input line */
  404. r->x = label->rect.x + label->rect.cols + 1 - WIDGET (label->owner)->rect.x;
  405. r->cols = width - label->rect.cols - 1;
  406. break;
  407. case input_label_right:
  408. if (r->x != x1)
  409. r->x = x2;
  410. if (g != NULL)
  411. r->x += 2;
  412. r->cols = width - label->rect.cols - 1;
  413. label->rect.x = r->x + r->cols + 1;
  414. break;
  415. default:
  416. if (r->x != x1)
  417. r->x = x2;
  418. if (g != NULL)
  419. r->x += 2;
  420. r->cols = width;
  421. break;
  422. }
  423. /* forced update internal variables of input line */
  424. r->lines = 1;
  425. widget_set_size_rect (item->widget, r);
  426. }
  427. break;
  428. case quick_start_groupbox:
  429. g = GROUPBOX (item->widget);
  430. if (r->x != x1)
  431. r->x = x2;
  432. r->cols = column_width;
  433. break;
  434. case quick_stop_groupbox:
  435. g = NULL;
  436. break;
  437. case quick_separator:
  438. if (item->widget != NULL)
  439. {
  440. if (g != NULL)
  441. {
  442. Widget *wg = WIDGET (g);
  443. HLINE (item->widget)->auto_adjust_cols = FALSE;
  444. r->x = wg->rect.x + 1 - WIDGET (wg->owner)->rect.x;
  445. r->cols = wg->rect.cols;
  446. }
  447. else if (two_columns)
  448. {
  449. HLINE (item->widget)->auto_adjust_cols = FALSE;
  450. if (r->x != x1)
  451. r->x = x2;
  452. r->x--;
  453. r->cols = column_width + 2;
  454. }
  455. else
  456. HLINE (item->widget)->auto_adjust_cols = TRUE;
  457. }
  458. break;
  459. case quick_start_columns:
  460. two_columns = TRUE;
  461. break;
  462. case quick_stop_columns:
  463. two_columns = FALSE;
  464. break;
  465. case quick_buttons:
  466. /* several buttons in bottom line */
  467. put_buttons = TRUE;
  468. break;
  469. default:
  470. break;
  471. }
  472. if (item->widget != NULL)
  473. {
  474. unsigned long id;
  475. /* add widget into dialog */
  476. item->widget->options |= item->quick_widget->options; /* FIXME: cannot reset flags, setup only */
  477. item->widget->state |= item->quick_widget->state; /* FIXME: cannot reset flags, setup only */
  478. id = group_add_widget_autopos (GROUP (dd), item->widget, item->quick_widget->pos_flags,
  479. NULL);
  480. if (item->quick_widget->id != NULL)
  481. *item->quick_widget->id = id;
  482. }
  483. }
  484. /* skip frame widget */
  485. if (dd->bg != NULL)
  486. nskip++;
  487. while (nskip-- != 0)
  488. group_set_current_widget_next (GROUP (dd));
  489. return_val = dlg_run (dd);
  490. /* Get the data if we found something interesting */
  491. if (return_val != B_CANCEL)
  492. for (i = 0; i < widgets->len; i++)
  493. {
  494. quick_widget_item_t *item;
  495. item = &g_array_index (widgets, quick_widget_item_t, i);
  496. switch (item->quick_widget->widget_type)
  497. {
  498. case quick_checkbox:
  499. *item->quick_widget->u.checkbox.state = CHECK (item->widget)->state;
  500. break;
  501. case quick_input:
  502. if ((item->quick_widget->u.input.completion_flags & INPUT_COMPLETE_CD) != 0)
  503. *item->quick_widget->u.input.result =
  504. tilde_expand (input_get_ctext (INPUT (item->widget)));
  505. else
  506. *item->quick_widget->u.input.result = input_get_text (INPUT (item->widget));
  507. break;
  508. case quick_radio:
  509. *item->quick_widget->u.radio.value = RADIO (item->widget)->sel;
  510. break;
  511. default:
  512. break;
  513. }
  514. }
  515. widget_destroy (WIDGET (dd));
  516. g_list_free_full (input_labels, g_free); /* destroy input labels created before */
  517. g_array_free (widgets, TRUE);
  518. return return_val;
  519. }
  520. /* --------------------------------------------------------------------------------------------- */