quick.c 20 KB

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