/* This file is a part of the NVDA project. URL: http://www.nvda-project.org/ Copyright 2006-2010 NVDA contributers. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License version 2.0, as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. This license can be found at: http://www.gnu.org/licenses/old-licenses/gpl-2.0.html */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "utils.h" #include "storage.h" using namespace std; VBufStorage_textContainer_t::VBufStorage_textContainer_t(wstring str): wstring(str) {} VBufStorage_textContainer_t::~VBufStorage_textContainer_t() {} const wstring& VBufStorage_textContainer_t::getString() { return *this; } void VBufStorage_textContainer_t::destroy() { delete this; } //controlFieldNodeIdentifier implementation VBufStorage_controlFieldNodeIdentifier_t::VBufStorage_controlFieldNodeIdentifier_t(int docHandleArg, int IDArg) : docHandle(docHandleArg), ID(IDArg) { } bool VBufStorage_controlFieldNodeIdentifier_t::operator<(const VBufStorage_controlFieldNodeIdentifier_t &other) const { int docHandleCmp=this->docHandle-other.docHandle; if(docHandleCmp==0) { return (this->ID-other.ID)<0; } return docHandleCmp<0; } bool VBufStorage_controlFieldNodeIdentifier_t::operator!=(const VBufStorage_controlFieldNodeIdentifier_t &other) const { return (this->docHandle!=other.docHandle)||(this->ID!=other.ID); } bool VBufStorage_controlFieldNodeIdentifier_t::operator==(const VBufStorage_controlFieldNodeIdentifier_t &other) const { return (this->docHandle==other.docHandle)&&(this->ID==other.ID); } //field node implementation VBufStorage_fieldNode_t* VBufStorage_fieldNode_t::nextNodeInTree(int direction, VBufStorage_fieldNode_t* limitNode, int *relativeStartOffset) { int relativeOffset=0; VBufStorage_fieldNode_t* tempNode=this; if(direction==TREEDIRECTION_FORWARD) { LOG_DEBUG(L"moving forward"); if(tempNode->firstChild!=NULL) { LOG_DEBUG(L"Moving to first child"); tempNode=tempNode->firstChild; } else { while(tempNode!=NULL&&tempNode->next==NULL) { LOG_DEBUG(L"moving past parent"); tempNode=tempNode->parent; if(tempNode==limitNode) tempNode=NULL; } if(tempNode==NULL||tempNode->next==NULL) { LOG_DEBUG(L"cannot move any further, returning NULL"); return NULL; } LOG_DEBUG(L"Moving to next"); relativeOffset=this->length; tempNode=tempNode->next; } } else if(direction==TREEDIRECTION_BACK) { LOG_DEBUG(L"Moving backwards"); if(tempNode->previous!=NULL) { if(tempNode->previous==limitNode) return NULL; LOG_DEBUG(L"Using previous node"); tempNode=tempNode->previous; while(tempNode->lastChild!=NULL&&tempNode->lastChild!=limitNode) { LOG_DEBUG(L"Using lastChild"); tempNode=tempNode->lastChild; } relativeOffset-=tempNode->length; } else if(tempNode->parent!=NULL) { LOG_DEBUG(L"Using parent"); tempNode=tempNode->parent; } else { LOG_DEBUG(L"No parent or previous, returning NULL"); return NULL; } } else if(direction==TREEDIRECTION_SYMMETRICAL_BACK) { LOG_DEBUG(L"Moving symmetrical backwards"); if(tempNode->lastChild!=NULL) { LOG_DEBUG(L"Moving to last child"); tempNode=tempNode->lastChild; relativeOffset=this->length-tempNode->length; } else { while(tempNode!=NULL&&tempNode->previous==NULL) { LOG_DEBUG(L"moving past parent"); tempNode=tempNode->parent; if(tempNode==limitNode) tempNode=NULL; } if(tempNode==NULL||tempNode->previous==NULL) { LOG_DEBUG(L"cannot move any further, returning NULL"); return NULL; } tempNode=tempNode->previous; relativeOffset-=tempNode->length; } } if(tempNode==limitNode) { LOG_DEBUG(L"passed limit node at "<& attribs, const std::wregex& regexp) { wostringstream test; for (vector::const_iterator attribName = attribs.begin(); attribName != attribs.end(); ++attribName) { outputEscapedAttribute(test, *attribName); test << L":"; VBufStorage_attributeMap_t::const_iterator foundAttrib = attributes.find(*attribName); if (foundAttrib != attributes.end()) outputEscapedAttribute(test, foundAttrib->second); test << L";"; } return regex_match(test.str(), regexp); } int VBufStorage_fieldNode_t::calculateOffsetInTree() const { int startOffset=0; for(VBufStorage_fieldNode_t* previous=this->previous;previous!=NULL;previous=previous->previous) { startOffset+=previous->length; } LOG_DEBUG(L"node has local offset of "<parent) { startOffset+=this->parent->calculateOffsetInTree(); LOG_DEBUG(L"With parents offset is now "<firstChild!=NULL||this->length==0); //Length of a node with out children can not be greater than 0 for(VBufStorage_fieldNode_t* child=this->firstChild;child!=NULL;child=child->next) { if(offsetlength) { LOG_DEBUG(L"found child at offset "<locateTextFieldNodeAtOffset(offset-tempOffset, relativeOffset); nhAssert(textFieldNode); //textFieldNode can't be NULL return textFieldNode; } else { tempOffset+=child->length; } } LOG_DEBUG(L"No textFieldNode found, returning NULL"); return NULL; } void VBufStorage_fieldNode_t::generateAttributesForMarkupOpeningTag(std::wstring& text, int startOffset, int endOffset) { wostringstream s; s<=this->length?1:0)<isBlock<isHidden<firstChild;child!=NULL;child=child->next) { ++childCount; if((child->length)>0&&child->firstChild) ++childControlCount; } int parentChildCount=1; int indexInParent=0; for(VBufStorage_fieldNode_t* prev=this->previous;prev!=NULL;prev=prev->previous) { ++indexInParent; ++parentChildCount; } for(VBufStorage_fieldNode_t* next=this->next;next!=NULL;next=next->next) { ++parentChildCount; } s<attributes.begin();i!=this->attributes.end();++i) { text+=sanitizeXMLAttribName(i->first); text+=L"=\""; for(std::wstring::iterator j=i->second.begin();j!=i->second.end();++j) { appendCharToXML(*j,text,true); } text+=L"\" "; } } void VBufStorage_fieldNode_t::generateMarkupOpeningTag(std::wstring& text, int startOffset, int endOffset) { text+=L"<"; this->generateMarkupTagName(text); text+=L" "; this->generateAttributesForMarkupOpeningTag(text,startOffset,endOffset); text+=L">"; } void VBufStorage_fieldNode_t::generateMarkupClosingTag(std::wstring& text) { text+=L"generateMarkupTagName(text); text+=L">"; } void VBufStorage_fieldNode_t::getTextInRange(int startOffset, int endOffset, std::wstring& text, bool useMarkup, bool(*filter)(VBufStorage_fieldNode_t*)) { if(this->length==0) { LOG_DEBUG(L"node has 0 length, not collecting text"); return; } LOG_DEBUG(L"getting text between offsets "<=0); //startOffset can't be negative nhAssert(startOffsetlength); //endOffset can't be bigger than node length if(useMarkup) { this->generateMarkupOpeningTag(text,startOffset,endOffset); } nhAssert(this->firstChild!=NULL||this->length==0); //Length of a node with out children can not be greater than 0 int childStart=0; int childEnd=0; int childLength=0; for(VBufStorage_fieldNode_t* child=this->firstChild;child!=NULL;child=child->next) { childLength=child->length; nhAssert(childLength>=0); //length can't be negative childEnd+=childLength; LOG_DEBUG(L"child with offsets of "<startOffset&&endOffset>childStart&&(!filter||filter(child))) { LOG_DEBUG(L"child offsets overlap requested offsets"); child->getTextInRange(max(startOffset,childStart)-childStart,min(endOffset-childStart,childLength),text,useMarkup,filter); } childStart+=childLength; LOG_DEBUG(L"childStart is now "<generateMarkupClosingTag(text); } LOG_DEBUG(L"Generated, text string is now "<attributes[name]=value; return true; } std::wstring VBufStorage_fieldNode_t::getAttributesString() const { std::wstring attributesString; for(std::map::const_iterator i=attributes.begin();i!=attributes.end();++i) { attributesString+=i->first; attributesString+=L':'; attributesString+=i->second; attributesString+=L';'; } return attributesString; } std::wstring VBufStorage_fieldNode_t::getDebugInfo() const { std::wostringstream s; s<VBufStorage_fieldNode_t::generateAttributesForMarkupOpeningTag(text,startOffset,endOffset); } void VBufStorage_controlFieldNode_t::disassociateFromBuffer(VBufStorage_buffer_t* buffer) { nhAssert(buffer); //Must be associated with a buffer LOG_DEBUG(L"Disassociating controlFieldNode from buffer"); buffer->forgetControlFieldNode(this); this->VBufStorage_fieldNode_t::disassociateFromBuffer(buffer); } VBufStorage_controlFieldNode_t::VBufStorage_controlFieldNode_t(int docHandle, int ID, bool isBlockArg): VBufStorage_fieldNode_t(0,isBlockArg), identifier(docHandle,ID) { LOG_DEBUG(L"controlFieldNode initialization at "<identifier.docHandle; *ID=this->identifier.ID; return true; } std::wstring VBufStorage_controlFieldNode_t::getDebugInfo() const { std::wostringstream s; s<VBufStorage_fieldNode_t::getDebugInfo()<generateMarkupOpeningTag(text,startOffset,endOffset); } nhAssert(startOffset>=0); //StartOffset must be not negative nhAssert(startOffsetlength); //endOffset can't be greater than node length if(useMarkup) { wchar_t c; for(int offset=startOffset;offsettext[offset]; appendCharToXML(c,text); } } else { text.append(this->text,startOffset,endOffset-startOffset); } if(useMarkup) { this->generateMarkupClosingTag(text); } LOG_DEBUG(L"generated, text string is now of length "<(textArg.length()),false), text(textArg) { LOG_DEBUG(L"textFieldNode initialization, with text of length "<VBufStorage_fieldNode_t::getDebugInfo(); return s.str(); } //buffer implementation void VBufStorage_buffer_t::forgetControlFieldNode(VBufStorage_controlFieldNode_t* node) { nhAssert(node); //Node can't be NULL map::iterator i=controlFieldNodesByIdentifier.find(node->identifier); nhAssert(i!=controlFieldNodesByIdentifier.end()); nhAssert(i->second==node); controlFieldNodesByIdentifier.erase(i); } bool VBufStorage_buffer_t::insertNode(VBufStorage_controlFieldNode_t* parent, VBufStorage_fieldNode_t* previous, VBufStorage_fieldNode_t* node) { if(!node) { LOG_DEBUGWARNING(L"Cannot insert a NULL node. Returning false"); return false; } if(!parent&&previous) { LOG_DEBUGWARNING(L"Previous cannot be specified with no parent. Returning false"); return false; } if(parent&&!isNodeInBuffer(parent)) { LOG_DEBUGWARNING(L"Bad parent: node at "<parent) { LOG_DEBUGWARNING(L"Bad relation: parent at "<rootNode) { LOG_DEBUGWARNING(L"No parent specified but the root node already exists at "<rootNode<parent; next=(previous?previous->next:(parent?parent->firstChild:NULL)); LOG_DEBUG(L"using parent: "<<(parent?parent->getDebugInfo():L"NULL")); LOG_DEBUG(L"Using previous: "<<(previous?previous->getDebugInfo():L"NULL")); LOG_DEBUG(L"Using next: "<<(next?next->getDebugInfo():L"NULL")); if(parent==NULL) { LOG_DEBUG(L"making node root node of buffer"); this->rootNode=node; } else { if(previous==NULL) { LOG_DEBUG(L"Making node first child of parent"); parent->firstChild=node; } if(next==NULL) { LOG_DEBUG(L"Making node last child of parent"); parent->lastChild=node; } } if(previous) { LOG_DEBUG(L"Making node previous's next"); previous->next=node; } if(next) { LOG_DEBUG(L"Making node next's previous"); next->previous=node; } LOG_DEBUG(L"setting node's parent, previous and next"); node->parent=parent; node->previous=previous; node->next=next; if(node->length>0) { LOG_DEBUG(L"Widening ancestors by "<length); for(VBufStorage_fieldNode_t* ancestor=node->parent;ancestor!=NULL;ancestor=ancestor->parent) { LOG_DEBUG(L"Ancestor: "<getDebugInfo()); ancestor->length+=node->length; nhAssert(ancestor->length>=0); //length must never be negative LOG_DEBUG(L"Ancestor length now"<length); } } LOG_DEBUG(L"Inserted subtree"); nhAssert(this->nodes.count(node)==0); this->nodes.insert(node); return true; } void VBufStorage_buffer_t::deleteNode(VBufStorage_fieldNode_t* node) { nhAssert(node); node->disassociateFromBuffer(this); nhAssert(this->nodes.count(node)==1); this->nodes.erase(node); LOG_DEBUG(L"deleting node at "<getDebugInfo()); //Save off next before deleting the subtree for(VBufStorage_fieldNode_t* child=node->firstChild;child!=NULL;) { VBufStorage_fieldNode_t* next=child->next; deleteSubtree(child); child=next; } deleteNode(node); LOG_DEBUG(L"Deleted subtree"); } VBufStorage_buffer_t::VBufStorage_buffer_t(): rootNode(NULL), nodes(), controlFieldNodesByIdentifier(), selectionStart(0), selectionLength(0) { LOG_DEBUG(L"buffer initializing"); } VBufStorage_buffer_t::~VBufStorage_buffer_t() { LOG_DEBUG(L"buffer being destroied"); this->clearBuffer(); } VBufStorage_controlFieldNode_t* VBufStorage_buffer_t::addControlFieldNode(VBufStorage_controlFieldNode_t* parent, VBufStorage_fieldNode_t* previous, int docHandle, int ID, bool isBlock) { LOG_DEBUG(L"Adding control field node to buffer with parent at "<getDebugInfo()); if(addControlFieldNode(parent,previous,controlFieldNode)!=controlFieldNode) { LOG_DEBUGWARNING(L"Error adding control field node to buffer"); delete controlFieldNode; return NULL; } return controlFieldNode; } VBufStorage_controlFieldNode_t* VBufStorage_buffer_t::addControlFieldNode(VBufStorage_controlFieldNode_t* parent, VBufStorage_fieldNode_t* previous, VBufStorage_controlFieldNode_t* controlFieldNode) { if(!controlFieldNode) { LOG_DEBUGWARNING(L"Node is NULL. Returnning NULL"); return NULL; } LOG_DEBUG(L"Add controlFieldNode using parent at "<identifier)>0) { LOG_DEBUGWARNING(L"Buffer at "<getDebugInfo()<identifier]=controlFieldNode; LOG_DEBUG(L"Added new controlFieldNode, returning node"); return controlFieldNode; } VBufStorage_textFieldNode_t* VBufStorage_buffer_t::addTextFieldNode(VBufStorage_controlFieldNode_t* parent, VBufStorage_fieldNode_t* previous, const std::wstring& text) { LOG_DEBUG(L"Add textFieldNode using parent at "<getDebugInfo()); if(addTextFieldNode(parent,previous,textFieldNode)!=textFieldNode) { LOG_DEBUGWARNING(L"Error adding textFieldNode to buffer"); delete textFieldNode; return NULL; } return textFieldNode; } VBufStorage_textFieldNode_t* VBufStorage_buffer_t::addTextFieldNode(VBufStorage_controlFieldNode_t* parent, VBufStorage_fieldNode_t* previous, VBufStorage_textFieldNode_t* textFieldNode) { if(!textFieldNode) { LOG_DEBUGWARNING(L"Node is NULL. Returnning NULL"); return NULL; } LOG_DEBUG(L"Add textFieldNode using parent at "<& m) { VBufStorage_controlFieldNode_t* parent=NULL; VBufStorage_fieldNode_t* previous=NULL; //Using the current selection start, record a list of ancestor fields by their identifier, //and a relative offset of the selection start to those fields, so that the selection can be corrected after the replacement. list> identifierList; if(this->getTextLength()>0) { int controlDocHandle, controlID, controlNodeStart, controlNodeEnd; parent=this->locateControlFieldNodeAtOffset(this->selectionStart,&controlNodeStart,&controlNodeEnd,&controlDocHandle,&controlID); int relativeSelectionStart=this->selectionStart-controlNodeStart; for(;parent!=NULL;parent=parent->parent) { identifierList.push_front(pair(parent->identifier,relativeSelectionStart)); for(previous=parent->previous;previous!=NULL;relativeSelectionStart+=previous->length,previous=previous->previous); } } //For each node in the map, //Replace the node on this buffer, with the content of the buffer in the map for that node //Note that controlField info will automatically be removed, but not added again bool failedBuffers=false; for(map::iterator i=m.begin();i!=m.end();) { VBufStorage_fieldNode_t* node=i->first; VBufStorage_buffer_t* buffer=i->second; if(buffer==this) { LOG_DEBUGWARNING(L"Cannot replace a subtree on a buffer with the same buffer. Skipping"); failedBuffers=true; m.erase(i++); continue; } parent=node->parent; previous=node->previous; if(!this->removeFieldNode(node)) { LOG_DEBUGWARNING(L"Error removing node. Skipping"); failedBuffers=true; buffer->clearBuffer(); delete buffer; m.erase(i++); continue; } if(!this->insertNode(parent,previous,buffer->rootNode)) { LOG_DEBUGWARNING(L"Error inserting node. Skipping"); failedBuffers=true; buffer->clearBuffer(); delete buffer; m.erase(i++); continue; } buffer->nodes.erase(buffer->rootNode); this->nodes.insert(buffer->nodes.begin(),buffer->nodes.end()); buffer->nodes.clear(); buffer->rootNode=NULL; ++i; } //Update the controlField info on this buffer using all the buffers in the map //We do this all in one go instead of for each replacement in case there are issues with ordering //e.g. an identifier appears in one place before its removed in another for(map::iterator i=m.begin();i!=m.end();++i) { VBufStorage_buffer_t* buffer=i->second; int failedIDs=0; for(map::iterator j=buffer->controlFieldNodesByIdentifier.begin();j!=buffer->controlFieldNodesByIdentifier.end();++j) { map::iterator existing=this->controlFieldNodesByIdentifier.find(j->first); if(existing!=this->controlFieldNodesByIdentifier.end()) { ++failedIDs; if(!removeFieldNode(existing->second,false)) { LOG_DEBUGWARNING(L"Error removing old node to make when handling ID clash"); continue; } nhAssert(this->controlFieldNodesByIdentifier.count(j->first)==0); } this->controlFieldNodesByIdentifier.insert(make_pair(j->first,j->second)); } buffer->controlFieldNodesByIdentifier.clear(); delete buffer; if(failedIDs>0) { LOG_DEBUGWARNING(L"Duplicate IDs when replacing subtree. Duplicate count "< >::iterator i=identifierList.begin();i!=identifierList.end();++i) { VBufStorage_controlFieldNode_t* currentAncestorNode=this->getControlFieldNodeWithIdentifier(i->first.docHandle,i->first.ID); if(currentAncestorNode==NULL) break; if(currentAncestorNode->parent!=lastAncestorNode) break; lastAncestorNode=currentAncestorNode; lastRelativeSelectionStart=i->second; } if(lastAncestorNode!=NULL) { int lastAncestorStartOffset, lastAncestorEndOffset; if(!this->getFieldNodeOffsets(lastAncestorNode,&lastAncestorStartOffset,&lastAncestorEndOffset)) { LOG_DEBUGWARNING(L"Error getting offsets for last ancestor node. Returnning false"); return false; } this->selectionStart=lastAncestorStartOffset+min(lastRelativeSelectionStart,max(lastAncestorNode->length-1,0)); } } return !failedBuffers; } bool VBufStorage_buffer_t::removeFieldNode(VBufStorage_fieldNode_t* node,bool removeDescendants) { if(!isNodeInBuffer(node)) { LOG_DEBUGWARNING(L"Node at "<rootNode&&!removeDescendants) { LOG_DEBUGWARNING(L"Cannot remove the rootNode without removing its descedants. Returnning false"); return false; } if((removeDescendants||!node->firstChild)&&node->length>0) { LOG_DEBUG(L"collapsing length of ancestors by "<length); for(VBufStorage_fieldNode_t* ancestor=node->parent;ancestor!=NULL;ancestor=ancestor->parent) { LOG_DEBUG(L"Ancestor: "<getDebugInfo()); ancestor->length-=node->length; nhAssert(ancestor->length>=0); //ancestor length can't be negative LOG_DEBUG(L"Ancestor length now"<length); } } LOG_DEBUG(L"Disconnecting node from its siblings and or parent"); if(node->next!=NULL) { node->next->previous=(!removeDescendants&&node->lastChild)?node->lastChild:node->previous; } else if(node->parent) { node->parent->lastChild=(!removeDescendants&&node->lastChild)?node->lastChild:node->previous; } if(node->previous!=NULL) { node->previous->next=(!removeDescendants&&node->firstChild)?node->firstChild:node->next; } else if(node->parent) { node->parent->firstChild=(!removeDescendants&&node->firstChild)?node->firstChild:node->next; } if(!removeDescendants) { for(VBufStorage_fieldNode_t* child=node->firstChild;child!=NULL;child=child->next) child->parent=node->parent; if(node->firstChild) node->firstChild->previous=node->previous; if(node->lastChild) node->lastChild->next=node->next; deleteNode(node); } else { LOG_DEBUG(L"Deleting subtree"); deleteSubtree(node); } if(node==this->rootNode) { LOG_DEBUG(L"Removing root node from buffer "); this->rootNode=NULL; } LOG_DEBUG(L"Removed fieldNode and descendants, returning true"); return true; } void VBufStorage_buffer_t::clearBuffer() { for(set::iterator i=nodes.begin();i!=nodes.end();++i) { nhAssert(*i); delete *i; } nodes.clear(); controlFieldNodesByIdentifier.clear(); selectionStart=selectionLength=0; this->rootNode=NULL; } bool VBufStorage_buffer_t::getFieldNodeOffsets(VBufStorage_fieldNode_t* node, int *startOffset, int *endOffset) { if(!isNodeInBuffer(node)) { LOG_DEBUGWARNING(L"Node at "<calculateOffsetInTree(); *endOffset=(*startOffset)+node->length; LOG_DEBUG(L"node has offsets "<<*startOffset<=this->getTextLength()) { LOG_DEBUGWARNING(L"Offset "<=endOffset) { LOG_DEBUG(L"node is not at offset, returning false"); return false; } LOG_DEBUG(L"Node is at offset "<rootNode==NULL) { LOG_DEBUGWARNING(L"Buffer is empty, returning NULL"); return NULL; } if(offset<0||offset>=this->getTextLength()) { LOG_DEBUGWARNING(L"Offset "<rootNode->locateTextFieldNodeAtOffset(offset,&relativeOffset); if(node==NULL) { LOG_DEBUGWARNING(L"Could not locate node, returning NULL"); return NULL; } int startOffset=offset-relativeOffset; if(nodeStartOffset) *nodeStartOffset=startOffset; if(nodeEndOffset) *nodeEndOffset=startOffset+node->length; LOG_DEBUG(L"Located node, returning node at "<locateTextFieldNodeAtOffset(offset,&startOffset,&endOffset); if(node==NULL) { LOG_DEBUGWARNING(L"Could not locate node at offset, returning NULL"); return NULL; } nhAssert(node->parent); for(VBufStorage_fieldNode_t* previous=node->previous;previous!=NULL;previous=previous->previous) { startOffset-=previous->length; } endOffset=startOffset+node->parent->length; nhAssert(startOffset>=0&&endOffset>=startOffset); //Offsets must not be negative VBufStorage_controlFieldNode_t* controlFieldNode = node->parent; if(nodeStartOffset) *nodeStartOffset=startOffset; if(nodeEndOffset) *nodeEndOffset=endOffset; if(docHandle) *docHandle=controlFieldNode->identifier.docHandle; if(ID) *ID=controlFieldNode->identifier.ID; LOG_DEBUG(L"Found node, returning "<getDebugInfo()); return controlFieldNode; } VBufStorage_controlFieldNode_t* VBufStorage_buffer_t::getControlFieldNodeWithIdentifier(int docHandle, int ID) { VBufStorage_controlFieldNodeIdentifier_t identifier(docHandle, ID); std::map::iterator i=this->controlFieldNodesByIdentifier.find(identifier); if(i==this->controlFieldNodesByIdentifier.end()) { LOG_DEBUG(L"No controlFieldNode with identifier, returning NULL"); return NULL; } VBufStorage_controlFieldNode_t* node=i->second; nhAssert(node); //Node can not be NULL LOG_DEBUG(L"returning node at "<identifier.docHandle; if(ID) *ID=node->identifier.ID; return true; } bool VBufStorage_buffer_t::getSelectionOffsets(int *startOffset, int *endOffset) const { nhAssert(this->selectionStart>=0&&this->selectionLength>=0); //Selection can't be negative int minStartOffset=0; int maxEndOffset=(this->rootNode)?this->rootNode->length:0; if(startOffset) *startOffset=max(minStartOffset,this->selectionStart); if(endOffset) *endOffset=min(this->selectionStart+this->selectionLength,maxEndOffset); LOG_DEBUG(L"Selection is "<<*startOffset<selectionStart=startOffset; this->selectionLength=endOffset-startOffset; LOG_DEBUG(L"Selection set to "<rootNode)?this->rootNode->length:0; LOG_DEBUG(L"Returning length of "<rootNode==NULL) { LOG_DEBUGWARNING(L"buffer is empty, returning NULL"); return NULL; } if(startOffset<0||startOffset>=endOffset||endOffset>this->rootNode->length) { LOG_DEBUGWARNING(L"Bad offsets of "<rootNode->getTextInRange(startOffset,endOffset,text,useMarkup); LOG_DEBUG(L"Got text between offsets "<rootNode==NULL) { LOG_DEBUGWARNING(L"buffer empty, returning NULL"); return NULL; } if(offset>=this->rootNode->length) { LOG_DEBUGWARNING(L" offset "<rootNode; bufferStart=0; bufferEnd=node->length; } else if(offset>=0) { node=this->locateTextFieldNodeAtOffset(offset,&bufferStart,&bufferEnd); } else { LOG_DEBUGWARNING(L"Invalid offset: "< attribsList; copy(istream_iterator>(wistringstream(attribs)), istream_iterator>(), back_inserter >(attribsList)); wregex regexObj; try { regexObj=wregex(regexp); } catch (...) { LOG_ERROR(L"Error in regular expression"); return NULL; } LOG_DEBUG(L"starting from node "<getDebugInfo()); LOG_DEBUG(L"initial start is "<nextNodeInTree(TREEDIRECTION_FORWARD,NULL,&tempRelativeStart);node!=NULL;node=node->nextNodeInTree(TREEDIRECTION_FORWARD,NULL,&tempRelativeStart)) { bufferStart+=tempRelativeStart; bufferEnd=bufferStart+node->length; LOG_DEBUG(L"start is now "<getDebugInfo()); if(node->length>0&&!(node->isHidden)&&node->matchAttributes(attribsList,regexObj)) { LOG_DEBUG(L"found a match"); break; } } } else if(direction==VBufStorage_findDirection_back) { LOG_DEBUG(L"searching back"); bool skippedFirstMatch=false; for(node=node->nextNodeInTree(TREEDIRECTION_BACK,NULL,&tempRelativeStart);node!=NULL;node=node->nextNodeInTree(TREEDIRECTION_BACK,NULL,&tempRelativeStart)) { bufferStart+=tempRelativeStart; bufferEnd=bufferStart+node->length; LOG_DEBUG(L"start is now "<length>0&&!(node->isHidden)&&node->matchAttributes(attribsList,regexObj)) { //Skip first containing parent match or parent match where offset hasn't changed if((bufferStart==offset)||(!skippedFirstMatch&&bufferStartoffset)) { LOG_DEBUG(L"skipping initial parent"); skippedFirstMatch=true; continue; } LOG_DEBUG(L"found match"); break; } } } else if(direction==VBufStorage_findDirection_up) { LOG_DEBUG(L"searching up"); do { for(;node->previous!=NULL;node=node->previous,bufferStart-=node->length); LOG_DEBUG(L"start is now "<parent; if(node) { bufferEnd=bufferStart+node->length; } } while(node!=NULL&&(node->isHidden||!node->matchAttributes(attribsList,regexObj))); LOG_DEBUG(L"end is now "<rootNode==NULL||offset>=this->rootNode->length) { LOG_DEBUGWARNING(L"Offset of "<getDebugInfo()); std::set possibleBreaks; //Find the node at which to limit the search for line endings. VBufStorage_fieldNode_t* limitBlockNode=NULL; for(limitBlockNode=initNode->parent;limitBlockNode!=NULL&&!limitBlockNode->isBlock;limitBlockNode=limitBlockNode->parent); //Some needed variables for searching back and forward VBufStorage_fieldNode_t* node=NULL; int relative, bufferStart, bufferEnd, tempRelativeStart; bool foundHardBreak=false; //Search forward for the next line ending. node = initNode; relative = offset - initBufferStart; bufferStart = initBufferStart; bufferEnd = initBufferEnd; int lineEnd; do { possibleBreaks.insert(bufferStart); possibleBreaks.insert(bufferEnd); if(node->length>0&&node->firstChild==NULL) { std::wstring text; lineEnd = bufferEnd; node->getTextInRange(0,node->length,text,false); bool lastWasSpace = false; for (int i = relative; i < node->length; ++i) { if ((text[i] == L'\r' && (i + 1 >= node->length || text[i + 1] != L'\n')) || text[i] == L'\n' ) { lineEnd = bufferStart + i + 1; foundHardBreak=true; break; } if(iswspace(text[i])) { lastWasSpace = true; } else { if(lastWasSpace) { possibleBreaks.insert(bufferStart + i); } lastWasSpace = false; } } if (foundHardBreak) { //A hard line break was found. break; } } //Move on to the next node. node = node->nextNodeInTree(TREEDIRECTION_FORWARD,limitBlockNode,&tempRelativeStart); //If not using screen layout, make sure not to pass in to another control field node if(node&&((!useScreenLayout&&node->firstChild)||node->isBlock)) { node=NULL; } if(node) { bufferStart+=tempRelativeStart; bufferEnd=bufferStart+node->length; relative = 0; } } while (node); //Search backward for the previous line ending. node = initNode; relative = offset - initBufferStart; bufferStart = initBufferStart; bufferEnd = initBufferEnd; int lineStart; foundHardBreak=false; do { possibleBreaks.insert(bufferStart); possibleBreaks.insert(bufferEnd); if(node->length>0&&node->firstChild==NULL) { std::wstring text; lineStart = bufferStart; node->getTextInRange(0,node->length,text,false); bool lastWasSpace = false; for (int i = relative - 1; i >= 0; i--) { if ((text[i] == L'\r' && (i + 1 >= node->length || text[i + 1] != L'\n')) || text[i] == L'\n' ) { lineStart = bufferStart + i + 1; foundHardBreak=true; break; } if (iswspace(text[i])) { if (!lastWasSpace) { possibleBreaks.insert(bufferStart + i + 1); } lastWasSpace = true; } else { lastWasSpace = false; } } if (foundHardBreak) { //A hard line break was found. break; } } //Move on to the previous node. node = node->nextNodeInTree(TREEDIRECTION_SYMMETRICAL_BACK,useScreenLayout?limitBlockNode:node->parent,&tempRelativeStart); //If not using screen layout, make sure not to pass in to another control field node if(node&&node->isBlock) { node=NULL; } if(node) { bufferStart+=tempRelativeStart; bufferEnd=bufferStart+node->length; relative = node->length; } } while (node); LOG_DEBUG(L"line offsets after searching back and forth for line feeds and block edges is "<0) { set realBreaks; realBreaks.insert(lineStart); realBreaks.insert(lineEnd); for(int i=lineStart,lineCharCounter=0;i0) { set::iterator possible=possibleBreaks.upper_bound(i); if((possible!=possibleBreaks.begin())&&(*(--possible)>(i-maxLineLength))) { i=*possible; } } realBreaks.insert(i); lineCharCounter=0; } } set::iterator real=realBreaks.upper_bound(offset); lineEnd=*real; lineStart=*(--real); LOG_DEBUG(L"limits after fixing for maxLineLength %: start "<rootNode)?true:false; } bool VBufStorage_buffer_t::isDescendantNode(VBufStorage_fieldNode_t* parent, VBufStorage_fieldNode_t* descendant) { nhAssert(parent); nhAssert(descendant); LOG_DEBUG(L"is node at "<parent;tempNode!=NULL;tempNode=tempNode->parent) { if(tempNode==parent) { LOG_DEBUG(L"Node is a descendant"); return true; } } LOG_DEBUG(L"Not a descendant"); return false; } bool VBufStorage_buffer_t::isNodeInBuffer(VBufStorage_fieldNode_t* node) { return this->nodes.count(node)?true:false; } std::wstring VBufStorage_buffer_t::getDebugInfo() const { std::wostringstream s; s<