2008-06-04 George Giolfan <georgegiolfan@yahoo.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 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                         // clip region is not correct on win32.
263                         // use the entire form's area.
264                         clip = new Rectangle (0, 0, form.Width, form.Height);
265                         ThemeEngine.Current.DrawManagedWindowDecorations (pe.Graphics, clip, this);
266                         XplatUI.PaintEventEnd (ref m, form.Handle, false);
267                         return true;
268                 }
269
270                 protected virtual bool HandleNCCalcSize (ref Message m)
271                 {
272                         XplatUIWin32.NCCALCSIZE_PARAMS ncp;
273                         XplatUIWin32.RECT rect;
274
275                         if (m.WParam == (IntPtr)1) {
276                                 ncp = (XplatUIWin32.NCCALCSIZE_PARAMS)Marshal.PtrToStructure (m.LParam,
277                                                 typeof (XplatUIWin32.NCCALCSIZE_PARAMS));
278                                 
279                                 ncp.rgrc1 = NCCalcSize (ncp.rgrc1);
280
281                                 Marshal.StructureToPtr (ncp, m.LParam, true);
282                         } else {
283                                 rect = (XplatUIWin32.RECT) Marshal.PtrToStructure (m.LParam, typeof (XplatUIWin32.RECT));
284                                 
285                                 rect = NCCalcSize (rect);
286                                 
287                                 Marshal.StructureToPtr (rect, m.LParam, true);
288                         }
289                         
290                         return true;
291                 }
292
293                 protected virtual XplatUIWin32.RECT NCCalcSize (XplatUIWin32.RECT proposed_window_rect)
294                 {
295                         int bw = ThemeEngine.Current.ManagedWindowBorderWidth (this);
296
297                         if (HasBorders) {
298                                 proposed_window_rect.top += TitleBarHeight + bw;
299                                 proposed_window_rect.bottom -= bw;
300                                 proposed_window_rect.left += bw;
301                                 proposed_window_rect.right -= bw;
302                         }
303
304                         // This is necessary for Linux, can't handle <= 0-sized 
305                         // client areas correctly.
306                         if (proposed_window_rect.right <= proposed_window_rect.left) {
307                                 proposed_window_rect.right += proposed_window_rect.left - proposed_window_rect.right + 1;
308                         }
309                         if (proposed_window_rect.top >= proposed_window_rect.bottom) {
310                                 proposed_window_rect.bottom += proposed_window_rect.top - proposed_window_rect.bottom + 1;
311                         }
312
313                         return proposed_window_rect;
314                 }
315
316                 protected virtual bool HandleNCHitTest (ref Message m)
317                 {
318
319                         int x = Control.LowOrder ((int)m.LParam.ToInt32 ());
320                         int y = Control.HighOrder ((int)m.LParam.ToInt32 ());
321
322                         NCPointToClient (ref x, ref y);
323
324                         FormPos pos = FormPosForCoords (x, y);
325
326                         if (pos == FormPos.TitleBar) {
327                                 m.Result = new IntPtr ((int)HitTest.HTCAPTION);
328                                 return true;
329                         }
330
331                         if (!IsSizable)
332                                 return false;
333
334                         switch (pos) {
335                         case FormPos.Top:
336                                 m.Result = new IntPtr ((int)HitTest.HTTOP);
337                                 break;
338                         case FormPos.Left:
339                                 m.Result = new IntPtr ((int)HitTest.HTLEFT);
340                                 break;
341                         case FormPos.Right:
342                                 m.Result = new IntPtr ((int)HitTest.HTRIGHT);
343                                 break;
344                         case FormPos.Bottom:
345                                 m.Result = new IntPtr ((int)HitTest.HTBOTTOM);
346                                 break;
347                         case FormPos.TopLeft:
348                                 m.Result = new IntPtr ((int)HitTest.HTTOPLEFT);
349                                 break;
350                         case FormPos.TopRight:
351                                 m.Result = new IntPtr ((int)HitTest.HTTOPRIGHT);
352                                 break;
353                         case FormPos.BottomLeft:
354                                 m.Result = new IntPtr ((int)HitTest.HTBOTTOMLEFT);
355                                 break;
356                         case FormPos.BottomRight:
357                                 m.Result = new IntPtr ((int)HitTest.HTBOTTOMRIGHT);
358                                 break;
359                         default:
360                                 // We return false so that DefWndProc handles things
361                                 return false;
362                         }
363                         return true;
364                 }
365
366                 public virtual void UpdateBorderStyle (FormBorderStyle border_style)
367                 {
368                         if (form.IsHandleCreated) {
369                                 XplatUI.SetBorderStyle (form.Handle, border_style);
370                         }
371
372                         if (ShouldRemoveWindowManager (border_style)) {
373                                 form.RemoveWindowManager ();
374                                 return;
375                         }
376                                 
377                         ThemeEngine.Current.ManagedWindowSetButtonLocations (this);
378                 }
379
380                 
381                 
382                 public virtual void SetWindowState (FormWindowState old_state, FormWindowState window_state)
383                 {
384                         UpdateWindowState (old_state, window_state, false);
385                 }
386
387                 public virtual FormWindowState GetWindowState ()
388                 {
389                         return form.window_state;
390                 }
391
392                 public virtual void PointToClient (ref int x, ref int y)
393                 {
394                         // toolwindows stay in screencoords we just have to make sure
395                         // they obey the working area
396                         Rectangle working = SystemInformation.WorkingArea;
397
398                         if (x > working.Right)
399                                 x = working.Right;
400                         if (x < working.Left)
401                                 x = working.Left;
402
403                         if (y < working.Top)
404                                 y = working.Top;
405                         if (y > working.Bottom)
406                                 y = working.Bottom;
407                 }
408
409                 public virtual void PointToScreen (ref int x, ref int y)
410                 {
411                         XplatUI.ClientToScreen (form.Handle, ref x, ref y);
412                 }
413
414                 protected virtual bool ShouldRemoveWindowManager (FormBorderStyle style)
415                 {
416                         return style != FormBorderStyle.FixedToolWindow && style != FormBorderStyle.SizableToolWindow;
417                 }
418
419                 public bool IconRectangleContains (int x, int y)
420                 {
421                         if (form.Icon == null)
422                                 return false;
423
424                         int bw = ThemeEngine.Current.ManagedWindowBorderWidth (this);
425                         Rectangle icon = new Rectangle (bw + 3, bw + 2, IconWidth, IconWidth);
426                         return icon.Contains (x, y);
427                 }
428
429                 protected virtual void Activate ()
430                 {
431                         form.Refresh ();
432                 }
433
434                 public virtual bool IsActive {
435                         get {
436                                 return true;
437                         }
438                 }
439
440
441                 private void FormSizeChangedHandler (object sender, EventArgs e)
442                 {
443                         if (form.IsHandleCreated) {
444                                 ThemeEngine.Current.ManagedWindowSetButtonLocations (this);
445                                 XplatUI.InvalidateNC (form.Handle);
446                         }
447                 }
448
449                 protected virtual bool HandleRButtonDown (ref Message m)
450                 {
451                         Activate ();
452                         return false;
453                 }
454                 
455                 protected virtual bool HandleLButtonDown (ref Message m)
456                 {
457                         Activate ();
458                         return false;
459                 }
460
461                 protected virtual bool HandleLButtonDblClick(ref Message m)
462                 {
463                         return false;
464                 }
465                 
466                 protected virtual bool HandleNCMouseLeave (ref Message m)
467                 {
468                         int x = Control.LowOrder ((int)m.LParam.ToInt32 ());
469                         int y = Control.HighOrder ((int)m.LParam.ToInt32 ());
470
471                         NCPointToClient (ref x, ref y);
472                         FormPos pos = FormPosForCoords (x, y);
473
474                         if (pos != FormPos.TitleBar) {
475                                 HandleTitleBarLeave (x, y);
476                                 return true;
477                         }
478
479                         return true;
480                 }
481                 
482                 protected virtual bool HandleNCMouseMove (ref Message m)
483                 {
484                         int x = Control.LowOrder((int)m.LParam.ToInt32( ));
485                         int y = Control.HighOrder((int)m.LParam.ToInt32( ));
486
487                         NCPointToClient (ref x, ref y);
488                         FormPos pos = FormPosForCoords (x, y);
489
490                         if (pos == FormPos.TitleBar) {
491                                 HandleTitleBarMouseMove (x, y);
492                                 return true;
493                         }
494
495                         if (form.ActiveMenu != null && XplatUI.IsEnabled (form.Handle)) {
496                                 MouseEventArgs mea = new MouseEventArgs (Form.FromParamToMouseButtons (m.WParam.ToInt32 ()), form.mouse_clicks, x, y, 0);
497                                 form.ActiveMenu.OnMouseMove (form, mea);
498                         }
499
500                         return true;
501                         
502                 }
503                 
504                 protected virtual bool HandleNCLButtonDown (ref Message m)
505                 {
506                         Activate ();
507
508                         start = Cursor.Position;
509                         virtual_position = form.Bounds;
510                         
511                         int x = Control.LowOrder ((int) m.LParam.ToInt32 ());
512                         int y = Control.HighOrder ((int) m.LParam.ToInt32 ());
513                         
514                         // Need to adjust because we are in NC land
515                         NCPointToClient (ref x, ref y);
516                         FormPos pos = FormPosForCoords (x, y);
517                         
518                         if (form.ActiveMenu != null && XplatUI.IsEnabled (form.Handle)) {
519                                 MouseEventArgs mea = new MouseEventArgs (Form.FromParamToMouseButtons (m.WParam.ToInt32 ()), form.mouse_clicks, x, y - TitleBarHeight, 0);
520                                 form.ActiveMenu.OnMouseDown (form, mea);
521                         }
522                         
523                         if (pos == FormPos.TitleBar) {
524                                 HandleTitleBarDown (x, y);
525                                 return true;
526                         }
527
528                         if (IsSizable) {
529                                 if ((pos & FormPos.AnyEdge) == 0)
530                                         return false;
531
532                                 virtual_position = form.Bounds;
533                                 state = State.Sizing;
534                                 sizing_edge = pos;
535                                 form.Capture = true;
536                                 return true;
537                         }
538
539                         return false;
540                 }
541
542                 protected virtual void HandleNCLButtonDblClick (ref Message m)
543                 {
544                         int x = Control.LowOrder ((int)m.LParam.ToInt32 ());
545                         int y = Control.HighOrder ((int)m.LParam.ToInt32 ());
546
547                         // Need to adjust because we are in NC land
548                         NCPointToClient (ref x, ref y);
549
550                         FormPos pos = FormPosForCoords (x, y);
551                         if (pos == FormPos.TitleBar || pos == FormPos.Top)
552                                 HandleTitleBarDoubleClick (x, y);
553
554                 }
555                 
556                 protected virtual void HandleTitleBarDoubleClick (int x, int y)
557                 {
558                 
559                 }
560                 
561                 protected virtual void HandleTitleBarLeave (int x, int y)
562                 {
563                         title_buttons.MouseLeave (x, y);
564                 }
565                 
566                 protected virtual void HandleTitleBarMouseMove (int x, int y)
567                 {
568                         bool any_change = false;
569                         
570                         any_change = title_buttons.MouseMove (x, y);
571                         
572                         if (any_change) {
573                                 if (IsMaximized && form.IsMdiChild)
574                                         XplatUI.InvalidateNC (form.MdiParent.Handle);
575                                 else
576                                         XplatUI.InvalidateNC (form.Handle);
577                         }
578                 }
579                 
580                 protected virtual void HandleTitleBarUp (int x, int y)
581                 {
582                         title_buttons.MouseUp (x, y);
583
584                         return;
585                 }
586                 
587                 protected virtual void HandleTitleBarDown (int x, int y)
588                 {
589                         title_buttons.MouseDown (x, y);
590
591                         if (!TitleButtons.AnyPushedTitleButtons && !IsMaximized) {
592                                 state = State.Moving;
593                                 clicked_point = new Point (x, y);
594                                 if (form.Parent != null) {
595                                         form.CaptureWithConfine (form.Parent);
596                                 } else {
597                                         form.Capture = true;
598                                 }
599                         }
600                         
601                         XplatUI.InvalidateNC (form.Handle);
602                 }
603
604                 private bool HandleMouseMove (Form form, ref Message m)
605                 {
606                         switch (state) {
607                         case State.Moving:
608                                 HandleWindowMove (m);
609                                 return true;
610                         case State.Sizing:
611                                 HandleSizing (m);
612                                 return true;
613                         }
614                         
615                         return false;
616                 }
617
618                 private void HandleMouseLeave (ref Message m)
619                 {
620                         form.ResetCursor ();
621                 }
622         
623                 protected virtual void HandleWindowMove (Message m)
624                 {
625                         Point move = MouseMove (m);
626
627                         UpdateVP (virtual_position.X + move.X, virtual_position.Y + move.Y,
628                                         virtual_position.Width, virtual_position.Height);
629                 }
630
631                 private void HandleSizing (Message m)
632                 {
633                         Rectangle pos = virtual_position;
634                         Size minimum_size = SystemInformation.MinWindowTrackSize;
635                         int mw = minimum_size.Width;
636                         int mh = minimum_size.Height;
637                         int x = Cursor.Position.X;
638                         int y = Cursor.Position.Y;
639
640                         PointToClient (ref x, ref y);
641
642                         if ((sizing_edge & FormPos.Top) != 0) {
643                                 if (pos.Bottom - y < mh)
644                                         y = pos.Bottom - mh;
645                                 pos.Height = pos.Bottom - y;
646                                 pos.Y = y;
647                         } else if ((sizing_edge & FormPos.Bottom) != 0) {
648                                 int height = y - pos.Top;
649                                 if (height <= mh)
650                                         height = mh;
651                                 pos.Height = height;
652                         }
653
654                         if ((sizing_edge & FormPos.Left) != 0) {
655                                 if (pos.Right - x < mw)
656                                         x = pos.Right - mw;
657                                 pos.Width = pos.Right - x;
658                                 pos.X = x;
659                         } else if ((sizing_edge & FormPos.Right) != 0) {
660                                 int width = x - form.Left;
661                                 if (width <= mw)
662                                         width = mw;
663                                 pos.Width = width;
664                         }
665
666                         UpdateVP (pos);
667                 }
668
669                 public bool IsMaximized {
670                         get { return GetWindowState () == FormWindowState.Maximized; }
671                 }
672
673                 public bool IsMinimized {
674                         get { return GetWindowState () == FormWindowState.Minimized; }
675                 }
676
677                 public bool IsSizable {
678                         get {
679                                 switch (form.FormBorderStyle) {
680                                 case FormBorderStyle.Sizable:
681                                 case FormBorderStyle.SizableToolWindow:
682                                         return (form.window_state != FormWindowState.Minimized);
683                                 default:
684                                         return false;
685                                 }
686                         }
687                 }
688
689                 public bool HasBorders {
690                         get {
691                                 return form.FormBorderStyle != FormBorderStyle.None;
692                         }
693                 }
694
695                 public bool IsToolWindow {
696                         get {
697                                 if (form.FormBorderStyle == FormBorderStyle.SizableToolWindow ||
698                                                 form.FormBorderStyle == FormBorderStyle.FixedToolWindow)
699                                         return true;
700                                 return false;
701                         }
702                 }
703
704                 public int TitleBarHeight {
705                         get {
706                                 return ThemeEngine.Current.ManagedWindowTitleBarHeight (this);
707                         }
708                 }
709
710                 public int BorderWidth {
711                         get {
712                                 return ThemeEngine.Current.ManagedWindowBorderWidth (this);
713                         }
714                 }
715                 
716                 public virtual int MenuHeight {
717                         get {
718                                 return (form.Menu != null ? ThemeEngine.Current.MenuHeight : 0);
719                         }
720                 }
721
722                 protected void UpdateVP (Rectangle r)
723                 {
724                         UpdateVP (r.X, r.Y, r.Width, r.Height);
725                 }
726
727                 protected void UpdateVP (Point loc, int w, int h)
728                 {
729                         UpdateVP (loc.X, loc.Y, w, h);
730                 }
731
732                 protected void UpdateVP (int x, int y, int w, int h)
733                 {
734                         virtual_position.X = x;
735                         virtual_position.Y = y;
736                         virtual_position.Width = w;
737                         virtual_position.Height = h;
738
739                         DrawVirtualPosition (virtual_position);
740                 }
741
742                 protected virtual void HandleLButtonUp (ref Message m)
743                 {
744                         if (state == State.Idle)
745                                 return;
746
747                         ClearVirtualPosition ();
748
749                         form.Capture = false;
750                         if (state == State.Moving && form.Location != virtual_position.Location) 
751                                 form.Location = virtual_position.Location;
752                         else if (state == State.Sizing && form.Bounds != virtual_position)
753                                 form.Bounds = virtual_position;
754                         state = State.Idle;
755
756                         OnWindowFinishedMoving ();
757                 }
758
759                 private bool HandleNCLButtonUp (ref Message m)
760                 {
761                         if (form.Capture) {
762                                 ClearVirtualPosition ();
763
764                                 form.Capture = false;
765                                 state = State.Idle;
766                                 if (form.MdiContainer != null)
767                                         form.MdiContainer.SizeScrollBars();
768                         }
769                                 
770                         int x = Control.LowOrder ((int) m.LParam.ToInt32 ());
771                         int y = Control.HighOrder ((int) m.LParam.ToInt32 ());
772
773                         NCPointToClient (ref x, ref y);
774                         FormPos pos = FormPosForCoords (x, y);
775
776                         if (pos == FormPos.TitleBar) {
777                                 HandleTitleBarUp (x, y);
778                                 return true;
779                         }
780                         
781                         return true;
782                 }
783                 
784                 protected void DrawTitleButton (Graphics dc, TitleButton button, Rectangle clip)
785                 {
786                         if (!button.Rectangle.IntersectsWith (clip))
787                                 return;
788
789                         dc.FillRectangle (SystemBrushes.Control, button.Rectangle);
790                         ControlPaint.DrawCaptionButton (dc, button.Rectangle,
791                                         button.Caption, button.State);
792                 }
793
794                 public virtual void DrawMaximizedButtons (object sender, PaintEventArgs pe)
795                 {
796                 }
797
798                 protected Point MouseMove (Message m)
799                 {
800                         Point cp = Cursor.Position;
801                         return new Point (cp.X - start.X, cp.Y - start.Y);
802                 }
803
804                 protected virtual void DrawVirtualPosition (Rectangle virtual_position)
805                 {
806                         form.Bounds = virtual_position;
807                         start = Cursor.Position;
808                 }
809
810                 protected virtual void ClearVirtualPosition ()
811                 {
812                         
813                 }
814
815                 protected virtual void OnWindowFinishedMoving ()
816                 {
817                 }
818
819                 protected virtual void NCPointToClient(ref int x, ref int y) {
820                         form.PointToClient(ref x, ref y);
821                         NCClientToNC (ref x, ref y);
822                 }
823
824                 protected virtual void NCClientToNC (ref int x, ref int y) {
825                         y += TitleBarHeight;
826                         y += BorderWidth;
827                         y += MenuHeight;
828                 }
829                 
830                 internal Point GetMenuOrigin ()
831                 {
832                         return new Point (BorderWidth, BorderWidth + TitleBarHeight);
833                 }
834                 
835                 protected FormPos FormPosForCoords (int x, int y)
836                 {
837                         int bw = BorderWidth;
838                         if (y < TitleBarHeight + bw) {
839                                 //      Console.WriteLine ("A");
840                                 if (y > bw && x > bw &&
841                                                 x < form.Width - bw)
842                                         return FormPos.TitleBar;
843
844                                 if (x < bw || (x < 20 && y < bw))
845                                         return FormPos.TopLeft;
846
847                                 if (x > form.Width - bw ||
848                                         (x > form.Width - 20 && y < bw))
849                                         return FormPos.TopRight;
850
851                                 if (y < bw)
852                                         return FormPos.Top;
853
854                         } else if (y > form.Height - 20) {
855                                 //      Console.WriteLine ("B");
856                                 if (x < bw ||
857                                                 (x < 20 && y > form.Height - bw))
858                                         return FormPos.BottomLeft;
859
860                                 if (x > form.Width - (bw * 2) ||
861                                                 (x > form.Width - 20 &&
862                                                  y > form.Height - bw))
863                                         return FormPos.BottomRight;
864
865                                 if (y > form.Height - (bw * 2))
866                                         return FormPos.Bottom;
867
868
869                         } else if (x < bw) {
870                                 //      Console.WriteLine ("C");
871                                 return FormPos.Left;
872                         } else if (x > form.Width - (bw * 2)) {
873 //                              Console.WriteLine ("D");
874                                 return FormPos.Right;
875                         } else {
876                                 //                      Console.WriteLine ("E   {0}", form.Width - bw);
877                         }
878                         
879                         return FormPos.None;
880                 }
881         }
882         internal class TitleButton
883         {
884                 public Rectangle Rectangle;
885                 public ButtonState State;
886                 public CaptionButton Caption;
887                 private EventHandler Clicked;
888                 public bool Visible;
889
890                 public TitleButton (CaptionButton caption, EventHandler clicked)
891                 {
892                         Caption = caption;
893                         Clicked = clicked;
894                 }
895                 
896                 public void OnClick ()
897                 {
898                         if (Clicked != null) {
899                                 Clicked (this, EventArgs.Empty);
900                         }
901                 }
902         }
903
904         internal class TitleButtons : System.Collections.IEnumerable
905         {
906                 public TitleButton MinimizeButton;
907                 public TitleButton MaximizeButton;
908                 public TitleButton RestoreButton;
909                 public TitleButton CloseButton;
910                 public TitleButton HelpButton;
911
912                 public TitleButton [] AllButtons;
913                 public bool Visible;
914
915                 private ToolTip.ToolTipWindow tooltip;
916                 private Timer tooltip_timer;
917                 private TitleButton tooltip_hovered_button;
918                 private TitleButton tooltip_hidden_button;
919                 private const int tooltip_hide_interval = 3000;
920                 private const int tooltip_show_interval = 1000;
921                 private Form form;
922                 
923                 public TitleButtons (Form frm)
924                 {
925                         this.form = frm;
926                         this.Visible = true;
927                         
928                         MinimizeButton = new TitleButton (CaptionButton.Minimize, new EventHandler (ClickHandler));
929                         MaximizeButton = new TitleButton (CaptionButton.Maximize, new EventHandler (ClickHandler));
930                         RestoreButton = new TitleButton (CaptionButton.Restore, new EventHandler (ClickHandler));
931                         CloseButton = new TitleButton (CaptionButton.Close, new EventHandler (ClickHandler));
932                         HelpButton = new TitleButton (CaptionButton.Help, new EventHandler (ClickHandler));
933
934                         AllButtons = new TitleButton [] { MinimizeButton, MaximizeButton, RestoreButton, CloseButton, HelpButton };
935                 }
936                 
937                 private void ClickHandler (object sender, EventArgs e)
938                 {
939                         if (!Visible) {
940                                 return;
941                         }
942                         
943                         TitleButton button = (TitleButton) sender;
944                         
945                         switch (button.Caption) {
946                                 case CaptionButton.Close: 
947                                         form.Close ();
948                                         break;
949                                 case CaptionButton.Help:
950                                         Console.WriteLine ("Help not implemented.");
951                                         break;
952                                 case CaptionButton.Maximize:
953                                         form.WindowState = FormWindowState.Maximized;
954                                         break;
955                                 case CaptionButton.Minimize:
956                                         form.WindowState = FormWindowState.Minimized;
957                                         break;
958                                 case CaptionButton.Restore:
959                                         form.WindowState = FormWindowState.Normal;
960                                         break;
961                         }
962                 }
963                 
964                 public TitleButton FindButton (int x, int y)
965                 {
966                         if (!Visible) {
967                                 return null;
968                         }
969                         
970                         foreach (TitleButton button in AllButtons) {
971                                 if (button.Visible && button.Rectangle.Contains (x, y)) {
972                                         return button;
973                                 }
974                         }
975                         return null;
976                 }
977                 
978                 public bool AnyPushedTitleButtons {
979                         get {
980                                 if (!Visible) {
981                                         return false;
982                                 }
983                                 
984                                 foreach (TitleButton button in AllButtons) {
985                                         if (button.Visible && button.State == ButtonState.Pushed) {
986                                                 return true;
987                                         }
988                                 }
989                                 return false;
990                         }
991                 }
992
993                 #region IEnumerable Members
994
995                 public System.Collections.IEnumerator GetEnumerator ()
996                 {
997                         return AllButtons.GetEnumerator ();
998                 }
999                 #endregion
1000
1001                 #region ToolTip helpers
1002                 // Called from MouseMove if mouse is over a button
1003                 public void ToolTipStart (TitleButton button)
1004                 {
1005                         tooltip_hovered_button = button;
1006
1007                         if (tooltip_hovered_button == tooltip_hidden_button)
1008                                 return;
1009                         tooltip_hidden_button = null;
1010
1011                         if (tooltip != null && tooltip.Visible)
1012                                 ToolTipShow (true);
1013
1014                         if (tooltip_timer == null) {
1015
1016                                 tooltip_timer = new Timer ();
1017                                 tooltip_timer.Tick += new EventHandler (ToolTipTimerTick);
1018                         }
1019
1020                         tooltip_timer.Interval = tooltip_show_interval;
1021                         tooltip_timer.Start ();
1022                         tooltip_hovered_button = button;
1023                 }
1024
1025                 public void ToolTipTimerTick (object sender, EventArgs e)
1026                 {
1027                         if (tooltip_timer.Interval == tooltip_hide_interval) {
1028                                 tooltip_hidden_button = tooltip_hovered_button;
1029                                 ToolTipHide (false);
1030                         } else {
1031                                 ToolTipShow (false);
1032                         }
1033                 }
1034                 // Called from timer (with only_refresh = false)
1035                 // Called from ToolTipStart if tooltip is already shown (with only_refresh = true)
1036                 public void ToolTipShow (bool only_refresh)
1037                 {
1038                         if (!form.Visible)
1039                                 return;
1040
1041                         string text = Locale.GetText (tooltip_hovered_button.Caption.ToString ());
1042
1043                         tooltip_timer.Interval = tooltip_hide_interval;
1044                         tooltip_timer.Enabled = true;
1045
1046                         if (only_refresh && (tooltip == null || !tooltip.Visible)) {
1047                                 return;
1048                         }
1049
1050                         if (tooltip == null)
1051                                 tooltip = new ToolTip.ToolTipWindow ();
1052                         else if (tooltip.Text == text && tooltip.Visible)
1053                                 return;
1054                         else if (tooltip.Visible)
1055                                 tooltip.Visible = false;
1056
1057                         if (form.WindowState == FormWindowState.Maximized && form.MdiParent != null)
1058                                 tooltip.Present (form.MdiParent, text);
1059                         else
1060                                 tooltip.Present (form, text);
1061                         
1062                 }
1063                 
1064                 // Called from MouseLeave (with reset_hidden_button = true)
1065                 // Called from MouseDown  (with reset_hidden_button = false)
1066                 // Called from MouseMove if mouse isn't over any button (with reset_hidden_button = false)
1067                 // Called from Timer if hiding (with reset_hidden_button = false)
1068                 public void ToolTipHide (bool reset_hidden_button)
1069                 {
1070                         if (tooltip_timer != null)
1071                                 tooltip_timer.Enabled = false;
1072                         if (tooltip != null && tooltip.Visible)
1073                                 tooltip.Visible = false;
1074                         if (reset_hidden_button)
1075                                 tooltip_hidden_button = null;
1076                 }
1077                 #endregion
1078                 
1079                 public bool MouseMove (int x, int y)
1080                 {
1081                         if (!Visible) {
1082                                 return false;
1083                         }
1084
1085                         bool any_change = false;
1086                         bool any_pushed_buttons = AnyPushedTitleButtons;
1087                         bool any_tooltip = false;
1088                         TitleButton over_button = FindButton (x, y);
1089
1090                         foreach (TitleButton button in this) {
1091                                 if (button == null)
1092                                         continue;
1093
1094                                 if (button == over_button) {
1095                                         if (any_pushed_buttons) {
1096                                                 any_change |= button.State != ButtonState.Pushed;
1097                                                 button.State = ButtonState.Pushed;
1098                                         }
1099                                         ToolTipStart (button);
1100                                         any_tooltip = true;
1101                                 } else {
1102                                         if (any_pushed_buttons) {
1103                                                 any_change |= button.State != ButtonState.Normal;
1104                                                 button.State = ButtonState.Normal;
1105                                         }
1106                                 }
1107                         }
1108
1109                         if (!any_tooltip)
1110                                 ToolTipHide (false);
1111
1112                         return any_change;
1113                 }
1114
1115                 public void MouseDown (int x, int y)
1116                 {
1117                         if (!Visible) {
1118                                 return;
1119                         }
1120
1121                         ToolTipHide (false);
1122
1123                         foreach (TitleButton button in this) {
1124                                 if (button != null) {
1125                                         button.State = ButtonState.Normal;
1126                                 }
1127                         }
1128                         TitleButton clicked_button = FindButton (x, y);
1129                         if (clicked_button != null) {
1130                                 clicked_button.State = ButtonState.Pushed;
1131                         }
1132                 }
1133
1134                 public void MouseUp (int x, int y)
1135                 {
1136                         if (!Visible) {
1137                                 return;
1138                         }
1139                         
1140                         TitleButton clicked_button = FindButton (x, y);
1141                         if (clicked_button != null) {
1142                                 clicked_button.OnClick ();
1143                         }
1144
1145                         foreach (TitleButton button in this) {
1146                                 if (button == null)
1147                                         continue;
1148
1149                                 button.State = ButtonState.Normal;
1150                         }
1151
1152                         if (clicked_button == CloseButton && !form.closing)
1153                                 XplatUI.InvalidateNC (form.Handle);
1154                 }
1155
1156                 internal void MouseLeave (int x, int y)
1157                 {
1158                         if (!Visible) {
1159                                 return;
1160                         }
1161                         
1162                         foreach (TitleButton button in this) {
1163                                 if (button == null)
1164                                         continue;
1165
1166                                 button.State = ButtonState.Normal;
1167                         }
1168                         
1169                         ToolTipHide (true);
1170                 }
1171         }
1172 }
1173
1174