root/lib/widget/widget-common.c

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

DEFINITIONS

This source file includes following definitions.
  1. widget_do_focus
  2. widget_focus
  3. widget_reorder
  4. hotkey_cmp
  5. hotkey_new
  6. hotkey_free
  7. hotkey_width
  8. hotkey_equal
  9. hotkey_draw
  10. hotkey_get_text
  11. widget_init
  12. widget_destroy
  13. widget_default_callback
  14. widget_set_options
  15. widget_set_state
  16. widget_adjust_position
  17. widget_set_size
  18. widget_selectcolor
  19. widget_erase
  20. widget_is_active
  21. widget_draw
  22. widget_replace
  23. widget_select
  24. widget_set_bottom
  25. widget_overlapped
  26. mouse_get_local
  27. mouse_global_in_widget

   1 /*
   2    Widgets for the Midnight Commander
   3 
   4    Copyright (C) 1994-2019
   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, 2010, 2011, 2012, 2013
  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 <http://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 /*** file scope variables ************************************************************************/
  55 
  56 /* --------------------------------------------------------------------------------------------- */
  57 /*** file scope functions ************************************************************************/
  58 /* --------------------------------------------------------------------------------------------- */
  59 
  60 static void
  61 widget_do_focus (Widget * w, gboolean enable)
     /* [previous][next][first][last][top][bottom][index][help]  */
  62 {
  63     if (w != NULL && widget_get_state (WIDGET (w->owner), WST_FOCUSED))
  64         widget_set_state (w, WST_FOCUSED, enable);
  65 }
  66 
  67 /* --------------------------------------------------------------------------------------------- */
  68 /**
  69  * Focus specified widget in it's owner.
  70  *
  71  * @param w widget to be focused.
  72  */
  73 
  74 static void
  75 widget_focus (Widget * w)
     /* [previous][next][first][last][top][bottom][index][help]  */
  76 {
  77     WDialog *h = DIALOG (w->owner);
  78 
  79     if (h == NULL)
  80         return;
  81 
  82     if (WIDGET (h->current->data) != w)
  83     {
  84         widget_do_focus (WIDGET (h->current->data), FALSE);
  85         /* Test if focus lost was allowed and focus has really been loose */
  86         if (h->current == NULL || !widget_get_state (WIDGET (h->current->data), WST_FOCUSED))
  87         {
  88             widget_do_focus (w, TRUE);
  89             h->current = dlg_find (h, w);
  90         }
  91     }
  92     else if (!widget_get_state (w, WST_FOCUSED))
  93         widget_do_focus (w, TRUE);
  94 }
  95 
  96 /* --------------------------------------------------------------------------------------------- */
  97 
  98 /**
  99  * Put widget on top or bottom of Z-order.
 100  */
 101 static void
 102 widget_reorder (GList * l, gboolean set_top)
     /* [previous][next][first][last][top][bottom][index][help]  */
 103 {
 104     WDialog *h = WIDGET (l->data)->owner;
 105 
 106     h->widgets = g_list_remove_link (h->widgets, l);
 107     if (set_top)
 108         h->widgets = g_list_concat (h->widgets, l);
 109     else
 110         h->widgets = g_list_concat (l, h->widgets);
 111 }
 112 
 113 /* --------------------------------------------------------------------------------------------- */
 114 
 115 static gboolean
 116 hotkey_cmp (const char *s1, const char *s2)
     /* [previous][next][first][last][top][bottom][index][help]  */
 117 {
 118     gboolean n1, n2;
 119 
 120     n1 = s1 != NULL;
 121     n2 = s2 != NULL;
 122 
 123     if (n1 != n2)
 124         return FALSE;
 125 
 126     if (n1 && n2 && strcmp (s1, s2) != 0)
 127         return FALSE;
 128 
 129     return TRUE;
 130 }
 131 
 132 /* --------------------------------------------------------------------------------------------- */
 133 /*** public functions ****************************************************************************/
 134 /* --------------------------------------------------------------------------------------------- */
 135 
 136 struct hotkey_t
 137 hotkey_new (const char *text)
     /* [previous][next][first][last][top][bottom][index][help]  */
 138 {
 139     hotkey_t result;
 140     const char *cp, *p;
 141 
 142     if (text == NULL)
 143         text = "";
 144 
 145     /* search for '&', that is not on the of text */
 146     cp = strchr (text, '&');
 147     if (cp != NULL && cp[1] != '\0')
 148     {
 149         result.start = g_strndup (text, cp - text);
 150 
 151         /* skip '&' */
 152         cp++;
 153         p = str_cget_next_char (cp);
 154         result.hotkey = g_strndup (cp, p - cp);
 155 
 156         cp = p;
 157         result.end = g_strdup (cp);
 158     }
 159     else
 160     {
 161         result.start = g_strdup (text);
 162         result.hotkey = NULL;
 163         result.end = NULL;
 164     }
 165 
 166     return result;
 167 }
 168 
 169 /* --------------------------------------------------------------------------------------------- */
 170 
 171 void
 172 hotkey_free (const hotkey_t hotkey)
     /* [previous][next][first][last][top][bottom][index][help]  */
 173 {
 174     g_free (hotkey.start);
 175     g_free (hotkey.hotkey);
 176     g_free (hotkey.end);
 177 }
 178 
 179 /* --------------------------------------------------------------------------------------------- */
 180 
 181 int
 182 hotkey_width (const hotkey_t hotkey)
     /* [previous][next][first][last][top][bottom][index][help]  */
 183 {
 184     int result;
 185 
 186     result = str_term_width1 (hotkey.start);
 187     result += (hotkey.hotkey != NULL) ? str_term_width1 (hotkey.hotkey) : 0;
 188     result += (hotkey.end != NULL) ? str_term_width1 (hotkey.end) : 0;
 189     return result;
 190 }
 191 
 192 /* --------------------------------------------------------------------------------------------- */
 193 
 194 gboolean
 195 hotkey_equal (const hotkey_t hotkey1, const hotkey_t hotkey2)
     /* [previous][next][first][last][top][bottom][index][help]  */
 196 {
 197     /* *INDENT-OFF* */
 198     return (strcmp (hotkey1.start, hotkey2.start) == 0) &&
 199            hotkey_cmp (hotkey1.hotkey, hotkey2.hotkey) &&
 200            hotkey_cmp (hotkey1.end, hotkey2.end);
 201     /* *INDENT-ON* */
 202 }
 203 
 204 /* --------------------------------------------------------------------------------------------- */
 205 
 206 void
 207 hotkey_draw (Widget * w, const hotkey_t hotkey, gboolean focused)
     /* [previous][next][first][last][top][bottom][index][help]  */
 208 {
 209     if (hotkey.start[0] != '\0')
 210     {
 211         widget_selectcolor (w, focused, FALSE);
 212         tty_print_string (hotkey.start);
 213     }
 214 
 215     if (hotkey.hotkey != NULL)
 216     {
 217         widget_selectcolor (w, focused, TRUE);
 218         tty_print_string (hotkey.hotkey);
 219     }
 220 
 221     if (hotkey.end != NULL)
 222     {
 223         widget_selectcolor (w, focused, FALSE);
 224         tty_print_string (hotkey.end);
 225     }
 226 }
 227 
 228 /* --------------------------------------------------------------------------------------------- */
 229 
 230 char *
 231 hotkey_get_text (const hotkey_t hotkey)
     /* [previous][next][first][last][top][bottom][index][help]  */
 232 {
 233     GString *text;
 234 
 235     text = g_string_new (hotkey.start);
 236 
 237     if (hotkey.hotkey != NULL)
 238     {
 239         g_string_append_c (text, '&');
 240         g_string_append (text, hotkey.hotkey);
 241     }
 242 
 243     if (hotkey.end != NULL)
 244         g_string_append (text, hotkey.end);
 245 
 246     return g_string_free (text, FALSE);
 247 }
 248 
 249 /* --------------------------------------------------------------------------------------------- */
 250 
 251 void
 252 widget_init (Widget * w, int y, int x, int lines, int cols,
     /* [previous][next][first][last][top][bottom][index][help]  */
 253              widget_cb_fn callback, widget_mouse_cb_fn mouse_callback)
 254 {
 255     w->x = x;
 256     w->y = y;
 257     w->cols = cols;
 258     w->lines = lines;
 259     w->pos_flags = WPOS_KEEP_DEFAULT;
 260     w->callback = callback;
 261     w->mouse_callback = mouse_callback;
 262     w->owner = NULL;
 263     w->mouse.forced_capture = FALSE;
 264     w->mouse.capture = FALSE;
 265     w->mouse.last_msg = MSG_MOUSE_NONE;
 266     w->mouse.last_buttons_down = 0;
 267 
 268     w->options = WOP_DEFAULT;
 269     w->state = WST_DEFAULT;
 270 }
 271 
 272 /* --------------------------------------------------------------------------------------------- */
 273 
 274 void
 275 widget_destroy (Widget * w)
     /* [previous][next][first][last][top][bottom][index][help]  */
 276 {
 277     send_message (w, NULL, MSG_DESTROY, 0, NULL);
 278     g_free (w);
 279 }
 280 
 281 /* --------------------------------------------------------------------------------------------- */
 282 
 283 /* Default callback for widgets */
 284 cb_ret_t
 285 widget_default_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 286 {
 287     (void) w;
 288     (void) sender;
 289     (void) parm;
 290     (void) data;
 291 
 292     switch (msg)
 293     {
 294     case MSG_INIT:
 295     case MSG_FOCUS:
 296     case MSG_UNFOCUS:
 297     case MSG_ENABLE:
 298     case MSG_DISABLE:
 299     case MSG_DRAW:
 300     case MSG_DESTROY:
 301     case MSG_CURSOR:
 302     case MSG_IDLE:
 303         return MSG_HANDLED;
 304 
 305     default:
 306         return MSG_NOT_HANDLED;
 307     }
 308 }
 309 
 310 /* --------------------------------------------------------------------------------------------- */
 311 
 312 /**
 313  * Apply new options to widget.
 314  *
 315  * @param w       widget
 316  * @param options widget option flags to modify. Several flags per call can be modified.
 317  * @param enable  TRUE if specified options should be added, FALSE if options should be removed
 318  */
 319 void
 320 widget_set_options (Widget * w, widget_options_t options, gboolean enable)
     /* [previous][next][first][last][top][bottom][index][help]  */
 321 {
 322     if (enable)
 323         w->options |= options;
 324     else
 325         w->options &= ~options;
 326 }
 327 
 328 /* --------------------------------------------------------------------------------------------- */
 329 
 330 /**
 331  * Modify state of widget.
 332  *
 333  * @param w      widget
 334  * @param state  widget state flag to modify
 335  * @param enable specifies whether to turn the flag on (TRUE) or off (FALSE).
 336  *               Only one flag per call can be modified.
 337  * @return       MSG_HANDLED if set was handled successfully, MSG_NOT_HANDLED otherwise.
 338  */
 339 cb_ret_t
 340 widget_set_state (Widget * w, widget_state_t state, gboolean enable)
     /* [previous][next][first][last][top][bottom][index][help]  */
 341 {
 342     gboolean ret = MSG_HANDLED;
 343 
 344     if (enable)
 345         w->state |= state;
 346     else
 347         w->state &= ~state;
 348 
 349     if (enable)
 350     {
 351         /* exclusive bits */
 352         if ((state & WST_CONSTRUCT) != 0)
 353             w->state &= ~(WST_ACTIVE | WST_SUSPENDED | WST_CLOSED);
 354         else if ((state & WST_ACTIVE) != 0)
 355             w->state &= ~(WST_CONSTRUCT | WST_SUSPENDED | WST_CLOSED);
 356         else if ((state & WST_SUSPENDED) != 0)
 357             w->state &= ~(WST_CONSTRUCT | WST_ACTIVE | WST_CLOSED);
 358         else if ((state & WST_CLOSED) != 0)
 359             w->state &= ~(WST_CONSTRUCT | WST_ACTIVE | WST_SUSPENDED);
 360     }
 361 
 362     if (w->owner == NULL)
 363         return MSG_NOT_HANDLED;
 364 
 365     switch (state)
 366     {
 367     case WST_DISABLED:
 368         ret = send_message (w, NULL, enable ? MSG_DISABLE : MSG_ENABLE, 0, NULL);
 369         if (ret == MSG_HANDLED && widget_get_state (WIDGET (w->owner), WST_ACTIVE))
 370             ret = send_message (w, NULL, MSG_DRAW, 0, NULL);
 371         break;
 372 
 373     case WST_FOCUSED:
 374         {
 375             widget_msg_t msg;
 376 
 377             msg = enable ? MSG_FOCUS : MSG_UNFOCUS;
 378             ret = send_message (w, NULL, msg, 0, NULL);
 379             if (ret == MSG_HANDLED && widget_get_state (WIDGET (w->owner), WST_ACTIVE))
 380             {
 381                 send_message (w, NULL, MSG_DRAW, 0, NULL);
 382                 /* Notify owner that focus was moved from one widget to another */
 383                 send_message (w->owner, w, MSG_CHANGED_FOCUS, 0, NULL);
 384             }
 385         }
 386         break;
 387 
 388     default:
 389         break;
 390     }
 391 
 392     return ret;
 393 }
 394 
 395 /* --------------------------------------------------------------------------------------------- */
 396 
 397 void
 398 widget_adjust_position (widget_pos_flags_t pos_flags, int *y, int *x, int *lines, int *cols)
     /* [previous][next][first][last][top][bottom][index][help]  */
 399 {
 400     if ((pos_flags & WPOS_FULLSCREEN) != 0)
 401     {
 402         *y = 0;
 403         *x = 0;
 404         *lines = LINES;
 405         *cols = COLS;
 406     }
 407     else
 408     {
 409         if ((pos_flags & WPOS_CENTER_HORZ) != 0)
 410             *x = (COLS - *cols) / 2;
 411 
 412         if ((pos_flags & WPOS_CENTER_VERT) != 0)
 413             *y = (LINES - *lines) / 2;
 414 
 415         if ((pos_flags & WPOS_TRYUP) != 0)
 416         {
 417             if (*y > 3)
 418                 *y -= 2;
 419             else if (*y == 3)
 420                 *y = 2;
 421         }
 422     }
 423 }
 424 
 425 /* --------------------------------------------------------------------------------------------- */
 426 
 427 void
 428 widget_set_size (Widget * widget, int y, int x, int lines, int cols)
     /* [previous][next][first][last][top][bottom][index][help]  */
 429 {
 430     widget->x = x;
 431     widget->y = y;
 432     widget->cols = cols;
 433     widget->lines = lines;
 434     send_message (widget, NULL, MSG_RESIZE, 0, NULL);
 435     if (widget->owner != NULL && widget_get_state (WIDGET (widget->owner), WST_ACTIVE))
 436         send_message (widget, NULL, MSG_DRAW, 0, NULL);
 437 }
 438 
 439 /* --------------------------------------------------------------------------------------------- */
 440 
 441 void
 442 widget_selectcolor (Widget * w, gboolean focused, gboolean hotkey)
     /* [previous][next][first][last][top][bottom][index][help]  */
 443 {
 444     WDialog *h = w->owner;
 445     int color;
 446 
 447     if (widget_get_state (w, WST_DISABLED))
 448         color = DISABLED_COLOR;
 449     else if (hotkey)
 450     {
 451         if (focused)
 452             color = h->color[DLG_COLOR_HOT_FOCUS];
 453         else
 454             color = h->color[DLG_COLOR_HOT_NORMAL];
 455     }
 456     else
 457     {
 458         if (focused)
 459             color = h->color[DLG_COLOR_FOCUS];
 460         else
 461             color = h->color[DLG_COLOR_NORMAL];
 462     }
 463 
 464     tty_setcolor (color);
 465 }
 466 
 467 /* --------------------------------------------------------------------------------------------- */
 468 
 469 void
 470 widget_erase (Widget * w)
     /* [previous][next][first][last][top][bottom][index][help]  */
 471 {
 472     if (w != NULL)
 473         tty_fill_region (w->y, w->x, w->lines, w->cols, ' ');
 474 }
 475 
 476 /* --------------------------------------------------------------------------------------------- */
 477 /**
 478   * Check whether widget is active or not.
 479   * @param w the widget
 480   *
 481   * @return TRUE if the widget is active, FALSE otherwise
 482   */
 483 
 484 gboolean
 485 widget_is_active (const void *w)
     /* [previous][next][first][last][top][bottom][index][help]  */
 486 {
 487     return (w == CONST_WIDGET (w)->owner->current->data);
 488 }
 489 
 490 /* --------------------------------------------------------------------------------------------- */
 491 
 492 void
 493 widget_draw (Widget * w)
     /* [previous][next][first][last][top][bottom][index][help]  */
 494 {
 495     if (w != NULL)
 496     {
 497         WDialog *h = w->owner;
 498 
 499         if (h != NULL && widget_get_state (WIDGET (h), WST_ACTIVE))
 500             w->callback (w, NULL, MSG_DRAW, 0, NULL);
 501     }
 502 }
 503 
 504 /* --------------------------------------------------------------------------------------------- */
 505 /**
 506   * Replace widget in the dialog.
 507   *
 508   * @param old_w old widget that need to be replaced
 509   * @param new_w new widget that will replace @old_w
 510   */
 511 
 512 void
 513 widget_replace (Widget * old_w, Widget * new_w)
     /* [previous][next][first][last][top][bottom][index][help]  */
 514 {
 515     WDialog *h = old_w->owner;
 516     gboolean should_focus = FALSE;
 517     GList *holder;
 518 
 519     if (h->widgets == NULL)
 520         return;
 521 
 522     if (h->current == NULL)
 523         h->current = h->widgets;
 524 
 525     /* locate widget position in the list */
 526     if (old_w == h->current->data)
 527         holder = h->current;
 528     else
 529         holder = g_list_find (h->widgets, old_w);
 530 
 531     /* if old widget is focused, we should focus the new one... */
 532     if (widget_get_state (old_w, WST_FOCUSED))
 533         should_focus = TRUE;
 534     /* ...but if new widget isn't selectable, we cannot focus it */
 535     if (!widget_get_options (new_w, WOP_SELECTABLE))
 536         should_focus = FALSE;
 537 
 538     /* if new widget isn't selectable, select other widget before replace */
 539     if (!should_focus)
 540     {
 541         GList *l;
 542 
 543         for (l = dlg_get_widget_next_of (holder);
 544              !widget_get_options (WIDGET (l->data), WOP_SELECTABLE)
 545              && !widget_get_state (WIDGET (l->data), WST_DISABLED); l = dlg_get_widget_next_of (l))
 546             ;
 547 
 548         widget_select (WIDGET (l->data));
 549     }
 550 
 551     /* replace widget */
 552     new_w->owner = h;
 553     new_w->id = old_w->id;
 554     holder->data = new_w;
 555 
 556     send_message (old_w, NULL, MSG_DESTROY, 0, NULL);
 557     send_message (new_w, NULL, MSG_INIT, 0, NULL);
 558 
 559     if (should_focus)
 560         widget_select (new_w);
 561     else
 562         widget_draw (new_w);
 563 }
 564 
 565 /* --------------------------------------------------------------------------------------------- */
 566 /**
 567  * Select specified widget in it's owner.
 568  *
 569  * Note: this function (and widget_focus(), which it calls) is a no-op
 570  * if the widget is already selected.
 571  *
 572  * @param w widget to be selected
 573  */
 574 
 575 void
 576 widget_select (Widget * w)
     /* [previous][next][first][last][top][bottom][index][help]  */
 577 {
 578     WDialog *h;
 579 
 580     if (!widget_get_options (w, WOP_SELECTABLE))
 581         return;
 582 
 583     h = w->owner;
 584     if (h != NULL)
 585     {
 586         if (widget_get_options (w, WOP_TOP_SELECT))
 587         {
 588             GList *l;
 589 
 590             l = dlg_find (h, w);
 591             widget_reorder (l, TRUE);
 592         }
 593 
 594         widget_focus (w);
 595     }
 596 }
 597 
 598 /* --------------------------------------------------------------------------------------------- */
 599 /**
 600  * Set widget at bottom of widget list.
 601  */
 602 
 603 void
 604 widget_set_bottom (Widget * w)
     /* [previous][next][first][last][top][bottom][index][help]  */
 605 {
 606     widget_reorder (dlg_find (w->owner, w), FALSE);
 607 }
 608 
 609 /* --------------------------------------------------------------------------------------------- */
 610 /**
 611   * Check whether two widgets are overlapped or not.
 612   * @param a 1st widget
 613   * @param b 2nd widget
 614   *
 615   * @return TRUE if widgets are overlapped, FALSE otherwise.
 616   */
 617 
 618 gboolean
 619 widget_overlapped (const Widget * a, const Widget * b)
     /* [previous][next][first][last][top][bottom][index][help]  */
 620 {
 621     return !((b->x >= a->x + a->cols)
 622              || (a->x >= b->x + b->cols) || (b->y >= a->y + a->lines) || (a->y >= b->y + b->lines));
 623 }
 624 
 625 /* --------------------------------------------------------------------------------------------- */
 626 /* get mouse pointer location within widget */
 627 
 628 Gpm_Event
 629 mouse_get_local (const Gpm_Event * global, const Widget * w)
     /* [previous][next][first][last][top][bottom][index][help]  */
 630 {
 631     Gpm_Event local;
 632 
 633     local.buttons = global->buttons;
 634 #ifdef HAVE_LIBGPM
 635     local.clicks = 0;
 636     local.margin = 0;
 637     local.modifiers = 0;
 638     local.vc = 0;
 639 #endif
 640     local.x = global->x - w->x;
 641     local.y = global->y - w->y;
 642     local.type = global->type;
 643 
 644     return local;
 645 }
 646 
 647 /* --------------------------------------------------------------------------------------------- */
 648 
 649 gboolean
 650 mouse_global_in_widget (const Gpm_Event * event, const Widget * w)
     /* [previous][next][first][last][top][bottom][index][help]  */
 651 {
 652     return (event->x > w->x) && (event->y > w->y) && (event->x <= w->x + w->cols)
 653         && (event->y <= w->y + w->lines);
 654 }
 655 
 656 /* --------------------------------------------------------------------------------------------- */

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