/// <summary> // RULE: A variable cc can copied wherever. // If its repeat ancestors change, its "vary in repeat" // will need to change (to lowest common denominator). // If this cc is not in any repeat, // make the answer top level and we're done // Otherwise, could assume its position is OK wrt // existing repeats. // So if anything, we just need to move it // up the tree until // we reach a node which contains this additional repeat. // But we'd like this code to // be used for both moves and copy. (Move case // needs this constraint relaxed) // So our algorithm finds viable positions // (ie ancestors common to all // repeats). // That node and higher are candidates for new position // Ask user to choose. /// </summary> protected void handleXPath(string xpathID, bool dontAskIfOkAsis) { xpathsXpath xp = xppe.getXPathByID(xpathID); string questionID = xp.questionID; // If this cc is not in any repeat, // make the answer top level and we're done // Otherwise, we can assume its position is OK wrt // existing repeats. // So if anything, we just need to move it // up the tree until // we reach a node which contains this additional repeat. // But for the variable move case (handled in MoveHandler) // an existing repeat will no longer be a constraint. // So better to just have a single algorithm which // finds viable positions (ie ancestors common to all // repeats). // 2 algorithms for doing this. // The first: Make a list of the ancestors of the // first repeat. Then cross off that list anything // which isn't an ancestor of the other repeats. // The second: Get XPath for each repeat. // Find the shortest XPath. // Then find the shortest substring common to each // repeat. Then make a list out of that. // I like the first better. // Find all cc's in which this question is used. QuestionHelper qh = new QuestionHelper(xppe, cpe); List<Word.ContentControl> thisQuestionControls = qh.getControlsUsingQuestion(questionID, xpathID); // For each such cc, find closest repeat ancestor cc (if any). // With each such repeat, do the above algorithm. // Find all cc's in which this question is used. List<Word.ContentControl> relevantRepeats = new List<Word.ContentControl>(); foreach (Word.ContentControl ccx in thisQuestionControls) { Word.ContentControl rpt = RepeatHelper.getYoungestRepeatAncestor(ccx); if (rpt == null) { // make the answer top level and we're done. // That means moving it in AF, and XPaths part. log.Info("question " + questionID + " used at top level, so moving it there."); NodeMover nm = new NodeMover(); nm.Move(xp.dataBinding.xpath, "/oda:answers"); nm.adjustBinding(thisQuestionControls, "/oda:answers", questionID); return; } else { relevantRepeats.Add(rpt); } } Office.CustomXMLPart answersPart = model.answersPart; // userParts[0]; // TODO: make this better // That node and higher are candidates for new position // Ask user to choose, or cancel (and remove this cc, // but what about any impending child add events?? Maybe // need a cancel state) this.questionsPart = model.questionsPart; questionnaire = new questionnaire(); questionnaire.Deserialize(questionsPart.XML, out questionnaire); FormMoveQuestion formMoveQuestion = new FormMoveQuestion(answersPart, relevantRepeats, questionnaire, questionID, xppe); if (dontAskIfOkAsis && formMoveQuestion.OkAsis() ) { // Do nothing } else { formMoveQuestion.ShowDialog(); formMoveQuestion.moveIfNecessary(questionID, xp, answersPart); //string varyInRepeat = formMoveQuestion.getVaryingRepeat(); //if (varyInRepeat == null) //{ // // make the answer top level and we're done. // NodeMover nm = new NodeMover(); // nm.Move(xp.dataBinding.xpath, "/oda:answers"); // nm.adjustBinding(thisQuestionControls, "/oda:answers", questionID); //} //else //{ // // Move it to the selected repeat // // get the node corresponding to the repeat's row // Office.CustomXMLNode node = answersPart.SelectSingleNode("//oda:repeat[@qref='" + varyInRepeat + "']/oda:row[1]"); // if (node == null) // { // log.Error("no node for nested repeat " + varyInRepeat); // } // string toRepeat = NodeToXPath.getXPath(node); // NodeMover nm = new NodeMover(); // nm.Move(xp.dataBinding.xpath, toRepeat); // nm.adjustBinding(thisQuestionControls, toRepeat, questionID); //} } formMoveQuestion.Dispose(); }
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); }