Bug Summary

File:home/maarten/src/libreoffice/core/include/rtl/ref.hxx
Warning:line 192, column 9
Use of memory after it is freed

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name edit.cxx -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/11.0.0 -isystem /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/glm -isystem /usr/include/glib-2.0 -isystem /usr/lib64/glib-2.0/include -isystem /usr/include/libmount -isystem /usr/include/blkid -isystem /usr/include/cairo -isystem /usr/include/glib-2.0 -isystem /usr/lib64/glib-2.0/include -isystem /usr/include/pixman-1 -isystem /usr/include/freetype2 -isystem /usr/include/libpng16 -isystem /usr/include/libxml2 -isystem /usr/include/freetype2 -isystem /usr/include/libpng16 -isystem /usr/include/dbus-1.0 -isystem /usr/lib64/dbus-1.0/include -isystem /usr/include/freetype2 -isystem /usr/include/libpng16 -D BOOST_ERROR_CODE_HEADER_ONLY -D BOOST_SYSTEM_NO_DEPRECATED -D CPPU_ENV=gcc3 -D LINUX -D OSL_DEBUG_LEVEL=1 -D SAL_LOG_INFO -D SAL_LOG_WARN -D UNIX -D UNX -D X86_64 -D _PTHREADS -D _REENTRANT -D VCL_DLLIMPLEMENTATION -D DLLIMPLEMENTATION_UITEST -D CUI_DLL_NAME="libcuilo.so" -D DESKTOP_DETECTOR_DLL_NAME="libdesktop_detectorlo.so" -D TK_DLL_NAME="libtklo.so" -D SYSTEM_ZLIB -D GLM_FORCE_CTOR_INIT -D SK_USER_CONFIG_HEADER=</home/maarten/src/libreoffice/core/config_host/config_skia.h> -D SKIA_DLL -D ENABLE_CUPS -D HAVE_VALGRIND_HEADERS -D EXCEPTIONS_ON -D LIBO_INTERNAL_ONLY -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/epoxy/include -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/skia/include/core -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/skia/include/effects -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/skia/include/gpu -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/skia/include/config -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/skia/include/ports -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/skia/include/third_party/vulkan -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/skia/tools/gpu -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/skia -I /home/maarten/src/libreoffice/core/external/skia/inc/ -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/mdds/include -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/lcms2/include -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/icu/source -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/icu/source/i18n -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/icu/source/common -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/harfbuzz/src -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/graphite/include -I /home/maarten/src/libreoffice/core/external/boost/include -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/boost -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/pdfium -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/pdfium/public -D COMPONENT_BUILD -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/libpng -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/libjpeg-turbo -I /home/maarten/src/libreoffice/core/include -I /usr/lib/jvm/java-11-openjdk-11.0.9.10-0.0.ea.fc33.x86_64/include -I /usr/lib/jvm/java-11-openjdk-11.0.9.10-0.0.ea.fc33.x86_64/include/linux -I /home/maarten/src/libreoffice/core/config_host -I /home/maarten/src/libreoffice/core/vcl/inc -I /home/maarten/src/libreoffice/core/workdir/UnoApiHeadersTarget/udkapi/normal -I /home/maarten/src/libreoffice/core/workdir/UnoApiHeadersTarget/offapi/normal -I /home/maarten/src/libreoffice/core/workdir/CustomTarget/officecfg/registry -I /usr/include/freetype2 -I /usr/include/libpng16 -I /usr/include/libxml2 -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10 -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10/x86_64-redhat-linux -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10/backward -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/11.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -O0 -Wno-missing-braces -std=c++17 -fdeprecated-macro -fdebug-compilation-dir /home/maarten/src/libreoffice/core -ferror-limit 19 -fvisibility hidden -fvisibility-inlines-hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcxx-exceptions -fexceptions -debug-info-kind=constructor -analyzer-output=html -faddrsig -o /home/maarten/tmp/wis/scan-build-libreoffice/output/report/2020-10-07-141433-9725-1 -x c++ /home/maarten/src/libreoffice/core/vcl/source/control/edit.cxx

