Coverage Report

Created: 2026-05-16 09:25

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