public void CreateIndentedNodes_basic() { m_DataTree = new DataTree(); m_Slice = GenerateSlice(Cache, m_DataTree); // Data taken from a running Sena 3 var caller = CreateXmlElementFromOuterXmlOf("<part ref=\"AsLexemeForm\" label=\"Lexeme Form\" expansion=\"expanded\"><indent><part ref=\"IsAbstractBasic\" label=\"Is Abstract Form\" visibility=\"never\" /><!-- could use 'ifTrue' if we had it --><part ref=\"MorphTypeBasic\" visibility=\"ifdata\" /><part ref=\"PhoneEnvBasic\" visibility=\"ifdata\" /><part ref=\"StemNameForLexemeForm\" visibility=\"ifdata\" /></indent></part>"); var obj = Cache.ServiceLocator.GetInstance <IMoStemAllomorphFactory>().Create(); const int indent = 0; int insPos = 1; var path = GeneratePath(); var reuseMap = new ObjSeqHashMap(); // Data taken from a running Sena 3 var node = CreateXmlElementFromOuterXmlOf("<slice field=\"Form\" label=\"Form\" editor=\"multistring\" ws=\"all vernacular\" weight=\"light\" menu=\"mnuDataTree-LexemeForm\" contextMenu=\"mnuDataTree-LexemeFormContext\" spell=\"no\"><properties><bold value=\"on\" /><fontsize value=\"120%\" /></properties></slice>"); m_Slice.CreateIndentedNodes(caller, obj, indent, ref insPos, path, reuseMap, node); }
public void CreateGhostStringSlice_ParentSliceNotNull() { var path = GeneratePath(); var reuseMap = new ObjSeqHashMap(); var obj = Cache.ServiceLocator.GetInstance <IMoStemAllomorphFactory>().Create(); m_DataTree = new DataTree(); m_Slice = GenerateSlice(Cache, m_DataTree); m_Mediator = new Mediator(); m_Slice.Mediator = m_Mediator; m_propertyTable = new PropertyTable(m_Mediator); m_Slice.PropTable = m_propertyTable; var node = CreateXmlElementFromOuterXmlOf("<seq field=\"Pronunciations\" layout=\"Normal\" ghost=\"Form\" ghostWs=\"pronunciation\" ghostLabel=\"Pronunciation\" menu=\"mnuDataTree-Pronunciation\" />"); int indent = 0; int insertPosition = 0; int flidEmptyProp = 5002031; // runtime flid of ghost field m_DataTree.MakeGhostSlice(path, node, reuseMap, obj, m_Slice, flidEmptyProp, null, indent, ref insertPosition); var ghostSlice = m_DataTree.Slices[0]; Assert.NotNull(ghostSlice); Assert.AreEqual(ghostSlice.PropTable, m_Slice.PropTable); }
/// <summary> /// Create slices appropriate for current root object and layout, reusing any existing slices, /// and clearing out any that remain unused. If it is for a different object, reuse is more limited. /// </summary> private void CreateSlices(bool differentObject) { var watch = new Stopwatch(); watch.Start(); bool wasVisible = this.Visible; var previousSlices = new ObjSeqHashMap(); int oldSliceCount = Slices.Count; ConstructingSlices = true; try { // Bizarrely, calling Hide has been known to cause OnEnter to be called in a slice; we need to suppress this, // hence guarding it by setting ConstructingSlices. Hide(); if (m_currentSlice != null) m_currentSlice.SetCurrentState(false); // needs to know no longer current, may want to save something. m_currentSlice = null; if (differentObject) m_currentSliceNew = null; //if (differentObject) // Slices.Clear(); var dummySlices = new List<Slice>(Slices.Count); foreach (Slice slice in Slices) { slice.Visible = false; if (slice.Key != null) // dummy slices may not have keys and shouldn't be reused. previousSlices.Add(slice.Key, slice); else dummySlices.Add(slice); } bool gonnerHasToolTip = false; // Does any goner have one? // Get rid of the dummies we aren't going to remove. foreach (Slice slice in dummySlices) { gonnerHasToolTip |= slice.ToolTip != null; RemoveSlice(slice); } previousSlices.ClearUnwantedPart(differentObject); CreateSlicesFor(m_root, null, m_rootLayoutName, m_layoutChoiceField, 0, 0, new ArrayList(20), previousSlices, null); // Clear out any slices NOT reused. RemoveSlice both // removes them from the DataTree's controls collection and disposes them. foreach (Slice gonner in previousSlices.Values) { gonnerHasToolTip |= gonner.ToolTip != null; RemoveSlice(gonner); } if (gonnerHasToolTip) { // Since the dumb MS ToolTip class can't just remove one, // we have to remove them all and re-add the remaining ones // in order to have it really turn loose of the SliceTreeNode. m_tooltip.RemoveAll(); foreach (Slice keeper in Slices) SetToolTip(keeper); } ResetTabIndices(0); } finally { ConstructingSlices = false; } if (wasVisible) Show(); watch.Stop(); // Uncomment this to investigate slice performance or issues with dissappearing slices //Debug.WriteLine("CreateSlices took " + watch.ElapsedMilliseconds + " ms. Originally had " + oldSliceCount + " controls; now " + Slices.Count); //previousSlices.Report(); }
public override void GenerateChildren(XmlNode node, XmlNode caller, ICmObject obj, int indent, ref int insPos, ArrayList path, ObjSeqHashMap reuseMap) { CheckDisposed(); // If node has children, figure what to do with them... XmlNodeList children = node.ChildNodes; // It's important to initialize m_refs here rather than in FinishInit, because we need it // to be updated when the slice is reused in a regenerate. // Refactor JohnT: better still, make it a virtual attribute, and Refresh will automatically // clear it from the cache. if (m_vh != null) { m_refs = m_vh.LexReferences(m_obj.Hvo); } else { // DEPRECATED. string className = m_cache.MetaDataCacheAccessor.GetClassName((uint)m_obj.ClassID); // m_flid = AutoDataTreeMenuHandler.ContextMenuHelper.GetFlid(m_cache.MetaDataCacheAccessor, // className, m_fieldName); string qry = string.Format("SELECT DISTINCT [Src] FROM LexReference_Targets WHERE [Dst]={0}", m_obj.Hvo); m_refs = DbOps.ReadIntsFromCommand(m_cache, qry, null); } if (m_refs.Count == 0) { // It could have children but currently can't: we always show this as collapsedEmpty. Expansion = DataTree.TreeItemState.ktisCollapsedEmpty; return; } for (int i = 0; i < m_refs.Count; i++) { GenerateChildNode(i, node, caller, indent, ref insPos, path, reuseMap); } Expansion = DataTree.TreeItemState.ktisExpanded; }
/// <summary></summary> public static Slice Create(FdoCache cache, string editor, int flid, XmlNode node, ICmObject obj, StringTable stringTbl, IPersistenceProvider persistenceProvider, Mediator mediator, XmlNode caller, ObjSeqHashMap reuseMap) { Slice slice; switch (editor) { case "multistring": // first, these are the most common slices. { if (flid == 0) { throw new ApplicationException("field attribute required for multistring " + node.OuterXml); } string wsSpec = XmlUtils.GetOptionalAttributeValue(node, "ws"); int wsMagic = WritingSystemServices.GetMagicWsIdFromName(wsSpec); if (wsMagic == 0) { throw new ApplicationException( "ws must be 'all vernacular', 'all analysis', 'analysis vernacular', or 'vernacular analysis'" + " it said '" + wsSpec + "'."); } bool forceIncludeEnglish = XmlUtils.GetOptionalBooleanAttributeValue(node, "forceIncludeEnglish", false); bool spellCheck = XmlUtils.GetOptionalBooleanAttributeValue(node, "spell", true); // Either the part or the caller can specify that it isn't editable. // (The part may 'know' this, e.g. because it's a virtual attr not capable of editing; // more commonly the caller knows there isn't enough context for safe editing. bool editable = XmlUtils.GetOptionalBooleanAttributeValue(caller, "editable", true) && XmlUtils.GetOptionalBooleanAttributeValue(node, "editable", true); string optionalWsSpec = XmlUtils.GetOptionalAttributeValue(node, "optionalWs"); int wsMagicOptional = WritingSystemServices.GetMagicWsIdFromName(optionalWsSpec); MultiStringSlice msSlice = reuseMap.GetSliceToReuse("MultiStringSlice") as MultiStringSlice; if (msSlice == null) { slice = new MultiStringSlice(obj, flid, wsMagic, wsMagicOptional, forceIncludeEnglish, editable, spellCheck); } else { slice = msSlice; msSlice.Reuse(obj, flid, wsMagic, wsMagicOptional, forceIncludeEnglish, editable, spellCheck); } break; } case "defaultvectorreference": // second most common. { var rvSlice = reuseMap.GetSliceToReuse("ReferenceVectorSlice") as ReferenceVectorSlice; if (rvSlice == null) { slice = new ReferenceVectorSlice(cache, obj, flid); } else { slice = rvSlice; rvSlice.Reuse(obj, flid); } break; } case "possvectorreference": { var prvSlice = reuseMap.GetSliceToReuse("PossibilityReferenceVectorSlice") as PossibilityReferenceVectorSlice; if (prvSlice == null) { slice = new PossibilityReferenceVectorSlice(cache, obj, flid); } else { slice = prvSlice; prvSlice.Reuse(obj, flid); } break; } case "semdomvectorreference": { var prvSlice = reuseMap.GetSliceToReuse("SemanticDomainReferenceVectorSlice") as SemanticDomainReferenceVectorSlice; if (prvSlice == null) { slice = new SemanticDomainReferenceVectorSlice(cache, obj, flid); } else { slice = prvSlice; prvSlice.Reuse(obj, flid); } break; } case "string": { if (flid == 0) { throw new ApplicationException("field attribute required for basic properties " + node.OuterXml); } int ws = GetWs(mediator, cache, node); if (ws != 0) { slice = new StringSlice(obj, flid, ws); } else { slice = new StringSlice(obj, flid); } var fShowWsLabel = XmlUtils.GetOptionalBooleanAttributeValue(node, "labelws", false); if (fShowWsLabel) { (slice as StringSlice).ShowWsLabel = true; } int wsEmpty = GetWs(mediator, cache, node, "wsempty"); if (wsEmpty != 0) { (slice as StringSlice).DefaultWs = wsEmpty; } break; } case "jtview": { string layout = XmlUtils.GetOptionalAttributeValue(caller, "param"); if (layout == null) { layout = XmlUtils.GetManditoryAttributeValue(node, "layout"); } // Editable if BOTH the caller (part ref) AND the node itself (the slice) say so...or at least if neither says not. bool editable = XmlUtils.GetOptionalBooleanAttributeValue(caller, "editable", true) && XmlUtils.GetOptionalBooleanAttributeValue(node, "editable", true); slice = new ViewSlice(new XmlView(obj.Hvo, layout, stringTbl, editable)); break; } case "summary": { slice = new SummarySlice(); break; } case "enumcombobox": { slice = new EnumComboSlice(cache, obj, flid, stringTbl, node["deParams"]); break; } case "referencecombobox": { slice = new ReferenceComboBoxSlice(cache, obj, flid, persistenceProvider); break; } case "typeaheadrefatomic": { slice = new AtomicRefTypeAheadSlice(obj, flid); break; } case "msareferencecombobox": { slice = new MSAReferenceComboBoxSlice(cache, obj, flid, persistenceProvider); break; } case "lit": // was "message" { string message = XmlUtils.GetManditoryAttributeValue(node, "message"); if (stringTbl != null) { string sTranslate = XmlUtils.GetOptionalAttributeValue(node, "translate", ""); if (sTranslate.Trim().ToLower() != "do not translate") { message = stringTbl.LocalizeLiteralValue(message); } } slice = new MessageSlice(message); break; } case "picture": { slice = new PictureSlice((ICmPicture)obj); break; } case "image": { try { slice = new ImageSlice(DirectoryFinder.FWCodeDirectory, XmlUtils.GetManditoryAttributeValue(node, "param1")); } catch (Exception error) { slice = new MessageSlice(String.Format(DetailControlsStrings.ksImageSliceFailed, error.Message)); } break; } case "checkbox": { slice = new CheckboxSlice(cache, obj, flid, node); break; } case "checkboxwithrefresh": { slice = new CheckboxRefreshSlice(cache, obj, flid, node); break; } case "time": { slice = new DateSlice(cache, obj, flid); break; } case "integer": // produced in the auto-generated parts from the conceptual model case "int": // was "integer" { slice = new IntegerSlice(cache, obj, flid); break; } case "gendate": { slice = new GenDateSlice(cache, obj, flid); break; } case "morphtypeatomicreference": { slice = new MorphTypeAtomicReferenceSlice(cache, obj, flid); break; } case "atomicreferencepos": { slice = new AtomicReferencePOSSlice(cache, obj, flid, persistenceProvider, mediator); break; } case "possatomicreference": { slice = new PossibilityAtomicReferenceSlice(cache, obj, flid); break; } case "atomicreferenceposdisabled": { slice = new AutomicReferencePOSDisabledSlice(cache, obj, flid, persistenceProvider, mediator); break; } case "defaultatomicreference": { slice = new AtomicReferenceSlice(cache, obj, flid); break; } case "defaultatomicreferencedisabled": { slice = new AtomicReferenceDisabledSlice(cache, obj, flid); break; } case "derivmsareference": { slice = new DerivMSAReferenceSlice(cache, obj, flid); break; } case "inflmsareference": { slice = new InflMSAReferenceSlice(cache, obj, flid); break; } case "phoneenvreference": { slice = new PhoneEnvReferenceSlice(cache, obj, flid); break; } case "sttext": { slice = new StTextSlice(obj, flid, GetWs(mediator, cache, node)); break; } case "custom": { slice = (Slice)DynamicLoader.CreateObject(node); break; } case "customwithparams": { slice = (Slice)DynamicLoader.CreateObject(node, new object[] { cache, editor, flid, node, obj, stringTbl, persistenceProvider, GetWs(mediator, cache, node) }); break; } case "ghostvector": { slice = new GhostReferenceVectorSlice(cache, obj, node); break; } case "command": { slice = new CommandSlice(node["deParams"]); break; } case null: //grouping nodes do not necessarily have any editor { slice = new Slice(); break; } case "message": // case "integer": // added back in to behave as "int" above throw new Exception("use of obsolete editor type (message->lit, integer->int)"); case "autocustom": slice = MakeAutoCustomSlice(cache, obj, caller); if (slice == null) { return(null); } break; case "defaultvectorreferencedisabled": // second most common. { ReferenceVectorDisabledSlice rvSlice = reuseMap.GetSliceToReuse("ReferenceVectorDisabledSlice") as ReferenceVectorDisabledSlice; if (rvSlice == null) { slice = new ReferenceVectorDisabledSlice(cache, obj, flid); } else { slice = rvSlice; rvSlice.Reuse(obj, flid); } break; } default: { //Since the editor has not been implemented yet, //is there a bitmap file that we can show for this editor? //Such bitmaps belong in the distFiles xde directory string fwCodeDir = DirectoryFinder.FWCodeDirectory; string editorBitmapRelativePath = "xde/" + editor + ".bmp"; if (File.Exists(Path.Combine(fwCodeDir, editorBitmapRelativePath))) { slice = new ImageSlice(fwCodeDir, editorBitmapRelativePath); } else { slice = new MessageSlice(String.Format(DetailControlsStrings.ksBadEditorType, editor)); } break; } } slice.AccessibleName = editor; return(slice); }
/// <summary></summary> public virtual void GenerateChildren(XmlNode node, XmlNode caller, ICmObject obj, int indent, ref int insPos, ArrayList path, ObjSeqHashMap reuseMap, bool fUsePersistentExpansion) { CheckDisposed(); // If node has children, figure what to do with them... // XmlNodeList children = node.ChildNodes; // unused variable DataTree.NodeTestResult ntr; // We may get child nodes from either the node itself or the calling part, but currently // don't try to handle both; we consider the children of the caller, if any, to override // the children of the node (but not unify with them, since a different kind of children // are involved). // A newly created slice is always in state ktisFixed, but that is not appropriate if it // has children from either source. However, a node which notionally has children may in fact have nothing to // show, perhaps because a sequence is empty. First evaluate this, and if true, set it // to ktisCollapsedEmpty. //bool fUseChildrenOfNode; XmlNode indentNode = null; if (caller != null) indentNode = caller.SelectSingleNode("indent"); if (indentNode != null) { // Similarly pretest for children of caller, to see whether anything is produced. ContainingDataTree.ApplyLayout(obj, this, indentNode, indent + ExtraIndent(indentNode), insPos, path, reuseMap, true, out ntr); //fUseChildrenOfNode = false; } else { int insPosT = insPos; // don't modify the real one in this test call. ntr = ContainingDataTree.ProcessPartChildren(node, path, reuseMap, obj, this, indent + ExtraIndent(node), ref insPosT, true, null, false, node); //fUseChildrenOfNode = true; } if (ntr == DataTree.NodeTestResult.kntrNothing) Expansion = DataTree.TreeItemState.ktisFixed; // probably redundant, but play safe else if (ntr == DataTree.NodeTestResult.kntrPossible) { // It could have children but currently can't: we always show this as collapsedEmpty. Expansion = DataTree.TreeItemState.ktisCollapsedEmpty; } // Remaining branches are for a node that really has children. else if (Expansion == DataTree.TreeItemState.ktisCollapsed) { // Reusing a node that was collapsed (and it has something to expand): // leave it that way (whatever the spec says). } else { // It has children: decide whether to expand them. // Old code does not expand by default, couple of ways to override. // else if (Expansion == DataTree.TreeItemState.ktisExpanded // || (fUseChildrenOfNode && XmlUtils.GetOptionalAttributeValue(node, "expansion") == "expanded") // || (XmlUtils.GetOptionalAttributeValue(caller, "expansion") == "expanded") // || Expansion == DataTree.TreeItemState.ktisCollapsedEmpty) bool fExpand = XmlUtils.GetOptionalAttributeValue(node, "expansion") != "doNotExpand"; if (fUsePersistentExpansion && m_mediator != null) // mediator null only in testing? { Expansion = DataTree.TreeItemState.ktisCollapsed; // Needs to be an expandable state to have ExpansionStateKey. fExpand = m_mediator.PropertyTable.GetBoolProperty(ExpansionStateKey, fExpand); } if (fExpand) { // Record the expansion state and generate the children. Expansion = DataTree.TreeItemState.ktisExpanded; CreateIndentedNodes(caller, obj, indent, ref insPos, path, reuseMap, node); } else { // Record expansion state and skip generating children. Expansion = DataTree.TreeItemState.ktisCollapsed; } } }
public override void GenerateChildren(XmlNode node, XmlNode caller, ICmObject obj, int indent, ref int insPos, ArrayList path, ObjSeqHashMap reuseMap, bool fUsePersistentExpansion) { CheckDisposed(); // If node has children, figure what to do with them... // It's important to initialize m_refs here rather than in FinishInit, because we need it // to be updated when the slice is reused in a regenerate. // Refactor JohnT: better still, make it a virtual attribute, and Refresh will automatically // clear it from the cache. SetRefs(); if (m_refs.Count == 0) { // It could have children but currently can't: we always show this as collapsedEmpty. Expansion = DataTree.TreeItemState.ktisCollapsedEmpty; return; } for (int i = 0; i < m_refs.Count; i++) { GenerateChildNode(i, node, caller, indent, ref insPos, path, reuseMap); } Expansion = DataTree.TreeItemState.ktisExpanded; }
public override void GenerateChildren(XmlNode node, XmlNode caller, ICmObject obj, int indent, ref int insPos, ArrayList path, ObjSeqHashMap reuseMap, bool fUsePersistentExpansion) { CheckDisposed(); foreach (IRnRoledPartic roledPartic in Record.ParticipantsOC) { if (roledPartic.RoleRA != null) GenerateChildNode(roledPartic, node, caller, indent, ref insPos, path, reuseMap); } Expansion = Record.ParticipantsOC.Count == 0 ? DataTree.TreeItemState.ktisCollapsedEmpty : DataTree.TreeItemState.ktisExpanded; }
/// <summary> /// Apply a layout to an object, producing the specified slices. /// </summary> /// <param name="obj">The object we want a detai view of</param> /// <param name="template">the 'layout' element</param> /// <param name="indent">How deeply indented the tree is at this point.</param> /// <param name="insertPosition">index in slices where we should insert nodes</param> /// <param name="path">sequence of nodes and HVOs inside which this is nested</param> /// <param name="reuseMap">map of key/slice combinations from a DataTree being refreshed. Exact matches may be /// reused, and also, the expansion state of exact matches is preserved.</param> /// <returns> updated insertPosition for next item after the ones inserted.</returns> public int ApplyLayout(ICmObject obj, XmlNode template, int indent, int insertPosition, ArrayList path, ObjSeqHashMap reuseMap) { CheckDisposed(); NodeTestResult ntr; return ApplyLayout(obj, template, indent, insertPosition, path, reuseMap, false, out ntr); }
/// <summary> /// Look for a reusable slice that matches the current path. If found, remove from map and return; /// otherwise, return null. /// </summary> private Slice GetMatchingSlice(ArrayList path, ObjSeqHashMap reuseMap) { // Review JohnT(RandyR): I don't see how this can really work. // The original path (the key) used to set this does not, (and cannot) change, // but it is very common for slices to come and go, as they are inserted/deleted, // or when the Show hidden control is changed. // Those kinds of big changes will produce the input 'path' parm, // which has little hope of matching that fixed orginal key, won't it. // I can see how it would work when a simple F4 refresh is being done, // since the count of slcies should remain the same. IList list = reuseMap[(IList)path]; if (list.Count > 0) { Slice slice = (Slice)list[0]; reuseMap.Remove(path, slice); return slice; } return null; }
/// <summary> /// Create slices for the specified object by finding a relevant template in the spec. /// </summary> /// <param name="obj"> The object to make slices for.</param> /// <param name="mode">If not null, use only templates having this mode.</param> /// <param name="level">level at which to put the new top-level slices</param> /// <param name="preceding">index after which to insert the new slice(s) in slices array.</param> /// <param name="path">sequence of nodes and HVOs inside which this is nested</param> /// <param name="reuseMap">map of key/slice combinations from a DataTree being refreshed. Exact matches may be /// reused, and also, the expansion state of exact matches is preserved.</param> /// <param name="unifyWith">If not null, this is a node to be 'unified' with the one looked up /// using the layout name.</param> /// <returns> updated insertPosition for next item after the ones inserted.</returns> public virtual int CreateSlicesFor(ICmObject obj, string layoutName, int indent, int insertPosition, ArrayList path, ObjSeqHashMap reuseMap, XmlNode unifyWith) { CheckDisposed(); // NB: 'path' can hold either ints or XmlNodes, so a generic can't be used for it. if (obj == null) return insertPosition; XmlNode template = GetTemplateForObjLayout(obj, layoutName); path.Add(template); XmlNode template2 = template; if (unifyWith != null && unifyWith.ChildNodes.Count > 0) { // This assumes that the attributes don't need to be unified. template2 = m_layoutInventory.GetUnified(template, unifyWith); } insertPosition = ApplyLayout(obj, template2, indent, insertPosition, path, reuseMap); path.RemoveAt(path.Count - 1); return insertPosition; }
/// <summary> /// Create slices appropriate for current root object and layout, reusing any existing slices, /// and clearing out any that remain unused. /// </summary> private void CreateSlices() { m_currentSlice = null; ObjSeqHashMap previousSlices = new ObjSeqHashMap(); List<Slice> dummySlices = new List<Slice>(Controls.Count); foreach (Slice slice in Controls) { if (slice.Key != null) // dummy slices may not have keys and shouldn't be reused. previousSlices.Add(slice.Key, slice); else dummySlices.Add(slice); } // Get rid of the dummies we aren't going to remove. foreach (Slice slice in dummySlices) RemoveSlice(slice); CreateSlicesFor(m_root, m_rootLayoutName, 0, 0, new ArrayList(20), previousSlices, null); // Clear out any slices NOT reused. Removing them from the collection both // removes them from the DataTree's controls collection and disposes them. foreach (IList sliceList in previousSlices.Values) { foreach (Slice gonner in sliceList) { RemoveSlice(gonner); } } ResetTabIndices(0); }
/// <summary> /// Tests to see if it should add the field (IfData), then adds the field. /// </summary> /// <param name="path">The path.</param> /// <param name="node">The node.</param> /// <param name="reuseMap">The reuse map.</param> /// <param name="editor">Type of Contained Data</param> /// <param name="flid">Field ID</param> /// <param name="obj">The obj.</param> /// <param name="parentSlice">The parent slice.</param> /// <param name="indent">The indent.</param> /// <param name="insPos">The ins pos.</param> /// <param name="fTestOnly">if set to <c>true</c> [f test only].</param> /// <param name="fVisIfData">IfData</param> /// <param name="caller">The caller.</param> /// <returns> /// NodeTestResult, an enum showing if usable data is contained in the field /// </returns> private NodeTestResult AddSimpleNode(ArrayList path, XmlNode node, ObjSeqHashMap reuseMap, string editor, int flid, ICmObject obj, Slice parentSlice, int indent, ref int insPos, bool fTestOnly, bool fVisIfData, XmlNode caller) { var realSda = m_cache.DomainDataByFlid; if (parentSlice != null) Debug.Assert(!parentSlice.IsDisposed, "AddSimpleNode parameter 'parentSlice' is Disposed!"); IWritingSystemContainer wsContainer = m_cache.ServiceLocator.WritingSystems; if (fVisIfData) // Contains the tests to see if usable data is inside the field (for all types of fields) { if (editor != null && editor == "custom") { Type typeFound; System.Reflection.MethodInfo mi = XmlUtils.GetStaticMethod(node, "assemblyPath", "class", "ShowSliceForVisibleIfData", out typeFound); if (mi != null) { var parameters = new object[2]; parameters[0] = node; parameters[1] = obj; object result = mi.Invoke(typeFound, System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic, null, parameters, null); if (!(bool)result) return NodeTestResult.kntrNothing; } } else if (flid == 0 && editor != null && editor == "autocustom") { flid = SliceFactory.GetCustomFieldFlid(caller, realSda.MetaDataCache, obj); } if (flid != 0) { var fieldType = (CellarPropertyType)(realSda.MetaDataCache.GetFieldType(flid) & (int)CellarPropertyTypeFilter.VirtualMask); switch (fieldType) { default: // if we don't know how to check, make it visible. break; // These cases are a bit tricky. We're duplicating some information here about how the slices // interpret their ws parameter. Don't see how to avoid it, though, without creating the slices even if not needed. case CellarPropertyType.MultiString: case CellarPropertyType.MultiUnicode: string ws = XmlUtils.GetOptionalAttributeValue(node, "ws", null); switch (ws) { case "vernacular": if (realSda.get_MultiStringAlt(obj.Hvo, flid, wsContainer.DefaultVernacularWritingSystem.Handle).Length == 0) return NodeTestResult.kntrNothing; break; case "analysis": if (realSda.get_MultiStringAlt(obj.Hvo, flid, wsContainer.DefaultAnalysisWritingSystem.Handle).Length == 0) return NodeTestResult.kntrNothing; break; default: if (editor == "jtview") { if (realSda.get_MultiStringAlt(obj.Hvo, flid, wsContainer.DefaultAnalysisWritingSystem.Handle).Length == 0) return NodeTestResult.kntrNothing; } // try one of the magic ones for multistring int wsMagic = WritingSystemServices.GetMagicWsIdFromName(ws); if (wsMagic == 0 && editor == "autocustom") { wsMagic = realSda.MetaDataCache.GetFieldWs(flid); } if (wsMagic == 0 && editor != "autocustom") break; // not recognized, treat as visible var rgws = WritingSystemServices.GetWritingSystemList(m_cache, wsMagic, false).ToArray(); bool anyNonEmpty = false; foreach (IWritingSystem wsInst in rgws) { if (realSda.get_MultiStringAlt(obj.Hvo, flid, wsInst.Handle).Length != 0) { anyNonEmpty = true; break; } } if (!anyNonEmpty) return NodeTestResult.kntrNothing; break; } break; case CellarPropertyType.String: if (realSda.get_StringProp(obj.Hvo, flid).Length == 0) return NodeTestResult.kntrNothing; break; case CellarPropertyType.Unicode: string val = realSda.get_UnicodeProp(obj.Hvo, flid); if (string.IsNullOrEmpty(val)) return NodeTestResult.kntrNothing; break; // Usually, the header nodes for sequences and atomic object props // have no editor. But sometimes they may have a jtview summary // or the like. If an object-prop flid is specified, check it, // in case we want to suppress the whole header. case CellarPropertyType.OwningAtomic: case CellarPropertyType.ReferenceAtomic: int hvoT = realSda.get_ObjectProp(obj.Hvo, flid); if (hvoT == 0) return NodeTestResult.kntrNothing; var objt = m_cache.ServiceLocator.GetInstance<ICmObjectRepository>().GetObject(hvoT); if (objt.ClassID == StTextTags.kClassId) // if clid is an sttext clid { var txt = (IStText) objt; // Test if the StText has only one paragraph int cpara = txt.ParagraphsOS.Count; if (cpara == 1) { // Tests if paragraph is empty ITsString tss = ((IStTxtPara) txt.ParagraphsOS[0]).Contents; if (tss == null || tss.Length == 0) return NodeTestResult.kntrNothing; } } break; case CellarPropertyType.ReferenceCollection: // Currently this special case is only needed for ReferenceCollection (specifically for PublishIn). // We can broaden it if necessary, but why take the time to look for it elsewhere? int visibilityFlid = flid; string visField = XmlUtils.GetOptionalAttributeValue(node, "visField"); if (visField != null) { int clsid = Cache.MetaDataCacheAccessor.GetOwnClsId(flid); visibilityFlid = Cache.MetaDataCacheAccessor.GetFieldId2(clsid, visField, true); } if (realSda.get_VecSize(obj.Hvo, visibilityFlid) == 0) return NodeTestResult.kntrNothing; break; case CellarPropertyType.OwningCollection: case CellarPropertyType.OwningSequence: case CellarPropertyType.ReferenceSequence: if (realSda.get_VecSize(obj.Hvo, flid) == 0) return NodeTestResult.kntrNothing; break; } } else if (editor == null) { // may be a summary node for a sequence or atomic node. Suppress it as well as the prop. XmlNode child = null; int cnodes = 0; foreach (XmlNode n in node.ChildNodes) { if (node is XmlComment) continue; cnodes++; if (cnodes > 1) break; child = n; } if (child != null && cnodes == 1) // exactly one non-comment child { int flidChild = GetFlidFromNode(child, obj); // If it's an obj or seq node and the property is empty, we'll show nothing. if (flidChild != 0) { if ((child.Name == "seq" || child.Name == "obj") && realSda.get_VecSize(obj.Hvo, flidChild) == 0) { return NodeTestResult.kntrNothing; } } } } } if (fTestOnly) return NodeTestResult.kntrSomething; // slices always produce something. path.Add(node); Slice slice = GetMatchingSlice(path, reuseMap); if (slice == null) { slice = SliceFactory.Create(m_cache, editor, flid, node, obj, StringTbl, PersistenceProvder, m_mediator, caller, reuseMap); if (slice == null) { // One way this can happen in TestLangProj is with a part ref for a custom field that // has been deleted. return NodeTestResult.kntrNothing; } Debug.Assert(slice != null); // Set the label and abbreviation (in that order...abbr defaults to label if not given if (slice.Label == null) slice.Label = GetLabel(caller, node, obj, "label"); slice.Abbreviation = GetLabelAbbr(caller, node, obj, slice.Label, "abbr"); // Install new item at appropriate position and level. slice.Indent = indent; slice.Object = obj; slice.Cache = m_cache; slice.StringTbl = StringTbl; slice.PersistenceProvider = PersistenceProvder; // We need a copy since we continue to modify path, so make it as compact as possible. slice.Key = path.ToArray(); // old code just set mediator, nothing ever set m_configurationParams. Maybe the two are redundant and should merge? slice.Init(m_mediator, null); slice.ConfigurationNode = node; slice.CallerNode = caller; slice.OverrideBackColor(XmlUtils.GetOptionalAttributeValue(node, "backColor")); slice.ShowContextMenu += OnShowContextMenu; slice.SmallImages = SmallImages; SetNodeWeight(node, slice); slice.FinishInit(); // Now done in Slice.ctor //slice.Visible = false; // don't show it until we position and size it. InsertSliceAndRegisterWithContextHelp(insPos, slice); } else { // Now done in Slice.ctor //slice.Visible = false; // Since some slices are invisible, all must be, or Show() will reorder them. EnsureValidIndexForReusedSlice(slice, insPos); } slice.ParentSlice = parentSlice; insPos++; slice.GenerateChildren(node, caller, obj, indent, ref insPos, path, reuseMap, true); path.RemoveAt(path.Count - 1); return NodeTestResult.kntrNothing; // arbitrary what we return if not testing (see first line of method.) }
private NodeTestResult AddSeqNode(ArrayList path, XmlNode node, ObjSeqHashMap reuseMap, int flid, ICmObject obj, Slice parentSlice, int indent, ref int insertPosition, bool fTestOnly, string layoutName, bool fVisIfData, XmlNode caller) { if (flid == 0) throw new ApplicationException("field attribute required for seq properties " + node.OuterXml); int cobj = m_cache.DomainDataByFlid.get_VecSize(obj.Hvo, flid); // monitor it even if we're testing: result may change. m_monitoredProps.Add(Tuple.Create(obj.Hvo, flid)); if (fVisIfData && cobj == 0) return NodeTestResult.kntrNothing; if (fTestOnly) { if (cobj > 0 || XmlUtils.GetOptionalAttributeValue(node, "ghost") != null) return NodeTestResult.kntrSomething; return NodeTestResult.kntrPossible; } path.Add(node); string layoutOverride = XmlUtils.GetOptionalAttributeValue(node, "layout", layoutName); string layoutChoiceField = XmlUtils.GetOptionalAttributeValue(node, "layoutChoiceField"); if (cobj == 0) { // Nothing in seq....do we want a ghost slice? if (XmlUtils.GetOptionalAttributeValue(node, "ghost") != null) { MakeGhostSlice(path, node, reuseMap, obj, parentSlice, flid, caller, indent, ref insertPosition); } } else if (cobj < kInstantSliceMax || // This may be a little on the small side m_currentObjectFlids.Contains(flid) || (!String.IsNullOrEmpty(m_currentSlicePartName) && m_currentSliceObjGuid != Guid.Empty && m_currentSliceNew == null)) { //Create slices immediately var contents = SetupContents(flid, obj); foreach (int hvo in contents) { path.Add(hvo); insertPosition = CreateSlicesFor(m_cache.ServiceLocator.GetInstance<ICmObjectRepository>().GetObject(hvo), parentSlice, layoutOverride, layoutChoiceField, indent, insertPosition, path, reuseMap, caller); path.RemoveAt(path.Count - 1); } } else { // Create unique DummyObjectSlices for each slice. This may reduce the initial // preceived benefit, but this way doesn't crash now that the slices are being // disposed of. int cnt = 0; var contents = SetupContents(flid, obj); foreach (int hvo in contents) { // TODO (DamienD): do we need to add the layout choice field to the monitored props for a dummy slice? // LT-12302 exposed a path through here that was messed up when hvo was added before Dummy slices //path.Add(hvo); // try putting this AFTER the dos creation var dos = new DummyObjectSlice(indent, node, (ArrayList)(path.Clone()), obj, flid, cnt, layoutOverride, layoutChoiceField, caller) {Cache = m_cache, ParentSlice = parentSlice}; path.Add(hvo); // This is really important. Since some slices are invisible, all must be, // or Show() will reorder them. dos.Visible = false; InsertSlice(insertPosition++, dos); path.RemoveAt(path.Count - 1); cnt++; } } path.RemoveAt(path.Count - 1); return NodeTestResult.kntrNothing; }
/// <summary> /// Tests to see if it should add the field (IfData), then adds the field. /// </summary> /// <param name="path"></param> /// <param name="node"></param> /// <param name="reuseMap"></param> /// <param name="editor">Type of Contained Data</param> /// <param name="flid">Field ID</param> /// <param name="obj"></param> /// <param name="indent"></param> /// <param name="insPos"></param> /// <param name="fTestOnly"></param> /// <param name="fVisIfData">IfData</param> /// <param name="caller"></param> /// <returns>NodeTestResult, an enum showing if usable data is contained in the field</returns> private NodeTestResult AddSimpleNode(ArrayList path, XmlNode node, ObjSeqHashMap reuseMap, string editor, int flid, ICmObject obj, int indent, ref int insPos, bool fTestOnly, bool fVisIfData, XmlNode caller) { if (fVisIfData) // Contains the tests to see if usable data is inside the field (for all types of fields) { if (editor != null && editor == "custom") { System.Type typeFound; System.Reflection.MethodInfo mi = XmlUtils.GetStaticMethod(node, "assemblyPath", "class", "ShowSliceForVisibleIfData", out typeFound); if (mi != null) { object[] parameters = new object[2]; parameters[0] = (object)node; parameters[1] = (object)obj; object result = mi.Invoke(typeFound, System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic, null, parameters, null); if (!(bool)result) return NodeTestResult.kntrNothing; } } else if (flid == 0 && editor != null && editor == "autocustom") { flid = SliceFactory.GetCustomFieldFlid(caller, m_cache.MetaDataCacheAccessor, obj); } if (flid != 0) { FieldType fieldType = m_cache.GetFieldType(flid); fieldType &= FieldType.kcptVirtualMask; // strip virtual bit. switch (fieldType) { default: // if we don't know how to check, make it visible. break; // These cases are a bit tricky. We're duplicating some information here about how the slices // interpret their ws parameter. Don't see how to avoid it, though, without creating the slices even if not needed. case FieldType.kcptMultiString: case FieldType.kcptMultiUnicode: case FieldType.kcptMultiBigString: case FieldType.kcptMultiBigUnicode: string ws = XmlUtils.GetOptionalAttributeValue(node, "ws", null); switch(ws) { case "vernacular": if (m_cache.MainCacheAccessor.get_MultiStringAlt(obj.Hvo, flid, m_cache.DefaultVernWs).Length == 0) return NodeTestResult.kntrNothing; break; case "analysis": if (m_cache.MainCacheAccessor.get_MultiStringAlt(obj.Hvo, flid, m_cache.DefaultAnalWs).Length == 0) return NodeTestResult.kntrNothing; break; default: if (editor == "jtview") { if (m_cache.MainCacheAccessor.get_MultiStringAlt(obj.Hvo, flid, m_cache.DefaultAnalWs).Length == 0) return NodeTestResult.kntrNothing; } // try one of the magic ones for multistring int wsMagic = LangProject.GetMagicWsIdFromName(ws); if (wsMagic == 0 && editor == "autocustom") { wsMagic = m_cache.MetaDataCacheAccessor.GetFieldWs((uint)flid); } if (wsMagic == 0 && editor != "autocustom") break; // not recognized, treat as visible ILgWritingSystem[] rgws = SIL.FieldWorks.Common.Widgets.LabeledMultiStringView.GetWritingSystemList(m_cache, wsMagic, false); bool anyNonEmpty = false; foreach (ILgWritingSystem wsInst in rgws) { if (m_cache.MainCacheAccessor.get_MultiStringAlt(obj.Hvo, flid, wsInst.Hvo).Length != 0) { anyNonEmpty = true; break; } } if (!anyNonEmpty) return NodeTestResult.kntrNothing; break; } break; case FieldType.kcptString: case FieldType.kcptBigString: if (m_cache.MainCacheAccessor.get_StringProp(obj.Hvo, flid).Length == 0) return NodeTestResult.kntrNothing; break; case FieldType.kcptUnicode: case FieldType.kcptBigUnicode: string val = m_cache.MainCacheAccessor.get_UnicodeProp(obj.Hvo, flid); if (val == null || val.Length == 0) return NodeTestResult.kntrNothing; break; // Usually, the header nodes for sequences and atomic object props // have no editor. But sometimes they may have a jtview summary // or the like. If an object-prop flid is specified, check it, // in case we want to suppress the whole header. case FieldType.kcptOwningAtom: case FieldType.kcptReferenceAtom: int hvoT = m_cache.MainCacheAccessor.get_ObjectProp(obj.Hvo, flid); if (hvoT == 0) return NodeTestResult.kntrNothing; int clid = m_cache.GetClassOfObject(hvoT); if (clid == (int)CellarModuleDefns.kclidStText) // if clid is an sttext clid { // Test if the StText has only one paragraph int cpara = m_cache.GetVectorSize(hvoT, (int)CellarModuleDefns.kflidStText_Paragraphs); if (cpara == 1) { // Tests if paragraph is empty int hvoPara = m_cache.GetVectorItem(hvoT, (int)CellarModuleDefns.kflidStText_Paragraphs, 0); if (hvoPara == 0) return NodeTestResult.kntrNothing; ITsString tss = m_cache.GetTsStringProperty(hvoPara, (int)CellarModuleDefns.kflidStTxtPara_Contents); if (tss == null || tss.Length == 0) return NodeTestResult.kntrNothing; } } break; case FieldType.kcptOwningCollection: case FieldType.kcptOwningSequence: case FieldType.kcptReferenceCollection: case FieldType.kcptReferenceSequence: if (m_cache.MainCacheAccessor.get_VecSize(obj.Hvo, flid) == 0) return NodeTestResult.kntrNothing; break; } } else if (editor == null) { // may be a summary node for a sequence or atomic node. Suppress it as well as the prop. XmlNode child = null; int cnodes = 0; foreach (XmlNode n in node.ChildNodes) { if (node.GetType() == typeof(XmlComment)) continue; cnodes++; if (cnodes > 1) break; child = n; } if (cnodes == 1) // exactly one non-comment child { int flidChild = GetFlidFromNode(child, obj); // If it's an obj or seq node and the property is empty, we'll show nothing. if (flidChild != 0 && child.Name == "seq" && m_cache.MainCacheAccessor.get_VecSize(obj.Hvo, flidChild) == 0) { return NodeTestResult.kntrNothing; } if (flidChild != 0 && child.Name == "obj" && m_cache.MainCacheAccessor.get_ObjectProp(obj.Hvo, flidChild) == 0) { return NodeTestResult.kntrNothing; } } } } if (fTestOnly) return NodeTestResult.kntrSomething; // slices always produce something. path.Add(node); Slice slice = GetMatchingSlice(path, reuseMap); if (slice == null) { slice = SliceFactory.Create(m_cache, editor, flid, node, obj, StringTbl, PersistenceProvder, m_mediator, caller); if (slice == null) { // One way this can happen in TestLangProj is with a part ref for a custom field that // has been deleted. return NodeTestResult.kntrNothing; } Debug.Assert(slice != null); // Set the label and abbreviation (in that order...abbr defaults to label if not given if (slice.Label == null) slice.Label = GetLabel(caller, node, obj, "label"); slice.Abbreviation = GetLabelAbbr(caller, node, obj, slice.Label, "abbr"); // Install new item at appropriate position and level. slice.Indent = indent; slice.Object = obj; slice.Cache = m_cache; slice.Mediator = m_mediator; // We need a copy since we continue to modify path, so make it as compact as possible. slice.Key = path.ToArray(); slice.ConfigurationNode = node; slice.CallerNode = caller; slice.OverrideBackColor(XmlUtils.GetOptionalAttributeValue(node, "backColor")); slice.ShowContextMenu += new TreeNodeEventHandler(this.OnShowContextMenu); slice.SmallImages = SmallImages; SetNodeWeight(node, slice); slice.FinishInit(); slice.Visible = false; // don't show it until we position and size it. InsertSliceAndRegisterWithContextHelp(insPos, slice, node); } else { EnsureValidIndexForReusedSlice(slice, insPos); } insPos++; slice.GenerateChildren(node, caller, obj, indent, ref insPos, path, reuseMap); path.RemoveAt(path.Count - 1); return NodeTestResult.kntrNothing; // arbitrary what we return if not testing (see first line of method.) }
/// <summary> /// Calls ApplyLayout for each child of the argument node. /// </summary> /// <param name="obj"></param> /// <param name="template"></param> /// <param name="indent"></param> /// <param name="insertPosition"></param> /// <param name="path"></param> /// <param name="reuseMap"></param> /// <returns></returns> public int ApplyChildren(ICmObject obj, XmlNode template, int indent, int insertPosition, ArrayList path, ObjSeqHashMap reuseMap) { CheckDisposed(); int insertPos = insertPosition; foreach (XmlNode node in template.ChildNodes) { if (node.Name == "ChangeRecordHandler") continue; // Handle only at the top level (at least for now). insertPos = ApplyLayout(obj, node, indent, insertPos, path, reuseMap); } return insertPos; }
/// <summary> /// This is the guts of ApplyLayout, but it has extra arguments to allow it to be used both to actually produce /// slices, and just to query whether any slices will be produced. /// </summary> /// <param name="obj"></param> /// <param name="template"></param> /// <param name="indent"></param> /// <param name="insertPosition"></param> /// <param name="path"></param> /// <param name="reuseMap"></param> /// <param name="fTestOnly"></param> /// <param name="fGotAnything"></param> /// <returns></returns> protected internal virtual int ApplyLayout(ICmObject obj, XmlNode template, int indent, int insertPosition, ArrayList path, ObjSeqHashMap reuseMap, bool isTestOnly, out NodeTestResult testResult) { int insPos = insertPosition; testResult = NodeTestResult.kntrNothing; int cPossible = 0; // This loop handles the multiple parts of a layout. foreach (XmlNode partRef in template.ChildNodes) { if (partRef.GetType() == typeof(XmlComment)) continue; // This code looks for the a special part definition with an attribute called "customFields" // It doesn't matter what this attribute is set to, as long as it exists. If this attribute is // found, the custom fields will not be generated. if(XmlUtils.GetOptionalAttributeValue(partRef, "customFields", null) != null) { if(!isTestOnly) EnsureCustomFields(obj, template, partRef); continue; } testResult = ProcessPartRefNode(partRef, path, reuseMap, obj, indent, ref insPos, isTestOnly); if (isTestOnly) { switch (testResult) { case NodeTestResult.kntrNothing: break; case NodeTestResult.kntrPossible: // nothing definite yet, but flag at least one possible. ++cPossible; break; default: // if we're just looking to see if there would be any slices, and // there was, then don't bother thinking about any more slices. return insertPosition; } } } if (cPossible > 0) testResult = NodeTestResult.kntrPossible; // everything else was nothing... //TODO: currently, we are making a custom fields show up all over the place... i.e., // the initial algorithm here (show the custom fields for a class whenever we are applying a template of that class) // has turned out to be too simplistic, since apparently we and templates of a given class multiple times // to show different parts of the class. // if(template.Name == "template") //if (fGenerateCustomFields) // testResult = AddCustomFields(obj, template, indent, ref insPos, path, reuseMap,isTestOnly); return insPos; }
private void GenerateChildNode(IRnRoledPartic roledPartic, XmlNode node, XmlNode caller, int indent, ref int insPos, ArrayList path, ObjSeqHashMap reuseMap) { var sliceElem = new XElement("slice", new XAttribute("label", roledPartic.RoleRA.Name.BestAnalysisAlternative.Text), new XAttribute("field", "Participants"), new XAttribute("editor", "possVectorReference"), new XAttribute("menu", "mnuDataTree-Participants")); foreach (XmlNode childNode in node.ChildNodes) { if (childNode.NodeType != XmlNodeType.Comment) sliceElem.Add(XElement.Parse(childNode.OuterXml)); } node.InnerXml = sliceElem.ToString(); CreateIndentedNodes(caller, roledPartic, indent, ref insPos, path, reuseMap, node); node.InnerXml = ""; }
/// <summary> /// Process a top-level child of a layout (other than a comment). /// Currently these are all part nodes (with ref= indicating the part to use). /// </summary> /// <param name="part"></param> /// <param name="path"></param> /// <param name="reuseMap"></param> /// <param name="obj"></param> /// <param name="indent"></param> /// <param name="insPos"></param> /// <param name="isTestOnly"></param> /// <returns>NodeTestResult</returns> private NodeTestResult ProcessPartRefNode(XmlNode partRef, ArrayList path, ObjSeqHashMap reuseMap, ICmObject obj, int indent, ref int insPos, bool isTestOnly) { // Use the part inventory to find the indicated part. Debug.Assert(partRef.Name == "part"); // If the previously selected slice doesn't display in this refresh, we try for the next // visible slice instead. So m_fSetCurrentSliceNew might still be set. See LT-9010. string partName = XmlUtils.GetManditoryAttributeValue(partRef, "ref"); if (!m_fSetCurrentSliceNew && m_currentSlicePartName != null && obj.Hvo == m_currentSliceObjHvo) { for (uint clid = (uint)obj.ClassID; clid != 0; clid = m_mdc.GetBaseClsId(clid)) { string sFullPartName = String.Format("{0}-Detail-{1}", m_mdc.GetClassName(clid), partName); if (m_currentSlicePartName == sFullPartName) { m_fSetCurrentSliceNew = true; break; } } } string visibility = "always"; if (!m_fShowAllFields) { visibility = XmlUtils.GetOptionalAttributeValue(partRef, "visibility", "always"); if (visibility == "never") return NodeTestResult.kntrNothing; Debug.Assert(visibility == "always" || visibility == "ifdata"); } uint classId = (uint)obj.ClassID; string classname; XmlNode part = null; for( ; ; ) { classname = m_mdc.GetClassName(classId); // Inventory of parts has key ID. The ID is made up of the class name, "-Detail-", partname. string key = classname + "-Detail-"+partName; part = m_partInventory.GetElement("part", new string[] {key}); int temp = 0; if (part != null) temp = part.GetHashCode(); if (part != null) break; if (classId == 0) // we've just tried CmObject. { Debug.WriteLine("Warning: No matching part found for " + classname + "-Detail-" + partName); // Just omit the missing part. return NodeTestResult.kntrNothing; } // Otherwise try superclass. classId = m_mdc.GetBaseClsId(classId); } string parameter = XmlUtils.GetOptionalAttributeValue(partRef, "param", null); // If you are wondering why we put the partref in the key, one reason is that it may be needed // when expanding a collapsed slice. path.Add(partRef); NodeTestResult ntr = ProcessPartChildren(part, path, reuseMap, obj, indent, ref insPos, isTestOnly, parameter, visibility=="ifdata", partRef); path.RemoveAt(path.Count - 1); return ntr; }
public void CreateIndentedNodes_basic() { m_DataTree = new DataTree(); m_Slice = GenerateSlice(Cache, m_DataTree); // Data taken from a running Sena 3 var caller = CreateXmlElementFromOuterXmlOf("<part ref=\"AsLexemeForm\" label=\"Lexeme Form\" expansion=\"expanded\"><indent><part ref=\"IsAbstractBasic\" label=\"Is Abstract Form\" visibility=\"never\" /><!-- could use 'ifTrue' if we had it --><part ref=\"MorphTypeBasic\" visibility=\"ifdata\" /><part ref=\"PhoneEnvBasic\" visibility=\"ifdata\" /><part ref=\"StemNameForLexemeForm\" visibility=\"ifdata\" /></indent></part>"); var obj = Cache.ServiceLocator.GetInstance<IMoStemAllomorphFactory>().Create(); const int indent = 0; int insPos = 1; var path = GeneratePath(); var reuseMap = new ObjSeqHashMap(); // Data taken from a running Sena 3 var node = CreateXmlElementFromOuterXmlOf("<slice field=\"Form\" label=\"Form\" editor=\"multistring\" ws=\"all vernacular\" weight=\"light\" menu=\"mnuDataTree-LexemeForm\" contextMenu=\"mnuDataTree-LexemeFormContext\" spell=\"no\"><properties><bold value=\"on\" /><fontsize value=\"120%\" /></properties></slice>"); m_Slice.CreateIndentedNodes(caller, obj, indent, ref insPos, path, reuseMap, node); }
internal NodeTestResult ProcessPartChildren(XmlNode part, ArrayList path, ObjSeqHashMap reuseMap, ICmObject obj, int indent, ref int insPos, bool isTestOnly, string parameter, bool fVisIfData, XmlNode caller) { CheckDisposed(); // The children of the part element must now be processed. Often there is only one. foreach (XmlNode node in part.ChildNodes) { if (node.GetType() == typeof(XmlComment)) continue; NodeTestResult testResult = ProcessSubpartNode(node, path, reuseMap, obj, indent, ref insPos, isTestOnly, parameter, fVisIfData, caller); // If we're just looking to see if there would be any slices, and there was, // then don't bother thinking about any more slices. if (isTestOnly && testResult != NodeTestResult.kntrNothing) return testResult; } return NodeTestResult.kntrNothing; // valid if isTestOnly, otherwise don't care. }
/// <summary></summary> public virtual void CreateIndentedNodes(XmlNode caller, ICmObject obj, int indent, ref int insPos, ArrayList path, ObjSeqHashMap reuseMap, XmlNode node) { CheckDisposed(); string parameter = null; if (caller != null) parameter = XmlUtils.GetOptionalAttributeValue(caller, "param"); XmlNode indentNode = null; if (caller != null) indentNode = caller.SelectSingleNode("indent"); if (indentNode != null) { DataTree.NodeTestResult ntr; insPos = ContainingDataTree.ApplyLayout(obj, this, indentNode, indent + ExtraIndent(indentNode), insPos, path, reuseMap, false, out ntr); } else ContainingDataTree.ProcessPartChildren(node, path, reuseMap, obj, this, indent + ExtraIndent(node), ref insPos, false, parameter, false, caller); }
/// <summary> /// Handle one (non-comment) child node of a template (or other node) being used to /// create slices. Update insertPosition to indicate how many were added (it also /// specifies where to add). If fTestOnly is true, do not update insertPosition, just /// return true if any slices would be created. Note that this method is recursive /// indirectly through ProcessPartChildren(). /// </summary> /// <param name="node"></param> /// <param name="path"></param> /// <param name="reuseMap"></param> /// <param name="obj"></param> /// <param name="indent"></param> /// <param name="insertPosition"></param> /// <param name="fTestOnly"></param> /// <param name="parameter"></param> /// <param name="fVisIfData">If true, show slice only if data present.</param> /// <returns></returns> private NodeTestResult ProcessSubpartNode(XmlNode node, ArrayList path, ObjSeqHashMap reuseMap, ICmObject obj, int indent, ref int insertPosition, bool fTestOnly, string parameter, bool fVisIfData, XmlNode caller) { string editor = XmlUtils.GetOptionalAttributeValue(node, "editor"); try { if (editor != null) editor = editor.ToLower(); int flid = GetFlidFromNode(node, obj); if (m_sliceFilter != null && flid != 0 && !m_sliceFilter.IncludeSlice(node, obj, flid)) { return NodeTestResult.kntrNothing; } switch (node.Name) { default: break; // Nothing to do for unrecognized element, such as deParams. case "slice": return AddSimpleNode(path, node, reuseMap, editor, flid, obj, indent, ref insertPosition, fTestOnly, fVisIfData, caller); case "seq": return AddSeqNode(path, node, reuseMap, editor, flid, obj, indent + Slice.ExtraIndent(node), ref insertPosition, fTestOnly, parameter, fVisIfData, caller); case "obj": return AddAtomicNode(path, node, reuseMap, editor, flid, obj, indent + Slice.ExtraIndent(node), ref insertPosition, fTestOnly, parameter, fVisIfData, caller); case "if": if (XmlVc.ConditionPasses(node, obj.Hvo, m_cache)) { NodeTestResult ntr = ProcessPartChildren(node, path, reuseMap, obj, indent, ref insertPosition, fTestOnly, parameter, fVisIfData, caller); if (fTestOnly && ntr != NodeTestResult.kntrNothing) return ntr; } break; case "ifnot": if (!XmlVc.ConditionPasses(node, obj.Hvo, m_cache)) { NodeTestResult ntr = ProcessPartChildren(node, path, reuseMap, obj, indent, ref insertPosition, fTestOnly, parameter, fVisIfData, caller); if (fTestOnly && ntr != NodeTestResult.kntrNothing) return ntr; } break; case "choice": foreach (XmlNode clause in node.ChildNodes) { if (clause.Name == "where") { if (XmlVc.ConditionPasses(clause, obj.Hvo, m_cache)) { NodeTestResult ntr = ProcessPartChildren(clause, path, reuseMap, obj, indent, ref insertPosition, fTestOnly, parameter, fVisIfData, caller); if (fTestOnly && ntr != NodeTestResult.kntrNothing) return ntr; break; } // Allow multiple where elements to be processed, but expand only // the first one whose condition passes. } else if (clause.Name == "otherwise") { // enhance: verify last node? NodeTestResult ntr = ProcessPartChildren(clause, path, reuseMap, obj, indent, ref insertPosition, fTestOnly, parameter, fVisIfData, caller); if (fTestOnly && ntr != NodeTestResult.kntrNothing) return ntr; break; } else { throw new Exception( "elements in choice must be <where...> or <otherwise>."); } } break; case "RecordChangeHandler": // No, since it isn't owned by the data tree, even though it created it. //if (m_rch != null && m_rch is IDisposable) // (m_rch as IDisposable).Dispose(); if (m_rch != null && !m_rch.HasRecordListUpdater && m_rch is IDisposable) { // The above version of the Dispose call was bad, // when m_rlu 'owned' the m_rch. // Now, we know there is no 'owning' m_rlu, so we have to do it. (m_rch as IDisposable).Dispose(); m_rch = null; } m_rch = (IRecordChangeHandler)DynamicLoader.CreateObject(node, null); m_rch.Disposed += new EventHandler(m_rch_Disposed); Debug.Assert(m_rch != null); m_listName = XmlUtils.GetOptionalAttributeValue(node, "listName"); m_rlu = null; ResetRecordListUpdater(); // m_rlu may still be null, but that appears to be just fine. m_rch.Setup(obj, m_rlu); return NodeTestResult.kntrNothing; } } catch (Exception error) { // This doesn't need to be localized because it's displayed in a "yellow box" // error report. string s = "FieldWorks ran into a problem trying to display this object"; s += " in DataTree::ApplyLayout: " + error.Message; s += "\r\nThe object id was " + obj.Hvo.ToString() + "."; if (editor != null) s += " The editor was '" + editor + "'.\r\n"; s += " The text of the current node was " + node.OuterXml; //now send it on throw new ApplicationException(s, error); } // other types of child nodes, for example, parameters for jtview, don't even have // the potential for expansion. return NodeTestResult.kntrNothing; }
public static Slice Create(FdoCache cache, string editor, int flid, XmlNode node, ICmObject obj, StringTable stringTbl, IPersistenceProvider persistenceProvider, Mediator mediator, XmlNode caller, ObjSeqHashMap reuseMap) { Slice slice; switch(editor) { case "multistring": // first, these are the most common slices. { if (flid == 0) throw new ApplicationException("field attribute required for multistring " + node.OuterXml); string wsSpec = XmlUtils.GetOptionalAttributeValue(node, "ws"); int wsMagic = WritingSystemServices.GetMagicWsIdFromName(wsSpec); if (wsMagic == 0) throw new ApplicationException( "ws must be 'all vernacular', 'all analysis', 'analysis vernacular', or 'vernacular analysis'" + " it said '" + wsSpec + "'."); bool forceIncludeEnglish = XmlUtils.GetOptionalBooleanAttributeValue(node, "forceIncludeEnglish", false); bool spellCheck = XmlUtils.GetOptionalBooleanAttributeValue(node, "spell", true); // Either the part or the caller can specify that it isn't editable. // (The part may 'know' this, e.g. because it's a virtual attr not capable of editing; // more commonly the caller knows there isn't enough context for safe editing. bool editable = XmlUtils.GetOptionalBooleanAttributeValue(caller, "editable", true) && XmlUtils.GetOptionalBooleanAttributeValue(node, "editable", true); string optionalWsSpec = XmlUtils.GetOptionalAttributeValue(node, "optionalWs"); int wsMagicOptional = WritingSystemServices.GetMagicWsIdFromName(optionalWsSpec); MultiStringSlice msSlice = reuseMap.GetSliceToReuse("MultiStringSlice") as MultiStringSlice; if (msSlice == null) slice = new MultiStringSlice(obj, flid, wsMagic, wsMagicOptional, forceIncludeEnglish, editable, spellCheck); else { slice = msSlice; msSlice.Reuse(obj, flid, wsMagic, wsMagicOptional, forceIncludeEnglish, editable, spellCheck); } break; } case "defaultvectorreference": // second most common. { var rvSlice = reuseMap.GetSliceToReuse("ReferenceVectorSlice") as ReferenceVectorSlice; if (rvSlice == null) slice = new ReferenceVectorSlice(cache, obj, flid); else { slice = rvSlice; rvSlice.Reuse(obj, flid); } break; } case "possvectorreference": { var prvSlice = reuseMap.GetSliceToReuse("PossibilityReferenceVectorSlice") as PossibilityReferenceVectorSlice; if (prvSlice == null) slice = new PossibilityReferenceVectorSlice(cache, obj, flid); else { slice = prvSlice; prvSlice.Reuse(obj, flid); } break; } case "semdomvectorreference": { var prvSlice = reuseMap.GetSliceToReuse("SemanticDomainReferenceVectorSlice") as SemanticDomainReferenceVectorSlice; if (prvSlice == null) slice = new SemanticDomainReferenceVectorSlice(cache, obj, flid); else { slice = prvSlice; prvSlice.Reuse(obj, flid); } break; } case "string": { if (flid == 0) throw new ApplicationException("field attribute required for basic properties " + node.OuterXml); int ws = GetWs(mediator, cache, node); if (ws != 0) slice = new StringSlice(obj, flid, ws); else slice = new StringSlice(obj, flid); var fShowWsLabel = XmlUtils.GetOptionalBooleanAttributeValue(node, "labelws", false); if (fShowWsLabel) (slice as StringSlice).ShowWsLabel = true; int wsEmpty = GetWs(mediator, cache, node, "wsempty"); if (wsEmpty != 0) (slice as StringSlice).DefaultWs = wsEmpty; break; } case "jtview": { string layout = XmlUtils.GetOptionalAttributeValue(caller, "param"); if (layout == null) layout = XmlUtils.GetManditoryAttributeValue(node, "layout"); // Editable if BOTH the caller (part ref) AND the node itself (the slice) say so...or at least if neither says not. bool editable = XmlUtils.GetOptionalBooleanAttributeValue(caller, "editable", true) && XmlUtils.GetOptionalBooleanAttributeValue(node, "editable", true); slice = new ViewSlice(new XmlView(obj.Hvo, layout, stringTbl, editable)); break; } case "summary": { slice = new SummarySlice(); break; } case "enumcombobox": { slice = new EnumComboSlice(cache, obj, flid, stringTbl, node["deParams"]); break; } case "referencecombobox": { slice = new ReferenceComboBoxSlice(cache, obj, flid, persistenceProvider); break; } case "typeaheadrefatomic": { slice = new AtomicRefTypeAheadSlice(obj, flid); break; } case "msareferencecombobox": { slice = new MSAReferenceComboBoxSlice(cache, obj, flid, persistenceProvider); break; } case "lit": // was "message" { string message = XmlUtils.GetManditoryAttributeValue(node, "message"); if (stringTbl != null) { string sTranslate = XmlUtils.GetOptionalAttributeValue(node, "translate", ""); if (sTranslate.Trim().ToLower() != "do not translate") message = stringTbl.LocalizeLiteralValue(message); } slice = new MessageSlice(message); break; } case "picture": { slice = new PictureSlice((ICmPicture)obj); break; } case "image": { try { slice = new ImageSlice(FwDirectoryFinder.CodeDirectory, XmlUtils.GetManditoryAttributeValue(node, "param1")); } catch (Exception error) { slice = new MessageSlice(String.Format(DetailControlsStrings.ksImageSliceFailed, error.Message)); } break; } case "checkbox": { slice = new CheckboxSlice(cache, obj, flid, node); break; } case "checkboxwithrefresh": { slice = new CheckboxRefreshSlice(cache, obj, flid, node); break; } case "time": { slice = new DateSlice(cache, obj, flid); break; } case "integer": // produced in the auto-generated parts from the conceptual model case "int": // was "integer" { slice = new IntegerSlice(cache, obj, flid); break; } case "gendate": { slice = new GenDateSlice(cache, obj, flid); break; } case "morphtypeatomicreference": { slice = new MorphTypeAtomicReferenceSlice(cache, obj, flid); break; } case "atomicreferencepos": { slice = new AtomicReferencePOSSlice(cache, obj, flid, persistenceProvider, mediator); break; } case "possatomicreference": { slice = new PossibilityAtomicReferenceSlice(cache, obj, flid); break; } case "atomicreferenceposdisabled": { slice = new AutomicReferencePOSDisabledSlice(cache, obj, flid, persistenceProvider, mediator); break; } case "defaultatomicreference": { slice = new AtomicReferenceSlice(cache, obj, flid); break; } case "defaultatomicreferencedisabled": { slice = new AtomicReferenceDisabledSlice(cache, obj, flid); break; } case "derivmsareference": { slice = new DerivMSAReferenceSlice(cache, obj, flid); break; } case "inflmsareference": { slice = new InflMSAReferenceSlice(cache, obj, flid); break; } case "phoneenvreference": { slice = new PhoneEnvReferenceSlice(cache, obj, flid); break; } case "sttext": { slice = new StTextSlice(obj, flid, GetWs(mediator, cache, node)); break; } case "custom": { slice = (Slice)DynamicLoader.CreateObject(node); break; } case "customwithparams": { slice = (Slice)DynamicLoader.CreateObject(node, new object[]{cache, editor, flid, node, obj, stringTbl, persistenceProvider, GetWs(mediator, cache, node)}); break; } case "ghostvector": { slice = new GhostReferenceVectorSlice(cache, obj, node); break; } case "command": { slice = new CommandSlice(node["deParams"]); break; } case null: //grouping nodes do not necessarily have any editor { slice = new Slice(); break; } case "message": // case "integer": // added back in to behave as "int" above throw new Exception("use of obsolete editor type (message->lit, integer->int)"); case "autocustom": slice = MakeAutoCustomSlice(cache, obj, caller); if (slice == null) return null; break; case "defaultvectorreferencedisabled": // second most common. { ReferenceVectorDisabledSlice rvSlice = reuseMap.GetSliceToReuse("ReferenceVectorDisabledSlice") as ReferenceVectorDisabledSlice; if (rvSlice == null) slice = new ReferenceVectorDisabledSlice(cache, obj, flid); else { slice = rvSlice; rvSlice.Reuse(obj, flid); } break; } default: { //Since the editor has not been implemented yet, //is there a bitmap file that we can show for this editor? //Such bitmaps belong in the distFiles xde directory string fwCodeDir = FwDirectoryFinder.CodeDirectory; string editorBitmapRelativePath = "xde/" + editor + ".bmp"; if(File.Exists(Path.Combine(fwCodeDir, editorBitmapRelativePath))) slice = new ImageSlice(fwCodeDir, editorBitmapRelativePath); else slice = new MessageSlice(String.Format(DetailControlsStrings.ksBadEditorType, editor)); break; } } slice.AccessibleName = editor; return slice; }
private NodeTestResult AddAtomicNode(ArrayList path, XmlNode node, ObjSeqHashMap reuseMap, string editor, int flid, ICmObject obj, int indent, ref int insertPosition, bool fTestOnly, string layoutName, bool fVisIfData, XmlNode caller) { // Facilitate insertion of an expandable tree node representing an owned or ref'd object. if (flid == 0) throw new ApplicationException("field attribute required for atomic properties " + node.OuterXml); ICmObject innerObj = obj.GetObjectInAtomicField(flid); m_monitoredProps.Add(new KeyValuePair<int, int>(obj.Hvo, flid)); if (fVisIfData && innerObj == null) return NodeTestResult.kntrNothing; if (fTestOnly) { if (innerObj != null || XmlUtils.GetOptionalAttributeValue(node, "ghost") != null) return NodeTestResult.kntrSomething; else return NodeTestResult.kntrPossible; } path.Add(node); if(innerObj != null) { string layoutOverride = XmlUtils.GetOptionalAttributeValue(node, "layout", layoutName); path.Add(innerObj.Hvo); insertPosition = CreateSlicesFor(CmObject.CreateFromDBObject(m_cache, innerObj.Hvo), layoutOverride, indent, insertPosition, path, reuseMap, caller); path.RemoveAt(path.Count - 1); } else { // No inner object...do we want a ghost slice? if (XmlUtils.GetOptionalAttributeValue(node, "ghost") != null) { MakeGhostSlice(path, node, reuseMap, obj, flid, caller, indent, ref insertPosition); } } path.RemoveAt(path.Count - 1); return NodeTestResult.kntrNothing; }
void MakeGhostSlice(ArrayList path, XmlNode node, ObjSeqHashMap reuseMap, ICmObject obj, int flidEmptyProp, XmlNode caller, int indent, ref int insertPosition) { // It's a really bad idea to add it to the path, since it kills // the code that hot swaps it, when becoming real. //path.Add(node); Slice slice = GetMatchingSlice(path, reuseMap); if (slice == null) { slice = new GhostStringSlice(obj.Hvo, flidEmptyProp, node, m_cache); // Set the label and abbreviation (in that order...abbr defaults to label if not given. // Note that we don't have a "caller" here, so we pass 'node' as both arguments... // means it gets searched twice if not found, but that's fairly harmless. slice.Label = GetLabel(node, node, obj, "ghostLabel"); slice.Abbreviation = GetLabelAbbr(node, node, obj, slice.Label, "ghostAbbr"); // Install new item at appropriate position and level. slice.Indent = indent; slice.Object = obj; slice.Cache = m_cache; slice.Mediator = m_mediator; // We need a copy since we continue to modify path, so make it as compact as possible. slice.Key = path.ToArray(); slice.ConfigurationNode = node; slice.CallerNode = caller; // don't mess with this, the obj/seq node would not have a meaningful back color override // for the slice. If we need it invent a new attribute. //slice.OverrideBackColor(XmlUtils.GetOptionalAttributeValue(node, "backColor")); // dubious...should the string slice really get the context menu for the object? slice.ShowContextMenu += new TreeNodeEventHandler(this.OnShowContextMenu); slice.SmallImages = SmallImages; SetNodeWeight(node, slice); slice.FinishInit(); InsertSliceAndRegisterWithContextHelp(insertPosition, slice, node); } else { EnsureValidIndexForReusedSlice(slice, insertPosition); } insertPosition++; // Since we didn't add it to the path, // then there is nothign to do at this end either.. //slice.GenerateChildren(node, caller, obj, indent, ref insertPosition, path, reuseMap); //path.RemoveAt(path.Count - 1); }
private NodeTestResult AddSeqNode(ArrayList path, XmlNode node, ObjSeqHashMap reuseMap, string editor, int flid, ICmObject obj, int indent, ref int insertPosition, bool fTestOnly, string layoutName, bool fVisIfData, XmlNode caller) { if (flid == 0) throw new ApplicationException("field attribute required for seq properties " + node.OuterXml); int cobj = m_cache.GetVectorSize(obj.Hvo, flid); // monitor it even if we're testing: result may change. m_monitoredProps.Add(new KeyValuePair<int, int>(obj.Hvo, flid)); if (fVisIfData && cobj == 0) return NodeTestResult.kntrNothing; if (fTestOnly) { if (cobj > 0 || XmlUtils.GetOptionalAttributeValue(node, "ghost") != null) return NodeTestResult.kntrSomething; else return NodeTestResult.kntrPossible; } path.Add(node); string layoutOverride = XmlUtils.GetOptionalAttributeValue(node, "layout", layoutName); if (cobj == 0) { // Nothing in seq....do we want a ghost slice? if (XmlUtils.GetOptionalAttributeValue(node, "ghost") != null) { MakeGhostSlice(path, node, reuseMap, obj, flid, caller, indent, ref insertPosition); } } else if (cobj < 15 || // This may be a little on the small side m_currentObjectFlids.Contains(flid) || (!String.IsNullOrEmpty(m_currentSlicePartName) && m_currentSliceObjHvo != 0 && m_currentSliceNew == null)) { // Create slices immediately foreach (int hvo in m_cache.GetVectorProperty(obj.Hvo, flid, false)) { path.Add(hvo); insertPosition = CreateSlicesFor(CmObject.CreateFromDBObject(m_cache, hvo), layoutOverride, indent, insertPosition, path, reuseMap, caller); path.RemoveAt(path.Count - 1); } } else { // Create unique DummyObjectSlices for each slice. This may reduce the initial // preceived benefit, but this way doesn't crash now that the slices are being // disposed of. int cnt = 0; foreach (int hvo in m_cache.GetVectorProperty(obj.Hvo, flid, false)) { path.Add(hvo); DummyObjectSlice dos = new DummyObjectSlice(this, indent, node, (ArrayList)(path.Clone()), obj, flid, cnt, layoutOverride, caller); dos.Cache = m_cache; InsertSlice(insertPosition++, dos); ////InstallSlice(dos, -1); ////ResetTabIndices(insertPosition); ////insertPosition++; path.RemoveAt(path.Count - 1); cnt++; } } path.RemoveAt(path.Count - 1); return NodeTestResult.kntrNothing; }
private void GenerateChildNode(int iChild, XmlNode node, XmlNode caller, int indent, ref int insPos, ArrayList path, ObjSeqHashMap reuseMap) { ILexReference lr = LexReference.CreateFromDBObject(m_cache, (int)m_refs[iChild]); ILexRefType lrt = LexRefType.CreateFromDBObject(m_cache, lr.OwnerHVO); string sLabel = lrt.ShortName; if (sLabel == null || sLabel == string.Empty) sLabel = lrt.Abbreviation.BestAnalysisAlternative.Text; bool fTreeRoot = true; ISilDataAccess sda = m_cache.MainCacheAccessor; int chvoTargets = sda.get_VecSize(lr.Hvo, (int)LexReference.LexReferenceTags.kflidTargets); // change the label for a Tree relationship. switch ((LexRefType.MappingTypes)lrt.MappingType) { case LexRefType.MappingTypes.kmtSenseTree: case LexRefType.MappingTypes.kmtEntryTree: case LexRefType.MappingTypes.kmtEntryOrSenseTree: case LexRefType.MappingTypes.kmtSenseAsymmetricPair: // Sense Pair with different Forward/Reverse names case LexRefType.MappingTypes.kmtEntryAsymmetricPair: // Entry Pair with different Forward/Reverse names case LexRefType.MappingTypes.kmtEntryOrSenseAsymmetricPair: // Entry or sense Pair with different Forward/Reverse names //int chvo = sda.get_VecSize(lr.Hvo, (int)LexReference.LexReferenceTags.kflidTargets); if (chvoTargets > 0) { int hvoFirst = sda.get_VecItem(lr.Hvo, (int)LexReference.LexReferenceTags.kflidTargets, 0); if (hvoFirst != m_obj.Hvo) { sLabel = lrt.ReverseName.BestAnalysisAlternative.Text; if (sLabel == null || sLabel == string.Empty) sLabel = lrt.ReverseAbbreviation.BestAnalysisAlternative.Text; fTreeRoot = false; } } break; } if (sLabel == null || sLabel == string.Empty) sLabel = LexEdStrings.ksStars; string sXml = "<slice label=\"" + sLabel + "\" field=\"Targets\"" + " editor=\"Custom\" assemblyPath=\"LexEdDll.dll\""; //string sMenu = "mnuDataTree-DeleteFromLexSenseReference"; we used to have distinct strings in the menu string sMenu = "mnuDataTree-DeleteAddLexReference"; // generate Xml for a specific slice matching this reference switch ((LexRefType.MappingTypes)lrt.MappingType) { case LexRefType.MappingTypes.kmtSenseCollection: sXml += " class=\"SIL.FieldWorks.XWorks.LexEd.LexReferenceCollectionSlice\""; break; case LexRefType.MappingTypes.kmtSensePair: case LexRefType.MappingTypes.kmtSenseAsymmetricPair: // Sense Pair with different Forward/Reverse names case LexRefType.MappingTypes.kmtEntryPair: case LexRefType.MappingTypes.kmtEntryAsymmetricPair: // Entry Pair with different Forward/Reverse names case LexRefType.MappingTypes.kmtEntryOrSensePair: case LexRefType.MappingTypes.kmtEntryOrSenseAsymmetricPair: // Entry or sense Pair with different forward/Reverse names sXml += " class=\"SIL.FieldWorks.XWorks.LexEd.LexReferencePairSlice\""; sMenu = "mnuDataTree-DeleteReplaceLexReference"; break; case LexRefType.MappingTypes.kmtSenseTree: if (fTreeRoot) { sXml += " class=\"SIL.FieldWorks.XWorks.LexEd.LexReferenceTreeBranchesSlice\""; sMenu = "mnuDataTree-DeleteAddLexReference"; } else { sXml += " class=\"SIL.FieldWorks.XWorks.LexEd.LexReferenceTreeRootSlice\""; sMenu = "mnuDataTree-DeleteReplaceLexReference"; } break; case LexRefType.MappingTypes.kmtSenseSequence: case LexRefType.MappingTypes.kmtEntrySequence: case LexRefType.MappingTypes.kmtEntryOrSenseSequence: sXml += " class=\"SIL.FieldWorks.XWorks.LexEd.LexReferenceSequenceSlice\""; break; case LexRefType.MappingTypes.kmtEntryCollection: sXml += " class=\"SIL.FieldWorks.XWorks.LexEd.LexReferenceCollectionSlice\""; //sMenu = "mnuDataTree-DeleteFromLexEntryReference"; we used to have distinct strings in the menu sMenu = "mnuDataTree-DeleteAddLexReference"; break; case LexRefType.MappingTypes.kmtEntryTree: //sMenu = "mnuDataTree-DeleteFromLexEntryReference"; we used to have distinct strings in the menu sMenu = "mnuDataTree-DeleteAddLexReference"; if (fTreeRoot) { sXml += " class=\"SIL.FieldWorks.XWorks.LexEd.LexReferenceTreeBranchesSlice\""; sMenu = "mnuDataTree-DeleteAddLexReference"; } else { sXml += " class=\"SIL.FieldWorks.XWorks.LexEd.LexReferenceTreeRootSlice\""; sMenu = "mnuDataTree-DeleteReplaceLexReference"; } break; case LexRefType.MappingTypes.kmtEntryOrSenseCollection: sXml += " class=\"SIL.FieldWorks.XWorks.LexEd.LexReferenceCollectionSlice\""; if (m_obj is LexEntry) //sMenu = "mnuDataTree-DeleteFromLexEntryReference"; we used to have distinct strings in the menu sMenu = "mnuDataTree-DeleteAddLexReference"; break; case LexRefType.MappingTypes.kmtEntryOrSenseTree: if (m_obj is LexEntry) //sMenu = "mnuDataTree-DeleteFromLexEntryReference"; we used to have distinct strings in the menu sMenu = "mnuDataTree-DeleteAddLexReference"; if (fTreeRoot) { sXml += " class=\"SIL.FieldWorks.XWorks.LexEd.LexReferenceTreeBranchesSlice\""; sMenu = "mnuDataTree-DeleteAddLexReference"; } else { sXml += " class=\"SIL.FieldWorks.XWorks.LexEd.LexReferenceTreeRootSlice\""; } break; } sXml += " mappingType=\"" + lrt.MappingType + "\" hvoDisplayParent=\"" + m_obj.Hvo + "\"" + " menu=\"" + sMenu + "\"><deParams displayProperty=\"HeadWord\"/></slice>"; node.InnerXml = sXml; int firstNewSliceIndex = insPos; CreateIndentedNodes(caller, lr, indent, ref insPos, path, reuseMap, node); for (int islice = firstNewSliceIndex; islice < insPos; islice++) { Slice child = Parent.Controls[islice] as Slice; if (child is ILexReferenceSlice) { (child as ILexReferenceSlice).MasterSlice = this; } } node.InnerXml = ""; }
public virtual void GenerateChildren(XmlNode node, XmlNode caller, ICmObject obj, int indent, ref int insPos, ArrayList path, ObjSeqHashMap reuseMap) { CheckDisposed(); // If node has children, figure what to do with them... XmlNodeList children = node.ChildNodes; DataTree.NodeTestResult ntr = DataTree.NodeTestResult.kntrNothing; // We may get child nodes from either the node itself or the calling part, but currently // don't try to handle both; we consider the children of the caller, if any, to override // the children of the node (but not unify with them, since a different kind of children // are involved). // A newly created slice is always in state ktisFixed, but that is not appropriate if it // has children from either source. However, a node which notionally has children may in fact have nothing to // show, perhaps because a sequence is empty. First evaluate this, and if true, set it // to ktisCollapsedEmpty. //bool fUseChildrenOfNode; XmlNode indentNode = null; if (caller != null) indentNode = caller.SelectSingleNode("indent"); if (indentNode != null) { // Similarly pretest for children of caller, to see whether anything is produced. ContainingDataTree.ApplyLayout(obj, indentNode, indent + ExtraIndent(indentNode), insPos, path, reuseMap, true, out ntr); //fUseChildrenOfNode = false; } else { int insPosT = insPos; // don't modify the real one in this test call. ntr = ContainingDataTree.ProcessPartChildren(node, path, reuseMap, obj, indent + ExtraIndent(node), ref insPosT, true, null, false, node); //fUseChildrenOfNode = true; } if (ntr == DataTree.NodeTestResult.kntrNothing) Expansion = DataTree.TreeItemState.ktisFixed; // probably redundant, but play safe else if (ntr == DataTree.NodeTestResult.kntrPossible) { // It could have children but currently can't: we always show this as collapsedEmpty. Expansion = DataTree.TreeItemState.ktisCollapsedEmpty; } // Remaining branches are for a node that really has children. else if (Expansion == DataTree.TreeItemState.ktisCollapsed) { // Reusing a node that was collapsed (and it has something to expand): // leave it that way (whatever the spec says). } else if (XmlUtils.GetOptionalAttributeValue(node, "expansion") != "doNotExpand") // Old code does not expand by default, couple of ways to override. // else if (Expansion == DataTree.TreeItemState.ktisExpanded // || (fUseChildrenOfNode && XmlUtils.GetOptionalAttributeValue(node, "expansion") == "expanded") // || (XmlUtils.GetOptionalAttributeValue(caller, "expansion") == "expanded") // || Expansion == DataTree.TreeItemState.ktisCollapsedEmpty) { // Either re-using a node that was expanded, or the slice spec says to auto-expand, // or the calling element says to auto-expand, // or a node that was previously empty now has real children, probably a first object // that was just added that the user wants to see: // fill in the children. Expansion = DataTree.TreeItemState.ktisExpanded; CreateIndentedNodes(caller, obj, indent, ref insPos, path, reuseMap, node); } else { // Either a new node or one previously collapsedEmpty. But it now definitely has children, // so neither of those states is appropriate. And we've covered all the cases where it // ought to be expanded. So show it as collapsed. Expansion = DataTree.TreeItemState.ktisCollapsed; } }