/home/maarten/src/libreoffice/core/vcl/source/control/edit.cxx

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 <vcl/builder.hxx>
21#include <vcl/event.hxx>
22#include <vcl/cursor.hxx>
23#include <vcl/menu.hxx>
24#include <vcl/toolkit/edit.hxx>
25#include <vcl/weld.hxx>
26#include <vcl/specialchars.hxx>
27#include <vcl/svapp.hxx>
28#include <vcl/settings.hxx>
29#include <vcl/uitest/uiobject.hxx>
30#include <vcl/ptrstyle.hxx>
31
32#include <window.h>
33#include <svdata.hxx>
34#include <strings.hrc>
35#include <controldata.hxx>
36
37#include <com/sun/star/i18n/BreakIterator.hpp>
38#include <com/sun/star/i18n/CharacterIteratorMode.hpp>
39#include <com/sun/star/i18n/WordType.hpp>
40#include <com/sun/star/datatransfer/XTransferable.hpp>
41#include <com/sun/star/datatransfer/clipboard/XClipboard.hpp>
42
43#include <com/sun/star/datatransfer/dnd/DNDConstants.hpp>
44#include <com/sun/star/datatransfer/dnd/XDragGestureRecognizer.hpp>
45#include <com/sun/star/datatransfer/dnd/XDropTarget.hpp>
46
47#include <com/sun/star/i18n/InputSequenceChecker.hpp>
48#include <com/sun/star/i18n/InputSequenceCheckMode.hpp>
49#include <com/sun/star/i18n/ScriptType.hpp>
50
51#include <com/sun/star/uno/Any.hxx>
52
53#include <comphelper/processfactory.hxx>
54#include <comphelper/string.hxx>
55
56#include <sot/exchange.hxx>
57#include <sot/formats.hxx>
58#include <sal/macros.h>
59#include <sal/log.hxx>
60
61#include <i18nlangtag/languagetag.hxx>
62#include <vcl/unohelp2.hxx>
63#include <o3tl/safeint.hxx>
64#include <officecfg/Office/Common.hxx>
65
66#include <algorithm>
67#include <memory>
68#include <string_view>
69
70using namespace ::com::sun::star;
71using namespace ::com::sun::star::uno;
72using namespace ::com::sun::star::lang;
73
74// - Redo
75// - if Tracking-Cancel recreate DefaultSelection
76
77static FncGetSpecialChars pImplFncGetSpecialChars = nullptr;
78
79#define EDIT_ALIGN_LEFT1 1
80#define EDIT_ALIGN_CENTER2 2
81#define EDIT_ALIGN_RIGHT3 3
82
83#define EDIT_DEL_LEFT1 1
84#define EDIT_DEL_RIGHT2 2
85
86#define EDIT_DELMODE_SIMPLE11 11
87#define EDIT_DELMODE_RESTOFWORD12 12
88#define EDIT_DELMODE_RESTOFCONTENT13 13
89
90struct DDInfo
91{
92 vcl::Cursor aCursor;
93 Selection aDndStartSel;
94 sal_Int32 nDropPos;
95 bool bStarterOfDD;
96 bool bDroppedInMe;
97 bool bVisCursor;
98 bool bIsStringSupported;
99
100 DDInfo()
101 {
102 aCursor.SetStyle( CURSOR_SHADOW(sal_uInt16(0x0001)) );
103 nDropPos = 0;
104 bStarterOfDD = false;
105 bDroppedInMe = false;
106 bVisCursor = false;
107 bIsStringSupported = false;
108 }
109};
110
111struct Impl_IMEInfos
112{
113 OUString aOldTextAfterStartPos;
114 std::unique_ptr<ExtTextInputAttr[]>
115 pAttribs;
116 sal_Int32 nPos;
117 sal_Int32 nLen;
118 bool bCursor;
119 bool bWasCursorOverwrite;
120
121 Impl_IMEInfos(sal_Int32 nPos, const OUString& rOldTextAfterStartPos);
122
123 void CopyAttribs(const ExtTextInputAttr* pA, sal_Int32 nL);
124 void DestroyAttribs();
125};
126
127Impl_IMEInfos::Impl_IMEInfos(sal_Int32 nP, const OUString& rOldTextAfterStartPos)
128 : aOldTextAfterStartPos(rOldTextAfterStartPos),
129 nPos(nP),
130 nLen(0),
131 bCursor(true),
132 bWasCursorOverwrite(false)
133{
134}
135
136void Impl_IMEInfos::CopyAttribs(const ExtTextInputAttr* pA, sal_Int32 nL)
137{
138 nLen = nL;
139 pAttribs.reset(new ExtTextInputAttr[ nL ]);
140 memcpy( pAttribs.get(), pA, nL*sizeof(ExtTextInputAttr) );
141}
142
143void Impl_IMEInfos::DestroyAttribs()
144{
145 pAttribs.reset();
146 nLen = 0;
147}
148
149Edit::Edit( WindowType nType )
150 : Control( nType )
151{
152 ImplInitEditData();
153}
154
155Edit::Edit( vcl::Window* pParent, WinBits nStyle )
156 : Control( WindowType::EDIT )
157{
158 ImplInitEditData();
159 ImplInit( pParent, nStyle );
160}
161
162void Edit::SetWidthInChars(sal_Int32 nWidthInChars)
163{
164 if (mnWidthInChars != nWidthInChars)
165 {
166 mnWidthInChars = nWidthInChars;
167 queue_resize();
168 }
169}
170
171void Edit::setMaxWidthChars(sal_Int32 nWidth)
172{
173 if (nWidth != mnMaxWidthChars)
174 {
175 mnMaxWidthChars = nWidth;
176 queue_resize();
177 }
178}
179
180bool Edit::set_property(const OString &rKey, const OUString &rValue)
181{
182 if (rKey == "width-chars")
183 SetWidthInChars(rValue.toInt32());
184 else if (rKey == "max-width-chars")
185 setMaxWidthChars(rValue.toInt32());
186 else if (rKey == "max-length")
187 {
188 sal_Int32 nTextLen = rValue.toInt32();
189 SetMaxTextLen(nTextLen == 0 ? EDIT_NOLIMIT((sal_Int32) 0x7FFFFFFF) : nTextLen);
190 }
191 else if (rKey == "editable")
192 {
193 SetReadOnly(!toBool(rValue));
194 }
195 else if (rKey == "overwrite-mode")
196 {
197 SetInsertMode(!toBool(rValue));
198 }
199 else if (rKey == "visibility")
200 {
201 mbPassword = false;
202 if (!toBool(rValue))
203 mbPassword = true;
204 }
205 else if (rKey == "placeholder-text")
206 SetPlaceholderText(rValue);
207 else if (rKey == "shadow-type")
208 {
209 if (GetStyle() & WB_BORDER)
210 SetBorderStyle(rValue == "none" ? WindowBorderStyle::MONO : WindowBorderStyle::NORMAL);
211 }
212 else
213 return Control::set_property(rKey, rValue);
214 return true;
215}
216
217Edit::~Edit()
218{
219 disposeOnce();
220}
221
222void Edit::dispose()
223{
224 mpUIBuilder.reset();
225 mpDDInfo.reset();
226
227 vcl::Cursor* pCursor = GetCursor();
228 if ( pCursor )
229 {
230 SetCursor( nullptr );
231 delete pCursor;
232 }
233
234 mpIMEInfos.reset();
235
236 if ( mxDnDListener.is() )
237 {
238 if ( GetDragGestureRecognizer().is() )
239 {
240 uno::Reference< datatransfer::dnd::XDragGestureListener> xDGL( mxDnDListener, uno::UNO_QUERY );
241 GetDragGestureRecognizer()->removeDragGestureListener( xDGL );
242 }
243 if ( GetDropTarget().is() )
244 {
245 uno::Reference< datatransfer::dnd::XDropTargetListener> xDTL( mxDnDListener, uno::UNO_QUERY );
246 GetDropTarget()->removeDropTargetListener( xDTL );
247 }
248
249 mxDnDListener->disposing( lang::EventObject() ); // #95154# #96585# Empty Source means it's the Client
250 mxDnDListener.clear();
251 }
252
253 SetType(WindowType::WINDOW);
254
255 mpSubEdit.disposeAndClear();
256 Control::dispose();
257}
258
259void Edit::ImplInitEditData()
260{
261 mpSubEdit = VclPtr<Edit>();
262 mpFilterText = nullptr;
263 mnXOffset = 0;
264 mnAlign = EDIT_ALIGN_LEFT1;
265 mnMaxTextLen = EDIT_NOLIMIT((sal_Int32) 0x7FFFFFFF);
266 mnWidthInChars = -1;
267 mnMaxWidthChars = -1;
268 mbInternModified = false;
269 mbReadOnly = false;
270 mbInsertMode = true;
271 mbClickedInSelection = false;
272 mbActivePopup = false;
273 mbIsSubEdit = false;
274 mbForceControlBackground = false;
275 mbPassword = false;
276 mpDDInfo = nullptr;
277 mpIMEInfos = nullptr;
278 mcEchoChar = 0;
279
280 // no default mirroring for Edit controls
281 // note: controls that use a subedit will revert this (SpinField, ComboBox)
282 EnableRTL( false );
283
284 vcl::unohelper::DragAndDropWrapper* pDnDWrapper = new vcl::unohelper::DragAndDropWrapper( this );
285 mxDnDListener = pDnDWrapper;
286}
287
288bool Edit::ImplUseNativeBorder(vcl::RenderContext const & rRenderContext, WinBits nStyle)
289{
290 bool bRet = rRenderContext.IsNativeControlSupported(ImplGetNativeControlType(),
291 ControlPart::HasBackgroundTexture)
292 && ((nStyle & WB_BORDER) && !(nStyle & WB_NOBORDER));
293 if (!bRet && mbIsSubEdit)
294 {
295 vcl::Window* pWindow = GetParent();
296 nStyle = pWindow->GetStyle();
297 bRet = pWindow->IsNativeControlSupported(ImplGetNativeControlType(),
298 ControlPart::HasBackgroundTexture)
299 && ((nStyle & WB_BORDER) && !(nStyle & WB_NOBORDER));
300 }
301 return bRet;
302}
303
304void Edit::ImplInit(vcl::Window* pParent, WinBits nStyle)
305{
306 nStyle = ImplInitStyle(nStyle);
307
308 if (!(nStyle & (WB_CENTER | WB_RIGHT)))
309 nStyle |= WB_LEFT;
310
311 Control::ImplInit(pParent, nStyle, nullptr);
312
313 mbReadOnly = (nStyle & WB_READONLY) != 0;
314
315 mnAlign = EDIT_ALIGN_LEFT1;
316
317 // hack: right align until keyinput and cursor travelling works
318 if( IsRTLEnabled() )
319 mnAlign = EDIT_ALIGN_RIGHT3;
320
321 if ( nStyle & WB_RIGHT )
322 mnAlign = EDIT_ALIGN_RIGHT3;
323 else if ( nStyle & WB_CENTER )
324 mnAlign = EDIT_ALIGN_CENTER2;
325
326 SetCursor( new vcl::Cursor );
327
328 SetPointer( PointerStyle::Text );
329 ApplySettings(*this);
330
331 uno::Reference< datatransfer::dnd::XDragGestureListener> xDGL( mxDnDListener, uno::UNO_QUERY );
332 uno::Reference< datatransfer::dnd::XDragGestureRecognizer > xDGR = GetDragGestureRecognizer();
333 if ( xDGR.is() )
334 {
335 xDGR->addDragGestureListener( xDGL );
336 uno::Reference< datatransfer::dnd::XDropTargetListener> xDTL( mxDnDListener, uno::UNO_QUERY );
337 GetDropTarget()->addDropTargetListener( xDTL );
338 GetDropTarget()->setActive( true );
339 GetDropTarget()->setDefaultActions( datatransfer::dnd::DNDConstants::ACTION_COPY_OR_MOVE );
340 }
341}
342
343WinBits Edit::ImplInitStyle( WinBits nStyle )
344{
345 if ( !(nStyle & WB_NOTABSTOP) )
346 nStyle |= WB_TABSTOP;
347 if ( !(nStyle & WB_NOGROUP) )
348 nStyle |= WB_GROUP;
349
350 return nStyle;
351}
352
353bool Edit::IsCharInput( const KeyEvent& rKeyEvent )
354{
355 // In the future we must use new Unicode functions for this
356 sal_Unicode cCharCode = rKeyEvent.GetCharCode();
357 return ((cCharCode >= 32) && (cCharCode != 127) &&
358 !rKeyEvent.GetKeyCode().IsMod3() &&
359 !rKeyEvent.GetKeyCode().IsMod2() &&
360 !rKeyEvent.GetKeyCode().IsMod1() );
361}
362
363void Edit::ApplySettings(vcl::RenderContext& rRenderContext)
364{
365 Control::ApplySettings(rRenderContext);
366
367 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
368
369 const vcl::Font& aFont = rStyleSettings.GetFieldFont();
370 ApplyControlFont(rRenderContext, aFont);
371
372 ImplClearLayoutData();
373
374 Color aTextColor = rStyleSettings.GetFieldTextColor();
375 ApplyControlForeground(rRenderContext, aTextColor);
376
377 if (IsControlBackground())
378 {
379 rRenderContext.SetBackground(GetControlBackground());
380 rRenderContext.SetFillColor(GetControlBackground());
381
382 if (ImplUseNativeBorder(rRenderContext, GetStyle()))
383 {
384 // indicates that no non-native drawing of background should take place
385 mpWindowImpl->mnNativeBackground = ControlPart::Entire;
386 }
387 }
388 else if (ImplUseNativeBorder(rRenderContext, GetStyle()))
389 {
390 // Transparent background
391 rRenderContext.SetBackground();
392 rRenderContext.SetFillColor();
393 }
394 else
395 {
396 rRenderContext.SetBackground(rStyleSettings.GetFieldColor());
397 rRenderContext.SetFillColor(rStyleSettings.GetFieldColor());
398 }
399}
400
401long Edit::ImplGetExtraXOffset() const
402{
403 // MT 09/2002: nExtraOffsetX should become a member, instead of checking every time,
404 // but I need an incompatible update for this...
405 // #94095# Use extra offset only when edit has a border
406 long nExtraOffset = 0;
407 if( ( GetStyle() & WB_BORDER ) || ( mbIsSubEdit && ( GetParent()->GetStyle() & WB_BORDER ) ) )
408 nExtraOffset = 2;
409
410 return nExtraOffset;
411}
412
413long Edit::ImplGetExtraYOffset() const
414{
415 long nExtraOffset = 0;
416 ControlType eCtrlType = ImplGetNativeControlType();
417 if (eCtrlType != ControlType::EditboxNoBorder)
418 {
419 // add some space between text entry and border
420 nExtraOffset = 2;
421 }
422 return nExtraOffset;
423}
424
425OUString Edit::ImplGetText() const
426{
427 if ( mcEchoChar || mbPassword )
428 {
429 sal_Unicode cEchoChar;
430 if ( mcEchoChar )
431 cEchoChar = mcEchoChar;
432 else
433 cEchoChar = u'\x2022';
434 OUStringBuffer aText;
435 comphelper::string::padToLength(aText, maText.getLength(), cEchoChar);
436 return aText.makeStringAndClear();
437 }
438 else
439 return maText.toString();
440}
441
442void Edit::ImplInvalidateOrRepaint()
443{
444 if( IsPaintTransparent() )
445 {
446 Invalidate();
447 // FIXME: this is currently only on macOS
448 if( ImplGetSVData()->maNWFData.mbNoFocusRects )
449 PaintImmediately();
450 }
451 else
452 Invalidate();
453}
454
455long Edit::ImplGetTextYPosition() const
456{
457 if ( GetStyle() & WB_TOP )
458 return ImplGetExtraXOffset();
459 else if ( GetStyle() & WB_BOTTOM )
460 return GetOutputSizePixel().Height() - GetTextHeight() - ImplGetExtraXOffset();
461 return ( GetOutputSizePixel().Height() - GetTextHeight() ) / 2;
462}
463
464void Edit::ImplRepaint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRectangle)
465{
466 if (!IsReallyVisible())
467 return;
468
469 ApplySettings(rRenderContext);
470
471 const OUString aText = ImplGetText();
472 const sal_Int32 nLen = aText.getLength();
473
474 long nDXBuffer[256];
475 std::unique_ptr<long[]> pDXBuffer;
476 long* pDX = nDXBuffer;
477
478 if (nLen)
479 {
480 if (o3tl::make_unsigned(2 * nLen) > SAL_N_ELEMENTS(nDXBuffer)(sizeof(sal_n_array_size(nDXBuffer))))
481 {
482 pDXBuffer.reset(new long[2 * (nLen + 1)]);
483 pDX = pDXBuffer.get();
484 }
485
486 GetCaretPositions(aText, pDX, 0, nLen);
487 }
488
489 long nTH = GetTextHeight();
490 Point aPos(mnXOffset, ImplGetTextYPosition());
491
492 vcl::Cursor* pCursor = GetCursor();
493 bool bVisCursor = pCursor && pCursor->IsVisible();
494 if (pCursor)
495 pCursor->Hide();
496
497 ImplClearBackground(rRenderContext, rRectangle, 0, GetOutputSizePixel().Width()-1);
498
499 bool bPaintPlaceholderText = aText.isEmpty() && !maPlaceholderText.isEmpty();
500
501 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
502
503 if (!IsEnabled() || bPaintPlaceholderText)
504 rRenderContext.SetTextColor(rStyleSettings.GetDisableColor());
505
506 // Set background color of the normal text
507 if (mbForceControlBackground && IsControlBackground())
508 {
509 // check if we need to set ControlBackground even in NWF case
510 rRenderContext.Push(PushFlags::FILLCOLOR | PushFlags::LINECOLOR);
511 rRenderContext.SetLineColor();
512 rRenderContext.SetFillColor(GetControlBackground());
513 rRenderContext.DrawRect(tools::Rectangle(aPos, Size(GetOutputSizePixel().Width() - 2 * mnXOffset, GetOutputSizePixel().Height())));
514 rRenderContext.Pop();
515
516 rRenderContext.SetTextFillColor(GetControlBackground());
517 }
518 else if (IsPaintTransparent() || ImplUseNativeBorder(rRenderContext, GetStyle()))
519 rRenderContext.SetTextFillColor();
520 else
521 rRenderContext.SetTextFillColor(IsControlBackground() ? GetControlBackground() : rStyleSettings.GetFieldColor());
522
523 ImplPaintBorder(rRenderContext);
524
525 bool bDrawSelection = maSelection.Len() && (HasFocus() || (GetStyle() & WB_NOHIDESELECTION) || mbActivePopup);
526
527 aPos.setX( mnXOffset + ImplGetExtraXOffset() );
528 if (bPaintPlaceholderText)
529 {
530 rRenderContext.DrawText(aPos, maPlaceholderText);
531 }
532 else if (!bDrawSelection && !mpIMEInfos)
533 {
534 rRenderContext.DrawText(aPos, aText, 0, nLen);
535 }
536 else
537 {
538 // save graphics state
539 rRenderContext.Push();
540 // first calculate highlighted and non highlighted clip regions
541 vcl::Region aHighlightClipRegion;
542 vcl::Region aNormalClipRegion;
543 Selection aTmpSel(maSelection);
544 aTmpSel.Justify();
545 // selection is highlighted
546 for(sal_Int32 i = 0; i < nLen; ++i)
547 {
548 tools::Rectangle aRect(aPos, Size(10, nTH));
549 aRect.SetLeft( pDX[2 * i] + mnXOffset + ImplGetExtraXOffset() );
550 aRect.SetRight( pDX[2 * i + 1] + mnXOffset + ImplGetExtraXOffset() );
551 aRect.Justify();
552 bool bHighlight = false;
553 if (i >= aTmpSel.Min() && i < aTmpSel.Max())
554 bHighlight = true;
555
556 if (mpIMEInfos && mpIMEInfos->pAttribs &&
557 i >= mpIMEInfos->nPos && i < (mpIMEInfos->nPos+mpIMEInfos->nLen) &&
558 (mpIMEInfos->pAttribs[i - mpIMEInfos->nPos] & ExtTextInputAttr::Highlight))
559 {
560 bHighlight = true;
561 }
562
563 if (bHighlight)
564 aHighlightClipRegion.Union(aRect);
565 else
566 aNormalClipRegion.Union(aRect);
567 }
568 // draw normal text
569 Color aNormalTextColor = rRenderContext.GetTextColor();
570 rRenderContext.SetClipRegion(aNormalClipRegion);
571
572 if (IsPaintTransparent())
573 rRenderContext.SetTextFillColor();
574 else
575 {
576 // Set background color when part of the text is selected
577 if (ImplUseNativeBorder(rRenderContext, GetStyle()))
578 {
579 if( mbForceControlBackground && IsControlBackground() )
580 rRenderContext.SetTextFillColor(GetControlBackground());
581 else
582 rRenderContext.SetTextFillColor();
583 }
584 else
585 {
586 rRenderContext.SetTextFillColor(IsControlBackground() ? GetControlBackground() : rStyleSettings.GetFieldColor());
587 }
588 }
589 rRenderContext.DrawText(aPos, aText, 0, nLen);
590
591 // draw highlighted text
592 rRenderContext.SetClipRegion(aHighlightClipRegion);
593 rRenderContext.SetTextColor(rStyleSettings.GetHighlightTextColor());
594 rRenderContext.SetTextFillColor(rStyleSettings.GetHighlightColor());
595 rRenderContext.DrawText(aPos, aText, 0, nLen);
596
597 // if IME info exists loop over portions and output different font attributes
598 if (mpIMEInfos && mpIMEInfos->pAttribs)
599 {
600 for(int n = 0; n < 2; n++)
601 {
602 vcl::Region aRegion;
603 if (n == 0)
604 {
605 rRenderContext.SetTextColor(aNormalTextColor);
606 if (IsPaintTransparent())
607 rRenderContext.SetTextFillColor();
608 else
609 rRenderContext.SetTextFillColor(IsControlBackground() ? GetControlBackground() : rStyleSettings.GetFieldColor());
610 aRegion = aNormalClipRegion;
611 }
612 else
613 {
614 rRenderContext.SetTextColor(rStyleSettings.GetHighlightTextColor());
615 rRenderContext.SetTextFillColor(rStyleSettings.GetHighlightColor());
616 aRegion = aHighlightClipRegion;
617 }
618
619 for(int i = 0; i < mpIMEInfos->nLen; )
620 {
621 ExtTextInputAttr nAttr = mpIMEInfos->pAttribs[i];
622 vcl::Region aClip;
623 int nIndex = i;
624 while (nIndex < mpIMEInfos->nLen && mpIMEInfos->pAttribs[nIndex] == nAttr) // #112631# check nIndex before using it
625 {
626 tools::Rectangle aRect( aPos, Size( 10, nTH ) );
627 aRect.SetLeft( pDX[2 * (nIndex + mpIMEInfos->nPos)] + mnXOffset + ImplGetExtraXOffset() );
628 aRect.SetRight( pDX[2 * (nIndex + mpIMEInfos->nPos) + 1] + mnXOffset + ImplGetExtraXOffset() );
629 aRect.Justify();
630 aClip.Union(aRect);
631 nIndex++;
632 }
633 i = nIndex;
634 aClip.Intersect(aRegion);
635 if (!aClip.IsEmpty() && nAttr != ExtTextInputAttr::NONE)
636 {
637 vcl::Font aFont = rRenderContext.GetFont();
638 if (nAttr & ExtTextInputAttr::Underline)
639 aFont.SetUnderline(LINESTYLE_SINGLE);
640 else if (nAttr & ExtTextInputAttr::BoldUnderline)
641 aFont.SetUnderline( LINESTYLE_BOLD);
642 else if (nAttr & ExtTextInputAttr::DottedUnderline)
643 aFont.SetUnderline( LINESTYLE_DOTTED);
644 else if (nAttr & ExtTextInputAttr::DashDotUnderline)
645 aFont.SetUnderline( LINESTYLE_DASHDOT);
646 else if (nAttr & ExtTextInputAttr::GrayWaveline)
647 {
648 aFont.SetUnderline(LINESTYLE_WAVE);
649 rRenderContext.SetTextLineColor(COL_LIGHTGRAY);
650 }
651 rRenderContext.SetFont(aFont);
652
653 if (nAttr & ExtTextInputAttr::RedText)
654 rRenderContext.SetTextColor(COL_RED);
655 else if (nAttr & ExtTextInputAttr::HalfToneText)
656 rRenderContext.SetTextColor(COL_LIGHTGRAY);
657
658 rRenderContext.SetClipRegion(aClip);
659 rRenderContext.DrawText(aPos, aText, 0, nLen);
660 }
661 }
662 }
663 }
664
665 // restore graphics state
666 rRenderContext.Pop();
667 }
668
669 if (bVisCursor && (!mpIMEInfos || mpIMEInfos->bCursor))
670 pCursor->Show();
671}
672
673void Edit::ImplDelete( const Selection& rSelection, sal_uInt8 nDirection, sal_uInt8 nMode )
674{
675 const sal_Int32 nTextLen = ImplGetText().getLength();
676
677 // deleting possible?
678 if ( !rSelection.Len() &&
679 (((rSelection.Min() == 0) && (nDirection == EDIT_DEL_LEFT1)) ||
680 ((rSelection.Max() == nTextLen) && (nDirection == EDIT_DEL_RIGHT2))) )
681 return;
682
683 ImplClearLayoutData();
684
685 Selection aSelection( rSelection );
686 aSelection.Justify();
687
688 if ( !aSelection.Len() )
689 {
690 uno::Reference < i18n::XBreakIterator > xBI = ImplGetBreakIterator();
691 if ( nDirection == EDIT_DEL_LEFT1 )
692 {
693 if ( nMode == EDIT_DELMODE_RESTOFWORD12 )
694 {
695 i18n::Boundary aBoundary = xBI->getWordBoundary( maText.toString(), aSelection.Min(),
696 GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES, true );
697 auto startPos = aBoundary.startPos;
698 if ( startPos == aSelection.Min() )
699 {
700 aBoundary = xBI->previousWord( maText.toString(), aSelection.Min(),
701 GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES );
702 startPos = std::max(aBoundary.startPos, sal_Int32(0));
703 }
704 aSelection.Min() = startPos;
705 }
706 else if ( nMode == EDIT_DELMODE_RESTOFCONTENT13 )
707 {
708 aSelection.Min() = 0;
709 }
710 else
711 {
712 sal_Int32 nCount = 1;
713 aSelection.Min() = xBI->previousCharacters( maText.toString(), aSelection.Min(),
714 GetSettings().GetLanguageTag().getLocale(), i18n::CharacterIteratorMode::SKIPCHARACTER, nCount, nCount );
715 }
716 }
717 else
718 {
719 if ( nMode == EDIT_DELMODE_RESTOFWORD12 )
720 {
721 i18n::Boundary aBoundary = xBI->nextWord( maText.toString(), aSelection.Max(),
722 GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES );
723 aSelection.Max() = aBoundary.startPos;
724 }
725 else if ( nMode == EDIT_DELMODE_RESTOFCONTENT13 )
726 {
727 aSelection.Max() = nTextLen;
728 }
729 else
730 {
731 sal_Int32 nCount = 1;
732 aSelection.Max() = xBI->nextCharacters( maText.toString(), aSelection.Max(),
733 GetSettings().GetLanguageTag().getLocale(), i18n::CharacterIteratorMode::SKIPCHARACTER, nCount, nCount );
734 }
735 }
736 }
737
738 const auto nSelectionMin = aSelection.Min();
739 maText.remove( nSelectionMin, aSelection.Len() );
740 maSelection.Min() = nSelectionMin;
741 maSelection.Max() = nSelectionMin;
742 ImplAlignAndPaint();
743 mbInternModified = true;
744}
745
746OUString Edit::ImplGetValidString( const OUString& rString )
747{
748 OUString aValidString = rString.replaceAll("\n", "").replaceAll("\r", "");
749 aValidString = aValidString.replace('\t', ' ');
750 return aValidString;
751}
752
753uno::Reference <i18n::XBreakIterator> const& Edit::ImplGetBreakIterator()
754{
755 if (!mxBreakIterator)
756 mxBreakIterator = i18n::BreakIterator::create(::comphelper::getProcessComponentContext());
757 return mxBreakIterator;
758}
759
760uno::Reference <i18n::XExtendedInputSequenceChecker> const& Edit::ImplGetInputSequenceChecker()
761{
762 if (!mxISC.is())
763 mxISC = i18n::InputSequenceChecker::create(::comphelper::getProcessComponentContext());
764 return mxISC;
765}
766
767void Edit::ShowTruncationWarning(weld::Widget* pParent)
768{
769 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pParent, VclMessageType::Warning,
770 VclButtonsType::Ok, VclResId(SV_EDIT_WARNING_STRreinterpret_cast<char const *>("SV_EDIT_WARNING_STR" "\004"
u8"The inserted text exceeded the maximum length of this text field. The text was truncated."
)
)));
771 xBox->run();
772}
773
774bool Edit::ImplTruncateToMaxLen( OUString& rStr, sal_Int32 nSelectionLen ) const
775{
776 bool bWasTruncated = false;
777 if (maText.getLength() - nSelectionLen > mnMaxTextLen - rStr.getLength())
778 {
779 sal_Int32 nErasePos = mnMaxTextLen - maText.getLength() + nSelectionLen;
780 rStr = rStr.copy( 0, nErasePos );
781 bWasTruncated = true;
782 }
783 return bWasTruncated;
784}
785
786void Edit::ImplInsertText( const OUString& rStr, const Selection* pNewSel, bool bIsUserInput )
787{
788 Selection aSelection( maSelection );
789 aSelection.Justify();
790
791 OUString aNewText( ImplGetValidString( rStr ) );
792
793 // as below, if there's no selection, but we're in overwrite mode and not beyond
794 // the end of the existing text then that's like a selection of 1
795 auto nSelectionLen = aSelection.Len();
796 if (!nSelectionLen && !mbInsertMode && aSelection.Max() < maText.getLength())
797 nSelectionLen = 1;
798 ImplTruncateToMaxLen( aNewText, nSelectionLen );
799
800 ImplClearLayoutData();
801
802 if ( aSelection.Len() )
803 maText.remove( aSelection.Min(), aSelection.Len() );
804 else if (!mbInsertMode && aSelection.Max() < maText.getLength())
805 maText.remove( aSelection.Max(), 1 );
806
807 // take care of input-sequence-checking now
808 if (bIsUserInput && !rStr.isEmpty())
809 {
810 SAL_WARN_IF( rStr.getLength() != 1, "vcl", "unexpected string length. User input is expected to provide 1 char only!" )do { if (true && (rStr.getLength() != 1)) { switch (sal_detail_log_report
(::SAL_DETAIL_LOG_LEVEL_WARN, "vcl")) { case SAL_DETAIL_LOG_ACTION_IGNORE
: break; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail
::getResult( ::sal::detail::StreamStart() << "unexpected string length. User input is expected to provide 1 char only!"
) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("vcl"
), ("/home/maarten/src/libreoffice/core/vcl/source/control/edit.cxx"
":" "810" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "unexpected string length. User input is expected to provide 1 char only!"
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "unexpected string length. User input is expected to provide 1 char only!"
; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("vcl"),
("/home/maarten/src/libreoffice/core/vcl/source/control/edit.cxx"
":" "810" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "unexpected string length. User input is expected to provide 1 char only!"
) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("vcl"
), ("/home/maarten/src/libreoffice/core/vcl/source/control/edit.cxx"
":" "810" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "unexpected string length. User input is expected to provide 1 char only!"
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "unexpected string length. User input is expected to provide 1 char only!"
; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("vcl"),
("/home/maarten/src/libreoffice/core/vcl/source/control/edit.cxx"
":" "810" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
811
812 // determine if input-sequence-checking should be applied or not
813
814 uno::Reference < i18n::XBreakIterator > xBI = ImplGetBreakIterator();
815 bool bIsInputSequenceChecking = rStr.getLength() == 1 &&
816 officecfg::Office::Common::I18N::CTL::CTLFont::get() &&
817 officecfg::Office::Common::I18N::CTL::CTLSequenceChecking::get() &&
818 aSelection.Min() > 0 && /* first char needs not to be checked */
819 xBI.is() && i18n::ScriptType::COMPLEX == xBI->getScriptType( rStr, 0 );
820
821 if (bIsInputSequenceChecking)
822 {
823 uno::Reference < i18n::XExtendedInputSequenceChecker > xISC = ImplGetInputSequenceChecker();
824 if (xISC.is())
825 {
826 sal_Unicode cChar = rStr[0];
827 sal_Int32 nTmpPos = aSelection.Min();
828 sal_Int16 nCheckMode = officecfg::Office::Common::I18N::CTL::CTLSequenceCheckingRestricted::get()?
829 i18n::InputSequenceCheckMode::STRICT : i18n::InputSequenceCheckMode::BASIC;
830
831 // the text that needs to be checked is only the one
832 // before the current cursor position
833 const OUString aOldText( maText.getStr(), nTmpPos);
834 OUString aTmpText( aOldText );
835 if (officecfg::Office::Common::I18N::CTL::CTLSequenceCheckingTypeAndReplace::get())
836 {
837 xISC->correctInputSequence( aTmpText, nTmpPos - 1, cChar, nCheckMode );
838
839 // find position of first character that has changed
840 sal_Int32 nOldLen = aOldText.getLength();
841 sal_Int32 nTmpLen = aTmpText.getLength();
842 const sal_Unicode *pOldTxt = aOldText.getStr();
843 const sal_Unicode *pTmpTxt = aTmpText.getStr();
844 sal_Int32 nChgPos = 0;
845 while ( nChgPos < nOldLen && nChgPos < nTmpLen &&
846 pOldTxt[nChgPos] == pTmpTxt[nChgPos] )
847 ++nChgPos;
848
849 const OUString aChgText( aTmpText.copy( nChgPos ) );
850
851 // remove text from first pos to be changed to current pos
852 maText.remove( nChgPos, nTmpPos - nChgPos );
853
854 if (!aChgText.isEmpty())
855 {
856 aNewText = aChgText;
857 aSelection.Min() = nChgPos; // position for new text to be inserted
858 }
859 else
860 aNewText.clear();
861 }
862 else
863 {
864 // should the character be ignored (i.e. not get inserted) ?
865 if (!xISC->checkInputSequence( aOldText, nTmpPos - 1, cChar, nCheckMode ))
866 aNewText.clear();
867 }
868 }
869 }
870
871 // at this point now we will insert the non-empty text 'normally' some lines below...
872 }
873
874 if ( !aNewText.isEmpty() )
875 maText.insert( aSelection.Min(), aNewText );
876
877 if ( !pNewSel )
878 {
879 maSelection.Min() = aSelection.Min() + aNewText.getLength();
880 maSelection.Max() = maSelection.Min();
881 }
882 else
883 {
884 maSelection = *pNewSel;
885 if ( maSelection.Min() > maText.getLength() )
886 maSelection.Min() = maText.getLength();
887 if ( maSelection.Max() > maText.getLength() )
888 maSelection.Max() = maText.getLength();
889 }
890
891 ImplAlignAndPaint();
892 mbInternModified = true;
893}
894
895void Edit::ImplSetText( const OUString& rText, const Selection* pNewSelection )
896{
897 // we delete text by "selecting" the old text completely then calling InsertText; this is flicker free
898 if ( ( rText.getLength() > mnMaxTextLen ) ||
899 ( std::u16string_view(rText) == std::u16string_view(maText.getStr(), maText.getLength())
900 && (!pNewSelection || (*pNewSelection == maSelection)) ) )
901 return;
902
903 ImplClearLayoutData();
904 maSelection.Min() = 0;
905 maSelection.Max() = maText.getLength();
906 if ( mnXOffset || HasPaintEvent() )
907 {
908 mnXOffset = 0;
909 maText = ImplGetValidString( rText );
910
911 // #i54929# recalculate mnXOffset before ImplSetSelection,
912 // else cursor ends up in wrong position
913 ImplAlign();
914
915 if ( pNewSelection )
916 ImplSetSelection( *pNewSelection, false );
917
918 if ( mnXOffset && !pNewSelection )
919 maSelection.Max() = 0;
920
921 Invalidate();
922 }
923 else
924 ImplInsertText( rText, pNewSelection );
925
926 CallEventListeners( VclEventId::EditModify );
927}
928
929ControlType Edit::ImplGetNativeControlType() const
930{
931 ControlType nCtrl = ControlType::Generic;
932 const vcl::Window* pControl = mbIsSubEdit ? GetParent() : this;
933
934 switch (pControl->GetType())
935 {
936 case WindowType::COMBOBOX:
937 case WindowType::PATTERNBOX:
938 case WindowType::NUMERICBOX:
939 case WindowType::METRICBOX:
940 case WindowType::CURRENCYBOX:
941 case WindowType::DATEBOX:
942 case WindowType::TIMEBOX:
943 case WindowType::LONGCURRENCYBOX:
944 nCtrl = ControlType::Combobox;
945 break;
946
947 case WindowType::MULTILINEEDIT:
948 if ( GetWindow( GetWindowType::Border ) != this )
949 nCtrl = ControlType::MultilineEditbox;
950 else
951 nCtrl = ControlType::EditboxNoBorder;
952 break;
953
954 case WindowType::EDIT:
955 case WindowType::PATTERNFIELD:
956 case WindowType::METRICFIELD:
957 case WindowType::CURRENCYFIELD:
958 case WindowType::DATEFIELD:
959 case WindowType::TIMEFIELD:
960 case WindowType::SPINFIELD:
961 case WindowType::FORMATTEDFIELD:
962 if (pControl->GetStyle() & WB_SPIN)
963 nCtrl = ControlType::Spinbox;
964 else
965 {
966 if (GetWindow(GetWindowType::Border) != this)
967 nCtrl = ControlType::Editbox;
968 else
969 nCtrl = ControlType::EditboxNoBorder;
970 }
971 break;
972
973 default:
974 nCtrl = ControlType::Editbox;
975 }
976 return nCtrl;
977}
978
979void Edit::ImplClearBackground(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRectangle, long nXStart, long nXEnd )
980{
981 /*
982 * note: at this point the cursor must be switched off already
983 */
984 tools::Rectangle aRect(Point(), GetOutputSizePixel());
985 aRect.SetLeft( nXStart );
986 aRect.SetRight( nXEnd );
987
988 if( !(ImplUseNativeBorder(rRenderContext, GetStyle()) || IsPaintTransparent()))
989 rRenderContext.Erase(aRect);
990 else if (SupportsDoubleBuffering() && mbIsSubEdit)
991 {
992 // ImplPaintBorder() is a NOP, we have a native border, and this is a sub-edit of a control.
993 // That means we have to draw the parent native widget to paint the edit area to clear our background.
994 vcl::PaintBufferGuard g(ImplGetWindowImpl()->mpFrameData, GetParent());
995 GetParent()->Paint(rRenderContext, rRectangle);
996 }
997}
998
999void Edit::ImplPaintBorder(vcl::RenderContext const & rRenderContext)
1000{
1001 // this is not needed when double-buffering
1002 if (SupportsDoubleBuffering())
1003 return;
1004
1005 if (!(ImplUseNativeBorder(rRenderContext, GetStyle()) || IsPaintTransparent()))
1006 return;
1007
1008 // draw the inner part by painting the whole control using its border window
1009 vcl::Window* pBorder = GetWindow(GetWindowType::Border);
1010 if (pBorder == this)
1011 {
1012 // we have no border, use parent
1013 vcl::Window* pControl = mbIsSubEdit ? GetParent() : this;
1014 pBorder = pControl->GetWindow(GetWindowType::Border);
1015 if (pBorder == this)
1016 pBorder = GetParent();
1017 }
1018
1019 if (!pBorder)
1020 return;
1021
1022 // set proper clipping region to not overdraw the whole control
1023 vcl::Region aClipRgn = GetPaintRegion();
1024 if (!aClipRgn.IsNull())
1025 {
1026 // transform clipping region to border window's coordinate system
1027 if (IsRTLEnabled() != pBorder->IsRTLEnabled() && AllSettings::GetLayoutRTL())
1028 {
1029 // need to mirror in case border is not RTL but edit is (or vice versa)
1030
1031 // mirror
1032 tools::Rectangle aBounds(aClipRgn.GetBoundRect());
1033 int xNew = GetOutputSizePixel().Width() - aBounds.GetWidth() - aBounds.Left();
1034 aClipRgn.Move(xNew - aBounds.Left(), 0);
1035
1036 // move offset of border window
1037 Point aBorderOffs = pBorder->ScreenToOutputPixel(OutputToScreenPixel(Point()));
1038 aClipRgn.Move(aBorderOffs.X(), aBorderOffs.Y());
1039 }
1040 else
1041 {
1042 // normal case
1043 Point aBorderOffs = pBorder->ScreenToOutputPixel(OutputToScreenPixel(Point()));
1044 aClipRgn.Move(aBorderOffs.X(), aBorderOffs.Y());
1045 }
1046
1047 vcl::Region oldRgn(pBorder->GetClipRegion());
1048 pBorder->SetClipRegion(aClipRgn);
1049
1050 pBorder->Paint(*pBorder, tools::Rectangle());
1051
1052 pBorder->SetClipRegion(oldRgn);
1053 }
1054 else
1055 {
1056 pBorder->Paint(*pBorder, tools::Rectangle());
1057 }
1058}
1059
1060void Edit::ImplShowCursor( bool bOnlyIfVisible )
1061{
1062 if ( !IsUpdateMode() || ( bOnlyIfVisible && !IsReallyVisible() ) )
1063 return;
1064
1065 vcl::Cursor* pCursor = GetCursor();
1066 OUString aText = ImplGetText();
1067
1068 long nTextPos = 0;
1069
1070 long nDXBuffer[256];
1071 std::unique_ptr<long[]> pDXBuffer;
1072 long* pDX = nDXBuffer;
1073
1074 if( !aText.isEmpty() )
1075 {
1076 if( o3tl::make_unsigned(2*aText.getLength()) > SAL_N_ELEMENTS(nDXBuffer)(sizeof(sal_n_array_size(nDXBuffer))) )
1077 {
1078 pDXBuffer.reset(new long[2*(aText.getLength()+1)]);
1079 pDX = pDXBuffer.get();
1080 }
1081
1082 GetCaretPositions( aText, pDX, 0, aText.getLength() );
1083
1084 if( maSelection.Max() < aText.getLength() )
1085 nTextPos = pDX[ 2*maSelection.Max() ];
1086 else
1087 nTextPos = pDX[ 2*aText.getLength()-1 ];
1088 }
1089
1090 long nCursorWidth = 0;
1091 if ( !mbInsertMode && !maSelection.Len() && (maSelection.Max() < aText.getLength()) )
1092 nCursorWidth = GetTextWidth(aText, maSelection.Max(), 1);
1093 long nCursorPosX = nTextPos + mnXOffset + ImplGetExtraXOffset();
1094
1095 // cursor should land in visible area
1096 const Size aOutSize = GetOutputSizePixel();
1097 if ( (nCursorPosX < 0) || (nCursorPosX >= aOutSize.Width()) )
1098 {
1099 long nOldXOffset = mnXOffset;
1100
1101 if ( nCursorPosX < 0 )
1102 {
1103 mnXOffset = - nTextPos;
1104 long nMaxX = 0;
1105 mnXOffset += aOutSize.Width() / 5;
1106 if ( mnXOffset > nMaxX )
1107 mnXOffset = nMaxX;
1108 }
1109 else
1110 {
1111 mnXOffset = (aOutSize.Width()-ImplGetExtraXOffset()) - nTextPos;
1112 // Something more?
1113 if ( (aOutSize.Width()-ImplGetExtraXOffset()) < nTextPos )
1114 {
1115 long nMaxNegX = (aOutSize.Width()-ImplGetExtraXOffset()) - GetTextWidth( aText );
1116 mnXOffset -= aOutSize.Width() / 5;
1117 if ( mnXOffset < nMaxNegX ) // both negative...
1118 mnXOffset = nMaxNegX;
1119 }
1120 }
1121
1122 nCursorPosX = nTextPos + mnXOffset + ImplGetExtraXOffset();
1123 if ( nCursorPosX == aOutSize.Width() ) // then invisible...
1124 nCursorPosX--;
1125
1126 if ( mnXOffset != nOldXOffset )
1127 ImplInvalidateOrRepaint();
1128 }
1129
1130 const long nTextHeight = GetTextHeight();
1131 const long nCursorPosY = ImplGetTextYPosition();
1132 if (pCursor)
1133 {
1134 pCursor->SetPos( Point( nCursorPosX, nCursorPosY ) );
1135 pCursor->SetSize( Size( nCursorWidth, nTextHeight ) );
1136 pCursor->Show();
1137 }
1138}
1139
1140void Edit::ImplAlign()
1141{
1142 if (mnAlign == EDIT_ALIGN_LEFT1 && !mnXOffset)
1143 {
1144 // short circuit common case and avoid slow GetTextWidth() calc
1145 return;
1146 }
1147
1148 long nTextWidth = GetTextWidth( ImplGetText() );
1149 long nOutWidth = GetOutputSizePixel().Width();
1150
1151 if ( mnAlign == EDIT_ALIGN_LEFT1 )
1152 {
1153 if (nTextWidth < nOutWidth)
1154 mnXOffset = 0;
1155 }
1156 else if ( mnAlign == EDIT_ALIGN_RIGHT3 )
1157 {
1158 long nMinXOffset = nOutWidth - nTextWidth - 1 - ImplGetExtraXOffset();
1159 bool bRTL = IsRTLEnabled();
1160 if( mbIsSubEdit && GetParent() )
1161 bRTL = GetParent()->IsRTLEnabled();
1162 if( bRTL )
1163 {
1164 if( nTextWidth < nOutWidth )
1165 mnXOffset = nMinXOffset;
1166 }
1167 else
1168 {
1169 if( nTextWidth < nOutWidth )
1170 mnXOffset = nMinXOffset;
1171 else if ( mnXOffset < nMinXOffset )
1172 mnXOffset = nMinXOffset;
1173 }
1174 }
1175 else if( mnAlign == EDIT_ALIGN_CENTER2 )
1176 {
1177 // would be nicer with check while scrolling but then it's not centred in scrolled state
1178 mnXOffset = (nOutWidth - nTextWidth) / 2;
1179 }
1180}
1181
1182void Edit::ImplAlignAndPaint()
1183{
1184 ImplAlign();
1185 ImplInvalidateOrRepaint();
1186 ImplShowCursor();
1187}
1188
1189sal_Int32 Edit::ImplGetCharPos( const Point& rWindowPos ) const
1190{
1191 sal_Int32 nIndex = EDIT_NOLIMIT((sal_Int32) 0x7FFFFFFF);
1192 OUString aText = ImplGetText();
1193
1194 long nDXBuffer[256];
1195 std::unique_ptr<long[]> pDXBuffer;
1196 long* pDX = nDXBuffer;
1197 if( o3tl::make_unsigned(2*aText.getLength()) > SAL_N_ELEMENTS(nDXBuffer)(sizeof(sal_n_array_size(nDXBuffer))) )
1198 {
1199 pDXBuffer.reset(new long[2*(aText.getLength()+1)]);
1200 pDX = pDXBuffer.get();
1201 }
1202
1203 GetCaretPositions( aText, pDX, 0, aText.getLength() );
1204 long nX = rWindowPos.X() - mnXOffset - ImplGetExtraXOffset();
1205 for (sal_Int32 i = 0; i < aText.getLength(); aText.iterateCodePoints(&i))
1206 {
1207 if( (pDX[2*i] >= nX && pDX[2*i+1] <= nX) ||
1208 (pDX[2*i+1] >= nX && pDX[2*i] <= nX))
1209 {
1210 nIndex = i;
1211 if( pDX[2*i] < pDX[2*i+1] )
1212 {
1213 if( nX > (pDX[2*i]+pDX[2*i+1])/2 )
1214 aText.iterateCodePoints(&nIndex);
1215 }
1216 else
1217 {
1218 if( nX < (pDX[2*i]+pDX[2*i+1])/2 )
1219 aText.iterateCodePoints(&nIndex);
1220 }
1221 break;
1222 }
1223 }
1224 if( nIndex == EDIT_NOLIMIT((sal_Int32) 0x7FFFFFFF) )
1225 {
1226 nIndex = 0;
1227 sal_Int32 nFinalIndex = 0;
1228 long nDiff = std::abs( pDX[0]-nX );
1229 sal_Int32 i = 0;
1230 if (!aText.isEmpty())
1231 {
1232 aText.iterateCodePoints(&i); //skip the first character
1233 }
1234 while (i < aText.getLength())
1235 {
1236 long nNewDiff = std::abs( pDX[2*i]-nX );
1237
1238 if( nNewDiff < nDiff )
1239 {
1240 nIndex = i;
1241 nDiff = nNewDiff;
1242 }
1243
1244 nFinalIndex = i;
1245
1246 aText.iterateCodePoints(&i);
1247 }
1248 if (nIndex == nFinalIndex && std::abs( pDX[2*nIndex+1] - nX ) < nDiff)
1249 nIndex = EDIT_NOLIMIT((sal_Int32) 0x7FFFFFFF);
1250 }
1251
1252 return nIndex;
1253}
1254
1255void Edit::ImplSetCursorPos( sal_Int32 nChar, bool bSelect )
1256{
1257 Selection aSelection( maSelection );
1258 aSelection.Max() = nChar;
1259 if ( !bSelect )
1260 aSelection.Min() = aSelection.Max();
1261 ImplSetSelection( aSelection );
1262}
1263
1264void Edit::ImplCopyToSelectionClipboard()
1265{
1266 if ( GetSelection().Len() )
1267 {
1268 css::uno::Reference<css::datatransfer::clipboard::XClipboard> aSelection(GetPrimarySelection());
1269 ImplCopy( aSelection );
1270 }
1271}
1272
1273void Edit::ImplCopy( uno::Reference< datatransfer::clipboard::XClipboard > const & rxClipboard )
1274{
1275 vcl::unohelper::TextDataObject::CopyStringTo( GetSelected(), rxClipboard );
1276}
1277
1278void Edit::ImplPaste( uno::Reference< datatransfer::clipboard::XClipboard > const & rxClipboard )
1279{
1280 if ( !rxClipboard.is() )
1281 return;
1282
1283 uno::Reference< datatransfer::XTransferable > xDataObj;
1284
1285 try
1286 {
1287 SolarMutexReleaser aReleaser;
1288 xDataObj = rxClipboard->getContents();
1289 }
1290 catch( const css::uno::Exception& )
1291 {
1292 }
1293
1294 if ( !xDataObj.is() )
1295 return;
1296
1297 datatransfer::DataFlavor aFlavor;
1298 SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING, aFlavor );
1299 try
1300 {
1301 uno::Any aData = xDataObj->getTransferData( aFlavor );
1302 OUString aText;
1303 aData >>= aText;
1304
1305 Selection aSelection(maSelection);
1306 aSelection.Justify();
1307 if (ImplTruncateToMaxLen(aText, aSelection.Len()))
1308 ShowTruncationWarning(GetFrameWeld());
1309
1310 ReplaceSelected( aText );
1311 }
1312 catch( const css::uno::Exception& )
1313 {
1314 }
1315}
1316
1317void Edit::MouseButtonDown( const MouseEvent& rMEvt )
1318{
1319 if ( mpSubEdit )
1320 {
1321 Control::MouseButtonDown( rMEvt );
1322 return;
1323 }
1324
1325 sal_Int32 nCharPos = ImplGetCharPos( rMEvt.GetPosPixel() );
1326 Selection aSelection( maSelection );
1327 aSelection.Justify();
1328
1329 if ( rMEvt.GetClicks() < 4 )
1330 {
1331 mbClickedInSelection = false;
1332 if ( rMEvt.GetClicks() == 3 )
1333 {
1334 ImplSetSelection( Selection( 0, EDIT_NOLIMIT((sal_Int32) 0x7FFFFFFF)) );
1335 ImplCopyToSelectionClipboard();
1336
1337 }
1338 else if ( rMEvt.GetClicks() == 2 )
1339 {
1340 uno::Reference < i18n::XBreakIterator > xBI = ImplGetBreakIterator();
1341 i18n::Boundary aBoundary = xBI->getWordBoundary( maText.toString(), aSelection.Max(),
1342 GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES, true );
1343 ImplSetSelection( Selection( aBoundary.startPos, aBoundary.endPos ) );
1344 ImplCopyToSelectionClipboard();
1345 }
1346 else if ( !rMEvt.IsShift() && HasFocus() && aSelection.IsInside( nCharPos ) )
1347 mbClickedInSelection = true;
1348 else if ( rMEvt.IsLeft() )
1349 ImplSetCursorPos( nCharPos, rMEvt.IsShift() );
1350
1351 if ( !mbClickedInSelection && rMEvt.IsLeft() && ( rMEvt.GetClicks() == 1 ) )
1352 StartTracking( StartTrackingFlags::ScrollRepeat );
1353 }
1354
1355 GrabFocus();
1356}
1357
1358void Edit::MouseButtonUp( const MouseEvent& rMEvt )
1359{
1360 if ( mbClickedInSelection && rMEvt.IsLeft() )
1361 {
1362 sal_Int32 nCharPos = ImplGetCharPos( rMEvt.GetPosPixel() );
1363 ImplSetCursorPos( nCharPos, false );
1364 mbClickedInSelection = false;
1365 }
1366 else if ( rMEvt.IsMiddle() && !mbReadOnly &&
1367 ( GetSettings().GetMouseSettings().GetMiddleButtonAction() == MouseMiddleButtonAction::PasteSelection ) )
1368 {
1369 css::uno::Reference<css::datatransfer::clipboard::XClipboard> aSelection(Window::GetPrimarySelection());
1370 ImplPaste( aSelection );
1371 Modify();
1372 }
1373}
1374
1375void Edit::Tracking( const TrackingEvent& rTEvt )
1376{
1377 if ( rTEvt.IsTrackingEnded() )
1378 {
1379 if ( mbClickedInSelection )
1380 {
1381 sal_Int32 nCharPos = ImplGetCharPos( rTEvt.GetMouseEvent().GetPosPixel() );
1382 ImplSetCursorPos( nCharPos, false );
1383 mbClickedInSelection = false;
1384 }
1385 else if ( rTEvt.GetMouseEvent().IsLeft() )
1386 {
1387 ImplCopyToSelectionClipboard();
1388 }
1389 }
1390 else
1391 {
1392 if( !mbClickedInSelection )
1393 {
1394 sal_Int32 nCharPos = ImplGetCharPos( rTEvt.GetMouseEvent().GetPosPixel() );
1395 ImplSetCursorPos( nCharPos, true );
1396 }
1397 }
1398}
1399
1400bool Edit::ImplHandleKeyEvent( const KeyEvent& rKEvt )
1401{
1402 bool bDone = false;
1403 sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode();
1404 KeyFuncType eFunc = rKEvt.GetKeyCode().GetFunction();
1405
1406 mbInternModified = false;
1407
1408 if ( eFunc != KeyFuncType::DONTKNOW )
1409 {
1410 switch ( eFunc )
1411 {
1412 case KeyFuncType::CUT:
1413 {
1414 if ( !mbReadOnly && maSelection.Len() && !mbPassword )
1415 {
1416 Cut();
1417 Modify();
1418 bDone = true;
1419 }
1420 }
1421 break;
1422
1423 case KeyFuncType::COPY:
1424 {
1425 if ( !mbPassword )
1426 {
1427 Copy();
1428 bDone = true;
1429 }
1430 }
1431 break;
1432
1433 case KeyFuncType::PASTE:
1434 {
1435 if ( !mbReadOnly )
1436 {
1437 Paste();
1438 bDone = true;
1439 }
1440 }
1441 break;
1442
1443 case KeyFuncType::UNDO:
1444 {
1445 if ( !mbReadOnly )
1446 {
1447 Undo();
1448 bDone = true;
1449 }
1450 }
1451 break;
1452
1453 default:
1454 eFunc = KeyFuncType::DONTKNOW;
1455 }
1456 }
1457
1458 if ( !bDone && rKEvt.GetKeyCode().IsMod1() && !rKEvt.GetKeyCode().IsMod2() )
1459 {
1460 if ( nCode == KEY_A )
1461 {
1462 ImplSetSelection( Selection( 0, maText.getLength() ) );
1463 bDone = true;
1464 }
1465 else if ( rKEvt.GetKeyCode().IsShift() && (nCode == KEY_S) )
1466 {
1467 if ( pImplFncGetSpecialChars )
1468 {
1469 Selection aSaveSel = GetSelection(); // if someone changes the selection in Get/LoseFocus, e.g. URL bar
1470 OUString aChars = pImplFncGetSpecialChars( GetFrameWeld(), GetFont() );
1471 SetSelection( aSaveSel );
1472 if ( !aChars.isEmpty() )
1473 {
1474 ImplInsertText( aChars );
1475 Modify();
1476 }
1477 bDone = true;
1478 }
1479 }
1480 }
1481
1482 if ( eFunc == KeyFuncType::DONTKNOW && ! bDone )
1483 {
1484 switch ( nCode )
1485 {
1486 case css::awt::Key::SELECT_ALL:
1487 {
1488 ImplSetSelection( Selection( 0, maText.getLength() ) );
1489 bDone = true;
1490 }
1491 break;
1492
1493 case KEY_LEFT:
1494 case KEY_RIGHT:
1495 case KEY_HOME:
1496 case KEY_END:
1497 case css::awt::Key::MOVE_WORD_FORWARD:
1498 case css::awt::Key::SELECT_WORD_FORWARD:
1499 case css::awt::Key::MOVE_WORD_BACKWARD:
1500 case css::awt::Key::SELECT_WORD_BACKWARD:
1501 case css::awt::Key::MOVE_TO_BEGIN_OF_LINE:
1502 case css::awt::Key::MOVE_TO_END_OF_LINE:
1503 case css::awt::Key::SELECT_TO_BEGIN_OF_LINE:
1504 case css::awt::Key::SELECT_TO_END_OF_LINE:
1505 case css::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH:
1506 case css::awt::Key::MOVE_TO_END_OF_PARAGRAPH:
1507 case css::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH:
1508 case css::awt::Key::SELECT_TO_END_OF_PARAGRAPH:
1509 case css::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT:
1510 case css::awt::Key::MOVE_TO_END_OF_DOCUMENT:
1511 case css::awt::Key::SELECT_TO_BEGIN_OF_DOCUMENT:
1512 case css::awt::Key::SELECT_TO_END_OF_DOCUMENT:
1513 {
1514 if ( !rKEvt.GetKeyCode().IsMod2() )
1515 {
1516 ImplClearLayoutData();
1517 uno::Reference < i18n::XBreakIterator > xBI = ImplGetBreakIterator();
1518
1519 Selection aSel( maSelection );
1520 bool bWord = rKEvt.GetKeyCode().IsMod1();
1521 bool bSelect = rKEvt.GetKeyCode().IsShift();
1522 bool bGoLeft = (nCode == KEY_LEFT);
1523 bool bGoRight = (nCode == KEY_RIGHT);
1524 bool bGoHome = (nCode == KEY_HOME);
1525 bool bGoEnd = (nCode == KEY_END);
1526
1527 switch( nCode )
1528 {
1529 case css::awt::Key::MOVE_WORD_FORWARD:
1530 bGoRight = bWord = true;break;
1531 case css::awt::Key::SELECT_WORD_FORWARD:
1532 bGoRight = bSelect = bWord = true;break;
1533 case css::awt::Key::MOVE_WORD_BACKWARD:
1534 bGoLeft = bWord = true;break;
1535 case css::awt::Key::SELECT_WORD_BACKWARD:
1536 bGoLeft = bSelect = bWord = true;break;
1537 case css::awt::Key::SELECT_TO_BEGIN_OF_LINE:
1538 case css::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH:
1539 case css::awt::Key::SELECT_TO_BEGIN_OF_DOCUMENT:
1540 bSelect = true;
1541 [[fallthrough]];
1542 case css::awt::Key::MOVE_TO_BEGIN_OF_LINE:
1543 case css::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH:
1544 case css::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT:
1545 bGoHome = true;break;
1546 case css::awt::Key::SELECT_TO_END_OF_LINE:
1547 case css::awt::Key::SELECT_TO_END_OF_PARAGRAPH:
1548 case css::awt::Key::SELECT_TO_END_OF_DOCUMENT:
1549 bSelect = true;
1550 [[fallthrough]];
1551 case css::awt::Key::MOVE_TO_END_OF_LINE:
1552 case css::awt::Key::MOVE_TO_END_OF_PARAGRAPH:
1553 case css::awt::Key::MOVE_TO_END_OF_DOCUMENT:
1554 bGoEnd = true;break;
1555 default:
1556 break;
1557 }
1558
1559 // range is checked in ImplSetSelection ...
1560 if ( bGoLeft && aSel.Max() )
1561 {
1562 if ( bWord )
1563 {
1564 i18n::Boundary aBoundary = xBI->getWordBoundary( maText.toString(), aSel.Max(),
1565 GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES, true );
1566 if ( aBoundary.startPos == aSel.Max() )
1567 aBoundary = xBI->previousWord( maText.toString(), aSel.Max(),
1568 GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES );
1569 aSel.Max() = aBoundary.startPos;
1570 }
1571 else
1572 {
1573 sal_Int32 nCount = 1;
1574 aSel.Max() = xBI->previousCharacters( maText.toString(), aSel.Max(),
1575 GetSettings().GetLanguageTag().getLocale(), i18n::CharacterIteratorMode::SKIPCHARACTER, nCount, nCount );
1576 }
1577 }
1578 else if ( bGoRight && ( aSel.Max() < maText.getLength() ) )
1579 {
1580 if ( bWord )
1581 {
1582 i18n::Boundary aBoundary = xBI->nextWord( maText.toString(), aSel.Max(),
1583 GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES );
1584 aSel.Max() = aBoundary.startPos;
1585 }
1586 else
1587 {
1588 sal_Int32 nCount = 1;
1589 aSel.Max() = xBI->nextCharacters( maText.toString(), aSel.Max(),
1590 GetSettings().GetLanguageTag().getLocale(), i18n::CharacterIteratorMode::SKIPCHARACTER, nCount, nCount );
1591 }
1592 }
1593 else if ( bGoHome )
1594 {
1595 aSel.Max() = 0;
1596 }
1597 else if ( bGoEnd )
1598 {
1599 aSel.Max() = EDIT_NOLIMIT((sal_Int32) 0x7FFFFFFF);
1600 }
1601
1602 if ( !bSelect )
1603 aSel.Min() = aSel.Max();
1604
1605 if ( aSel != GetSelection() )
1606 {
1607 ImplSetSelection( aSel );
1608 ImplCopyToSelectionClipboard();
1609 }
1610
1611 if (bGoEnd && maAutocompleteHdl.IsSet() && !rKEvt.GetKeyCode().GetModifier())
1612 {
1613 if ( (maSelection.Min() == maSelection.Max()) && (maSelection.Min() == maText.getLength()) )
1614 {
1615 maAutocompleteHdl.Call(*this);
1616 }
1617 }
1618
1619 bDone = true;
1620 }
1621 }
1622 break;
1623
1624 case css::awt::Key::DELETE_WORD_BACKWARD:
1625 case css::awt::Key::DELETE_WORD_FORWARD:
1626 case css::awt::Key::DELETE_TO_BEGIN_OF_LINE:
1627 case css::awt::Key::DELETE_TO_END_OF_LINE:
1628 case KEY_BACKSPACE:
1629 case KEY_DELETE:
1630 {
1631 if ( !mbReadOnly && !rKEvt.GetKeyCode().IsMod2() )
1632 {
1633 sal_uInt8 nDel = (nCode == KEY_DELETE) ? EDIT_DEL_RIGHT2 : EDIT_DEL_LEFT1;
1634 sal_uInt8 nMode = rKEvt.GetKeyCode().IsMod1() ? EDIT_DELMODE_RESTOFWORD12 : EDIT_DELMODE_SIMPLE11;
1635 if ( (nMode == EDIT_DELMODE_RESTOFWORD12) && rKEvt.GetKeyCode().IsShift() )
1636 nMode = EDIT_DELMODE_RESTOFCONTENT13;
1637 switch( nCode )
1638 {
1639 case css::awt::Key::DELETE_WORD_BACKWARD:
1640 nDel = EDIT_DEL_LEFT1;
1641 nMode = EDIT_DELMODE_RESTOFWORD12;
1642 break;
1643 case css::awt::Key::DELETE_WORD_FORWARD:
1644 nDel = EDIT_DEL_RIGHT2;
1645 nMode = EDIT_DELMODE_RESTOFWORD12;
1646 break;
1647 case css::awt::Key::DELETE_TO_BEGIN_OF_LINE:
1648 nDel = EDIT_DEL_LEFT1;
1649 nMode = EDIT_DELMODE_RESTOFCONTENT13;
1650 break;
1651 case css::awt::Key::DELETE_TO_END_OF_LINE:
1652 nDel = EDIT_DEL_RIGHT2;
1653 nMode = EDIT_DELMODE_RESTOFCONTENT13;
1654 break;
1655 default: break;
1656 }
1657 sal_Int32 nOldLen = maText.getLength();
1658 ImplDelete( maSelection, nDel, nMode );
1659 if ( maText.getLength() != nOldLen )
1660 Modify();
1661 bDone = true;
1662 }
1663 }
1664 break;
1665
1666 case KEY_INSERT:
1667 {
1668 if ( !mpIMEInfos && !mbReadOnly && !rKEvt.GetKeyCode().IsMod2() )
1669 {
1670 SetInsertMode( !mbInsertMode );
1671 bDone = true;
1672 }
1673 }
1674 break;
1675
1676 case KEY_RETURN:
1677 if (maActivateHdl.IsSet())
1678 {
1679 bDone = maActivateHdl.Call(*this);
1680 }
1681 break;
1682
1683 default:
1684 {
1685 if ( IsCharInput( rKEvt ) )
1686 {
1687 bDone = true; // read characters also when in ReadOnly
1688 if ( !mbReadOnly )
1689 {
1690 ImplInsertText(OUString(rKEvt.GetCharCode()), nullptr, true);
1691 if (maAutocompleteHdl.IsSet())
1692 {
1693 if ( (maSelection.Min() == maSelection.Max()) && (maSelection.Min() == maText.getLength()) )
1694 {
1695 maAutocompleteHdl.Call(*this);
1696 }
1697 }
1698 }
1699 }
1700 }
1701 }
1702 }
1703
1704 if ( mbInternModified )
1705 Modify();
1706
1707 return bDone;
1708}
1709
1710void Edit::KeyInput( const KeyEvent& rKEvt )
1711{
1712 if ( mpSubEdit || !ImplHandleKeyEvent( rKEvt ) )
1713 Control::KeyInput( rKEvt );
1714}
1715
1716void Edit::FillLayoutData() const
1717{
1718 mpControlData->mpLayoutData.reset( new vcl::ControlLayoutData );
1719 const_cast<Edit*>(this)->Invalidate();
1720}
1721
1722void Edit::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRectangle)
1723{
1724 if (!mpSubEdit)
1725 ImplRepaint(rRenderContext, rRectangle);
1726}
1727
1728void Edit::Resize()
1729{
1730 if ( !mpSubEdit && IsReallyVisible() )
1731 {
1732 Control::Resize();
1733 // because of vertical centering...
1734 mnXOffset = 0;
1735 ImplAlign();
1736 Invalidate();
1737 ImplShowCursor();
1738 }
1739}
1740
1741void Edit::Draw( OutputDevice* pDev, const Point& rPos, DrawFlags nFlags )
1742{
1743 ApplySettings(*pDev);
1744
1745 Point aPos = pDev->LogicToPixel( rPos );
1746 Size aSize = GetSizePixel();
1747 vcl::Font aFont = GetDrawPixelFont( pDev );
1748
1749 pDev->Push();
1750 pDev->SetMapMode();
1751 pDev->SetFont( aFont );
1752 pDev->SetTextFillColor();
1753
1754 // Border/Background
1755 pDev->SetLineColor();
1756 pDev->SetFillColor();
1757 bool bBorder = (GetStyle() & WB_BORDER);
1758 bool bBackground = IsControlBackground();
1759 if ( bBorder || bBackground )
1760 {
1761 tools::Rectangle aRect( aPos, aSize );
1762 if ( bBorder )
1763 {
1764 ImplDrawFrame( pDev, aRect );
1765 }
1766 if ( bBackground )
1767 {
1768 pDev->SetFillColor( GetControlBackground() );
1769 pDev->DrawRect( aRect );
1770 }
1771 }
1772
1773 // Content
1774 if ( nFlags & DrawFlags::Mono )
1775 pDev->SetTextColor( COL_BLACK );
1776 else
1777 {
1778 if ( !IsEnabled() )
1779 {
1780 const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
1781 pDev->SetTextColor( rStyleSettings.GetDisableColor() );
1782 }
1783 else
1784 {
1785 pDev->SetTextColor( GetTextColor() );
1786 }
1787 }
1788
1789 const long nOnePixel = GetDrawPixel( pDev, 1 );
1790 const long nOffX = 3*nOnePixel;
1791 DrawTextFlags nTextStyle = DrawTextFlags::VCenter;
1792 tools::Rectangle aTextRect( aPos, aSize );
1793
1794 if ( GetStyle() & WB_CENTER )
1795 nTextStyle |= DrawTextFlags::Center;
1796 else if ( GetStyle() & WB_RIGHT )
1797 nTextStyle |= DrawTextFlags::Right;
1798 else
1799 nTextStyle |= DrawTextFlags::Left;
1800
1801 aTextRect.AdjustLeft(nOffX );
1802 aTextRect.AdjustRight( -nOffX );
1803
1804 OUString aText = ImplGetText();
1805 long nTextHeight = pDev->GetTextHeight();
1806 long nTextWidth = pDev->GetTextWidth( aText );
1807 long nOffY = (aSize.Height() - nTextHeight) / 2;
1808
1809 // Clipping?
1810 if ( (nOffY < 0) ||
1811 ((nOffY+nTextHeight) > aSize.Height()) ||
1812 ((nOffX+nTextWidth) > aSize.Width()) )
1813 {
1814 tools::Rectangle aClip( aPos, aSize );
1815 if ( nTextHeight > aSize.Height() )
1816 aClip.AdjustBottom(nTextHeight-aSize.Height()+1 ); // prevent HP printers from 'optimizing'
1817 pDev->IntersectClipRegion( aClip );
1818 }
1819
1820 pDev->DrawText( aTextRect, aText, nTextStyle );
1821 pDev->Pop();
1822
1823 if ( GetSubEdit() )
1824 {
1825 Size aOrigSize(GetSubEdit()->GetSizePixel());
1826 GetSubEdit()->SetSizePixel(GetSizePixel());
1827 GetSubEdit()->Draw(pDev, rPos, nFlags);
1828 GetSubEdit()->SetSizePixel(aOrigSize);
1829 }
1830}
1831
1832void Edit::ImplInvalidateOutermostBorder( vcl::Window* pWin )
1833{
1834 // allow control to show focused state
1835 vcl::Window *pInvalWin = pWin;
1836 for (;;)
1837 {
1838 vcl::Window* pBorder = pInvalWin->GetWindow( GetWindowType::Border );
1839 if (pBorder == pInvalWin || !pBorder ||
1840 pInvalWin->ImplGetFrame() != pBorder->ImplGetFrame() )
1841 break;
1842 pInvalWin = pBorder;
1843 }
1844
1845 pInvalWin->Invalidate( InvalidateFlags::Children | InvalidateFlags::Update );
1846}
1847
1848void Edit::GetFocus()
1849{
1850 if ( mpSubEdit )
1851 mpSubEdit->ImplGrabFocus( GetGetFocusFlags() );
1852 else if ( !mbActivePopup )
1853 {
1854 maUndoText = maText.toString();
1855 SelectionOptions nSelOptions = GetSettings().GetStyleSettings().GetSelectionOptions();
1856 if ( !( GetStyle() & (WB_NOHIDESELECTION|WB_READONLY) )
1857 && ( GetGetFocusFlags() & (GetFocusFlags::Init|GetFocusFlags::Tab|GetFocusFlags::CURSOR|GetFocusFlags::Mnemonic) ) )
1858 {
1859 if ( nSelOptions & SelectionOptions::ShowFirst )
1860 {
1861 maSelection.Min() = maText.getLength();
1862 maSelection.Max() = 0;
1863 }
1864 else
1865 {
1866 maSelection.Min() = 0;
1867 maSelection.Max() = maText.getLength();
1868 }
1869 if ( mbIsSubEdit )
1870 static_cast<Edit*>(GetParent())->CallEventListeners( VclEventId::EditSelectionChanged );
1871 else
1872 CallEventListeners( VclEventId::EditSelectionChanged );
1873 }
1874
1875 ImplShowCursor();
1876
1877 // FIXME: this is currently only on macOS
1878 // check for other platforms that need similar handling
1879 if( ImplGetSVData()->maNWFData.mbNoFocusRects &&
1880 IsNativeWidgetEnabled() &&
1881 IsNativeControlSupported( ControlType::Editbox, ControlPart::Entire ) )
1882 {
1883 ImplInvalidateOutermostBorder( mbIsSubEdit ? GetParent() : this );
1884 }
1885 else if ( maSelection.Len() )
1886 {
1887 // paint the selection
1888 if ( !HasPaintEvent() )
1889 ImplInvalidateOrRepaint();
1890 else
1891 Invalidate();
1892 }
1893
1894 SetInputContext( InputContext( GetFont(), !IsReadOnly() ? InputContextFlags::Text|InputContextFlags::ExtText : InputContextFlags::NONE ) );
1895 }
1896
1897 Control::GetFocus();
1898}
1899
1900void Edit::LoseFocus()
1901{
1902 if ( !mpSubEdit )
1903 {
1904 // FIXME: this is currently only on macOS
1905 // check for other platforms that need similar handling
1906 if( ImplGetSVData()->maNWFData.mbNoFocusRects &&
1907 IsNativeWidgetEnabled() &&
1908 IsNativeControlSupported( ControlType::Editbox, ControlPart::Entire ) )
1909 {
1910 ImplInvalidateOutermostBorder( mbIsSubEdit ? GetParent() : this );
1911 }
1912
1913 if ( !mbActivePopup && !( GetStyle() & WB_NOHIDESELECTION ) && maSelection.Len() )
1914 ImplInvalidateOrRepaint(); // paint the selection
1915 }
1916
1917 Control::LoseFocus();
1918}
1919
1920void Edit::Command( const CommandEvent& rCEvt )
1921{
1922 if ( rCEvt.GetCommand() == CommandEventId::ContextMenu )
1923 {
1924 VclPtr<PopupMenu> pPopup = Edit::CreatePopupMenu();
1925
1926 bool bEnableCut = true;
1927 bool bEnableCopy = true;
1928 bool bEnableDelete = true;
1929 bool bEnablePaste = true;
1930 bool bEnableSpecialChar = true;
1931
1932 if ( !maSelection.Len() )
1933 {
1934 bEnableCut = false;
1935 bEnableCopy = false;
1936 bEnableDelete = false;
1937 }
1938
1939 if ( IsReadOnly() )
1940 {
1941 bEnableCut = false;
1942 bEnablePaste = false;
1943 bEnableDelete = false;
1944 bEnableSpecialChar = false;
1945 }
1946 else
1947 {
1948 // only paste if text available in clipboard
1949 bool bData = false;
1950 uno::Reference< datatransfer::clipboard::XClipboard > xClipboard = GetClipboard();
1951
1952 if ( xClipboard.is() )
1953 {
1954 uno::Reference< datatransfer::XTransferable > xDataObj;
1955 {
1956 SolarMutexReleaser aReleaser;
1957 xDataObj = xClipboard->getContents();
1958 }
1959 if ( xDataObj.is() )
1960 {
1961 datatransfer::DataFlavor aFlavor;
1962 SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING, aFlavor );
1963 bData = xDataObj->isDataFlavorSupported( aFlavor );
1964 }
1965 }
1966 bEnablePaste = bData;
1967 }
1968
1969 pPopup->EnableItem(pPopup->GetItemId("cut"), bEnableCut);
1970 pPopup->EnableItem(pPopup->GetItemId("copy"), bEnableCopy);
1971 pPopup->EnableItem(pPopup->GetItemId("delete"), bEnableDelete);
1972 pPopup->EnableItem(pPopup->GetItemId("paste"), bEnablePaste);
1973 pPopup->EnableItem(pPopup->GetItemId("specialchar"), bEnableSpecialChar);
1974 pPopup->EnableItem(
1975 pPopup->GetItemId("undo"),
1976 std::u16string_view(maUndoText)
1977 != std::u16string_view(maText.getStr(), maText.getLength()));
1978 bool bAllSelected = maSelection.Min() == 0 && maSelection.Max() == maText.getLength();
1979 pPopup->EnableItem(pPopup->GetItemId("selectall"), !bAllSelected);
1980 pPopup->ShowItem(pPopup->GetItemId("specialchar"), pImplFncGetSpecialChars != nullptr);
1981
1982 mbActivePopup = true;
1983 Selection aSaveSel = GetSelection(); // if someone changes selection in Get/LoseFocus, e.g. URL bar
1984 Point aPos = rCEvt.GetMousePosPixel();
1985 if ( !rCEvt.IsMouseEvent() )
1986 {
1987 // Show menu eventually centered in selection
1988 Size aSize = GetOutputSizePixel();
1989 aPos = Point( aSize.Width()/2, aSize.Height()/2 );
1990 }
1991 sal_uInt16 n = pPopup->Execute( this, aPos );
1992 SetSelection( aSaveSel );
1993 OString sCommand = pPopup->GetItemIdent(n);
1994 if (sCommand == "undo")
1995 {
1996 Undo();
1997 Modify();
1998 }
1999 else if (sCommand == "cut")
2000 {
2001 Cut();
2002 Modify();
2003 }
2004 else if (sCommand == "copy")
2005 {
2006 Copy();
2007 }
2008 else if (sCommand == "paste")
2009 {
2010 Paste();
2011 Modify();
2012 }
2013 else if (sCommand == "delete")
2014 {
2015 DeleteSelected();
2016 Modify();
2017 }
2018 else if (sCommand == "selectall")
2019 {
2020 ImplSetSelection( Selection( 0, maText.getLength() ) );
2021 }
2022 else if (sCommand == "specialchar" && pImplFncGetSpecialChars)
2023 {
2024 OUString aChars = pImplFncGetSpecialChars(GetFrameWeld(), GetFont());
2025 if (!IsDisposed()) // destroyed while the insert special character dialog was still open
2026 {
2027 SetSelection( aSaveSel );
2028 if (!aChars.isEmpty())
2029 {
2030 ImplInsertText( aChars );
2031 Modify();
2032 }
2033 }
2034 }
2035 pPopup.clear();
2036 mbActivePopup = false;
2037 }
2038 else if ( rCEvt.GetCommand() == CommandEventId::StartExtTextInput )
2039 {
2040 DeleteSelected();
2041 sal_Int32 nPos = maSelection.Max();
2042 mpIMEInfos.reset(new Impl_IMEInfos( nPos, maText.copy(nPos).makeStringAndClear() ));
2043 mpIMEInfos->bWasCursorOverwrite = !IsInsertMode();
2044 }
2045 else if ( rCEvt.GetCommand() == CommandEventId::EndExtTextInput )
2046 {
2047 bool bInsertMode = !mpIMEInfos->bWasCursorOverwrite;
2048 mpIMEInfos.reset();
2049
2050 SetInsertMode(bInsertMode);
2051 Modify();
2052
2053 Invalidate();
2054
2055 // #i25161# call auto complete handler for ext text commit also
2056 if (maAutocompleteHdl.IsSet())
2057 {
2058 if ( (maSelection.Min() == maSelection.Max()) && (maSelection.Min() == maText.getLength()) )
2059 {
2060 maAutocompleteHdl.Call(*this);
2061 }
2062 }
2063 }
2064 else if ( rCEvt.GetCommand() == CommandEventId::ExtTextInput )
2065 {
2066 const CommandExtTextInputData* pData = rCEvt.GetExtTextInputData();
2067
2068 maText.remove( mpIMEInfos->nPos, mpIMEInfos->nLen );
2069 maText.insert( mpIMEInfos->nPos, pData->GetText() );
2070 if ( mpIMEInfos->bWasCursorOverwrite )
2071 {
2072 const sal_Int32 nOldIMETextLen = mpIMEInfos->nLen;
2073 const sal_Int32 nNewIMETextLen = pData->GetText().getLength();
2074 if ( ( nOldIMETextLen > nNewIMETextLen ) &&
2075 ( nNewIMETextLen < mpIMEInfos->aOldTextAfterStartPos.getLength() ) )
2076 {
2077 // restore old characters
2078 const sal_Int32 nRestore = nOldIMETextLen - nNewIMETextLen;
2079 maText.insert( mpIMEInfos->nPos + nNewIMETextLen, mpIMEInfos->aOldTextAfterStartPos.copy( nNewIMETextLen, nRestore ) );
2080 }
2081 else if ( ( nOldIMETextLen < nNewIMETextLen ) &&
2082 ( nOldIMETextLen < mpIMEInfos->aOldTextAfterStartPos.getLength() ) )
2083 {
2084 const sal_Int32 nOverwrite = ( nNewIMETextLen > mpIMEInfos->aOldTextAfterStartPos.getLength()
2085 ? mpIMEInfos->aOldTextAfterStartPos.getLength() : nNewIMETextLen ) - nOldIMETextLen;
2086 maText.remove( mpIMEInfos->nPos + nNewIMETextLen, nOverwrite );
2087 }
2088 }
2089
2090 if ( pData->GetTextAttr() )
2091 {
2092 mpIMEInfos->CopyAttribs( pData->GetTextAttr(), pData->GetText().getLength() );
2093 mpIMEInfos->bCursor = pData->IsCursorVisible();
2094 }
2095 else
2096 {
2097 mpIMEInfos->DestroyAttribs();
2098 }
2099
2100 ImplAlignAndPaint();
2101 sal_Int32 nCursorPos = mpIMEInfos->nPos + pData->GetCursorPos();
2102 SetSelection( Selection( nCursorPos, nCursorPos ) );
2103 SetInsertMode( !pData->IsCursorOverwrite() );
2104
2105 if ( pData->IsCursorVisible() )
2106 GetCursor()->Show();
2107 else
2108 GetCursor()->Hide();
2109 }
2110 else if ( rCEvt.GetCommand() == CommandEventId::CursorPos )
2111 {
2112 if ( mpIMEInfos )
2113 {
2114 sal_Int32 nCursorPos = GetSelection().Max();
2115 SetCursorRect( nullptr, GetTextWidth( maText.toString(), nCursorPos, mpIMEInfos->nPos+mpIMEInfos->nLen-nCursorPos ) );
2116 }
2117 else
2118 {
2119 SetCursorRect();
2120 }
2121 }
2122 else if ( rCEvt.GetCommand() == CommandEventId::SelectionChange )
2123 {
2124 const CommandSelectionChangeData *pData = rCEvt.GetSelectionChangeData();
2125 Selection aSelection( pData->GetStart(), pData->GetEnd() );
2126 SetSelection(aSelection);
2127 }
2128 else if ( rCEvt.GetCommand() == CommandEventId::QueryCharPosition )
2129 {
2130 if (mpIMEInfos && mpIMEInfos->nLen > 0)
2131 {
2132 OUString aText = ImplGetText();
2133 long nDXBuffer[256];
2134 std::unique_ptr<long[]> pDXBuffer;
2135 long* pDX = nDXBuffer;
2136
2137 if( !aText.isEmpty() )
2138 {
2139 if( o3tl::make_unsigned(2*aText.getLength()) > SAL_N_ELEMENTS(nDXBuffer)(sizeof(sal_n_array_size(nDXBuffer))) )
2140 {
2141 pDXBuffer.reset(new long[2*(aText.getLength()+1)]);
2142 pDX = pDXBuffer.get();
2143 }
2144
2145 GetCaretPositions( aText, pDX, 0, aText.getLength() );
2146 }
2147 long nTH = GetTextHeight();
2148 Point aPos( mnXOffset, ImplGetTextYPosition() );
2149
2150 std::unique_ptr<tools::Rectangle[]> aRects(new tools::Rectangle[ mpIMEInfos->nLen ]);
2151 for ( int nIndex = 0; nIndex < mpIMEInfos->nLen; ++nIndex )
2152 {
2153 tools::Rectangle aRect( aPos, Size( 10, nTH ) );
2154 aRect.SetLeft( pDX[2*(nIndex+mpIMEInfos->nPos)] + mnXOffset + ImplGetExtraXOffset() );
2155 aRects[ nIndex ] = aRect;
2156 }
2157 SetCompositionCharRect( aRects.get(), mpIMEInfos->nLen );
2158 }
2159 }
2160 else
2161 Control::Command( rCEvt );
2162}
2163
2164void Edit::StateChanged( StateChangedType nType )
2165{
2166 if (nType == StateChangedType::InitShow)
2167 {
2168 if (!mpSubEdit)
2169 {
2170 mnXOffset = 0; // if GrabFocus before while size was still wrong
2171 ImplAlign();
2172 if (!mpSubEdit)
2173 ImplShowCursor(false);
2174 Invalidate();
2175 }
2176 }
2177 else if (nType == StateChangedType::Enable)
2178 {
2179 if (!mpSubEdit)
2180 {
2181 // change text color only
2182 ImplInvalidateOrRepaint();
2183 }
2184 }
2185 else if (nType == StateChangedType::Style || nType == StateChangedType::Mirroring)
2186 {
2187 WinBits nStyle = GetStyle();
2188 if (nType == StateChangedType::Style)
2189 {
2190 nStyle = ImplInitStyle(GetStyle());
2191 SetStyle(nStyle);
2192 }
2193
2194 sal_uInt16 nOldAlign = mnAlign;
2195 mnAlign = EDIT_ALIGN_LEFT1;
2196
2197 // hack: right align until keyinput and cursor travelling works
2198 // edits are always RTL disabled
2199 // however the parent edits contain the correct setting
2200 if (mbIsSubEdit && GetParent()->IsRTLEnabled())
2201 {
2202 if (GetParent()->GetStyle() & WB_LEFT)
2203 mnAlign = EDIT_ALIGN_RIGHT3;
2204 if (nType == StateChangedType::Mirroring)
2205 SetLayoutMode(ComplexTextLayoutFlags::BiDiRtl | ComplexTextLayoutFlags::TextOriginLeft);
2206 }
2207 else if (mbIsSubEdit && !GetParent()->IsRTLEnabled())
2208 {
2209 if (nType == StateChangedType::Mirroring)
2210 SetLayoutMode(ComplexTextLayoutFlags::TextOriginLeft);
2211 }
2212
2213 if (nStyle & WB_RIGHT)
2214 mnAlign = EDIT_ALIGN_RIGHT3;
2215 else if (nStyle & WB_CENTER)
2216 mnAlign = EDIT_ALIGN_CENTER2;
2217 if (!maText.isEmpty() && (mnAlign != nOldAlign))
2218 {
2219 ImplAlign();
2220 Invalidate();
2221 }
2222
2223 }
2224 else if ((nType == StateChangedType::Zoom) || (nType == StateChangedType::ControlFont))
2225 {
2226 if (!mpSubEdit)
2227 {
2228 ApplySettings(*this);
2229 ImplShowCursor();
2230 Invalidate();
2231 }
2232 }
2233 else if ((nType == StateChangedType::ControlForeground) || (nType == StateChangedType::ControlBackground))
2234 {
2235 if (!mpSubEdit)
2236 {
2237 ApplySettings(*this);
2238 Invalidate();
2239 }
2240 }
2241
2242 Control::StateChanged(nType);
2243}
2244
2245void Edit::DataChanged( const DataChangedEvent& rDCEvt )
2246{
2247 if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
2248 (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
2249 ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
2250 (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) )
2251 {
2252 if ( !mpSubEdit )
2253 {
2254 ApplySettings(*this);
2255 ImplShowCursor();
2256 Invalidate();
2257 }
2258 }
2259
2260 Control::DataChanged( rDCEvt );
2261}
2262
2263void Edit::ImplShowDDCursor()
2264{
2265 if (!mpDDInfo->bVisCursor)
2266 {
2267 long nTextWidth = GetTextWidth( maText.toString(), 0, mpDDInfo->nDropPos );
2268 long nTextHeight = GetTextHeight();
2269 tools::Rectangle aCursorRect( Point( nTextWidth + mnXOffset, (GetOutputSize().Height()-nTextHeight)/2 ), Size( 2, nTextHeight ) );
2270 mpDDInfo->aCursor.SetWindow( this );
2271 mpDDInfo->aCursor.SetPos( aCursorRect.TopLeft() );
2272 mpDDInfo->aCursor.SetSize( aCursorRect.GetSize() );
2273 mpDDInfo->aCursor.Show();
2274 mpDDInfo->bVisCursor = true;
2275 }
2276}
2277
2278void Edit::ImplHideDDCursor()
2279{
2280 if ( mpDDInfo && mpDDInfo->bVisCursor )
2281 {
2282 mpDDInfo->aCursor.Hide();
2283 mpDDInfo->bVisCursor = false;
2284 }
2285}
2286
2287TextFilter::TextFilter(const OUString &rForbiddenChars)
2288 : sForbiddenChars(rForbiddenChars)
2289{
2290}
2291
2292TextFilter::~TextFilter()
2293{
2294}
2295
2296OUString TextFilter::filter(const OUString &rText)
2297{
2298 OUString sTemp(rText);
2299 for (sal_Int32 i = 0; i < sForbiddenChars.getLength(); ++i)
2300 {
2301 sTemp = sTemp.replaceAll(OUStringChar(sForbiddenChars[i]), "");
2302 }
2303 return sTemp;
2304}
2305
2306void Edit::filterText()
2307{
2308 Selection aSel = GetSelection();
2309 const OUString sOrig = GetText();
2310 const OUString sNew = mpFilterText->filter(GetText());
2311 if (sOrig != sNew)
2312 {
2313 sal_Int32 nDiff = sOrig.getLength() - sNew.getLength();
2314 if (nDiff)
2315 {
2316 aSel.setMin(aSel.getMin() - nDiff);
2317 aSel.setMax(aSel.getMin());
2318 }
2319 SetText(sNew);
2320 SetSelection(aSel);
2321 }
2322}
2323
2324void Edit::Modify()
2325{
2326 if (mpFilterText)
2327 filterText();
2328
2329 if ( mbIsSubEdit )
2330 {
2331 static_cast<Edit*>(GetParent())->Modify();
2332 }
2333 else
2334 {
2335 if ( ImplCallEventListenersAndHandler( VclEventId::EditModify, [this] () { maModifyHdl.Call(*this); } ) )
2336 // have been destroyed while calling into the handlers
2337 return;
2338
2339 // #i13677# notify edit listeners about caret position change
2340 CallEventListeners( VclEventId::EditCaretChanged );
2341 // FIXME: this is currently only on macOS
2342 // check for other platforms that need similar handling
2343 if( ImplGetSVData()->maNWFData.mbNoFocusRects &&
2344 IsNativeWidgetEnabled() &&
2345 IsNativeControlSupported( ControlType::Editbox, ControlPart::Entire ) )
2346 {
2347 ImplInvalidateOutermostBorder( this );
2348 }
2349 }
2350}
2351
2352void Edit::SetEchoChar( sal_Unicode c )
2353{
2354 mcEchoChar = c;
2355 if ( mpSubEdit )
2356 mpSubEdit->SetEchoChar( c );
2357}
2358
2359void Edit::SetReadOnly( bool bReadOnly )
2360{
2361 if ( mbReadOnly != bReadOnly )
2362 {
2363 mbReadOnly = bReadOnly;
2364 if ( mpSubEdit )
2365 mpSubEdit->SetReadOnly( bReadOnly );
2366
2367 CompatStateChanged( StateChangedType::ReadOnly );
2368 }
2369}
2370
2371void Edit::SetInsertMode( bool bInsert )
2372{
2373 if ( bInsert != mbInsertMode )
2374 {
2375 mbInsertMode = bInsert;
2376 if ( mpSubEdit )
2377 mpSubEdit->SetInsertMode( bInsert );
2378 else
2379 ImplShowCursor();
2380 }
2381}
2382
2383bool Edit::IsInsertMode() const
2384{
2385 if ( mpSubEdit )
2386 return mpSubEdit->IsInsertMode();
2387 else
2388 return mbInsertMode;
2389}
2390
2391void Edit::SetMaxTextLen(sal_Int32 nMaxLen)
2392{
2393 mnMaxTextLen = nMaxLen > 0 ? nMaxLen : EDIT_NOLIMIT((sal_Int32) 0x7FFFFFFF);
2394
2395 if ( mpSubEdit )
2396 mpSubEdit->SetMaxTextLen( mnMaxTextLen );
2397 else
2398 {
2399 if ( maText.getLength() > mnMaxTextLen )
2400 ImplDelete( Selection( mnMaxTextLen, maText.getLength() ), EDIT_DEL_RIGHT2, EDIT_DELMODE_SIMPLE11 );
2401 }
2402}
2403
2404void Edit::SetSelection( const Selection& rSelection )
2405{
2406 // If the selection was changed from outside, e.g. by MouseButtonDown, don't call Tracking()
2407 // directly afterwards which would change the selection again
2408 if ( IsTracking() )
2409 EndTracking();
2410 else if ( mpSubEdit && mpSubEdit->IsTracking() )
2411 mpSubEdit->EndTracking();
2412
2413 ImplSetSelection( rSelection );
2414}
2415
2416void Edit::ImplSetSelection( const Selection& rSelection, bool bPaint )
2417{
2418 if ( mpSubEdit )
2419 mpSubEdit->ImplSetSelection( rSelection );
2420 else
2421 {
2422 if ( rSelection != maSelection )
2423 {
2424 Selection aOld( maSelection );
2425 Selection aNew( rSelection );
2426
2427 if ( aNew.Min() > maText.getLength() )
2428 aNew.Min() = maText.getLength();
2429 if ( aNew.Max() > maText.getLength() )
2430 aNew.Max() = maText.getLength();
2431 if ( aNew.Min() < 0 )
2432 aNew.Min() = 0;
2433 if ( aNew.Max() < 0 )
2434 aNew.Max() = 0;
2435
2436 if ( aNew != maSelection )
2437 {
2438 ImplClearLayoutData();
2439 Selection aTemp = maSelection;
2440 maSelection = aNew;
2441
2442 if ( bPaint && ( aOld.Len() || aNew.Len() || IsPaintTransparent() ) )
2443 ImplInvalidateOrRepaint();
2444 ImplShowCursor();
2445
2446 bool bCaret = false, bSelection = false;
2447 long nB=aNew.Max(), nA=aNew.Min(),oB=aTemp.Max(), oA=aTemp.Min();
2448 long nGap = nB-nA, oGap = oB-oA;
2449 if (nB != oB)
2450 bCaret = true;
2451 if (nGap != 0 || oGap != 0)
2452 bSelection = true;
2453
2454 if (bSelection)
2455 {
2456 if ( mbIsSubEdit )
2457 static_cast<Edit*>(GetParent())->CallEventListeners( VclEventId::EditSelectionChanged );
2458 else
2459 CallEventListeners( VclEventId::EditSelectionChanged );
2460 }
2461
2462 if (bCaret)
2463 {
2464 if ( mbIsSubEdit )
2465 static_cast<Edit*>(GetParent())->CallEventListeners( VclEventId::EditCaretChanged );
2466 else
2467 CallEventListeners( VclEventId::EditCaretChanged );
2468 }
2469
2470 // #103511# notify combobox listeners of deselection
2471 if( !maSelection && GetParent() && GetParent()->GetType() == WindowType::COMBOBOX )
2472 static_cast<Edit*>(GetParent())->CallEventListeners( VclEventId::ComboboxDeselect );
2473 }
2474 }
2475 }
2476}
2477
2478const Selection& Edit::GetSelection() const
2479{
2480 if ( mpSubEdit )
2481 return mpSubEdit->GetSelection();
2482 else
2483 return maSelection;
2484}
2485
2486void Edit::ReplaceSelected( const OUString& rStr )
2487{
2488 if ( mpSubEdit )
2489 mpSubEdit->ReplaceSelected( rStr );
2490 else
2491 ImplInsertText( rStr );
2492}
2493
2494void Edit::DeleteSelected()
2495{
2496 if ( mpSubEdit )
2497 mpSubEdit->DeleteSelected();
2498 else
2499 {
2500 if ( maSelection.Len() )
2501 ImplDelete( maSelection, EDIT_DEL_RIGHT2, EDIT_DELMODE_SIMPLE11 );
2502 }
2503}
2504
2505OUString Edit::GetSelected() const
2506{
2507 if ( mpSubEdit )
2508 return mpSubEdit->GetSelected();
2509 else
2510 {
2511 Selection aSelection( maSelection );
2512 aSelection.Justify();
2513 return OUString( maText.getStr() + aSelection.Min(), aSelection.Len() );
2514 }
2515}
2516
2517void Edit::Cut()
2518{
2519 if ( !mbPassword )
2520 {
2521 Copy();
2522 ReplaceSelected( OUString() );
2523 }
2524}
2525
2526void Edit::Copy()
2527{
2528 if ( !mbPassword )
2529 {
2530 css::uno::Reference<css::datatransfer::clipboard::XClipboard> aClipboard(GetClipboard());
2531 ImplCopy( aClipboard );
2532 }
2533}
2534
2535void Edit::Paste()
2536{
2537 css::uno::Reference<css::datatransfer::clipboard::XClipboard> aClipboard(GetClipboard());
2538 ImplPaste( aClipboard );
2539}
2540
2541void Edit::Undo()
2542{
2543 if ( mpSubEdit )
2544 mpSubEdit->Undo();
2545 else
2546 {
2547 const OUString aText( maText.toString() );
2548 ImplDelete( Selection( 0, aText.getLength() ), EDIT_DEL_RIGHT2, EDIT_DELMODE_SIMPLE11 );
2549 ImplInsertText( maUndoText );
2550 ImplSetSelection( Selection( 0, maUndoText.getLength() ) );
2551 maUndoText = aText;
2552 }
2553}
2554
2555void Edit::SetText( const OUString& rStr )
2556{
2557 if ( mpSubEdit )
2558 mpSubEdit->SetText( rStr ); // not directly ImplSetText if SetText overridden
2559 else
2560 {
2561 Selection aNewSel( 0, 0 ); // prevent scrolling
2562 ImplSetText( rStr, &aNewSel );
2563 }
2564}
2565
2566void Edit::SetText( const OUString& rStr, const Selection& rSelection )
2567{
2568 if ( mpSubEdit )
2569 mpSubEdit->SetText( rStr, rSelection );
2570 else
2571 ImplSetText( rStr, &rSelection );
2572}
2573
2574OUString Edit::GetText() const
2575{
2576 if ( mpSubEdit )
2577 return mpSubEdit->GetText();
2578 else
2579 return maText.toString();
2580}
2581
2582void Edit::SetCursorAtLast(){
2583 ImplSetCursorPos( GetText().getLength(), false );
2584}
2585
2586void Edit::SetPlaceholderText( const OUString& rStr )
2587{
2588 if ( mpSubEdit )
2589 mpSubEdit->SetPlaceholderText( rStr );
2590 else if ( maPlaceholderText != rStr )
2591 {
2592 maPlaceholderText = rStr;
2593 if ( GetText().isEmpty() )
2594 Invalidate();
2595 }
2596}
2597
2598void Edit::SetModifyFlag()
2599{
2600}
2601
2602void Edit::SetSubEdit(Edit* pEdit)
2603{
2604 mpSubEdit.disposeAndClear();
1
Calling 'VclPtr::disposeAndClear'
2605 mpSubEdit.set(pEdit);
2606
2607 if (mpSubEdit)
2608 {
2609 SetPointer(PointerStyle::Arrow); // Only SubEdit has the BEAM...
2610 mpSubEdit->mbIsSubEdit = true;
2611
2612 mpSubEdit->SetReadOnly(mbReadOnly);
2613 mpSubEdit->maAutocompleteHdl = maAutocompleteHdl;
2614 }
2615}
2616
2617Size Edit::CalcMinimumSizeForText(const OUString &rString) const
2618{
2619 ControlType eCtrlType = ImplGetNativeControlType();
2620
2621 Size aSize;
2622 if (mnWidthInChars != -1)
2623 {
2624 //CalcSize calls CalcWindowSize, but we will call that also in this
2625 //function, so undo the first one with CalcOutputSize
2626 aSize = CalcOutputSize(CalcSize(mnWidthInChars));
2627 }
2628 else
2629 {
2630 OUString aString;
2631 if (mnMaxWidthChars != -1 && mnMaxWidthChars < rString.getLength())
2632 aString = rString.copy(0, mnMaxWidthChars);
2633 else
2634 aString = rString;
2635
2636 aSize.setHeight( GetTextHeight() );
2637 aSize.setWidth( GetTextWidth(aString) );
2638 aSize.AdjustWidth(ImplGetExtraXOffset() * 2 );
2639
2640 // do not create edit fields in which one cannot enter anything
2641 // a default minimum width should exist for at least 3 characters
2642
2643 //CalcSize calls CalcWindowSize, but we will call that also in this
2644 //function, so undo the first one with CalcOutputSize
2645 Size aMinSize(CalcOutputSize(CalcSize(3)));
2646 if (aSize.Width() < aMinSize.Width())
2647 aSize.setWidth( aMinSize.Width() );
2648 }
2649
2650 aSize.AdjustHeight(ImplGetExtraYOffset() * 2 );
2651
2652 aSize = CalcWindowSize( aSize );
2653
2654 // ask NWF what if it has an opinion, too
2655 ImplControlValue aControlValue;
2656 tools::Rectangle aRect( Point( 0, 0 ), aSize );
2657 tools::Rectangle aContent, aBound;
2658 if (GetNativeControlRegion(eCtrlType, ControlPart::Entire, aRect, ControlState::NONE,
2659 aControlValue, aBound, aContent))
2660 {
2661 if (aBound.GetHeight() > aSize.Height())
2662 aSize.setHeight( aBound.GetHeight() );
2663 }
2664 return aSize;
2665}
2666
2667Size Edit::CalcMinimumSize() const
2668{
2669 return CalcMinimumSizeForText(GetText());
2670}
2671
2672Size Edit::GetOptimalSize() const
2673{
2674 return CalcMinimumSize();
2675}
2676
2677Size Edit::CalcSize(sal_Int32 nChars) const
2678{
2679 // width for N characters, independent from content.
2680 // works only correct for fixed fonts, average otherwise
2681 Size aSz( GetTextWidth( "x" ), GetTextHeight() );
2682 aSz.setWidth( aSz.Width() * nChars );
2683 aSz.AdjustWidth(ImplGetExtraXOffset() * 2 );
2684 aSz = CalcWindowSize( aSz );
2685 return aSz;
2686}
2687
2688sal_Int32 Edit::GetMaxVisChars() const
2689{
2690 const vcl::Window* pW = mpSubEdit ? mpSubEdit : this;
2691 sal_Int32 nOutWidth = pW->GetOutputSizePixel().Width();
2692 sal_Int32 nCharWidth = GetTextWidth( "x" );
2693 return nCharWidth ? nOutWidth/nCharWidth : 0;
2694}
2695
2696namespace vcl
2697{
2698 void SetGetSpecialCharsFunction( FncGetSpecialChars fn )
2699 {
2700 pImplFncGetSpecialChars = fn;
2701 }
2702
2703 FncGetSpecialChars GetGetSpecialCharsFunction()
2704 {
2705 return pImplFncGetSpecialChars;
2706 }
2707}
2708
2709VclPtr<PopupMenu> Edit::CreatePopupMenu()
2710{
2711 if (!mpUIBuilder)
2712 mpUIBuilder.reset(new VclBuilder(nullptr, AllSettings::GetUIRootDir(), "vcl/ui/editmenu.ui", ""));
2713 VclPtr<PopupMenu> pPopup = mpUIBuilder->get_menu("menu");
2714 const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
2715 if (rStyleSettings.GetHideDisabledMenuItems())
2716 pPopup->SetMenuFlags( MenuFlags::HideDisabledEntries );
2717 else
2718 pPopup->SetMenuFlags ( MenuFlags::AlwaysShowDisabledEntries );
2719 if (rStyleSettings.GetContextMenuShortcuts())
2720 {
2721 pPopup->SetAccelKey(pPopup->GetItemId("undo"), vcl::KeyCode( KeyFuncType::UNDO));
2722 pPopup->SetAccelKey(pPopup->GetItemId("cut"), vcl::KeyCode( KeyFuncType::CUT));
2723 pPopup->SetAccelKey(pPopup->GetItemId("copy"), vcl::KeyCode( KeyFuncType::COPY));
2724 pPopup->SetAccelKey(pPopup->GetItemId("paste"), vcl::KeyCode( KeyFuncType::PASTE));
2725 pPopup->SetAccelKey(pPopup->GetItemId("delete"), vcl::KeyCode( KeyFuncType::DELETE));
2726 pPopup->SetAccelKey(pPopup->GetItemId("selectall"), vcl::KeyCode( KEY_A, false, true, false, false));
2727 pPopup->SetAccelKey(pPopup->GetItemId("specialchar"), vcl::KeyCode( KEY_S, true, true, false, false));
2728 }
2729 return pPopup;
2730}
2731
2732// css::datatransfer::dnd::XDragGestureListener
2733void Edit::dragGestureRecognized( const css::datatransfer::dnd::DragGestureEvent& rDGE )
2734{
2735 SolarMutexGuard aVclGuard;
2736
2737 if ( !(!IsTracking() && maSelection.Len() &&
2738 !mbPassword && (!mpDDInfo || !mpDDInfo->bStarterOfDD)) ) // no repeated D&D
2739 return;
2740
2741 Selection aSel( maSelection );
2742 aSel.Justify();
2743
2744 // only if mouse in the selection...
2745 Point aMousePos( rDGE.DragOriginX, rDGE.DragOriginY );
2746 sal_Int32 nCharPos = ImplGetCharPos( aMousePos );
2747 if ( (nCharPos < aSel.Min()) || (nCharPos >= aSel.Max()) )
2748 return;
2749
2750 if ( !mpDDInfo )
2751 mpDDInfo.reset(new DDInfo);
2752
2753 mpDDInfo->bStarterOfDD = true;
2754 mpDDInfo->aDndStartSel = aSel;
2755
2756 if ( IsTracking() )
2757 EndTracking(); // before D&D disable tracking
2758
2759 vcl::unohelper::TextDataObject* pDataObj = new vcl::unohelper::TextDataObject( GetSelected() );
2760 sal_Int8 nActions = datatransfer::dnd::DNDConstants::ACTION_COPY;
2761 if ( !IsReadOnly() )
2762 nActions |= datatransfer::dnd::DNDConstants::ACTION_MOVE;
2763 rDGE.DragSource->startDrag( rDGE, nActions, 0 /*cursor*/, 0 /*image*/, pDataObj, mxDnDListener );
2764 if ( GetCursor() )
2765 GetCursor()->Hide();
2766}
2767
2768// css::datatransfer::dnd::XDragSourceListener
2769void Edit::dragDropEnd( const css::datatransfer::dnd::DragSourceDropEvent& rDSDE )
2770{
2771 SolarMutexGuard aVclGuard;
2772
2773 if (rDSDE.DropSuccess && (rDSDE.DropAction & datatransfer::dnd::DNDConstants::ACTION_MOVE) && mpDDInfo)
2774 {
2775 Selection aSel( mpDDInfo->aDndStartSel );
2776 if ( mpDDInfo->bDroppedInMe )
2777 {
2778 if ( aSel.Max() > mpDDInfo->nDropPos )
2779 {
2780 long nLen = aSel.Len();
2781 aSel.Min() += nLen;
2782 aSel.Max() += nLen;
2783 }
2784 }
2785 ImplDelete( aSel, EDIT_DEL_RIGHT2, EDIT_DELMODE_SIMPLE11 );
2786 Modify();
2787 }
2788
2789 ImplHideDDCursor();
2790 mpDDInfo.reset();
2791}
2792
2793// css::datatransfer::dnd::XDropTargetListener
2794void Edit::drop( const css::datatransfer::dnd::DropTargetDropEvent& rDTDE )
2795{
2796 SolarMutexGuard aVclGuard;
2797
2798 bool bChanges = false;
2799 if ( !mbReadOnly && mpDDInfo )
2800 {
2801 ImplHideDDCursor();
2802
2803 Selection aSel( maSelection );
2804 aSel.Justify();
2805
2806 if ( aSel.Len() && !mpDDInfo->bStarterOfDD )
2807 ImplDelete( aSel, EDIT_DEL_RIGHT2, EDIT_DELMODE_SIMPLE11 );
2808
2809 mpDDInfo->bDroppedInMe = true;
2810
2811 aSel.Min() = mpDDInfo->nDropPos;
2812 aSel.Max() = mpDDInfo->nDropPos;
2813 ImplSetSelection( aSel );
2814
2815 uno::Reference< datatransfer::XTransferable > xDataObj = rDTDE.Transferable;
2816 if ( xDataObj.is() )
2817 {
2818 datatransfer::DataFlavor aFlavor;
2819 SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING, aFlavor );
2820 if ( xDataObj->isDataFlavorSupported( aFlavor ) )
2821 {
2822 uno::Any aData = xDataObj->getTransferData( aFlavor );
2823 OUString aText;
2824 aData >>= aText;
2825 ImplInsertText( aText );
2826 bChanges = true;
2827 Modify();
2828 }
2829 }
2830
2831 if ( !mpDDInfo->bStarterOfDD )
2832 {
2833 mpDDInfo.reset();
2834 }
2835 }
2836
2837 rDTDE.Context->dropComplete( bChanges );
2838}
2839
2840void Edit::dragEnter( const css::datatransfer::dnd::DropTargetDragEnterEvent& rDTDE )
2841{
2842 if ( !mpDDInfo )
2843 {
2844 mpDDInfo.reset(new DDInfo);
2845 }
2846 // search for string data type
2847 const Sequence< css::datatransfer::DataFlavor >& rFlavors( rDTDE.SupportedDataFlavors );
2848 mpDDInfo->bIsStringSupported = std::any_of(rFlavors.begin(), rFlavors.end(),
2849 [](const css::datatransfer::DataFlavor& rFlavor) {
2850 sal_Int32 nIndex = 0;
2851 const OUString aMimetype = rFlavor.MimeType.getToken( 0, ';', nIndex );
2852 return aMimetype == "text/plain";
2853 });
2854}
2855
2856void Edit::dragExit( const css::datatransfer::dnd::DropTargetEvent& )
2857{
2858 SolarMutexGuard aVclGuard;
2859
2860 ImplHideDDCursor();
2861}
2862
2863void Edit::dragOver( const css::datatransfer::dnd::DropTargetDragEvent& rDTDE )
2864{
2865 SolarMutexGuard aVclGuard;
2866
2867 Point aMousePos( rDTDE.LocationX, rDTDE.LocationY );
2868
2869 sal_Int32 nPrevDropPos = mpDDInfo->nDropPos;
2870 mpDDInfo->nDropPos = ImplGetCharPos( aMousePos );
2871
2872 /*
2873 Size aOutSize = GetOutputSizePixel();
2874 if ( ( aMousePos.X() < 0 ) || ( aMousePos.X() > aOutSize.Width() ) )
2875 {
2876 // Scroll?
2877 // No, I will not receive events in this case...
2878 }
2879 */
2880
2881 Selection aSel( maSelection );
2882 aSel.Justify();
2883
2884 // Don't accept drop in selection or read-only field...
2885 if ( IsReadOnly() || aSel.IsInside( mpDDInfo->nDropPos ) || ! mpDDInfo->bIsStringSupported )
2886 {
2887 ImplHideDDCursor();
2888 rDTDE.Context->rejectDrag();
2889 }
2890 else
2891 {
2892 // draw the old cursor away...
2893 if ( !mpDDInfo->bVisCursor || ( nPrevDropPos != mpDDInfo->nDropPos ) )
2894 {
2895 ImplHideDDCursor();
2896 ImplShowDDCursor();
2897 }
2898 rDTDE.Context->acceptDrag( rDTDE.DropAction );
2899 }
2900}
2901
2902OUString Edit::GetSurroundingText() const
2903{
2904 if (mpSubEdit)
2905 return mpSubEdit->GetSurroundingText();
2906 return maText.toString();
2907}
2908
2909Selection Edit::GetSurroundingTextSelection() const
2910{
2911 return GetSelection();
2912}
2913
2914FactoryFunction Edit::GetUITestFactory() const
2915{
2916 return EditUIObject::create;
2917}
2918
2919/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

/home/maarten/src/libreoffice/core/include/vcl/vclptr.hxx

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#ifndef INCLUDED_VCL_PTR_HXX
21#define INCLUDED_VCL_PTR_HXX
22
23#include <sal/config.h>
24
25#include <rtl/ref.hxx>
26
27#include <utility>
28#include <type_traits>
29
30#ifdef DBG_UTIL
31#ifndef _WIN32
32#include <vcl/vclmain.hxx>
33#endif
34#endif
35
36class VclReferenceBase;
37
38namespace vcl::detail {
39
40template<typename>
41constexpr bool isIncompleteOrDerivedFromVclReferenceBase(...) { return true; }
42
43template<typename T> constexpr bool isIncompleteOrDerivedFromVclReferenceBase(
44 int (*)[sizeof(T)])
45{ return std::is_base_of<VclReferenceBase, T>::value; }
46
47} // namespace vcl::detail
48
49/**
50 * A thin wrapper around rtl::Reference to implement the acquire and dispose semantics we want for references to vcl::Window subclasses.
51 *
52 * For more details on the design please see vcl/README.lifecycle
53 *
54 * @param reference_type must be a subclass of vcl::Window
55 */
56template <class reference_type>
57class VclPtr
58{
59 static_assert(
60 vcl::detail::isIncompleteOrDerivedFromVclReferenceBase<reference_type>(
61 nullptr),
62 "template argument type must be derived from VclReferenceBase");
63
64 ::rtl::Reference<reference_type> m_rInnerRef;
65
66public:
67 /** Constructor...
68 */
69 VclPtr()
70 : m_rInnerRef()
71 {}
72
73 /** Constructor...
74 */
75 VclPtr (reference_type * pBody)
76 : m_rInnerRef(pBody)
77 {}
78
79 /** Constructor... that doesn't take a ref.
80 */
81 VclPtr (reference_type * pBody, __sal_NoAcquire)
82 : m_rInnerRef(pBody, SAL_NO_ACQUIRE)
83 {}
84
85 /** Up-casting conversion constructor: Copies interface reference.
86
87 Does not work for up-casts to ambiguous bases. For the special case of
88 up-casting to Reference< XInterface >, see the corresponding conversion
89 operator.
90
91 @param rRef another reference
92 */
93 template< class derived_type >
94 VclPtr(
95 const VclPtr< derived_type > & rRef,
96 typename std::enable_if<
97 std::is_base_of<reference_type, derived_type>::value, int>::type
98 = 0 )
99 : m_rInnerRef( static_cast<reference_type*>(rRef) )
100 {
101 }
102
103#if defined(DBG_UTIL) && !defined(_WIN32)
104 virtual ~VclPtr()
105 {
106 assert(m_rInnerRef.get() == nullptr || vclmain::isAlive())(static_cast <bool> (m_rInnerRef.get() == nullptr || vclmain
::isAlive()) ? void (0) : __assert_fail ("m_rInnerRef.get() == nullptr || vclmain::isAlive()"
, "/home/maarten/src/libreoffice/core/include/vcl/vclptr.hxx"
, 106, __extension__ __PRETTY_FUNCTION__))
;
107 // We can be one of the intermediate counts, but if we are the last
108 // VclPtr keeping this object alive, then something forgot to call dispose().
109 assert((!m_rInnerRef.get() || m_rInnerRef->isDisposed() || m_rInnerRef->getRefCount() > 1)(static_cast <bool> ((!m_rInnerRef.get() || m_rInnerRef
->isDisposed() || m_rInnerRef->getRefCount() > 1) &&
"someone forgot to call dispose()") ? void (0) : __assert_fail
("(!m_rInnerRef.get() || m_rInnerRef->isDisposed() || m_rInnerRef->getRefCount() > 1) && \"someone forgot to call dispose()\""
, "/home/maarten/src/libreoffice/core/include/vcl/vclptr.hxx"
, 110, __extension__ __PRETTY_FUNCTION__))
110 && "someone forgot to call dispose()")(static_cast <bool> ((!m_rInnerRef.get() || m_rInnerRef
->isDisposed() || m_rInnerRef->getRefCount() > 1) &&
"someone forgot to call dispose()") ? void (0) : __assert_fail
("(!m_rInnerRef.get() || m_rInnerRef->isDisposed() || m_rInnerRef->getRefCount() > 1) && \"someone forgot to call dispose()\""
, "/home/maarten/src/libreoffice/core/include/vcl/vclptr.hxx"
, 110, __extension__ __PRETTY_FUNCTION__))
;
111 }
112 VclPtr(VclPtr const &) = default;
113 VclPtr(VclPtr &&) = default;
114 VclPtr & operator =(VclPtr const &) = default;
115 VclPtr & operator =(VclPtr &&) = default;
116#endif
117
118 /**
119 * A construction helper for VclPtr. Since VclPtr types are created
120 * with a reference-count of one - to help fit into the existing
121 * code-flow; this helps us to construct them easily.
122 *
123 * For more details on the design please see vcl/README.lifecycle
124 *
125 * @tparam reference_type must be a subclass of vcl::Window
126 */
127 template<typename... Arg> [[nodiscard]] static VclPtr< reference_type > Create(Arg &&... arg)
128 {
129 return VclPtr< reference_type >( new reference_type(std::forward<Arg>(arg)...), SAL_NO_ACQUIRE );
130 }
131
132 /** Probably most common used: handle->someBodyOp().
133 */
134 reference_type * operator->() const
135 {
136 return m_rInnerRef.get();
137 }
138
139 /** Get the body. Can be used instead of operator->().
140 I.e. handle->someBodyOp() and handle.get()->someBodyOp()
141 are the same.
142 */
143 reference_type * get() const
144 {
145 return m_rInnerRef.get();
146 }
147
148 void set(reference_type *pBody)
149 {
150 m_rInnerRef.set(pBody);
151 }
152
153 void reset(reference_type *pBody)
154 {
155 m_rInnerRef.set(pBody);
156 }
157
158 /** Up-casting copy assignment operator.
159
160 Does not work for up-casts to ambiguous bases.
161
162 @param rRef another reference
163 */
164 template<typename derived_type>
165 typename std::enable_if<
166 std::is_base_of<reference_type, derived_type>::value,
167 VclPtr &>::type
168 operator =(VclPtr<derived_type> const & rRef)
169 {
170 m_rInnerRef.set(rRef.get());
171 return *this;
172 }
173
174 VclPtr & operator =(reference_type * pBody)
175 {
176 m_rInnerRef.set(pBody);
177 return *this;
178 }
179
180 operator reference_type * () const
181 {
182 return m_rInnerRef.get();
183 }
184
185 explicit operator bool () const
186 {
187 return m_rInnerRef.get() != nullptr;
188 }
189
190 void clear()
191 {
192 m_rInnerRef.clear();
193 }
194
195 void reset()
196 {
197 m_rInnerRef.clear();
198 }
199
200 void disposeAndClear()
201 {
202 // hold it alive for the lifetime of this method
203 ::rtl::Reference<reference_type> aTmp(m_rInnerRef);
2
Calling copy constructor for 'Reference<Edit>'
5
Returning from copy constructor for 'Reference<Edit>'
204 m_rInnerRef.clear(); // we should use some 'swap' method ideally ;-)
6
Calling 'Reference::clear'
13
Returning; memory was released
205 if (aTmp.get()) {
14
Calling 'Reference::get'
206 aTmp->disposeOnce();
207 }
208 }
209
210 /** Needed to place VclPtr's into STL collection.
211 */
212 bool operator< (const VclPtr<reference_type> & handle) const
213 {
214 return (m_rInnerRef < handle.m_rInnerRef);
215 }
216}; // class VclPtr
217
218template<typename T1, typename T2>
219inline bool operator ==(VclPtr<T1> const & p1, VclPtr<T2> const & p2) {
220 return p1.get() == p2.get();
221}
222
223template<typename T> inline bool operator ==(VclPtr<T> const & p1, T const * p2)
224{
225 return p1.get() == p2;
226}
227
228template<typename T> inline bool operator ==(VclPtr<T> const & p1, T * p2) {
229 return p1.get() == p2;
230}
231
232template<typename T> inline bool operator ==(T const * p1, VclPtr<T> const & p2)
233{
234 return p1 == p2.get();
235}
236
237template<typename T> inline bool operator ==(T * p1, VclPtr<T> const & p2) {
238 return p1 == p2.get();
239}
240
241template<typename T1, typename T2>
242inline bool operator !=(VclPtr<T1> const & p1, VclPtr<T2> const & p2) {
243 return !(p1 == p2);
244}
245
246template<typename T> inline bool operator !=(VclPtr<T> const & p1, T const * p2)
247{
248 return !(p1 == p2);
249}
250
251template<typename T> inline bool operator !=(VclPtr<T> const & p1, T * p2) {
252 return !(p1 == p2);
253}
254
255template<typename T> inline bool operator !=(T const * p1, VclPtr<T> const & p2)
256{
257 return !(p1 == p2);
258}
259
260template<typename T> inline bool operator !=(T * p1, VclPtr<T> const & p2) {
261 return !(p1 == p2);
262}
263
264/**
265 * A construction helper for a temporary VclPtr. Since VclPtr types
266 * are created with a reference-count of one - to help fit into
267 * the existing code-flow; this helps us to construct them easily.
268 * see also VclPtr::Create and ScopedVclPtr
269 *
270 * For more details on the design please see vcl/README.lifecycle
271 *
272 * @param reference_type must be a subclass of vcl::Window
273 */
274template <class reference_type>
275class SAL_WARN_UNUSED__attribute__((warn_unused)) VclPtrInstance final : public VclPtr<reference_type>
276{
277public:
278 template<typename... Arg> VclPtrInstance(Arg &&... arg)
279 : VclPtr<reference_type>( new reference_type(std::forward<Arg>(arg)...), SAL_NO_ACQUIRE )
280 {
281 }
282
283 /**
284 * Override and disallow this, to prevent people accidentally calling it and actually
285 * getting VclPtr::Create and getting a naked VclPtr<> instance
286 */
287 template<typename... Arg> static VclPtrInstance< reference_type > Create(Arg &&... ) = delete;
288};
289
290template <class reference_type>
291class ScopedVclPtr : public VclPtr<reference_type>
292{
293public:
294 /** Constructor...
295 */
296 ScopedVclPtr()
297 : VclPtr<reference_type>()
298 {}
299
300 /** Constructor
301 */
302 ScopedVclPtr (reference_type * pBody)
303 : VclPtr<reference_type>(pBody)
304 {}
305
306 /** Copy constructor...
307 */
308 ScopedVclPtr (const VclPtr<reference_type> & handle)
309 : VclPtr<reference_type>(handle)
310 {}
311
312 /**
313 Assignment that releases the last reference.
314 */
315 void disposeAndReset(reference_type *pBody)
316 {
317 if (pBody != this->get()) {
318 VclPtr<reference_type>::disposeAndClear();
319 VclPtr<reference_type>::set(pBody);
320 }
321 }
322
323 /**
324 Assignment that releases the last reference.
325 */
326 ScopedVclPtr<reference_type>& operator = (reference_type * pBody)
327 {
328 disposeAndReset(pBody);
329 return *this;
330 }
331
332 /** Up-casting conversion constructor: Copies interface reference.
333
334 Does not work for up-casts to ambiguous bases. For the special case of
335 up-casting to Reference< XInterface >, see the corresponding conversion
336 operator.
337
338 @param rRef another reference
339 */
340 template< class derived_type >
341 ScopedVclPtr(
342 const VclPtr< derived_type > & rRef,
343 typename std::enable_if<
344 std::is_base_of<reference_type, derived_type>::value, int>::type
345 = 0 )
346 : VclPtr<reference_type>( rRef )
347 {
348 }
349
350 /** Up-casting assignment operator.
351
352 Does not work for up-casts to ambiguous bases.
353
354 @param rRef another VclPtr
355 */
356 template<typename derived_type>
357 typename std::enable_if<
358 std::is_base_of<reference_type, derived_type>::value,
359 ScopedVclPtr &>::type
360 operator =(VclPtr<derived_type> const & rRef)
361 {
362 disposeAndReset(rRef.get());
363 return *this;
364 }
365
366 /**
367 * Override and disallow this, to prevent people accidentally calling it and actually
368 * getting VclPtr::Create and getting a naked VclPtr<> instance
369 */
370 template<typename... Arg> static ScopedVclPtr< reference_type > Create(Arg &&... ) = delete;
371
372 ~ScopedVclPtr()
373 {
374 VclPtr<reference_type>::disposeAndClear();
375 assert(VclPtr<reference_type>::get() == nullptr)(static_cast <bool> (VclPtr<reference_type>::get(
) == nullptr) ? void (0) : __assert_fail ("VclPtr<reference_type>::get() == nullptr"
, "/home/maarten/src/libreoffice/core/include/vcl/vclptr.hxx"
, 375, __extension__ __PRETTY_FUNCTION__))
; // make sure there are no lingering references
376 }
377
378private:
379 // Most likely we don't want this default copy-constructor.
380 ScopedVclPtr (const ScopedVclPtr<reference_type> &) = delete;
381 // And certainly we don't want a default assignment operator.
382 ScopedVclPtr<reference_type>& operator = (const ScopedVclPtr<reference_type> &) = delete;
383 // And disallow reset as that doesn't call disposeAndClear on the original reference
384 void reset() = delete;
385 void reset(reference_type *pBody) = delete;
386
387protected:
388 ScopedVclPtr (reference_type * pBody, __sal_NoAcquire)
389 : VclPtr<reference_type>(pBody, SAL_NO_ACQUIRE)
390 {}
391};
392
393/**
394 * A construction helper for ScopedVclPtr. Since VclPtr types are created
395 * with a reference-count of one - to help fit into the existing
396 * code-flow; this helps us to construct them easily.
397 *
398 * For more details on the design please see vcl/README.lifecycle
399 *
400 * @param reference_type must be a subclass of vcl::Window
401 */
402#if defined _MSC_VER
403#pragma warning(push)
404#pragma warning(disable: 4521) // " multiple copy constructors specified"
405#endif
406template <class reference_type>
407class SAL_WARN_UNUSED__attribute__((warn_unused)) ScopedVclPtrInstance final : public ScopedVclPtr<reference_type>
408{
409public:
410 template<typename... Arg> ScopedVclPtrInstance(Arg &&... arg)
411 : ScopedVclPtr<reference_type>( new reference_type(std::forward<Arg>(arg)...), SAL_NO_ACQUIRE )
412 {
413 }
414
415 /**
416 * Override and disallow this, to prevent people accidentally calling it and actually
417 * getting VclPtr::Create and getting a naked VclPtr<> instance
418 */
419 template<typename... Arg> static ScopedVclPtrInstance< reference_type > Create(Arg &&...) = delete;
420
421private:
422 // Prevent the above perfect forwarding ctor from hijacking (accidental)
423 // attempts at ScopedVclPtrInstance copy construction (where the hijacking
424 // would typically lead to somewhat obscure error messages); both non-const
425 // and const variants are needed here, as the ScopedVclPtr base class has a
426 // const--variant copy ctor, so the implicitly declared copy ctor for
427 // ScopedVclPtrInstance would also be the const variant, so non-const copy
428 // construction attempts would be hijacked by the perfect forwarding ctor;
429 // but if we only declared a non-const variant here, the const variant would
430 // no longer be implicitly declared (as there would already be an explicitly
431 // declared copy ctor), so const copy construction attempts would then be
432 // hijacked by the perfect forwarding ctor:
433 ScopedVclPtrInstance(ScopedVclPtrInstance &) = delete;
434 ScopedVclPtrInstance(ScopedVclPtrInstance const &) = delete;
435};
436#if defined _MSC_VER
437#pragma warning(pop)
438#endif
439
440#endif // INCLUDED_VCL_PTR_HXX
441
442/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

/home/maarten/src/libreoffice/core/include/rtl/ref.hxx

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#ifndef INCLUDED_RTL_REF_HXX
21#define INCLUDED_RTL_REF_HXX
22
23#include "sal/config.h"
24
25#include <cassert>
26#include <cstddef>
27#include <functional>
28#ifdef LIBO_INTERNAL_ONLY1
29#include <type_traits>
30#endif
31
32#include "sal/types.h"
33
34namespace rtl
35{
36
37/** Template reference class for reference type.
38*/
39template <class reference_type>
40class Reference
41{
42 /** The <b>reference_type</b> body pointer.
43 */
44 reference_type * m_pBody;
45
46
47public:
48 /** Constructor...
49 */
50 Reference()
51 : m_pBody (NULL__null)
52 {}
53
54
55 /** Constructor...
56 */
57 Reference (reference_type * pBody, __sal_NoAcquire)
58 : m_pBody (pBody)
59 {
60 }
61
62 /** Constructor...
63 */
64 Reference (reference_type * pBody)
65 : m_pBody (pBody)
66 {
67 if (m_pBody)
68 m_pBody->acquire();
69 }
70
71 /** Copy constructor...
72 */
73 Reference (const Reference<reference_type> & handle)
74 : m_pBody (handle.m_pBody)
75 {
76 if (m_pBody)
3
Assuming field 'm_pBody' is non-null
4
Taking true branch
77 m_pBody->acquire();
78 }
79
80#ifdef LIBO_INTERNAL_ONLY1
81 /** Move constructor...
82 */
83 Reference (Reference<reference_type> && handle) noexcept
84 : m_pBody (handle.m_pBody)
85 {
86 handle.m_pBody = nullptr;
87 }
88#endif
89
90#if defined LIBO_INTERNAL_ONLY1
91 /** Up-casting conversion constructor: Copies interface reference.
92
93 Does not work for up-casts to ambiguous bases.
94
95 @param rRef another reference
96 */
97 template< class derived_type >
98 inline Reference(
99 const Reference< derived_type > & rRef,
100 std::enable_if_t<std::is_base_of_v<reference_type, derived_type>, int> = 0 )
101 : m_pBody (rRef.get())
102 {
103 if (m_pBody)
104 m_pBody->acquire();
105 }
106#endif
107
108 /** Destructor...
109 */
110 ~Reference() COVERITY_NOEXCEPT_FALSE
111 {
112 if (m_pBody)
113 m_pBody->release();
114 }
115
116 /** Set...
117 Similar to assignment.
118 */
119 Reference<reference_type> &
120 SAL_CALL set (reference_type * pBody)
121 {
122 if (pBody)
123 pBody->acquire();
124 reference_type * const pOld = m_pBody;
125 m_pBody = pBody;
126 if (pOld)
127 pOld->release();
128 return *this;
129 }
130
131 /** Assignment.
132 Unbinds this instance from its body (if bound) and
133 bind it to the body represented by the handle.
134 */
135 Reference<reference_type> &
136 SAL_CALL operator= (const Reference<reference_type> & handle)
137 {
138 return set( handle.m_pBody );
139 }
140
141#ifdef LIBO_INTERNAL_ONLY1
142 /** Assignment.
143 * Unbinds this instance from its body (if bound),
144 * bind it to the body represented by the handle, and
145 * set the body represented by the handle to nullptr.
146 */
147 Reference<reference_type> &
148 operator= (Reference<reference_type> && handle)
149 {
150 // self-movement guts ourself
151 if (m_pBody)
152 m_pBody->release();
153 m_pBody = handle.m_pBody;
154 handle.m_pBody = nullptr;
155 return *this;
156 }
157#endif
158
159 /** Assignment...
160 */
161 Reference<reference_type> &
162 SAL_CALL operator= (reference_type * pBody)
163 {
164 return set( pBody );
165 }
166
167 /** Unbind the body from this handle.
168 Note that for a handle representing a large body,
169 "handle.clear().set(new body());" _might_
170 perform a little bit better than "handle.set(new body());",
171 since in the second case two large objects exist in memory
172 (the old body and the new body).
173 */
174 Reference<reference_type> & SAL_CALL clear()
175 {
176 if (m_pBody
6.1
Field 'm_pBody' is non-null
6.1
Field 'm_pBody' is non-null
6.1
Field 'm_pBody' is non-null
6.1
Field 'm_pBody' is non-null
)
7
Taking true branch
177 {
178 reference_type * const pOld = m_pBody;
179 m_pBody = NULL__null;
180 pOld->release();
8
Calling 'VclReferenceBase::release'
12
Returning; memory was released
181 }
182 return *this;
183 }
184
185
186 /** Get the body. Can be used instead of operator->().
187 I.e. handle->someBodyOp() and handle.get()->someBodyOp()
188 are the same.
189 */
190 reference_type * SAL_CALL get() const
191 {
192 return m_pBody;
15
Use of memory after it is freed
193 }
194
195
196 /** Probably most common used: handle->someBodyOp().
197 */
198 reference_type * SAL_CALL operator->() const
199 {
200 assert(m_pBody != NULL)(static_cast <bool> (m_pBody != __null) ? void (0) : __assert_fail
("m_pBody != NULL", "/home/maarten/src/libreoffice/core/include/rtl/ref.hxx"
, 200, __extension__ __PRETTY_FUNCTION__))
;
201 return m_pBody;
202 }
203
204
205 /** Allows (*handle).someBodyOp().
206 */
207 reference_type & SAL_CALL operator*() const
208 {
209 assert(m_pBody != NULL)(static_cast <bool> (m_pBody != __null) ? void (0) : __assert_fail
("m_pBody != NULL", "/home/maarten/src/libreoffice/core/include/rtl/ref.hxx"
, 209, __extension__ __PRETTY_FUNCTION__))
;
210 return *m_pBody;
211 }
212
213
214 /** Returns True if the handle does point to a valid body.
215 */
216 bool SAL_CALL is() const
217 {
218 return (m_pBody != NULL__null);
219 }
220
221#if defined LIBO_INTERNAL_ONLY1
222 /** Returns True if the handle does point to a valid body.
223 */
224 explicit operator bool() const
225 {
226 return is();
227 }
228#endif
229
230 /** Returns True if this points to pBody.
231 */
232 bool SAL_CALL operator== (const reference_type * pBody) const
233 {
234 return (m_pBody == pBody);
235 }
236
237
238 /** Returns True if handle points to the same body.
239 */
240 bool
241 SAL_CALL operator== (const Reference<reference_type> & handle) const
242 {
243 return (m_pBody == handle.m_pBody);
244 }
245
246
247 /** Needed to place References into STL collection.
248 */
249 bool
250 SAL_CALL operator!= (const Reference<reference_type> & handle) const
251 {
252 return (m_pBody != handle.m_pBody);
253 }
254
255
256 /** Needed to place References into STL collection.
257 */
258 bool
259 SAL_CALL operator< (const Reference<reference_type> & handle) const
260 {
261 return (m_pBody < handle.m_pBody);
262 }
263
264
265 /** Needed to place References into STL collection.
266 */
267 bool
268 SAL_CALL operator> (const Reference<reference_type> & handle) const
269 {
270 return (m_pBody > handle.m_pBody);
271 }
272};
273
274} // namespace rtl
275
276#if defined LIBO_INTERNAL_ONLY1
277namespace std
278{
279
280/// @cond INTERNAL
281/**
282 Make rtl::Reference hashable by default for use in STL containers.
283
284 @since LibreOffice 6.3
285*/
286template<typename T>
287struct hash<::rtl::Reference<T>>
288{
289 std::size_t operator()(::rtl::Reference<T> const & s) const
290 { return std::size_t(s.get()); }
291};
292/// @endcond
293
294}
295
296#endif
297
298#endif /* ! INCLUDED_RTL_REF_HXX */
299
300/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

/home/maarten/src/libreoffice/core/include/vcl/vclreferencebase.hxx

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#ifndef INCLUDED_VCL_Reference_HXX
20#define INCLUDED_VCL_Reference_HXX
21
22#include <vcl/dllapi.h>
23#include <osl/interlck.h>
24
25class VCL_DLLPUBLIC__attribute__ ((visibility("default"))) VclReferenceBase
26{
27 mutable oslInterlockedCount mnRefCnt;
28
29 template<typename T> friend class VclPtr;
30
31public:
32 void acquire() const
33 {
34 osl_atomic_increment(&mnRefCnt)__sync_add_and_fetch((&mnRefCnt), 1);
35 }
36
37 void release() const
38 {
39 if (osl_atomic_decrement(&mnRefCnt)__sync_sub_and_fetch((&mnRefCnt), 1) == 0)
9
Assuming the condition is true
10
Taking true branch
40 delete this;
11
Memory is released
41 }
42#ifdef DBG_UTIL
43#ifndef _WIN32
44 sal_Int32 getRefCount() const { return mnRefCnt; }
45#endif
46#endif
47
48
49private:
50 VclReferenceBase(const VclReferenceBase&) = delete;
51 VclReferenceBase& operator=(const VclReferenceBase&) = delete;
52
53 bool mbDisposed : 1;
54
55protected:
56 VclReferenceBase();
57protected:
58 virtual ~VclReferenceBase();
59
60protected:
61 virtual void dispose();
62
63public:
64 void disposeOnce();
65 bool isDisposed() const { return mbDisposed; }
66
67};
68#endif