1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include "richtextimplcontrol.hxx"
21 #include "textattributelistener.hxx"
22 #include "richtextengine.hxx"
23 #include <sal/log.hxx>
24 #include <osl/diagnose.h>
25 #include <i18nlangtag/languagetag.hxx>
26 #include <editeng/editids.hrc>
27 #include <editeng/editview.hxx>
28 #include <editeng/editstat.hxx>
29 #include <editeng/scripttypeitem.hxx>
30 
31 #include <svl/itempool.hxx>
32 #include <svl/itemset.hxx>
33 #include <tools/mapunit.hxx>
34 #include <vcl/svapp.hxx>
35 #include <vcl/settings.hxx>
36 #include <vcl/commandevent.hxx>
37 
38 #define EMPTY_PAPER_SIZE    0x7FFFFFFF
39 
40 
41 namespace frm
42 {
43 
RichTextControlImpl(Control * _pAntiImpl,RichTextEngine * _pEngine,ITextAttributeListener * _pTextAttrListener,ITextSelectionListener * _pSelectionListener)44     RichTextControlImpl::RichTextControlImpl( Control* _pAntiImpl, RichTextEngine* _pEngine, ITextAttributeListener* _pTextAttrListener, ITextSelectionListener* _pSelectionListener )
45         :m_pAntiImpl            ( _pAntiImpl          )
46         ,m_pViewport            ( nullptr                )
47         ,m_pHScroll             ( nullptr                )
48         ,m_pVScroll             ( nullptr                )
49         ,m_pScrollCorner        ( nullptr                )
50         ,m_pEngine              ( _pEngine            )
51         ,m_pTextAttrListener    ( _pTextAttrListener  )
52         ,m_pSelectionListener   ( _pSelectionListener )
53         ,m_bHasEverBeenShown    ( false               )
54     {
55         OSL_ENSURE( m_pAntiImpl, "RichTextControlImpl::RichTextControlImpl: invalid window!" );
56         OSL_ENSURE( m_pEngine,   "RichTextControlImpl::RichTextControlImpl: invalid edit engine! This will *definitely* crash!" );
57 
58         m_pViewport = VclPtr<RichTextViewPort>::Create( m_pAntiImpl );
59         m_pViewport->setAttributeInvalidationHandler( LINK( this, RichTextControlImpl, OnInvalidateAllAttributes ) );
60         m_pViewport->Show();
61 
62         // ensure that both the window and the reference device have the same map unit
63         MapMode aRefDeviceMapMode( m_pEngine->GetRefDevice()->GetMapMode() );
64         m_pAntiImpl->SetMapMode( aRefDeviceMapMode );
65         m_pViewport->SetMapMode( aRefDeviceMapMode );
66 
67         m_pView.reset(new EditView( m_pEngine, m_pViewport ));
68         m_pEngine->InsertView( m_pView.get() );
69         m_pViewport->setView( *m_pView );
70 
71         m_pEngine->registerEngineStatusListener( this );
72 
73         {
74             EVControlBits nViewControlWord = m_pView->GetControlWord();
75             nViewControlWord |= EVControlBits::AUTOSCROLL;
76             m_pView->SetControlWord( nViewControlWord );
77         }
78 
79         // ensure that it's initially scrolled to the upper left
80         m_pView->SetVisArea( tools::Rectangle( Point( ), m_pViewport->GetOutDev()->GetOutputSize() ) );
81 
82         ensureScrollbars();
83 
84         m_pAntiImpl->SetBackground( Wallpaper( m_pAntiImpl->GetSettings().GetStyleSettings().GetFieldColor() ) );
85     }
86 
87 
~RichTextControlImpl()88     RichTextControlImpl::~RichTextControlImpl( )
89     {
90         m_pEngine->RemoveView( m_pView.get() );
91         m_pEngine->revokeEngineStatusListener( this );
92         m_pView.reset();
93         m_pViewport.disposeAndClear();
94         m_pHScroll.disposeAndClear();
95         m_pVScroll.disposeAndClear();
96         m_pScrollCorner.disposeAndClear();
97     }
98 
99 
implUpdateAttribute(const AttributeHandlerPool::const_iterator & _pHandler)100     void RichTextControlImpl::implUpdateAttribute( const AttributeHandlerPool::const_iterator& _pHandler )
101     {
102         if  (  ( _pHandler->first == SID_ATTR_CHAR_WEIGHT )
103             || ( _pHandler->first == SID_ATTR_CHAR_POSTURE )
104             || ( _pHandler->first == SID_ATTR_CHAR_FONT )
105             || ( _pHandler->first == SID_ATTR_CHAR_FONTHEIGHT )
106             )
107         {
108             // these are attributes whose value depends on the current script type.
109             // I.e., in real, there are *three* items in the ItemSet: One for each script
110             // type (Latin, Asian, Complex). However, if we have an observer who is interested
111             // in the state of this attribute, we have to kind of *merge* the three attributes
112             // to only one.
113             // This is useful in case the observer is for instance a toolbox which contains only
114             // an, e.g., "bold" slot, and thus not interested in the particular script type of the
115             // current selection.
116             SvxScriptSetItem aNormalizedSet( static_cast<WhichId>(_pHandler->first), *m_pView->GetAttribs().GetPool() );
117             normalizeScriptDependentAttribute( aNormalizedSet );
118 
119             implCheckUpdateCache( _pHandler->first, _pHandler->second->getState( aNormalizedSet.GetItemSet() ) );
120         }
121         else
122             implCheckUpdateCache( _pHandler->first, _pHandler->second->getState( m_pView->GetAttribs() ) );
123     }
124 
125 
updateAttribute(AttributeId _nAttribute)126     void RichTextControlImpl::updateAttribute( AttributeId _nAttribute )
127     {
128         AttributeHandlerPool::const_iterator pHandler = m_aAttributeHandlers.find( _nAttribute );
129         if ( pHandler != m_aAttributeHandlers.end() )
130             implUpdateAttribute( pHandler );
131     }
132 
133 
updateAllAttributes()134     void RichTextControlImpl::updateAllAttributes( )
135     {
136         for (   AttributeHandlerPool::const_iterator pHandler = m_aAttributeHandlers.begin();
137                 pHandler != m_aAttributeHandlers.end();
138                 ++pHandler
139             )
140         {
141             implUpdateAttribute( pHandler );
142         }
143 
144         // notify changes of the selection, if necessary
145         if ( m_pSelectionListener && m_pView )
146         {
147             ESelection aCurrentSelection = m_pView->GetSelection();
148             if ( aCurrentSelection != m_aLastKnownSelection )
149             {
150                 m_aLastKnownSelection = aCurrentSelection;
151                 m_pSelectionListener->onSelectionChanged();
152             }
153         }
154     }
155 
156 
getAttributeState(AttributeId _nAttributeId) const157     AttributeState RichTextControlImpl::getAttributeState( AttributeId _nAttributeId ) const
158     {
159         StateCache::const_iterator aCachedStatePos = m_aLastKnownStates.find( _nAttributeId );
160         if ( aCachedStatePos == m_aLastKnownStates.end() )
161         {
162             OSL_FAIL( "RichTextControlImpl::getAttributeState: Don't ask for the state of an attribute which I never encountered!" );
163             return AttributeState( eIndetermined );
164         }
165         return aCachedStatePos->second;
166     }
167 
168 
executeAttribute(const SfxItemSet & _rCurrentAttribs,SfxItemSet & _rAttribs,AttributeId _nAttribute,const SfxPoolItem * _pArgument,SvtScriptType _nForScriptType)169     bool RichTextControlImpl::executeAttribute( const SfxItemSet& _rCurrentAttribs, SfxItemSet& _rAttribs, AttributeId _nAttribute, const SfxPoolItem* _pArgument, SvtScriptType _nForScriptType )
170     {
171         // let's see whether we have a handler for this attribute
172         AttributeHandlerPool::const_iterator aHandlerPos = m_aAttributeHandlers.find( _nAttribute );
173         if ( aHandlerPos != m_aAttributeHandlers.end() )
174         {
175             aHandlerPos->second->executeAttribute( _rCurrentAttribs, _rAttribs, _pArgument, _nForScriptType );
176             return true;
177         }
178         return false;
179     }
180 
181 
enableAttributeNotification(AttributeId _nAttributeId,ITextAttributeListener * _pListener)182     void RichTextControlImpl::enableAttributeNotification( AttributeId _nAttributeId, ITextAttributeListener* _pListener )
183     {
184         AttributeHandlerPool::const_iterator aHandlerPos = m_aAttributeHandlers.find( _nAttributeId  );
185         if ( aHandlerPos == m_aAttributeHandlers.end() )
186         {
187             ::rtl::Reference< AttributeHandler > aHandler = AttributeHandlerFactory::getHandlerFor( _nAttributeId, *m_pEngine->GetEmptyItemSet().GetPool() );
188             OSL_ENSURE( aHandler.is(), "RichTextControlImpl::enableAttributeNotification: no handler available for this attribute!" );
189             if ( !aHandler.is() )
190                 return;
191             SAL_WARN_IF( _nAttributeId != aHandler->getAttributeId(), "forms.richtext", "RichTextControlImpl::enableAttributeNotification: suspicious handler!" );
192 
193             aHandlerPos = m_aAttributeHandlers.emplace( _nAttributeId , aHandler ).first;
194         }
195 
196         // remember the listener
197         if ( _pListener )
198             m_aAttributeListeners.emplace( _nAttributeId, _pListener );
199 
200         // update (and broadcast) the state of this attribute
201         updateAttribute( _nAttributeId );
202     }
203 
204 
disableAttributeNotification(AttributeId _nAttributeId)205     void RichTextControlImpl::disableAttributeNotification( AttributeId _nAttributeId )
206     {
207         // forget the handler for this attribute
208         AttributeHandlerPool::iterator aHandlerPos = m_aAttributeHandlers.find( _nAttributeId );
209         if ( aHandlerPos != m_aAttributeHandlers.end() )
210             m_aAttributeHandlers.erase( aHandlerPos );
211 
212         // as well as the listener
213         AttributeListenerPool::iterator aListenerPos = m_aAttributeListeners.find( _nAttributeId );
214         if ( aListenerPos != m_aAttributeListeners.end() )
215             m_aAttributeListeners.erase( aListenerPos );
216     }
217 
218 
normalizeScriptDependentAttribute(SvxScriptSetItem & _rScriptSetItem)219     void RichTextControlImpl::normalizeScriptDependentAttribute( SvxScriptSetItem& _rScriptSetItem )
220     {
221         _rScriptSetItem.GetItemSet().Put( m_pView->GetAttribs(), false );
222         const SfxPoolItem* pNormalizedItem = _rScriptSetItem.GetItemOfScript( getSelectedScriptType() );
223 
224         WhichId nNormalizedWhichId = _rScriptSetItem.GetItemSet().GetPool()->GetWhich( _rScriptSetItem.Which() );
225         if ( pNormalizedItem )
226         {
227             _rScriptSetItem.GetItemSet().Put( pNormalizedItem->CloneSetWhich(nNormalizedWhichId) );
228         }
229         else
230             _rScriptSetItem.GetItemSet().InvalidateItem( nNormalizedWhichId );
231     }
232 
233 
implCheckUpdateCache(AttributeId _nAttribute,const AttributeState & _rState)234     void RichTextControlImpl::implCheckUpdateCache( AttributeId _nAttribute, const AttributeState& _rState )
235     {
236         StateCache::iterator aCachePos = m_aLastKnownStates.find( _nAttribute );
237         if ( aCachePos == m_aLastKnownStates.end() )
238         {   // nothing known about this attribute, yet
239             m_aLastKnownStates.emplace( _nAttribute, _rState );
240         }
241         else
242         {
243             if ( aCachePos->second == _rState )
244             {
245                 // nothing to do
246                 return;
247             }
248             aCachePos->second = _rState;
249         }
250 
251         // is there a dedicated listener for this particular attribute?
252         AttributeListenerPool::const_iterator aListenerPos = m_aAttributeListeners.find( _nAttribute );
253         if ( aListenerPos != m_aAttributeListeners.end( ) )
254             aListenerPos->second->onAttributeStateChanged( _nAttribute );
255 
256         // call our global listener, if there is one
257         if ( m_pTextAttrListener )
258             m_pTextAttrListener->onAttributeStateChanged( _nAttribute );
259     }
260 
261 
getSelectedScriptType() const262     SvtScriptType RichTextControlImpl::getSelectedScriptType() const
263     {
264         SvtScriptType nScript = m_pView->GetSelectedScriptType();
265         if ( nScript == SvtScriptType::NONE )
266             nScript = SvtLanguageOptions::GetScriptTypeOfLanguage( Application::GetSettings().GetLanguageTag().getLanguageType() );
267         return nScript;
268     }
269 
270 
EditEngineStatusChanged(const EditStatus & _rStatus)271     void RichTextControlImpl::EditEngineStatusChanged( const EditStatus& _rStatus )
272     {
273         EditStatusFlags nStatusWord( _rStatus.GetStatusWord() );
274         if  (   ( nStatusWord & EditStatusFlags::TEXTWIDTHCHANGED )
275             ||  ( nStatusWord & EditStatusFlags::TextHeightChanged )
276             )
277         {
278             if ( ( nStatusWord & EditStatusFlags::TextHeightChanged ) && windowHasAutomaticLineBreak() )
279                 m_pEngine->SetPaperSize( Size( m_pEngine->GetPaperSize().Width(), m_pEngine->GetTextHeight() ) );
280 
281             updateScrollbars();
282         }
283 
284         bool bHScroll = bool( nStatusWord & EditStatusFlags::HSCROLL );
285         bool bVScroll = bool( nStatusWord & EditStatusFlags::VSCROLL );
286 
287         // In case of *no* automatic line breaks, we also need to check for the *range* here.
288         // Normally, we would do this only after an EditStatusFlags::TEXTWIDTHCHANGED. However, due to a bug
289         // in the EditEngine (I believe so) this is not fired when the engine does not have
290         // the AutoPaperSize bits set.
291         // So in order to be properly notified, we would need the AutoPaperSize. But, with
292         // AutoPaperSize, other things do not work anymore: Either, when we set a MaxAutoPaperSize,
293         // then the view does automatic soft line breaks at the paper end - which we definitely do
294         // want. Or, if we did not set a MaxAutoPaperSize, then the view does not automatically scroll
295         // anymore in horizontal direction.
296         // So this is some kind of lose-lose situation ... :(
297         if ( !windowHasAutomaticLineBreak() && bHScroll )
298         {
299             updateScrollbars();
300             return;
301         }
302 
303         if ( bHScroll && m_pHScroll )
304             m_pHScroll->SetThumbPos( m_pView->GetVisArea().Left() );
305         if ( bVScroll && m_pVScroll )
306             m_pVScroll->SetThumbPos( m_pView->GetVisArea().Top() );
307     }
308 
309 
IMPL_LINK_NOARG(RichTextControlImpl,OnInvalidateAllAttributes,LinkParamNone *,void)310     IMPL_LINK_NOARG( RichTextControlImpl, OnInvalidateAllAttributes, LinkParamNone*, void )
311     {
312         updateAllAttributes();
313     }
314 
315 
IMPL_LINK(RichTextControlImpl,OnHScroll,ScrollBar *,_pScrollbar,void)316     IMPL_LINK( RichTextControlImpl, OnHScroll, ScrollBar*, _pScrollbar, void )
317     {
318         m_pView->Scroll( -_pScrollbar->GetDelta(), 0, ScrollRangeCheck::PaperWidthTextSize );
319     }
320 
321 
IMPL_LINK(RichTextControlImpl,OnVScroll,ScrollBar *,_pScrollbar,void)322     IMPL_LINK( RichTextControlImpl, OnVScroll, ScrollBar*, _pScrollbar, void )
323     {
324         m_pView->Scroll( 0, -_pScrollbar->GetDelta(), ScrollRangeCheck::PaperWidthTextSize );
325     }
326 
327 
ensureScrollbars()328     void RichTextControlImpl::ensureScrollbars()
329     {
330         bool bNeedVScroll = 0 != ( m_pAntiImpl->GetStyle() & WB_VSCROLL );
331         bool bNeedHScroll = 0 != ( m_pAntiImpl->GetStyle() & WB_HSCROLL );
332 
333         if ( ( bNeedVScroll == hasVScrollBar() ) && ( bNeedHScroll == hasHScrollBar( ) ) )
334             // nothing to do
335             return;
336 
337         // create or delete the scrollbars, as necessary
338         if ( !bNeedVScroll )
339         {
340             m_pVScroll.disposeAndClear();
341         }
342         else
343         {
344             m_pVScroll = VclPtr<ScrollBar>::Create( m_pAntiImpl, WB_VSCROLL | WB_DRAG | WB_REPEAT );
345             m_pVScroll->SetScrollHdl ( LINK( this, RichTextControlImpl, OnVScroll ) );
346             m_pVScroll->Show();
347         }
348 
349         if ( !bNeedHScroll )
350         {
351             m_pHScroll.disposeAndClear();
352         }
353         else
354         {
355             m_pHScroll = VclPtr<ScrollBar>::Create( m_pAntiImpl, WB_HSCROLL | WB_DRAG | WB_REPEAT );
356             m_pHScroll->SetScrollHdl ( LINK( this, RichTextControlImpl, OnHScroll ) );
357             m_pHScroll->Show();
358         }
359 
360         if ( m_pHScroll && m_pVScroll )
361         {
362             m_pScrollCorner.disposeAndClear();
363             m_pScrollCorner = VclPtr<ScrollBarBox>::Create( m_pAntiImpl );
364             m_pScrollCorner->Show();
365         }
366         else
367         {
368             m_pScrollCorner.disposeAndClear();
369         }
370 
371         layoutWindow();
372     }
373 
374 
ensureLineBreakSetting()375     void RichTextControlImpl::ensureLineBreakSetting()
376     {
377         if ( !windowHasAutomaticLineBreak() )
378             m_pEngine->SetPaperSize( Size( EMPTY_PAPER_SIZE, EMPTY_PAPER_SIZE ) );
379 
380         layoutWindow();
381     }
382 
383 
layoutWindow()384     void RichTextControlImpl::layoutWindow()
385     {
386         if ( !m_bHasEverBeenShown )
387             // no need to do anything. Especially, no need to set the paper size on the
388             // EditEngine to anything...
389             return;
390 
391         const StyleSettings& rStyleSettings = m_pAntiImpl->GetSettings().GetStyleSettings();
392 
393         tools::Long nScrollBarWidth = m_pVScroll ? rStyleSettings.GetScrollBarSize() : 0;
394         tools::Long nScrollBarHeight = m_pHScroll ? rStyleSettings.GetScrollBarSize() : 0;
395 
396         if ( m_pAntiImpl->IsZoom() )
397         {
398             nScrollBarWidth = m_pAntiImpl->CalcZoom( nScrollBarWidth );
399             nScrollBarHeight = m_pAntiImpl->CalcZoom( nScrollBarHeight );
400         }
401 
402         // the overall size we can use
403         Size aPlaygroundSizePixel( m_pAntiImpl->GetOutputSizePixel() );
404 
405         // the size of the viewport - note that the viewport does *not* occupy all the place
406         // which is left when subtracting the scrollbar width/height
407         Size aViewportPlaygroundPixel( aPlaygroundSizePixel );
408         aViewportPlaygroundPixel.setWidth( ::std::max( tools::Long( 10 ), tools::Long( aViewportPlaygroundPixel.Width() - nScrollBarWidth ) ) );
409         aViewportPlaygroundPixel.setHeight( ::std::max( tools::Long( 10 ), tools::Long( aViewportPlaygroundPixel.Height() - nScrollBarHeight ) ) );
410         Size aViewportPlaygroundLogic( m_pViewport->PixelToLogic( aViewportPlaygroundPixel ) );
411 
412         const tools::Long nOffset = 2;
413         Size aViewportSizePixel( aViewportPlaygroundPixel.Width() - 2 * nOffset, aViewportPlaygroundPixel.Height() - 2 * nOffset );
414         Size aViewportSizeLogic( m_pViewport->PixelToLogic( aViewportSizePixel ) );
415 
416         // position the viewport
417         m_pViewport->SetPosSizePixel( Point( nOffset, nOffset ), aViewportSizePixel );
418         // position the scrollbars
419         if ( m_pVScroll )
420             m_pVScroll->SetPosSizePixel( Point( aViewportPlaygroundPixel.Width(), 0 ), Size( nScrollBarWidth, aViewportPlaygroundPixel.Height() ) );
421         if ( m_pHScroll )
422             m_pHScroll->SetPosSizePixel( Point( 0, aViewportPlaygroundPixel.Height() ), Size( aViewportPlaygroundPixel.Width(), nScrollBarHeight ) );
423         if ( m_pScrollCorner )
424             m_pScrollCorner->SetPosSizePixel( Point( aViewportPlaygroundPixel.Width(), aViewportPlaygroundPixel.Height() ), Size( nScrollBarWidth, nScrollBarHeight ) );
425 
426         // paper size
427         if ( windowHasAutomaticLineBreak() )
428             m_pEngine->SetPaperSize( Size( aViewportSizeLogic.Width(), m_pEngine->GetTextHeight() ) );
429 
430         // output area of the view
431         m_pView->SetOutputArea( tools::Rectangle( Point( ), aViewportSizeLogic ) );
432         m_pView->SetVisArea( tools::Rectangle( Point( ), aViewportSizeLogic ) );
433 
434         if ( m_pVScroll )
435         {
436             m_pVScroll->SetVisibleSize( aViewportPlaygroundLogic.Height() );
437 
438             // the default height of a text line...
439             tools::Long nFontHeight = m_pEngine->GetStandardFont(0).GetFontSize().Height();
440             // ... is the scroll size for the vertical scrollbar
441             m_pVScroll->SetLineSize( nFontHeight );
442             // the viewport width, minus one line, is the page scroll size
443             m_pVScroll->SetPageSize( ::std::max( nFontHeight, aViewportPlaygroundLogic.Height() - nFontHeight ) );
444         }
445 
446         // the font width
447         if ( m_pHScroll )
448         {
449             m_pHScroll->SetVisibleSize( aViewportPlaygroundLogic.Width() );
450 
451             tools::Long nFontWidth = m_pEngine->GetStandardFont(0).GetFontSize().Width();
452             if ( !nFontWidth )
453             {
454                 m_pViewport->GetOutDev()->Push( PushFlags::FONT );
455                 m_pViewport->SetFont( m_pEngine->GetStandardFont(0) );
456                 nFontWidth = m_pViewport->GetTextWidth( "x" );
457                 m_pViewport->GetOutDev()->Pop();
458             }
459             // ... is the scroll size for the horizontal scrollbar
460             m_pHScroll->SetLineSize( 5 * nFontWidth );
461             // the viewport height, minus one character, is the page scroll size
462             m_pHScroll->SetPageSize( ::std::max( nFontWidth, aViewportPlaygroundLogic.Width() - nFontWidth ) );
463         }
464 
465         // update range and position of the scrollbars
466         updateScrollbars();
467     }
468 
469 
updateScrollbars()470     void RichTextControlImpl::updateScrollbars()
471     {
472         if ( m_pVScroll )
473         {
474             tools::Long nOverallTextHeight = m_pEngine->GetTextHeight();
475             m_pVScroll->SetRange( Range( 0, nOverallTextHeight ) );
476             m_pVScroll->SetThumbPos( m_pView->GetVisArea().Top() );
477         }
478 
479         if ( m_pHScroll )
480         {
481             Size aPaperSize( m_pEngine->GetPaperSize() );
482             tools::Long nOverallTextWidth = ( aPaperSize.Width() == EMPTY_PAPER_SIZE ) ? m_pEngine->CalcTextWidth() : aPaperSize.Width();
483             m_pHScroll->SetRange( Range( 0, nOverallTextWidth ) );
484             m_pHScroll->SetThumbPos( m_pView->GetVisArea().Left() );
485         }
486     }
487 
488 
notifyInitShow()489     void RichTextControlImpl::notifyInitShow()
490     {
491         if ( !m_bHasEverBeenShown )
492         {
493             m_bHasEverBeenShown = true;
494             layoutWindow();
495         }
496     }
497 
498 
notifyStyleChanged()499     void RichTextControlImpl::notifyStyleChanged()
500     {
501         ensureScrollbars();
502         ensureLineBreakSetting();
503     }
504 
505 
notifyZoomChanged()506     void RichTextControlImpl::notifyZoomChanged()
507     {
508         const Fraction& rZoom = m_pAntiImpl->GetZoom();
509 
510         MapMode aMapMode( m_pAntiImpl->GetMapMode() );
511         aMapMode.SetScaleX( rZoom );
512         aMapMode.SetScaleY( rZoom );
513         m_pAntiImpl->SetMapMode( aMapMode );
514 
515         m_pViewport->SetZoom( rZoom );
516         m_pViewport->SetMapMode( aMapMode );
517 
518         layoutWindow();
519     }
520 
521 
windowHasAutomaticLineBreak()522     bool RichTextControlImpl::windowHasAutomaticLineBreak()
523     {
524         return ( m_pAntiImpl->GetStyle() & WB_WORDBREAK ) != 0;
525     }
526 
527 
SetReadOnly(bool _bReadOnly)528     void RichTextControlImpl::SetReadOnly( bool _bReadOnly )
529     {
530         m_pView->SetReadOnly( _bReadOnly );
531     }
532 
533 
IsReadOnly() const534     bool RichTextControlImpl::IsReadOnly() const
535     {
536         return m_pView->IsReadOnly( );
537     }
538 
539 
540     namespace
541     {
lcl_inflate(tools::Rectangle & _rRect,tools::Long _nInflateX,tools::Long _nInflateY)542         void lcl_inflate( tools::Rectangle& _rRect, tools::Long _nInflateX, tools::Long _nInflateY )
543         {
544             _rRect.AdjustLeft( -_nInflateX );
545             _rRect.AdjustRight(_nInflateX );
546             _rRect.AdjustTop( -_nInflateY );
547             _rRect.AdjustBottom(_nInflateY );
548         }
549     }
550 
HandleCommand(const CommandEvent & _rEvent)551     bool RichTextControlImpl::HandleCommand( const CommandEvent& _rEvent )
552     {
553         if (  ( _rEvent.GetCommand() == CommandEventId::Wheel )
554            || ( _rEvent.GetCommand() == CommandEventId::StartAutoScroll )
555            || ( _rEvent.GetCommand() == CommandEventId::AutoScroll )
556            )
557         {
558             m_pAntiImpl->HandleScrollCommand( _rEvent, m_pHScroll, m_pVScroll );
559             return true;
560         }
561         return false;
562     }
563 
564 
Draw(OutputDevice * _pDev,const Point & _rPos,const Size & _rSize)565     void RichTextControlImpl::Draw( OutputDevice* _pDev, const Point& _rPos, const Size& _rSize )
566     {
567         // need to normalize the map mode of the device - every paint operation on any device needs
568         // to use the same map mode
569         _pDev->Push( PushFlags::MAPMODE | PushFlags::LINECOLOR | PushFlags::FILLCOLOR );
570 
571         // enforce our "normalize map mode" on the device
572         MapMode aRefMapMode( m_pEngine->GetRefDevice()->GetMapMode() );
573         MapMode aOriginalMapMode( _pDev->GetMapMode() );
574         MapMode aNormalizedMapMode( aRefMapMode.GetMapUnit(), aRefMapMode.GetOrigin(), aOriginalMapMode.GetScaleX(), aOriginalMapMode.GetScaleY() );
575         _pDev->SetMapMode( aNormalizedMapMode );
576 
577         // translate coordinates
578         Point aPos( _rPos );
579         Size aSize( _rSize );
580         if ( aOriginalMapMode.GetMapUnit() == MapUnit::MapPixel )
581         {
582             aPos = _pDev->PixelToLogic( _rPos, aNormalizedMapMode );
583             aSize = _pDev->PixelToLogic( _rSize, aNormalizedMapMode );
584         }
585         else
586         {
587             aPos = OutputDevice::LogicToLogic( _rPos, aOriginalMapMode, aNormalizedMapMode );
588             aSize = OutputDevice::LogicToLogic( _rSize, aOriginalMapMode, aNormalizedMapMode );
589         }
590 
591         tools::Rectangle aPlayground( aPos, aSize );
592         Size aOnePixel( _pDev->PixelToLogic( Size( 1, 1 ) ) );
593         aPlayground.AdjustRight( -(aOnePixel.Width()) );
594         aPlayground.AdjustBottom( -(aOnePixel.Height()) );
595 
596         // background
597         _pDev->SetLineColor();
598         _pDev->DrawRect( aPlayground );
599 
600         // do we need to draw a border?
601         bool bBorder = ( m_pAntiImpl->GetStyle() & WB_BORDER );
602         if ( bBorder )
603             _pDev->SetLineColor( m_pAntiImpl->GetSettings().GetStyleSettings().GetMonoColor() );
604         else
605             _pDev->SetLineColor();
606         _pDev->SetFillColor( m_pAntiImpl->GetBackground().GetColor() );
607         _pDev->DrawRect( aPlayground );
608 
609         if ( bBorder )
610             // don't draw the text over the border
611             lcl_inflate( aPlayground, -aOnePixel.Width(), -aOnePixel.Height() );
612 
613         // leave a space of two pixels between the "surroundings" of the control
614         // and the content
615         lcl_inflate( aPlayground, -aOnePixel.Width(), -aOnePixel.Height() );
616         lcl_inflate( aPlayground, -aOnePixel.Width(), -aOnePixel.Height() );
617 
618         // actually draw the content
619         m_pEngine->Draw(*_pDev, aPlayground, Point(), true);
620 
621         _pDev->Pop();
622     }
623 
624 
SetBackgroundColor()625     void RichTextControlImpl::SetBackgroundColor( )
626     {
627         SetBackgroundColor( Application::GetSettings().GetStyleSettings().GetFieldColor() );
628     }
629 
630 
SetBackgroundColor(const Color & _rColor)631     void RichTextControlImpl::SetBackgroundColor( const Color& _rColor )
632     {
633         Wallpaper aWallpaper( _rColor );
634         m_pAntiImpl->SetBackground( aWallpaper );
635         m_pViewport->SetBackground( aWallpaper );
636     }
637 
638 
SetHideInactiveSelection(bool _bHide)639     void RichTextControlImpl::SetHideInactiveSelection( bool _bHide )
640     {
641         m_pViewport->SetHideInactiveSelection( _bHide );
642     }
643 
644 
GetHideInactiveSelection() const645     bool RichTextControlImpl::GetHideInactiveSelection() const
646     {
647         return m_pViewport->GetHideInactiveSelection( );
648     }
649 
650 
651 }   // namespace frm
652 
653 
654 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
655