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