Dock.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619
  1. /*
  2. * $Id$
  3. *
  4. * Copyright (C) 2004 Todd Berman <tberman@off.net>
  5. * Copyright (C) 2004 Jeroen Zwartepoorte <jeroen@xs4all.nl>
  6. * Copyright (C) 2005 John Luke <john.luke@gmail.com>
  7. *
  8. * based on work by:
  9. * Copyright (C) 2002 Gustavo Giráldez <gustavo.giraldez@gmx.net>
  10. *
  11. * This program is free software; you can redistribute it and/or modify
  12. * it under the terms of the GNU General Public License as published by
  13. * the Free Software Foundation; either version 2 of the License, or
  14. * (at your option) any later version.
  15. *
  16. * This program is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. * GNU General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU General Public License
  22. * along with this program; if not, write to the Free Software
  23. * Foundation, Inc., 59 Temple Place - Suite 330,
  24. * Boston, MA 02111-1307, USA.
  25. */
  26. using System;
  27. using System.Collections;
  28. using System.Xml;
  29. using Gtk;
  30. namespace Gdl
  31. {
  32. public class Dock : DockObject
  33. {
  34. private readonly float SplitRatio = 0.3f;
  35. private DockObject root = null;
  36. private bool floating;
  37. private Widget window;
  38. private bool autoTitle;
  39. private int floatX;
  40. private int floatY;
  41. private int width = -1;
  42. private int height = -1;
  43. private Gdk.GC xorGC;
  44. private string title;
  45. protected Dock (IntPtr raw) : base (raw) { }
  46. public Dock () : this (null, false)
  47. {
  48. }
  49. public Dock (Dock original, bool floating)
  50. : this (original, floating, 0, 0, -1, -1)
  51. {
  52. }
  53. public Dock (Dock original, bool floating, int x, int y, int width, int height)
  54. {
  55. floatX = x;
  56. floatY = y;
  57. this.width = width;
  58. this.height = height;
  59. this.floating = floating;
  60. SetFlag (WidgetFlags.NoWindow);
  61. if (original != null)
  62. Bind (original.Master);
  63. /* create a master for the dock if none was provided */
  64. if (Master == null) {
  65. DockObjectFlags &= ~(DockObjectFlags.Automatic);
  66. Bind (new DockMaster ());
  67. }
  68. if (floating) {
  69. /* create floating window for this dock */
  70. window = new Window (WindowType.Toplevel);
  71. Window wnd = window as Window;
  72. /* set position and default size */
  73. wnd.WindowPosition = WindowPosition.Mouse;
  74. wnd.SetDefaultSize (width, height);
  75. wnd.TypeHint = Gdk.WindowTypeHint.Normal;
  76. /* metacity ignores this */
  77. wnd.Move (floatX, floatY);
  78. /* connect to the configure event so we can track down window geometry */
  79. wnd.ConfigureEvent += new ConfigureEventHandler (OnFloatingConfigure);
  80. /* set the title */
  81. SetWindowTitle ();
  82. /* set transient for the first dock if that is a non-floating dock */
  83. DockObject controller = Master.Controller;
  84. if (controller != null && controller is Dock) {
  85. if (!((Dock)controller).Floating) {
  86. if (controller.Toplevel != null && controller.Toplevel is Window) {
  87. wnd.TransientFor = (Window)controller.Toplevel;
  88. }
  89. }
  90. }
  91. wnd.Add (this);
  92. wnd.DeleteEvent += new DeleteEventHandler (OnFloatingDelete);
  93. }
  94. DockObjectFlags |= DockObjectFlags.Attached;
  95. }
  96. [Export]
  97. public bool Floating {
  98. get {
  99. return floating;
  100. }
  101. set {
  102. floating = value;
  103. }
  104. }
  105. [Export]
  106. public int FloatX {
  107. get {
  108. return floatX;
  109. }
  110. set {
  111. floatX = value;
  112. if (floating && window != null && window is Window)
  113. ((Window)window).Resize (width, height);
  114. }
  115. }
  116. [Export]
  117. public int FloatY {
  118. get {
  119. return floatY;
  120. }
  121. set {
  122. floatY = value;
  123. if (floating && window != null && window is Window)
  124. ((Window)window).Resize (width, height);
  125. }
  126. }
  127. [Export]
  128. public int Height {
  129. get {
  130. return height;
  131. }
  132. set {
  133. height = value;
  134. if (floating && window != null && window is Window)
  135. ((Window)window).Resize (width, height);
  136. }
  137. }
  138. private bool IsController {
  139. get {
  140. if (Master == null)
  141. return false;
  142. return Master.Controller == this;
  143. }
  144. }
  145. public ICollection NamedItems {
  146. get {
  147. return Master.DockObjects;
  148. }
  149. }
  150. public DockObject Root {
  151. get {
  152. return root;
  153. }
  154. set {
  155. root = value;
  156. }
  157. }
  158. public string Title {
  159. get {
  160. return title;
  161. }
  162. set {
  163. title = value;
  164. }
  165. }
  166. [Export]
  167. public int Width {
  168. get { return width; }
  169. set {
  170. width = value;
  171. if (floating && window != null && window is Window)
  172. ((Window)window).Resize (width, height);
  173. }
  174. }
  175. protected override void OnSizeRequested (ref Requisition requisition)
  176. {
  177. // make request to root
  178. if (root != null && root.Visible)
  179. requisition = root.SizeRequest ();
  180. else
  181. requisition.Width = requisition.Height = 0;
  182. requisition.Width += 2 * (int)BorderWidth;
  183. requisition.Height += 2 * (int)BorderWidth;
  184. }
  185. protected override void OnSizeAllocated (Gdk.Rectangle allocation)
  186. {
  187. base.OnSizeAllocated (allocation);
  188. // reduce allocation by border width
  189. int bw = (int)BorderWidth;
  190. allocation.X += bw;
  191. allocation.Y += bw;
  192. allocation.Width = Math.Max (1, allocation.Width - 2 * bw);
  193. allocation.Height = Math.Max (1, allocation.Height - 2 * bw);
  194. if (root != null && root.Visible)
  195. root.SizeAllocate (allocation);
  196. }
  197. protected override void OnMapped ()
  198. {
  199. base.OnMapped ();
  200. if (root != null && root.Visible && !root.IsMapped)
  201. root.Map ();
  202. }
  203. protected override void OnUnmapped ()
  204. {
  205. base.OnUnmapped ();
  206. if (root != null && root.Visible && root.IsMapped)
  207. root.Unmap ();
  208. if (window != null)
  209. window.Unmap ();
  210. }
  211. protected override void OnShown ()
  212. {
  213. base.OnShown ();
  214. if (floating && window != null)
  215. window.ShowAll ();
  216. if (IsController) {
  217. foreach (DockObject item in Master.TopLevelDocks) {
  218. if (item == this)
  219. continue;
  220. if (item.IsAutomatic)
  221. item.Show ();
  222. }
  223. }
  224. }
  225. protected override void OnHidden ()
  226. {
  227. base.OnHidden ();
  228. if (floating && window != null)
  229. window.Hide ();
  230. if (IsController) {
  231. foreach (DockObject item in Master.TopLevelDocks) {
  232. if (item == this)
  233. continue;
  234. if (item.IsAutomatic)
  235. item.Hide ();
  236. }
  237. }
  238. }
  239. protected override void OnDestroyed ()
  240. {
  241. if (window != null) {
  242. window.Destroy ();
  243. floating = false;
  244. window = null;
  245. }
  246. // destroy the xor gc
  247. if (xorGC != null)
  248. xorGC = null;
  249. base.OnDestroyed ();
  250. }
  251. protected override void OnAdded (Widget widget)
  252. {
  253. DockItem child = widget as DockItem;
  254. AddItem (child, DockPlacement.Top);
  255. }
  256. protected override void OnRemoved (Widget widget)
  257. {
  258. bool wasVisible = widget.Visible;
  259. if (root == widget) {
  260. root.DockObjectFlags &= ~(DockObjectFlags.Attached);
  261. root = null;
  262. widget.Unparent ();
  263. if (wasVisible && Visible)
  264. QueueResize ();
  265. }
  266. }
  267. protected override void ForAll (bool include_internals, Callback cb)
  268. {
  269. if (root != null)
  270. cb (root);
  271. }
  272. public override void OnDetached (bool recursive)
  273. {
  274. if (recursive && root != null)
  275. root.Detach (recursive);
  276. DockObjectFlags &= ~(DockObjectFlags.Attached);
  277. }
  278. public override void OnReduce ()
  279. {
  280. if (root != null)
  281. return;
  282. if (IsAutomatic) {
  283. Destroy ();
  284. } else if (!IsAttached) {
  285. if (floating)
  286. Hide ();
  287. else if (Parent != null && Parent is Container)
  288. ((Container)Parent).Remove (this);
  289. }
  290. }
  291. public override bool OnDockRequest (int x, int y, ref DockRequest request)
  292. {
  293. bool mayDock = false;
  294. /* we get (x,y) in our allocation coordinates system */
  295. /* Get dock size. */
  296. Gdk.Rectangle alloc = Allocation;
  297. int bw = (int)BorderWidth;
  298. /* Get coordinates relative to our allocation area. */
  299. int relX = x - alloc.X;
  300. int relY = y - alloc.Y;
  301. /* Check if coordinates are in GdlDock widget. */
  302. if (relX > 0 && relX < alloc.Width &&
  303. relY > 0 && relY < alloc.Height) {
  304. /* It's inside our area. */
  305. mayDock = true;
  306. /* Set docking indicator rectangle to the Dock size. */
  307. request.X = alloc.X + bw;
  308. request.Y = alloc.Y + bw;
  309. request.Width = alloc.Width - 2 * bw;
  310. request.Height = alloc.Height - 2 * bw;
  311. /* If Dock has no root item yet, set the dock
  312. itself as possible target. */
  313. if (root == null) {
  314. request.Position = DockPlacement.Top;
  315. request.Target = this;
  316. } else {
  317. request.Target = root;
  318. /* See if it's in the BorderWidth band. */
  319. if (relX < bw) {
  320. request.Position = DockPlacement.Left;
  321. request.Width = (int)(request.Width * SplitRatio);
  322. } else if (relX > alloc.Width - bw) {
  323. request.Position = DockPlacement.Right;
  324. request.X += (int)(request.Width * (1 - SplitRatio));
  325. request.Width = (int)(request.Width * SplitRatio);
  326. } else if (relY < bw) {
  327. request.Position = DockPlacement.Top;
  328. request.Height = (int)(request.Height * SplitRatio);
  329. } else if (relY > alloc.Height - bw) {
  330. request.Position = DockPlacement.Bottom;
  331. request.Y += (int)(request.Height * (1 - SplitRatio));
  332. request.Height = (int)(request.Height * SplitRatio);
  333. } else {
  334. /* Otherwise try our children. */
  335. /* give them allocation coordinates
  336. (we are a NoWindow widget) */
  337. mayDock = root.OnDockRequest (x, y, ref request);
  338. }
  339. }
  340. }
  341. return mayDock;
  342. }
  343. public override void OnDocked (DockObject requestor, DockPlacement position, object data)
  344. {
  345. /* only dock items allowed at this time */
  346. if (!(requestor is DockItem))
  347. return;
  348. if (position == DockPlacement.Floating) {
  349. int x = 0, y = 0, width = -1, height = -1;
  350. if (data != null && data is Gdk.Rectangle) {
  351. Gdk.Rectangle rect = (Gdk.Rectangle)data;
  352. x = rect.X;
  353. y = rect.Y;
  354. width = rect.Width;
  355. height = rect.Height;
  356. }
  357. AddFloatingItem ((DockItem)requestor, x, y, width, height);
  358. } else if (root != null) {
  359. /* This is somewhat a special case since we know
  360. which item to pass the request on because we only
  361. have one child */
  362. root.Dock (requestor, position, null);
  363. SetWindowTitle ();
  364. } else { /* Item about to be added is root item. */
  365. root = requestor;
  366. root.DockObjectFlags |= DockObjectFlags.Attached;
  367. root.Parent = this;
  368. ((DockItem)root).ShowGrip ();
  369. /* Realize the item (create its corresponding GdkWindow)
  370. when the Dock has been realized. */
  371. if (IsRealized)
  372. root.Realize ();
  373. /* Map the widget if it's visible and the parent is
  374. visible and has been mapped. This is done to make
  375. sure that the GdkWindow is visible. */
  376. if (Visible && root.Visible) {
  377. if (IsMapped)
  378. root.Map ();
  379. /* Make the widget resize. */
  380. root.QueueResize ();
  381. }
  382. SetWindowTitle ();
  383. }
  384. }
  385. public override bool OnReorder (DockObject requestor, DockPlacement position, object data)
  386. {
  387. if (Floating && position == DockPlacement.Floating && root == requestor) {
  388. if (window != null && data != null && data is Gdk.Rectangle) {
  389. Gdk.Rectangle rect = (Gdk.Rectangle)data;
  390. ((Window)window).Move (rect.X, rect.Y);
  391. return true;
  392. }
  393. }
  394. return false;
  395. }
  396. public override bool OnChildPlacement (DockObject child, ref DockPlacement placement)
  397. {
  398. if (root == child) {
  399. if (placement == DockPlacement.None ||
  400. placement == DockPlacement.Floating)
  401. placement = DockPlacement.Top;
  402. return true;
  403. }
  404. return false;
  405. }
  406. public override void OnPresent (DockObject child)
  407. {
  408. if (Floating && window != null && window is Window)
  409. ((Window)window).Present ();
  410. }
  411. public void AddItem (DockItem item, DockPlacement placement)
  412. {
  413. if (item == null)
  414. return;
  415. if (placement == DockPlacement.Floating)
  416. AddFloatingItem (item, 0, 0, -1, -1);
  417. else
  418. Dock (item, placement, null);
  419. }
  420. public void AddFloatingItem (DockItem item, int x, int y, int width, int height)
  421. {
  422. Dock dock = new Dock (this, true, x, y, width, height);
  423. if (Visible) {
  424. dock.Show ();
  425. if (IsMapped)
  426. dock.Map ();
  427. dock.QueueResize ();
  428. }
  429. dock.AddItem (item, DockPlacement.Top);
  430. }
  431. public DockItem GetItemByName (string name)
  432. {
  433. if (name == null)
  434. return null;
  435. DockObject found = Master.GetObject (name);
  436. if (found != null && found is DockItem)
  437. return (DockItem)found;
  438. else
  439. return null;
  440. }
  441. public DockPlaceholder GetPlaceholderByName (string name)
  442. {
  443. if (name == null)
  444. return null;
  445. DockObject found = Master.GetObject (name);
  446. if (found != null && found is DockPlaceholder)
  447. return (DockPlaceholder)found;
  448. else
  449. return null;
  450. }
  451. public static Dock GetTopLevel (DockObject obj)
  452. {
  453. DockObject parent = obj;
  454. while (parent != null && !(parent is Dock))
  455. parent = parent.ParentObject;
  456. return parent != null ? (Dock)parent : null;
  457. }
  458. public void XorRect (Gdk.Rectangle rect)
  459. {
  460. if (xorGC == null) {
  461. if (IsRealized) {
  462. Gdk.GCValues values = new Gdk.GCValues ();
  463. values.Function = Gdk.Function.Invert;
  464. values.SubwindowMode = Gdk.SubwindowMode.IncludeInferiors;
  465. xorGC = new Gdk.GC (GdkWindow);
  466. xorGC.SetValues (values, Gdk.GCValuesMask.Function |
  467. Gdk.GCValuesMask.Subwindow);
  468. } else {
  469. return;
  470. }
  471. }
  472. xorGC.SetLineAttributes (1, Gdk.LineStyle.OnOffDash,
  473. Gdk.CapStyle.NotLast,
  474. Gdk.JoinStyle.Bevel);
  475. xorGC.SetDashes (1, new sbyte[] { 1, 1}, 2);
  476. GdkWindow.DrawRectangle (xorGC, false, rect.X, rect.Y,
  477. rect.Width, rect.Height);
  478. xorGC.SetDashes (0, new sbyte[] { 1, 1}, 2);
  479. GdkWindow.DrawRectangle (xorGC, false, rect.X + 1,
  480. rect.Y + 1, rect.Width - 2,
  481. rect.Height - 2);
  482. }
  483. private void SetWindowTitle ()
  484. {
  485. if (window == null)
  486. return;
  487. if (!autoTitle && LongName != null)
  488. title = LongName;
  489. else if (Master != null)
  490. title = Master.DefaultTitle;
  491. if (title == null && root != null)
  492. title = root.LongName;
  493. if (title == null) {
  494. autoTitle = true;
  495. title = "Dock " + Master.DockNumber++;
  496. LongName = title;
  497. }
  498. ((Window)window).Title = title;
  499. }
  500. [GLib.ConnectBefore]
  501. private void OnFloatingConfigure (object o, ConfigureEventArgs e)
  502. {
  503. floatX = e.Event.X;
  504. floatY = e.Event.Y;
  505. width = e.Event.Width;
  506. height = e.Event.Height;
  507. e.RetVal = false;
  508. }
  509. private void OnFloatingDelete (object o, DeleteEventArgs e)
  510. {
  511. if (root != null)
  512. /* this will call reduce on ourselves, hiding
  513. the window if appropriate */
  514. ((DockItem)root).HideItem ();
  515. e.RetVal = true;
  516. }
  517. }
  518. }