+void
+GtkCanvas::start_tooltip_timeout (Item* item)
+{
+ stop_tooltip_timeout ();
+
+ if (item && Gtkmm2ext::PersistentTooltip::tooltips_enabled ()) {
+ current_tooltip_item = item;
+
+ /* wait for the first idle that happens after this is
+ called. this means that we've stopped processing events, which
+ in turn implies that the user has stopped doing stuff for a
+ little while.
+ */
+
+ Glib::signal_idle().connect (sigc::mem_fun (*this, &GtkCanvas::really_start_tooltip_timeout));
+ }
+}
+
+bool
+GtkCanvas::really_start_tooltip_timeout ()
+{
+ /* an idle has occurred since we entered a tooltip-bearing widget. Now
+ * wait 1 second and if the timeout isn't cancelled, show the tooltip.
+ */
+
+ if (current_tooltip_item) {
+ tooltip_timeout_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &GtkCanvas::show_tooltip), tooltip_timeout_msecs);
+ }
+
+ return false; /* this is called from an idle callback, don't call it again */
+}
+
+void
+GtkCanvas::stop_tooltip_timeout ()
+{
+ current_tooltip_item = 0;
+ tooltip_timeout_connection.disconnect ();
+}
+
+bool
+GtkCanvas::show_tooltip ()
+{
+ Rect tooltip_item_bbox;
+
+ if (!current_tooltip_item || current_tooltip_item->tooltip().empty() || !current_tooltip_item->bounding_box()) {
+ return false;
+ }
+
+ if (!tooltip_window) {
+ tooltip_window = new Gtk::Window (Gtk::WINDOW_POPUP);
+ tooltip_label = manage (new Gtk::Label);
+ tooltip_label->show ();
+ tooltip_window->add (*tooltip_label);
+ tooltip_window->set_border_width (1);
+ tooltip_window->set_name ("tooltip");
+ }
+
+ tooltip_label->set_text (current_tooltip_item->tooltip());
+
+ /* figure out where to position the tooltip */
+
+ Gtk::Widget* toplevel = get_toplevel();
+ assert (toplevel);
+ int pointer_x, pointer_y;
+ Gdk::ModifierType mask;
+
+ (void) toplevel->get_window()->get_pointer (pointer_x, pointer_y, mask);
+
+ Duple tooltip_window_origin (pointer_x, pointer_y);
+
+ /* convert to root window coordinates */
+
+ int win_x, win_y;
+ dynamic_cast<Gtk::Window*>(toplevel)->get_position (win_x, win_y);
+
+ tooltip_window_origin = tooltip_window_origin.translate (Duple (win_x, win_y));
+
+ /* we don't want the pointer to be inside the window when it is
+ * displayed, because then we generate a leave/enter event pair when
+ * the window is displayed then hidden - the enter event will
+ * trigger a new tooltip timeout.
+ *
+ * So move the window right of the pointer position by just a enough
+ * to get it away from the pointer.
+ */
+
+ tooltip_window_origin.x += 30;
+ tooltip_window_origin.y += 45;
+
+ /* move the tooltip window into position */
+
+ tooltip_window->move (tooltip_window_origin.x, tooltip_window_origin.y);
+
+ /* ready to show */
+
+ tooltip_window->present ();
+
+ /* called from a timeout handler, don't call it again */
+
+ return false;
+}
+
+void
+GtkCanvas::hide_tooltip ()
+{
+ /* hide it if its there */
+
+ if (tooltip_window) {
+ tooltip_window->hide ();
+
+ // Delete the tooltip window so it'll get re-created
+ // (i.e. properly re-sized) on the next usage.
+ delete tooltip_window;
+ tooltip_window = NULL;
+ }
+}
+
+Glib::RefPtr<Pango::Context>
+GtkCanvas::get_pango_context ()
+{
+ return Glib::wrap (gdk_pango_context_get());
+}
+