public static List <TikzParseItem> GetFullSelection(IEnumerable <TikzParseItem> ItemList) { List <TikzParseItem> ret = new List <TikzParseItem>(); if (ItemList == null || ItemList.Count() == 0) { return(ret); } // ** Find the least common ancestor ** // find all ancestors for each Item var AncestorList = ItemList.Select(l => FindAncestors(l)).ToList(); // as long as the highest ancestor is common, remove this ancestor from the lists while (AncestorList.AllEqual(l => l.Last()) && AncestorList.All(l => l.Count >= 2)) { foreach (var l in AncestorList) { l.RemoveAt(l.Count - 1); } } // Now the last elements of all lists are ancestors on one "level" of the tree. // Remove duplicates and sort var elements = AncestorList.Select(l => l.Last()) .Distinct() .OrderBy(tpi => tpi.StartPosition()) .ToList(); // Handle a special case: if the items are all coordinates in a single Tikz_Path, // that (other than those items) contains only Tikz_Somethings, then return the whole path TikzContainerParseItem commonparent = elements.First().parent; if (commonparent is Tikz_Path) { if (commonparent.Children.All(tpi => (tpi is Tikz_Something) || elements.Contains(tpi))) { ret.Add(commonparent); return(ret); } } // otherwise just return the elements found so far return(elements); }
/// <summary> /// Recursive function to scan the parsetree in a depth first manner. /// nodelist contains the current map from _old_ nodenames to nodes. it is used to update node references /// </summary> private static void ScanTree(TikzContainerParseItem tc, Dictionary <string, Tikz_Node> nodelist, Tikz_Picture tp) { foreach (var tpi in tc.Children) { if (tpi is Tikz_Node) { Tikz_Node tn = tpi as Tikz_Node; if (tn.name != null && tn.name.Trim() != "") { string name = CleanName(tn.name.Trim()); // remember the node with its old name (all coordinates referring to this name henceforth will be changed to the new name nodelist[name] = tn; if (tp.nodelist.ContainsKey(name)) { // we have to change the name tn.name = UniquefyName(name, tp); tn.UpdateText(); } tn.RegisterNodeAndStyleRefs(); } } else if (tpi is Tikz_Coord) { Tikz_Coord tco = tpi as Tikz_Coord; if (tco.type == Tikz_CoordType.Named) { string nameref = CleanName(tco.nameref); if (nodelist.ContainsKey(nameref)) { if (CleanName(nodelist[nameref].name) != nameref) { tco.nameref = CleanName(nodelist[nameref].name); tco.UpdateText(); } } } } else if (tpi is TikzContainerParseItem) { ScanTree(tpi as TikzContainerParseItem, nodelist, tp); } } }
/// <summary> /// The method returns a list of TikzParseItems that together form the "code block" of the argument. /// </summary> /// <param name="?"></param> /// <returns></returns> public static List <TikzParseItem> GetCodeBlock(IEnumerable <TikzParseItem> ItemList) { List <TikzParseItem> ret = new List <TikzParseItem>(); if (ItemList == null || ItemList.Count() == 0) { return(ret); } // find all ancestors for each Item var AncestorList = ItemList.Select(l => FindAncestors(l)).ToList(); // as long as the highest ancestor is common, remove this ancestor from the lists while (AncestorList.AllEqual(l => l.Last()) && AncestorList.All(l => l.Count >= 2)) { foreach (var l in AncestorList) { l.RemoveAt(l.Count - 1); } } // Now the last elements of all lists are ancestors on one "level" of the tree. Find the smallest and largest in the horizontal ordering var elements = AncestorList.Select(l => l.Last()); TikzContainerParseItem commonparent = elements.First().parent; int firstind = elements.Min(t => commonparent.Children.IndexOf(t)); int lastind = elements.Max(t => commonparent.Children.IndexOf(t)); // Handle a special case: if the items are all coordinates in a single Tikz_Path, // that (other than those items) contains only Tikz_Somethings, then return the whole path if (commonparent is Tikz_Path) { if (commonparent.Children.FindIndex(tpi => !(tpi is Tikz_Something)) >= firstind && commonparent.Children.FindLastIndex(tpi => !(tpi is Tikz_Something)) <= lastind) { ret.Add(commonparent); return(ret); } } return(commonparent.Children.GetRange(firstind, lastind - firstind + 1).ToList()); }
//create a new CurAddTo (even though their already might be one) //(needed for edge tool) protected virtual bool AddNewCurAddTo() { // find tikzpicture Tikz_Picture tpict = overlay.ParseTree.GetTikzPicture(); if (tpict == null) { return(false); } Parser.Tikz_Path tp = new Parser.Tikz_Path(); tp.starttag = @"\draw "; tp.endtag = ";"; if (overlay.EdgeStyle != "") { Parser.Tikz_Options topt = new Parser.Tikz_Options(); topt.starttag = "["; topt.endtag = "]"; Parser.Tikz_Option to = new Parser.Tikz_Option(); to.type = Parser.Tikz_OptionType.key; to.key = overlay.EdgeStyle; topt.AddOption(to); tp.AddChild(topt); tp.options = topt; } if (overlay.CurEditing != null) { overlay.CurEditing.tikzitem.AddChild(tp); overlay.CurEditing.tikzitem.AddChild(new Parser.Tikz_Something("\r\n")); } else { tpict.AddChild(tp); tpict.AddChild(new Parser.Tikz_Something("\r\n")); } curAddTo = tp; return(true); }
bool FillNodesOnArc() { TikzContainerParseItem pa = curDragged.item.parent; if (!(curDragged.item is Tikz_XYItem)) { return(false); } Tikz_XYItem cur = curDragged.item as Tikz_XYItem; nodesOnArc.Clear(); nodesOnArc.Add(cur); // find all arcs to the right for (int i = pa.Children.IndexOf(cur) + 1; i < pa.Children.Count; i++) { if (pa.Children[i] is Tikz_Arc) { nodesOnArc.Add(pa.Children[i] as Tikz_Arc); } else if (pa.Children[i].ChangesCurPoint()) { break; } } // find all arcs to left, including startpoint if (cur is Tikz_Arc) { for (int i = pa.Children.IndexOf(cur) - 1; i >= 0; i--) { if (pa.Children[i] is Tikz_Arc) { nodesOnArc.Insert(0, pa.Children[i] as Tikz_Arc); } else if (pa.Children[i].ChangesCurPoint()) { if (pa.Children[i] is Tikz_XYItem) { nodesOnArc.Insert(0, pa.Children[i] as Tikz_XYItem); } break; } } } // todo: no startpoint present... if (nodesOnArc.First() is Tikz_Arc) { } // compute radius and center // in absolute tikz coordinates Point center, p; double R; Tikz_Arc ta; if (cur is Tikz_Arc) { ta = cur as Tikz_Arc; } else if (nodesOnArc.Count > 1 && nodesOnArc[1] is Tikz_Arc) { ta = nodesOnArc[1] as Tikz_Arc; } else { return(false); } if (!ta.GetArcCenterAbs(out center) || !ta.GetAbsPos(out p)) { throw new Exception("ArcEditTool: Invalid coordinate discovered."); } R = (p - center).Length; // throw out all items with non-fitting parameters for (int i = nodesOnArc.Count - 1; i >= 0; i--) { if (nodesOnArc[i] is Tikz_Arc) { Point cc, pp; double RR; if (!(nodesOnArc[i] as Tikz_Arc).GetArcCenterAbs(out cc) || !nodesOnArc[i].GetAbsPos(out pp)) { throw new Exception("ArcEditTool: Invalid coordinate discovered."); } RR = (cc - pp).Length; if ((cc - center).Length > 1e-3 || Math.Abs(RR - R) > 1e-3) { nodesOnArc.RemoveAt(i); } } else // other Tikz_XYItem, namely startpoint { Point pp; if (!nodesOnArc[i].GetAbsPos(out pp)) { throw new Exception("ArcEditTool: Invalid coordinate discovered."); } // check if pooint lies on circle if (Math.Abs(R - (pp - center).Length) > 1e-3) { nodesOnArc.RemoveAt(i); } } } curDraggedInd = nodesOnArc.IndexOf(cur); PreviewArc.Center = overlay.TikzToScreen(center, true); center_tikz = center; // Fill the PreviewArcs Spokes array List <double> spokes = new List <double>(); for (int i = 0; i < nodesOnArc.Count; i++) { if (nodesOnArc[i] is Tikz_Arc) { Tikz_Arc tia = nodesOnArc[i] as Tikz_Arc; double a1 = tia.phi1.GetInCM() * Math.PI / 180, a2 = tia.phi2.GetInCM() * Math.PI / 180; //if (!(spokes.Count > 0 && spokes.Last() == a1)) // spokes.Add(a1); spokes.Add(a2); //Point pp; //if (!nodesOnArc[i].GetAbsPos(out pp)) // throw new Exception("ArcEditTool: Invalid coordinate discovered."); //Vector v = pp - center; } else { // should go here at most for the first item in list double a1 = (nodesOnArc[1] as Tikz_Arc).phi1.GetInCM() * Math.PI / 180; spokes.Add(a1); } } PreviewArc.Spokes = spokes; return(true); }
/// <summary> /// Performs some action on the currently selected items. /// /// If necessary, raises the ReplaceText event (via calling RaiseRelaceText) to request text changes. /// This is necessary since the Parsetree currently does not support deletions. /// Ideally, we would directly do the changes to the Parsetree instead of firing the ReplaceText event. /// </summary> public void PerformCodeBlockOperation(CodeBlockAction action) { // The operations are only valid when the select tool is active if (View.Tool != OverlayToolType.move) { return; } List <TikzParseItem> FullSelection = TikzParseTreeHelper.GetFullSelection(selectionTool.SelItems.Select(ols => ols.item)); if (!FullSelection.Any()) { return; } // get codeblock text string cbtext = "", cbtextE = ""; foreach (var tpi in FullSelection) { cbtext += tpi.ToString() + Environment.NewLine; } // if the selected items are within a path, enscope by adding { }. If they are within another scope or the tikzpicture, enscope by \begin{scope} \end{scope} TikzContainerParseItem tc = FullSelection.First().parent; if (tc is Tikz_Picture || tc is Tikz_Scope) { cbtextE = "\\begin{scope}[]" + Environment.NewLine + cbtext + Environment.NewLine + "\\end{scope}" + Environment.NewLine; } else { cbtextE = " { " + cbtext + " } "; } var ReplacementList = new List <ReplaceTextEventArgs.ReplaceData>(); if (action == CodeBlockAction.Copy) { Clipboard.SetText(cbtext); } else if (action == CodeBlockAction.CopyEnscoped) { Clipboard.SetText(cbtextE); } else if (action == CodeBlockAction.Delete || action == CodeBlockAction.Cut || action == CodeBlockAction.CutEnscoped) { foreach (var tpi in FullSelection) { ReplacementList.Insert(0, new ReplaceTextEventArgs.ReplaceData() { StartPosition = tpi.StartPosition(), Length = tpi.Length, ReplacementText = "" }); } if (action == CodeBlockAction.Cut) { Clipboard.SetText(cbtext); } else if (action == CodeBlockAction.CutEnscoped) { Clipboard.SetText(cbtextE); } View.RaiseReplaceText(new ReplaceTextEventArgs() { Replacements = ReplacementList }); } else if (action == CodeBlockAction.Collect || action == CodeBlockAction.CollectEnscoped) { // Text to delete ... mind the order foreach (var tpi in FullSelection) { ReplacementList.Insert(0, new ReplaceTextEventArgs.ReplaceData() { StartPosition = tpi.StartPosition(), Length = tpi.Length, ReplacementText = "" }); } // text to insert (text of selected nodes, gathered together, optionally enscoped if (action == CodeBlockAction.Collect) { ReplacementList.Add(new ReplaceTextEventArgs.ReplaceData() { StartPosition = FullSelection.First().StartPosition(), Length = 0, ReplacementText = cbtext }); } else { ReplacementList.Add(new ReplaceTextEventArgs.ReplaceData() { StartPosition = FullSelection.First().StartPosition(), Length = 0, ReplacementText = cbtextE }); } View.RaiseReplaceText(new ReplaceTextEventArgs() { Replacements = ReplacementList }); } }
static bool FillItem(TikzContainerParseItem item, CommonTree t, CommonTokenStream tokens) { int curToken = t.TokenStartIndex; if (item is Tikz_ParseTree) { curToken = 0; // for root, start at the beginning } if (t.Children == null) { return(false); } foreach (CommonTree childt in t.Children) { addSomething(item, tokens, curToken, childt.TokenStartIndex - 1); switch (childt.Type) { case simpletikzParser.IM_PICTURE: Tikz_Picture tp = new Tikz_Picture(); FillItem(tp, childt, tokens); item.AddChild(tp); break; case simpletikzParser.IM_STARTTAG: item.starttag = getTokensString(tokens, childt.TokenStartIndex, childt.TokenStopIndex); break; case simpletikzParser.IM_ENDTAG: item.endtag = getTokensString(tokens, childt.TokenStartIndex, childt.TokenStopIndex); break; case simpletikzParser.IM_PATH: Tikz_Path tpath = new Tikz_Path(); FillItem(tpath, childt, tokens); item.AddChild(tpath); break; case simpletikzParser.IM_SCOPE: Tikz_Scope tscope = new Tikz_Scope(); FillItem(tscope, childt, tokens); item.AddChild(tscope); break; case simpletikzParser.IM_COORD: Tikz_Coord tc = Tikz_Coord.FromCommonTree(childt, tokens); tc.text = getTokensString(tokens, childt); item.AddChild(tc); break; case simpletikzParser.IM_ARC: Tikz_Arc ta = Tikz_Arc.FromCommonTree(childt, tokens); ta.text = getTokensString(tokens, childt); item.AddChild(ta); break; case simpletikzParser.IM_NODE: Tikz_Node tn = Tikz_Node.FromCommonTree(childt, tokens); tn.text = getTokensString(tokens, childt); item.AddChild(tn); break; case simpletikzParser.IM_OPTION_KV: case simpletikzParser.IM_OPTION_STYLE: Tikz_Option topt = Tikz_Option.FromCommonTree(childt, tokens); if (topt == null) { break; } //topt.text = getTokensString(tokens, childt); String s = getTokensString(tokens, childt); topt.text = s; item.AddChild(topt); break; case simpletikzParser.IM_OPTIONS: //Tikz_Options to = Tikz_Options.FromCommonTree(childt); Tikz_Options to = new Tikz_Options(); FillItem(to, childt, tokens); item.AddChild(to); //to.text = getTokensString(tokens, childt); //item.AddChild(tn); if (item.options == null) { // determine whether option belongs to the item (e.g. \draw [this belongs to draw] blabla [thisnot]) // i.e., the scope of the options is the whole item's body // this is hacky if (item.Children.Count == 1 || (item.Children.Count == 2 && (item.Children[0] is Tikz_Something) && item.Children[0].ToString().Trim() == "")) { item.options = to; } } break; case simpletikzParser.IM_TIKZSET: Tikz_Options to2 = new Tikz_Options(); FillItem(to2, childt, tokens); item.AddChild(to2); break; case simpletikzParser.IM_STYLE: Tikz_Option topt2 = Tikz_Option.FromCommonTree(childt, tokens); //FillItem(to2, childt, tokens); topt2.text = getTokensString(tokens, childt); item.AddChild(topt2); break; case simpletikzParser.IM_CONTROLS: Tikz_Controls tcontrols = new Tikz_Controls(); FillItem(tcontrols, childt, tokens); item.AddChild(tcontrols); break; case simpletikzParser.IM_SIZE: Tikz_Size tsize = Tikz_Size.FromCommonTree(childt, tokens); tsize.text = getTokensString(tokens, childt); item.AddChild(tsize); //Tikz_Size tsize = new Tikz_Size(); //item.AddChild(tsize); break; //case simpletikzParser.ID: //case simpletikzParser.IM_STRING: //case simpletikzParser.COMMAND: //case simpletikzParser.T__57: // break; case simpletikzParser.IM_TIKZEDT_CMD: Tikz_EdtCommand cmd = new Tikz_EdtCommand(getTokensString(tokens, childt)); item.AddChild(cmd); break; case simpletikzParser.IM_DONTCARE: Tikz_Something st = new Tikz_Something(getTokensString(tokens, childt)); item.AddChild(st); break; default: // getting here is an error throw new Exception(" childt.Type not handled! " + childt.Type.ToString() + " (\"" + getTokensString(tokens, childt) + "\")"); //break; } curToken = childt.TokenStopIndex + 1; } if (t.TokenStartIndex >= 0) // rule out empty code { addSomething(item, tokens, curToken, t.TokenStopIndex); } return(true); }
public override void OnDeactivate() { base.OnDeactivate(); curAddTo = null; }