gtktooltips.c 21.4 KB
Newer Older
Elliot Lee's avatar
Elliot Lee committed
1 2 3 4
/* GTK - The GIMP Toolkit
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
Elliot Lee's avatar
Elliot Lee committed
6 7 8 9 10 11
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
12
 * Lesser General Public License for more details.
Elliot Lee's avatar
Elliot Lee committed
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
15 16 17
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
Elliot Lee's avatar
Elliot Lee committed
18
 */
19 20

/*
21
 * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22 23 24 25 26
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
 */

27
#include <config.h>
Elliot Lee's avatar
Elliot Lee committed
28 29
#include <stdlib.h>
#include <string.h>
30
#include <stdio.h>
Elliot Lee's avatar
Elliot Lee committed
31

32
#include "gtklabel.h"
Elliot Lee's avatar
Elliot Lee committed
33
#include "gtkmain.h"
34
#include "gtkmenuitem.h"
35
#include "gtkprivate.h"
Elliot Lee's avatar
Elliot Lee committed
36
#include "gtkwidget.h"
Owen Taylor's avatar
Owen Taylor committed
37
#include "gtkwindow.h"
Elliot Lee's avatar
Elliot Lee committed
38 39
#include "gtkstyle.h"
#include "gtktooltips.h"
Matthias Clasen's avatar
Matthias Clasen committed
40
#include "gtkintl.h"
41
#include "gtkalias.h"
Elliot Lee's avatar
Elliot Lee committed
42 43 44


#define DEFAULT_DELAY 500           /* Default delay in ms */
45 46 47 48 49 50
#define STICKY_DELAY 0              /* Delay before popping up next tip
                                     * if we're sticky
                                     */
#define STICKY_REVERT_DELAY 1000    /* Delay before sticky tooltips revert
				     * to normal
                                     */
Elliot Lee's avatar
Elliot Lee committed
51

52 53 54
static void gtk_tooltips_class_init        (GtkTooltipsClass *klass);
static void gtk_tooltips_init              (GtkTooltips      *tooltips);
static void gtk_tooltips_destroy           (GtkObject        *object);
Elliot Lee's avatar
Elliot Lee committed
55

56
static void gtk_tooltips_event_handler     (GtkWidget   *widget,
Elliot Lee's avatar
Elliot Lee committed
57 58 59 60 61 62 63 64
                                            GdkEvent    *event);
static void gtk_tooltips_widget_unmap      (GtkWidget   *widget,
                                            gpointer     data);
static void gtk_tooltips_widget_remove     (GtkWidget   *widget,
                                            gpointer     data);
static void gtk_tooltips_set_active_widget (GtkTooltips *tooltips,
                                            GtkWidget   *widget);
static gint gtk_tooltips_timeout           (gpointer     data);
65

66
static gint gtk_tooltips_paint_window      (GtkTooltips *tooltips);
Elliot Lee's avatar
Elliot Lee committed
67
static void gtk_tooltips_draw_tips         (GtkTooltips *tooltips);
68
static void gtk_tooltips_unset_tip_window  (GtkTooltips *tooltips);
Elliot Lee's avatar
Elliot Lee committed
69

70 71
static gboolean get_keyboard_mode          (GtkWidget   *widget);

Havoc Pennington's avatar
Delete.  
Havoc Pennington committed
72
static GtkObjectClass *parent_class;
73 74
static const gchar  tooltips_data_key[] = "_GtkTooltipsData";
static const gchar  tooltips_info_key[] = "_GtkTooltipsInfo";
Elliot Lee's avatar
Elliot Lee committed
75

Manish Singh's avatar
Manish Singh committed
76
GType
77
gtk_tooltips_get_type (void)
78
{
Manish Singh's avatar
Manish Singh committed
79
  static GType tooltips_type = 0;
Elliot Lee's avatar
Elliot Lee committed
80

81
  if (!tooltips_type)
Elliot Lee's avatar
Elliot Lee committed
82
    {
Manish Singh's avatar
Manish Singh committed
83
      static const GTypeInfo tooltips_info =
84 85
      {
	sizeof (GtkTooltipsClass),
Manish Singh's avatar
Manish Singh committed
86 87 88 89 90 91 92 93
	NULL,		/* base_init */
	NULL,		/* base_finalize */
	(GClassInitFunc) gtk_tooltips_class_init,
	NULL,		/* class_finalize */
	NULL,		/* class_data */
	sizeof (GtkTooltips),
	0,		/* n_preallocs */
	(GInstanceInitFunc) gtk_tooltips_init,
94 95
      };

Matthias Clasen's avatar
Matthias Clasen committed
96
      tooltips_type = g_type_register_static (GTK_TYPE_OBJECT, I_("GtkTooltips"),
Manish Singh's avatar
Manish Singh committed
97
					      &tooltips_info, 0);
Elliot Lee's avatar
Elliot Lee committed
98 99
    }

100
  return tooltips_type;
Elliot Lee's avatar
Elliot Lee committed
101 102
}

