Coverage Report

Created: 2025-07-07 10:01

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