// // 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; using Xwt.Motion; using MonoDevelop.Core; namespace MonoDevelop.Components.Docking { public class DockFrame: HBox, IAnimatable { internal const double ItemDockCenterArea = 0.4; internal const int GroupDockSeparatorSize = 40; internal bool ShadedSeparators = true; DockContainer container; int handleSize = 1; int handlePadding = 0; int defaultItemWidth = 300; int defaultItemHeight = 250; uint autoShowDelay = 400; uint autoHideDelay = 500; SortedDictionary layouts = new SortedDictionary (); List topLevels = new List (); string currentLayout; int compactGuiLevel = 3; DockBar dockBarTop, dockBarBottom, dockBarLeft, dockBarRight; VBox mainBox; DockVisualStyle defaultStyle; Gtk.Widget overlayWidget; public DockFrame () { Mono.TextEditor.GtkWorkarounds.FixContainerLeak (this); 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; UpdateDockbarsVisibility (); DefaultVisualStyle = new DockVisualStyle (); } public bool DockbarsVisible { get { return !OverlayWidgetVisible; } } internal bool UseWindowsForTopLevelFrames { get { return Platform.IsMac; } } /// /// Compactness level of the gui, from 1 (not compact) to 5 (very compact). /// 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 (); } } internal bool OverlayWidgetVisible { get; set; } public void AddOverlayWidget (Widget widget, bool animate = false) { RemoveOverlayWidget (false); this.overlayWidget = widget; widget.Parent = this; OverlayWidgetVisible = true; MinimizeAllAutohidden (); if (animate) { currentOverlayPosition = Math.Max (0, Allocation.Y + Allocation.Height); this.Animate ( "ShowOverlayWidget", ShowOverlayWidgetAnimation, easing: Easing.CubicOut); } else { currentOverlayPosition = Math.Max (0, Allocation.Y); QueueResize (); } UpdateDockbarsVisibility (); } public void RemoveOverlayWidget (bool animate = false) { this.AbortAnimation ("ShowOverlayWidget"); this.AbortAnimation ("HideOverlayWidget"); OverlayWidgetVisible = false; if (overlayWidget != null) { if (animate) { currentOverlayPosition = Allocation.Y; this.Animate ( "HideOverlayWidget", HideOverlayWidgetAnimation, finished: (a,b) => { if (overlayWidget != null) { overlayWidget.Unparent (); overlayWidget = null; } }, easing: Easing.SinOut); } else { overlayWidget.Unparent (); overlayWidget = null; QueueResize (); } } UpdateDockbarsVisibility (); } int currentOverlayPosition; void UpdateDockbarsVisibility () { dockBarTop.UpdateVisibility (); dockBarBottom.UpdateVisibility (); dockBarLeft.UpdateVisibility (); dockBarRight.UpdateVisibility (); } void ShowOverlayWidgetAnimation (double value) { currentOverlayPosition = Allocation.Y + (int)((double)Allocation.Height * (1f - value)); overlayWidget.SizeAllocate (new Rectangle (Allocation.X, currentOverlayPosition, Allocation.Width, Allocation.Height)); } void HideOverlayWidgetAnimation (double value) { currentOverlayPosition = Allocation.Y + (int)((double)Allocation.Height * value); overlayWidget.SizeAllocate (new Rectangle (Allocation.X, currentOverlayPosition, Allocation.Width, Allocation.Height)); } void IAnimatable.BatchBegin () { } void IAnimatable.BatchCommit () { } // Registered region styles. We are using a list instead of a dictionary because // the registering order is important List> regionStyles = new List> (); // Styles specific to items Dictionary stylesById = new Dictionary (); public DockVisualStyle DefaultVisualStyle { get { return defaultStyle; } set { defaultStyle = DockVisualStyle.CreateDefaultStyle (); defaultStyle.CopyValuesFrom (value); } } /// /// Sets the style for a region of the dock frame /// /// /// A region is a collection with the format: "ItemId1/Position1;ItemId2/Position2..." /// ItemId is the id of a dock item. Position is one of the values of the DockPosition enumeration /// /// /// Style. /// public void SetRegionStyle (string regionPosition, DockVisualStyle style) { // Remove any old region style and add it regionStyles.RemoveAll (s => s.Item1 == regionPosition); if (style != null) regionStyles.Add (new Tuple (regionPosition, style)); } public void SetDockItemStyle (string itemId, DockVisualStyle style) { if (style != null) stylesById [itemId] = style; else stylesById.Remove (itemId); } internal void UpdateRegionStyle (DockObject obj) { obj.VisualStyle = GetRegionStyleForObject (obj); } /// /// Gets the style for a dock object, which will inherit values from all region/style definitions /// internal DockVisualStyle GetRegionStyleForObject (DockObject obj) { DockVisualStyle mergedStyle = null; if (obj is DockGroupItem) { DockVisualStyle s; if (stylesById.TryGetValue (((DockGroupItem)obj).Id, out s)) { mergedStyle = DefaultVisualStyle.Clone (); mergedStyle.CopyValuesFrom (s); } } foreach (var e in regionStyles) { if (InRegion (e.Item1, obj)) { if (mergedStyle == null) mergedStyle = DefaultVisualStyle.Clone (); mergedStyle.CopyValuesFrom (e.Item2); } } return mergedStyle ?? DefaultVisualStyle; } internal DockVisualStyle GetRegionStyleForItem (DockItem item) { DockVisualStyle s; if (stylesById.TryGetValue (item.Id, out s)) { var ds = DefaultVisualStyle.Clone (); ds.CopyValuesFrom (s); return ds; } return DefaultVisualStyle; } /// /// Gets the style assigned to a specific position of the layout /// /// /// The region style for position. /// /// /// Group which contains the position /// /// /// Index of the position inside the group /// /// /// If true, the position will be inserted (meaning that the objects in childIndex will be shifted 1 position) /// internal DockVisualStyle GetRegionStyleForPosition (DockGroup parentGroup, int childIndex, bool insertingPosition) { DockVisualStyle mergedStyle = null; foreach (var e in regionStyles) { if (InRegion (e.Item1, parentGroup, childIndex, insertingPosition)) { if (mergedStyle == null) mergedStyle = DefaultVisualStyle.Clone (); mergedStyle.CopyValuesFrom (e.Item2); } } return mergedStyle ?? DefaultVisualStyle; } internal bool InRegion (string location, DockObject obj) { if (obj.ParentGroup == null) return false; return InRegion (location, obj.ParentGroup, obj.ParentGroup.GetObjectIndex (obj), false); } internal bool InRegion (string location, DockGroup objToFindParent, int objToFindIndex, bool insertingPosition) { // Checks if the object is in the specified region. // A region is a collection with the format: "ItemId1/Position1;ItemId2/Position2..." string[] positions = location.Split (';'); foreach (string pos in positions) { // We individually check each entry in the region specification int i = pos.IndexOf ('/'); if (i == -1) continue; string id = pos.Substring (0,i).Trim (); DockGroup g = container.Layout.FindGroupContaining (id); if (g != null) { DockPosition dpos; try { dpos = (DockPosition) Enum.Parse (typeof(DockPosition), pos.Substring(i+1).Trim(), true); } catch { continue; } var refItem = g.FindDockGroupItem (id); if (InRegion (g, dpos, refItem, objToFindParent, objToFindIndex, insertingPosition)) return true; } } return false; } bool InRegion (DockGroup grp, DockPosition pos, DockObject refObject, DockGroup objToFindParent, int objToFindIndex, bool insertingPosition) { if (grp == null) return false; if (grp.Type == DockGroupType.Tabbed) { if (pos != DockPosition.Center && pos != DockPosition.CenterBefore) return InRegion (grp.ParentGroup, pos, grp, objToFindParent, objToFindIndex, insertingPosition); } if (grp.Type == DockGroupType.Horizontal) { if (pos != DockPosition.Left && pos != DockPosition.Right) return InRegion (grp.ParentGroup, pos, grp, objToFindParent, objToFindIndex, insertingPosition); } if (grp.Type == DockGroupType.Vertical) { if (pos != DockPosition.Top && pos != DockPosition.Bottom) return InRegion (grp.ParentGroup, pos, grp, objToFindParent, objToFindIndex, insertingPosition); } bool foundAtLeftSide = true; bool findingLeft = pos == DockPosition.Left || pos == DockPosition.Top || pos == DockPosition.CenterBefore; if (objToFindParent == grp) { // Check positions beyond the current range of items if (objToFindIndex < 0 && findingLeft) return true; if (objToFindIndex >= grp.Objects.Count && !findingLeft) return true; } for (int n=0; n GetItems () { return container.Items; } bool LoadLayout (string layoutName) { DockLayout dl; if (!layouts.TryGetValue (layoutName, out dl)) return false; var focus = GetActiveWidget (); container.LoadLayout (dl); // Keep the currently focused widget when switching layouts if (focus != null && focus.IsRealized && focus.Visible) DockItem.SetFocus (focus); return true; } Gtk.Widget GetActiveWidget () { Gtk.Widget widget = this; while (widget is Gtk.Container) { Gtk.Widget child = ((Gtk.Container)widget).FocusChild; if (child != null) widget = child; else break; } return widget; } 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 UpdateStyle (DockItem item) { DockGroupItem gitem = container.FindDockGroupItem (item.Id); if (gitem == null) return; gitem.ParentGroup.UpdateStyle (item); dockBarTop.UpdateStyle (item); dockBarBottom.UpdateStyle (item); dockBarLeft.UpdateStyle (item); dockBarRight.UpdateStyle (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 (); } internal void SetDockLocation (DockItem item, string placement) { bool vis = item.Visible; DockItemStatus stat = item.Status; item.ResetMode (); container.Layout.RemoveItemRec (item); AddItemAtLocation (container.Layout, item, placement, vis, stat); } DockLayout GetDefaultLayout () { DockLayout group = new DockLayout (this); // Add items which don't have relative defaut positions List todock = new List (); 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 clone = new List (topLevels); foreach (DockFrameTopLevel child in clone) callback (child); if (overlayWidget != null) callback (overlayWidget); } protected override bool OnButtonPressEvent (EventButton evnt) { MinimizeAllAutohidden (); return base.OnButtonPressEvent (evnt); } void MinimizeAllAutohidden () { foreach (var it in GetItems ()) { if (it.Visible && it.Status == DockItemStatus.AutoHide) it.Minimize (); } } 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); } } public class DockStyle { public const string Default = "Default"; public const string Browser = "Browser"; } internal delegate void DockDelegate (DockItem item); }