PageRenderTime 51ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/Visual Studio 2013/Source/wpf/src/Framework/System/Windows/Documents/List.cs

https://github.com/tforsberg/z
C# | 354 lines | 203 code | 37 blank | 114 comment | 44 complexity | dfec6e6f7bb5428b52ea5b005024d882 MD5 | raw file
  1. //---------------------------------------------------------------------------
  2. //
  3. // Copyright (C) Microsoft Corporation. All rights reserved.
  4. //
  5. // Description: Implements a List element, a container for ListElementItems: '
  6. // block elements designed to be formatted with markers such as
  7. // bullets and numbering.
  8. //
  9. // History:
  10. // 06/06/2003 : [....] - created.
  11. // 10/28/2004 : [....] - ContentElements refactoring.
  12. //
  13. //---------------------------------------------------------------------------
  14. using System.ComponentModel;
  15. using System.Windows.Markup;
  16. using MS.Internal;
  17. using MS.Internal.PtsHost.UnsafeNativeMethods; // PTS restrictions
  18. namespace System.Windows.Documents
  19. {
  20. /// <summary>
  21. /// Implements a List element, a container for ListItems: block
  22. /// elements designed to be formatted with markers such as bullets and
  23. /// numbering.
  24. /// </summary>
  25. [ContentProperty("ListItems")]
  26. public class List : Block
  27. {
  28. //-------------------------------------------------------------------
  29. //
  30. // Constructors
  31. //
  32. //-------------------------------------------------------------------
  33. #region Constructors
  34. /// <summary>
  35. /// List static constructor. Registers metadata for its properties.
  36. /// </summary>
  37. static List()
  38. {
  39. DefaultStyleKeyProperty.OverrideMetadata(typeof(List), new FrameworkPropertyMetadata(typeof(List)));
  40. }
  41. /// <summary>
  42. /// Initializes a new instance of a List class.
  43. /// </summary>
  44. public List()
  45. : base()
  46. {
  47. }
  48. /// <summary>
  49. /// Initializes a new instance of a List class specifying its first ListItem child.
  50. /// </summary>
  51. /// <param name="listItem">
  52. /// ListItem to be inserted as a first child of this List.
  53. /// </param>
  54. public List(ListItem listItem)
  55. : base()
  56. {
  57. if (listItem == null)
  58. {
  59. throw new ArgumentNullException("listItem");
  60. }
  61. this.ListItems.Add(listItem);
  62. }
  63. #endregion Constructors
  64. //-------------------------------------------------------------------
  65. //
  66. // Public Properties
  67. //
  68. //-------------------------------------------------------------------
  69. #region Public Properties
  70. /// <value>
  71. /// Collection of ListItems contained in this List.
  72. /// </value>
  73. [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
  74. public ListItemCollection ListItems
  75. {
  76. get
  77. {
  78. return new ListItemCollection(this, /*isOwnerParent*/true);
  79. }
  80. }
  81. /// <summary>
  82. /// DependencyProperty for <see cref="MarkerStyle" /> property.
  83. /// </summary>
  84. public static readonly DependencyProperty MarkerStyleProperty =
  85. DependencyProperty.Register(
  86. "MarkerStyle",
  87. typeof(TextMarkerStyle),
  88. typeof(List),
  89. new FrameworkPropertyMetadata(
  90. TextMarkerStyle.Disc,
  91. FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender),
  92. new ValidateValueCallback(IsValidMarkerStyle));
  93. /// <summary>
  94. /// Type of bullet or number to be used by default with ListElementItems
  95. /// contained by this List
  96. /// </summary>
  97. public TextMarkerStyle MarkerStyle
  98. {
  99. get { return (TextMarkerStyle)GetValue(MarkerStyleProperty); }
  100. set { SetValue(MarkerStyleProperty, value); }
  101. }
  102. /// <summary>
  103. /// DependencyProperty for <see cref="MarkerOffset" /> property.
  104. /// </summary>
  105. public static readonly DependencyProperty MarkerOffsetProperty =
  106. DependencyProperty.Register(
  107. "MarkerOffset",
  108. typeof(double),
  109. typeof(List),
  110. new FrameworkPropertyMetadata(
  111. Double.NaN,
  112. FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender),
  113. new ValidateValueCallback(IsValidMarkerOffset));
  114. /// <summary>
  115. /// Desired distance between each contained ListItem's content and
  116. /// near edge of the associated marker.
  117. /// </summary>
  118. [TypeConverter(typeof(LengthConverter))]
  119. public double MarkerOffset
  120. {
  121. get { return (double)GetValue(MarkerOffsetProperty); }
  122. set { SetValue(MarkerOffsetProperty, value); }
  123. }
  124. /// <summary>
  125. /// DependencyProperty for <see cref="StartIndex" /> property.
  126. /// </summary>
  127. public static readonly DependencyProperty StartIndexProperty =
  128. DependencyProperty.Register(
  129. "StartIndex",
  130. typeof(int),
  131. typeof(List),
  132. new FrameworkPropertyMetadata(
  133. 1,
  134. FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender),
  135. new ValidateValueCallback(IsValidStartIndex));
  136. /// <summary>
  137. /// Item index of the first ListItem that is immediate child of
  138. /// this List.
  139. /// </summary>
  140. public int StartIndex
  141. {
  142. get { return (int)GetValue(StartIndexProperty); }
  143. set { SetValue(StartIndexProperty, value); }
  144. }
  145. #endregion Public Properties
  146. //-------------------------------------------------------------------
  147. //
  148. // Internal Methods
  149. //
  150. //-------------------------------------------------------------------
  151. #region Internal Methods
  152. /// <summary>
  153. /// Returns the integer "index" of a specified ListItem that is an immediate child of
  154. /// this List. This index is defined to be a sequential counter of ListElementItems only
  155. /// (skipping other elements) among this List's immediate children.
  156. ///
  157. /// The list item index of the first child of type ListItem is specified by
  158. /// this.StartListIndex, which has a default value of 1.
  159. ///
  160. /// The index returned by this method is used in the formation of some ListItem
  161. /// markers such as "(b)" and "viii." (as opposed to others, like disks and wedges,
  162. /// which are not sequential-position-dependent).
  163. /// </summary>
  164. /// <param name="item">The item whose index is to be returned.</param>
  165. /// <returns>Returns the index of a specified ListItem.</returns>
  166. internal int GetListItemIndex(ListItem item)
  167. {
  168. // Check for valid arg
  169. if (item == null)
  170. {
  171. throw new ArgumentNullException("item");
  172. }
  173. if (item.Parent != this)
  174. {
  175. throw new InvalidOperationException(SR.Get(SRID.ListElementItemNotAChildOfList));
  176. }
  177. // Count ListItem siblings (not other element types) back to first item.
  178. int itemIndex = StartIndex;
  179. TextPointer textNav = new TextPointer(this.ContentStart);
  180. while (textNav.CompareTo(this.ContentEnd) != 0)
  181. {
  182. // ListItem is a content element, so look for ElementStart runs only
  183. if (textNav.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.ElementStart)
  184. {
  185. DependencyObject element = textNav.GetAdjacentElementFromOuterPosition(LogicalDirection.Forward);
  186. if (element is ListItem)
  187. {
  188. if (element == item)
  189. {
  190. break;
  191. }
  192. if (itemIndex < int.MaxValue)
  193. {
  194. ++itemIndex;
  195. }
  196. }
  197. // Skip entire content element content, because we are looking
  198. // only for immediate children.
  199. textNav.MoveToPosition(((TextElement)element).ElementEnd);
  200. }
  201. else
  202. {
  203. textNav.MoveToNextContextPosition(LogicalDirection.Forward);
  204. }
  205. }
  206. return itemIndex;
  207. }
  208. /// <summary>
  209. /// Inserts a List around a sequence of Blocks
  210. /// starting from firstBlock ending with lastBlock.
  211. /// the List must be empty and not inserted in a tree
  212. /// before the operation
  213. /// </summary>
  214. /// <param name="firstBlock"></param>
  215. /// <param name="lastBlock"></param>
  216. internal void Apply(Block firstBlock, Block lastBlock)
  217. {
  218. Invariant.Assert(this.Parent == null, "Cannot Apply List Because It Is Inserted In The Tree Already.");
  219. Invariant.Assert(this.IsEmpty, "Cannot Apply List Because It Is Not Empty.");
  220. Invariant.Assert(firstBlock.Parent == lastBlock.Parent, "Cannot Apply List Because Block Are Not Siblings.");
  221. TextContainer textContainer = this.TextContainer;
  222. textContainer.BeginChange();
  223. try
  224. {
  225. // Wrap all block items into this List element
  226. this.Reposition(firstBlock.ElementStart, lastBlock.ElementEnd);
  227. // Add ListItem elements
  228. Block block = firstBlock;
  229. while (block != null)
  230. {
  231. ListItem listItem;
  232. if (block is List)
  233. {
  234. // To wrap List into list item we pull it into previous ListItem (if any) as sublist
  235. listItem = block.ElementStart.GetAdjacentElement(LogicalDirection.Backward) as ListItem;
  236. if (listItem != null)
  237. {
  238. // Wrap the List into preceding ListItem
  239. listItem.Reposition(listItem.ContentStart, block.ElementEnd);
  240. }
  241. else
  242. {
  243. // No preceding ListItem. Create new one
  244. listItem = new ListItem();
  245. listItem.Reposition(block.ElementStart, block.ElementEnd);
  246. }
  247. }
  248. else
  249. {
  250. // To wrap paragraph into list item we need to create a new one
  251. //
  252. listItem = new ListItem();
  253. listItem.Reposition(block.ElementStart, block.ElementEnd);
  254. // MS Word-like heuristic: clear margin from a paragraph before wrapping it into a list item
  255. // Note: using TextContainer to make sure that undo unit is created.
  256. block.ClearValue(Block.MarginProperty);
  257. block.ClearValue(Block.PaddingProperty);
  258. block.ClearValue(Paragraph.TextIndentProperty);
  259. }
  260. // Stop when the last paragraph is covered
  261. block = block == lastBlock ? null : (Block)listItem.ElementEnd.GetAdjacentElement(LogicalDirection.Forward);
  262. }
  263. // We need to set appropriate FlowDirection property on the new List and its paragraph children.
  264. // We take the FlowDirection value from the first paragraph's FlowDirection value.
  265. TextRangeEdit.SetParagraphProperty(this.ElementStart, this.ElementEnd,
  266. Paragraph.FlowDirectionProperty, firstBlock.GetValue(Paragraph.FlowDirectionProperty));
  267. }
  268. finally
  269. {
  270. textContainer.EndChange();
  271. }
  272. }
  273. #endregion Internal Methods
  274. //-------------------------------------------------------------------
  275. //
  276. // Private Methods
  277. //
  278. //-------------------------------------------------------------------
  279. #region Private Methods
  280. private static bool IsValidMarkerStyle(object o)
  281. {
  282. TextMarkerStyle value = (TextMarkerStyle)o;
  283. return value == TextMarkerStyle.None
  284. || value == TextMarkerStyle.Disc
  285. || value == TextMarkerStyle.Circle
  286. || value == TextMarkerStyle.Square
  287. || value == TextMarkerStyle.Box
  288. || value == TextMarkerStyle.LowerRoman
  289. || value == TextMarkerStyle.UpperRoman
  290. || value == TextMarkerStyle.LowerLatin
  291. || value == TextMarkerStyle.UpperLatin
  292. || value == TextMarkerStyle.Decimal;
  293. }
  294. private static bool IsValidStartIndex(object o)
  295. {
  296. int value = (int)o;
  297. return (value > 0);
  298. }
  299. private static bool IsValidMarkerOffset(object o)
  300. {
  301. double value = (double)o;
  302. double maxOffset = Math.Min(1000000, PTS.MaxPageSize);
  303. double minOffset = -maxOffset;
  304. if (Double.IsNaN(value))
  305. {
  306. // Default
  307. return true;
  308. }
  309. if (value < minOffset || value > maxOffset)
  310. {
  311. return false;
  312. }
  313. return true;
  314. }
  315. #endregion Private Methods
  316. }
  317. }