103 104
static void
gtk_tooltips_class_init (GtkTooltipsClass *class)
Elliot Lee's avatar
Elliot Lee committed
105
{
106 107 108
  GtkObjectClass *object_class;

  object_class = (GtkObjectClass*) class;
Manish Singh's avatar
Manish Singh committed
109 110

  parent_class = g_type_class_peek_parent (class);
111 112

  object_class->destroy = gtk_tooltips_destroy;
Elliot Lee's avatar
Elliot Lee committed
113 114
}

115 116
static void
gtk_tooltips_init (GtkTooltips *tooltips)
Elliot Lee's avatar
Elliot Lee committed
117
{
118 119 120 121 122 123 124
  tooltips->tip_window = NULL;
  tooltips->active_tips_data = NULL;
  tooltips->tips_data_list = NULL;
  
  tooltips->delay = DEFAULT_DELAY;
  tooltips->enabled = TRUE;
  tooltips->timer_tag = 0;
125 126 127
  tooltips->use_sticky_delay = FALSE;
  tooltips->last_popdown.tv_sec = -1;
  tooltips->last_popdown.tv_usec = -1;
128 129 130
}

GtkTooltips *
131
gtk_tooltips_new (void)
132
{
Manish Singh's avatar
Manish Singh committed
133
  return g_object_new (GTK_TYPE_TOOLTIPS, NULL);
Elliot Lee's avatar
Elliot Lee committed
134 135 136
}

static void
137
gtk_tooltips_destroy_data (GtkTooltipsData *tooltipsdata)
Elliot Lee's avatar
Elliot Lee committed
138
{
139 140
  g_free (tooltipsdata->tip_text);
  g_free (tooltipsdata->tip_private);
Manish Singh's avatar
Manish Singh committed
141 142 143 144 145 146 147 148 149 150 151

  g_signal_handlers_disconnect_by_func (tooltipsdata->widget,
					gtk_tooltips_event_handler,
					tooltipsdata);
  g_signal_handlers_disconnect_by_func (tooltipsdata->widget,
					gtk_tooltips_widget_unmap,
					tooltipsdata);
  g_signal_handlers_disconnect_by_func (tooltipsdata->widget,
					gtk_tooltips_widget_remove,
					tooltipsdata);

Matthias Clasen's avatar
Matthias Clasen committed
152
  g_object_set_data (G_OBJECT (tooltipsdata->widget), I_(tooltips_data_key), NULL);
Manish Singh's avatar
Manish Singh committed
153
  g_object_unref (tooltipsdata->widget);
Elliot Lee's avatar
Elliot Lee committed
154 155 156
  g_free (tooltipsdata);
}

157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
static void
tip_window_display_closed (GdkDisplay  *display,
			   gboolean     was_error,
			   GtkTooltips *tooltips)
{
  gtk_tooltips_unset_tip_window (tooltips);
}

static void
disconnect_tip_window_display_closed (GtkTooltips *tooltips)
{
  g_signal_handlers_disconnect_by_func (gtk_widget_get_display (tooltips->tip_window),
					(gpointer) tip_window_display_closed,
					tooltips);
}

static void
gtk_tooltips_unset_tip_window (GtkTooltips *tooltips)
{
  if (tooltips->tip_window)
    {
      disconnect_tip_window_display_closed (tooltips);
      
      gtk_widget_destroy (tooltips->tip_window);
      tooltips->tip_window = NULL;
    }
}

