commit/nvda: 11 new changesets

  • From: commits-noreply@xxxxxxxxxxxxx
  • To: nvda-addons-commits@xxxxxxxxxxxxx
  • Date: Sat, 22 Feb 2014 11:02:33 -0000

11 new commits in nvda:

https://bitbucket.org/nvdaaddonteam/nvda/commits/1fe0135823d4/
Changeset:   1fe0135823d4
Branch:      None
User:        jteh
Date:        2014-02-20 07:36:35
Summary:     In browse mode, labels on landmarks are now reported. They are 
also included in the Elements List dialog.

Affected #:  1 file

diff --git a/source/virtualBuffers/__init__.py 
b/source/virtualBuffers/__init__.py
index c49a1c6..1435c46 100644
--- a/source/virtualBuffers/__init__.py
+++ b/source/virtualBuffers/__init__.py
@@ -233,6 +233,10 @@ class 
VirtualBufferTextInfo(textInfos.offsets.OffsetsTextInfo):
                textList = []
                landmark = attrs.get("landmark")
                if formatConfig["reportLandmarks"] and fieldType == 
"start_addedToControlFieldStack" and landmark:
+                       try:
+                               textList.append(attrs["name"])
+                       except KeyError:
+                               pass
                        textList.append(_("%s landmark") % 
aria.landmarkRoles[landmark])
                textList.append(super(VirtualBufferTextInfo, 
self).getControlFieldSpeech(attrs, ancestorAttrs, fieldType, formatConfig, 
extraDetail, reason))
                return " ".join(textList)
@@ -241,6 +245,10 @@ class 
VirtualBufferTextInfo(textInfos.offsets.OffsetsTextInfo):
                textList = []
                landmark = field.get("landmark")
                if formatConfig["reportLandmarks"] and reportStart and landmark 
and field.get("_startOfNode"):
+                       try:
+                               textList.append(field["name"])
+                       except KeyError:
+                               pass
                        # Translators: This is spoken and brailled to indicate 
a landmark (example output: main landmark).
                        textList.append(_("%s landmark") % 
aria.landmarkRoles[landmark])
                text = super(VirtualBufferTextInfo, 
self).getControlFieldBraille(field, ancestors, reportStart, formatConfig)
@@ -275,7 +283,7 @@ class ElementsListDialog(wx.Dialog):
                # in the browse mode Elements List dialog.
                ("landmark", _("Lan&dmarks")),
        )
-       Element = collections.namedtuple("Element", ("textInfo", "text", 
"parent"))
+       Element = collections.namedtuple("Element", ("textInfo", "docHandle", 
"id", "text", "parent"))
 
        lastSelectedElementType=0
 
@@ -354,11 +362,16 @@ class ElementsListDialog(wx.Dialog):
 
                parentElements = []
                for node, start, end in self.vbuf._iterNodesByType(elType):
+                       docHandle = ctypes.c_int()
+                       id = ctypes.c_int()
+                       
NVDAHelper.localLib.VBuf_getIdentifierFromControlFieldNode(self.vbuf.VBufHandle,
 node, ctypes.byref(docHandle), ctypes.byref(id))
+                       docHandle = docHandle.value
+                       id = id.value
                        elInfo = 
self.vbuf.makeTextInfo(textInfos.offsets.Offsets(start, end))
 
                        # Find the parent element, if any.
                        for parent in reversed(parentElements):
-                               if self.isChildElement(elType, parent.textInfo, 
elInfo):
+                               if self.isChildElement(elType, parent, elInfo, 
docHandle, id):
                                        break
                                else:
                                        # We're not a child of this parent, so 
this parent has no more children and can be removed from the stack.
@@ -368,7 +381,8 @@ class ElementsListDialog(wx.Dialog):
                                # Note that parentElements will be empty at 
this point, as all parents are no longer relevant and have thus been removed 
from the stack.
                                parent = None
 
-                       element = self.Element(elInfo, 
self.getElementText(elInfo, elType), parent)
+                       element = self.Element(elInfo, docHandle, id,
+                               self.getElementText(elInfo, docHandle, id, 
elType), parent)
                        self._elements.append(element)
 
                        if not self._initialElement and 
elInfo.compareEndPoints(caret, "startToStart") > 0:
@@ -430,36 +444,41 @@ class ElementsListDialog(wx.Dialog):
                        self.activateButton.Enable()
                self.moveButton.Enable()
 
-       def _getControlFieldAttrib(self, info, attrib):
+       def _getControlFieldAttribs(self, info, docHandle, id):
                info = info.copy()
                info.expand(textInfos.UNIT_CHARACTER)
                for field in reversed(info.getTextWithFields()):
                        if not (isinstance(field, textInfos.FieldCommand) and 
field.command == "controlStart"):
                                # Not a control field.
                                continue
-                       val = field.field.get(attrib)
-                       if val:
-                               return val
-               return None
+                       attrs = field.field
+                       if int(attrs["controlIdentifier_docHandle"]) == 
docHandle and int(attrs["controlIdentifier_ID"]) == id:
+                               return attrs
+               raise LookupError
 
-       def getElementText(self, elInfo, elType):
+       def getElementText(self, elInfo, docHandle, id, elType):
                if elType == "landmark":
-                       landmark = self._getControlFieldAttrib(elInfo, 
"landmark")
+                       attrs = self._getControlFieldAttribs(elInfo, docHandle, 
id)
+                       landmark = attrs.get("landmark")
                        if landmark:
-                               return aria.landmarkRoles[landmark]
+                               name = attrs.get("name", "")
+                               if name:
+                                       name += " "
+                               return name + aria.landmarkRoles[landmark]
 
                else:
                        return elInfo.text.strip()
 
-       def isChildElement(self, elType, parent, child):
-               if parent.isOverlapping(child):
+       def isChildElement(self, elType, parent, childInfo, childDoc, childId):
+               if parent.textInfo.isOverlapping(childInfo):
                        return True
 
                elif elType == "heading":
                        try:
-                               if int(self._getControlFieldAttrib(child, 
"level")) > int(self._getControlFieldAttrib(parent, "level")):
+                               if (int(self._getControlFieldAttribs(childInfo, 
childDoc, childId)["level"])
+                                               > 
int(self._getControlFieldAttribs(parent.textInfo, parent.docHandle, 
parent.id)["level"])):
                                        return True
-                       except (ValueError, TypeError):
+                       except (KeyError, ValueError, TypeError):
                                return False
 
                return False


https://bitbucket.org/nvdaaddonteam/nvda/commits/90cff713c7f3/
Changeset:   90cff713c7f3
Branch:      None
User:        jteh
Date:        2014-02-20 07:48:17
Summary:     Initial work on support for the region role in browse mode.

Regions are treated as landmarks, but only if they are labelled. Also, the word 
"landmark" isn't reported, since this seems rather superfluous.
At present, reporting works correctly, but quick navigation will jump to 
unlabelled regions and the Elements List breaks when unlabelled regions are 
encountered.

Affected #:  2 files

diff --git a/source/aria.py b/source/aria.py
index a23d2e4..8c54bc4 100755
--- a/source/aria.py
+++ b/source/aria.py
@@ -1,6 +1,6 @@
 #aria.py
 #A part of NonVisual Desktop Access (NVDA)
-#Copyright (C) 2009-2012 NV Access Limited
+#Copyright (C) 2009-2014 NV Access Limited
 #This file is covered by the GNU General Public License.
 #See the file COPYING for more details.
 
@@ -79,4 +79,7 @@ landmarkRoles = {
        "search": _("search"),
        # Translators: Reported for the form landmark, normally found on web 
pages.
        "form": _("form"),
+       # Strictly speaking, region isn't a landmark, but it is very similar.
+       # Translators: Reported for a significant region, normally found on web 
pages.
+       "region": _("region"),
 }

diff --git a/source/virtualBuffers/__init__.py 
b/source/virtualBuffers/__init__.py
index 1435c46..95f4d99 100644
--- a/source/virtualBuffers/__init__.py
+++ b/source/virtualBuffers/__init__.py
@@ -201,6 +201,10 @@ class 
VirtualBufferTextInfo(textInfos.offsets.OffsetsTextInfo):
                                
textList.append(self.obj.makeTextInfo(textInfos.offsets.Offsets(start, 
end)).text)
                        attrs["table-%sheadertext" % axis] = "\n".join(textList)
 
