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