2009-09-11 Ivan N. Zlatev <contact@i-nz.net>
[mono.git] / mcs / class / Managed.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 NET_2_0
435                                 if (!Form.ShowIcon)
436                                         return false;
437 #endif
438                                 if (!HasBorders)
439                                         return false;
440                                 if (IsMinimized)
441                                         return true;
442                                 if (IsToolWindow || Form.FormBorderStyle == FormBorderStyle.FixedDialog)
443                                         return false;
444                                 return true;
445                         }
446                 }
447
448                 protected virtual void Activate ()
449                 {
450                         form.Invalidate (true);
451                         form.Update ();
452                 }
453
454                 public virtual bool IsActive {
455                         get {
456                                 return true;
457                         }
458                 }
459
460
461                 private void FormSizeChangedHandler (object sender, EventArgs e)
462                 {
463                         if (form.IsHandleCreated) {
464                                 ThemeEngine.Current.ManagedWindowSetButtonLocations (this);
465                                 XplatUI.InvalidateNC (form.Handle);
466                         }
467                 }
468
469                 protected virtual bool HandleRButtonDown (ref Message m)
470                 {
471                         Activate ();
472                         return false;
473                 }
474                 
475                 protected virtual bool HandleLButtonDown (ref Message m)
476                 {
477                         Activate ();
478                         return false;
479                 }
480
481                 protected virtual bool HandleLButtonDblClick(ref Message m)
482                 {
483                         return false;
484                 }
485                 
486                 protected virtual bool HandleNCMouseLeave (ref Message m)
487                 {
488                         int x = Control.LowOrder ((int)m.LParam.ToInt32 ());
489                         int y = Control.HighOrder ((int)m.LParam.ToInt32 ());
490
491                         NCPointToClient (ref x, ref y);
492                         FormPos pos = FormPosForCoords (x, y);
493
494                         if (pos != FormPos.TitleBar) {
495                                 HandleTitleBarLeave (x, y);
496                                 return true;
497                         }
498
499                         return true;
500                 }
501                 
502                 protected virtual bool HandleNCMouseMove (ref Message m)
503                 {
504                         int x = Control.LowOrder((int)m.LParam.ToInt32( ));
505                         int y = Control.HighOrder((int)m.LParam.ToInt32( ));
506
507                         NCPointToClient (ref x, ref y);
508                         FormPos pos = FormPosForCoords (x, y);
509
510                         if (pos == FormPos.TitleBar) {
511                                 HandleTitleBarMouseMove (x, y);
512                                 return true;
513                         }
514
515                         if (form.ActiveMenu != null && XplatUI.IsEnabled (form.Handle)) {
516                                 MouseEventArgs mea = new MouseEventArgs (Form.FromParamToMouseButtons (m.WParam.ToInt32 ()), form.mouse_clicks, x, y, 0);
517                                 form.ActiveMenu.OnMouseMove (form, mea);
518                         }
519
520                         return true;
521                         
522                 }
523                 
524                 protected virtual bool HandleNCLButtonDown (ref Message m)
525                 {
526                         Activate ();
527
528                         start = Cursor.Position;
529                         virtual_position = form.Bounds;
530                         
531                         int x = Control.LowOrder ((int) m.LParam.ToInt32 ());
532                         int y = Control.HighOrder ((int) m.LParam.ToInt32 ());
533                         
534                         // Need to adjust because we are in NC land
535                         NCPointToClient (ref x, ref y);
536                         FormPos pos = FormPosForCoords (x, y);
537                         
538                         if (form.ActiveMenu != null && XplatUI.IsEnabled (form.Handle)) {
539                                 MouseEventArgs mea = new MouseEventArgs (Form.FromParamToMouseButtons (m.WParam.ToInt32 ()), form.mouse_clicks, x, y - TitleBarHeight, 0);
540                                 form.ActiveMenu.OnMouseDown (form, mea);
541                         }
542                         
543                         if (pos == FormPos.TitleBar) {
544                                 HandleTitleBarDown (x, y);
545                                 return true;
546                         }
547
548                         if (IsSizable) {
549                                 if ((pos & FormPos.AnyEdge) == 0)
550                                         return false;
551
552                                 virtual_position = form.Bounds;
553                                 state = State.Sizing;
554                                 sizing_edge = pos;
555                                 form.Capture = true;
556                                 return true;
557                         }
558
559                         return false;
560                 }
561
562                 protected virtual void HandleNCLButtonDblClick (ref Message m)
563                 {
564                         int x = Control.LowOrder ((int)m.LParam.ToInt32 ());
565                         int y = Control.HighOrder ((int)m.LParam.ToInt32 ());
566
567                         // Need to adjust because we are in NC land
568                         NCPointToClient (ref x, ref y);
569
570                         FormPos pos = FormPosForCoords (x, y);
571                         if (pos == FormPos.TitleBar || pos == FormPos.Top)
572                                 HandleTitleBarDoubleClick (x, y);
573
574                 }
575                 
576                 protected virtual void HandleTitleBarDoubleClick (int x, int y)
577                 {
578                 
579                 }
580                 
581                 protected virtual void HandleTitleBarLeave (int x, int y)
582                 {
583                         title_buttons.MouseLeave (x, y);
584                 }
585                 
586                 protected virtual void HandleTitleBarMouseMove (int x, int y)
587                 {
588                         if (title_buttons.MouseMove (x, y))
589                                 XplatUI.InvalidateNC (form.Handle);
590                 }
591                 
592                 protected virtual void HandleTitleBarUp (int x, int y)
593                 {
594                         title_buttons.MouseUp (x, y);
595
596                         return;
597                 }
598                 
599                 protected virtual void HandleTitleBarDown (int x, int y)
600                 {
601                         title_buttons.MouseDown (x, y);
602
603                         if (!TitleButtons.AnyPushedTitleButtons && !IsMaximized) {
604                                 state = State.Moving;
605                                 clicked_point = new Point (x, y);
606                                 if (form.Parent != null) {
607                                         form.CaptureWithConfine (form.Parent);
608                                 } else {
609                                         form.Capture = true;
610                                 }
611                         }
612                         
613                         XplatUI.InvalidateNC (form.Handle);
614                 }
615
616                 private bool HandleMouseMove (Form form, ref Message m)
617                 {
618                         switch (state) {
619                         case State.Moving:
620                                 HandleWindowMove (m);
621                                 return true;
622                         case State.Sizing:
623                                 HandleSizing (m);
624                                 return true;
625                         }
626                         
627                         return false;
628                 }
629
630                 private void HandleMouseLeave (ref Message m)
631                 {
632                         form.ResetCursor ();
633                 }
634         
635                 protected virtual void HandleWindowMove (Message m)
636                 {
637                         Point move = MouseMove (Cursor.Position);
638
639                         UpdateVP (virtual_position.X + move.X, virtual_position.Y + move.Y,
640                                         virtual_position.Width, virtual_position.Height);
641                 }
642
643                 private void HandleSizing (Message m)
644                 {
645                         Rectangle pos = virtual_position;
646                         int mw;
647                         int mh;
648                         if (IsToolWindow) {
649                                 int border_width = BorderWidth;
650                                 mw = 2 * (border_width + Theme.ManagedWindowSpacingAfterLastTitleButton) + ThemeEngine.Current.ManagedWindowButtonSize (this).Width;
651                                 mh = 2 * border_width + TitleBarHeight;
652                         } else {
653                                 Size minimum_size = SystemInformation.MinWindowTrackSize;
654                                 mw = minimum_size.Width;
655                                 mh = minimum_size.Height;
656                         }
657                         int x = Cursor.Position.X;
658                         int y = Cursor.Position.Y;
659
660                         PointToClient (ref x, ref y);
661
662                         if ((sizing_edge & FormPos.Top) != 0) {
663                                 if (pos.Bottom - y < mh)
664                                         y = pos.Bottom - mh;
665                                 pos.Height = pos.Bottom - y;
666                                 pos.Y = y;
667                         } else if ((sizing_edge & FormPos.Bottom) != 0) {
668                                 int height = y - pos.Top;
669                                 if (height <= mh)
670                                         height = mh;
671                                 pos.Height = height;
672                         }
673
674                         if ((sizing_edge & FormPos.Left) != 0) {
675                                 if (pos.Right - x < mw)
676                                         x = pos.Right - mw;
677                                 pos.Width = pos.Right - x;
678                                 pos.X = x;
679                         } else if ((sizing_edge & FormPos.Right) != 0) {
680                                 int width = x - form.Left;
681                                 if (width <= mw)
682                                         width = mw;
683                                 pos.Width = width;
684                         }
685
686                         UpdateVP (pos);
687                 }
688
689                 public bool IsMaximized {
690                         get { return GetWindowState () == FormWindowState.Maximized; }
691                 }
692
693                 public bool IsMinimized {
694                         get { return GetWindowState () == FormWindowState.Minimized; }
695                 }
696
697                 public bool IsSizable {
698                         get {
699                                 switch (form.FormBorderStyle) {
700                                 case FormBorderStyle.Sizable:
701                                 case FormBorderStyle.SizableToolWindow:
702                                         return (form.window_state != FormWindowState.Minimized);
703                                 default:
704                                         return false;
705                                 }
706                         }
707                 }
708
709                 public bool HasBorders {
710                         get {
711                                 return form.FormBorderStyle != FormBorderStyle.None;
712                         }
713                 }
714
715                 public bool IsToolWindow {
716                         get {
717                                 if (form.FormBorderStyle == FormBorderStyle.SizableToolWindow ||
718                                     form.FormBorderStyle == FormBorderStyle.FixedToolWindow || 
719                                     form.GetCreateParams().IsSet (WindowExStyles.WS_EX_TOOLWINDOW))
720                                         return true;
721                                 return false;
722                         }
723                 }
724
725                 public int TitleBarHeight {
726                         get {
727                                 return ThemeEngine.Current.ManagedWindowTitleBarHeight (this);
728                         }
729                 }
730
731                 public int BorderWidth {
732                         get {
733                                 return ThemeEngine.Current.ManagedWindowBorderWidth (this);
734                         }
735                 }
736                 
737                 public virtual int MenuHeight {
738                         get {
739                                 return (form.Menu != null ? ThemeEngine.Current.MenuHeight : 0);
740                         }
741                 }
742
743                 protected void UpdateVP (Rectangle r)
744                 {
745                         UpdateVP (r.X, r.Y, r.Width, r.Height);
746                 }
747
748                 protected void UpdateVP (Point loc, int w, int h)
749                 {
750                         UpdateVP (loc.X, loc.Y, w, h);
751                 }
752
753                 protected void UpdateVP (int x, int y, int w, int h)
754                 {
755                         virtual_position.X = x;
756                         virtual_position.Y = y;
757                         virtual_position.Width = w;
758                         virtual_position.Height = h;
759
760                         DrawVirtualPosition (virtual_position);
761                 }
762
763                 protected virtual void HandleLButtonUp (ref Message m)
764                 {
765                         if (state == State.Idle)
766                                 return;
767
768                         ClearVirtualPosition ();
769
770                         form.Capture = false;
771                         if (state == State.Moving && form.Location != virtual_position.Location) 
772                                 form.Location = virtual_position.Location;
773                         else if (state == State.Sizing && form.Bounds != virtual_position)
774                                 form.Bounds = virtual_position;
775                         state = State.Idle;
776
777                         OnWindowFinishedMoving ();
778                 }
779
780                 private bool HandleNCLButtonUp (ref Message m)
781                 {
782                         if (form.Capture) {
783                                 ClearVirtualPosition ();
784
785                                 form.Capture = false;
786                                 state = State.Idle;
787                                 if (form.MdiContainer != null)
788                                         form.MdiContainer.SizeScrollBars();
789                         }
790                                 
791                         int x = Control.LowOrder ((int) m.LParam.ToInt32 ());
792                         int y = Control.HighOrder ((int) m.LParam.ToInt32 ());
793
794                         NCPointToClient (ref x, ref y);
795                         FormPos pos = FormPosForCoords (x, y);
796
797                         if (pos == FormPos.TitleBar) {
798                                 HandleTitleBarUp (x, y);
799                                 return true;
800                         }
801                         
802                         return true;
803                 }
804                 
805                 protected void DrawTitleButton (Graphics dc, TitleButton button, Rectangle clip)
806                 {
807                         if (!button.Rectangle.IntersectsWith (clip))
808                                 return;
809
810                         ThemeEngine.Current.ManagedWindowDrawMenuButton (dc, button, clip, this);
811                 }
812
813                 public virtual void DrawMaximizedButtons (object sender, PaintEventArgs pe)
814                 {
815                 }
816
817                 protected Point MouseMove (Point pos)
818                 {
819                         return new Point (pos.X - start.X, pos.Y - start.Y);
820                 }
821
822                 protected virtual void DrawVirtualPosition (Rectangle virtual_position)
823                 {
824                         form.Bounds = virtual_position;
825                         start = Cursor.Position;
826                 }
827
828                 protected virtual void ClearVirtualPosition ()
829                 {
830                         
831                 }
832
833                 protected virtual void OnWindowFinishedMoving ()
834                 {
835                 }
836
837                 protected virtual void NCPointToClient(ref int x, ref int y) {
838                         form.PointToClient(ref x, ref y);
839                         NCClientToNC (ref x, ref y);
840                 }
841
842                 protected virtual void NCClientToNC (ref int x, ref int y) {
843                         y += TitleBarHeight;
844                         y += BorderWidth;
845                         y += MenuHeight;
846                 }
847                 
848                 internal Point GetMenuOrigin ()
849                 {
850                         return new Point (BorderWidth, BorderWidth + TitleBarHeight);
851                 }
852                 
853                 protected FormPos FormPosForCoords (int x, int y)
854                 {
855                         int bw = BorderWidth;
856                         if (y < TitleBarHeight + bw) {
857                                 //      Console.WriteLine ("A");
858                                 if (y > bw && x > bw &&
859                                                 x < form.Width - bw)
860                                         return FormPos.TitleBar;
861
862                                 if (x < bw || (x < 20 && y < bw))
863                                         return FormPos.TopLeft;
864
865                                 if (x > form.Width - bw ||
866                                         (x > form.Width - 20 && y < bw))
867                                         return FormPos.TopRight;
868
869                                 if (y < bw)
870                                         return FormPos.Top;
871
872                         } else if (y > form.Height - 20) {
873                                 //      Console.WriteLine ("B");
874                                 if (x < bw ||
875                                                 (x < 20 && y > form.Height - bw))
876                                         return FormPos.BottomLeft;
877
878                                 if (x > form.Width - (bw * 2) ||
879                                                 (x > form.Width - 20 &&
880                                                  y > form.Height - bw))
881                                         return FormPos.BottomRight;
882
883                                 if (y > form.Height - (bw * 2))
884                                         return FormPos.Bottom;
885
886
887                         } else if (x < bw) {
888                                 //      Console.WriteLine ("C");
889                                 return FormPos.Left;
890                         } else if (x > form.Width - (bw * 2)) {
891 //                              Console.WriteLine ("D");
892                                 return FormPos.Right;
893                         } else {
894                                 //                      Console.WriteLine ("E   {0}", form.Width - bw);
895                         }
896                         
897                         return FormPos.None;
898                 }
899         }
900         internal class TitleButton
901         {
902                 public Rectangle Rectangle;
903                 public ButtonState State;
904                 public CaptionButton Caption;
905                 private EventHandler Clicked;
906                 public bool Visible;
907                 bool entered;
908
909                 public TitleButton (CaptionButton caption, EventHandler clicked)
910                 {
911                         Caption = caption;
912                         Clicked = clicked;
913                 }
914                 
915                 public void OnClick ()
916                 {
917                         if (Clicked != null) {
918                                 Clicked (this, EventArgs.Empty);
919                         }
920                 }
921
922                 public bool Entered {
923                         get { return entered; }
924                         set { entered = value; }
925                 }
926         }
927
928         internal class TitleButtons : System.Collections.IEnumerable
929         {
930                 public TitleButton MinimizeButton;
931                 public TitleButton MaximizeButton;
932                 public TitleButton RestoreButton;
933                 public TitleButton CloseButton;
934                 public TitleButton HelpButton;
935
936                 public TitleButton [] AllButtons;
937                 public bool Visible;
938
939                 private ToolTip.ToolTipWindow tooltip;
940                 private Timer tooltip_timer;
941                 private TitleButton tooltip_hovered_button;
942                 private TitleButton tooltip_hidden_button;
943                 private const int tooltip_hide_interval = 3000;
944                 private const int tooltip_show_interval = 1000;
945                 private Form form;
946                 
947                 public TitleButtons (Form frm)
948                 {
949                         this.form = frm;
950                         this.Visible = true;
951                         
952                         MinimizeButton = new TitleButton (CaptionButton.Minimize, new EventHandler (ClickHandler));
953                         MaximizeButton = new TitleButton (CaptionButton.Maximize, new EventHandler (ClickHandler));
954                         RestoreButton = new TitleButton (CaptionButton.Restore, new EventHandler (ClickHandler));
955                         CloseButton = new TitleButton (CaptionButton.Close, new EventHandler (ClickHandler));
956                         HelpButton = new TitleButton (CaptionButton.Help, new EventHandler (ClickHandler));
957
958                         AllButtons = new TitleButton [] { MinimizeButton, MaximizeButton, RestoreButton, CloseButton, HelpButton };
959                 }
960                 
961                 private void ClickHandler (object sender, EventArgs e)
962                 {
963                         if (!Visible) {
964                                 return;
965                         }
966                         
967                         TitleButton button = (TitleButton) sender;
968                         
969                         switch (button.Caption) {
970                                 case CaptionButton.Close: 
971                                         form.Close ();
972                                         break;
973                                 case CaptionButton.Help:
974                                         Console.WriteLine ("Help not implemented.");
975                                         break;
976                                 case CaptionButton.Maximize:
977                                         form.WindowState = FormWindowState.Maximized;
978                                         break;
979                                 case CaptionButton.Minimize:
980                                         form.WindowState = FormWindowState.Minimized;
981                                         break;
982                                 case CaptionButton.Restore:
983                                         form.WindowState = FormWindowState.Normal;
984                                         break;
985                         }
986                 }
987                 
988                 public TitleButton FindButton (int x, int y)
989                 {
990                         if (!Visible) {
991                                 return null;
992                         }
993                         
994                         foreach (TitleButton button in AllButtons) {
995                                 if (button.Visible && button.Rectangle.Contains (x, y)) {
996                                         return button;
997                                 }
998                         }
999                         return null;
1000                 }
1001                 
1002                 public bool AnyPushedTitleButtons {
1003                         get {
1004                                 if (!Visible) {
1005                                         return false;
1006                                 }
1007                                 
1008                                 foreach (TitleButton button in AllButtons) {
1009                                         if (button.Visible && button.State == ButtonState.Pushed) {
1010                                                 return true;
1011                                         }
1012                                 }
1013                                 return false;
1014                         }
1015                 }
1016
1017                 #region IEnumerable Members
1018
1019                 public System.Collections.IEnumerator GetEnumerator ()
1020                 {
1021                         return AllButtons.GetEnumerator ();
1022                 }
1023                 #endregion
1024
1025                 #region ToolTip helpers
1026                 // Called from MouseMove if mouse is over a button
1027                 public void ToolTipStart (TitleButton button)
1028                 {
1029                         tooltip_hovered_button = button;
1030
1031                         if (tooltip_hovered_button == tooltip_hidden_button)
1032                                 return;
1033                         tooltip_hidden_button = null;
1034
1035                         if (tooltip != null && tooltip.Visible)
1036                                 ToolTipShow (true);
1037
1038                         if (tooltip_timer == null) {
1039
1040                                 tooltip_timer = new Timer ();
1041                                 tooltip_timer.Tick += new EventHandler (ToolTipTimerTick);
1042                         }
1043
1044                         tooltip_timer.Interval = tooltip_show_interval;
1045                         tooltip_timer.Start ();
1046                         tooltip_hovered_button = button;
1047                 }
1048
1049                 public void ToolTipTimerTick (object sender, EventArgs e)
1050                 {
1051                         if (tooltip_timer.Interval == tooltip_hide_interval) {
1052                                 tooltip_hidden_button = tooltip_hovered_button;
1053                                 ToolTipHide (false);
1054                         } else {
1055                                 ToolTipShow (false);
1056                         }
1057                 }
1058                 // Called from timer (with only_refresh = false)
1059                 // Called from ToolTipStart if tooltip is already shown (with only_refresh = true)
1060                 public void ToolTipShow (bool only_refresh)
1061                 {
1062                         if (!form.Visible)
1063                                 return;
1064
1065                         string text = Locale.GetText (tooltip_hovered_button.Caption.ToString ());
1066
1067                         tooltip_timer.Interval = tooltip_hide_interval;
1068                         tooltip_timer.Enabled = true;
1069
1070                         if (only_refresh && (tooltip == null || !tooltip.Visible)) {
1071                                 return;
1072                         }
1073
1074                         if (tooltip == null)
1075                                 tooltip = new ToolTip.ToolTipWindow ();
1076                         else if (tooltip.Text == text && tooltip.Visible)
1077                                 return;
1078                         else if (tooltip.Visible)
1079                                 tooltip.Visible = false;
1080
1081                         if (form.WindowState == FormWindowState.Maximized && form.MdiParent != null)
1082                                 tooltip.Present (form.MdiParent, text);
1083                         else
1084                                 tooltip.Present (form, text);
1085                         
1086                 }
1087                 
1088                 // Called from MouseLeave (with reset_hidden_button = true)
1089                 // Called from MouseDown  (with reset_hidden_button = false)
1090                 // Called from MouseMove if mouse isn't over any button (with reset_hidden_button = false)
1091                 // Called from Timer if hiding (with reset_hidden_button = false)
1092                 public void ToolTipHide (bool reset_hidden_button)
1093                 {
1094                         if (tooltip_timer != null)
1095                                 tooltip_timer.Enabled = false;
1096                         if (tooltip != null && tooltip.Visible)
1097                                 tooltip.Visible = false;
1098                         if (reset_hidden_button)
1099                                 tooltip_hidden_button = null;
1100                 }
1101                 #endregion
1102                 
1103                 public bool MouseMove (int x, int y)
1104                 {
1105                         if (!Visible) {
1106                                 return false;
1107                         }
1108
1109                         bool any_change = false;
1110                         bool any_pushed_buttons = AnyPushedTitleButtons;
1111                         bool any_tooltip = false;
1112                         TitleButton over_button = FindButton (x, y);
1113
1114                         foreach (TitleButton button in this) {
1115                                 if (button == null)
1116                                         continue;
1117                                 
1118                                 if (button.State == ButtonState.Inactive)
1119                                         continue;
1120                                         
1121                                 if (button == over_button) {
1122                                         if (any_pushed_buttons) {
1123                                                 any_change |= button.State != ButtonState.Pushed;
1124                                                 button.State = ButtonState.Pushed;
1125                                         }
1126                                         ToolTipStart (button);
1127                                         any_tooltip = true;
1128                                         if (!button.Entered) {
1129                                                 button.Entered = true;
1130                                                 if (ThemeEngine.Current.ManagedWindowTitleButtonHasHotElementStyle (button, form))
1131                                                         any_change = true;
1132                                         }
1133                                 } else {
1134                                         if (any_pushed_buttons) {
1135                                                 any_change |= button.State != ButtonState.Normal;
1136                                                 button.State = ButtonState.Normal;
1137                                         }
1138                                         if (button.Entered) {
1139                                                 button.Entered = false;
1140                                                 if (ThemeEngine.Current.ManagedWindowTitleButtonHasHotElementStyle (button, form))
1141                                                         any_change = true;
1142                                         }
1143                                 }
1144                         }
1145
1146                         if (!any_tooltip)
1147                                 ToolTipHide (false);
1148
1149                         return any_change;
1150                 }
1151
1152                 public void MouseDown (int x, int y)
1153                 {
1154                         if (!Visible) {
1155                                 return;
1156                         }
1157
1158                         ToolTipHide (false);
1159
1160                         foreach (TitleButton button in this) {
1161                                 if (button != null && button.State != ButtonState.Inactive) {
1162                                         button.State = ButtonState.Normal;
1163                                 }
1164                         }
1165                         TitleButton clicked_button = FindButton (x, y);
1166                         if (clicked_button != null && clicked_button.State != ButtonState.Inactive) {
1167                                 clicked_button.State = ButtonState.Pushed;
1168                         }
1169                 }
1170
1171                 public void MouseUp (int x, int y)
1172                 {
1173                         if (!Visible) {
1174                                 return;
1175                         }
1176                         
1177                         TitleButton clicked_button = FindButton (x, y);
1178                         if (clicked_button != null && clicked_button.State != ButtonState.Inactive) {
1179                                 clicked_button.OnClick ();
1180                         }
1181
1182                         foreach (TitleButton button in this) {
1183                                 if (button == null || button.State == ButtonState.Inactive)
1184                                         continue;
1185
1186                                 button.State = ButtonState.Normal;
1187                         }
1188
1189                         if (clicked_button == CloseButton && !form.closing)
1190                                 XplatUI.InvalidateNC (form.Handle);
1191                                 
1192                         ToolTipHide (true);
1193                 }
1194
1195                 internal void MouseLeave (int x, int y)
1196                 {
1197                         if (!Visible) {
1198                                 return;
1199                         }
1200                         
1201                         foreach (TitleButton button in this) {
1202                                 if (button == null || button.State == ButtonState.Inactive)
1203                                         continue;
1204
1205                                 button.State = ButtonState.Normal;
1206                         }
1207                         
1208                         ToolTipHide (true);
1209                 }
1210         }
1211 }
1212
1213