Merge pull request #2057 from directhex/monolite-on-jenkins
[mono.git] / mcs / class / System.Windows.Forms / System.Windows.Forms / InternalWindowManager.cs
1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
8 // 
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
11 // 
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 //
20 // Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
21 //
22 // Authors:
23 //      Jackson Harper (jackson@ximian.com)
24 //
25 //
26
27
28 using System;
29 using System.Drawing;
30 using System.Runtime.InteropServices;
31
32
33 namespace System.Windows.Forms {
34
35         internal abstract class InternalWindowManager {
36                 private TitleButtons title_buttons;
37                 internal Form form;
38
39                 // moving windows
40                 internal Point start;
41                 internal State state;
42                 protected Point clicked_point;
43                 private FormPos sizing_edge;
44                 internal Rectangle virtual_position;
45
46                 private Rectangle normal_bounds;
47                 private Rectangle iconic_bounds;
48                 
49
50                 public enum State {
51                         Idle,
52                         Moving,
53                         Sizing,
54                 }
55
56                 [Flags]
57                 public enum FormPos {
58                         None,
59
60                         TitleBar = 1,
61
62                         Top = 2,
63                         Left = 4,
64                         Right = 8,
65                         Bottom = 16,
66
67                         TopLeft = Top | Left,
68                         TopRight = Top | Right,
69
70                         BottomLeft = Bottom | Left,
71                         BottomRight = Bottom | Right,
72
73                         AnyEdge = Top | Left | Right | Bottom,
74                 }
75
76                 public InternalWindowManager (Form form)
77                 {
78                         this.form = form;
79
80                         form.SizeChanged += new EventHandler (FormSizeChangedHandler);
81
82                         title_buttons = new TitleButtons (form);
83                         ThemeEngine.Current.ManagedWindowSetButtonLocations (this);
84                 }
85
86                 public Form Form {
87                         get { return form; }
88                 }
89                 
90                 public int IconWidth {
91                         get { return TitleBarHeight - 5; }
92                 }
93
94                 public TitleButtons TitleButtons {
95                         get {
96                                 return title_buttons;
97                         }
98                 }
99                 internal Rectangle NormalBounds {
100                         get {
101                                 return normal_bounds;
102                         }
103                         set {
104                                 normal_bounds = value;
105                         }
106                 }
107                 internal Size IconicSize {
108                         get {
109                                 return SystemInformation.MinimizedWindowSize;
110                         }
111                 }
112                 
113                 internal Rectangle IconicBounds {
114                         get {
115                                 if (iconic_bounds == Rectangle.Empty)
116                                         return Rectangle.Empty;
117                                 Rectangle result = iconic_bounds;
118                                 result.Y = Form.Parent.ClientRectangle.Bottom - iconic_bounds.Y;
119                                 return result;
120                         }
121                         set {
122                                 iconic_bounds = value;
123                                 iconic_bounds.Y = Form.Parent.ClientRectangle.Bottom - iconic_bounds.Y;
124                         }
125                 }
126
127                 internal virtual Rectangle MaximizedBounds {
128                         get {
129                                 return Form.Parent.ClientRectangle;
130                         }
131                 }
132                                 
133                 public virtual void UpdateWindowState (FormWindowState old_window_state, FormWindowState new_window_state, bool force)
134                 {
135                         if (old_window_state == FormWindowState.Normal) {
136                                 NormalBounds = form.Bounds;
137                         } else if (old_window_state == FormWindowState.Minimized) {
138                                 IconicBounds = form.Bounds;
139                         }
140
141                         switch (new_window_state) {
142                         case FormWindowState.Minimized:
143                                 if (IconicBounds == Rectangle.Empty) {
144                                         Size size = IconicSize;
145                                         Point location = new Point (0, Form.Parent.ClientSize.Height - size.Height);
146                                         IconicBounds = new Rectangle (location, size);
147                                 }
148                                 form.Bounds = IconicBounds;
149                                 break;
150                         case FormWindowState.Maximized:
151                                 form.Bounds = MaximizedBounds;
152                                 break;
153                         case FormWindowState.Normal:
154                                 form.Bounds = NormalBounds;
155                                 break;
156                         }
157
158                         UpdateWindowDecorations (new_window_state);
159                         form.ResetCursor ();
160                 }
161                 
162                 public virtual void UpdateWindowDecorations (FormWindowState window_state)
163                 {
164                         ThemeEngine.Current.ManagedWindowSetButtonLocations (this);
165                         if (form.IsHandleCreated)
166                                 XplatUI.RequestNCRecalc (form.Handle);
167                 }
168                 
169                 public virtual bool WndProc (ref Message m)
170                 {
171 #if debug
172                         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 ());
173 #endif
174
175                         switch ((Msg)m.Msg) {
176
177
178                                 // The mouse handling messages are actually
179                                 // not WM_NC* messages except for the first button and NCMOVEs
180                                 // down because we capture on the form
181
182                         case Msg.WM_MOUSEMOVE:
183                                 return HandleMouseMove (form, ref m);
184
185                         case Msg.WM_LBUTTONUP:
186                                 HandleLButtonUp (ref m);
187                                 break;
188
189                         case Msg.WM_RBUTTONDOWN:
190                                 return HandleRButtonDown (ref m);
191                                 
192                         case Msg.WM_LBUTTONDOWN:
193                                 return HandleLButtonDown (ref m);
194                                 
195                         case Msg.WM_LBUTTONDBLCLK:
196                                 return HandleLButtonDblClick (ref m);
197                                 
198                         case Msg.WM_PARENTNOTIFY:
199                                 if (Control.LowOrder(m.WParam.ToInt32()) == (int) Msg.WM_LBUTTONDOWN) 
200                                         Activate ();
201                                 break;
202
203                         case Msg.WM_NCHITTEST: 
204                                 return HandleNCHitTest (ref m);
205
206                                 // Return true from these guys, otherwise win32 will mess up z-order
207                         case Msg.WM_NCLBUTTONUP:
208                                 HandleNCLButtonUp (ref m);
209                                 return true;
210
211                         case Msg.WM_NCLBUTTONDOWN:
212                                 HandleNCLButtonDown (ref m);
213                                 return true;
214
215                         case Msg.WM_NCMOUSEMOVE:
216                                 HandleNCMouseMove (ref m);
217                                 return true;
218                                 
219                         case Msg.WM_NCLBUTTONDBLCLK:
220                                 HandleNCLButtonDblClick (ref m);
221                                 break;
222
223                         case Msg.WM_NCMOUSELEAVE:
224                                 HandleNCMouseLeave (ref m);
225                                 break;
226                         
227                         case Msg.WM_MOUSELEAVE:
228                                 HandleMouseLeave (ref m);
229                                 break;
230
231                         case Msg.WM_NCCALCSIZE:
232                                 return HandleNCCalcSize (ref m);
233
234                         case Msg.WM_NCPAINT:
235                                 return HandleNCPaint (ref m);
236                         }
237
238                         return false;
239                 }
240
241                 protected virtual bool HandleNCPaint (ref Message m)
242                 {
243                         PaintEventArgs pe = XplatUI.PaintEventStart (ref m, form.Handle, false);
244
245                         Rectangle clip;
246                         
247                         if (form.ActiveMenu != null) {
248                                 Point pnt;
249
250                                 pnt = GetMenuOrigin ();
251
252                                 // The entire menu has to be in the clip rectangle because the 
253                                 // control buttons are right-aligned and otherwise they would
254                                 // stay painted when the window gets resized.
255                                 clip = new Rectangle (pnt.X, pnt.Y, form.ClientSize.Width, 0);
256                                 clip = Rectangle.Union (clip, pe.ClipRectangle);
257                                 pe.SetClip (clip);
258                                 pe.Graphics.SetClip (clip);
259
260                                 form.ActiveMenu.Draw (pe, new Rectangle (pnt.X, pnt.Y, form.ClientSize.Width, 0));
261                         }
262                         if (HasBorders || IsMinimized && !(Form.IsMdiChild && IsMaximized)) {
263                                 // clip region is not correct on win32.
264                                 // use the entire form's area.
265                                 clip = new Rectangle (0, 0, form.Width, form.Height);
266                                 ThemeEngine.Current.DrawManagedWindowDecorations (pe.Graphics, clip, this);
267                         }
268                         XplatUI.PaintEventEnd (ref m, form.Handle, false);
269                         return true;
270                 }
271
272                 protected virtual bool HandleNCCalcSize (ref Message m)
273                 {
274                         XplatUIWin32.NCCALCSIZE_PARAMS ncp;
275                         XplatUIWin32.RECT rect;
276
277                         if (m.WParam == (IntPtr)1) {
278                                 ncp = (XplatUIWin32.NCCALCSIZE_PARAMS)Marshal.PtrToStructure (m.LParam,
279                                                 typeof (XplatUIWin32.NCCALCSIZE_PARAMS));
280                                 
281                                 ncp.rgrc1 = NCCalcSize (ncp.rgrc1);
282
283                                 Marshal.StructureToPtr (ncp, m.LParam, true);
284                         } else {
285                                 rect = (XplatUIWin32.RECT) Marshal.PtrToStructure (m.LParam, typeof (XplatUIWin32.RECT));
286                                 
287                                 rect = NCCalcSize (rect);
288                                 
289                                 Marshal.StructureToPtr (rect, m.LParam, true);
290                         }
291                         
292                         return true;
293                 }
294
295                 protected virtual XplatUIWin32.RECT NCCalcSize (XplatUIWin32.RECT proposed_window_rect)
296                 {
297                         int bw = ThemeEngine.Current.ManagedWindowBorderWidth (this);
298
299                         if (HasBorders) {
300                                 proposed_window_rect.top += TitleBarHeight + bw;
301                                 proposed_window_rect.bottom -= bw;
302                                 proposed_window_rect.left += bw;
303                                 proposed_window_rect.right -= bw;
304                         }
305
306                         if (XplatUI.RequiresPositiveClientAreaSize) {
307                                 // This is necessary for Linux, can't handle <= 0-sized 
308                                 // client areas correctly.
309                                 if (proposed_window_rect.right <= proposed_window_rect.left) {
310                                         proposed_window_rect.right += proposed_window_rect.left - proposed_window_rect.right + 1;
311                                 }
312                                 if (proposed_window_rect.top >= proposed_window_rect.bottom) {
313                                         proposed_window_rect.bottom += proposed_window_rect.top - proposed_window_rect.bottom + 1;
314                                 }
315                         }
316
317                         return proposed_window_rect;
318                 }
319
320                 protected virtual bool HandleNCHitTest (ref Message m)
321                 {
322
323                         int x = Control.LowOrder ((int)m.LParam.ToInt32 ());
324                         int y = Control.HighOrder ((int)m.LParam.ToInt32 ());
325
326                         NCPointToClient (ref x, ref y);
327
328                         FormPos pos = FormPosForCoords (x, y);
329
330                         if (pos == FormPos.TitleBar) {
331                                 m.Result = new IntPtr ((int)HitTest.HTCAPTION);
332                                 return true;
333                         }
334
335                         if (!IsSizable)
336                                 return false;
337
338                         switch (pos) {
339                         case FormPos.Top:
340                                 m.Result = new IntPtr ((int)HitTest.HTTOP);
341                                 break;
342                         case FormPos.Left:
343                                 m.Result = new IntPtr ((int)HitTest.HTLEFT);
344                                 break;
345                         case FormPos.Right:
346                                 m.Result = new IntPtr ((int)HitTest.HTRIGHT);
347                                 break;
348                         case FormPos.Bottom:
349                                 m.Result = new IntPtr ((int)HitTest.HTBOTTOM);
350                                 break;
351                         case FormPos.TopLeft:
352                                 m.Result = new IntPtr ((int)HitTest.HTTOPLEFT);
353                                 break;
354                         case FormPos.TopRight:
355                                 m.Result = new IntPtr ((int)HitTest.HTTOPRIGHT);
356                                 break;
357                         case FormPos.BottomLeft:
358                                 m.Result = new IntPtr ((int)HitTest.HTBOTTOMLEFT);
359                                 break;
360                         case FormPos.BottomRight:
361                                 m.Result = new IntPtr ((int)HitTest.HTBOTTOMRIGHT);
362                                 break;
363                         default:
364                                 // We return false so that DefWndProc handles things
365                                 return false;
366                         }
367                         return true;
368                 }
369
370                 public virtual void UpdateBorderStyle (FormBorderStyle border_style)
371                 {
372                         if (form.IsHandleCreated) {
373                                 XplatUI.SetBorderStyle (form.Handle, border_style);
374                         }
375
376                         if (ShouldRemoveWindowManager (border_style)) {
377                                 form.RemoveWindowManager ();
378                                 return;
379                         }
380                                 
381                         ThemeEngine.Current.ManagedWindowSetButtonLocations (this);
382                 }
383
384                 
385                 
386                 public virtual void SetWindowState (FormWindowState old_state, FormWindowState window_state)
387                 {
388                         UpdateWindowState (old_state, window_state, false);
389                 }
390
391                 public virtual FormWindowState GetWindowState ()
392                 {
393                         return form.window_state;
394                 }
395
396                 public virtual void PointToClient (ref int x, ref int y)
397                 {
398                         // toolwindows stay in screencoords we just have to make sure
399                         // they obey the working area
400                         Rectangle working = SystemInformation.WorkingArea;
401
402                         if (x > working.Right)
403                                 x = working.Right;
404                         if (x < working.Left)
405                                 x = working.Left;
406
407                         if (y < working.Top)
408                                 y = working.Top;
409                         if (y > working.Bottom)
410                                 y = working.Bottom;
411                 }
412
413                 public virtual void PointToScreen (ref int x, ref int y)
414                 {
415                         XplatUI.ClientToScreen (form.Handle, ref x, ref y);
416                 }
417
418                 protected virtual bool ShouldRemoveWindowManager (FormBorderStyle style)
419                 {
420                         return style != FormBorderStyle.FixedToolWindow && style != FormBorderStyle.SizableToolWindow;
421                 }
422
423                 public bool IconRectangleContains (int x, int y)
424                 {
425                         if (!ShowIcon)
426                                 return false;
427
428                         Rectangle icon = ThemeEngine.Current.ManagedWindowGetTitleBarIconArea (this);
429                         return icon.Contains (x, y);
430                 }
431
432                 public bool ShowIcon {
433                         get {
434                                 if (!Form.ShowIcon)
435                                         return false;
436                                 if (!HasBorders)
437                                         return false;
438                                 if (IsMinimized)
439                                         return true;
440                                 if (IsToolWindow || Form.FormBorderStyle == FormBorderStyle.FixedDialog)
441                                         return false;
442                                 return true;
443                         }
444                 }
445
446                 protected virtual void Activate ()
447                 {
448                         form.Invalidate (true);
449                         form.Update ();
450                 }
451
452                 public virtual bool IsActive {
453                         get {
454                                 return true;
455                         }
456                 }
457
458
459                 private void FormSizeChangedHandler (object sender, EventArgs e)
460                 {
461                         if (form.IsHandleCreated) {
462                                 ThemeEngine.Current.ManagedWindowSetButtonLocations (this);
463                                 XplatUI.InvalidateNC (form.Handle);
464                         }
465                 }
466
467                 protected virtual bool HandleRButtonDown (ref Message m)
468                 {
469                         Activate ();
470                         return false;
471                 }
472                 
473                 protected virtual bool HandleLButtonDown (ref Message m)
474                 {
475                         Activate ();
476                         return false;
477                 }
478
479                 protected virtual bool HandleLButtonDblClick(ref Message m)
480                 {
481                         return false;
482                 }
483                 
484                 protected virtual bool HandleNCMouseLeave (ref Message m)
485                 {
486                         int x = Control.LowOrder ((int)m.LParam.ToInt32 ());
487                         int y = Control.HighOrder ((int)m.LParam.ToInt32 ());
488
489                         NCPointToClient (ref x, ref y);
490                         FormPos pos = FormPosForCoords (x, y);
491
492                         if (pos != FormPos.TitleBar) {
493                                 HandleTitleBarLeave (x, y);
494                                 return true;
495                         }
496
497                         return true;
498                 }
499                 
500                 protected virtual bool HandleNCMouseMove (ref Message m)
501                 {
502                         int x = Control.LowOrder((int)m.LParam.ToInt32( ));
503                         int y = Control.HighOrder((int)m.LParam.ToInt32( ));
504
505                         NCPointToClient (ref x, ref y);
506                         FormPos pos = FormPosForCoords (x, y);
507
508                         if (pos == FormPos.TitleBar) {
509                                 HandleTitleBarMouseMove (x, y);
510                                 return true;
511                         }
512
513                         if (form.ActiveMenu != null && XplatUI.IsEnabled (form.Handle)) {
514                                 MouseEventArgs mea = new MouseEventArgs (Form.FromParamToMouseButtons (m.WParam.ToInt32 ()), form.mouse_clicks, x, y, 0);
515                                 form.ActiveMenu.OnMouseMove (form, mea);
516                         }
517
518                         return true;
519                         
520                 }
521                 
522                 protected virtual bool HandleNCLButtonDown (ref Message m)
523                 {
524                         Activate ();
525
526                         start = Cursor.Position;
527                         virtual_position = form.Bounds;
528                         
529                         int x = Control.LowOrder ((int) m.LParam.ToInt32 ());
530                         int y = Control.HighOrder ((int) m.LParam.ToInt32 ());
531                         
532                         // Need to adjust because we are in NC land
533                         NCPointToClient (ref x, ref y);
534                         FormPos pos = FormPosForCoords (x, y);
535                         
536                         if (form.ActiveMenu != null && XplatUI.IsEnabled (form.Handle)) {
537                                 MouseEventArgs mea = new MouseEventArgs (Form.FromParamToMouseButtons (m.WParam.ToInt32 ()), form.mouse_clicks, x, y - TitleBarHeight, 0);
538                                 form.ActiveMenu.OnMouseDown (form, mea);
539                         }
540                         
541                         if (pos == FormPos.TitleBar) {
542                                 HandleTitleBarDown (x, y);
543                                 return true;
544                         }
545
546                         if (IsSizable) {
547                                 if ((pos & FormPos.AnyEdge) == 0)
548                                         return false;
549
550                                 virtual_position = form.Bounds;
551                                 state = State.Sizing;
552                                 sizing_edge = pos;
553                                 form.Capture = true;
554                                 return true;
555                         }
556
557                         return false;
558                 }
559
560                 protected virtual void HandleNCLButtonDblClick (ref Message m)
561                 {
562                         int x = Control.LowOrder ((int)m.LParam.ToInt32 ());
563                         int y = Control.HighOrder ((int)m.LParam.ToInt32 ());
564
565                         // Need to adjust because we are in NC land
566                         NCPointToClient (ref x, ref y);
567
568                         FormPos pos = FormPosForCoords (x, y);
569                         if (pos == FormPos.TitleBar || pos == FormPos.Top)
570                                 HandleTitleBarDoubleClick (x, y);
571
572                 }
573                 
574                 protected virtual void HandleTitleBarDoubleClick (int x, int y)
575                 {
576                 
577                 }
578                 
579                 protected virtual void HandleTitleBarLeave (int x, int y)
580                 {
581                         title_buttons.MouseLeave (x, y);
582                 }
583                 
584                 protected virtual void HandleTitleBarMouseMove (int x, int y)
585                 {
586                         if (title_buttons.MouseMove (x, y))
587                                 XplatUI.InvalidateNC (form.Handle);
588                 }
589                 
590                 protected virtual void HandleTitleBarUp (int x, int y)
591                 {
592                         title_buttons.MouseUp (x, y);
593
594                         return;
595                 }
596                 
597                 protected virtual void HandleTitleBarDown (int x, int y)
598                 {
599                         title_buttons.MouseDown (x, y);
600
601                         if (!TitleButtons.AnyPushedTitleButtons && !IsMaximized) {
602                                 state = State.Moving;
603                                 clicked_point = new Point (x, y);
604                                 if (form.Parent != null) {
605                                         form.CaptureWithConfine (form.Parent);
606                                 } else {
607                                         form.Capture = true;
608                                 }
609                         }
610                         
611                         XplatUI.InvalidateNC (form.Handle);
612                 }
613
614                 private bool HandleMouseMove (Form form, ref Message m)
615                 {
616                         switch (state) {
617                         case State.Moving:
618                                 HandleWindowMove (m);
619                                 return true;
620                         case State.Sizing:
621                                 HandleSizing (m);
622                                 return true;
623                         }
624                         
625                         return false;
626                 }
627
628                 private void HandleMouseLeave (ref Message m)
629                 {
630                         form.ResetCursor ();
631                 }
632         
633                 protected virtual void HandleWindowMove (Message m)
634                 {
635                         Point move = MouseMove (Cursor.Position);
636
637                         UpdateVP (virtual_position.X + move.X, virtual_position.Y + move.Y,
638                                         virtual_position.Width, virtual_position.Height);
639                 }
640
641                 private void HandleSizing (Message m)
642                 {
643                         Rectangle pos = virtual_position;
644                         int mw;
645                         int mh;
646                         if (IsToolWindow) {
647                                 int border_width = BorderWidth;
648                                 mw = 2 * (border_width + Theme.ManagedWindowSpacingAfterLastTitleButton) + ThemeEngine.Current.ManagedWindowButtonSize (this).Width;
649                                 mh = 2 * border_width + TitleBarHeight;
650                         } else {
651                                 Size minimum_size = SystemInformation.MinWindowTrackSize;
652                                 mw = minimum_size.Width;
653                                 mh = minimum_size.Height;
654                         }
655                         int x = Cursor.Position.X;
656                         int y = Cursor.Position.Y;
657
658                         PointToClient (ref x, ref y);
659
660                         if ((sizing_edge & FormPos.Top) != 0) {
661                                 if (pos.Bottom - y < mh)
662                                         y = pos.Bottom - mh;
663                                 pos.Height = pos.Bottom - y;
664                                 pos.Y = y;
665                         } else if ((sizing_edge & FormPos.Bottom) != 0) {
666                                 int height = y - pos.Top;
667                                 if (height <= mh)
668                                         height = mh;
669                                 pos.Height = height;
670                         }
671
672                         if ((sizing_edge & FormPos.Left) != 0) {
673                                 if (pos.Right - x < mw)
674                                         x = pos.Right - mw;
675                                 pos.Width = pos.Right - x;
676                                 pos.X = x;
677                         } else if ((sizing_edge & FormPos.Right) != 0) {
678                                 int width = x - form.Left;
679                                 if (width <= mw)
680                                         width = mw;
681                                 pos.Width = width;
682                         }
683
684                         UpdateVP (pos);
685                 }
686
687                 public bool IsMaximized {
688                         get { return GetWindowState () == FormWindowState.Maximized; }
689                 }
690
691                 public bool IsMinimized {
692                         get { return GetWindowState () == FormWindowState.Minimized; }
693                 }
694
695                 public bool IsSizable {
696                         get {
697                                 switch (form.FormBorderStyle) {
698                                 case FormBorderStyle.Sizable:
699                                 case FormBorderStyle.SizableToolWindow:
700                                         return (form.window_state != FormWindowState.Minimized);
701                                 default:
702                                         return false;
703                                 }
704                         }
705                 }
706
707                 public bool HasBorders {
708                         get {
709                                 return form.FormBorderStyle != FormBorderStyle.None;
710                         }
711                 }
712
713                 public bool IsToolWindow {
714                         get {
715                                 if (form.FormBorderStyle == FormBorderStyle.SizableToolWindow ||
716                                     form.FormBorderStyle == FormBorderStyle.FixedToolWindow || 
717                                     form.GetCreateParams().IsSet (WindowExStyles.WS_EX_TOOLWINDOW))
718                                         return true;
719                                 return false;
720                         }
721                 }
722
723                 public int TitleBarHeight {
724                         get {
725                                 return ThemeEngine.Current.ManagedWindowTitleBarHeight (this);
726                         }
727                 }
728
729                 public int BorderWidth {
730                         get {
731                                 return ThemeEngine.Current.ManagedWindowBorderWidth (this);
732                         }
733                 }
734                 
735                 public virtual int MenuHeight {
736                         get {
737                                 return (form.Menu != null ? ThemeEngine.Current.MenuHeight : 0);
738                         }
739                 }
740
741                 protected void UpdateVP (Rectangle r)
742                 {
743                         UpdateVP (r.X, r.Y, r.Width, r.Height);
744                 }
745
746                 protected void UpdateVP (Point loc, int w, int h)
747                 {
748                         UpdateVP (loc.X, loc.Y, w, h);
749                 }
750
751                 protected void UpdateVP (int x, int y, int w, int h)
752                 {
753                         virtual_position.X = x;
754                         virtual_position.Y = y;
755                         virtual_position.Width = w;
756                         virtual_position.Height = h;
757
758                         DrawVirtualPosition (virtual_position);
759                 }
760
761                 protected virtual void HandleLButtonUp (ref Message m)
762                 {
763                         if (state == State.Idle)
764                                 return;
765
766                         ClearVirtualPosition ();
767
768                         form.Capture = false;
769                         if (state == State.Moving && form.Location != virtual_position.Location) 
770                                 form.Location = virtual_position.Location;
771                         else if (state == State.Sizing && form.Bounds != virtual_position)
772                                 form.Bounds = virtual_position;
773                         state = State.Idle;
774
775                         OnWindowFinishedMoving ();
776                 }
777
778                 private bool HandleNCLButtonUp (ref Message m)
779                 {
780                         if (form.Capture) {
781                                 ClearVirtualPosition ();
782
783                                 form.Capture = false;
784                                 state = State.Idle;
785                                 if (form.MdiContainer != null)
786                                         form.MdiContainer.SizeScrollBars();
787                         }
788                                 
789                         int x = Control.LowOrder ((int) m.LParam.ToInt32 ());
790                         int y = Control.HighOrder ((int) m.LParam.ToInt32 ());
791
792                         NCPointToClient (ref x, ref y);
793                         FormPos pos = FormPosForCoords (x, y);
794
795                         if (pos == FormPos.TitleBar) {
796                                 HandleTitleBarUp (x, y);
797                                 return true;
798                         }
799                         
800                         return true;
801                 }
802                 
803                 protected void DrawTitleButton (Graphics dc, TitleButton button, Rectangle clip)
804                 {
805                         if (!button.Rectangle.IntersectsWith (clip))
806                                 return;
807
808                         ThemeEngine.Current.ManagedWindowDrawMenuButton (dc, button, clip, this);
809                 }
810
811                 public virtual void DrawMaximizedButtons (object sender, PaintEventArgs pe)
812                 {
813                 }
814
815                 protected Point MouseMove (Point pos)
816                 {
817                         return new Point (pos.X - start.X, pos.Y - start.Y);
818                 }
819
820                 protected virtual void DrawVirtualPosition (Rectangle virtual_position)
821                 {
822                         form.Bounds = virtual_position;
823                         start = Cursor.Position;
824                 }
825
826                 protected virtual void ClearVirtualPosition ()
827                 {
828                         
829                 }
830
831                 protected virtual void OnWindowFinishedMoving ()
832                 {
833                 }
834
835                 protected virtual void NCPointToClient(ref int x, ref int y) {
836                         form.PointToClient(ref x, ref y);
837                         NCClientToNC (ref x, ref y);
838                 }
839
840                 protected virtual void NCClientToNC (ref int x, ref int y) {
841                         y += TitleBarHeight;
842                         y += BorderWidth;
843                         y += MenuHeight;
844                 }
845                 
846                 internal Point GetMenuOrigin ()
847                 {
848                         return new Point (BorderWidth, BorderWidth + TitleBarHeight);
849                 }
850                 
851                 protected FormPos FormPosForCoords (int x, int y)
852                 {
853                         int bw = BorderWidth;
854                         if (y < TitleBarHeight + bw) {
855                                 //      Console.WriteLine ("A");
856                                 if (y > bw && x > bw &&
857                                                 x < form.Width - bw)
858                                         return FormPos.TitleBar;
859
860                                 if (x < bw || (x < 20 && y < bw))
861                                         return FormPos.TopLeft;
862
863                                 if (x > form.Width - bw ||
864                                         (x > form.Width - 20 && y < bw))
865                                         return FormPos.TopRight;
866
867                                 if (y < bw)
868                                         return FormPos.Top;
869
870                         } else if (y > form.Height - 20) {
871                                 //      Console.WriteLine ("B");
872                                 if (x < bw ||
873                                                 (x < 20 && y > form.Height - bw))
874                                         return FormPos.BottomLeft;
875
876                                 if (x > form.Width - (bw * 2) ||
877                                                 (x > form.Width - 20 &&
878                                                  y > form.Height - bw))
879                                         return FormPos.BottomRight;
880
881                                 if (y > form.Height - (bw * 2))
882                                         return FormPos.Bottom;
883
884
885                         } else if (x < bw) {
886                                 //      Console.WriteLine ("C");
887                                 return FormPos.Left;
888                         } else if (x > form.Width - (bw * 2)) {
889 //                              Console.WriteLine ("D");
890                                 return FormPos.Right;
891                         } else {
892                                 //                      Console.WriteLine ("E   {0}", form.Width - bw);
893                         }
894                         
895                         return FormPos.None;
896                 }
897         }
898         internal class TitleButton
899         {
900                 public Rectangle Rectangle;
901                 public ButtonState State;
902                 public CaptionButton Caption;
903                 private EventHandler Clicked;
904                 public bool Visible;
905                 bool entered;
906
907                 public TitleButton (CaptionButton caption, EventHandler clicked)
908                 {
909                         Caption = caption;
910                         Clicked = clicked;
911                 }
912                 
913                 public void OnClick ()
914                 {
915                         if (Clicked != null) {
916                                 Clicked (this, EventArgs.Empty);
917                         }
918                 }
919
920                 public bool Entered {
921                         get { return entered; }
922                         set { entered = value; }
923                 }
924         }
925
926         internal class TitleButtons : System.Collections.IEnumerable
927         {
928                 public TitleButton MinimizeButton;
929                 public TitleButton MaximizeButton;
930                 public TitleButton RestoreButton;
931                 public TitleButton CloseButton;
932                 public TitleButton HelpButton;
933
934                 public TitleButton [] AllButtons;
935                 public bool Visible;
936
937                 private ToolTip.ToolTipWindow tooltip;
938                 private Timer tooltip_timer;
939                 private TitleButton tooltip_hovered_button;
940                 private TitleButton tooltip_hidden_button;
941                 private const int tooltip_hide_interval = 3000;
942                 private const int tooltip_show_interval = 1000;
943                 private Form form;
944                 
945                 public TitleButtons (Form frm)
946                 {
947                         this.form = frm;
948                         this.Visible = true;
949                         
950                         MinimizeButton = new TitleButton (CaptionButton.Minimize, new EventHandler (ClickHandler));
951                         MaximizeButton = new TitleButton (CaptionButton.Maximize, new EventHandler (ClickHandler));
952                         RestoreButton = new TitleButton (CaptionButton.Restore, new EventHandler (ClickHandler));
953                         CloseButton = new TitleButton (CaptionButton.Close, new EventHandler (ClickHandler));
954                         HelpButton = new TitleButton (CaptionButton.Help, new EventHandler (ClickHandler));
955
956                         AllButtons = new TitleButton [] { MinimizeButton, MaximizeButton, RestoreButton, CloseButton, HelpButton };
957                 }
958                 
959                 private void ClickHandler (object sender, EventArgs e)
960                 {
961                         if (!Visible) {
962                                 return;
963                         }
964                         
965                         TitleButton button = (TitleButton) sender;
966                         
967                         switch (button.Caption) {
968                                 case CaptionButton.Close: 
969                                         form.Close ();
970                                         break;
971                                 case CaptionButton.Help:
972                                         Console.WriteLine ("Help not implemented.");
973                                         break;
974                                 case CaptionButton.Maximize:
975                                         form.WindowState = FormWindowState.Maximized;
976                                         break;
977                                 case CaptionButton.Minimize:
978                                         form.WindowState = FormWindowState.Minimized;
979                                         break;
980                                 case CaptionButton.Restore:
981                                         form.WindowState = FormWindowState.Normal;
982                                         break;
983                         }
984                 }
985                 
986                 public TitleButton FindButton (int x, int y)
987                 {
988                         if (!Visible) {
989                                 return null;
990                         }
991                         
992                         foreach (TitleButton button in AllButtons) {
993                                 if (button.Visible && button.Rectangle.Contains (x, y)) {
994                                         return button;
995                                 }
996                         }
997                         return null;
998                 }
999                 
1000                 public bool AnyPushedTitleButtons {
1001                         get {
1002                                 if (!Visible) {
1003                                         return false;
1004                                 }
1005                                 
1006                                 foreach (TitleButton button in AllButtons) {
1007                                         if (button.Visible && button.State == ButtonState.Pushed) {
1008                                                 return true;
1009                                         }
1010                                 }
1011                                 return false;
1012                         }
1013                 }
1014
1015                 #region IEnumerable Members
1016
1017                 public System.Collections.IEnumerator GetEnumerator ()
1018                 {
1019                         return AllButtons.GetEnumerator ();
1020                 }
1021                 #endregion
1022
1023                 #region ToolTip helpers
1024                 // Called from MouseMove if mouse is over a button
1025                 public void ToolTipStart (TitleButton button)
1026                 {
1027                         tooltip_hovered_button = button;
1028
1029                         if (tooltip_hovered_button == tooltip_hidden_button)
1030                                 return;
1031                         tooltip_hidden_button = null;
1032
1033                         if (tooltip != null && tooltip.Visible)
1034                                 ToolTipShow (true);
1035
1036                         if (tooltip_timer == null) {
1037
1038                                 tooltip_timer = new Timer ();
1039                                 tooltip_timer.Tick += new EventHandler (ToolTipTimerTick);
1040                         }
1041
1042                         tooltip_timer.Interval = tooltip_show_interval;
1043                         tooltip_timer.Start ();
1044                         tooltip_hovered_button = button;
1045                 }
1046
1047                 public void ToolTipTimerTick (object sender, EventArgs e)
1048                 {
1049                         if (tooltip_timer.Interval == tooltip_hide_interval) {
1050                                 tooltip_hidden_button = tooltip_hovered_button;
1051                                 ToolTipHide (false);
1052                         } else {
1053                                 ToolTipShow (false);
1054                         }
1055                 }
1056                 // Called from timer (with only_refresh = false)
1057                 // Called from ToolTipStart if tooltip is already shown (with only_refresh = true)
1058                 public void ToolTipShow (bool only_refresh)
1059                 {
1060                         if (!form.Visible)
1061                                 return;
1062
1063                         string text = Locale.GetText (tooltip_hovered_button.Caption.ToString ());
1064
1065                         tooltip_timer.Interval = tooltip_hide_interval;
1066                         tooltip_timer.Enabled = true;
1067
1068                         if (only_refresh && (tooltip == null || !tooltip.Visible)) {
1069                                 return;
1070                         }
1071
1072                         if (tooltip == null)
1073                                 tooltip = new ToolTip.ToolTipWindow ();
1074                         else if (tooltip.Text == text && tooltip.Visible)
1075                                 return;
1076                         else if (tooltip.Visible)
1077                                 tooltip.Visible = false;
1078
1079                         if (form.WindowState == FormWindowState.Maximized && form.MdiParent != null)
1080                                 tooltip.Present (form.MdiParent, text);
1081                         else
1082                                 tooltip.Present (form, text);
1083                         
1084                 }
1085                 
1086                 // Called from MouseLeave (with reset_hidden_button = true)
1087                 // Called from MouseDown  (with reset_hidden_button = false)
1088                 // Called from MouseMove if mouse isn't over any button (with reset_hidden_button = false)
1089                 // Called from Timer if hiding (with reset_hidden_button = false)
1090                 public void ToolTipHide (bool reset_hidden_button)
1091                 {
1092                         if (tooltip_timer != null)
1093                                 tooltip_timer.Enabled = false;
1094                         if (tooltip != null && tooltip.Visible)
1095                                 tooltip.Visible = false;
1096                         if (reset_hidden_button)
1097                                 tooltip_hidden_button = null;
1098                 }
1099                 #endregion
1100                 
1101                 public bool MouseMove (int x, int y)
1102                 {
1103                         if (!Visible) {
1104                                 return false;
1105                         }
1106
1107                         bool any_change = false;
1108                         bool any_pushed_buttons = AnyPushedTitleButtons;
1109                         bool any_tooltip = false;
1110                         TitleButton over_button = FindButton (x, y);
1111
1112                         foreach (TitleButton button in this) {
1113                                 if (button == null)
1114                                         continue;
1115                                 
1116                                 if (button.State == ButtonState.Inactive)
1117                                         continue;
1118                                         
1119                                 if (button == over_button) {
1120                                         if (any_pushed_buttons) {
1121                                                 any_change |= button.State != ButtonState.Pushed;
1122                                                 button.State = ButtonState.Pushed;
1123                                         }
1124                                         ToolTipStart (button);
1125                                         any_tooltip = true;
1126                                         if (!button.Entered) {
1127                                                 button.Entered = true;
1128                                                 if (ThemeEngine.Current.ManagedWindowTitleButtonHasHotElementStyle (button, form))
1129                                                         any_change = true;
1130                                         }
1131                                 } else {
1132                                         if (any_pushed_buttons) {
1133                                                 any_change |= button.State != ButtonState.Normal;
1134                                                 button.State = ButtonState.Normal;
1135                                         }
1136                                         if (button.Entered) {
1137                                                 button.Entered = false;
1138                                                 if (ThemeEngine.Current.ManagedWindowTitleButtonHasHotElementStyle (button, form))
1139                                                         any_change = true;
1140                                         }
1141                                 }
1142                         }
1143
1144                         if (!any_tooltip)
1145                                 ToolTipHide (false);
1146
1147                         return any_change;
1148                 }
1149
1150                 public void MouseDown (int x, int y)
1151                 {
1152                         if (!Visible) {
1153                                 return;
1154                         }
1155
1156                         ToolTipHide (false);
1157
1158                         foreach (TitleButton button in this) {
1159                                 if (button != null && button.State != ButtonState.Inactive) {
1160                                         button.State = ButtonState.Normal;
1161                                 }
1162                         }
1163                         TitleButton clicked_button = FindButton (x, y);
1164                         if (clicked_button != null && clicked_button.State != ButtonState.Inactive) {
1165                                 clicked_button.State = ButtonState.Pushed;
1166                         }
1167                 }
1168
1169                 public void MouseUp (int x, int y)
1170                 {
1171                         if (!Visible) {
1172                                 return;
1173                         }
1174                         
1175                         TitleButton clicked_button = FindButton (x, y);
1176                         if (clicked_button != null && clicked_button.State != ButtonState.Inactive) {
1177                                 clicked_button.OnClick ();
1178                         }
1179
1180                         foreach (TitleButton button in this) {
1181                                 if (button == null || button.State == ButtonState.Inactive)
1182                                         continue;
1183
1184                                 button.State = ButtonState.Normal;
1185                         }
1186
1187                         if (clicked_button == CloseButton && !form.closing)
1188                                 XplatUI.InvalidateNC (form.Handle);
1189                                 
1190                         ToolTipHide (true);
1191                 }
1192
1193                 internal void MouseLeave (int x, int y)
1194                 {
1195                         if (!Visible) {
1196                                 return;
1197                         }
1198                         
1199                         foreach (TitleButton button in this) {
1200                                 if (button == null || button.State == ButtonState.Inactive)
1201                                         continue;
1202
1203                                 button.State = ButtonState.Normal;
1204                         }
1205                         
1206                         ToolTipHide (true);
1207                 }
1208         }
1209 }
1210
1211