//Word.ContentControl copiedCC)
        public ContentControlHandlerAbstract()
        {
            fabDocxState = (FabDocxState)Globals.ThisAddIn.Application.ActiveDocument.GetVstoObject(Globals.Factory).Tag;
            //this.copiedCC = copiedCC;

            model = fabDocxState.model;
            xppe = new XPathsPartEntry(model); // used to get entries
            cpe = new ConditionsPartEntry(model);
        }
        public QuestionListHelper(Model model, XPathsPartEntry xppe, questionnaire questionnaire, 
                    Word.ContentControl cc)
        {
            this.model = model;
            this.xppe = xppe;
            this.questionnaire = questionnaire;

            this.cc = cc;
        }
        public FormMoveQuestion(Office.CustomXMLPart answersPart,
            List<Word.ContentControl> relevantRepeats,
            questionnaire questionnaire, 
            string questionID,
            XPathsPartEntry xppe)
        {
            InitializeComponent();

            this.textBoxQuestion.Text = questionnaire.getQuestion(questionID).text;

            this.controlQuestionVaryWhichRepeat1.init(answersPart,
                relevantRepeats,
                questionnaire, questionID,
                xppe);
        }
        public static bool doesConditionUseQuestion(XPathsPartEntry xppe, conditions conditions, condition c, string questionID)
        {
            //List<xpathsXpath> xpaths = ConditionsPartEntry.getXPathsUsedInCondition(c, xppe);
            List<xpathsXpath> xpaths = new List<xpathsXpath>();
            c.listXPaths(xpaths, conditions, xppe.getXPaths());

            foreach (xpathsXpath xpathObj in xpaths)
            {

                String xpathVal = xpathObj.dataBinding.xpath;

                if (xpathVal.StartsWith("/"))
                {
                    // simple
                    //System.out.println("question " + xpathObj.getQuestionID()
                    //        + " is in use via boolean condition " + conditionId);
                    if (xpathObj.questionID.Equals(questionID))
                    {
                        return true;
                    }

                }
                else if (xpathVal.Contains("position"))
                {
                    continue;
                }
                else
                {
                    //System.out.println(xpathVal);

                    String qid = xpathVal.Substring(
                        xpathVal.LastIndexOf("@id") + 5);
                    //						System.out.println("Got qid: " + qid);
                    qid = qid.Substring(0, qid.IndexOf("'"));
                    //						System.out.println("Got qid: " + qid);

                    //System.out.println("question " + qid
                    //        + " is in use via condition " + conditionId);

                    if (qid.Equals(questionID))
                    {
                        return true;
                    }

                }
            }
            return false;
        }
        /// <summary>
        /// Add xpath to xpaths part, and a condition to the conditions part.
        /// </summary>
        /// <param name="model"></param>
        /// <param name="cxpId"></param>
        /// <param name="strXPath"></param>
        /// <param name="prefixMappings"></param>
        public condition setup(string cxpId, string strXPath, string prefixMappings,
                               bool setupQuestionNow)
        {
            //////////////////////////
            // First, add the new XPath

            // Drop any trailing "/" from a Condition XPath
            if (strXPath.EndsWith("/"))
            {
                strXPath = strXPath.Substring(0, strXPath.Length - 1);
                log.Debug("truncated to " + strXPath);
            }

            XPathsPartEntry xppe  = new XPathsPartEntry(model);
            xpathsXpath     xpath = xppe.setup("cond", cxpId, strXPath, prefixMappings, setupQuestionNow);

            xppe.save();

            return(setup(xpath));
        }
        public FormConditionBuilder(Word.ContentControl cc, ConditionsPartEntry cpe, condition existingCondition)
        {
            InitializeComponent();

            // NET 4 way; see http://msdn.microsoft.com/en-us/library/microsoft.office.tools.word.extensions.aspx
            FabDocxState fabDocxState = (FabDocxState)Globals.Factory.GetVstoObject(Globals.ThisAddIn.Application.ActiveDocument).Tag;

            // NET 3.5 way, which requires using Microsoft.Office.Tools.Word.Extensions
            //FabDocxState fabDocxState = (FabDocxState)Globals.ThisAddIn.Application.ActiveDocument.GetVstoObject(Globals.Factory).Tag;
            this.model = fabDocxState.model;
            xppe = new XPathsPartEntry(model);

            this.cc = cc;

            this.cpe = cpe;
            this.existingCondition = existingCondition;

            this.questionsPart = model.questionsPart;
            questionnaire qtmp = new questionnaire();
            questionnaire.Deserialize(questionsPart.XML, out qtmp);
            questionnaire = qtmp;

            conditions ctmp = new conditions();
            conditions.Deserialize(model.conditionsPart.XML, out ctmp);
            conditions = ctmp;

            log.Debug("conditions: " + conditions.Serialize());

            this.listBoxGovernor.Items.Add("all");
            this.listBoxGovernor.Items.Add("any");
            this.listBoxGovernor.Items.Add("none");
            this.listBoxGovernor.SelectedItem = "all";

            rowHelper = new Helpers.ConditionsFormRowHelper(model, xppe, questionnaire, cc, this);

            rowHelper.init(this.dataGridView);

            DataGridViewRow row = this.dataGridView.Rows[0];
            rowHelper.populateRow(row, null, null);
        }