185
static void
186
gtk_tooltips_destroy (GtkObject *object)
Elliot Lee's avatar
Elliot Lee committed
187
{
188
  GtkTooltips *tooltips = GTK_TOOLTIPS (object);
Elliot Lee's avatar
Elliot Lee committed
189 190 191 192 193
  GList *current;
  GtkTooltipsData *tooltipsdata;

  g_return_if_fail (tooltips != NULL);

194
  if (tooltips->timer_tag)
Elliot Lee's avatar
Elliot Lee committed
195
    {
196
      g_source_remove (tooltips->timer_tag);
197
      tooltips->timer_tag = 0;
Elliot Lee's avatar
Elliot Lee committed
198 199
    }

200
  if (tooltips->tips_data_list != NULL)
Elliot Lee's avatar
Elliot Lee committed
201
    {
202
      current = g_list_first (tooltips->tips_data_list);
Elliot Lee's avatar
Elliot Lee committed
203 204 205 206
      while (current != NULL)
	{
	  tooltipsdata = (GtkTooltipsData*) current->data;
	  current = current->next;
207
	  gtk_tooltips_widget_remove (tooltipsdata->widget, tooltipsdata);
Elliot Lee's avatar
Elliot Lee committed
208 209 210
	}
    }

211
  gtk_tooltips_unset_tip_window (tooltips);
212 213

  GTK_OBJECT_CLASS (parent_class)->destroy (object);
214 215 216 217 218 219 220 221
}

static void
gtk_tooltips_update_screen (GtkTooltips *tooltips,
			    gboolean     new_window)
{
  gboolean screen_changed = FALSE;
  
222 223
  if (tooltips->active_tips_data &&
      tooltips->active_tips_data->widget)
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
    {
      GdkScreen *screen = gtk_widget_get_screen (tooltips->active_tips_data->widget);

      screen_changed = (screen != gtk_widget_get_screen (tooltips->tip_window));

      if (screen_changed)
	{
	  if (!new_window)
	    disconnect_tip_window_display_closed (tooltips);
      
	  gtk_window_set_screen (GTK_WINDOW (tooltips->tip_window), screen);
	}
    }

  if (screen_changed || new_window)
    g_signal_connect (gtk_widget_get_display (tooltips->tip_window), "closed",
		      G_CALLBACK (tip_window_display_closed), tooltips);

Elliot Lee's avatar
Elliot Lee committed
242 243
}

244 245
void
gtk_tooltips_force_window (GtkTooltips *tooltips)
246
{
247 248 249 250
  g_return_if_fail (GTK_IS_TOOLTIPS (tooltips));

  if (!tooltips->tip_window)
    {
Owen Taylor's avatar
Owen Taylor committed
251
      tooltips->tip_window = gtk_window_new (GTK_WINDOW_POPUP);
252
      gtk_tooltips_update_screen (tooltips, TRUE);
253
      gtk_widget_set_app_paintable (tooltips->tip_window, TRUE);
254
      gtk_window_set_resizable (GTK_WINDOW (tooltips->tip_window), FALSE);
255
      gtk_widget_set_name (tooltips->tip_window, "gtk-tooltips");
256 257
      gtk_container_set_border_width (GTK_CONTAINER (tooltips->tip_window), 4);

Manish Singh's avatar
Manish Singh committed
258 259 260 261
      g_signal_connect_swapped (tooltips->tip_window,
				"expose_event",
				G_CALLBACK (gtk_tooltips_paint_window), 
				tooltips);
262

263 264 265 266 267 268 269
      tooltips->tip_label = gtk_label_new (NULL);
      gtk_label_set_line_wrap (GTK_LABEL (tooltips->tip_label), TRUE);
      gtk_misc_set_alignment (GTK_MISC (tooltips->tip_label), 0.5, 0.5);
      gtk_widget_show (tooltips->tip_label);
      
      gtk_container_add (GTK_CONTAINER (tooltips->tip_window), tooltips->tip_label);

Manish Singh's avatar
Manish Singh committed
270 271 272 273
      g_signal_connect (tooltips->tip_window,
			"destroy",
			G_CALLBACK (gtk_widget_destroyed),
			&tooltips->tip_window);
274
    }
275 276
}

Elliot Lee's avatar
Elliot Lee committed
277 278 279 280 281 282 283 284 285 286 287 288 289
void
gtk_tooltips_enable (GtkTooltips *tooltips)
{
  g_return_if_fail (tooltips != NULL);

  tooltips->enabled = TRUE;
}

void
gtk_tooltips_disable (GtkTooltips *tooltips)
{
  g_return_if_fail (tooltips != NULL);

290
  gtk_tooltips_set_active_widget (tooltips, NULL);
Elliot Lee's avatar
Elliot Lee committed
291

292
  tooltips->enabled = FALSE;
Elliot Lee's avatar
Elliot Lee committed
293 294 295 296
}

