context: // GtkUtil.cs
  //
  // Author:
  //   Lluis Sanchez Gual <lluis@novell.com>
  //
  // Copyright (c) 2008 Novell, Inc (http://www.novell.com)
  //
  // Permission is hereby granted, free of charge, to any person obtaining a copy
  // of this software and associated documentation files (the "Software"), to deal
  // in the Software without restriction, including without limitation the rights
  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  // copies of the Software, and to permit persons to whom the Software is
  // furnished to do so, subject to the following conditions:
  //
  // The above copyright notice and this permission notice shall be included in
  // all copies or substantial portions of the Software.
  //
  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  // THE SOFTWARE.
  //
  //
  
  using System;
  using System.Collections.Generic;
  using Gtk;
  using System.Runtime.InteropServices;
  
  namespace Docking.Helper
  {
     public static class GtkUtil
     {
        static Dictionary<TreeView, TreeViewTooltipsData> treeData = new Dictionary<TreeView, TreeViewTooltipsData> ();
  
        public static Cairo.Color ToCairoColor (this Gdk.Color color)
        {
           return new Cairo.Color ((double)color.Red / ushort.MaxValue,
                                   (double)color.Green / ushort.MaxValue,
                                   (double)color.Blue / ushort.MaxValue);
        }
  
        public static Gdk.Color ToGdkColor (this Cairo.Color color)
        {
           return new Gdk.Color ((byte)(color.R * 255d), (byte)(color.G * 255d), (byte)(color.B * 255d));
        }
  
        /// <summary>
        /// Makes a color lighter or darker
        /// </summary>
        /// <param name='lightAmount'>
        /// Amount of lightness to add. If the value is positive, the color will be lighter,
        /// if negative it will be darker. Value must be between 0 and 1.
        /// </param>
        public static Gdk.Color AddLight (this Gdk.Color color, double lightAmount)
        {
           HslColor c = color;
           c.L += lightAmount;
           return c;
        }
  
        public static Cairo.Color AddLight (this Cairo.Color color, double lightAmount)
        {
           HslColor c = color;
           c.L += lightAmount;
           return c;
        }
  
        //public static Gtk.Widget ToGtkWidget (this Xwt.Widget widget)
        //{
        // return (Gtk.Widget) Xwt.Engine.WidgetRegistry.GetNativeWidget (widget);
        //}
  
        public static void EnableAutoTooltips (this Gtk.TreeView tree)
        {
           TreeViewTooltipsData data = new TreeViewTooltipsData ();
           treeData [tree] = data;
           tree.MotionNotifyEvent += HandleMotionNotifyEvent;
           tree.LeaveNotifyEvent += HandleLeaveNotifyEvent;
           tree.ButtonPressEvent += HandleButtonPressEvent;
           tree.ScrollEvent += HandleTreeScrollEvent;
           tree.Hidden += HandleTreeHidden;
           tree.Unrealized += HandleTreeHidden;
           tree.Destroyed += delegate {
              ResetTooltip (tree);
              treeData.Remove (tree);
           };
        }
  
        static void ResetTooltip (Gtk.TreeView tree)
        {
           TreeViewTooltipsData data;
           if (!treeData.TryGetValue (tree, out data))
              return;
           if (data.ShowTimer != 0)
              GLib.Source.Remove (data.ShowTimer);
           if (data.LeaveTimer != 0)
              GLib.Source.Remove (data.LeaveTimer);
           //if (data.Tooltip != null)
           // data.Tooltip.Destroy ();
        }
  
        static void HandleTreeHidden (object sender, EventArgs e)
        {
           ResetTooltip ((Gtk.TreeView) sender);
        }
  
        [GLib.ConnectBeforeAttribute]
        static void HandleTreeScrollEvent (object o, ScrollEventArgs args)
        {
           HideTooltip ((Gtk.TreeView)o);
        }
  
        [GLib.ConnectBeforeAttribute]
        static void HandleLeaveNotifyEvent(object o, LeaveNotifyEventArgs args)
        {
           TreeView tree = (TreeView) o;
           TreeViewTooltipsData data;
           if (!treeData.TryGetValue (tree, out data))
              return;
           data.LeaveTimer = GLib.Timeout.Add (50, delegate {
              data.LeaveTimer = 0;
              //if (data != null && data.Tooltip != null && data.Tooltip.MouseIsOver)
              // return false;
              HideTooltip (tree);
              return false;
           });
        }
  
        internal static void HideTooltip (TreeView tree)
        {
           TreeViewTooltipsData data;
           if (!treeData.TryGetValue (tree, out data))
              return;
           if (data.ShowTimer != 0) {
              GLib.Source.Remove (data.ShowTimer);
              data.ShowTimer = 0;
              return;
           }
           //if (data.Tooltip != null) {
           // data.Tooltip.Destroy ();
           // data.Tooltip = null;
           //}
        }
  
        [GLib.ConnectBeforeAttribute]
        static void HandleMotionNotifyEvent(object o, MotionNotifyEventArgs args)
        {
           TreeView tree = (TreeView) o;
           TreeViewTooltipsData data;
           if (!treeData.TryGetValue (tree, out data))
              return;
  
           HideTooltip (tree);
  
           int cx, cy;
           TreePath path;
           TreeViewColumn col;
           if (!tree.GetPathAtPos ((int)args.Event.X, (int)args.Event.Y, out path, out col, out cx, out cy))
              return;
           data.Path = path;
           data.ShowTimer = GLib.Timeout.Add (300, delegate {
              data.ShowTimer = 0;
              int ox, oy;
              tree.BinWindow.GetOrigin (out ox, out oy);
              //Gdk.Rectangle rect = tree.GetCellArea (path, col);
              //data.Tooltip = new CellTooltipWindow (tree, col, path);
              //if (rect.X + data.Tooltip.SizeRequest ().Width > tree.Allocation.Width) {
              // data.Tooltip.Move (ox + rect.X - 1, oy + rect.Y);
              // data.Tooltip.ShowAll ();
              //} else {
              // data.Tooltip.Destroy ();
              // data.Tooltip = null;
              //}
              return false;
           });
        }
  
        [GLib.ConnectBeforeAttribute]
        static void HandleButtonPressEvent (object o, Gtk.ButtonPressEventArgs ev)
        {
           UnscheduleTooltipShow (o);
        }
  
        static void UnscheduleTooltipShow (object tree)
        {
           TreeViewTooltipsData data;
           if (!treeData.TryGetValue ((Gtk.TreeView)tree, out data))
              return;
           if (data.ShowTimer != 0) {
              GLib.Source.Remove (data.ShowTimer);
              data.ShowTimer = 0;
           }
        }
  
        public static Gdk.Rectangle ToScreenCoordinates (Gtk.Widget widget, Gdk.Window w, Gdk.Rectangle rect)
        {
           return new Gdk.Rectangle (ToScreenCoordinates (widget, w, rect.X, rect.Y), rect.Size);
        }
  
        public static Gdk.Point ToScreenCoordinates (Gtk.Widget widget, Gdk.Window w, int x, int y)
        {
           int ox, oy;
           w.GetOrigin (out ox, out oy);
           ox += widget.Allocation.X;
           oy += widget.Allocation.Y;
           return new Gdk.Point (ox + x, oy + y);
        }
  
        public static Gdk.Rectangle ToWindowCoordinates (Gtk.Widget widget, Gdk.Window w, Gdk.Rectangle rect)
        {
           return new Gdk.Rectangle (ToWindowCoordinates (widget, w, rect.X, rect.Y), rect.Size);
        }
  
        public static Gdk.Point ToWindowCoordinates (Gtk.Widget widget, Gdk.Window w, int x, int y)
        {
           int ox, oy;
           w.GetOrigin (out ox, out oy);
           ox += widget.Allocation.X;
           oy += widget.Allocation.Y;
           return new Gdk.Point (x - ox, y - oy);
        }
  
        public static T ReplaceWithWidget<T> (this Gtk.Widget oldWidget, T newWidget, bool transferChildren = false) where T:Gtk.Widget
        {
           Gtk.Container parent = (Gtk.Container) oldWidget.Parent;
           if (parent == null)
              throw new InvalidOperationException ();
  
           if (parent is Box) {
              var box = (Box) parent;
              var bc = (Gtk.Box.BoxChild) parent [oldWidget];
              box.Add (newWidget);
              var nc = (Gtk.Box.BoxChild) parent [newWidget];
              nc.Expand = bc.Expand;
              nc.Fill = bc.Fill;
              nc.PackType = bc.PackType;
              nc.Padding = bc.Padding;
              nc.Position = bc.Position;
              box.Remove (oldWidget);
           }
           else if (parent is Table) {
              var table = (Table) parent;
              var bc = (Gtk.Table.TableChild) parent [oldWidget];
              table.Add (newWidget);
              var nc = (Gtk.Table.TableChild) parent [newWidget];
              nc.BottomAttach = bc.BottomAttach;
              nc.LeftAttach = bc.LeftAttach;
              nc.RightAttach = bc.RightAttach;
              nc.TopAttach = bc.TopAttach;
              nc.XOptions = bc.XOptions;
              nc.XPadding = bc.XPadding;
              nc.YOptions = bc.YOptions;
              nc.YPadding = bc.YPadding;
              table.Remove (oldWidget);
           }
           else if (parent is Paned) {
              var paned = (Paned) parent;
              var bc = (Gtk.Paned.PanedChild) parent [oldWidget];
              var resize = bc.Resize;
              var shrink = bc.Shrink;
              if (oldWidget == paned.Child1) {
                 paned.Remove (oldWidget);
                 paned.Add1 (newWidget);
              } else {
                 paned.Remove (oldWidget);
                 paned.Add2 (newWidget);
              }
  
gt:             var nc = (Gtk.Paned.PanedChild) parent [newWidget];