dialog.c 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353
  1. /*
  2. Dialog box features module for the Midnight Commander
  3. Copyright (C) 1994-2015
  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 <fcntl.h> /* open() */
  29. #include "lib/global.h"
  30. #include "lib/tty/tty.h"
  31. #include "lib/skin.h"
  32. #include "lib/tty/key.h"
  33. #include "lib/strutil.h"
  34. #include "lib/widget.h"
  35. #include "lib/fileloc.h" /* MC_HISTORY_FILE */
  36. #include "lib/event.h" /* mc_event_dispatch() */
  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. 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_dispatch (h->event_group, MCEVENT_HISTORY_LOAD, &event_data, NULL, NULL);
  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. event_return_t event_ret;
  216. const char *event_group_name = MCEVENT_GROUP_WIDGET_DIALOG;
  217. const char *event_name = NULL;
  218. void *event_data = h;
  219. event_ret.b = TRUE;
  220. switch (command)
  221. {
  222. case CK_Ok:
  223. h->ret_value = B_ENTER;
  224. dlg_stop (h);
  225. break;
  226. case CK_Cancel:
  227. h->ret_value = B_CANCEL;
  228. dlg_stop (h);
  229. break;
  230. case CK_Up:
  231. case CK_Left:
  232. dlg_one_up (h);
  233. break;
  234. case CK_Down:
  235. case CK_Right:
  236. dlg_one_down (h);
  237. break;
  238. case CK_Help:
  239. {
  240. ev_help_t help_event_data = { NULL, h->help_ctx };
  241. mc_event_dispatch (MCEVENT_GROUP_CORE, "help", &help_event_data, NULL, NULL);
  242. }
  243. break;
  244. case CK_Suspend:
  245. mc_event_dispatch (MCEVENT_GROUP_CORE, "suspend", NULL, NULL, NULL);
  246. refresh_cmd ();
  247. break;
  248. case CK_Refresh:
  249. refresh_cmd ();
  250. break;
  251. case CK_ScreenList:
  252. event_name = "show_dialog_list";
  253. break;
  254. case CK_ScreenNext:
  255. if (!h->modal)
  256. dialog_switch_next ();
  257. else
  258. ret = MSG_NOT_HANDLED;
  259. break;
  260. case CK_ScreenPrev:
  261. if (!h->modal)
  262. dialog_switch_prev ();
  263. else
  264. ret = MSG_NOT_HANDLED;
  265. break;
  266. default:
  267. ret = MSG_NOT_HANDLED;
  268. }
  269. if (mc_event_dispatch (event_group_name, event_name, event_data, &event_ret, NULL))
  270. {
  271. return (event_ret.b) ? MSG_HANDLED : MSG_NOT_HANDLED;
  272. }
  273. return ret;
  274. }
  275. /* --------------------------------------------------------------------------------------------- */
  276. static cb_ret_t
  277. dlg_handle_key (WDialog * h, int d_key)
  278. {
  279. unsigned long command;
  280. command = keybind_lookup_keymap_command (dialog_map, d_key);
  281. if (command == CK_IgnoreKey)
  282. return MSG_NOT_HANDLED;
  283. if (send_message (h, NULL, MSG_ACTION, command, NULL) == MSG_HANDLED
  284. || dlg_execute_cmd (h, command) == MSG_HANDLED)
  285. return MSG_HANDLED;
  286. return MSG_NOT_HANDLED;
  287. }
  288. /* --------------------------------------------------------------------------------------------- */
  289. static int
  290. dlg_mouse_event (WDialog * h, Gpm_Event * event)
  291. {
  292. Widget *wh = WIDGET (h);
  293. GList *p, *first;
  294. /* close the dialog by mouse left click out of dialog area */
  295. if (mouse_close_dialog && !h->fullscreen && ((event->buttons & GPM_B_LEFT) != 0)
  296. && ((event->type & GPM_DOWN) != 0) && !mouse_global_in_widget (event, wh))
  297. {
  298. h->ret_value = B_CANCEL;
  299. dlg_stop (h);
  300. return MOU_NORMAL;
  301. }
  302. if (wh->mouse != NULL)
  303. {
  304. int mou;
  305. mou = wh->mouse (event, wh);
  306. if (mou != MOU_UNHANDLED)
  307. return mou;
  308. }
  309. first = h->current;
  310. p = first;
  311. do
  312. {
  313. Widget *w = WIDGET (p->data);
  314. p = dlg_widget_prev (h, p);
  315. if ((w->options & W_DISABLED) == 0 && w->mouse != NULL)
  316. {
  317. /* put global cursor position to the widget */
  318. int ret;
  319. ret = w->mouse (event, w);
  320. if (ret != MOU_UNHANDLED)
  321. return ret;
  322. }
  323. }
  324. while (p != first);
  325. return MOU_UNHANDLED;
  326. }
  327. /* --------------------------------------------------------------------------------------------- */
  328. static cb_ret_t
  329. dlg_try_hotkey (WDialog * h, int d_key)
  330. {
  331. GList *hot_cur;
  332. Widget *current;
  333. cb_ret_t handled;
  334. int c;
  335. if (h->widgets == NULL)
  336. return MSG_NOT_HANDLED;
  337. if (h->current == NULL)
  338. h->current = h->widgets;
  339. /*
  340. * Explanation: we don't send letter hotkeys to other widgets if
  341. * the currently selected widget is an input line
  342. */
  343. current = WIDGET (h->current->data);
  344. if ((current->options & W_DISABLED) != 0)
  345. return MSG_NOT_HANDLED;
  346. if (current->options & W_IS_INPUT)
  347. {
  348. /* skip ascii control characters, anything else can valid character in
  349. * some encoding */
  350. if (d_key >= 32 && d_key < 256)
  351. return MSG_NOT_HANDLED;
  352. }
  353. /* If it's an alt key, send the message */
  354. c = d_key & ~ALT (0);
  355. if (d_key & ALT (0) && g_ascii_isalpha (c))
  356. d_key = g_ascii_tolower (c);
  357. handled = MSG_NOT_HANDLED;
  358. if ((current->options & W_WANT_HOTKEY) != 0)
  359. handled = send_message (current, NULL, MSG_HOTKEY, d_key, NULL);
  360. /* If not used, send hotkey to other widgets */
  361. if (handled == MSG_HANDLED)
  362. return MSG_HANDLED;
  363. hot_cur = dlg_widget_next (h, h->current);
  364. /* send it to all widgets */
  365. while (h->current != hot_cur && handled == MSG_NOT_HANDLED)
  366. {
  367. current = WIDGET (hot_cur->data);
  368. if ((current->options & W_WANT_HOTKEY) != 0 && (current->options & W_DISABLED) == 0)
  369. handled = send_message (current, NULL, MSG_HOTKEY, d_key, NULL);
  370. if (handled == MSG_NOT_HANDLED)
  371. hot_cur = dlg_widget_next (h, hot_cur);
  372. }
  373. if (handled == MSG_HANDLED)
  374. do_select_widget (h, hot_cur, SELECT_EXACT);
  375. return handled;
  376. }
  377. /* --------------------------------------------------------------------------------------------- */
  378. static void
  379. dlg_key_event (WDialog * h, int d_key)
  380. {
  381. cb_ret_t handled;
  382. if (h->widgets == NULL)
  383. return;
  384. if (h->current == NULL)
  385. h->current = h->widgets;
  386. /* TAB used to cycle */
  387. if ((h->flags & DLG_WANT_TAB) == 0)
  388. {
  389. if (d_key == '\t')
  390. {
  391. dlg_one_down (h);
  392. return;
  393. }
  394. else if ((d_key & ~(KEY_M_SHIFT | KEY_M_CTRL)) == '\t')
  395. {
  396. dlg_one_up (h);
  397. return;
  398. }
  399. }
  400. /* first can dlg_callback handle the key */
  401. handled = send_message (h, NULL, MSG_KEY, d_key, NULL);
  402. /* next try the hotkey */
  403. if (handled == MSG_NOT_HANDLED)
  404. handled = dlg_try_hotkey (h, d_key);
  405. if (handled == MSG_HANDLED)
  406. send_message (h, NULL, MSG_HOTKEY_HANDLED, 0, NULL);
  407. else
  408. /* not used - then try widget_callback */
  409. handled = send_message (h->current->data, NULL, MSG_KEY, d_key, NULL);
  410. /* not used- try to use the unhandled case */
  411. if (handled == MSG_NOT_HANDLED)
  412. handled = send_message (h, NULL, MSG_UNHANDLED_KEY, d_key, NULL);
  413. if (handled == MSG_NOT_HANDLED)
  414. handled = dlg_handle_key (h, d_key);
  415. (void) handled;
  416. send_message (h, NULL, MSG_POST_KEY, d_key, NULL);
  417. }
  418. /* --------------------------------------------------------------------------------------------- */
  419. static void
  420. frontend_dlg_run (WDialog * h)
  421. {
  422. Gpm_Event event;
  423. event.x = -1;
  424. /* close opened editors, viewers, etc */
  425. if (!h->modal && mc_global.midnight_shutdown)
  426. {
  427. send_message (h, NULL, MSG_VALIDATE, 0, NULL);
  428. return;
  429. }
  430. while (h->state == DLG_ACTIVE)
  431. {
  432. int d_key;
  433. if (mc_global.tty.winch_flag != 0)
  434. dialog_change_screen_size ();
  435. if (is_idle ())
  436. {
  437. if (idle_hook)
  438. execute_hooks (idle_hook);
  439. while ((WIDGET (h)->options & W_WANT_IDLE) != 0 && is_idle ())
  440. send_message (h, NULL, MSG_IDLE, 0, NULL);
  441. /* Allow terminating the dialog from the idle handler */
  442. if (h->state != DLG_ACTIVE)
  443. break;
  444. }
  445. update_cursor (h);
  446. /* Clear interrupt flag */
  447. tty_got_interrupt ();
  448. d_key = tty_get_event (&event, h->mouse_status == MOU_REPEAT, TRUE);
  449. dlg_process_event (h, d_key, &event);
  450. if (h->state == DLG_CLOSED)
  451. send_message (h, NULL, MSG_VALIDATE, 0, NULL);
  452. }
  453. }
  454. /* --------------------------------------------------------------------------------------------- */
  455. static int
  456. dlg_find_widget_by_id (gconstpointer a, gconstpointer b)
  457. {
  458. Widget *w = WIDGET (a);
  459. unsigned long id = GPOINTER_TO_UINT (b);
  460. return w->id == id ? 0 : 1;
  461. }
  462. /* --------------------------------------------------------------------------------------------- */
  463. /*** public functions ****************************************************************************/
  464. /* --------------------------------------------------------------------------------------------- */
  465. /** Clean the dialog area, draw the frame and the title */
  466. void
  467. dlg_default_repaint (WDialog * h)
  468. {
  469. Widget *wh = WIDGET (h);
  470. int space;
  471. if (h->state != DLG_ACTIVE)
  472. return;
  473. space = (h->flags & DLG_COMPACT) ? 0 : 1;
  474. tty_setcolor (h->color[DLG_COLOR_NORMAL]);
  475. dlg_erase (h);
  476. tty_draw_box (wh->y + space, wh->x + space, wh->lines - 2 * space, wh->cols - 2 * space, FALSE);
  477. if (h->title != NULL)
  478. {
  479. tty_setcolor (h->color[DLG_COLOR_TITLE]);
  480. widget_move (h, space, (wh->cols - str_term_width1 (h->title)) / 2);
  481. tty_print_string (h->title);
  482. }
  483. }
  484. /* --------------------------------------------------------------------------------------------- */
  485. /** this function allows to set dialog position */
  486. void
  487. dlg_set_position (WDialog * h, int y1, int x1, int y2, int x2)
  488. {
  489. Widget *wh = WIDGET (h);
  490. /* save old positions, will be used to reposition childs */
  491. int ox, oy, oc, ol;
  492. int shift_x, shift_y, scale_x, scale_y;
  493. /* save old positions, will be used to reposition childs */
  494. ox = wh->x;
  495. oy = wh->y;
  496. oc = wh->cols;
  497. ol = wh->lines;
  498. wh->x = x1;
  499. wh->y = y1;
  500. wh->lines = y2 - y1;
  501. wh->cols = x2 - x1;
  502. /* dialog is empty */
  503. if (h->widgets == NULL)
  504. return;
  505. if (h->current == NULL)
  506. h->current = h->widgets;
  507. /* values by which controls should be moved */
  508. shift_x = wh->x - ox;
  509. shift_y = wh->y - oy;
  510. scale_x = wh->cols - oc;
  511. scale_y = wh->lines - ol;
  512. if ((shift_x != 0) || (shift_y != 0) || (scale_x != 0) || (scale_y != 0))
  513. {
  514. GList *w;
  515. for (w = h->widgets; w != NULL; w = g_list_next (w))
  516. {
  517. /* there are, mainly, 2 generally possible
  518. situations:
  519. 1. control sticks to one side - it
  520. should be moved
  521. 2. control sticks to two sides of
  522. one direction - it should be sized */
  523. Widget *c = WIDGET (w->data);
  524. int x = c->x;
  525. int y = c->y;
  526. int cols = c->cols;
  527. int lines = c->lines;
  528. if ((c->pos_flags & WPOS_CENTER_HORZ) != 0)
  529. x = wh->x + (wh->cols - c->cols) / 2;
  530. else if ((c->pos_flags & WPOS_KEEP_LEFT) != 0 && (c->pos_flags & WPOS_KEEP_RIGHT) != 0)
  531. {
  532. x += shift_x;
  533. cols += scale_x;
  534. }
  535. else if ((c->pos_flags & WPOS_KEEP_LEFT) != 0)
  536. x += shift_x;
  537. else if ((c->pos_flags & WPOS_KEEP_RIGHT) != 0)
  538. x += shift_x + scale_x;
  539. if ((c->pos_flags & WPOS_CENTER_VERT) != 0)
  540. y = wh->y + (wh->lines - c->lines) / 2;
  541. else if ((c->pos_flags & WPOS_KEEP_TOP) != 0 && (c->pos_flags & WPOS_KEEP_BOTTOM) != 0)
  542. {
  543. y += shift_y;
  544. lines += scale_y;
  545. }
  546. else if ((c->pos_flags & WPOS_KEEP_TOP) != 0)
  547. y += shift_y;
  548. else if ((c->pos_flags & WPOS_KEEP_BOTTOM) != 0)
  549. y += shift_y + scale_y;
  550. widget_set_size (c, y, x, lines, cols);
  551. }
  552. }
  553. }
  554. /* --------------------------------------------------------------------------------------------- */
  555. /** Set dialog size and position */
  556. void
  557. dlg_set_size (WDialog * h, int lines, int cols)
  558. {
  559. int x = WIDGET (h)->x;
  560. int y = WIDGET (h)->y;
  561. if ((h->flags & DLG_CENTER) != 0)
  562. {
  563. y = (LINES - lines) / 2;
  564. x = (COLS - cols) / 2;
  565. }
  566. if ((h->flags & DLG_TRYUP) != 0)
  567. {
  568. if (y > 3)
  569. y -= 2;
  570. else if (y == 3)
  571. y = 2;
  572. }
  573. dlg_set_position (h, y, x, y + lines, x + cols);
  574. }
  575. /* --------------------------------------------------------------------------------------------- */
  576. /** Default dialog callback */
  577. cb_ret_t
  578. dlg_default_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
  579. {
  580. WDialog *h = DIALOG (w);
  581. (void) sender;
  582. (void) parm;
  583. (void) data;
  584. switch (msg)
  585. {
  586. case MSG_DRAW:
  587. if (h->color != NULL)
  588. {
  589. dlg_default_repaint (h);
  590. return MSG_HANDLED;
  591. }
  592. return MSG_NOT_HANDLED;
  593. case MSG_IDLE:
  594. dlg_broadcast_msg_to (h, MSG_IDLE, FALSE, W_WANT_IDLE);
  595. return MSG_HANDLED;
  596. case MSG_RESIZE:
  597. /* this is default resizing mechanism */
  598. /* the main idea of this code is to resize dialog
  599. according to flags (if any of flags require automatic
  600. resizing, like DLG_CENTER, end after that reposition
  601. controls in dialog according to flags of widget) */
  602. dlg_set_size (h, w->lines, w->cols);
  603. return MSG_HANDLED;
  604. default:
  605. break;
  606. }
  607. return MSG_NOT_HANDLED;
  608. }
  609. /* --------------------------------------------------------------------------------------------- */
  610. WDialog *
  611. dlg_create (gboolean modal, int y1, int x1, int lines, int cols,
  612. const int *colors, widget_cb_fn callback, mouse_h mouse_handler,
  613. const char *help_ctx, const char *title, dlg_flags_t flags)
  614. {
  615. WDialog *new_d;
  616. Widget *w;
  617. new_d = g_new0 (WDialog, 1);
  618. w = WIDGET (new_d);
  619. widget_init (w, y1, x1, lines, cols, (callback != NULL) ? callback : dlg_default_callback,
  620. mouse_handler);
  621. widget_want_cursor (w, FALSE);
  622. new_d->state = DLG_CONSTRUCT;
  623. new_d->modal = modal;
  624. new_d->color = colors;
  625. new_d->help_ctx = help_ctx;
  626. new_d->flags = flags;
  627. new_d->data = NULL;
  628. dlg_set_size (new_d, lines, cols);
  629. new_d->fullscreen = (w->x == 0 && w->y == 0 && w->cols == COLS && w->lines == LINES);
  630. new_d->mouse_status = MOU_UNHANDLED;
  631. /* Strip existing spaces, add one space before and after the title */
  632. if (title != NULL && *title != '\0')
  633. {
  634. char *t;
  635. t = g_strstrip (g_strdup (title));
  636. if (*t != '\0')
  637. new_d->title = g_strdup_printf (" %s ", t);
  638. g_free (t);
  639. }
  640. /* unique name of event group for this dialog */
  641. new_d->event_group = g_strdup_printf ("%s_%p", MCEVENT_GROUP_WIDGET_DIALOG, (void *) new_d);
  642. return new_d;
  643. }
  644. /* --------------------------------------------------------------------------------------------- */
  645. void
  646. dlg_set_default_colors (void)
  647. {
  648. dialog_colors[DLG_COLOR_NORMAL] = COLOR_NORMAL;
  649. dialog_colors[DLG_COLOR_FOCUS] = COLOR_FOCUS;
  650. dialog_colors[DLG_COLOR_HOT_NORMAL] = COLOR_HOT_NORMAL;
  651. dialog_colors[DLG_COLOR_HOT_FOCUS] = COLOR_HOT_FOCUS;
  652. dialog_colors[DLG_COLOR_TITLE] = COLOR_TITLE;
  653. alarm_colors[DLG_COLOR_NORMAL] = ERROR_COLOR;
  654. alarm_colors[DLG_COLOR_FOCUS] = ERROR_FOCUS;
  655. alarm_colors[DLG_COLOR_HOT_NORMAL] = ERROR_HOT_NORMAL;
  656. alarm_colors[DLG_COLOR_HOT_FOCUS] = ERROR_HOT_FOCUS;
  657. alarm_colors[DLG_COLOR_TITLE] = ERROR_TITLE;
  658. listbox_colors[DLG_COLOR_NORMAL] = PMENU_ENTRY_COLOR;
  659. listbox_colors[DLG_COLOR_FOCUS] = PMENU_SELECTED_COLOR;
  660. listbox_colors[DLG_COLOR_HOT_NORMAL] = PMENU_ENTRY_COLOR;
  661. listbox_colors[DLG_COLOR_HOT_FOCUS] = PMENU_SELECTED_COLOR;
  662. listbox_colors[DLG_COLOR_TITLE] = PMENU_TITLE_COLOR;
  663. }
  664. /* --------------------------------------------------------------------------------------------- */
  665. void
  666. dlg_erase (WDialog * h)
  667. {
  668. if ((h != NULL) && (h->state == DLG_ACTIVE))
  669. {
  670. Widget *wh = WIDGET (h);
  671. tty_fill_region (wh->y, wh->x, wh->lines, wh->cols, ' ');
  672. }
  673. }
  674. /* --------------------------------------------------------------------------------------------- */
  675. /**
  676. * Insert widget to dialog before requested widget. Make the widget current. Return widget ID.
  677. */
  678. unsigned long
  679. add_widget_autopos (WDialog * h, void *w, widget_pos_flags_t pos_flags, const void *before)
  680. {
  681. Widget *wh = WIDGET (h);
  682. Widget *widget;
  683. /* Don't accept 0 widgets */
  684. if (w == NULL)
  685. abort ();
  686. widget = WIDGET (w);
  687. if ((pos_flags & WPOS_CENTER_HORZ) != 0)
  688. widget->x = (wh->cols - widget->cols) / 2;
  689. widget->x += wh->x;
  690. if ((pos_flags & WPOS_CENTER_VERT) != 0)
  691. widget->y = (wh->lines - widget->lines) / 2;
  692. widget->y += wh->y;
  693. widget->owner = h;
  694. widget->pos_flags = pos_flags;
  695. widget->id = h->widget_id++;
  696. if (h->widgets == NULL || before == NULL)
  697. {
  698. h->widgets = g_list_append (h->widgets, widget);
  699. h->current = g_list_last (h->widgets);
  700. }
  701. else
  702. {
  703. GList *b;
  704. b = g_list_find (h->widgets, before);
  705. /* don't accept widget not from dialog. This shouldn't happen */
  706. if (b == NULL)
  707. abort ();
  708. b = g_list_next (b);
  709. h->widgets = g_list_insert_before (h->widgets, b, widget);
  710. if (b != NULL)
  711. h->current = g_list_previous (b);
  712. else
  713. h->current = g_list_last (h->widgets);
  714. }
  715. /* widget has been added in runtime */
  716. if (h->state == DLG_ACTIVE)
  717. {
  718. send_message (widget, NULL, MSG_INIT, 0, NULL);
  719. send_message (widget, NULL, MSG_DRAW, 0, NULL);
  720. send_message (widget, NULL, MSG_FOCUS, 0, NULL);
  721. }
  722. return widget->id;
  723. }
  724. /* --------------------------------------------------------------------------------------------- */
  725. /** wrapper to simply add lefttop positioned controls */
  726. unsigned long
  727. add_widget (WDialog * h, void *w)
  728. {
  729. return add_widget_autopos (h, w, WPOS_KEEP_DEFAULT,
  730. h->current != NULL ? h->current->data : NULL);
  731. }
  732. /* --------------------------------------------------------------------------------------------- */
  733. unsigned long
  734. add_widget_before (WDialog * h, void *w, void *before)
  735. {
  736. return add_widget_autopos (h, w, WPOS_KEEP_DEFAULT, before);
  737. }
  738. /* --------------------------------------------------------------------------------------------- */
  739. /** delete widget from dialog */
  740. void
  741. del_widget (void *w)
  742. {
  743. WDialog *h;
  744. GList *d;
  745. /* Don't accept NULL widget. This shouldn't happen */
  746. if (w == NULL)
  747. abort ();
  748. h = WIDGET (w)->owner;
  749. d = g_list_find (h->widgets, w);
  750. if (d == h->current)
  751. h->current = dlg_widget_next (h, d);
  752. h->widgets = g_list_remove_link (h->widgets, d);
  753. send_message (d->data, NULL, MSG_DESTROY, 0, NULL);
  754. g_free (d->data);
  755. g_list_free_1 (d);
  756. /* widget has been deleted in runtime */
  757. if (h->state == DLG_ACTIVE)
  758. {
  759. dlg_redraw (h);
  760. dlg_focus (h);
  761. }
  762. }
  763. /* --------------------------------------------------------------------------------------------- */
  764. void
  765. do_refresh (void)
  766. {
  767. GList *d = top_dlg;
  768. if (fast_refresh)
  769. {
  770. if ((d != NULL) && (d->data != NULL))
  771. dlg_redraw (DIALOG (d->data));
  772. }
  773. else
  774. {
  775. /* Search first fullscreen dialog */
  776. for (; d != NULL; d = g_list_next (d))
  777. if (d->data != NULL && DIALOG (d->data)->fullscreen)
  778. break;
  779. /* back to top dialog */
  780. for (; d != NULL; d = g_list_previous (d))
  781. if (d->data != NULL)
  782. dlg_redraw (DIALOG (d->data));
  783. }
  784. }
  785. /* --------------------------------------------------------------------------------------------- */
  786. /** broadcast a message to all the widgets in a dialog */
  787. void
  788. dlg_broadcast_msg (WDialog * h, widget_msg_t msg)
  789. {
  790. dlg_broadcast_msg_to (h, msg, FALSE, 0);
  791. }
  792. /* --------------------------------------------------------------------------------------------- */
  793. gboolean
  794. dlg_focus (WDialog * h)
  795. {
  796. /* cannot focus disabled widget */
  797. if ((h->current != NULL) && (h->state == DLG_CONSTRUCT || h->state == DLG_ACTIVE))
  798. {
  799. Widget *current = WIDGET (h->current->data);
  800. if (((current->options & W_DISABLED) == 0)
  801. && (send_message (current, NULL, MSG_FOCUS, 0, NULL) == MSG_HANDLED))
  802. {
  803. send_message (h, current, MSG_FOCUS, 0, NULL);
  804. return TRUE;
  805. }
  806. }
  807. return FALSE;
  808. }
  809. /* --------------------------------------------------------------------------------------------- */
  810. /** Find the widget with the given callback in the dialog h */
  811. Widget *
  812. find_widget_type (const WDialog * h, widget_cb_fn callback)
  813. {
  814. GList *w;
  815. w = g_list_find_custom (h->widgets, callback, dlg_find_widget_callback);
  816. return (w == NULL) ? NULL : WIDGET (w->data);
  817. }
  818. /* --------------------------------------------------------------------------------------------- */
  819. /** Find the widget with the given id */
  820. Widget *
  821. dlg_find_by_id (const WDialog * h, unsigned long id)
  822. {
  823. GList *w;
  824. w = g_list_find_custom (h->widgets, GUINT_TO_POINTER (id), dlg_find_widget_by_id);
  825. return w != NULL ? WIDGET (w->data) : NULL;
  826. }
  827. /* --------------------------------------------------------------------------------------------- */
  828. /** Find the widget with the given id in the dialog h and select it */
  829. void
  830. dlg_select_by_id (const WDialog * h, unsigned long id)
  831. {
  832. Widget *w;
  833. w = dlg_find_by_id (h, id);
  834. if (w != NULL)
  835. dlg_select_widget (w);
  836. }
  837. /* --------------------------------------------------------------------------------------------- */
  838. /**
  839. * Try to select widget in the dialog.
  840. */
  841. void
  842. dlg_select_widget (void *w)
  843. {
  844. Widget *widget = WIDGET (w);
  845. WDialog *h = widget->owner;
  846. do_select_widget (h, g_list_find (h->widgets, widget), SELECT_EXACT);
  847. }
  848. /* --------------------------------------------------------------------------------------------- */
  849. /**
  850. * Set widget at top of widget list and make it current.
  851. */
  852. void
  853. dlg_set_top_widget (void *w)
  854. {
  855. Widget *widget = WIDGET (w);
  856. WDialog *h = widget->owner;
  857. GList *l;
  858. l = g_list_find (h->widgets, w);
  859. if (l == NULL)
  860. abort (); /* widget is not in dialog, this should not happen */
  861. /* unfocus prevoius widget and focus current one before widget reordering */
  862. if (h->state == DLG_ACTIVE)
  863. do_select_widget (h, l, SELECT_EXACT);
  864. /* widget reordering */
  865. h->widgets = g_list_remove_link (h->widgets, l);
  866. h->widgets = g_list_concat (h->widgets, l);
  867. h->current = l;
  868. }
  869. /* --------------------------------------------------------------------------------------------- */
  870. /** Try to select previous widget in the tab order */
  871. void
  872. dlg_one_up (WDialog * h)
  873. {
  874. if (h->widgets != NULL)
  875. do_select_widget (h, dlg_widget_prev (h, h->current), SELECT_PREV);
  876. }
  877. /* --------------------------------------------------------------------------------------------- */
  878. /** Try to select next widget in the tab order */
  879. void
  880. dlg_one_down (WDialog * h)
  881. {
  882. if (h->widgets != NULL)
  883. do_select_widget (h, dlg_widget_next (h, h->current), SELECT_NEXT);
  884. }
  885. /* --------------------------------------------------------------------------------------------- */
  886. void
  887. update_cursor (WDialog * h)
  888. {
  889. GList *p = h->current;
  890. if ((p != NULL) && (h->state == DLG_ACTIVE))
  891. {
  892. Widget *w;
  893. w = WIDGET (p->data);
  894. if (((w->options & W_DISABLED) == 0) && ((w->options & W_WANT_CURSOR) != 0))
  895. send_message (w, NULL, MSG_CURSOR, 0, NULL);
  896. else
  897. do
  898. {
  899. p = dlg_widget_next (h, p);
  900. if (p == h->current)
  901. break;
  902. w = WIDGET (p->data);
  903. if (((w->options & W_DISABLED) == 0) && ((w->options & W_WANT_CURSOR) != 0))
  904. if (send_message (w, NULL, MSG_CURSOR, 0, NULL) == MSG_HANDLED)
  905. break;
  906. }
  907. while (TRUE);
  908. }
  909. }
  910. /* --------------------------------------------------------------------------------------------- */
  911. /**
  912. * Redraw the widgets in reverse order, leaving the current widget
  913. * as the last one
  914. */
  915. void
  916. dlg_redraw (WDialog * h)
  917. {
  918. if (h->state != DLG_ACTIVE)
  919. return;
  920. if (h->winch_pending)
  921. {
  922. h->winch_pending = FALSE;
  923. send_message (h, NULL, MSG_RESIZE, 0, NULL);
  924. }
  925. send_message (h, NULL, MSG_DRAW, 0, NULL);
  926. dlg_broadcast_msg (h, MSG_DRAW);
  927. update_cursor (h);
  928. }
  929. /* --------------------------------------------------------------------------------------------- */
  930. void
  931. dlg_stop (WDialog * h)
  932. {
  933. h->state = DLG_CLOSED;
  934. }
  935. /* --------------------------------------------------------------------------------------------- */
  936. /** Init the process */
  937. void
  938. dlg_init (WDialog * h)
  939. {
  940. if (top_dlg != NULL && DIALOG (top_dlg->data)->modal)
  941. h->modal = TRUE;
  942. /* add dialog to the stack */
  943. top_dlg = g_list_prepend (top_dlg, h);
  944. /* Initialize dialog manager and widgets */
  945. if (h->state == DLG_CONSTRUCT)
  946. {
  947. if (!h->modal)
  948. dialog_switch_add (h);
  949. send_message (h, NULL, MSG_INIT, 0, NULL);
  950. dlg_broadcast_msg (h, MSG_INIT);
  951. dlg_read_history (h);
  952. }
  953. h->state = DLG_ACTIVE;
  954. /* first send MSG_DRAW to dialog itself and all widgets... */
  955. dlg_redraw (h);
  956. /* ...then send MSG_FOCUS to select the first widget that can take focus */
  957. while (h->current != NULL && !dlg_focus (h))
  958. h->current = dlg_widget_next (h, h->current);
  959. h->ret_value = 0;
  960. }
  961. /* --------------------------------------------------------------------------------------------- */
  962. void
  963. dlg_process_event (WDialog * h, int key, Gpm_Event * event)
  964. {
  965. if (key == EV_NONE)
  966. {
  967. if (tty_got_interrupt ())
  968. if (send_message (h, NULL, MSG_ACTION, CK_Cancel, NULL) != MSG_HANDLED)
  969. dlg_execute_cmd (h, CK_Cancel);
  970. return;
  971. }
  972. if (key == EV_MOUSE)
  973. h->mouse_status = dlg_mouse_event (h, event);
  974. else
  975. dlg_key_event (h, key);
  976. }
  977. /* --------------------------------------------------------------------------------------------- */
  978. /** Shutdown the dlg_run */
  979. void
  980. dlg_run_done (WDialog * h)
  981. {
  982. top_dlg = g_list_remove (top_dlg, h);
  983. if (h->state == DLG_CLOSED)
  984. {
  985. send_message (h, h->current->data, MSG_END, 0, NULL);
  986. if (!h->modal)
  987. dialog_switch_remove (h);
  988. }
  989. }
  990. /* --------------------------------------------------------------------------------------------- */
  991. /**
  992. * Standard run dialog routine
  993. * We have to keep this routine small so that we can duplicate it's
  994. * behavior on complex routines like the file routines, this way,
  995. * they can call the dlg_process_event without rewriting all the code
  996. */
  997. int
  998. dlg_run (WDialog * h)
  999. {
  1000. dlg_init (h);
  1001. frontend_dlg_run (h);
  1002. dlg_run_done (h);
  1003. return h->ret_value;
  1004. }
  1005. /* --------------------------------------------------------------------------------------------- */
  1006. void
  1007. dlg_destroy (WDialog * h)
  1008. {
  1009. /* if some widgets have history, save all history at one moment here */
  1010. dlg_save_history (h);
  1011. dlg_broadcast_msg (h, MSG_DESTROY);
  1012. g_list_free_full (h->widgets, g_free);
  1013. mc_event_group_del (h->event_group);
  1014. g_free (h->event_group);
  1015. g_free (h->title);
  1016. g_free (h);
  1017. do_refresh ();
  1018. }
  1019. /* --------------------------------------------------------------------------------------------- */
  1020. /**
  1021. * Write history to the ${XDG_CACHE_HOME}/mc/history file
  1022. */
  1023. void
  1024. dlg_save_history (WDialog * h)
  1025. {
  1026. char *profile;
  1027. int i;
  1028. if (num_history_items_recorded == 0) /* this is how to disable */
  1029. return;
  1030. profile = mc_config_get_full_path (MC_HISTORY_FILE);
  1031. i = open (profile, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
  1032. if (i != -1)
  1033. close (i);
  1034. /* Make sure the history is only readable by the user */
  1035. if (chmod (profile, S_IRUSR | S_IWUSR) != -1 || errno == ENOENT)
  1036. {
  1037. ev_history_load_save_t event_data;
  1038. event_data.cfg = mc_config_init (profile, FALSE);
  1039. event_data.receiver = NULL;
  1040. /* get all histories in dialog */
  1041. mc_event_dispatch (h->event_group, MCEVENT_HISTORY_SAVE, &event_data, NULL, NULL);
  1042. mc_config_save_file (event_data.cfg, NULL);
  1043. mc_config_deinit (event_data.cfg);
  1044. }
  1045. g_free (profile);
  1046. }
  1047. /* --------------------------------------------------------------------------------------------- */
  1048. char *
  1049. dlg_get_title (const WDialog * h, size_t len)
  1050. {
  1051. char *t;
  1052. if (h == NULL)
  1053. abort ();
  1054. if (h->get_title != NULL)
  1055. t = h->get_title (h, len);
  1056. else
  1057. t = g_strdup ("");
  1058. return t;
  1059. }
  1060. /* --------------------------------------------------------------------------------------------- */