diff options
author | Lluis Sanchez <lluis@novell.com> | 2010-03-17 15:35:28 +0300 |
---|---|---|
committer | Lluis Sanchez <lluis@novell.com> | 2010-03-17 15:35:28 +0300 |
commit | 8fa37870e9cc22ffccdd494fa951b2c3807d7978 (patch) | |
tree | bda14806802947c51676c183b08f166878964c40 /main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking | |
parent | f1a8582658af8aeb0f6fa459965a2e4d0684c347 (diff) | |
parent | 585086f0ea0a49166046bb8f48d2def87907d0e0 (diff) |
Merged MD.Projects into MD.Core, and MD.Projects.Gui, MD.Core.Gui and MD.Components into MD.Ide.
svn path=/trunk/monodevelop/; revision=153730
Diffstat (limited to 'main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking')
25 files changed, 6200 insertions, 0 deletions
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/AutoHideBox.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/AutoHideBox.cs new file mode 100644 index 0000000000..2b181c0c96 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/AutoHideBox.cs @@ -0,0 +1,403 @@ +// +// AutoHideBox.cs +// +// Author: +// Lluis Sanchez Gual +// + +// +// Copyright (C) 2007 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 Gdk; + +namespace MonoDevelop.Components.Docking +{ + class AutoHideBox: DockFrameTopLevel + { + static Gdk.Cursor resizeCursorW = new Gdk.Cursor (Gdk.CursorType.SbHDoubleArrow); + static Gdk.Cursor resizeCursorH = new Gdk.Cursor (Gdk.CursorType.SbVDoubleArrow); + + bool resizing; + int resizePos; + int origSize; + int origPos; + bool horiz; + bool startPos; + DockFrame frame; + bool animating; + int targetSize; + int targetPos; + ScrollableContainer scrollable; + Gtk.PositionType position; + bool disposed; + bool insideGrip; + + const int gripSize = 8; + + public AutoHideBox (DockFrame frame, DockItem item, Gtk.PositionType pos, int size) + { + this.position = pos; + this.frame = frame; + this.targetSize = size; + horiz = pos == PositionType.Left || pos == PositionType.Right; + startPos = pos == PositionType.Top || pos == PositionType.Left; + Events = Events | Gdk.EventMask.EnterNotifyMask | Gdk.EventMask.LeaveNotifyMask; + + Box fr; + CustomFrame cframe = new CustomFrame (); + switch (pos) { + case PositionType.Left: cframe.SetMargins (1, 1, 0, 1); break; + case PositionType.Right: cframe.SetMargins (1, 1, 1, 0); break; + case PositionType.Top: cframe.SetMargins (0, 1, 1, 1); break; + case PositionType.Bottom: cframe.SetMargins (1, 0, 1, 1); break; + } + EventBox sepBox = new EventBox (); + cframe.Add (sepBox); + + if (horiz) { + fr = new HBox (); + sepBox.Realized += delegate { sepBox.GdkWindow.Cursor = resizeCursorW; }; + sepBox.WidthRequest = gripSize; + } else { + fr = new VBox (); + sepBox.Realized += delegate { sepBox.GdkWindow.Cursor = resizeCursorH; }; + sepBox.HeightRequest = gripSize; + } + + sepBox.Events = EventMask.AllEventsMask; + + if (pos == PositionType.Left || pos == PositionType.Top) + fr.PackEnd (cframe, false, false, 0); + else + fr.PackStart (cframe, false, false, 0); + + Add (fr); + ShowAll (); + Hide (); + + scrollable = new ScrollableContainer (); + scrollable.ScrollMode = false; + scrollable.Show (); + + item.Widget.Show (); + scrollable.Add (item.Widget); + fr.PackStart (scrollable, true, true, 0); + + sepBox.ButtonPressEvent += OnSizeButtonPress; + sepBox.ButtonReleaseEvent += OnSizeButtonRelease; + sepBox.MotionNotifyEvent += OnSizeMotion; + sepBox.ExposeEvent += OnGripExpose; + sepBox.EnterNotifyEvent += delegate { insideGrip = true; sepBox.QueueDraw (); }; + sepBox.LeaveNotifyEvent += delegate { insideGrip = false; sepBox.QueueDraw (); }; + } + + public bool Disposed { + get { return disposed; } + set { disposed = value; } + } + + public void AnimateShow () + { + animating = true; + scrollable.ScrollMode = true; + scrollable.SetSize (position, targetSize); + + switch (position) { + case PositionType.Left: + WidthRequest = 0; + break; + case PositionType.Right: + targetPos = X = X + WidthRequest; + WidthRequest = 0; + break; + case PositionType.Top: + HeightRequest = 0; + break; + case PositionType.Bottom: + targetPos = Y = Y + HeightRequest; + HeightRequest = 0; + break; + } + Show (); + GLib.Timeout.Add (10, RunAnimateShow); + } + + protected override void OnShown () + { + base.OnShown (); + } + + + public void AnimateHide () + { + animating = true; + scrollable.ScrollMode = true; + scrollable.SetSize (position, targetSize); + GLib.Timeout.Add (10, RunAnimateHide); + } + + bool RunAnimateShow () + { + if (!animating) + return false; + + switch (position) { + case PositionType.Left: + WidthRequest += 1 + (targetSize - WidthRequest) / 3; + if (WidthRequest < targetSize) + return true; + break; + case PositionType.Right: + WidthRequest += 1 + (targetSize - WidthRequest) / 3; + X = targetPos - WidthRequest; + if (WidthRequest < targetSize) + return true; + break; + case PositionType.Top: + HeightRequest += 1 + (targetSize - HeightRequest) / 3; + if (HeightRequest < targetSize) + return true; + break; + case PositionType.Bottom: + HeightRequest += 1 + (targetSize - HeightRequest) / 3; + Y = targetPos - HeightRequest; + if (HeightRequest < targetSize) + return true; + break; + } + + scrollable.ScrollMode = false; + if (horiz) + WidthRequest = targetSize; + else + HeightRequest = targetSize; + animating = false; + return false; + } + + bool RunAnimateHide () + { + if (!animating) + return false; + + switch (position) { + case PositionType.Left: { + int ns = WidthRequest - 1 - WidthRequest / 3; + if (ns > 0) { + WidthRequest = ns; + return true; + } + break; + } + case PositionType.Right: { + int ns = WidthRequest - 1 - WidthRequest / 3; + if (ns > 0) { + WidthRequest = ns; + X = targetPos - ns; + return true; + } + break; + } + case PositionType.Top: { + int ns = HeightRequest - 1 - HeightRequest / 3; + if (ns > 0) { + HeightRequest = ns; + return true; + } + break; + } + case PositionType.Bottom: { + int ns = HeightRequest - 1 - HeightRequest / 3; + if (ns > 0) { + HeightRequest = ns; + Y = targetPos - ns; + return true; + } + break; + } + } + + Hide (); + animating = false; + return false; + } + + protected override void OnHidden () + { + base.OnHidden (); + animating = false; + } + + + public int Size { + get { + return horiz ? WidthRequest : HeightRequest; + } + } + + void OnSizeButtonPress (object ob, Gtk.ButtonPressEventArgs args) + { + if (args.Event.Button == 1 && !animating) { + int n; + if (horiz) { + Toplevel.GetPointer (out resizePos, out n); + origSize = WidthRequest; + if (!startPos) { + origPos = X + origSize; + } + } else { + Toplevel.GetPointer (out n, out resizePos); + origSize = HeightRequest; + if (!startPos) { + origPos = Y + origSize; + } + } + resizing = true; + } + } + + void OnSizeButtonRelease (object ob, Gtk.ButtonReleaseEventArgs args) + { + resizing = false; + } + + void OnSizeMotion (object ob, Gtk.MotionNotifyEventArgs args) + { + if (resizing) { + int newPos, n; + if (horiz) { + Toplevel.GetPointer (out newPos, out n); + int diff = startPos ? (newPos - resizePos) : (resizePos - newPos); + int newSize = origSize + diff; + if (newSize < Child.SizeRequest ().Width) + newSize = Child.SizeRequest ().Width; + if (!startPos) { + X = origPos - newSize; + } + WidthRequest = newSize; + } else { + Toplevel.GetPointer (out n, out newPos); + int diff = startPos ? (newPos - resizePos) : (resizePos - newPos); + int newSize = origSize + diff; + if (newSize < Child.SizeRequest ().Height) + newSize = Child.SizeRequest ().Height; + if (!startPos) { + Y = origPos - newSize; + } + HeightRequest = newSize; + } + frame.QueueResize (); + } + } + + void OnGripExpose (object ob, Gtk.ExposeEventArgs args) + { + EventBox w = (EventBox) ob; + Gdk.Rectangle handleRect = w.Allocation; +// w.GdkWindow.DrawRectangle (w.Style.DarkGC (StateType.Normal), true, handleRect); + handleRect.X = handleRect.Y = 0; + +/* switch (position) { + case PositionType.Top: + handleRect.Height -= 4; handleRect.Y += 1; + Gtk.Style.PaintHline (w.Style, w.GdkWindow, StateType.Normal, args.Event.Area, w, "", 0, w.Allocation.Width, gripSize - 2); + break; + case PositionType.Bottom: + handleRect.Height -= 4; handleRect.Y += 3; + Gtk.Style.PaintHline (w.Style, w.GdkWindow, StateType.Normal, args.Event.Area, w, "", 0, w.Allocation.Width, 0); + break; + case PositionType.Left: + handleRect.Width -= 4; handleRect.X += 1; + Gtk.Style.PaintVline (w.Style, w.GdkWindow, StateType.Normal, args.Event.Area, w, "", 0, w.Allocation.Height, gripSize - 2); + break; + case PositionType.Right: + handleRect.Width -= 4; handleRect.X += 3; + Gtk.Style.PaintVline (w.Style, w.GdkWindow, StateType.Normal, args.Event.Area, w, "", 0, w.Allocation.Height, 0); + break; + }*/ + + Orientation or = horiz ? Orientation.Vertical : Orientation.Horizontal; + StateType s = insideGrip ? StateType.Prelight : StateType.Normal; + Gtk.Style.PaintHandle (w.Style, w.GdkWindow, s, ShadowType.None, args.Event.Area, w, "paned", handleRect.Left, handleRect.Top, handleRect.Width, handleRect.Height, or); + } + } + + class ScrollableContainer: EventBox + { + PositionType expandPos; + bool scrollMode; + int targetSize; + + public bool ScrollMode { + get { + return scrollMode; + } + set { + scrollMode = value; + QueueResize (); + } + } + + public void SetSize (PositionType expandPosition, int targetSize) + { + this.expandPos = expandPosition; + this.targetSize = targetSize; + QueueResize (); + } + + protected override void OnSizeRequested (ref Requisition req) + { + base.OnSizeRequested (ref req); + if (scrollMode || Child == null) { + req.Width = 0; + req.Height = 0; + } + else + req = Child.SizeRequest (); + } + + protected override void OnSizeAllocated (Rectangle alloc) + { + if (scrollMode && Child != null) { + switch (expandPos) { + case PositionType.Bottom: + alloc = new Rectangle (alloc.X, alloc.Y, alloc.Width, targetSize); + break; + case PositionType.Top: + alloc = new Rectangle (alloc.X, alloc.Y - targetSize + alloc.Height, alloc.Width, targetSize); + break; + case PositionType.Right: + alloc = new Rectangle (alloc.X, alloc.Y, targetSize, alloc.Height); + break; + case PositionType.Left: + alloc = new Rectangle (alloc.X - targetSize + alloc.Width, alloc.Y, targetSize, alloc.Height); + break; + } + } + base.OnSizeAllocated (alloc); + } + } + +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockBar.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockBar.cs new file mode 100644 index 0000000000..d62ab5dd1c --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockBar.cs @@ -0,0 +1,176 @@ +// +// DockBar.cs +// +// Author: +// Lluis Sanchez Gual +// + +// +// Copyright (C) 2007 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.Collections.Generic; + +namespace MonoDevelop.Components.Docking +{ + public class DockBar: Gtk.EventBox + { + Gtk.PositionType position; + Box box; + DockFrame frame; + Label filler; + bool alwaysVisible; + + internal DockBar (DockFrame frame, Gtk.PositionType position) + { + frame.ShadedContainer.Add (this); + VisibleWindow = false; + this.frame = frame; + this.position = position; + Gtk.Alignment al = new Alignment (0,0,0,0); + if (Orientation == Gtk.Orientation.Horizontal) + box = new HBox (); + else + box = new VBox (); + + uint sizePadding = 1; + uint startPadding = 6; + switch (Frame.CompactGuiLevel) { + case 1: sizePadding = 2; break; + case 4: startPadding = 3; break; + case 5: startPadding = 0; sizePadding = 0; break; + } + + switch (position) { + case PositionType.Top: al.BottomPadding = sizePadding; al.LeftPadding = al.RightPadding = startPadding; break; + case PositionType.Bottom: al.TopPadding = sizePadding; al.LeftPadding = al.RightPadding = startPadding; break; + case PositionType.Left: al.RightPadding = sizePadding; al.TopPadding = al.BottomPadding = startPadding; break; + case PositionType.Right: al.LeftPadding = sizePadding; al.TopPadding = al.BottomPadding = startPadding; break; + } + + box.Spacing = 3; + al.Add (box); + Add (al); + + filler = new Label (); + filler.WidthRequest = 4; + filler.HeightRequest = 4; + box.PackEnd (filler); + + ShowAll (); + UpdateVisibility (); + } + + public bool IsExtracted { + get { return OriginalBar != null; } + } + + internal DockBar OriginalBar { get; set; } + + public bool AlwaysVisible { + get { return this.alwaysVisible; } + set { this.alwaysVisible = value; UpdateVisibility (); } + } + + + internal Gtk.Orientation Orientation { + get { + return (position == PositionType.Left || position == PositionType.Right) ? Gtk.Orientation.Vertical : Gtk.Orientation.Horizontal; + } + } + + internal Gtk.PositionType Position { + get { + return position; + } + } + + internal DockFrame Frame { + get { + return frame; + } + } + + internal DockBarItem AddItem (DockItem item, int size) + { + DockBarItem it = new DockBarItem (this, item, size); + box.PackStart (it, false, false, 0); + it.ShowAll (); + UpdateVisibility (); + it.Shown += OnItemVisibilityChanged; + it.Hidden += OnItemVisibilityChanged;
+ return it; + } + + void OnItemVisibilityChanged (object o, EventArgs args) + { + UpdateVisibility (); + } + + internal void OnCompactLevelChanged () + { + UpdateVisibility (); + if (OriginalBar != null) + OriginalBar.UpdateVisibility (); + } + + internal void UpdateVisibility () + { + filler.Visible = (Frame.CompactGuiLevel < 3); + int visibleCount = 0; + foreach (Gtk.Widget w in box.Children) { + if (w.Visible) + visibleCount++; + } + Visible = alwaysVisible || filler.Visible || visibleCount > 0; + } + + internal void RemoveItem (DockBarItem it) + { + box.Remove (it);
+ it.Shown -= OnItemVisibilityChanged; + it.Hidden -= OnItemVisibilityChanged; + UpdateVisibility (); + } + + internal void UpdateTitle (DockItem item) + { + foreach (Widget w in box.Children) { + DockBarItem it = w as DockBarItem; + if (it != null && it.DockItem == item) { + it.UpdateTab (); + break; + } + } + } + + protected override bool OnExposeEvent (Gdk.EventExpose evnt) + { + frame.ShadedContainer.DrawBackground (this); + return base.OnExposeEvent (evnt); + } + } +} + diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockBarItem.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockBarItem.cs new file mode 100644 index 0000000000..b7354ad4b5 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockBarItem.cs @@ -0,0 +1,373 @@ +// +// DockBarItem.cs +// +// Author: +// Lluis Sanchez Gual +// + +// +// Copyright (C) 2007 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; + +namespace MonoDevelop.Components.Docking +{ + class DockBarItem: EventBox + { + DockBar bar; + DockItem it; + Box box; + Label label; + Alignment mainBox; + AutoHideBox autoShowFrame; + AutoHideBox hiddenFrame; + uint autoShowTimeout = uint.MaxValue; + uint autoHideTimeout = uint.MaxValue; + int size; + + public DockBarItem (DockBar bar, DockItem it, int size) + { + Events = Events | Gdk.EventMask.EnterNotifyMask | Gdk.EventMask.LeaveNotifyMask; + this.size = size; + this.bar = bar; + this.it = it; + VisibleWindow = false; + UpdateTab (); + } + + public void Close () + { + UnscheduleAutoShow (); + UnscheduleAutoHide (); + AutoHide (false); + bar.RemoveItem (this); + Destroy (); + } + + public int Size { + get { return size; } + set { size = value; } + } + + public void UpdateTab () + { + if (Child != null) { + Widget w = Child; + Remove (w); + w.Destroy (); + } + + mainBox = new Alignment (0,0,1,1); + if (bar.Orientation == Gtk.Orientation.Horizontal) { + box = new HBox (); + mainBox.LeftPadding = mainBox.RightPadding = 2; + } + else { + box = new VBox (); + mainBox.TopPadding = mainBox.BottomPadding = 2; + } + + Gtk.Widget customLabel = null; + if (it.DockLabelProvider != null) + customLabel = it.DockLabelProvider.CreateLabel (bar.Orientation); + + if (customLabel != null) { + customLabel.ShowAll (); + box.PackStart (customLabel, true, true, 0); + } + else { + if (it.Icon != null) + box.PackStart (new Gtk.Image (it.Icon), false, false, 0); + + if (!string.IsNullOrEmpty (it.Label)) { + label = new Gtk.Label (it.Label); + label.UseMarkup = true; + if (bar.Orientation == Gtk.Orientation.Vertical) + label.Angle = 270; + box.PackStart (label, true, true, 0); + } else + label = null; + } + + box.BorderWidth = 2; + box.Spacing = 2; + mainBox.Add (box); + mainBox.ShowAll (); + Add (mainBox); + SetNormalColor (); + } + + public MonoDevelop.Components.Docking.DockItem DockItem { + get { + return it; + } + } + + protected override void OnHidden () + { + base.OnHidden (); + UnscheduleAutoShow (); + UnscheduleAutoHide (); + AutoHide (false); + } + + protected override bool OnExposeEvent (Gdk.EventExpose evnt) + { + if (State == StateType.Prelight) { + int w = Allocation.Width, h = Allocation.Height; + double x=Allocation.Left, y=Allocation.Top, r=3; + x += 0.5; y += 0.5; h -=1; w -= 1; + + using (Cairo.Context ctx = Gdk.CairoHelper.Create (GdkWindow)) { + HslColor c = new HslColor (Style.Background (Gtk.StateType.Normal)); + HslColor c1 = c; + HslColor c2 = c; + if (State != StateType.Prelight) { + c1.L *= 0.8; + c2.L *= 0.95; + } else { + c1.L *= 1.1; + c2.L *= 1; + } + Cairo.Gradient pat; + switch (bar.Position) { + case PositionType.Top: pat = new Cairo.LinearGradient (x, y, x, y+h); break; + case PositionType.Bottom: pat = new Cairo.LinearGradient (x, y, x, y+h); break; + case PositionType.Left: pat = new Cairo.LinearGradient (x+w, y, x, y); break; + default: pat = new Cairo.LinearGradient (x, y, x+w, y); break; + } + pat.AddColorStop (0, c1); + pat.AddColorStop (1, c2); + ctx.NewPath (); + ctx.Arc (x+r, y+r, r, 180 * (Math.PI / 180), 270 * (Math.PI / 180)); + ctx.LineTo (x+w-r, y); + ctx.Arc (x+w-r, y+r, r, 270 * (Math.PI / 180), 360 * (Math.PI / 180)); + ctx.LineTo (x+w, y+h); + ctx.LineTo (x, y+h); + ctx.ClosePath (); + ctx.Pattern = pat; + ctx.FillPreserve (); + c1 = c; + c1.L *= 0.7; + ctx.LineWidth = 1; + ctx.Color = c1; + ctx.Stroke (); + + // Inner line + ctx.NewPath (); + ctx.Arc (x+r+1, y+r+1, r, 180 * (Math.PI / 180), 270 * (Math.PI / 180)); + ctx.LineTo (x+w-r-1, y+1); + ctx.Arc (x+w-r-1, y+r+1, r, 270 * (Math.PI / 180), 360 * (Math.PI / 180)); + ctx.LineTo (x+w-1, y+h-1); + ctx.LineTo (x+1, y+h-1); + ctx.ClosePath (); + c1 = c; + //c1.L *= 0.9; + ctx.LineWidth = 1; + ctx.Color = c1; + ctx.Stroke (); + } + } + + bool res = base.OnExposeEvent (evnt); + return res; + } + + public void Present (bool giveFocus) + { + AutoShow (); + if (giveFocus) { + GLib.Timeout.Add (200, delegate { + // Using a small delay because AutoShow uses an animation and setting focus may + // not work until the item is visible + it.SetFocus (); + ScheduleAutoHide (false); + return false; + }); + } + } + + void AutoShow () + { + UnscheduleAutoHide (); + if (autoShowFrame == null) { + if (hiddenFrame != null) + bar.Frame.AutoHide (it, hiddenFrame, false); + autoShowFrame = bar.Frame.AutoShow (it, bar, size); + autoShowFrame.EnterNotifyEvent += OnFrameEnter; + autoShowFrame.LeaveNotifyEvent += OnFrameLeave; + autoShowFrame.KeyPressEvent += OnFrameKeyPress; + SetPrelight (); + } + } + + void AutoHide (bool animate) + { + UnscheduleAutoShow (); + if (autoShowFrame != null) { + size = autoShowFrame.Size; + hiddenFrame = autoShowFrame; + autoShowFrame.Hidden += delegate { + hiddenFrame = null; + }; + bar.Frame.AutoHide (it, autoShowFrame, animate); + autoShowFrame.EnterNotifyEvent -= OnFrameEnter; + autoShowFrame.LeaveNotifyEvent -= OnFrameLeave; + autoShowFrame.KeyPressEvent -= OnFrameKeyPress; + autoShowFrame = null; + UnsetPrelight (); + } + } + + void ScheduleAutoShow () + { + UnscheduleAutoHide (); + if (autoShowTimeout == uint.MaxValue) { + autoShowTimeout = GLib.Timeout.Add (bar.Frame.AutoShowDelay, delegate { + autoShowTimeout = uint.MaxValue; + AutoShow (); + return false; + }); + } + } + + void ScheduleAutoHide (bool cancelAutoShow) + { + ScheduleAutoHide (cancelAutoShow, false); + } + + void ScheduleAutoHide (bool cancelAutoShow, bool force) + { + if (cancelAutoShow) + UnscheduleAutoShow (); + if (force) + it.Widget.FocusChild = null; + if (autoHideTimeout == uint.MaxValue) { + autoHideTimeout = GLib.Timeout.Add (force ? 0 : bar.Frame.AutoHideDelay, delegate { + // Don't hide the item if it has the focus. Try again later. + if (it.Widget.FocusChild != null) + return true; + autoHideTimeout = uint.MaxValue; + AutoHide (true); + return false; + }); + } + } + + void UnscheduleAutoShow () + { + if (autoShowTimeout != uint.MaxValue) { + GLib.Source.Remove (autoShowTimeout); + autoShowTimeout = uint.MaxValue; + } + } + + void UnscheduleAutoHide () + { + if (autoHideTimeout != uint.MaxValue) { + GLib.Source.Remove (autoHideTimeout); + autoHideTimeout = uint.MaxValue; + } + } + + protected override bool OnEnterNotifyEvent (Gdk.EventCrossing evnt) + { + ScheduleAutoShow (); + SetPrelight (); + return base.OnEnterNotifyEvent (evnt); + } + + protected override bool OnLeaveNotifyEvent (Gdk.EventCrossing evnt) + { + ScheduleAutoHide (true); + if (autoShowFrame == null) + UnsetPrelight (); + return base.OnLeaveNotifyEvent (evnt); + } + + void SetPrelight () + { + if (State != StateType.Prelight) { + State = StateType.Prelight; + if (label != null) + label.ModifyFg (StateType.Normal, Style.Foreground (Gtk.StateType.Normal)); + } + } + + void UnsetPrelight () + { + if (State == StateType.Prelight) { + State = StateType.Normal; + SetNormalColor (); + } + } + + protected override void OnRealized () + { + base.OnRealized(); + SetNormalColor (); + } + + + void SetNormalColor () + { + if (label != null) { + HslColor c = Style.Background (Gtk.StateType.Normal); + c.L *= 0.4; + label.ModifyFg (StateType.Normal, c); + } + } + + void OnFrameEnter (object s, Gtk.EnterNotifyEventArgs args) + { + AutoShow (); + } + + void OnFrameKeyPress (object s, Gtk.KeyPressEventArgs args) + { + if (args.Event.Key == Gdk.Key.Escape) + ScheduleAutoHide (true, true); + } + + void OnFrameLeave (object s, Gtk.LeaveNotifyEventArgs args) + { + if (args.Event.Detail != Gdk.NotifyType.Inferior) + ScheduleAutoHide (true); + } + + protected override bool OnButtonPressEvent (Gdk.EventButton evnt) + { + if (evnt.Button == 1) { + if (evnt.Type == Gdk.EventType.TwoButtonPress) + it.Status = DockItemStatus.Dockable; + else + AutoShow (); + } + else if (evnt.Button == 3) + it.ShowDockPopupMenu (evnt.Time); + return base.OnButtonPressEvent (evnt); + } + } +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockContainer.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockContainer.cs new file mode 100644 index 0000000000..cba733da8e --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockContainer.cs @@ -0,0 +1,450 @@ +// +// DockContainer.cs +// +// Author: +// Lluis Sanchez Gual +// + +// +// Copyright (C) 2007 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 Gdk; + +namespace MonoDevelop.Components.Docking +{ + class DockContainer: Container, IShadedWidget + { + DockLayout layout; + DockFrame frame; + + List<TabStrip> notebooks = new List<TabStrip> (); + List<DockItem> items = new List<DockItem> (); + + bool needsRelayout = true; + + DockGroup currentHandleGrp; + int currentHandleIndex; + bool dragging; + int dragPos; + int dragSize; + + PlaceholderWindow placeholderWindow; + + static Gdk.Cursor hresizeCursor = new Gdk.Cursor (CursorType.SbHDoubleArrow); + static Gdk.Cursor vresizeCursor = new Gdk.Cursor (CursorType.SbVDoubleArrow); + + public DockContainer (DockFrame frame) + { + this.Events = EventMask.ButtonPressMask | EventMask.ButtonReleaseMask | EventMask.PointerMotionMask | EventMask.LeaveNotifyMask; + this.frame = frame; + frame.ShadedContainer.Add (this); + } + + internal DockGroupItem FindDockGroupItem (string id) + { + if (layout == null) + return null; + else + return layout.FindDockGroupItem (id); + } + + public List<DockItem> Items { + get { return items; } + } + + public DockLayout Layout { + get { return layout; } + set { layout = value; } + } + + public void Clear () + { + layout = null; + } + + public void LoadLayout (DockLayout dl) + { + // Sticky items currently selected in notebooks will remain + // selected after switching the layout + List<DockItem> sickyOnTop = new List<DockItem> (); + foreach (DockItem it in items) { + if ((it.Behavior & DockItemBehavior.Sticky) != 0) { + DockGroupItem gitem = FindDockGroupItem (it.Id); + if (gitem != null && gitem.ParentGroup.IsSelectedPage (it)) + sickyOnTop.Add (it); + } + } + + if (layout != null) + layout.StoreAllocation (); + layout = dl; + layout.RestoreAllocation (); + + // Make sure items not present in this layout are hidden + foreach (DockItem it in items) { + if ((it.Behavior & DockItemBehavior.Sticky) != 0) + it.Visible = it.StickyVisible; + if (layout.FindDockGroupItem (it.Id) == null) + it.HideWidget (); + } + + RelayoutWidgets (); + + foreach (DockItem it in sickyOnTop) + it.Present (false); + } + + public void StoreAllocation () + { + if (layout != null) + layout.StoreAllocation (); + } + + protected override void OnSizeRequested (ref Requisition req) + { + if (layout != null) { + LayoutWidgets (); + req = layout.SizeRequest (); + } + } + + protected override void OnSizeAllocated (Gdk.Rectangle rect) + { + base.OnSizeAllocated (rect); + if (layout == null) + return; + + // This container has its own window, so allocation of children + // is relative to 0,0 + rect.X = rect.Y = 0; + LayoutWidgets (); + layout.Size = -1; + layout.SizeAllocate (rect); + } + + protected override void ForAll (bool include_internals, Gtk.Callback callback) + { + List<Widget> widgets = new List<Widget> (); + foreach (Widget w in notebooks) + widgets.Add (w); + foreach (DockItem it in items) { + if (it.HasWidget && it.Widget.Parent == this) + widgets.Add (it.Widget); + } + foreach (Widget w in widgets) + callback (w); + } + + protected override bool OnExposeEvent (Gdk.EventExpose evnt) + { + bool res = base.OnExposeEvent (evnt); + + if (layout != null) { + layout.Draw (evnt.Area, currentHandleGrp, currentHandleIndex); + } + return res; + } + + + public void RelayoutWidgets () + { + needsRelayout = true; + QueueResize (); + } + + void LayoutWidgets () + { + if (!needsRelayout) + return; + needsRelayout = false; + + // Create the needed notebooks and place the widgets in there + + List<DockGroup> tabbedGroups = new List<DockGroup> (); + GetTabbedGroups (layout, tabbedGroups); + + for (int n=0; n<tabbedGroups.Count; n++) { + DockGroup grp = tabbedGroups [n]; + TabStrip ts; + if (n < notebooks.Count) { + ts = notebooks [n]; + } + else { + ts = new TabStrip (frame); + ts.Show (); + notebooks.Add (ts); + ts.Parent = this; + } + grp.UpdateNotebook (ts); + } + + // Remove spare tab strips + for (int n = notebooks.Count - 1; n >= tabbedGroups.Count; n--) { + TabStrip ts = notebooks [n]; + notebooks.RemoveAt (n); + ts.Clear (); + ts.Unparent (); + ts.Destroy (); + } + + // Add widgets to the container + + layout.LayoutWidgets (); + NotifySeparatorsChanged (); + } + + void GetTabbedGroups (DockGroup grp, List<DockGroup> tabbedGroups) + { + if (grp.Type == DockGroupType.Tabbed) { + if (grp.VisibleObjects.Count > 1) + tabbedGroups.Add (grp); + else + grp.ResetNotebook (); + } + else { + // Make sure it doesn't have a notebook bound to it + grp.ResetNotebook (); + foreach (DockObject ob in grp.Objects) { + if (ob is DockGroup) + GetTabbedGroups ((DockGroup) ob, tabbedGroups); + } + } + } + + protected override bool OnButtonPressEvent (Gdk.EventButton ev) + { + if (currentHandleGrp != null) { + dragging = true; + dragPos = (currentHandleGrp.Type == DockGroupType.Horizontal) ? (int)ev.X : (int)ev.Y; + DockObject obj = currentHandleGrp.VisibleObjects [currentHandleIndex]; + dragSize = (currentHandleGrp.Type == DockGroupType.Horizontal) ? obj.Allocation.Width : obj.Allocation.Height; + } + return base.OnButtonPressEvent (ev); + } + + protected override bool OnButtonReleaseEvent (Gdk.EventButton e) + { + dragging = false; + return base.OnButtonReleaseEvent (e); + } + + protected override bool OnMotionNotifyEvent (Gdk.EventMotion e) + { + if (dragging) { + NotifySeparatorsChanged (); + int newpos = (currentHandleGrp.Type == DockGroupType.Horizontal) ? (int)e.X : (int)e.Y; + if (newpos != dragPos) { + int nsize = dragSize + (newpos - dragPos); + currentHandleGrp.ResizeItem (currentHandleIndex, nsize); + layout.DrawSeparators (Allocation, currentHandleGrp, currentHandleIndex, true, null); + } + } + else if (layout != null && placeholderWindow == null) { + int index; + DockGroup grp; + if (FindHandle (layout, (int)e.X, (int)e.Y, out grp, out index)) { + if (currentHandleGrp != grp || currentHandleIndex != index) { + if (grp.Type == DockGroupType.Horizontal) + this.GdkWindow.Cursor = hresizeCursor; + else + this.GdkWindow.Cursor = vresizeCursor; + currentHandleGrp = grp; + currentHandleIndex = index; + layout.DrawSeparators (Allocation, currentHandleGrp, currentHandleIndex, true, null); + } + } + else if (currentHandleGrp != null) { + ResetHandleHighlight (); + } + } + return base.OnMotionNotifyEvent (e); + } + + void ResetHandleHighlight () + { + this.GdkWindow.Cursor = null; + currentHandleGrp = null; + currentHandleIndex = -1; + if (layout != null) + layout.DrawSeparators (Allocation, null, -1, true, null); + } + + protected override bool OnLeaveNotifyEvent (EventCrossing evnt) + { + if (!dragging && evnt.Mode != CrossingMode.Grab) + ResetHandleHighlight (); + return base.OnLeaveNotifyEvent (evnt); + } + + + bool FindHandle (DockGroup grp, int x, int y, out DockGroup foundGrp, out int objectIndex) + { + if (grp.Type != DockGroupType.Tabbed && grp.Allocation.Contains (x, y)) { + for (int n=0; n<grp.VisibleObjects.Count; n++) { + DockObject obj = grp.VisibleObjects [n]; + if (n < grp.Objects.Count - 1) { + if ((grp.Type == DockGroupType.Horizontal && x > obj.Allocation.Right && x < obj.Allocation.Right + frame.TotalHandleSize) || + (grp.Type == DockGroupType.Vertical && y > obj.Allocation.Bottom && y < obj.Allocation.Bottom + frame.TotalHandleSize)) + { + foundGrp = grp; + objectIndex = n; + return true; + } + } + if (obj is DockGroup) { + if (FindHandle ((DockGroup) obj, x, y, out foundGrp, out objectIndex)) + return true; + } + } + } + + foundGrp = null; + objectIndex = 0; + return false; + } + + protected override void OnRealized () + { + WidgetFlags |= WidgetFlags.Realized; + + Gdk.WindowAttr attributes = new Gdk.WindowAttr (); + attributes.X = Allocation.X; + attributes.Y = Allocation.Y; + attributes.Height = Allocation.Height; + attributes.Width = Allocation.Width; + attributes.WindowType = Gdk.WindowType.Child; + attributes.Wclass = Gdk.WindowClass.InputOutput; + attributes.Visual = Visual; + attributes.Colormap = Colormap; + attributes.EventMask = (int)(Events | + Gdk.EventMask.ExposureMask | + Gdk.EventMask.Button1MotionMask | + Gdk.EventMask.ButtonPressMask | + Gdk.EventMask.ButtonReleaseMask); + + Gdk.WindowAttributesType attributes_mask = + Gdk.WindowAttributesType.X | + Gdk.WindowAttributesType.Y | + Gdk.WindowAttributesType.Colormap | + Gdk.WindowAttributesType.Visual; + GdkWindow = new Gdk.Window (ParentWindow, attributes, (int)attributes_mask); + GdkWindow.UserData = Handle; + + Style = Style.Attach (GdkWindow); + Style.SetBackground (GdkWindow, State); + + //GdkWindow.SetBackPixmap (null, true); + } + + internal void ShowPlaceholder () + { + placeholderWindow = new PlaceholderWindow (frame); + } + + internal bool UpdatePlaceholder (DockItem item, Gdk.Size size, bool allowDocking) + { + if (placeholderWindow == null) + return false; + + int px, py; + GetPointer (out px, out py); + + placeholderWindow.AllowDocking = allowDocking; + + DockDelegate dockDelegate; + Gdk.Rectangle rect; + if (allowDocking && layout.GetDockTarget (item, px, py, out dockDelegate, out rect)) { + int ox, oy; + GdkWindow.GetOrigin (out ox, out oy); + + placeholderWindow.Relocate (ox + rect.X, oy + rect.Y, rect.Width, rect.Height, true); + placeholderWindow.Show (); + return true; + } else { + int ox, oy; + GdkWindow.GetOrigin (out ox, out oy); + placeholderWindow.Relocate (ox + px - size.Width / 2, oy + py - 18, size.Width, size.Height, false); + placeholderWindow.Show (); + } + return false; + } + + internal void DockInPlaceholder (DockItem item) + { + if (placeholderWindow == null || !placeholderWindow.Visible) + return; + + item.Status = DockItemStatus.Dockable; + + int px, py; + GetPointer (out px, out py); + + DockDelegate dockDelegate; + Gdk.Rectangle rect; + if (placeholderWindow.AllowDocking && layout.GetDockTarget (item, px, py, out dockDelegate, out rect)) { + DockGroupItem dummyItem = new DockGroupItem (frame, new DockItem (frame, "__dummy")); + DockGroupItem gitem = layout.FindDockGroupItem (item.Id); + gitem.ParentGroup.ReplaceItem (gitem, dummyItem); + dockDelegate (item); + dummyItem.ParentGroup.Remove (dummyItem); + RelayoutWidgets (); + } else { + DockGroupItem gi = FindDockGroupItem (item.Id); + int pw, ph; + placeholderWindow.GetPosition (out px, out py); + placeholderWindow.GetSize (out pw, out ph); + gi.FloatRect = new Rectangle (px, py, pw, ph); + item.Status = DockItemStatus.Floating; + } + } + + internal void HidePlaceholder () + { + if (placeholderWindow != null) { + placeholderWindow.Destroy (); + placeholderWindow = null; + } + } + + public IEnumerable<Rectangle> GetShadedAreas () + { + List<Gdk.Rectangle> rects = new List<Gdk.Rectangle> (); + layout.DrawSeparators (Allocation, currentHandleGrp, currentHandleIndex, true, rects); + return rects; + } + + internal void NotifySeparatorsChanged () + { + if (AreasChanged != null) + AreasChanged (this, EventArgs.Empty); + } + + public event EventHandler AreasChanged; + + } +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockFrame.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockFrame.cs new file mode 100644 index 0000000000..f372dd4245 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockFrame.cs @@ -0,0 +1,668 @@ +// +// MonoDevelop.Components.Docking.cs +// +// Author: +// Lluis Sanchez Gual +// + +// +// Copyright (C) 2007 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.Xml; +using System.Collections; +using System.Collections.Generic; +using Gtk; +using Gdk; + +namespace MonoDevelop.Components.Docking +{ + public class DockFrame: HBox + { + internal const double ItemDockCenterArea = 0.4; + internal const int GroupDockSeparatorSize = 40; + + internal bool ShadedSeparators = true; + + DockContainer container; + + int handleSize = IsWindows ? 4 : 6; + int handlePadding = 0; + int defaultItemWidth = 130; + int defaultItemHeight = 130; + uint autoShowDelay = 400; + uint autoHideDelay = 500; + + SortedDictionary<string,DockLayout> layouts = new SortedDictionary<string,DockLayout> (); + List<DockFrameTopLevel> topLevels = new List<DockFrameTopLevel> (); + string currentLayout; + int compactGuiLevel = 3; + + DockBar dockBarTop, dockBarBottom, dockBarLeft, dockBarRight; + VBox mainBox; + ShadedContainer shadedContainer; + + public DockFrame () + { + shadedContainer = new ShadedContainer (); + + dockBarTop = new DockBar (this, Gtk.PositionType.Top); + dockBarBottom = new DockBar (this, Gtk.PositionType.Bottom); + dockBarLeft = new DockBar (this, Gtk.PositionType.Left); + dockBarRight = new DockBar (this, Gtk.PositionType.Right); + + container = new DockContainer (this); + HBox hbox = new HBox (); + hbox.PackStart (dockBarLeft, false, false, 0); + hbox.PackStart (container, true, true, 0); + hbox.PackStart (dockBarRight, false, false, 0); + mainBox = new VBox (); + mainBox.PackStart (dockBarTop, false, false, 0); + mainBox.PackStart (hbox, true, true, 0); + mainBox.PackStart (dockBarBottom, false, false, 0); + Add (mainBox); + mainBox.ShowAll (); + mainBox.NoShowAll = true; + CompactGuiLevel = 2; + dockBarTop.UpdateVisibility ();
+ dockBarBottom.UpdateVisibility (); + dockBarLeft.UpdateVisibility (); + dockBarRight.UpdateVisibility (); + } + + /// <summary> + /// Compactness level of the gui, from 1 (not compact) to 5 (very compact). + /// </summary> + public int CompactGuiLevel { + get { return compactGuiLevel; } + set { + compactGuiLevel = value; + switch (compactGuiLevel) { + case 1: handleSize = 6; break; + case 2: + case 3: handleSize = IsWindows ? 4 : 6; break; + case 4: + case 5: handleSize = 3; break; + } + handlePadding = 0; + dockBarTop.OnCompactLevelChanged (); + dockBarBottom.OnCompactLevelChanged (); + dockBarLeft.OnCompactLevelChanged (); + dockBarRight.OnCompactLevelChanged (); + container.RelayoutWidgets (); + } + } + + public DockBar ExtractDockBar (PositionType pos) + { + DockBar db = new DockBar (this, pos); + switch (pos) { + case PositionType.Left: db.OriginalBar = dockBarLeft; dockBarLeft = db; break; + case PositionType.Top: db.OriginalBar = dockBarTop; dockBarTop = db; break; + case PositionType.Right: db.OriginalBar = dockBarRight; dockBarRight = db; break; + case PositionType.Bottom: db.OriginalBar = dockBarBottom; dockBarBottom = db; break; + } + return db; + } + + internal DockBar GetDockBar (PositionType pos) + { + switch (pos) { + case Gtk.PositionType.Top: return dockBarTop; + case Gtk.PositionType.Bottom: return dockBarBottom; + case Gtk.PositionType.Left: return dockBarLeft; + case Gtk.PositionType.Right: return dockBarRight; + } + return null; + } + + internal DockContainer Container { + get { return container; } + } + + public ShadedContainer ShadedContainer { + get { return this.shadedContainer; } + } + + public int HandleSize { + get { + return handleSize; + } + set { + handleSize = value; + } + } + + public int HandlePadding { + get { + return handlePadding; + } + set { + handlePadding = value; + } + } + + public int DefaultItemWidth { + get { + return defaultItemWidth; + } + set { + defaultItemWidth = value; + } + } + + public int DefaultItemHeight { + get { + return defaultItemHeight; + } + set { + defaultItemHeight = value; + } + } + + internal int TotalHandleSize { + get { return handleSize + handlePadding*2; } + } + + public DockItem AddItem (string id) + { + foreach (DockItem dit in container.Items) { + if (dit.Id == id) { + if (dit.IsPositionMarker) { + dit.IsPositionMarker = false; + return dit; + } + throw new InvalidOperationException ("An item with id '" + id + "' already exists."); + } + } + + DockItem it = new DockItem (this, id); + container.Items.Add (it); + return it; + } + + public void RemoveItem (DockItem it) + { + if (container.Layout != null) + container.Layout.RemoveItemRec (it); + foreach (DockGroup grp in layouts.Values) + grp.RemoveItemRec (it); + container.Items.Remove (it); + } + + public DockItem GetItem (string id) + { + foreach (DockItem it in container.Items) { + if (it.Id == id) { + if (!it.IsPositionMarker) + return it; + else + return null; + } + } + return null; + } + + public IEnumerable<DockItem> GetItems () + { + return container.Items; + } + + bool LoadLayout (string layoutName) + { + DockLayout dl; + if (!layouts.TryGetValue (layoutName, out dl)) + return false; + + container.LoadLayout (dl); + return true; + } + + public void CreateLayout (string name) + { + CreateLayout (name, false); + } + + public void DeleteLayout (string name) + { + layouts.Remove (name); + } + + public void CreateLayout (string name, bool copyCurrent) + { + DockLayout dl; + if (container.Layout == null || !copyCurrent) { + dl = GetDefaultLayout (); + } else { + container.StoreAllocation (); + dl = (DockLayout) container.Layout.Clone (); + } + dl.Name = name; + layouts [name] = dl; + } + + public string CurrentLayout { + get { + return currentLayout; + } + set { + if (currentLayout == value) + return; + if (LoadLayout (value)) { + currentLayout = value; + } + } + } + + public bool HasLayout (string id) + { + return layouts.ContainsKey (id); + } + + public string[] Layouts { + get { + if (layouts.Count == 0) + return new string [0]; + string[] arr = new string [layouts.Count]; + layouts.Keys.CopyTo (arr, 0); + return arr; + } + } + + public uint AutoShowDelay { + get { + return autoShowDelay; + } + set { + autoShowDelay = value; + } + } + + public uint AutoHideDelay { + get { + return autoHideDelay; + } + set { + autoHideDelay = value; + } + } + + public void SaveLayouts (string file) + { + using (XmlTextWriter w = new XmlTextWriter (file, System.Text.Encoding.UTF8)) { + w.Formatting = Formatting.Indented; + SaveLayouts (w); + } + } + + public void SaveLayouts (XmlWriter writer) + { + if (container.Layout != null) + container.Layout.StoreAllocation (); + writer.WriteStartElement ("layouts"); + foreach (DockLayout la in layouts.Values) + la.Write (writer); + writer.WriteEndElement (); + } + + public void LoadLayouts (string file) + { + using (XmlReader r = new XmlTextReader (new System.IO.StreamReader (file))) { + LoadLayouts (r); + } + } + + public void LoadLayouts (XmlReader reader) + { + layouts.Clear (); + container.Clear (); + currentLayout = null; + + reader.MoveToContent (); + if (reader.IsEmptyElement) { + reader.Skip (); + return; + } + reader.ReadStartElement ("layouts"); + reader.MoveToContent (); + while (reader.NodeType != XmlNodeType.EndElement) { + if (reader.NodeType == XmlNodeType.Element) { + DockLayout layout = DockLayout.Read (this, reader); + layouts.Add (layout.Name, layout); + } + else + reader.Skip (); + reader.MoveToContent (); + } + reader.ReadEndElement (); + container.RelayoutWidgets (); + } + + internal void UpdateTitle (DockItem item) + { + DockGroupItem gitem = container.FindDockGroupItem (item.Id); + if (gitem == null) + return; + + gitem.ParentGroup.UpdateTitle (item); + dockBarTop.UpdateTitle (item); + dockBarBottom.UpdateTitle (item); + dockBarLeft.UpdateTitle (item); + dockBarRight.UpdateTitle (item); + } + + internal void Present (DockItem item, bool giveFocus) + { + DockGroupItem gitem = container.FindDockGroupItem (item.Id); + if (gitem == null) + return; + + gitem.ParentGroup.Present (item, giveFocus); + } + + internal bool GetVisible (DockItem item) + { + DockGroupItem gitem = container.FindDockGroupItem (item.Id); + if (gitem == null) + return false; + return gitem.VisibleFlag; + } + + internal bool GetVisible (DockItem item, string layoutName) + { + DockLayout dl; + if (!layouts.TryGetValue (layoutName, out dl)) + return false; + + DockGroupItem gitem = dl.FindDockGroupItem (item.Id); + if (gitem == null) + return false; + return gitem.VisibleFlag; + } + + internal void SetVisible (DockItem item, bool visible) + { + if (container.Layout == null) + return; + DockGroupItem gitem = container.FindDockGroupItem (item.Id); + + if (gitem == null) { + if (visible) { + // The item is not present in the layout. Add it now. + if (!string.IsNullOrEmpty (item.DefaultLocation)) + gitem = AddDefaultItem (container.Layout, item); + + if (gitem == null) { + // No default position + gitem = new DockGroupItem (this, item); + container.Layout.AddObject (gitem); + } + } else + return; // Already invisible + } + gitem.SetVisible (visible); + container.RelayoutWidgets (); + } + + internal DockItemStatus GetStatus (DockItem item) + { + DockGroupItem gitem = container.FindDockGroupItem (item.Id); + if (gitem == null) + return DockItemStatus.Dockable; + return gitem.Status; + } + + internal void SetStatus (DockItem item, DockItemStatus status) + { + DockGroupItem gitem = container.FindDockGroupItem (item.Id); + if (gitem == null) { + item.DefaultStatus = status; + return; + } + gitem.StoreAllocation (); + gitem.Status = status; + container.RelayoutWidgets (); + } + + DockLayout GetDefaultLayout () + { + DockLayout group = new DockLayout (this); + + // Add items which don't have relative defaut positions + + List<DockItem> todock = new List<DockItem> (); + foreach (DockItem item in container.Items) { + if (string.IsNullOrEmpty (item.DefaultLocation)) { + DockGroupItem dgt = new DockGroupItem (this, item); + dgt.SetVisible (item.DefaultVisible); + group.AddObject (dgt); + } + else + todock.Add (item); + } + + // Add items with relative positions. + int lastCount = 0; + while (lastCount != todock.Count) { + lastCount = todock.Count; + for (int n=0; n<todock.Count; n++) { + DockItem it = todock [n]; + if (AddDefaultItem (group, it) != null) { + todock.RemoveAt (n); + n--; + } + } + } + + // Items which could not be docked because of an invalid default location + foreach (DockItem item in todock) { + DockGroupItem dgt = new DockGroupItem (this, item); + dgt.SetVisible (false); + group.AddObject (dgt); + } +// group.Dump (); + return group; + } + + DockGroupItem AddDefaultItem (DockGroup grp, DockItem it) + { + string[] positions = it.DefaultLocation.Split (';'); + foreach (string pos in positions) { + int i = pos.IndexOf ('/'); + if (i == -1) continue; + string id = pos.Substring (0,i).Trim (); + DockGroup g = grp.FindGroupContaining (id); + if (g != null) { + DockPosition dpos; + try { + dpos = (DockPosition) Enum.Parse (typeof(DockPosition), pos.Substring(i+1).Trim(), true); + } + catch { + continue; + } + DockGroupItem dgt = g.AddObject (it, dpos, id); + dgt.SetVisible (it.DefaultVisible); + dgt.Status = it.DefaultStatus; + return dgt; + } + } + return null; + } + + internal void AddTopLevel (DockFrameTopLevel w, int x, int y) + { + w.Parent = this; + w.X = x; + w.Y = y; + Requisition r = w.SizeRequest (); + w.Allocation = new Gdk.Rectangle (Allocation.X + x, Allocation.Y + y, r.Width, r.Height); + topLevels.Add (w); + } + + internal void RemoveTopLevel (DockFrameTopLevel w) + { + w.Unparent (); + topLevels.Remove (w); + QueueResize (); + } + + public Gdk.Rectangle GetCoordinates (Gtk.Widget w) + { + int px, py; + if (!w.TranslateCoordinates (this, 0, 0, out px, out py)) + return new Gdk.Rectangle (0,0,0,0); + + Gdk.Rectangle rect = w.Allocation; + rect.X = px - Allocation.X; + rect.Y = py - Allocation.Y; + return rect; + } + + internal void ShowPlaceholder () + { + container.ShowPlaceholder (); + } + + internal void DockInPlaceholder (DockItem item) + { + container.DockInPlaceholder (item); + } + + internal void HidePlaceholder () + { + container.HidePlaceholder (); + } + + internal void UpdatePlaceholder (DockItem item, Gdk.Size size, bool allowDocking) + { + container.UpdatePlaceholder (item, size, allowDocking); + } + + internal DockBarItem BarDock (Gtk.PositionType pos, DockItem item, int size) + { + return GetDockBar (pos).AddItem (item, size); + } + + internal AutoHideBox AutoShow (DockItem item, DockBar bar, int size) + { + AutoHideBox aframe = new AutoHideBox (this, item, bar.Position, size); + Gdk.Size sTop = GetBarFrameSize (dockBarTop); + Gdk.Size sBot = GetBarFrameSize (dockBarBottom); + Gdk.Size sLeft = GetBarFrameSize (dockBarLeft); + Gdk.Size sRgt = GetBarFrameSize (dockBarRight); + + int x,y; + if (bar == dockBarLeft || bar == dockBarRight) { + aframe.HeightRequest = Allocation.Height - sTop.Height - sBot.Height; + aframe.WidthRequest = size; + y = sTop.Height; + if (bar == dockBarLeft) + x = sLeft.Width; + else + x = Allocation.Width - size - sRgt.Width; + } else { + aframe.WidthRequest = Allocation.Width - sLeft.Width - sRgt.Width; + aframe.HeightRequest = size; + x = sLeft.Width; + if (bar == dockBarTop) + y = sTop.Height; + else + y = Allocation.Height - size - sBot.Height; + } + AddTopLevel (aframe, x, y); + aframe.AnimateShow (); + return aframe; + } + + Gdk.Size GetBarFrameSize (DockBar bar) + { + if (bar.OriginalBar != null) + bar = bar.OriginalBar; + if (!bar.Visible) + return new Gdk.Size (0,0); + Gtk.Requisition req = bar.SizeRequest (); + return new Gdk.Size (req.Width, req.Height); + } + + internal void AutoHide (DockItem item, AutoHideBox widget, bool animate) + { + if (animate) { + widget.Hidden += delegate { + if (!widget.Disposed) + AutoHide (item, widget, false); + }; + widget.AnimateHide (); + } + else { + Gtk.Container parent = (Gtk.Container) item.Widget.Parent; + parent.Remove (item.Widget); + RemoveTopLevel (widget); + widget.Disposed = true; + widget.Destroy ();
+ } + } + + protected override void OnSizeAllocated (Rectangle allocation) + { + base.OnSizeAllocated (allocation); + + foreach (DockFrameTopLevel tl in topLevels) { + Requisition r = tl.SizeRequest (); + tl.SizeAllocate (new Gdk.Rectangle (allocation.X + tl.X, allocation.Y + tl.Y, r.Width, r.Height)); + } + } + + protected override void ForAll (bool include_internals, Callback callback) + { + base.ForAll (include_internals, callback); + List<DockFrameTopLevel> clone = new List<DockFrameTopLevel> (topLevels); + foreach (DockFrameTopLevel child in clone) + callback (child); + } + + protected override void OnRealized () + { + base.OnRealized (); + HslColor cLight = new HslColor (Style.Background (Gtk.StateType.Normal)); + HslColor cDark = cLight; + cLight.L *= 0.9; + cDark.L *= 0.8; + shadedContainer.LightColor = cLight; + shadedContainer.DarkColor = cDark; + } +
+
+ static internal bool IsWindows {
+ get { return System.IO.Path.DirectorySeparatorChar == '\\'; }
+ }
+
+ internal static Cairo.Color ToCairoColor (Gdk.Color color)
+ {
+ return new Cairo.Color (color.Red / (double) ushort.MaxValue, color.Green / (double) ushort.MaxValue, color.Blue / (double) ushort.MaxValue);
+ }
+ } + + + internal delegate void DockDelegate (DockItem item); + +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockFrameTopLevel.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockFrameTopLevel.cs new file mode 100644 index 0000000000..7cb00af05b --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockFrameTopLevel.cs @@ -0,0 +1,61 @@ +// +// DockFrameTopLevel.cs +// +// Author: +// Lluis Sanchez Gual +// + +// +// Copyright (C) 2007 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; + +namespace MonoDevelop.Components.Docking +{ + class DockFrameTopLevel: EventBox + { + int x, y; + + public int X { + get { return x; } + set { + x = value; + if (Parent != null) + Parent.QueueResize (); + } + } + + public int Y { + get { return y; } + set { + y = value; + if (Parent != null) + Parent.QueueResize (); + } + } + } + +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockGroup.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockGroup.cs new file mode 100644 index 0000000000..6fc5396660 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockGroup.cs @@ -0,0 +1,1134 @@ +// +// MonoDevelop.Components.Docking.cs +// +// Author: +// Lluis Sanchez Gual +// + +// +// Copyright (C) 2007 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.Xml; +using System.Collections.Generic; +using Gtk; + +namespace MonoDevelop.Components.Docking +{ + class DockGroup: DockObject + { + DockGroupType type; + List<DockObject> dockObjects = new List<DockObject> (); + List<DockObject> visibleObjects; + AllocStatus allocStatus = AllocStatus.NotSet; + TabStrip boundTabStrip; + DockGroupItem tabFocus; + int currentTabPage; + + enum AllocStatus { NotSet, Invalid, RestorePending, NewSizeRequest, Valid }; + + public DockGroup (DockFrame frame, DockGroupType type): base (frame) + { + this.type = type; + } + + internal DockGroup (DockFrame frame): base (frame) + { + } + + public DockGroupType Type { + get { + return type; + } + set { + type = value; + } + } + + public List<DockObject> Objects { + get { return dockObjects; } + } + + void MarkForRelayout () + { + if (allocStatus == AllocStatus.Valid) + allocStatus = AllocStatus.Invalid; + } + + public void AddObject (DockObject obj) + { + obj.ParentGroup = this; + dockObjects.Add (obj); + ResetVisibleGroups (); + } + + public DockGroupItem AddObject (DockItem obj, DockPosition pos, string relItemId) + { + int npos = -1; + if (relItemId != null) { + for (int n=0; n<dockObjects.Count; n++) { + DockGroupItem it = dockObjects [n] as DockGroupItem; + if (it != null && it.Id == relItemId) + npos = n; + } + } + + if (npos == -1) { + if (pos == DockPosition.Left || pos == DockPosition.Top) + npos = 0; + else + npos = dockObjects.Count - 1; + } + + DockGroupItem gitem = null; + + if (pos == DockPosition.Left || pos == DockPosition.Right) { + if (type != DockGroupType.Horizontal) + gitem = Split (DockGroupType.Horizontal, pos == DockPosition.Left, obj, npos); + else + gitem = InsertObject (obj, npos, pos); + } + else if (pos == DockPosition.Top || pos == DockPosition.Bottom) { + if (type != DockGroupType.Vertical) + gitem = Split (DockGroupType.Vertical, pos == DockPosition.Top, obj, npos); + else + gitem = InsertObject (obj, npos, pos); + } + else if (pos == DockPosition.CenterBefore || pos == DockPosition.Center) { + if (type != DockGroupType.Tabbed) + gitem = Split (DockGroupType.Tabbed, pos == DockPosition.CenterBefore, obj, npos); + else { + if (pos == DockPosition.Center) + npos++; + gitem = new DockGroupItem (Frame, obj); + dockObjects.Insert (npos, gitem); + gitem.ParentGroup = this; + } + } + ResetVisibleGroups (); + return gitem; + } + + DockGroupItem InsertObject (DockItem obj, int npos, DockPosition pos) + { + if (pos == DockPosition.Bottom || pos == DockPosition.Right) + npos++; + + DockGroupItem gitem = new DockGroupItem (Frame, obj); + dockObjects.Insert (npos, gitem); + gitem.ParentGroup = this; + return gitem; + } + + DockGroupItem Split (DockGroupType newType, bool addFirst, DockItem obj, int npos) + { + DockGroupItem item = new DockGroupItem (Frame, obj); + + if (npos == -1 || type == DockGroupType.Tabbed) { + if (ParentGroup != null && ParentGroup.Type == newType) { + // No need to split. Just add the new item as a sibling of this one. + int i = ParentGroup.Objects.IndexOf (this); + if (addFirst) + ParentGroup.Objects.Insert (i, item); + else + ParentGroup.Objects.Insert (i+1, item); + item.ParentGroup = ParentGroup; + item.ResetDefaultSize (); + } + else { + DockGroup grp = Copy (); + dockObjects.Clear (); + if (addFirst) { + dockObjects.Add (item); + dockObjects.Add (grp); + } else { + dockObjects.Add (grp); + dockObjects.Add (item); + } + item.ParentGroup = this; + item.ResetDefaultSize (); + grp.ParentGroup = this; + grp.ResetDefaultSize (); + Type = newType; + } + } + else { + DockGroup grp = new DockGroup (Frame, newType); + DockObject replaced = dockObjects[npos]; + if (addFirst) { + grp.AddObject (item); + grp.AddObject (replaced); + } else { + grp.AddObject (replaced); + grp.AddObject (item); + } + grp.CopySizeFrom (replaced); + dockObjects [npos] = grp; + grp.ParentGroup = this; + } + return item; + } + + internal DockGroup FindGroupContaining (string id) + { + DockGroupItem it = FindDockGroupItem (id); + if (it != null) + return it.ParentGroup; + else + return null; + } + + internal DockGroupItem FindDockGroupItem (string id) + { + foreach (DockObject ob in dockObjects) { + DockGroupItem it = ob as DockGroupItem; + if (it != null && it.Id == id) + return it; + DockGroup g = ob as DockGroup; + if (g != null) { + it = g.FindDockGroupItem (id); + if (it != null) + return it; + } + } + return null; + } + + DockGroup Copy () + { + DockGroup grp = new DockGroup (Frame, type); + grp.dockObjects = new List<MonoDevelop.Components.Docking.DockObject> (dockObjects); + foreach (DockObject obj in grp.dockObjects) + obj.ParentGroup = grp; + + grp.CopySizeFrom (this); + return grp; + } + + public int GetObjectIndex (DockObject obj) + { + for (int n=0; n<dockObjects.Count; n++) { + if (dockObjects [n] == obj) + return n; + } + return -1; + } + + public bool RemoveItemRec (DockItem item) + { + foreach (DockObject ob in dockObjects) { + if (ob is DockGroup) { + if (((DockGroup)ob).RemoveItemRec (item)) + return true; + } else { + DockGroupItem dit = ob as DockGroupItem; + if (dit != null && dit.Item == item) { + Remove (ob); + return true; + } + } + } + return false; + } + + public void Remove (DockObject obj) + { + dockObjects.Remove (obj); + Reduce (); + obj.ParentGroup = null; + visibleObjects = null; + + if (VisibleObjects.Count > 0) { + CalcNewSizes (); + MarkForRelayout (); + } else + ParentGroup.UpdateVisible (this); + } + + public void Reduce () + { + if (ParentGroup != null && dockObjects.Count == 1) { + DockObject obj = dockObjects [0]; + int n = ParentGroup.GetObjectIndex (this); + ParentGroup.dockObjects [n] = obj; + obj.ParentGroup = ParentGroup; + obj.CopySizeFrom (this); + dockObjects.Clear (); + ResetVisibleGroups (); + ParentGroup.ResetVisibleGroups (); + } + } + + internal List<DockObject> VisibleObjects { + get { + if (visibleObjects == null) { + visibleObjects = new List<DockObject> (); + foreach (DockObject obj in dockObjects) + if (obj.Visible) + visibleObjects.Add (obj); + } + return visibleObjects; + } + } + + void ResetVisibleGroups () + { + visibleObjects = null; + MarkForRelayout (); + } + + internal void UpdateVisible (DockObject child) + { + visibleObjects = null; + bool visChanged; + CalcNewSizes (); + MarkForRelayout (); + + visChanged = child.Visible ? VisibleObjects.Count == 1 : VisibleObjects.Count == 0; + + if (visChanged && ParentGroup != null) + ParentGroup.UpdateVisible (this); + } + + internal override void RestoreAllocation () + { + base.RestoreAllocation (); + allocStatus = Size >= 0 ? AllocStatus.RestorePending : AllocStatus.NotSet; + foreach (DockObject ob in dockObjects) + ob.RestoreAllocation (); + } + + internal override void StoreAllocation () + { + base.StoreAllocation (); + foreach (DockObject ob in dockObjects) + ob.StoreAllocation (); + if (Type == DockGroupType.Tabbed && boundTabStrip != null) + currentTabPage = boundTabStrip.CurrentTab; + } + + public override bool Expand { + get { + foreach (DockObject ob in dockObjects) + if (ob.Expand) + return true; + return false; + } + } + + public override void SizeAllocate (Gdk.Rectangle newAlloc) + { + Gdk.Rectangle oldAlloc = Allocation; + base.SizeAllocate (newAlloc); + + if (type == DockGroupType.Tabbed) { + if (boundTabStrip != null) { + int tabsHeight = boundTabStrip.SizeRequest ().Height; + boundTabStrip.SizeAllocate (new Gdk.Rectangle (newAlloc.X, newAlloc.Bottom - tabsHeight, newAlloc.Width, tabsHeight)); + } + if (allocStatus == AllocStatus.Valid && newAlloc == oldAlloc) { + // Even if allocation has not changed, SizeAllocation has to be called on all items to avoid redrawing issues. + foreach (DockObject ob in VisibleObjects) + ob.SizeAllocate (ob.Allocation); + return; + } + if (VisibleObjects.Count > 1 && boundTabStrip != null) { + int tabsHeight = boundTabStrip.SizeRequest ().Height; + newAlloc.Height -= tabsHeight; + boundTabStrip.QueueDraw (); + } else if (VisibleObjects.Count != 0) { + ((DockGroupItem)VisibleObjects [0]).Item.Widget.Show (); + } + allocStatus = AllocStatus.Valid; + foreach (DockObject ob in VisibleObjects) { + ob.Size = ob.PrefSize = -1; + ob.SizeAllocate (newAlloc); + } + return; + } + + bool horiz = type == DockGroupType.Horizontal; + int pos = horiz ? Allocation.Left : Allocation.Top; + + if (allocStatus == AllocStatus.Valid && newAlloc == oldAlloc) { + // The layout of this group (as a whole) has not changed, but the layout + // of child items may have changed. Assign the new sizes. + + if (CheckMinSizes ()) + allocStatus = AllocStatus.NewSizeRequest; + else { + foreach (DockObject ob in VisibleObjects) { + Gdk.Rectangle rect; + int ins = ob.AllocSize; + if (horiz) + rect = new Gdk.Rectangle (pos, Allocation.Y, ins, Allocation.Height); + else + rect = new Gdk.Rectangle (Allocation.X, pos, Allocation.Width, ins); + ob.SizeAllocate (rect); + pos += ins + Frame.TotalHandleSize; + } + return; + } + } + + // This is the space available for the child items (excluding size + // required for the resize handles) + int realSize = GetRealSize (VisibleObjects); + + if (allocStatus == AllocStatus.NotSet/* || allocStatus == AllocStatus.RestorePending*/) { + // It is the first size allocation. Calculate all sizes. + CalcNewSizes (); + } + else if (allocStatus != AllocStatus.NewSizeRequest) { + // Available space has changed, so the size of the items must be changed. + // First of all, get the change fraction + double change; + if (horiz) + change = (double) newAlloc.Width / (double) oldAlloc.Width; + else + change = (double) newAlloc.Height / (double) oldAlloc.Height; + + // Get the old total size of the visible objects. Used to calculate the + // proportion of size of each item. + double tsize = 0; + double rsize = 0; + foreach (DockObject ob in VisibleObjects) { + tsize += ob.PrefSize; + rsize += ob.Size; + } + + foreach (DockObject ob in dockObjects) { + if (ob.Visible) { + // Proportionally spread the new available space among all visible objects + ob.Size = ob.PrefSize = (ob.PrefSize / tsize) * (double) realSize; + } else { + // For non-visible objects, change the size by the same grow fraction. In this + // way, when the item is shown again, it size will have the correct proportions. + ob.Size = ob.Size * change; + ob.PrefSize = ob.PrefSize * change; + } + ob.DefaultSize = ob.DefaultSize * change; + } + CheckMinSizes (); + } + + allocStatus = AllocStatus.Valid; + + // Sizes for all items have been set. + // Sizes are real numbers to ensure that the values are not degradated when resizing + // pixel by pixel. Now those have to be converted to integers, that is, actual allocated sizes. + + int ts = 0; + for (int n=0; n<VisibleObjects.Count; n++) { + DockObject ob = VisibleObjects [n]; + + int ins = (int) Math.Truncate (ob.Size); + + if (n == VisibleObjects.Count - 1) + ins = realSize - ts; + + ts += ins; + + if (ins < 0) + ins = 0; + + ob.AllocSize = ins; + + if (horiz) + ob.SizeAllocate (new Gdk.Rectangle (pos, Allocation.Y, ins, Allocation.Height)); + else + ob.SizeAllocate (new Gdk.Rectangle (Allocation.X, pos, Allocation.Width, ins)); + + pos += ins + Frame.TotalHandleSize; + } + } + + int GetRealSize (List<DockObject> objects) + { + // Returns the space available for the child items (excluding size + // required for the resize handles) + + int realSize; + if (type == DockGroupType.Horizontal) + realSize = Allocation.Width; + else + realSize = Allocation.Height; + + // Ignore space required for the handles + if (objects.Count > 1) + realSize -= (Frame.TotalHandleSize * (objects.Count - 1)); + + return realSize; + } + + internal void CalcNewSizes () + { + // Calculates the size assigned by default to each child item. + // Size is proportionally assigned to each item, taking into account + // the available space, and the default size of each item. + + // If there are items with the Expand flag set, those will proportionally + // take the space left after allocating the other (not exandable) items. + + // This is the space available for the child items (excluding size + // required for the resize handles) + double realSize = (double) GetRealSize (VisibleObjects); + + bool hasExpandItems = false; + double noexpandSize = 0; + double minExpandSize = 0; + double defaultExpandSize = 0; + + for (int n=0; n<VisibleObjects.Count; n++) { + DockObject ob = VisibleObjects [n]; + if (ob.Expand) { + minExpandSize += ob.MinSize; + defaultExpandSize += ob.DefaultSize; + hasExpandItems = true; + } + else { + ob.Size = ob.DefaultSize; + noexpandSize += ob.DefaultSize; + } + } + + double expandSize = realSize - noexpandSize; + foreach (DockObject ob in VisibleObjects) { + if (!hasExpandItems) + ob.Size = (ob.DefaultSize / noexpandSize) * realSize; + else if (ob.Expand) + ob.Size = (ob.DefaultSize / defaultExpandSize) * expandSize; + ob.PrefSize = ob.Size; + } + + CheckMinSizes (); + } + + bool CheckMinSizes () + { + // Checks if any of the items has a size smaller than permitted. + // In this case it tries to regain size by reducing other items. + + // First of all calculate the size to be regained, and the size available + // from other items + + bool sizesChanged = false; + + double avSize = 0; + double regSize = 0; + foreach (DockObject ob in VisibleObjects) { + if (ob.Size < ob.MinSize) { + regSize += ob.MinSize - ob.Size; + ob.Size = ob.MinSize; + sizesChanged = true; + } else { + avSize += ob.Size - ob.MinSize; + } + } + + if (!sizesChanged) + return false; + + // Now spread the required size among the resizable items + + if (regSize > avSize) + regSize = avSize; + + double ratio = (avSize - regSize) / avSize; + foreach (DockObject ob in VisibleObjects) { + if (ob.Size <= ob.MinSize) + continue; + double avs = ob.Size - ob.MinSize; + ob.Size = ob.MinSize + avs * ratio; + } + return sizesChanged; + } + + internal override Gtk.Requisition SizeRequest () + { + bool getMaxW = true, getMaxH = true; + if (type == DockGroupType.Horizontal) + getMaxW = false; + else if (type == DockGroupType.Vertical) + getMaxH = false; + + Requisition ret = new Requisition (); + ret.Height = VisibleObjects.Count * Frame.TotalHandleSize; + foreach (DockObject ob in VisibleObjects) { + Requisition req = ob.SizeRequest (); + if (getMaxH) { + if (req.Height > ret.Height) + ret.Height = req.Height; + } else + ret.Height += req.Height; + + if (getMaxW) { + if (req.Width > ret.Width) + ret.Width = req.Width; + } else + ret.Width += req.Width; + } + if (type == DockGroupType.Tabbed && VisibleObjects.Count > 1 && boundTabStrip != null) { + Gtk.Requisition tabs = boundTabStrip.SizeRequest (); + ret.Height += tabs.Height; + if (ret.Width < tabs.Width) + ret.Width = tabs.Width; + } + return ret; + } + + internal void UpdateNotebook (TabStrip ts) + { + Gtk.Widget oldpage = null; + int oldtab = -1; + + if (tabFocus != null) { + oldpage = tabFocus.Item.Widget; + tabFocus = null; + } else if (boundTabStrip != null) { + oldpage = boundTabStrip.CurrentPage; + oldtab = boundTabStrip.CurrentTab; + } + + ts.Clear (); + + // Add missing pages + foreach (DockObject ob in VisibleObjects) { + DockGroupItem it = ob as DockGroupItem; + ts.AddTab (it.Item.Widget, it.Item.Icon, it.Item.Label); + } + + boundTabStrip = ts; + + if (currentTabPage != -1 && currentTabPage < boundTabStrip.TabCount) { + boundTabStrip.CurrentTab = currentTabPage; + // Discard the currentTabPage value. Current page is now tracked by the tab strip + currentTabPage = -1; + } + else if (oldpage != null) + boundTabStrip.CurrentPage = oldpage; + + if (boundTabStrip.CurrentTab == -1) { + if (oldtab != -1) { + if (oldtab < boundTabStrip.TabCount) + boundTabStrip.CurrentTab = oldtab; + else + boundTabStrip.CurrentTab = boundTabStrip.TabCount - 1; + } else + boundTabStrip.CurrentTab = 0; + } + if (Frame.CompactGuiLevel == 3 && IsNextToMargin (PositionType.Bottom)) + boundTabStrip.BottomPadding = 3; + else + boundTabStrip.BottomPadding = 0; + } + + internal void Present (DockItem it, bool giveFocus) + { + if (type == DockGroupType.Tabbed) { + for (int n=0; n<VisibleObjects.Count; n++) { + DockGroupItem dit = VisibleObjects[n] as DockGroupItem; + if (dit.Item == it) { + currentTabPage = n; + if (boundTabStrip != null) + boundTabStrip.CurrentPage = it.Widget; + break; + } + } + } + if (giveFocus && it.Visible) + it.SetFocus (); + } + + internal bool IsSelectedPage (DockItem it) + { + if (type != DockGroupType.Tabbed || boundTabStrip == null || boundTabStrip.CurrentTab == -1 || VisibleObjects == null || boundTabStrip.CurrentTab >= VisibleObjects.Count) + return false; + DockGroupItem dit = VisibleObjects[boundTabStrip.CurrentTab] as DockGroupItem; + return dit.Item == it; + } + + internal void UpdateTitle (DockItem it) + { + if (it.Visible && type == DockGroupType.Tabbed && boundTabStrip != null) + boundTabStrip.SetTabLabel (it.Widget, it.Icon, it.Label); + } + + internal void FocusItem (DockGroupItem it) + { + tabFocus = it; + } + + internal void ResetNotebook () + { + boundTabStrip = null; + } + + public void LayoutWidgets () + { + foreach (DockObject ob in VisibleObjects) { + DockGroupItem it = ob as DockGroupItem; + if (it != null) { + if (it.Item.Widget.Parent == null) + it.Item.Widget.Parent = Frame.Container; + if (!it.Item.Widget.Visible && type != DockGroupType.Tabbed) + it.Item.Widget.Show (); + } + else + ((DockGroup)ob).LayoutWidgets (); + } + } + + internal override void GetDefaultSize (out int width, out int height) + { + if (type == DockGroupType.Tabbed) { + width = -1; + height = -1; + foreach (DockObject ob in VisibleObjects) { + int dh, dw; + ob.GetDefaultSize (out dw, out dh); + if (dw > width) + width = dw; + if (dh > height) + height = dh; + } + } + else if (type == DockGroupType.Vertical) { + height = VisibleObjects.Count > 0 ? (VisibleObjects.Count - 1) * Frame.TotalHandleSize : 0; + width = -1; + foreach (DockObject ob in VisibleObjects) { + int dh, dw; + ob.GetDefaultSize (out dw, out dh); + if (dw > width) + width = dw; + height += dh; + } + } + else { + width = VisibleObjects.Count > 0 ? (VisibleObjects.Count - 1) * Frame.TotalHandleSize : 0; + height = -1; + foreach (DockObject ob in VisibleObjects) { + int dh, dw; + ob.GetDefaultSize (out dw, out dh); + if (dh > height) + height = dh; + width += dw; + } + } + } + + internal override void GetMinSize (out int width, out int height) + { + if (type == DockGroupType.Tabbed) { + width = -1; + height = -1; + foreach (DockObject ob in VisibleObjects) { + int dh, dw; + ob.GetMinSize (out dw, out dh); + if (dw > width) + width = dw; + if (dh > height) + height = dh; + } + } + else if (type == DockGroupType.Vertical) { + height = VisibleObjects.Count > 1 ? (VisibleObjects.Count - 1) * Frame.TotalHandleSize : 0; + width = -1; + foreach (DockObject ob in VisibleObjects) { + int dh, dw; + ob.GetMinSize (out dw, out dh); + if (dw > width) + width = dw; + height += dh; + } + } + else { + width = VisibleObjects.Count > 0 ? (VisibleObjects.Count - 1) * Frame.TotalHandleSize : 0; + height = -1; + foreach (DockObject ob in VisibleObjects) { + int dh, dw; + ob.GetMinSize (out dw, out dh); + if (dh > height) + height = dh; + width += dw; + } + } + } + + public void Draw (Gdk.Rectangle exposedArea, DockGroup currentHandleGrp, int currentHandleIndex) + { + if (type != DockGroupType.Tabbed) { + DrawSeparators (exposedArea, currentHandleGrp, currentHandleIndex, false, false, null); + foreach (DockObject it in VisibleObjects) { + DockGroup grp = it as DockGroup; + if (grp != null) + grp.Draw (exposedArea, currentHandleGrp, currentHandleIndex); + } + } + } + + public void DrawSeparators (Gdk.Rectangle exposedArea, DockGroup currentHandleGrp, int currentHandleIndex, bool invalidateOnly, List<Gdk.Rectangle> areasList) + { + DrawSeparators (exposedArea, currentHandleGrp, currentHandleIndex, invalidateOnly, true, areasList); + } + + void DrawSeparators (Gdk.Rectangle exposedArea, DockGroup currentHandleGrp, int currentHandleIndex, bool invalidateOnly, bool drawChildrenSep, List<Gdk.Rectangle> areasList) + { + if (type == DockGroupType.Tabbed || VisibleObjects.Count == 0) + return; + + DockObject last = VisibleObjects [VisibleObjects.Count - 1]; + + bool horiz = type == DockGroupType.Horizontal; + int x = Allocation.X; + int y = Allocation.Y; + int hw = horiz ? Frame.HandleSize : Allocation.Width; + int hh = horiz ? Allocation.Height : Frame.HandleSize; + Gtk.Orientation or = horiz ? Gtk.Orientation.Vertical : Gtk.Orientation.Horizontal; + + for (int n=0; n<VisibleObjects.Count; n++) { + DockObject ob = VisibleObjects [n]; + DockGroup grp = ob as DockGroup; + if (grp != null && drawChildrenSep) + grp.DrawSeparators (exposedArea, currentHandleGrp, currentHandleIndex, invalidateOnly, areasList); + if (ob != last) { + if (horiz) + x += ob.Allocation.Width + Frame.HandlePadding; + else + y += ob.Allocation.Height + Frame.HandlePadding; + + if (areasList != null) { + if (Frame.ShadedSeparators) + areasList.Add (new Gdk.Rectangle (x, y, hw, hh)); + } else if (invalidateOnly) { + Frame.Container.QueueDrawArea (x, y, hw, hh); + } + else { + if (Frame.ShadedSeparators) { + Frame.ShadedContainer.DrawBackground (Frame.Container, new Gdk.Rectangle (x, y, hw, hh)); + } else { + StateType state = (currentHandleGrp == this && currentHandleIndex == n) ? StateType.Prelight : StateType.Normal; + if (!DockFrame.IsWindows) + Gtk.Style.PaintHandle (Frame.Style, Frame.Container.GdkWindow, state, ShadowType.None, exposedArea, Frame, "paned", x, y, hw, hh, or); + } + } + + if (horiz) + x += Frame.HandleSize + Frame.HandlePadding; + else + y += Frame.HandleSize + Frame.HandlePadding; + } + } + } + + public void ResizeItem (int index, int newSize) + { + DockObject o1 = VisibleObjects [index]; + DockObject o2 = VisibleObjects [index+1]; + + int dsize; + + dsize = newSize - o1.AllocSize; + if (dsize < 0 && o1.AllocSize + dsize < o1.MinSize) + dsize = o1.MinSize - o1.AllocSize; + else if (dsize > 0 && o2.AllocSize - dsize < o2.MinSize) + dsize = o2.AllocSize - o2.MinSize; + + // Assign the new sizes, applying the current ratio + double sizeDif = (double)dsize; + + o1.AllocSize += dsize; + o2.AllocSize -= dsize; + + o1.DefaultSize += (o1.DefaultSize * sizeDif) / o1.Size; + o1.Size = o1.AllocSize; + o1.PrefSize = o1.Size; + + o2.DefaultSize -= (o2.DefaultSize * sizeDif) / o2.Size; + o2.Size = o2.AllocSize; + o2.PrefSize = o2.Size; + + o1.QueueResize (); + o2.QueueResize (); + } + + internal override void QueueResize () + { + foreach (DockObject obj in VisibleObjects) + obj.QueueResize (); + } + + internal double GetObjectsSize () + { + double total = 0; + foreach (DockObject obj in VisibleObjects) + total += obj.Size; + return total; + } + + void DockTarget (DockItem item, int n) + { + DockGroupItem gitem = new DockGroupItem (Frame, item); + dockObjects.Insert (n, gitem); + gitem.ParentGroup = this; + gitem.SetVisible (true); + ResetVisibleGroups (); + CalcNewSizes (); + } + + internal override bool GetDockTarget (DockItem item, int px, int py, out DockDelegate dockDelegate, out Gdk.Rectangle rect) + { + if (!Allocation.Contains (px, py) || VisibleObjects.Count == 0) { + dockDelegate = null; + rect = Gdk.Rectangle.Zero; + return false; + } + + if (type == DockGroupType.Tabbed) { + // Tabs can only contain DockGroupItems + return ((DockGroupItem)VisibleObjects[0]).GetDockTarget (item, px, py, Allocation, out dockDelegate, out rect); + } + else if (type == DockGroupType.Horizontal) { + if (px >= Allocation.Right - DockFrame.GroupDockSeparatorSize) { + // Dock to the right of the group + dockDelegate = delegate (DockItem it) { + DockTarget (it, dockObjects.Count); + }; + rect = new Gdk.Rectangle (Allocation.Right - DockFrame.GroupDockSeparatorSize, Allocation.Y, DockFrame.GroupDockSeparatorSize, Allocation.Height); + return true; + } + else if (px <= Allocation.Left + DockFrame.GroupDockSeparatorSize) { + // Dock to the left of the group + dockDelegate = delegate (DockItem it) { + DockTarget (it, 0); + }; + rect = new Gdk.Rectangle (Allocation.Left, Allocation.Y, DockFrame.GroupDockSeparatorSize, Allocation.Height); + return true; + } + // Dock in a separator + for (int n=0; n<VisibleObjects.Count; n++) { + DockObject ob = VisibleObjects [n]; + if (n < VisibleObjects.Count - 1 && + px >= ob.Allocation.Right - DockFrame.GroupDockSeparatorSize/2 && + px <= ob.Allocation.Right + DockFrame.GroupDockSeparatorSize/2) + { + int dn = dockObjects.IndexOf (ob); + dockDelegate = delegate (DockItem it) { + DockTarget (it, dn+1); + }; + rect = new Gdk.Rectangle (ob.Allocation.Right - DockFrame.GroupDockSeparatorSize/2, Allocation.Y, DockFrame.GroupDockSeparatorSize, Allocation.Height); + return true; + } + else if (ob.GetDockTarget (item, px, py, out dockDelegate, out rect)) + return true; + } + } + else if (type == DockGroupType.Vertical) { + if (py >= Allocation.Bottom - DockFrame.GroupDockSeparatorSize) { + // Dock to the bottom of the group + dockDelegate = delegate (DockItem it) { + DockTarget (it, dockObjects.Count); + }; + rect = new Gdk.Rectangle (Allocation.X, Allocation.Bottom - DockFrame.GroupDockSeparatorSize, Allocation.Width, DockFrame.GroupDockSeparatorSize); + return true; + } + else if (py <= Allocation.Top + DockFrame.GroupDockSeparatorSize) { + // Dock to the top of the group + dockDelegate = delegate (DockItem it) { + DockTarget (it, 0); + }; + rect = new Gdk.Rectangle (Allocation.X, Allocation.Top, Allocation.Width, DockFrame.GroupDockSeparatorSize); + return true; + } + // Dock in a separator + for (int n=0; n<VisibleObjects.Count; n++) { + DockObject ob = VisibleObjects [n]; + if (n < VisibleObjects.Count - 1 && + py >= ob.Allocation.Bottom - DockFrame.GroupDockSeparatorSize/2 && + py <= ob.Allocation.Bottom + DockFrame.GroupDockSeparatorSize/2) + { + int dn = dockObjects.IndexOf (ob); + dockDelegate = delegate (DockItem it) { + DockTarget (it, dn+1); + }; + rect = new Gdk.Rectangle (Allocation.X, ob.Allocation.Bottom - DockFrame.GroupDockSeparatorSize/2, Allocation.Width, DockFrame.GroupDockSeparatorSize); + return true; + } + else if (ob.GetDockTarget (item, px, py, out dockDelegate, out rect)) + return true; + } + } + dockDelegate = null; + rect = Gdk.Rectangle.Zero; + return false; + } + + public void ReplaceItem (DockObject ob1, DockObject ob2) + { + int i = dockObjects.IndexOf (ob1); + dockObjects [i] = ob2; + ob2.ParentGroup = this; + ob2.ResetDefaultSize (); + ob2.Size = ob1.Size; + ob2.DefaultSize = ob1.DefaultSize; + ob2.AllocSize = ob1.AllocSize; + ResetVisibleGroups (); + } + + public override void CopyFrom (DockObject other) + { + base.CopyFrom (other); + DockGroup grp = (DockGroup) other; + dockObjects = new List<DockObject> (); + foreach (DockObject ob in grp.dockObjects) { + DockObject cob = ob.Clone (); + cob.ParentGroup = this; + dockObjects.Add (cob); + } + type = grp.type; + ResetVisibleGroups (); + boundTabStrip = null; + tabFocus = null; + } + + internal override bool Visible { + get { + foreach (DockObject ob in dockObjects) + if (ob.Visible) + return true; + return false; + } + } + + internal void Dump () + { + Dump (0); + } + + internal override void Dump (int ind) + { + Console.WriteLine (new string (' ', ind) + "Group (" + type + ") size:" + Size + " DefaultSize:" + DefaultSize + " alloc:" + Allocation); + foreach (DockObject ob in dockObjects) { + ob.Dump (ind + 2); + } + } + + internal override void Write (XmlWriter writer) + { + base.Write (writer); + writer.WriteAttributeString ("type", type.ToString ()); + if (type == DockGroupType.Tabbed && currentTabPage != -1) + writer.WriteAttributeString ("currentTabPage", currentTabPage.ToString ()); + + foreach (DockObject ob in dockObjects) { + if (ob is DockGroupItem) + writer.WriteStartElement ("item"); + else + writer.WriteStartElement ("group"); + ob.Write (writer); + writer.WriteEndElement (); + } + } + + internal override void Read (XmlReader reader) + { + base.Read (reader); + type = (DockGroupType) Enum.Parse (typeof(DockGroupType), reader.GetAttribute ("type")); + if (type == DockGroupType.Tabbed) { + string s = reader.GetAttribute ("currentTabPage"); + if (s != null) + currentTabPage = int.Parse (s); + } + + reader.MoveToElement (); + if (reader.IsEmptyElement) { + reader.Skip (); + return; + } + + reader.ReadStartElement (); + reader.MoveToContent (); + while (reader.NodeType != XmlNodeType.EndElement) { + if (reader.NodeType == XmlNodeType.Element) { + if (reader.LocalName == "item") { + string id = reader.GetAttribute ("id"); + DockItem it = Frame.GetItem (id); + if (it == null) { + it = Frame.AddItem (id); + it.IsPositionMarker = true; + } + DockGroupItem gitem = new DockGroupItem (Frame, it); + gitem.Read (reader); + AddObject (gitem); + + reader.MoveToElement (); + reader.Skip (); + } + else if (reader.LocalName == "group") { + DockGroup grp = new DockGroup (Frame); + grp.Read (reader); + AddObject (grp); + } + } + else + reader.Skip (); + reader.MoveToContent (); + } + reader.ReadEndElement (); + } + + public bool IsChildNextToMargin (Gtk.PositionType margin, DockObject obj) + { + if (type == DockGroupType.Tabbed) + return true; + else if (type == DockGroupType.Horizontal) { + if (margin == PositionType.Top || margin == PositionType.Bottom) + return true; + int i = VisibleObjects.IndexOf (obj); + if (margin == PositionType.Left && i == 0) + return true; + if (margin == PositionType.Right && i == VisibleObjects.Count - 1) + return true; + } + else if (type == DockGroupType.Vertical) { + if (margin == PositionType.Left || margin == PositionType.Right) + return true; + int i = VisibleObjects.IndexOf (obj); + if (margin == PositionType.Top && i == 0) + return true; + if (margin == PositionType.Bottom && i == VisibleObjects.Count - 1) + return true; + } + return false; + } + + internal TabStrip TabStrip { + get { return boundTabStrip; } + } + + public override string ToString () + { + return "[DockGroup " + type + "]"; + } + } +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockGroupItem.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockGroupItem.cs new file mode 100644 index 0000000000..89e01c2b90 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockGroupItem.cs @@ -0,0 +1,385 @@ +// +// DockGroupItem.cs +// +// Author: +// Lluis Sanchez Gual +// + +// +// Copyright (C) 2007 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.Xml; +using Gtk; + +namespace MonoDevelop.Components.Docking +{ + internal class DockGroupItem: DockObject + { + DockItem item; + bool visibleFlag; + DockItemStatus status; + Gdk.Rectangle floatRect; + Gtk.PositionType barDocPosition; + int autoHideSize = -1; + + public DockItem Item { + get { + return item; + } + set { + item = value; + } + } + + public string Id { + get { return item.Id; } + } + + public DockGroupItem (DockFrame frame, DockItem item): base (frame) + { + this.item = item; + visibleFlag = item.Visible; + } + + internal override void GetDefaultSize (out int width, out int height) + { + width = item.DefaultWidth; + height = item.DefaultHeight; + } + + internal override void GetMinSize (out int width, out int height) + { + Requisition req = SizeRequest (); + width = req.Width; + height = req.Height; + } + + internal override Requisition SizeRequest () + { + return item.Widget.SizeRequest (); + } + + public override void SizeAllocate (Gdk.Rectangle newAlloc) + { + item.Widget.SizeAllocate (newAlloc); + base.SizeAllocate (newAlloc); + } + + public override bool Expand { + get { return item.Expand; } + } + + internal override void QueueResize () + { + item.Widget.QueueResize (); + } + + internal override bool GetDockTarget (DockItem item, int px, int py, out DockDelegate dockDelegate, out Gdk.Rectangle rect) + { + return GetDockTarget (item, px, py, Allocation, out dockDelegate, out rect); + } + + public bool GetDockTarget (DockItem item, int px, int py, Gdk.Rectangle rect, out DockDelegate dockDelegate, out Gdk.Rectangle outrect) + { + dockDelegate = null; + + if (item != this.item && this.item.Visible && rect.Contains (px, py)) { + int xdockMargin = (int) ((double)rect.Width * (1.0 - DockFrame.ItemDockCenterArea)) / 2; + int ydockMargin = (int) ((double)rect.Height * (1.0 - DockFrame.ItemDockCenterArea)) / 2; + DockPosition pos; + +/* if (ParentGroup.Type == DockGroupType.Tabbed) { + rect = new Gdk.Rectangle (rect.X + xdockMargin, rect.Y + ydockMargin, rect.Width - xdockMargin*2, rect.Height - ydockMargin*2); + pos = DockPosition.CenterAfter; + } +*/ if (px <= rect.X + xdockMargin && ParentGroup.Type != DockGroupType.Horizontal) { + outrect = new Gdk.Rectangle (rect.X, rect.Y, xdockMargin, rect.Height); + pos = DockPosition.Left; + } + else if (px >= rect.Right - xdockMargin && ParentGroup.Type != DockGroupType.Horizontal) { + outrect = new Gdk.Rectangle (rect.Right - xdockMargin, rect.Y, xdockMargin, rect.Height); + pos = DockPosition.Right; + } + else if (py <= rect.Y + ydockMargin && ParentGroup.Type != DockGroupType.Vertical) { + outrect = new Gdk.Rectangle (rect.X, rect.Y, rect.Width, ydockMargin); + pos = DockPosition.Top; + } + else if (py >= rect.Bottom - ydockMargin && ParentGroup.Type != DockGroupType.Vertical) { + outrect = new Gdk.Rectangle (rect.X, rect.Bottom - ydockMargin, rect.Width, ydockMargin); + pos = DockPosition.Bottom; + } + else { + outrect = new Gdk.Rectangle (rect.X + xdockMargin, rect.Y + ydockMargin, rect.Width - xdockMargin*2, rect.Height - ydockMargin*2); + pos = DockPosition.Center; + } + + dockDelegate = delegate (DockItem dit) { + DockGroupItem it = ParentGroup.AddObject (dit, pos, Id); + it.SetVisible (true); + ParentGroup.FocusItem (it); + }; + return true; + } + outrect = Gdk.Rectangle.Zero; + return false; + } + + internal override void Dump (int ind) + { + Console.WriteLine (new string (' ', ind) + item.Id + " size:" + Size + " alloc:" + Allocation); + } + + internal override void Write (XmlWriter writer) + { + base.Write (writer); + writer.WriteAttributeString ("id", item.Id); + writer.WriteAttributeString ("visible", visibleFlag.ToString ()); + writer.WriteAttributeString ("status", status.ToString ()); + + if (status == DockItemStatus.AutoHide) + writer.WriteAttributeString ("autoHidePosition", barDocPosition.ToString ()); + + if (autoHideSize != -1) + writer.WriteAttributeString ("autoHideSize", autoHideSize.ToString ()); + + if (!floatRect.Equals (Gdk.Rectangle.Zero)) { + writer.WriteAttributeString ("floatX", floatRect.X.ToString ()); + writer.WriteAttributeString ("floatY", floatRect.Y.ToString ()); + writer.WriteAttributeString ("floatWidth", floatRect.Width.ToString ()); + writer.WriteAttributeString ("floatHeight", floatRect.Height.ToString ()); + } + } + + internal override void Read (XmlReader reader) + { + base.Read (reader); + visibleFlag = bool.Parse (reader.GetAttribute ("visible")) && !item.IsPositionMarker; + status = (DockItemStatus) Enum.Parse (typeof (DockItemStatus), reader.GetAttribute ("status")); + int fx=0, fy=0, fw=0, fh=0; + string s = reader.GetAttribute ("floatX"); + if (s != null) + fx = int.Parse (s); + s = reader.GetAttribute ("floatY"); + if (s != null) + fy = int.Parse (s); + s = reader.GetAttribute ("floatWidth"); + if (s != null) + fw = int.Parse (s); + s = reader.GetAttribute ("floatHeight"); + if (s != null) + fh = int.Parse (s); + s = reader.GetAttribute ("autoHidePosition"); + if (s != null) + barDocPosition = (PositionType) Enum.Parse (typeof (PositionType), s); + s = reader.GetAttribute ("autoHideSize"); + if (s != null) + autoHideSize = int.Parse (s); + floatRect = new Gdk.Rectangle (fx, fy, fw, fh); + } + + public override void CopyFrom (DockObject ob) + { + base.CopyFrom (ob); + DockGroupItem it = (DockGroupItem)ob; + item = it.item; + visibleFlag = it.visibleFlag; + floatRect = it.floatRect; + } + + internal override bool Visible { + get { return visibleFlag && status == DockItemStatus.Dockable; } + } + + internal bool VisibleFlag { + get { return visibleFlag; } + } + + public DockItemStatus Status { + get { + return status; + } + set { + if (status == value) + return; + + DockItemStatus oldValue = status; + status = value; + + if (status == DockItemStatus.Floating) { + if (floatRect.Equals (Gdk.Rectangle.Zero)) { + int x, y; + item.Widget.TranslateCoordinates (item.Widget.Toplevel, 0, 0, out x, out y); + Gtk.Window win = Frame.Toplevel as Window; + if (win != null) { + int wx, wy; + win.GetPosition (out wx, out wy); + floatRect = new Gdk.Rectangle (wx + x, wy + y, Allocation.Width, Allocation.Height); + } + } + item.SetFloatMode (floatRect); + } + else if (status == DockItemStatus.AutoHide) { + SetBarDocPosition (); + item.SetAutoHideMode (barDocPosition, GetAutoHideSize (barDocPosition)); + } + else + item.ResetMode (); + + if (oldValue == DockItemStatus.Dockable || status == DockItemStatus.Dockable) { + // Update visibility if changing from/to dockable mode + if (ParentGroup != null) + ParentGroup.UpdateVisible (this); + } + } + } + + void SetBarDocPosition () + { + // Determine the best position for docking the item + + if (Allocation.IsEmpty) { + // If the item is in a group, use the dock location of other items + DockObject current = this; + do { + if (EstimateBarDocPosition (current.ParentGroup, current, out barDocPosition, out autoHideSize)) + return; + current = current.ParentGroup; + } while (current.ParentGroup != null); + + // Can't find a good location. Just guess. + barDocPosition = PositionType.Bottom; + autoHideSize = 200; + return; + } + barDocPosition = CalcBarDocPosition (); + } + + bool EstimateBarDocPosition (DockGroup grp, DockObject ignoreChild, out PositionType pos, out int size) + { + foreach (DockObject ob in grp.Objects) { + if (ob == ignoreChild) + continue; + if (ob is DockGroup) { + if (EstimateBarDocPosition ((DockGroup)ob, null, out pos, out size)) + return true; + } else if (ob is DockGroupItem) { + DockGroupItem it = (DockGroupItem) ob; + if (it.status == DockItemStatus.AutoHide) { + pos = it.barDocPosition; + size = it.autoHideSize; + return true; + } + if (!it.Allocation.IsEmpty) { + pos = it.CalcBarDocPosition (); + size = it.GetAutoHideSize (pos); + return true; + } + } + } + pos = PositionType.Bottom; + size = 0; + return false; + } + + PositionType CalcBarDocPosition () + { + if (Allocation.Width < Allocation.Height) { + int mid = Allocation.Left + Allocation.Width / 2; + if (mid > Frame.Allocation.Left + Frame.Allocation.Width / 2) + return PositionType.Right; + else + return PositionType.Left; + } else { + int mid = Allocation.Top + Allocation.Height / 2; + if (mid > Frame.Allocation.Top + Frame.Allocation.Height / 2) + return PositionType.Bottom; + else + return PositionType.Top; + } + } + + internal void SetVisible (bool value) + { + if (visibleFlag != value) { + visibleFlag = value; + if (visibleFlag) + item.ShowWidget (); + else + item.HideWidget (); + if (ParentGroup != null) + ParentGroup.UpdateVisible (this); + } + } + + internal override void StoreAllocation () + { + base.StoreAllocation (); + if (Status == DockItemStatus.Floating) + floatRect = item.FloatingPosition; + else if (Status == DockItemStatus.AutoHide) + autoHideSize = item.AutoHideSize; + } + + internal override void RestoreAllocation () + { + base.RestoreAllocation (); + item.UpdateVisibleStatus (); + + if (Status == DockItemStatus.Floating) + item.SetFloatMode (floatRect); + else if (Status == DockItemStatus.AutoHide) + item.SetAutoHideMode (barDocPosition, GetAutoHideSize (barDocPosition)); + else + item.ResetMode (); + + if (!visibleFlag) + item.HideWidget (); + } + + int GetAutoHideSize (Gtk.PositionType pos) + { + if (autoHideSize != -1) + return autoHideSize; + + if (pos == PositionType.Left || pos == PositionType.Right) + return Allocation.Width; + else + return Allocation.Height; + } + + public Gdk.Rectangle FloatRect { + get { + return floatRect; + } + set { + floatRect = value; + } + } + + public override string ToString () + { + return "[DockItem " + Item.Id + "]"; + } + } +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockGroupType.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockGroupType.cs new file mode 100644 index 0000000000..f77a3e9a60 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockGroupType.cs @@ -0,0 +1,41 @@ +// +// DockGroupType.cs +// +// Author: +// Lluis Sanchez Gual +// + +// +// Copyright (C) 2007 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; + +namespace MonoDevelop.Components.Docking +{ + enum DockGroupType + { + Horizontal, + Vertical, + Tabbed + } +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItem.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItem.cs new file mode 100644 index 0000000000..dd47a99572 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItem.cs @@ -0,0 +1,513 @@ +// +// DockItem.cs +// +// Author: +// Lluis Sanchez Gual +// + +// +// Copyright (C) 2007 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.Xml; +using Gtk; +using Mono.Unix; + +namespace MonoDevelop.Components.Docking +{ + public class DockItem + { + Widget content; + DockItemContainer widget; + string defaultLocation; + bool defaultVisible = true; + DockItemStatus defaultStatus = DockItemStatus.Dockable; + string id; + DockFrame frame; + int defaultWidth = -1; + int defaultHeight = -1; + string label; + Gdk.Pixbuf icon; + bool expand; + bool drawFrame = true; + DockItemBehavior behavior; + Gtk.Window floatingWindow; + DockBarItem dockBarItem; + bool lastVisibleStatus; + bool lastContentVisibleStatus; + bool gettingContent; + bool isPositionMarker; + bool stickyVisible; + IDockItemLabelProvider dockLabelProvider; + DockItemToolbar toolbarTop; + DockItemToolbar toolbarBottom; + DockItemToolbar toolbarLeft; + DockItemToolbar toolbarRight; + + public event EventHandler VisibleChanged; + public event EventHandler ContentVisibleChanged; + public event EventHandler ContentRequired; + + internal DockItem (DockFrame frame, string id) + { + this.frame = frame; + this.id = id; + } + + internal DockItem (DockFrame frame, Widget w, string id) + { + this.frame = frame; + this.id = id; + content = w; + } + + public string Id { + get { return id; } + } + + internal bool StickyVisible { + get { return stickyVisible; } + set { stickyVisible = value; } + } + + public string Label { + get { return label ?? string.Empty; } + set { + label = value; + if (widget != null) + widget.Label = label; + frame.UpdateTitle (this); + if (floatingWindow != null) + floatingWindow.Title = GetWindowTitle (); + } + } + + public bool Visible { + get { + return frame.GetVisible (this); + } + set { + stickyVisible = value; + frame.SetVisible (this, value); + UpdateVisibleStatus (); + } + } + + public bool VisibleInLayout (string layout) + { + return frame.GetVisible (this, layout); + } + + public DockItemStatus Status { + get { + return frame.GetStatus (this); + } + set { + frame.SetStatus (this, value); + } + } + + public IDockItemLabelProvider DockLabelProvider { + get { return this.dockLabelProvider; } + set { this.dockLabelProvider = value; } + } + + internal DockItemContainer Widget { + get { + if (widget == null) { + widget = new DockItemContainer (frame, this); + widget.Visible = false; // Required to ensure that the Shown event is fired + widget.Label = label; + widget.Shown += SetupContent; + } + return widget; + } + } + + void SetupContent (object ob, EventArgs args) + { + widget.Shown -= SetupContent; + + if (ContentRequired != null) { + gettingContent = true; + try { + ContentRequired (this, EventArgs.Empty); + } finally { + gettingContent = false; + } + } + + widget.UpdateContent (); + widget.Shown += delegate { + UpdateContentVisibleStatus (); + }; + widget.Hidden += delegate { + UpdateContentVisibleStatus (); + }; + widget.ParentSet += delegate { + UpdateContentVisibleStatus (); + }; + UpdateContentVisibleStatus (); + } + + public Widget Content { + get { + return content; + } + set { + content = value; + if (!gettingContent && widget != null) + widget.UpdateContent (); + } + } + + public DockItemToolbar GetToolbar (PositionType position) + { + switch (position) { + case PositionType.Top: + if (toolbarTop == null) + toolbarTop = new DockItemToolbar (this, PositionType.Top); + return toolbarTop; + case PositionType.Bottom: + if (toolbarBottom == null) + toolbarBottom = new DockItemToolbar (this, PositionType.Bottom); + return toolbarBottom; + case PositionType.Left: + if (toolbarLeft == null) + toolbarLeft = new DockItemToolbar (this, PositionType.Left); + return toolbarLeft; + case PositionType.Right: + if (toolbarRight == null) + toolbarRight = new DockItemToolbar (this, PositionType.Right); + return toolbarRight; + default: throw new ArgumentException (); + } + } + + internal bool HasWidget { + get { return widget != null; } + } + + public string DefaultLocation { + get { return defaultLocation; } + set { defaultLocation = value; } + } + + public bool DefaultVisible { + get { return defaultVisible; } + set { defaultVisible = value; } + } + + public DockItemStatus DefaultStatus { + get { return defaultStatus; } + set { defaultStatus = value; } + } + + public int DefaultWidth { + get { + return defaultWidth; + } + set { + defaultWidth = value; + } + } + + public int DefaultHeight { + get { + return defaultHeight; + } + set { + defaultHeight = value; + } + } + + public Gdk.Pixbuf Icon { + get { + return icon; + } + set { + icon = value; + } + } + + public DockItemBehavior Behavior { + get { + return behavior; + } + set { + behavior = value; + if (widget != null) + widget.UpdateBehavior (); + } + } + + public bool Expand { + get { + return expand; + } + set { + expand = value; + } + } + + public bool DrawFrame { + get { + return drawFrame; + } + set { + drawFrame = value; + } + } + + public void Present (bool giveFocus) + { + if (dockBarItem != null) + dockBarItem.Present (Status == DockItemStatus.AutoHide || giveFocus); + else + frame.Present (this, Status == DockItemStatus.AutoHide || giveFocus); + } + + public bool ContentVisible { + get { + if (widget == null) + return false; + return widget.Parent != null && widget.Visible; + } + } + + internal void SetFocus () + { + SetFocus (Widget); + } + + internal static void SetFocus (Widget w) + { + w.ChildFocus (DirectionType.TabForward); + + Window win = w.Toplevel as Gtk.Window; + if (win == null) + return; + + // Make sure focus is not given to internal children + if (win.Focus != null) { + Container c = win.Focus.Parent as Container; + if (c.Children.Length == 0) + win.Focus = c; + } + } + + internal void UpdateVisibleStatus () + { + bool vis = frame.GetVisible (this); + if (vis != lastVisibleStatus) { + lastVisibleStatus = vis; + if (VisibleChanged != null) + VisibleChanged (this, EventArgs.Empty); + } + UpdateContentVisibleStatus (); + } + + internal void UpdateContentVisibleStatus () + { + bool vis = ContentVisible; + if (vis != lastContentVisibleStatus) { + lastContentVisibleStatus = vis; + if (ContentVisibleChanged != null) + ContentVisibleChanged (this, EventArgs.Empty); + } + } + + internal void ShowWidget () + { + if (floatingWindow != null) + floatingWindow.Show (); + if (dockBarItem != null) + dockBarItem.Show (); + Widget.Show (); + } + + internal void HideWidget () + { + if (floatingWindow != null) + floatingWindow.Hide (); + else if (dockBarItem != null) + dockBarItem.Hide (); + else if (widget != null) + widget.Hide (); + } + + internal void SetFloatMode (Gdk.Rectangle rect) + { + ResetBarUndockMode (); + if (floatingWindow == null) { + if (Widget.Parent != null) + Widget.Unparent (); + floatingWindow = new Window (GetWindowTitle ()); + floatingWindow.TransientFor = frame.Toplevel as Gtk.Window; + floatingWindow.TypeHint = Gdk.WindowTypeHint.Utility; + floatingWindow.Add (Widget); + floatingWindow.DeleteEvent += delegate (object o, DeleteEventArgs a) { + if (behavior == DockItemBehavior.CantClose) + Status = DockItemStatus.Dockable; + else + Visible = false; + a.RetVal = true; + }; + } + floatingWindow.Move (rect.X, rect.Y); + floatingWindow.Resize (rect.Width, rect.Height); + floatingWindow.Show (); + Widget.UpdateBehavior (); + Widget.Show (); + } + + internal void ResetFloatMode () + { + if (floatingWindow != null) { + floatingWindow.Remove (Widget); + floatingWindow.Destroy (); + floatingWindow = null; + widget.UpdateBehavior (); + } + } + + internal Gdk.Rectangle FloatingPosition { + get { + if (floatingWindow != null) { + int x,y,w,h; + floatingWindow.GetPosition (out x, out y); + floatingWindow.GetSize (out w, out h); + return new Gdk.Rectangle (x,y,w,h); + } + else + return Gdk.Rectangle.Zero; + } + } + + internal void ResetMode () + { + ResetFloatMode (); + ResetBarUndockMode (); + } + + internal void SetAutoHideMode (Gtk.PositionType pos, int size) + { + ResetMode (); + if (widget != null) { + widget.Hide (); // Avoids size allocation warning + widget.Unparent (); + } + dockBarItem = frame.BarDock (pos, this, size); + if (widget != null) + widget.UpdateBehavior (); + } + + void ResetBarUndockMode () + { + if (dockBarItem != null) { + dockBarItem.Close (); + dockBarItem = null; + if (widget != null) + widget.UpdateBehavior (); + } + } + + internal int AutoHideSize { + get { + if (dockBarItem != null) + return dockBarItem.Size; + else + return -1; + } + } + + internal bool IsPositionMarker { + get { + return isPositionMarker; + } + set { + isPositionMarker = value; + } + } + + string GetWindowTitle () + { + if (Label.IndexOf ('<') == -1) + return Label; + try { + XmlDocument doc = new XmlDocument (); + doc.LoadXml ("<a>" + Label + "</a>"); + return doc.InnerText; + } catch { + return label; + } + } + + internal void ShowDockPopupMenu (uint time) + { + Menu menu = new Menu (); + + // Hide menuitem + if ((Behavior & DockItemBehavior.CantClose) == 0) { + MenuItem mitem = new MenuItem (Catalog.GetString("Hide")); + mitem.Activated += delegate { Visible = false; }; + menu.Append (mitem); + } + + CheckMenuItem citem; + + // Dockable menuitem + citem = new CheckMenuItem (Catalog.GetString("Dockable")); + citem.Active = Status == DockItemStatus.Dockable; + citem.DrawAsRadio = true; + citem.Toggled += delegate { Status = DockItemStatus.Dockable; }; + menu.Append (citem); + + // Floating menuitem + if ((Behavior & DockItemBehavior.NeverFloating) == 0) { + citem = new CheckMenuItem (Catalog.GetString("Floating")); + citem.Active = Status == DockItemStatus.Floating; + citem.DrawAsRadio = true; + citem.Toggled += delegate { Status = DockItemStatus.Floating; }; + menu.Append (citem); + } + + // Auto Hide menuitem + if ((Behavior & DockItemBehavior.CantAutoHide) == 0) { + citem = new CheckMenuItem (Catalog.GetString("Auto Hide")); + citem.Active = Status == DockItemStatus.AutoHide; + citem.DrawAsRadio = true; + citem.Toggled += delegate { Status = DockItemStatus.AutoHide; }; + menu.Append (citem); + } + + menu.ShowAll (); + menu.Popup (null, null, null, 3, time); + } + } + + public interface IDockItemLabelProvider + { + Gtk.Widget CreateLabel (Orientation orientation); + } +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemBehavior.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemBehavior.cs new file mode 100644 index 0000000000..1c62d92188 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemBehavior.cs @@ -0,0 +1,48 @@ +// +// DockItemBehavior.cs +// +// Author: +// Lluis Sanchez Gual +// + +// +// Copyright (C) 2007 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; + +namespace MonoDevelop.Components.Docking +{ + [Flags] + public enum DockItemBehavior + { + Normal, + NeverFloating = 1 << 0, + NeverVertical = 1 << 1, + NeverHorizontal = 1 << 2, + CantClose = 1 << 3, + CantAutoHide = 1 << 4, + NoGrip = 1 << 5, + Sticky = 1 << 6, // Visibility is the same for al layouts + Locked = NoGrip, + } +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemContainer.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemContainer.cs new file mode 100644 index 0000000000..43af768044 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemContainer.cs @@ -0,0 +1,425 @@ +// +// DockItemContainer.cs +// +// Author: +// Lluis Sanchez Gual +// + +// +// Copyright (C) 2007 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 Mono.Unix; + +namespace MonoDevelop.Components.Docking +{ + class DockItemContainer: VBox + { + static Gdk.Pixbuf pixClose; + static Gdk.Pixbuf pixAutoHide; + static Gdk.Pixbuf pixDock; + + Gtk.Label title; + Gtk.Button btnClose; + Gtk.Button btnDock; + string txt; + Gtk.EventBox header; + Gtk.Alignment headerAlign; + DockFrame frame; + DockItem item; + Widget widget; + Container borderFrame; + bool allowPlaceholderDocking; + bool pointerHover; + Box contentBox; + + static Gdk.Cursor fleurCursor = new Gdk.Cursor (Gdk.CursorType.Fleur); + static Gdk.Cursor handCursor = new Gdk.Cursor (Gdk.CursorType.Hand2); + + static DockItemContainer () + { + try { + pixClose = Gdk.Pixbuf.LoadFromResource ("stock-close-12.png"); + pixAutoHide = Gdk.Pixbuf.LoadFromResource ("stock-auto-hide.png"); + pixDock = Gdk.Pixbuf.LoadFromResource ("stock-dock.png"); + } catch (Exception) { + } + } + + public DockItemContainer (DockFrame frame, DockItem item) + { + this.frame = frame; + this.item = item; + + ResizeMode = Gtk.ResizeMode.Queue; + Spacing = 0; + + title = new Gtk.Label (); + title.Xalign = 0; + title.Xpad = 3; + title.UseMarkup = true; + + btnDock = new Button (new Gtk.Image (pixAutoHide)); + btnDock.Relief = ReliefStyle.None; + btnDock.CanFocus = false; + btnDock.WidthRequest = btnDock.HeightRequest = 17; + btnDock.Clicked += OnClickDock; + + btnClose = new Button (new Gtk.Image (pixClose)); + btnClose.TooltipText = Catalog.GetString ("Hide"); + btnClose.Relief = ReliefStyle.None; + btnClose.CanFocus = false; + btnClose.WidthRequest = btnClose.HeightRequest = 17; + btnClose.Clicked += delegate { + item.Visible = false; + }; + + HBox box = new HBox (false, 0); + box.PackStart (title, true, true, 0); + box.PackEnd (btnClose, false, false, 0); + box.PackEnd (btnDock, false, false, 0); + + headerAlign = new Alignment (0.0f, 0.0f, 1.0f, 1.0f); + headerAlign.TopPadding = headerAlign.BottomPadding = headerAlign.RightPadding = headerAlign.LeftPadding = 1; + headerAlign.Add (box); + + header = new EventBox (); + header.Events |= Gdk.EventMask.KeyPressMask | Gdk.EventMask.KeyReleaseMask; + header.ButtonPressEvent += HeaderButtonPress; + header.ButtonReleaseEvent += HeaderButtonRelease; + header.MotionNotifyEvent += HeaderMotion; + header.KeyPressEvent += HeaderKeyPress; + header.KeyReleaseEvent += HeaderKeyRelease; + header.Add (headerAlign); + header.ExposeEvent += HeaderExpose;
+ header.Realized += delegate { + header.GdkWindow.Cursor = handCursor; + }; + + foreach (Widget w in new Widget [] { header, btnDock, btnClose }) { + w.EnterNotifyEvent += HeaderEnterNotify; + w.LeaveNotifyEvent += HeaderLeaveNotify; + } + + PackStart (header, false, false, 0); + ShowAll (); + + PackStart (item.GetToolbar (PositionType.Top).Container, false, false, 0); + + HBox hbox = new HBox (); + hbox.Show (); + hbox.PackStart (item.GetToolbar (PositionType.Left).Container, false, false, 0); + + contentBox = new HBox (); + contentBox.Show (); + hbox.PackStart (contentBox, true, true, 0); + + hbox.PackStart (item.GetToolbar (PositionType.Right).Container, false, false, 0); + + PackStart (hbox, true, true, 0); + + PackStart (item.GetToolbar (PositionType.Bottom).Container, false, false, 0); + + UpdateBehavior (); + } + + void OnClickDock (object s, EventArgs a) + { + if (item.Status == DockItemStatus.AutoHide || item.Status == DockItemStatus.Floating) + item.Status = DockItemStatus.Dockable; + else + item.Status = DockItemStatus.AutoHide; + } + + public void UpdateContent () + { + if (widget != null) + ((Gtk.Container)widget.Parent).Remove (widget); + widget = item.Content; + + if (item.DrawFrame) { + if (borderFrame == null) { + borderFrame = new CustomFrame (1, 1, 1, 1); + borderFrame.Show (); + contentBox.Add (borderFrame); + } + if (widget != null) { + borderFrame.Add (widget); + widget.Show (); + } + } + else if (widget != null) { + if (borderFrame != null) { + contentBox.Remove (borderFrame); + borderFrame = null; + } + contentBox.Add (widget); + widget.Show (); + } + } + + public void UpdateBehavior () + { + btnClose.Visible = (item.Behavior & DockItemBehavior.CantClose) == 0; + header.Visible = (item.Behavior & DockItemBehavior.Locked) == 0; + btnDock.Visible = (item.Behavior & DockItemBehavior.CantAutoHide) == 0; + + if (item.Status == DockItemStatus.AutoHide || item.Status == DockItemStatus.Floating) { + btnDock.Image = new Gtk.Image (pixDock); + btnDock.TooltipText = Catalog.GetString ("Dock"); + } + else { + btnDock.Image = new Gtk.Image (pixAutoHide); + btnDock.TooltipText = Catalog.GetString ("Auto Hide"); + } + } + + void HeaderButtonPress (object ob, Gtk.ButtonPressEventArgs args) + { + if (args.Event.Button == 1) { + frame.ShowPlaceholder (); + header.GdkWindow.Cursor = fleurCursor; + frame.Toplevel.KeyPressEvent += HeaderKeyPress; + frame.Toplevel.KeyReleaseEvent += HeaderKeyRelease; + allowPlaceholderDocking = true; + } + else if (args.Event.Button == 3) { + item.ShowDockPopupMenu (args.Event.Time); + } + } + + void HeaderButtonRelease (object ob, Gtk.ButtonReleaseEventArgs args) + { + if (args.Event.Button == 1) { + frame.DockInPlaceholder (item); + frame.HidePlaceholder (); + if (header.GdkWindow != null) + header.GdkWindow.Cursor = handCursor; + frame.Toplevel.KeyPressEvent -= HeaderKeyPress; + frame.Toplevel.KeyReleaseEvent -= HeaderKeyRelease; + } + } + + void HeaderMotion (object ob, Gtk.MotionNotifyEventArgs args) + { + frame.UpdatePlaceholder (item, Allocation.Size, allowPlaceholderDocking); + } + + [GLib.ConnectBeforeAttribute] + void HeaderKeyPress (object ob, Gtk.KeyPressEventArgs a) + { + if (a.Event.Key == Gdk.Key.Control_L || a.Event.Key == Gdk.Key.Control_R) { + allowPlaceholderDocking = false; + frame.UpdatePlaceholder (item, Allocation.Size, false); + } + if (a.Event.Key == Gdk.Key.Escape) { + frame.HidePlaceholder (); + frame.Toplevel.KeyPressEvent -= HeaderKeyPress; + frame.Toplevel.KeyReleaseEvent -= HeaderKeyRelease; + Gdk.Pointer.Ungrab (0); + } + } + + [GLib.ConnectBeforeAttribute] + void HeaderKeyRelease (object ob, Gtk.KeyReleaseEventArgs a) + { + if (a.Event.Key == Gdk.Key.Control_L || a.Event.Key == Gdk.Key.Control_R) { + allowPlaceholderDocking = true; + frame.UpdatePlaceholder (item, Allocation.Size, true); + } + } + + private void HeaderExpose (object ob, Gtk.ExposeEventArgs a) + { + Gdk.Rectangle rect = new Gdk.Rectangle (0, 0, header.Allocation.Width - 1, header.Allocation.Height); + HslColor gcol = frame.Style.Background (Gtk.StateType.Normal); + + if (pointerHover) + gcol.L *= 1.1; + else + gcol.L *= 1; + + if (gcol.L > 1) + gcol.L = 1; + + using (Cairo.Context cr = Gdk.CairoHelper.Create (a.Event.Window)) { + cr.NewPath (); + cr.MoveTo (0, 0); + cr.RelLineTo (rect.Width, 0); + cr.RelLineTo (0, rect.Height); + cr.RelLineTo (-rect.Width, 0); + cr.RelLineTo (0, -rect.Height); + cr.ClosePath (); + Cairo.Gradient pat = new Cairo.LinearGradient (0, 0, rect.Width, rect.Height); + Cairo.Color color1 = gcol; + pat.AddColorStop (0, color1); + color1.A = 0.3; + pat.AddColorStop (1, color1); + cr.Pattern = pat; + cr.FillPreserve (); + } + +// header.GdkWindow.DrawRectangle (gc, true, rect); + header.GdkWindow.DrawRectangle (frame.Style.DarkGC (Gtk.StateType.Normal), false, rect); + + foreach (Widget child in header.Children) + header.PropagateExpose (child, a.Event); + } + + private void HeaderLeaveNotify (object ob, EventArgs a) + { + pointerHover = false; + header.QueueDraw (); + } + + private void HeaderEnterNotify (object ob, EventArgs a) + { + pointerHover = true; + header.QueueDraw (); + } + + public string Label { + get { return txt; } + set { + title.Markup = "<small>" + value + "</small>"; + txt = value; + } + } + }
+
+ class CustomFrame: Bin
+ {
+ Gtk.Widget child; + int topMargin; + int bottomMargin; + int leftMargin; + int rightMargin; + + int topPadding; + int bottomPadding; + int leftPadding; + int rightPadding; + + public CustomFrame () + { + } + + public CustomFrame (int topMargin, int bottomMargin, int leftMargin, int rightMargin) + { + SetMargins (topMargin, bottomMargin, leftMargin, rightMargin); + } + + public void SetMargins (int topMargin, int bottomMargin, int leftMargin, int rightMargin) + { + this.topMargin = topMargin; + this.bottomMargin = bottomMargin; + this.leftMargin = leftMargin; + this.rightMargin = rightMargin; + } + + public void SetPadding (int topPadding, int bottomPadding, int leftPadding, int rightPadding) + { + this.topPadding = topPadding; + this.bottomPadding = bottomPadding; + this.leftPadding = leftPadding; + this.rightPadding = rightPadding; + } + + public bool GradientBackround { get; set; } +
+ protected override void OnAdded (Widget widget)
+ {
+ base.OnAdded (widget);
+ child = widget;
+ }
+
+ protected override void OnSizeRequested (ref Requisition requisition)
+ {
+ requisition = child.SizeRequest ();
+ requisition.Width += leftMargin + rightMargin + leftPadding + rightPadding;
+ requisition.Height += topMargin + bottomMargin + topPadding + bottomPadding;
+ }
+
+ protected override void OnSizeAllocated (Gdk.Rectangle allocation)
+ {
+ base.OnSizeAllocated (allocation); + if (allocation.Width > leftMargin + rightMargin + leftPadding + rightPadding) { + allocation.X += leftMargin + leftPadding; + allocation.Width -= leftMargin + rightMargin + leftPadding + rightPadding; + } + if (allocation.Height > topMargin + bottomMargin + topPadding + bottomPadding) { + allocation.Y += topMargin + topPadding; + allocation.Height -= topMargin + bottomMargin + topPadding + bottomPadding; + } + child.SizeAllocate (allocation);
+ }
+
+ protected override bool OnExposeEvent (Gdk.EventExpose evnt)
+ {
+ Gdk.Rectangle rect; + + if (GradientBackround) { + rect = new Gdk.Rectangle (Allocation.X, Allocation.Y, Allocation.Width, Allocation.Height); + HslColor gcol = Style.Background (Gtk.StateType.Normal); + + using (Cairo.Context cr = Gdk.CairoHelper.Create (GdkWindow)) { + cr.NewPath (); + cr.MoveTo (rect.X, rect.Y); + cr.RelLineTo (rect.Width, 0); + cr.RelLineTo (0, rect.Height); + cr.RelLineTo (-rect.Width, 0); + cr.RelLineTo (0, -rect.Height); + cr.ClosePath (); + Cairo.Gradient pat = new Cairo.LinearGradient (rect.X, rect.Y, rect.X, rect.Bottom); + Cairo.Color color1 = gcol; + pat.AddColorStop (0, color1); + gcol.L -= 0.1; + if (gcol.L < 0) gcol.L = 0; + pat.AddColorStop (1, gcol); + cr.Pattern = pat; + cr.FillPreserve (); + } + } + + bool res = base.OnExposeEvent (evnt); + + Gdk.GC borderColor = Style.DarkGC (Gtk.StateType.Normal); + + rect = Allocation; + for (int n=0; n<topMargin; n++) + GdkWindow.DrawLine (borderColor, rect.X, rect.Y + n, rect.Right - 1, rect.Y + n); +
+ for (int n=0; n<bottomMargin; n++) + GdkWindow.DrawLine (borderColor, rect.X, rect.Bottom - n - 1, rect.Right - 1, rect.Bottom - n - 1); + + for (int n=0; n<leftMargin; n++) + GdkWindow.DrawLine (borderColor, rect.X + n, rect.Y, rect.X + n, rect.Bottom - 1); + + for (int n=0; n<rightMargin; n++) + GdkWindow.DrawLine (borderColor, rect.Right - n - 1, rect.Y, rect.Right - n - 1, rect.Bottom - 1); + + return res;
+ }
+ } +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemStatus.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemStatus.cs new file mode 100644 index 0000000000..e6e56d2fa4 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemStatus.cs @@ -0,0 +1,42 @@ +// +// DockItemStatus.cs +// +// Author: +// Lluis Sanchez Gual +// + +// +// Copyright (C) 2007 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; + +namespace MonoDevelop.Components.Docking +{ + public enum DockItemStatus + { + Dockable, + Floating, + AutoHide + } +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemToolbar.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemToolbar.cs new file mode 100644 index 0000000000..d85c8c8f89 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemToolbar.cs @@ -0,0 +1,181 @@ +// +// DockItemToolbar.cs +// +// Author: +// Lluis Sanchez Gual <lluis@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; + +namespace MonoDevelop.Components.Docking +{ + public class DockItemToolbar + { + DockItem parentItem; + CustomFrame frame; + Box box; + PositionType position; + bool empty = true; + + internal DockItemToolbar (DockItem parentItem, PositionType position) + { + this.parentItem = parentItem; + frame = new CustomFrame (); + switch (position) { + case PositionType.Top: + frame.SetMargins (0, 0, 1, 1); + frame.SetPadding (0, 2, 2, 0); + break; + case PositionType.Bottom: + frame.SetMargins (0, 1, 1, 1); + frame.SetPadding (2, 2, 2, 0); + break; + case PositionType.Left: + frame.SetMargins (0, 1, 1, 0); + frame.SetPadding (0, 0, 2, 2); + break; + case PositionType.Right: + frame.SetMargins (0, 1, 0, 1); + frame.SetPadding (0, 0, 2, 2); + break; + } + this.position = position; + if (position == PositionType.Top || position == PositionType.Bottom) + box = new HBox (false, 3); + else + box = new VBox (false, 3); + box.Show (); + frame.Add (box); + frame.GradientBackround = true; + } + + public DockItem DockItem { + get { return parentItem; } + } + + internal Widget Container { + get { return frame; } + } + + public PositionType Position { + get { return this.position; } + } + + public void Add (Widget widget) + { + Add (widget, false); + } + + public void Add (Widget widget, bool fill) + { + Add (widget, fill, -1); + } + + public void Add (Widget widget, bool fill, int padding) + { + Add (widget, fill, padding, -1); + } + + void Add (Widget widget, bool fill, int padding, int index) + { + int defaultPadding = 3; + + if (widget is Button) { + ((Button)widget).Relief = ReliefStyle.None; + ((Button)widget).FocusOnClick = false; + defaultPadding = 0; + } + else if (widget is Entry) { + ((Entry)widget).HasFrame = false; + } + else if (widget is ComboBox) { + ((ComboBox)widget).HasFrame = false; + } + else if (widget is VSeparator) + ((VSeparator)widget).HeightRequest = 10; + + if (padding == -1) + padding = defaultPadding; + + box.PackStart (widget, fill, fill, (uint)padding); + if (empty) { + empty = false; + frame.Show (); + } + if (index != -1) { + Box.BoxChild bc = (Box.BoxChild) box [widget]; + bc.Position = index; + } + } + + public void Insert (Widget w, int index) + { + Add (w, false, 0, index); + } + + public void Remove (Widget widget) + { + box.Remove (widget); + } + + public bool Visible { + get { + return empty || frame.Visible; + } + set { + frame.Visible = value; + } + } + + public bool Sensitive { + get { return frame.Sensitive; } + set { frame.Sensitive = value; } + } + + public void ShowAll () + { + frame.ShowAll (); + } + + public Widget[] Children { + get { return box.Children; } + } + } + + public class DockToolButton: Gtk.Button + { + public DockToolButton (string stockId) + { + Image = new Gtk.Image (stockId, IconSize.Menu); + Image.Show (); + } + + public DockToolButton (string stockId, string label) + { + Label = label; + Image = new Gtk.Image (stockId, IconSize.Menu); + Image.Show (); + } + } +} + diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockLayout.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockLayout.cs new file mode 100644 index 0000000000..e215378f3f --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockLayout.cs @@ -0,0 +1,107 @@ +// +// MonoDevelop.Components.Docking.cs +// +// Author: +// Lluis Sanchez Gual +// + +// +// Copyright (C) 2007 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.Xml; +using System.Collections.Generic; + +namespace MonoDevelop.Components.Docking +{ + class DockLayout: DockGroup + { + string name; + int layoutWidth = 1024; + int layoutHeight = 768; + + public DockLayout (DockFrame frame): base (frame, DockGroupType.Horizontal) + { + } + + public string Name { + get { + return name; + } + set { + name = value; + } + } + + internal override void Write (XmlWriter writer) + { + writer.WriteStartElement ("layout"); + writer.WriteAttributeString ("name", name); + writer.WriteAttributeString ("width", layoutWidth.ToString ()); + writer.WriteAttributeString ("height", layoutHeight.ToString ()); + base.Write (writer); + writer.WriteEndElement (); + } + + internal override void Read (XmlReader reader) + { + name = reader.GetAttribute ("name"); + string s = reader.GetAttribute ("width"); + if (s != null) + layoutWidth = int.Parse (s); + s = reader.GetAttribute ("height"); + if (s != null) + layoutHeight = int.Parse (s); + base.Read (reader); + } + + public static DockLayout Read (DockFrame frame, XmlReader reader) + { + DockLayout layout = new DockLayout (frame); + layout.Read (reader); + return layout; + } + + public override void SizeAllocate (Gdk.Rectangle rect) + { + Size = rect.Width; + base.SizeAllocate (rect); + } + + internal override void StoreAllocation () + { + base.StoreAllocation (); + layoutWidth = Allocation.Width; + layoutHeight = Allocation.Height; + } + + internal override void RestoreAllocation () + { + Allocation = new Gdk.Rectangle (0, 0, layoutWidth, layoutHeight); + base.RestoreAllocation (); + } + + } +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockObject.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockObject.cs new file mode 100644 index 0000000000..f909bf3542 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockObject.cs @@ -0,0 +1,284 @@ +// +// DockObject.cs +// +// Author: +// Lluis Sanchez Gual +// + +// +// Copyright (C) 2007 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.Xml; +using Gtk; +using System.Globalization; + +namespace MonoDevelop.Components.Docking +{ + internal abstract class DockObject + { + DockGroup parentGroup; + DockFrame frame; + Gdk.Rectangle rect; + + // The current size in pixels of this item + double size = -1; + + // The current size in pixels of this item, but as an integer. + // In general it is the same value as size, but it may change a bit due to rounding. + int allocSize = -1; + + double defaultHorSize = -1; + double defaultVerSize = -1; + double prefSize = 0; + + // Those are the last known coordinates of the item. They are stored in StoreAllocation + // and restored to rect in RestoreAllocation. This is needed for example when a layout + // is cloned. It is convenient to have allocation information in the cloned layout, even + // if the layout has never been displayed (e.g., to decide the autohide dock location) + int ax=-1, ay=-1; + + public DockObject (DockFrame frame) + { + this.frame = frame; + } + + internal DockGroup ParentGroup { + get { + return parentGroup; + } + set { + parentGroup = value; + if (size < 0) + size = prefSize = DefaultSize; + } + } + + public double Size { + get { + return size; + } + set { + size = value; + } + } + + public bool HasAllocatedSize { + get { return allocSize != -1; } + } + + public double DefaultSize { + get { + if (defaultHorSize < 0) + InitDefaultSizes (); + if (parentGroup != null) { + if (parentGroup.Type == DockGroupType.Horizontal) + return defaultHorSize; + else if (parentGroup.Type == DockGroupType.Vertical) + return defaultVerSize; + } + return 0; + } + set { + if (parentGroup != null) { + if (parentGroup.Type == DockGroupType.Horizontal) + defaultHorSize = value; + else if (parentGroup.Type == DockGroupType.Vertical) + defaultVerSize = value; + } + } + } + + internal void ResetDefaultSize () + { + defaultHorSize = -1; + defaultVerSize = -1; + } + + public int MinSize { + get { + int w,h; + GetMinSize (out w, out h); + if (parentGroup != null) { + if (parentGroup.Type == DockGroupType.Horizontal) + return w; + else if (parentGroup.Type == DockGroupType.Vertical) + return h; + } + return w; + } + } + + public abstract bool Expand { get; } + + public virtual void SizeAllocate (Gdk.Rectangle rect) + { + this.rect = rect; + } + + internal Gdk.Rectangle Allocation { + get { + return rect; + } + set { + rect = value; + } + } + + public int AllocSize { + get { + return allocSize; + } + set { + allocSize = value; + } + } + + public MonoDevelop.Components.Docking.DockFrame Frame { + get { + return frame; + } + } + + public double PrefSize { + get { + return prefSize; + } + set { + prefSize = value; + } + } + + void InitDefaultSizes () + { + int width, height; + GetDefaultSize (out width, out height); + if (width == -1) + width = frame.DefaultItemWidth; + if (height == -1) + height = frame.DefaultItemHeight; + defaultHorSize = (double) width; + defaultVerSize = (double) height; + } + + internal virtual void GetDefaultSize (out int width, out int height) + { + width = -1; + height = -1; + } + + internal virtual void GetMinSize (out int width, out int height) + { + width = 0; + height = 0; + } + + internal abstract void QueueResize (); + + internal abstract bool GetDockTarget (DockItem item, int px, int py, out DockDelegate dockDelegate, out Gdk.Rectangle rect); + + internal abstract Gtk.Requisition SizeRequest (); + + internal abstract bool Visible { get; } + + internal abstract void Dump (int ind); + + internal virtual void RestoreAllocation () + { + if (parentGroup != null) { + int x = ax != -1 ? ax : 0; + int y = ay != -1 ? ay : 0; + if (parentGroup.Type == DockGroupType.Horizontal) + rect = new Gdk.Rectangle (x, y, (int)size, parentGroup.Allocation.Height); + else if (parentGroup.Type == DockGroupType.Vertical) + rect = new Gdk.Rectangle (x, y, parentGroup.Allocation.Width, (int)size); + } + } + + internal virtual void StoreAllocation () + { + if (Visible) { + if (parentGroup == null || parentGroup.Type == DockGroupType.Horizontal) + size = prefSize = (int) rect.Width; + else if (parentGroup.Type == DockGroupType.Vertical) + size = prefSize = (int) rect.Height; + ax = Allocation.X; + ay = Allocation.Y; + } + } + + internal virtual void Write (XmlWriter writer) + { + writer.WriteAttributeString ("size", size.ToString (CultureInfo.InvariantCulture)); + writer.WriteAttributeString ("prefSize", prefSize.ToString (CultureInfo.InvariantCulture)); + writer.WriteAttributeString ("defaultHorSize", defaultHorSize.ToString (CultureInfo.InvariantCulture)); + writer.WriteAttributeString ("defaultVerSize", defaultVerSize.ToString (CultureInfo.InvariantCulture)); + } + + internal virtual void Read (XmlReader reader) + { + size = double.Parse (reader.GetAttribute ("size"), CultureInfo.InvariantCulture); + prefSize = double.Parse (reader.GetAttribute ("prefSize"), CultureInfo.InvariantCulture); + defaultHorSize = double.Parse (reader.GetAttribute ("defaultHorSize"), CultureInfo.InvariantCulture); + defaultVerSize = double.Parse (reader.GetAttribute ("defaultVerSize"), CultureInfo.InvariantCulture); + } + + public virtual void CopyFrom (DockObject ob) + { + parentGroup = null; + frame = ob.frame; + rect = ob.rect; + size = ob.size; + allocSize = ob.allocSize; + defaultHorSize = ob.defaultHorSize; + defaultVerSize = ob.defaultVerSize; + prefSize = ob.prefSize; + } + + public DockObject Clone () + { + DockObject ob = (DockObject) this.MemberwiseClone (); + ob.CopyFrom (this); + return ob; + } + + public virtual void CopySizeFrom (DockObject obj) + { + size = obj.size; + allocSize = obj.allocSize; + defaultHorSize = obj.defaultHorSize; + defaultVerSize = obj.defaultVerSize; + prefSize = obj.prefSize; + } + + public virtual bool IsNextToMargin (Gtk.PositionType margin) + { + if (ParentGroup == null) + return true; + if (!ParentGroup.IsNextToMargin (margin)) + return false; + return ParentGroup.IsChildNextToMargin (margin, this); + } + } +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockPosition.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockPosition.cs new file mode 100644 index 0000000000..39404ad97e --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockPosition.cs @@ -0,0 +1,45 @@ +// +// DockPosition.cs +// +// Author: +// Lluis Sanchez Gual +// + +// +// Copyright (C) 2007 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; + +namespace MonoDevelop.Components.Docking +{ + enum DockPosition + { + Left, + Right, + Top, + Bottom, + Center, + CenterBefore + } +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/PlaceholderWindow.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/PlaceholderWindow.cs new file mode 100644 index 0000000000..a2f1f4977a --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/PlaceholderWindow.cs @@ -0,0 +1,143 @@ +// +// PlaceholderWindow.cs +// +// Author: +// Lluis Sanchez Gual +// + +// +// Copyright (C) 2007 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 Gtk; + +namespace MonoDevelop.Components.Docking +{ + internal class PlaceholderWindow: Gtk.Window + { + Gdk.GC redgc; + uint anim; + int rx, ry, rw, rh; + bool allowDocking; + + public bool AllowDocking { + get { + return allowDocking; + } + set { + allowDocking = value; + } + } + + public PlaceholderWindow (DockFrame frame): base (Gtk.WindowType.Popup) + { + SkipTaskbarHint = true; + Decorated = false; + TransientFor = (Gtk.Window) frame.Toplevel; + TypeHint = WindowTypeHint.Utility; + + // Create the mask for the arrow + + Realize (); + redgc = new Gdk.GC (GdkWindow); + redgc.RgbFgColor = frame.Style.Background (StateType.Selected); + } + + void CreateShape (int width, int height) + { + Gdk.Color black, white; + black = new Gdk.Color (0, 0, 0); + black.Pixel = 1; + white = new Gdk.Color (255, 255, 255); + white.Pixel = 0; + + Gdk.Pixmap pm = new Pixmap (this.GdkWindow, width, height, 1); + Gdk.GC gc = new Gdk.GC (pm); + gc.Background = white; + gc.Foreground = white; + pm.DrawRectangle (gc, true, 0, 0, width, height); + + gc.Foreground = black; + pm.DrawRectangle (gc, false, 0, 0, width - 1, height - 1); + pm.DrawRectangle (gc, false, 1, 1, width - 3, height - 3); + + this.ShapeCombineMask (pm, 0, 0); + } + + protected override void OnSizeAllocated (Rectangle allocation) + { + base.OnSizeAllocated (allocation); + CreateShape (allocation.Width, allocation.Height); + } + + + protected override bool OnExposeEvent (Gdk.EventExpose args) + { + //base.OnExposeEvent (args); + int w, h; + this.GetSize (out w, out h); + this.GdkWindow.DrawRectangle (redgc, false, 0, 0, w-1, h-1); + this.GdkWindow.DrawRectangle (redgc, false, 1, 1, w-3, h-3); + return true; + } + + public void Relocate (int x, int y, int w, int h, bool animate) + { + if (x != rx || y != ry || w != rw || h != rh) { + Move (x, y); + Resize (w, h); + + rx = x; ry = y; rw = w; rh = h; + + if (anim != 0) { + GLib.Source.Remove (anim); + anim = 0; + } + if (animate && w < 150 && h < 150) { + int sa = 7; + Move (rx-sa, ry-sa); + Resize (rw+sa*2, rh+sa*2); + anim = GLib.Timeout.Add (10, RunAnimation); + } + } + } + + bool RunAnimation () + { + int cx, cy, ch, cw; + GetSize (out cw, out ch); + GetPosition (out cx, out cy); + + if (cx != rx) { + cx++; cy++; + ch-=2; cw-=2; + Move (cx, cy); + Resize (cw, ch); + return true; + } + anim = 0; + return false; + } + } +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/ShadedContainer.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/ShadedContainer.cs new file mode 100644 index 0000000000..f8d20a906e --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/ShadedContainer.cs @@ -0,0 +1,314 @@ +// +// ShadedContainer.cs +// +// Author: +// Lluis Sanchez Gual <lluis@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 System.Collections.Generic; +using Gtk; +using MonoDevelop.Components; + +namespace MonoDevelop.Components.Docking +{ + public class ShadedContainer + { + struct Section { + public int Offset; + public int Size; + } + + Gdk.Color lightColor; + Gdk.Color darkColor; + int shadowSize = 2; + + List<Widget> widgets = new List<Widget> (); + Dictionary<Widget, Gdk.Rectangle[]> allocations = new Dictionary<Widget, Gdk.Rectangle[]> (); + + public ShadedContainer () + { + } + + public int ShadowSize { + get { return this.shadowSize; } + set { this.shadowSize = value; RedrawAll (); } + } + + public Gdk.Color LightColor { + get { return this.lightColor; } + set { this.lightColor = value; RedrawAll (); } + } + + public Gdk.Color DarkColor { + get { return this.darkColor; } + set { this.darkColor = value; RedrawAll (); } + } + + public void Add (Gtk.Widget w) + { + widgets.Add (w); + UpdateAllocation (w); + w.Destroyed += HandleWDestroyed; + w.Shown += HandleWShown; + w.Hidden += HandleWHidden; + w.SizeAllocated += HandleWSizeAllocated; + IShadedWidget sw = w as IShadedWidget; + if (sw != null) + sw.AreasChanged += HandleSwAreasChanged; + RedrawAll (); + } + + public void Remove (Widget w) + { + widgets.Remove (w); + allocations.Remove (w); + w.Destroyed -= HandleWDestroyed; + w.Shown -= HandleWShown; + w.Hidden -= HandleWHidden; + IShadedWidget sw = w as IShadedWidget; + if (sw != null) + sw.AreasChanged -= HandleSwAreasChanged; + RedrawAll (); + } + + void UpdateAllocation (Widget w) + { + if (w.IsRealized) { + int x, y; + w.GdkWindow.GetOrigin (out x, out y); + IShadedWidget sw = w as IShadedWidget; + if (sw != null) { + List<Gdk.Rectangle> rects = new List<Gdk.Rectangle> (); + foreach (Gdk.Rectangle ar in sw.GetShadedAreas ()) { + Gdk.Rectangle r = new Gdk.Rectangle (x + ar.X, y + ar.Y, ar.Width, ar.Height); + rects.Add (r); + } + allocations [w] = rects.ToArray (); + } else { + Gdk.Rectangle r = new Gdk.Rectangle (x + w.Allocation.X, y + w.Allocation.Y, w.Allocation.Width, w.Allocation.Height); + allocations [w] = new Gdk.Rectangle [] { r }; + } + } + else { + allocations.Remove (w); + } + } + + void HandleSwAreasChanged (object sender, EventArgs e) + { + UpdateAllocation ((Gtk.Widget)sender); + RedrawAll (); + } + + void HandleWSizeAllocated (object o, SizeAllocatedArgs args) + { + UpdateAllocation ((Widget) o); + RedrawAll (); + } + + void HandleWHidden (object sender, EventArgs e) + { + RedrawAll (); + } + + void HandleWShown (object sender, EventArgs e) + { + RedrawAll (); + } + + void HandleWDestroyed (object sender, EventArgs e) + { + Remove ((Widget)sender); + } + + void RedrawAll () + { + foreach (Widget w in widgets) + w.QueueDraw (); + } + + public void DrawBackground (Gtk.Widget w) + { + DrawBackground (w, w.Allocation); + } + + public void DrawBackground (Gtk.Widget w, Gdk.Rectangle allocation) + { + if (shadowSize == 0) { + Gdk.Rectangle wr = new Gdk.Rectangle (allocation.X, allocation.Y, allocation.Width, allocation.Height); + using (Cairo.Context ctx = Gdk.CairoHelper.Create (w.GdkWindow)) { + ctx.Rectangle (wr.X, wr.Y, wr.Width, wr.Height); + ctx.Color = GtkUtil.ToCairoColor (lightColor); + ctx.Fill (); + } + return; + } + + List<Section> secsT = new List<Section> (); + List<Section> secsB = new List<Section> (); + List<Section> secsR = new List<Section> (); + List<Section> secsL = new List<Section> (); + + int x, y; + w.GdkWindow.GetOrigin (out x, out y); + Gdk.Rectangle rect = new Gdk.Rectangle (x + allocation.X, y + allocation.Y, allocation.Width, allocation.Height); + + Section s = new Section (); + s.Size = rect.Width; + secsT.Add (s); + secsB.Add (s); + s.Size = rect.Height; + secsL.Add (s); + secsR.Add (s); + + foreach (var rects in allocations.Values) { + foreach (Gdk.Rectangle sr in rects) { + if (sr == rect) + continue; + + if (sr.Right == rect.X) + RemoveSection (secsL, sr.Y - rect.Y, sr.Height); + if (sr.Bottom == rect.Y) + RemoveSection (secsT, sr.X - rect.X, sr.Width); + if (sr.X == rect.Right) + RemoveSection (secsR, sr.Y - rect.Y, sr.Height); + if (sr.Y == rect.Bottom) + RemoveSection (secsB, sr.X - rect.X, sr.Width); + } + } + + Gdk.Rectangle r = new Gdk.Rectangle (allocation.X, allocation.Y, allocation.Width, allocation.Height); + using (Cairo.Context ctx = Gdk.CairoHelper.Create (w.GdkWindow)) { + ctx.Rectangle (r.X, r.Y, r.Width, r.Height); + ctx.Color = GtkUtil.ToCairoColor (lightColor); + ctx.Fill (); + + DrawShadow (ctx, r, PositionType.Left, secsL); + DrawShadow (ctx, r, PositionType.Top, secsT); + DrawShadow (ctx, r, PositionType.Right, secsR); + DrawShadow (ctx, r, PositionType.Bottom, secsB); + } + } + + void DrawShadow (Cairo.Context ctx, Gdk.Rectangle ar, PositionType pos, List<Section> secs) + { + foreach (Section s in secs) { + Cairo.Gradient pat = null; + Gdk.Rectangle r = ar; + switch (pos) { + case PositionType.Top: + r.Height = shadowSize > r.Height ? r.Height / 2 : shadowSize; + r.X += s.Offset; + r.Width = s.Size; + pat = new Cairo.LinearGradient (r.X, r.Y, r.X, r.Bottom); + break; + case PositionType.Bottom: + r.Y = r.Bottom - shadowSize; + r.Height = shadowSize > r.Height ? r.Height / 2 : shadowSize; + r.X = r.X + s.Offset; + r.Width = s.Size; + pat = new Cairo.LinearGradient (r.X, r.Bottom, r.X, r.Y); + break; + case PositionType.Left: + r.Width = shadowSize > r.Width ? r.Width / 2 : shadowSize; + r.Y += s.Offset; + r.Height = s.Size; + pat = new Cairo.LinearGradient (r.X, r.Y, r.Right, r.Y); + break; + case PositionType.Right: + r.X = r.Right - shadowSize; + r.Width = shadowSize > r.Width ? r.Width / 2 : shadowSize; + r.Y += s.Offset; + r.Height = s.Size; + pat = new Cairo.LinearGradient (r.Right, r.Y, r.X, r.Y); + break; + } + Cairo.Color c = GtkUtil.ToCairoColor (darkColor); + pat.AddColorStop (0, c); + c.A = 0; + pat.AddColorStop (1, c); + ctx.NewPath (); + ctx.Rectangle (r.X, r.Y, r.Width, r.Height); + ctx.Pattern = pat; + ctx.Fill (); + } + } + + void RemoveSection (List<Section> secs, int offset, int size) + { + if (offset < 0) { + size += offset; + offset = 0; + } + if (size <= 0 || secs.Count == 0) + return; + Section last = secs [secs.Count - 1]; + int rem = (last.Offset + last.Size) - (offset + size); + if (rem < 0) { + size += rem; + if (size <= 0) + return; + } + for (int n=0; n<secs.Count; n++) { + Section s = secs [n]; + if (s.Offset >= offset + size) + continue; + if (offset >= s.Offset + s.Size) + continue; + if (offset <= s.Offset && offset + size >= s.Offset + s.Size) { + // Remove the whole section + secs.RemoveAt (n); + n--; + continue; + } + if (offset <= s.Offset) { + int newOfs = offset + size; + s.Size = s.Size - (newOfs - s.Offset); + s.Offset = newOfs; + secs [n] = s; + // Nothing else to remove + return; + } + if (offset + size >= s.Offset + s.Size) { + s.Size = offset - s.Offset; + secs [n] = s; + continue; + } + // Split section + Section s2 = new Section (); + s2.Offset = offset + size; + s2.Size = (s.Offset + s.Size) - (offset + size); + secs.Insert (n + 1, s2); + s.Size = offset - s.Offset; + secs [n] = s; + } + } + } + + public interface IShadedWidget + { + IEnumerable<Gdk.Rectangle> GetShadedAreas (); + event EventHandler AreasChanged; + } +} + diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/TabStrip.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/TabStrip.cs new file mode 100644 index 0000000000..67976a6ee5 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/TabStrip.cs @@ -0,0 +1,407 @@ +// +// TabStrip.cs +// +// Author: +// Lluis Sanchez Gual +// + +// +// Copyright (C) 2007 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 Gtk; + +using System; + +namespace MonoDevelop.Components.Docking +{ + class TabStrip: Notebook + { + int currentTab = -1; + bool ellipsized = true; + HBox box = new HBox (); + DockFrame frame; + Label bottomFiller = new Label (); + + public TabStrip (DockFrame frame) + { + this.frame = frame; + frame.ShadedContainer.Add (this); + VBox vbox = new VBox (); + box = new HBox (); + vbox.PackStart (box, false, false, 0); + vbox.PackStart (bottomFiller, false, false, 0); + AppendPage (vbox, null); + ShowBorder = false; + ShowTabs = false; + ShowAll (); + bottomFiller.Hide (); + BottomPadding = 3; + } + + public int BottomPadding { + get { return bottomFiller.HeightRequest; } + set { + bottomFiller.HeightRequest = value; + bottomFiller.Visible = value > 0; + } + } + + public void AddTab (Gtk.Widget page, Gdk.Pixbuf icon, string label) + { + Tab tab = new Tab (); + tab.SetLabel (page, icon, label); + tab.ShowAll (); + box.PackStart (tab, true, true, 0); + if (currentTab == -1) + CurrentTab = box.Children.Length - 1; + else { + tab.Active = false; + page.Hide (); + } + + tab.ButtonPressEvent += OnTabPress; + } + + public void SetTabLabel (Gtk.Widget page, Gdk.Pixbuf icon, string label) + { + foreach (Tab tab in box.Children) { + if (tab.Page == page) { + tab.SetLabel (page, icon, label); + UpdateEllipsize (Allocation); + break; + } + } + } + + public int TabCount { + get { return box.Children.Length; } + } + + public int CurrentTab { + get { return currentTab; } + set { + if (currentTab == value) + return; + if (currentTab != -1) { + Tab t = (Tab) box.Children [currentTab]; + t.Page.Hide (); + t.Active = false; + } + currentTab = value; + if (currentTab != -1) { + Tab t = (Tab) box.Children [currentTab]; + t.Active = true; + t.Page.Show (); + } + } + } + + new public Gtk.Widget CurrentPage { + get { + if (currentTab != -1) { + Tab t = (Tab) box.Children [currentTab]; + return t.Page; + } else + return null; + } + set { + if (value != null) { + Gtk.Widget[] tabs = box.Children; + for (int n = 0; n < tabs.Length; n++) { + Tab tab = (Tab) tabs [n]; + if (tab.Page == value) { + CurrentTab = n; + return; + } + } + } + CurrentTab = -1; + } + } + + public void Clear () + { + ellipsized = true; + currentTab = -1; + foreach (Widget w in box.Children) { + box.Remove (w); + w.Destroy (); + } + } + + void OnTabPress (object s, Gtk.ButtonPressEventArgs args) + { + CurrentTab = Array.IndexOf (box.Children, s); + Tab t = (Tab) s; + DockItem.SetFocus (t.Page); + QueueDraw (); + } + + protected override void OnSizeAllocated (Gdk.Rectangle allocation) + { + UpdateEllipsize (allocation); + base.OnSizeAllocated (allocation); + } + + void UpdateEllipsize (Gdk.Rectangle allocation) + { + int tsize = 0; + foreach (Tab tab in box.Children) + tsize += tab.LabelWidth; + + bool ellipsize = tsize > allocation.Width; + if (ellipsize != ellipsized) { + foreach (Tab tab in box.Children) { + tab.SetEllipsize (ellipsize); + Gtk.Box.BoxChild bc = (Gtk.Box.BoxChild) box [tab]; + bc.Expand = bc.Fill = ellipsize; + } + ellipsized = ellipsize; + } + }
+
+ public Gdk.Rectangle GetTabArea (int ntab)
+ {
+ Gtk.Widget[] tabs = box.Children;
+ Tab tab = (Tab) tabs[ntab];
+ Gdk.Rectangle rect = GetTabArea (tab, ntab);
+ int x, y;
+ tab.GdkWindow.GetRootOrigin (out x, out y);
+ rect.X += x;
+ rect.Y += y;
+ return rect;
+ } + + protected override bool OnExposeEvent (Gdk.EventExpose evnt) + { + frame.ShadedContainer.DrawBackground (this); + + Gtk.Widget[] tabs = box.Children; + for (int n=tabs.Length - 1; n>=0; n--) { + Tab tab = (Tab) tabs [n]; + if (n != currentTab) + DrawTab (evnt, tab, n); + } + if (currentTab != -1) { + Tab ctab = (Tab) tabs [currentTab]; +// GdkWindow.DrawLine (Style.DarkGC (Gtk.StateType.Normal), Allocation.X, Allocation.Y, Allocation.Right, Allocation.Y); + DrawTab (evnt, ctab, currentTab); + } + return base.OnExposeEvent (evnt); + }
+
+ public Gdk.Rectangle GetTabArea (Tab tab, int pos)
+ {
+ Gdk.Rectangle rect = tab.Allocation;
+
+ int xdif = 0;
+ if (pos > 0)
+ xdif = 2;
+
+ int reqh;
+// StateType st;
+
+ if (tab.Active) {
+// st = StateType.Normal;
+ reqh = tab.Allocation.Height;
+ }
+ else {
+ reqh = tab.Allocation.Height - 3;
+// st = StateType.Active;
+ }
+
+ if (DockFrame.IsWindows) {
+ rect.Height = reqh - 1;
+ rect.Width--;
+ if (pos > 0) {
+ rect.X--;
+ rect.Width++;
+ }
+ return rect;
+ }
+ else {
+ rect.X -= xdif;
+ rect.Width += xdif;
+ rect.Height = reqh;
+ return rect;
+ }
+ }
+
+ void DrawTab (Gdk.EventExpose evnt, Tab tab, int pos) + {
+ Gdk.Rectangle rect = GetTabArea (tab, pos); + StateType st; + if (tab.Active) + st = StateType.Normal; + else + st = StateType.Active; +
+ if (DockFrame.IsWindows) {
+ GdkWindow.DrawRectangle (Style.DarkGC (Gtk.StateType.Normal), false, rect);
+ rect.X++;
+ rect.Width--;
+ if (tab.Active) {
+ GdkWindow.DrawRectangle (Style.LightGC (Gtk.StateType.Normal), true, rect);
+ }
+ else {
+ using (Cairo.Context cr = Gdk.CairoHelper.Create (evnt.Window)) {
+ cr.NewPath ();
+ cr.MoveTo (rect.X, rect.Y);
+ cr.RelLineTo (rect.Width, 0);
+ cr.RelLineTo (0, rect.Height);
+ cr.RelLineTo (-rect.Width, 0);
+ cr.RelLineTo (0, -rect.Height);
+ cr.ClosePath ();
+ Cairo.Gradient pat = new Cairo.LinearGradient (rect.X, rect.Y, rect.X, rect.Y + rect.Height);
+ Cairo.Color color1 = DockFrame.ToCairoColor (Style.Mid (Gtk.StateType.Normal));
+ pat.AddColorStop (0, color1);
+ color1.R *= 1.2;
+ color1.G *= 1.2;
+ color1.B *= 1.2;
+ pat.AddColorStop (1, color1);
+ cr.Pattern = pat;
+ cr.FillPreserve ();
+ }
+ }
+ }
+ else
+ Gtk.Style.PaintExtension (Style, GdkWindow, st, ShadowType.Out, evnt.Area, this, "tab", rect.X, rect.Y, rect.Width, rect.Height, Gtk.PositionType.Top); + } + } + + class Tab: Gtk.EventBox + { + bool active; + Gtk.Widget page; + Gtk.Label labelWidget; + int labelWidth; + + const int TopPadding = 2; + const int BottomPadding = 4; + const int TopPaddingActive = 3; + const int BottomPaddingActive = 5; + const int HorzPadding = 5; + + public Tab () + { + this.VisibleWindow = false; + } + + public void SetLabel (Gtk.Widget page, Gdk.Pixbuf icon, string label) + { + Pango.EllipsizeMode oldMode = Pango.EllipsizeMode.End; + + this.page = page; + if (Child != null) { + if (labelWidget != null) + oldMode = labelWidget.Ellipsize; + Gtk.Widget oc = Child; + Remove (oc); + oc.Destroy (); + } + + Gtk.HBox box = new HBox (); + box.Spacing = 2; + + if (icon != null) + box.PackStart (new Gtk.Image (icon), false, false, 0); + + if (!string.IsNullOrEmpty (label)) { + labelWidget = new Gtk.Label (label); + labelWidget.UseMarkup = true; + box.PackStart (labelWidget, true, true, 0); + } else { + labelWidget = null; + } + + Add (box); + + // Get the required size before setting the ellipsize property, since ellipsized labels + // have a width request of 0 + ShowAll (); + labelWidth = SizeRequest ().Width; + + if (labelWidget != null) + labelWidget.Ellipsize = oldMode; + } + + public void SetEllipsize (bool elipsize) + { + if (labelWidget != null) { + if (elipsize) + labelWidget.Ellipsize = Pango.EllipsizeMode.End; + else + labelWidget.Ellipsize = Pango.EllipsizeMode.None; + } + } + + public int LabelWidth { + get { return labelWidth; } + } + + public bool Active { + get { + return active; + } + set { + active = value; + this.QueueResize (); + QueueDraw (); + } + } + + public Widget Page { + get { + return page; + } + } + + protected override void OnSizeRequested (ref Gtk.Requisition req) + { + req = Child.SizeRequest (); + req.Width += HorzPadding * 2; + if (active) + req.Height += TopPaddingActive + BottomPaddingActive; + else + req.Height += TopPadding + BottomPadding; + } + + protected override void OnSizeAllocated (Gdk.Rectangle rect) + { + base.OnSizeAllocated (rect); + + rect.X += HorzPadding; + rect.Width -= HorzPadding * 2; + + if (active) { + rect.Y += TopPaddingActive; + rect.Height = Child.SizeRequest ().Height; + } + else { + rect.Y += TopPadding; + rect.Height = Child.SizeRequest ().Height; + } + Child.SizeAllocate (rect); + } + } +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/stock-auto-hide.png b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/stock-auto-hide.png Binary files differnew file mode 100644 index 0000000000..e31a471569 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/stock-auto-hide.png diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/stock-close-12.png b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/stock-close-12.png Binary files differnew file mode 100644 index 0000000000..71016e2f06 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/stock-close-12.png diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/stock-dock.png b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/stock-dock.png Binary files differnew file mode 100644 index 0000000000..225c2abfc7 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/stock-dock.png diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/stock-menu-left-12.png b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/stock-menu-left-12.png Binary files differnew file mode 100644 index 0000000000..c1d3befddc --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/stock-menu-left-12.png diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/stock-menu-right-12.png b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/stock-menu-right-12.png Binary files differnew file mode 100644 index 0000000000..df945cd85f --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/stock-menu-right-12.png |