dialog.c 35 KB

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