Example #1
0
        static object OnCoerceValueContentProperty(DependencyObject d, object baseValue)
        {
            DockableFloatingWindow fl = ((DockableFloatingWindow)d);

            if (fl.Content != null)
            {
                throw new InvalidOperationException("Content on floating windows can't be set more than one time.");
            }

            if (!(baseValue is DockableContent) &&
                !(baseValue is DockablePane))
            {
                throw new InvalidOperationException("Content must be of type DockableContent or DockablePane");
            }

            FloatingDockablePane paneToReturn = null;

            if (baseValue is DockableContent)
            {
                paneToReturn = new FloatingDockablePane(fl, baseValue as DockableContent);
            }
            else if (baseValue is DockablePane)
            {
                paneToReturn = new FloatingDockablePane(fl, baseValue as DockablePane);
            }

            return(paneToReturn);
        }
Example #2
0
        void SaveLayout(XmlWriter xmlWriter, DockableFloatingWindow flWindow)
        {
            xmlWriter.WriteStartElement("FloatingWindow");
            xmlWriter.WriteAttributeString("IsDockableWindow", XmlConvert.ToString(flWindow.IsDockableWindow));

            xmlWriter.WriteAttributeString("Top", XmlConvert.ToString(flWindow.Top));
            xmlWriter.WriteAttributeString("Left", XmlConvert.ToString(flWindow.Left));
            xmlWriter.WriteAttributeString("Width", XmlConvert.ToString(flWindow.Width));
            xmlWriter.WriteAttributeString("Height", XmlConvert.ToString(flWindow.Height));

            SaveLayout(xmlWriter, flWindow.HostedPane as DockablePane);

            xmlWriter.WriteEndElement();
        }
Example #3
0
        /// <summary>
        /// Internal main restore layout method
        /// </summary>
        /// <param name="doc">Document Xml from which restore layout</param>
        void RestoreLayout(XmlDocument doc)
        {
            if (!_isControlLoaded)
                throw new InvalidOperationException("Unable to deserialize a docking layout while DockingManager control is unloaded");

            if (doc.DocumentElement == null ||
                doc.DocumentElement.Name != "DockingManager")
            {
                Debug.Assert(false, "Layout file hasn't a valid structure!");
                throw new InvalidOperationException("Layout file had not a valid structure!");
            }

            if (doc.DocumentElement.GetAttribute("version") != layoutFileVersion)
                throw new FileFormatException("Unsupported layout file version");

            if (doc.DocumentElement.ChildNodes.Count != 3 ||
                (doc.DocumentElement.ChildNodes[0].Name != "ResizingPanel" && doc.DocumentElement.ChildNodes[0].Name != "DocumentPane") ||
                doc.DocumentElement.ChildNodes[1].Name != "Hidden" ||
                doc.DocumentElement.ChildNodes[2].Name != "Windows")
            {
                Debug.Assert(false, "Layout file hasn't a valid structure!");
                throw new InvalidOperationException("Layout file hasn't a valid structure!");
            }

            //Hide temp windows
            HideFlyoutWindow();
            HideNavigatorWindow();
            //HideDocumentNavigatorWindow();

            RestoringLayout = true;

            //show all auto hidden panes
            var panesAutoHidden = DockableContents.Where(c => c.State == DockableContentState.AutoHide).Select(c => c.ContainerPane).Distinct();
            foreach (DockablePane pane in panesAutoHidden)
                pane.ToggleAutoHide();

            DockableContent[] actualContents = DockableContents.ToArray();
            DocumentContent[] actualDocuments = Documents.ToArray();

            //first detach all my actual contents
            this.Content = null;
            this.ActiveContent = null;
            this.ActiveDocument = null;

            //restore main panel
            XmlElement rootElement = doc.DocumentElement.ChildNodes[0] as XmlElement;
            DocumentPane mainDocumentPane = null;
            this.Content = RestoreLayout(rootElement, actualContents, actualDocuments, ref mainDocumentPane);
            MainDocumentPane = mainDocumentPane;

            //restore hidden contents
            foreach (XmlElement hiddenContentElement in doc.DocumentElement.ChildNodes[1].ChildNodes)
            {
                var hiddenContentName = hiddenContentElement.GetAttribute("Name");

                var hiddenContent = actualContents.FirstOrDefault(c => c.Name == hiddenContentName &&
                    c.State != DockableContentState.Hidden);

                if (hiddenContent != null)
                {
                    Hide(hiddenContent);
                    hiddenContent.RestoreLayout(hiddenContentElement);
                }
            }

            //restore floating windows
            foreach (XmlElement flWindowElement in doc.DocumentElement.ChildNodes[2].ChildNodes)
            {
                if (flWindowElement.ChildNodes.Count != 1)
                    continue;//handles invalid layouts structures

                bool isDockableWindow = XmlConvert.ToBoolean(flWindowElement.GetAttribute("IsDockableWindow"));
                Point location = new Point(XmlConvert.ToDouble(flWindowElement.GetAttribute("Left")), XmlConvert.ToDouble(flWindowElement.GetAttribute("Top")));
                Size size = new Size(XmlConvert.ToDouble(flWindowElement.GetAttribute("Width")), XmlConvert.ToDouble(flWindowElement.GetAttribute("Height")));

                XmlElement paneElement = flWindowElement.ChildNodes[0] as XmlElement;

                DockablePane paneForFloatingWindow = new DockablePane();
                if (paneElement.HasAttribute("ResizingWidth"))
                    ResizingPanel.SetResizeWidth(paneForFloatingWindow, (GridLength)GLConverter.ConvertFromInvariantString(paneElement.GetAttribute("ResizeWidth")));
                if (paneElement.HasAttribute("ResizingHeight"))
                    ResizingPanel.SetResizeHeight(paneForFloatingWindow, (GridLength)GLConverter.ConvertFromInvariantString(paneElement.GetAttribute("ResizeHeight")));
                paneForFloatingWindow.Anchor = (AnchorStyle)Enum.Parse(typeof(AnchorStyle), paneElement.GetAttribute("Anchor"));

                DockableContent contentToTransfer = null;
                foreach (XmlElement contentElement in paneElement.ChildNodes)
                {
                    #region Find the content to transfer
                    string contentToFindName = contentElement.GetAttribute("Name");
                    contentToTransfer = actualContents.FirstOrDefault(c => c.Name == contentToFindName);

                    if (contentToTransfer == null &&
                        DeserializationCallback != null)
                    {
                        DeserializationCallbackEventArgs e = new DeserializationCallbackEventArgs(contentToFindName);
                        DeserializationCallback(this, e);

                        contentToTransfer = e.Content as DockableContent;
                    }
                    #endregion
                    if (contentToTransfer != null)
                    {
                        DetachContentFromDockingManager(contentToTransfer);
                        paneForFloatingWindow.Items.Add(contentToTransfer);
                        contentToTransfer.RestoreLayout(contentElement);
                    }
                }

                if (paneForFloatingWindow.Items.Count > 0)
                {
                    var flWindow = new DockableFloatingWindow(this);
                    flWindow.Content = paneForFloatingWindow;
                    flWindow.Left = location.X;
                    flWindow.Top = location.Y;
                    flWindow.Width = size.Width;
                    flWindow.Height = size.Height;
                    flWindow.Owner = Window.GetWindow(this);

                    flWindow.IsDockableWindow = isDockableWindow;
                    flWindow.ShowActivated = false;

                    flWindow.ApplyTemplate();
                    flWindow.Show();
                }
            }

            ClearEmptyPanels(Content as ResizingPanel);

            //get documents that are not present in last layout and must be included
            //in the new one
            var documentsNotTransferred = actualDocuments.Where(d => d.ContainerPane == null || d.ContainerPane.GetManager() != this).ToArray();

            Debug.Assert(MainDocumentPane != null && MainDocumentPane.GetManager() == this);

            if (MainDocumentPane != null && documentsNotTransferred.Count() > 0)
            {
                documentsNotTransferred.ForEach(d => MainDocumentPane.Items.Add(d.DetachFromContainerPane()));
            }

            //get contents that are not present in the new layout and hide them
            var contentsNotTransferred = actualContents.Where(c => c.ContainerPane == null || c.ContainerPane.GetManager() != this).ToArray();

            contentsNotTransferred.ForEach(c =>
                {
                    Hide(c);
                });

            RestoringLayout = false;

            ClearEmptyPanes();
            RefreshContents();

            if (ActiveDocument != null &&
               (ActiveDocument.ContainerPane == null ||
               ActiveDocument.ContainerPane.GetManager() != this))
            {
                if (Documents.Count > 0)
                    ActiveDocument = Documents[0];
                else
                    ActiveDocument = null;
            }

            ActiveContent = ActiveDocument;
        }
