Skip to content

Commit 338e882

Browse files
authored
Fix LT-22187: Allow reordering fields (#435)
* Fix LT-22187: Allow reordering fields * Make Category Info under Grammatical Info Details immovable * Remove unused line of code * Make Lexeme Form not be moveable * Remove System.Buffers.dll
1 parent dd6900d commit 338e882

1 file changed

Lines changed: 183 additions & 35 deletions

File tree

  • Src/Common/Controls/DetailControls

Src/Common/Controls/DetailControls/Slice.cs

Lines changed: 183 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,16 @@
1111
using System.Text;
1212
using System.Windows.Forms;
1313
using System.Xml;
14-
using SIL.LCModel.Core.Cellar;
1514
using SIL.FieldWorks.Common.Controls;
1615
using SIL.FieldWorks.Common.Framework.DetailControls.Resources;
17-
using SIL.LCModel.Core.KernelInterfaces;
1816
using SIL.FieldWorks.Common.FwUtils;
1917
using SIL.FieldWorks.Common.RootSites;
20-
using SIL.LCModel;
21-
using SIL.LCModel.Infrastructure;
2218
using SIL.FieldWorks.FdoUi;
2319
using SIL.FieldWorks.LexText.Controls;
20+
using SIL.LCModel;
21+
using SIL.LCModel.Core.Cellar;
22+
using SIL.LCModel.Core.KernelInterfaces;
23+
using SIL.LCModel.Infrastructure;
2424
using SIL.LCModel.Utils;
2525
using SIL.PlatformUtilities;
2626
using SIL.Utils;
@@ -2800,42 +2800,90 @@ internal protected virtual bool UpdateDisplayIfNeeded(int hvo, int tag)
28002800
private void MoveField(Direction dir)
28012801
{
28022802
CheckDisposed();
2803-
if (ContainingDataTree.ShowingAllFields)
2803+
XmlNode swapWith;
2804+
XmlNode fieldRef = MoveableFieldReferenceForSlice();
2805+
2806+
if (fieldRef == null)
28042807
{
2805-
XmlNode swapWith;
2806-
XmlNode fieldRef = FieldReferenceForSlice();
2808+
Debug.Fail("Could not identify field to move on slice.");
2809+
return;
2810+
}
28072811

2808-
if (fieldRef == null)
2809-
{
2810-
Debug.Fail("Could not identify field to move on slice.");
2811-
return;
2812-
}
2812+
if (dir == Direction.Up)
2813+
{
2814+
swapWith = PrevSliceSiblingPart(fieldRef);
2815+
}
2816+
else
2817+
{
2818+
swapWith = NextSliceSiblingPart(fieldRef);
2819+
}
28132820

2821+
var parent = fieldRef.ParentNode;
2822+
// Reorder in the parent node in the xml
2823+
if (parent != null)
2824+
{
2825+
parent.RemoveChild(fieldRef);
28142826
if (dir == Direction.Up)
2815-
{
2816-
swapWith = PrevPartSibling(fieldRef);
2817-
}
2827+
parent.InsertBefore(fieldRef, swapWith);
28182828
else
2829+
parent.InsertAfter(fieldRef, swapWith);
2830+
}
2831+
2832+
// Persist in the parent part (might not be the immediate parent node)
2833+
Inventory.GetInventory("layouts", m_cache.ProjectId.Name)
2834+
.PersistOverrideElement(PartParent(fieldRef));
2835+
ContainingDataTree.RefreshList(true);
2836+
}
2837+
2838+
/// <summary>
2839+
/// Get the sibling for the current slice in the given dir.
2840+
/// </summary>
2841+
internal Slice GetSibling(Direction dir)
2842+
{
2843+
int islice = IndexInContainer;
2844+
int cslice = ContainingDataTree.Slices.Count;
2845+
int increment = (dir == Direction.Down ? 1 : -1);
2846+
int depth = GetMoveableDepth();
2847+
XmlNode fieldRef = MoveableFieldReferenceForSlice();
2848+
while (true)
2849+
{
2850+
islice += increment;
2851+
if (dir == Direction.Down)
28192852
{
2820-
swapWith = NextPartSibling(fieldRef);
2853+
if (islice >= cslice) break;
28212854
}
2822-
2823-
var parent = fieldRef.ParentNode;
2824-
// Reorder in the parent node in the xml
2825-
if (parent != null)
2855+
else
28262856
{
2827-
parent.RemoveChild(fieldRef);
2828-
if (dir == Direction.Up)
2829-
parent.InsertBefore(fieldRef, swapWith);
2830-
else
2831-
parent.InsertAfter(fieldRef, swapWith);
2857+
if (islice < 0) break;
28322858
}
2859+
var slice = ContainingDataTree.Slices[islice];
2860+
int sliceDepth = slice.GetMoveableDepth();
2861+
// Skip over our children.
2862+
if (sliceDepth > depth)
2863+
continue;
2864+
// Stop if we get past the children of our parent.
2865+
if (sliceDepth < depth)
2866+
break;
2867+
XmlNode sliceFieldRef = slice.MoveableFieldReferenceForSlice();
2868+
if (sliceFieldRef == fieldRef)
2869+
// Skip slices with the same fieldRef as self.
2870+
// This happens with nested headers.
2871+
continue;
2872+
return slice;
2873+
}
2874+
return null;
2875+
}
28332876

2834-
// Persist in the parent part (might not be the immediate parent node)
2835-
Inventory.GetInventory("layouts", m_cache.ProjectId.Name)
2836-
.PersistOverrideElement(PartParent(fieldRef));
2837-
ContainingDataTree.RefreshList(true);
2877+
private int GetMoveableDepth()
2878+
{
2879+
int count = 0;
2880+
foreach (object obj in Key)
2881+
{
2882+
var node = obj as XmlNode;
2883+
if (IsMoveableNode(node))
2884+
count++;
28382885
}
2886+
return count;
28392887
}
28402888

28412889
/// <summary>
@@ -2854,13 +2902,79 @@ private XmlNode FieldReferenceForSlice()
28542902
{
28552903
continue;
28562904
}
2857-
28582905
fieldRef = node;
28592906
}
28602907

