///// <summary>
        ///// Generate a simple ID
        ///// </summary>
        ///// <returns></returns>
        //private string generateId()
        //{
        //    int i = this.answerID.Count;
        //    do
        //    {
        //        i++;
        //    } while (this.answerID.Contains("rpt" + i));
        //    return "rpt" + i;
        //}
        private void buttonNext_Click(object sender, EventArgs e)
        {
            int min = 1;
            int defautlVal = 2;
            int max = 4;
            int step = 1;

            //log.Info(".Text:" + answerID.Text);
            //log.Info(".SelectedText:" + answerID.SelectedText);

            //log.Info(".textBoxQuestionText:" + textBoxQuestionText.Text);

            if (//string.IsNullOrWhiteSpace(this.answerID.Text) ||
                 string.IsNullOrWhiteSpace(this.textBoxQuestionText.Text))
            {
                Mbox.ShowSimpleMsgBoxError("Required data missing!");
                DialogResult = DialogResult.None; // or use on closing event; see http://stackoverflow.com/questions/2499644/preventing-a-dialog-from-closing-in-the-buttons-click-event-handler
                return;
            }
            String repeatName = this.textBoxQuestionText.Text.Trim();

            try
            {
                string foo = repeatNameToIdMap[repeatName];

                Mbox.ShowSimpleMsgBoxError("You already have a repeat named '" + repeatName
                    + "' . Click the 're-use' tab to re-use it, or choose another name.");
                DialogResult = DialogResult.None;
                return;
            }
            catch (KeyNotFoundException knfe)
            {
                // Good
            }

            // Basic data validation
            try
            {
                min = int.Parse(this.textBoxMin.Text);
                defautlVal = int.Parse(this.textBoxDefault.Text);
                max = int.Parse(this.textBoxMax.Text);
                step = int.Parse(this.textBoxRangeStep.Text);
            } catch (Exception) {
                log.Warn("Repeat range val didn't parse to int properly");
            }
            if (min < 0) min = 0;
            if (min > 20) min = 20;
            if (max < min) max = min + 4;
            if (max > 30) max = 30;
            int av = (int)Math.Round((min + max) / 2.0);
            if (defautlVal < min || defautlVal > max)
                defautlVal = av;
            if (step > av) step = 1;

            TagData td;

            // Work out where to put this repeat
            // It will just go in /answers, unless it
            // has a repeat ancestor.
            // So look for one...
            Word.ContentControl repeatAncestor = null;
            Word.ContentControl currentCC = cc.ParentContentControl;
            while (repeatAncestor == null && currentCC != null)
            {
                if (currentCC.Tag.Contains("od:repeat"))
                {
                    repeatAncestor = currentCC;
                    break;
                }
                currentCC = currentCC.ParentContentControl;
            }

            // Generate a nice ID
            // .. first we need a list of existing IDs
            List<string> reserved = new List<string>();
            foreach (question qx in questionnaire.questions)
            {
                reserved.Add(qx.id);
            }
            ID = IdHelper.SuggestID(repeatName, reserved) + "_" + IdHelper.GenerateShortID(2);

            string xpath;
            if (repeatAncestor == null)
            {
                // create it in /answers
                Office.CustomXMLNode node = answersPart.SelectSingleNode("/oda:answers");
                //string xml = "<repeat qref=\"" + ID + "\" xmlns=\"http://opendope.org/answers\"><row/></repeat>";
                string xml = "<oda:repeat qref=\"" + ID + "\" xmlns:oda=\"http://opendope.org/answers\" ><oda:row/></oda:repeat>";

                node.AppendChildSubtree(xml);
                xpath = "/oda:answers/oda:repeat[@qref='" + ID + "']/oda:row"; // avoid trailing [1]

            } else
            {
                td = new TagData(repeatAncestor.Tag);
                string ancestorRepeatXPathID = td.getRepeatID();

                // Get the XPath, to find the question ID,
                // which is what we use to find the repeat answer.
                xpathsXpath xp = xppe.getXPathByID(ancestorRepeatXPathID);

                Office.CustomXMLNode node = answersPart.SelectSingleNode("//oda:repeat[@qref='" + xp.questionID + "']/oda:row");

                string parentXPath = NodeToXPath.getXPath(node);

                //string xml = "<repeat qref=\"" + ID + "\" xmlns=\"http://opendope.org/answers\"><row/></repeat>";
                string xml = "<oda:repeat qref=\"" + ID + "\"  xmlns:oda=\"http://opendope.org/answers\" ><oda:row/></oda:repeat>";

                node.AppendChildSubtree(xml);

                xpath = parentXPath + "/oda:repeat[@qref='" + ID + "']/oda:row"; // avoid trailing [1]
            }

            log.Info(answersPart.XML);

            // Question
            q = new question();
            //q.id = this.answerID.Text; // not SelectedText
            q.id = ID;
            q.text = repeatName;

            if (!string.IsNullOrWhiteSpace(this.textBoxHelp.Text))
            {
                q.help = this.textBoxHelp.Text;
            }
            if (!string.IsNullOrWhiteSpace(this.textBoxHint.Text))
            {
                q.hint = this.textBoxHint.Text;
            }

            if (this.isAppearanceCompact())
            {
                q.appearance = appearanceType.compact;
                q.appearanceSpecified = true;
            }
            else
            {
                q.appearanceSpecified = false;
            }

            questionnaire.questions.Add(q);

            // Bind to answer XPath
            xpathsXpath xpathEntry = xppe.setup("", answersPart.Id, xpath, null, false);
            xpathEntry.questionID = q.id;
            xpathEntry.dataBinding.prefixMappings = "xmlns:oda='http://opendope.org/answers'";
            xpathEntry.type = "nonNegativeInteger";
            xppe.save();

            // Repeat
            td = new TagData("");
            td.set("od:repeat", xppe.xpathId);
            cc.Tag = td.asQueryString();

            // At this point, answer should be present.  Sanity check!

            cc.Title = "REPEAT " + repeatName;
            cc.SetPlaceholderText(null, null, "Repeating content goes here.");

            //Not for wdContentControlRichText!
            //cc.XMLMapping.SetMapping(xpath, null, model.userParts[0]);

            // Responses
            response responses = q.response;
            if (responses == null)
            {
                responses = new response();
                q.response = responses;
            }

            // MCQ: display response form
            responseFixed responseFixed = new responseFixed();
            responses.Item = responseFixed;

            //for (int i = min; i<=max; i=i+step)
            //{
            //    responseFixedItem item = new OpenDoPEModel.responseFixedItem();

            //    item.value = ""+i;
            //    item.label = "" + i;

            //    responseFixed.item.Add(item);
            //}

            responseFixed.canSelectMany = false;

            // Finally, add to part
            updateQuestionsPart();

            this.Close();
        }
        private void buttonReuseOK_Click(object sender, EventArgs e)
        {
            if (this.comboBoxRepeatNames.SelectedItem == null)
            {
                // do nothing for now.
                // TODO: grey out the OK button unless there is a selected item
                DialogResult = DialogResult.None;
                return;
            }
            string repeatName = (string)this.comboBoxRepeatNames.SelectedItem;

            // Get the ID we need to use
            String repeatId = repeatNameToIdMap[repeatName];

            TagData td = new TagData("");
            td.set("od:repeat", repeatId);
            cc.Tag = td.asQueryString();

            cc.Title = "REPEAT " + repeatName;
            cc.SetPlaceholderText(null, null, "Repeating content goes here.");

            // They could be trying to re-usw this repeat somewhere
            // outside its ancestral repeat scope.  This should handle that.
            ContentControlCopyHandler handler = new ContentControlCopyHandler();
            handler.handle(cc);
        }
        private void buttonCondition_Click(object sender, RibbonControlEventArgs e)
        {
            Word.Document document = Globals.ThisAddIn.Application.ActiveDocument;

            // Workaround for reported Word crash.
            // Can't reproduce with Word 2010 sp1: 14.0.6129.500
            Word.ContentControl currentCC = ContentControlMaker.getActiveContentControl(document, Globals.ThisAddIn.Application.Selection);
            if (currentCC != null && currentCC.Type != Word.WdContentControlType.wdContentControlRichText)
            {
                MessageBox.Show("You can't add a condition here.");
                return;
            }

            OpenDoPEModel.DesignMode designMode = new OpenDoPEModel.DesignMode(document);
            designMode.Off();

            // Find a content control within the selection
            List<Word.ContentControl> shallowChildren = ContentControlUtilities.getShallowestSelectedContentControls(document, Globals.ThisAddIn.Application.Selection);
            log.Debug(shallowChildren.Count + " shallowChildren found.");

            Word.ContentControl conditionCC = null;
            object missing = System.Type.Missing;
            try
            {
                if (Globals.ThisAddIn.Application.Selection.Type == Microsoft.Office.Interop.Word.WdSelectionType.wdSelectionIP)
                {
                    // Nothing is selected, so type "condition"
                    document.Windows[1].Selection.Text="condition";

                }
                object range = Globals.ThisAddIn.Application.Selection.Range;
                conditionCC = document.ContentControls.Add(Word.WdContentControlType.wdContentControlRichText, ref range);

                // Limitation here: you can't make your content control of eg type picture
                designMode.On();
            }
            catch (System.Exception)
            {
                MessageBox.Show("Selection must be either part of a single paragraph, or one or more whole paragraphs");
                designMode.restoreState();
                return;
            }

            conditionCC.Title = "Condition [unbound]"; // // This used if they later click edit

            if (shallowChildren.Count == 0)
            {
                log.Debug("No child control found. So Condition not set on our new CC");
                //MessageBox.Show("Unbound content control only added. Click the edit button to setup the condition.");
                editXPath(conditionCC);
                return;
            }
            // For now, just use the tag on the first simple bind we find.
            // Later, we could try parsing a condition or repeat
            Word.ContentControl usableChild = null;
            foreach (Word.ContentControl child in shallowChildren)
            {
                //if (child.Tag.Contains("od:xpath"))
                if (child.XMLMapping.IsMapped)
                {
                    usableChild = child;
                    break;
                }
            }
            if (usableChild == null)
            {
                log.Debug("No usable child found. So Condition not set on our new CC");
                //MessageBox.Show("Naked content control only added. Click the edit button to setup the condition.");
                editXPath(conditionCC);
                return;
            }

            // Get XPath. Could use the od xpaths part, but
            // easier here to get it from the binding
            string strXPath = usableChild.XMLMapping.XPath;
            log.Debug("Getting count condition from " + strXPath);
            strXPath = "count(" + strXPath + ")>0";
            log.Debug(strXPath);

            ConditionsPartEntry cpe = new ConditionsPartEntry(Model.ModelFactory(document));
            // TODO fix usableChild.XMLMapping.PrefixMappings
            cpe.setup(usableChild.XMLMapping.CustomXMLPart.Id, strXPath, "", true);
            cpe.save();

            conditionCC.Title = "Conditional: " + cpe.conditionId;
            // Write tag
            TagData td = new TagData("");
            td.set("od:condition", cpe.conditionId);
            conditionCC.Tag = td.asQueryString();

            editXPath(conditionCC);
        }
        // We can't add a tag for a repeat or a condition,
        // unless we know the XPath.  And we can't know that
        // if there is no suitable child to deduce it from.
        // But if they later press the edit button, we
        // would prefer not to have to ask them whether it
        // is a repeat or a condition. We could track the
        // content controls by their ID, but it seems
        // better to just use the Title.
        /// <summary>
        /// Wrap the selection in a Repeat.
        /// The XPath is deduced from plain binds in the contents.  
        /// If there are none of these, just insert an empty content control.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void buttonRepeat_Click(object sender, RibbonControlEventArgs e)
        {
            Word.Document document = Globals.ThisAddIn.Application.ActiveDocument;

            // Workaround for reported Word crash.
            // Can't reproduce with Word 2010 sp1: 14.0.6129.500
            Word.ContentControl currentCC = ContentControlMaker.getActiveContentControl(document, Globals.ThisAddIn.Application.Selection);
            if (currentCC != null && currentCC.Type != Word.WdContentControlType.wdContentControlRichText)
            {
                MessageBox.Show("You can't add a repeat here.");
                return;
            }

            OpenDoPEModel.DesignMode designMode = new OpenDoPEModel.DesignMode(document);
            designMode.Off();

            // Find a content control within the selection
            List<Word.ContentControl> shallowChildren = ContentControlUtilities.getShallowestSelectedContentControls(document, Globals.ThisAddIn.Application.Selection);
            log.Debug(shallowChildren.Count + " shallowChildren found.");

            Word.ContentControl repeatCC = null;
            object missing = System.Type.Missing;
            try
            {
                repeatCC = document.ContentControls.Add(Word.WdContentControlType.wdContentControlRichText, ref missing);
                // Limitation here: you can't make your content control of eg type picture
                log.Debug("New content control added, with id: " + repeatCC.ID);

                designMode.On();

            }
            catch (System.Exception) {
                MessageBox.Show("Selection must be either part of a single paragraph, or one or more whole paragraphs");
                designMode.restoreState();
                return;
            }

            repeatCC.Title = "Repeat [unbound]"; // This used if they later click edit

            if (shallowChildren.Count == 0)
            {
                log.Debug("No child control found. So Repeat not set on our new CC");
                //MessageBox.Show("Unbound content control only added. Click the edit button to setup the repeat.");
                editXPath(repeatCC);
                return;
            }
            // For now, just use the tag on the first simple bind we find.
            // Later, we could try parsing a condition or repeat
            Word.ContentControl usableChild = null;
            foreach (Word.ContentControl child in shallowChildren)
            {
                //if (child.Tag.Contains("od:xpath"))
                if (child.XMLMapping.IsMapped)
                {
                    usableChild = child;
                    break;
                }
            }
            if (usableChild == null)
            {
                log.Debug("No usable child found. So Repeat not set on our new CC");
                //MessageBox.Show("Naked content control only added. Click the edit button to setup the repeat.");
                editXPath(repeatCC);
                return;
            }

            string strXPath = null;

            // Need to work out what repeats.  Could be this child,
            // the parent, etc.  If its obvious from this exemplar xml doc,
            // we can do it automatically. Otherwise, we'll ask user.
            // Currently the logic supports repeating ., .., or grandparent.

            // See whether this child repeats in this exemplar.
            Office.CustomXMLNode thisNode = usableChild.XMLMapping.CustomXMLNode;
            Office.CustomXMLNode thisNodeSibling = usableChild.XMLMapping.CustomXMLNode.NextSibling;
            Office.CustomXMLNode parent = usableChild.XMLMapping.CustomXMLNode.ParentNode;
            Office.CustomXMLNode parentSibling = usableChild.XMLMapping.CustomXMLNode.ParentNode.NextSibling;
            if (thisNodeSibling!=null
                && thisNodeSibling.BaseName.Equals(thisNode.BaseName) ) {
                // Looks like this node repeats :-)

                    strXPath = usableChild.XMLMapping.XPath;
                        // Get XPath. Could use the od xpaths part, but
                        // easier here to work with the binding
                    log.Debug("Using . as repeat: " + strXPath);

            } // If it doesn't, test parent.
            else if (parentSibling != null
                && parentSibling.BaseName.Equals(parent.BaseName))
            {
                strXPath = usableChild.XMLMapping.XPath;
                log.Debug("Using parent for repeat ");
                strXPath = strXPath.Substring(0, strXPath.LastIndexOf("/"));
                log.Debug("Using: " + strXPath);
            }
            else // If that doesn't either, ask user.
            {
                Office.CustomXMLNode grandparent = null;
                if (parent != null)
                {
                    grandparent = parent.ParentNode;
                }

                using (Forms.FormSelectRepeatedElement sr = new Forms.FormSelectRepeatedElement())
                {
                    sr.labelXPath.Text = usableChild.XMLMapping.XPath;
                    sr.listElementNames.Items.Add(thisNode.BaseName);
                    sr.listElementNames.Items.Add(parent.BaseName);
                    if (grandparent != null)
                    {
                        sr.listElementNames.Items.Add(grandparent.BaseName);
                    }
                    sr.listElementNames.SelectedIndex = 0;
                    sr.ShowDialog();
                    if (sr.listElementNames.SelectedIndex == 0)
                    {
                        strXPath = usableChild.XMLMapping.XPath;
                    }
                    else if (sr.listElementNames.SelectedIndex == 1)
                    {
                        strXPath = parent.XPath;
                        log.Debug("Using parent for repeat: " + strXPath);
                    }
                    else
                    {
                        // Grandparent
                        strXPath = grandparent.XPath;
                        log.Debug("Using grandparent for repeat: " + strXPath);
                    }
                }
            }

            // 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(Model.ModelFactory(document));
            // TODO fix usableChild.XMLMapping.PrefixMappings
            xppe.setup("rpt", usableChild.XMLMapping.CustomXMLPart.Id, strXPath, "", false);  // No Q for repeat
            xppe.save();

            repeatCC.Title = "Repeat: " + xppe.xpathId;
            // Write tag
            TagData td = new TagData("");
            td.set("od:repeat", xppe.xpathId);
            repeatCC.Tag = td.asQueryString();
        }
        /// <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;

            // 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 = bind.SetMapping(strXPath, prefixMappings,
                    CustomXmlUtilities.getPartById(document, cxpId) );
                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
                        object missing = System.Type.Missing;
                        Globals.ThisAddIn.Application.Selection.Collapse(ref missing);
                        cc = document.ContentControls.Add(
                            Word.WdContentControlType.wdContentControlPicture, ref missing);

                        cc.XMLMapping.SetMapping(strXPath, prefixMappings,
                            CustomXmlUtilities.getPartById(document, cxpId));

                    } 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);

                            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();

                    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? ");
                }

            }
        }
        private void buttonOK_Click(object sender, EventArgs e)
        {
            string titleText = "";
            String newXPath = null;
            string pred;
            TagData td;

            if (listBoxTypeFilter.SelectedItem != null
                && listBoxTypeFilter.SelectedItem.ToString().Equals(Helpers.QuestionListHelper.REPEAT_POS))
            {
                // Special case
                newXPath = null;
                pred = (string)this.listBoxPredicate.SelectedItem;

                if (pred.Equals("first"))
                {
                    newXPath = "position()=1";
                    titleText = "If first entry in repeat";
                }
                else if (pred.Equals("not first"))
                {
                    newXPath = "position()&gt;1";
                    titleText = "If not the first entry in repeat";
                }
                else if (pred.Equals("second"))
                {
                    newXPath = "position()=2";
                    titleText = "If second entry in repeat";
                }
                else if (pred.Equals("second last"))
                {
                    newXPath = "position()=last()-1";
                    titleText = "If second last entry in repeat";

                }
                else if (pred.Equals("last"))
                {
                    newXPath = "position()=last()";
                    titleText = "If last entry in repeat";
                }
                else if (pred.Equals("not last"))
                {
                    newXPath = "position()!=last()";
                    titleText = "If not the last entry in repeat";
                }
                else
                {
                    log.Error("unexpected predicate " + pred);
                }
                // No point making this a condition

                //condition result = conditionsHelper.setup(xpathExisting.dataBinding.storeItemID,
                //    newXPath, xpathExisting.dataBinding.prefixMappings, false);

                xpathsXpath xpathEntry = xppe.setup("", model.answersPart.Id, newXPath, null, false);
                //xpathEntry.questionID = q.id;
                xpathEntry.dataBinding.prefixMappings = "xmlns:oda='http://opendope.org/answers'";
                //xpathEntry.type = dataType;
                xppe.save();

                td = new TagData("");
                td.set("od:RptPosCon", xpathEntry.id);
                cc.Tag = td.asQueryString();

                cc.Title = titleText;
                cc.SetPlaceholderText(null, null, "Type the text that'll appear between repeated items.");
                // that'll only be displayed if the cc is not being wrapped around existing content :-)

                // Don't :
                // ContentControlNewConditionCheck variableRelocator = new ContentControlNewConditionCheck();
                // variableRelocator.checkAnswerAncestry(xpathExisting.id);

                postconditionsMet = true;
                return;

            }

            xpathsXpath xpathExisting = null;
            string val = null;

            // Validation
            question q = (question)listBoxQuestions.SelectedItem;
            if (q == null)
            {
                MessageBox.Show("You must select a question!");
                DialogResult = DialogResult.None; // or use on closing event; see http://stackoverflow.com/questions/2499644/preventing-a-dialog-from-closing-in-the-buttons-click-event-handler
                return;
            }
            else
            {
                // Get the XPath for the selected question
                xpathExisting = xppe.getXPathByQuestionID(q.id);
            }

            //if (listBoxTypeFilter.SelectedItem != null
            //    && listBoxTypeFilter.SelectedItem.ToString().Equals("repeat")) {
            //        // Special case
            //    }
            //else
            //{
                //More validation

                object o = this.comboBoxValues.SelectedItem;
                if (o==null)
                {
                    if (comboBoxValues.Text == null)
                    {
                        MessageBox.Show("You must specify a value!");
                        DialogResult = DialogResult.None;
                        return;
                    }
                    else
                    {
                        o = comboBoxValues.Text;
                    }
                }
                if (o is string)
                {
                    val = (string)o;
                }
                else
                {
                    //responseFixed
                    val = ((responseFixedItem)o).value;
                }
            //}
            ConditionsPartEntry conditionsHelper = new ConditionsPartEntry(model);

            //if (xpathExisting!=null && xpathExisting.type.Equals("boolean")
            //    && (val.ToLower().Equals("true")
            //    || val.ToLower().Equals("false"))) {
            //    // if its boolean true, all we need to do is create a condition pointing to that
            //    // if its boolean false, all we need to do is create a condition not pointing to that

            //    TagData td = new TagData("");

            //    if (val.ToLower().Equals("true") )
            //    {
            //        // if its boolean true, all we need to do is create a condition pointing to that
            //        log.Info("boolean true - just need a condition");

            //        condition c = conditionsHelper.setup(xpathExisting);
            //        td.set("od:condition", c.id);
            //        cc.Tag = td.asQueryString();

            //        titleText = "If '" + val + "' for Q: " + q.text;
            //    }
            //    else if (val.ToLower().Equals("false") )
            //    {
            //        // if its boolean false, all we need to do is create a condition not pointing to that
            //        log.Info("boolean true - just need a NOT condition");
            //        condition c = new condition();

            //        xpathref xpathref = new xpathref();
            //        xpathref.id = xpathExisting.id;

            //        not n = new not();
            //        n.Item = xpathref;

            //        c.Item = n;

            //        conditionsHelper.add(c, "not" + xpathref.id);
            //        td.set("od:condition", c.id);
            //        cc.Tag = td.asQueryString();

            //        titleText = "If '" + val + "' for Q: " + q.text;
            //    }
            //    else
            //    {
            //        MessageBox.Show("Only true/yes or false/no are allowed for this question");
            //        return;
            //    }

            //} else {

                // otherwise, create a new xpath object, and a condition pointing to it
                pred = (string)this.listBoxPredicate.SelectedItem;

                if (pred == null)
                {
                    MessageBox.Show("For " + xpathExisting.type + ", you must select a relation!");
                    DialogResult = DialogResult.None;
                    return;
                }

                log.Info("create a new xpath object, and a condition pointing to it.  Predicate is " + pred);

                newXPath = null;

                if (listBoxTypeFilter.SelectedItem != null
                    && listBoxTypeFilter.SelectedItem.ToString().Equals(Helpers.QuestionListHelper.REPEAT_COUNT))
                {
                    if (pred.Equals("="))
                    {
                        newXPath = "count(" + xpathExisting.dataBinding.xpath + ")=" + val;
                        titleText = "If Repeat " + q.text + " has " + val;
                    }
                    else if (pred.Equals(">"))
                    {
                        newXPath = "count(" + xpathExisting.dataBinding.xpath + ")>" + val;
                        titleText = "If Repeat " + q.text + " > " + val;
                    }
                    else if (pred.Equals(">="))
                    {
                        newXPath = "count(" + xpathExisting.dataBinding.xpath + ")>=" + val;
                        titleText = "If Repeat " + q.text + " >= " + val;
                    }
                    else if (pred.Equals("<"))
                    {
                        newXPath = "count(" + xpathExisting.dataBinding.xpath + ")<" + val;
                        titleText = "If Repeat " + q.text + " < " + val;

                    }
                    else if (pred.Equals("<="))
                    {
                        newXPath = "count(" + xpathExisting.dataBinding.xpath + ")<=" + val;
                        titleText = "If Repeat " + q.text + " <= " + val;
                    }
                    else
                    {
                        log.Error("unexpected predicate " + pred);
                    }

                } else if (xpathExisting.type.Equals("boolean")) {

                    // done this way, since XPath spec says the boolean value of a string is true,
                    // if it is not empty!

                    newXPath = "string(" + xpathExisting.dataBinding.xpath + ")='" + val + "'";
                    titleText = "If '" + val + "' for Q: " + q.text;

                } else if (xpathExisting.type.Equals("string"))
                {
                    if (pred.Equals("equals"))
                    {
                        newXPath = "string(" + xpathExisting.dataBinding.xpath + ")='" + val + "'";
                        titleText = "If '" + val + "' for Q: " + q.text;

                    }
                    else if (pred.Equals("is not"))
                    {
                        newXPath = "string(" + xpathExisting.dataBinding.xpath + ")!='" + val + "'";
                        titleText = "If NOT '" + val + "' for Q: " + q.text;
                    }
                    else if (pred.Equals("starts-with"))
                    {
                        newXPath = "starts-with(string(" + xpathExisting.dataBinding.xpath + "), '" + val + "')";
                        titleText = "If starts-with '" + val + "' for Q: " + q.text;

                    }
                    else if (pred.Equals("contains"))
                    {
                        newXPath = "contains(string(" + xpathExisting.dataBinding.xpath + "), '" + val + "')";
                        titleText = "If contains '" + val + "' for Q: " + q.text;
                    }
                    else
                    {
                        log.Error("unexpected predicate " + pred);
                    }
                } else if (xpathExisting.type.Equals("decimal")
                    || xpathExisting.type.Equals("integer")
                    || xpathExisting.type.Equals("positiveInteger")
                    || xpathExisting.type.Equals("nonPositiveInteger")
                    || xpathExisting.type.Equals("negativeInteger")
                    || xpathExisting.type.Equals("nonNegativeInteger")
                    )
                {
                    if (pred.Equals("="))
                    {
                        newXPath = "number(" + xpathExisting.dataBinding.xpath + ")=" + val;
                        titleText = "If '" + val + "' for Q: " + q.text;
                    }
                    else if (pred.Equals(">"))
                    {
                        newXPath = "number(" + xpathExisting.dataBinding.xpath + ")>" + val;
                        titleText = "If >" + val + " for Q: " + q.text;
                    }
                    else if (pred.Equals(">="))
                    {
                        newXPath = "number(" + xpathExisting.dataBinding.xpath + ")>=" + val;
                        titleText = "If >=" + val + " for Q: " + q.text;
                    }
                    else if (pred.Equals("<"))
                    {
                        newXPath = "number(" + xpathExisting.dataBinding.xpath + ")<" + val;
                        titleText = "If <" + val + " for Q: " + q.text;

                    }
                    else if (pred.Equals("<="))
                    {
                        newXPath = "number(" + xpathExisting.dataBinding.xpath + ")<=" + val;
                        titleText = "If <=" + val + " for Q: " + q.text;
                    }
                    else
                    {
                        log.Error("unexpected predicate " + pred);
                    }

                }
                else if (xpathExisting.type.Equals("date"))
                {
                    // Requires XPath 2.0

                    if (pred.Equals("equals"))
                    {
                        newXPath = "xs:date(" + xpathExisting.dataBinding.xpath + ") = xs:date('" + val + "')";
                        titleText = "If '" + val + "' for Q: " + q.text;
                    }
                    else if (pred.Equals("is before"))
                    {
                        newXPath = "xs:date(" + xpathExisting.dataBinding.xpath + ") < xs:date('" + val + "')";
                        titleText = "If before '" + val + "' for Q: " + q.text;
                    }
                    else if (pred.Equals("is after"))
                    {
                        newXPath = "xs:date(" + xpathExisting.dataBinding.xpath + ") > xs:date('" + val + "')";
                        titleText = "If after '" + val + "' for Q: " + q.text;
                    }
                    else
                    {
                        log.Error("unexpected predicate " + pred);
                    }

                } else
                {
                    log.Error("Unexpected data type " + xpathExisting.type);
                }

                if (existingCondition == null)
                {
                    // Create new condition

                    condition result = conditionsHelper.setup(xpathExisting.dataBinding.storeItemID,
                        newXPath, xpathExisting.dataBinding.prefixMappings, false);
                    td = new TagData("");
                    td.set("od:condition", result.id);
                    cc.Tag = td.asQueryString();

                    //}

                    cc.SetPlaceholderText(null, null, "Type the text for when this condition is satisfied.");
                    // that'll only be displayed if the cc is not being wrapped around existing content :-)
                }
                else
                {
                    // Update existing condition

                    // Drop any trailing "/" from a Condition XPath
                    if (newXPath.EndsWith("/"))
                    {
                        newXPath = newXPath.Substring(0, newXPath.Length - 1);
                    }
                    log.Debug("Creating condition using XPath:" + newXPath);

                    XPathsPartEntry xppe = new XPathsPartEntry(model);
                    xpathsXpath xpath = xppe.setup("cond", xpathExisting.dataBinding.storeItemID,
                        newXPath, xpathExisting.dataBinding.prefixMappings, false);
                    xppe.save();

                    xpathref xpathref = new xpathref();
                    xpathref.id = xpath.id;

                    // NB no attempt is made here to delete the old xpathref
                    // TODO

                    existingCondition.Item = xpathref;

                    // Save the conditions in docx
                    cpe.save();

                }
                cc.Title = titleText;

                if (listBoxTypeFilter.SelectedItem != null
                    && listBoxTypeFilter.SelectedItem.ToString().Equals(Helpers.QuestionListHelper.REPEAT_COUNT))
                {
                    // Skip ContentControlNewConditionCheck
                }
                else
                {
                    // Make sure this question is allowed here
                    // ie the it is top level or in a common repeat ancestor.
                    // We do this last, so this cc has od:condition on it,
                    // in which case we can re-use existing code to do the check
                    // TODO: when we support and/or, will need to do this
                    // for each variable.
                    ContentControlNewConditionCheck variableRelocator = new ContentControlNewConditionCheck();
                    variableRelocator.checkAnswerAncestry(xpathExisting.id);
                }
            postconditionsMet = true;
        }
        /// <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();
            }
        }
        /// <summary>
        /// Add od:source=library, so later, we'll be able
        /// to use this to suppress any action in content control
        /// after add event, which fires before building block 
        /// insert.
        /// </summary>
        /// <param name="range"></param>
        public void TagsSourceAdd(Word.Range range)
        {
            foreach (Word.ContentControl cc in range.ContentControls)
            {
                if (cc.Tag == null) continue;

                TagData td = new TagData(cc.Tag);
                td.set("od:source", "library");
                cc.Tag = td.asQueryString();
            }
        }
        private void setTag(Word.ContentControl cc, condition result)
        {
            TagData td = new TagData("");
            td.set("od:condition", result.id);
            cc.Tag = td.asQueryString();

            cc.SetPlaceholderText(null, null, "Type the text for when this condition is satisfied.");
            // that'll only be displayed if the cc is not being wrapped around existing content :-)

            log.Info("Created condition " + result.Serialize());
        }
        private void buildCondition()
        {
            ConditionsPartEntry cpe = new ConditionsPartEntry(model);
            TagData td;

            if (this.dataGridView.Rows.Count == 2 // auto last row
                &&  !this.listBoxGovernor.SelectedItem.ToString().Equals("none") ) // none handled separately
            {
                // a simple condition

                if (this.dataGridView.Rows[0].Cells["Questions"].Value is condition)
                {
                    // this is just condition re-use!
                    condition cReused = (condition)this.dataGridView.Rows[0].Cells["Questions"].Value;
                    setTag(cc, cReused);
                    cc.Title = cReused.description; // that'll do for now
                    return;
                }

                // Usual case
                Pairing pair = buildXPathRef(this.dataGridView.Rows[0]);
                cc.Title = restrict64chars(pair.titleText);

                if (pair.xpathEntry.dataBinding.xpath.Contains("position()"))
                {
                    // special case.  TODO: make this a normal condition!
                    // since this approach won't work if it is in complex condition
                    td = new TagData("");
                    td.set("od:RptPosCon", pair.xpathEntry.id);
                    cc.Tag = td.asQueryString();

                    cc.SetPlaceholderText(null, null, "Type the text that'll appear between repeated items.");
                    // that'll only be displayed if the cc is not being wrapped around existing content :-)
                    return;
                }

                condition result = cpe.setup(pair.xpathEntry);
                result.name = this.textBoxName.Text;
                if (string.IsNullOrWhiteSpace(this.textBoxDescription.Text))
                {
                    result.description = pair.titleText;
                }
                else
                {
                    result.description = this.textBoxDescription.Text;
                }
                cpe.save();

                setTag(cc, result);

                return;
            }

            // multi-row
            int last = this.dataGridView.Rows.Count - 1;
            condition outer = new condition();
            cc.Title = null;
            if (this.listBoxGovernor.SelectedItem.ToString().Equals("all"))
            {
                // = and
                and and = new and();
                outer.Item = and;

                foreach (DataGridViewRow row in this.dataGridView.Rows)
                {
                    // Last row is added automatically
                    if (row == this.dataGridView.Rows[last]) continue;

                    if (row.Cells["Questions"].Value is condition)
                    {
                        // this is just condition re-use!
                        condition cReused = (condition)row.Cells["Questions"].Value;

                        if (cc.Title == null)
                        {
                            cc.Title = this.restrict64chars(cReused.description); // that'll do for now
                        }
                        else
                        {
                            cc.Title = this.restrict64chars(cc.Title + " and " + cReused.description); // that'll do for now
                        }

                        conditionref conditionref = new conditionref();
                        conditionref.id = cReused.id;
                        and.Items.Add(conditionref);

                    }
                    else
                    {
                        // xpathref
                        Pairing pair = buildXPathRef(row);

                        if (cc.Title == null)
                        {
                            cc.Title = this.restrict64chars(pair.titleText);
                        }
                        else
                        {
                            cc.Title = this.restrict64chars(cc.Title + " and " + pair.titleText);
                        }

                        xpathref xpathref = new xpathref();
                        xpathref.id = pair.xpathEntry.id;

                        and.Items.Add(xpathref);
                    }
                }

                outer.name = this.textBoxName.Text;
                if (string.IsNullOrWhiteSpace(this.textBoxDescription.Text))
                {
                    outer.description = cc.Title;
                }
                else
                {
                    outer.description = this.textBoxDescription.Text;
                }

                cpe.add(outer, null);
                cpe.save();

                setTag(cc, outer);

                return;
            }

            if (this.listBoxGovernor.SelectedItem.ToString().Equals("any") ) {
                // = or
                or or = new or();
                outer.Item = or;

                foreach (DataGridViewRow row in this.dataGridView.Rows)
                {
                    // Last row is added automatically
                    if (row == this.dataGridView.Rows[last]) continue;

                    if (row.Cells["Questions"].Value is condition)
                    {
                        // this is just condition re-use!
                        condition cReused = (condition)row.Cells["Questions"].Value;

                        if (cc.Title == null)
                        {
                            cc.Title = this.restrict64chars(cReused.description); // that'll do for now
                        }
                        else
                        {
                            cc.Title = this.restrict64chars(cc.Title + " and " + cReused.description); // that'll do for now
                        }

                        conditionref conditionref = new conditionref();
                        conditionref.id = cReused.id;
                        or.Items.Add(conditionref);

                    }
                    else
                    {

                        Pairing pair = buildXPathRef(row);

                        if (cc.Title == null)
                        {
                            cc.Title = this.restrict64chars(pair.titleText);
                        }
                        else
                        {
                            cc.Title = this.restrict64chars(cc.Title + " or " + pair.titleText);
                        }

                        xpathref xpathref = new xpathref();
                        xpathref.id = pair.xpathEntry.id;

                        or.Items.Add(xpathref);
                    }
                }

                outer.name = this.textBoxName.Text;
                if (string.IsNullOrWhiteSpace(this.textBoxDescription.Text))
                {
                    outer.description = cc.Title;
                }
                else
                {
                    outer.description = this.textBoxDescription.Text;
                }

                cpe.add(outer, null);
                cpe.save();

                setTag(cc, outer);

                return;
            }

            if (this.listBoxGovernor.SelectedItem.ToString().Equals("none"))
            {
                // none:  not(A || B) = !A && !B
                not not = new not();
                outer.Item = not;

                or or = new or();
                not.Item = or;

                cc.Title = "NONE OF ";
                foreach (DataGridViewRow row in this.dataGridView.Rows)
                {
                    // Last row is added automatically
                    if (row == this.dataGridView.Rows[last]) continue;
                    if (row.Cells["Questions"].Value is condition)
                    {
                        // this is just condition re-use!
                        condition cReused = (condition)row.Cells["Questions"].Value;

                        if (cc.Title == null)
                        {
                            cc.Title = this.restrict64chars(cReused.description); // that'll do for now
                        }
                        else
                        {
                            cc.Title = this.restrict64chars(cc.Title + " and " + cReused.description); // that'll do for now
                        }

                        conditionref conditionref = new conditionref();
                        conditionref.id = cReused.id;
                        or.Items.Add(conditionref);

                    }
                    else
                    {

                        Pairing pair = buildXPathRef(row);

                        if (cc.Title.Equals("NONE OF "))
                        {
                            cc.Title = this.restrict64chars(cc.Title + pair.titleText);
                        }
                        else
                        {
                            cc.Title = this.restrict64chars(cc.Title + " or " + pair.titleText);
                        }

                        xpathref xpathref = new xpathref();
                        xpathref.id = pair.xpathEntry.id;

                        or.Items.Add(xpathref);
                    }
                }

                outer.name = this.textBoxName.Text;
                if (string.IsNullOrWhiteSpace(this.textBoxDescription.Text))
                {
                    outer.description = cc.Title;
                }
                else
                {
                    outer.description = this.textBoxDescription.Text;
                }

                cpe.add(outer, null);
                cpe.save();

                setTag(cc, outer);

                return;
            }

            //// Make sure this question is allowed here
            //// ie the it is top level or in a common repeat ancestor.
            //// We do this last, so this cc has od:condition on it,
            //// in which case we can re-use existing code to do the check
            //// TODO: when we support and/or, will need to do this
            //// for each variable.
            //ContentControlNewConditionCheck variableRelocator = new ContentControlNewConditionCheck();
            //variableRelocator.checkAnswerAncestry(xpathExisting.id);
        }
        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>
        /// Create OpenDoPE parts, including optionally, question part.
        /// </summary>
        public void process()
        {
            Microsoft.Office.Interop.Word.Document document = null;
            try
            {
                document = Globals.ThisAddIn.Application.ActiveDocument;
            }
            catch (Exception ex)
            {
                Mbox.ShowSimpleMsgBoxError("No document is open/active. Create or open a docx first.");
                return;
            }

            Model model = Model.ModelFactory(document);

            // Button shouldn't be available if this exists,
            // but ..
            if (model.conditionsPart == null)
            {
                conditions conditions = new conditions();
                string conditionsXml = conditions.Serialize();
                model.conditionsPart = addCustomXmlPart(document, conditionsXml);
            }

            if (model.componentsPart == null)
            {
                components components = new components();
                string componentsXml = components.Serialize();
                model.componentsPart = addCustomXmlPart(document, componentsXml);
            }

            // Add XPath
            xpaths xpaths = new xpaths();
            // Button shouldn't be available if this exists,
            // but ..
            if (model.xpathsPart != null)
            {
                xpaths.Deserialize(model.xpathsPart.XML, out xpaths);
            }
            int idInt = 1;
            foreach (Word.ContentControl cc in Globals.ThisAddIn.Application.ActiveDocument.ContentControls)
            {
                if (cc.XMLMapping.IsMapped)
                {
                    log.Debug("Adding xpath for " + cc.ID);
                    // then we need to add an XPath
                    string xmXpath = cc.XMLMapping.XPath;

                    xpathsXpath item = new xpathsXpath();
                    // I make no effort here to check whether the xpath
                    // already exists, since the part shouldn't already exist!

                    item.id = "x" + idInt;

                    xpathsXpathDataBinding db = new xpathsXpathDataBinding();
                    db.xpath = xmXpath;
                    db.storeItemID = cc.XMLMapping.CustomXMLPart.Id;
                    if (!string.IsNullOrWhiteSpace(cc.XMLMapping.PrefixMappings))
                        db.prefixMappings = cc.XMLMapping.PrefixMappings;
                    item.dataBinding = db;

                    xpaths.xpath.Add(item);

                    // Write tag
                    TagData td = new TagData(cc.Tag);
                    td.set("od:xpath", item.id);
                    cc.Tag = td.asQueryString();

                    log.Debug(".. added for " + cc.ID);
                    idInt++;
                }
            }
            string xpathsXml = xpaths.Serialize();
            if (model.xpathsPart == null)
            {
                model.xpathsPart = addCustomXmlPart(document, xpathsXml);
            }
            else
            {
                CustomXmlUtilities.replaceXmlDoc(model.xpathsPart, xpathsXml);
            }

            Microsoft.Office.Tools.Word.Document extendedDocument
                = Globals.ThisAddIn.Application.ActiveDocument.GetVstoObject(Globals.Factory);

            //Microsoft.Office.Tools.CustomTaskPane ctp
            //    = Globals.ThisAddIn.createCTP(document, cxp, xpathsPart, conditionsPart, questionsPart, componentsPart);
            //extendedDocument.Tag = ctp;
            //// Want a 2 way association
            //WedTaskPane wedTaskPane = (WedTaskPane)ctp.Control;
            //wedTaskPane.associatedDocument = document;

            //extendedDocument.Shutdown += new EventHandler(
            //    Globals.ThisAddIn.extendedDocument_Shutdown);

            //taskPane.setupCcEvents(document);

            log.Debug("Done. Task pane now also open.");
        }