// // ShiftUI.Design.UISelectionService // // Authors: // Ivan N. Zlatev (contact i-nZ.net) // // (C) 2007 Ivan N. Zlatev // // 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; using System.ComponentModel; using System.ComponentModel.Design; using System.Drawing; using System.Drawing.Drawing2D; using ShiftUI; namespace ShiftUI.Design { internal class UISelectionService : IUISelectionService { private IServiceProvider _serviceProvider; private DesignerTransaction _transaction; private ISelectionService _selectionService; public UISelectionService (IServiceProvider serviceProvider) { if (serviceProvider == null) throw new ArgumentNullException ("serviceProvider"); _serviceProvider = serviceProvider; _transaction = null; _selectionService = serviceProvider.GetService (typeof (ISelectionService)) as ISelectionService; if (_selectionService == null) { IServiceContainer serviceContainer = serviceProvider.GetService (typeof (IServiceContainer)) as IServiceContainer; _selectionService = new UISelectionService (serviceContainer) as ISelectionService; serviceContainer.AddService (typeof (ISelectionService), (ISelectionService) _selectionService); } _selectionService.SelectionChanged += new EventHandler (OnSelectionChanged); } private ISelectionService SelectionService { get { return _selectionService; } } private object GetService (Type service) { return _serviceProvider.GetService (service); } public bool SelectionInProgress { get { return _selecting; } } public bool DragDropInProgress { get { return _dragging; } } public bool ResizeInProgress{ get { return _resizing; } } public bool SetCursor (int x, int y) { bool modified = false; // if moving mouse around - set cursor if mouse is hovering a selectionframes' grabhandles // SelectionFrame frame = GetSelectionFrameAt (x, y); if (frame != null && frame.HitTest (x, y) && frame.SetCursor (x, y)) modified = true; return modified; } public void MouseDragBegin (Widget container, int x, int y) { // * start resizing the selection frame // * start selecting // SelectionFrame frame = GetSelectionFrameAt (x, y); if (frame != null && frame.HitTest (x, y)) { this.SelectionService.SetSelectedComponents (new IComponent[] { frame.Widget }); if (_transaction == null) { IDesignerHost host = this.GetService (typeof (IDesignerHost)) as IDesignerHost; _transaction = host.CreateTransaction ("Resize " + (this.SelectionService.SelectionCount == 1 ? ((IComponent)this.SelectionService.PrimarySelection).Site.Name : "controls")); } this.ResizeBegin (x, y); } else { SelectionBegin (container, x, y); } } public void MouseDragMove (int x, int y) { if (_selecting) SelectionContinue (x, y); else if (_resizing) ResizeContinue (x, y); } public void MouseDragEnd (bool cancel) { if (_selecting) SelectionEnd (cancel); else if (_resizing) { ResizeEnd (cancel); if (_transaction != null) { if (cancel) _transaction.Cancel (); else _transaction.Commit (); _transaction = null; } } if (Cursor.Current != Cursors.Default) Cursor.Current = Cursors.Default; } #region Dragging private bool _dragging = false; private Point _prevMousePosition; private bool _firstMove = false; // container coordinates (primary selection's) // public void DragBegin () { // Console.WriteLine ("DragBegin"); if (_transaction == null) { IDesignerHost host = this.GetService (typeof (IDesignerHost)) as IDesignerHost; _transaction = host.CreateTransaction ("Move " + (this.SelectionService.SelectionCount == 1? ((IComponent)this.SelectionService.PrimarySelection).Site.Name : "controls")); } _dragging = true; _firstMove = true; if (this.SelectionService.PrimarySelection != null) ((Widget)this.SelectionService.PrimarySelection).DoDragDrop (new WidgetDataObject ((Widget)this.SelectionService.PrimarySelection), DragDropEffects.All); } // container cordinates // public void DragOver (Widget container, int x, int y) { // Console.WriteLine ("DragOver"); if (_dragging) { if (_firstMove) { _prevMousePosition = new Point (x, y); _firstMove = false; } else { int dx = x - _prevMousePosition.X; int dy = y - _prevMousePosition.Y; MoveSelection (container, dx, dy); _prevMousePosition = new Point (x, y); // Repaint everything >_< // IDesignerHost host = this.GetService (typeof (IDesignerHost)) as IDesignerHost; if (host != null && host.RootComponent != null) ((Widget)host.RootComponent).Refresh (); //container.Refresh (); } } } // container coordinates // public void DragDrop (bool cancel, Widget container, int x, int y) { // Console.WriteLine ("UISelectionService.DragDrop: in " + container.Site.Name); if (_dragging) { int dx = x - _prevMousePosition.X; int dy = y - _prevMousePosition.Y; MoveSelection (container, dx, dy); _dragging = false; // Repaint everything // IDesignerHost host = this.GetService (typeof (IDesignerHost)) as IDesignerHost; if (host != null && host.RootComponent != null) ((Widget)host.RootComponent).Refresh (); // Send mouse up message to the primary selection // Else for parentcontroldesigner there is no mouseup event and it doesn't set allow drop back to false // Native.SendMessage (((Widget)this.SelectionService.PrimarySelection).Handle, Native.Msg.WM_LBUTTONUP, (IntPtr) 0, (IntPtr) 0); if (_transaction != null) { if (cancel) _transaction.Cancel (); else _transaction.Commit (); _transaction = null; } } } private void MoveSelection (Widget container, int dx, int dy) { bool reparent = false; Widget oldParent = null; if (((Widget)this.SelectionService.PrimarySelection).Parent != container && !this.SelectionService.GetComponentSelected (container)) { reparent = true; oldParent = ((Widget)this.SelectionService.PrimarySelection).Parent; } // FIXME: Should check selectionstyle per control to determine if it's locked... // if locked -> don't move // ICollection selection = this.SelectionService.GetSelectedComponents (); foreach (Component component in selection) { Widget control = component as Widget; if (reparent) TypeDescriptor.GetProperties (control)["Parent"].SetValue (control, container); PropertyDescriptor property = TypeDescriptor.GetProperties (control)["Location"]; Point location = (Point) property.GetValue (control); location.X += dx; location.Y += dy; property.SetValue (control, location); } if (reparent) { oldParent.Invalidate (false); oldParent.Update (); } } #endregion #region Selection private bool _selecting = false; private Widget _selectionContainer = null; private Point _initialMousePosition; private Rectangle _selectionRectangle; // XXX private ArrayList _selectionFrames = new ArrayList (); public Rectangle SelectionBounds { get { return _selectionRectangle; } } // container coordinates // private void SelectionBegin (Widget container, int x, int y) { // Console.WriteLine ("SelectionBegin"); _selecting = true; _selectionContainer = container; _prevMousePosition = new Point (x, y); _initialMousePosition = _prevMousePosition; _selectionRectangle = new Rectangle (x , y, 0, 0); } private void SelectionContinue (int x, int y) { // Console.WriteLine ("SelectionContinue"); if (_selecting) { // right to right // if (x > _selectionRectangle.Right) { _selectionRectangle.Width = x - _selectionRectangle.X; } // right to left else if (x > _selectionRectangle.X && x < _selectionRectangle.Right && x < _prevMousePosition.X) { _selectionRectangle.Width = x - _selectionRectangle.X; } // left to left - f else if (x < _selectionRectangle.X) { // hasn't flipped if (_prevMousePosition.X > _selectionRectangle.X) { _selectionRectangle.X = _initialMousePosition.X; _selectionRectangle.Width = 0; } else { _selectionRectangle.Width += _selectionRectangle.X - x; _selectionRectangle.X = x; } } // left to right - f else if (x > _selectionRectangle.X && x < _selectionRectangle.Right && x > _prevMousePosition.X) { if (_prevMousePosition.X < _selectionRectangle.X) { _selectionRectangle.X = _initialMousePosition.X; _selectionRectangle.Width = 0; } else { _selectionRectangle.Width -= x - _selectionRectangle.X; _selectionRectangle.X = x; } } if (y > _selectionRectangle.Bottom) { _selectionRectangle.Height = y - _selectionRectangle.Y; } else if (y > _selectionRectangle.Y && y < _selectionRectangle.Bottom && y < _prevMousePosition.Y) { _selectionRectangle.Height = y - _selectionRectangle.Y; } else if (y < _selectionRectangle.Y) { if (_prevMousePosition.Y > _selectionRectangle.Y) { _selectionRectangle.Y = _initialMousePosition.Y; _selectionRectangle.Height = 0; } else { _selectionRectangle.Height += _selectionRectangle.Y - y; _selectionRectangle.Y = y; } } else if (y > _selectionRectangle.Y && y < _selectionRectangle.Bottom && y > _prevMousePosition.Y) { if (_prevMousePosition.Y < _selectionRectangle.Y) { _selectionRectangle.Y = _initialMousePosition.Y; _selectionRectangle.Height = 0; } else { _selectionRectangle.Height -= y - _selectionRectangle.Y; _selectionRectangle.Y = y; } } _prevMousePosition.X = x; _prevMousePosition.Y = y; _selectionContainer.Refresh (); } } private void SelectionEnd (bool cancel) { // Console.WriteLine ("SelectionEnd"); _selecting = false; ICollection selectedWidgets = GetWidgetsIn (_selectionRectangle); // do not change selection if nothing has changed // if (selectedWidgets.Count != 0) this.SelectionService.SetSelectedComponents (selectedWidgets, SelectionTypes.Replace); _selectionContainer.Refresh (); } #endregion #region Resizing private SelectionFrame _selectionFrame; private bool _resizing = false; private void ResizeBegin (int x, int y) { // Console.WriteLine ("ResizeBegin"); _resizing = true; _selectionFrame = this.GetSelectionFrameAt (x, y); _selectionFrame.ResizeBegin (x, y); } private void ResizeContinue (int x, int y) { // Console.WriteLine ("ResizeContinue"); Rectangle deltaBounds = _selectionFrame.ResizeContinue (x, y); ICollection selection = this.SelectionService.GetSelectedComponents (); foreach (IComponent component in selection) { if (component is Widget) { SelectionFrame frame = GetSelectionFrameFor ((Widget)component); if (frame != _selectionFrame) frame.Resize (deltaBounds); } } } private void ResizeEnd (bool cancel) { // Console.WriteLine ("ResizeEnd"); _selectionFrame.ResizeEnd (cancel); _resizing = false; } private SelectionFrame GetSelectionFrameAt (int x, int y) { SelectionFrame result = null; foreach (SelectionFrame frame in _selectionFrames) { if (frame.Bounds.Contains (new Point (x, y))) { result = frame; break; } } return result; } private SelectionFrame GetSelectionFrameFor (Widget control) { foreach (SelectionFrame frame in _selectionFrames) { if (control == frame.Widget) return frame; } return null; } #endregion public bool AdornmentsHitTest (Widget control, int x, int y) { SelectionFrame frame = GetSelectionFrameAt (x, y); if (frame != null) return frame.HitTest (x, y); return false; } // This method is called by all ParentWidgetDesigner.OnPaintAdornments. // Selection frames are drawn in the parent container of the primary selection // selection rectangle is drawn in the primary selection // public void PaintAdornments (Widget container, Graphics gfx) { IDesignerHost host = this.GetService (typeof (IDesignerHost)) as IDesignerHost; if (host == null || !(this.SelectionService.PrimarySelection is Widget)) return; if ((Widget)this.SelectionService.PrimarySelection == container) { if (_selecting) { Color negativeColor = Color.FromArgb ((byte)~(_selectionContainer.BackColor.R), (byte)~(_selectionContainer.BackColor.G), (byte)~(_selectionContainer.BackColor.B)); DrawSelectionRectangle (gfx, _selectionRectangle, negativeColor); } } else if (((Widget)this.SelectionService.PrimarySelection).Parent == container) { foreach (SelectionFrame frame in _selectionFrames) frame.OnPaint (gfx); } } private void DrawSelectionRectangle (Graphics gfx, Rectangle frame, Color color) { Pen pen = new Pen (color); pen.DashStyle = DashStyle.Dash; gfx.DrawRectangle (pen, frame); } private void OnSelectionChanged (object sender, EventArgs args) { ICollection selection = this.SelectionService.GetSelectedComponents (); if (_selectionFrames.Count == 0) { foreach (Component component in selection) { _selectionFrames.Add (new SelectionFrame ((Widget) component)); } // this code should get executed only once! (when initial primary selection is set) } else { int i = 0; foreach (Component component in selection) { if (i >= _selectionFrames.Count) _selectionFrames.Add (new SelectionFrame ((Widget) component)); else ((SelectionFrame)_selectionFrames[i]).Widget = (Widget) component; i++; } if (i < _selectionFrames.Count) _selectionFrames.RemoveRange (i, _selectionFrames.Count - i); } // Refresh the whole design surface (including the view) // IDesignerHost host = this.GetService (typeof (IDesignerHost)) as IDesignerHost; Widget root = host.RootComponent as Widget; if (root != null) { if (root.Parent != null) root.Parent.Refresh (); else root.Refresh (); } } private ICollection GetWidgetsIn (Rectangle rect) { ArrayList selectedWidgets = new ArrayList (); foreach (Widget control in _selectionContainer.Widgets) { if (rect.Contains (control.Bounds) || rect.IntersectsWith (control.Bounds)) selectedWidgets.Add (control); } return selectedWidgets; } } }