예제 #7
0
        public void buttonFormat_Click(Office.IRibbonControl control)
        {
            /* You can only format a date or a number here.
             *
             * For a date, we use cc date stuff:
             *
                <w:sdt>
                  <w:sdtPr>
                    <w:date w:fullDate="2012-07-13T00:00:00Z">
                      <w:dateFormat w:val="dddd, d MMMM yyyy"/>
                      <w:lid w:val="en-AU"/>
                      <w:storeMappedDataAs w:val="date"/>
                      <w:calendar w:val="gregorian"/>
                    </w:date>
                  </w:sdtPr>
             *
             * For a number, ...
             */

            if (currentCC.Tag != null)
            {
                String XPathID = (new TagData(currentCC.Tag)).getXPathID();
                if (XPathID != null)
                {
                    // Get the xpath obj
                    FabDocxState fabDocxState = getState();
                    XPathsPartEntry xppe = new XPathsPartEntry(fabDocxState.model);

                    xpathsXpath xpo = xppe.getXPathByID(XPathID);

                    if (xpo.type != null)
                    {
                        if (xpo.type.Equals("date"))
                        {
                            Forms.FormFormatDate formDate = new Forms.FormFormatDate(currentCC);
                            formDate.ShowDialog();
                            formDate.Dispose();
                        }

                        //else if (xpo.type.Equals("decimal")
                        //    || xpo.type.Equals("integer")
                        //    || xpo.type.Equals("positiveInteger")
                        //    || xpo.type.Equals("nonPositiveInteger")
                        //    || xpo.type.Equals("negativeInteger")
                        //    || xpo.type.Equals("nonNegativeInteger")
                        //    )
                        //{
                        //    return true;
                        //}
                    }
                }
            }
        }
        private void injectLogicXPath(XPathsPartEntry targetXppe, xpathsXpath xp, string sourceAttr, string answersPartStoreID)
        {
            if (sourceAttr != null) xp.source = sourceAttr;

            if (answersPartStoreID != null)
            {
                xp.dataBinding.storeItemID = answersPartStoreID;
            }

            targetXppe.getXPaths().xpath.Add(xp);  // this is a HashSet, so we're overrwriting, not adding :-)
        }
        private void QuestionCreateNew()
        {
            FormQA formQA = new FormQA(cc, false);
            formQA.ShowDialog();
            question q = formQA.getQuestion();
            formQA.Dispose();

            // Refresh these
            xppe = new XPathsPartEntry(model);
            fcb.xppe = xppe;

            questionnaire.Deserialize(model.questionsPart.XML, out questionnaire);
            fcb.questionnaire = questionnaire;

            // How do we know what the new question was? Just refresh..
            //listBoxTypeFilter.Value = "ALL";
            //populateQuestions("ALL");
            //dataGridView.RefreshEdit(); //critical

            listBoxQuestions.Items.Add(q);
            listBoxQuestions.Value = q;
            //myCachedComboBox.SelectedItem = q;
            dataGridView.RefreshEdit(); //critical
            log.Debug("set question " + q);

            //log.Debug("Repopulating row " + rowIndex);
            //DataGridViewRow row = dataGridView.Rows[rowIndex];
            //populateRow(row, null, null);
            //dataGridView.RefreshEdit();
        }
        private void init1()
        {
            FabDocxState fabDocxState = (FabDocxState)Globals.ThisAddIn.Application.ActiveDocument.GetVstoObject(Globals.Factory).Tag;
            this.model = fabDocxState.model;
            xppe = new XPathsPartEntry(model);

            this.questionsPart = model.questionsPart;
            questionnaire = new questionnaire();
            questionnaire.Deserialize(questionsPart.XML, out questionnaire);

            this.answersPart = model.answersPart;
        }
예제 #11
0
        public FormRepeat(Office.CustomXMLPart questionsPart,
            Office.CustomXMLPart answersPart,
            Model model,
            Word.ContentControl cc)
        {
            InitializeComponent();

            this.model = model;
            this.cc = cc;

            this.questionsPart = questionsPart;
            questionnaire = new questionnaire();
            questionnaire.Deserialize(questionsPart.XML, out questionnaire);

            this.answersPart = answersPart;

            Office.CustomXMLNodes answers = answersPart.SelectNodes("//oda:repeat");
            foreach (Office.CustomXMLNode answer in answers)
            {
                this.answerID.Add(CustomXMLNodeHelper.getAttribute(answer, "qref")); // ID
            }

            // Suggest ID .. the idea is that
            // the question ID = answer ID.
               // this.ID = generateId();

            xppe = new XPathsPartEntry(model);
            // List of repeat names, for re-use purposes
            // (we need a map from repeat name (shown in the UI) to repeat id,
            //  which is xppe.xpathId).
            // What is it that distinguishes a repeat from any other question?
            // Answer: The fact that the XPath pointing to it ends with oda:row

            // Only show repeats which are in scope (since this repeat is not allowed elsewhere)
            // - if no ancestor repeat, then top level repeats.
            // - if there is an ancestor repeat, then just those which are direct children of it.
            Word.ContentControl rptAncestor = RepeatHelper.getYoungestRepeatAncestor(cc);
            String scope = "/oda:answers";
            if (rptAncestor != null)
            {
                // find its XPath
                scope = xppe.getXPathByID(  (new TagData(rptAncestor.Tag)).getRepeatID() ).dataBinding.xpath;

            }

            repeatNameToIdMap = new Dictionary<string, string>();
            foreach (xpathsXpath xpath in xppe.getXPaths().xpath)
            {
                if (xpath.dataBinding.xpath.EndsWith("oda:row"))
                {
                    if (isRepeatInScope(scope, xpath.dataBinding.xpath)) {
                        // the repeat "name" is its question text.
                        // Get that.
                        question q = questionnaire.getQuestion(xpath.questionID);
                        repeatNameToIdMap.Add(q.text, xpath.id);
                    }
                }
            }
            // Populate the comboBox
            foreach(KeyValuePair<String,String> entry in repeatNameToIdMap)
            {
                this.comboBoxRepeatNames.Items.Add(entry.Key);
            }
        }
        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? ");
                }

            }
        }
 public QuestionHelper(XPathsPartEntry xppe, ConditionsPartEntry cpe)
 {
     this.xppe = xppe;
     this.cpe = cpe;
 }
