a575963da9
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
1212 lines
29 KiB
C#
1212 lines
29 KiB
C#
// 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);
|
|
}
|
|
}
|
|
}
|
|
|
|
|