void
gtk_tooltips_set_delay (GtkTooltips *tooltips,
297
                        guint         delay)
Elliot Lee's avatar
Elliot Lee committed
298 299 300 301 302 303
{
  g_return_if_fail (tooltips != NULL);

  tooltips->delay = delay;
}

304 305 306 307 308
GtkTooltipsData*
gtk_tooltips_data_get (GtkWidget       *widget)
{
  g_return_val_if_fail (widget != NULL, NULL);

Manish Singh's avatar
Manish Singh committed
309
  return g_object_get_data (G_OBJECT (widget), tooltips_data_key);
310 311
}

Elliot Lee's avatar
Elliot Lee committed
312
void
313 314 315 316
gtk_tooltips_set_tip (GtkTooltips *tooltips,
		      GtkWidget   *widget,
		      const gchar *tip_text,
		      const gchar *tip_private)
Elliot Lee's avatar
Elliot Lee committed
317 318 319
{
  GtkTooltipsData *tooltipsdata;

320
  g_return_if_fail (GTK_IS_TOOLTIPS (tooltips));
Elliot Lee's avatar
Elliot Lee committed
321 322
  g_return_if_fail (widget != NULL);

323
  tooltipsdata = gtk_tooltips_data_get (widget);
Elliot Lee's avatar
Elliot Lee committed
324

325
  if (!tip_text)
326 327 328 329 330 331 332 333 334 335 336 337
    {
      if (tooltipsdata)
	gtk_tooltips_widget_remove (tooltipsdata->widget, tooltipsdata);
      return;
    }
  
  if (tooltips->active_tips_data 
      && tooltips->active_tips_data->widget == widget
      && GTK_WIDGET_DRAWABLE (tooltips->active_tips_data->widget))
    {
      g_free (tooltipsdata->tip_text);
      g_free (tooltipsdata->tip_private);
Elliot Lee's avatar
Elliot Lee committed
338

339 340 341 342 343 344
      tooltipsdata->tip_text = g_strdup (tip_text);
      tooltipsdata->tip_private = g_strdup (tip_private);
      
      gtk_tooltips_draw_tips (tooltips);
    }
  else 
Elliot Lee's avatar
Elliot Lee committed
345
    {
Manish Singh's avatar
Manish Singh committed
346
      g_object_ref (widget);
347 348 349 350 351 352
      
      if (tooltipsdata)
        gtk_tooltips_widget_remove (tooltipsdata->widget, tooltipsdata);
      
      tooltipsdata = g_new0 (GtkTooltipsData, 1);
      
353
      tooltipsdata->tooltips = tooltips;
Elliot Lee's avatar
Elliot Lee committed
354 355
      tooltipsdata->widget = widget;

356 357
      tooltipsdata->tip_text = g_strdup (tip_text);
      tooltipsdata->tip_private = g_strdup (tip_private);
Elliot Lee's avatar
Elliot Lee committed
358

359
      tooltips->tips_data_list = g_list_append (tooltips->tips_data_list,
360
                                                tooltipsdata);
Manish Singh's avatar
Manish Singh committed
361 362 363
      g_signal_connect_after (widget, "event-after",
                              G_CALLBACK (gtk_tooltips_event_handler),
			      tooltipsdata);
Elliot Lee's avatar
Elliot Lee committed
364

Matthias Clasen's avatar
Matthias Clasen committed
365
      g_object_set_data (G_OBJECT (widget), I_(tooltips_data_key),
Manish Singh's avatar
Manish Singh committed
366
                         tooltipsdata);
Elliot Lee's avatar
Elliot Lee committed
367

Manish Singh's avatar
Manish Singh committed
368 369 370
      g_signal_connect (widget, "unmap",
			G_CALLBACK (gtk_tooltips_widget_unmap),
			tooltipsdata);
Elliot Lee's avatar
Elliot Lee committed
371

Manish Singh's avatar
Manish Singh committed
372 373 374
      g_signal_connect (widget, "unrealize",
			G_CALLBACK (gtk_tooltips_widget_unmap),
			tooltipsdata);
375

Manish Singh's avatar
Manish Singh committed
376 377 378
      g_signal_connect (widget, "destroy",
                        G_CALLBACK (gtk_tooltips_widget_remove),
			tooltipsdata);
Elliot Lee's avatar
Elliot Lee committed
379 380 381
    }
}

