Coverage Report

Created: 2026-09-28 10:59

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