/// <summary> /// Return true if Delete() will delete something. Default is that it will not. /// </summary> public override bool CanDelete() { var sel = new InsertionPoint(Hookup, StringPosition, AssociatePrevious); sel = sel.MoveByKey(new KeyEventArgs(Keys.Right)) as InsertionPoint; return(Hookup.CanDelete(this, sel)); }
public void MakeSimpleRange() { ParaBox para; RootBox root = ParaBuilderTests.MakeTestParaSimpleString(m_gm.VwGraphics, ParaBuilderTests.MockBreakOption.ThreeFullLines, out para); InsertionPoint ip = root.SelectAtEnd(); InsertionPoint ip2 = new InsertionPoint(ip.Hookup, ip.StringPosition - 2, false); RangeSelection range = new RangeSelection(ip, ip2); Assert.AreEqual(ip, range.Anchor); Assert.AreEqual(ip2, range.DragEnd); Assert.That(range.EndBeforeAnchor, Is.True); Assert.That(range.Start, Is.EqualTo(ip2)); Assert.That(range.End, Is.EqualTo(ip)); StringBox first = para.FirstBox as StringBox; StringBox second = para.FirstBox.Next as StringBox; StringBox third = second.Next as StringBox; MockSegment seg3 = third.Segment as MockSegment; seg3.DrawRangeLeft = 17; seg3.DrawRangeRight = 23; PaintTransform ptrans = new PaintTransform(2, 4, 96, 100, 0, 10, 120, 128); range.Draw(m_gm.VwGraphics, ptrans); // All three segments should be invited to draw it, though only one will. // The top of rsSrc gets more negative each line; the destination rectangle where we actually draw keeps getting lower. // Remember the effect of 10 pixels of scroll offset. VerifyRangeSegmentDrawing(para, first, first.Segment as MockSegment, range, -4, -6, 4); VerifyRangeSegmentDrawing(para, second, second.Segment as MockSegment, range, -14, 4, 14); VerifyRangeSegmentDrawing(para, third, seg3, range , -24, 14, 24); }
public InsertionPoint NextIp(int distance) { InsertionPoint newIp; IClientRun run = ContainingRun; ParaBox box = run.Hookup.ParaBox; if (StringPosition + distance > run.Text.Length) { distance -= run.Text.Length - StringPosition; if (box.Source.ClientRuns[run.Hookup.ClientRunIndex] != box.Source.ClientRuns.Last()) { run = box.Source.ClientRuns[run.Hookup.ClientRunIndex + 1]; } else { box = box.NextParaBox; if (box == null) { return(null); } run = box.Source.ClientRuns[0]; } newIp = run.SelectAtStart(box); newIp = newIp.NextIp(distance); } else { newIp = new InsertionPoint(Hookup, StringPosition + distance, AssociatePrevious); } return(newIp); }
public RangeSelection(InsertionPoint anchor, InsertionPoint drag) { Anchor = anchor; DragEnd = drag; var anchorPara = Anchor.Para; var dragPara = DragEnd.Para; Debug.Assert(Anchor != null && DragEnd != null && !Anchor.SameLocation(DragEnd)); bool endFirst; if (anchorPara == dragPara) { endFirst = DragEnd.LogicalParaPosition < Anchor.LogicalParaPosition; } else { Box anchorChild, dragChild; var commonContainer = anchorPara.CommonContainer(dragPara, out anchorChild, out dragChild); if (commonContainer == anchorPara) { throw new NotImplementedException( "selections extending from a paragraph to a descendant paragraph not implemented."); } if (commonContainer == dragPara) { throw new NotImplementedException( "selections extending from a paragraph to a descendant paragraph not implemented."); } // otherwise anchorChild and dragChild are different children of CommonContainer, just need their order endFirst = anchorChild.Follows(dragChild); } // Make sure the ends associate inwards. DragEnd = DragEnd.Associate(!endFirst); Anchor = Anchor.Associate(endFirst); }
/// <summary> /// Delete the selected material, or whatever else is appropriate when the Delete key is pressed. /// (Insertion Point deletes the following character.) /// </summary> public override void Delete() { Invalidate(); // while we still know the old position. if (Hookup == null) { return; } if (StringPosition == Hookup.Text.Length) { if (Hookup.ClientRunIndex == Para.Source.ClientRuns.Count - 1) { DeleteLineBreak(); return; } if (Para.Source.ClientRuns[Hookup.ClientRunIndex + 1] is TextClientRun) { // Delete at end of previous run. var nextClientRun = Para.Source.NonEmptyStringClientRunBeginningAfter(Hookup.ClientRunIndex); if (nextClientRun == null) { return; } // Enhance JohnT: maybe some kind of hookup can merge with previous para or delete an embedded object? CopyFrom(nextClientRun.SelectAtStart(Para)); //Debug.Assert(StringPosition != Hookup.Text.Length - 1, "should have selected at the START of a non-empty run"); } } var insertionPointEnd = new InsertionPoint(Hookup, StringPosition + 1, AssociatePrevious); if (!Hookup.CanDelete(this, insertionPointEnd)) { return; } Hookup.Delete(this, insertionPointEnd); }
/// <summary> /// Return the index of the one of your child hookups that contains the given selection, /// or -1 if not found. /// </summary> internal int IndexOfChild(InsertionPoint ip) { var child = ChildContaining(ip); if (child == null) return -1; return Children.IndexOf(child); }
public override void ApplyStyle(string style) { var hookup = Start.Hookup; var start = new InsertionPoint(hookup, Start.StringPosition, Start.AssociatePrevious); IClientRun run = start.ContainingRun; var box = start.Para; int lastIndex = End.Para.Source.ClientRuns.IndexOf(End.ContainingRun); int numBoxes = 0; IStyle styleToBeApplied = box.Style.Stylesheet.Style(style); if (styleToBeApplied == null) { return; } var isParagraphStyle = styleToBeApplied.IsParagraphStyle; for (int i = start.Para.Source.ClientRuns.IndexOf(run) + 1; hookup != End.Hookup; i++) { if (isParagraphStyle) { numBoxes++; } else { hookup.ApplyStyle(start, hookup.SelectAtEnd(), style); } if (i >= box.Source.ClientRuns.Count) { box = box.NextParaBox; if (box == null) { return; } hookup = box.SelectAtStart().Hookup; i = 0; } else { hookup = box.Source.ClientRuns[i].SelectAtStart(box).Hookup; } start = hookup.SelectAtStart(); } if (isParagraphStyle) { numBoxes++; ApplyParagraphStyle(Start, numBoxes, style); } else { hookup.ApplyStyle(start, End, style); } }
public InsertionPointRestoreData(InsertionPoint ip) { StoredInsertionPoint = new InsertionPoint(ip.Hookup, ip.StringPosition, ip.AssociatePrevious); var parents = FindParents(StoredInsertionPoint.Para).Reverse(); var childrenList = StoredInsertionPoint.RootBox.Children.ToList(); foreach (GroupBox box in parents) { Indexes.Add(childrenList.IndexOf(box)); childrenList = box.Children.ToList(); } runIndex = StoredInsertionPoint.Para.Source.ClientRuns.IndexOf(StoredInsertionPoint.ContainingRun); }
public InsertionPointRestoreData(InsertionPoint ip) { StoredInsertionPoint = new InsertionPoint(ip.Hookup, ip.StringPosition, ip.AssociatePrevious); var parents = FindParents(StoredInsertionPoint.Para).Reverse(); var childrenList = StoredInsertionPoint.RootBox.Children.ToList(); foreach (GroupBox box in parents) { Indexes.Add(childrenList.IndexOf(box)); childrenList = box.Children.ToList(); } runIndex = StoredInsertionPoint.Para.Source.ClientRuns.IndexOf(StoredInsertionPoint.ContainingRun); }
private void ApplyParagraphStyle(InsertionPoint start, int numBoxes, string style) { ISelectionRestoreData restoreData = RestoreData(RootBox.Selection); IParagraphOperations paragraphOps; GroupHookup parentHookup; int index; if (!start.GetParagraphOps(out paragraphOps, out parentHookup, out index)) { return; } paragraphOps.ApplyParagraphStyle(index, numBoxes, style); restoreData.RestoreSelection(); }
public override bool Contains(InsertionPoint ip) { if (ip == null) { return(false); } if (ip.Para == Start.Para) { if (ip.LogicalParaPosition < Start.LogicalParaPosition) { return(false); } if (ip.LogicalParaPosition == Start.LogicalParaPosition) { return(!ip.AssociatePrevious || ip.Para.Source.Length == 0); } } if (ip.Para == End.Para) { if (ip.LogicalParaPosition > End.LogicalParaPosition) { return(false); } if (ip.LogicalParaPosition == End.LogicalParaPosition) { return(ip.AssociatePrevious || ip.Para.Source.Length == 0); } } // We now know it is neither before the start in the same paragraph, nor after the end in the // same paragraph. If it is in the same paragraph at all, it must be included. if (ip.Para == Start.Para || ip.Para == End.Para) { return(true); } // does it belong to some intermediate paragraph? for (Box box = Start.Para; box != End.Para; box = box.NextInSelectionSequence(true)) { if (box == ip.Para) { return(true); } } return(false); }
/// <summary> /// Implement the backspace key function (delete one character, or merge two paragraphs). /// </summary> public void Backspace() { Invalidate(); // while we still know the old position. if (Hookup == null) { return; } if (StringPosition == 0) { if (Hookup.ClientRunIndex == 0) { BackspaceDeleteLineBreak(); return; } if (Para.Source.ClientRuns[Hookup.ClientRunIndex - 1] is TextClientRun) { // Delete at end of previous run. var prevClientRun = Para.Source.NonEmptyStringClientRunEndingBefore(Hookup.ClientRunIndex); if (prevClientRun == null) { return; } // Enhance JohnT: maybe some kind of hookup can merge with previous para or delete an embedded object? CopyFrom(prevClientRun.SelectAtEnd(Para)); Debug.Assert(StringPosition != 0, "should have selected at the END of a non-empty run"); } } string oldValue = Hookup.Text; int newPos = Surrogates.PrevChar(oldValue, StringPosition); // Enhance JohnT: should we delete back to a base? var start = new InsertionPoint(Hookup, newPos, false); if (!Hookup.CanDelete(start, this)) { return; } Hookup.Delete(start, this); StringPosition = newPos; }
internal override void InsertText(InsertionPoint ip, string input) { var bldr = ((TssClientRun) ParaBox.Source.ClientRuns[ClientRunIndex]).Tss.GetBldr(); // Where there is a choice, we want the new text to have the properties of the neighbor // character that the IP is most closely associated with. ITsTextProps props; if (ip.StringPosition > 0 && ip.AssociatePrevious) props = bldr.get_PropertiesAt(ip.StringPosition - 1); else props = bldr.get_PropertiesAt(ip.StringPosition); // might be the lim, but that's OK. if (ip.StyleToBeApplied != null) { var propsBldr = props.GetBldr(); propsBldr.SetStrPropValue((int) FwTextPropType.ktptNamedStyle, ip.StyleToBeApplied.Name); props = propsBldr.GetTextProps(); } // Enhance JohnT: there may possibly be some special case, e.g., where the indicated character // is an ORC linked to certain kinds of data or a verse number, where we don't want to copy all // the properties. bldr.Replace(ip.StringPosition, ip.StringPosition, input, props); Writer(bldr.GetString()); }
/// <summary> /// Implement the backspace key function (delete one character, or merge two paragraphs). /// </summary> public void Backspace() { Invalidate(); // while we still know the old position. if (Hookup == null) return; if (StringPosition == 0) { if (Hookup.ClientRunIndex == 0) { BackspaceDeleteLineBreak(); return; } if (Para.Source.ClientRuns[Hookup.ClientRunIndex - 1] is TextClientRun) { // Delete at end of previous run. var prevClientRun = Para.Source.NonEmptyStringClientRunEndingBefore(Hookup.ClientRunIndex); if (prevClientRun == null) return; // Enhance JohnT: maybe some kind of hookup can merge with previous para or delete an embedded object? CopyFrom(prevClientRun.SelectAtEnd(Para)); Debug.Assert(StringPosition != 0, "should have selected at the END of a non-empty run"); } } string oldValue = Hookup.Text; int newPos = Surrogates.PrevChar(oldValue, StringPosition); // Enhance JohnT: should we delete back to a base? var start = new InsertionPoint(Hookup, newPos, false); if (!Hookup.CanDelete(start, this)) return; Hookup.Delete(start, this); StringPosition = newPos; }
/// <summary> /// Answer true if the selection is contained in the other, that is, it is associated with /// one of the selected characters. /// </summary> public virtual bool Contains(InsertionPoint ip) { return(false); }
/// <summary> /// Get the IP location, if in this segment; if not return a dummy rectangle and 'here' will be false. /// </summary> public Rectangle GetIpLocation(InsertionPoint ip, IVwGraphics vg, PaintTransform ptrans, out bool here) { PaintTransform segTrans = ptrans.PaintTransformOffsetBy(Left, Top); Rect rectPrimary, rectSec; bool fPrimaryHere, fSecHere; Segment.PositionsOfIP(IchMin, vg, segTrans.SourceRect, segTrans.DestRect, ip.RenderParaPosition, ip.AssociatePrevious, LgIPDrawMode.kdmNormal, out rectPrimary, out rectSec, out fPrimaryHere, out fSecHere); if (fPrimaryHere) { here = true; return new Rectangle(rectPrimary.left, rectPrimary.top, rectPrimary.right - rectPrimary.left, rectPrimary.bottom - rectPrimary.top); } here = false; return new Rectangle(); }
/// <summary> /// Return true if we can delete the specified range. A LiteralStringHookup never can, /// but some subclasses can. /// </summary> internal virtual bool CanDelete(InsertionPoint start, InsertionPoint end) { return false; }
/// <summary> /// A generic hookup knows it can't, but various subclasses can. /// </summary> internal virtual bool CanInsertText(InsertionPoint ip) { return false; }
/// <summary> /// Returns false because a style cannot be stored in a string /// </summary> internal override bool CanApplyStyle(InsertionPoint start, InsertionPoint end, string style) { return false; }
internal override void InsertText(InsertionPoint ip, ITsString input) { var bldr = ((TssClientRun)ParaBox.Source.ClientRuns[ClientRunIndex]).Tss.GetBldr(); bldr.ReplaceTsString(ip.StringPosition, ip.StringPosition, input); Writer(bldr.GetString()); }
public override string GetStyleNameAt(InsertionPoint ip) { return ((TssClientRun) ParaBox.Source.ClientRuns[ClientRunIndex]).CharacterStyleNameAt(ip.StringPosition); }
internal override void ApplyStyle(InsertionPoint start, InsertionPoint end, string style) { if (!CanApplyStyle(start, end, style)) return; var bldr = ((TssClientRun) ParaBox.Source.ClientRuns[ClientRunIndex]).Tss.GetBldr(); int newPos = start.StringPosition; bldr.SetStrPropValue(newPos, end.StringPosition, (int) FwTextPropType.ktptNamedStyle, style); Writer(bldr.GetString()); }
internal override bool CanApplyStyle(InsertionPoint start, InsertionPoint end, string style) { return start != null && end != null && Writer != null && ParaBox != null && style != null && ParaBox.Style.Stylesheet.Style(style) != null; }
public RangeSelection ExpandToWord() { var cpe = LgIcuCharPropEngineClass.Create(); RangeSelection rangeSel = new RangeSelection(this, new InsertionPoint(Hookup, StringPosition + 1, AssociatePrevious)); var backwardSel = new InsertionPoint(Hookup, StringPosition, AssociatePrevious); var forwardSel = new InsertionPoint(Hookup, StringPosition, AssociatePrevious); while (true) { if (backwardSel.StringPosition == 0) { break; } backwardSel.StringPosition--; char testChar = backwardSel.ContainingRun.Text[backwardSel.StringPosition]; int testInt = testChar; if (Surrogates.IsTrailSurrogate(testChar)) { backwardSel.StringPosition--; testInt = Surrogates.Int32FromSurrogates(backwardSel.ContainingRun.Text[backwardSel.StringPosition], testChar); } else if (Surrogates.IsLeadSurrogate(testChar)) { testInt = Surrogates.Int32FromSurrogates(testChar, backwardSel.ContainingRun.Text[backwardSel.StringPosition + 1]); } if (!cpe.get_IsNumber(testInt) && !cpe.get_IsWordForming(testInt) || backwardSel.ContainingRun.WritingSystemAt(backwardSel.StringPosition) != ContainingRun.WritingSystemAt(StringPosition)) { backwardSel.StringPosition++; break; } rangeSel = new RangeSelection(backwardSel, this); } backwardSel = rangeSel.Anchor; while (true) { if (forwardSel.StringPosition == forwardSel.ContainingRun.Length) { if (backwardSel.StringPosition == forwardSel.StringPosition) { return(null); } break; } char testChar = forwardSel.ContainingRun.Text[forwardSel.StringPosition]; int testInt = testChar; if (Surrogates.IsLeadSurrogate(testChar)) { forwardSel.StringPosition++; testInt = Surrogates.Int32FromSurrogates(testChar, forwardSel.ContainingRun.Text[forwardSel.StringPosition]); testChar = (char)testInt; } else if (Surrogates.IsTrailSurrogate(testChar)) { testInt = Surrogates.Int32FromSurrogates(forwardSel.ContainingRun.Text[forwardSel.StringPosition - 1], testChar); testChar = (char)testInt; } if (!cpe.get_IsNumber(testInt) && !cpe.get_IsWordForming(testInt) || forwardSel.ContainingRun.WritingSystemAt(forwardSel.StringPosition) != ContainingRun.WritingSystemAt(StringPosition)) { if (testChar.Equals(" ".ToCharArray()[0])) { forwardSel.StringPosition++; rangeSel = new RangeSelection(backwardSel, forwardSel); } break; } forwardSel.StringPosition++; rangeSel = new RangeSelection(backwardSel, forwardSel); } return(rangeSel); }
/// <summary> /// True if the two selections are at the same place (ignoring AssociatePrevious). /// </summary> public bool SameLocation(InsertionPoint other) { return(other != null && Para == other.Para && LogicalParaPosition == other.LogicalParaPosition); }
internal override void Delete(InsertionPoint start, InsertionPoint end) { if (CanDelete(start, end)) { string oldValue = ((StringClientRun)ParaBox.Source.ClientRuns[ClientRunIndex]).Contents; int newPos = start.StringPosition; string newValue = oldValue.Remove(newPos, end.StringPosition - newPos); Writer(newValue); } }
internal override bool CanInsertText(InsertionPoint ip) { return Writer != null && ParaBox != null; // todo: test this case }
// A generic hookup isn't able to do this, but various subclasses can. internal virtual void InsertText(InsertionPoint ip, ITsString input) { }
internal override void Delete(InsertionPoint start, InsertionPoint end) { var bldr = ((TssClientRun)ParaBox.Source.ClientRuns[ClientRunIndex]).Tss.GetBldr(); int newPos = start.StringPosition; bldr.Replace(newPos, end.StringPosition, "", null); Writer(bldr.GetString()); }
public virtual string GetStyleNameAt(InsertionPoint ip) { return ""; }
/// <summary> /// Return the one of your direct children which contains the given IP (or null if none does). /// </summary> internal Hookup ChildContaining(InsertionPoint ip) { return ChildContaining(ip.Hookup); }
internal void DrawIp(InsertionPoint ip, IVwGraphics vg, PaintTransform ptrans) { PaintTransform segTrans = ptrans.PaintTransformOffsetBy(Left, Top); Segment.DrawInsertionPoint(IchMin, vg, segTrans.SourceRect, segTrans.DestRect, ip.RenderParaPosition, ip.AssociatePrevious, true, LgIPDrawMode.kdmNormal); }
/// <summary> /// Get the location of the primary insertion point. /// Review JohnT: is it possible that more than one segment contains this, and we need to combine them? /// Or that none does? /// </summary> public Rectangle GetIpLocation(InsertionPoint ip, IVwGraphics vg, PaintTransform ptrans) { PaintTransform childTrans = ChildTransformFromRootTransform(ptrans); int ichMin = ip.RenderParaPosition; int ichLim = ip.LastRenderParaPosition; if (ichLim > ichMin) { // Displaying a substitute string. var bounds = new Rect(); bool first = true; DoRangePaintingOp(ichMin, ichLim, vg, childTrans, (box, vg1, childTrans1, top, bottom, ichMin1, ichLim1) => { first = GetRangeLocationInBox(box, vg1, childTrans1, top, bottom, ichMin1, ichLim1, first, ref bounds); }); return new Rectangle(bounds.left, bounds.top, bounds.right - bounds.left, bounds.bottom - bounds.top); } for (Box current = FirstBox; current != null; current = current.Next) { var sb = current as StringBox; if (sb == null) continue; bool fLocHere; Rectangle temp = sb.GetIpLocation(ip, vg, childTrans, out fLocHere); if (fLocHere) { return temp; } } throw new ApplicationException("No paragraph segment has the location of the primary IP"); }
/// <summary> /// Draw an insertion point. Currently every segment is given the chance to draw it, though typically only one will. /// Sometimes a split insertion point may be drawn at an unexpected place. /// Enhance JohnT: Support Graphite by passing other draw modes when there is a split cursor at segment boundaries. /// </summary> public void DrawIp(InsertionPoint ip, IVwGraphics vg, PaintTransform ptrans) { PaintTransform childTrans = ptrans.PaintTransformOffsetBy(Left, Top); int ichMin = ip.RenderParaPosition; int ichLim = ip.LastRenderParaPosition; if (ichLim > ichMin) { // Displaying a substitute string. DoRangePaintingOp(ichMin, ichLim, vg, ptrans, DrawSelectionInBox); return; } for (Box current = FirstBox; current != null; current = current.Next) { var sb = current as StringBox; if (sb == null) continue; sb.DrawIp(ip, vg, childTrans); } }
internal virtual void ApplyStyle(InsertionPoint start, InsertionPoint end, string style) { }
public InsertionPoint NextIp(int distance) { InsertionPoint newIp; IClientRun run = ContainingRun; ParaBox box = run.Hookup.ParaBox; if (StringPosition + distance > run.Text.Length) { distance -= run.Text.Length - StringPosition; if (box.Source.ClientRuns[run.Hookup.ClientRunIndex] != box.Source.ClientRuns.Last()) run = box.Source.ClientRuns[run.Hookup.ClientRunIndex + 1]; else { box = box.NextParaBox; if (box == null) return null; run = box.Source.ClientRuns[0]; } newIp = run.SelectAtStart(box); newIp = newIp.NextIp(distance); } else { newIp = new InsertionPoint(Hookup, StringPosition + distance, AssociatePrevious); } return newIp; }
internal override void InsertText(InsertionPoint ip, string input) { string oldValue = ((StringClientRun)ParaBox.Source.ClientRuns[ClientRunIndex]).Contents; string newValue = oldValue.Insert(ip.StringPosition, input); Writer(newValue); }
internal override bool CanInsertText(InsertionPoint ip) { return Writer != null && ParaBox != null; }
internal override bool CanDelete(InsertionPoint start, InsertionPoint end) { return start != null && end != null && Writer != null && ParaBox != null && start.Hookup == this && end.Hookup == this && start.StringPosition < end.StringPosition; }
/// <summary> /// Delete the specified range (some subclasses actually can). /// Todo JohnT: more should be able to. /// </summary> internal virtual void Delete(InsertionPoint start, InsertionPoint end) { }
/// <summary> /// Make your state equivalent to the other IP. /// </summary> private void CopyFrom(InsertionPoint other) { Hookup = other.Hookup; StringPosition = other.StringPosition; AssociatePrevious = other.AssociatePrevious; }
/// <summary> /// Answer true if the selection is contained in the other, that is, it is associated with /// one of the selected characters. /// </summary> public virtual bool Contains(InsertionPoint ip) { return false; }
internal virtual bool CanApplyStyle(InsertionPoint start, InsertionPoint end, string style) { return false; }