Coverage Report

Created: 2026-07-10 11:04

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