2 * Copyright (C) 2008 Nuanti Ltd.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
21 #include "AXObjectCache.h"
23 #include "AccessibilityObject.h"
24 #include "AccessibilityObjectWrapperAtk.h"
25 #include "AccessibilityRenderObject.h"
28 #include "SelectElement.h"
29 #include "TextIterator.h"
33 void AXObjectCache::detachWrapper(AccessibilityObject* obj)
35 webkit_accessible_detach(WEBKIT_ACCESSIBLE(obj->wrapper()));
38 void AXObjectCache::attachWrapper(AccessibilityObject* obj)
40 AtkObject* atkObj = ATK_OBJECT(webkit_accessible_new(obj));
41 obj->setWrapper(atkObj);
42 g_object_unref(atkObj);
45 static void notifyChildrenSelectionChange(AccessibilityObject* object)
47 // This static variable is needed to keep track of the old focused
48 // object as per previous calls to this function, in order to
49 // properly decide whether to emit some signals or not.
50 static RefPtr<AccessibilityObject> oldFocusedObject = 0;
52 // Only list boxes supported so far.
53 if (!object || !object->isListBox())
56 // Emit signal from the listbox's point of view first.
57 g_signal_emit_by_name(object->wrapper(), "selection-changed");
59 // Find the item where the selection change was triggered from.
60 AccessibilityObject::AccessibilityChildrenVector items = object->children();
61 SelectElement* select = toSelectElement(static_cast<Element*>(object->node()));
64 int changedItemIndex = select->activeSelectionStartListIndex();
65 if (changedItemIndex < 0 || changedItemIndex >= static_cast<int>(items.size()))
67 AccessibilityObject* item = items.at(changedItemIndex).get();
69 // Ensure the oldFocusedObject belongs to the same document that
70 // the current item so further comparisons make sense. Otherwise,
71 // just reset oldFocusedObject so it won't be taken into account.
72 if (item && oldFocusedObject && item->document() != oldFocusedObject->document())
75 AtkObject* axItem = item ? item->wrapper() : 0;
76 AtkObject* axOldFocusedObject = oldFocusedObject ? oldFocusedObject->wrapper() : 0;
78 // Old focused object just lost focus, so emit the events.
79 if (axOldFocusedObject && axItem != axOldFocusedObject) {
80 g_signal_emit_by_name(axOldFocusedObject, "focus-event", false);
81 g_signal_emit_by_name(axOldFocusedObject, "state-change", "focused", false);
84 // Emit needed events for the currently (un)selected item.
86 bool isSelected = item->isSelected();
87 g_signal_emit_by_name(axItem, "state-change", "selected", isSelected);
88 g_signal_emit_by_name(axItem, "focus-event", isSelected);
89 g_signal_emit_by_name(axItem, "state-change", "focused", isSelected);
92 // Update pointer to the previously focused object.
93 oldFocusedObject = item;
96 void AXObjectCache::postPlatformNotification(AccessibilityObject* coreObject, AXNotification notification)
98 AtkObject* axObject = coreObject->wrapper();
102 if (notification == AXCheckedStateChanged) {
103 if (!coreObject->isCheckboxOrRadio())
105 g_signal_emit_by_name(axObject, "state-change", "checked", coreObject->isChecked());
106 } else if (notification == AXMenuListValueChanged) {
107 if (!coreObject->isMenuList())
109 g_signal_emit_by_name(axObject, "focus-event", true);
110 g_signal_emit_by_name(axObject, "state-change", "focused", true);
111 } else if (notification == AXSelectedChildrenChanged)
112 notifyChildrenSelectionChange(coreObject);
115 static void emitTextChanged(AccessibilityRenderObject* object, AXObjectCache::AXTextChange textChange, unsigned offset, unsigned count)
117 // Get the axObject for the parent object
118 AtkObject* wrapper = object->parentObjectUnignored()->wrapper();
119 if (!wrapper || !ATK_IS_TEXT(wrapper))
122 // Select the right signal to be emitted
124 switch (textChange) {
125 case AXObjectCache::AXTextInserted:
126 detail = "text-changed::insert";
128 case AXObjectCache::AXTextDeleted:
129 detail = "text-changed::delete";
133 if (!detail.isNull())
134 g_signal_emit_by_name(wrapper, detail.data(), offset, count);
137 void AXObjectCache::nodeTextChangePlatformNotification(AccessibilityObject* object, AXTextChange textChange, unsigned offset, unsigned count)
140 if (count < 1 || !object || !object->isAccessibilityRenderObject())
143 Node* node = object->node();
144 RefPtr<Range> range = Range::create(node->document(), Position(node->parentNode(), 0), Position(node, 0));
145 emitTextChanged(toAccessibilityRenderObject(object), textChange, offset + TextIterator::rangeLength(range.get()), count);
148 void AXObjectCache::handleFocusedUIElementChanged(RenderObject* oldFocusedRender, RenderObject* newFocusedRender)
150 RefPtr<AccessibilityObject> oldObject = getOrCreate(oldFocusedRender);
152 g_signal_emit_by_name(oldObject->wrapper(), "focus-event", false);
153 g_signal_emit_by_name(oldObject->wrapper(), "state-change", "focused", false);
155 RefPtr<AccessibilityObject> newObject = getOrCreate(newFocusedRender);
157 g_signal_emit_by_name(newObject->wrapper(), "focus-event", true);
158 g_signal_emit_by_name(newObject->wrapper(), "state-change", "focused", true);
162 void AXObjectCache::handleScrolledToAnchor(const Node*)
166 } // namespace WebCore