Simplify using designated initializers
[LibreOffice.git] / vcl / source / control / edit.cxx
blob563d47d06654e6992bc5ec1027f669902d0f4e5e
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 GetDragGestureRecognizer()->removeDragGestureListener( mxDnDListener );
245 if ( GetDropTarget().is() )
247 GetDropTarget()->removeDropTargetListener( mxDnDListener );
250 mxDnDListener->disposing( lang::EventObject() ); // #95154# #96585# Empty Source means it's the Client
251 mxDnDListener.clear();
254 SetType(WindowType::WINDOW);
256 mpSubEdit.disposeAndClear();
257 Control::dispose();
260 void Edit::ImplInitEditData()
262 mpSubEdit = VclPtr<Edit>();
263 mpFilterText = nullptr;
264 mnXOffset = 0;
265 mnAlign = EDIT_ALIGN_LEFT;
266 mnMaxTextLen = EDIT_NOLIMIT;
267 mnWidthInChars = -1;
268 mnMaxWidthChars = -1;
269 mbInternModified = false;
270 mbReadOnly = false;
271 mbInsertMode = true;
272 mbClickedInSelection = false;
273 mbActivePopup = false;
274 mbIsSubEdit = false;
275 mbForceControlBackground = false;
276 mbPassword = false;
277 mpDDInfo = nullptr;
278 mpIMEInfos = nullptr;
279 mcEchoChar = 0;
281 // no default mirroring for Edit controls
282 // note: controls that use a subedit will revert this (SpinField, ComboBox)
283 EnableRTL( false );
285 mxDnDListener = new vcl::unohelper::DragAndDropWrapper( this );
288 bool Edit::ImplUseNativeBorder(vcl::RenderContext const & rRenderContext, WinBits nStyle) const
290 bool bRet = rRenderContext.IsNativeControlSupported(ImplGetNativeControlType(),
291 ControlPart::HasBackgroundTexture)
292 && ((nStyle & WB_BORDER) && !(nStyle & WB_NOBORDER));
293 if (!bRet && mbIsSubEdit)
295 vcl::Window* pWindow = GetParent();
296 nStyle = pWindow->GetStyle();
297 bRet = pWindow->IsNativeControlSupported(ImplGetNativeControlType(),
298 ControlPart::HasBackgroundTexture)
299 && ((nStyle & WB_BORDER) && !(nStyle & WB_NOBORDER));
301 return bRet;
304 void Edit::ImplInit(vcl::Window* pParent, WinBits nStyle)
306 nStyle = ImplInitStyle(nStyle);
308 if (!(nStyle & (WB_CENTER | WB_RIGHT)))
309 nStyle |= WB_LEFT;
311 Control::ImplInit(pParent, nStyle, nullptr);
313 mbReadOnly = (nStyle & WB_READONLY) != 0;
315 mnAlign = EDIT_ALIGN_LEFT;
317 // hack: right align until keyinput and cursor travelling works
318 if( IsRTLEnabled() )
319 mnAlign = EDIT_ALIGN_RIGHT;
321 if ( nStyle & WB_RIGHT )
322 mnAlign = EDIT_ALIGN_RIGHT;
323 else if ( nStyle & WB_CENTER )
324 mnAlign = EDIT_ALIGN_CENTER;
326 SetCursor( new vcl::Cursor );
328 SetPointer( PointerStyle::Text );
329 ApplySettings(*GetOutDev());
331 uno::Reference< datatransfer::dnd::XDragGestureRecognizer > xDGR = GetDragGestureRecognizer();
332 if ( xDGR.is() )
334 xDGR->addDragGestureListener( mxDnDListener );
335 GetDropTarget()->addDropTargetListener( mxDnDListener );
336 GetDropTarget()->setActive( true );
337 GetDropTarget()->setDefaultActions( datatransfer::dnd::DNDConstants::ACTION_COPY_OR_MOVE );
341 WinBits Edit::ImplInitStyle( WinBits nStyle )
343 if ( !(nStyle & WB_NOTABSTOP) )
344 nStyle |= WB_TABSTOP;
345 if ( !(nStyle & WB_NOGROUP) )
346 nStyle |= WB_GROUP;
348 return nStyle;
351 bool Edit::IsCharInput( const KeyEvent& rKeyEvent )
353 // In the future we must use new Unicode functions for this
354 sal_Unicode cCharCode = rKeyEvent.GetCharCode();
355 return ((cCharCode >= 32) && (cCharCode != 127) &&
356 !rKeyEvent.GetKeyCode().IsMod3() &&
357 !rKeyEvent.GetKeyCode().IsMod2() &&
358 !rKeyEvent.GetKeyCode().IsMod1() );
361 void Edit::ApplySettings(vcl::RenderContext& rRenderContext)
363 Control::ApplySettings(rRenderContext);
365 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
367 const vcl::Font& aFont = rStyleSettings.GetFieldFont();
368 ApplyControlFont(rRenderContext, aFont);
370 ImplClearLayoutData();
372 Color aTextColor = rStyleSettings.GetFieldTextColor();
373 ApplyControlForeground(rRenderContext, aTextColor);
375 if (IsControlBackground())
377 rRenderContext.SetBackground(GetControlBackground());
378 rRenderContext.SetFillColor(GetControlBackground());
380 if (ImplUseNativeBorder(rRenderContext, GetStyle()))
382 // indicates that no non-native drawing of background should take place
383 mpWindowImpl->mnNativeBackground = ControlPart::Entire;
386 else if (ImplUseNativeBorder(rRenderContext, GetStyle()))
388 // Transparent background
389 rRenderContext.SetBackground();
390 rRenderContext.SetFillColor();
392 else
394 rRenderContext.SetBackground(rStyleSettings.GetFieldColor());
395 rRenderContext.SetFillColor(rStyleSettings.GetFieldColor());
399 tools::Long Edit::ImplGetExtraXOffset() const
401 // MT 09/2002: nExtraOffsetX should become a member, instead of checking every time,
402 // but I need an incompatible update for this...
403 // #94095# Use extra offset only when edit has a border
404 tools::Long nExtraOffset = 0;
405 if( ( GetStyle() & WB_BORDER ) || ( mbIsSubEdit && ( GetParent()->GetStyle() & WB_BORDER ) ) )
406 nExtraOffset = 2;
408 return nExtraOffset;
411 tools::Long Edit::ImplGetExtraYOffset() const
413 tools::Long nExtraOffset = 0;
414 ControlType eCtrlType = ImplGetNativeControlType();
415 if (eCtrlType != ControlType::EditboxNoBorder)
417 // add some space between text entry and border
418 nExtraOffset = 2;
420 return nExtraOffset;
423 OUString Edit::ImplGetText() const
425 if ( mcEchoChar || mbPassword )
427 sal_Unicode cEchoChar;
428 if ( mcEchoChar )
429 cEchoChar = mcEchoChar;
430 else
431 cEchoChar = u'\x2022';
432 OUStringBuffer aText(maText.getLength());
433 comphelper::string::padToLength(aText, maText.getLength(), cEchoChar);
434 return aText.makeStringAndClear();
436 else
437 return maText.toString();
440 void Edit::ImplInvalidateOrRepaint()
442 if( IsPaintTransparent() )
444 Invalidate();
445 // FIXME: this is currently only on macOS
446 if( ImplGetSVData()->maNWFData.mbNoFocusRects )
447 PaintImmediately();
449 else
450 Invalidate();
453 tools::Long Edit::ImplGetTextYPosition() const
455 if ( GetStyle() & WB_TOP )
456 return ImplGetExtraXOffset();
457 else if ( GetStyle() & WB_BOTTOM )
458 return GetOutputSizePixel().Height() - GetTextHeight() - ImplGetExtraXOffset();
459 return ( GetOutputSizePixel().Height() - GetTextHeight() ) / 2;
462 void Edit::ImplRepaint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRectangle)
464 if (!IsReallyVisible())
465 return;
467 ApplySettings(rRenderContext);
469 const OUString aText = ImplGetText();
470 const sal_Int32 nLen = aText.getLength();
472 KernArray aDX;
473 if (nLen)
474 GetOutDev()->GetCaretPositions(aText, aDX, 0, nLen);
476 tools::Long nTH = GetTextHeight();
477 Point aPos(mnXOffset, ImplGetTextYPosition());
479 vcl::Cursor* pCursor = GetCursor();
480 bool bVisCursor = pCursor && pCursor->IsVisible();
481 if (pCursor)
482 pCursor->Hide();
484 ImplClearBackground(rRenderContext, rRectangle, 0, GetOutputSizePixel().Width()-1);
486 bool bPaintPlaceholderText = aText.isEmpty() && !maPlaceholderText.isEmpty();
488 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
490 if (!IsEnabled() || bPaintPlaceholderText)
491 rRenderContext.SetTextColor(rStyleSettings.GetDisableColor());
493 // Set background color of the normal text
494 if (mbForceControlBackground && IsControlBackground())
496 // check if we need to set ControlBackground even in NWF case
497 rRenderContext.Push(vcl::PushFlags::FILLCOLOR | vcl::PushFlags::LINECOLOR);
498 rRenderContext.SetLineColor();
499 rRenderContext.SetFillColor(GetControlBackground());
500 rRenderContext.DrawRect(tools::Rectangle(aPos, Size(GetOutputSizePixel().Width() - 2 * mnXOffset, GetOutputSizePixel().Height())));
501 rRenderContext.Pop();
503 rRenderContext.SetTextFillColor(GetControlBackground());
505 else if (IsPaintTransparent() || ImplUseNativeBorder(rRenderContext, GetStyle()))
506 rRenderContext.SetTextFillColor();
507 else
508 rRenderContext.SetTextFillColor(IsControlBackground() ? GetControlBackground() : rStyleSettings.GetFieldColor());
510 ImplPaintBorder(rRenderContext);
512 bool bDrawSelection = maSelection.Len() && (HasFocus() || (GetStyle() & WB_NOHIDESELECTION) || mbActivePopup);
514 aPos.setX( mnXOffset + ImplGetExtraXOffset() );
515 if (bPaintPlaceholderText)
517 rRenderContext.DrawText(aPos, maPlaceholderText);
519 else if (!bDrawSelection && !mpIMEInfos)
521 rRenderContext.DrawText(aPos, aText, 0, nLen);
523 else
525 // save graphics state
526 rRenderContext.Push();
527 // first calculate highlighted and non highlighted clip regions
528 vcl::Region aHighlightClipRegion;
529 vcl::Region aNormalClipRegion;
530 Selection aTmpSel(maSelection);
531 aTmpSel.Normalize();
532 // selection is highlighted
533 for(sal_Int32 i = 0; i < nLen; ++i)
535 tools::Rectangle aRect(aPos, Size(10, nTH));
536 aRect.SetLeft( aDX[2 * i] + mnXOffset + ImplGetExtraXOffset() );
537 aRect.SetRight( aDX[2 * i + 1] + mnXOffset + ImplGetExtraXOffset() );
538 aRect.Normalize();
539 bool bHighlight = false;
540 if (i >= aTmpSel.Min() && i < aTmpSel.Max())
541 bHighlight = true;
543 if (mpIMEInfos && mpIMEInfos->pAttribs &&
544 i >= mpIMEInfos->nPos && i < (mpIMEInfos->nPos+mpIMEInfos->nLen) &&
545 (mpIMEInfos->pAttribs[i - mpIMEInfos->nPos] & ExtTextInputAttr::Highlight))
547 bHighlight = true;
550 if (bHighlight)
551 aHighlightClipRegion.Union(aRect);
552 else
553 aNormalClipRegion.Union(aRect);
555 // draw normal text
556 Color aNormalTextColor = rRenderContext.GetTextColor();
557 rRenderContext.SetClipRegion(aNormalClipRegion);
559 if (IsPaintTransparent())
560 rRenderContext.SetTextFillColor();
561 else
563 // Set background color when part of the text is selected
564 if (ImplUseNativeBorder(rRenderContext, GetStyle()))
566 if( mbForceControlBackground && IsControlBackground() )
567 rRenderContext.SetTextFillColor(GetControlBackground());
568 else
569 rRenderContext.SetTextFillColor();
571 else
573 rRenderContext.SetTextFillColor(IsControlBackground() ? GetControlBackground() : rStyleSettings.GetFieldColor());
576 rRenderContext.DrawText(aPos, aText, 0, nLen);
578 // draw highlighted text
579 rRenderContext.SetClipRegion(aHighlightClipRegion);
580 rRenderContext.SetTextColor(rStyleSettings.GetHighlightTextColor());
581 rRenderContext.SetTextFillColor(rStyleSettings.GetHighlightColor());
582 rRenderContext.DrawText(aPos, aText, 0, nLen);
584 // if IME info exists loop over portions and output different font attributes
585 if (mpIMEInfos && mpIMEInfos->pAttribs)
587 for(int n = 0; n < 2; n++)
589 vcl::Region aRegion;
590 if (n == 0)
592 rRenderContext.SetTextColor(aNormalTextColor);
593 if (IsPaintTransparent())
594 rRenderContext.SetTextFillColor();
595 else
596 rRenderContext.SetTextFillColor(IsControlBackground() ? GetControlBackground() : rStyleSettings.GetFieldColor());
597 aRegion = aNormalClipRegion;
599 else
601 rRenderContext.SetTextColor(rStyleSettings.GetHighlightTextColor());
602 rRenderContext.SetTextFillColor(rStyleSettings.GetHighlightColor());
603 aRegion = aHighlightClipRegion;
606 for(int i = 0; i < mpIMEInfos->nLen; )
608 ExtTextInputAttr nAttr = mpIMEInfos->pAttribs[i];
609 vcl::Region aClip;
610 int nIndex = i;
611 while (nIndex < mpIMEInfos->nLen && mpIMEInfos->pAttribs[nIndex] == nAttr) // #112631# check nIndex before using it
613 tools::Rectangle aRect( aPos, Size( 10, nTH ) );
614 aRect.SetLeft( aDX[2 * (nIndex + mpIMEInfos->nPos)] + mnXOffset + ImplGetExtraXOffset() );
615 aRect.SetRight( aDX[2 * (nIndex + mpIMEInfos->nPos) + 1] + mnXOffset + ImplGetExtraXOffset() );
616 aRect.Normalize();
617 aClip.Union(aRect);
618 nIndex++;
620 i = nIndex;
621 aClip.Intersect(aRegion);
622 if (!aClip.IsEmpty() && nAttr != ExtTextInputAttr::NONE)
624 vcl::Font aFont = rRenderContext.GetFont();
625 if (nAttr & ExtTextInputAttr::Underline)
626 aFont.SetUnderline(LINESTYLE_SINGLE);
627 else if (nAttr & ExtTextInputAttr::DoubleUnderline)
628 aFont.SetUnderline(LINESTYLE_DOUBLE);
629 else if (nAttr & ExtTextInputAttr::BoldUnderline)
630 aFont.SetUnderline( LINESTYLE_BOLD);
631 else if (nAttr & ExtTextInputAttr::DottedUnderline)
632 aFont.SetUnderline( LINESTYLE_DOTTED);
633 else if (nAttr & ExtTextInputAttr::DashDotUnderline)
634 aFont.SetUnderline( LINESTYLE_DASHDOT);
635 else if (nAttr & ExtTextInputAttr::GrayWaveline)
637 aFont.SetUnderline(LINESTYLE_WAVE);
638 rRenderContext.SetTextLineColor(COL_LIGHTGRAY);
640 rRenderContext.SetFont(aFont);
642 if (nAttr & ExtTextInputAttr::RedText)
643 rRenderContext.SetTextColor(COL_RED);
644 else if (nAttr & ExtTextInputAttr::HalfToneText)
645 rRenderContext.SetTextColor(COL_LIGHTGRAY);
647 rRenderContext.SetClipRegion(aClip);
648 rRenderContext.DrawText(aPos, aText, 0, nLen);
654 // restore graphics state
655 rRenderContext.Pop();
658 if (bVisCursor && (!mpIMEInfos || mpIMEInfos->bCursor))
659 pCursor->Show();
662 void Edit::ImplDelete( const Selection& rSelection, sal_uInt8 nDirection, sal_uInt8 nMode )
664 const sal_Int32 nTextLen = ImplGetText().getLength();
666 // deleting possible?
667 if ( !rSelection.Len() &&
668 (((rSelection.Min() == 0) && (nDirection == EDIT_DEL_LEFT)) ||
669 ((rSelection.Max() == nTextLen) && (nDirection == EDIT_DEL_RIGHT))) )
670 return;
672 ImplClearLayoutData();
674 Selection aSelection( rSelection );
675 aSelection.Normalize();
677 if ( !aSelection.Len() )
679 uno::Reference < i18n::XBreakIterator > xBI = ImplGetBreakIterator();
680 if ( nDirection == EDIT_DEL_LEFT )
682 if ( nMode == EDIT_DELMODE_RESTOFWORD )
684 const OUString sText = maText.toString();
685 i18n::Boundary aBoundary = xBI->getWordBoundary( sText, aSelection.Min(),
686 GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES, true );
687 auto startPos = aBoundary.startPos;
688 if ( startPos == aSelection.Min() )
690 aBoundary = xBI->previousWord( sText, aSelection.Min(),
691 GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES );
692 startPos = std::max(aBoundary.startPos, sal_Int32(0));
694 aSelection.Min() = startPos;
696 else if ( nMode == EDIT_DELMODE_RESTOFCONTENT )
698 aSelection.Min() = 0;
700 else
702 sal_Int32 nCount = 1;
703 aSelection.Min() = xBI->previousCharacters( maText.toString(), aSelection.Min(),
704 GetSettings().GetLanguageTag().getLocale(), i18n::CharacterIteratorMode::SKIPCHARACTER, nCount, nCount );
707 else
709 if ( nMode == EDIT_DELMODE_RESTOFWORD )
711 i18n::Boundary aBoundary = xBI->nextWord( maText.toString(), aSelection.Max(),
712 GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES );
713 aSelection.Max() = aBoundary.startPos;
715 else if ( nMode == EDIT_DELMODE_RESTOFCONTENT )
717 aSelection.Max() = nTextLen;
719 else
721 sal_Int32 nCount = 1;
722 aSelection.Max() = xBI->nextCharacters( maText.toString(), aSelection.Max(),
723 GetSettings().GetLanguageTag().getLocale(), i18n::CharacterIteratorMode::SKIPCHARACTER, nCount, nCount );
728 const auto nSelectionMin = aSelection.Min();
729 maText.remove( nSelectionMin, aSelection.Len() );
730 maSelection.Min() = nSelectionMin;
731 maSelection.Max() = nSelectionMin;
732 ImplAlignAndPaint();
733 mbInternModified = true;
736 OUString Edit::ImplGetValidString( const OUString& rString )
738 OUString aValidString = rString.replaceAll("\n", "").replaceAll("\r", "");
739 aValidString = aValidString.replace('\t', ' ');
740 return aValidString;
743 uno::Reference <i18n::XBreakIterator> const& Edit::ImplGetBreakIterator()
745 if (!mxBreakIterator)
746 mxBreakIterator = i18n::BreakIterator::create(::comphelper::getProcessComponentContext());
747 return mxBreakIterator;
750 uno::Reference <i18n::XExtendedInputSequenceChecker> const& Edit::ImplGetInputSequenceChecker()
752 if (!mxISC.is())
753 mxISC = i18n::InputSequenceChecker::create(::comphelper::getProcessComponentContext());
754 return mxISC;
757 void Edit::ShowTruncationWarning(weld::Widget* pParent)
759 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pParent, VclMessageType::Warning,
760 VclButtonsType::Ok, VclResId(SV_EDIT_WARNING_STR)));
761 xBox->run();
764 bool Edit::ImplTruncateToMaxLen( OUString& rStr, sal_Int32 nSelectionLen ) const
766 bool bWasTruncated = false;
767 if (maText.getLength() - nSelectionLen > mnMaxTextLen - rStr.getLength())
769 sal_Int32 nErasePos = mnMaxTextLen - maText.getLength() + nSelectionLen;
770 rStr = rStr.copy( 0, nErasePos );
771 bWasTruncated = true;
773 return bWasTruncated;
776 void Edit::ImplInsertText( const OUString& rStr, const Selection* pNewSel, bool bIsUserInput )
778 Selection aSelection( maSelection );
779 aSelection.Normalize();
781 OUString aNewText( ImplGetValidString( rStr ) );
783 // as below, if there's no selection, but we're in overwrite mode and not beyond
784 // the end of the existing text then that's like a selection of 1
785 auto nSelectionLen = aSelection.Len();
786 if (!nSelectionLen && !mbInsertMode && aSelection.Max() < maText.getLength())
787 nSelectionLen = 1;
788 ImplTruncateToMaxLen( aNewText, nSelectionLen );
790 ImplClearLayoutData();
792 if ( aSelection.Len() )
793 maText.remove( aSelection.Min(), aSelection.Len() );
794 else if (!mbInsertMode && aSelection.Max() < maText.getLength())
795 maText.remove( aSelection.Max(), 1 );
797 // take care of input-sequence-checking now
798 if (bIsUserInput && !rStr.isEmpty())
800 SAL_WARN_IF( rStr.getLength() != 1, "vcl", "unexpected string length. User input is expected to provide 1 char only!" );
802 // determine if input-sequence-checking should be applied or not
804 uno::Reference < i18n::XBreakIterator > xBI = ImplGetBreakIterator();
805 bool bIsInputSequenceChecking = rStr.getLength() == 1 &&
806 officecfg::Office::Common::I18N::CTL::CTLFont::get() &&
807 officecfg::Office::Common::I18N::CTL::CTLSequenceChecking::get() &&
808 aSelection.Min() > 0 && /* first char needs not to be checked */
809 xBI.is() && i18n::ScriptType::COMPLEX == xBI->getScriptType( rStr, 0 );
811 if (bIsInputSequenceChecking)
813 uno::Reference < i18n::XExtendedInputSequenceChecker > xISC = ImplGetInputSequenceChecker();
814 if (xISC.is())
816 sal_Unicode cChar = rStr[0];
817 sal_Int32 nTmpPos = aSelection.Min();
818 sal_Int16 nCheckMode = officecfg::Office::Common::I18N::CTL::CTLSequenceCheckingRestricted::get()?
819 i18n::InputSequenceCheckMode::STRICT : i18n::InputSequenceCheckMode::BASIC;
821 // the text that needs to be checked is only the one
822 // before the current cursor position
823 const OUString aOldText( maText.subView(0, nTmpPos) );
824 OUString aTmpText( aOldText );
825 if (officecfg::Office::Common::I18N::CTL::CTLSequenceCheckingTypeAndReplace::get())
827 xISC->correctInputSequence( aTmpText, nTmpPos - 1, cChar, nCheckMode );
829 // find position of first character that has changed
830 sal_Int32 nOldLen = aOldText.getLength();
831 sal_Int32 nTmpLen = aTmpText.getLength();
832 const sal_Unicode *pOldTxt = aOldText.getStr();
833 const sal_Unicode *pTmpTxt = aTmpText.getStr();
834 sal_Int32 nChgPos = 0;
835 while ( nChgPos < nOldLen && nChgPos < nTmpLen &&
836 pOldTxt[nChgPos] == pTmpTxt[nChgPos] )
837 ++nChgPos;
839 const OUString aChgText( aTmpText.copy( nChgPos ) );
841 // remove text from first pos to be changed to current pos
842 maText.remove( nChgPos, nTmpPos - nChgPos );
844 if (!aChgText.isEmpty())
846 aNewText = aChgText;
847 aSelection.Min() = nChgPos; // position for new text to be inserted
849 else
850 aNewText.clear();
852 else
854 // should the character be ignored (i.e. not get inserted) ?
855 if (!xISC->checkInputSequence( aOldText, nTmpPos - 1, cChar, nCheckMode ))
856 aNewText.clear();
861 // at this point now we will insert the non-empty text 'normally' some lines below...
864 if ( !aNewText.isEmpty() )
865 maText.insert( aSelection.Min(), aNewText );
867 if ( !pNewSel )
869 maSelection.Min() = aSelection.Min() + aNewText.getLength();
870 maSelection.Max() = maSelection.Min();
872 else
874 maSelection = *pNewSel;
875 if ( maSelection.Min() > maText.getLength() )
876 maSelection.Min() = maText.getLength();
877 if ( maSelection.Max() > maText.getLength() )
878 maSelection.Max() = maText.getLength();
881 ImplAlignAndPaint();
882 mbInternModified = true;
885 void Edit::ImplSetText( const OUString& rText, const Selection* pNewSelection )
887 // we delete text by "selecting" the old text completely then calling InsertText; this is flicker free
888 if ( ( rText.getLength() > mnMaxTextLen ) ||
889 ( std::u16string_view(rText) == std::u16string_view(maText)
890 && (!pNewSelection || (*pNewSelection == maSelection)) ) )
891 return;
893 ImplClearLayoutData();
894 maSelection.Min() = 0;
895 maSelection.Max() = maText.getLength();
896 if ( mnXOffset || HasPaintEvent() )
898 mnXOffset = 0;
899 maText = ImplGetValidString( rText );
901 // #i54929# recalculate mnXOffset before ImplSetSelection,
902 // else cursor ends up in wrong position
903 ImplAlign();
905 if ( pNewSelection )
906 ImplSetSelection( *pNewSelection, false );
908 if ( mnXOffset && !pNewSelection )
909 maSelection.Max() = 0;
911 Invalidate();
913 else
914 ImplInsertText( rText, pNewSelection );
916 CallEventListeners( VclEventId::EditModify );
919 ControlType Edit::ImplGetNativeControlType() const
921 ControlType nCtrl = ControlType::Generic;
922 const vcl::Window* pControl = mbIsSubEdit ? GetParent() : this;
924 switch (pControl->GetType())
926 case WindowType::COMBOBOX:
927 case WindowType::PATTERNBOX:
928 case WindowType::NUMERICBOX:
929 case WindowType::METRICBOX:
930 case WindowType::CURRENCYBOX:
931 case WindowType::DATEBOX:
932 case WindowType::TIMEBOX:
933 case WindowType::LONGCURRENCYBOX:
934 nCtrl = ControlType::Combobox;
935 break;
937 case WindowType::MULTILINEEDIT:
938 if ( GetWindow( GetWindowType::Border ) != this )
939 nCtrl = ControlType::MultilineEditbox;
940 else
941 nCtrl = ControlType::EditboxNoBorder;
942 break;
944 case WindowType::EDIT:
945 case WindowType::PATTERNFIELD:
946 case WindowType::METRICFIELD:
947 case WindowType::CURRENCYFIELD:
948 case WindowType::DATEFIELD:
949 case WindowType::TIMEFIELD:
950 case WindowType::SPINFIELD:
951 case WindowType::FORMATTEDFIELD:
952 if (pControl->GetStyle() & WB_SPIN)
953 nCtrl = ControlType::Spinbox;
954 else
956 if (GetWindow(GetWindowType::Border) != this)
957 nCtrl = ControlType::Editbox;
958 else
959 nCtrl = ControlType::EditboxNoBorder;
961 break;
963 default:
964 nCtrl = ControlType::Editbox;
966 return nCtrl;
969 void Edit::ImplClearBackground(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRectangle, tools::Long nXStart, tools::Long nXEnd )
972 * note: at this point the cursor must be switched off already
974 tools::Rectangle aRect(Point(), GetOutputSizePixel());
975 aRect.SetLeft( nXStart );
976 aRect.SetRight( nXEnd );
978 if( !(ImplUseNativeBorder(rRenderContext, GetStyle()) || IsPaintTransparent()))
979 rRenderContext.Erase(aRect);
980 else if (SupportsDoubleBuffering() && mbIsSubEdit)
982 // ImplPaintBorder() is a NOP, we have a native border, and this is a sub-edit of a control.
983 // That means we have to draw the parent native widget to paint the edit area to clear our background.
984 vcl::PaintBufferGuard g(ImplGetWindowImpl()->mpFrameData, GetParent());
985 GetParent()->Paint(rRenderContext, rRectangle);
989 void Edit::ImplPaintBorder(vcl::RenderContext const & rRenderContext)
991 // this is not needed when double-buffering
992 if (SupportsDoubleBuffering())
993 return;
995 if (!(ImplUseNativeBorder(rRenderContext, GetStyle()) || IsPaintTransparent()))
996 return;
998 // draw the inner part by painting the whole control using its border window
999 vcl::Window* pBorder = GetWindow(GetWindowType::Border);
1000 if (pBorder == this)
1002 // we have no border, use parent
1003 vcl::Window* pControl = mbIsSubEdit ? GetParent() : this;
1004 pBorder = pControl->GetWindow(GetWindowType::Border);
1005 if (pBorder == this)
1006 pBorder = GetParent();
1009 if (!pBorder)
1010 return;
1012 // set proper clipping region to not overdraw the whole control
1013 vcl::Region aClipRgn = GetPaintRegion();
1014 if (!aClipRgn.IsNull())
1016 // transform clipping region to border window's coordinate system
1017 if (IsRTLEnabled() != pBorder->IsRTLEnabled() && AllSettings::GetLayoutRTL())
1019 // need to mirror in case border is not RTL but edit is (or vice versa)
1021 // mirror
1022 tools::Rectangle aBounds(aClipRgn.GetBoundRect());
1023 int xNew = GetOutputSizePixel().Width() - aBounds.GetWidth() - aBounds.Left();
1024 aClipRgn.Move(xNew - aBounds.Left(), 0);
1026 // move offset of border window
1027 Point aBorderOffs = pBorder->ScreenToOutputPixel(OutputToScreenPixel(Point()));
1028 aClipRgn.Move(aBorderOffs.X(), aBorderOffs.Y());
1030 else
1032 // normal case
1033 Point aBorderOffs = pBorder->ScreenToOutputPixel(OutputToScreenPixel(Point()));
1034 aClipRgn.Move(aBorderOffs.X(), aBorderOffs.Y());
1037 vcl::Region oldRgn(pBorder->GetOutDev()->GetClipRegion());
1038 pBorder->GetOutDev()->SetClipRegion(aClipRgn);
1040 pBorder->Paint(*pBorder->GetOutDev(), tools::Rectangle());
1042 pBorder->GetOutDev()->SetClipRegion(oldRgn);
1044 else
1046 pBorder->Paint(*pBorder->GetOutDev(), tools::Rectangle());
1050 void Edit::ImplShowCursor( bool bOnlyIfVisible )
1052 if ( !IsUpdateMode() || ( bOnlyIfVisible && !IsReallyVisible() ) )
1053 return;
1055 vcl::Cursor* pCursor = GetCursor();
1056 OUString aText = ImplGetText();
1058 tools::Long nTextPos = 0;
1060 if( !aText.isEmpty() )
1062 KernArray aDX;
1063 GetOutDev()->GetCaretPositions(aText, aDX, 0, aText.getLength());
1065 if( maSelection.Max() < aText.getLength() )
1066 nTextPos = aDX[ 2*maSelection.Max() ];
1067 else
1068 nTextPos = aDX[ 2*aText.getLength()-1 ];
1071 tools::Long nCursorWidth = 0;
1072 if ( !mbInsertMode && !maSelection.Len() && (maSelection.Max() < aText.getLength()) )
1073 nCursorWidth = GetTextWidth(aText, maSelection.Max(), 1);
1074 tools::Long nCursorPosX = nTextPos + mnXOffset + ImplGetExtraXOffset();
1076 // cursor should land in visible area
1077 const Size aOutSize = GetOutputSizePixel();
1078 if ( (nCursorPosX < 0) || (nCursorPosX >= aOutSize.Width()) )
1080 tools::Long nOldXOffset = mnXOffset;
1082 if ( nCursorPosX < 0 )
1084 mnXOffset = - nTextPos;
1085 tools::Long nMaxX = 0;
1086 mnXOffset += aOutSize.Width() / 5;
1087 if ( mnXOffset > nMaxX )
1088 mnXOffset = nMaxX;
1090 else
1092 mnXOffset = (aOutSize.Width()-ImplGetExtraXOffset()) - nTextPos;
1093 // Something more?
1094 if ( (aOutSize.Width()-ImplGetExtraXOffset()) < nTextPos )
1096 tools::Long nMaxNegX = (aOutSize.Width()-ImplGetExtraXOffset()) - GetTextWidth( aText );
1097 mnXOffset -= aOutSize.Width() / 5;
1098 if ( mnXOffset < nMaxNegX ) // both negative...
1099 mnXOffset = nMaxNegX;
1103 nCursorPosX = nTextPos + mnXOffset + ImplGetExtraXOffset();
1104 if ( nCursorPosX == aOutSize.Width() ) // then invisible...
1105 nCursorPosX--;
1107 if ( mnXOffset != nOldXOffset )
1108 ImplInvalidateOrRepaint();
1111 const tools::Long nTextHeight = GetTextHeight();
1112 const tools::Long nCursorPosY = ImplGetTextYPosition();
1113 if (pCursor)
1115 pCursor->SetPos( Point( nCursorPosX, nCursorPosY ) );
1116 pCursor->SetSize( Size( nCursorWidth, nTextHeight ) );
1117 pCursor->Show();
1121 void Edit::ImplAlign()
1123 if (mnAlign == EDIT_ALIGN_LEFT && !mnXOffset)
1125 // short circuit common case and avoid slow GetTextWidth() calc
1126 return;
1129 tools::Long nTextWidth = GetTextWidth( ImplGetText() );
1130 tools::Long nOutWidth = GetOutputSizePixel().Width();
1132 if ( mnAlign == EDIT_ALIGN_LEFT )
1134 if (nTextWidth < nOutWidth)
1135 mnXOffset = 0;
1137 else if ( mnAlign == EDIT_ALIGN_RIGHT )
1139 tools::Long nMinXOffset = nOutWidth - nTextWidth - 1 - ImplGetExtraXOffset();
1140 bool bRTL = IsRTLEnabled();
1141 if( mbIsSubEdit && GetParent() )
1142 bRTL = GetParent()->IsRTLEnabled();
1143 if( bRTL )
1145 if( nTextWidth < nOutWidth )
1146 mnXOffset = nMinXOffset;
1148 else
1150 if( nTextWidth < nOutWidth )
1151 mnXOffset = nMinXOffset;
1152 else if ( mnXOffset < nMinXOffset )
1153 mnXOffset = nMinXOffset;
1156 else if( mnAlign == EDIT_ALIGN_CENTER )
1158 // would be nicer with check while scrolling but then it's not centred in scrolled state
1159 mnXOffset = (nOutWidth - nTextWidth) / 2;
1163 void Edit::ImplAlignAndPaint()
1165 ImplAlign();
1166 ImplInvalidateOrRepaint();
1167 ImplShowCursor();
1170 sal_Int32 Edit::ImplGetCharPos( const Point& rWindowPos ) const
1172 sal_Int32 nIndex = EDIT_NOLIMIT;
1173 OUString aText = ImplGetText();
1175 if (aText.isEmpty())
1176 return nIndex;
1178 KernArray aDX;
1179 GetOutDev()->GetCaretPositions(aText, aDX, 0, aText.getLength());
1180 tools::Long nX = rWindowPos.X() - mnXOffset - ImplGetExtraXOffset();
1181 for (sal_Int32 i = 0; i < aText.getLength(); aText.iterateCodePoints(&i))
1183 if( (aDX[2*i] >= nX && aDX[2*i+1] <= nX) ||
1184 (aDX[2*i+1] >= nX && aDX[2*i] <= nX))
1186 nIndex = i;
1187 if( aDX[2*i] < aDX[2*i+1] )
1189 if( nX > (aDX[2*i]+aDX[2*i+1])/2 )
1190 aText.iterateCodePoints(&nIndex);
1192 else
1194 if( nX < (aDX[2*i]+aDX[2*i+1])/2 )
1195 aText.iterateCodePoints(&nIndex);
1197 break;
1200 if( nIndex == EDIT_NOLIMIT )
1202 nIndex = 0;
1203 sal_Int32 nFinalIndex = 0;
1204 tools::Long nDiff = std::abs( aDX[0]-nX );
1205 sal_Int32 i = 0;
1206 if (!aText.isEmpty())
1208 aText.iterateCodePoints(&i); //skip the first character
1210 while (i < aText.getLength())
1212 tools::Long nNewDiff = std::abs( aDX[2*i]-nX );
1214 if( nNewDiff < nDiff )
1216 nIndex = i;
1217 nDiff = nNewDiff;
1220 nFinalIndex = i;
1222 aText.iterateCodePoints(&i);
1224 if (nIndex == nFinalIndex && std::abs( aDX[2*nIndex+1] - nX ) < nDiff)
1225 nIndex = EDIT_NOLIMIT;
1228 return nIndex;
1231 void Edit::ImplSetCursorPos( sal_Int32 nChar, bool bSelect )
1233 Selection aSelection( maSelection );
1234 aSelection.Max() = nChar;
1235 if ( !bSelect )
1236 aSelection.Min() = aSelection.Max();
1237 ImplSetSelection( aSelection );
1240 void Edit::ImplCopyToSelectionClipboard()
1242 if ( GetSelection().Len() )
1244 css::uno::Reference<css::datatransfer::clipboard::XClipboard> aSelection(GetSystemPrimarySelection());
1245 ImplCopy( aSelection );
1249 void Edit::ImplCopy( uno::Reference< datatransfer::clipboard::XClipboard > const & rxClipboard )
1251 vcl::unohelper::TextDataObject::CopyStringTo( GetSelected(), rxClipboard );
1254 void Edit::ImplPaste( uno::Reference< datatransfer::clipboard::XClipboard > const & rxClipboard )
1256 if ( !rxClipboard.is() )
1257 return;
1259 uno::Reference< datatransfer::XTransferable > xDataObj;
1263 SolarMutexReleaser aReleaser;
1264 xDataObj = rxClipboard->getContents();
1266 catch( const css::uno::Exception& )
1270 if ( !xDataObj.is() )
1271 return;
1273 datatransfer::DataFlavor aFlavor;
1274 SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING, aFlavor );
1277 uno::Any aData = xDataObj->getTransferData( aFlavor );
1278 OUString aText;
1279 aData >>= aText;
1281 // tdf#127588 - extend selection to the entire field or paste the text
1282 // from the clipboard to the current position if there is no selection
1283 if (mnMaxTextLen < EDIT_NOLIMIT && maSelection.Len() == 0)
1285 const sal_Int32 aTextLen = aText.getLength();
1286 if (aTextLen == mnMaxTextLen)
1288 maSelection.Min() = 0;
1289 maSelection.Max() = mnMaxTextLen;
1290 } else
1291 maSelection.Max() = std::min<sal_Int32>(maSelection.Min() + aTextLen, mnMaxTextLen);
1294 Selection aSelection(maSelection);
1295 aSelection.Normalize();
1296 if (ImplTruncateToMaxLen(aText, aSelection.Len()))
1297 ShowTruncationWarning(GetFrameWeld());
1299 ReplaceSelected( aText );
1301 catch( const css::uno::Exception& )
1306 void Edit::MouseButtonDown( const MouseEvent& rMEvt )
1308 if ( mpSubEdit )
1310 Control::MouseButtonDown( rMEvt );
1311 return;
1314 sal_Int32 nCharPos = ImplGetCharPos( rMEvt.GetPosPixel() );
1315 Selection aSelection( maSelection );
1316 aSelection.Normalize();
1318 if ( rMEvt.GetClicks() < 4 )
1320 mbClickedInSelection = false;
1321 if ( rMEvt.GetClicks() == 3 )
1323 ImplSetSelection( Selection( 0, EDIT_NOLIMIT) );
1324 ImplCopyToSelectionClipboard();
1327 else if ( rMEvt.GetClicks() == 2 )
1329 uno::Reference < i18n::XBreakIterator > xBI = ImplGetBreakIterator();
1330 i18n::Boundary aBoundary = xBI->getWordBoundary( maText.toString(), aSelection.Max(),
1331 GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES, true );
1332 ImplSetSelection( Selection( aBoundary.startPos, aBoundary.endPos ) );
1333 ImplCopyToSelectionClipboard();
1335 else if ( !rMEvt.IsShift() && HasFocus() && aSelection.Contains( nCharPos ) )
1336 mbClickedInSelection = true;
1337 else if ( rMEvt.IsLeft() )
1338 ImplSetCursorPos( nCharPos, rMEvt.IsShift() );
1340 if ( !mbClickedInSelection && rMEvt.IsLeft() && ( rMEvt.GetClicks() == 1 ) )
1341 StartTracking( StartTrackingFlags::ScrollRepeat );
1344 GrabFocus();
1347 void Edit::MouseButtonUp( const MouseEvent& rMEvt )
1349 if ( mbClickedInSelection && rMEvt.IsLeft() )
1351 sal_Int32 nCharPos = ImplGetCharPos( rMEvt.GetPosPixel() );
1352 ImplSetCursorPos( nCharPos, false );
1353 mbClickedInSelection = false;
1355 else if ( rMEvt.IsMiddle() && !mbReadOnly &&
1356 ( GetSettings().GetMouseSettings().GetMiddleButtonAction() == MouseMiddleButtonAction::PasteSelection ) )
1358 css::uno::Reference<css::datatransfer::clipboard::XClipboard> aSelection(GetSystemPrimarySelection());
1359 ImplPaste( aSelection );
1360 Modify();
1364 void Edit::Tracking( const TrackingEvent& rTEvt )
1366 if ( rTEvt.IsTrackingEnded() )
1368 if ( mbClickedInSelection )
1370 sal_Int32 nCharPos = ImplGetCharPos( rTEvt.GetMouseEvent().GetPosPixel() );
1371 ImplSetCursorPos( nCharPos, false );
1372 mbClickedInSelection = false;
1374 else if ( rTEvt.GetMouseEvent().IsLeft() )
1376 ImplCopyToSelectionClipboard();
1379 else
1381 if( !mbClickedInSelection )
1383 sal_Int32 nCharPos = ImplGetCharPos( rTEvt.GetMouseEvent().GetPosPixel() );
1384 ImplSetCursorPos( nCharPos, true );
1389 bool Edit::ImplHandleKeyEvent( const KeyEvent& rKEvt )
1391 bool bDone = false;
1392 sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode();
1393 KeyFuncType eFunc = rKEvt.GetKeyCode().GetFunction();
1395 mbInternModified = false;
1397 if ( eFunc != KeyFuncType::DONTKNOW )
1399 switch ( eFunc )
1401 case KeyFuncType::CUT:
1403 if ( !mbReadOnly && maSelection.Len() && !mbPassword )
1405 Cut();
1406 Modify();
1407 bDone = true;
1410 break;
1412 case KeyFuncType::COPY:
1414 if ( !mbPassword )
1416 Copy();
1417 bDone = true;
1420 break;
1422 case KeyFuncType::PASTE:
1424 if ( !mbReadOnly )
1426 Paste();
1427 bDone = true;
1430 break;
1432 case KeyFuncType::UNDO:
1434 if ( !mbReadOnly )
1436 Undo();
1437 bDone = true;
1440 break;
1442 default:
1443 eFunc = KeyFuncType::DONTKNOW;
1447 if ( !bDone && rKEvt.GetKeyCode().IsMod1() && !rKEvt.GetKeyCode().IsMod2() )
1449 if ( nCode == KEY_A )
1451 ImplSetSelection( Selection( 0, maText.getLength() ) );
1452 bDone = true;
1454 else if ( rKEvt.GetKeyCode().IsShift() && (nCode == KEY_S) )
1456 if ( pImplFncGetSpecialChars )
1458 Selection aSaveSel = GetSelection(); // if someone changes the selection in Get/LoseFocus, e.g. URL bar
1459 OUString aChars = pImplFncGetSpecialChars( GetFrameWeld(), GetFont() );
1460 SetSelection( aSaveSel );
1461 if ( !aChars.isEmpty() )
1463 ImplInsertText( aChars );
1464 Modify();
1466 bDone = true;
1471 if ( eFunc == KeyFuncType::DONTKNOW && ! bDone )
1473 switch ( nCode )
1475 case css::awt::Key::SELECT_ALL:
1477 ImplSetSelection( Selection( 0, maText.getLength() ) );
1478 bDone = true;
1480 break;
1482 case KEY_LEFT:
1483 case KEY_RIGHT:
1484 case KEY_HOME:
1485 case KEY_END:
1486 case css::awt::Key::MOVE_WORD_FORWARD:
1487 case css::awt::Key::SELECT_WORD_FORWARD:
1488 case css::awt::Key::MOVE_WORD_BACKWARD:
1489 case css::awt::Key::SELECT_WORD_BACKWARD:
1490 case css::awt::Key::MOVE_TO_BEGIN_OF_LINE:
1491 case css::awt::Key::MOVE_TO_END_OF_LINE:
1492 case css::awt::Key::SELECT_TO_BEGIN_OF_LINE:
1493 case css::awt::Key::SELECT_TO_END_OF_LINE:
1494 case css::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH:
1495 case css::awt::Key::MOVE_TO_END_OF_PARAGRAPH:
1496 case css::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH:
1497 case css::awt::Key::SELECT_TO_END_OF_PARAGRAPH:
1498 case css::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT:
1499 case css::awt::Key::MOVE_TO_END_OF_DOCUMENT:
1500 case css::awt::Key::SELECT_TO_BEGIN_OF_DOCUMENT:
1501 case css::awt::Key::SELECT_TO_END_OF_DOCUMENT:
1503 if ( !rKEvt.GetKeyCode().IsMod2() )
1505 ImplClearLayoutData();
1506 uno::Reference < i18n::XBreakIterator > xBI = ImplGetBreakIterator();
1508 Selection aSel( maSelection );
1509 bool bWord = rKEvt.GetKeyCode().IsMod1();
1510 bool bSelect = rKEvt.GetKeyCode().IsShift();
1511 bool bGoLeft = (nCode == KEY_LEFT);
1512 bool bGoRight = (nCode == KEY_RIGHT);
1513 bool bGoHome = (nCode == KEY_HOME);
1514 bool bGoEnd = (nCode == KEY_END);
1516 switch( nCode )
1518 case css::awt::Key::MOVE_WORD_FORWARD:
1519 bGoRight = bWord = true;break;
1520 case css::awt::Key::SELECT_WORD_FORWARD:
1521 bGoRight = bSelect = bWord = true;break;
1522 case css::awt::Key::MOVE_WORD_BACKWARD:
1523 bGoLeft = bWord = true;break;
1524 case css::awt::Key::SELECT_WORD_BACKWARD:
1525 bGoLeft = bSelect = bWord = true;break;
1526 case css::awt::Key::SELECT_TO_BEGIN_OF_LINE:
1527 case css::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH:
1528 case css::awt::Key::SELECT_TO_BEGIN_OF_DOCUMENT:
1529 bSelect = true;
1530 [[fallthrough]];
1531 case css::awt::Key::MOVE_TO_BEGIN_OF_LINE:
1532 case css::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH:
1533 case css::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT:
1534 bGoHome = true;break;
1535 case css::awt::Key::SELECT_TO_END_OF_LINE:
1536 case css::awt::Key::SELECT_TO_END_OF_PARAGRAPH:
1537 case css::awt::Key::SELECT_TO_END_OF_DOCUMENT:
1538 bSelect = true;
1539 [[fallthrough]];
1540 case css::awt::Key::MOVE_TO_END_OF_LINE:
1541 case css::awt::Key::MOVE_TO_END_OF_PARAGRAPH:
1542 case css::awt::Key::MOVE_TO_END_OF_DOCUMENT:
1543 bGoEnd = true;break;
1544 default:
1545 break;
1548 // range is checked in ImplSetSelection ...
1549 if ( bGoLeft && aSel.Max() )
1551 if ( bWord )
1553 const OUString sText = maText.toString();
1554 i18n::Boundary aBoundary = xBI->getWordBoundary( sText, aSel.Max(),
1555 GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES, true );
1556 if ( aBoundary.startPos == aSel.Max() )
1557 aBoundary = xBI->previousWord( sText, aSel.Max(),
1558 GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES );
1559 aSel.Max() = aBoundary.startPos;
1561 else
1563 sal_Int32 nCount = 1;
1564 aSel.Max() = xBI->previousCharacters( maText.toString(), aSel.Max(),
1565 GetSettings().GetLanguageTag().getLocale(), i18n::CharacterIteratorMode::SKIPCHARACTER, nCount, nCount );
1568 else if ( bGoRight && ( aSel.Max() < maText.getLength() ) )
1570 if ( bWord )
1572 i18n::Boundary aBoundary = xBI->nextWord( maText.toString(), aSel.Max(),
1573 GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES );
1574 aSel.Max() = aBoundary.startPos;
1576 else
1578 sal_Int32 nCount = 1;
1579 aSel.Max() = xBI->nextCharacters( maText.toString(), aSel.Max(),
1580 GetSettings().GetLanguageTag().getLocale(), i18n::CharacterIteratorMode::SKIPCHARACTER, nCount, nCount );
1583 else if ( bGoHome )
1585 aSel.Max() = 0;
1587 else if ( bGoEnd )
1589 aSel.Max() = EDIT_NOLIMIT;
1592 if ( !bSelect )
1593 aSel.Min() = aSel.Max();
1595 if ( aSel != GetSelection() )
1597 ImplSetSelection( aSel );
1598 ImplCopyToSelectionClipboard();
1601 if (bGoEnd && maAutocompleteHdl.IsSet() && !rKEvt.GetKeyCode().GetModifier())
1603 if ( (maSelection.Min() == maSelection.Max()) && (maSelection.Min() == maText.getLength()) )
1605 maAutocompleteHdl.Call(*this);
1609 bDone = true;
1612 break;
1614 case css::awt::Key::DELETE_WORD_BACKWARD:
1615 case css::awt::Key::DELETE_WORD_FORWARD:
1616 case css::awt::Key::DELETE_TO_BEGIN_OF_LINE:
1617 case css::awt::Key::DELETE_TO_END_OF_LINE:
1618 case KEY_BACKSPACE:
1619 case KEY_DELETE:
1621 if ( !mbReadOnly && !rKEvt.GetKeyCode().IsMod2() )
1623 sal_uInt8 nDel = (nCode == KEY_DELETE) ? EDIT_DEL_RIGHT : EDIT_DEL_LEFT;
1624 sal_uInt8 nMode = rKEvt.GetKeyCode().IsMod1() ? EDIT_DELMODE_RESTOFWORD : EDIT_DELMODE_SIMPLE;
1625 if ( (nMode == EDIT_DELMODE_RESTOFWORD) && rKEvt.GetKeyCode().IsShift() )
1626 nMode = EDIT_DELMODE_RESTOFCONTENT;
1627 switch( nCode )
1629 case css::awt::Key::DELETE_WORD_BACKWARD:
1630 nDel = EDIT_DEL_LEFT;
1631 nMode = EDIT_DELMODE_RESTOFWORD;
1632 break;
1633 case css::awt::Key::DELETE_WORD_FORWARD:
1634 nDel = EDIT_DEL_RIGHT;
1635 nMode = EDIT_DELMODE_RESTOFWORD;
1636 break;
1637 case css::awt::Key::DELETE_TO_BEGIN_OF_LINE:
1638 nDel = EDIT_DEL_LEFT;
1639 nMode = EDIT_DELMODE_RESTOFCONTENT;
1640 break;
1641 case css::awt::Key::DELETE_TO_END_OF_LINE:
1642 nDel = EDIT_DEL_RIGHT;
1643 nMode = EDIT_DELMODE_RESTOFCONTENT;
1644 break;
1645 default: break;
1647 sal_Int32 nOldLen = maText.getLength();
1648 ImplDelete( maSelection, nDel, nMode );
1649 if ( maText.getLength() != nOldLen )
1650 Modify();
1651 bDone = true;
1654 break;
1656 case KEY_INSERT:
1658 if ( !mpIMEInfos && !mbReadOnly && !rKEvt.GetKeyCode().IsMod2() )
1660 SetInsertMode( !mbInsertMode );
1661 bDone = true;
1664 break;
1666 case KEY_RETURN:
1667 if (maActivateHdl.IsSet() && !rKEvt.GetKeyCode().GetModifier())
1668 bDone = maActivateHdl.Call(*this);
1669 break;
1671 default:
1673 if ( IsCharInput( rKEvt ) )
1675 bDone = true; // read characters also when in ReadOnly
1676 if ( !mbReadOnly )
1678 ImplInsertText(OUString(rKEvt.GetCharCode()), nullptr, true);
1679 if (maAutocompleteHdl.IsSet())
1681 if ( (maSelection.Min() == maSelection.Max()) && (maSelection.Min() == maText.getLength()) )
1683 maAutocompleteHdl.Call(*this);
1692 if ( mbInternModified )
1693 Modify();
1695 return bDone;
1698 void Edit::KeyInput( const KeyEvent& rKEvt )
1700 if ( mpSubEdit || !ImplHandleKeyEvent( rKEvt ) )
1701 Control::KeyInput( rKEvt );
1704 void Edit::FillLayoutData() const
1706 mxLayoutData.emplace();
1707 const_cast<Edit*>(this)->Invalidate();
1710 void Edit::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRectangle)
1712 if (!mpSubEdit)
1713 ImplRepaint(rRenderContext, rRectangle);
1716 void Edit::Resize()
1718 if ( !mpSubEdit && IsReallyVisible() )
1720 Control::Resize();
1721 // because of vertical centering...
1722 mnXOffset = 0;
1723 ImplAlign();
1724 Invalidate();
1725 ImplShowCursor();
1729 void Edit::Draw( OutputDevice* pDev, const Point& rPos, SystemTextColorFlags nFlags )
1731 ApplySettings(*pDev);
1733 Point aPos = pDev->LogicToPixel( rPos );
1734 Size aSize = GetSizePixel();
1735 vcl::Font aFont = GetDrawPixelFont( pDev );
1737 pDev->Push();
1738 pDev->SetMapMode();
1739 pDev->SetFont( aFont );
1740 pDev->SetTextFillColor();
1742 // Border/Background
1743 pDev->SetLineColor();
1744 pDev->SetFillColor();
1745 bool bBorder = (GetStyle() & WB_BORDER);
1746 bool bBackground = IsControlBackground();
1747 if ( bBorder || bBackground )
1749 tools::Rectangle aRect( aPos, aSize );
1750 if ( bBorder )
1752 ImplDrawFrame( pDev, aRect );
1754 if ( bBackground )
1756 pDev->SetFillColor( GetControlBackground() );
1757 pDev->DrawRect( aRect );
1761 // Content
1762 if ( nFlags & SystemTextColorFlags::Mono )
1763 pDev->SetTextColor( COL_BLACK );
1764 else
1766 if ( !IsEnabled() )
1768 const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
1769 pDev->SetTextColor( rStyleSettings.GetDisableColor() );
1771 else
1773 pDev->SetTextColor( GetTextColor() );
1777 const tools::Long nOnePixel = GetDrawPixel( pDev, 1 );
1778 const tools::Long nOffX = 3*nOnePixel;
1779 DrawTextFlags nTextStyle = DrawTextFlags::VCenter;
1780 tools::Rectangle aTextRect( aPos, aSize );
1782 if ( GetStyle() & WB_CENTER )
1783 nTextStyle |= DrawTextFlags::Center;
1784 else if ( GetStyle() & WB_RIGHT )
1785 nTextStyle |= DrawTextFlags::Right;
1786 else
1787 nTextStyle |= DrawTextFlags::Left;
1789 aTextRect.AdjustLeft(nOffX );
1790 aTextRect.AdjustRight( -nOffX );
1792 OUString aText = ImplGetText();
1793 tools::Long nTextHeight = pDev->GetTextHeight();
1794 tools::Long nTextWidth = pDev->GetTextWidth( aText );
1795 tools::Long nOffY = (aSize.Height() - nTextHeight) / 2;
1797 // Clipping?
1798 if ( (nOffY < 0) ||
1799 ((nOffY+nTextHeight) > aSize.Height()) ||
1800 ((nOffX+nTextWidth) > aSize.Width()) )
1802 tools::Rectangle aClip( aPos, aSize );
1803 if ( nTextHeight > aSize.Height() )
1804 aClip.AdjustBottom(nTextHeight-aSize.Height()+1 ); // prevent HP printers from 'optimizing'
1805 pDev->IntersectClipRegion( aClip );
1808 pDev->DrawText( aTextRect, aText, nTextStyle );
1809 pDev->Pop();
1811 if ( GetSubEdit() )
1813 Size aOrigSize(GetSubEdit()->GetSizePixel());
1814 GetSubEdit()->SetSizePixel(GetSizePixel());
1815 GetSubEdit()->Draw(pDev, rPos, nFlags);
1816 GetSubEdit()->SetSizePixel(aOrigSize);
1820 void Edit::ImplInvalidateOutermostBorder( vcl::Window* pWin )
1822 // allow control to show focused state
1823 vcl::Window *pInvalWin = pWin;
1824 for (;;)
1826 vcl::Window* pBorder = pInvalWin->GetWindow( GetWindowType::Border );
1827 if (pBorder == pInvalWin || !pBorder ||
1828 pInvalWin->ImplGetFrame() != pBorder->ImplGetFrame() )
1829 break;
1830 pInvalWin = pBorder;
1833 pInvalWin->Invalidate( InvalidateFlags::Children | InvalidateFlags::Update );
1836 void Edit::GetFocus()
1838 Control::GetFocus();
1840 if ( mpSubEdit )
1841 mpSubEdit->ImplGrabFocus( GetGetFocusFlags() );
1842 else if ( !mbActivePopup )
1844 maUndoText = maText.toString();
1845 SelectionOptions nSelOptions = GetSettings().GetStyleSettings().GetSelectionOptions();
1846 if ( !( GetStyle() & (WB_NOHIDESELECTION|WB_READONLY) )
1847 && ( GetGetFocusFlags() & (GetFocusFlags::Init|GetFocusFlags::Tab|GetFocusFlags::CURSOR|GetFocusFlags::Mnemonic) ) )
1849 if ( nSelOptions & SelectionOptions::ShowFirst )
1851 maSelection.Min() = maText.getLength();
1852 maSelection.Max() = 0;
1854 else
1856 maSelection.Min() = 0;
1857 maSelection.Max() = maText.getLength();
1859 if ( mbIsSubEdit )
1860 static_cast<Edit*>(GetParent())->CallEventListeners( VclEventId::EditSelectionChanged );
1861 else
1862 CallEventListeners( VclEventId::EditSelectionChanged );
1865 ImplShowCursor();
1867 if (IsNativeWidgetEnabled() &&
1868 IsNativeControlSupported( ControlType::Editbox, ControlPart::Entire ))
1870 ImplInvalidateOutermostBorder( mbIsSubEdit ? GetParent() : this );
1872 else if ( maSelection.Len() )
1874 // paint the selection
1875 if ( !HasPaintEvent() )
1876 ImplInvalidateOrRepaint();
1877 else
1878 Invalidate();
1881 SetInputContext( InputContext( GetFont(), !IsReadOnly() ? InputContextFlags::Text|InputContextFlags::ExtText : InputContextFlags::NONE ) );
1885 void Edit::LoseFocus()
1887 if ( !mpSubEdit )
1889 if (IsNativeWidgetEnabled() &&
1890 IsNativeControlSupported(ControlType::Editbox, ControlPart::Entire))
1892 ImplInvalidateOutermostBorder( mbIsSubEdit ? GetParent() : this );
1895 if ( !mbActivePopup && !( GetStyle() & WB_NOHIDESELECTION ) && maSelection.Len() )
1896 ImplInvalidateOrRepaint(); // paint the selection
1899 Control::LoseFocus();
1902 bool Edit::PreNotify(NotifyEvent& rNEvt)
1904 if (rNEvt.GetType() == NotifyEventType::MOUSEMOVE)
1906 const MouseEvent* pMouseEvt = rNEvt.GetMouseEvent();
1907 if (pMouseEvt && !pMouseEvt->GetButtons() && !pMouseEvt->IsSynthetic() && !pMouseEvt->IsModifierChanged())
1909 // trigger redraw if mouse over state has changed
1910 if (pMouseEvt->IsLeaveWindow() || pMouseEvt->IsEnterWindow())
1912 if (IsNativeWidgetEnabled() &&
1913 IsNativeControlSupported(ControlType::Editbox, ControlPart::Entire))
1915 ImplInvalidateOutermostBorder(this);
1921 return Control::PreNotify(rNEvt);
1924 void Edit::Command( const CommandEvent& rCEvt )
1926 if ( rCEvt.GetCommand() == CommandEventId::ContextMenu )
1928 VclPtr<PopupMenu> pPopup = Edit::CreatePopupMenu();
1930 bool bEnableCut = true;
1931 bool bEnableCopy = true;
1932 bool bEnableDelete = true;
1933 bool bEnablePaste = true;
1934 bool bEnableSpecialChar = true;
1936 if ( !maSelection.Len() )
1938 bEnableCut = false;
1939 bEnableCopy = false;
1940 bEnableDelete = false;
1943 if ( IsReadOnly() )
1945 bEnableCut = false;
1946 bEnablePaste = false;
1947 bEnableDelete = false;
1948 bEnableSpecialChar = false;
1950 else
1952 // only paste if text available in clipboard
1953 bool bData = false;
1954 uno::Reference< datatransfer::clipboard::XClipboard > xClipboard = GetClipboard();
1956 if ( xClipboard.is() )
1958 uno::Reference< datatransfer::XTransferable > xDataObj;
1960 SolarMutexReleaser aReleaser;
1961 xDataObj = xClipboard->getContents();
1963 if ( xDataObj.is() )
1965 datatransfer::DataFlavor aFlavor;
1966 SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING, aFlavor );
1967 bData = xDataObj->isDataFlavorSupported( aFlavor );
1970 bEnablePaste = bData;
1973 pPopup->EnableItem(pPopup->GetItemId(u"cut"), bEnableCut);
1974 pPopup->EnableItem(pPopup->GetItemId(u"copy"), bEnableCopy);
1975 pPopup->EnableItem(pPopup->GetItemId(u"delete"), bEnableDelete);
1976 pPopup->EnableItem(pPopup->GetItemId(u"paste"), bEnablePaste);
1977 pPopup->SetItemText(pPopup->GetItemId(u"specialchar"),
1978 BuilderUtils::convertMnemonicMarkup(VclResId(STR_SPECIAL_CHARACTER_MENU_ENTRY)));
1979 pPopup->EnableItem(pPopup->GetItemId(u"specialchar"), bEnableSpecialChar);
1980 pPopup->EnableItem(
1981 pPopup->GetItemId(u"undo"),
1982 std::u16string_view(maUndoText) != std::u16string_view(maText));
1983 bool bAllSelected = maSelection.Min() == 0 && maSelection.Max() == maText.getLength();
1984 pPopup->EnableItem(pPopup->GetItemId(u"selectall"), !bAllSelected);
1985 pPopup->ShowItem(pPopup->GetItemId(u"specialchar"), pImplFncGetSpecialChars != nullptr);
1987 mbActivePopup = true;
1988 Selection aSaveSel = GetSelection(); // if someone changes selection in Get/LoseFocus, e.g. URL bar
1989 Point aPos = rCEvt.GetMousePosPixel();
1990 if ( !rCEvt.IsMouseEvent() )
1992 // Show menu eventually centered in selection
1993 Size aSize = GetOutputSizePixel();
1994 aPos = Point( aSize.Width()/2, aSize.Height()/2 );
1996 sal_uInt16 n = pPopup->Execute( this, aPos );
1997 SetSelection( aSaveSel );
1998 OUString sCommand = pPopup->GetItemIdent(n);
1999 if (sCommand == "undo")
2001 Undo();
2002 Modify();
2004 else if (sCommand == "cut")
2006 Cut();
2007 Modify();
2009 else if (sCommand == "copy")
2011 Copy();
2013 else if (sCommand == "paste")
2015 Paste();
2016 Modify();
2018 else if (sCommand == "delete")
2020 DeleteSelected();
2021 Modify();
2023 else if (sCommand == "selectall")
2025 ImplSetSelection( Selection( 0, maText.getLength() ) );
2027 else if (sCommand == "specialchar" && pImplFncGetSpecialChars)
2029 OUString aChars = pImplFncGetSpecialChars(GetFrameWeld(), GetFont());
2030 if (!isDisposed()) // destroyed while the insert special character dialog was still open
2032 SetSelection( aSaveSel );
2033 if (!aChars.isEmpty())
2035 ImplInsertText( aChars );
2036 Modify();
2040 pPopup.clear();
2041 mbActivePopup = false;
2043 else if ( rCEvt.GetCommand() == CommandEventId::StartExtTextInput )
2045 DeleteSelected();
2046 sal_Int32 nPos = maSelection.Max();
2047 mpIMEInfos.reset(new Impl_IMEInfos( nPos, maText.copy(nPos).makeStringAndClear() ));
2048 mpIMEInfos->bWasCursorOverwrite = !IsInsertMode();
2050 else if ( rCEvt.GetCommand() == CommandEventId::EndExtTextInput )
2052 bool bInsertMode = !mpIMEInfos->bWasCursorOverwrite;
2053 mpIMEInfos.reset();
2055 SetInsertMode(bInsertMode);
2056 Modify();
2058 Invalidate();
2060 // #i25161# call auto complete handler for ext text commit also
2061 if (maAutocompleteHdl.IsSet())
2063 if ( (maSelection.Min() == maSelection.Max()) && (maSelection.Min() == maText.getLength()) )
2065 maAutocompleteHdl.Call(*this);
2069 else if ( rCEvt.GetCommand() == CommandEventId::ExtTextInput )
2071 const CommandExtTextInputData* pData = rCEvt.GetExtTextInputData();
2073 maText.remove( mpIMEInfos->nPos, mpIMEInfos->nLen );
2074 maText.insert( mpIMEInfos->nPos, pData->GetText() );
2075 if ( mpIMEInfos->bWasCursorOverwrite )
2077 const sal_Int32 nOldIMETextLen = mpIMEInfos->nLen;
2078 const sal_Int32 nNewIMETextLen = pData->GetText().getLength();
2079 if ( ( nOldIMETextLen > nNewIMETextLen ) &&
2080 ( nNewIMETextLen < mpIMEInfos->aOldTextAfterStartPos.getLength() ) )
2082 // restore old characters
2083 const sal_Int32 nRestore = nOldIMETextLen - nNewIMETextLen;
2084 maText.insert( mpIMEInfos->nPos + nNewIMETextLen, mpIMEInfos->aOldTextAfterStartPos.subView( nNewIMETextLen, nRestore ) );
2086 else if ( ( nOldIMETextLen < nNewIMETextLen ) &&
2087 ( nOldIMETextLen < mpIMEInfos->aOldTextAfterStartPos.getLength() ) )
2089 const sal_Int32 nOverwrite = ( nNewIMETextLen > mpIMEInfos->aOldTextAfterStartPos.getLength()
2090 ? mpIMEInfos->aOldTextAfterStartPos.getLength() : nNewIMETextLen ) - nOldIMETextLen;
2091 maText.remove( mpIMEInfos->nPos + nNewIMETextLen, nOverwrite );
2095 if ( pData->GetTextAttr() )
2097 mpIMEInfos->CopyAttribs( pData->GetTextAttr(), pData->GetText().getLength() );
2098 mpIMEInfos->bCursor = pData->IsCursorVisible();
2100 else
2102 mpIMEInfos->DestroyAttribs();
2105 ImplAlignAndPaint();
2106 sal_Int32 nCursorPos = mpIMEInfos->nPos + pData->GetCursorPos();
2107 SetSelection( Selection( nCursorPos, nCursorPos ) );
2108 SetInsertMode( !pData->IsCursorOverwrite() );
2110 if ( pData->IsCursorVisible() )
2111 GetCursor()->Show();
2112 else
2113 GetCursor()->Hide();
2115 else if ( rCEvt.GetCommand() == CommandEventId::CursorPos )
2117 if ( mpIMEInfos )
2119 sal_Int32 nCursorPos = GetSelection().Max();
2120 SetCursorRect( nullptr, GetTextWidth( maText.toString(), nCursorPos, mpIMEInfos->nPos+mpIMEInfos->nLen-nCursorPos ) );
2122 else
2124 SetCursorRect();
2127 else if ( rCEvt.GetCommand() == CommandEventId::SelectionChange )
2129 const CommandSelectionChangeData *pData = rCEvt.GetSelectionChangeData();
2130 Selection aSelection( pData->GetStart(), pData->GetEnd() );
2131 SetSelection(aSelection);
2133 else if ( rCEvt.GetCommand() == CommandEventId::QueryCharPosition )
2135 if (mpIMEInfos && mpIMEInfos->nLen > 0)
2137 OUString aText = ImplGetText();
2138 KernArray aDX;
2139 GetOutDev()->GetCaretPositions(aText, aDX, 0, aText.getLength());
2141 tools::Long nTH = GetTextHeight();
2142 Point aPos( mnXOffset, ImplGetTextYPosition() );
2144 std::vector<tools::Rectangle> aRects(mpIMEInfos->nLen);
2145 for ( int nIndex = 0; nIndex < mpIMEInfos->nLen; ++nIndex )
2147 tools::Rectangle aRect( aPos, Size( 10, nTH ) );
2148 aRect.SetLeft( aDX[2*(nIndex+mpIMEInfos->nPos)] + mnXOffset + ImplGetExtraXOffset() );
2149 aRects[ nIndex ] = aRect;
2151 SetCompositionCharRect(aRects.data(), mpIMEInfos->nLen);
2154 else
2155 Control::Command( rCEvt );
2158 void Edit::StateChanged( StateChangedType nType )
2160 if (nType == StateChangedType::InitShow)
2162 if (!mpSubEdit)
2164 mnXOffset = 0; // if GrabFocus before while size was still wrong
2165 ImplAlign();
2166 if (!mpSubEdit)
2167 ImplShowCursor(false);
2168 Invalidate();
2171 else if (nType == StateChangedType::Enable)
2173 if (!mpSubEdit)
2175 // change text color only
2176 ImplInvalidateOrRepaint();
2179 else if (nType == StateChangedType::Style || nType == StateChangedType::Mirroring)
2181 WinBits nStyle = GetStyle();
2182 if (nType == StateChangedType::Style)
2184 nStyle = ImplInitStyle(GetStyle());
2185 SetStyle(nStyle);
2188 sal_uInt16 nOldAlign = mnAlign;
2189 mnAlign = EDIT_ALIGN_LEFT;
2191 // hack: right align until keyinput and cursor travelling works
2192 // edits are always RTL disabled
2193 // however the parent edits contain the correct setting
2194 if (mbIsSubEdit && GetParent()->IsRTLEnabled())
2196 if (GetParent()->GetStyle() & WB_LEFT)
2197 mnAlign = EDIT_ALIGN_RIGHT;
2198 if (nType == StateChangedType::Mirroring)
2199 GetOutDev()->SetLayoutMode(vcl::text::ComplexTextLayoutFlags::BiDiRtl | vcl::text::ComplexTextLayoutFlags::TextOriginLeft);
2201 else if (mbIsSubEdit && !GetParent()->IsRTLEnabled())
2203 if (nType == StateChangedType::Mirroring)
2204 GetOutDev()->SetLayoutMode(vcl::text::ComplexTextLayoutFlags::TextOriginLeft);
2207 if (nStyle & WB_RIGHT)
2208 mnAlign = EDIT_ALIGN_RIGHT;
2209 else if (nStyle & WB_CENTER)
2210 mnAlign = EDIT_ALIGN_CENTER;
2211 if (!maText.isEmpty() && (mnAlign != nOldAlign))
2213 ImplAlign();
2214 Invalidate();
2218 else if ((nType == StateChangedType::Zoom) || (nType == StateChangedType::ControlFont))
2220 if (!mpSubEdit)
2222 ApplySettings(*GetOutDev());
2223 ImplShowCursor();
2224 Invalidate();
2227 else if ((nType == StateChangedType::ControlForeground) || (nType == StateChangedType::ControlBackground))
2229 if (!mpSubEdit)
2231 ApplySettings(*GetOutDev());
2232 Invalidate();
2236 Control::StateChanged(nType);
2239 void Edit::DataChanged( const DataChangedEvent& rDCEvt )
2241 if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
2242 (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
2243 ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
2244 (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) )
2246 if ( !mpSubEdit )
2248 ApplySettings(*GetOutDev());
2249 ImplShowCursor();
2250 Invalidate();
2254 Control::DataChanged( rDCEvt );
2257 void Edit::ImplShowDDCursor()
2259 if (!mpDDInfo->bVisCursor)
2261 tools::Long nTextWidth = GetTextWidth( maText.toString(), 0, mpDDInfo->nDropPos );
2262 tools::Long nTextHeight = GetTextHeight();
2263 tools::Rectangle aCursorRect( Point( nTextWidth + mnXOffset, (GetOutDev()->GetOutputSize().Height()-nTextHeight)/2 ), Size( 2, nTextHeight ) );
2264 mpDDInfo->aCursor.SetWindow( this );
2265 mpDDInfo->aCursor.SetPos( aCursorRect.TopLeft() );
2266 mpDDInfo->aCursor.SetSize( aCursorRect.GetSize() );
2267 mpDDInfo->aCursor.Show();
2268 mpDDInfo->bVisCursor = true;
2272 void Edit::ImplHideDDCursor()
2274 if ( mpDDInfo && mpDDInfo->bVisCursor )
2276 mpDDInfo->aCursor.Hide();
2277 mpDDInfo->bVisCursor = false;
2281 TextFilter::TextFilter(OUString _aForbiddenChars)
2282 : sForbiddenChars(std::move(_aForbiddenChars))
2286 TextFilter::~TextFilter()
2290 OUString TextFilter::filter(const OUString &rText)
2292 OUString sTemp(rText);
2293 for (sal_Int32 i = 0; i < sForbiddenChars.getLength(); ++i)
2295 sTemp = sTemp.replaceAll(OUStringChar(sForbiddenChars[i]), "");
2297 return sTemp;
2300 void Edit::filterText()
2302 Selection aSel = GetSelection();
2303 const OUString sOrig = GetText();
2304 const OUString sNew = mpFilterText->filter(GetText());
2305 if (sOrig != sNew)
2307 sal_Int32 nDiff = sOrig.getLength() - sNew.getLength();
2308 if (nDiff)
2310 aSel.setMin(aSel.getMin() - nDiff);
2311 aSel.setMax(aSel.getMin());
2313 SetText(sNew);
2314 SetSelection(aSel);
2318 void Edit::Modify()
2320 if (mpFilterText)
2321 filterText();
2323 if ( mbIsSubEdit )
2325 static_cast<Edit*>(GetParent())->Modify();
2327 else
2329 if ( ImplCallEventListenersAndHandler( VclEventId::EditModify, [this] () { maModifyHdl.Call(*this); } ) )
2330 // have been destroyed while calling into the handlers
2331 return;
2333 // #i13677# notify edit listeners about caret position change
2334 CallEventListeners( VclEventId::EditCaretChanged );
2335 // FIXME: this is currently only on macOS
2336 // check for other platforms that need similar handling
2337 if( ImplGetSVData()->maNWFData.mbNoFocusRects &&
2338 IsNativeWidgetEnabled() &&
2339 IsNativeControlSupported( ControlType::Editbox, ControlPart::Entire ) )
2341 ImplInvalidateOutermostBorder( this );
2346 void Edit::SetEchoChar( sal_Unicode c )
2348 mcEchoChar = c;
2349 if ( mpSubEdit )
2350 mpSubEdit->SetEchoChar( c );
2353 void Edit::SetReadOnly( bool bReadOnly )
2355 if ( mbReadOnly != bReadOnly )
2357 mbReadOnly = bReadOnly;
2358 if ( mpSubEdit )
2359 mpSubEdit->SetReadOnly( bReadOnly );
2361 CompatStateChanged( StateChangedType::ReadOnly );
2365 void Edit::SetInsertMode( bool bInsert )
2367 if ( bInsert != mbInsertMode )
2369 mbInsertMode = bInsert;
2370 if ( mpSubEdit )
2371 mpSubEdit->SetInsertMode( bInsert );
2372 else
2373 ImplShowCursor();
2377 bool Edit::IsInsertMode() const
2379 if ( mpSubEdit )
2380 return mpSubEdit->IsInsertMode();
2381 else
2382 return mbInsertMode;
2385 void Edit::SetMaxTextLen(sal_Int32 nMaxLen)
2387 mnMaxTextLen = nMaxLen > 0 ? nMaxLen : EDIT_NOLIMIT;
2389 if ( mpSubEdit )
2390 mpSubEdit->SetMaxTextLen( mnMaxTextLen );
2391 else
2393 if ( maText.getLength() > mnMaxTextLen )
2394 ImplDelete( Selection( mnMaxTextLen, maText.getLength() ), EDIT_DEL_RIGHT, EDIT_DELMODE_SIMPLE );
2398 void Edit::SetSelection( const Selection& rSelection )
2400 // If the selection was changed from outside, e.g. by MouseButtonDown, don't call Tracking()
2401 // directly afterwards which would change the selection again
2402 if ( IsTracking() )
2403 EndTracking();
2404 else if ( mpSubEdit && mpSubEdit->IsTracking() )
2405 mpSubEdit->EndTracking();
2407 ImplSetSelection( rSelection );
2410 void Edit::ImplSetSelection( const Selection& rSelection, bool bPaint )
2412 if ( mpSubEdit )
2413 mpSubEdit->ImplSetSelection( rSelection );
2414 else
2416 if ( rSelection != maSelection )
2418 Selection aOld( maSelection );
2419 Selection aNew( rSelection );
2421 if ( aNew.Min() > maText.getLength() )
2422 aNew.Min() = maText.getLength();
2423 if ( aNew.Max() > maText.getLength() )
2424 aNew.Max() = maText.getLength();
2425 if ( aNew.Min() < 0 )
2426 aNew.Min() = 0;
2427 if ( aNew.Max() < 0 )
2428 aNew.Max() = 0;
2430 if ( aNew != maSelection )
2432 ImplClearLayoutData();
2433 Selection aTemp = maSelection;
2434 maSelection = aNew;
2436 if ( bPaint && ( aOld.Len() || aNew.Len() || IsPaintTransparent() ) )
2437 ImplInvalidateOrRepaint();
2438 ImplShowCursor();
2440 bool bCaret = false, bSelection = false;
2441 tools::Long nB=aNew.Max(), nA=aNew.Min(),oB=aTemp.Max(), oA=aTemp.Min();
2442 tools::Long nGap = nB-nA, oGap = oB-oA;
2443 if (nB != oB)
2444 bCaret = true;
2445 if (nGap != 0 || oGap != 0)
2446 bSelection = true;
2448 if (bSelection)
2450 if ( mbIsSubEdit )
2451 static_cast<Edit*>(GetParent())->CallEventListeners( VclEventId::EditSelectionChanged );
2452 else
2453 CallEventListeners( VclEventId::EditSelectionChanged );
2456 if (bCaret)
2458 if ( mbIsSubEdit )
2459 static_cast<Edit*>(GetParent())->CallEventListeners( VclEventId::EditCaretChanged );
2460 else
2461 CallEventListeners( VclEventId::EditCaretChanged );
2464 // #103511# notify combobox listeners of deselection
2465 if( !maSelection && GetParent() && GetParent()->GetType() == WindowType::COMBOBOX )
2466 static_cast<Edit*>(GetParent())->CallEventListeners( VclEventId::ComboboxDeselect );
2472 const Selection& Edit::GetSelection() const
2474 if ( mpSubEdit )
2475 return mpSubEdit->GetSelection();
2476 else
2477 return maSelection;
2480 void Edit::ReplaceSelected( const OUString& rStr )
2482 if ( mpSubEdit )
2483 mpSubEdit->ReplaceSelected( rStr );
2484 else
2485 ImplInsertText( rStr );
2488 void Edit::DeleteSelected()
2490 if ( mpSubEdit )
2491 mpSubEdit->DeleteSelected();
2492 else
2494 if ( maSelection.Len() )
2495 ImplDelete( maSelection, EDIT_DEL_RIGHT, EDIT_DELMODE_SIMPLE );
2499 OUString Edit::GetSelected() const
2501 if ( mpSubEdit )
2502 return mpSubEdit->GetSelected();
2503 else
2505 Selection aSelection( maSelection );
2506 aSelection.Normalize();
2507 return OUString( maText.getStr() + aSelection.Min(), aSelection.Len() );
2511 void Edit::Cut()
2513 if ( !mbPassword )
2515 Copy();
2516 ReplaceSelected( OUString() );
2520 void Edit::Copy()
2522 if ( !mbPassword )
2524 css::uno::Reference<css::datatransfer::clipboard::XClipboard> aClipboard(GetClipboard());
2525 ImplCopy( aClipboard );
2529 void Edit::Paste()
2531 css::uno::Reference<css::datatransfer::clipboard::XClipboard> aClipboard(GetClipboard());
2532 ImplPaste( aClipboard );
2535 void Edit::Undo()
2537 if ( mpSubEdit )
2538 mpSubEdit->Undo();
2539 else
2541 const OUString aText( maText.toString() );
2542 ImplDelete( Selection( 0, aText.getLength() ), EDIT_DEL_RIGHT, EDIT_DELMODE_SIMPLE );
2543 ImplInsertText( maUndoText );
2544 ImplSetSelection( Selection( 0, maUndoText.getLength() ) );
2545 maUndoText = aText;
2549 void Edit::SetText( const OUString& rStr )
2551 if ( mpSubEdit )
2552 mpSubEdit->SetText( rStr ); // not directly ImplSetText if SetText overridden
2553 else
2555 Selection aNewSel( 0, 0 ); // prevent scrolling
2556 ImplSetText( rStr, &aNewSel );
2560 void Edit::SetText( const OUString& rStr, const Selection& rSelection )
2562 if ( mpSubEdit )
2563 mpSubEdit->SetText( rStr, rSelection );
2564 else
2565 ImplSetText( rStr, &rSelection );
2568 OUString Edit::GetText() const
2570 if ( mpSubEdit )
2571 return mpSubEdit->GetText();
2572 else
2573 return maText.toString();
2576 void Edit::SetCursorAtLast(){
2577 ImplSetCursorPos( GetText().getLength(), false );
2580 void Edit::SetPlaceholderText( const OUString& rStr )
2582 if ( mpSubEdit )
2583 mpSubEdit->SetPlaceholderText( rStr );
2584 else if ( maPlaceholderText != rStr )
2586 maPlaceholderText = rStr;
2587 if ( GetText().isEmpty() )
2588 Invalidate();
2592 void Edit::SetModifyFlag()
2596 void Edit::SetSubEdit(Edit* pEdit)
2598 mpSubEdit.disposeAndClear();
2599 mpSubEdit.set(pEdit);
2601 if (mpSubEdit)
2603 SetPointer(PointerStyle::Arrow); // Only SubEdit has the BEAM...
2604 mpSubEdit->mbIsSubEdit = true;
2606 mpSubEdit->SetReadOnly(mbReadOnly);
2607 mpSubEdit->maAutocompleteHdl = maAutocompleteHdl;
2611 Size Edit::CalcMinimumSizeForText(const OUString &rString) const
2613 ControlType eCtrlType = ImplGetNativeControlType();
2615 Size aSize;
2616 if (mnWidthInChars != -1)
2618 //CalcSize calls CalcWindowSize, but we will call that also in this
2619 //function, so undo the first one with CalcOutputSize
2620 aSize = CalcOutputSize(CalcSize(mnWidthInChars));
2622 else
2624 OUString aString;
2625 if (mnMaxWidthChars != -1 && mnMaxWidthChars < rString.getLength())
2626 aString = rString.copy(0, mnMaxWidthChars);
2627 else
2628 aString = rString;
2630 aSize.setHeight( GetTextHeight() );
2631 aSize.setWidth( GetTextWidth(aString) );
2632 aSize.AdjustWidth(ImplGetExtraXOffset() * 2 );
2634 // do not create edit fields in which one cannot enter anything
2635 // a default minimum width should exist for at least 3 characters
2637 //CalcSize calls CalcWindowSize, but we will call that also in this
2638 //function, so undo the first one with CalcOutputSize
2639 Size aMinSize(CalcOutputSize(CalcSize(3)));
2640 if (aSize.Width() < aMinSize.Width())
2641 aSize.setWidth( aMinSize.Width() );
2644 aSize.AdjustHeight(ImplGetExtraYOffset() * 2 );
2646 aSize = CalcWindowSize( aSize );
2648 // ask NWF what if it has an opinion, too
2649 ImplControlValue aControlValue;
2650 tools::Rectangle aRect( Point( 0, 0 ), aSize );
2651 tools::Rectangle aContent, aBound;
2652 if (GetNativeControlRegion(eCtrlType, ControlPart::Entire, aRect, ControlState::NONE,
2653 aControlValue, aBound, aContent))
2655 if (aBound.GetHeight() > aSize.Height())
2656 aSize.setHeight( aBound.GetHeight() );
2658 return aSize;
2661 Size Edit::CalcMinimumSize() const
2663 return CalcMinimumSizeForText(GetText());
2666 Size Edit::GetOptimalSize() const
2668 return CalcMinimumSize();
2671 Size Edit::CalcSize(sal_Int32 nChars) const
2673 // width for N characters, independent from content.
2674 // works only correct for fixed fonts, average otherwise
2675 float fUnitWidth = std::max(approximate_char_width(), approximate_digit_width());
2676 Size aSz(fUnitWidth * nChars, GetTextHeight());
2677 aSz.AdjustWidth(ImplGetExtraXOffset() * 2 );
2678 aSz = CalcWindowSize( aSz );
2679 return aSz;
2682 sal_Int32 Edit::GetMaxVisChars() const
2684 const vcl::Window* pW = mpSubEdit ? mpSubEdit : this;
2685 sal_Int32 nOutWidth = pW->GetOutputSizePixel().Width();
2686 float fUnitWidth = std::max(approximate_char_width(), approximate_digit_width());
2687 return nOutWidth / fUnitWidth;
2690 namespace vcl
2692 void SetGetSpecialCharsFunction( FncGetSpecialChars fn )
2694 pImplFncGetSpecialChars = fn;
2697 FncGetSpecialChars GetGetSpecialCharsFunction()
2699 return pImplFncGetSpecialChars;
2703 VclPtr<PopupMenu> Edit::CreatePopupMenu()
2705 if (!mpUIBuilder)
2706 mpUIBuilder.reset(new VclBuilder(nullptr, AllSettings::GetUIRootDir(), u"vcl/ui/editmenu.ui"_ustr, u""_ustr));
2707 VclPtr<PopupMenu> pPopup = mpUIBuilder->get_menu(u"menu");
2708 const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
2709 if (rStyleSettings.GetHideDisabledMenuItems())
2710 pPopup->SetMenuFlags( MenuFlags::HideDisabledEntries );
2711 else
2712 pPopup->SetMenuFlags ( MenuFlags::AlwaysShowDisabledEntries );
2713 if (rStyleSettings.GetContextMenuShortcuts())
2715 pPopup->SetAccelKey(pPopup->GetItemId(u"undo"), vcl::KeyCode( KeyFuncType::UNDO));
2716 pPopup->SetAccelKey(pPopup->GetItemId(u"cut"), vcl::KeyCode( KeyFuncType::CUT));
2717 pPopup->SetAccelKey(pPopup->GetItemId(u"copy"), vcl::KeyCode( KeyFuncType::COPY));
2718 pPopup->SetAccelKey(pPopup->GetItemId(u"paste"), vcl::KeyCode( KeyFuncType::PASTE));
2719 pPopup->SetAccelKey(pPopup->GetItemId(u"delete"), vcl::KeyCode( KeyFuncType::DELETE));
2720 pPopup->SetAccelKey(pPopup->GetItemId(u"selectall"), vcl::KeyCode( KEY_A, false, true, false, false));
2721 pPopup->SetAccelKey(pPopup->GetItemId(u"specialchar"), vcl::KeyCode( KEY_S, true, true, false, false));
2723 return pPopup;
2726 // css::datatransfer::dnd::XDragGestureListener
2727 void Edit::dragGestureRecognized( const css::datatransfer::dnd::DragGestureEvent& rDGE )
2729 SolarMutexGuard aVclGuard;
2731 if ( !(!IsTracking() && maSelection.Len() &&
2732 !mbPassword && (!mpDDInfo || !mpDDInfo->bStarterOfDD)) ) // no repeated D&D
2733 return;
2735 Selection aSel( maSelection );
2736 aSel.Normalize();
2738 // only if mouse in the selection...
2739 Point aMousePos( rDGE.DragOriginX, rDGE.DragOriginY );
2740 sal_Int32 nCharPos = ImplGetCharPos( aMousePos );
2741 if ( (nCharPos < aSel.Min()) || (nCharPos >= aSel.Max()) )
2742 return;
2744 if ( !mpDDInfo )
2745 mpDDInfo.reset(new DDInfo);
2747 mpDDInfo->bStarterOfDD = true;
2748 mpDDInfo->aDndStartSel = aSel;
2750 if ( IsTracking() )
2751 EndTracking(); // before D&D disable tracking
2753 rtl::Reference<vcl::unohelper::TextDataObject> pDataObj = new vcl::unohelper::TextDataObject( GetSelected() );
2754 sal_Int8 nActions = datatransfer::dnd::DNDConstants::ACTION_COPY;
2755 if ( !IsReadOnly() )
2756 nActions |= datatransfer::dnd::DNDConstants::ACTION_MOVE;
2757 rDGE.DragSource->startDrag( rDGE, nActions, 0 /*cursor*/, 0 /*image*/, pDataObj, mxDnDListener );
2758 if ( GetCursor() )
2759 GetCursor()->Hide();
2762 // css::datatransfer::dnd::XDragSourceListener
2763 void Edit::dragDropEnd( const css::datatransfer::dnd::DragSourceDropEvent& rDSDE )
2765 SolarMutexGuard aVclGuard;
2767 if (rDSDE.DropSuccess && (rDSDE.DropAction & datatransfer::dnd::DNDConstants::ACTION_MOVE) && mpDDInfo)
2769 Selection aSel( mpDDInfo->aDndStartSel );
2770 if ( mpDDInfo->bDroppedInMe )
2772 if ( aSel.Max() > mpDDInfo->nDropPos )
2774 tools::Long nLen = aSel.Len();
2775 aSel.Min() += nLen;
2776 aSel.Max() += nLen;
2779 ImplDelete( aSel, EDIT_DEL_RIGHT, EDIT_DELMODE_SIMPLE );
2780 Modify();
2783 ImplHideDDCursor();
2784 mpDDInfo.reset();
2787 // css::datatransfer::dnd::XDropTargetListener
2788 void Edit::drop( const css::datatransfer::dnd::DropTargetDropEvent& rDTDE )
2790 SolarMutexGuard aVclGuard;
2792 bool bChanges = false;
2793 if ( !mbReadOnly && mpDDInfo )
2795 ImplHideDDCursor();
2797 Selection aSel( maSelection );
2798 aSel.Normalize();
2800 if ( aSel.Len() && !mpDDInfo->bStarterOfDD )
2801 ImplDelete( aSel, EDIT_DEL_RIGHT, EDIT_DELMODE_SIMPLE );
2803 mpDDInfo->bDroppedInMe = true;
2805 aSel.Min() = mpDDInfo->nDropPos;
2806 aSel.Max() = mpDDInfo->nDropPos;
2807 ImplSetSelection( aSel );
2809 uno::Reference< datatransfer::XTransferable > xDataObj = rDTDE.Transferable;
2810 if ( xDataObj.is() )
2812 datatransfer::DataFlavor aFlavor;
2813 SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING, aFlavor );
2814 if ( xDataObj->isDataFlavorSupported( aFlavor ) )
2816 uno::Any aData = xDataObj->getTransferData( aFlavor );
2817 OUString aText;
2818 aData >>= aText;
2819 ImplInsertText( aText );
2820 bChanges = true;
2821 Modify();
2825 if ( !mpDDInfo->bStarterOfDD )
2827 mpDDInfo.reset();
2831 rDTDE.Context->dropComplete( bChanges );
2834 void Edit::dragEnter( const css::datatransfer::dnd::DropTargetDragEnterEvent& rDTDE )
2836 if ( !mpDDInfo )
2838 mpDDInfo.reset(new DDInfo);
2840 // search for string data type
2841 const Sequence< css::datatransfer::DataFlavor >& rFlavors( rDTDE.SupportedDataFlavors );
2842 mpDDInfo->bIsStringSupported = std::any_of(rFlavors.begin(), rFlavors.end(),
2843 [](const css::datatransfer::DataFlavor& rFlavor) {
2844 sal_Int32 nIndex = 0;
2845 const std::u16string_view aMimetype = o3tl::getToken(rFlavor.MimeType, 0, ';', nIndex );
2846 return aMimetype == u"text/plain";
2850 void Edit::dragExit( const css::datatransfer::dnd::DropTargetEvent& )
2852 SolarMutexGuard aVclGuard;
2854 ImplHideDDCursor();
2857 void Edit::dragOver( const css::datatransfer::dnd::DropTargetDragEvent& rDTDE )
2859 SolarMutexGuard aVclGuard;
2861 Point aMousePos( rDTDE.LocationX, rDTDE.LocationY );
2863 sal_Int32 nPrevDropPos = mpDDInfo->nDropPos;
2864 mpDDInfo->nDropPos = ImplGetCharPos( aMousePos );
2867 Size aOutSize = GetOutputSizePixel();
2868 if ( ( aMousePos.X() < 0 ) || ( aMousePos.X() > aOutSize.Width() ) )
2870 // Scroll?
2871 // No, I will not receive events in this case...
2875 Selection aSel( maSelection );
2876 aSel.Normalize();
2878 // Don't accept drop in selection or read-only field...
2879 if ( IsReadOnly() || aSel.Contains( mpDDInfo->nDropPos ) || ! mpDDInfo->bIsStringSupported )
2881 ImplHideDDCursor();
2882 rDTDE.Context->rejectDrag();
2884 else
2886 // draw the old cursor away...
2887 if ( !mpDDInfo->bVisCursor || ( nPrevDropPos != mpDDInfo->nDropPos ) )
2889 ImplHideDDCursor();
2890 ImplShowDDCursor();
2892 rDTDE.Context->acceptDrag( rDTDE.DropAction );
2896 OUString Edit::GetSurroundingText() const
2898 if (mpSubEdit)
2899 return mpSubEdit->GetSurroundingText();
2900 return maText.toString();
2903 Selection Edit::GetSurroundingTextSelection() const
2905 return GetSelection();
2908 bool Edit::DeleteSurroundingText(const Selection& rSelection)
2910 SetSelection(rSelection);
2911 DeleteSelected();
2912 // maybe we should update mpIMEInfos here
2913 return true;
2916 FactoryFunction Edit::GetUITestFactory() const
2918 return EditUIObject::create;
2922 void Edit::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
2924 Control::DumpAsPropertyTree(rJsonWriter);
2926 if (!maPlaceholderText.isEmpty())
2927 rJsonWriter.put("placeholder", maPlaceholderText);
2929 if (IsPassword())
2930 rJsonWriter.put("password", true);
2933 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */