1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_accessibility.hxx"
26 
27 #ifndef _TOOLKIT_AWT_VCLXACCESSIBLECOMPONENT_HXX_
28 #include <accessibility/extended/textwindowaccessibility.hxx>
29 #endif
30 #include "comphelper/accessibleeventnotifier.hxx"
31 #include "unotools/accessiblerelationsethelper.hxx"
32 #include <unotools/accessiblestatesethelper.hxx>
33 #include <vcl/window.hxx>
34 #include <toolkit/helper/convert.hxx>
35 
36 #include <algorithm>
37 #include <vector>
38 #include <hash_map>
39 
40 namespace css = ::com::sun::star;
41 
42 namespace accessibility
43 {
44     ::sal_Int32 getSelectionType(::sal_Int32 nNewFirstPara, ::sal_Int32 nNewFirstPos, ::sal_Int32 nNewLastPara, ::sal_Int32 nNewLastPos);
45     void sendEvent(::sal_Int32 start, ::sal_Int32 end, ::sal_Int16 nEventId);
46 
47 // Both ::osl::Mutex and ParagraphBase implement acquire and release, and thus
48 // ::rtl::Reference< Paragraph > does not work.  So ParagraphImpl was factored
49 // out and ::rtl::Reference< ParagraphImpl > is used instead.
50 class Paragraph: private ::osl::Mutex, public ParagraphImpl
51 {
52 public:
53     inline Paragraph(::rtl::Reference< Document > const & rDocument,
54                      Paragraphs::size_type nNumber):
55         ParagraphImpl(rDocument, nNumber, *this) {}
56 };
57 
58 void SfxListenerGuard::startListening(::SfxBroadcaster & rNotifier)
59 {
60     OSL_ENSURE(m_pNotifier == 0, "called more than once");
61     m_pNotifier = &rNotifier;
62     m_rListener.StartListening(*m_pNotifier, true);
63 }
64 
65 void SfxListenerGuard::endListening()
66 {
67     if (m_pNotifier != 0)
68     {
69         m_rListener.EndListening(*m_pNotifier);
70         m_pNotifier = 0;
71     }
72 }
73 
74 void WindowListenerGuard::startListening(::Window & rNotifier)
75 {
76     OSL_ENSURE(m_pNotifier == 0, "called more than once");
77     m_pNotifier = &rNotifier;
78     m_pNotifier->AddEventListener(m_aListener);
79 }
80 
81 void WindowListenerGuard::endListening()
82 {
83     if (m_pNotifier != 0)
84     {
85         m_pNotifier->RemoveEventListener(m_aListener);
86         m_pNotifier = 0;
87     }
88 }
89 
90 ParagraphImpl::ParagraphImpl(::rtl::Reference< Document > const & rDocument,
91                              Paragraphs::size_type nNumber,
92                              ::osl::Mutex & rMutex):
93     ParagraphBase(rMutex),
94     m_xDocument(rDocument),
95     m_nNumber(nNumber),
96     m_nClientId(0)
97 {
98     m_aParagraphText = m_xDocument->retrieveParagraphText(this);
99 }
100 
101 void
102 ParagraphImpl::numberChanged(bool bIncremented)
103 {
104     if (bIncremented)
105         ++m_nNumber;
106     else
107         --m_nNumber;
108 }
109 
110 void ParagraphImpl::textChanged()
111 {
112     ::rtl::OUString aParagraphText = implGetText();
113 	::css::uno::Any aOldValue, aNewValue;
114     if ( implInitTextChangedEvent( m_aParagraphText, aParagraphText, aOldValue, aNewValue ) )
115     {
116         m_aParagraphText = aParagraphText;
117         notifyEvent(::css::accessibility::AccessibleEventId::
118                     TEXT_CHANGED,
119                     aOldValue, aNewValue);
120     }
121 }
122 
123 void ParagraphImpl::notifyEvent(::sal_Int16 nEventId,
124                                 ::css::uno::Any const & rOldValue,
125                                 ::css::uno::Any const & rNewValue)
126 {
127 	if (m_nClientId)
128 		comphelper::AccessibleEventNotifier::addEvent( m_nClientId, ::css::accessibility::AccessibleEventObject(
129                              static_cast< ::cppu::OWeakObject * >(this),
130                              nEventId, rNewValue, rOldValue) );
131 }
132 
133 // virtual
134 ::css::uno::Reference< ::css::accessibility::XAccessibleContext > SAL_CALL
135 ParagraphImpl::getAccessibleContext() throw (::css::uno::RuntimeException)
136 {
137     checkDisposed();
138     return this;
139 }
140 
141 // virtual
142 ::sal_Int32 SAL_CALL ParagraphImpl::getAccessibleChildCount()
143     throw (::css::uno::RuntimeException)
144 {
145     checkDisposed();
146     return 0;
147 }
148 
149 // virtual
150 ::css::uno::Reference< ::css::accessibility::XAccessible > SAL_CALL
151 ParagraphImpl::getAccessibleChild(::sal_Int32)
152     throw (::css::lang::IndexOutOfBoundsException,
153            ::css::uno::RuntimeException)
154 {
155     checkDisposed();
156     throw ::css::lang::IndexOutOfBoundsException(
157         ::rtl::OUString(
158             RTL_CONSTASCII_USTRINGPARAM(
159                 "textwindowaccessibility.cxx:"
160                 " ParagraphImpl::getAccessibleChild")),
161         static_cast< ::css::uno::XWeak * >(this));
162 }
163 
164 // virtual
165 ::css::uno::Reference< ::css::accessibility::XAccessible > SAL_CALL
166 ParagraphImpl::getAccessibleParent()
167     throw (::css::uno::RuntimeException)
168 {
169     checkDisposed();
170     return m_xDocument->getAccessible();
171 }
172 
173 // virtual
174 ::sal_Int32 SAL_CALL ParagraphImpl::getAccessibleIndexInParent()
175     throw (::css::uno::RuntimeException)
176 {
177     checkDisposed();
178     return m_xDocument->retrieveParagraphIndex(this);
179 }
180 
181 // virtual
182 ::sal_Int16 SAL_CALL ParagraphImpl::getAccessibleRole()
183     throw (::css::uno::RuntimeException)
184 {
185     checkDisposed();
186     return ::css::accessibility::AccessibleRole::PARAGRAPH;
187 }
188 
189 // virtual
190 ::rtl::OUString SAL_CALL ParagraphImpl::getAccessibleDescription()
191     throw (::css::uno::RuntimeException)
192 {
193     checkDisposed();
194     return ::rtl::OUString();
195 }
196 
197 // virtual
198 ::rtl::OUString SAL_CALL ParagraphImpl::getAccessibleName()
199     throw (::css::uno::RuntimeException)
200 {
201     checkDisposed();
202     return ::rtl::OUString();
203 }
204 
205 // virtual
206 ::css::uno::Reference< ::css::accessibility::XAccessibleRelationSet >
207 SAL_CALL ParagraphImpl::getAccessibleRelationSet()
208     throw (::css::uno::RuntimeException)
209 {
210     checkDisposed();
211     return m_xDocument->retrieveParagraphRelationSet( this );
212 }
213 
214 // virtual
215 ::css::uno::Reference< ::css::accessibility::XAccessibleStateSet >
216 SAL_CALL ParagraphImpl::getAccessibleStateSet()
217     throw (::css::uno::RuntimeException)
218 {
219     checkDisposed();
220 
221     // FIXME  Notification of changes (STATE_CHANGED) missing when
222     // m_rView.IsReadOnly() changes:
223     return new ::utl::AccessibleStateSetHelper(
224         m_xDocument->retrieveParagraphState(this));
225 }
226 
227 // virtual
228 ::css::lang::Locale SAL_CALL ParagraphImpl::getLocale()
229     throw (::css::accessibility::IllegalAccessibleComponentStateException,
230            ::css::uno::RuntimeException)
231 {
232     checkDisposed();
233     return m_xDocument->retrieveLocale();
234 }
235 
236 // virtual
237 ::sal_Bool SAL_CALL ParagraphImpl::containsPoint(::css::awt::Point const & rPoint)
238     throw (::css::uno::RuntimeException)
239 {
240     checkDisposed();
241     ::css::awt::Rectangle aRect(m_xDocument->retrieveParagraphBounds(this,
242                                                                      false));
243     return rPoint.X >= 0 && rPoint.X < aRect.Width
244         && rPoint.Y >= 0 && rPoint.Y < aRect.Height;
245 }
246 
247 // virtual
248 ::css::uno::Reference< ::css::accessibility::XAccessible > SAL_CALL
249 ParagraphImpl::getAccessibleAtPoint(::css::awt::Point const &)
250     throw (::css::uno::RuntimeException)
251 {
252     checkDisposed();
253     return 0;
254 }
255 
256 // virtual
257 ::css::awt::Rectangle SAL_CALL ParagraphImpl::getBounds()
258     throw (::css::uno::RuntimeException)
259 {
260     checkDisposed();
261     return m_xDocument->retrieveParagraphBounds(this, false);
262 }
263 
264 // virtual
265 ::css::awt::Point SAL_CALL ParagraphImpl::getLocation()
266     throw (::css::uno::RuntimeException)
267 {
268     checkDisposed();
269     ::css::awt::Rectangle aRect(m_xDocument->retrieveParagraphBounds(this,
270                                                                      false));
271     return ::css::awt::Point(aRect.X, aRect.Y);
272 }
273 
274 // virtual
275 ::css::awt::Point SAL_CALL ParagraphImpl::getLocationOnScreen()
276     throw (::css::uno::RuntimeException)
277 {
278     checkDisposed();
279     ::css::awt::Rectangle aRect(m_xDocument->retrieveParagraphBounds(this,
280                                                                      true));
281     return ::css::awt::Point(aRect.X, aRect.Y);
282 }
283 
284 // virtual
285 ::css::awt::Size SAL_CALL ParagraphImpl::getSize()
286     throw (::css::uno::RuntimeException)
287 {
288     checkDisposed();
289     ::css::awt::Rectangle aRect(m_xDocument->retrieveParagraphBounds(this,
290                                                                      false));
291     return ::css::awt::Size(aRect.Width, aRect.Height);
292 }
293 
294 // virtual
295 void SAL_CALL ParagraphImpl::grabFocus() throw (::css::uno::RuntimeException)
296 {
297     checkDisposed();
298     Window* pWindow = m_xDocument->GetWindow();
299     if ( pWindow )
300     {
301         pWindow->GrabFocus();
302     }
303     try
304     {
305         m_xDocument->changeParagraphSelection(this, 0, 0);
306     }
307     catch (::css::lang::IndexOutOfBoundsException & rEx)
308     {
309         OSL_TRACE(
310             "textwindowaccessibility.cxx: ParagraphImpl::grabFocus:"
311             " caught unexpected %s\n",
312             ::rtl::OUStringToOString(rEx.Message, RTL_TEXTENCODING_UTF8).
313             getStr());
314     }
315 }
316 
317 // virtual
318 ::css::uno::Any SAL_CALL ParagraphImpl::getAccessibleKeyBinding()
319     throw (::css::uno::RuntimeException)
320 {
321     checkDisposed();
322     return ::css::uno::Any();
323 }
324 
325 // virtual
326 ::css::util::Color SAL_CALL ParagraphImpl::getForeground()
327     throw (::css::uno::RuntimeException)
328 {
329     return 0; // TODO
330 }
331 
332 // virtual
333 ::css::util::Color SAL_CALL ParagraphImpl::getBackground()
334     throw (::css::uno::RuntimeException)
335 {
336     return 0; // TODO
337 }
338 
339 // virtual
340 ::sal_Int32 SAL_CALL ParagraphImpl::getCaretPosition()
341     throw (::css::uno::RuntimeException)
342 {
343     checkDisposed();
344     return m_xDocument->retrieveParagraphCaretPosition(this);
345 }
346 
347 // virtual
348 ::sal_Bool SAL_CALL ParagraphImpl::setCaretPosition(::sal_Int32 nIndex)
349     throw (::css::lang::IndexOutOfBoundsException,
350            ::css::uno::RuntimeException)
351 {
352     checkDisposed();
353     m_xDocument->changeParagraphSelection(this, nIndex, nIndex);
354     return true;
355 }
356 
357 // virtual
358 ::sal_Unicode SAL_CALL ParagraphImpl::getCharacter(::sal_Int32 nIndex)
359     throw (::css::lang::IndexOutOfBoundsException,
360            ::css::uno::RuntimeException)
361 {
362     checkDisposed();
363     return OCommonAccessibleText::getCharacter(nIndex);
364 }
365 
366 // virtual
367 ::css::uno::Sequence< ::css::beans::PropertyValue > SAL_CALL
368 ParagraphImpl::getCharacterAttributes(::sal_Int32 nIndex, const ::com::sun::star::uno::Sequence< ::rtl::OUString >& aRequestedAttributes)
369     throw (::css::lang::IndexOutOfBoundsException,
370            ::css::uno::RuntimeException)
371 {
372     checkDisposed();
373     return m_xDocument->retrieveCharacterAttributes( this, nIndex, aRequestedAttributes );
374 }
375 
376 // virtual
377 ::css::awt::Rectangle SAL_CALL
378 ParagraphImpl::getCharacterBounds(::sal_Int32 nIndex)
379     throw (::css::lang::IndexOutOfBoundsException,
380            ::css::uno::RuntimeException)
381 {
382     checkDisposed();
383     ::css::awt::Rectangle aBounds(m_xDocument->retrieveCharacterBounds(this, nIndex));
384     ::css::awt::Rectangle aParaBounds(m_xDocument->retrieveParagraphBounds(this, false));
385 	aBounds.X -= aParaBounds.X;
386 	aBounds.Y -= aParaBounds.Y;
387 	return aBounds;
388 }
389 
390 // virtual
391 ::sal_Int32 SAL_CALL ParagraphImpl::getCharacterCount()
392     throw (::css::uno::RuntimeException)
393 {
394     checkDisposed();
395     return OCommonAccessibleText::getCharacterCount();
396 }
397 
398 // virtual
399 ::sal_Int32 SAL_CALL
400 ParagraphImpl::getIndexAtPoint(::css::awt::Point const & rPoint)
401     throw (::css::uno::RuntimeException)
402 {
403     checkDisposed();
404 	::css::awt::Point aPoint(rPoint);
405     ::css::awt::Rectangle aParaBounds(m_xDocument->retrieveParagraphBounds(this, false));
406 	aPoint.X += aParaBounds.X;
407 	aPoint.Y += aParaBounds.Y;
408     return m_xDocument->retrieveCharacterIndex(this, aPoint);
409 }
410 
411 // virtual
412 ::rtl::OUString SAL_CALL ParagraphImpl::getSelectedText()
413     throw (::css::uno::RuntimeException)
414 {
415     checkDisposed();
416 
417     return OCommonAccessibleText::getSelectedText();
418 }
419 
420 // virtual
421 ::sal_Int32 SAL_CALL ParagraphImpl::getSelectionStart()
422     throw (::css::uno::RuntimeException)
423 {
424     checkDisposed();
425     return OCommonAccessibleText::getSelectionStart();
426 }
427 
428 // virtual
429 ::sal_Int32 SAL_CALL ParagraphImpl::getSelectionEnd()
430     throw (::css::uno::RuntimeException)
431 {
432     checkDisposed();
433     return OCommonAccessibleText::getSelectionEnd();
434 }
435 
436 // virtual
437 ::sal_Bool SAL_CALL ParagraphImpl::setSelection(::sal_Int32 nStartIndex,
438                                                 ::sal_Int32 nEndIndex)
439     throw (::css::lang::IndexOutOfBoundsException,
440            ::css::uno::RuntimeException)
441 {
442     checkDisposed();
443     m_xDocument->changeParagraphSelection(this, nStartIndex, nEndIndex);
444     return true;
445 }
446 
447 // virtual
448 ::rtl::OUString SAL_CALL ParagraphImpl::getText()
449     throw (::css::uno::RuntimeException)
450 {
451     checkDisposed();
452     return OCommonAccessibleText::getText();
453 }
454 
455 // virtual
456 ::rtl::OUString SAL_CALL ParagraphImpl::getTextRange(::sal_Int32 nStartIndex,
457                                                      ::sal_Int32 nEndIndex)
458     throw (::css::lang::IndexOutOfBoundsException,
459            ::css::uno::RuntimeException)
460 {
461     checkDisposed();
462     return OCommonAccessibleText::getTextRange(nStartIndex, nEndIndex);
463 }
464 
465 // virtual
466 ::com::sun::star::accessibility::TextSegment SAL_CALL ParagraphImpl::getTextAtIndex( sal_Int32 nIndex, sal_Int16 aTextType ) throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException)
467 {
468     checkDisposed();
469     return OCommonAccessibleText::getTextAtIndex(nIndex, aTextType);
470 }
471 
472 // virtual
473 ::com::sun::star::accessibility::TextSegment SAL_CALL ParagraphImpl::getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 aTextType ) throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException)
474 {
475     checkDisposed();
476     return OCommonAccessibleText::getTextBeforeIndex(nIndex, aTextType);
477 }
478 
479 // virtual
480 ::com::sun::star::accessibility::TextSegment SAL_CALL ParagraphImpl::getTextBehindIndex( sal_Int32 nIndex, sal_Int16 aTextType ) throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException)
481 {
482     checkDisposed();
483     return OCommonAccessibleText::getTextBehindIndex(nIndex, aTextType);
484 }
485 
486 // virtual
487 ::sal_Bool SAL_CALL ParagraphImpl::copyText(::sal_Int32 nStartIndex,
488                                             ::sal_Int32 nEndIndex)
489     throw (::css::lang::IndexOutOfBoundsException,
490            ::css::uno::RuntimeException)
491 {
492     checkDisposed();
493     m_xDocument->copyParagraphText(this, nStartIndex, nEndIndex);
494     return true;
495 }
496 
497 // virtual
498 ::sal_Bool SAL_CALL ParagraphImpl::cutText(::sal_Int32 nStartIndex,
499                                            ::sal_Int32 nEndIndex)
500     throw (::css::lang::IndexOutOfBoundsException,
501            ::css::uno::RuntimeException)
502 {
503     checkDisposed();
504     m_xDocument->changeParagraphText(this, nStartIndex, nEndIndex, true, false,
505                                      ::rtl::OUString());
506     return true;
507 }
508 
509 // virtual
510 ::sal_Bool SAL_CALL ParagraphImpl::pasteText(::sal_Int32 nIndex)
511     throw (::css::lang::IndexOutOfBoundsException,
512            ::css::uno::RuntimeException)
513 {
514     checkDisposed();
515     m_xDocument->changeParagraphText(this, nIndex, nIndex, false, true,
516                                      ::rtl::OUString());
517     return true;
518 }
519 
520 // virtual
521 ::sal_Bool SAL_CALL ParagraphImpl::deleteText(::sal_Int32 nStartIndex,
522                                           ::sal_Int32 nEndIndex)
523     throw (::css::lang::IndexOutOfBoundsException,
524            ::css::uno::RuntimeException)
525 {
526     checkDisposed();
527     m_xDocument->changeParagraphText(this, nStartIndex, nEndIndex, false, false,
528                                      ::rtl::OUString());
529     return true;
530 }
531 
532 // virtual
533 ::sal_Bool SAL_CALL ParagraphImpl::insertText(::rtl::OUString const & rText,
534                                               ::sal_Int32 nIndex)
535     throw (::css::lang::IndexOutOfBoundsException,
536            ::css::uno::RuntimeException)
537 {
538     checkDisposed();
539     m_xDocument->changeParagraphText(this, nIndex, nIndex, false, false, rText);
540     return true;
541 }
542 
543 // virtual
544 ::sal_Bool SAL_CALL
545 ParagraphImpl::replaceText(::sal_Int32 nStartIndex, ::sal_Int32 nEndIndex,
546                            ::rtl::OUString const & rReplacement)
547     throw (::css::lang::IndexOutOfBoundsException,
548            ::css::uno::RuntimeException)
549 {
550     checkDisposed();
551     m_xDocument->changeParagraphText(this, nStartIndex, nEndIndex, false, false,
552                                      rReplacement);
553     return true;
554 }
555 
556 // virtual
557 ::sal_Bool SAL_CALL ParagraphImpl::setAttributes(
558     ::sal_Int32 nStartIndex, ::sal_Int32 nEndIndex,
559     ::css::uno::Sequence< ::css::beans::PropertyValue > const & rAttributeSet)
560     throw (::css::lang::IndexOutOfBoundsException,
561            ::css::uno::RuntimeException)
562 {
563     checkDisposed();
564     m_xDocument->changeParagraphAttributes(this, nStartIndex, nEndIndex,
565                                            rAttributeSet);
566     return true;
567 }
568 
569 // virtual
570 ::sal_Bool SAL_CALL ParagraphImpl::setText(::rtl::OUString const & rText)
571     throw (::css::uno::RuntimeException)
572 {
573     checkDisposed();
574     m_xDocument->changeParagraphText(this, rText);
575     return true;
576 }
577 
578 // virtual
579 ::css::uno::Sequence< ::css::beans::PropertyValue > SAL_CALL
580 ParagraphImpl::getDefaultAttributes(const ::css::uno::Sequence< ::rtl::OUString >& RequestedAttributes)
581     throw (::css::uno::RuntimeException)
582 {
583     checkDisposed();
584     return m_xDocument->retrieveDefaultAttributes( this, RequestedAttributes );
585 }
586 
587 // virtual
588 ::css::uno::Sequence< ::css::beans::PropertyValue > SAL_CALL
589 ParagraphImpl::getRunAttributes(::sal_Int32 Index, const ::css::uno::Sequence< ::rtl::OUString >& RequestedAttributes)
590     throw (::css::lang::IndexOutOfBoundsException,
591            ::css::uno::RuntimeException)
592 {
593     checkDisposed();
594     return m_xDocument->retrieveRunAttributes( this, Index, RequestedAttributes );
595 }
596 
597 // virtual
598 ::sal_Int32 SAL_CALL ParagraphImpl::getLineNumberAtIndex( ::sal_Int32 nIndex )
599     throw (::css::lang::IndexOutOfBoundsException,
600            ::css::uno::RuntimeException)
601 {
602     checkDisposed();
603 
604     ::sal_Int32 nLineNo = -1;
605     ::css::i18n::Boundary aBoundary =
606         m_xDocument->retrieveParagraphLineBoundary( this, nIndex, &nLineNo );
607 
608     return nLineNo;
609 }
610 
611 // virtual
612 ::css::accessibility::TextSegment SAL_CALL ParagraphImpl::getTextAtLineNumber( ::sal_Int32 nLineNo )
613     throw (::css::lang::IndexOutOfBoundsException,
614            ::css::uno::RuntimeException)
615 {
616     checkDisposed();
617 
618     ::css::i18n::Boundary aBoundary =
619         m_xDocument->retrieveParagraphBoundaryOfLine( this, nLineNo );
620 
621     return ::css::accessibility::TextSegment( getTextRange(aBoundary.startPos, aBoundary.endPos),
622         aBoundary.startPos, aBoundary.endPos);
623 }
624 
625 // virtual
626 ::css::accessibility::TextSegment SAL_CALL ParagraphImpl::getTextAtLineWithCaret(  )
627     throw (::css::uno::RuntimeException)
628 {
629     checkDisposed();
630 
631     sal_Int32 nLineNo = getNumberOfLineWithCaret();
632 
633     try {
634         return ( nLineNo >= 0 ) ?
635             getTextAtLineNumber( nLineNo ) :
636             ::css::accessibility::TextSegment();
637     } catch (const ::css::lang::IndexOutOfBoundsException&) {
638         throw ::css::uno::RuntimeException(
639             ::rtl::OUString(
640                 RTL_CONSTASCII_USTRINGPARAM(
641                     "textwindowaccessibility.cxx:"
642                     " ParagraphImpl::getTextAtLineWithCaret") ),
643             static_cast< ::css::uno::XWeak * >( this ) );
644     }
645 }
646 
647 // virtual
648 ::sal_Int32 SAL_CALL ParagraphImpl::getNumberOfLineWithCaret(  )
649     throw (::css::uno::RuntimeException)
650 {
651     checkDisposed();
652     return m_xDocument->retrieveParagraphLineWithCursor(this);
653 }
654 
655 
656 // virtual
657 void SAL_CALL ParagraphImpl::addEventListener(
658     ::css::uno::Reference<
659     ::css::accessibility::XAccessibleEventListener > const & rListener)
660     throw (::css::uno::RuntimeException)
661 {
662     if (rListener.is())
663     {
664         ::osl::ClearableMutexGuard aGuard(rBHelper.rMutex);
665         if (rBHelper.bDisposed || rBHelper.bInDispose)
666         {
667             aGuard.clear();
668             rListener->disposing(::css::lang::EventObject(
669                                     static_cast< ::cppu::OWeakObject * >(this)));
670         }
671         else
672         {
673 			if (!m_nClientId)
674                 m_nClientId = comphelper::AccessibleEventNotifier::registerClient( );
675 			comphelper::AccessibleEventNotifier::addEventListener( m_nClientId, rListener );
676 		}
677     }
678 }
679 
680 // virtual
681 void SAL_CALL ParagraphImpl::removeEventListener(
682     ::css::uno::Reference<
683     ::css::accessibility::XAccessibleEventListener > const & rListener)
684     throw (::css::uno::RuntimeException)
685 {
686 	comphelper::AccessibleEventNotifier::TClientId nId = 0;
687 	{
688         ::osl::ClearableMutexGuard aGuard(rBHelper.rMutex);
689 		if (rListener.is() && m_nClientId != 0
690 			&& comphelper::AccessibleEventNotifier::removeEventListener( m_nClientId, rListener ) == 0)
691 		{
692 			nId = m_nClientId;
693 			m_nClientId = 0;
694 		}
695 	}
696 	if (nId != 0)
697 	{
698 		// no listeners anymore
699 		// -> revoke ourself. This may lead to the notifier thread dying (if we were the last client),
700 		// and at least to us not firing any events anymore, in case somebody calls
701 		// NotifyAccessibleEvent, again
702 		comphelper::AccessibleEventNotifier::revokeClient(nId);
703 	}
704 }
705 
706 // virtual
707 void SAL_CALL ParagraphImpl::disposing()
708 {
709 	comphelper::AccessibleEventNotifier::TClientId nId = 0;
710 	{
711         ::osl::ClearableMutexGuard aGuard(rBHelper.rMutex);
712 		nId = m_nClientId;
713 		m_nClientId = 0;
714 	}
715 	if (nId != 0)
716         comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing(nId, *this);
717 }
718 
719 // virtual
720 ::rtl::OUString ParagraphImpl::implGetText()
721 {
722     return m_xDocument->retrieveParagraphText(this);
723 }
724 
725 // virtual
726 ::css::lang::Locale ParagraphImpl::implGetLocale()
727 {
728     return m_xDocument->retrieveLocale();
729 }
730 
731 // virtual
732 void ParagraphImpl::implGetSelection(::sal_Int32 & rStartIndex,
733                                      ::sal_Int32 & rEndIndex)
734 {
735     m_xDocument->retrieveParagraphSelection(this, &rStartIndex, &rEndIndex);
736 }
737 
738 // virtual
739 void ParagraphImpl::implGetParagraphBoundary( ::css::i18n::Boundary& rBoundary,
740                                               ::sal_Int32 nIndex )
741 {
742     ::rtl::OUString sText( implGetText() );
743     ::sal_Int32 nLength = sText.getLength();
744 
745     if ( implIsValidIndex( nIndex, nLength ) )
746     {
747         rBoundary.startPos = 0;
748         rBoundary.endPos = nLength;
749     }
750     else
751     {
752         rBoundary.startPos = nIndex;
753         rBoundary.endPos = nIndex;
754     }
755 }
756 
757 // virtual
758 void ParagraphImpl::implGetLineBoundary( ::css::i18n::Boundary& rBoundary,
759                                          ::sal_Int32 nIndex )
760 {
761     ::rtl::OUString sText( implGetText() );
762     ::sal_Int32 nLength = sText.getLength();
763 
764     if ( implIsValidIndex( nIndex, nLength ) || nIndex == nLength )
765     {
766         ::css::i18n::Boundary aBoundary =
767             m_xDocument->retrieveParagraphLineBoundary( this, nIndex );
768         rBoundary.startPos = aBoundary.startPos;
769         rBoundary.endPos = aBoundary.endPos;
770     }
771     else
772     {
773         rBoundary.startPos = nIndex;
774         rBoundary.endPos = nIndex;
775     }
776 }
777 
778 
779 void ParagraphImpl::checkDisposed()
780 {
781     ::osl::MutexGuard aGuard(rBHelper.rMutex);
782     if (!(rBHelper.bDisposed || rBHelper.bInDispose))
783         return;
784     throw ::css::lang::DisposedException(
785         ::rtl::OUString(), static_cast< ::css::uno::XWeak * >(this));
786 }
787 
788 Document::Document(::VCLXWindow * pVclXWindow, ::TextEngine & rEngine,
789                    ::TextView & rView, bool bCompoundControlChild):
790     VCLXAccessibleComponent(pVclXWindow),
791     m_xAccessible(pVclXWindow),
792     m_rEngine(rEngine),
793     m_rView(rView),
794     m_aEngineListener(*this),
795     m_aViewListener(LINK(this, Document, WindowEventHandler)),
796 	m_bCompoundControlChild(bCompoundControlChild)
797 {}
798 
799 ::css::lang::Locale Document::retrieveLocale()
800 {
801     ::osl::Guard< ::comphelper::IMutex > aExternalGuard(getExternalLock());
802     return m_rEngine.GetLocale();
803 }
804 
805 ::sal_Int32 Document::retrieveParagraphIndex(ParagraphImpl const * pParagraph)
806 {
807     ::osl::MutexGuard aInternalGuard(GetMutex());
808 
809     // If a client holds on to a Paragraph that is no longer visible, it can
810     // happen that this Paragraph lies outside the range from m_aVisibleBegin
811     // to m_aVisibleEnd.  In that case, return -1 instead of a valid index:
812     Paragraphs::iterator aPara(m_xParagraphs->begin()
813                                + pParagraph->getNumber());
814     return aPara < m_aVisibleBegin || aPara >= m_aVisibleEnd
815         ? -1 : static_cast< ::sal_Int32 >(aPara - m_aVisibleBegin);
816         // XXX  numeric overflow
817 }
818 
819 ::sal_Int64 Document::retrieveParagraphState(ParagraphImpl const * pParagraph)
820 {
821     ::osl::MutexGuard aInternalGuard(GetMutex());
822 
823     // If a client holds on to a Paragraph that is no longer visible, it can
824     // happen that this Paragraph lies outside the range from m_aVisibleBegin
825     // to m_aVisibleEnd.  In that case, it is neither VISIBLE nor SHOWING:
826     ::sal_Int64 nState
827           = (static_cast< ::sal_Int64 >(1)
828              << ::css::accessibility::AccessibleStateType::ENABLED)
829           | (static_cast< ::sal_Int64 >(1)
830              << ::css::accessibility::AccessibleStateType::SENSITIVE)
831           | (static_cast< ::sal_Int64 >(1)
832              << ::css::accessibility::AccessibleStateType::FOCUSABLE)
833           | (static_cast< ::sal_Int64 >(1)
834              << ::css::accessibility::AccessibleStateType::MULTI_LINE);
835     if (!m_rView.IsReadOnly())
836         nState |= (static_cast< ::sal_Int64 >(1)
837                    << ::css::accessibility::AccessibleStateType::EDITABLE);
838     Paragraphs::iterator aPara(m_xParagraphs->begin()
839                                + pParagraph->getNumber());
840     if (aPara >= m_aVisibleBegin && aPara < m_aVisibleEnd)
841     {
842         nState
843             |= (static_cast< ::sal_Int64 >(1)
844                 << ::css::accessibility::AccessibleStateType::VISIBLE)
845             | (static_cast< ::sal_Int64 >(1)
846                << ::css::accessibility::AccessibleStateType::SHOWING);
847         if (aPara == m_aFocused)
848             nState |= (static_cast< ::sal_Int64 >(1)
849                        << ::css::accessibility::AccessibleStateType::FOCUSED);
850     }
851     return nState;
852 };
853 
854 ::css::awt::Rectangle
855 Document::retrieveParagraphBounds(ParagraphImpl const * pParagraph,
856                                   bool bAbsolute)
857 {
858     ::osl::Guard< ::comphelper::IMutex > aExternalGuard(getExternalLock());
859     ::osl::MutexGuard aInternalGuard(GetMutex());
860 
861     // If a client holds on to a Paragraph that is no longer visible (as it
862     // scrolled out the top of the view), it can happen that this Paragraph
863     // lies before m_aVisibleBegin.  In that case, calculate the vertical
864     // position of the Paragraph starting at paragraph 0, otherwise optimize
865     // and start at m_aVisibleBegin:
866     Paragraphs::iterator aPara(m_xParagraphs->begin()
867                                + pParagraph->getNumber());
868     ::sal_Int32 nPos;
869     Paragraphs::iterator aIt;
870     if (aPara < m_aVisibleBegin)
871     {
872         nPos = 0;
873         aIt = m_xParagraphs->begin();
874     }
875     else
876     {
877         nPos = m_nViewOffset - m_nVisibleBeginOffset;
878         aIt = m_aVisibleBegin;
879     }
880     for (; aIt != aPara; ++aIt)
881         nPos += aIt->getHeight();
882 
883     Point aOrig(0, 0);
884     if (bAbsolute)
885         aOrig = m_rView.GetWindow()->OutputToAbsoluteScreenPixel(aOrig);
886 
887     return ::css::awt::Rectangle(
888         static_cast< ::sal_Int32 >(aOrig.X()),
889         static_cast< ::sal_Int32 >(aOrig.Y()) + nPos - m_nViewOffset,
890         m_rView.GetWindow()->GetOutputSizePixel().Width(), aPara->getHeight());
891         // XXX  numeric overflow (3x)
892 }
893 
894 ::rtl::OUString
895 Document::retrieveParagraphText(ParagraphImpl const * pParagraph)
896 {
897     ::osl::Guard< ::comphelper::IMutex > aExternalGuard(getExternalLock());
898     ::osl::MutexGuard aInternalGuard(GetMutex());
899     return m_rEngine.GetText(static_cast< ::sal_uLong >(pParagraph->getNumber()));
900         // numeric overflow cannot happen here
901 }
902 
903 void Document::retrieveParagraphSelection(ParagraphImpl const * pParagraph,
904                                           ::sal_Int32 * pBegin,
905                                           ::sal_Int32 * pEnd)
906 {
907     ::osl::Guard< ::comphelper::IMutex > aExternalGuard(getExternalLock());
908     ::osl::MutexGuard aInternalGuard(GetMutex());
909     ::TextSelection const & rSelection = m_rView.GetSelection();
910     Paragraphs::size_type nNumber = pParagraph->getNumber();
911     TextPaM aStartPaM( rSelection.GetStart() );
912     TextPaM aEndPaM( rSelection.GetEnd() );
913     TextPaM aMinPaM( ::std::min( aStartPaM, aEndPaM ) );
914     TextPaM aMaxPaM( ::std::max( aStartPaM, aEndPaM ) );
915 
916     if ( nNumber >= aMinPaM.GetPara() && nNumber <= aMaxPaM.GetPara() )
917     {
918         *pBegin = nNumber > aMinPaM.GetPara()
919             ? 0
920             : static_cast< ::sal_Int32 >( aMinPaM.GetIndex() );
921             // XXX numeric overflow
922         *pEnd = nNumber < aMaxPaM.GetPara()
923             ? static_cast< ::sal_Int32 >( m_rEngine.GetText(static_cast< ::sal_uLong >(nNumber)).Len() )
924             : static_cast< ::sal_Int32 >( aMaxPaM.GetIndex() );
925             // XXX  numeric overflow (3x)
926 
927         if ( aStartPaM > aEndPaM )
928             ::std::swap( *pBegin, *pEnd );
929     }
930     else
931     {
932         *pBegin = 0;
933         *pEnd = 0;
934     }
935 }
936 
937 ::sal_Int32 Document::retrieveParagraphCaretPosition(ParagraphImpl const * pParagraph)
938 {
939     ::osl::Guard< ::comphelper::IMutex > aExternalGuard(getExternalLock());
940     ::osl::MutexGuard aInternalGuard(GetMutex());
941     ::TextSelection const & rSelection = m_rView.GetSelection();
942     Paragraphs::size_type nNumber = pParagraph->getNumber();
943     TextPaM aEndPaM( rSelection.GetEnd() );
944 
945     return aEndPaM.GetPara() == nNumber
946         ? static_cast< ::sal_Int32 >(aEndPaM.GetIndex()) : -1;
947 }
948 
949 ::css::awt::Rectangle
950 Document::retrieveCharacterBounds(ParagraphImpl const * pParagraph,
951                                   ::sal_Int32 nIndex)
952 {
953     ::osl::Guard< ::comphelper::IMutex > aExternalGuard(getExternalLock());
954     ::osl::MutexGuard aInternalGuard(GetMutex());
955     ::sal_uLong nNumber = static_cast< ::sal_uLong >(pParagraph->getNumber());
956     sal_Int32 nLength = m_rEngine.GetText(nNumber).Len();
957         // XXX  numeric overflow
958     if (nIndex < 0 || nIndex > nLength)
959         throw ::css::lang::IndexOutOfBoundsException(
960             ::rtl::OUString(
961                 RTL_CONSTASCII_USTRINGPARAM(
962                     "textwindowaccessibility.cxx:"
963                     " Document::retrieveCharacterAttributes")),
964             static_cast< ::css::uno::XWeak * >(this));
965     ::css::awt::Rectangle aBounds( 0, 0, 0, 0 );
966     if ( nIndex == nLength )
967     {
968         aBounds = AWTRectangle(
969             m_rEngine.PaMtoEditCursor(::TextPaM(nNumber,
970                                                 static_cast< ::sal_uInt16 >(nIndex))));
971     }
972     else
973     {
974         ::Rectangle aLeft(
975             m_rEngine.PaMtoEditCursor(::TextPaM(nNumber,
976                                                 static_cast< ::sal_uInt16 >(nIndex))));
977             // XXX  numeric overflow
978         ::Rectangle aRight(
979             m_rEngine.PaMtoEditCursor(::TextPaM(nNumber,
980                                                 static_cast< ::sal_uInt16 >(nIndex)
981                                                 + 1)));
982             // XXX  numeric overflow (2x)
983         // FIXME  If the vertical extends of the two cursors do not match, assume
984         // nIndex is the last character on the line; the bounding box will then
985         // extend to m_rEnginge.GetMaxTextWidth():
986         ::sal_Int32 nWidth = (aLeft.Top() == aRight.Top()
987                             && aLeft.Bottom() == aRight.Bottom())
988             ? static_cast< ::sal_Int32 >(aRight.Left() - aLeft.Left())
989             : static_cast< ::sal_Int32 >(m_rEngine.GetMaxTextWidth()
990                                         - aLeft.Left());
991             // XXX  numeric overflow (4x)
992         aBounds = ::css::awt::Rectangle(static_cast< ::sal_Int32 >(aLeft.Left()),
993                                         static_cast< ::sal_Int32 >(aLeft.Top() - m_nViewOffset),
994                                         nWidth,
995                                         static_cast< ::sal_Int32 >(aLeft.Bottom()
996                                                                     - aLeft.Top()));
997             // XXX  numeric overflow (4x)
998     }
999     return aBounds;
1000 }
1001 
1002 ::sal_Int32 Document::retrieveCharacterIndex(ParagraphImpl const * pParagraph,
1003                                              ::css::awt::Point const & rPoint)
1004 {
1005     ::osl::Guard< ::comphelper::IMutex > aExternalGuard(getExternalLock());
1006     ::osl::MutexGuard aInternalGuard(GetMutex());
1007     ::sal_uLong nNumber = static_cast< ::sal_uLong >(pParagraph->getNumber());
1008         // XXX  numeric overflow
1009     ::TextPaM aPaM(m_rEngine.GetPaM(::Point(static_cast< long >(rPoint.X),
1010                                             static_cast< long >(rPoint.Y))));
1011         // XXX  numeric overflow (2x)
1012     return aPaM.GetPara() == nNumber
1013         ? static_cast< ::sal_Int32 >(aPaM.GetIndex()) : -1;
1014         // XXX  numeric overflow
1015 }
1016 
1017 struct IndexCompare
1018 {
1019 	const ::css::beans::PropertyValue* pValues;
1020 	IndexCompare( const ::css::beans::PropertyValue* pVals ) : pValues(pVals) {}
1021 	bool operator() ( const sal_Int32& a, const sal_Int32& b ) const
1022 	{
1023 		return (pValues[a].Name < pValues[b].Name) ? true : false;
1024 	}
1025 };
1026 
1027 ::css::uno::Sequence< ::css::beans::PropertyValue >
1028 Document::retrieveCharacterAttributes(
1029     ParagraphImpl const * pParagraph, ::sal_Int32 nIndex,
1030     const ::css::uno::Sequence< ::rtl::OUString >& aRequestedAttributes)
1031 {
1032     ::osl::Guard< ::comphelper::IMutex > aExternalGuard(getExternalLock());
1033 
1034 	Font aFont = m_rEngine.GetFont();
1035 	const sal_Int32 AttributeCount = 9;
1036 	sal_Int32 i = 0;
1037 	::css::uno::Sequence< ::css::beans::PropertyValue > aAttribs( AttributeCount );
1038 	//character background color
1039 	{
1040 		aAttribs[i].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("CharBackColor"));
1041 		aAttribs[i].Handle = -1;
1042 		aAttribs[i].Value = mapFontColor( aFont.GetFillColor() );
1043 		aAttribs[i].State = ::css::beans::PropertyState_DIRECT_VALUE;
1044 		i++;
1045 	}
1046 	//character color
1047 	{
1048 		aAttribs[i].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("CharColor"));
1049 		aAttribs[i].Handle = -1;
1050 		//aAttribs[i].Value = mapFontColor( aFont.GetColor() );
1051 		aAttribs[i].Value = mapFontColor( m_rEngine.GetTextColor() );
1052 		aAttribs[i].State = ::css::beans::PropertyState_DIRECT_VALUE;
1053 		i++;
1054 	}
1055 	//character font name
1056 	{
1057 		aAttribs[i].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("CharFontName"));
1058 		aAttribs[i].Handle = -1;
1059 		aAttribs[i].Value = ::css::uno::makeAny( (::rtl::OUString)aFont.GetName() );
1060 		aAttribs[i].State = ::css::beans::PropertyState_DIRECT_VALUE;
1061 		i++;
1062 	}
1063 	//character height
1064 	{
1065 		aAttribs[i].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("CharHeight"));
1066 		aAttribs[i].Handle = -1;
1067 		aAttribs[i].Value = ::css::uno::makeAny( (sal_Int16)aFont.GetHeight() );
1068 		aAttribs[i].State = ::css::beans::PropertyState_DIRECT_VALUE;
1069 		i++;
1070 	}
1071 	//character posture
1072 	{
1073 		aAttribs[i].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("CharPosture"));
1074 		aAttribs[i].Handle = -1;
1075 		aAttribs[i].Value = ::css::uno::makeAny( (sal_Int16)aFont.GetItalic() );
1076 		aAttribs[i].State = ::css::beans::PropertyState_DIRECT_VALUE;
1077 		i++;
1078 	}
1079 	//character relief
1080 	/*{
1081 		aAttribs[i].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("CharRelief"));
1082 		aAttribs[i].Handle = -1;
1083 		aAttribs[i].Value = ::css::uno::makeAny( (sal_Int16)aFont.GetRelief() );
1084 		aAttribs[i].State = ::css::beans::PropertyState_DIRECT_VALUE;
1085 		i++;
1086 	}*/
1087 	//character strikeout
1088 	{
1089 		aAttribs[i].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("CharStrikeout"));
1090 		aAttribs[i].Handle = -1;
1091 		aAttribs[i].Value = ::css::uno::makeAny( (sal_Int16)aFont.GetStrikeout() );
1092 		aAttribs[i].State = ::css::beans::PropertyState_DIRECT_VALUE;
1093 		i++;
1094 	}
1095 	//character underline
1096 	{
1097 		aAttribs[i].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("CharUnderline"));
1098 		aAttribs[i].Handle = -1;
1099 		aAttribs[i].Value = ::css::uno::makeAny( (sal_Int16)aFont.GetUnderline() );
1100 		aAttribs[i].State = ::css::beans::PropertyState_DIRECT_VALUE;
1101 		i++;
1102 	}
1103 	//character weight
1104 	{
1105 		aAttribs[i].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("CharWeight"));
1106 		aAttribs[i].Handle = -1;
1107 		aAttribs[i].Value = ::css::uno::makeAny( (float)aFont.GetWeight() );
1108 		aAttribs[i].State = ::css::beans::PropertyState_DIRECT_VALUE;
1109 		i++;
1110 	}
1111 	//character alignment
1112 	{
1113 		aAttribs[i].Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("ParaAdjust"));
1114 		aAttribs[i].Handle = -1;
1115 		aAttribs[i].Value = ::css::uno::makeAny( (sal_Int16)m_rEngine.GetTextAlign() );
1116 		aAttribs[i].State = ::css::beans::PropertyState_DIRECT_VALUE;
1117 		i++;
1118 	}
1119     ::osl::MutexGuard aInternalGuard(GetMutex());
1120     ::sal_uLong nNumber = static_cast< ::sal_uLong >(pParagraph->getNumber());
1121         // XXX  numeric overflow
1122 	// nIndex can be equal to Len();
1123     //if (nIndex < 0 || nIndex >= m_rEngine.GetText(nNumber).Len())
1124 	if (nIndex < 0 || nIndex > m_rEngine.GetText(nNumber).Len())
1125         throw ::css::lang::IndexOutOfBoundsException(
1126             ::rtl::OUString(
1127                 RTL_CONSTASCII_USTRINGPARAM(
1128                     "textwindowaccessibility.cxx:"
1129                     " Document::retrieveCharacterAttributes")),
1130             static_cast< ::css::uno::XWeak * >(this));
1131 
1132     // retrieve default attributes
1133     tPropValMap aCharAttrSeq;
1134     retrieveDefaultAttributesImpl( pParagraph, aRequestedAttributes, aCharAttrSeq );
1135 
1136     // retrieve run attributes
1137     tPropValMap aRunAttrSeq;
1138     retrieveRunAttributesImpl( pParagraph, nIndex, aRequestedAttributes, aRunAttrSeq );
1139 
1140     // merge default and run attributes
1141     for ( tPropValMap::const_iterator aRunIter  = aRunAttrSeq.begin();
1142           aRunIter != aRunAttrSeq.end();
1143           ++aRunIter )
1144     {
1145         aCharAttrSeq[ aRunIter->first ] = aRunIter->second;
1146     }
1147 
1148 	::css::beans::PropertyValue* pValues = aAttribs.getArray();
1149 	for (i = 0; i < AttributeCount; i++,pValues++)
1150 	{
1151 		aCharAttrSeq[ pValues->Name ] = *pValues;
1152 	}
1153 
1154     ::css::uno::Sequence< ::css::beans::PropertyValue > aRes = convertHashMapToSequence( aCharAttrSeq );
1155 
1156 	// sort the attributes
1157 	sal_Int32 nLength = aRes.getLength();
1158 	const ::css::beans::PropertyValue* pPairs = aRes.getConstArray();
1159 	sal_Int32* pIndices = new sal_Int32[nLength];
1160 	for( i = 0; i < nLength; i++ )
1161 		pIndices[i] = i;
1162 	std::sort( &pIndices[0], &pIndices[nLength], IndexCompare(pPairs) );
1163 	// create sorted sequences according to index array
1164 	::css::uno::Sequence< ::css::beans::PropertyValue > aNewValues( nLength );
1165 	::css::beans::PropertyValue* pNewValues = aNewValues.getArray();
1166 	for( i = 0; i < nLength; i++ )
1167 	{
1168 		pNewValues[i] = pPairs[pIndices[i]];
1169 	}
1170 	delete[] pIndices;
1171 
1172 	return aNewValues;
1173 }
1174 
1175 void Document::retrieveDefaultAttributesImpl(
1176     ParagraphImpl const * pParagraph,
1177     const ::css::uno::Sequence< ::rtl::OUString >& RequestedAttributes,
1178     tPropValMap& rDefAttrSeq)
1179 {
1180     // default attributes are not supported by text engine
1181     (void) pParagraph;
1182     (void) RequestedAttributes;
1183     (void) rDefAttrSeq;
1184 }
1185 
1186 ::css::uno::Sequence< ::css::beans::PropertyValue >
1187 Document::retrieveDefaultAttributes(
1188     ParagraphImpl const * pParagraph,
1189     const ::css::uno::Sequence< ::rtl::OUString >& RequestedAttributes)
1190 {
1191     ::osl::Guard< ::comphelper::IMutex > aExternalGuard( getExternalLock() );
1192     ::osl::MutexGuard aInternalGuard( GetMutex() );
1193 
1194     tPropValMap aDefAttrSeq;
1195     retrieveDefaultAttributesImpl( pParagraph, RequestedAttributes, aDefAttrSeq );
1196     return convertHashMapToSequence( aDefAttrSeq );
1197 }
1198 
1199 // static
1200 ::css::uno::Sequence< ::css::beans::PropertyValue >
1201 Document::convertHashMapToSequence(tPropValMap& rAttrSeq)
1202 {
1203     ::css::uno::Sequence< ::css::beans::PropertyValue > aValues( rAttrSeq.size() );
1204     ::css::beans::PropertyValue* pValues = aValues.getArray();
1205     ::sal_Int32 i = 0;
1206     for ( tPropValMap::const_iterator aIter  = rAttrSeq.begin();
1207           aIter != rAttrSeq.end();
1208           ++aIter )
1209     {
1210         pValues[i] = aIter->second;
1211         ++i;
1212     }
1213     return aValues;
1214 }
1215 
1216 void Document::retrieveRunAttributesImpl(
1217     ParagraphImpl const * pParagraph, ::sal_Int32 Index,
1218     const ::css::uno::Sequence< ::rtl::OUString >& RequestedAttributes,
1219     tPropValMap& rRunAttrSeq)
1220 {
1221     ::sal_uLong nNumber = static_cast< ::sal_uLong >( pParagraph->getNumber() );
1222     ::TextPaM aPaM( nNumber, static_cast< ::sal_uInt16 >( Index ) );
1223         // XXX  numeric overflow
1224     // FIXME  TEXTATTR_HYPERLINK ignored:
1225     ::TextAttribFontColor const * pColor
1226           = static_cast< ::TextAttribFontColor const * >(
1227               m_rEngine.FindAttrib( aPaM, TEXTATTR_FONTCOLOR ) );
1228     ::TextAttribFontWeight const * pWeight
1229           = static_cast< ::TextAttribFontWeight const * >(
1230               m_rEngine.FindAttrib( aPaM, TEXTATTR_FONTWEIGHT ) );
1231     tPropValMap aRunAttrSeq;
1232     if ( pColor )
1233     {
1234         ::css::beans::PropertyValue aPropVal;
1235         aPropVal.Name =
1236             ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "CharColor" ) );
1237         aPropVal.Handle = -1;
1238         aPropVal.Value = mapFontColor( pColor->GetColor() );
1239         aPropVal.State = ::css::beans::PropertyState_DIRECT_VALUE;
1240         aRunAttrSeq[ aPropVal.Name ] = aPropVal;
1241     }
1242     if ( pWeight )
1243     {
1244         ::css::beans::PropertyValue aPropVal;
1245         aPropVal.Name =
1246             ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "CharWeight" ) );
1247         aPropVal.Handle = -1;
1248         aPropVal.Value = mapFontWeight( pWeight->getFontWeight() );
1249         aPropVal.State = ::css::beans::PropertyState_DIRECT_VALUE;
1250         aRunAttrSeq[ aPropVal.Name ] = aPropVal;
1251     }
1252     if ( RequestedAttributes.getLength() == 0 )
1253     {
1254         rRunAttrSeq = aRunAttrSeq;
1255     }
1256     else
1257     {
1258         const ::rtl::OUString* pReqAttrs = RequestedAttributes.getConstArray();
1259         const ::sal_Int32 nLength = RequestedAttributes.getLength();
1260         for ( ::sal_Int32 i = 0; i < nLength; ++i )
1261         {
1262             tPropValMap::iterator aIter = aRunAttrSeq.find( pReqAttrs[i] );
1263             if ( aIter != aRunAttrSeq.end() )
1264             {
1265                 rRunAttrSeq[ (*aIter).first ] = (*aIter).second;
1266             }
1267         }
1268     }
1269 }
1270 
1271 ::css::uno::Sequence< ::css::beans::PropertyValue >
1272 Document::retrieveRunAttributes(
1273     ParagraphImpl const * pParagraph, ::sal_Int32 Index,
1274     const ::css::uno::Sequence< ::rtl::OUString >& RequestedAttributes)
1275 {
1276     ::osl::Guard< ::comphelper::IMutex > aExternalGuard( getExternalLock() );
1277     ::osl::MutexGuard aInternalGuard( GetMutex() );
1278     ::sal_uLong nNumber = static_cast< ::sal_uLong >( pParagraph->getNumber() );
1279         // XXX  numeric overflow
1280     if ( Index < 0 || Index >= m_rEngine.GetText(nNumber).Len() )
1281         throw ::css::lang::IndexOutOfBoundsException(
1282             ::rtl::OUString(
1283                 RTL_CONSTASCII_USTRINGPARAM(
1284                     "textwindowaccessibility.cxx:"
1285                     " Document::retrieveRunAttributes") ),
1286             static_cast< ::css::uno::XWeak * >( this ) );
1287 
1288     tPropValMap aRunAttrSeq;
1289     retrieveRunAttributesImpl( pParagraph, Index, RequestedAttributes, aRunAttrSeq );
1290     return convertHashMapToSequence( aRunAttrSeq );
1291 }
1292 
1293 void Document::changeParagraphText(ParagraphImpl * pParagraph,
1294                                    ::rtl::OUString const & rText)
1295 {
1296     ::osl::Guard< ::comphelper::IMutex > aExternalGuard(getExternalLock());
1297     {
1298         ::osl::MutexGuard aInternalGuard(GetMutex());
1299         ::sal_uLong nNumber = static_cast< ::sal_uLong >(pParagraph->getNumber());
1300             // XXX  numeric overflow
1301         changeParagraphText(nNumber, 0, m_rEngine.GetTextLen(nNumber), false,
1302                             false, rText);
1303     }
1304 }
1305 
1306 void Document::changeParagraphText(ParagraphImpl * pParagraph,
1307                                    ::sal_Int32 nBegin, ::sal_Int32 nEnd,
1308                                    bool bCut, bool bPaste,
1309                                    ::rtl::OUString const & rText)
1310 {
1311     ::osl::Guard< ::comphelper::IMutex > aExternalGuard(getExternalLock());
1312     {
1313         ::osl::MutexGuard aInternalGuard(GetMutex());
1314         ::sal_uLong nNumber = static_cast< ::sal_uLong >(pParagraph->getNumber());
1315             // XXX  numeric overflow
1316         if (nBegin < 0 || nBegin > nEnd
1317             || nEnd > m_rEngine.GetText(nNumber).Len())
1318             throw ::css::lang::IndexOutOfBoundsException(
1319                 ::rtl::OUString(
1320                     RTL_CONSTASCII_USTRINGPARAM(
1321                         "textwindowaccessibility.cxx:"
1322                         " Document::changeParagraphText")),
1323                 static_cast< ::css::uno::XWeak * >(this));
1324         changeParagraphText(nNumber, static_cast< ::sal_uInt16 >(nBegin),
1325                             static_cast< ::sal_uInt16 >(nEnd), bCut, bPaste, rText);
1326             // XXX  numeric overflow (2x)
1327     }
1328 }
1329 
1330 void Document::copyParagraphText(ParagraphImpl const * pParagraph,
1331                                  ::sal_Int32 nBegin, ::sal_Int32 nEnd)
1332 {
1333     ::osl::Guard< ::comphelper::IMutex > aExternalGuard(getExternalLock());
1334     {
1335         ::osl::MutexGuard aInternalGuard(GetMutex());
1336         ::sal_uLong nNumber = static_cast< ::sal_uLong >(pParagraph->getNumber());
1337             // XXX  numeric overflow
1338         if (nBegin < 0 || nBegin > nEnd
1339             || nEnd > m_rEngine.GetText(nNumber).Len())
1340             throw ::css::lang::IndexOutOfBoundsException(
1341                 ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(
1342                                     "textwindowaccessibility.cxx:"
1343                                     " Document::copyParagraphText")),
1344                 static_cast< ::css::uno::XWeak * >(this));
1345         m_rView.SetSelection(
1346             ::TextSelection(::TextPaM(nNumber, static_cast< ::sal_uInt16 >(nBegin)),
1347                             ::TextPaM(nNumber, static_cast< ::sal_uInt16 >(nEnd))));
1348             // XXX  numeric overflow (2x)
1349         m_rView.Copy();
1350     }
1351 }
1352 
1353 void Document::changeParagraphAttributes(
1354     ParagraphImpl * pParagraph, ::sal_Int32 nBegin, ::sal_Int32 nEnd,
1355     ::css::uno::Sequence< ::css::beans::PropertyValue > const & rAttributeSet)
1356 {
1357     ::osl::Guard< ::comphelper::IMutex > aExternalGuard(getExternalLock());
1358     {
1359         ::osl::MutexGuard aInternalGuard(GetMutex());
1360         ::sal_uLong nNumber = static_cast< ::sal_uLong >(pParagraph->getNumber());
1361         // XXX  numeric overflow
1362         if (nBegin < 0 || nBegin > nEnd
1363             || nEnd > m_rEngine.GetText(nNumber).Len())
1364             throw ::css::lang::IndexOutOfBoundsException(
1365                 ::rtl::OUString(
1366                     RTL_CONSTASCII_USTRINGPARAM(
1367                         "textwindowaccessibility.cxx:"
1368                         " Document::changeParagraphAttributes")),
1369                 static_cast< ::css::uno::XWeak * >(this));
1370 
1371         // FIXME  The new attributes are added to any attributes already set,
1372         // they do not replace the old attributes as required by
1373         // XAccessibleEditableText.setAttributes:
1374         for (::sal_Int32 i = 0; i < rAttributeSet.getLength(); ++i)
1375             if (rAttributeSet[i].Name.equalsAsciiL(
1376                     RTL_CONSTASCII_STRINGPARAM("CharColor")))
1377                 m_rEngine.SetAttrib(::TextAttribFontColor(
1378                                         mapFontColor(rAttributeSet[i].Value)),
1379                                     nNumber, static_cast< ::sal_uInt16 >(nBegin),
1380                                     static_cast< ::sal_uInt16 >(nEnd));
1381                     // XXX  numeric overflow (2x)
1382             else if (rAttributeSet[i].Name.equalsAsciiL(
1383                          RTL_CONSTASCII_STRINGPARAM("CharWeight")))
1384                 m_rEngine.SetAttrib(::TextAttribFontWeight(
1385                                         mapFontWeight(rAttributeSet[i].Value)),
1386                                     nNumber, static_cast< ::sal_uInt16 >(nBegin),
1387                                     static_cast< ::sal_uInt16 >(nEnd));
1388                     // XXX  numeric overflow (2x)
1389     }
1390 }
1391 
1392 void Document::changeParagraphSelection(ParagraphImpl * pParagraph,
1393                                         ::sal_Int32 nBegin, ::sal_Int32 nEnd)
1394 {
1395     ::osl::Guard< ::comphelper::IMutex > aExternalGuard(getExternalLock());
1396     {
1397         ::osl::MutexGuard aInternalGuard(GetMutex());
1398         ::sal_uLong nNumber = static_cast< ::sal_uLong >(pParagraph->getNumber());
1399             // XXX  numeric overflow
1400         if (nBegin < 0 || nBegin > nEnd
1401             || nEnd > m_rEngine.GetText(nNumber).Len())
1402             throw ::css::lang::IndexOutOfBoundsException(
1403                 ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(
1404                                     "textwindowaccessibility.cxx:"
1405                                     " Document::changeParagraphSelection")),
1406                 static_cast< ::css::uno::XWeak * >(this));
1407         m_rView.SetSelection(
1408             ::TextSelection(::TextPaM(nNumber, static_cast< ::sal_uInt16 >(nBegin)),
1409                             ::TextPaM(nNumber, static_cast< ::sal_uInt16 >(nEnd))));
1410             // XXX  numeric overflow (2x)
1411     }
1412 }
1413 
1414 ::css::i18n::Boundary
1415 Document::retrieveParagraphLineBoundary( ParagraphImpl const * pParagraph,
1416                                          ::sal_Int32 nIndex, ::sal_Int32 *pLineNo )
1417 {
1418     ::css::i18n::Boundary aBoundary;
1419     aBoundary.startPos = nIndex;
1420     aBoundary.endPos = nIndex;
1421 
1422     ::osl::Guard< ::comphelper::IMutex > aExternalGuard( getExternalLock() );
1423     {
1424         ::osl::MutexGuard aInternalGuard( GetMutex() );
1425         ::sal_uLong nNumber = static_cast< ::sal_uLong >( pParagraph->getNumber() );
1426         if ( nIndex < 0 || nIndex > m_rEngine.GetText( nNumber ).Len() )
1427             throw ::css::lang::IndexOutOfBoundsException(
1428                 ::rtl::OUString(
1429                     RTL_CONSTASCII_USTRINGPARAM(
1430                         "textwindowaccessibility.cxx:"
1431                         " Document::retrieveParagraphLineBoundary" ) ),
1432                 static_cast< ::css::uno::XWeak * >( this ) );
1433         ::sal_Int32 nLineStart = 0;
1434         ::sal_Int32 nLineEnd = 0;
1435         ::sal_uInt16 nLineCount = m_rEngine.GetLineCount( nNumber );
1436         for ( ::sal_uInt16 nLine = 0; nLine < nLineCount; ++nLine )
1437         {
1438             ::sal_Int32 nLineLength = static_cast< ::sal_Int32 >(
1439                 m_rEngine.GetLineLen( nNumber, nLine ) );
1440             nLineStart = nLineEnd;
1441             nLineEnd += nLineLength;
1442             if ( nIndex >= nLineStart && ( ( nLine == nLineCount - 1 ) ? nIndex <= nLineEnd : nIndex < nLineEnd ) )
1443             {
1444                 aBoundary.startPos = nLineStart;
1445                 aBoundary.endPos = nLineEnd;
1446                 if( pLineNo )
1447                     pLineNo[0] = nLine;
1448                 break;
1449             }
1450         }
1451     }
1452 
1453     return aBoundary;
1454 }
1455 
1456 ::css::i18n::Boundary
1457 Document::retrieveParagraphBoundaryOfLine( ParagraphImpl const * pParagraph,
1458                                            ::sal_Int32 nLineNo )
1459 {
1460     ::css::i18n::Boundary aBoundary;
1461     aBoundary.startPos = 0;
1462     aBoundary.endPos = 0;
1463 
1464     ::osl::Guard< ::comphelper::IMutex > aExternalGuard( getExternalLock() );
1465     {
1466         ::osl::MutexGuard aInternalGuard( GetMutex() );
1467         ::sal_uLong nNumber = static_cast< ::sal_uLong >( pParagraph->getNumber() );
1468         if ( nLineNo >= m_rEngine.GetLineCount( nNumber ) )
1469             throw ::css::lang::IndexOutOfBoundsException(
1470                 ::rtl::OUString(
1471                     RTL_CONSTASCII_USTRINGPARAM(
1472                         "textwindowaccessibility.cxx:"
1473                         " Document::retrieveParagraphBoundaryOfLine" ) ),
1474                 static_cast< ::css::uno::XWeak * >( this ) );
1475         ::sal_Int32 nLineStart = 0;
1476         ::sal_Int32 nLineEnd = 0;
1477         for ( ::sal_uInt16 nLine = 0; nLine <= nLineNo; ++nLine )
1478         {
1479             ::sal_Int32 nLineLength = static_cast< ::sal_Int32 >(
1480                 m_rEngine.GetLineLen( nNumber, nLine ) );
1481             nLineStart = nLineEnd;
1482             nLineEnd += nLineLength;
1483         }
1484 
1485         aBoundary.startPos = nLineStart;
1486         aBoundary.endPos = nLineEnd;
1487     }
1488 
1489     return aBoundary;
1490 }
1491 
1492 sal_Int32 Document::retrieveParagraphLineWithCursor( ParagraphImpl const * pParagraph )
1493 {
1494     ::osl::Guard< ::comphelper::IMutex > aExternalGuard(getExternalLock());
1495     ::osl::MutexGuard aInternalGuard(GetMutex());
1496     ::TextSelection const & rSelection = m_rView.GetSelection();
1497     Paragraphs::size_type nNumber = pParagraph->getNumber();
1498     TextPaM aEndPaM( rSelection.GetEnd() );
1499 
1500     return aEndPaM.GetPara() == nNumber
1501         ? m_rView.GetLineNumberOfCursorInSelection() : -1;
1502 }
1503 
1504 
1505 ::css::uno::Reference< ::css::accessibility::XAccessibleRelationSet >
1506 Document::retrieveParagraphRelationSet( ParagraphImpl const * pParagraph )
1507 {
1508     ::osl::MutexGuard aInternalGuard( GetMutex() );
1509 
1510     ::utl::AccessibleRelationSetHelper* pRelationSetHelper = new ::utl::AccessibleRelationSetHelper();
1511     ::css::uno::Reference< ::css::accessibility::XAccessibleRelationSet > xSet = pRelationSetHelper;
1512 
1513     Paragraphs::iterator aPara( m_xParagraphs->begin() + pParagraph->getNumber() );
1514 
1515     if ( aPara > m_aVisibleBegin && aPara < m_aVisibleEnd )
1516     {
1517         ::css::uno::Sequence< ::css::uno::Reference< ::css::uno::XInterface > > aSequence(1);
1518         aSequence[0] = getAccessibleChild( aPara - 1 );
1519         ::css::accessibility::AccessibleRelation aRelation( ::css::accessibility::AccessibleRelationType::CONTENT_FLOWS_FROM, aSequence );
1520         pRelationSetHelper->AddRelation( aRelation );
1521     }
1522 
1523     if ( aPara >= m_aVisibleBegin && aPara < m_aVisibleEnd -1 )
1524     {
1525         ::css::uno::Sequence< ::css::uno::Reference< ::css::uno::XInterface > > aSequence(1);
1526         aSequence[0] = getAccessibleChild( aPara + 1 );
1527         ::css::accessibility::AccessibleRelation aRelation( ::css::accessibility::AccessibleRelationType::CONTENT_FLOWS_TO, aSequence );
1528         pRelationSetHelper->AddRelation( aRelation );
1529     }
1530 
1531     return xSet;
1532 }
1533 
1534 void Document::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent )
1535 {
1536 	VCLXAccessibleComponent::ProcessWindowEvent( rVclWindowEvent );
1537 }
1538 
1539 // virtual
1540 ::sal_Int32 SAL_CALL Document::getAccessibleChildCount()
1541     throw (::css::uno::RuntimeException)
1542 {
1543     ::comphelper::OExternalLockGuard aGuard(this);
1544     init();
1545     return m_aVisibleEnd - m_aVisibleBegin;
1546 }
1547 
1548 // virtual
1549 ::css::uno::Reference< ::css::accessibility::XAccessible > SAL_CALL
1550 Document::getAccessibleChild(::sal_Int32 i)
1551     throw (::css::lang::IndexOutOfBoundsException,
1552            ::css::uno::RuntimeException)
1553 {
1554     ::comphelper::OExternalLockGuard aGuard(this);
1555     init();
1556     if (i < 0 || i >= m_aVisibleEnd - m_aVisibleBegin)
1557         throw ::css::lang::IndexOutOfBoundsException(
1558             ::rtl::OUString(
1559                 RTL_CONSTASCII_USTRINGPARAM(
1560                     "textwindowaccessibility.cxx:"
1561                     " Document::getAccessibleChild")),
1562             static_cast< ::css::uno::XWeak * >(this));
1563     return getAccessibleChild(m_aVisibleBegin
1564                               + static_cast< Paragraphs::size_type >(i));
1565 }
1566 
1567 // virtual
1568 ::sal_Int16 SAL_CALL Document::getAccessibleRole()
1569     throw (::css::uno::RuntimeException)
1570 {
1571     return ::css::accessibility::AccessibleRole::TEXT_FRAME;
1572 }
1573 
1574 // virtual
1575 ::css::uno::Reference< ::css::accessibility::XAccessible > SAL_CALL
1576 Document::getAccessibleAtPoint(::css::awt::Point const & rPoint)
1577     throw (::css::uno::RuntimeException)
1578 {
1579     ::comphelper::OExternalLockGuard aGuard(this);
1580     init();
1581     if (rPoint.X >= 0
1582         && rPoint.X < m_rView.GetWindow()->GetOutputSizePixel().Width()
1583         && rPoint.Y >= 0 && rPoint.Y < m_nViewHeight)
1584     {
1585         ::sal_Int32 nOffset = m_nViewOffset + rPoint.Y; // XXX  numeric overflow
1586         ::sal_Int32 nPos = m_nViewOffset - m_nVisibleBeginOffset;
1587         for (Paragraphs::iterator aIt(m_aVisibleBegin); aIt != m_aVisibleEnd;
1588              ++aIt)
1589         {
1590             nPos += aIt->getHeight(); // XXX  numeric overflow
1591             if (nOffset < nPos)
1592                 return getAccessibleChild(aIt);
1593         }
1594     }
1595     return 0;
1596 }
1597 void Document::FillAccessibleStateSet( utl::AccessibleStateSetHelper& rStateSet )
1598 {
1599 	VCLXAccessibleComponent::FillAccessibleStateSet( rStateSet );
1600 	if (!m_rView.IsReadOnly())
1601 		rStateSet.AddState( ::css::accessibility::AccessibleStateType::EDITABLE );
1602 }
1603 
1604 void	Document::FillAccessibleRelationSet( utl::AccessibleRelationSetHelper& rRelationSet )
1605 {
1606 	if( getAccessibleParent()->getAccessibleContext()->getAccessibleRole() == ::css::accessibility::AccessibleRole::SCROLL_PANE )
1607 	{
1608 		::css::uno::Sequence< ::css::uno::Reference< ::css::uno::XInterface > > aSequence(1);
1609 		aSequence[0] = getAccessibleParent();
1610 		rRelationSet.AddRelation( ::css::accessibility::AccessibleRelation( ::css::accessibility::AccessibleRelationType::MEMBER_OF, aSequence ) );
1611 	}
1612 	else
1613 	{
1614 		 VCLXAccessibleComponent::FillAccessibleRelationSet(rRelationSet);
1615 	}
1616 }
1617 // virtual
1618 void SAL_CALL Document::disposing()
1619 {
1620     m_aEngineListener.endListening();
1621     m_aViewListener.endListening();
1622     if (m_xParagraphs.get() != 0)
1623         disposeParagraphs();
1624     VCLXAccessibleComponent::disposing();
1625 }
1626 
1627 // virtual
1628 void Document::Notify(::SfxBroadcaster &, ::SfxHint const & rHint)
1629 {
1630     if (rHint.ISA(::TextHint))
1631     {
1632         ::TextHint const & rTextHint
1633               = static_cast< ::TextHint const & >(rHint);
1634         switch (rTextHint.GetId())
1635         {
1636         case TEXT_HINT_PARAINSERTED:
1637         case TEXT_HINT_PARAREMOVED:
1638             // TEXT_HINT_PARAINSERTED and TEXT_HINT_PARAREMOVED are sent at
1639             // "unsafe" times (when the text engine has not yet re-formatted its
1640             // content), so that for example calling ::TextEngine::GetTextHeight
1641             // from within the code that handles TEXT_HINT_PARAINSERTED causes
1642             // trouble within the text engine.  Therefore, these hints are just
1643             // buffered until a following ::TextEngine::FormatDoc causes a
1644             // TEXT_HINT_TEXTFORMATTED to come in:
1645         case TEXT_HINT_FORMATPARA:
1646             // ::TextEngine::FormatDoc sends a sequence of
1647             // TEXT_HINT_FORMATPARAs, followed by an optional
1648             // TEXT_HINT_TEXTHEIGHTCHANGED, followed in all cases by one
1649             // TEXT_HINT_TEXTFORMATTED.  Only the TEXT_HINT_FORMATPARAs contain
1650             // the the numbers of the affected paragraphs, but they are sent
1651             // before the changes are applied.  Therefore, TEXT_HINT_FORMATPARAs
1652             // are just buffered until another hint comes in:
1653             {
1654                 ::osl::MutexGuard aInternalGuard(GetMutex());
1655                 if (!isAlive())
1656                     break;
1657 
1658                 m_aParagraphNotifications.push(rTextHint);
1659                 break;
1660             }
1661         case TEXT_HINT_TEXTFORMATTED:
1662         case TEXT_HINT_TEXTHEIGHTCHANGED:
1663         case TEXT_HINT_MODIFIED:
1664             {
1665                 ::osl::MutexGuard aInternalGuard(GetMutex());
1666                 if (!isAlive())
1667                     break;
1668                 handleParagraphNotifications();
1669                 break;
1670             }
1671         case TEXT_HINT_VIEWSCROLLED:
1672             {
1673                 ::osl::MutexGuard aInternalGuard(GetMutex());
1674                 if (!isAlive())
1675                     break;
1676                 handleParagraphNotifications();
1677 
1678                 ::sal_Int32 nOffset = static_cast< ::sal_Int32 >(
1679                     m_rView.GetStartDocPos().Y());
1680                     // XXX  numeric overflow
1681                 if (nOffset != m_nViewOffset)
1682                 {
1683                     m_nViewOffset = nOffset;
1684 
1685                     Paragraphs::iterator aOldVisibleBegin(
1686                         m_aVisibleBegin);
1687                     Paragraphs::iterator aOldVisibleEnd(m_aVisibleEnd);
1688 
1689                     determineVisibleRange();
1690 
1691                     notifyVisibleRangeChanges(aOldVisibleBegin,
1692                                                 aOldVisibleEnd,
1693                                                 m_xParagraphs->end());
1694                 }
1695                 break;
1696             }
1697         case TEXT_HINT_VIEWSELECTIONCHANGED:
1698             {
1699                 ::osl::MutexGuard aInternalGuard(GetMutex());
1700                 if (!isAlive())
1701                     break;
1702 
1703                 if (m_aParagraphNotifications.empty())
1704                 {
1705                     handleSelectionChangeNotification();
1706                 }
1707                 else
1708                 {
1709                     // TEXT_HINT_VIEWSELECTIONCHANGED is sometimes sent at
1710                     // "unsafe" times (when the text engine has not yet re-
1711                     // formatted its content), so that for example calling
1712                     // ::TextEngine::GetTextHeight from within the code that
1713                     // handles a previous TEXT_HINT_PARAINSERTED causes
1714                     // trouble within the text engine.  Therefore, these
1715                     // hints are just buffered (along with
1716                     // TEXT_HINT_PARAINSERTED/REMOVED/FORMATPARA) until a
1717                     // following ::TextEngine::FormatDoc causes a
1718                     // TEXT_HINT_TEXTFORMATTED to come in:
1719                     m_bSelectionChangedNotification = true;
1720                 }
1721                 break;
1722             }
1723         }
1724     }
1725 }
1726 
1727 IMPL_LINK(Document, WindowEventHandler, ::VclSimpleEvent *, pEvent)
1728 {
1729     switch (pEvent->GetId())
1730     {
1731     case VCLEVENT_WINDOW_RESIZE:
1732         {
1733             ::osl::MutexGuard aInternalGuard(GetMutex());
1734             if (!isAlive())
1735                 break;
1736 
1737             ::sal_Int32 nHeight = static_cast< ::sal_Int32 >(
1738                 m_rView.GetWindow()->GetOutputSizePixel().Height());
1739                 // XXX  numeric overflow
1740             if (nHeight != m_nViewHeight)
1741             {
1742                 m_nViewHeight = nHeight;
1743 
1744                 Paragraphs::iterator aOldVisibleBegin(m_aVisibleBegin);
1745                 Paragraphs::iterator aOldVisibleEnd(m_aVisibleEnd);
1746 
1747                 determineVisibleRange();
1748 
1749                 notifyVisibleRangeChanges(aOldVisibleBegin, aOldVisibleEnd,
1750                                             m_xParagraphs->end());
1751             }
1752             break;
1753         }
1754     case VCLEVENT_WINDOW_GETFOCUS:
1755         {
1756             ::osl::MutexGuard aInternalGuard(GetMutex());
1757             if (!isAlive())
1758                 break;
1759 			//to enable the PARAGRAPH to get focus for multiline edit
1760 			::sal_Int32 count = getAccessibleChildCount();
1761 			::sal_Bool bEmpty = m_aFocused == m_aVisibleEnd && count == 1;
1762             if ((m_aFocused >= m_aVisibleBegin && m_aFocused < m_aVisibleEnd) || bEmpty)
1763             {
1764 				Paragraphs::iterator m_aTemp = bEmpty ? m_aVisibleBegin : m_aFocused;
1765                 ::rtl::Reference< ParagraphImpl > xParagraph(getParagraph(m_aTemp));
1766 				if (xParagraph.is())
1767 				{
1768 					xParagraph->notifyEvent(
1769 						::css::accessibility::AccessibleEventId::
1770 						STATE_CHANGED,
1771 						::css::uno::Any(),
1772 						::css::uno::makeAny(
1773 							::css::accessibility::AccessibleStateType::
1774 							FOCUSED));
1775 				}
1776             }
1777 			/*
1778                 ::rtl::Reference< ParagraphImpl > xParagraph(
1779                     getParagraph(m_aFocused));
1780                 if (xParagraph.is())
1781                     xParagraph->notifyEvent(
1782                         ::css::accessibility::AccessibleEventId::
1783                         STATE_CHANGED,
1784                         ::css::uno::Any(),
1785                         ::css::uno::makeAny(
1786                             ::css::accessibility::AccessibleStateType::
1787                             FOCUSED));
1788 			*/
1789             break;
1790         }
1791     case VCLEVENT_WINDOW_LOSEFOCUS:
1792         {
1793             ::osl::MutexGuard aInternalGuard(GetMutex());
1794             if (!isAlive())
1795                 break;
1796 			//to enable the PARAGRAPH to get focus for multiline edit
1797 			::sal_Int32 count = getAccessibleChildCount();
1798 			::sal_Bool bEmpty = m_aFocused == m_aVisibleEnd && count == 1;
1799             if ((m_aFocused >= m_aVisibleBegin && m_aFocused < m_aVisibleEnd) || bEmpty)
1800             {
1801 				Paragraphs::iterator m_aTemp = bEmpty ? m_aVisibleBegin : m_aFocused;
1802                 ::rtl::Reference< ParagraphImpl > xParagraph(getParagraph(m_aTemp));
1803                 if (xParagraph.is())
1804                     xParagraph->notifyEvent(
1805                         ::css::accessibility::AccessibleEventId::
1806                         STATE_CHANGED,
1807                         ::css::uno::makeAny(
1808                             ::css::accessibility::AccessibleStateType::
1809                             FOCUSED),
1810                         ::css::uno::Any());
1811             }
1812 
1813 			/*
1814             if (m_aFocused >= m_aVisibleBegin && m_aFocused < m_aVisibleEnd)
1815             {
1816                 ::rtl::Reference< ParagraphImpl > xParagraph(
1817                     getParagraph(m_aFocused));
1818                 if (xParagraph.is())
1819                     xParagraph->notifyEvent(
1820                         ::css::accessibility::AccessibleEventId::
1821                         STATE_CHANGED,
1822                         ::css::uno::makeAny(
1823                             ::css::accessibility::AccessibleStateType::
1824                             FOCUSED),
1825                         ::css::uno::Any());
1826             }
1827 			*/
1828             break;
1829         }
1830     }
1831     return 0;
1832 }
1833 
1834 void Document::init()
1835 {
1836     if (m_xParagraphs.get() == 0)
1837     {
1838         ::sal_uLong nCount = m_rEngine.GetParagraphCount();
1839         ::std::auto_ptr< Paragraphs > p(new Paragraphs);
1840         p->reserve(static_cast< Paragraphs::size_type >(nCount));
1841             // numeric overflow is harmless here
1842         for (::sal_uLong i = 0; i < nCount; ++i)
1843             p->push_back(ParagraphInfo(static_cast< ::sal_Int32 >(
1844                                            m_rEngine.GetTextHeight(i))));
1845                 // XXX  numeric overflow
1846         m_nViewOffset = static_cast< ::sal_Int32 >(
1847             m_rView.GetStartDocPos().Y()); // XXX  numeric overflow
1848         m_nViewHeight = static_cast< ::sal_Int32 >(
1849             m_rView.GetWindow()->GetOutputSizePixel().Height());
1850             // XXX  numeric overflow
1851         m_xParagraphs = p;
1852         determineVisibleRange();
1853         m_nSelectionFirstPara = -1;
1854         m_nSelectionFirstPos = -1;
1855         m_nSelectionLastPara = -1;
1856         m_nSelectionLastPos = -1;
1857         m_aFocused = m_xParagraphs->end();
1858         m_bSelectionChangedNotification = false;
1859         m_aEngineListener.startListening(m_rEngine);
1860         m_aViewListener.startListening(*m_rView.GetWindow());
1861     }
1862 }
1863 
1864 ::rtl::Reference< ParagraphImpl >
1865 Document::getParagraph(Paragraphs::iterator const & rIt)
1866 {
1867     return static_cast< ParagraphImpl * >(
1868         ::css::uno::Reference< ::css::accessibility::XAccessible >(
1869             rIt->getParagraph()).get());
1870 }
1871 
1872 ::css::uno::Reference< ::css::accessibility::XAccessible >
1873 Document::getAccessibleChild(Paragraphs::iterator const & rIt)
1874 {
1875     ::css::uno::Reference< ::css::accessibility::XAccessible > xParagraph(
1876         rIt->getParagraph());
1877     if (!xParagraph.is())
1878     {
1879         xParagraph = new Paragraph(this, rIt - m_xParagraphs->begin());
1880         rIt->setParagraph(xParagraph);
1881     }
1882     return xParagraph;
1883 }
1884 
1885 void Document::determineVisibleRange()
1886 {
1887     m_aVisibleBegin = m_xParagraphs->end();
1888     m_aVisibleEnd = m_aVisibleBegin;
1889     ::sal_Int32 nPos = 0;
1890     for (Paragraphs::iterator aIt = m_xParagraphs->begin();;)
1891     {
1892         if (aIt == m_xParagraphs->end())
1893         {
1894             m_nVisibleBeginOffset = 0;
1895             break;
1896         }
1897         ::sal_Int32 nOldPos = nPos;
1898         nPos += aIt->getHeight(); // XXX  numeric overflow
1899         if (m_aVisibleBegin == m_xParagraphs->end() && nPos >= m_nViewOffset)
1900         {
1901             m_aVisibleBegin = aIt;
1902             m_nVisibleBeginOffset = m_nViewOffset - nOldPos;
1903         }
1904         ++aIt;
1905         if (m_aVisibleBegin != m_xParagraphs->end()
1906             && (aIt == m_xParagraphs->end()
1907                 || nPos >= m_nViewOffset + m_nViewHeight))
1908             // XXX  numeric overflow
1909         {
1910             m_aVisibleEnd = aIt;
1911             break;
1912         }
1913     }
1914 }
1915 
1916 void Document::notifyVisibleRangeChanges(
1917     Paragraphs::iterator const & rOldVisibleBegin,
1918     Paragraphs::iterator const & rOldVisibleEnd,
1919     Paragraphs::iterator const & rInserted)
1920 {
1921     // XXX  Replace this code that determines which paragraphs have changed from
1922     // invisible to visible or vice versa with a better algorithm.
1923     {for (Paragraphs::iterator aIt(rOldVisibleBegin); aIt != rOldVisibleEnd;
1924           ++aIt)
1925         if (aIt != rInserted
1926             && (aIt < m_aVisibleBegin || aIt >= m_aVisibleEnd))
1927             NotifyAccessibleEvent(
1928                 ::css::accessibility::AccessibleEventId::
1929                 CHILD,
1930                 ::css::uno::makeAny(getAccessibleChild(aIt)),
1931                 ::css::uno::Any());
1932     }
1933     {for (Paragraphs::iterator aIt(m_aVisibleBegin); aIt != m_aVisibleEnd;
1934           ++aIt)
1935         if (aIt == rInserted
1936             || aIt < rOldVisibleBegin || aIt >= rOldVisibleEnd)
1937             NotifyAccessibleEvent(
1938                 ::css::accessibility::AccessibleEventId::
1939                 CHILD,
1940                 ::css::uno::Any(),
1941                 ::css::uno::makeAny(getAccessibleChild(aIt)));
1942     }
1943 }
1944 
1945 void
1946 Document::changeParagraphText(::sal_uLong nNumber, ::sal_uInt16 nBegin, ::sal_uInt16 nEnd,
1947                               bool bCut, bool bPaste,
1948                               ::rtl::OUString const & rText)
1949 {
1950     m_rView.SetSelection(::TextSelection(::TextPaM(nNumber, nBegin),
1951                                          ::TextPaM(nNumber, nEnd)));
1952     if (bCut)
1953         m_rView.Cut();
1954     else if (nBegin != nEnd)
1955         m_rView.DeleteSelected();
1956     if (bPaste)
1957         m_rView.Paste();
1958     else if ( !rText.isEmpty() )
1959         m_rView.InsertText(rText);
1960 }
1961 
1962 void Document::handleParagraphNotifications()
1963 {
1964     while (!m_aParagraphNotifications.empty())
1965     {
1966         ::TextHint aHint(m_aParagraphNotifications.front());
1967         m_aParagraphNotifications.pop();
1968         switch (aHint.GetId())
1969         {
1970         case TEXT_HINT_PARAINSERTED:
1971             {
1972                 ::sal_uLong n = aHint.GetValue();
1973                 OSL_ENSURE(n <= m_xParagraphs->size(),
1974                            "bad TEXT_HINT_PARAINSERTED event");
1975 
1976                 // Save the values of old iterators (the iterators themselves
1977                 // will get invalidated), and adjust the old values so that they
1978                 // reflect the insertion of the new paragraph:
1979                 Paragraphs::size_type nOldVisibleBegin
1980                     = m_aVisibleBegin - m_xParagraphs->begin();
1981                 Paragraphs::size_type nOldVisibleEnd
1982                     = m_aVisibleEnd - m_xParagraphs->begin();
1983                 Paragraphs::size_type nOldFocused
1984                     = m_aFocused - m_xParagraphs->begin();
1985                 if (n <= nOldVisibleBegin)
1986                     ++nOldVisibleBegin; // XXX  numeric overflow
1987                 if (n <= nOldVisibleEnd)
1988                     ++nOldVisibleEnd; // XXX  numeric overflow
1989                 if (n <= nOldFocused)
1990                     ++nOldFocused; // XXX  numeric overflow
1991                 if (sal::static_int_cast<sal_Int32>(n) <= m_nSelectionFirstPara)
1992                     ++m_nSelectionFirstPara; // XXX  numeric overflow
1993                 if (sal::static_int_cast<sal_Int32>(n) <= m_nSelectionLastPara)
1994                     ++m_nSelectionLastPara; // XXX  numeric overflow
1995 
1996                 Paragraphs::iterator aIns(
1997                     m_xParagraphs->insert(
1998                         m_xParagraphs->begin() + n,
1999                         ParagraphInfo(static_cast< ::sal_Int32 >(
2000                                           m_rEngine.GetTextHeight(n)))));
2001                     // XXX  numeric overflow (2x)
2002 
2003                 determineVisibleRange();
2004                 m_aFocused = m_xParagraphs->begin() + nOldFocused;
2005 
2006                 for (Paragraphs::iterator aIt(aIns);;)
2007                 {
2008                     ++aIt;
2009                     if (aIt == m_xParagraphs->end())
2010                         break;
2011                     ::rtl::Reference< ParagraphImpl > xParagraph(
2012                         getParagraph(aIt));
2013                     if (xParagraph.is())
2014                         xParagraph->numberChanged(true);
2015                 }
2016 
2017                 notifyVisibleRangeChanges(
2018                     m_xParagraphs->begin() + nOldVisibleBegin,
2019                     m_xParagraphs->begin() + nOldVisibleEnd, aIns);
2020                 break;
2021             }
2022         case TEXT_HINT_PARAREMOVED:
2023             {
2024                 ::sal_uLong n = aHint.GetValue();
2025                 if (n == TEXT_PARA_ALL)
2026                 {
2027                     {for (Paragraphs::iterator aIt(m_aVisibleBegin);
2028                           aIt != m_aVisibleEnd; ++aIt)
2029                         NotifyAccessibleEvent(
2030                             ::css::accessibility::AccessibleEventId::
2031                             CHILD,
2032                             ::css::uno::makeAny(getAccessibleChild(aIt)),
2033                             ::css::uno::Any());
2034                     }
2035                     disposeParagraphs();
2036                     m_xParagraphs->clear();
2037                     determineVisibleRange();
2038                     m_nSelectionFirstPara = -1;
2039                     m_nSelectionFirstPos = -1;
2040                     m_nSelectionLastPara = -1;
2041                     m_nSelectionLastPos = -1;
2042                     m_aFocused = m_xParagraphs->end();
2043                 }
2044                 else
2045                 {
2046                     OSL_ENSURE(n < m_xParagraphs->size(),
2047                                "Bad TEXT_HINT_PARAREMOVED event");
2048 
2049                     Paragraphs::iterator aIt(m_xParagraphs->begin() + n);
2050                         // numeric overflow cannot occur
2051 
2052                     // Save the values of old iterators (the iterators
2053                     // themselves will get invalidated), and adjust the old
2054                     // values so that they reflect the removal of the paragraph:
2055                     Paragraphs::size_type nOldVisibleBegin
2056                         = m_aVisibleBegin - m_xParagraphs->begin();
2057                     Paragraphs::size_type nOldVisibleEnd
2058                         = m_aVisibleEnd - m_xParagraphs->begin();
2059                     bool bWasVisible
2060                         = nOldVisibleBegin <= n && n < nOldVisibleEnd;
2061                     Paragraphs::size_type nOldFocused
2062                         = m_aFocused - m_xParagraphs->begin();
2063                     bool bWasFocused = aIt == m_aFocused;
2064                     if (n < nOldVisibleBegin)
2065                         --nOldVisibleBegin;
2066                     if (n < nOldVisibleEnd)
2067                         --nOldVisibleEnd;
2068                     if (n < nOldFocused)
2069                         --nOldFocused;
2070                     if (sal::static_int_cast<sal_Int32>(n) < m_nSelectionFirstPara)
2071                         --m_nSelectionFirstPara;
2072                     else if (sal::static_int_cast<sal_Int32>(n) == m_nSelectionFirstPara)
2073                     {
2074                         if (m_nSelectionFirstPara == m_nSelectionLastPara)
2075                         {
2076                             m_nSelectionFirstPara = -1;
2077                             m_nSelectionFirstPos = -1;
2078                             m_nSelectionLastPara = -1;
2079                             m_nSelectionLastPos = -1;
2080                         }
2081                         else
2082                         {
2083                             ++m_nSelectionFirstPara;
2084                             m_nSelectionFirstPos = 0;
2085                         }
2086                     }
2087                     if (sal::static_int_cast<sal_Int32>(n) < m_nSelectionLastPara)
2088                         --m_nSelectionLastPara;
2089                     else if (sal::static_int_cast<sal_Int32>(n) == m_nSelectionLastPara)
2090                     {
2091                         OSL_ENSURE(m_nSelectionFirstPara < m_nSelectionLastPara,
2092                                    "logic error");
2093                         --m_nSelectionLastPara;
2094                         m_nSelectionLastPos = 0x7FFFFFFF;
2095                     }
2096 
2097                     ::css::uno::Reference< ::css::accessibility::XAccessible >
2098                           xStrong;
2099                     if (bWasVisible)
2100                         xStrong = getAccessibleChild(aIt);
2101                     ::css::uno::WeakReference<
2102                           ::css::accessibility::XAccessible > xWeak(
2103                               aIt->getParagraph());
2104                     aIt = m_xParagraphs->erase(aIt);
2105 
2106                     determineVisibleRange();
2107                     m_aFocused = bWasFocused ? m_xParagraphs->end()
2108                         : m_xParagraphs->begin() + nOldFocused;
2109 
2110                     for (; aIt != m_xParagraphs->end(); ++aIt)
2111                     {
2112                         ::rtl::Reference< ParagraphImpl > xParagraph(
2113                             getParagraph(aIt));
2114                         if (xParagraph.is())
2115                             xParagraph->numberChanged(false);
2116                     }
2117 
2118                     if (bWasVisible)
2119                         NotifyAccessibleEvent(
2120                             ::css::accessibility::AccessibleEventId::
2121                             CHILD,
2122                             ::css::uno::makeAny(getAccessibleChild(aIt)),
2123                             ::css::uno::Any());
2124 
2125                     ::css::uno::Reference< ::css::lang::XComponent > xComponent(
2126                         xWeak.get(), ::css::uno::UNO_QUERY);
2127                     if (xComponent.is())
2128                         xComponent->dispose();
2129 
2130                     notifyVisibleRangeChanges(
2131                         m_xParagraphs->begin() + nOldVisibleBegin,
2132                         m_xParagraphs->begin() + nOldVisibleEnd,
2133                         m_xParagraphs->end());
2134                 }
2135                 break;
2136             }
2137         case TEXT_HINT_FORMATPARA:
2138             {
2139                 ::sal_uLong n = aHint.GetValue();
2140                 OSL_ENSURE(n < m_xParagraphs->size(),
2141                            "Bad TEXT_HINT_FORMATPARA event");
2142 
2143                 (*m_xParagraphs)[static_cast< Paragraphs::size_type >(n)].
2144                     changeHeight(static_cast< ::sal_Int32 >(
2145                                      m_rEngine.GetTextHeight(n)));
2146                     // XXX  numeric overflow
2147                 Paragraphs::iterator aOldVisibleBegin(m_aVisibleBegin);
2148                 Paragraphs::iterator aOldVisibleEnd(m_aVisibleEnd);
2149                 determineVisibleRange();
2150                 notifyVisibleRangeChanges(aOldVisibleBegin, aOldVisibleEnd,
2151                                           m_xParagraphs->end());
2152 
2153                 if (n < m_xParagraphs->size())
2154                 {
2155                     Paragraphs::iterator aIt(m_xParagraphs->begin() + n);
2156                     ::rtl::Reference< ParagraphImpl > xParagraph(getParagraph(aIt));
2157                     if (xParagraph.is())
2158                         xParagraph->textChanged();
2159                 }
2160                 break;
2161             }
2162         default:
2163             OSL_ENSURE(false, "bad buffered hint");
2164             break;
2165         }
2166     }
2167     if (m_bSelectionChangedNotification)
2168     {
2169         m_bSelectionChangedNotification = false;
2170         handleSelectionChangeNotification();
2171     }
2172 }
2173 
2174 ::sal_Int32 Document::getSelectionType(::sal_Int32 nNewFirstPara, ::sal_Int32 nNewFirstPos, ::sal_Int32 nNewLastPara, ::sal_Int32 nNewLastPos)
2175 {
2176 	if (m_nSelectionFirstPara == -1)
2177 		return -1;
2178 	::sal_Int32 Osp = m_nSelectionFirstPara, Osl = m_nSelectionFirstPos, Oep = m_nSelectionLastPara, Oel = m_nSelectionLastPos;
2179 	::sal_Int32 Nsp = nNewFirstPara, Nsl = nNewFirstPos, Nep = nNewLastPara, Nel = nNewLastPos;
2180 	TextPaM Ns(Nsp, sal_uInt16(Nsl));
2181 	TextPaM Ne(Nep, sal_uInt16(Nel));
2182 	TextPaM Os(Osp, sal_uInt16(Osl));
2183 	TextPaM Oe(Oep, sal_uInt16(Oel));
2184 
2185 	if (Os == Oe && Ns == Ne)
2186 	{
2187 		//only caret moves.
2188 		return 1;
2189 	}
2190 	else if (Os == Oe && Ns != Ne)
2191 	{
2192 		//old has no selection but new has selection
2193 		return 2;
2194 	}
2195 	else if (Os != Oe && Ns == Ne)
2196 	{
2197 		//old has selection but new has no selection.
2198 		return 3;
2199 	}
2200 	else if (Os != Oe && Ns != Ne && Osp == Nsp && Osl == Nsl)
2201 	{
2202 		//both old and new have selections.
2203 		if (Oep == Nep )
2204 		{
2205 			//Send text_selection_change event on Nep
2206 
2207 			return 4;
2208 		}
2209 		else if (Oep < Nep)
2210 		{
2211 			//all the following examples like 1,2->1,3 means that old start select para is 1, old end select para is 2,
2212 			// then press shift up, the new start select para is 1, new end select para is 3;
2213 			//for example, 1, 2 -> 1, 3; 4,1 -> 4, 7; 4,1 -> 4, 2; 4,4->4,5
2214 			if (Nep >= Nsp)
2215 			{
2216 				// 1, 2 -> 1, 3; 4, 1 -> 4, 7; 4,4->4,5;
2217 				if (Oep < Osp)
2218 				{
2219 					// 4,1 -> 4,7;
2220 					return 5;
2221 				}
2222 				else if (Oep >= Osp)
2223 				{
2224 					// 1, 2 -> 1, 3; 4,4->4,5;
2225 					return 6;
2226 				}
2227 			}
2228 			else
2229 			{
2230 				// 4,1 -> 4,2,
2231 				if (Oep < Osp)
2232 				{
2233 					// 4,1 -> 4,2,
2234 					return 7;
2235 				}
2236 				else if (Oep >= Osp)
2237 				{
2238 					// no such condition. Oep > Osp = Nsp > Nep
2239 				}
2240 			}
2241 		}
2242 		else if (Oep > Nep)
2243 		{
2244 			// 3,2 -> 3,1; 4,7 -> 4,1; 4, 7 -> 4,6; 4,4 -> 4,3
2245 			if (Nep >= Nsp)
2246 			{
2247 				// 4,7 -> 4,6
2248 				if (Oep <= Osp)
2249 				{
2250 					//no such condition, Oep<Osp=Nsp <= Nep
2251 				}
2252 				else if (Oep > Osp)
2253 				{
2254 					// 4,7 ->4,6
2255 					return 8;
2256 				}
2257 			}
2258 			else
2259 			{
2260 				// 3,2 -> 3,1, 4,7 -> 4,1; 4,4->4,3
2261 				if (Oep <= Osp)
2262 				{
2263 					// 3,2 -> 3,1; 4,4->4,3
2264 					return 9;
2265 				}
2266 				else if (Oep > Osp)
2267 				{
2268 					// 4,7 -> 4,1
2269 					return 10;
2270 				}
2271 			}
2272 		}
2273 	}
2274 	return -1;
2275 }
2276 
2277 
2278 void Document::sendEvent(::sal_Int32 start, ::sal_Int32 end, ::sal_Int16 nEventId)
2279 {
2280 	 Paragraphs::iterator aEnd = ::std::min(m_xParagraphs->begin() + end + 1, m_aVisibleEnd);
2281 	for (Paragraphs::iterator aIt = ::std::max(m_xParagraphs->begin() + start, m_aVisibleBegin);
2282 	     aIt < aEnd; ++aIt)
2283 	{
2284 	    ::rtl::Reference< ParagraphImpl > xParagraph(getParagraph(aIt));
2285 	    if (xParagraph.is())
2286 	        xParagraph->notifyEvent(
2287 			nEventId,
2288 	            ::css::uno::Any(), ::css::uno::Any());
2289 	}
2290 }
2291 
2292 void Document::handleSelectionChangeNotification()
2293 {
2294     ::TextSelection const & rSelection = m_rView.GetSelection();
2295     OSL_ENSURE(rSelection.GetStart().GetPara() < m_xParagraphs->size()
2296                && rSelection.GetEnd().GetPara() < m_xParagraphs->size(),
2297                "bad TEXT_HINT_VIEWSELECTIONCHANGED event");
2298     ::sal_Int32 nNewFirstPara
2299           = static_cast< ::sal_Int32 >(rSelection.GetStart().GetPara());
2300     ::sal_Int32 nNewFirstPos
2301           = static_cast< ::sal_Int32 >(rSelection.GetStart().GetIndex());
2302         // XXX  numeric overflow
2303     ::sal_Int32 nNewLastPara
2304           = static_cast< ::sal_Int32 >(rSelection.GetEnd().GetPara());
2305     ::sal_Int32 nNewLastPos
2306           = static_cast< ::sal_Int32 >(rSelection.GetEnd().GetIndex());
2307         // XXX  numeric overflow
2308 
2309     // Lose focus:
2310     Paragraphs::iterator aIt(m_xParagraphs->begin() + nNewLastPara);
2311     if (m_aFocused != m_xParagraphs->end() && m_aFocused != aIt
2312         && m_aFocused >= m_aVisibleBegin && m_aFocused < m_aVisibleEnd)
2313     {
2314         ::rtl::Reference< ParagraphImpl > xParagraph(getParagraph(m_aFocused));
2315         if (xParagraph.is())
2316             xParagraph->notifyEvent(
2317                 ::css::accessibility::AccessibleEventId::
2318                 STATE_CHANGED,
2319                 ::css::uno::makeAny(
2320                     ::css::accessibility::AccessibleStateType::FOCUSED),
2321                 ::css::uno::Any());
2322     }
2323 
2324     // Gain focus and update cursor position:
2325     if (aIt >= m_aVisibleBegin && aIt < m_aVisibleEnd
2326         && (aIt != m_aFocused
2327             || nNewLastPara != m_nSelectionLastPara
2328             || nNewLastPos != m_nSelectionLastPos))
2329     {
2330         ::rtl::Reference< ParagraphImpl > xParagraph(getParagraph(aIt));
2331         if (xParagraph.is())
2332         {
2333 		//disable the first event when user types in empty field.
2334 		::sal_Int32 count = getAccessibleChildCount();
2335 		::sal_Bool bEmpty = count > 1;
2336             //if (aIt != m_aFocused)
2337 			if (aIt != m_aFocused && bEmpty)
2338                 xParagraph->notifyEvent(
2339                     ::css::accessibility::AccessibleEventId::
2340                     STATE_CHANGED,
2341                     ::css::uno::Any(),
2342                     ::css::uno::makeAny(
2343                         ::css::accessibility::AccessibleStateType::FOCUSED));
2344             if (nNewLastPara != m_nSelectionLastPara
2345                 || nNewLastPos != m_nSelectionLastPos)
2346                 xParagraph->notifyEvent(
2347                     ::css::accessibility::AccessibleEventId::
2348                     CARET_CHANGED,
2349                     ::css::uno::makeAny< ::sal_Int32 >(
2350                         nNewLastPara == m_nSelectionLastPara
2351                         ? m_nSelectionLastPos : 0),
2352                     ::css::uno::makeAny(nNewLastPos));
2353         }
2354     }
2355     m_aFocused = aIt;
2356 
2357     ::sal_Int32 nMin;
2358     ::sal_Int32 nMax;
2359     ::sal_Int32 ret = getSelectionType(nNewFirstPara, nNewFirstPos, nNewLastPara, nNewLastPos);
2360 	switch (ret)
2361 	{
2362 		case -1:
2363 			{
2364 				//no event
2365 			}
2366 			break;
2367 		case 1:
2368 			{
2369 				//only caret moved, already handled in above
2370 			}
2371 			break;
2372 		case 2:
2373 			{
2374 				//old has no selection but new has selection
2375 				nMin = ::std::min(nNewFirstPara, nNewLastPara);
2376 				nMax = ::std::max(nNewFirstPara, nNewLastPara);
2377 				sendEvent(nMin, nMax,  ::css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2378 				sendEvent(nMin, nMax,  ::css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
2379 			}
2380 			break;
2381 		case 3:
2382 			{
2383 				//old has selection but new has no selection.
2384 				nMin = ::std::min(m_nSelectionFirstPara, m_nSelectionLastPara);
2385 				nMax = ::std::max(m_nSelectionFirstPara, m_nSelectionLastPara);
2386 				sendEvent(nMin, nMax,  ::css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2387 				sendEvent(nMin, nMax,  ::css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
2388 			}
2389 			break;
2390 		case 4:
2391 			{
2392 				//Send text_selection_change event on Nep
2393 				sendEvent(nNewLastPara, nNewLastPara, ::css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
2394 			}
2395 			break;
2396 		case 5:
2397 			{
2398 				// 4, 1 -> 4, 7
2399 				sendEvent(m_nSelectionLastPara, m_nSelectionFirstPara-1, ::css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2400 				sendEvent(nNewFirstPara+1, nNewLastPara, ::css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2401 
2402 				sendEvent(m_nSelectionLastPara, nNewLastPara, ::css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
2403 			}
2404 			break;
2405 		case 6:
2406 			{
2407 				// 1, 2 -> 1, 4; 4,4->4,5;
2408 				sendEvent(m_nSelectionLastPara+1, nNewLastPara, ::css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2409 
2410 				sendEvent(m_nSelectionLastPara, nNewLastPara, ::css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
2411 			}
2412 			break;
2413 		case 7:
2414 			{
2415 				// 4,1 -> 4,3,
2416 				sendEvent(m_nSelectionLastPara +1, nNewLastPara , ::css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2417 
2418 				sendEvent(m_nSelectionLastPara, nNewLastPara, ::css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
2419 			}
2420 			break;
2421 		case 8:
2422 			{
2423 				// 4,7 ->4,5;
2424 				sendEvent(nNewLastPara + 1, m_nSelectionLastPara, ::css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2425 
2426 				sendEvent(nNewLastPara, m_nSelectionLastPara, ::css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
2427 			}
2428 			break;
2429 		case 9:
2430 			{
2431 				// 3,2 -> 3,1; 4,4->4,3
2432 				sendEvent(nNewLastPara, m_nSelectionLastPara - 1, ::css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2433 
2434 				sendEvent(nNewLastPara, m_nSelectionLastPara, ::css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
2435 			}
2436 			break;
2437 		case 10:
2438 			{
2439 				// 4,7 -> 4,1
2440 				sendEvent(m_nSelectionFirstPara + 1, m_nSelectionLastPara, ::css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2441 				sendEvent(nNewLastPara, nNewFirstPara - 1, ::css::accessibility::AccessibleEventId::SELECTION_CHANGED);
2442 
2443 				sendEvent(nNewLastPara, m_nSelectionLastPara, ::css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED);
2444 			}
2445 			break;
2446 		default:
2447 			break;
2448 	}
2449 
2450     /*
2451     // Update both old and new selection.  (Regardless of how the two selections
2452     // look like, there will always be two ranges to the left and right of the
2453     // overlap---the overlap and/or the range to the right of it possibly being
2454     // empty.  Only for these two ranges notifications have to be sent.)
2455 
2456     TextPaM aOldTextStart( static_cast< sal_uLong >( m_nSelectionFirstPara ), static_cast< sal_uInt16 >( m_nSelectionFirstPos ) );
2457     TextPaM aOldTextEnd( static_cast< sal_uLong >( m_nSelectionLastPara ), static_cast< sal_uInt16 >( m_nSelectionLastPos ) );
2458     TextPaM aNewTextStart( static_cast< sal_uLong >( nNewFirstPara ), static_cast< sal_uInt16 >( nNewFirstPos ) );
2459     TextPaM aNewTextEnd( static_cast< sal_uLong >( nNewLastPara ), static_cast< sal_uInt16 >( nNewLastPos ) );
2460 
2461     // justify selections
2462     justifySelection( aOldTextStart, aOldTextEnd );
2463     justifySelection( aNewTextStart, aNewTextEnd );
2464 
2465     sal_Int32 nFirst1;
2466     sal_Int32 nLast1;
2467     sal_Int32 nFirst2;
2468     sal_Int32 nLast2;
2469 
2470     if ( m_nSelectionFirstPara == -1 )
2471     {
2472         // old selection not initialized yet => notify events only for new selection (if not empty)
2473         nFirst1 = aNewTextStart.GetPara();
2474         nLast1 = aNewTextEnd.GetPara() + ( aNewTextStart != aNewTextEnd ? 1 : 0 );
2475         nFirst2 = 0;
2476         nLast2 = 0;
2477     }
2478     else if ( aOldTextStart == aOldTextEnd && aNewTextStart == aNewTextEnd )
2479     {
2480         // old an new selection empty => no events
2481         nFirst1 = 0;
2482         nLast1 = 0;
2483         nFirst2 = 0;
2484         nLast2 = 0;
2485     }
2486     else if ( aOldTextStart != aOldTextEnd && aNewTextStart == aNewTextEnd )
2487     {
2488         // old selection not empty + new selection empty => notify events only for old selection
2489         nFirst1 = aOldTextStart.GetPara();
2490         nLast1 = aOldTextEnd.GetPara() + 1;
2491         nFirst2 = 0;
2492         nLast2 = 0;
2493     }
2494     else if ( aOldTextStart == aOldTextEnd && aNewTextStart != aNewTextEnd )
2495     {
2496         // old selection empty + new selection not empty => notify events only for new selection
2497         nFirst1 = aNewTextStart.GetPara();
2498         nLast1 = aNewTextEnd.GetPara() + 1;
2499         nFirst2 = 0;
2500         nLast2 = 0;
2501     }
2502     else
2503     {
2504         // old and new selection not empty => notify events for the two ranges left and right of the overlap
2505         ::std::vector< TextPaM > aTextPaMs(4);
2506         aTextPaMs[0] = aOldTextStart;
2507         aTextPaMs[1] = aOldTextEnd;
2508         aTextPaMs[2] = aNewTextStart;
2509         aTextPaMs[3] = aNewTextEnd;
2510         ::std::sort( aTextPaMs.begin(), aTextPaMs.end() );
2511 
2512         nFirst1 = aTextPaMs[0].GetPara();
2513         nLast1 = aTextPaMs[1].GetPara() + ( aTextPaMs[0] != aTextPaMs[1] ? 1 : 0 );
2514 
2515         nFirst2 = aTextPaMs[2].GetPara();
2516         nLast2 = aTextPaMs[3].GetPara() + ( aTextPaMs[2] != aTextPaMs[3] ? 1 : 0 );
2517 
2518         // adjust overlapping ranges
2519         if ( nLast1 > nFirst2 )
2520             nLast1 = nFirst2;
2521     }
2522 
2523     // notify selection changes
2524     notifySelectionChange( nFirst1, nLast1 );
2525     notifySelectionChange( nFirst2, nLast2 );
2526 	*/
2527     m_nSelectionFirstPara = nNewFirstPara;
2528     m_nSelectionFirstPos = nNewFirstPos;
2529     m_nSelectionLastPara = nNewLastPara;
2530     m_nSelectionLastPos = nNewLastPos;
2531 }
2532 
2533 void Document::notifySelectionChange( sal_Int32 nFirst, sal_Int32 nLast )
2534 {
2535     if ( nFirst < nLast )
2536     {
2537         Paragraphs::iterator aEnd( ::std::min( m_xParagraphs->begin() + nLast, m_aVisibleEnd ) );
2538         for ( Paragraphs::iterator aIt = ::std::max( m_xParagraphs->begin() + nFirst, m_aVisibleBegin ); aIt < aEnd; ++aIt )
2539         {
2540             ::rtl::Reference< ParagraphImpl > xParagraph( getParagraph( aIt ) );
2541             if ( xParagraph.is() )
2542             {
2543                 xParagraph->notifyEvent(
2544                     ::css::accessibility::AccessibleEventId::SELECTION_CHANGED,
2545                     ::css::uno::Any(), ::css::uno::Any() );
2546                 xParagraph->notifyEvent(
2547                     ::css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED,
2548                     ::css::uno::Any(), ::css::uno::Any() );
2549             }
2550         }
2551     }
2552 }
2553 
2554 void Document::justifySelection( TextPaM& rTextStart, TextPaM& rTextEnd )
2555 {
2556     if ( rTextStart > rTextEnd )
2557     {
2558         TextPaM aTextPaM( rTextStart );
2559         rTextStart = rTextEnd;
2560         rTextEnd = aTextPaM;
2561     }
2562 }
2563 
2564 void Document::disposeParagraphs()
2565 {
2566     for (Paragraphs::iterator aIt(m_xParagraphs->begin());
2567          aIt != m_xParagraphs->end(); ++aIt)
2568     {
2569         ::css::uno::Reference< ::css::lang::XComponent > xComponent(
2570             aIt->getParagraph().get(), ::css::uno::UNO_QUERY);
2571         if (xComponent.is())
2572             xComponent->dispose();
2573     }
2574 }
2575 
2576 // static
2577 ::css::uno::Any Document::mapFontColor(::Color const & rColor)
2578 {
2579     return ::css::uno::makeAny(
2580         static_cast< ::sal_Int32 >(COLORDATA_RGB(rColor.GetColor())));
2581         // FIXME  keep transparency?
2582 }
2583 
2584 // static
2585 ::Color Document::mapFontColor(::css::uno::Any const & rColor)
2586 {
2587     ::sal_Int32 nColor = 0;
2588     rColor >>= nColor;
2589     return ::Color(static_cast< ::ColorData >(nColor));
2590 }
2591 
2592 // static
2593 ::css::uno::Any Document::mapFontWeight(::FontWeight nWeight)
2594 {
2595     // Map from ::FontWeight to ::css:awt::FontWeight, depends on order of
2596     // elements in ::FontWeight (vcl/vclenum.hxx):
2597     static float const aWeight[]
2598         = { ::css::awt::FontWeight::DONTKNOW, // WEIGHT_DONTKNOW
2599             ::css::awt::FontWeight::THIN, // WEIGHT_THIN
2600             ::css::awt::FontWeight::ULTRALIGHT, // WEIGHT_ULTRALIGHT
2601             ::css::awt::FontWeight::LIGHT, // WEIGHT_LIGHT
2602             ::css::awt::FontWeight::SEMILIGHT, // WEIGHT_SEMILIGHT
2603             ::css::awt::FontWeight::NORMAL, // WEIGHT_NORMAL
2604             ::css::awt::FontWeight::NORMAL, // WEIGHT_MEDIUM
2605             ::css::awt::FontWeight::SEMIBOLD, // WEIGHT_SEMIBOLD
2606             ::css::awt::FontWeight::BOLD, // WEIGHT_BOLD
2607             ::css::awt::FontWeight::ULTRABOLD, // WEIGHT_ULTRABOLD
2608             ::css::awt::FontWeight::BLACK }; // WEIGHT_BLACK
2609     return ::css::uno::makeAny(aWeight[nWeight]);
2610 }
2611 
2612 // static
2613 ::FontWeight Document::mapFontWeight(::css::uno::Any const & rWeight)
2614 {
2615     float nWeight = ::css::awt::FontWeight::NORMAL;
2616     rWeight >>= nWeight;
2617     return nWeight <= ::css::awt::FontWeight::DONTKNOW ? WEIGHT_DONTKNOW
2618         : nWeight <= ::css::awt::FontWeight::THIN ? WEIGHT_THIN
2619         : nWeight <= ::css::awt::FontWeight::ULTRALIGHT ? WEIGHT_ULTRALIGHT
2620         : nWeight <= ::css::awt::FontWeight::LIGHT ? WEIGHT_LIGHT
2621         : nWeight <= ::css::awt::FontWeight::SEMILIGHT ? WEIGHT_SEMILIGHT
2622         : nWeight <= ::css::awt::FontWeight::NORMAL ? WEIGHT_NORMAL
2623         : nWeight <= ::css::awt::FontWeight::SEMIBOLD ? WEIGHT_SEMIBOLD
2624         : nWeight <= ::css::awt::FontWeight::BOLD ? WEIGHT_BOLD
2625         : nWeight <= ::css::awt::FontWeight::ULTRABOLD ? WEIGHT_ULTRABOLD
2626         : WEIGHT_BLACK;
2627 }
2628 
2629 }
2630 
2631