1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include "richtextcontrol.hxx"
21 #include <frm_strings.hxx>
22 #include <services.hxx>
24 #include "richtextmodel.hxx"
25 #include "richtextvclcontrol.hxx"
26 #include "clipboarddispatcher.hxx"
27 #include "parametrizedattributedispatcher.hxx"
28 #include "specialdispatchers.hxx"
30 #include <com/sun/star/awt/PosSize.hpp>
32 #include <toolkit/helper/vclunohelper.hxx>
33 #include <tools/debug.hxx>
34 #include <comphelper/diagnose_ex.hxx>
35 #include <sal/log.hxx>
36 #include <vcl/svapp.hxx>
38 #include <svx/svxids.hrc>
39 #include <editeng/editview.hxx>
40 #include <svl/itemset.hxx>
41 #include <svl/itempool.hxx>
42 #include <sfx2/msgpool.hxx>
43 #include <sfx2/msg.hxx>
49 using namespace ::com::sun::star::uno
;
50 using namespace ::com::sun::star::beans
;
51 using namespace ::com::sun::star::awt
;
52 using namespace ::com::sun::star::lang
;
53 using namespace ::com::sun::star::frame
;
55 ORichTextControl::ORichTextControl()
60 ORichTextControl::~ORichTextControl()
65 IMPLEMENT_FORWARD_XTYPEPROVIDER2( ORichTextControl
, UnoEditControl
, ORichTextControl_Base
)
68 Any SAL_CALL
ORichTextControl::queryAggregation( const Type
& _rType
)
70 Any aReturn
= UnoEditControl::queryAggregation( _rType
);
72 if ( !aReturn
.hasValue() )
73 aReturn
= ORichTextControl_Base::queryInterface( _rType
);
82 void implAdjustTriStateFlag( const Reference
< XPropertySet
>& _rxProps
, const OUString
& _rPropertyName
,
83 WinBits
& _rAllBits
, WinBits _nPositiveFlag
, WinBits nNegativeFlag
)
85 bool bFlagValue
= false;
86 if ( _rxProps
->getPropertyValue( _rPropertyName
) >>= bFlagValue
)
87 _rAllBits
|= ( bFlagValue
? _nPositiveFlag
: nNegativeFlag
);
91 void implAdjustTwoStateFlag( const Any
& _rValue
, WinBits
& _rAllBits
, WinBits _nFlag
, bool _bInvert
)
93 bool bFlagValue
= false;
94 if ( _rValue
>>= bFlagValue
)
97 bFlagValue
= !bFlagValue
;
101 _rAllBits
&= ~_nFlag
;
106 void implAdjustTwoStateFlag( const Reference
< XPropertySet
>& _rxProps
, const OUString
& _rPropertyName
,
107 WinBits
& _rAllBits
, WinBits _nFlag
, bool _bInvert
= false )
109 implAdjustTwoStateFlag( _rxProps
->getPropertyValue( _rPropertyName
), _rAllBits
, _nFlag
, _bInvert
);
113 void adjustTwoStateWinBit( vcl::Window
* _pWindow
, const Any
& _rValue
, WinBits _nFlag
, bool _bInvert
= false )
115 WinBits nBits
= _pWindow
->GetStyle();
116 implAdjustTwoStateFlag( _rValue
, nBits
, _nFlag
, _bInvert
);
117 _pWindow
->SetStyle( nBits
);
121 WinBits
getWinBits( const Reference
< XControlModel
>& _rxModel
)
126 Reference
< XPropertySet
> xProps( _rxModel
, UNO_QUERY
);
129 sal_Int16 nBorder
= 0;
130 xProps
->getPropertyValue( PROPERTY_BORDER
) >>= nBorder
;
134 implAdjustTriStateFlag( xProps
, PROPERTY_TABSTOP
, nBits
, WB_TABSTOP
, WB_NOTABSTOP
);
135 implAdjustTwoStateFlag( xProps
, PROPERTY_HSCROLL
, nBits
, WB_HSCROLL
);
136 implAdjustTwoStateFlag( xProps
, PROPERTY_VSCROLL
, nBits
, WB_VSCROLL
);
137 implAdjustTwoStateFlag( xProps
, PROPERTY_HARDLINEBREAKS
, nBits
, WB_WORDBREAK
, true );
140 catch( const Exception
& )
142 DBG_UNHANDLED_EXCEPTION("forms.richtext");
149 void SAL_CALL
ORichTextControl::createPeer( const Reference
< XToolkit
>& _rToolkit
, const Reference
< XWindowPeer
>& _rParentPeer
)
151 bool bReallyActAsRichText
= false;
154 Reference
< XPropertySet
> xModelProps( getModel(), UNO_QUERY_THROW
);
155 xModelProps
->getPropertyValue( PROPERTY_RICH_TEXT
) >>= bReallyActAsRichText
;
157 catch( const Exception
& )
159 DBG_UNHANDLED_EXCEPTION("forms.richtext");
162 if ( !bReallyActAsRichText
)
164 UnoEditControl::createPeer( _rToolkit
, _rParentPeer
);
168 SolarMutexGuard aGuard
;
173 mbCreatingPeer
= true;
175 // determine the VCL window for the parent
176 vcl::Window
* pParentWin
= nullptr;
177 if ( _rParentPeer
.is() )
179 VCLXWindow
* pParentXWin
= dynamic_cast<VCLXWindow
*>( _rParentPeer
.get() );
181 pParentWin
= pParentXWin
->GetWindow();
182 DBG_ASSERT( pParentWin
, "ORichTextControl::createPeer: could not obtain the VCL-level parent window!" );
186 Reference
< XControlModel
> xModel( getModel() );
187 rtl::Reference
<ORichTextPeer
> pPeer
= ORichTextPeer::Create( xModel
, pParentWin
, getWinBits( xModel
) );
188 DBG_ASSERT( pPeer
, "ORichTextControl::createPeer: invalid peer returned!" );
191 // announce the peer to the base class
194 // initialize ourself (and thus the peer) with the model properties
197 Reference
< XView
> xPeerView( getPeer(), UNO_QUERY
);
198 if ( xPeerView
.is() )
200 xPeerView
->setZoom( maComponentInfos
.nZoomX
, maComponentInfos
.nZoomY
);
201 xPeerView
->setGraphics( mxGraphics
);
204 // a lot of initial settings from our component infos
205 setPosSize( maComponentInfos
.nX
, maComponentInfos
.nY
, maComponentInfos
.nWidth
, maComponentInfos
.nHeight
, PosSize::POSSIZE
);
207 pPeer
->setVisible ( maComponentInfos
.bVisible
&& !mbDesignMode
);
208 pPeer
->setEnable ( maComponentInfos
.bEnable
);
209 pPeer
->setDesignMode( mbDesignMode
);
214 mbCreatingPeer
= false;
217 OUString SAL_CALL
ORichTextControl::getImplementationName()
219 return "com.sun.star.comp.form.ORichTextControl";
222 Sequence
< OUString
> SAL_CALL
ORichTextControl::getSupportedServiceNames()
224 return { "com.sun.star.awt.UnoControl",
225 "com.sun.star.awt.UnoControlEdit",
226 FRM_SUN_CONTROL_RICHTEXTCONTROL
};
229 Reference
< XDispatch
> SAL_CALL
ORichTextControl::queryDispatch( const css::util::URL
& _rURL
, const OUString
& _rTargetFrameName
, sal_Int32 _nSearchFlags
)
231 Reference
< XDispatch
> aReturn
;
232 Reference
< XDispatchProvider
> xTypedPeer( getPeer(), UNO_QUERY
);
233 if ( xTypedPeer
.is() )
235 aReturn
= xTypedPeer
->queryDispatch( _rURL
, _rTargetFrameName
, _nSearchFlags
);
240 Sequence
< Reference
< XDispatch
> > SAL_CALL
ORichTextControl::queryDispatches( const Sequence
< DispatchDescriptor
>& _rRequests
)
242 Reference
<XDispatchProvider
> xTypedPeer(getPeer(), UNO_QUERY
);
244 return xTypedPeer
->queryDispatches(_rRequests
);
245 return Sequence
<Reference
<XDispatch
>>();
248 bool ORichTextControl::requiresNewPeer( const OUString
& _rPropertyName
) const
250 return UnoControl::requiresNewPeer( _rPropertyName
) || _rPropertyName
== PROPERTY_RICH_TEXT
;
254 rtl::Reference
<ORichTextPeer
> ORichTextPeer::Create( const Reference
< XControlModel
>& _rxModel
, vcl::Window
* _pParentWindow
, WinBits _nStyle
)
256 DBG_TESTSOLARMUTEX();
258 // the EditEngine of the model
259 RichTextEngine
* pEngine
= ORichTextModel::getEditEngine( _rxModel
);
260 OSL_ENSURE( pEngine
, "ORichTextPeer::Create: could not obtain the edit engine from the model!" );
265 rtl::Reference
<ORichTextPeer
> pPeer(new ORichTextPeer
);
267 // the VCL control for the peer
268 VclPtrInstance
<RichTextControl
> pRichTextControl( pEngine
, _pParentWindow
, _nStyle
, nullptr, pPeer
.get() );
271 pRichTextControl
->SetComponentInterface( pPeer
);
278 ORichTextPeer::ORichTextPeer()
283 ORichTextPeer::~ORichTextPeer()
288 void ORichTextPeer::dispose( )
291 SolarMutexGuard aGuard
;
292 VclPtr
< RichTextControl
> pRichTextControl
= GetAs
< RichTextControl
>();
294 if ( pRichTextControl
)
296 for (auto const& dispatcher
: m_aDispatchers
)
298 pRichTextControl
->disableAttributeNotification(dispatcher
.first
);
299 dispatcher
.second
->dispose();
303 AttributeDispatchers().swap(m_aDispatchers
);
306 VCLXWindow::dispose();
310 void SAL_CALL
ORichTextPeer::draw( sal_Int32 _nX
, sal_Int32 _nY
)
312 SolarMutexGuard aGuard
;
314 VclPtr
< RichTextControl
> pControl
= GetAs
< RichTextControl
>();
318 OutputDevice
* pTargetDevice
= VCLUnoHelper::GetOutputDevice( getGraphics() );
319 OSL_ENSURE( pTargetDevice
!= nullptr, "ORichTextPeer::draw: no graphics -> no drawing!" );
320 if ( !pTargetDevice
)
323 const MapUnit eTargetUnit
= pTargetDevice
->GetMapMode().GetMapUnit();
324 ::Point
aPos( _nX
, _nY
);
325 // the XView::draw API talks about pixels, always ...
326 if ( eTargetUnit
!= MapUnit::MapPixel
)
327 aPos
= pTargetDevice
->PixelToLogic( aPos
);
329 pControl
->Draw( pTargetDevice
, aPos
, SystemTextColorFlags::NoControls
);
333 void SAL_CALL
ORichTextPeer::setProperty( const OUString
& _rPropertyName
, const Any
& _rValue
)
339 VCLXWindow::setProperty( _rPropertyName
, _rValue
);
343 if ( _rPropertyName
== PROPERTY_BACKGROUNDCOLOR
)
345 VclPtr
< RichTextControl
> pControl
= GetAs
< RichTextControl
>();
346 if ( !_rValue
.hasValue() )
348 pControl
->SetBackgroundColor( );
352 Color nColor
= COL_TRANSPARENT
;
354 pControl
->SetBackgroundColor( nColor
);
357 else if ( _rPropertyName
== PROPERTY_HSCROLL
)
359 adjustTwoStateWinBit( GetWindow(), _rValue
, WB_HSCROLL
);
361 else if ( _rPropertyName
== PROPERTY_VSCROLL
)
363 adjustTwoStateWinBit( GetWindow(), _rValue
, WB_VSCROLL
);
365 else if ( _rPropertyName
== PROPERTY_HARDLINEBREAKS
)
367 adjustTwoStateWinBit( GetWindow(), _rValue
, WB_WORDBREAK
, true );
369 else if ( _rPropertyName
== PROPERTY_READONLY
)
371 VclPtr
< RichTextControl
> pControl
= GetAs
< RichTextControl
>();
372 bool bReadOnly( pControl
->IsReadOnly() );
373 OSL_VERIFY( _rValue
>>= bReadOnly
);
374 pControl
->SetReadOnly( bReadOnly
);
376 // update the dispatchers
377 for (auto const& dispatcher
: m_aDispatchers
)
379 dispatcher
.second
->invalidate();
382 else if ( _rPropertyName
== PROPERTY_HIDEINACTIVESELECTION
)
384 VclPtr
< RichTextControl
> pRichTextControl
= GetAs
< RichTextControl
>();
385 bool bHide
= pRichTextControl
->GetHideInactiveSelection();
386 OSL_VERIFY( _rValue
>>= bHide
);
387 pRichTextControl
->SetHideInactiveSelection( bHide
);
390 VCLXWindow::setProperty( _rPropertyName
, _rValue
);
394 IMPLEMENT_FORWARD_XINTERFACE2( ORichTextPeer
, VCLXWindow
, ORichTextPeer_Base
)
397 IMPLEMENT_FORWARD_XTYPEPROVIDER2( ORichTextPeer
, VCLXWindow
, ORichTextPeer_Base
)
402 SfxSlotId
lcl_translateConflictingSlot( SfxSlotId _nIDFromPool
)
405 // unfortunately, some of our applications have some conflicting slots,
406 // i.e. slots which have the same UNO name as an existing other (common)
408 // For instance, both the slots SID_SET_SUPER_SCRIPT (from SVX) and FN_SET_SUPER_SCRIPT
409 // (from SW) have the UNO name "SuperScript".
410 // Now, if the controls lives in a text document, and asks the SfxSlotPool for
411 // the id belonging to "SuperScript", it gets the FN_SET_SUPER_SCRIPT - which
412 // is completely unknown to the EditEngine.
413 // So, we need to translate such conflicting ids.
415 // Note that the real solution would be to fix the applications to
416 // *not* define conflicting slots. Alternatively, if SFX would provide a slot pool
417 // which is *static* (i.e. independent on the active application), then we
418 // would also never encounter such a conflict.
419 SfxSlotId
nReturn( _nIDFromPool
);
420 switch ( _nIDFromPool
)
422 case 20411: /* FM_SET_SUPER_SCRIPT, originating in SW */
423 nReturn
= SID_SET_SUPER_SCRIPT
;
425 case 20412: /* FN_SET_SUB_SCRIPT, originating in SW */
426 nReturn
= SID_SET_SUB_SCRIPT
;
434 ORichTextPeer::SingleAttributeDispatcher
ORichTextPeer::implCreateDispatcher( SfxSlotId _nSlotId
, const css::util::URL
& _rURL
)
436 VclPtr
< RichTextControl
> pRichTextControl
= GetAs
< RichTextControl
>();
437 OSL_PRECOND( pRichTextControl
, "ORichTextPeer::implCreateDispatcher: invalid window!" );
438 if ( !pRichTextControl
)
439 return SingleAttributeDispatcher( nullptr );
441 rtl::Reference
<ORichTextFeatureDispatcher
> pDispatcher
;
442 rtl::Reference
<OAttributeDispatcher
> pAttributeDispatcher
;
446 pDispatcher
= new OClipboardDispatcher( pRichTextControl
->getView(), OClipboardDispatcher::eCut
);
450 pDispatcher
= new OClipboardDispatcher( pRichTextControl
->getView(), OClipboardDispatcher::eCopy
);
454 pDispatcher
= new OPasteClipboardDispatcher( pRichTextControl
->getView() );
458 pDispatcher
= new OSelectAllDispatcher( pRichTextControl
->getView(), _rURL
);
461 case SID_ATTR_PARA_LEFT_TO_RIGHT
:
462 case SID_ATTR_PARA_RIGHT_TO_LEFT
:
463 pAttributeDispatcher
= new OParagraphDirectionDispatcher( pRichTextControl
->getView(), _nSlotId
, _rURL
, pRichTextControl
);
466 case SID_TEXTDIRECTION_TOP_TO_BOTTOM
:
467 case SID_TEXTDIRECTION_LEFT_TO_RIGHT
:
468 pDispatcher
= new OTextDirectionDispatcher( pRichTextControl
->getView(), _rURL
);
471 case SID_ATTR_PARA_HANGPUNCTUATION
:
472 case SID_ATTR_PARA_FORBIDDEN_RULES
:
473 case SID_ATTR_PARA_SCRIPTSPACE
:
474 pAttributeDispatcher
= new OAsianFontLayoutDispatcher( pRichTextControl
->getView(), _nSlotId
, _rURL
, pRichTextControl
);
479 const SfxItemPool
& rPool
= *pRichTextControl
->getView().GetEmptyItemSet().GetPool();
480 bool bSupportedSlot
= rPool
.IsInRange( rPool
.GetWhich( _nSlotId
) );
482 if ( !bSupportedSlot
)
483 bSupportedSlot
= RichTextControl::isMappableSlot( _nSlotId
);
485 if ( bSupportedSlot
)
486 { // it's really a slot which is supported by the EditEngine
488 bool bNeedParametrizedDispatcher
= true;
489 if ( ( _nSlotId
== SID_ATTR_CHAR_POSTURE
)
490 || ( _nSlotId
== SID_ATTR_CHAR_CJK_POSTURE
)
491 || ( _nSlotId
== SID_ATTR_CHAR_CTL_POSTURE
)
492 || ( _nSlotId
== SID_ATTR_CHAR_LATIN_POSTURE
)
493 || ( _nSlotId
== SID_ATTR_CHAR_WEIGHT
)
494 || ( _nSlotId
== SID_ATTR_CHAR_CJK_WEIGHT
)
495 || ( _nSlotId
== SID_ATTR_CHAR_CTL_WEIGHT
)
496 || ( _nSlotId
== SID_ATTR_CHAR_LATIN_WEIGHT
)
497 || ( _nSlotId
== SID_ATTR_CHAR_LANGUAGE
)
498 || ( _nSlotId
== SID_ATTR_CHAR_CJK_LANGUAGE
)
499 || ( _nSlotId
== SID_ATTR_CHAR_CTL_LANGUAGE
)
500 || ( _nSlotId
== SID_ATTR_CHAR_LATIN_LANGUAGE
)
501 || ( _nSlotId
== SID_ATTR_CHAR_CONTOUR
)
502 || ( _nSlotId
== SID_ATTR_CHAR_SHADOWED
)
503 || ( _nSlotId
== SID_ATTR_CHAR_WORDLINEMODE
)
504 || ( _nSlotId
== SID_ATTR_CHAR_COLOR
)
505 || ( _nSlotId
== SID_ATTR_CHAR_RELIEF
)
506 || ( _nSlotId
== SID_ATTR_CHAR_KERNING
)
507 || ( _nSlotId
== SID_ATTR_CHAR_AUTOKERN
)
508 || ( _nSlotId
== SID_ATTR_CHAR_SCALEWIDTH
)
511 bNeedParametrizedDispatcher
= true;
515 SfxSlotPool
& rSlotPool
= SfxSlotPool::GetSlotPool();
516 const SfxSlot
* pSlot
= rSlotPool
.GetSlot( _nSlotId
);
517 const SfxType
* pType
= pSlot
? pSlot
->GetType() : nullptr;
520 bNeedParametrizedDispatcher
= ( pType
->nAttribs
> 0 );
524 if ( bNeedParametrizedDispatcher
)
526 pAttributeDispatcher
= new OParametrizedAttributeDispatcher( pRichTextControl
->getView(), _nSlotId
, _rURL
, pRichTextControl
);
530 pAttributeDispatcher
= new OAttributeDispatcher( pRichTextControl
->getView(), _nSlotId
, _rURL
, pRichTextControl
);
535 SAL_WARN("forms.richtext", "ORichTextPeer::implCreateDispatcher: not creating dispatcher (unsupported slot) for "
542 SingleAttributeDispatcher
xDispatcher( pDispatcher
);
543 if ( pAttributeDispatcher
)
545 xDispatcher
= SingleAttributeDispatcher( pAttributeDispatcher
);
546 pRichTextControl
->enableAttributeNotification( _nSlotId
, pAttributeDispatcher
.get() );
555 SfxSlotId
lcl_getSlotFromUnoName( SfxSlotPool
const & _rSlotPool
, const OUString
& _rUnoSlotName
)
557 const SfxSlot
* pSlot
= _rSlotPool
.GetUnoSlot( _rUnoSlotName
);
560 // okay, there's a slot with the given UNO name
561 return lcl_translateConflictingSlot( pSlot
->GetSlotId() );
564 // some hard-coded slots, which do not have a UNO name at SFX level, but which
565 // we nevertheless need to transport via UNO mechanisms, so we need a name
566 if ( _rUnoSlotName
== "AllowHangingPunctuation" )
567 return SID_ATTR_PARA_HANGPUNCTUATION
;
568 if ( _rUnoSlotName
== "ApplyForbiddenCharacterRules" )
569 return SID_ATTR_PARA_FORBIDDEN_RULES
;
570 if ( _rUnoSlotName
== "UseScriptSpacing" )
571 return SID_ATTR_PARA_SCRIPTSPACE
;
573 OSL_ENSURE( pSlot
, "lcl_getSlotFromUnoName: unknown UNO slot name!" );
579 Reference
< XDispatch
> SAL_CALL
ORichTextPeer::queryDispatch( const css::util::URL
& _rURL
, const OUString
& /*_rTargetFrameName*/, sal_Int32
/*_nSearchFlags*/ )
581 Reference
< XDispatch
> xReturn
;
584 OSL_FAIL( "ORichTextPeer::queryDispatch: already disposed?" );
589 static constexpr std::u16string_view
sUnoProtocolPrefix( u
".uno:" );
590 if ( _rURL
.Complete
.startsWith( sUnoProtocolPrefix
) )
592 OUString sUnoSlotName
= _rURL
.Complete
.copy( sUnoProtocolPrefix
.size() );
593 SfxSlotId nSlotId
= lcl_getSlotFromUnoName( SfxSlotPool::GetSlotPool(), sUnoSlotName
);
596 // do we already have a dispatcher for this?
597 AttributeDispatchers::const_iterator aDispatcherPos
= m_aDispatchers
.find( nSlotId
);
598 if ( aDispatcherPos
== m_aDispatchers
.end() )
600 SingleAttributeDispatcher pDispatcher
= implCreateDispatcher( nSlotId
, _rURL
);
601 if ( pDispatcher
.is() )
603 aDispatcherPos
= m_aDispatchers
.emplace( nSlotId
, pDispatcher
).first
;
607 if ( aDispatcherPos
!= m_aDispatchers
.end() )
608 xReturn
= aDispatcherPos
->second
.get();
616 Sequence
< Reference
< XDispatch
> > SAL_CALL
ORichTextPeer::queryDispatches( const Sequence
< DispatchDescriptor
>& _rRequests
)
618 Sequence
< Reference
< XDispatch
> > aReturn( _rRequests
.getLength() );
619 Reference
< XDispatch
>* pReturn
= aReturn
.getArray();
621 const DispatchDescriptor
* pRequest
= _rRequests
.getConstArray();
622 const DispatchDescriptor
* pRequestEnd
= pRequest
+ _rRequests
.getLength();
623 for ( ; pRequest
!= pRequestEnd
; ++pRequest
, ++pReturn
)
625 *pReturn
= queryDispatch( pRequest
->FeatureURL
, pRequest
->FrameName
, pRequest
->SearchFlags
);
631 void ORichTextPeer::onSelectionChanged()
633 AttributeDispatchers::iterator aDispatcherPos
= m_aDispatchers
.find( SID_COPY
);
634 if ( aDispatcherPos
!= m_aDispatchers
.end() )
635 aDispatcherPos
->second
->invalidate();
637 aDispatcherPos
= m_aDispatchers
.find( SID_CUT
);
638 if ( aDispatcherPos
!= m_aDispatchers
.end() )
639 aDispatcherPos
->second
->invalidate();
645 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
646 com_sun_star_comp_form_ORichTextControl_get_implementation(css::uno::XComponentContext
*,
647 css::uno::Sequence
<css::uno::Any
> const &)
649 return cppu::acquire(new frm::ORichTextControl());
652 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */