dialog.c 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336
  1. /*
  2. Dialog box features module for the Midnight Commander
  3. Copyright (C) 1994-2017
  4. Free Software Foundation, Inc.
  5. This file is part of the Midnight Commander.
  6. The Midnight Commander is free software: you can redistribute it
  7. and/or modify it under the terms of the GNU General Public License as
  8. published by the Free Software Foundation, either version 3 of the License,
  9. or (at your option) any later version.
  10. The Midnight Commander is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. */
  17. /** \file dialog.c
  18. * \brief Source: dialog box features module
  19. */
  20. #include <config.h>
  21. #include <ctype.h>
  22. #include <errno.h>
  23. #include <stdlib.h>
  24. #include <stdio.h>
  25. #include <string.h>
  26. #include <sys/types.h>
  27. #include <sys/stat.h>
  28. #include "lib/global.h"
  29. #include "lib/tty/tty.h"
  30. #include "lib/skin.h"
  31. #include "lib/tty/key.h"
  32. #include "lib/strutil.h"
  33. #include "lib/fileloc.h" /* MC_HISTORY_FILE */
  34. #include "lib/event.h" /* mc_event_raise() */
  35. #include "lib/widget.h"
  36. #include "lib/widget/mouse.h"
  37. /*** global variables ****************************************************************************/
  38. /* Color styles for normal and error dialogs */
  39. dlg_colors_t dialog_colors;
  40. dlg_colors_t alarm_colors;
  41. dlg_colors_t listbox_colors;
  42. /* Primitive way to check if the the current dialog is our dialog */
  43. /* This is needed by async routines like load_prompt */
  44. GList *top_dlg = NULL;
  45. /* A hook list for idle events */
  46. hook_t *idle_hook = NULL;
  47. /* If set then dialogs just clean the screen when refreshing, else */
  48. /* they do a complete refresh, refreshing all the parts of the program */
  49. gboolean fast_refresh = FALSE;
  50. /* left click outside of dialog closes it */
  51. gboolean mouse_close_dialog = FALSE;
  52. const global_keymap_t *dialog_map = NULL;
  53. /*** file scope macro definitions ****************************************************************/
  54. /*** file scope type declarations ****************************************************************/
  55. /* Control widget positions in dialog */
  56. typedef struct
  57. {
  58. int shift_x;
  59. int scale_x;
  60. int shift_y;
  61. int scale_y;
  62. } widget_shift_scale_t;
  63. /*** file scope variables ************************************************************************/
  64. /* --------------------------------------------------------------------------------------------- */
  65. /*** file scope functions ************************************************************************/
  66. /* --------------------------------------------------------------------------------------------- */
  67. static GList *
  68. dlg_get_next_or_prev_of (const GList * list, gboolean next)
  69. {
  70. GList *l = NULL;
  71. if (list != NULL && list->data != NULL)
  72. {
  73. const WDialog *owner = CONST_WIDGET (list->data)->owner;
  74. if (owner != NULL)
  75. {
  76. if (next)
  77. {
  78. l = g_list_next (list);
  79. if (l == NULL)
  80. l = owner->widgets;
  81. }
  82. else
  83. {
  84. l = g_list_previous (list);
  85. if (l == NULL)
  86. l = g_list_last (owner->widgets);
  87. }
  88. }
  89. }
  90. return l;
  91. }
  92. /* --------------------------------------------------------------------------------------------- */
  93. static void
  94. dlg_select_next_or_prev (WDialog * h, gboolean next)
  95. {
  96. if (h->widgets != NULL && h->current != NULL)
  97. {
  98. GList *l = h->current;
  99. Widget *w;
  100. do
  101. {
  102. l = dlg_get_next_or_prev_of (l, next);
  103. w = WIDGET (l->data);
  104. }
  105. while ((widget_get_state (w, WST_DISABLED) || !widget_get_options (w, WOP_SELECTABLE))
  106. && l != h->current);
  107. widget_select (l->data);
  108. }
  109. }
  110. /* --------------------------------------------------------------------------------------------- */
  111. /**
  112. * broadcast a message to all the widgets in a dialog that have
  113. * the options set to flags. If flags is zero, the message is sent
  114. * to all widgets.
  115. */
  116. static void
  117. dlg_broadcast_msg_to (WDialog * h, widget_msg_t msg, gboolean reverse, widget_options_t flags)
  118. {
  119. GList *p, *first;
  120. if (h->widgets == NULL)
  121. return;
  122. if (h->current == NULL)
  123. h->current = h->widgets;
  124. p = dlg_get_next_or_prev_of (h->current, !reverse);
  125. first = p;
  126. do
  127. {
  128. Widget *w = WIDGET (p->data);
  129. p = dlg_get_next_or_prev_of (p, !reverse);
  130. if ((flags == 0) || ((flags & w->options) != 0))
  131. send_message (w, NULL, msg, 0, NULL);
  132. }
  133. while (first != p);
  134. }
  135. /* --------------------------------------------------------------------------------------------- */
  136. /**
  137. * Read histories from the ${XDG_CACHE_HOME}/mc/history file
  138. */
  139. static void
  140. dlg_read_history (WDialog * h)
  141. {
  142. char *profile;
  143. ev_history_load_save_t event_data;
  144. if (num_history_items_recorded == 0) /* this is how to disable */
  145. return;
  146. profile = mc_config_get_full_path (MC_HISTORY_FILE);
  147. event_data.cfg = mc_config_init (profile, TRUE);
  148. event_data.receiver = NULL;
  149. /* create all histories in dialog */
  150. mc_event_raise (h->event_group, MCEVENT_HISTORY_LOAD, &event_data);
  151. mc_config_deinit (event_data.cfg);
  152. g_free (profile);
  153. }
  154. /* --------------------------------------------------------------------------------------------- */
  155. static int
  156. dlg_find_widget_callback (const void *a, const void *b)
  157. {
  158. const Widget *w = CONST_WIDGET (a);
  159. const widget_cb_fn f = b;
  160. return (w->callback == f) ? 0 : 1;
  161. }
  162. /* --------------------------------------------------------------------------------------------- */
  163. static void
  164. refresh_cmd (void)
  165. {
  166. #ifdef HAVE_SLANG
  167. tty_touch_screen ();
  168. mc_refresh ();
  169. #else
  170. /* Use this if the refreshes fail */
  171. clr_scr ();
  172. repaint_screen ();
  173. #endif /* HAVE_SLANG */
  174. }
  175. /* --------------------------------------------------------------------------------------------- */
  176. static cb_ret_t
  177. dlg_execute_cmd (WDialog * h, long command)
  178. {
  179. cb_ret_t ret = MSG_HANDLED;
  180. switch (command)
  181. {
  182. case CK_Ok:
  183. h->ret_value = B_ENTER;
  184. dlg_stop (h);
  185. break;
  186. case CK_Cancel:
  187. h->ret_value = B_CANCEL;
  188. dlg_stop (h);
  189. break;
  190. case CK_Up:
  191. case CK_Left:
  192. dlg_select_prev_widget (h);
  193. break;
  194. case CK_Down:
  195. case CK_Right:
  196. dlg_select_next_widget (h);
  197. break;
  198. case CK_Help:
  199. {
  200. ev_help_t event_data = { NULL, h->help_ctx };
  201. mc_event_raise (MCEVENT_GROUP_CORE, "help", &event_data);
  202. }
  203. break;
  204. case CK_Suspend:
  205. mc_event_raise (MCEVENT_GROUP_CORE, "suspend", NULL);
  206. refresh_cmd ();
  207. break;
  208. case CK_Refresh:
  209. refresh_cmd ();
  210. break;
  211. case CK_ScreenList:
  212. if (!widget_get_state (WIDGET (h), WST_MODAL))
  213. dialog_switch_list ();
  214. else
  215. ret = MSG_NOT_HANDLED;
  216. break;
  217. case CK_ScreenNext:
  218. if (!widget_get_state (WIDGET (h), WST_MODAL))
  219. dialog_switch_next ();
  220. else
  221. ret = MSG_NOT_HANDLED;
  222. break;
  223. case CK_ScreenPrev:
  224. if (!widget_get_state (WIDGET (h), WST_MODAL))
  225. dialog_switch_prev ();
  226. else
  227. ret = MSG_NOT_HANDLED;
  228. break;
  229. default:
  230. ret = MSG_NOT_HANDLED;
  231. }
  232. return ret;
  233. }
  234. /* --------------------------------------------------------------------------------------------- */
  235. static cb_ret_t
  236. dlg_handle_key (WDialog * h, int d_key)
  237. {
  238. long command;
  239. command = keybind_lookup_keymap_command (dialog_map, d_key);
  240. if (command == CK_IgnoreKey)
  241. return MSG_NOT_HANDLED;
  242. if (send_message (h, NULL, MSG_ACTION, command, NULL) == MSG_HANDLED
  243. || dlg_execute_cmd (h, command) == MSG_HANDLED)
  244. return MSG_HANDLED;
  245. return MSG_NOT_HANDLED;
  246. }
  247. /* --------------------------------------------------------------------------------------------- */
  248. /**
  249. * This is the low-level mouse handler.
  250. * It receives a Gpm_Event event and translates it into a higher level protocol.
  251. */
  252. static int
  253. dlg_mouse_translator (Gpm_Event * event, Widget * w)
  254. {
  255. mouse_event_t me;
  256. me = mouse_translate_event (w, event);
  257. return mouse_process_event (w, &me);
  258. }
  259. /* --------------------------------------------------------------------------------------------- */
  260. static int
  261. dlg_mouse_event (WDialog * h, Gpm_Event * event)
  262. {
  263. Widget *wh = WIDGET (h);
  264. GList *p;
  265. /* close the dialog by mouse left click out of dialog area */
  266. if (mouse_close_dialog && (wh->pos_flags & WPOS_FULLSCREEN) == 0
  267. && ((event->buttons & GPM_B_LEFT) != 0) && ((event->type & GPM_DOWN) != 0)
  268. && !mouse_global_in_widget (event, wh))
  269. {
  270. h->ret_value = B_CANCEL;
  271. dlg_stop (h);
  272. return MOU_NORMAL;
  273. }
  274. if (wh->mouse_callback != NULL)
  275. {
  276. int mou;
  277. mou = dlg_mouse_translator (event, wh);
  278. if (mou != MOU_UNHANDLED)
  279. return mou;
  280. }
  281. /* send the event to widgets in reverse Z-order */
  282. p = g_list_last (h->widgets);
  283. do
  284. {
  285. Widget *w = WIDGET (p->data);
  286. if (!widget_get_state (w, WST_DISABLED) && w->mouse_callback != NULL)
  287. {
  288. /* put global cursor position to the widget */
  289. int ret;
  290. ret = dlg_mouse_translator (event, w);
  291. if (ret != MOU_UNHANDLED)
  292. return ret;
  293. }
  294. p = g_list_previous (p);
  295. }
  296. while (p != NULL);
  297. return MOU_UNHANDLED;
  298. }
  299. /* --------------------------------------------------------------------------------------------- */
  300. static cb_ret_t
  301. dlg_try_hotkey (WDialog * h, int d_key)
  302. {
  303. GList *hot_cur;
  304. Widget *current;
  305. cb_ret_t handled;
  306. int c;
  307. if (h->widgets == NULL)
  308. return MSG_NOT_HANDLED;
  309. if (h->current == NULL)
  310. h->current = h->widgets;
  311. /*
  312. * Explanation: we don't send letter hotkeys to other widgets if
  313. * the currently selected widget is an input line
  314. */
  315. current = WIDGET (h->current->data);
  316. if (widget_get_state (current, WST_DISABLED))
  317. return MSG_NOT_HANDLED;
  318. if (widget_get_options (current, WOP_IS_INPUT))
  319. {
  320. /* skip ascii control characters, anything else can valid character in
  321. * some encoding */
  322. if (d_key >= 32 && d_key < 256)
  323. return MSG_NOT_HANDLED;
  324. }
  325. /* If it's an alt key, send the message */
  326. c = d_key & ~ALT (0);
  327. if (d_key & ALT (0) && g_ascii_isalpha (c))
  328. d_key = g_ascii_tolower (c);
  329. handled = MSG_NOT_HANDLED;
  330. if (widget_get_options (current, WOP_WANT_HOTKEY))
  331. handled = send_message (current, NULL, MSG_HOTKEY, d_key, NULL);
  332. /* If not used, send hotkey to other widgets */
  333. if (handled == MSG_HANDLED)
  334. return MSG_HANDLED;
  335. hot_cur = dlg_get_widget_next_of (h->current);
  336. /* send it to all widgets */
  337. while (h->current != hot_cur && handled == MSG_NOT_HANDLED)
  338. {
  339. current = WIDGET (hot_cur->data);
  340. if (widget_get_options (current, WOP_WANT_HOTKEY)
  341. && !widget_get_state (current, WST_DISABLED))
  342. handled = send_message (current, NULL, MSG_HOTKEY, d_key, NULL);
  343. if (handled == MSG_NOT_HANDLED)
  344. hot_cur = dlg_get_widget_next_of (hot_cur);
  345. }
  346. if (handled == MSG_HANDLED)
  347. widget_select (WIDGET (hot_cur->data));
  348. return handled;
  349. }
  350. /* --------------------------------------------------------------------------------------------- */
  351. static void
  352. dlg_key_event (WDialog * h, int d_key)
  353. {
  354. cb_ret_t handled;
  355. if (h->widgets == NULL)
  356. return;
  357. if (h->current == NULL)
  358. h->current = h->widgets;
  359. /* TAB used to cycle */
  360. if (!widget_get_options (WIDGET (h), WOP_WANT_TAB))
  361. {
  362. if (d_key == '\t')
  363. {
  364. dlg_select_next_widget (h);
  365. return;
  366. }
  367. else if ((d_key & ~(KEY_M_SHIFT | KEY_M_CTRL)) == '\t')
  368. {
  369. dlg_select_prev_widget (h);
  370. return;
  371. }
  372. }
  373. /* first can dlg_callback handle the key */
  374. handled = send_message (h, NULL, MSG_KEY, d_key, NULL);
  375. /* next try the hotkey */
  376. if (handled == MSG_NOT_HANDLED)
  377. handled = dlg_try_hotkey (h, d_key);
  378. if (handled == MSG_HANDLED)
  379. send_message (h, NULL, MSG_HOTKEY_HANDLED, 0, NULL);
  380. else
  381. /* not used - then try widget_callback */
  382. handled = send_message (h->current->data, NULL, MSG_KEY, d_key, NULL);
  383. /* not used- try to use the unhandled case */
  384. if (handled == MSG_NOT_HANDLED)
  385. handled = send_message (h, NULL, MSG_UNHANDLED_KEY, d_key, NULL);
  386. if (handled == MSG_NOT_HANDLED)
  387. handled = dlg_handle_key (h, d_key);
  388. (void) handled;
  389. send_message (h, NULL, MSG_POST_KEY, d_key, NULL);
  390. }
  391. /* --------------------------------------------------------------------------------------------- */
  392. static void
  393. frontend_dlg_run (WDialog * h)
  394. {
  395. Widget *wh = WIDGET (h);
  396. Gpm_Event event;
  397. event.x = -1;
  398. /* close opened editors, viewers, etc */
  399. if (!widget_get_state (wh, WST_MODAL) && mc_global.midnight_shutdown)
  400. {
  401. send_message (h, NULL, MSG_VALIDATE, 0, NULL);
  402. return;
  403. }
  404. while (widget_get_state (wh, WST_ACTIVE))
  405. {
  406. int d_key;
  407. if (mc_global.tty.winch_flag != 0)
  408. dialog_change_screen_size ();
  409. if (is_idle ())
  410. {
  411. if (idle_hook)
  412. execute_hooks (idle_hook);
  413. while (widget_get_state (wh, WST_IDLE) && is_idle ())
  414. send_message (wh, NULL, MSG_IDLE, 0, NULL);
  415. /* Allow terminating the dialog from the idle handler */
  416. if (!widget_get_state (wh, WST_ACTIVE))
  417. break;
  418. }
  419. update_cursor (h);
  420. /* Clear interrupt flag */
  421. tty_got_interrupt ();
  422. d_key = tty_get_event (&event, h->mouse_status == MOU_REPEAT, TRUE);
  423. dlg_process_event (h, d_key, &event);
  424. if (widget_get_state (wh, WST_CLOSED))
  425. send_message (h, NULL, MSG_VALIDATE, 0, NULL);
  426. }
  427. }
  428. /* --------------------------------------------------------------------------------------------- */
  429. static int
  430. dlg_find_widget_by_id (gconstpointer a, gconstpointer b)
  431. {
  432. const Widget *w = CONST_WIDGET (a);
  433. unsigned long id = GPOINTER_TO_UINT (b);
  434. return w->id == id ? 0 : 1;
  435. }
  436. /* --------------------------------------------------------------------------------------------- */
  437. static void
  438. dlg_widget_set_position (gpointer data, gpointer user_data)
  439. {
  440. /* there are, mainly, 2 generally possible situations:
  441. * 1. control sticks to one side - it should be moved
  442. * 2. control sticks to two sides of one direction - it should be sized
  443. */
  444. Widget *c = WIDGET (data);
  445. Widget *wh = WIDGET (c->owner);
  446. const widget_shift_scale_t *wss = (const widget_shift_scale_t *) user_data;
  447. int x = c->x;
  448. int y = c->y;
  449. int cols = c->cols;
  450. int lines = c->lines;
  451. if ((c->pos_flags & WPOS_CENTER_HORZ) != 0)
  452. x = wh->x + (wh->cols - c->cols) / 2;
  453. else if ((c->pos_flags & WPOS_KEEP_LEFT) != 0 && (c->pos_flags & WPOS_KEEP_RIGHT) != 0)
  454. {
  455. x += wss->shift_x;
  456. cols += wss->scale_x;
  457. }
  458. else if ((c->pos_flags & WPOS_KEEP_LEFT) != 0)
  459. x += wss->shift_x;
  460. else if ((c->pos_flags & WPOS_KEEP_RIGHT) != 0)
  461. x += wss->shift_x + wss->scale_x;
  462. if ((c->pos_flags & WPOS_CENTER_VERT) != 0)
  463. y = wh->y + (wh->lines - c->lines) / 2;
  464. else if ((c->pos_flags & WPOS_KEEP_TOP) != 0 && (c->pos_flags & WPOS_KEEP_BOTTOM) != 0)
  465. {
  466. y += wss->shift_y;
  467. lines += wss->scale_y;
  468. }
  469. else if ((c->pos_flags & WPOS_KEEP_TOP) != 0)
  470. y += wss->shift_y;
  471. else if ((c->pos_flags & WPOS_KEEP_BOTTOM) != 0)
  472. y += wss->shift_y + wss->scale_y;
  473. widget_set_size (c, y, x, lines, cols);
  474. }
  475. /* --------------------------------------------------------------------------------------------- */
  476. static void
  477. dlg_adjust_position (widget_pos_flags_t pos_flags, int *y, int *x, int *lines, int *cols)
  478. {
  479. if ((pos_flags & WPOS_FULLSCREEN) != 0)
  480. {
  481. *y = 0;
  482. *x = 0;
  483. *lines = LINES;
  484. *cols = COLS;
  485. }
  486. else
  487. {
  488. if ((pos_flags & WPOS_CENTER_HORZ) != 0)
  489. *x = (COLS - *cols) / 2;
  490. if ((pos_flags & WPOS_CENTER_VERT) != 0)
  491. *y = (LINES - *lines) / 2;
  492. if ((pos_flags & WPOS_TRYUP) != 0)
  493. {
  494. if (*y > 3)
  495. *y -= 2;
  496. else if (*y == 3)
  497. *y = 2;
  498. }
  499. }
  500. }
  501. /* --------------------------------------------------------------------------------------------- */
  502. /*** public functions ****************************************************************************/
  503. /* --------------------------------------------------------------------------------------------- */
  504. /** Clean the dialog area, draw the frame and the title */
  505. void
  506. dlg_default_repaint (WDialog * h)
  507. {
  508. Widget *wh = WIDGET (h);
  509. int space;
  510. if (!widget_get_state (wh, WST_ACTIVE))
  511. return;
  512. space = h->compact ? 0 : 1;
  513. tty_setcolor (h->color[DLG_COLOR_NORMAL]);
  514. dlg_erase (h);
  515. tty_draw_box (wh->y + space, wh->x + space, wh->lines - 2 * space, wh->cols - 2 * space, FALSE);
  516. if (h->title != NULL)
  517. {
  518. tty_setcolor (h->color[DLG_COLOR_TITLE]);
  519. widget_move (h, space, (wh->cols - str_term_width1 (h->title)) / 2);
  520. tty_print_string (h->title);
  521. }
  522. }
  523. /* --------------------------------------------------------------------------------------------- */
  524. /** this function allows to set dialog position */
  525. void
  526. dlg_set_position (WDialog * h, int y, int x, int lines, int cols)
  527. {
  528. Widget *wh = WIDGET (h);
  529. widget_shift_scale_t wss;
  530. /* save old positions, will be used to reposition childs */
  531. int ox, oy, oc, ol;
  532. /* save old positions, will be used to reposition childs */
  533. ox = wh->x;
  534. oy = wh->y;
  535. oc = wh->cols;
  536. ol = wh->lines;
  537. wh->x = x;
  538. wh->y = y;
  539. wh->lines = lines;
  540. wh->cols = cols;
  541. /* dialog is empty */
  542. if (h->widgets == NULL)
  543. return;
  544. if (h->current == NULL)
  545. h->current = h->widgets;
  546. /* values by which controls should be moved */
  547. wss.shift_x = wh->x - ox;
  548. wss.scale_x = wh->cols - oc;
  549. wss.shift_y = wh->y - oy;
  550. wss.scale_y = wh->lines - ol;
  551. if (wss.shift_x != 0 || wss.shift_y != 0 || wss.scale_x != 0 || wss.scale_y != 0)
  552. g_list_foreach (h->widgets, dlg_widget_set_position, &wss);
  553. }
  554. /* --------------------------------------------------------------------------------------------- */
  555. /** Set dialog size and position */
  556. void
  557. dlg_set_size (WDialog * h, int lines, int cols)
  558. {
  559. int x = 0, y = 0;
  560. dlg_adjust_position (WIDGET (h)->pos_flags, &y, &x, &lines, &cols);
  561. dlg_set_position (h, y, x, lines, cols);
  562. }
  563. /* --------------------------------------------------------------------------------------------- */
  564. /** Default dialog callback */
  565. cb_ret_t
  566. dlg_default_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
  567. {
  568. WDialog *h = DIALOG (w);
  569. (void) sender;
  570. (void) parm;
  571. (void) data;
  572. switch (msg)
  573. {
  574. case MSG_DRAW:
  575. if (h->color != NULL)
  576. {
  577. dlg_default_repaint (h);
  578. return MSG_HANDLED;
  579. }
  580. return MSG_NOT_HANDLED;
  581. case MSG_IDLE:
  582. /* we don't want endless loop */
  583. widget_idle (w, FALSE);
  584. return MSG_HANDLED;
  585. case MSG_RESIZE:
  586. /* this is default resizing mechanism */
  587. /* the main idea of this code is to resize dialog
  588. according to flags (if any of flags require automatic
  589. resizing, like WPOS_CENTER, end after that reposition
  590. controls in dialog according to flags of widget) */
  591. dlg_set_size (h, w->lines, w->cols);
  592. return MSG_HANDLED;
  593. default:
  594. break;
  595. }
  596. return MSG_NOT_HANDLED;
  597. }
  598. /* --------------------------------------------------------------------------------------------- */
  599. WDialog *
  600. dlg_create (gboolean modal, int y1, int x1, int lines, int cols, widget_pos_flags_t pos_flags,
  601. gboolean compact, const int *colors, widget_cb_fn callback,
  602. widget_mouse_cb_fn mouse_callback, const char *help_ctx, const char *title)
  603. {
  604. WDialog *new_d;
  605. Widget *w;
  606. new_d = g_new0 (WDialog, 1);
  607. w = WIDGET (new_d);
  608. dlg_adjust_position (pos_flags, &y1, &x1, &lines, &cols);
  609. widget_init (w, y1, x1, lines, cols, (callback != NULL) ? callback : dlg_default_callback,
  610. mouse_callback);
  611. w->pos_flags = pos_flags;
  612. w->options |= WOP_SELECTABLE | WOP_TOP_SELECT;
  613. w->state |= WST_CONSTRUCT | WST_FOCUSED;
  614. if (modal)
  615. w->state |= WST_MODAL;
  616. new_d->color = colors;
  617. new_d->help_ctx = help_ctx;
  618. new_d->compact = compact;
  619. new_d->data = NULL;
  620. new_d->mouse_status = MOU_UNHANDLED;
  621. /* Strip existing spaces, add one space before and after the title */
  622. if (title != NULL && *title != '\0')
  623. {
  624. char *t;
  625. t = g_strstrip (g_strdup (title));
  626. if (*t != '\0')
  627. new_d->title = g_strdup_printf (" %s ", t);
  628. g_free (t);
  629. }
  630. /* unique name of event group for this dialog */
  631. new_d->event_group = g_strdup_printf ("%s_%p", MCEVENT_GROUP_DIALOG, (void *) new_d);
  632. return new_d;
  633. }
  634. /* --------------------------------------------------------------------------------------------- */
  635. void
  636. dlg_set_default_colors (void)
  637. {
  638. dialog_colors[DLG_COLOR_NORMAL] = COLOR_NORMAL;
  639. dialog_colors[DLG_COLOR_FOCUS] = COLOR_FOCUS;
  640. dialog_colors[DLG_COLOR_HOT_NORMAL] = COLOR_HOT_NORMAL;
  641. dialog_colors[DLG_COLOR_HOT_FOCUS] = COLOR_HOT_FOCUS;
  642. dialog_colors[DLG_COLOR_TITLE] = COLOR_TITLE;
  643. alarm_colors[DLG_COLOR_NORMAL] = ERROR_COLOR;
  644. alarm_colors[DLG_COLOR_FOCUS] = ERROR_FOCUS;
  645. alarm_colors[DLG_COLOR_HOT_NORMAL] = ERROR_HOT_NORMAL;
  646. alarm_colors[DLG_COLOR_HOT_FOCUS] = ERROR_HOT_FOCUS;
  647. alarm_colors[DLG_COLOR_TITLE] = ERROR_TITLE;
  648. listbox_colors[DLG_COLOR_NORMAL] = PMENU_ENTRY_COLOR;
  649. listbox_colors[DLG_COLOR_FOCUS] = PMENU_SELECTED_COLOR;
  650. listbox_colors[DLG_COLOR_HOT_NORMAL] = PMENU_ENTRY_COLOR;
  651. listbox_colors[DLG_COLOR_HOT_FOCUS] = PMENU_SELECTED_COLOR;
  652. listbox_colors[DLG_COLOR_TITLE] = PMENU_TITLE_COLOR;
  653. }
  654. /* --------------------------------------------------------------------------------------------- */
  655. void
  656. dlg_erase (WDialog * h)
  657. {
  658. Widget *wh = WIDGET (h);
  659. if (wh != NULL && widget_get_state (wh, WST_ACTIVE))
  660. tty_fill_region (wh->y, wh->x, wh->lines, wh->cols, ' ');
  661. }
  662. /* --------------------------------------------------------------------------------------------- */
  663. /**
  664. * Insert widget to dialog before requested widget. Make the widget current. Return widget ID.
  665. */
  666. unsigned long
  667. add_widget_autopos (WDialog * h, void *w, widget_pos_flags_t pos_flags, const void *before)
  668. {
  669. Widget *wh = WIDGET (h);
  670. Widget *widget;
  671. GList *new_current;
  672. /* Don't accept 0 widgets */
  673. if (w == NULL)
  674. abort ();
  675. widget = WIDGET (w);
  676. if ((pos_flags & WPOS_CENTER_HORZ) != 0)
  677. widget->x = (wh->cols - widget->cols) / 2;
  678. widget->x += wh->x;
  679. if ((pos_flags & WPOS_CENTER_VERT) != 0)
  680. widget->y = (wh->lines - widget->lines) / 2;
  681. widget->y += wh->y;
  682. widget->owner = h;
  683. widget->pos_flags = pos_flags;
  684. widget->id = h->widget_id++;
  685. if (h->widgets == NULL || before == NULL)
  686. {
  687. h->widgets = g_list_append (h->widgets, widget);
  688. new_current = g_list_last (h->widgets);
  689. }
  690. else
  691. {
  692. GList *b;
  693. b = g_list_find (h->widgets, before);
  694. /* don't accept widget not from dialog. This shouldn't happen */
  695. if (b == NULL)
  696. abort ();
  697. b = g_list_next (b);
  698. h->widgets = g_list_insert_before (h->widgets, b, widget);
  699. if (b != NULL)
  700. new_current = g_list_previous (b);
  701. else
  702. new_current = g_list_last (h->widgets);
  703. }
  704. /* widget has been added at runtime */
  705. if (widget_get_state (wh, WST_ACTIVE))
  706. {
  707. send_message (widget, NULL, MSG_INIT, 0, NULL);
  708. widget_select (widget);
  709. }
  710. else
  711. h->current = new_current;
  712. return widget->id;
  713. }
  714. /* --------------------------------------------------------------------------------------------- */
  715. /** wrapper to simply add lefttop positioned controls */
  716. unsigned long
  717. add_widget (WDialog * h, void *w)
  718. {
  719. return add_widget_autopos (h, w, WPOS_KEEP_DEFAULT,
  720. h->current != NULL ? h->current->data : NULL);
  721. }
  722. /* --------------------------------------------------------------------------------------------- */
  723. unsigned long
  724. add_widget_before (WDialog * h, void *w, void *before)
  725. {
  726. return add_widget_autopos (h, w, WPOS_KEEP_DEFAULT, before);
  727. }
  728. /* --------------------------------------------------------------------------------------------- */
  729. /** delete widget from dialog */
  730. void
  731. del_widget (void *w)
  732. {
  733. WDialog *h;
  734. GList *d;
  735. /* Don't accept NULL widget. This shouldn't happen */
  736. if (w == NULL)
  737. abort ();
  738. h = WIDGET (w)->owner;
  739. d = g_list_find (h->widgets, w);
  740. if (d == h->current)
  741. dlg_set_current_widget_next (h);
  742. h->widgets = g_list_remove_link (h->widgets, d);
  743. if (h->widgets == NULL)
  744. h->current = NULL;
  745. send_message (d->data, NULL, MSG_DESTROY, 0, NULL);
  746. g_free (d->data);
  747. g_list_free_1 (d);
  748. /* widget has been deleted in runtime */
  749. if (widget_get_state (WIDGET (h), WST_ACTIVE))
  750. {
  751. dlg_redraw (h);
  752. dlg_select_current_widget (h);
  753. }
  754. }
  755. /* --------------------------------------------------------------------------------------------- */
  756. void
  757. do_refresh (void)
  758. {
  759. GList *d = top_dlg;
  760. if (fast_refresh)
  761. {
  762. if ((d != NULL) && (d->data != NULL))
  763. dlg_redraw (DIALOG (d->data));
  764. }
  765. else
  766. {
  767. /* Search first fullscreen dialog */
  768. for (; d != NULL; d = g_list_next (d))
  769. if (d->data != NULL && (WIDGET (d->data)->pos_flags & WPOS_FULLSCREEN) != 0)
  770. break;
  771. /* back to top dialog */
  772. for (; d != NULL; d = g_list_previous (d))
  773. if (d->data != NULL)
  774. dlg_redraw (DIALOG (d->data));
  775. }
  776. }
  777. /* --------------------------------------------------------------------------------------------- */
  778. /** broadcast a message to all the widgets in a dialog */
  779. void
  780. dlg_broadcast_msg (WDialog * h, widget_msg_t msg)
  781. {
  782. dlg_broadcast_msg_to (h, msg, FALSE, 0);
  783. }
  784. /* --------------------------------------------------------------------------------------------- */
  785. /** Find the widget with the given callback in the dialog h */
  786. Widget *
  787. find_widget_type (const WDialog * h, widget_cb_fn callback)
  788. {
  789. GList *w;
  790. w = g_list_find_custom (h->widgets, (gconstpointer) callback, dlg_find_widget_callback);
  791. return (w == NULL) ? NULL : WIDGET (w->data);
  792. }
  793. /* --------------------------------------------------------------------------------------------- */
  794. GList *
  795. dlg_find (const WDialog * h, const Widget * w)
  796. {
  797. return (w->owner == NULL || w->owner != h) ? NULL : g_list_find (h->widgets, w);
  798. }
  799. /* --------------------------------------------------------------------------------------------- */
  800. /** Find the widget with the given id */
  801. Widget *
  802. dlg_find_by_id (const WDialog * h, unsigned long id)
  803. {
  804. GList *w;
  805. w = g_list_find_custom (h->widgets, GUINT_TO_POINTER (id), dlg_find_widget_by_id);
  806. return w != NULL ? WIDGET (w->data) : NULL;
  807. }
  808. /* --------------------------------------------------------------------------------------------- */
  809. /** Find the widget with the given id in the dialog h and select it */
  810. void
  811. dlg_select_by_id (const WDialog * h, unsigned long id)
  812. {
  813. Widget *w;
  814. w = dlg_find_by_id (h, id);
  815. if (w != NULL)
  816. widget_select (w);
  817. }
  818. /* --------------------------------------------------------------------------------------------- */
  819. /** Try to select previous widget in the tab order */
  820. void
  821. dlg_select_prev_widget (WDialog * h)
  822. {
  823. dlg_select_next_or_prev (h, FALSE);
  824. }
  825. /* --------------------------------------------------------------------------------------------- */
  826. /** Try to select next widget in the tab order */
  827. void
  828. dlg_select_next_widget (WDialog * h)
  829. {
  830. dlg_select_next_or_prev (h, TRUE);
  831. }
  832. /* --------------------------------------------------------------------------------------------- */
  833. void
  834. update_cursor (WDialog * h)
  835. {
  836. GList *p = h->current;
  837. if (p != NULL && widget_get_state (WIDGET (h), WST_ACTIVE))
  838. {
  839. Widget *w;
  840. w = WIDGET (p->data);
  841. if (!widget_get_state (w, WST_DISABLED) && widget_get_options (w, WOP_WANT_CURSOR))
  842. send_message (w, NULL, MSG_CURSOR, 0, NULL);
  843. else
  844. do
  845. {
  846. p = dlg_get_widget_next_of (p);
  847. if (p == h->current)
  848. break;
  849. w = WIDGET (p->data);
  850. if (!widget_get_state (w, WST_DISABLED) && widget_get_options (w, WOP_WANT_CURSOR)
  851. && send_message (w, NULL, MSG_CURSOR, 0, NULL) == MSG_HANDLED)
  852. break;
  853. }
  854. while (TRUE);
  855. }
  856. }
  857. /* --------------------------------------------------------------------------------------------- */
  858. /**
  859. * Redraw the widgets in reverse order, leaving the current widget
  860. * as the last one
  861. */
  862. void
  863. dlg_redraw (WDialog * h)
  864. {
  865. if (!widget_get_state (WIDGET (h), WST_ACTIVE))
  866. return;
  867. if (h->winch_pending)
  868. {
  869. h->winch_pending = FALSE;
  870. send_message (h, NULL, MSG_RESIZE, 0, NULL);
  871. }
  872. send_message (h, NULL, MSG_DRAW, 0, NULL);
  873. dlg_broadcast_msg (h, MSG_DRAW);
  874. update_cursor (h);
  875. }
  876. /* --------------------------------------------------------------------------------------------- */
  877. void
  878. dlg_stop (WDialog * h)
  879. {
  880. widget_set_state (WIDGET (h), WST_CLOSED, TRUE);
  881. }
  882. /* --------------------------------------------------------------------------------------------- */
  883. /** Init the process */
  884. void
  885. dlg_init (WDialog * h)
  886. {
  887. Widget *wh = WIDGET (h);
  888. if (top_dlg != NULL && widget_get_state (WIDGET (top_dlg->data), WST_MODAL))
  889. widget_set_state (wh, WST_MODAL, TRUE);
  890. /* add dialog to the stack */
  891. top_dlg = g_list_prepend (top_dlg, h);
  892. /* Initialize dialog manager and widgets */
  893. if (widget_get_state (wh, WST_CONSTRUCT))
  894. {
  895. if (!widget_get_state (wh, WST_MODAL))
  896. dialog_switch_add (h);
  897. send_message (h, NULL, MSG_INIT, 0, NULL);
  898. dlg_broadcast_msg (h, MSG_INIT);
  899. dlg_read_history (h);
  900. }
  901. /* Select the first widget that takes focus */
  902. while (h->current != NULL && !widget_get_options (WIDGET (h->current->data), WOP_SELECTABLE)
  903. && !widget_get_state (WIDGET (h->current->data), WST_DISABLED))
  904. dlg_set_current_widget_next (h);
  905. widget_set_state (wh, WST_ACTIVE, TRUE);
  906. dlg_redraw (h);
  907. /* focus found widget */
  908. widget_set_state (WIDGET (h->current->data), WST_FOCUSED, TRUE);
  909. h->ret_value = 0;
  910. }
  911. /* --------------------------------------------------------------------------------------------- */
  912. void
  913. dlg_process_event (WDialog * h, int key, Gpm_Event * event)
  914. {
  915. if (key == EV_NONE)
  916. {
  917. if (tty_got_interrupt ())
  918. if (send_message (h, NULL, MSG_ACTION, CK_Cancel, NULL) != MSG_HANDLED)
  919. dlg_execute_cmd (h, CK_Cancel);
  920. }
  921. else if (key == EV_MOUSE)
  922. h->mouse_status = dlg_mouse_event (h, event);
  923. else
  924. dlg_key_event (h, key);
  925. }
  926. /* --------------------------------------------------------------------------------------------- */
  927. /** Shutdown the dlg_run */
  928. void
  929. dlg_run_done (WDialog * h)
  930. {
  931. top_dlg = g_list_remove (top_dlg, h);
  932. if (widget_get_state (WIDGET (h), WST_CLOSED))
  933. {
  934. send_message (h, h->current->data, MSG_END, 0, NULL);
  935. if (!widget_get_state (WIDGET (h), WST_MODAL))
  936. dialog_switch_remove (h);
  937. }
  938. }
  939. /* --------------------------------------------------------------------------------------------- */
  940. /**
  941. * Standard run dialog routine
  942. * We have to keep this routine small so that we can duplicate it's
  943. * behavior on complex routines like the file routines, this way,
  944. * they can call the dlg_process_event without rewriting all the code
  945. */
  946. int
  947. dlg_run (WDialog * h)
  948. {
  949. dlg_init (h);
  950. frontend_dlg_run (h);
  951. dlg_run_done (h);
  952. return h->ret_value;
  953. }
  954. /* --------------------------------------------------------------------------------------------- */
  955. void
  956. dlg_destroy (WDialog * h)
  957. {
  958. /* if some widgets have history, save all history at one moment here */
  959. dlg_save_history (h);
  960. dlg_broadcast_msg (h, MSG_DESTROY);
  961. g_list_free_full (h->widgets, g_free);
  962. mc_event_group_del (h->event_group);
  963. g_free (h->event_group);
  964. g_free (h->title);
  965. g_free (h);
  966. do_refresh ();
  967. }
  968. /* --------------------------------------------------------------------------------------------- */
  969. /**
  970. * Write history to the ${XDG_CACHE_HOME}/mc/history file
  971. */
  972. void
  973. dlg_save_history (WDialog * h)
  974. {
  975. char *profile;
  976. int i;
  977. if (num_history_items_recorded == 0) /* this is how to disable */
  978. return;
  979. profile = mc_config_get_full_path (MC_HISTORY_FILE);
  980. i = open (profile, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
  981. if (i != -1)
  982. close (i);
  983. /* Make sure the history is only readable by the user */
  984. if (chmod (profile, S_IRUSR | S_IWUSR) != -1 || errno == ENOENT)
  985. {
  986. ev_history_load_save_t event_data;
  987. event_data.cfg = mc_config_init (profile, FALSE);
  988. event_data.receiver = NULL;
  989. /* get all histories in dialog */
  990. mc_event_raise (h->event_group, MCEVENT_HISTORY_SAVE, &event_data);
  991. mc_config_save_file (event_data.cfg, NULL);
  992. mc_config_deinit (event_data.cfg);
  993. }
  994. g_free (profile);
  995. }
  996. /* --------------------------------------------------------------------------------------------- */
  997. char *
  998. dlg_get_title (const WDialog * h, size_t len)
  999. {
  1000. char *t;
  1001. if (h == NULL)
  1002. abort ();
  1003. if (h->get_title != NULL)
  1004. t = h->get_title (h, len);
  1005. else
  1006. t = g_strdup ("");
  1007. return t;
  1008. }
  1009. /* --------------------------------------------------------------------------------------------- */
  1010. /**
  1011. * Switch current widget to widget after current in dialog.
  1012. *
  1013. * @param h WDialog widget
  1014. */
  1015. void
  1016. dlg_set_current_widget_next (WDialog * h)
  1017. {
  1018. h->current = dlg_get_next_or_prev_of (h->current, TRUE);
  1019. }
  1020. /* --------------------------------------------------------------------------------------------- */
  1021. /**
  1022. * Switch current widget to widget before current in dialog.
  1023. *
  1024. * @param h WDialog widget
  1025. */
  1026. void
  1027. dlg_set_current_widget_prev (WDialog * h)
  1028. {
  1029. h->current = dlg_get_next_or_prev_of (h->current, FALSE);
  1030. }
  1031. /* --------------------------------------------------------------------------------------------- */
  1032. /**
  1033. * Get widget that is after specified widget in dialog.
  1034. *
  1035. * @param w widget holder
  1036. *
  1037. * @return widget that is after "w" or NULL if "w" is NULL or widget doesn't have owner
  1038. */
  1039. GList *
  1040. dlg_get_widget_next_of (GList * w)
  1041. {
  1042. return dlg_get_next_or_prev_of (w, TRUE);
  1043. }
  1044. /* --------------------------------------------------------------------------------------------- */
  1045. /**
  1046. * Get widget that is before specified widget in dialog.
  1047. *
  1048. * @param w widget holder
  1049. *
  1050. * @return widget that is before "w" or NULL if "w" is NULL or widget doesn't have owner
  1051. */
  1052. GList *
  1053. dlg_get_widget_prev_of (GList * w)
  1054. {
  1055. return dlg_get_next_or_prev_of (w, FALSE);
  1056. }
  1057. /* --------------------------------------------------------------------------------------------- */