group.c 26 KB

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