28612908
return fieldRef;
28622909
}
28632910

2911+
/// <summary>
2912+
/// Get the last moveable field reference for slice.
2913+
/// </summary>
2914+
private XmlNode MoveableFieldReferenceForSlice()
2915+
{
2916+
XmlNode fieldRef = null;
2917+
foreach (object obj in Key)
2918+
{
2919+
var node = obj as XmlNode;
2920+
if (IsMoveableNode(node))
2921+
{
2922+
fieldRef = node;
2923+
}
2924+
}
2925+
2926+
return fieldRef;
2927+
}
2928+
2929+
/// <summary>
2930+
/// Can this node be moved?
2931+
/// </summary>
2932+
private bool IsMoveableNode(XmlNode node)
2933+
{
2934+
if (!IsRefPartNode(node))
2935+
return false;
2936+
if (node.PreviousSibling != null)
2937+
// node has siblings, so it can be moved.
2938+
return true;
2939+
// This is the first node in a (possibly singleton) sequence.
2940+
// Sometimes the first node represents the sequence as a whole (e.g. "Variant Type").
2941+
// In this case, it is not moveable at this level, but where it is invoked (e.g. ref="EntryRefs").
2942+
// Other times, the sequence as a whole is represented by a header (e.g. "Category Info." is represented by "Grammatical Info. Details").
2943+
// In this case, the first node is moveable.
2944+
// We look at the reference part nodes above node to determine whether the node is moveable.
2945+
bool found = false;
2946+
for (int i = Key.Length - 1; i >= 0; i--)
2947+
{
2948+
XmlNode keyNode = Key[i] as XmlNode;
2949+
if (!IsRefPartNode(keyNode)) continue;
2950+
if (keyNode == node)
2951+
{
2952+
found = true;
2953+
continue;
2954+
}
2955+
if (!found) continue;
2956+
// keyNode is a ref part node above node.
2957+
string keyNodeLabel = XmlUtils.GetOptionalAttributeValue(keyNode, "label", null);
2958+
if (keyNodeLabel != null)
2959+
// keyNode represents the sequence as a whole.
2960+
// So node represents itself and can be moved at this level.
2961+
// Example: node label="Category Info.", keyNodeLabel="Grammatical Info. Details".
2962+
return true;
2963+
if (keyNode.PreviousSibling != null || keyNode.NextSibling != null)
2964+
// node represents the sequence as a whole.
2965+
// So it does not represent itself and cannot be moved at this level.
2966+
// Example: node label="Variant Type", keyNode ref="EntryRefs".
2967+
return false;
2968+
}
2969+
return true;
2970+
}
2971+
2972+
private bool IsRefPartNode(XmlNode node)
2973+
{
2974+
return node != null && node.Name == "part"
2975+
&& XmlUtils.GetOptionalAttributeValue(node, "ref", null) != null;
2976+
}
2977+
28642978
protected void SetFieldVisibility(string visibility)
28652979
{
28662980
CheckDisposed();
@@ -2976,13 +3090,47 @@ protected bool IsVisibilityItemChecked(string visibility)
29763090

29773091
private bool CheckValidMove(UIItemDisplayProperties display, Direction dir)
29783092
{
2979-
XmlNode lastPartRef = FieldReferenceForSlice();
3093+
XmlNode lastPartRef = MoveableFieldReferenceForSlice();
29803094

29813095
if (lastPartRef == null)
29823096
return false;
29833097
return dir == Direction.Up
2984-
? PrevPartSibling(lastPartRef) != null
2985-
: NextPartSibling(lastPartRef) != null;
3098+
? PrevSliceSiblingPart(lastPartRef) != null
3099+
: NextSliceSiblingPart(lastPartRef) != null;
3100+
}
3101+
3102+
private XmlNode PrevSliceSiblingPart(XmlNode partRef)
3103+
{
3104+
Slice targetSlice = GetSibling(Direction.Up);
3105+
XmlNode targetNode = targetSlice?.MoveableFieldReferenceForSlice();
3106+
if (targetNode == null)
3107+
return null;
3108+
3109+
XmlNode prev = PrevPartSibling(partRef);
3110+
while (prev != null)
3111+
{
3112+
if (prev == targetNode)
3113+
return prev;
3114+
prev = PrevPartSibling(prev);
3115+
}
3116+
return null;
3117+
}
3118+
3119+
private XmlNode NextSliceSiblingPart(XmlNode partRef)
3120+
{
3121+
Slice targetSlice = GetSibling(Direction.Down);
3122+
XmlNode targetNode = targetSlice?.MoveableFieldReferenceForSlice();
3123+
if (targetNode == null)
3124+
return null;
3125+
3126+
XmlNode next = NextPartSibling(partRef);
3127+
while (next != null)
3128+
{
3129+
if (next == targetNode)
3130+
return next;
3131+
next = NextPartSibling(next);
3132+
}
3133+
return null;
29863134
}
29873135

29883136
private XmlNode PrevPartSibling(XmlNode partRef)
@@ -3024,7 +3172,7 @@ private XmlNode PartParent(XmlNode partRef)
30243172
public bool OnDisplayMoveFieldUp(object args, ref UIItemDisplayProperties display)
30253173
{
30263174
CheckDisposed();
3027-
display.Enabled = ContainingDataTree.ShowingAllFields && CheckValidMove(display, Direction.Up);
3175+
display.Enabled = CheckValidMove(display, Direction.Up);
30283176

30293177
return true;
30303178
}
@@ -3033,7 +3181,7 @@ public bool OnDisplayMoveFieldUp(object args, ref UIItemDisplayProperties displa
30333181
public bool OnDisplayMoveFieldDown(object args, ref UIItemDisplayProperties display)
30343182
{
30353183
CheckDisposed();
3036-
display.Enabled = ContainingDataTree.ShowingAllFields && CheckValidMove(display, Direction.Down);
3184+
display.Enabled = CheckValidMove(display, Direction.Down);
30373185
return true;
30383186
}
30393187

0 commit comments

Comments
 (0)