dialog.c 36 KB

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