PageRenderTime 31ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Core/PdnBaseForm.cs

https://bitbucket.org/tuldok89/openpdn
C# | 1039 lines | 782 code | 170 blank | 87 comment | 132 complexity | be56126cee1725101f213495c0a804ad MD5 | raw file
  1. /////////////////////////////////////////////////////////////////////////////////
  2. // Paint.NET //
  3. // Copyright (C) dotPDN LLC, Rick Brewster, Tom Jackson, and contributors. //
  4. // Portions Copyright (C) Microsoft Corporation. All Rights Reserved. //
  5. // See src/Resources/Files/License.txt for full licensing and attribution //
  6. // details. //
  7. // . //
  8. /////////////////////////////////////////////////////////////////////////////////
  9. using System.Linq;
  10. using PaintDotNet.Base;
  11. using PaintDotNet.SystemLayer;
  12. using System;
  13. using System.Drawing;
  14. using System.Collections.Generic;
  15. using System.ComponentModel;
  16. using System.Resources;
  17. using System.Threading;
  18. using System.Windows.Forms;
  19. namespace PaintDotNet
  20. {
  21. /// <summary>
  22. /// This Form class is used to fix a few bugs in Windows Forms, and to add a few performance
  23. /// enhancements, such as disabling opacity != 1.0 when running in a remote TS/RD session.
  24. /// We derive from this class instead of Windows.Forms.Form directly.
  25. /// </summary>
  26. public class PdnBaseForm
  27. : Form,
  28. ISnapManagerHost
  29. {
  30. static PdnBaseForm()
  31. {
  32. Application.EnterThreadModal += ApplicationEnterThreadModal;
  33. Application.LeaveThreadModal += ApplicationLeaveThreadModal;
  34. ApplicationEnterThreadModal(null, EventArgs.Empty);
  35. }
  36. // This set keeps track of forms which cannot be the current modal form.
  37. private static readonly Stack<List<Form>> ParentModalForms = new Stack<List<Form>>();
  38. private static bool IsInParentModalForms(Form form)
  39. {
  40. return ParentModalForms.Any(formList => formList.Any(parentModalForm => parentModalForm == form));
  41. }
  42. private static List<Form> GetAllPeerForms(Form form)
  43. {
  44. if (form == null)
  45. {
  46. return new List<Form>();
  47. }
  48. if (form.Owner != null)
  49. {
  50. return GetAllPeerForms(form.Owner);
  51. }
  52. var forms = new List<Form> {form};
  53. forms.AddRange(form.OwnedForms);
  54. return forms;
  55. }
  56. private static void ApplicationEnterThreadModal(object sender, EventArgs e)
  57. {
  58. Form activeForm = ActiveForm;
  59. List<Form> allPeerForms = GetAllPeerForms(activeForm);
  60. ParentModalForms.Push(allPeerForms);
  61. }
  62. private static void ApplicationLeaveThreadModal(object sender, EventArgs e)
  63. {
  64. ParentModalForms.Pop();
  65. }
  66. protected override void OnShown(EventArgs e)
  67. {
  68. IsShown = true;
  69. Tracing.LogFeature("ShowDialog(" + GetType().FullName + ")");
  70. base.OnShown(e);
  71. }
  72. public bool IsShown { get; private set; }
  73. private bool _enableOpacity = true;
  74. private double _ourOpacity = 1.0; // store opacity setting so that when we go from disabled->enabled opacity we can set the correct value
  75. private SnapManager _snapManager;
  76. private IContainer components;
  77. private bool _instanceEnableOpacity = true;
  78. private static bool _globalEnableOpacity = true;
  79. private readonly FormEx _formEx;
  80. private bool _processFormHotKeyMutex; // if we're already processing a form hotkey, don't let other hotkeys execute.
  81. private static Dictionary<Keys, Function<bool, Keys>> _hotkeyRegistrar;
  82. /// <summary>
  83. /// Registers a form-wide hot key, and a callback for when the key is pressed.
  84. /// The callback must be an instance method on a Control. Whatever Form the Control
  85. /// is on will process the hotkey, as long as the Form is derived from PdnBaseForm.
  86. /// </summary>
  87. public static void RegisterFormHotKey(Keys keys, Function<bool, Keys> callback)
  88. {
  89. var targetAsComponent = callback.Target as IComponent;
  90. var targetAsHotKeyTarget = callback.Target as IHotKeyTarget;
  91. if (targetAsComponent == null && targetAsHotKeyTarget == null)
  92. {
  93. throw new ArgumentException("target instance must implement IComponent or IHotKeyTarget", "callback");
  94. }
  95. if (_hotkeyRegistrar == null)
  96. {
  97. _hotkeyRegistrar = new Dictionary<Keys, Function<bool, Keys>>();
  98. }
  99. Function<bool, Keys> theDelegate;
  100. if (_hotkeyRegistrar.ContainsKey(keys))
  101. {
  102. theDelegate = _hotkeyRegistrar[keys];
  103. theDelegate += callback;
  104. _hotkeyRegistrar[keys] = theDelegate;
  105. }
  106. else
  107. {
  108. theDelegate = new Function<bool, Keys>(callback);
  109. _hotkeyRegistrar.Add(keys, theDelegate);
  110. }
  111. if (targetAsComponent != null)
  112. {
  113. targetAsComponent.Disposed += TargetAsComponentDisposed;
  114. }
  115. else
  116. {
  117. targetAsHotKeyTarget.Disposed += TargetAsHotKeyTargetDisposed;
  118. }
  119. }
  120. private static bool ShouldProcessHotKey(Keys keys)
  121. {
  122. Keys keyOnly = keys & ~Keys.Modifiers;
  123. if (keyOnly == Keys.Back ||
  124. keyOnly == Keys.Delete ||
  125. keyOnly == Keys.Left ||
  126. keyOnly == Keys.Right ||
  127. keyOnly == Keys.Up ||
  128. keyOnly == Keys.Down ||
  129. keys == (Keys.Control | Keys.A) || // select all
  130. keys == (Keys.Control | Keys.Z) || // undo
  131. keys == (Keys.Control | Keys.Y) || // redo
  132. keys == (Keys.Control | Keys.X) || // cut
  133. keys == (Keys.Control | Keys.C) || // copy
  134. keys == (Keys.Control | Keys.V) || // paste
  135. keys == (Keys.Shift | Keys.Delete) || // cut (left-handed)
  136. keys == (Keys.Control | Keys.Insert) || // copy (left-handed)
  137. keys == (Keys.Shift | Keys.Insert) // paste (left-handed)
  138. )
  139. {
  140. Control focused = Utility.FindFocus();
  141. if (focused is TextBox || focused is ComboBox || focused is UpDownBase)
  142. {
  143. return false;
  144. }
  145. }
  146. return true;
  147. }
  148. private static void TargetAsComponentDisposed(object sender, EventArgs e)
  149. {
  150. ((IComponent)sender).Disposed -= TargetAsComponentDisposed;
  151. RemoveDisposedTarget(sender);
  152. }
  153. private static void TargetAsHotKeyTargetDisposed(object sender, EventArgs e)
  154. {
  155. ((IHotKeyTarget)sender).Disposed -= TargetAsHotKeyTargetDisposed;
  156. RemoveDisposedTarget(sender);
  157. }
  158. static void RemoveDisposedTarget(object sender)
  159. {
  160. // Control was disposed, but it never unregistered for its hotkeys!
  161. var keysList = new List<Keys>(_hotkeyRegistrar.Keys);
  162. foreach (Keys keys in keysList)
  163. {
  164. Function<bool, Keys> theMultiDelegate = _hotkeyRegistrar[keys];
  165. foreach (Delegate theDelegate in
  166. theMultiDelegate.GetInvocationList().Where(theDelegate => ReferenceEquals(theDelegate.Target, sender)))
  167. {
  168. UnregisterFormHotKey(keys, (Function<bool, Keys>)theDelegate);
  169. }
  170. }
  171. }
  172. public static void UnregisterFormHotKey(Keys keys, Function<bool, Keys> callback)
  173. {
  174. if (_hotkeyRegistrar == null) return;
  175. Function<bool, Keys> theDelegate = _hotkeyRegistrar[keys];
  176. theDelegate -= callback;
  177. _hotkeyRegistrar[keys] = theDelegate;
  178. var targetAsComponent = callback.Target as IComponent;
  179. if (targetAsComponent != null)
  180. {
  181. targetAsComponent.Disposed -= TargetAsComponentDisposed;
  182. }
  183. var targetAsHotKeyTarget = callback.Target as IHotKeyTarget;
  184. if (targetAsHotKeyTarget != null)
  185. {
  186. targetAsHotKeyTarget.Disposed -= TargetAsHotKeyTargetDisposed;
  187. }
  188. if (theDelegate.GetInvocationList().Length == 0)
  189. {
  190. _hotkeyRegistrar.Remove(keys);
  191. }
  192. if (_hotkeyRegistrar.Count == 0)
  193. {
  194. _hotkeyRegistrar = null;
  195. }
  196. }
  197. public void Flash()
  198. {
  199. UI.FlashForm(this);
  200. }
  201. public void RestoreWindow()
  202. {
  203. if (WindowState == FormWindowState.Minimized)
  204. {
  205. UI.RestoreWindow(this);
  206. }
  207. }
  208. /// <summary>
  209. /// Returns the currently active modal form if the process is in the foreground and is active.
  210. /// </summary>
  211. /// <remarks>
  212. /// If Form.ActiveForm is modeless, we search up the chain of owner forms
  213. /// to find its modeless owner form.
  214. /// </remarks>
  215. public static Form CurrentModalForm
  216. {
  217. get
  218. {
  219. Form theForm = ActiveForm;
  220. while (theForm != null && !theForm.Modal && theForm.Owner != null)
  221. {
  222. theForm = theForm.Owner;
  223. }
  224. return theForm;
  225. }
  226. }
  227. /// <summary>
  228. /// Gets whether the current form is the processes' top level modal form.
  229. /// </summary>
  230. public bool IsCurrentModalForm
  231. {
  232. get
  233. {
  234. if (IsInParentModalForms(this))
  235. {
  236. return false;
  237. }
  238. if (ContainsFocus)
  239. {
  240. return true;
  241. }
  242. if (OwnedForms.Any(ownedForm => ownedForm.ContainsFocus))
  243. {
  244. return true;
  245. }
  246. return (this == CurrentModalForm);
  247. }
  248. }
  249. private static bool IsTargetFormActive(object target)
  250. {
  251. Control targetControl = null;
  252. {
  253. var asControl = target as Control;
  254. if (asControl != null)
  255. {
  256. targetControl = asControl;
  257. }
  258. }
  259. if (targetControl == null)
  260. {
  261. var asIFormAssociate = target as IFormAssociate;
  262. if (asIFormAssociate != null)
  263. {
  264. targetControl = asIFormAssociate.AssociatedForm;
  265. }
  266. }
  267. // target is not a control, or a type of non-control that we recognize as hosted by a control
  268. if (targetControl == null)
  269. {
  270. return false;
  271. }
  272. Form targetForm = targetControl.FindForm();
  273. // target is not on a form
  274. if (targetForm == null)
  275. {
  276. return false;
  277. }
  278. // is the target on the currently active form?
  279. Form activeModalForm = CurrentModalForm;
  280. return targetForm == activeModalForm;
  281. // Nope.
  282. }
  283. private static object GetConcreteTarget(object target)
  284. {
  285. var asDelegate = target as Delegate;
  286. return asDelegate == null ? target : GetConcreteTarget(asDelegate.Target);
  287. }
  288. private bool ProcessFormHotKey(Keys keyData)
  289. {
  290. bool processed = false;
  291. if (_processFormHotKeyMutex)
  292. {
  293. processed = true;
  294. }
  295. else
  296. {
  297. _processFormHotKeyMutex = true;
  298. try
  299. {
  300. if (_hotkeyRegistrar != null && _hotkeyRegistrar.ContainsKey(keyData))
  301. {
  302. Function<bool, Keys> theDelegate = _hotkeyRegistrar[keyData];
  303. Delegate[] invokeList = theDelegate.GetInvocationList();
  304. for (int i = invokeList.Length - 1; i >= 0; --i)
  305. {
  306. var invokeMe = (Function<bool, Keys>)invokeList[i];
  307. object concreteTarget = GetConcreteTarget(invokeMe.Target);
  308. if (!IsTargetFormActive(concreteTarget)) continue;
  309. bool result = invokeMe(keyData);
  310. if (!result) continue;
  311. // The callback handled the key.
  312. processed = true;
  313. break;
  314. }
  315. }
  316. }
  317. finally
  318. {
  319. _processFormHotKeyMutex = false;
  320. }
  321. }
  322. return processed;
  323. }
  324. private void OnProcessCmdKeyRelay(object sender, FormEx.ProcessCmdKeyEventArgs e)
  325. {
  326. bool handled = e.Handled;
  327. if (handled) return;
  328. handled = ProcessCmdKeyData(e.KeyData);
  329. e.Handled = handled;
  330. }
  331. public bool RelayProcessCmdKey(ref Message msg, Keys keyData)
  332. {
  333. return ProcessCmdKeyData(keyData);
  334. }
  335. protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
  336. {
  337. bool processed = ProcessCmdKeyData(keyData);
  338. if (!processed)
  339. {
  340. processed = base.ProcessCmdKey(ref msg, keyData);
  341. }
  342. return processed;
  343. }
  344. private bool ProcessCmdKeyData(Keys keyData)
  345. {
  346. bool shouldHandle = ShouldProcessHotKey(keyData);
  347. if (shouldHandle)
  348. {
  349. bool processed = ProcessFormHotKey(keyData);
  350. return processed;
  351. }
  352. return false;
  353. }
  354. public static void UpdateAllForms()
  355. {
  356. try
  357. {
  358. foreach (Form form in Application.OpenForms)
  359. {
  360. try
  361. {
  362. form.Update();
  363. }
  364. catch (Exception)
  365. {
  366. }
  367. }
  368. }
  369. catch (InvalidOperationException)
  370. {
  371. }
  372. }
  373. protected override void OnHelpRequested(HelpEventArgs hevent)
  374. {
  375. if (!hevent.Handled)
  376. {
  377. Utility.ShowHelp(this);
  378. hevent.Handled = true;
  379. }
  380. base.OnHelpRequested(hevent);
  381. }
  382. public static EventHandler EnableOpacityChanged;
  383. private static void OnEnableOpacityChanged()
  384. {
  385. if (EnableOpacityChanged != null)
  386. {
  387. EnableOpacityChanged(null, EventArgs.Empty);
  388. }
  389. }
  390. public bool EnableInstanceOpacity
  391. {
  392. get
  393. {
  394. return _instanceEnableOpacity;
  395. }
  396. set
  397. {
  398. _instanceEnableOpacity = value;
  399. DecideOpacitySetting();
  400. }
  401. }
  402. /// <summary>
  403. /// Gets or sets a flag that enables or disables opacity for all PdnBaseForm instances.
  404. /// If a particular form's EnableInstanceOpacity property is false, that will override
  405. /// this property being 'true'.
  406. /// </summary>
  407. public static bool EnableOpacity
  408. {
  409. get
  410. {
  411. return _globalEnableOpacity;
  412. }
  413. set
  414. {
  415. _globalEnableOpacity = value;
  416. OnEnableOpacityChanged();
  417. }
  418. }
  419. /// <summary>
  420. /// Gets or sets the titlebar rendering behavior for when the form is deactivated.
  421. /// </summary>
  422. /// <remarks>
  423. /// If this property is false, the titlebar will be rendered in a different color when the form
  424. /// is inactive as opposed to active. If this property is true, it will always render with the
  425. /// active style. If the whole application is deactivated, the title bar will still be drawn in
  426. /// an inactive state.
  427. /// </remarks>
  428. public bool ForceActiveTitleBar
  429. {
  430. get
  431. {
  432. return _formEx.ForceActiveTitleBar;
  433. }
  434. set
  435. {
  436. _formEx.ForceActiveTitleBar = value;
  437. }
  438. }
  439. private readonly ThreadPriority _originalPriority;
  440. protected override void OnScroll(ScrollEventArgs se)
  441. {
  442. Thread.CurrentThread.Priority = _originalPriority;
  443. base.OnScroll(se);
  444. }
  445. public PdnBaseForm()
  446. {
  447. _originalPriority = Thread.CurrentThread.Priority;
  448. Thread.CurrentThread.Priority = ThreadPriority.AboveNormal;
  449. UI.InitScaling(this);
  450. SuspendLayout();
  451. InitializeComponent();
  452. _formEx = new FormEx(this, RealWndProc);
  453. Controls.Add(_formEx);
  454. _formEx.Visible = false;
  455. DecideOpacitySetting();
  456. ResumeLayout(false);
  457. _formEx.ProcessCmdKeyRelay += OnProcessCmdKeyRelay;
  458. }
  459. protected override void OnLoad(EventArgs e)
  460. {
  461. if (!DesignMode)
  462. {
  463. LoadResources();
  464. }
  465. base.OnLoad(e);
  466. }
  467. public virtual void LoadResources()
  468. {
  469. if (DesignMode) return;
  470. string stringName = Name + ".Localized";
  471. string stringValue = StringsResourceManager.GetString(stringName);
  472. if (stringValue == null)
  473. {
  474. }
  475. else
  476. {
  477. try
  478. {
  479. bool boolValue = bool.Parse(stringValue);
  480. if (boolValue)
  481. {
  482. LoadLocalizedResources();
  483. }
  484. }
  485. catch (Exception)
  486. {
  487. }
  488. }
  489. }
  490. protected virtual ResourceManager StringsResourceManager
  491. {
  492. get
  493. {
  494. return PdnResources.Strings;
  495. }
  496. }
  497. private void LoadLocalizedResources()
  498. {
  499. LoadLocalizedResources(Name, this);
  500. }
  501. private static void ParsePair(string theString, out int x, out int y)
  502. {
  503. string[] split = theString.Split(',');
  504. x = int.Parse(split[0]);
  505. y = int.Parse(split[1]);
  506. }
  507. private void LoadLocalizedResources(string baseName, Control control)
  508. {
  509. // Text
  510. string textStringName = baseName + ".Text";
  511. string textString = StringsResourceManager.GetString(textStringName);
  512. if (textString != null)
  513. {
  514. control.Text = textString;
  515. }
  516. // Location
  517. string locationStringName = baseName + ".Location";
  518. string locationString = StringsResourceManager.GetString(locationStringName);
  519. if (locationString != null)
  520. {
  521. try
  522. {
  523. int x;
  524. int y;
  525. ParsePair(locationString, out x, out y);
  526. control.Location = new Point(x, y);
  527. }
  528. catch (Exception ex)
  529. {
  530. Tracing.Ping(locationStringName + " is invalid: " + locationString + ", exception: " + ex.ToString());
  531. }
  532. }
  533. // Size
  534. string sizeStringName = baseName + ".Size";
  535. string sizeString = StringsResourceManager.GetString(sizeStringName);
  536. if (sizeString != null)
  537. {
  538. try
  539. {
  540. int width;
  541. int height;
  542. ParsePair(sizeString, out width, out height);
  543. control.Size = new Size(width, height);
  544. }
  545. catch (Exception ex)
  546. {
  547. Tracing.Ping(sizeStringName + " is invalid: " + sizeString + ", exception: " + ex.ToString());
  548. }
  549. }
  550. // Recurse
  551. foreach (Control child in control.Controls)
  552. {
  553. if (child.Name == null || child.Name.Length > 0)
  554. {
  555. string newBaseName = baseName + "." + child.Name;
  556. LoadLocalizedResources(newBaseName, child);
  557. }
  558. else
  559. {
  560. Tracing.Ping("Name property not set for an instance of " + child.GetType().Name + " within " + baseName);
  561. }
  562. }
  563. }
  564. protected override void OnClosing(CancelEventArgs e)
  565. {
  566. base.OnClosing(e);
  567. if (!e.Cancel)
  568. {
  569. ForceActiveTitleBar = false;
  570. }
  571. }
  572. private void EnableOpacityChangedHandler(object sender, EventArgs e)
  573. {
  574. DecideOpacitySetting();
  575. }
  576. protected override void OnHandleCreated(EventArgs e)
  577. {
  578. base.OnHandleCreated (e);
  579. EnableOpacityChanged += EnableOpacityChangedHandler;
  580. UserSessions.SessionChanged += UserSessionsSessionChanged;
  581. DecideOpacitySetting();
  582. }
  583. protected override void OnHandleDestroyed(EventArgs e)
  584. {
  585. base.OnHandleDestroyed(e);
  586. EnableOpacityChanged -= EnableOpacityChangedHandler;
  587. UserSessions.SessionChanged -= UserSessionsSessionChanged;
  588. }
  589. /// <summary>
  590. /// Clean up any resources being used.
  591. /// </summary>
  592. protected override void Dispose(bool disposing)
  593. {
  594. if (disposing)
  595. {
  596. if (components != null)
  597. {
  598. components.Dispose();
  599. components = null;
  600. }
  601. }
  602. base.Dispose(disposing);
  603. }
  604. /// <summary>
  605. /// Sets the opacity of the form.
  606. /// </summary>
  607. /// <param name="newOpacity">The new opacity value.</param>
  608. /// <remarks>
  609. /// Depending on the system configuration, this request may be ignored. For example,
  610. /// when running within a Terminal Service (or Remote Desktop) session, opacity will
  611. /// always be set to 1.0 for performance reasons.
  612. /// </remarks>
  613. public new double Opacity
  614. {
  615. get
  616. {
  617. return _ourOpacity;
  618. }
  619. set
  620. {
  621. if (_enableOpacity)
  622. {
  623. // Bypassing Form.Opacity eliminates a "black flickering" that occurs when
  624. // the form transitions from Opacity=1.0 to Opacity != 1.0, or vice versa.
  625. // It appears to be a result of toggling the WS_EX_LAYERED style, or the
  626. // fact that Form.Opacity re-applies visual styles when this value transition
  627. // takes place.
  628. UI.SetFormOpacity(this, value);
  629. }
  630. _ourOpacity = value;
  631. }
  632. }
  633. /// <summary>
  634. /// Decides whether or not to have opacity be enabled.
  635. /// </summary>
  636. private void DecideOpacitySetting()
  637. {
  638. if (UserSessions.IsRemote || !_globalEnableOpacity || !EnableInstanceOpacity)
  639. {
  640. if (_enableOpacity)
  641. {
  642. try
  643. {
  644. UI.SetFormOpacity(this, 1.0);
  645. }
  646. // This fails in certain odd situations (bug #746), so we just eat the exception.
  647. catch (Win32Exception)
  648. {
  649. }
  650. }
  651. _enableOpacity = false;
  652. }
  653. else
  654. {
  655. if (!_enableOpacity)
  656. {
  657. // This fails in certain odd situations (bug #746), so we just eat the exception.
  658. try
  659. {
  660. UI.SetFormOpacity(this, _ourOpacity);
  661. }
  662. catch (Win32Exception)
  663. {
  664. }
  665. }
  666. _enableOpacity = true;
  667. }
  668. }
  669. public double ScreenAspect
  670. {
  671. get
  672. {
  673. Rectangle bounds = Screen.FromControl(this).Bounds;
  674. double aspect = (double)bounds.Width / (double)bounds.Height;
  675. return aspect;
  676. }
  677. }
  678. #region Windows Form Designer generated code
  679. /// <summary>
  680. /// Required method for Designer support - do not modify
  681. /// the contents of this method with the code editor.
  682. /// </summary>
  683. private void InitializeComponent()
  684. {
  685. this.components = new System.ComponentModel.Container();
  686. this.SuspendLayout();
  687. //
  688. // PdnBaseForm
  689. //
  690. this.AutoScaleDimensions = new SizeF(96F, 96F);
  691. this.AutoScaleMode = AutoScaleMode.Dpi;
  692. this.ClientSize = new System.Drawing.Size(291, 270);
  693. this.Name = "PdnBaseForm";
  694. this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
  695. this.Text = "PdnBaseForm";
  696. this.ResumeLayout(false);
  697. }
  698. #endregion
  699. public event MovingEventHandler Moving;
  700. protected virtual void OnMoving(MovingEventArgs mea)
  701. {
  702. if (Moving != null)
  703. {
  704. Moving(this, mea);
  705. }
  706. }
  707. public event CancelEventHandler QueryEndSession;
  708. protected virtual void OnQueryEndSession(CancelEventArgs e)
  709. {
  710. if (QueryEndSession != null)
  711. {
  712. QueryEndSession(this, e);
  713. }
  714. }
  715. private void UserSessionsSessionChanged(object sender, EventArgs e)
  716. {
  717. DecideOpacitySetting();
  718. }
  719. void RealWndProc(ref Message m)
  720. {
  721. OurWndProc(ref m);
  722. }
  723. protected override void WndProc(ref Message m)
  724. {
  725. if (_formEx == null)
  726. {
  727. base.WndProc(ref m);
  728. }
  729. else if (!_formEx.HandleParentWndProc(ref m))
  730. {
  731. OurWndProc(ref m);
  732. }
  733. }
  734. private void OurWndProc(ref Message m)
  735. {
  736. switch (m.Msg)
  737. {
  738. case 0x0216: // WM_MOVING
  739. unsafe
  740. {
  741. var p = (int *)m.LParam;
  742. Rectangle rect = Rectangle.FromLTRB(p[0], p[1], p[2], p[3]);
  743. var mea = new MovingEventArgs(rect);
  744. OnMoving(mea);
  745. p[0] = mea.Rectangle.Left;
  746. p[1] = mea.Rectangle.Top;
  747. p[2] = mea.Rectangle.Right;
  748. p[3] = mea.Rectangle.Bottom;
  749. m.Result = new IntPtr(1);
  750. }
  751. break;
  752. // WinForms doesn't handle this message correctly and wrongly returns 0 instead of 1.
  753. case 0x0011: // WM_QUERYENDSESSION
  754. var e = new CancelEventArgs();
  755. OnQueryEndSession(e);
  756. m.Result = e.Cancel ? IntPtr.Zero : new IntPtr(1);
  757. break;
  758. default:
  759. base.WndProc(ref m);
  760. break;
  761. }
  762. }
  763. public SnapManager SnapManager
  764. {
  765. get { return _snapManager ?? (_snapManager = new SnapManager()); }
  766. }
  767. public Size ClientSizeToWindowSize(Size clientSize)
  768. {
  769. Size baseClientSize = ClientSize;
  770. Size baseWindowSize = Size;
  771. int extraWidth = baseWindowSize.Width - baseClientSize.Width;
  772. int extraHeight = baseWindowSize.Height - baseClientSize.Height;
  773. var windowSize = new Size(clientSize.Width + extraWidth, clientSize.Height + extraHeight);
  774. return windowSize;
  775. }
  776. public Size WindowSizeToClientSize(Size windowSize)
  777. {
  778. Size baseClientSize = ClientSize;
  779. Size baseWindowSize = Size;
  780. int extraWidth = baseWindowSize.Width - baseClientSize.Width;
  781. int extraHeight = baseWindowSize.Height - baseClientSize.Height;
  782. var clientSize = new Size(windowSize.Width - extraWidth, windowSize.Height - extraHeight);
  783. return clientSize;
  784. }
  785. public Rectangle ClientBoundsToWindowBounds(Rectangle clientBounds)
  786. {
  787. Rectangle currentBounds = Bounds;
  788. Rectangle currentClientBounds = RectangleToScreen(ClientRectangle);
  789. var newWindowBounds = new Rectangle(
  790. clientBounds.Left - (currentClientBounds.Left - currentBounds.Left),
  791. clientBounds.Top - (currentClientBounds.Top - currentBounds.Top),
  792. clientBounds.Width + (currentBounds.Width - currentClientBounds.Width),
  793. clientBounds.Height + (currentBounds.Height - currentClientBounds.Height));
  794. return newWindowBounds;
  795. }
  796. public Rectangle WindowBoundsToClientBounds(Rectangle windowBounds)
  797. {
  798. Rectangle currentBounds = Bounds;
  799. Rectangle currentClientBounds = RectangleToScreen(ClientRectangle);
  800. var newClientBounds = new Rectangle(
  801. windowBounds.Left + (currentClientBounds.Left - currentBounds.Left),
  802. windowBounds.Top + (currentClientBounds.Top - currentBounds.Top),
  803. windowBounds.Width - (currentBounds.Width - currentClientBounds.Width),
  804. windowBounds.Height - (currentBounds.Height - currentClientBounds.Height));
  805. return newClientBounds;
  806. }
  807. public void EnsureFormIsOnScreen()
  808. {
  809. if (WindowState == FormWindowState.Maximized)
  810. {
  811. return;
  812. }
  813. if (WindowState == FormWindowState.Minimized)
  814. {
  815. return;
  816. }
  817. Screen ourScreen;
  818. try
  819. {
  820. ourScreen = Screen.FromControl(this);
  821. }
  822. catch (Exception)
  823. {
  824. ourScreen = null;
  825. }
  826. if (ourScreen == null)
  827. {
  828. ourScreen = Screen.PrimaryScreen;
  829. }
  830. Rectangle currentBounds = Bounds;
  831. Rectangle newBounds = EnsureRectIsOnScreen(ourScreen, currentBounds);
  832. Bounds = newBounds;
  833. }
  834. public static Rectangle EnsureRectIsOnScreen(Screen screen, Rectangle bounds)
  835. {
  836. Rectangle newBounds = bounds;
  837. Rectangle screenBounds = screen.WorkingArea;
  838. // Make sure the bottom and right do not fall off the edge, by moving the bounds
  839. if (newBounds.Right > screenBounds.Right)
  840. {
  841. newBounds.X -= (newBounds.Right - screenBounds.Right);
  842. }
  843. if (newBounds.Bottom > screenBounds.Bottom)
  844. {
  845. newBounds.Y -= (newBounds.Bottom - screenBounds.Bottom);
  846. }
  847. // Make sure the top and left haven't fallen off, by moving
  848. if (newBounds.Left < screenBounds.Left)
  849. {
  850. newBounds.X = screenBounds.Left;
  851. }
  852. if (newBounds.Top < screenBounds.Top)
  853. {
  854. newBounds.Y = screenBounds.Top;
  855. }
  856. // Make sure that we are not too wide / tall, by resizing
  857. if (newBounds.Right > screenBounds.Right)
  858. {
  859. newBounds.Width -= (newBounds.Right - screenBounds.Right);
  860. }
  861. if (newBounds.Bottom > screenBounds.Bottom)
  862. {
  863. newBounds.Height -= (newBounds.Bottom - screenBounds.Bottom);
  864. }
  865. // All done.
  866. return newBounds;
  867. }
  868. }
  869. }