2010-03-12 Jb Evain <jbevain@novell.com>
[mono.git] / mcs / class / Managed.Windows.Forms / Test / System.Windows.Forms / DragAndDropTest.cs
1 //
2 // DragAndDropTest.cs: tests for general drag and drop operations.
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining
5 // a copy of this software and associated documentation files (the
6 // "Software"), to deal in the Software without restriction, including
7 // without limitation the rights to use, copy, modify, merge, publish,
8 // distribute, sublicense, and/or sell copies of the Software, and to
9 // permit persons to whom the Software is furnished to do so, subject to
10 // the following conditions:
11 // 
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
14 // 
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 //
23 // Author:
24 //   Carlos Alberto Cortez <ccortes@novell.com>
25 //
26 // (C) 2008 Novell, Inc. (http://www.novell.com)
27 //
28 // NOTE: Since this is an interactive set of tests, I didn't include it as part
29 // of the MWF test suite build. You will need to build it by yourself and then
30 // use nunit-console, since the nunit GUI has some problems with DND (basically a COM
31 // issue).
32 //
33
34 using System;
35 using System.Drawing;
36 using System.Reflection;
37 using System.Windows.Forms;
38
39 using NUnit.Framework;
40
41 namespace MonoTests.System.Windows.Forms
42 {
43         [TestFixture]
44         public class DragAndDropTest
45         {
46                 [Test]
47                 public void DropCancelledByEsc ()
48                 {
49                         DNDForm form = new DNDForm ();
50                         form.Text = MethodBase.GetCurrentMethod ().Name;
51                         form.InstructionsText =
52                                 "Instructions:" + Environment.NewLine + Environment.NewLine +
53                                 "1. Click with left button on the control on the left, holding it." + Environment.NewLine +
54                                 "2. Move mouse pointer to the control in the right. " + Environment.NewLine +
55                                 "3. While still pressing the mouse button, press ESC." + Environment.NewLine +
56                                 "4. Close the form.";
57                         form.DragControl.DragData = "hello";
58                         form.DragControl.AllowedEffects = DragDropEffects.All;
59                         form.DropControl.DropEffect = DragDropEffects.Copy;
60
61                         Application.Run (form);
62
63                         Assert.AreEqual (DragDropEffects.None, form.DragControl.PerformedEffect, "A1");
64                         Assert.AreEqual (1, form.DropControl.EnterFiredCount, "A2");
65                         Assert.AreEqual (1, form.DropControl.LeaveFiredCount, "A3");
66                         Assert.AreEqual (0, form.DropControl.DropFiredCount, "A4");
67                 }
68
69                 [Test]
70                 public void DropCancelledByQuery ()
71                 {
72                         DNDForm form = new DNDForm ();
73                         form.Text = MethodBase.GetCurrentMethod ().Name;
74                         form.InstructionsText =
75                                 "Instructions:" + Environment.NewLine + Environment.NewLine +
76                                 "1. Click with left button on the control on the left, holding it." + Environment.NewLine +
77                                 "2. Move mouse pointer to the control in the right. " + Environment.NewLine +
78                                 "3. The drop should be cancelled after some seconds." + Environment.NewLine +
79                                 "4. Close the form.";
80                         form.DragControl.DragData = "hello";
81                         form.DragControl.AllowedEffects = DragDropEffects.All;
82                         form.DropControl.DropEffect = DragDropEffects.Copy;
83
84                         query_counter = 0;
85                         form.DragControl.QueryContinueDrag += new QueryContinueDragEventHandler (DragControl_QueryContinueDrag);
86
87                         Application.Run (form);
88
89                         Assert.AreEqual (DragDropEffects.None, form.DragControl.PerformedEffect, "A1");
90                         Assert.AreEqual (1, form.DropControl.EnterFiredCount, "A2");
91                         Assert.AreEqual (1, form.DropControl.LeaveFiredCount, "A3");
92                         Assert.AreEqual (0, form.DropControl.DropFiredCount, "A4");
93                 }
94
95                 int query_counter = 0;
96
97                 void DragControl_QueryContinueDrag (object sender, QueryContinueDragEventArgs e)
98                 {
99                         DragDropControl c = (DragDropControl)sender;
100                         DNDForm f = (DNDForm)c.Parent;
101                         if (f.DropControl.EnterFiredCount == 0)
102                                 return;
103
104                         // Cancel dnd operation AFTER we have reached the drop control
105                         if (query_counter++ >= 32)
106                                 e.Action = DragAction.Cancel;
107                 }
108
109                 [Test]
110                 public void EventsAfterDrop ()
111                 {
112                         DNDForm form = new DNDForm ();
113                         form.Text = MethodBase.GetCurrentMethod ().Name;
114                         form.InstructionsText =
115                                 "Instructions:" + Environment.NewLine + Environment.NewLine +
116                                 "1. Click with left button on the control on the left, holding it." + Environment.NewLine +
117                                 "2. Move mouse pointer to the control in the right. " + Environment.NewLine +
118                                 "3. Move the pointer around and then release the mouse button." + Environment.NewLine +
119                                 "4. Close the form.";
120                         form.DragControl.DragData = "hello";
121                         form.DragControl.AllowedEffects = DragDropEffects.All;
122                         form.DropControl.DropEffect = DragDropEffects.Copy;
123
124                         // These should clear the event counters AND
125                         // thus the event counters should remain at 0 after drop occurred
126                         // AND we should get any MouseUp event.
127                         mouse_up_counter = 0;
128                         form.DropControl.DragDrop += new DragEventHandler (DropControl_DragDrop);
129                         form.DropControl.MouseUp += new MouseEventHandler (DropControl_MouseUp);
130
131                         Application.Run (form);
132
133                         Assert.AreEqual (DragDropEffects.Copy, form.DragControl.PerformedEffect, "A1");
134                         Assert.AreEqual (0, form.DropControl.EnterFiredCount, "A2");
135                         Assert.AreEqual (0, form.DropControl.LeaveFiredCount, "A3");
136                         Assert.AreEqual (0, form.DropControl.DropFiredCount, "A4");
137                         Assert.AreEqual (0, form.DropControl.DragOverFiredCount, "A5");
138                         Assert.AreEqual (0, mouse_up_counter, "A6");
139                 }
140
141                 void DropControl_DragDrop (object sender, DragEventArgs e)
142                 {
143                         DragDropControl c = (DragDropControl)sender;
144                         c.ResetEventCounters ();
145                 }
146
147                 int mouse_up_counter;
148
149                 void DropControl_MouseUp (object sender, MouseEventArgs e)
150                 {
151                         mouse_up_counter++;
152                 }
153
154                 [Test]
155                 public void SimpleDragDrop ()
156                 {
157                         DNDForm form = new DNDForm ();
158                         form.Text = MethodBase.GetCurrentMethod ().Name;
159                         form.InstructionsText =
160                                 "Instructions:" + Environment.NewLine + Environment.NewLine +
161                                 "1. Click with left button on the control on the left, holding it." + Environment.NewLine +
162                                 "2. Move mouse pointer to the control in the right. " + Environment.NewLine +
163                                 "3. Drop on the right control." + Environment.NewLine +
164                                 "4. Close the form.";
165                         form.DragControl.DragData = "SimpleDragDropMessage";
166                         form.DragControl.AllowedEffects = DragDropEffects.Copy;
167                         form.DropControl.DropEffect = DragDropEffects.Copy;
168
169                         Application.Run (form);
170
171                         Assert.AreEqual (DragDropEffects.Copy, form.DragControl.PerformedEffect, "A1");
172                         Assert.IsTrue (form.DropControl.Data != null, "A2");
173                         Assert.AreEqual (true, form.DropControl.Data.GetDataPresent (typeof (string)), "A3");
174                         Assert.AreEqual ("SimpleDragDropMessage", form.DropControl.Data.GetData (typeof (string)), "A4");
175                 }
176
177                 [Test]
178                 public void SimpleLeave ()
179                 {
180                         DNDForm form = new DNDForm ();
181                         form.Text = MethodBase.GetCurrentMethod ().Name;
182                         form.InstructionsText =
183                                 "Instructions:" + Environment.NewLine + Environment.NewLine +
184                                 "1. Click with left button on the control on the left, holding it." + Environment.NewLine +
185                                 "2. Move mouse pointer to the control in the right. " + Environment.NewLine +
186                                 "3. Move the mouse pointer outside the drop control." + Environment.NewLine +
187                                 "5. Release mouse button." + Environment.NewLine +
188                                 "6. Close the form.";
189                         form.DragControl.DragData = "hello";
190                         form.DragControl.AllowedEffects = DragDropEffects.All;
191                         form.DropControl.DropEffect = DragDropEffects.Copy;
192
193                         Application.Run (form);
194
195                         Assert.AreEqual (DragDropEffects.None, form.DragControl.PerformedEffect, "A1");
196                         Assert.AreEqual (1, form.DropControl.EnterFiredCount, "A2");
197                         Assert.AreEqual (1, form.DropControl.LeaveFiredCount, "A3");
198                         Assert.AreEqual (0, form.DropControl.DropFiredCount, "A4");
199                 }
200
201                 [Test]
202                 public void NotAllowedDropEffect ()
203                 {
204                         DNDForm form = new DNDForm ();
205                         form.Text = MethodBase.GetCurrentMethod ().Name;
206                         form.InstructionsText =
207                                 "Instructions:" + Environment.NewLine + Environment.NewLine +
208                                 "1. Click with left button on the control on the left, holding it." + Environment.NewLine +
209                                 "2. Move mouse pointer to the control in the right. " + Environment.NewLine +
210                                 "3. Drop on the right control." + Environment.NewLine +
211                                 "4. Close the form.";
212                         form.DragControl.DragData = "SimpleDragDropMessage";
213                         form.DragControl.AllowedEffects = DragDropEffects.Copy;
214                         form.DropControl.DropEffect = DragDropEffects.Move;
215
216                         Application.Run (form);
217
218                         Assert.AreEqual (DragDropEffects.None, form.DragControl.PerformedEffect, "A1");
219                         Assert.AreEqual (1, form.DropControl.EnterFiredCount, "A2");
220                         Assert.AreEqual (0, form.DropControl.DropFiredCount, "A3");
221                         Assert.AreEqual (1, form.DropControl.LeaveFiredCount, "A4");
222                 }
223
224                 // This test should probably include a 'log' check
225                 [Test]
226                 public void SequentialOperations ()
227                 {
228                         DNDForm form = new DNDForm ();
229                         form.Text = MethodBase.GetCurrentMethod ().Name;
230                         form.InstructionsText =
231                                 "Instructions:" + Environment.NewLine + Environment.NewLine +
232                                 "1. Click with left button on the control on the left, holding it." + Environment.NewLine +
233                                 "2. Drag on the control on the right. " + Environment.NewLine + Environment.NewLine +
234                                 "3. Click with left button on the control on the left again, holding it." + Environment.NewLine +
235                                 "4. Move the mouse pointer to the control in the right." + Environment.NewLine +
236                                 "5. Move the mouse pointer outside the drop control." + Environment.NewLine +
237                                 "6. Release mouse button." + Environment.NewLine + Environment.NewLine +
238                                 "7. Click with left button on the control on the left again, holding it." + Environment.NewLine +
239                                 "8. Drag on the control on the right. " + Environment.NewLine + Environment.NewLine +
240                                 "9. Close the form.";
241                         form.DragControl.DragData = "SimpleDragDropMessage";
242                         form.DragControl.AllowedEffects = DragDropEffects.Move;
243                         form.DropControl.DropEffect = DragDropEffects.Move;
244
245                         Application.Run (form);
246
247                         Assert.AreEqual (2, form.DropControl.DropFiredCount, "A1");
248                         Assert.AreEqual (1, form.DropControl.LeaveFiredCount, "A2");
249                         Assert.AreEqual (DragDropEffects.Move, form.DragControl.PerformedEffect, "A3");
250                 }
251
252                 [Test]
253                 public void DropWithoutMovement ()
254                 {
255                         DNDForm form = new DNDForm ();
256                         form.Text = MethodBase.GetCurrentMethod ().Name;
257                         form.InstructionsText =
258                                 "Instructions:" + Environment.NewLine + Environment.NewLine +
259                                 "1. Click with left button on the control on the left, holding it, WITHOUT MOVING IT." + Environment.NewLine +
260                                 "2. Release the button." + Environment.NewLine + 
261                                 "3. Close the form.";
262                         form.DragControl.DragData = "no movement";
263                         form.DragControl.AllowDrop = true;
264                         form.DragControl.AllowedEffects = DragDropEffects.Move;
265                         form.DragControl.DropEffect = DragDropEffects.Move;
266
267                         // Force to automatically do a dnd operation when mouse is pressed,
268                         // instead of waiting for movement
269                         form.DragControl.MouseDown += new MouseEventHandler (DragControl_MouseDown);
270
271                         Application.Run (form);
272
273                         Assert.AreEqual (1, form.DragControl.EnterFiredCount, "A1");
274                         Assert.AreEqual (0, form.DragControl.LeaveFiredCount, "A2");
275                         Assert.AreEqual (1, form.DragControl.DropFiredCount, "A3");
276                         Assert.AreEqual (0, form.DragControl.DragOverFiredCount, "A4");
277                         Assert.AreEqual (true, form.DragControl.Data != null, "A5");
278                         // The assertion below is weird: We had a successfully drop, but the returned value is None
279                         Assert.AreEqual (DragDropEffects.None, form.DragControl.PerformedEffect, "A6");
280                 }
281
282                 void DragControl_MouseDown (object sender, MouseEventArgs e)
283                 {
284                         DragDropControl ctrl = (DragDropControl)sender;
285                         ctrl.DoDragDrop (ctrl.DragData, ctrl.AllowedEffects);
286                 }
287
288                 [Test]
289                 public void DragDropInSameControl ()
290                 {
291                         DNDForm form = new DNDForm ();
292                         form.Text = MethodBase.GetCurrentMethod ().Name;
293                         form.InstructionsText =
294                                 "Instructions:" + Environment.NewLine + Environment.NewLine +
295                                 "1. Click with left button on the control on the left, holding it." + Environment.NewLine +
296                                 "2. Move the mouse inside left control. " + Environment.NewLine +
297                                 "3. Drop on left control (same)." + Environment.NewLine + Environment.NewLine +
298                                 "4. Click with left button on the control on the left again, holding it." + Environment.NewLine +
299                                 "5. Move the mouse inside the left control. " + Environment.NewLine +
300                                 "6. Press ESC, release mouse button and move mouse pointer outside control." + Environment.NewLine +
301                                 "7. Close the form.";
302                         form.DragControl.DragData = "SameControl";
303                         form.DragControl.AllowDrop = true;
304                         form.DragControl.AllowedEffects = DragDropEffects.Copy;
305
306                         data = null;
307                         drag_enter_count = drag_leave_count = 0;
308                         form.DragControl.DragEnter += new DragEventHandler (DragDropInSameControl_DragEnter);
309                         form.DragControl.DragLeave += new EventHandler (DragDropInSameControl_DragLeave);
310                         form.DragControl.DragDrop += new DragEventHandler (DragDropInSameControl_DragDrop);
311
312                         Application.Run (form);
313
314                         Assert.AreEqual (2, drag_enter_count, "A1");
315                         Assert.AreEqual (1, drag_leave_count, "A2");
316                         Assert.AreEqual (1, drag_drop_count, "A3");
317                         Assert.AreEqual ("SameControl", data, "A4");
318                 }
319
320                 int drag_enter_count;
321                 int drag_leave_count;
322                 int drag_drop_count;
323                 object data;
324
325                 void DragDropInSameControl_DragDrop (object sender, DragEventArgs e)
326                 {
327                         drag_drop_count++;
328                         data = e.Data.GetData (typeof (string));
329                 }
330
331                 void DragDropInSameControl_DragLeave (object sender, EventArgs e)
332                 {
333                         drag_leave_count++;
334                 }
335
336                 void DragDropInSameControl_DragEnter (object sender, DragEventArgs e)
337                 {
338                         e.Effect = DragDropEffects.Copy;
339                         drag_enter_count++;
340                 }
341         }
342
343         public class DNDForm : Form
344         {
345                 DragDropControl drag_control;
346                 DragDropControl drop_control;
347                 TextBox instructions_tb;
348                 Label test_name;
349
350                 public DNDForm ()
351                 {
352                         test_name = new Label ();
353                         test_name.Location = new Point (5, 5);
354                         test_name.AutoSize = true;
355                         test_name.Font = new Font (Font, FontStyle.Bold | Font.Style);
356                         test_name.Text = Text;
357
358                         instructions_tb = new TextBox ();
359                         instructions_tb.Multiline = true;
360                         instructions_tb.ScrollBars = ScrollBars.Vertical;
361                         instructions_tb.Location = new Point (5, test_name.Bottom + 5);
362                         instructions_tb.Size = new Size (460, 180);
363
364                         drag_control = new DragDropControl ();
365                         drag_control.Location = new Point (5, instructions_tb.Bottom + 10);
366                         drag_control.Size = new Size (220, 180);
367                         drag_control.BackColor = Color.LightYellow;
368                         drag_control.DragDropColor = Color.Yellow;
369                         drag_control.Text = "Drag Control";
370                         drag_control.DoDrag = true;
371
372                         drop_control = new DragDropControl ();
373                         drop_control.Location = new Point (drag_control.Right + 20, instructions_tb.Bottom + 10);
374                         drop_control.Size = new Size (220, 180);
375                         drop_control.BackColor = Color.LightGreen;
376                         drop_control.DragDropColor = Color.Green;
377                         drop_control.Text = "Drop Control";
378                         drop_control.AllowDrop = true;
379
380                         Controls.AddRange (new Control [] { test_name, instructions_tb, drag_control, drop_control });
381
382                         Size = new Size (480, 440);
383                 }
384
385                 public string InstructionsText {
386                         get {
387                                 return instructions_tb.Text;
388                         }
389                         set {
390                                 instructions_tb.Text = value;
391                         }
392                 }
393
394                 public DragDropControl DragControl {
395                         get {
396                                 return drag_control;
397                         }
398                 }
399
400                 public DragDropControl DropControl {
401                         get {
402                                 return drop_control;
403                         }
404                 }
405
406                 protected override void OnTextChanged (EventArgs args)
407                 {
408                         base.OnTextChanged (args);
409                         test_name.Text = Text;
410                 }
411         }
412
413         public class DragDropControl : Control
414         {
415                 DragDropEffects effect;
416                 DragDropEffects allowed_effects;
417                 DragDropEffects performed_effect;
418                 object drag_data;
419                 IDataObject data;
420                 Color drop_color;
421                 Color prev_color;
422                 Rectangle drag_rect;
423                 bool do_drag;
424
425                 int drop_fired_count;
426                 int leave_fired_count;
427                 int drag_over_fired_count;
428                 int enter_fired_count;
429
430                 public DragDropControl ()
431                 {
432                         drag_rect = new Rectangle (new Point (-1, -1), SystemInformation.DragSize);
433                 }
434
435                 // to call or not DoDragDrop when mouse movement is detected. Only handle dnd events otherwise.
436                 public bool DoDrag {
437                         get {
438                                 return do_drag;
439                         }
440                         set {
441                                 do_drag = value;
442                         }
443                 }
444
445                 // Color of the control when an operation is having place
446                 public Color DragDropColor {
447                         get {
448                                 return drop_color;
449                         } 
450                         set {
451                                 drop_color = value;
452                         }
453                 }
454
455                 // Data to pass to Control.DoDragDrop 
456                 public object DragData {
457                         get {
458                                 return drag_data;
459                         }
460                         set {
461                                 drag_data = value;
462                         }
463                 }
464
465                 // Effects passed to Control.DoDragDrop
466                 public DragDropEffects AllowedEffects {
467                         get {
468                                 return allowed_effects;
469                         }
470                         set {
471                                 allowed_effects = value;
472                         }
473                 }
474
475                 // Effect returned by Control.DoDragDrop
476                 public DragDropEffects PerformedEffect {
477                         get {
478                                 return performed_effect;
479                         }
480                 }
481
482                 // The value DragEventArgs.Effect gets in DragEnter event
483                 public DragDropEffects DropEffect {
484                         get {
485                                 return effect;
486                         }
487                         set {
488                                 effect = value;
489                         }
490                 }
491
492                 // The value in DragEventArgs.Data
493                 public IDataObject Data {
494                         get {
495                                 return data;
496                         }
497                 }
498
499                 public int DropFiredCount {
500                         get {
501                                 return drop_fired_count;
502                         }
503                 }
504
505                 public int EnterFiredCount {
506                         get {
507                                 return enter_fired_count;
508                         }
509                 }
510
511                 public int LeaveFiredCount {
512                         get {
513                                 return leave_fired_count;
514                         }
515                 }
516
517                 public int DragOverFiredCount {
518                         get {
519                                 return drag_over_fired_count;
520                         }
521                 }
522
523                 public void ResetEventCounters ()
524                 {
525                         drop_fired_count = enter_fired_count = leave_fired_count = drag_over_fired_count = 0;
526                 }
527
528                 protected override void OnDragEnter (DragEventArgs drgevent)
529                 {
530                         if (!do_drag) {
531                                 prev_color = BackColor;
532                                 BackColor = drop_color;
533                         }
534
535                         enter_fired_count++;
536                         drgevent.Effect = effect;
537
538                         base.OnDragEnter (drgevent);
539                 }
540
541                 protected override void OnDragOver (DragEventArgs drgevent)
542                 {
543                         drag_over_fired_count++;
544
545                         base.OnDragOver (drgevent);
546                 }
547
548                 protected override void OnDragLeave (EventArgs e)
549                 {
550                         data = null;
551                         leave_fired_count++;
552
553                         if (!do_drag)
554                                 BackColor = prev_color;
555
556                         base.OnDragLeave (e);
557                 }
558
559                 protected override void OnDragDrop (DragEventArgs drgevent)
560                 {
561                         data = drgevent.Data;
562                         drop_fired_count++;
563
564                         if (!do_drag)
565                                 BackColor = prev_color;
566
567                         base.OnDragDrop (drgevent);
568                 }
569
570                 protected override void OnMouseMove (MouseEventArgs e)
571                 {
572                         base.OnMouseMove (e);
573
574                         if (!do_drag)
575                                 return;
576
577                         if (e.Button == MouseButtons.Left || e.Button == MouseButtons.Right) {
578
579                                 if (drag_rect.X == -1 && drag_rect.Y == -1)
580                                         drag_rect.Location = new Point (e.X, e.Y);
581                                 else {
582                                         if (!drag_rect.Contains (new Point (e.X, e.Y))) {
583                                                 Color prev_color = BackColor;
584                                                 BackColor = drop_color;
585
586                                                 performed_effect = DoDragDrop (drag_data, allowed_effects);
587
588                                                 drag_rect.Location = new Point (-1, -1);
589                                                 BackColor = prev_color;
590                                         }
591                                 }
592
593                         }
594                 }
595
596                 protected override void OnPaint (PaintEventArgs e)
597                 {
598                         base.OnPaint (e);
599
600                         Graphics g = e.Graphics;
601
602                         StringFormat sf = new StringFormat ();
603                         sf.Alignment = StringAlignment.Center;
604                         sf.LineAlignment = StringAlignment.Center;
605
606                         g.DrawString (Text, SystemFonts.DefaultFont, SystemBrushes.ControlDark,
607                                 ClientRectangle, sf);
608
609                         sf.Dispose ();
610                 }
611         }
612 }
613