dialog.c 31 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217
  1. /* Dialog box features module for the Midnight Commander
  2. Copyright (C) 1994, 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
  3. 2005, 2007, 2009, 2010 Free Software Foundation, Inc.
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  15. */
  16. /** \file dialog.c
  17. * \brief Source: dialog box features module
  18. */
  19. #include <config.h>
  20. #include <ctype.h>
  21. #include <stdlib.h>
  22. #include <stdarg.h>
  23. #include <stdio.h>
  24. #include <string.h>
  25. #include "lib/global.h"
  26. #include "lib/tty/tty.h"
  27. #include "lib/skin.h"
  28. #include "lib/tty/mouse.h"
  29. #include "lib/tty/key.h"
  30. #include "lib/strutil.h"
  31. #include "lib/widget.h"
  32. /* TODO: these includes should be removed! */
  33. #include "src/help.h" /* interactive_display() */
  34. #include "src/filemanager/layout.h"
  35. #include "src/execute.h" /* suspend_cmd() */
  36. #include "src/keybind-defaults.h"
  37. /*** global variables ****************************************************************************/
  38. /* Color styles for normal and error dialogs */
  39. dlg_colors_t dialog_colors;
  40. dlg_colors_t alarm_colors;
  41. /* Primitive way to check if the the current dialog is our dialog */
  42. /* This is needed by async routines like load_prompt */
  43. GList *top_dlg = NULL;
  44. /* A hook list for idle events */
  45. hook_t *idle_hook = NULL;
  46. /* If set then dialogs just clean the screen when refreshing, else */
  47. /* they do a complete refresh, refreshing all the parts of the program */
  48. int fast_refresh = 0;
  49. /* left click outside of dialog closes it */
  50. int mouse_close_dialog = 0;
  51. /*** file scope macro definitions ****************************************************************/
  52. /*** file scope type declarations ****************************************************************/
  53. /** What to do if the requested widget doesn't take focus */
  54. typedef enum
  55. {
  56. SELECT_NEXT, /* go the the next widget */
  57. SELECT_PREV, /* go the the previous widget */
  58. SELECT_EXACT /* use current widget */
  59. } select_dir_t;
  60. /*** file scope variables ************************************************************************/
  61. /*** file scope functions ************************************************************************/
  62. /* --------------------------------------------------------------------------------------------- */
  63. static void dlg_broadcast_msg_to (Dlg_head * h, widget_msg_t msg, gboolean reverse, int flags);
  64. /* --------------------------------------------------------------------------------------------- */
  65. /**
  66. * broadcast a message to all the widgets in a dialog that have
  67. * the options set to flags. If flags is zero, the message is sent
  68. * to all widgets.
  69. */
  70. static void
  71. dlg_broadcast_msg_to (Dlg_head * h, widget_msg_t msg, gboolean reverse, int flags)
  72. {
  73. GList *p, *first;
  74. if (h->widgets == NULL)
  75. return;
  76. if (h->current == NULL)
  77. h->current = h->widgets;
  78. if (reverse)
  79. {
  80. p = g_list_previous (h->current);
  81. if (p == NULL)
  82. p = g_list_last (h->widgets);
  83. }
  84. else
  85. {
  86. p = g_list_next (h->current);
  87. if (p == NULL)
  88. p = h->widgets;
  89. }
  90. first = p;
  91. do
  92. {
  93. Widget *w = (Widget *) p->data;
  94. if (reverse)
  95. {
  96. p = g_list_previous (p);
  97. if (p == NULL)
  98. p = g_list_last (h->widgets);
  99. }
  100. else
  101. {
  102. p = g_list_next (p);
  103. if (p == NULL)
  104. p = h->widgets;
  105. }
  106. if ((flags == 0) || ((flags & w->options) != 0))
  107. send_message (w, msg, 0);
  108. }
  109. while (first != p);
  110. }
  111. /* --------------------------------------------------------------------------------------------- */
  112. static int
  113. dlg_unfocus (Dlg_head * h)
  114. {
  115. /* ... but can unfocus disabled widget */
  116. if ((h->current != NULL) && (h->state == DLG_ACTIVE))
  117. {
  118. Widget *current = (Widget *) h->current->data;
  119. if (send_message (current, WIDGET_UNFOCUS, 0) == MSG_HANDLED)
  120. {
  121. h->callback (h, current, DLG_UNFOCUS, 0, NULL);
  122. return 1;
  123. }
  124. }
  125. return 0;
  126. }
  127. /* --------------------------------------------------------------------------------------------- */
  128. static int
  129. dlg_find_widget_callback (const void *a, const void *b)
  130. {
  131. const Widget *w = (const Widget *) a;
  132. callback_fn f = (callback_fn) b;
  133. return (w->callback == f) ? 0 : 1;
  134. }
  135. /* --------------------------------------------------------------------------------------------- */
  136. /**
  137. * Try to select another widget. If forward is set, follow tab order.
  138. * Otherwise go to the previous widget.
  139. */
  140. static void
  141. do_select_widget (Dlg_head * h, GList * w, select_dir_t dir)
  142. {
  143. Widget *w0 = (Widget *) h->current->data;
  144. if (!dlg_unfocus (h))
  145. return;
  146. h->current = w;
  147. do
  148. {
  149. if (dlg_focus (h))
  150. break;
  151. switch (dir)
  152. {
  153. case SELECT_NEXT:
  154. h->current = g_list_next (h->current);
  155. if (h->current == NULL)
  156. h->current = h->widgets;
  157. break;
  158. case SELECT_PREV:
  159. h->current = g_list_previous (h->current);
  160. if (h->current == NULL)
  161. h->current = g_list_last (h->widgets);
  162. break;
  163. case SELECT_EXACT:
  164. h->current = g_list_find (h->widgets, w0);
  165. dlg_focus (h);
  166. return;
  167. }
  168. }
  169. while (h->current != w /* && (((Widget *) h->current->data)->options & W_DISABLED) == 0 */ );
  170. if (dlg_overlap (w0, (Widget *) h->current->data))
  171. {
  172. send_message ((Widget *) h->current->data, WIDGET_DRAW, 0);
  173. send_message ((Widget *) h->current->data, WIDGET_FOCUS, 0);
  174. }
  175. }
  176. /* --------------------------------------------------------------------------------------------- */
  177. static void
  178. refresh_cmd (void)
  179. {
  180. #ifdef HAVE_SLANG
  181. tty_touch_screen ();
  182. mc_refresh ();
  183. #else
  184. /* Use this if the refreshes fail */
  185. clr_scr ();
  186. repaint_screen ();
  187. #endif /* HAVE_SLANG */
  188. }
  189. /* --------------------------------------------------------------------------------------------- */
  190. static cb_ret_t
  191. dlg_execute_cmd (Dlg_head * h, unsigned long command)
  192. {
  193. cb_ret_t ret = MSG_HANDLED;
  194. switch (command)
  195. {
  196. case CK_DialogOK:
  197. h->ret_value = B_ENTER;
  198. dlg_stop (h);
  199. break;
  200. case CK_DialogCancel:
  201. h->ret_value = B_CANCEL;
  202. dlg_stop (h);
  203. break;
  204. case CK_DialogPrevItem:
  205. dlg_one_up (h);
  206. break;
  207. case CK_DialogNextItem:
  208. dlg_one_down (h);
  209. break;
  210. case CK_DialogHelp:
  211. interactive_display (NULL, h->help_ctx);
  212. do_refresh ();
  213. break;
  214. case CK_DialogSuspend:
  215. suspend_cmd ();
  216. refresh_cmd ();
  217. break;
  218. case CK_DialogRefresh:
  219. refresh_cmd ();
  220. break;
  221. case CK_DialogListCmd:
  222. if (!h->modal)
  223. dialog_switch_list ();
  224. else
  225. ret = MSG_NOT_HANDLED;
  226. break;
  227. case CK_DialogNextCmd:
  228. if (!h->modal)
  229. dialog_switch_next ();
  230. else
  231. ret = MSG_NOT_HANDLED;
  232. break;
  233. case CK_DialogPrevCmd:
  234. if (!h->modal)
  235. dialog_switch_prev ();
  236. else
  237. ret = MSG_NOT_HANDLED;
  238. break;
  239. default:
  240. ret = MSG_NOT_HANDLED;
  241. }
  242. return ret;
  243. }
  244. /* --------------------------------------------------------------------------------------------- */
  245. static cb_ret_t
  246. dlg_handle_key (Dlg_head * h, int d_key)
  247. {
  248. unsigned long command;
  249. command = keybind_lookup_keymap_command (dialog_map, d_key);
  250. if ((command == CK_Ignore_Key) || (dlg_execute_cmd (h, command) == MSG_NOT_HANDLED))
  251. return MSG_NOT_HANDLED;
  252. else
  253. return MSG_HANDLED;
  254. }
  255. /* --------------------------------------------------------------------------------------------- */
  256. static int
  257. dlg_mouse_event (Dlg_head * h, Gpm_Event * event)
  258. {
  259. GList *item;
  260. GList *starting_widget = h->current;
  261. Gpm_Event new_event;
  262. int x = event->x;
  263. int y = event->y;
  264. /* close the dialog by mouse click out of dialog area */
  265. if (mouse_close_dialog && !h->fullscreen && ((event->buttons & GPM_B_LEFT) != 0) && ((event->type & GPM_DOWN) != 0) /* left click */
  266. && !((x > h->x) && (x <= h->x + h->cols) && (y > h->y) && (y <= h->y + h->lines)))
  267. {
  268. h->ret_value = B_CANCEL;
  269. dlg_stop (h);
  270. return MOU_NORMAL;
  271. }
  272. item = starting_widget;
  273. do
  274. {
  275. Widget *widget;
  276. widget = (Widget *) item->data;
  277. item = g_list_next (item);
  278. if (item == NULL)
  279. item = h->widgets;
  280. if (((widget->options & W_DISABLED) == 0)
  281. && (x > widget->x) && (x <= widget->x + widget->cols)
  282. && (y > widget->y) && (y <= widget->y + widget->lines))
  283. {
  284. new_event = *event;
  285. new_event.x -= widget->x;
  286. new_event.y -= widget->y;
  287. if (widget->mouse != NULL)
  288. return widget->mouse (&new_event, widget);
  289. }
  290. }
  291. while (item != starting_widget);
  292. return MOU_NORMAL;
  293. }
  294. /* --------------------------------------------------------------------------------------------- */
  295. static cb_ret_t
  296. dlg_try_hotkey (Dlg_head * h, int d_key)
  297. {
  298. GList *hot_cur;
  299. Widget *current;
  300. cb_ret_t handled;
  301. int c;
  302. if (h->widgets == NULL)
  303. return MSG_NOT_HANDLED;
  304. if (h->current == NULL)
  305. h->current = h->widgets;
  306. /*
  307. * Explanation: we don't send letter hotkeys to other widgets if
  308. * the currently selected widget is an input line
  309. */
  310. current = (Widget *) h->current->data;
  311. if ((current->options & W_DISABLED) != 0)
  312. return MSG_NOT_HANDLED;
  313. if (current->options & W_IS_INPUT)
  314. {
  315. /* skip ascii control characters, anything else can valid character in
  316. * some encoding */
  317. if (d_key >= 32 && d_key < 256)
  318. return MSG_NOT_HANDLED;
  319. }
  320. /* If it's an alt key, send the message */
  321. c = d_key & ~ALT (0);
  322. if (d_key & ALT (0) && g_ascii_isalpha (c))
  323. d_key = g_ascii_tolower (c);
  324. handled = MSG_NOT_HANDLED;
  325. if ((current->options & W_WANT_HOTKEY) != 0)
  326. handled = send_message (current, WIDGET_HOTKEY, d_key);
  327. /* If not used, send hotkey to other widgets */
  328. if (handled == MSG_HANDLED)
  329. return MSG_HANDLED;
  330. hot_cur = g_list_next (h->current);
  331. if (hot_cur == NULL)
  332. hot_cur = h->widgets;
  333. /* send it to all widgets */
  334. while (h->current != hot_cur && handled == MSG_NOT_HANDLED)
  335. {
  336. current = (Widget *) hot_cur->data;
  337. if ((current->options & W_WANT_HOTKEY) != 0)
  338. handled = send_message (current, WIDGET_HOTKEY, d_key);
  339. if (handled == MSG_NOT_HANDLED)
  340. {
  341. hot_cur = g_list_next (hot_cur);
  342. if (hot_cur == NULL)
  343. hot_cur = h->widgets;
  344. }
  345. }
  346. if (handled == MSG_HANDLED)
  347. do_select_widget (h, hot_cur, SELECT_EXACT);
  348. return handled;
  349. }
  350. /* --------------------------------------------------------------------------------------------- */
  351. static void
  352. dlg_key_event (Dlg_head * h, int d_key)
  353. {
  354. cb_ret_t handled;
  355. if (h->widgets == NULL)
  356. return;
  357. if (h->current == NULL)
  358. h->current = h->widgets;
  359. /* TAB used to cycle */
  360. if ((h->flags & DLG_WANT_TAB) == 0)
  361. {
  362. if (d_key == '\t')
  363. {
  364. dlg_one_down (h);
  365. return;
  366. }
  367. else if (d_key == KEY_BTAB)
  368. {
  369. dlg_one_up (h);
  370. return;
  371. }
  372. }
  373. /* first can dlg_callback handle the key */
  374. handled = h->callback (h, NULL, DLG_KEY, d_key, NULL);
  375. /* next try the hotkey */
  376. if (handled == MSG_NOT_HANDLED)
  377. handled = dlg_try_hotkey (h, d_key);
  378. if (handled == MSG_HANDLED)
  379. h->callback (h, NULL, DLG_HOTKEY_HANDLED, 0, NULL);
  380. else
  381. /* not used - then try widget_callback */
  382. handled = send_message ((Widget *) h->current->data, WIDGET_KEY, d_key);
  383. /* not used- try to use the unhandled case */
  384. if (handled == MSG_NOT_HANDLED)
  385. handled = h->callback (h, NULL, DLG_UNHANDLED_KEY, d_key, NULL);
  386. if (handled == MSG_NOT_HANDLED)
  387. handled = dlg_handle_key (h, d_key);
  388. h->callback (h, NULL, DLG_POST_KEY, d_key, NULL);
  389. }
  390. /* --------------------------------------------------------------------------------------------- */
  391. static void
  392. frontend_run_dlg (Dlg_head * h)
  393. {
  394. int d_key;
  395. Gpm_Event event;
  396. event.x = -1;
  397. /* close opened editors, viewers, etc */
  398. if (!h->modal && midnight_shutdown)
  399. {
  400. h->callback (h, NULL, DLG_VALIDATE, 0, NULL);
  401. return;
  402. }
  403. while (h->state == DLG_ACTIVE)
  404. {
  405. if (winch_flag)
  406. change_screen_size ();
  407. if (is_idle ())
  408. {
  409. if (idle_hook)
  410. execute_hooks (idle_hook);
  411. while ((h->flags & DLG_WANT_IDLE) && is_idle ())
  412. h->callback (h, NULL, DLG_IDLE, 0, NULL);
  413. /* Allow terminating the dialog from the idle handler */
  414. if (h->state != DLG_ACTIVE)
  415. break;
  416. }
  417. update_cursor (h);
  418. /* Clear interrupt flag */
  419. tty_got_interrupt ();
  420. d_key = tty_get_event (&event, h->mouse_status == MOU_REPEAT, TRUE);
  421. dlg_process_event (h, d_key, &event);
  422. if (h->state == DLG_CLOSED)
  423. h->callback (h, NULL, DLG_VALIDATE, 0, NULL);
  424. }
  425. }
  426. /* --------------------------------------------------------------------------------------------- */
  427. /*** public functions ****************************************************************************/
  428. /* --------------------------------------------------------------------------------------------- */
  429. /** draw box in window */
  430. void
  431. draw_box (Dlg_head * h, int y, int x, int ys, int xs, gboolean single)
  432. {
  433. tty_draw_box (h->y + y, h->x + x, ys, xs, single);
  434. }
  435. /* --------------------------------------------------------------------------------------------- */
  436. /** Clean the dialog area, draw the frame and the title */
  437. void
  438. common_dialog_repaint (Dlg_head * h)
  439. {
  440. int space;
  441. if (h->state != DLG_ACTIVE)
  442. return;
  443. space = (h->flags & DLG_COMPACT) ? 0 : 1;
  444. tty_setcolor (h->color[DLG_COLOR_NORMAL]);
  445. dlg_erase (h);
  446. draw_box (h, space, space, h->lines - 2 * space, h->cols - 2 * space, FALSE);
  447. if (h->title != NULL)
  448. {
  449. tty_setcolor (h->color[DLG_COLOR_TITLE]);
  450. dlg_move (h, space, (h->cols - str_term_width1 (h->title)) / 2);
  451. tty_print_string (h->title);
  452. }
  453. }
  454. /* --------------------------------------------------------------------------------------------- */
  455. /** this function allows to set dialog position */
  456. void
  457. dlg_set_position (Dlg_head * h, int y1, int x1, int y2, int x2)
  458. {
  459. /* save old positions, will be used to reposition childs */
  460. int ox, oy, oc, ol;
  461. int shift_x, shift_y, scale_x, scale_y;
  462. /* save old positions, will be used to reposition childs */
  463. ox = h->x;
  464. oy = h->y;
  465. oc = h->cols;
  466. ol = h->lines;
  467. h->x = x1;
  468. h->y = y1;
  469. h->lines = y2 - y1;
  470. h->cols = x2 - x1;
  471. /* dialog is empty */
  472. if (h->widgets == NULL)
  473. return;
  474. if (h->current == NULL)
  475. h->current = h->widgets;
  476. /* values by which controls should be moved */
  477. shift_x = h->x - ox;
  478. shift_y = h->y - oy;
  479. scale_x = h->cols - oc;
  480. scale_y = h->lines - ol;
  481. if ((shift_x != 0) || (shift_y != 0) || (scale_x != 0) || (scale_y != 0))
  482. {
  483. GList *w;
  484. for (w = h->widgets; w != NULL; w = g_list_next (w))
  485. {
  486. /* there are, mainly, 2 generally possible
  487. situations:
  488. 1. control sticks to one side - it
  489. should be moved
  490. 2. control sticks to two sides of
  491. one direction - it should be sized */
  492. Widget *c = (Widget *) w->data;
  493. int x = c->x;
  494. int y = c->y;
  495. int cols = c->cols;
  496. int lines = c->lines;
  497. if ((c->pos_flags & WPOS_KEEP_LEFT) && (c->pos_flags & WPOS_KEEP_RIGHT))
  498. {
  499. x += shift_x;
  500. cols += scale_x;
  501. }
  502. else if (c->pos_flags & WPOS_KEEP_LEFT)
  503. x += shift_x;
  504. else if (c->pos_flags & WPOS_KEEP_RIGHT)
  505. x += shift_x + scale_x;
  506. if ((c->pos_flags & WPOS_KEEP_TOP) && (c->pos_flags & WPOS_KEEP_BOTTOM))
  507. {
  508. y += shift_y;
  509. lines += scale_y;
  510. }
  511. else if (c->pos_flags & WPOS_KEEP_TOP)
  512. y += shift_y;
  513. else if (c->pos_flags & WPOS_KEEP_BOTTOM)
  514. y += shift_y + scale_y;
  515. widget_set_size (c, y, x, lines, cols);
  516. }
  517. }
  518. }
  519. /* --------------------------------------------------------------------------------------------- */
  520. /** this function sets only size, leaving positioning to automatic methods */
  521. void
  522. dlg_set_size (Dlg_head * h, int lines, int cols)
  523. {
  524. int x = h->x;
  525. int y = h->y;
  526. if (h->flags & DLG_CENTER)
  527. {
  528. y = (LINES - lines) / 2;
  529. x = (COLS - cols) / 2;
  530. }
  531. if ((h->flags & DLG_TRYUP) && (y > 3))
  532. y -= 2;
  533. dlg_set_position (h, y, x, y + lines, x + cols);
  534. }
  535. /* --------------------------------------------------------------------------------------------- */
  536. /** Default dialog callback */
  537. cb_ret_t
  538. default_dlg_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
  539. {
  540. (void) sender;
  541. (void) parm;
  542. (void) data;
  543. switch (msg)
  544. {
  545. case DLG_DRAW:
  546. if (h->color != NULL)
  547. {
  548. common_dialog_repaint (h);
  549. return MSG_HANDLED;
  550. }
  551. return MSG_NOT_HANDLED;
  552. case DLG_IDLE:
  553. dlg_broadcast_msg_to (h, WIDGET_IDLE, FALSE, W_WANT_IDLE);
  554. return MSG_HANDLED;
  555. case DLG_RESIZE:
  556. /* this is default resizing mechanism */
  557. /* the main idea of this code is to resize dialog
  558. according to flags (if any of flags require automatic
  559. resizing, like DLG_CENTER, end after that reposition
  560. controls in dialog according to flags of widget) */
  561. dlg_set_size (h, h->lines, h->cols);
  562. return MSG_HANDLED;
  563. default:
  564. break;
  565. }
  566. return MSG_NOT_HANDLED;
  567. }
  568. /* --------------------------------------------------------------------------------------------- */
  569. Dlg_head *
  570. create_dlg (gboolean modal, int y1, int x1, int lines, int cols,
  571. const int *colors, dlg_cb_fn callback, const char *help_ctx,
  572. const char *title, dlg_flags_t flags)
  573. {
  574. Dlg_head *new_d;
  575. new_d = g_new0 (Dlg_head, 1);
  576. new_d->modal = modal;
  577. if (colors != NULL)
  578. {
  579. memmove (new_d->color, colors, sizeof (dlg_colors_t));
  580. }
  581. new_d->help_ctx = help_ctx;
  582. new_d->callback = (callback != NULL) ? callback : default_dlg_callback;
  583. new_d->x = x1;
  584. new_d->y = y1;
  585. new_d->flags = flags;
  586. new_d->data = NULL;
  587. dlg_set_size (new_d, lines, cols);
  588. new_d->fullscreen = (new_d->x == 0 && new_d->y == 0
  589. && new_d->cols == COLS && new_d->lines == LINES);
  590. new_d->mouse_status = MOU_NORMAL;
  591. /* Strip existing spaces, add one space before and after the title */
  592. if (title != NULL)
  593. {
  594. char *t;
  595. t = g_strstrip (g_strdup (title));
  596. if (*t != '\0')
  597. new_d->title = g_strdup_printf (" %s ", t);
  598. g_free (t);
  599. }
  600. return new_d;
  601. }
  602. /* --------------------------------------------------------------------------------------------- */
  603. void
  604. dlg_set_default_colors (void)
  605. {
  606. dialog_colors[DLG_COLOR_NORMAL] = COLOR_NORMAL;
  607. dialog_colors[DLG_COLOR_FOCUS] = COLOR_FOCUS;
  608. dialog_colors[DLG_COLOR_HOT_NORMAL] = COLOR_HOT_NORMAL;
  609. dialog_colors[DLG_COLOR_HOT_FOCUS] = COLOR_HOT_FOCUS;
  610. dialog_colors[DLG_COLOR_TITLE] = COLOR_TITLE;
  611. alarm_colors[DLG_COLOR_NORMAL] = ERROR_COLOR;
  612. alarm_colors[DLG_COLOR_FOCUS] = ERROR_FOCUS;
  613. alarm_colors[DLG_COLOR_HOT_NORMAL] = ERROR_HOT_NORMAL;
  614. alarm_colors[DLG_COLOR_HOT_FOCUS] = ERROR_HOT_FOCUS;
  615. alarm_colors[DLG_COLOR_TITLE] = ERROR_TITLE;
  616. }
  617. /* --------------------------------------------------------------------------------------------- */
  618. void
  619. dlg_erase (Dlg_head * h)
  620. {
  621. if ((h != NULL) && (h->state == DLG_ACTIVE))
  622. tty_fill_region (h->y, h->x, h->lines, h->cols, ' ');
  623. }
  624. /* --------------------------------------------------------------------------------------------- */
  625. void
  626. set_idle_proc (Dlg_head * d, int enable)
  627. {
  628. if (enable)
  629. d->flags |= DLG_WANT_IDLE;
  630. else
  631. d->flags &= ~DLG_WANT_IDLE;
  632. }
  633. /* --------------------------------------------------------------------------------------------- */
  634. /**
  635. * Insert widget to dialog before current widget. For dialogs populated
  636. * from the bottom, make the widget current. Return widget number.
  637. */
  638. int
  639. add_widget_autopos (Dlg_head * h, void *w, widget_pos_flags_t pos_flags)
  640. {
  641. Widget *widget = (Widget *) w;
  642. /* Don't accept 0 widgets */
  643. if (w == NULL)
  644. abort ();
  645. widget->x += h->x;
  646. widget->y += h->y;
  647. widget->owner = h;
  648. widget->pos_flags = pos_flags;
  649. widget->id = g_list_length (h->widgets);
  650. if ((h->flags & DLG_REVERSE) != 0)
  651. h->widgets = g_list_prepend (h->widgets, widget);
  652. else
  653. h->widgets = g_list_append (h->widgets, widget);
  654. h->current = h->widgets;
  655. return widget->id;
  656. }
  657. /* --------------------------------------------------------------------------------------------- */
  658. /** wrapper to simply add lefttop positioned controls */
  659. int
  660. add_widget (Dlg_head * h, void *w)
  661. {
  662. return add_widget_autopos (h, w, WPOS_KEEP_LEFT | WPOS_KEEP_TOP);
  663. }
  664. /* --------------------------------------------------------------------------------------------- */
  665. void
  666. do_refresh (void)
  667. {
  668. GList *d = top_dlg;
  669. if (fast_refresh)
  670. {
  671. if ((d != NULL) && (d->data != NULL))
  672. dlg_redraw ((Dlg_head *) d->data);
  673. }
  674. else
  675. {
  676. /* Search first fullscreen dialog */
  677. for (; d != NULL; d = g_list_next (d))
  678. if ((d->data != NULL) && ((Dlg_head *) d->data)->fullscreen)
  679. break;
  680. /* back to top dialog */
  681. for (; d != NULL; d = g_list_previous (d))
  682. if (d->data != NULL)
  683. dlg_redraw ((Dlg_head *) d->data);
  684. }
  685. }
  686. /* --------------------------------------------------------------------------------------------- */
  687. /** broadcast a message to all the widgets in a dialog */
  688. void
  689. dlg_broadcast_msg (Dlg_head * h, widget_msg_t msg, gboolean reverse)
  690. {
  691. dlg_broadcast_msg_to (h, msg, reverse, 0);
  692. }
  693. /* --------------------------------------------------------------------------------------------- */
  694. int
  695. dlg_focus (Dlg_head * h)
  696. {
  697. /* cannot focus disabled widget ... */
  698. if ((h->current != NULL) && (h->state == DLG_ACTIVE))
  699. {
  700. Widget *current = (Widget *) h->current->data;
  701. if (((current->options & W_DISABLED) == 0)
  702. && (send_message (current, WIDGET_FOCUS, 0) == MSG_HANDLED))
  703. {
  704. h->callback (h, current, DLG_FOCUS, 0, NULL);
  705. return 1;
  706. }
  707. }
  708. return 0;
  709. }
  710. /* --------------------------------------------------------------------------------------------- */
  711. /** Return true if the windows overlap */
  712. int
  713. dlg_overlap (Widget * a, Widget * b)
  714. {
  715. return !((b->x >= a->x + a->cols)
  716. || (a->x >= b->x + b->cols) || (b->y >= a->y + a->lines) || (a->y >= b->y + b->lines));
  717. }
  718. /* --------------------------------------------------------------------------------------------- */
  719. /** Find the widget with the given callback in the dialog h */
  720. Widget *
  721. find_widget_type (const Dlg_head * h, callback_fn callback)
  722. {
  723. GList *w;
  724. w = g_list_find_custom (h->widgets, callback, dlg_find_widget_callback);
  725. return (w == NULL) ? NULL : (Widget *) w->data;
  726. }
  727. /* --------------------------------------------------------------------------------------------- */
  728. /** Find the widget with the given id */
  729. Widget *
  730. dlg_find_by_id (const Dlg_head * h, unsigned int id)
  731. {
  732. if (h->widgets != NULL)
  733. {
  734. GList *w;
  735. for (w = h->widgets; w != NULL; w = g_list_next (w))
  736. if (((Widget *) w->data)->id == id)
  737. return (Widget *) w->data;
  738. }
  739. return NULL;
  740. }
  741. /* --------------------------------------------------------------------------------------------- */
  742. /** Find the widget with the given id in the dialog h and select it */
  743. void
  744. dlg_select_by_id (const Dlg_head * h, unsigned int id)
  745. {
  746. Widget *w;
  747. w = dlg_find_by_id (h, id);
  748. if (w != NULL)
  749. dlg_select_widget (w);
  750. }
  751. /* --------------------------------------------------------------------------------------------- */
  752. /*
  753. * Try to select widget in the dialog.
  754. */
  755. void
  756. dlg_select_widget (void *w)
  757. {
  758. const Widget *widget = (Widget *) w;
  759. Dlg_head *h = widget->owner;
  760. do_select_widget (h, g_list_find (h->widgets, widget), SELECT_NEXT);
  761. }
  762. /* --------------------------------------------------------------------------------------------- */
  763. /** Try to select previous widget in the tab order */
  764. void
  765. dlg_one_up (Dlg_head * h)
  766. {
  767. if (h->widgets != NULL)
  768. {
  769. GList *prev;
  770. prev = g_list_previous (h->current);
  771. if (prev == NULL)
  772. prev = g_list_last (h->widgets);
  773. do_select_widget (h, prev, SELECT_PREV);
  774. }
  775. }
  776. /* --------------------------------------------------------------------------------------------- */
  777. /** Try to select next widget in the tab order */
  778. void
  779. dlg_one_down (Dlg_head * h)
  780. {
  781. if (h->widgets != NULL)
  782. {
  783. GList *next;
  784. next = g_list_next (h->current);
  785. if (next == NULL)
  786. next = h->widgets;
  787. do_select_widget (h, next, SELECT_NEXT);
  788. }
  789. }
  790. /* --------------------------------------------------------------------------------------------- */
  791. void
  792. update_cursor (Dlg_head * h)
  793. {
  794. GList *p = h->current;
  795. if ((p != NULL) && (h->state == DLG_ACTIVE))
  796. {
  797. Widget *w;
  798. w = (Widget *) p->data;
  799. if (((w->options & W_DISABLED) == 0) && ((w->options & W_WANT_CURSOR) != 0))
  800. send_message (w, WIDGET_CURSOR, 0);
  801. else
  802. do
  803. {
  804. p = g_list_next (p);
  805. if (p == NULL)
  806. p = h->widgets;
  807. if (p == h->current)
  808. break;
  809. w = (Widget *) p->data;
  810. if (((w->options & W_DISABLED) == 0) && ((w->options & W_WANT_CURSOR) != 0))
  811. if (send_message (w, WIDGET_CURSOR, 0) == MSG_HANDLED)
  812. break;
  813. }
  814. while (TRUE);
  815. }
  816. }
  817. /* --------------------------------------------------------------------------------------------- */
  818. /**
  819. * Redraw the widgets in reverse order, leaving the current widget
  820. * as the last one
  821. */
  822. void
  823. dlg_redraw (Dlg_head * h)
  824. {
  825. if (h->state != DLG_ACTIVE)
  826. return;
  827. if (h->winch_pending)
  828. {
  829. h->winch_pending = FALSE;
  830. h->callback (h, NULL, DLG_RESIZE, 0, NULL);
  831. }
  832. h->callback (h, NULL, DLG_DRAW, 0, NULL);
  833. dlg_broadcast_msg (h, WIDGET_DRAW, TRUE);
  834. update_cursor (h);
  835. }
  836. /* --------------------------------------------------------------------------------------------- */
  837. void
  838. dlg_stop (Dlg_head * h)
  839. {
  840. h->state = DLG_CLOSED;
  841. }
  842. /* --------------------------------------------------------------------------------------------- */
  843. /** Init the process */
  844. void
  845. init_dlg (Dlg_head * h)
  846. {
  847. if ((top_dlg != NULL) && ((Dlg_head *) top_dlg->data)->modal)
  848. h->modal = TRUE;
  849. /* add dialog to the stack */
  850. top_dlg = g_list_prepend (top_dlg, h);
  851. /* Initialize dialog manager and widgets */
  852. if (h->state == DLG_ACTIVE)
  853. {
  854. if (!h->modal)
  855. dialog_switch_add (h);
  856. h->callback (h, NULL, DLG_INIT, 0, NULL);
  857. dlg_broadcast_msg (h, WIDGET_INIT, FALSE);
  858. }
  859. h->state = DLG_ACTIVE;
  860. dlg_redraw (h);
  861. /* Select the first widget that takes focus */
  862. while (h->current != NULL && !dlg_focus (h))
  863. {
  864. h->current = g_list_next (h->current);
  865. if (h->current == NULL)
  866. h->current = h->widgets;
  867. }
  868. h->ret_value = 0;
  869. }
  870. /* --------------------------------------------------------------------------------------------- */
  871. void
  872. dlg_process_event (Dlg_head * h, int key, Gpm_Event * event)
  873. {
  874. if (key == EV_NONE)
  875. {
  876. if (tty_got_interrupt ())
  877. dlg_execute_cmd (h, CK_DialogCancel);
  878. return;
  879. }
  880. if (key == EV_MOUSE)
  881. h->mouse_status = dlg_mouse_event (h, event);
  882. else
  883. dlg_key_event (h, key);
  884. }
  885. /* --------------------------------------------------------------------------------------------- */
  886. /** Shutdown the run_dlg */
  887. void
  888. dlg_run_done (Dlg_head * h)
  889. {
  890. top_dlg = g_list_remove (top_dlg, h);
  891. if (h->state == DLG_CLOSED)
  892. {
  893. h->callback (h, (Widget *) h->current->data, DLG_END, 0, NULL);
  894. if (!h->modal)
  895. dialog_switch_remove (h);
  896. }
  897. }
  898. /* --------------------------------------------------------------------------------------------- */
  899. /**
  900. * Standard run dialog routine
  901. * We have to keep this routine small so that we can duplicate it's
  902. * behavior on complex routines like the file routines, this way,
  903. * they can call the dlg_process_event without rewriting all the code
  904. */
  905. int
  906. run_dlg (Dlg_head * h)
  907. {
  908. init_dlg (h);
  909. frontend_run_dlg (h);
  910. dlg_run_done (h);
  911. return h->ret_value;
  912. }
  913. /* --------------------------------------------------------------------------------------------- */
  914. void
  915. destroy_dlg (Dlg_head * h)
  916. {
  917. dlg_broadcast_msg (h, WIDGET_DESTROY, FALSE);
  918. g_list_foreach (h->widgets, (GFunc) g_free, NULL);
  919. g_list_free (h->widgets);
  920. g_free (h->title);
  921. g_free (h);
  922. do_refresh ();
  923. }
  924. /* --------------------------------------------------------------------------------------------- */
  925. char *
  926. dlg_get_title (const Dlg_head * h, size_t len)
  927. {
  928. char *t;
  929. if (h == NULL)
  930. abort ();
  931. if (h->get_title != NULL)
  932. t = h->get_title (h, len);
  933. else
  934. t = g_strdup ("");
  935. return t;
  936. }
  937. /* --------------------------------------------------------------------------------------------- */
  938. /** Replace widget old_w for widget new_w in the dialog */
  939. void
  940. dlg_replace_widget (Widget * old_w, Widget * new_w)
  941. {
  942. Dlg_head *h = old_w->owner;
  943. gboolean should_focus = FALSE;
  944. if (h->widgets == NULL)
  945. return;
  946. if (h->current == NULL)
  947. h->current = h->widgets;
  948. if (old_w == h->current->data)
  949. should_focus = TRUE;
  950. new_w->owner = h;
  951. new_w->id = old_w->id;
  952. if (should_focus)
  953. h->current->data = new_w;
  954. else
  955. g_list_find (h->widgets, old_w)->data = new_w;
  956. send_message (old_w, WIDGET_DESTROY, 0);
  957. send_message (new_w, WIDGET_INIT, 0);
  958. if (should_focus)
  959. dlg_select_widget (new_w);
  960. send_message (new_w, WIDGET_DRAW, 0);
  961. }
  962. /* --------------------------------------------------------------------------------------------- */