/// <summary> /// Handle Word's AfterAdd event for content controls, to set new controls' placeholder text /// </summary> /// <param name="ccAdded"></param> /// <param name="InUndoRedo"></param> private void doc_ContentControlAfterAdd(Word.ContentControl ccAdded, bool InUndoRedo) { log.Info("doc_ContentControlAfterAdd..."); if (InUndoRedo) { } else if (m_cmTaskPane.RecentDragDrop) { log.Info("recent drag drop..."); ccAdded.Application.ScreenUpdating = false; /* * Don't set placeholder text. We don't want it! * It prevents child content controls from being added. * And it is very difficult to get rid of! * * //set the placeholder text * if (m_cmTaskPane.controlMode1.isModeBind() * && ccAdded.Type != Word.WdContentControlType.wdContentControlRichText * && ccAdded.Type != Word.WdContentControlType.wdContentControlPicture // TODO, what other types don't support placeholder text? * ) * { * log.Debug("Setting placeholder text (fwiw)"); * // set the placeholder text has the side effect of clearing out the control's contents, * // so grab the current text in the node (if any) * string currentText = null; * if (ccAdded.XMLMapping.IsMapped) * { * currentText = ccAdded.XMLMapping.CustomXMLNode.Text; * } * * ccAdded.SetPlaceholderText(null, null, Utilities.GetPlaceholderText(ccAdded.Type)); * * // now bring back the original text * if (currentText != null) * { * ccAdded.Range.Text = currentText; * } * * } * */ string xml = ccAdded.Range.Text; if (ContentDetection.IsBase64Encoded(xml)) { // Don't need to do anything here... } else if (ContentDetection.IsFlatOPCContent(xml)) { xml = xml.Replace(""", "\""); log.Debug(xml); // Need it to be block level Inline2Block i2b = new Inline2Block(); AsyncMethodCallerFlatOPC caller = new AsyncMethodCallerFlatOPC(i2b.blockLevelFlatOPC); caller.BeginInvoke(ccAdded, xml, null, null); } else if (ContentDetection.IsXHTMLContent(ccAdded.Range.Text)) { log.Info("is XHTML .. " + ccAdded.Range.Text); if (Inline2Block.containsBlockLevelContent(ccAdded.Range.Text)) { Inline2Block i2b = new Inline2Block(); AsyncMethodCallerXHTML caller = new AsyncMethodCallerXHTML(i2b.convertToBlockLevel); caller.BeginInvoke(ccAdded, true, true, null, null); } } else { // Have to do CopyAdjacentFormat outside this event. // (It works from OnExit, but not from AfterAdd. Go figure...) ContentControlStyle ccs = new ContentControlStyle(); AsyncMethodCallerPlainText caller2 = new AsyncMethodCallerPlainText(ccs.CopyAdjacentFormat); caller2.BeginInvoke(ccAdded, null, null); } } }
/// <summary> /// Create a content control mapped to the selected XML node. /// </summary> /// <param name="CCType">A WdContentControlType value specifying the type of control to create.</param> public void CreateMappedControl(Word.WdContentControlType CCType, ControlTreeView.OpenDopeType odType, ControlTreeView controlTreeView, ControlMain controlMain, Word.Document CurrentDocument, Office.CustomXMLPart CurrentPart, //XmlDocument OwnerDocument, bool _PictureContentControlsReplace ) { OpenDoPEModel.DesignMode designMode = new OpenDoPEModel.DesignMode(CurrentDocument); designMode.Off(); try { object missing = Type.Missing; TreeNode tn = controlTreeView.treeView.SelectedNode; if (((XmlNode)tn.Tag).NodeType == XmlNodeType.Text) { tn = tn.Parent; } //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 click for 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"); String val = ((XmlNode)tn.Tag).InnerText; //bool isXHTML = HasXHTMLContent(tn); bool isPicture = false; bool isXHTML = false; bool isFlatOPC = ContentDetection.IsFlatOPCContent(val); Word.ContentControl cc = null; if (isFlatOPC) { // <?mso-application progid="Word.Document"?> // <pkg:package xmlns:pkg="http://schemas.microsoft.com/office/2006/xmlPackage"> log.Debug(".. contains block content "); cc = CurrentDocument.Application.Selection.ContentControls.Add( Word.WdContentControlType.wdContentControlRichText, ref missing); // 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; } } else { isXHTML = ContentDetection.IsXHTMLContent(val); } if (isXHTML) { cc = CurrentDocument.Application.Selection.ContentControls.Add( Word.WdContentControlType.wdContentControlRichText, ref missing); 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."); designMode.restoreState(); return; } } } else if (ContentDetection.IsBase64Encoded(val)) { isPicture = true; if (_PictureContentControlsReplace) { // Use a rich text control instead cc = CurrentDocument.ContentControls.Add( Word.WdContentControlType.wdContentControlRichText, ref missing); PictureUtils.pastePictureIntoCC(cc, Convert.FromBase64String(val)); } else { // Force picture content control log.Debug("Detected picture"); cc = CurrentDocument.Application.Selection.ContentControls.Add(Word.WdContentControlType.wdContentControlPicture, ref missing); } } else if (!isFlatOPC) { log.Debug("Not picture or XHTML; " + CCType.ToString()); // This formulation seems more susceptible to "locked for editing" //object rng = CurrentDocument.Application.Selection.Range; //cc = CurrentDocument.ContentControls.Add(Word.WdContentControlType.wdContentControlText, ref rng); // so prefer: cc = CurrentDocument.Application.Selection.ContentControls.Add(CCType, ref missing); } XPathsPartEntry xppe = new XPathsPartEntry(controlMain.model); xppe.setup(null, CurrentPart.Id, strXPath, prefixMappings, true); xppe.save(); td.set("od:xpath", xppe.xpathId); if (isFlatOPC) { 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 (isXHTML) { td.set("od:ContentType", "application/xhtml+xml"); cc.Title = "XHTML: " + xppe.xpathId; cc.Range.Text = val; } else if (isPicture) { PictureUtils.setPictureHandler(td); cc.Title = "Image: " + xppe.xpathId; } else { cc.Title = "Data value: " + xppe.xpathId; } cc.Tag = td.asQueryString(); if (cc.Type == Word.WdContentControlType.wdContentControlText) { cc.MultiLine = true; } if (cc.Type != Word.WdContentControlType.wdContentControlRichText) { cc.XMLMapping.SetMappingByNode(Utilities.MxnFromTn(tn, CurrentPart, true)); } designMode.restoreState(); } else if ((controlMain.modeControlEnabled == true && controlMain.controlMode1.isModeCondition()) || odType == ControlTreeView.OpenDopeType.Condition) { log.Debug("In condition mode"); // User can make a condition whatever type they like, // but if they make it text, change it to RichText. if (CCType == Word.WdContentControlType.wdContentControlText) { CCType = Word.WdContentControlType.wdContentControlRichText; } Word.ContentControl cc = CurrentDocument.Application.Selection.ContentControls.Add(CCType, ref missing); 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(); // We want to be in Design Mode, so user can see their gesture take effect designMode.On(); Ribbon.editXPath(cc); } else if ((controlMain.modeControlEnabled == true && controlMain.controlMode1.isModeRepeat()) || odType == ControlTreeView.OpenDopeType.Repeat) { log.Debug("In repeat mode"); // User can make a repeat whatever type they like // (though does it ever make sense for it to be other than RichText?), // but if they make it text, change it to RichText. if (CCType == Word.WdContentControlType.wdContentControlText) { CCType = Word.WdContentControlType.wdContentControlRichText; } Word.ContentControl cc = CurrentDocument.Application.Selection.ContentControls.Add(CCType, ref missing); // 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(); // We want to be in Design Mode, so user can see their gesture take effect designMode.On(); } } catch (COMException cex) { controlTreeView.ShowErrorMessage(cex.Message); designMode.restoreState(); } }
public void treeView_ItemDrag(object sender, ItemDragEventArgs e, ControlTreeView controlTreeView, ControlMain controlMain, Word.Document CurrentDocument, Office.CustomXMLPart CurrentPart, XmlDocument OwnerDocument, bool _PictureContentControlsReplace) { object missing = System.Type.Missing; TreeNode tn = (TreeNode)e.Item; if (tn == null) { Debug.Fail("no tn"); return; } //check if this is something we can drag if (((XmlNode)tn.Tag).NodeType == XmlNodeType.ProcessingInstruction || ((XmlNode)tn.Tag).NodeType == XmlNodeType.Comment) { return; } if (controlMain.modeControlEnabled == false || // ie always mode bind controlMain.controlMode1.isModeBind()) { if (!ControlTreeView.IsLeafNode(tn) || ((XmlNode)tn.Tag).NodeType == XmlNodeType.Text && !ControlTreeView.IsLeafNode(tn.Parent)) { return; } } // repeats and conditions; let them drag any node //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("Dragging XPath: " + strXPath); string prefixMappings = Utilities.GetPrefixMappings(xmlnsMgr); // OpenDoPE TagData td = new TagData(""); String val = ((XmlNode)tn.Tag).InnerText; DesignMode designMode = new OpenDoPEModel.DesignMode(CurrentDocument); // Special case for pictures, since drag/drop does not seem // to work properly (the XHTML pasted doesn't do what it should?) bool isPicture = ContentDetection.IsBase64Encoded(val); if (isPicture && !_PictureContentControlsReplace) { designMode.Off(); log.Debug("Special case handling for pictures.."); // Selection can't be textual content, so ensure it isn't. // It is allowed to be a picture, so in the future we could // leave the selection alone if it is just a picture. Globals.ThisAddIn.Application.Selection.Collapse(ref missing); // Are they dragging to an existing picture content control Word.ContentControl picCC = ContentControlMaker.getActiveContentControl(CurrentDocument, Globals.ThisAddIn.Application.Selection); try { if (picCC == null || (picCC.Type != Word.WdContentControlType.wdContentControlPicture)) { picCC = CurrentDocument.ContentControls.Add( Word.WdContentControlType.wdContentControlPicture, ref missing); designMode.restoreState(); } } catch (COMException ce) { // Will happen if you try to drag a text node onto an existing image content control log.Debug("Ignoring " + ce.Message); return; } XPathsPartEntry xppe = new XPathsPartEntry(controlMain.model); xppe.setup(null, CurrentPart.Id, strXPath, prefixMappings, false); xppe.save(); td.set("od:xpath", xppe.xpathId); picCC.Tag = td.asQueryString(); picCC.Title = "Data value: " + xppe.xpathId; picCC.XMLMapping.SetMappingByNode(Utilities.MxnFromTn(tn, CurrentPart, true)); return; } log.Debug("\n\ntreeView_ItemDrag for WdSelectionType " + Globals.ThisAddIn.Application.Selection.Type.ToString()); bool isXHTML = false; bool isFlatOPC = ContentDetection.IsFlatOPCContent(val); if (!isFlatOPC) { isXHTML = ContentDetection.IsXHTMLContent(val); } if (Globals.ThisAddIn.Application.Selection.Type != Microsoft.Office.Interop.Word.WdSelectionType.wdSelectionIP) { // ie something is selected, since "inline paragraph selection" // just means the cursor is somewhere inside // a paragraph, but with nothing selected. designMode.Off(); // Selection types: http://msdn.microsoft.com/en-us/library/microsoft.office.interop.word.wdselectiontype(v=office.11).aspx log.Debug("treeView_ItemDrag fired, but interpreted as gesture for WdSelectionType " + Globals.ThisAddIn.Application.Selection.Type.ToString()); Word.ContentControl parentCC = ContentControlMaker.getActiveContentControl(CurrentDocument, Globals.ThisAddIn.Application.Selection); // Insert bind | condition | repeat // depending on which mode button is pressed. if (controlMain.modeControlEnabled == false || // ie always mode bind controlMain.controlMode1.isModeBind()) { log.Debug("In bind mode"); Word.ContentControl cc = null; try { if (isFlatOPC || isXHTML || (isPicture && _PictureContentControlsReplace)) { // Rich text if (parentCC != null && ContentControlOpenDoPEType.isBound(parentCC)) { // Reuse existing cc cc = ContentControlMaker.MakeOrReuse(true, Word.WdContentControlType.wdContentControlRichText, CurrentDocument, Globals.ThisAddIn.Application.Selection); } else { // Make new cc cc = ContentControlMaker.MakeOrReuse(true, Word.WdContentControlType.wdContentControlRichText, CurrentDocument, Globals.ThisAddIn.Application.Selection); } if (isFlatOPC) { log.Debug(".. contains block content "); // 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; } } else if (isXHTML && // and thus not picture Inline2Block.containsBlockLevelContent(val)) { log.Debug(".. contains block content "); // Ensure block level Inline2Block i2b = new Inline2Block(); cc = i2b.convertToBlockLevel(cc, false, true); if (cc == null) { MessageBox.Show("Problems inserting block level XHTML at this location."); return; } } } else { // Plain text if (parentCC != null && ContentControlOpenDoPEType.isBound(parentCC)) { // Reuse existing cc cc = ContentControlMaker.MakeOrReuse(true, Word.WdContentControlType.wdContentControlText, CurrentDocument, Globals.ThisAddIn.Application.Selection); } else { // Make new cc cc = ContentControlMaker.MakeOrReuse(false, Word.WdContentControlType.wdContentControlText, CurrentDocument, Globals.ThisAddIn.Application.Selection); } cc.MultiLine = true; // Is a text content control always run-level? // No, not if you have a single para selected and you do drag gesture // (or if you remap a rich text control) } } catch (Exception ex) { log.Error("Couldn't add content control: " + ex.Message); return; } XPathsPartEntry xppe = new XPathsPartEntry(controlMain.model); xppe.setup(null, CurrentPart.Id, strXPath, prefixMappings, true); xppe.save(); td.set("od:xpath", xppe.xpathId); if (isFlatOPC) { // <?mso-application progid="Word.Document"?> // <pkg:package xmlns:pkg="http://schemas.microsoft.com/office/2006/xmlPackage"> 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 (isXHTML) { td.set("od:ContentType", "application/xhtml+xml"); cc.Title = "XHTML: " + xppe.xpathId; cc.Range.Text = val; // don't escape it } else if (isPicture) { PictureUtils.setPictureHandler(td); cc.Title = "Image: " + xppe.xpathId; string picContent = CurrentPart.SelectSingleNode(strXPath).Text; PictureUtils.pastePictureIntoCC(cc, Convert.FromBase64String(picContent)); } else { cc.XMLMapping.SetMappingByNode(Utilities.MxnFromTn(tn, CurrentPart, true)); string nodeXML = cc.XMLMapping.CustomXMLNode.XML; log.Info(nodeXML); cc.Title = "Data value: " + xppe.xpathId; } cc.Tag = td.asQueryString(); designMode.restoreState(); } else if (controlMain.controlMode1.isModeCondition()) { log.Debug("In condition mode"); Word.ContentControl cc = null; try { // always make new cc = ContentControlMaker.MakeOrReuse(false, Word.WdContentControlType.wdContentControlRichText, CurrentDocument, Globals.ThisAddIn.Application.Selection); } catch (Exception ex) { log.Error("Couldn't add content control: " + ex.Message); return; } 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(); designMode.On(); } else if (controlMain.controlMode1.isModeRepeat()) { log.Debug("In repeat mode"); Word.ContentControl cc = null; try { // always make new cc = ContentControlMaker.MakeOrReuse(false, Word.WdContentControlType.wdContentControlRichText, CurrentDocument, Globals.ThisAddIn.Application.Selection); } catch (Exception ex) { log.Error("Couldn't add content control: " + ex.Message); return; } // 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); // no Q for repeat xppe.save(); cc.Title = "Repeat: " + xppe.xpathId; // Write tag td.set("od:repeat", xppe.xpathId); cc.Tag = td.asQueryString(); designMode.On(); } return; } // end if (Globals.ThisAddIn.Application.Selection.Type != Microsoft.Office.Interop.Word.WdSelectionType.wdSelectionIP) // Selection.Type: Microsoft.Office.Interop.Word.WdSelectionType.wdSelectionIP // ie cursor is somewhere inside a paragraph, but with nothing selected. log.Info("In wdSelectionIP specific code."); // leave designMode alone here // Following processing uses clipboard HTML to implement drag/drop processing // Could avoid dealing with that (what's the problem anyway?) if they are dragging onto an existing content control, with: //Word.ContentControl existingCC = ContentControlMaker.getActiveContentControl(CurrentDocument, Globals.ThisAddIn.Application.Selection); //if (existingCC != null) return; // But that stops them from dragging any more content into a repeat. string title = ""; string tag = ""; bool needBind = false; log.Debug(strXPath); Office.CustomXMLNode targetNode = CurrentPart.SelectSingleNode(strXPath); string nodeContent = targetNode.Text; // or ((XmlNode)tn.Tag).InnerXml // Insert bind | condition | repeat // depending on which mode button is pressed. if (controlMain.modeControlEnabled == false || // ie always mode bind controlMain.controlMode1.isModeBind()) { log.Debug("In bind mode"); // OpenDoPE: create w:tag=od:xpath=x1 // and add XPath to xpaths part XPathsPartEntry xppe = new XPathsPartEntry(controlMain.model); xppe.setup(null, CurrentPart.Id, strXPath, prefixMappings, false); // Don't setup Q until after drop xppe.save(); // Write tag td.set("od:xpath", xppe.xpathId); // Does this node contain XHTML? // TODO: error handling log.Info(nodeContent); if (isFlatOPC) { // <?mso-application progid="Word.Document"?> // <pkg:package xmlns:pkg="http://schemas.microsoft.com/office/2006/xmlPackage"> td.set("od:progid", "Word.Document"); title = "Word: " + xppe.xpathId; needBind = false; // make it a rich text control } else if (isXHTML) { td.set("od:ContentType", "application/xhtml+xml"); // TODO since this is a run-level sdt, // the XHTML content will need to be run-level. // Help the user with this? // Or in a run-level context, docx4j could convert // p to soft-enter? But what to do about tables? title = "XHTML: " + xppe.xpathId; needBind = false; // make it a rich text control // Word will only replace our HTML-imported-to-docx with the raw HTML // if we have the bind. // Without this, giving the user visual feedback in Word is a TODO } else if (isPicture) { designMode.Off(); log.Debug("NEW Special case handling for pictures.."); //object missing = System.Type.Missing; Globals.ThisAddIn.Application.Selection.Collapse(ref missing); // Are they dragging to an existing picture content control Word.ContentControl picCC = ContentControlMaker.getActiveContentControl(CurrentDocument, Globals.ThisAddIn.Application.Selection); try { if (picCC == null || (picCC.Type != Word.WdContentControlType.wdContentControlPicture)) { picCC = CurrentDocument.ContentControls.Add( Word.WdContentControlType.wdContentControlRichText, ref missing); designMode.restoreState(); } } catch (COMException ce) { // Will happen if you try to drag a text node onto an existing image content control log.Debug("Ignoring " + ce.Message); return; } PictureUtils.setPictureHandler(td); picCC.Title = "Image: " + xppe.xpathId; picCC.Tag = td.asQueryString(); PictureUtils.pastePictureIntoCC(picCC, Convert.FromBase64String(nodeContent)); return; } else { title = "Data value: " + xppe.xpathId; needBind = true; } tag = td.asQueryString(); } else if (controlMain.controlMode1.isModeCondition()) { log.Debug("In condition mode"); ConditionsPartEntry cpe = new ConditionsPartEntry(controlMain.model); cpe.setup(CurrentPart.Id, strXPath, prefixMappings, false); cpe.save(); title = "Conditional: " + cpe.conditionId; // Write tag td.set("od:condition", cpe.conditionId); tag = td.asQueryString(); } else if (controlMain.controlMode1.isModeRepeat()) { log.Debug("In repeat mode"); // 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(); title = "Data value: " + xppe.xpathId; // Write tag td.set("od:repeat", xppe.xpathId); tag = td.asQueryString(); } //create the HTML string strHTML = string.Empty; if (isFlatOPC) { // <?mso-application progid="Word.Document"?> // <pkg:package xmlns:pkg="http://schemas.microsoft.com/office/2006/xmlPackage"> nodeContent = ControlTreeView.EscapeXHTML(nodeContent); strHTML = ClipboardUtilities.GenerateClipboardHTML(needBind, strXPath, Utilities.GetPrefixMappings(xmlnsMgr), CurrentPart.Id, Utilities.MappingType.RichText, title, tag, nodeContent); } else if (isXHTML) { // need to escape eg <span> for it to get through the Clipboard nodeContent = ControlTreeView.EscapeXHTML(nodeContent); // use a RichText control, and set nodeContent strHTML = ClipboardUtilities.GenerateClipboardHTML(needBind, strXPath, Utilities.GetPrefixMappings(xmlnsMgr), CurrentPart.Id, Utilities.MappingType.RichText, title, tag, nodeContent); // alternatively, this could be done in DocumentEvents.doc_ContentControlAfterAdd // but to do it there, we'd need to manually resolve the XPath to // find the value of the CustomXMLNode it pointed to. } else if (!needBind) { // For conditions & repeats, we use a RichText control strHTML = ClipboardUtilities.GenerateClipboardHTML(needBind, strXPath, Utilities.GetPrefixMappings(xmlnsMgr), CurrentPart.Id, Utilities.MappingType.RichText, title, tag); } else { // Normal bind if (OwnerDocument.Schemas.Count > 0) { switch (Utilities.CheckNodeType((XmlNode)tn.Tag)) { case Utilities.MappingType.Date: strHTML = ClipboardUtilities.GenerateClipboardHTML(needBind, strXPath, prefixMappings, CurrentPart.Id, Utilities.MappingType.Date, title, tag); break; case Utilities.MappingType.DropDown: strHTML = ClipboardUtilities.GenerateClipboardHTML(needBind, strXPath, prefixMappings, CurrentPart.Id, Utilities.MappingType.DropDown, title, tag); break; case Utilities.MappingType.Picture: strHTML = ClipboardUtilities.GenerateClipboardHTML(needBind, strXPath, prefixMappings, CurrentPart.Id, Utilities.MappingType.Picture, title, tag); break; default: strHTML = ClipboardUtilities.GenerateClipboardHTML(needBind, strXPath, prefixMappings, CurrentPart.Id, Utilities.MappingType.Text, title, tag); break; } } else { //String val = ((XmlNode)tn.Tag).InnerText; if (ContentDetection.IsBase64Encoded(val)) { // Force picture content control strHTML = ClipboardUtilities.GenerateClipboardHTML(needBind, strXPath, Utilities.GetPrefixMappings(xmlnsMgr), CurrentPart.Id, Utilities.MappingType.Picture, title, tag); } else { strHTML = ClipboardUtilities.GenerateClipboardHTML(needBind, strXPath, Utilities.GetPrefixMappings(xmlnsMgr), CurrentPart.Id, Utilities.MappingType.Text, title, tag); } } } // All cases:- //notify ourselves of a pending drag/drop controlMain.NotifyDragDrop(true); //throw it on the clipboard to drag DataObject dobj = new DataObject(); dobj.SetData(DataFormats.Html, strHTML); dobj.SetData(DataFormats.Text, tn.Text); controlTreeView.DoDragDrop(dobj, DragDropEffects.Move); //notify ourselves of a completed drag/drop controlMain.NotifyDragDrop(false); Clipboard.SetData(DataFormats.Text, ((XmlNode)tn.Tag).InnerXml); }
/// <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(); } }
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? "); } } }