root/lib/widget/widget-common.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. widget_set_id
  2. widget_default_resize
  3. widget_do_focus
  4. widget_focus
  5. widget_reorder
  6. hotkey_cmp
  7. widget_default_mouse_callback
  8. widget_default_get_colors
  9. hotkey_new
  10. hotkey_free
  11. hotkey_width
  12. hotkey_equal
  13. hotkey_draw
  14. hotkey_get_text
  15. widget_init
  16. widget_default_callback
  17. widget_set_options
  18. widget_adjust_position
  19. widget_set_size
  20. widget_set_size_rect
  21. widget_selectcolor
  22. widget_erase
  23. widget_set_visibility
  24. widget_is_active
  25. widget_draw
  26. widget_replace
  27. widget_is_focusable
  28. widget_select
  29. widget_set_bottom
  30. widget_lookup_key
  31. widget_default_make_global
  32. widget_default_make_local
  33. widget_default_find
  34. widget_default_find_by_type
  35. widget_default_find_by_id
  36. widget_default_set_state
  37. widget_default_destroy
  38. mouse_get_local
  39. mouse_global_in_widget

   1 /*
   2    Widgets for the Midnight Commander
   3 
   4    Copyright (C) 1994-2025
   5    Free Software Foundation, Inc.
   6 
   7    Authors:
   8    Radek Doulik, 1994, 1995
   9    Miguel de Icaza, 1994, 1995
  10    Jakub Jelinek, 1995
  11    Andrej Borsenkow, 1996
  12    Norbert Warmuth, 1997
  13    Andrew Borodin <aborodin@vmail.ru>, 2009-2022
  14 
  15    This file is part of the Midnight Commander.
  16 
  17    The Midnight Commander is free software: you can redistribute it
  18    and/or modify it under the terms of the GNU General Public License as
  19    published by the Free Software Foundation, either version 3 of the License,
  20    or (at your option) any later version.
  21 
  22    The Midnight Commander is distributed in the hope that it will be useful,
  23    but WITHOUT ANY WARRANTY; without even the implied warranty of
  24    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  25    GNU General Public License for more details.
  26 
  27    You should have received a copy of the GNU General Public License
  28    along with this program.  If not, see <https://www.gnu.org/licenses/>.
  29  */
  30 
  31 /** \file widget-common.c
  32  *  \brief Source: shared stuff of widgets
  33  */
  34 
  35 #include <config.h>
  36 
  37 #include <stdlib.h>
  38 #include <string.h>
  39 
  40 #include "lib/global.h"
  41 
  42 #include "lib/tty/tty.h"
  43 #include "lib/tty/color.h"
  44 #include "lib/skin.h"
  45 #include "lib/strutil.h"
  46 #include "lib/widget.h"
  47 
  48 /*** global variables ****************************************************************************/
  49 
  50 /*** file scope macro definitions ****************************************************************/
  51 
  52 /*** file scope type declarations ****************************************************************/
  53 
  54 /*** forward declarations (file scope functions) *************************************************/
  55 
  56 /*** file scope variables ************************************************************************/
  57 
  58 /* maximum value of used widget ID */
  59 static unsigned long widget_id = 0;
  60 
  61 /* --------------------------------------------------------------------------------------------- */
  62 /*** file scope functions ************************************************************************/
  63 /* --------------------------------------------------------------------------------------------- */
  64 
  65 /**
  66  * Calc widget ID,
  67  * Widget ID is uniq for each widget created during MC session (like PID in OS).
  68  *
  69  * @return widget ID.
  70  */
  71 static unsigned long
  72 widget_set_id (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
  73 {
  74     unsigned long id;
  75 
  76     id = widget_id++;
  77     // TODO IF NEEDED: if id is already used, find next free id.
  78 
  79     return id;
  80 }
  81 
  82 /* --------------------------------------------------------------------------------------------- */
  83 
  84 static cb_ret_t
  85 widget_default_resize (Widget *w, const WRect *r)
     /* [previous][next][first][last][top][bottom][index][help]  */
  86 {
  87     if (r == NULL)
  88         return MSG_NOT_HANDLED;
  89 
  90     w->rect = *r;
  91 
  92     return MSG_HANDLED;
  93 }
  94 
  95 /* --------------------------------------------------------------------------------------------- */
  96 
  97 static void
  98 widget_do_focus (Widget *w, gboolean enable)
     /* [previous][next][first][last][top][bottom][index][help]  */
  99 {
 100     if (w != NULL && widget_get_state (WIDGET (w->owner), WST_VISIBLE | WST_FOCUSED))
 101         widget_set_state (w, WST_FOCUSED, enable);
 102 }
 103 
 104 /* --------------------------------------------------------------------------------------------- */
 105 /**
 106  * Focus specified widget in it's owner.
 107  *
 108  * @param w widget to be focused.
 109  */
 110 
 111 static void
 112 widget_focus (Widget *w)
     /* [previous][next][first][last][top][bottom][index][help]  */
 113 {
 114     WGroup *g = w->owner;
 115 
 116     if (g == NULL || g->current == NULL)
 117         return;
 118 
 119     if (WIDGET (g->current->data) != w)
 120     {
 121         widget_do_focus (WIDGET (g->current->data), FALSE);
 122         // Test if focus lost was allowed and focus has really been loose
 123         if (g->current == NULL || !widget_get_state (WIDGET (g->current->data), WST_FOCUSED))
 124         {
 125             widget_do_focus (w, TRUE);
 126             g->current = widget_find (WIDGET (g), w);
 127         }
 128     }
 129     else if (!widget_get_state (w, WST_FOCUSED))
 130         widget_do_focus (w, TRUE);
 131 }
 132 
 133 /* --------------------------------------------------------------------------------------------- */
 134 
 135 /**
 136  * Put widget on top or bottom of Z-order.
 137  */
 138 static void
 139 widget_reorder (GList *l, gboolean set_top)
     /* [previous][next][first][last][top][bottom][index][help]  */
 140 {
 141     WGroup *g = WIDGET (l->data)->owner;
 142 
 143     g->widgets = g_list_remove_link (g->widgets, l);
 144     if (set_top)
 145         g->widgets = g_list_concat (g->widgets, l);
 146     else
 147         g->widgets = g_list_concat (l, g->widgets);
 148 }
 149 
 150 /* --------------------------------------------------------------------------------------------- */
 151 
 152 static gboolean
 153 hotkey_cmp (const char *s1, const char *s2)
     /* [previous][next][first][last][top][bottom][index][help]  */
 154 {
 155     gboolean n1, n2;
 156 
 157     n1 = s1 != NULL;
 158     n2 = s2 != NULL;
 159 
 160     if (n1 != n2)
 161         return FALSE;
 162 
 163     if (n1 && n2 && strcmp (s1, s2) != 0)
 164         return FALSE;
 165 
 166     return TRUE;
 167 }
 168 
 169 /* --------------------------------------------------------------------------------------------- */
 170 
 171 static void
 172 widget_default_mouse_callback (Widget *w, mouse_msg_t msg, mouse_event_t *event)
     /* [previous][next][first][last][top][bottom][index][help]  */
 173 {
 174     // do nothing
 175     (void) w;
 176     (void) msg;
 177     (void) event;
 178 }
 179 
 180 /* --------------------------------------------------------------------------------------------- */
 181 
 182 static const int *
 183 widget_default_get_colors (const Widget *w)
     /* [previous][next][first][last][top][bottom][index][help]  */
 184 {
 185     const Widget *owner = CONST_WIDGET (w->owner);
 186 
 187     return (owner == NULL ? NULL : widget_get_colors (owner));
 188 }
 189 
 190 /* --------------------------------------------------------------------------------------------- */
 191 /*** public functions ****************************************************************************/
 192 /* --------------------------------------------------------------------------------------------- */
 193 
 194 struct hotkey_t
 195 hotkey_new (const char *text)
     /* [previous][next][first][last][top][bottom][index][help]  */
 196 {
 197     hotkey_t result;
 198     const char *cp, *p;
 199 
 200     if (text == NULL)
 201         text = "";
 202 
 203     // search for '&', that is not on the of text
 204     cp = strchr (text, '&');
 205     if (cp != NULL && cp[1] != '\0')
 206     {
 207         result.start = g_strndup (text, cp - text);
 208 
 209         // skip '&'
 210         cp++;
 211         p = str_cget_next_char (cp);
 212         result.hotkey = g_strndup (cp, p - cp);
 213 
 214         cp = p;
 215         result.end = g_strdup (cp);
 216     }
 217     else
 218     {
 219         result.start = g_strdup (text);
 220         result.hotkey = NULL;
 221         result.end = NULL;
 222     }
 223 
 224     return result;
 225 }
 226 
 227 /* --------------------------------------------------------------------------------------------- */
 228 
 229 void
 230 hotkey_free (const hotkey_t hotkey)
     /* [previous][next][first][last][top][bottom][index][help]  */
 231 {
 232     g_free (hotkey.start);
 233     g_free (hotkey.hotkey);
 234     g_free (hotkey.end);
 235 }
 236 
 237 /* --------------------------------------------------------------------------------------------- */
 238 
 239 int
 240 hotkey_width (const hotkey_t hotkey)
     /* [previous][next][first][last][top][bottom][index][help]  */
 241 {
 242     int result;
 243 
 244     result = str_term_width1 (hotkey.start);
 245     result += (hotkey.hotkey != NULL) ? str_term_width1 (hotkey.hotkey) : 0;
 246     result += (hotkey.end != NULL) ? str_term_width1 (hotkey.end) : 0;
 247     return result;
 248 }
 249 
 250 /* --------------------------------------------------------------------------------------------- */
 251 
 252 gboolean
 253 hotkey_equal (const hotkey_t hotkey1, const hotkey_t hotkey2)
     /* [previous][next][first][last][top][bottom][index][help]  */
 254 {
 255     return (strcmp (hotkey1.start, hotkey2.start) == 0)
 256         && hotkey_cmp (hotkey1.hotkey, hotkey2.hotkey) && hotkey_cmp (hotkey1.end, hotkey2.end);
 257 }
 258 
 259 /* --------------------------------------------------------------------------------------------- */
 260 
 261 void
 262 hotkey_draw (const Widget *w, const hotkey_t hotkey, gboolean focused)
     /* [previous][next][first][last][top][bottom][index][help]  */
 263 {
 264     if (hotkey.start[0] != '\0')
 265     {
 266         widget_selectcolor (w, focused, FALSE);
 267         tty_print_string (hotkey.start);
 268     }
 269 
 270     if (hotkey.hotkey != NULL)
 271     {
 272         widget_selectcolor (w, focused, TRUE);
 273         tty_print_string (hotkey.hotkey);
 274     }
 275 
 276     if (hotkey.end != NULL)
 277     {
 278         widget_selectcolor (w, focused, FALSE);
 279         tty_print_string (hotkey.end);
 280     }
 281 }
 282 
 283 /* --------------------------------------------------------------------------------------------- */
 284 
 285 char *
 286 hotkey_get_text (const hotkey_t hotkey)
     /* [previous][next][first][last][top][bottom][index][help]  */
 287 {
 288     GString *text;
 289 
 290     text = g_string_new (hotkey.start);
 291 
 292     if (hotkey.hotkey != NULL)
 293     {
 294         g_string_append_c (text, '&');
 295         g_string_append (text, hotkey.hotkey);
 296     }
 297 
 298     if (hotkey.end != NULL)
 299         g_string_append (text, hotkey.end);
 300 
 301     return g_string_free (text, FALSE);
 302 }
 303 
 304 /* --------------------------------------------------------------------------------------------- */
 305 
 306 void
 307 widget_init (Widget *w, const WRect *r, widget_cb_fn callback, widget_mouse_cb_fn mouse_callback)
     /* [previous][next][first][last][top][bottom][index][help]  */
 308 {
 309     w->id = widget_set_id ();
 310     w->rect = *r;
 311     w->pos_flags = WPOS_KEEP_DEFAULT;
 312     w->callback = callback;
 313 
 314     w->keymap = NULL;
 315     w->ext_keymap = NULL;
 316     w->ext_mode = FALSE;
 317 
 318     w->mouse_callback = mouse_callback != NULL ? mouse_callback : widget_default_mouse_callback;
 319     w->owner = NULL;
 320     w->mouse_handler = mouse_handle_event;
 321     w->mouse.forced_capture = FALSE;
 322     w->mouse.capture = FALSE;
 323     w->mouse.last_msg = MSG_MOUSE_NONE;
 324     w->mouse.last_buttons_down = 0;
 325 
 326     w->options = WOP_DEFAULT;
 327     w->state = WST_CONSTRUCT | WST_VISIBLE;
 328 
 329     w->make_global = widget_default_make_global;
 330     w->make_local = widget_default_make_local;
 331 
 332     w->find = widget_default_find;
 333     w->find_by_type = widget_default_find_by_type;
 334     w->find_by_id = widget_default_find_by_id;
 335 
 336     w->set_state = widget_default_set_state;
 337     w->destroy = widget_default_destroy;
 338     w->get_colors = widget_default_get_colors;
 339 }
 340 
 341 /* --------------------------------------------------------------------------------------------- */
 342 
 343 /* Default callback for widgets */
 344 cb_ret_t
 345 widget_default_callback (Widget *w, Widget *sender, widget_msg_t msg, int parm, void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 346 {
 347     (void) sender;
 348     (void) parm;
 349 
 350     switch (msg)
 351     {
 352     case MSG_INIT:
 353     case MSG_FOCUS:
 354     case MSG_UNFOCUS:
 355     case MSG_ENABLE:
 356     case MSG_DISABLE:
 357     case MSG_DRAW:
 358     case MSG_DESTROY:
 359     case MSG_CURSOR:
 360     case MSG_IDLE:
 361         return MSG_HANDLED;
 362 
 363     case MSG_RESIZE:
 364         return widget_default_resize (w, CONST_RECT (data));
 365 
 366     default:
 367         return MSG_NOT_HANDLED;
 368     }
 369 }
 370 
 371 /* --------------------------------------------------------------------------------------------- */
 372 
 373 /**
 374  * Apply new options to widget.
 375  *
 376  * @param w       widget
 377  * @param options widget option flags to modify. Several flags per call can be modified.
 378  * @param enable  TRUE if specified options should be added, FALSE if options should be removed
 379  */
 380 void
 381 widget_set_options (Widget *w, widget_options_t options, gboolean enable)
     /* [previous][next][first][last][top][bottom][index][help]  */
 382 {
 383     if (enable)
 384         w->options |= options;
 385     else
 386         w->options &= ~options;
 387 }
 388 
 389 /* --------------------------------------------------------------------------------------------- */
 390 
 391 void
 392 widget_adjust_position (widget_pos_flags_t pos_flags, WRect *r)
     /* [previous][next][first][last][top][bottom][index][help]  */
 393 {
 394     if ((pos_flags & WPOS_FULLSCREEN) != 0)
 395     {
 396         r->y = 0;
 397         r->x = 0;
 398         r->lines = LINES;
 399         r->cols = COLS;
 400     }
 401     else
 402     {
 403         if ((pos_flags & WPOS_CENTER_HORZ) != 0)
 404             r->x = (COLS - r->cols) / 2;
 405 
 406         if ((pos_flags & WPOS_CENTER_VERT) != 0)
 407             r->y = (LINES - r->lines) / 2;
 408 
 409         if ((pos_flags & WPOS_TRYUP) != 0)
 410         {
 411             if (r->y > 3)
 412                 r->y -= 2;
 413             else if (r->y == 3)
 414                 r->y = 2;
 415         }
 416     }
 417 }
 418 
 419 /* --------------------------------------------------------------------------------------------- */
 420 /**
 421  * Change widget position and size.
 422  *
 423  * @param w widget
 424  * @param y y coordinate of top-left corner
 425  * @param x x coordinate of top-left corner
 426  * @param lines width
 427  * @param cols height
 428  */
 429 
 430 void
 431 widget_set_size (Widget *w, int y, int x, int lines, int cols)
     /* [previous][next][first][last][top][bottom][index][help]  */
 432 {
 433     WRect r = { y, x, lines, cols };
 434 
 435     send_message (w, NULL, MSG_RESIZE, 0, &r);
 436     widget_draw (w);
 437 }
 438 
 439 /* --------------------------------------------------------------------------------------------- */
 440 /**
 441  * Change widget position and size.
 442  *
 443  * @param w widget
 444  * @param r WRect object that holds position and size
 445  */
 446 
 447 void
 448 widget_set_size_rect (Widget *w, WRect *r)
     /* [previous][next][first][last][top][bottom][index][help]  */
 449 {
 450     send_message (w, NULL, MSG_RESIZE, 0, r);
 451     widget_draw (w);
 452 }
 453 
 454 /* --------------------------------------------------------------------------------------------- */
 455 
 456 void
 457 widget_selectcolor (const Widget *w, gboolean focused, gboolean hotkey)
     /* [previous][next][first][last][top][bottom][index][help]  */
 458 {
 459     int color;
 460     const int *colors;
 461 
 462     colors = widget_get_colors (w);
 463 
 464     if (widget_get_state (w, WST_DISABLED))
 465         color = DISABLED_COLOR;
 466     else if (hotkey)
 467         color = colors[focused ? DLG_COLOR_HOT_FOCUS : DLG_COLOR_HOT_NORMAL];
 468     else
 469         color = colors[focused ? DLG_COLOR_FOCUS : DLG_COLOR_NORMAL];
 470 
 471     tty_setcolor (color);
 472 }
 473 
 474 /* --------------------------------------------------------------------------------------------- */
 475 
 476 void
 477 widget_erase (Widget *w)
     /* [previous][next][first][last][top][bottom][index][help]  */
 478 {
 479     if (w != NULL)
 480         tty_fill_region (w->rect.y, w->rect.x, w->rect.lines, w->rect.cols, ' ');
 481 }
 482 
 483 /* --------------------------------------------------------------------------------------------- */
 484 
 485 void
 486 widget_set_visibility (Widget *w, gboolean make_visible)
     /* [previous][next][first][last][top][bottom][index][help]  */
 487 {
 488     if (widget_get_state (w, WST_VISIBLE) != make_visible)
 489         widget_set_state (w, WST_VISIBLE, make_visible);
 490 }
 491 
 492 /* --------------------------------------------------------------------------------------------- */
 493 /**
 494  * Check whether widget is active or not.
 495  * Widget is active if it's current in the its owner and each owner in the chain is current too.
 496  *
 497  * @param w the widget
 498  *
 499  * @return TRUE if the widget is active, FALSE otherwise
 500  */
 501 
 502 gboolean
 503 widget_is_active (const void *w)
     /* [previous][next][first][last][top][bottom][index][help]  */
 504 {
 505     const WGroup *owner;
 506 
 507     // Is group top?
 508     if (w == top_dlg->data)
 509         return TRUE;
 510 
 511     owner = CONST_WIDGET (w)->owner;
 512 
 513     // Is widget in any group?
 514     if (owner == NULL)
 515         return FALSE;
 516 
 517     if (w != owner->current->data)
 518         return FALSE;
 519 
 520     return widget_is_active (owner);
 521 }
 522 
 523 /* --------------------------------------------------------------------------------------------- */
 524 
 525 cb_ret_t
 526 widget_draw (Widget *w)
     /* [previous][next][first][last][top][bottom][index][help]  */
 527 {
 528     cb_ret_t ret = MSG_NOT_HANDLED;
 529 
 530     if (w != NULL && widget_get_state (w, WST_VISIBLE))
 531     {
 532         WGroup *g = w->owner;
 533 
 534         if (g != NULL && widget_get_state (WIDGET (g), WST_ACTIVE))
 535             ret = w->callback (w, NULL, MSG_DRAW, 0, NULL);
 536     }
 537 
 538     return ret;
 539 }
 540 
 541 /* --------------------------------------------------------------------------------------------- */
 542 /**
 543  * Replace widget in the dialog.
 544  *
 545  * @param old_w old widget that need to be replaced
 546  * @param new_w new widget that will replace @old_w
 547  */
 548 
 549 void
 550 widget_replace (Widget *old_w, Widget *new_w)
     /* [previous][next][first][last][top][bottom][index][help]  */
 551 {
 552     WGroup *g = old_w->owner;
 553     gboolean should_focus = FALSE;
 554     GList *holder;
 555 
 556     if (g->widgets == NULL)
 557         return;
 558 
 559     if (g->current == NULL)
 560         g->current = g->widgets;
 561 
 562     // locate widget position in the list
 563     if (old_w == g->current->data)
 564         holder = g->current;
 565     else
 566         holder = g_list_find (g->widgets, old_w);
 567 
 568     // if old widget is focused, we should focus the new one...
 569     if (widget_get_state (old_w, WST_FOCUSED))
 570         should_focus = TRUE;
 571     // ...but if new widget isn't selectable, we cannot focus it
 572     if (!widget_get_options (new_w, WOP_SELECTABLE))
 573         should_focus = FALSE;
 574 
 575     // if new widget isn't selectable, select other widget before replace
 576     if (!should_focus)
 577     {
 578         GList *l;
 579 
 580         for (l = group_get_widget_next_of (holder);
 581              !widget_is_focusable (WIDGET (l->data)) && l != holder;
 582              l = group_get_widget_next_of (l))
 583             ;
 584 
 585         widget_select (WIDGET (l->data));
 586     }
 587 
 588     // replace widget
 589     new_w->owner = g;
 590     new_w->id = old_w->id;
 591     holder->data = new_w;
 592 
 593     send_message (old_w, NULL, MSG_DESTROY, 0, NULL);
 594     send_message (new_w, NULL, MSG_INIT, 0, NULL);
 595 
 596     if (should_focus)
 597         widget_select (new_w);
 598     else
 599         widget_draw (new_w);
 600 }
 601 
 602 /* --------------------------------------------------------------------------------------------- */
 603 
 604 gboolean
 605 widget_is_focusable (const Widget *w)
     /* [previous][next][first][last][top][bottom][index][help]  */
 606 {
 607     return (widget_get_options (w, WOP_SELECTABLE) && widget_get_state (w, WST_VISIBLE)
 608             && !widget_get_state (w, WST_DISABLED));
 609 }
 610 
 611 /* --------------------------------------------------------------------------------------------- */
 612 /**
 613  * Select specified widget in it's owner.
 614  *
 615  * Note: this function (and widget_focus(), which it calls) is a no-op
 616  * if the widget is already selected.
 617  *
 618  * @param w widget to be selected
 619  */
 620 
 621 void
 622 widget_select (Widget *w)
     /* [previous][next][first][last][top][bottom][index][help]  */
 623 {
 624     WGroup *g;
 625 
 626     if (!widget_get_options (w, WOP_SELECTABLE))
 627         return;
 628 
 629     g = GROUP (w->owner);
 630     if (g != NULL)
 631     {
 632         if (widget_get_options (w, WOP_TOP_SELECT))
 633         {
 634             GList *l;
 635 
 636             l = widget_find (WIDGET (g), w);
 637             widget_reorder (l, TRUE);
 638         }
 639 
 640         widget_focus (w);
 641     }
 642 }
 643 
 644 /* --------------------------------------------------------------------------------------------- */
 645 /**
 646  * Set widget at bottom of widget list.
 647  */
 648 
 649 void
 650 widget_set_bottom (Widget *w)
     /* [previous][next][first][last][top][bottom][index][help]  */
 651 {
 652     widget_reorder (widget_find (WIDGET (w->owner), w), FALSE);
 653 }
 654 
 655 /* --------------------------------------------------------------------------------------------- */
 656 /**
 657  * Look up key event of widget and translate it to command ID.
 658  * @param w   widget
 659  * @param key key event
 660  *
 661  * @return command ID binded with @key.
 662  */
 663 
 664 long
 665 widget_lookup_key (Widget *w, int key)
     /* [previous][next][first][last][top][bottom][index][help]  */
 666 {
 667     if (w->ext_mode)
 668     {
 669         w->ext_mode = FALSE;
 670         return keybind_lookup_keymap_command (w->ext_keymap, key);
 671     }
 672 
 673     return keybind_lookup_keymap_command (w->keymap, key);
 674 }
 675 
 676 /* --------------------------------------------------------------------------------------------- */
 677 
 678 /**
 679  * Default widget callback to convert widget coordinates from local (relative to owner) to global
 680  * (relative to screen).
 681  *
 682  * @param w widget
 683  * @delta offset for top-left corner coordinates. Used for child widgets of WGroup
 684  */
 685 
 686 void
 687 widget_default_make_global (Widget *w, const WRect *delta)
     /* [previous][next][first][last][top][bottom][index][help]  */
 688 {
 689     if (delta != NULL)
 690         rect_move (&w->rect, delta->y, delta->x);
 691     else if (w->owner != NULL)
 692         rect_move (&w->rect, WIDGET (w->owner)->rect.y, WIDGET (w->owner)->rect.x);
 693 }
 694 
 695 /* --------------------------------------------------------------------------------------------- */
 696 
 697 /**
 698  * Default widget callback to convert widget coordinates from global (relative to screen) to local
 699  * (relative to owner).
 700  *
 701  * @param w widget
 702  * @delta offset for top-left corner coordinates. Used for child widgets of WGroup
 703  */
 704 
 705 void
 706 widget_default_make_local (Widget *w, const WRect *delta)
     /* [previous][next][first][last][top][bottom][index][help]  */
 707 {
 708     if (delta != NULL)
 709         rect_move (&w->rect, -delta->y, -delta->x);
 710     else if (w->owner != NULL)
 711         rect_move (&w->rect, -WIDGET (w->owner)->rect.y, -WIDGET (w->owner)->rect.x);
 712 }
 713 
 714 /* --------------------------------------------------------------------------------------------- */
 715 /**
 716  * Default callback function to find widget.
 717  *
 718  * @param w widget
 719  * @param what widget to find
 720  *
 721  * @return holder of @what if widget is @what, NULL otherwise
 722  */
 723 
 724 GList *
 725 widget_default_find (const Widget *w, const Widget *what)
     /* [previous][next][first][last][top][bottom][index][help]  */
 726 {
 727     return (w != what || w->owner == NULL) ? NULL
 728                                            : g_list_find (CONST_GROUP (w->owner)->widgets, what);
 729 }
 730 
 731 /* --------------------------------------------------------------------------------------------- */
 732 
 733 /**
 734  * Default callback function to find widget by widget type using widget callback.
 735  *
 736  * @param w widget
 737  * @param cb widget callback
 738  *
 739  * @return @w if widget callback is @cb, NULL otherwise
 740  */
 741 
 742 Widget *
 743 widget_default_find_by_type (const Widget *w, widget_cb_fn cb)
     /* [previous][next][first][last][top][bottom][index][help]  */
 744 {
 745     return (w->callback == cb ? WIDGET (w) : NULL);
 746 }
 747 
 748 /* --------------------------------------------------------------------------------------------- */
 749 /**
 750  * Default callback function to find widget by widget ID.
 751  *
 752  * @param w widget
 753  * @param id widget ID
 754  *
 755  * @return @w if widget id is equal to @id, NULL otherwise
 756  */
 757 
 758 Widget *
 759 widget_default_find_by_id (const Widget *w, unsigned long id)
     /* [previous][next][first][last][top][bottom][index][help]  */
 760 {
 761     return (w->id == id ? WIDGET (w) : NULL);
 762 }
 763 
 764 /* --------------------------------------------------------------------------------------------- */
 765 
 766 /**
 767  * Default callback function to modify state of widget.
 768  *
 769  * @param w      widget
 770  * @param state  widget state flag to modify
 771  * @param enable specifies whether to turn the flag on (TRUE) or off (FALSE).
 772  *               Only one flag per call can be modified.
 773  * @return       MSG_HANDLED if set was handled successfully, MSG_NOT_HANDLED otherwise.
 774  */
 775 
 776 cb_ret_t
 777 widget_default_set_state (Widget *w, widget_state_t state, gboolean enable)
     /* [previous][next][first][last][top][bottom][index][help]  */
 778 {
 779     gboolean ret = MSG_HANDLED;
 780     Widget *owner = WIDGET (GROUP (w->owner));
 781 
 782     if (enable)
 783         w->state |= state;
 784     else
 785         w->state &= ~state;
 786 
 787     if (enable)
 788     {
 789         // exclusive bits
 790         switch (state)
 791         {
 792         case WST_CONSTRUCT:
 793             w->state &= ~(WST_ACTIVE | WST_SUSPENDED | WST_CLOSED);
 794             break;
 795         case WST_ACTIVE:
 796             w->state &= ~(WST_CONSTRUCT | WST_SUSPENDED | WST_CLOSED);
 797             break;
 798         case WST_SUSPENDED:
 799             w->state &= ~(WST_CONSTRUCT | WST_ACTIVE | WST_CLOSED);
 800             break;
 801         case WST_CLOSED:
 802             w->state &= ~(WST_CONSTRUCT | WST_ACTIVE | WST_SUSPENDED);
 803             break;
 804         default:
 805             break;
 806         }
 807     }
 808 
 809     if (owner == NULL)
 810         return MSG_NOT_HANDLED;
 811 
 812     switch (state)
 813     {
 814     case WST_VISIBLE:
 815         if (widget_get_state (owner, WST_ACTIVE))
 816         {
 817             widget_draw (owner);  // redraw owner to show/hide widget
 818 
 819             if (!enable)
 820             {
 821                 // try select another widget if current one got hidden
 822                 if (w == GROUP (owner)->current->data)
 823                     group_select_next_widget (GROUP (owner));
 824 
 825                 widget_update_cursor (owner);  // FIXME: unneeded?
 826             }
 827         }
 828         break;
 829 
 830     case WST_DISABLED:
 831         ret = send_message (w, NULL, enable ? MSG_DISABLE : MSG_ENABLE, 0, NULL);
 832         if (ret == MSG_HANDLED && widget_get_state (owner, WST_ACTIVE))
 833             ret = widget_draw (w);
 834         break;
 835 
 836     case WST_FOCUSED:
 837     {
 838         widget_msg_t msg;
 839 
 840         msg = enable ? MSG_FOCUS : MSG_UNFOCUS;
 841         ret = send_message (w, NULL, msg, 0, NULL);
 842         if (ret == MSG_HANDLED && widget_get_state (owner, WST_ACTIVE))
 843         {
 844             widget_draw (w);
 845             // Notify owner that focus was moved from one widget to another
 846             send_message (owner, w, MSG_CHANGED_FOCUS, 0, NULL);
 847         }
 848     }
 849     break;
 850 
 851     default:
 852         break;
 853     }
 854 
 855     return ret;
 856 }
 857 
 858 /* --------------------------------------------------------------------------------------------- */
 859 /**
 860  * Default callback function to destroy widget.
 861  *
 862  * @param w widget
 863  */
 864 
 865 void
 866 widget_default_destroy (Widget *w)
     /* [previous][next][first][last][top][bottom][index][help]  */
 867 {
 868     send_message (w, NULL, MSG_DESTROY, 0, NULL);
 869     g_free (w);
 870 }
 871 
 872 /* --------------------------------------------------------------------------------------------- */
 873 /* get mouse pointer location within widget */
 874 
 875 Gpm_Event
 876 mouse_get_local (const Gpm_Event *global, const Widget *w)
     /* [previous][next][first][last][top][bottom][index][help]  */
 877 {
 878     Gpm_Event local;
 879 
 880     memset (&local, 0, sizeof (local));
 881 
 882     local.buttons = global->buttons;
 883     local.x = global->x - w->rect.x;
 884     local.y = global->y - w->rect.y;
 885     local.type = global->type;
 886 
 887     return local;
 888 }
 889 
 890 /* --------------------------------------------------------------------------------------------- */
 891 
 892 gboolean
 893 mouse_global_in_widget (const Gpm_Event *event, const Widget *w)
     /* [previous][next][first][last][top][bottom][index][help]  */
 894 {
 895     const WRect *r = &w->rect;
 896 
 897     return (event->x > r->x) && (event->y > r->y) && (event->x <= r->x + r->cols)
 898         && (event->y <= r->y + r->lines);
 899 }
 900 
 901 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */