Index: src/Fl_Browser.cxx
===================================================================
--- src/Fl_Browser.cxx	(revision 6732)
+++ src/Fl_Browser.cxx	(working copy)
@@ -835,6 +835,64 @@
   swap(ai,bi);
 }
 
+
+#ifdef FL_HAVE_MSAA
+#include <FL/Fl_Msaa_Proxy.H>
+///////////////////////////////////////////////////////////////////////
+//
+//  The following code implements Microsoft Active Accessibility
+
+long
+Fl_Browser::msaa_browser_size()
+{
+  return size();
+}
+
+
+bool
+Fl_Browser::msaa_browser_displayed(long id)
+{
+  return displayed(id);
+}
+
+
+bool
+Fl_Browser::msaa_browser_selected(long id)
+{
+  return selected(id);
+}
+
+
+long 
+Fl_Browser::msaa_browser_find_item(void* l)
+{
+  return lineno(l);
+}
+
+
+long
+Fl_Browser::msaa_browser_focus()
+{
+  return value();
+}
+
+
+void
+Fl_Browser::msaa_browser_set_focus(long id)
+{
+  select(id, selected(id));
+  display(id);
+}
+
+
+bool
+Fl_Browser::msaa_browser_text(long id, BSTR* bstr)
+{
+  fl_str_to_bstr(text(id), -1, bstr);
+  return true;
+}
+#endif // FL_HAVE_MSAA
+
 //
 // End of "$Id$".
 //
Index: src/Fl_Tabs.cxx
===================================================================
--- src/Fl_Tabs.cxx	(revision 6732)
+++ src/Fl_Tabs.cxx	(working copy)
@@ -285,6 +285,9 @@
       o->hide();
     }
   }
+#ifdef FL_HAVE_MSAA
+  if (ret && (Fl::focus() == this))  msaa_on_focus();
+#endif // FL_HAVE_MSAA
   return ret;
 }
 
@@ -420,8 +423,585 @@
 {
   box(FL_THIN_UP_BOX);
   push_ = 0;
+#ifdef FL_HAVE_MSAA
+  // Initialize MSAA stuff
+  msaa_tabs_proxy_ = 0;
+#endif // FL_HAVE_MSAA
 }
 
+
+#ifdef FL_HAVE_MSAA
+#include <FL/Fl_Msaa_Proxy.H>
+#include <FL/x.H>
+///////////////////////////////////////////////////////////////////////
+//
+//  The following code implements Microsoft Active Accessibility
+
+#define CID_TABS	1
+
+
+class Fl_Msaa_Tabs_Proxy : public Fl_Msaa_Proxy
+{
+public:
+  Fl_Msaa_Tabs_Proxy(Fl_Widget* o) : Fl_Msaa_Proxy(o)  {}
+  long LookupChild(Fl_Widget* o)  {return -1;}
+
+  // IAccessible implementation
+  HRESULT STDMETHODCALLTYPE get_accParent(IDispatch**);
+  HRESULT STDMETHODCALLTYPE get_accChildCount(long*);
+  HRESULT STDMETHODCALLTYPE get_accChild(VARIANT, IDispatch**);
+  HRESULT STDMETHODCALLTYPE get_accName(VARIANT, BSTR*);
+  HRESULT STDMETHODCALLTYPE get_accValue(VARIANT, BSTR*);
+  HRESULT STDMETHODCALLTYPE get_accDescription(VARIANT, BSTR*);
+  HRESULT STDMETHODCALLTYPE get_accRole(VARIANT, VARIANT*);
+  HRESULT STDMETHODCALLTYPE get_accState(VARIANT, VARIANT*);
+  HRESULT STDMETHODCALLTYPE get_accHelp(VARIANT, BSTR*);
+  HRESULT STDMETHODCALLTYPE get_accHelpTopic(BSTR*, VARIANT, long*);
+  HRESULT STDMETHODCALLTYPE get_accKeyboardShortcut(VARIANT, BSTR*);
+  HRESULT STDMETHODCALLTYPE get_accFocus(VARIANT*);
+  HRESULT STDMETHODCALLTYPE get_accSelection(VARIANT*);
+  HRESULT STDMETHODCALLTYPE get_accDefaultAction(VARIANT, BSTR*);
+  HRESULT STDMETHODCALLTYPE accSelect(long, VARIANT);
+  HRESULT STDMETHODCALLTYPE accLocation(long*, long*, long*, long*, VARIANT);
+  HRESULT STDMETHODCALLTYPE accNavigate(long, VARIANT, VARIANT*);
+  HRESULT STDMETHODCALLTYPE accHitTest(long, long, VARIANT*);
+  HRESULT STDMETHODCALLTYPE accDoDefaultAction(VARIANT);
+  HRESULT STDMETHODCALLTYPE put_accValue(VARIANT, BSTR);
+};
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Tabs_Proxy::get_accParent(
+    /* [retval][out] */ IDispatch** ppdispParent)
+{
+  Fl_Tabs* tabs = (Fl_Tabs*)mpObj;
+  if (!tabs)  return CO_E_OBJNOTCONNECTED;
+  if (!ppdispParent)  return E_INVALIDARG;
+  *ppdispParent = NULL;
+
+  // Fl_Msaa_Tabs_Proxy is a child of the widget's proxy
+  Fl_Msaa_Proxy* proxy = tabs->msaa_proxy();
+  if (!proxy)  return S_FALSE;
+  proxy->QueryInterface(IID_IDispatch, (void**)ppdispParent);
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Tabs_Proxy::get_accChildCount(
+    /* [retval][out] */ long* pcountChildren)
+{
+  Fl_Tabs* tabs = (Fl_Tabs*)mpObj;
+  if (!tabs)  return CO_E_OBJNOTCONNECTED;
+  if (!pcountChildren)  return E_INVALIDARG;
+
+  // Each child widget is represented by a tab
+  *pcountChildren = tabs->children();
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Tabs_Proxy::get_accChild(
+    /* [in] */ VARIANT varChild,
+    /* [retval][out] */ IDispatch** ppdispChild)
+{
+  Fl_Tabs* tabs = (Fl_Tabs*)mpObj;
+  if (!tabs)  return CO_E_OBJNOTCONNECTED;
+  if (varChild.vt != VT_I4)  return E_INVALIDARG;
+  if (!ppdispChild)  return E_INVALIDARG;
+  *ppdispChild = NULL;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > tabs->children())  return E_INVALIDARG;
+
+  // All tabs are simple elements
+  return S_FALSE;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Tabs_Proxy::get_accName(
+    /* [optional][in] */ VARIANT varChild,
+    /* [retval][out] */ BSTR* pszName)
+{
+  Fl_Tabs* tabs = (Fl_Tabs*)mpObj;
+  if (!tabs)  return CO_E_OBJNOTCONNECTED;
+  if (!pszName)  return E_INVALIDARG;
+  *pszName = NULL;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > tabs->children())  return E_INVALIDARG;
+
+  if (id && tabs->msaa_tabs_name(id, pszName))  return S_OK;
+  return DISP_E_MEMBERNOTFOUND;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Tabs_Proxy::get_accValue(
+    /* [optional][in] */ VARIANT varChild,
+    /* [retval][out] */ BSTR* pszValue)
+{
+  Fl_Tabs* tabs = (Fl_Tabs*)mpObj;
+  if (!tabs)  return CO_E_OBJNOTCONNECTED;
+  if (!pszValue)  return E_INVALIDARG;
+  *pszValue = NULL;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > tabs->children())  return E_INVALIDARG;
+
+  // Tabs do not have values
+  return DISP_E_MEMBERNOTFOUND;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Tabs_Proxy::get_accDescription(
+    /* [optional][in] */ VARIANT varChild,
+    /* [retval][out] */ BSTR* pszDescription)
+{
+  Fl_Tabs* tabs = (Fl_Tabs*)mpObj;
+  if (!tabs)  return CO_E_OBJNOTCONNECTED;
+  if (!pszDescription)  return E_INVALIDARG;
+  *pszDescription = NULL;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > tabs->children())  return E_INVALIDARG;
+
+  // Tabs do not have description
+  return DISP_E_MEMBERNOTFOUND;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Tabs_Proxy::get_accRole(
+    /* [optional][in] */ VARIANT varChild,
+    /* [retval][out] */ VARIANT* pvarRole)
+{
+  Fl_Tabs* tabs = (Fl_Tabs*)mpObj;
+  if (!tabs)  return CO_E_OBJNOTCONNECTED;
+  if (!pvarRole)  return E_INVALIDARG;
+  pvarRole->vt = VT_EMPTY;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > tabs->children())  return E_INVALIDARG;
+
+  pvarRole->vt   = VT_I4;
+  pvarRole->lVal = (id ? ROLE_SYSTEM_PAGETAB : ROLE_SYSTEM_PAGETABLIST);
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Tabs_Proxy::get_accState(
+    /* [optional][in] */ VARIANT varChild,
+    /* [retval][out] */ VARIANT* pvarState)
+{
+  Fl_Tabs* tabs = (Fl_Tabs*)mpObj;
+  if (!tabs)  return CO_E_OBJNOTCONNECTED;
+  if (!pvarState)  return E_INVALIDARG;
+  pvarState->vt = VT_EMPTY;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > tabs->children())  return E_INVALIDARG;
+
+  pvarState->vt   = VT_I4;
+  pvarState->lVal = tabs->msaa_tabs_state(id);
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Tabs_Proxy::get_accHelp(
+    /* [optional][in] */ VARIANT varChild,
+    /* [retval][out] */ BSTR* pszHelp)
+{
+  Fl_Tabs* tabs = (Fl_Tabs*)mpObj;
+  if (!tabs)  return CO_E_OBJNOTCONNECTED;
+  if (!pszHelp)  return E_INVALIDARG;
+  *pszHelp = NULL;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > tabs->children())  return E_INVALIDARG;
+
+  if (id && tabs->msaa_tabs_help(id, pszHelp))  return S_OK;
+  return DISP_E_MEMBERNOTFOUND;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Tabs_Proxy::get_accHelpTopic(
+    /* [out] */ BSTR* pszHelpFile,
+    /* [optional][in] */ VARIANT varChild,
+    /* [retval][out] */ long* pidTopic)
+{
+  Fl_Tabs* tabs = (Fl_Tabs*)mpObj;
+  if (!tabs)  return CO_E_OBJNOTCONNECTED;
+  if (!pszHelpFile)  return E_INVALIDARG;
+  if (!pidTopic)  return E_INVALIDARG;
+  *pszHelpFile = NULL;
+  *pidTopic = 0;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > tabs->children())  return E_INVALIDARG;
+
+  // Tabs do not have help topics
+  return DISP_E_MEMBERNOTFOUND;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Tabs_Proxy::get_accKeyboardShortcut(
+    /* [optional][in] */ VARIANT varChild,
+    /* [retval][out] */ BSTR* pszKeyboardShortcut)
+{
+  Fl_Tabs* tabs = (Fl_Tabs*)mpObj;
+  if (!tabs)  return CO_E_OBJNOTCONNECTED;
+  if (!pszKeyboardShortcut)  return E_INVALIDARG;
+  *pszKeyboardShortcut = NULL;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > tabs->children())  return E_INVALIDARG;
+
+  if (id && tabs->msaa_tabs_shortcut(id, pszKeyboardShortcut))  return S_OK;
+  return DISP_E_MEMBERNOTFOUND;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Tabs_Proxy::get_accFocus(
+    /* [retval][out] */ VARIANT *pvarChild)
+{
+  Fl_Tabs* tabs = (Fl_Tabs*)mpObj;
+  if (!tabs)  return CO_E_OBJNOTCONNECTED;
+  if (!pvarChild)  return E_INVALIDARG;
+  pvarChild->vt = VT_EMPTY;
+
+  // Report ID of selected tab
+  if (Fl::focus() != tabs)  return S_OK;
+  int id = tabs->msaa_tabs_selected_id();
+  if (id <= 0)  return S_OK;
+  pvarChild->vt   = VT_I4;
+  pvarChild->lVal = id;
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Tabs_Proxy::get_accSelection(
+    /* [retval][out] */ VARIANT* pvarChildren)
+{
+  Fl_Tabs* tabs = (Fl_Tabs*)mpObj;
+  if (!tabs)  return CO_E_OBJNOTCONNECTED;
+  if (!pvarChildren)  return E_INVALIDARG;
+  pvarChildren->vt = VT_EMPTY;
+
+  // Tab list do not have selection
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Tabs_Proxy::get_accDefaultAction(
+    /* [optional][in] */ VARIANT varChild,
+    /* [retval][out] */ BSTR* pszDefaultAction)
+{
+  Fl_Tabs* tabs = (Fl_Tabs*)mpObj;
+  if (!tabs)  return CO_E_OBJNOTCONNECTED;
+  if (!pszDefaultAction)  return E_INVALIDARG;
+  *pszDefaultAction = NULL;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > tabs->children())  return E_INVALIDARG;
+
+  // Switching between tabs is possible
+  if (id == 0)  return DISP_E_MEMBERNOTFOUND;
+  fl_str_to_bstr("Switch", -1, pszDefaultAction);
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Tabs_Proxy::accSelect(
+    /* [in] */ long flagsSelect,
+    /* [optional][in] */ VARIANT varChild)
+{
+  Fl_Tabs* tabs = (Fl_Tabs*)mpObj;
+  if (!tabs)  return CO_E_OBJNOTCONNECTED;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > tabs->children())  return E_INVALIDARG;
+
+  // Selection is not possible with tab list
+  return DISP_E_MEMBERNOTFOUND;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Tabs_Proxy::accLocation(
+    /* [out] */ long* pxLeft,
+    /* [out] */ long* pyTop,
+    /* [out] */ long* pcxWidth,
+    /* [out] */ long* pcyHeight,
+    /* [optional][in] */ VARIANT varChild)
+{
+  Fl_Tabs* tabs = (Fl_Tabs*)mpObj;
+  if (!tabs)  return CO_E_OBJNOTCONNECTED;
+  if (!pxLeft || !pyTop || !pcxWidth || !pcyHeight)  return E_INVALIDARG;
+  *pxLeft = *pyTop = *pcxWidth = *pcyHeight = 0;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > tabs->children())  return E_INVALIDARG;
+
+  if (tabs->msaa_tabs_location(id, pxLeft, pyTop, pcxWidth, pcyHeight))
+    return S_OK;
+  return DISP_E_MEMBERNOTFOUND;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Tabs_Proxy::accNavigate(
+    /* [in] */ long navDir,
+    /* [optional][in] */ VARIANT varStart,
+    /* [retval][out] */ VARIANT* pvarEndUpAt)
+{
+  Fl_Tabs* tabs = (Fl_Tabs*)mpObj;
+  if (!tabs)  return CO_E_OBJNOTCONNECTED;
+  if (!pvarEndUpAt)  return E_INVALIDARG;
+  pvarEndUpAt->vt = VT_EMPTY;
+  long id = (varStart.vt == VT_I4 ? varStart.lVal : 0);
+  if (id < 0 || id > tabs->children())  return E_INVALIDARG;
+
+  // Handle navigation relative to this widget
+  if ((id == 0) &&
+      (navDir != NAVDIR_FIRSTCHILD) && (navDir != NAVDIR_LASTCHILD))
+  {
+    // Forward request to Fl_Tabs widget
+    varStart.vt   = VT_I4;
+    varStart.lVal = CID_TABS;
+    return Fl_Msaa_Proxy::accNavigate(navDir, varStart, pvarEndUpAt);
+  }
+
+  // Handle navigation between tabs
+  long max_id = tabs->children();
+  if (max_id < 1)  return DISP_E_MEMBERNOTFOUND;
+  long dst;
+  switch (navDir)
+  {
+    case NAVDIR_FIRSTCHILD:  dst = 1;  break;
+    case NAVDIR_LASTCHILD:   dst = max_id;  break;
+    case NAVDIR_PREVIOUS:    dst = (id == 1 ? -1 : (id - 1));  break;
+    case NAVDIR_NEXT:	     dst = (id == max_id ? -1 : (id + 1));  break;
+
+    // Spatial navigation not implemented
+    case NAVDIR_UP:	     // Fall through
+    case NAVDIR_DOWN:	     // Fall through
+    case NAVDIR_LEFT:	     // Fall through
+    case NAVDIR_RIGHT:	     // Fall through
+    default:		     dst = -1;  break;
+  }
+  if (dst < 0)  return DISP_E_MEMBERNOTFOUND;
+  pvarEndUpAt->vt   = VT_I4;
+  pvarEndUpAt->lVal = dst;
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Tabs_Proxy::accHitTest(
+    /* [in] */ long xLeft,
+    /* [in] */ long yTop,
+    /* [retval][out] */ VARIANT* pvarChild)
+{
+  Fl_Tabs* tabs = (Fl_Tabs*)mpObj;
+  if (!tabs)  return CO_E_OBJNOTCONNECTED;
+  if (!pvarChild)  return E_INVALIDARG;
+  pvarChild->vt = VT_EMPTY;
+
+  // Make sure the point is inside this widget
+  long X, Y, W, H;
+  if (!tabs->msaa_tabs_location(0, &X, &Y, &W, &H))  return S_FALSE;
+  if ((xLeft < X) || (xLeft >= (X + W)))  return S_FALSE;
+  if ((yTop < Y) || (yTop >= (Y + H)))  return S_FALSE;
+
+  // Check child elements from top to bottom
+  for (int n = tabs->children();  n > 0;  --n)
+  {
+    if (!tabs->msaa_tabs_location(n, &X, &Y, &W, &H))  continue;
+    if ((xLeft < X) || (xLeft >= (X + W)))  continue;
+    if ((yTop < Y) || (yTop >= (Y + H)))  continue;
+
+    // Return child ID
+    pvarChild->vt   = VT_I4;
+    pvarChild->lVal = n;
+    return S_OK;
+  }
+
+  pvarChild->vt   = VT_I4;
+  pvarChild->lVal = 0;
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Tabs_Proxy::accDoDefaultAction(
+    /* [optional][in] */ VARIANT varChild)
+{
+  Fl_Tabs* tabs = (Fl_Tabs*)mpObj;
+  if (!tabs)  return CO_E_OBJNOTCONNECTED;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > tabs->children())  return E_INVALIDARG;
+
+  // Switch to required tab
+  if (id == 0)  return DISP_E_MEMBERNOTFOUND;
+  tabs->value(tabs->child(id - 1));
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Tabs_Proxy::put_accValue(
+    /* [optional][in] */ VARIANT varChild,
+    /* [in] */ BSTR szValue)
+{
+  Fl_Tabs* tabs = (Fl_Tabs*)mpObj;
+  if (!tabs)  return CO_E_OBJNOTCONNECTED;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > tabs->children())  return E_INVALIDARG;
+
+  // Tab list does not accept value
+  return DISP_E_MEMBERNOTFOUND;
+}
+
+
+/*------------------------- Fl_Tabs MSAA support -----------------------*/
+
+void
+Fl_Tabs::msaa_tabs_on_focus(long id)
+{
+  if (Fl::focus() != this)  return;
+  Fl_Msaa_Proxy* proxy = msaa_child(CID_TABS);
+  NotifyWinEvent(EVENT_OBJECT_FOCUS, fl_xid(msaa_window()), (LONG)proxy, id);
+}
+
+
+Fl_Msaa_Proxy*
+Fl_Tabs::msaa_child(long id)
+{
+  if (id != CID_TABS)  return Fl_Group::msaa_child(id);
+
+  // Create specialized proxy for tab list
+  if (!msaa_tabs_proxy_)
+  {
+    msaa_tabs_proxy_ = new Fl_Msaa_Tabs_Proxy(this);
+    if (msaa_tabs_proxy_)  msaa_tabs_proxy_->AddRef();
+  }
+  return msaa_tabs_proxy_;
+}
+
+
+Fl_Widget*
+Fl_Tabs::msaa_child_widget(long id)  const
+{
+  if (id == 0)  return (Fl_Widget*)this;
+  if (id != CID_TABS)  return Fl_Group::msaa_child_widget(id - 1);
+  return NULL;
+}
+
+
+long
+Fl_Tabs::msaa_child_id(Fl_Widget* o) const
+{
+  long id = Fl_Group::msaa_child_id(o);
+  if (id <= 0)  return id;
+  return id + 1;
+}
+
+
+void
+Fl_Tabs::msaa_cleanup()
+{
+  // Destroy custom proxy
+  if (msaa_tabs_proxy_)
+  {
+    msaa_tabs_proxy_->Detach();
+    msaa_tabs_proxy_->Release();
+  }
+
+  // Destroy standard proxy
+  Fl_Group::msaa_cleanup();
+}
+
+
+long
+Fl_Tabs::msaa_tabs_selected_id()
+{
+  for (int n = 0;  n < children();  ++n)
+    if (child(n)->visible())  return n + 1;
+  return 0;
+}
+
+
+bool
+Fl_Tabs::msaa_tabs_name(long id, BSTR* bstr)
+{
+  return Fl_Group::msaa_name(id + 1, bstr);
+}
+
+
+long
+Fl_Tabs::msaa_tabs_state(long id)
+{
+  long state = Fl_Group::msaa_state(0) & ~STATE_SYSTEM_FOCUSED;
+  if (id)
+  {
+    state |= (STATE_SYSTEM_SELECTABLE | STATE_SYSTEM_FOCUSABLE);
+    if (id == msaa_tabs_selected_id())
+    {
+      state |= STATE_SYSTEM_SELECTED;
+      if (Fl::focus() == this)  state |= STATE_SYSTEM_FOCUSED;
+    }
+  }
+  return state;
+}
+
+
+bool
+Fl_Tabs::msaa_tabs_shortcut(long id, BSTR* bstr)
+{
+  return Fl_Group::msaa_shortcut(id + 1, bstr);
+}
+
+
+bool
+Fl_Tabs::msaa_tabs_help(long id, BSTR* bstr)
+{
+  return Fl_Group::msaa_help(id + 1, bstr);
+}
+
+
+bool
+Fl_Tabs::msaa_tabs_location(long id, long* X, long* Y, long* W, long* H)
+{
+  if (!Fl_Group::msaa_location(0, X, Y, W, H))  return false;
+  int tab_h = tab_height();
+  if (tab_h >= 0)  *H = tab_h + Fl::box_dy(box());
+  else
+  {
+    *H = Fl::box_dy(box()) - tab_h;
+    *Y += (h() - tab_h);
+  }
+  if (id == 0)  return true;
+
+  // Find location of required tab button
+  int p[128];
+  int wp[128];
+  tab_positions(p, wp);
+  *X += p[id - 1];
+  *W  = wp[id - 1];
+  return true;
+}
+
+
+void
+Fl_Tabs::msaa_on_focus()
+{
+  // Focusing Fl_Tabs widget means active tab button is focused 
+  int id = msaa_tabs_selected_id();
+  if (!id)  return;
+  msaa_tabs_on_focus(id);
+}
+#endif // FL_HAVE_MSAA
+
 //
 // End of "$Id$".
 //
Index: src/Fl_Text_Editor.cxx
===================================================================
--- src/Fl_Text_Editor.cxx	(revision 6732)
+++ src/Fl_Text_Editor.cxx	(working copy)
@@ -498,6 +498,9 @@
       if (buffer()->selected()) redraw(); // Redraw selections...
     case FL_HIDE:
       if (when() & FL_WHEN_RELEASE) maybe_do_callback();
+#ifdef FL_HAVE_MSAA
+      msaa_hide_caret();
+#endif // FL_HAVE_MSAA
       return 1;
 
     case FL_KEYBOARD:
Index: src/Fl_Input_.cxx
===================================================================
--- src/Fl_Input_.cxx	(revision 6732)
+++ src/Fl_Input_.cxx	(working copy)
@@ -298,6 +298,10 @@
 	x1 += (float)expandpos(p, pp, buf, &offset1);
 	fl_draw(buf, offset1, xpos, (float)(Y+ypos+desc));
       }
+#ifdef FL_HAVE_MSAA
+      if ((pp >= p) && (position() == selstart))
+        msaa_show_caret((int)(x1-1), Y+ypos, 2, height);
+#endif // FL_HAVE_MSAA
       pp = value()+selend;
       float x2 = (float)(X+W);
       int offset2;
@@ -311,6 +315,10 @@
 	fl_color(tc);
 	fl_draw(buf+offset2, strlen(buf+offset2), x2, (float)(Y+ypos+desc));
       }
+#ifdef FL_HAVE_MSAA
+      if ((pp <= e) && (position() == selend))
+        msaa_show_caret((int)(x2-1), Y+ypos, 2, height);
+#endif // FL_HAVE_MSAA
     } else {
       // draw unselected text
       fl_color(tc);
@@ -331,6 +339,9 @@
       } else {
         fl_rectf((int)(xpos+curx+0.5), Y+ypos, 2, height);
       }
+#ifdef FL_HAVE_MSAA
+      msaa_show_caret((int)(xpos+curx+0.5), Y+ypos, 2, height);
+#endif // FL_HAVE_MSAA
     }
 
   CONTINUE:
@@ -811,6 +822,9 @@
     fl_reset_spot();
     if (!readonly() && (when() & FL_WHEN_RELEASE))
       maybe_do_callback();
+#ifdef FL_HAVE_MSAA
+    msaa_hide_caret();
+#endif // FL_HAVE_MSAA
     return 1;
 
   case FL_PUSH:
@@ -983,6 +997,9 @@
     minimal_update(0);
   }
   position(readonly() ? 0 : size());
+#ifdef FL_HAVE_MSAA
+  msaa_input_on_change();
+#endif // FL_HAVE_MSAA
   return 1;
 }
 
@@ -1022,6 +1039,56 @@
   if (bufsize) free((void*)buffer);
 }
 
+
+#ifdef FL_HAVE_MSAA
+#include <FL/Fl_Msaa_Proxy.H>
+///////////////////////////////////////////////////////////////////////
+//
+//  The following code implements Microsoft Active Accessibility
+
+void
+Fl_Input_::msaa_input_on_change()
+{
+  msaa_notify(EVENT_OBJECT_VALUECHANGE);
+}
+
+
+long
+Fl_Input_::msaa_role(long id)
+{
+  if (id != 0)  return Fl_Widget::msaa_role(id);
+  return ROLE_SYSTEM_TEXT;
+}
+
+
+bool
+Fl_Input_::msaa_value(long id, BSTR* bstr)
+{
+  if (id != 0)  return false;
+  if (input_type() != FL_SECRET_INPUT)
+    fl_str_to_bstr(value(), -1, bstr);
+  else
+  {
+    int   len = size();
+    char* s = (char*)malloc(len);
+    memset(s, '*', len);
+    fl_str_to_bstr(s, len, bstr);
+    fl_free_str(s);
+  }
+  return true;
+}
+
+
+bool 
+Fl_Input_::msaa_put_value(long id, const char* str)
+{
+  if (id != 0)  return false;
+  value(str);
+  return true;
+}
+#endif // FL_HAVE_MSAA
+
+
 //
 // End of "$Id$".
 //
Index: src/Fl_Scroll.cxx
===================================================================
--- src/Fl_Scroll.cxx	(revision 6732)
+++ src/Fl_Scroll.cxx	(working copy)
@@ -29,6 +29,9 @@
 #include <FL/Fl_Tiled_Image.H>
 #include <FL/Fl_Scroll.H>
 #include <FL/fl_draw.H>
+#ifdef FL_HAVE_MSAA
+#include <FL/Fl_Msaa_Proxy.H>
+#endif
 
 /** Clear all but the scrollbars... */
 void Fl_Scroll::clear() {
@@ -317,6 +320,12 @@
   hscrollbar.type(FL_HORIZONTAL);
   hscrollbar.callback(hscrollbar_cb);
   scrollbar.callback(scrollbar_cb);
+#ifdef FL_HAVE_MSAA
+  // Initialize MSAA stuff
+  msaa_scroll_proxy_ = 0;
+  scrollbar.msaa_proxy()->OverrideParent(msaa_proxy());
+  hscrollbar.msaa_proxy()->OverrideParent(msaa_proxy());
+#endif // FL_HAVE_MSAA
 }
 
 int Fl_Scroll::handle(int event) {
@@ -324,6 +333,606 @@
   return Fl_Group::handle(event);
 }
 
+
+#ifdef FL_HAVE_MSAA
+///////////////////////////////////////////////////////////////////////
+//
+//  The following code implements Microsoft Active Accessibility
+
+#define CID_INSIDE	1
+#define CID_VERTICAL	2
+#define CID_HORIZONTAL	3
+#define CID_GRIP	4
+
+
+class Fl_Msaa_Scroll_Proxy : public Fl_Msaa_Proxy
+{
+public:
+  Fl_Msaa_Scroll_Proxy(Fl_Widget* o) : Fl_Msaa_Proxy(o)  {}
+  long LookupChild(Fl_Widget* o);
+
+  // IAccessible implementation
+  HRESULT STDMETHODCALLTYPE get_accParent(IDispatch**);
+  HRESULT STDMETHODCALLTYPE get_accChildCount(long*);
+  HRESULT STDMETHODCALLTYPE get_accChild(VARIANT, IDispatch**);
+  HRESULT STDMETHODCALLTYPE get_accName(VARIANT, BSTR*);
+  HRESULT STDMETHODCALLTYPE get_accValue(VARIANT, BSTR*);
+  HRESULT STDMETHODCALLTYPE get_accDescription(VARIANT, BSTR*);
+  HRESULT STDMETHODCALLTYPE get_accRole(VARIANT, VARIANT*);
+  HRESULT STDMETHODCALLTYPE get_accState(VARIANT, VARIANT*);
+  HRESULT STDMETHODCALLTYPE get_accHelp(VARIANT, BSTR*);
+  HRESULT STDMETHODCALLTYPE get_accHelpTopic(BSTR*, VARIANT, long*);
+  HRESULT STDMETHODCALLTYPE get_accKeyboardShortcut(VARIANT, BSTR*);
+  HRESULT STDMETHODCALLTYPE get_accFocus(VARIANT*);
+  HRESULT STDMETHODCALLTYPE get_accSelection(VARIANT*);
+  HRESULT STDMETHODCALLTYPE get_accDefaultAction(VARIANT, BSTR*);
+  HRESULT STDMETHODCALLTYPE accSelect(long, VARIANT);
+  HRESULT STDMETHODCALLTYPE accLocation(long*, long*, long*, long*, VARIANT);
+  HRESULT STDMETHODCALLTYPE accNavigate(long, VARIANT, VARIANT*);
+  HRESULT STDMETHODCALLTYPE accHitTest(long, long, VARIANT*);
+  HRESULT STDMETHODCALLTYPE accDoDefaultAction(VARIANT);
+  HRESULT STDMETHODCALLTYPE put_accValue(VARIANT, BSTR);
+};
+
+
+long
+Fl_Msaa_Scroll_Proxy::LookupChild(Fl_Widget* o)
+{
+  Fl_Scroll* scroll = (Fl_Scroll*)mpObj;
+  if (!scroll)  return -1;
+  if (o == &scroll->scrollbar)  return CID_VERTICAL;
+  if (o == &scroll->hscrollbar)  return CID_HORIZONTAL;
+  return -1;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Scroll_Proxy::get_accParent(
+    /* [retval][out] */ IDispatch** ppdispParent)
+{
+  Fl_Scroll* scroll = (Fl_Scroll*)mpObj;
+  if (!scroll)  return CO_E_OBJNOTCONNECTED;
+  if (!ppdispParent)  return E_INVALIDARG;
+  *ppdispParent = NULL;
+
+  // Fl_Msaa_Scroll_Proxy_ is directly under parent widget
+  if (!scroll->msaa_scroll_parent(ppdispParent))  return S_FALSE;
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Scroll_Proxy::get_accChildCount(
+    /* [retval][out] */ long* pcountChildren)
+{
+  Fl_Scroll* scroll = (Fl_Scroll*)mpObj;
+  if (!scroll)  return CO_E_OBJNOTCONNECTED;
+  if (!pcountChildren)  return E_INVALIDARG;
+
+  *pcountChildren = scroll->msaa_scroll_size();
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Scroll_Proxy::get_accChild(
+    /* [in] */ VARIANT varChild,
+    /* [retval][out] */ IDispatch** ppdispChild)
+{
+  Fl_Scroll* scroll = (Fl_Scroll*)mpObj;
+  if (!scroll)  return CO_E_OBJNOTCONNECTED;
+  if (varChild.vt != VT_I4)  return E_INVALIDARG;
+  if (!ppdispChild)  return E_INVALIDARG;
+  *ppdispChild = NULL;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > scroll->msaa_scroll_size())  return E_INVALIDARG;
+
+  Fl_Msaa_Proxy* proxy = scroll->msaa_scroll_child(id);
+  if (!proxy)  return S_FALSE;
+  proxy->QueryInterface(IID_IDispatch, (void**)ppdispChild);
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Scroll_Proxy::get_accName(
+    /* [optional][in] */ VARIANT varChild,
+    /* [retval][out] */ BSTR* pszName)
+{
+  Fl_Scroll* scroll = (Fl_Scroll*)mpObj;
+  if (!scroll)  return CO_E_OBJNOTCONNECTED;
+  if (!pszName)  return E_INVALIDARG;
+  *pszName = NULL;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > scroll->msaa_scroll_size())  return E_INVALIDARG;
+
+  return DISP_E_MEMBERNOTFOUND;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Scroll_Proxy::get_accValue(
+    /* [optional][in] */ VARIANT varChild,
+    /* [retval][out] */ BSTR* pszValue)
+{
+  Fl_Scroll* scroll = (Fl_Scroll*)mpObj;
+  if (!scroll)  return CO_E_OBJNOTCONNECTED;
+  if (!pszValue)  return E_INVALIDARG;
+  *pszValue = NULL;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > scroll->msaa_scroll_size())  return E_INVALIDARG;
+
+  return DISP_E_MEMBERNOTFOUND;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Scroll_Proxy::get_accDescription(
+    /* [optional][in] */ VARIANT varChild,
+    /* [retval][out] */ BSTR* pszDescription)
+{
+  Fl_Scroll* scroll = (Fl_Scroll*)mpObj;
+  if (!scroll)  return CO_E_OBJNOTCONNECTED;
+  if (!pszDescription)  return E_INVALIDARG;
+  *pszDescription = NULL;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > scroll->msaa_scroll_size())  return E_INVALIDARG;
+
+  return DISP_E_MEMBERNOTFOUND;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Scroll_Proxy::get_accRole(
+    /* [optional][in] */ VARIANT varChild,
+    /* [retval][out] */ VARIANT* pvarRole)
+{
+  Fl_Scroll* scroll = (Fl_Scroll*)mpObj;
+  if (!scroll)  return CO_E_OBJNOTCONNECTED;
+  if (!pvarRole)  return E_INVALIDARG;
+  pvarRole->vt = VT_EMPTY;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > scroll->msaa_scroll_size())  return E_INVALIDARG;
+
+  pvarRole->vt   = VT_I4;
+  pvarRole->lVal = scroll->msaa_scroll_role(id);
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Scroll_Proxy::get_accState(
+    /* [optional][in] */ VARIANT varChild,
+    /* [retval][out] */ VARIANT* pvarState)
+{
+  Fl_Scroll* scroll = (Fl_Scroll*)mpObj;
+  if (!scroll)  return CO_E_OBJNOTCONNECTED;
+  if (!pvarState)  return E_INVALIDARG;
+  pvarState->vt = VT_EMPTY;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > scroll->msaa_scroll_size())  return E_INVALIDARG;
+
+  pvarState->vt   = VT_I4;
+  pvarState->lVal = scroll->msaa_scroll_state(id);
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Scroll_Proxy::get_accHelp(
+    /* [optional][in] */ VARIANT varChild,
+    /* [retval][out] */ BSTR* pszHelp)
+{
+  Fl_Scroll* scroll = (Fl_Scroll*)mpObj;
+  if (!scroll)  return CO_E_OBJNOTCONNECTED;
+  if (!pszHelp)  return E_INVALIDARG;
+  *pszHelp = NULL;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > scroll->msaa_scroll_size())  return E_INVALIDARG;
+
+  return DISP_E_MEMBERNOTFOUND;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Scroll_Proxy::get_accHelpTopic(
+    /* [out] */ BSTR* pszHelpFile,
+    /* [optional][in] */ VARIANT varChild,
+    /* [retval][out] */ long* pidTopic)
+{
+  Fl_Scroll* scroll = (Fl_Scroll*)mpObj;
+  if (!scroll)  return CO_E_OBJNOTCONNECTED;
+  if (!pszHelpFile)  return E_INVALIDARG;
+  if (!pidTopic)  return E_INVALIDARG;
+  *pszHelpFile = NULL;
+  *pidTopic = 0;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > scroll->msaa_scroll_size())  return E_INVALIDARG;
+
+  return DISP_E_MEMBERNOTFOUND;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Scroll_Proxy::get_accKeyboardShortcut(
+    /* [optional][in] */ VARIANT varChild,
+    /* [retval][out] */ BSTR* pszKeyboardShortcut)
+{
+  Fl_Scroll* scroll = (Fl_Scroll*)mpObj;
+  if (!scroll)  return CO_E_OBJNOTCONNECTED;
+  if (!pszKeyboardShortcut)  return E_INVALIDARG;
+  *pszKeyboardShortcut = NULL;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > scroll->msaa_scroll_size())  return E_INVALIDARG;
+
+  return DISP_E_MEMBERNOTFOUND;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Scroll_Proxy::get_accFocus(
+    /* [retval][out] */ VARIANT *pvarChild)
+{
+  Fl_Scroll* scroll = (Fl_Scroll*)mpObj;
+  if (!scroll)  return CO_E_OBJNOTCONNECTED;
+  if (!pvarChild)  return E_INVALIDARG;
+  pvarChild->vt = VT_EMPTY;
+
+  if (scroll->msaa_focus() < 0)  return S_OK;
+  Fl_Msaa_Proxy* proxy = scroll->msaa_child(0);
+  if (!proxy)
+  {
+    pvarChild->vt   = VT_I4;
+    pvarChild->lVal = CID_INSIDE;
+  }
+  else
+  {
+    pvarChild->vt = VT_DISPATCH;
+    proxy->QueryInterface(IID_IDispatch, (void**)&pvarChild->pdispVal);
+  }
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Scroll_Proxy::get_accSelection(
+    /* [retval][out] */ VARIANT* pvarChildren)
+{
+  Fl_Scroll* scroll = (Fl_Scroll*)mpObj;
+  if (!scroll)  return CO_E_OBJNOTCONNECTED;
+  if (!pvarChildren)  return E_INVALIDARG;
+  pvarChildren->vt = VT_EMPTY;
+
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Scroll_Proxy::get_accDefaultAction(
+    /* [optional][in] */ VARIANT varChild,
+    /* [retval][out] */ BSTR* pszDefaultAction)
+{
+  Fl_Scroll* scroll = (Fl_Scroll*)mpObj;
+  if (!scroll)  return CO_E_OBJNOTCONNECTED;
+  if (!pszDefaultAction)  return E_INVALIDARG;
+  pszDefaultAction = NULL;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > scroll->msaa_scroll_size())  return E_INVALIDARG;
+
+  return DISP_E_MEMBERNOTFOUND;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Scroll_Proxy::accSelect(
+    /* [in] */ long flagsSelect,
+    /* [optional][in] */ VARIANT varChild)
+{
+  Fl_Scroll* scroll = (Fl_Scroll*)mpObj;
+  if (!scroll)  return CO_E_OBJNOTCONNECTED;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > scroll->msaa_scroll_size())  return E_INVALIDARG;
+
+  return DISP_E_MEMBERNOTFOUND;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Scroll_Proxy::accLocation(
+    /* [out] */ long* pxLeft,
+    /* [out] */ long* pyTop,
+    /* [out] */ long* pcxWidth,
+    /* [out] */ long* pcyHeight,
+    /* [optional][in] */ VARIANT varChild)
+{
+  Fl_Scroll* scroll = (Fl_Scroll*)mpObj;
+  if (!scroll)  return CO_E_OBJNOTCONNECTED;
+  if (!pxLeft || !pyTop || !pcxWidth || !pcyHeight)  return E_INVALIDARG;
+  *pxLeft = *pyTop = *pcxWidth = *pcyHeight = 0;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > scroll->msaa_scroll_size())  return E_INVALIDARG;
+
+  if (!scroll->msaa_scroll_location(id, pxLeft, pyTop, pcxWidth, pcyHeight))
+    return DISP_E_MEMBERNOTFOUND;
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Scroll_Proxy::accNavigate(
+    /* [in] */ long navDir,
+    /* [optional][in] */ VARIANT varStart,
+    /* [retval][out] */ VARIANT* pvarEndUpAt)
+{
+  Fl_Scroll* scroll = (Fl_Scroll*)mpObj;
+  if (!scroll)  return CO_E_OBJNOTCONNECTED;
+  if (!pvarEndUpAt)  return E_INVALIDARG;
+  pvarEndUpAt->vt = VT_EMPTY;
+  long id = (varStart.vt == VT_I4 ? varStart.lVal : 0);
+  if (id < 0 || id > scroll->msaa_scroll_size())  return E_INVALIDARG;
+
+  // Handle navigation relative to this widget
+  if ((id == 0) &&
+      (navDir != NAVDIR_FIRSTCHILD) && (navDir != NAVDIR_LASTCHILD))
+    return Fl_Msaa_Proxy::accNavigate(navDir, varStart, pvarEndUpAt);
+
+  // Handle navigation within scroll content
+  long max_id = scroll->msaa_scroll_size();
+  if (max_id < 1)  return DISP_E_MEMBERNOTFOUND;
+  long dst;
+  switch (navDir)
+  {
+    case NAVDIR_FIRSTCHILD:  dst = 1;  break;
+    case NAVDIR_LASTCHILD:   dst = max_id;  break;
+    case NAVDIR_PREVIOUS:    dst = (id == 1 ? -1 : (id - 1));  break;
+    case NAVDIR_NEXT:	     dst = (id == max_id ? -1 : (id + 1));  break;
+
+    // Spatial navigation not implemented
+    case NAVDIR_UP:	     // Fall through
+    case NAVDIR_DOWN:	     // Fall through
+    case NAVDIR_LEFT:	     // Fall through
+    case NAVDIR_RIGHT:	     // Fall through
+    default:		     dst = -1;  break;
+  }
+  if (dst < 0)  return DISP_E_MEMBERNOTFOUND;
+  Fl_Msaa_Proxy* proxy = scroll->msaa_scroll_child(dst);
+  if (!proxy)
+  {
+    pvarEndUpAt->vt   = VT_I4;
+    pvarEndUpAt->lVal = dst;
+  }
+  else
+  {
+    pvarEndUpAt->vt = VT_DISPATCH;
+    proxy->QueryInterface(IID_IDispatch, (void**)&pvarEndUpAt->pdispVal);
+  }
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Scroll_Proxy::accHitTest(
+    /* [in] */ long xLeft,
+    /* [in] */ long yTop,
+    /* [retval][out] */ VARIANT* pvarChild)
+{
+  Fl_Scroll* scroll = (Fl_Scroll*)mpObj;
+  if (!scroll)  return CO_E_OBJNOTCONNECTED;
+  if (!pvarChild)  return E_INVALIDARG;
+  pvarChild->vt = VT_EMPTY;
+
+  // Make sure the point is inside this widget
+  long X, Y, W, H;
+  if (!scroll->msaa_scroll_location(0, &X, &Y, &W, &H))  return S_FALSE;
+  if ((xLeft < X) || (xLeft >= (X + W)))  return S_FALSE;
+  if ((yTop < Y) || (yTop >= (Y + H)))  return S_FALSE;
+
+  // Check child elements from top to bottom
+  for (int n = scroll->msaa_scroll_size();  n > 0;  --n)
+  {
+    if (!scroll->msaa_scroll_location(n, &X, &Y, &W, &H))  continue;
+    if ((xLeft < X) || (xLeft >= (X + W)))  continue;
+    if ((yTop < Y) || (yTop >= (Y + H)))  continue;
+
+    // Try to obtain child proxy
+    Fl_Msaa_Proxy* proxy = scroll->msaa_scroll_child(n);
+    if (proxy)
+    {
+	pvarChild->vt = VT_DISPATCH;
+	proxy->QueryInterface(IID_IDispatch, (void**)&pvarChild->pdispVal);
+	return S_OK;
+    }
+
+    // Otherwise return child ID
+    pvarChild->vt   = VT_I4;
+    pvarChild->lVal = n;
+    return S_OK;
+  }
+
+  pvarChild->vt   = VT_I4;
+  pvarChild->lVal = 0;
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Scroll_Proxy::accDoDefaultAction(
+    /* [optional][in] */ VARIANT varChild)
+{
+  Fl_Scroll* scroll = (Fl_Scroll*)mpObj;
+  if (!scroll)  return CO_E_OBJNOTCONNECTED;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > scroll->msaa_scroll_size())  return E_INVALIDARG;
+
+  return DISP_E_MEMBERNOTFOUND;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Scroll_Proxy::put_accValue(
+    /* [optional][in] */ VARIANT varChild,
+    /* [in] */ BSTR szValue)
+{
+  Fl_Scroll* scroll = (Fl_Scroll*)mpObj;
+  if (!scroll)  return CO_E_OBJNOTCONNECTED;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > scroll->msaa_scroll_size())  return E_INVALIDARG;
+
+  return DISP_E_MEMBERNOTFOUND;
+}
+
+
+/*------------------------- Fl_Scroll MSAA support -----------------------*/
+
+Fl_Msaa_Proxy*
+Fl_Scroll::msaa_proxy()
+{
+  // Create specialized proxy for Fl_Scroll widget
+  if (!msaa_scroll_proxy_)
+  {
+    msaa_scroll_proxy_ = new Fl_Msaa_Scroll_Proxy(this);
+    if (msaa_scroll_proxy_)  msaa_scroll_proxy_->AddRef();
+  }
+  return msaa_scroll_proxy_;
+}
+
+
+void
+Fl_Scroll::msaa_cleanup()
+{
+  // Destroy custom proxy
+  if (msaa_scroll_proxy_)
+  {
+    msaa_scroll_proxy_->Detach();
+    msaa_scroll_proxy_->Release();
+  }
+
+  // Destroy standard proxy
+  Fl_Group::msaa_cleanup();
+}
+
+
+bool
+Fl_Scroll::msaa_parent(IDispatch** dispatch)
+{
+  // Fl_Scroll widget's proxy has a Fl_Msaa_Scroll_Proxy parent
+  Fl_Msaa_Proxy* proxy = msaa_proxy();
+  if (!proxy)  return false;
+  proxy->QueryInterface(IID_IDispatch, (void**)dispatch);
+  return true;
+}
+
+
+bool
+Fl_Scroll::msaa_location(long id, long* X, long* Y, long* W, long* H)
+{
+  if (id != 0)  return Fl_Group::msaa_location(id, X, Y, W, H);
+  if (!msaa_visible())  return false;
+  msaa_scroll_bbox(X, Y, W, H);
+  return true;
+}
+
+
+long
+Fl_Scroll::msaa_role(long id)
+{
+  if (id != 0)  return Fl_Group::msaa_role(id);
+  return ROLE_SYSTEM_PANE;
+}
+
+
+bool
+Fl_Scroll::msaa_scroll_parent(IDispatch** dispatch)
+{
+  return Fl_Group::msaa_parent(dispatch);
+}
+
+
+Fl_Msaa_Proxy*
+Fl_Scroll::msaa_scroll_child(long id)
+{
+  switch (id)
+  {
+    case 0:		  return msaa_proxy();
+    case CID_INSIDE:	  return msaa_child(0);
+    case CID_VERTICAL:	  return scrollbar.msaa_proxy();
+    case CID_HORIZONTAL:  return hscrollbar.msaa_proxy();
+    default:		  return NULL;
+  }
+}
+
+
+void
+Fl_Scroll::msaa_scroll_bbox(long* X, long* Y, long* W, long* H)
+{
+  Fl_Window* win = window();
+  int oX, oY, oW, oH;
+  bbox(oX, oY, oW, oH);
+  *X = oX + win->x();
+  *Y = oY + win->y();
+  *W = oW;
+  *H = oH;
+}
+
+
+long
+Fl_Scroll::msaa_scroll_role(long id)
+{
+  switch (id)
+  {
+    case 0:		  return ROLE_SYSTEM_GROUPING;
+    case CID_GRIP:	  return ROLE_SYSTEM_GRIP;
+    default:		  return false;
+  }
+}
+
+
+long
+Fl_Scroll::msaa_scroll_state(long id)
+{
+  switch (id)
+  {
+    case 0:
+      return Fl_Group::msaa_state(0) & ~STATE_SYSTEM_FOCUSED;
+    case CID_GRIP:
+      return (scrollbar.visible() && hscrollbar.visible()) ?
+        0 : STATE_SYSTEM_INVISIBLE;
+    default:
+      return Fl_Group::msaa_state(id);
+  }
+}
+
+
+bool
+Fl_Scroll::msaa_scroll_location(long id, long* X, long* Y, long* W, long* H)
+{
+  if (!msaa_visible())  return false;
+  switch (id)
+  {
+    case 0:
+      return Fl_Widget::msaa_location(0, X, Y, W, H);
+    case CID_INSIDE:
+      msaa_scroll_bbox(X, Y, W, H);
+      return true;
+    case CID_VERTICAL:
+      return Fl_Group::msaa_location(find(&scrollbar) + 1, X, Y, W, H);
+    case CID_HORIZONTAL:
+      return Fl_Group::msaa_location(find(&hscrollbar) + 1, X, Y, W, H);
+    case CID_GRIP:
+      if (scrollbar.visible() && hscrollbar.visible())
+      {
+	long dummy;
+	Fl_Group::msaa_location(find(&scrollbar) + 1, X, &dummy, W, &dummy);
+	Fl_Group::msaa_location(find(&hscrollbar) + 1, &dummy, Y, &dummy, H);
+        return true;
+      }
+      return false;
+    default:
+      return false;
+  }
+}
+#endif // FL_HAVE_MSAA
+
 //
 // End of "$Id$".
 //
Index: src/Fl_Menu_.cxx
===================================================================
--- src/Fl_Menu_.cxx	(revision 6732)
+++ src/Fl_Menu_.cxx	(working copy)
@@ -188,6 +188,12 @@
   textsize(FL_NORMAL_SIZE);
   textcolor(FL_FOREGROUND_COLOR);
   down_box(FL_NO_BOX);
+
+#ifdef FL_HAVE_MSAA
+  // Initialize MSAA stuff
+  msaa_menu_items_  = 0;
+  msaa_menu_popups_ = 0;
+#endif // FL_HAVE_MSAA
 }
 
 /**
@@ -248,6 +254,9 @@
   Menus must not be cleared during a callback to the same menu.
 */
 void Fl_Menu_::clear() {
+#ifdef FL_HAVE_MSAA
+  msaa_menu_clear();
+#endif // FL_HAVE_MSAA
   if (alloc) {
     if (alloc>1) for (int i = size(); i--;)
       if (menu_[i].text) free((void*)menu_[i].text);
@@ -261,6 +270,715 @@
   }
 }
 
+
+#ifdef FL_HAVE_MSAA
+#include <FL/Fl_Msaa_Proxy.H>
+///////////////////////////////////////////////////////////////////////
+//
+//  The following code implements Microsoft Active Accessibility
+
+// Specialized MSAA proxy for Fl_Menu_ object
+class Fl_Menu_Proxy : public Fl_Msaa_Proxy
+{
+private:
+  Fl_Menu_Proxy* GetChild(int id);
+
+protected:
+  int  mPos;		// Index to menu's first child in menu bar
+  bool mIsPopup;	// Is this a proxy for menu item container
+
+public:
+  Fl_Menu_Proxy(Fl_Widget* o, int pos, bool isPopup) :
+      Fl_Msaa_Proxy(o), mPos(pos), mIsPopup(isPopup)  {}
+
+  // IAccessible implementation
+  HRESULT STDMETHODCALLTYPE get_accParent(IDispatch**);
+  HRESULT STDMETHODCALLTYPE get_accChildCount(long*);
+  HRESULT STDMETHODCALLTYPE get_accChild(VARIANT, IDispatch**);
+  HRESULT STDMETHODCALLTYPE get_accName(VARIANT, BSTR*);
+  HRESULT STDMETHODCALLTYPE get_accValue(VARIANT, BSTR*);
+  HRESULT STDMETHODCALLTYPE get_accDescription(VARIANT, BSTR*);
+  HRESULT STDMETHODCALLTYPE get_accRole(VARIANT, VARIANT*);
+  HRESULT STDMETHODCALLTYPE get_accState(VARIANT, VARIANT*);
+  HRESULT STDMETHODCALLTYPE get_accHelp(VARIANT, BSTR*);
+  HRESULT STDMETHODCALLTYPE get_accHelpTopic(BSTR*, VARIANT, long*);
+  HRESULT STDMETHODCALLTYPE get_accKeyboardShortcut(VARIANT, BSTR*);
+  HRESULT STDMETHODCALLTYPE get_accFocus(VARIANT*);
+  HRESULT STDMETHODCALLTYPE get_accSelection(VARIANT*);
+  HRESULT STDMETHODCALLTYPE get_accDefaultAction(VARIANT, BSTR*);
+  HRESULT STDMETHODCALLTYPE accSelect(long, VARIANT);
+  HRESULT STDMETHODCALLTYPE accLocation(long*, long*, long*, long*, VARIANT);
+  HRESULT STDMETHODCALLTYPE accNavigate(long, VARIANT, VARIANT*);
+  HRESULT STDMETHODCALLTYPE accHitTest(long, long, VARIANT*);
+  HRESULT STDMETHODCALLTYPE accDoDefaultAction(VARIANT);
+  HRESULT STDMETHODCALLTYPE put_accName(VARIANT, BSTR);
+  HRESULT STDMETHODCALLTYPE put_accValue(VARIANT, BSTR);
+};
+
+
+static int
+get_child_count(const Fl_Menu_Item* item)
+{
+  int count = 0;
+  while (item->label())
+  {
+    item = item->next();
+    count++;
+  }
+  return count;
+}
+
+
+Fl_Menu_Proxy*
+Fl_Menu_Proxy::GetChild(int id)
+{
+  // Proxy for submenu item contains only a popup
+  Fl_Menu_* menubar = (Fl_Menu_*)mpObj;
+  if (!mIsPopup)  return menubar->msaa_menu_proxy(mPos, true);
+
+  // Get proxy item only for sub-menu
+  const Fl_Menu_Item* child = menubar->msaa_menu_get_item(mPos, id);
+  if (!child)  return NULL;
+  if (!(child->flags & FL_SUBMENU))  return NULL;
+  return menubar->msaa_menu_proxy((child - menubar->menu()) + 1, false);
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Menu_Proxy::get_accParent(
+    /* [retval][out] */ IDispatch** ppdispParent)
+{
+  Fl_Menu_* menubar = (Fl_Menu_*)mpObj;
+  if (!menubar)  return CO_E_OBJNOTCONNECTED;
+  if (!ppdispParent)  return E_INVALIDARG;
+  if (mPos == 0)  return Fl_Msaa_Proxy::get_accParent(ppdispParent);
+  *ppdispParent = NULL;
+
+  Fl_Msaa_Proxy* proxy;
+  if (mIsPopup)
+  {
+    // Parent of pop-up menu is corresponding item
+    proxy = menubar->msaa_menu_proxy(mPos, false);
+  }
+  else
+  {
+    // Parent of menu item is pop-up menu
+    proxy = menubar->msaa_menu_proxy(menubar->msaa_menu_parent(mPos), true);
+  }
+
+  if (!proxy)  return S_FALSE;
+  proxy->QueryInterface(IID_IDispatch, (void**)ppdispParent);
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Menu_Proxy::get_accChildCount(
+    /* [retval][out] */ long* pcountChildren)
+{
+  Fl_Menu_* menubar = (Fl_Menu_*)mpObj;
+  if (!menubar)  return CO_E_OBJNOTCONNECTED;
+  if (!pcountChildren)  return E_INVALIDARG;
+  if (mIsPopup)
+    *pcountChildren = get_child_count(menubar->menu() + mPos);
+  else
+    *pcountChildren = 1;
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Menu_Proxy::get_accChild(
+    /* [in] */ VARIANT varChild,
+    /* [retval][out] */ IDispatch** ppdispChild)
+{
+  Fl_Menu_* menubar = (Fl_Menu_*)mpObj;
+  if (!menubar)  return CO_E_OBJNOTCONNECTED;
+  if (varChild.vt != VT_I4)  return E_INVALIDARG;
+  if (!ppdispChild)  return E_INVALIDARG;
+  *ppdispChild = NULL;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+
+  long max_id = (mIsPopup ? get_child_count(menubar->menu() + mPos) : 1);
+  if (id < 0 || id > max_id)  return E_INVALIDARG;
+
+  Fl_Menu_Proxy* proxy = GetChild(id);
+  if (!proxy)  return S_FALSE;
+  proxy->QueryInterface(IID_IDispatch, (void**)ppdispChild);
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Menu_Proxy::get_accName(
+    /* [optional][in] */ VARIANT varChild,
+    /* [retval][out] */ BSTR* pszName)
+{
+  Fl_Menu_* menubar = (Fl_Menu_*)mpObj;
+  if (!menubar)  return CO_E_OBJNOTCONNECTED;
+  if (!pszName)  return E_INVALIDARG;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if ((mPos == 0) && (id == 0))
+    return Fl_Msaa_Proxy::get_accName(varChild, pszName);
+  *pszName = NULL;
+
+  const Fl_Menu_Item* item = menubar->msaa_menu_get_item(mPos, id);
+  if (!item)  return E_INVALIDARG;
+  if (!item->msaa_name(pszName))  return DISP_E_MEMBERNOTFOUND;
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Menu_Proxy::get_accValue(
+    /* [optional][in] */ VARIANT varChild,
+    /* [retval][out] */ BSTR* pszValue)
+{
+  Fl_Menu_* menubar = (Fl_Menu_*)mpObj;
+  if (!menubar)  return CO_E_OBJNOTCONNECTED;
+  if (!pszValue)  return E_INVALIDARG;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if ((mPos == 0) && (id == 0))
+    return Fl_Msaa_Proxy::get_accValue(varChild, pszValue);
+  *pszValue = NULL;
+
+  const Fl_Menu_Item* item = menubar->msaa_menu_get_item(mPos, id);
+  if (!item)  return E_INVALIDARG;
+  if (!item->msaa_value(pszValue))  return DISP_E_MEMBERNOTFOUND;
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Menu_Proxy::get_accDescription(
+    /* [optional][in] */ VARIANT varChild,
+    /* [retval][out] */ BSTR* pszDescription)
+{
+  Fl_Menu_* menubar = (Fl_Menu_*)mpObj;
+  if (!menubar)  return CO_E_OBJNOTCONNECTED;
+  if (!pszDescription)  return E_INVALIDARG;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if ((mPos == 0) && (id == 0))
+    return Fl_Msaa_Proxy::get_accDescription(varChild, pszDescription);
+  *pszDescription = NULL;
+  return DISP_E_MEMBERNOTFOUND;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Menu_Proxy::get_accRole(
+    /* [optional][in] */ VARIANT varChild,
+    /* [retval][out] */ VARIANT* pvarRole)
+{
+  Fl_Menu_* menubar = (Fl_Menu_*)mpObj;
+  if (!menubar)  return CO_E_OBJNOTCONNECTED;
+  if (!pvarRole)  return E_INVALIDARG;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if ((mPos == 0) && (id == 0))
+    return Fl_Msaa_Proxy::get_accRole(varChild, pvarRole);
+
+  pvarRole->vt   = VT_I4;
+  if (mPos == 0)
+    pvarRole->lVal = ROLE_SYSTEM_MENUBAR;
+  else if (mIsPopup && (id == 0))
+    pvarRole->lVal = ROLE_SYSTEM_MENUPOPUP;
+  else 
+    pvarRole->lVal = ROLE_SYSTEM_MENUITEM;
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Menu_Proxy::get_accState(
+    /* [optional][in] */ VARIANT varChild,
+    /* [retval][out] */ VARIANT* pvarState)
+{
+  Fl_Menu_* menubar = (Fl_Menu_*)mpObj;
+  if (!menubar)  return CO_E_OBJNOTCONNECTED;
+  if (!pvarState)  return E_INVALIDARG;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  pvarState->vt = VT_I4;
+  if ((mPos == 0) || !mIsPopup || (id != 0))
+    pvarState->lVal = 0;
+  else
+    pvarState->lVal = STATE_SYSTEM_INVISIBLE;
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Menu_Proxy::get_accHelp(
+    /* [optional][in] */ VARIANT varChild,
+    /* [retval][out] */ BSTR* pszHelp)
+{
+  Fl_Menu_* menubar = (Fl_Menu_*)mpObj;
+  if (!menubar)  return CO_E_OBJNOTCONNECTED;
+  if (!pszHelp)  return E_INVALIDARG;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if ((mPos == 0) && (id == 0))
+    return Fl_Msaa_Proxy::get_accHelp(varChild, pszHelp);
+  *pszHelp = NULL;
+  return DISP_E_MEMBERNOTFOUND;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Menu_Proxy::get_accHelpTopic(
+    /* [out] */ BSTR* pszHelpFile,
+    /* [optional][in] */ VARIANT varChild,
+    /* [retval][out] */ long* pidTopic)
+{
+  Fl_Menu_* menubar = (Fl_Menu_*)mpObj;
+  if (!menubar)  return CO_E_OBJNOTCONNECTED;
+  if (!pszHelpFile)  return E_INVALIDARG;
+  if (!pidTopic)  return E_INVALIDARG;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if ((mPos == 0) && (id == 0))
+    return Fl_Msaa_Proxy::get_accHelpTopic(pszHelpFile, varChild, pidTopic);
+  *pszHelpFile = NULL;
+  *pidTopic = 0;
+  return DISP_E_MEMBERNOTFOUND;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Menu_Proxy::get_accKeyboardShortcut(
+    /* [optional][in] */ VARIANT varChild,
+    /* [retval][out] */ BSTR* pszKeyboardShortcut)
+{
+  Fl_Menu_* menubar = (Fl_Menu_*)mpObj;
+  if (!menubar)  return CO_E_OBJNOTCONNECTED;
+  if (!pszKeyboardShortcut)  return E_INVALIDARG;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if ((mPos == 0) && (id == 0))
+    return Fl_Msaa_Proxy::get_accKeyboardShortcut(
+    	varChild, pszKeyboardShortcut);
+  *pszKeyboardShortcut = NULL;
+
+  // Popup proxy has no shortcut
+  if (mIsPopup && (id == 0))  return S_FALSE;
+  if (!menubar->msaa_menu_shortcut(mPos, id, pszKeyboardShortcut))
+    return DISP_E_MEMBERNOTFOUND;
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Menu_Proxy::get_accFocus(
+    /* [retval][out] */ VARIANT *pvarChild)
+{
+  Fl_Menu_* menubar = (Fl_Menu_*)mpObj;
+  if (!menubar)  return CO_E_OBJNOTCONNECTED;
+  if (!pvarChild)  return E_INVALIDARG;
+  pvarChild->vt = VT_EMPTY;
+  return S_FALSE;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Menu_Proxy::get_accSelection(
+    /* [retval][out] */ VARIANT* pvarChildren)
+{
+  Fl_Menu_* menubar = (Fl_Menu_*)mpObj;
+  if (!menubar)  return CO_E_OBJNOTCONNECTED;
+  if (!pvarChildren)  return E_INVALIDARG;
+  pvarChildren->vt = VT_EMPTY;
+  return S_FALSE;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Menu_Proxy::get_accDefaultAction(
+    /* [optional][in] */ VARIANT varChild,
+    /* [retval][out] */ BSTR* pszDefaultAction)
+{
+  Fl_Menu_* menubar = (Fl_Menu_*)mpObj;
+  if (!menubar)  return CO_E_OBJNOTCONNECTED;
+  if (!pszDefaultAction)  return E_INVALIDARG;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  *pszDefaultAction = NULL;
+
+  if (!mIsPopup || (mIsPopup && (id == 0)))  return S_FALSE;
+  const Fl_Menu_Item* item = menubar->msaa_menu_get_item(mPos, id);
+  if (!item)  return E_INVALIDARG;
+  fl_str_to_bstr("Execute", -1, pszDefaultAction);
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Menu_Proxy::accSelect(
+    /* [in] */ long flagsSelect,
+    /* [optional][in] */ VARIANT varChild)
+{
+  Fl_Menu_* menubar = (Fl_Menu_*)mpObj;
+  if (!menubar)  return CO_E_OBJNOTCONNECTED;
+  return S_FALSE;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Menu_Proxy::accLocation(
+    /* [out] */ long* pxLeft,
+    /* [out] */ long* pyTop,
+    /* [out] */ long* pcxWidth,
+    /* [out] */ long* pcyHeight,
+    /* [optional][in] */ VARIANT varChild)
+{
+  Fl_Menu_* menubar = (Fl_Menu_*)mpObj;
+  if (!menubar)  return CO_E_OBJNOTCONNECTED;
+  if (!pxLeft || !pyTop || !pcxWidth || !pcyHeight)  return E_INVALIDARG;
+  *pxLeft    = 0;
+  *pyTop     = 0;
+  *pcxWidth  = 0;
+  *pcyHeight = 0;
+
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > get_child_count(menubar->menu() + mPos))
+    return E_INVALIDARG;
+  if (!menubar->msaa_menu_location(mPos, id,
+      pxLeft, pyTop, pcxWidth, pcyHeight))
+    return DISP_E_MEMBERNOTFOUND;
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Menu_Proxy::accNavigate(
+    /* [in] */ long navDir,
+    /* [optional][in] */ VARIANT varStart,
+    /* [retval][out] */ VARIANT* pvarEndUpAt)
+{
+  Fl_Menu_* menubar = (Fl_Menu_*)mpObj;
+  if (!menubar)  return CO_E_OBJNOTCONNECTED;
+  if (!pvarEndUpAt)  return E_INVALIDARG;
+  pvarEndUpAt->vt = VT_EMPTY;
+  long id = (varStart.vt == VT_I4 ? varStart.lVal : 0);
+  if ((id != 0) &&
+      ((navDir == NAVDIR_FIRSTCHILD) || (navDir == NAVDIR_LASTCHILD)))
+    return S_FALSE;
+
+  // Handle navigation relative to menu item itself
+  if ((id == 0) &&
+      (navDir != NAVDIR_FIRSTCHILD) && (navDir != NAVDIR_LASTCHILD))
+  {
+    // Handle navigation relative to top menubar
+    if (mPos == 0)
+      return Fl_Msaa_Proxy::accNavigate(navDir, varStart, pvarEndUpAt);
+
+    // Forward request to parent menu
+    VARIANT self;
+    self.vt = VT_I4;
+    Fl_Msaa_Proxy* proxy;
+    if (mIsPopup)
+    {
+      proxy = menubar->msaa_menu_proxy(mPos, false);
+      self.lVal = 1;
+    }
+    else
+    {
+      proxy = menubar->msaa_menu_proxy(menubar->msaa_menu_parent(mPos), true);
+      self.lVal = menubar->msaa_menu_child_id(mPos);
+    }
+    if (!proxy)  return S_FALSE;
+    return proxy->accNavigate(navDir, self, pvarEndUpAt);
+  }
+
+  // Handle navigation between menu items
+  long max_id = (mIsPopup ? get_child_count(menubar->menu() + mPos) : 1);
+  if (max_id < 1)  return S_FALSE;
+  long dst;
+  switch (navDir)
+  {
+    case NAVDIR_FIRSTCHILD:
+      dst = 1;
+      break;
+    case NAVDIR_LASTCHILD:
+      dst = max_id;
+      break;
+    case NAVDIR_PREVIOUS:
+      if (id == 1)  return S_FALSE;
+      dst = id - 1;
+      break;
+    case NAVDIR_NEXT:
+      if (id == max_id)  return S_FALSE;
+      dst = id + 1;
+      break;
+
+    // Spatial navigation not implemented
+    case NAVDIR_UP:	// Fall through
+    case NAVDIR_DOWN:	// Fall through
+    case NAVDIR_LEFT:	// Fall through
+    case NAVDIR_RIGHT:	// Fall through
+    default:
+      return S_FALSE;
+  }
+
+  // Lookup child proxy
+  Fl_Msaa_Proxy* proxy = GetChild(dst);
+  if (!proxy)
+  {
+    pvarEndUpAt->vt   = VT_I4;
+    pvarEndUpAt->lVal = dst;
+  }
+  else
+  {
+    pvarEndUpAt->vt = VT_DISPATCH;
+    proxy->QueryInterface(IID_IDispatch, (void**)&pvarEndUpAt->pdispVal);
+  }
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Menu_Proxy::accHitTest(
+    /* [in] */ long xLeft,
+    /* [in] */ long yTop,
+    /* [retval][out] */ VARIANT* pvarChild)
+{
+  Fl_Menu_* menubar = (Fl_Menu_*)mpObj;
+  if (!menubar)  return CO_E_OBJNOTCONNECTED;
+  if (!pvarChild)  return E_INVALIDARG;
+  pvarChild->vt = VT_EMPTY;
+
+  // Make sure the point is inside this widget
+  long X, Y, W, H;
+  if (!menubar->msaa_menu_location(mPos, 0, &X, &Y, &W, &H))  return S_FALSE;
+  if ((xLeft < X) || (xLeft >= (X + W)))  return S_FALSE;
+  if ((yTop < Y) || (yTop >= (Y + H)))  return S_FALSE;
+  pvarChild->vt   = VT_I4;
+  pvarChild->lVal = 0;
+
+  // Check child elements from top to bottom
+  for (int n = get_child_count(menubar->menu() + mPos);  n > 0;  --n)
+  {
+    // Obtain location through proxy first, but fall back to function call
+    Fl_Msaa_Proxy* proxy = GetChild(n);
+    if (proxy && (proxy->accLocation(&X, &Y, &W, &H, *pvarChild) != S_OK))
+      continue;
+    else if (!proxy && !menubar->msaa_menu_location(mPos, n, &X, &Y, &W, &H))
+      continue;
+
+    // Make sure point is inside child object
+    if ((xLeft < X) || (xLeft >= (X + W)))  continue;
+    if ((yTop < Y) || (yTop >= (Y + H)))  continue;
+
+    // Setup result
+    if (proxy)
+    {
+      pvarChild->vt = VT_DISPATCH;
+      proxy->QueryInterface(IID_IDispatch, (void**)&pvarChild->pdispVal);
+    }
+    else
+    {
+      pvarChild->vt   = VT_I4;
+      pvarChild->lVal = n;
+    }
+    return S_OK;
+  }
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Menu_Proxy::accDoDefaultAction(
+    /* [optional][in] */ VARIANT varChild)
+{
+  Fl_Menu_* menubar = (Fl_Menu_*)mpObj;
+  if (!menubar)  return CO_E_OBJNOTCONNECTED;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if ((mPos == 0) && (id == 0))
+    return Fl_Msaa_Proxy::accDoDefaultAction(varChild);
+
+  if (!mIsPopup || (mIsPopup && (id == 0)))  return DISP_E_MEMBERNOTFOUND;
+  const Fl_Menu_Item* item = menubar->msaa_menu_get_item(mPos, id);
+  if (!item)  return E_INVALIDARG;
+  menubar->picked(item);
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Menu_Proxy::put_accName(
+    /* [optional][in] */ VARIANT varChild,
+    /* [in] */ BSTR szName)
+{
+  Fl_Menu_* menubar = (Fl_Menu_*)mpObj;
+  if (!menubar)  return CO_E_OBJNOTCONNECTED;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if ((mPos == 0) && (id == 0))
+    return Fl_Msaa_Proxy::put_accName(varChild, szName);
+  return DISP_E_MEMBERNOTFOUND;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Menu_Proxy::put_accValue(
+    /* [optional][in] */ VARIANT varChild,
+    /* [in] */ BSTR szValue)
+{
+  Fl_Menu_* menubar = (Fl_Menu_*)mpObj;
+  if (!menubar)  return CO_E_OBJNOTCONNECTED;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if ((mPos == 0) && (id == 0))
+    return Fl_Msaa_Proxy::put_accValue(varChild, szValue);
+
+  Fl_Menu_Item* item = (Fl_Menu_Item*)menubar->msaa_menu_get_item(mPos, id);
+  if (!item)  return E_INVALIDARG;
+  char* value = fl_bstr_to_str(szValue);
+  bool ok = item->msaa_put_value(value);
+  fl_free_str(value);
+  return (ok ? S_OK : DISP_E_MEMBERNOTFOUND);
+}
+
+
+/*-------------------------- Fl_Menu_ MSAA support ------------------------*/
+
+void
+Fl_Menu_::msaa_menu_clear()
+{
+  // Destroy cache of MSAA proxy
+  for (int i = size();  msaa_menu_items_ && i--;  )
+  {
+    Fl_Menu_Proxy* proxy;
+    if ((proxy = msaa_menu_items_[i]))
+    {
+      proxy->Detach();
+      proxy->Release();
+    }
+    if ((proxy = msaa_menu_popups_[i]))
+    {
+      proxy->Detach();
+      proxy->Release();
+    }
+  }
+  if (msaa_menu_items_)
+  {
+    free(msaa_menu_items_);
+    free(msaa_menu_popups_);
+    msaa_menu_items_  = 0;
+    msaa_menu_popups_ = 0;
+  }
+}
+
+
+Fl_Menu_Proxy*
+Fl_Menu_::msaa_menu_proxy(int pos, bool isPopup)
+{
+  // Do not cache root item
+  if (pos == 0)
+  {
+    if (!msaa_proxy_)
+    {
+      msaa_proxy_ = new Fl_Menu_Proxy(this, 0, true);
+      if (msaa_proxy_)  msaa_proxy_->AddRef();
+    }
+    return (Fl_Menu_Proxy*)msaa_proxy_;
+  }
+
+  // Create new storage for proxies if necessary
+  if (!msaa_menu_items_)
+  {
+    msaa_menu_items_  = (Fl_Menu_Proxy**)calloc(size(), sizeof(Fl_Menu_Proxy*));
+    msaa_menu_popups_ = (Fl_Menu_Proxy**)calloc(size(), sizeof(Fl_Menu_Proxy*));
+  }
+
+  // Determine required proxy type
+  Fl_Menu_Proxy** cache = isPopup ? msaa_menu_popups_ : msaa_menu_items_;
+  if (cache[pos])  return cache[pos];
+
+  // Create missing proxy
+  Fl_Menu_Proxy* proxy = new Fl_Menu_Proxy(this, pos, isPopup);
+  if (proxy)
+  {
+    proxy->AddRef();
+    cache[pos] = proxy;
+  }
+  return proxy;
+}
+
+
+Fl_Msaa_Proxy*
+Fl_Menu_::msaa_child(long id)
+{
+  if (id != 0)  return false;
+
+  // Create specialized proxy for top item
+  return msaa_menu_proxy(0, true);
+}
+
+
+const Fl_Menu_Item*
+Fl_Menu_::msaa_menu_get_item(int pos, long id)
+{
+  if ((pos == 0) && (id == 0))  return NULL;
+  if (id == 0)  return menu() + pos - 1;
+  const Fl_Menu_Item* item = menu() + pos;
+  for (;  id > 1;  --id, item = item->next());
+  return item;
+}
+
+
+int
+Fl_Menu_::msaa_menu_parent(int pos)
+{
+  --pos;
+  const Fl_Menu_Item* item = menu() + pos;
+  for (int nest = 1;  pos >= 0;  --pos, --item)
+  {
+    if (!item->label())  ++nest;
+    else if (item->flags & FL_SUBMENU)
+    {
+      --nest;
+      if (nest < 0)  break;
+    }
+  }
+  return pos + 1;
+}
+
+
+int
+Fl_Menu_::msaa_menu_child_id(int pos)
+{
+  --pos;
+  const Fl_Menu_Item* item = menu() + pos;
+  int id = 0;
+  for (int nest = 1;  pos >= 0;  --pos, --item)
+  {
+    if (!item->label())  ++nest;  
+    else
+    {
+      if (item->flags & FL_SUBMENU)
+      {
+	--nest;
+	if (nest < 0)  break;
+      }
+      if (nest == 0)  ++id;
+    }
+  }
+  return id;
+}
+
+
+bool
+Fl_Menu_::msaa_menu_location(int pos, long id,
+  long* X, long* Y, long* W, long* H)
+{
+  if ((pos != 0) || (id != 0))  return false;
+  return Fl_Widget::msaa_location(0, X, Y, W, H);
+}
+
+
+bool
+Fl_Menu_::msaa_menu_shortcut(int pos, long id, BSTR* bstr)
+{
+  const Fl_Menu_Item* item = msaa_menu_get_item(pos, id);
+  if (!item)  return false;
+  return item->msaa_shortcut(false, bstr);
+}
+#endif // FL_HAVE_MSAA
+
+
 //
 // End of "$Id$".
 //
Index: src/Fl_Menu_Bar.cxx
===================================================================
--- src/Fl_Menu_Bar.cxx	(revision 6732)
+++ src/Fl_Menu_Bar.cxx	(working copy)
@@ -73,6 +73,63 @@
   return 0;
 }
 
+
+#ifdef FL_HAVE_MSAA
+#include <FL/Fl_Msaa_Proxy.H>
+///////////////////////////////////////////////////////////////////////
+//
+//  The following code implements Microsoft Active Accessibility
+
+long
+Fl_Menu_Bar::msaa_role(long id)
+{
+  if (id != 0)  return Fl_Widget::msaa_role(id);
+  return ROLE_SYSTEM_MENUBAR;
+}
+
+
+bool
+Fl_Menu_Bar::msaa_menu_location(int pos, long id,
+  long* X, long* Y, long* W, long* H)
+{
+  bool on_menubar = false;
+  if (pos != 0)  on_menubar = ((id == 0) && (msaa_menu_parent(pos) == 0));
+
+  // Return the size of the widget for top level menubar
+  if (!Fl_Widget::msaa_location(0, X, Y, W, H))  return false;
+  if ((pos == 0) && (id == 0))  return true;
+
+  // Return location only for top-most items
+  if (on_menubar)  id = msaa_menu_child_id(pos);
+  else if (pos != 0)  return false;
+
+  // NOTE: This must match algorithm in draw()
+  const Fl_Menu_Item* m = menu()->first();
+  for (*X += 3;  --id;  m = m->next())
+  {
+    if (!m->text)  return false;
+    *X += m->measure(0, this) + 16;
+  }
+  *W = m->measure(0, this) + 12;
+  *Y += 3;
+  *H -= 6;
+  return true;
+}
+
+
+bool
+Fl_Menu_Bar::msaa_menu_shortcut(int pos, long id, BSTR* bstr)
+{
+  const Fl_Menu_Item* item = msaa_menu_get_item(pos, id);
+  if (!item)  return false;
+
+  bool on_menubar;
+  if (pos == 0)  on_menubar = true;
+  else  on_menubar = ((id == 0) && (msaa_menu_parent(pos) == 0));
+  return item->msaa_shortcut(on_menubar, bstr);
+}
+#endif // FL_HAVE_MSAA
+
 //
 // End of "$Id$".
 //
Index: src/Fl_Window.cxx
===================================================================
--- src/Fl_Window.cxx	(revision 6732)
+++ src/Fl_Window.cxx	(working copy)
@@ -57,6 +57,11 @@
   size_range_set = 0;
   minw = maxw = minh = maxh = 0;
   callback((Fl_Callback*)default_callback);
+#ifdef FL_HAVE_MSAA
+  // Initialize MSAA stuff
+  msaa_caret_w_ = 0;
+  msaa_caret_h_ = 0;
+#endif
 }
 
 Fl_Window::Fl_Window(int X,int Y,int W, int H, const char *l)
@@ -178,6 +183,53 @@
 }
 
 
+#ifdef FL_HAVE_MSAA
+#include <FL/Fl_Msaa_Proxy.H>
+#include <FL/x.H>
+///////////////////////////////////////////////////////////////////////
+//
+//  The following code implements Microsoft Active Accessibility
+
+long
+Fl_Window::msaa_role(long id)
+{
+  if (id != 0)  return Fl_Group::msaa_role(id);
+  return ROLE_SYSTEM_CLIENT;
+}
+
+
+long
+Fl_Window::msaa_state(long id)
+{
+  if (id != 0)  return Fl_Group::msaa_state(id);
+  return (STATE_SYSTEM_FOCUSABLE | (shown() ? 0 : STATE_SYSTEM_INVISIBLE));
+}
+
+
+void
+Fl_Window::msaa_set_caret(int X, int Y, int W, int H) const
+{
+  if (W < 0)  W = 0;
+  if (H < 0)  H = 0;
+
+  // Destroy old caret if it has a different size
+  if (W != msaa_caret_w_ || H != msaa_caret_h_)
+  {
+    Fl_Window* wnd = (Fl_Window*)this;
+    DestroyCaret();
+    wnd->msaa_caret_w_ = W;
+    wnd->msaa_caret_h_ = H;
+
+    // Create new caret if it has a definite size
+    if (W && H)  CreateCaret(fl_xid(this), NULL, W, H);
+  }
+
+  // Move caret only if it has a definite size
+  if (W && H)  SetCaretPos(X, Y);
+}
+#endif // FL_HAVE_MSAA
+
+
 //
 // End of "$Id$".
 //
Index: src/Fl_Value_Slider.cxx
===================================================================
--- src/Fl_Value_Slider.cxx	(revision 6732)
+++ src/Fl_Value_Slider.cxx	(working copy)
@@ -81,6 +81,34 @@
 			   shh-Fl::box_dh(box()));
 }
 
+
+#ifdef FL_HAVE_MSAA
+#include <FL/Fl_Msaa_Proxy.H>
+///////////////////////////////////////////////////////////////////////
+//
+//  The following code implements Microsoft Active Accessibility
+
+bool
+Fl_Value_Slider::msaa_location(long id, long* X, long* Y, long* W, long* H)
+{
+  if (id == 0)  return Fl_Widget::msaa_location(0, X, Y, W, H);
+  if (!msaa_visible())  return false;
+
+  // Note: size calculations must match those in draw()
+  long sxx, syy, sww, shh;
+  Fl_Widget::msaa_location(0, &sxx, &syy, &sww, &shh);
+  if (horizontal()) {
+    sxx += 35; sww -= 35;
+  } else {
+    syy += 25; shh -= 25;
+  }
+  return msaa_slider_location(id,
+    sxx + Fl::box_dx(box()), syy + Fl::box_dy(box()),
+    sww - Fl::box_dw(box()), shh - Fl::box_dh(box()), X, Y, W, H);
+}
+#endif // FL_HAVE_MSAA
+
+
 //
 // End of "$Id$".
 //
Index: src/Fl_Value_Input.cxx
===================================================================
--- src/Fl_Value_Input.cxx	(revision 6732)
+++ src/Fl_Value_Input.cxx	(working copy)
@@ -142,6 +142,29 @@
     input.parent(0);   // *revert* ctor kludge!
 }
 
+
+#ifdef FL_HAVE_MSAA
+#include <FL/Fl_Msaa_Proxy.H>
+///////////////////////////////////////////////////////////////////////
+//
+//  The following code implements Microsoft Active Accessibility
+
+long
+Fl_Value_Input::msaa_role(long id)
+{
+  if (id != 0)  return Fl_Widget::msaa_role(id);
+  return ROLE_SYSTEM_TEXT;
+}
+
+
+long
+Fl_Value_Input::msaa_state(long id)
+{
+  return (STATE_SYSTEM_FOCUSABLE | Fl_Widget::msaa_state(id));
+}
+#endif // FL_HAVE_MSAA
+
+
 //
 // End of "$Id$".
 //
Index: src/Fl_Button.cxx
===================================================================
--- src/Fl_Button.cxx	(revision 6732)
+++ src/Fl_Button.cxx	(working copy)
@@ -48,6 +48,9 @@
     value_ = v;
     if (box()) redraw();
     else redraw_label();
+#ifdef FL_HAVE_MSAA
+    msaa_notify(EVENT_OBJECT_STATECHANGE);
+#endif // FL_HAVE_MSAA
     return 1;
   } else {
     return 0;
@@ -185,6 +188,118 @@
   set_flag(SHORTCUT_LABEL);
 }
 
+
+#ifdef FL_HAVE_MSAA
+#include <FL/Fl_Msaa_Proxy.H>
+///////////////////////////////////////////////////////////////////////
+//
+//  The following code implements Microsoft Active Accessibility
+
+bool
+Fl_Button::msaa_value(long id, BSTR* bstr)
+{
+  if (id != 0)  return Fl_Widget::msaa_value(id, bstr);
+  fl_str_to_bstr((value_ ? "1" : "0"), -1, bstr);
+  return true;
+}
+
+
+long
+Fl_Button::msaa_state(long id)
+{
+  if (id != 0)  return Fl_Widget::msaa_state(id);
+  long s = Fl_Widget::msaa_state(0);
+  switch(type())
+  {
+    case FL_TOGGLE_BUTTON:  // Fall through
+    case FL_RADIO_BUTTON:
+      if (value())  s |= STATE_SYSTEM_CHECKED;
+      return s;
+    default:
+      return s;
+  }
+}
+
+
+long
+Fl_Button::msaa_role(long id)
+{
+  if (id != 0)  return Fl_Widget::msaa_role(id);
+  switch (type())
+  {
+    case FL_TOGGLE_BUTTON:  return ROLE_SYSTEM_CHECKBUTTON;
+    case FL_RADIO_BUTTON:   return ROLE_SYSTEM_RADIOBUTTON;
+    default:                return ROLE_SYSTEM_PUSHBUTTON;
+  }
+}
+
+
+bool
+Fl_Button::msaa_action(long id, BSTR* bstr)
+{
+  if (id != 0)  return false;
+
+  // Return localized MSAA action
+  const char* action;
+  switch (type())
+  {
+    case FL_TOGGLE_BUTTON:  // Fall through
+    case FL_RADIO_BUTTON:   action = (value_ ? "Uncheck" : "Check");  break;
+    default:		    action = "Press";  break;
+  }
+  fl_str_to_bstr(action, -1, bstr);
+  return true;
+}
+
+
+bool 
+Fl_Button::msaa_do_action(long id)
+{
+  // NOTE: behaviour must match click handling in handle()
+  if (id != 0)  return false;
+
+  // Check for no change in button state
+  int newval = (type() == FL_RADIO_BUTTON ? 1 : !oldval);
+  if (newval == oldval) {
+    if (when() & FL_WHEN_NOT_CHANGED) do_callback();
+    return true;
+  }
+
+  // Simulate FL_PUSH, FL_DRAG and FL_RELEASE on button
+  set_changed();
+  if (type() == FL_RADIO_BUTTON) {
+    setonly();
+    redraw();
+    if (when() & FL_WHEN_CHANGED) do_callback();
+  }
+  else if (type() == FL_TOGGLE_BUTTON) {
+    oldval = value_ = newval;
+    redraw();
+    if (when() & FL_WHEN_CHANGED) do_callback();
+  }
+  else {
+    value_ = newval;
+    if (when() & FL_WHEN_CHANGED) do_callback();
+    value_ = oldval;
+    if (when() & FL_WHEN_CHANGED) do_callback();
+  }
+  if (when() & FL_WHEN_RELEASE) do_callback();
+  return true;
+}
+
+
+bool 
+Fl_Button::msaa_put_value(long id, const char* str)
+{
+  if (id != 0)  return false;
+  int v = atoi(str);
+  if (type() == FL_RADIO_BUTTON && v)  setonly();
+  else  value(v);
+  return true;
+}
+#endif // FL_HAVE_MSAA
+
+
 //
 // End of "$Id$".
 //
Index: src/Fl_Menu_add.cxx
===================================================================
--- src/Fl_Menu_add.cxx	(revision 6732)
+++ src/Fl_Menu_add.cxx	(working copy)
@@ -265,6 +265,9 @@
   int value_offset = value_-menu_;
   menu_ = local_array; // in case it reallocated it
   if (value_) value_ = menu_+value_offset;
+#ifdef FL_HAVE_MSAA
+  msaa_menu_clear();
+#endif // FL_HAVE_MSAA
   return r;
 }
 
@@ -332,6 +335,9 @@
   }
   // MRS: "n" is the menu size(), which includes the trailing NULL entry...
   memmove(item, next_item, (menu_+n-next_item)*sizeof(Fl_Menu_Item));
+#ifdef FL_HAVE_MSAA
+  msaa_menu_clear();
+#endif // FL_HAVE_MSAA
 }
 
 //
Index: src/Fl_Scrollbar.cxx
===================================================================
--- src/Fl_Scrollbar.cxx	(revision 6732)
+++ src/Fl_Scrollbar.cxx	(working copy)
@@ -276,6 +276,183 @@
 }
 
 
+#ifdef FL_HAVE_MSAA
+#include <FL/Fl_Msaa_Proxy.H>
+///////////////////////////////////////////////////////////////////////
+//
+//  The following code implements Microsoft Active Accessibility
+
+#define CID_LINE_UP	1
+#define CID_PAGE_UP	2
+#define CID_POS		3
+#define CID_PAGE_DN	4
+#define CID_LINE_DN	5
+
+
+void
+Fl_Scrollbar::msaa_valuator_on_change()
+{
+  Fl_Valuator::msaa_valuator_on_change();
+  msaa_notify(EVENT_OBJECT_STATECHANGE, CID_PAGE_UP);
+  msaa_notify(EVENT_OBJECT_STATECHANGE, CID_PAGE_DN);
+}
+
+
+long
+Fl_Scrollbar::msaa_role(long id)
+{
+  switch (id)
+  {
+    case CID_POS:	return ROLE_SYSTEM_INDICATOR;
+    case CID_LINE_UP:	// Fall through
+    case CID_PAGE_UP:	// Fall through
+    case CID_PAGE_DN:	// Fall through
+    case CID_LINE_DN:	return ROLE_SYSTEM_PUSHBUTTON;
+    default:  		return ROLE_SYSTEM_SCROLLBAR;
+  }
+}
+
+
+bool
+Fl_Scrollbar::msaa_name(long id, BSTR* bstr)
+{
+  const char* l;
+  switch (id)
+  {
+    case 0:		return Fl_Slider::msaa_name(0, bstr);
+    case CID_LINE_UP:	l = horizontal() ? "Column left" : "Line up";  break;
+    case CID_LINE_DN:	l = horizontal() ? "Column right" : "Line down";  break;
+    case CID_PAGE_UP:	// Fall through
+    case CID_POS:	// Fall through
+    case CID_PAGE_DN:	return Fl_Slider::msaa_name(id - 1, bstr);
+    default:		return false;
+  }
+  fl_str_to_bstr(l, -1, bstr);
+  return true;
+}
+
+
+long
+Fl_Scrollbar::msaa_state(long id)
+{
+  // Note: size calculations must match those in draw()
+  int X = x() + Fl::box_dx(box());
+  int Y = y() + Fl::box_dy(box());
+  int W = w() - Fl::box_dw(box());
+  int H = h() - Fl::box_dh(box());
+  bool slider_only =
+    ((horizontal() && (W < 3*H)) || (!horizontal() && (H < 3*W)));
+
+  long s = Fl_Widget::msaa_state(0) & ~STATE_SYSTEM_FOCUSABLE;
+  switch (id)
+  {
+    case CID_LINE_UP:	return s | (slider_only ? STATE_SYSTEM_INVISIBLE : 0);
+    case CID_LINE_DN:	return s | (slider_only ? STATE_SYSTEM_INVISIBLE : 0);
+    case CID_PAGE_UP:	// Fall through
+    case CID_POS:	// Fall through
+    case CID_PAGE_DN:	return Fl_Slider::msaa_state(id - 1);
+    default:		return s;
+  }
+}
+
+
+bool
+Fl_Scrollbar::msaa_action(long id, BSTR* bstr)
+{
+  const char* l;
+  switch (id)
+  {
+    case CID_LINE_UP:	// Fall through
+    case CID_PAGE_UP:	// Fall through
+    case CID_PAGE_DN:	// Fall through
+    case CID_LINE_DN:	l = "Press";  break;
+    default:		return false;
+  }
+  fl_str_to_bstr(l, -1, bstr);
+  return true;
+}
+
+
+bool
+Fl_Scrollbar::msaa_location(long id, long* X, long* Y, long* W, long* H)
+{
+  if (id == 0)  return Fl_Widget::msaa_location(0, X, Y, W, H);
+  if (!msaa_visible())  return false;
+
+  // Note: size calculations must match those in draw()
+  long ox, oy, ow, oh;
+  long sxx, syy, sww, shh;
+  Fl_Widget::msaa_location(0, &ox, &oy, &ow, &oh);
+  sxx = (ox += Fl::box_dx(box()));
+  syy = (oy += Fl::box_dy(box()));
+  sww = (ow -= Fl::box_dw(box()));
+  shh = (oh -= Fl::box_dh(box()));
+  bool slider_only =
+    ((horizontal() && (sww < 3*shh)) || (!horizontal() && (shh < 3*sww)));
+  if (!slider_only)
+  {
+    if (horizontal())
+    {
+      sxx += shh; 
+      sww -= 2 * shh;
+    }
+    else
+    {
+      syy += sww;
+      shh -= 2 * sww;
+    }
+  }
+
+  switch (id)
+  {
+    case CID_LINE_UP:
+      if (slider_only)  return false;
+      *X = ox;
+      *Y = oy;
+      *W = *H = (horizontal() ? oh : ow);
+      return true;
+    case CID_LINE_DN:
+      if (slider_only)  return false;
+      *X = ox + (horizontal() ? (ow - oh) : 0);
+      *Y = oy + (horizontal() ? 0 : (oh - ow));
+      *W = *H = (horizontal() ? oh : ow);
+      return true;
+    case CID_PAGE_UP:	// Fall through
+    case CID_POS:	// Fall through
+    case CID_PAGE_DN:
+      return Fl_Slider::msaa_slider_location(id - 1,
+	sxx, syy, sww, shh, X, Y, W, H);
+    default:
+      return false;
+  }
+}
+
+
+bool
+Fl_Scrollbar::msaa_do_action(long id)
+{
+  if (slider_size() >= 1.0)  return false;
+
+  // Calculate new position
+  int ls = maximum()>=minimum() ? linesize_ : -linesize_;
+  int ps = int((maximum()-minimum())*slider_size()/(1.0-slider_size()));
+  int i;
+  switch (id)
+  {
+    case CID_LINE_UP:	i = -ls;  break;
+    case CID_PAGE_UP:	i = -ps + ls;  if (i > -ls)  i = -ls;  break;
+    case CID_PAGE_DN:	i = ps - ls;  if (i < ls)  i = ls;  break;
+    case CID_LINE_DN:	i = ls;  break;
+    default:		return false;
+  }
+  handle_push();
+  handle_drag(clamp(value() + i));
+  handle_release();
+  return true;
+}
+#endif // FL_HAVE_MSAA
+
+
 //
 // End of "$Id$".
 //
Index: src/Fl_Menu_Button.cxx
===================================================================
--- src/Fl_Menu_Button.cxx	(revision 6732)
+++ src/Fl_Menu_Button.cxx	(working copy)
@@ -117,6 +117,31 @@
   down_box(FL_NO_BOX);
 }
 
+
+#ifdef FL_HAVE_MSAA
+#include <FL/Fl_Msaa_Proxy.H>
+///////////////////////////////////////////////////////////////////////
+//
+//  The following code implements Microsoft Active Accessibility
+
+long
+Fl_Menu_Button::msaa_role(long id)
+{
+  if (id != 0)  return Fl_Widget::msaa_role(id);
+  return ROLE_SYSTEM_BUTTONDROPDOWN;
+}
+
+
+bool
+Fl_Menu_Button::msaa_menu_location(int pos, long id,
+  long* X, long* Y, long* W, long* H)
+{
+  // Popup menu and child items do not have on-screen location
+  if ((type() != 0) || (pos != 0) || (id != 0))  return false;
+  return Fl_Widget::msaa_location(0, X, Y, W, H);
+}
+#endif // FL_HAVE_MSAA
+
 //
 // End of "$Id$".
 //
Index: src/Fl_Menu.cxx
===================================================================
--- src/Fl_Menu.cxx	(revision 6732)
+++ src/Fl_Menu.cxx	(working copy)
@@ -90,6 +90,16 @@
 public:
   const Fl_Menu_Item* menu;
   menutitle(int X, int Y, int W, int H, const Fl_Menu_Item*);
+
+#ifdef FL_HAVE_MSAA
+protected:
+  // MSAA support
+  bool msaa_name(long id, BSTR* bstr);
+  bool msaa_shortcut(long id, BSTR* bstr);
+
+public:
+  bool on_menubar_;
+#endif // FL_HAVE_MSAA
 };
 
 // each vertical menu has one of these:
@@ -117,6 +127,20 @@
   void autoscroll(int);
   void position(int x, int y);
   int is_inside(int x, int y);
+
+#ifdef FL_HAVE_MSAA
+  // MSAA support
+protected:
+  long msaa_child_count();
+  bool msaa_name(long id, BSTR* bstr);
+  bool msaa_value(long id, BSTR* bstr);
+  bool msaa_shortcut(long id, BSTR* bstr);
+  bool msaa_put_value(long id, const char* str);
+  bool msaa_location(long id, long* X, long* Y, long* W, long* H);
+
+public:
+  static void msaa_menuwindow_on_select();
+#endif // FL_HAVE_MSAA
 };
 
 #define LEADING 4 // extra vertical leading
@@ -372,6 +396,9 @@
       int ht = Htitle+2*BW+3;
       title = new menutitle(X, Y-ht-dy, Wtitle, ht, t);
     }
+#ifdef FL_HAVE_MSAA
+    title->on_menubar_ = menubar_title;
+#endif // FL_HAVE_MSAA
   } else
     title = 0;
 }
@@ -555,16 +582,28 @@
 }
 
 static inline void setitem(const Fl_Menu_Item* i, int m, int n) {
+#ifdef FL_HAVE_MSAA
+  bool changed = (p->menu_number != m) || (p->item_number != n);
+#endif // FL_HAVE_MSAA
   p->current_item = i;
   p->menu_number = m;
   p->item_number = n;
+#ifdef FL_HAVE_MSAA
+  if (changed)  menuwindow::msaa_menuwindow_on_select();
+#endif // FL_HAVE_MSAA
 }
 
 static void setitem(int m, int n) {
+#ifdef FL_HAVE_MSAA
+  bool changed = (p->menu_number != m) || (p->item_number != n);
+#endif // FL_HAVE_MSAA
   menustate &pp = *p;
   pp.current_item = (n >= 0) ? pp.p[m]->menu->next(n) : 0;
   pp.menu_number = m;
   pp.item_number = n;
+#ifdef FL_HAVE_MSAA
+  if (changed)  menuwindow::msaa_menuwindow_on_select();
+#endif // FL_HAVE_MSAA
 }
 
 static int forward(int menu) { // go to next item in menu menu if possible
@@ -794,6 +833,10 @@
 
   // preselected item, pop up submenus if necessary:
   if (initial_item && mw.selected >= 0) {
+#ifdef FL_HAVE_MSAA
+    if (mw.title) mw.title->show();
+    mw.show();
+#endif // FL_HAVE_MSAA
     setitem(0, mw.selected);
     goto STARTUP;
   }
@@ -989,6 +1032,198 @@
   return ret;
 }
 
+
+#ifdef FL_HAVE_MSAA
+#include <FL/Fl_Msaa_Proxy.H>
+///////////////////////////////////////////////////////////////////////
+//
+//  The following code implements Microsoft Active Accessibility
+
+bool
+Fl_Menu_Item::msaa_name(BSTR* bstr) const
+{
+  // Copy human-readable label and shortcut
+  const char* l = fl_label_string(labeltype_, text);
+  size_t len;
+  char* name = fl_label_to_text(l, &len);
+  if (!shortcut())  fl_str_to_bstr(name, len, bstr);
+  else
+  {
+    const char* str = fl_shortcut_label(shortcut());
+    size_t full_len = len + strlen(str) + 1;
+    char* s = (char*)malloc(full_len + 1);
+    sprintf(s, "%s\t%s", name, str);
+    fl_str_to_bstr(s, full_len, bstr);
+    free(s);
+  }
+  fl_free_str(name);
+  return true;
+}
+
+
+bool
+Fl_Menu_Item::msaa_value(BSTR* bstr) const
+{
+  // Return value only for button type item
+  if (!checkbox() && !radio())  return false;
+  fl_str_to_bstr((value() ? "1" : "0"), -1, bstr);
+  return true;
+}
+
+
+bool
+Fl_Menu_Item::msaa_shortcut(bool on_menubar, BSTR* bstr) const
+{
+  const char* l = fl_label_string(labeltype_, text);
+  size_t len;
+  char* str = fl_label_to_shortcut(l, &len);
+  if (len && on_menubar)
+  {
+    const char* prefix = "Alt + ";
+    len += strlen(prefix);
+    char* shortcut = (char*)malloc(len + 1);
+    sprintf(shortcut, "%s%s", prefix, str);
+    fl_str_to_bstr(shortcut, len, bstr);
+    free(shortcut);
+  }
+  else if (len)
+  {
+    fl_str_to_bstr(str, len, bstr);
+  }
+  fl_free_str(str);
+  return len > 0;
+}
+
+
+bool
+Fl_Menu_Item::msaa_put_value(const char* str)
+{
+  if (!checkbox() && !radio())  return false;
+  int v = atoi(str);
+  if (radio() && v)  setonly();
+  else if (v)  set();
+  else  clear();
+  return true;
+}
+
+
+bool
+menutitle::msaa_name(long id, BSTR* bstr)
+{
+  if (id != 0)  return false;
+  return menu->msaa_name(bstr);
+}
+
+
+bool
+menutitle::msaa_shortcut(long id, BSTR* bstr)
+{
+  if (id != 0)  return false;
+  return menu->msaa_shortcut(on_menubar_, bstr);
+}
+
+
+void
+menuwindow::msaa_menuwindow_on_select()
+{
+  int m = p->menu_number;
+  if (m < 0)  return;
+
+  int id = p->item_number + 1;
+  if ((m == 0) && p->menubar)
+  {
+    // Set focus to menubar item when it is selected
+    ((Fl_Menu_*)button)->msaa_notify(EVENT_OBJECT_FOCUS, id);
+  }
+  else
+  {
+    // Otherwise set focus to menu item
+    p->p[m]->msaa_notify(EVENT_OBJECT_FOCUS, id);
+  }
+}
+
+
+long
+menuwindow::msaa_child_count()
+{
+  long count = 0;
+  for (const Fl_Menu_Item* m = menu->first();  m->text;
+      m = m->next(),  ++count);
+  return count;
+}
+
+
+bool
+menuwindow::msaa_location(long id, long* X, long* Y, long* W, long* H)
+{
+  bool ok = Fl_Window::msaa_location(0, X, Y, W, H);
+  if ((id == 0) || !ok)  return ok;
+
+  if (!itemheight)
+  {
+    // Handle menubar
+    // NOTE: This must match algorithm in Fl_Menu_::draw()
+    *X += 6;
+    const Fl_Menu_Item* m = menu->first();
+    for (*X += 6;  --id;  m = m->next())
+      *X += m->measure(0, button) + 16;
+    *W = m->measure(0, button) + 6;
+  }
+  else
+  {
+    // Handle popup menu
+    // NOTE: This must match algorithm in menuitem::drawentry()
+    int BW = Fl::box_dx(box());
+    *X += BW;
+    *Y += BW + 1 + (id - 1) * itemheight;
+    *W  = w() - 2 * BW - 1;
+    *H  = itemheight - LEADING;
+  }
+  return true;
+}
+
+
+bool
+menuwindow::msaa_name(long id, BSTR* bstr)
+{
+  if (id == 0)  return Fl_Window::msaa_name(0, bstr);
+  const Fl_Menu_Item* m = menu->first();
+  while (--id)  m = m->next();
+  return m->msaa_name(bstr);
+}
+
+
+bool
+menuwindow::msaa_value(long id, BSTR* bstr)
+{
+  if (id == 0)  return Fl_Window::msaa_value(0, bstr);
+  const Fl_Menu_Item* m = menu->first();
+  while (--id)  m = m->next();
+  return m->msaa_value(bstr);
+}
+
+
+bool
+menuwindow::msaa_shortcut(long id, BSTR* bstr)
+{
+  if (id == 0)  return Fl_Window::msaa_shortcut(0, bstr);
+  const Fl_Menu_Item* m = menu->first();
+  while (--id)  m = m->next();
+  return m->msaa_shortcut(false, bstr);
+}
+
+
+bool
+menuwindow::msaa_put_value(long id, const char* str)
+{
+  if (id == 0)  return Fl_Window::msaa_put_value(0, str);
+  const Fl_Menu_Item* m = menu->first();
+  while (--id)  m = m->next();
+  return ((Fl_Menu_Item*)m)->msaa_put_value(str);
+}
+#endif // FL_HAVE_MSAA
+
+
 //
 // End of "$Id$".
 //
Index: src/Fl_Text_Display.cxx
===================================================================
--- src/Fl_Text_Display.cxx	(revision 6732)
+++ src/Fl_Text_Display.cxx	(working copy)
@@ -3188,6 +3188,15 @@
     fl_pop_clip();
   }
   fl_pop_clip();
+
+#ifdef FL_HAVE_MSAA
+  int caret_x, caret_y;
+  if (position_to_xy(mCursorPos, &caret_x, &caret_y))
+  {
+    int caret_w = 4;
+    msaa_show_caret(caret_x - caret_w / 2, caret_y, caret_w, mMaxsize);
+  }
+#endif // FL_HAVE_MSAA
 }
 
 // this processes drag events due to mouse for Fl_Text_Display and
@@ -3279,6 +3288,9 @@
     case FL_HIDE:
       if (active_r() && window()) {
         window()->cursor(FL_CURSOR_DEFAULT);
+#ifdef FL_HAVE_MSAA
+        msaa_hide_caret();
+#endif
 
 	return 1;
       } else {
@@ -3404,6 +3416,9 @@
       else return mHScrollBar->handle(event);
 
     case FL_UNFOCUS:
+#ifdef FL_HAVE_MSAA
+      msaa_hide_caret();
+#endif
       if (active_r() && window()) window()->cursor(FL_CURSOR_DEFAULT);
     case FL_FOCUS:
       if (buffer()->selected()) {
Index: src/Fl_Slider.cxx
===================================================================
--- src/Fl_Slider.cxx	(revision 6732)
+++ src/Fl_Slider.cxx	(working copy)
@@ -350,6 +350,242 @@
 		h()-Fl::box_dh(box()));
 }
 
+
+#ifdef FL_HAVE_MSAA
+#include <FL/Fl_Msaa_Proxy.H>
+///////////////////////////////////////////////////////////////////////
+//
+//  The following code implements Microsoft Active Accessibility
+
+#define CID_PAGE_UP	1
+#define CID_POS		2
+#define CID_PAGE_DN	3
+
+
+void
+Fl_Slider::msaa_valuator_on_change()
+{
+  Fl_Valuator::msaa_valuator_on_change();
+  msaa_notify(EVENT_OBJECT_STATECHANGE, CID_PAGE_UP);
+  msaa_notify(EVENT_OBJECT_STATECHANGE, CID_PAGE_DN);
+}
+
+
+bool
+Fl_Slider::msaa_slider_location(long id,
+  long oX, long oY, long oW, long oH, long* X, long* Y, long* W, long* H)
+{
+  // Note: size calculations must match those in draw()
+  int full_len = horizontal() ? oW : oH;
+  int pos_len = full_len;
+  int up_len = 0;
+  int dn_len = 0;
+
+  // Find position indicator length if it doesn't fill the whole slider
+  if ((slider_size_ >= 0.0) || (slider_size_ <= 1.0))
+  {
+    double v;
+    if (minimum() == maximum())  v = 0.5;
+    else
+    {
+      v = (value() - minimum()) / (maximum() - minimum());
+      if (v > 1.0)  v = 1.0;
+      else if (v < 0.0)  v = 0.0;
+    }
+
+    // Calculate length of position indicator
+    if ((type() == FL_HOR_FILL_SLIDER) || (type() == FL_VERT_FILL_SLIDER))
+      pos_len = 0;
+    else
+    {
+      int min_len = (horizontal() ? oH : oW) / 2 + 1;
+      if ((type() == FL_HOR_NICE_SLIDER) || (type() == FL_VERT_NICE_SLIDER))
+	min_len += 4;
+      pos_len = (int)(slider_size_ * full_len + 0.5);
+      if (pos_len < min_len)  pos_len = min_len;
+    }
+
+    // Calculate length before and after position indicator
+    up_len = (int)(v * (full_len - pos_len) + 0.5);
+    dn_len = full_len - up_len - pos_len;
+  }
+
+  switch (id)
+  {
+    case CID_PAGE_UP:
+      if (up_len == 0)  return false;
+      if (horizontal())
+      {
+        *X = oX;
+        *Y = oY;
+        *W = up_len;
+        *H = oH;
+      }
+      else
+      {
+        *X = oX;
+        *Y = oY;
+        *W = oW;
+        *H = up_len;
+      }
+      return true;
+
+    case CID_POS:
+      if (pos_len == 0)  return false;
+      if (horizontal())
+      {
+        *X = oX + up_len;
+        *Y = oY;
+        *W = pos_len;
+        *H = oH;
+      }
+      else
+      {
+        *X = oX;
+        *Y = oY + up_len;
+        *W = oW;
+        *H = pos_len;
+      }
+      return true;
+
+    case CID_PAGE_DN:
+      if (dn_len == 0)  return false;
+      if (horizontal())
+      {
+        *X = oX + up_len + pos_len;
+        *Y = oY;
+        *W = dn_len;
+        *H = oH;
+      }
+      else
+      {
+        *X = oX;
+        *Y = oY + up_len + pos_len;
+        *W = oW;
+        *H = dn_len;
+      }
+      return true;
+
+    default:
+      return false;
+  }
+}
+
+
+bool
+Fl_Slider::msaa_name(long id, BSTR* bstr)
+{
+  const char* l;
+  switch (id)
+  {
+    case 0:
+	l = label();
+	if (!l || !l[0])  l = horizontal() ? "Horizontal" : "Vertical";
+	break;
+    case CID_PAGE_UP:
+    	l = horizontal() ? "Page left" : "Page up";
+	break;
+    case CID_POS:
+    	l = "Position";
+	break;
+    case CID_PAGE_DN:
+    	l = horizontal() ? "Page right" : "Page down";
+	break;
+    default:
+    	return false;
+  }
+  fl_str_to_bstr(l, -1, bstr);
+  return true;
+}
+
+
+long
+Fl_Slider::msaa_role(long id)
+{
+  switch (id)
+  {
+    case CID_POS:	return ROLE_SYSTEM_INDICATOR;
+    case CID_PAGE_UP:	// Fall through
+    case CID_PAGE_DN:	return ROLE_SYSTEM_PUSHBUTTON;
+    default:  		return ROLE_SYSTEM_SLIDER;
+  }
+}
+
+
+long
+Fl_Slider::msaa_state(long id)
+{
+  long s = Fl_Widget::msaa_state(0) & ~STATE_SYSTEM_FOCUSABLE;
+  switch (id)
+  {
+    case 0:
+      return s | STATE_SYSTEM_FOCUSABLE;
+    case CID_PAGE_UP:
+      return s | (value() <= minimum() ? STATE_SYSTEM_INVISIBLE : 0);
+    case CID_POS:
+      return s | (((type() == FL_HOR_FILL_SLIDER) ||
+      	(type() == FL_VERT_FILL_SLIDER)) ? STATE_SYSTEM_INVISIBLE : 0);
+    case CID_PAGE_DN:
+      return s | (value() >= maximum() ? STATE_SYSTEM_INVISIBLE : 0);
+    default:
+      return s;
+  }
+}
+
+
+bool
+Fl_Slider::msaa_action(long id, BSTR* bstr)
+{
+  const char* l;
+  switch (id)
+  {
+    case CID_PAGE_UP:	// Fall through
+    case CID_PAGE_DN:	l = "Press";  break;
+    default:		return false;
+  }
+  fl_str_to_bstr(l, -1, bstr);
+  return true;
+}
+
+
+bool
+Fl_Slider::msaa_location(long id, long* X, long* Y, long* W, long* H)
+{
+  if (id == 0)  return Fl_Widget::msaa_location(0, X, Y, W, H);
+  if (!msaa_visible())  return false;
+
+  // Note: size calculations must match those in draw()
+  long sxx, syy, sww, shh;
+  Fl_Widget::msaa_location(0, &sxx, &syy, &sww, &shh);
+  return msaa_slider_location(id,
+    sxx + Fl::box_dx(box()), syy + Fl::box_dy(box()),
+    sww - Fl::box_dw(box()), shh - Fl::box_dh(box()), X, Y, W, H);
+}
+
+
+bool
+Fl_Slider::msaa_do_action(long id)
+{
+  // Move slider
+  switch (id)
+  {
+    case CID_PAGE_UP:
+      handle_push();
+      handle_drag(clamp(increment(value(),-1)));
+      handle_release();
+      return true;
+    case CID_PAGE_DN:
+      handle_push();
+      handle_drag(clamp(increment(value(),1)));
+      handle_release();
+      return true;
+    default:
+      return false;
+  }
+}
+#endif // FL_HAVE_MSAA
+
+
 //
 // End of "$Id$".
 //
Index: src/Fl_Browser_.cxx
===================================================================
--- src/Fl_Browser_.cxx	(revision 6732)
+++ src/Fl_Browser_.cxx	(working copy)
@@ -1,4 +1,5 @@
 //
+//
 // "$Id$"
 //
 // Base Browser widget class for the Fast Light Tool Kit (FLTK).
@@ -667,6 +668,9 @@
     set_changed();
     do_callback();
   }
+#ifdef FL_HAVE_MSAA
+  msaa_on_focus();
+#endif // FL_HAVE_MSAA
   return 1;
 }
 
@@ -750,6 +754,9 @@
         case ' ':
           selection_ = l;
           select(l, !item_selected(l), when() & ~FL_WHEN_ENTER_KEY);
+#ifdef FL_HAVE_MSAA
+          msaa_on_focus();
+#endif // FL_HAVE_MSAA
           return 1;
         case FL_Down:
           while ((l = item_next(l))) {
@@ -769,6 +776,9 @@
           if (selection_) redraw_line(selection_);
           selection_ = l; redraw_line(l);
           display(l);
+#ifdef FL_HAVE_MSAA
+          msaa_on_focus();
+#endif // FL_HAVE_MSAA
           return 1;
         }
       }
@@ -906,6 +916,9 @@
       select_only(l, when() & FL_WHEN_CHANGED);
     }
     py = my;
+#ifdef FL_HAVE_MSAA
+    msaa_on_focus();
+#endif // FL_HAVE_MSAA
     return 1;
   case FL_RELEASE:
     if (type() == FL_SELECT_BROWSER) {
@@ -966,6 +979,10 @@
   max_width_item = 0;
   redraw1 = redraw2 = 0;
   end();
+#ifdef FL_HAVE_MSAA
+  // Initialize MSAA stuff
+  msaa_browser_proxy_ = 0;
+#endif // FL_HAVE_MSAA
 }
 
 /**
@@ -1083,6 +1100,792 @@
 */
 int Fl_Browser_::item_selected(void* item) const { return item==selection_ ? 1 : 0; }
 
+
+#ifdef FL_HAVE_MSAA
+#include <FL/Fl_Msaa_Proxy.H>
+#include <FL/x.H>
+///////////////////////////////////////////////////////////////////////
+//
+//  The following code implements Microsoft Active Accessibility
+
+#define CID_LIST	1
+#define CID_VERTICAL	2
+#define CID_HORIZONTAL	3
+#define CID_GRIP	4
+
+
+// Specialized MSAA proxy for Fl_Browser_ object
+class Fl_Msaa_Browser_Proxy : public Fl_Msaa_Proxy
+{
+public:
+  Fl_Msaa_Browser_Proxy(Fl_Widget* o) : Fl_Msaa_Proxy(o)  {}
+
+  // IAccessible implementation
+  HRESULT STDMETHODCALLTYPE get_accParent(IDispatch**);
+  HRESULT STDMETHODCALLTYPE get_accChildCount(long*);
+  HRESULT STDMETHODCALLTYPE get_accChild(VARIANT, IDispatch**);
+  HRESULT STDMETHODCALLTYPE get_accName(VARIANT, BSTR*);
+  HRESULT STDMETHODCALLTYPE get_accValue(VARIANT, BSTR*);
+  HRESULT STDMETHODCALLTYPE get_accDescription(VARIANT, BSTR*);
+  HRESULT STDMETHODCALLTYPE get_accRole(VARIANT, VARIANT*);
+  HRESULT STDMETHODCALLTYPE get_accState(VARIANT, VARIANT*);
+  HRESULT STDMETHODCALLTYPE get_accHelp(VARIANT, BSTR*);
+  HRESULT STDMETHODCALLTYPE get_accHelpTopic(BSTR*, VARIANT, long*);
+  HRESULT STDMETHODCALLTYPE get_accKeyboardShortcut(VARIANT, BSTR*);
+  HRESULT STDMETHODCALLTYPE get_accFocus(VARIANT*);
+  HRESULT STDMETHODCALLTYPE get_accSelection(VARIANT*);
+  HRESULT STDMETHODCALLTYPE get_accDefaultAction(VARIANT, BSTR*);
+  HRESULT STDMETHODCALLTYPE accSelect(long, VARIANT);
+  HRESULT STDMETHODCALLTYPE accLocation(long*, long*, long*, long*, VARIANT);
+  HRESULT STDMETHODCALLTYPE accNavigate(long, VARIANT, VARIANT*);
+  HRESULT STDMETHODCALLTYPE accHitTest(long, long, VARIANT*);
+  HRESULT STDMETHODCALLTYPE accDoDefaultAction(VARIANT);
+  HRESULT STDMETHODCALLTYPE put_accValue(VARIANT, BSTR);
+};
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Browser_Proxy::get_accParent(
+    /* [retval][out] */ IDispatch** ppdispParent)
+{
+  Fl_Browser_* browser = (Fl_Browser_*)mpObj;
+  if (!browser)  return CO_E_OBJNOTCONNECTED;
+  if (!ppdispParent)  return E_INVALIDARG;
+  *ppdispParent = NULL;
+
+  // Fl_Msaa_Browser_Proxy_ has a Fl_Msaa_Proxy parent
+  Fl_Msaa_Proxy* parent = browser->msaa_child(0);
+  if (!parent)  return S_FALSE;
+  parent->QueryInterface(IID_IDispatch, (void**)ppdispParent);
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Browser_Proxy::get_accChildCount(
+    /* [retval][out] */ long* pcountChildren)
+{
+  Fl_Browser_* browser = (Fl_Browser_*)mpObj;
+  if (!browser)  return CO_E_OBJNOTCONNECTED;
+  if (!pcountChildren)  return E_INVALIDARG;
+  *pcountChildren = browser->msaa_browser_size();
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Browser_Proxy::get_accChild(
+    /* [in] */ VARIANT varChild,
+    /* [retval][out] */ IDispatch** ppdispChild)
+{
+  Fl_Browser_* browser = (Fl_Browser_*)mpObj;
+  if (!browser)  return CO_E_OBJNOTCONNECTED;
+  if (varChild.vt != VT_I4)  return E_INVALIDARG;
+  if (!ppdispChild)  return E_INVALIDARG;
+  *ppdispChild = NULL;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > browser->msaa_browser_size())  return E_INVALIDARG;
+  return S_FALSE;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Browser_Proxy::get_accName(
+    /* [optional][in] */ VARIANT varChild,
+    /* [retval][out] */ BSTR* pszName)
+{
+  Fl_Browser_* browser = (Fl_Browser_*)mpObj;
+  if (!browser)  return CO_E_OBJNOTCONNECTED;
+  if (!pszName)  return E_INVALIDARG;
+  *pszName = NULL;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > browser->msaa_browser_size())  return E_INVALIDARG;
+  if (id == 0)  return Fl_Msaa_Proxy::get_accName(varChild, pszName);
+
+  // Report text of required item as name
+  if (!browser->msaa_browser_text(id, pszName))  return DISP_E_MEMBERNOTFOUND;
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Browser_Proxy::get_accValue(
+    /* [optional][in] */ VARIANT varChild,
+    /* [retval][out] */ BSTR* pszValue)
+{
+  Fl_Browser_* browser = (Fl_Browser_*)mpObj;
+  if (!browser)  return CO_E_OBJNOTCONNECTED;
+  if (!pszValue)  return E_INVALIDARG;
+  *pszValue = NULL;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > browser->msaa_browser_size())  return E_INVALIDARG;
+  if (id == 0)  return Fl_Msaa_Proxy::get_accValue(varChild, pszValue);
+
+  // No value for list items
+  return DISP_E_MEMBERNOTFOUND;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Browser_Proxy::get_accDescription(
+    /* [optional][in] */ VARIANT varChild,
+    /* [retval][out] */ BSTR* pszDescription)
+{
+  Fl_Browser_* browser = (Fl_Browser_*)mpObj;
+  if (!browser)  return CO_E_OBJNOTCONNECTED;
+  if (!pszDescription)  return E_INVALIDARG;
+  *pszDescription = NULL;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > browser->msaa_browser_size())  return E_INVALIDARG;
+  if (id == 0)
+    return Fl_Msaa_Proxy::get_accDescription(varChild, pszDescription);
+
+  // Report description of required list item
+  if (!browser->msaa_browser_description(id, pszDescription))
+    return DISP_E_MEMBERNOTFOUND;
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Browser_Proxy::get_accRole(
+    /* [optional][in] */ VARIANT varChild,
+    /* [retval][out] */ VARIANT* pvarRole)
+{
+  Fl_Browser_* browser = (Fl_Browser_*)mpObj;
+  if (!browser)  return CO_E_OBJNOTCONNECTED;
+  if (!pvarRole)  return E_INVALIDARG;
+  pvarRole->vt = VT_EMPTY;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > browser->msaa_browser_size())  return E_INVALIDARG;
+
+  pvarRole->vt   = VT_I4;
+  pvarRole->lVal = (id == 0 ? ROLE_SYSTEM_LIST : ROLE_SYSTEM_LISTITEM);
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Browser_Proxy::get_accState(
+    /* [optional][in] */ VARIANT varChild,
+    /* [retval][out] */ VARIANT* pvarState)
+{
+  Fl_Browser_* browser = (Fl_Browser_*)mpObj;
+  if (!browser)  return CO_E_OBJNOTCONNECTED;
+  if (!pvarState)  return E_INVALIDARG;
+  pvarState->vt = VT_EMPTY;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > browser->msaa_browser_size())  return E_INVALIDARG;
+  if (id == 0)  return Fl_Msaa_Proxy::get_accState(varChild, pvarState);
+
+  // Indicate whether a list item is visible
+  pvarState->vt   = VT_I4;
+  pvarState->lVal = STATE_SYSTEM_SELECTABLE |
+    (browser->type() == FL_MULTI_BROWSER ? STATE_SYSTEM_MULTISELECTABLE : 0) |
+    (browser->msaa_browser_selected(id) ? STATE_SYSTEM_SELECTED : 0) |
+    (browser->msaa_browser_displayed(id) ? 0 :
+    	(STATE_SYSTEM_INVISIBLE | STATE_SYSTEM_OFFSCREEN));
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Browser_Proxy::get_accHelp(
+    /* [optional][in] */ VARIANT varChild,
+    /* [retval][out] */ BSTR* pszHelp)
+{
+  Fl_Browser_* browser = (Fl_Browser_*)mpObj;
+  if (!browser)  return CO_E_OBJNOTCONNECTED;
+  if (!pszHelp)  return E_INVALIDARG;
+  *pszHelp = NULL;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > browser->msaa_browser_size())  return E_INVALIDARG;
+  if (id == 0)  return Fl_Msaa_Proxy::get_accHelp(varChild, pszHelp);
+
+  return DISP_E_MEMBERNOTFOUND;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Browser_Proxy::get_accHelpTopic(
+    /* [out] */ BSTR* pszHelpFile,
+    /* [optional][in] */ VARIANT varChild,
+    /* [retval][out] */ long* pidTopic)
+{
+  Fl_Browser_* browser = (Fl_Browser_*)mpObj;
+  if (!browser)  return CO_E_OBJNOTCONNECTED;
+  if (!pszHelpFile)  return E_INVALIDARG;
+  if (!pidTopic)  return E_INVALIDARG;
+  *pszHelpFile = NULL;
+  *pidTopic = 0;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > browser->msaa_browser_size())  return E_INVALIDARG;
+  if (id == 0)
+    return Fl_Msaa_Proxy::get_accHelpTopic(pszHelpFile, varChild, pidTopic);
+
+  return DISP_E_MEMBERNOTFOUND;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Browser_Proxy::get_accKeyboardShortcut(
+    /* [optional][in] */ VARIANT varChild,
+    /* [retval][out] */ BSTR* pszKeyboardShortcut)
+{
+  Fl_Browser_* browser = (Fl_Browser_*)mpObj;
+  if (!browser)  return CO_E_OBJNOTCONNECTED;
+  if (!pszKeyboardShortcut)  return E_INVALIDARG;
+  *pszKeyboardShortcut = NULL;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > browser->msaa_browser_size())  return E_INVALIDARG;
+  if (id == 0)
+    return Fl_Msaa_Proxy::get_accKeyboardShortcut(
+      varChild, pszKeyboardShortcut);
+
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Browser_Proxy::get_accFocus(
+    /* [retval][out] */ VARIANT *pvarChild)
+{
+  Fl_Browser_* browser = (Fl_Browser_*)mpObj;
+  if (!browser)  return CO_E_OBJNOTCONNECTED;
+  if (!pvarChild)  return E_INVALIDARG;
+  pvarChild->vt = VT_EMPTY;
+
+  // Check whether the browser has keyboard focus
+  Fl_Widget* f = Fl::focus();
+  if (f != browser)  return S_OK;
+
+  // Now return index of item with focus
+  pvarChild->vt   = VT_I4;
+  pvarChild->lVal = browser->msaa_browser_focus();
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Browser_Proxy::get_accSelection(
+    /* [retval][out] */ VARIANT* pvarChildren)
+{
+  Fl_Browser_* browser = (Fl_Browser_*)mpObj;
+  if (!browser)  return CO_E_OBJNOTCONNECTED;
+  if (!pvarChildren)  return E_INVALIDARG;
+
+  // Build list of selected items
+  long*  sel = NULL;
+  size_t buf_size = 0;
+  size_t count = 0;
+  if (browser->type() == FL_MULTI_BROWSER)
+  {
+    void* l = browser->item_first();
+    for (int n = 1;  l;  l = browser->item_next(l), ++n)
+    {
+      if (browser->item_selected(l))
+      {
+        if (count >= buf_size)
+        {
+	  if (buf_size == 0)  buf_size = 4;
+	  else  buf_size *= 2;
+	  sel = (long*)realloc(sel, sizeof(long) * buf_size);
+	}
+        sel[count++] = n;
+      }
+      if (l == browser->item_last())  break;
+    }
+  }
+  else if (browser->selection())
+  {
+    sel = (long*)malloc(sizeof(long));
+    sel[0] = browser->msaa_browser_find_item(browser->selection());
+    count = 1;
+  }
+
+  // Put selection in VARIANT
+  Fl_Widget::msaa_convert_selection(sel, count, pvarChildren);
+  if (sel)  free(sel);
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Browser_Proxy::get_accDefaultAction(
+    /* [optional][in] */ VARIANT varChild,
+    /* [retval][out] */ BSTR* pszDefaultAction)
+{
+  Fl_Browser_* browser = (Fl_Browser_*)mpObj;
+  if (!browser)  return CO_E_OBJNOTCONNECTED;
+  if (!pszDefaultAction)  return E_INVALIDARG;
+  *pszDefaultAction = NULL;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > browser->msaa_browser_size())  return E_INVALIDARG;
+  if (id == 0)
+    return Fl_Msaa_Proxy::get_accDefaultAction(varChild, pszDefaultAction);
+
+  // No default action for list items
+  return DISP_E_MEMBERNOTFOUND;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Browser_Proxy::accSelect(
+    /* [in] */ long flagsSelect,
+    /* [optional][in] */ VARIANT varChild)
+{
+  Fl_Browser_* browser = (Fl_Browser_*)mpObj;
+  if (!browser)  return CO_E_OBJNOTCONNECTED;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > browser->msaa_browser_size())  return E_INVALIDARG;
+  if (id == 0)  return Fl_Msaa_Proxy::accSelect(flagsSelect, varChild);
+
+  if (flagsSelect == SELFLAG_NONE)  return S_OK;
+  if (!browser->msaa_browser_do_select(id, flagsSelect))
+    return DISP_E_MEMBERNOTFOUND;
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Browser_Proxy::accLocation(
+    /* [out] */ long* pxLeft,
+    /* [out] */ long* pyTop,
+    /* [out] */ long* pcxWidth,
+    /* [out] */ long* pcyHeight,
+    /* [optional][in] */ VARIANT varChild)
+{
+  Fl_Browser_* browser = (Fl_Browser_*)mpObj;
+  if (!browser)  return CO_E_OBJNOTCONNECTED;
+  if (!pxLeft || !pyTop || !pcxWidth || !pcyHeight)  return E_INVALIDARG;
+  *pxLeft = *pyTop = *pcxWidth = *pcyHeight = 0;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > browser->msaa_browser_size())  return E_INVALIDARG;
+
+  if (id == 0)
+  {
+    browser->msaa_browser_bbox(pxLeft, pyTop, pcxWidth, pcyHeight);
+    return S_OK;
+  }
+  if (browser->msaa_browser_item_bbox(id, pxLeft, pyTop, pcxWidth, pcyHeight))
+    return S_OK;
+  return DISP_E_MEMBERNOTFOUND;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Browser_Proxy::accNavigate(
+    /* [in] */ long navDir,
+    /* [optional][in] */ VARIANT varStart,
+    /* [retval][out] */ VARIANT* pvarEndUpAt)
+{
+  Fl_Browser_* browser = (Fl_Browser_*)mpObj;
+  if (!browser)  return CO_E_OBJNOTCONNECTED;
+  if (!pvarEndUpAt)  return E_INVALIDARG;
+  pvarEndUpAt->vt = VT_EMPTY;
+  long id = (varStart.vt == VT_I4 ? varStart.lVal : 0);
+  if (id < 0 || id > browser->msaa_browser_size())  return E_INVALIDARG;
+
+  // Handle navigation relative to this widget
+  if ((id == 0) &&
+      (navDir != NAVDIR_FIRSTCHILD) && (navDir != NAVDIR_LASTCHILD))
+  {
+    // Forward request to browser widget
+    varStart.vt   = VT_I4;
+    varStart.lVal = CID_LIST;
+    return Fl_Msaa_Proxy::accNavigate(navDir, varStart, pvarEndUpAt);
+  }
+
+  // Handle navigation within browser content
+  long max_id = browser->msaa_browser_size();
+  if (max_id < 1)  return DISP_E_MEMBERNOTFOUND;
+  long dst;
+  switch (navDir)
+  {
+    case NAVDIR_FIRSTCHILD:  dst = 1;  break;
+    case NAVDIR_LASTCHILD:   dst = max_id;  break;
+    case NAVDIR_UP:	     // Fall through
+    case NAVDIR_PREVIOUS:    dst = (id == 1 ? -1 : (id - 1));  break;
+    case NAVDIR_DOWN:	     // Fall through
+    case NAVDIR_NEXT:	     dst = (id == max_id ? -1 : (id + 1));  break;
+
+    // Left/right navigation not supported
+    case NAVDIR_LEFT:	     // Fall through
+    case NAVDIR_RIGHT:	     // Fall through
+    default:		     dst = -1;  break;
+  }
+  if (dst < 0)  return DISP_E_MEMBERNOTFOUND;
+  pvarEndUpAt->vt   = VT_I4;
+  pvarEndUpAt->lVal = dst;
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Browser_Proxy::accHitTest(
+    /* [in] */ long xLeft,
+    /* [in] */ long yTop,
+    /* [retval][out] */ VARIANT* pvarChild)
+{
+  Fl_Browser_* browser = (Fl_Browser_*)mpObj;
+  if (!browser)  return CO_E_OBJNOTCONNECTED;
+  if (!pvarChild)  return E_INVALIDARG;
+  pvarChild->vt = VT_EMPTY;
+
+  // Convert to Fl_Window co-ordinates
+  POINT pt = {xLeft, yTop};
+  ScreenToClient(fl_xid(browser->window()), &pt);
+
+  // Check if it's outside the browser
+  if ((pt.x < browser->x()) || (pt.x >= browser->x() + browser->w()) ||
+      (pt.y < browser->y()) || (pt.y >= browser->y() + browser->h()))
+    return S_FALSE;
+
+  // Return child ID
+  pvarChild->vt   = VT_I4;
+  pvarChild->lVal = browser->msaa_browser_find_item(browser->find_item(pt.y));
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Browser_Proxy::accDoDefaultAction(
+    /* [optional][in] */ VARIANT varChild)
+{
+  Fl_Browser_* browser = (Fl_Browser_*)mpObj;
+  if (!browser)  return CO_E_OBJNOTCONNECTED;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > browser->msaa_browser_size())  return E_INVALIDARG;
+
+  return DISP_E_MEMBERNOTFOUND;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Browser_Proxy::put_accValue(
+    /* [optional][in] */ VARIANT varChild,
+    /* [in] */ BSTR szValue)
+{
+  Fl_Browser_* browser = (Fl_Browser_*)mpObj;
+  if (!browser)  return CO_E_OBJNOTCONNECTED;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > browser->msaa_browser_size())  return E_INVALIDARG;
+
+  return DISP_E_MEMBERNOTFOUND;
+}
+
+
+/*------------------------- Fl_Browser_ MSAA support -----------------------*/
+
+void
+Fl_Browser_::msaa_cleanup()
+{
+  // Destroy custom proxy
+  if (msaa_browser_proxy_)
+  {
+    msaa_browser_proxy_->Detach();
+    msaa_browser_proxy_->Release();
+  }
+
+  // Destroy standard proxy
+  Fl_Group::msaa_cleanup();
+}
+
+
+long
+Fl_Browser_::msaa_child_id(Fl_Widget* o) const
+{
+  if (o == this)	 return 0;
+  if (o == &scrollbar)	 return CID_VERTICAL;
+  if (o == &hscrollbar)	 return CID_HORIZONTAL;
+  return -1;
+}
+
+
+Fl_Widget*
+Fl_Browser_::msaa_child_widget(long id) const
+{
+  switch (id)
+  {
+    case 0:		  return (Fl_Widget*)this;
+    case CID_VERTICAL:	  return (Fl_Widget*)&scrollbar;
+    case CID_HORIZONTAL:  return (Fl_Widget*)&hscrollbar;
+    default:		  return NULL;
+  }
+}
+
+
+Fl_Msaa_Proxy*
+Fl_Browser_::msaa_child(long id)
+{
+  if (id != CID_LIST)  return Fl_Group::msaa_child(id);
+
+  // Create specialized list proxy
+  if (!msaa_browser_proxy_)
+  {
+    msaa_browser_proxy_ = new Fl_Msaa_Browser_Proxy(this);
+    if (msaa_browser_proxy_)  msaa_browser_proxy_->AddRef();
+  }
+  return msaa_browser_proxy_;
+}
+
+
+long
+Fl_Browser_::msaa_role(long id)
+{
+  switch (id)
+  {
+    case CID_LIST:	  return ROLE_SYSTEM_LIST;
+    case CID_GRIP:	  return ROLE_SYSTEM_GRIP;
+    default:		  return Fl_Group::msaa_role(id);
+  }
+}
+
+
+long
+Fl_Browser_::msaa_state(long id)
+{
+  switch (id)
+  {
+    case 0:
+      return Fl_Group::msaa_state(id) & ~STATE_SYSTEM_FOCUSED;
+    case CID_LIST:
+      return Fl_Group::msaa_state(id) |
+        (Fl::focus() == this ? STATE_SYSTEM_FOCUSED : 0);
+    case CID_GRIP:
+      return (scrollbar.visible() && hscrollbar.visible()) ?
+        0 : STATE_SYSTEM_INVISIBLE;
+    default:
+      return Fl_Group::msaa_state(id);
+  }
+}
+
+
+long
+Fl_Browser_::msaa_focus()
+{
+  int id = Fl_Group::msaa_focus();
+  if (id == 0)  return CID_LIST;
+  return id;
+}
+
+
+bool 
+Fl_Browser_::msaa_location(long id, long* X, long* Y, long* W, long* H)
+{
+  if (!msaa_visible())  return false;
+  switch (id)
+  {
+    case CID_LIST:
+      msaa_browser_bbox(X, Y, W, H);
+      return true;
+
+    case CID_GRIP:
+      if (scrollbar.visible() && hscrollbar.visible())
+      {
+	long dummy;
+        Fl_Group::msaa_location(CID_VERTICAL, X, &dummy, W, &dummy);
+	Fl_Group::msaa_location(CID_HORIZONTAL, &dummy, Y, &dummy, H);
+        return true;
+      }
+      return false;
+
+    default:
+      return Fl_Group::msaa_location(id, X, Y, W, H);
+  }
+}
+
+
+void
+Fl_Browser_::msaa_browser_bbox(long* X, long* Y, long* W, long* H)
+{
+  Fl_Window* win = window();
+  int oX, oY, oW, oH;
+  bbox(oX, oY, oW, oH);
+  *X = oX + win->x();
+  *Y = oY + win->y();
+  *W = oW;
+  *H = oH;
+}
+
+
+void
+Fl_Browser_::msaa_on_focus()
+{
+  if (Fl::focus() != this)  return;
+  if (!selection_)  Fl_Widget::msaa_on_focus();
+  Fl_Msaa_Proxy* proxy = msaa_child(CID_LIST);
+  if (!proxy)  return;
+  NotifyWinEvent(EVENT_OBJECT_FOCUS, fl_xid(msaa_window()),
+    (LONG)proxy, msaa_browser_focus());
+}
+
+
+bool
+Fl_Browser_::msaa_browser_item_bbox(long id,
+    long* X, long* Y, long* W, long* H)
+{
+  if (id < 1)  return false;
+  void* l;
+  for (l = item_first();  --id;  l = item_next(l));
+  if (!displayed(l))  return false;
+  long top_y, full_h;
+  msaa_browser_bbox(X, &top_y, W, &full_h);
+  *Y = top_y - offset_;
+  *H = item_height(l);
+  while (l != top_)
+  {
+    l = item_prev(l);
+    *Y += item_height(l);
+  }
+  if ((*Y + *H) > (top_y + full_h))  *H -= (top_y + full_h - *Y);
+  return true;
+}
+
+
+long
+Fl_Browser_::msaa_browser_size()
+{
+  long count = 0;
+  for (void* l = item_first();  l;  l = item_next(l))  count++;
+  return count;
+}
+
+
+bool
+Fl_Browser_::msaa_browser_displayed(long id)
+{
+  if (id < 1)  return false;
+  void* l;
+  for (l = item_first();  --id;  l = item_next(l));
+  return displayed(l);
+}
+
+
+bool
+Fl_Browser_::msaa_browser_selected(long id)
+{
+  if (id < 1)  return false;
+  void* l;
+  for (l = item_first();  --id;  l = item_next(l));
+  return item_selected(l);
+}
+
+
+long 
+Fl_Browser_::msaa_browser_find_item(void* l)
+{
+  int line = 0;
+  for (;  l;  l = item_prev(l), ++line);
+  return line;
+}
+
+
+long
+Fl_Browser_::msaa_browser_focus()
+{
+  return msaa_browser_find_item(selection_);
+}
+
+
+void
+Fl_Browser_::msaa_browser_set_focus(long id)
+{
+  if (id < 1)  return;
+  void* l;
+  for (l = item_first();  --id;  l = item_next(l));
+  selection_ = l;
+  display(l);
+}
+
+
+bool
+Fl_Browser_::msaa_browser_do_select(long id, long flags)
+{
+  if (id < 1)  return false;
+
+  // Handle focusing of list item
+  if (flags == SELFLAG_TAKEFOCUS)
+  {
+    msaa_browser_set_focus(id);
+    return true;
+  }
+
+  // Handle single selection of list item
+  if (flags & SELFLAG_TAKESELECTION)
+  {
+    msaa_browser_set_focus(id);
+    select_only(selection_, when());
+    return true;
+  }
+
+  // The remaining operations are permitted only in multi-selection browser
+  if (type() != FL_MULTI_BROWSER)  return false;
+  bool is_extend = flags & SELFLAG_EXTENDSELECTION;
+  bool is_add = flags & SELFLAG_ADDSELECTION;
+  bool is_remove = flags & SELFLAG_REMOVESELECTION;
+
+  // Handle addition of item to selection
+  if (is_add && !is_extend)
+  {
+    msaa_browser_set_focus(id);
+    select(selection_, 1, when());
+    return true;
+  }
+
+  // Handle removal of item from selection
+  if (is_remove && !is_extend)
+  {
+    msaa_browser_set_focus(id);
+    select(selection_, 0, when());
+    return true;
+  }
+
+  // Handle extension of selection
+  if (is_extend)
+  {
+    // Determine state of extended items
+    void* old_l = selection_;
+    long  anchor;
+    bool  new_state;
+    if (!old_l)
+    {
+      old_l     = item_first();
+      anchor    = 1;
+      new_state = 1;
+    }
+    else
+    {
+      anchor = msaa_browser_focus();
+      if (is_add)          new_state = 1;
+      else if (is_remove)  new_state = 0;
+      else                 new_state = item_selected(old_l);
+    }
+
+    // Update the selection now
+    msaa_browser_set_focus(id);
+    void* new_l = selection_;
+    for (;;)
+    {
+      select(old_l, new_state, 0);
+      if (old_l == new_l)  break;
+      old_l = (anchor < id ? item_next(old_l) : item_prev(old_l));
+    }
+
+    // Invoke callback function
+    if (when())
+    {
+      set_changed();
+      do_callback();
+    }
+    return true;
+  }
+
+  return false;
+}
+#endif // FL_HAVE_MSAA
+
+
 //
 // End of "$Id$".
 //
Index: src/Fl_Value_Output.cxx
===================================================================
--- src/Fl_Value_Output.cxx	(revision 6732)
+++ src/Fl_Value_Output.cxx	(working copy)
@@ -103,6 +103,22 @@
   soft_ = 0;
 }
 
+
+#ifdef FL_HAVE_MSAA
+#include <FL/Fl_Msaa_Proxy.H>
+///////////////////////////////////////////////////////////////////////
+//
+//  The following code implements Microsoft Active Accessibility
+
+long
+Fl_Value_Output::msaa_role(long id)
+{
+  if (id != 0)  return Fl_Widget::msaa_role(id);
+  return ROLE_SYSTEM_STATICTEXT;
+}
+#endif // FL_HAVE_MSAA
+
+
 //
 // End of "$Id$".
 //
Index: src/Fl_Valuator.cxx
===================================================================
--- src/Fl_Valuator.cxx	(revision 6732)
+++ src/Fl_Valuator.cxx	(working copy)
@@ -83,6 +83,9 @@
   if (v == value_) return 0;
   value_ = v;
   value_damage();
+#ifdef FL_HAVE_MSAA
+  msaa_valuator_on_change();
+#endif // FL_HAVE_MSAA
   return 1;
 }
 /** Clamps the value, but accepts v if the previous value is not already out of range */
@@ -102,6 +105,9 @@
     value_damage();
     set_changed();
     if (when() & FL_WHEN_CHANGED) do_callback();
+#ifdef FL_HAVE_MSAA
+    msaa_valuator_on_change();
+#endif // FL_HAVE_MSAA
   }
 }
 /** Called after an FL_WHEN_RELEASE event is received and before the callback. */
@@ -115,6 +121,9 @@
     if (value_ != previous_value_ || when() & FL_WHEN_NOT_CHANGED) {
       do_callback();
     }
+#ifdef FL_HAVE_MSAA
+    if (value_ != previous_value_)  msaa_valuator_on_change();
+#endif // FL_HAVE_MSAA
   }
 }
 
@@ -191,6 +200,41 @@
   return snprintf(buffer, 128, "%.*f", c, v);
 }
 
+
+#ifdef FL_HAVE_MSAA
+#include <FL/Fl_Msaa_Proxy.H>
+///////////////////////////////////////////////////////////////////////
+//
+//  The following code implements Microsoft Active Accessibility
+
+void
+Fl_Valuator::msaa_valuator_on_change()
+{
+  msaa_notify(EVENT_OBJECT_VALUECHANGE);
+}
+
+
+bool
+Fl_Valuator::msaa_value(long id, BSTR* bstr)
+{
+  if (id != 0)  return Fl_Widget::msaa_value(id, bstr);
+  char str[128];
+  format(str);
+  fl_str_to_bstr(str, -1, bstr);
+  return true;
+}
+
+
+bool 
+Fl_Valuator::msaa_put_value(long id, const char* str)
+{
+  if (id != 0)  return Fl_Widget::msaa_put_value(id, str);
+  value(atof(str));
+  return true;
+}
+#endif // FL_HAVE_MSAA
+
+
 //
 // End of "$Id$".
 //
Index: src/Fl_win32.cxx
===================================================================
--- src/Fl_win32.cxx	(revision 6732)
+++ src/Fl_win32.cxx	(working copy)
@@ -73,6 +73,63 @@
 
 #include "aimm.h"
 
+
+#ifdef FL_HAVE_MSAA
+#  include <FL/Fl_Msaa_Proxy.H>
+
+/*
+ * Dynamic linking of oleacc.dll
+ * This library is only needed for two functions relating to MSAA
+ */
+flTypeLresultFromObject flLresultFromObject = 0;
+flTypeAccessibleObjectFromWindow flAccessibleObjectFromWindow = 0;
+
+static HMODULE s_oleacc_module = 0;
+static HMODULE get_oleacc_module() {
+  if (!s_oleacc_module) {
+    s_oleacc_module = LoadLibrary("OLEACC.DLL");
+    if (!s_oleacc_module)
+      Fl::fatal("FLTK Lib Error: OLEACC.DLL file not found!\n\n"
+        "Please check your active accessibility library availability.");
+    flLresultFromObject = (flTypeLresultFromObject)
+        GetProcAddress(s_oleacc_module, "LresultFromObject");
+    flAccessibleObjectFromWindow = (flTypeAccessibleObjectFromWindow)
+        GetProcAddress(s_oleacc_module, "AccessibleObjectFromWindow");
+  }
+  return s_oleacc_module;
+}
+
+
+/*
+ * Dynamic linking of oleaut32.dll
+ * This library is only needed for four functions relating to MSAA
+ */
+flTypeVariantCopy flVariantCopy = 0;
+flTypeVariantClear flVariantClear = 0;
+flTypeSysStringLen flSysStringLen = 0;
+flTypeSysAllocStringLen flSysAllocStringLen = 0;
+
+static HMODULE s_oleaut32_module = 0;
+static HMODULE get_oleaut32_module() {
+  if (!s_oleaut32_module) {
+    s_oleaut32_module = LoadLibrary("OLEAUT32.DLL");
+    if (!s_oleaut32_module)
+      Fl::fatal("FLTK Lib Error: OLEAUT32.DLL file not found!\n\n"
+        "Please check your OLE32 library availability.");
+    flVariantCopy = (flTypeVariantCopy)
+        GetProcAddress(s_oleaut32_module, "VariantCopy");
+    flVariantClear = (flTypeVariantClear)
+        GetProcAddress(s_oleaut32_module, "VariantClear");
+    flSysStringLen = (flTypeSysStringLen)
+        GetProcAddress(s_oleaut32_module, "SysStringLen");
+    flSysAllocStringLen = (flTypeSysAllocStringLen)
+        GetProcAddress(s_oleaut32_module, "SysAllocStringLen");
+  }
+  return s_oleaut32_module;
+}
+#endif // FL_HAVE_MSAA
+
+
 //
 // USE_ASYNC_SELECT - define it if you have WSAAsyncSelect()...
 // USE_ASYNC_SELECT is OBSOLETED in 1.3 for the following reasons:
@@ -1173,6 +1230,28 @@
     if (fl_msg.message == WM_RENDERALLFORMATS) CloseClipboard();
     return 1;}
 
+#ifdef FL_HAVE_MSAA
+  case WM_GETOBJECT: {
+    get_oleacc_module();
+    get_oleaut32_module();
+
+    Fl_Msaa_Proxy* proxy = NULL;
+    if ((DWORD)fl_msg.lParam == OBJID_CLIENT) {
+      // Return IAccessible proxy for client area
+      proxy = window->msaa_proxy();
+    } else if (fl_msg.lParam > 0) {
+      // Return IAccessible proxy for custom object IDs
+      proxy = (Fl_Msaa_Proxy*)fl_msg.lParam;
+    }
+
+    // Make sure a proxy object exists
+    if (!proxy)  break;
+
+    // Pass IAccessible proxy to MSAA
+    return flLresultFromObject(IID_IAccessible,
+	fl_msg.wParam, (IUnknown*)proxy);}
+#endif // FL_HAVE_MSAA
+
   default:
     if (Fl::handle(0,0)) return 0;
     break;
Index: src/Fl_Choice.cxx
===================================================================
--- src/Fl_Choice.cxx	(revision 6732)
+++ src/Fl_Choice.cxx	(working copy)
@@ -203,6 +203,46 @@
   }
 }
 
+
+#ifdef FL_HAVE_MSAA
+#include <FL/Fl_Msaa_Proxy.H>
+///////////////////////////////////////////////////////////////////////
+//
+//  The following code implements Microsoft Active Accessibility
+
+bool
+Fl_Choice::msaa_value(long id, BSTR* bstr)
+{
+  if (id != 0)  return Fl_Widget::msaa_value(id, bstr);
+  const Fl_Menu_Item* m = mvalue();
+  if (!m)  return false;
+
+  // For Fl_Choice, name of the selected item becomes its value
+  size_t len;
+  char* name = fl_label_to_text(m->label(), &len);
+  fl_str_to_bstr(name, len, bstr);
+  fl_free_str(name);
+  return true;
+}
+
+
+long
+Fl_Choice::msaa_role(long id)
+{
+  if (id != 0)  return Fl_Widget::msaa_role(id);
+  return ROLE_SYSTEM_COMBOBOX;
+}
+
+
+bool
+Fl_Choice::msaa_put_value(long id, const char* str)
+{
+  if (id != 0)  return Fl_Widget::msaa_put_value(id, str);
+  value(atoi(str));
+  return true;
+}
+#endif // FL_HAVE_MSAA
+
 //
 // End of "$Id$".
 //
Index: src/Fl_Group.cxx
===================================================================
--- src/Fl_Group.cxx	(revision 6732)
+++ src/Fl_Group.cxx	(working copy)
@@ -764,6 +764,180 @@
   widget.draw_label(X,Y,W,H,(Fl_Align)a);
 }
 
+
+#ifdef FL_HAVE_MSAA
+#include <FL/Fl_Msaa_Proxy.H>
+///////////////////////////////////////////////////////////////////////
+//
+//  The following code implements Microsoft Active Accessibility
+
+long
+Fl_Group::msaa_child_id(Fl_Widget* o) const
+{
+  if (o == this)  return 0;
+  int idx = find(o);
+  if (idx < children())  return idx + 1;
+  return -1;
+}
+
+
+Fl_Widget*
+Fl_Group::msaa_child_widget(long id) const
+{
+  if (id == 0)  return (Fl_Widget*)this;
+  if ((id >= 1) && (id <= children()))  return child(id - 1);
+  return NULL;
+}
+
+
+Fl_Msaa_Proxy*
+Fl_Group::msaa_child(long id)
+{
+  if (id == 0)  return Fl_Widget::msaa_child(0);
+  Fl_Widget* o = msaa_child_widget(id);
+  if (o)  return o->msaa_proxy();
+  return NULL;
+}
+
+
+bool
+Fl_Group::msaa_name(long id, BSTR* bstr)
+{
+  if (id == 0)  return Fl_Widget::msaa_name(0, bstr);
+  Fl_Widget* o = msaa_child_widget(id);
+  if (o)  return o->msaa_name(0, bstr);
+  return false;
+}
+
+
+bool
+Fl_Group::msaa_value(long id, BSTR* bstr)
+{
+  if (id == 0)  return Fl_Widget::msaa_value(0, bstr);
+  Fl_Widget* o = msaa_child_widget(id);
+  if (o)  return o->msaa_value(0, bstr);
+  return false;
+}
+
+
+bool
+Fl_Group::msaa_description(long id, BSTR* bstr)
+{
+  if (id == 0)  return Fl_Widget::msaa_description(0, bstr);
+  Fl_Widget* o = msaa_child_widget(id);
+  if (o)  return o->msaa_description(0, bstr);
+  return false;
+}
+
+
+long
+Fl_Group::msaa_role(long id)
+{
+  if (id == 0)  return ROLE_SYSTEM_GROUPING;
+  Fl_Widget* o = msaa_child_widget(id);
+  if (o)  return o->msaa_role(0);
+  return Fl_Widget::msaa_role(0);
+}
+
+
+long
+Fl_Group::msaa_state(long id)
+{
+  if (id == 0)  return Fl_Widget::msaa_state(0);
+  Fl_Widget* o = msaa_child_widget(id);
+  if (o)  return o->msaa_state(0);
+  return Fl_Widget::msaa_state(0);
+}
+
+
+bool
+Fl_Group::msaa_help(long id, BSTR* bstr)
+{
+  if (id == 0)  return Fl_Widget::msaa_help(0, bstr);
+  Fl_Widget* o = msaa_child_widget(id);
+  if (o)  return o->msaa_help(0, bstr);
+  return false;
+}
+
+
+bool
+Fl_Group::msaa_help_topic(long id, BSTR* bstr, long* num)
+{
+  if (id == 0)  return Fl_Widget::msaa_help_topic(0, bstr, num);
+  Fl_Widget* o = msaa_child_widget(id);
+  if (o)  return o->msaa_help_topic(0, bstr, num);
+  return false;
+}
+
+
+bool
+Fl_Group::msaa_shortcut(long id, BSTR* bstr)
+{
+  if (id == 0)  return Fl_Widget::msaa_shortcut(0, bstr);
+  Fl_Widget* o = msaa_child_widget(id);
+  if (o)  return o->msaa_shortcut(0, bstr);
+  return false;
+}
+
+
+long
+Fl_Group::msaa_focus()
+{
+  return msaa_child_id(Fl::focus());
+}
+
+
+bool
+Fl_Group::msaa_action(long id, BSTR* bstr)
+{
+  if (id == 0)  return Fl_Widget::msaa_action(0, bstr);
+  Fl_Widget* o = msaa_child_widget(id);
+  if (o)  return o->msaa_action(0, bstr);
+  return false;
+}
+
+
+bool
+Fl_Group::msaa_do_select(long id, long flags)
+{
+  if (id == 0)  return Fl_Widget::msaa_do_select(0, flags);
+  Fl_Widget* o = msaa_child_widget(id);
+  if (o)  return o->msaa_do_select(0, flags);
+  return false;
+}
+
+
+bool
+Fl_Group::msaa_location(long id, long* X, long* Y, long* W, long* H)
+{
+  if (id == 0)  return Fl_Widget::msaa_location(0, X, Y, W, H);
+  Fl_Widget* o = msaa_child_widget(id);
+  if (o)  return o->msaa_location(0, X, Y, W, H);
+  return false;
+}
+
+
+bool
+Fl_Group::msaa_do_action(long id)
+{
+  if (id == 0)  return Fl_Widget::msaa_do_action(0);
+  Fl_Widget* o = msaa_child_widget(id);
+  if (o)  return o->msaa_do_action(0);
+  return false;
+}
+
+
+bool
+Fl_Group::msaa_put_value(long id, const char* str)
+{
+  if (id == 0)  return Fl_Widget::msaa_put_value(0, str);
+  Fl_Widget* o = msaa_child_widget(id);
+  if (o)  return o->msaa_put_value(0, str);
+  return false;
+}
+#endif // FL_HAVE_MSAA
+
+
 //
 // End of "$Id$".
 //
Index: src/Fl.cxx
===================================================================
--- src/Fl.cxx	(revision 6732)
+++ src/Fl.cxx	(working copy)
@@ -748,6 +748,9 @@
       fl_oldfocus = p;
     }
     e_number = old_event;
+#ifdef FL_HAVE_MSAA
+    if (o)  o->msaa_on_focus();
+#endif // FL_HAVE_MSAA
   }
 }
 
Index: src/Fl_Widget.cxx
===================================================================
--- src/Fl_Widget.cxx	(revision 6732)
+++ src/Fl_Widget.cxx	(working copy)
@@ -106,6 +106,11 @@
   align_	 = FL_ALIGN_CENTER;
   when_		 = FL_WHEN_RELEASE;
 
+#ifdef FL_HAVE_MSAA
+  // Initialize MSAA stuff
+  msaa_proxy_	 = 0;
+#endif // FL_HAVE_MSAA
+
   parent_ = 0;
   if (Fl_Group::current()) Fl_Group::current()->add(this);
 }
@@ -150,6 +155,10 @@
 #endif // DEBUG_DELETE
   parent_ = 0; // Don't throw focus to a parent widget.
   fl_throw_focus(this);
+
+#ifdef FL_HAVE_MSAA
+  msaa_cleanup();
+#endif // FL_HAVE_MSAA
 }
 
 /** Draws a focus box for the widget at the given position and size */
@@ -235,6 +244,9 @@
       redraw();
       redraw_label();
       handle(FL_SHOW);
+#ifdef FL_HAVE_MSAA
+      msaa_notify(EVENT_OBJECT_SHOW);
+#endif // FL_HAVE_MSAA
       if (inside(Fl::focus())) Fl::focus()->take_focus();
     }
   }
@@ -246,6 +258,9 @@
     for (Fl_Widget *p = parent(); p; p = p->parent())
       if (p->box() || !p->parent()) {p->redraw(); break;}
     handle(FL_HIDE);
+#ifdef FL_HAVE_MSAA
+    msaa_notify(EVENT_OBJECT_HIDE);
+#endif // FL_HAVE_MSAA
     fl_throw_focus(this);
   } else {
     set_flag(INVISIBLE);
@@ -277,6 +292,9 @@
   }
   label_.value=a;
   redraw_label();
+#ifdef FL_HAVE_MSAA
+  msaa_notify(EVENT_OBJECT_NAMECHANGE);
+#endif // FL_HAVE_MSAA
 }
 
 
@@ -291,6 +309,9 @@
     label_.value=(char *)0;
   }
   redraw_label();
+#ifdef FL_HAVE_MSAA
+  msaa_notify(EVENT_OBJECT_NAMECHANGE);
+#endif // FL_HAVE_MSAA
 }
 
 /** Calls the widget callback.
@@ -310,6 +331,1205 @@
     clear_changed();
 }
 
+
+#ifdef FL_HAVE_MSAA
+#include <FL/Fl_Msaa_Proxy.H>
+#include <FL/Fl_Multi_Label.H>
+#include <FL/x.H>
+///////////////////////////////////////////////////////////////////////
+//
+//  The following code implements Microsoft Active Accessibility
+
+/*----------------------- IEnumVARIANT implementation ----------------------*/
+
+class Fl_Msaa_Enum : public IEnumVARIANT
+{
+private:
+  LONG		mRefCount;	// Reference counter
+  unsigned long mPos;		// Current read offset in array
+  unsigned long mSize;		// Number of items in array
+  VARIANT*      mpItems;	// Storage for items
+
+  Fl_Msaa_Enum(Fl_Msaa_Enum& src);
+  void InitObject(size_t size);
+
+public:
+  Fl_Msaa_Enum(long* ids, size_t count);
+  virtual ~Fl_Msaa_Enum();
+
+  // IUnknown implementation
+  HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void**);
+  ULONG STDMETHODCALLTYPE AddRef();
+  ULONG STDMETHODCALLTYPE Release();
+
+  // IEnumVARIANT implementation
+  HRESULT STDMETHODCALLTYPE Next(unsigned long, VARIANT*, unsigned long*);
+  HRESULT STDMETHODCALLTYPE Skip(unsigned long);
+  HRESULT STDMETHODCALLTYPE Reset();
+  HRESULT STDMETHODCALLTYPE Clone(IEnumVARIANT**);
+};
+
+
+void
+Fl_Msaa_Enum::InitObject(size_t size)
+{
+  mRefCount = 0;
+  mPos      = 0;
+  mSize     = size;
+  mpItems   = size ? (VARIANT*)malloc(sizeof(VARIANT) * size) : NULL;
+  for (VARIANT* ptr = mpItems;  size--;  ++ptr)  flVariantClear(ptr);
+}
+
+
+Fl_Msaa_Enum::Fl_Msaa_Enum(Fl_Msaa_Enum& src)
+{
+  InitObject(src.mSize);
+
+  // Perform full copy
+  VARIANT* sptr = src.mpItems;
+  VARIANT* dptr = mpItems;
+  mPos = src.mPos;
+  for (mSize = 0;  mSize < src.mSize;  ++mSize, ++sptr, ++dptr)
+    flVariantCopy(dptr, sptr);
+}
+
+
+Fl_Msaa_Enum::Fl_Msaa_Enum(long* ids, size_t count)
+{
+  InitObject(count);
+
+  // Setup an array of VT_I4
+  for (VARIANT* ptr = mpItems;  count--;  ++ptr, ++ids)
+  {
+    ptr->vt   = VT_I4;
+    ptr->lVal = *ids;
+  }
+}
+
+
+Fl_Msaa_Enum::~Fl_Msaa_Enum()
+{
+  if (!mpItems)  return;
+  for (VARIANT* ptr = mpItems;  mSize--;  ++ptr)  flVariantClear(ptr);
+  free(mpItems);
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Enum::QueryInterface(
+    /* [in] */ REFIID riid,
+    /* [iid_is][out] */ void** ppvObject)
+{
+  // Reject unsupported interfaces
+  if ((riid == IID_IEnumVARIANT) ||
+      (riid == IID_IUnknown))
+  {
+    // Add reference for supported interfaces
+    *ppvObject = this;
+    ((LPUNKNOWN)*ppvObject)->AddRef();
+    return S_OK;
+  }
+
+  *ppvObject = NULL;
+  return E_NOINTERFACE;
+}
+
+
+ULONG STDMETHODCALLTYPE
+Fl_Msaa_Enum::AddRef()
+{
+  return InterlockedIncrement(&mRefCount);
+}
+
+
+ULONG STDMETHODCALLTYPE
+Fl_Msaa_Enum::Release()
+{
+  ULONG count = InterlockedDecrement(&mRefCount);
+  if (count == 0)  delete this;
+  return count;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Enum::Next(unsigned long count, VARIANT* pvar, unsigned long* pFetched)
+{
+  if (!pvar)  return E_INVALIDARG;
+
+  // Determine final read offset and number of items fetched
+  unsigned long end = mPos + count;
+  if (end > mSize)  end = mSize;
+  unsigned long fetched = end - mPos;
+
+  // Perform actual copy
+  VARIANT* dst = pvar;
+  for (VARIANT* ptr = mpItems + mPos;  mPos < end;  ++ptr, ++mPos, ++dst)
+  {
+    HRESULT result = flVariantCopy(dst, ptr);
+    if (S_OK == result)  continue;
+
+    // Encountered error, clean up partial copy and exit
+    for (dst = pvar;  count--;  ++dst)  flVariantClear(dst);
+    return result;
+  }
+
+  // Report result to caller
+  if (pFetched)  *pFetched = fetched;
+  return (fetched == count) ? S_OK : S_FALSE;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Enum::Skip(unsigned long count)
+{
+  mPos += count;
+  if (mPos > mSize)
+  {
+    // Report failure when skipping outside array
+    mPos = mSize;
+    return S_FALSE;
+  }
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Enum::Reset()
+{
+  mPos = 0;
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Enum::Clone(IEnumVARIANT** ppvar)
+{
+  // Check for valid destination variable
+  if (!ppvar)  return E_INVALIDARG;
+
+  // Copy self and Check for out of memory condition
+  *ppvar = new Fl_Msaa_Enum(*this);
+  if (!*ppvar)  return E_OUTOFMEMORY;
+
+  // Increase reference count
+  (*ppvar)->AddRef();
+  return S_OK;
+}
+
+
+/*--------------------- MSAA proxy for Fl_Widget object --------------------*/
+
+Fl_Msaa_Proxy::~Fl_Msaa_Proxy()
+{
+  OverrideParent(NULL);
+}
+
+
+void
+Fl_Msaa_Proxy::OverrideParent(Fl_Msaa_Proxy* parent)
+{
+  if (parent)  parent->AddRef();
+  if (mpParent)  mpParent->Release();
+  mpParent = parent;
+}
+
+
+long
+Fl_Msaa_Proxy::LookupChild(Fl_Widget* o)
+{
+  if (!mpObj)  return -1;
+  return mpObj->msaa_child_id(o);
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Proxy::QueryInterface(
+    /* [in] */ REFIID riid,
+    /* [iid_is][out] */ void** ppvObject)
+{
+  // Reject unsupported interfaces
+  if ((riid == IID_IAccessible) ||
+      (riid == IID_IDispatch) ||
+      (riid == IID_IUnknown))
+  {
+    // Add reference for supported interfaces
+    *ppvObject = this;
+    ((LPUNKNOWN)*ppvObject)->AddRef();
+    return S_OK;
+  }
+
+  *ppvObject = NULL;
+  return E_NOINTERFACE;
+}
+
+
+ULONG STDMETHODCALLTYPE
+Fl_Msaa_Proxy::AddRef()
+{
+  return InterlockedIncrement(&mRefCount);
+}
+
+
+ULONG STDMETHODCALLTYPE
+Fl_Msaa_Proxy::Release()
+{
+  ULONG count = InterlockedDecrement(&mRefCount);
+  if (count == 0)  delete this;
+  return count;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Proxy::GetTypeInfoCount(
+    /* [out] */ UINT* pctinfo)
+{
+  *pctinfo = 0;
+  return E_NOTIMPL;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Proxy::GetTypeInfo(
+    /* [in] */ UINT iTInfo,
+    /* [in] */ LCID lcid,
+    /* [out] */ ITypeInfo** ppTInfo)
+{
+  *ppTInfo = 0;
+  return E_NOTIMPL;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Proxy::GetIDsOfNames(
+    /* [in] */ REFIID riid,
+    /* [size_is][in] */ LPOLESTR* rgszNames,
+    /* [in] */ UINT cNames,
+    /* [in] */ LCID lcid,
+    /* [size_is][out] */ DISPID* rgDispId)
+{
+  *rgDispId = 0;
+  return E_NOTIMPL;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Proxy::Invoke(
+    /* [in] */ DISPID dispIdMember,
+    /* [in] */ REFIID riid,
+    /* [in] */ LCID lcid,
+    /* [in] */ WORD wFlags,
+    /* [out][in] */ DISPPARAMS* pDispParams,
+    /* [out] */ VARIANT* pVarResult,
+    /* [out] */ EXCEPINFO* pExcepInfo,
+    /* [out] */ UINT* puArgErr)
+{
+  *puArgErr = 0;
+  return E_NOTIMPL;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Proxy::get_accParent(
+    /* [retval][out] */ IDispatch** ppdispParent)
+{
+  if (!mpObj)  return CO_E_OBJNOTCONNECTED;
+  if (!ppdispParent)  return E_INVALIDARG;
+  *ppdispParent = NULL;
+
+  if (mpParent)
+  {
+    mpParent->QueryInterface(IID_IDispatch, (void**)ppdispParent);
+    return S_OK;
+  }
+  if (!mpObj->msaa_parent(ppdispParent))  return S_FALSE;
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Proxy::get_accChildCount(
+    /* [retval][out] */ long* pcountChildren)
+{
+  if (!mpObj)  return CO_E_OBJNOTCONNECTED;
+  if (!pcountChildren)  return E_INVALIDARG;
+
+  *pcountChildren = mpObj->msaa_child_count();
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Proxy::get_accChild(
+    /* [in] */ VARIANT varChild,
+    /* [retval][out] */ IDispatch** ppdispChild)
+{
+  if (!mpObj)  return CO_E_OBJNOTCONNECTED;
+  if (varChild.vt != VT_I4)  return E_INVALIDARG;
+  if (!ppdispChild)  return E_INVALIDARG;
+  *ppdispChild = NULL;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > mpObj->msaa_child_count())  return E_INVALIDARG;
+
+  Fl_Msaa_Proxy* proxy = mpObj->msaa_child(id);
+  if (!proxy)  return S_FALSE;
+  proxy->QueryInterface(IID_IDispatch, (void**)ppdispChild);
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Proxy::get_accName(
+    /* [optional][in] */ VARIANT varChild,
+    /* [retval][out] */ BSTR* pszName)
+{
+  if (!mpObj)  return CO_E_OBJNOTCONNECTED;
+  if (!pszName)  return E_INVALIDARG;
+  *pszName = NULL;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > mpObj->msaa_child_count())  return E_INVALIDARG;
+
+  if (!mpObj->msaa_name(id, pszName))  return DISP_E_MEMBERNOTFOUND;
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Proxy::get_accValue(
+    /* [optional][in] */ VARIANT varChild,
+    /* [retval][out] */ BSTR* pszValue)
+{
+  if (!mpObj)  return CO_E_OBJNOTCONNECTED;
+  if (!pszValue)  return E_INVALIDARG;
+  *pszValue = NULL;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > mpObj->msaa_child_count())  return E_INVALIDARG;
+
+  if (!mpObj->msaa_value(id, pszValue))  return DISP_E_MEMBERNOTFOUND;
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Proxy::get_accDescription(
+    /* [optional][in] */ VARIANT varChild,
+    /* [retval][out] */ BSTR* pszDescription)
+{
+  if (!mpObj)  return CO_E_OBJNOTCONNECTED;
+  if (!pszDescription)  return E_INVALIDARG;
+  *pszDescription = NULL;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > mpObj->msaa_child_count())  return E_INVALIDARG;
+
+  if (!mpObj->msaa_description(id, pszDescription))
+    return DISP_E_MEMBERNOTFOUND;
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Proxy::get_accRole(
+    /* [optional][in] */ VARIANT varChild,
+    /* [retval][out] */ VARIANT* pvarRole)
+{
+  if (!mpObj)  return CO_E_OBJNOTCONNECTED;
+  if (!pvarRole)  return E_INVALIDARG;
+  pvarRole->vt = VT_EMPTY;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > mpObj->msaa_child_count())  return E_INVALIDARG;
+
+  pvarRole->vt   = VT_I4;
+  pvarRole->lVal = mpObj->msaa_role(id);
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Proxy::get_accState(
+    /* [optional][in] */ VARIANT varChild,
+    /* [retval][out] */ VARIANT* pvarState)
+{
+  if (!mpObj)  return CO_E_OBJNOTCONNECTED;
+  if (!pvarState)  return E_INVALIDARG;
+  pvarState->vt = VT_EMPTY;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > mpObj->msaa_child_count())  return E_INVALIDARG;
+
+  pvarState->vt   = VT_I4;
+  pvarState->lVal = mpObj->msaa_state(id);
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Proxy::get_accHelp(
+    /* [optional][in] */ VARIANT varChild,
+    /* [retval][out] */ BSTR* pszHelp)
+{
+  if (!mpObj)  return CO_E_OBJNOTCONNECTED;
+  if (!pszHelp)  return E_INVALIDARG;
+  *pszHelp = NULL;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > mpObj->msaa_child_count())  return E_INVALIDARG;
+
+  if (!mpObj->msaa_help(id, pszHelp))  return DISP_E_MEMBERNOTFOUND;
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Proxy::get_accHelpTopic(
+    /* [out] */ BSTR* pszHelpFile,
+    /* [optional][in] */ VARIANT varChild,
+    /* [retval][out] */ long* pidTopic)
+{
+  if (!mpObj)  return CO_E_OBJNOTCONNECTED;
+  if (!pszHelpFile)  return E_INVALIDARG;
+  if (!pidTopic)  return E_INVALIDARG;
+  *pszHelpFile = NULL;
+  *pidTopic = 0;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > mpObj->msaa_child_count())  return E_INVALIDARG;
+
+  if (!mpObj->msaa_help_topic(id, pszHelpFile, pidTopic))
+    return DISP_E_MEMBERNOTFOUND;
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Proxy::get_accKeyboardShortcut(
+    /* [optional][in] */ VARIANT varChild,
+    /* [retval][out] */ BSTR* pszKeyboardShortcut)
+{
+  if (!mpObj)  return CO_E_OBJNOTCONNECTED;
+  if (!pszKeyboardShortcut)  return E_INVALIDARG;
+  *pszKeyboardShortcut = NULL;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > mpObj->msaa_child_count())  return E_INVALIDARG;
+
+  if (!mpObj->msaa_shortcut(id, pszKeyboardShortcut))
+    return DISP_E_MEMBERNOTFOUND;
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Proxy::get_accFocus(
+    /* [retval][out] */ VARIANT *pvarChild)
+{
+  if (!mpObj)  return CO_E_OBJNOTCONNECTED;
+  if (!pvarChild)  return E_INVALIDARG;
+  pvarChild->vt = VT_EMPTY;
+
+  long id = mpObj->msaa_focus();
+  if (id < 0)  return S_OK;
+
+  // Try to obtain child proxy
+  if (id > 0)
+  {
+    Fl_Msaa_Proxy* proxy = mpObj->msaa_child(id);
+    if (proxy)
+    {
+      pvarChild->vt = VT_DISPATCH;
+      proxy->QueryInterface(IID_IDispatch, (void**)&pvarChild->pdispVal);
+      return S_OK;
+    }
+  }
+
+  // Otherwise return child ID
+  pvarChild->vt   = VT_I4;
+  pvarChild->lVal = id;
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Proxy::get_accSelection(
+    /* [retval][out] */ VARIANT* pvarChildren)
+{
+  if (!mpObj)  return CO_E_OBJNOTCONNECTED;
+  if (!pvarChildren)  return E_INVALIDARG;
+
+  // Put selection inside VARIANT
+  mpObj->msaa_selection(pvarChildren);
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Proxy::get_accDefaultAction(
+    /* [optional][in] */ VARIANT varChild,
+    /* [retval][out] */ BSTR* pszDefaultAction)
+{
+  if (!mpObj)  return CO_E_OBJNOTCONNECTED;
+  if (!pszDefaultAction)  return E_INVALIDARG;
+  *pszDefaultAction = NULL;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > mpObj->msaa_child_count())  return E_INVALIDARG;
+
+  if (!mpObj->msaa_action(id, pszDefaultAction))
+    return DISP_E_MEMBERNOTFOUND;
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Proxy::accSelect(
+    /* [in] */ long flagsSelect,
+    /* [optional][in] */ VARIANT varChild)
+{
+  if (!mpObj)  return CO_E_OBJNOTCONNECTED;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > mpObj->msaa_child_count())  return E_INVALIDARG;
+
+  if (flagsSelect == SELFLAG_NONE)  return S_OK;
+  if (!mpObj->msaa_do_select(id, flagsSelect))  return DISP_E_MEMBERNOTFOUND;
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Proxy::accLocation(
+    /* [out] */ long* pxLeft,
+    /* [out] */ long* pyTop,
+    /* [out] */ long* pcxWidth,
+    /* [out] */ long* pcyHeight,
+    /* [optional][in] */ VARIANT varChild)
+{
+  if (!mpObj)  return CO_E_OBJNOTCONNECTED;
+  if (!pxLeft || !pyTop || !pcxWidth || !pcyHeight)  return E_INVALIDARG;
+  *pxLeft    = 0;
+  *pyTop     = 0;
+  *pcxWidth  = 0;
+  *pcyHeight = 0;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > mpObj->msaa_child_count())  return E_INVALIDARG;
+
+  if (!mpObj->msaa_location(id, pxLeft, pyTop, pcxWidth, pcyHeight))
+    return DISP_E_MEMBERNOTFOUND;
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Proxy::accNavigate(
+    /* [in] */ long navDir,
+    /* [optional][in] */ VARIANT varStart,
+    /* [retval][out] */ VARIANT* pvarEndUpAt)
+{
+  if (!mpObj)  return CO_E_OBJNOTCONNECTED;
+  if (!pvarEndUpAt)  return E_INVALIDARG;
+  pvarEndUpAt->vt = VT_EMPTY;
+  long id = (varStart.vt == VT_I4 ? varStart.lVal : 0);
+  if (id < 0 || id > mpObj->msaa_child_count())  return E_INVALIDARG;
+  if ((id != 0) &&
+      ((navDir == NAVDIR_FIRSTCHILD) || (navDir == NAVDIR_LASTCHILD)))
+    return DISP_E_MEMBERNOTFOUND;
+
+  // Handle navigation relative to widget itself
+  if ((id == 0) &&
+      (navDir != NAVDIR_FIRSTCHILD) && (navDir != NAVDIR_LASTCHILD))
+  {
+    // Forward request to parent widget
+    Fl_Msaa_Proxy* proxy;
+    if (mpParent)  proxy = mpParent;
+    else
+    {
+      Fl_Group* parent_obj = mpObj->parent();
+      if (!parent_obj)  return DISP_E_MEMBERNOTFOUND;
+      proxy = parent_obj->msaa_as_parent();
+    }
+    if (!proxy)  return DISP_E_MEMBERNOTFOUND;
+    VARIANT self;
+    self.vt   = VT_I4;
+    self.lVal = proxy->LookupChild(mpObj);
+    if (self.lVal < 1)  return DISP_E_MEMBERNOTFOUND;
+    return proxy->accNavigate(navDir, self, pvarEndUpAt);
+  }
+
+  // Handle navigation between child items
+  long dst = mpObj->msaa_navigate(id, navDir);
+  if (dst < 0)  return DISP_E_MEMBERNOTFOUND;
+  Fl_Msaa_Proxy* proxy = mpObj->msaa_child(dst);
+  if (!proxy)
+  {
+    pvarEndUpAt->vt   = VT_I4;
+    pvarEndUpAt->lVal = dst;
+  }
+  else
+  {
+    pvarEndUpAt->vt = VT_DISPATCH;
+    proxy->QueryInterface(IID_IDispatch, (void**)&pvarEndUpAt->pdispVal);
+  }
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Proxy::accHitTest(
+    /* [in] */ long xLeft,
+    /* [in] */ long yTop,
+    /* [retval][out] */ VARIANT* pvarChild)
+{
+  if (!mpObj)  return CO_E_OBJNOTCONNECTED;
+  if (!pvarChild)  return E_INVALIDARG;
+  pvarChild->vt = VT_EMPTY;
+
+  // Make sure the point is inside this widget
+  long X, Y, W, H;
+  if (!mpObj->msaa_location(0, &X, &Y, &W, &H))  return S_FALSE;
+  if ((xLeft < X) || (xLeft >= (X + W)))  return S_FALSE;
+  if ((yTop < Y) || (yTop >= (Y + H)))  return S_FALSE;
+
+  // Check child elements from top to bottom
+  VARIANT self;
+  self.vt   = VT_I4;
+  self.lVal = 0;
+  for (int n = mpObj->msaa_child_count();  n > 0;  --n)
+  {
+    // Obtain location through proxy first, but fall back to function call
+    Fl_Msaa_Proxy* proxy = mpObj->msaa_child(n);
+    if (proxy && (proxy->accLocation(&X, &Y, &W, &H, self) != S_OK))
+      continue;
+    else if (!proxy && !mpObj->msaa_location(n, &X, &Y, &W, &H))
+      continue;
+
+    // Make sure point is inside child object
+    if ((xLeft < X) || (xLeft >= (X + W)))  continue;
+    if ((yTop < Y) || (yTop >= (Y + H)))  continue;
+
+    // Setup result
+    if (proxy)
+    {
+      pvarChild->vt = VT_DISPATCH;
+      proxy->QueryInterface(IID_IDispatch, (void**)&pvarChild->pdispVal);
+    }
+    else
+    {
+      pvarChild->vt   = VT_I4;
+      pvarChild->lVal = n;
+    }
+    return S_OK;
+  }
+
+  pvarChild->vt   = VT_I4;
+  pvarChild->lVal = 0;
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Proxy::accDoDefaultAction(
+    /* [optional][in] */ VARIANT varChild)
+{
+  if (!mpObj)  return CO_E_OBJNOTCONNECTED;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > mpObj->msaa_child_count())  return E_INVALIDARG;
+
+  if (!mpObj->msaa_do_action(id))  return DISP_E_MEMBERNOTFOUND;
+  return S_OK;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Proxy::put_accName(
+    /* [optional][in] */ VARIANT varChild,
+    /* [in] */ BSTR szName)
+{
+  if (!mpObj)  return CO_E_OBJNOTCONNECTED;
+  return E_NOTIMPL;
+}
+
+
+HRESULT STDMETHODCALLTYPE
+Fl_Msaa_Proxy::put_accValue(
+    /* [optional][in] */ VARIANT varChild,
+    /* [in] */ BSTR szValue)
+{
+  if (!mpObj)  return CO_E_OBJNOTCONNECTED;
+  long id = (varChild.vt == VT_I4 ? varChild.lVal : 0);
+  if (id < 0 || id > mpObj->msaa_child_count())  return E_INVALIDARG;
+
+  char* value = fl_bstr_to_str(szValue);
+  bool ok = mpObj->msaa_put_value(id, value);
+  fl_free_str(value);
+  return (ok ? S_OK : DISP_E_MEMBERNOTFOUND);
+}
+
+
+/*-------------------------- BSTR utility functions -----------------------*/
+
+void
+fl_str_to_bstr(const char* str, int len, BSTR* bstr)
+{
+  if (!str)  str = "";
+  if (len < 0)  len = strlen(str);
+  DWORD blen = MultiByteToWideChar(CP_UTF8, 0, str, len, 0, 0);
+  *bstr = flSysAllocStringLen(0, blen);
+  if (*bstr)  MultiByteToWideChar(CP_UTF8, 0, str, len, *bstr, blen);
+}
+
+
+char*
+fl_bstr_to_str(const BSTR bstr, size_t* pLen)
+{
+  DWORD blen = flSysStringLen(bstr);
+  int len;
+  if (blen)  len = WideCharToMultiByte(CP_UTF8, 0, bstr, blen, 0, 0, 0, 0);
+  else       len = 0;
+  CHAR* str = (CHAR*)malloc(len + 1); 
+  if (str)
+  {
+    if (len)  WideCharToMultiByte(CP_UTF8, 0, bstr, blen, str, len, 0, 0);
+    str[len] = '\0';
+  }
+  if (pLen)  *pLen = len;
+  return (char*)str;
+}
+
+
+static const char*
+fl_symbol_to_text(const char* symbol)
+{
+  if (*symbol != '@')  return symbol;
+
+  const char* p = symbol + 1;
+  if (*p == '@')  return p;
+  if (*p == '#')  ++p;
+  if ((*p == '-' || *p == '+') && (p[1] >= '1' || p[1] <= '9'))  p += 2;
+
+  bool flip_x = false;
+  if (*p == '$')
+  {
+    flip_x = true;
+    ++p;
+  }
+
+  bool flip_y = false;
+  if (*p == '%')
+  {
+    flip_y = true;
+    ++p;
+  }
+
+  int angle = 0;
+  switch (*p++)
+  {
+    case '0':
+      angle = 100*(p[1]-'0') + 10*(p[2]-'0') + (p[3]-'0');
+      if (angle > 360)  angle %= 360; 
+      angle = angle - (angle % 45);
+      p += 3;
+      break;
+    case '1':  angle = 225;  break;
+    case '2':  angle = 270;  break;
+    case '3':  angle = 315;  break;
+    case '4':  angle = 180;  break;
+    case '5':  break;
+    case '6':  break;
+    case '7':  angle = 135;  break;
+    case '8':  angle =  90;  break;
+    case '9':  angle =  45;  break;
+    default:   p--;  break;
+  }
+
+  // Handle special symbols
+  const char* special[] =
+  {
+    "search",		"Search",
+    "FLTK",		"FLTK",
+    "filenew",		"New File",
+    "fileopen",		"Open File",
+    "filesave",		"Save",
+    "filesaveas",	"Save As",
+    "fileprint",	"Print",
+    "refresh",		"Refresh",
+    "reload",		"Reload",
+    "undo",		"Undo",
+    "redo",		"Redo",
+    NULL
+  };
+  for (const char** ptr = special;  *ptr;  ptr += 2)
+  {
+    size_t len = strlen(ptr[0]);
+    if (strncmp(p, ptr[0], len))  continue;
+    if (p[len] && !isspace(p[len]))  continue;
+    return ptr[1];
+  }
+
+  // Treat left-arrows as arrows rotated by 180 degrees
+  const char* left_arrows[] = {"<-", "<", "<<", "|<", "[]<", "<|", NULL};
+  for (const char** ptr = left_arrows;  *ptr;  ++ptr)
+  {
+    if (0 == strncmp(p, *ptr, strlen(*ptr)))
+    {
+      angle = (180 + angle) % 360;
+      break;
+    }
+  }
+
+  // Adjust angle due to flipping
+  if (flip_x)  angle = (540 - angle) % 360;
+  if (flip_y)  angle = (360 - angle) % 360;
+
+  // Obtain name based on angle now
+  switch (angle)
+  {
+    case   0:  return "Right";
+    case  45:  return "Top Right";
+    case  90:  return "Up";
+    case 135:  return "Top Left";
+    case 180:  return "Left";
+    case 225:  return "Bottom Left";
+    case 270:  return "Down";
+    case 315:  return "Bottom Right";
+    default:   return symbol;
+  }
+}
+
+
+char*
+fl_label_to_text(const char* label, size_t* pLen)
+{
+  if (!label)  label = "";
+  char* text = (char*)malloc(strlen(label) + 1);
+  char* ptr = text;
+  bool  escaped = false;
+  for (;  *label;  label++)
+  {
+    if (!escaped && (*label == '&'))  escaped = true;
+    else
+    {
+      *ptr++ = *label;
+      escaped = false;
+    }
+  }
+  *ptr = '\0';
+  if (pLen)  *pLen = ptr - text;
+  return text;
+}
+
+
+char*
+fl_label_to_shortcut(const char* label, size_t* pLen)
+{
+  if (!label)  label = "";
+  if (*label == '@')
+  {
+    // Skip up to spacing character
+    while (*label && !isspace(*label))  ++label;
+  }
+  char* shortcut = (char*)malloc(2);
+  char* ptr = shortcut;
+  bool  escaped = false;
+  for (;  *label;  label++)
+  {
+    if (escaped)
+    {
+      *ptr++ = tolower(*label);
+      break;
+    }
+    else if (*label == '&')  escaped = true;
+  }
+  *ptr = '\0';
+  if (pLen)  *pLen = ptr - shortcut;
+  return shortcut;
+}
+
+
+const char*
+fl_label_string(int l_type, const char* l_text)
+{
+  switch (l_type)
+  {
+    case FL_NORMAL_LABEL:
+    case _FL_SHADOW_LABEL:
+    case _FL_ENGRAVED_LABEL:
+    case _FL_EMBOSSED_LABEL:
+      // All other label types have no textual name
+      return l_text;
+
+    case _FL_MULTI_LABEL:
+      // Search for text in multi label recursively
+      {
+	Fl_Multi_Label* multi = (Fl_Multi_Label*)l_text;
+	const char* l = fl_label_string(multi->typea, multi->labela);
+	if (l)  return l;
+	else  return fl_label_string(multi->typeb, multi->labelb);
+      }
+
+    default:
+      // All other label types have no textual name
+      return NULL;
+  }
+}
+
+
+void
+fl_free_str(char* str)
+{
+  free(str);
+}
+
+
+/*------------------------- Fl_Widget MSAA support -------------------------*/
+
+void
+Fl_Widget::msaa_show_caret(long X, long Y, long W, long H)
+{
+  // Show caret in parent window
+  const Fl_Window* wnd = msaa_window();
+  if (wnd)  wnd->msaa_set_caret(X, Y, W, H);
+}
+
+
+void
+Fl_Widget::msaa_hide_caret()
+{
+  // Show caret in parent window
+  const Fl_Window* wnd = msaa_window();
+  if (wnd)  wnd->msaa_set_caret(0, 0, 0, 0);
+}
+
+
+void
+Fl_Widget::msaa_convert_selection(long* ids, size_t count, VARIANT* pvar)
+{
+  if (!pvar)  return;
+
+  if (!ids || !count)
+  {
+    // Empty selection
+    pvar->vt = VT_EMPTY;
+  }
+  else if (count == 1)
+  {
+    // Convert single selection as simple element
+    pvar->vt   = VT_I4;
+    pvar->lVal = ids[0];
+  }
+  else
+  {
+    // Convert multi-selection to IEnumVARIANT
+    Fl_Msaa_Enum* enumvar = new Fl_Msaa_Enum(ids, count);
+    if (!enumvar)  return;
+    pvar->vt = VT_UNKNOWN;
+    enumvar->QueryInterface(IID_IUnknown, (void**)&pvar->punkVal);
+  }
+}
+
+
+void
+Fl_Widget::msaa_cleanup()
+{
+  // Detach MSAA proxy object and release reference
+  if (msaa_proxy_)
+  {
+    msaa_proxy_->Detach();
+    msaa_proxy_->Release();
+  }
+}
+
+
+bool
+Fl_Widget::msaa_parent(IDispatch** dispatch)
+{
+  Fl_Group* p = parent();
+  if (p)
+  {
+    // Traversing inside FLTK hierarchy
+    Fl_Msaa_Proxy* proxy = p->msaa_as_parent();
+    if (!proxy)  return false;
+    proxy->QueryInterface(IID_IDispatch, (void**)dispatch);
+    return true;
+  }
+  else
+  {
+    // Moving outside FLTK hierarchy
+    const Fl_Window* wnd = msaa_window();
+    if (!wnd)  return false;
+    HWND hwnd = fl_xid(wnd);
+    if (!hwnd)  return false;
+    return S_OK == flAccessibleObjectFromWindow(hwnd, OBJID_WINDOW,
+	IID_IDispatch, (void**)dispatch);
+  }
+}
+
+
+Fl_Msaa_Proxy*
+Fl_Widget::msaa_child(long id)
+{
+  if (id > 0)  return NULL;
+
+  // Allow some widgets to be represented as simple elements
+  if (!msaa_use_proxy())  return NULL;
+
+  if (!msaa_proxy_)
+  {
+    // Link an IAccessible proxy to this widget
+    msaa_proxy_ = new Fl_Msaa_Proxy(this);
+    if (msaa_proxy_)  msaa_proxy_->AddRef();
+  }
+  return msaa_proxy_;
+}
+
+
+bool
+Fl_Widget::msaa_name(long id, BSTR* bstr)
+{
+  const char* l = fl_label_string(label_.type, label_.value);
+  if (!l)  l = "";
+  if (*l == '@')
+  {
+    // Handle symbol strings
+    fl_str_to_bstr(fl_symbol_to_text(l), -1, bstr);
+    return true;
+  }
+  size_t len;
+  char* str = fl_label_to_text(l, &len);
+  fl_str_to_bstr(str, len, bstr);
+  fl_free_str(str);
+  return true;
+}
+
+
+long
+Fl_Widget::msaa_role(long id)
+{
+  return ROLE_SYSTEM_STATICTEXT;
+}
+
+
+long
+Fl_Widget::msaa_state(long id)
+{
+  if (id != 0)  return 0;
+  return
+    (msaa_visible() ? 0 : STATE_SYSTEM_INVISIBLE) |
+    (active_r() ? 0 : STATE_SYSTEM_UNAVAILABLE) |
+    (!msaa_focusable() ? 0 : STATE_SYSTEM_FOCUSABLE) |
+    (Fl::focus() != this ? 0 : STATE_SYSTEM_FOCUSED);
+}
+
+
+bool
+Fl_Widget::msaa_help(long id, BSTR* bstr)
+{
+  if (id != 0)  return false;
+  fl_str_to_bstr(tooltip(), -1, bstr);
+  return true;
+}
+
+
+bool
+Fl_Widget::msaa_shortcut(long id, BSTR* bstr)
+{
+  const char* l = fl_label_string(label_.type, label_.value);
+  size_t len;
+  char* str = fl_label_to_shortcut(l, &len);
+  if (len)  fl_str_to_bstr(str, len, bstr);
+  fl_free_str(str);
+  return len > 0;
+}
+
+
+long
+Fl_Widget::msaa_focus()
+{
+  return (Fl::focus() == this ? 0 : -1);
+}
+
+
+bool
+Fl_Widget::msaa_do_select(long id, long flags)
+{
+  if (id > 0)  return false;
+  if (flags && SELFLAG_TAKEFOCUS)
+  {
+    take_focus();
+    return true;
+  }
+  return false;
+}
+
+
+bool
+Fl_Widget::msaa_location(long id, long* X, long* Y, long* W, long* H)
+{
+  if (id > 0)  return false;
+  if (!msaa_visible())  return false;
+
+  // Convert to screen co-ordinates
+  const Fl_Window* wnd = msaa_window();
+  *X = x();
+  *Y = y();
+  *W = w();
+  *H = h();
+  if (this != wnd)
+  {
+    // Normal widget location is relative to parent window
+    *X += wnd->x_root();
+    *Y += wnd->y_root();
+  }
+  else
+  {
+    const Fl_Window* parent_wnd = window();
+    if (parent_wnd)
+    {
+      // Sub-window location is also relative to parent window
+      *X += parent_wnd->x();
+      *Y += parent_wnd->y();
+    }
+  }
+  return true;
+}
+
+
+long
+Fl_Widget::msaa_navigate(long id, long dir)
+{
+  // Handle navigation within child widget
+  long max_id = msaa_child_count();
+  if (max_id < 1)  return -1;
+  switch (dir)
+  {
+    case NAVDIR_FIRSTCHILD:	return 1;
+    case NAVDIR_LASTCHILD:	return max_id;
+    case NAVDIR_PREVIOUS:	return (id == 1 ? -1 : (id - 1));
+    case NAVDIR_NEXT:		return (id == max_id ? -1 : (id + 1));
+
+    // Spatial navigation not implemented
+    case NAVDIR_UP:		// Fall through
+    case NAVDIR_DOWN:		// Fall through
+    case NAVDIR_LEFT:		// Fall through
+    case NAVDIR_RIGHT:		// Fall through
+    default:			return -1;
+  }
+}
+
+
+void
+Fl_Widget::msaa_notify(DWORD event, long childId)
+{
+  // Use proxy object pointer as object ID 
+  Fl_Msaa_Proxy* proxy = msaa_proxy();
+  if (!proxy)  return;
+  const Fl_Window* wnd = msaa_window();
+  if (!wnd)  return;
+  HWND hwnd = fl_xid(wnd);
+  if (!hwnd)  return;
+  NotifyWinEvent(event, hwnd, (LONG)proxy, childId);
+}
+
+
+void
+Fl_Widget::msaa_on_focus()
+{
+  msaa_notify(EVENT_OBJECT_FOCUS);
+}
+#endif // FL_HAVE_MSAA
+
+
 //
 // End of "$Id$".
 //
Index: FL/Fl_Value_Slider.H
===================================================================
--- FL/Fl_Value_Slider.H	(revision 6732)
+++ FL/Fl_Value_Slider.H	(working copy)
@@ -60,6 +60,12 @@
     Fl_Color textcolor() const {return (Fl_Color)textcolor_;}
     /**    Sets the color of the text in the value box.  */
     void textcolor(unsigned s) {textcolor_ = s;}
+
+#ifdef FL_HAVE_MSAA
+  // MSAA support
+protected:
+  bool msaa_location(long id, long* X, long* Y, long* W, long* H);
+#endif // FL_HAVE_MSAA
 };
 
 #endif
Index: FL/Enumerations.H
===================================================================
--- FL/Enumerations.H	(revision 6732)
+++ FL/Enumerations.H	(working copy)
@@ -587,6 +587,8 @@
   FL_FREE_LABELTYPE		///< first free labeltype to use for creating own labeltypes
 };
 
+FL_EXPORT const char* fl_label_string(int l_type, const char* l_text);
+
 /** 
   Sets the current  label type end return its corresponding Fl_Labeltype value. 
   @{
Index: FL/Fl_Tabs.H
===================================================================
--- FL/Fl_Tabs.H	(revision 6732)
+++ FL/Fl_Tabs.H	(working copy)
@@ -77,6 +77,32 @@
   int push(Fl_Widget *);
   Fl_Tabs(int,int,int,int,const char * = 0);
   Fl_Widget *which(int event_x, int event_y);
+
+#ifdef FL_HAVE_MSAA
+  // MSAA support
+  friend class Fl_Msaa_Tabs_Proxy;
+
+private:
+  Fl_Msaa_Tabs_Proxy* msaa_tabs_proxy_;
+  void msaa_tabs_on_focus(long id);
+
+protected:
+  void msaa_cleanup();
+  long msaa_child_count()  {return children() + 1;}
+  Fl_Msaa_Proxy* msaa_child(long id);
+  long msaa_child_id(Fl_Widget* o) const;
+  Fl_Widget* msaa_child_widget(long id) const;
+
+  long msaa_tabs_selected_id();
+  bool msaa_tabs_name(long id, BSTR* bstr);
+  long msaa_tabs_state(long id);
+  bool msaa_tabs_shortcut(long id, BSTR* bstr);
+  bool msaa_tabs_help(long id, BSTR* bstr);
+  bool msaa_tabs_location(long id, long* X, long* Y, long* W, long* H);
+
+public:
+  void msaa_on_focus();
+#endif // FL_HAVE_MSAA
 };
 
 #endif
Index: FL/Fl_Group.H
===================================================================
--- FL/Fl_Group.H	(revision 6732)
+++ FL/Fl_Group.H	(working copy)
@@ -192,6 +192,33 @@
 
   /** This is for forms compatibility only */
   void forms_end();
+
+#ifdef FL_HAVE_MSAA
+  // MSAA support
+protected:
+  bool msaa_focusable()  {return false;}
+  long msaa_child_count()  {return children();}
+  Fl_Msaa_Proxy* msaa_child(long id);
+  bool msaa_name(long id, BSTR* bstr);
+  bool msaa_value(long id, BSTR* bstr);
+  bool msaa_description(long id, BSTR* bstr);
+  long msaa_role(long id);
+  long msaa_state(long id);
+  bool msaa_help(long id, BSTR* bstr);
+  bool msaa_help_topic(long id, BSTR* bstr, long* num);
+  bool msaa_shortcut(long id, BSTR* bstr);
+  long msaa_focus();
+  bool msaa_action(long id, BSTR* bstr);
+  bool msaa_do_select(long id, long flags);
+  bool msaa_location(long id, long* X, long* Y, long* W, long* H);
+  bool msaa_do_action(long id);
+  bool msaa_put_value(long id, const char* str);
+
+  virtual Fl_Widget* msaa_child_widget(long id) const;
+
+public:
+  long msaa_child_id(Fl_Widget*) const;
+#endif // FL_HAVE_MSAA
 };
 
 // dummy class used to end child groups in constructors for complex
Index: FL/Fl_Slider.H
===================================================================
--- FL/Fl_Slider.H	(revision 6732)
+++ FL/Fl_Slider.H	(working copy)
@@ -111,6 +111,21 @@
 
   /** Sets the slider box type. */
   void slider(Fl_Boxtype c) {slider_ = c;}
+
+#ifdef FL_HAVE_MSAA
+  // MSAA support
+protected:
+  void msaa_valuator_on_change();
+  bool msaa_slider_location(long id,
+    long oX, long oY, long oW, long oH, long* X, long* Y, long* W, long* H);
+  long msaa_child_count()  {return 3;}
+  bool msaa_name(long id, BSTR* bstr);
+  long msaa_role(long id);
+  long msaa_state(long id);
+  bool msaa_action(long id, BSTR* bstr);
+  bool msaa_location(long id, long* X, long* Y, long* W, long* H);
+  bool msaa_do_action(long id);
+#endif // FL_HAVE_MSAA
 };
 
 #endif
Index: FL/Fl_Browser_.H
===================================================================
--- FL/Fl_Browser_.H	(revision 6732)
+++ FL/Fl_Browser_.H	(working copy)
@@ -323,6 +323,41 @@
   */
   void scrollbar_left() { scrollbar.align(FL_ALIGN_LEFT); }
   void sort(int flags=0);
+#ifdef FL_HAVE_MSAA
+  // MSAA support
+  friend class Fl_Msaa_Browser_Proxy;
+
+private:
+  Fl_Msaa_Browser_Proxy* msaa_browser_proxy_;
+
+protected:
+  void msaa_cleanup();
+  bool msaa_focusable()  {return true;}
+  long msaa_child_count()  {return 4;}
+  Fl_Msaa_Proxy* msaa_child(long id);
+  long msaa_role(long id);
+  long msaa_state(long id);
+  long msaa_focus();
+  bool msaa_location(long id, long* X, long* Y, long* W, long* H);
+  Fl_Widget* msaa_child_widget(long id) const;
+
+  virtual void msaa_browser_bbox(long* X, long* Y, long* W, long* H);
+  virtual bool msaa_browser_item_bbox(long childId,
+    long* X, long* Y, long* W, long* H);
+  virtual long msaa_browser_size();
+  virtual bool msaa_browser_displayed(long id);
+  virtual bool msaa_browser_selected(long id);
+  virtual long msaa_browser_find_item(void* l);
+  virtual long msaa_browser_focus();
+  virtual void msaa_browser_set_focus(long id);
+  virtual bool msaa_browser_text(long id, BSTR* bstr)  {return false;}
+  virtual bool msaa_browser_description(long id, BSTR* bstr)  {return false;}
+  virtual bool msaa_browser_do_select(long id, long flags);
+
+public:
+  long msaa_child_id(Fl_Widget* o) const;
+  void msaa_on_focus();
+#endif // FL_HAVE_MSAA
 };
 
 #endif
Index: FL/Fl_Export.H
===================================================================
--- FL/Fl_Export.H	(revision 6732)
+++ FL/Fl_Export.H	(working copy)
@@ -29,6 +29,15 @@
 #  define Fl_Export_H
 
 /*
+ * Set WINVER before it gets defined in winver.h
+ */
+
+#  if !defined(WINVER) && defined(__MINGW32__)
+#    define WINVER 0x0500
+#  endif
+
+
+/*
  * The following is only used when building DLLs under WIN32...
  */
 
Index: FL/Fl_Menu_Item.H
===================================================================
--- FL/Fl_Menu_Item.H	(revision 6732)
+++ FL/Fl_Menu_Item.H	(working copy)
@@ -394,6 +394,14 @@
     return add(a,fl_old_shortcut(b),c,d,e);}
 
   int size() const ;
+
+#ifdef FL_HAVE_MSAA
+  // MSAA support
+  bool msaa_name(BSTR* bstr) const;
+  bool msaa_value(BSTR* bstr) const;
+  bool msaa_shortcut(bool on_menubar_, BSTR* bstr) const;
+  bool msaa_put_value(const char* str);
+#endif // FL_HAVE_MSAA
 };
 
 typedef Fl_Menu_Item Fl_Menu; // back compatibility
Index: FL/Fl_Widget.H
===================================================================
--- FL/Fl_Widget.H	(revision 6732)
+++ FL/Fl_Widget.H	(working copy)
@@ -33,6 +33,12 @@
 
 #include "Enumerations.H"
 
+#if defined(WIN32) && (!defined(__GNUC__) || __GNUC__ >= 3)
+#  define FL_HAVE_MSAA
+#  include <ole2.h>
+class Fl_Msaa_Proxy;
+#endif
+
 class Fl_Widget;
 class Fl_Window;
 class Fl_Group;
@@ -911,6 +917,51 @@
       \deprecated Use selection_color(unsigned) instead.
   */
   void color2(unsigned a) {color2_ = a;}
+
+#ifdef FL_HAVE_MSAA
+  // MSAA support
+  friend class Fl_Msaa_Proxy;
+
+protected:
+  Fl_Msaa_Proxy* msaa_proxy_;
+
+  bool msaa_visible() const  {return visible_r() && (w() > 0) && (h() > 0);}
+  void msaa_show_caret(long X, long Y, long W, long H);
+  void msaa_hide_caret();
+  static void msaa_convert_selection(long* ids, size_t count, VARIANT* pvar);
+
+  virtual bool msaa_use_proxy()  {return true;}
+  virtual bool msaa_focusable()  {return handle(FL_FOCUS);}
+  virtual void msaa_cleanup();
+  virtual const Fl_Window* msaa_window() const  {return window();}
+  virtual long msaa_child_count()  {return 0;}
+  virtual bool msaa_parent(IDispatch** dispatch);
+  virtual Fl_Msaa_Proxy* msaa_child(long id);
+  virtual Fl_Msaa_Proxy* msaa_as_parent()  {return msaa_child(0);}
+  virtual long msaa_child_id(Fl_Widget* o) const  {return -1;}
+  virtual bool msaa_name(long id, BSTR* bstr);
+  virtual bool msaa_value(long id, BSTR* bstr)  {return false;}
+  virtual bool msaa_description(long id, BSTR* bstr)  {return false;}
+  virtual long msaa_role(long id);
+  virtual long msaa_state(long id);
+  virtual bool msaa_help(long id, BSTR* bstr);
+  virtual bool msaa_help_topic(long id, BSTR* bstr, long* num)  {return false;}
+  virtual bool msaa_shortcut(long id, BSTR* bstr);
+  virtual long msaa_focus();
+  virtual bool msaa_selection(VARIANT* variant)  {return false;}
+  virtual bool msaa_action(long id, BSTR* bstr)  {return false;}
+  virtual bool msaa_do_select(long id, long flags);
+  virtual bool msaa_location(long id, long* X, long* Y, long* W, long* H);
+  virtual long msaa_navigate(long id, long dir);
+  virtual bool msaa_do_action(long id)  {return false;}
+  virtual bool msaa_put_value(long id, const char* str)  {return false;}
+
+public:
+  void msaa_notify(DWORD event, long childId = 0);
+
+  virtual Fl_Msaa_Proxy* msaa_proxy()  {return msaa_child(0);}
+  virtual void msaa_on_focus();
+#endif // FL_HAVE_MSAA
 };
 
 /**
Index: FL/Fl_Menu_Bar.H
===================================================================
--- FL/Fl_Menu_Bar.H	(revision 6732)
+++ FL/Fl_Menu_Bar.H	(working copy)
@@ -80,6 +80,14 @@
   */
     Fl_Menu_Bar(int X, int Y, int W, int H,const char *l=0)
       : Fl_Menu_(X,Y,W,H,l) {}
+
+#ifdef FL_HAVE_MSAA
+  // MSAA support
+protected:
+  long msaa_role(long id);
+  bool msaa_menu_location(int pos, long id, long* X, long* Y, long* W, long* H);
+  bool msaa_menu_shortcut(int pos, long id, BSTR* bstr);
+#endif // FL_HAVE_MSAA
 };
 
 #endif
Index: FL/Fl_Choice.H
===================================================================
--- FL/Fl_Choice.H	(revision 6732)
+++ FL/Fl_Choice.H	(working copy)
@@ -99,6 +99,14 @@
   int value(int v);
 
   int value(const Fl_Menu_Item* v);
+
+#ifdef FL_HAVE_MSAA
+  // MSAA support
+protected:
+  bool msaa_value(long id, BSTR* bstr);
+  long msaa_role(long id);
+  bool msaa_put_value(long id, const char* str);
+#endif // FL_HAVE_MSAA
 };
 
 #endif
Index: FL/Fl_Browser.H
===================================================================
--- FL/Fl_Browser.H	(revision 6732)
+++ FL/Fl_Browser.H	(working copy)
@@ -309,6 +309,18 @@
   /** For back compatibility only. */
   void replace(int a, const char* b) { text(a, b); }
   void display(int line, int val=1);
+
+#ifdef FL_HAVE_MSAA
+  // MSAA support
+protected:
+  long msaa_browser_size();
+  bool msaa_browser_displayed(long id);
+  bool msaa_browser_selected(long id);
+  long msaa_browser_find_item(void* l);
+  long msaa_browser_focus();
+  void msaa_browser_set_focus(long id);
+  bool msaa_browser_text(long id, BSTR* bstr);
+#endif // FL_HAVE_MSAA
 };
 
 #endif
Index: FL/Fl_Valuator.H
===================================================================
--- FL/Fl_Valuator.H	(revision 6732)
+++ FL/Fl_Valuator.H	(working copy)
@@ -135,6 +135,15 @@
   double round(double); // round to nearest multiple of step
   double clamp(double); // keep in range
   double increment(double, int); // add n*step to value
+
+#ifdef FL_HAVE_MSAA
+  // MSAA support
+protected:
+  virtual void msaa_valuator_on_change();
+
+  bool msaa_value(long id, BSTR* bstr);
+  bool msaa_put_value(long id, const char* str);
+#endif // FL_HAVE_MSAA
 };
 
 #endif
Index: FL/Fl_Menu_.H
===================================================================
--- FL/Fl_Menu_.H	(revision 6732)
+++ FL/Fl_Menu_.H	(working copy)
@@ -36,6 +36,8 @@
 #endif
 #include "Fl_Menu_Item.H"
 
+class Fl_Menu_Proxy;
+
 /**
   Base class of all widgets that have a menu in FLTK.
   Currently FLTK provides you with 
@@ -137,6 +139,27 @@
   Fl_Color down_color() const {return selection_color();}
   /** For back compatibility, same as selection_color() */
   void down_color(unsigned c) {selection_color(c);}
+
+#ifdef FL_HAVE_MSAA
+  // MSAA support
+  friend class Fl_Menu_Proxy;
+
+private:
+  Fl_Menu_Proxy** msaa_menu_items_;
+  Fl_Menu_Proxy** msaa_menu_popups_;
+  void msaa_menu_clear();
+
+protected:
+  Fl_Msaa_Proxy* msaa_child(long id);
+  Fl_Menu_Proxy* msaa_menu_proxy(int pos, bool isPopup);
+
+  const Fl_Menu_Item* msaa_menu_get_item(int pos, long id);
+  int msaa_menu_parent(int pos);
+  int msaa_menu_child_id(int pos);
+  virtual bool msaa_menu_location(int pos, long id,
+    long* X, long* Y, long* W, long* H);
+  virtual bool msaa_menu_shortcut(int pos, long id, BSTR* bstr);
+#endif // FL_HAVE_MSAA
 };
 
 #endif
Index: FL/Fl_Msaa_Proxy.H
===================================================================
--- FL/Fl_Msaa_Proxy.H	(revision 0)
+++ FL/Fl_Msaa_Proxy.H	(revision 0)
@@ -0,0 +1,122 @@
+//
+// "$Id: $"
+//
+// WIN32 MSAA proxy header file for the Fast Light Tool Kit (FLTK).
+//
+// Copyright 2008 by Bill Spitzak and others.
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library 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.  See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+// USA.
+//
+// Please report all bugs and problems on the following page:
+//
+//     http://www.fltk.org/str.php
+//
+
+#ifndef Fl_Msaa_Proxy_H
+#define Fl_Msaa_Proxy_H
+
+#include "Fl_Widget.H"
+
+#ifdef FL_HAVE_MSAA
+#  include <windows.h>
+#  include <oleacc.h>
+
+// MSAA proxy for Fl_Widget object
+class FL_EXPORT Fl_Msaa_Proxy : public IAccessible
+{
+private:
+  LONG           mRefCount;	// Reference counter
+  Fl_Msaa_Proxy* mpParent;	// Forced parent proxy
+
+public:
+  Fl_Widget* mpObj;		// Pointer to widget being represented
+
+  Fl_Msaa_Proxy(Fl_Widget* o) : mRefCount(0), mpParent(NULL), mpObj(o)  {}
+  virtual ~Fl_Msaa_Proxy();
+
+  void Detach()  {mpObj = NULL;}
+  void OverrideParent(Fl_Msaa_Proxy* parent);
+
+  virtual long LookupChild(Fl_Widget* o);
+
+  // IUnknown implementation
+  HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void**);
+  ULONG STDMETHODCALLTYPE AddRef();
+  ULONG STDMETHODCALLTYPE Release();
+
+  // IDispatch implementation
+  HRESULT STDMETHODCALLTYPE GetTypeInfoCount(UINT*);
+  HRESULT STDMETHODCALLTYPE GetTypeInfo(UINT, LCID, ITypeInfo**);
+  HRESULT STDMETHODCALLTYPE GetIDsOfNames(REFIID, LPOLESTR*,
+      UINT, LCID, DISPID*);
+  HRESULT STDMETHODCALLTYPE Invoke(DISPID, REFIID, LCID, WORD,
+      DISPPARAMS*, VARIANT*, EXCEPINFO*, UINT*);
+
+  // IAccessible implementation
+  HRESULT STDMETHODCALLTYPE get_accParent(IDispatch**);
+  HRESULT STDMETHODCALLTYPE get_accChildCount(long*);
+  HRESULT STDMETHODCALLTYPE get_accChild(VARIANT, IDispatch**);
+  HRESULT STDMETHODCALLTYPE get_accName(VARIANT, BSTR*);
+  HRESULT STDMETHODCALLTYPE get_accValue(VARIANT, BSTR*);
+  HRESULT STDMETHODCALLTYPE get_accDescription(VARIANT, BSTR*);
+  HRESULT STDMETHODCALLTYPE get_accRole(VARIANT, VARIANT*);
+  HRESULT STDMETHODCALLTYPE get_accState(VARIANT, VARIANT*);
+  HRESULT STDMETHODCALLTYPE get_accHelp(VARIANT, BSTR*);
+  HRESULT STDMETHODCALLTYPE get_accHelpTopic(BSTR*, VARIANT, long*);
+  HRESULT STDMETHODCALLTYPE get_accKeyboardShortcut(VARIANT, BSTR*);
+  HRESULT STDMETHODCALLTYPE get_accFocus(VARIANT*);
+  HRESULT STDMETHODCALLTYPE get_accSelection(VARIANT*);
+  HRESULT STDMETHODCALLTYPE get_accDefaultAction(VARIANT, BSTR*);
+  HRESULT STDMETHODCALLTYPE accSelect(long, VARIANT);
+  HRESULT STDMETHODCALLTYPE accLocation(long*, long*, long*, long*, VARIANT);
+  HRESULT STDMETHODCALLTYPE accNavigate(long, VARIANT, VARIANT*);
+  HRESULT STDMETHODCALLTYPE accHitTest(long, long, VARIANT*);
+  HRESULT STDMETHODCALLTYPE accDoDefaultAction(VARIANT);
+  HRESULT STDMETHODCALLTYPE put_accName(VARIANT, BSTR);
+  HRESULT STDMETHODCALLTYPE put_accValue(VARIANT, BSTR);
+};
+
+// Utility functions for BSTR
+extern FL_EXPORT void fl_str_to_bstr(const char* str, int len, BSTR* bstr);
+extern FL_EXPORT char* fl_bstr_to_str(const BSTR bstr, size_t* pLen = 0);
+extern FL_EXPORT char* fl_label_to_text(const char* label, size_t* pLen = 0);
+extern FL_EXPORT char* fl_label_to_shortcut(const char* label, size_t* pLen = 0);
+extern FL_EXPORT void fl_free_str(char* str);
+
+// OLEACC.DLL symbols
+typedef LRESULT (WINAPI* flTypeLresultFromObject)(REFIID, WPARAM, LPUNKNOWN);
+extern FL_EXPORT flTypeLresultFromObject flLresultFromObject;
+typedef HRESULT (WINAPI* flTypeAccessibleObjectFromWindow)(
+    HWND, DWORD, REFIID, void**);
+extern FL_EXPORT flTypeAccessibleObjectFromWindow flAccessibleObjectFromWindow;
+
+// OLEAUT32.DLL symbols
+typedef HRESULT (WINAPI* flTypeVariantCopy)(LPVARIANTARG, LPVARIANTARG);
+extern FL_EXPORT flTypeVariantCopy flVariantCopy;
+typedef HRESULT (WINAPI* flTypeVariantClear)(LPVARIANTARG);
+extern FL_EXPORT flTypeVariantClear flVariantClear;
+typedef BSTR (WINAPI* flTypeSysAllocStringLen)(const OLECHAR*, unsigned int);
+extern FL_EXPORT flTypeSysAllocStringLen flSysAllocStringLen;
+typedef UINT (WINAPI* flTypeSysStringLen)(BSTR);
+extern FL_EXPORT flTypeSysStringLen flSysStringLen;
+
+#endif // FL_HAVE_MSAA
+
+#endif
+
+//
+// End of "$Id: $".
+//
Index: FL/Fl_Box.H
===================================================================
--- FL/Fl_Box.H	(revision 6732)
+++ FL/Fl_Box.H	(working copy)
@@ -60,6 +60,12 @@
 	: Fl_Widget(X,Y,W,H,l) {box(b);}
 
   virtual int handle(int);
+
+#ifdef FL_HAVE_MSAA
+protected:
+  // MSAA support
+  bool msaa_use_proxy()  {return false;}
+#endif // FL_HAVE_MSAA
 };
 
 #endif
Index: FL/Fl_Window.H
===================================================================
--- FL/Fl_Window.H	(revision 6732)
+++ FL/Fl_Window.H	(working copy)
@@ -364,6 +364,20 @@
   void default_cursor(Fl_Cursor, Fl_Color=FL_BLACK, Fl_Color=FL_WHITE);
   static void default_callback(Fl_Window*, void* v);
 
+#ifdef FL_HAVE_MSAA
+  // MSAA support
+private:
+  int msaa_caret_w_;
+  int msaa_caret_h_;
+
+protected:
+  const Fl_Window* msaa_window() const  {return this;}
+  long msaa_role(long id);
+  long msaa_state(long id);
+
+public:
+  void msaa_set_caret(int X, int Y, int W, int H) const;
+#endif // FL_HAVE_MSAA
 };
 
 #endif
Index: FL/Fl_Value_Input.H
===================================================================
--- FL/Fl_Value_Input.H	(revision 6732)
+++ FL/Fl_Value_Input.H	(working copy)
@@ -126,6 +126,12 @@
   /** Sets the color of the text cursor. The text cursor is black by default. */
   void cursor_color(unsigned n) {input.cursor_color(n);}
 
+#ifdef FL_HAVE_MSAA
+  // MSAA support
+protected:
+  long msaa_role(long id);
+  long msaa_state(long id);
+#endif // FL_HAVE_MSAA
 };
 
 #endif
Index: FL/Fl_Value_Output.H
===================================================================
--- FL/Fl_Value_Output.H	(revision 6732)
+++ FL/Fl_Value_Output.H	(working copy)
@@ -86,6 +86,12 @@
   Fl_Color textcolor() const {return (Fl_Color)textcolor_;}
   /**    Gets the color of the text in the value box.  */
   void textcolor(unsigned s) {textcolor_ = s;}
+
+#ifdef FL_HAVE_MSAA
+  // MSAA support
+protected:
+  long msaa_role(long id);
+#endif // FL_HAVE_MSAA
 };
 
 #endif
Index: FL/Fl_Scroll.H
===================================================================
--- FL/Fl_Scroll.H	(revision 6732)
+++ FL/Fl_Scroll.H	(working copy)
@@ -131,6 +131,32 @@
   int yposition() const {return yposition_;}
   void scroll_to(int, int);
   void clear();
+
+#ifdef FL_HAVE_MSAA
+  // MSAA support
+  friend class Fl_Msaa_Scroll_Proxy;
+
+private:
+  Fl_Msaa_Scroll_Proxy* msaa_scroll_proxy_;
+
+protected:
+  void msaa_cleanup();
+  long msaa_child_count()  {return children() - 2;}
+  bool msaa_parent(IDispatch** dispatch);
+  long msaa_role(long id);
+  bool msaa_location(long id, long* X, long* Y, long* W, long* H);
+
+  long msaa_scroll_size() const  {return 4;}
+  bool msaa_scroll_parent(IDispatch** dispatch);
+  Fl_Msaa_Proxy* msaa_scroll_child(long id);
+  void msaa_scroll_bbox(long* X, long* Y, long* W, long* H);
+  long msaa_scroll_role(long id);
+  long msaa_scroll_state(long id);
+  bool msaa_scroll_location(long id, long* X, long* Y, long* W, long* H);
+
+public:
+  Fl_Msaa_Proxy* msaa_proxy();
+#endif // FL_HAVE_MSAA
 };
 
 #endif
Index: FL/Fl_Input_.H
===================================================================
--- FL/Fl_Input_.H	(revision 6732)
+++ FL/Fl_Input_.H	(working copy)
@@ -265,6 +265,16 @@
   */
   void wrap(int b) { if (b) type((uchar)(type() | FL_INPUT_WRAP));
                          else type((uchar)(type() & ~FL_INPUT_WRAP)); }
+
+#ifdef FL_HAVE_MSAA
+  // MSAA support
+protected:
+  long msaa_role(long id);
+  bool msaa_value(long id, BSTR* bstr);
+  bool msaa_put_value(long id, const char* str);
+
+  virtual void msaa_input_on_change();
+#endif // FL_HAVE_MSAA
 };
 
 #endif 
Index: FL/Fl_Button.H
===================================================================
--- FL/Fl_Button.H	(revision 6732)
+++ FL/Fl_Button.H	(working copy)
@@ -167,6 +167,17 @@
 
   /// (for backwards compatibility)
   void down_color(unsigned c) {selection_color(c);}
+
+#ifdef FL_HAVE_MSAA
+  // MSAA support
+protected:
+  bool msaa_value(long id, BSTR* bstr);
+  long msaa_role(long id);
+  long msaa_state(long id);
+  bool msaa_action(long id, BSTR* bstr);
+  bool msaa_do_action(long id);
+  bool msaa_put_value(long id, const char* str);
+#endif // FL_HAVE_MSAA
 };
 
 #endif
Index: FL/Fl_Scrollbar.H
===================================================================
--- FL/Fl_Scrollbar.H	(revision 6732)
+++ FL/Fl_Scrollbar.H	(working copy)
@@ -111,6 +111,18 @@
   */
   void linesize(int i) {linesize_ = i;}
 
+#ifdef FL_HAVE_MSAA
+  // MSAA support
+protected:
+  void msaa_valuator_on_change();
+  long msaa_child_count()  {return 5;}
+  bool msaa_name(long id, BSTR* bstr);
+  long msaa_role(long id);
+  long msaa_state(long id);
+  bool msaa_action(long id, BSTR* bstr);
+  bool msaa_location(long id, long* x, long* y, long* w, long* h);
+  bool msaa_do_action(long id);
+#endif // FL_HAVE_MSAA
 };
 
 #endif
Index: FL/Fl_Menu_Button.H
===================================================================
--- FL/Fl_Menu_Button.H	(revision 6732)
+++ FL/Fl_Menu_Button.H	(working copy)
@@ -63,6 +63,13 @@
   int handle(int);
   const Fl_Menu_Item* popup();
   Fl_Menu_Button(int,int,int,int,const char * =0);
+
+#ifdef FL_HAVE_MSAA
+  // MSAA support
+protected:
+  long msaa_role(long id);
+  bool msaa_menu_location(int pos, long id, long* X, long* Y, long* W, long* H);
+#endif // FL_HAVE_MSAA
 };
 
 #endif
Index: fluid/Fl_Type.cxx
===================================================================
--- fluid/Fl_Type.cxx	(revision 6732)
+++ fluid/Fl_Type.cxx	(working copy)
@@ -188,6 +188,13 @@
   int handle(int);
   void callback();
   Widget_Browser(int,int,int,int,const char * =0);
+
+#ifdef FL_HAVE_MSAA
+  // MSAA support
+protected:
+  bool msaa_browser_text(long id, BSTR* bstr);
+  bool msaa_browser_description(long id, BSTR* bstr);
+#endif // FL_HAVE_MSAA
 };
 
 static Widget_Browser *widget_browser;
@@ -1012,6 +1020,100 @@
 }
 
 
+#ifdef FL_HAVE_MSAA
+#include <FL/Fl_Msaa_Proxy.H>
+///////////////////////////////////////////////////////////////////////
+//
+//  The following code implements Microsoft Active Accessibility
+
+bool
+Widget_Browser::msaa_browser_text(long id, BSTR* bstr)
+{
+  if (id < 1)  return false;
+  void* v;
+  for (v = item_first();  --id;  v = item_next(v));
+
+  // NOTE: String extraction must match algorithm in item_draw()
+  Fl_Type *l = (Fl_Type *)v;
+  if (l->is_widget() || l->is_class())
+  {
+    const char* subclass = subclassname(l);
+    if (!strncmp(subclass, "Fl_", 3))  subclass += 3;
+    size_t len = strlen(subclass);
+    const char* c = l->name();
+    if (c)
+    {
+      // Widget has a name
+      len += (strlen(c) + 2);
+      char* buf = (char*)malloc(len + 1);
+      sprintf(buf, "%s  %s", subclass, c);
+      fl_str_to_bstr(buf, len, bstr);
+      free(buf);
+    }
+    else if (c = l->label())
+    {
+      // Widget has a label
+      char* buf = (char*)malloc(len + 52);
+      sprintf(buf, "%s  ", subclass);
+      char* p = buf + len + 2;
+      *p++ = '"';
+      for (int i = 20; i--;) {
+        if (! (*c & -32)) break;
+        *p++ = *c++;
+      }
+      if (*c) {strcpy(p,"..."); p+=3;}
+      *p++ = '"';
+      fl_str_to_bstr(buf, p - buf, bstr);
+      free(buf);
+    }
+    else
+    {
+      // Only widget class is known
+      fl_str_to_bstr(subclass, len, bstr);
+    }
+    return true;
+  }
+  else
+  {
+    const char* c = l->title();
+    char buf[60]; char* p = buf;
+    for (int i = 55; i--;) {
+      if (! (*c & -32)) break;
+      *p++ = *c++;
+    }
+    if (*c) {strcpy(p,"..."); p+=3;}
+    *p = 0;
+    fl_str_to_bstr(buf, p - buf, bstr);
+  }
+  return true;
+}
+
+
+bool
+Widget_Browser::msaa_browser_description(long id, BSTR* bstr)
+{
+  if (!show_comments)  return false;
+
+  if (id < 1)  return false;
+  void* v;
+  for (v = item_first();  --id;  v = item_next(v));
+  Fl_Type *l = (Fl_Type *)v;
+
+  // NOTE: String extraction must match algorithm in item_draw()
+  const char *s = l->comment();
+  if (!s)  return false;
+  char buf[82], *d = buf;
+  for (int i=0; i<80; i++) {
+    char c = *s++;
+    if (c==0 || c=='\n') break;
+    *d++ = c;
+  }
+  *d = 0;
+  fl_str_to_bstr(buf, d - buf, bstr);
+  return true;
+}
+#endif // FL_HAVE_MSAA
+
 //
 // End of "$Id$".
 //
