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