382
static gint
383
gtk_tooltips_paint_window (GtkTooltips *tooltips)
384
{
385 386 387
  GtkRequisition req;

  gtk_widget_size_request (tooltips->tip_window, &req);
388 389 390
  gtk_paint_flat_box (tooltips->tip_window->style, tooltips->tip_window->window,
		      GTK_STATE_NORMAL, GTK_SHADOW_OUT, 
		      NULL, GTK_WIDGET(tooltips->tip_window), "tooltip",
391
		      0, 0, req.width, req.height);
392

393
  return FALSE;
394 395
}

Elliot Lee's avatar
Elliot Lee committed
396
static void
397
gtk_tooltips_draw_tips (GtkTooltips *tooltips)
Elliot Lee's avatar
Elliot Lee committed
398
{
399
  GtkRequisition requisition;
Elliot Lee's avatar
Elliot Lee committed
400
  GtkWidget *widget;
401
  gint x, y, w, h;
Elliot Lee's avatar
Elliot Lee committed
402
  GtkTooltipsData *data;
403
  gboolean keyboard_mode;
404
  GdkScreen *screen;
405 406 407
  GdkScreen *pointer_screen;
  gint monitor_num, px, py;
  GdkRectangle monitor;
Elliot Lee's avatar
Elliot Lee committed
408

409
  if (!tooltips->tip_window)
410
    gtk_tooltips_force_window (tooltips);
411
  else if (GTK_WIDGET_VISIBLE (tooltips->tip_window))
412
    g_get_current_time (&tooltips->last_popdown);
Elliot Lee's avatar
Elliot Lee committed
413

414
  gtk_widget_ensure_style (tooltips->tip_window);
415
  
416
  widget = tooltips->active_tips_data->widget;
Matthias Clasen's avatar
Matthias Clasen committed
417
  g_object_set_data (G_OBJECT (tooltips->tip_window), I_(tooltips_info_key),
418
                     tooltips);
Elliot Lee's avatar
Elliot Lee committed
419

420
  keyboard_mode = get_keyboard_mode (widget);
421 422

  gtk_tooltips_update_screen (tooltips, FALSE);
423 424
  
  screen = gtk_widget_get_screen (widget);
Elliot Lee's avatar
Elliot Lee committed
425

426
  data = tooltips->active_tips_data;
427

428
  gtk_label_set_text (GTK_LABEL (tooltips->tip_label), data->tip_text);
429

430 431 432
  gtk_widget_size_request (tooltips->tip_window, &requisition);
  w = requisition.width;
  h = requisition.height;
Elliot Lee's avatar
Elliot Lee committed
433

434
  gdk_window_get_origin (widget->window, &x, &y);
435
  if (GTK_WIDGET_NO_WINDOW (widget))
436 437 438 439 440 441 442 443
    {
      x += widget->allocation.x;
      y += widget->allocation.y;
    }

  x += widget->allocation.width / 2;
    
  if (!keyboard_mode)
444 445
    gdk_window_get_pointer (gdk_screen_get_root_window (screen),
			    &x, NULL, NULL);
Elliot Lee's avatar
Elliot Lee committed
446

447
  x -= (w / 2 + 4);
Elliot Lee's avatar
Elliot Lee committed
448

449 450 451 452 453 454 455 456 457 458 459 460 461 462
  gdk_display_get_pointer (gdk_screen_get_display (screen),
			   &pointer_screen, &px, &py, NULL);
  if (pointer_screen != screen) 
    {
      px = x;
      py = y;
    }
  monitor_num = gdk_screen_get_monitor_at_point (screen, px, py);
  gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);

  if ((x + w) > monitor.x + monitor.width)
    x -= (x + w) - (monitor.x + monitor.width);
  else if (x < monitor.x)
    x = monitor.x;
Elliot Lee's avatar
Elliot Lee committed
463

464
  if ((y + h + widget->allocation.height + 4) > monitor.y + monitor.height)
Elliot Lee's avatar
Elliot Lee committed
465 466 467 468
    y = y - h - 4;
  else
    y = y + widget->allocation.height + 4;

469
  gtk_window_move (GTK_WINDOW (tooltips->tip_window), x, y);
470
  gtk_widget_show (tooltips->tip_window);
Elliot Lee's avatar
Elliot Lee committed
471 472 473 474 475 476 477
}

