svn path=/branches/mono-1-1-9/mcs/; revision=51212
[mono.git] / mcs / class / System.Web / System.Web.UI.HtmlControls / HtmlSelect.cs
1 //
2 // System.Web.UI.HtmlControls.HtmlSelect.cs
3 //
4 // Author:
5 //      Dick Porter  <dick@ximian.com>
6 //
7 // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28
29 using System.Web.UI.WebControls;
30 using System.Web.Util;
31 using System.ComponentModel;
32 using System.Collections;
33 using System.Collections.Specialized;
34 using System.Globalization;
35 using System.Security.Permissions;
36
37 namespace System.Web.UI.HtmlControls 
38 {
39         // CAS
40         [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
41         [AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
42         // attributes
43         [DefaultEvent ("ServerChange")]
44         [ValidationProperty ("Value")]
45         [ControlBuilder (typeof (HtmlSelectBuilder))]
46         public class HtmlSelect : HtmlContainerControl , IPostBackDataHandler
47 #if NET_2_0
48         , IParserAccessor
49 #endif
50         {
51 #if NET_2_0
52                 DataSourceView boundDataSourceView;
53                 IDataSource boundDataSource;
54                 private bool requiresDataBinding;
55                 IEnumerable data;
56 #endif
57                 public HtmlSelect () : base ("select")
58                 {
59                 }
60
61                 [DefaultValue ("")]
62                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
63                 [WebSysDescription("")]
64                 [WebCategory("Data")]
65                 public virtual string DataMember 
66                 {
67                         get {
68                                 string member = Attributes["datamember"];
69
70                                 if (member == null) {
71                                         return (String.Empty);
72                                 }
73
74                                 return (member);
75                         }
76                         set {
77                                 if (value == null) {
78                                         Attributes.Remove ("datamember");
79                                 } else {
80                                         Attributes["datamember"] = value;
81                                 }
82                         }
83                 }
84
85                 object datasource;
86                 
87                 [DefaultValue (null)]
88                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
89                 [WebSysDescription("")]
90                 [WebCategory("Data")]
91                 public virtual object DataSource 
92                 {
93                         get {
94                                 return (datasource);
95                         }
96                         set {
97                                 if ((value != null) &&
98                                     !(value is IEnumerable) &&
99                                     !(value is IListSource)) {
100                                         throw new ArgumentException ();
101                                 }
102
103                                 datasource = value;
104                         }
105                 }
106
107 #if NET_2_0
108                 [DefaultValue ("")]
109                 public virtual string DataSourceID
110                 {
111                         get {
112                                 return ViewState.GetString ("DataSourceID", "");
113                         }
114                         set {
115                                 if (datasource != null)
116                                         throw new HttpException ("Only one of DataSource and DataSourceID can be specified.");
117                                 ViewState ["DataSourceID"] = value;
118
119                                 OnDataPropertyChanged ();
120                         }
121                 }
122 #endif
123                                 
124
125                 [DefaultValue ("")]
126                 [WebSysDescription("")]
127                 [WebCategory("Data")]
128                 public virtual string DataTextField 
129                 {
130                         get {
131                                 string text = Attributes["datatextfield"];
132
133                                 if (text == null) {
134                                         return (String.Empty);
135                                 }
136
137                                 return (text);
138                         }
139                         set {
140                                 if (value == null) {
141                                         Attributes.Remove ("datatextfield");
142                                 } else {
143                                         Attributes["datatextfield"] = value;
144                                 }
145                         }
146                 }
147
148                 [DefaultValue ("")]
149                 [WebSysDescription("")]
150                 [WebCategory("Data")]
151                 public virtual string DataValueField 
152                 {
153                         get {
154                                 string value = Attributes["datavaluefield"];
155
156                                 if (value == null) {
157                                         return (String.Empty);
158                                 }
159
160                                 return (value);
161                         }
162                         set {
163                                 if (value == null) {
164                                         Attributes.Remove ("datavaluefield");
165                                 } else {
166                                         Attributes["datavaluefield"] = value;
167                                 }
168                         }
169                 }
170
171                 public override string InnerHtml 
172                 {
173                         get {
174                                 throw new NotSupportedException ();
175                         }
176                         set {
177                                 throw new NotSupportedException ();
178                         }
179                 }
180
181                 public override string InnerText
182                 {
183                         get {
184                                 throw new NotSupportedException ();
185                         }
186                         set {
187                                 throw new NotSupportedException ();
188                         }
189                 }
190
191 #if NET_2_0
192                 protected bool IsBoundUsingDataSourceID 
193                 {
194                         get {
195                                 return (DataSourceID.Length != 0);
196                         }
197                 }
198 #endif          
199
200                 ListItemCollection items;
201                 
202                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
203                 [Browsable (false)]
204                 public ListItemCollection Items 
205                 {
206                         get {
207                                 if (items == null) {
208                                         items = new ListItemCollection ();
209                                 }
210
211                                 return (items);
212                         }
213                 }
214
215                 [DefaultValue ("")]
216                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
217                 [WebSysDescription("")]
218                 [WebCategory("Behavior")]
219                 public bool Multiple 
220                 {
221                         get {
222                                 string multi = Attributes["multiple"];
223
224                                 if (multi == null) {
225                                         return (false);
226                                 }
227
228                                 return (true);
229                         }
230                         set {
231                                 if (value == false) {
232                                         Attributes.Remove ("multiple");
233                                 } else {
234                                         Attributes["multiple"] = "multiple";
235                                 }
236                         }
237                 }
238                 
239                 [DefaultValue ("")]
240                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
241                 [WebSysDescription("")]
242                 [WebCategory("Behavior")]
243                 public string Name 
244                 {
245                         get {
246                                 return (UniqueID);
247                         }
248                         set {
249                                 /* Do nothing */
250                         }
251                 }
252
253 #if NET_2_0
254                 protected bool RequiresDataBinding 
255                 {
256                         get { return requiresDataBinding; }
257                         set { requiresDataBinding = value; }
258                 }
259 #endif
260
261                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
262                 [Browsable (false)]
263                 public virtual int SelectedIndex 
264                 {
265                         get {
266                                 /* Make sure Items has been initialised */
267                                 ListItemCollection listitems = Items;
268
269                                 for (int i = 0; i < listitems.Count; i++) {
270                                         if (listitems[i].Selected) {
271                                                 return (i);
272                                         }
273                                 }
274
275                                 /* There is always a selected item in
276                                  * non-multiple mode, if the size is
277                                  * <= 1
278                                  */
279                                 if (!Multiple && Size <= 1) {
280                                         /* Select the first item */
281                                         if (listitems.Count > 0) {
282                                                 /* And make it stick
283                                                  * if there is
284                                                  * anything in the
285                                                  * list
286                                                  */
287                                                 listitems[0].Selected = true;
288                                         }
289                                         
290                                         return (0);
291                                 }
292                                 
293                                 return (-1);
294                         }
295                         set {
296                                 ClearSelection ();
297
298                                 if (value == -1 || items == null) {
299                                         return;
300                                 }
301
302                                 if (value < 0 || value >= items.Count) {
303                                         throw new ArgumentOutOfRangeException ("value");
304                                 }
305
306                                 items[value].Selected = true;
307                         }
308                 }
309
310                 /* "internal infrastructure" according to the docs,
311                  * but has some documentation in 2.0
312                  */
313                 protected virtual int[] SelectedIndices
314                 {
315                         get {
316                                 ArrayList selected = new ArrayList ();
317
318                                 int count = Items.Count;
319
320                                 for (int i = 0; i < count; i++) {
321                                         if (Items [i].Selected) {
322                                                 selected.Add (i);
323                                         }
324                                 }
325                                 
326                                 return ((int[])selected.ToArray (typeof (int)));
327                         }
328                 }
329                 
330
331                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
332                 public int Size 
333                 {
334                         get {
335                                 string size = Attributes["size"];
336
337                                 if (size == null) {
338                                         return (-1);
339                                 }
340
341                                 return (Int32.Parse (size, CultureInfo.InvariantCulture));
342                         }
343                         set {
344                                 if (value == -1) {
345                                         Attributes.Remove ("size");
346                                 } else {
347                                         Attributes["size"] = value.ToString ();
348                                 }
349                         }
350                 }
351
352                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
353                 public string Value 
354                 {
355                         get {
356                                 int sel = SelectedIndex;
357
358                                 if (sel >= 0 && sel < Items.Count) {
359                                         return (Items[sel].Value);
360                                 }
361                                 
362                                 return (String.Empty);
363                         }
364                         set {
365                                 int sel = Items.IndexOf (value);
366
367                                 if (sel >= 0) {
368                                         SelectedIndex = sel;
369                                 }
370                         }
371                 }
372
373                 private static readonly object EventServerChange = new object ();
374                 
375                 [WebSysDescription("")]
376                 [WebCategory("Action")]
377                 public event EventHandler ServerChange
378                 {
379                         add {
380                                 Events.AddHandler (EventServerChange, value);
381                         }
382                         remove {
383                                 Events.RemoveHandler (EventServerChange, value);
384                         }
385                 }
386
387                 protected override void AddParsedSubObject (object obj)
388                 {
389                         if (!(obj is ListItem)) {
390                                 throw new HttpException ("HtmlSelect can only contain ListItem");
391                         }
392
393                         Items.Add ((ListItem)obj);
394                         
395                         base.AddParsedSubObject (obj);
396                 }
397
398                 /* "internal infrastructure" according to the docs,
399                  * but has some documentation in 2.0
400                  */
401                 protected virtual void ClearSelection ()
402                 {
403                         if (items == null) {
404                                 return;
405                         }
406
407                         int count = items.Count;
408                         for (int i = 0; i < count; i++) {
409                                 items[i].Selected = false;
410                         }
411                 }
412                 
413                 protected override ControlCollection CreateControlCollection ()
414                 {
415                         return (base.CreateControlCollection ());
416                 }
417
418 #if NET_2_0
419                 protected void EnsureDataBound ()
420                 {
421                         if (IsBoundUsingDataSourceID && RequiresDataBinding)
422                                 DataBind ();
423                 }
424
425                 private void SelectCallback (IEnumerable data)
426                 {
427                         this.data = data;
428                 }
429
430                 protected virtual IEnumerable GetData ()
431                 {
432                         IEnumerable result;
433
434                         if (DataSourceID.Length == 0)
435                                 return null;
436
437                         boundDataSourceView = boundDataSource.GetView (String.Empty);
438                         boundDataSourceView.Select (new DataSourceSelectArguments (), SelectCallback);
439                         boundDataSourceView.DataSourceViewChanged += OnDataSourceViewChanged;
440
441                         result = data;
442                         data = null;
443
444                         return result;
445                 }
446 #endif          
447
448                 protected override void LoadViewState (object savedState)
449                 {
450                         object first = null;
451                         object second = null;
452                         int[] selected = null;
453
454                         Triplet triplet = savedState as Triplet;
455                         if (triplet != null) {
456                                 first = triplet.First;
457                                 second = triplet.Second;
458                                 selected = triplet.Third as int[];
459                         }
460
461                         base.LoadViewState (first);
462
463                         if (second != null) {
464                                 IStateManager manager = Items as IStateManager;
465                                 manager.LoadViewState (second);
466                         }
467
468                         if (selected != null) {
469                                 Select (selected);
470                         }
471                 }
472
473                 protected override void OnDataBinding (EventArgs e)
474                 {
475                         base.OnDataBinding (e);
476
477                         /* Make sure Items has been initialised */
478                         ListItemCollection listitems = Items;
479
480                         listitems.Clear ();
481                         
482                         IEnumerable list;
483
484 #if NET_2_0
485                         if (IsBoundUsingDataSourceID)
486                                 list = GetData ();
487                         else
488 #endif
489                                 list = DataSourceResolver.ResolveDataSource (DataSource, DataMember);
490
491                         if (list == null) {
492                                 return;
493                         }
494                         
495                         foreach (object container in list) {
496                                 string text = null;
497                                 string value = null;
498
499                                 if (DataTextField == String.Empty &&
500                                     DataValueField == String.Empty) {
501                                         text = container.ToString ();
502                                         value = text;
503                                 } else {
504                                         if (DataTextField != String.Empty) {
505                                                 text = DataBinder.Eval (container, DataTextField).ToString ();
506                                         }
507
508                                         if (DataValueField != String.Empty) {
509                                                 value = DataBinder.Eval (container, DataValueField).ToString ();
510                                         } else {
511                                                 value = text;
512                                         }
513
514                                         if (text == null &&
515                                             value != null) {
516                                                 text = value;
517                                         }
518                                 }
519
520                                 if (text == null) {
521                                         text = String.Empty;
522                                 }
523                                 if (value == null) {
524                                         value = String.Empty;
525                                 }
526
527                                 ListItem item = new ListItem (text, value);
528                                 listitems.Add (item);
529                         }
530                 }
531
532 #if NET_2_0
533                 [MonoTODO]
534                 protected virtual void OnDataPropertyChanged ()
535                 {
536                         RequiresDataBinding = true;
537                 }
538
539                 [MonoTODO]
540                 protected virtual void OnDataSourceViewChanged (object sender,
541                                                                 EventArgs e)
542                 {
543                         RequiresDataBinding = true;
544                 }
545
546                 [MonoTODO]
547                 protected internal override void OnInit (EventArgs e)
548                 {
549                         base.OnInit (e);
550                 }
551
552                 protected internal override void OnLoad (EventArgs e)
553                 {
554                         if ((Page != null) && !Page.IsPostBack)
555                                 RequiresDataBinding = true;
556
557                         base.OnLoad (e);
558
559                         if (IsBoundUsingDataSourceID)
560                                 ConnectToDataSource ();
561                 }
562
563                 void ConnectToDataSource ()
564                 {
565                         /* verify that the data source exists and is an IDataSource */
566                         object ctrl = null;
567                         if (Page != null)
568                                 ctrl = Page.FindControl (DataSourceID);
569
570                         if (ctrl == null || !(ctrl is IDataSource)) {
571                                 string format;
572
573                                 if (ctrl == null)
574                                         format = "DataSourceID of '{0}' must be the ID of a control of type IDataSource.  A control with ID '{1}' could not be found.";
575                                 else
576                                         format = "DataSourceID of '{0}' must be the ID of a control of type IDataSource.  '{1}' is not an IDataSource.";
577
578                                 throw new HttpException (String.Format (format, ID, DataSourceID));
579                         }
580
581                         boundDataSource = (IDataSource)ctrl;
582                 }
583 #endif
584
585 #if NET_2_0
586                 protected internal
587 #else           
588                 protected
589 #endif          
590                 override void OnPreRender (EventArgs e)
591                 {
592 #if NET_2_0
593                         EnsureDataBound ();
594 #endif
595
596                         base.OnPreRender (e);
597
598                         if (Page != null) {
599                                 Page.RegisterRequiresPostBack (this);
600                         }
601                 }
602
603                 protected virtual void OnServerChange (EventArgs e)
604                 {
605                         EventHandler handler = (EventHandler)Events[EventServerChange];
606
607                         if (handler != null) {
608                                 handler (this, e);
609                         }
610                 }
611                 
612                 protected override void RenderAttributes (HtmlTextWriter w)
613                 {
614                         /* If there is no "name" attribute,
615                          * LoadPostData doesn't work...
616                          */
617                         w.WriteAttribute ("name", Name);
618                         Attributes.Remove ("name");
619                         
620                         /* Don't render the databinding attributes */
621                         Attributes.Remove ("datamember");
622                         Attributes.Remove ("datatextfield");
623                         Attributes.Remove ("datavaluefield");
624                         
625                         base.RenderAttributes (w);
626                 }
627                 
628 #if NET_2_0
629                 protected internal
630 #else
631                 protected
632 #endif          
633                 override void RenderChildren (HtmlTextWriter w)
634                 {
635                         base.RenderChildren (w);
636
637                         if (items == null) {
638                                 return;
639                         }
640                         
641                         w.WriteLine ();
642
643                         bool done_sel = false;
644                         
645                         int count = items.Count;
646                         for (int i = 0; i < count; i++) {
647                                 ListItem item = items[i];
648
649                                 w.Indent++;
650                                 
651                                 /* Write the <option> elements this
652                                  * way so that the output HTML matches
653                                  * the ms version (can't make
654                                  * HtmlTextWriterTag.Option an inline
655                                  * element, cos that breaks other
656                                  * stuff.)
657                                  */
658                                 w.WriteBeginTag ("option");
659                                 if (item.Selected && !done_sel) {
660
661                                         w.WriteAttribute ("selected", "selected");
662
663                                         if (!Multiple) {
664                                                 done_sel = true;
665                                         }
666                                 }
667                                 
668                                 w.WriteAttribute ("value", item.Value);
669                                 w.Write (HtmlTextWriter.TagRightChar);
670                                 
671                                 w.Write (item.Text);
672                                 w.WriteEndTag ("option");
673                                 w.WriteLine ();
674
675                                 w.Indent--;
676                         }
677                 }
678
679                 protected override object SaveViewState ()
680                 {
681                         object first = null;
682                         object second = null;
683                         object selected = null;
684
685                         first = base.SaveViewState ();
686
687                         IStateManager manager = items as IStateManager;
688                         if (manager != null) {
689                                 second = manager.SaveViewState ();
690                         }
691
692                         selected = SelectedIndices;
693                         
694                         if (first == null &&
695                             second == null &&
696                             selected == null) {
697                                 return (null);
698                         }
699
700                         return (new Triplet (first, second, selected));
701                 }
702
703                 /* "internal infrastructure" according to the docs,
704                  * but has some documentation in 2.0
705                  */
706                 protected virtual void Select (int[] selectedIndices)
707                 {
708                         if (items == null) {
709                                 return;
710                         }
711
712                         ClearSelection ();
713                         
714                         int count = items.Count;
715                         foreach (int i in selectedIndices) {
716                                 if (i >= 0 && i < count) {
717                                         items[i].Selected = true;
718                                 }
719                         }
720                 }
721
722                 protected override void TrackViewState ()
723                 {
724                         base.TrackViewState ();
725
726                         IStateManager manager = items as IStateManager;
727                         if (manager != null) {
728                                 manager.TrackViewState ();
729                         }
730                 }
731
732 #if NET_2_0
733                 protected virtual bool LoadPostData (string postDataKey, NameValueCollection postCollection) 
734                 {
735                         return (LoadPostData (postDataKey, postCollection));
736                 }
737
738                 protected virtual void RaisePostDataChangedEvent ()
739                 {
740                         RaisePostDataChangedEvent ();
741                 }
742 #endif
743
744                 bool IPostBackDataHandler.LoadPostData (string postDataKey, NameValueCollection postCollection)
745                 {
746                         /* postCollection contains the values that are
747                          * selected
748                          */
749
750                         string[] values = postCollection.GetValues (postDataKey);
751                         bool changed = false;
752
753                         if (values != null) {
754                                 if (Multiple) {
755                                         /* We have a set of
756                                          * selections.  We can't just
757                                          * set the new list, because
758                                          * we need to know if the set
759                                          * has changed from last time
760                                          */
761                                         int value_len = values.Length;
762                                         int[] old_sel = SelectedIndices;
763                                         int[] new_sel = new int[value_len];
764                                         int old_sel_len = old_sel.Length;
765                                         
766                                         for (int i = 0; i < value_len; i++) {
767                                                 new_sel[i] = Items.IndexOf (values[i]);
768                                                 if (old_sel_len != value_len ||
769                                                     old_sel[i] != new_sel[i]) {
770                                                         changed = true;
771                                                 }
772                                         }
773
774                                         if (changed) {
775                                                 Select (new_sel);
776                                         }
777                                 } else {
778                                         /* Just take the first one */
779                                         int sel = Items.IndexOf (values[0]);
780
781                                         if (sel != SelectedIndex) {
782                                                 SelectedIndex = sel;
783                                                 changed = true;
784                                         }
785                                 }
786                         }
787                         
788                         return (changed);
789                 }
790
791                 void IPostBackDataHandler.RaisePostDataChangedEvent ()
792                 {
793                         OnServerChange (EventArgs.Empty);
794                 }
795                 
796         }
797 }