예제 #14
0
        /// <summary>
        /// Add xpath to xpaths part, and a condition to the conditions part.  
        /// </summary>
        /// <param name="model"></param>
        /// <param name="cxpId"></param>
        /// <param name="strXPath"></param>
        /// <param name="prefixMappings"></param>
        public condition setup(string cxpId, string strXPath, string prefixMappings,
            bool setupQuestionNow)
        {
            //////////////////////////
            // First, add the new XPath

            // Drop any trailing "/" from a Condition XPath
            if (strXPath.EndsWith("/"))
            {
                strXPath = strXPath.Substring(0, strXPath.Length - 1);
                log.Debug("truncated to " + strXPath);
            }

            XPathsPartEntry xppe = new XPathsPartEntry(model);
            xpathsXpath xpath = xppe.setup("cond", cxpId, strXPath, prefixMappings, setupQuestionNow);
            xppe.save();

            return setup(xpath);
        }
        public void init(
            Office.CustomXMLPart answersPart,
            List<Word.ContentControl> relevantRepeats,
            questionnaire questionnaire, 
            string questionID,
            XPathsPartEntry xppe)
        {
            List<Office.CustomXMLNode> commonAncestors =null;
            foreach (Word.ContentControl repeat in relevantRepeats)
            {
                log.Info("considering relevantRepeat cc " + repeat.ID + " " + repeat.Tag);
                TagData repeatTD = new TagData(repeat.Tag);
                string repeatXPathID = repeatTD.getRepeatID();
                xpathsXpath repeatXP = xppe.getXPathByID(repeatXPathID);
                Office.CustomXMLNode repeatNode = answersPart.SelectSingleNode(repeatXP.dataBinding.xpath).ParentNode;

                if (commonAncestors == null)
                {
                    // First entry, so init
                    // Make a list of the ancestors of the
                    // first repeat.
                    commonAncestors = new List<Microsoft.Office.Core.CustomXMLNode>();
                    commonAncestors.Add(repeatNode);
                    log.Info("Added to common ancestors " + CustomXMLNodeHelper.getAttribute(repeatNode, "qref"));
                    addAncestors(commonAncestors, repeatNode);
                }
                else
                {
                    // cross off that list anything
                    // which isn't an ancestor of the other repeats.
                    List<Microsoft.Office.Core.CustomXMLNode> whitelist = new List<Microsoft.Office.Core.CustomXMLNode>();
                    whitelist.Add(repeatNode);
                    addAncestors(whitelist, repeatNode);
                    removeNonCommonAncestor(commonAncestors, whitelist);
                }

                if (commonAncestors.Count == 0) break;
            }
            if (commonAncestors == null)
            {
                    commonAncestors = new List<Microsoft.Office.Core.CustomXMLNode>();
            }

            // Is it OK where it is?
            // Yes - if it is top level
            log.Debug(questionID + " --> " + xppe.getXPathByQuestionID(questionID).dataBinding.xpath);
            // eg /oda:answers/oda:repeat[@qref='rpt1']/oda:row[1]/oda:answer[@id='qa_2']
            OkAsis = (xppe.getXPathByQuestionID(questionID).dataBinding.xpath.IndexOf("oda:repeat") < 0);
            Microsoft.Office.Core.CustomXMLNode currentPos = null; // so we can highlight existing choice
            // Yes - if it is a child of common ancestors
            if (OkAsis)
            {
                log.Debug("its top level");
            }
            else
            {
                foreach (Microsoft.Office.Core.CustomXMLNode currentNode in commonAncestors)
                {
                    Microsoft.Office.Core.CustomXMLNode selection =
                        currentNode.SelectSingleNode("oda:row[1]/oda:answer[@id='" + questionID + "']");
                    if (selection != null)
                    {
                        log.Debug("found it");
                        OkAsis = true;
                        currentPos = currentNode;
                        break;
                    }
                }
            }

            // Now make the tree from what is left in commonAncestors

            root = new TreeNode("Ask only once");
            this.treeViewRepeat.Nodes.Add(root);

            TreeNode thisNode = null;
            TreeNode previousNode = null;
            treeViewRepeat.HideSelection = false; // keep selection when focus is lost

            TreeNode nodeToSelect = null;

            foreach (Microsoft.Office.Core.CustomXMLNode currentNode in commonAncestors)
            {
                // Find the question associated with this repeat
                string rptQRef = CustomXMLNodeHelper.getAttribute(currentNode, "qref");
                    //currentNode.Attributes[1].NodeValue;
                question q = questionnaire.getQuestion(rptQRef);
                if (q == null)
                {
                    log.Error("no question with id " + rptQRef);
                }
                thisNode = new TreeNode(q.text);
                thisNode.Tag = rptQRef;

                if (currentNode == currentPos)
                {
                    nodeToSelect = thisNode;
                }

                if (previousNode == null)
                {
                    // Check the innermost (may be overridden below by what level user already had, if possible)
                    this.treeViewRepeat.SelectedNode = thisNode;
                }
                else
                {
                    thisNode.Nodes.Add(previousNode);
                }
                previousNode = thisNode;

            }

            if (thisNode != null)
            {
                root.Nodes.Add(thisNode);
            }
            treeViewRepeat.ExpandAll();

            if (nodeToSelect != null)
            {
                this.treeViewRepeat.SelectedNode = nodeToSelect;
                originalValue = thisNode;
            }
            else if (OkAsis)
            {
                originalValue = root;
                this.treeViewRepeat.SelectedNode = root;
            }
        }
        public void init(
            Office.CustomXMLPart answersPart,
            questionnaire questionnaire,
            question q,
            XPathsPartEntry xppe,
            ConditionsPartEntry cpe)
        {
            QuestionHelper qh = new QuestionHelper(xppe, cpe);
            thisQuestionControls = qh.getControlsUsingQuestion(q);

            List<Word.ContentControl> relevantRepeats = new List<Word.ContentControl>();
            foreach (Word.ContentControl ccx in thisQuestionControls)
            {
                Word.ContentControl rpt = RepeatHelper.getYoungestRepeatAncestor(ccx);
                if (rpt == null)
                {
                    // will have to make the answer top level and we're done.
                    break;
                }
                else
                {
                    relevantRepeats.Add(rpt);
                }
            }

            init(
                answersPart,
                relevantRepeats,
                 questionnaire,
                 q.id,
                 xppe);
        }
        /// <summary>
        /// When question is first being added, it is being added to a particular CC,
        /// so we only need to consider that CC's ancestors.
        /// 
        /// When a question is being edited, it could be in several content controls,
        /// so that case is handled by the more generic method.
        /// </summary>
        /// <param name="cc"></param>
        /// <param name="questionnaire"></param>
        /// <param name="xppe"></param>
        public void init(Word.ContentControl cc, questionnaire questionnaire, XPathsPartEntry xppe)
        {
            root = new TreeNode("Ask only once");

            Word.ContentControl currentCC = cc.ParentContentControl;

            TreeNode thisNode = null;
            TreeNode previousNode = null;
            treeViewRepeat.HideSelection = false; // keep selection when focus is lost

            while (currentCC != null)
            {
                if (currentCC.Tag.Contains("od:repeat"))
                {
                    TagData td = new TagData(currentCC.Tag);
                    string ancestorRepeatXPathID = td.getRepeatID();

                    // Find associated question
                    xpathsXpath xp = xppe.getXPathByID(ancestorRepeatXPathID);
                    question q = questionnaire.getQuestion(xp.questionID);

                    thisNode = new TreeNode(q.text);
                    thisNode.Tag = ancestorRepeatXPathID;

                    if (previousNode == null)
                    {
                        // Check the innermost
                        treeViewRepeat.SelectedNode = thisNode;
                    }
                    else
                    {
                        thisNode.Nodes.Add(previousNode);
                    }
                    previousNode = thisNode;
                }
                currentCC = currentCC.ParentContentControl;
            }

            if (thisNode == null)
            {
                // Hide the control
            //                this.groupBoxRepeat.Visible = false;
                root = null;
            }
            else
            {
                root.Nodes.Add(thisNode);
                this.treeViewRepeat.Nodes.Add(root);
                treeViewRepeat.ExpandAll();
            }
        }
        public LibraryHelper(Model srcModel)
        {
            srcXppe = new XPathsPartEntry(srcModel); // used to get entries
            this.srcXPathsPart = srcModel.xpathsPart;

            srcCpe = new ConditionsPartEntry(srcModel);
            this.srcConditionsPart = srcModel.conditionsPart;

            this.srcQuestionsPart = srcModel.questionsPart;
            srcQuestionnaire = new questionnaire();
            questionnaire.Deserialize(srcQuestionsPart.XML, out srcQuestionnaire);

            srcAnswersPart = srcModel.answersPart;
            srcAnswers = new answers();
            answers.Deserialize(srcAnswersPart.XML, out srcAnswers);
        }
        /// <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>
        /// 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();
            }
        }
        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;
        }
        // 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();
        }
        private void buttonQuestionAdd_Click(object sender, EventArgs e)
        {
            FormQA formQA = new FormQA(cc, false);
            formQA.ShowDialog();
            formQA.Dispose();

            // Refresh these
            xppe = new XPathsPartEntry(model);
            questionnaire.Deserialize(questionsPart.XML, out questionnaire);

            questionListHelper.filterAction();
        }
        public void buttonRepeat_Click(Office.IRibbonControl control)
        {
            Word.Document document = Globals.ThisAddIn.Application.ActiveDocument;

            FabDocxState fabDocxState = (FabDocxState)Globals.ThisAddIn.Application.ActiveDocument.GetVstoObject(Globals.Factory).Tag;

            Model model = fabDocxState.model;
            Office.CustomXMLPart answersPart = model.answersPart; //.userParts[0]; // TODO: make this better
            XPathsPartEntry xppe = new XPathsPartEntry(model); // used to get entries

            questionnaire questionnaire = new questionnaire();
            questionnaire.Deserialize(model.questionsPart.XML, out questionnaire);

            Microsoft.Office.Interop.Word.Range rng = document.ActiveWindow.Selection.Range;

            // Are there any content controls in the selection?
            Word.ContentControls ccs = rng.ContentControls;

            // First identify nested repeats
            List<Word.ContentControl> nestedRepeats = new List<Word.ContentControl>();
            foreach (Word.ContentControl desc in ccs)
            {
                if (desc.Tag.Contains("od:repeat"))
                {
                    nestedRepeats.Add(desc);
                }
            }

            // questions contains questions wrapped by repeat,
            // but not nested inside another repeat
            List<question> questions = new List<question>();

            foreach (Word.ContentControl desc in ccs)
            {
                if (desc.Tag.Contains("od:repeat"))
                {
                    continue; // will handle repeats later
                }

                // exclude if in nested repeat, since
                // this question will have previously been dealt with
                // (ie it already varies with that repeat, or the
                // use has said they don't want it to)
                if (isInside(nestedRepeats, desc))
                {
                    continue;
                }

                //log.Warn("got a desc with tag " + desc.Tag);
                // Get the tag
                if (desc.Tag.Contains("od:xpath"))
                {
                    TagData td = new TagData(desc.Tag);
                    string xpathID = td.getXPathID();
                    //log.Warn("xpath is  " + xpathID);
                    xpathsXpath xp = xppe.getXPathByID(xpathID);
                    log.Warn("qid is  " + xp.questionID);
                    question q = questionnaire.getQuestion(xp.questionID);
                    if (q == null)
                    {
                        log.Error("Consistency issue: couldn't find question {0} used in xpath {1}", xp.questionID, xpathID);
                    }
                    else
                    {
                        questions.Add(q);
                    }
                }
                else if (desc.Tag.Contains("od:condition"))
                {
                    // TODO: find questions inside conditions
                }
            }

            if (questions.Count > 0)
            {
                // Rule: Only questions which aren't used elsewhere can vary in a repeat.
                // Check that none of the questions that will be
                // inside the repeat are also used outside of it.
                List<question> questionsUsedOutside = new List<question>();
                foreach (Word.ContentControl ccx in document.ContentControls)
                {
                    if (isListed(ccs, ccx))
                    {
                        // this control is inside the repeat
                    }
                    else
                    {
                        // its outside, so look at its question
                        // TODO: conditions, repeats
                        if (ccx.Tag.Contains("od:xpath"))
                        {
                            TagData td = new TagData(ccx.Tag);
                            string xpathID = td.getXPathID();
                            //log.Warn("xpath is  " + xpathID);
                            xpathsXpath xp = xppe.getXPathByID(xpathID);
                            //log.Warn("qid is  " + xp.questionID);
                            question q = questionnaire.getQuestion(xp.questionID);
                            if (q == null)
                            {
                                log.Error("Consistency issue: couldn't find question {0} used in xpath {1}", xp.questionID, xpathID);
                            }
                            else
                            {
                                if (questions.Contains(q))
                                {
                                    questionsUsedOutside.Add(q);
                                }
                            }
                        }
                    }
                } // foreach

                // If they are, they can't vary in repeat.  Get the user to OK this.
                if (questionsUsedOutside.Count == 0)
                {
                    log.Info("None of the questions in wrapping repeat are used elsewhere");
                }
                else
                {
                    log.Info(questionsUsedOutside.Count + " of the questions in wrapping repeat are used elsewhere");
                    DialogResult dresult = MessageBox.Show(
                        questionsUsedOutside.Count + " of the questions here are also used elsewhere. If you continue, these won't vary in each repeat.",
                        "Questions used elsewhere", MessageBoxButtons.OKCancel);
                    if (dresult == DialogResult.OK)
                    {
                        // Just remove them from the list
                        foreach (question qx in questionsUsedOutside)
                        {
                            questions.Remove(qx);
                        }
                    }
                    else
                    {
                        log.Info("User cancelled wrapping repeat coz questions used elsewhere");
                        return;
                    }
                }
            }

            // Create control
            Word.ContentControl wrappingRepeatCC = null;
            object oRng = rng;
            try
            {
                fabDocxState.inPlutextAdd = true;
                wrappingRepeatCC = document.ContentControls.Add(Word.WdContentControlType.wdContentControlRichText, ref oRng);
                //cc.MultiLine = true; // Causes error for RichText
            }
            catch (System.Exception ex)
            {
                log.Warn(ex);
                MessageBox.Show("Selection must be either part of a single paragraph, or one or more whole paragraphs");
                fabDocxState.inPlutextAdd = false;
                return;
            }

            FormRepeat formRepeat = new FormRepeat(model.questionsPart, answersPart, model, wrappingRepeatCC);
            formRepeat.ShowDialog();
            string repeatId = formRepeat.ID;
            formRepeat.Dispose();

            // necessary here? shouldn't be..
            //answersPart.NamespaceManager.AddNamespace("answers", "http://opendope.org/answers");

            // Destination for moves
            Office.CustomXMLNode destination = answersPart.SelectSingleNode("//oda:repeat[@qref='" + repeatId + "']/oda:row");
            if (destination == null)
            {
                log.Error("no rpt node " + repeatId);
            }

            Dictionary<string, string> xpathChanges = new Dictionary<string, string>();

            // Questions, Conditions
            // ^^^^^^^^^^^^^^^^^^^^^
            // If so, the associated questions may need to be moved into the repeat in the answers XML.
            // Present a table of questions, where the user can say yes/no to each,
            // then move.. table excludes:
            // 1. any that are used outside the repeat, since these can't be made to vary (see above)
            // 2. any that are in a nested repeat
            if (questions.Count > 0)
            {
                FormRepeatWhichVariables formRepeatWhichVariables = new FormRepeatWhichVariables(questions);
                formRepeatWhichVariables.ShowDialog();

                List<question> questionsWhichRepeat = formRepeatWhichVariables.getVars();

                formRepeatWhichVariables.Dispose();

                log.Info(answersPart.XML);

                foreach (question q in questionsWhichRepeat)
                {
                    // Find the relevant answer (by ID)
                    // (easiest to do using XPath on XML document
                    Office.CustomXMLNode node = answersPart.SelectSingleNode("//oda:answer[@id='" + q.id + "']");
                    if (node == null)
                    {
                        log.Error("no node " + q.id);
                    }
                    string fromXPath = NodeToXPath.getXPath(node);
                    log.Info("from: " + fromXPath);

                    // Move it
                    String nodeXML = node.XML; // No API to add a node!
                    node.ParentNode.RemoveChild(node);
                    destination.AppendChildSubtree(nodeXML);

                    // So we'll have to change its xpath in XPaths part
                    // eg from:
                    //   "/oda:answers/oda:answer[@id='qa_2']"
                    // to:
                    //   "/oda:answers/oda:repeat[@qref='rpt1"]/oda:row[1]/oda:answer[@id='qa_2']"
                    //
                    // CustomXMLNode's Xpath produces something like: /ns2:answers[1]/ns2:answer[1]
                    // which we don't want

                    string toXPath = NodeToXPath.getXPath(destination.LastChild);
                    log.Info("to: " + toXPath);

                    xpathChanges.Add(fromXPath, toXPath);
                }

            }
            // nested repeats
            // ^^^^^^^^^^^^^^
            // 1. move the nested repeat answer structure
            // 2. change the xpath for all questions re anything in the nested repeat

            // Note: if wrapping repeat r0 around r1 which in turn contains r2,
            // must avoid explicitly processing  r2, since doing stuff to r1 here is
            // enough to take care of r2.

            // So, step 0. Find top level nested repeats
            // Already have nestedRepeats list, so just remove from it
            // those which aren't top level.
            foreach (Word.ContentControl desc in nestedRepeats)
            {
                if (!desc.ParentContentControl.ID.Equals(wrappingRepeatCC.ID) )
                {
                    // not top level, so remove
                    nestedRepeats.Remove(desc);
                }
            }

            if (nestedRepeats.Count > 0)
            {
                foreach (Word.ContentControl desc in nestedRepeats)
                {
                    TagData td = new TagData(desc.Tag);
                    string nestedRepeatXPathID = td.getRepeatID();

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

                    // 1. move the nested repeat answer structure
                    Office.CustomXMLNode node = answersPart.SelectSingleNode("//oda:repeat[@qref='" + xp.questionID + "']");
                    if (node == null)
                    {
                        log.Error("no node for nested repeat " + xp.questionID);
                    }
                    string fromXPath = NodeToXPath.getXPath(node);
                    log.Info("from: " + fromXPath);

                    // Move it
                    String nodeXML = node.XML; // No API to add a node!
                    node.ParentNode.RemoveChild(node);
                    destination.AppendChildSubtree(nodeXML);

                    // 2. change the xpath for all questions re anything in the nested repeat
                    // With a bit of luck, this will just work!
                    string toXPath = NodeToXPath.getXPath(destination.LastChild);
                    log.Info("to: " + toXPath);

                    xpathChanges.Add(fromXPath, toXPath);
                }
            }

            // Now do the substitutions in the XPaths part - for all
            string xpaths = model.xpathsPart.XML;
            foreach (KeyValuePair<string, string> entry in xpathChanges)
            {
                xpaths = xpaths.Replace(entry.Key, entry.Value);
            }
            CustomXmlUtilities.replaceXmlDoc(model.xpathsPart, xpaths);
            //log.Info(model.xpathsPart.XML);
            //log.Info(answersPart.XML);

            // Now do the substitutions in the content control databindings
            // (Added 2012 12 16, since docx4j relies on the databinding element to do its bit)
            foreach (Word.ContentControl cc in wrappingRepeatCC.Range.ContentControls)
            //foreach (Word.ContentControl cc in Globals.ThisAddIn.Application.ActiveDocument.ContentControls)
            {
                // XMLMapping.IsMapped returns false here,
                // in cases where it is mapped! So avoid using that.
                // (Could be a defect elsewhere .. check for usage)
                if (cc.XMLMapping!=null
                    && cc.XMLMapping.XPath!=null
                    && cc.XMLMapping.PrefixMappings!=null)
                {
                    foreach (KeyValuePair<string, string> entry in xpathChanges)
                    {
                        //log.Info("Comparing " + cc.XMLMapping.XPath + " with " + entry.Key);

                        if (cc.XMLMapping.XPath.Equals(entry.Key))
                        {
                            // matched, so replace
                            cc.XMLMapping.SetMapping(entry.Value, cc.XMLMapping.PrefixMappings, answersPart);
                            break;
                        }
                    }
                }
            }
        }
        public FormCondition(Word.ContentControl cc, ConditionsPartEntry cpe, condition existingCondition)
        {
            InitializeComponent();

            // NET 4 way; see http://msdn.microsoft.com/en-us/library/microsoft.office.tools.word.extensions.aspx
            FabDocxState fabDocxState = (FabDocxState)Globals.Factory.GetVstoObject(Globals.ThisAddIn.Application.ActiveDocument).Tag;

            // NET 3.5 way, which requires using Microsoft.Office.Tools.Word.Extensions
            //FabDocxState fabDocxState = (FabDocxState)Globals.ThisAddIn.Application.ActiveDocument.GetVstoObject(Globals.Factory).Tag;
            this.model = fabDocxState.model;
            xppe = new XPathsPartEntry(model);

            this.cc = cc;

            this.cpe = cpe;
            this.existingCondition = existingCondition;

            this.questionsPart = model.questionsPart;
            questionnaire = new questionnaire();
            questionnaire.Deserialize(questionsPart.XML, out questionnaire);

            questionListHelper = new Helpers.QuestionListHelperForConditionsForm(model, xppe, questionnaire, cc);
            questionListHelper.listBoxTypeFilter = listBoxTypeFilter;
            questionListHelper.listBoxQuestions = listBoxQuestions;
            questionListHelper.checkBoxScope = checkBoxScope;

            questionListHelper.comboBoxValues = comboBoxValues;
            questionListHelper.listBoxPredicate = listBoxPredicate;

            this.listBoxQuestions.SelectedIndexChanged += new System.EventHandler(questionListHelper.listBoxQuestions_SelectedIndexChanged);
            this.listBoxTypeFilter.SelectedIndexChanged += new System.EventHandler(questionListHelper.listBoxTypeFilter_SelectedIndexChanged);

            question existingQuestion = null;
            string matchResponse = null;
            if (existingCondition != null)
            {
                // Use the question associated with it, to pre-select
                // the correct entries in the dialog.

                // Re-label the window, so user can see what the condition was about
                this.Text = "Editing Condition:   " + cc.Title;

                //List<xpathsXpath> xpaths = ConditionsPartEntry.getXPathsUsedInCondition(existingCondition, xppe);
                List<xpathsXpath> xpaths = new List<xpathsXpath>();
                existingCondition.listXPaths(xpaths, cpe.conditions, xppe.getXPaths());

                if (xpaths.Count > 1)
                {
                    // TODO: use complex conditions editor
                }
                xpathsXpath xpathObj = xpaths[0];

                String xpathVal = xpathObj.dataBinding.xpath;

                if (xpathVal.StartsWith("/"))
                {
                    // simple
                    //System.out.println("question " + xpathObj.getQuestionID()
                    //        + " is in use via boolean condition " + conditionId);

                    existingQuestion = this.questionnaire.getQuestion(xpathObj.questionID);
                    matchResponse = xpathVal;
                }
                else if (xpathVal.Contains("position"))
                {
                    // TODO
                }
                else
                {
                    //System.out.println(xpathVal);

                    String qid = xpathVal.Substring(
                        xpathVal.LastIndexOf("@id") + 5);
                    //						System.out.println("Got qid: " + qid);
                    qid = qid.Substring(0, qid.IndexOf("'"));
                    //						System.out.println("Got qid: " + qid);

                    //System.out.println("question " + qid
                    //        + " is in use via condition " + conditionId);

                    existingQuestion = this.questionnaire.getQuestion(qid);
                    matchResponse = xpathVal;

                }

            }

            questionListHelper.populateTypeFilter(true);

            if (existingQuestion == null)
            {
                // for init, populate with all questions
                questionListHelper.populateQuestions(null);
            }
            else
            {
                // Just show the existing question
                listBoxQuestions.Items.Add(existingQuestion);
            }

            if (this.listBoxQuestions.Items.Count == 0) // Never happens if in a repeat, and nor do we want it to, since user might just want to use "repeat pos" stuff
            {
                // Try including out of scope
                this.checkBoxScope.Checked = true;
                questionListHelper.populateQuestions(null);
                if (this.listBoxQuestions.Items.Count == 0)
                {
                    MessageBox.Show("You can't define a condition until you have set up at least one question. Let's do that now. ");

                    FormQA formQA = new FormQA(cc, false);
                    formQA.ShowDialog();
                    formQA.Dispose();

                    // Refresh these
                    xppe = new XPathsPartEntry(model);
                    questionnaire.Deserialize(questionsPart.XML, out questionnaire);

                    questionListHelper.filterAction();

                    return;
                }
            }
            // value
            question q;
            if (existingQuestion == null)
            {
                // for init, populate with all questions
                q = (question)this.listBoxQuestions.Items[0];
            }
            else
            {
                q = existingQuestion;
            }

            this.listBoxQuestions.SelectedItem = q;
            if (q.response.Item is responseFixed)
            {
                questionListHelper.populateValues((responseFixed)q.response.Item, matchResponse);
            }

            // predicate =
            questionListHelper.populatePredicates(q);  // TODO: set this correctly in editing mode
        }
        /*
         * For each question, I need to keep track of the last value
         * it had on a given row.  This I can store in DataGridViewBand.Tag
         * so a Dictionary<Q, object> is OK for this.
          *
          * Actually, the last value seems to be retained. Without looking
          * to see why/how, I don't need to do anything.
         *
         */
        public ConditionsFormRowHelper(Model model, XPathsPartEntry xppe, questionnaire questionnaire, 
                    Word.ContentControl cc, Forms.FormConditionBuilder fcb)
        {
            this.model = model;
            this.xppe = xppe;
            this.questionnaire = questionnaire;
            this.fcb = fcb;

            this.cc = cc;
        }
예제 #27
0
        public Boolean IsFormatEnabled(Office.IRibbonControl control)
        {
            if (getState() == null) return false;

            // Enable for date and (TODO) numbers
            if (currentCC!=null && currentCC.Tag != null)
            {
                String XPathID = (new TagData(currentCC.Tag)).getXPathID();
                if (XPathID != null)
                {
                    // Get the xpath obj
                    FabDocxState fabDocxState = getState();
                    XPathsPartEntry xppe = new XPathsPartEntry(fabDocxState.model);

                    xpathsXpath xpo = xppe.getXPathByID(XPathID);

                    if (xpo.type != null)
                    {
                        if (xpo.type.Equals("date"))
                        {
                            return true;
                        }

                        //else if (xpo.type.Equals("decimal")
                        //    || xpo.type.Equals("integer")
                        //    || xpo.type.Equals("positiveInteger")
                        //    || xpo.type.Equals("nonPositiveInteger")
                        //    || xpo.type.Equals("negativeInteger")
                        //    || xpo.type.Equals("nonNegativeInteger")
                        //    )
                        //{
                        //    return true;
                        //}
                    }
                }
            }
            return false;
        }
        /// <summary>
        /// Handle Word's OnEnter event for content controls, to set the selection in the pane (if the option is set).
        /// </summary>
        /// <param name="ccEntered">A ContentControl object specifying the control that was entered.</param>
        private void doc_ContentControlOnEnter(Word.ContentControl ccEntered)
        {
            log.Debug("Document.ContentControlOnEnter fired.");
            if (ccEntered.XMLMapping.IsMapped)
            {
                log.Debug("control mapped to " + ccEntered.XMLMapping.XPath);
                if (ccEntered.XMLMapping.CustomXMLNode != null)
                {
                    m_cmTaskPane.RefreshControls(Controls.ControlMain.ChangeReason.OnEnter, null, null, null, ccEntered.XMLMapping.CustomXMLNode, null);
                }
                else
                {
                    // Not mapped to anything; probably because the part was replaced?
                    log.Debug(".. but XMLMapping.CustomXMLNode is null");
                    m_cmTaskPane.controlTreeView.DeselectNode();
                    m_cmTaskPane.WarnViaProperties(ccEntered.XMLMapping.XPath);
                }
            }
            else if (ccEntered.Tag!=null)
            {

                TagData td = new TagData(ccEntered.Tag);
                string xpathid = td.getXPathID();
                if (xpathid==null) {
                    xpathid = td.getRepeatID();
                }

                if (xpathid == null)
                {
                    // Visually indicate in the task pane that we're no longer in a mapped control
                    m_cmTaskPane.controlTreeView.DeselectNode();
                    m_cmTaskPane.PropertiesClear();

                } else {
                    log.Debug("control mapped via tag to " + xpathid);

                    // Repeats, escaped XHTML; we don't show anything for conditions
                    XPathsPartEntry xppe = new XPathsPartEntry(m_cmTaskPane.model);
                    xpathsXpath xx = xppe.getXPathByID(xpathid);
                    Office.CustomXMLNode customXMLNode = null;
                    if (xx != null)
                    {
                        log.Debug(xx.dataBinding.xpath);
                        customXMLNode = m_currentPart.SelectSingleNode(xx.dataBinding.xpath);
                    }
                    if (customXMLNode == null)
                    {
                        // Not mapped to anything; probably because the part was replaced?
                        m_cmTaskPane.controlTreeView.DeselectNode();
                        if (xx == null)
                        {
                            log.Error("Couldn't find xpath for " + xpathid);
                            m_cmTaskPane.WarnViaProperties("Missing");
                        }
                        else
                        {
                            log.Warn("Couldn't find target node for " + xx.dataBinding.xpath);
                            m_cmTaskPane.WarnViaProperties(xx.dataBinding.xpath);
                        }
                    }
                    else
                    {
                        m_cmTaskPane.RefreshControls(Controls.ControlMain.ChangeReason.OnEnter, null, null, null, customXMLNode, null);
                    }
                }
            }
            Ribbon.buttonEditEnabled = true;
            Ribbon.buttonDeleteEnabled = true;

            Ribbon.myInvalidate();
        }
