bump product version to 7.6.3.2-android
[LibreOffice.git] / vcl / source / control / edit.cxx
blob10c68412a627d00c6b512cfe5ef0e767bf009a19
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <utility>
21 #include <vcl/builder.hxx>
22 #include <vcl/event.hxx>
23 #include <vcl/cursor.hxx>
24 #include <vcl/menu.hxx>
25 #include <vcl/toolkit/edit.hxx>
26 #include <vcl/weld.hxx>
27 #include <vcl/specialchars.hxx>
28 #include <vcl/svapp.hxx>
29 #include <vcl/settings.hxx>
30 #include <vcl/transfer.hxx>
31 #include <vcl/uitest/uiobject.hxx>
32 #include <vcl/ptrstyle.hxx>
34 #include <window.h>
35 #include <svdata.hxx>
36 #include <strings.hrc>
38 #include <com/sun/star/i18n/BreakIterator.hpp>
39 #include <com/sun/star/i18n/CharacterIteratorMode.hpp>
40 #include <com/sun/star/i18n/WordType.hpp>
41 #include <com/sun/star/datatransfer/XTransferable.hpp>
42 #include <com/sun/star/datatransfer/clipboard/XClipboard.hpp>
44 #include <com/sun/star/datatransfer/dnd/DNDConstants.hpp>
45 #include <com/sun/star/datatransfer/dnd/XDragGestureRecognizer.hpp>
46 #include <com/sun/star/datatransfer/dnd/XDropTarget.hpp>
48 #include <com/sun/star/i18n/InputSequenceChecker.hpp>
49 #include <com/sun/star/i18n/InputSequenceCheckMode.hpp>
50 #include <com/sun/star/i18n/ScriptType.hpp>
52 #include <com/sun/star/uno/Any.hxx>
54 #include <comphelper/processfactory.hxx>
55 #include <comphelper/string.hxx>
57 #include <sot/exchange.hxx>
58 #include <sot/formats.hxx>
59 #include <sal/macros.h>
60 #include <sal/log.hxx>
62 #include <i18nlangtag/languagetag.hxx>
63 #include <vcl/unohelp2.hxx>
64 #include <o3tl/safeint.hxx>
65 #include <o3tl/string_view.hxx>
66 #include <officecfg/Office/Common.hxx>
67 #include <tools/json_writer.hxx>
69 #include <algorithm>
70 #include <memory>
71 #include <string_view>
73 using namespace ::com::sun::star;
74 using namespace ::com::sun::star::uno;
75 using namespace ::com::sun::star::lang;
77 // - Redo
78 // - if Tracking-Cancel recreate DefaultSelection
80 static FncGetSpecialChars pImplFncGetSpecialChars = nullptr;
82 #define EDIT_ALIGN_LEFT 1
83 #define EDIT_ALIGN_CENTER 2
84 #define EDIT_ALIGN_RIGHT 3
86 #define EDIT_DEL_LEFT 1
87 #define EDIT_DEL_RIGHT 2
89 #define EDIT_DELMODE_SIMPLE 11
90 #define EDIT_DELMODE_RESTOFWORD 12
91 #define EDIT_DELMODE_RESTOFCONTENT 13
93 struct DDInfo
95 vcl::Cursor aCursor;
96 Selection aDndStartSel;
97 sal_Int32 nDropPos;
98 bool bStarterOfDD;
99 bool bDroppedInMe;
100 bool bVisCursor;
101 bool bIsStringSupported;
103 DDInfo()
105 aCursor.SetStyle( CURSOR_SHADOW );
106 nDropPos = 0;
107 bStarterOfDD = false;
108 bDroppedInMe = false;
109 bVisCursor = false;
110 bIsStringSupported = false;
114 struct Impl_IMEInfos
116 OUString aOldTextAfterStartPos;
117 std::unique_ptr<ExtTextInputAttr[]>
118 pAttribs;
119 sal_Int32 nPos;
120 sal_Int32 nLen;
121 bool bCursor;
122 bool bWasCursorOverwrite;
124 Impl_IMEInfos(sal_Int32 nPos, OUString aOldTextAfterStartPos);
126 void CopyAttribs(const ExtTextInputAttr* pA, sal_Int32 nL);
127 void DestroyAttribs();
130 Impl_IMEInfos::Impl_IMEInfos(sal_Int32 nP, OUString _aOldTextAfterStartPos)
131 : aOldTextAfterStartPos(std::move(_aOldTextAfterStartPos)),
132 nPos(nP),
133 nLen(0),
134 bCursor(true),
135 bWasCursorOverwrite(false)
139 void Impl_IMEInfos::CopyAttribs(const ExtTextInputAttr* pA, sal_Int32 nL)
141 nLen = nL;
142 pAttribs.reset(new ExtTextInputAttr[ nL ]);
143 memcpy( pAttribs.get(), pA, nL*sizeof(ExtTextInputAttr) );
146 void Impl_IMEInfos::DestroyAttribs()
148 pAttribs.reset();
149 nLen = 0;
152 Edit::Edit( WindowType nType )
153 : Control( nType )
155 ImplInitEditData();
158 Edit::Edit( vcl::Window* pParent, WinBits nStyle )
159 : Control( WindowType::EDIT )
161 ImplInitEditData();
162 ImplInit( pParent, nStyle );
165 void Edit::SetWidthInChars(sal_Int32 nWidthInChars)
167 if (mnWidthInChars != nWidthInChars)
169 mnWidthInChars = nWidthInChars;
170 queue_resize();
174 void Edit::setMaxWidthChars(sal_Int32 nWidth)
176 if (nWidth != mnMaxWidthChars)
178 mnMaxWidthChars = nWidth;
179 queue_resize();
183 bool Edit::set_property(const OUString &rKey, const OUString &rValue)
185 if (rKey == "width-chars")
186 SetWidthInChars(rValue.toInt32());
187 else if (rKey == "max-width-chars")
188 setMaxWidthChars(rValue.toInt32());
189 else if (rKey == "max-length")
191 sal_Int32 nTextLen = rValue.toInt32();
192 SetMaxTextLen(nTextLen == 0 ? EDIT_NOLIMIT : nTextLen);
194 else if (rKey == "editable")
196 SetReadOnly(!toBool(rValue));
198 else if (rKey == "overwrite-mode")
200 SetInsertMode(!toBool(rValue));
202 else if (rKey == "visibility")
204 mbPassword = false;
205 if (!toBool(rValue))
206 mbPassword = true;
208 else if (rKey == "placeholder-text")
209 SetPlaceholderText(rValue);
210 else if (rKey == "shadow-type")
212 if (GetStyle() & WB_BORDER)
213 SetBorderStyle(rValue == "none" ? WindowBorderStyle::MONO : WindowBorderStyle::NORMAL);
215 else
216 return Control::set_property(rKey, rValue);
217 return true;
220 Edit::~Edit()
222 disposeOnce();
225 void Edit::dispose()
227 mpUIBuilder.reset();
228 mpDDInfo.reset();
230 vcl::Cursor* pCursor = GetCursor();
231 if ( pCursor )
233 SetCursor( nullptr );
234 delete pCursor;
237 mpIMEInfos.reset();
239 if ( mxDnDListener.is() )
241 if ( GetDragGestureRecognizer().is() )
243 uno::Reference< datatransfer::dnd::XDragGestureListener> xDGL( mxDnDListener, uno::UNO_QUERY );
244 GetDragGestureRecognizer()->removeDragGestureListener( xDGL );
246 if ( GetDropTarget().is() )
248 uno::Reference< datatransfer::dnd::XDropTargetListener> xDTL( mxDnDListener, uno::UNO_QUERY );
249 GetDropTarget()->removeDropTargetListener( xDTL );
252 mxDnDListener->disposing( lang::EventObject() ); // #95154# #96585# Empty Source means it's the Client
253 mxDnDListener.clear();
256 SetType(WindowType::WINDOW);
258 mpSubEdit.disposeAndClear();
259 Control::dispose();
262 void Edit::ImplInitEditData()
264 mpSubEdit = VclPtr<Edit>();
265 mpFilterText = nullptr;
266 mnXOffset = 0;
267 mnAlign = EDIT_ALIGN_LEFT;
268 mnMaxTextLen = EDIT_NOLIMIT;
269 mnWidthInChars = -1;
270 mnMaxWidthChars = -1;
271 mbInternModified = false;
272 mbReadOnly = false;
273 mbInsertMode = true;
274 mbClickedInSelection = false;
275 mbActivePopup = false;
276 mbIsSubEdit = false;
277 mbForceControlBackground = false;
278 mbPassword = false;
279 mpDDInfo = nullptr;
280 mpIMEInfos = nullptr;
281 mcEchoChar = 0;
283 // no default mirroring for Edit controls
284 // note: controls that use a subedit will revert this (SpinField, ComboBox)
285 EnableRTL( false );
287 mxDnDListener = new vcl::unohelper::DragAndDropWrapper( this );
290 bool Edit::ImplUseNativeBorder(vcl::RenderContext const & rRenderContext, WinBits nStyle) const
292 bool bRet = rRenderContext.IsNativeControlSupported(ImplGetNativeControlType(),
293 ControlPart::HasBackgroundTexture)
294 && ((nStyle & WB_BORDER) && !(nStyle & WB_NOBORDER));
295 if (!bRet && mbIsSubEdit)
297 vcl::Window* pWindow = GetParent();
298 nStyle = pWindow->GetStyle();
299 bRet = pWindow->IsNativeControlSupported(ImplGetNativeControlType(),
300 ControlPart::HasBackgroundTexture)
301 && ((nStyle & WB_BORDER) && !(nStyle & WB_NOBORDER));
303 return bRet;
306 void Edit::ImplInit(vcl::Window* pParent, WinBits nStyle)
308 nStyle = ImplInitStyle(nStyle);
310 if (!(nStyle & (WB_CENTER | WB_RIGHT)))
311 nStyle |= WB_LEFT;
313 Control::ImplInit(pParent, nStyle, nullptr);
315 mbReadOnly = (nStyle & WB_READONLY) != 0;
317 mnAlign = EDIT_ALIGN_LEFT;
319 // hack: right align until keyinput and cursor travelling works
320 if( IsRTLEnabled() )
321 mnAlign = EDIT_ALIGN_RIGHT;
323 if ( nStyle & WB_RIGHT )
324 mnAlign = EDIT_ALIGN_RIGHT;
325 else if ( nStyle & WB_CENTER )
326 mnAlign = EDIT_ALIGN_CENTER;
328 SetCursor( new vcl::Cursor );
330 SetPointer( PointerStyle::Text );
331 ApplySettings(*GetOutDev());
333 uno::Reference< datatransfer::dnd::XDragGestureListener> xDGL( mxDnDListener, uno::UNO_QUERY );
334 uno::Reference< datatransfer::dnd::XDragGestureRecognizer > xDGR = GetDragGestureRecognizer();
335 if ( xDGR.is() )
337 xDGR->addDragGestureListener( xDGL );
338 uno::Reference< datatransfer::dnd::XDropTargetListener> xDTL( mxDnDListener, uno::UNO_QUERY );
339 GetDropTarget()->addDropTargetListener( xDTL );
340 GetDropTarget()->setActive( true );
341 GetDropTarget()->setDefaultActions( datatransfer::dnd::DNDConstants::ACTION_COPY_OR_MOVE );
345 WinBits Edit::ImplInitStyle( WinBits nStyle )
347 if ( !(nStyle & WB_NOTABSTOP) )
348 nStyle |= WB_TABSTOP;
349 if ( !(nStyle & WB_NOGROUP) )
350 nStyle |= WB_GROUP;
352 return nStyle;
355 bool Edit::IsCharInput( const KeyEvent& rKeyEvent )
357 // In the future we must use new Unicode functions for this
358 sal_Unicode cCharCode = rKeyEvent.GetCharCode();
359 return ((cCharCode >= 32) && (cCharCode != 127) &&
360 !rKeyEvent.GetKeyCode().IsMod3() &&
361 !rKeyEvent.GetKeyCode().IsMod2() &&
362 !rKeyEvent.GetKeyCode().IsMod1() );
365 void Edit::ApplySettings(vcl::RenderContext& rRenderContext)
367 Control::ApplySettings(rRenderContext);
369 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
371 const vcl::Font& aFont = rStyleSettings.GetFieldFont();
372 ApplyControlFont(rRenderContext, aFont);
374 ImplClearLayoutData();
376 Color aTextColor = rStyleSettings.GetFieldTextColor();
377 ApplyControlForeground(rRenderContext, aTextColor);
379 if (IsControlBackground())
381 rRenderContext.SetBackground(GetControlBackground());
382 rRenderContext.SetFillColor(GetControlBackground());
384 if (ImplUseNativeBorder(rRenderContext, GetStyle()))
386 // indicates that no non-native drawing of background should take place
387 mpWindowImpl->mnNativeBackground = ControlPart::Entire;
390 else if (ImplUseNativeBorder(rRenderContext, GetStyle()))
392 // Transparent background
393 rRenderContext.SetBackground();
394 rRenderContext.SetFillColor();
396 else
398 rRenderContext.SetBackground(rStyleSettings.GetFieldColor());
399 rRenderContext.SetFillColor(rStyleSettings.GetFieldColor());
403 tools::Long Edit::ImplGetExtraXOffset() const
405 // MT 09/2002: nExtraOffsetX should become a member, instead of checking every time,
406 // but I need an incompatible update for this...
407 // #94095# Use extra offset only when edit has a border
408 tools::Long nExtraOffset = 0;
409 if( ( GetStyle() & WB_BORDER ) || ( mbIsSubEdit && ( GetParent()->GetStyle() & WB_BORDER ) ) )
410 nExtraOffset = 2;
412 return nExtraOffset;
415 tools::Long Edit::ImplGetExtraYOffset() const
417 tools::Long nExtraOffset = 0;
418 ControlType eCtrlType = ImplGetNativeControlType();
419 if (eCtrlType != ControlType::EditboxNoBorder)
421 // add some space between text entry and border
422 nExtraOffset = 2;
424 return nExtraOffset;
427 OUString Edit::ImplGetText() const
429 if ( mcEchoChar || mbPassword )
431 sal_Unicode cEchoChar;
432 if ( mcEchoChar )
433 cEchoChar = mcEchoChar;
434 else
435 cEchoChar = u'\x2022';
436 OUStringBuffer aText(maText.getLength());
437 comphelper::string::padToLength(aText, maText.getLength(), cEchoChar);
438 return aText.makeStringAndClear();
440 else
441 return maText.toString();
444 void Edit::ImplInvalidateOrRepaint()
446 if( IsPaintTransparent() )
448 Invalidate();
449 // FIXME: this is currently only on macOS
450 if( ImplGetSVData()->maNWFData.mbNoFocusRects )
451 PaintImmediately();
453 else
454 Invalidate();
457 tools::Long Edit::ImplGetTextYPosition() const
459 if ( GetStyle() & WB_TOP )
460 return ImplGetExtraXOffset();
461 else if ( GetStyle() & WB_BOTTOM )
462 return GetOutputSizePixel().Height() - GetTextHeight() - ImplGetExtraXOffset();
463 return ( GetOutputSizePixel().Height() - GetTextHeight() ) / 2;
466 void Edit::ImplRepaint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRectangle)
468 if (!IsReallyVisible())
469 return;
471 ApplySettings(rRenderContext);
473 const OUString aText = ImplGetText();
474 const sal_Int32 nLen = aText.getLength();
476 sal_Int32 nDXBuffer[256];
477 std::unique_ptr<sal_Int32[]> pDXBuffer;
478 sal_Int32* pDX = nDXBuffer;
480 if (nLen)
482 if (o3tl::make_unsigned(2 * nLen) > SAL_N_ELEMENTS(nDXBuffer))
484 pDXBuffer.reset(new sal_Int32[2 * (nLen + 1)]);
485 pDX = pDXBuffer.get();
488 GetOutDev()->GetCaretPositions(aText, pDX, 0, nLen);
491 tools::Long nTH = GetTextHeight();
492 Point aPos(mnXOffset, ImplGetTextYPosition());
494 vcl::Cursor* pCursor = GetCursor();
495 bool bVisCursor = pCursor && pCursor->IsVisible();
496 if (pCursor)
497 pCursor->Hide();
499 ImplClearBackground(rRenderContext, rRectangle, 0, GetOutputSizePixel().Width()-1);
501 bool bPaintPlaceholderText = aText.isEmpty() && !maPlaceholderText.isEmpty();
503 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
505 if (!IsEnabled() || bPaintPlaceholderText)
506 rRenderContext.SetTextColor(rStyleSettings.GetDisableColor());
508 // Set background color of the normal text
509 if (mbForceControlBackground && IsControlBackground())
511 // check if we need to set ControlBackground even in NWF case
512 rRenderContext.Push(vcl::PushFlags::FILLCOLOR | vcl::PushFlags::LINECOLOR);
513 rRenderContext.SetLineColor();
514 rRenderContext.SetFillColor(GetControlBackground());
515 rRenderContext.DrawRect(tools::Rectangle(aPos, Size(GetOutputSizePixel().Width() - 2 * mnXOffset, GetOutputSizePixel().Height())));
516 rRenderContext.Pop();
518 rRenderContext.SetTextFillColor(GetControlBackground());
520 else if (IsPaintTransparent() || ImplUseNativeBorder(rRenderContext, GetStyle()))
521 rRenderContext.SetTextFillColor();
522 else
523 rRenderContext.SetTextFillColor(IsControlBackground() ? GetControlBackground() : rStyleSettings.GetFieldColor());
525 ImplPaintBorder(rRenderContext);
527 bool bDrawSelection = maSelection.Len() && (HasFocus() || (GetStyle() & WB_NOHIDESELECTION) || mbActivePopup);
529 aPos.setX( mnXOffset + ImplGetExtraXOffset() );
530 if (bPaintPlaceholderText)
532 rRenderContext.DrawText(aPos, maPlaceholderText);
534 else if (!bDrawSelection && !mpIMEInfos)
536 rRenderContext.DrawText(aPos, aText, 0, nLen);
538 else
540 // save graphics state
541 rRenderContext.Push();
542 // first calculate highlighted and non highlighted clip regions
543 vcl::Region aHighlightClipRegion;
544 vcl::Region aNormalClipRegion;
545 Selection aTmpSel(maSelection);
546 aTmpSel.Normalize();
547 // selection is highlighted
548 for(sal_Int32 i = 0; i < nLen; ++i)
550 tools::Rectangle aRect(aPos, Size(10, nTH));
551 aRect.SetLeft( pDX[2 * i] + mnXOffset + ImplGetExtraXOffset() );
552 aRect.SetRight( pDX[2 * i + 1] + mnXOffset + ImplGetExtraXOffset() );
553 aRect.Normalize();
554 bool bHighlight = false;
555 if (i >= aTmpSel.Min() && i < aTmpSel.Max())
556 bHighlight = true;
558 if (mpIMEInfos && mpIMEInfos->pAttribs &&
559 i >= mpIMEInfos->nPos && i < (mpIMEInfos->nPos+mpIMEInfos->nLen) &&
560 (mpIMEInfos->pAttribs[i - mpIMEInfos->nPos] & ExtTextInputAttr::Highlight))
562 bHighlight = true;
565 if (bHighlight)
566 aHighlightClipRegion.Union(aRect);
567 else
568 aNormalClipRegion.Union(aRect);
570 // draw normal text
571 Color aNormalTextColor = rRenderContext.GetTextColor();
572 rRenderContext.SetClipRegion(aNormalClipRegion);
574 if (IsPaintTransparent())
575 rRenderContext.SetTextFillColor();
576 else
578 // Set background color when part of the text is selected
579 if (ImplUseNativeBorder(rRenderContext, GetStyle()))
581 if( mbForceControlBackground && IsControlBackground() )
582 rRenderContext.SetTextFillColor(GetControlBackground());
583 else
584 rRenderContext.SetTextFillColor();
586 else
588 rRenderContext.SetTextFillColor(IsControlBackground() ? GetControlBackground() : rStyleSettings.GetFieldColor());
591 rRenderContext.DrawText(aPos, aText, 0, nLen);
593 // draw highlighted text
594 rRenderContext.SetClipRegion(aHighlightClipRegion);
595 rRenderContext.SetTextColor(rStyleSettings.GetHighlightTextColor());
596 rRenderContext.SetTextFillColor(rStyleSettings.GetHighlightColor());
597 rRenderContext.DrawText(aPos, aText, 0, nLen);
599 // if IME info exists loop over portions and output different font attributes
600 if (mpIMEInfos && mpIMEInfos->pAttribs)
602 for(int n = 0; n < 2; n++)
604 vcl::Region aRegion;
605 if (n == 0)
607 rRenderContext.SetTextColor(aNormalTextColor);
608 if (IsPaintTransparent())
609 rRenderContext.SetTextFillColor();
610 else
611 rRenderContext.SetTextFillColor(IsControlBackground() ? GetControlBackground() : rStyleSettings.GetFieldColor());
612 aRegion = aNormalClipRegion;
614 else
616 rRenderContext.SetTextColor(rStyleSettings.GetHighlightTextColor());
617 rRenderContext.SetTextFillColor(rStyleSettings.GetHighlightColor());
618 aRegion = aHighlightClipRegion;
621 for(int i = 0; i < mpIMEInfos->nLen; )
623 ExtTextInputAttr nAttr = mpIMEInfos->pAttribs[i];
624 vcl::Region aClip;
625 int nIndex = i;
626 while (nIndex < mpIMEInfos->nLen && mpIMEInfos->pAttribs[nIndex] == nAttr) // #112631# check nIndex before using it
628 tools::Rectangle aRect( aPos, Size( 10, nTH ) );
629 aRect.SetLeft( pDX[2 * (nIndex + mpIMEInfos->nPos)] + mnXOffset + ImplGetExtraXOffset() );
630 aRect.SetRight( pDX[2 * (nIndex + mpIMEInfos->nPos) + 1] + mnXOffset + ImplGetExtraXOffset() );
631 aRect.Normalize();
632 aClip.Union(aRect);
633 nIndex++;
635 i = nIndex;
636 aClip.Intersect(aRegion);
637 if (!aClip.IsEmpty() && nAttr != ExtTextInputAttr::NONE)
639 vcl::Font aFont = rRenderContext.GetFont();
640 if (nAttr & ExtTextInputAttr::Underline)
641 aFont.SetUnderline(LINESTYLE_SINGLE);
642 else if (nAttr & ExtTextInputAttr::DoubleUnderline)
643 aFont.SetUnderline(LINESTYLE_DOUBLE);
644 else if (nAttr & ExtTextInputAttr::BoldUnderline)
645 aFont.SetUnderline( LINESTYLE_BOLD);
646 else if (nAttr & ExtTextInputAttr::DottedUnderline)
647 aFont.SetUnderline( LINESTYLE_DOTTED);
648 else if (nAttr & ExtTextInputAttr::DashDotUnderline)
649 aFont.SetUnderline( LINESTYLE_DASHDOT);
650 else if (nAttr & ExtTextInputAttr::GrayWaveline)
652 aFont.SetUnderline(LINESTYLE_WAVE);
653 rRenderContext.SetTextLineColor(COL_LIGHTGRAY);
655 rRenderContext.SetFont(aFont);
657 if (nAttr & ExtTextInputAttr::RedText)
658 rRenderContext.SetTextColor(COL_RED);
659 else if (nAttr & ExtTextInputAttr::HalfToneText)
660 rRenderContext.SetTextColor(COL_LIGHTGRAY);
662 rRenderContext.SetClipRegion(aClip);
663 rRenderContext.DrawText(aPos, aText, 0, nLen);
669 // restore graphics state
670 rRenderContext.Pop();
673 if (bVisCursor && (!mpIMEInfos || mpIMEInfos->bCursor))
674 pCursor->Show();
677 void Edit::ImplDelete( const Selection& rSelection, sal_uInt8 nDirection, sal_uInt8 nMode )
679 const sal_Int32 nTextLen = ImplGetText().getLength();
681 // deleting possible?
682 if ( !rSelection.Len() &&
683 (((rSelection.Min() == 0) && (nDirection == EDIT_DEL_LEFT)) ||
684 ((rSelection.Max() == nTextLen) && (nDirection == EDIT_DEL_RIGHT))) )
685 return;
687 ImplClearLayoutData();
689 Selection aSelection( rSelection );
690 aSelection.Normalize();
692 if ( !aSelection.Len() )
694 uno::Reference < i18n::XBreakIterator > xBI = ImplGetBreakIterator();
695 if ( nDirection == EDIT_DEL_LEFT )
697 if ( nMode == EDIT_DELMODE_RESTOFWORD )
699 const OUString sText = maText.toString();
700 i18n::Boundary aBoundary = xBI->getWordBoundary( sText, aSelection.Min(),
701 GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES, true );
702 auto startPos = aBoundary.startPos;
703 if ( startPos == aSelection.Min() )
705 aBoundary = xBI->previousWord( sText, aSelection.Min(),
706 GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES );
707 startPos = std::max(aBoundary.startPos, sal_Int32(0));
709 aSelection.Min() = startPos;
711 else if ( nMode == EDIT_DELMODE_RESTOFCONTENT )
713 aSelection.Min() = 0;
715 else
717 sal_Int32 nCount = 1;
718 aSelection.Min() = xBI->previousCharacters( maText.toString(), aSelection.Min(),
719 GetSettings().GetLanguageTag().getLocale(), i18n::CharacterIteratorMode::SKIPCHARACTER, nCount, nCount );
722 else
724 if ( nMode == EDIT_DELMODE_RESTOFWORD )
726 i18n::Boundary aBoundary = xBI->nextWord( maText.toString(), aSelection.Max(),
727 GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES );
728 aSelection.Max() = aBoundary.startPos;
730 else if ( nMode == EDIT_DELMODE_RESTOFCONTENT )
732 aSelection.Max() = nTextLen;
734 else
736 sal_Int32 nCount = 1;
737 aSelection.Max() = xBI->nextCharacters( maText.toString(), aSelection.Max(),
738 GetSettings().GetLanguageTag().getLocale(), i18n::CharacterIteratorMode::SKIPCHARACTER, nCount, nCount );
743 const auto nSelectionMin = aSelection.Min();
744 maText.remove( nSelectionMin, aSelection.Len() );
745 maSelection.Min() = nSelectionMin;
746 maSelection.Max() = nSelectionMin;
747 ImplAlignAndPaint();
748 mbInternModified = true;
751 OUString Edit::ImplGetValidString( const OUString& rString )
753 OUString aValidString = rString.replaceAll("\n", "").replaceAll("\r", "");
754 aValidString = aValidString.replace('\t', ' ');
755 return aValidString;
758 uno::Reference <i18n::XBreakIterator> const& Edit::ImplGetBreakIterator()
760 if (!mxBreakIterator)
761 mxBreakIterator = i18n::BreakIterator::create(::comphelper::getProcessComponentContext());
762 return mxBreakIterator;
765 uno::Reference <i18n::XExtendedInputSequenceChecker> const& Edit::ImplGetInputSequenceChecker()
767 if (!mxISC.is())
768 mxISC = i18n::InputSequenceChecker::create(::comphelper::getProcessComponentContext());
769 return mxISC;
772 void Edit::ShowTruncationWarning(weld::Widget* pParent)
774 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pParent, VclMessageType::Warning,
775 VclButtonsType::Ok, VclResId(SV_EDIT_WARNING_STR)));
776 xBox->run();
779 bool Edit::ImplTruncateToMaxLen( OUString& rStr, sal_Int32 nSelectionLen ) const
781 bool bWasTruncated = false;
782 if (maText.getLength() - nSelectionLen > mnMaxTextLen - rStr.getLength())
784 sal_Int32 nErasePos = mnMaxTextLen - maText.getLength() + nSelectionLen;
785 rStr = rStr.copy( 0, nErasePos );
786 bWasTruncated = true;
788 return bWasTruncated;
791 void Edit::ImplInsertText( const OUString& rStr, const Selection* pNewSel, bool bIsUserInput )
793 Selection aSelection( maSelection );
794 aSelection.Normalize();
796 OUString aNewText( ImplGetValidString( rStr ) );
798 // as below, if there's no selection, but we're in overwrite mode and not beyond
799 // the end of the existing text then that's like a selection of 1
800 auto nSelectionLen = aSelection.Len();
801 if (!nSelectionLen && !mbInsertMode && aSelection.Max() < maText.getLength())
802 nSelectionLen = 1;
803 ImplTruncateToMaxLen( aNewText, nSelectionLen );
805 ImplClearLayoutData();
807 if ( aSelection.Len() )
808 maText.remove( aSelection.Min(), aSelection.Len() );
809 else if (!mbInsertMode && aSelection.Max() < maText.getLength())
810 maText.remove( aSelection.Max(), 1 );
812 // take care of input-sequence-checking now
813 if (bIsUserInput && !rStr.isEmpty())
815 SAL_WARN_IF( rStr.getLength() != 1, "vcl", "unexpected string length. User input is expected to provide 1 char only!" );
817 // determine if input-sequence-checking should be applied or not
819 uno::Reference < i18n::XBreakIterator > xBI = ImplGetBreakIterator();
820 bool bIsInputSequenceChecking = rStr.getLength() == 1 &&
821 officecfg::Office::Common::I18N::CTL::CTLFont::get() &&
822 officecfg::Office::Common::I18N::CTL::CTLSequenceChecking::get() &&
823 aSelection.Min() > 0 && /* first char needs not to be checked */
824 xBI.is() && i18n::ScriptType::COMPLEX == xBI->getScriptType( rStr, 0 );
826 if (bIsInputSequenceChecking)
828 uno::Reference < i18n::XExtendedInputSequenceChecker > xISC = ImplGetInputSequenceChecker();
829 if (xISC.is())
831 sal_Unicode cChar = rStr[0];
832 sal_Int32 nTmpPos = aSelection.Min();
833 sal_Int16 nCheckMode = officecfg::Office::Common::I18N::CTL::CTLSequenceCheckingRestricted::get()?
834 i18n::InputSequenceCheckMode::STRICT : i18n::InputSequenceCheckMode::BASIC;
836 // the text that needs to be checked is only the one
837 // before the current cursor position
838 const OUString aOldText( maText.subView(0, nTmpPos) );
839 OUString aTmpText( aOldText );
840 if (officecfg::Office::Common::I18N::CTL::CTLSequenceCheckingTypeAndReplace::get())
842 xISC->correctInputSequence( aTmpText, nTmpPos - 1, cChar, nCheckMode );
844 // find position of first character that has changed
845 sal_Int32 nOldLen = aOldText.getLength();
846 sal_Int32 nTmpLen = aTmpText.getLength();
847 const sal_Unicode *pOldTxt = aOldText.getStr();
848 const sal_Unicode *pTmpTxt = aTmpText.getStr();
849 sal_Int32 nChgPos = 0;
850 while ( nChgPos < nOldLen && nChgPos < nTmpLen &&
851 pOldTxt[nChgPos] == pTmpTxt[nChgPos] )
852 ++nChgPos;
854 const OUString aChgText( aTmpText.copy( nChgPos ) );
856 // remove text from first pos to be changed to current pos
857 maText.remove( nChgPos, nTmpPos - nChgPos );
859 if (!aChgText.isEmpty())
861 aNewText = aChgText;
862 aSelection.Min() = nChgPos; // position for new text to be inserted
864 else
865 aNewText.clear();
867 else
869 // should the character be ignored (i.e. not get inserted) ?
870 if (!xISC->checkInputSequence( aOldText, nTmpPos - 1, cChar, nCheckMode ))
871 aNewText.clear();
876 // at this point now we will insert the non-empty text 'normally' some lines below...
879 if ( !aNewText.isEmpty() )
880 maText.insert( aSelection.Min(), aNewText );
882 if ( !pNewSel )
884 maSelection.Min() = aSelection.Min() + aNewText.getLength();
885 maSelection.Max() = maSelection.Min();
887 else
889 maSelection = *pNewSel;
890 if ( maSelection.Min() > maText.getLength() )
891 maSelection.Min() = maText.getLength();
892 if ( maSelection.Max() > maText.getLength() )
893 maSelection.Max() = maText.getLength();
896 ImplAlignAndPaint();
897 mbInternModified = true;
900 void Edit::ImplSetText( const OUString& rText, const Selection* pNewSelection )
902 // we delete text by "selecting" the old text completely then calling InsertText; this is flicker free
903 if ( ( rText.getLength() > mnMaxTextLen ) ||
904 ( std::u16string_view(rText) == std::u16string_view(maText)
905 && (!pNewSelection || (*pNewSelection == maSelection)) ) )
906 return;
908 ImplClearLayoutData();
909 maSelection.Min() = 0;
910 maSelection.Max() = maText.getLength();
911 if ( mnXOffset || HasPaintEvent() )
913 mnXOffset = 0;
914 maText = ImplGetValidString( rText );
916 // #i54929# recalculate mnXOffset before ImplSetSelection,
917 // else cursor ends up in wrong position
918 ImplAlign();
920 if ( pNewSelection )
921 ImplSetSelection( *pNewSelection, false );
923 if ( mnXOffset && !pNewSelection )
924 maSelection.Max() = 0;
926 Invalidate();
928 else
929 ImplInsertText( rText, pNewSelection );
931 CallEventListeners( VclEventId::EditModify );
934 ControlType Edit::ImplGetNativeControlType() const
936 ControlType nCtrl = ControlType::Generic;
937 const vcl::Window* pControl = mbIsSubEdit ? GetParent() : this;
939 switch (pControl->GetType())
941 case WindowType::COMBOBOX:
942 case WindowType::PATTERNBOX:
943 case WindowType::NUMERICBOX:
944 case WindowType::METRICBOX:
945 case WindowType::CURRENCYBOX:
946 case WindowType::DATEBOX:
947 case WindowType::TIMEBOX:
948 case WindowType::LONGCURRENCYBOX:
949 nCtrl = ControlType::Combobox;
950 break;
952 case WindowType::MULTILINEEDIT:
953 if ( GetWindow( GetWindowType::Border ) != this )
954 nCtrl = ControlType::MultilineEditbox;
955 else
956 nCtrl = ControlType::EditboxNoBorder;
957 break;
959 case WindowType::EDIT:
960 case WindowType::PATTERNFIELD:
961 case WindowType::METRICFIELD:
962 case WindowType::CURRENCYFIELD:
963 case WindowType::DATEFIELD:
964 case WindowType::TIMEFIELD:
965 case WindowType::SPINFIELD:
966 case WindowType::FORMATTEDFIELD:
967 if (pControl->GetStyle() & WB_SPIN)
968 nCtrl = ControlType::Spinbox;
969 else
971 if (GetWindow(GetWindowType::Border) != this)
972 nCtrl = ControlType::Editbox;
973 else
974 nCtrl = ControlType::EditboxNoBorder;
976 break;
978 default:
979 nCtrl = ControlType::Editbox;
981 return nCtrl;
984 void Edit::ImplClearBackground(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRectangle, tools::Long nXStart, tools::Long nXEnd )
987 * note: at this point the cursor must be switched off already
989 tools::Rectangle aRect(Point(), GetOutputSizePixel());
990 aRect.SetLeft( nXStart );
991 aRect.SetRight( nXEnd );
993 if( !(ImplUseNativeBorder(rRenderContext, GetStyle()) || IsPaintTransparent()))
994 rRenderContext.Erase(aRect);
995 else if (SupportsDoubleBuffering() && mbIsSubEdit)
997 // ImplPaintBorder() is a NOP, we have a native border, and this is a sub-edit of a control.
998 // That means we have to draw the parent native widget to paint the edit area to clear our background.
999 vcl::PaintBufferGuard g(ImplGetWindowImpl()->mpFrameData, GetParent());
1000 GetParent()->Paint(rRenderContext, rRectangle);
1004 void Edit::ImplPaintBorder(vcl::RenderContext const & rRenderContext)
1006 // this is not needed when double-buffering
1007 if (SupportsDoubleBuffering())
1008 return;
1010 if (!(ImplUseNativeBorder(rRenderContext, GetStyle()) || IsPaintTransparent()))
1011 return;
1013 // draw the inner part by painting the whole control using its border window
1014 vcl::Window* pBorder = GetWindow(GetWindowType::Border);
1015 if (pBorder == this)
1017 // we have no border, use parent
1018 vcl::Window* pControl = mbIsSubEdit ? GetParent() : this;
1019 pBorder = pControl->GetWindow(GetWindowType::Border);
1020 if (pBorder == this)
1021 pBorder = GetParent();
1024 if (!pBorder)
1025 return;
1027 // set proper clipping region to not overdraw the whole control
1028 vcl::Region aClipRgn = GetPaintRegion();
1029 if (!aClipRgn.IsNull())
1031 // transform clipping region to border window's coordinate system
1032 if (IsRTLEnabled() != pBorder->IsRTLEnabled() && AllSettings::GetLayoutRTL())
1034 // need to mirror in case border is not RTL but edit is (or vice versa)
1036 // mirror
1037 tools::Rectangle aBounds(aClipRgn.GetBoundRect());
1038 int xNew = GetOutputSizePixel().Width() - aBounds.GetWidth() - aBounds.Left();
1039 aClipRgn.Move(xNew - aBounds.Left(), 0);
1041 // move offset of border window
1042 Point aBorderOffs = pBorder->ScreenToOutputPixel(OutputToScreenPixel(Point()));
1043 aClipRgn.Move(aBorderOffs.X(), aBorderOffs.Y());
1045 else
1047 // normal case
1048 Point aBorderOffs = pBorder->ScreenToOutputPixel(OutputToScreenPixel(Point()));
1049 aClipRgn.Move(aBorderOffs.X(), aBorderOffs.Y());
1052 vcl::Region oldRgn(pBorder->GetOutDev()->GetClipRegion());
1053 pBorder->GetOutDev()->SetClipRegion(aClipRgn);
1055 pBorder->Paint(*pBorder->GetOutDev(), tools::Rectangle());
1057 pBorder->GetOutDev()->SetClipRegion(oldRgn);
1059 else
1061 pBorder->Paint(*pBorder->GetOutDev(), tools::Rectangle());
1065 void Edit::ImplShowCursor( bool bOnlyIfVisible )
1067 if ( !IsUpdateMode() || ( bOnlyIfVisible && !IsReallyVisible() ) )
1068 return;
1070 vcl::Cursor* pCursor = GetCursor();
1071 OUString aText = ImplGetText();
1073 tools::Long nTextPos = 0;
1075 sal_Int32 nDXBuffer[256];
1076 std::unique_ptr<sal_Int32[]> pDXBuffer;
1077 sal_Int32* pDX = nDXBuffer;
1079 if( !aText.isEmpty() )
1081 if( o3tl::make_unsigned(2*aText.getLength()) > SAL_N_ELEMENTS(nDXBuffer) )
1083 pDXBuffer.reset(new sal_Int32[2*(aText.getLength()+1)]);
1084 pDX = pDXBuffer.get();
1087 GetOutDev()->GetCaretPositions( aText, pDX, 0, aText.getLength() );
1089 if( maSelection.Max() < aText.getLength() )
1090 nTextPos = pDX[ 2*maSelection.Max() ];
1091 else
1092 nTextPos = pDX[ 2*aText.getLength()-1 ];
1095 tools::Long nCursorWidth = 0;
1096 if ( !mbInsertMode && !maSelection.Len() && (maSelection.Max() < aText.getLength()) )
1097 nCursorWidth = GetTextWidth(aText, maSelection.Max(), 1);
1098 tools::Long nCursorPosX = nTextPos + mnXOffset + ImplGetExtraXOffset();
1100 // cursor should land in visible area
1101 const Size aOutSize = GetOutputSizePixel();
1102 if ( (nCursorPosX < 0) || (nCursorPosX >= aOutSize.Width()) )
1104 tools::Long nOldXOffset = mnXOffset;
1106 if ( nCursorPosX < 0 )
1108 mnXOffset = - nTextPos;
1109 tools::Long nMaxX = 0;
1110 mnXOffset += aOutSize.Width() / 5;
1111 if ( mnXOffset > nMaxX )
1112 mnXOffset = nMaxX;
1114 else
1116 mnXOffset = (aOutSize.Width()-ImplGetExtraXOffset()) - nTextPos;
1117 // Something more?
1118 if ( (aOutSize.Width()-ImplGetExtraXOffset()) < nTextPos )
1120 tools::Long nMaxNegX = (aOutSize.Width()-ImplGetExtraXOffset()) - GetTextWidth( aText );
1121 mnXOffset -= aOutSize.Width() / 5;
1122 if ( mnXOffset < nMaxNegX ) // both negative...
1123 mnXOffset = nMaxNegX;
1127 nCursorPosX = nTextPos + mnXOffset + ImplGetExtraXOffset();
1128 if ( nCursorPosX == aOutSize.Width() ) // then invisible...
1129 nCursorPosX--;
1131 if ( mnXOffset != nOldXOffset )
1132 ImplInvalidateOrRepaint();
1135 const tools::Long nTextHeight = GetTextHeight();
1136 const tools::Long nCursorPosY = ImplGetTextYPosition();
1137 if (pCursor)
1139 pCursor->SetPos( Point( nCursorPosX, nCursorPosY ) );
1140 pCursor->SetSize( Size( nCursorWidth, nTextHeight ) );
1141 pCursor->Show();
1145 void Edit::ImplAlign()
1147 if (mnAlign == EDIT_ALIGN_LEFT && !mnXOffset)
1149 // short circuit common case and avoid slow GetTextWidth() calc
1150 return;
1153 tools::Long nTextWidth = GetTextWidth( ImplGetText() );
1154 tools::Long nOutWidth = GetOutputSizePixel().Width();
1156 if ( mnAlign == EDIT_ALIGN_LEFT )
1158 if (nTextWidth < nOutWidth)
1159 mnXOffset = 0;
1161 else if ( mnAlign == EDIT_ALIGN_RIGHT )
1163 tools::Long nMinXOffset = nOutWidth - nTextWidth - 1 - ImplGetExtraXOffset();
1164 bool bRTL = IsRTLEnabled();
1165 if( mbIsSubEdit && GetParent() )
1166 bRTL = GetParent()->IsRTLEnabled();
1167 if( bRTL )
1169 if( nTextWidth < nOutWidth )
1170 mnXOffset = nMinXOffset;
1172 else
1174 if( nTextWidth < nOutWidth )
1175 mnXOffset = nMinXOffset;
1176 else if ( mnXOffset < nMinXOffset )
1177 mnXOffset = nMinXOffset;
1180 else if( mnAlign == EDIT_ALIGN_CENTER )
1182 // would be nicer with check while scrolling but then it's not centred in scrolled state
1183 mnXOffset = (nOutWidth - nTextWidth) / 2;
1187 void Edit::ImplAlignAndPaint()
1189 ImplAlign();
1190 ImplInvalidateOrRepaint();
1191 ImplShowCursor();
1194 sal_Int32 Edit::ImplGetCharPos( const Point& rWindowPos ) const
1196 sal_Int32 nIndex = EDIT_NOLIMIT;
1197 OUString aText = ImplGetText();
1199 sal_Int32 nDXBuffer[256];
1200 std::unique_ptr<sal_Int32[]> pDXBuffer;
1201 sal_Int32* pDX = nDXBuffer;
1202 if( o3tl::make_unsigned(2*aText.getLength()) > SAL_N_ELEMENTS(nDXBuffer) )
1204 pDXBuffer.reset(new sal_Int32[2*(aText.getLength()+1)]);
1205 pDX = pDXBuffer.get();
1208 GetOutDev()->GetCaretPositions( aText, pDX, 0, aText.getLength() );
1209 tools::Long nX = rWindowPos.X() - mnXOffset - ImplGetExtraXOffset();
1210 for (sal_Int32 i = 0; i < aText.getLength(); aText.iterateCodePoints(&i))
1212 if( (pDX[2*i] >= nX && pDX[2*i+1] <= nX) ||
1213 (pDX[2*i+1] >= nX && pDX[2*i] <= nX))
1215 nIndex = i;
1216 if( pDX[2*i] < pDX[2*i+1] )
1218 if( nX > (pDX[2*i]+pDX[2*i+1])/2 )
1219 aText.iterateCodePoints(&nIndex);
1221 else
1223 if( nX < (pDX[2*i]+pDX[2*i+1])/2 )
1224 aText.iterateCodePoints(&nIndex);
1226 break;
1229 if( nIndex == EDIT_NOLIMIT )
1231 nIndex = 0;
1232 sal_Int32 nFinalIndex = 0;
1233 tools::Long nDiff = std::abs( pDX[0]-nX );
1234 sal_Int32 i = 0;
1235 if (!aText.isEmpty())
1237 aText.iterateCodePoints(&i); //skip the first character
1239 while (i < aText.getLength())
1241 tools::Long nNewDiff = std::abs( pDX[2*i]-nX );
1243 if( nNewDiff < nDiff )
1245 nIndex = i;
1246 nDiff = nNewDiff;
1249 nFinalIndex = i;
1251 aText.iterateCodePoints(&i);
1253 if (nIndex == nFinalIndex && std::abs( pDX[2*nIndex+1] - nX ) < nDiff)
1254 nIndex = EDIT_NOLIMIT;
1257 return nIndex;
1260 void Edit::ImplSetCursorPos( sal_Int32 nChar, bool bSelect )
1262 Selection aSelection( maSelection );
1263 aSelection.Max() = nChar;
1264 if ( !bSelect )
1265 aSelection.Min() = aSelection.Max();
1266 ImplSetSelection( aSelection );
1269 void Edit::ImplCopyToSelectionClipboard()
1271 if ( GetSelection().Len() )
1273 css::uno::Reference<css::datatransfer::clipboard::XClipboard> aSelection(GetSystemPrimarySelection());
1274 ImplCopy( aSelection );
1278 void Edit::ImplCopy( uno::Reference< datatransfer::clipboard::XClipboard > const & rxClipboard )
1280 vcl::unohelper::TextDataObject::CopyStringTo( GetSelected(), rxClipboard );
1283 void Edit::ImplPaste( uno::Reference< datatransfer::clipboard::XClipboard > const & rxClipboard )
1285 if ( !rxClipboard.is() )
1286 return;
1288 uno::Reference< datatransfer::XTransferable > xDataObj;
1292 SolarMutexReleaser aReleaser;
1293 xDataObj = rxClipboard->getContents();
1295 catch( const css::uno::Exception& )
1299 if ( !xDataObj.is() )
1300 return;
1302 datatransfer::DataFlavor aFlavor;
1303 SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING, aFlavor );
1306 uno::Any aData = xDataObj->getTransferData( aFlavor );
1307 OUString aText;
1308 aData >>= aText;
1310 // tdf#127588 - extend selection to the entire field or paste the text
1311 // from the clipboard to the current position if there is no selection
1312 if (mnMaxTextLen < EDIT_NOLIMIT && maSelection.Len() == 0)
1314 const sal_Int32 aTextLen = aText.getLength();
1315 if (aTextLen == mnMaxTextLen)
1317 maSelection.Min() = 0;
1318 maSelection.Max() = mnMaxTextLen;
1319 } else
1320 maSelection.Max() = std::min<sal_Int32>(maSelection.Min() + aTextLen, mnMaxTextLen);
1323 Selection aSelection(maSelection);
1324 aSelection.Normalize();
1325 if (ImplTruncateToMaxLen(aText, aSelection.Len()))
1326 ShowTruncationWarning(GetFrameWeld());
1328 ReplaceSelected( aText );
1330 catch( const css::uno::Exception& )
1335 void Edit::MouseButtonDown( const MouseEvent& rMEvt )
1337 if ( mpSubEdit )
1339 Control::MouseButtonDown( rMEvt );
1340 return;
1343 sal_Int32 nCharPos = ImplGetCharPos( rMEvt.GetPosPixel() );
1344 Selection aSelection( maSelection );
1345 aSelection.Normalize();
1347 if ( rMEvt.GetClicks() < 4 )
1349 mbClickedInSelection = false;
1350 if ( rMEvt.GetClicks() == 3 )
1352 ImplSetSelection( Selection( 0, EDIT_NOLIMIT) );
1353 ImplCopyToSelectionClipboard();
1356 else if ( rMEvt.GetClicks() == 2 )
1358 uno::Reference < i18n::XBreakIterator > xBI = ImplGetBreakIterator();
1359 i18n::Boundary aBoundary = xBI->getWordBoundary( maText.toString(), aSelection.Max(),
1360 GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES, true );
1361 ImplSetSelection( Selection( aBoundary.startPos, aBoundary.endPos ) );
1362 ImplCopyToSelectionClipboard();
1364 else if ( !rMEvt.IsShift() && HasFocus() && aSelection.Contains( nCharPos ) )
1365 mbClickedInSelection = true;
1366 else if ( rMEvt.IsLeft() )
1367 ImplSetCursorPos( nCharPos, rMEvt.IsShift() );
1369 if ( !mbClickedInSelection && rMEvt.IsLeft() && ( rMEvt.GetClicks() == 1 ) )
1370 StartTracking( StartTrackingFlags::ScrollRepeat );
1373 GrabFocus();
1376 void Edit::MouseButtonUp( const MouseEvent& rMEvt )
1378 if ( mbClickedInSelection && rMEvt.IsLeft() )
1380 sal_Int32 nCharPos = ImplGetCharPos( rMEvt.GetPosPixel() );
1381 ImplSetCursorPos( nCharPos, false );
1382 mbClickedInSelection = false;
1384 else if ( rMEvt.IsMiddle() && !mbReadOnly &&
1385 ( GetSettings().GetMouseSettings().GetMiddleButtonAction() == MouseMiddleButtonAction::PasteSelection ) )
1387 css::uno::Reference<css::datatransfer::clipboard::XClipboard> aSelection(GetSystemPrimarySelection());
1388 ImplPaste( aSelection );
1389 Modify();
1393 void Edit::Tracking( const TrackingEvent& rTEvt )
1395 if ( rTEvt.IsTrackingEnded() )
1397 if ( mbClickedInSelection )
1399 sal_Int32 nCharPos = ImplGetCharPos( rTEvt.GetMouseEvent().GetPosPixel() );
1400 ImplSetCursorPos( nCharPos, false );
1401 mbClickedInSelection = false;
1403 else if ( rTEvt.GetMouseEvent().IsLeft() )
1405 ImplCopyToSelectionClipboard();
1408 else
1410 if( !mbClickedInSelection )
1412 sal_Int32 nCharPos = ImplGetCharPos( rTEvt.GetMouseEvent().GetPosPixel() );
1413 ImplSetCursorPos( nCharPos, true );
1418 bool Edit::ImplHandleKeyEvent( const KeyEvent& rKEvt )
1420 bool bDone = false;
1421 sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode();
1422 KeyFuncType eFunc = rKEvt.GetKeyCode().GetFunction();
1424 mbInternModified = false;
1426 if ( eFunc != KeyFuncType::DONTKNOW )
1428 switch ( eFunc )
1430 case KeyFuncType::CUT:
1432 if ( !mbReadOnly && maSelection.Len() && !mbPassword )
1434 Cut();
1435 Modify();
1436 bDone = true;
1439 break;
1441 case KeyFuncType::COPY:
1443 if ( !mbPassword )
1445 Copy();
1446 bDone = true;
1449 break;
1451 case KeyFuncType::PASTE:
1453 if ( !mbReadOnly )
1455 Paste();
1456 bDone = true;
1459 break;
1461 case KeyFuncType::UNDO:
1463 if ( !mbReadOnly )
1465 Undo();
1466 bDone = true;
1469 break;
1471 default:
1472 eFunc = KeyFuncType::DONTKNOW;
1476 if ( !bDone && rKEvt.GetKeyCode().IsMod1() && !rKEvt.GetKeyCode().IsMod2() )
1478 if ( nCode == KEY_A )
1480 ImplSetSelection( Selection( 0, maText.getLength() ) );
1481 bDone = true;
1483 else if ( rKEvt.GetKeyCode().IsShift() && (nCode == KEY_S) )
1485 if ( pImplFncGetSpecialChars )
1487 Selection aSaveSel = GetSelection(); // if someone changes the selection in Get/LoseFocus, e.g. URL bar
1488 OUString aChars = pImplFncGetSpecialChars( GetFrameWeld(), GetFont() );
1489 SetSelection( aSaveSel );
1490 if ( !aChars.isEmpty() )
1492 ImplInsertText( aChars );
1493 Modify();
1495 bDone = true;
1500 if ( eFunc == KeyFuncType::DONTKNOW && ! bDone )
1502 switch ( nCode )
1504 case css::awt::Key::SELECT_ALL:
1506 ImplSetSelection( Selection( 0, maText.getLength() ) );
1507 bDone = true;
1509 break;
1511 case KEY_LEFT:
1512 case KEY_RIGHT:
1513 case KEY_HOME:
1514 case KEY_END:
1515 case css::awt::Key::MOVE_WORD_FORWARD:
1516 case css::awt::Key::SELECT_WORD_FORWARD:
1517 case css::awt::Key::MOVE_WORD_BACKWARD:
1518 case css::awt::Key::SELECT_WORD_BACKWARD:
1519 case css::awt::Key::MOVE_TO_BEGIN_OF_LINE:
1520 case css::awt::Key::MOVE_TO_END_OF_LINE:
1521 case css::awt::Key::SELECT_TO_BEGIN_OF_LINE:
1522 case css::awt::Key::SELECT_TO_END_OF_LINE:
1523 case css::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH:
1524 case css::awt::Key::MOVE_TO_END_OF_PARAGRAPH:
1525 case css::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH:
1526 case css::awt::Key::SELECT_TO_END_OF_PARAGRAPH:
1527 case css::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT:
1528 case css::awt::Key::MOVE_TO_END_OF_DOCUMENT:
1529 case css::awt::Key::SELECT_TO_BEGIN_OF_DOCUMENT:
1530 case css::awt::Key::SELECT_TO_END_OF_DOCUMENT:
1532 if ( !rKEvt.GetKeyCode().IsMod2() )
1534 ImplClearLayoutData();
1535 uno::Reference < i18n::XBreakIterator > xBI = ImplGetBreakIterator();
1537 Selection aSel( maSelection );
1538 bool bWord = rKEvt.GetKeyCode().IsMod1();
1539 bool bSelect = rKEvt.GetKeyCode().IsShift();
1540 bool bGoLeft = (nCode == KEY_LEFT);
1541 bool bGoRight = (nCode == KEY_RIGHT);
1542 bool bGoHome = (nCode == KEY_HOME);
1543 bool bGoEnd = (nCode == KEY_END);
1545 switch( nCode )
1547 case css::awt::Key::MOVE_WORD_FORWARD:
1548 bGoRight = bWord = true;break;
1549 case css::awt::Key::SELECT_WORD_FORWARD:
1550 bGoRight = bSelect = bWord = true;break;
1551 case css::awt::Key::MOVE_WORD_BACKWARD:
1552 bGoLeft = bWord = true;break;
1553 case css::awt::Key::SELECT_WORD_BACKWARD:
1554 bGoLeft = bSelect = bWord = true;break;
1555 case css::awt::Key::SELECT_TO_BEGIN_OF_LINE:
1556 case css::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH:
1557 case css::awt::Key::SELECT_TO_BEGIN_OF_DOCUMENT:
1558 bSelect = true;
1559 [[fallthrough]];
1560 case css::awt::Key::MOVE_TO_BEGIN_OF_LINE:
1561 case css::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH:
1562 case css::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT:
1563 bGoHome = true;break;
1564 case css::awt::Key::SELECT_TO_END_OF_LINE:
1565 case css::awt::Key::SELECT_TO_END_OF_PARAGRAPH:
1566 case css::awt::Key::SELECT_TO_END_OF_DOCUMENT:
1567 bSelect = true;
1568 [[fallthrough]];
1569 case css::awt::Key::MOVE_TO_END_OF_LINE:
1570 case css::awt::Key::MOVE_TO_END_OF_PARAGRAPH:
1571 case css::awt::Key::MOVE_TO_END_OF_DOCUMENT:
1572 bGoEnd = true;break;
1573 default:
1574 break;
1577 // range is checked in ImplSetSelection ...
1578 if ( bGoLeft && aSel.Max() )
1580 if ( bWord )
1582 const OUString sText = maText.toString();
1583 i18n::Boundary aBoundary = xBI->getWordBoundary( sText, aSel.Max(),
1584 GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES, true );
1585 if ( aBoundary.startPos == aSel.Max() )
1586 aBoundary = xBI->previousWord( sText, aSel.Max(),
1587 GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES );
1588 aSel.Max() = aBoundary.startPos;
1590 else
1592 sal_Int32 nCount = 1;
1593 aSel.Max() = xBI->previousCharacters( maText.toString(), aSel.Max(),
1594 GetSettings().GetLanguageTag().getLocale(), i18n::CharacterIteratorMode::SKIPCHARACTER, nCount, nCount );
1597 else if ( bGoRight && ( aSel.Max() < maText.getLength() ) )
1599 if ( bWord )
1601 i18n::Boundary aBoundary = xBI->nextWord( maText.toString(), aSel.Max(),
1602 GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES );
1603 aSel.Max() = aBoundary.startPos;
1605 else
1607 sal_Int32 nCount = 1;
1608 aSel.Max() = xBI->nextCharacters( maText.toString(), aSel.Max(),
1609 GetSettings().GetLanguageTag().getLocale(), i18n::CharacterIteratorMode::SKIPCHARACTER, nCount, nCount );
1612 else if ( bGoHome )
1614 aSel.Max() = 0;
1616 else if ( bGoEnd )
1618 aSel.Max() = EDIT_NOLIMIT;
1621 if ( !bSelect )
1622 aSel.Min() = aSel.Max();
1624 if ( aSel != GetSelection() )
1626 ImplSetSelection( aSel );
1627 ImplCopyToSelectionClipboard();
1630 if (bGoEnd && maAutocompleteHdl.IsSet() && !rKEvt.GetKeyCode().GetModifier())
1632 if ( (maSelection.Min() == maSelection.Max()) && (maSelection.Min() == maText.getLength()) )
1634 maAutocompleteHdl.Call(*this);
1638 bDone = true;
1641 break;
1643 case css::awt::Key::DELETE_WORD_BACKWARD:
1644 case css::awt::Key::DELETE_WORD_FORWARD:
1645 case css::awt::Key::DELETE_TO_BEGIN_OF_LINE:
1646 case css::awt::Key::DELETE_TO_END_OF_LINE:
1647 case KEY_BACKSPACE:
1648 case KEY_DELETE:
1650 if ( !mbReadOnly && !rKEvt.GetKeyCode().IsMod2() )
1652 sal_uInt8 nDel = (nCode == KEY_DELETE) ? EDIT_DEL_RIGHT : EDIT_DEL_LEFT;
1653 sal_uInt8 nMode = rKEvt.GetKeyCode().IsMod1() ? EDIT_DELMODE_RESTOFWORD : EDIT_DELMODE_SIMPLE;
1654 if ( (nMode == EDIT_DELMODE_RESTOFWORD) && rKEvt.GetKeyCode().IsShift() )
1655 nMode = EDIT_DELMODE_RESTOFCONTENT;
1656 switch( nCode )
1658 case css::awt::Key::DELETE_WORD_BACKWARD:
1659 nDel = EDIT_DEL_LEFT;
1660 nMode = EDIT_DELMODE_RESTOFWORD;
1661 break;
1662 case css::awt::Key::DELETE_WORD_FORWARD:
1663 nDel = EDIT_DEL_RIGHT;
1664 nMode = EDIT_DELMODE_RESTOFWORD;
1665 break;
1666 case css::awt::Key::DELETE_TO_BEGIN_OF_LINE:
1667 nDel = EDIT_DEL_LEFT;
1668 nMode = EDIT_DELMODE_RESTOFCONTENT;
1669 break;
1670 case css::awt::Key::DELETE_TO_END_OF_LINE:
1671 nDel = EDIT_DEL_RIGHT;
1672 nMode = EDIT_DELMODE_RESTOFCONTENT;
1673 break;
1674 default: break;
1676 sal_Int32 nOldLen = maText.getLength();
1677 ImplDelete( maSelection, nDel, nMode );
1678 if ( maText.getLength() != nOldLen )
1679 Modify();
1680 bDone = true;
1683 break;
1685 case KEY_INSERT:
1687 if ( !mpIMEInfos && !mbReadOnly && !rKEvt.GetKeyCode().IsMod2() )
1689 SetInsertMode( !mbInsertMode );
1690 bDone = true;
1693 break;
1695 case KEY_RETURN:
1696 if (maActivateHdl.IsSet() && !rKEvt.GetKeyCode().GetModifier())
1697 bDone = maActivateHdl.Call(*this);
1698 break;
1700 default:
1702 if ( IsCharInput( rKEvt ) )
1704 bDone = true; // read characters also when in ReadOnly
1705 if ( !mbReadOnly )
1707 ImplInsertText(OUString(rKEvt.GetCharCode()), nullptr, true);
1708 if (maAutocompleteHdl.IsSet())
1710 if ( (maSelection.Min() == maSelection.Max()) && (maSelection.Min() == maText.getLength()) )
1712 maAutocompleteHdl.Call(*this);
1721 if ( mbInternModified )
1722 Modify();
1724 return bDone;
1727 void Edit::KeyInput( const KeyEvent& rKEvt )
1729 if ( mpSubEdit || !ImplHandleKeyEvent( rKEvt ) )
1730 Control::KeyInput( rKEvt );
1733 void Edit::FillLayoutData() const
1735 mxLayoutData.emplace();
1736 const_cast<Edit*>(this)->Invalidate();
1739 void Edit::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRectangle)
1741 if (!mpSubEdit)
1742 ImplRepaint(rRenderContext, rRectangle);
1745 void Edit::Resize()
1747 if ( !mpSubEdit && IsReallyVisible() )
1749 Control::Resize();
1750 // because of vertical centering...
1751 mnXOffset = 0;
1752 ImplAlign();
1753 Invalidate();
1754 ImplShowCursor();
1758 void Edit::Draw( OutputDevice* pDev, const Point& rPos, SystemTextColorFlags nFlags )
1760 ApplySettings(*pDev);
1762 Point aPos = pDev->LogicToPixel( rPos );
1763 Size aSize = GetSizePixel();
1764 vcl::Font aFont = GetDrawPixelFont( pDev );
1766 pDev->Push();
1767 pDev->SetMapMode();
1768 pDev->SetFont( aFont );
1769 pDev->SetTextFillColor();
1771 // Border/Background
1772 pDev->SetLineColor();
1773 pDev->SetFillColor();
1774 bool bBorder = (GetStyle() & WB_BORDER);
1775 bool bBackground = IsControlBackground();
1776 if ( bBorder || bBackground )
1778 tools::Rectangle aRect( aPos, aSize );
1779 if ( bBorder )
1781 ImplDrawFrame( pDev, aRect );
1783 if ( bBackground )
1785 pDev->SetFillColor( GetControlBackground() );
1786 pDev->DrawRect( aRect );
1790 // Content
1791 if ( nFlags & SystemTextColorFlags::Mono )
1792 pDev->SetTextColor( COL_BLACK );
1793 else
1795 if ( !IsEnabled() )
1797 const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
1798 pDev->SetTextColor( rStyleSettings.GetDisableColor() );
1800 else
1802 pDev->SetTextColor( GetTextColor() );
1806 const tools::Long nOnePixel = GetDrawPixel( pDev, 1 );
1807 const tools::Long nOffX = 3*nOnePixel;
1808 DrawTextFlags nTextStyle = DrawTextFlags::VCenter;
1809 tools::Rectangle aTextRect( aPos, aSize );
1811 if ( GetStyle() & WB_CENTER )
1812 nTextStyle |= DrawTextFlags::Center;
1813 else if ( GetStyle() & WB_RIGHT )
1814 nTextStyle |= DrawTextFlags::Right;
1815 else
1816 nTextStyle |= DrawTextFlags::Left;
1818 aTextRect.AdjustLeft(nOffX );
1819 aTextRect.AdjustRight( -nOffX );
1821 OUString aText = ImplGetText();
1822 tools::Long nTextHeight = pDev->GetTextHeight();
1823 tools::Long nTextWidth = pDev->GetTextWidth( aText );
1824 tools::Long nOffY = (aSize.Height() - nTextHeight) / 2;
1826 // Clipping?
1827 if ( (nOffY < 0) ||
1828 ((nOffY+nTextHeight) > aSize.Height()) ||
1829 ((nOffX+nTextWidth) > aSize.Width()) )
1831 tools::Rectangle aClip( aPos, aSize );
1832 if ( nTextHeight > aSize.Height() )
1833 aClip.AdjustBottom(nTextHeight-aSize.Height()+1 ); // prevent HP printers from 'optimizing'
1834 pDev->IntersectClipRegion( aClip );
1837 pDev->DrawText( aTextRect, aText, nTextStyle );
1838 pDev->Pop();
1840 if ( GetSubEdit() )
1842 Size aOrigSize(GetSubEdit()->GetSizePixel());
1843 GetSubEdit()->SetSizePixel(GetSizePixel());
1844 GetSubEdit()->Draw(pDev, rPos, nFlags);
1845 GetSubEdit()->SetSizePixel(aOrigSize);
1849 void Edit::ImplInvalidateOutermostBorder( vcl::Window* pWin )
1851 // allow control to show focused state
1852 vcl::Window *pInvalWin = pWin;
1853 for (;;)
1855 vcl::Window* pBorder = pInvalWin->GetWindow( GetWindowType::Border );
1856 if (pBorder == pInvalWin || !pBorder ||
1857 pInvalWin->ImplGetFrame() != pBorder->ImplGetFrame() )
1858 break;
1859 pInvalWin = pBorder;
1862 pInvalWin->Invalidate( InvalidateFlags::Children | InvalidateFlags::Update );
1865 void Edit::GetFocus()
1867 if ( mpSubEdit )
1868 mpSubEdit->ImplGrabFocus( GetGetFocusFlags() );
1869 else if ( !mbActivePopup )
1871 maUndoText = maText.toString();
1872 SelectionOptions nSelOptions = GetSettings().GetStyleSettings().GetSelectionOptions();
1873 if ( !( GetStyle() & (WB_NOHIDESELECTION|WB_READONLY) )
1874 && ( GetGetFocusFlags() & (GetFocusFlags::Init|GetFocusFlags::Tab|GetFocusFlags::CURSOR|GetFocusFlags::Mnemonic) ) )
1876 if ( nSelOptions & SelectionOptions::ShowFirst )
1878 maSelection.Min() = maText.getLength();
1879 maSelection.Max() = 0;
1881 else
1883 maSelection.Min() = 0;
1884 maSelection.Max() = maText.getLength();
1886 if ( mbIsSubEdit )
1887 static_cast<Edit*>(GetParent())->CallEventListeners( VclEventId::EditSelectionChanged );
1888 else
1889 CallEventListeners( VclEventId::EditSelectionChanged );
1892 ImplShowCursor();
1894 if (IsNativeWidgetEnabled() &&
1895 IsNativeControlSupported( ControlType::Editbox, ControlPart::Entire ))
1897 ImplInvalidateOutermostBorder( mbIsSubEdit ? GetParent() : this );
1899 else if ( maSelection.Len() )
1901 // paint the selection
1902 if ( !HasPaintEvent() )
1903 ImplInvalidateOrRepaint();
1904 else
1905 Invalidate();
1908 SetInputContext( InputContext( GetFont(), !IsReadOnly() ? InputContextFlags::Text|InputContextFlags::ExtText : InputContextFlags::NONE ) );
1911 Control::GetFocus();
1914 void Edit::LoseFocus()
1916 if ( !mpSubEdit )
1918 if (IsNativeWidgetEnabled() &&
1919 IsNativeControlSupported(ControlType::Editbox, ControlPart::Entire))
1921 ImplInvalidateOutermostBorder( mbIsSubEdit ? GetParent() : this );
1924 if ( !mbActivePopup && !( GetStyle() & WB_NOHIDESELECTION ) && maSelection.Len() )
1925 ImplInvalidateOrRepaint(); // paint the selection
1928 Control::LoseFocus();
1931 bool Edit::PreNotify(NotifyEvent& rNEvt)
1933 if (rNEvt.GetType() == NotifyEventType::MOUSEMOVE)
1935 const MouseEvent* pMouseEvt = rNEvt.GetMouseEvent();
1936 if (pMouseEvt && !pMouseEvt->GetButtons() && !pMouseEvt->IsSynthetic() && !pMouseEvt->IsModifierChanged())
1938 // trigger redraw if mouse over state has changed
1939 if (pMouseEvt->IsLeaveWindow() || pMouseEvt->IsEnterWindow())
1941 if (IsNativeWidgetEnabled() &&
1942 IsNativeControlSupported(ControlType::Editbox, ControlPart::Entire))
1944 ImplInvalidateOutermostBorder(this);
1950 return Control::PreNotify(rNEvt);
1953 void Edit::Command( const CommandEvent& rCEvt )
1955 if ( rCEvt.GetCommand() == CommandEventId::ContextMenu )
1957 VclPtr<PopupMenu> pPopup = Edit::CreatePopupMenu();
1959 bool bEnableCut = true;
1960 bool bEnableCopy = true;
1961 bool bEnableDelete = true;
1962 bool bEnablePaste = true;
1963 bool bEnableSpecialChar = true;
1965 if ( !maSelection.Len() )
1967 bEnableCut = false;
1968 bEnableCopy = false;
1969 bEnableDelete = false;
1972 if ( IsReadOnly() )
1974 bEnableCut = false;
1975 bEnablePaste = false;
1976 bEnableDelete = false;
1977 bEnableSpecialChar = false;
1979 else
1981 // only paste if text available in clipboard
1982 bool bData = false;
1983 uno::Reference< datatransfer::clipboard::XClipboard > xClipboard = GetClipboard();
1985 if ( xClipboard.is() )
1987 uno::Reference< datatransfer::XTransferable > xDataObj;
1989 SolarMutexReleaser aReleaser;
1990 xDataObj = xClipboard->getContents();
1992 if ( xDataObj.is() )
1994 datatransfer::DataFlavor aFlavor;
1995 SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING, aFlavor );
1996 bData = xDataObj->isDataFlavorSupported( aFlavor );
1999 bEnablePaste = bData;
2002 pPopup->EnableItem(pPopup->GetItemId(u"cut"), bEnableCut);
2003 pPopup->EnableItem(pPopup->GetItemId(u"copy"), bEnableCopy);
2004 pPopup->EnableItem(pPopup->GetItemId(u"delete"), bEnableDelete);
2005 pPopup->EnableItem(pPopup->GetItemId(u"paste"), bEnablePaste);
2006 pPopup->EnableItem(pPopup->GetItemId(u"specialchar"), bEnableSpecialChar);
2007 pPopup->EnableItem(
2008 pPopup->GetItemId(u"undo"),
2009 std::u16string_view(maUndoText) != std::u16string_view(maText));
2010 bool bAllSelected = maSelection.Min() == 0 && maSelection.Max() == maText.getLength();
2011 pPopup->EnableItem(pPopup->GetItemId(u"selectall"), !bAllSelected);
2012 pPopup->ShowItem(pPopup->GetItemId(u"specialchar"), pImplFncGetSpecialChars != nullptr);
2014 mbActivePopup = true;
2015 Selection aSaveSel = GetSelection(); // if someone changes selection in Get/LoseFocus, e.g. URL bar
2016 Point aPos = rCEvt.GetMousePosPixel();
2017 if ( !rCEvt.IsMouseEvent() )
2019 // Show menu eventually centered in selection
2020 Size aSize = GetOutputSizePixel();
2021 aPos = Point( aSize.Width()/2, aSize.Height()/2 );
2023 sal_uInt16 n = pPopup->Execute( this, aPos );
2024 SetSelection( aSaveSel );
2025 OUString sCommand = pPopup->GetItemIdent(n);
2026 if (sCommand == "undo")
2028 Undo();
2029 Modify();
2031 else if (sCommand == "cut")
2033 Cut();
2034 Modify();
2036 else if (sCommand == "copy")
2038 Copy();
2040 else if (sCommand == "paste")
2042 Paste();
2043 Modify();
2045 else if (sCommand == "delete")
2047 DeleteSelected();
2048 Modify();
2050 else if (sCommand == "selectall")
2052 ImplSetSelection( Selection( 0, maText.getLength() ) );
2054 else if (sCommand == "specialchar" && pImplFncGetSpecialChars)
2056 OUString aChars = pImplFncGetSpecialChars(GetFrameWeld(), GetFont());
2057 if (!isDisposed()) // destroyed while the insert special character dialog was still open
2059 SetSelection( aSaveSel );
2060 if (!aChars.isEmpty())
2062 ImplInsertText( aChars );
2063 Modify();
2067 pPopup.clear();
2068 mbActivePopup = false;
2070 else if ( rCEvt.GetCommand() == CommandEventId::StartExtTextInput )
2072 DeleteSelected();
2073 sal_Int32 nPos = maSelection.Max();
2074 mpIMEInfos.reset(new Impl_IMEInfos( nPos, maText.copy(nPos).makeStringAndClear() ));
2075 mpIMEInfos->bWasCursorOverwrite = !IsInsertMode();
2077 else if ( rCEvt.GetCommand() == CommandEventId::EndExtTextInput )
2079 bool bInsertMode = !mpIMEInfos->bWasCursorOverwrite;
2080 mpIMEInfos.reset();
2082 SetInsertMode(bInsertMode);
2083 Modify();
2085 Invalidate();
2087 // #i25161# call auto complete handler for ext text commit also
2088 if (maAutocompleteHdl.IsSet())
2090 if ( (maSelection.Min() == maSelection.Max()) && (maSelection.Min() == maText.getLength()) )
2092 maAutocompleteHdl.Call(*this);
2096 else if ( rCEvt.GetCommand() == CommandEventId::ExtTextInput )
2098 const CommandExtTextInputData* pData = rCEvt.GetExtTextInputData();
2100 maText.remove( mpIMEInfos->nPos, mpIMEInfos->nLen );
2101 maText.insert( mpIMEInfos->nPos, pData->GetText() );
2102 if ( mpIMEInfos->bWasCursorOverwrite )
2104 const sal_Int32 nOldIMETextLen = mpIMEInfos->nLen;
2105 const sal_Int32 nNewIMETextLen = pData->GetText().getLength();
2106 if ( ( nOldIMETextLen > nNewIMETextLen ) &&
2107 ( nNewIMETextLen < mpIMEInfos->aOldTextAfterStartPos.getLength() ) )
2109 // restore old characters
2110 const sal_Int32 nRestore = nOldIMETextLen - nNewIMETextLen;
2111 maText.insert( mpIMEInfos->nPos + nNewIMETextLen, mpIMEInfos->aOldTextAfterStartPos.subView( nNewIMETextLen, nRestore ) );
2113 else if ( ( nOldIMETextLen < nNewIMETextLen ) &&
2114 ( nOldIMETextLen < mpIMEInfos->aOldTextAfterStartPos.getLength() ) )
2116 const sal_Int32 nOverwrite = ( nNewIMETextLen > mpIMEInfos->aOldTextAfterStartPos.getLength()
2117 ? mpIMEInfos->aOldTextAfterStartPos.getLength() : nNewIMETextLen ) - nOldIMETextLen;
2118 maText.remove( mpIMEInfos->nPos + nNewIMETextLen, nOverwrite );
2122 if ( pData->GetTextAttr() )
2124 mpIMEInfos->CopyAttribs( pData->GetTextAttr(), pData->GetText().getLength() );
2125 mpIMEInfos->bCursor = pData->IsCursorVisible();
2127 else
2129 mpIMEInfos->DestroyAttribs();
2132 ImplAlignAndPaint();
2133 sal_Int32 nCursorPos = mpIMEInfos->nPos + pData->GetCursorPos();
2134 SetSelection( Selection( nCursorPos, nCursorPos ) );
2135 SetInsertMode( !pData->IsCursorOverwrite() );
2137 if ( pData->IsCursorVisible() )
2138 GetCursor()->Show();
2139 else
2140 GetCursor()->Hide();
2142 else if ( rCEvt.GetCommand() == CommandEventId::CursorPos )
2144 if ( mpIMEInfos )
2146 sal_Int32 nCursorPos = GetSelection().Max();
2147 SetCursorRect( nullptr, GetTextWidth( maText.toString(), nCursorPos, mpIMEInfos->nPos+mpIMEInfos->nLen-nCursorPos ) );
2149 else
2151 SetCursorRect();
2154 else if ( rCEvt.GetCommand() == CommandEventId::SelectionChange )
2156 const CommandSelectionChangeData *pData = rCEvt.GetSelectionChangeData();
2157 Selection aSelection( pData->GetStart(), pData->GetEnd() );
2158 SetSelection(aSelection);
2160 else if ( rCEvt.GetCommand() == CommandEventId::QueryCharPosition )
2162 if (mpIMEInfos && mpIMEInfos->nLen > 0)
2164 OUString aText = ImplGetText();
2165 std::vector<sal_Int32> aDX(2*(aText.getLength()+1));
2167 GetOutDev()->GetCaretPositions( aText, aDX.data(), 0, aText.getLength() );
2169 tools::Long nTH = GetTextHeight();
2170 Point aPos( mnXOffset, ImplGetTextYPosition() );
2172 std::vector<tools::Rectangle> aRects(mpIMEInfos->nLen);
2173 for ( int nIndex = 0; nIndex < mpIMEInfos->nLen; ++nIndex )
2175 tools::Rectangle aRect( aPos, Size( 10, nTH ) );
2176 aRect.SetLeft( aDX[2*(nIndex+mpIMEInfos->nPos)] + mnXOffset + ImplGetExtraXOffset() );
2177 aRects[ nIndex ] = aRect;
2179 SetCompositionCharRect(aRects.data(), mpIMEInfos->nLen);
2182 else
2183 Control::Command( rCEvt );
2186 void Edit::StateChanged( StateChangedType nType )
2188 if (nType == StateChangedType::InitShow)
2190 if (!mpSubEdit)
2192 mnXOffset = 0; // if GrabFocus before while size was still wrong
2193 ImplAlign();
2194 if (!mpSubEdit)
2195 ImplShowCursor(false);
2196 Invalidate();
2199 else if (nType == StateChangedType::Enable)
2201 if (!mpSubEdit)
2203 // change text color only
2204 ImplInvalidateOrRepaint();
2207 else if (nType == StateChangedType::Style || nType == StateChangedType::Mirroring)
2209 WinBits nStyle = GetStyle();
2210 if (nType == StateChangedType::Style)
2212 nStyle = ImplInitStyle(GetStyle());
2213 SetStyle(nStyle);
2216 sal_uInt16 nOldAlign = mnAlign;
2217 mnAlign = EDIT_ALIGN_LEFT;
2219 // hack: right align until keyinput and cursor travelling works
2220 // edits are always RTL disabled
2221 // however the parent edits contain the correct setting
2222 if (mbIsSubEdit && GetParent()->IsRTLEnabled())
2224 if (GetParent()->GetStyle() & WB_LEFT)
2225 mnAlign = EDIT_ALIGN_RIGHT;
2226 if (nType == StateChangedType::Mirroring)
2227 GetOutDev()->SetLayoutMode(vcl::text::ComplexTextLayoutFlags::BiDiRtl | vcl::text::ComplexTextLayoutFlags::TextOriginLeft);
2229 else if (mbIsSubEdit && !GetParent()->IsRTLEnabled())
2231 if (nType == StateChangedType::Mirroring)
2232 GetOutDev()->SetLayoutMode(vcl::text::ComplexTextLayoutFlags::TextOriginLeft);
2235 if (nStyle & WB_RIGHT)
2236 mnAlign = EDIT_ALIGN_RIGHT;
2237 else if (nStyle & WB_CENTER)
2238 mnAlign = EDIT_ALIGN_CENTER;
2239 if (!maText.isEmpty() && (mnAlign != nOldAlign))
2241 ImplAlign();
2242 Invalidate();
2246 else if ((nType == StateChangedType::Zoom) || (nType == StateChangedType::ControlFont))
2248 if (!mpSubEdit)
2250 ApplySettings(*GetOutDev());
2251 ImplShowCursor();
2252 Invalidate();
2255 else if ((nType == StateChangedType::ControlForeground) || (nType == StateChangedType::ControlBackground))
2257 if (!mpSubEdit)
2259 ApplySettings(*GetOutDev());
2260 Invalidate();
2264 Control::StateChanged(nType);
2267 void Edit::DataChanged( const DataChangedEvent& rDCEvt )
2269 if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
2270 (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
2271 ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
2272 (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) )
2274 if ( !mpSubEdit )
2276 ApplySettings(*GetOutDev());
2277 ImplShowCursor();
2278 Invalidate();
2282 Control::DataChanged( rDCEvt );
2285 void Edit::ImplShowDDCursor()
2287 if (!mpDDInfo->bVisCursor)
2289 tools::Long nTextWidth = GetTextWidth( maText.toString(), 0, mpDDInfo->nDropPos );
2290 tools::Long nTextHeight = GetTextHeight();
2291 tools::Rectangle aCursorRect( Point( nTextWidth + mnXOffset, (GetOutDev()->GetOutputSize().Height()-nTextHeight)/2 ), Size( 2, nTextHeight ) );
2292 mpDDInfo->aCursor.SetWindow( this );
2293 mpDDInfo->aCursor.SetPos( aCursorRect.TopLeft() );
2294 mpDDInfo->aCursor.SetSize( aCursorRect.GetSize() );
2295 mpDDInfo->aCursor.Show();
2296 mpDDInfo->bVisCursor = true;
2300 void Edit::ImplHideDDCursor()
2302 if ( mpDDInfo && mpDDInfo->bVisCursor )
2304 mpDDInfo->aCursor.Hide();
2305 mpDDInfo->bVisCursor = false;
2309 TextFilter::TextFilter(OUString _aForbiddenChars)
2310 : sForbiddenChars(std::move(_aForbiddenChars))
2314 TextFilter::~TextFilter()
2318 OUString TextFilter::filter(const OUString &rText)
2320 OUString sTemp(rText);
2321 for (sal_Int32 i = 0; i < sForbiddenChars.getLength(); ++i)
2323 sTemp = sTemp.replaceAll(OUStringChar(sForbiddenChars[i]), "");
2325 return sTemp;
2328 void Edit::filterText()
2330 Selection aSel = GetSelection();
2331 const OUString sOrig = GetText();
2332 const OUString sNew = mpFilterText->filter(GetText());
2333 if (sOrig != sNew)
2335 sal_Int32 nDiff = sOrig.getLength() - sNew.getLength();
2336 if (nDiff)
2338 aSel.setMin(aSel.getMin() - nDiff);
2339 aSel.setMax(aSel.getMin());
2341 SetText(sNew);
2342 SetSelection(aSel);
2346 void Edit::Modify()
2348 if (mpFilterText)
2349 filterText();
2351 if ( mbIsSubEdit )
2353 static_cast<Edit*>(GetParent())->Modify();
2355 else
2357 if ( ImplCallEventListenersAndHandler( VclEventId::EditModify, [this] () { maModifyHdl.Call(*this); } ) )
2358 // have been destroyed while calling into the handlers
2359 return;
2361 // #i13677# notify edit listeners about caret position change
2362 CallEventListeners( VclEventId::EditCaretChanged );
2363 // FIXME: this is currently only on macOS
2364 // check for other platforms that need similar handling
2365 if( ImplGetSVData()->maNWFData.mbNoFocusRects &&
2366 IsNativeWidgetEnabled() &&
2367 IsNativeControlSupported( ControlType::Editbox, ControlPart::Entire ) )
2369 ImplInvalidateOutermostBorder( this );
2374 void Edit::SetEchoChar( sal_Unicode c )
2376 mcEchoChar = c;
2377 if ( mpSubEdit )
2378 mpSubEdit->SetEchoChar( c );
2381 void Edit::SetReadOnly( bool bReadOnly )
2383 if ( mbReadOnly != bReadOnly )
2385 mbReadOnly = bReadOnly;
2386 if ( mpSubEdit )
2387 mpSubEdit->SetReadOnly( bReadOnly );
2389 CompatStateChanged( StateChangedType::ReadOnly );
2393 void Edit::SetInsertMode( bool bInsert )
2395 if ( bInsert != mbInsertMode )
2397 mbInsertMode = bInsert;
2398 if ( mpSubEdit )
2399 mpSubEdit->SetInsertMode( bInsert );
2400 else
2401 ImplShowCursor();
2405 bool Edit::IsInsertMode() const
2407 if ( mpSubEdit )
2408 return mpSubEdit->IsInsertMode();
2409 else
2410 return mbInsertMode;
2413 void Edit::SetMaxTextLen(sal_Int32 nMaxLen)
2415 mnMaxTextLen = nMaxLen > 0 ? nMaxLen : EDIT_NOLIMIT;
2417 if ( mpSubEdit )
2418 mpSubEdit->SetMaxTextLen( mnMaxTextLen );
2419 else
2421 if ( maText.getLength() > mnMaxTextLen )
2422 ImplDelete( Selection( mnMaxTextLen, maText.getLength() ), EDIT_DEL_RIGHT, EDIT_DELMODE_SIMPLE );
2426 void Edit::SetSelection( const Selection& rSelection )
2428 // If the selection was changed from outside, e.g. by MouseButtonDown, don't call Tracking()
2429 // directly afterwards which would change the selection again
2430 if ( IsTracking() )
2431 EndTracking();
2432 else if ( mpSubEdit && mpSubEdit->IsTracking() )
2433 mpSubEdit->EndTracking();
2435 ImplSetSelection( rSelection );
2438 void Edit::ImplSetSelection( const Selection& rSelection, bool bPaint )
2440 if ( mpSubEdit )
2441 mpSubEdit->ImplSetSelection( rSelection );
2442 else
2444 if ( rSelection != maSelection )
2446 Selection aOld( maSelection );
2447 Selection aNew( rSelection );
2449 if ( aNew.Min() > maText.getLength() )
2450 aNew.Min() = maText.getLength();
2451 if ( aNew.Max() > maText.getLength() )
2452 aNew.Max() = maText.getLength();
2453 if ( aNew.Min() < 0 )
2454 aNew.Min() = 0;
2455 if ( aNew.Max() < 0 )
2456 aNew.Max() = 0;
2458 if ( aNew != maSelection )
2460 ImplClearLayoutData();
2461 Selection aTemp = maSelection;
2462 maSelection = aNew;
2464 if ( bPaint && ( aOld.Len() || aNew.Len() || IsPaintTransparent() ) )
2465 ImplInvalidateOrRepaint();
2466 ImplShowCursor();
2468 bool bCaret = false, bSelection = false;
2469 tools::Long nB=aNew.Max(), nA=aNew.Min(),oB=aTemp.Max(), oA=aTemp.Min();
2470 tools::Long nGap = nB-nA, oGap = oB-oA;
2471 if (nB != oB)
2472 bCaret = true;
2473 if (nGap != 0 || oGap != 0)
2474 bSelection = true;
2476 if (bSelection)
2478 if ( mbIsSubEdit )
2479 static_cast<Edit*>(GetParent())->CallEventListeners( VclEventId::EditSelectionChanged );
2480 else
2481 CallEventListeners( VclEventId::EditSelectionChanged );
2484 if (bCaret)
2486 if ( mbIsSubEdit )
2487 static_cast<Edit*>(GetParent())->CallEventListeners( VclEventId::EditCaretChanged );
2488 else
2489 CallEventListeners( VclEventId::EditCaretChanged );
2492 // #103511# notify combobox listeners of deselection
2493 if( !maSelection && GetParent() && GetParent()->GetType() == WindowType::COMBOBOX )
2494 static_cast<Edit*>(GetParent())->CallEventListeners( VclEventId::ComboboxDeselect );
2500 const Selection& Edit::GetSelection() const
2502 if ( mpSubEdit )
2503 return mpSubEdit->GetSelection();
2504 else
2505 return maSelection;
2508 void Edit::ReplaceSelected( const OUString& rStr )
2510 if ( mpSubEdit )
2511 mpSubEdit->ReplaceSelected( rStr );
2512 else
2513 ImplInsertText( rStr );
2516 void Edit::DeleteSelected()
2518 if ( mpSubEdit )
2519 mpSubEdit->DeleteSelected();
2520 else
2522 if ( maSelection.Len() )
2523 ImplDelete( maSelection, EDIT_DEL_RIGHT, EDIT_DELMODE_SIMPLE );
2527 OUString Edit::GetSelected() const
2529 if ( mpSubEdit )
2530 return mpSubEdit->GetSelected();
2531 else
2533 Selection aSelection( maSelection );
2534 aSelection.Normalize();
2535 return OUString( maText.getStr() + aSelection.Min(), aSelection.Len() );
2539 void Edit::Cut()
2541 if ( !mbPassword )
2543 Copy();
2544 ReplaceSelected( OUString() );
2548 void Edit::Copy()
2550 if ( !mbPassword )
2552 css::uno::Reference<css::datatransfer::clipboard::XClipboard> aClipboard(GetClipboard());
2553 ImplCopy( aClipboard );
2557 void Edit::Paste()
2559 css::uno::Reference<css::datatransfer::clipboard::XClipboard> aClipboard(GetClipboard());
2560 ImplPaste( aClipboard );
2563 void Edit::Undo()
2565 if ( mpSubEdit )
2566 mpSubEdit->Undo();
2567 else
2569 const OUString aText( maText.toString() );
2570 ImplDelete( Selection( 0, aText.getLength() ), EDIT_DEL_RIGHT, EDIT_DELMODE_SIMPLE );
2571 ImplInsertText( maUndoText );
2572 ImplSetSelection( Selection( 0, maUndoText.getLength() ) );
2573 maUndoText = aText;
2577 void Edit::SetText( const OUString& rStr )
2579 if ( mpSubEdit )
2580 mpSubEdit->SetText( rStr ); // not directly ImplSetText if SetText overridden
2581 else
2583 Selection aNewSel( 0, 0 ); // prevent scrolling
2584 ImplSetText( rStr, &aNewSel );
2588 void Edit::SetText( const OUString& rStr, const Selection& rSelection )
2590 if ( mpSubEdit )
2591 mpSubEdit->SetText( rStr, rSelection );
2592 else
2593 ImplSetText( rStr, &rSelection );
2596 OUString Edit::GetText() const
2598 if ( mpSubEdit )
2599 return mpSubEdit->GetText();
2600 else
2601 return maText.toString();
2604 void Edit::SetCursorAtLast(){
2605 ImplSetCursorPos( GetText().getLength(), false );
2608 void Edit::SetPlaceholderText( const OUString& rStr )
2610 if ( mpSubEdit )
2611 mpSubEdit->SetPlaceholderText( rStr );
2612 else if ( maPlaceholderText != rStr )
2614 maPlaceholderText = rStr;
2615 if ( GetText().isEmpty() )
2616 Invalidate();
2620 void Edit::SetModifyFlag()
2624 void Edit::SetSubEdit(Edit* pEdit)
2626 mpSubEdit.disposeAndClear();
2627 mpSubEdit.set(pEdit);
2629 if (mpSubEdit)
2631 SetPointer(PointerStyle::Arrow); // Only SubEdit has the BEAM...
2632 mpSubEdit->mbIsSubEdit = true;
2634 mpSubEdit->SetReadOnly(mbReadOnly);
2635 mpSubEdit->maAutocompleteHdl = maAutocompleteHdl;
2639 Size Edit::CalcMinimumSizeForText(const OUString &rString) const
2641 ControlType eCtrlType = ImplGetNativeControlType();
2643 Size aSize;
2644 if (mnWidthInChars != -1)
2646 //CalcSize calls CalcWindowSize, but we will call that also in this
2647 //function, so undo the first one with CalcOutputSize
2648 aSize = CalcOutputSize(CalcSize(mnWidthInChars));
2650 else
2652 OUString aString;
2653 if (mnMaxWidthChars != -1 && mnMaxWidthChars < rString.getLength())
2654 aString = rString.copy(0, mnMaxWidthChars);
2655 else
2656 aString = rString;
2658 aSize.setHeight( GetTextHeight() );
2659 aSize.setWidth( GetTextWidth(aString) );
2660 aSize.AdjustWidth(ImplGetExtraXOffset() * 2 );
2662 // do not create edit fields in which one cannot enter anything
2663 // a default minimum width should exist for at least 3 characters
2665 //CalcSize calls CalcWindowSize, but we will call that also in this
2666 //function, so undo the first one with CalcOutputSize
2667 Size aMinSize(CalcOutputSize(CalcSize(3)));
2668 if (aSize.Width() < aMinSize.Width())
2669 aSize.setWidth( aMinSize.Width() );
2672 aSize.AdjustHeight(ImplGetExtraYOffset() * 2 );
2674 aSize = CalcWindowSize( aSize );
2676 // ask NWF what if it has an opinion, too
2677 ImplControlValue aControlValue;
2678 tools::Rectangle aRect( Point( 0, 0 ), aSize );
2679 tools::Rectangle aContent, aBound;
2680 if (GetNativeControlRegion(eCtrlType, ControlPart::Entire, aRect, ControlState::NONE,
2681 aControlValue, aBound, aContent))
2683 if (aBound.GetHeight() > aSize.Height())
2684 aSize.setHeight( aBound.GetHeight() );
2686 return aSize;
2689 Size Edit::CalcMinimumSize() const
2691 return CalcMinimumSizeForText(GetText());
2694 Size Edit::GetOptimalSize() const
2696 return CalcMinimumSize();
2699 Size Edit::CalcSize(sal_Int32 nChars) const
2701 // width for N characters, independent from content.
2702 // works only correct for fixed fonts, average otherwise
2703 float fUnitWidth = std::max(approximate_char_width(), approximate_digit_width());
2704 Size aSz(fUnitWidth * nChars, GetTextHeight());
2705 aSz.AdjustWidth(ImplGetExtraXOffset() * 2 );
2706 aSz = CalcWindowSize( aSz );
2707 return aSz;
2710 sal_Int32 Edit::GetMaxVisChars() const
2712 const vcl::Window* pW = mpSubEdit ? mpSubEdit : this;
2713 sal_Int32 nOutWidth = pW->GetOutputSizePixel().Width();
2714 float fUnitWidth = std::max(approximate_char_width(), approximate_digit_width());
2715 return nOutWidth / fUnitWidth;
2718 namespace vcl
2720 void SetGetSpecialCharsFunction( FncGetSpecialChars fn )
2722 pImplFncGetSpecialChars = fn;
2725 FncGetSpecialChars GetGetSpecialCharsFunction()
2727 return pImplFncGetSpecialChars;
2731 VclPtr<PopupMenu> Edit::CreatePopupMenu()
2733 if (!mpUIBuilder)
2734 mpUIBuilder.reset(new VclBuilder(nullptr, AllSettings::GetUIRootDir(), "vcl/ui/editmenu.ui", ""));
2735 VclPtr<PopupMenu> pPopup = mpUIBuilder->get_menu(u"menu");
2736 const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
2737 if (rStyleSettings.GetHideDisabledMenuItems())
2738 pPopup->SetMenuFlags( MenuFlags::HideDisabledEntries );
2739 else
2740 pPopup->SetMenuFlags ( MenuFlags::AlwaysShowDisabledEntries );
2741 if (rStyleSettings.GetContextMenuShortcuts())
2743 pPopup->SetAccelKey(pPopup->GetItemId(u"undo"), vcl::KeyCode( KeyFuncType::UNDO));
2744 pPopup->SetAccelKey(pPopup->GetItemId(u"cut"), vcl::KeyCode( KeyFuncType::CUT));
2745 pPopup->SetAccelKey(pPopup->GetItemId(u"copy"), vcl::KeyCode( KeyFuncType::COPY));
2746 pPopup->SetAccelKey(pPopup->GetItemId(u"paste"), vcl::KeyCode( KeyFuncType::PASTE));
2747 pPopup->SetAccelKey(pPopup->GetItemId(u"delete"), vcl::KeyCode( KeyFuncType::DELETE));
2748 pPopup->SetAccelKey(pPopup->GetItemId(u"selectall"), vcl::KeyCode( KEY_A, false, true, false, false));
2749 pPopup->SetAccelKey(pPopup->GetItemId(u"specialchar"), vcl::KeyCode( KEY_S, true, true, false, false));
2751 return pPopup;
2754 // css::datatransfer::dnd::XDragGestureListener
2755 void Edit::dragGestureRecognized( const css::datatransfer::dnd::DragGestureEvent& rDGE )
2757 SolarMutexGuard aVclGuard;
2759 if ( !(!IsTracking() && maSelection.Len() &&
2760 !mbPassword && (!mpDDInfo || !mpDDInfo->bStarterOfDD)) ) // no repeated D&D
2761 return;
2763 Selection aSel( maSelection );
2764 aSel.Normalize();
2766 // only if mouse in the selection...
2767 Point aMousePos( rDGE.DragOriginX, rDGE.DragOriginY );
2768 sal_Int32 nCharPos = ImplGetCharPos( aMousePos );
2769 if ( (nCharPos < aSel.Min()) || (nCharPos >= aSel.Max()) )
2770 return;
2772 if ( !mpDDInfo )
2773 mpDDInfo.reset(new DDInfo);
2775 mpDDInfo->bStarterOfDD = true;
2776 mpDDInfo->aDndStartSel = aSel;
2778 if ( IsTracking() )
2779 EndTracking(); // before D&D disable tracking
2781 rtl::Reference<vcl::unohelper::TextDataObject> pDataObj = new vcl::unohelper::TextDataObject( GetSelected() );
2782 sal_Int8 nActions = datatransfer::dnd::DNDConstants::ACTION_COPY;
2783 if ( !IsReadOnly() )
2784 nActions |= datatransfer::dnd::DNDConstants::ACTION_MOVE;
2785 rDGE.DragSource->startDrag( rDGE, nActions, 0 /*cursor*/, 0 /*image*/, pDataObj, mxDnDListener );
2786 if ( GetCursor() )
2787 GetCursor()->Hide();
2790 // css::datatransfer::dnd::XDragSourceListener
2791 void Edit::dragDropEnd( const css::datatransfer::dnd::DragSourceDropEvent& rDSDE )
2793 SolarMutexGuard aVclGuard;
2795 if (rDSDE.DropSuccess && (rDSDE.DropAction & datatransfer::dnd::DNDConstants::ACTION_MOVE) && mpDDInfo)
2797 Selection aSel( mpDDInfo->aDndStartSel );
2798 if ( mpDDInfo->bDroppedInMe )
2800 if ( aSel.Max() > mpDDInfo->nDropPos )
2802 tools::Long nLen = aSel.Len();
2803 aSel.Min() += nLen;
2804 aSel.Max() += nLen;
2807 ImplDelete( aSel, EDIT_DEL_RIGHT, EDIT_DELMODE_SIMPLE );
2808 Modify();
2811 ImplHideDDCursor();
2812 mpDDInfo.reset();
2815 // css::datatransfer::dnd::XDropTargetListener
2816 void Edit::drop( const css::datatransfer::dnd::DropTargetDropEvent& rDTDE )
2818 SolarMutexGuard aVclGuard;
2820 bool bChanges = false;
2821 if ( !mbReadOnly && mpDDInfo )
2823 ImplHideDDCursor();
2825 Selection aSel( maSelection );
2826 aSel.Normalize();
2828 if ( aSel.Len() && !mpDDInfo->bStarterOfDD )
2829 ImplDelete( aSel, EDIT_DEL_RIGHT, EDIT_DELMODE_SIMPLE );
2831 mpDDInfo->bDroppedInMe = true;
2833 aSel.Min() = mpDDInfo->nDropPos;
2834 aSel.Max() = mpDDInfo->nDropPos;
2835 ImplSetSelection( aSel );
2837 uno::Reference< datatransfer::XTransferable > xDataObj = rDTDE.Transferable;
2838 if ( xDataObj.is() )
2840 datatransfer::DataFlavor aFlavor;
2841 SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING, aFlavor );
2842 if ( xDataObj->isDataFlavorSupported( aFlavor ) )
2844 uno::Any aData = xDataObj->getTransferData( aFlavor );
2845 OUString aText;
2846 aData >>= aText;
2847 ImplInsertText( aText );
2848 bChanges = true;
2849 Modify();
2853 if ( !mpDDInfo->bStarterOfDD )
2855 mpDDInfo.reset();
2859 rDTDE.Context->dropComplete( bChanges );
2862 void Edit::dragEnter( const css::datatransfer::dnd::DropTargetDragEnterEvent& rDTDE )
2864 if ( !mpDDInfo )
2866 mpDDInfo.reset(new DDInfo);
2868 // search for string data type
2869 const Sequence< css::datatransfer::DataFlavor >& rFlavors( rDTDE.SupportedDataFlavors );
2870 mpDDInfo->bIsStringSupported = std::any_of(rFlavors.begin(), rFlavors.end(),
2871 [](const css::datatransfer::DataFlavor& rFlavor) {
2872 sal_Int32 nIndex = 0;
2873 const std::u16string_view aMimetype = o3tl::getToken(rFlavor.MimeType, 0, ';', nIndex );
2874 return aMimetype == u"text/plain";
2878 void Edit::dragExit( const css::datatransfer::dnd::DropTargetEvent& )
2880 SolarMutexGuard aVclGuard;
2882 ImplHideDDCursor();
2885 void Edit::dragOver( const css::datatransfer::dnd::DropTargetDragEvent& rDTDE )
2887 SolarMutexGuard aVclGuard;
2889 Point aMousePos( rDTDE.LocationX, rDTDE.LocationY );
2891 sal_Int32 nPrevDropPos = mpDDInfo->nDropPos;
2892 mpDDInfo->nDropPos = ImplGetCharPos( aMousePos );
2895 Size aOutSize = GetOutputSizePixel();
2896 if ( ( aMousePos.X() < 0 ) || ( aMousePos.X() > aOutSize.Width() ) )
2898 // Scroll?
2899 // No, I will not receive events in this case...
2903 Selection aSel( maSelection );
2904 aSel.Normalize();
2906 // Don't accept drop in selection or read-only field...
2907 if ( IsReadOnly() || aSel.Contains( mpDDInfo->nDropPos ) || ! mpDDInfo->bIsStringSupported )
2909 ImplHideDDCursor();
2910 rDTDE.Context->rejectDrag();
2912 else
2914 // draw the old cursor away...
2915 if ( !mpDDInfo->bVisCursor || ( nPrevDropPos != mpDDInfo->nDropPos ) )
2917 ImplHideDDCursor();
2918 ImplShowDDCursor();
2920 rDTDE.Context->acceptDrag( rDTDE.DropAction );
2924 OUString Edit::GetSurroundingText() const
2926 if (mpSubEdit)
2927 return mpSubEdit->GetSurroundingText();
2928 return maText.toString();
2931 Selection Edit::GetSurroundingTextSelection() const
2933 return GetSelection();
2936 bool Edit::DeleteSurroundingText(const Selection& rSelection)
2938 SetSelection(rSelection);
2939 DeleteSelected();
2940 // maybe we should update mpIMEInfos here
2941 return true;
2944 FactoryFunction Edit::GetUITestFactory() const
2946 return EditUIObject::create;
2950 void Edit::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
2952 Control::DumpAsPropertyTree(rJsonWriter);
2954 if (!maPlaceholderText.isEmpty())
2955 rJsonWriter.put("placeholder", maPlaceholderText);
2957 if (IsPassword())
2958 rJsonWriter.put("password", true);
2961 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */