wtools.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  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
  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
  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 wtools.c
  25. * \brief Source: widget based utility functions
  26. */
  27. #include <config.h>
  28. #include <stdarg.h>
  29. #include <stdlib.h>
  30. #include "lib/global.h"
  31. #include "lib/tty/tty.h"
  32. #include "lib/tty/key.h" /* tty_getch() */
  33. #include "lib/strutil.h"
  34. #include "lib/util.h" /* tilde_expand() */
  35. #include "lib/widget.h"
  36. #include "lib/event.h" /* mc_event_raise() */
  37. /*** global variables ****************************************************************************/
  38. /*** file scope macro definitions ****************************************************************/
  39. /*** file scope type declarations ****************************************************************/
  40. /*** file scope variables ************************************************************************/
  41. static Dlg_head *last_query_dlg;
  42. static int sel_pos = 0;
  43. /*** file scope functions ************************************************************************/
  44. /* --------------------------------------------------------------------------------------------- */
  45. /** default query callback, used to reposition query */
  46. static cb_ret_t
  47. default_query_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
  48. {
  49. switch (msg)
  50. {
  51. case DLG_RESIZE:
  52. if ((h->flags & DLG_CENTER) == 0)
  53. {
  54. Dlg_head *prev_dlg = NULL;
  55. int ypos, xpos;
  56. /* get dialog under h */
  57. if (top_dlg != NULL)
  58. {
  59. if (top_dlg->data != (void *) h)
  60. prev_dlg = (Dlg_head *) top_dlg->data;
  61. else
  62. {
  63. GList *p;
  64. /* Top dialog is current if it is visible.
  65. Get previous dialog in stack */
  66. p = g_list_next (top_dlg);
  67. if (p != NULL)
  68. prev_dlg = (Dlg_head *) p->data;
  69. }
  70. }
  71. /* if previous dialog is not fullscreen'd -- overlap it */
  72. if (prev_dlg == NULL || prev_dlg->fullscreen)
  73. ypos = LINES / 3 - (h->lines - 3) / 2;
  74. else
  75. ypos = prev_dlg->y + 2;
  76. xpos = COLS / 2 - h->cols / 2;
  77. /* set position */
  78. dlg_set_position (h, ypos, xpos, ypos + h->lines, xpos + h->cols);
  79. return MSG_HANDLED;
  80. }
  81. /* fallthrough */
  82. default:
  83. return default_dlg_callback (h, sender, msg, parm, data);
  84. }
  85. }
  86. /* --------------------------------------------------------------------------------------------- */
  87. /** Create message dialog */
  88. static struct Dlg_head *
  89. do_create_message (int flags, const char *title, const char *text)
  90. {
  91. char *p;
  92. Dlg_head *d;
  93. /* Add empty lines before and after the message */
  94. p = g_strconcat ("\n", text, "\n", (char *) NULL);
  95. query_dialog (title, p, flags, 0);
  96. d = last_query_dlg;
  97. /* do resize before initing and running */
  98. default_query_callback (d, NULL, DLG_RESIZE, 0, NULL);
  99. init_dlg (d);
  100. g_free (p);
  101. return d;
  102. }
  103. /* --------------------------------------------------------------------------------------------- */
  104. /**
  105. * Show message dialog. Dismiss it when any key is pressed.
  106. * Not safe to call from background.
  107. */
  108. static void
  109. fg_message (int flags, const char *title, const char *text)
  110. {
  111. Dlg_head *d;
  112. d = do_create_message (flags, title, text);
  113. tty_getch ();
  114. dlg_run_done (d);
  115. destroy_dlg (d);
  116. }
  117. /* --------------------------------------------------------------------------------------------- */
  118. /** Show message box from background */
  119. #ifdef ENABLE_BACKGROUND
  120. static void
  121. bg_message (int dummy, int *flags, char *title, const char *text)
  122. {
  123. (void) dummy;
  124. title = g_strconcat (_("Background process:"), " ", title, (char *) NULL);
  125. fg_message (*flags, title, text);
  126. g_free (title);
  127. }
  128. #endif /* ENABLE_BACKGROUND */
  129. /* --------------------------------------------------------------------------------------------- */
  130. /**
  131. * Show dialog, not background safe.
  132. *
  133. * If the arguments "header" and "text" should be translated,
  134. * that MUST be done by the caller of fg_input_dialog_help().
  135. *
  136. * The argument "history_name" holds the name of a section
  137. * in the history file. Data entered in the input field of
  138. * the dialog box will be stored there.
  139. *
  140. */
  141. static char *
  142. fg_input_dialog_help (const char *header, const char *text, const char *help,
  143. const char *history_name, const char *def_text, gboolean strip_password)
  144. {
  145. char *my_str;
  146. int flags = (strip_password) ? 4 : 0;
  147. QuickWidget quick_widgets[] = {
  148. /* 0 */ QUICK_BUTTON (6, 64, 1, 0, N_("&Cancel"), B_CANCEL, NULL),
  149. /* 1 */ QUICK_BUTTON (3, 64, 1, 0, N_("&OK"), B_ENTER, NULL),
  150. /* 2 */ QUICK_INPUT (3, 64, 0, 0, def_text, 58, flags, NULL, &my_str),
  151. /* 3 */ QUICK_LABEL (3, 64, 2, 0, ""),
  152. QUICK_END
  153. };
  154. int b0_len, b1_len, b_len, gap;
  155. char histname[64] = "inp|";
  156. int lines, cols;
  157. int len;
  158. int i;
  159. char *p_text;
  160. int ret;
  161. /* buttons */
  162. #ifdef ENABLE_NLS
  163. quick_widgets[0].u.button.text = _(quick_widgets[0].u.button.text);
  164. quick_widgets[1].u.button.text = _(quick_widgets[1].u.button.text);
  165. #endif /* ENABLE_NLS */
  166. b0_len = str_term_width1 (quick_widgets[0].u.button.text) + 3;
  167. b1_len = str_term_width1 (quick_widgets[1].u.button.text) + 5; /* default button */
  168. b_len = b0_len + b1_len + 2; /* including gap */
  169. /* input line */
  170. if (history_name != NULL && *history_name != '\0')
  171. {
  172. g_strlcpy (histname + 3, history_name, sizeof (histname) - 3);
  173. quick_widgets[2].u.input.histname = histname;
  174. }
  175. /* The special value of def_text is used to identify password boxes
  176. and hide characters with "*". Don't save passwords in history! */
  177. if (def_text == INPUT_PASSWORD)
  178. {
  179. quick_widgets[2].u.input.flags = 1;
  180. histname[3] = '\0';
  181. quick_widgets[2].u.input.text = "";
  182. }
  183. /* text */
  184. p_text = g_strstrip (g_strdup (text));
  185. str_msg_term_size (p_text, &lines, &cols);
  186. quick_widgets[3].u.label.text = p_text;
  187. /* dialog width */
  188. len = str_term_width1 (header);
  189. len = max (max (len, cols) + 4, 64);
  190. len = min (max (len, b_len + 6), COLS);
  191. /* button locations */
  192. gap = (len - 8 - b_len) / 3;
  193. quick_widgets[1].relative_x = 3 + gap;
  194. quick_widgets[0].relative_x = quick_widgets[1].relative_x + b1_len + gap + 2;
  195. {
  196. QuickDialog Quick_input = {
  197. len, lines + 6, -1, -1, header,
  198. help, quick_widgets, NULL, TRUE
  199. };
  200. for (i = 0; i < 4; i++)
  201. {
  202. quick_widgets[i].x_divisions = Quick_input.xlen;
  203. quick_widgets[i].y_divisions = Quick_input.ylen;
  204. }
  205. for (i = 0; i < 3; i++)
  206. quick_widgets[i].relative_y += 2 + lines;
  207. /* input line length */
  208. quick_widgets[2].u.input.len = Quick_input.xlen - 6;
  209. ret = quick_dialog (&Quick_input);
  210. }
  211. g_free (p_text);
  212. return (ret != B_CANCEL) ? my_str : NULL;
  213. }
  214. /* --------------------------------------------------------------------------------------------- */
  215. #ifdef ENABLE_BACKGROUND
  216. static int
  217. wtools_parent_call (void *routine, gpointer ctx, int argc, ...)
  218. {
  219. ev_background_parent_call_t event_data;
  220. event_data.routine = routine;
  221. event_data.ctx = ctx;
  222. event_data.argc = argc;
  223. va_start (event_data.ap, argc);
  224. mc_event_raise (MCEVENT_GROUP_CORE, "background_parent_call", (gpointer) & event_data);
  225. va_end (event_data.ap);
  226. return event_data.ret.i;
  227. }
  228. /* --------------------------------------------------------------------------------------------- */
  229. static char *
  230. wtools_parent_call_string (void *routine, int argc, ...)
  231. {
  232. ev_background_parent_call_t event_data;
  233. event_data.routine = routine;
  234. event_data.argc = argc;
  235. va_start (event_data.ap, argc);
  236. mc_event_raise (MCEVENT_GROUP_CORE, "background_parent_call_string", (gpointer) & event_data);
  237. va_end (event_data.ap);
  238. return event_data.ret.s;
  239. }
  240. #endif /* ENABLE_BACKGROUND */
  241. /* --------------------------------------------------------------------------------------------- */
  242. /*** public functions ****************************************************************************/
  243. /* --------------------------------------------------------------------------------------------- */
  244. /** Used to ask questions to the user */
  245. int
  246. query_dialog (const char *header, const char *text, int flags, int count, ...)
  247. {
  248. va_list ap;
  249. Dlg_head *query_dlg;
  250. WButton *button;
  251. WButton *defbutton = NULL;
  252. int win_len = 0;
  253. int i;
  254. int result = -1;
  255. int cols, lines;
  256. char *cur_name;
  257. const int *query_colors = (flags & D_ERROR) != 0 ? alarm_colors : dialog_colors;
  258. dlg_flags_t dlg_flags = (flags & D_CENTER) != 0 ? (DLG_CENTER | DLG_TRYUP) : DLG_NONE;
  259. if (header == MSG_ERROR)
  260. header = _("Error");
  261. if (count > 0)
  262. {
  263. va_start (ap, count);
  264. for (i = 0; i < count; i++)
  265. {
  266. char *cp = va_arg (ap, char *);
  267. win_len += str_term_width1 (cp) + 6;
  268. if (strchr (cp, '&') != NULL)
  269. win_len--;
  270. }
  271. va_end (ap);
  272. }
  273. /* count coordinates */
  274. str_msg_term_size (text, &lines, &cols);
  275. cols = 6 + max (win_len, max (str_term_width1 (header), cols));
  276. lines += 4 + (count > 0 ? 2 : 0);
  277. /* prepare dialog */
  278. query_dlg =
  279. create_dlg (TRUE, 0, 0, lines, cols, query_colors, default_query_callback,
  280. "[QueryBox]", header, dlg_flags);
  281. if (count > 0)
  282. {
  283. cols = (cols - win_len - 2) / 2 + 2;
  284. va_start (ap, count);
  285. for (i = 0; i < count; i++)
  286. {
  287. int xpos;
  288. cur_name = va_arg (ap, char *);
  289. xpos = str_term_width1 (cur_name) + 6;
  290. if (strchr (cur_name, '&') != NULL)
  291. xpos--;
  292. button = button_new (lines - 3, cols, B_USER + i, NORMAL_BUTTON, cur_name, 0);
  293. add_widget (query_dlg, button);
  294. cols += xpos;
  295. if (i == sel_pos)
  296. defbutton = button;
  297. }
  298. va_end (ap);
  299. add_widget (query_dlg, label_new (2, 3, text));
  300. /* do resize before running and selecting any widget */
  301. default_query_callback (query_dlg, NULL, DLG_RESIZE, 0, NULL);
  302. if (defbutton)
  303. dlg_select_widget (defbutton);
  304. /* run dialog and make result */
  305. switch (run_dlg (query_dlg))
  306. {
  307. case B_CANCEL:
  308. break;
  309. default:
  310. result = query_dlg->ret_value - B_USER;
  311. }
  312. /* free used memory */
  313. destroy_dlg (query_dlg);
  314. }
  315. else
  316. {
  317. add_widget (query_dlg, label_new (2, 3, text));
  318. add_widget (query_dlg, button_new (0, 0, 0, HIDDEN_BUTTON, "-", 0));
  319. last_query_dlg = query_dlg;
  320. }
  321. sel_pos = 0;
  322. return result;
  323. }
  324. /* --------------------------------------------------------------------------------------------- */
  325. void
  326. query_set_sel (int new_sel)
  327. {
  328. sel_pos = new_sel;
  329. }
  330. /* --------------------------------------------------------------------------------------------- */
  331. /**
  332. * Create message dialog. The caller must call dlg_run_done() and
  333. * destroy_dlg() to dismiss it. Not safe to call from background.
  334. */
  335. struct Dlg_head *
  336. create_message (int flags, const char *title, const char *text, ...)
  337. {
  338. va_list args;
  339. Dlg_head *d;
  340. char *p;
  341. va_start (args, text);
  342. p = g_strdup_vprintf (text, args);
  343. va_end (args);
  344. d = do_create_message (flags, title, p);
  345. g_free (p);
  346. return d;
  347. }
  348. /* --------------------------------------------------------------------------------------------- */
  349. /** Show message box, background safe */
  350. void
  351. message (int flags, const char *title, const char *text, ...)
  352. {
  353. char *p;
  354. va_list ap;
  355. va_start (ap, text);
  356. p = g_strdup_vprintf (text, ap);
  357. va_end (ap);
  358. if (title == MSG_ERROR)
  359. title = _("Error");
  360. #ifdef ENABLE_BACKGROUND
  361. if (mc_global.we_are_background)
  362. {
  363. union
  364. {
  365. void *p;
  366. void (*f) (int, int *, char *, const char *);
  367. } func;
  368. func.f = bg_message;
  369. wtools_parent_call (func.p, NULL, 3, sizeof (flags), &flags, strlen (title), title,
  370. strlen (p), p);
  371. }
  372. else
  373. #endif /* ENABLE_BACKGROUND */
  374. fg_message (flags, title, p);
  375. g_free (p);
  376. }
  377. /* --------------------------------------------------------------------------------------------- */
  378. /**
  379. * Show input dialog, background safe.
  380. *
  381. * If the arguments "header" and "text" should be translated,
  382. * that MUST be done by the caller of these wrappers.
  383. */
  384. char *
  385. input_dialog_help (const char *header, const char *text, const char *help,
  386. const char *history_name, const char *def_text, gboolean strip_password)
  387. {
  388. #ifdef ENABLE_BACKGROUND
  389. if (mc_global.we_are_background)
  390. {
  391. union
  392. {
  393. void *p;
  394. char *(*f) (const char *, const char *, const char *, const char *, const char *,
  395. gboolean);
  396. } func;
  397. func.f = fg_input_dialog_help;
  398. return wtools_parent_call_string (func.p, 6,
  399. strlen (header), header, strlen (text),
  400. text, strlen (help), help,
  401. strlen (history_name), history_name,
  402. strlen (def_text), def_text,
  403. sizeof (gboolean), strip_password);
  404. }
  405. else
  406. #endif /* ENABLE_BACKGROUND */
  407. return fg_input_dialog_help (header, text, help, history_name, def_text, strip_password);
  408. }
  409. /* --------------------------------------------------------------------------------------------- */
  410. /** Show input dialog with default help, background safe */
  411. char *
  412. input_dialog (const char *header, const char *text, const char *history_name, const char *def_text)
  413. {
  414. return input_dialog_help (header, text, "[Input Line Keys]", history_name, def_text, FALSE);
  415. }
  416. /* --------------------------------------------------------------------------------------------- */
  417. char *
  418. input_expand_dialog (const char *header, const char *text,
  419. const char *history_name, const char *def_text)
  420. {
  421. char *result;
  422. char *expanded;
  423. result = input_dialog (header, text, history_name, def_text);
  424. if (result)
  425. {
  426. expanded = tilde_expand (result);
  427. g_free (result);
  428. return expanded;
  429. }
  430. return result;
  431. }
  432. /* --------------------------------------------------------------------------------------------- */