Example #4
0
        /// <summary>
        /// Show a dockable content in its container with a desidered state
        /// </summary>
        /// <param name="content">Content to show</param>
        /// <param name="desideredState">State desidered</param>
        /// <param name="desideredAnchor">Border to which anchor the newly created container pane</param>
        /// <remarks></remarks>
        internal void Show(DockableContent content, DockableContentState desideredState, AnchorStyle desideredAnchor)
        {
            Debug.WriteLine(string.Format("Show Content={0}, desideredState={1}, desideredAnchor={2}", content.Name, desideredState, desideredAnchor));

            #region Dockable content

            if (desideredState == DockableContentState.Hidden)//??!!show hidden?
                Hide(content);

            if (content.State == DockableContentState.AutoHide)
            {
                //first redock the content
                (content.ContainerPane as DockablePane).ToggleAutoHide();
                //then show it as desidered
                Show(content, desideredState, desideredAnchor);
            }
            else if (content.State == DockableContentState.Docked ||
                content.State == DockableContentState.Document ||
                content.State == DockableContentState.None)
            {
                if (content.ContainerPane == null ||
                    content.State == DockableContentState.None)
                {
                    //Problem!? try to rescue
                    if (content.State == DockableContentState.Docked ||
                        content.State == DockableContentState.None)
                    {
                        //find the the pane which the desidered anchor style
                        //DockablePane foundPane = this.FindChildDockablePane(desideredAnchor != AnchorStyle.None ? desideredAnchor : AnchorStyle.Right);
                        //first search for a pane with other contents (avoiding empty panes which are containers for hidden contents)
                        ILinqToTree<DependencyObject> itemFound = new LogicalTreeAdapter(this).Descendants().FirstOrDefault(el => el.Item is DockablePane && (el.Item as DockablePane).Anchor == desideredAnchor && (el.Item as DockablePane).IsDocked);

                        if (itemFound == null)//search for all panes (even empty)
                            itemFound = new LogicalTreeAdapter(this).Descendants().FirstOrDefault(el => el.Item is DockablePane && (el.Item as DockablePane).Anchor == desideredAnchor && (el.Item as DockablePane).Items.Count == 0);

                        DockablePane foundPane = itemFound != null ? itemFound.Item as DockablePane : null;

                        if (foundPane != null)
                        {
                            content.SetStateToDock();
                            foundPane.Items.Add(content);
                            var containerPanel = foundPane.Parent as ResizingPanel;
                            if (containerPanel != null)
                                containerPanel.InvalidateMeasure();
                        }
                        else
                        {
                            //if no suitable pane was found create e new one on the fly
                            if (content.ContainerPane != null)
                            {
                                content.ContainerPane.RemoveContent(content);
                            }

                            DockablePane pane = new DockablePane();
                            pane.Items.Add(content);
                            Anchor(pane, desideredAnchor);
                        }
                    }
                    else
                    {
                        //add to main document pane
                        MainDocumentPane.Items.Add(content);
                    }

                }

                if (content.ContainerPane.GetManager() == null)
                {
                    //disconnect the parent pane from previous panel
                    //((Panel)content.ContainerPane.Parent).Children.Remove(content.ContainerPane);
                    if (content.ContainerPane.Parent != null)
                    {
                        ((Panel)content.ContainerPane.Parent).Children.Remove(content.ContainerPane);
                    }

                    Anchor(content.ContainerPane as DockablePane, desideredAnchor);
                }

                if (desideredState == DockableContentState.DockableWindow ||
                     desideredState == DockableContentState.FloatingWindow)
                {
                    var floatingWindow = new DockableFloatingWindow(this);
                    floatingWindow.Content = content;

                    var mainWindow = Window.GetWindow(this);
                    if (mainWindow.IsVisible)
                        floatingWindow.Owner = mainWindow;

                    //floatingWindow.WindowStartupLocation = WindowStartupLocation.CenterOwner;
                    //if (content.Content != null)
                    //{
                    //    floatingWindow.Width = Math.Min(((FrameworkElement)content.Content).ActualWidth, ResizingPanel.GetResizeWidth(content.ContainerPane));
                    //    floatingWindow.Height = Math.Min(((FrameworkElement)content.Content).ActualHeight, ResizingPanel.GetResizeHeight(content.ContainerPane));
                    //}
                    //else
                    ////{
                    //    floatingWindow.Width = 400;
                    //    floatingWindow.Height = 400;
                    //}

                    floatingWindow.Show();

                }
                else if (desideredState == DockableContentState.AutoHide)
                {
                    var paneContainer = content.ContainerPane as DockablePane;
                    Debug.Assert(paneContainer != null);

                    if (paneContainer != null)
                        paneContainer.ToggleAutoHide();

                    content.Activate();
                }
                else if (desideredState == DockableContentState.Document)
                {
                    DocumentPane docPane = MainDocumentPane;
                    if (docPane != null)
                    {
                        docPane.Items.Add(content.DetachFromContainerPane());
                        docPane.SelectedItem = content;
                        content.SetStateToDocument();
                    }
                }
                else
                {
                    content.ContainerPane.SelectedItem = content;
                    content.Activate();

                    DockablePane dockParent = content.ContainerPane as DockablePane;
                    if (content.ActualWidth == 0.0 && (
                        dockParent.Anchor == AnchorStyle.Left || dockParent.Anchor == AnchorStyle.Right))
                    {
                        ResizingPanel.SetResizeWidth(dockParent, new GridLength(200));
                        ResizingPanel.SetEffectiveSize(dockParent, new Size(200, 0.0));
                    }
                    else if (content.ActualWidth == 0.0 && (
                        dockParent.Anchor == AnchorStyle.Top || dockParent.Anchor == AnchorStyle.Bottom))
                    {
                        ResizingPanel.SetResizeHeight(dockParent, new GridLength(200));
                        ResizingPanel.SetEffectiveSize(dockParent, new Size(200, 0.0));
                    }

                }
            }
            else if (content.State == DockableContentState.Document)
            {
                if (content.ContainerPane != null)
                    content.ContainerPane.SelectedItem = this;
                content.Activate();
            }
            else if (content.State == DockableContentState.Hidden ||
                content.State == DockableContentState.DockableWindow ||
                content.State == DockableContentState.FloatingWindow)
            {
                if (content.State == DockableContentState.Hidden)
                {
                    //Debug.Assert(HiddenContents.Contains(content));
                    //HiddenContents.Remove(content);
                }
                else
                {
                    FloatingWindow floatingWindow = null;
                    floatingWindow = (content.ContainerPane as FloatingDockablePane).FloatingWindow;
                    content.DetachFromContainerPane();

                    if (floatingWindow.HostedPane.Items.Count == 0)
                        floatingWindow.Close();
                }

                if (desideredState == DockableContentState.Docked ||
                    desideredState == DockableContentState.AutoHide)
                {

                    if (content.SavedStateAndPosition != null &&
                        content.SavedStateAndPosition.ContainerPane != null &&
                        content.SavedStateAndPosition.ChildIndex >= 0 &&
                        content.SavedStateAndPosition.ContainerPane.GetManager() == this &&
                        desideredState == DockableContentState.Docked)
                    {
                        //ok previous container pane is here..
                        Pane prevPane = content.SavedStateAndPosition.ContainerPane;

                        if (content.SavedStateAndPosition.ChildIndex < prevPane.Items.Count)
                        {
                            prevPane.Items.Insert(content.SavedStateAndPosition.ChildIndex, content);
                        }
                        else
                        {
                            prevPane.Items.Add(content);
                        }

                        if (prevPane.Items.Count == 1)
                        {
                            if (!double.IsNaN(content.SavedStateAndPosition.Width) ||
                                !double.IsInfinity(content.SavedStateAndPosition.Width))
                            {
                                ResizingPanel.SetResizeWidth(content,
                                    new GridLength(content.SavedStateAndPosition.Width));
                            }
                        }

                        DockablePane prevDockablePane = prevPane as DockablePane;
                        if (prevDockablePane != null && prevDockablePane.IsAutoHidden)
                        {
                            prevDockablePane.ToggleAutoHide();
                        }

                        content.SetStateToDock();
                        content.Activate();

                        (prevPane.Parent as UIElement).InvalidateMeasure();
                    }
                    else
                    {
                        if (desideredAnchor == AnchorStyle.None &&
                            content.SavedStateAndPosition != null &&
                            content.SavedStateAndPosition.Anchor != AnchorStyle.None)
                            desideredAnchor = content.SavedStateAndPosition.Anchor;

                        if (desideredAnchor == AnchorStyle.None)
                            desideredAnchor = AnchorStyle.Right;

                        DockablePane foundPane = null;

                        if (desideredState == DockableContentState.Docked)
                        {
                            //first not empty panes
                            ILinqToTree<DependencyObject> itemFound = new LogicalTreeAdapter(this).Descendants().FirstOrDefault(el => el.Item is DockablePane && (el.Item as DockablePane).Anchor == desideredAnchor && (el.Item as DockablePane).IsDocked);

                            if (itemFound == null)//look for all panes even empty
                                itemFound = new LogicalTreeAdapter(this).Descendants().FirstOrDefault(el => el.Item is DockablePane && (el.Item as DockablePane).Anchor == desideredAnchor && (el.Item as DockablePane).Items.Count == 0);

                            foundPane = itemFound != null ? itemFound.Item as DockablePane : null;
                        }

                        if (foundPane != null)
                        {
                            content.SetStateToDock();
                            foundPane.Items.Add(content);

                            if ((foundPane.IsAutoHidden && desideredState == DockableContentState.Docked) ||
                                 (!foundPane.IsAutoHidden && desideredState == DockableContentState.AutoHide))
                                foundPane.ToggleAutoHide();
                        }
                        else
                        {
                            DockablePane newHostpane = new DockablePane();
                            newHostpane.Items.Add(content);

                            if (desideredAnchor == AnchorStyle.Left ||
                                desideredAnchor == AnchorStyle.Right)
                            {
                                double w = 200;
                                if (content.SavedStateAndPosition != null &&
                                    !double.IsInfinity(content.SavedStateAndPosition.Width) &&
                                    !double.IsNaN(content.SavedStateAndPosition.Width))
                                    w = content.SavedStateAndPosition.Width;

                                ResizingPanel.SetResizeWidth(newHostpane, new GridLength(w));
                                ResizingPanel.SetEffectiveSize(newHostpane, new Size(w, 0.0));
                            }
                            else
                            {
                                double h = 200;
                                if (content.SavedStateAndPosition != null &&
                                    !double.IsInfinity(content.SavedStateAndPosition.Height) &&
                                    !double.IsNaN(content.SavedStateAndPosition.Height))
                                    h = content.SavedStateAndPosition.Height;

                                ResizingPanel.SetResizeHeight(newHostpane, new GridLength(h));
                                ResizingPanel.SetEffectiveSize(newHostpane, new Size(0.0, h));
                            }

                            Anchor(newHostpane, desideredAnchor);

                            if (desideredState == DockableContentState.AutoHide)
                            {
                                ToggleAutoHide(newHostpane);
                            }
                        }
                    }

                    ActiveContent = content;
                }
                else if (desideredState == DockableContentState.DockableWindow ||
                    desideredState == DockableContentState.FloatingWindow)
                {
                    DockablePane newHostpane = null;
                    FloatingDockablePane prevHostpane = null;
                    if (content.SavedStateAndPosition != null && content.SavedStateAndPosition.ContainerPane != null && content.SavedStateAndPosition.ContainerPane is FloatingDockablePane)
                    {
                        prevHostpane = content.SavedStateAndPosition.ContainerPane as FloatingDockablePane;
                        if (!prevHostpane.Items.Contains(content))
                            prevHostpane.Items.Add(content);
                    }
                    else
                    {
                        newHostpane = new DockablePane();
                        newHostpane.Items.Add(content);

                    }

                    if (desideredState == DockableContentState.DockableWindow)
                        content.SetStateToDockableWindow();
                    else if (desideredState == DockableContentState.FloatingWindow)
                        content.SetStateToFloatingWindow();

                    if (prevHostpane != null)
                    {
                        //check to see if floating window that host prevHostPane is already loaded (hosting other contents)
                        var floatingWindow = prevHostpane.Parent as DockableFloatingWindow;
                        if (floatingWindow != null && floatingWindow.IsLoaded)
                        {
                            floatingWindow.Activate();
                        }
                        else
                        {
                            floatingWindow = new DockableFloatingWindow(this);
                            floatingWindow.Content = content;
                            floatingWindow.WindowStartupLocation = WindowStartupLocation.Manual;
                            floatingWindow.Top = prevHostpane.FloatingWindow.Top;
                            floatingWindow.Left = prevHostpane.FloatingWindow.Left;
                            floatingWindow.Width = prevHostpane.FloatingWindow.Width;
                            floatingWindow.Height = prevHostpane.FloatingWindow.Height;
                            //floatingWindow.Owner = Window.GetWindow(this);
                            var mainWindow = Window.GetWindow(this);
                            if (mainWindow.IsVisible)
                                floatingWindow.Owner = mainWindow;

                            //now I've created a new pane to host the hidden content
                            //if a an hidden content is shown that has prevHostpane as saved pane
                            //I want that it is relocated in this new pane that I've created right now
                            var hiddenContents = DockableContents.Where(c => c.State == DockableContentState.Hidden).ToArray();
                            foreach (var hiddenContent in hiddenContents)
                            {
                                if (hiddenContent.SavedStateAndPosition.ContainerPane == prevHostpane)
                                {
                                    hiddenContent.SavedStateAndPosition = new DockableContentStateAndPosition(
                                        (floatingWindow.Content as Pane),
                                        hiddenContent.SavedStateAndPosition.ChildIndex,
                                        hiddenContent.SavedStateAndPosition.Width,
                                        hiddenContent.SavedStateAndPosition.Height,
                                        hiddenContent.SavedStateAndPosition.Anchor,
                                        hiddenContent.SavedStateAndPosition.State);
                                }
                            }

                            floatingWindow.Show();
                        }
                    }
                    else if (newHostpane != null)
                    {
                        var floatingWindow = new DockableFloatingWindow(this);
                        floatingWindow.Content = newHostpane;
                        floatingWindow.WindowStartupLocation = WindowStartupLocation.CenterScreen;
                        floatingWindow.Width = 200;
                        floatingWindow.Height = 500;
                        //floatingWindow.Owner = Window.GetWindow(this);
                        var mainWindow = Window.GetWindow(this);
                        if (mainWindow.IsVisible)
                            floatingWindow.Owner = mainWindow;

                        floatingWindow.Show();
                    }

                }
                else if (desideredState == DockableContentState.Document)
                {
                    DocumentPane docPane = MainDocumentPane;
                    if (docPane != null)
                    {
                        docPane.Items.Add(content);
                        docPane.SelectedItem = content;
                        content.SetStateToDocument();
                    }
                }
            }

               #endregion
        }
Example #5
0
 internal void Drag(DockablePane dockablePane, Point point, Point offset)
 {
     if (CaptureMouse())
     {
         var floatingWindow = new DockableFloatingWindow(this);
         floatingWindow.Content = dockablePane;
         floatingWindow.Owner = Window.GetWindow(this);
         Drag(floatingWindow, point, offset);
     }
 }
Example #6
0
 internal FloatingDockablePane(DockableFloatingWindow floatingWindow, DockableContent contentToTransfer)
 {
     _floatingWindow    = floatingWindow;
     _contentToTransfer = contentToTransfer;
 }
Example #7
0
 internal FloatingDockablePane(DockableFloatingWindow floatingWindow, DockablePane paneToTransfer)
 {
     _floatingWindow = floatingWindow;
     _paneToTransfer = paneToTransfer;
 }
 internal FloatingDockablePane(DockableFloatingWindow floatingWindow, DockableContent contentToTransfer)
 {
     _floatingWindow = floatingWindow;
     _contentToTransfer = contentToTransfer;
 }
 internal FloatingDockablePane(DockableFloatingWindow floatingWindow, DockablePane paneToTransfer)
 {
     _floatingWindow = floatingWindow;
     _paneToTransfer = paneToTransfer;
 }
Example #10
0
 internal void Drag(DockableContent dockableContent, Point point, Point offset)
 {
     if (CaptureMouse())
     {
         var floatingWindow = new DockableFloatingWindow(this);
         floatingWindow.Content = dockableContent;
         floatingWindow.Owner = Window.GetWindow(this);
         floatingWindow.CopyInputBindingsFromOwner();
         Drag(floatingWindow, point, offset);
     }
 }