static gint
gtk_tooltips_timeout (gpointer data)
{
  GtkTooltips *tooltips = (GtkTooltips *) data;

478
  GDK_THREADS_ENTER ();
479
  
480 481
  if (tooltips->active_tips_data != NULL &&
      GTK_WIDGET_DRAWABLE (tooltips->active_tips_data->widget))
Elliot Lee's avatar
Elliot Lee committed
482 483
    gtk_tooltips_draw_tips (tooltips);

484 485
  tooltips->timer_tag = 0;

486
  GDK_THREADS_LEAVE ();
487

Elliot Lee's avatar
Elliot Lee committed
488 489 490 491 492 493 494
  return FALSE;
}

static void
gtk_tooltips_set_active_widget (GtkTooltips *tooltips,
                                GtkWidget   *widget)
{
495
  if (tooltips->tip_window)
496 497 498 499 500
    {
      if (GTK_WIDGET_VISIBLE (tooltips->tip_window))
	g_get_current_time (&tooltips->last_popdown);
      gtk_widget_hide (tooltips->tip_window);
    }
501
  if (tooltips->timer_tag)
Elliot Lee's avatar
Elliot Lee committed
502
    {
503
      g_source_remove (tooltips->timer_tag);
504 505 506 507 508 509 510 511 512 513
      tooltips->timer_tag = 0;
    }
  
  tooltips->active_tips_data = NULL;
  
  if (widget)
    {
      GList *list;
      
      for (list = tooltips->tips_data_list; list; list = list->next)
Elliot Lee's avatar
Elliot Lee committed
514
	{
515 516 517 518 519 520 521 522 523 524
	  GtkTooltipsData *tooltipsdata;
	  
	  tooltipsdata = list->data;
	  
	  if (tooltipsdata->widget == widget &&
	      GTK_WIDGET_DRAWABLE (widget))
	    {
	      tooltips->active_tips_data = tooltipsdata;
	      break;
	    }
Elliot Lee's avatar
Elliot Lee committed
525 526
	}
    }
527 528 529 530 531 532
  else
    {
      tooltips->use_sticky_delay = FALSE;
    }
}

533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561
static void
gtk_tooltips_show_tip (GtkWidget *widget)
{
  GtkTooltipsData *tooltipsdata;

  tooltipsdata = gtk_tooltips_data_get (widget);

  if (tooltipsdata &&
      (!tooltipsdata->tooltips->active_tips_data ||
       tooltipsdata->tooltips->active_tips_data->widget != widget))
    {
      gtk_tooltips_set_active_widget (tooltipsdata->tooltips, widget);
      gtk_tooltips_draw_tips (tooltipsdata->tooltips);
    }
}

static void
gtk_tooltips_hide_tip (GtkWidget *widget)
{
  GtkTooltipsData *tooltipsdata;

  tooltipsdata = gtk_tooltips_data_get (widget);

  if (tooltipsdata &&
      (tooltipsdata->tooltips->active_tips_data &&
       tooltipsdata->tooltips->active_tips_data->widget == widget))
    gtk_tooltips_set_active_widget (tooltipsdata->tooltips, NULL);
}

562 563 564 565 566 567 568 569 570 571
static gboolean
gtk_tooltips_recently_shown (GtkTooltips *tooltips)
{
  GTimeVal now;
  glong msec;
  
  g_get_current_time (&now);
  msec = (now.tv_sec  - tooltips->last_popdown.tv_sec) * 1000 +
	  (now.tv_usec - tooltips->last_popdown.tv_usec) / 1000;
  return (msec < STICKY_REVERT_DELAY);
Elliot Lee's avatar
Elliot Lee committed
572 573
}

574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590
static gboolean
get_keyboard_mode (GtkWidget *widget)
{
  GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
  if (GTK_IS_WINDOW (toplevel))
    return GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (toplevel), "gtk-tooltips-keyboard-mode"));
  else
    return FALSE;
}

static void
start_keyboard_mode (GtkWidget *widget)
{
  GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
  if (GTK_IS_WINDOW (toplevel))
    {
      GtkWidget *focus = GTK_WINDOW (toplevel)->focus_widget;
591

Matthias Clasen's avatar
Matthias Clasen committed
592
      g_object_set_data (G_OBJECT (toplevel), I_("gtk-tooltips-keyboard-mode"), GUINT_TO_POINTER (TRUE));
593

594 595 596 597 598 599 600 601 602 603 604 605 606 607 608
      if (focus)
	gtk_tooltips_show_tip (focus);
    }
}