예제 #29
0
        public void buttonEditQuestion_Click(Office.IRibbonControl control)
        {
            FabDocxState fabDocxState = getState();
            if (currentCC.Tag != null)
            {
                String XPathID = (new TagData(currentCC.Tag)).getXPathID();
                if (XPathID != null)
                {
                    // Get the xpath obj
                    XPathsPartEntry xppe = new XPathsPartEntry(fabDocxState.model);

                    xpathsXpath xpo = xppe.getXPathByID(XPathID);

                    if (xpo.questionID != null)
                    {
                        Forms.FormQuestionEdit fqe = new Forms.FormQuestionEdit(xpo.questionID);
                        fqe.ShowDialog();
                        fqe.Dispose();
                    }
                    else
                    {
                        log.Debug("No question associated with xpath " + xpo.dataBinding.xpath);
                    }
                }

            }

            if (fabDocxState.TaskPane.Visible)
            {
                Controls.LogicTaskPaneUserControl ltp = (Controls.LogicTaskPaneUserControl)fabDocxState.TaskPane.Control;
                ltp.populateLogicInUse();
            }
        }
 public QuestionListHelperForConditionsForm(Model model, XPathsPartEntry xppe, questionnaire questionnaire,
             Word.ContentControl cc)
     : base(model,  xppe,  questionnaire, cc)
 {
 }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="target"></param>
        /// <param name="setSourceAttr">When saving a building block, we want to write the ID of the source part</param>
        /// <param name="setBindingStore">When re-using a building block, storeItemID to write to the xpath; otherwise null</param>
        /// <param name="overwriteExisting">If re-using a building block back into original source, we want to skip silently.
        /// When going the other way, we want to overwrite the logic in any existing building block (since
        /// it may have been updated).</param>
        public void injectLogic(Model targetModel, bool setSourceAttr, bool setBindingStore, bool overwriteExisting)
        {
            //Model targetModel = Model.ModelFactory(target);

            // XPaths
            XPathsPartEntry targetXppe = new XPathsPartEntry(targetModel);
            string sourceAttr = null;
            if (setSourceAttr)
            {
                sourceAttr = srcXPathsPart.Id;
            }
            string answersPartStoreID = null;
            if (setBindingStore)
            {
                answersPartStoreID = targetModel.answersPart.Id;
            }

            // .. add em
            foreach (xpathsXpath xp in BBxpaths)
            {
                xpathsXpath existing = targetXppe.getXPathByID(xp.id);
                if (existing == null)
                {
                    injectLogicXPath(targetXppe, xp, sourceAttr, answersPartStoreID);
                } else
                {
                    // Does it come from this doc?
                    //log.Debug("xp.source: " + xp.source);
                    //log.Debug("existing.source: " + existing.source);
                    //log.Debug("targetModel.xpathsPart.Id: " + targetModel.xpathsPart.Id);
                    if (xp.source != null && xp.source.Equals(targetModel.xpathsPart.Id))
                    {
                        // yes ..
                        if (overwriteExisting)
                        {
                            injectLogicXPath(targetXppe, xp, sourceAttr, answersPartStoreID);
                        }
                        else
                        {
                            continue;
                        }
                    }
                    else if (xp.source != null &&
                        existing.source != null &&
                        xp.source.Equals(existing.source))
                    {
                        // It has already been copied in.
                        // so don't do it again, whether we're copying to template
                        // (could go either way, but for now, only update from original source),
                        // or into docx
                        continue;

                    } else
                    {
                        // Yikes! ID collision
                        throw new BuildingBlockLogicException("XPath with ID " + xp.id + " is already present.");
                    }
                }
            }

            // Questions
            questionnaire targetQuestionnaire = new questionnaire();
            questionnaire.Deserialize(targetModel.questionsPart.XML, out targetQuestionnaire);
            // .. add em
            foreach (question q in BBquestions)
            {
                question existing = targetQuestionnaire.getQuestion(q.id);
                if (existing == null)
                {
                    targetQuestionnaire.questions.Add(q);
                    if (setSourceAttr)
                    {
                        q.source = srcQuestionsPart.Id;
                    }
                }
                else
                {
                    // Does it come from this doc?
                    //log.Debug("q.source: " + q.source);
                    //log.Debug("existing.source: " + existing.source);
                    //log.Debug("targetModel.questionsPart.Id: " + targetModel.questionsPart.Id);
                    if (q.source != null && q.source.Equals(targetModel.questionsPart.Id))
                    {
                        // yes ..
                        if (overwriteExisting)
                        {
                            targetQuestionnaire.questions.Add(q); // this is a HashSet, so we're overrwriting, not adding :-)
                            if (setSourceAttr)
                            {
                                q.source = targetModel.questionsPart.Id;
                            }
                        }
                        else
                        {
                            continue;
                        }
                    }
                    else if (q.source != null &&
                        existing.source != null &&
                        q.source.Equals(existing.source))
                    {
                        // It has already been copied in.
                        continue;
                    }
                    else
                    {
                        // Yikes! ID collision
                        throw new BuildingBlockLogicException("Question with ID " + q.id + " is already present.");
                    }
                }
            }

            // Answers
            answers targetAnswers = new answers();
            answers.Deserialize(targetModel.answersPart.XML, out targetAnswers);
            foreach (answer a in BBanswers)
            {
                answer existing = getAnswer(targetAnswers, a.id);
                if (existing == null)
                {
                    targetAnswers.Items.Add(a);
                    if (setSourceAttr)
                    {
                        a.source = srcAnswersPart.Id;
                    }
                }
                else
                {
                    // Does it come from this doc?
                    if (a.source != null && a.source.Equals(targetModel.answersPart.Id))
                    {
                        log.Debug("source is this part");
                        // yes ..
                        if (overwriteExisting)
                        {
                            log.Debug(".. and overwriting..");
                            targetAnswers.Items.Add(a); // this is a HashSet, so we're overrwriting, not adding :-)
                            if (setSourceAttr)
                            {
                                a.source = srcAnswersPart.Id;
                            }
                        }
                        else
                        {
                            continue;
                        }
                    }
                    else if (a.source != null &&
                        existing.source != null &&
                        a.source.Equals(existing.source))
                    {
                        // It has already been copied in.
                        log.Debug("this logic already present");
                        continue;
                    }
                    else
                    {
                        // Yikes! ID collision
                        throw new BuildingBlockLogicException("Answer with ID " + a.id + " from different source is already present.");
                    }
                }
            }
            foreach (repeat r in BBrepeats)
            {
                repeat existing = getRepeat(targetAnswers, r.qref);
                if (existing == null)
                {
                    targetAnswers.Items.Add(r);
                    if (setSourceAttr)
                    {
                        r.source = srcAnswersPart.Id;
                    }
                }
                else
                {
                    // Does it come from this doc?
                    if (r.source != null && r.source.Equals(targetModel.answersPart.Id))
                    {
                        // yes ..
                        if (overwriteExisting)
                        {
                            targetAnswers.Items.Add(r); // this is a HashSet, so we're overrwriting, not adding :-)
                            if (setSourceAttr)
                            {
                                r.source = srcAnswersPart.Id;
                            }
                        }
                        else
                        {
                            continue;
                        }
                    }
                    else if (r.source != null &&
                        existing.source != null &&
                        r.source.Equals(existing.source))
                    {
                        // It has already been copied in.
                        continue;
                    }
                    else
                    {
                        // Yikes! ID collision
                        throw new BuildingBlockLogicException("Answer with ID " + r.qref + " is already present.");
                    }
                }
            }

            // Conditions
            conditions targetConditions = new conditions();
            conditions.Deserialize(targetModel.conditionsPart.XML, out targetConditions);
            foreach (condition c in BBconditions)
            {
                condition existing = getCondition(targetConditions, c.id);
                if (existing == null)
                {
                    targetConditions.condition.Add(c);
                    if (setSourceAttr)
                    {
                        c.source = srcConditionsPart.Id;
                    }
                }
                else
                {
                    // Does it come from this doc?
                    if (c.source != null && c.source.Equals(targetModel.conditionsPart.Id))
                    {
                        // yes ..
                        if (overwriteExisting)
                        {
                            targetConditions.condition.Add(c);  // this is a HashSet, so we're overrwriting, not adding :-)
                            if (setSourceAttr)
                            {
                                c.source = targetModel.conditionsPart.Id;
                            }
                        }
                        else
                        {
                            continue;
                        }
                    }
                    else if (c.source != null &&
                        existing.source != null &&
                        c.source.Equals(existing.source))
                    {
                        // It has already been copied in.
                        continue;
                    }
                    else
                    {
                        // Yikes! ID collision
                        throw new BuildingBlockLogicException("Condition with ID " + c.id + " is already present.");
                    }
                }
            }

            // .. save: we only save if there have been no ID collisions.
            // Otherwise, we will have aborted with a BuildingBlockLogicException
            targetXppe.save();
            CustomXmlUtilities.replaceXmlDoc(targetModel.questionsPart, targetQuestionnaire.Serialize());
            CustomXmlUtilities.replaceXmlDoc(targetModel.conditionsPart, targetConditions.Serialize());
            CustomXmlUtilities.replaceXmlDoc(targetModel.answersPart, targetAnswers.Serialize());
        }