quick.c 20 KB

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