diff options
Diffstat (limited to 'main/src/core/MonoDevelop.Ide/MonoDevelop.Components')
25 files changed, 2360 insertions, 25 deletions
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/CairoExtensions.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/CairoExtensions.cs index 0b948cd7d0..3a21270833 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/CairoExtensions.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/CairoExtensions.cs @@ -33,7 +33,6 @@ using System.Runtime.InteropServices; using Gdk; using Cairo; using MonoDevelop.Core; -using Mono.TextEditor; namespace MonoDevelop.Components { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/CompactScrolledWindow.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/CompactScrolledWindow.cs index aef0dcf873..e6f0c23f4b 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/CompactScrolledWindow.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/CompactScrolledWindow.cs @@ -25,7 +25,6 @@ // THE SOFTWARE. using System; -using Mono.TextEditor; namespace MonoDevelop.Components { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/ContextMenuExtensionsGtk.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/ContextMenuExtensionsGtk.cs index 52607bf131..035a36cb79 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/ContextMenuExtensionsGtk.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/ContextMenuExtensionsGtk.cs @@ -50,7 +50,7 @@ namespace MonoDevelop.Components if (menu == null) throw new ArgumentNullException ("menu"); - Mono.TextEditor.GtkWorkarounds.ShowContextMenu (menu, parent, evt); + GtkWorkarounds.ShowContextMenu (menu, parent, evt); } static Gtk.MenuItem CreateMenuItem (ContextMenuItem item) @@ -93,7 +93,7 @@ namespace MonoDevelop.Components var img = new ImageView (item.Image); img.ShowAll (); imageItem.Image = img; - GtkWorkarounds.ForceImageOnMenuItem (imageItem); + Xwt.GtkBackend.GtkWorkarounds.ForceImageOnMenuItem (imageItem); } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/ContextMenuTreeView.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/ContextMenuTreeView.cs index a0e32d669e..c6ea099579 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/ContextMenuTreeView.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/ContextMenuTreeView.cs @@ -24,7 +24,6 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. using System; -using Mono.TextEditor; namespace MonoDevelop.Components { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/Control.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/Control.cs index dda73fc351..2cd9d466c5 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/Control.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/Control.cs @@ -43,6 +43,8 @@ namespace MonoDevelop.Components public Control (object widget) { + if (widget == null) + throw new ArgumentNullException ("widget"); this.nativeWidget = widget; } @@ -63,7 +65,9 @@ namespace MonoDevelop.Components if (!(w is T)) w = ConvertToType (typeof(T), w); if (w is Gtk.Widget) { - var c = new CommandRouterContainer ((Gtk.Widget)w, this, true); + var gtkWidget = (Gtk.Widget)w; + var c = new CommandRouterContainer (gtkWidget, this, true); + c.FocusChain = new [] { gtkWidget }; c.Show (); nativeWidget = c; c.Destroyed += delegate { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/DropDownBox.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/DropDownBox.cs index 1f02fdce8a..df6e49c424 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/DropDownBox.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/DropDownBox.cs @@ -29,7 +29,6 @@ using System.ComponentModel; using Gtk; using MonoDevelop.Ide; -using Mono.TextEditor; namespace MonoDevelop.Components { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/GtkGestures.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/GtkGestures.cs new file mode 100644 index 0000000000..a819fc0f74 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/GtkGestures.cs @@ -0,0 +1,252 @@ +// +// GtkGestures.cs +// +// Author: +// Michael Hutchinson <mhutch@xamarin.com> +// +// Copyright (c) 2012 Xamarin Inc. (http://xamarin.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.Runtime.InteropServices; +using Gdk; + +namespace MonoDevelop.Components +{ + public static class GtkGestures + { + const int GDK_GESTURE_MAGNIFY = 37; + const int GDK_GESTURE_ROTATE = 38; + const int GDK_GESTURE_SWIPE = 39;
+
+ [DllImport (PangoUtil.LIBGDK, CallingConvention = CallingConvention.Cdecl)] + static extern bool gdk_quartz_supports_gesture_events (); + + static bool isSupported; + + static GtkGestures () + { + if (MonoDevelop.Core.Platform.IsMac) { + try { + isSupported = gdk_quartz_supports_gesture_events (); + } catch (EntryPointNotFoundException) { + } + } + } + + public static bool IsSupported { get { return isSupported; } } + + public static void AddGestureMagnifyHandler (this Gtk.Widget widget, EventHandler<GestureMagnifyEventArgs> handler) + { + if (!isSupported) + throw new NotSupportedException (); + var signal = GLib.Signal.Lookup (widget, "gesture-magnify-event", typeof(GestureMagnifyEventArgs)); + signal.AddDelegate (new EventHandler<GestureMagnifyEventArgs> (handler)); + } + + public static void AddGestureRotateHandler (this Gtk.Widget widget, EventHandler<GestureRotateEventArgs> handler) + { + if (!isSupported) + throw new NotSupportedException (); + var signal = GLib.Signal.Lookup (widget, "gesture-rotate-event", typeof(GestureRotateEventArgs)); + signal.AddDelegate (new EventHandler<GestureRotateEventArgs> (handler)); + } + + public static void AddGestureSwipeHandler (this Gtk.Widget widget, EventHandler<GestureSwipeEventArgs> handler) + { + if (!isSupported) + throw new NotSupportedException (); + var signal = GLib.Signal.Lookup (widget, "gesture-swipe-event", typeof(GestureSwipeEventArgs)); + signal.AddDelegate (new EventHandler<GestureSwipeEventArgs> (handler)); + } + } + + public unsafe class GestureMagnifyEventArgs : GLib.SignalArgs + { + //have to force pack=4, or Mono aligns doubles to 8 bytes + [StructLayout (LayoutKind.Sequential, Pack=4)] + struct GdkEventGestureMagnify + { + public Gdk.EventType type; + public IntPtr window; + public sbyte send_event; + public uint time; + public double x, y; + public uint state; + public double magnification; + public IntPtr device; + public double x_root, y_root; + } + + // I tried to mimic the GTK# pattern of having a subclassed Event object on the EventArgs, but I gave up on + // figuring out how to get GTK# to marshal the event to a custom GestureMagnifyEvent class. Instead we just + // lift all the accessors up to the args class and dereference the handle directly. + GdkEventGestureMagnify *evt { + get { + var handle = ((Event)Args[0]).Handle; + return (GdkEventGestureMagnify *) handle; + } + } + + public Gdk.Window Window { + get { + return (Gdk.Window) GLib.Object.GetObject (evt->window); + } + } + + public Device Device { + get { + return (Device) GLib.Object.GetObject (evt->device); + } + } + + public uint Time { get { return evt->time; } } + public double X { get { return evt->x; } } + public double Y { get { return evt->y; } } + public ModifierType State { get { return (ModifierType) evt->state; } } + public double Magnification { get { return evt->magnification; } } + public double XRoot { get { return evt->x_root; } } + public double YRoot { get { return evt->y_root; } } + } + + public unsafe class GestureRotateEventArgs : GLib.SignalArgs + { + [StructLayout (LayoutKind.Sequential, Pack=4)] + struct GdkEventGestureRotate + { + public Gdk.EventType type; + public IntPtr window; + public sbyte send_event; + public uint time; + public double x, y; + public uint state; + public double rotation; + public IntPtr device; + public double x_root, y_root; + } + + GdkEventGestureRotate *evt { + get { + var handle = ((Event)Args[0]).Handle; + return (GdkEventGestureRotate *) handle; + } + } + + public Gdk.Window Window { + get { + return (Gdk.Window) GLib.Object.GetObject (evt->window); + } + } + + public Device Device { + get { + return (Device) GLib.Object.GetObject (evt->device); + } + } + + public uint Time { get { return evt->time; } } + public double X { get { return evt->x; } } + public double Y { get { return evt->y; } } + public ModifierType State { get { return (ModifierType) evt->state; } } + public double Rotation { get { return evt->rotation; } } + public double XRoot { get { return evt->x_root; } } + public double YRoot { get { return evt->y_root; } } + } + + public unsafe class GestureSwipeEventArgs : GLib.SignalArgs + { + [StructLayout (LayoutKind.Sequential, Pack=4)] + struct GdkEventGestureSwipe + { + public Gdk.EventType type; + public IntPtr window; + public sbyte send_event; + public uint time; + public double x, y; + public uint state; + public double delta_x, delta_y; + public IntPtr device; + public double x_root, y_root; + } + + GdkEventGestureSwipe *evt { + get { + var handle = ((Event)Args[0]).Handle; + return (GdkEventGestureSwipe *) handle; + } + } + + public Gdk.Window Window { + get { + return (Gdk.Window) GLib.Object.GetObject (evt->window); + } + } + + public Device Device { + get { + return (Device) GLib.Object.GetObject (evt->device); + } + } + + public uint Time { get { return evt->time; } } + public double X { get { return evt->x; } } + public double Y { get { return evt->y; } } + public ModifierType State { get { return (ModifierType) evt->state; } } + public double DeltaX { get { return evt->delta_x; } } + public double DeltaY { get { return evt->delta_y; } } + public double XRoot { get { return evt->x_root; } } + public double YRoot { get { return evt->y_root; } } + } + + /* + void PrintOffsets () + { + GdkEventGestureMagnify *v = (GdkEventGestureMagnify *)0; + Console.WriteLine ("type {0}", (int)&(v->type)); + Console.WriteLine ("window {0}", (int)&(v->window)); + Console.WriteLine ("send_event {0}", (int)&(v->send_event)); + Console.WriteLine ("time {0}", (int)&(v->time)); + Console.WriteLine ("x {0}", (int)&(v->x)); + Console.WriteLine ("y {0}", (int)&(v->y)); + Console.WriteLine ("state {0}", (int)&(v->state)); + Console.WriteLine ("magnification {0}", (int)&(v->magnification)); + Console.WriteLine ("x_root {0}", (int)&(v->x_root)); + Console.WriteLine ("y_root {0}", (int)&(v->y_root)); + } + + // gcc -m32 test.c `pkg-config --cflags gtk+-2.0` + #include <gtk/gtk.h> + + int main (int argc, char* argv) + { + GdkEventGestureMagnify *v = (GdkEventGestureMagnify *)0; + printf ("type %d\n", (int)&(v->type)); + printf ("window %d\n", (int)&(v->window)); + printf ("send_event %d\n", (int)&(v->send_event)); + printf ("time %d\n", (int)&(v->time)); + printf ("x %d\n", (int)&(v->x)); + printf ("y %d\n", (int)&(v->y)); + printf ("state %d\n", (int)&(v->state)); + printf ("magnification %d\n", (int)&(v->magnification)); + printf ("x_root %d\n", (int)&(v->x_root)); + printf ("y_root %d\n", (int)&(v->y_root)); + } + */ +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/GtkUtil.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/GtkUtil.cs index 5818c33e5b..a083d352f2 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/GtkUtil.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/GtkUtil.cs @@ -88,6 +88,19 @@ namespace MonoDevelop.Components return c.ToGdkColor (); } + /// <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 HslColor AddLight (this HslColor color, double lightAmount) + { + color.L += lightAmount; + return color; + } + public static Cairo.Color AddLight (this Cairo.Color color, double lightAmount) { var c = color.ToXwtColor (); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/GtkWorkarounds.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/GtkWorkarounds.cs new file mode 100644 index 0000000000..398ae0e0e9 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/GtkWorkarounds.cs @@ -0,0 +1,1275 @@ +// +// GtkWorkarounds.cs +// +// Authors: Jeffrey Stedfast <jeff@xamarin.com> +// +// Copyright (C) 2011 Xamarin Inc. +// +// 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.Drawing; +using System.Runtime.InteropServices; +using System.Collections.Generic; +using System.Reflection; +using System.Reflection.Emit; +using Gtk; +using MonoDevelop.Core; +using MonoDevelop.Ide.Editor.Highlighting; +using System.Text.RegularExpressions; + +namespace MonoDevelop.Components +{ + public static class GtkWorkarounds + { + const string LIBOBJC ="/usr/lib/libobjc.dylib"; + const string USER32DLL = "User32.dll"; + + [DllImport (LIBOBJC, EntryPoint = "sel_registerName")] + static extern IntPtr sel_registerName (string selector); + + [DllImport (LIBOBJC, EntryPoint = "objc_getClass")] + static extern IntPtr objc_getClass (string klass); + + [DllImport (LIBOBJC, EntryPoint = "objc_msgSend")] + static extern IntPtr objc_msgSend_IntPtr (IntPtr klass, IntPtr selector); + + [DllImport (LIBOBJC, EntryPoint = "objc_msgSend")] + static extern void objc_msgSend_void_bool (IntPtr klass, IntPtr selector, bool arg); + + [DllImport (LIBOBJC, EntryPoint = "objc_msgSend")] + static extern bool objc_msgSend_bool (IntPtr klass, IntPtr selector); + + [DllImport (LIBOBJC, EntryPoint = "objc_msgSend")] + static extern int objc_msgSend_NSInt32_NSInt32 (IntPtr klass, IntPtr selector, int arg); + + [DllImport (LIBOBJC, EntryPoint = "objc_msgSend")] + static extern long objc_msgSend_NSInt64_NSInt64 (IntPtr klass, IntPtr selector, long arg); + + [DllImport (LIBOBJC, EntryPoint = "objc_msgSend")] + static extern uint objc_msgSend_NSUInt32 (IntPtr klass, IntPtr selector); + + [DllImport (LIBOBJC, EntryPoint = "objc_msgSend")] + static extern ulong objc_msgSend_NSUInt64 (IntPtr klass, IntPtr selector); + + [DllImport (LIBOBJC, EntryPoint = "objc_msgSend_stret")] + static extern void objc_msgSend_CGRect32 (out CGRect32 rect, IntPtr klass, IntPtr selector); + + [DllImport (LIBOBJC, EntryPoint = "objc_msgSend_stret")] + static extern void objc_msgSend_CGRect64 (out CGRect64 rect, IntPtr klass, IntPtr selector); + + [DllImport (PangoUtil.LIBQUARTZ)] + static extern IntPtr gdk_quartz_window_get_nswindow (IntPtr window); + + [DllImport (PangoUtil.LIBQUARTZ)] + static extern bool gdk_window_has_embedded_nsview_focus (IntPtr window); + + struct CGRect32 + { + public float X, Y, Width, Height; + } + + struct CGRect64 + { + public double X, Y, Width, Height; + + public CGRect64 (CGRect32 rect32) + { + X = rect32.X; + Y = rect32.Y; + Width = rect32.Width; + Height = rect32.Height; + } + } + + static IntPtr cls_NSScreen; + static IntPtr sel_screens, sel_objectEnumerator, sel_nextObject, sel_frame, sel_visibleFrame, + sel_requestUserAttention, sel_setHasShadow, sel_invalidateShadow; + static IntPtr sharedApp; + static IntPtr cls_NSEvent; + static IntPtr sel_modifierFlags; + + const int NSCriticalRequest = 0; + const int NSInformationalRequest = 10; + + static System.Reflection.MethodInfo glibObjectGetProp, glibObjectSetProp; + + public static int GtkMinorVersion = 12, GtkMicroVersion = 0; + static bool oldMacKeyHacks = false; + + static GtkWorkarounds () + { + if (Platform.IsMac) { + InitMac (); + } + + var flags = System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic; + glibObjectSetProp = typeof (GLib.Object).GetMethod ("SetProperty", flags); + glibObjectGetProp = typeof (GLib.Object).GetMethod ("GetProperty", flags); + + foreach (int i in new [] { 24, 22, 20, 18, 16, 14 }) { + if (Gtk.Global.CheckVersion (2, (uint)i, 0) == null) { + GtkMinorVersion = i; + break; + } + } + + for (int i = 1; i < 20; i++) { + if (Gtk.Global.CheckVersion (2, (uint)GtkMinorVersion, (uint)i) == null) { + GtkMicroVersion = i; + } else { + break; + } + } + + //opt into the fixes on GTK+ >= 2.24.8 + if (Platform.IsMac) { + try { + gdk_quartz_set_fix_modifiers (true); + } catch (EntryPointNotFoundException) { + oldMacKeyHacks = true; + } + } + + keymap.KeysChanged += delegate { + mappedKeys.Clear (); + }; + } + + static void InitMac () + { + cls_NSScreen = objc_getClass ("NSScreen"); + cls_NSEvent = objc_getClass ("NSEvent"); + sel_screens = sel_registerName ("screens"); + sel_objectEnumerator = sel_registerName ("objectEnumerator"); + sel_nextObject = sel_registerName ("nextObject"); + sel_visibleFrame = sel_registerName ("visibleFrame"); + sel_frame = sel_registerName ("frame"); + sel_requestUserAttention = sel_registerName ("requestUserAttention:"); + sel_modifierFlags = sel_registerName ("modifierFlags"); + sel_setHasShadow = sel_registerName ("setHasShadow:"); + sel_invalidateShadow = sel_registerName ("invalidateShadow"); + sharedApp = objc_msgSend_IntPtr (objc_getClass ("NSApplication"), sel_registerName ("sharedApplication")); + } + + static Gdk.Rectangle MacGetUsableMonitorGeometry (Gdk.Screen screen, int monitor) + { + IntPtr array = objc_msgSend_IntPtr (cls_NSScreen, sel_screens); + IntPtr iter = objc_msgSend_IntPtr (array, sel_objectEnumerator); + Gdk.Rectangle ygeometry = screen.GetMonitorGeometry (monitor); + Gdk.Rectangle xgeometry = screen.GetMonitorGeometry (0); + IntPtr scrn; + int i = 0; + + while ((scrn = objc_msgSend_IntPtr (iter, sel_nextObject)) != IntPtr.Zero && i < monitor) + i++; + + if (scrn == IntPtr.Zero) + return screen.GetMonitorGeometry (monitor); + + CGRect64 visible, frame; + + if (IntPtr.Size == 8) { + objc_msgSend_CGRect64 (out visible, scrn, sel_visibleFrame); + objc_msgSend_CGRect64 (out frame, scrn, sel_frame); + } else { + CGRect32 visible32, frame32; + objc_msgSend_CGRect32 (out visible32, scrn, sel_visibleFrame); + objc_msgSend_CGRect32 (out frame32, scrn, sel_frame); + visible = new CGRect64 (visible32); + frame = new CGRect64 (frame32); + } + + // Note: Frame and VisibleFrame rectangles are relative to monitor 0, but we need absolute + // coordinates. + visible.X += xgeometry.X; + frame.X += xgeometry.X; + + // VisibleFrame.Y is the height of the Dock if it is at the bottom of the screen, so in order + // to get the menu height, we just figure out the difference between the visibleFrame height + // and the actual frame height, then subtract the Dock height. + // + // We need to swap the Y offset with the menu height because our callers expect the Y offset + // to be from the top of the screen, not from the bottom of the screen. + double x, y, width, height; + + if (visible.Height < frame.Height) { + double dockHeight = visible.Y - frame.Y; + double menubarHeight = (frame.Height - visible.Height) - dockHeight; + + height = frame.Height - menubarHeight - dockHeight; + y = ygeometry.Y + menubarHeight; + } else { + height = frame.Height; + y = ygeometry.Y; + } + + // Takes care of the possibility of the Dock being positioned on the left or right edge of the screen. + width = System.Math.Min (visible.Width, frame.Width); + x = System.Math.Max (visible.X, frame.X); + + return new Gdk.Rectangle ((int) x, (int) y, (int) width, (int) height); + } + + static void MacRequestAttention (bool critical) + { + int kind = critical? NSCriticalRequest : NSInformationalRequest; + if (IntPtr.Size == 8) { + objc_msgSend_NSInt64_NSInt64 (sharedApp, sel_requestUserAttention, kind); + } else { + objc_msgSend_NSInt32_NSInt32 (sharedApp, sel_requestUserAttention, kind); + } + } + + // Note: we can't reuse RectangleF because the layout is different... + [StructLayout (LayoutKind.Sequential)] + struct Rect { + public int Left; + public int Top; + public int Right; + public int Bottom; + + public int X { get { return Left; } } + public int Y { get { return Top; } } + public int Width { get { return Right - Left; } } + public int Height { get { return Bottom - Top; } } + } + + const int MonitorInfoFlagsPrimary = 0x01; + + [StructLayout (LayoutKind.Sequential)] + unsafe struct MonitorInfo { + public int Size; + public Rect Frame; // Monitor + public Rect VisibleFrame; // Work + public int Flags; + public fixed byte Device[32]; + } + + [UnmanagedFunctionPointer (CallingConvention.Winapi)] + delegate int EnumMonitorsCallback (IntPtr hmonitor, IntPtr hdc, IntPtr prect, IntPtr user_data); + + [DllImport (USER32DLL)] + extern static int EnumDisplayMonitors (IntPtr hdc, IntPtr clip, EnumMonitorsCallback callback, IntPtr user_data); + + [DllImport (USER32DLL)] + extern static int GetMonitorInfoA (IntPtr hmonitor, ref MonitorInfo info); + + static Gdk.Rectangle WindowsGetUsableMonitorGeometry (Gdk.Screen screen, int monitor_id) + { + Gdk.Rectangle geometry = screen.GetMonitorGeometry (monitor_id); + List<MonitorInfo> screens = new List<MonitorInfo> (); + + EnumDisplayMonitors (IntPtr.Zero, IntPtr.Zero, delegate (IntPtr hmonitor, IntPtr hdc, IntPtr prect, IntPtr user_data) { + var info = new MonitorInfo (); + + unsafe { + info.Size = sizeof (MonitorInfo); + } + + GetMonitorInfoA (hmonitor, ref info); + + // In order to keep the order the same as Gtk, we need to put the primary monitor at the beginning. + if ((info.Flags & MonitorInfoFlagsPrimary) != 0) + screens.Insert (0, info); + else + screens.Add (info); + + return 1; + }, IntPtr.Zero); + + MonitorInfo monitor = screens[monitor_id]; + Rect visible = monitor.VisibleFrame; + Rect frame = monitor.Frame; + + // Rebase the VisibleFrame off of Gtk's idea of this monitor's geometry (since they use different coordinate systems) + int x = geometry.X + (visible.Left - frame.Left); + int width = visible.Width; + + int y = geometry.Y + (visible.Top - frame.Top); + int height = visible.Height; + + return new Gdk.Rectangle (x, y, width, height); + } + + public static Gdk.Rectangle GetUsableMonitorGeometry (this Gdk.Screen screen, int monitor) + { + if (Platform.IsWindows) + return WindowsGetUsableMonitorGeometry (screen, monitor); + + if (Platform.IsMac) + return MacGetUsableMonitorGeometry (screen, monitor); + + return screen.GetMonitorGeometry (monitor); + } + + public static int RunDialogWithNotification (Gtk.Dialog dialog) + { + if (Platform.IsMac) + MacRequestAttention (dialog.Modal); + + return dialog.Run (); + } + + public static void PresentWindowWithNotification (this Gtk.Window window) + { + window.Present (); + + if (Platform.IsMac) { + var dialog = window as Gtk.Dialog; + MacRequestAttention (dialog == null? false : dialog.Modal); + } + } + + public static GLib.Value GetProperty (this GLib.Object obj, string name) + { + return (GLib.Value) glibObjectGetProp.Invoke (obj, new object[] { name }); + } + + public static void SetProperty (this GLib.Object obj, string name, GLib.Value value) + { + glibObjectSetProp.Invoke (obj, new object[] { name, value }); + } + + public static bool TriggersContextMenu (this Gdk.EventButton evt) + { + return evt.Type == Gdk.EventType.ButtonPress && IsContextMenuButton (evt); + } + + public static bool IsContextMenuButton (this Gdk.EventButton evt) + { + if (evt.Button == 3 && + (evt.State & (Gdk.ModifierType.Button1Mask | Gdk.ModifierType.Button2Mask)) == 0) + return true; + + if (Platform.IsMac) { + if (!oldMacKeyHacks && + evt.Button == 1 && + (evt.State & Gdk.ModifierType.ControlMask) != 0 && + (evt.State & (Gdk.ModifierType.Button2Mask | Gdk.ModifierType.Button3Mask)) == 0) + { + return true; + } + } + + return false; + } + + public static Gdk.ModifierType GetCurrentKeyModifiers () + { + if (Platform.IsMac) { + Gdk.ModifierType mtype = Gdk.ModifierType.None; + ulong mod; + if (IntPtr.Size == 8) { + mod = objc_msgSend_NSUInt64 (cls_NSEvent, sel_modifierFlags); + } else { + mod = objc_msgSend_NSUInt32 (cls_NSEvent, sel_modifierFlags); + } + if ((mod & (1 << 17)) != 0) + mtype |= Gdk.ModifierType.ShiftMask; + if ((mod & (1 << 18)) != 0) + mtype |= Gdk.ModifierType.ControlMask; + if ((mod & (1 << 19)) != 0) + mtype |= Gdk.ModifierType.Mod1Mask; // Alt key + if ((mod & (1 << 20)) != 0) + mtype |= Gdk.ModifierType.Mod2Mask; // Command key + return mtype; + } + else { + Gdk.ModifierType mtype; + Gtk.Global.GetCurrentEventState (out mtype); + return mtype; + } + } + + public static void GetPageScrollPixelDeltas (this Gdk.EventScroll evt, double pageSizeX, double pageSizeY, + out double deltaX, out double deltaY) + { + if (!GetEventScrollDeltas (evt, out deltaX, out deltaY)) { + var direction = evt.Direction; + deltaX = deltaY = 0; + if (pageSizeY != 0 && (direction == Gdk.ScrollDirection.Down || direction == Gdk.ScrollDirection.Up)) { + deltaY = System.Math.Pow (pageSizeY, 2.0 / 3.0); + deltaX = 0.0; + if (direction == Gdk.ScrollDirection.Up) + deltaY = -deltaY; + } else if (pageSizeX != 0) { + deltaX = System.Math.Pow (pageSizeX, 2.0 / 3.0); + deltaY = 0.0; + if (direction == Gdk.ScrollDirection.Left) + deltaX = -deltaX; + } + } + } + + public static void AddValueClamped (this Gtk.Adjustment adj, double value) + { + adj.Value = System.Math.Max (adj.Lower, System.Math.Min (adj.Value + value, adj.Upper - adj.PageSize)); + } + + [DllImport (PangoUtil.LIBGTK, CallingConvention = CallingConvention.Cdecl)] + extern static bool gdk_event_get_scroll_deltas (IntPtr eventScroll, out double deltaX, out double deltaY); + static bool scrollDeltasNotSupported; + + public static bool GetEventScrollDeltas (Gdk.EventScroll evt, out double deltaX, out double deltaY) + { + if (!scrollDeltasNotSupported) { + try { + return gdk_event_get_scroll_deltas (evt.Handle, out deltaX, out deltaY); + } catch (EntryPointNotFoundException) { + scrollDeltasNotSupported = true; + } + } + deltaX = deltaY = 0; + return false; + } + + /// <summary>Shows a context menu.</summary> + /// <param name='menu'>The menu.</param> + /// <param name='parent'>The parent widget.</param> + /// <param name='evt'>The mouse event. May be null if triggered by keyboard.</param> + /// <param name='caret'>The caret/selection position within the parent, if the EventButton is null.</param> + public static void ShowContextMenu (Gtk.Menu menu, Gtk.Widget parent, Gdk.EventButton evt, Gdk.Rectangle caret) + { + Gtk.MenuPositionFunc posFunc = null; + + if (parent != null) { + menu.AttachToWidget (parent, null); + menu.Hidden += (sender, e) => { + if (menu.AttachWidget != null) + menu.Detach (); + }; + posFunc = delegate (Gtk.Menu m, out int x, out int y, out bool pushIn) { + Gdk.Window window = evt != null? evt.Window : parent.GdkWindow; + window.GetOrigin (out x, out y); + var alloc = parent.Allocation; + if (evt != null) { + x += (int) evt.X; + y += (int) evt.Y; + } else if (caret.X >= alloc.X && caret.Y >= alloc.Y) { + x += caret.X; + y += caret.Y + caret.Height; + } else { + x += alloc.X; + y += alloc.Y; + } + Gtk.Requisition request = m.SizeRequest (); + var screen = parent.Screen; + Gdk.Rectangle geometry = GetUsableMonitorGeometry (screen, screen.GetMonitorAtPoint (x, y)); + + //whether to push or flip menus that would extend offscreen + //FIXME: this is the correct behaviour for mac, check other platforms + bool flip_left = true; + bool flip_up = false; + + if (x + request.Width > geometry.X + geometry.Width) { + if (flip_left) { + x -= request.Width; + } else { + x = geometry.X + geometry.Width - request.Width; + } + + if (x < geometry.Left) + x = geometry.Left; + } + + if (y + request.Height > geometry.Y + geometry.Height) { + if (flip_up) { + y -= request.Height; + } else { + y = geometry.Y + geometry.Height - request.Height; + } + + if (y < geometry.Top) + y = geometry.Top; + } + + pushIn = false; + }; + } + + uint time; + uint button; + + if (evt == null) { + time = Gtk.Global.CurrentEventTime; + button = 0; + } else { + time = evt.Time; + button = evt.Button; + } + + //HACK: work around GTK menu issues on mac when passing button to menu.Popup + //some menus appear and immediately hide, and submenus don't activate + if (Platform.IsMac) { + button = 0; + } + + menu.Popup (null, null, posFunc, button, time); + } + + public static void ShowContextMenu (Gtk.Menu menu, Gtk.Widget parent, Gdk.EventButton evt) + { + ShowContextMenu (menu, parent, evt, Gdk.Rectangle.Zero); + } + + public static void ShowContextMenu (Gtk.Menu menu, Gtk.Widget parent, Gdk.Rectangle caret) + { + ShowContextMenu (menu, parent, null, caret); + } + + struct MappedKeys + { + public Gdk.Key Key; + public Gdk.ModifierType State; + public KeyboardShortcut[] Shortcuts; + } + + //introduced in GTK 2.20 + [DllImport (PangoUtil.LIBGDK, CallingConvention = CallingConvention.Cdecl)] + extern static bool gdk_keymap_add_virtual_modifiers (IntPtr keymap, ref Gdk.ModifierType state); + + //Custom patch in Mono Mac w/GTK+ 2.24.8+ + [DllImport (PangoUtil.LIBGDK, CallingConvention = CallingConvention.Cdecl)] + extern static bool gdk_quartz_set_fix_modifiers (bool fix); + + static Gdk.Keymap keymap = Gdk.Keymap.Default; + static Dictionary<ulong,MappedKeys> mappedKeys = new Dictionary<ulong,MappedKeys> (); + + /// <summary>Map raw GTK key input to work around platform bugs and decompose accelerator keys</summary> + /// <param name='evt'>The raw key event</param> + /// <param name='key'>The composed key</param> + /// <param name='state'>The composed modifiers</param> + /// <param name='shortcuts'>All the key/modifier decompositions that can be used as accelerators</param> + public static void MapKeys (Gdk.EventKey evt, out Gdk.Key key, out Gdk.ModifierType state, + out KeyboardShortcut[] shortcuts) + { + //this uniquely identifies the raw key + ulong id; + unchecked { + id = (((ulong)(uint)evt.State) | (((ulong)evt.HardwareKeycode) << 32) | (((ulong)evt.Group) << 48)); + } + + bool remapKey = Platform.IsWindows && evt.HardwareKeycode == 0; + MappedKeys mapped; + if (remapKey || !mappedKeys.TryGetValue (id, out mapped)) + mappedKeys[id] = mapped = MapKeys (evt); + + shortcuts = mapped.Shortcuts; + state = mapped.State; + key = mapped.Key; + + if (remapKey) { + key = (Gdk.Key)evt.KeyValue; + } + } + + static MappedKeys MapKeys (Gdk.EventKey evt) + { + MappedKeys mapped; + Gdk.ModifierType modifier = evt.State; + byte grp = evt.Group; + + if (GtkMinorVersion >= 20) { + gdk_keymap_add_virtual_modifiers (keymap.Handle, ref modifier); + } + + //full key mapping + uint keyval; + int effectiveGroup, level; + Gdk.ModifierType consumedModifiers; + TranslateKeyboardState (evt, modifier, grp, out keyval, out effectiveGroup, + out level, out consumedModifiers); + mapped.Key = (Gdk.Key)keyval; + mapped.State = FixMacModifiers (evt.State & ~consumedModifiers, grp); + + //decompose the key into accel combinations + var accelList = new List<KeyboardShortcut> (); + + const Gdk.ModifierType accelMods = Gdk.ModifierType.ShiftMask | Gdk.ModifierType.Mod1Mask + | Gdk.ModifierType.ControlMask | Gdk.ModifierType.SuperMask |Gdk.ModifierType.MetaMask; + + //all accels ignore the lock key + modifier &= ~Gdk.ModifierType.LockMask; + + //fully decomposed + TranslateKeyboardState (evt, Gdk.ModifierType.None, 0, + out keyval, out effectiveGroup, out level, out consumedModifiers); + accelList.Add (new KeyboardShortcut ((Gdk.Key)keyval, FixMacModifiers (modifier, grp) & accelMods)); + + //with shift composed + if ((modifier & Gdk.ModifierType.ShiftMask) != 0) { + keymap.TranslateKeyboardState (evt.HardwareKeycode, Gdk.ModifierType.ShiftMask, 0, + out keyval, out effectiveGroup, out level, out consumedModifiers); + + if (Platform.IsWindows && evt.HardwareKeycode == 0) { + keyval = (ushort)evt.KeyValue; + } + + // Prevent consumption of non-Shift modifiers (that we didn't even provide!) + consumedModifiers &= Gdk.ModifierType.ShiftMask; + + var m = FixMacModifiers ((modifier & ~consumedModifiers), grp) & accelMods; + AddIfNotDuplicate (accelList, new KeyboardShortcut ((Gdk.Key)keyval, m)); + } + + //with group 1 composed + if (grp == 1) { + TranslateKeyboardState (evt, modifier & ~Gdk.ModifierType.ShiftMask, 1, + out keyval, out effectiveGroup, out level, out consumedModifiers); + + // Prevent consumption of Shift modifier (that we didn't even provide!) + consumedModifiers &= ~Gdk.ModifierType.ShiftMask; + + var m = FixMacModifiers ((modifier & ~consumedModifiers), 0) & accelMods; + AddIfNotDuplicate (accelList, new KeyboardShortcut ((Gdk.Key)keyval, m)); + } + + //with group 1 and shift composed + if (grp == 1 && (modifier & Gdk.ModifierType.ShiftMask) != 0) { + TranslateKeyboardState (evt, modifier, 1, + out keyval, out effectiveGroup, out level, out consumedModifiers); + var m = FixMacModifiers ((modifier & ~consumedModifiers), 0) & accelMods; + AddIfNotDuplicate (accelList, new KeyboardShortcut ((Gdk.Key)keyval, m)); + } + + //and also allow the fully mapped key as an accel + AddIfNotDuplicate (accelList, new KeyboardShortcut (mapped.Key, mapped.State & accelMods)); + + mapped.Shortcuts = accelList.ToArray (); + return mapped; + } + + // Workaround for bug "Bug 688247 - Ctrl+Alt key not work on windows7 with bootcamp on a Mac Book Pro" + // Ctrl+Alt should behave like right alt key - unfortunately TranslateKeyboardState doesn't handle it. + static void TranslateKeyboardState (Gdk.EventKey evt, Gdk.ModifierType state, int group, out uint keyval, + out int effective_group, out int level, out Gdk.ModifierType consumed_modifiers) + { + uint hardware_keycode = evt.HardwareKeycode; + + if (Platform.IsWindows) { + const Gdk.ModifierType ctrlAlt = Gdk.ModifierType.ControlMask | Gdk.ModifierType.Mod1Mask; + if ((state & ctrlAlt) == ctrlAlt) { + state = (state & ~ctrlAlt) | Gdk.ModifierType.Mod2Mask; + group = 1; + } + // Case: Caps lock on + shift + key + // See: Bug 8069 - [UI Refresh] If caps lock is on, holding the shift key prevents typed characters from appearing + if (state.HasFlag (Gdk.ModifierType.ShiftMask)) { + state &= ~Gdk.ModifierType.ShiftMask; + } + } + + keymap.TranslateKeyboardState (hardware_keycode, state, group, out keyval, out effective_group, + out level, out consumed_modifiers); + + if (Platform.IsWindows && hardware_keycode == 0) { + keyval = evt.KeyValue; + } + } + + static Gdk.ModifierType FixMacModifiers (Gdk.ModifierType mod, byte grp) + { + if (!oldMacKeyHacks) + return mod; + + // Mac GTK+ maps the command key to the Mod1 modifier, which usually means alt/ + // We map this instead to meta, because the Mac GTK+ has mapped the cmd key + // to the meta key (yay inconsistency!). IMO super would have been saner. + if ((mod & Gdk.ModifierType.Mod1Mask) != 0) { + mod ^= Gdk.ModifierType.Mod1Mask; + mod |= Gdk.ModifierType.MetaMask; + } + + //some versions of GTK map opt as mod5, which converts to the virtual super modifier + if ((mod & (Gdk.ModifierType.Mod5Mask | Gdk.ModifierType.SuperMask)) != 0) { + mod ^= (Gdk.ModifierType.Mod5Mask | Gdk.ModifierType.SuperMask); + mod |= Gdk.ModifierType.Mod1Mask; + } + + // When opt modifier is active, we need to decompose this to make the command appear correct for Mac. + // In addition, we can only inspect whether the opt/alt key is pressed by examining + // the key's "group", because the Mac GTK+ treats opt as a group modifier and does + // not expose it as an actual GDK modifier. + if (grp == (byte) 1) { + mod |= Gdk.ModifierType.Mod1Mask; + } + + return mod; + } + + static void AddIfNotDuplicate<T> (List<T> list, T item) where T : IEquatable<T> + { + for (int i = 0; i < list.Count; i++) { + if (list[i].Equals (item)) + return; + } + list.Add (item); + } + + [System.Runtime.InteropServices.DllImport (PangoUtil.LIBGDK, CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr gdk_win32_drawable_get_handle (IntPtr drawable); + + enum DwmWindowAttribute + { + NcRenderingEnabled = 1, + NcRenderingPolicy, + TransitionsForceDisabled, + AllowNcPaint, + CaptionButtonBounds, + NonClientRtlLayout, + ForceIconicRepresentation, + Flip3DPolicy, + ExtendedFrameBounds, + HasIconicBitmap, + DisallowPeek, + ExcludedFromPeek, + Last, + } + + struct Win32Rect + { + public int Left, Top, Right, Bottom; + + public Win32Rect (int left, int top, int right, int bottom) + { + this.Left = left; + this.Top = top; + this.Right = right; + this.Bottom = bottom; + } + } + + [DllImport ("dwmapi.dll")] + static extern int DwmGetWindowAttribute (IntPtr hwnd, DwmWindowAttribute attribute, out Win32Rect value, int valueSize); + + [DllImport ("dwmapi.dll")] + static extern int DwmIsCompositionEnabled (out bool enabled); + + [DllImport (USER32DLL)] + static extern bool GetWindowRect (IntPtr hwnd, out Win32Rect rect); + + public static void SetImCursorLocation (Gtk.IMContext ctx, Gdk.Window clientWindow, Gdk.Rectangle cursor) + { + // work around GTK+ Bug 663096 - Windows IME position is wrong when Aero glass is enabled + // https://bugzilla.gnome.org/show_bug.cgi?id=663096 + if (Platform.IsWindows && System.Environment.OSVersion.Version.Major >= 6) { + bool enabled; + if (DwmIsCompositionEnabled (out enabled) == 0 && enabled) { + var hwnd = gdk_win32_drawable_get_handle (clientWindow.Toplevel.Handle); + Win32Rect rect; + // this module gets the WINVER=6 version of GetWindowRect, which returns the correct value + if (GetWindowRect (hwnd, out rect)) { + int x, y; + clientWindow.Toplevel.GetPosition (out x, out y); + cursor.X = cursor.X - x + rect.Left; + cursor.Y = cursor.Y - y + rect.Top - cursor.Height; + } + } + } + ctx.CursorLocation = cursor; + } + + /// <summary>X coordinate of the pixels inside the right edge of the rectangle</summary> + /// <remarks>Workaround for inconsistency of Right property between GTK# versions</remarks> + public static int RightInside (this Gdk.Rectangle rect) + { + return rect.X + rect.Width - 1; + } + + /// <summary>Y coordinate of the pixels inside the bottom edge of the rectangle</summary> + /// <remarks>Workaround for inconsistency of Bottom property between GTK# versions#</remarks> + public static int BottomInside (this Gdk.Rectangle rect) + { + return rect.Y + rect.Height - 1; + } + + /// <summary> + /// Shows or hides the shadow of the window rendered by the native toolkit + /// </summary> + public static void ShowNativeShadow (Gtk.Window window, bool show) + { + if (Platform.IsMac) { + var ptr = gdk_quartz_window_get_nswindow (window.GdkWindow.Handle); + objc_msgSend_void_bool (ptr, sel_setHasShadow, show); + } + } + + public static void UpdateNativeShadow (Gtk.Window window) + { + if (!Platform.IsMac) + return; + + var ptr = gdk_quartz_window_get_nswindow (window.GdkWindow.Handle); + objc_msgSend_IntPtr (ptr, sel_invalidateShadow); + } + + public static bool HasNSTextFieldFocus (Gdk.Window window) + { + if (Platform.IsMac) { + try { + return gdk_window_has_embedded_nsview_focus (window.Handle); + } catch (Exception e) { + return false; + } + } else { + return false; + } + } + + [DllImport (PangoUtil.LIBGTKGLUE, CallingConvention = CallingConvention.Cdecl)] + static extern void gtksharp_container_leak_fixed_marker (); + + static HashSet<Type> fixedContainerTypes; + static Dictionary<IntPtr,ForallDelegate> forallCallbacks; + static bool containerLeakFixed; + + // Works around BXC #3801 - Managed Container subclasses are incorrectly resurrected, then leak. + // It does this by registering an alternative callback for gtksharp_container_override_forall, which + // ignores callbacks if the wrapper no longer exists. This means that the objects no longer enter a + // finalized->release->dispose->re-wrap resurrection cycle. + // We use a dynamic method to access internal/private GTK# API in a performant way without having to track + // per-instance delegates. + public static void FixContainerLeak (Gtk.Container c) + { + if (containerLeakFixed) { + return; + } + + FixContainerLeak (c.GetType ()); + } + + static void FixContainerLeak (Type t) + { + if (containerLeakFixed) { + return; + } + + if (fixedContainerTypes == null) { + try { + gtksharp_container_leak_fixed_marker (); + containerLeakFixed = true; + return; + } catch (EntryPointNotFoundException) { + } + fixedContainerTypes = new HashSet<Type>(); + forallCallbacks = new Dictionary<IntPtr, ForallDelegate> (); + } + + if (!fixedContainerTypes.Add (t)) { + return; + } + + //need to fix the callback for the type and all the managed supertypes + var lookupGType = typeof (GLib.Object).GetMethod ("LookupGType", BindingFlags.Static | BindingFlags.NonPublic); + do { + var gt = (GLib.GType) lookupGType.Invoke (null, new[] { t }); + var cb = CreateForallCallback (gt.Val); + forallCallbacks[gt.Val] = cb; + gtksharp_container_override_forall (gt.Val, cb); + t = t.BaseType; + } while (fixedContainerTypes.Add (t) && t.Assembly != typeof (Gtk.Container).Assembly); + } + + static ForallDelegate CreateForallCallback (IntPtr gtype) + { + var dm = new DynamicMethod ( + "ContainerForallCallback", + typeof(void), + new Type[] { typeof(IntPtr), typeof(bool), typeof(IntPtr), typeof(IntPtr) }, + typeof(GtkWorkarounds).Module, + true); + + var invokerType = typeof(Gtk.Container.CallbackInvoker); + + //this was based on compiling a similar method and disassembling it + ILGenerator il = dm.GetILGenerator (); + var IL_002b = il.DefineLabel (); + var IL_003f = il.DefineLabel (); + var IL_0060 = il.DefineLabel (); + var label_return = il.DefineLabel (); + + var loc_container = il.DeclareLocal (typeof(Gtk.Container)); + var loc_obj = il.DeclareLocal (typeof(object)); + var loc_invoker = il.DeclareLocal (invokerType); + var loc_ex = il.DeclareLocal (typeof(Exception)); + + //check that the type is an exact match + // prevent stack overflow, because the callback on a more derived type will handle everything + il.Emit (OpCodes.Ldarg_0); + il.Emit (OpCodes.Call, typeof(GLib.ObjectManager).GetMethod ("gtksharp_get_type_id", BindingFlags.Static | BindingFlags.NonPublic)); + + il.Emit (OpCodes.Ldc_I8, gtype.ToInt64 ()); + il.Emit (OpCodes.Newobj, typeof (IntPtr).GetConstructor (new Type[] { typeof (Int64) })); + il.Emit (OpCodes.Call, typeof (IntPtr).GetMethod ("op_Equality", BindingFlags.Static | BindingFlags.Public)); + il.Emit (OpCodes.Brfalse, label_return); + + il.BeginExceptionBlock (); + il.Emit (OpCodes.Ldnull); + il.Emit (OpCodes.Stloc, loc_container); + il.Emit (OpCodes.Ldsfld, typeof (GLib.Object).GetField ("Objects", BindingFlags.Static | BindingFlags.NonPublic)); + il.Emit (OpCodes.Ldarg_0); + il.Emit (OpCodes.Box, typeof (IntPtr)); + il.Emit (OpCodes.Callvirt, typeof (System.Collections.Hashtable).GetProperty ("Item").GetGetMethod ()); + il.Emit (OpCodes.Stloc, loc_obj); + il.Emit (OpCodes.Ldloc, loc_obj); + il.Emit (OpCodes.Brfalse, IL_002b); + + var tref = typeof (GLib.Object).Assembly.GetType ("GLib.ToggleRef"); + il.Emit (OpCodes.Ldloc, loc_obj); + il.Emit (OpCodes.Castclass, tref); + il.Emit (OpCodes.Callvirt, tref.GetProperty ("Target").GetGetMethod ()); + il.Emit (OpCodes.Isinst, typeof (Gtk.Container)); + il.Emit (OpCodes.Stloc, loc_container); + + il.MarkLabel (IL_002b); + il.Emit (OpCodes.Ldloc, loc_container); + il.Emit (OpCodes.Brtrue, IL_003f); + + il.Emit (OpCodes.Ldarg_0); + il.Emit (OpCodes.Ldarg_1); + il.Emit (OpCodes.Ldarg_2); + il.Emit (OpCodes.Ldarg_3); + il.Emit (OpCodes.Call, typeof (Gtk.Container).GetMethod ("gtksharp_container_base_forall", BindingFlags.Static | BindingFlags.NonPublic)); + il.Emit (OpCodes.Br, IL_0060); + + il.MarkLabel (IL_003f); + il.Emit (OpCodes.Ldloca_S, 2); + il.Emit (OpCodes.Ldarg_2); + il.Emit (OpCodes.Ldarg_3); + il.Emit (OpCodes.Call, invokerType.GetConstructor ( + BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { typeof (IntPtr), typeof (IntPtr) }, null)); + il.Emit (OpCodes.Ldloc, loc_container); + il.Emit (OpCodes.Ldarg_1); + il.Emit (OpCodes.Ldloc, loc_invoker); + il.Emit (OpCodes.Box, invokerType); + il.Emit (OpCodes.Ldftn, invokerType.GetMethod ("Invoke")); + il.Emit (OpCodes.Newobj, typeof (Gtk.Callback).GetConstructor ( + BindingFlags.Instance | BindingFlags.Public, null, new Type[] { typeof (object), typeof (IntPtr) }, null)); + var forallMeth = typeof (Gtk.Container).GetMethod ("ForAll", + BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { typeof (bool), typeof (Gtk.Callback) }, null); + il.Emit (OpCodes.Callvirt, forallMeth); + + il.MarkLabel (IL_0060); + + il.BeginCatchBlock (typeof (Exception)); + il.Emit (OpCodes.Stloc, loc_ex); + il.Emit (OpCodes.Ldloc, loc_ex); + il.Emit (OpCodes.Ldc_I4_0); + il.Emit (OpCodes.Call, typeof (GLib.ExceptionManager).GetMethod ("RaiseUnhandledException")); + il.Emit (OpCodes.Leave, label_return); + il.EndExceptionBlock (); + + il.MarkLabel (label_return); + il.Emit (OpCodes.Ret); + + return (ForallDelegate) dm.CreateDelegate (typeof (ForallDelegate)); + } + + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + delegate void ForallDelegate (IntPtr container, bool include_internals, IntPtr cb, IntPtr data); + + [DllImport(PangoUtil.LIBGTKGLUE, CallingConvention = CallingConvention.Cdecl)] + static extern void gtksharp_container_override_forall (IntPtr gtype, ForallDelegate cb); + + const string urlRegexStr = @"(http|ftp)s?\:\/\/[\w\d\.,;_/\-~%@()+:?&^=#!]*[\w\d/]"; + static readonly Regex UrlRegex = new Regex (urlRegexStr, RegexOptions.Compiled | RegexOptions.ExplicitCapture); + + public static string MarkupLinks (string text) + { + if (GtkMinorVersion < 18) + return text; + return UrlRegex.Replace (text, MatchToUrl); + } + + static string MatchToUrl (System.Text.RegularExpressions.Match m) + { + var s = m.ToString (); + return String.Format ("<a href='{0}'>{1}</a>", s, s.Replace ("_", "__")); + } + + public static void SetLinkHandler (this Gtk.Label label, Action<string> urlHandler) + { + if (GtkMinorVersion >= 18) + new UrlHandlerClosure (urlHandler).ConnectTo (label); + } + + //create closure manually so we can apply ConnectBefore + class UrlHandlerClosure + { + Action<string> urlHandler; + + public UrlHandlerClosure (Action<string> urlHandler) + { + this.urlHandler = urlHandler; + } + + [GLib.ConnectBefore] + void HandleLink (object sender, ActivateLinkEventArgs args) + { + urlHandler (args.Url); + args.RetVal = true; + } + + public void ConnectTo (Gtk.Label label) + { + var signal = GLib.Signal.Lookup (label, "activate-link", typeof(ActivateLinkEventArgs)); + signal.AddDelegate (new EventHandler<ActivateLinkEventArgs> (HandleLink)); + } + + class ActivateLinkEventArgs : GLib.SignalArgs + { + public string Url { get { return (string)base.Args [0]; } } + } + } + + static bool canSetOverlayScrollbarPolicy = true; + + [DllImport (PangoUtil.LIBQUARTZ)] + static extern void gtk_scrolled_window_set_overlay_policy (IntPtr sw, Gtk.PolicyType hpolicy, Gtk.PolicyType vpolicy); + + [DllImport (PangoUtil.LIBQUARTZ)] + static extern void gtk_scrolled_window_get_overlay_policy (IntPtr sw, out Gtk.PolicyType hpolicy, out Gtk.PolicyType vpolicy); + + public static void SetOverlayScrollbarPolicy (Gtk.ScrolledWindow sw, Gtk.PolicyType hpolicy, Gtk.PolicyType vpolicy) + { + if (!canSetOverlayScrollbarPolicy) { + return; + } + try { + gtk_scrolled_window_set_overlay_policy (sw.Handle, hpolicy, vpolicy); + return; + } catch (DllNotFoundException) { + } catch (EntryPointNotFoundException) { + } + } + + public static void GetOverlayScrollbarPolicy (Gtk.ScrolledWindow sw, out Gtk.PolicyType hpolicy, out Gtk.PolicyType vpolicy) + { + if (!canSetOverlayScrollbarPolicy) { + hpolicy = vpolicy = 0; + return; + } + try { + gtk_scrolled_window_get_overlay_policy (sw.Handle, out hpolicy, out vpolicy); + return; + } catch (DllNotFoundException) { + } catch (EntryPointNotFoundException) { + } + hpolicy = vpolicy = 0; + canSetOverlayScrollbarPolicy = false; + } + + [DllImport (PangoUtil.LIBGTK, CallingConvention = CallingConvention.Cdecl)] + static extern bool gtk_tree_view_get_tooltip_context (IntPtr raw, ref int x, ref int y, bool keyboard_tip, out IntPtr model, out IntPtr path, IntPtr iter); + + //the GTK# version of this has 'out' instead of 'ref', preventing passing the x,y values in + public static bool GetTooltipContext (this TreeView tree, ref int x, ref int y, bool keyboardTip, + out TreeModel model, out TreePath path, out Gtk.TreeIter iter) + { + IntPtr intPtr = Marshal.AllocHGlobal (Marshal.SizeOf (typeof(TreeIter))); + IntPtr handle; + IntPtr intPtr2; + bool result = gtk_tree_view_get_tooltip_context (tree.Handle, ref x, ref y, keyboardTip, out handle, out intPtr2, intPtr); + model = TreeModelAdapter.GetObject (handle, false); + path = intPtr2 == IntPtr.Zero ? null : ((TreePath)GLib.Opaque.GetOpaque (intPtr2, typeof(TreePath), false)); + iter = TreeIter.New (intPtr); + Marshal.FreeHGlobal (intPtr); + return result; + } + + static bool supportsHiResIcons = true; + + [DllImport (PangoUtil.LIBGTK, CallingConvention = CallingConvention.Cdecl)] + static extern void gtk_icon_source_set_scale (IntPtr source, double scale); + + [DllImport (PangoUtil.LIBGTK, CallingConvention = CallingConvention.Cdecl)] + static extern void gtk_icon_source_set_scale_wildcarded (IntPtr source, bool setting); + + [DllImport (PangoUtil.LIBGTK, CallingConvention = CallingConvention.Cdecl)] + static extern double gtk_widget_get_scale_factor (IntPtr widget); + + [DllImport (PangoUtil.LIBGDK, CallingConvention = CallingConvention.Cdecl)] + static extern double gdk_screen_get_monitor_scale_factor (IntPtr widget, int monitor); + + [DllImport (PangoUtil.LIBGOBJECT, CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr g_object_get_data (IntPtr source, string name); + + [DllImport (PangoUtil.LIBGTK, CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr gtk_icon_set_render_icon_scaled (IntPtr handle, IntPtr style, int direction, int state, int size, IntPtr widget, IntPtr intPtr, ref double scale); + + public static bool SetSourceScale (Gtk.IconSource source, double scale) + { + if (!supportsHiResIcons) + return false; + + try { + gtk_icon_source_set_scale (source.Handle, scale); + return true; + } catch (DllNotFoundException) { + } catch (EntryPointNotFoundException) { + } + supportsHiResIcons = false; + return false; + } + + public static bool SetSourceScaleWildcarded (Gtk.IconSource source, bool setting) + { + if (!supportsHiResIcons) + return false; + + try { + gtk_icon_source_set_scale_wildcarded (source.Handle, setting); + return true; + } catch (DllNotFoundException) { + } catch (EntryPointNotFoundException) { + } + supportsHiResIcons = false; + return false; + } + + public static Gdk.Pixbuf Get2xVariant (Gdk.Pixbuf px) + { + if (!supportsHiResIcons) + return null; + + try { + IntPtr res = g_object_get_data (px.Handle, "gdk-pixbuf-2x-variant"); + if (res != IntPtr.Zero && res != px.Handle) + return (Gdk.Pixbuf) GLib.Object.GetObject (res); + else + return null; + } catch (DllNotFoundException) { + } catch (EntryPointNotFoundException) { + } + supportsHiResIcons = false; + return null; + } + + public static void Set2xVariant (Gdk.Pixbuf px, Gdk.Pixbuf variant2x) + { + } + + public static double GetScaleFactor (Gtk.Widget w) + { + if (!supportsHiResIcons) + return 1; + + try { + return gtk_widget_get_scale_factor (w.Handle); + } catch (DllNotFoundException) { + } catch (EntryPointNotFoundException) { + } + supportsHiResIcons = false; + return 1; + } + + public static double GetScaleFactor (this Gdk.Screen screen, int monitor) + { + if (!supportsHiResIcons) + return 1; + + try { + return gdk_screen_get_monitor_scale_factor (screen.Handle, monitor); + } catch (DllNotFoundException) { + } catch (EntryPointNotFoundException) { + } + supportsHiResIcons = false; + return 1; + } + + public static double GetScaleFactor () + { + return GetScaleFactor (Gdk.Screen.Default, 0); + } + + public static double GetPixelScale () + { + if (Platform.IsWindows) + return GetScaleFactor (); + else + return 1d; + } + + public static Gdk.Pixbuf RenderIcon (this Gtk.IconSet iconset, Gtk.Style style, Gtk.TextDirection direction, Gtk.StateType state, Gtk.IconSize size, Gtk.Widget widget, string detail, double scale) + { + if (scale == 1d) + return iconset.RenderIcon (style, direction, state, size, widget, detail); + + if (!supportsHiResIcons) + return null; + + try { + IntPtr intPtr = GLib.Marshaller.StringToPtrGStrdup (detail); + IntPtr o = gtk_icon_set_render_icon_scaled (iconset.Handle, (style != null) ? style.Handle : IntPtr.Zero, (int)direction, (int)state, (int)size, (widget != null) ? widget.Handle : IntPtr.Zero, intPtr, ref scale); + Gdk.Pixbuf result = (Gdk.Pixbuf) GLib.Object.GetObject (o); + GLib.Marshaller.Free (intPtr); + return result; + } catch (DllNotFoundException) { + } catch (EntryPointNotFoundException) { + } + supportsHiResIcons = false; + return null; + } + } + + public struct KeyboardShortcut : IEquatable<KeyboardShortcut> + { + public static readonly KeyboardShortcut Empty = new KeyboardShortcut ((Gdk.Key) 0, (Gdk.ModifierType) 0); + + Gdk.ModifierType modifier; + Gdk.Key key; + + public KeyboardShortcut (Gdk.Key key, Gdk.ModifierType modifier) + { + this.modifier = modifier; + this.key = key; + } + + public Gdk.Key Key { + get { return key; } + } + + public Gdk.ModifierType Modifier { + get { return modifier; } + } + + public bool IsEmpty { + get { return Key == (Gdk.Key) 0; } + } + + public override bool Equals (object obj) + { + return obj is KeyboardShortcut && this.Equals ((KeyboardShortcut) obj); + } + + public override int GetHashCode () + { + //FIXME: we're only using a few bits of mod and mostly the lower bits of key - distribute it better + return (int) Key ^ (int) Modifier; + } + + public bool Equals (KeyboardShortcut other) + { + return other.Key == Key && other.Modifier == Modifier; + } + } + +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/HPanedThin.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/HPanedThin.cs index d1de2f1c9f..a4f1fec7b4 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/HPanedThin.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/HPanedThin.cs @@ -37,7 +37,7 @@ namespace MonoDevelop.Components public HPanedThin () { - Mono.TextEditor.GtkWorkarounds.FixContainerLeak (this); + GtkWorkarounds.FixContainerLeak (this); handle = new CustomPanedHandle (this); handle.Parent = this; } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/HeaderBox.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/HeaderBox.cs index 6edbdf9213..c34e0eda46 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/HeaderBox.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/HeaderBox.cs @@ -25,7 +25,6 @@ // THE SOFTWARE. using System; using Gtk; -using Mono.TextEditor; namespace MonoDevelop.Components { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/HelperMethods.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/HelperMethods.cs new file mode 100644 index 0000000000..5b758e5a50 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/HelperMethods.cs @@ -0,0 +1,136 @@ +// HelperMethods.cs +// +// Cut & paste from PangoCairoHelper. +// +// Author: +// Aaron Bockover <abockover@novell.com> +// Mike Krüger <mkrueger@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.Linq; +using System.Runtime.InteropServices; +using System.Collections.Generic; + +namespace MonoDevelop.Components +{ + public static class HelperMethods + { + [DllImport(PangoUtil.LIBPANGOCAIRO, CallingConvention=CallingConvention.Cdecl)] + static extern void pango_cairo_show_layout (IntPtr cr, IntPtr layout); + + public static void ShowLayout (this Cairo.Context cr, Pango.Layout layout) + { + pango_cairo_show_layout (cr == null ? IntPtr.Zero : cr.Handle, layout == null ? IntPtr.Zero : layout.Handle); + } + + [DllImport(PangoUtil.LIBPANGOCAIRO, CallingConvention=CallingConvention.Cdecl)] + static extern IntPtr pango_cairo_create_layout (IntPtr cr); + + public static Pango.Layout CreateLayout (this Cairo.Context cr) + { + IntPtr raw_ret = pango_cairo_create_layout (cr == null ? IntPtr.Zero : cr.Handle); + return GLib.Object.GetObject (raw_ret) as Pango.Layout; + } + + [DllImport(PangoUtil.LIBPANGOCAIRO, CallingConvention=CallingConvention.Cdecl)] + static extern void pango_cairo_layout_path (IntPtr cr, IntPtr layout); + + public static void LayoutPath (this Cairo.Context cr, Pango.Layout layout) + { + pango_cairo_layout_path (cr == null ? IntPtr.Zero : cr.Handle, layout == null ? IntPtr.Zero : layout.Handle); + } + + [DllImport(PangoUtil.LIBPANGOCAIRO, CallingConvention=CallingConvention.Cdecl)] + static extern void pango_cairo_context_set_resolution (IntPtr pango_context, double dpi); + + public static void ContextSetResolution (this Pango.Context context, double dpi) + { + pango_cairo_context_set_resolution (context == null ? IntPtr.Zero : context.Handle, dpi); + } + + [DllImport(PangoUtil.LIBPANGOCAIRO, CallingConvention=CallingConvention.Cdecl)] + static extern IntPtr pango_layout_get_context (IntPtr layout); + + public static string GetColorString (Gdk.Color color) + { + return string.Format ("#{0:X02}{1:X02}{2:X02}", color.Red / 256, color.Green / 256, color.Blue / 256); + } + + public static Pango.Context LayoutGetContext (this Pango.Layout layout) + { + IntPtr handle = pango_layout_get_context (layout.Handle); + return handle.Equals (IntPtr.Zero) ? null : GLib.Object.GetObject (handle) as Pango.Context; + } + + public static void DrawLine (this Cairo.Context cr, Cairo.Color color, double x1, double y1, double x2, double y2) + { + cr.SetSourceColor (color); + cr.MoveTo (x1, y1); + cr.LineTo (x2, y2); + cr.Stroke (); + } + + public static void Line (this Cairo.Context cr, double x1, double y1, double x2, double y2) + { + cr.MoveTo (x1, y1); + cr.LineTo (x2, y2); + } + + public static void SharpLineX (this Cairo.Context cr, double x1, double y1, double x2, double y2) + { + cr.MoveTo (x1 + 0.5, y1); + cr.LineTo (x2 + 0.5, y2); + } + + public static void SharpLineY (this Cairo.Context cr, double x1, double y1, double x2, double y2) + { + cr.MoveTo (x1, y1 + 0.5); + cr.LineTo (x2, y2 + 0.5); + } + + public static void SetSourceColor (this Cairo.Context cr, Cairo.Color color) + { + cr.SetSourceRGBA (color.R, color.G, color.B, color.A); + } + + //this is needed for building against old Mono.Cairo versions + [Obsolete] + public static void SetSource (this Cairo.Context cr, Cairo.Pattern pattern) + { + cr.Pattern = pattern; + } + + [Obsolete] + public static Cairo.Surface GetTarget (this Cairo.Context cr) + { + return cr.Target; + } + + [Obsolete] + public static void Dispose (this Cairo.Context cr) + { + ((IDisposable)cr).Dispose (); + } + } +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/HslColor.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/HslColor.cs new file mode 100644 index 0000000000..7e56d747d1 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/HslColor.cs @@ -0,0 +1,292 @@ +// +// HslColor.cs +// +// Author: +// Mike Krüger <mkrueger@novell.com> +// +// Copyright (c) 2009 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 Gdk; +using System.Collections.Generic; + +namespace MonoDevelop.Components +{ + public struct HslColor + { + public double H { + get; + set; + } + + public double S { + get; + set; + } + + public double L { + get; + set; + } + + public double Alpha { + get; + set; + } + + void ToRgb(out double r, out double g, out double b) + { + if (L == 0) { + r = g = b = 0; + return; + } + + if (S == 0) { + r = g = b = L; + } else { + double temp2 = L <= 0.5 ? L * (1.0 + S) : L + S -(L * S); + double temp1 = 2.0 * L - temp2; + + double[] t3 = new double[] { H + 1.0 / 3.0, H, H - 1.0 / 3.0}; + double[] clr= new double[] { 0, 0, 0}; + for (int i = 0; i < 3; i++) { + if (t3[i] < 0) + t3[i] += 1.0; + if (t3[i] > 1) + t3[i]-=1.0; + if (6.0 * t3[i] < 1.0) + clr[i] = temp1 + (temp2 - temp1) * t3[i] * 6.0; + else if (2.0 * t3[i] < 1.0) + clr[i] = temp2; + else if (3.0 * t3[i] < 2.0) + clr[i] = (temp1 + (temp2 - temp1) * ((2.0 / 3.0) - t3[i]) * 6.0); + else + clr[i] = temp1; + } + + r = clr[0]; + g = clr[1]; + b = clr[2]; + } + } + + public static implicit operator Color (HslColor hsl) + { + double r = 0, g = 0, b = 0; + hsl.ToRgb (out r, out g, out b); + return new Color ((byte)(255 * r), + (byte)(255 * g), + (byte)(255 * b)); + } + + public static implicit operator Cairo.Color (HslColor hsl) + { + double r = 0, g = 0, b = 0; + hsl.ToRgb (out r, out g, out b); + return new Cairo.Color (r, g, b, hsl.Alpha); + } + + public static implicit operator HslColor (Color color) + { + return new HslColor (color); + } + + public static implicit operator HslColor (Cairo.Color color) + { + return new HslColor (color); + } + + #if MAC + + public static implicit operator HslColor (AppKit.NSColor color) + { + return new HslColor ((double)color.RedComponent, (double)color.GreenComponent, (double)color.BlueComponent); + } + + public static implicit operator AppKit.NSColor (HslColor hsl) + { + double r = 0, g = 0, b = 0; + hsl.ToRgb (out r, out g, out b); + return AppKit.NSColor.FromDeviceRgba ((nfloat)r, (nfloat)g, (nfloat)b, (nfloat)hsl.Alpha); + } + + + public static implicit operator CoreGraphics.CGColor (HslColor hsl) + { + double r = 0, g = 0, b = 0; + hsl.ToRgb (out r, out g, out b); + return new CoreGraphics.CGColor ((nfloat)r, (nfloat)g, (nfloat)b, (nfloat)hsl.Alpha); + } + #endif + + + public static HslColor FromHsl (double h, double s, double l) + { + return new HslColor { + H = h, + S = s, + L = l, + Alpha = 1.0d + }; + } + + public uint ToPixel () + { + double r, g, b; + ToRgb(out r, out g, out b); + uint rv = (uint)(r * 255); + uint gv = (uint)(g * 255); + uint bv = (uint)(b * 255); + return rv << 16 | gv << 8 | bv; + } + + public static HslColor FromPixel (uint pixel) + { + var r = ((pixel >> 16) & 0xFF) / 255.0; + var g = ((pixel >> 8) & 0xFF) / 255.0; + var b = (pixel & 0xFF) / 255.0; + return new HslColor (r, g, b); + } + + public HslColor (double r, double g, double b, double a = 1.0) : this () + { + double v = System.Math.Max (r, g); + v = System.Math.Max (v, b); + + double m = System.Math.Min (r, g); + m = System.Math.Min (m, b); + + this.L = (m + v) / 2.0; + if (this.L <= 0.0) + return; + double vm = v - m; + this.S = vm; + + if (this.S > 0.0) { + this.S /= (this.L <= 0.5) ? (v + m) : (2.0 - v - m); + } else { + return; + } + + double r2 = (v - r) / vm; + double g2 = (v - g) / vm; + double b2 = (v - b) / vm; + + if (r == v) { + this.H = (g == m ? 5.0 + b2 : 1.0 - g2); + } else if (g == v) { + this.H = (b == m ? 1.0 + r2 : 3.0 - b2); + } else { + this.H = (r == m ? 3.0 + g2 : 5.0 - r2); + } + this.H /= 6.0; + + this.Alpha = a; + } + + public HslColor (Color color) : this (color.Red / (double)ushort.MaxValue, color.Green / (double)ushort.MaxValue, color.Blue / (double)ushort.MaxValue) + { + Alpha = 1.0; + } + + public HslColor (Cairo.Color color) : this (color.R, color.G, color.B, color.A) + { + } + + public static HslColor Parse (string color) + { + Gdk.Color col = new Gdk.Color (0, 0, 0); + Gdk.Color.Parse (color, ref col); + return (HslColor)col; + } + + public static double Brightness (HslColor c) + { + return Brightness ((Cairo.Color)c); + } + + public static double Brightness (Cairo.Color c) + { + double r = c.R; + double g = c.G; + double b = c.B; + return System.Math.Sqrt (r * .241 + g * .691 + b * .068); + } + + public static double Brightness (Gdk.Color c) + { + double r = c.Red / (double)ushort.MaxValue; + double g = c.Green / (double)ushort.MaxValue; + double b = c.Blue / (double)ushort.MaxValue; + return System.Math.Sqrt (r * .241 + g * .691 + b * .068); + } + + public static List<HslColor> GenerateHighlightColors (HslColor backGround, HslColor foreGround, int n) + { + double bgH = (backGround.H == 0 && backGround.S == 0) ? 2 / 3.0 : backGround.H; + var result = new List<HslColor> (); + for (int i = 0; i < n; i++) { + double h = bgH + (i + 1.0) / (double)n; + + // for monochromatic backround the h value doesn't matter + if (i + 1 == n && !(backGround.H == 0 && backGround.S == 0)) + h = bgH + 0.5; + + if (h > 1.0) + h -= 1.0; + + double s = 0.85; + double l = 0.5; + if (backGround.H == 0 && backGround.S == 0 && backGround.L < 0.5) + l = 0.8; + result.Add (HslColor.FromHsl (h, s, l)); + } + return result; + } + + public override string ToString () + { + return string.Format ("[HslColor: H={0}, S={1}, L={2}, A={3}]", H, S, L, Alpha); + } + + public string ToPangoString () + { + var resultColor = (Cairo.Color)this; + return string.Format ("#{0:x2}{1:x2}{2:x2}", + (int)(resultColor.R * 255), + (int)(resultColor.G * 255), + (int)(resultColor.B * 255)); + } + + + public string ToMarkup () + { + if (Alpha == 1.0) + return ToPangoString (); + var resultColor = (Cairo.Color)this; + return string.Format ("#{0:x2}{1:x2}{2:x2}{3:x2}", + (int)(resultColor.R * 255), + (int)(resultColor.G * 255), + (int)(resultColor.B * 255), + (int)(resultColor.A * 255)); + } + } +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/ImageView.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/ImageView.cs index 5851edf92f..8b9bc5a851 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/ImageView.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/ImageView.cs @@ -70,7 +70,7 @@ namespace MonoDevelop.Components } double IconScale { - get { return Mono.TextEditor.GtkWorkarounds.GetPixelScale (); } + get { return GtkWorkarounds.GetPixelScale (); } } protected override void OnSizeRequested (ref Gtk.Requisition requisition) diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/MiniButton.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/MiniButton.cs index 34a646b8e4..715045bf8c 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/MiniButton.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/MiniButton.cs @@ -26,7 +26,6 @@ using System; using Gtk; -using Mono.TextEditor; namespace MonoDevelop.Components { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/PangoUtil.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/PangoUtil.cs new file mode 100644 index 0000000000..86de702f7f --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/PangoUtil.cs @@ -0,0 +1,260 @@ +// +// PangoUtils.cs +// +// Author: +// Michael Hutchinson <mhutchinson@novell.com> +// +// Copyright (c) 2010 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 Gtk; +using System.Runtime.InteropServices; + +namespace MonoDevelop.Components +{ + public static class PangoUtil + { + internal const string LIBGTK = "libgtk-win32-2.0-0.dll"; + internal const string LIBATK = "libatk-1.0-0.dll"; + internal const string LIBGLIB = "libglib-2.0-0.dll"; + internal const string LIBGDK = "libgdk-win32-2.0-0.dll"; + internal const string LIBGOBJECT = "libgobject-2.0-0.dll"; + internal const string LIBPANGO = "libpango-1.0-0.dll"; + internal const string LIBPANGOCAIRO = "libpangocairo-1.0-0.dll"; + internal const string LIBQUARTZ = "libgtk-quartz-2.0.dylib"; + internal const string LIBGTKGLUE = "gtksharpglue-2"; + + /// <summary> + /// This doesn't leak Pango layouts, unlike some other ways to create them in GTK# <= 2.12.11 + /// </summary> + public static Pango.Layout CreateLayout (Widget widget) + { + var ptr = gtk_widget_create_pango_layout (widget.Handle, IntPtr.Zero); + return ptr == IntPtr.Zero? null : new Pango.Layout (ptr); + } + + public static Pango.Layout CreateLayout (Widget widget, string text) + { + IntPtr textPtr = text == null? IntPtr.Zero : GLib.Marshaller.StringToPtrGStrdup (text); + + var ptr = gtk_widget_create_pango_layout (widget.Handle, textPtr); + + if (textPtr != IntPtr.Zero) + GLib.Marshaller.Free (textPtr); + + return ptr == IntPtr.Zero? null : new Pango.Layout (ptr); + } + + public static Pango.Layout CreateLayout (PrintContext context) + { + var ptr = gtk_print_context_create_pango_layout (context.Handle); + return ptr == IntPtr.Zero? null : new Pango.Layout (ptr); + } + + [DllImport (LIBGTK, CallingConvention=CallingConvention.Cdecl)] + static extern IntPtr gtk_widget_create_pango_layout (IntPtr widget, IntPtr text); + + [DllImport (LIBGTK, CallingConvention=CallingConvention.Cdecl)] + static extern IntPtr gtk_print_context_create_pango_layout (IntPtr context); + } + + /// <summary> + /// This creates a Pango list and applies attributes to it with *much* less overhead than the GTK# version. + /// </summary> + class FastPangoAttrList : IDisposable + { + IntPtr list; + + public FastPangoAttrList () + { + list = pango_attr_list_new (); + } + + public void AddStyleAttribute (Pango.Style style, uint start, uint end) + { + Add (pango_attr_style_new (style), start, end); + } + + public void AddWeightAttribute (Pango.Weight weight, uint start, uint end) + { + Add (pango_attr_weight_new (weight), start, end); + } + + public void AddForegroundAttribute (Gdk.Color color, uint start, uint end) + { + Add (pango_attr_foreground_new (color.Red, color.Green, color.Blue), start, end); + } + + public void AddBackgroundAttribute (Gdk.Color color, uint start, uint end) + { + Add (pango_attr_background_new (color.Red, color.Green, color.Blue), start, end); + } + + public void AddUnderlineAttribute (Pango.Underline underline, uint start, uint end) + { + Add (pango_attr_underline_new (underline), start, end); + } + + void Add (IntPtr attribute, uint start, uint end) + { + unsafe { + PangoAttribute *attPtr = (PangoAttribute *) attribute; + attPtr->start_index = start; + attPtr->end_index = end; + } + pango_attr_list_insert (list, attribute); + } + + /// <summary> + /// Like Splice, except it only offsets/clamps the inserted items, doesn't affect items already in the list. + /// </summary> + public void InsertOffsetList (Pango.AttrList atts, uint startOffset, uint endOffset) + { + //HACK: atts.Iterator.Attrs broken (throws NRE), so manually P/Invoke + var iter = pango_attr_list_get_iterator (atts.Handle); + try { + do { + IntPtr list = pango_attr_iterator_get_attrs (iter); + try { + int len = g_slist_length (list); + for (uint i = 0; i < len; i++) { + IntPtr val = g_slist_nth_data (list, i); + AddOffsetCopy (val, startOffset, endOffset); + } + } finally { + g_slist_free (list); + } + } while (pango_attr_iterator_next (iter)); + } finally { + pango_attr_iterator_destroy (iter); + } + } + + void AddOffsetCopy (IntPtr attr, uint startOffset, uint endOffset) + { + var copy = pango_attribute_copy (attr); + unsafe { + PangoAttribute *attPtr = (PangoAttribute *) copy; + attPtr->start_index = startOffset + attPtr->start_index; + attPtr->end_index = System.Math.Min (endOffset, startOffset + attPtr->end_index); + } + pango_attr_list_insert (list, copy); + } + + [DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)] + static extern IntPtr pango_attr_style_new (Pango.Style style); + + [DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)] + static extern IntPtr pango_attr_stretch_new (Pango.Stretch stretch); + + [DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)] + static extern IntPtr pango_attr_weight_new (Pango.Weight weight); + + [DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)] + static extern IntPtr pango_attr_foreground_new (ushort red, ushort green, ushort blue); + + [DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)] + static extern IntPtr pango_attr_background_new (ushort red, ushort green, ushort blue); + + [DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)] + static extern IntPtr pango_attr_underline_new (Pango.Underline underline); + + [DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)] + static extern IntPtr pango_attr_list_new (); + + [DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)] + static extern void pango_attr_list_unref (IntPtr list); + + [DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)] + static extern void pango_attr_list_insert (IntPtr list, IntPtr attr); + + [DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)] + static extern void pango_layout_set_attributes (IntPtr layout, IntPtr attrList); + + [DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)] + static extern void pango_attr_list_splice (IntPtr attr_list, IntPtr other, Int32 pos, Int32 len); + + [DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)] + static extern IntPtr pango_attribute_copy (IntPtr attr); + + [DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)] + static extern IntPtr pango_attr_list_get_iterator (IntPtr list); + + [DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)] + static extern bool pango_attr_iterator_next (IntPtr iterator); + + [DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)] + static extern void pango_attr_iterator_destroy (IntPtr iterator); + + [DllImport (PangoUtil.LIBPANGO, CallingConvention=CallingConvention.Cdecl)] + static extern IntPtr pango_attr_iterator_get_attrs (IntPtr iterator); + + [DllImport (PangoUtil.LIBGLIB, CallingConvention = CallingConvention.Cdecl)] + private static extern int g_slist_length (IntPtr l); + + [DllImport (PangoUtil.LIBGLIB, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr g_slist_nth_data (IntPtr l, uint n); + + [DllImport (PangoUtil.LIBGLIB, CallingConvention = CallingConvention.Cdecl)] + private static extern void g_slist_free (IntPtr l); + + public void Splice (Pango.AttrList attrs, int pos, int len) + { + pango_attr_list_splice (list, attrs.Handle, pos, len); + } + + public void AssignTo (Pango.Layout layout) + { + pango_layout_set_attributes (layout.Handle, list); + } + + [StructLayout (LayoutKind.Sequential)] + struct PangoAttribute + { + public IntPtr klass; + public uint start_index; + public uint end_index; + } + + public void Dispose () + { + if (list != IntPtr.Zero) { + GC.SuppressFinalize (this); + Destroy (); + } + } + + //NOTE: the list destroys all its attributes when the ref count reaches zero + void Destroy () + { + pango_attr_list_unref (list); + list = IntPtr.Zero; + } + + ~FastPangoAttrList () + { + GLib.Idle.Add (delegate { + Destroy (); + return false; + }); + } + } +}
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/PathBar.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/PathBar.cs index 8ff221923e..d90d8d8a81 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/PathBar.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/PathBar.cs @@ -25,15 +25,13 @@ // THE SOFTWARE. using System; -using System.Collections.Generic; using System.Linq; using Gtk; using Gdk; using MonoDevelop.Ide; using MonoDevelop.Ide.Gui; -using Mono.TextEditor; -using ICSharpCode.NRefactory; +using MonoDevelop.Core.Text; namespace MonoDevelop.Components { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/PopoverWindow.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/PopoverWindow.cs index 04d0d9f32b..97423f16c6 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/PopoverWindow.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/PopoverWindow.cs @@ -27,7 +27,6 @@ using System; using Gtk; using MonoDevelop.Ide; using MonoDevelop.Ide.Gui; -using Mono.TextEditor; using Gdk; using Xwt.Motion; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/PopoverWindowTheme.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/PopoverWindowTheme.cs index d477f4700b..29bf1dcc25 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/PopoverWindowTheme.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/PopoverWindowTheme.cs @@ -27,8 +27,9 @@ using System; using Gtk; using MonoDevelop.Ide; using MonoDevelop.Ide.Gui; -using Mono.TextEditor; using Gdk; +using MonoDevelop.Ide.Editor; +using MonoDevelop.Ide.Editor.Highlighting; namespace MonoDevelop.Components { @@ -264,7 +265,7 @@ namespace MonoDevelop.Components Font = Pango.FontDescription.FromString ("Normal"); } - public void SetSchemeColors (Mono.TextEditor.Highlighting.ColorScheme scheme) + public void SetSchemeColors (ColorScheme scheme) { TopColor = scheme.TooltipText.Background.AddLight (0.03); BottomColor = scheme.TooltipText.Background; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/SearchEntry.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/SearchEntry.cs index 44bd4e78ed..188f400dd3 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/SearchEntry.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/SearchEntry.cs @@ -31,7 +31,6 @@ using System; using Gtk; using MonoDevelop.Core; using MonoDevelop.Ide.Gui; -using Mono.TextEditor; namespace MonoDevelop.Components { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/SectionList.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/SectionList.cs index 02688602d0..6ea647114b 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/SectionList.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/SectionList.cs @@ -29,7 +29,6 @@ using System.Collections.Generic; using Gtk; using Gdk; using Cairo; -using Mono.TextEditor; namespace MonoDevelop.Components { @@ -91,7 +90,7 @@ namespace MonoDevelop.Components public SectionList () { - Mono.TextEditor.GtkWorkarounds.FixContainerLeak (this); + GtkWorkarounds.FixContainerLeak (this); this.WidgetFlags |= WidgetFlags.NoWindow; WidthRequest = 100; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/Tabstrip.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/Tabstrip.cs index d6c04f907a..a60edb358e 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/Tabstrip.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/Tabstrip.cs @@ -30,7 +30,6 @@ using System.ComponentModel; using System.Drawing.Design; using Cairo; using Gtk; -using Mono.TextEditor; using System.Linq; namespace MonoDevelop.Components diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/TooltipWindow.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/TooltipWindow.cs index e5685f27af..95036e0cbe 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/TooltipWindow.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/TooltipWindow.cs @@ -26,12 +26,10 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // -using System; using MonoDevelop.Ide; using Gtk; using Gdk; -using Mono.TextEditor.PopupWindow; namespace MonoDevelop.Components { @@ -41,7 +39,7 @@ namespace MonoDevelop.Components public string LinkColor { get { - var color = Mono.TextEditor.HslColor.GenerateHighlightColors (Style.Background (State), Style.Text (State), 3)[2]; + var color = HslColor.GenerateHighlightColors (Style.Background (State), Style.Text (State), 3)[2]; return color.ToPangoString (); } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/VPanedThin.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/VPanedThin.cs index 838c04f970..ac607fda01 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/VPanedThin.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/VPanedThin.cs @@ -34,7 +34,7 @@ namespace MonoDevelop.Components public VPanedThin () { - Mono.TextEditor.GtkWorkarounds.FixContainerLeak (this); + GtkWorkarounds.FixContainerLeak (this); handle = new CustomPanedHandle (this); handle.Parent = this; } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/WindowTransparencyDecorator.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/WindowTransparencyDecorator.cs new file mode 100644 index 0000000000..9b8e8526d8 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/WindowTransparencyDecorator.cs @@ -0,0 +1,116 @@ +// +// WindowTransparencyDecorator.cs +// +// Author: +// Michael Hutchinson <mhutch@xamarin.com> +// +// Based on code derived from Banshee.Widgets.EllipsizeLabel +// by Aaron Bockover (aaron@aaronbock.net) +// +// Copyright (C) 2005-2008 Novell, Inc (http://www.novell.com) +// Copyright (C) 2012 Xamarin Inc (http://www.xamarin.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.Reflection; +using System.Runtime.InteropServices; + +using Gtk; +using Gdk; + +namespace MonoDevelop.Components +{ + public class WindowTransparencyDecorator + { + Gtk.Window window; + bool semiTransparent; + bool snooperInstalled; + uint snooperID; + const double opacity = 0.2; + + WindowTransparencyDecorator (Gtk.Window window) + { + this.window = window; + + window.Shown += ShownHandler; + window.Hidden += HiddenHandler; + window.Destroyed += DestroyedHandler; + } + + public static WindowTransparencyDecorator Attach (Gtk.Window window) + { + return new WindowTransparencyDecorator (window); + } + + public void Detach () + { + if (window == null) + return; + + //remove the snooper + HiddenHandler (null, null); + + //annul allreferences between this and the window + window.Shown -= ShownHandler; + window.Hidden -= HiddenHandler; + window.Destroyed -= DestroyedHandler; + window = null; + } + + void ShownHandler (object sender, EventArgs args) + { + if (!snooperInstalled) + snooperID = Gtk.Key.SnooperInstall (TransparencyKeySnooper); + snooperInstalled = true; + + //NOTE: we unset transparency when showing, instead of when hiding + //because the latter case triggers a metacity+compositing bug that shows the window again + SemiTransparent = false; + } + + void HiddenHandler (object sender, EventArgs args) + { + if (snooperInstalled) + Gtk.Key.SnooperRemove (snooperID); + snooperInstalled = false; + } + + void DestroyedHandler (object sender, EventArgs args) + { + Detach (); + } + + int TransparencyKeySnooper (Gtk.Widget widget, EventKey evnt) + { + if (evnt != null && evnt.Key == Gdk.Key.Control_L || evnt.Key == Gdk.Key.Control_R) + SemiTransparent = (evnt.Type == Gdk.EventType.KeyPress); + return 0; //FALSE + } + + bool SemiTransparent { + set { + if (semiTransparent != value) { + semiTransparent = value; + window.Opacity = semiTransparent? opacity : 1.0; + } + } + } + } +} |