+               if attrs.get("landmark") == "region" and not attrs.get("name"):
+                       # We only consider region to be a landmark if it has a 
name.
+                       del attrs["landmark"]
+
                return attrs
 
        def _normalizeFormatField(self, attrs):
@@ -237,7 +241,11 @@ class 
VirtualBufferTextInfo(textInfos.offsets.OffsetsTextInfo):
                                textList.append(attrs["name"])
                        except KeyError:
                                pass
-                       textList.append(_("%s landmark") % 
aria.landmarkRoles[landmark])
+                       if landmark == "region":
+                               # The word landmark is superfluous for regions.
+                               textList.append(aria.landmarkRoles[landmark])
+                       else:
+                               textList.append(_("%s landmark") % 
aria.landmarkRoles[landmark])
                textList.append(super(VirtualBufferTextInfo, 
self).getControlFieldSpeech(attrs, ancestorAttrs, fieldType, formatConfig, 
extraDetail, reason))
                return " ".join(textList)
 
@@ -249,8 +257,12 @@ class 
VirtualBufferTextInfo(textInfos.offsets.OffsetsTextInfo):
                                textList.append(field["name"])
                        except KeyError:
                                pass
-                       # Translators: This is spoken and brailled to indicate 
a landmark (example output: main landmark).
-                       textList.append(_("%s landmark") % 
aria.landmarkRoles[landmark])
+                       if landmark == "region":
+                               # The word landmark is superfluous for regions.
+                               textList.append(aria.landmarkRoles[landmark])
+                       else:
+                               # Translators: This is spoken and brailled to 
indicate a landmark (example output: main landmark).
+                               textList.append(_("%s landmark") % 
aria.landmarkRoles[landmark])
                text = super(VirtualBufferTextInfo, 
self).getControlFieldBraille(field, ancestors, reportStart, formatConfig)
                if text:
                        textList.append(text)
@@ -459,12 +471,10 @@ class ElementsListDialog(wx.Dialog):
        def getElementText(self, elInfo, docHandle, id, elType):
                if elType == "landmark":
                        attrs = self._getControlFieldAttribs(elInfo, docHandle, 
id)
-                       landmark = attrs.get("landmark")
-                       if landmark:
-                               name = attrs.get("name", "")
-                               if name:
-                                       name += " "
-                               return name + aria.landmarkRoles[landmark]
+                       name = attrs.get("name", "")
+                       if name:
+                               name += " "
+                       return name + aria.landmarkRoles[attrs["landmark"]]
 
                else:
                        return elInfo.text.strip()


https://bitbucket.org/nvdaaddonteam/nvda/commits/1d07c6da11ad/
Changeset:   1d07c6da11ad
Branch:      None
User:        jteh
Date:        2014-02-20 07:48:17
Summary:     When searching for a node by attributes in a virtual buffer, use 
regular expressions matching against a string of requested attributes.

This allows us to match against multiple, completely separate options without 
needing to write a complex custom query engine. It also allows for easier 
enhancement in future.
This is necessary to support regions as a landmark because normal landmarks 
don't need names but regions do.

Affected #:  6 files

