Author: gert
Date: 2006-10-06 14:19:29 -0400 (Fri, 06 Oct 2006)
New Revision: 66371
Modified:
trunk/mcs/class/Managed.Windows.Forms/System.Windows.Forms/ChangeLog
trunk/mcs/class/Managed.Windows.Forms/System.Windows.Forms/ListView.cs
trunk/mcs/class/Managed.Windows.Forms/System.Windows.Forms/ListViewItem.cs
trunk/mcs/class/Managed.Windows.Forms/Test/System.Windows.Forms/ChangeLog
trunk/mcs/class/Managed.Windows.Forms/Test/System.Windows.Forms/ListViewCollectionsTest.cs
trunk/mcs/class/Managed.Windows.Forms/Test/System.Windows.Forms/ListViewTest.cs
Log:
* ListViewItem.cs: Collections of selected and checked items are now
dynamically rebuilt. Whenever an item is (un)checked or (de)selected
we mark the collection "dirty".
* ListView.cs: Marked collections readonly. Modified UpdateSelection
to only clear SelectedItems when a new item is selected and MultiSelect
is enabled. CheckedItems and SelectedItems now subscribe to Changed
event of ListViewItemCollection, and mark its list dirty whenever
that event is fire. This allows us to return selected/checked items
in the same order as they are in the Items collection. This matches
the MS behavior.
* ListViewCollectionsTest.cs: Added tests for bug #79415.
* ListViewTest.cs: Enabled test for bug #79416.
Modified: trunk/mcs/class/Managed.Windows.Forms/System.Windows.Forms/ChangeLog
===================================================================
--- trunk/mcs/class/Managed.Windows.Forms/System.Windows.Forms/ChangeLog
2006-10-06 18:13:37 UTC (rev 66370)
+++ trunk/mcs/class/Managed.Windows.Forms/System.Windows.Forms/ChangeLog
2006-10-06 18:19:29 UTC (rev 66371)
@@ -1,3 +1,16 @@
+2006-10-06 Gert Driesen <[EMAIL PROTECTED]>
+
+ * ListViewItem.cs: Collections of selected and checked items are now
+ dynamically rebuilt. Whenever an item is (un)checked or (de)selected
+ we mark the collection "dirty".
+ * ListView.cs: Marked collections readonly. Modified UpdateSelection
+ to only clear SelectedItems when a new item is selected and MultiSelect
+ is enabled. CheckedItems and SelectedItems now subscribe to Changed
+ event of ListViewItemCollection, and mark its list dirty whenever
+ that event is fire. This allows us to return selected/checked items
+ in the same order as they are in the Items collection. This matches
+ the MS behavior.
+
2006-10-06 Chris Toshok <[EMAIL PROTECTED]>
* NotifyIcon.cs (HandleMouseUp): only show the context menu on
@@ -604,7 +617,7 @@
* TextBoxBase.cs: in CalculateScrollBars make sure LargeChange is
>= 0. Fixes bug #79370.
-2006-09-15 Jonathan Pobst <[EMAIL PROTECTED]>
+2006-09-15 Jonathan Pobst <[EMAIL PROTECTED]>
* FlowLayoutPanel.cs, FlowLayoutSettings.cs: Initial commit.
* Control.cs:
Add properties: LayoutEngine, Margin, DefaultMargin.
@@ -672,7 +685,7 @@
* FileDialog.cs: Store the configuration file in ~/.mono/mwf_config
-2006-09-13 Jonathan Pobst <[EMAIL PROTECTED]>
+2006-09-13 Jonathan Pobst <[EMAIL PROTECTED]>
* SplitContainer.cs, SplitterPanel.cs: Initial implementation [2.0].
Modified: trunk/mcs/class/Managed.Windows.Forms/System.Windows.Forms/ListView.cs
===================================================================
--- trunk/mcs/class/Managed.Windows.Forms/System.Windows.Forms/ListView.cs
2006-10-06 18:13:37 UTC (rev 66370)
+++ trunk/mcs/class/Managed.Windows.Forms/System.Windows.Forms/ListView.cs
2006-10-06 18:19:29 UTC (rev 66371)
@@ -52,9 +52,9 @@
private bool allow_column_reorder = false;
private bool auto_arrange = true;
private bool check_boxes = false;
- private CheckedIndexCollection checked_indices;
- private CheckedListViewItemCollection checked_items;
- private ColumnHeaderCollection columns;
+ private readonly CheckedIndexCollection checked_indices;
+ private readonly CheckedListViewItemCollection checked_items;
+ private readonly ColumnHeaderCollection columns;
internal ListViewItem focused_item;
private bool full_row_select = false;
private bool grid_lines = false;
@@ -62,13 +62,13 @@
private bool hide_selection = true;
private bool hover_selection = false;
private IComparer item_sorter;
- private ListViewItemCollection items;
+ private readonly ListViewItemCollection items;
private bool label_edit = false;
private bool label_wrap = true;
private bool multiselect = true;
private bool scrollable = true;
- private SelectedIndexCollection selected_indices;
- private SelectedListViewItemCollection selected_items;
+ private readonly SelectedIndexCollection selected_indices;
+ private readonly SelectedListViewItemCollection selected_items;
private SortOrder sort_order = SortOrder.None;
private ImageList state_image_list;
private bool updating = false;
@@ -130,11 +130,11 @@
public ListView ()
{
background_color = ThemeEngine.Current.ColorWindow;
+ items = new ListViewItemCollection (this);
checked_indices = new CheckedIndexCollection (this);
checked_items = new CheckedListViewItemCollection
(this);
columns = new ColumnHeaderCollection (this);
foreground_color = SystemColors.WindowText;
- items = new ListViewItemCollection (this);
selected_indices = new SelectedIndexCollection (this);
selected_items = new SelectedListViewItemCollection
(this);
@@ -1098,19 +1098,6 @@
}
}
- internal void UpdateSelection (ListViewItem item)
- {
- if (item.Selected) {
- if (!MultiSelect && SelectedItems.Count > 0)
- SelectedItems.Clear ();
-
- if (!SelectedItems.list.Contains (item))
- SelectedItems.list.Add (item);
- } else {
- SelectedItems.list.Remove (item);
- }
- }
-
private bool KeySearchString (KeyEventArgs ke)
{
int current_tickcnt = Environment.TickCount;
@@ -1192,7 +1179,7 @@
private bool SelectItems (ArrayList sel_items)
{
bool changed = false;
- ArrayList curr_items = (ArrayList)
SelectedItems.list.Clone ();
+ ArrayList curr_items = SelectedItems.List;
foreach (ListViewItem item in curr_items)
if (!sel_items.Contains (item)) {
item.Selected = false;
@@ -1516,8 +1503,8 @@
else
box_select_mode =
BoxSelect.Normal;
box_select_start = pt;
- prev_selection = (ArrayList)
owner.SelectedItems.list.Clone ();
- } else if (owner.selected_indices.Count
> 0) {
+ prev_selection =
owner.SelectedItems.List;
+ } else if (owner.SelectedItems.Count >
0) {
owner.SelectedItems.Clear ();
owner.OnSelectedIndexChanged
(EventArgs.Empty);
}
@@ -1930,7 +1917,7 @@
return;
}
- items.list.Sort (item_sorter);
+ items.Sort (item_sorter);
if (redraw)
this.Redraw (true);
}
@@ -2284,9 +2271,10 @@
private int [] GetIndices ()
{
- int [] indices = new int [Count];
- for (int i = 0; i < owner.CheckedItems.Count;
i++) {
- ListViewItem item = owner.CheckedItems
[i];
+ ArrayList checked_items =
owner.CheckedItems.List;
+ int [] indices = new int [checked_items.Count];
+ for (int i = 0; i < checked_items.Count; i++) {
+ ListViewItem item = (ListViewItem)
checked_items [i];
indices [i] = item.Index;
}
return indices;
@@ -2295,14 +2283,15 @@
public class CheckedListViewItemCollection : IList,
ICollection, IEnumerable
{
- internal readonly ArrayList list;
private readonly ListView owner;
+ private ArrayList list;
#region Public Constructor
public CheckedListViewItemCollection (ListView owner)
{
- list = new ArrayList ();
this.owner = owner;
+ this.owner.Items.Changed += new
CollectionChangedHandler (
+ ItemsCollection_Changed);
}
#endregion // Public Constructor
@@ -2312,7 +2301,7 @@
get {
if (!owner.CheckBoxes)
return 0;
- return list.Count;
+ return List.Count;
}
}
@@ -2322,14 +2311,15 @@
public ListViewItem this [int index] {
get {
- if (index < 0 || index >= Count)
+ ArrayList checked_items = List;
+ if (index < 0 || index >=
checked_items.Count)
throw new
ArgumentOutOfRangeException ("index");
- return (ListViewItem) list [index];
+ return (ListViewItem) checked_items
[index];
}
}
bool ICollection.IsSynchronized {
- get { return list.IsSynchronized; }
+ get { return false; }
}
object ICollection.SyncRoot {
@@ -2351,21 +2341,21 @@
{
if (!owner.CheckBoxes)
return false;
- return list.Contains (item);
+ return List.Contains (item);
}
public void CopyTo (Array dest, int index)
{
if (!owner.CheckBoxes)
return;
- list.CopyTo (dest, index);
+ List.CopyTo (dest, index);
}
public IEnumerator GetEnumerator ()
{
if (!owner.CheckBoxes)
return (new ListViewItem
[0]).GetEnumerator ();
- return list.GetEnumerator ();
+ return List.GetEnumerator ();
}
int IList.Add (object value)
@@ -2411,9 +2401,33 @@
{
if (!owner.CheckBoxes)
return -1;
- return list.IndexOf (item);
+ return List.IndexOf (item);
}
#endregion // Public Methods
+
+ internal ArrayList List {
+ get {
+ if (list == null) {
+ list = new ArrayList ();
+ foreach (ListViewItem item in
owner.Items) {
+ if (item.Checked)
+ list.Add (item);
+ }
+ }
+ return list;
+ }
+ }
+
+ internal void Reset ()
+ {
+ // force re-population of list
+ list = null;
+ }
+
+ private void ItemsCollection_Changed ()
+ {
+ Reset ();
+ }
} // CheckedListViewItemCollection
public class ColumnHeaderCollection : IList, ICollection,
IEnumerable
@@ -2606,7 +2620,7 @@
public class ListViewItemCollection : IList, ICollection,
IEnumerable
{
- internal ArrayList list;
+ private readonly ArrayList list;
private readonly ListView owner;
#region Public Constructor
@@ -2643,6 +2657,7 @@
value.Owner = owner;
list [displayIndex] = value;
+ OnChange ();
owner.Redraw (true);
}
@@ -2667,6 +2682,7 @@
this [index] = (ListViewItem)
value;
else
this [index] = new ListViewItem
(value.ToString ());
+ OnChange ();
}
}
#endregion // Public Properties
@@ -2681,6 +2697,7 @@
list.Add (value);
owner.Sort (false);
+ OnChange ();
owner.Redraw (true);
return value;
}
@@ -2700,8 +2717,6 @@
public void AddRange (ListViewItem [] values)
{
list.Clear ();
- owner.SelectedItems.list.Clear ();
- owner.CheckedItems.list.Clear ();
foreach (ListViewItem item in values) {
item.Owner = owner;
@@ -2709,6 +2724,7 @@
}
owner.Sort (false);
+ OnChange ();
owner.Redraw (true);
}
@@ -2717,8 +2733,7 @@
owner.SetFocusedItem (null);
owner.h_scroll.Value = owner.v_scroll.Value = 0;
list.Clear ();
- owner.SelectedItems.list.Clear ();
- owner.CheckedItems.list.Clear ();
+ OnChange ();
owner.Redraw (true);
}
@@ -2752,6 +2767,7 @@
li.Owner = owner;
result = list.Add (li);
+ OnChange ();
owner.Redraw (true);
return result;
@@ -2795,6 +2811,7 @@
item.Owner = owner;
list.Insert (index, item);
+ OnChange ();
owner.Redraw (true);
return item;
}
@@ -2814,22 +2831,34 @@
if (!list.Contains (item))
return;
- owner.SelectedItems.list.Remove (item);
- owner.CheckedItems.list.Remove (item);
list.Remove (item);
+ OnChange ();
owner.Redraw (true);
}
public virtual void RemoveAt (int index)
{
- ListViewItem item = this [index];
+ if (index < 0 || index >= Count)
+ throw new ArgumentOutOfRangeException
("index");
list.RemoveAt (index);
- owner.SelectedItems.list.Remove (item);
- owner.CheckedItems.list.Remove (item);
+ OnChange ();
owner.Redraw (false);
}
#endregion // Public Methods
+ internal event CollectionChangedHandler Changed;
+
+ internal void Sort (IComparer comparer)
+ {
+ list.Sort (comparer);
+ OnChange ();
+ }
+
+ internal void OnChange ()
+ {
+ if (Changed != null)
+ Changed ();
+ }
} // ListViewItemCollection
public class SelectedIndexCollection : IList, ICollection,
IEnumerable
@@ -2957,26 +2986,27 @@
private int [] GetIndices ()
{
- int [] indices = new int [Count];
- for (int i = 0; i < owner.SelectedItems.Count;
i++) {
- ListViewItem item = owner.SelectedItems
[i];
+ ArrayList selected_items =
owner.SelectedItems.List;
+ int [] indices = new int [selected_items.Count];
+ for (int i = 0; i < selected_items.Count; i++) {
+ ListViewItem item = (ListViewItem)
selected_items [i];
indices [i] = item.Index;
}
return indices;
}
-
} // SelectedIndexCollection
public class SelectedListViewItemCollection : IList,
ICollection, IEnumerable
{
- internal ArrayList list;
private readonly ListView owner;
+ private ArrayList list;
#region Public Constructor
public SelectedListViewItemCollection (ListView owner)
{
- list = new ArrayList ();
this.owner = owner;
+ this.owner.Items.Changed += new
CollectionChangedHandler (
+ ItemsCollection_Changed);
}
#endregion // Public Constructor
@@ -2986,7 +3016,7 @@
get {
if (!owner.IsHandleCreated)
return 0;
- return list.Count;
+ return List.Count;
}
}
@@ -2996,14 +3026,15 @@
public ListViewItem this [int index] {
get {
- if (index < 0 || index >= Count)
+ ArrayList selected_items = List;
+ if (!owner.IsHandleCreated || index < 0
|| index >= selected_items.Count)
throw new
ArgumentOutOfRangeException ("index");
- return (ListViewItem) list [index];
+ return (ListViewItem) selected_items
[index];
}
}
bool ICollection.IsSynchronized {
- get { return list.IsSynchronized; }
+ get { return false; }
}
object ICollection.SyncRoot {
@@ -3026,32 +3057,29 @@
if (!owner.IsHandleCreated)
return;
- ArrayList copy = (ArrayList) list.Clone ();
- for (int i = 0; i < copy.Count; i++)
- ((ListViewItem) copy [i]).Selected =
false;
-
- list.Clear ();
+ foreach (ListViewItem item in List)
+ item.Selected = false;
}
public bool Contains (ListViewItem item)
{
if (!owner.IsHandleCreated)
return false;
- return list.Contains (item);
+ return List.Contains (item);
}
public void CopyTo (Array dest, int index)
{
if (!owner.IsHandleCreated)
return;
- list.CopyTo (dest, index);
+ List.CopyTo (dest, index);
}
public IEnumerator GetEnumerator ()
{
if (!owner.IsHandleCreated)
return (new ListViewItem
[0]).GetEnumerator ();
- return list.GetEnumerator ();
+ return List.GetEnumerator ();
}
int IList.Add (object value)
@@ -3092,12 +3120,37 @@
{
if (!owner.IsHandleCreated)
return -1;
- return list.IndexOf (item);
+ return List.IndexOf (item);
}
#endregion // Public Methods
+ internal ArrayList List {
+ get {
+ if (list == null) {
+ list = new ArrayList ();
+ foreach (ListViewItem item in
owner.Items) {
+ if (item.Selected)
+ list.Add (item);
+ }
+ }
+ return list;
+ }
+ }
+
+ internal void Reset ()
+ {
+ // force re-population of list
+ list = null;
+ }
+
+ private void ItemsCollection_Changed ()
+ {
+ Reset ();
+ }
} // SelectedListViewItemCollection
+ internal delegate void CollectionChangedHandler ();
+
#endregion // Subclasses
}
}
Modified:
trunk/mcs/class/Managed.Windows.Forms/System.Windows.Forms/ListViewItem.cs
===================================================================
--- trunk/mcs/class/Managed.Windows.Forms/System.Windows.Forms/ListViewItem.cs
2006-10-06 18:13:37 UTC (rev 66370)
+++ trunk/mcs/class/Managed.Windows.Forms/System.Windows.Forms/ListViewItem.cs
2006-10-06 18:19:29 UTC (rev 66371)
@@ -147,15 +147,8 @@
is_checked = value;
if (owner != null) {
- if (is_checked) {
- if (owner.CheckedItems.Contains
(this) == false) {
-
owner.CheckedItems.list.Add (this);
- }
- }
- else {
- owner.CheckedItems.list.Remove
(this);
- }
-
+ // force re-population of list
+ owner.CheckedItems.Reset ();
Layout ();
}
Invalidate ();
@@ -270,11 +263,15 @@
if (selected == value)
return;
- selected = value;
-
if (owner != null) {
- owner.UpdateSelection (this);
+ if (value && !owner.MultiSelect)
+ owner.SelectedItems.Clear ();
+ selected = value;
+ // force re-population of list
+ owner.SelectedItems.Reset ();
Layout ();
+ } else {
+ selected = value;
}
Invalidate ();
}
Modified:
trunk/mcs/class/Managed.Windows.Forms/Test/System.Windows.Forms/ChangeLog
===================================================================
--- trunk/mcs/class/Managed.Windows.Forms/Test/System.Windows.Forms/ChangeLog
2006-10-06 18:13:37 UTC (rev 66370)
+++ trunk/mcs/class/Managed.Windows.Forms/Test/System.Windows.Forms/ChangeLog
2006-10-06 18:19:29 UTC (rev 66371)
@@ -1,3 +1,8 @@
+2006-10-06 Gert Driesen <[EMAIL PROTECTED]>
+
+ * ListViewCollectionsTest.cs: Added tests for bug #79415.
+ * ListViewTest.cs: Enabled test for bug #79416.
+
2006-10-03 Sebastien Pouliot <[EMAIL PROTECTED]>
* ContainerControlTest.cs: New. Add a few test cases for
Modified:
trunk/mcs/class/Managed.Windows.Forms/Test/System.Windows.Forms/ListViewCollectionsTest.cs
===================================================================
---
trunk/mcs/class/Managed.Windows.Forms/Test/System.Windows.Forms/ListViewCollectionsTest.cs
2006-10-06 18:13:37 UTC (rev 66370)
+++
trunk/mcs/class/Managed.Windows.Forms/Test/System.Windows.Forms/ListViewCollectionsTest.cs
2006-10-06 18:19:29 UTC (rev 66371)
@@ -149,6 +149,54 @@
((IList)listview.CheckedItems).Add (5);
}
+ [Test]
+ public void CheckedItemCollectionTest_Order ()
+ {
+ Form form = new Form ();
+ ListView lvw = new ListView ();
+ lvw.CheckBoxes = true;
+ form.Controls.Add (lvw);
+ ListViewItem itemA = lvw.Items.Add ("A");
+ itemA.Checked = true;
+ ListViewItem itemB = lvw.Items.Add ("B");
+ itemB.Checked = true;
+ ListViewItem itemC = lvw.Items.Add ("C");
+ itemC.Checked = true;
+
+ Assert.AreEqual (3, lvw.CheckedItems.Count, "#A1");
+ Assert.AreSame (itemA, lvw.CheckedItems [0], "#A2");
+ Assert.AreSame (itemB, lvw.CheckedItems [1], "#A3");
+ Assert.AreSame (itemC, lvw.CheckedItems [2], "#A3");
+
+ itemB.Checked = false;
+
+ Assert.AreEqual (2, lvw.CheckedItems.Count, "#B1");
+ Assert.AreSame (itemA, lvw.CheckedItems [0], "#B2");
+ Assert.AreSame (itemC, lvw.CheckedItems [1], "#B3");
+
+ itemB.Checked = true;
+
+ Assert.AreEqual (3, lvw.CheckedItems.Count, "#C1");
+ Assert.AreSame (itemA, lvw.CheckedItems [0], "#C2");
+ Assert.AreSame (itemB, lvw.CheckedItems [1], "#C3");
+ Assert.AreSame (itemC, lvw.CheckedItems [2], "#C4");
+
+ lvw.Sorting = SortOrder.Descending;
+
+ Assert.AreEqual (3, lvw.CheckedItems.Count, "#D1");
+ Assert.AreSame (itemA, lvw.CheckedItems [0], "#D2");
+ Assert.AreSame (itemB, lvw.CheckedItems [1], "#D3");
+ Assert.AreSame (itemC, lvw.CheckedItems [2], "#D4");
+
+ // sorting only takes effect when listview is created
+ form.Show ();
+
+ Assert.AreEqual (3, lvw.CheckedItems.Count, "#E1");
+ Assert.AreSame (itemC, lvw.CheckedItems [0], "#E2");
+ Assert.AreSame (itemB, lvw.CheckedItems [1], "#E3");
+ Assert.AreSame (itemA, lvw.CheckedItems [2], "#E4");
+ }
+
[Test, ExpectedException (typeof (NotSupportedException))]
public void
CheckedItemCollectionTest_PropertiesTest_Remove_ExceptionTest ()
{
@@ -362,13 +410,13 @@
try {
ListViewItem x = lvw.SelectedItems [0];
- Assert.Fail ("#A1" + x.ToString ());
+ Assert.Fail ("#A1: " + x.ToString ());
} catch (ArgumentOutOfRangeException) {
}
try {
ListViewItem x = list [0] as ListViewItem;
- Assert.Fail ("#A2" + x.ToString ());
+ Assert.Fail ("#A2: " + x.ToString ());
} catch (ArgumentOutOfRangeException) {
}
@@ -401,6 +449,48 @@
Assert.AreEqual (-1, list.IndexOf (new ListViewItem
()), "#B4");
}
+ [Test]
+ public void SelectedItemCollectionTest_Order ()
+ {
+ Form form = new Form ();
+ ListView lvw = new ListView ();
+ lvw.MultiSelect = true;
+ form.Controls.Add (lvw);
+ ListViewItem itemA = lvw.Items.Add ("A");
+ itemA.Selected = true;
+ ListViewItem itemB = lvw.Items.Add ("B");
+ itemB.Selected = true;
+ ListViewItem itemC = lvw.Items.Add ("C");
+ itemC.Selected = true;
+
+ form.Show ();
+
+ Assert.AreEqual (3, lvw.SelectedItems.Count, "#A1");
+ Assert.AreSame (itemA, lvw.SelectedItems [0], "#A2");
+ Assert.AreSame (itemB, lvw.SelectedItems [1], "#A3");
+ Assert.AreSame (itemC, lvw.SelectedItems [2], "#A3");
+
+ itemB.Selected = false;
+
+ Assert.AreEqual (2, lvw.SelectedItems.Count, "#B1");
+ Assert.AreSame (itemA, lvw.SelectedItems [0], "#B2");
+ Assert.AreSame (itemC, lvw.SelectedItems [1], "#B3");
+
+ itemB.Selected = true;
+
+ Assert.AreEqual (3, lvw.SelectedItems.Count, "#C1");
+ Assert.AreSame (itemA, lvw.SelectedItems [0], "#C2");
+ Assert.AreSame (itemB, lvw.SelectedItems [1], "#C3");
+ Assert.AreSame (itemC, lvw.SelectedItems [2], "#C4");
+
+ lvw.Sorting = SortOrder.Descending;
+
+ Assert.AreEqual (3, lvw.SelectedItems.Count, "#D1");
+ Assert.AreSame (itemC, lvw.SelectedItems [0], "#D2");
+ Assert.AreSame (itemB, lvw.SelectedItems [1], "#D3");
+ Assert.AreSame (itemA, lvw.SelectedItems [2], "#D4");
+ }
+
/*
ListViewItemCollection
*/
Modified:
trunk/mcs/class/Managed.Windows.Forms/Test/System.Windows.Forms/ListViewTest.cs
===================================================================
---
trunk/mcs/class/Managed.Windows.Forms/Test/System.Windows.Forms/ListViewTest.cs
2006-10-06 18:13:37 UTC (rev 66370)
+++
trunk/mcs/class/Managed.Windows.Forms/Test/System.Windows.Forms/ListViewTest.cs
2006-10-06 18:19:29 UTC (rev 66371)
@@ -191,8 +191,7 @@
entryList.Items.Add (item);
}
- [Test]
- [Category ("NotWorking")] // once listview is created, multiple
items cannot be selected through code
+ [Test] // bug #79416
public void MultiSelect ()
{
Form form = new Form ();
_______________________________________________
Mono-patches maillist - [email protected]
http://lists.ximian.com/mailman/listinfo/mono-patches