static void
stop_keyboard_mode (GtkWidget *widget)
{
  GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
  if (GTK_IS_WINDOW (toplevel))
    {
      GtkWidget *focus = GTK_WINDOW (toplevel)->focus_widget;
      if (focus)
	gtk_tooltips_hide_tip (focus);
      
Matthias Clasen's avatar
Matthias Clasen committed
609
      g_object_set_data (G_OBJECT (toplevel), I_("gtk-tooltips-keyboard-mode"), GUINT_TO_POINTER (FALSE));
610 611 612
    }
}

613 614 615 616 617 618 619 620 621 622 623 624 625 626 627
static gboolean
tooltips_enabled (GtkTooltips *tooltips, GtkWidget *w)
{
  GtkSettings *settings;
  gboolean touchscreen;

  if (!tooltips->enabled)
    return FALSE;

  settings = gtk_widget_get_settings (w);
  g_object_get (settings, "gtk-touchscreen-mode", &touchscreen, NULL);
  
  return !touchscreen;
}

628 629 630 631 632 633 634
static void
gtk_tooltips_start_delay (GtkTooltips *tooltips,
			  GtkWidget   *widget)
{
  GtkTooltipsData *old_tips_data;
  
  old_tips_data = tooltips->active_tips_data;
635
  if (tooltips_enabled (tooltips, widget) &&
636 637 638 639 640 641 642 643 644 645 646
      (!old_tips_data || old_tips_data->widget != widget))
    {
      guint delay;
      
      gtk_tooltips_set_active_widget (tooltips, widget);
      
      if (tooltips->use_sticky_delay &&
	  gtk_tooltips_recently_shown (tooltips))
	delay = STICKY_DELAY;
      else
	delay = tooltips->delay;
647 648 649
      tooltips->timer_tag = g_timeout_add (delay,
					   gtk_tooltips_timeout,
					   (gpointer) tooltips);
650 651 652
    }
}

653
static void
Elliot Lee's avatar
Elliot Lee committed
654 655 656 657
gtk_tooltips_event_handler (GtkWidget *widget,
                            GdkEvent  *event)
{
  GtkTooltips *tooltips;
658
  GtkTooltipsData *old_tips_data;
659
  GtkWidget *event_widget;
660
  gboolean keyboard_mode = get_keyboard_mode (widget);
Elliot Lee's avatar
Elliot Lee committed
661 662 663

  if ((event->type == GDK_LEAVE_NOTIFY || event->type == GDK_ENTER_NOTIFY) &&
      event->crossing.detail == GDK_NOTIFY_INFERIOR)
664
    return;
665 666 667

  old_tips_data = gtk_tooltips_data_get (widget);
  tooltips = old_tips_data->tooltips;
Elliot Lee's avatar
Elliot Lee committed
668

669
  if (keyboard_mode)
Elliot Lee's avatar
Elliot Lee committed
670
    {
671
      switch (event->type)
Elliot Lee's avatar
Elliot Lee committed
672
	{
673 674 675 676 677 678 679 680 681 682 683 684
	case GDK_FOCUS_CHANGE:
	  if (event->focus_change.in)
	    gtk_tooltips_show_tip (widget);
	  else
	    gtk_tooltips_hide_tip (widget);
	  break;
	default:
	  break;
	}
    }
  else
    {
685 686 687 688 689 690
      if (event->type != GDK_KEY_PRESS && event->type != GDK_KEY_RELEASE)
	{
	  event_widget = gtk_get_event_widget (event);
	  if (event_widget != widget)
	    return;
	}
691 692 693 694 695 696 697
  
      switch (event->type)
	{
	case GDK_EXPOSE:
	  /* do nothing */
	  break;
	case GDK_ENTER_NOTIFY:
698 699
	  if (!(GTK_IS_MENU_ITEM (widget) && GTK_MENU_ITEM (widget)->submenu))
	    gtk_tooltips_start_delay (tooltips, widget);
700 701 702 703 704 705 706 707 708 709 710 711
	  break;
	  
	case GDK_LEAVE_NOTIFY:
	  {
	    gboolean use_sticky_delay;
	    
	    use_sticky_delay = tooltips->tip_window &&
	      GTK_WIDGET_VISIBLE (tooltips->tip_window);
	    gtk_tooltips_set_active_widget (tooltips, NULL);
	    tooltips->use_sticky_delay = use_sticky_delay;
	  }
	  break;
712

713 714 715 716
	case GDK_MOTION_NOTIFY:
	  /* Handle menu items specially ... pend popup for each motion
	   * on other widgets, we ignore motion.
	   */
717
	  if (GTK_IS_MENU_ITEM (widget) && !GTK_MENU_ITEM (widget)->submenu)
718
	    {
719 720 721
	      /* Completely evil hack to make sure we get the LEAVE_NOTIFY
	       */
	      GTK_PRIVATE_SET_FLAG (widget, GTK_LEAVE_PENDING);
722 723 724 725 726
	      gtk_tooltips_set_active_widget (tooltips, NULL);
	      gtk_tooltips_start_delay (tooltips, widget);
	      break;
	    }
	  break;		/* ignore */
727 728 729 730 731 732
	case GDK_BUTTON_PRESS:
	case GDK_BUTTON_RELEASE:
	case GDK_KEY_PRESS:
	case GDK_KEY_RELEASE:
	case GDK_PROXIMITY_IN:
	case GDK_SCROLL:
733 734
	  gtk_tooltips_set_active_widget (tooltips, NULL);
	  break;
735 736
	default:
	  break;
Elliot Lee's avatar
Elliot Lee committed
737 738 739 740 741 742 743 744
	}
    }
}

