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