build fix: no comphelper/profilezone.hxx in this branch
[LibreOffice.git] / vcl / source / control / edit.cxx
blobcf0fe9ce07595e108d3dbe5b6ac4a796656d8cef
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 <tools/rc.h>
21 #include <comphelper/lok.hxx>
23 #include <vcl/IDialogRenderable.hxx>
24 #include <vcl/decoview.hxx>
25 #include <vcl/event.hxx>
26 #include <vcl/cursor.hxx>
27 #include <vcl/virdev.hxx>
28 #include <vcl/menu.hxx>
29 #include <vcl/edit.hxx>
30 #include <vcl/layout.hxx>
31 #include <vcl/svapp.hxx>
32 #include <vcl/settings.hxx>
33 #include <vcl/uitest/uiobject.hxx>
35 #include <window.h>
36 #include <svdata.hxx>
37 #include <svids.hrc>
38 #include <controldata.hxx>
40 #include <osl/mutex.hxx>
42 #include <com/sun/star/i18n/BreakIterator.hpp>
43 #include <com/sun/star/i18n/CharacterIteratorMode.hpp>
44 #include <com/sun/star/i18n/WordType.hpp>
45 #include <cppuhelper/weak.hxx>
46 #include <com/sun/star/datatransfer/XTransferable.hpp>
47 #include <com/sun/star/datatransfer/clipboard/XClipboard.hpp>
48 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
50 #include <com/sun/star/datatransfer/dnd/DNDConstants.hpp>
51 #include <com/sun/star/datatransfer/dnd/XDragGestureRecognizer.hpp>
52 #include <com/sun/star/datatransfer/dnd/XDropTarget.hpp>
54 #include <com/sun/star/i18n/InputSequenceChecker.hpp>
55 #include <com/sun/star/i18n/InputSequenceCheckMode.hpp>
56 #include <com/sun/star/i18n/ScriptType.hpp>
57 #include <com/sun/star/container/XNameAccess.hpp>
59 #include <com/sun/star/uno/Any.hxx>
61 #include <comphelper/processfactory.hxx>
62 #include <comphelper/string.hxx>
64 #include <sot/exchange.hxx>
65 #include <sot/formats.hxx>
66 #include <sal/macros.h>
68 #include <vcl/unohelp.hxx>
69 #include <vcl/unohelp2.hxx>
71 #include <officecfg/Office/Common.hxx>
73 #include <memory>
75 using namespace ::com::sun::star;
76 using namespace ::com::sun::star::uno;
77 using namespace ::com::sun::star::lang;
79 // - Redo
80 // - Bei Tracking-Cancel DefaultSelection wieder herstellen
82 static FncGetSpecialChars pImplFncGetSpecialChars = nullptr;
84 #define EDIT_ALIGN_LEFT 1
85 #define EDIT_ALIGN_CENTER 2
86 #define EDIT_ALIGN_RIGHT 3
88 #define EDIT_DEL_LEFT 1
89 #define EDIT_DEL_RIGHT 2
91 #define EDIT_DELMODE_SIMPLE 11
92 #define EDIT_DELMODE_RESTOFWORD 12
93 #define EDIT_DELMODE_RESTOFCONTENT 13
95 struct DDInfo
97 vcl::Cursor aCursor;
98 Selection aDndStartSel;
99 sal_Int32 nDropPos;
100 bool bStarterOfDD;
101 bool bDroppedInMe;
102 bool bVisCursor;
103 bool bIsStringSupported;
105 DDInfo()
107 aCursor.SetStyle( CURSOR_SHADOW );
108 nDropPos = 0;
109 bStarterOfDD = false;
110 bDroppedInMe = false;
111 bVisCursor = false;
112 bIsStringSupported = false;
116 struct Impl_IMEInfos
118 OUString aOldTextAfterStartPos;
119 std::unique_ptr<ExtTextInputAttr[]>
120 pAttribs;
121 sal_Int32 nPos;
122 sal_Int32 nLen;
123 bool bCursor;
124 bool bWasCursorOverwrite;
126 Impl_IMEInfos(sal_Int32 nPos, const OUString& rOldTextAfterStartPos);
128 void CopyAttribs(const ExtTextInputAttr* pA, sal_Int32 nL);
129 void DestroyAttribs();
132 Impl_IMEInfos::Impl_IMEInfos(sal_Int32 nP, const OUString& rOldTextAfterStartPos)
133 : aOldTextAfterStartPos(rOldTextAfterStartPos)
135 nPos = nP;
136 nLen = 0;
137 bCursor = true;
138 pAttribs = nullptr;
139 bWasCursorOverwrite = false;
142 void Impl_IMEInfos::CopyAttribs(const ExtTextInputAttr* pA, sal_Int32 nL)
144 nLen = nL;
145 pAttribs.reset(new ExtTextInputAttr[ nL ]);
146 memcpy( pAttribs.get(), pA, nL*sizeof(ExtTextInputAttr) );
149 void Impl_IMEInfos::DestroyAttribs()
151 pAttribs.reset();
152 nLen = 0;
155 Edit::Edit( WindowType nType )
156 : Control( nType )
158 ImplInitEditData();
161 Edit::Edit( vcl::Window* pParent, WinBits nStyle )
162 : Control( WINDOW_EDIT )
164 ImplInitEditData();
165 ImplInit( pParent, nStyle );
168 void Edit::SetWidthInChars(sal_Int32 nWidthInChars)
170 if (mnWidthInChars != nWidthInChars)
172 mnWidthInChars = nWidthInChars;
173 queue_resize();
177 void Edit::setMaxWidthChars(sal_Int32 nWidth)
179 if (nWidth != mnMaxWidthChars)
181 mnMaxWidthChars = nWidth;
182 queue_resize();
186 bool Edit::set_property(const OString &rKey, const OString &rValue)
188 if (rKey == "width-chars")
189 SetWidthInChars(rValue.toInt32());
190 else if (rKey == "max-width-chars")
191 setMaxWidthChars(rValue.toInt32());
192 else if (rKey == "max-length")
194 sal_Int32 nTextLen = rValue.toInt32();
195 SetMaxTextLen(nTextLen == 0 ? EDIT_NOLIMIT : nTextLen);
197 else if (rKey == "editable")
199 bool bReadOnly = !toBool(rValue);
200 SetReadOnly(bReadOnly);
201 //disable tab to traverse into readonly editables
202 WinBits nBits = GetStyle();
203 nBits &= ~(WB_TABSTOP|WB_NOTABSTOP);
204 if (!bReadOnly)
205 nBits |= WB_TABSTOP;
206 else
207 nBits |= WB_NOTABSTOP;
208 SetStyle(nBits);
210 else if (rKey == "visibility")
212 WinBits nBits = GetStyle();
213 nBits &= ~(WB_PASSWORD);
214 if (!toBool(rValue))
215 nBits |= WB_PASSWORD;
216 SetStyle(nBits);
218 else if (rKey == "placeholder-text")
219 SetPlaceholderText(OStringToOUString(rValue, RTL_TEXTENCODING_UTF8));
220 else
221 return Control::set_property(rKey, rValue);
222 return true;
225 Edit::~Edit()
227 disposeOnce();
230 void Edit::dispose()
232 delete mpDDInfo;
233 mpDDInfo = nullptr;
235 vcl::Cursor* pCursor = GetCursor();
236 if ( pCursor )
238 SetCursor( nullptr );
239 delete pCursor;
242 delete mpIMEInfos;
243 mpIMEInfos = nullptr;
245 delete mpUpdateDataTimer;
246 mpUpdateDataTimer = nullptr;
248 if ( mxDnDListener.is() )
250 if ( GetDragGestureRecognizer().is() )
252 uno::Reference< datatransfer::dnd::XDragGestureListener> xDGL( mxDnDListener, uno::UNO_QUERY );
253 GetDragGestureRecognizer()->removeDragGestureListener( xDGL );
255 if ( GetDropTarget().is() )
257 uno::Reference< datatransfer::dnd::XDropTargetListener> xDTL( mxDnDListener, uno::UNO_QUERY );
258 GetDropTarget()->removeDropTargetListener( xDTL );
261 uno::Reference< lang::XEventListener> xEL( mxDnDListener, uno::UNO_QUERY );
262 xEL->disposing( lang::EventObject() ); // #95154# #96585# Empty Source means it's the Client
263 mxDnDListener.clear();
266 SetType(WINDOW_WINDOW);
268 mpSubEdit.disposeAndClear();
269 Control::dispose();
272 void Edit::ImplInitEditData()
274 mpSubEdit = VclPtr<Edit>();
275 mpUpdateDataTimer = nullptr;
276 mpFilterText = nullptr;
277 mnXOffset = 0;
278 mnAlign = EDIT_ALIGN_LEFT;
279 mnMaxTextLen = EDIT_NOLIMIT;
280 mnWidthInChars = -1;
281 mnMaxWidthChars = -1;
282 mbModified = false;
283 mbInternModified = false;
284 mbReadOnly = false;
285 mbInsertMode = true;
286 mbClickedInSelection = false;
287 mbActivePopup = false;
288 mbIsSubEdit = false;
289 mbInMBDown = false;
290 mpDDInfo = nullptr;
291 mpIMEInfos = nullptr;
292 mcEchoChar = 0;
294 // --- RTL --- no default mirroring for Edit controls
295 // note: controls that use a subedit will revert this (SpinField, ComboBox)
296 EnableRTL( false );
298 vcl::unohelper::DragAndDropWrapper* pDnDWrapper = new vcl::unohelper::DragAndDropWrapper( this );
299 mxDnDListener = pDnDWrapper;
302 bool Edit::ImplUseNativeBorder(vcl::RenderContext& rRenderContext, WinBits nStyle)
304 bool bRet = rRenderContext.IsNativeControlSupported(ImplGetNativeControlType(),
305 ControlPart::HasBackgroundTexture)
306 && ((nStyle & WB_BORDER) && !(nStyle & WB_NOBORDER));
307 if (!bRet && mbIsSubEdit)
309 vcl::Window* pWindow = GetParent();
310 nStyle = pWindow->GetStyle();
311 bRet = pWindow->IsNativeControlSupported(ImplGetNativeControlType(),
312 ControlPart::HasBackgroundTexture)
313 && ((nStyle & WB_BORDER) && !(nStyle & WB_NOBORDER));
315 return bRet;
318 void Edit::ImplInit(vcl::Window* pParent, WinBits nStyle)
320 nStyle = ImplInitStyle(nStyle);
322 if (!(nStyle & (WB_CENTER | WB_RIGHT)))
323 nStyle |= WB_LEFT;
325 Control::ImplInit(pParent, nStyle, nullptr);
327 mbReadOnly = (nStyle & WB_READONLY) != 0;
329 mnAlign = EDIT_ALIGN_LEFT;
331 // --- RTL --- hack: right align until keyinput and cursor travelling works
332 if( IsRTLEnabled() )
333 mnAlign = EDIT_ALIGN_RIGHT;
335 if ( nStyle & WB_RIGHT )
336 mnAlign = EDIT_ALIGN_RIGHT;
337 else if ( nStyle & WB_CENTER )
338 mnAlign = EDIT_ALIGN_CENTER;
340 SetCursor( new vcl::Cursor );
342 SetPointer( Pointer( PointerStyle::Text ) );
344 uno::Reference< datatransfer::dnd::XDragGestureListener> xDGL( mxDnDListener, uno::UNO_QUERY );
345 uno::Reference< datatransfer::dnd::XDragGestureRecognizer > xDGR = GetDragGestureRecognizer();
346 if ( xDGR.is() )
348 xDGR->addDragGestureListener( xDGL );
349 uno::Reference< datatransfer::dnd::XDropTargetListener> xDTL( mxDnDListener, uno::UNO_QUERY );
350 GetDropTarget()->addDropTargetListener( xDTL );
351 GetDropTarget()->setActive( true );
352 GetDropTarget()->setDefaultActions( datatransfer::dnd::DNDConstants::ACTION_COPY_OR_MOVE );
356 WinBits Edit::ImplInitStyle( WinBits nStyle )
358 if ( !(nStyle & WB_NOTABSTOP) )
359 nStyle |= WB_TABSTOP;
360 if ( !(nStyle & WB_NOGROUP) )
361 nStyle |= WB_GROUP;
363 return nStyle;
366 bool Edit::IsCharInput( const KeyEvent& rKeyEvent )
368 // In the future we must use new Unicode functions for this
369 sal_Unicode cCharCode = rKeyEvent.GetCharCode();
370 return ((cCharCode >= 32) && (cCharCode != 127) &&
371 !rKeyEvent.GetKeyCode().IsMod3() &&
372 !rKeyEvent.GetKeyCode().IsMod2() &&
373 !rKeyEvent.GetKeyCode().IsMod1() );
376 void Edit::ImplModified()
378 mbModified = true;
379 Modify();
382 void Edit::ApplySettings(vcl::RenderContext& rRenderContext)
384 Control::ApplySettings(rRenderContext);
386 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
388 const vcl::Font& aFont = rStyleSettings.GetFieldFont();
389 ApplyControlFont(rRenderContext, aFont);
391 ImplClearLayoutData();
393 Color aTextColor = rStyleSettings.GetFieldTextColor();
394 ApplyControlForeground(rRenderContext, aTextColor);
396 if (ImplUseNativeBorder(rRenderContext, GetStyle()) || IsPaintTransparent())
398 // Transparent background
399 rRenderContext.SetBackground();
400 rRenderContext.SetFillColor();
402 else if (IsControlBackground())
404 rRenderContext.SetBackground(GetControlBackground());
405 rRenderContext.SetFillColor(GetControlBackground());
407 else
409 rRenderContext.SetBackground(rStyleSettings.GetFieldColor());
410 rRenderContext.SetFillColor(rStyleSettings.GetFieldColor());
414 long Edit::ImplGetExtraXOffset() const
416 // MT 09/2002: nExtraOffsetX should become a member, instead of checking every time,
417 // but I need an incompatible update for this...
418 // #94095# Use extra offset only when edit has a border
419 long nExtraOffset = 0;
420 if( ( GetStyle() & WB_BORDER ) || ( mbIsSubEdit && ( GetParent()->GetStyle() & WB_BORDER ) ) )
421 nExtraOffset = 2;
423 return nExtraOffset;
426 long Edit::ImplGetExtraYOffset() const
428 long nExtraOffset = 0;
429 ControlType eCtrlType = ImplGetNativeControlType();
430 if (eCtrlType != ControlType::EditboxNoBorder)
432 // add some space between text entry and border
433 nExtraOffset = 2;
435 return nExtraOffset;
438 OUString Edit::ImplGetText() const
440 if ( mcEchoChar || (GetStyle() & WB_PASSWORD) )
442 sal_Unicode cEchoChar;
443 if ( mcEchoChar )
444 cEchoChar = mcEchoChar;
445 else
446 cEchoChar = sal_Unicode(0x2022);
447 OUStringBuffer aText;
448 comphelper::string::padToLength(aText, maText.getLength(), cEchoChar);
449 return aText.makeStringAndClear();
451 else
452 return maText.toString();
455 void Edit::ImplInvalidateOrRepaint()
457 if( IsPaintTransparent() )
459 Invalidate();
460 // FIXME: this is currently only on OS X
461 if( ImplGetSVData()->maNWFData.mbNoFocusRects )
462 Update();
464 else
465 Invalidate();
468 long Edit::ImplGetTextYPosition() const
470 if ( GetStyle() & WB_TOP )
471 return ImplGetExtraXOffset();
472 else if ( GetStyle() & WB_BOTTOM )
473 return GetOutputSizePixel().Height() - GetTextHeight() - ImplGetExtraXOffset();
474 return ( GetOutputSizePixel().Height() - GetTextHeight() ) / 2;
477 void Edit::ImplRepaint(vcl::RenderContext& rRenderContext, const Rectangle& rRectangle)
479 if (!IsReallyVisible())
480 return;
482 ApplySettings(rRenderContext);
484 const OUString aText = ImplGetText();
485 const sal_Int32 nLen = aText.getLength();
487 long nDXBuffer[256];
488 std::unique_ptr<long[]> pDXBuffer;
489 long* pDX = nDXBuffer;
491 if (nLen)
493 if ((size_t) (2 * nLen) > SAL_N_ELEMENTS(nDXBuffer))
495 pDXBuffer.reset(new long[2 * (nLen + 1)]);
496 pDX = pDXBuffer.get();
499 GetCaretPositions(aText, pDX, 0, nLen);
502 long nTH = GetTextHeight();
503 Point aPos(mnXOffset, ImplGetTextYPosition());
505 vcl::Cursor* pCursor = GetCursor();
506 bool bVisCursor = pCursor && pCursor->IsVisible();
507 if (pCursor)
508 pCursor->Hide();
510 ImplClearBackground(rRenderContext, rRectangle, 0, GetOutputSizePixel().Width()-1);
512 bool bPaintPlaceholderText = aText.isEmpty() && !maPlaceholderText.isEmpty();
514 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
516 if (!IsEnabled() || bPaintPlaceholderText)
517 rRenderContext.SetTextColor(rStyleSettings.GetDisableColor());
519 // Set background color of the normal text
520 if ((GetStyle() & WB_FORCECTRLBACKGROUND) != 0 && IsControlBackground())
522 // check if we need to set ControlBackground even in NWF case
523 rRenderContext.Push(PushFlags::FILLCOLOR | PushFlags::LINECOLOR);
524 rRenderContext.SetLineColor();
525 rRenderContext.SetFillColor(GetControlBackground());
526 rRenderContext.DrawRect(Rectangle(aPos, Size(GetOutputSizePixel().Width() - 2 * mnXOffset, GetOutputSizePixel().Height())));
527 rRenderContext.Pop();
529 rRenderContext.SetTextFillColor(GetControlBackground());
531 else if (IsPaintTransparent() || ImplUseNativeBorder(rRenderContext, GetStyle()))
532 rRenderContext.SetTextFillColor();
533 else
534 rRenderContext.SetTextFillColor(IsControlBackground() ? GetControlBackground() : rStyleSettings.GetFieldColor());
536 ImplPaintBorder(rRenderContext, 0, GetOutputSizePixel().Width());
538 bool bDrawSelection = maSelection.Len() && (HasFocus() || (GetStyle() & WB_NOHIDESELECTION) || mbActivePopup);
540 aPos.X() = mnXOffset + ImplGetExtraXOffset();
541 if (bPaintPlaceholderText)
543 rRenderContext.DrawText(aPos, maPlaceholderText);
545 else if (!bDrawSelection && !mpIMEInfos)
547 rRenderContext.DrawText(aPos, aText, 0, nLen);
549 else
551 // save graphics state
552 rRenderContext.Push();
553 // first calculate higlighted and non highlighted clip regions
554 vcl::Region aHiglightClipRegion;
555 vcl::Region aNormalClipRegion;
556 Selection aTmpSel(maSelection);
557 aTmpSel.Justify();
558 // selection is highlighted
559 for(sal_Int32 i = 0; i < nLen; ++i)
561 Rectangle aRect(aPos, Size(10, nTH));
562 aRect.Left() = pDX[2 * i] + mnXOffset + ImplGetExtraXOffset();
563 aRect.Right() = pDX[2 * i + 1] + mnXOffset + ImplGetExtraXOffset();
564 aRect.Justify();
565 bool bHighlight = false;
566 if (i >= aTmpSel.Min() && i < aTmpSel.Max())
567 bHighlight = true;
569 if (mpIMEInfos && mpIMEInfos->pAttribs &&
570 i >= mpIMEInfos->nPos && i < (mpIMEInfos->nPos+mpIMEInfos->nLen) &&
571 (mpIMEInfos->pAttribs[i - mpIMEInfos->nPos] & ExtTextInputAttr::Highlight))
573 bHighlight = true;
576 if (bHighlight)
577 aHiglightClipRegion.Union(aRect);
578 else
579 aNormalClipRegion.Union(aRect);
581 // draw normal text
582 Color aNormalTextColor = rRenderContext.GetTextColor();
583 rRenderContext.SetClipRegion(aNormalClipRegion);
585 if (IsPaintTransparent())
586 rRenderContext.SetTextFillColor();
587 else
589 // Set background color when part of the text is selected
590 if (ImplUseNativeBorder(rRenderContext, GetStyle()))
592 if( (GetStyle() & WB_FORCECTRLBACKGROUND) != 0 && IsControlBackground() )
593 rRenderContext.SetTextFillColor(GetControlBackground());
594 else
595 rRenderContext.SetTextFillColor();
597 else
599 rRenderContext.SetTextFillColor(IsControlBackground() ? GetControlBackground() : rStyleSettings.GetFieldColor());
602 rRenderContext.DrawText(aPos, aText, 0, nLen);
604 // draw highlighted text
605 rRenderContext.SetClipRegion(aHiglightClipRegion);
606 rRenderContext.SetTextColor(rStyleSettings.GetHighlightTextColor());
607 rRenderContext.SetTextFillColor(rStyleSettings.GetHighlightColor());
608 rRenderContext.DrawText(aPos, aText, 0, nLen);
610 // if IME info exists loop over portions and output different font attributes
611 if (mpIMEInfos && mpIMEInfos->pAttribs)
613 for(int n = 0; n < 2; n++)
615 vcl::Region aRegion;
616 if (n == 0)
618 rRenderContext.SetTextColor(aNormalTextColor);
619 if (IsPaintTransparent())
620 rRenderContext.SetTextFillColor();
621 else
622 rRenderContext.SetTextFillColor(IsControlBackground() ? GetControlBackground() : rStyleSettings.GetFieldColor());
623 aRegion = aNormalClipRegion;
625 else
627 rRenderContext.SetTextColor(rStyleSettings.GetHighlightTextColor());
628 rRenderContext.SetTextFillColor(rStyleSettings.GetHighlightColor());
629 aRegion = aHiglightClipRegion;
632 for(int i = 0; i < mpIMEInfos->nLen; )
634 ExtTextInputAttr nAttr = mpIMEInfos->pAttribs[i];
635 vcl::Region aClip;
636 int nIndex = i;
637 while (nIndex < mpIMEInfos->nLen && mpIMEInfos->pAttribs[nIndex] == nAttr) // #112631# check nIndex before using it
639 Rectangle aRect( aPos, Size( 10, nTH ) );
640 aRect.Left() = pDX[2 * (nIndex + mpIMEInfos->nPos)] + mnXOffset + ImplGetExtraXOffset();
641 aRect.Right() = pDX[2 * (nIndex + mpIMEInfos->nPos) + 1] + mnXOffset + ImplGetExtraXOffset();
642 aRect.Justify();
643 aClip.Union(aRect);
644 nIndex++;
646 i = nIndex;
647 aClip.Intersect(aRegion);
648 if (!aClip.IsEmpty() && nAttr != ExtTextInputAttr::NONE)
650 vcl::Font aFont = rRenderContext.GetFont();
651 if (nAttr & ExtTextInputAttr::Underline)
652 aFont.SetUnderline(LINESTYLE_SINGLE);
653 else if (nAttr & ExtTextInputAttr::BoldUnderline)
654 aFont.SetUnderline( LINESTYLE_BOLD);
655 else if (nAttr & ExtTextInputAttr::DottedUnderline)
656 aFont.SetUnderline( LINESTYLE_DOTTED);
657 else if (nAttr & ExtTextInputAttr::DashDotUnderline)
658 aFont.SetUnderline( LINESTYLE_DASHDOT);
659 else if (nAttr & ExtTextInputAttr::GrayWaveline)
661 aFont.SetUnderline(LINESTYLE_WAVE);
662 rRenderContext.SetTextLineColor(Color(COL_LIGHTGRAY));
664 rRenderContext.SetFont(aFont);
666 if (nAttr & ExtTextInputAttr::RedText)
667 rRenderContext.SetTextColor(Color(COL_RED));
668 else if (nAttr & ExtTextInputAttr::HalfToneText)
669 rRenderContext.SetTextColor(Color(COL_LIGHTGRAY));
671 rRenderContext.SetClipRegion(aClip);
672 rRenderContext.DrawText(aPos, aText, 0, nLen);
678 // restore graphics state
679 rRenderContext.Pop();
682 if (bVisCursor && (!mpIMEInfos || mpIMEInfos->bCursor))
683 pCursor->Show();
686 void Edit::ImplDelete( const Selection& rSelection, sal_uInt8 nDirection, sal_uInt8 nMode )
688 const sal_Int32 nTextLen = ImplGetText().getLength();
690 // loeschen moeglich?
691 if ( !rSelection.Len() &&
692 (((rSelection.Min() == 0) && (nDirection == EDIT_DEL_LEFT)) ||
693 ((rSelection.Max() == nTextLen) && (nDirection == EDIT_DEL_RIGHT))) )
694 return;
696 ImplClearLayoutData();
698 Selection aSelection( rSelection );
699 aSelection.Justify();
701 if ( !aSelection.Len() )
703 uno::Reference < i18n::XBreakIterator > xBI = ImplGetBreakIterator();
704 if ( nDirection == EDIT_DEL_LEFT )
706 if ( nMode == EDIT_DELMODE_RESTOFWORD )
708 i18n::Boundary aBoundary = xBI->getWordBoundary( maText.toString(), aSelection.Min(),
709 GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES, true );
710 if ( aBoundary.startPos == aSelection.Min() )
711 aBoundary = xBI->previousWord( maText.toString(), aSelection.Min(),
712 GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES );
713 aSelection.Min() = aBoundary.startPos;
715 else if ( nMode == EDIT_DELMODE_RESTOFCONTENT )
717 aSelection.Min() = 0;
719 else
721 sal_Int32 nCount = 1;
722 aSelection.Min() = xBI->previousCharacters( maText.toString(), aSelection.Min(),
723 GetSettings().GetLanguageTag().getLocale(), i18n::CharacterIteratorMode::SKIPCHARACTER, nCount, nCount );
726 else
728 if ( nMode == EDIT_DELMODE_RESTOFWORD )
730 i18n::Boundary aBoundary = xBI->nextWord( maText.toString(), aSelection.Max(),
731 GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES );
732 aSelection.Max() = aBoundary.startPos;
734 else if ( nMode == EDIT_DELMODE_RESTOFCONTENT )
736 aSelection.Max() = nTextLen;
738 else
740 sal_Int32 nCount = 1;
741 aSelection.Max() = xBI->nextCharacters( maText.toString(), aSelection.Max(),
742 GetSettings().GetLanguageTag().getLocale(), i18n::CharacterIteratorMode::SKIPCHARACTER, nCount, nCount );
747 maText.remove( static_cast<sal_Int32>(aSelection.Min()), static_cast<sal_Int32>(aSelection.Len()) );
748 maSelection.Min() = aSelection.Min();
749 maSelection.Max() = aSelection.Min();
750 ImplAlignAndPaint();
751 mbInternModified = true;
754 OUString Edit::ImplGetValidString( const OUString& rString )
756 OUString aValidString( rString );
757 aValidString = aValidString.replaceAll("\n", "").replaceAll("\r", "");
758 aValidString = aValidString.replace('\t', ' ');
759 return aValidString;
762 uno::Reference < i18n::XBreakIterator > Edit::ImplGetBreakIterator()
764 //!! since we don't want to become incompatible in the next minor update
765 //!! where this code will get integrated into, xISC will be a local
766 //!! variable instead of a class member!
767 uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
768 return i18n::BreakIterator::create(xContext);
771 uno::Reference < i18n::XExtendedInputSequenceChecker > const & Edit::ImplGetInputSequenceChecker()
773 if ( !mxISC.is() )
775 mxISC = i18n::InputSequenceChecker::create(
776 ::comphelper::getProcessComponentContext() );
778 return mxISC;
781 void Edit::ShowTruncationWarning( vcl::Window* pParent )
783 ResMgr* pResMgr = ImplGetResMgr();
784 if( pResMgr )
786 ScopedVclPtrInstance< MessageDialog > aBox( pParent, ResId(SV_EDIT_WARNING_STR, *pResMgr), VclMessageType::Warning );
787 aBox->Execute();
791 bool Edit::ImplTruncateToMaxLen( OUString& rStr, sal_Int32 nSelectionLen ) const
793 bool bWasTruncated = false;
794 if (maText.getLength() - nSelectionLen > mnMaxTextLen - rStr.getLength())
796 sal_Int32 nErasePos = mnMaxTextLen - maText.getLength() + nSelectionLen;
797 rStr = rStr.copy( 0, nErasePos );
798 bWasTruncated = true;
800 return bWasTruncated;
803 void Edit::ImplInsertText( const OUString& rStr, const Selection* pNewSel, bool bIsUserInput )
805 Selection aSelection( maSelection );
806 aSelection.Justify();
808 OUString aNewText( ImplGetValidString( rStr ) );
809 ImplTruncateToMaxLen( aNewText, aSelection.Len() );
811 ImplClearLayoutData();
813 if ( aSelection.Len() )
814 maText.remove( static_cast<sal_Int32>(aSelection.Min()), static_cast<sal_Int32>(aSelection.Len()) );
815 else if ( !mbInsertMode && (aSelection.Max() < maText.getLength()) )
816 maText.remove( static_cast<sal_Int32>(aSelection.Max()), 1 );
818 // take care of input-sequence-checking now
819 if (bIsUserInput && !rStr.isEmpty())
821 SAL_WARN_IF( rStr.getLength() != 1, "vcl", "unexpected string length. User input is expected to provide 1 char only!" );
823 // determine if input-sequence-checking should be applied or not
825 uno::Reference < i18n::XBreakIterator > xBI( ImplGetBreakIterator(), UNO_QUERY );
826 bool bIsInputSequenceChecking = rStr.getLength() == 1 &&
827 officecfg::Office::Common::I18N::CTL::CTLFont::get() &&
828 officecfg::Office::Common::I18N::CTL::CTLSequenceChecking::get() &&
829 aSelection.Min() > 0 && /* first char needs not to be checked */
830 xBI.is() && i18n::ScriptType::COMPLEX == xBI->getScriptType( rStr, 0 );
832 uno::Reference < i18n::XExtendedInputSequenceChecker > xISC;
833 if (bIsInputSequenceChecking && (xISC = ImplGetInputSequenceChecker()).is())
835 sal_Unicode cChar = rStr[0];
836 sal_Int32 nTmpPos = static_cast< sal_Int32 >( aSelection.Min() );
837 sal_Int16 nCheckMode = officecfg::Office::Common::I18N::CTL::CTLSequenceCheckingRestricted::get()?
838 i18n::InputSequenceCheckMode::STRICT : i18n::InputSequenceCheckMode::BASIC;
840 // the text that needs to be checked is only the one
841 // before the current cursor position
842 const OUString aOldText( maText.getStr(), nTmpPos);
843 OUString aTmpText( aOldText );
844 if (officecfg::Office::Common::I18N::CTL::CTLSequenceCheckingTypeAndReplace::get())
846 xISC->correctInputSequence( aTmpText, nTmpPos - 1, cChar, nCheckMode );
848 // find position of first character that has changed
849 sal_Int32 nOldLen = aOldText.getLength();
850 sal_Int32 nTmpLen = aTmpText.getLength();
851 const sal_Unicode *pOldTxt = aOldText.getStr();
852 const sal_Unicode *pTmpTxt = aTmpText.getStr();
853 sal_Int32 nChgPos = 0;
854 while ( nChgPos < nOldLen && nChgPos < nTmpLen &&
855 pOldTxt[nChgPos] == pTmpTxt[nChgPos] )
856 ++nChgPos;
858 const OUString aChgText( aTmpText.copy( nChgPos ) );
860 // remove text from first pos to be changed to current pos
861 maText.remove( nChgPos, nTmpPos - nChgPos );
863 if (!aChgText.isEmpty())
865 aNewText = aChgText;
866 aSelection.Min() = nChgPos; // position for new text to be inserted
868 else
869 aNewText.clear();
871 else
873 // should the character be ignored (i.e. not get inserted) ?
874 if (!xISC->checkInputSequence( aOldText, nTmpPos - 1, cChar, nCheckMode ))
875 aNewText.clear();
879 // at this point now we will insert the non-empty text 'normally' some lines below...
882 if ( !aNewText.isEmpty() )
883 maText.insert( static_cast<sal_Int32>(aSelection.Min()), aNewText );
885 if ( !pNewSel )
887 maSelection.Min() = aSelection.Min() + aNewText.getLength();
888 maSelection.Max() = maSelection.Min();
890 else
892 maSelection = *pNewSel;
893 if ( maSelection.Min() > maText.getLength() )
894 maSelection.Min() = maText.getLength();
895 if ( maSelection.Max() > maText.getLength() )
896 maSelection.Max() = maText.getLength();
899 ImplAlignAndPaint();
900 mbInternModified = true;
903 void Edit::ImplSetText( const OUString& rText, const Selection* pNewSelection )
905 // we delete text by "selecting" the old text completely then calling InsertText; this is flicker free
906 if ( ( rText.getLength() <= mnMaxTextLen ) &&
907 ( (rText != maText.getStr()) || (pNewSelection && (*pNewSelection != maSelection)) ) )
909 ImplClearLayoutData();
910 maSelection.Min() = 0;
911 maSelection.Max() = maText.getLength();
912 if ( mnXOffset || HasPaintEvent() )
914 mnXOffset = 0;
915 maText = ImplGetValidString( rText );
917 // #i54929# recalculate mnXOffset before ImplSetSelection,
918 // else cursor ends up in wrong position
919 ImplAlign();
921 if ( pNewSelection )
922 ImplSetSelection( *pNewSelection, false );
924 if ( mnXOffset && !pNewSelection )
925 maSelection.Max() = 0;
927 Invalidate();
929 else
930 ImplInsertText( rText, pNewSelection );
932 CallEventListeners( VCLEVENT_EDIT_MODIFY );
936 ControlType Edit::ImplGetNativeControlType() const
938 ControlType nCtrl = ControlType::Generic;
939 const vcl::Window* pControl = mbIsSubEdit ? GetParent() : this;
941 switch (pControl->GetType())
943 case WINDOW_COMBOBOX:
944 case WINDOW_PATTERNBOX:
945 case WINDOW_NUMERICBOX:
946 case WINDOW_METRICBOX:
947 case WINDOW_CURRENCYBOX:
948 case WINDOW_DATEBOX:
949 case WINDOW_TIMEBOX:
950 case WINDOW_LONGCURRENCYBOX:
951 nCtrl = ControlType::Combobox;
952 break;
954 case WINDOW_MULTILINEEDIT:
955 if ( GetWindow( GetWindowType::Border ) != this )
956 nCtrl = ControlType::MultilineEditbox;
957 else
958 nCtrl = ControlType::EditboxNoBorder;
959 break;
961 case WINDOW_EDIT:
962 case WINDOW_PATTERNFIELD:
963 case WINDOW_METRICFIELD:
964 case WINDOW_CURRENCYFIELD:
965 case WINDOW_DATEFIELD:
966 case WINDOW_TIMEFIELD:
967 case WINDOW_LONGCURRENCYFIELD:
968 case WINDOW_NUMERICFIELD:
969 case WINDOW_SPINFIELD:
970 if (pControl->GetStyle() & WB_SPIN)
971 nCtrl = ControlType::Spinbox;
972 else
974 if (GetWindow(GetWindowType::Border) != this)
975 nCtrl = ControlType::Editbox;
976 else
977 nCtrl = ControlType::EditboxNoBorder;
979 break;
981 default:
982 nCtrl = ControlType::Editbox;
984 return nCtrl;
987 void Edit::ImplClearBackground(vcl::RenderContext& rRenderContext, const Rectangle& rRectangle, long nXStart, long nXEnd )
990 * note: at this point the cursor must be switched off already
992 Point aTmpPoint;
993 Rectangle aRect(aTmpPoint, GetOutputSizePixel());
994 aRect.Left() = nXStart;
995 aRect.Right() = nXEnd;
997 if( !(ImplUseNativeBorder(rRenderContext, GetStyle()) || IsPaintTransparent()))
998 rRenderContext.Erase(aRect);
999 else if (SupportsDoubleBuffering() && mbIsSubEdit)
1001 // ImplPaintBorder() is a NOP, we have a native border, and this is a sub-edit of a control.
1002 // That means we have to draw the parent native widget to paint the edit area to clear our background.
1003 PaintBufferGuard g(ImplGetWindowImpl()->mpFrameData, GetParent());
1004 GetParent()->Paint(rRenderContext, rRectangle);
1008 void Edit::ImplPaintBorder(vcl::RenderContext& rRenderContext, long nXStart, long nXEnd)
1010 // this is not needed when double-buffering
1011 if (SupportsDoubleBuffering())
1012 return;
1014 Point aTmpPoint;
1015 Rectangle aRect(aTmpPoint, GetOutputSizePixel());
1016 aRect.Left() = nXStart;
1017 aRect.Right() = nXEnd;
1019 if (ImplUseNativeBorder(rRenderContext, GetStyle()) || IsPaintTransparent())
1021 // draw the inner part by painting the whole control using its border window
1022 vcl::Window* pBorder = GetWindow(GetWindowType::Border);
1023 if (pBorder == this)
1025 // we have no border, use parent
1026 vcl::Window* pControl = mbIsSubEdit ? GetParent() : this;
1027 pBorder = pControl->GetWindow(GetWindowType::Border);
1028 if (pBorder == this)
1029 pBorder = GetParent();
1032 if (pBorder)
1034 // set proper clipping region to not overdraw the whole control
1035 vcl::Region aClipRgn = GetPaintRegion();
1036 if (!aClipRgn.IsNull())
1038 // transform clipping region to border window's coordinate system
1039 if (IsRTLEnabled() != pBorder->IsRTLEnabled() && AllSettings::GetLayoutRTL())
1041 // need to mirror in case border is not RTL but edit is (or vice versa)
1043 // mirror
1044 Rectangle aBounds(aClipRgn.GetBoundRect());
1045 int xNew = GetOutputSizePixel().Width() - aBounds.GetWidth() - aBounds.Left();
1046 aClipRgn.Move(xNew - aBounds.Left(), 0);
1048 // move offset of border window
1049 Point aBorderOffs;
1050 aBorderOffs = pBorder->ScreenToOutputPixel(OutputToScreenPixel(aBorderOffs));
1051 aClipRgn.Move(aBorderOffs.X(), aBorderOffs.Y());
1053 else
1055 // normal case
1056 Point aBorderOffs;
1057 aBorderOffs = pBorder->ScreenToOutputPixel(OutputToScreenPixel(aBorderOffs));
1058 aClipRgn.Move(aBorderOffs.X(), aBorderOffs.Y());
1061 vcl::Region oldRgn(pBorder->GetClipRegion());
1062 pBorder->SetClipRegion(aClipRgn);
1064 pBorder->Paint(*pBorder, Rectangle());
1066 pBorder->SetClipRegion(oldRgn);
1068 else
1070 pBorder->Paint(*pBorder, Rectangle());
1076 void Edit::ImplShowCursor( bool bOnlyIfVisible )
1078 if ( !IsUpdateMode() || ( bOnlyIfVisible && !IsReallyVisible() ) )
1079 return;
1081 vcl::Cursor* pCursor = GetCursor();
1082 OUString aText = ImplGetText();
1084 long nTextPos = 0;
1086 long nDXBuffer[256];
1087 std::unique_ptr<long[]> pDXBuffer;
1088 long* pDX = nDXBuffer;
1090 if( !aText.isEmpty() )
1092 if( (size_t) (2*aText.getLength()) > SAL_N_ELEMENTS(nDXBuffer) )
1094 pDXBuffer.reset(new long[2*(aText.getLength()+1)]);
1095 pDX = pDXBuffer.get();
1098 GetCaretPositions( aText, pDX, 0, aText.getLength() );
1100 if( maSelection.Max() < aText.getLength() )
1101 nTextPos = pDX[ 2*maSelection.Max() ];
1102 else
1103 nTextPos = pDX[ 2*aText.getLength()-1 ];
1106 long nCursorWidth = 0;
1107 if ( !mbInsertMode && !maSelection.Len() && (maSelection.Max() < aText.getLength()) )
1108 nCursorWidth = GetTextWidth(aText, maSelection.Max(), 1);
1109 long nCursorPosX = nTextPos + mnXOffset + ImplGetExtraXOffset();
1111 // cursor should land in visible area
1112 const Size aOutSize = GetOutputSizePixel();
1113 if ( (nCursorPosX < 0) || (nCursorPosX >= aOutSize.Width()) )
1115 long nOldXOffset = mnXOffset;
1117 if ( nCursorPosX < 0 )
1119 mnXOffset = - nTextPos;
1120 long nMaxX = 0;
1121 mnXOffset += aOutSize.Width() / 5;
1122 if ( mnXOffset > nMaxX )
1123 mnXOffset = nMaxX;
1125 else
1127 mnXOffset = (aOutSize.Width()-ImplGetExtraXOffset()) - nTextPos;
1128 // Etwas mehr?
1129 if ( (aOutSize.Width()-ImplGetExtraXOffset()) < nTextPos )
1131 long nMaxNegX = (aOutSize.Width()-ImplGetExtraXOffset()) - GetTextWidth( aText );
1132 mnXOffset -= aOutSize.Width() / 5;
1133 if ( mnXOffset < nMaxNegX ) // beides negativ...
1134 mnXOffset = nMaxNegX;
1138 nCursorPosX = nTextPos + mnXOffset + ImplGetExtraXOffset();
1139 if ( nCursorPosX == aOutSize.Width() ) // dann nicht sichtbar...
1140 nCursorPosX--;
1142 if ( mnXOffset != nOldXOffset )
1143 ImplInvalidateOrRepaint();
1146 const long nTextHeight = GetTextHeight();
1147 const long nCursorPosY = ImplGetTextYPosition();
1148 if (pCursor)
1150 pCursor->SetPos( Point( nCursorPosX, nCursorPosY ) );
1151 pCursor->SetSize( Size( nCursorWidth, nTextHeight ) );
1152 pCursor->Show();
1156 void Edit::ImplAlign()
1158 long nTextWidth = GetTextWidth( ImplGetText() );
1159 long nOutWidth = GetOutputSizePixel().Width();
1161 if ( mnAlign == EDIT_ALIGN_LEFT )
1163 if( mnXOffset && ( nTextWidth < nOutWidth ) )
1164 mnXOffset = 0;
1167 else if ( mnAlign == EDIT_ALIGN_RIGHT )
1169 long nMinXOffset = nOutWidth - nTextWidth - 1 - ImplGetExtraXOffset();
1170 bool bRTL = IsRTLEnabled();
1171 if( mbIsSubEdit && GetParent() )
1172 bRTL = GetParent()->IsRTLEnabled();
1173 if( bRTL )
1175 if( nTextWidth < nOutWidth )
1176 mnXOffset = nMinXOffset;
1178 else
1180 if( nTextWidth < nOutWidth )
1181 mnXOffset = nMinXOffset;
1182 else if ( mnXOffset < nMinXOffset )
1183 mnXOffset = nMinXOffset;
1186 else if( mnAlign == EDIT_ALIGN_CENTER )
1188 // would be nicer with check while scrolling but then it's not centred in scrolled state
1189 mnXOffset = (nOutWidth - nTextWidth) / 2;
1193 void Edit::ImplAlignAndPaint()
1195 ImplAlign();
1196 ImplInvalidateOrRepaint();
1197 ImplShowCursor();
1200 sal_Int32 Edit::ImplGetCharPos( const Point& rWindowPos ) const
1202 sal_Int32 nIndex = EDIT_NOLIMIT;
1203 OUString aText = ImplGetText();
1205 long nDXBuffer[256];
1206 std::unique_ptr<long[]> pDXBuffer;
1207 long* pDX = nDXBuffer;
1208 if( (size_t) (2*aText.getLength()) > SAL_N_ELEMENTS(nDXBuffer) )
1210 pDXBuffer.reset(new long[2*(aText.getLength()+1)]);
1211 pDX = pDXBuffer.get();
1214 GetCaretPositions( aText, pDX, 0, aText.getLength() );
1215 long nX = rWindowPos.X() - mnXOffset - ImplGetExtraXOffset();
1216 for( sal_Int32 i = 0; i < aText.getLength(); i++ )
1218 if( (pDX[2*i] >= nX && pDX[2*i+1] <= nX) ||
1219 (pDX[2*i+1] >= nX && pDX[2*i] <= nX))
1221 nIndex = i;
1222 if( pDX[2*i] < pDX[2*i+1] )
1224 if( nX > (pDX[2*i]+pDX[2*i+1])/2 )
1225 nIndex++;
1227 else
1229 if( nX < (pDX[2*i]+pDX[2*i+1])/2 )
1230 nIndex++;
1232 break;
1235 if( nIndex == EDIT_NOLIMIT )
1237 nIndex = 0;
1238 long nDiff = std::abs( pDX[0]-nX );
1239 for( sal_Int32 i = 1; i < aText.getLength(); i++ )
1241 long nNewDiff = std::abs( pDX[2*i]-nX );
1243 if( nNewDiff < nDiff )
1245 nIndex = i;
1246 nDiff = nNewDiff;
1249 if( nIndex == aText.getLength()-1 && std::abs( pDX[2*nIndex+1] - nX ) < nDiff )
1250 nIndex = EDIT_NOLIMIT;
1253 return nIndex;
1256 void Edit::ImplSetCursorPos( sal_Int32 nChar, bool bSelect )
1258 Selection aSelection( maSelection );
1259 aSelection.Max() = nChar;
1260 if ( !bSelect )
1261 aSelection.Min() = aSelection.Max();
1262 ImplSetSelection( aSelection );
1265 void Edit::ImplCopyToSelectionClipboard()
1267 if ( GetSelection().Len() )
1269 css::uno::Reference<css::datatransfer::clipboard::XClipboard> aSelection(GetPrimarySelection());
1270 ImplCopy( aSelection );
1274 void Edit::ImplCopy( uno::Reference< datatransfer::clipboard::XClipboard >& rxClipboard )
1276 vcl::unohelper::TextDataObject::CopyStringTo( GetSelected(), rxClipboard );
1279 void Edit::ImplPaste( uno::Reference< datatransfer::clipboard::XClipboard >& rxClipboard )
1281 if ( rxClipboard.is() )
1283 uno::Reference< datatransfer::XTransferable > xDataObj;
1287 SolarMutexReleaser aReleaser;
1288 xDataObj = rxClipboard->getContents();
1290 catch( const css::uno::Exception& )
1294 if ( xDataObj.is() )
1296 datatransfer::DataFlavor aFlavor;
1297 SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING, aFlavor );
1300 uno::Any aData = xDataObj->getTransferData( aFlavor );
1301 OUString aText;
1302 aData >>= aText;
1303 if( ImplTruncateToMaxLen( aText, maSelection.Len() ) )
1304 ShowTruncationWarning( this );
1305 ReplaceSelected( aText );
1307 catch( const css::uno::Exception& )
1314 void Edit::MouseButtonDown( const MouseEvent& rMEvt )
1316 if ( mpSubEdit )
1318 Control::MouseButtonDown( rMEvt );
1319 return;
1322 sal_Int32 nCharPos = ImplGetCharPos( rMEvt.GetPosPixel() );
1323 Selection aSelection( maSelection );
1324 aSelection.Justify();
1326 if ( rMEvt.GetClicks() < 4 )
1328 mbClickedInSelection = false;
1329 if ( rMEvt.GetClicks() == 3 )
1331 ImplSetSelection( Selection( 0, EDIT_NOLIMIT) );
1332 ImplCopyToSelectionClipboard();
1335 else if ( rMEvt.GetClicks() == 2 )
1337 uno::Reference < i18n::XBreakIterator > xBI = ImplGetBreakIterator();
1338 i18n::Boundary aBoundary = xBI->getWordBoundary( maText.toString(), aSelection.Max(),
1339 GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES, true );
1340 ImplSetSelection( Selection( aBoundary.startPos, aBoundary.endPos ) );
1341 ImplCopyToSelectionClipboard();
1343 else if ( !rMEvt.IsShift() && HasFocus() && aSelection.IsInside( nCharPos ) )
1344 mbClickedInSelection = true;
1345 else if ( rMEvt.IsLeft() )
1346 ImplSetCursorPos( nCharPos, rMEvt.IsShift() );
1348 if ( !mbClickedInSelection && rMEvt.IsLeft() && ( rMEvt.GetClicks() == 1 ) )
1349 StartTracking( StartTrackingFlags::ScrollRepeat );
1352 mbInMBDown = true; // then do not select all in GetFocus
1353 GrabFocus();
1354 mbInMBDown = false;
1357 void Edit::MouseButtonUp( const MouseEvent& rMEvt )
1359 if ( mbClickedInSelection && rMEvt.IsLeft() )
1361 sal_Int32 nCharPos = ImplGetCharPos( rMEvt.GetPosPixel() );
1362 ImplSetCursorPos( nCharPos, false );
1363 mbClickedInSelection = false;
1365 else if ( rMEvt.IsMiddle() && !mbReadOnly &&
1366 ( GetSettings().GetMouseSettings().GetMiddleButtonAction() == MouseMiddleButtonAction::PasteSelection ) )
1368 css::uno::Reference<css::datatransfer::clipboard::XClipboard> aSelection(Window::GetPrimarySelection());
1369 ImplPaste( aSelection );
1370 ImplModified();
1374 void Edit::Tracking( const TrackingEvent& rTEvt )
1376 if ( rTEvt.IsTrackingEnded() )
1378 if ( mbClickedInSelection )
1380 sal_Int32 nCharPos = ImplGetCharPos( rTEvt.GetMouseEvent().GetPosPixel() );
1381 ImplSetCursorPos( nCharPos, false );
1382 mbClickedInSelection = false;
1384 else if ( rTEvt.GetMouseEvent().IsLeft() )
1386 ImplCopyToSelectionClipboard();
1389 else
1391 if( !mbClickedInSelection )
1393 sal_Int32 nCharPos = ImplGetCharPos( rTEvt.GetMouseEvent().GetPosPixel() );
1394 ImplSetCursorPos( nCharPos, true );
1398 if ( mpUpdateDataTimer && !mbIsSubEdit && mpUpdateDataTimer->IsActive() )
1399 mpUpdateDataTimer->Start();//do not update while the user is still travelling in the control
1402 bool Edit::ImplHandleKeyEvent( const KeyEvent& rKEvt )
1404 bool bDone = false;
1405 sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode();
1406 KeyFuncType eFunc = rKEvt.GetKeyCode().GetFunction();
1408 mbInternModified = false;
1410 if ( eFunc != KeyFuncType::DONTKNOW )
1412 switch ( eFunc )
1414 case KeyFuncType::CUT:
1416 if ( !mbReadOnly && maSelection.Len() && !(GetStyle() & WB_PASSWORD) )
1418 Cut();
1419 ImplModified();
1420 bDone = true;
1423 break;
1425 case KeyFuncType::COPY:
1427 if ( !(GetStyle() & WB_PASSWORD) )
1429 Copy();
1430 bDone = true;
1433 break;
1435 case KeyFuncType::PASTE:
1437 if ( !mbReadOnly )
1439 Paste();
1440 bDone = true;
1443 break;
1445 case KeyFuncType::UNDO:
1447 if ( !mbReadOnly )
1449 Undo();
1450 bDone = true;
1453 break;
1455 default:
1456 eFunc = KeyFuncType::DONTKNOW;
1460 if ( !bDone && rKEvt.GetKeyCode().IsMod1() && !rKEvt.GetKeyCode().IsMod2() )
1462 if ( nCode == KEY_A )
1464 ImplSetSelection( Selection( 0, maText.getLength() ) );
1465 bDone = true;
1467 else if ( rKEvt.GetKeyCode().IsShift() && (nCode == KEY_S) )
1469 if ( pImplFncGetSpecialChars )
1471 Selection aSaveSel = GetSelection(); // if someone changes the selection in Get/LoseFocus, e.g. URL bar
1472 OUString aChars = pImplFncGetSpecialChars( this, GetFont() );
1473 SetSelection( aSaveSel );
1474 if ( !aChars.isEmpty() )
1476 ImplInsertText( aChars );
1477 ImplModified();
1479 bDone = true;
1484 if ( eFunc == KeyFuncType::DONTKNOW && ! bDone )
1486 switch ( nCode )
1488 case css::awt::Key::SELECT_ALL:
1490 ImplSetSelection( Selection( 0, maText.getLength() ) );
1491 bDone = true;
1493 break;
1495 case KEY_LEFT:
1496 case KEY_RIGHT:
1497 case KEY_HOME:
1498 case KEY_END:
1499 case css::awt::Key::MOVE_WORD_FORWARD:
1500 case css::awt::Key::SELECT_WORD_FORWARD:
1501 case css::awt::Key::MOVE_WORD_BACKWARD:
1502 case css::awt::Key::SELECT_WORD_BACKWARD:
1503 case css::awt::Key::MOVE_TO_BEGIN_OF_LINE:
1504 case css::awt::Key::MOVE_TO_END_OF_LINE:
1505 case css::awt::Key::SELECT_TO_BEGIN_OF_LINE:
1506 case css::awt::Key::SELECT_TO_END_OF_LINE:
1507 case css::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH:
1508 case css::awt::Key::MOVE_TO_END_OF_PARAGRAPH:
1509 case css::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH:
1510 case css::awt::Key::SELECT_TO_END_OF_PARAGRAPH:
1511 case css::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT:
1512 case css::awt::Key::MOVE_TO_END_OF_DOCUMENT:
1513 case css::awt::Key::SELECT_TO_BEGIN_OF_DOCUMENT:
1514 case css::awt::Key::SELECT_TO_END_OF_DOCUMENT:
1516 if ( !rKEvt.GetKeyCode().IsMod2() )
1518 ImplClearLayoutData();
1519 uno::Reference < i18n::XBreakIterator > xBI = ImplGetBreakIterator();
1521 Selection aSel( maSelection );
1522 bool bWord = rKEvt.GetKeyCode().IsMod1();
1523 bool bSelect = rKEvt.GetKeyCode().IsShift();
1524 bool bGoLeft = (nCode == KEY_LEFT);
1525 bool bGoRight = (nCode == KEY_RIGHT);
1526 bool bGoHome = (nCode == KEY_HOME);
1527 bool bGoEnd = (nCode == KEY_END);
1529 switch( nCode )
1531 case css::awt::Key::MOVE_WORD_FORWARD:
1532 bGoRight = bWord = true;break;
1533 case css::awt::Key::SELECT_WORD_FORWARD:
1534 bGoRight = bSelect = bWord = true;break;
1535 case css::awt::Key::MOVE_WORD_BACKWARD:
1536 bGoLeft = bWord = true;break;
1537 case css::awt::Key::SELECT_WORD_BACKWARD:
1538 bGoLeft = bSelect = bWord = true;break;
1539 case css::awt::Key::SELECT_TO_BEGIN_OF_LINE:
1540 case css::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH:
1541 case css::awt::Key::SELECT_TO_BEGIN_OF_DOCUMENT:
1542 bSelect = true;
1543 SAL_FALLTHROUGH;
1544 case css::awt::Key::MOVE_TO_BEGIN_OF_LINE:
1545 case css::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH:
1546 case css::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT:
1547 bGoHome = true;break;
1548 case css::awt::Key::SELECT_TO_END_OF_LINE:
1549 case css::awt::Key::SELECT_TO_END_OF_PARAGRAPH:
1550 case css::awt::Key::SELECT_TO_END_OF_DOCUMENT:
1551 bSelect = true;
1552 SAL_FALLTHROUGH;
1553 case css::awt::Key::MOVE_TO_END_OF_LINE:
1554 case css::awt::Key::MOVE_TO_END_OF_PARAGRAPH:
1555 case css::awt::Key::MOVE_TO_END_OF_DOCUMENT:
1556 bGoEnd = true;break;
1557 default:
1558 break;
1561 // Range wird in ImplSetSelection geprueft...
1562 if ( bGoLeft && aSel.Max() )
1564 if ( bWord )
1566 i18n::Boundary aBoundary = xBI->getWordBoundary( maText.toString(), aSel.Max(),
1567 GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES, true );
1568 if ( aBoundary.startPos == aSel.Max() )
1569 aBoundary = xBI->previousWord( maText.toString(), aSel.Max(),
1570 GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES );
1571 aSel.Max() = aBoundary.startPos;
1573 else
1575 sal_Int32 nCount = 1;
1576 aSel.Max() = xBI->previousCharacters( maText.toString(), aSel.Max(),
1577 GetSettings().GetLanguageTag().getLocale(), i18n::CharacterIteratorMode::SKIPCHARACTER, nCount, nCount );
1580 else if ( bGoRight && ( aSel.Max() < maText.getLength() ) )
1582 if ( bWord )
1584 i18n::Boundary aBoundary = xBI->nextWord( maText.toString(), aSel.Max(),
1585 GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES );
1586 aSel.Max() = aBoundary.startPos;
1588 else
1590 sal_Int32 nCount = 1;
1591 aSel.Max() = xBI->nextCharacters( maText.toString(), aSel.Max(),
1592 GetSettings().GetLanguageTag().getLocale(), i18n::CharacterIteratorMode::SKIPCHARACTER, nCount, nCount );
1595 else if ( bGoHome )
1597 aSel.Max() = 0;
1599 else if ( bGoEnd )
1601 aSel.Max() = EDIT_NOLIMIT;
1604 if ( !bSelect )
1605 aSel.Min() = aSel.Max();
1607 if ( aSel != GetSelection() )
1609 ImplSetSelection( aSel );
1610 ImplCopyToSelectionClipboard();
1613 if (bGoEnd && maAutocompleteHdl.IsSet() && !rKEvt.GetKeyCode().GetModifier())
1615 if ( (maSelection.Min() == maSelection.Max()) && (maSelection.Min() == maText.getLength()) )
1617 maAutocompleteHdl.Call(*this);
1621 bDone = true;
1624 break;
1626 case css::awt::Key::DELETE_WORD_BACKWARD:
1627 case css::awt::Key::DELETE_WORD_FORWARD:
1628 case css::awt::Key::DELETE_TO_BEGIN_OF_LINE:
1629 case css::awt::Key::DELETE_TO_END_OF_LINE:
1630 case KEY_BACKSPACE:
1631 case KEY_DELETE:
1633 if ( !mbReadOnly && !rKEvt.GetKeyCode().IsMod2() )
1635 sal_uInt8 nDel = (nCode == KEY_DELETE) ? EDIT_DEL_RIGHT : EDIT_DEL_LEFT;
1636 sal_uInt8 nMode = rKEvt.GetKeyCode().IsMod1() ? EDIT_DELMODE_RESTOFWORD : EDIT_DELMODE_SIMPLE;
1637 if ( (nMode == EDIT_DELMODE_RESTOFWORD) && rKEvt.GetKeyCode().IsShift() )
1638 nMode = EDIT_DELMODE_RESTOFCONTENT;
1639 switch( nCode )
1641 case css::awt::Key::DELETE_WORD_BACKWARD:
1642 nDel = EDIT_DEL_LEFT;
1643 nMode = EDIT_DELMODE_RESTOFWORD;
1644 break;
1645 case css::awt::Key::DELETE_WORD_FORWARD:
1646 nDel = EDIT_DEL_RIGHT;
1647 nMode = EDIT_DELMODE_RESTOFWORD;
1648 break;
1649 case css::awt::Key::DELETE_TO_BEGIN_OF_LINE:
1650 nDel = EDIT_DEL_LEFT;
1651 nMode = EDIT_DELMODE_RESTOFCONTENT;
1652 break;
1653 case css::awt::Key::DELETE_TO_END_OF_LINE:
1654 nDel = EDIT_DEL_RIGHT;
1655 nMode = EDIT_DELMODE_RESTOFCONTENT;
1656 break;
1657 default: break;
1659 sal_Int32 nOldLen = maText.getLength();
1660 ImplDelete( maSelection, nDel, nMode );
1661 if ( maText.getLength() != nOldLen )
1662 ImplModified();
1663 bDone = true;
1666 break;
1668 case KEY_INSERT:
1670 if ( !mpIMEInfos && !mbReadOnly && !rKEvt.GetKeyCode().IsMod2() )
1672 SetInsertMode( !mbInsertMode );
1673 bDone = true;
1676 break;
1678 default:
1680 if ( IsCharInput( rKEvt ) )
1682 bDone = true; // read characters also when in ReadOnly
1683 if ( !mbReadOnly )
1685 ImplInsertText(OUString(rKEvt.GetCharCode()), nullptr, true);
1686 if (maAutocompleteHdl.IsSet())
1688 if ( (maSelection.Min() == maSelection.Max()) && (maSelection.Min() == maText.getLength()) )
1690 maAutocompleteHdl.Call(*this);
1699 if ( mbInternModified )
1700 ImplModified();
1702 return bDone;
1705 void Edit::KeyInput( const KeyEvent& rKEvt )
1707 if ( mpUpdateDataTimer && !mbIsSubEdit && mpUpdateDataTimer->IsActive() )
1708 mpUpdateDataTimer->Start();//do not update while the user is still travelling in the control
1710 if ( mpSubEdit || !ImplHandleKeyEvent( rKEvt ) )
1711 Control::KeyInput( rKEvt );
1714 void Edit::FillLayoutData() const
1716 mpControlData->mpLayoutData.reset( new vcl::ControlLayoutData );
1717 const_cast<Edit*>(this)->Invalidate();
1720 void Edit::Paint(vcl::RenderContext& rRenderContext, const Rectangle& rRectangle)
1722 if (!mpSubEdit)
1723 ImplRepaint(rRenderContext, rRectangle);
1726 void Edit::Resize()
1728 if ( !mpSubEdit && IsReallyVisible() )
1730 Control::Resize();
1731 // Wegen vertikaler Zentrierung...
1732 mnXOffset = 0;
1733 ImplAlign();
1734 Invalidate();
1735 ImplShowCursor();
1739 void Edit::Draw( OutputDevice* pDev, const Point& rPos, const Size& rSize, DrawFlags nFlags )
1741 ApplySettings(*pDev);
1743 Point aPos = pDev->LogicToPixel( rPos );
1744 Size aSize = pDev->LogicToPixel( rSize );
1745 vcl::Font aFont = GetDrawPixelFont( pDev );
1746 OutDevType eOutDevType = pDev->GetOutDevType();
1748 pDev->Push();
1749 pDev->SetMapMode();
1750 pDev->SetFont( aFont );
1751 pDev->SetTextFillColor();
1753 // Border/Background
1754 pDev->SetLineColor();
1755 pDev->SetFillColor();
1756 bool bBorder = !(nFlags & DrawFlags::NoBorder ) && (GetStyle() & WB_BORDER);
1757 bool bBackground = !(nFlags & DrawFlags::NoBackground) && IsControlBackground();
1758 if ( bBorder || bBackground )
1760 Rectangle aRect( aPos, aSize );
1761 if ( bBorder )
1763 ImplDrawFrame( pDev, aRect );
1765 if ( bBackground )
1767 pDev->SetFillColor( GetControlBackground() );
1768 pDev->DrawRect( aRect );
1772 // Inhalt
1773 if ( ( nFlags & DrawFlags::Mono ) || ( eOutDevType == OUTDEV_PRINTER ) )
1774 pDev->SetTextColor( Color( COL_BLACK ) );
1775 else
1777 if ( !(nFlags & DrawFlags::NoDisable ) && !IsEnabled() )
1779 const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
1780 pDev->SetTextColor( rStyleSettings.GetDisableColor() );
1782 else
1784 pDev->SetTextColor( GetTextColor() );
1788 const long nOnePixel = GetDrawPixel( pDev, 1 );
1789 const long nOffX = 3*nOnePixel;
1790 DrawTextFlags nTextStyle = DrawTextFlags::VCenter;
1791 Rectangle aTextRect( aPos, aSize );
1793 if ( GetStyle() & WB_CENTER )
1794 nTextStyle |= DrawTextFlags::Center;
1795 else if ( GetStyle() & WB_RIGHT )
1796 nTextStyle |= DrawTextFlags::Right;
1797 else
1798 nTextStyle |= DrawTextFlags::Left;
1800 aTextRect.Left() += nOffX;
1801 aTextRect.Right() -= nOffX;
1803 OUString aText = ImplGetText();
1804 long nTextHeight = pDev->GetTextHeight();
1805 long nTextWidth = pDev->GetTextWidth( aText );
1806 long nOffY = (aSize.Height() - nTextHeight) / 2;
1808 // Clipping?
1809 if ( (nOffY < 0) ||
1810 ((nOffY+nTextHeight) > aSize.Height()) ||
1811 ((nOffX+nTextWidth) > aSize.Width()) )
1813 Rectangle aClip( aPos, aSize );
1814 if ( nTextHeight > aSize.Height() )
1815 aClip.Bottom() += nTextHeight-aSize.Height()+1; // prevent HP printers from 'optimizing'
1816 pDev->IntersectClipRegion( aClip );
1819 pDev->DrawText( aTextRect, aText, nTextStyle );
1820 pDev->Pop();
1822 if ( GetSubEdit() )
1824 GetSubEdit()->Draw( pDev, rPos, rSize, nFlags );
1828 void Edit::ImplInvalidateOutermostBorder( vcl::Window* pWin )
1830 // allow control to show focused state
1831 vcl::Window *pInvalWin = pWin, *pBorder = pWin;
1832 while( ( pBorder = pInvalWin->GetWindow( GetWindowType::Border ) ) != pInvalWin && pBorder &&
1833 pInvalWin->ImplGetFrame() == pBorder->ImplGetFrame() )
1835 pInvalWin = pBorder;
1838 pInvalWin->Invalidate( InvalidateFlags::Children | InvalidateFlags::Update );
1841 void Edit::GetFocus()
1843 if ( mpSubEdit )
1844 mpSubEdit->ImplGrabFocus( GetGetFocusFlags() );
1845 else if ( !mbActivePopup )
1847 maUndoText = maText.toString();
1849 SelectionOptions nSelOptions = GetSettings().GetStyleSettings().GetSelectionOptions();
1850 if ( !( GetStyle() & (WB_NOHIDESELECTION|WB_READONLY) )
1851 && ( GetGetFocusFlags() & (GetFocusFlags::Init|GetFocusFlags::Tab|GetFocusFlags::CURSOR|GetFocusFlags::Mnemonic) ) )
1853 if ( nSelOptions & SelectionOptions::ShowFirst )
1855 maSelection.Min() = maText.getLength();
1856 maSelection.Max() = 0;
1858 else
1860 maSelection.Min() = 0;
1861 maSelection.Max() = maText.getLength();
1863 if ( mbIsSubEdit )
1864 static_cast<Edit*>(GetParent())->CallEventListeners( VCLEVENT_EDIT_SELECTIONCHANGED );
1865 else
1866 CallEventListeners( VCLEVENT_EDIT_SELECTIONCHANGED );
1869 ImplShowCursor();
1871 // FIXME: this is currently only on OS X
1872 // check for other platforms that need similar handling
1873 if( ImplGetSVData()->maNWFData.mbNoFocusRects &&
1874 IsNativeWidgetEnabled() &&
1875 IsNativeControlSupported( ControlType::Editbox, ControlPart::Entire ) )
1877 ImplInvalidateOutermostBorder( mbIsSubEdit ? GetParent() : this );
1879 else if ( maSelection.Len() )
1881 // Selektion malen
1882 if ( !HasPaintEvent() )
1883 ImplInvalidateOrRepaint();
1884 else
1885 Invalidate();
1888 SetInputContext( InputContext( GetFont(), !IsReadOnly() ? InputContextFlags::Text|InputContextFlags::ExtText : InputContextFlags::NONE ) );
1891 Control::GetFocus();
1894 void Edit::LoseFocus()
1896 if ( mpUpdateDataTimer && !mbIsSubEdit && mpUpdateDataTimer->IsActive() )
1898 //notify an update latest when the focus is lost
1899 mpUpdateDataTimer->Stop();
1900 mpUpdateDataTimer->Timeout();
1903 if ( !mpSubEdit )
1905 // FIXME: this is currently only on OS X
1906 // check for other platforms that need similar handling
1907 if( ImplGetSVData()->maNWFData.mbNoFocusRects &&
1908 IsNativeWidgetEnabled() &&
1909 IsNativeControlSupported( ControlType::Editbox, ControlPart::Entire ) )
1911 ImplInvalidateOutermostBorder( mbIsSubEdit ? GetParent() : this );
1914 if ( !mbActivePopup && !( GetStyle() & WB_NOHIDESELECTION ) && maSelection.Len() )
1915 ImplInvalidateOrRepaint(); // Selektion malen
1918 Control::LoseFocus();
1921 void Edit::Command( const CommandEvent& rCEvt )
1923 if ( rCEvt.GetCommand() == CommandEventId::ContextMenu )
1925 VclPtr<PopupMenu> pPopup = Edit::CreatePopupMenu();
1927 if ( !maSelection.Len() )
1929 pPopup->EnableItem( SV_MENU_EDIT_CUT, false );
1930 pPopup->EnableItem( SV_MENU_EDIT_COPY, false );
1931 pPopup->EnableItem( SV_MENU_EDIT_DELETE, false );
1934 if ( IsReadOnly() )
1936 pPopup->EnableItem( SV_MENU_EDIT_CUT, false );
1937 pPopup->EnableItem( SV_MENU_EDIT_PASTE, false );
1938 pPopup->EnableItem( SV_MENU_EDIT_DELETE, false );
1939 pPopup->EnableItem( SV_MENU_EDIT_INSERTSYMBOL, false );
1941 else
1943 // only paste if text available in clipboard
1944 bool bData = false;
1945 uno::Reference< datatransfer::clipboard::XClipboard > xClipboard = GetClipboard();
1947 if ( xClipboard.is() )
1949 uno::Reference< datatransfer::XTransferable > xDataObj;
1951 SolarMutexReleaser aReleaser;
1952 xDataObj = xClipboard->getContents();
1954 if ( xDataObj.is() )
1956 datatransfer::DataFlavor aFlavor;
1957 SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING, aFlavor );
1958 bData = xDataObj->isDataFlavorSupported( aFlavor );
1961 pPopup->EnableItem( SV_MENU_EDIT_PASTE, bData );
1964 if ( maUndoText == maText.getStr() )
1965 pPopup->EnableItem( SV_MENU_EDIT_UNDO, false );
1966 if ( ( maSelection.Min() == 0 ) && ( maSelection.Max() == maText.getLength() ) )
1967 pPopup->EnableItem( SV_MENU_EDIT_SELECTALL, false );
1968 if ( !pImplFncGetSpecialChars )
1970 sal_uInt16 nPos = pPopup->GetItemPos( SV_MENU_EDIT_INSERTSYMBOL );
1971 pPopup->RemoveItem( nPos );
1972 pPopup->RemoveItem( nPos-1 );
1975 mbActivePopup = true;
1976 Selection aSaveSel = GetSelection(); // if someone changes selection in Get/LoseFocus, e.g. URL bar
1977 Point aPos = rCEvt.GetMousePosPixel();
1978 if ( !rCEvt.IsMouseEvent() )
1980 // Show menu eventually centered in selection
1981 Size aSize = GetOutputSizePixel();
1982 aPos = Point( aSize.Width()/2, aSize.Height()/2 );
1984 sal_uInt16 n = pPopup->Execute( this, aPos );
1985 pPopup.disposeAndClear();
1986 SetSelection( aSaveSel );
1987 switch ( n )
1989 case SV_MENU_EDIT_UNDO:
1990 Undo();
1991 ImplModified();
1992 break;
1993 case SV_MENU_EDIT_CUT:
1994 Cut();
1995 ImplModified();
1996 break;
1997 case SV_MENU_EDIT_COPY:
1998 Copy();
1999 break;
2000 case SV_MENU_EDIT_PASTE:
2001 Paste();
2002 ImplModified();
2003 break;
2004 case SV_MENU_EDIT_DELETE:
2005 DeleteSelected();
2006 ImplModified();
2007 break;
2008 case SV_MENU_EDIT_SELECTALL:
2009 ImplSetSelection( Selection( 0, maText.getLength() ) );
2010 break;
2011 case SV_MENU_EDIT_INSERTSYMBOL:
2013 OUString aChars = pImplFncGetSpecialChars( this, GetFont() );
2014 SetSelection( aSaveSel );
2015 if ( !aChars.isEmpty() )
2017 ImplInsertText( aChars );
2018 ImplModified();
2021 break;
2023 mbActivePopup = false;
2025 else if ( rCEvt.GetCommand() == CommandEventId::StartExtTextInput )
2027 DeleteSelected();
2028 delete mpIMEInfos;
2029 sal_Int32 nPos = static_cast<sal_Int32>(maSelection.Max());
2030 mpIMEInfos = new Impl_IMEInfos( nPos, OUString(maText.getStr() + nPos ) );
2031 mpIMEInfos->bWasCursorOverwrite = !IsInsertMode();
2033 else if ( rCEvt.GetCommand() == CommandEventId::EndExtTextInput )
2035 bool bInsertMode = !mpIMEInfos->bWasCursorOverwrite;
2036 delete mpIMEInfos;
2037 mpIMEInfos = nullptr;
2039 SetInsertMode(bInsertMode);
2040 ImplModified();
2042 Invalidate();
2044 // #i25161# call auto complete handler for ext text commit also
2045 if (maAutocompleteHdl.IsSet())
2047 if ( (maSelection.Min() == maSelection.Max()) && (maSelection.Min() == maText.getLength()) )
2049 maAutocompleteHdl.Call(*this);
2053 else if ( rCEvt.GetCommand() == CommandEventId::ExtTextInput )
2055 const CommandExtTextInputData* pData = rCEvt.GetExtTextInputData();
2057 maText.remove( mpIMEInfos->nPos, mpIMEInfos->nLen );
2058 maText.insert( mpIMEInfos->nPos, pData->GetText() );
2059 if ( mpIMEInfos->bWasCursorOverwrite )
2061 const sal_Int32 nOldIMETextLen = mpIMEInfos->nLen;
2062 const sal_Int32 nNewIMETextLen = pData->GetText().getLength();
2063 if ( ( nOldIMETextLen > nNewIMETextLen ) &&
2064 ( nNewIMETextLen < mpIMEInfos->aOldTextAfterStartPos.getLength() ) )
2066 // restore old characters
2067 const sal_Int32 nRestore = nOldIMETextLen - nNewIMETextLen;
2068 maText.insert( mpIMEInfos->nPos + nNewIMETextLen, mpIMEInfos->aOldTextAfterStartPos.copy( nNewIMETextLen, nRestore ) );
2070 else if ( ( nOldIMETextLen < nNewIMETextLen ) &&
2071 ( nOldIMETextLen < mpIMEInfos->aOldTextAfterStartPos.getLength() ) )
2073 const sal_Int32 nOverwrite = ( nNewIMETextLen > mpIMEInfos->aOldTextAfterStartPos.getLength()
2074 ? mpIMEInfos->aOldTextAfterStartPos.getLength() : nNewIMETextLen ) - nOldIMETextLen;
2075 maText.remove( mpIMEInfos->nPos + nNewIMETextLen, nOverwrite );
2079 if ( pData->GetTextAttr() )
2081 mpIMEInfos->CopyAttribs( pData->GetTextAttr(), pData->GetText().getLength() );
2082 mpIMEInfos->bCursor = pData->IsCursorVisible();
2084 else
2086 mpIMEInfos->DestroyAttribs();
2089 ImplAlignAndPaint();
2090 sal_Int32 nCursorPos = mpIMEInfos->nPos + pData->GetCursorPos();
2091 SetSelection( Selection( nCursorPos, nCursorPos ) );
2092 SetInsertMode( !pData->IsCursorOverwrite() );
2094 if ( pData->IsCursorVisible() )
2095 GetCursor()->Show();
2096 else
2097 GetCursor()->Hide();
2099 else if ( rCEvt.GetCommand() == CommandEventId::CursorPos )
2101 if ( mpIMEInfos )
2103 sal_Int32 nCursorPos = GetSelection().Max();
2104 SetCursorRect( nullptr, GetTextWidth( maText.toString(), nCursorPos, mpIMEInfos->nPos+mpIMEInfos->nLen-nCursorPos ) );
2106 else
2108 SetCursorRect();
2111 else if ( rCEvt.GetCommand() == CommandEventId::SelectionChange )
2113 const CommandSelectionChangeData *pData = rCEvt.GetSelectionChangeData();
2114 Selection aSelection( pData->GetStart(), pData->GetEnd() );
2115 SetSelection(aSelection);
2117 else if ( rCEvt.GetCommand() == CommandEventId::QueryCharPosition )
2119 if (mpIMEInfos && mpIMEInfos->nLen > 0)
2121 OUString aText = ImplGetText();
2122 long nDXBuffer[256];
2123 std::unique_ptr<long[]> pDXBuffer;
2124 long* pDX = nDXBuffer;
2126 if( !aText.isEmpty() )
2128 if( (size_t) (2*aText.getLength()) > SAL_N_ELEMENTS(nDXBuffer) )
2130 pDXBuffer.reset(new long[2*(aText.getLength()+1)]);
2131 pDX = pDXBuffer.get();
2134 GetCaretPositions( aText, pDX, 0, aText.getLength() );
2136 long nTH = GetTextHeight();
2137 Point aPos( mnXOffset, ImplGetTextYPosition() );
2139 std::unique_ptr<Rectangle[]> aRects(new Rectangle[ mpIMEInfos->nLen ]);
2140 for ( int nIndex = 0; nIndex < mpIMEInfos->nLen; ++nIndex )
2142 Rectangle aRect( aPos, Size( 10, nTH ) );
2143 aRect.Left() = pDX[2*(nIndex+mpIMEInfos->nPos)] + mnXOffset + ImplGetExtraXOffset();
2144 aRects[ nIndex ] = aRect;
2146 SetCompositionCharRect( aRects.get(), mpIMEInfos->nLen );
2149 else
2150 Control::Command( rCEvt );
2153 void Edit::StateChanged( StateChangedType nType )
2155 if (nType == StateChangedType::InitShow)
2157 if (!mpSubEdit)
2159 mnXOffset = 0; // if GrabFocus before while size was still wrong
2160 ImplAlign();
2161 if (!mpSubEdit)
2162 ImplShowCursor(false);
2163 Invalidate();
2166 else if (nType == StateChangedType::Enable)
2168 if (!mpSubEdit)
2170 // change text color only
2171 ImplInvalidateOrRepaint();
2174 else if (nType == StateChangedType::Style || nType == StateChangedType::Mirroring)
2176 WinBits nStyle = GetStyle();
2177 if (nType == StateChangedType::Style)
2179 nStyle = ImplInitStyle(GetStyle());
2180 SetStyle(nStyle);
2183 sal_uInt16 nOldAlign = mnAlign;
2184 mnAlign = EDIT_ALIGN_LEFT;
2186 // --- RTL --- hack: right align until keyinput and cursor travelling works
2187 // edits are always RTL disabled
2188 // however the parent edits contain the correct setting
2189 if (mbIsSubEdit && GetParent()->IsRTLEnabled())
2191 if (GetParent()->GetStyle() & WB_LEFT)
2192 mnAlign = EDIT_ALIGN_RIGHT;
2193 if (nType == StateChangedType::Mirroring)
2194 SetLayoutMode(ComplexTextLayoutFlags::BiDiRtl | ComplexTextLayoutFlags::TextOriginLeft);
2196 else if (mbIsSubEdit && !GetParent()->IsRTLEnabled())
2198 if (nType == StateChangedType::Mirroring)
2199 SetLayoutMode(ComplexTextLayoutFlags::TextOriginLeft);
2202 if (nStyle & WB_RIGHT)
2203 mnAlign = EDIT_ALIGN_RIGHT;
2204 else if (nStyle & WB_CENTER)
2205 mnAlign = EDIT_ALIGN_CENTER;
2206 if (!maText.isEmpty() && (mnAlign != nOldAlign))
2208 ImplAlign();
2209 Invalidate();
2213 else if (nType == StateChangedType::Zoom)
2215 if (!mpSubEdit)
2217 ApplySettings(*this);
2218 ImplShowCursor();
2219 Invalidate();
2222 else if (nType == StateChangedType::ControlFont)
2224 if (!mpSubEdit)
2226 ApplySettings(*this);
2227 ImplShowCursor();
2228 Invalidate();
2231 else if (nType == StateChangedType::ControlForeground)
2233 if (!mpSubEdit)
2235 ApplySettings(*this);
2236 Invalidate();
2239 else if (nType == StateChangedType::ControlBackground)
2241 if (!mpSubEdit)
2243 ApplySettings(*this);
2244 Invalidate();
2248 Control::StateChanged(nType);
2251 void Edit::DataChanged( const DataChangedEvent& rDCEvt )
2253 if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
2254 (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
2255 ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
2256 (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) )
2258 if ( !mpSubEdit )
2260 ApplySettings(*this);
2261 ImplShowCursor();
2262 Invalidate();
2266 Control::DataChanged( rDCEvt );
2269 void Edit::ImplShowDDCursor()
2271 if (!mpDDInfo->bVisCursor)
2273 long nTextWidth = GetTextWidth( maText.toString(), 0, mpDDInfo->nDropPos );
2274 long nTextHeight = GetTextHeight();
2275 Rectangle aCursorRect( Point( nTextWidth + mnXOffset, (GetOutputSize().Height()-nTextHeight)/2 ), Size( 2, nTextHeight ) );
2276 mpDDInfo->aCursor.SetWindow( this );
2277 mpDDInfo->aCursor.SetPos( aCursorRect.TopLeft() );
2278 mpDDInfo->aCursor.SetSize( aCursorRect.GetSize() );
2279 mpDDInfo->aCursor.Show();
2280 mpDDInfo->bVisCursor = true;
2284 void Edit::ImplHideDDCursor()
2286 if ( mpDDInfo && mpDDInfo->bVisCursor )
2288 mpDDInfo->aCursor.Hide();
2289 mpDDInfo->bVisCursor = false;
2293 TextFilter::TextFilter(const OUString &rForbiddenChars)
2294 : sForbiddenChars(rForbiddenChars)
2298 TextFilter::~TextFilter()
2302 OUString TextFilter::filter(const OUString &rText)
2304 OUString sTemp(rText);
2305 for (sal_Int32 i = 0; i < sForbiddenChars.getLength(); ++i)
2307 sTemp = sTemp.replaceAll(OUStringLiteral1(sForbiddenChars[i]), "");
2309 return sTemp;
2312 void Edit::filterText()
2314 Selection aSel = GetSelection();
2315 const OUString sOrig = GetText();
2316 const OUString sNew = mpFilterText->filter(GetText());
2317 if (sOrig != sNew)
2319 sal_Int32 nDiff = sOrig.getLength() - sNew.getLength();
2320 if (nDiff)
2322 aSel.setMin(aSel.getMin() - nDiff);
2323 aSel.setMax(aSel.getMin());
2325 SetText(sNew);
2326 SetSelection(aSel);
2330 void Edit::Modify()
2332 if (mpFilterText)
2333 filterText();
2335 if ( mbIsSubEdit )
2337 static_cast<Edit*>(GetParent())->Modify();
2339 else
2341 if ( mpUpdateDataTimer )
2342 mpUpdateDataTimer->Start();
2344 if ( ImplCallEventListenersAndHandler( VCLEVENT_EDIT_MODIFY, [this] () { maModifyHdl.Call(*this); } ) )
2345 // have been destroyed while calling into the handlers
2346 return;
2348 // #i13677# notify edit listeners about caret position change
2349 CallEventListeners( VCLEVENT_EDIT_CARETCHANGED );
2350 // FIXME: this is currently only on OS X
2351 // check for other platforms that need similar handling
2352 if( ImplGetSVData()->maNWFData.mbNoFocusRects &&
2353 IsNativeWidgetEnabled() &&
2354 IsNativeControlSupported( ControlType::Editbox, ControlPart::Entire ) )
2356 ImplInvalidateOutermostBorder( this );
2361 void Edit::UpdateData()
2363 maUpdateDataHdl.Call( *this );
2366 IMPL_LINK_NOARG(Edit, ImplUpdateDataHdl, Timer *, void)
2368 UpdateData();
2371 void Edit::EnableUpdateData( sal_uLong nTimeout )
2373 if ( !nTimeout )
2374 DisableUpdateData();
2375 else
2377 if ( !mpUpdateDataTimer )
2379 mpUpdateDataTimer = new Timer("UpdateDataTimer");
2380 mpUpdateDataTimer->SetTimeoutHdl( LINK( this, Edit, ImplUpdateDataHdl ) );
2381 mpUpdateDataTimer->SetDebugName( "vcl::Edit mpUpdateDataTimer" );
2384 mpUpdateDataTimer->SetTimeout( nTimeout );
2388 void Edit::DisableUpdateData()
2390 delete mpUpdateDataTimer;
2391 mpUpdateDataTimer = nullptr;
2394 void Edit::SetEchoChar( sal_Unicode c )
2396 mcEchoChar = c;
2397 if ( mpSubEdit )
2398 mpSubEdit->SetEchoChar( c );
2401 void Edit::SetReadOnly( bool bReadOnly )
2403 if ( mbReadOnly != bool(bReadOnly) )
2405 mbReadOnly = bReadOnly;
2406 if ( mpSubEdit )
2407 mpSubEdit->SetReadOnly( bReadOnly );
2409 CompatStateChanged( StateChangedType::ReadOnly );
2413 void Edit::SetInsertMode( bool bInsert )
2415 if ( bInsert != mbInsertMode )
2417 mbInsertMode = bInsert;
2418 if ( mpSubEdit )
2419 mpSubEdit->SetInsertMode( bInsert );
2420 else
2421 ImplShowCursor();
2425 bool Edit::IsInsertMode() const
2427 if ( mpSubEdit )
2428 return mpSubEdit->IsInsertMode();
2429 else
2430 return mbInsertMode;
2433 void Edit::SetMaxTextLen(sal_Int32 nMaxLen)
2435 mnMaxTextLen = nMaxLen > 0 ? nMaxLen : EDIT_NOLIMIT;
2437 if ( mpSubEdit )
2438 mpSubEdit->SetMaxTextLen( mnMaxTextLen );
2439 else
2441 if ( maText.getLength() > mnMaxTextLen )
2442 ImplDelete( Selection( mnMaxTextLen, maText.getLength() ), EDIT_DEL_RIGHT, EDIT_DELMODE_SIMPLE );
2446 void Edit::SetSelection( const Selection& rSelection )
2448 // If the selection was changed from outside, e.g. by MouseButtonDown, don't call Tracking()
2449 // directly afterwards which would change the selection again
2450 if ( IsTracking() )
2451 EndTracking();
2452 else if ( mpSubEdit && mpSubEdit->IsTracking() )
2453 mpSubEdit->EndTracking();
2455 ImplSetSelection( rSelection );
2458 void Edit::ImplSetSelection( const Selection& rSelection, bool bPaint )
2460 if ( mpSubEdit )
2461 mpSubEdit->ImplSetSelection( rSelection );
2462 else
2464 if ( rSelection != maSelection )
2466 Selection aOld( maSelection );
2467 Selection aNew( rSelection );
2469 if ( aNew.Min() > maText.getLength() )
2470 aNew.Min() = maText.getLength();
2471 if ( aNew.Max() > maText.getLength() )
2472 aNew.Max() = maText.getLength();
2473 if ( aNew.Min() < 0 )
2474 aNew.Min() = 0;
2475 if ( aNew.Max() < 0 )
2476 aNew.Max() = 0;
2478 if ( aNew != maSelection )
2480 ImplClearLayoutData();
2481 Selection aTemp = maSelection;
2482 maSelection = aNew;
2484 if ( bPaint && ( aOld.Len() || aNew.Len() || IsPaintTransparent() ) )
2485 ImplInvalidateOrRepaint();
2486 ImplShowCursor();
2488 bool bCaret = false, bSelection = false;
2489 long nB=aNew.Max(), nA=aNew.Min(),oB=aTemp.Max(), oA=aTemp.Min();
2490 long nGap = nB-nA, oGap = oB-oA;
2491 if (nB != oB)
2492 bCaret = true;
2493 if (nGap != 0 || oGap != 0)
2494 bSelection = true;
2496 if (bSelection)
2498 if ( mbIsSubEdit )
2499 static_cast<Edit*>(GetParent())->CallEventListeners( VCLEVENT_EDIT_SELECTIONCHANGED );
2500 else
2501 CallEventListeners( VCLEVENT_EDIT_SELECTIONCHANGED );
2504 if (bCaret)
2506 if ( mbIsSubEdit )
2507 static_cast<Edit*>(GetParent())->CallEventListeners( VCLEVENT_EDIT_CARETCHANGED );
2508 else
2509 CallEventListeners( VCLEVENT_EDIT_CARETCHANGED );
2512 // #103511# notify combobox listeners of deselection
2513 if( !maSelection && GetParent() && GetParent()->GetType() == WINDOW_COMBOBOX )
2514 static_cast<Edit*>(GetParent())->CallEventListeners( VCLEVENT_COMBOBOX_DESELECT );
2520 const Selection& Edit::GetSelection() const
2522 if ( mpSubEdit )
2523 return mpSubEdit->GetSelection();
2524 else
2525 return maSelection;
2528 void Edit::ReplaceSelected( const OUString& rStr )
2530 if ( mpSubEdit )
2531 mpSubEdit->ReplaceSelected( rStr );
2532 else
2533 ImplInsertText( rStr );
2536 void Edit::DeleteSelected()
2538 if ( mpSubEdit )
2539 mpSubEdit->DeleteSelected();
2540 else
2542 if ( maSelection.Len() )
2543 ImplDelete( maSelection, EDIT_DEL_RIGHT, EDIT_DELMODE_SIMPLE );
2547 OUString Edit::GetSelected() const
2549 if ( mpSubEdit )
2550 return mpSubEdit->GetSelected();
2551 else
2553 Selection aSelection( maSelection );
2554 aSelection.Justify();
2555 return OUString( maText.getStr() + aSelection.Min(), aSelection.Len() );
2559 void Edit::Cut()
2561 if ( !(GetStyle() & WB_PASSWORD ) )
2563 Copy();
2564 ReplaceSelected( OUString() );
2568 void Edit::Copy()
2570 if ( !(GetStyle() & WB_PASSWORD ) )
2572 css::uno::Reference<css::datatransfer::clipboard::XClipboard> aClipboard(GetClipboard());
2573 ImplCopy( aClipboard );
2577 void Edit::Paste()
2579 css::uno::Reference<css::datatransfer::clipboard::XClipboard> aClipboard(GetClipboard());
2580 ImplPaste( aClipboard );
2583 void Edit::Undo()
2585 if ( mpSubEdit )
2586 mpSubEdit->Undo();
2587 else
2589 const OUString aText( maText.toString() );
2590 ImplDelete( Selection( 0, aText.getLength() ), EDIT_DEL_RIGHT, EDIT_DELMODE_SIMPLE );
2591 ImplInsertText( maUndoText );
2592 ImplSetSelection( Selection( 0, maUndoText.getLength() ) );
2593 maUndoText = aText;
2597 void Edit::SetText( const OUString& rStr )
2599 if ( mpSubEdit )
2600 mpSubEdit->SetText( rStr ); // not directly ImplSetText if SetText overridden
2601 else
2603 Selection aNewSel( 0, 0 ); // prevent scrolling
2604 ImplSetText( rStr, &aNewSel );
2608 void Edit::SetText( const OUString& rStr, const Selection& rSelection )
2610 if ( mpSubEdit )
2611 mpSubEdit->SetText( rStr, rSelection );
2612 else
2613 ImplSetText( rStr, &rSelection );
2616 OUString Edit::GetText() const
2618 if ( mpSubEdit )
2619 return mpSubEdit->GetText();
2620 else
2621 return maText.toString();
2624 void Edit::SetCursorAtLast(){
2625 ImplSetCursorPos( GetText().getLength(), false );
2628 void Edit::SetPlaceholderText( const OUString& rStr )
2630 if ( mpSubEdit )
2631 mpSubEdit->SetPlaceholderText( rStr );
2632 else if ( maPlaceholderText != rStr )
2634 maPlaceholderText = rStr;
2635 if ( GetText().isEmpty() )
2636 Invalidate();
2640 OUString Edit::GetPlaceholderText() const
2642 if ( mpSubEdit )
2643 return mpSubEdit->GetPlaceholderText();
2645 return maPlaceholderText;
2648 void Edit::SetModifyFlag()
2650 if ( mpSubEdit )
2651 mpSubEdit->mbModified = true;
2652 else
2653 mbModified = true;
2656 void Edit::ClearModifyFlag()
2658 if ( mpSubEdit )
2659 mpSubEdit->mbModified = false;
2660 else
2661 mbModified = false;
2664 void Edit::SetSubEdit(Edit* pEdit)
2666 mpSubEdit.disposeAndClear();
2667 mpSubEdit.set(pEdit);
2669 if (mpSubEdit)
2671 SetPointer(PointerStyle::Arrow); // Nur das SubEdit hat den BEAM...
2672 mpSubEdit->mbIsSubEdit = true;
2674 mpSubEdit->SetReadOnly(mbReadOnly);
2675 mpSubEdit->maAutocompleteHdl = maAutocompleteHdl;
2679 Size Edit::CalcMinimumSizeForText(const OUString &rString) const
2681 ControlType eCtrlType = ImplGetNativeControlType();
2683 Size aSize;
2684 if (mnWidthInChars != -1)
2686 //CalcSize calls CalcWindowSize, but we will call that also in this
2687 //function, so undo the first one with CalcOutputSize
2688 aSize = CalcOutputSize(CalcSize(mnWidthInChars));
2690 else
2692 OUString aString;
2693 if (mnMaxWidthChars != -1 && mnMaxWidthChars < rString.getLength())
2694 aString = rString.copy(0, mnMaxWidthChars);
2695 else
2696 aString = rString;
2698 aSize.Height() = GetTextHeight();
2699 aSize.Width() = GetTextWidth(aString);
2700 aSize.Width() += ImplGetExtraXOffset() * 2;
2702 // do not create edit fields in which one cannot enter anything
2703 // a default minimum width should exist for at least 3 characters
2705 //CalcSize calls CalcWindowSize, but we will call that also in this
2706 //function, so undo the first one with CalcOutputSize
2707 Size aMinSize(CalcOutputSize(CalcSize(3)));
2708 if (aSize.Width() < aMinSize.Width())
2709 aSize.Width() = aMinSize.Width();
2712 aSize.Height() += ImplGetExtraYOffset() * 2;
2714 aSize = CalcWindowSize( aSize );
2716 // ask NWF what if it has an opinion, too
2717 ImplControlValue aControlValue;
2718 Rectangle aRect( Point( 0, 0 ), aSize );
2719 Rectangle aContent, aBound;
2720 if (GetNativeControlRegion(eCtrlType, ControlPart::Entire, aRect, ControlState::NONE,
2721 aControlValue, OUString(), aBound, aContent))
2723 if (aBound.GetHeight() > aSize.Height())
2724 aSize.Height() = aBound.GetHeight();
2726 return aSize;
2729 Size Edit::CalcMinimumSize() const
2731 return CalcMinimumSizeForText(GetText());
2734 Size Edit::GetMinimumEditSize()
2736 vcl::Window* pDefWin = ImplGetDefaultWindow();
2737 ScopedVclPtrInstance< Edit > aEdit( pDefWin, WB_BORDER );
2738 Size aSize( aEdit->CalcMinimumSize() );
2739 return aSize;
2742 Size Edit::GetOptimalSize() const
2744 return CalcMinimumSize();
2747 Size Edit::CalcSize(sal_Int32 nChars) const
2749 // width for N characters, independent from content.
2750 // works only correct for fixed fonts, average otherwise
2751 Size aSz( GetTextWidth( "x" ), GetTextHeight() );
2752 aSz.Width() *= nChars;
2753 aSz.Width() += ImplGetExtraXOffset() * 2;
2754 aSz = CalcWindowSize( aSz );
2755 return aSz;
2758 sal_Int32 Edit::GetMaxVisChars() const
2760 const vcl::Window* pW = mpSubEdit ? mpSubEdit : this;
2761 sal_Int32 nOutWidth = pW->GetOutputSizePixel().Width();
2762 sal_Int32 nCharWidth = GetTextWidth( "x" );
2763 return nCharWidth ? nOutWidth/nCharWidth : 0;
2766 sal_Int32 Edit::GetCharPos( const Point& rWindowPos ) const
2768 return ImplGetCharPos( rWindowPos );
2771 void Edit::SetGetSpecialCharsFunction( FncGetSpecialChars fn )
2773 pImplFncGetSpecialChars = fn;
2776 FncGetSpecialChars Edit::GetGetSpecialCharsFunction()
2778 return pImplFncGetSpecialChars;
2781 VclPtr<PopupMenu> Edit::CreatePopupMenu()
2783 ResMgr* pResMgr = ImplGetResMgr();
2784 if( ! pResMgr )
2785 return VclPtr<PopupMenu>::Create();
2787 VclPtrInstance<PopupMenu> pPopup( ResId( SV_RESID_MENU_EDIT, *pResMgr ) );
2788 const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
2789 if ( rStyleSettings.GetHideDisabledMenuItems() )
2790 pPopup->SetMenuFlags( MenuFlags::HideDisabledEntries );
2791 else
2792 pPopup->SetMenuFlags ( MenuFlags::AlwaysShowDisabledEntries );
2793 if ( rStyleSettings.GetContextMenuShortcuts() )
2795 pPopup->SetAccelKey( SV_MENU_EDIT_UNDO, vcl::KeyCode( KeyFuncType::UNDO ) );
2796 pPopup->SetAccelKey( SV_MENU_EDIT_CUT, vcl::KeyCode( KeyFuncType::CUT ) );
2797 pPopup->SetAccelKey( SV_MENU_EDIT_COPY, vcl::KeyCode( KeyFuncType::COPY ) );
2798 pPopup->SetAccelKey( SV_MENU_EDIT_PASTE, vcl::KeyCode( KeyFuncType::PASTE ) );
2799 pPopup->SetAccelKey( SV_MENU_EDIT_DELETE, vcl::KeyCode( KeyFuncType::DELETE ) );
2800 pPopup->SetAccelKey( SV_MENU_EDIT_SELECTALL, vcl::KeyCode( KEY_A, false, true, false, false ) );
2801 pPopup->SetAccelKey( SV_MENU_EDIT_INSERTSYMBOL, vcl::KeyCode( KEY_S, true, true, false, false ) );
2803 return pPopup;
2806 // css::datatransfer::dnd::XDragGestureListener
2807 void Edit::dragGestureRecognized( const css::datatransfer::dnd::DragGestureEvent& rDGE ) throw (css::uno::RuntimeException, std::exception)
2809 SolarMutexGuard aVclGuard;
2811 if ( !IsTracking() && maSelection.Len() &&
2812 !(GetStyle() & WB_PASSWORD) && (!mpDDInfo || !mpDDInfo->bStarterOfDD) ) // Kein Mehrfach D&D
2814 Selection aSel( maSelection );
2815 aSel.Justify();
2817 // Nur wenn Maus in der Selektion...
2818 Point aMousePos( rDGE.DragOriginX, rDGE.DragOriginY );
2819 sal_Int32 nCharPos = ImplGetCharPos( aMousePos );
2820 if ( (nCharPos >= aSel.Min()) && (nCharPos < aSel.Max()) )
2822 if ( !mpDDInfo )
2823 mpDDInfo = new DDInfo;
2825 mpDDInfo->bStarterOfDD = true;
2826 mpDDInfo->aDndStartSel = aSel;
2828 if ( IsTracking() )
2829 EndTracking(); // Vor D&D Tracking ausschalten
2831 vcl::unohelper::TextDataObject* pDataObj = new vcl::unohelper::TextDataObject( GetSelected() );
2832 sal_Int8 nActions = datatransfer::dnd::DNDConstants::ACTION_COPY;
2833 if ( !IsReadOnly() )
2834 nActions |= datatransfer::dnd::DNDConstants::ACTION_MOVE;
2835 rDGE.DragSource->startDrag( rDGE, nActions, 0 /*cursor*/, 0 /*image*/, pDataObj, mxDnDListener );
2836 if ( GetCursor() )
2837 GetCursor()->Hide();
2843 // css::datatransfer::dnd::XDragSourceListener
2844 void Edit::dragDropEnd( const css::datatransfer::dnd::DragSourceDropEvent& rDSDE ) throw (css::uno::RuntimeException, std::exception)
2846 SolarMutexGuard aVclGuard;
2848 if (rDSDE.DropSuccess && (rDSDE.DropAction & datatransfer::dnd::DNDConstants::ACTION_MOVE) && mpDDInfo)
2850 Selection aSel( mpDDInfo->aDndStartSel );
2851 if ( mpDDInfo->bDroppedInMe )
2853 if ( aSel.Max() > mpDDInfo->nDropPos )
2855 long nLen = aSel.Len();
2856 aSel.Min() += nLen;
2857 aSel.Max() += nLen;
2860 ImplDelete( aSel, EDIT_DEL_RIGHT, EDIT_DELMODE_SIMPLE );
2861 ImplModified();
2864 ImplHideDDCursor();
2865 delete mpDDInfo;
2866 mpDDInfo = nullptr;
2869 // css::datatransfer::dnd::XDropTargetListener
2870 void Edit::drop( const css::datatransfer::dnd::DropTargetDropEvent& rDTDE ) throw (css::uno::RuntimeException, std::exception)
2872 SolarMutexGuard aVclGuard;
2874 bool bChanges = false;
2875 if ( !mbReadOnly && mpDDInfo )
2877 ImplHideDDCursor();
2879 Selection aSel( maSelection );
2880 aSel.Justify();
2882 if ( aSel.Len() && !mpDDInfo->bStarterOfDD )
2883 ImplDelete( aSel, EDIT_DEL_RIGHT, EDIT_DELMODE_SIMPLE );
2885 mpDDInfo->bDroppedInMe = true;
2887 aSel.Min() = mpDDInfo->nDropPos;
2888 aSel.Max() = mpDDInfo->nDropPos;
2889 ImplSetSelection( aSel );
2891 uno::Reference< datatransfer::XTransferable > xDataObj = rDTDE.Transferable;
2892 if ( xDataObj.is() )
2894 datatransfer::DataFlavor aFlavor;
2895 SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING, aFlavor );
2896 if ( xDataObj->isDataFlavorSupported( aFlavor ) )
2898 uno::Any aData = xDataObj->getTransferData( aFlavor );
2899 OUString aText;
2900 aData >>= aText;
2901 ImplInsertText( aText );
2902 bChanges = true;
2903 ImplModified();
2907 if ( !mpDDInfo->bStarterOfDD )
2909 delete mpDDInfo;
2910 mpDDInfo = nullptr;
2914 rDTDE.Context->dropComplete( bChanges );
2917 void Edit::dragEnter( const css::datatransfer::dnd::DropTargetDragEnterEvent& rDTDE ) throw (css::uno::RuntimeException, std::exception)
2919 if ( !mpDDInfo )
2921 mpDDInfo = new DDInfo;
2923 // search for string data type
2924 const Sequence< css::datatransfer::DataFlavor >& rFlavors( rDTDE.SupportedDataFlavors );
2925 sal_Int32 nEle = rFlavors.getLength();
2926 mpDDInfo->bIsStringSupported = false;
2927 for( sal_Int32 i = 0; i < nEle; i++ )
2929 sal_Int32 nIndex = 0;
2930 const OUString aMimetype = rFlavors[i].MimeType.getToken( 0, ';', nIndex );
2931 if ( aMimetype == "text/plain" )
2933 mpDDInfo->bIsStringSupported = true;
2934 break;
2939 void Edit::dragExit( const css::datatransfer::dnd::DropTargetEvent& ) throw (css::uno::RuntimeException, std::exception)
2941 SolarMutexGuard aVclGuard;
2943 ImplHideDDCursor();
2946 void Edit::dragOver( const css::datatransfer::dnd::DropTargetDragEvent& rDTDE ) throw (css::uno::RuntimeException, std::exception)
2948 SolarMutexGuard aVclGuard;
2950 Point aMousePos( rDTDE.LocationX, rDTDE.LocationY );
2952 sal_Int32 nPrevDropPos = mpDDInfo->nDropPos;
2953 mpDDInfo->nDropPos = ImplGetCharPos( aMousePos );
2956 Size aOutSize = GetOutputSizePixel();
2957 if ( ( aMousePos.X() < 0 ) || ( aMousePos.X() > aOutSize.Width() ) )
2959 // Scroll?
2960 // No, I will not receive events in this case....
2964 Selection aSel( maSelection );
2965 aSel.Justify();
2967 // Don't accept drop in selection or read-only field...
2968 if ( IsReadOnly() || aSel.IsInside( mpDDInfo->nDropPos ) || ! mpDDInfo->bIsStringSupported )
2970 ImplHideDDCursor();
2971 rDTDE.Context->rejectDrag();
2973 else
2975 // Alten Cursor wegzeichnen...
2976 if ( !mpDDInfo->bVisCursor || ( nPrevDropPos != mpDDInfo->nDropPos ) )
2978 ImplHideDDCursor();
2979 ImplShowDDCursor();
2981 rDTDE.Context->acceptDrag( rDTDE.DropAction );
2985 OUString Edit::GetSurroundingText() const
2987 if (mpSubEdit)
2988 return mpSubEdit->GetSurroundingText();
2989 return maText.toString();
2992 Selection Edit::GetSurroundingTextSelection() const
2994 return GetSelection();
2997 FactoryFunction Edit::GetUITestFactory() const
2999 return EditUIObject::create;
3002 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */