group.c 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970
  1. /*
  2. Widget group features module for the Midnight Commander
  3. Copyright (C) 2020-2024
  4. The Free Software Foundation, Inc.
  5. Written by:
  6. Andrew Borodin <aborodin@vmail.ru>, 2020-2022
  7. This file is part of the Midnight Commander.
  8. The Midnight Commander is free software: you can redistribute it
  9. and/or modify it under the terms of the GNU General Public License as
  10. published by the Free Software Foundation, either version 3 of the License,
  11. or (at your option) any later version.
  12. The Midnight Commander is distributed in the hope that it will be useful,
  13. but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. GNU General Public License for more details.
  16. You should have received a copy of the GNU General Public License
  17. along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. /** \file group.c
  20. * \brief Source: widget group features module
  21. */
  22. #include <config.h>
  23. #include <assert.h>
  24. #include <stdlib.h>
  25. #include <string.h>
  26. #include "lib/global.h"
  27. #include "lib/tty/key.h" /* ALT() */
  28. #include "lib/widget.h"
  29. /*** global variables ****************************************************************************/
  30. /*** file scope macro definitions ****************************************************************/
  31. /*** file scope type declarations ****************************************************************/
  32. /* Control widget positions in a group */
  33. typedef struct
  34. {
  35. int shift_x;
  36. int scale_x;
  37. int shift_y;
  38. int scale_y;
  39. } widget_shift_scale_t;
  40. typedef struct
  41. {
  42. widget_state_t state;
  43. gboolean enable;
  44. } widget_state_info_t;
  45. /*** forward declarations (file scope functions) *************************************************/
  46. /*** file scope variables ************************************************************************/
  47. /* --------------------------------------------------------------------------------------------- */
  48. /*** file scope functions ************************************************************************/
  49. /* --------------------------------------------------------------------------------------------- */
  50. static void
  51. group_widget_init (void *data, void *user_data)
  52. {
  53. (void) user_data;
  54. send_message (WIDGET (data), NULL, MSG_INIT, 0, NULL);
  55. }
  56. /* --------------------------------------------------------------------------------------------- */
  57. static GList *
  58. group_get_next_or_prev_of (GList *list, gboolean next)
  59. {
  60. GList *l = NULL;
  61. if (list != NULL)
  62. {
  63. WGroup *owner = WIDGET (list->data)->owner;
  64. if (owner != NULL)
  65. {
  66. if (next)
  67. {
  68. l = g_list_next (list);
  69. if (l == NULL)
  70. l = owner->widgets;
  71. }
  72. else
  73. {
  74. l = g_list_previous (list);
  75. if (l == NULL)
  76. l = g_list_last (owner->widgets);
  77. }
  78. }
  79. }
  80. return l;
  81. }
  82. /* --------------------------------------------------------------------------------------------- */
  83. static void
  84. group_select_next_or_prev (WGroup *g, gboolean next)
  85. {
  86. if (g->widgets != NULL && g->current != NULL)
  87. {
  88. GList *l = g->current;
  89. do
  90. {
  91. l = group_get_next_or_prev_of (l, next);
  92. }
  93. while (!widget_is_focusable (l->data) && l != g->current);
  94. widget_select (l->data);
  95. }
  96. }
  97. /* --------------------------------------------------------------------------------------------- */
  98. static void
  99. group_widget_set_state (gpointer data, gpointer user_data)
  100. {
  101. widget_state_info_t *state = (widget_state_info_t *) user_data;
  102. widget_set_state (WIDGET (data), state->state, state->enable);
  103. }
  104. /* --------------------------------------------------------------------------------------------- */
  105. /**
  106. * Send broadcast message to all widgets in the group that have specified options.
  107. *
  108. * @param g WGroup object
  109. * @param msg message sent to widgets
  110. * @param reverse if TRUE, send message in reverse order, FALSE -- in direct one.
  111. * @param options if WOP_DEFAULT, the message is sent to all widgets. Else message is sent to widgets
  112. * that have specified options.
  113. */
  114. static void
  115. group_send_broadcast_msg_custom (WGroup *g, widget_msg_t msg, gboolean reverse,
  116. widget_options_t options)
  117. {
  118. GList *p, *first;
  119. if (g->widgets == NULL)
  120. return;
  121. if (g->current == NULL)
  122. g->current = g->widgets;
  123. p = group_get_next_or_prev_of (g->current, !reverse);
  124. first = p;
  125. do
  126. {
  127. Widget *w = WIDGET (p->data);
  128. p = group_get_next_or_prev_of (p, !reverse);
  129. if (options == WOP_DEFAULT || (options & w->options) != 0)
  130. /* special case: don't draw invisible widgets */
  131. if (msg != MSG_DRAW || widget_get_state (w, WST_VISIBLE))
  132. send_message (w, NULL, msg, 0, NULL);
  133. }
  134. while (first != p);
  135. }
  136. /* --------------------------------------------------------------------------------------------- */
  137. /**
  138. * Default group callback to convert group coordinates from local (relative to owner) to global
  139. * (relative to screen).
  140. *
  141. * @param w widget
  142. */
  143. static void
  144. group_default_make_global (Widget *w, const WRect *delta)
  145. {
  146. GList *iter;
  147. if (delta != NULL)
  148. {
  149. /* change own coordinates */
  150. widget_default_make_global (w, delta);
  151. /* change child widget coordinates */
  152. for (iter = GROUP (w)->widgets; iter != NULL; iter = g_list_next (iter))
  153. WIDGET (iter->data)->make_global (WIDGET (iter->data), delta);
  154. }
  155. else if (w->owner != NULL)
  156. {
  157. WRect r = WIDGET (w->owner)->rect;
  158. r.lines = 0;
  159. r.cols = 0;
  160. /* change own coordinates */
  161. widget_default_make_global (w, &r);
  162. /* change child widget coordinates */
  163. for (iter = GROUP (w)->widgets; iter != NULL; iter = g_list_next (iter))
  164. WIDGET (iter->data)->make_global (WIDGET (iter->data), &r);
  165. }
  166. }
  167. /* --------------------------------------------------------------------------------------------- */
  168. /**
  169. * Default group callback to convert group coordinates from global (relative to screen) to local
  170. * (relative to owner).
  171. *
  172. * @param w widget
  173. */
  174. static void
  175. group_default_make_local (Widget *w, const WRect *delta)
  176. {
  177. GList *iter;
  178. if (delta != NULL)
  179. {
  180. /* change own coordinates */
  181. widget_default_make_local (w, delta);
  182. /* change child widget coordinates */
  183. for (iter = GROUP (w)->widgets; iter != NULL; iter = g_list_next (iter))
  184. WIDGET (iter->data)->make_local (WIDGET (iter->data), delta);
  185. }
  186. else if (w->owner != NULL)
  187. {
  188. WRect r = WIDGET (w->owner)->rect;
  189. r.lines = 0;
  190. r.cols = 0;
  191. /* change own coordinates */
  192. widget_default_make_local (w, &r);
  193. /* change child widget coordinates */
  194. for (iter = GROUP (w)->widgets; iter != NULL; iter = g_list_next (iter))
  195. WIDGET (iter->data)->make_local (WIDGET (iter->data), &r);
  196. }
  197. }
  198. /* --------------------------------------------------------------------------------------------- */
  199. /**
  200. * Default group callback function to find widget in the group.
  201. *
  202. * @param w WGroup object
  203. * @param what widget to find
  204. *
  205. * @return holder of @what if found, NULL otherwise
  206. */
  207. static GList *
  208. group_default_find (const Widget *w, const Widget *what)
  209. {
  210. GList *w0;
  211. w0 = widget_default_find (w, what);
  212. if (w0 == NULL)
  213. {
  214. GList *iter;
  215. for (iter = CONST_GROUP (w)->widgets; iter != NULL; iter = g_list_next (iter))
  216. {
  217. w0 = widget_find (WIDGET (iter->data), what);
  218. if (w0 != NULL)
  219. break;
  220. }
  221. }
  222. return w0;
  223. }
  224. /* --------------------------------------------------------------------------------------------- */
  225. /**
  226. * Default group callback function to find widget in the group using widget callback.
  227. *
  228. * @param w WGroup object
  229. * @param cb widget callback
  230. *
  231. * @return widget object if found, NULL otherwise
  232. */
  233. static Widget *
  234. group_default_find_by_type (const Widget *w, widget_cb_fn cb)
  235. {
  236. Widget *w0;
  237. w0 = widget_default_find_by_type (w, cb);
  238. if (w0 == NULL)
  239. {
  240. GList *iter;
  241. for (iter = CONST_GROUP (w)->widgets; iter != NULL; iter = g_list_next (iter))
  242. {
  243. w0 = widget_find_by_type (WIDGET (iter->data), cb);
  244. if (w0 != NULL)
  245. break;
  246. }
  247. }
  248. return w0;
  249. }
  250. /* --------------------------------------------------------------------------------------------- */
  251. /**
  252. * Default group callback function to find widget by widget ID in the group.
  253. *
  254. * @param w WGroup object
  255. * @param id widget ID
  256. *
  257. * @return widget object if widget with specified id is found in group, NULL otherwise
  258. */
  259. static Widget *
  260. group_default_find_by_id (const Widget *w, unsigned long id)
  261. {
  262. Widget *w0;
  263. w0 = widget_default_find_by_id (w, id);
  264. if (w0 == NULL)
  265. {
  266. GList *iter;
  267. for (iter = CONST_GROUP (w)->widgets; iter != NULL; iter = g_list_next (iter))
  268. {
  269. w0 = widget_find_by_id (WIDGET (iter->data), id);
  270. if (w0 != NULL)
  271. break;
  272. }
  273. }
  274. return w0;
  275. }
  276. /* --------------------------------------------------------------------------------------------- */
  277. /**
  278. * Update cursor position in the active widget of the group.
  279. *
  280. * @param g WGroup object
  281. *
  282. * @return MSG_HANDLED if cursor was updated in the specified group, MSG_NOT_HANDLED otherwise
  283. */
  284. static cb_ret_t
  285. group_update_cursor (WGroup *g)
  286. {
  287. GList *p = g->current;
  288. if (p != NULL && widget_get_state (WIDGET (g), WST_ACTIVE))
  289. do
  290. {
  291. Widget *w = WIDGET (p->data);
  292. /* Don't use widget_is_selectable() here.
  293. If WOP_SELECTABLE option is not set, widget can handle mouse events.
  294. For example, commandl line in file manager */
  295. if (widget_get_options (w, WOP_WANT_CURSOR) && widget_get_state (w, WST_VISIBLE)
  296. && !widget_get_state (w, WST_DISABLED) && widget_update_cursor (WIDGET (p->data)))
  297. return MSG_HANDLED;
  298. p = group_get_widget_next_of (p);
  299. }
  300. while (p != g->current);
  301. return MSG_NOT_HANDLED;
  302. }
  303. /* --------------------------------------------------------------------------------------------- */
  304. static void
  305. group_widget_set_position (gpointer data, gpointer user_data)
  306. {
  307. /* there are, mainly, 2 generally possible situations:
  308. * 1. control sticks to one side - it should be moved
  309. * 2. control sticks to two sides of one direction - it should be sized
  310. */
  311. Widget *c = WIDGET (data);
  312. const WRect *g = &CONST_WIDGET (c->owner)->rect;
  313. const widget_shift_scale_t *wss = (const widget_shift_scale_t *) user_data;
  314. WRect r = c->rect;
  315. if ((c->pos_flags & WPOS_CENTER_HORZ) != 0)
  316. r.x = g->x + (g->cols - c->rect.cols) / 2;
  317. else if ((c->pos_flags & WPOS_KEEP_LEFT) != 0 && (c->pos_flags & WPOS_KEEP_RIGHT) != 0)
  318. {
  319. r.x += wss->shift_x;
  320. r.cols += wss->scale_x;
  321. }
  322. else if ((c->pos_flags & WPOS_KEEP_LEFT) != 0)
  323. r.x += wss->shift_x;
  324. else if ((c->pos_flags & WPOS_KEEP_RIGHT) != 0)
  325. r.x += wss->shift_x + wss->scale_x;
  326. if ((c->pos_flags & WPOS_CENTER_VERT) != 0)
  327. r.y = g->y + (g->lines - c->rect.lines) / 2;
  328. else if ((c->pos_flags & WPOS_KEEP_TOP) != 0 && (c->pos_flags & WPOS_KEEP_BOTTOM) != 0)
  329. {
  330. r.y += wss->shift_y;
  331. r.lines += wss->scale_y;
  332. }
  333. else if ((c->pos_flags & WPOS_KEEP_TOP) != 0)
  334. r.y += wss->shift_y;
  335. else if ((c->pos_flags & WPOS_KEEP_BOTTOM) != 0)
  336. r.y += wss->shift_y + wss->scale_y;
  337. send_message (c, NULL, MSG_RESIZE, 0, &r);
  338. }
  339. /* --------------------------------------------------------------------------------------------- */
  340. static void
  341. group_set_position (WGroup *g, const WRect *r)
  342. {
  343. WRect *w = &WIDGET (g)->rect;
  344. widget_shift_scale_t wss;
  345. /* save old positions, will be used to reposition childs */
  346. WRect or = *w;
  347. *w = *r;
  348. /* dialog is empty */
  349. if (g->widgets == NULL)
  350. return;
  351. if (g->current == NULL)
  352. g->current = g->widgets;
  353. /* values by which controls should be moved */
  354. wss.shift_x = w->x - or.x;
  355. wss.scale_x = w->cols - or.cols;
  356. wss.shift_y = w->y - or.y;
  357. wss.scale_y = w->lines - or.lines;
  358. if (wss.shift_x != 0 || wss.shift_y != 0 || wss.scale_x != 0 || wss.scale_y != 0)
  359. g_list_foreach (g->widgets, group_widget_set_position, &wss);
  360. }
  361. /* --------------------------------------------------------------------------------------------- */
  362. static void
  363. group_default_resize (WGroup *g, WRect *r)
  364. {
  365. /* This is default resizing mechanism.
  366. * The main idea of this code is to resize dialog according to flags
  367. * (if any of flags require automatic resizing, like WPOS_CENTER,
  368. * end after that reposition controls in dialog according to flags of widget)
  369. */
  370. Widget *w = WIDGET (g);
  371. WRect r0;
  372. r0 = r != NULL ? *r : w->rect;
  373. widget_adjust_position (w->pos_flags, &r0);
  374. group_set_position (g, &r0);
  375. }
  376. /* --------------------------------------------------------------------------------------------- */
  377. static void
  378. group_draw (WGroup *g)
  379. {
  380. Widget *wg = WIDGET (g);
  381. /* draw all widgets in Z-order, from first to last */
  382. if (widget_get_state (wg, WST_ACTIVE))
  383. {
  384. GList *p;
  385. if (g->winch_pending)
  386. {
  387. g->winch_pending = FALSE;
  388. send_message (wg, NULL, MSG_RESIZE, 0, NULL);
  389. }
  390. for (p = g->widgets; p != NULL; p = g_list_next (p))
  391. widget_draw (WIDGET (p->data));
  392. widget_update_cursor (wg);
  393. }
  394. }
  395. /* --------------------------------------------------------------------------------------------- */
  396. static cb_ret_t
  397. group_handle_key (WGroup *g, int key)
  398. {
  399. cb_ret_t handled;
  400. /* first try the hotkey */
  401. handled = send_message (g, NULL, MSG_HOTKEY, key, NULL);
  402. /* not used - then try widget_callback */
  403. if (handled == MSG_NOT_HANDLED)
  404. handled = send_message (g->current->data, NULL, MSG_KEY, key, NULL);
  405. /* not used - try to use the unhandled case */
  406. if (handled == MSG_NOT_HANDLED)
  407. handled = send_message (g, g->current->data, MSG_UNHANDLED_KEY, key, NULL);
  408. return handled;
  409. }
  410. /* --------------------------------------------------------------------------------------------- */
  411. static cb_ret_t
  412. group_handle_hotkey (WGroup *g, int key)
  413. {
  414. GList *current;
  415. Widget *w;
  416. cb_ret_t handled = MSG_NOT_HANDLED;
  417. int c;
  418. if (g->widgets == NULL)
  419. return MSG_NOT_HANDLED;
  420. if (g->current == NULL)
  421. g->current = g->widgets;
  422. w = WIDGET (g->current->data);
  423. if (!widget_get_state (w, WST_VISIBLE) || widget_get_state (w, WST_DISABLED))
  424. return MSG_NOT_HANDLED;
  425. /* Explanation: we don't send letter hotkeys to other widgets
  426. * if the currently selected widget is an input line */
  427. if (widget_get_options (w, WOP_IS_INPUT))
  428. {
  429. /* skip ascii control characters, anything else can valid character in some encoding */
  430. if (key >= 32 && key < 256)
  431. return MSG_NOT_HANDLED;
  432. }
  433. /* If it's an alt key, send the message */
  434. c = key & ~ALT (0);
  435. if (key & ALT (0) && g_ascii_isalpha (c))
  436. key = g_ascii_tolower (c);
  437. if (widget_get_options (w, WOP_WANT_HOTKEY))
  438. handled = send_message (w, NULL, MSG_HOTKEY, key, NULL);
  439. /* If not used, send hotkey to other widgets */
  440. if (handled == MSG_HANDLED)
  441. return MSG_HANDLED;
  442. current = group_get_widget_next_of (g->current);
  443. /* send it to all widgets */
  444. while (g->current != current && handled == MSG_NOT_HANDLED)
  445. {
  446. w = WIDGET (current->data);
  447. if (widget_get_options (w, WOP_WANT_HOTKEY) && !widget_get_state (w, WST_DISABLED))
  448. handled = send_message (w, NULL, MSG_HOTKEY, key, NULL);
  449. if (handled == MSG_NOT_HANDLED)
  450. current = group_get_widget_next_of (current);
  451. }
  452. if (handled == MSG_HANDLED)
  453. {
  454. w = WIDGET (current->data);
  455. widget_select (w);
  456. send_message (g, w, MSG_HOTKEY_HANDLED, 0, NULL);
  457. }
  458. return handled;
  459. }
  460. /* --------------------------------------------------------------------------------------------- */
  461. /*** public functions ****************************************************************************/
  462. /* --------------------------------------------------------------------------------------------- */
  463. /**
  464. * Initialize group.
  465. *
  466. * @param g WGroup widget
  467. * @param y1 y-coordinate of top-left corner
  468. * @param x1 x-coordinate of top-left corner
  469. * @param lines group height
  470. * @param cols group width
  471. * @param callback group callback
  472. * @param mouse_callback group mouse handler
  473. */
  474. void
  475. group_init (WGroup *g, const WRect *r, widget_cb_fn callback, widget_mouse_cb_fn mouse_callback)
  476. {
  477. Widget *w = WIDGET (g);
  478. widget_init (w, r, callback != NULL ? callback : group_default_callback, mouse_callback);
  479. w->mouse_handler = group_handle_mouse_event;
  480. w->make_global = group_default_make_global;
  481. w->make_local = group_default_make_local;
  482. w->find = group_default_find;
  483. w->find_by_type = group_default_find_by_type;
  484. w->find_by_id = group_default_find_by_id;
  485. w->set_state = group_default_set_state;
  486. g->mouse_status = MOU_UNHANDLED;
  487. }
  488. /* --------------------------------------------------------------------------------------------- */
  489. cb_ret_t
  490. group_default_callback (Widget *w, Widget *sender, widget_msg_t msg, int parm, void *data)
  491. {
  492. WGroup *g = GROUP (w);
  493. switch (msg)
  494. {
  495. case MSG_INIT:
  496. g_list_foreach (g->widgets, group_widget_init, NULL);
  497. return MSG_HANDLED;
  498. case MSG_DRAW:
  499. group_draw (g);
  500. return MSG_HANDLED;
  501. case MSG_KEY:
  502. return group_handle_key (g, parm);
  503. case MSG_HOTKEY:
  504. return group_handle_hotkey (g, parm);
  505. case MSG_CURSOR:
  506. return group_update_cursor (g);
  507. case MSG_RESIZE:
  508. group_default_resize (g, RECT (data));
  509. return MSG_HANDLED;
  510. case MSG_DESTROY:
  511. g_list_foreach (g->widgets, (GFunc) widget_destroy, NULL);
  512. g_list_free (g->widgets);
  513. g->widgets = NULL;
  514. return MSG_HANDLED;
  515. default:
  516. return widget_default_callback (w, sender, msg, parm, data);
  517. }
  518. }
  519. /* --------------------------------------------------------------------------------------------- */
  520. /**
  521. * Change state of group.
  522. *
  523. * @param w group
  524. * @param state widget state flag to modify
  525. * @param enable specifies whether to turn the flag on (TRUE) or off (FALSE).
  526. * Only one flag per call can be modified.
  527. * @return MSG_HANDLED if set was handled successfully, MSG_NOT_HANDLED otherwise.
  528. */
  529. cb_ret_t
  530. group_default_set_state (Widget *w, widget_state_t state, gboolean enable)
  531. {
  532. gboolean ret = MSG_HANDLED;
  533. WGroup *g = GROUP (w);
  534. widget_state_info_t st = {
  535. .state = state,
  536. .enable = enable
  537. };
  538. ret = widget_default_set_state (w, state, enable);
  539. if (state == WST_ACTIVE || state == WST_SUSPENDED || state == WST_CLOSED)
  540. /* inform all child widgets */
  541. g_list_foreach (g->widgets, group_widget_set_state, &st);
  542. if ((w->state & WST_ACTIVE) != 0)
  543. {
  544. if ((w->state & WST_FOCUSED) != 0)
  545. {
  546. /* update current widget */
  547. if (g->current != NULL)
  548. widget_set_state (WIDGET (g->current->data), WST_FOCUSED, enable);
  549. }
  550. else
  551. /* inform all child widgets */
  552. g_list_foreach (g->widgets, group_widget_set_state, &st);
  553. }
  554. return ret;
  555. }
  556. /* --------------------------------------------------------------------------------------------- */
  557. /**
  558. * Handling mouse events.
  559. *
  560. * @param g WGroup object
  561. * @param event GPM mouse event
  562. *
  563. * @return result of mouse event handling
  564. */
  565. int
  566. group_handle_mouse_event (Widget *w, Gpm_Event *event)
  567. {
  568. WGroup *g = GROUP (w);
  569. if (g->widgets != NULL)
  570. {
  571. GList *p;
  572. /* send the event to widgets in reverse Z-order */
  573. p = g_list_last (g->widgets);
  574. do
  575. {
  576. Widget *wp = WIDGET (p->data);
  577. /* Don't use widget_is_selectable() here.
  578. If WOP_SELECTABLE option is not set, widget can handle mouse events.
  579. For example, commandl line in file manager */
  580. if (widget_get_state (w, WST_VISIBLE) && !widget_get_state (wp, WST_DISABLED))
  581. {
  582. /* put global cursor position to the widget */
  583. int ret;
  584. ret = wp->mouse_handler (wp, event);
  585. if (ret != MOU_UNHANDLED)
  586. return ret;
  587. }
  588. p = g_list_previous (p);
  589. }
  590. while (p != NULL);
  591. }
  592. return MOU_UNHANDLED;
  593. }
  594. /* --------------------------------------------------------------------------------------------- */
  595. /**
  596. * Insert widget to group before specified widget with specified positioning.
  597. * Make the inserted widget current.
  598. *
  599. * @param g WGroup object
  600. * @param w widget to be added
  601. * @pos positioning flags
  602. * @param before add @w before this widget
  603. *
  604. * @return widget ID
  605. */
  606. unsigned long
  607. group_add_widget_autopos (WGroup *g, void *w, widget_pos_flags_t pos_flags, const void *before)
  608. {
  609. Widget *wg = WIDGET (g);
  610. Widget *ww = WIDGET (w);
  611. GList *new_current;
  612. /* Don't accept NULL widget. This shouldn't happen */
  613. assert (ww != NULL);
  614. if ((pos_flags & WPOS_CENTER_HORZ) != 0)
  615. ww->rect.x = (wg->rect.cols - ww->rect.cols) / 2;
  616. if ((pos_flags & WPOS_CENTER_VERT) != 0)
  617. ww->rect.y = (wg->rect.lines - ww->rect.lines) / 2;
  618. ww->owner = g;
  619. ww->pos_flags = pos_flags;
  620. widget_make_global (ww);
  621. if (g->widgets == NULL || before == NULL)
  622. {
  623. g->widgets = g_list_append (g->widgets, ww);
  624. new_current = g_list_last (g->widgets);
  625. }
  626. else
  627. {
  628. GList *b;
  629. b = g_list_find (g->widgets, before);
  630. /* don't accept widget not from group. This shouldn't happen */
  631. assert (b != NULL);
  632. b = g_list_next (b);
  633. g->widgets = g_list_insert_before (g->widgets, b, ww);
  634. if (b != NULL)
  635. new_current = g_list_previous (b);
  636. else
  637. new_current = g_list_last (g->widgets);
  638. }
  639. /* widget has been added at runtime */
  640. if (widget_get_state (wg, WST_ACTIVE))
  641. {
  642. group_widget_init (ww, NULL);
  643. widget_select (ww);
  644. }
  645. else
  646. g->current = new_current;
  647. return ww->id;
  648. }
  649. /* --------------------------------------------------------------------------------------------- */
  650. /**
  651. * Remove widget from group.
  652. *
  653. * @param w Widget object
  654. */
  655. void
  656. group_remove_widget (void *w)
  657. {
  658. Widget *ww = WIDGET (w);
  659. WGroup *g;
  660. GList *d;
  661. /* Don't accept NULL widget. This shouldn't happen */
  662. assert (w != NULL);
  663. g = ww->owner;
  664. d = g_list_find (g->widgets, ww);
  665. if (d == g->current)
  666. group_set_current_widget_next (g);
  667. g->widgets = g_list_delete_link (g->widgets, d);
  668. if (g->widgets == NULL)
  669. g->current = NULL;
  670. /* widget has been deleted at runtime */
  671. if (widget_get_state (WIDGET (g), WST_ACTIVE))
  672. {
  673. group_draw (g);
  674. group_select_current_widget (g);
  675. }
  676. widget_make_local (ww);
  677. ww->owner = NULL;
  678. }
  679. /* --------------------------------------------------------------------------------------------- */
  680. /**
  681. * Switch current widget to widget after current in group.
  682. *
  683. * @param g WGroup object
  684. */
  685. void
  686. group_set_current_widget_next (WGroup *g)
  687. {
  688. g->current = group_get_next_or_prev_of (g->current, TRUE);
  689. }
  690. /* --------------------------------------------------------------------------------------------- */
  691. /**
  692. * Switch current widget to widget before current in group.
  693. *
  694. * @param g WGroup object
  695. */
  696. void
  697. group_set_current_widget_prev (WGroup *g)
  698. {
  699. g->current = group_get_next_or_prev_of (g->current, FALSE);
  700. }
  701. /* --------------------------------------------------------------------------------------------- */
  702. /**
  703. * Get widget that is after specified widget in group.
  704. *
  705. * @param w widget holder
  706. *
  707. * @return widget that is after "w" or NULL if "w" is NULL or widget doesn't have owner
  708. */
  709. GList *
  710. group_get_widget_next_of (GList *w)
  711. {
  712. return group_get_next_or_prev_of (w, TRUE);
  713. }
  714. /* --------------------------------------------------------------------------------------------- */
  715. /**
  716. * Get widget that is before specified widget in group.
  717. *
  718. * @param w widget holder
  719. *
  720. * @return widget that is before "w" or NULL if "w" is NULL or widget doesn't have owner
  721. */
  722. GList *
  723. group_get_widget_prev_of (GList *w)
  724. {
  725. return group_get_next_or_prev_of (w, FALSE);
  726. }
  727. /* --------------------------------------------------------------------------------------------- */
  728. /**
  729. * Try to select next widget in the Z order.
  730. *
  731. * @param g WGroup object
  732. */
  733. void
  734. group_select_next_widget (WGroup *g)
  735. {
  736. group_select_next_or_prev (g, TRUE);
  737. }
  738. /* --------------------------------------------------------------------------------------------- */
  739. /**
  740. * Try to select previous widget in the Z order.
  741. *
  742. * @param g WGroup object
  743. */
  744. void
  745. group_select_prev_widget (WGroup *g)
  746. {
  747. group_select_next_or_prev (g, FALSE);
  748. }
  749. /* --------------------------------------------------------------------------------------------- */
  750. /**
  751. * Find the widget with the specified ID in the group and select it
  752. *
  753. * @param g WGroup object
  754. * @param id widget ID
  755. */
  756. void
  757. group_select_widget_by_id (const WGroup *g, unsigned long id)
  758. {
  759. Widget *w;
  760. w = widget_find_by_id (CONST_WIDGET (g), id);
  761. if (w != NULL)
  762. widget_select (w);
  763. }
  764. /* --------------------------------------------------------------------------------------------- */
  765. /**
  766. * Send broadcast message to all widgets in the group.
  767. *
  768. * @param g WGroup object
  769. * @param msg message sent to widgets
  770. */
  771. void
  772. group_send_broadcast_msg (WGroup *g, widget_msg_t msg)
  773. {
  774. group_send_broadcast_msg_custom (g, msg, FALSE, WOP_DEFAULT);
  775. }
  776. /* --------------------------------------------------------------------------------------------- */