Codebase list dillo / upstream/0.8.6 src / dw_tooltip.c
upstream/0.8.6

Tree @upstream/0.8.6 (Download .tar.gz)

dw_tooltip.c @upstream/0.8.6raw · history · blame

/*
 * A few notes:
 *
 *    - Currently, a window is created every time before it is shown, and
 *      destroyed, before it is hidden. This saves (probably?) some
 *      memory, but can simply be changed. An alternative is having a
 *      global window for all tooltips.
 *
 *    - Tooltips are positioned near the pointer, as opposed to Gtk+
 *      tooltips, which are positioned near the widget.
 *
 * Sebastian
 */

#include <gtk/gtk.h>
#include "dw_tooltip.h"

/* The amount of space around the text, including the border. */
#define PADDING 4

/* The difference between pointer position and upper left corner of the
 * tooltip. */
#define DIFF 10

static gboolean Dw_tooltip_draw (DwTooltip *tooltip);

static DwTooltip *Dw_tooltip_new0 (const char *text, gint ref_count)
{
   DwTooltip *tooltip;

   tooltip = g_new (DwTooltip, 1);
   tooltip->ref_count = ref_count;
   tooltip->window = NULL;
   tooltip->timeout_id = 0;
   tooltip->text = g_strdup (text);
   return tooltip;
}

/*
 * Create a new tooltip, with ref_count set to 1.
 */
DwTooltip* a_Dw_tooltip_new (const gchar *text)
{
   return Dw_tooltip_new0(text, 1);
}

/*
 * Create a new tooltip, with ref_count set to 0. Tyically used for DwStyle.
 */
DwTooltip* a_Dw_tooltip_new_no_ref (const gchar *text)
{
   return Dw_tooltip_new0(text, 0);
}


/*
 * Destroy the tooltip. Used by Dw_tooltip_unref.
 */
void Dw_tooltip_destroy (DwTooltip *tooltip)
{
   a_Dw_tooltip_on_leave (tooltip);
   g_free (tooltip->text);
   g_free (tooltip);
}


/*
 * Call this function if the pointer has entered the widget/word.
 */
void a_Dw_tooltip_on_enter (DwTooltip *tooltip)
{
   a_Dw_tooltip_on_leave (tooltip);
   tooltip->timeout_id = gtk_timeout_add (500, (GtkFunction)Dw_tooltip_draw,
                                          tooltip);
}


/*
 * Call this function if the pointer has left the widget/word.
 */
void a_Dw_tooltip_on_leave (DwTooltip *tooltip)
{
   if (tooltip->timeout_id != 0) {
      gtk_timeout_remove (tooltip->timeout_id);
      tooltip->timeout_id = 0;
   }

   if (tooltip->window != NULL) {
      gtk_widget_destroy (tooltip->window);
      tooltip->window = NULL;
   }
}


/*
 * Call this function if the pointer has moved within the widget/word.
 */
void a_Dw_tooltip_on_motion (DwTooltip *tooltip)
{
   a_Dw_tooltip_on_enter (tooltip);
}

/*
 *  Draw the tooltip. Called as a timeout function.
 */
static gboolean Dw_tooltip_draw (DwTooltip *tooltip)
{
   GtkStyle *style;
   gint px, py, x, y, width, ascent, descent, screen_w, screen_h, ttw, tth;

   gdk_window_get_pointer (NULL, &px, &py, NULL);
   x = px + DIFF;
   y = py + DIFF;

   tooltip->window = gtk_window_new (GTK_WINDOW_POPUP);
   gtk_widget_set_app_paintable (tooltip->window, TRUE);
   gtk_widget_set_name (tooltip->window, "gtk-tooltips");
   gtk_widget_ensure_style (tooltip->window);
   style = tooltip->window->style;
   width = gdk_string_width (style->font, tooltip->text);
   ascent = style->font->ascent;
   descent = style->font->descent;

   ttw = width + 2 * PADDING;
   tth = ascent + descent + 2 * PADDING;
   gtk_widget_set_usize (tooltip->window, ttw, tth);

   screen_w = gdk_screen_width();
   screen_h = gdk_screen_height();

   if (ttw >= screen_w)
      /* If the width of a tooltips does not fit into the screen, put
       * them at x = 0. (Yes, that's far from perfect ...) */
      x = 0;
   else if (x + ttw > screen_w)
      /* If they would otherwise reach out of the screen, move them
       * a bit left. */
      x = screen_w - ttw;

   /* The case that the height of a tooltip of the screen is greater
    * that the screen height is ignored ;-) If the tooltip reaches
    * out of the screen at the bottom, it is displayed *above* the
    * pointer: to process events properly, it is necessary to keep
    * the pointer out of the tooltip. */
   if (y + tth > screen_h)
      y = py - tth - DIFF;

   gtk_widget_popup (tooltip->window, x, y);
   style = tooltip->window->style;
   gtk_paint_flat_box (style, tooltip->window->window,
                       GTK_STATE_NORMAL, GTK_SHADOW_OUT,
                       NULL, GTK_WIDGET (tooltip->window), "tooltip",
                       0, 0, -1, -1);
   gtk_paint_string (style, tooltip->window->window,
                     GTK_STATE_NORMAL,
                     NULL, GTK_WIDGET (tooltip->window), "tooltip",
                     PADDING, ascent + PADDING,
                     tooltip->text);

   tooltip->timeout_id = 0;
   return FALSE;
}