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