summaryrefslogtreecommitdiffstats
path: root/lib/widget/widget-common.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/widget/widget-common.c')
-rw-r--r--lib/widget/widget-common.c905
1 files changed, 905 insertions, 0 deletions
diff --git a/lib/widget/widget-common.c b/lib/widget/widget-common.c
new file mode 100644
index 0000000..821b7b3
--- /dev/null
+++ b/lib/widget/widget-common.c
@@ -0,0 +1,905 @@
+/*
+ Widgets for the Midnight Commander
+
+ Copyright (C) 1994-2023
+ Free Software Foundation, Inc.
+
+ Authors:
+ Radek Doulik, 1994, 1995
+ Miguel de Icaza, 1994, 1995
+ Jakub Jelinek, 1995
+ Andrej Borsenkow, 1996
+ Norbert Warmuth, 1997
+ Andrew Borodin <aborodin@vmail.ru>, 2009-2022
+
+ This file is part of the Midnight Commander.
+
+ The Midnight Commander is free software: you can redistribute it
+ and/or modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation, either version 3 of the License,
+ or (at your option) any later version.
+
+ The Midnight Commander is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** \file widget-common.c
+ * \brief Source: shared stuff of widgets
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "lib/global.h"
+
+#include "lib/tty/tty.h"
+#include "lib/tty/color.h"
+#include "lib/skin.h"
+#include "lib/strutil.h"
+#include "lib/widget.h"
+
+/*** global variables ****************************************************************************/
+
+/*** file scope macro definitions ****************************************************************/
+
+/*** file scope type declarations ****************************************************************/
+
+/*** forward declarations (file scope functions) *************************************************/
+
+/*** file scope variables ************************************************************************/
+
+/* maximum value of used widget ID */
+static unsigned long widget_id = 0;
+
+/* --------------------------------------------------------------------------------------------- */
+/*** file scope functions ************************************************************************/
+/* --------------------------------------------------------------------------------------------- */
+
+/**
+ * Calc widget ID,
+ * Widget ID is uniq for each widget created during MC session (like PID in OS).
+ *
+ * @return widget ID.
+ */
+static unsigned long
+widget_set_id (void)
+{
+ unsigned long id;
+
+ id = widget_id++;
+ /* TODO IF NEEDED: if id is already used, find next free id. */
+
+ return id;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static cb_ret_t
+widget_default_resize (Widget * w, const WRect * r)
+{
+ if (r == NULL)
+ return MSG_NOT_HANDLED;
+
+ w->rect = *r;
+
+ return MSG_HANDLED;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static void
+widget_do_focus (Widget * w, gboolean enable)
+{
+ if (w != NULL && widget_get_state (WIDGET (w->owner), WST_VISIBLE | WST_FOCUSED))
+ widget_set_state (w, WST_FOCUSED, enable);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/**
+ * Focus specified widget in it's owner.
+ *
+ * @param w widget to be focused.
+ */
+
+static void
+widget_focus (Widget * w)
+{
+ WGroup *g = w->owner;
+
+ if (g == NULL)
+ return;
+
+ if (WIDGET (g->current->data) != w)
+ {
+ widget_do_focus (WIDGET (g->current->data), FALSE);
+ /* Test if focus lost was allowed and focus has really been loose */
+ if (g->current == NULL || !widget_get_state (WIDGET (g->current->data), WST_FOCUSED))
+ {
+ widget_do_focus (w, TRUE);
+ g->current = widget_find (WIDGET (g), w);
+ }
+ }
+ else if (!widget_get_state (w, WST_FOCUSED))
+ widget_do_focus (w, TRUE);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+/**
+ * Put widget on top or bottom of Z-order.
+ */
+static void
+widget_reorder (GList * l, gboolean set_top)
+{
+ WGroup *g = WIDGET (l->data)->owner;
+
+ g->widgets = g_list_remove_link (g->widgets, l);
+ if (set_top)
+ g->widgets = g_list_concat (g->widgets, l);
+ else
+ g->widgets = g_list_concat (l, g->widgets);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static gboolean
+hotkey_cmp (const char *s1, const char *s2)
+{
+ gboolean n1, n2;
+
+ n1 = s1 != NULL;
+ n2 = s2 != NULL;
+
+ if (n1 != n2)
+ return FALSE;
+
+ if (n1 && n2 && strcmp (s1, s2) != 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static void
+widget_default_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event)
+{
+ /* do nothing */
+ (void) w;
+ (void) msg;
+ (void) event;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static const int *
+widget_default_get_colors (const Widget * w)
+{
+ const Widget *owner = CONST_WIDGET (w->owner);
+
+ return (owner == NULL ? NULL : widget_get_colors (owner));
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/*** public functions ****************************************************************************/
+/* --------------------------------------------------------------------------------------------- */
+
+struct hotkey_t
+hotkey_new (const char *text)
+{
+ hotkey_t result;
+ const char *cp, *p;
+
+ if (text == NULL)
+ text = "";
+
+ /* search for '&', that is not on the of text */
+ cp = strchr (text, '&');
+ if (cp != NULL && cp[1] != '\0')
+ {
+ result.start = g_strndup (text, cp - text);
+
+ /* skip '&' */
+ cp++;
+ p = str_cget_next_char (cp);
+ result.hotkey = g_strndup (cp, p - cp);
+
+ cp = p;
+ result.end = g_strdup (cp);
+ }
+ else
+ {
+ result.start = g_strdup (text);
+ result.hotkey = NULL;
+ result.end = NULL;
+ }
+
+ return result;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+void
+hotkey_free (const hotkey_t hotkey)
+{
+ g_free (hotkey.start);
+ g_free (hotkey.hotkey);
+ g_free (hotkey.end);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+int
+hotkey_width (const hotkey_t hotkey)
+{
+ int result;
+
+ result = str_term_width1 (hotkey.start);
+ result += (hotkey.hotkey != NULL) ? str_term_width1 (hotkey.hotkey) : 0;
+ result += (hotkey.end != NULL) ? str_term_width1 (hotkey.end) : 0;
+ return result;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+gboolean
+hotkey_equal (const hotkey_t hotkey1, const hotkey_t hotkey2)
+{
+ /* *INDENT-OFF* */
+ return (strcmp (hotkey1.start, hotkey2.start) == 0) &&
+ hotkey_cmp (hotkey1.hotkey, hotkey2.hotkey) &&
+ hotkey_cmp (hotkey1.end, hotkey2.end);
+ /* *INDENT-ON* */
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+void
+hotkey_draw (const Widget * w, const hotkey_t hotkey, gboolean focused)
+{
+ if (hotkey.start[0] != '\0')
+ {
+ widget_selectcolor (w, focused, FALSE);
+ tty_print_string (hotkey.start);
+ }
+
+ if (hotkey.hotkey != NULL)
+ {
+ widget_selectcolor (w, focused, TRUE);
+ tty_print_string (hotkey.hotkey);
+ }
+
+ if (hotkey.end != NULL)
+ {
+ widget_selectcolor (w, focused, FALSE);
+ tty_print_string (hotkey.end);
+ }
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+char *
+hotkey_get_text (const hotkey_t hotkey)
+{
+ GString *text;
+
+ text = g_string_new (hotkey.start);
+
+ if (hotkey.hotkey != NULL)
+ {
+ g_string_append_c (text, '&');
+ g_string_append (text, hotkey.hotkey);
+ }
+
+ if (hotkey.end != NULL)
+ g_string_append (text, hotkey.end);
+
+ return g_string_free (text, FALSE);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+void
+widget_init (Widget * w, const WRect * r, widget_cb_fn callback, widget_mouse_cb_fn mouse_callback)
+{
+ w->id = widget_set_id ();
+ w->rect = *r;
+ w->pos_flags = WPOS_KEEP_DEFAULT;
+ w->callback = callback;
+
+ w->keymap = NULL;
+ w->ext_keymap = NULL;
+ w->ext_mode = FALSE;
+
+ w->mouse_callback = mouse_callback != NULL ? mouse_callback : widget_default_mouse_callback;
+ w->owner = NULL;
+ w->mouse_handler = mouse_handle_event;
+ w->mouse.forced_capture = FALSE;
+ w->mouse.capture = FALSE;
+ w->mouse.last_msg = MSG_MOUSE_NONE;
+ w->mouse.last_buttons_down = 0;
+
+ w->options = WOP_DEFAULT;
+ w->state = WST_CONSTRUCT | WST_VISIBLE;
+
+ w->make_global = widget_default_make_global;
+ w->make_local = widget_default_make_local;
+
+ w->find = widget_default_find;
+ w->find_by_type = widget_default_find_by_type;
+ w->find_by_id = widget_default_find_by_id;
+
+ w->set_state = widget_default_set_state;
+ w->destroy = widget_default_destroy;
+ w->get_colors = widget_default_get_colors;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+/* Default callback for widgets */
+cb_ret_t
+widget_default_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
+{
+ (void) sender;
+ (void) parm;
+
+ switch (msg)
+ {
+ case MSG_INIT:
+ case MSG_FOCUS:
+ case MSG_UNFOCUS:
+ case MSG_ENABLE:
+ case MSG_DISABLE:
+ case MSG_DRAW:
+ case MSG_DESTROY:
+ case MSG_CURSOR:
+ case MSG_IDLE:
+ return MSG_HANDLED;
+
+ case MSG_RESIZE:
+ return widget_default_resize (w, CONST_RECT (data));
+
+ default:
+ return MSG_NOT_HANDLED;
+ }
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+/**
+ * Apply new options to widget.
+ *
+ * @param w widget
+ * @param options widget option flags to modify. Several flags per call can be modified.
+ * @param enable TRUE if specified options should be added, FALSE if options should be removed
+ */
+void
+widget_set_options (Widget * w, widget_options_t options, gboolean enable)
+{
+ if (enable)
+ w->options |= options;
+ else
+ w->options &= ~options;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+void
+widget_adjust_position (widget_pos_flags_t pos_flags, WRect * r)
+{
+ if ((pos_flags & WPOS_FULLSCREEN) != 0)
+ {
+ r->y = 0;
+ r->x = 0;
+ r->lines = LINES;
+ r->cols = COLS;
+ }
+ else
+ {
+ if ((pos_flags & WPOS_CENTER_HORZ) != 0)
+ r->x = (COLS - r->cols) / 2;
+
+ if ((pos_flags & WPOS_CENTER_VERT) != 0)
+ r->y = (LINES - r->lines) / 2;
+
+ if ((pos_flags & WPOS_TRYUP) != 0)
+ {
+ if (r->y > 3)
+ r->y -= 2;
+ else if (r->y == 3)
+ r->y = 2;
+ }
+ }
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/**
+ * Change widget position and size.
+ *
+ * @param w widget
+ * @param y y coordinate of top-left corner
+ * @param x x coordinate of top-left corner
+ * @param lines width
+ * @param cols height
+ */
+
+void
+widget_set_size (Widget * w, int y, int x, int lines, int cols)
+{
+ WRect r = { y, x, lines, cols };
+
+ send_message (w, NULL, MSG_RESIZE, 0, &r);
+ widget_draw (w);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/**
+ * Change widget position and size.
+ *
+ * @param w widget
+ * @param r WRect object that holds position and size
+ */
+
+void
+widget_set_size_rect (Widget * w, WRect * r)
+{
+ send_message (w, NULL, MSG_RESIZE, 0, r);
+ widget_draw (w);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+void
+widget_selectcolor (const Widget * w, gboolean focused, gboolean hotkey)
+{
+ int color;
+ const int *colors;
+
+ colors = widget_get_colors (w);
+
+ if (widget_get_state (w, WST_DISABLED))
+ color = DISABLED_COLOR;
+ else if (hotkey)
+ color = colors[focused ? DLG_COLOR_HOT_FOCUS : DLG_COLOR_HOT_NORMAL];
+ else
+ color = colors[focused ? DLG_COLOR_FOCUS : DLG_COLOR_NORMAL];
+
+ tty_setcolor (color);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+void
+widget_erase (Widget * w)
+{
+ if (w != NULL)
+ tty_fill_region (w->rect.y, w->rect.x, w->rect.lines, w->rect.cols, ' ');
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+void
+widget_set_visibility (Widget * w, gboolean make_visible)
+{
+ if (widget_get_state (w, WST_VISIBLE) != make_visible)
+ widget_set_state (w, WST_VISIBLE, make_visible);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/**
+ * Check whether widget is active or not.
+ * Widget is active if it's current in the its owner and each owner in the chain is current too.
+ *
+ * @param w the widget
+ *
+ * @return TRUE if the widget is active, FALSE otherwise
+ */
+
+gboolean
+widget_is_active (const void *w)
+{
+ const WGroup *owner;
+
+ /* Is group top? */
+ if (w == top_dlg->data)
+ return TRUE;
+
+ owner = CONST_WIDGET (w)->owner;
+
+ /* Is widget in any group? */
+ if (owner == NULL)
+ return FALSE;
+
+ if (w != owner->current->data)
+ return FALSE;
+
+ return widget_is_active (owner);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+cb_ret_t
+widget_draw (Widget * w)
+{
+ cb_ret_t ret = MSG_NOT_HANDLED;
+
+ if (w != NULL && widget_get_state (w, WST_VISIBLE))
+ {
+ WGroup *g = w->owner;
+
+ if (g != NULL && widget_get_state (WIDGET (g), WST_ACTIVE))
+ ret = w->callback (w, NULL, MSG_DRAW, 0, NULL);
+ }
+
+ return ret;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/**
+ * Replace widget in the dialog.
+ *
+ * @param old_w old widget that need to be replaced
+ * @param new_w new widget that will replace @old_w
+ */
+
+void
+widget_replace (Widget * old_w, Widget * new_w)
+{
+ WGroup *g = old_w->owner;
+ gboolean should_focus = FALSE;
+ GList *holder;
+
+ if (g->widgets == NULL)
+ return;
+
+ if (g->current == NULL)
+ g->current = g->widgets;
+
+ /* locate widget position in the list */
+ if (old_w == g->current->data)
+ holder = g->current;
+ else
+ holder = g_list_find (g->widgets, old_w);
+
+ /* if old widget is focused, we should focus the new one... */
+ if (widget_get_state (old_w, WST_FOCUSED))
+ should_focus = TRUE;
+ /* ...but if new widget isn't selectable, we cannot focus it */
+ if (!widget_get_options (new_w, WOP_SELECTABLE))
+ should_focus = FALSE;
+
+ /* if new widget isn't selectable, select other widget before replace */
+ if (!should_focus)
+ {
+ GList *l;
+
+ for (l = group_get_widget_next_of (holder);
+ !widget_is_focusable (WIDGET (l->data)) && l != holder;
+ l = group_get_widget_next_of (l))
+ ;
+
+ widget_select (WIDGET (l->data));
+ }
+
+ /* replace widget */
+ new_w->owner = g;
+ new_w->id = old_w->id;
+ holder->data = new_w;
+
+ send_message (old_w, NULL, MSG_DESTROY, 0, NULL);
+ send_message (new_w, NULL, MSG_INIT, 0, NULL);
+
+ if (should_focus)
+ widget_select (new_w);
+ else
+ widget_draw (new_w);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+gboolean
+widget_is_focusable (const Widget * w)
+{
+ return (widget_get_options (w, WOP_SELECTABLE) && widget_get_state (w, WST_VISIBLE) &&
+ !widget_get_state (w, WST_DISABLED));
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/**
+ * Select specified widget in it's owner.
+ *
+ * Note: this function (and widget_focus(), which it calls) is a no-op
+ * if the widget is already selected.
+ *
+ * @param w widget to be selected
+ */
+
+void
+widget_select (Widget * w)
+{
+ WGroup *g;
+
+ if (!widget_get_options (w, WOP_SELECTABLE))
+ return;
+
+ g = GROUP (w->owner);
+ if (g != NULL)
+ {
+ if (widget_get_options (w, WOP_TOP_SELECT))
+ {
+ GList *l;
+
+ l = widget_find (WIDGET (g), w);
+ widget_reorder (l, TRUE);
+ }
+
+ widget_focus (w);
+ }
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/**
+ * Set widget at bottom of widget list.
+ */
+
+void
+widget_set_bottom (Widget * w)
+{
+ widget_reorder (widget_find (WIDGET (w->owner), w), FALSE);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/**
+ * Look up key event of widget and translate it to command ID.
+ * @param w widget
+ * @param key key event
+ *
+ * @return command ID binded with @key.
+ */
+
+long
+widget_lookup_key (Widget * w, int key)
+{
+ if (w->ext_mode)
+ {
+ w->ext_mode = FALSE;
+ return keybind_lookup_keymap_command (w->ext_keymap, key);
+ }
+
+ return keybind_lookup_keymap_command (w->keymap, key);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+/**
+ * Default widget callback to convert widget coordinates from local (relative to owner) to global
+ * (relative to screen).
+ *
+ * @param w widget
+ * @delta offset for top-left corner coordinates. Used for child widgets of WGroup
+ */
+
+void
+widget_default_make_global (Widget * w, const WRect * delta)
+{
+ if (delta != NULL)
+ rect_move (&w->rect, delta->y, delta->x);
+ else if (w->owner != NULL)
+ rect_move (&w->rect, WIDGET (w->owner)->rect.y, WIDGET (w->owner)->rect.x);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+/**
+ * Default widget callback to convert widget coordinates from global (relative to screen) to local
+ * (relative to owner).
+ *
+ * @param w widget
+ * @delta offset for top-left corner coordinates. Used for child widgets of WGroup
+ */
+
+void
+widget_default_make_local (Widget * w, const WRect * delta)
+{
+ if (delta != NULL)
+ rect_move (&w->rect, -delta->y, -delta->x);
+ else if (w->owner != NULL)
+ rect_move (&w->rect, -WIDGET (w->owner)->rect.y, -WIDGET (w->owner)->rect.x);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/**
+ * Default callback function to find widget.
+ *
+ * @param w widget
+ * @param what widget to find
+ *
+ * @return holder of @what if widget is @what, NULL otherwise
+ */
+
+GList *
+widget_default_find (const Widget * w, const Widget * what)
+{
+ return (w != what
+ || w->owner == NULL) ? NULL : g_list_find (CONST_GROUP (w->owner)->widgets, what);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+/**
+ * Default callback function to find widget by widget type using widget callback.
+ *
+ * @param w widget
+ * @param cb widget callback
+ *
+ * @return @w if widget callback is @cb, NULL otherwise
+ */
+
+Widget *
+widget_default_find_by_type (const Widget * w, widget_cb_fn cb)
+{
+ return (w->callback == cb ? WIDGET (w) : NULL);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/**
+ * Default callback function to find widget by widget ID.
+ *
+ * @param w widget
+ * @param id widget ID
+ *
+ * @return @w if widget id is equal to @id, NULL otherwise
+ */
+
+Widget *
+widget_default_find_by_id (const Widget * w, unsigned long id)
+{
+ return (w->id == id ? WIDGET (w) : NULL);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+/**
+ * Default callback function to modify state of widget.
+ *
+ * @param w widget
+ * @param state widget state flag to modify
+ * @param enable specifies whether to turn the flag on (TRUE) or off (FALSE).
+ * Only one flag per call can be modified.
+ * @return MSG_HANDLED if set was handled successfully, MSG_NOT_HANDLED otherwise.
+ */
+
+cb_ret_t
+widget_default_set_state (Widget * w, widget_state_t state, gboolean enable)
+{
+ gboolean ret = MSG_HANDLED;
+ Widget *owner = WIDGET (GROUP (w->owner));
+
+ if (enable)
+ w->state |= state;
+ else
+ w->state &= ~state;
+
+ if (enable)
+ {
+ /* exclusive bits */
+ switch (state)
+ {
+ case WST_CONSTRUCT:
+ w->state &= ~(WST_ACTIVE | WST_SUSPENDED | WST_CLOSED);
+ break;
+ case WST_ACTIVE:
+ w->state &= ~(WST_CONSTRUCT | WST_SUSPENDED | WST_CLOSED);
+ break;
+ case WST_SUSPENDED:
+ w->state &= ~(WST_CONSTRUCT | WST_ACTIVE | WST_CLOSED);
+ break;
+ case WST_CLOSED:
+ w->state &= ~(WST_CONSTRUCT | WST_ACTIVE | WST_SUSPENDED);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (owner == NULL)
+ return MSG_NOT_HANDLED;
+
+ switch (state)
+ {
+ case WST_VISIBLE:
+ if (widget_get_state (owner, WST_ACTIVE))
+ {
+ /* redraw owner to show/hide widget */
+ widget_draw (owner);
+
+ if (!enable)
+ {
+ /* try select another widget if current one got hidden */
+ if (w == GROUP (owner)->current->data)
+ group_select_next_widget (GROUP (owner));
+
+ widget_update_cursor (owner); /* FIXME: unneeded? */
+ }
+ }
+ break;
+
+ case WST_DISABLED:
+ ret = send_message (w, NULL, enable ? MSG_DISABLE : MSG_ENABLE, 0, NULL);
+ if (ret == MSG_HANDLED && widget_get_state (owner, WST_ACTIVE))
+ ret = widget_draw (w);
+ break;
+
+ case WST_FOCUSED:
+ {
+ widget_msg_t msg;
+
+ msg = enable ? MSG_FOCUS : MSG_UNFOCUS;
+ ret = send_message (w, NULL, msg, 0, NULL);
+ if (ret == MSG_HANDLED && widget_get_state (owner, WST_ACTIVE))
+ {
+ widget_draw (w);
+ /* Notify owner that focus was moved from one widget to another */
+ send_message (owner, w, MSG_CHANGED_FOCUS, 0, NULL);
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/**
+ * Default callback function to destroy widget.
+ *
+ * @param w widget
+ */
+
+void
+widget_default_destroy (Widget * w)
+{
+ send_message (w, NULL, MSG_DESTROY, 0, NULL);
+ g_free (w);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/* get mouse pointer location within widget */
+
+Gpm_Event
+mouse_get_local (const Gpm_Event * global, const Widget * w)
+{
+ Gpm_Event local;
+
+ memset (&local, 0, sizeof (local));
+
+ local.buttons = global->buttons;
+ local.x = global->x - w->rect.x;
+ local.y = global->y - w->rect.y;
+ local.type = global->type;
+
+ return local;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+gboolean
+mouse_global_in_widget (const Gpm_Event * event, const Widget * w)
+{
+ const WRect *r = &w->rect;
+
+ return (event->x > r->x) && (event->y > r->y) && (event->x <= r->x + r->cols)
+ && (event->y <= r->y + r->lines);
+}
+
+/* --------------------------------------------------------------------------------------------- */