/// <summary> /// Initialize the control without an existing reading text. Creates /// an initial reading text with two spaces in between each field. /// </summary> /// <param name="fieldReplacements">The replacements to show instead of the replacement fields. The replacement fields will show as locked portions in the text.</param> /// <param name="predicateColor">The color for the normal editable text</param> /// <param name="replacementColor">The color for the replacement fields</param> public void Initialize(string[] fieldReplacements, Color predicateColor, Color replacementColor) { StartInitialize(); int fieldCount = fieldReplacements.Length; LockedField[] fields = new LockedField[fieldCount]; StringBuilder sb = new StringBuilder(); int offsetAdjustment = 0; for (int i = 0; i < fieldCount; ++i) { string replacement = fieldReplacements[i]; int replacementLength = replacement.Length; if (i != 0) { sb.Append(" "); offsetAdjustment += 2; } fields[i] = new LockedField(offsetAdjustment, replacementLength, string.Concat("{", i.ToString(CultureInfo.InvariantCulture), "}")); offsetAdjustment += replacementLength; sb.Append(replacement); } if (fieldCount == 1) { sb.Append(" "); } FinishInitialize(sb.ToString(), fields, predicateColor, replacementColor); }
/// <summary> /// Helper for different Initialize overloads /// </summary> /// <param name="displayText">The initial text to display</param> /// <param name="fields">An array LockedField elements. Can be null.</param> /// <param name="predicateColor">The color for the normal editable text</param> /// <param name="replacementColor">The color to use for the locked fields</param> private void FinishInitialize(string displayText, LockedField[] fields, Color predicateColor, Color replacementColor) { ForeColor = predicateColor; Text = displayText; if (fields != null) { int fieldCount = fields.Length; for (int i = 0; i < fieldCount; ++i) { LockedField field = fields[i]; Select(field.Index, field.Length); SelectionColor = replacementColor; SelectionProtected = true; } myLastText = displayText; myLastRtf = Rtf; bool customSelection = false; if (fieldCount == 1) { // If there is no front text, then select text after spaces trailing the field LockedField field = fields[0]; if (field.Index == 0) { customSelection = true; int fieldLength = field.Length; int fullLength = displayText.Length; int trailingLength = fullLength - fieldLength; if (trailingLength == 0) { Select(fieldLength, 0); } else { int trailingStart = fieldLength; while (trailingStart < fullLength) { if (displayText[trailingStart] != ' ') { break; } ++trailingStart; } if (trailingStart == fullLength) { Select(fieldLength + 1, trailingLength - 1); } else { Select(trailingStart, fullLength - trailingStart); } } } } else if (fieldCount >= 2) { // If there is no front text, then select text (without leading/trailing spaces) between the first two fields LockedField leftField = fields[0]; LockedField rightField = fields[1]; if (leftField.Index == 0) { int middleStart = leftField.Length; int middleLength = rightField.Index - middleStart; customSelection = true; if (middleLength == 0) { Select(middleStart, 0); } else { int middleEnd = middleStart + middleLength - 1; while (middleStart <= middleEnd) { if (displayText[middleStart] != ' ') { break; } ++middleStart; } while (middleEnd > middleStart) { if (displayText[middleEnd] != ' ') { break; } --middleEnd; } if (middleEnd < middleStart) { // Nothing but spaces switch (middleLength) { case 1: Select(middleEnd + 1, 0); break; case 2: Select(middleEnd, 0); break; default: Select(middleEnd - middleLength + 2, middleLength - 2); break; } } else { // Select stuff between the spaces Select(middleStart, middleEnd - middleStart + 1); } } } } if (!customSelection) { Select(0, 0); } } else // fields == null { // Freeform edit, nothing to track myLastRtf = null; myLastText = null; SelectAll(); } ClearUndo(); myLockedFields = fields; InitializeCompleted(); }
/// <summary> /// Initialize the control /// </summary> /// <param name="readingText">The initial reading text. A format string with simple replacement fields.</param> /// <param name="fieldReplacements">The replacements to show instead of the replacement fields. The replacement fields will show as locked portions in the text.</param> /// <param name="predicateColor">The color for the normal editable text</param> /// <param name="replacementColor">The color for the replacement fields</param> public void Initialize(string readingText, string[] fieldReplacements, Color predicateColor, Color replacementColor) { StartInitialize(); // First, lets see how many valid fields we have. A valid field // must be in range and must occur only once. If these conditions // do not hold, then those format replacements fields are invalid and // needs to be directly edited. int replacementCount = fieldReplacements.Length; int[] foundReplacements = new int[replacementCount]; int fieldCount = 0; bool allFieldsVisited; allFieldsVisited = Reading.VisitFields( readingText, delegate(int index) { if (index < replacementCount) { int currentCount = foundReplacements[index]; switch (currentCount) { case 0: ++fieldCount; break; case 1: --fieldCount; break; } foundReplacements[index] = currentCount + 1; //Keep going return true; } else { return false; } }); if (fieldCount != 0 && allFieldsVisited) { LockedField[] fields = new LockedField[fieldCount]; int offsetAdjustment = 0; int currentField = 0; string modifiedString = Reading.ReplaceFields( readingText, delegate(int index, Match match) { Group fieldGroup = match.Groups[Reading.ReplaceFieldsMatchFieldGroupName]; if (index < replacementCount && foundReplacements[index] == 1) { string replacement = fieldReplacements[index]; int replacementLength = replacement.Length; fields[currentField] = new LockedField(fieldGroup.Index + offsetAdjustment, replacementLength, fieldGroup.Value); offsetAdjustment += replacementLength - fieldGroup.Length; ++currentField; return replacement; } return null; }); FinishInitialize(modifiedString, fields, predicateColor, replacementColor); } else { FinishInitialize(readingText, null, predicateColor, replacementColor); } }
private void WmReflectNotify(ref Message m) { if (m.HWnd != Handle) { base.WndProc(ref m); } else { NativeMethods.NMHDR hdr = (NativeMethods.NMHDR)m.GetLParam(typeof(NativeMethods.NMHDR)); switch (hdr.code) { case NativeMethods.EN_PROTECTED: // WinForms is blocking edits at the beginning of the text // if the first character is locked, and between two locked // sections if they are adjacent to each other. If we have // a zero-length character range that falls at the beginning // of a locked field then stop the EN_PROTECTED notification // from reaching the base. Note that the OnProtected override // is especially lame: you get the notification after the // response to the notification is determined and after the control // has beeped! NativeMethods.ENPROTECTED protectedInfo = NativeMethods.ENPROTECTED.Create(ref m); NativeMethods.CHARRANGE range = protectedInfo.chrg; int characterIndex = range.cpMin; if (characterIndex == range.cpMax) { LockedField[] fields = myLockedFields; if (fields != null) { for (int i = 0; i < fields.Length; ++i) { int fieldIndex = fields[i].Index; int diff = fieldIndex - characterIndex; if (diff >= 0) { if (diff == 0) { if (protectedInfo.msg == NativeMethods.WM_KEYDOWN) { // If we a keydown, then we can't let through // a delete and we can't let through a backspace // if we're touching the previous field bool keepProtected = false; switch ((Keys)protectedInfo.wParam) { case Keys.Delete: keepProtected = true; break; case Keys.Back: if (i > 0) { LockedField previousField = fields[i - 1]; // If we're touching the previous field, then don't allow // a backspace. keepProtected = (previousField.Index + previousField.Length) == fieldIndex; } break; } if (keepProtected) { break; } } myAllowedProtectedEdit = true; m.Result = (IntPtr)0; return; } break; } } } } break; } base.WndProc(ref m); } }
/// <summary> /// Initialize the control /// </summary> /// <param name="readingText">The initial reading text. A format string with simple replacement fields.</param> /// <param name="fieldReplacements">The replacements to show instead of the replacement fields. The replacement fields will show as locked portions in the text.</param> /// <param name="predicateColor">The color for the normal editable text</param> /// <param name="replacementColor">The color for the replacement fields</param> public void Initialize(string readingText, string[] fieldReplacements, Color predicateColor, Color replacementColor) { StartInitialize(); // First, lets see how many valid fields we have. A valid field // must be in range and must occur only once. If these conditions // do not hold, then those format replacements fields are invalid and // needs to be directly edited. int replacementCount = fieldReplacements.Length; int[] foundReplacements = new int[replacementCount]; int fieldCount = 0; bool allFieldsVisited; allFieldsVisited = Reading.VisitFields( readingText, delegate(int index) { if (index < replacementCount) { int currentCount = foundReplacements[index]; switch (currentCount) { case 0: ++fieldCount; break; case 1: --fieldCount; break; } foundReplacements[index] = currentCount + 1; //Keep going return(true); } else { return(false); } }); if (fieldCount != 0 && allFieldsVisited) { LockedField[] fields = new LockedField[fieldCount]; int offsetAdjustment = 0; int currentField = 0; string modifiedString = Reading.ReplaceFields( readingText, delegate(int index, Match match) { Group fieldGroup = match.Groups[Reading.ReplaceFieldsMatchFieldGroupName]; if (index < replacementCount && foundReplacements[index] == 1) { string replacement = fieldReplacements[index]; int replacementLength = replacement.Length; fields[currentField] = new LockedField(fieldGroup.Index + offsetAdjustment, replacementLength, fieldGroup.Value); offsetAdjustment += replacementLength - fieldGroup.Length; ++currentField; return(replacement); } return(null); }); FinishInitialize(modifiedString, fields, predicateColor, replacementColor); } else { FinishInitialize(readingText, null, predicateColor, replacementColor); } }