DockPaned.cs 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  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.Xml;
  28. using Gtk;
  29. namespace Gdl
  30. {
  31. public class DockPaned : DockItem
  32. {
  33. private const float SplitRatio = 0.3f;
  34. bool positionChanged = false;
  35. protected DockPaned (IntPtr raw) : base (raw) { }
  36. public DockPaned () : this (Orientation.Horizontal)
  37. {
  38. // loading a layout from xml may need to change orientation later
  39. this.PropertyChanged += new PropertyChangedHandler (OnPropertyChanged);
  40. }
  41. public DockPaned (Orientation orientation)
  42. {
  43. this.Orientation = orientation;
  44. DockObjectFlags &= ~(DockObjectFlags.Automatic);
  45. CreateChild ();
  46. }
  47. public override bool HasGrip {
  48. get { return false; }
  49. }
  50. public override bool IsCompound {
  51. get { return true; }
  52. }
  53. [After]
  54. [Export]
  55. public int Position {
  56. get {
  57. if (Child != null && Child is Paned) {
  58. return ((Paned)Child).Position;
  59. }
  60. return 0;
  61. }
  62. set {
  63. if (Child != null && Child is Paned) {
  64. ((Paned)Child).Position = value;
  65. }
  66. }
  67. }
  68. private void CreateChild ()
  69. {
  70. if (Child != null)
  71. Child.Unparent ();
  72. /* create the container paned */
  73. if (this.Orientation == Orientation.Horizontal)
  74. Child = new HPaned ();
  75. else
  76. Child = new VPaned ();
  77. Child.AddNotification ("position", new GLib.NotifyHandler (OnNotifyPosition));
  78. Child.ButtonReleaseEvent += new ButtonReleaseEventHandler (OnButtonReleased);
  79. Child.KeyPressEvent += new KeyPressEventHandler (OnKeyPressed);
  80. Child.Parent = this;
  81. Child.Show ();
  82. }
  83. protected override void OnAdded (Widget widget)
  84. {
  85. if (Child == null)
  86. return;
  87. Paned paned = Child as Paned;
  88. if (paned.Child1 != null && paned.Child2 != null)
  89. return;
  90. DockItem item = widget as DockItem;
  91. DockPlacement pos = DockPlacement.None;
  92. if (paned.Child1 == null)
  93. pos = (Orientation == Orientation.Horizontal ?
  94. DockPlacement.Left : DockPlacement.Top);
  95. else
  96. pos = (Orientation == Orientation.Horizontal ?
  97. DockPlacement.Right : DockPlacement.Bottom);
  98. if (pos != DockPlacement.None)
  99. Dock (item, pos, null);
  100. }
  101. public override bool OnChildPlacement (DockObject child, ref DockPlacement placement)
  102. {
  103. DockPlacement pos = DockPlacement.None;
  104. if (this.Child != null) {
  105. Paned paned = this.Child as Paned;
  106. if (child == paned.Child1)
  107. pos = this.Orientation == Orientation.Horizontal ? DockPlacement.Left : DockPlacement.Top;
  108. else if (child == paned.Child2)
  109. pos = this.Orientation == Orientation.Horizontal ? DockPlacement.Right : DockPlacement.Bottom;
  110. }
  111. if (pos != DockPlacement.None) {
  112. placement = pos;
  113. return true;
  114. }
  115. return base.OnChildPlacement (child, ref pos);
  116. }
  117. protected override void OnDestroyed ()
  118. {
  119. // this first
  120. base.OnDestroyed ();
  121. // after that we can remove the Paned child
  122. if (Child != null) {
  123. Child.ButtonReleaseEvent -= new ButtonReleaseEventHandler (OnButtonReleased);
  124. Child.KeyPressEvent -= new KeyPressEventHandler (OnKeyPressed);
  125. Child.Unparent ();
  126. Child = null;
  127. }
  128. }
  129. protected override void ForAll (bool include_internals, Callback cb)
  130. {
  131. if (include_internals) {
  132. base.ForAll (include_internals, cb);
  133. } else {
  134. if (Child != null) {
  135. ((Paned)Child).Foreach (cb);
  136. }
  137. }
  138. }
  139. public override void OnDocked (DockObject requestor, DockPlacement position, object data)
  140. {
  141. if (Child == null)
  142. return;
  143. Paned paned = (Paned)Child;
  144. bool done = false;
  145. /* see if we can dock the item in our paned */
  146. switch (Orientation) {
  147. case Orientation.Horizontal:
  148. if (paned.Child1 == null && position == DockPlacement.Left) {
  149. paned.Pack1 (requestor, false, false);
  150. done = true;
  151. } else if (paned.Child2 == null && position == DockPlacement.Right) {
  152. paned.Pack2 (requestor, true, true);
  153. done = true;
  154. }
  155. break;
  156. case Orientation.Vertical:
  157. if (paned.Child1 == null && position == DockPlacement.Top) {
  158. paned.Pack1 (requestor, true, true);
  159. done = true;
  160. } else if (paned.Child2 == null && position == DockPlacement.Bottom) {
  161. paned.Pack2 (requestor, false, false);
  162. done = true;
  163. }
  164. break;
  165. }
  166. if (!done) {
  167. /* this will create another paned and reparent us there */
  168. base.OnDocked (requestor, position, data);
  169. } else {
  170. ((DockItem)requestor).ShowGrip ();
  171. requestor.DockObjectFlags |= DockObjectFlags.Attached;
  172. }
  173. }
  174. public override bool OnDockRequest (int x, int y, ref DockRequest request)
  175. {
  176. bool mayDock = false;
  177. /* we get (x,y) in our allocation coordinates system */
  178. /* Get item's allocation. */
  179. Gdk.Rectangle alloc = Allocation;
  180. int bw = (int)BorderWidth;
  181. /* Get coordinates relative to our window. */
  182. int relX = x - alloc.X;
  183. int relY = y - alloc.Y;
  184. /* Check if coordinates are inside the widget. */
  185. if (relX > 0 && relX < alloc.Width &&
  186. relY > 0 && relY < alloc.Height) {
  187. int divider = -1;
  188. /* It's inside our area. */
  189. mayDock = true;
  190. /* these are for calculating the extra docking parameter */
  191. Requisition other = ((DockItem)request.Applicant).PreferredSize;
  192. Requisition my = PreferredSize;
  193. /* Set docking indicator rectangle to the Dock size. */
  194. request.X = bw;
  195. request.Y = bw;
  196. request.Width = alloc.Width - 2 * bw;
  197. request.Height = alloc.Height - 2 * bw;
  198. request.Target = this;
  199. /* See if it's in the BorderWidth band. */
  200. if (relX < bw) {
  201. request.Position = DockPlacement.Left;
  202. request.Width = (int)(request.Width * SplitRatio);
  203. divider = other.Width;
  204. } else if (relX > alloc.Width - bw) {
  205. request.Position = DockPlacement.Right;
  206. request.X += (int)(request.Width * (1 - SplitRatio));
  207. request.Width = (int)(request.Width * SplitRatio);
  208. divider = Math.Max (0, my.Width - other.Width);
  209. } else if (relY < bw) {
  210. request.Position = DockPlacement.Top;
  211. request.Height = (int)(request.Height * SplitRatio);
  212. divider = other.Height;
  213. } else if (relY > alloc.Height - bw) {
  214. request.Position = DockPlacement.Bottom;
  215. request.Y += (int)(request.Height * (1 - SplitRatio));
  216. request.Height = (int)(request.Height * SplitRatio);
  217. divider = Math.Max (0, my.Height - other.Height);
  218. } else { /* Otherwise try our children. */
  219. mayDock = false;
  220. DockRequest myRequest = new DockRequest (request);
  221. foreach (DockObject item in Children) {
  222. if (item.OnDockRequest (relX, relY, ref myRequest)) {
  223. mayDock = true;
  224. request = myRequest;
  225. break;
  226. }
  227. }
  228. if (!mayDock) {
  229. /* the pointer is on the handle, so snap
  230. to top/bottom or left/right */
  231. mayDock = true;
  232. if (Orientation == Orientation.Horizontal) {
  233. if (relY < alloc.Height / 2) {
  234. request.Position = DockPlacement.Top;
  235. request.Height = (int)(request.Height * SplitRatio);
  236. divider = other.Height;
  237. } else {
  238. request.Position = DockPlacement.Bottom;
  239. request.Y += (int)(request.Height * (1 - SplitRatio));
  240. request.Height = (int)(request.Height * SplitRatio);
  241. divider = Math.Max (0, my.Height - other.Height);
  242. }
  243. } else {
  244. if (relX < alloc.Width / 2) {
  245. request.Position = DockPlacement.Left;
  246. request.Width = (int)(request.Width * SplitRatio);
  247. divider = other.Width;
  248. } else {
  249. request.Position = DockPlacement.Right;
  250. request.X += (int)(request.Width * (1 - SplitRatio));
  251. request.Width = (int)(request.Width * SplitRatio);
  252. divider = Math.Max (0, my.Width - other.Width);
  253. }
  254. }
  255. }
  256. }
  257. if (divider >= 0 && request.Position != DockPlacement.Center)
  258. request.Extra = divider;
  259. if (mayDock) {
  260. /* adjust returned coordinates so they are
  261. relative to our allocation */
  262. request.X += alloc.X;
  263. request.Y += alloc.Y;
  264. }
  265. }
  266. return mayDock;
  267. }
  268. void OnNotifyPosition (object sender, GLib.NotifyArgs a)
  269. {
  270. positionChanged = true;
  271. }
  272. [GLib.ConnectBefore]
  273. void OnKeyPressed (object sender, KeyPressEventArgs a)
  274. {
  275. // eat Shift|F8, see http://bugzilla.ximian.com/show_bug.cgi?id=61113
  276. if (a.Event.Key == Gdk.Key.F8 && a.Event.State == Gdk.ModifierType.ShiftMask)
  277. a.RetVal = true;
  278. }
  279. [GLib.ConnectBefore]
  280. void OnButtonReleased (object sender, ButtonReleaseEventArgs a)
  281. {
  282. if (a.Event.Button == 1 && positionChanged) {
  283. Master.EmitLayoutChangedEvent ();
  284. positionChanged = false;
  285. }
  286. }
  287. void OnPropertyChanged (object sender, string name)
  288. {
  289. if (name == "orientation") {
  290. CreateChild ();
  291. this.PropertyChanged -= new PropertyChangedHandler (OnPropertyChanged);
  292. }
  293. }
  294. }
  295. }