PageRenderTime 59ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/CSPspEmu.Frontend.WinForms/ObjectListView/ObjectListView/Implementation/VirtualGroups.cs

http://github.com/soywiz/cspspemu
C# | 382 lines | 162 code | 42 blank | 178 comment | 8 complexity | daa37da4219f23bf0146417ac8fa5c7e MD5 | raw file
  1. /*
  2. * Virtual groups - Classes and interfaces needed to implement virtual groups
  3. *
  4. * Author: Phillip Piper
  5. * Date: 28/08/2009 11:10am
  6. *
  7. * Change log:
  8. * 2011-02-21 JPP - Correctly honor group comparer and collapsible groups settings
  9. * v2.3
  10. * 2009-08-28 JPP - Initial version
  11. *
  12. * To do:
  13. *
  14. * Copyright (C) 2009-2012 Phillip Piper
  15. *
  16. * This program is free software: you can redistribute it and/or modify
  17. * it under the terms of the GNU General Public License as published by
  18. * the Free Software Foundation, either version 3 of the License, or
  19. * (at your option) any later version.
  20. *
  21. * This program is distributed in the hope that it will be useful,
  22. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  23. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  24. * GNU General Public License for more details.
  25. *
  26. * You should have received a copy of the GNU General Public License
  27. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  28. *
  29. * If you wish to use this code in a closed source application, please contact phillip_piper@bigfoot.com.
  30. */
  31. using System;
  32. using System.Collections.Generic;
  33. using System.Windows.Forms;
  34. using System.Runtime.InteropServices;
  35. namespace BrightIdeasSoftware
  36. {
  37. /// <summary>
  38. /// A IVirtualGroups is the interface that a virtual list must implement to support virtual groups
  39. /// </summary>
  40. public interface IVirtualGroups
  41. {
  42. /// <summary>
  43. /// Return the list of groups that should be shown according to the given parameters
  44. /// </summary>
  45. /// <param name="parameters"></param>
  46. /// <returns></returns>
  47. IList<OLVGroup> GetGroups(GroupingParameters parameters);
  48. /// <summary>
  49. /// Return the index of the item that appears at the given position within the given group.
  50. /// </summary>
  51. /// <param name="group"></param>
  52. /// <param name="indexWithinGroup"></param>
  53. /// <returns></returns>
  54. int GetGroupMember(OLVGroup group, int indexWithinGroup);
  55. /// <summary>
  56. /// Return the index of the group to which the given item belongs
  57. /// </summary>
  58. /// <param name="itemIndex"></param>
  59. /// <returns></returns>
  60. int GetGroup(int itemIndex);
  61. /// <summary>
  62. /// Return the index at which the given item is shown in the given group
  63. /// </summary>
  64. /// <param name="group"></param>
  65. /// <param name="itemIndex"></param>
  66. /// <returns></returns>
  67. int GetIndexWithinGroup(OLVGroup group, int itemIndex);
  68. /// <summary>
  69. /// A hint that the given range of items are going to be required
  70. /// </summary>
  71. /// <param name="fromGroupIndex"></param>
  72. /// <param name="fromIndex"></param>
  73. /// <param name="toGroupIndex"></param>
  74. /// <param name="toIndex"></param>
  75. void CacheHint(int fromGroupIndex, int fromIndex, int toGroupIndex, int toIndex);
  76. }
  77. /// <summary>
  78. /// This is a safe, do nothing implementation of a grouping strategy
  79. /// </summary>
  80. public class AbstractVirtualGroups : IVirtualGroups
  81. {
  82. /// <summary>
  83. /// Return the list of groups that should be shown according to the given parameters
  84. /// </summary>
  85. /// <param name="parameters"></param>
  86. /// <returns></returns>
  87. public virtual IList<OLVGroup> GetGroups(GroupingParameters parameters)
  88. {
  89. return new List<OLVGroup>();
  90. }
  91. /// <summary>
  92. /// Return the index of the item that appears at the given position within the given group.
  93. /// </summary>
  94. /// <param name="group"></param>
  95. /// <param name="indexWithinGroup"></param>
  96. /// <returns></returns>
  97. public virtual int GetGroupMember(OLVGroup group, int indexWithinGroup)
  98. {
  99. return -1;
  100. }
  101. /// <summary>
  102. /// Return the index of the group to which the given item belongs
  103. /// </summary>
  104. /// <param name="itemIndex"></param>
  105. /// <returns></returns>
  106. public virtual int GetGroup(int itemIndex)
  107. {
  108. return -1;
  109. }
  110. /// <summary>
  111. /// Return the index at which the given item is shown in the given group
  112. /// </summary>
  113. /// <param name="group"></param>
  114. /// <param name="itemIndex"></param>
  115. /// <returns></returns>
  116. public virtual int GetIndexWithinGroup(OLVGroup group, int itemIndex)
  117. {
  118. return -1;
  119. }
  120. /// <summary>
  121. /// A hint that the given range of items are going to be required
  122. /// </summary>
  123. /// <param name="fromGroupIndex"></param>
  124. /// <param name="fromIndex"></param>
  125. /// <param name="toGroupIndex"></param>
  126. /// <param name="toIndex"></param>
  127. public virtual void CacheHint(int fromGroupIndex, int fromIndex, int toGroupIndex, int toIndex)
  128. {
  129. }
  130. }
  131. /// <summary>
  132. /// Provides grouping functionality to a FastObjectListView
  133. /// </summary>
  134. public class FastListGroupingStrategy : AbstractVirtualGroups
  135. {
  136. /// <summary>
  137. /// Create groups for FastListView
  138. /// </summary>
  139. /// <param name="parmameters"></param>
  140. /// <returns></returns>
  141. public override IList<OLVGroup> GetGroups(GroupingParameters parmameters)
  142. {
  143. // There is a lot of overlap between this method and ObjectListView.MakeGroups()
  144. // Any changes made here may need to be reflected there
  145. // This strategy can only be used on FastObjectListViews
  146. FastObjectListView folv = (FastObjectListView) parmameters.ListView;
  147. // Separate the list view items into groups, using the group key as the descrimanent
  148. int objectCount = 0;
  149. NullableDictionary<object, List<object>> map = new NullableDictionary<object, List<object>>();
  150. foreach (object model in folv.FilteredObjects)
  151. {
  152. object key = parmameters.GroupByColumn.GetGroupKey(model);
  153. if (!map.ContainsKey(key))
  154. map[key] = new List<object>();
  155. map[key].Add(model);
  156. objectCount++;
  157. }
  158. // Sort the items within each group
  159. // TODO: Give parameters a ModelComparer property
  160. OLVColumn primarySortColumn = parmameters.SortItemsByPrimaryColumn
  161. ? parmameters.ListView.GetColumn(0)
  162. : parmameters.PrimarySort;
  163. ModelObjectComparer sorter = new ModelObjectComparer(primarySortColumn, parmameters.PrimarySortOrder,
  164. parmameters.SecondarySort, parmameters.SecondarySortOrder);
  165. foreach (object key in map.Keys)
  166. {
  167. map[key].Sort(sorter);
  168. }
  169. // Make a list of the required groups
  170. List<OLVGroup> groups = new List<OLVGroup>();
  171. foreach (object key in map.Keys)
  172. {
  173. string title = parmameters.GroupByColumn.ConvertGroupKeyToTitle(key);
  174. if (!string.IsNullOrEmpty(parmameters.TitleFormat))
  175. {
  176. int count = map[key].Count;
  177. string format = (count == 1 ? parmameters.TitleSingularFormat : parmameters.TitleFormat);
  178. try
  179. {
  180. title = string.Format(format, title, count);
  181. }
  182. catch (FormatException)
  183. {
  184. title = "Invalid group format: " + format;
  185. }
  186. }
  187. OLVGroup lvg = new OLVGroup(title);
  188. lvg.Collapsible = folv.HasCollapsibleGroups;
  189. lvg.Key = key;
  190. lvg.SortValue = key as IComparable;
  191. lvg.Contents = map[key].ConvertAll<int>(delegate(object x) { return folv.IndexOf(x); });
  192. lvg.VirtualItemCount = map[key].Count;
  193. if (parmameters.GroupByColumn.GroupFormatter != null)
  194. parmameters.GroupByColumn.GroupFormatter(lvg, parmameters);
  195. groups.Add(lvg);
  196. }
  197. // Sort the groups
  198. if (parmameters.GroupByOrder != SortOrder.None)
  199. groups.Sort(parmameters.GroupComparer ?? new OLVGroupComparer(parmameters.GroupByOrder));
  200. // Build an array that remembers which group each item belongs to.
  201. this.indexToGroupMap = new List<int>(objectCount);
  202. this.indexToGroupMap.AddRange(new int[objectCount]);
  203. for (int i = 0; i < groups.Count; i++)
  204. {
  205. OLVGroup group = groups[i];
  206. List<int> members = (List<int>) group.Contents;
  207. foreach (int j in members)
  208. this.indexToGroupMap[j] = i;
  209. }
  210. return groups;
  211. }
  212. private List<int> indexToGroupMap;
  213. /// <summary>
  214. ///
  215. /// </summary>
  216. /// <param name="group"></param>
  217. /// <param name="indexWithinGroup"></param>
  218. /// <returns></returns>
  219. public override int GetGroupMember(OLVGroup group, int indexWithinGroup)
  220. {
  221. return (int) group.Contents[indexWithinGroup];
  222. }
  223. /// <summary>
  224. ///
  225. /// </summary>
  226. /// <param name="itemIndex"></param>
  227. /// <returns></returns>
  228. public override int GetGroup(int itemIndex)
  229. {
  230. return this.indexToGroupMap[itemIndex];
  231. }
  232. /// <summary>
  233. ///
  234. /// </summary>
  235. /// <param name="group"></param>
  236. /// <param name="itemIndex"></param>
  237. /// <returns></returns>
  238. public override int GetIndexWithinGroup(OLVGroup group, int itemIndex)
  239. {
  240. return group.Contents.IndexOf(itemIndex);
  241. }
  242. }
  243. /// <summary>
  244. /// This is the COM interface that a ListView must be given in order for groups in virtual lists to work.
  245. /// </summary>
  246. /// <remarks>
  247. /// This interface is NOT documented by MS. It was found on Greg Chapell's site. This means that there is
  248. /// no guarantee that it will work on future versions of Windows, nor continue to work on current ones.
  249. /// </remarks>
  250. [ComImport(),
  251. InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
  252. Guid("44C09D56-8D3B-419D-A462-7B956B105B47")]
  253. internal interface IOwnerDataCallback
  254. {
  255. /// <summary>
  256. /// Not sure what this does
  257. /// </summary>
  258. /// <param name="i"></param>
  259. /// <param name="pt"></param>
  260. void GetItemPosition(int i, out NativeMethods.POINT pt);
  261. /// <summary>
  262. /// Not sure what this does
  263. /// </summary>
  264. /// <param name="t"></param>
  265. /// <param name="pt"></param>
  266. void SetItemPosition(int t, NativeMethods.POINT pt);
  267. /// <summary>
  268. /// Get the index of the item that occurs at the n'th position of the indicated group.
  269. /// </summary>
  270. /// <param name="groupIndex">Index of the group</param>
  271. /// <param name="n">Index within the group</param>
  272. /// <param name="itemIndex">Index of the item within the whole list</param>
  273. void GetItemInGroup(int groupIndex, int n, out int itemIndex);
  274. /// <summary>
  275. /// Get the index of the group to which the given item belongs
  276. /// </summary>
  277. /// <param name="itemIndex">Index of the item within the whole list</param>
  278. /// <param name="occurrenceCount">Which occurences of the item is wanted</param>
  279. /// <param name="groupIndex">Index of the group</param>
  280. void GetItemGroup(int itemIndex, int occurrenceCount, out int groupIndex);
  281. /// <summary>
  282. /// Get the number of groups that contain the given item
  283. /// </summary>
  284. /// <param name="itemIndex">Index of the item within the whole list</param>
  285. /// <param name="occurrenceCount">How many groups does it occur within</param>
  286. void GetItemGroupCount(int itemIndex, out int occurrenceCount);
  287. /// <summary>
  288. /// A hint to prepare any cache for the given range of requests
  289. /// </summary>
  290. /// <param name="i"></param>
  291. /// <param name="j"></param>
  292. void OnCacheHint(NativeMethods.LVITEMINDEX i, NativeMethods.LVITEMINDEX j);
  293. }
  294. /// <summary>
  295. /// A default implementation of the IOwnerDataCallback interface
  296. /// </summary>
  297. [Guid("6FC61F50-80E8-49b4-B200-3F38D3865ABD")]
  298. internal class OwnerDataCallbackImpl : IOwnerDataCallback
  299. {
  300. public OwnerDataCallbackImpl(VirtualObjectListView olv)
  301. {
  302. this.olv = olv;
  303. }
  304. VirtualObjectListView olv;
  305. #region IOwnerDataCallback Members
  306. public void GetItemPosition(int i, out NativeMethods.POINT pt)
  307. {
  308. //System.Diagnostics.Debug.WriteLine("GetItemPosition");
  309. throw new NotSupportedException();
  310. }
  311. public void SetItemPosition(int t, NativeMethods.POINT pt)
  312. {
  313. //System.Diagnostics.Debug.WriteLine("SetItemPosition");
  314. throw new NotSupportedException();
  315. }
  316. public void GetItemInGroup(int groupIndex, int n, out int itemIndex)
  317. {
  318. //System.Diagnostics.Debug.WriteLine(String.Format("-> GetItemInGroup({0}, {1})", groupIndex, n));
  319. itemIndex = this.olv.GroupingStrategy.GetGroupMember(this.olv.OLVGroups[groupIndex], n);
  320. //System.Diagnostics.Debug.WriteLine(String.Format("<- {0}", itemIndex));
  321. }
  322. public void GetItemGroup(int itemIndex, int occurrenceCount, out int groupIndex)
  323. {
  324. //System.Diagnostics.Debug.WriteLine(String.Format("GetItemGroup({0}, {1})", itemIndex, occurrenceCount));
  325. groupIndex = this.olv.GroupingStrategy.GetGroup(itemIndex);
  326. //System.Diagnostics.Debug.WriteLine(String.Format("<- {0}", groupIndex));
  327. }
  328. public void GetItemGroupCount(int itemIndex, out int occurrenceCount)
  329. {
  330. //System.Diagnostics.Debug.WriteLine(String.Format("GetItemGroupCount({0})", itemIndex));
  331. occurrenceCount = 1;
  332. }
  333. public void OnCacheHint(NativeMethods.LVITEMINDEX from, NativeMethods.LVITEMINDEX to)
  334. {
  335. //System.Diagnostics.Debug.WriteLine(String.Format("OnCacheHint({0}, {1}, {2}, {3})", from.iGroup, from.iItem, to.iGroup, to.iItem));
  336. this.olv.GroupingStrategy.CacheHint(from.iGroup, from.iItem, to.iGroup, to.iItem);
  337. }
  338. #endregion
  339. }
  340. }