1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_sc.hxx"
30 
31 #include "AccessiblePageHeader.hxx"
32 #include "AccessiblePageHeaderArea.hxx"
33 #include "AccessibilityHints.hxx"
34 #include "prevwsh.hxx"
35 #include "unoguard.hxx"
36 #include "miscuno.hxx"
37 #include "prevloc.hxx"
38 #include "document.hxx"
39 #include "stlpool.hxx"
40 #include "scitems.hxx"
41 #include "attrib.hxx"
42 #include "scresid.hxx"
43 #ifndef SC_SC_HRC
44 #include "sc.hrc"
45 #endif
46 
47 #include <com/sun/star/accessibility/AccessibleRole.hpp>
48 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
49 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
50 
51 #include <vcl/window.hxx>
52 #include <svl/smplhint.hxx>
53 #include <unotools/accessiblestatesethelper.hxx>
54 #include <svl/style.hxx>
55 #include <svl/itempool.hxx>
56 #include <editeng/editobj.hxx>
57 #include <toolkit/helper/convert.hxx>
58 
59 #include <algorithm>
60 
61 using namespace	::com::sun::star;
62 using namespace	::com::sun::star::accessibility;
63 
64 const sal_uInt8     MAX_AREAS = 3;
65 
66 //=====  internal  ============================================================
67 struct Acquire
68 {
69 	void operator() (ScAccessiblePageHeaderArea* pArea)
70 	{
71         if (pArea)
72             pArea->acquire();
73 	}
74 };
75 
76 struct Release
77 {
78 	void operator() (ScAccessiblePageHeaderArea*& pArea)
79 	{
80         if (pArea)
81             pArea->release();
82 	}
83 };
84 
85 struct Dispose
86 {
87 	void operator() (ScAccessiblePageHeaderArea*& pArea)
88 	{
89         if (pArea)
90         {
91             pArea->dispose();
92             pArea->release();
93         }
94         pArea = NULL;
95 	}
96 };
97 
98 ScAccessiblePageHeader::ScAccessiblePageHeader( const ::com::sun::star::uno::Reference<
99 						        ::com::sun::star::accessibility::XAccessible>& rxParent,
100 							ScPreviewShell* pViewShell, sal_Bool bHeader, sal_Int32 nIndex ) :
101 ScAccessibleContextBase( rxParent, bHeader ? AccessibleRole::HEADER : AccessibleRole::FOOTER ),
102 	mpViewShell( pViewShell ),
103 	mnIndex( nIndex ),
104 	mbHeader( bHeader ),
105     maAreas(MAX_AREAS, NULL),
106     mnChildCount(-1)
107 {
108 	if (mpViewShell)
109 		mpViewShell->AddAccessibilityObject(*this);
110 }
111 
112 ScAccessiblePageHeader::~ScAccessiblePageHeader()
113 {
114 	if (!ScAccessibleContextBase::IsDefunc() && !rBHelper.bInDispose)
115 	{
116 		// increment refcount to prevent double call off dtor
117 		osl_incrementInterlockedCount( &m_refCount );
118 		dispose();
119 	}
120 }
121 
122 void SAL_CALL ScAccessiblePageHeader::disposing()
123 {
124     ScUnoGuard aGuard;
125 	if (mpViewShell)
126 	{
127 		mpViewShell->RemoveAccessibilityObject(*this);
128 		mpViewShell = NULL;
129 	}
130     std::for_each(maAreas.begin(), maAreas.end(), Dispose());
131 
132 	ScAccessibleContextBase::disposing();
133 }
134 
135 //=====  SfxListener  =====================================================
136 
137 void ScAccessiblePageHeader::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
138 {
139 	if (rHint.ISA( SfxSimpleHint ) )
140 	{
141 		const SfxSimpleHint& rRef = (const SfxSimpleHint&)rHint;
142 		// only notify if child exist, otherwise it is not necessary
143 		if ((rRef.GetId() == SC_HINT_DATACHANGED))
144         {
145             ScHFAreas aOldAreas(maAreas);
146             std::for_each(aOldAreas.begin(), aOldAreas.end(), Acquire());
147             mnChildCount = -1;
148             getAccessibleChildCount();
149             for (sal_uInt8 i = 0; i < MAX_AREAS; ++i)
150             {
151                 if ((aOldAreas[i] && maAreas[i] && !ScGlobal::EETextObjEqual(aOldAreas[i]->GetEditTextObject(), maAreas[i]->GetEditTextObject())) ||
152                     (aOldAreas[i] && !maAreas[i]) || (!aOldAreas[i] && maAreas[i]))
153                 {
154                     if (aOldAreas[i] && aOldAreas[i]->GetEditTextObject())
155                     {
156 			            AccessibleEventObject aEvent;
157 			            aEvent.EventId = AccessibleEventId::CHILD;
158 			            aEvent.Source = uno::Reference< XAccessibleContext >(this);
159                         aEvent.OldValue = uno::makeAny(uno::Reference<XAccessible>(aOldAreas[i]));
160 
161 			            CommitChange(aEvent); // child gone - event
162                         aOldAreas[i]->dispose();
163                     }
164                     if (maAreas[i] && maAreas[i]->GetEditTextObject())
165                     {
166 			            AccessibleEventObject aEvent;
167 			            aEvent.EventId = AccessibleEventId::CHILD;
168 			            aEvent.Source = uno::Reference< XAccessibleContext >(this);
169                         aEvent.NewValue = uno::makeAny(uno::Reference<XAccessible>(maAreas[i]));
170 
171 			            CommitChange(aEvent); // new child - event
172                     }
173                 }
174             }
175             std::for_each(aOldAreas.begin(), aOldAreas.end(), Release());
176         }
177         else if (rRef.GetId() == SC_HINT_ACC_VISAREACHANGED)
178         {
179             AccessibleEventObject aEvent;
180             aEvent.EventId = AccessibleEventId::VISIBLE_DATA_CHANGED;
181 			aEvent.Source = uno::Reference< XAccessibleContext >(this);
182             CommitChange(aEvent);
183         }
184     }
185 
186 	ScAccessibleContextBase::Notify(rBC, rHint);
187 }
188 
189 //=====  XAccessibleComponent  ============================================
190 
191 uno::Reference< XAccessible > SAL_CALL ScAccessiblePageHeader::getAccessibleAtPoint( const awt::Point& aPoint )
192     							throw (uno::RuntimeException)
193 {
194 	uno::Reference<XAccessible> xRet;
195 
196     if (containsPoint(aPoint))
197     {
198     	ScUnoGuard aGuard;
199         IsObjectValid();
200 
201         sal_Int32 nCount(getAccessibleChildCount()); // fill the areas
202 
203         if (nCount)
204         {
205             // return the first with content, because they have all the same Bounding Box
206 	        sal_uInt8 i(0);
207             while(!xRet.is() && i < MAX_AREAS)
208             {
209                 if (maAreas[i])
210                     xRet = maAreas[i];
211                 else
212                     ++i;
213             }
214         }
215     }
216 
217 	return xRet;
218 }
219 
220 void SAL_CALL ScAccessiblePageHeader::grabFocus() throw (uno::RuntimeException)
221 {
222  	ScUnoGuard aGuard;
223     IsObjectValid();
224 	if (getAccessibleParent().is())
225 	{
226 		uno::Reference<XAccessibleComponent> xAccessibleComponent(getAccessibleParent()->getAccessibleContext(), uno::UNO_QUERY);
227 		if (xAccessibleComponent.is())
228 			xAccessibleComponent->grabFocus();
229 	}
230 }
231 
232 //=====  XAccessibleContext  ==============================================
233 
234 sal_Int32 SAL_CALL ScAccessiblePageHeader::getAccessibleChildCount() throw (uno::RuntimeException)
235 {
236 	ScUnoGuard aGuard;
237     IsObjectValid();
238 
239     if((mnChildCount < 0) && mpViewShell)
240     {
241         mnChildCount = 0;
242         ScDocument* pDoc = mpViewShell->GetDocument();
243         if (pDoc)
244         {
245         	// find out how many regions (left,center, right) are with content
246 
247             SfxStyleSheetBase* pStyle = pDoc->GetStyleSheetPool()->Find(pDoc->GetPageStyle(mpViewShell->GetLocationData().GetPrintTab()), SFX_STYLE_FAMILY_PAGE);
248             if (pStyle)
249             {
250                 sal_uInt16 nPageWhichId(0);
251                 if (mbHeader)
252                     nPageWhichId = mpViewShell->GetLocationData().IsHeaderLeft() ? ATTR_PAGE_HEADERLEFT : ATTR_PAGE_HEADERRIGHT;
253                 else
254                     nPageWhichId = mpViewShell->GetLocationData().IsFooterLeft() ? ATTR_PAGE_FOOTERLEFT : ATTR_PAGE_FOOTERRIGHT;
255 
256 			    const ScPageHFItem& rPageItem = static_cast<const ScPageHFItem&>(pStyle->GetItemSet().Get(nPageWhichId));
257 				AddChild(rPageItem.GetLeftArea(), 0, SVX_ADJUST_LEFT);
258 				AddChild(rPageItem.GetCenterArea(), 1, SVX_ADJUST_CENTER);
259 				AddChild(rPageItem.GetRightArea(), 2, SVX_ADJUST_RIGHT);
260             }
261         }
262     }
263 
264 	return mnChildCount;
265 }
266 
267 uno::Reference< XAccessible > SAL_CALL ScAccessiblePageHeader::getAccessibleChild( sal_Int32 nIndex )
268     							throw (lang::IndexOutOfBoundsException, uno::RuntimeException)
269 {
270 	ScUnoGuard aGuard;
271     IsObjectValid();
272 
273 	uno::Reference<XAccessible> xRet;
274 
275     if(mnChildCount < 0)
276         getAccessibleChildCount();
277 
278     ScHFAreas::iterator aItr = maAreas.begin();
279     ScHFAreas::iterator aEndItr = maAreas.end();
280     while (!xRet.is() && (nIndex >= 0) && (aItr != aEndItr))
281     {
282         if (*aItr)
283         {
284             if (nIndex == 0)
285                 xRet = *aItr;
286             else
287                 --nIndex;
288         }
289         else
290             ++aItr;
291     }
292 
293 	if ( !xRet.is() )
294 		throw lang::IndexOutOfBoundsException();
295 
296 	return xRet;
297 }
298 
299 sal_Int32 SAL_CALL ScAccessiblePageHeader::getAccessibleIndexInParent() throw (uno::RuntimeException)
300 {
301 	return mnIndex;
302 }
303 
304 uno::Reference< XAccessibleStateSet > SAL_CALL ScAccessiblePageHeader::getAccessibleStateSet()
305 								throw (uno::RuntimeException)
306 {
307 	ScUnoGuard aGuard;
308 	uno::Reference<XAccessibleStateSet> xParentStates;
309 	if (getAccessibleParent().is())
310 	{
311 		uno::Reference<XAccessibleContext> xParentContext = getAccessibleParent()->getAccessibleContext();
312 		xParentStates = xParentContext->getAccessibleStateSet();
313 	}
314 	utl::AccessibleStateSetHelper* pStateSet = new utl::AccessibleStateSetHelper();
315 	if (IsDefunc(xParentStates))
316 		pStateSet->AddState(AccessibleStateType::DEFUNC);
317     else
318     {
319 	    pStateSet->AddState(AccessibleStateType::ENABLED);
320 	    pStateSet->AddState(AccessibleStateType::OPAQUE);
321 	    if (isShowing())
322 		    pStateSet->AddState(AccessibleStateType::SHOWING);
323 	    if (isVisible())
324 		    pStateSet->AddState(AccessibleStateType::VISIBLE);
325     }
326 	return pStateSet;
327 }
328 
329 //=====  XServiceInfo  ====================================================
330 
331 rtl::OUString SAL_CALL ScAccessiblePageHeader::getImplementationName() throw(uno::RuntimeException)
332 {
333 	return rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("ScAccessiblePageHeader"));
334 }
335 
336 uno::Sequence<rtl::OUString> SAL_CALL ScAccessiblePageHeader::getSupportedServiceNames()
337 													throw(uno::RuntimeException)
338 {
339 	uno::Sequence< ::rtl::OUString > aSequence = ScAccessibleContextBase::getSupportedServiceNames();
340     sal_Int32 nOldSize(aSequence.getLength());
341     aSequence.realloc(nOldSize + 1);
342     ::rtl::OUString* pNames = aSequence.getArray();
343 
344 	pNames[nOldSize] = rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.text.AccessibleHeaderFooterView"));
345 
346 	return aSequence;
347 }
348 
349 //====  internal  =========================================================
350 
351 ::rtl::OUString SAL_CALL ScAccessiblePageHeader::createAccessibleDescription(void)
352 				    throw (uno::RuntimeException)
353 {
354     String sDesc(ScResId(mbHeader ? STR_ACC_HEADER_DESCR : STR_ACC_FOOTER_DESCR));
355     sDesc.SearchAndReplaceAscii("%1", String(ScResId(SCSTR_UNKNOWN)));
356 	return rtl::OUString( sDesc );
357 }
358 
359 ::rtl::OUString SAL_CALL ScAccessiblePageHeader::createAccessibleName(void)
360 				    throw (uno::RuntimeException)
361 {
362     String sName(ScResId(mbHeader ? STR_ACC_HEADER_NAME : STR_ACC_FOOTER_NAME));
363     sName.SearchAndReplaceAscii("%1", String(ScResId(SCSTR_UNKNOWN)));
364 	return rtl::OUString( sName );
365 }
366 
367 Rectangle ScAccessiblePageHeader::GetBoundingBoxOnScreen() const throw (uno::RuntimeException)
368 {
369 	Rectangle aCellRect(GetBoundingBox());
370 	if (mpViewShell)
371 	{
372 		Window* pWindow = mpViewShell->GetWindow();
373 		if (pWindow)
374 		{
375 			Rectangle aRect = pWindow->GetWindowExtentsRelative(NULL);
376 			aCellRect.setX(aCellRect.getX() + aRect.getX());
377 			aCellRect.setY(aCellRect.getY() + aRect.getY());
378 		}
379 	}
380 	return aCellRect;
381 }
382 
383 Rectangle ScAccessiblePageHeader::GetBoundingBox() const throw (uno::RuntimeException)
384 {
385 	Rectangle aRect;
386 	if (mpViewShell)
387 	{
388 		const ScPreviewLocationData& rData = mpViewShell->GetLocationData();
389 		if ( mbHeader )
390 			rData.GetHeaderPosition( aRect );
391 		else
392 			rData.GetFooterPosition( aRect );
393 
394         // the Rectangle could contain negative coordinates so it should be cliped
395         Rectangle aClipRect(Point(0, 0), aRect.GetSize());
396 		Window* pWindow = mpViewShell->GetWindow();
397 		if (pWindow)
398 			aClipRect = pWindow->GetWindowExtentsRelative(pWindow->GetAccessibleParentWindow());
399         aRect = aClipRect.GetIntersection(aRect);
400     }
401     if (aRect.IsEmpty())
402         aRect.SetSize(Size(-1, -1));
403 
404 	return aRect;
405 }
406 
407 sal_Bool ScAccessiblePageHeader::IsDefunc( const uno::Reference<XAccessibleStateSet>& rxParentStates )
408 {
409 	return ScAccessibleContextBase::IsDefunc() || (mpViewShell == NULL) || !getAccessibleParent().is() ||
410 		(rxParentStates.is() && rxParentStates->contains(AccessibleStateType::DEFUNC));
411 }
412 
413 void ScAccessiblePageHeader::AddChild(const EditTextObject* pArea, sal_uInt32 nIndex, SvxAdjust eAdjust)
414 {
415     if (pArea && (pArea->GetText(0).Len() || (pArea->GetParagraphCount() > 1)))
416     {
417         if (maAreas[nIndex])
418         {
419             if (!ScGlobal::EETextObjEqual(maAreas[nIndex]->GetEditTextObject(), pArea))
420             {
421                 maAreas[nIndex]->release();
422                 maAreas[nIndex] = new ScAccessiblePageHeaderArea(this, mpViewShell, pArea, mbHeader, eAdjust);
423                 maAreas[nIndex]->acquire();
424             }
425         }
426         else
427         {
428             maAreas[nIndex] = new ScAccessiblePageHeaderArea(this, mpViewShell, pArea, mbHeader, eAdjust);
429             maAreas[nIndex]->acquire();
430         }
431         ++mnChildCount;
432     }
433     else
434     {
435         if (maAreas[nIndex])
436         {
437             maAreas[nIndex]->release();
438             maAreas[nIndex] = NULL;
439         }
440     }
441 }
442