1 // 2 // PlaceholderWindow.cs 3 // 4 // Author: 5 // Mike Krüger <mkrueger@xamarin.com> 6 // 7 // Copyright (c) 2014 Xamarin Inc. (http://xamarin.com) 8 // 9 // Permission is hereby granted, free of charge, to any person obtaining a copy 10 // of this software and associated documentation files (the "Software"), to deal 11 // in the Software without restriction, including without limitation the rights 12 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 // copies of the Software, and to permit persons to whom the Software is 14 // furnished to do so, subject to the following conditions: 15 // 16 // The above copyright notice and this permission notice shall be included in 17 // all copies or substantial portions of the Software. 18 // 19 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 // THE SOFTWARE. 26 27 using Gdk; 28 using Gtk; 29 using Pinta.Docking; 30 using System.Collections.Generic; 31 using System; 32 using System.Linq; 33 using Pinta.Docking.Gui; 34 using MonoDevelop.Components; 35 36 namespace Pinta.Docking.DockNotebook 37 { 38 39 class PlaceholderWindow: Gtk.Window 40 { 41 uint anim; 42 int rx, ry, rw, rh; 43 List<DockNotebook> allNotebooks; 44 uint timeout; 45 46 DocumentTitleWindow titleWindow; 47 PlaceholderWindow()48 static PlaceholderWindow () 49 { 50 //IdeApp.Workbench.ActiveDocumentChanged += delegate { 51 // var doc = IdeApp.Workbench.ActiveDocument; 52 // if (doc == null) 53 // return; 54 // var rootWindow = doc.Window.ActiveViewContent.Control.Toplevel as DockWindow; 55 // if (rootWindow == null) 56 // return; 57 58 // rootWindow.Title = DefaultWorkbench.GetTitle (doc.Window); 59 //}; 60 } 61 62 DockNotebookTab frame; 63 PlaceholderWindow(DockNotebookTab tab)64 public PlaceholderWindow (DockNotebookTab tab): base (Gtk.WindowType.Toplevel) 65 { 66 this.frame = tab; 67 SkipTaskbarHint = true; 68 Decorated = false; 69 TypeHint = WindowTypeHint.Utility; 70 titleWindow = new DocumentTitleWindow (this, tab); 71 //IdeApp.Workbench.LockActiveWindowChangeEvent (); 72 73 titleWindow.FocusInEvent += delegate { 74 if (timeout != 0) { 75 GLib.Source.Remove (timeout); 76 timeout = 0; 77 } 78 }; 79 80 titleWindow.FocusOutEvent += delegate { 81 timeout = GLib.Timeout.Add (100, () => { 82 titleWindow.Close (); 83 return false; 84 }); 85 }; 86 87 //var windowStack = IdeApp.CommandService.TopLevelWindowStack.ToArray (); 88 allNotebooks = DockNotebook.AllNotebooks.ToList (); 89 //allNotebooks.Sort (delegate(DockNotebook x, DockNotebook y) { 90 // var ix = Array.IndexOf (windowStack, (Gtk.Window) x.Toplevel); 91 // var iy = Array.IndexOf (windowStack, (Gtk.Window) y.Toplevel); 92 // if (ix == -1) ix = int.MaxValue; 93 // if (iy == -1) iy = int.MaxValue; 94 // return ix.CompareTo (iy); 95 //}); 96 } 97 98 DockNotebook hoverNotebook; 99 CanPlaceInHoverNotebook()100 bool CanPlaceInHoverNotebook () 101 { 102 return !titleWindow.ControlPressed && hoverNotebook != null; 103 } 104 OnDestroyed()105 protected override void OnDestroyed () 106 { 107 base.OnDestroyed (); 108 Gtk.Application.Invoke (delegate { 109 titleWindow.Destroy (); 110 }); 111 //IdeApp.Workbench.UnlockActiveWindowChangeEvent (); 112 } 113 114 int curX, curY; 115 UpdatePosition()116 public void UpdatePosition () 117 { 118 MovePosition (curX, curY); 119 } 120 MovePosition(int x, int y)121 public void MovePosition (int x, int y) 122 { 123 this.curX = x; 124 this.curY = y; 125 126 ShowPlaceholder (x, y); 127 128 var alloc = titleWindow.Child.SizeRequest (); 129 titleWindow.Move (x - alloc.Width / 2, y - alloc.Height / 2); 130 titleWindow.Show (); 131 titleWindow.Present (); 132 } 133 ShowPlaceholder(int x, int y)134 public void ShowPlaceholder (int x, int y) 135 { 136 hoverNotebook = null; 137 138 // TODO: Handle z-ordering of floating windows. 139 int ox = 0, oy = 0; 140 foreach (var notebook in allNotebooks) { 141 if (notebook.GdkWindow == null) 142 continue; 143 144 int ox2, oy2; 145 notebook.ParentWindow.GetOrigin (out ox2, out oy2); 146 var alloc = notebook.Allocation; 147 ox2 += alloc.X; 148 ox2 += alloc.Y; 149 if (ox2 <= x && x <= ox2 + alloc.Width && oy2 <= y && y <= oy2 + alloc.Height) { 150 hoverNotebook = notebook; 151 TransientFor = (Gtk.Window) hoverNotebook.Toplevel; 152 ox = ox2; 153 oy = oy2; 154 break; 155 } 156 } 157 158 if (CanPlaceInHoverNotebook ()) { 159 var container = hoverNotebook.Container; 160 var alloc = hoverNotebook.Allocation; 161 var targetTabCount = hoverNotebook.TabCount; 162 var overTabStrip = y <= oy + hoverNotebook.BarHeight; 163 164 if (hoverNotebook.Tabs.Contains (frame)) 165 targetTabCount--; // Current is going to be removed, so it doesn't count 166 167 if (targetTabCount > 0 && x <= ox + alloc.Width / 3 && !overTabStrip) { 168 if (container.AllowLeftInsert) { 169 Relocate ( 170 ox, 171 oy, 172 alloc.Width / 2, 173 alloc.Height, 174 false 175 ); 176 placementDelegate = delegate(DockNotebook arg1, DockNotebookTab tab, Rectangle allocation2, int x2, int y2) { 177 var window = (SdiWorkspaceWindow)tab.Content; 178 container.InsertLeft (window); 179 window.SelectWindow (); 180 }; 181 return; 182 } 183 } 184 185 if (targetTabCount > 0 && x >= ox + alloc.Width - alloc.Width / 3 && !overTabStrip) { 186 if (container.AllowRightInsert) { 187 Relocate ( 188 ox + alloc.Width / 2, 189 oy, 190 alloc.Width / 2, 191 alloc.Height, 192 false 193 ); 194 placementDelegate = delegate(DockNotebook arg1, DockNotebookTab tab, Rectangle allocation2, int x2, int y2) { 195 var window = (SdiWorkspaceWindow)tab.Content; 196 container.InsertRight (window); 197 window.SelectWindow (); 198 }; 199 return; 200 } 201 } 202 203 Relocate ( 204 ox, 205 oy, 206 alloc.Width, 207 alloc.Height, 208 false 209 ); 210 if (!hoverNotebook.Tabs.Contains (frame)) 211 placementDelegate = PlaceInHoverNotebook; 212 else 213 placementDelegate = null; 214 return; 215 } 216 217 Hide (); 218 placementDelegate = PlaceInFloatingFrame; 219 titleWindow.SetDectorated (true); 220 } 221 OnFocusInEvent(EventFocus evt)222 protected override bool OnFocusInEvent (EventFocus evt) 223 { 224 if (timeout != 0) { 225 GLib.Source.Remove (timeout); 226 timeout = 0; 227 } 228 229 return base.OnFocusInEvent (evt); 230 } 231 OnFocusOutEvent(EventFocus evt)232 protected override bool OnFocusOutEvent (EventFocus evt) 233 { 234 timeout = GLib.Timeout.Add (100, () => { 235 titleWindow.Close (); 236 return false; 237 }); 238 239 return base.OnFocusOutEvent (evt); 240 } 241 OnRealized()242 protected override void OnRealized () 243 { 244 base.OnRealized (); 245 GdkWindow.Opacity = 0.4; 246 } OnExposeEvent(EventExpose evnt)247 protected override bool OnExposeEvent (EventExpose evnt) 248 { 249 int w, h; 250 GetSize (out w, out h); 251 252 using (var ctx = CairoHelper.Create (evnt.Window)) { 253 ctx.SetSourceColor (new Cairo.Color (0.17, 0.55, 0.79)); 254 ctx.Rectangle (Allocation.ToCairoRect ()); 255 ctx.Fill (); 256 } 257 return true; 258 } 259 Relocate(int x, int y, int w, int h, bool animate)260 public void Relocate (int x, int y, int w, int h, bool animate) 261 { 262 if (!Visible || x != rx || y != ry || w != rw || h != rh) { 263 Hide (); 264 if (w != rw || h != rh) 265 Resize (w, h); 266 Move (x, y); 267 ShowAll (); 268 titleWindow.SetDectorated (false); 269 270 rx = x; ry = y; rw = w; rh = h; 271 272 if (anim != 0) { 273 GLib.Source.Remove (anim); 274 anim = 0; 275 } 276 if (animate && w < 150 && h < 150) { 277 const int sa = 7; 278 Move (rx-sa, ry-sa); 279 Resize (rw+sa*2, rh+sa*2); 280 anim = GLib.Timeout.Add (10, RunAnimation); 281 } 282 } 283 } 284 RunAnimation()285 bool RunAnimation () 286 { 287 int cx, cy, ch, cw; 288 GetSize (out cw, out ch); 289 GetPosition (out cx, out cy); 290 291 if (cx != rx) { 292 cx++; cy++; 293 ch-=2; cw-=2; 294 Move (cx, cy); 295 Resize (cw, ch); 296 return true; 297 } 298 anim = 0; 299 return false; 300 } 301 302 public DockDelegate DockDelegate { get; private set; } 303 public Rectangle DockRect { get; private set; } 304 SetDockInfo(DockDelegate dockDelegate, Rectangle rect)305 public void SetDockInfo (DockDelegate dockDelegate, Rectangle rect) 306 { 307 DockDelegate = dockDelegate; 308 DockRect = rect; 309 } 310 PlaceInFloatingFrame(DockNotebook notebook, DockNotebookTab tab, Rectangle allocation, int ox, int oy)311 static void PlaceInFloatingFrame (DockNotebook notebook, DockNotebookTab tab, Rectangle allocation, int ox, int oy) 312 { 313 var newWindow = new DockWindow (); 314 var newNotebook = newWindow.Container.GetFirstNotebook (); 315 var newTab = newNotebook.AddTab (); 316 317 var workspaceWindow = (SdiWorkspaceWindow)tab.Content; 318 newTab.Content = workspaceWindow; 319 // JONTODO 320 //newWindow.Title = DefaultWorkbench.GetTitle (workspaceWindow); 321 322 workspaceWindow.SetDockNotebook (newNotebook, newTab); 323 newWindow.Move (ox - w / 2, oy - h / 2); 324 newWindow.Resize (w, h); 325 newWindow.ShowAll (); 326 DockNotebook.ActiveNotebook = newNotebook; 327 } 328 329 const int w = 640; 330 const int h = 480; 331 PlaceInHoverNotebook(DockNotebook notebook, DockNotebookTab tab, Rectangle allocation, int ox, int oy)332 void PlaceInHoverNotebook (DockNotebook notebook, DockNotebookTab tab, Rectangle allocation, int ox, int oy) 333 { 334 var window = (SdiWorkspaceWindow)tab.Content; 335 var newTab = hoverNotebook.AddTab (window); 336 window.SetDockNotebook (hoverNotebook, newTab); 337 window.SelectWindow (); 338 } 339 340 Action<DockNotebook, DockNotebookTab, Rectangle, int, int> placementDelegate; 341 PlaceWindow(DockNotebook notebook)342 public void PlaceWindow (DockNotebook notebook) 343 { 344 try { 345 //IdeApp.Workbench.LockActiveWindowChangeEvent (); 346 var allocation = Allocation; 347 Destroy (); 348 349 if (placementDelegate != null) { 350 var tab = notebook.CurrentTab; 351 notebook.RemoveTab (tab.Index, true); 352 placementDelegate (notebook, tab, allocation, curX, curY); 353 } else { 354 ((SdiWorkspaceWindow)frame.Content).SelectWindow (); 355 } 356 } finally { 357 //IdeApp.Workbench.UnlockActiveWindowChangeEvent (); 358 } 359 } 360 } 361 362 363 class DocumentTitleWindow: Gtk.Window 364 { 365 int controlKeyMask; 366 PlaceholderWindow placeholder; 367 HBox titleBox; 368 DocumentTitleWindow(PlaceholderWindow placeholder, DockNotebookTab draggedItem)369 public DocumentTitleWindow (PlaceholderWindow placeholder, DockNotebookTab draggedItem): base (Gtk.WindowType.Toplevel) 370 { 371 this.placeholder = placeholder; 372 373 SkipTaskbarHint = true; 374 Decorated = false; 375 376 //TransientFor = parent; 377 TypeHint = WindowTypeHint.Utility; 378 379 VBox mainBox = new VBox (); 380 mainBox.Spacing = 3; 381 382 titleBox = new HBox (false, 3); 383 if (draggedItem.Icon != null) { 384 var img = new ImageView (draggedItem.Icon); 385 titleBox.PackStart (img, false, false, 0); 386 } 387 Gtk.Label la = new Label (); 388 la.Markup = draggedItem.Text; 389 titleBox.PackStart (la, false, false, 0); 390 391 mainBox.PackStart (titleBox, false, false, 0); 392 393 var wi = RenderWidget (draggedItem.Content); 394 if (wi != null) { 395 wi = wi.WithBoxSize (200); 396 mainBox.PackStart (new ImageView (wi), false, false, 0); 397 } 398 399 CustomFrame f = new CustomFrame (); 400 f.SetPadding (2, 2, 2, 2); 401 f.SetMargins (1, 1, 1, 1); 402 f.Add (mainBox); 403 404 Add (f); 405 mainBox.CanFocus = true; 406 Child.ShowAll (); 407 } 408 SetDectorated(bool decorated)409 public void SetDectorated (bool decorated) 410 { 411 // Decorated = decorated; 412 // titleBox.Visible = !decorated; 413 } 414 415 public bool ControlPressed { 416 get { return controlKeyMask != 0; } 417 } 418 OnKeyPressEvent(EventKey evnt)419 protected override bool OnKeyPressEvent (EventKey evnt) 420 { 421 if (evnt.Key == Gdk.Key.Escape) 422 Close (); 423 if (evnt.Key == Gdk.Key.Control_L) 424 controlKeyMask |= 1; 425 if (evnt.Key == Gdk.Key.Control_R) 426 controlKeyMask |= 2; 427 placeholder.UpdatePosition (); 428 429 return base.OnKeyPressEvent (evnt); 430 } 431 432 OnKeyReleaseEvent(EventKey evnt)433 protected override bool OnKeyReleaseEvent (EventKey evnt) 434 { 435 if (evnt.Key == Gdk.Key.Control_L) 436 controlKeyMask &= ~1; 437 if (evnt.Key == Gdk.Key.Control_R) 438 controlKeyMask &= ~2; 439 placeholder.UpdatePosition (); 440 441 return base.OnKeyReleaseEvent (evnt); 442 } 443 OnButtonReleaseEvent(EventButton evnt)444 protected override bool OnButtonReleaseEvent (EventButton evnt) 445 { 446 Close (); 447 return base.OnButtonReleaseEvent (evnt); 448 } 449 OnLeaveNotifyEvent(EventCrossing evnt)450 protected override bool OnLeaveNotifyEvent (EventCrossing evnt) 451 { 452 Close (); 453 return base.OnLeaveNotifyEvent (evnt); 454 } 455 RenderWidget(Widget w)456 Gdk.Pixbuf RenderWidget (Widget w) 457 { 458 Gdk.Window win = w.GdkWindow; 459 460 if (win != null && win.IsViewable) 461 return Gdk.Pixbuf.FromDrawable (win, Colormap.System, w.Allocation.X, w.Allocation.Y, 0, 0, w.Allocation.Width, w.Allocation.Height); 462 else 463 return null; 464 } 465 Close()466 public void Close () 467 { 468 Application.Invoke (delegate { 469 placeholder.Destroy (); 470 }); 471 } 472 } 473 } 474