diff --git a/nvdaHelper/interfaces/vbuf/vbuf.idl 
b/nvdaHelper/interfaces/vbuf/vbuf.idl
index b952a0b..47e5877 100755
--- a/nvdaHelper/interfaces/vbuf/vbuf.idl
+++ b/nvdaHelper/interfaces/vbuf/vbuf.idl
@@ -124,13 +124,14 @@ interface VBuf {
  * @param buffer the virtual buffer to use
  * @param offset offset in the buffer to start searching from
  * @param direction which direction to search
- * @param attribsString the attributes the node should contain
+ * @param attribs the attributes to search
+ * @param regexp regular expression the requested attributes must match
  * @param startOffset memory where the start offset of the found node can be 
placed
  * @param endOffset memory where the end offset of the found node will be 
placed
  * @param foundNode the found field node
  * @return non-zero if the node is found.
  */
-       int findNodeByAttributes([in] VBufRemote_bufferHandle_t buffer, [in] 
int offset, [in] int direction, [in,string] const wchar_t* attribsString, [out] 
int *startOffset, [out] int *endOffset, [out] VBufRemote_nodeHandle_t* 
foundNode);
+       int findNodeByAttributes([in] VBufRemote_bufferHandle_t buffer, [in] 
int offset, [in] int direction, [in,string] const wchar_t* attribs, [in,string] 
const wchar_t* regexp, [out] int *startOffset, [out] int *endOffset, [out] 
VBufRemote_nodeHandle_t* foundNode);
 
 /**
  * Retreaves the current selection offsets for the buffer

diff --git a/nvdaHelper/remote/vbufRemote.cpp b/nvdaHelper/remote/vbufRemote.cpp
index 149a8c8..2905eb2 100755
--- a/nvdaHelper/remote/vbufRemote.cpp
+++ b/nvdaHelper/remote/vbufRemote.cpp
@@ -109,10 +109,10 @@ int 
VBufRemote_getIdentifierFromControlFieldNode(VBufRemote_bufferHandle_t buffe
        return res;
 }
 
-int VBufRemote_findNodeByAttributes(VBufRemote_bufferHandle_t buffer, int 
offset, int direction, const wchar_t* attribsString, int *startOffset, int 
*endOffset, VBufRemote_nodeHandle_t* foundNode) { 
+int VBufRemote_findNodeByAttributes(VBufRemote_bufferHandle_t buffer, int 
offset, int direction, const wchar_t* attribs, const wchar_t* regexp, int 
*startOffset, int *endOffset, VBufRemote_nodeHandle_t* foundNode) { 
        VBufBackend_t* backend=(VBufBackend_t*)buffer;
        backend->lock.acquire();
-       
*foundNode=(VBufRemote_nodeHandle_t)(backend->findNodeByAttributes(offset,(VBufStorage_findDirection_t)direction,attribsString,startOffset,endOffset));
+       
*foundNode=(VBufRemote_nodeHandle_t)(backend->findNodeByAttributes(offset,(VBufStorage_findDirection_t)direction,attribs,regexp,startOffset,endOffset));
        backend->lock.release();
        return (*foundNode)!=0;
 }

diff --git a/nvdaHelper/vbufBackends/gecko_ia2/gecko_ia2.cpp 
b/nvdaHelper/vbufBackends/gecko_ia2/gecko_ia2.cpp
index 9092b8b..68a232a 100755
--- a/nvdaHelper/vbufBackends/gecko_ia2/gecko_ia2.cpp
+++ b/nvdaHelper/vbufBackends/gecko_ia2/gecko_ia2.cpp
@@ -281,6 +281,9 @@ bool isLabelVisible(IAccessible2* acc) {
        return true;
 }
 
+const vector<wstring>ATTRLIST_ROLES(1, L"IAccessible2::attribute_xml-roles");
+const wregex 
REGEX_PRESENTATION_ROLE(L"IAccessible2\\\\:\\\\:attribute_xml-roles:.*\\bpresentation\\b.*;");
+
 VBufStorage_fieldNode_t* GeckoVBufBackend_t::fillVBuf(IAccessible2* pacc,
        VBufStorage_buffer_t* buffer, VBufStorage_controlFieldNode_t* 
parentNode, VBufStorage_fieldNode_t* previousNode,
        IAccessibleTable* paccTable, IAccessibleTable2* paccTable2, long 
tableID,
@@ -428,7 +431,7 @@ VBufStorage_fieldNode_t* 
GeckoVBufBackend_t::fillVBuf(IAccessible2* pacc,
        parentNode->isBlock=isBlockElement;
 
        // force   isHidden to True if this has an ARIA role of presentation 
but its focusble -- Gecko does not hide this itself.
-       
if((states&STATE_SYSTEM_FOCUSABLE)&&parentNode->matchAttributes(L"IAccessible2\\:\\:attribute_xml-roles:~wpresentation;"))
 {
+       
if((states&STATE_SYSTEM_FOCUSABLE)&&parentNode->matchAttributes(ATTRLIST_ROLES, 
REGEX_PRESENTATION_ROLE)) {
                parentNode->isHidden=true;
        }
        BSTR name=NULL;

diff --git a/nvdaHelper/vbufBase/storage.cpp b/nvdaHelper/vbufBase/storage.cpp
index 2a91469..ed7fbd3 100644
--- a/nvdaHelper/vbufBase/storage.cpp
+++ b/nvdaHelper/vbufBase/storage.cpp
@@ -19,6 +19,10 @@ http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 #include <list>
 #include <set>
 #include <sstream>
+#include <regex>
+#include <vector>
+#include <sstream>
+#include <algorithm>
 #include <common/xml.h>
 #include <common/log.h>
 #include "utils.h"
@@ -130,42 +134,30 @@ VBufStorage_fieldNode_t* 
VBufStorage_fieldNode_t::nextNodeInTree(int direction,
        return tempNode;
 }
 
-bool VBufStorage_fieldNode_t::matchAttributes(const std::wstring& 
attribsString) {
-       LOG_DEBUG(L"using attributes of "<<attribsString);
-       multiValueAttribsMap attribsMap;
-       multiValueAttribsStringToMap(attribsString,attribsMap);
-       bool outerMatch=true;
-       for(multiValueAttribsMap::iterator 
i=attribsMap.begin();i!=attribsMap.end();++i) {
-               LOG_DEBUG(L"Checking for attrib "<<i->first);
-               VBufStorage_attributeMap_t::iterator 
foundIterator=attributes.find(i->first);
-               const std::wstring& 
foundValue=(foundIterator!=attributes.end())?foundIterator->second:L"";
-               wstring foundValueTrailingSpaces=L" "+foundValue+L" ";
-               LOG_DEBUG(L"node's value for this attribute is "<<foundValue);
-               multiValueAttribsMap::iterator 
upperBound=attribsMap.upper_bound(i->first);
-               bool innerMatch=false;
-               for(multiValueAttribsMap::iterator j=i;j!=upperBound;++j) { 
-                       i=j;
-                       if(innerMatch)
-                               continue;
-                       LOG_DEBUG(L"Checking value "<<j->second);
-                       if(
-                               // Word match.
-                               (j->second.compare(0, 2, 
L"~w")==0&&foundValueTrailingSpaces.find(L" "+j->second.substr(2)+L" 
")!=wstring::npos)
-                               // Full match.
-                               ||(j->second==foundValue)
-                       ) {
-                               LOG_DEBUG(L"values match");
-                               innerMatch=true;
-                       }
-               }
-               outerMatch=innerMatch;
-               if(!outerMatch) { 
-                       LOG_DEBUG(L"given attributes do not match node's 
attributes, returning false");
-                       return false;
+inline void outputEscapedAttribute(wostringstream& out, const wstring& text) {
+       for (wstring::const_iterator it = text.begin(); it != text.end(); ++it) 
{
+               switch (*it) {
+                       case L':':
+                       case L';':
+                       case L'\\':
+                       out << L"\\";
+                       default:
+                       out << *it;
                }
        }
-       LOG_DEBUG(L"Given attributes match node's attributes, returning true");
-       return true;
+}
+
+bool VBufStorage_fieldNode_t::matchAttributes(const std::vector<std::wstring>& 
attribs, const std::wregex& regexp) {
+       wostringstream test;
+       for (vector<wstring>::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 {
@@ -896,7 +888,7 @@ VBufStorage_textContainer_t*  
VBufStorage_buffer_t::getTextInRange(int startOffs
        return new VBufStorage_textContainer_t(text);
 }
 
-VBufStorage_fieldNode_t* VBufStorage_buffer_t::findNodeByAttributes(int 
offset, VBufStorage_findDirection_t direction, const std::wstring& 
attribsString, int *startOffset, int *endOffset) {
+VBufStorage_fieldNode_t* VBufStorage_buffer_t::findNodeByAttributes(int 
offset, VBufStorage_findDirection_t direction, const std::wstring& attribs, 
const std::wstring &regexp, int *startOffset, int *endOffset) {
        if(this->rootNode==NULL) {
                LOG_DEBUGWARNING(L"buffer empty, returning NULL");
                return NULL;
@@ -922,6 +914,18 @@ VBufStorage_fieldNode_t* 
VBufStorage_buffer_t::findNodeByAttributes(int offset,
                LOG_DEBUGWARNING(L"Could not find node at offset "<<offset<<L", 
returning NULL");
                return NULL;
        }
+       // Split attribs at spaces.
+       vector<wstring> attribsList;
+       copy(istream_iterator<wstring, wchar_t, 
std::char_traits<wchar_t>>(wistringstream(attribs)),
+               istream_iterator<wstring, wchar_t, std::char_traits<wchar_t>>(),
+               back_inserter<vector<wstring> >(attribsList));
+       wregex regexObj;
+       try {
+               regexObj=wregex(regexp);
+       } catch (...) {
+               LOG_ERROR(L"Error in regular expression");
+               return NULL;
+       }
        LOG_DEBUG(L"starting from node "<<node->getDebugInfo());
        LOG_DEBUG(L"initial start is "<<bufferStart<<L" and initial end is 
"<<bufferEnd);
        if(direction==VBufStorage_findDirection_forward) {
@@ -931,7 +935,7 @@ VBufStorage_fieldNode_t* 
VBufStorage_buffer_t::findNodeByAttributes(int offset,
                        bufferEnd=bufferStart+node->length;
                        LOG_DEBUG(L"start is now "<<bufferStart<<L" and end is 
now "<<bufferEnd);
                        LOG_DEBUG(L"Checking node "<<node->getDebugInfo());
-                       
if(node->length>0&&!(node->isHidden)&&node->matchAttributes(attribsString)) {
+                       
if(node->length>0&&!(node->isHidden)&&node->matchAttributes(attribsList,regexObj))
 {
                                LOG_DEBUG(L"found a match");
                                break;
                        }
@@ -943,7 +947,7 @@ VBufStorage_fieldNode_t* 
VBufStorage_buffer_t::findNodeByAttributes(int offset,
                        bufferStart+=tempRelativeStart;
                        bufferEnd=bufferStart+node->length;
                        LOG_DEBUG(L"start is now "<<bufferStart<<L" and end is 
now "<<bufferEnd);
-                       
if(node->length>0&&!(node->isHidden)&&node->matchAttributes(attribsString)) {
+                       
if(node->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&&bufferStart<offset&&bufferEnd>offset))
 {
                                        LOG_DEBUG(L"skipping initial parent");
@@ -963,7 +967,7 @@ VBufStorage_fieldNode_t* 
VBufStorage_buffer_t::findNodeByAttributes(int offset,
                        if(node) {
                                bufferEnd=bufferStart+node->length;
                        }
-               } 
while(node!=NULL&&(node->isHidden||!node->matchAttributes(attribsString)));
+               } 
while(node!=NULL&&(node->isHidden||!node->matchAttributes(attribsList,regexObj)));
                LOG_DEBUG(L"end is now "<<bufferEnd);
        }
        if(node==NULL) {

diff --git a/nvdaHelper/vbufBase/storage.h b/nvdaHelper/vbufBase/storage.h
index 2a1e27a..3456584 100644
--- a/nvdaHelper/vbufBase/storage.h
+++ b/nvdaHelper/vbufBase/storage.h
@@ -19,6 +19,8 @@ http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 #include <map>
 #include <set>
 #include <list>
+#include <vector>
+#include <regex>
 
 /**
  * values to indicate a direction for searching
@@ -224,7 +226,7 @@ class VBufStorage_fieldNode_t {
  * @param attribsString the string containing the attributes, each attribute 
can have multiple values to match on.
   * @return true if the attributes exist, false otherwize.
  */
-       bool matchAttributes(const std::wstring& attribsString);
+       bool matchAttributes(const std::vector<std::wstring>& attribs, const 
std::wregex& regexp);
 
        /**
        * True if this node his hidden - searches will not locate this node.
@@ -554,12 +556,13 @@ class VBufStorage_buffer_t {
  * Finds a field node that contains particular attributes.
  * @param offset offset in the buffer to start searching from, if -1 then 
starts at the root of the buffer.
  * @param direction which direction to search
- * @param attribsString the attributes the node should contain
+ * @param attribs the attributes to search
+ * @param regexp regular expression the requested attributes must match
  * @param startOffset memory where the start offset of the found node can be 
placed
  * @param endOffset memory where the end offset of the found node will be 
placed
  * @return the found field node
  */
-       virtual VBufStorage_fieldNode_t* findNodeByAttributes(int offset, 
VBufStorage_findDirection_t  direction, const std::wstring &attribsString, int 
*startOffset, int *endOffset);
+       virtual VBufStorage_fieldNode_t* findNodeByAttributes(int offset, 
VBufStorage_findDirection_t  direction, const std::wstring &attribs, const 
std::wstring &regexp, int *startOffset, int *endOffset);
 
 /**
  * Retreaves the current selection offsets for the buffer

diff --git a/source/virtualBuffers/__init__.py 
b/source/virtualBuffers/__init__.py
index 95f4d99..78cb24d 100644
--- a/source/virtualBuffers/__init__.py
+++ b/source/virtualBuffers/__init__.py
@@ -42,23 +42,53 @@ VBufStorage_findDirection_up=2
 VBufRemote_nodeHandle_t=ctypes.c_ulonglong
 
 
-def VBufStorage_findMatch_word(word):
-       return "~w%s" % word
-
-def dictToMultiValueAttribsString(d):
-       mainList=[]
-       for k,v in d.iteritems():
-               
k=unicode(k).replace(':','\\:').replace(';','\\;').replace(',','\\,')
-               valList=[]
-               for i in v:
-                       if i is None:
-                               i=""
+class VBufStorage_findMatch_word(unicode):
+       pass
+
+FINDBYATTRIBS_ESCAPE_TABLE = {
+       # Symbols that are escaped in the attributes string.
+       ord(u":"): ur"\\:",
+       ord(u";"): ur"\\;",
+       ord(u"\\"): u"\\\\\\\\",
+}
+# Symbols that must be escaped for a regular expression.
+FINDBYATTRIBS_ESCAPE_TABLE.update({(ord(s), u"\\" + s) for s in 
u"^$.*+?()[]{}|"})
+def _prepareForFindByAttributes(attribs):
+       escape = lambda text: 
unicode(text).translate(FINDBYATTRIBS_ESCAPE_TABLE)
+       reqAttrs = []
+       regexp = []
+       if isinstance(attribs, dict):
+               # Single option.
+               attribs = (attribs,)
+       # All options will match against all requested attributes,
+       # so first build the list of requested attributes.
+       for option in attribs:
+               for name in option:
+                       reqAttrs.append(unicode(name))
+       # Now build the regular expression.
+       for option in attribs:
+               optRegexp = []
+               for name in reqAttrs:
+                       optRegexp.append("%s:" % escape(name))
+                       values = option.get(name)
+                       if not values:
+                               # The value isn't tested for this attribute, so 
match any value.
+                               optRegexp.append(r"(?:\\;|[^;])+;")
+                       elif values[0] is None:
+                               # There must be no value for this attribute.
+                               optRegexp.append(r";")
+                       elif isinstance(values[0], VBufStorage_findMatch_word):
+                               # Assume all are word matches.
+                               optRegexp.append(r"(?:\\;|[^;])*\b(?:")
+                               optRegexp.append("|".join(escape(val) for val 
in values))
+                               optRegexp.append(r")\b(?:\\;|[^;])*;")
                        else:
-                               
i=unicode(i).replace(':','\\:').replace(';','\\;').replace(',','\\,')
-                       valList.append(i)
-               attrib="%s:%s"%(k,",".join(valList))
-               mainList.append(attrib)
-       return "%s;"%";".join(mainList)
+                               # Assume all are exact matches.
+                               optRegexp.append("(?:")
+                               optRegexp.append("|".join(escape(val) for val 
in values))
+                               optRegexp.append(");")
+               regexp.append("".join(optRegexp))
+       return u" ".join(reqAttrs), u"|".join(regexp)
 
 class VirtualBufferTextInfo(textInfos.offsets.OffsetsTextInfo):
 
@@ -908,7 +938,7 @@ class VirtualBuffer(cursorManager.CursorManager, 
treeInterceptorHandler.TreeInte
                return self._iterNodesByAttribs(attribs, direction, offset)
 
        def _iterNodesByAttribs(self, attribs, direction="next", offset=-1):
-               attribs=dictToMultiValueAttribsString(attribs)
+               reqAttrs, regexp = _prepareForFindByAttributes(attribs)
                startOffset=ctypes.c_int()
                endOffset=ctypes.c_int()
                if direction=="next":
@@ -922,7 +952,7 @@ class VirtualBuffer(cursorManager.CursorManager, 
treeInterceptorHandler.TreeInte
                while True:
                        try:
                                node=VBufRemote_nodeHandle_t()
-                               
NVDAHelper.localLib.VBuf_findNodeByAttributes(self.VBufHandle,offset,direction,attribs,ctypes.byref(startOffset),ctypes.byref(endOffset),ctypes.byref(node))
+                               
NVDAHelper.localLib.VBuf_findNodeByAttributes(self.VBufHandle,offset,direction,reqAttrs,regexp,ctypes.byref(startOffset),ctypes.byref(endOffset),ctypes.byref(node))
                        except:
                                return
                        if not node:


https://bitbucket.org/nvdaaddonteam/nvda/commits/6d31b774aa3b/
Changeset:   6d31b774aa3b
Branch:      None
User:        jteh
Date:        2014-02-20 07:48:17
Summary:     When navigating by landmark, don't navigate to regions with no 
label.

Affected #:  3 files

diff --git a/source/virtualBuffers/MSHTML.py b/source/virtualBuffers/MSHTML.py
index 010a7d5..bcb5284 100644
--- a/source/virtualBuffers/MSHTML.py
+++ b/source/virtualBuffers/MSHTML.py
@@ -6,7 +6,7 @@
 
 from comtypes import COMError
 import eventHandler
-from . import VirtualBuffer, VirtualBufferTextInfo, VBufStorage_findMatch_word
+from . import VirtualBuffer, VirtualBufferTextInfo, 
VBufStorage_findMatch_word, VBufStorage_findMatch_notEmpty
 import controlTypes
 import NVDAObjects.IAccessible.MSHTML
 import winUser
@@ -261,7 +261,11 @@ class MSHTML(VirtualBuffer):
                elif nodeType=="focusable":
                        
attrs={"IAccessible::state_%s"%oleacc.STATE_SYSTEM_FOCUSABLE:[1]}
                elif nodeType=="landmark":
-                       
attrs={"HTMLAttrib::role":[VBufStorage_findMatch_word(lr) for lr in 
aria.landmarkRoles]}
+                       attrs = [
+                               {"HTMLAttrib::role": 
[VBufStorage_findMatch_word(lr) for lr in aria.landmarkRoles if lr != 
"region"]},
+                               {"HTMLAttrib::role": 
[VBufStorage_findMatch_word("region")],
+                                       "name": 
[VBufStorage_findMatch_notEmpty]}
+                               ]
                elif nodeType == "embeddedObject":
                        attrs = {"IHTMLDOMNode::nodeName": 
["OBJECT","EMBED","APPLET"]}
                elif nodeType == "separator":

diff --git a/source/virtualBuffers/__init__.py 
b/source/virtualBuffers/__init__.py
index 78cb24d..45f94c3 100644
--- a/source/virtualBuffers/__init__.py
+++ b/source/virtualBuffers/__init__.py
@@ -44,6 +44,7 @@ VBufRemote_nodeHandle_t=ctypes.c_ulonglong
 
 class VBufStorage_findMatch_word(unicode):
        pass
+VBufStorage_findMatch_notEmpty = object()
 
 FINDBYATTRIBS_ESCAPE_TABLE = {
        # Symbols that are escaped in the attributes string.
@@ -72,7 +73,10 @@ def _prepareForFindByAttributes(attribs):
                        optRegexp.append("%s:" % escape(name))
                        values = option.get(name)
                        if not values:
-                               # The value isn't tested for this attribute, so 
match any value.
+                               # The value isn't tested for this attribute, so 
match any (or no) value.
+                               optRegexp.append(r"(?:\\;|[^;])*;")
+                       elif values[0] is VBufStorage_findMatch_notEmpty:
+                               # There must be a value for this attribute.
                                optRegexp.append(r"(?:\\;|[^;])+;")
                        elif values[0] is None:
                                # There must be no value for this attribute.

diff --git a/source/virtualBuffers/gecko_ia2.py 
b/source/virtualBuffers/gecko_ia2.py
index 6d4f8e5..6c34466 100755
--- a/source/virtualBuffers/gecko_ia2.py
+++ b/source/virtualBuffers/gecko_ia2.py
@@ -4,7 +4,7 @@
 #See the file COPYING for more details.
 #Copyright (C) 2008-2012 NV Access Limited
 
-from . import VirtualBuffer, VirtualBufferTextInfo, VBufStorage_findMatch_word
+from . import VirtualBuffer, VirtualBufferTextInfo, 
VBufStorage_findMatch_word, VBufStorage_findMatch_notEmpty
 import treeInterceptorHandler
 import controlTypes
 import NVDAObjects.IAccessible.mozilla
@@ -217,7 +217,11 @@ class Gecko_ia2(VirtualBuffer):
                elif nodeType=="focusable":
                        
attrs={"IAccessible::state_%s"%oleacc.STATE_SYSTEM_FOCUSABLE:[1]}
                elif nodeType=="landmark":
-                       
attrs={"IAccessible2::attribute_xml-roles":[VBufStorage_findMatch_word(lr) for 
lr in aria.landmarkRoles]}
+                       attrs = [
+                               {"IAccessible2::attribute_xml-roles": 
[VBufStorage_findMatch_word(lr) for lr in aria.landmarkRoles if lr != 
"region"]},
+                               {"IAccessible2::attribute_xml-roles": 
[VBufStorage_findMatch_word("region")],
+                                       "name": 
[VBufStorage_findMatch_notEmpty]}
+                               ]
                elif nodeType=="embeddedObject":
                        
attrs={"IAccessible2::attribute_tag":self._searchableTagValues(["embed","object","applet"])}
                else:


https://bitbucket.org/nvdaaddonteam/nvda/commits/db819f3a6f61/
Changeset:   db819f3a6f61
Branch:      None
User:        jteh
Date:        2014-02-20 07:51:44
Summary:     Merge branch 't1195' into next

Incubates #1195.

Affected #:  0 files



https://bitbucket.org/nvdaaddonteam/nvda/commits/41e2ffc5e8b2/
Changeset:   41e2ffc5e8b2
Branch:      None
User:        jteh
Date:        2014-02-20 07:51:56
Summary:     Merge branch 't3741' into next

Incubates #3741.

Affected #:  9 files

diff --git a/nvdaHelper/interfaces/vbuf/vbuf.idl 
b/nvdaHelper/interfaces/vbuf/vbuf.idl
index b952a0b..47e5877 100755
--- a/nvdaHelper/interfaces/vbuf/vbuf.idl
+++ b/nvdaHelper/interfaces/vbuf/vbuf.idl
@@ -124,13 +124,14 @@ interface VBuf {
  * @param buffer the virtual buffer to use
  * @param offset offset in the buffer to start searching from
  * @param direction which direction to search
- * @param attribsString the attributes the node should contain
+ * @param attribs the attributes to search
+ * @param regexp regular expression the requested attributes must match
  * @param startOffset memory where the start offset of the found node can be 
placed
  * @param endOffset memory where the end offset of the found node will be 
placed
  * @param foundNode the found field node
  * @return non-zero if the node is found.
  */
-       int findNodeByAttributes([in] VBufRemote_bufferHandle_t buffer, [in] 
int offset, [in] int direction, [in,string] const wchar_t* attribsString, [out] 
int *startOffset, [out] int *endOffset, [out] VBufRemote_nodeHandle_t* 
foundNode);
+       int findNodeByAttributes([in] VBufRemote_bufferHandle_t buffer, [in] 
int offset, [in] int direction, [in,string] const wchar_t* attribs, [in,string] 
const wchar_t* regexp, [out] int *startOffset, [out] int *endOffset, [out] 
VBufRemote_nodeHandle_t* foundNode);
 
 /**
  * Retreaves the current selection offsets for the buffer

diff --git a/nvdaHelper/remote/vbufRemote.cpp b/nvdaHelper/remote/vbufRemote.cpp
index 149a8c8..2905eb2 100755
--- a/nvdaHelper/remote/vbufRemote.cpp
+++ b/nvdaHelper/remote/vbufRemote.cpp
@@ -109,10 +109,10 @@ int 
VBufRemote_getIdentifierFromControlFieldNode(VBufRemote_bufferHandle_t buffe
        return res;
 }
 
-int VBufRemote_findNodeByAttributes(VBufRemote_bufferHandle_t buffer, int 
offset, int direction, const wchar_t* attribsString, int *startOffset, int 
*endOffset, VBufRemote_nodeHandle_t* foundNode) { 
+int VBufRemote_findNodeByAttributes(VBufRemote_bufferHandle_t buffer, int 
offset, int direction, const wchar_t* attribs, const wchar_t* regexp, int 
*startOffset, int *endOffset, VBufRemote_nodeHandle_t* foundNode) { 
        VBufBackend_t* backend=(VBufBackend_t*)buffer;
        backend->lock.acquire();
-       
*foundNode=(VBufRemote_nodeHandle_t)(backend->findNodeByAttributes(offset,(VBufStorage_findDirection_t)direction,attribsString,startOffset,endOffset));
+       
*foundNode=(VBufRemote_nodeHandle_t)(backend->findNodeByAttributes(offset,(VBufStorage_findDirection_t)direction,attribs,regexp,startOffset,endOffset));
        backend->lock.release();
        return (*foundNode)!=0;
 }

diff --git a/nvdaHelper/vbufBackends/gecko_ia2/gecko_ia2.cpp 
b/nvdaHelper/vbufBackends/gecko_ia2/gecko_ia2.cpp
index 9092b8b..68a232a 100755
--- a/nvdaHelper/vbufBackends/gecko_ia2/gecko_ia2.cpp
+++ b/nvdaHelper/vbufBackends/gecko_ia2/gecko_ia2.cpp
@@ -281,6 +281,9 @@ bool isLabelVisible(IAccessible2* acc) {
        return true;
 }
 
+const vector<wstring>ATTRLIST_ROLES(1, L"IAccessible2::attribute_xml-roles");
+const wregex 
REGEX_PRESENTATION_ROLE(L"IAccessible2\\\\:\\\\:attribute_xml-roles:.*\\bpresentation\\b.*;");
+
 VBufStorage_fieldNode_t* GeckoVBufBackend_t::fillVBuf(IAccessible2* pacc,
        VBufStorage_buffer_t* buffer, VBufStorage_controlFieldNode_t* 
parentNode, VBufStorage_fieldNode_t* previousNode,
        IAccessibleTable* paccTable, IAccessibleTable2* paccTable2, long 
tableID,
@@ -428,7 +431,7 @@ VBufStorage_fieldNode_t* 
GeckoVBufBackend_t::fillVBuf(IAccessible2* pacc,
        parentNode->isBlock=isBlockElement;
 
        // force   isHidden to True if this has an ARIA role of presentation 
but its focusble -- Gecko does not hide this itself.
-       
if((states&STATE_SYSTEM_FOCUSABLE)&&parentNode->matchAttributes(L"IAccessible2\\:\\:attribute_xml-roles:~wpresentation;"))
 {
+       
if((states&STATE_SYSTEM_FOCUSABLE)&&parentNode->matchAttributes(ATTRLIST_ROLES, 
REGEX_PRESENTATION_ROLE)) {
                parentNode->isHidden=true;
        }
        BSTR name=NULL;

diff --git a/nvdaHelper/vbufBase/storage.cpp b/nvdaHelper/vbufBase/storage.cpp
index 2a91469..ed7fbd3 100644
--- a/nvdaHelper/vbufBase/storage.cpp
+++ b/nvdaHelper/vbufBase/storage.cpp
@@ -19,6 +19,10 @@ http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 #include <list>
 #include <set>
 #include <sstream>
+#include <regex>
+#include <vector>
+#include <sstream>
+#include <algorithm>
 #include <common/xml.h>
 #include <common/log.h>
 #include "utils.h"
@@ -130,42 +134,30 @@ VBufStorage_fieldNode_t* 
VBufStorage_fieldNode_t::nextNodeInTree(int direction,
        return tempNode;
 }
 
-bool VBufStorage_fieldNode_t::matchAttributes(const std::wstring& 
attribsString) {
-       LOG_DEBUG(L"using attributes of "<<attribsString);
-       multiValueAttribsMap attribsMap;
-       multiValueAttribsStringToMap(attribsString,attribsMap);
-       bool outerMatch=true;
-       for(multiValueAttribsMap::iterator 
i=attribsMap.begin();i!=attribsMap.end();++i) {
-               LOG_DEBUG(L"Checking for attrib "<<i->first);
-               VBufStorage_attributeMap_t::iterator 
foundIterator=attributes.find(i->first);
-               const std::wstring& 
foundValue=(foundIterator!=attributes.end())?foundIterator->second:L"";
-               wstring foundValueTrailingSpaces=L" "+foundValue+L" ";
-               LOG_DEBUG(L"node's value for this attribute is "<<foundValue);
-               multiValueAttribsMap::iterator 
upperBound=attribsMap.upper_bound(i->first);
-               bool innerMatch=false;
-               for(multiValueAttribsMap::iterator j=i;j!=upperBound;++j) { 
-                       i=j;
-                       if(innerMatch)
-                               continue;
-                       LOG_DEBUG(L"Checking value "<<j->second);
-                       if(
-                               // Word match.
-                               (j->second.compare(0, 2, 
L"~w")==0&&foundValueTrailingSpaces.find(L" "+j->second.substr(2)+L" 
")!=wstring::npos)
-                               // Full match.
-                               ||(j->second==foundValue)
-                       ) {
-                               LOG_DEBUG(L"values match");
-                               innerMatch=true;
-                       }
-               }
-               outerMatch=innerMatch;
-               if(!outerMatch) { 
-                       LOG_DEBUG(L"given attributes do not match node's 
attributes, returning false");
-                       return false;
+inline void outputEscapedAttribute(wostringstream& out, const wstring& text) {
+       for (wstring::const_iterator it = text.begin(); it != text.end(); ++it) 
{
+               switch (*it) {
+                       case L':':
+                       case L';':
+                       case L'\\':
+                       out << L"\\";
+                       default:
+                       out << *it;
                }
        }
-       LOG_DEBUG(L"Given attributes match node's attributes, returning true");
-       return true;
+}
+
+bool VBufStorage_fieldNode_t::matchAttributes(const std::vector<std::wstring>& 
attribs, const std::wregex& regexp) {
+       wostringstream test;
+       for (vector<wstring>::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 {
@@ -896,7 +888,7 @@ VBufStorage_textContainer_t*  
VBufStorage_buffer_t::getTextInRange(int startOffs
        return new VBufStorage_textContainer_t(text);
 }
 
-VBufStorage_fieldNode_t* VBufStorage_buffer_t::findNodeByAttributes(int 
offset, VBufStorage_findDirection_t direction, const std::wstring& 
attribsString, int *startOffset, int *endOffset) {
+VBufStorage_fieldNode_t* VBufStorage_buffer_t::findNodeByAttributes(int 
offset, VBufStorage_findDirection_t direction, const std::wstring& attribs, 
const std::wstring &regexp, int *startOffset, int *endOffset) {
        if(this->rootNode==NULL) {
                LOG_DEBUGWARNING(L"buffer empty, returning NULL");
                return NULL;
@@ -922,6 +914,18 @@ VBufStorage_fieldNode_t* 
VBufStorage_buffer_t::findNodeByAttributes(int offset,
                LOG_DEBUGWARNING(L"Could not find node at offset "<<offset<<L", 
returning NULL");
                return NULL;
        }
+       // Split attribs at spaces.
+       vector<wstring> attribsList;
+       copy(istream_iterator<wstring, wchar_t, 
std::char_traits<wchar_t>>(wistringstream(attribs)),
+               istream_iterator<wstring, wchar_t, std::char_traits<wchar_t>>(),
+               back_inserter<vector<wstring> >(attribsList));
+       wregex regexObj;
+       try {
+               regexObj=wregex(regexp);
+       } catch (...) {
+               LOG_ERROR(L"Error in regular expression");
+               return NULL;
+       }
        LOG_DEBUG(L"starting from node "<<node->getDebugInfo());
        LOG_DEBUG(L"initial start is "<<bufferStart<<L" and initial end is 
"<<bufferEnd);
        if(direction==VBufStorage_findDirection_forward) {
@@ -931,7 +935,7 @@ VBufStorage_fieldNode_t* 
VBufStorage_buffer_t::findNodeByAttributes(int offset,
                        bufferEnd=bufferStart+node->length;
                        LOG_DEBUG(L"start is now "<<bufferStart<<L" and end is 
now "<<bufferEnd);
                        LOG_DEBUG(L"Checking node "<<node->getDebugInfo());
-                       
if(node->length>0&&!(node->isHidden)&&node->matchAttributes(attribsString)) {
+                       
if(node->length>0&&!(node->isHidden)&&node->matchAttributes(attribsList,regexObj))
 {
                                LOG_DEBUG(L"found a match");
                                break;
                        }
@@ -943,7 +947,7 @@ VBufStorage_fieldNode_t* 
VBufStorage_buffer_t::findNodeByAttributes(int offset,
                        bufferStart+=tempRelativeStart;
                        bufferEnd=bufferStart+node->length;
                        LOG_DEBUG(L"start is now "<<bufferStart<<L" and end is 
now "<<bufferEnd);
-                       
if(node->length>0&&!(node->isHidden)&&node->matchAttributes(attribsString)) {
+                       
if(node->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&&bufferStart<offset&&bufferEnd>offset))
 {
                                        LOG_DEBUG(L"skipping initial parent");
@@ -963,7 +967,7 @@ VBufStorage_fieldNode_t* 
VBufStorage_buffer_t::findNodeByAttributes(int offset,
                        if(node) {
                                bufferEnd=bufferStart+node->length;
                        }
-               } 
while(node!=NULL&&(node->isHidden||!node->matchAttributes(attribsString)));
+               } 
while(node!=NULL&&(node->isHidden||!node->matchAttributes(attribsList,regexObj)));
                LOG_DEBUG(L"end is now "<<bufferEnd);
        }
        if(node==NULL) {

diff --git a/nvdaHelper/vbufBase/storage.h b/nvdaHelper/vbufBase/storage.h
index 2a1e27a..3456584 100644
--- a/nvdaHelper/vbufBase/storage.h
+++ b/nvdaHelper/vbufBase/storage.h
@@ -19,6 +19,8 @@ http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 #include <map>
 #include <set>
 #include <list>
+#include <vector>
+#include <regex>
 
 /**
  * values to indicate a direction for searching
@@ -224,7 +226,7 @@ class VBufStorage_fieldNode_t {
  * @param attribsString the string containing the attributes, each attribute 
can have multiple values to match on.
   * @return true if the attributes exist, false otherwize.
  */
-       bool matchAttributes(const std::wstring& attribsString);
+       bool matchAttributes(const std::vector<std::wstring>& attribs, const 
std::wregex& regexp);
 
        /**
        * True if this node his hidden - searches will not locate this node.
@@ -554,12 +556,13 @@ class VBufStorage_buffer_t {
  * Finds a field node that contains particular attributes.
  * @param offset offset in the buffer to start searching from, if -1 then 
starts at the root of the buffer.
  * @param direction which direction to search
- * @param attribsString the attributes the node should contain
+ * @param attribs the attributes to search
+ * @param regexp regular expression the requested attributes must match
  * @param startOffset memory where the start offset of the found node can be 
placed
  * @param endOffset memory where the end offset of the found node will be 
placed
  * @return the found field node
  */
-       virtual VBufStorage_fieldNode_t* findNodeByAttributes(int offset, 
VBufStorage_findDirection_t  direction, const std::wstring &attribsString, int 
*startOffset, int *endOffset);
+       virtual VBufStorage_fieldNode_t* findNodeByAttributes(int offset, 
VBufStorage_findDirection_t  direction, const std::wstring &attribs, const 
std::wstring &regexp, int *startOffset, int *endOffset);
 
 /**
  * Retreaves the current selection offsets for the buffer

diff --git a/source/aria.py b/source/aria.py
index a23d2e4..8c54bc4 100755
--- a/source/aria.py
+++ b/source/aria.py
@@ -1,6 +1,6 @@
 #aria.py
 #A part of NonVisual Desktop Access (NVDA)
-#Copyright (C) 2009-2012 NV Access Limited
+#Copyright (C) 2009-2014 NV Access Limited
 #This file is covered by the GNU General Public License.
 #See the file COPYING for more details.
 
@@ -79,4 +79,7 @@ landmarkRoles = {
        "search": _("search"),
        # Translators: Reported for the form landmark, normally found on web 
pages.
        "form": _("form"),
+       # Strictly speaking, region isn't a landmark, but it is very similar.
+       # Translators: Reported for a significant region, normally found on web 
pages.
+       "region": _("region"),
 }

diff --git a/source/virtualBuffers/MSHTML.py b/source/virtualBuffers/MSHTML.py
index 010a7d5..bcb5284 100644
--- a/source/virtualBuffers/MSHTML.py
+++ b/source/virtualBuffers/MSHTML.py
@@ -6,7 +6,7 @@
 
 from comtypes import COMError
 import eventHandler
-from . import VirtualBuffer, VirtualBufferTextInfo, VBufStorage_findMatch_word
+from . import VirtualBuffer, VirtualBufferTextInfo, 
VBufStorage_findMatch_word, VBufStorage_findMatch_notEmpty
 import controlTypes
 import NVDAObjects.IAccessible.MSHTML
 import winUser
@@ -261,7 +261,11 @@ class MSHTML(VirtualBuffer):
                elif nodeType=="focusable":
                        
attrs={"IAccessible::state_%s"%oleacc.STATE_SYSTEM_FOCUSABLE:[1]}
                elif nodeType=="landmark":
-                       
attrs={"HTMLAttrib::role":[VBufStorage_findMatch_word(lr) for lr in 
aria.landmarkRoles]}
+                       attrs = [
+                               {"HTMLAttrib::role": 
[VBufStorage_findMatch_word(lr) for lr in aria.landmarkRoles if lr != 
"region"]},
+                               {"HTMLAttrib::role": 
[VBufStorage_findMatch_word("region")],
+                                       "name": 
[VBufStorage_findMatch_notEmpty]}
+                               ]
                elif nodeType == "embeddedObject":
                        attrs = {"IHTMLDOMNode::nodeName": 
["OBJECT","EMBED","APPLET"]}
                elif nodeType == "separator":

diff --git a/source/virtualBuffers/__init__.py 
b/source/virtualBuffers/__init__.py
index 1435c46..45f94c3 100644
--- a/source/virtualBuffers/__init__.py
+++ b/source/virtualBuffers/__init__.py
@@ -42,23 +42,57 @@ VBufStorage_findDirection_up=2
 VBufRemote_nodeHandle_t=ctypes.c_ulonglong
 
 
-def VBufStorage_findMatch_word(word):
-       return "~w%s" % word
-
-def dictToMultiValueAttribsString(d):
-       mainList=[]
-       for k,v in d.iteritems():
-               
k=unicode(k).replace(':','\\:').replace(';','\\;').replace(',','\\,')
-               valList=[]
-               for i in v:
-                       if i is None:
-                               i=""
+class VBufStorage_findMatch_word(unicode):
+       pass
+VBufStorage_findMatch_notEmpty = object()
+
+FINDBYATTRIBS_ESCAPE_TABLE = {
+       # Symbols that are escaped in the attributes string.
+       ord(u":"): ur"\\:",
+       ord(u";"): ur"\\;",
+       ord(u"\\"): u"\\\\\\\\",
+}
+# Symbols that must be escaped for a regular expression.
+FINDBYATTRIBS_ESCAPE_TABLE.update({(ord(s), u"\\" + s) for s in 
u"^$.*+?()[]{}|"})
+def _prepareForFindByAttributes(attribs):
+       escape = lambda text: 
unicode(text).translate(FINDBYATTRIBS_ESCAPE_TABLE)
+       reqAttrs = []
+       regexp = []
+       if isinstance(attribs, dict):
+               # Single option.
+               attribs = (attribs,)
+       # All options will match against all requested attributes,
+       # so first build the list of requested attributes.
+       for option in attribs:
+               for name in option:
+                       reqAttrs.append(unicode(name))
+       # Now build the regular expression.
+       for option in attribs:
+               optRegexp = []
+               for name in reqAttrs:
+                       optRegexp.append("%s:" % escape(name))
+                       values = option.get(name)
+                       if not values:
+                               # The value isn't tested for this attribute, so 
match any (or no) value.
+                               optRegexp.append(r"(?:\\;|[^;])*;")
+                       elif values[0] is VBufStorage_findMatch_notEmpty:
+                               # There must be a value for this attribute.
+                               optRegexp.append(r"(?:\\;|[^;])+;")
+                       elif values[0] is None:
+                               # There must be no value for this attribute.
+                               optRegexp.append(r";")
+                       elif isinstance(values[0], VBufStorage_findMatch_word):
+                               # Assume all are word matches.
+                               optRegexp.append(r"(?:\\;|[^;])*\b(?:")
+                               optRegexp.append("|".join(escape(val) for val 
in values))
+                               optRegexp.append(r")\b(?:\\;|[^;])*;")
                        else:
-                               
i=unicode(i).replace(':','\\:').replace(';','\\;').replace(',','\\,')
-                       valList.append(i)
-               attrib="%s:%s"%(k,",".join(valList))
-               mainList.append(attrib)
-       return "%s;"%";".join(mainList)
+                               # Assume all are exact matches.
+                               optRegexp.append("(?:")
+                               optRegexp.append("|".join(escape(val) for val 
in values))
+                               optRegexp.append(");")
+               regexp.append("".join(optRegexp))
+       return u" ".join(reqAttrs), u"|".join(regexp)
 
 class VirtualBufferTextInfo(textInfos.offsets.OffsetsTextInfo):
 
@@ -201,6 +235,10 @@ class 
VirtualBufferTextInfo(textInfos.offsets.OffsetsTextInfo):
                                
textList.append(self.obj.makeTextInfo(textInfos.offsets.Offsets(start, 
end)).text)
                        attrs["table-%sheadertext" % axis] = "\n".join(textList)
 
+               if attrs.get("landmark") == "region" and not attrs.get("name"):
+                       # We only consider region to be a landmark if it has a 
name.
+                       del attrs["landmark"]
+
                return attrs
 
        def _normalizeFormatField(self, attrs):
@@ -237,7 +275,11 @@ class 
VirtualBufferTextInfo(textInfos.offsets.OffsetsTextInfo):
                                textList.append(attrs["name"])
                        except KeyError:
                                pass
-                       textList.append(_("%s landmark") % 
aria.landmarkRoles[landmark])
+                       if landmark == "region":
+                               # The word landmark is superfluous for regions.
+                               textList.append(aria.landmarkRoles[landmark])
+                       else:
+                               textList.append(_("%s landmark") % 
aria.landmarkRoles[landmark])
                textList.append(super(VirtualBufferTextInfo, 
self).getControlFieldSpeech(attrs, ancestorAttrs, fieldType, formatConfig, 
extraDetail, reason))
                return " ".join(textList)
 
@@ -249,8 +291,12 @@ class 
VirtualBufferTextInfo(textInfos.offsets.OffsetsTextInfo):
                                textList.append(field["name"])
                        except KeyError:
                                pass
-                       # Translators: This is spoken and brailled to indicate 
a landmark (example output: main landmark).
-                       textList.append(_("%s landmark") % 
aria.landmarkRoles[landmark])
+                       if landmark == "region":
+                               # The word landmark is superfluous for regions.
+                               textList.append(aria.landmarkRoles[landmark])
+                       else:
+                               # Translators: This is spoken and brailled to 
indicate a landmark (example output: main landmark).
+                               textList.append(_("%s landmark") % 
aria.landmarkRoles[landmark])
                text = super(VirtualBufferTextInfo, 
self).getControlFieldBraille(field, ancestors, reportStart, formatConfig)
                if text:
                        textList.append(text)
@@ -459,12 +505,10 @@ class ElementsListDialog(wx.Dialog):
        def getElementText(self, elInfo, docHandle, id, elType):
                if elType == "landmark":
                        attrs = self._getControlFieldAttribs(elInfo, docHandle, 
id)
-                       landmark = attrs.get("landmark")
-                       if landmark:
-                               name = attrs.get("name", "")
-                               if name:
-                                       name += " "
-                               return name + aria.landmarkRoles[landmark]
+                       name = attrs.get("name", "")
+                       if name:
+                               name += " "
+                       return name + aria.landmarkRoles[attrs["landmark"]]
 
                else:
                        return elInfo.text.strip()
@@ -898,7 +942,7 @@ class VirtualBuffer(cursorManager.CursorManager, 
treeInterceptorHandler.TreeInte
                return self._iterNodesByAttribs(attribs, direction, offset)
 
        def _iterNodesByAttribs(self, attribs, direction="next", offset=-1):
-               attribs=dictToMultiValueAttribsString(attribs)
+               reqAttrs, regexp = _prepareForFindByAttributes(attribs)
                startOffset=ctypes.c_int()
                endOffset=ctypes.c_int()
                if direction=="next":
@@ -912,7 +956,7 @@ class VirtualBuffer(cursorManager.CursorManager, 
treeInterceptorHandler.TreeInte
                while True:
                        try:
                                node=VBufRemote_nodeHandle_t()
-                               
NVDAHelper.localLib.VBuf_findNodeByAttributes(self.VBufHandle,offset,direction,attribs,ctypes.byref(startOffset),ctypes.byref(endOffset),ctypes.byref(node))
+                               
NVDAHelper.localLib.VBuf_findNodeByAttributes(self.VBufHandle,offset,direction,reqAttrs,regexp,ctypes.byref(startOffset),ctypes.byref(endOffset),ctypes.byref(node))
                        except:
                                return
                        if not node:

diff --git a/source/virtualBuffers/gecko_ia2.py 
b/source/virtualBuffers/gecko_ia2.py
index 6d4f8e5..6c34466 100755
--- a/source/virtualBuffers/gecko_ia2.py
+++ b/source/virtualBuffers/gecko_ia2.py
@@ -4,7 +4,7 @@
 #See the file COPYING for more details.
 #Copyright (C) 2008-2012 NV Access Limited
 
-from . import VirtualBuffer, VirtualBufferTextInfo, VBufStorage_findMatch_word
+from . import VirtualBuffer, VirtualBufferTextInfo, 
VBufStorage_findMatch_word, VBufStorage_findMatch_notEmpty
 import treeInterceptorHandler
 import controlTypes
 import NVDAObjects.IAccessible.mozilla
@@ -217,7 +217,11 @@ class Gecko_ia2(VirtualBuffer):
                elif nodeType=="focusable":
                        
attrs={"IAccessible::state_%s"%oleacc.STATE_SYSTEM_FOCUSABLE:[1]}
                elif nodeType=="landmark":
-                       
attrs={"IAccessible2::attribute_xml-roles":[VBufStorage_findMatch_word(lr) for 
lr in aria.landmarkRoles]}
+                       attrs = [
+                               {"IAccessible2::attribute_xml-roles": 
[VBufStorage_findMatch_word(lr) for lr in aria.landmarkRoles if lr != 
"region"]},
+                               {"IAccessible2::attribute_xml-roles": 
[VBufStorage_findMatch_word("region")],
+                                       "name": 
[VBufStorage_findMatch_notEmpty]}
+                               ]
                elif nodeType=="embeddedObject":
                        
attrs={"IAccessible2::attribute_tag":self._searchableTagValues(["embed","object","applet"])}
                else:


https://bitbucket.org/nvdaaddonteam/nvda/commits/3d963157c035/
Changeset:   3d963157c035
Branch:      None
User:        mdcurran
Date:        2014-02-21 00:12:42
Summary:     Ensure that text copied to clipboard from displayModel contains 
line breaks.

Affected #:  1 file

diff --git a/source/displayModel.py b/source/displayModel.py
index 035ad7e..36e34d8 100644
--- a/source/displayModel.py
+++ b/source/displayModel.py
@@ -407,7 +407,7 @@ class DisplayModelTextInfo(OffsetsTextInfo):
                return startOffset,endOffset
 
        def _get_clipboardText(self):
-               return 
super(DisplayModelTextInfo,self).clipboardText.replace('\0',' ')
+               return "\r\n".join(x.strip('\r\n') for x in 
self.getTextInChunks(textInfos.UNIT_LINE))
 
        def getTextInChunks(self,unit):
                #Specifically handle the line unit as we have the line offsets 
pre-calculated, and we can not guarantee lines end with \n


https://bitbucket.org/nvdaaddonteam/nvda/commits/6b7430601ce9/
Changeset:   6b7430601ce9
Branch:      None
User:        mdcurran
Date:        2014-02-21 00:13:42
Summary:     Merge branch 't3900' into next. Incubates #3900

Affected #:  1 file

diff --git a/source/displayModel.py b/source/displayModel.py
index 2b3dec1..75d6ee4 100644
--- a/source/displayModel.py
+++ b/source/displayModel.py
@@ -449,7 +449,7 @@ class DisplayModelTextInfo(OffsetsTextInfo):
                return startOffset,endOffset
 
        def _get_clipboardText(self):
-               return 
super(DisplayModelTextInfo,self).clipboardText.replace('\0',' ')
+               return "\r\n".join(x.strip('\r\n') for x in 
self.getTextInChunks(textInfos.UNIT_LINE))
 
        def getTextInChunks(self,unit):
                #Specifically handle the line unit as we have the line offsets 
pre-calculated, and we can not guarantee lines end with \n


https://bitbucket.org/nvdaaddonteam/nvda/commits/e3eceac18ac8/
Changeset:   e3eceac18ac8
Branch:      None
User:        jteh
Date:        2014-02-22 09:27:48
Summary:     The focus is now restored correctly when releasing alt+tab without 
actually switching applications in some configurations.

We map switchEnd and some other events to the desktop window because the 
original window is invalid, so eventHandler.shouldAcceptEvent had to be taught 
this.
Re #3897.

Affected #:  1 file

diff --git a/source/eventHandler.py b/source/eventHandler.py
index e71b6c9..bfec029 100755
--- a/source/eventHandler.py
+++ b/source/eventHandler.py
@@ -194,6 +194,10 @@ def shouldAcceptEvent(eventName, windowHandle=None):
        if eventName == "alert" and 
winUser.getClassName(winUser.getAncestor(windowHandle, winUser.GA_PARENT)) == 
"ToastChildWindowClass":
                # Toast notifications.
                return True
+       if windowHandle == winUser.getDesktopWindow():
+               # #3897: We fire some events such as switchEnd and menuEnd on 
the desktop window
+               # because the original window is now invalid.
+               return True
 
        fg = winUser.getForegroundWindow()
        if winUser.isDescendantWindow(fg, windowHandle):


https://bitbucket.org/nvdaaddonteam/nvda/commits/7935e8f4fafb/
Changeset:   7935e8f4fafb
Branch:      None
User:        jteh
Date:        2014-02-22 09:47:35
Summary:     Stop ignoring the Firefox Page Bookmarked window, 
OpenOffice/LibreOffice context menus and possibly other cases.

In eventHandler.shouldAcceptEvent, as well as testing whether the window is a 
descendant of the foreground window, also check whether it's root owner is a 
descendant of the foreground.
Re #3899, #3905.

Affected #:  1 file

diff --git a/source/eventHandler.py b/source/eventHandler.py
index bfec029..b27ead7 100755
--- a/source/eventHandler.py
+++ b/source/eventHandler.py
@@ -200,7 +200,8 @@ def shouldAcceptEvent(eventName, windowHandle=None):
                return True
 
        fg = winUser.getForegroundWindow()
-       if winUser.isDescendantWindow(fg, windowHandle):
+       if (winUser.isDescendantWindow(fg, windowHandle)
+                       or winUser.isDescendantWindow(fg, 
winUser.getAncestor(windowHandle, winUser.GA_ROOTOWNER))):
                # This is for the foreground application.
                return True
        if (winUser.user32.GetWindowLongW(windowHandle, winUser.GWL_EXSTYLE) & 
winUser.WS_EX_TOPMOST


https://bitbucket.org/nvdaaddonteam/nvda/commits/e55984bccda6/
Changeset:   e55984bccda6
Branch:      next
User:        jteh
Date:        2014-02-22 09:49:31
Summary:     Merge branch 't3831' into next

Incubates #3831. Fixes #3899, #3905.

Affected #:  1 file

diff --git a/source/eventHandler.py b/source/eventHandler.py
index e71b6c9..b27ead7 100755
--- a/source/eventHandler.py
+++ b/source/eventHandler.py
@@ -194,9 +194,14 @@ def shouldAcceptEvent(eventName, windowHandle=None):
        if eventName == "alert" and 
winUser.getClassName(winUser.getAncestor(windowHandle, winUser.GA_PARENT)) == 
"ToastChildWindowClass":
                # Toast notifications.
                return True
+       if windowHandle == winUser.getDesktopWindow():
+               # #3897: We fire some events such as switchEnd and menuEnd on 
the desktop window
+               # because the original window is now invalid.
+               return True
 
        fg = winUser.getForegroundWindow()
-       if winUser.isDescendantWindow(fg, windowHandle):
+       if (winUser.isDescendantWindow(fg, windowHandle)
+                       or winUser.isDescendantWindow(fg, 
winUser.getAncestor(windowHandle, winUser.GA_ROOTOWNER))):
                # This is for the foreground application.
                return True
        if (winUser.user32.GetWindowLongW(windowHandle, winUser.GWL_EXSTYLE) & 
winUser.WS_EX_TOPMOST

Repository URL: https://bitbucket.org/nvdaaddonteam/nvda/

--

This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.

Other related posts: