1 namespace System.Web.UI.WebControls { 2 using System; 3 using System.Collections; 4 using System.Collections.Generic; 5 using System.ComponentModel; 6 using System.Globalization; 7 using System.Text; 8 using System.Web.UI; 9 10 public sealed class MenuItemCollection : ICollection, IStateManager { 11 private List<MenuItem> _list; 12 private MenuItem _owner; 13 private int _version; 14 15 private bool _isTrackingViewState; 16 17 private List<LogItem> _log; 18 19 MenuItemCollection()20 public MenuItemCollection() : this(null) { 21 } 22 23 MenuItemCollection(MenuItem owner)24 public MenuItemCollection(MenuItem owner) { 25 _owner = owner; 26 _list = new List<MenuItem>(); 27 } 28 29 30 public int Count { 31 get { 32 return _list.Count; 33 } 34 } 35 36 37 public bool IsSynchronized { 38 get { 39 return ((ICollection)_list).IsSynchronized; 40 } 41 } 42 43 private List<LogItem> Log { 44 get { 45 if (_log == null) { 46 _log = new List<LogItem>(); 47 } 48 return _log; 49 } 50 } 51 52 53 public object SyncRoot { 54 get { 55 return ((ICollection)_list).SyncRoot; 56 } 57 } 58 59 60 public MenuItem this[int index] { 61 get { 62 return _list[index]; 63 } 64 } 65 66 Add(MenuItem child)67 public void Add(MenuItem child) { 68 AddAt(_list.Count, child); 69 } 70 71 AddAt(int index, MenuItem child)72 public void AddAt(int index, MenuItem child) { 73 if (child == null) { 74 throw new ArgumentNullException("child"); 75 } 76 77 if (child.Owner != null && child.Parent == null) { 78 child.Owner.Items.Remove(child); 79 } 80 if (child.Parent != null) { 81 child.Parent.ChildItems.Remove(child); 82 } 83 84 if (_owner != null) { 85 child.SetParent(_owner); 86 child.SetOwner(_owner.Owner); 87 } 88 89 _list.Insert(index, child); 90 _version++; 91 92 if (_isTrackingViewState) { 93 ((IStateManager)child).TrackViewState(); 94 child.SetDirty(); 95 } 96 Log.Add(new LogItem(LogItemType.Insert, index, _isTrackingViewState)); 97 } 98 99 Clear()100 public void Clear() { 101 if (this.Count == 0) return; 102 if (_owner != null) { 103 Menu owner = _owner.Owner; 104 if (owner != null) { 105 MenuItem current = owner.SelectedItem; 106 // Check if the selected item is under this collection 107 while (current != null) { 108 if (this.Contains(current)) { 109 owner.SetSelectedItem(null); 110 break; 111 } 112 current = current.Parent; 113 } 114 } 115 } 116 foreach (MenuItem item in _list) { 117 item.SetParent(null); 118 } 119 _list.Clear(); 120 _version++; 121 if (_isTrackingViewState) { 122 // Clearing invalidates all previous log entries, so we can just clear them out and save some space 123 Log.Clear(); 124 } 125 Log.Add(new LogItem(LogItemType.Clear, 0, _isTrackingViewState)); 126 } 127 128 CopyTo(Array array, int index)129 public void CopyTo(Array array, int index) { 130 if (!(array is MenuItem[])) { 131 throw new ArgumentException(SR.GetString(SR.MenuItemCollection_InvalidArrayType), "array"); 132 } 133 _list.CopyTo((MenuItem[])array, index); 134 } 135 136 CopyTo(MenuItem[] array, int index)137 public void CopyTo(MenuItem[] array, int index) { 138 _list.CopyTo(array, index); 139 } 140 141 Contains(MenuItem c)142 public bool Contains(MenuItem c) { 143 return _list.Contains(c); 144 } 145 FindItem(string[] path, int pos)146 internal MenuItem FindItem(string[] path, int pos) { 147 if (pos == path.Length) { 148 return _owner; 149 } 150 151 string pathPart = TreeView.UnEscape(path[pos]); 152 for (int i = 0; i < Count; i++) { 153 MenuItem node = _list[i]; 154 if (node.Value == pathPart) { 155 return node.ChildItems.FindItem(path, pos + 1); 156 } 157 } 158 159 return null; 160 } 161 162 GetEnumerator()163 public IEnumerator GetEnumerator() { 164 return new MenuItemCollectionEnumerator(this); 165 } 166 167 IndexOf(MenuItem value)168 public int IndexOf(MenuItem value) { 169 return _list.IndexOf(value); 170 } 171 172 Remove(MenuItem value)173 public void Remove(MenuItem value) { 174 if (value == null) { 175 throw new ArgumentNullException("value"); 176 } 177 178 int index = _list.IndexOf(value); 179 if (index != -1) { 180 RemoveAt(index); 181 } 182 } 183 184 RemoveAt(int index)185 public void RemoveAt(int index) { 186 MenuItem item = _list[index]; 187 Menu owner = item.Owner; 188 if (owner != null) { 189 MenuItem current = owner.SelectedItem; 190 // Check if the selected item is under this item 191 while (current != null) { 192 if (current == item) { 193 owner.SetSelectedItem(null); 194 break; 195 } 196 current = current.Parent; 197 } 198 } 199 item.SetParent(null); 200 201 _list.RemoveAt(index); 202 _version++; 203 Log.Add(new LogItem(LogItemType.Remove, index, _isTrackingViewState)); 204 } 205 SetDirty()206 internal void SetDirty() { 207 foreach (LogItem item in Log) { 208 item.Tracked = true; 209 } 210 for (int i = 0; i < Count; i++) { 211 this[i].SetDirty(); 212 } 213 } 214 215 #region IStateManager implementation 216 217 /// <internalonly/> 218 bool IStateManager.IsTrackingViewState { 219 get { 220 return _isTrackingViewState; 221 } 222 } 223 224 225 /// <internalonly/> IStateManager.LoadViewState(object state)226 void IStateManager.LoadViewState(object state) { 227 object[] nodeState = (object[])state; 228 if (nodeState != null) { 229 if (nodeState[0] != null) { 230 string logString = (string)nodeState[0]; 231 // Process each log entry 232 string[] items = logString.Split(','); 233 for (int i = 0; i < items.Length; i++) { 234 string[] parts = items[i].Split(':'); 235 LogItemType type = (LogItemType)Int32.Parse(parts[0], CultureInfo.InvariantCulture); 236 int index = Int32.Parse(parts[1], CultureInfo.InvariantCulture); 237 238 if (type == LogItemType.Insert) { 239 AddAt(index, new MenuItem()); 240 } 241 else if (type == LogItemType.Remove) { 242 RemoveAt(index); 243 } 244 else if (type == LogItemType.Clear) { 245 Clear(); 246 } 247 } 248 } 249 250 for (int i = 0; i < nodeState.Length - 1; i++) { 251 if ((nodeState[i + 1] != null) && (this[i] != null)) { 252 ((IStateManager)this[i]).LoadViewState(nodeState[i + 1]); 253 } 254 } 255 } 256 } 257 258 259 /// <internalonly/> IStateManager.SaveViewState()260 object IStateManager.SaveViewState() { 261 object[] nodes = new object[Count + 1]; 262 263 bool hasViewState = false; 264 265 if ((_log != null) && (_log.Count > 0)) { 266 // Construct a string representation of the log, delimiting entries with commas 267 // and seperator command and index with a colon 268 StringBuilder builder = new StringBuilder(); 269 int realLogCount = 0; 270 for (int i = 0; i < _log.Count; i++) { 271 LogItem item = (LogItem)_log[i]; 272 if (item.Tracked) { 273 builder.Append((int)item.Type); 274 builder.Append(":"); 275 builder.Append(item.Index); 276 if (i < (_log.Count - 1)) { 277 builder.Append(","); 278 } 279 280 realLogCount++; 281 } 282 } 283 284 if (realLogCount > 0) { 285 nodes[0] = builder.ToString(); 286 hasViewState = true; 287 } 288 } 289 290 for (int i = 0; i < Count; i++) { 291 nodes[i + 1] = ((IStateManager)this[i]).SaveViewState(); 292 if (nodes[i + 1] != null) { 293 hasViewState = true; 294 } 295 } 296 297 return (hasViewState ? nodes : null); 298 } 299 300 301 /// <internalonly/> IStateManager.TrackViewState()302 void IStateManager.TrackViewState() { 303 _isTrackingViewState = true; 304 for (int i = 0; i < Count; i++) { 305 ((IStateManager)this[i]).TrackViewState(); 306 } 307 } 308 #endregion 309 310 /// <devdoc> 311 /// Convenience class for storing and using log entries. 312 /// </devdoc> 313 private class LogItem { 314 private LogItemType _type; 315 private int _index; 316 private bool _tracked; 317 LogItem(LogItemType type, int index, bool tracked)318 public LogItem(LogItemType type, int index, bool tracked) { 319 _type = type; 320 _index = index; 321 _tracked = tracked; 322 } 323 324 public int Index { 325 get { 326 return _index; 327 } 328 } 329 330 public bool Tracked { 331 get { 332 return _tracked; 333 } 334 set { 335 _tracked = value; 336 } 337 } 338 339 public LogItemType Type { 340 get { 341 return _type; 342 } 343 } 344 345 } 346 347 /// <devdoc> 348 /// Convenience enumeration for identifying log commands 349 /// </devdoc> 350 private enum LogItemType { 351 Insert = 0, 352 Remove = 1, 353 Clear = 2 354 } 355 356 // This is a copy of the ArrayListEnumeratorSimple in ArrayList.cs 357 private class MenuItemCollectionEnumerator : IEnumerator { 358 private MenuItemCollection list; 359 private int index; 360 private int version; 361 private MenuItem currentElement; 362 MenuItemCollectionEnumerator(MenuItemCollection list)363 internal MenuItemCollectionEnumerator(MenuItemCollection list) { 364 this.list = list; 365 this.index = -1; 366 version = list._version; 367 } 368 MoveNext()369 public bool MoveNext() { 370 if (version != list._version) 371 throw new InvalidOperationException(SR.GetString(SR.ListEnumVersionMismatch)); 372 373 if (index < (list.Count - 1)) { 374 index++; 375 currentElement = list[index]; 376 return true; 377 } 378 else 379 index = list.Count; 380 return false; 381 } 382 383 object IEnumerator.Current { 384 get { 385 return Current; 386 } 387 } 388 389 public MenuItem Current { 390 get { 391 if (index == -1) 392 throw new InvalidOperationException(SR.GetString(SR.ListEnumCurrentOutOfRange)); 393 if (index >= list.Count) 394 throw new InvalidOperationException(SR.GetString(SR.ListEnumCurrentOutOfRange)); 395 return currentElement; 396 } 397 } 398 Reset()399 public void Reset() { 400 if (version != list._version) 401 throw new InvalidOperationException(SR.GetString(SR.ListEnumVersionMismatch)); 402 currentElement = null; 403 index = -1; 404 } 405 } 406 } 407 } 408