public void buttonDelete_Click(Office.IRibbonControl control) { Word.Document document = Globals.ThisAddIn.Application.ActiveDocument; // Get the content control Word.ContentControl cc = ContentControlMaker.getActiveContentControl(document, Globals.ThisAddIn.Application.Selection); if (cc == null) { // Shouldn't happen MessageBox.Show("Which content control?"); return; } // Delete the content control, but keep the contents log.Warn("Deleting control with tag " + cc.Tag); cc.Delete(false); }
public static void Update2D(Word.ContentControl cc, string tempfileName, string guid) { string module = $"{Product}.{Class}.{MethodBase.GetCurrentMethod().Name}()"; Globals.Chem4WordV3.Telemetry.Write(module, "Information", $"Updating 2D structure in ContentControl {cc.ID} Tag {guid}"); Word.Document doc = cc.Application.ActiveDocument; var wordSettings = new WordSettings(cc.Application); cc.LockContents = false; if (cc.Type == Word.WdContentControlType.wdContentControlPicture) { // Handle old Word 2007 style Word.Range range = cc.Range; cc.Delete(); cc = doc.ContentControls.Add(Word.WdContentControlType.wdContentControlRichText, range); cc.Tag = guid; cc.Title = Constants.ContentControlTitle; cc.Range.Delete(); } else { cc.Range.Delete(); } string bookmarkName = Constants.OoXmlBookmarkPrefix + guid; cc.Range.InsertFile(tempfileName, bookmarkName); if (doc.Bookmarks.Exists(bookmarkName)) { doc.Bookmarks[bookmarkName].Delete(); } wordSettings.RestoreSettings(cc.Application); cc.Tag = guid; cc.Title = Constants.ContentControlTitle; cc.LockContents = true; }
/// <summary> /// used when they right click then select "map to" /// </summary> /// <param name="odType"></param> public void mapToSelectedControl(ControlTreeView.OpenDopeType odType, ControlTreeView controlTreeView, ControlMain controlMain, Word.Document CurrentDocument, Office.CustomXMLPart CurrentPart, //XmlDocument OwnerDocument, bool _PictureContentControlsReplace ) { object missing = System.Type.Missing; DesignMode designMode = new OpenDoPEModel.DesignMode(CurrentDocument); // In this method, we're usually not creating a control, // so we don't need to turn off try { //create a binding Word.ContentControl cc = null; if (CurrentDocument.Application.Selection.ContentControls.Count == 1) { log.Debug("CurrentDocument.Application.Selection.ContentControls.Count == 1"); object objOne = 1; cc = CurrentDocument.Application.Selection.ContentControls.get_Item(ref objOne); log.Info("Mapped content control to tree view node " + controlTreeView.treeView.SelectedNode.Name); } else if (CurrentDocument.Application.Selection.ParentContentControl != null) { log.Debug("ParentContentControl != null"); cc = CurrentDocument.Application.Selection.ParentContentControl; } if (cc != null) { TreeNode tn = controlTreeView.treeView.SelectedNode; //get an nsmgr NameTable nt = new NameTable(); //generate the xpath and the ns manager XmlNamespaceManager xmlnsMgr = new XmlNamespaceManager(nt); string strXPath = Utilities.XpathFromXn(CurrentPart.NamespaceManager, (XmlNode)tn.Tag, true, xmlnsMgr); log.Info("Right clicked with XPath: " + strXPath); string prefixMappings = Utilities.GetPrefixMappings(xmlnsMgr); // Insert bind | condition | repeat // depending on which mode button is pressed. TagData td = new TagData(""); if ((controlMain.modeControlEnabled == false && odType == ControlTreeView.OpenDopeType.Unspecified) || // ie always mode bind (controlMain.modeControlEnabled == true && controlMain.controlMode1.isModeBind()) || odType == ControlTreeView.OpenDopeType.Bind) { log.Debug("In bind mode"); XPathsPartEntry xppe = new XPathsPartEntry(controlMain.model); xppe.setup(null, CurrentPart.Id, strXPath, prefixMappings, true); xppe.save(); td.set("od:xpath", xppe.xpathId); String val = ((XmlNode)tn.Tag).InnerText; bool isXHTML = false; bool isFlatOPC = ContentDetection.IsFlatOPCContent(val); if (isFlatOPC) { // <?mso-application progid="Word.Document"?> // <pkg:package xmlns:pkg="http://schemas.microsoft.com/office/2006/xmlPackage"> log.Debug(".. contains Flat OPC content "); cc.Type = Word.WdContentControlType.wdContentControlRichText; // Ensure block level Inline2Block i2b = new Inline2Block(); cc = i2b.convertToBlockLevel(cc, false, true); if (cc == null) { MessageBox.Show("Problems inserting block level WordML at this location."); return; } td.set("od:progid", "Word.Document"); cc.Title = "Word: " + xppe.xpathId; //cc.Range.Text = val; // don't escape it cc.Range.InsertXML(val, ref missing); } else if (ContentDetection.IsBase64Encoded(val)) { // Force picture content control ... // cc.Type = Word.WdContentControlType.wdContentControlPicture; // from wdContentControlText (or wdContentControlRichText for that matter) // doesn't work (you get "inappropriate range for applying this // content control type"). // They've said map, so delete existing, and replace it. designMode.Off(); cc.Delete(true); // Now add a new cc Globals.ThisAddIn.Application.Selection.Collapse(ref missing); if (_PictureContentControlsReplace) { // Use a rich text control instead cc = CurrentDocument.ContentControls.Add( Word.WdContentControlType.wdContentControlRichText, ref missing); PictureUtils.setPictureHandler(td); cc.Title = "Image: " + xppe.xpathId; PictureUtils.pastePictureIntoCC(cc, Convert.FromBase64String(val)); } else { cc = CurrentDocument.ContentControls.Add( Word.WdContentControlType.wdContentControlPicture, ref missing); } designMode.restoreState(); } else { isXHTML = ContentDetection.IsXHTMLContent(val); } if (cc.Type == Word.WdContentControlType.wdContentControlText) { // cc.Type = Word.WdContentControlType.wdContentControlText; // ??? cc.MultiLine = true; } //if (HasXHTMLContent(tn)) if (isXHTML) { log.Info("detected XHTML.. "); td.set("od:ContentType", "application/xhtml+xml"); cc.Title = "XHTML: " + xppe.xpathId; cc.Type = Word.WdContentControlType.wdContentControlRichText; cc.Range.Text = val; // don't escape it if (Inline2Block.containsBlockLevelContent(val)) { Inline2Block i2b = new Inline2Block(); cc = i2b.convertToBlockLevel(cc, true, true); if (cc == null) { MessageBox.Show("Problems inserting block level XHTML at this location."); return; } } } else if (!isFlatOPC) { cc.Title = "Data value: " + xppe.xpathId; } cc.Tag = td.asQueryString(); if (cc.Type != Word.WdContentControlType.wdContentControlRichText) { cc.XMLMapping.SetMappingByNode( Utilities.MxnFromTn(controlTreeView.treeView.SelectedNode, CurrentPart, true)); } } else if ((controlMain.modeControlEnabled == true && controlMain.controlMode1.isModeCondition()) || odType == ControlTreeView.OpenDopeType.Condition) { log.Debug("In condition mode"); // We want to be in Design Mode, so user can see their gesture take effect designMode.On(); ConditionsPartEntry cpe = new ConditionsPartEntry(controlMain.model); cpe.setup(CurrentPart.Id, strXPath, prefixMappings, true); cpe.save(); cc.Title = "Conditional: " + cpe.conditionId; // Write tag td.set("od:condition", cpe.conditionId); cc.Tag = td.asQueryString(); // Make it RichText; remove any pre-existing bind if (cc.XMLMapping.IsMapped) { cc.XMLMapping.Delete(); } if (cc.Type == Word.WdContentControlType.wdContentControlText) { cc.Type = Word.WdContentControlType.wdContentControlRichText; } } else if ((controlMain.modeControlEnabled == true && controlMain.controlMode1.isModeRepeat()) || odType == ControlTreeView.OpenDopeType.Repeat) { log.Debug("In repeat mode"); // We want to be in Design Mode, so user can see their gesture take effect designMode.On(); // Need to drop eg [1] (if any), so BetterForm-based interactive processing works if (strXPath.EndsWith("]")) { strXPath = strXPath.Substring(0, strXPath.LastIndexOf("[")); log.Debug("Having dropped '[]': " + strXPath); } XPathsPartEntry xppe = new XPathsPartEntry(controlMain.model); xppe.setup("rpt", CurrentPart.Id, strXPath, prefixMappings, false); xppe.save(); cc.Title = "Data value: " + xppe.xpathId; // Write tag td.set("od:repeat", xppe.xpathId); cc.Tag = td.asQueryString(); // Make it RichText; remove any pre-existing bind if (cc.XMLMapping.IsMapped) { cc.XMLMapping.Delete(); } if (cc.Type == Word.WdContentControlType.wdContentControlText) { cc.Type = Word.WdContentControlType.wdContentControlRichText; } } //ensure it's checked controlTreeView.mapToSelectedControlToolStripMenuItem.Checked = true; } } catch (COMException cex) { controlTreeView.ShowErrorMessage(cex.Message); designMode.restoreState(); } }
/// <summary> /// An inline rich text control can't contain carriage returns. /// </summary> /// <param name="control"></param> public Word.ContentControl convertToBlockLevel(Word.ContentControl currentCC, bool keepContents, bool updateScreen) { Word.Document document = Globals.ThisAddIn.Application.ActiveDocument; OpenDoPEModel.DesignMode designMode = new OpenDoPEModel.DesignMode(document); // Only do it if the content control is rich text if (!currentCC.Type.Equals(Word.WdContentControlType.wdContentControlRichText)) { log.Warn("convert to block level only operates on rich text controls, not " + currentCC.Type); return(null); } string majorVersionString = Globals.ThisAddIn.Application.Version.Split(new char[] { '.' })[0]; int majorVersion = Convert.ToInt32(majorVersionString); // Only do it if the content control is not already block level // if (isBlockLevel(currentCC)) { return(currentCC); } // Can only do this if the content control is not // nested within some other inline content control if (currentCC.ParentContentControl != null && !isBlockLevel(currentCC.ParentContentControl)) { MessageBox.Show("This content control contains block level content, but can't be converted automatically. Please correct this yourself, by deleting it, and re-creating at block level."); return(null); } if (majorVersion >= 14) { getWordApp().UndoRecord.StartCustomRecord("Promote content control to block-level"); } bool ccIsAtPStart = ccStartsAtStartOfParagraph(currentCC); object collapseStart = Word.WdCollapseDirection.wdCollapseStart; //object collapseEnd = Word.WdCollapseDirection.wdCollapseEnd; object unitCharacter = Word.WdUnits.wdCharacter; // Get a range start of cc. Word.Range ccRange = currentCC.Range; ccRange.Collapse(ref collapseStart); // Delete the cc, but preserve Tag, Title string tagVal = currentCC.Tag; string titleVal = currentCC.Title; string contents = currentCC.Range.Text; currentCC.Delete(true); ccRange.Select(); if (ccIsAtPStart) { document.Windows[1].Selection.TypeParagraph(); } else { // Insert 2 new paragraphs document.Windows[1].Selection.TypeParagraph(); document.Windows[1].Selection.TypeParagraph(); } // Create a cc around the first new p object start = ccRange.Start + 1; object end = ccRange.Start + 1; object newRange = document.Range(ref start, ref end); log.Info("target {0}, {1}", ((Word.Range)newRange).Start, ((Word.Range)newRange).End); designMode.Off(); Word.ContentControl newCC = document.ContentControls.Add(Word.WdContentControlType.wdContentControlRichText, ref newRange); designMode.restoreState(); newCC.Tag = tagVal; newCC.Title = titleVal; if (keepContents) // want to do this for XHTML { newCC.Range.Text = contents; } if (updateScreen) { newCC.Application.ScreenUpdating = true; newCC.Application.ScreenRefresh(); } return(newCC); // Approach: // .. Get the paragraph // .. Make a copy // .. in the copy, delete up to our position // .. in the original, delete after our position //Word.Range splittingPoint = sel.Range; //object para1DeleteStartPoint = splittingPoint.Start; //Word.Range paraOrig = Globals.ThisAddIn.Application.ActiveDocument.Range(ref para1DeleteStartPoint, ref para1DeleteStartPoint); //paraOrig.MoveStart(ref unitParagraph, ref back1); //int lengthStartSegment = paraOrig.End - paraOrig.Start; //object startPoint = paraOrig.Start; //paraOrig.MoveEnd(ref unitParagraph, ref forward1); //object endPoint = paraOrig.End; //object endPointPlusOne = paraOrig.End + 1; //// copy it //Word.Range insertPoint = Globals.ThisAddIn.Application.ActiveDocument.Range(ref endPoint, ref endPoint); //paraOrig.Copy(); //insertPoint.Paste(); //// In the copy, delete the first half //// (do this operation first, to preserve our original position calculations) //object para2DeleteEndpoint = (int)endPoint + lengthStartSegment; //Word.Range para2Deletion = Globals.ThisAddIn.Application.ActiveDocument.Range(ref endPoint, ref para2DeleteEndpoint); //para2Deletion.Delete(); //// In the original, delete the second half //Word.Range para1Deletion = Globals.ThisAddIn.Application.ActiveDocument.Range(ref para1DeleteStartPoint, ref endPoint); //para1Deletion.Delete(); if (majorVersion >= 14) { getWordApp().UndoRecord.EndCustomRecord(); } }
public static void editXPath(Word.ContentControl cc) { Word.Document document = Globals.ThisAddIn.Application.ActiveDocument; object missing = System.Type.Missing; // First, work out whether this is a condition or a repeat or a plain bind bool isCondition = false; bool isRepeat = false; bool isBind = false; if ((cc.Title != null && cc.Title.StartsWith("Condition")) || (cc.Tag != null && cc.Tag.Contains("od:condition"))) { isCondition = true; } else if ((cc.Title != null && cc.Title.StartsWith("Repeat")) || (cc.Tag != null && cc.Tag.Contains("od:repeat"))) { isRepeat = true; } else if ((cc.Title != null && cc.Title.StartsWith("Data")) || (cc.Tag != null && cc.Tag.Contains("od:xpath")) || cc.XMLMapping.IsMapped ) { isBind = true; } else { // Ask user using (Forms.ConditionOrRepeat cor = new Forms.ConditionOrRepeat()) { if (cor.ShowDialog() == DialogResult.OK) { isCondition = cor.radioButtonCondition.Checked; isRepeat = cor.radioButtonRepeat.Checked; isBind = cor.radioButtonBind.Checked; } else { // They cancelled return; } } } // OK, now we know whether its a condition or a repeat or a bind // Is it already mapped to something? TagData td = new TagData(cc.Tag); Model model = Model.ModelFactory(document); string strXPath = ""; // In order to get Id and prefix mappings for current part CustomTaskPane ctpPaneForThisWindow = Utilities.FindTaskPaneForCurrentWindow(); Controls.ControlMain ccm = (Controls.ControlMain)ctpPaneForThisWindow.Control; string cxpId = ccm.CurrentPart.Id; string prefixMappings = ""; // TODO GetPrefixMappings(ccm.CurrentPart.NamespaceManager); log.Debug("default prefixMappings: " + prefixMappings); XPathsPartEntry xppe = null; ConditionsPartEntry cpe = null; if (isCondition && td.get("od:condition") != null) { string conditionId = td.get("od:condition"); cpe = new ConditionsPartEntry(model); condition c = cpe.getConditionByID(conditionId); string xpathid = null; if (c != null && c.Item is xpathref) { xpathref ex = (xpathref)c.Item; xpathid = ex.id; // Now fetch the XPath xppe = new XPathsPartEntry(model); xpathsXpath xx = xppe.getXPathByID(xpathid); if (xx != null) { strXPath = xx.dataBinding.xpath; cxpId = xx.dataBinding.storeItemID; prefixMappings = xx.dataBinding.prefixMappings; } } } else if (isRepeat && td.get("od:repeat") != null) { string repeatId = td.get("od:repeat"); // Now fetch the XPath xppe = new XPathsPartEntry(model); xpathsXpath xx = xppe.getXPathByID(repeatId); if (xx != null) { strXPath = xx.dataBinding.xpath; cxpId = xx.dataBinding.storeItemID; prefixMappings = xx.dataBinding.prefixMappings; } } else if (isBind) { if (cc.XMLMapping.IsMapped) { // Prefer this, if for some reason it contradicts od:xpath strXPath = cc.XMLMapping.XPath; cxpId = cc.XMLMapping.CustomXMLPart.Id; prefixMappings = cc.XMLMapping.PrefixMappings; } else if (td.get("od:xpath") != null) { string xpathId = td.get("od:xpath"); // Now fetch the XPath xppe = new XPathsPartEntry(model); xpathsXpath xx = xppe.getXPathByID(xpathId); if (xx != null) { strXPath = xx.dataBinding.xpath; cxpId = xx.dataBinding.storeItemID; prefixMappings = xx.dataBinding.prefixMappings; } } } // Now we can present the form using (Forms.XPathEditor xpe = new Forms.XPathEditor()) { xpe.textBox1.Text = strXPath; if (xpe.ShowDialog() == DialogResult.OK) { strXPath = xpe.textBox1.Text; } else { // They cancelled return; } } // Now give effect to it td = new TagData(""); if (isCondition) { // Create the new condition. Doesn't attempt to delete // the old one (if any) if (cpe == null) { cpe = new ConditionsPartEntry(model); } cpe.setup(cxpId, strXPath, prefixMappings, true); cpe.save(); cc.Title = "Conditional: " + cpe.conditionId; // Write tag td.set("od:condition", cpe.conditionId); cc.Tag = td.asQueryString(); } else if (isRepeat) { // Create the new repeat. Doesn't attempt to delete // the old one (if any) if (xppe == null) { xppe = new XPathsPartEntry(model); } xppe.setup("rpt", cxpId, strXPath, prefixMappings, false); xppe.save(); cc.Title = "Repeat: " + xppe.xpathId; // Write tag td.set("od:repeat", xppe.xpathId); cc.Tag = td.asQueryString(); } else if (isBind) { // Create the new bind. Doesn't attempt to delete // the old one (if any) if (xppe == null) { xppe = new XPathsPartEntry(model); } Word.XMLMapping bind = cc.XMLMapping; bool mappable = false; try { mappable = bind.SetMapping(strXPath, prefixMappings, CustomXmlUtilities.getPartById(document, cxpId)); } catch (COMException ce) { if (ce.Message.Contains("Data bindings cannot be created for rich text content controls")) { // TODO: editing a rich text control // TODO manually check whether it is mappable // So for now, cc.Delete(true); cc = document.ContentControls.Add( Word.WdContentControlType.wdContentControlText, ref missing); mappable = cc.XMLMapping.SetMapping(strXPath, prefixMappings, CustomXmlUtilities.getPartById(document, cxpId)); } else { log.Error(ce); //What to do?? } } if (mappable) { // What does the XPath point to? string val = cc.XMLMapping.CustomXMLNode.Text; cc.Title = "Data value: " + xppe.xpathId; if (ContentDetection.IsBase64Encoded(val)) { // Force picture content control ... // cc.Type = Word.WdContentControlType.wdContentControlPicture; // from wdContentControlText (or wdContentControlRichText for that matter) // doesn't work (you get "inappropriate range for applying this // content control type"). cc.Delete(true); // Now add a new cc Globals.ThisAddIn.Application.Selection.Collapse(ref missing); bool _PictureContentControlsReplace = true; String picSetting = System.Configuration.ConfigurationManager.AppSettings["ContentControl.Picture.RichText.Override"]; if (picSetting != null) { Boolean.TryParse(picSetting, out _PictureContentControlsReplace); } if (_PictureContentControlsReplace) { // Use a rich text control instead cc = document.ContentControls.Add( Word.WdContentControlType.wdContentControlRichText, ref missing); PictureUtils.setPictureHandler(td); cc.Title = "Image: " + xppe.xpathId; PictureUtils.pastePictureIntoCC(cc, Convert.FromBase64String(val)); } else { cc = document.ContentControls.Add( Word.WdContentControlType.wdContentControlPicture, ref missing); cc.XMLMapping.SetMapping(strXPath, prefixMappings, CustomXmlUtilities.getPartById(document, cxpId)); } } else if (ContentDetection.IsFlatOPCContent(val)) { // <?mso-application progid="Word.Document"?> // <pkg:package xmlns:pkg="http://schemas.microsoft.com/office/2006/xmlPackage"> td.set("od:progid", "Word.Document"); cc.Tag = td.asQueryString(); cc.XMLMapping.Delete(); cc.Type = Word.WdContentControlType.wdContentControlRichText; cc.Title = "Word: " + xppe.xpathId; //cc.Range.Text = val; // don't escape it Inline2Block i2b = new Inline2Block(); cc = i2b.convertToBlockLevel(cc, false, true); if (cc == null) { MessageBox.Show("Problems inserting block level WordML at this location."); return; } cc.Range.InsertXML(val, ref missing); } else if (ContentDetection.IsXHTMLContent(val)) { td.set("od:ContentType", "application/xhtml+xml"); cc.Tag = td.asQueryString(); cc.XMLMapping.Delete(); cc.Type = Word.WdContentControlType.wdContentControlRichText; cc.Title = "XHTML: " + xppe.xpathId; if (Inline2Block.containsBlockLevelContent(val)) { Inline2Block i2b = new Inline2Block(); cc = i2b.convertToBlockLevel(cc, true, true); if (cc == null) { MessageBox.Show("Problems inserting block level XHTML at this location."); return; } } } xppe.setup(null, cxpId, strXPath, prefixMappings, true); xppe.save(); td.set("od:xpath", xppe.xpathId); cc.Tag = td.asQueryString(); } else { xppe.setup(null, cxpId, strXPath, prefixMappings, true); xppe.save(); td.set("od:xpath", xppe.xpathId); cc.Title = "Data value: " + xppe.xpathId; cc.Tag = td.asQueryString(); // FIXME TODO handle pictures/FlatOPC/XHTML in this case log.Warn(" XPath \n\r " + strXPath + "\n\r does not return an element. The OpenDoPE pre-processor will attempt to evaluate it, but Word will never update the result. "); bind.Delete(); MessageBox.Show(" XPath \n\r " + strXPath + "\n\r does not return an element. Check this is what you want? "); } } }