static void
gtk_tooltips_widget_unmap (GtkWidget *widget,
			   gpointer   data)
{
745 746
  GtkTooltipsData *tooltipsdata = (GtkTooltipsData *)data;
  GtkTooltips *tooltips = tooltipsdata->tooltips;
747 748 749 750
  
  if (tooltips->active_tips_data &&
      (tooltips->active_tips_data->widget == widget))
    gtk_tooltips_set_active_widget (tooltips, NULL);
Elliot Lee's avatar
Elliot Lee committed
751 752 753 754 755 756
}

static void
gtk_tooltips_widget_remove (GtkWidget *widget,
			    gpointer   data)
{
757
  GtkTooltipsData *tooltipsdata = (GtkTooltipsData*) data;
758
  GtkTooltips *tooltips = tooltipsdata->tooltips;
Elliot Lee's avatar
Elliot Lee committed
759 760

  gtk_tooltips_widget_unmap (widget, data);
761 762
  tooltips->tips_data_list = g_list_remove (tooltips->tips_data_list,
					    tooltipsdata);
763
  gtk_tooltips_destroy_data (tooltipsdata);
Elliot Lee's avatar
Elliot Lee committed
764
}
765

766 767
void
_gtk_tooltips_toggle_keyboard_mode (GtkWidget *widget)
768
{
769 770 771 772
  if (get_keyboard_mode (widget))
    stop_keyboard_mode (widget);
  else
    start_keyboard_mode (widget);
773
}
774

775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813
/**
 * gtk_tooltips_get_info_from_tip_window:
 * @tip_window: a #GtkWindow 
 * @tooltips: the return location for the tooltips which are displayed 
 *    in @tip_window, or %NULL
 * @current_widget: the return location for the widget whose tooltips 
 *    are displayed, or %NULL
 * 
 * Determines the tooltips and the widget they belong to from the window in 
 * which they are displayed. 
 *
 * This function is mostly intended for use by accessibility technologies;
 * applications should have little use for it.
 * 
 * Return value: %TRUE if @tip_window is displaying tooltips, otherwise %FALSE.
 *
 * Since: 2.4
 **/
gboolean
gtk_tooltips_get_info_from_tip_window (GtkWindow    *tip_window,
                                       GtkTooltips **tooltips,
                                       GtkWidget   **current_widget)
{
  GtkTooltips  *current_tooltips;  
  gboolean has_tips;

  g_return_val_if_fail (GTK_IS_WINDOW (tip_window), FALSE);

  current_tooltips = g_object_get_data (G_OBJECT (tip_window), tooltips_info_key);

  has_tips = current_tooltips != NULL;

  if (tooltips)
    *tooltips = current_tooltips;
  if (current_widget)
    *current_widget = has_tips ? current_tooltips->active_tips_data->widget : NULL;

  return has_tips;
}
814 815 816

#define __GTK_TOOLTIPS_C__
#include "gtkaliasdef.c"