// Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // // Copyright (c) 2005 Novell, Inc. (http://www.novell.com) // // Authors: // Jackson Harper (jackson@ximian.com) // // using System; using System.Drawing; using System.Runtime.InteropServices; namespace System.Windows.Forms { internal abstract class InternalWindowManager { private TitleButtons title_buttons; internal Form form; // moving windows internal Point start; internal State state; protected Point clicked_point; private FormPos sizing_edge; internal Rectangle virtual_position; private Rectangle normal_bounds; private Rectangle iconic_bounds; public enum State { Idle, Moving, Sizing, } [Flags] public enum FormPos { None, TitleBar = 1, Top = 2, Left = 4, Right = 8, Bottom = 16, TopLeft = Top | Left, TopRight = Top | Right, BottomLeft = Bottom | Left, BottomRight = Bottom | Right, AnyEdge = Top | Left | Right | Bottom, } public InternalWindowManager (Form form) { this.form = form; form.SizeChanged += new EventHandler (FormSizeChangedHandler); title_buttons = new TitleButtons (form); ThemeEngine.Current.ManagedWindowSetButtonLocations (this); } public Form Form { get { return form; } } public int IconWidth { get { return TitleBarHeight - 5; } } public TitleButtons TitleButtons { get { return title_buttons; } } internal Rectangle NormalBounds { get { return normal_bounds; } set { normal_bounds = value; } } internal Size IconicSize { get { return SystemInformation.MinimizedWindowSize; } } internal Rectangle IconicBounds { get { if (iconic_bounds == Rectangle.Empty) return Rectangle.Empty; Rectangle result = iconic_bounds; result.Y = Form.Parent.ClientRectangle.Bottom - iconic_bounds.Y; return result; } set { iconic_bounds = value; iconic_bounds.Y = Form.Parent.ClientRectangle.Bottom - iconic_bounds.Y; } } internal virtual Rectangle MaximizedBounds { get { return Form.Parent.ClientRectangle; } } public virtual void UpdateWindowState (FormWindowState old_window_state, FormWindowState new_window_state, bool force) { if (old_window_state == FormWindowState.Normal) { NormalBounds = form.Bounds; } else if (old_window_state == FormWindowState.Minimized) { IconicBounds = form.Bounds; } switch (new_window_state) { case FormWindowState.Minimized: if (IconicBounds == Rectangle.Empty) { Size size = IconicSize; Point location = new Point (0, Form.Parent.ClientSize.Height - size.Height); IconicBounds = new Rectangle (location, size); } form.Bounds = IconicBounds; break; case FormWindowState.Maximized: form.Bounds = MaximizedBounds; break; case FormWindowState.Normal: form.Bounds = NormalBounds; break; } UpdateWindowDecorations (new_window_state); form.ResetCursor (); } public virtual void UpdateWindowDecorations (FormWindowState window_state) { ThemeEngine.Current.ManagedWindowSetButtonLocations (this); if (form.IsHandleCreated) XplatUI.RequestNCRecalc (form.Handle); } public virtual bool WndProc (ref Message m) { #if debug Console.WriteLine(DateTime.Now.ToLongTimeString () + " " + this.GetType () .Name + " (Handle={0},Text={1}) received message {2}", form.IsHandleCreated ? form.Handle : IntPtr.Zero, form.Text, m.ToString ()); #endif switch ((Msg)m.Msg) { // The mouse handling messages are actually // not WM_NC* messages except for the first button and NCMOVEs // down because we capture on the form case Msg.WM_MOUSEMOVE: return HandleMouseMove (form, ref m); case Msg.WM_LBUTTONUP: HandleLButtonUp (ref m); break; case Msg.WM_RBUTTONDOWN: return HandleRButtonDown (ref m); case Msg.WM_LBUTTONDOWN: return HandleLButtonDown (ref m); case Msg.WM_LBUTTONDBLCLK: return HandleLButtonDblClick (ref m); case Msg.WM_PARENTNOTIFY: if (Control.LowOrder(m.WParam.ToInt32()) == (int) Msg.WM_LBUTTONDOWN) Activate (); break; case Msg.WM_NCHITTEST: return HandleNCHitTest (ref m); // Return true from these guys, otherwise win32 will mess up z-order case Msg.WM_NCLBUTTONUP: HandleNCLButtonUp (ref m); return true; case Msg.WM_NCLBUTTONDOWN: HandleNCLButtonDown (ref m); return true; case Msg.WM_NCMOUSEMOVE: HandleNCMouseMove (ref m); return true; case Msg.WM_NCLBUTTONDBLCLK: HandleNCLButtonDblClick (ref m); break; case Msg.WM_NCMOUSELEAVE: HandleNCMouseLeave (ref m); break; case Msg.WM_MOUSELEAVE: HandleMouseLeave (ref m); break; case Msg.WM_NCCALCSIZE: return HandleNCCalcSize (ref m); case Msg.WM_NCPAINT: return HandleNCPaint (ref m); } return false; } protected virtual bool HandleNCPaint (ref Message m) { PaintEventArgs pe = XplatUI.PaintEventStart (ref m, form.Handle, false); Rectangle clip; if (form.ActiveMenu != null) { Point pnt; pnt = GetMenuOrigin (); // The entire menu has to be in the clip rectangle because the // control buttons are right-aligned and otherwise they would // stay painted when the window gets resized. clip = new Rectangle (pnt.X, pnt.Y, form.ClientSize.Width, 0); clip = Rectangle.Union (clip, pe.ClipRectangle); pe.SetClip (clip); pe.Graphics.SetClip (clip); form.ActiveMenu.Draw (pe, new Rectangle (pnt.X, pnt.Y, form.ClientSize.Width, 0)); } if (HasBorders || IsMinimized && !(Form.IsMdiChild && IsMaximized)) { // clip region is not correct on win32. // use the entire form's area. clip = new Rectangle (0, 0, form.Width, form.Height); ThemeEngine.Current.DrawManagedWindowDecorations (pe.Graphics, clip, this); } XplatUI.PaintEventEnd (ref m, form.Handle, false); return true; } protected virtual bool HandleNCCalcSize (ref Message m) { XplatUIWin32.NCCALCSIZE_PARAMS ncp; XplatUIWin32.RECT rect; if (m.WParam == (IntPtr)1) { ncp = (XplatUIWin32.NCCALCSIZE_PARAMS)Marshal.PtrToStructure (m.LParam, typeof (XplatUIWin32.NCCALCSIZE_PARAMS)); ncp.rgrc1 = NCCalcSize (ncp.rgrc1); Marshal.StructureToPtr (ncp, m.LParam, true); } else { rect = (XplatUIWin32.RECT) Marshal.PtrToStructure (m.LParam, typeof (XplatUIWin32.RECT)); rect = NCCalcSize (rect); Marshal.StructureToPtr (rect, m.LParam, true); } return true; } protected virtual XplatUIWin32.RECT NCCalcSize (XplatUIWin32.RECT proposed_window_rect) { int bw = ThemeEngine.Current.ManagedWindowBorderWidth (this); if (HasBorders) { proposed_window_rect.top += TitleBarHeight + bw; proposed_window_rect.bottom -= bw; proposed_window_rect.left += bw; proposed_window_rect.right -= bw; } if (XplatUI.RequiresPositiveClientAreaSize) { // This is necessary for Linux, can't handle <= 0-sized // client areas correctly. if (proposed_window_rect.right <= proposed_window_rect.left) { proposed_window_rect.right += proposed_window_rect.left - proposed_window_rect.right + 1; } if (proposed_window_rect.top >= proposed_window_rect.bottom) { proposed_window_rect.bottom += proposed_window_rect.top - proposed_window_rect.bottom + 1; } } return proposed_window_rect; } protected virtual bool HandleNCHitTest (ref Message m) { int x = Control.LowOrder ((int)m.LParam.ToInt32 ()); int y = Control.HighOrder ((int)m.LParam.ToInt32 ()); NCPointToClient (ref x, ref y); FormPos pos = FormPosForCoords (x, y); if (pos == FormPos.TitleBar) { m.Result = new IntPtr ((int)HitTest.HTCAPTION); return true; } if (!IsSizable) return false; switch (pos) { case FormPos.Top: m.Result = new IntPtr ((int)HitTest.HTTOP); break; case FormPos.Left: m.Result = new IntPtr ((int)HitTest.HTLEFT); break; case FormPos.Right: m.Result = new IntPtr ((int)HitTest.HTRIGHT); break; case FormPos.Bottom: m.Result = new IntPtr ((int)HitTest.HTBOTTOM); break; case FormPos.TopLeft: m.Result = new IntPtr ((int)HitTest.HTTOPLEFT); break; case FormPos.TopRight: m.Result = new IntPtr ((int)HitTest.HTTOPRIGHT); break; case FormPos.BottomLeft: m.Result = new IntPtr ((int)HitTest.HTBOTTOMLEFT); break; case FormPos.BottomRight: m.Result = new IntPtr ((int)HitTest.HTBOTTOMRIGHT); break; default: // We return false so that DefWndProc handles things return false; } return true; } public virtual void UpdateBorderStyle (FormBorderStyle border_style) { if (form.IsHandleCreated) { XplatUI.SetBorderStyle (form.Handle, border_style); } if (ShouldRemoveWindowManager (border_style)) { form.RemoveWindowManager (); return; } ThemeEngine.Current.ManagedWindowSetButtonLocations (this); } public virtual void SetWindowState (FormWindowState old_state, FormWindowState window_state) { UpdateWindowState (old_state, window_state, false); } public virtual FormWindowState GetWindowState () { return form.window_state; } public virtual void PointToClient (ref int x, ref int y) { // toolwindows stay in screencoords we just have to make sure // they obey the working area Rectangle working = SystemInformation.WorkingArea; if (x > working.Right) x = working.Right; if (x < working.Left) x = working.Left; if (y < working.Top) y = working.Top; if (y > working.Bottom) y = working.Bottom; } public virtual void PointToScreen (ref int x, ref int y) { XplatUI.ClientToScreen (form.Handle, ref x, ref y); } protected virtual bool ShouldRemoveWindowManager (FormBorderStyle style) { return style != FormBorderStyle.FixedToolWindow && style != FormBorderStyle.SizableToolWindow; } public bool IconRectangleContains (int x, int y) { if (!ShowIcon) return false; Rectangle icon = ThemeEngine.Current.ManagedWindowGetTitleBarIconArea (this); return icon.Contains (x, y); } public bool ShowIcon { get { if (!Form.ShowIcon) return false; if (!HasBorders) return false; if (IsMinimized) return true; if (IsToolWindow || Form.FormBorderStyle == FormBorderStyle.FixedDialog) return false; return true; } } protected virtual void Activate () { form.Invalidate (true); form.Update (); } public virtual bool IsActive { get { return true; } } private void FormSizeChangedHandler (object sender, EventArgs e) { if (form.IsHandleCreated) { ThemeEngine.Current.ManagedWindowSetButtonLocations (this); XplatUI.InvalidateNC (form.Handle); } } protected virtual bool HandleRButtonDown (ref Message m) { Activate (); return false; } protected virtual bool HandleLButtonDown (ref Message m) { Activate (); return false; } protected virtual bool HandleLButtonDblClick(ref Message m) { return false; } protected virtual bool HandleNCMouseLeave (ref Message m) { int x = Control.LowOrder ((int)m.LParam.ToInt32 ()); int y = Control.HighOrder ((int)m.LParam.ToInt32 ()); NCPointToClient (ref x, ref y); FormPos pos = FormPosForCoords (x, y); if (pos != FormPos.TitleBar) { HandleTitleBarLeave (x, y); return true; } return true; } protected virtual bool HandleNCMouseMove (ref Message m) { int x = Control.LowOrder((int)m.LParam.ToInt32( )); int y = Control.HighOrder((int)m.LParam.ToInt32( )); NCPointToClient (ref x, ref y); FormPos pos = FormPosForCoords (x, y); if (pos == FormPos.TitleBar) { HandleTitleBarMouseMove (x, y); return true; } if (form.ActiveMenu != null && XplatUI.IsEnabled (form.Handle)) { MouseEventArgs mea = new MouseEventArgs (Form.FromParamToMouseButtons (m.WParam.ToInt32 ()), form.mouse_clicks, x, y, 0); form.ActiveMenu.OnMouseMove (form, mea); } return true; } protected virtual bool HandleNCLButtonDown (ref Message m) { Activate (); start = Cursor.Position; virtual_position = form.Bounds; int x = Control.LowOrder ((int) m.LParam.ToInt32 ()); int y = Control.HighOrder ((int) m.LParam.ToInt32 ()); // Need to adjust because we are in NC land NCPointToClient (ref x, ref y); FormPos pos = FormPosForCoords (x, y); if (form.ActiveMenu != null && XplatUI.IsEnabled (form.Handle)) { MouseEventArgs mea = new MouseEventArgs (Form.FromParamToMouseButtons (m.WParam.ToInt32 ()), form.mouse_clicks, x, y - TitleBarHeight, 0); form.ActiveMenu.OnMouseDown (form, mea); } if (pos == FormPos.TitleBar) { HandleTitleBarDown (x, y); return true; } if (IsSizable) { if ((pos & FormPos.AnyEdge) == 0) return false; virtual_position = form.Bounds; state = State.Sizing; sizing_edge = pos; form.Capture = true; return true; } return false; } protected virtual void HandleNCLButtonDblClick (ref Message m) { int x = Control.LowOrder ((int)m.LParam.ToInt32 ()); int y = Control.HighOrder ((int)m.LParam.ToInt32 ()); // Need to adjust because we are in NC land NCPointToClient (ref x, ref y); FormPos pos = FormPosForCoords (x, y); if (pos == FormPos.TitleBar || pos == FormPos.Top) HandleTitleBarDoubleClick (x, y); } protected virtual void HandleTitleBarDoubleClick (int x, int y) { } protected virtual void HandleTitleBarLeave (int x, int y) { title_buttons.MouseLeave (x, y); } protected virtual void HandleTitleBarMouseMove (int x, int y) { if (title_buttons.MouseMove (x, y)) XplatUI.InvalidateNC (form.Handle); } protected virtual void HandleTitleBarUp (int x, int y) { title_buttons.MouseUp (x, y); return; } protected virtual void HandleTitleBarDown (int x, int y) { title_buttons.MouseDown (x, y); if (!TitleButtons.AnyPushedTitleButtons && !IsMaximized) { state = State.Moving; clicked_point = new Point (x, y); if (form.Parent != null) { form.CaptureWithConfine (form.Parent); } else { form.Capture = true; } } XplatUI.InvalidateNC (form.Handle); } private bool HandleMouseMove (Form form, ref Message m) { switch (state) { case State.Moving: HandleWindowMove (m); return true; case State.Sizing: HandleSizing (m); return true; } return false; } private void HandleMouseLeave (ref Message m) { form.ResetCursor (); } protected virtual void HandleWindowMove (Message m) { Point move = MouseMove (Cursor.Position); UpdateVP (virtual_position.X + move.X, virtual_position.Y + move.Y, virtual_position.Width, virtual_position.Height); } private void HandleSizing (Message m) { Rectangle pos = virtual_position; int mw; int mh; if (IsToolWindow) { int border_width = BorderWidth; mw = 2 * (border_width + Theme.ManagedWindowSpacingAfterLastTitleButton) + ThemeEngine.Current.ManagedWindowButtonSize (this).Width; mh = 2 * border_width + TitleBarHeight; } else { Size minimum_size = SystemInformation.MinWindowTrackSize; mw = minimum_size.Width; mh = minimum_size.Height; } int x = Cursor.Position.X; int y = Cursor.Position.Y; PointToClient (ref x, ref y); if ((sizing_edge & FormPos.Top) != 0) { if (pos.Bottom - y < mh) y = pos.Bottom - mh; pos.Height = pos.Bottom - y; pos.Y = y; } else if ((sizing_edge & FormPos.Bottom) != 0) { int height = y - pos.Top; if (height <= mh) height = mh; pos.Height = height; } if ((sizing_edge & FormPos.Left) != 0) { if (pos.Right - x < mw) x = pos.Right - mw; pos.Width = pos.Right - x; pos.X = x; } else if ((sizing_edge & FormPos.Right) != 0) { int width = x - form.Left; if (width <= mw) width = mw; pos.Width = width; } UpdateVP (pos); } public bool IsMaximized { get { return GetWindowState () == FormWindowState.Maximized; } } public bool IsMinimized { get { return GetWindowState () == FormWindowState.Minimized; } } public bool IsSizable { get { switch (form.FormBorderStyle) { case FormBorderStyle.Sizable: case FormBorderStyle.SizableToolWindow: return (form.window_state != FormWindowState.Minimized); default: return false; } } } public bool HasBorders { get { return form.FormBorderStyle != FormBorderStyle.None; } } public bool IsToolWindow { get { if (form.FormBorderStyle == FormBorderStyle.SizableToolWindow || form.FormBorderStyle == FormBorderStyle.FixedToolWindow || form.GetCreateParams().IsSet (WindowExStyles.WS_EX_TOOLWINDOW)) return true; return false; } } public int TitleBarHeight { get { return ThemeEngine.Current.ManagedWindowTitleBarHeight (this); } } public int BorderWidth { get { return ThemeEngine.Current.ManagedWindowBorderWidth (this); } } public virtual int MenuHeight { get { return (form.Menu != null ? ThemeEngine.Current.MenuHeight : 0); } } protected void UpdateVP (Rectangle r) { UpdateVP (r.X, r.Y, r.Width, r.Height); } protected void UpdateVP (Point loc, int w, int h) { UpdateVP (loc.X, loc.Y, w, h); } protected void UpdateVP (int x, int y, int w, int h) { virtual_position.X = x; virtual_position.Y = y; virtual_position.Width = w; virtual_position.Height = h; DrawVirtualPosition (virtual_position); } protected virtual void HandleLButtonUp (ref Message m) { if (state == State.Idle) return; ClearVirtualPosition (); form.Capture = false; if (state == State.Moving && form.Location != virtual_position.Location) form.Location = virtual_position.Location; else if (state == State.Sizing && form.Bounds != virtual_position) form.Bounds = virtual_position; state = State.Idle; OnWindowFinishedMoving (); } private bool HandleNCLButtonUp (ref Message m) { if (form.Capture) { ClearVirtualPosition (); form.Capture = false; state = State.Idle; if (form.MdiContainer != null) form.MdiContainer.SizeScrollBars(); } int x = Control.LowOrder ((int) m.LParam.ToInt32 ()); int y = Control.HighOrder ((int) m.LParam.ToInt32 ()); NCPointToClient (ref x, ref y); FormPos pos = FormPosForCoords (x, y); if (pos == FormPos.TitleBar) { HandleTitleBarUp (x, y); return true; } return true; } protected void DrawTitleButton (Graphics dc, TitleButton button, Rectangle clip) { if (!button.Rectangle.IntersectsWith (clip)) return; ThemeEngine.Current.ManagedWindowDrawMenuButton (dc, button, clip, this); } public virtual void DrawMaximizedButtons (object sender, PaintEventArgs pe) { } protected Point MouseMove (Point pos) { return new Point (pos.X - start.X, pos.Y - start.Y); } protected virtual void DrawVirtualPosition (Rectangle virtual_position) { form.Bounds = virtual_position; start = Cursor.Position; } protected virtual void ClearVirtualPosition () { } protected virtual void OnWindowFinishedMoving () { } protected virtual void NCPointToClient(ref int x, ref int y) { form.PointToClient(ref x, ref y); NCClientToNC (ref x, ref y); } protected virtual void NCClientToNC (ref int x, ref int y) { y += TitleBarHeight; y += BorderWidth; y += MenuHeight; } internal Point GetMenuOrigin () { return new Point (BorderWidth, BorderWidth + TitleBarHeight); } protected FormPos FormPosForCoords (int x, int y) { int bw = BorderWidth; if (y < TitleBarHeight + bw) { // Console.WriteLine ("A"); if (y > bw && x > bw && x < form.Width - bw) return FormPos.TitleBar; if (x < bw || (x < 20 && y < bw)) return FormPos.TopLeft; if (x > form.Width - bw || (x > form.Width - 20 && y < bw)) return FormPos.TopRight; if (y < bw) return FormPos.Top; } else if (y > form.Height - 20) { // Console.WriteLine ("B"); if (x < bw || (x < 20 && y > form.Height - bw)) return FormPos.BottomLeft; if (x > form.Width - (bw * 2) || (x > form.Width - 20 && y > form.Height - bw)) return FormPos.BottomRight; if (y > form.Height - (bw * 2)) return FormPos.Bottom; } else if (x < bw) { // Console.WriteLine ("C"); return FormPos.Left; } else if (x > form.Width - (bw * 2)) { // Console.WriteLine ("D"); return FormPos.Right; } else { // Console.WriteLine ("E {0}", form.Width - bw); } return FormPos.None; } } internal class TitleButton { public Rectangle Rectangle; public ButtonState State; public CaptionButton Caption; private EventHandler Clicked; public bool Visible; bool entered; public TitleButton (CaptionButton caption, EventHandler clicked) { Caption = caption; Clicked = clicked; } public void OnClick () { if (Clicked != null) { Clicked (this, EventArgs.Empty); } } public bool Entered { get { return entered; } set { entered = value; } } } internal class TitleButtons : System.Collections.IEnumerable { public TitleButton MinimizeButton; public TitleButton MaximizeButton; public TitleButton RestoreButton; public TitleButton CloseButton; public TitleButton HelpButton; public TitleButton [] AllButtons; public bool Visible; private ToolTip.ToolTipWindow tooltip; private Timer tooltip_timer; private TitleButton tooltip_hovered_button; private TitleButton tooltip_hidden_button; private const int tooltip_hide_interval = 3000; private const int tooltip_show_interval = 1000; private Form form; public TitleButtons (Form frm) { this.form = frm; this.Visible = true; MinimizeButton = new TitleButton (CaptionButton.Minimize, new EventHandler (ClickHandler)); MaximizeButton = new TitleButton (CaptionButton.Maximize, new EventHandler (ClickHandler)); RestoreButton = new TitleButton (CaptionButton.Restore, new EventHandler (ClickHandler)); CloseButton = new TitleButton (CaptionButton.Close, new EventHandler (ClickHandler)); HelpButton = new TitleButton (CaptionButton.Help, new EventHandler (ClickHandler)); AllButtons = new TitleButton [] { MinimizeButton, MaximizeButton, RestoreButton, CloseButton, HelpButton }; } private void ClickHandler (object sender, EventArgs e) { if (!Visible) { return; } TitleButton button = (TitleButton) sender; switch (button.Caption) { case CaptionButton.Close: form.Close (); break; case CaptionButton.Help: Console.WriteLine ("Help not implemented."); break; case CaptionButton.Maximize: form.WindowState = FormWindowState.Maximized; break; case CaptionButton.Minimize: form.WindowState = FormWindowState.Minimized; break; case CaptionButton.Restore: form.WindowState = FormWindowState.Normal; break; } } public TitleButton FindButton (int x, int y) { if (!Visible) { return null; } foreach (TitleButton button in AllButtons) { if (button.Visible && button.Rectangle.Contains (x, y)) { return button; } } return null; } public bool AnyPushedTitleButtons { get { if (!Visible) { return false; } foreach (TitleButton button in AllButtons) { if (button.Visible && button.State == ButtonState.Pushed) { return true; } } return false; } } #region IEnumerable Members public System.Collections.IEnumerator GetEnumerator () { return AllButtons.GetEnumerator (); } #endregion #region ToolTip helpers // Called from MouseMove if mouse is over a button public void ToolTipStart (TitleButton button) { tooltip_hovered_button = button; if (tooltip_hovered_button == tooltip_hidden_button) return; tooltip_hidden_button = null; if (tooltip != null && tooltip.Visible) ToolTipShow (true); if (tooltip_timer == null) { tooltip_timer = new Timer (); tooltip_timer.Tick += new EventHandler (ToolTipTimerTick); } tooltip_timer.Interval = tooltip_show_interval; tooltip_timer.Start (); tooltip_hovered_button = button; } public void ToolTipTimerTick (object sender, EventArgs e) { if (tooltip_timer.Interval == tooltip_hide_interval) { tooltip_hidden_button = tooltip_hovered_button; ToolTipHide (false); } else { ToolTipShow (false); } } // Called from timer (with only_refresh = false) // Called from ToolTipStart if tooltip is already shown (with only_refresh = true) public void ToolTipShow (bool only_refresh) { if (!form.Visible) return; string text = Locale.GetText (tooltip_hovered_button.Caption.ToString ()); tooltip_timer.Interval = tooltip_hide_interval; tooltip_timer.Enabled = true; if (only_refresh && (tooltip == null || !tooltip.Visible)) { return; } if (tooltip == null) tooltip = new ToolTip.ToolTipWindow (); else if (tooltip.Text == text && tooltip.Visible) return; else if (tooltip.Visible) tooltip.Visible = false; if (form.WindowState == FormWindowState.Maximized && form.MdiParent != null) tooltip.Present (form.MdiParent, text); else tooltip.Present (form, text); } // Called from MouseLeave (with reset_hidden_button = true) // Called from MouseDown (with reset_hidden_button = false) // Called from MouseMove if mouse isn't over any button (with reset_hidden_button = false) // Called from Timer if hiding (with reset_hidden_button = false) public void ToolTipHide (bool reset_hidden_button) { if (tooltip_timer != null) tooltip_timer.Enabled = false; if (tooltip != null && tooltip.Visible) tooltip.Visible = false; if (reset_hidden_button) tooltip_hidden_button = null; } #endregion public bool MouseMove (int x, int y) { if (!Visible) { return false; } bool any_change = false; bool any_pushed_buttons = AnyPushedTitleButtons; bool any_tooltip = false; TitleButton over_button = FindButton (x, y); foreach (TitleButton button in this) { if (button == null) continue; if (button.State == ButtonState.Inactive) continue; if (button == over_button) { if (any_pushed_buttons) { any_change |= button.State != ButtonState.Pushed; button.State = ButtonState.Pushed; } ToolTipStart (button); any_tooltip = true; if (!button.Entered) { button.Entered = true; if (ThemeEngine.Current.ManagedWindowTitleButtonHasHotElementStyle (button, form)) any_change = true; } } else { if (any_pushed_buttons) { any_change |= button.State != ButtonState.Normal; button.State = ButtonState.Normal; } if (button.Entered) { button.Entered = false; if (ThemeEngine.Current.ManagedWindowTitleButtonHasHotElementStyle (button, form)) any_change = true; } } } if (!any_tooltip) ToolTipHide (false); return any_change; } public void MouseDown (int x, int y) { if (!Visible) { return; } ToolTipHide (false); foreach (TitleButton button in this) { if (button != null && button.State != ButtonState.Inactive) { button.State = ButtonState.Normal; } } TitleButton clicked_button = FindButton (x, y); if (clicked_button != null && clicked_button.State != ButtonState.Inactive) { clicked_button.State = ButtonState.Pushed; } } public void MouseUp (int x, int y) { if (!Visible) { return; } TitleButton clicked_button = FindButton (x, y); if (clicked_button != null && clicked_button.State != ButtonState.Inactive) { clicked_button.OnClick (); } foreach (TitleButton button in this) { if (button == null || button.State == ButtonState.Inactive) continue; button.State = ButtonState.Normal; } if (clicked_button == CloseButton && !form.closing) XplatUI.InvalidateNC (form.Handle); ToolTipHide (true); } internal void MouseLeave (int x, int y) { if (!Visible) { return; } foreach (TitleButton button in this) { if (button == null || button.State == ButtonState.Inactive) continue; button.State = ButtonState.Normal; } ToolTipHide (true); } } }